mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-23 04:51:32 +01:00
Some light rigs don't even have boots to deploy, so they shouldn't get slowdown because of it. Decided just to make all light RIGs have no slowdown, as none of them had footstep sounds anyway. changes: - bugfix: "Fixes light rigs getting slowdown from having undeployed boots." - bugfix: "Fixes light rigs having no energy armour."
1348 lines
44 KiB
Plaintext
1348 lines
44 KiB
Plaintext
/*
|
|
* Defines the behavior of hardsuits/rigs/power armor.
|
|
*/
|
|
|
|
/obj/item/rig
|
|
name = "hardsuit control module"
|
|
desc = "A back-mounted hardsuit deployment and control mechanism."
|
|
icon = 'icons/obj/rig_modules.dmi'
|
|
contained_sprite = TRUE
|
|
slot_flags = SLOT_BACK
|
|
req_one_access = list()
|
|
req_access = list()
|
|
w_class = WEIGHT_CLASS_BULKY
|
|
|
|
// These values are passed on to all component pieces.
|
|
armor = list(
|
|
MELEE = ARMOR_MELEE_RESISTANT,
|
|
BULLET = ARMOR_BALLISTIC_MINOR,
|
|
LASER = ARMOR_LASER_SMALL,
|
|
ENERGY = ARMOR_ENERGY_MINOR,
|
|
BOMB = ARMOR_BOMB_PADDED,
|
|
BIO = ARMOR_BIO_SHIELDED,
|
|
RAD = ARMOR_RAD_MINOR
|
|
)
|
|
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE
|
|
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
|
|
max_pressure_protection = RIG_MAX_PRESSURE
|
|
min_pressure_protection = 0
|
|
|
|
siemens_coefficient = 0.35
|
|
permeability_coefficient = 0.1
|
|
unacidable = 1
|
|
slowdown = 0.5 // All rigs by default should have slowdown.
|
|
|
|
action_button_name = "Open Hardsuit Interface"
|
|
|
|
var/has_sealed_state = FALSE
|
|
var/has_hidden_jumpsuit = FALSE
|
|
|
|
/// Used for AI moving.
|
|
var/wearer_move_delay
|
|
var/ai_controlled_move_delay = 10
|
|
/// When did a mounted pAI or AI use a module? used to prevent admin msg spam
|
|
var/last_remote_message
|
|
|
|
/// Keeps track of what this rig should spawn with.
|
|
var/suit_type = "hardsuit"
|
|
var/list/initial_modules
|
|
var/chest_type = /obj/item/clothing/suit/space/rig
|
|
var/helm_type = /obj/item/clothing/head/helmet/space/rig
|
|
var/boot_type = /obj/item/clothing/shoes/magboots/rig
|
|
var/glove_type = /obj/item/clothing/gloves/rig
|
|
var/cell_type = /obj/item/cell/high
|
|
var/air_type = /obj/item/tank/oxygen
|
|
|
|
//Component/device holders.
|
|
/// Air tank, if any.
|
|
var/obj/item/tank/air_supply
|
|
/// Deployable boots, if any.
|
|
var/obj/item/clothing/shoes/boots = null
|
|
/// Deployable chestpiece, if any.
|
|
var/obj/item/clothing/suit/space/rig/chest
|
|
/// Deployable helmet, if any.
|
|
var/obj/item/clothing/head/helmet/space/rig/helmet = null
|
|
/// Deployable gauntlets, if any.
|
|
var/obj/item/clothing/gloves/rig/gloves = null
|
|
/// Power supply, if any.
|
|
var/obj/item/cell/cell
|
|
/// Primary system (used with middle-click)
|
|
var/obj/item/rig_module/selected_module = null
|
|
/// Kinda shitty to have a var for a module, but saves time.
|
|
var/obj/item/rig_module/vision/visor
|
|
/// As above.
|
|
var/obj/item/rig_module/voice/speech
|
|
/// The person currently wearing the rig.
|
|
var/mob/living/carbon/human/wearer
|
|
/// Holder for on-mob icon.
|
|
var/image/mob_icon
|
|
/// Power consumption/use bookkeeping.
|
|
var/list/installed_modules = list()
|
|
|
|
// Rig status vars.
|
|
/// Access panel status.
|
|
var/open = FALSE
|
|
/// Lock status.
|
|
var/locked = TRUE
|
|
/// To whom do we belong?
|
|
var/dnaLock
|
|
/// Are we crushing the occupant to death?
|
|
var/crushing = FALSE
|
|
var/subverted = 0
|
|
/// Interfaces get locked when the corresponding rig wire is pulsed.
|
|
var/interface_locked = FALSE
|
|
/// Whether or not the AI is currently overriding control of the suit.
|
|
var/control_overridden = FALSE
|
|
/// Whether or not the AI is capable of overriding control of the suit.
|
|
var/ai_override_enabled = FALSE
|
|
/// Access req status.
|
|
var/security_check_enabled = TRUE
|
|
var/malfunctioning = 0
|
|
var/malfunction_delay = 0
|
|
var/electrified = 0
|
|
/// Ensures that you can't queue up multiple piece toggle commands at once and bug anything out.
|
|
var/piece_being_deployed = FALSE
|
|
|
|
/// Rate at which the suit cell will passively use power when online.
|
|
var/cell_draw_rate = CHARGE_DRAIN_DEFAULT
|
|
|
|
var/seal_delay = SEAL_DELAY
|
|
/// Keeps track of seal status independantly of canremove.
|
|
var/sealing
|
|
/// Should we be applying suit maluses? Note that this is tristate: 0 = online; 1 = offline, modules still active; 2 = offline, inc. modules
|
|
/// Generally speaking, a suit is 0 when on, 1 when just turned off, and 2 after it has run process(), which deactivates all modules if offline == 1
|
|
var/offline = 0
|
|
/// If the suit is deployed and unpowered, it sets slowdown to this.
|
|
var/offline_slowdown = 1.5
|
|
var/vision_restriction = TINT_NONE
|
|
var/offline_vision_restriction = TINT_HEAVY
|
|
///If set, will adjust the ITEM_FLAG_AIRTIGHT flag on components. Otherwise it should leave them untouched.
|
|
var/airtight = TRUE
|
|
|
|
var/emp_protection = 0
|
|
|
|
// Wiring! How exciting.
|
|
var/datum/wires/rig/wires
|
|
var/datum/effect_system/sparks/spark_system
|
|
|
|
/// All rigs by default should have access to general
|
|
var/allowed_module_types = MODULE_GENERAL
|
|
var/list/species_restricted = list(BODYTYPE_HUMAN,BODYTYPE_TAJARA,BODYTYPE_UNATHI, BODYTYPE_SKRELL, BODYTYPE_IPC, BODYTYPE_IPC_BISHOP, BODYTYPE_IPC_ZENGHU)
|
|
///If TRUE, this rig will protect against anomalies. Currently only used for AMI hardsuit.
|
|
var/anomaly_protection = FALSE
|
|
|
|
var/ui_configure_ref
|
|
|
|
/obj/item/rig/get_examine_text(mob/user, distance, is_adjacent, infix, suffix)
|
|
. = ..()
|
|
if(wearer)
|
|
for(var/obj/item/piece in list(helmet,gloves,chest,boots))
|
|
if(!piece || piece.loc != wearer)
|
|
continue
|
|
. += "[icon2html(piece, usr)] \The [piece] [piece.gender == PLURAL ? "are" : "is"] deployed."
|
|
|
|
if(src.loc == usr)
|
|
. += "The maintenance panel is [open ? "open" : "closed"]."
|
|
. += "Hardsuit systems are [offline ? SPAN_WARNING("offline") : SPAN_GOOD("<span class='good'>online")]."
|
|
|
|
/obj/item/rig/Initialize()
|
|
. = ..()
|
|
|
|
item_state = icon_state
|
|
wires = new(src)
|
|
|
|
if(!LAZYLEN(req_access) && !LAZYLEN(req_one_access))
|
|
locked = FALSE
|
|
|
|
spark_system = bind_spark(src, 5)
|
|
|
|
last_remote_message = world.time
|
|
|
|
if(initial_modules && initial_modules.len)
|
|
for(var/path in initial_modules)
|
|
var/obj/item/rig_module/module = new path(src)
|
|
installed_modules += module
|
|
module.installed(src)
|
|
|
|
// Create and initialize our various segments.
|
|
if(cell_type)
|
|
cell = new cell_type(src)
|
|
if(air_type)
|
|
air_supply = new air_type(src)
|
|
if(glove_type)
|
|
gloves = new glove_type(src)
|
|
verbs |= /obj/item/rig/proc/toggle_gauntlets
|
|
if(helm_type)
|
|
helmet = new helm_type(src)
|
|
verbs |= /obj/item/rig/proc/toggle_helmet
|
|
if(boot_type)
|
|
boots = new boot_type(src)
|
|
verbs |= /obj/item/rig/proc/toggle_boots
|
|
if(chest_type)
|
|
chest = new chest_type(src)
|
|
if(allowed)
|
|
chest.allowed = allowed
|
|
verbs |= /obj/item/rig/proc/toggle_chest
|
|
|
|
for(var/obj/item/clothing/piece in list(gloves,helmet,boots,chest))
|
|
if(!istype(piece))
|
|
continue
|
|
piece.canremove = FALSE
|
|
piece.name = "[suit_type] [initial(piece.name)]"
|
|
piece.desc = "It seems to be part of a [src.name]."
|
|
piece.icon = icon
|
|
piece.icon_state = "[initial(icon_state)]_[piece.clothing_class()]"
|
|
piece.item_state = "[initial(icon_state)]"
|
|
piece.contained_sprite = TRUE
|
|
if(length(icon_supported_species_tags))
|
|
set_piece_adaptation(piece)
|
|
piece.min_cold_protection_temperature = min_cold_protection_temperature
|
|
piece.max_heat_protection_temperature = max_heat_protection_temperature
|
|
if(piece.siemens_coefficient > siemens_coefficient) //So that insulated gloves keep their insulation.
|
|
piece.siemens_coefficient = siemens_coefficient
|
|
piece.permeability_coefficient = permeability_coefficient
|
|
piece.unacidable = unacidable
|
|
piece.species_restricted = species_restricted
|
|
if(islist(armor))
|
|
var/datum/component/armor/armor_component = piece.GetComponent(/datum/component/armor)
|
|
if(istype(armor_component))
|
|
qdel(armor_component)
|
|
piece.AddComponent(/datum/component/armor, armor, ARMOR_TYPE_STANDARD|ARMOR_TYPE_RIG)
|
|
|
|
if(chest.flags_inv & HIDEJUMPSUIT)
|
|
has_hidden_jumpsuit = TRUE
|
|
chest.flags_inv &= ~HIDEJUMPSUIT
|
|
|
|
set_vision(!offline)
|
|
update_icon(1)
|
|
|
|
/// Sets the icon adaptation and species supported tags equal to parent, can be overriden for custom functionality
|
|
/obj/item/rig/proc/set_piece_adaptation(var/obj/item/clothing/piece)
|
|
piece.icon_auto_adapt = TRUE
|
|
piece.icon_supported_species_tags = icon_supported_species_tags
|
|
|
|
/obj/item/rig/Destroy()
|
|
STOP_PROCESSING(SSprocessing, src)
|
|
QDEL_NULL(air_supply)
|
|
QDEL_NULL(boots)
|
|
QDEL_NULL(chest)
|
|
QDEL_NULL(helmet)
|
|
QDEL_NULL(gloves)
|
|
QDEL_NULL(cell)
|
|
selected_module = null
|
|
visor = null
|
|
speech = null
|
|
wearer?.wearing_rig = null
|
|
wearer = null
|
|
mob_icon = null
|
|
QDEL_LIST(installed_modules)
|
|
QDEL_NULL(wires)
|
|
QDEL_NULL(spark_system)
|
|
return ..()
|
|
|
|
/obj/item/rig/proc/set_vision(var/active)
|
|
if(helmet)
|
|
helmet.tint = (active? vision_restriction : offline_vision_restriction)
|
|
|
|
/**
|
|
* Updates the hardsuit's slowdown if it changes.
|
|
* This gives a heavy slowdown penalty if the suit is deployed but the boots are not.
|
|
* Otherwise, it is an easy exploit to make any hardsuit stealthy by wearing everything EXCEPT the boots.
|
|
* Now you can creep in your hardsuit, but you have to go slowly to do it.
|
|
*/
|
|
/obj/item/rig/proc/update_slowdown()
|
|
var/new_slowdown = initial(slowdown)
|
|
|
|
if(offline)
|
|
new_slowdown = offline_slowdown
|
|
else if(chest && wearer?.wear_suit == chest && (!boots || wearer.shoes != boots)) //If the chest is deployed, but the boots are not.
|
|
new_slowdown = offline_slowdown + 2 //The boots aren't taking the weight of the suit, so the wearer isn't benefiting from the hardsuit's powered servos.
|
|
|
|
if(slowdown != new_slowdown)
|
|
slowdown = new_slowdown
|
|
wearer?.update_equipment_speed_mods() //This should only proc if worn, but just in case we check.
|
|
if(slowdown == offline_slowdown + 2)
|
|
to_chat(wearer, SPAN_WARNING("Without the boots deployed, the hardsuit's servos aren't supporting the weight of the suit."))
|
|
|
|
/obj/item/rig/proc/suit_is_deployed()
|
|
if(!istype(wearer) || src.loc != wearer || wearer.back != src)
|
|
return 0
|
|
if(helm_type && !(helmet && wearer.head == helmet))
|
|
return 0
|
|
if(glove_type && !(gloves && wearer.gloves == gloves))
|
|
return 0
|
|
if(boot_type && !(boots && wearer.shoes == boots))
|
|
return 0
|
|
if(chest_type && !(chest && wearer.wear_suit == chest))
|
|
return 0
|
|
return 1
|
|
|
|
/// Not currently used.
|
|
/obj/item/rig/proc/reset()
|
|
offline = 2
|
|
canremove = 1
|
|
crushing = FALSE
|
|
for(var/obj/item/piece in list(helmet,boots,gloves,chest))
|
|
if(!piece) continue
|
|
piece.icon_state = "[initial(icon_state)]"
|
|
if(airtight)
|
|
piece.max_pressure_protection = initial(piece.max_pressure_protection)
|
|
piece.min_pressure_protection = initial(piece.min_pressure_protection)
|
|
piece.item_flags &= ~ITEM_FLAG_AIRTIGHT
|
|
update_icon(1)
|
|
|
|
/obj/item/rig/proc/toggle_seals(var/mob/initiator,var/instant)
|
|
|
|
if(sealing) return
|
|
|
|
// Seal toggling can be initiated by the suit AI, too
|
|
if(!wearer)
|
|
to_chat(initiator, SPAN_DANGER("Cannot toggle suit: The suit is currently not being worn by anyone."))
|
|
return 0
|
|
|
|
if(!check_power_cost(wearer))
|
|
return 0
|
|
|
|
deploy(wearer,instant)
|
|
|
|
var/seal_target = !canremove
|
|
var/failed_to_seal
|
|
|
|
canremove = 0 // No removing the suit while unsealing.
|
|
sealing = 1
|
|
|
|
if(!seal_target && !suit_is_deployed())
|
|
wearer.visible_message(
|
|
SPAN_DANGER("[wearer]'s suit flashes an error light."),
|
|
SPAN_DANGER("Your suit flashes an error light. It can't function properly without being fully deployed.")
|
|
)
|
|
playsound(src, 'sound/items/rfd_empty.ogg', 20, FALSE)
|
|
failed_to_seal = 1
|
|
|
|
var/is_in_cycler = istype(initiator.loc, /obj/structure/machinery/suit_cycler)
|
|
seal_delay = is_in_cycler ? 1 : initial(seal_delay)
|
|
|
|
var/jumpsuit_was_hidden = FALSE
|
|
if(suit_is_deployed() && (wearer.wear_suit.flags_inv & HIDEJUMPSUIT) && !instant && has_hidden_jumpsuit)
|
|
/// Don't hide the jumpsuit while removing the rig.
|
|
wearer.wear_suit.flags_inv &= ~HIDEJUMPSUIT
|
|
wearer.update_inv_wear_suit()
|
|
jumpsuit_was_hidden = TRUE
|
|
|
|
if(!failed_to_seal)
|
|
if(!instant)
|
|
wearer.visible_message(SPAN_NOTICE("[wearer]'s suit emits a quiet hum as it begins to adjust its seals."),
|
|
SPAN_NOTICE("With a quiet hum, the suit begins running checks and adjusting components."))
|
|
|
|
if(seal_delay && !do_after(wearer, seal_delay, src, do_flags = DO_DEFAULT & ~DO_USER_SAME_HAND))
|
|
if(wearer) to_chat(wearer, SPAN_WARNING("You must remain still while the suit is adjusting the components."))
|
|
playsound(src, 'sound/items/rfd_empty.ogg', 20, FALSE)
|
|
failed_to_seal = 1
|
|
|
|
if(!wearer)
|
|
failed_to_seal = 1
|
|
else
|
|
for(var/list/piece_data in list(
|
|
list(wearer.shoes, boots, "boots", boot_type),
|
|
list(wearer.gloves, gloves, "gloves", glove_type),
|
|
list(wearer.head, helmet, "helmet", helm_type),
|
|
list(wearer.wear_suit, chest, "chest", chest_type)))
|
|
|
|
var/obj/item/clothing/piece = piece_data[1]
|
|
var/obj/item/compare_piece = piece_data[2]
|
|
var/msg_type = piece_data[3]
|
|
var/piece_type = piece_data[4]
|
|
|
|
if(!piece || !piece_type)
|
|
continue
|
|
|
|
if(!istype(wearer) || !istype(piece) || !istype(compare_piece) || !msg_type)
|
|
if(wearer) to_chat(wearer, SPAN_WARNING("You must remain still while the suit is adjusting the components."))
|
|
playsound(src, 'sound/items/rfd_empty.ogg', 20, FALSE)
|
|
failed_to_seal = 1
|
|
break
|
|
|
|
if(!failed_to_seal && wearer.back == src && piece == compare_piece)
|
|
|
|
if(seal_delay && !instant && !do_after(wearer, seal_delay, src, do_flags = DO_DEFAULT & ~DO_USER_SAME_HAND))
|
|
failed_to_seal = 1
|
|
|
|
update_sealed_piece_icon(piece, seal_target)
|
|
switch(msg_type)
|
|
if("boots")
|
|
to_chat(wearer, SPAN_NOTICE("\The [piece] [!seal_target ? "seal around your feet" : "relax their grip on your legs"]."))
|
|
wearer.update_inv_shoes()
|
|
if("gloves")
|
|
to_chat(wearer, SPAN_NOTICE("\The [piece] [!seal_target ? "tighten around your fingers and wrists" : "become loose around your fingers"]."))
|
|
wearer.update_inv_gloves()
|
|
if("chest")
|
|
to_chat(wearer, SPAN_NOTICE("\The [piece] [!seal_target ? "cinches your chest tightly" : "releases your chest"]."))
|
|
if(has_hidden_jumpsuit)
|
|
if(!seal_target)
|
|
piece.flags_inv |= HIDEJUMPSUIT
|
|
else
|
|
piece.flags_inv &= ~HIDEJUMPSUIT
|
|
wearer.update_inv_wear_suit()
|
|
if("helmet")
|
|
to_chat(wearer, SPAN_NOTICE("\The [piece] hisses [!seal_target ? "closed" : "open"]."))
|
|
wearer.update_inv_head()
|
|
if(helmet)
|
|
helmet.update_light(wearer)
|
|
|
|
//sealed pieces become airtight, protecting against diseases
|
|
var/datum/component/armor/armor_component = piece.GetComponent(/datum/component/armor)
|
|
if(istype(armor_component))
|
|
armor_component.sealed = !seal_target
|
|
playsound(src, "[!seal_target ? 'sound/machines/rig/rig_deploy.ogg' : 'sound/machines/rig/rig_retract.ogg']", 30, FALSE)
|
|
|
|
else
|
|
failed_to_seal = 1
|
|
|
|
if((wearer && !(istype(wearer) && wearer.back == src)) || (!seal_target && !suit_is_deployed()))
|
|
failed_to_seal = 1
|
|
|
|
sealing = null
|
|
|
|
if(failed_to_seal)
|
|
for(var/obj/item/piece in list(helmet,boots,gloves,chest))
|
|
if(!piece)
|
|
continue
|
|
piece.icon_state = "[initial(icon_state)][!seal_target ? "" : "_sealed"]"
|
|
canremove = !seal_target
|
|
if(airtight)
|
|
update_component_sealed()
|
|
if(jumpsuit_was_hidden && wearer.wear_suit == chest && has_hidden_jumpsuit)
|
|
wearer.wear_suit.flags_inv |= HIDEJUMPSUIT
|
|
update_icon(1)
|
|
return 0
|
|
|
|
// Success!
|
|
canremove = seal_target
|
|
to_chat(wearer, SPAN_NOTICE("<b>Your entire suit [canremove ? "loosens as the components relax" : "tightens around you as the components lock into place"].</b>"))
|
|
playsound(src, 'sound/items/rped.ogg', 30, FALSE)
|
|
if (has_sealed_state)
|
|
icon_state = canremove ? initial(icon_state) : "[initial(icon_state)]_sealed"
|
|
if(dnaLock && !offline)
|
|
if(dnaLock != wearer.dna)
|
|
visible_message("[icon2html(src, viewers(get_turf(src)))] <b>[src]</b> announces, <span class='notice'>\"DNA mismatch. Unauthorized access detected.\"</span>")
|
|
crushing = TRUE
|
|
|
|
if(wearer != initiator)
|
|
to_chat(initiator, SPAN_NOTICE("Suit adjustment complete. Suit is now [canremove ? "unsealed" : "sealed"]."))
|
|
|
|
if(canremove)
|
|
for(var/obj/item/rig_module/module in installed_modules)
|
|
module.deactivate(initiator)
|
|
if(airtight)
|
|
update_component_sealed()
|
|
update_icon(1)
|
|
if(is_in_cycler)
|
|
initiator.loc.update_icon()
|
|
SSstatpanels.set_action_tabs(initiator.client, initiator)
|
|
|
|
/// Sets the piece's icon and item state based on the seal target, can be overriden for custom functionality
|
|
/obj/item/rig/proc/update_sealed_piece_icon(var/obj/item/clothing/piece, var/seal_target)
|
|
piece.icon_state = "[initial(icon_state)][!seal_target ? "_sealed" : ""]_[piece.clothing_class()]"
|
|
piece.item_state = "[initial(icon_state)][!seal_target ? "_sealed" : ""]"
|
|
|
|
/obj/item/rig/proc/update_component_sealed()
|
|
for(var/obj/item/piece in list(helmet,boots,gloves,chest))
|
|
if(canremove)
|
|
piece.max_pressure_protection = initial(piece.max_pressure_protection)
|
|
piece.min_pressure_protection = initial(piece.min_pressure_protection)
|
|
piece.item_flags &= ~ITEM_FLAG_AIRTIGHT
|
|
else
|
|
piece.max_pressure_protection = max_pressure_protection
|
|
piece.min_pressure_protection = min_pressure_protection
|
|
piece.item_flags |= ITEM_FLAG_AIRTIGHT
|
|
update_icon(1)
|
|
|
|
/obj/item/rig/process(seconds_per_tick)
|
|
// If we've lost any parts, grab them back.
|
|
var/mob/living/M
|
|
for(var/obj/item/piece in list(gloves,boots,helmet,chest))
|
|
if(piece.loc != src && !(wearer && piece.loc == wearer))
|
|
if(istype(piece.loc, /mob/living))
|
|
M = piece.loc
|
|
M.drop_from_inventory(piece,src)
|
|
else
|
|
piece.forceMove(src)
|
|
|
|
var/previous_offline_status = offline
|
|
// Consume energy per tick while active. If you don't have a cell... Weird, but we'll deal with that shortly.
|
|
var/power_usage_this_tick = ((!offline && cell) ? (cell_draw_rate * seconds_per_tick) : 0.0)
|
|
|
|
if(!istype(wearer) || loc != wearer || wearer.back != src || canremove || !cell || cell.charge <= 0)
|
|
if(!cell || cell.charge <= 0)
|
|
if(electrified > 0)
|
|
electrified = 0
|
|
if(!offline)
|
|
if(istype(wearer))
|
|
playsound(src, 'sound/machines/rig/rig_shutdown.ogg', 35, FALSE)
|
|
if(!canremove)
|
|
if(offline_slowdown < 3)
|
|
to_chat(wearer, SPAN_DANGER("Your suit beeps stridently, and suddenly goes dead."))
|
|
else
|
|
to_chat(wearer, SPAN_DANGER("Your suit beeps stridently, and suddenly you're wearing a leaden mass of metal and plastic composites instead of a powered suit."))
|
|
if(offline_vision_restriction == 1)
|
|
to_chat(wearer, SPAN_DANGER("The suit optics flicker and die, leaving you with restricted vision."))
|
|
else if(offline_vision_restriction == 2)
|
|
to_chat(wearer, SPAN_DANGER("The suit optics drop out completely, drowning you in darkness."))
|
|
if(!offline)
|
|
offline = 1
|
|
else
|
|
if(offline)
|
|
offline = 0
|
|
if(istype(wearer) && !wearer.wearing_rig)
|
|
wearer.wearing_rig = src
|
|
|
|
update_slowdown()
|
|
|
|
if(offline != previous_offline_status)
|
|
update_icon(TRUE)
|
|
|
|
set_vision(!offline)
|
|
|
|
if(cell && cell.charge > 0 && electrified > 0)
|
|
electrified -= seconds_per_tick
|
|
|
|
if(crushing)
|
|
wearer.apply_damage(10 * seconds_per_tick) // Applies 10 brute damage per second to a random extremity
|
|
if(wearer.stat == DEAD)
|
|
crushing = FALSE
|
|
visible_message(SPAN_DANGER("A squelching sound comes from within the sealed hardsuit..")) // this denotes that the user inside has died.
|
|
|
|
if(offline)
|
|
crushing = FALSE
|
|
if(offline == 1)
|
|
for(var/obj/item/rig_module/module in installed_modules)
|
|
module.deactivate()
|
|
offline = 2
|
|
update_slowdown()
|
|
return
|
|
|
|
if(malfunction_delay > 0)
|
|
malfunction_delay--
|
|
else if(malfunctioning)
|
|
malfunctioning--
|
|
malfunction()
|
|
|
|
for(var/obj/item/rig_module/module in installed_modules)
|
|
power_usage_this_tick += module.process()
|
|
|
|
if(power_usage_this_tick)
|
|
cell.use(power_usage_this_tick)
|
|
|
|
if(!malfunctioning && !electrified && !open && offline && !wearer) //If nothing is wrong with it and it is not in use, it should stop processing.
|
|
return PROCESS_KILL
|
|
|
|
/obj/item/rig/proc/check_power_cost(var/mob/living/user, var/cost, var/use_unconcious, var/obj/item/rig_module/mod, var/user_is_ai)
|
|
|
|
if(!istype(user))
|
|
return FALSE
|
|
|
|
var/fail_msg
|
|
|
|
if(!user_is_ai)
|
|
var/mob/living/carbon/human/H = user
|
|
if(istype(H) && H.back != src)
|
|
fail_msg = SPAN_WARNING("You must be wearing \the [src] to do this.")
|
|
else if(user.incorporeal_move)
|
|
fail_msg = SPAN_WARNING("You must be solid to do this.")
|
|
if(sealing)
|
|
fail_msg = SPAN_WARNING("The hardsuit is in the process of adjusting seals and cannot be activated.")
|
|
else if(!fail_msg && ((use_unconcious && user.stat > 1) || (!use_unconcious && user.stat)))
|
|
fail_msg = SPAN_WARNING("You are in no fit state to do that.")
|
|
else if(!cell)
|
|
fail_msg = SPAN_WARNING("There is no cell installed in the suit.")
|
|
else if(cost && cell?.charge < cost) //TODO: Cellrate?
|
|
fail_msg = SPAN_WARNING("Not enough stored power.")
|
|
|
|
if(fail_msg)
|
|
// If a module has been passed to this proc, then the module will handle its own error messages.
|
|
if(!mod)
|
|
to_chat(user, "[fail_msg]")
|
|
playsound(src, 'sound/items/rfd_empty.ogg', 20, FALSE)
|
|
return FALSE
|
|
|
|
// This is largely for cancelling stealth and whatever.
|
|
if(mod && mod.disruptive)
|
|
for(var/obj/item/rig_module/module in (installed_modules - mod))
|
|
if(module.active && module.disruptable)
|
|
module.deactivate()
|
|
|
|
cell.use(cost)
|
|
return TRUE
|
|
|
|
/obj/item/rig/update_icon(var/update_mob_icon)
|
|
//TODO: Maybe consider a cache for this (use mob_icon as blank canvas, use suit icon overlay).
|
|
ClearOverlays()
|
|
if(!mob_icon || update_mob_icon)
|
|
var/species_icon = 'icons/mob/rig_back.dmi'
|
|
// Since setting mob_icon will override the species checks in
|
|
// update_inv_wear_suit(), handle species checks here.
|
|
if(wearer && sprite_sheets && sprite_sheets[wearer.species.get_bodytype()])
|
|
species_icon = sprite_sheets[wearer.species.get_bodytype()]
|
|
mob_icon = image("icon" = species_icon, "icon_state" = "[icon_state]")
|
|
|
|
if(length(installed_modules))
|
|
for(var/obj/item/rig_module/module in installed_modules)
|
|
if(module.suit_overlay)
|
|
chest.AddOverlays(image("icon" = 'icons/mob/rig_modules.dmi', "icon_state" = "[module.suit_overlay]", "dir" = SOUTH))
|
|
|
|
if(wearer)
|
|
wearer.update_inv_shoes()
|
|
wearer.update_inv_gloves()
|
|
wearer.update_inv_head()
|
|
wearer.update_inv_wear_suit()
|
|
wearer.update_inv_back()
|
|
return
|
|
|
|
/obj/item/rig/get_mob_overlay(var/mob/living/carbon/human/H, var/mob_icon, var/mob_state, var/slot)
|
|
var/image/I = ..()
|
|
if(length(installed_modules))
|
|
if((chest_type && (chest && wearer?.wear_suit == chest)))
|
|
for(var/obj/item/rig_module/module in installed_modules)
|
|
if(module.suit_overlay)
|
|
I.AddOverlays(image('icons/mob/rig_modules.dmi', null, module.suit_overlay))
|
|
return I
|
|
|
|
/obj/item/rig/get_cell()
|
|
if(cell)
|
|
return cell
|
|
return ..()
|
|
|
|
/obj/item/rig/proc/is_integrated_rig_ai(var/mob/living/user)
|
|
if(!user)
|
|
return FALSE
|
|
for(var/obj/item/rig_module/ai_container/module in installed_modules)
|
|
if(module.integrated_ai == user)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/obj/item/rig/proc/check_suit_access(var/mob/living/user)
|
|
if(!security_check_enabled || !locked)
|
|
return TRUE
|
|
|
|
if(is_integrated_rig_ai(user))
|
|
return TRUE
|
|
|
|
if(ishuman(user))
|
|
var/mob/living/carbon/human/H = user
|
|
if(malfunction_check(H))
|
|
return FALSE
|
|
if(H.back != src)
|
|
return FALSE
|
|
else if(!src.allowed(H))
|
|
sound_to(H, 'sound/machines/terminal/terminal_error.ogg')
|
|
balloon_alert(H, "access denied!")
|
|
return FALSE
|
|
|
|
else if(!ai_override_enabled)
|
|
to_chat(user, SPAN_DANGER("Synthetic access disabled. Please consult hardware provider."))
|
|
return FALSE
|
|
|
|
return TRUE
|
|
|
|
/obj/item/rig/ui_interact(mob/user, datum/tgui/ui)
|
|
if(!user)
|
|
return
|
|
|
|
ui = SStgui.try_update_ui(user, src, ui)
|
|
if(!ui)
|
|
ui = new(user, src, "Hardsuit", src, 480, 550)
|
|
ui.open()
|
|
ui.set_autoupdate(TRUE)
|
|
|
|
/obj/item/rig/ui_data(mob/user)
|
|
var/list/data = list()
|
|
|
|
if(selected_module)
|
|
data["primarysystem"] = selected_module.interface_name
|
|
data["primarysystem_ref"] = REF(selected_module)
|
|
// Needs to be explicitly nulled when de-selected, otherwise the old data persists in the UI.
|
|
else
|
|
data["primarysystem"] = null
|
|
data["primarysystem_ref"] = null
|
|
|
|
data["ai"] = (src.loc != user)
|
|
|
|
data["seals"] = canremove
|
|
data["sealing"] = sealing
|
|
|
|
data["helmet"] = helmet ? helmet.name : "None."
|
|
data["gauntlets"] = gloves ? gloves.name : "None."
|
|
data["boots"] = boots ? boots.name : "None."
|
|
data["chest"] = chest ? chest.name : "None."
|
|
|
|
var/data_charge = cell ? round(cell.charge, 1) : 0
|
|
var/data_maxcharge = cell ? cell.maxcharge : 1
|
|
data["charge"] = data_charge
|
|
data["maxcharge"] = data_maxcharge
|
|
data["chargedisplay"] = "[power_joules_readable(data_charge)] / [power_joules_readable(data_maxcharge)]"
|
|
data["chargestatus"] = cell ? FLOOR((cell.charge / cell.maxcharge) * 50, 1) : 0
|
|
|
|
data["emagged"] = subverted
|
|
data["coverlock"] = locked
|
|
data["interfacelock"] = interface_locked
|
|
data["aicontrol"] = control_overridden
|
|
data["aioverride"] = ai_override_enabled
|
|
data["id_lock"] = security_check_enabled
|
|
data["malf"] = malfunction_delay
|
|
|
|
var/list/module_list = list()
|
|
var/i = 1
|
|
for(var/obj/item/rig_module/module in installed_modules)
|
|
var/list/module_data = list(
|
|
"index" = i,
|
|
"module_name" = module.interface_name,
|
|
"desc" = module.interface_desc,
|
|
"module_type" = module.module_type,
|
|
"module_active" = module.active,
|
|
"module_selected" = selected_module == module ? TRUE : FALSE,
|
|
"engagecost" = module.use_power_cost,
|
|
"activecost" = module.active_power_cost,
|
|
"passivecost" = module.passive_power_cost,
|
|
"engagestring" = module.engage_string,
|
|
"activatestring" = module.activate_string,
|
|
"deactivatestring" = module.deactivate_string,
|
|
"damage" = module.damage,
|
|
"ref" = REF(module),
|
|
"configuration_data" = module.get_configuration(user)
|
|
)
|
|
|
|
if(module.charges && module.charges.len)
|
|
module_data["charges"] = list()
|
|
|
|
var/datum/rig_charge/selected = module.charges[module.charge_selected]
|
|
module_data["charge_selected"] = "[module.charge_selected]"
|
|
module_data["chargetype"] = selected ? selected.display_name : "none"
|
|
|
|
for(var/chargetype in module.charges)
|
|
var/datum/rig_charge/charge = module.charges[chargetype]
|
|
module_data["charges"] += list(list(
|
|
"chargetype" = "[chargetype]",
|
|
"charges" = "[charge.charges]",
|
|
"index" = "[chargetype]"
|
|
))
|
|
|
|
module_list += list(module_data)
|
|
i++
|
|
|
|
if(module_list.len)
|
|
data["modules"] = module_list
|
|
|
|
return data
|
|
|
|
/obj/item/rig/ui_act(action, params)
|
|
. = ..()
|
|
if(.)
|
|
return TRUE
|
|
|
|
var/mob/user = usr
|
|
if(!user)
|
|
return TRUE
|
|
// Just handles the UI's examine buttons. Doesn't need to check for access.
|
|
switch(action)
|
|
// Returns the Armor Values UI.
|
|
if("examine_armor")
|
|
var/list/armor_details = list()
|
|
for(var/armor_type in armor)
|
|
armor_details[armor_type] = armor[armor_type]
|
|
var/datum/tgui_module/armor_values/AV = new /datum/tgui_module/armor_values(user, capitalize_first_letters(name), armor_details)
|
|
AV.ui_interact(user)
|
|
return TRUE
|
|
if("examine_fluff")
|
|
examine(user, show_extended = TRUE)
|
|
return TRUE
|
|
|
|
if(!check_suit_access(user))
|
|
return FALSE
|
|
|
|
// Suit access-gated actions.
|
|
switch(action)
|
|
// Toggles a 'clothing' piece on or off.
|
|
if("toggle_piece")
|
|
var/piece = "[params["piece"]]"
|
|
if(!piece)
|
|
return TRUE
|
|
|
|
if(ishuman(user) && (user.stat || user.stunned || user.lying))
|
|
return TRUE
|
|
|
|
toggle_piece(piece, user)
|
|
|
|
// Deploys all pieces.
|
|
if("toggle_seals")
|
|
toggle_seals(user)
|
|
|
|
// Module interactions.
|
|
if("interact_module")
|
|
var/module_index = text2num(params["index"])
|
|
if(!module_index)
|
|
return TRUE
|
|
|
|
if(module_index > 0 && module_index <= installed_modules.len)
|
|
var/obj/item/rig_module/module = installed_modules[module_index]
|
|
var/mode = "[params["mode"]]"
|
|
switch(mode)
|
|
if("activate")
|
|
module.activate(user)
|
|
sound_to(usr, module?.sound_activate)
|
|
if("deactivate")
|
|
module.deactivate(user)
|
|
sound_to(usr, module?.sound_deactivate)
|
|
if("engage")
|
|
module.do_engage(null, user)
|
|
if("select")
|
|
if(selected_module != module)
|
|
// Clear the currently selected module's overlay.
|
|
if(selected_module)
|
|
selected_module.suit_overlay = null
|
|
selected_module = module
|
|
// Set the new selected module's suit overlay.
|
|
if(selected_module.suit_overlay_active)
|
|
selected_module.suit_overlay = selected_module.suit_overlay_active
|
|
sound_to(usr, module?.sound_activate)
|
|
balloon_alert_to_viewers("deploys \the [module.interface_name]", "deploys \the [module.interface_name]")
|
|
else
|
|
sound_to(usr, selected_module?.sound_deactivate)
|
|
selected_module.suit_overlay = null
|
|
selected_module = null
|
|
balloon_alert_to_viewers("retracts \the [module.interface_name]", "retracts \the [module.interface_name]")
|
|
update_icon(TRUE)
|
|
playsound(src.loc, 'sound/items/rfd_dispense.ogg', 25, FALSE)
|
|
if("select_charge_type")
|
|
module.charge_selected = "[params["charge_type"]]"
|
|
sound_to(usr, module.sound_config)
|
|
|
|
if("toggle_ai_control")
|
|
ai_override_enabled = !ai_override_enabled
|
|
notify_ai("Synthetic suit control has been [ai_override_enabled ? "enabled" : "disabled"].")
|
|
sound_to(usr, 'sound/machines/terminal/terminal_prompt_deny.ogg')
|
|
|
|
// Cover panel locked or unlocked (by access ID).
|
|
if("toggle_suit_lock")
|
|
locked = !locked
|
|
sound_to(usr, 'sound/machines/terminal/terminal_prompt_deny.ogg')
|
|
// Toggles whether access locks are enabled.
|
|
if("toggle_id_lock")
|
|
// Only those people who have access rights in the first place should be able to toggle access on or off.
|
|
if(!src.allowed(user))
|
|
sound_to(user, 'sound/machines/terminal/terminal_error.ogg')
|
|
balloon_alert(user, "access denied!")
|
|
else
|
|
security_check_enabled = !security_check_enabled
|
|
sound_to(usr, 'sound/machines/terminal/terminal_prompt_deny.ogg')
|
|
|
|
if("configure")
|
|
var/obj/item/rig_module/module = locate(params["ref"]) in installed_modules
|
|
if(!module)
|
|
return
|
|
module.configure_edit(params["key"], params["value"], user)
|
|
sound_to(usr, module.sound_config)
|
|
|
|
user.set_machine(src)
|
|
src.add_fingerprint(user)
|
|
|
|
return TRUE
|
|
|
|
// Makes sure the UI is only available if you're wearing it, unless you're an AI.
|
|
/obj/item/rig/ui_state(mob/user)
|
|
if(!issilicon(user))
|
|
return GLOB.inventory_state
|
|
|
|
/obj/item/rig/proc/notify_ai(var/message)
|
|
for(var/obj/item/rig_module/ai_container/module in installed_modules)
|
|
if(module.integrated_ai && module.integrated_ai.client && !module.integrated_ai.stat)
|
|
to_chat(module.integrated_ai, "[message]")
|
|
. = 1
|
|
|
|
/obj/item/rig/check_equipped(mob/living/carbon/human/M, slot, assisted_equip = FALSE)
|
|
if(istype(M) && slot == slot_back)
|
|
if(!assisted_equip && seal_delay > 0)
|
|
M.visible_message(SPAN_NOTICE("[M] starts putting on \the [src]..."), SPAN_NOTICE("You start putting on \the [src]..."))
|
|
if(!do_after(M, seal_delay))
|
|
return FALSE
|
|
|
|
if(wearer && wearer != M)
|
|
unregister_wearer_signals(wearer)
|
|
|
|
M.visible_message(SPAN_NOTICE("<b>[M] struggles into \the [src].</b>"), SPAN_NOTICE("<b>You struggle into \the [src].</b>"))
|
|
wearer = M
|
|
wearer.wearing_rig = src
|
|
register_wearer_signals(wearer)
|
|
update_icon(TRUE)
|
|
return TRUE
|
|
return TRUE
|
|
|
|
/obj/item/rig/proc/toggle_piece(var/piece, var/mob/initiator, var/deploy_mode)
|
|
if(piece_being_deployed)
|
|
return
|
|
|
|
if(!usr || sealing || !cell || !cell.charge)
|
|
return
|
|
|
|
if(!istype(wearer) || !wearer.back == src)
|
|
return
|
|
|
|
if(initiator == wearer && (usr.stat||usr.paralysis||usr.stunned)) // If the initiator isn't wearing the suit it's probably an AI.
|
|
return
|
|
|
|
var/obj/item/check_slot
|
|
var/equip_to
|
|
var/obj/item/use_obj
|
|
|
|
if(!wearer)
|
|
return
|
|
|
|
switch(piece)
|
|
if("helmet")
|
|
equip_to = slot_head
|
|
use_obj = helmet
|
|
check_slot = wearer.head
|
|
if("gauntlets")
|
|
equip_to = slot_gloves
|
|
use_obj = gloves
|
|
check_slot = wearer.gloves
|
|
if("boots")
|
|
equip_to = slot_shoes
|
|
use_obj = boots
|
|
check_slot = wearer.shoes
|
|
if("chest")
|
|
equip_to = slot_wear_suit
|
|
use_obj = chest
|
|
check_slot = wearer.wear_suit
|
|
|
|
piece_being_deployed = TRUE
|
|
if(use_obj)
|
|
// Handle retracting the piece, if possible.
|
|
if(check_slot == use_obj && deploy_mode != ONLY_DEPLOY)
|
|
var/mob/living/carbon/human/holder
|
|
holder = use_obj.loc
|
|
if(istype(holder))
|
|
if(check_slot == use_obj)
|
|
to_chat(wearer, "<font color='blue'><b>Your [use_obj.name] [use_obj.gender == PLURAL ? "retract" : "retracts"] swiftly.</b></font>")
|
|
playsound(src, 'sound/machines/rig/rig_retract.ogg', 30, FALSE)
|
|
use_obj.canremove = 1
|
|
holder.drop_from_inventory(use_obj,get_turf(src)) //TODO: TEST THIS CODE!
|
|
use_obj.dropped(wearer)
|
|
use_obj.canremove = 0
|
|
use_obj.forceMove(src)
|
|
|
|
// Handle deploying the piece, if possible.
|
|
else if (deploy_mode != ONLY_RETRACT)
|
|
if(check_slot && check_slot == use_obj)
|
|
piece_being_deployed = FALSE
|
|
return TRUE
|
|
if(!do_after(wearer, 8))
|
|
if(wearer)
|
|
to_chat(wearer, SPAN_WARNING("You must remain still while the suit deploys its parts."))
|
|
piece_being_deployed = FALSE
|
|
return FALSE
|
|
if(!wearer || wearer.back != src) ///Prevents an edge case where a suit with a storage module can be removed while deploying, causing a runtime.
|
|
piece_being_deployed = FALSE
|
|
return FALSE
|
|
use_obj.forceMove(wearer)
|
|
if(src.color)
|
|
use_obj.color = src.color
|
|
if(!wearer.equip_to_slot_if_possible(use_obj, equip_to, 0, 1))
|
|
use_obj.forceMove(src)
|
|
if(check_slot)
|
|
to_chat(initiator, SPAN_DANGER("You are unable to deploy \the [piece] as \the [check_slot] [check_slot.gender == PLURAL ? "are" : "is"] in the way."))
|
|
playsound(src, 'sound/items/rfd_empty.ogg', 20, FALSE)
|
|
piece_being_deployed = FALSE
|
|
return FALSE
|
|
else
|
|
to_chat(wearer, SPAN_NOTICE("Your [use_obj.name] [use_obj.gender == PLURAL ? "deploy" : "deploys"] swiftly."))
|
|
playsound(src, 'sound/machines/rig/rig_deploy.ogg', 30, FALSE)
|
|
|
|
if(piece == "helmet" && helmet)
|
|
helmet.update_light(wearer)
|
|
|
|
update_icon(TRUE)
|
|
update_slowdown()
|
|
|
|
piece_being_deployed = FALSE
|
|
return TRUE
|
|
|
|
/obj/item/rig/proc/deploy(mob/M,var/sealed)
|
|
var/mob/living/carbon/human/H = M
|
|
|
|
if(!H || !istype(H)) return
|
|
|
|
if(H.back != src)
|
|
return
|
|
|
|
if(sealed)
|
|
if(H.head)
|
|
var/obj/item/garbage = H.head
|
|
H.head = null
|
|
qdel(garbage)
|
|
|
|
if(H.gloves)
|
|
var/obj/item/garbage = H.gloves
|
|
H.gloves = null
|
|
qdel(garbage)
|
|
|
|
if(H.shoes)
|
|
var/obj/item/garbage = H.shoes
|
|
H.shoes = null
|
|
qdel(garbage)
|
|
|
|
if(H.wear_suit)
|
|
var/obj/item/garbage = H.wear_suit
|
|
H.wear_suit = null
|
|
qdel(garbage)
|
|
|
|
for(var/piece in list("helmet","gauntlets","boots",BP_CHEST))
|
|
toggle_piece(piece, H, ONLY_DEPLOY)
|
|
|
|
/obj/item/rig/proc/retract(mob/M,var/sealed)
|
|
var/mob/living/carbon/human/H = M
|
|
|
|
if(!H || !istype(H)) return
|
|
|
|
if(H.back != src)
|
|
return
|
|
|
|
if(sealed)
|
|
if(H.head)
|
|
var/obj/item/garbage = H.head
|
|
H.head = null
|
|
qdel(garbage)
|
|
|
|
if(H.gloves)
|
|
var/obj/item/garbage = H.gloves
|
|
H.gloves = null
|
|
qdel(garbage)
|
|
|
|
if(H.shoes)
|
|
var/obj/item/garbage = H.shoes
|
|
H.shoes = null
|
|
qdel(garbage)
|
|
|
|
if(H.wear_suit)
|
|
var/obj/item/garbage = H.wear_suit
|
|
H.wear_suit = null
|
|
qdel(garbage)
|
|
|
|
for(var/piece in list("helmet","gauntlets",BP_CHEST,"boots"))
|
|
toggle_piece(piece, H, ONLY_RETRACT)
|
|
|
|
/obj/item/rig/proc/null_wearer(var/mob/user)
|
|
unregister_wearer_signals(wearer)
|
|
for(var/piece in list("helmet","gauntlets",BP_CHEST,"boots"))
|
|
toggle_piece(piece, user, ONLY_RETRACT)
|
|
if(wearer)
|
|
wearer.wearing_rig = null
|
|
wearer = null
|
|
|
|
/obj/item/rig/proc/register_wearer_signals(mob/living/carbon/human/new_wearer)
|
|
if(!new_wearer)
|
|
return
|
|
|
|
RegisterSignal(new_wearer, COMSIG_MOB_CLICKON, PROC_REF(handle_wearer_click))
|
|
|
|
/obj/item/rig/proc/unregister_wearer_signals(mob/living/carbon/human/old_wearer)
|
|
if(!old_wearer)
|
|
return
|
|
|
|
UnregisterSignal(old_wearer, COMSIG_MOB_CLICKON)
|
|
|
|
/obj/item/rig/proc/handle_wearer_click(mob/user, atom/target, modifiers)
|
|
SIGNAL_HANDLER
|
|
if(offline == 2)
|
|
return
|
|
if(LAZYACCESS(modifiers, SHIFT_CLICK))
|
|
return
|
|
if(LAZYACCESS(modifiers, ALT_CLICK))
|
|
return
|
|
if(LAZYACCESS(modifiers, RIGHT_CLICK))
|
|
return
|
|
|
|
if(user.in_throw_mode && (isturf(target) || isturf(target.loc)) && user.throw_item(target)) //Prevents throwing items while remaining cloaked.
|
|
attack_disrupt_check()
|
|
|
|
if(ismob(target)) //This doesn't prevent guns firing at turfs, that is handled in /obj/item/gun/proc/handle_post_fire
|
|
if(target != user)
|
|
attack_disrupt_check()
|
|
|
|
/obj/item/rig/on_slotmove(var/mob/user)
|
|
..()
|
|
null_wearer(user)
|
|
|
|
/obj/item/rig/dropped(mob/user)
|
|
..()
|
|
|
|
if(user.client)
|
|
SSstatpanels.set_action_tabs_RIG(user.client, user)
|
|
|
|
null_wearer(user)
|
|
STOP_PROCESSING(SSprocessing, src)
|
|
//Todo
|
|
/obj/item/rig/proc/malfunction()
|
|
return 0
|
|
|
|
/obj/item/rig/emp_act(severity)
|
|
. = ..()
|
|
|
|
var/obj/item/rig_module/storage/storage = locate() in installed_modules
|
|
if(storage)
|
|
storage.pockets.emp_act(severity)
|
|
|
|
//set malfunctioning
|
|
if(emp_protection < 30) //for ninjas, really.
|
|
malfunctioning += 10
|
|
if(malfunction_delay <= 0)
|
|
malfunction_delay = max(malfunction_delay, round(30/severity))
|
|
|
|
//drain some charge
|
|
if(cell) cell.powerdrain((severity + 15)*1+(0.1*emp_protection))
|
|
|
|
//possibly damage some modules
|
|
take_hit((100/severity), "electrical pulse", 1)
|
|
|
|
/// Handles running electrocute_mob() on the passed user. If the user is wearing the suit, it picks between head, chest, and groin for contact_zone.
|
|
/// If not wearing the suit (i.e. tampering with the wires), contact_zone is assumed to be the passed user's hands.
|
|
/obj/item/rig/proc/shock(mob/user)
|
|
var/touchy = pick(BP_CHEST,BP_HEAD,BP_GROIN)
|
|
if(!wearer)
|
|
touchy = "hand"
|
|
|
|
if(electrocute_mob(user, cell, src, contact_zone = touchy)) //electrocute_mob() handles removing charge from the cell, no need to do that here.
|
|
spark_system.queue()
|
|
if(user.stunned)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/obj/item/rig/proc/take_hit(damage, source, is_emp=0)
|
|
|
|
if(!installed_modules.len)
|
|
return
|
|
|
|
var/chance
|
|
if(!is_emp)
|
|
var/damage_resistance = 0
|
|
if(istype(chest, /obj/item/clothing/suit/space))
|
|
damage_resistance = chest.breach_threshold
|
|
chance = 2*max(0, damage - damage_resistance)
|
|
else
|
|
//Want this to be roughly independant of the number of modules, meaning that X emp hits will disable Y% of the suit's modules on average.
|
|
//that way people designing hardsuits don't have to worry (as much) about how adding that extra module will affect emp resiliance by 'soaking' hits for other modules
|
|
chance = 2*max(0, damage - emp_protection)*min(installed_modules.len/15, 1)
|
|
|
|
if(!prob(chance))
|
|
return
|
|
|
|
//deal addition damage to already damaged module first.
|
|
//This way the chances of a module being disabled aren't so remote.
|
|
var/list/valid_modules = list()
|
|
var/list/damaged_modules = list()
|
|
for(var/obj/item/rig_module/module in installed_modules)
|
|
if(module.damage < 2)
|
|
valid_modules |= module
|
|
if(module.damage > 0)
|
|
damaged_modules |= module
|
|
|
|
var/obj/item/rig_module/dam_module = null
|
|
if(damaged_modules.len)
|
|
dam_module = pick(damaged_modules)
|
|
else if(valid_modules.len)
|
|
dam_module = pick(valid_modules)
|
|
|
|
if(!dam_module) return
|
|
|
|
dam_module.damage++
|
|
|
|
if(!source)
|
|
source = "hit"
|
|
|
|
if(wearer)
|
|
if(dam_module.damage >= 2)
|
|
to_chat(wearer, SPAN_DANGER("The [source] has disabled your [dam_module.interface_name]!"))
|
|
else
|
|
to_chat(wearer, SPAN_WARNING("The [source] has damaged your [dam_module.interface_name]!"))
|
|
dam_module.deactivate()
|
|
|
|
/obj/item/rig/proc/malfunction_check(var/mob/living/user)
|
|
if(malfunction_delay)
|
|
if(offline)
|
|
to_chat(user, SPAN_DANGER("The suit is completely unresponsive."))
|
|
else
|
|
to_chat(user, SPAN_DANGER("ERROR: Hardware fault. Rebooting interface..."))
|
|
return 1
|
|
return 0
|
|
|
|
/obj/item/rig/proc/ai_can_move_suit(var/mob/user, var/check_user_module = 0, var/check_for_ai = 0)
|
|
|
|
if(check_for_ai)
|
|
if(!(locate(/obj/item/rig_module/ai_container) in contents))
|
|
return 0
|
|
var/found_ai
|
|
for(var/obj/item/rig_module/ai_container/module in contents)
|
|
if(module.damage >= 2)
|
|
continue
|
|
if(module.integrated_ai && module.integrated_ai.client && !module.integrated_ai.stat)
|
|
found_ai = 1
|
|
break
|
|
if(!found_ai)
|
|
return 0
|
|
|
|
if(check_user_module)
|
|
if(!user || !user.loc || !user.loc.loc)
|
|
return 0
|
|
var/obj/item/rig_module/ai_container/module = user.loc.loc
|
|
if(!istype(module) || module.damage >= 2)
|
|
to_chat(user, SPAN_WARNING("Your host module is unable to interface with the suit."))
|
|
return 0
|
|
|
|
if(offline || !cell || !cell.charge)
|
|
if(user) to_chat(user, SPAN_WARNING("Your host rig is unpowered and unresponsive."))
|
|
return 0
|
|
if(!wearer || wearer.back != src)
|
|
if(user) to_chat(user, SPAN_WARNING("Your host rig is not being worn."))
|
|
return 0
|
|
if(!wearer.stat && !control_overridden && !ai_override_enabled)
|
|
if(user) to_chat(user, SPAN_WARNING("You are locked out of the suit servo controller."))
|
|
return 0
|
|
return 1
|
|
|
|
/obj/item/rig/proc/force_rest(var/mob/user)
|
|
if(!ai_can_move_suit(user, check_user_module = 1))
|
|
return
|
|
wearer.lay_down()
|
|
to_chat(user, SPAN_NOTICE("\The [wearer] is now [wearer.resting ? "resting" : "getting up"]."))
|
|
|
|
/obj/item/rig/proc/forced_move(var/direction, var/mob/user)
|
|
// Why is all this shit in client/Move()? Who knows?
|
|
if(world.time < wearer_move_delay)
|
|
return
|
|
|
|
if(!wearer || !wearer.loc || !ai_can_move_suit(user, check_user_module = 1))
|
|
return
|
|
|
|
if(!wearer.stat && !wearer.paralysis) // don't force move if our wearer is awake and capable of moving
|
|
return
|
|
|
|
//This is sota the goto stop mobs from moving var
|
|
if(wearer.transforming || !wearer.canmove)
|
|
return
|
|
|
|
if(!wearer.lastarea)
|
|
wearer.lastarea = get_area(wearer.loc)
|
|
|
|
if(!wearer.check_solid_ground())
|
|
var/allowmove = wearer.Allow_Spacemove(0)
|
|
if(!allowmove)
|
|
return 0
|
|
else if(allowmove == -1 && wearer.handle_spaceslipping()) //Check to see if we slipped
|
|
return 0
|
|
else
|
|
wearer.inertia_dir = 0 //If not then we can reset inertia and move
|
|
|
|
if(malfunctioning)
|
|
direction = pick(GLOB.cardinals)
|
|
|
|
// Inside an object, tell it we moved.
|
|
if(isobj(wearer.loc) || ismob(wearer.loc))
|
|
var/atom/O = wearer.loc
|
|
return O.relaymove(wearer, direction)
|
|
|
|
if(isturf(wearer.loc))
|
|
if(wearer.restrained())//Why being pulled while cuffed prevents you from moving
|
|
for(var/mob/M in range(wearer, 1))
|
|
if(M.pulling == wearer)
|
|
if(!M.restrained() && M.stat == 0 && M.canmove && wearer.Adjacent(M))
|
|
to_chat(user, SPAN_NOTICE("Your host is restrained! They can't move!"))
|
|
return 0
|
|
else
|
|
M.stop_pulling()
|
|
|
|
if(wearer.pinned.len)
|
|
to_chat(src, "<span class='notice'>Your host is pinned to a wall by [wearer.pinned[1]]</span>!")
|
|
return 0
|
|
|
|
// AIs are a bit slower than regular and ignore move intent.
|
|
wearer_move_delay = world.time + ai_controlled_move_delay
|
|
|
|
if(istype(wearer.buckled_to, /obj/vehicle))
|
|
//manually set move_delay for vehicles so we don't inherit any mob movement penalties
|
|
//specific vehicle move delays are set in code\modules\vehicles\vehicle.dm
|
|
wearer_move_delay = world.time
|
|
return wearer.buckled_to.relaymove(wearer, direction)
|
|
|
|
if(istype(wearer.machine, /obj/structure/machinery))
|
|
if(wearer.machine.relaymove(wearer, direction))
|
|
return
|
|
|
|
if(wearer.pulledby || wearer.buckled_to) // Wheelchair driving!
|
|
if(istype(wearer.loc, /turf/space))
|
|
return // No wheelchair driving in space
|
|
if(istype(wearer.pulledby, /obj/structure/bed/stool/chair/office/wheelchair))
|
|
return wearer.pulledby.relaymove(wearer, direction)
|
|
else if(istype(wearer.buckled_to, /obj/structure/bed/stool/chair/office/wheelchair))
|
|
if(ishuman(wearer.buckled_to))
|
|
var/obj/item/organ/external/l_hand = wearer.get_organ(BP_L_HAND)
|
|
var/obj/item/organ/external/r_hand = wearer.get_organ(BP_R_HAND)
|
|
if((!l_hand || (l_hand.status & ORGAN_DESTROYED)) && (!r_hand || (r_hand.status & ORGAN_DESTROYED)))
|
|
return // No hands to drive your chair? Tough luck!
|
|
wearer_move_delay += 2
|
|
return wearer.buckled_to.relaymove(wearer,direction)
|
|
|
|
cell.use(10)
|
|
wearer.Move(get_step(get_turf(wearer),direction),direction)
|
|
|
|
// This returns the rig if you are contained inside one, but not if you are wearing it
|
|
/atom/proc/get_rig()
|
|
if(loc)
|
|
return loc.get_rig()
|
|
return null
|
|
|
|
/obj/item/rig/get_rig()
|
|
return src
|
|
|
|
/mob/living/carbon/human/get_rig()
|
|
return back
|
|
|
|
//Used in random rig spawning for cargo
|
|
//Randomly deletes modules
|
|
/obj/item/rig/proc/lose_modules(var/probability)
|
|
for(var/obj/item/rig_module/module in installed_modules)
|
|
if (probability)
|
|
qdel(module)
|
|
|
|
|
|
//Fiddles with some wires to possibly make the suit malfunction a little.
|
|
/obj/item/rig/proc/misconfigure(var/probability)
|
|
if (prob(probability))
|
|
wires.emp_pulse()
|
|
if (prob(probability))
|
|
wires.emp_pulse()
|
|
|
|
//Drains, rigs or removes the cell
|
|
/obj/item/rig/proc/sabotage_cell()
|
|
if (!cell)
|
|
return
|
|
|
|
if (prob(50))
|
|
cell.charge = rand(0, cell.charge*0.5)
|
|
else if (prob(15))
|
|
cell.rigged = 1
|
|
else
|
|
cell = null
|
|
|
|
//Depletes or removes the airtank
|
|
/obj/item/rig/proc/sabotage_tank()
|
|
if (!air_supply)
|
|
return
|
|
|
|
if (prob(70))
|
|
air_supply.remove_air(air_supply.air_contents.total_moles)
|
|
else
|
|
air_supply = null
|
|
|
|
/obj/item/rig/equipped()
|
|
. = ..()
|
|
START_PROCESSING(SSprocessing, src)
|