You can't (un)equip garments on/from obscured inventory slots anymore.

This commit is contained in:
Ghommie
2020-06-28 01:10:22 +02:00
parent 07c7432f43
commit a34a7ba5d4
13 changed files with 153 additions and 141 deletions

View File

@@ -153,7 +153,7 @@
var/image/item_overlay = image(holding) var/image/item_overlay = image(holding)
item_overlay.alpha = 92 item_overlay.alpha = 92
if(!user.can_equip(holding, slot_id, TRUE)) if(!user.can_equip(holding, slot_id, TRUE, TRUE, TRUE))
item_overlay.color = "#FF0000" item_overlay.color = "#FF0000"
else else
item_overlay.color = "#00ff00" item_overlay.color = "#00ff00"

View File

@@ -496,11 +496,11 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
//if this is being done by a mob other than M, it will include the mob equipper, who is trying to equip the item to mob M. equipper will be null otherwise. //if this is being done by a mob other than M, it will include the mob equipper, who is trying to equip the item to mob M. equipper will be null otherwise.
//If you are making custom procs but would like to retain partial or complete functionality of this one, include a 'return ..()' to where you want this to happen. //If you are making custom procs but would like to retain partial or complete functionality of this one, include a 'return ..()' to where you want this to happen.
//Set disable_warning to TRUE if you wish it to not give you outputs. //Set disable_warning to TRUE if you wish it to not give you outputs.
/obj/item/proc/mob_can_equip(mob/living/M, mob/living/equipper, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE) /obj/item/proc/mob_can_equip(mob/living/M, mob/living/equipper, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, clothing_check = FALSE, list/return_warning)
if(!M) if(!M)
return FALSE return FALSE
return M.can_equip(src, slot, disable_warning, bypass_equip_delay_self) return M.can_equip(src, slot, disable_warning, bypass_equip_delay_self, clothing_check, return_warning)
/obj/item/verb/verb_pickup() /obj/item/verb/verb_pickup()
set src in oview(1) set src in oview(1)

View File

