Files
VOREStation/code/game/objects/items/devices/paicard.dm
T
Will 14e23e07f0 upports syndicate pai pet from chomp and emag behaviors (#19249)
* upporting syndicate pai pet

* tgui data

* don't emag cards without a pai

* aaugghh

* allow bingle pai, pai counts for lonely

* modular computers

* fix that

* .

* emag

* that'S fine

* pai card as antag tools

* .

* trashcatch

* that

* .

* only show emag chassis if we have a toolkit too

* .

* .

---------

Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
2026-03-05 21:53:36 +01:00

889 lines
29 KiB
Plaintext

/obj/item/paicard
name = "personal AI device"
icon = 'icons/obj/pda.dmi'
icon_state = "pai"
item_state = "electronic"
w_class = ITEMSIZE_SMALL
slot_flags = SLOT_BELT | SLOT_HOLSTER
origin_tech = list(TECH_DATA = 2)
show_messages = 0
preserve_item = 1
var/obj/item/radio/borg/pai/radio
var/mob/living/silicon/pai/pai
var/image/screen_layer
var/screen_color = "#00ff0d"
var/current_emotion = 1
var/last_notify = 0
var/screen_msg
pickup_sound = 'sound/items/pickup/device.ogg'
drop_sound = 'sound/items/drop/device.ogg'
// Parts and upgrades
var/panel_open = FALSE
var/cell = PP_FUNCTIONAL //critical- power
var/processor = PP_FUNCTIONAL //critical- the thinky part
var/board = PP_FUNCTIONAL //critical- makes everything work
var/capacitor = PP_FUNCTIONAL //critical- power processing
var/projector = PP_FUNCTIONAL //non-critical- affects unfolding
var/emitter = PP_FUNCTIONAL //non-critical- affects unfolding
var/speech_synthesizer = PP_FUNCTIONAL //non-critical- affects speech
///Var for attack_self chain
var/special_handling = FALSE
var/selected_pai
// Special modules
var/emagged = FALSE
var/has_emag_toolkit = TRUE
var/obj/item/multitool/multitool
var/obj/item/assembly/signaler/signaler
//Currently selected SubSystem
var/static/list/systems_list = list("pAI","MultiTool","Emag","Signaler")
var/selected_system = "pAI"
/obj/item/paicard/relaymove(mob/user, direction)
if(user.stat || user.stunned)
return
var/obj/item/rig/rig = src.get_rig()
if(istype(rig))
rig.forced_move(direction, user)
/obj/item/paicard/Initialize(mapload)
. = ..()
setEmotion(16)
/obj/item/paicard/Destroy()
//Will stop people throwing friend pAIs into the singularity so they can respawn
if(!QDELETED(pai)) // Either the pai or card could be deleted first, prevent a loop
pai.death(0)
QDEL_NULL(radio)
QDEL_NULL(multitool)
QDEL_NULL(signaler)
return ..()
/obj/item/paicard/attack_ghost(mob/user)
if(pai) //Have a person in them already?
return ..()
if(is_damage_critical())
to_chat(user, span_warning("That card is too damaged to activate!"))
return
var/time_till_respawn = user.time_till_respawn()
if(time_till_respawn == -1) // Special case, never allowed to respawn
to_chat(user, span_warning("Respawning is not allowed!"))
else if(time_till_respawn) // Nonzero time to respawn
to_chat(user, span_warning("You can't do that yet! You died too recently. You need to wait another [round(time_till_respawn/10/60, 0.1)] minutes."))
return
if(jobban_isbanned(user, JOB_PAI))
to_chat(user,span_warning("You cannot join a pAI card when you are banned from playing as a pAI."))
return
if(SSpai.check_is_already_pai(user.ckey))
to_chat(user, span_warning("You can't just rejoin any old pAI card!!! Your card still exists."))
return
var/pai_name = user.client?.prefs.read_preference(/datum/preference/text/pai_name)
if(!pai_name || pai_name == PAI_UNSET)
to_chat(user, span_danger("You have no pai name set."))
return
var/choice = tgui_alert(user, "Do you want to inhabit this pAI using \"[pai_name]\"?", "Load pAI", list("Load pAI Data", "Cancel"))
if(choice == "Load pAI Data")
ghost_inhabit(user)
return
/obj/item/paicard/proc/ghost_inhabit(mob/user)
RETURN_TYPE(/mob/living/silicon/pai)
var/pai_name = user.client?.prefs.read_preference(/datum/preference/text/pai_name)
if(!pai_name || pai_name == PAI_UNSET) // Lets avoid "None Set" pais joining
to_chat(user, span_danger("You have no pai name set."))
return
// Setup pai
var/mob/living/silicon/pai/new_pai = new(src)
new_pai.key = user.key
GLOB.paikeys |= new_pai.ckey
setPersonality(new_pai)
new_pai.apply_preferences(new_pai.client)
return new_pai
/obj/item/paicard/tgui_interact(mob/user, datum/tgui/ui)
if(is_damage_critical())
to_chat(user, span_warning("WARNING: CRITICAL HARDWARE FAILURE, SERVICE DEVICE IMMEDIATELY"))
return
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "PAICard", "Personal AI Device")
ui.open()
/obj/item/paicard/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/spritesheet_batched/pai_icons),
)
/obj/item/paicard/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = list(
"active_pai_data" = null,
"selected_pai_data" = null,
"available_pais" = null,
"waiting_for_response" = in_use,
"emag_systems" = null,
)
if(pai) // Only set pai data if we have one
data["active_pai_data"] = get_active_data()
return data
if(selected_pai)
data["selected_pai_data"] = SSpai.get_detailed_invite_data(selected_pai)
// Only get the invite list if we can browse for them
data["available_pais"] = SSpai.get_invite_list_data()
return data
/obj/item/paicard/proc/get_active_data()
var/list/radio_data
if(radio)
radio_data = list(
"radio_transmit" = radio.broadcasting,
"radio_recieve" = radio.listening,
)
var/list/emag_data
if(emagged && has_emag_toolkit) // Special pAI tools
emag_data = list(
"emag_systems" = systems_list,
"selected_system" = selected_system
)
var/datum/asset/spritesheet_batched/pai_icons/spritesheet = get_asset_datum(/datum/asset/spritesheet_batched/pai_icons)
var/datum/pai_sprite/sprite_datum = SSpai.chassis_data(pai.chassis_name)
var/css_class = sanitize_css_class_name("[sprite_datum.type]")
return list(
"name" = pai.name,
"color" = screen_color,
"chassis" = pai.chassis_name,
"health" = pai.health,
"law_zero" = pai.pai_law0,
"law_extra" = pai.pai_laws,
"master_name" = pai.master,
"master_dna" = pai.master_dna,
"screen_msg" = screen_msg,
"radio_data" = radio_data,
"sprite_datum_class" = css_class,
"sprite_datum_size" = spritesheet.icon_size_id(css_class + "S"), // just get the south icon's size, the rest will be the same
"emag_data" = emag_data,
)
/obj/item/paicard/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(is_damage_critical())
return FALSE
if(..())
return TRUE
add_fingerprint(ui.user)
switch(action)
if("preview")
if(pai)
return FALSE
if(in_use)
return FALSE
var/new_selection = params["ref"]
if(!istext(new_selection))
return FALSE
selected_pai = new_selection
return TRUE
if("clear_preview")
if(pai)
return FALSE
if(in_use)
return FALSE
selected_pai = null
return TRUE
if("setdna")
if(!pai)
return FALSE
if(pai.master_dna)
return FALSE
var/mob/M = ui.user
var/has_dna = FALSE
if(istype(M, /mob/living/carbon))
var/mob/living/carbon/carby = M
var/datum/species/spec = carby.species
has_dna = TRUE
if(spec.flags & NO_DNA)
has_dna = FALSE
if(has_dna)
var/datum/dna/dna = M.dna
pai.master = M.real_name
pai.master_dna = dna.unique_enzymes
to_chat(pai, span_warning(span_large("You have been bound to a new master.")))
return TRUE
to_chat(ui.user, span_notice("You don't have any DNA, or your DNA is incompatible with this device."))
return FALSE
if("cleardna")
if(!pai)
return FALSE
pai.master = null
pai.master_dna = null
return TRUE
/*
if("wipe")
if(!pai)
return FALSE
if(in_use)
return FALSE
in_use = TRUE
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"))
in_use = FALSE
if(!confirm)
return FALSE
if(confirm == "Yes")
for(var/mob/M in src)
to_chat(M, span_red("<h2>You feel yourself slipping away from reality.</h2>"))
to_chat(M, "<font color = #ff4d4d><h3>Byte by byte you lose your sense of self.</h3></font>")
to_chat(M, "<font color = #ff8787><h4>Your mental faculties leave you.</h4></font>")
to_chat(M, "<font color = #ffc4c4><h5>oblivion... </h5></font>")
M.death(0)
removePersonality()
return TRUE
*/
if("wires")
if(!pai)
return FALSE
// WIRE_SIGNAL = 1
// WIRE_RECEIVE = 2
// WIRE_TRANSMIT = 4
switch(text2num(params["wires"]))
if(4)
radio.ToggleBroadcast()
return TRUE
if(2)
radio.ToggleReception()
return TRUE
return FALSE
if("setlaws")
if(!pai)
return FALSE
if(in_use)
return FALSE
var/newlaws = sanitize(params["directive"], MAX_MESSAGE_LEN, FALSE, FALSE, TRUE)
if(newlaws)
pai.pai_laws = newlaws
show_laws(TRUE)
return TRUE
if("clearlaws")
if(!pai)
return FALSE
pai.pai_laws = null
return TRUE
if("select_pai")
if(pai)
return FALSE
if(in_use)
return FALSE
in_use = TRUE
SSpai.invite_ghost(ui.user, params["ref"], src)
in_use = FALSE
selected_pai = null
return TRUE
if("select_tool") // Emag tools
if(!emagged || !has_emag_toolkit)
return FALSE
var/new_tool = params["tool"]
if(!(new_tool in systems_list))
return FALSE
selected_system = new_tool
return TRUE
if("activate_tool") // Emag tools
if(!emagged || !has_emag_toolkit || !selected_system)
return FALSE
switch(selected_system)
if("MultiTool")
multitool.attack_self(ui.user)
return TRUE
if("Signaler")
signaler.attack_self(ui.user)
return TRUE
return FALSE
/obj/item/paicard/pre_attack(atom/A, mob/user, params)
if(emagged && has_emag_toolkit)
// Perform builtin tool actions if we have a emag system selected
switch(selected_system)
if("Emag")
if(istype(A,/obj/machinery/door))
return TRUE //for doors use the doorjack
return A.emag_act(1,user,src)
if("MultiTool")
A.attackby(multitool,user)
return TRUE
if("Signaler")
A.attackby(signaler,user)
return TRUE
. = ..()
/obj/item/paicard/proc/show_laws(updated = FALSE)
to_chat(pai, examine_block(span_notice((updated ? "Your supplemental directives have been updated. Your new" : "Your") + " directives are:") + "<br>" + "Prime Directive: [span_info(pai.pai_law0)]<br>Supplemental Directives: [span_info(pai.pai_laws)]"))
/obj/item/paicard/proc/setPersonality(mob/living/silicon/pai/personality)
pai = personality
setEmotion(1)
/obj/item/paicard/proc/removePersonality()
pai = null
setEmotion(16)
/obj/item/paicard/proc/setEmotion(emotion)
cut_overlays()
qdel(screen_layer)
screen_layer = null
switch(emotion)
if(1) screen_layer = image(icon, "pai-neutral")
if(2) screen_layer = image(icon, "pai-what")
if(3) screen_layer = image(icon, "pai-happy")
if(4) screen_layer = image(icon, "pai-cat")
if(5) screen_layer = image(icon, "pai-extremely-happy")
if(6) screen_layer = image(icon, "pai-face")
if(7) screen_layer = image(icon, "pai-laugh")
if(8) screen_layer = image(icon, "pai-sad")
if(9) screen_layer = image(icon, "pai-angry")
if(10) screen_layer = image(icon, "pai-silly")
if(11) screen_layer = image(icon, "pai-nose")
if(12) screen_layer = image(icon, "pai-smirk")
if(13) screen_layer = image(icon, "pai-exclamation")
if(14) screen_layer = image(icon, "pai-question")
if(15) screen_layer = image(icon, "pai-blank")
if(16) screen_layer = image(icon, "pai-off")
screen_layer.color = pai ? pai.eye_color : initial(screen_color)
add_overlay(screen_layer)
current_emotion = emotion
/obj/item/paicard/proc/alertUpdate()
if(pai)
return
if(last_notify == 0 || (5 MINUTES <= world.time - last_notify))
audible_message(span_notice("\The [src] flashes a message across its screen, \"Additional personalities available for download.\""), hearing_distance = world.view, runemessage = "bleeps!")
last_notify = world.time
/obj/item/paicard/emp_act(severity, recursive)
for(var/mob/M in src)
M.emp_act(severity, recursive)
/obj/item/paicard/ex_act(severity)
if(pai)
pai.ex_act(severity)
else
qdel(src)
/obj/item/paicard/see_emote(mob/living/M, text)
if(pai && pai.client && !pai.canmove)
var/rendered = span_message("[text]")
pai.show_message(rendered, 2)
..()
/obj/item/paicard/show_message(msg, type, alt, alt_type)
if(pai && pai.client)
var/rendered = span_message("[msg]")
pai.show_message(rendered, type)
..()
/obj/item/paicard/proc/clear_invite_overlay()
SHOULD_NOT_OVERRIDE(TRUE)
PRIVATE_PROC(TRUE)
if(pai) // Don't wipe emotion if a pai was invited
return
setEmotion(16)
/obj/item/paicard/attackby(var/obj/item/I as obj, mob/user as mob)
if(istype(I,/obj/item/tool/screwdriver))
if(panel_open)
panel_open = FALSE
user.visible_message(span_notice("\The [user] secured \the [src]'s maintenance panel."))
playsound(src, 'sound/items/Screwdriver.ogg', 50, 1)
else if(pai)
if(do_after(user, 3 SECONDS, target = src))
panel_open = TRUE
user.visible_message(span_warning("\The [user] opened \the [src]'s maintenance panel."))
playsound(src, 'sound/items/Screwdriver.ogg', 50, 1)
if(istype(I,/obj/item/robotanalyzer))
if(!panel_open)
to_chat(user, span_warning("The panel isn't open. You will need to unscrew it to open it."))
else
if(cell == PP_FUNCTIONAL)
to_chat(user,"Power cell: " + span_notice("functional"))
else if(cell == PP_BROKEN)
to_chat(user,"Power cell: " + span_warning("damaged - CRITICAL"))
else
to_chat(user,"Power cell: " + span_warning("missing - CRITICAL"))
if(processor == PP_FUNCTIONAL)
to_chat(user,"Processor: " + span_notice("functional"))
else if(processor == PP_BROKEN)
to_chat(user,"Processor: " + span_warning("damaged - CRITICAL"))
else
to_chat(user,"Processor: " + span_warning("missing - CRITICAL"))
if(board == PP_FUNCTIONAL)
to_chat(user,"Board: " + span_notice("functional"))
else if(board == PP_BROKEN)
to_chat(user,"Board: " + span_warning("damaged - CRITICAL"))
else
to_chat(user,"Board: " + span_warning("missing - CRITICAL"))
if(capacitor == PP_FUNCTIONAL)
to_chat(user,"Capacitors: " + span_notice("functional"))
else if(capacitor == PP_BROKEN)
to_chat(user,"Capacitors: " + span_warning("damaged - CRITICAL"))
else
to_chat(user,"Capacitors: " + span_warning("missing - CRITICAL"))
if(projector == PP_FUNCTIONAL)
to_chat(user,"Projectors: " + span_notice("functional"))
else if(projector == PP_BROKEN)
to_chat(user,"Projectors: " + span_warning("damaged"))
else
to_chat(user,"Projectors: " + span_warning("missing"))
if(emitter == PP_FUNCTIONAL)
to_chat(user,"Emitters: " + span_notice("functional"))
else if(emitter == PP_BROKEN)
to_chat(user,"Emitters: " + span_warning("damaged"))
else
to_chat(user,"Emitters: " + span_warning("missing"))
if(speech_synthesizer == PP_FUNCTIONAL)
to_chat(user,"Speech Synthesizer: " + span_notice("functional"))
else if(speech_synthesizer == PP_BROKEN)
to_chat(user,"Speech Synthesizer: " + span_warning("damaged"))
else
to_chat(user,"Speech Synthesizer: " + span_warning("missing"))
if(istype(I,/obj/item/multitool))
if(!panel_open)
to_chat(user, span_warning("You can't do that in this state."))
else
var/list/parts = list()
if(cell != PP_MISSING)
parts |= "cell"
if(processor != PP_MISSING)
parts |= "processor"
if(board != PP_MISSING)
parts |= "board"
if(capacitor != PP_MISSING)
parts |= "capacitor"
if(projector != PP_MISSING)
parts |= "projector"
if(emitter != PP_MISSING)
parts |= "emitter"
if(speech_synthesizer != PP_MISSING)
parts |= "speech synthesizer"
var/choice = tgui_input_list(user, "Which part would you like to check?", "Check part", parts)
switch(choice)
if("cell")
if(cell == PP_FUNCTIONAL)
to_chat(user,"Power cell: " + span_notice("functional"))
else if(speech_synthesizer == PP_BROKEN)
to_chat(user,"Power cell: " + span_warning("damaged"))
else
to_chat(user,"Power cell: " + span_warning("missing"))
if("processor")
if(processor == PP_FUNCTIONAL)
to_chat(user,"Processor: " + span_notice("functional"))
else if(speech_synthesizer == PP_BROKEN)
to_chat(user,"Processor: " + span_warning("damaged"))
else
to_chat(user,"Processor: " + span_warning("missing"))
if("board")
if(board == PP_FUNCTIONAL)
to_chat(user,"Board: " + span_notice("functional"))
else if(speech_synthesizer == PP_BROKEN)
to_chat(user,"Board: " + span_warning("damaged"))
else
to_chat(user,"Board: " + span_warning("missing"))
if("capacitor")
if(capacitor == PP_FUNCTIONAL)
to_chat(user,"Capacitors: " + span_notice("functional"))
else if(speech_synthesizer == PP_BROKEN)
to_chat(user,"Capacitors: " + span_warning("damaged"))
else
to_chat(user,"Capacitors: " + span_warning("missing"))
if("projector")
if(projector == PP_FUNCTIONAL)
to_chat(user,"Projectors: " + span_notice("functional"))
else if(speech_synthesizer == PP_BROKEN)
to_chat(user,"Projectors: " + span_warning("damaged"))
else
to_chat(user,"Projectors: " + span_warning("missing"))
if("emitter")
if(emitter == PP_FUNCTIONAL)
to_chat(user,"Emitters: " + span_notice("functional"))
else if(speech_synthesizer == PP_BROKEN)
to_chat(user,"Emitters: " + span_warning("damaged"))
else
to_chat(user,"Emitters: " + span_warning("missing"))
if("speech synthesizer")
if(speech_synthesizer == PP_FUNCTIONAL)
to_chat(user,"Speech Synthesizer: " + span_notice("functional"))
else if(speech_synthesizer == PP_BROKEN)
to_chat(user,"Speech Synthesizer: " + span_warning("damaged"))
else
to_chat(user,"Speech Synthesizer: " + span_warning("missing"))
if(istype(I,/obj/item/paiparts/cell))
if(cell == PP_MISSING)
if(do_after(user, 3 SECONDS, target = src))
user.visible_message(span_notice("\The [user] installs \the [I] into \the [src]."),span_notice("You install \the [I] into \the [src]."))
cell = PP_FUNCTIONAL
user.drop_from_inventory(I)
qdel(I)
else
to_chat(user, span_warning("You would need to remove the installed [I] first!"))
if(istype(I,/obj/item/paiparts/processor))
if(processor == PP_MISSING)
if(do_after(user, 3 SECONDS, target = src))
user.visible_message(span_notice("\The [user] installs \the [I] into \the [src]."),span_notice("You install \the [I] into \the [src]."))
processor = PP_FUNCTIONAL
user.drop_from_inventory(I)
qdel(I)
else
to_chat(user, span_warning("You would need to remove the installed [I] first!"))
if(istype(I,/obj/item/paiparts/board))
if(board == PP_MISSING)
if(do_after(user, 3 SECONDS, target = src))
user.visible_message(span_notice("\The [user] installs \the [I] into \the [src]."),span_notice("You install \the [I] into \the [src]."))
board = PP_FUNCTIONAL
user.drop_from_inventory(I)
qdel(I)
else
to_chat(user, span_warning("You would need to remove the installed [I] first!"))
if(istype(I,/obj/item/paiparts/capacitor))
if(capacitor == PP_MISSING)
if(do_after(user, 3 SECONDS, target = src))
user.visible_message(span_notice("\The [user] installs \the [I] into \the [src]."),span_notice("You install \the [I] into \the [src]."))
capacitor = PP_FUNCTIONAL
user.drop_from_inventory(I)
qdel(I)
else
to_chat(user, span_warning("You would need to remove the installed [I] first!"))
if(istype(I,/obj/item/paiparts/projector))
if(projector == PP_MISSING)
if(do_after(user, 3 SECONDS, target = src))
user.visible_message(span_notice("\The [user] installs \the [I] into \the [src]."),span_notice("You install \the [I] into \the [src]."))
projector = PP_FUNCTIONAL
user.drop_from_inventory(I)
qdel(I)
else
to_chat(user, span_warning("You would need to remove the installed [I] first!"))
if(istype(I,/obj/item/paiparts/emitter))
if(emitter == PP_MISSING)
if(do_after(user, 3 SECONDS, target = src))
user.visible_message(span_notice("\The [user] installs \the [I] into \the [src]."),span_notice("You install \the [I] into \the [src]."))
emitter = PP_FUNCTIONAL
user.drop_from_inventory(I)
qdel(I)
else
to_chat(user, span_warning("You would need to remove the installed [I] first!"))
if(istype(I,/obj/item/paiparts/speech_synthesizer))
if(speech_synthesizer == PP_MISSING)
if(do_after(user, 3 SECONDS, target = src))
user.visible_message(span_notice("\The [user] installs \the [I] into \the [src]."),span_notice("You install \the [I] into \the [src]."))
speech_synthesizer = PP_FUNCTIONAL
user.drop_from_inventory(I)
qdel(I)
else
to_chat(user, span_warning("You would need to remove the installed [I] first!"))
/obj/item/paicard/attack_self(mob/user, callback)
. = ..(user)
if(.)
return TRUE
if(special_handling && !callback)
return FALSE
if(!panel_open)
tgui_interact(user)
return
var/list/parts = list()
if(cell != PP_MISSING)
parts |= "cell"
if(processor != PP_MISSING)
parts |= "processor"
if(board != PP_MISSING)
parts |= "board"
if(capacitor != PP_MISSING)
parts |= "capacitor"
if(projector != PP_MISSING)
parts |= "projector"
if(emitter != PP_MISSING)
parts |= "emitter"
if(speech_synthesizer != PP_MISSING)
parts |= "speech synthesizer"
var/choice = tgui_input_list(user, "Which part would you like to remove?", "Remove part", parts)
if(choice)
playsound(src, 'sound/items/pickup/component.ogg', vary = TRUE)
else
return
if(!do_after(user, 3 SECONDS, target = src))
return
switch(choice)
if("cell")
if(cell == PP_FUNCTIONAL)
new /obj/item/paiparts/cell(get_turf(user))
else
new /obj/item/paiparts(get_turf(user))
user.visible_message(span_warning("\The [user] removes \the [choice] from \the [src]."),span_warning("You remove \the [choice] from \the [src]."))
cell = PP_MISSING
if("processor")
if(processor == PP_FUNCTIONAL)
new /obj/item/paiparts/processor(get_turf(user))
else
new /obj/item/paiparts(get_turf(user))
user.visible_message(span_warning("\The [user] removes \the [choice] from \the [src]."),span_warning("You remove \the [choice] from \the [src]."))
processor = PP_MISSING
if("board")
board = PP_MISSING
if(board == PP_FUNCTIONAL)
new /obj/item/paiparts/board(get_turf(user))
else
new /obj/item/paiparts(get_turf(user))
user.visible_message(span_warning("\The [user] removes \the [choice] from \the [src]."),span_warning("You remove \the [choice] from \the [src]."))
if("capacitor")
if(capacitor == PP_FUNCTIONAL)
new /obj/item/paiparts/capacitor(get_turf(user))
else
new /obj/item/paiparts(get_turf(user))
user.visible_message(span_warning("\The [user] removes \the [choice] from \the [src]."),span_warning("You remove \the [choice] from \the [src]."))
capacitor = PP_MISSING
if("projector")
if(projector == PP_FUNCTIONAL)
new /obj/item/paiparts/projector(get_turf(user))
else
new /obj/item/paiparts(get_turf(user))
user.visible_message(span_warning("\The [user] removes \the [choice] from \the [src]."),span_warning("You remove \the [choice] from \the [src]."))
projector = PP_MISSING
if("emitter")
if(emitter == PP_FUNCTIONAL)
new /obj/item/paiparts/emitter(get_turf(user))
else
new /obj/item/paiparts(get_turf(user))
user.visible_message(span_warning("\The [user] removes \the [choice] from \the [src]."),span_warning("You remove \the [choice] from \the [src]."))
emitter = PP_MISSING
if("speech synthesizer")
if(speech_synthesizer == PP_FUNCTIONAL)
new /obj/item/paiparts/speech_synthesizer(get_turf(user))
else
new /obj/item/paiparts(get_turf(user))
user.visible_message(span_warning("\The [user] removes \the [choice] from \the [src]."),span_warning("You remove \the [choice] from \the [src]."))
speech_synthesizer = PP_MISSING
/obj/item/paicard/proc/death_damage()
var/number = rand(1,4)
while(number)
number --
switch(rand(1,4))
if(1)
cell = PP_BROKEN
if(2)
processor = PP_BROKEN
if(3)
board = PP_BROKEN
if(4)
capacitor = PP_BROKEN
/obj/item/paicard/proc/damage_random_component(nonfatal = FALSE)
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
s.set_up(2, 1, src)
s.start()
if(prob(80) || nonfatal) //Way more likely to be non-fatal part damage
switch(rand(1,3))
if(1)
projector = PP_BROKEN
if(2)
emitter = PP_BROKEN
if(3)
speech_synthesizer = PP_BROKEN
else
switch(rand(1,4))
if(1)
cell = PP_BROKEN
if(2)
processor = PP_BROKEN
if(3)
board = PP_BROKEN
if(4)
capacitor = PP_BROKEN
/obj/item/paicard/proc/is_damage_critical()
if(cell != PP_FUNCTIONAL || processor != PP_FUNCTIONAL || board != PP_FUNCTIONAL || capacitor != PP_FUNCTIONAL)
return TRUE
return FALSE
/obj/item/paicard/emag_act(remaining_charges, mob/user, emag_source)
. = ..()
if(!pai)
if(!emagged)
to_chat(user, span_warning("Without a pAI inhabiting \the [src] nothing happens."))
return
if(!emagged)
if(user)
to_chat(user, span_notice("\The [src] buzzes and beeps."))
playsound(src, 'sound/machines/buzzbeep.ogg', 50, 0)
emagged = TRUE
// Add tools
if(has_emag_toolkit)
multitool = new /obj/item/multitool(src)
signaler = new /obj/item/assembly/signaler(src)
return 1
///////////////////////////////
//////////pAI Parts //////////
///////////////////////////////
/obj/item/paiparts
name = "broken pAI component"
desc = "It's broken scrap from a pAI card!"
icon = 'icons/obj/paicard.dmi'
icon_state = "broken"
pickup_sound = 'sound/items/pickup/card.ogg'
drop_sound = 'sound/items/drop/card.ogg'
/obj/item/paiparts/Initialize(mapload)
. = ..()
pixel_x = rand(-10,10)
pixel_y = rand(-10,10)
/obj/item/paiparts/cell
name = "pAI power cell"
desc = "It's very small and efficient! It powers the pAI!"
icon_state = "cell"
/obj/item/paiparts/processor
name = "pAI processor"
desc = "It's the brain of your computer friend!"
icon_state = "processor"
/obj/item/paiparts/board
name = "pAI board"
desc = "It's the thing all the other parts get attatched to!"
icon_state = "board"
/obj/item/paiparts/capacitor
name = "pAI capacitor"
desc = "It helps regulate power flow!"
icon_state = "capacitor"
/obj/item/paiparts/projector
name = "pAI projector"
desc = "It projects the pAI's form!"
icon_state = "projector"
/obj/item/paiparts/emitter
name = "pAI emitter"
desc = "It emits the fields to help the pAI get around!"
icon_state = "emitter"
/obj/item/paiparts/speech_synthesizer
name = "pAI speech synthesizer"
desc = "It's a little voice box!"
icon_state = "speech_synthesizer"
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// VoreEdit: Living Machine Stuff after this.
// This adds a var and proc for all machines to take a pAI. (The pAI can't control anything, it's just for RP.)
// You need to add usage of the proc to each machine to actually add support. For an example of this, see code\modules\food\kitchen\microwave.dm
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/obj/machinery
var/obj/item/paicard/paicard = null
/obj/machinery/proc/insertpai(mob/user, obj/item/paicard/card)
//var/obj/item/paicard/card = I
var/mob/living/silicon/pai/AI = card.pai
if(paicard)
to_chat(user, span_notice("This bot is already under PAI Control!"))
return
if(!istype(card)) // TODO: Add sleevecard support.
return
if(!card.pai)
to_chat(user, span_notice("This card does not currently have a personality!"))
return
paicard = card
user.unEquip(card)
card.forceMove(src)
AI.reset_perspective(src) // focus this machine
to_chat(AI, span_notice("Your location is [card.loc].")) // DEBUG. TODO: Make unfolding the chassis trigger an eject.
name = AI.name
to_chat(AI, span_notice("You feel a tingle in your circuits as your systems interface with \the [initial(src.name)]."))
/obj/machinery/proc/ejectpai(mob/user)
if(paicard)
paicard.forceMove(get_turf(src))
var/mob/living/silicon/pai/AI = paicard.pai
AI.reset_perspective() // return to the card
paicard = null
name = initial(src.name)
to_chat(AI, span_notice("You feel a tad claustrophobic as your mind closes back into your card, ejecting from \the [initial(src.name)]."))
if(user)
to_chat(user, span_notice("You eject the card from \the [initial(src.name)]."))
///////////////////////////////
//////////pAI Radios//////////
///////////////////////////////
//Thanks heroman!
/obj/item/radio/borg/pai
name = "integrated radio"
icon = 'icons/obj/robot_component.dmi' // Cyborgs radio icons should look like the component.
icon_state = "radio"
loudspeaker = FALSE
/obj/item/radio/borg/pai/attackby(obj/item/W, mob/user)
return
/obj/item/radio/borg/pai/recalculateChannels()
if(!istype(loc,/obj/item/paicard))
return
var/obj/item/paicard/card = loc
secure_radio_connections = list()
channels = list()
for(var/internal_chan in internal_channels)
var/ch_name = GLOB.radio_channels_by_freq[internal_chan]
if(has_channel_access(card.pai, internal_chan))
channels += ch_name
channels[ch_name] = 1
secure_radio_connections[ch_name] = SSradio.add_object(src, GLOB.radiochannels[ch_name], RADIO_CHAT)
/obj/item/paicard/typeb
name = "personal AI device"
icon = 'icons/obj/paicard.dmi'
/obj/random/paicard
name = "personal AI device spawner"
icon = 'icons/obj/paicard.dmi'
icon_state = "pai"
/obj/random/paicard/item_to_spawn()
return pick(/obj/item/paicard ,/obj/item/paicard/typeb)
/obj/item/paicard/digest_act(var/atom/movable/item_storage = null)
if(pai.digestable)
return ..()