/** * Delete a mob * * Removes mob from the following global lists * * GLOB.mob_list * * GLOB.dead_mob_list * * GLOB.alive_mob_list * * GLOB.all_clockwork_mobs * * GLOB.mob_directory * * Unsets the focus var * * Clears alerts for this mob * * Resets all the observers perspectives to the tile this mob is on * * qdels any client colours in place on this mob * * Ghostizes the client attached to this mob * * Parent call * * Returns QDEL_HINT_HARDDEL (don't change this) */ /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 GLOB.mob_directory -= tag focus = null for (var/alert in alerts) clear_alert(alert, TRUE) if(observers && observers.len) for(var/M in observers) var/mob/dead/observe = M observe.reset_perspective(null) qdel(hud_used) QDEL_LIST(client_colours) ghostize() ..() return QDEL_HINT_HARDDEL /** * Intialize a mob * * Sends global signal COMSIG_GLOB_MOB_CREATED * * Adds to global lists * * GLOB.mob_list * * GLOB.mob_directory (by tag) * * GLOB.dead_mob_list - if mob is dead * * GLOB.alive_mob_list - if the mob is alive * * Other stuff: * * Sets the mob focus to itself * * Generates huds * * If there are any global alternate apperances apply them to this mob * * set a random nutrition level * * Intialize the movespeed of the mob */ /mob/Initialize() SEND_GLOBAL_SIGNAL(COMSIG_GLOB_MOB_CREATED, src) GLOB.mob_list += src GLOB.mob_directory[tag] = src if(stat == DEAD) GLOB.dead_mob_list += src else GLOB.alive_mob_list += src set_focus(src) prepare_huds() for(var/v in GLOB.active_alternate_appearances) if(!v) continue var/datum/atom_hud/alternate_appearance/AA = v AA.onNewMob(src) set_nutrition(rand(NUTRITION_LEVEL_START_MIN, NUTRITION_LEVEL_START_MAX)) . = ..() update_config_movespeed() update_movespeed(TRUE) /** * Generate the tag for this mob * * This is simply "mob_"+ a global incrementing counter that goes up for every mob */ /mob/GenerateTag() tag = "mob_[next_mob_id++]" /** * Prepare the huds for this atom * * Goes through hud_possible list and adds the images to the hud_list variable (if not already * cached) */ /atom/proc/prepare_huds() hud_list = list() for(var/hud in hud_possible) var/hint = hud_possible[hud] switch(hint) if(HUD_LIST_LIST) hud_list[hud] = list() else var/image/I = image('icons/mob/hud.dmi', src, "") I.appearance_flags = RESET_COLOR|RESET_TRANSFORM hud_list[hud] = I /** * Some kind of debug verb that gives atmosphere environment details */ /mob/proc/Cell() set category = "Admin" set hidden = 1 if(!loc) return 0 var/datum/gas_mixture/environment = loc.return_air() var/t = "Coordinates: [x],[y] \n" t += "Temperature: [environment.temperature] \n" for(var/id in environment.gases) var/gas = environment.gases[id] if(gas[MOLES]) t+="[gas[GAS_META][META_GAS_NAME]]: [gas[MOLES]] \n" to_chat(usr, t) /** * Return the desc of this mob for a photo */ /mob/proc/get_photo_description(obj/item/camera/camera) return "a ... thing?" /** * Show a message to this mob (visual or audible) */ /mob/proc/show_message(msg, type, alt_msg, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2) if(!client) return msg = copytext(msg, 1, MAX_MESSAGE_LEN) if(type) if(type & MSG_VISUAL && eye_blind )//Vision related if(!alt_msg) return else msg = alt_msg type = alt_type if(type & MSG_AUDIBLE && !can_hear())//Hearing related if(!alt_msg) return else msg = alt_msg type = alt_type if(type & MSG_VISUAL && eye_blind) return // voice muffling if(stat == UNCONSCIOUS) if(type & MSG_AUDIBLE) //audio to_chat(src, "... You can almost hear something ...") return to_chat(src, msg) /** * Generate a visible message from this atom * * Show a message to all player mobs who sees this atom * * Show a message to the src mob (if the src is a mob) * * Use for atoms performing visible actions * * message is output to anyone who can see, e.g. "The [src] does something!" * * Vars: * * 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!" * * vision_distance (optional) define how many tiles away the message can be seen. * * ignored_mob (optional) doesn't show any message to a given mob if TRUE. */ /atom/proc/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs) var/turf/T = get_turf(src) if(!T) return if(!islist(ignored_mobs)) ignored_mobs = list(ignored_mobs) var/list/hearers = get_hearers_in_view(vision_distance, src) //caches the hearers and then removes ignored mobs. hearers -= ignored_mobs if(self_message) hearers -= src for(var/mob/M in hearers) if(!M.client) continue //This entire if/else chain could be in two lines but isn't for readibilties sake. var/msg = message if(M.see_invisible < invisibility)//if src is invisible to M msg = blind_message else if(T != loc && T != src) //if src is inside something and not a turf. msg = blind_message else if(T.lighting_object && T.lighting_object.invisibility <= M.see_invisible && T.is_softly_lit()) //if it is too dark. msg = blind_message if(!msg) continue M.show_message(msg, MSG_VISUAL, blind_message, MSG_AUDIBLE) ///Adds the functionality to self_message. /mob/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs) . = ..() if(self_message) show_message(self_message, MSG_VISUAL, blind_message, MSG_AUDIBLE) /** * Show a message to all mobs in earshot of this atom * * Use for objects performing audible actions * * vars: * * 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 = DEFAULT_MESSAGE_RANGE, self_message) var/list/hearers = get_hearers_in_view(hearing_distance, src) if(self_message) hearers -= src for(var/mob/M in hearers) M.show_message(message, MSG_AUDIBLE, deaf_message, MSG_VISUAL) /** * Show a message to all mobs in earshot of this one * * This would be for audible actions by the src mob * * vars: * * 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 = DEFAULT_MESSAGE_RANGE, self_message) . = ..() if(self_message) show_message(self_message, MSG_AUDIBLE, deaf_message, MSG_VISUAL) ///Get the item on the mob in the storage slot identified by the id passed in /mob/proc/get_item_by_slot(slot_id) return null ///Is the mob restrained /mob/proc/restrained(ignore_grab) return ///Is the mob incapacitated /mob/proc/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, check_immobilized = FALSE) return /** * This proc is called whenever someone clicks an inventory ui slot. * * Mostly tries to put the item into the slot if possible, or call attack hand * on the item in the slot if the users active hand is empty */ /mob/proc/attack_ui(slot) var/obj/item/W = get_active_held_item() if(istype(W)) if(equip_to_slot_if_possible(W, slot,0,0,0)) return 1 if(!W) // Activate the item var/obj/item/I = get_item_by_slot(slot) if(istype(I)) I.attack_hand(src) return 0 /** * Try to equip an item to a slot on the mob * * This is a SAFE proc. Use this instead of equip_to_slot()! * * set qdel_on_fail to have it delete W if it fails to equip * * set disable_warning to disable the 'you are unable to equip that' warning. * * unset redraw_mob to prevent the mob icons from being redrawn at the end. * * Initial is used to indicate whether or not this is the initial equipment (job datums etc) or just a player doing it */ /mob/proc/equip_to_slot_if_possible(obj/item/W, slot, qdel_on_fail = FALSE, disable_warning = FALSE, redraw_mob = TRUE, bypass_equip_delay_self = FALSE, initial = FALSE) if(!istype(W)) return FALSE if(!W.mob_can_equip(src, null, slot, disable_warning, bypass_equip_delay_self)) if(qdel_on_fail) qdel(W) else if(!disable_warning) to_chat(src, "You are unable to equip that!") return FALSE equip_to_slot(W, slot, redraw_mob, initial) //This proc should not ever fail. return TRUE /** * Actually equips an item to a slot (UNSAFE) * * This is an UNSAFE proc. It merely handles the actual job of equipping. All the checks on * whether you can or can't equip need to be done before! Use mob_can_equip() for that task. * *In most cases you will want to use equip_to_slot_if_possible() */ /mob/proc/equip_to_slot(obj/item/W, slot) return /** * Equip an item to the slot or delete * * This is just a commonly used configuration for the equip_to_slot_if_possible() proc, used to * equip people when the round starts and when events happen and such. * * Also bypasses equip delay checks, since the mob isn't actually putting it on. * Initial is used to indicate whether or not this is the initial equipment (job datums etc) or just a player doing it */ /mob/proc/equip_to_slot_or_del(obj/item/W, slot, initial = FALSE) return equip_to_slot_if_possible(W, slot, TRUE, TRUE, FALSE, TRUE, initial) /** * Auto equip the passed in item the appropriate slot based on equipment priority * * puts the item "W" into an appropriate slot in a human's inventory * * returns 0 if it cannot, 1 if successful */ /mob/proc/equip_to_appropriate_slot(obj/item/W) if(!istype(W)) return 0 var/slot_priority = W.slot_equipment_priority if(!slot_priority) slot_priority = list( \ ITEM_SLOT_BACK, ITEM_SLOT_ID,\ ITEM_SLOT_ICLOTHING, ITEM_SLOT_OCLOTHING,\ ITEM_SLOT_MASK, ITEM_SLOT_HEAD, ITEM_SLOT_NECK,\ ITEM_SLOT_FEET, ITEM_SLOT_GLOVES,\ ITEM_SLOT_EARS, ITEM_SLOT_EYES,\ ITEM_SLOT_BELT, ITEM_SLOT_SUITSTORE,\ ITEM_SLOT_LPOCKET, ITEM_SLOT_RPOCKET,\ ITEM_SLOT_DEX_STORAGE\ ) for(var/slot in slot_priority) if(equip_to_slot_if_possible(W, slot, 0, 1, 1)) //qdel_on_fail = 0; disable_warning = 1; redraw_mob = 1 return 1 return 0 /** * Reset the attached clients perspective (viewpoint) * * reset_perspective() set eye to common default : mob on turf, loc otherwise * reset_perspective(thing) set the eye to the thing (if it's equal to current default reset to mob perspective) */ /mob/proc/reset_perspective(atom/A) if(client) if(A) if(ismovableatom(A)) //Set the the thing unless it's us if(A != src) client.perspective = EYE_PERSPECTIVE client.eye = A else client.eye = client.mob client.perspective = MOB_PERSPECTIVE else if(isturf(A)) //Set to the turf unless it's our current turf if(A != loc) client.perspective = EYE_PERSPECTIVE client.eye = A else client.eye = client.mob client.perspective = MOB_PERSPECTIVE else //Do nothing else //Reset to common defaults: mob if on turf, otherwise current loc if(isturf(loc)) client.eye = client.mob client.perspective = MOB_PERSPECTIVE else client.perspective = EYE_PERSPECTIVE client.eye = loc return 1 /// Show the mob's inventory to another mob /mob/proc/show_inv(mob/user) return /** * Examine a mob * * mob verbs are faster than object verbs. See * [this byond forum post](https://secure.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()) //It used to be oview(12), but I can't really say why set name = "Examine" set category = "IC" if(isturf(A) && !(sight & SEE_TURFS) && !(A in view(client ? client.view : world.view, src))) // shift-click catcher may issue examinate() calls for out-of-sight turfs return if(is_blind(src)) to_chat(src, "Something is there but you can't see it!") return face_atom(A) var/list/result = A.examine(src) to_chat(src, result.Join("\n")) SEND_SIGNAL(src, COMSIG_MOB_EXAMINATE, A) /** * Point at an atom * * mob verbs are faster than object verbs. See * [this byond forum post](https://secure.byond.com/forum/?post=1326139&page=2#comment8198716) * for why this isn't atom/verb/pointed() * * note: ghosts can point, this is intended * * visible_message will handle invisibility properly * * overridden here and in /mob/dead/observer for different point span classes and sanity checks */ /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(client.view, src))) return FALSE if(istype(A, /obj/effect/temp_visual/point)) return FALSE var/tile = get_turf(A) if (!tile) return FALSE new /obj/effect/temp_visual/point(A,invisibility) return TRUE ///Can this mob resist (default FALSE) /mob/proc/can_resist() return FALSE //overridden in living.dm ///Spin this mob around it's central axis /mob/proc/spin(spintime, speed) set waitfor = 0 var/D = dir if((spintime < 1)||(speed < 1)||!spintime||!speed) return while(spintime >= speed) sleep(speed) switch(D) if(NORTH) D = EAST if(SOUTH) D = WEST if(EAST) D = SOUTH if(WEST) D = NORTH setDir(D) spintime -= speed ///Update the pulling hud icon /mob/proc/update_pull_hud_icon() hud_used?.pull_icon?.update_icon() ///Update the resting hud icon /mob/proc/update_rest_hud_icon() hud_used?.rest_icon?.update_icon() /** * Verb to activate the object in your held hand * * Calls attack self on the item and updates the inventory hud for hands */ /mob/verb/mode() set name = "Activate Held Object" set category = "Object" set src = usr if(ismecha(loc)) return if(incapacitated()) return var/obj/item/I = get_active_held_item() if(I) I.attack_self(src) update_inv_hands() /** * Get the notes of this mob * * This actually gets the mind datums notes */ /mob/verb/memory() set name = "Notes" set category = "IC" set desc = "View your character's notes memory." if(mind) mind.show_memory(src) else to_chat(src, "You don't have a mind datum for some reason, so you can't look at your notes, if you had any.") /** * Add a note to the mind datum */ /mob/verb/add_memory(msg as message) set name = "Add Note" set category = "IC" if(mind) if (world.time < memory_throttle_time) return memory_throttle_time = world.time + 5 SECONDS msg = copytext(msg, 1, MAX_MESSAGE_LEN) msg = sanitize(msg) mind.store_memory(msg) else to_chat(src, "You don't have a mind datum for some reason, so you can't add a note to it.") /** * Allows you to respawn, abandoning your current mob * * This sends you back to the lobby creating a new dead mob * * Only works if flag/norespawn is allowed in config */ /mob/verb/abandon_mob() set name = "Respawn" set category = "OOC" if (CONFIG_GET(flag/norespawn)) return if ((stat != DEAD || !( SSticker ))) to_chat(usr, "You must be dead to use this!") return log_game("[key_name(usr)] used abandon mob.") to_chat(usr, "Please roleplay correctly!") if(!client) log_game("[key_name(usr)] AM failed due to disconnect.") return client.screen.Cut() client.screen += client.void if(!client) log_game("[key_name(usr)] AM failed due to disconnect.") return var/mob/dead/new_player/M = new /mob/dead/new_player() if(!client) log_game("[key_name(usr)] AM failed due to disconnect.") qdel(M) return M.key = key // M.Login() //wat return /** * Sometimes helps if the user is stuck in another perspective or camera */ /mob/verb/cancel_camera() set name = "Cancel Camera View" set category = "OOC" reset_perspective(null) unset_machine() //suppress the .click/dblclick macros so people can't use them to identify the location of items or aimbot /mob/verb/DisClick(argu = null as anything, sec = "" as text, number1 = 0 as num , number2 = 0 as num) set name = ".click" set hidden = TRUE set category = null return /mob/verb/DisDblClick(argu = null as anything, sec = "" as text, number1 = 0 as num , number2 = 0 as num) set name = ".dblclick" set hidden = TRUE set category = null return /** * Topic call back for any mob * * * Unset machines if "mach_close" sent * * refresh the inventory of machines in range if "refresh" sent * * handles the strip panel equip and unequip as well if "item" sent */ /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["refresh"]) if(machine && in_range(src, usr)) show_inv(machine) if(href_list["item"] && usr.canUseTopic(src, BE_CLOSE, NO_DEXTERITY)) var/slot = text2num(href_list["item"]) var/hand_index = text2num(href_list["hand_index"]) var/obj/item/what if(hand_index) what = get_item_for_held_index(hand_index) slot = list(slot,hand_index) else what = get_item_by_slot(slot) if(what) if(!(what.item_flags & ABSTRACT)) usr.stripPanelUnequip(what,src,slot) else usr.stripPanelEquip(what,src,slot) if(usr.machine == src) if(Adjacent(usr)) show_inv(usr) else usr << browse(null,"window=mob[REF(src)]") // The src mob is trying to strip an item from someone // Defined in living.dm /mob/proc/stripPanelUnequip(obj/item/what, mob/who) return // The src mob is trying to place an item on someone // Defined in living.dm /mob/proc/stripPanelEquip(obj/item/what, mob/who) return /** * Controls if a mouse drop succeeds (return null if it doesnt) */ /mob/MouseDrop(mob/M) . = ..() if(M != usr) return if(usr == src) return if(!Adjacent(usr)) return if(isAI(M)) return /** * Handle the result of a click drag onto this mob * * For mobs this just shows the inventory */ /mob/MouseDrop_T(atom/dropping, atom/user) . = ..() if(ismob(dropping) && dropping != user) var/mob/M = dropping if(ismob(user)) var/mob/U = user if(!iscyborg(U) || U.a_intent == INTENT_HARM) M.show_inv(U) else M.show_inv(user) ///Is the mob muzzled (default false) /mob/proc/is_muzzled() return 0 /** * Output an update to the stat panel for the client * * calculates client ping, round id, server time, time dilation and other data about the round * and puts it in the mob status panel on a regular loop */ /mob/Stat() ..() if(statpanel("Status")) if (client) stat(null, "Ping: [round(client.lastping, 1)]ms (Average: [round(client.avgping, 1)]ms)") stat(null, "Map: [SSmapping.config?.map_name || "Loading..."]") var/datum/map_config/cached = SSmapping.next_map_config if(cached) stat(null, "Next Map: [cached.map_name]") stat(null, "Round ID: [GLOB.round_id ? GLOB.round_id : "NULL"]") stat(null, "Server Time: [time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss")]") stat(null, "Round Time: [worldtime2text()]") stat(null, "Station Time: [station_time_timestamp()]") 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(SSshuttle.emergency) var/ETA = SSshuttle.emergency.getModeStr() if(ETA) stat(null, "[ETA] [SSshuttle.emergency.getTimerStr()]") if(client && client.holder) if(statpanel("MC")) var/turf/T = get_turf(client.eye) stat("Location:", COORD(T)) stat("CPU:", "[world.cpu]") stat("Instances:", "[num2text(world.contents.len, 10)]") stat("World Time:", "[world.time]") GLOB.stat_entry() config.stat_entry() stat(null) 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() GLOB.cameranet.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/i in GLOB.sdql2_queries) var/datum/SDQL2_query/Q = i Q.generate_stat() if(listed_turf && client) if(!TurfAdjacent(listed_turf)) listed_turf = null else statpanel(listed_turf.name, null, listed_turf) var/list/overrides = list() for(var/image/I in client.images) if(I.loc && I.loc.loc == listed_turf && I.override) overrides += I.loc for(var/atom/A in listed_turf) if(!A.mouse_opacity) continue if(A.invisibility > see_invisible) continue if(overrides.len && (A in overrides)) continue if(A.IsObscured()) continue statpanel(listed_turf.name, null, A) if(mind) add_spells_to_statpanel(mind.spell_list) add_spells_to_statpanel(mob_spell_list) /** * Convert a list of spells into a displyable list for the statpanel * * Shows charge and other important info */ /mob/proc/add_spells_to_statpanel(list/spells) for(var/obj/effect/proc_holder/spell/S in spells) if(S.can_be_cast_by(src)) switch(S.charge_type) if("recharge") statpanel("[S.panel]","[S.charge_counter/10.0]/[S.charge_max/10]",S) if("charges") statpanel("[S.panel]","[S.charge_counter]/[S.charge_max]",S) if("holdervar") statpanel("[S.panel]","[S.holder_var_type] [S.holder_var_amount]",S) #define MOB_FACE_DIRECTION_DELAY 1 // facing verbs /** * Returns true if a mob can turn to face things * * Conditions: * * client.last_turn > world.time * * not dead or unconcious * * not anchored * * no transform not set * * we are not restrained */ /mob/proc/canface() if(world.time < client.last_turn) return FALSE if(stat == DEAD || stat == UNCONSCIOUS) return FALSE if(anchored) return FALSE if(notransform) return FALSE if(restrained()) return FALSE return TRUE ///Checks mobility move as well as parent checks /mob/living/canface() if(!(mobility_flags & MOBILITY_MOVE)) return FALSE return ..() /mob/dead/observer/canface() return TRUE ///Hidden verb to turn east /mob/verb/eastface() set hidden = TRUE if(!canface()) return FALSE setDir(EAST) client.last_turn = world.time + MOB_FACE_DIRECTION_DELAY return TRUE ///Hidden verb to turn west /mob/verb/westface() set hidden = TRUE if(!canface()) return FALSE setDir(WEST) client.last_turn = world.time + MOB_FACE_DIRECTION_DELAY return TRUE ///Hidden verb to turn north /mob/verb/northface() set hidden = TRUE if(!canface()) return FALSE setDir(NORTH) client.last_turn = world.time + MOB_FACE_DIRECTION_DELAY return TRUE ///Hidden verb to turn south /mob/verb/southface() set hidden = TRUE if(!canface()) return FALSE setDir(SOUTH) client.last_turn = world.time + MOB_FACE_DIRECTION_DELAY return TRUE ///This might need a rename but it should replace the can this mob use things check /mob/proc/IsAdvancedToolUser() return FALSE /mob/proc/swap_hand() return /mob/proc/activate_hand(selhand) return /mob/proc/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null) //For sec bot threat assessment return 0 ///Get the ghost of this mob (from the mind) /mob/proc/get_ghost(even_if_they_cant_reenter, ghosts_with_clients) if(mind) return mind.get_ghost(even_if_they_cant_reenter, ghosts_with_clients) ///Force get the ghost from the mind /mob/proc/grab_ghost(force) if(mind) return mind.grab_ghost(force = force) ///Notify a ghost that it's body is being cloned /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) var/mob/dead/observer/ghost = get_ghost() if(ghost) ghost.notify_cloning(message, sound, source, flashwindow) return ghost ///Add a spell to the mobs spell list /mob/proc/AddSpell(obj/effect/proc_holder/spell/S) mob_spell_list += S S.action.Grant(src) ///Remove a spell from the mobs spell list /mob/proc/RemoveSpell(obj/effect/proc_holder/spell/spell) if(!spell) return for(var/X in mob_spell_list) var/obj/effect/proc_holder/spell/S = X if(istype(S, spell)) mob_spell_list -= S qdel(S) ///Return any anti magic atom on this mob that matches the magic type /mob/proc/anti_magic_check(magic = TRUE, holy = FALSE, tinfoil = FALSE, chargecost = 1, self = FALSE) if(!magic && !holy && !tinfoil) return var/list/protection_sources = list() if(SEND_SIGNAL(src, COMSIG_MOB_RECEIVE_MAGIC, src, magic, holy, tinfoil, chargecost, self, protection_sources) & COMPONENT_BLOCK_MAGIC) if(protection_sources.len) return pick(protection_sources) else return src if((magic && HAS_TRAIT(src, TRAIT_ANTIMAGIC)) || (holy && HAS_TRAIT(src, TRAIT_HOLY))) return src /** * 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 FALSE 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 FALSE 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 1 ///can the mob be unbuckled from something by default? /mob/proc/can_unbuckle() return 1 ///Can the mob interact() with an atom? /mob/proc/can_interact_with(atom/A) return IsAdminGhost(src) || Adjacent(A) ///Can the mob use Topic to interact with machines /mob/proc/canUseTopic(atom/movable/M, be_close=FALSE, no_dexterity=FALSE, no_tk=FALSE) return ///Can this mob use storage /mob/proc/canUseStorage() return FALSE /** * Check if the other mob has any factions the same as us * * If exact match is set, then all our factions must match exactly */ /mob/proc/faction_check_mob(mob/target, exact_match) 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) /* * Compare two lists of factions, returning true if any match * * If exact match is passed through we only return true if both faction lists match equally */ /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 /** * Fully update the name of a mob * * This will update a mob's name, real_name, mind.name, GLOB.data_core records, pda, id and traitor text * * Calling this proc without an oldname will only update the mob and skip updating the pda, id and records ~Carn */ /mob/proc/fully_replace_character_name(oldname,newname) log_message("[src] name changed from [oldname] to [newname]", LOG_OWNERSHIP) if(!newname) return 0 log_played_names(ckey,newname) real_name = newname name = newname if(mind) mind.name = newname if(mind.key) log_played_names(mind.key,newname) //Just in case the mind is unsynced at the moment. if(oldname) //update the datacore records! This is goig to be a bit costly. replace_records_name(oldname,newname) //update our pda and id if we have them on our person replace_identification_name(oldname,newname) for(var/datum/mind/T in SSticker.minds) for(var/datum/objective/obj in T.get_all_objectives()) // Only update if this player is a target if(obj.target && obj.target.current && obj.target.current.real_name == name) obj.update_explanation_text() return 1 ///Updates GLOB.data_core records with new name , see mob/living/carbon/human /mob/proc/replace_records_name(oldname,newname) return ///update the ID name of this mob /mob/proc/replace_identification_name(oldname,newname) var/list/searching = GetAllContents() var/search_id = 1 var/search_pda = 1 for(var/A in searching) if( search_id && istype(A, /obj/item/card/id) ) var/obj/item/card/id/ID = A if(ID.registered_name == oldname) ID.registered_name = newname ID.update_label() if(ID.registered_account?.account_holder == oldname) ID.registered_account.account_holder = newname if(!search_pda) break search_id = 0 else if( search_pda && istype(A, /obj/item/pda) ) var/obj/item/pda/PDA = A if(PDA.owner == oldname) PDA.owner = newname PDA.update_label() if(!search_id) break search_pda = 0 /mob/proc/update_stat() return /mob/proc/update_health_hud() return ///Update the lighting plane and sight of this mob (sends COMSIG_MOB_UPDATE_SIGHT) /mob/proc/update_sight() SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) sync_lighting_plane_alpha() ///Set the lighting plane hud alpha to the mobs lighting_alpha var /mob/proc/sync_lighting_plane_alpha() if(hud_used) var/obj/screen/plane_master/lighting/L = hud_used.plane_masters["[LIGHTING_PLANE]"] if (L) L.alpha = lighting_alpha ///Update the mouse pointer of the attached client in this mob /mob/proc/update_mouse_pointer() if (!client) return client.mouse_pointer_icon = initial(client.mouse_pointer_icon) if (ismecha(loc)) var/obj/mecha/M = loc if(M.mouse_pointer) client.mouse_pointer_icon = M.mouse_pointer else if (istype(loc, /obj/vehicle/sealed)) var/obj/vehicle/sealed/E = loc if(E.mouse_pointer) client.mouse_pointer_icon = E.mouse_pointer ///This mob is abile to read books /mob/proc/is_literate() return FALSE ///Can this mob read (is literate and not blind) /mob/proc/can_read(obj/O) if(is_blind(src)) to_chat(src, "As you are trying to read [O], you suddenly feel very stupid!") return if(!is_literate()) to_chat(src, "You try to read [O], but can't comprehend any of it.") return return TRUE ///Can this mob hold items /mob/proc/can_hold_items() return FALSE ///Get the id card on this mob /mob/proc/get_idcard(hand_first) return /mob/proc/get_id_in_hand() return /** * Get the mob VV dropdown extras */ /mob/vv_get_dropdown() . = ..() VV_DROPDOWN_OPTION("", "---------") VV_DROPDOWN_OPTION(VV_HK_GIB, "Gib") VV_DROPDOWN_OPTION(VV_HK_GIVE_SPELL, "Give Spell") VV_DROPDOWN_OPTION(VV_HK_REMOVE_SPELL, "Remove Spell") VV_DROPDOWN_OPTION(VV_HK_GIVE_DISEASE, "Give Disease") VV_DROPDOWN_OPTION(VV_HK_GODMODE, "Toggle Godmode") VV_DROPDOWN_OPTION(VV_HK_DROP_ALL, "Drop Everything") VV_DROPDOWN_OPTION(VV_HK_REGEN_ICONS, "Regenerate Icons") VV_DROPDOWN_OPTION(VV_HK_PLAYER_PANEL, "Show player panel") VV_DROPDOWN_OPTION(VV_HK_BUILDMODE, "Toggle Buildmode") VV_DROPDOWN_OPTION(VV_HK_DIRECT_CONTROL, "Assume Direct Control") VV_DROPDOWN_OPTION(VV_HK_OFFER_GHOSTS, "Offer Control to Ghosts") /mob/vv_do_topic(list/href_list) . = ..() if(href_list[VV_HK_REGEN_ICONS]) if(!check_rights(NONE)) return regenerate_icons() if(href_list[VV_HK_PLAYER_PANEL]) if(!check_rights(NONE)) return usr.client.holder.show_player_panel(src) if(href_list[VV_HK_GODMODE]) if(!check_rights(R_ADMIN)) return usr.client.cmd_admin_godmode(src) if(href_list[VV_HK_GIVE_SPELL]) if(!check_rights(NONE)) return usr.client.give_spell(src) if(href_list[VV_HK_REMOVE_SPELL]) if(!check_rights(NONE)) return usr.client.remove_spell(src) if(href_list[VV_HK_GIVE_DISEASE]) if(!check_rights(NONE)) return usr.client.give_disease(src) if(href_list[VV_HK_GIB]) if(!check_rights(R_FUN)) return usr.client.cmd_admin_gib(src) if(href_list[VV_HK_BUILDMODE]) if(!check_rights(R_BUILD)) return togglebuildmode(src) if(href_list[VV_HK_DROP_ALL]) if(!check_rights(NONE)) return usr.client.cmd_admin_drop_everything(src) if(href_list[VV_HK_DIRECT_CONTROL]) if(!check_rights(NONE)) return usr.client.cmd_assume_direct_control(src) if(href_list[VV_HK_OFFER_GHOSTS]) if(!check_rights(NONE)) return offer_control(src) /** * extra var handling for the logging var */ /mob/vv_get_var(var_name) switch(var_name) if("logging") return debug_variable(var_name, logging, 0, src, FALSE) . = ..() /mob/vv_auto_rename(new_name) //Do not do parent's actions, as we *usually* do this differently. fully_replace_character_name(real_name, new_name) ///Show the language menu for this mob /mob/verb/open_language_menu() set name = "Open Language Menu" set category = "IC" var/datum/language_holder/H = get_language_holder() H.open_language_menu(usr) ///Adjust the nutrition of a mob /mob/proc/adjust_nutrition(var/change) //Honestly FUCK the oldcoders for putting nutrition on /mob someone else can move it up because holy hell I'd have to fix SO many typechecks nutrition = max(0, nutrition + change) ///Force set the mob nutrition /mob/proc/set_nutrition(var/change) //Seriously fuck you oldcoders. nutrition = max(0, change) ///Set the movement type of the mob and update it's movespeed /mob/setMovetype(newval) . = ..() update_movespeed(FALSE) /// Updates the grab state of the mob and updates movespeed /mob/setGrabState(newstate) . = ..() if(grab_state == GRAB_PASSIVE) remove_movespeed_modifier(MOVESPEED_ID_MOB_GRAB_STATE, update=TRUE) else add_movespeed_modifier(MOVESPEED_ID_MOB_GRAB_STATE, update=TRUE, priority=100, override=TRUE, multiplicative_slowdown=grab_state*3, blacklisted_movetypes=FLOATING) /mob/proc/update_equipment_speed_mods() var/speedies = equipped_speed_mods() if(!speedies) remove_movespeed_modifier(MOVESPEED_ID_MOB_EQUIPMENT, update=TRUE) else add_movespeed_modifier(MOVESPEED_ID_MOB_EQUIPMENT, update=TRUE, priority=100, override=TRUE, multiplicative_slowdown=speedies, blacklisted_movetypes=FLOATING) /// Gets the combined speed modification of all worn items /// Except base mob type doesnt really wear items /mob/proc/equipped_speed_mods() for(var/obj/item/I in held_items) if(I.item_flags & SLOWS_WHILE_IN_HAND) . += I.slowdown