diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index c9fe06d47c..2164dbe5dd 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -84,12 +84,10 @@ RestrainedClickOn(A) return 1 - if(in_throw_mode) - if(isturf(A) || isturf(A.loc)) - throw_item(A) - trigger_aiming(TARGET_CAN_CLICK) - return 1 + if(in_throw_mode && (isturf(A) || isturf(A.loc)) && throw_item(A)) + trigger_aiming(TARGET_CAN_CLICK) throw_mode_off() + return TRUE var/obj/item/W = get_active_hand() @@ -286,7 +284,7 @@ if(T && user.TurfAdjacent(T)) user.ToggleTurfTab(T) return 1 - + /mob/proc/ToggleTurfTab(var/turf/T) if(listed_turf == T) listed_turf = null diff --git a/code/modules/mob/living/carbon/give.dm b/code/modules/mob/living/carbon/give.dm index c361959976..0d3edc90c8 100644 --- a/code/modules/mob/living/carbon/give.dm +++ b/code/modules/mob/living/carbon/give.dm @@ -1,42 +1,46 @@ -/mob/living/carbon/human/verb/give(var/mob/living/carbon/target in living_mobs(1)) +/mob/living/verb/give(var/mob/living/target in living_mobs(1)) set category = "IC" set name = "Give" - // TODO : Change to incapacitated() on merge. - if(src.stat || src.lying || src.resting || src.handcuffed) + do_give(target) + +/mob/living/proc/do_give(var/mob/living/carbon/human/target) + + if(src.incapacitated()) return - if(!istype(target) || target.stat || target.lying || target.resting || target.handcuffed || target.client == null) + if(!istype(target) || target.incapacitated() || target.client == null) return var/obj/item/I = src.get_active_hand() if(!I) I = src.get_inactive_hand() if(!I) - to_chat(src, "You don't have anything in your hands to give to \the [target].") + to_chat(src, SPAN_WARNING("You don't have anything in your hands to give to \the [target].")) return - if(tgui_alert(target,"[src] wants to give you \a [I]. Will you accept it?","Item Offer",list("Yes","No")) == "No") //VOREStation Edit - make yes on the left to be consistent with other dialogs - target.visible_message("\The [src] tried to hand \the [I] to \the [target], \ - but \the [target] didn't want it.") + usr.visible_message(SPAN_NOTICE("\The [usr] holds out \the [I] to \the [target]."), SPAN_NOTICE("You hold out \the [I] to \the [target], waiting for them to accept it.")) + + if(tgui_alert(target,"[src] wants to give you \a [I]. Will you accept it?","Item Offer",list("No","Yes")) == "No") + target.visible_message(SPAN_NOTICE("\The [src] tried to hand \the [I] to \the [target], but \the [target] didn't want it.")) return if(!I) return if(!Adjacent(target)) - to_chat(src, "You need to stay in reaching distance while giving an object.") - to_chat(target, "\The [src] moved too far away.") + to_chat(src, SPAN_WARNING("You need to stay in reaching distance while giving an object")) + to_chat(target, SPAN_WARNING("\The [src] moved too far away.")) return if(I.loc != src || !src.item_is_in_hands(I)) - to_chat(src, "You need to keep the item in your hands.") - to_chat(target, "\The [src] seems to have given up on passing \the [I] to you.") + to_chat(src, SPAN_WARNING("You need to keep the item in your hands.")) + to_chat(target, SPAN_WARNING("\The [src] seems to have given up on passing \the [I] to you.")) return if(target.hands_are_full()) - to_chat(target, "Your hands are full.") - to_chat(src, "Their hands are full.") + to_chat(target, SPAN_WARNING("Your hands are full.")) + to_chat(src, SPAN_WARNING("Their hands are full.")) return if(src.unEquip(I)) target.put_in_hands(I) // If this fails it will just end up on the floor, but that's fitting for things like dionaea. - target.visible_message("\The [src] handed \the [I] to \the [target].") + target.visible_message(SPAN_NOTICE("\The [src] handed \the [I] to \the [target]")) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 5b2d45a29e..90a1d32170 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1004,14 +1004,13 @@ swap_hand() /mob/living/throw_item(atom/target) - src.throw_mode_off() - if(usr.stat || !target) - return - if(target.type == /obj/screen) return + if(incapacitated() || !target || istype(target, /obj/screen)) + return FALSE var/atom/movable/item = src.get_active_hand() - if(!item) return + if(!item) + return FALSE var/throw_range = item.throw_range if (istype(item, /obj/item/weapon/grab)) @@ -1031,10 +1030,32 @@ if((N.health + N.halloss) < config.health_threshold_crit || N.stat == DEAD) N.adjustBruteLoss(rand(10,30)) src.drop_from_inventory(G) + return TRUE + else + return FALSE - src.drop_from_inventory(item) - if(!item || !isturf(item.loc)) - return + if(!item) + return FALSE //Grab processing has a chance of returning null + + if(a_intent == I_HELP && Adjacent(target) && isitem(item)) + var/obj/item/I = item + if(ishuman(target)) + var/mob/living/carbon/human/H = target + if(H.in_throw_mode && H.a_intent == I_HELP && unEquip(I)) + H.put_in_hands(I) // If this fails it will just end up on the floor, but that's fitting for things like dionaea. + visible_message("[src] hands \the [H] \a [I].", SPAN_NOTICE("You give \the [target] \a [I].")) + else + to_chat(src, SPAN_NOTICE("You offer \the [I] to \the [target].")) + do_give(H) + return TRUE + make_item_drop_sound(I) + I.forceMove(get_turf(target)) + return TRUE + + drop_from_inventory(item) + + if(!item || QDELETED(item)) + return TRUE //It may not have thrown, but it sure as hell left your hand successfully. //actually throw it! src.visible_message("[src] has thrown [item].") @@ -1055,6 +1076,7 @@ item.throw_at(target, throw_range, item.throw_speed, src) + return TRUE /mob/living/get_sound_env(var/pressure_factor) if (hallucination) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index be223341b8..1c607f824c 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1,3 +1,4 @@ +<<<<<<< HEAD /mob/Destroy()//This makes sure that mobs withGLOB.clients/keys are not just deleted from the game. mob_list -= src dead_mob_list -= src @@ -1228,3 +1229,2466 @@ GLOBAL_LIST_EMPTY_TYPED(living_players_by_zlevel, /list) /mob/proc/grab_ghost(force) if(mind) return mind.grab_ghost(force = force) +||||||| parent of f1417f9455... Merge pull request #11560 from VOREStation/upstream-merge-8247 +/mob/Destroy()//This makes sure that mobs withGLOB.clients/keys are not just deleted from the game. + mob_list -= src + dead_mob_list -= src + living_mob_list -= src + unset_machine() + qdel(hud_used) + clear_fullscreen() + if(client) + for(var/obj/screen/movable/spell_master/spell_master in spell_masters) + qdel(spell_master) + remove_screen_obj_references() + client.screen = list() + if(mind && mind.current == src) + spellremove(src) + ghostize() + QDEL_NULL(plane_holder) + ..() + return QDEL_HINT_HARDDEL_NOW + +/mob/proc/remove_screen_obj_references() + hands = null + pullin = null + purged = null + internals = null + i_select = null + m_select = null + healths = null + throw_icon = null + pain = null + item_use_icon = null + gun_move_icon = null + gun_setting_icon = null + spell_masters = null + zone_sel = null + +/mob/Initialize() + mob_list += src + if(stat == DEAD) + dead_mob_list += src + else + living_mob_list += src + lastarea = get_area(src) + set_focus(src) // VOREStation Add - Key Handling + hook_vr("mob_new",list(src)) //VOREStation Code + update_transform() // Some mobs may start bigger or smaller than normal. + return ..() + +/mob/proc/show_message(msg, type, alt, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2) + + if(!client && !teleop) return + + if (type) + if((type & VISIBLE_MESSAGE) && (is_blind() || paralysis) )//Vision related + if (!( alt )) + return + else + msg = alt + type = alt_type + if ((type & AUDIBLE_MESSAGE) && is_deaf())//Hearing related + if (!( alt )) + return + else + msg = alt + type = alt_type + if ((type & VISIBLE_MESSAGE) && (sdisabilities & BLIND)) + return + // Added voice muffling for Issue 41. + if(stat == UNCONSCIOUS || sleeping > 0) + to_chat(src, "... You can almost hear someone talking ...") + else + to_chat(src,msg) + if(teleop) + to_chat(teleop, create_text_tag("body", "BODY:", teleop) + "[msg]") + return + +// Show a message to all mobs and objects in sight of this one +// This would be for visible actions by the src mob +// message is the message output to anyone who can see e.g. "[src] does something!" +// self_message (optional) is what the src mob sees e.g. "You do something!" +// blind_message (optional) is what blind people will hear e.g. "You hear something!" +/mob/visible_message(var/message, var/self_message, var/blind_message, var/list/exclude_mobs = null, var/range = world.view, var/runemessage) + if(self_message) + if(LAZYLEN(exclude_mobs)) + exclude_mobs |= src + else + exclude_mobs = list(src) + src.show_message(self_message, 1, blind_message, 2) + if(isnull(runemessage)) + runemessage = -1 + . = ..(message, blind_message, exclude_mobs, range, runemessage) // Really not ideal that atom/visible_message has different arg numbering :( + +// Returns an amount of power drawn from the object (-1 if it's not viable). +// If drain_check is set it will not actually drain power, just return a value. +// If surge is set, it will destroy/damage the recipient and not return any power. +// Not sure where to define this, so it can sit here for the rest of time. +/atom/proc/drain_power(var/drain_check,var/surge, var/amount = 0) + return -1 + +// Show a message to all mobs and objects in earshot of this one +// This would be for audible actions by the src mob +// message is the message output to anyone who can hear. +// self_message (optional) is what the src mob hears. +// deaf_message (optional) is what deaf people will see. +// hearing_distance (optional) is the range, how many tiles away the message can be heard. +/mob/audible_message(var/message, var/deaf_message, var/hearing_distance, var/self_message, var/radio_message, var/runemessage) + + var/range = hearing_distance || world.view + var/list/hear = get_mobs_and_objs_in_view_fast(get_turf(src),range,remote_ghosts = FALSE) + + var/list/hearing_mobs = hear["mobs"] + var/list/hearing_objs = hear["objs"] + + if(isnull(runemessage)) + runemessage = -1 // Symmetry with mob/audible_message, despite the fact this one doesn't call parent. Maybe it should! + + if(radio_message) + for(var/obj/O as anything in hearing_objs) + O.hear_talk(src, list(new /datum/multilingual_say_piece(GLOB.all_languages["Noise"], radio_message)), null) + else + for(var/obj/O as anything in hearing_objs) + O.show_message(message, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE) + + for(var/mob/M as anything in hearing_mobs) + var/msg = message + if(self_message && M==src) + msg = self_message + M.show_message(msg, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE) + if(runemessage != -1) + M.create_chat_message(src, "[runemessage || message]", FALSE, list("emote"), audible = FALSE) + +/mob/proc/findname(msg) + for(var/mob/M in mob_list) + if (M.real_name == text("[]", msg)) + return M + return 0 + +/mob/proc/Life() +// if(organStructure) +// organStructure.ProcessOrgans() + return + +#define UNBUCKLED 0 +#define PARTIALLY_BUCKLED 1 +#define FULLY_BUCKLED 2 +/mob/proc/buckled() + // Preliminary work for a future buckle rewrite, + // where one might be fully restrained (like an elecrical chair), or merely secured (shuttle chair, keeping you safe but not otherwise restrained from acting) + if(!buckled) + return UNBUCKLED + return restrained() ? FULLY_BUCKLED : PARTIALLY_BUCKLED + +/mob/proc/is_blind() + return ((sdisabilities & BLIND) || blinded || incapacitated(INCAPACITATION_KNOCKOUT)) + +/mob/proc/is_deaf() + return ((sdisabilities & DEAF) || ear_deaf || incapacitated(INCAPACITATION_KNOCKOUT)) + +/mob/proc/is_physically_disabled() + return incapacitated(INCAPACITATION_DISABLED) + +/mob/proc/cannot_stand() + return incapacitated(INCAPACITATION_KNOCKDOWN) + +/mob/proc/incapacitated(var/incapacitation_flags = INCAPACITATION_DEFAULT) + if ((incapacitation_flags & INCAPACITATION_STUNNED) && stunned) + return 1 + + if ((incapacitation_flags & INCAPACITATION_FORCELYING) && (weakened || resting)) + return 1 + + if ((incapacitation_flags & INCAPACITATION_KNOCKOUT) && (stat || paralysis || sleeping || (status_flags & FAKEDEATH))) + return 1 + + if((incapacitation_flags & INCAPACITATION_RESTRAINED) && restrained()) + return 1 + + if((incapacitation_flags & (INCAPACITATION_BUCKLED_PARTIALLY|INCAPACITATION_BUCKLED_FULLY))) + var/buckling = buckled() + if(buckling >= PARTIALLY_BUCKLED && (incapacitation_flags & INCAPACITATION_BUCKLED_PARTIALLY)) + return 1 + if(buckling == FULLY_BUCKLED && (incapacitation_flags & INCAPACITATION_BUCKLED_FULLY)) + return 1 + + return 0 + +#undef UNBUCKLED +#undef PARTIALLY_BUCKLED +#undef FULLY_BUCKLED + +/mob/proc/restrained() + return + +/mob/proc/reset_view(atom/A) + if (client) + if (istype(A, /atom/movable)) + client.perspective = EYE_PERSPECTIVE + client.eye = A + else + if (isturf(loc)) + client.eye = client.mob + client.perspective = MOB_PERSPECTIVE + else + client.perspective = EYE_PERSPECTIVE + client.eye = loc + return TRUE + +/mob/verb/pointed(atom/A as mob|obj|turf in view()) + set name = "Point To" + set category = "Object" + + if(!src || !isturf(src.loc) || !(A in view(src.loc))) + return 0 + if(istype(A, /obj/effect/decal/point)) + return 0 + + var/turf/tile = get_turf(A) + if (!tile) + return 0 + + var/turf/our_tile = get_turf(src) + var/obj/visual = new /obj/effect/decal/point(our_tile) + visual.invisibility = invisibility + visual.plane = ABOVE_PLANE + visual.layer = FLY_LAYER + + animate(visual, + pixel_x = (tile.x - our_tile.x) * world.icon_size + A.pixel_x, + pixel_y = (tile.y - our_tile.y) * world.icon_size + A.pixel_y, + time = 1.7, + easing = EASE_OUT) + + QDEL_IN(visual, 2 SECONDS) //Better qdel + + face_atom(A) + return 1 + + +/mob/proc/ret_grab(list/L, flag) + return + +/mob/verb/mode() + set name = "Activate Held Object" + set category = "Object" + set src = usr + + return + +/* +/mob/verb/dump_source() + + var/master = "
"
+ for(var/t in typesof(/area))
+ master += text("[]\n", t)
+ //Foreach goto(26)
+ src << browse(master)
+ return
+*/
+
+/mob/verb/memory()
+ set name = "Notes"
+ set category = "IC"
+ if(mind)
+ mind.show_memory(src)
+ else
+ to_chat(src, "The game appears to have misplaced your mind datum, so we can't show you your notes.")
+
+/mob/verb/add_memory(msg as message)
+ set name = "Add Note"
+ set category = "IC"
+
+ msg = sanitize(msg)
+
+ if(mind)
+ mind.store_memory(msg)
+ else
+ to_chat(src, "The game appears to have misplaced your mind datum, so we can't show you your notes.")
+
+/mob/proc/store_memory(msg as message, popup, sane = 1)
+ msg = copytext(msg, 1, MAX_MESSAGE_LEN)
+
+ if (sane)
+ msg = sanitize(msg)
+
+ if (length(memory) == 0)
+ memory += msg
+ else
+ memory += "
[msg]"
+
+ if (popup)
+ memory()
+
+/mob/proc/update_flavor_text()
+ set src in usr
+ if(usr != src)
+ to_chat(usr, "No.")
+ var/msg = sanitize(input(usr,"Set the flavor text in your 'examine' verb.","Flavor Text",html_decode(flavor_text)) as message|null, extra = 0) //VOREStation Edit: separating out OOC notes
+
+ if(msg != null)
+ flavor_text = msg
+
+/mob/proc/warn_flavor_changed()
+ if(flavor_text && flavor_text != "") // don't spam people that don't use it!
+ to_chat(src, "OOC Warning:
")
+ to_chat(src, "Your flavor text is likely out of date! Change")
+
+/mob/proc/print_flavor_text()
+ if (flavor_text && flavor_text != "")
+ var/msg = replacetext(flavor_text, "\n", " ")
+ if(length(msg) <= 40)
+ return "[msg]"
+ else
+ return "[copytext_preserve_html(msg, 1, 37)]... More..."
+
+/*
+/mob/verb/help()
+ set name = "Help"
+ src << browse('html/help.html', "window=help")
+ return
+*/
+
+/mob/proc/set_respawn_timer(var/time)
+ // Try to figure out what time to use
+
+ // Special cases, can never respawn
+ if(ticker?.mode?.deny_respawn)
+ time = -1
+ else if(!config.abandon_allowed)
+ time = -1
+ else if(!config.respawn)
+ time = -1
+
+ // Special case for observing before game start
+ else if(ticker?.current_state <= GAME_STATE_SETTING_UP)
+ time = 1 MINUTE
+
+ // Wasn't given a time, use the config time
+ else if(!time)
+ time = config.respawn_time
+
+ var/keytouse = ckey
+ // Try harder to find a key to use
+ if(!keytouse && key)
+ keytouse = ckey(key)
+ else if(!keytouse && mind?.key)
+ keytouse = ckey(mind.key)
+
+ GLOB.respawn_timers[keytouse] = world.time + time
+
+/mob/observer/dead/set_respawn_timer()
+ if(config.antag_hud_restricted && has_enabled_antagHUD)
+ ..(-1)
+ else
+ return // Don't set it, no need
+
+/mob/verb/abandon_mob()
+ set name = "Return to Menu"
+ set category = "OOC"
+
+ if(stat != DEAD || !ticker)
+ to_chat(usr, "You must be dead to use this!")
+ return
+
+ // Final chance to abort "respawning"
+ if(mind && timeofdeath) // They had spawned before
+ var/choice = tgui_alert(usr, "Returning to the menu will prevent your character from being revived in-round. Are you sure?", "Confirmation", list("No, wait", "Yes, leave"))
+ if(choice == "No, wait")
+ return
+
+ // Beyond this point, you're going to respawn
+ to_chat(usr, config.respawn_message)
+
+ if(!client)
+ log_game("[usr.key] AM failed due to disconnect.")
+ return
+ client.screen.Cut()
+ client.screen += client.void
+ if(!client)
+ log_game("[usr.key] AM failed due to disconnect.")
+ return
+
+ announce_ghost_joinleave(client, 0)
+
+ var/mob/new_player/M = new /mob/new_player()
+ if(!client)
+ log_game("[usr.key] AM failed due to disconnect.")
+ qdel(M)
+ return
+
+ M.key = key
+ if(M.mind)
+ M.mind.reset()
+ return
+
+/client/verb/changes()
+ set name = "Changelog"
+ set category = "OOC"
+ src << browse('html/changelog.html', "window=changes;size=675x650")
+ if(prefs.lastchangelog != changelog_hash)
+ prefs.lastchangelog = changelog_hash
+ SScharacter_setup.queue_preferences_save(prefs)
+ winset(src, "rpane.changelog", "background-color=none;font-style=;")
+
+/mob/verb/observe()
+ set name = "Observe"
+ set category = "OOC"
+ var/is_admin = 0
+
+ if(client.holder && (client.holder.rights & R_ADMIN|R_EVENT))
+ is_admin = 1
+ else if(stat != DEAD || istype(src, /mob/new_player))
+ to_chat(usr, "You must be observing to use this!")
+ return
+
+ if(is_admin && stat == DEAD)
+ is_admin = 0
+
+ var/list/targets = list()
+
+
+ targets += observe_list_format(nuke_disks)
+ targets += observe_list_format(all_singularities)
+ targets += getmobs()
+ targets += observe_list_format(sortAtom(mechas_list))
+ targets += observe_list_format(SSshuttles.ships)
+
+ client.perspective = EYE_PERSPECTIVE
+
+ var/eye_name = null
+
+ var/ok = "[is_admin ? "Admin Observe" : "Observe"]"
+ eye_name = tgui_input_list(usr, "Select something to [ok]:", "Select Target", targets)
+
+ if (!eye_name)
+ return
+
+ var/mob/mob_eye = targets[eye_name]
+
+ if(client && mob_eye)
+ client.eye = mob_eye
+ if (is_admin)
+ client.adminobs = 1
+ if(mob_eye == client.mob || client.eye == client.mob)
+ client.adminobs = 0
+
+/mob/verb/cancel_camera()
+ set name = "Cancel Camera View"
+ set category = "OOC"
+ unset_machine()
+ reset_view(null)
+
+/mob/Topic(href, href_list)
+ if(href_list["mach_close"])
+ var/t1 = text("window=[href_list["mach_close"]]")
+ unset_machine()
+ src << browse(null, t1)
+
+ if(href_list["flavor_more"])
+ usr << browse(text("[] []", name, replacetext(flavor_text, "\n", "
")), text("window=[];size=500x200", name))
+ onclose(usr, "[name]")
+ if(href_list["flavor_change"])
+ update_flavor_text()
+// ..()
+ return
+
+
+/mob/proc/pull_damage()
+ return 0
+
+/mob/verb/stop_pulling()
+
+ set name = "Stop Pulling"
+ set category = "IC"
+
+ if(pulling)
+ if(ishuman(pulling))
+ var/mob/living/carbon/human/H = pulling
+ visible_message(SPAN_WARNING("\The [src] lets go of \the [H]."), SPAN_NOTICE("You let go of \the [H]."), exclude_mobs = list(H))
+ if(!H.stat)
+ to_chat(H, SPAN_WARNING("\The [src] lets go of you."))
+ pulling.pulledby = null
+ pulling = null
+ if(pullin)
+ pullin.icon_state = "pull0"
+
+/mob/proc/start_pulling(var/atom/movable/AM)
+
+ if ( !AM || !usr || src==AM || !isturf(src.loc) ) //if there's no person pulling OR the person is pulling themself OR the object being pulled is inside something: abort!
+ return
+
+ if (AM.anchored)
+ to_chat(src, "It won't budge!")
+ return
+
+ var/mob/M = AM
+ if(ismob(AM))
+
+ if(!can_pull_mobs || !can_pull_size)
+ to_chat(src, "They won't budge!")
+ return
+
+ if((mob_size < M.mob_size) && (can_pull_mobs != MOB_PULL_LARGER))
+ to_chat(src, "[M] is too large for you to move!")
+ return
+
+ if((mob_size == M.mob_size) && (can_pull_mobs == MOB_PULL_SMALLER))
+ to_chat(src, "[M] is too heavy for you to move!")
+ return
+
+ // If your size is larger than theirs and you have some
+ // kind of mob pull value AT ALL, you will be able to pull
+ // them, so don't bother checking that explicitly.
+
+ if(M.grabbed_by.len)
+ // Only start pulling when nobody else has a grab on them
+ . = 1
+ for(var/obj/item/weapon/grab/G in M.grabbed_by)
+ if(G.assailant != usr)
+ . = 0
+ else
+ qdel(G)
+ if(!.)
+ to_chat(src, "Somebody has a grip on them!")
+ return
+
+ if(!iscarbon(src))
+ M.LAssailant = null
+ else
+ M.LAssailant = usr
+
+ else if(isobj(AM))
+ var/obj/I = AM
+ if(!can_pull_size || can_pull_size < I.w_class)
+ to_chat(src, "It won't budge!")
+ return
+
+ if(pulling)
+ var/pulling_old = pulling
+ stop_pulling()
+ // Are we pulling the same thing twice? Just stop pulling.
+ if(pulling_old == AM)
+ return
+
+ src.pulling = AM
+ AM.pulledby = src
+
+ if(pullin)
+ pullin.icon_state = "pull1"
+
+ if(ishuman(AM))
+ var/mob/living/carbon/human/H = AM
+ if(H.lying) // If they're on the ground we're probably dragging their arms to move them
+ visible_message(SPAN_WARNING("\The [src] leans down and grips \the [H]'s arms."), SPAN_NOTICE("You lean down and grip \the [H]'s arms."), exclude_mobs = list(H))
+ if(!H.stat)
+ to_chat(H, SPAN_WARNING("\The [src] leans down and grips your arms."))
+ else //Otherwise we're probably just holding their arm to lead them somewhere
+ visible_message(SPAN_WARNING("\The [src] grips \the [H]'s arm."), SPAN_NOTICE("You grip \the [H]'s arm."), exclude_mobs = list(H))
+ if(!H.stat)
+ to_chat(H, SPAN_WARNING("\The [src] grips your arm."))
+ playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 25) //Quieter than hugging/grabbing but we still want some audio feedback
+
+ if(H.pull_damage())
+ to_chat(src, "Pulling \the [H] in their current condition would probably be a bad idea.")
+
+ //Attempted fix for people flying away through space when cuffed and dragged.
+ if(ismob(AM))
+ var/mob/pulled = AM
+ pulled.inertia_dir = 0
+
+/mob/proc/can_use_hands()
+ return
+
+/mob/proc/is_active()
+ return (0 >= usr.stat)
+
+/mob/proc/is_dead()
+ return stat == DEAD
+
+/mob/proc/is_mechanical()
+ if(mind && (mind.assigned_role == "Cyborg" || mind.assigned_role == "AI"))
+ return 1
+ return istype(src, /mob/living/silicon) || get_species() == "Machine"
+
+/mob/proc/is_ready()
+ return client && !!mind
+
+/mob/proc/get_gender()
+ return gender
+
+/mob/proc/see(message)
+ if(!is_active())
+ return 0
+ to_chat(src,message)
+ return 1
+
+/mob/proc/show_viewers(message)
+ for(var/mob/M in viewers())
+ M.see(message)
+
+/mob/Stat()
+ ..()
+ . = (is_client_active(10 MINUTES))
+
+ if(.)
+ if(statpanel("Status"))
+ stat(null, "Time Dilation: [round(SStime_track.time_dilation_current,1)]% AVG:([round(SStime_track.time_dilation_avg_fast,1)]%, [round(SStime_track.time_dilation_avg,1)]%, [round(SStime_track.time_dilation_avg_slow,1)]%)")
+ if(ticker && ticker.current_state != GAME_STATE_PREGAME)
+ stat("Station Time", stationtime2text())
+ stat("Station Date", stationdate2text())
+ stat("Round Duration", roundduration2text())
+
+ if(client.holder)
+ if(statpanel("Status"))
+ stat("Location:", "([x], [y], [z]) [loc]")
+ stat("CPU:","[world.cpu]")
+ stat("Instances:","[world.contents.len]")
+ stat(null, "Time Dilation: [round(SStime_track.time_dilation_current,1)]% AVG:([round(SStime_track.time_dilation_avg_fast,1)]%, [round(SStime_track.time_dilation_avg,1)]%, [round(SStime_track.time_dilation_avg_slow,1)]%)")
+ stat("Keys Held", keys2text(client.move_keys_held | client.mod_keys_held))
+ stat("Next Move ADD", dirs2text(client.next_move_dir_add))
+ stat("Next Move SUB", dirs2text(client.next_move_dir_sub))
+
+ if(statpanel("MC"))
+ stat("Location:", "([x], [y], [z]) [loc]")
+ stat("CPU:","[world.cpu]")
+ stat("Instances:","[world.contents.len]")
+ stat("World Time:", world.time)
+ stat("Real time of day:", REALTIMEOFDAY)
+ stat(null)
+ if(GLOB)
+ GLOB.stat_entry()
+ else
+ stat("Globals:", "ERROR")
+ if(Master)
+ Master.stat_entry()
+ else
+ stat("Master Controller:", "ERROR")
+ if(Failsafe)
+ Failsafe.stat_entry()
+ else
+ stat("Failsafe Controller:", "ERROR")
+ if(Master)
+ stat(null)
+ for(var/datum/controller/subsystem/SS in Master.subsystems)
+ SS.stat_entry()
+
+ if(statpanel("Tickets"))
+ GLOB.ahelp_tickets.stat_entry()
+
+
+ if(length(GLOB.sdql2_queries))
+ if(statpanel("SDQL2"))
+ stat("Access Global SDQL2 List", GLOB.sdql2_vv_statobj)
+ for(var/datum/SDQL2_query/Q as anything in GLOB.sdql2_queries)
+ Q.generate_stat()
+
+ if(listed_turf && client)
+ if(!TurfAdjacent(listed_turf))
+ listed_turf = null
+ else
+ if(statpanel("Turf"))
+ stat(listed_turf)
+ for(var/atom/A in listed_turf)
+ if(!A.mouse_opacity)
+ continue
+ if(A.invisibility > see_invisible)
+ continue
+ if(is_type_in_list(A, shouldnt_see))
+ continue
+ if(A.plane > plane)
+ continue
+ stat(A)
+
+
+// facing verbs
+/mob/proc/canface()
+// if(!canmove) return 0 //VOREStation Edit. Redundant check that only affects conscious proning, actual inability to turn and shift around handled by actual inabilities.
+ if(stat) return 0
+ if(anchored) return 0
+ if(transforming) return 0
+ return 1
+
+// Not sure what to call this. Used to check if humans are wearing an AI-controlled exosuit and hence don't need to fall over yet.
+/mob/proc/can_stand_overridden()
+ return 0
+
+//Updates canmove, lying and icons. Could perhaps do with a rename but I can't think of anything to describe it.
+/mob/proc/update_canmove()
+ return canmove
+
+
+/mob/proc/facedir(var/ndir)
+ if(!canface() || (client && (client.moving || !checkMoveCooldown())))
+ DEBUG_INPUT("Denying Facedir for [src] (moving=[client?.moving])")
+ return 0
+ set_dir(ndir)
+ if(buckled && buckled.buckle_movable)
+ buckled.set_dir(ndir)
+ setMoveCooldown(movement_delay())
+ return 1
+
+
+/mob/verb/eastface()
+ set hidden = 1
+ return facedir(client.client_dir(EAST))
+
+
+/mob/verb/westface()
+ set hidden = 1
+ return facedir(client.client_dir(WEST))
+
+
+/mob/verb/northface()
+ set hidden = 1
+ return facedir(client.client_dir(NORTH))
+
+
+/mob/verb/southface()
+ set hidden = 1
+ return facedir(client.client_dir(SOUTH))
+
+
+//This might need a rename but it should replace the can this mob use things check
+/mob/proc/IsAdvancedToolUser()
+ return 0
+
+/mob/proc/Stun(amount)
+ if(status_flags & CANSTUN)
+ facing_dir = null
+ stunned = max(max(stunned,amount),0) //can't go below 0, getting a low amount of stun doesn't lower your current stun
+ update_canmove() //updates lying, canmove and icons
+ return
+
+/mob/proc/SetStunned(amount) //if you REALLY need to set stun to a set amount without the whole "can't go below current stunned"
+ if(status_flags & CANSTUN)
+ stunned = max(amount,0)
+ update_canmove() //updates lying, canmove and icons
+ return
+
+/mob/proc/AdjustStunned(amount)
+ if(status_flags & CANSTUN)
+ stunned = max(stunned + amount,0)
+ update_canmove() //updates lying, canmove and icons
+ return
+
+/mob/proc/Weaken(amount)
+ if(status_flags & CANWEAKEN)
+ facing_dir = null
+ weakened = max(max(weakened,amount),0)
+ update_canmove() //updates lying, canmove and icons
+ return
+
+/mob/proc/SetWeakened(amount)
+ if(status_flags & CANWEAKEN)
+ weakened = max(amount,0)
+ update_canmove() //can you guess what this does yet?
+ return
+
+/mob/proc/AdjustWeakened(amount)
+ if(status_flags & CANWEAKEN)
+ weakened = max(weakened + amount,0)
+ update_canmove() //updates lying, canmove and icons
+ return
+
+/mob/proc/Paralyse(amount)
+ if(status_flags & CANPARALYSE)
+ facing_dir = null
+ paralysis = max(max(paralysis,amount),0)
+ return
+
+/mob/proc/SetParalysis(amount)
+ if(status_flags & CANPARALYSE)
+ paralysis = max(amount,0)
+ return
+
+/mob/proc/AdjustParalysis(amount)
+ if(status_flags & CANPARALYSE)
+ paralysis = max(paralysis + amount,0)
+ return
+
+/mob/proc/Sleeping(amount)
+ facing_dir = null
+ sleeping = max(max(sleeping,amount),0)
+ return
+
+/mob/proc/SetSleeping(amount)
+ sleeping = max(amount,0)
+ return
+
+/mob/proc/AdjustSleeping(amount)
+ sleeping = max(sleeping + amount,0)
+ return
+
+/mob/proc/Confuse(amount)
+ confused = max(max(confused,amount),0)
+ return
+
+/mob/proc/SetConfused(amount)
+ confused = max(amount,0)
+ return
+
+/mob/proc/AdjustConfused(amount)
+ confused = max(confused + amount,0)
+ return
+
+/mob/proc/Blind(amount)
+ eye_blind = max(max(eye_blind,amount),0)
+ return
+
+/mob/proc/SetBlinded(amount)
+ eye_blind = max(amount,0)
+ return
+
+/mob/proc/AdjustBlinded(amount)
+ eye_blind = max(eye_blind + amount,0)
+ return
+
+/mob/proc/Resting(amount)
+ facing_dir = null
+ resting = max(max(resting,amount),0)
+ update_canmove()
+ return
+
+/mob/proc/SetResting(amount)
+ resting = max(amount,0)
+ update_canmove()
+ return
+
+/mob/proc/AdjustResting(amount)
+ resting = max(resting + amount,0)
+ update_canmove()
+ return
+
+/mob/proc/AdjustLosebreath(amount)
+ losebreath = CLAMP(losebreath + amount, 0, 25)
+
+/mob/proc/SetLosebreath(amount)
+ losebreath = CLAMP(amount, 0, 25)
+
+/mob/proc/get_species()
+ return ""
+
+/mob/proc/flash_weak_pain()
+ flick("weak_pain",pain)
+
+/mob/proc/get_visible_implants(var/class = 0)
+ var/list/visible_implants = list()
+ for(var/obj/item/O in embedded)
+ if(O.w_class > class)
+ visible_implants += O
+ return visible_implants
+
+/mob/proc/embedded_needs_process()
+ return (embedded.len > 0)
+
+/mob/proc/yank_out_object()
+ set category = "Object"
+ set name = "Yank out object"
+ set desc = "Remove an embedded item at the cost of bleeding and pain."
+ set src in view(1)
+
+ if(!isliving(usr) || !usr.checkClickCooldown())
+ return
+ usr.setClickCooldown(20)
+
+ if(usr.stat == 1)
+ to_chat(usr, "You are unconcious and cannot do that!")
+ return
+
+ if(usr.restrained())
+ to_chat(usr, "You are restrained and cannot do that!")
+ return
+
+ var/mob/S = src
+ var/mob/U = usr
+ var/list/valid_objects = list()
+ var/self = null
+
+ if(S == U)
+ self = 1 // Removing object from yourself.
+
+ valid_objects = get_visible_implants(0)
+ if(!valid_objects.len)
+ if(self)
+ to_chat(src, "You have nothing stuck in your body that is large enough to remove.")
+ else
+ to_chat(U, "[src] has nothing stuck in their wounds that is large enough to remove.")
+ return
+
+ var/obj/item/weapon/selection = tgui_input_list(usr, "What do you want to yank out?", "Embedded objects", valid_objects)
+
+ if(self)
+ to_chat(src, "You attempt to get a good grip on [selection] in your body.")
+ else
+ to_chat(U, "You attempt to get a good grip on [selection] in [S]'s body.")
+
+ if(!do_after(U, 30))
+ return
+ if(!selection || !S || !U)
+ return
+
+ if(self)
+ visible_message("[src] rips [selection] out of their body.","You rip [selection] out of your body.")
+ else
+ visible_message("[usr] rips [selection] out of [src]'s body.","[usr] rips [selection] out of your body.")
+ valid_objects = get_visible_implants(0)
+ if(valid_objects.len == 1) //Yanking out last object - removing verb.
+ src.verbs -= /mob/proc/yank_out_object
+ clear_alert("embeddedobject")
+
+ if(ishuman(src))
+ var/mob/living/carbon/human/H = src
+ var/obj/item/organ/external/affected
+
+ for(var/obj/item/organ/external/organ in H.organs) //Grab the organ holding the implant.
+ for(var/obj/item/O in organ.implants)
+ if(O == selection)
+ affected = organ
+
+ affected.implants -= selection
+ H.shock_stage+=20
+ affected.take_damage((selection.w_class * 3), 0, 0, 1, "Embedded object extraction")
+
+ if(prob(selection.w_class * 5) && (affected.robotic < ORGAN_ROBOT)) //I'M SO ANEMIC I COULD JUST -DIE-.
+ var/datum/wound/internal_bleeding/I = new (min(selection.w_class * 5, 15))
+ affected.wounds += I
+ H.custom_pain("Something tears wetly in your [affected] as [selection] is pulled free!", 50)
+
+ if (ishuman(U))
+ var/mob/living/carbon/human/human_user = U
+ human_user.bloody_hands(H)
+
+ else if(issilicon(src))
+ var/mob/living/silicon/robot/R = src
+ R.embedded -= selection
+ R.adjustBruteLoss(5)
+ R.adjustFireLoss(10)
+
+ selection.forceMove(get_turf(src))
+ U.put_in_hands(selection)
+
+ for(var/obj/item/weapon/O in pinned)
+ if(O == selection)
+ pinned -= O
+ if(!pinned.len)
+ anchored = FALSE
+ return 1
+
+//Check for brain worms in head.
+/mob/proc/has_brain_worms()
+
+ for(var/I in contents)
+ if(istype(I,/mob/living/simple_mob/animal/borer))
+ return I
+
+ return 0
+
+/mob/proc/updateicon()
+ return
+
+// Please always use this proc, never just set the var directly.
+/mob/proc/set_stat(var/new_stat)
+ . = (stat != new_stat)
+ stat = new_stat
+
+/mob/verb/face_direction()
+
+ set name = "Face Direction"
+ set category = "IC"
+ set src = usr
+
+ set_face_dir()
+
+ if(!facing_dir)
+ to_chat(usr, "You are now not facing anything.")
+ else
+ to_chat(usr, "You are now facing [dir2text(facing_dir)].")
+
+/mob/proc/set_face_dir(var/newdir)
+ if(newdir == facing_dir)
+ facing_dir = null
+ else if(newdir)
+ set_dir(newdir)
+ facing_dir = newdir
+ else if(facing_dir)
+ facing_dir = null
+ else
+ set_dir(dir)
+ facing_dir = dir
+
+/mob/set_dir()
+ if(facing_dir)
+ if(!canface() || lying || buckled || restrained())
+ facing_dir = null
+ else if(dir != facing_dir)
+ return ..(facing_dir)
+ else
+ return ..()
+
+/mob/verb/northfaceperm()
+ set hidden = 1
+ set_face_dir(client.client_dir(NORTH))
+
+/mob/verb/southfaceperm()
+ set hidden = 1
+ set_face_dir(client.client_dir(SOUTH))
+
+/mob/verb/eastfaceperm()
+ set hidden = 1
+ set_face_dir(client.client_dir(EAST))
+
+/mob/verb/westfaceperm()
+ set hidden = 1
+ set_face_dir(client.client_dir(WEST))
+
+// Begin VOREstation edit
+/mob/verb/shiftnorth()
+ set hidden = TRUE
+ if(!canface())
+ return FALSE
+ if(pixel_y <= (default_pixel_y + 16))
+ pixel_y++
+ is_shifted = TRUE
+
+/mob/verb/shiftsouth()
+ set hidden = TRUE
+ if(!canface())
+ return FALSE
+ if(pixel_y >= (default_pixel_y - 16))
+ pixel_y--
+ is_shifted = TRUE
+
+/mob/verb/shiftwest()
+ set hidden = TRUE
+ if(!canface())
+ return FALSE
+ if(pixel_x >= (default_pixel_x - 16))
+ pixel_x--
+ is_shifted = TRUE
+
+/mob/verb/shifteast()
+ set hidden = TRUE
+ if(!canface())
+ return FALSE
+ if(pixel_x <= (default_pixel_x + 16))
+ pixel_x++
+ is_shifted = TRUE
+// End VOREstation edit
+
+/mob/proc/adjustEarDamage()
+ return
+
+/mob/proc/setEarDamage()
+ return
+
+// Set client view distance (size of client's screen). Returns TRUE if anything changed.
+/mob/proc/set_viewsize(var/new_view = world.view)
+ if (client && new_view != client.view)
+ client.view = new_view
+ return TRUE
+ return FALSE
+
+//Throwing stuff
+
+/mob/proc/toggle_throw_mode()
+ if (src.in_throw_mode)
+ throw_mode_off()
+ else
+ throw_mode_on()
+
+/mob/proc/throw_mode_off()
+ src.in_throw_mode = 0
+ if(src.throw_icon) //in case we don't have the HUD and we use the hotkey
+ src.throw_icon.icon_state = "act_throw_off"
+
+/mob/proc/throw_mode_on()
+ src.in_throw_mode = 1
+ if(src.throw_icon)
+ src.throw_icon.icon_state = "act_throw_on"
+
+/mob/proc/isSynthetic()
+ return 0
+
+/mob/proc/is_muzzled()
+ return 0
+
+//Exploitable Info Update
+
+/mob/proc/amend_exploitable(var/obj/item/I)
+ if(istype(I))
+ exploit_addons |= I
+ var/exploitmsg = html_decode("\n" + "Has " + I.name + ".")
+ exploit_record += exploitmsg
+
+/client/proc/check_has_body_select()
+ return mob && mob.hud_used && istype(mob.zone_sel, /obj/screen/zone_sel)
+
+/client/verb/body_toggle_head()
+ set name = "body-toggle-head"
+ set hidden = 1
+ toggle_zone_sel(list(BP_HEAD, O_EYES, O_MOUTH))
+
+/client/verb/body_r_arm()
+ set name = "body-r-arm"
+ set hidden = 1
+ toggle_zone_sel(list(BP_R_ARM,BP_R_HAND))
+
+/client/verb/body_l_arm()
+ set name = "body-l-arm"
+ set hidden = 1
+ toggle_zone_sel(list(BP_L_ARM,BP_L_HAND))
+
+/client/verb/body_chest()
+ set name = "body-chest"
+ set hidden = 1
+ toggle_zone_sel(list(BP_TORSO))
+
+/client/verb/body_groin()
+ set name = "body-groin"
+ set hidden = 1
+ toggle_zone_sel(list(BP_GROIN))
+
+/client/verb/body_r_leg()
+ set name = "body-r-leg"
+ set hidden = 1
+ toggle_zone_sel(list(BP_R_LEG,BP_R_FOOT))
+
+/client/verb/body_l_leg()
+ set name = "body-l-leg"
+ set hidden = 1
+ toggle_zone_sel(list(BP_L_LEG,BP_L_FOOT))
+
+/client/proc/toggle_zone_sel(list/zones)
+ if(!check_has_body_select())
+ return
+ var/obj/screen/zone_sel/selector = mob.zone_sel
+ selector.set_selected_zone(next_in_list(mob.zone_sel.selecting,zones))
+
+// This handles setting the client's color variable, which makes everything look a specific color.
+// This proc is here so it can be called without needing to check if the client exists, or if the client relogs.
+// This is for inheritence since /mob/living will serve most cases. If you need ghosts to use this you'll have to implement that yourself.
+/mob/proc/update_client_color()
+ if(client && client.color)
+ animate(client, color = null, time = 10)
+ return
+
+/mob/proc/swap_hand()
+ return
+
+//Throwing stuff
+/mob/proc/throw_item(atom/target)
+ return
+
+/mob/proc/will_show_tooltip()
+ if(alpha <= EFFECTIVE_INVIS)
+ return FALSE
+ return TRUE
+
+/mob/MouseEntered(location, control, params)
+ if(usr != src && usr.is_preference_enabled(/datum/client_preference/mob_tooltips) && src.will_show_tooltip())
+ openToolTip(user = usr, tip_src = src, params = params, title = get_nametag_name(usr), content = get_nametag_desc(usr))
+
+ ..()
+
+/mob/MouseDown()
+ closeToolTip(usr) //No reason not to, really
+
+ ..()
+
+/mob/MouseExited()
+ closeToolTip(usr) //No reason not to, really
+
+ ..()
+
+// Manages a global list of mobs with clients attached, indexed by z-level.
+/mob/proc/update_client_z(new_z) // +1 to register, null to unregister.
+ if(registered_z != new_z)
+ if(registered_z)
+ GLOB.players_by_zlevel[registered_z] -= src
+ if(client)
+ if(new_z)
+ GLOB.players_by_zlevel[new_z] += src
+ registered_z = new_z
+ else
+ registered_z = null
+
+GLOBAL_LIST_EMPTY_TYPED(living_players_by_zlevel, /list)
+/mob/living/update_client_z(new_z)
+ var/precall_reg_z = registered_z
+ . = ..() // will update registered_z if necessary
+ if(precall_reg_z != registered_z) // parent did work, let's do work too
+ if(precall_reg_z)
+ GLOB.living_players_by_zlevel[precall_reg_z] -= src
+ if(registered_z)
+ GLOB.living_players_by_zlevel[registered_z] += src
+
+/mob/onTransitZ(old_z, new_z)
+ ..()
+ update_client_z(new_z)
+
+/mob/cloak()
+ . = ..()
+ if(client && cloaked_selfimage)
+ client.images += cloaked_selfimage
+
+/mob/uncloak()
+ if(client && cloaked_selfimage)
+ client.images -= cloaked_selfimage
+ return ..()
+
+/mob/get_cloaked_selfimage()
+ var/icon/selficon = getCompoundIcon(src)
+ selficon.MapColors(0,0,0, 0,0,0, 0,0,0, 1,1,1) //White
+ var/image/selfimage = image(selficon)
+ selfimage.color = "#0000FF"
+ selfimage.alpha = 100
+ selfimage.layer = initial(layer)
+ selfimage.plane = initial(plane)
+ selfimage.loc = src
+
+ return selfimage
+
+/mob/proc/GetAltName()
+ return ""
+
+/mob/proc/get_ghost(even_if_they_cant_reenter = 0)
+ if(mind)
+ return mind.get_ghost(even_if_they_cant_reenter)
+
+/mob/proc/grab_ghost(force)
+ if(mind)
+ return mind.grab_ghost(force = force)
+=======
+/mob/Destroy()//This makes sure that mobs withGLOB.clients/keys are not just deleted from the game.
+ mob_list -= src
+ dead_mob_list -= src
+ living_mob_list -= src
+ unset_machine()
+ qdel(hud_used)
+ clear_fullscreen()
+ if(client)
+ for(var/obj/screen/movable/spell_master/spell_master in spell_masters)
+ qdel(spell_master)
+ remove_screen_obj_references()
+ client.screen = list()
+ if(mind && mind.current == src)
+ spellremove(src)
+ ghostize()
+ QDEL_NULL(plane_holder)
+ ..()
+ return QDEL_HINT_HARDDEL_NOW
+
+/mob/proc/remove_screen_obj_references()
+ hands = null
+ pullin = null
+ purged = null
+ internals = null
+ i_select = null
+ m_select = null
+ healths = null
+ throw_icon = null
+ pain = null
+ item_use_icon = null
+ gun_move_icon = null
+ gun_setting_icon = null
+ spell_masters = null
+ zone_sel = null
+
+/mob/Initialize()
+ mob_list += src
+ if(stat == DEAD)
+ dead_mob_list += src
+ else
+ living_mob_list += src
+ lastarea = get_area(src)
+ set_focus(src) // VOREStation Add - Key Handling
+ hook_vr("mob_new",list(src)) //VOREStation Code
+ update_transform() // Some mobs may start bigger or smaller than normal.
+ return ..()
+
+/mob/proc/show_message(msg, type, alt, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2)
+
+ if(!client && !teleop) return
+
+ if (type)
+ if((type & VISIBLE_MESSAGE) && (is_blind() || paralysis) )//Vision related
+ if (!( alt ))
+ return
+ else
+ msg = alt
+ type = alt_type
+ if ((type & AUDIBLE_MESSAGE) && is_deaf())//Hearing related
+ if (!( alt ))
+ return
+ else
+ msg = alt
+ type = alt_type
+ if ((type & VISIBLE_MESSAGE) && (sdisabilities & BLIND))
+ return
+ // Added voice muffling for Issue 41.
+ if(stat == UNCONSCIOUS || sleeping > 0)
+ to_chat(src, "... You can almost hear someone talking ...")
+ else
+ to_chat(src,msg)
+ if(teleop)
+ to_chat(teleop, create_text_tag("body", "BODY:", teleop) + "[msg]")
+ return
+
+// Show a message to all mobs and objects in sight of this one
+// This would be for visible actions by the src mob
+// message is the message output to anyone who can see e.g. "[src] does something!"
+// self_message (optional) is what the src mob sees e.g. "You do something!"
+// blind_message (optional) is what blind people will hear e.g. "You hear something!"
+/mob/visible_message(var/message, var/self_message, var/blind_message, var/list/exclude_mobs = null, var/range = world.view, var/runemessage)
+ if(self_message)
+ if(LAZYLEN(exclude_mobs))
+ exclude_mobs |= src
+ else
+ exclude_mobs = list(src)
+ src.show_message(self_message, 1, blind_message, 2)
+ if(isnull(runemessage))
+ runemessage = -1
+ . = ..(message, blind_message, exclude_mobs, range, runemessage) // Really not ideal that atom/visible_message has different arg numbering :(
+
+// Returns an amount of power drawn from the object (-1 if it's not viable).
+// If drain_check is set it will not actually drain power, just return a value.
+// If surge is set, it will destroy/damage the recipient and not return any power.
+// Not sure where to define this, so it can sit here for the rest of time.
+/atom/proc/drain_power(var/drain_check,var/surge, var/amount = 0)
+ return -1
+
+// Show a message to all mobs and objects in earshot of this one
+// This would be for audible actions by the src mob
+// message is the message output to anyone who can hear.
+// self_message (optional) is what the src mob hears.
+// deaf_message (optional) is what deaf people will see.
+// hearing_distance (optional) is the range, how many tiles away the message can be heard.
+/mob/audible_message(var/message, var/deaf_message, var/hearing_distance, var/self_message, var/radio_message, var/runemessage)
+
+ var/range = hearing_distance || world.view
+ var/list/hear = get_mobs_and_objs_in_view_fast(get_turf(src),range,remote_ghosts = FALSE)
+
+ var/list/hearing_mobs = hear["mobs"]
+ var/list/hearing_objs = hear["objs"]
+
+ if(isnull(runemessage))
+ runemessage = -1 // Symmetry with mob/audible_message, despite the fact this one doesn't call parent. Maybe it should!
+
+ if(radio_message)
+ for(var/obj/O as anything in hearing_objs)
+ O.hear_talk(src, list(new /datum/multilingual_say_piece(GLOB.all_languages["Noise"], radio_message)), null)
+ else
+ for(var/obj/O as anything in hearing_objs)
+ O.show_message(message, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE)
+
+ for(var/mob/M as anything in hearing_mobs)
+ var/msg = message
+ if(self_message && M==src)
+ msg = self_message
+ M.show_message(msg, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE)
+ if(runemessage != -1)
+ M.create_chat_message(src, "[runemessage || message]", FALSE, list("emote"), audible = FALSE)
+
+/mob/proc/findname(msg)
+ for(var/mob/M in mob_list)
+ if (M.real_name == text("[]", msg))
+ return M
+ return 0
+
+/mob/proc/Life()
+// if(organStructure)
+// organStructure.ProcessOrgans()
+ return
+
+#define UNBUCKLED 0
+#define PARTIALLY_BUCKLED 1
+#define FULLY_BUCKLED 2
+/mob/proc/buckled()
+ // Preliminary work for a future buckle rewrite,
+ // where one might be fully restrained (like an elecrical chair), or merely secured (shuttle chair, keeping you safe but not otherwise restrained from acting)
+ if(!buckled)
+ return UNBUCKLED
+ return restrained() ? FULLY_BUCKLED : PARTIALLY_BUCKLED
+
+/mob/proc/is_blind()
+ return ((sdisabilities & BLIND) || blinded || incapacitated(INCAPACITATION_KNOCKOUT))
+
+/mob/proc/is_deaf()
+ return ((sdisabilities & DEAF) || ear_deaf || incapacitated(INCAPACITATION_KNOCKOUT))
+
+/mob/proc/is_physically_disabled()
+ return incapacitated(INCAPACITATION_DISABLED)
+
+/mob/proc/cannot_stand()
+ return incapacitated(INCAPACITATION_KNOCKDOWN)
+
+/mob/proc/incapacitated(var/incapacitation_flags = INCAPACITATION_DEFAULT)
+ if ((incapacitation_flags & INCAPACITATION_STUNNED) && stunned)
+ return 1
+
+ if ((incapacitation_flags & INCAPACITATION_FORCELYING) && (weakened || resting))
+ return 1
+
+ if ((incapacitation_flags & INCAPACITATION_KNOCKOUT) && (stat || paralysis || sleeping || (status_flags & FAKEDEATH)))
+ return 1
+
+ if((incapacitation_flags & INCAPACITATION_RESTRAINED) && restrained())
+ return 1
+
+ if((incapacitation_flags & (INCAPACITATION_BUCKLED_PARTIALLY|INCAPACITATION_BUCKLED_FULLY)))
+ var/buckling = buckled()
+ if(buckling >= PARTIALLY_BUCKLED && (incapacitation_flags & INCAPACITATION_BUCKLED_PARTIALLY))
+ return 1
+ if(buckling == FULLY_BUCKLED && (incapacitation_flags & INCAPACITATION_BUCKLED_FULLY))
+ return 1
+
+ return 0
+
+#undef UNBUCKLED
+#undef PARTIALLY_BUCKLED
+#undef FULLY_BUCKLED
+
+/mob/proc/restrained()
+ return
+
+/mob/proc/reset_view(atom/A)
+ if (client)
+ if (istype(A, /atom/movable))
+ client.perspective = EYE_PERSPECTIVE
+ client.eye = A
+ else
+ if (isturf(loc))
+ client.eye = client.mob
+ client.perspective = MOB_PERSPECTIVE
+ else
+ client.perspective = EYE_PERSPECTIVE
+ client.eye = loc
+ return TRUE
+
+/mob/verb/pointed(atom/A as mob|obj|turf in view())
+ set name = "Point To"
+ set category = "Object"
+
+ if(!src || !isturf(src.loc) || !(A in view(src.loc)))
+ return 0
+ if(istype(A, /obj/effect/decal/point))
+ return 0
+
+ var/turf/tile = get_turf(A)
+ if (!tile)
+ return 0
+
+ var/turf/our_tile = get_turf(src)
+ var/obj/visual = new /obj/effect/decal/point(our_tile)
+ visual.invisibility = invisibility
+ visual.plane = ABOVE_PLANE
+ visual.layer = FLY_LAYER
+
+ animate(visual,
+ pixel_x = (tile.x - our_tile.x) * world.icon_size + A.pixel_x,
+ pixel_y = (tile.y - our_tile.y) * world.icon_size + A.pixel_y,
+ time = 1.7,
+ easing = EASE_OUT)
+
+ QDEL_IN(visual, 2 SECONDS) //Better qdel
+
+ face_atom(A)
+ return 1
+
+
+/mob/proc/ret_grab(list/L, flag)
+ return
+
+/mob/verb/mode()
+ set name = "Activate Held Object"
+ set category = "Object"
+ set src = usr
+
+ return
+
+/*
+/mob/verb/dump_source()
+
+ var/master = ""
+ for(var/t in typesof(/area))
+ master += text("[]\n", t)
+ //Foreach goto(26)
+ src << browse(master)
+ return
+*/
+
+/mob/verb/memory()
+ set name = "Notes"
+ set category = "IC"
+ if(mind)
+ mind.show_memory(src)
+ else
+ to_chat(src, "The game appears to have misplaced your mind datum, so we can't show you your notes.")
+
+/mob/verb/add_memory(msg as message)
+ set name = "Add Note"
+ set category = "IC"
+
+ msg = sanitize(msg)
+
+ if(mind)
+ mind.store_memory(msg)
+ else
+ to_chat(src, "The game appears to have misplaced your mind datum, so we can't show you your notes.")
+
+/mob/proc/store_memory(msg as message, popup, sane = 1)
+ msg = copytext(msg, 1, MAX_MESSAGE_LEN)
+
+ if (sane)
+ msg = sanitize(msg)
+
+ if (length(memory) == 0)
+ memory += msg
+ else
+ memory += "
[msg]"
+
+ if (popup)
+ memory()
+
+/mob/proc/update_flavor_text()
+ set src in usr
+ if(usr != src)
+ to_chat(usr, "No.")
+ var/msg = sanitize(input(usr,"Set the flavor text in your 'examine' verb.","Flavor Text",html_decode(flavor_text)) as message|null, extra = 0) //VOREStation Edit: separating out OOC notes
+
+ if(msg != null)
+ flavor_text = msg
+
+/mob/proc/warn_flavor_changed()
+ if(flavor_text && flavor_text != "") // don't spam people that don't use it!
+ to_chat(src, "OOC Warning:
")
+ to_chat(src, "Your flavor text is likely out of date! Change")
+
+/mob/proc/print_flavor_text()
+ if (flavor_text && flavor_text != "")
+ var/msg = replacetext(flavor_text, "\n", " ")
+ if(length(msg) <= 40)
+ return "[msg]"
+ else
+ return "[copytext_preserve_html(msg, 1, 37)]... More..."
+
+/*
+/mob/verb/help()
+ set name = "Help"
+ src << browse('html/help.html', "window=help")
+ return
+*/
+
+/mob/proc/set_respawn_timer(var/time)
+ // Try to figure out what time to use
+
+ // Special cases, can never respawn
+ if(ticker?.mode?.deny_respawn)
+ time = -1
+ else if(!config.abandon_allowed)
+ time = -1
+ else if(!config.respawn)
+ time = -1
+
+ // Special case for observing before game start
+ else if(ticker?.current_state <= GAME_STATE_SETTING_UP)
+ time = 1 MINUTE
+
+ // Wasn't given a time, use the config time
+ else if(!time)
+ time = config.respawn_time
+
+ var/keytouse = ckey
+ // Try harder to find a key to use
+ if(!keytouse && key)
+ keytouse = ckey(key)
+ else if(!keytouse && mind?.key)
+ keytouse = ckey(mind.key)
+
+ GLOB.respawn_timers[keytouse] = world.time + time
+
+/mob/observer/dead/set_respawn_timer()
+ if(config.antag_hud_restricted && has_enabled_antagHUD)
+ ..(-1)
+ else
+ return // Don't set it, no need
+
+/mob/verb/abandon_mob()
+ set name = "Return to Menu"
+ set category = "OOC"
+
+ if(stat != DEAD || !ticker)
+ to_chat(usr, "You must be dead to use this!")
+ return
+
+ // Final chance to abort "respawning"
+ if(mind && timeofdeath) // They had spawned before
+ var/choice = tgui_alert(usr, "Returning to the menu will prevent your character from being revived in-round. Are you sure?", "Confirmation", list("No, wait", "Yes, leave"))
+ if(choice == "No, wait")
+ return
+
+ // Beyond this point, you're going to respawn
+ to_chat(usr, config.respawn_message)
+
+ if(!client)
+ log_game("[usr.key] AM failed due to disconnect.")
+ return
+ client.screen.Cut()
+ client.screen += client.void
+ if(!client)
+ log_game("[usr.key] AM failed due to disconnect.")
+ return
+
+ announce_ghost_joinleave(client, 0)
+
+ var/mob/new_player/M = new /mob/new_player()
+ if(!client)
+ log_game("[usr.key] AM failed due to disconnect.")
+ qdel(M)
+ return
+
+ M.key = key
+ if(M.mind)
+ M.mind.reset()
+ return
+
+/client/verb/changes()
+ set name = "Changelog"
+ set category = "OOC"
+ src << browse('html/changelog.html', "window=changes;size=675x650")
+ if(prefs.lastchangelog != changelog_hash)
+ prefs.lastchangelog = changelog_hash
+ SScharacter_setup.queue_preferences_save(prefs)
+ winset(src, "rpane.changelog", "background-color=none;font-style=;")
+
+/mob/verb/observe()
+ set name = "Observe"
+ set category = "OOC"
+ var/is_admin = 0
+
+ if(client.holder && (client.holder.rights & R_ADMIN|R_EVENT))
+ is_admin = 1
+ else if(stat != DEAD || istype(src, /mob/new_player))
+ to_chat(usr, "You must be observing to use this!")
+ return
+
+ if(is_admin && stat == DEAD)
+ is_admin = 0
+
+ var/list/targets = list()
+
+
+ targets += observe_list_format(nuke_disks)
+ targets += observe_list_format(all_singularities)
+ targets += getmobs()
+ targets += observe_list_format(sortAtom(mechas_list))
+ targets += observe_list_format(SSshuttles.ships)
+
+ client.perspective = EYE_PERSPECTIVE
+
+ var/eye_name = null
+
+ var/ok = "[is_admin ? "Admin Observe" : "Observe"]"
+ eye_name = tgui_input_list(usr, "Select something to [ok]:", "Select Target", targets)
+
+ if (!eye_name)
+ return
+
+ var/mob/mob_eye = targets[eye_name]
+
+ if(client && mob_eye)
+ client.eye = mob_eye
+ if (is_admin)
+ client.adminobs = 1
+ if(mob_eye == client.mob || client.eye == client.mob)
+ client.adminobs = 0
+
+/mob/verb/cancel_camera()
+ set name = "Cancel Camera View"
+ set category = "OOC"
+ unset_machine()
+ reset_view(null)
+
+/mob/Topic(href, href_list)
+ if(href_list["mach_close"])
+ var/t1 = text("window=[href_list["mach_close"]]")
+ unset_machine()
+ src << browse(null, t1)
+
+ if(href_list["flavor_more"])
+ usr << browse(text("[] []", name, replacetext(flavor_text, "\n", "
")), text("window=[];size=500x200", name))
+ onclose(usr, "[name]")
+ if(href_list["flavor_change"])
+ update_flavor_text()
+// ..()
+ return
+
+
+/mob/proc/pull_damage()
+ return 0
+
+/mob/verb/stop_pulling()
+
+ set name = "Stop Pulling"
+ set category = "IC"
+
+ if(pulling)
+ if(ishuman(pulling))
+ var/mob/living/carbon/human/H = pulling
+ visible_message(SPAN_WARNING("\The [src] lets go of \the [H]."), SPAN_NOTICE("You let go of \the [H]."), exclude_mobs = list(H))
+ if(!H.stat)
+ to_chat(H, SPAN_WARNING("\The [src] lets go of you."))
+ pulling.pulledby = null
+ pulling = null
+ if(pullin)
+ pullin.icon_state = "pull0"
+
+/mob/proc/start_pulling(var/atom/movable/AM)
+
+ if ( !AM || !usr || src==AM || !isturf(src.loc) ) //if there's no person pulling OR the person is pulling themself OR the object being pulled is inside something: abort!
+ return
+
+ if (AM.anchored)
+ to_chat(src, "It won't budge!")
+ return
+
+ var/mob/M = AM
+ if(ismob(AM))
+
+ if(!can_pull_mobs || !can_pull_size)
+ to_chat(src, "They won't budge!")
+ return
+
+ if((mob_size < M.mob_size) && (can_pull_mobs != MOB_PULL_LARGER))
+ to_chat(src, "[M] is too large for you to move!")
+ return
+
+ if((mob_size == M.mob_size) && (can_pull_mobs == MOB_PULL_SMALLER))
+ to_chat(src, "[M] is too heavy for you to move!")
+ return
+
+ // If your size is larger than theirs and you have some
+ // kind of mob pull value AT ALL, you will be able to pull
+ // them, so don't bother checking that explicitly.
+
+ if(M.grabbed_by.len)
+ // Only start pulling when nobody else has a grab on them
+ . = 1
+ for(var/obj/item/weapon/grab/G in M.grabbed_by)
+ if(G.assailant != usr)
+ . = 0
+ else
+ qdel(G)
+ if(!.)
+ to_chat(src, "Somebody has a grip on them!")
+ return
+
+ if(!iscarbon(src))
+ M.LAssailant = null
+ else
+ M.LAssailant = usr
+
+ else if(isobj(AM))
+ var/obj/I = AM
+ if(!can_pull_size || can_pull_size < I.w_class)
+ to_chat(src, "It won't budge!")
+ return
+
+ if(pulling)
+ var/pulling_old = pulling
+ stop_pulling()
+ // Are we pulling the same thing twice? Just stop pulling.
+ if(pulling_old == AM)
+ return
+
+ src.pulling = AM
+ AM.pulledby = src
+
+ if(pullin)
+ pullin.icon_state = "pull1"
+
+ if(ishuman(AM))
+ var/mob/living/carbon/human/H = AM
+ if(H.lying) // If they're on the ground we're probably dragging their arms to move them
+ visible_message(SPAN_WARNING("\The [src] leans down and grips \the [H]'s arms."), SPAN_NOTICE("You lean down and grip \the [H]'s arms."), exclude_mobs = list(H))
+ if(!H.stat)
+ to_chat(H, SPAN_WARNING("\The [src] leans down and grips your arms."))
+ else //Otherwise we're probably just holding their arm to lead them somewhere
+ visible_message(SPAN_WARNING("\The [src] grips \the [H]'s arm."), SPAN_NOTICE("You grip \the [H]'s arm."), exclude_mobs = list(H))
+ if(!H.stat)
+ to_chat(H, SPAN_WARNING("\The [src] grips your arm."))
+ playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 25) //Quieter than hugging/grabbing but we still want some audio feedback
+
+ if(H.pull_damage())
+ to_chat(src, "Pulling \the [H] in their current condition would probably be a bad idea.")
+
+ //Attempted fix for people flying away through space when cuffed and dragged.
+ if(ismob(AM))
+ var/mob/pulled = AM
+ pulled.inertia_dir = 0
+
+/mob/proc/can_use_hands()
+ return
+
+/mob/proc/is_active()
+ return (0 >= usr.stat)
+
+/mob/proc/is_dead()
+ return stat == DEAD
+
+/mob/proc/is_mechanical()
+ if(mind && (mind.assigned_role == "Cyborg" || mind.assigned_role == "AI"))
+ return 1
+ return istype(src, /mob/living/silicon) || get_species() == "Machine"
+
+/mob/proc/is_ready()
+ return client && !!mind
+
+/mob/proc/get_gender()
+ return gender
+
+/mob/proc/see(message)
+ if(!is_active())
+ return 0
+ to_chat(src,message)
+ return 1
+
+/mob/proc/show_viewers(message)
+ for(var/mob/M in viewers())
+ M.see(message)
+
+/mob/Stat()
+ ..()
+ . = (is_client_active(10 MINUTES))
+
+ if(.)
+ if(statpanel("Status"))
+ stat(null, "Time Dilation: [round(SStime_track.time_dilation_current,1)]% AVG:([round(SStime_track.time_dilation_avg_fast,1)]%, [round(SStime_track.time_dilation_avg,1)]%, [round(SStime_track.time_dilation_avg_slow,1)]%)")
+ if(ticker && ticker.current_state != GAME_STATE_PREGAME)
+ stat("Station Time", stationtime2text())
+ stat("Station Date", stationdate2text())
+ stat("Round Duration", roundduration2text())
+
+ if(client.holder)
+ if(statpanel("Status"))
+ stat("Location:", "([x], [y], [z]) [loc]")
+ stat("CPU:","[world.cpu]")
+ stat("Instances:","[world.contents.len]")
+ stat(null, "Time Dilation: [round(SStime_track.time_dilation_current,1)]% AVG:([round(SStime_track.time_dilation_avg_fast,1)]%, [round(SStime_track.time_dilation_avg,1)]%, [round(SStime_track.time_dilation_avg_slow,1)]%)")
+ stat("Keys Held", keys2text(client.move_keys_held | client.mod_keys_held))
+ stat("Next Move ADD", dirs2text(client.next_move_dir_add))
+ stat("Next Move SUB", dirs2text(client.next_move_dir_sub))
+
+ if(statpanel("MC"))
+ stat("Location:", "([x], [y], [z]) [loc]")
+ stat("CPU:","[world.cpu]")
+ stat("Instances:","[world.contents.len]")
+ stat("World Time:", world.time)
+ stat("Real time of day:", REALTIMEOFDAY)
+ stat(null)
+ if(GLOB)
+ GLOB.stat_entry()
+ else
+ stat("Globals:", "ERROR")
+ if(Master)
+ Master.stat_entry()
+ else
+ stat("Master Controller:", "ERROR")
+ if(Failsafe)
+ Failsafe.stat_entry()
+ else
+ stat("Failsafe Controller:", "ERROR")
+ if(Master)
+ stat(null)
+ for(var/datum/controller/subsystem/SS in Master.subsystems)
+ SS.stat_entry()
+
+ if(statpanel("Tickets"))
+ GLOB.ahelp_tickets.stat_entry()
+
+
+ if(length(GLOB.sdql2_queries))
+ if(statpanel("SDQL2"))
+ stat("Access Global SDQL2 List", GLOB.sdql2_vv_statobj)
+ for(var/datum/SDQL2_query/Q as anything in GLOB.sdql2_queries)
+ Q.generate_stat()
+
+ if(listed_turf && client)
+ if(!TurfAdjacent(listed_turf))
+ listed_turf = null
+ else
+ if(statpanel("Turf"))
+ stat(listed_turf)
+ for(var/atom/A in listed_turf)
+ if(!A.mouse_opacity)
+ continue
+ if(A.invisibility > see_invisible)
+ continue
+ if(is_type_in_list(A, shouldnt_see))
+ continue
+ if(A.plane > plane)
+ continue
+ stat(A)
+
+
+// facing verbs
+/mob/proc/canface()
+// if(!canmove) return 0 //VOREStation Edit. Redundant check that only affects conscious proning, actual inability to turn and shift around handled by actual inabilities.
+ if(stat) return 0
+ if(anchored) return 0
+ if(transforming) return 0
+ return 1
+
+// Not sure what to call this. Used to check if humans are wearing an AI-controlled exosuit and hence don't need to fall over yet.
+/mob/proc/can_stand_overridden()
+ return 0
+
+//Updates canmove, lying and icons. Could perhaps do with a rename but I can't think of anything to describe it.
+/mob/proc/update_canmove()
+ return canmove
+
+
+/mob/proc/facedir(var/ndir)
+ if(!canface() || (client && (client.moving || !checkMoveCooldown())))
+ DEBUG_INPUT("Denying Facedir for [src] (moving=[client?.moving])")
+ return 0
+ set_dir(ndir)
+ if(buckled && buckled.buckle_movable)
+ buckled.set_dir(ndir)
+ setMoveCooldown(movement_delay())
+ return 1
+
+
+/mob/verb/eastface()
+ set hidden = 1
+ return facedir(client.client_dir(EAST))
+
+
+/mob/verb/westface()
+ set hidden = 1
+ return facedir(client.client_dir(WEST))
+
+
+/mob/verb/northface()
+ set hidden = 1
+ return facedir(client.client_dir(NORTH))
+
+
+/mob/verb/southface()
+ set hidden = 1
+ return facedir(client.client_dir(SOUTH))
+
+
+//This might need a rename but it should replace the can this mob use things check
+/mob/proc/IsAdvancedToolUser()
+ return 0
+
+/mob/proc/Stun(amount)
+ if(status_flags & CANSTUN)
+ facing_dir = null
+ stunned = max(max(stunned,amount),0) //can't go below 0, getting a low amount of stun doesn't lower your current stun
+ update_canmove() //updates lying, canmove and icons
+ return
+
+/mob/proc/SetStunned(amount) //if you REALLY need to set stun to a set amount without the whole "can't go below current stunned"
+ if(status_flags & CANSTUN)
+ stunned = max(amount,0)
+ update_canmove() //updates lying, canmove and icons
+ return
+
+/mob/proc/AdjustStunned(amount)
+ if(status_flags & CANSTUN)
+ stunned = max(stunned + amount,0)
+ update_canmove() //updates lying, canmove and icons
+ return
+
+/mob/proc/Weaken(amount)
+ if(status_flags & CANWEAKEN)
+ facing_dir = null
+ weakened = max(max(weakened,amount),0)
+ update_canmove() //updates lying, canmove and icons
+ return
+
+/mob/proc/SetWeakened(amount)
+ if(status_flags & CANWEAKEN)
+ weakened = max(amount,0)
+ update_canmove() //can you guess what this does yet?
+ return
+
+/mob/proc/AdjustWeakened(amount)
+ if(status_flags & CANWEAKEN)
+ weakened = max(weakened + amount,0)
+ update_canmove() //updates lying, canmove and icons
+ return
+
+/mob/proc/Paralyse(amount)
+ if(status_flags & CANPARALYSE)
+ facing_dir = null
+ paralysis = max(max(paralysis,amount),0)
+ return
+
+/mob/proc/SetParalysis(amount)
+ if(status_flags & CANPARALYSE)
+ paralysis = max(amount,0)
+ return
+
+/mob/proc/AdjustParalysis(amount)
+ if(status_flags & CANPARALYSE)
+ paralysis = max(paralysis + amount,0)
+ return
+
+/mob/proc/Sleeping(amount)
+ facing_dir = null
+ sleeping = max(max(sleeping,amount),0)
+ return
+
+/mob/proc/SetSleeping(amount)
+ sleeping = max(amount,0)
+ return
+
+/mob/proc/AdjustSleeping(amount)
+ sleeping = max(sleeping + amount,0)
+ return
+
+/mob/proc/Confuse(amount)
+ confused = max(max(confused,amount),0)
+ return
+
+/mob/proc/SetConfused(amount)
+ confused = max(amount,0)
+ return
+
+/mob/proc/AdjustConfused(amount)
+ confused = max(confused + amount,0)
+ return
+
+/mob/proc/Blind(amount)
+ eye_blind = max(max(eye_blind,amount),0)
+ return
+
+/mob/proc/SetBlinded(amount)
+ eye_blind = max(amount,0)
+ return
+
+/mob/proc/AdjustBlinded(amount)
+ eye_blind = max(eye_blind + amount,0)
+ return
+
+/mob/proc/Resting(amount)
+ facing_dir = null
+ resting = max(max(resting,amount),0)
+ update_canmove()
+ return
+
+/mob/proc/SetResting(amount)
+ resting = max(amount,0)
+ update_canmove()
+ return
+
+/mob/proc/AdjustResting(amount)
+ resting = max(resting + amount,0)
+ update_canmove()
+ return
+
+/mob/proc/AdjustLosebreath(amount)
+ losebreath = CLAMP(losebreath + amount, 0, 25)
+
+/mob/proc/SetLosebreath(amount)
+ losebreath = CLAMP(amount, 0, 25)
+
+/mob/proc/get_species()
+ return ""
+
+/mob/proc/flash_weak_pain()
+ flick("weak_pain",pain)
+
+/mob/proc/get_visible_implants(var/class = 0)
+ var/list/visible_implants = list()
+ for(var/obj/item/O in embedded)
+ if(O.w_class > class)
+ visible_implants += O
+ return visible_implants
+
+/mob/proc/embedded_needs_process()
+ return (embedded.len > 0)
+
+/mob/proc/yank_out_object()
+ set category = "Object"
+ set name = "Yank out object"
+ set desc = "Remove an embedded item at the cost of bleeding and pain."
+ set src in view(1)
+
+ if(!isliving(usr) || !usr.checkClickCooldown())
+ return
+ usr.setClickCooldown(20)
+
+ if(usr.stat == 1)
+ to_chat(usr, "You are unconcious and cannot do that!")
+ return
+
+ if(usr.restrained())
+ to_chat(usr, "You are restrained and cannot do that!")
+ return
+
+ var/mob/S = src
+ var/mob/U = usr
+ var/list/valid_objects = list()
+ var/self = null
+
+ if(S == U)
+ self = 1 // Removing object from yourself.
+
+ valid_objects = get_visible_implants(0)
+ if(!valid_objects.len)
+ if(self)
+ to_chat(src, "You have nothing stuck in your body that is large enough to remove.")
+ else
+ to_chat(U, "[src] has nothing stuck in their wounds that is large enough to remove.")
+ return
+
+ var/obj/item/weapon/selection = tgui_input_list(usr, "What do you want to yank out?", "Embedded objects", valid_objects)
+
+ if(self)
+ to_chat(src, "You attempt to get a good grip on [selection] in your body.")
+ else
+ to_chat(U, "You attempt to get a good grip on [selection] in [S]'s body.")
+
+ if(!do_after(U, 30))
+ return
+ if(!selection || !S || !U)
+ return
+
+ if(self)
+ visible_message("[src] rips [selection] out of their body.","You rip [selection] out of your body.")
+ else
+ visible_message("[usr] rips [selection] out of [src]'s body.","[usr] rips [selection] out of your body.")
+ valid_objects = get_visible_implants(0)
+ if(valid_objects.len == 1) //Yanking out last object - removing verb.
+ src.verbs -= /mob/proc/yank_out_object
+ clear_alert("embeddedobject")
+
+ if(ishuman(src))
+ var/mob/living/carbon/human/H = src
+ var/obj/item/organ/external/affected
+
+ for(var/obj/item/organ/external/organ in H.organs) //Grab the organ holding the implant.
+ for(var/obj/item/O in organ.implants)
+ if(O == selection)
+ affected = organ
+
+ affected.implants -= selection
+ H.shock_stage+=20
+ affected.take_damage((selection.w_class * 3), 0, 0, 1, "Embedded object extraction")
+
+ if(prob(selection.w_class * 5) && (affected.robotic < ORGAN_ROBOT)) //I'M SO ANEMIC I COULD JUST -DIE-.
+ var/datum/wound/internal_bleeding/I = new (min(selection.w_class * 5, 15))
+ affected.wounds += I
+ H.custom_pain("Something tears wetly in your [affected] as [selection] is pulled free!", 50)
+
+ if (ishuman(U))
+ var/mob/living/carbon/human/human_user = U
+ human_user.bloody_hands(H)
+
+ else if(issilicon(src))
+ var/mob/living/silicon/robot/R = src
+ R.embedded -= selection
+ R.adjustBruteLoss(5)
+ R.adjustFireLoss(10)
+
+ selection.forceMove(get_turf(src))
+ U.put_in_hands(selection)
+
+ for(var/obj/item/weapon/O in pinned)
+ if(O == selection)
+ pinned -= O
+ if(!pinned.len)
+ anchored = FALSE
+ return 1
+
+//Check for brain worms in head.
+/mob/proc/has_brain_worms()
+
+ for(var/I in contents)
+ if(istype(I,/mob/living/simple_mob/animal/borer))
+ return I
+
+ return 0
+
+/mob/proc/updateicon()
+ return
+
+// Please always use this proc, never just set the var directly.
+/mob/proc/set_stat(var/new_stat)
+ . = (stat != new_stat)
+ stat = new_stat
+
+/mob/verb/face_direction()
+
+ set name = "Face Direction"
+ set category = "IC"
+ set src = usr
+
+ set_face_dir()
+
+ if(!facing_dir)
+ to_chat(usr, "You are now not facing anything.")
+ else
+ to_chat(usr, "You are now facing [dir2text(facing_dir)].")
+
+/mob/proc/set_face_dir(var/newdir)
+ if(newdir == facing_dir)
+ facing_dir = null
+ else if(newdir)
+ set_dir(newdir)
+ facing_dir = newdir
+ else if(facing_dir)
+ facing_dir = null
+ else
+ set_dir(dir)
+ facing_dir = dir
+
+/mob/set_dir()
+ if(facing_dir)
+ if(!canface() || lying || buckled || restrained())
+ facing_dir = null
+ else if(dir != facing_dir)
+ return ..(facing_dir)
+ else
+ return ..()
+
+/mob/verb/northfaceperm()
+ set hidden = 1
+ set_face_dir(client.client_dir(NORTH))
+
+/mob/verb/southfaceperm()
+ set hidden = 1
+ set_face_dir(client.client_dir(SOUTH))
+
+/mob/verb/eastfaceperm()
+ set hidden = 1
+ set_face_dir(client.client_dir(EAST))
+
+/mob/verb/westfaceperm()
+ set hidden = 1
+ set_face_dir(client.client_dir(WEST))
+
+// Begin VOREstation edit
+/mob/verb/shiftnorth()
+ set hidden = TRUE
+ if(!canface())
+ return FALSE
+ if(pixel_y <= (default_pixel_y + 16))
+ pixel_y++
+ is_shifted = TRUE
+
+/mob/verb/shiftsouth()
+ set hidden = TRUE
+ if(!canface())
+ return FALSE
+ if(pixel_y >= (default_pixel_y - 16))
+ pixel_y--
+ is_shifted = TRUE
+
+/mob/verb/shiftwest()
+ set hidden = TRUE
+ if(!canface())
+ return FALSE
+ if(pixel_x >= (default_pixel_x - 16))
+ pixel_x--
+ is_shifted = TRUE
+
+/mob/verb/shifteast()
+ set hidden = TRUE
+ if(!canface())
+ return FALSE
+ if(pixel_x <= (default_pixel_x + 16))
+ pixel_x++
+ is_shifted = TRUE
+// End VOREstation edit
+
+/mob/proc/adjustEarDamage()
+ return
+
+/mob/proc/setEarDamage()
+ return
+
+// Set client view distance (size of client's screen). Returns TRUE if anything changed.
+/mob/proc/set_viewsize(var/new_view = world.view)
+ if (client && new_view != client.view)
+ client.view = new_view
+ return TRUE
+ return FALSE
+
+//Throwing stuff
+
+/mob/proc/toggle_throw_mode()
+ if (src.in_throw_mode)
+ throw_mode_off()
+ else
+ throw_mode_on()
+
+/mob/proc/throw_mode_off()
+ src.in_throw_mode = 0
+ if(src.throw_icon) //in case we don't have the HUD and we use the hotkey
+ src.throw_icon.icon_state = "act_throw_off"
+
+/mob/proc/throw_mode_on()
+ src.in_throw_mode = 1
+ if(src.throw_icon)
+ src.throw_icon.icon_state = "act_throw_on"
+
+/mob/proc/isSynthetic()
+ return 0
+
+/mob/proc/is_muzzled()
+ return 0
+
+//Exploitable Info Update
+
+/mob/proc/amend_exploitable(var/obj/item/I)
+ if(istype(I))
+ exploit_addons |= I
+ var/exploitmsg = html_decode("\n" + "Has " + I.name + ".")
+ exploit_record += exploitmsg
+
+/client/proc/check_has_body_select()
+ return mob && mob.hud_used && istype(mob.zone_sel, /obj/screen/zone_sel)
+
+/client/verb/body_toggle_head()
+ set name = "body-toggle-head"
+ set hidden = 1
+ toggle_zone_sel(list(BP_HEAD, O_EYES, O_MOUTH))
+
+/client/verb/body_r_arm()
+ set name = "body-r-arm"
+ set hidden = 1
+ toggle_zone_sel(list(BP_R_ARM,BP_R_HAND))
+
+/client/verb/body_l_arm()
+ set name = "body-l-arm"
+ set hidden = 1
+ toggle_zone_sel(list(BP_L_ARM,BP_L_HAND))
+
+/client/verb/body_chest()
+ set name = "body-chest"
+ set hidden = 1
+ toggle_zone_sel(list(BP_TORSO))
+
+/client/verb/body_groin()
+ set name = "body-groin"
+ set hidden = 1
+ toggle_zone_sel(list(BP_GROIN))
+
+/client/verb/body_r_leg()
+ set name = "body-r-leg"
+ set hidden = 1
+ toggle_zone_sel(list(BP_R_LEG,BP_R_FOOT))
+
+/client/verb/body_l_leg()
+ set name = "body-l-leg"
+ set hidden = 1
+ toggle_zone_sel(list(BP_L_LEG,BP_L_FOOT))
+
+/client/proc/toggle_zone_sel(list/zones)
+ if(!check_has_body_select())
+ return
+ var/obj/screen/zone_sel/selector = mob.zone_sel
+ selector.set_selected_zone(next_in_list(mob.zone_sel.selecting,zones))
+
+// This handles setting the client's color variable, which makes everything look a specific color.
+// This proc is here so it can be called without needing to check if the client exists, or if the client relogs.
+// This is for inheritence since /mob/living will serve most cases. If you need ghosts to use this you'll have to implement that yourself.
+/mob/proc/update_client_color()
+ if(client && client.color)
+ animate(client, color = null, time = 10)
+ return
+
+/mob/proc/swap_hand()
+ return
+
+//Throwing stuff
+/mob/proc/throw_item(atom/target)
+ return FALSE
+
+/mob/proc/will_show_tooltip()
+ if(alpha <= EFFECTIVE_INVIS)
+ return FALSE
+ return TRUE
+
+/mob/MouseEntered(location, control, params)
+ if(usr != src && usr.is_preference_enabled(/datum/client_preference/mob_tooltips) && src.will_show_tooltip())
+ openToolTip(user = usr, tip_src = src, params = params, title = get_nametag_name(usr), content = get_nametag_desc(usr))
+
+ ..()
+
+/mob/MouseDown()
+ closeToolTip(usr) //No reason not to, really
+
+ ..()
+
+/mob/MouseExited()
+ closeToolTip(usr) //No reason not to, really
+
+ ..()
+
+// Manages a global list of mobs with clients attached, indexed by z-level.
+/mob/proc/update_client_z(new_z) // +1 to register, null to unregister.
+ if(registered_z != new_z)
+ if(registered_z)
+ GLOB.players_by_zlevel[registered_z] -= src
+ if(client)
+ if(new_z)
+ GLOB.players_by_zlevel[new_z] += src
+ registered_z = new_z
+ else
+ registered_z = null
+
+GLOBAL_LIST_EMPTY_TYPED(living_players_by_zlevel, /list)
+/mob/living/update_client_z(new_z)
+ var/precall_reg_z = registered_z
+ . = ..() // will update registered_z if necessary
+ if(precall_reg_z != registered_z) // parent did work, let's do work too
+ if(precall_reg_z)
+ GLOB.living_players_by_zlevel[precall_reg_z] -= src
+ if(registered_z)
+ GLOB.living_players_by_zlevel[registered_z] += src
+
+/mob/onTransitZ(old_z, new_z)
+ ..()
+ update_client_z(new_z)
+
+/mob/cloak()
+ . = ..()
+ if(client && cloaked_selfimage)
+ client.images += cloaked_selfimage
+
+/mob/uncloak()
+ if(client && cloaked_selfimage)
+ client.images -= cloaked_selfimage
+ return ..()
+
+/mob/get_cloaked_selfimage()
+ var/icon/selficon = getCompoundIcon(src)
+ selficon.MapColors(0,0,0, 0,0,0, 0,0,0, 1,1,1) //White
+ var/image/selfimage = image(selficon)
+ selfimage.color = "#0000FF"
+ selfimage.alpha = 100
+ selfimage.layer = initial(layer)
+ selfimage.plane = initial(plane)
+ selfimage.loc = src
+
+ return selfimage
+
+/mob/proc/GetAltName()
+ return ""
+
+/mob/proc/get_ghost(even_if_they_cant_reenter = 0)
+ if(mind)
+ return mind.get_ghost(even_if_they_cant_reenter)
+
+/mob/proc/grab_ghost(force)
+ if(mind)
+ return mind.grab_ghost(force = force)
+>>>>>>> f1417f9455... Merge pull request #11560 from VOREStation/upstream-merge-8247