Files
fulpstation/code/modules/mob/living/silicon/robot/robot.dm
vuonojenmustaturska 39375d5cc2 Replaces a bunch of mob loops with hopefully better ones (#32786)
* Free codebase churn

* fixes dviewmob

* a commit

* Partial revert "Free codebase churn"

This reverts commit 9dd41b3860c331637bbc85e694dd32dc59768ad5.

* better fix to dviewmob

* renames living_mob_list to alive_mob_list, adds mob_living_list and carbon_list

* make some use out of the shiny carbon list

* make some use out of the shiny new living list

* more things, also make the mobs subsystem (Life) use living list

* bonus

* domo arigato mr. roboto

* compile fixes, also made the drone code less dumb

* better?

* make admin message prettier

* honk

* fixes blobs

* rev fixes

* one small thing
2017-11-16 23:08:40 +01:00

1163 lines
37 KiB
Plaintext

/mob/living/silicon/robot
name = "Cyborg"
real_name = "Cyborg"
icon = 'icons/mob/robots.dmi'
icon_state = "robot"
maxHealth = 100
health = 100
macro_default = "robot-default"
macro_hotkeys = "robot-hotkeys"
bubble_icon = "robot"
designation = "Default" //used for displaying the prefix & getting the current module of cyborg
has_limbs = 1
var/custom_name = ""
var/braintype = "Cyborg"
var/obj/item/robot_suit/robot_suit = null //Used for deconstruction to remember what the borg was constructed out of..
var/obj/item/device/mmi/mmi = null
var/shell = FALSE
var/deployed = FALSE
var/mob/living/silicon/ai/mainframe = null
var/datum/action/innate/undeployment/undeployment_action = new
//Hud stuff
var/obj/screen/inv1 = null
var/obj/screen/inv2 = null
var/obj/screen/inv3 = null
var/obj/screen/lamp_button = null
var/obj/screen/thruster_button = null
var/obj/screen/hands = null
var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not
var/obj/screen/robot_modules_background
//3 Modules can be activated at any one time.
var/obj/item/robot_module/module = null
var/obj/item/module_active = null
held_items = list(null, null, null) //we use held_items for the module holding, because that makes sense to do!
var/mutable_appearance/eye_lights
var/mob/living/silicon/ai/connected_ai = null
var/obj/item/stock_parts/cell/cell = null
var/opened = 0
var/emagged = FALSE
var/emag_cooldown = 0
var/wiresexposed = 0
var/ident = 0
var/locked = TRUE
var/list/req_access = list(ACCESS_ROBOTICS)
var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list(), "Burglar"=list())
var/speed = 0 // VTEC speed boost.
var/magpulse = FALSE // Magboot-like effect.
var/ionpulse = FALSE // Jetpack-like effect.
var/ionpulse_on = FALSE // Jetpack-like effect.
var/datum/effect_system/trail_follow/ion/ion_trail // Ionpulse effect.
var/low_power_mode = 0 //whether the robot has no charge left.
var/datum/effect_system/spark_spread/spark_system // So they can initialize sparks whenever/N
var/lawupdate = 1 //Cyborgs will sync their laws with their AI by default
var/scrambledcodes = 0 // Used to determine if a borg shows up on the robotics console. Setting to one hides them.
var/lockcharge //Boolean of whether the borg is locked down or not
var/toner = 0
var/tonermax = 40
var/lamp_max = 10 //Maximum brightness of a borg lamp. Set as a var for easy adjusting.
var/lamp_intensity = 0 //Luminosity of the headlamp. 0 is off. Higher settings than the minimum require power.
var/lamp_cooldown = 0 //Flag for if the lamp is on cooldown after being forcibly disabled.
var/sight_mode = 0
hud_possible = list(ANTAG_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD, DIAG_TRACK_HUD)
var/list/upgrades = list()
var/obj/item/hat
var/hat_offset = -3
var/list/equippable_hats = list(/obj/item/clothing/head/caphat,
/obj/item/clothing/head/hardhat/cakehat,
/obj/item/clothing/head/centhat,
/obj/item/clothing/head/HoS,
/obj/item/clothing/head/hopcap,
/obj/item/clothing/head/wizard,
/obj/item/clothing/head/nursehat,
/obj/item/clothing/head/sombrero,
/obj/item/clothing/head/witchunter_hat)
var/remote_range = 7 //How far can you interact with machines.
can_buckle = TRUE
buckle_lying = FALSE
var/static/list/can_ride_typecache = typecacheof(/mob/living/carbon/human)
/mob/living/silicon/robot/get_cell()
return cell
/mob/living/silicon/robot/Initialize(mapload)
spark_system = new /datum/effect_system/spark_spread()
spark_system.set_up(5, 0, src)
spark_system.attach(src)
wires = new /datum/wires/robot(src)
robot_modules_background = new()
robot_modules_background.icon_state = "block"
robot_modules_background.layer = HUD_LAYER //Objects that appear on screen are on layer ABOVE_HUD_LAYER, UI should be just below it.
robot_modules_background.plane = HUD_PLANE
ident = rand(1, 999)
if(!cell)
cell = new /obj/item/stock_parts/cell(src)
cell.maxcharge = 7500
cell.charge = 7500
if(lawupdate)
make_laws()
if(!TryConnectToAI())
lawupdate = FALSE
radio = new /obj/item/device/radio/borg(src)
if(!scrambledcodes && !builtInCamera)
builtInCamera = new (src)
builtInCamera.c_tag = real_name
builtInCamera.network = list("SS13")
builtInCamera.internal_light = FALSE
if(wires.is_cut(WIRE_CAMERA))
builtInCamera.status = 0
module = new /obj/item/robot_module(src)
module.rebuild_modules()
update_icons()
. = ..()
//If this body is meant to be a borg controlled by the AI player
if(shell)
make_shell()
//MMI stuff. Held togheter by magic. ~Miauw
else if(!mmi || !mmi.brainmob)
mmi = new (src)
mmi.brain = new /obj/item/organ/brain(mmi)
mmi.brain.name = "[real_name]'s brain"
mmi.icon_state = "mmi_full"
mmi.name = "Man-Machine Interface: [real_name]"
mmi.brainmob = new(mmi)
mmi.brainmob.name = src.real_name
mmi.brainmob.real_name = src.real_name
mmi.brainmob.container = mmi
updatename()
equippable_hats = typecacheof(equippable_hats)
playsound(loc, 'sound/voice/liveagain.ogg', 75, 1)
aicamera = new/obj/item/device/camera/siliconcam/robot_camera(src)
toner = tonermax
diag_hud_set_borgcell()
//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO
/mob/living/silicon/robot/Destroy()
if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside.
var/turf/T = get_turf(loc)//To hopefully prevent run time errors.
if(T)
mmi.forceMove(T)
if(mmi.brainmob)
if(mmi.brainmob.stat == DEAD)
mmi.brainmob.stat = CONSCIOUS
GLOB.dead_mob_list -= mmi.brainmob
GLOB.alive_mob_list += mmi.brainmob
mind.transfer_to(mmi.brainmob)
mmi.update_icon()
else
to_chat(src, "<span class='boldannounce'>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()
stack_trace("Borg MMI lacked a brainmob")
mmi = null
if(connected_ai)
connected_ai.connected_robots -= src
if(shell)
GLOB.available_ai_shells -= src
qdel(wires)
qdel(module)
qdel(eye_lights)
wires = null
module = null
eye_lights = null
cell = null
return ..()
/mob/living/silicon/robot/proc/pick_module()
if(module.type != /obj/item/robot_module)
return
if(wires.is_cut(WIRE_RESET_MODULE))
to_chat(src,"<span class='userdanger'>ERROR: Module installer reply timeout. Please check internal connections.</span>")
return
var/list/modulelist = list("Standard" = /obj/item/robot_module/standard, \
"Engineering" = /obj/item/robot_module/engineering, \
"Medical" = /obj/item/robot_module/medical, \
"Miner" = /obj/item/robot_module/miner, \
"Janitor" = /obj/item/robot_module/janitor, \
"Service" = /obj/item/robot_module/butler)
if(!CONFIG_GET(flag/disable_peaceborg))
modulelist["Peacekeeper"] = /obj/item/robot_module/peacekeeper
if(!CONFIG_GET(flag/disable_secborg))
modulelist["Security"] = /obj/item/robot_module/security
var/input_module = input("Please, select a module!", "Robot", null, null) as null|anything in modulelist
if(!input_module || module.type != /obj/item/robot_module)
return
module.transform_to(modulelist[input_module])
/mob/living/silicon/robot/proc/updatename()
if(shell)
return
var/changed_name = ""
if(custom_name)
changed_name = custom_name
if(changed_name == "" && client)
changed_name = client.prefs.custom_names["cyborg"]
if(!changed_name)
changed_name = get_standard_name()
real_name = changed_name
name = real_name
if(!QDELETED(builtInCamera))
builtInCamera.c_tag = real_name //update the camera name too
/mob/living/silicon/robot/proc/get_standard_name()
return "[(designation ? "[designation] " : "")][mmi.braintype]-[ident]"
/mob/living/silicon/robot/verb/cmd_robot_alerts()
set category = "Robot Commands"
set name = "Show Alerts"
if(usr.stat == DEAD)
to_chat(src, "<span class='userdanger'>Alert: You are dead.</span>")
return //won't work if dead
robot_alerts()
//for borg hotkeys, here module refers to borg inv slot, not core module
/mob/living/silicon/robot/verb/cmd_toggle_module(module as num)
set name = "Toggle Module"
set hidden = 1
toggle_module(module)
/mob/living/silicon/robot/verb/cmd_unequip_module()
set name = "Unequip Module"
set hidden = 1
uneq_active()
/mob/living/silicon/robot/proc/robot_alerts()
var/dat = ""
for (var/cat in alarms)
dat += text("<B>[cat]</B><BR>\n")
var/list/L = alarms[cat]
if (L.len)
for (var/alarm in L)
var/list/alm = L[alarm]
var/area/A = alm[1]
dat += "<NOBR>"
dat += text("-- [A.name]")
dat += "</NOBR><BR>\n"
else
dat += "-- All Systems Nominal<BR>\n"
dat += "<BR>\n"
var/datum/browser/alerts = new(usr, "robotalerts", "Current Station Alerts", 400, 410)
alerts.set_content(dat)
alerts.open()
/mob/living/silicon/robot/proc/ionpulse()
if(!ionpulse_on)
return
if(cell.charge <= 50)
toggle_ionpulse()
return
cell.charge -= 50 // 500 steps on a default cell.
return 1
/mob/living/silicon/robot/proc/toggle_ionpulse()
if(!ionpulse)
to_chat(src, "<span class='notice'>No thrusters are installed!</span>")
return
if(!ion_trail)
ion_trail = new
ion_trail.set_up(src)
ionpulse_on = !ionpulse_on
to_chat(src, "<span class='notice'>You [ionpulse_on ? null :"de"]activate your ion thrusters.</span>")
if(ionpulse_on)
ion_trail.start()
else
ion_trail.stop()
if(thruster_button)
thruster_button.icon_state = "ionpulse[ionpulse_on]"
/mob/living/silicon/robot/Stat()
..()
if(statpanel("Status"))
if(cell)
stat("Charge Left:", "[cell.charge]/[cell.maxcharge]")
else
stat(null, text("No Cell Inserted!"))
if(module)
for(var/datum/robot_energy_storage/st in module.storages)
stat("[st.name]:", "[st.energy]/[st.max_energy]")
if(connected_ai)
stat("Master AI:", connected_ai.name)
/mob/living/silicon/robot/restrained(ignore_grab)
. = 0
/mob/living/silicon/robot/triggerAlarm(class, area/A, O, obj/alarmsource)
if(alarmsource.z != z)
return
if(stat == DEAD)
return 1
var/list/L = alarms[class]
for (var/I in L)
if (I == A.name)
var/list/alarm = L[I]
var/list/sources = alarm[3]
if (!(alarmsource in sources))
sources += alarmsource
return 1
var/obj/machinery/camera/C = null
var/list/CL = null
if (O && istype(O, /list))
CL = O
if (CL.len == 1)
C = CL[1]
else if (O && istype(O, /obj/machinery/camera))
C = O
L[A.name] = list(A, (C) ? C : O, list(alarmsource))
queueAlarm(text("--- [class] alarm detected in [A.name]!"), class)
return 1
/mob/living/silicon/robot/cancelAlarm(class, area/A, obj/origin)
var/list/L = alarms[class]
var/cleared = 0
for (var/I in L)
if (I == A.name)
var/list/alarm = L[I]
var/list/srcs = alarm[3]
if (origin in srcs)
srcs -= origin
if (srcs.len == 0)
cleared = 1
L -= I
if (cleared)
queueAlarm("--- [class] alarm in [A.name] has been cleared.", class, 0)
return !cleared
/mob/living/silicon/robot/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/weldingtool) && (user.a_intent != INTENT_HARM || user == src))
user.changeNext_move(CLICK_CD_MELEE)
var/obj/item/weldingtool/WT = W
if (!getBruteLoss())
to_chat(user, "<span class='warning'>[src] is already in good condition!</span>")
return
if (WT.remove_fuel(0, user)) //The welder has 1u of fuel consumed by it's afterattack, so we don't need to worry about taking any away.
if(src == user)
to_chat(user, "<span class='notice'>You start fixing yourself...</span>")
if(!do_after(user, 50, target = src))
return
adjustBruteLoss(-30)
updatehealth()
add_fingerprint(user)
visible_message("<span class='notice'>[user] has fixed some of the dents on [src].</span>")
return
else
to_chat(user, "<span class='warning'>The welder must be on for this task!</span>")
return
else if(istype(W, /obj/item/stack/cable_coil) && wiresexposed)
user.changeNext_move(CLICK_CD_MELEE)
var/obj/item/stack/cable_coil/coil = W
if (getFireLoss() > 0 || getToxLoss() > 0)
if(src == user)
to_chat(user, "<span class='notice'>You start fixing yourself...</span>")
if(!do_after(user, 50, target = src))
return
if (coil.use(1))
adjustFireLoss(-30)
adjustToxLoss(-30)
updatehealth()
user.visible_message("[user] has fixed some of the burnt wires on [src].", "<span class='notice'>You fix some of the burnt wires on [src].</span>")
else
to_chat(user, "<span class='warning'>You need more cable to repair [src]!</span>")
else
to_chat(user, "The wires seem fine, there's no need to fix them.")
else if(istype(W, /obj/item/crowbar)) // crowbar means open or close the cover
if(opened)
to_chat(user, "<span class='notice'>You close the cover.</span>")
opened = 0
update_icons()
else
if(locked)
to_chat(user, "<span class='warning'>The cover is locked and cannot be opened!</span>")
else
to_chat(user, "<span class='notice'>You open the cover.</span>")
opened = 1
update_icons()
else if(istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside
if(wiresexposed)
to_chat(user, "<span class='warning'>Close the cover first!</span>")
else if(cell)
to_chat(user, "<span class='warning'>There is a power cell already installed!</span>")
else
if(!user.transferItemToLoc(W, src))
return
cell = W
to_chat(user, "<span class='notice'>You insert the power cell.</span>")
update_icons()
diag_hud_set_borgcell()
else if(is_wire_tool(W))
if (wiresexposed)
wires.interact(user)
else
to_chat(user, "<span class='warning'>You can't reach the wiring!</span>")
else if(istype(W, /obj/item/screwdriver) && opened && !cell) // haxing
wiresexposed = !wiresexposed
to_chat(user, "The wires have been [wiresexposed ? "exposed" : "unexposed"]")
update_icons()
else if(istype(W, /obj/item/screwdriver) && opened && cell) // radio
if(shell)
to_chat(user, "You cannot seem to open the radio compartment") //Prevent AI radio key theft
else if(radio)
radio.attackby(W,user)//Push it to the radio to let it handle everything
else
to_chat(user, "<span class='warning'>Unable to locate a radio!</span>")
update_icons()
else if(istype(W, /obj/item/wrench) && opened && !cell) //Deconstruction. The flashes break from the fall, to prevent this from being a ghetto reset module.
if(!lockcharge)
to_chat(user, "<span class='boldannounce'>[src]'s bolts spark! Maybe you should lock them down first!</span>")
spark_system.start()
return
else
playsound(src, W.usesound, 50, 1)
to_chat(user, "<span class='notice'>You start to unfasten [src]'s securing bolts...</span>")
if(do_after(user, 50*W.toolspeed, target = src) && !cell)
user.visible_message("[user] deconstructs [src]!", "<span class='notice'>You unfasten the securing bolts, and [src] falls to pieces!</span>")
deconstruct()
else if(istype(W, /obj/item/aiModule))
var/obj/item/aiModule/MOD = W
if(!opened)
to_chat(user, "<span class='warning'>You need access to the robot's insides to do that!</span>")
return
if(wiresexposed)
to_chat(user, "<span class='warning'>You need to close the wire panel to do that!</span>")
return
if(!cell)
to_chat(user, "<span class='warning'>You need to install a power cell to do that!</span>")
return
if(shell) //AI shells always have the laws of the AI
to_chat(user, "<span class='warning'>[src] is controlled remotely! You cannot upload new laws this way!</span>")
return
if(emagged || (connected_ai && lawupdate)) //Can't be sure which, metagamers
emote("buzz-[user.name]")
return
if(!mind) //A player mind is required for law procs to run antag checks.
to_chat(user, "<span class='warning'>[src] is entirely unresponsive!</span>")
return
MOD.install(laws, user) //Proc includes a success mesage so we don't need another one
return
else if(istype(W, /obj/item/device/encryptionkey/) && opened)
if(radio)//sanityyyyyy
radio.attackby(W,user)//GTFO, you have your own procs
else
to_chat(user, "<span class='warning'>Unable to locate a radio!</span>")
else if (istype(W, /obj/item/card/id)||istype(W, /obj/item/device/pda)) // trying to unlock the interface with an ID card
if(emagged)//still allow them to open the cover
to_chat(user, "<span class='notice'>The interface seems slightly damaged.</span>")
if(opened)
to_chat(user, "<span class='warning'>You must close the cover to swipe an ID card!</span>")
else
if(allowed(usr))
locked = !locked
to_chat(user, "<span class='notice'>You [ locked ? "lock" : "unlock"] [src]'s cover.</span>")
update_icons()
else
to_chat(user, "<span class='danger'>Access denied.</span>")
else if(istype(W, /obj/item/borg/upgrade/))
var/obj/item/borg/upgrade/U = W
if(!opened)
to_chat(user, "<span class='warning'>You must access the borg's internals!</span>")
else if(!src.module && U.require_module)
to_chat(user, "<span class='warning'>The borg must choose a module before it can be upgraded!</span>")
else if(U.locked)
to_chat(user, "<span class='warning'>The upgrade is locked and cannot be used yet!</span>")
else
if(!user.temporarilyRemoveItemFromInventory(U))
return
if(U.action(src))
to_chat(user, "<span class='notice'>You apply the upgrade to [src].</span>")
if(U.one_use)
qdel(U)
else
U.forceMove(src)
upgrades += U
else
to_chat(user, "<span class='danger'>Upgrade error.</span>")
U.forceMove(drop_location())
else if(istype(W, /obj/item/device/toner))
if(toner >= tonermax)
to_chat(user, "<span class='warning'>The toner level of [src] is at its highest level possible!</span>")
else
if(!user.temporarilyRemoveItemFromInventory(W))
return
toner = tonermax
qdel(W)
to_chat(user, "<span class='notice'>You fill the toner level of [src] to its max capacity.</span>")
else if(istype(W, /obj/item/device/flashlight))
if(!opened)
to_chat(user, "<span class='warning'>You need to open the panel to repair the headlamp!</span>")
if(lamp_cooldown <= world.time)
to_chat(user, "<span class='warning'>The headlamp is already functional!</span>")
else
if(!user.temporarilyRemoveItemFromInventory(W))
to_chat(user, "<span class='warning'>[W] seems to be stuck to your hand. You'll have to find a different light.</span>")
return
lamp_cooldown = 0
qdel(W)
to_chat(user, "<span class='notice'>You replace the headlamp bulbs.</span>")
else
return ..()
/mob/living/silicon/robot/verb/unlock_own_cover()
set category = "Robot Commands"
set name = "Unlock Cover"
set desc = "Unlocks your own cover if it is locked. You can not lock it again. A human will have to lock it for you."
if(stat == DEAD)
return //won't work if dead
if(locked)
switch(alert("You cannot lock your cover again, are you sure?\n (You can still ask for a human to lock it)", "Unlock Own Cover", "Yes", "No"))
if("Yes")
locked = FALSE
update_icons()
to_chat(usr, "<span class='notice'>You unlock your cover.</span>")
/mob/living/silicon/robot/proc/allowed(mob/M)
//check if it doesn't require any access at all
if(check_access(null))
return 1
if(ishuman(M))
var/mob/living/carbon/human/H = M
//if they are holding or wearing a card that has access, that works
if(check_access(H.get_active_held_item()) || check_access(H.wear_id))
return 1
else if(ismonkey(M))
var/mob/living/carbon/monkey/george = M
//they can only hold things :(
if(isitem(george.get_active_held_item()))
return check_access(george.get_active_held_item())
return 0
/mob/living/silicon/robot/proc/check_access(obj/item/card/id/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(!istype(I, /obj/item/card/id) && isitem(I))
I = I.GetID()
if(!I || !I.access) //not ID or no access
return 0
for(var/req in req_access)
if(!(req in I.access)) //doesn't have this access
return 0
return 1
/mob/living/silicon/robot/regenerate_icons()
return update_icons()
/mob/living/silicon/robot/update_icons()
cut_overlays()
icon_state = module.cyborg_base_icon
if(stat != DEAD && !(IsUnconscious() || IsStun() || IsKnockdown() || low_power_mode)) //Not dead, not stunned.
if(!eye_lights)
eye_lights = new()
if(lamp_intensity > 2)
eye_lights.icon_state = "[module.special_light_key ? "[module.special_light_key]":"[module.cyborg_base_icon]"]_l"
else
eye_lights.icon_state = "[module.special_light_key ? "[module.special_light_key]":"[module.cyborg_base_icon]"]_e[is_servant_of_ratvar(src) ? "_r" : ""]"
eye_lights.icon = icon
add_overlay(eye_lights)
if(opened)
if(wiresexposed)
add_overlay("ov-opencover +w")
else if(cell)
add_overlay("ov-opencover +c")
else
add_overlay("ov-opencover -c")
if(hat)
var/mutable_appearance/head_overlay = hat.build_worn_icon(state = hat.icon_state, default_layer = 20, default_icon_file = 'icons/mob/head.dmi')
head_overlay.pixel_y += hat_offset
add_overlay(head_overlay)
update_fire()
/mob/living/silicon/robot/proc/self_destruct()
if(emagged)
if(mmi)
qdel(mmi)
explosion(src.loc,1,2,4,flame_range = 2)
else
explosion(src.loc,-1,0,2)
gib()
/mob/living/silicon/robot/proc/UnlinkSelf()
if(src.connected_ai)
connected_ai.connected_robots -= src
src.connected_ai = null
lawupdate = 0
lockcharge = 0
canmove = 1
scrambledcodes = 1
//Disconnect it's camera so it's not so easily tracked.
if(!QDELETED(builtInCamera))
QDEL_NULL(builtInCamera)
// I'm trying to get the Cyborg to not be listed in the camera list
// Instead of being listed as "deactivated". The downside is that I'm going
// to have to check if every camera is null or not before doing anything, to prevent runtime errors.
// I could change the network to null but I don't know what would happen, and it seems too hacky for me.
/mob/living/silicon/robot/proc/ResetSecurityCodes()
set category = "Robot Commands"
set name = "Reset Identity Codes"
set desc = "Scrambles your security and identification codes and resets your current buffers. Unlocks you and permanently severs you from your AI and the robotics console and will deactivate your camera system."
var/mob/living/silicon/robot/R = src
if(R)
R.UnlinkSelf()
to_chat(R, "Buffers flushed and reset. Camera system shutdown. All systems operational.")
src.verbs -= /mob/living/silicon/robot/proc/ResetSecurityCodes
/mob/living/silicon/robot/mode()
set name = "Activate Held Object"
set category = "IC"
set src = usr
if(incapacitated())
return
var/obj/item/W = get_active_held_item()
if(W)
W.attack_self(src)
/mob/living/silicon/robot/proc/SetLockdown(state = 1)
// They stay locked down if their wire is cut.
if(wires.is_cut(WIRE_LOCKDOWN))
state = 1
if(state)
throw_alert("locked", /obj/screen/alert/locked)
else
clear_alert("locked")
lockcharge = state
update_canmove()
/mob/living/silicon/robot/proc/SetEmagged(new_state)
emagged = new_state
module.rebuild_modules()
update_icons()
if(emagged)
throw_alert("hacked", /obj/screen/alert/hacked)
else
clear_alert("hacked")
/mob/living/silicon/robot/verb/outputlaws()
set category = "Robot Commands"
set name = "State Laws"
if(usr.stat == DEAD)
return //won't work if dead
checklaws()
/mob/living/silicon/robot/verb/set_automatic_say_channel() //Borg version of setting the radio for autosay messages.
set name = "Set Auto Announce Mode"
set desc = "Modify the default radio setting for stating your laws."
set category = "Robot Commands"
if(usr.stat == DEAD)
return //won't work if dead
set_autosay()
/mob/living/silicon/robot/proc/control_headlamp()
if(stat || lamp_cooldown > world.time || low_power_mode)
to_chat(src, "<span class='danger'>This function is currently offline.</span>")
return
//Some sort of magical "modulo" thing which somehow increments lamp power by 2, until it hits the max and resets to 0.
lamp_intensity = (lamp_intensity+2) % (lamp_max+2)
to_chat(src, "[lamp_intensity ? "Headlamp power set to Level [lamp_intensity/2]" : "Headlamp disabled."]")
update_headlamp()
/mob/living/silicon/robot/proc/update_headlamp(var/turn_off = 0, var/cooldown = 100)
set_light(0)
if(lamp_intensity && (turn_off || stat || low_power_mode))
to_chat(src, "<span class='danger'>Your headlamp has been deactivated.</span>")
lamp_intensity = 0
lamp_cooldown = world.time + cooldown
else
set_light(lamp_intensity)
if(lamp_button)
lamp_button.icon_state = "lamp[lamp_intensity]"
update_icons()
/mob/living/silicon/robot/proc/deconstruct()
var/turf/T = get_turf(src)
if (robot_suit)
robot_suit.forceMove(T)
robot_suit.l_leg.forceMove(T)
robot_suit.l_leg = null
robot_suit.r_leg.forceMove(T)
robot_suit.r_leg = null
new /obj/item/stack/cable_coil(T, robot_suit.chest.wired)
robot_suit.chest.forceMove(T)
robot_suit.chest.wired = 0
robot_suit.chest = null
robot_suit.l_arm.forceMove(T)
robot_suit.l_arm = null
robot_suit.r_arm.forceMove(T)
robot_suit.r_arm = null
robot_suit.head.forceMove(T)
robot_suit.head.flash1.forceMove(T)
robot_suit.head.flash1.burn_out()
robot_suit.head.flash1 = null
robot_suit.head.flash2.forceMove(T)
robot_suit.head.flash2.burn_out()
robot_suit.head.flash2 = null
robot_suit.head = null
robot_suit.updateicon()
else
new /obj/item/robot_suit(T)
new /obj/item/bodypart/l_leg/robot(T)
new /obj/item/bodypart/r_leg/robot(T)
new /obj/item/stack/cable_coil(T, 1)
new /obj/item/bodypart/chest/robot(T)
new /obj/item/bodypart/l_arm/robot(T)
new /obj/item/bodypart/r_arm/robot(T)
new /obj/item/bodypart/head/robot(T)
var/b
for(b=0, b!=2, b++)
var/obj/item/device/assembly/flash/handheld/F = new /obj/item/device/assembly/flash/handheld(T)
F.burn_out()
if (cell) //Sanity check.
cell.forceMove(T)
cell = null
qdel(src)
/mob/living/silicon/robot/modules
var/set_module = null
/mob/living/silicon/robot/modules/Initialize()
. = ..()
module.transform_to(set_module)
/mob/living/silicon/robot/modules/standard
set_module = /obj/item/robot_module/standard
/mob/living/silicon/robot/modules/medical
set_module = /obj/item/robot_module/medical
/mob/living/silicon/robot/modules/engineering
set_module = /obj/item/robot_module/engineering
/mob/living/silicon/robot/modules/security
set_module = /obj/item/robot_module/security
/mob/living/silicon/robot/modules/peacekeeper
set_module = /obj/item/robot_module/peacekeeper
/mob/living/silicon/robot/modules/miner
set_module = /obj/item/robot_module/miner
/mob/living/silicon/robot/modules/janitor
set_module = /obj/item/robot_module/janitor
/mob/living/silicon/robot/modules/syndicate
icon_state = "syndie_bloodhound"
faction = list("syndicate")
bubble_icon = "syndibot"
req_access = list(ACCESS_SYNDICATE)
lawupdate = FALSE
scrambledcodes = TRUE // These are rogue borgs.
ionpulse = TRUE
var/playstyle_string = "<span class='userdanger'>You are a Syndicate assault cyborg!</span><br>\
<b>You are armed with powerful offensive tools to aid you in your mission: help the operatives secure the nuclear authentication disk. \
Your cyborg LMG will slowly produce ammunition from your power supply, and your operative pinpointer will find and locate fellow nuclear operatives. \
<i>Help the operatives secure the disk at all costs!</i></b>"
set_module = /obj/item/robot_module/syndicate
/mob/living/silicon/robot/modules/syndicate/Initialize()
. = ..()
cell.maxcharge = 25000
cell.charge = 25000
radio = new /obj/item/device/radio/borg/syndicate(src)
laws = new /datum/ai_laws/syndicate_override()
addtimer(CALLBACK(src, .proc/show_playstyle), 5)
/mob/living/silicon/robot/modules/syndicate/proc/show_playstyle()
if(playstyle_string)
to_chat(src, playstyle_string)
/mob/living/silicon/robot/modules/syndicate/ResetModule()
return
/mob/living/silicon/robot/modules/syndicate/medical
icon_state = "syndi-medi"
playstyle_string = "<span class='userdanger'>You are a Syndicate medical cyborg!</span><br>\
<b>You are armed with powerful medical tools to aid you in your mission: help the operatives secure the nuclear authentication disk. \
Your hypospray will produce Restorative Nanites, a wonder-drug that will heal most types of bodily damages, including clone and brain damage. It also produces morphine for offense. \
Your defibrillator paddles can revive operatives through their hardsuits, or can be used on harm intent to shock enemies! \
Your energy saw functions as a circular saw, but can be activated to deal more damage, and your operative pinpointer will find and locate fellow nuclear operatives. \
<i>Help the operatives secure the disk at all costs!</i></b>"
set_module = /obj/item/robot_module/syndicate_medical
/mob/living/silicon/robot/proc/notify_ai(notifytype, oldname, newname)
if(!connected_ai)
return
switch(notifytype)
if(NEW_BORG) //New Cyborg
to_chat(connected_ai, "<br><br><span class='notice'>NOTICE - New cyborg connection detected: <a href='?src=[REF(connected_ai)];track=[html_encode(name)]'>[name]</a></span><br>")
if(NEW_MODULE) //New Module
to_chat(connected_ai, "<br><br><span class='notice'>NOTICE - Cyborg module change detected: [name] has loaded the [designation] module.</span><br>")
if(RENAME) //New Name
to_chat(connected_ai, "<br><br><span class='notice'>NOTICE - Cyborg reclassification detected: [oldname] is now designated as [newname].</span><br>")
if(AI_SHELL) //New Shell
to_chat(connected_ai, "<br><br><span class='notice'>NOTICE - New cyborg shell detected: <a href='?src=[REF(connected_ai)];track=[html_encode(name)]'>[name]</a></span><br>")
if(DISCONNECT) //Tampering with the wires
to_chat(connected_ai, "<br><br><span class='notice'>NOTICE - Remote telemetry lost with [name].</span><br>")
/mob/living/silicon/robot/canUseTopic(atom/movable/M, be_close = 0)
if(stat || lockcharge || low_power_mode)
return
if(be_close && !in_range(M, src))
return
return 1
/mob/living/silicon/robot/updatehealth()
..()
if(health < maxHealth*0.5) //Gradual break down of modules as more damage is sustained
if(uneq_module(held_items[3]))
to_chat(src, "<span class='warning'>SYSTEM ERROR: Module 3 OFFLINE.</span>")
if(health < 0)
if(uneq_module(held_items[2]))
to_chat(src, "<span class='warning'>SYSTEM ERROR: Module 2 OFFLINE.</span>")
if(health < -maxHealth*0.5)
if(uneq_module(held_items[1]))
to_chat(src, "<span class='warning'>CRITICAL ERROR: All modules OFFLINE.</span>")
/mob/living/silicon/robot/update_sight()
if(!client)
return
if(stat == DEAD)
sight = (SEE_TURFS|SEE_MOBS|SEE_OBJS)
see_in_dark = 8
see_invisible = SEE_INVISIBLE_OBSERVER
return
see_invisible = initial(see_invisible)
see_in_dark = initial(see_in_dark)
sight = initial(sight)
lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE
if(client.eye != src)
var/atom/A = client.eye
if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates.
return
if(sight_mode & BORGMESON)
sight |= SEE_TURFS
lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE
see_in_dark = 1
if(sight_mode & BORGMATERIAL)
sight |= SEE_OBJS
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
see_in_dark = 1
if(sight_mode & BORGXRAY)
sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
see_invisible = SEE_INVISIBLE_LIVING
see_in_dark = 8
if(sight_mode & BORGTHERM)
sight |= SEE_MOBS
see_invisible = min(see_invisible, SEE_INVISIBLE_LIVING)
see_in_dark = 8
if(see_override)
see_invisible = see_override
sync_lighting_plane_alpha()
/mob/living/silicon/robot/update_stat()
if(status_flags & GODMODE)
return
if(stat != DEAD)
if(health <= -maxHealth) //die only once
death()
return
if(IsUnconscious() || IsStun() || IsKnockdown() || getOxyLoss() > maxHealth*0.5)
if(stat == CONSCIOUS)
stat = UNCONSCIOUS
blind_eyes(1)
update_canmove()
update_headlamp()
else
if(stat == UNCONSCIOUS)
stat = CONSCIOUS
adjust_blindness(-1)
update_canmove()
update_headlamp()
diag_hud_set_status()
diag_hud_set_health()
diag_hud_set_aishell()
update_health_hud()
/mob/living/silicon/robot/revive(full_heal = 0, admin_revive = 0)
if(..()) //successfully ressuscitated from death
if(!QDELETED(builtInCamera) && !wires.is_cut(WIRE_CAMERA))
builtInCamera.toggle_cam(src,0)
update_headlamp()
if(admin_revive)
locked = TRUE
notify_ai(NEW_BORG)
. = 1
/mob/living/silicon/robot/fully_replace_character_name(oldname, newname)
..()
if(oldname != real_name)
notify_ai(RENAME, oldname, newname)
if(!QDELETED(builtInCamera))
builtInCamera.c_tag = real_name
custom_name = newname
/mob/living/silicon/robot/proc/ResetModule()
uneq_all()
shown_robot_modules = FALSE
if(hud_used)
hud_used.update_robot_modules_display()
module.transform_to(/obj/item/robot_module)
// Remove upgrades.
for(var/obj/item/I in upgrades)
I.forceMove(get_turf(src))
upgrades.Cut()
speed = 0
ionpulse = FALSE
revert_shell()
return 1
/mob/living/silicon/robot/proc/has_module()
if(!module || module.type == /obj/item/robot_module)
. = FALSE
else
. = TRUE
/mob/living/silicon/robot/proc/update_module_innate()
designation = module.name
if(hands)
hands.icon_state = module.moduleselect_icon
if(module.can_be_pushed)
status_flags |= CANPUSH
else
status_flags &= ~CANPUSH
if(module.clean_on_move)
flags_1 |= CLEAN_ON_MOVE_1
else
flags_1 &= ~CLEAN_ON_MOVE_1
hat_offset = module.hat_offset
magpulse = module.magpulsing
updatename()
/mob/living/silicon/robot/proc/place_on_head(obj/item/new_hat)
if(hat)
hat.forceMove(get_turf(src))
hat = new_hat
new_hat.forceMove(src)
update_icons()
/mob/living/silicon/robot/proc/make_shell(var/obj/item/borg/upgrade/ai/board)
if(!board)
upgrades |= new /obj/item/borg/upgrade/ai(src)
shell = TRUE
braintype = "AI Shell"
name = "[designation] AI Shell [rand(100,999)]"
real_name = name
GLOB.available_ai_shells |= src
if(!QDELETED(builtInCamera))
builtInCamera.c_tag = real_name //update the camera name too
diag_hud_set_aishell()
notify_ai(AI_SHELL)
/mob/living/silicon/robot/proc/revert_shell()
if(!shell)
return
undeploy()
for(var/obj/item/borg/upgrade/ai/boris in src)
//A player forced reset of a borg would drop the module before this is called, so this is for catching edge cases
qdel(boris)
shell = FALSE
GLOB.available_ai_shells -= src
name = "Unformatted Cyborg [rand(100,999)]"
real_name = name
if(!QDELETED(builtInCamera))
builtInCamera.c_tag = real_name
diag_hud_set_aishell()
/mob/living/silicon/robot/proc/deploy_init(var/mob/living/silicon/ai/AI)
real_name = "[AI.real_name] shell [rand(100, 999)] - [designation]" //Randomizing the name so it shows up separately in the shells list
name = real_name
if(!QDELETED(builtInCamera))
builtInCamera.c_tag = real_name //update the camera name too
mainframe = AI
deployed = TRUE
connected_ai = mainframe
mainframe.connected_robots |= src
lawupdate = TRUE
lawsync()
if(radio && AI.radio) //AI keeps all channels, including Syndie if it is a Traitor
if(AI.radio.syndie)
radio.make_syndie()
radio.subspace_transmission = TRUE
radio.channels = AI.radio.channels
for(var/chan in radio.channels)
radio.secure_radio_connections[chan] = add_radio(radio, GLOB.radiochannels[chan])
diag_hud_set_aishell()
undeployment_action.Grant(src)
/datum/action/innate/undeployment
name = "Disconnect from shell"
desc = "Stop controlling your shell and resume normal core operations."
icon_icon = 'icons/mob/actions/actions_AI.dmi'
button_icon_state = "ai_core"
/datum/action/innate/undeployment/Trigger()
if(!..())
return FALSE
var/mob/living/silicon/robot/R = owner
R.undeploy()
return TRUE
/mob/living/silicon/robot/proc/undeploy()
if(!deployed || !mind || !mainframe)
return
mainframe.redeploy_action.Grant(mainframe)
mainframe.redeploy_action.last_used_shell = src
mind.transfer_to(mainframe)
deployed = FALSE
mainframe.deployed_shell = null
undeployment_action.Remove(src)
if(radio) //Return radio to normal
radio.recalculateChannels()
if(!QDELETED(builtInCamera))
builtInCamera.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/attack_ai(mob/user)
if(shell && (!connected_ai || connected_ai == user))
var/mob/living/silicon/ai/AI = user
AI.deploy_to_shell(src)
/mob/living/silicon/robot/shell
shell = TRUE
/mob/living/silicon/robot/MouseDrop_T(mob/living/M, mob/living/user)
. = ..()
if(!(M in buckled_mobs) && isliving(M))
buckle_mob(M)
/mob/living/silicon/robot/buckle_mob(mob/living/M, force = FALSE, check_loc = TRUE)
if(!is_type_in_typecache(M, can_ride_typecache))
M.visible_message("<span class='warning'>[M] really can't seem to mount [src]...</span>")
return
if(!riding_datum)
riding_datum = new /datum/riding/cyborg(src)
if(buckled_mobs)
if(buckled_mobs.len >= max_buckled_mobs)
return
if(M in buckled_mobs)
return
if(stat)
return
if(incapacitated())
return
if(M.incapacitated())
return
if(module)
if(!module.allow_riding)
M.visible_message("<span class='boldwarning'>Unfortunately, [M] just can't seem to hold onto [src]!</span>")
return
if(iscarbon(M) && (!riding_datum.equip_buckle_inhands(M, 1)))
M.visible_message("<span class='boldwarning'>[M] can't climb onto [src] because [M.p_their()] hands are full!</span>")
return
. = ..(M, force, check_loc)
/mob/living/silicon/robot/unbuckle_mob(mob/user)
if(iscarbon(user))
if(riding_datum)
riding_datum.unequip_buckle_inhands(user)
riding_datum.restore_position(user)
. = ..(user)
/mob/living/silicon/robot/proc/TryConnectToAI()
connected_ai = select_active_ai_with_fewest_borgs()
if(connected_ai)
connected_ai.connected_robots += src
lawsync()
lawupdate = 1
return TRUE
return FALSE