@@ -158,7 +158,7 @@
//Returns if a certain item can be equipped to a certain slot. //Returns if a certain item can be equipped to a certain slot.
// Currently invalid for two-handed items - call obj/item/mob_can_equip() instead. // Currently invalid for two-handed items - call obj/item/mob_can_equip() instead.
/mob/proc/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE) /mob/proc/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, clothing_check = FALSE, list/return_warning)
return FALSE return FALSE
/mob/proc/can_put_in_hand(I, hand_index) /mob/proc/can_put_in_hand(I, hand_index)
@@ -338,50 +338,63 @@
return FALSE return FALSE
return TRUE return TRUE
//This is a SAFE proc. Use this instead of equip_to_slot()!
//set qdel_on_fail to have it delete W if it fails to equip
//set disable_warning to disable the 'you are unable to equip that' warning.
//unset redraw_mob to prevent the mob from being redrawn at the end.
/mob/proc/equip_to_slot_if_possible(obj/item/W, slot, qdel_on_fail = FALSE, disable_warning = FALSE, redraw_mob = TRUE, bypass_equip_delay_self = FALSE, clothing_check = FALSE)
if(!istype(W))
return FALSE
var/list/warning = list("<span class='warning'>You are unable to equip that!</span>")
if(!W.mob_can_equip(src, null, slot, disable_warning, bypass_equip_delay_self, clothing_check, warning))
if(qdel_on_fail)
qdel(W)
else if(!disable_warning)
to_chat(src, warning[1])
return FALSE
equip_to_slot(W, slot, redraw_mob) //This proc should not ever fail.
return TRUE
//This is an UNSAFE proc. It merely handles the actual job of equipping. All the checks on whether you can or can't equip need to be done before! Use mob_can_equip() for that task.
//In most cases you will want to use equip_to_slot_if_possible()
/mob/proc/equip_to_slot(obj/item/W, slot)
return
//This is just a commonly used configuration for the equip_to_slot_if_possible() proc, used to equip people when the round starts and when events happen and such.
//Also bypasses equip delay checks, since the mob isn't actually putting it on.
/mob/proc/equip_to_slot_or_del(obj/item/W, slot)
return equip_to_slot_if_possible(W, slot, TRUE, TRUE, FALSE, TRUE)
//puts the item "W" into an appropriate slot in a human's inventory
//returns 0 if it cannot, 1 if successful
/mob/proc/equip_to_appropriate_slot(obj/item/W, clothing_check = FALSE)
if(!istype(W))
return 0
var/slot_priority = W.slot_equipment_priority
if(!slot_priority)
slot_priority = list( \
SLOT_BACK, SLOT_WEAR_ID,\
SLOT_W_UNIFORM, SLOT_WEAR_SUIT,\
SLOT_WEAR_MASK, SLOT_HEAD, SLOT_NECK,\
SLOT_SHOES, SLOT_GLOVES,\
SLOT_EARS, SLOT_GLASSES,\
SLOT_BELT, SLOT_S_STORE,\
SLOT_L_STORE, SLOT_R_STORE,\
SLOT_GENERC_DEXTROUS_STORAGE\
)
for(var/slot in slot_priority)
if(equip_to_slot_if_possible(W, slot, FALSE, TRUE, TRUE, FALSE, clothing_check)) //qdel_on_fail = 0; disable_warning = 1; redraw_mob = 1
return 1
return 0
//Outdated but still in use apparently. This should at least be a human proc. //Outdated but still in use apparently. This should at least be a human proc.
//Daily reminder to murder this - Remie. //Daily reminder to murder this - Remie.
/mob/living/proc/get_equipped_items(include_pockets = FALSE) /mob/living/proc/get_equipped_items(include_pockets = FALSE)
return return
/mob/living/carbon/get_equipped_items(include_pockets = FALSE)
var/list/items = list()
if(back)
items += back
if(head)
items += head
if(wear_mask)
items += wear_mask
if(wear_neck)
items += wear_neck
return items
/mob/living/carbon/human/get_equipped_items(include_pockets = FALSE)
var/list/items = ..()
if(belt)
items += belt
if(ears)
items += ears
if(glasses)
items += glasses
if(gloves)
items += gloves
if(shoes)
items += shoes
if(wear_id)
items += wear_id
if(wear_suit)
items += wear_suit
if(w_uniform)
items += w_uniform
if(include_pockets)
if(l_store)
items += l_store
if(r_store)
items += r_store
if(s_store)
items += s_store
return items
/mob/living/proc/unequip_everything() /mob/living/proc/unequip_everything()
var/list/items = list() var/list/items = list()
items |= get_equipped_items(TRUE) items |= get_equipped_items(TRUE)
@@ -394,7 +407,7 @@
to_chat(M, "<span class='warning'>You are not holding anything to equip!</span>") to_chat(M, "<span class='warning'>You are not holding anything to equip!</span>")
return FALSE return FALSE
if(M.equip_to_appropriate_slot(src)) if(M.equip_to_appropriate_slot(src, TRUE))
M.update_inv_hands() M.update_inv_hands()
return TRUE return TRUE
else else

View File

@@ -1160,3 +1160,16 @@
dna.features["body_model"] = MALE dna.features["body_model"] = MALE
if(update_icon) if(update_icon)
update_body() update_body()
/mob/living/carbon/check_obscured_slots()
if(head)
if(head.flags_inv & HIDEMASK)
LAZYOR(., SLOT_WEAR_MASK)
if(head.flags_inv & HIDEEYES)
LAZYOR(., SLOT_GLASSES)
if(head.flags_inv & HIDEEARS)
LAZYOR(., SLOT_EARS)
if(wear_mask)
if(wear_mask.flags_inv & HIDEEYES)
LAZYOR(., SLOT_GLASSES)

View File

@@ -511,33 +511,15 @@
// Might need re-wording. // Might need re-wording.
to_chat(user, "<span class='alert'>There is no exposed flesh or thin material [above_neck(target_zone) ? "on [p_their()] head" : "on [p_their()] body"].</span>") to_chat(user, "<span class='alert'>There is no exposed flesh or thin material [above_neck(target_zone) ? "on [p_their()] head" : "on [p_their()] body"].</span>")
/mob/living/carbon/human/proc/check_obscured_slots() /mob/living/carbon/human/check_obscured_slots()
var/list/obscured = list() . = ..()
if(wear_suit) if(wear_suit)
if(wear_suit.flags_inv & HIDEGLOVES) if(wear_suit.flags_inv & HIDEGLOVES)
obscured |= SLOT_GLOVES LAZYOR(., SLOT_GLOVES)
if(wear_suit.flags_inv & HIDEJUMPSUIT) if(wear_suit.flags_inv & HIDEJUMPSUIT)
obscured |= SLOT_W_UNIFORM LAZYOR(., SLOT_W_UNIFORM)
if(wear_suit.flags_inv & HIDESHOES) if(wear_suit.flags_inv & HIDESHOES)
obscured |= SLOT_SHOES LAZYOR(., SLOT_SHOES)
if(head)
if(head.flags_inv & HIDEMASK)
obscured |= SLOT_WEAR_MASK
if(head.flags_inv & HIDEEYES)
obscured |= SLOT_GLASSES
if(head.flags_inv & HIDEEARS)
obscured |= SLOT_EARS
if(wear_mask)
if(wear_mask.flags_inv & HIDEEYES)
obscured |= SLOT_GLASSES
if(obscured.len)
return obscured
else
return null
/mob/living/carbon/human/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null) /mob/living/carbon/human/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null)
if(judgement_criteria & JUDGE_EMAGGED) if(judgement_criteria & JUDGE_EMAGGED)

