/mob/Destroy()//This makes sure that mobs with clients/keys are not just deleted from the game. GLOB.mob_list -= src GLOB.dead_mob_list -= src GLOB.alive_mob_list -= src input_focus = null if(s_active) s_active.close(src) if(!QDELETED(hud_used)) QDEL_NULL(hud_used) if(mind && mind.current == src) spellremove() mobspellremove() QDEL_LIST_CONTENTS(viruses) QDEL_LIST_CONTENTS(actions) for(var/alert in alerts) clear_alert(alert) ghostize() QDEL_LIST_ASSOC_VAL(tkgrabbed_objects) for(var/I in tkgrabbed_objects) qdel(tkgrabbed_objects[I]) tkgrabbed_objects = null if(buckled) buckled.unbuckle_mob(src, force = TRUE) if(viewing_alternate_appearances) for(var/datum/alternate_appearance/AA in viewing_alternate_appearances) AA.viewers -= src viewing_alternate_appearances = null runechat_msg_location = null if(length(observers)) for(var/mob/dead/observe as anything in observers) observe.reset_perspective(null) return ..() /mob/Initialize(mapload) GLOB.mob_list += src if(stat == DEAD) GLOB.dead_mob_list += src else GLOB.alive_mob_list += src input_focus = src reset_perspective(src) prepare_huds() update_runechat_msg_location() ADD_TRAIT(src, TRAIT_CAN_STRIP, TRAIT_GENERIC) . = ..() /atom/proc/prepare_huds() hud_list = list() var/static/list/hud_dmis = list( SPECIALROLE_HUD = 'icons/mob/hud/antaghud.dmi', DIAG_TRACK_HUD = 'icons/mob/hud/diaghud.dmi', DIAG_AIRLOCK_HUD = 'icons/mob/hud/diaghud.dmi', DIAG_STAT_HUD = 'icons/mob/hud/diaghud.dmi', DIAG_HUD = 'icons/mob/hud/diaghud.dmi', DIAG_BATT_HUD = 'icons/mob/hud/diaghud.dmi', DIAG_MECH_HUD = 'icons/mob/hud/diaghud.dmi', DIAG_BOT_HUD = 'icons/mob/hud/diaghud.dmi', PLANT_NUTRIENT_HUD = 'icons/mob/hud/hydrohud.dmi', PLANT_WATER_HUD = 'icons/mob/hud/hydrohud.dmi', PLANT_STATUS_HUD = 'icons/mob/hud/hydrohud.dmi', PLANT_HEALTH_HUD = 'icons/mob/hud/hydrohud.dmi', PLANT_TOXIN_HUD = 'icons/mob/hud/hydrohud.dmi', PLANT_PEST_HUD = 'icons/mob/hud/hydrohud.dmi', PLANT_WEED_HUD = 'icons/mob/hud/hydrohud.dmi', HEALTH_HUD = 'icons/mob/hud/medhud.dmi', STATUS_HUD = 'icons/mob/hud/medhud.dmi', ID_HUD = 'icons/mob/hud/sechud.dmi', WANTED_HUD = 'icons/mob/hud/sechud.dmi', IMPMINDSHIELD_HUD = 'icons/mob/hud/sechud.dmi', IMPCHEM_HUD = 'icons/mob/hud/sechud.dmi', IMPTRACK_HUD = 'icons/mob/hud/sechud.dmi', PRESSURE_HUD = 'icons/effects/effects.dmi', MALF_AI_HUD = 'icons/mob/hud/malfhud.dmi', ) for(var/hud in hud_possible) var/use_this_dmi = hud_dmis[hud] if(!use_this_dmi) use_this_dmi = 'icons/mob/hud/hud_misc.dmi' var/image/I = image(use_this_dmi, src, "") I.appearance_flags = RESET_COLOR | RESET_TRANSFORM hud_list[hud] = I /mob/proc/generate_name() return name /mob/proc/GetAltName() return "" /mob/proc/Cell() set category = "Admin" set hidden = 1 var/turf/T = get_turf(src) var/datum/gas_mixture/environment = T.get_readonly_air() if(!environment) return var/t = "Coordinates: [x],[y] \n" t+= "Temperature: [environment.temperature()] \n" t+= "Nitrogen: [environment.nitrogen()] \n" t+= "Oxygen: [environment.oxygen()] \n" t+= "Plasma : [environment.toxins()] \n" t+= "Carbon Dioxide: [environment.carbon_dioxide()] \n" t+= "N2O: [environment.sleeping_agent()] \n" t+= "Agent B: [environment.agent_b()] \n" usr.show_message(t, EMOTE_VISIBLE) /mob/proc/show_message(msg, type, alt, alt_type, chat_message_type) // Message, type of message (1 or 2), alternative message, alt message type (1 or 2) #ifndef GAME_TESTS if(!client) return #endif if(type) if(type & EMOTE_VISIBLE && !has_vision(information_only = TRUE)) // Vision related if(!alt) return msg = alt type = alt_type if(type & EMOTE_AUDIBLE && !can_hear()) // Hearing related if(!alt) return msg = alt type = alt_type if(type & EMOTE_VISIBLE && !has_vision(information_only = TRUE)) return // Added voice muffling for Issue 41. if(stat == UNCONSCIOUS) to_chat(src, "... You can almost hear someone talking ...", MESSAGE_TYPE_LOCALCHAT) return to_chat(src, msg, chat_message_type) // Show a message to all mobs 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(message, self_message, blind_message, chat_message_type) if(!isturf(loc)) // mobs inside objects (such as lockers) shouldn't have their actions visible to those outside the object for(var/mob/M as anything in get_mobs_in_view(3, src, ai_eyes = AI_EYE_INCLUDE)) if(M.see_invisible < invisibility) continue //can't view the invisible var/msg = message if(self_message && M == src) msg = self_message if(M.loc != loc) if(!blind_message) // for some reason VISIBLE action has blind_message param so if we are not in the same object but next to it, lets show it continue msg = blind_message M.show_message(msg, EMOTE_VISIBLE, blind_message, EMOTE_AUDIBLE, chat_message_type) return for(var/mob/M as anything in get_mobs_in_view(7, src, ai_eyes = AI_EYE_INCLUDE)) if(M.see_invisible < invisibility) continue //can't view the invisible var/msg = message if(self_message && M == src) msg = self_message M.show_message(msg, EMOTE_VISIBLE, blind_message, EMOTE_AUDIBLE, chat_message_type) // Show a message to all mobs in sight of this atom // Use for objects performing visible actions // message is output to anyone who can see, e.g. "The [src] does something!" // blind_message (optional) is what blind people will hear e.g. "You hear something!" /atom/proc/visible_message(message, blind_message) for(var/mob/M as anything in get_mobs_in_view(7, src, ai_eyes = AI_EYE_INCLUDE)) #ifndef GAME_TESTS if(!M.client) continue #endif M.show_message(message, EMOTE_VISIBLE, blind_message, EMOTE_AUDIBLE) // Show a message to all mobs 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(message, deaf_message, hearing_distance) var/range = 7 if(hearing_distance) range = hearing_distance var/msg = message for(var/mob/M as anything in get_mobs_in_view(range, src, ai_eyes = AI_EYE_REQUIRE_HEAR)) M.show_message(msg, EMOTE_AUDIBLE, deaf_message, EMOTE_VISIBLE) // based on say code var/omsg = replacetext(message, "[src] ", "") var/list/listening_obj = list() for(var/atom/movable/A in view(range, src)) if(ismob(A)) var/mob/M = A for(var/obj/O in M.contents) listening_obj |= O else if(isobj(A)) var/obj/O = A listening_obj |= O for(var/obj/O in listening_obj) O.hear_message(src, omsg) // Show a message to all mobs in earshot of this atom // Use for objects performing audible actions // message is the message output to anyone who can hear. // deaf_message (optional) is what deaf people will see. // hearing_distance (optional) is the range, how many tiles away the message can be heard. /atom/proc/audible_message(message, deaf_message, hearing_distance) var/range = 7 if(hearing_distance) range = hearing_distance for(var/mob/M as anything in get_mobs_in_view(range, src, ai_eyes = AI_EYE_REQUIRE_HEAR)) M.show_message(message, EMOTE_AUDIBLE, deaf_message, EMOTE_VISIBLE) /mob/proc/findname(msg) for(var/mob/M in GLOB.mob_list) if(M.real_name == "[msg]") return M return 0 /mob/proc/movement_delay() return 0 //This proc is called whenever someone clicks an inventory ui slot. /mob/proc/attack_ui(slot) var/obj/item/W = get_active_hand() if(istype(W)) equip_to_slot_if_possible(W, slot) else if(!restrained()) W = get_item_by_slot(slot) if(W) W.attack_hand(src) if(ishuman(src) && W == src:head) src:update_hair() src:update_fhair() /mob/proc/put_in_any_hand_if_possible(obj/item/W as obj, del_on_fail = 0, disable_warning = 1) if(equip_to_slot_if_possible(W, ITEM_SLOT_LEFT_HAND, del_on_fail, disable_warning)) return 1 else if(equip_to_slot_if_possible(W, ITEM_SLOT_RIGHT_HAND, del_on_fail, disable_warning)) return 1 return 0 //This is a SAFE proc. Use this instead of equip_to_slot()! //set del_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. /mob/proc/equip_to_slot_if_possible(obj/item/W, slot, del_on_fail = FALSE, disable_warning = FALSE, initial = FALSE) if(!istype(W)) return FALSE if(!W.mob_can_equip(src, slot, disable_warning)) if(del_on_fail) qdel(W) else if(!disable_warning) to_chat(src, "You are unable to equip that.")//Only print if del_on_fail is false return FALSE equip_to_slot(W, slot, initial) //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 eqip 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, initial = FALSE) return //This is just a commonly used configuration for the equip_to_slot_if_possible() proc, used to equip people when the rounds tarts and when events happen and such. /mob/proc/equip_to_slot_or_del(obj/item/W, slot, initial = FALSE) return equip_to_slot_if_possible(W, slot, TRUE, TRUE, initial) // Convinience proc. Collects crap that fails to equip either onto the mob's back, or drops it. // Used in job equipping so shit doesn't pile up at the start loc. /mob/living/carbon/human/proc/equip_or_collect(obj/item/W, slot, initial = FALSE) if(W.mob_can_equip(src, slot, TRUE)) //Mob can equip. Equip it. equip_to_slot_or_del(W, slot, initial) else //Mob can't equip it. Put it their backpack or toss it on the floor if(isstorage(back)) var/obj/item/storage/S = back //Now, S represents a container we can insert W into. S.handle_item_insertion(W, src, TRUE, TRUE) return S if(ismodcontrol(back)) var/obj/item/mod/control/C = back if(C.bag) C.bag.handle_item_insertion(W, src, TRUE, TRUE) return C.bag var/turf/T = get_turf(src) if(istype(T)) W.forceMove(T) return T //The list of slots by priority. equip_to_appropriate_slot() uses this list. Doesn't matter if a mob type doesn't have a slot. GLOBAL_LIST_INIT(slot_equipment_priority, list( \ ITEM_SLOT_BACK,\ ITEM_SLOT_PDA,\ ITEM_SLOT_ID,\ ITEM_SLOT_JUMPSUIT,\ ITEM_SLOT_OUTER_SUIT,\ ITEM_SLOT_MASK,\ ITEM_SLOT_NECK,\ ITEM_SLOT_HEAD,\ ITEM_SLOT_SHOES,\ ITEM_SLOT_GLOVES,\ ITEM_SLOT_LEFT_EAR,\ ITEM_SLOT_RIGHT_EAR,\ ITEM_SLOT_EYES,\ ITEM_SLOT_BELT,\ ITEM_SLOT_SUIT_STORE,\ ITEM_SLOT_ACCESSORY,\ ITEM_SLOT_LEFT_POCKET,\ ITEM_SLOT_RIGHT_POCKET\ )) //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 for(var/slot in GLOB.slot_equipment_priority) if(isstorage(W) && slot == ITEM_SLOT_HEAD) // Storage items should be put on the belt before the head continue if(W.prefered_slot_flags && !(W.prefered_slot_flags & slot)) //If there's a prefered slot flags list, make sure this slot is in it continue if(equip_to_slot_if_possible(W, slot, FALSE, TRUE)) //del_on_fail = 0; disable_warning = 0 return 1 return 0 /mob/proc/check_for_open_slot(obj/item/W) if(!istype(W)) return 0 var/openslot = 0 for(var/slot in GLOB.slot_equipment_priority) if(W.mob_check_equip(src, slot, 1) == 1) openslot = 1 break return openslot /obj/item/proc/mob_check_equip(M as mob, slot, disable_warning = 0) if(!M) return 0 if(!slot) return 0 if(ishuman(M)) //START HUMAN var/mob/living/carbon/human/H = M switch(slot) if(ITEM_SLOT_LEFT_HAND) if(H.l_hand) return 0 return 1 if(ITEM_SLOT_RIGHT_HAND) if(H.r_hand) return 0 return 1 if(ITEM_SLOT_MASK) if(!(slot_flags & ITEM_SLOT_MASK)) return 0 if(H.wear_mask) return 0 return 1 if(ITEM_SLOT_BACK) if(!(slot_flags & ITEM_SLOT_BACK)) return 0 if(H.back) if(!(H.back.flags & NODROP)) return 2 else return 0 return 1 if(ITEM_SLOT_OUTER_SUIT) if(!(slot_flags & ITEM_SLOT_OUTER_SUIT)) return 0 if(H.wear_suit) if(!(H.wear_suit.flags & NODROP)) return 2 else return 0 return 1 if(ITEM_SLOT_GLOVES) if(!(slot_flags & ITEM_SLOT_GLOVES)) return 0 if(H.gloves) if(!(H.gloves.flags & NODROP)) return 2 else return 0 return 1 if(ITEM_SLOT_NECK) if(!(slot_flags & ITEM_SLOT_NECK)) return 0 if(H.neck) if(!(H.neck.flags & NODROP)) return 2 else return 0 return 1 if(ITEM_SLOT_SHOES) if(!(slot_flags & ITEM_SLOT_SHOES)) return 0 if(H.shoes) if(!(H.shoes.flags & NODROP)) return 2 else return 0 return 1 if(ITEM_SLOT_BELT) if(!H.w_uniform) if(!disable_warning) to_chat(H, "You need a jumpsuit before you can attach this [name].") return 0 if(!(slot_flags & ITEM_SLOT_BELT)) return 0 if(H.belt) if(!(H.belt.flags & NODROP)) return 2 else return 0 return 1 if(ITEM_SLOT_EYES) if(!(slot_flags & ITEM_SLOT_EYES)) return 0 if(H.glasses) if(!(H.glasses.flags & NODROP)) return 2 else return 0 return 1 if(ITEM_SLOT_HEAD) if(!(slot_flags & ITEM_SLOT_HEAD)) return 0 if(H.head) if(!(H.head.flags & NODROP)) return 2 else return 0 return 1 if(ITEM_SLOT_LEFT_EAR) if(!(slot_flags & ITEM_SLOT_LEFT_EAR)) return 0 if(H.l_ear) if(!(H.l_ear.flags & NODROP)) return 2 else return 0 return 1 if(ITEM_SLOT_RIGHT_EAR) if(!(slot_flags & ITEM_SLOT_RIGHT_EAR)) return 0 if(H.r_ear) if(!(H.r_ear.flags & NODROP)) return 2 else return 0 return 1 if(ITEM_SLOT_JUMPSUIT) if(!(slot_flags & ITEM_SLOT_JUMPSUIT)) return 0 if(H.w_uniform) if(!(H.w_uniform.flags & NODROP)) return 2 else return 0 return 1 if(ITEM_SLOT_ID) if(!H.w_uniform) if(!disable_warning) to_chat(H, "You need a jumpsuit before you can attach this [name].") return 0 if(!(slot_flags & ITEM_SLOT_ID)) return 0 if(H.wear_id) if(!(H.wear_id.flags & NODROP)) return 2 else return 0 return 1 if(ITEM_SLOT_LEFT_POCKET) if(H.l_store) return 0 if(!H.w_uniform) if(!disable_warning) to_chat(H, "You need a jumpsuit before you can attach this [name].") return 0 if(w_class <= WEIGHT_CLASS_SMALL || (slot_flags & ITEM_SLOT_BOTH_POCKETS)) return 1 if(ITEM_SLOT_RIGHT_POCKET) if(H.r_store) return 0 if(!H.w_uniform) if(!disable_warning) to_chat(H, "You need a jumpsuit before you can attach this [name].") return 0 if(w_class <= WEIGHT_CLASS_SMALL || (slot_flags & ITEM_SLOT_BOTH_POCKETS)) return 1 return 0 if(ITEM_SLOT_SUIT_STORE) if(!H.wear_suit) if(!disable_warning) to_chat(H, "You need a suit before you can attach this [name].") return 0 if(!H.wear_suit.allowed) if(!disable_warning) to_chat(usr, "You somehow have a suit with no defined allowed items for suit storage, stop that.") return 0 if(w_class > H.wear_suit.max_suit_w) if(!disable_warning) to_chat(usr, "The [name] is too big to attach.") return 0 if(is_pda(src) || is_pen(src) || is_type_in_list(src, H.wear_suit.allowed)) if(H.s_store) if(!(H.s_store.flags & NODROP)) return 2 else return 0 else return 1 return 0 if(ITEM_SLOT_HANDCUFFED) if(H.handcuffed) return 0 return istype(src, /obj/item/restraints/handcuffs) if(ITEM_SLOT_LEGCUFFED) if(H.legcuffed) return 0 return istype(src, /obj/item/restraints/legcuffs) if(ITEM_SLOT_IN_BACKPACK) if(H.back && istype(H.back, /obj/item/storage/backpack)) var/obj/item/storage/backpack/B = H.back if(length(B.contents) < B.storage_slots && w_class <= B.max_w_class) return 1 return 0 return 0 //Unsupported slot //END HUMAN /mob/proc/get_visible_mobs() var/list/seen_mobs = list() for(var/mob/M in view(src)) seen_mobs += M return seen_mobs /** * Returns an assoc list which contains the mobs in range and their "visible" name. * Mobs out of view but in range will be listed as unknown. Else they will have their visible name */ /mob/proc/get_telepathic_targets() var/list/validtargets = list() /list() var/turf/T = get_turf(src) var/list/mobs_in_view = get_visible_mobs() for(var/mob/living/M in range(14, T)) if(M && M.mind) if(M == src) continue var/mob_name if(M in mobs_in_view) mob_name = M.name else mob_name = "Unknown entity" var/i = 0 var/result_name do result_name = mob_name if(i++) result_name += " ([i])" // Avoid dupes while(validtargets[result_name]) validtargets[result_name] = M return validtargets // If you're looking for `reset_perspective`, that's a synonym for this proc. /mob/proc/reset_perspective(atom/A) if(client) if(istype(A, /atom/movable)) if(is_ventcrawling(src)) client.eye = get_turf(A) client.perspective = MOB_PERSPECTIVE else 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 1 /mob/living/reset_perspective(atom/A) . = ..() if(.) // Above check means the mob has a client update_sight() if(client.eye != src) var/atom/AT = client.eye AT.get_remote_view_fullscreens(src) else clear_fullscreen("remote_view", 0) update_pipe_vision() /mob/dead/reset_perspective(atom/A) . = ..() if(.) // Allows sharing HUDs with ghosts if(hud_used) client.clear_screen() hud_used.show_hud(hud_used.hud_version) //mob verbs are faster than object verbs. See http://www.byond.com/forum/?post=1326139&page=2#comment8198716 for why this isn't atom/verb/examine() /mob/verb/examinate(atom/A as mob|obj|turf in view()) set name = "Examine" set category = "IC" DEFAULT_QUEUE_OR_CALL_VERB(VERB_CALLBACK(src, PROC_REF(run_examinate), A)) /mob/proc/run_examinate(atom/A) if(QDELETED(A)) return if(A.invisibility > see_invisible) A = get_turf(A) if(!has_vision(information_only = TRUE) && !isobserver(src)) to_chat(src, chat_box_regular("Something is there but you can't see it."), MESSAGE_TYPE_INFO, confidential = TRUE) return TRUE face_atom(A) if(!client) var/list/result = A.examine(src) to_chat(src, chat_box_examine(result.Join("
"))) return var/list/result LAZYINITLIST(client.recent_examines) for(var/key in client.recent_examines) if(client.recent_examines[key] < world.time) client.recent_examines -= key var/ref_to_atom = A.UID() if(LAZYACCESS(client.recent_examines, ref_to_atom)) result = A.examine_more(src) if(!length(result)) result = A.examine(src) else result = A.examine(src) if(length(A.examine_more())) result += "You can examine [A.p_them()] again to take a closer look..." client.recent_examines[ref_to_atom] = world.time + EXAMINE_MORE_WINDOW // set to when we should not examine something broadcast_examine(A) to_chat(src, chat_box_examine(result.Join("\n")), MESSAGE_TYPE_INFO, confidential = TRUE) /// Tells nearby mobs about our examination. /mob/proc/broadcast_examine(atom/examined) if(examined == src) return // If TRUE, the usr's view() for the examined object too var/examining_worn_item = FALSE var/examining_stored_item = FALSE var/loc_str = "at something off in the distance." if(isitem(examined)) var/obj/item/I = examined if(I.in_storage) while(isstorage(I.loc)) // grab the top level storage item I = I.loc if(get(I, /mob/living) == src) if(istype(I, /obj/item/storage/hidden_implant)) // Don't annnounce items in a bluespace pocket. return loc_str = "inside [p_their()] [I.name]..." else loc_str = "inside [I]..." examining_stored_item = TRUE else if(I.loc == src) // Hide items in pockets. if(get_slot_by_item(I) & ITEM_SLOT_BOTH_POCKETS) loc_str = "inside [p_their()] pockets." else loc_str = "at [p_their()] [I.name]." examining_worn_item = TRUE var/can_see_str = "\The [src] looks at [examined]." if(examining_worn_item || examining_stored_item) can_see_str = "\The [src] looks [loc_str]" var/cannot_see_str = "\The [src] looks [loc_str]" var/list/can_see_target = hearers(examined) // Don't broadcast if we can't see the item. if(!(examining_stored_item || examining_worn_item) && !(src in can_see_target)) return for(var/mob/M as anything in viewers(2, src)) if(!M.client || M.stat != CONSCIOUS ||HAS_TRAIT(M, TRAIT_BLIND)) continue if(examining_worn_item || (M == src) || (M in can_see_target)) to_chat(M, can_see_str) else to_chat(M, cannot_see_str) /mob/living/broadcast_examine(atom/examined) if(stat != CONSCIOUS) return return ..() /mob/living/carbon/human/broadcast_examine(atom/examined) var/obj/item/glasses = get_item_by_slot(ITEM_SLOT_EYES) if(glasses && (HAS_TRAIT(glasses, TRAIT_HIDE_EXAMINE))) return var/obj/item/mask = get_item_by_slot(ITEM_SLOT_MASK) if(mask && ((mask.flags_inv & HIDEFACE) || HAS_TRAIT(mask, TRAIT_HIDE_EXAMINE))) return var/obj/item/head = get_item_by_slot(ITEM_SLOT_HEAD) if(head && ((head.flags_inv & HIDEFACE) || HAS_TRAIT(head, TRAIT_HIDE_EXAMINE))) return // We'll just assume if your eyes have tinted covering you can't see them very well. if(get_total_tint()) return return ..() /mob/dead/broadcast_examine(atom/examined) return //Observers arent real the government is lying to you /mob/living/silicon/ai/broadcast_examine(atom/examined) var/mob/living/silicon/ai/ai = src // Only show the AI's examines if they're in a holopad if(istype(ai.current, /obj/machinery/hologram/holopad)) return ..() /mob/proc/ret_grab(obj/effect/list_container/mobl/L as obj, flag) if((!istype(l_hand, /obj/item/grab) && !istype(r_hand, /obj/item/grab))) if(!L) return null else return L.container else if(!L) L = new /obj/effect/list_container/mobl( null ) L.container += src L.master = src if(istype(l_hand, /obj/item/grab)) var/obj/item/grab/G = l_hand if(!L.container.Find(G.affecting)) L.container += G.affecting if(G.affecting) G.affecting.ret_grab(L, 1) if(istype(r_hand, /obj/item/grab)) var/obj/item/grab/G = r_hand if(!L.container.Find(G.affecting)) L.container += G.affecting if(G.affecting) G.affecting.ret_grab(L, 1) if(!flag) if(L.master == src) var/list/temp = list() temp += L.container //L = null qdel(L) return temp else return L.container return /mob/verb/mode() set name = "Activate Held Object" set category = null DEFAULT_QUEUE_OR_CALL_VERB(VERB_CALLBACK(src, PROC_REF(run_mode))) ///proc version to finish /mob/verb/mode() execution. used in case the proc needs to be queued for the tick after its first called /mob/proc/run_mode() if(ismecha(loc)) return if(hand) var/obj/item/W = l_hand if(W) if(W.new_attack_chain) W.activate_self(src) else W.attack_self__legacy__attackchain(src) update_inv_l_hand() else var/obj/item/W = r_hand if(W) if(W.new_attack_chain) W.activate_self(src) else W.attack_self__legacy__attackchain(src) update_inv_r_hand() 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 = copytext(msg, 1, MAX_MESSAGE_LEN)
	msg = sanitize_simple(html_encode(msg), list("\n" = "
")) var/combined = length(memory + msg) if(mind && (combined < MAX_PAPER_MESSAGE_LEN)) mind.store_memory(msg) else if(combined >= MAX_PAPER_MESSAGE_LEN) to_chat(src, "Your brain can't hold that much information!") return 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() if(usr != src) to_chat(usr, "You can't change the flavor text of this mob") return if(stat) to_chat(usr, "You have to be conscious to change your flavor text") return var/msg = tgui_input_text(usr, "Set the flavor text in your 'examine' verb. The flavor text should be a physical descriptor of your character at a glance. SFW Drawn Art of your character is acceptable.", "Flavor Text", flavor_text, multiline = TRUE) if(isnull(msg)) return if(stat) to_chat(usr, "You have to be conscious to change your flavor text") return msg = copytext(msg, 1, MAX_MESSAGE_LEN) if(dna) dna.flavor_text = msg // Only carbon mobs have DNA. flavor_text = msg /mob/proc/print_flavor_text(shrink = TRUE) if(flavor_text && flavor_text != "") var/msg = dna?.flavor_text ? replacetext(dna.flavor_text, "\n", " ") : replacetext(flavor_text, "\n", " ") if(length(msg) <= MAX_FLAVORTEXT_PRINT || !shrink) return "[msg]" // There is already encoded by tgui_input else return "[copytext_preserve_html(msg, 1, MAX_FLAVORTEXT_PRINT - 3)]... More..." /mob/proc/is_dead() return stat == DEAD /mob/verb/cancel_camera() set name = "Cancel Camera View" set category = "OOC" reset_perspective(null) unset_machine() if(isliving(src)) if(src:camera_follow) src:camera_follow = null /mob/Topic(href, href_list) if(href_list["flavor_more"]) usr << browse(text("[][]", name, replacetext(flavor_text, "\n", "
")), "window=[name];size=500x200") onclose(usr, "[name]") if(href_list["flavor_change"]) update_flavor_text() if(href_list["station_report"]) var/obj/item/clipboard/station_report/report = GLOB.station_report if(!istype(report)) to_chat(src, "Nobody wrote a station report this shift!") else if(!report.toppaper) to_chat(src, "Nobody wrote a station report this shift!") else if(istype(report.toppaper, /obj/item/paper_bundle)) var/obj/item/paper_bundle/report_page = report.toppaper report_page.show_content(src) else if(istype(report.toppaper, /obj/item/paper)) var/obj/item/paper/report_page = report.toppaper report_page.show_content(src) if(usr != src) return TRUE if(..()) return TRUE if(href_list["mach_close"]) var/t1 = "window=[href_list["mach_close"]]" unset_machine() src << browse(null, t1) if(href_list["scoreboard"]) usr << browse(GLOB.scoreboard, "window=roundstats;size=500x600") /mob/MouseDrop(mob/M as mob, src_location, over_location, src_control, over_control, params) if((M != usr) || !istype(M)) ..() return if(isliving(M)) var/mob/living/L = M if(L.mob_size <= MOB_SIZE_SMALL) return // Stops pAI drones and small mobs (parrots, crabs) from stripping people. --DZD if(!HAS_TRAIT(M, TRAIT_CAN_STRIP)) return if(usr == src) return if(!Adjacent(usr)) return if(IsFrozen(src) && !is_admin(usr)) to_chat(usr, "Interacting with admin-frozen players is not permitted.") return if(isLivingSSD(src) && M.client && M.client.send_ssd_warning(src)) return SEND_SIGNAL(src, COMSIG_DO_MOB_STRIP, M, usr) /mob/proc/can_use_hands() return /mob/proc/is_mechanical() return mind && (mind.assigned_role == "Cyborg" || mind.assigned_role == "AI") /mob/proc/is_in_brig() if(!loc || !loc.loc) return 0 // They should be in a cell or the Brig portion of the shuttle. var/area/A = loc.loc if(!istype(A, /area/station/security/prison)) if(!istype(A, /area/shuttle/escape) || loc.name != "Brig floor") return 0 // If they still have their ID they're not brigged. for(var/obj/item/card/id/card in src) return 0 for(var/obj/item/pda/P in src) if(P.id) return 0 return 1 /mob/proc/get_gender() return gender /mob/proc/is_muzzled() return FALSE /mob/proc/get_status_tab_items() SHOULD_CALL_PARENT(TRUE) var/list/status_tab_data = list() if(check_rights(R_ADMIN, 0, src)) status_tab_data = SSstatpanels.admin_data.Copy() return status_tab_data // facing verbs /mob/proc/canface() if(client.moving) return FALSE if(stat == DEAD) return FALSE if(anchored) return FALSE if(notransform) return FALSE if(restrained()) return FALSE return TRUE /mob/living/canface() if(!(mobility_flags & MOBILITY_MOVE)) return FALSE . = ..() /mob/dead/canface() return TRUE /mob/proc/fall() drop_l_hand() drop_r_hand() /mob/living/fall() ..() set_body_position(LYING_DOWN) /mob/proc/facedir(ndir) if(!canface()) return FALSE setDir(ndir) return TRUE /mob/verb/eastface() set hidden = 1 return facedir(EAST) /mob/verb/westface() set hidden = 1 return facedir(WEST) /mob/verb/northface() set hidden = 1 return facedir(NORTH) /mob/verb/southface() set hidden = 1 return facedir(SOUTH) /mob/proc/IsAdvancedToolUser()//This might need a rename but it should replace the can this mob use things check return FALSE /mob/proc/swap_hand() return /mob/proc/activate_hand(selhand) return FALSE /mob/dead/observer/verb/respawn() set name = "Respawn as NPC" set category = "Ghost" if(jobban_isbanned(usr, ROLE_SENTIENT)) to_chat(usr, "You are banned from playing as sentient animals.") return if(SSticker.current_state < GAME_STATE_PLAYING) to_chat(src, "You can't respawn as an NPC before the game starts!") return if((HAS_TRAIT(usr, TRAIT_RESPAWNABLE)) && (stat == DEAD || isobserver(usr))) var/list/creatures = list("Mouse") for(var/mob/living/simple_animal/L in GLOB.alive_mob_list) if(!(is_station_level(L.z) || is_admin_level(L.z))) // Prevents players from spawning in space continue if(L.npc_safe(src) && L.stat != DEAD && !L.key) creatures += L // Dumb duplicate code until we have no more simple mobs for(var/mob/living/basic/B in GLOB.alive_mob_list) if(!(is_station_level(B.z) || is_admin_level(B.z))) // Prevents players from spawning in space continue if(B.valid_respawn_target_for(src) && B.stat != DEAD && !B.key) creatures += B var/picked = tgui_input_list(usr, "Please select an NPC to respawn as", "Respawn as NPC", creatures) switch(picked) if("Mouse") become_mouse() else var/mob/living/NPC = picked if(istype(NPC) && !NPC.key) NPC.key = key else to_chat(usr, "You are not dead or you have given up your right to be respawned!") return /** * Returns true if the player successfully becomes a mouse */ /mob/proc/become_mouse() var/timedifference = world.time - client.persistent.time_died_as_mouse if(client.persistent.time_died_as_mouse && timedifference <= GLOB.mouse_respawn_time * 600) var/timedifference_text = time2text(GLOB.mouse_respawn_time * 600 - timedifference,"mm:ss") to_chat(src, "You may only spawn again as a mouse more than [GLOB.mouse_respawn_time] minutes after your death. You have [timedifference_text] left.") return FALSE //find a viable mouse candidate var/list/found_vents = get_valid_vent_spawns() if(!length(found_vents)) found_vents = get_valid_vent_spawns(min_network_size = 0) if(!length(found_vents)) to_chat(src, "Unable to find any unwelded vents to spawn mice at.") return FALSE var/obj/vent_found = pick(found_vents) var/mob/living/basic/mouse/host = new(vent_found.loc) host.ckey = src.ckey to_chat(host, "You are now a mouse, a small and fragile creature capable of scurrying through vents and under doors. Be careful who you reveal yourself to, for that will decide whether you receive cheese or death.") host.forceMove(vent_found) host.add_ventcrawl(vent_found) return TRUE /mob/proc/assess_threat() //For sec bot threat assessment return 5 /mob/proc/get_ghost(even_if_they_cant_reenter = 0) if(mind) return mind.get_ghost(even_if_they_cant_reenter) /mob/proc/check_ghost_client() if(mind) return mind.check_ghost_client() /mob/proc/grab_ghost(force) if(mind) return mind.grab_ghost(force = force) /mob/proc/notify_ghost_cloning(message = "Someone is trying to revive you. Re-enter your corpse if you want to be revived!", sound = 'sound/effects/genetics.ogg', atom/source = null, flashwindow = TRUE) var/mob/dead/observer/ghost = get_ghost() if(ghost) if(flashwindow) window_flash(ghost.client) ghost.notify_cloning(message, sound, source) return ghost /mob/proc/fakevomit(green = 0, no_text = 0) //for aesthetic vomits that need to be instant and do not stun. -Fox if(stat==DEAD) return var/turf/location = loc if(issimulatedturf(location)) if(green) if(!no_text) visible_message("[src] vomits up some green goo!","You vomit up some green goo!") add_vomit_floor(FALSE, TRUE) else if(!no_text) visible_message("[src] pukes all over [p_themselves()]!","You puke all over yourself!") add_vomit_floor(TRUE) /mob/proc/AddSpell(datum/spell/S) mob_spell_list += S S.action.Grant(src) /mob/proc/RemoveSpell(datum/spell/spell) //To remove a specific spell from a mind if(!spell) return for(var/datum/spell/S in mob_spell_list) if(istype(S, spell)) qdel(S) mob_spell_list -= S /mob/proc/handle_ventcrawl() return // Only living mobs can ventcrawl /** * Buckle to another mob * * You can buckle on mobs if you're next to them since most are dense * * Turns you to face the other mob too */ /mob/buckle_mob(mob/living/M, force = FALSE, check_loc = TRUE) if(M.buckled) return 0 var/turf/T = get_turf(src) if(M.loc != T) var/old_density = density density = FALSE var/can_step = step_towards(M, T) density = old_density if(!can_step) return 0 return ..() ///Call back post buckle to a mob to offset your visual height /mob/post_buckle_mob(mob/living/M) var/height = M.get_mob_buckling_height(src) M.pixel_y = initial(M.pixel_y) + height if(M.layer < layer) M.layer = layer + 0.1 ///Call back post unbuckle from a mob, (reset your visual height here) /mob/post_unbuckle_mob(mob/living/M) M.layer = initial(M.layer) M.pixel_y = initial(M.pixel_y) ///returns the height in pixel the mob should have when buckled to another mob. /mob/proc/get_mob_buckling_height(mob/seat) if(isliving(seat)) var/mob/living/L = seat if(L.mob_size <= MOB_SIZE_SMALL) //being on top of a small mob doesn't put you very high. return 0 return 9 ///can the mob be buckled to something by default? /mob/proc/can_buckle() return TRUE ///can the mob be unbuckled from something by default? /mob/proc/can_unbuckle() return TRUE // Can the mob see reagents inside of transparent containers? /mob/proc/reagent_vision() return FALSE // Can the mob see reagents inside any container and also identify blood types? /mob/proc/advanced_reagent_vision() return FALSE //Can this mob leave its location without breaking things terrifically? /mob/proc/can_safely_leave_loc() return TRUE // Yes, you can /mob/proc/IsVocal() return TRUE /mob/proc/cannot_speak_loudly() return FALSE /mob/proc/get_access() return list() //must return list or IGNORE_ACCESS /mob/proc/create_attack_log(text, collapse = TRUE) LAZYINITLIST(attack_log_old) create_log_in_list(attack_log_old, text, collapse, last_log) last_log = world.timeofday /mob/proc/create_debug_log(text, collapse = TRUE) LAZYINITLIST(debug_log) create_log_in_list(debug_log, text, collapse, world.timeofday) /mob/proc/create_log(log_type, what, target = null, turf/where = get_turf(src), force_no_usr_check = FALSE, automatic = FALSE) if(!ckey) return var/real_ckey = ckey if(ckey[1] == "@") // Admin aghosting will do this real_ckey = copytext(ckey, 2) var/datum/log_record/record = new(log_type, src, what, target, where, world.time, force_no_usr_check, automatic) GLOB.logging.add_log(real_ckey, record) /proc/create_log_in_list(list/target, text, collapse = TRUE, last_log)//forgive me code gods for this shitcode proc //this proc enables lovely stuff like an attack log that looks like this: "[18:20:29-18:20:45]21x John Smith attacked Andrew Jackson with a crowbar." //That makes the logs easier to read, but because all of this is stored in strings, weird things have to be used to get it all out. var/new_log = "\[[time_stamp()]] [text]" if(length(target))//if there are other logs already present var/previous_log = target[length(target)]//get the latest log var/last_log_is_range = (copytext(previous_log, 10, 11) == "-") //whether the last log is a time range or not. The "-" will be an indicator that it is. var/x_sign_position = findtext(previous_log, "x") if(world.timeofday - last_log > 100)//if more than 10 seconds from last log collapse = 0//don't collapse anyway //the following checks if the last log has the same contents as the new one if(last_log_is_range) if(!(copytext(previous_log, x_sign_position + 13) == text))//the 13 is there because of span classes; you won't see those normally in-game collapse = 0 else if(!(copytext(previous_log, 12) == text)) collapse = 0 if(collapse == 1) var/rep = 0 var/old_timestamp = copytext(previous_log, 2, 10)//copy the first time value. This one doesn't move when it's a timespan, so no biggie //An attack log entry can either be a time range with multiple occurences of an action or a single one, with just one time stamp if(last_log_is_range) rep = text2num(copytext(previous_log, 44, x_sign_position))//get whatever number is right before the 'x' new_log = "\[[old_timestamp]-[time_stamp()]][rep?rep+1:2]x [text]" target -= target[length(target)]//remove the last log target += new_log ///Can this mob resist (default FALSE) /mob/proc/can_resist() return FALSE //overridden in living.dm /mob/proc/spin(spintime, speed) set waitfor = FALSE if(!spintime || !speed || spintime > 100) CRASH("Aborted attempted call of /mob/proc/spin with invalid args ([spintime],[speed]) which could have frozen the server.") while(spintime >= speed) sleep(speed) switch(dir) if(NORTH) setDir(EAST) if(SOUTH) setDir(WEST) if(EAST) setDir(SOUTH) if(WEST) setDir(NORTH) spintime -= speed /mob/proc/is_literate() return FALSE /mob/proc/faction_check_mob(mob/target, exact_match) if(!target) return faction_check(faction, null, FALSE) if(exact_match) //if we need an exact match, we need to do some bullfuckery. var/list/faction_src = faction.Copy() var/list/faction_target = target.faction.Copy() if(!("\ref[src]" in faction_target)) //if they don't have our ref faction, remove it from our factions list. faction_src -= "\ref[src]" //if we don't do this, we'll never have an exact match. if(!("\ref[target]" in faction_src)) faction_target -= "\ref[target]" //same thing here. return faction_check(faction_src, faction_target, TRUE) return faction_check(faction, target.faction, FALSE) /proc/faction_check(list/faction_A, list/faction_B, exact_match) var/list/match_list if(exact_match) match_list = faction_A & faction_B //only items in both lists var/length = LAZYLEN(match_list) if(length) return (length == LAZYLEN(faction_A)) //if they're not the same len(gth) or we don't have a len, then this isn't an exact match. else match_list = faction_A & faction_B return LAZYLEN(match_list) return FALSE /mob/proc/update_sight() SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) sync_lighting_plane_alpha() /mob/proc/set_sight(datum/vision_override/O) QDEL_NULL(vision_type) if(O) //in case of null vision_type = new O update_sight() /mob/proc/sync_lighting_plane_alpha() if(hud_used) var/atom/movable/screen/plane_master/lighting/L = hud_used.plane_masters["[LIGHTING_PLANE]"] if(L) L.alpha = lighting_alpha var/atom/movable/screen/plane_master/smoke/S = hud_used.plane_masters["[SMOKE_PLANE]"] if(S) S.alpha = 255 if(sight & SEE_MOBS) S.alpha = 200 if((sight & SEE_TURFS|SEE_MOBS|SEE_OBJS) == (SEE_TURFS|SEE_MOBS|SEE_OBJS)) S.alpha = 128 sync_nightvision_screen() //Sync up the overlay used for nightvision to the amount of see_in_dark a mob has. This needs to be called everywhere sync_lighting_plane_alpha() is. /mob/proc/sync_nightvision_screen() var/atom/movable/screen/fullscreen/stretch/see_through_darkness/S = screens["see_through_darkness"] if(S) var/suffix = "" switch(see_in_dark) if(3 to 8) suffix = "_[see_in_dark]" if(8 to INFINITY) suffix = "_8" S.icon_state = "[initial(S.icon_state)][suffix]" ///Adjust the nutrition of a mob /mob/proc/adjust_nutrition(change) nutrition = max(0, nutrition + change) ///Force set the mob nutrition /mob/proc/set_nutrition(change) nutrition = max(0, change) /mob/clean_blood(radiation_clean = FALSE, clean_hands = TRUE, clean_mask = TRUE, clean_feet = TRUE) . = ..() if(bloody_hands && clean_hands) bloody_hands = 0 update_inv_gloves() if(l_hand) if(l_hand.clean_blood(radiation_clean)) update_inv_l_hand() if(r_hand) if(r_hand.clean_blood(radiation_clean)) update_inv_r_hand() if(back) if(back.clean_blood(radiation_clean)) update_inv_back() if(wear_mask && clean_mask) if(wear_mask.clean_blood(radiation_clean)) update_inv_wear_mask() if(clean_feet) feet_blood_color = null qdel(feet_blood_DNA) bloody_feet = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0, BLOOD_BASE_ALPHA = BLOODY_FOOTPRINT_BASE_ALPHA) blood_state = BLOOD_STATE_NOT_BLOODY update_inv_shoes() update_icons() //apply the now updated overlays to the mob /** * Updates the mob's runechat maptext display location. * * By default, we set this to the src mob's `UID()`. */ /mob/proc/update_runechat_msg_location() runechat_msg_location = UID() /** * Show an overlay of radiation levels on radioactive objects. */ /mob/proc/show_rads(range) for(var/turf/place in range(range, src)) var/list/rads = SSradiation.get_turf_radiation(place) if(!rads || (rads[1] + rads[2] + rads[3]) < RAD_BACKGROUND_RADIATION) continue var/alpha_strength = round(rads[1] / 1000, 0.1) var/beta_strength = round(rads[2] / 1000, 0.1) var/gamma_strength = round(rads[3] / 1000, 0.1) var/image/pic = image(loc = place) var/mutable_appearance/MA = new() MA.maptext = MAPTEXT("Α [alpha_strength]k\nΒ [beta_strength]k\nΓ [gamma_strength]k") MA.color = "#04e604" MA.layer = RAD_TEXT_LAYER MA.plane = GAME_PLANE pic.appearance = MA flick_overlay(pic, list(client), 10) GLOBAL_LIST_INIT(holy_areas, typecacheof(list( /area/station/service/chapel ))) /mob/proc/holy_check() if(!is_type_in_typecache(loc.loc, GLOB.holy_areas)) return FALSE if(!mind) return FALSE //Allows cult to bypass holy areas once they summon if(mind.has_antag_datum(/datum/antagonist/cultist) && SSticker.mode.cult_team.cult_status == NARSIE_HAS_RISEN) return FALSE //Execption for Holy Constructs if(isconstruct(src) && !mind.has_antag_datum(/datum/antagonist/cultist)) return FALSE to_chat(src, "Your powers are useless on this holy ground.") return TRUE /mob/proc/reset_visibility() invisibility = initial(invisibility) alpha = initial(alpha) add_to_all_human_data_huds() /mob/living/carbon/human/reset_visibility() ..() alpha = get_alpha() /mob/proc/make_invisible() invisibility = INVISIBILITY_LEVEL_TWO alpha = 128 remove_from_all_data_huds() /mob/proc/set_stat(new_stat) if(new_stat == stat) return . = stat stat = new_stat SEND_SIGNAL(src, COMSIG_MOB_STATCHANGE, new_stat, .) if(.) set_typing_indicator(FALSE) ///Makes a call in the context of a different usr. Use sparingly /world/proc/invoke_callback_with_usr(mob/user_mob, datum/callback/invoked_callback, ...) var/temp = usr usr = user_mob if(length(args) > 2) . = invoked_callback.Invoke(arglist(args.Copy(3))) else . = invoked_callback.Invoke() usr = temp /mob/verb/give_kudos(mob/living/target as mob in oview()) set category = null set name = "Give Kudos (OOC)" if(target == src) to_chat(src, "You cannot give kudos to yourself!") return to_chat(src, "You've given kudos to [target]!") // Pretend we've always succeeded when we might not have. // This should prevent people from using it to suss anything out about mobs' states if(!client || !target.client) return target.client.persistent.kudos_received_from |= ckey /mob/living/simple_animal/relaymove(mob/living/user, direction) if(user.incapacitated()) return return relaydrive(user, direction) /** * Checks to see if the mob can cast normal magic spells. * * args: * * magic_flags (optional) A bitfield with the type of magic being cast (see flags at: /datum/component/anti_magic) **/ /mob/proc/can_cast_magic(magic_flags = MAGIC_RESISTANCE) if(magic_flags == NONE) // magic with the NONE flag can always be cast return TRUE var/restrict_magic_flags = SEND_SIGNAL(src, COMSIG_MOB_RESTRICT_MAGIC, magic_flags) return restrict_magic_flags == NONE /** * Checks to see if the mob can block magic * * args: * * casted_magic_flags (optional) A bitfield with the types of magic resistance being checked (see flags at: /datum/component/anti_magic) * * charge_cost (optional) The cost of charge to block a spell that will be subtracted from the protection used **/ /mob/proc/can_block_magic(casted_magic_flags, charge_cost = 1) if(!casted_magic_flags || casted_magic_flags == NONE) // magic with the NONE flag is immune to blocking return FALSE // A list of all things which are providing anti-magic to us var/list/antimagic_sources = list() var/is_magic_blocked = FALSE if(SEND_SIGNAL(src, COMSIG_MOB_RECEIVE_MAGIC, casted_magic_flags, charge_cost, antimagic_sources) & COMPONENT_MAGIC_BLOCKED) is_magic_blocked = TRUE if(HAS_TRAIT(src, TRAIT_ANTIMAGIC)) is_magic_blocked = TRUE if((casted_magic_flags & MAGIC_RESISTANCE_HOLY) && HAS_TRAIT(src, TRAIT_HOLY)) is_magic_blocked = TRUE if(is_magic_blocked && charge_cost > 0 && !HAS_TRAIT(src, TRAIT_RECENTLY_BLOCKED_MAGIC)) on_block_magic_effects(casted_magic_flags, antimagic_sources) return is_magic_blocked /// Called whenever a magic effect with a charge cost is blocked and we haven't recently blocked magic. /mob/proc/on_block_magic_effects(magic_flags, list/antimagic_sources) return /mob/living/on_block_magic_effects(magic_flags, list/antimagic_sources) ADD_TRAIT(src, TRAIT_RECENTLY_BLOCKED_MAGIC, MAGIC_TRAIT) addtimer(CALLBACK(src, PROC_REF(remove_recent_magic_block)), 6 SECONDS) var/mutable_appearance/antimagic_effect var/antimagic_color var/atom/antimagic_source = length(antimagic_sources) ? pick(antimagic_sources) : src if(magic_flags & MAGIC_RESISTANCE) visible_message( "[src] pulses red as [ismob(antimagic_source) ? p_they() : antimagic_source] absorbs magic energy!", "An intense magical aura pulses around [ismob(antimagic_source) ? "you" : antimagic_source] as it dissipates into the air!", ) antimagic_effect = mutable_appearance('icons/effects/effects.dmi', "shield-red", ABOVE_MOB_LAYER) antimagic_color = LIGHT_COLOR_BLOOD_MAGIC playsound(src, 'sound/magic/magic_block.ogg', 50, TRUE) else if(magic_flags & MAGIC_RESISTANCE_HOLY) visible_message( "[src] starts to glow as [ismob(antimagic_source) ? p_they() : antimagic_source] emits a halo of light!", "A feeling of warmth washes over [ismob(antimagic_source) ? "you" : antimagic_source] as rays of light surround your body and protect you!", ) antimagic_effect = mutable_appearance('icons/mob/genetics.dmi', "servitude", ABOVE_MOB_LAYER) antimagic_color = LIGHT_COLOR_HOLY_MAGIC playsound(src, 'sound/magic/magic_block_holy.ogg', 50, TRUE) else if(magic_flags & MAGIC_RESISTANCE_MIND) visible_message( "[src] forehead shines as [ismob(antimagic_source) ? p_they() : antimagic_source] repulses magic from their mind!", "A feeling of cold splashes on [ismob(antimagic_source) ? "you" : antimagic_source] as your forehead reflects magic usering your mind!", ) antimagic_effect = mutable_appearance('icons/mob/genetics.dmi', "telekinesishead", ABOVE_MOB_LAYER) antimagic_color = LIGHT_COLOR_DARK_BLUE playsound(src, 'sound/magic/magic_block_mind.ogg', 50, TRUE) mob_light(_color = antimagic_color, _range = 2, _power = 2, _duration = 5 SECONDS) add_overlay(antimagic_effect) addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, cut_overlay), antimagic_effect), 5 SECONDS) /mob/living/proc/remove_recent_magic_block() REMOVE_TRAIT(src, TRAIT_RECENTLY_BLOCKED_MAGIC, MAGIC_TRAIT) /mob/living/proc/adjustHealth(amount, updating_health = TRUE) if(status_flags & GODMODE) return FALSE var/oldbruteloss = bruteloss bruteloss = clamp(bruteloss + amount, 0, maxHealth) if(oldbruteloss == bruteloss) updating_health = FALSE . = STATUS_UPDATE_NONE else . = STATUS_UPDATE_HEALTH if(updating_health) updatehealth() /mob/proc/add_mousepointer(priority = INFINITY, new_icon) mousepointers["[priority]"] = new_icon update_mousepointer() /mob/proc/remove_mousepointer(priority) mousepointers -= "[priority]" update_mousepointer() /mob/proc/update_mousepointer() if(!client) return var/lowest_prio = INFINITY for(var/prio in mousepointers) prio = text2num(prio) if(prio < lowest_prio) lowest_prio = prio if(lowest_prio == INFINITY) client.mouse_pointer_icon = null return client.mouse_pointer_icon = mousepointers["[lowest_prio]"]