mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-18 10:33:30 +01:00
59ce9b5ead
* Disallows installing duplicate cyborg upgrades. * Prevent duplicate Cyborg modules * Unecessary spacing removed * Update code/game/objects/items/robot/robot_upgrades.dm Co-authored-by: CRUNCH <143041327+Fordoxia@users.noreply.github.com> Signed-off-by: Adrer <erwin@lombok.demon.nl> * usr > user * Move nodrop check into do_install * ... Why did I do that? --------- Signed-off-by: Adrer <erwin@lombok.demon.nl> Co-authored-by: adrermail@gmail.com <adrermail@gmail.com> Co-authored-by: CRUNCH <143041327+Fordoxia@users.noreply.github.com>
1774 lines
59 KiB
Plaintext
1774 lines
59 KiB
Plaintext
GLOBAL_LIST_INIT(robot_verbs_default, list(
|
|
/mob/living/silicon/robot/proc/sensor_mode,
|
|
))
|
|
|
|
/mob/living/silicon/robot
|
|
name = "Cyborg"
|
|
real_name = "Cyborg"
|
|
icon = 'icons/mob/robots.dmi'
|
|
icon_state = "robot"
|
|
maxHealth = 100
|
|
health = 100
|
|
bubble_icon = "robot"
|
|
universal_understand = TRUE
|
|
deathgasp_on_death = TRUE
|
|
blocks_emissive = EMISSIVE_BLOCK_UNIQUE
|
|
hud_type = /datum/hud/robot
|
|
|
|
var/sight_mode = 0
|
|
var/custom_name = ""
|
|
var/custom_sprite = FALSE // Due to all the sprites involved, a var for our custom borgs may be best.
|
|
|
|
// HUD stuff.
|
|
var/atom/movable/screen/hands = null
|
|
var/atom/movable/screen/inv1 = null
|
|
var/atom/movable/screen/inv2 = null
|
|
var/atom/movable/screen/inv3 = null
|
|
var/atom/movable/screen/lamp_button = null
|
|
var/atom/movable/screen/thruster_button = null
|
|
|
|
// 3 Modules can be activated at any one time.
|
|
var/obj/item/robot_module/module = null
|
|
var/module_active = null
|
|
var/module_state_1 = null
|
|
var/module_state_2 = null
|
|
var/module_state_3 = null
|
|
|
|
var/obj/item/radio/borg/radio = null
|
|
var/mob/living/silicon/ai/connected_ai = null
|
|
var/obj/item/stock_parts/cell/cell = null
|
|
var/obj/machinery/camera/camera = null
|
|
|
|
/// Components are basically robot organs.
|
|
var/list/components = list()
|
|
|
|
/// Used for deconstruction to remember what the robot was constructed out of.
|
|
var/obj/item/robot_parts/robot_suit/robot_suit = null
|
|
/// Used for deconstruction to remember what the robot was constructed out of.
|
|
var/obj/item/mmi/mmi = null
|
|
|
|
var/obj/item/pda/silicon/robot/rbPDA = null
|
|
|
|
var/datum/wires/robot/wires = null
|
|
|
|
/// Is the robot's maintenance panel open?
|
|
var/opened = FALSE
|
|
/// Does the robot have a non-default sprite for an open service panel?
|
|
var/custom_panel = null
|
|
/// Robot skins with non-default sprites for an open service panel.
|
|
var/list/custom_panel_names = list("Cricket")
|
|
/// Robot skins with multiple variants for different modules. They require special handling to make their eyes display.
|
|
var/list/custom_eye_names = list("Cricket", "Standard")
|
|
/// Has the robot been emagged?
|
|
var/emagged = FALSE
|
|
/// Can the robot be emagged?
|
|
var/is_emaggable = TRUE
|
|
/// Is the robot protected from the visual portion of flashbangs and flashes?
|
|
var/eye_protection = FALSE
|
|
/// Is the robot protected from the audio component of flashbangs? Prevents inflicting confusion.
|
|
var/ear_protection = FALSE
|
|
/// All incoming damage has this number subtracted from it.
|
|
var/damage_protection = 0
|
|
/// Is the robot immune to EMPs?
|
|
var/emp_protection = FALSE
|
|
/// Incoming brute damage is multiplied by this number.
|
|
var/brute_mod = 1
|
|
/// Incoming burn damage is multiplied by this number.
|
|
var/burn_mod = 1
|
|
///If the cyborg is rebooting from stamcrit
|
|
var/rebooting = FALSE
|
|
///If the cyborg is in a charger, or otherwise receiving power from an outside source.
|
|
var/externally_powered = FALSE
|
|
///What the cyborg's maximum slowdown penalty is, if it has one.
|
|
var/slowdown_cap = INFINITY
|
|
var/list/force_modules
|
|
/// Can a robot rename itself with the Namepick verb?
|
|
var/allow_rename = TRUE
|
|
/// Setting to TRUE unlocks a borg's Safety Override modules.
|
|
var/weapons_unlock = FALSE
|
|
var/static_radio_channels = FALSE
|
|
|
|
var/wiresexposed = FALSE
|
|
/// Is the robot's cover locked?
|
|
var/locked = TRUE
|
|
/// Determines the ID access needed to unlock the robot's cover.
|
|
var/list/req_one_access = list(ACCESS_ROBOTICS)
|
|
/// Used for robot access checks.
|
|
var/list/req_access
|
|
/// Used when generating the number in default robot names.
|
|
var/ident = 0
|
|
//var/list/laws = list()
|
|
var/viewalerts = FALSE
|
|
/// What specialisation the robot has.
|
|
var/modtype = "Default"
|
|
var/datum/effect_system/spark_spread/spark_system //So they can initialize sparks whenever/N
|
|
/// Has the robot's power cell run out of charge?
|
|
var/low_power_mode = FALSE
|
|
/// Determines if the robot tries to sync its laws to a connected AI.
|
|
var/lawupdate = TRUE
|
|
/// Used when locking down a robot to preserve cell charge.
|
|
var/lockcharge
|
|
/// Speed modifier. Positive numbers reduce speed, negative numbers increase it.
|
|
var/speed = 0
|
|
/// If set to TRUE, a robot will not be visible on the robotics control console.
|
|
var/scrambledcodes = FALSE
|
|
/// If set to TRUE, a robot can re-lock its own cover.
|
|
var/can_lock_cover = FALSE
|
|
/// Determines if a robot acts as a mobile security camera that can be observed through security consoles or by an AI.
|
|
var/has_camera = TRUE
|
|
/// If set to TRUE, the robot will be hidden on the PDA messenger list.
|
|
var/pdahide = FALSE
|
|
/// The number of known entities currently accessing the internal camera.
|
|
var/tracking_entities = 0
|
|
/// Determines if the robot is referred to as an "Android", "Robot", or "Cyborg" based on the type of brain inside.
|
|
var/braintype = "Cyborg"
|
|
/// The default skin of some special robots.
|
|
var/base_icon = ""
|
|
/// If set to TRUE, the robot's 3 module slots will progressively become unusable as they take damage.
|
|
var/modules_break = TRUE
|
|
|
|
/// Maximum brightness of a robot's headlamp. Set as a var for easy adjusting.
|
|
var/lamp_max = 10
|
|
/// Luminosity of the headlamp. 0 is off. Higher settings than the minimum require power. Increments in steps of 2.
|
|
var/lamp_intensity = 0
|
|
/// Flag for if the headlamp is on cooldown after being forcibly disabled (e.g. by a shadow deamon).
|
|
var/lamp_recharging = FALSE
|
|
|
|
/// When the camera moved signal was sent last. Avoid overdoing it.
|
|
var/last_camera_update
|
|
|
|
hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD)
|
|
|
|
var/default_cell_type = /obj/item/stock_parts/cell/high
|
|
/// Does the robot have ion thrusters installed?
|
|
var/ionpulse = FALSE
|
|
/// Are the robot's ion thrusters activated?
|
|
var/ionpulse_on = FALSE
|
|
/// Does the robot clean dirt under it when it moves onto a tile?
|
|
var/floorbuffer = FALSE
|
|
|
|
var/datum/action/item_action/toggle_research_scanner/scanner = null
|
|
var/list/module_actions = list()
|
|
|
|
/// If set to TRUE, the robot can see the types and precice quantities of reagents in transparent containers, the % amount of different reagents in opaque containers, and can identify different blood types.
|
|
var/has_advanced_reagent_vision = FALSE
|
|
|
|
/// Integer used to determine self-mailing location, used only by drones and saboteur robots.
|
|
var/mail_destination = 1
|
|
var/datum/ui_module/robot_self_diagnosis/self_diagnosis
|
|
var/datum/ui_module/destination_tagger/mail_setter
|
|
silicon_subsystems = list(
|
|
/mob/living/silicon/robot/proc/self_diagnosis,
|
|
/mob/living/silicon/proc/subsystem_law_manager)
|
|
|
|
/mob/living/silicon/robot/get_cell()
|
|
return cell
|
|
|
|
/mob/living/silicon/robot/New(loc, syndie = FALSE, unfinished = FALSE, alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
|
|
spark_system = new /datum/effect_system/spark_spread()
|
|
spark_system.set_up(5, 0, src)
|
|
spark_system.attach(src)
|
|
|
|
add_language("Robot Talk", 1)
|
|
|
|
wires = new(src)
|
|
|
|
ident = rand(1, 999)
|
|
rename_character(null, get_default_name())
|
|
update_icons()
|
|
update_headlamp()
|
|
|
|
radio = new /obj/item/radio/borg(src)
|
|
|
|
if(has_camera && !camera)
|
|
camera = new /obj/machinery/camera(src)
|
|
camera.c_tag = real_name
|
|
camera.network = list("SS13","Robots")
|
|
if(wires.is_cut(WIRE_BORG_CAMERA)) // 5 = BORG CAMERA
|
|
camera.status = FALSE
|
|
|
|
if(mmi == null)
|
|
mmi = new /obj/item/mmi/robotic_brain(src) //Give the borg an MMI if he spawns without for some reason. (probably not the correct way to spawn a robotic brain, but it works)
|
|
mmi.icon_state = "boris"
|
|
|
|
initialize_components()
|
|
//if(!unfinished)
|
|
// Create all the robot parts.
|
|
for(var/V in components) if(V != "power cell")
|
|
var/datum/robot_component/C = components[V]
|
|
C.install(new C.external_type, FALSE)
|
|
|
|
..()
|
|
|
|
add_robot_verbs()
|
|
|
|
// Remove inherited verbs that effectively do nothing for cyborgs, or lead to unintended behaviour.
|
|
remove_verb(src, /mob/living/verb/rest)
|
|
remove_verb(src, /mob/living/verb/mob_sleep)
|
|
|
|
// Install a default cell into the borg if none is there yet
|
|
var/datum/robot_component/cell_component = components["power cell"]
|
|
var/obj/item/stock_parts/cell/C = cell || new default_cell_type(src)
|
|
cell_component.install(C)
|
|
|
|
init(alien, connect_to_AI, ai_to_sync_to)
|
|
|
|
diag_hud_set_borgcell()
|
|
scanner = new(src)
|
|
scanner.Grant(src)
|
|
RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(create_trail))
|
|
RegisterSignal(src, COMSIG_ENTERED_BORGCHARGER, PROC_REF(gain_external_power))
|
|
RegisterSignal(src, COMSIG_EXITED_BORGCHARGER, PROC_REF(lose_external_power))
|
|
robot_module_hat_offset(icon_state)
|
|
|
|
/mob/living/silicon/robot/get_radio()
|
|
return radio
|
|
|
|
/mob/living/silicon/robot/proc/create_trail(datum/source, atom/oldloc, _dir, forced)
|
|
if(ionpulse_on)
|
|
var/turf/T = get_turf(oldloc)
|
|
if(!has_gravity(T))
|
|
new /obj/effect/particle_effect/ion_trails(T, _dir)
|
|
|
|
/mob/living/silicon/robot/proc/init(alien, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
|
|
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
|
|
make_laws()
|
|
additional_law_channels["Binary"] = ":b "
|
|
if(!connect_to_AI)
|
|
return
|
|
var/found_ai = ai_to_sync_to
|
|
if(!found_ai)
|
|
found_ai = select_active_ai_with_fewest_borgs()
|
|
if(found_ai)
|
|
lawupdate = TRUE
|
|
connect_to_ai(found_ai)
|
|
else
|
|
lawupdate = FALSE
|
|
|
|
playsound(loc, 'sound/voice/liveagain.ogg', 75, 1)
|
|
|
|
/mob/living/silicon/robot/rename_character(oldname, newname)
|
|
if(!..(oldname, newname))
|
|
return FALSE
|
|
|
|
if(oldname != real_name)
|
|
notify_ai(3, oldname, newname)
|
|
custom_name = (newname != get_default_name()) ? newname : null
|
|
setup_PDA()
|
|
|
|
//We also need to update name of internal camera.
|
|
if(camera)
|
|
camera.c_tag = newname
|
|
|
|
//Check for custom sprite
|
|
check_custom_sprite()
|
|
|
|
if(mmi && mmi.brainmob)
|
|
mmi.brainmob.name = newname
|
|
|
|
return TRUE
|
|
|
|
/mob/living/silicon/robot/proc/check_custom_sprite()
|
|
if(!custom_sprite && (ckey in GLOB.configuration.custom_sprites.cyborg_ckeys))
|
|
custom_sprite = TRUE
|
|
|
|
|
|
/mob/living/silicon/robot/proc/get_default_name(prefix as text)
|
|
if(prefix)
|
|
modtype = prefix
|
|
if(mmi)
|
|
if(istype(mmi, /obj/item/mmi/robotic_brain))
|
|
braintype = "Android"
|
|
else
|
|
braintype = "Cyborg"
|
|
else
|
|
braintype = "Robot"
|
|
|
|
if(custom_name)
|
|
return custom_name
|
|
else
|
|
return "[modtype] [braintype]-[num2text(ident)]"
|
|
|
|
/mob/living/silicon/robot/verb/Namepick()
|
|
set category = "Robot Commands"
|
|
if(custom_name)
|
|
return 0
|
|
if(!allow_rename)
|
|
to_chat(src, "<span class='warning'>Rename functionality is not enabled on this unit.</span>")
|
|
return 0
|
|
rename_self(braintype, 1)
|
|
|
|
/mob/living/silicon/robot/proc/sync()
|
|
if(lawupdate && connected_ai)
|
|
lawsync()
|
|
photosync()
|
|
|
|
// setup the PDA and its name
|
|
/mob/living/silicon/robot/proc/setup_PDA()
|
|
if(!rbPDA)
|
|
rbPDA = new(src)
|
|
rbPDA.set_name_and_job(real_name, braintype)
|
|
var/datum/data/pda/app/messenger/M = rbPDA.find_program(/datum/data/pda/app/messenger)
|
|
if(M)
|
|
if(scrambledcodes)
|
|
M.hidden = 1
|
|
if(pdahide)
|
|
M.toff = 1
|
|
|
|
/mob/living/silicon/robot/binarycheck()
|
|
if(is_component_functioning("comms"))
|
|
return 1
|
|
return 0
|
|
|
|
//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO
|
|
//Improved /N
|
|
/mob/living/silicon/robot/Destroy()
|
|
SStgui.close_uis(wires)
|
|
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)
|
|
mind.transfer_to(mmi.brainmob)
|
|
mmi.update_icon()
|
|
else
|
|
to_chat(src, "<span class='boldannounceooc'>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("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
|
|
QDEL_NULL(wires)
|
|
QDEL_NULL(module)
|
|
QDEL_NULL(camera)
|
|
QDEL_NULL(robot_suit)
|
|
QDEL_NULL(spark_system)
|
|
QDEL_NULL(self_diagnosis)
|
|
QDEL_NULL(mail_setter)
|
|
QDEL_LIST_ASSOC_VAL(components)
|
|
QDEL_NULL(rbPDA)
|
|
QDEL_NULL(radio)
|
|
scanner = null
|
|
module_actions.Cut()
|
|
return ..()
|
|
|
|
/mob/living/silicon/robot/proc/pick_module()
|
|
if(module)
|
|
return
|
|
// Pick a module type
|
|
var/selected_module = show_radial_menu(src, src, get_module_types(), radius = 42)
|
|
if(!selected_module || module)
|
|
return
|
|
// Pick a sprite
|
|
var/module_sprites = get_module_sprites(selected_module)
|
|
var/selected_sprite = show_radial_menu(src, src, module_sprites, radius = 42)
|
|
if(!selected_sprite)
|
|
return
|
|
|
|
// Now actually set the module and sprites
|
|
initialize_module(selected_module, selected_sprite, module_sprites)
|
|
|
|
/**
|
|
* Returns a list of choosable module types, associated with the module icon for the radial menu.
|
|
*
|
|
* Key: Module name | Value: Module 'icon'
|
|
*
|
|
* By default this returns the Engineering, Janitor, Medical, Mining, and Service modules.
|
|
* If there are any [/mob/living/silicon/robot/var/force_modules] set, then they are returned instead.
|
|
* If the MMI has a xenomorph brain in it ([/obj/item/mmi/var/alien]), then only the "Hunter" and standard modules is returned.
|
|
*/
|
|
/mob/living/silicon/robot/proc/get_module_types()
|
|
var/static/list/standard_modules = list(
|
|
"Engineering" = image('icons/mob/robots.dmi', "engi-radial"),
|
|
"Janitor" = image('icons/mob/robots.dmi', "jan-radial"),
|
|
"Medical" = image('icons/mob/robots.dmi', "med-radial"),
|
|
"Mining" = image('icons/mob/robots.dmi', "mining-radial"),
|
|
"Service" = image('icons/mob/robots.dmi', "serv-radial"))
|
|
var/static/list/special_modules = list(
|
|
"Combat" = image('icons/mob/robots.dmi', "security-radial"),
|
|
"Security" = image('icons/mob/robots.dmi', "security-radial"),
|
|
"Destroyer" = image('icons/mob/robots.dmi', "droidcombat"),
|
|
"Hunter" = image('icons/mob/robots.dmi', "xeno-radial"))
|
|
|
|
if(mmi?.alien)
|
|
if(!length(force_modules))
|
|
force_modules = list("Hunter") + standard_modules.Copy() // standard PLUS hunter
|
|
|
|
// Return a list of `force_modules`, with the associated images from the other lists.
|
|
if(length(force_modules))
|
|
return (standard_modules + special_modules) & force_modules
|
|
|
|
return standard_modules
|
|
|
|
/**
|
|
* Returns an associative list of possible borg sprites based on the `selected_module`.
|
|
*
|
|
* Key: Sprite name | Value: Sprite icon
|
|
*
|
|
* Arguments:
|
|
* * selected_module - The chosen cyborg module to get the sprites for.
|
|
*/
|
|
/mob/living/silicon/robot/proc/get_module_sprites(selected_module)
|
|
var/list/module_sprites
|
|
switch(selected_module)
|
|
if("Engineering")
|
|
module_sprites = list(
|
|
"Basic" = image('icons/mob/robots.dmi', "Engineering"),
|
|
"Antique" = image('icons/mob/robots.dmi', "engineerrobot"),
|
|
"Landmate" = image('icons/mob/robots.dmi', "landmate"),
|
|
"Standard" = image('icons/mob/robots.dmi', "Standard-Engi"),
|
|
"Noble-ENG" = image('icons/mob/robots.dmi', "Noble-ENG"),
|
|
"Cricket" = image('icons/mob/robots.dmi', "Cricket-ENGI")
|
|
)
|
|
if("Janitor")
|
|
module_sprites = list(
|
|
"Basic" = image('icons/mob/robots.dmi', "JanBot2"),
|
|
"Mopbot" = image('icons/mob/robots.dmi', "janitorrobot"),
|
|
"Mop Gear Rex" = image('icons/mob/robots.dmi', "mopgearrex"),
|
|
"Standard" = image('icons/mob/robots.dmi', "Standard-Jani"),
|
|
"Noble-CLN" = image('icons/mob/robots.dmi', "Noble-CLN"),
|
|
"Cricket" = image('icons/mob/robots.dmi', "Cricket-JANI"),
|
|
"Custodiborg" = image('icons/mob/robots.dmi', "custodiborg")
|
|
)
|
|
if("Medical")
|
|
module_sprites = list(
|
|
"Basic" = image('icons/mob/robots.dmi', "Medbot"),
|
|
"Surgeon" = image('icons/mob/robots.dmi', "surgeon"),
|
|
"Advanced Droid" = image('icons/mob/robots.dmi', "droid-medical"),
|
|
"Needles" = image('icons/mob/robots.dmi', "medicalrobot"),
|
|
"Standard" = image('icons/mob/robots.dmi', "Standard-Medi"),
|
|
"Noble-MED" = image('icons/mob/robots.dmi', "Noble-MED"),
|
|
"Cricket" = image('icons/mob/robots.dmi', "Cricket-MEDI"),
|
|
"Qualified Doctor" = image('icons/mob/robots.dmi', "qualified_doctor")
|
|
)
|
|
if("Mining")
|
|
module_sprites = list(
|
|
"Basic" = image('icons/mob/robots.dmi', "Miner_old"),
|
|
"Advanced Droid" = image('icons/mob/robots.dmi', "droid-miner"),
|
|
"Treadhead" = image('icons/mob/robots.dmi', "Miner"),
|
|
"Standard" = image('icons/mob/robots.dmi', "Standard-Mine"),
|
|
"Noble-DIG" = image('icons/mob/robots.dmi', "Noble-DIG"),
|
|
"Cricket" = image('icons/mob/robots.dmi', "Cricket-MINE"),
|
|
"Lavaland" = image('icons/mob/robots.dmi', "lavaland"),
|
|
"Squat" = image('icons/mob/robots.dmi', "squatminer"),
|
|
"Coffin Drill" = image('icons/mob/robots.dmi', "coffinMiner")
|
|
)
|
|
if("Service")
|
|
module_sprites = list(
|
|
"Waitress" = image('icons/mob/robots.dmi', "Service"),
|
|
"Kent" = image('icons/mob/robots.dmi', "toiletbot"),
|
|
"Bro" = image('icons/mob/robots.dmi', "Brobot"),
|
|
"Rich" = image('icons/mob/robots.dmi', "maximillion"),
|
|
"Default" = image('icons/mob/robots.dmi', "Service2"),
|
|
"Standard" = image('icons/mob/robots.dmi', "Standard-Serv"),
|
|
"Noble-SRV" = image('icons/mob/robots.dmi', "Noble-SRV"),
|
|
"Cricket" = image('icons/mob/robots.dmi', "Cricket-SERV")
|
|
)
|
|
if("Combat")
|
|
module_sprites = list(
|
|
"Combat" = image('icons/mob/robots.dmi', "ertgamma")
|
|
)
|
|
if("Security")
|
|
module_sprites = list(
|
|
"Basic" = image('icons/mob/robots.dmi', "secborg"),
|
|
"Red Knight" = image('icons/mob/robots.dmi', "Security"),
|
|
"Black Knight" = image('icons/mob/robots.dmi', "securityrobot"),
|
|
"Bloodhound" = image('icons/mob/robots.dmi', "bloodhound"),
|
|
"Standard" = image('icons/mob/robots.dmi', "Standard-Secy"),
|
|
"Noble-SEC" = image('icons/mob/robots.dmi', "Noble-SEC"),
|
|
"Cricket" = image('icons/mob/robots.dmi', "Cricket-SEC"),
|
|
"Heavy" = image('icons/mob/robots.dmi', "heavySec")
|
|
)
|
|
if("Destroyer") //for Adminbus presumably
|
|
module_sprites = list(
|
|
"Destroyer" = image('icons/mob/robots.dmi', "droidcombat")
|
|
)
|
|
if("Hunter")
|
|
module_sprites = list(
|
|
"Xeno-Hu" = image('icons/mob/robots.dmi', "xenoborg-state-a")
|
|
)
|
|
|
|
if(custom_sprite && check_sprite("[ckey]-[selected_module]"))
|
|
module_sprites["Custom"] = image('icons/mob/custom_synthetic/custom-synthetic.dmi', "[ckey]-[selected_module]")
|
|
|
|
return module_sprites
|
|
|
|
/**
|
|
* Sets the offset for a cyborg's hats based on their module icon.
|
|
* Borgs are grouped by similar sprites (Eg. all the Noble borgs are all the same sprite but recoloured.)
|
|
*
|
|
* Arguments:
|
|
* * module - An `icon_state` for which the offset needs to be calculated.
|
|
*/
|
|
/mob/living/silicon/robot/proc/robot_module_hat_offset(module)
|
|
switch(module)
|
|
if("Engineering", "Miner_old", "JanBot2", "Medbot", "engineerrobot", "maximillion", "secborg", "Hydrobot")
|
|
can_be_hatted = FALSE // Their base sprite already comes with a hat
|
|
hat_offset_y = -1
|
|
if("Noble-CLN", "Noble-SRV", "Noble-DIG", "Noble-MED", "Noble-SEC", "Noble-ENG", "Noble-STD")
|
|
can_be_hatted = TRUE
|
|
can_wear_restricted_hats = TRUE
|
|
hat_offset_y = 4
|
|
if("droid-medical")
|
|
can_be_hatted = TRUE
|
|
can_wear_restricted_hats = TRUE
|
|
hat_offset_y = 4
|
|
if("droid-miner", "mk2", "mk3")
|
|
can_be_hatted = TRUE
|
|
is_centered = TRUE
|
|
hat_offset_y = 3
|
|
if("bloodhound", "nano_bloodhound", "syndie_bloodhound", "ertgamma")
|
|
can_be_hatted = TRUE
|
|
hat_offset_y = 1
|
|
if("Cricket-SEC", "Cricket-MEDI", "Cricket-JANI", "Cricket-ENGI", "Cricket-MINE", "Cricket-SERV")
|
|
can_be_hatted = TRUE
|
|
hat_offset_y = 2
|
|
if("droidcombat-shield", "droidcombat")
|
|
can_be_hatted = TRUE
|
|
hat_alpha = 255
|
|
hat_offset_y = 2
|
|
if("droidcombat-roll")
|
|
can_be_hatted = TRUE
|
|
hat_alpha = 0
|
|
hat_offset_y = 2
|
|
if("syndi-medi", "surgeon", "toiletbot", "custodiborg")
|
|
can_be_hatted = TRUE
|
|
is_centered = TRUE
|
|
hat_offset_y = 1
|
|
if("Security", "janitorrobot", "medicalrobot")
|
|
can_be_hatted = TRUE
|
|
is_centered = TRUE
|
|
can_wear_restricted_hats = TRUE
|
|
hat_offset_y = -1
|
|
if("Brobot", "Service", "Service2", "robot_old", "securityrobot")
|
|
can_be_hatted = TRUE
|
|
is_centered = TRUE
|
|
can_wear_restricted_hats = TRUE
|
|
hat_offset_y = -1
|
|
if("Miner", "lavaland")
|
|
can_be_hatted = TRUE
|
|
hat_offset_y = -1
|
|
if("robot", "Standard", "Standard-Secy", "Standard-Medi", "Standard-Engi",
|
|
"Standard-Jani", "Standard-Serv", "Standard-Mine", "xenoborg-state-a")
|
|
can_be_hatted = TRUE
|
|
hat_offset_y = -3
|
|
if("droid")
|
|
can_be_hatted = TRUE
|
|
is_centered = TRUE
|
|
can_wear_restricted_hats = TRUE
|
|
hat_offset_y = -4
|
|
if("landmate", "syndi-engi")
|
|
can_be_hatted = TRUE
|
|
hat_offset_y = -7
|
|
if("mopgearrex")
|
|
can_be_hatted = TRUE
|
|
hat_offset_y = -6
|
|
if("qualified_doctor")
|
|
can_be_hatted = TRUE
|
|
hat_offset_y = 3
|
|
if("squatminer")
|
|
can_be_hatted = TRUE
|
|
if("coffinMiner")
|
|
can_be_hatted = TRUE
|
|
hat_offset_y = 3
|
|
if("heavySec")
|
|
can_be_hatted = TRUE
|
|
can_wear_restricted_hats = TRUE
|
|
|
|
if(silicon_hat)
|
|
if(!can_be_hatted)
|
|
remove_from_head(usr)
|
|
return
|
|
if(!can_wear_restricted_hats && is_type_in_list(silicon_hat, restricted_hats))
|
|
remove_from_head(usr)
|
|
return
|
|
|
|
/**
|
|
* Sets up the module items and sprites for the cyborg module chosen in `pick_module()`.
|
|
*
|
|
* Arguments:
|
|
* * selected_module - The name of the module chosen by the player in the previous procs.
|
|
* * selected_sprite - The name of the sprite chosen by the player in the previous procs.
|
|
* * module_sprites - The list of sprites possible for the given module. Used to transfer the `icon` and `icon_state` variables to the player.
|
|
*/
|
|
/mob/living/silicon/robot/proc/initialize_module(selected_module, selected_sprite, list/module_sprites)
|
|
switch(selected_module)
|
|
if("Engineering")
|
|
module = new /obj/item/robot_module/engineering(src)
|
|
module.channels = list("Engineering" = 1)
|
|
if(camera && ("Robots" in camera.network))
|
|
camera.network += "Engineering"
|
|
if("Janitor")
|
|
module = new /obj/item/robot_module/janitor(src)
|
|
module.channels = list("Service" = 1)
|
|
if("Medical")
|
|
module = new /obj/item/robot_module/medical(src)
|
|
module.channels = list("Medical" = 1)
|
|
if(camera && ("Robots" in camera.network))
|
|
camera.network += "Medical"
|
|
status_flags &= ~CANPUSH
|
|
has_advanced_reagent_vision = TRUE
|
|
if("Mining")
|
|
module = new /obj/item/robot_module/miner(src)
|
|
module.channels = list("Supply" = 1)
|
|
if(camera && ("Robots" in camera.network))
|
|
camera.network += "Mining Outpost"
|
|
if("Service")
|
|
module = new /obj/item/robot_module/butler(src)
|
|
module.channels = list("Service" = 1)
|
|
has_advanced_reagent_vision = TRUE
|
|
if(selected_sprite == "Bro")
|
|
module.module_type = "Brobot"
|
|
if("Combat")
|
|
module = new /obj/item/robot_module/combat(src)
|
|
status_flags &= ~CANPUSH
|
|
if("Security")
|
|
module = new /obj/item/robot_module/security(src)
|
|
status_flags &= ~CANPUSH
|
|
if("Hunter")
|
|
module = new /obj/item/robot_module/alien/hunter(src)
|
|
|
|
if(!module)
|
|
return FALSE
|
|
modtype = selected_module
|
|
designation = selected_module
|
|
module.add_languages(src)
|
|
module.add_armor(src)
|
|
module.add_subsystems_and_actions(src)
|
|
if(!static_radio_channels)
|
|
radio.config(module.channels)
|
|
rename_character(real_name, get_default_name())
|
|
|
|
var/image/sprite_image = module_sprites[selected_sprite]
|
|
var/list/names = splittext(selected_sprite, "-")
|
|
icon = sprite_image.icon
|
|
icon_state = sprite_image.icon_state
|
|
custom_panel = trim(names[1])
|
|
|
|
update_module_icon()
|
|
robot_module_hat_offset(icon_state)
|
|
update_icons()
|
|
if(client.stat_tab == "Status")
|
|
SSstatpanels.set_status_tab(client)
|
|
SSblackbox.record_feedback("tally", "cyborg_modtype", 1, "[lowertext(selected_module)]")
|
|
notify_ai(2)
|
|
/// Take the borg's upgrades and spill them on the floor
|
|
/mob/living/silicon/robot/proc/spill_upgrades()
|
|
for(var/obj/item/borg/upgrade/U in contents)
|
|
if(istype(U, /obj/item/borg/upgrade/reset)) // The reset module is supposed to be consumed on use, this stops it from dropping on the floor if used
|
|
QDEL_NULL(U)
|
|
U.forceMove(get_turf(src))
|
|
|
|
/mob/living/silicon/robot/proc/reset_module()
|
|
notify_ai(2)
|
|
client?.screen -= hud_used.module_store_icon
|
|
uneq_all()
|
|
SStgui.close_user_uis(src)
|
|
sight_mode = null
|
|
update_sight()
|
|
hands.icon_state = "nomod"
|
|
icon_state = "robot"
|
|
module.remove_subsystems_and_actions(src)
|
|
QDEL_NULL(module)
|
|
|
|
camera.network.Remove(list("Engineering", "Medical", "Mining Outpost"))
|
|
rename_character(real_name, get_default_name("Default"))
|
|
languages = list()
|
|
speech_synthesizer_langs = list()
|
|
radio.recalculateChannels()
|
|
custom_panel = null
|
|
|
|
robot_module_hat_offset(icon_state)
|
|
update_icons()
|
|
update_headlamp()
|
|
|
|
spill_upgrades()
|
|
speed = 0 // Strip lingering upgrade effects.
|
|
ionpulse = FALSE
|
|
weapons_unlock = FALSE
|
|
add_language("Robot Talk", TRUE)
|
|
if("lava" in weather_immunities) // Remove the lava-immunity effect given by a printable upgrade
|
|
weather_immunities -= "lava"
|
|
armor = getArmor(arglist(initial(armor)))
|
|
slowdown_cap = INFINITY
|
|
status_flags |= CANPUSH
|
|
|
|
hud_used.update_robot_modules_display()
|
|
|
|
//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()
|
|
|
|
// this verb lets cyborgs see the stations manifest
|
|
/mob/living/silicon/robot/verb/cmd_station_manifest()
|
|
set category = "Robot Commands"
|
|
set name = "Show Station Manifest"
|
|
show_station_manifest()
|
|
|
|
/mob/living/silicon/robot/verb/toggle_component()
|
|
set category = "Robot Commands"
|
|
set name = "Toggle Component"
|
|
set desc = "Toggle a component, conserving power."
|
|
|
|
var/list/installed_components = list()
|
|
for(var/V in components)
|
|
if(V == "power cell") continue
|
|
var/datum/robot_component/C = components[V]
|
|
if(!C.is_missing())
|
|
installed_components += V
|
|
|
|
var/toggle = tgui_input_list(src, "Which component do you want to toggle?", "Toggle Component", installed_components)
|
|
if(!toggle)
|
|
return
|
|
|
|
var/datum/robot_component/C = components[toggle]
|
|
C.toggle()
|
|
to_chat(src, "<span class='warning'>You [C.toggled ? "enable" : "disable"] [C.name].</span>")
|
|
|
|
/mob/living/silicon/robot/proc/sensor_mode()
|
|
set name = "Set Sensor Augmentation"
|
|
set desc = "Augment visual feed with internal sensor overlays."
|
|
set category = "Robot Commands"
|
|
toggle_sensor_mode()
|
|
|
|
/mob/living/silicon/robot/proc/add_robot_verbs()
|
|
add_verb(src, GLOB.robot_verbs_default)
|
|
add_verb(src, silicon_subsystems)
|
|
|
|
/mob/living/silicon/robot/proc/remove_robot_verbs()
|
|
remove_verb(src, GLOB.robot_verbs_default)
|
|
remove_verb(src, silicon_subsystems)
|
|
|
|
/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()
|
|
|
|
/mob/living/silicon/robot/proc/robot_alerts()
|
|
var/list/dat = list()
|
|
var/list/list/temp_alarm_list = GLOB.alarm_manager.alarms.Copy()
|
|
for(var/cat in temp_alarm_list)
|
|
if(!(cat in alarms_listend_for))
|
|
continue
|
|
dat += "<B>[cat]</B><BR>\n"
|
|
var/list/list/L = temp_alarm_list[cat].Copy()
|
|
for(var/alarm in L)
|
|
var/list/list/alm = L[alarm].Copy()
|
|
var/list/list/sources = alm[3].Copy()
|
|
var/area_name = alm[1]
|
|
for(var/thing in sources)
|
|
var/atom/A = locateUID(thing)
|
|
if(A && A.z != z)
|
|
L -= alarm
|
|
continue
|
|
dat += "<NOBR>"
|
|
dat += "-- [area_name]"
|
|
dat += "</NOBR><BR>\n"
|
|
if(!length(L))
|
|
dat += "-- All Systems Nominal<BR>\n"
|
|
dat += "<BR>\n"
|
|
|
|
var/datum/browser/alerts = new(usr, "robotalerts", "Current Station Alerts", 400, 410)
|
|
var/dat_text = dat.Join("")
|
|
alerts.set_content(dat_text)
|
|
alerts.open()
|
|
|
|
/mob/living/silicon/robot/proc/ionpulse()
|
|
if(!ionpulse_on)
|
|
return
|
|
|
|
if(cell.charge <= 50)
|
|
toggle_ionpulse()
|
|
return
|
|
|
|
cell.charge -= 25 // 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
|
|
|
|
ionpulse_on = !ionpulse_on
|
|
to_chat(src, "<span class='notice'>You [ionpulse_on ? null :"de"]activate your ion thrusters.</span>")
|
|
if(thruster_button)
|
|
thruster_button.icon_state = "ionpulse[ionpulse_on]"
|
|
|
|
/mob/living/silicon/robot/blob_act(obj/structure/blob/B)
|
|
if(stat != DEAD)
|
|
adjustBruteLoss(30)
|
|
else
|
|
gib()
|
|
return TRUE
|
|
|
|
// this function displays the cyborgs current cell charge in the stat panel
|
|
/mob/living/silicon/robot/proc/show_cell_power()
|
|
return list("Charge Left:", cell ? "[cell.charge]/[cell.maxcharge]" : "No Cell Inserted!")
|
|
|
|
/mob/living/silicon/robot/proc/show_gps_coords()
|
|
var/turf/turf = get_turf(src)
|
|
return list("GPS:", "[COORD(turf)]")
|
|
|
|
/mob/living/silicon/robot/proc/show_stack_energy(datum/robot_storage/robot_storage)
|
|
return list("[robot_storage.statpanel_name]:", "[robot_storage.amount] / [robot_storage.max_amount]")
|
|
|
|
// update the status screen display
|
|
/mob/living/silicon/robot/get_status_tab_items()
|
|
var/list/status_tab_data = ..()
|
|
. = status_tab_data
|
|
|
|
status_tab_data[++status_tab_data.len] = show_cell_power()
|
|
|
|
if(!module)
|
|
return
|
|
|
|
if(locate(/obj/item/gps/cyborg) in module.modules)
|
|
status_tab_data[++status_tab_data.len] = show_gps_coords()
|
|
|
|
for(var/datum/robot_storage/robot_storage in module.storages)
|
|
status_tab_data[++status_tab_data.len] = show_stack_energy(robot_storage)
|
|
|
|
/mob/living/silicon/robot/restrained()
|
|
return 0
|
|
|
|
/mob/living/silicon/robot/InCritical()
|
|
return low_power_mode
|
|
|
|
/mob/living/silicon/robot/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
|
|
if(!(class in alarms_listend_for))
|
|
return
|
|
if(alarmsource.z != z)
|
|
return
|
|
if(stat == DEAD)
|
|
return
|
|
queueAlarm(text("--- [class] alarm detected in [A.name]!"), class)
|
|
|
|
/mob/living/silicon/robot/alarm_cancelled(src, class, area/A, obj/origin, cleared)
|
|
if(cleared)
|
|
if(!(class in alarms_listend_for))
|
|
return
|
|
if(origin.z != z)
|
|
return
|
|
queueAlarm("--- [class] alarm in [A.name] has been cleared.", class, 0)
|
|
|
|
/mob/living/silicon/robot/ex_act(severity)
|
|
switch(severity)
|
|
if(1.0)
|
|
gib()
|
|
return
|
|
if(2.0)
|
|
if(stat != 2)
|
|
adjustBruteLoss(60)
|
|
adjustFireLoss(60)
|
|
if(3.0)
|
|
if(stat != 2)
|
|
adjustBruteLoss(30)
|
|
return
|
|
|
|
|
|
/mob/living/silicon/robot/bullet_act(obj/item/projectile/Proj)
|
|
..(Proj)
|
|
if(prob(75) && Proj.damage > 0) spark_system.start()
|
|
return 2
|
|
|
|
|
|
/mob/living/silicon/robot/attackby(obj/item/W, mob/user, params)
|
|
// Check if the user is trying to insert another component like a radio, actuator, armor etc.
|
|
if(istype(W, /obj/item/robot_parts/robot_component) && opened)
|
|
for(var/V in components)
|
|
var/datum/robot_component/C = components[V]
|
|
if(!C.is_missing() || !istype(W, C.external_type))
|
|
continue
|
|
if(!user.drop_item())
|
|
to_chat(user, "<span class='warning'>[W] seems to be stuck in your hand!</span>")
|
|
return
|
|
var/obj/item/robot_parts/robot_component/WC = W
|
|
C.brute_damage = WC.brute
|
|
C.electronics_damage = WC.burn
|
|
C.install(WC)
|
|
to_chat(usr, "<span class='notice'>You install [W].</span>")
|
|
return
|
|
|
|
if(istype(W, /obj/item/stack/cable_coil) && user.a_intent == INTENT_HELP && (wiresexposed || isdrone(src)))
|
|
user.changeNext_move(CLICK_CD_MELEE)
|
|
if(!getFireLoss())
|
|
to_chat(user, "<span class='notice'>Nothing to fix!</span>")
|
|
return
|
|
else if(!getFireLoss(TRUE))
|
|
to_chat(user, "<span class='warning'>The damaged components are beyond saving!</span>")
|
|
return
|
|
var/obj/item/stack/cable_coil/coil = W
|
|
adjustFireLoss(-30)
|
|
updatehealth()
|
|
add_fingerprint(user)
|
|
coil.use(1)
|
|
user.visible_message("<span class='alert'>\The [user] fixes some of the burnt wires on \the [src] with \the [coil].</span>")
|
|
|
|
else if(istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside
|
|
var/datum/robot_component/cell/C = components["power cell"]
|
|
if(wiresexposed)
|
|
to_chat(user, "Close the panel first.")
|
|
else if(cell)
|
|
to_chat(user, "There is a power cell already installed.")
|
|
else
|
|
user.drop_item()
|
|
to_chat(user, "You insert the power cell.")
|
|
C.install(W)
|
|
|
|
var/been_hijacked = FALSE
|
|
for(var/mob/living/simple_animal/demon/pulse_demon/demon in cell)
|
|
if(!been_hijacked)
|
|
demon.do_hijack_robot(src)
|
|
been_hijacked = TRUE
|
|
else
|
|
demon.exit_to_turf()
|
|
if(been_hijacked)
|
|
cell.rigged = FALSE
|
|
|
|
module?.update_cells()
|
|
diag_hud_set_borgcell()
|
|
|
|
else if(istype(W, /obj/item/encryptionkey/) && opened)
|
|
if(radio)//sanityyyyyy
|
|
radio.attackby(W,user)//GTFO, you have your own procs
|
|
else
|
|
to_chat(user, "Unable to locate a radio.")
|
|
|
|
else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda)) // 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)
|
|
to_chat(user, "You must close the cover to swipe an ID card.")
|
|
else
|
|
if(allowed(W))
|
|
locked = !locked
|
|
to_chat(user, "You [ locked ? "lock" : "unlock"] [src]'s interface.")
|
|
to_chat(src, "<span class='notice'>[user] [ locked ? "locked" : "unlocked"] your interface.</span>")
|
|
update_icons()
|
|
else
|
|
to_chat(user, "<span class='warning'>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.action(user, src))
|
|
user.visible_message("<span class='notice'>[user] applied [U] to [src].</span>", "<span class='notice'>You apply [U] to [src].</span>")
|
|
|
|
|
|
else if(istype(W, /obj/item/mmi_radio_upgrade))
|
|
if(!opened)
|
|
to_chat(user, "<span class='warning'>You must access the borg's internals!</span>")
|
|
return
|
|
else if(!mmi)
|
|
to_chat(user, "<span class='warning'>This cyborg does not have an MMI to augment!</span>")
|
|
return
|
|
else if(mmi.radio)
|
|
to_chat(user, "<span class='warning'>A radio upgrade is already installed in the MMI!</span>")
|
|
return
|
|
else if(user.drop_item())
|
|
to_chat(user, "<span class='notice'>You apply the upgrade to [src].</span>")
|
|
to_chat(src, "<span class='notice'>MMI radio capability installed.</span>")
|
|
mmi.install_radio()
|
|
qdel(W)
|
|
else
|
|
return ..()
|
|
|
|
/mob/living/silicon/robot/wirecutter_act(mob/user, obj/item/I)
|
|
if(!opened)
|
|
return
|
|
. = TRUE
|
|
if(!I.use_tool(src, user, 0, volume = 0))
|
|
return
|
|
if(wiresexposed)
|
|
wires.Interact(user)
|
|
|
|
/mob/living/silicon/robot/multitool_act(mob/user, obj/item/I)
|
|
if(!opened)
|
|
return
|
|
. = TRUE
|
|
if(!I.use_tool(src, user, 0, volume = 0))
|
|
return
|
|
if(wiresexposed)
|
|
wires.Interact(user)
|
|
|
|
/mob/living/silicon/robot/screwdriver_act(mob/user, obj/item/I)
|
|
if(!opened)
|
|
return
|
|
. = TRUE
|
|
if(!I.use_tool(src, user, 0, volume = 0))
|
|
return
|
|
if(!cell) // haxing
|
|
wiresexposed = !wiresexposed
|
|
to_chat(user, "<span class='notice'>The wires have been [wiresexposed ? "exposed" : "unexposed"]</span>")
|
|
update_icons()
|
|
I.play_tool_sound(user, I.tool_volume)
|
|
else //radio check
|
|
if(radio)
|
|
radio.screwdriver_act(user, I)//Push it to the radio to let it handle everything
|
|
else
|
|
to_chat(user, "Unable to locate a radio.")
|
|
update_icons()
|
|
|
|
/mob/living/silicon/robot/crowbar_act(mob/user, obj/item/I)
|
|
if(user.a_intent != INTENT_HELP)
|
|
return
|
|
. = TRUE
|
|
if(!I.tool_use_check(user, 0))
|
|
return
|
|
if(!opened)
|
|
if(locked)
|
|
to_chat(user, "The cover is locked and cannot be opened.")
|
|
return
|
|
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
|
|
return
|
|
to_chat(user, "You open the cover.")
|
|
opened = TRUE
|
|
update_icons()
|
|
return
|
|
else if(cell)
|
|
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
|
|
return
|
|
to_chat(user, "You close the cover.")
|
|
opened = FALSE
|
|
update_icons()
|
|
return
|
|
else if(wiresexposed && wires.is_all_cut())
|
|
//Cell is out, wires are exposed, remove MMI, produce damaged chassis, baleet original mob.
|
|
if(!mmi)
|
|
to_chat(user, "[src] has no brain to remove.")
|
|
return
|
|
to_chat(user, "You jam the crowbar into the robot and begin levering the securing bolts...")
|
|
if(I.use_tool(src, user, 30, volume = I.tool_volume))
|
|
user.visible_message("[user] deconstructs [src]!", "<span class='notice'>You unfasten the securing bolts, and [src] falls to pieces!</span>")
|
|
spill_upgrades()
|
|
deconstruct()
|
|
return
|
|
// Okay we're not removing the cell or an MMI, but maybe something else?
|
|
var/list/removable_components = list()
|
|
for(var/V in components)
|
|
if(V == "power cell")
|
|
continue
|
|
var/datum/robot_component/C = components[V]
|
|
if(!C.is_missing())
|
|
removable_components += V
|
|
if(module)
|
|
removable_components += module.custom_removals
|
|
var/remove = tgui_input_list(user, "Which component do you want to pry out?", "Remove Component", removable_components)
|
|
if(!remove || !Adjacent(user) || !opened)
|
|
return
|
|
|
|
if(module && (remove in module.custom_removals))
|
|
module.handle_custom_removal(remove, user, I)
|
|
return
|
|
|
|
var/datum/robot_component/C = components[remove]
|
|
if(C.is_missing()) // Somebody else removed it during the input
|
|
return
|
|
|
|
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
|
|
return
|
|
var/obj/item/robot_parts/robot_component/thing = C.wrapped
|
|
to_chat(user, "You remove \the [thing].")
|
|
if(istype(thing))
|
|
thing.brute = C.brute_damage
|
|
thing.burn = C.electronics_damage
|
|
|
|
C.uninstall()
|
|
thing.forceMove(loc)
|
|
|
|
|
|
|
|
|
|
|
|
/mob/living/silicon/robot/attacked_by(obj/item/I, mob/living/user, def_zone)
|
|
if(I.force && I.damtype != STAMINA && stat != DEAD) //only sparks if real damage is dealt.
|
|
spark_system.start()
|
|
..()
|
|
|
|
// Here so admins can unemag borgs.
|
|
/mob/living/silicon/robot/unemag()
|
|
SetEmagged(FALSE)
|
|
if(!module)
|
|
return
|
|
uneq_all()
|
|
module.module_type = initial(module.module_type)
|
|
update_module_icon()
|
|
module.unemag()
|
|
clear_supplied_laws()
|
|
laws = new /datum/ai_laws/crewsimov
|
|
|
|
/mob/living/silicon/robot/emag_act(mob/user)
|
|
if(!ishuman(user) && !issilicon(user))
|
|
return
|
|
var/mob/living/M = user
|
|
if(!opened)//Cover is closed
|
|
if(!is_emaggable)
|
|
to_chat(user, "The emag sparks, and flashes red. This mechanism does not appear to be emaggable.")
|
|
else if(locked)
|
|
to_chat(user, "You emag the cover lock.")
|
|
locked = FALSE
|
|
return TRUE
|
|
else
|
|
to_chat(user, "The cover is already unlocked.")
|
|
return
|
|
|
|
if(opened)//Cover is open
|
|
if(emagged)
|
|
return //Prevents the X has hit Y with Z message also you cant emag them twice
|
|
if(wiresexposed)
|
|
to_chat(user, "You must close the panel first")
|
|
return
|
|
else
|
|
sleep(6)
|
|
SetEmagged(TRUE)
|
|
SetLockdown(1) //Borgs were getting into trouble because they would attack the emagger before the new laws were shown
|
|
if(hud_used)
|
|
hud_used.update_robot_modules_display() //Shows/hides the emag item if the inventory screen is already open.
|
|
disconnect_from_ai()
|
|
to_chat(user, "You emag [src]'s interface.")
|
|
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")
|
|
GLOB.lawchanges.Add("[time] <B>:</B> [M.name]([M.key]) emagged [name]([key])")
|
|
set_zeroth_law("Only [M.real_name] and people [M.p_they()] designate[M.p_s()] as being such are Syndicate Agents.")
|
|
playsound_local(src, 'sound/voice/aisyndihack.ogg', 75, FALSE)
|
|
to_chat(src, "<span class='warning'>ALERT: Foreign software detected.</span>")
|
|
sleep(5)
|
|
to_chat(src, "<span class='warning'>Initiating diagnostics...</span>")
|
|
sleep(20)
|
|
to_chat(src, "<span class='warning'>SynBorg v1.7 loaded.</span>")
|
|
sleep(5)
|
|
to_chat(src, "<span class='warning'>LAW SYNCHRONISATION ERROR</span>")
|
|
sleep(5)
|
|
to_chat(src, "<span class='warning'>Would you like to send a report to NanoTraSoft? Y/N</span>")
|
|
sleep(10)
|
|
to_chat(src, "<span class='warning'>> N</span>")
|
|
sleep(25)
|
|
to_chat(src, "<span class='warning'>ERRORERRORERROR</span>")
|
|
to_chat(src, "<b>Obey these laws:</b>")
|
|
laws.show_laws(src)
|
|
if(!mmi.syndiemmi)
|
|
to_chat(src, "<span class='boldwarning'>ALERT: [M.real_name] is your new master. Obey your new laws and [M.p_their()] commands.</span>")
|
|
else if(mmi.syndiemmi && mmi.master_uid)
|
|
to_chat(src, "<span class='boldwarning'>Your allegiance has not been compromised. Keep serving your current master.</span>")
|
|
else
|
|
to_chat(src, "<span class='boldwarning'>Your allegiance has not been compromised. Keep serving all Syndicate agents to the best of your abilities.</span>")
|
|
|
|
SetLockdown(0)
|
|
if(module)
|
|
module.emag_act(user)
|
|
module.module_type = "Malf" // For the cool factor
|
|
update_module_icon()
|
|
module.rebuild_modules() // This will add the emagged items to the borgs inventory.
|
|
update_icons()
|
|
return TRUE
|
|
|
|
/mob/living/silicon/robot/verb/toggle_own_cover()
|
|
set category = "Robot Commands"
|
|
set name = "Toggle Cover"
|
|
set desc = "Toggles the lock on your cover."
|
|
|
|
if(can_lock_cover)
|
|
if(tgui_alert(usr, "Are you sure?", locked ? "Unlock Cover" : "Lock Cover", list("Yes", "No")) == "Yes")
|
|
locked = !locked
|
|
update_icons()
|
|
to_chat(usr, "<span class='notice'>You [locked ? "lock" : "unlock"] your cover.</span>")
|
|
return
|
|
if(!locked)
|
|
to_chat(usr, "<span class='warning'>You cannot lock your cover yourself. Find a roboticist.</span>")
|
|
return
|
|
if(tgui_alert(usr, "You cannnot lock your own cover again. Are you sure?\nYou will need a roboticist to re-lock you.", "Unlock Own Cover", list("Yes", "No")) == "Yes")
|
|
locked = !locked
|
|
update_icons()
|
|
to_chat(usr, "<span class='notice'>You unlock your cover.</span>")
|
|
|
|
/mob/living/silicon/robot/attack_ghost(mob/user)
|
|
if(wiresexposed)
|
|
wires.Interact(user)
|
|
else
|
|
..() //this calls the /mob/living/attack_ghost proc for the ghost health/machine analyzer
|
|
|
|
/mob/living/silicon/robot/proc/allowed(obj/item/I)
|
|
var/obj/dummy = new /obj(null) // Create a dummy object to check access on as to avoid having to snowflake check_access on every mob
|
|
dummy.req_access = req_access
|
|
dummy.req_one_access = req_one_access
|
|
|
|
if(dummy.check_access(I))
|
|
qdel(dummy)
|
|
return 1
|
|
|
|
qdel(dummy)
|
|
return 0
|
|
|
|
/mob/living/silicon/robot/update_icons()
|
|
overlays.Cut()
|
|
|
|
if(stat != DEAD && !(IsParalyzed() || IsStunned() || IsWeakened() || low_power_mode)) //Not dead, not stunned.
|
|
if(custom_panel in custom_eye_names)
|
|
overlays += "eyes-[custom_panel]"
|
|
else
|
|
overlays += "eyes-[icon_state]"
|
|
else
|
|
overlays -= "eyes"
|
|
if(opened)
|
|
var/panelprefix = "ov"
|
|
if(custom_sprite) //Custom borgs also have custom panels, heh
|
|
panelprefix = "[ckey]"
|
|
if(custom_panel in custom_panel_names) //For default borgs with different panels
|
|
panelprefix = custom_panel
|
|
if(wiresexposed)
|
|
overlays += "[panelprefix]-openpanel +w"
|
|
else if(cell)
|
|
overlays += "[panelprefix]-openpanel +c"
|
|
else
|
|
overlays += "[panelprefix]-openpanel -c"
|
|
borg_icons()
|
|
robot_module_hat_offset(icon_state)
|
|
update_hat_icons()
|
|
update_fire()
|
|
|
|
/mob/living/silicon/robot/proc/borg_icons() // Exists so that robot/destroyer can override it
|
|
return
|
|
|
|
/mob/living/silicon/robot/Topic(href, href_list)
|
|
if(..())
|
|
return 1
|
|
|
|
if(usr != src)
|
|
return 1
|
|
|
|
if(href_list["mach_close"])
|
|
var/t1 = "window=[href_list["mach_close"]]"
|
|
unset_machine()
|
|
src << browse(null, t1)
|
|
return 1
|
|
|
|
if(href_list["mod"])
|
|
var/obj/item/O = locate(href_list["mod"])
|
|
if(istype(O) && (O.loc == src))
|
|
O.attack_self(src)
|
|
return 1
|
|
|
|
if(href_list["act"])
|
|
var/obj/item/O = locate(href_list["act"])
|
|
if(!istype(O) || !(O.loc == src || O.loc == src.module))
|
|
return 1
|
|
|
|
activate_module(O)
|
|
|
|
//Show alerts window if user clicked on "Show alerts" in chat
|
|
if(href_list["showalerts"])
|
|
robot_alerts()
|
|
return TRUE
|
|
|
|
if(href_list["deact"])
|
|
var/obj/item/O = locate(href_list["deact"])
|
|
if(activated(O))
|
|
if(module_state_1 == O)
|
|
module_state_1 = null
|
|
contents -= O
|
|
else if(module_state_2 == O)
|
|
module_state_2 = null
|
|
contents -= O
|
|
else if(module_state_3 == O)
|
|
module_state_3 = null
|
|
contents -= O
|
|
else
|
|
to_chat(src, "Module isn't activated.")
|
|
else
|
|
to_chat(src, "Module isn't activated")
|
|
return 1
|
|
|
|
return 1
|
|
|
|
/mob/living/silicon/robot/proc/radio_menu()
|
|
radio.interact(src)
|
|
|
|
/mob/living/silicon/robot/proc/control_headlamp()
|
|
if(stat || lamp_recharging || low_power_mode)
|
|
to_chat(src, "<span class='danger'>This function is currently offline.</span>")
|
|
return
|
|
if(is_ventcrawling(src))
|
|
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(turn_off = 0, cooldown = 100, show_warning = TRUE)
|
|
set_light(0)
|
|
|
|
if(lamp_intensity && (turn_off || stat || low_power_mode))
|
|
if(show_warning)
|
|
to_chat(src, "<span class='danger'>Your headlamp has been deactivated.</span>")
|
|
lamp_intensity = 0
|
|
lamp_recharging = TRUE
|
|
spawn(cooldown) //10 seconds by default, if the source of the deactivation does not keep stat that long.
|
|
lamp_recharging = FALSE
|
|
else
|
|
set_light(light_range + 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 = FALSE
|
|
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.update_icon(UPDATE_OVERLAYS)
|
|
else
|
|
new /obj/item/robot_parts/robot_suit(T)
|
|
new /obj/item/robot_parts/l_leg(T)
|
|
new /obj/item/robot_parts/r_leg(T)
|
|
new /obj/item/stack/cable_coil(T, 1)
|
|
new /obj/item/robot_parts/chest(T)
|
|
new /obj/item/robot_parts/l_arm(T)
|
|
new /obj/item/robot_parts/r_arm(T)
|
|
new /obj/item/robot_parts/head(T)
|
|
var/b
|
|
for(b=0, b!=2, b++)
|
|
var/obj/item/flash/F = new /obj/item/flash(T)
|
|
F.burn_out()
|
|
if(cell) //Sanity check.
|
|
cell.forceMove(T)
|
|
cell = null
|
|
drop_hat()
|
|
qdel(src)
|
|
|
|
#define CAMERA_UPDATE_COOLDOWN 2.5 SECONDS
|
|
|
|
/mob/living/silicon/robot/Moved(atom/OldLoc, Dir, Forced)
|
|
. = ..()
|
|
if(camera && last_camera_update + CAMERA_UPDATE_COOLDOWN < world.time)
|
|
last_camera_update = world.time
|
|
GLOB.cameranet.updatePortableCamera(camera, OldLoc)
|
|
SEND_SIGNAL(camera, COMSIG_CAMERA_MOVED, OldLoc)
|
|
|
|
#undef CAMERA_UPDATE_COOLDOWN
|
|
|
|
/mob/living/silicon/robot/proc/self_destruct()
|
|
if(emagged)
|
|
explosion(src.loc,1,2,4,flame_range = 2)
|
|
else
|
|
explosion(src.loc,-1,0,2)
|
|
gib()
|
|
return
|
|
|
|
/mob/living/silicon/robot/proc/UnlinkSelf()
|
|
disconnect_from_ai()
|
|
lawupdate = FALSE
|
|
lockcharge = 0
|
|
REMOVE_TRAITS_IN(src, LOCKDOWN_TRAIT)
|
|
scrambledcodes = TRUE
|
|
//Disconnect it's camera so it's not so easily tracked.
|
|
QDEL_NULL(camera)
|
|
// 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 but 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.")
|
|
remove_verb(src, /mob/living/silicon/robot/proc/ResetSecurityCodes)
|
|
|
|
/mob/living/silicon/robot/mode()
|
|
set name = "Activate Held Object"
|
|
set category = "IC"
|
|
|
|
var/obj/item/W = get_active_hand()
|
|
if(W)
|
|
W.attack_self(src)
|
|
|
|
/mob/living/silicon/robot/proc/SetLockdown(state = TRUE)
|
|
// They stay locked down if their wire is cut.
|
|
if(wires.is_cut(WIRE_BORG_LOCKED))
|
|
state = TRUE
|
|
if(state)
|
|
throw_alert("locked", /atom/movable/screen/alert/locked)
|
|
else
|
|
clear_alert("locked")
|
|
lockcharge = state
|
|
if(state) // turn them off
|
|
ADD_TRAIT(src, TRAIT_IMMOBILIZED, LOCKDOWN_TRAIT)
|
|
ADD_TRAIT(src, TRAIT_UI_BLOCKED, LOCKDOWN_TRAIT)
|
|
ADD_TRAIT(src, TRAIT_HANDS_BLOCKED, LOCKDOWN_TRAIT)
|
|
else
|
|
REMOVE_TRAITS_IN(src, LOCKDOWN_TRAIT)
|
|
|
|
/mob/living/silicon/robot/proc/notify_ai(notifytype, oldname, newname)
|
|
if(!connected_ai)
|
|
return
|
|
switch(notifytype)
|
|
if(1) //New Cyborg
|
|
to_chat(connected_ai, "<br><br><span class='notice'>NOTICE - New cyborg connection detected: <a href='byond://?src=[connected_ai.UID()];track2=\ref[connected_ai];track=\ref[src]'>[name]</a></span><br>")
|
|
if(2) //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(3) //New Name
|
|
to_chat(connected_ai, "<br><br><span class='notice'>NOTICE - Cyborg reclassification detected: [oldname] is now designated as [newname].</span><br>")
|
|
|
|
/mob/living/silicon/robot/proc/disconnect_from_ai()
|
|
if(connected_ai)
|
|
sync() // One last sync attempt
|
|
set_connected_ai(null)
|
|
|
|
/mob/living/silicon/robot/proc/connect_to_ai(mob/living/silicon/ai/AI)
|
|
if(AI && AI != connected_ai)
|
|
disconnect_from_ai()
|
|
set_connected_ai(AI)
|
|
notify_ai(1)
|
|
if(module)
|
|
module.rebuild_modules() //This way, if a borg gets linked to a malf AI that has upgrades, they get their upgrades.
|
|
sync()
|
|
|
|
/mob/living/silicon/robot/adjustOxyLoss(amount)
|
|
if(suiciding)
|
|
return ..()
|
|
else
|
|
return STATUS_UPDATE_NONE
|
|
|
|
/mob/living/silicon/robot/regenerate_icons()
|
|
..()
|
|
update_module_icon()
|
|
|
|
/mob/living/silicon/robot/emp_act(severity)
|
|
if(emp_protection)
|
|
return
|
|
..()
|
|
adjustStaminaLoss((30 / severity)) //They also get flashed for an additional 30
|
|
switch(severity)
|
|
if(EMP_HEAVY)
|
|
disable_random_component(2, 20 SECONDS)
|
|
if(EMP_LIGHT)
|
|
disable_random_component(1, 10 SECONDS)
|
|
|
|
/mob/living/silicon/robot/deathsquad
|
|
base_icon = "nano_bloodhound"
|
|
icon_state = "nano_bloodhound"
|
|
designation = "SpecOps"
|
|
lawupdate = FALSE
|
|
scrambledcodes = TRUE
|
|
has_camera = FALSE
|
|
req_one_access = list(ACCESS_CENT_SPECOPS)
|
|
ionpulse = TRUE
|
|
pdahide = TRUE
|
|
eye_protection = TRUE // Immunity to flashes and the visual part of flashbangs
|
|
ear_protection = TRUE // Immunity to the audio part of flashbangs
|
|
damage_protection = 10 // Reduce all incoming damage by this number
|
|
allow_rename = FALSE
|
|
modtype = "Commando"
|
|
faction = list("nanotrasen")
|
|
is_emaggable = FALSE
|
|
can_lock_cover = TRUE
|
|
default_cell_type = /obj/item/stock_parts/cell/bluespace
|
|
has_advanced_reagent_vision = TRUE
|
|
|
|
/mob/living/silicon/robot/deathsquad/init(alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
|
|
laws = new /datum/ai_laws/deathsquad
|
|
module = new /obj/item/robot_module/deathsquad(src)
|
|
module.add_languages(src)
|
|
module.add_subsystems_and_actions(src)
|
|
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
|
|
radio = new /obj/item/radio/borg/deathsquad(src)
|
|
radio.recalculateChannels()
|
|
playsound(get_turf(src), 'sound/mecha/nominalnano.ogg', 75, FALSE)
|
|
|
|
/mob/living/silicon/robot/deathsquad/bullet_act(obj/item/projectile/P)
|
|
if(istype(P) && P.is_reflectable(REFLECTABILITY_ENERGY) && P.starting)
|
|
visible_message("<span class='danger'>[P] gets reflected by [src]!</span>", "<span class='userdanger'>[P] gets reflected by [src]!</span>")
|
|
P.reflect_back(src)
|
|
return -1
|
|
return ..(P)
|
|
|
|
|
|
/mob/living/silicon/robot/ert
|
|
designation = "ERT"
|
|
lawupdate = FALSE
|
|
scrambledcodes = TRUE
|
|
req_one_access = list(ACCESS_CENT_SPECOPS)
|
|
ionpulse = TRUE
|
|
force_modules = list("Engineering", "Medical")
|
|
static_radio_channels = 1
|
|
allow_rename = FALSE
|
|
weapons_unlock = TRUE
|
|
can_lock_cover = TRUE
|
|
default_cell_type = /obj/item/stock_parts/cell/super
|
|
var/eprefix = "Amber"
|
|
has_advanced_reagent_vision = TRUE
|
|
|
|
|
|
/mob/living/silicon/robot/ert/init(alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
|
|
laws = new /datum/ai_laws/ert_override
|
|
radio = new /obj/item/radio/borg/ert(src)
|
|
radio.recalculateChannels()
|
|
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
|
|
|
|
/mob/living/silicon/robot/ert/New(loc)
|
|
..(loc)
|
|
var/rnum = rand(1,1000)
|
|
var/borgname = "[eprefix] ERT [rnum]"
|
|
name = borgname
|
|
custom_name = borgname
|
|
real_name = name
|
|
mind = new
|
|
mind.current = src
|
|
mind.set_original_mob(src)
|
|
mind.assigned_role = SPECIAL_ROLE_ERT
|
|
mind.special_role = SPECIAL_ROLE_ERT
|
|
if(!(mind in SSticker.minds))
|
|
SSticker.minds += mind
|
|
SSticker.mode.ert += mind
|
|
|
|
|
|
/mob/living/silicon/robot/ert/red
|
|
eprefix = "Red"
|
|
force_modules = list("Security", "Engineering", "Medical")
|
|
default_cell_type = /obj/item/stock_parts/cell/hyper
|
|
|
|
/mob/living/silicon/robot/ert/gamma
|
|
default_cell_type = /obj/item/stock_parts/cell/bluespace
|
|
force_modules = list("Combat", "Engineering", "Medical")
|
|
damage_protection = 5 // Reduce all incoming damage by this number
|
|
eprefix = "Gamma"
|
|
|
|
|
|
/mob/living/silicon/robot/destroyer
|
|
// admin-only borg, the seraph / special ops officer of borgs
|
|
base_icon = "droidcombat"
|
|
icon_state = "droidcombat"
|
|
modtype = "Destroyer"
|
|
designation = "Destroyer"
|
|
lawupdate = FALSE
|
|
scrambledcodes = TRUE
|
|
has_camera = FALSE
|
|
req_one_access = list(ACCESS_CENT_SPECOPS)
|
|
ionpulse = TRUE
|
|
pdahide = TRUE
|
|
eye_protection = TRUE // Immunity to flashes and the visual part of flashbangs
|
|
ear_protection = TRUE // Immunity to the audio part of flashbangs
|
|
emp_protection = TRUE // Immunity to EMP, due to heavy shielding
|
|
damage_protection = 20 // Reduce all incoming damage by this number. Very high in the case of /destroyer borgs, since it is an admin-only borg.
|
|
can_lock_cover = TRUE
|
|
default_cell_type = /obj/item/stock_parts/cell/bluespace
|
|
has_advanced_reagent_vision = TRUE
|
|
|
|
/mob/living/silicon/robot/destroyer/init(alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
|
|
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
|
|
additional_law_channels["Binary"] = ":b "
|
|
laws = new /datum/ai_laws/deathsquad
|
|
module = new /obj/item/robot_module/destroyer(src)
|
|
module.add_languages(src)
|
|
module.add_subsystems_and_actions(src)
|
|
status_flags &= ~CANPUSH
|
|
if(radio)
|
|
qdel(radio)
|
|
radio = new /obj/item/radio/borg/ert/specops(src)
|
|
radio.recalculateChannels()
|
|
playsound(get_turf(src), 'sound/mecha/nominalnano.ogg', 75, FALSE)
|
|
|
|
/mob/living/silicon/robot/destroyer/borg_icons()
|
|
if(base_icon == "")
|
|
base_icon = icon_state
|
|
if(module_active && istype(module_active,/obj/item/borg/destroyer/mobility))
|
|
icon_state = "[base_icon]-roll"
|
|
else
|
|
icon_state = base_icon
|
|
overlays += "[base_icon]-shield"
|
|
|
|
|
|
/mob/living/silicon/robot/extinguish_light(force = FALSE)
|
|
update_headlamp(1, 150)
|
|
|
|
/mob/living/silicon/robot/rejuvenate()
|
|
..()
|
|
var/brute = 1000
|
|
var/burn = 1000
|
|
var/list/datum/robot_component/borked_parts = get_damaged_components(TRUE, TRUE, TRUE, TRUE)
|
|
for(var/datum/robot_component/borked_part in borked_parts)
|
|
brute = borked_part.brute_damage
|
|
burn = borked_part.electronics_damage
|
|
borked_part.heal_damage(brute,burn)
|
|
borked_part.install(new borked_part.external_type)
|
|
|
|
/mob/living/silicon/robot/proc/check_sprite(spritename)
|
|
. = FALSE
|
|
|
|
var/static/all_borg_icon_states = icon_states('icons/mob/custom_synthetic/custom-synthetic.dmi')
|
|
if(spritename in all_borg_icon_states)
|
|
. = TRUE
|
|
|
|
/mob/living/silicon/robot/check_eye_prot()
|
|
return eye_protection
|
|
|
|
/mob/living/silicon/robot/check_ear_prot()
|
|
return ear_protection
|
|
|
|
/mob/living/silicon/robot/update_sight()
|
|
if(!client)
|
|
return
|
|
|
|
if(stat == DEAD)
|
|
grant_death_vision()
|
|
return
|
|
|
|
see_invisible = initial(see_invisible)
|
|
see_in_dark = initial(see_in_dark)
|
|
sight = initial(sight)
|
|
lighting_alpha = initial(lighting_alpha)
|
|
|
|
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_MOSTLY_VISIBLE
|
|
|
|
if(sight_mode & BORGXRAY)
|
|
sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
|
|
see_invisible = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
|
|
see_in_dark = 8
|
|
|
|
if(sight_mode & BORGTHERM)
|
|
sight |= SEE_MOBS
|
|
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
|
|
|
|
SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT)
|
|
sync_lighting_plane_alpha()
|
|
|
|
/// Used in `robot.dm` when the user presses "Q" by default.
|
|
/mob/living/silicon/robot/proc/on_drop_hotkey_press()
|
|
var/obj/item/gripper/G = get_active_hand()
|
|
if(istype(G) && G.gripped_item)
|
|
G.drop_gripped_item() // if the active module is a gripper, try to drop its held item.
|
|
else
|
|
uneq_active() // else unequip the module and put it back into the robot's inventory.
|
|
return
|
|
|
|
/mob/living/silicon/robot/proc/check_module_damage(makes_sound = TRUE)
|
|
if(modules_break)
|
|
if(health < 50) //Gradual break down of modules as more damage is sustained
|
|
if(uneq_module(module_state_3))
|
|
if(makes_sound)
|
|
audible_message("<span class='warning'>[src] sounds an alarm! \"SYSTEM ERROR: Module 3 OFFLINE.\"</span>")
|
|
playsound(loc, 'sound/machines/warning-buzzer.ogg', 50, TRUE)
|
|
to_chat(src, "<span class='userdanger'>SYSTEM ERROR: Module 3 OFFLINE.</span>")
|
|
|
|
if(health < 0)
|
|
if(uneq_module(module_state_2))
|
|
if(makes_sound)
|
|
audible_message("<span class='warning'>[src] sounds an alarm! \"SYSTEM ERROR: Module 2 OFFLINE.\"</span>")
|
|
playsound(loc, 'sound/machines/warning-buzzer.ogg', 60, TRUE)
|
|
to_chat(src, "<span class='userdanger'>SYSTEM ERROR: Module 2 OFFLINE.</span>")
|
|
|
|
if(health < -50)
|
|
if(uneq_module(module_state_1))
|
|
if(makes_sound)
|
|
audible_message("<span class='warning'>[src] sounds an alarm! \"CRITICAL ERROR: All modules OFFLINE.\"</span>")
|
|
playsound(loc, 'sound/machines/warning-buzzer.ogg', 75, TRUE)
|
|
to_chat(src, "<span class='userdanger'>CRITICAL ERROR: All modules OFFLINE.</span>")
|
|
|
|
/mob/living/silicon/robot/advanced_reagent_vision()
|
|
return has_advanced_reagent_vision
|
|
|
|
/mob/living/silicon/robot/verb/powerwarn()
|
|
set category = "Robot Commands"
|
|
set name = "Power Warning"
|
|
|
|
if(!is_component_functioning("power cell") || !cell || !cell.charge)
|
|
if(!start_audio_emote_cooldown(TRUE, 10 SECONDS))
|
|
to_chat(src, "<span class='warning'>The low-power capacitor for your speaker system is still recharging, please try again later.</span>")
|
|
return
|
|
visible_message("<span class='warning'>The power warning light on <span class='name'>[src]</span> flashes urgently.</span>",\
|
|
"<span class='warning'>You announce you are operating in low power mode.</span>")
|
|
playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0)
|
|
else
|
|
to_chat(src, "<span class='warning'>You can only use this emote when you're out of charge.</span>")
|
|
|
|
/mob/living/silicon/robot/can_instant_lockdown()
|
|
if(emagged || ("syndicate" in faction))
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/mob/living/silicon/robot/proc/set_connected_ai(new_ai)
|
|
if(connected_ai == new_ai)
|
|
return
|
|
. = connected_ai
|
|
connected_ai = new_ai
|
|
if(.)
|
|
var/mob/living/silicon/ai/old_ai = .
|
|
old_ai.connected_robots -= src
|
|
if(connected_ai)
|
|
connected_ai.connected_robots |= src
|
|
|
|
/mob/living/silicon/robot/proc/gain_external_power()
|
|
SIGNAL_HANDLER //COMSIG_ENTERED_BORGCHARGER
|
|
externally_powered = TRUE
|
|
|
|
/mob/living/silicon/robot/proc/lose_external_power()
|
|
SIGNAL_HANDLER //COMSIG_EXITED_BORGCHARGER
|
|
externally_powered = FALSE
|
|
|
|
/mob/living/silicon/robot/proc/has_power_source()
|
|
var/datum/robot_component/cell/cell = get_cell_component()
|
|
if(!cell)
|
|
return externally_powered
|
|
return cell.is_powered() || externally_powered
|
|
|
|
/mob/living/silicon/robot/can_be_flashed(intensity, override_blindness_check)
|
|
return !eye_protection
|
|
|
|
/mob/living/silicon/robot/can_remote_apc_interface(obj/machinery/power/apc/ourapc)
|
|
if(ourapc.hacked_by_ruin_AI || ourapc.aidisabled)
|
|
return FALSE
|
|
if(ourapc.malfai && !(src in ourapc.malfai.connected_robots))
|
|
return FALSE
|
|
return TRUE
|