View File

@@ -1,5 +1,32 @@
/mob/living/carbon/human/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE) /mob/living/carbon/human/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, clothing_check = FALSE, list/return_warning)
return dna.species.can_equip(I, slot, disable_warning, src, bypass_equip_delay_self) return dna.species.can_equip(I, slot, disable_warning, src, bypass_equip_delay_self, clothing_check, return_warning)
/mob/living/carbon/human/get_equipped_items(include_pockets = FALSE)
var/list/items = ..()
if(belt)
items += belt
if(ears)
items += ears
if(glasses)
items += glasses
if(gloves)
items += gloves
if(shoes)
items += shoes
if(wear_id)
items += wear_id
if(wear_suit)
items += wear_suit
if(w_uniform)
items += w_uniform
if(include_pockets)
if(l_store)
items += l_store
if(r_store)
items += r_store
if(s_store)
items += s_store
return items
// Return the item currently in the slot ID // Return the item currently in the slot ID
/mob/living/carbon/human/get_item_by_slot(slot_id) /mob/living/carbon/human/get_item_by_slot(slot_id)

View File

@@ -1065,11 +1065,16 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
// handles the equipping of species-specific gear // handles the equipping of species-specific gear
return return
/datum/species/proc/can_equip(obj/item/I, slot, disable_warning, mob/living/carbon/human/H, bypass_equip_delay_self = FALSE) /datum/species/proc/can_equip(obj/item/I, slot, disable_warning, mob/living/carbon/human/H, bypass_equip_delay_self = FALSE, clothing_check = FALSE, list/return_warning)
if(slot in no_equip) if(slot in no_equip)
if(!I.species_exception || !is_type_in_list(src, I.species_exception)) if(!I.species_exception || !is_type_in_list(src, I.species_exception))
return FALSE return FALSE
if(clothing_check && (slot in H.check_obscured_slots()))
if(return_warning)
return_warning[1] = "<span class='warning'>You are unable to equip with your current garments in the way!</span>"
return FALSE
var/num_arms = H.get_num_arms(FALSE) var/num_arms = H.get_num_arms(FALSE)
var/num_legs = H.get_num_legs(FALSE) var/num_legs = H.get_num_legs(FALSE)
@@ -1131,8 +1136,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(!CHECK_BITFIELD(I.item_flags, NO_UNIFORM_REQUIRED)) if(!CHECK_BITFIELD(I.item_flags, NO_UNIFORM_REQUIRED))
var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_CHEST) var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_CHEST)
if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC)) if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC))
if(!disable_warning) if(return_warning)
to_chat(H, "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>") return_warning[1] = "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>"
return FALSE return FALSE
if(!(I.slot_flags & ITEM_SLOT_BELT)) if(!(I.slot_flags & ITEM_SLOT_BELT))
return return
@@ -1173,8 +1178,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(!CHECK_BITFIELD(I.item_flags, NO_UNIFORM_REQUIRED)) if(!CHECK_BITFIELD(I.item_flags, NO_UNIFORM_REQUIRED))
var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_CHEST) var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_CHEST)
if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC)) if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC))
if(!disable_warning) if(return_warning)
to_chat(H, "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>") return_warning[1] = "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>"
return FALSE return FALSE
if( !(I.slot_flags & ITEM_SLOT_ID) ) if( !(I.slot_flags & ITEM_SLOT_ID) )
return FALSE return FALSE
@@ -1188,8 +1193,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_L_LEG) var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_L_LEG)
if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC)) if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC))
if(!disable_warning) if(return_warning)
to_chat(H, "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>") return_warning[1] = "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>"
return FALSE return FALSE
if(I.slot_flags & ITEM_SLOT_DENYPOCKET) if(I.slot_flags & ITEM_SLOT_DENYPOCKET)
return FALSE return FALSE
@@ -1204,8 +1209,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_R_LEG) var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_R_LEG)
if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC)) if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC))
if(!disable_warning) if(return_warning)
to_chat(H, "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>") return_warning[1] = "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>"
return FALSE return FALSE
if(I.slot_flags & ITEM_SLOT_DENYPOCKET) if(I.slot_flags & ITEM_SLOT_DENYPOCKET)
return FALSE return FALSE
@@ -1218,16 +1223,16 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(H.s_store) if(H.s_store)
return FALSE return FALSE
if(!H.wear_suit) if(!H.wear_suit)
if(!disable_warning) if(return_warning)
to_chat(H, "<span class='warning'>You need a suit before you can attach this [I.name]!</span>") return_warning[1] = "<span class='warning'>You need a suit before you can attach this [I.name]!</span>"
return FALSE return FALSE
if(!H.wear_suit.allowed) if(!H.wear_suit.allowed)
if(!disable_warning) if(return_warning)
to_chat(H, "You somehow have a suit with no defined allowed items for suit storage, stop that.") return_warning[1] = "You somehow have a suit with no defined allowed items for suit storage, stop that."
return FALSE return FALSE
if(I.w_class > WEIGHT_CLASS_BULKY) if(I.w_class > WEIGHT_CLASS_BULKY)
if(!disable_warning) if(return_warning)
to_chat(H, "The [I.name] is too big to attach.") //should be src? return_warning[1] = "The [I.name] is too big to attach."
return FALSE return FALSE
if( istype(I, /obj/item/pda) || istype(I, /obj/item/pen) || is_type_in_list(I, H.wear_suit.allowed) ) if( istype(I, /obj/item/pda) || istype(I, /obj/item/pen) || is_type_in_list(I, H.wear_suit.allowed) )
return TRUE return TRUE

