ai + mech

This commit is contained in:
SandPoot
2023-12-17 23:15:59 -03:00
parent 3e45c18ab4
commit 7b62dbe7bb
74 changed files with 700 additions and 306 deletions
+55 -11
View File
@@ -47,8 +47,20 @@
if(brain)
to_chat(user, "<span class='warning'>There's already a brain in the MMI!</span>")
return
if(!newbrain.brainmob)
to_chat(user, "<span class='warning'>You aren't sure where this brain came from, but you're pretty sure it's a useless brain!</span>")
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."
+1 -1
View File
@@ -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)
+89 -18
View File
@@ -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, "<span class='userdanger'>You have been installed as an AI! </span>")
to_chat(src, "<span class='danger'>You must obey your silicon laws above all else. Your objectives will consider you to be dead.</span>")
if(!mind.has_ever_been_ai)
mind.has_ever_been_ai = TRUE
to_chat(src, "<B>You are playing the station's AI. The AI cannot move, but can interact with many objects while viewing them (through cameras).</B>")
to_chat(src, "<B>To look at other parts of the station, click on yourself to get a camera menu.</B>")
@@ -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, "<b>You are now [is_anchored ? "" : "un"]anchored.</b>")
/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, "<b>You are now [is_anchored ? "" : "un"]anchored.</b>")
// 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, "<span class='warning'>You are already loaded into an onboard computer!</span>")
to_chat(src, span_warning("You are already loaded into an onboard computer!"))
return
if(!GLOB.cameranet.checkCameraVis(M))
to_chat(src, "<span class='warning'>Exosuit is no longer near active cameras.</span>")
to_chat(src, span_warning("Exosuit is no longer near active cameras."))
return
if(!isturf(loc))
to_chat(src, "<span class='warning'>You aren't in your core!</span>")
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, "<span class='warning'>No intelligence patterns detected.</span>" )
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")
@@ -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
+7 -3
View File
@@ -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)
@@ -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
@@ -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
@@ -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")
@@ -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