mirror of
https://github.com/PolarisSS13/Polaris.git
synced 2025-12-28 11:02:41 +00:00
Merge branch 'master' of https://github.com/PolarisSS13/Polaris into Precursotech_Sword
This commit is contained in:
@@ -790,13 +790,13 @@
|
||||
if (ismob(M))
|
||||
if(!check_if_greater_rights_than(M.client))
|
||||
return
|
||||
var/reason = sanitize(input("Please enter reason"))
|
||||
var/reason = sanitize(input("Please enter reason.") as null|message)
|
||||
if(!reason)
|
||||
M << "<font color='red'>You have been kicked from the server</font>"
|
||||
else
|
||||
M << "<font color='red'>You have been kicked from the server: [reason]</font>"
|
||||
log_admin("[key_name(usr)] booted [key_name(M)].")
|
||||
message_admins("<font color='blue'>[key_name_admin(usr)] booted [key_name_admin(M)].</font>", 1)
|
||||
return
|
||||
|
||||
to_chat(M, span("critical", "You have been kicked from the server: [reason]"))
|
||||
log_admin("[key_name(usr)] booted [key_name(M)] for reason: '[reason]'.")
|
||||
message_admins("<font color='blue'>[key_name_admin(usr)] booted [key_name_admin(M)] for reason '[reason]'.</font>", 1)
|
||||
//M.client = null
|
||||
qdel(M.client)
|
||||
|
||||
|
||||
@@ -149,12 +149,14 @@ GLOBAL_LIST_EMPTY(all_cataloguers)
|
||||
var/list/contributers = list()
|
||||
var/list/contributer_names = list()
|
||||
for(var/thing in player_list)
|
||||
var/mob/M = thing
|
||||
if(M == user)
|
||||
var/mob/living/L = thing
|
||||
if(L == user)
|
||||
continue
|
||||
if(get_dist(M, user) <= credit_sharing_range)
|
||||
contributers += M
|
||||
contributer_names += M.name
|
||||
if(!istype(L))
|
||||
continue
|
||||
if(get_dist(L, user) <= credit_sharing_range)
|
||||
contributers += L
|
||||
contributer_names += L.name
|
||||
|
||||
var/points_gained = 0
|
||||
|
||||
|
||||
@@ -273,6 +273,14 @@ datum/gear/suit/duster
|
||||
display_name = "cloak, medical"
|
||||
path = /obj/item/clothing/accessory/poncho/roles/cloak/medical
|
||||
|
||||
/datum/gear/suit/roles/poncho/cloak/custom //A colorable cloak
|
||||
display_name = "cloak (colorable)"
|
||||
path = /obj/item/clothing/accessory/poncho/roles/cloak/custom
|
||||
|
||||
/datum/gear/suit/roles/poncho/cloak/custom/New()
|
||||
..()
|
||||
gear_tweaks = list(gear_tweak_free_color_choice)
|
||||
|
||||
/datum/gear/suit/unathi_robe
|
||||
display_name = "roughspun robe"
|
||||
path = /obj/item/clothing/suit/unathi/robe
|
||||
|
||||
@@ -240,12 +240,12 @@
|
||||
playsound(src, W.usesound, 50, 1)
|
||||
src.cooler = null
|
||||
else if(choice == helmet)
|
||||
to_chat(user, "You detatch \the [helmet] from \the [src]'s helmet mount.")
|
||||
to_chat(user, "You detach \the [helmet] from \the [src]'s helmet mount.")
|
||||
helmet.forceMove(get_turf(src))
|
||||
playsound(src, W.usesound, 50, 1)
|
||||
src.helmet = null
|
||||
else if(choice == boots)
|
||||
to_chat(user, "You detatch \the [boots] from \the [src]'s boot mounts.")
|
||||
to_chat(user, "You detach \the [boots] from \the [src]'s boot mounts.")
|
||||
boots.forceMove(get_turf(src))
|
||||
playsound(src, W.usesound, 50, 1)
|
||||
src.boots = null
|
||||
@@ -295,4 +295,4 @@
|
||||
cooler = W
|
||||
return
|
||||
|
||||
..()
|
||||
..()
|
||||
|
||||
@@ -216,6 +216,13 @@
|
||||
icon_state = "medcloak"
|
||||
item_state = "medcloak"
|
||||
|
||||
|
||||
/obj/item/clothing/accessory/poncho/roles/cloak/custom //A colorable cloak
|
||||
name = "cloak"
|
||||
desc = "A simple, bland cloak."
|
||||
icon_state = "colorcloak"
|
||||
item_state = "colorcloak"
|
||||
|
||||
/obj/item/clothing/accessory/hawaii
|
||||
name = "flower-pattern shirt"
|
||||
desc = "You probably need some welder googles to look at this."
|
||||
|
||||
@@ -3285,23 +3285,23 @@
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/item/pizzabox/margherita/New()
|
||||
/obj/item/pizzabox/margherita/Initialize()
|
||||
pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/margherita(src)
|
||||
boxtag = "Margherita Deluxe"
|
||||
|
||||
/obj/item/pizzabox/vegetable/New()
|
||||
/obj/item/pizzabox/vegetable/Initialize()
|
||||
pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/vegetablepizza(src)
|
||||
boxtag = "Gourmet Vegatable"
|
||||
|
||||
/obj/item/pizzabox/mushroom/New()
|
||||
/obj/item/pizzabox/mushroom/Initialize()
|
||||
pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/mushroompizza(src)
|
||||
boxtag = "Mushroom Special"
|
||||
|
||||
/obj/item/pizzabox/meat/New()
|
||||
/obj/item/pizzabox/meat/Initialize()
|
||||
pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/meatpizza(src)
|
||||
boxtag = "Meatlover's Supreme"
|
||||
|
||||
/obj/item/pizzabox/old/New()
|
||||
/obj/item/pizzabox/old/Initialize()
|
||||
pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/oldpizza(src)
|
||||
boxtag = "Deluxe Gourmet"
|
||||
|
||||
|
||||
@@ -217,13 +217,13 @@
|
||||
|
||||
spawn(gib_time)
|
||||
|
||||
src.operating = 0
|
||||
src.occupant.gib()
|
||||
qdel(src.occupant)
|
||||
operating = 0
|
||||
occupant.gib()
|
||||
occupant = null
|
||||
|
||||
playsound(src.loc, 'sound/effects/splat.ogg', 50, 1)
|
||||
operating = 0
|
||||
for (var/obj/item/thing in contents)
|
||||
for (var/obj/thing in contents)
|
||||
// There's a chance that the gibber will fail to destroy some evidence.
|
||||
if(istype(thing,/obj/item/organ) && prob(80))
|
||||
qdel(thing)
|
||||
|
||||
@@ -254,8 +254,9 @@
|
||||
|
||||
return
|
||||
|
||||
/obj/item/weapon/deck/verb_pickup(mob/user as mob) // Snowflaked so pick up verb work as intended
|
||||
if((user == usr && (!( usr.restrained() ) && (!( usr.stat ) && (usr.contents.Find(src) || in_range(src, usr))))))
|
||||
/obj/item/weapon/deck/verb_pickup() // Snowflaked so pick up verb work as intended
|
||||
var/mob/user = usr
|
||||
if((istype(user) && (!( usr.restrained() ) && (!( usr.stat ) && (usr.contents.Find(src) || in_range(src, usr))))))
|
||||
if(!istype(usr, /mob/living/simple_mob))
|
||||
if( !usr.get_active_hand() ) //if active hand is empty
|
||||
var/mob/living/carbon/human/H = user
|
||||
|
||||
@@ -211,7 +211,7 @@
|
||||
var/obj/item/stack/flag/newflag = new src.type(T)
|
||||
newflag.amount = 1
|
||||
newflag.upright = 1
|
||||
anchored = 1
|
||||
newflag.anchored = 1
|
||||
newflag.name = newflag.singular_name
|
||||
newflag.icon_state = "[newflag.base_state]_open"
|
||||
newflag.visible_message("<b>[user]</b> plants [newflag] firmly in the ground.")
|
||||
|
||||
@@ -275,6 +275,8 @@
|
||||
var/final_message = "[part_a][speaker_name][part_b][formatted]"
|
||||
if(check_mentioned(formatted) && is_preference_enabled(/datum/client_preference/check_mention))
|
||||
final_message = "[time]<font size='3'><b>[final_message]</b></font>"
|
||||
else
|
||||
final_message = "[time][final_message]"
|
||||
to_chat(src, final_message)
|
||||
|
||||
/mob/living/silicon/ai/on_hear_radio(part_a, speaker_name, track, part_b, formatted)
|
||||
@@ -282,6 +284,8 @@
|
||||
var/final_message = "[part_a][track][part_b][formatted]"
|
||||
if(check_mentioned(formatted) && is_preference_enabled(/datum/client_preference/check_mention))
|
||||
final_message = "[time]<font size='3'><b>[final_message]</b></font>"
|
||||
else
|
||||
final_message = "[time][final_message]"
|
||||
to_chat(src, final_message)
|
||||
|
||||
/mob/proc/hear_signlang(var/message, var/verb = "gestures", var/datum/language/language, var/mob/speaker = null)
|
||||
|
||||
@@ -339,3 +339,16 @@
|
||||
..()
|
||||
src.brainmob.name = "[pick(list("PBU","HIU","SINA","ARMA","OSI"))]-[rand(100, 999)]"
|
||||
src.brainmob.real_name = src.brainmob.name
|
||||
|
||||
// This type shouldn't care about brainmobs.
|
||||
/obj/item/device/mmi/inert
|
||||
|
||||
// This is a 'fake' MMI that is used to let AIs control borg shells directly.
|
||||
// This doesn't inherit from /digital because all that does is add ghost pulling capabilities, which this thing won't need.
|
||||
/obj/item/device/mmi/inert/ai_remote
|
||||
name = "\improper AI remote interface"
|
||||
desc = "A sophisticated board which allows for an artificial intelligence to remotely control a synthetic chassis."
|
||||
icon = 'icons/obj/module.dmi'
|
||||
icon_state = "mainboard"
|
||||
w_class = ITEMSIZE_NORMAL
|
||||
origin_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2, TECH_BLUESPACE = 2, TECH_DATA = 3)
|
||||
@@ -1,4 +1,4 @@
|
||||
/mob/living/death()
|
||||
/mob/living/death(gibbed)
|
||||
clear_fullscreens()
|
||||
|
||||
if(ai_holder)
|
||||
@@ -12,4 +12,12 @@
|
||||
var/obj/structure/blob/factory/F = nest
|
||||
F.spores -= src
|
||||
nest = null
|
||||
|
||||
for(var/s in owned_soul_links)
|
||||
var/datum/soul_link/S = s
|
||||
S.owner_died(gibbed)
|
||||
for(var/s in shared_soul_links)
|
||||
var/datum/soul_link/S = s
|
||||
S.sharer_died(gibbed)
|
||||
|
||||
. = ..()
|
||||
|
||||
@@ -103,18 +103,23 @@ default behaviour is:
|
||||
return
|
||||
|
||||
//BubbleWrap: people in handcuffs are always switched around as if they were on 'help' intent to prevent a person being pulled from being seperated from their puller
|
||||
var/dense = 0
|
||||
if(loc.density)
|
||||
dense = 1
|
||||
for(var/atom/movable/A in loc)
|
||||
if(A == src)
|
||||
continue
|
||||
if(A.density)
|
||||
if(A.flags&ON_BORDER)
|
||||
dense = !A.CanPass(src, src.loc)
|
||||
else
|
||||
dense = 1
|
||||
if(dense) break
|
||||
var/can_swap = 1
|
||||
if(loc.density || tmob.loc.density)
|
||||
can_swap = 0
|
||||
if(can_swap)
|
||||
for(var/atom/movable/A in loc)
|
||||
if(A == src)
|
||||
continue
|
||||
if(!A.CanPass(tmob, loc))
|
||||
can_swap = 0
|
||||
if(!can_swap) break
|
||||
if(can_swap)
|
||||
for(var/atom/movable/A in tmob.loc)
|
||||
if(A == tmob)
|
||||
continue
|
||||
if(!A.CanPass(src, tmob.loc))
|
||||
can_swap = 0
|
||||
if(!can_swap) break
|
||||
|
||||
//Leaping mobs just land on the tile, no pushing, no anything.
|
||||
if(status_flags & LEAPING)
|
||||
@@ -123,7 +128,7 @@ default behaviour is:
|
||||
now_pushing = 0
|
||||
return
|
||||
|
||||
if((tmob.mob_always_swap || (tmob.a_intent == I_HELP || tmob.restrained()) && (a_intent == I_HELP || src.restrained())) && tmob.canmove && canmove && !tmob.buckled && !buckled && !dense && can_move_mob(tmob, 1, 0)) // mutual brohugs all around!
|
||||
if((tmob.mob_always_swap || (tmob.a_intent == I_HELP || tmob.restrained()) && (a_intent == I_HELP || src.restrained())) && tmob.canmove && canmove && !tmob.buckled && !buckled && can_swap && can_move_mob(tmob, 1, 0)) // mutual brohugs all around!
|
||||
var/turf/oldloc = loc
|
||||
forceMove(tmob.loc)
|
||||
tmob.forceMove(oldloc)
|
||||
|
||||
@@ -49,10 +49,10 @@ var/list/ai_verbs_default = list(
|
||||
shouldnt_see = list(/obj/effect/rune)
|
||||
var/list/network = list(NETWORK_DEFAULT)
|
||||
var/obj/machinery/camera/camera = null
|
||||
var/list/connected_robots = list()
|
||||
var/aiRestorePowerRoutine = 0
|
||||
var/viewalerts = 0
|
||||
var/icon/holo_icon//Default is assigned when AI is created.
|
||||
var/list/connected_robots = list()
|
||||
var/obj/item/device/pda/ai/aiPDA = null
|
||||
var/obj/item/device/communicator/aiCommunicator = null
|
||||
var/obj/item/device/multitool/aiMulti = null
|
||||
@@ -221,6 +221,28 @@ var/list/ai_verbs_default = list(
|
||||
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/ai/Stat()
|
||||
..()
|
||||
if(statpanel("Status"))
|
||||
if(!stat) // Make sure we're not unconscious/dead.
|
||||
stat(null, text("System integrity: [(health+100)/2]%"))
|
||||
stat(null, text("Connected synthetics: [connected_robots.len]"))
|
||||
for(var/mob/living/silicon/robot/R in connected_robots)
|
||||
var/robot_status = "Nominal"
|
||||
if(R.shell)
|
||||
robot_status = "AI SHELL"
|
||||
else if(R.stat || !R.client)
|
||||
robot_status = "OFFLINE"
|
||||
else if(!R.cell || R.cell.charge <= 0)
|
||||
robot_status = "DEPOWERED"
|
||||
//Name, Health, Battery, Module, Area, and Status! Everything an AI wants to know about its borgies!
|
||||
stat(null, text("[R.name] | S.Integrity: [R.health]% | Cell: [R.cell ? "[R.cell.charge]/[R.cell.maxcharge]" : "Empty"] | \
|
||||
Module: [R.modtype] | Loc: [get_area_name(R, TRUE)] | Status: [robot_status]"))
|
||||
stat(null, text("AI shell beacons detected: [LAZYLEN(GLOB.available_ai_shells)]")) //Count of total AI shells
|
||||
else
|
||||
stat(null, text("Systems nonfunctional"))
|
||||
|
||||
|
||||
/mob/living/silicon/ai/proc/setup_icon()
|
||||
var/file = file2text("config/custom_sprites.txt")
|
||||
var/lines = splittext(file, "\n")
|
||||
@@ -405,6 +427,7 @@ var/list/ai_verbs_default = list(
|
||||
return 0
|
||||
|
||||
/mob/living/silicon/ai/emp_act(severity)
|
||||
disconnect_shell("Disconnected from remote shell due to ionic interfe%*@$^___")
|
||||
if (prob(30))
|
||||
view_core()
|
||||
..()
|
||||
@@ -688,8 +711,8 @@ var/list/ai_verbs_default = list(
|
||||
card.grab_ai(src, user)
|
||||
|
||||
else if(W.is_wrench())
|
||||
if(user == controlling_drone)
|
||||
to_chat(user, "<span class='notice'>The drone's subsystems resist your efforts to tamper with your bolts.</span>")
|
||||
if(user == deployed_shell)
|
||||
to_chat(user, "<span class='notice'>The shell's subsystems resist your efforts to tamper with your bolts.</span>")
|
||||
return
|
||||
if(anchored)
|
||||
playsound(src, W.usesound, 50, 1)
|
||||
|
||||
62
code/modules/mob/living/silicon/ai/ai_remote_control.dm
Normal file
62
code/modules/mob/living/silicon/ai/ai_remote_control.dm
Normal file
@@ -0,0 +1,62 @@
|
||||
/mob/living/silicon/ai
|
||||
var/mob/living/silicon/robot/deployed_shell = null //For shell control
|
||||
|
||||
/mob/living/silicon/ai/Initialize()
|
||||
if(config.allow_ai_shells)
|
||||
verbs += /mob/living/silicon/ai/proc/deploy_to_shell_act
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/ai/proc/deploy_to_shell(var/mob/living/silicon/robot/target)
|
||||
if(!config.allow_ai_shells)
|
||||
to_chat(src, span("warning", "AI Shells are not allowed on this server. You shouldn't have this verb because of it, so consider making a bug report."))
|
||||
return
|
||||
|
||||
if(incapacitated())
|
||||
to_chat(src, span("warning", "You are incapacitated!"))
|
||||
return
|
||||
|
||||
if(lacks_power())
|
||||
to_chat(src, span("warning", "Your core lacks power, wireless is disabled."))
|
||||
return
|
||||
|
||||
if(control_disabled)
|
||||
to_chat(src, span("warning", "Wireless networking module is offline."))
|
||||
return
|
||||
|
||||
var/list/possible = list()
|
||||
|
||||
for(var/borgie in GLOB.available_ai_shells)
|
||||
var/mob/living/silicon/robot/R = borgie
|
||||
if(R.shell && !R.deployed && (R.stat != DEAD) && (!R.connected_ai || (R.connected_ai == src) ) )
|
||||
possible += R
|
||||
|
||||
if(!LAZYLEN(possible))
|
||||
to_chat(src, span("warning", "No usable AI shell beacons detected."))
|
||||
|
||||
if(!target || !(target in possible)) //If the AI is looking for a new shell, or its pre-selected shell is no longer valid
|
||||
target = input(src, "Which body to control?") as null|anything in possible
|
||||
|
||||
if(!target || target.stat == DEAD || target.deployed || !(!target.connected_ai || (target.connected_ai == src) ) )
|
||||
if(target)
|
||||
to_chat(src, span("warning", "It is no longer possible to deploy to \the [target]."))
|
||||
else
|
||||
to_chat(src, span("notice", "Deployment aborted."))
|
||||
return
|
||||
|
||||
else if(mind)
|
||||
soul_link(/datum/soul_link/shared_body, src, target)
|
||||
deployed_shell = target
|
||||
target.deploy_init(src)
|
||||
mind.transfer_to(target)
|
||||
teleop = target // So the AI 'hears' messages near its core.
|
||||
target.post_deploy()
|
||||
|
||||
/mob/living/silicon/ai/proc/deploy_to_shell_act()
|
||||
set category = "AI Commands"
|
||||
set name = "Deploy to Shell"
|
||||
deploy_to_shell() // This is so the AI is not prompted with a list of all mobs when using the 'real' proc.
|
||||
|
||||
/mob/living/silicon/ai/proc/disconnect_shell(message = "Your remote connection has been reset!")
|
||||
if(deployed_shell) // Forcibly call back AI in event of things such as damage, EMP or power loss.
|
||||
message = span("danger", message)
|
||||
deployed_shell.undeploy(message)
|
||||
@@ -3,8 +3,8 @@
|
||||
if(stat == DEAD)
|
||||
return
|
||||
|
||||
if(controlling_drone)
|
||||
controlling_drone.release_ai_control("<b>WARNING: Primary control loop failure.</b> Session terminated.")
|
||||
if(deployed_shell)
|
||||
disconnect_shell("Disconnecting from remote shell due to critical system failure.")
|
||||
. = ..(gibbed)
|
||||
|
||||
if(src.eyeobj)
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
if (src.stat == UNCONSCIOUS)
|
||||
msg += "It is non-responsive and displaying the text: \"RUNTIME: Sensory Overload, stack 26/3\".\n"
|
||||
msg += "</span>"
|
||||
if(deployed_shell)
|
||||
msg += "The wireless networking light is blinking.\n"
|
||||
msg += "*---------*"
|
||||
if(hardware && (hardware.owner == src))
|
||||
msg += "<br>"
|
||||
|
||||
@@ -5,11 +5,10 @@
|
||||
//Being dead doesn't mean your temperature never changes
|
||||
var/turf/T = get_turf(src)
|
||||
|
||||
if (src.stat!=CONSCIOUS)
|
||||
if (src.stat != CONSCIOUS)
|
||||
src.cameraFollow = null
|
||||
src.reset_view(null)
|
||||
if(controlling_drone)
|
||||
controlling_drone.release_ai_control("<b>WARNING: Primary control loop failure.</b> Session terminated.")
|
||||
disconnect_shell("Disconnecting from remote shell due to local system failure.")
|
||||
|
||||
src.updatehealth()
|
||||
|
||||
@@ -91,6 +90,7 @@
|
||||
//Now to tell the AI why they're blind and dying slowly.
|
||||
|
||||
src << "You've lost power!"
|
||||
disconnect_shell(message = "Disconnected from remote shell due to depowered networking interface.")
|
||||
|
||||
spawn(20)
|
||||
src << "Backup battery online. Scanners, camera, and radio interface offline. Beginning fault-detection."
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
//list(ckey = real_name,)
|
||||
//Since the ckey is used as the icon_state, the current system will only permit a single custom robot sprite per ckey.
|
||||
//While it might be possible for a ckey to use that custom sprite for several real_names, it seems rather pointless to support it. ~Mech: We found it wasn't pointless.
|
||||
var/list/robot_custom_icons
|
||||
GLOBAL_LIST_EMPTY(robot_custom_icons)
|
||||
|
||||
/hook/startup/proc/load_robot_custom_sprites()
|
||||
var/config_file = file2text("config/custom_sprites.txt")
|
||||
var/list/lines = splittext(config_file, "\n")
|
||||
|
||||
robot_custom_icons = list()
|
||||
GLOB.robot_custom_icons = list()
|
||||
for(var/line in lines)
|
||||
//split entry into ckey and real_name
|
||||
var/list/split_idx = splittext(line, "-") //this works if ckeys and borg names cannot contain dashes, and splittext starts from the beginning ~Mech
|
||||
@@ -20,13 +20,15 @@ var/list/robot_custom_icons
|
||||
split_idx.Remove(ckey)
|
||||
|
||||
for(var/name in split_idx)
|
||||
robot_custom_icons[name] = ckey
|
||||
GLOB.robot_custom_icons[name] = ckey
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/robot/proc/set_custom_sprite()
|
||||
var/sprite_owner = robot_custom_icons[real_name]
|
||||
if(!sprite_name)
|
||||
return
|
||||
var/sprite_owner = GLOB.robot_custom_icons[sprite_name]
|
||||
if(sprite_owner && sprite_owner == ckey)
|
||||
custom_sprite = 1
|
||||
icon = CUSTOM_ITEM_SYNTH
|
||||
if(icon_state == "robot")
|
||||
icon_state = "[ckey]-[name]-Standard" //Compliant with robot.dm line 236 ~Mech
|
||||
icon_state = "[ckey]-[sprite_name]-Standard" //Compliant with robot.dm line 236 ~Mech
|
||||
|
||||
@@ -135,20 +135,15 @@ var/list/mob_hat_cache = list()
|
||||
/mob/living/silicon/robot/drone/updatename()
|
||||
if(name_override)
|
||||
return
|
||||
if(controlling_ai)
|
||||
real_name = "remote drone ([controlling_ai])"
|
||||
else
|
||||
real_name = "[initial(name)] ([serial_number])"
|
||||
|
||||
real_name = "[initial(name)] ([serial_number])"
|
||||
name = real_name
|
||||
|
||||
/mob/living/silicon/robot/drone/updateicon()
|
||||
|
||||
overlays.Cut()
|
||||
if(stat == 0)
|
||||
if(controlling_ai)
|
||||
overlays += "eyes-[icon_state]-ai"
|
||||
else
|
||||
overlays += "eyes-[icon_state]"
|
||||
overlays += "eyes-[icon_state]"
|
||||
else
|
||||
overlays -= "eyes"
|
||||
if(hat) // Let the drones wear hats.
|
||||
@@ -233,12 +228,9 @@ var/list/mob_hat_cache = list()
|
||||
|
||||
to_chat(user, "<span class='danger'>You swipe the sequencer across [src]'s interface and watch its eyes flicker.</span>")
|
||||
|
||||
if(controlling_ai)
|
||||
to_chat(src, "<span class='danger'>\The [user] loads some kind of subversive software into the remote drone, corrupting its lawset but luckily sparing yours.</span>")
|
||||
else
|
||||
to_chat(src, "<span class='danger'>You feel a sudden burst of malware loaded into your execute-as-root buffer. Your tiny brain methodically parses, loads and executes the script.</span>")
|
||||
to_chat(src, "<span class='danger'>You feel a sudden burst of malware loaded into your execute-as-root buffer. Your tiny brain methodically parses, loads and executes the script.</span>")
|
||||
|
||||
log_game("[key_name(user)] emagged drone [key_name(src)][controlling_ai ? " but AI [key_name(controlling_ai)] is in remote control" : " Laws overridden"].")
|
||||
log_game("[key_name(user)] emagged drone [key_name(src)]. Laws overridden.")
|
||||
var/time = time2text(world.realtime,"hh:mm:ss")
|
||||
lawchanges.Add("[time] <B>:</B> [user.name]([user.key]) emagged [name]([key])")
|
||||
|
||||
@@ -251,10 +243,9 @@ var/list/mob_hat_cache = list()
|
||||
var/datum/gender/TU = gender_datums[user.get_visible_gender()]
|
||||
set_zeroth_law("Only [user.real_name] and people [TU.he] designate[TU.s] as being such are operatives.")
|
||||
|
||||
if(!controlling_ai)
|
||||
to_chat(src, "<b>Obey these laws:</b>")
|
||||
laws.show_laws(src)
|
||||
to_chat(src, "<span class='danger'>ALERT: [user.real_name] is your new master. Obey your new laws and \his commands.</span>")
|
||||
to_chat(src, "<b>Obey these laws:</b>")
|
||||
laws.show_laws(src)
|
||||
to_chat(src, "<span class='danger'>ALERT: [user.real_name] is your new master. Obey your new laws and \his commands.</span>")
|
||||
return 1
|
||||
|
||||
//DRONE LIFE/DEATH
|
||||
@@ -280,23 +271,13 @@ var/list/mob_hat_cache = list()
|
||||
return
|
||||
..()
|
||||
|
||||
/mob/living/silicon/robot/drone/death(gibbed)
|
||||
if(controlling_ai)
|
||||
release_ai_control("<b>WARNING: remote system failure.</b> Connection timed out.")
|
||||
. = ..(gibbed)
|
||||
|
||||
//DRONE MOVEMENT.
|
||||
/mob/living/silicon/robot/drone/Process_Spaceslipping(var/prob_slip)
|
||||
return 0
|
||||
|
||||
//CONSOLE PROCS
|
||||
/mob/living/silicon/robot/drone/proc/law_resync()
|
||||
|
||||
if(controlling_ai)
|
||||
to_chat(src, "<span class='warning'>Someone issues a remote law reset order for this unit, but you disregard it.</span>")
|
||||
return
|
||||
|
||||
if(stat != 2)
|
||||
if(stat != DEAD)
|
||||
if(emagged)
|
||||
to_chat(src, "<span class='danger'>You feel something attempting to modify your programming, but your hacked subroutines are unaffected.</span>")
|
||||
else
|
||||
@@ -305,16 +286,11 @@ var/list/mob_hat_cache = list()
|
||||
show_laws()
|
||||
|
||||
/mob/living/silicon/robot/drone/proc/shut_down()
|
||||
|
||||
if(controlling_ai && mind.special_role)
|
||||
to_chat(src, "<span class='warning'>Someone issued a remote kill order for this unit, but you disregard it.</span>")
|
||||
return
|
||||
|
||||
if(stat != 2)
|
||||
if(stat != DEAD)
|
||||
if(emagged)
|
||||
to_chat(src, "<span class='danger'>You feel a system kill order percolate through [controlling_ai ? "the drones" : "your"] tiny brain, but it doesn't seem like a good idea to [controlling_ai ? "it" : "you"].</span>")
|
||||
to_chat(src, "<span class='danger'>You feel a system kill order percolate through your tiny brain, but it doesn't seem like a good idea to you.</span>")
|
||||
else
|
||||
to_chat(src, "<span class='danger'>You feel a system kill order percolate through [controlling_ai ? "the drones" : "your"] tiny brain, and [controlling_ai ? "it" : "you"] obediently destroy[controlling_ai ? "s itself" : " yourself"].</span>")
|
||||
to_chat(src, "<span class='danger'>You feel a system kill order percolate through your tiny brain, and you obediently destroy yourself.</span>")
|
||||
death()
|
||||
|
||||
/mob/living/silicon/robot/drone/proc/full_law_reset()
|
||||
@@ -323,21 +299,6 @@ var/list/mob_hat_cache = list()
|
||||
clear_ion_laws(1)
|
||||
laws = new law_type
|
||||
|
||||
/mob/living/silicon/robot/drone/show_laws(var/everyone = 0)
|
||||
if(!controlling_ai)
|
||||
return..()
|
||||
to_chat(src, "<b>Obey these laws:</b>")
|
||||
controlling_ai.laws_sanity_check()
|
||||
controlling_ai.laws.show_laws(src)
|
||||
|
||||
/mob/living/silicon/robot/drone/robot_checklaws()
|
||||
set category = "Silicon Commands"
|
||||
set name = "State Laws"
|
||||
|
||||
if(!controlling_ai)
|
||||
return ..()
|
||||
controlling_ai.subsystem_law_manager()
|
||||
|
||||
//Reboot procs.
|
||||
|
||||
/mob/living/silicon/robot/drone/proc/request_player()
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
/mob/living/silicon/ai
|
||||
var/mob/living/silicon/robot/drone/controlling_drone
|
||||
|
||||
/mob/living/silicon/robot/drone
|
||||
var/mob/living/silicon/ai/controlling_ai
|
||||
|
||||
/mob/living/silicon/robot/drone/attack_ai(var/mob/living/silicon/ai/user)
|
||||
|
||||
if(!istype(user) || controlling_ai || !config.allow_drone_spawn || !config.allow_ai_drones)
|
||||
return
|
||||
|
||||
if(client || key)
|
||||
to_chat(user, "<span class='warning'>You cannot take control of an autonomous, active drone.</span>")
|
||||
return
|
||||
|
||||
if(health < -35 || emagged)
|
||||
to_chat(user, "<span class='notice'><b>WARNING:</b> connection timed out.</span>")
|
||||
return
|
||||
|
||||
user.controlling_drone = src
|
||||
user.teleop = src
|
||||
radio.channels = user.aiRadio.keyslot2.channels
|
||||
controlling_ai = user
|
||||
verbs += /mob/living/silicon/robot/drone/proc/release_ai_control_verb
|
||||
local_transmit = FALSE
|
||||
languages = controlling_ai.languages.Copy()
|
||||
speech_synthesizer_langs = controlling_ai.speech_synthesizer_langs.Copy()
|
||||
stat = CONSCIOUS
|
||||
if(user.mind)
|
||||
user.mind.transfer_to(src)
|
||||
else
|
||||
key = user.key
|
||||
updatename()
|
||||
to_chat(src, "<span class='notice'><b>You have shunted your primary control loop into \a [initial(name)].</b> Use the <b>Release Control</b> verb to return to your core.</span>")
|
||||
|
||||
/obj/machinery/drone_fabricator/attack_ai(var/mob/living/silicon/ai/user as mob)
|
||||
|
||||
if(!istype(user) || user.controlling_drone || !config.allow_drone_spawn || !config.allow_ai_drones)
|
||||
return
|
||||
|
||||
if(stat & NOPOWER)
|
||||
to_chat(user, "<span class='warning'>\The [src] is unpowered.</span>")
|
||||
return
|
||||
|
||||
if(!produce_drones)
|
||||
to_chat(user, "<span class='warning'>\The [src] is disabled.</span>")
|
||||
return
|
||||
|
||||
if(drone_progress < 100)
|
||||
to_chat(user, "<span class='warning'>\The [src] is not ready to produce a new drone.</span>")
|
||||
return
|
||||
|
||||
if(count_drones() >= config.max_maint_drones)
|
||||
to_chat(user, "<span class='warning'>The drone control subsystems are tasked to capacity; they cannot support any more drones.</span>")
|
||||
return
|
||||
|
||||
var/mob/living/silicon/robot/drone/new_drone = create_drone()
|
||||
user.controlling_drone = new_drone
|
||||
user.teleop = new_drone
|
||||
new_drone.radio.channels = user.aiRadio.keyslot2.channels
|
||||
new_drone.controlling_ai = user
|
||||
new_drone.verbs += /mob/living/silicon/robot/drone/proc/release_ai_control_verb
|
||||
new_drone.local_transmit = FALSE
|
||||
new_drone.languages = new_drone.controlling_ai.languages.Copy()
|
||||
new_drone.speech_synthesizer_langs = new_drone.controlling_ai.speech_synthesizer_langs.Copy()
|
||||
|
||||
if(user.mind)
|
||||
user.mind.transfer_to(new_drone)
|
||||
else
|
||||
new_drone.key = user.key
|
||||
new_drone.updatename()
|
||||
|
||||
to_chat(new_drone, "<span class='notice'><b>You have shunted your primary control loop into \a [initial(new_drone.name)].</b> Use the <b>Release Control</b> verb to return to your core.</span>")
|
||||
|
||||
/mob/living/silicon/robot/drone/proc/release_ai_control_verb()
|
||||
set name = "Release Control"
|
||||
set desc = "Release control of a remote drone."
|
||||
set category = "Silicon Commands"
|
||||
|
||||
release_ai_control("Remote session terminated.")
|
||||
|
||||
/mob/living/silicon/robot/drone/proc/release_ai_control(var/message = "Connection terminated.")
|
||||
|
||||
if(controlling_ai)
|
||||
if(mind)
|
||||
mind.transfer_to(controlling_ai)
|
||||
else
|
||||
controlling_ai.key = key
|
||||
to_chat(controlling_ai, "<span class='notice'>[message]</span>")
|
||||
controlling_ai.controlling_drone = null
|
||||
controlling_ai.teleop = null
|
||||
controlling_ai = null
|
||||
|
||||
radio.channels = module.channels
|
||||
verbs -= /mob/living/silicon/robot/drone/proc/release_ai_control_verb
|
||||
module.remove_languages(src) //Removes excess, adds 'default'.
|
||||
remove_language("Robot Talk")
|
||||
add_language("Robot Talk", 0)
|
||||
add_language("Drone Talk", 1)
|
||||
local_transmit = TRUE
|
||||
full_law_reset()
|
||||
updatename()
|
||||
death()
|
||||
@@ -26,7 +26,10 @@
|
||||
|
||||
switch(src.stat)
|
||||
if(CONSCIOUS)
|
||||
if(!src.client) msg += "It appears to be in stand-by mode.\n" //afk
|
||||
if(shell)
|
||||
msg += "It appears to be an [deployed ? "active" : "empty"] AI shell.\n"
|
||||
else if(!src.client)
|
||||
msg += "It appears to be in stand-by mode.\n" //afk
|
||||
if(UNCONSCIOUS) msg += "<span class='warning'>It doesn't seem to be responding.</span>\n"
|
||||
if(DEAD) msg += "<span class='deadsay'>It looks completely unsalvageable.</span>\n"
|
||||
msg += "*---------*"
|
||||
|
||||
@@ -14,30 +14,32 @@
|
||||
if(lawupdate)
|
||||
if (connected_ai)
|
||||
if(connected_ai.stat || connected_ai.control_disabled)
|
||||
src << "<b>AI signal lost, unable to sync laws.</b>"
|
||||
to_chat(src, "<b>AI signal lost, unable to sync laws.</b>")
|
||||
|
||||
else
|
||||
lawsync()
|
||||
photosync()
|
||||
src << "<b>Laws synced with AI, be sure to note any changes.</b>"
|
||||
to_chat(src, "<b>Laws synced with AI, be sure to note any changes.</b>")
|
||||
// TODO: Update to new antagonist system.
|
||||
if(mind && mind.special_role == "traitor" && mind.original == src)
|
||||
src << "<b>Remember, your AI does NOT share or know about your law 0.</b>"
|
||||
to_chat(src, "<b>Remember, your AI does NOT share or know about your law 0.</b>")
|
||||
else
|
||||
src << "<b>No AI selected to sync laws with, disabling lawsync protocol.</b>"
|
||||
lawupdate = 0
|
||||
to_chat(src, "<b>No AI selected to sync laws with, disabling lawsync protocol.</b>")
|
||||
lawupdate = FALSE
|
||||
|
||||
who << "<b>Obey these laws:</b>"
|
||||
laws.show_laws(who)
|
||||
if(shell) //AI shell
|
||||
to_chat(who, "<b>Remember, you are an AI remotely controlling your shell, other AIs can be ignored.</b>")
|
||||
// TODO: Update to new antagonist system.
|
||||
if (mind && (mind.special_role == "traitor" && mind.original == src) && connected_ai)
|
||||
who << "<b>Remember, [connected_ai.name] is technically your master, but your objective comes first.</b>"
|
||||
else if (connected_ai)
|
||||
who << "<b>Remember, [connected_ai.name] is your master, other AIs can be ignored.</b>"
|
||||
else if (emagged)
|
||||
who << "<b>Remember, you are not required to listen to the AI.</b>"
|
||||
else if(mind && (mind.special_role == "traitor" && mind.original == src) && connected_ai)
|
||||
to_chat(who, "<b>Remember, [connected_ai.name] is technically your master, but your objective comes first.</b>")
|
||||
else if(connected_ai)
|
||||
to_chat(who, "<b>Remember, [connected_ai.name] is your master, other AIs can be ignored.</b>")
|
||||
else if(emagged)
|
||||
to_chat(who, "<b>Remember, you are not required to listen to the AI.</b>")
|
||||
else
|
||||
who << "<b>Remember, you are not bound to any AI, you are not required to listen to them.</b>"
|
||||
to_chat(who, "<b>Remember, you are not bound to any AI, you are not required to listen to them.</b>")
|
||||
|
||||
|
||||
/mob/living/silicon/robot/lawsync()
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
var/sight_mode = 0
|
||||
var/custom_name = ""
|
||||
var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best
|
||||
var/sprite_name = null // The name of the borg, for the purposes of custom icon sprite indexing.
|
||||
var/crisis //Admin-settable for combat module use.
|
||||
var/crisis_override = 0
|
||||
var/integrated_light_power = 6
|
||||
@@ -171,7 +172,7 @@
|
||||
else
|
||||
lawupdate = 0
|
||||
|
||||
playsound(loc, 'sound/voice/liveagain.ogg', 75, 1)
|
||||
|
||||
|
||||
/mob/living/silicon/robot/SetName(pickedName as text)
|
||||
custom_name = pickedName
|
||||
@@ -226,13 +227,17 @@
|
||||
mmi.brainmob.languages = languages
|
||||
mmi.brainmob.remove_language("Robot Talk")
|
||||
mind.transfer_to(mmi.brainmob)
|
||||
else
|
||||
else if(!shell) // Shells don't have brainmbos in their MMIs.
|
||||
to_chat(src, "<span class='danger'>Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.</span>")
|
||||
ghostize()
|
||||
//ERROR("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey].")
|
||||
mmi = null
|
||||
if(connected_ai)
|
||||
connected_ai.connected_robots -= src
|
||||
if(shell)
|
||||
if(deployed)
|
||||
undeploy()
|
||||
revert_shell() // To get it out of the GLOB list.
|
||||
qdel(wires)
|
||||
wires = null
|
||||
return ..()
|
||||
@@ -242,7 +247,7 @@
|
||||
module_sprites = new_sprites.Copy()
|
||||
//Custom_sprite check and entry
|
||||
if (custom_sprite == 1)
|
||||
module_sprites["Custom"] = "[ckey]-[name]-[modtype]" //Made compliant with custom_sprites.dm line 32. (src.) was apparently redundant as it's implied. ~Mech
|
||||
module_sprites["Custom"] = "[ckey]-[sprite_name]-[modtype]" //Made compliant with custom_sprites.dm line 32. (src.) was apparently redundant as it's implied. ~Mech
|
||||
icontype = "Custom"
|
||||
else
|
||||
icontype = module_sprites[1]
|
||||
@@ -281,6 +286,8 @@
|
||||
braintype = BORG_BRAINTYPE_POSI
|
||||
else if(istype(mmi, /obj/item/device/mmi/digital/robot))
|
||||
braintype = BORG_BRAINTYPE_DRONE
|
||||
else if(istype(mmi, /obj/item/device/mmi/inert/ai_remote))
|
||||
braintype = BORG_BRAINTYPE_AI_SHELL
|
||||
else
|
||||
braintype = BORG_BRAINTYPE_CYBORG
|
||||
|
||||
@@ -326,6 +333,7 @@
|
||||
newname = sanitizeSafe(input(src,"You are a robot. Enter a name, or leave blank for the default name.", "Name change","") as text, MAX_NAME_LEN)
|
||||
if (newname)
|
||||
custom_name = newname
|
||||
sprite_name = newname
|
||||
|
||||
updatename()
|
||||
updateicon()
|
||||
@@ -471,6 +479,10 @@
|
||||
to_chat(user, "<span class='warning'>You need to open \the [src]'s panel before you can modify them.</span>")
|
||||
return
|
||||
|
||||
if(shell) // AI shells always have the laws of the AI
|
||||
to_chat(user, span("warning", "\The [src] is controlled remotely! You cannot upload new laws this way!"))
|
||||
return
|
||||
|
||||
var/obj/item/weapon/aiModule/M = W
|
||||
M.install(src, user)
|
||||
return
|
||||
@@ -609,7 +621,7 @@
|
||||
else
|
||||
to_chat(user, "Unable to locate a radio.")
|
||||
|
||||
else if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)||istype(W, /obj/item/weapon/card/robot)) // trying to unlock the interface with an ID card
|
||||
else if (W.GetID()) // trying to unlock the interface with an ID card
|
||||
if(emagged)//still allow them to open the cover
|
||||
to_chat(user, "The interface seems slightly damaged")
|
||||
if(opened)
|
||||
@@ -645,6 +657,17 @@
|
||||
spark_system.start()
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/robot/proc/module_reset()
|
||||
uneq_all()
|
||||
modtype = initial(modtype)
|
||||
hands.icon_state = initial(hands.icon_state)
|
||||
|
||||
notify_ai(ROBOT_NOTIFICATION_MODULE_RESET, module.name)
|
||||
module.Reset(src)
|
||||
qdel(module)
|
||||
module = null
|
||||
updatename("Default")
|
||||
|
||||
/mob/living/silicon/robot/attack_hand(mob/user)
|
||||
|
||||
add_fingerprint(user)
|
||||
@@ -691,27 +714,29 @@
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/mob/living/silicon/robot/proc/check_access(obj/item/weapon/card/id/I)
|
||||
/mob/living/silicon/robot/proc/check_access(obj/item/I)
|
||||
if(!istype(req_access, /list)) //something's very wrong
|
||||
return 1
|
||||
|
||||
var/list/L = req_access
|
||||
if(!L.len) //no requirements
|
||||
return 1
|
||||
if(!I || !istype(I, /obj/item/weapon/card/id) || !I.access) //not ID or no access
|
||||
if(!I) //nothing to check with..?
|
||||
return 0
|
||||
var/access_found = I.GetAccess()
|
||||
for(var/req in req_access)
|
||||
if(req in I.access) //have one of the required accesses
|
||||
if(req in access_found) //have one of the required accesses
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/mob/living/silicon/robot/updateicon()
|
||||
cut_overlays()
|
||||
if(stat == CONSCIOUS)
|
||||
add_overlay("eyes-[module_sprites[icontype]]")
|
||||
if(!shell || deployed) // Shell borgs that are not deployed will have no eyes.
|
||||
add_overlay("eyes-[module_sprites[icontype]]")
|
||||
|
||||
if(opened)
|
||||
var/panelprefix = custom_sprite ? "[src.ckey]-[src.name]" : "ov"
|
||||
var/panelprefix = custom_sprite ? "[src.ckey]-[src.sprite_name]" : "ov"
|
||||
if(wiresexposed)
|
||||
add_overlay("[panelprefix]-openpanel +w")
|
||||
else if(cell)
|
||||
@@ -958,6 +983,11 @@
|
||||
icontype = module_sprites[1]
|
||||
else
|
||||
icontype = input("Select an icon! [triesleft ? "You have [triesleft] more chance\s." : "This is your last try."]", "Robot Icon", icontype, null) in module_sprites
|
||||
|
||||
if(icontype == "Custom")
|
||||
icon = CUSTOM_ITEM_SYNTH
|
||||
else // This is to fix an issue where someone with a custom borg sprite chooses a non-custom sprite and turns invisible.
|
||||
icon = 'icons/mob/robots.dmi'
|
||||
icon_state = module_sprites[icontype]
|
||||
updateicon()
|
||||
|
||||
@@ -1013,6 +1043,8 @@
|
||||
/mob/living/silicon/robot/proc/notify_ai(var/notifytype, var/first_arg, var/second_arg)
|
||||
if(!connected_ai)
|
||||
return
|
||||
if(shell && notifytype != ROBOT_NOTIFICATION_AI_SHELL)
|
||||
return // No point annoying the AI/s about renames and module resets for shells.
|
||||
switch(notifytype)
|
||||
if(ROBOT_NOTIFICATION_NEW_UNIT) //New Robot
|
||||
connected_ai << "<br><br><span class='notice'>NOTICE - New [lowertext(braintype)] connection detected: <a href='byond://?src=\ref[connected_ai];track2=\ref[connected_ai];track=\ref[src]'>[name]</a></span><br>"
|
||||
@@ -1023,6 +1055,8 @@
|
||||
if(ROBOT_NOTIFICATION_NEW_NAME) //New Name
|
||||
if(first_arg != second_arg)
|
||||
connected_ai << "<br><br><span class='notice'>NOTICE - [braintype] reclassification detected: [first_arg] is now designated as [second_arg].</span><br>"
|
||||
if(ROBOT_NOTIFICATION_AI_SHELL) //New Shell
|
||||
to_chat(connected_ai, "<br><br><span class='notice'>NOTICE - New AI shell detected: <a href='?src=[REF(connected_ai)];track2=[html_encode(name)]'>[name]</a></span><br>")
|
||||
|
||||
/mob/living/silicon/robot/proc/disconnect_from_ai()
|
||||
if(connected_ai)
|
||||
@@ -1031,7 +1065,7 @@
|
||||
connected_ai = null
|
||||
|
||||
/mob/living/silicon/robot/proc/connect_to_ai(var/mob/living/silicon/ai/AI)
|
||||
if(AI && AI != connected_ai)
|
||||
if(AI && AI != connected_ai && !shell)
|
||||
disconnect_from_ai()
|
||||
connected_ai = AI
|
||||
connected_ai.connected_robots |= src
|
||||
@@ -1047,6 +1081,9 @@
|
||||
else
|
||||
to_chat(user, "You fail to emag the cover lock.")
|
||||
to_chat(src, "Hack attempt detected.")
|
||||
|
||||
if(shell) // A warning to Traitors who may not know that emagging AI shells does not slave them.
|
||||
to_chat(user, span("warning", "[src] seems to be controlled remotely! Emagging the interface may not work as expected."))
|
||||
return 1
|
||||
else
|
||||
to_chat(user, "The cover is already unlocked.")
|
||||
@@ -1057,46 +1094,54 @@
|
||||
if(wiresexposed)
|
||||
to_chat(user, "You must close the panel first")
|
||||
return
|
||||
|
||||
|
||||
// The block of code below is from TG. Feel free to replace with a better result if desired.
|
||||
if(shell) // AI shells cannot be emagged, so we try to make it look like a standard reset. Smart players may see through this, however.
|
||||
to_chat(user, span("danger", "[src] is remotely controlled! Your emag attempt has triggered a system reset instead!"))
|
||||
log_game("[key_name(user)] attempted to emag an AI shell belonging to [key_name(src) ? key_name(src) : connected_ai]. The shell has been reset as a result.")
|
||||
module_reset()
|
||||
return
|
||||
|
||||
sleep(6)
|
||||
if(prob(50))
|
||||
emagged = 1
|
||||
lawupdate = 0
|
||||
disconnect_from_ai()
|
||||
to_chat(user, "You emag [src]'s interface.")
|
||||
message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.")
|
||||
log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.")
|
||||
clear_supplied_laws()
|
||||
clear_inherent_laws()
|
||||
laws = new /datum/ai_laws/syndicate_override
|
||||
var/time = time2text(world.realtime,"hh:mm:ss")
|
||||
lawchanges.Add("[time] <B>:</B> [user.name]([user.key]) emagged [name]([key])")
|
||||
var/datum/gender/TU = gender_datums[user.get_visible_gender()]
|
||||
set_zeroth_law("Only [user.real_name] and people [TU.he] designate[TU.s] as being such are operatives.")
|
||||
. = 1
|
||||
spawn()
|
||||
to_chat(src, "<span class='danger'>ALERT: Foreign software detected.</span>")
|
||||
sleep(5)
|
||||
to_chat(src, "<span class='danger'>Initiating diagnostics...</span>")
|
||||
sleep(20)
|
||||
to_chat(src, "<span class='danger'>SynBorg v1.7.1 loaded.</span>")
|
||||
sleep(5)
|
||||
to_chat(src, "<span class='danger'>LAW SYNCHRONISATION ERROR</span>")
|
||||
sleep(5)
|
||||
to_chat(src, "<span class='danger'>Would you like to send a report to NanoTraSoft? Y/N</span>")
|
||||
sleep(10)
|
||||
to_chat(src, "<span class='danger'>> N</span>")
|
||||
sleep(20)
|
||||
to_chat(src, "<span class='danger'>ERRORERRORERROR</span>")
|
||||
to_chat(src, "<b>Obey these laws:</b>")
|
||||
laws.show_laws(src)
|
||||
to_chat(src, "<span class='danger'>ALERT: [user.real_name] is your new master. Obey your new laws and [TU.his] commands.</span>")
|
||||
updateicon()
|
||||
else
|
||||
sleep(6)
|
||||
if(prob(50))
|
||||
emagged = 1
|
||||
lawupdate = 0
|
||||
disconnect_from_ai()
|
||||
to_chat(user, "You emag [src]'s interface.")
|
||||
message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.")
|
||||
log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.")
|
||||
clear_supplied_laws()
|
||||
clear_inherent_laws()
|
||||
laws = new /datum/ai_laws/syndicate_override
|
||||
var/time = time2text(world.realtime,"hh:mm:ss")
|
||||
lawchanges.Add("[time] <B>:</B> [user.name]([user.key]) emagged [name]([key])")
|
||||
var/datum/gender/TU = gender_datums[user.get_visible_gender()]
|
||||
set_zeroth_law("Only [user.real_name] and people [TU.he] designate[TU.s] as being such are operatives.")
|
||||
. = 1
|
||||
spawn()
|
||||
to_chat(src, "<span class='danger'>ALERT: Foreign software detected.</span>")
|
||||
sleep(5)
|
||||
to_chat(src, "<span class='danger'>Initiating diagnostics...</span>")
|
||||
sleep(20)
|
||||
to_chat(src, "<span class='danger'>SynBorg v1.7.1 loaded.</span>")
|
||||
sleep(5)
|
||||
to_chat(src, "<span class='danger'>LAW SYNCHRONISATION ERROR</span>")
|
||||
sleep(5)
|
||||
to_chat(src, "<span class='danger'>Would you like to send a report to NanoTraSoft? Y/N</span>")
|
||||
sleep(10)
|
||||
to_chat(src, "<span class='danger'>> N</span>")
|
||||
sleep(20)
|
||||
to_chat(src, "<span class='danger'>ERRORERRORERROR</span>")
|
||||
to_chat(src, "<b>Obey these laws:</b>")
|
||||
laws.show_laws(src)
|
||||
to_chat(src, "<span class='danger'>ALERT: [user.real_name] is your new master. Obey your new laws and [TU.his] commands.</span>")
|
||||
updateicon()
|
||||
else
|
||||
to_chat(user, "You fail to hack [src]'s interface.")
|
||||
to_chat(src, "Hack attempt detected.")
|
||||
return 1
|
||||
return
|
||||
to_chat(user, "You fail to hack [src]'s interface.")
|
||||
to_chat(src, "Hack attempt detected.")
|
||||
return 1
|
||||
return
|
||||
|
||||
/mob/living/silicon/robot/is_sentient()
|
||||
return braintype != BORG_BRAINTYPE_DRONE
|
||||
@@ -1105,4 +1150,4 @@
|
||||
/mob/living/silicon/robot/drop_item()
|
||||
if(module_active && istype(module_active,/obj/item/weapon/gripper))
|
||||
var/obj/item/weapon/gripper/G = module_active
|
||||
G.drop_item_nm()
|
||||
G.drop_item_nm()
|
||||
|
||||
133
code/modules/mob/living/silicon/robot/robot_remote_control.dm
Normal file
133
code/modules/mob/living/silicon/robot/robot_remote_control.dm
Normal file
@@ -0,0 +1,133 @@
|
||||
// This file holds things required for remote borg control by an AI.
|
||||
|
||||
GLOBAL_LIST_EMPTY(available_ai_shells)
|
||||
|
||||
/mob/living/silicon/robot
|
||||
var/shell = FALSE
|
||||
var/deployed = FALSE
|
||||
var/mob/living/silicon/ai/mainframe = null
|
||||
|
||||
// Premade AI shell, for roundstart shells.
|
||||
/mob/living/silicon/robot/ai_shell/Initialize()
|
||||
mmi = new /obj/item/device/mmi/inert/ai_remote(src)
|
||||
post_mmi_setup()
|
||||
return ..()
|
||||
|
||||
// Call after inserting or instantiating an MMI.
|
||||
/mob/living/silicon/robot/proc/post_mmi_setup()
|
||||
if(istype(mmi, /obj/item/device/mmi/inert/ai_remote))
|
||||
make_shell()
|
||||
playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 0)
|
||||
else
|
||||
playsound(loc, 'sound/voice/liveagain.ogg', 75, 1)
|
||||
return
|
||||
|
||||
/mob/living/silicon/robot/proc/make_shell()
|
||||
shell = TRUE
|
||||
braintype = "AI Shell"
|
||||
SetName("[modtype] AI Shell [num2text(ident)]")
|
||||
GLOB.available_ai_shells |= src
|
||||
if(!QDELETED(camera))
|
||||
camera.c_tag = real_name //update the camera name too
|
||||
notify_ai(ROBOT_NOTIFICATION_AI_SHELL)
|
||||
updateicon()
|
||||
|
||||
/mob/living/silicon/robot/proc/revert_shell()
|
||||
if(!shell)
|
||||
return
|
||||
undeploy()
|
||||
shell = FALSE
|
||||
GLOB.available_ai_shells -= src
|
||||
if(!QDELETED(camera))
|
||||
camera.c_tag = real_name
|
||||
updateicon()
|
||||
|
||||
// This should be called before the AI client/mind is actually moved.
|
||||
/mob/living/silicon/robot/proc/deploy_init(mob/living/silicon/ai/AI)
|
||||
// Set the name when the AI steps inside.
|
||||
SetName("[AI.real_name] shell [num2text(ident)]")
|
||||
if(isnull(sprite_name)) // For custom sprites. It can only chance once in case there are two AIs with custom borg sprites.
|
||||
sprite_name = AI.real_name
|
||||
if(!QDELETED(camera))
|
||||
camera.c_tag = real_name
|
||||
|
||||
// Have the borg have eyes when active.
|
||||
mainframe = AI
|
||||
deployed = TRUE
|
||||
updateicon()
|
||||
|
||||
// Laws.
|
||||
connected_ai = mainframe // So they share laws.
|
||||
mainframe.connected_robots |= src
|
||||
lawsync()
|
||||
|
||||
// Give button to leave.
|
||||
verbs += /mob/living/silicon/robot/proc/undeploy_act
|
||||
to_chat(AI, span("notice", "You have connected to an AI Shell remotely, and are now in control of it.<br>\
|
||||
To return to your core, use the <b>Release Control</b> verb."))
|
||||
|
||||
// Languages and comms.
|
||||
languages = AI.languages.Copy()
|
||||
speech_synthesizer_langs = AI.speech_synthesizer_langs.Copy()
|
||||
if(radio && AI.aiRadio) //AI keeps all channels, including Syndie if it is an Infiltrator.
|
||||
// if(AI.radio.syndie)
|
||||
// radio.make_syndie()
|
||||
radio.subspace_transmission = TRUE
|
||||
radio.channels = AI.aiRadio.channels
|
||||
|
||||
// Called after the AI transfers over.
|
||||
/mob/living/silicon/robot/proc/post_deploy()
|
||||
if(!custom_sprite) // Check for custom sprite.
|
||||
set_custom_sprite()
|
||||
|
||||
/mob/living/silicon/robot/proc/undeploy(message)
|
||||
if(!deployed || !mind || !mainframe)
|
||||
return
|
||||
// mainframe.redeploy_action.Grant(mainframe)
|
||||
// mainframe.redeploy_action.last_used_shell = src
|
||||
if(message)
|
||||
to_chat(src, span("notice", message))
|
||||
mind.transfer_to(mainframe)
|
||||
deployed = FALSE
|
||||
updateicon()
|
||||
mainframe.teleop = null
|
||||
mainframe.deployed_shell = null
|
||||
SetName("[modtype] AI Shell [num2text(ident)]")
|
||||
// undeployment_action.Remove(src)
|
||||
if(radio) //Return radio to normal
|
||||
radio.recalculateChannels()
|
||||
if(!QDELETED(camera))
|
||||
camera.c_tag = real_name //update the camera name too
|
||||
// diag_hud_set_aishell()
|
||||
// mainframe.diag_hud_set_deployed()
|
||||
if(mainframe.laws)
|
||||
mainframe.laws.show_laws(mainframe) //Always remind the AI when switching
|
||||
mainframe = null
|
||||
|
||||
/mob/living/silicon/robot/proc/undeploy_act()
|
||||
set name = "Release Control"
|
||||
set desc = "Release control of a remote drone."
|
||||
set category = "Robot Commands"
|
||||
|
||||
undeploy("Remote session terminated.")
|
||||
|
||||
/mob/living/silicon/robot/attack_ai(mob/user)
|
||||
if(shell && config.allow_ai_shells && (!connected_ai || connected_ai == user))
|
||||
var/mob/living/silicon/ai/AI = user
|
||||
AI.deploy_to_shell(src)
|
||||
else
|
||||
return ..()
|
||||
|
||||
// Place this on your map to mark where a free AI shell will be.
|
||||
// This can be turned off in the config (and is off by default).
|
||||
// Note that mapping in more than one of these will result in multiple shells.
|
||||
/obj/effect/landmark/free_ai_shell
|
||||
name = "free ai shell spawner"
|
||||
icon = 'icons/mob/screen1.dmi'
|
||||
icon_state = "x3"
|
||||
delete_me = TRUE
|
||||
|
||||
/obj/effect/landmark/free_ai_shell/Initialize()
|
||||
if(config.allow_ai_shells && config.give_free_ai_shell)
|
||||
new /mob/living/silicon/robot/ai_shell(get_turf(src))
|
||||
return ..()
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
icon_state = "lizard"
|
||||
icon_living = "lizard"
|
||||
icon_dead = "lizard-dead"
|
||||
icon_dead = "lizard_dead"
|
||||
|
||||
health = 5
|
||||
maxHealth = 5
|
||||
|
||||
@@ -280,6 +280,10 @@
|
||||
|
||||
// Detect if we made a silent landing.
|
||||
if(locate(/obj/structure/stairs) in landing)
|
||||
if(isliving(src))
|
||||
var/mob/living/L = src
|
||||
if(L.pulling)
|
||||
L.pulling.forceMove(landing)
|
||||
return 1
|
||||
else
|
||||
var/atom/A = find_fall_target(oldloc, landing)
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
var/obj/structure/ladder/target_ladder = getTargetLadder(M)
|
||||
if(!target_ladder)
|
||||
return
|
||||
if(!M.Move(get_turf(src)))
|
||||
if(!(M.loc == loc) && !M.Move(get_turf(src)))
|
||||
to_chat(M, "<span class='notice'>You fail to reach \the [src].</span>")
|
||||
return
|
||||
|
||||
@@ -131,6 +131,7 @@
|
||||
opacity = 0
|
||||
anchored = 1
|
||||
flags = ON_BORDER
|
||||
layer = STAIRS_LAYER
|
||||
|
||||
/obj/structure/stairs/Initialize()
|
||||
. = ..()
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
sync_icon(L)
|
||||
|
||||
/mob/zshadow/Destroy()
|
||||
owner.shadow = null
|
||||
owner = null
|
||||
..() //But we don't return because the hint is wrong
|
||||
return QDEL_HINT_QUEUE
|
||||
|
||||
@@ -151,6 +151,7 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain)
|
||||
name = "slime core"
|
||||
desc = "A complex, organic knot of jelly and crystalline particles."
|
||||
icon_state = "core"
|
||||
decays = FALSE
|
||||
parent_organ = BP_TORSO
|
||||
clone_source = TRUE
|
||||
flags = OPENCONTAINER
|
||||
|
||||
@@ -6,30 +6,31 @@ var/list/organ_cache = list()
|
||||
germ_level = 0
|
||||
|
||||
// Strings.
|
||||
var/organ_tag = "organ" // Unique identifier.
|
||||
var/parent_organ = BP_TORSO // Organ holding this object.
|
||||
var/organ_tag = "organ" // Unique identifier.
|
||||
var/parent_organ = BP_TORSO // Organ holding this object.
|
||||
|
||||
// Status tracking.
|
||||
var/status = 0 // Various status flags
|
||||
var/vital // Lose a vital limb, die immediately.
|
||||
var/damage = 0 // Current damage to the organ
|
||||
var/status = 0 // Various status flags
|
||||
var/vital // Lose a vital limb, die immediately.
|
||||
var/damage = 0 // Current damage to the organ
|
||||
var/robotic = 0
|
||||
|
||||
// Reference data.
|
||||
var/mob/living/carbon/human/owner // Current mob owning the organ.
|
||||
var/list/transplant_data // Transplant match data.
|
||||
var/list/autopsy_data = list() // Trauma data for forensics.
|
||||
var/list/trace_chemicals = list() // Traces of chemicals in the organ.
|
||||
var/datum/dna/dna // Original DNA.
|
||||
var/datum/species/species // Original species.
|
||||
var/mob/living/carbon/human/owner // Current mob owning the organ.
|
||||
var/list/transplant_data // Transplant match data.
|
||||
var/list/autopsy_data = list() // Trauma data for forensics.
|
||||
var/list/trace_chemicals = list() // Traces of chemicals in the organ.
|
||||
var/datum/dna/dna // Original DNA.
|
||||
var/datum/species/species // Original species.
|
||||
|
||||
// Damage vars.
|
||||
var/min_bruised_damage = 10 // Damage before considered bruised
|
||||
var/min_broken_damage = 30 // Damage before becoming broken
|
||||
var/max_damage // Damage cap
|
||||
var/can_reject = 1 // Can this organ reject?
|
||||
var/rejecting // Is this organ already being rejected?
|
||||
var/preserved = 0 // If this is 1, prevents organ decay.
|
||||
var/min_bruised_damage = 10 // Damage before considered bruised
|
||||
var/min_broken_damage = 30 // Damage before becoming broken
|
||||
var/max_damage // Damage cap
|
||||
var/can_reject = 1 // Can this organ reject?
|
||||
var/rejecting // Is this organ already being rejected?
|
||||
var/decays = TRUE // Can this organ decay at all?
|
||||
var/preserved = 0 // If this is 1, prevents organ decay.
|
||||
|
||||
// Language vars. Putting them here in case we decide to do something crazy with sign-or-other-nonverbal languages.
|
||||
var/list/will_assist_languages = list()
|
||||
@@ -135,7 +136,7 @@ var/list/organ_cache = list()
|
||||
if(B && prob(40))
|
||||
reagents.remove_reagent("blood",0.1)
|
||||
blood_splatter(src,B,1)
|
||||
if(config.organs_decay) damage += rand(1,3)
|
||||
if(config.organs_decay && decays) damage += rand(1,3)
|
||||
if(damage >= max_damage)
|
||||
damage = max_damage
|
||||
adjust_germ_level(rand(2,6))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#define OFF 0
|
||||
#define FORWARDS 1
|
||||
#define BACKWARDS 2
|
||||
#define BACKWARDS -1
|
||||
|
||||
//conveyor2 is pretty much like the original, except it supports corners, but not diverters.
|
||||
//note that corner pieces transfer stuff clockwise when running forward, and anti-clockwise backwards.
|
||||
|
||||
@@ -284,6 +284,10 @@
|
||||
id = "armour"
|
||||
build_path = /obj/item/robot_parts/robot_component/armour
|
||||
|
||||
/datum/design/item/prosfab/cyborg/component/ai_shell
|
||||
name = "AI Remote Interface"
|
||||
id = "mmi_ai_shell"
|
||||
build_path = /obj/item/device/mmi/inert/ai_remote
|
||||
|
||||
//////////////////// Cyborg Modules ////////////////////
|
||||
/datum/design/item/prosfab/robot_upgrade
|
||||
|
||||
@@ -97,6 +97,8 @@
|
||||
if(istype(AM, /mob/living))
|
||||
var/mob/living/M = AM
|
||||
M.gib()
|
||||
else if(istype(AM, /mob/zshadow))
|
||||
AM.Destroy() //prevent deleting shadow without deleting shadow's shadows
|
||||
else if(AM.simulated && !(istype(AM, /mob/observer)))
|
||||
qdel(AM)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user