View File

@@ -117,6 +117,18 @@
if(!QDELETED(src)) if(!QDELETED(src))
update_inv_legcuffed() update_inv_legcuffed()
/mob/living/carbon/get_equipped_items(include_pockets = FALSE)
var/list/items = list()
if(back)
items += back
if(head)
items += head
if(wear_mask)
items += wear_mask
if(wear_neck)
items += wear_neck
return items
//handle stuff to update when a mob equips/unequips a mask. //handle stuff to update when a mob equips/unequips a mask.
/mob/living/proc/wear_mask_update(obj/item/clothing/C, toggle_off = 1) /mob/living/proc/wear_mask_update(obj/item/clothing/C, toggle_off = 1)
update_inv_wear_mask() update_inv_wear_mask()

View File

@@ -81,7 +81,7 @@
/mob/living/carbon/monkey/proc/pickup_and_wear(obj/item/I) /mob/living/carbon/monkey/proc/pickup_and_wear(obj/item/I)
if(QDELETED(I) || I.loc != src) if(QDELETED(I) || I.loc != src)
return return
equip_to_appropriate_slot(I) equip_to_appropriate_slot(I, TRUE)
/mob/living/carbon/monkey/resist_restraints() /mob/living/carbon/monkey/resist_restraints()
var/obj/item/I = null var/obj/item/I = null

View File

@@ -1,4 +1,9 @@
/mob/living/carbon/monkey/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE) /mob/living/carbon/monkey/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, clothing_check = FALSE, list/return_warning)
if(clothing_check && (slot in check_obscured_slots()))
if(return_warning)
return_warning[1] = "<span class='warning'>You are unable to equip with your current garments in the way!</span>"
return FALSE
switch(slot) switch(slot)
if(SLOT_HANDS) if(SLOT_HANDS)
if(get_empty_held_indexes()) if(get_empty_held_indexes())

View File

@@ -19,7 +19,7 @@
return 0 return 0
/mob/living/simple_animal/drone/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE) /mob/living/simple_animal/drone/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, clothing_check = FALSE, list/return_warning)
switch(slot) switch(slot)
if(SLOT_HEAD) if(SLOT_HEAD)
if(head) if(head)

View File

@@ -50,7 +50,7 @@
return 1 return 1
return 0 return 0
/mob/living/simple_animal/hostile/guardian/dextrous/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE) /mob/living/simple_animal/hostile/guardian/dextrous/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, clothing_check = FALSE, list/return_warning)
switch(slot) switch(slot)
if(SLOT_GENERC_DEXTROUS_STORAGE) if(SLOT_GENERC_DEXTROUS_STORAGE)
if(internal_storage) if(internal_storage)

View File

@@ -227,69 +227,24 @@ mob/visible_message(message, self_message, blind_message, vision_distance = DEFA
var/obj/item/W = get_active_held_item() var/obj/item/W = get_active_held_item()
if(istype(W)) if(istype(W))
if(equip_to_slot_if_possible(W, slot,0,0,0)) if(equip_to_slot_if_possible(W, slot, FALSE, FALSE, FALSE, TRUE))
return 1 return TRUE
if(!W) if(!W)
// Activate the item // Activate the item
var/obj/item/I = get_item_by_slot(slot) var/obj/item/I = get_item_by_slot(slot)
if(istype(I)) if(istype(I))
if(slot in check_obscured_slots())
to_chat(src, "<span class='warning'>You are unable to unequip while wearing other garments over it!</span>")
return FALSE
I.attack_hand(src) I.attack_hand(src)
return 0 return FALSE
//This is a SAFE proc. Use this instead of equip_to_slot()! /// Checks for slots that are currently obscured by other garments.
//set qdel_on_fail to have it delete W if it fails to equip /mob/proc/check_obscured_slots()
//set disable_warning to disable the 'you are unable to equip that' warning.
//unset redraw_mob to prevent the mob from being redrawn at the end.
/mob/proc/equip_to_slot_if_possible(obj/item/W, slot, qdel_on_fail = FALSE, disable_warning = FALSE, redraw_mob = TRUE, bypass_equip_delay_self = FALSE)
if(!istype(W))
return FALSE
if(!W.mob_can_equip(src, null, slot, disable_warning, bypass_equip_delay_self))
if(qdel_on_fail)
qdel(W)
else
if(!disable_warning)
to_chat(src, "<span class='warning'>You are unable to equip that!</span>")
return FALSE
equip_to_slot(W, slot, redraw_mob) //This proc should not ever fail.
return TRUE
//This is an UNSAFE proc. It merely handles the actual job of equipping. All the checks on whether you can or can't equip need to be done before! Use mob_can_equip() for that task.
//In most cases you will want to use equip_to_slot_if_possible()
/mob/proc/equip_to_slot(obj/item/W, slot)
return return
//This is just a commonly used configuration for the equip_to_slot_if_possible() proc, used to equip people when the round starts and when events happen and such.
//Also bypasses equip delay checks, since the mob isn't actually putting it on.
/mob/proc/equip_to_slot_or_del(obj/item/W, slot)
return equip_to_slot_if_possible(W, slot, TRUE, TRUE, FALSE, TRUE)
//puts the item "W" into an appropriate slot in a human's inventory
//returns 0 if it cannot, 1 if successful
/mob/proc/equip_to_appropriate_slot(obj/item/W)
if(!istype(W))
return 0
var/slot_priority = W.slot_equipment_priority
if(!slot_priority)
slot_priority = list( \
SLOT_BACK, SLOT_WEAR_ID,\
SLOT_W_UNIFORM, SLOT_WEAR_SUIT,\
SLOT_WEAR_MASK, SLOT_HEAD, SLOT_NECK,\
SLOT_SHOES, SLOT_GLOVES,\
SLOT_EARS, SLOT_GLASSES,\
SLOT_BELT, SLOT_S_STORE,\
SLOT_L_STORE, SLOT_R_STORE,\
SLOT_GENERC_DEXTROUS_STORAGE\
)
for(var/slot in slot_priority)
if(equip_to_slot_if_possible(W, slot, 0, 1, 1)) //qdel_on_fail = 0; disable_warning = 1; redraw_mob = 1
return 1
return 0
// reset_perspective(thing) set the eye to the thing (if it's equal to current default reset to mob perspective) // reset_perspective(thing) set the eye to the thing (if it's equal to current default reset to mob perspective)
// reset_perspective() set eye to common default : mob on turf, loc otherwise // reset_perspective() set eye to common default : mob on turf, loc otherwise
/mob/proc/reset_perspective(atom/A) /mob/proc/reset_perspective(atom/A)