diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm index 0617c5d3..ec7a1108 100644 --- a/code/__DEFINES/vv.dm +++ b/code/__DEFINES/vv.dm @@ -19,3 +19,105 @@ #define VV_RESTORE_DEFAULT "Restore to Default" #define VV_MARKED_DATUM "Marked Datum" #define VV_BITFIELD "Bitfield" +#define VV_TEXT_LOCATE "Custom Reference Locate" +#define VV_PROCCALL_RETVAL "Return Value of Proccall" + +#define VV_MSG_MARKED "
Marked Object" +#define VV_MSG_EDITED "
Var Edited" +#define VV_MSG_DELETED "
Deleted" + +#define VV_NORMAL_LIST_NO_EXPAND_THRESHOLD 50 +#define VV_SPECIAL_LIST_NO_EXPAND_THRESHOLD 150 + +//#define IS_VALID_ASSOC_KEY(V) (istext(V) || ispath(V) || isdatum(V) || islist(V)) +#define IS_VALID_ASSOC_KEY(V) (!isnum(V)) //hhmmm.. + +//General helpers +#define VV_HREF_TARGET_INTERNAL(target, href_key) "?_src_=vars;[HrefToken()];[href_key]=TRUE;[VV_HK_TARGET]=[REF(target)]" +#define VV_HREF_TARGETREF_INTERNAL(targetref, href_key) "?_src_=vars;[HrefToken()];[href_key]=TRUE;[VV_HK_TARGET]=[targetref]" +#define VV_HREF_TARGET(target, href_key, text) "[text]" +#define VV_HREF_TARGETREF(targetref, href_key, text) "[text]" +#define VV_HREF_TARGET_1V(target, href_key, text, varname) "[text]" //for stuff like basic varedits, one variable +#define VV_HREF_TARGETREF_1V(targetref, href_key, text, varname) "[text]" + +#define GET_VV_TARGET locate(href_list[VV_HK_TARGET]) +#define GET_VV_VAR_TARGET href_list[VV_HK_VARNAME] + +//Helper for getting something to vv_do_topic in general +#define VV_TOPIC_LINK(datum, href_key, text) "text" + +//Helpers for vv_get_dropdown() +#define VV_DROPDOWN_OPTION(href_key, name) . += "" + +// VV HREF KEYS +#define VV_HK_TARGET "target" +#define VV_HK_VARNAME "targetvar" //name or index of var for 1 variable targetting hrefs. + +// vv_do_list() keys +#define VV_HK_LIST_ADD "listadd" +#define VV_HK_LIST_EDIT "listedit" +#define VV_HK_LIST_CHANGE "listchange" +#define VV_HK_LIST_REMOVE "listremove" +#define VV_HK_LIST_ERASE_NULLS "listnulls" +#define VV_HK_LIST_ERASE_DUPES "listdupes" +#define VV_HK_LIST_SHUFFLE "listshuffle" +#define VV_HK_LIST_SET_LENGTH "listlen" + +// vv_do_basic() keys +#define VV_HK_BASIC_EDIT "datumedit" +#define VV_HK_BASIC_CHANGE "datumchange" +#define VV_HK_BASIC_MASSEDIT "massedit" + +// /datum +#define VV_HK_DELETE "delete" +#define VV_HK_EXPOSE "expose" +#define VV_HK_CALLPROC "proc_call" +#define VV_HK_MARK "mark" +#define VV_HK_MODIFY_TRAITS "modtraits" + +// /atom +#define VV_HK_MODIFY_TRANSFORM "atom_transform" +#define VV_HK_ADD_REAGENT "addreagent" +#define VV_HK_TRIGGER_EMP "empulse" +#define VV_HK_TRIGGER_EXPLOSION "explode" +#define VV_HK_AUTO_RENAME "auto_rename" + +// /obj +#define VV_HK_OSAY "osay" +#define VV_HK_MASS_DEL_TYPE "mass_delete_type" +#define VV_HK_ARMOR_MOD "mod_obj_armor" + +// /mob +#define VV_HK_GIB "gib" +#define VV_HK_GIVE_SPELL "give_spell" +#define VV_HK_REMOVE_SPELL "remove_spell" +#define VV_HK_GIVE_DISEASE "give_disease" +#define VV_HK_GODMODE "godmode" +#define VV_HK_DROP_ALL "dropall" +#define VV_HK_REGEN_ICONS "regen_icons" +#define VV_HK_PLAYER_PANEL "player_panel" +#define VV_HK_BUILDMODE "buildmode" +#define VV_HK_DIRECT_CONTROL "direct_control" +#define VV_HK_OFFER_GHOSTS "offer_ghosts" + +// /mob/living/carbon +#define VV_HK_MAKE_AI "aiify" +#define VV_HK_MODIFY_BODYPART "mod_bodypart" +#define VV_HK_MODIFY_ORGANS "organs_modify" +#define VV_HK_HALLUCINATION "force_hallucinate" +#define VV_HK_MARTIAL_ART "give_martial_art" +#define VV_HK_GIVE_TRAUMA "give_trauma" +#define VV_HK_CURE_TRAUMA "cure_trauma" + +// /mob/living/carbon/human +#define VV_HK_COPY_OUTFIT "copy_outfit" +#define VV_HK_MOD_QUIRKS "quirkmod" +#define VV_HK_MAKE_MONKEY "human_monkify" +#define VV_HK_MAKE_CYBORG "human_cyborgify" +#define VV_HK_MAKE_SLIME "human_slimeify" +#define VV_HK_MAKE_ALIEN "human_alienify" +#define VV_HK_SET_SPECIES "setspecies" +#define VV_HK_PURRBATION "purrbation" + +// misc +#define VV_HK_SPACEVINE_PURGE "spacevine_purge" diff --git a/code/__HELPERS/reagents.dm b/code/__HELPERS/reagents.dm index f1208abd..fa655efc 100644 --- a/code/__HELPERS/reagents.dm +++ b/code/__HELPERS/reagents.dm @@ -72,3 +72,32 @@ if(!GLOB.chemical_reactions_list[primary_reagent]) GLOB.chemical_reactions_list[primary_reagent] = list() GLOB.chemical_reactions_list[primary_reagent] += R + +/proc/choose_reagent_id(mob/user) + var/chosen_id + switch(alert(user, "Choose a method.", "Add Reagents", "Search", "Choose from a list", "I'm feeling lucky")) + if("Search") + var/valid_id + while(!valid_id) + chosen_id = input(user, "Enter the ID of the reagent you want to add.", "Search reagents") as null|text + if(isnull(chosen_id)) //Get me out of here! + break + if(!ispath(text2path(chosen_id))) + chosen_id = pick_closest_path(chosen_id, make_types_fancy(subtypesof(/datum/reagent))) + if(ispath(chosen_id)) + valid_id = TRUE + else + valid_id = TRUE + if(!valid_id) + to_chat(user, "A reagent with that ID doesn't exist!") + if("Choose from a list") + chosen_id = input(user, "Choose a reagent to add.", "Choose a reagent.") as null|anything in subtypesof(/datum/reagent) + if("I'm feeling lucky") + chosen_id = pick(subtypesof(/datum/reagent)) + return chosen_id + +/proc/find_reagent_object_from_type(input) + if(GLOB.chemical_reagents_list[input]) //prefer IDs! + return GLOB.chemical_reagents_list[input] + else + return null diff --git a/code/__HELPERS/type_processing.dm b/code/__HELPERS/type_processing.dm new file mode 100644 index 00000000..ac7e5cab --- /dev/null +++ b/code/__HELPERS/type_processing.dm @@ -0,0 +1,60 @@ + +/proc/make_types_fancy(var/list/types) + if (ispath(types)) + types = list(types) + . = list() + for(var/type in types) + var/typename = "[type]" + var/static/list/TYPES_SHORTCUTS = list( + /obj/effect/decal/cleanable = "CLEANABLE", + /obj/item/radio/headset = "HEADSET", + /obj/item/clothing/head/helmet/space = "SPESSHELMET", + /obj/item/book/manual = "MANUAL", + /obj/item/reagent_containers/food/drinks = "DRINK", //longest paths comes first + /obj/item/reagent_containers/food = "FOOD", + /obj/item/reagent_containers = "REAGENT_CONTAINERS", + /obj/machinery/atmospherics = "ATMOS_MECH", + /obj/machinery/portable_atmospherics = "PORT_ATMOS", + /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack = "MECHA_MISSILE_RACK", + /obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP", + /obj/item/organ = "ORGAN", + /obj/item = "ITEM", + /obj/machinery = "MACHINERY", + /obj/effect = "EFFECT", + /obj = "O", + /datum = "D", + /turf/open = "OPEN", + /turf/closed = "CLOSED", + /turf = "T", + /mob/living/carbon = "CARBON", + /mob/living/simple_animal = "SIMPLE", + /mob/living = "LIVING", + /mob = "M" + ) + for (var/tn in TYPES_SHORTCUTS) + if (copytext(typename,1, length("[tn]/")+1)=="[tn]/" /*findtextEx(typename,"[tn]/",1,2)*/ ) + typename = TYPES_SHORTCUTS[tn]+copytext(typename,length("[tn]/")) + break + .[typename] = type + +/proc/get_fancy_list_of_atom_types() + var/static/list/pre_generated_list + if (!pre_generated_list) //init + pre_generated_list = make_types_fancy(typesof(/atom)) + return pre_generated_list + + +/proc/get_fancy_list_of_datum_types() + var/static/list/pre_generated_list + if (!pre_generated_list) //init + pre_generated_list = make_types_fancy(sortList(typesof(/datum) - typesof(/atom))) + return pre_generated_list + + +/proc/filter_fancy_list(list/L, filter as text) + var/list/matches = new + for(var/key in L) + var/value = L[key] + if(findtext("[key]", filter) || findtext("[value]", filter)) + matches[key] = value + return matches diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index 65b1ce35..f97fde6f 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -1,1428 +1,50 @@ -#define VV_MSG_MARKED "
Marked Object" -#define VV_MSG_EDITED "
Var Edited" -#define VV_MSG_DELETED "
Deleted" - -/datum/proc/CanProcCall(procname) - return TRUE - -/datum/proc/can_vv_get(var_name) - return TRUE - -/datum/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited - if(var_name == NAMEOF(src, vars)) - return FALSE - vars[var_name] = var_value - datum_flags |= DF_VAR_EDITED - return TRUE - -/datum/proc/vv_get_var(var_name) - switch(var_name) - if ("vars") - return debug_variable(var_name, list(), 0, src) - return debug_variable(var_name, vars[var_name], 0, src) - -//please call . = ..() first and append to the result, that way parent items are always at the top and child items are further down -//add separaters by doing . += "---" -/datum/proc/vv_get_dropdown() - . = list() - . += "---" - .["Call Proc"] = "?_src_=vars;[HrefToken()];proc_call=\ref[src]" - .["Mark Object"] = "?_src_=vars;[HrefToken()];mark_object=\ref[src]" - .["Delete"] = "?_src_=vars;[HrefToken()];delete=\ref[src]" - .["Show VV To Player"] = "?_src_=vars;[HrefToken(TRUE)];expose=\ref[src]" - - -/datum/proc/on_reagent_change(changetype) - return - - -/client/proc/debug_variables(datum/D in world) - set category = "Debug" - set name = "View Variables" - //set src in world - var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round. - - if(!usr.client || !usr.client.holder) //The usr vs src abuse in this proc is intentional and must not be changed - to_chat(usr, "You need to be an administrator to access this.") - return - - if(!D) - return - - var/islist = islist(D) - if (!islist && !istype(D)) - return - - var/title = "" - var/refid = "\ref[D]" - var/icon/sprite - var/hash - - var/type = /list - if (!islist) - type = D.type - - if(istype(D, /atom)) - var/atom/AT = D - if(AT.icon && AT.icon_state) - sprite = new /icon(AT.icon, AT.icon_state) - hash = md5(AT.icon) - hash = md5(hash + AT.icon_state) - src << browse_rsc(sprite, "vv[hash].png") - - title = "[D] ([REF(D)]) = [type]" - var/formatted_type = replacetext("[type]", "/", "/") - - var/sprite_text - if(sprite) - sprite_text = "" - var/list/atomsnowflake = list() - - if(istype(D, /atom)) - var/atom/A = D - if(isliving(A)) - atomsnowflake += "[D]" - atomsnowflake += "
<< [dir2text(A.dir) || A.dir] >>" - var/mob/living/M = A - atomsnowflake += {" -
[M.ckey || "No ckey"] / [M.real_name || "No real name"] -
- BRUTE:[M.getBruteLoss()] - FIRE:[M.getFireLoss()] - TOXIN:[M.getToxLoss()] - OXY:[M.getOxyLoss()] - CLONE:[M.getCloneLoss()] - BRAIN:[M.getOrganLoss(ORGAN_SLOT_BRAIN)] - STAMINA:[M.getStaminaLoss()] - AROUSAL:[M.getArousalLoss()] - "} - if(GLOB.Debug2) - atomsnowflake += {" - HEART:[M.getOrganLoss(ORGAN_SLOT_HEART)] - LIVER:[M.getOrganLoss(ORGAN_SLOT_LIVER)] - LUNGS:[M.getOrganLoss(ORGAN_SLOT_LUNGS)] - EYES:[M.getOrganLoss(ORGAN_SLOT_EYES)] - EARS:[M.getOrganLoss(ORGAN_SLOT_EARS)] - STOMACH:[M.getOrganLoss(ORGAN_SLOT_STOMACH)] - TONGUE:[M.getOrganLoss(ORGAN_SLOT_TONGUE)] - APPENDIX:[M.getOrganLoss(ORGAN_SLOT_APPENDIX)] - "} - atomsnowflake += {" - - "} - else - atomsnowflake += "[D]" - atomsnowflake += "
<< [dir2text(A.dir) || A.dir] >>" - else if("name" in D.vars) - atomsnowflake += "[D]" - else - atomsnowflake += "[formatted_type]" - formatted_type = null - - var/marked - if(holder && holder.marked_datum && holder.marked_datum == D) - marked = VV_MSG_MARKED - var/varedited_line = "" - if(!islist && (D.datum_flags & DF_VAR_EDITED)) - varedited_line = VV_MSG_EDITED - var/deleted_line - if(!islist && D.gc_destroyed) - deleted_line = VV_MSG_DELETED - - var/list/dropdownoptions = list() - if (islist) - dropdownoptions = list( - "---", - "Add Item" = "?_src_=vars;[HrefToken()];listadd=[refid]", - "Remove Nulls" = "?_src_=vars;[HrefToken()];listnulls=[refid]", - "Remove Dupes" = "?_src_=vars;[HrefToken()];listdupes=[refid]", - "Set len" = "?_src_=vars;[HrefToken()];listlen=[refid]", - "Shuffle" = "?_src_=vars;[HrefToken()];listshuffle=[refid]", - "Show VV To Player" = "?_src_=vars;[HrefToken()];expose=[refid]" - ) - else - dropdownoptions = D.vv_get_dropdown() - var/list/dropdownoptions_html = list() - - for (var/name in dropdownoptions) - var/link = dropdownoptions[name] - if (link) - dropdownoptions_html += "" - else - dropdownoptions_html += "" - - var/list/names = list() - if (!islist) - for (var/V in D.vars) - names += V - sleep(1)//For some reason, without this sleep, VVing will cause client to disconnect on certain objects. - - var/list/variable_html = list() - if (islist) - var/list/L = D - for (var/i in 1 to L.len) - var/key = L[i] - var/value - if (IS_NORMAL_LIST(L) && !isnum(key)) - value = L[key] - variable_html += debug_variable(i, value, 0, D) - else - - names = sortList(names) - for (var/V in names) - if(D.can_vv_get(V)) - variable_html += D.vv_get_var(V) - - var/html = {" - - - [title] - - - - -
- - - - - -
- - - - -
- [sprite_text] -
- [atomsnowflake.Join()] -
-
-
- [formatted_type] - [marked] - [varedited_line] - [deleted_line] -
-
-
- Refresh -
- -
-
-
-
-
- - E - Edit, tries to determine the variable type by itself.
- C - Change, asks you for the var type first.
- M - Mass modify: changes this variable for all objects of this type.
-
-
- - - - - -
-
- Search: -
-
- -
-
-
    - [variable_html.Join()] -
- - - -"} - src << browse(html, "window=variables[refid];size=475x650") - - -/client/proc/vv_update_display(datum/D, span, content) - src << output("[span]:[content]", "variables\ref[D].browser:replace_span") - - -#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing ) -/proc/debug_variable(name, value, level, datum/DA = null, sanitize = TRUE) - var/header - if(DA) - if (islist(DA)) - var/index = name - if (value) - name = DA[name] //name is really the index until this line - else - value = DA[name] - header = "
  • (E) (C) (-) " - else - header = "
  • (E) (C) (M) " - else - header = "
  • " - - var/item - if (isnull(value)) - item = "[VV_HTML_ENCODE(name)] = null" - - else if (istext(value)) - item = "[VV_HTML_ENCODE(name)] = \"[VV_HTML_ENCODE(value)]\"" - - else if (isicon(value)) - #ifdef VARSICON - var/icon/I = new/icon(value) - var/rnd = rand(1,10000) - var/rname = "tmp\ref[I][rnd].png" - usr << browse_rsc(I, rname) - item = "[VV_HTML_ENCODE(name)] = ([value]) " - #else - item = "[VV_HTML_ENCODE(name)] = /icon ([value])" - #endif - - else if (isfile(value)) - item = "[VV_HTML_ENCODE(name)] = '[value]'" - - else if (istype(value, /datum)) - var/datum/D = value - if ("[D]" != "[D.type]") //if the thing as a name var, lets use it. - item = "[VV_HTML_ENCODE(name)] [REF(value)] = [D] [D.type]" - else - item = "[VV_HTML_ENCODE(name)] [REF(value)] = [D.type]" - - else if (islist(value)) - var/list/L = value - var/list/items = list() - - if (L.len > 0 && !(name == "underlays" || name == "overlays" || L.len > (IS_NORMAL_LIST(L) ? 50 : 150))) - for (var/i in 1 to L.len) - var/key = L[i] - var/val - if (IS_NORMAL_LIST(L) && !isnum(key)) - val = L[key] - if (isnull(val)) // we still want to display non-null false values, such as 0 or "" - val = key - key = i - - items += debug_variable(key, val, level + 1, sanitize = sanitize) - - item = "[VV_HTML_ENCODE(name)] = /list ([L.len])
      [items.Join()]
    " - else - item = "[VV_HTML_ENCODE(name)] = /list ([L.len])" - - else if (name in GLOB.bitfields) - var/list/flags = list() - for (var/i in GLOB.bitfields[name]) - if (value & GLOB.bitfields[name][i]) - flags += i - item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(jointext(flags, ", "))]" - else - item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(value)]" - - return "[header][item]
  • " - -#undef VV_HTML_ENCODE - -/client/proc/view_var_Topic(href, href_list, hsrc) - if( (usr.client != src) || !src.holder || !holder.CheckAdminHref(href, href_list)) - return - if(href_list["Vars"]) - debug_variables(locate(href_list["Vars"])) - - else if(href_list["datumrefresh"]) - var/datum/DAT = locate(href_list["datumrefresh"]) - if(!DAT) //can't be an istype() because /client etc aren't datums - return - src.debug_variables(DAT) - - else if(href_list["mob_player_panel"]) - if(!check_rights(NONE)) - return - - var/mob/M = locate(href_list["mob_player_panel"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.holder.show_player_panel(M) - - else if(href_list["godmode"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locate(href_list["godmode"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.cmd_admin_godmode(M) - - else if(href_list["mark_object"]) - if(!check_rights(NONE)) - return - - var/datum/D = locate(href_list["mark_object"]) - if(!istype(D)) - to_chat(usr, "This can only be done to instances of type /datum") - return - - if(holder.marked_datum) - vv_update_display(holder.marked_datum, "marked", "") - holder.marked_datum = D - vv_update_display(D, "marked", VV_MSG_MARKED) - - else if(href_list["proc_call"]) - if(!check_rights(NONE)) - return - - var/T = locate(href_list["proc_call"]) - - if(T) - callproc_datum(T) - - else if(href_list["delete"]) - if(!check_rights(R_DEBUG, 0)) - return - - var/datum/D = locate(href_list["delete"]) - if(!istype(D)) - to_chat(usr, "Unable to locate item!") - admin_delete(D) - if (isturf(D)) // show the turf that took its place - debug_variables(D) - - else if(href_list["osay"]) - if(!check_rights(R_FUN, 0)) - return - usr.client.object_say(locate(href_list["osay"])) - - else if(href_list["regenerateicons"]) - if(!check_rights(NONE)) - return - - var/mob/M = locate(href_list["regenerateicons"]) in GLOB.mob_list - if(!ismob(M)) - to_chat(usr, "This can only be done to instances of type /mob") - return - M.regenerate_icons() - else if(href_list["expose"]) - if(!check_rights(R_ADMIN, FALSE)) - return - var/thing = locate(href_list["expose"]) - if (!thing) - return - var/value = vv_get_value(VV_CLIENT) - if (value["class"] != VV_CLIENT) - return - var/client/C = value["value"] - if (!C) - return - var/prompt = alert("Do you want to grant [C] access to view this VV window? (they will not be able to edit or change anything nor open nested vv windows unless they themselves are an admin)", "Confirm", "Yes", "No") - if (prompt != "Yes" || !usr.client) - return - message_admins("[key_name_admin(usr)] Showed [key_name_admin(C)] a VV window") - log_admin("Admin [key_name(usr)] Showed [key_name(C)] a VV window of a [thing]") - to_chat(C, "[usr.client.holder.fakekey ? "an Administrator" : "[usr.client.key]"] has granted you access to view a View Variables window") - C.debug_variables(thing) - - -//Needs +VAREDIT past this point - - else if(check_rights(R_VAREDIT)) - - - //~CARN: for renaming mobs (updates their name, real_name, mind.name, their ID/PDA and datacore records). - - if(href_list["rename"]) - if(!check_rights(NONE)) - return - - var/mob/M = locate(href_list["rename"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - var/new_name = stripped_input(usr,"What would you like to name this mob?","Input a name",M.real_name,MAX_NAME_LEN) - if( !new_name || !M ) - return - - message_admins("Admin [key_name_admin(usr)] renamed [key_name_admin(M)] to [new_name].") - M.fully_replace_character_name(M.real_name,new_name) - vv_update_display(M, "name", new_name) - vv_update_display(M, "real_name", M.real_name || "No real name") - - else if(href_list["varnameedit"] && href_list["datumedit"]) - if(!check_rights(NONE)) - return - - var/datum/D = locate(href_list["datumedit"]) - if(!istype(D, /datum)) - to_chat(usr, "This can only be used on datums") - return - - if (!modify_variables(D, href_list["varnameedit"], 1)) - return - switch(href_list["varnameedit"]) - if("name") - vv_update_display(D, "name", "[D]") - if("dir") - if(isatom(D)) - var/dir = D.vars["dir"] - vv_update_display(D, "dir", dir2text(dir) || dir) - if("ckey") - if(isliving(D)) - vv_update_display(D, "ckey", D.vars["ckey"] || "No ckey") - if("real_name") - if(isliving(D)) - vv_update_display(D, "real_name", D.vars["real_name"] || "No real name") - - else if(href_list["varnamechange"] && href_list["datumchange"]) - if(!check_rights(NONE)) - return - - var/D = locate(href_list["datumchange"]) - if(!istype(D, /datum)) - to_chat(usr, "This can only be used on datums") - return - - modify_variables(D, href_list["varnamechange"], 0) - - else if(href_list["varnamemass"] && href_list["datummass"]) - if(!check_rights(NONE)) - return - - var/datum/D = locate(href_list["datummass"]) - if(!istype(D)) - to_chat(usr, "This can only be used on instances of type /datum") - return - - cmd_mass_modify_object_variables(D, href_list["varnamemass"]) - - else if(href_list["listedit"] && href_list["index"]) - var/index = text2num(href_list["index"]) - if (!index) - return - - var/list/L = locate(href_list["listedit"]) - if (!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - mod_list(L, null, "list", "contents", index, autodetect_class = TRUE) - - else if(href_list["listchange"] && href_list["index"]) - var/index = text2num(href_list["index"]) - if (!index) - return - - var/list/L = locate(href_list["listchange"]) - if (!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - mod_list(L, null, "list", "contents", index, autodetect_class = FALSE) - - else if(href_list["listremove"] && href_list["index"]) - var/index = text2num(href_list["index"]) - if (!index) - return - - var/list/L = locate(href_list["listremove"]) - if (!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - var/variable = L[index] - var/prompt = alert("Do you want to remove item number [index] from list?", "Confirm", "Yes", "No") - if (prompt != "Yes") - return - L.Cut(index, index+1) - log_world("### ListVarEdit by [src]: /list's contents: REMOVED=[html_encode("[variable]")]") - log_admin("[key_name(src)] modified list's contents: REMOVED=[variable]") - message_admins("[key_name_admin(src)] modified list's contents: REMOVED=[variable]") - - else if(href_list["listadd"]) - var/list/L = locate(href_list["listadd"]) - if (!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - mod_list_add(L, null, "list", "contents") - - else if(href_list["listdupes"]) - var/list/L = locate(href_list["listdupes"]) - if (!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - uniqueList_inplace(L) - log_world("### ListVarEdit by [src]: /list contents: CLEAR DUPES") - log_admin("[key_name(src)] modified list's contents: CLEAR DUPES") - message_admins("[key_name_admin(src)] modified list's contents: CLEAR DUPES") - - else if(href_list["listnulls"]) - var/list/L = locate(href_list["listnulls"]) - if (!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - listclearnulls(L) - log_world("### ListVarEdit by [src]: /list contents: CLEAR NULLS") - log_admin("[key_name(src)] modified list's contents: CLEAR NULLS") - message_admins("[key_name_admin(src)] modified list's contents: CLEAR NULLS") - - else if(href_list["listlen"]) - var/list/L = locate(href_list["listlen"]) - if (!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - var/value = vv_get_value(VV_NUM) - if (value["class"] != VV_NUM) - return - - L.len = value["value"] - log_world("### ListVarEdit by [src]: /list len: [L.len]") - log_admin("[key_name(src)] modified list's len: [L.len]") - message_admins("[key_name_admin(src)] modified list's len: [L.len]") - - else if(href_list["listshuffle"]) - var/list/L = locate(href_list["listshuffle"]) - if (!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - shuffle_inplace(L) - log_world("### ListVarEdit by [src]: /list contents: SHUFFLE") - log_admin("[key_name(src)] modified list's contents: SHUFFLE") - message_admins("[key_name_admin(src)] modified list's contents: SHUFFLE") - - else if(href_list["give_spell"]) - if(!check_rights(NONE)) - return - - var/mob/M = locate(href_list["give_spell"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.give_spell(M) - - else if(href_list["remove_spell"]) - if(!check_rights(NONE)) - return - - var/mob/M = locate(href_list["remove_spell"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - remove_spell(M) - - else if(href_list["give_disease"]) - if(!check_rights(NONE)) - return - - var/mob/M = locate(href_list["give_disease"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.give_disease(M) - - else if(href_list["gib"]) - if(!check_rights(R_FUN)) - return - - var/mob/M = locate(href_list["gib"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.cmd_admin_gib(M) - - else if(href_list["build_mode"]) - if(!check_rights(R_BUILDMODE)) - return - - var/mob/M = locate(href_list["build_mode"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - togglebuildmode(M) - - else if(href_list["drop_everything"]) - if(!check_rights(NONE)) - return - - var/mob/M = locate(href_list["drop_everything"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - if(usr.client) - usr.client.cmd_admin_drop_everything(M) - - else if(href_list["direct_control"]) - if(!check_rights(NONE)) - return - - var/mob/M = locate(href_list["direct_control"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - if(usr.client) - usr.client.cmd_assume_direct_control(M) - - else if(href_list["offer_control"]) - if(!check_rights(NONE)) - return - - var/mob/M = locate(href_list["offer_control"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - offer_control(M) - - else if (href_list["modarmor"]) - if(!check_rights(NONE)) - return - - var/obj/O = locate(href_list["modarmor"]) - if(!istype(O)) - to_chat(usr, "This can only be used on instances of type /obj") - return - - var/list/pickerlist = list() - var/list/armorlist = O.armor.getList() - - for (var/i in armorlist) - pickerlist += list(list("value" = armorlist[i], "name" = i)) - - var/list/result = presentpicker(usr, "Modify armor", "Modify armor: [O]", Button1="Save", Button2 = "Cancel", Timeout=FALSE, inputtype = "text", values = pickerlist) - - if (islist(result)) - if (result["button"] == 2) // If the user pressed the cancel button - return - // text2num conveniently returns a null on invalid values - O.armor = O.armor.setRating(melee = text2num(result["values"]["melee"]),\ - bullet = text2num(result["values"]["bullet"]),\ - laser = text2num(result["values"]["laser"]),\ - energy = text2num(result["values"]["energy"]),\ - bomb = text2num(result["values"]["bomb"]),\ - bio = text2num(result["values"]["bio"]),\ - rad = text2num(result["values"]["rad"]),\ - fire = text2num(result["values"]["fire"]),\ - acid = text2num(result["values"]["acid"])) - log_admin("[key_name(usr)] modified the armor on [O] ([O.type]) to melee: [O.armor.melee], bullet: [O.armor.bullet], laser: [O.armor.laser], energy: [O.armor.energy], bomb: [O.armor.bomb], bio: [O.armor.bio], rad: [O.armor.rad], fire: [O.armor.fire], acid: [O.armor.acid]") - message_admins("[key_name_admin(usr)] modified the armor on [O] ([O.type]) to melee: [O.armor.melee], bullet: [O.armor.bullet], laser: [O.armor.laser], energy: [O.armor.energy], bomb: [O.armor.bomb], bio: [O.armor.bio], rad: [O.armor.rad], fire: [O.armor.fire], acid: [O.armor.acid]") - else - return - - else if(href_list["delall"]) - if(!check_rights(R_DEBUG|R_SERVER)) - return - - var/obj/O = locate(href_list["delall"]) - if(!isobj(O)) - to_chat(usr, "This can only be used on instances of type /obj") - return - - var/action_type = alert("Strict type ([O.type]) or type and all subtypes?",,"Strict type","Type and subtypes","Cancel") - if(action_type == "Cancel" || !action_type) - return - - if(alert("Are you really sure you want to delete all objects of type [O.type]?",,"Yes","No") != "Yes") - return - - if(alert("Second confirmation required. Delete?",,"Yes","No") != "Yes") - return - - var/O_type = O.type - switch(action_type) - if("Strict type") - var/i = 0 - for(var/obj/Obj in world) - if(Obj.type == O_type) - i++ - qdel(Obj) - CHECK_TICK - if(!i) - to_chat(usr, "No objects of this type exist") - return - log_admin("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted) ") - message_admins("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted) ") - if("Type and subtypes") - var/i = 0 - for(var/obj/Obj in world) - if(istype(Obj,O_type)) - i++ - qdel(Obj) - CHECK_TICK - if(!i) - to_chat(usr, "No objects of this type exist") - return - log_admin("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) ") - message_admins("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) ") - - else if(href_list["addreagent"]) - if(!check_rights(NONE)) - return - - var/atom/A = locate(href_list["addreagent"]) - - if(!A.reagents) - var/amount = input(usr, "Specify the reagent size of [A]", "Set Reagent Size", 50) as num - if(amount) - A.create_reagents(amount) - - if(A.reagents) - var/chosen_id - switch(alert(usr, "Choose a method.", "Add Reagents", "Search", "Choose from a list", "I'm feeling lucky")) - if("Search") - var/valid_id - while(!valid_id) - chosen_id = input(usr, "Enter the ID of the reagent you want to add.", "Search reagents") as null|text - if(isnull(chosen_id)) //Get me out of here! - break - if(!ispath(text2path(chosen_id))) - chosen_id = pick_closest_path(chosen_id, make_types_fancy(subtypesof(/datum/reagent))) - if(ispath(chosen_id)) - valid_id = TRUE - else - valid_id = TRUE - if(!valid_id) - to_chat(usr, "A reagent with that ID doesn't exist!") - if("Choose from a list") - chosen_id = input(usr, "Choose a reagent to add.", "Choose a reagent.") as null|anything in subtypesof(/datum/reagent) - if("I'm feeling lucky") - chosen_id = pick(subtypesof(/datum/reagent)) - if(chosen_id) - var/amount = input(usr, "Choose the amount to add.", "Choose the amount.", A.reagents.maximum_volume) as num - if(amount) - A.reagents.add_reagent(chosen_id, amount) - log_admin("[key_name(usr)] has added [amount] units of [chosen_id] to \the [A]") - message_admins("[key_name(usr)] has added [amount] units of [chosen_id] to \the [A]") - - else if(href_list["explode"]) - if(!check_rights(R_FUN)) - return - - var/atom/A = locate(href_list["explode"]) - if(!isobj(A) && !ismob(A) && !isturf(A)) - to_chat(usr, "This can only be done to instances of type /obj, /mob and /turf") - return - - src.cmd_admin_explosion(A) - - else if(href_list["emp"]) - if(!check_rights(R_FUN)) - return - - var/atom/A = locate(href_list["emp"]) - if(!isobj(A) && !ismob(A) && !isturf(A)) - to_chat(usr, "This can only be done to instances of type /obj, /mob and /turf") - return - - src.cmd_admin_emp(A) - - else if(href_list["modtransform"]) - if(!check_rights(R_DEBUG)) - return - - var/atom/A = locate(href_list["modtransform"]) - if(!istype(A)) - to_chat(usr, "This can only be done to atoms.") - return - - var/result = input(usr, "Choose the transformation to apply","Transform Mod") as null|anything in list("Scale","Translate","Rotate") - var/matrix/M = A.transform - switch(result) - if("Scale") - var/x = input(usr, "Choose x mod","Transform Mod") as null|num - var/y = input(usr, "Choose y mod","Transform Mod") as null|num - if(!isnull(x) && !isnull(y)) - A.transform = M.Scale(x,y) - if("Translate") - var/x = input(usr, "Choose x mod","Transform Mod") as null|num - var/y = input(usr, "Choose y mod","Transform Mod") as null|num - if(!isnull(x) && !isnull(y)) - A.transform = M.Translate(x,y) - if("Rotate") - var/angle = input(usr, "Choose angle to rotate","Transform Mod") as null|num - if(!isnull(angle)) - A.transform = M.Turn(angle) - - else if(href_list["rotatedatum"]) - if(!check_rights(NONE)) - return - - var/atom/A = locate(href_list["rotatedatum"]) - if(!istype(A)) - to_chat(usr, "This can only be done to instances of type /atom") - return - - switch(href_list["rotatedir"]) - if("right") - A.setDir(turn(A.dir, -45)) - if("left") - A.setDir(turn(A.dir, 45)) - vv_update_display(A, "dir", dir2text(A.dir)) - - else if(href_list["editorgans"]) - if(!check_rights(NONE)) - return - - var/mob/living/carbon/C = locate(href_list["editorgans"]) in GLOB.mob_list - if(!istype(C)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon") - return - - manipulate_organs(C) - - else if(href_list["givemartialart"]) - if(!check_rights(NONE)) - return - - var/mob/living/carbon/C = locate(href_list["givemartialart"]) in GLOB.carbon_list - if(!istype(C)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon") - return - - var/list/artpaths = subtypesof(/datum/martial_art) - var/list/artnames = list() - for(var/i in artpaths) - var/datum/martial_art/M = i - artnames[initial(M.name)] = M - - var/result = input(usr, "Choose the martial art to teach","JUDO CHOP") as null|anything in artnames - if(!usr) - return - if(QDELETED(C)) - to_chat(usr, "Mob doesn't exist anymore") - return - - if(result) - var/chosenart = artnames[result] - var/datum/martial_art/MA = new chosenart - MA.teach(C) - - else if(href_list["givetrauma"]) - if(!check_rights(NONE)) - return - - var/mob/living/carbon/C = locate(href_list["givetrauma"]) in GLOB.mob_list - if(!istype(C)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon") - return - - var/list/traumas = subtypesof(/datum/brain_trauma) - var/result = input(usr, "Choose the brain trauma to apply","Traumatize") as null|anything in traumas - if(!usr) - return - if(QDELETED(C)) - to_chat(usr, "Mob doesn't exist anymore") - return - - if(result) - C.gain_trauma(result) - - else if(href_list["curetraumas"]) - if(!check_rights(NONE)) - return - - var/mob/living/carbon/C = locate(href_list["curetraumas"]) in GLOB.mob_list - if(!istype(C)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon") - return - - C.cure_all_traumas(TRAUMA_RESILIENCE_ABSOLUTE) - - else if(href_list["hallucinate"]) - if(!check_rights(NONE)) - return - - var/mob/living/carbon/C = locate(href_list["hallucinate"]) in GLOB.mob_list - if(!istype(C)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon") - return - - var/list/hallucinations = subtypesof(/datum/hallucination) - var/result = input(usr, "Choose the hallucination to apply","Send Hallucination") as null|anything in hallucinations - if(!usr) - return - if(QDELETED(C)) - to_chat(usr, "Mob doesn't exist anymore") - return - - if(result) - new result(C, TRUE) - - else if(href_list["makehuman"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/living/carbon/monkey/Mo = locate(href_list["makehuman"]) in GLOB.mob_list - if(!istype(Mo)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/monkey") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") - return - if(!Mo) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("humanone"=href_list["makehuman"])) - - else if(href_list["makemonkey"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/living/carbon/human/H = locate(href_list["makemonkey"]) in GLOB.mob_list - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") - return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("monkeyone"=href_list["makemonkey"])) - - else if(href_list["makerobot"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/living/carbon/human/H = locate(href_list["makerobot"]) in GLOB.mob_list - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") - return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("makerobot"=href_list["makerobot"])) - - else if(href_list["makealien"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/living/carbon/human/H = locate(href_list["makealien"]) in GLOB.mob_list - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") - return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("makealien"=href_list["makealien"])) - - else if(href_list["makeslime"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/living/carbon/human/H = locate(href_list["makeslime"]) in GLOB.mob_list - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") - return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("makeslime"=href_list["makeslime"])) - - else if(href_list["makeai"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/living/carbon/H = locate(href_list["makeai"]) in GLOB.mob_list - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") - return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("makeai"=href_list["makeai"])) - - else if(href_list["setspecies"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/living/carbon/human/H = locate(href_list["setspecies"]) in GLOB.mob_list - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - var/result = input(usr, "Please choose a new species","Species") as null|anything in GLOB.species_list - - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - - if(result) - var/newtype = GLOB.species_list[result] - admin_ticket_log("[key_name_admin(usr)] has modified the bodyparts of [H] to [result]") - H.set_species(newtype) - - else if(href_list["editbodypart"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/living/carbon/C = locate(href_list["editbodypart"]) in GLOB.mob_list - if(!istype(C)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon") - return - - var/edit_action = input(usr, "What would you like to do?","Modify Body Part") as null|anything in list("add","remove", "augment") - if(!edit_action) - return - var/list/limb_list = list(BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) - if(edit_action == "augment") - limb_list += BODY_ZONE_CHEST - var/result = input(usr, "Please choose which body part to [edit_action]","[capitalize(edit_action)] Body Part") as null|anything in limb_list - - if(!C) - to_chat(usr, "Mob doesn't exist anymore") - return - - if(result) - var/obj/item/bodypart/BP = C.get_bodypart(result) - switch(edit_action) - if("remove") - if(BP) - BP.drop_limb() - else - to_chat(usr, "[C] doesn't have such bodypart.") - if("add") - if(BP) - to_chat(usr, "[C] already has such bodypart.") - else - if(!C.regenerate_limb(result)) - to_chat(usr, "[C] cannot have such bodypart.") - if("augment") - if(ishuman(C)) - if(BP) - BP.change_bodypart_status(BODYPART_ROBOTIC, TRUE, TRUE) - else - to_chat(usr, "[C] doesn't have such bodypart.") - else - to_chat(usr, "Only humans can be augmented.") - admin_ticket_log("[key_name_admin(usr)] has modified the bodyparts of [C]") - - - else if(href_list["purrbation"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/living/carbon/human/H = locate(href_list["purrbation"]) in GLOB.mob_list - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - if(!ishumanbasic(H)) - to_chat(usr, "This can only be done to the basic human species at the moment.") - return - - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - - var/success = purrbation_toggle(H) - if(success) - to_chat(usr, "Put [H] on purrbation.") - log_admin("[key_name(usr)] has put [key_name(H)] on purrbation.") - var/msg = "[key_name_admin(usr)] has put [key_name(H)] on purrbation." - message_admins(msg) - admin_ticket_log(H, msg) - - else - to_chat(usr, "Removed [H] from purrbation.") - log_admin("[key_name(usr)] has removed [key_name(H)] from purrbation.") - var/msg = "[key_name_admin(usr)] has removed [key_name(H)] from purrbation." - message_admins(msg) - admin_ticket_log(H, msg) - - else if(href_list["adjustDamage"] && href_list["mobToDamage"]) - if(!check_rights(NONE)) - return - - var/mob/living/L = locate(href_list["mobToDamage"]) in GLOB.mob_list - if(!istype(L)) - return - - var/Text = href_list["adjustDamage"] - - var/amount = input("Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) as num - - if(!L) - to_chat(usr, "Mob doesn't exist anymore") - return - - var/newamt - switch(Text) - if("brute") - L.adjustBruteLoss(amount) - newamt = L.getBruteLoss() - if("fire") - L.adjustFireLoss(amount) - newamt = L.getFireLoss() - if("toxin") - L.adjustToxLoss(amount) - newamt = L.getToxLoss() - if("oxygen") - L.adjustOxyLoss(amount) - newamt = L.getOxyLoss() - if("brain") - L.adjustOrganLoss(ORGAN_SLOT_BRAIN, amount) - newamt = L.getOrganLoss(ORGAN_SLOT_BRAIN) - if("clone") - L.adjustCloneLoss(amount) - newamt = L.getCloneLoss() - if("stamina") - L.adjustStaminaLoss(amount) - newamt = L.getStaminaLoss() - if("arousal") - L.adjustArousalLoss(amount) - newamt = L.getArousalLoss() - if("heart") - L.adjustOrganLoss(ORGAN_SLOT_HEART, amount) - newamt = L.getOrganLoss(ORGAN_SLOT_HEART) - if("liver") - L.adjustOrganLoss(ORGAN_SLOT_LIVER, amount) - newamt = L.getOrganLoss(ORGAN_SLOT_LIVER) - if("lungs") - L.adjustOrganLoss(ORGAN_SLOT_LUNGS, amount) - newamt = L.getOrganLoss(ORGAN_SLOT_LUNGS) - if("eye_sight") - L.adjustOrganLoss(ORGAN_SLOT_EYES, amount) - newamt = L.getOrganLoss(ORGAN_SLOT_EYES) - if("ears") - L.adjustOrganLoss(ORGAN_SLOT_EARS, amount) - newamt = L.getOrganLoss(ORGAN_SLOT_EARS) - if("stomach") - L.adjustOrganLoss(ORGAN_SLOT_STOMACH, amount) - newamt = L.getOrganLoss(ORGAN_SLOT_STOMACH) - if("tongue") - L.adjustOrganLoss(ORGAN_SLOT_TONGUE, amount) - newamt = L.getOrganLoss(ORGAN_SLOT_TONGUE) - if("appendix") - L.adjustOrganLoss(ORGAN_SLOT_APPENDIX, amount) - newamt = L.getOrganLoss(ORGAN_SLOT_APPENDIX) - else - to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]") - return - - if(amount != 0) - log_admin("[key_name(usr)] dealt [amount] amount of [Text] damage to [L] ") - var/msg = "[key_name(usr)] dealt [amount] amount of [Text] damage to [L] " - message_admins("[msg]") - admin_ticket_log(L, msg) - vv_update_display(L, Text, "[newamt]") - else if(href_list["copyoutfit"]) - if(!check_rights(R_SPAWN)) - return - var/mob/living/carbon/human/H = locate(href_list["copyoutfit"]) in GLOB.carbon_list - if(istype(H)) - H.copy_outfit() - else if(href_list["modquirks"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/living/carbon/human/H = locate(href_list["modquirks"]) in GLOB.mob_list - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - var/list/options = list("Clear"="Clear") - for(var/x in subtypesof(/datum/quirk)) - var/datum/quirk/T = x - var/qname = initial(T.name) - options[H.has_quirk(T) ? "[qname] (Remove)" : "[qname] (Add)"] = T - - var/result = input(usr, "Choose quirk to add/remove","Quirk Mod") as null|anything in options - if(result) - if(result == "Clear") - for(var/datum/quirk/q in H.roundstart_quirks) - H.remove_quirk(q.type) - else - var/T = options[result] - if(H.has_quirk(T)) - H.remove_quirk(T) - else - H.add_quirk(T,TRUE) +/datum/proc/CanProcCall(procname) + return TRUE + +/datum/proc/can_vv_get(var_name) + return TRUE + +/datum/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited + if(var_name == NAMEOF(src, vars)) + return FALSE + vars[var_name] = var_value + datum_flags |= DF_VAR_EDITED + return TRUE + +/datum/proc/vv_get_var(var_name) + switch(var_name) + if ("vars") + return debug_variable(var_name, list(), 0, src) + return debug_variable(var_name, vars[var_name], 0, src) + +/datum/proc/can_vv_mark() + return TRUE + +//please call . = ..() first and append to the result, that way parent items are always at the top and child items are further down +//add separaters by doing . += "---" +/datum/proc/vv_get_dropdown() + . = list() + VV_DROPDOWN_OPTION("", "---") + VV_DROPDOWN_OPTION(VV_HK_CALLPROC, "Call Proc") + VV_DROPDOWN_OPTION(VV_HK_MARK, "Mark Object") + VV_DROPDOWN_OPTION(VV_HK_DELETE, "Delete") + VV_DROPDOWN_OPTION(VV_HK_EXPOSE, "Show VV To Player") +// VV_DROPDOWN_OPTION(VV_HK_MODIFY_TRAITS, "Modify Traits") + +//This proc is only called if everything topic-wise is verified. The only verifications that should happen here is things like permission checks! +//href_list is a reference, modifying it in these procs WILL change the rest of the proc in topic.dm of admin/view_variables! +//This proc is for "high level" actions like admin heal/set species/etc/etc. The low level debugging things should go in admin/view_variables/topic_basic.dm incase this runtimes. +/datum/proc/vv_do_topic(list/href_list) + if(!usr || !usr.client || !usr.client.holder || !check_rights(NONE)) + return FALSE //This is VV, not to be called by anything else. +// if(href_list[VV_HK_MODIFY_TRAITS]) +// usr.client.holder.modify_traits(src) + return TRUE + +/datum/proc/vv_get_header() + . = list() + if(("name" in vars) && !isatom(src)) + . += "[vars["name"]]
    " + +/datum/proc/on_reagent_change(changetype) + return diff --git a/code/game/atoms.dm b/code/game/atoms.dm index f89d5e87..035b75a9 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1,926 +1,979 @@ -/atom - layer = TURF_LAYER - plane = GAME_PLANE - var/level = 2 - var/article // If non-null, overrides a/an/some in all cases - - var/flags_1 = NONE - var/interaction_flags_atom = NONE - var/datum/reagents/reagents = null - - //This atom's HUD (med/sec, etc) images. Associative list. - var/list/image/hud_list = null - //HUD images that this atom can provide. - var/list/hud_possible - - /// Last name used to calculate a color for the chatmessage overlays - var/chat_color_name - /// Last color calculated for the the chatmessage overlays - var/chat_color - /// A luminescence-shifted value of the last color calculated for chatmessage overlays - var/chat_color_darkened - - //Value used to increment ex_act() if reactionary_explosions is on - var/explosion_block = 0 - - var/list/atom_colours //used to store the different colors on an atom - //its inherent color, the colored paint applied on it, special color effect etc... - - var/list/priority_overlays //overlays that should remain on top and not normally removed when using cut_overlay functions, like c4. - var/list/remove_overlays // a very temporary list of overlays to remove - var/list/add_overlays // a very temporary list of overlays to add - - var/list/managed_vis_overlays //vis overlays managed by SSvis_overlays to automaticaly turn them like other overlays - ///overlays managed by update_overlays() to prevent removing overlays that weren't added by the same proc - var/list/managed_overlays - - var/datum/proximity_monitor/proximity_monitor - var/buckle_message_cooldown = 0 - var/fingerprintslast - - var/list/filter_data //For handling persistent filters - - var/datum/component/orbiter/orbiters - - var/rad_flags = NONE // Will move to flags_1 when i can be arsed to - var/rad_insulation = RAD_NO_INSULATION - - var/icon/blood_splatter_icon - var/icon/cum_splatter_icon - var/list/fingerprints - var/list/fingerprintshidden - var/list/blood_DNA - var/list/suit_fibers - -/atom/New(loc, ...) - //atom creation method that preloads variables at creation - if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New() - world.preloader_load(src) - - if(datum_flags & DF_USE_TAG) - GenerateTag() - - var/do_initialize = SSatoms.initialized - if(do_initialize != INITIALIZATION_INSSATOMS) - args[1] = do_initialize == INITIALIZATION_INNEW_MAPLOAD - if(SSatoms.InitAtom(src, args)) - //we were deleted - return - -//Called after New if the map is being loaded. mapload = TRUE -//Called from base of New if the map is not being loaded. mapload = FALSE -//This base must be called or derivatives must set initialized to TRUE -//must not sleep -//Other parameters are passed from New (excluding loc), this does not happen if mapload is TRUE -//Must return an Initialize hint. Defined in __DEFINES/subsystems.dm - -//Note: the following functions don't call the base for optimization and must copypasta: -// /turf/Initialize -// /turf/open/space/Initialize - -/atom/proc/Initialize(mapload, ...) - if(flags_1 & INITIALIZED_1) - stack_trace("Warning: [src]([type]) initialized multiple times!") - flags_1 |= INITIALIZED_1 - - //atom color stuff - if(color) - add_atom_colour(color, FIXED_COLOUR_PRIORITY) - - if (light_power && light_range) - update_light() - - if (opacity && isturf(loc)) - var/turf/T = loc - T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guaranteed to be on afterwards anyways. - - if (canSmoothWith) - canSmoothWith = typelist("canSmoothWith", canSmoothWith) - - ComponentInitialize() - - return INITIALIZE_HINT_NORMAL - -//called if Initialize returns INITIALIZE_HINT_LATELOAD -/atom/proc/LateInitialize() - return - -// Put your AddComponent() calls here -/atom/proc/ComponentInitialize() - return - -/atom/Destroy() - if(alternate_appearances) - for(var/K in alternate_appearances) - var/datum/atom_hud/alternate_appearance/AA = alternate_appearances[K] - AA.remove_from_hud(src) - - if(reagents) - qdel(reagents) - - LAZYCLEARLIST(overlays) - LAZYCLEARLIST(priority_overlays) - - QDEL_NULL(light) - - return ..() - -/atom/proc/handle_ricochet(obj/item/projectile/P) - return - -/atom/proc/CanPass(atom/movable/mover, turf/target) - return !density - -/atom/proc/onCentCom() - var/turf/T = get_turf(src) - if(!T) - return FALSE - - if(is_reserved_level(T.z)) - for(var/A in SSshuttle.mobile) - var/obj/docking_port/mobile/M = A - if(M.launch_status == ENDGAME_TRANSIT) - for(var/place in M.shuttle_areas) - var/area/shuttle/shuttle_area = place - if(T in shuttle_area) - return TRUE - - if(!is_centcom_level(T.z))//if not, don't bother - return FALSE - - //Check for centcom itself - if(istype(T.loc, /area/centcom)) - return TRUE - - //Check for centcom shuttles - for(var/A in SSshuttle.mobile) - var/obj/docking_port/mobile/M = A - if(M.launch_status == ENDGAME_LAUNCHED) - for(var/place in M.shuttle_areas) - var/area/shuttle/shuttle_area = place - if(T in shuttle_area) - return TRUE - -/atom/proc/onSyndieBase() - var/turf/T = get_turf(src) - if(!T) - return FALSE - - if(!is_centcom_level(T.z))//if not, don't bother - return FALSE - - if(istype(T.loc, /area/shuttle/syndicate) || istype(T.loc, /area/syndicate_mothership) || istype(T.loc, /area/shuttle/assault_pod)) - return TRUE - - return FALSE - -/atom/proc/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) - SEND_SIGNAL(src, COMSIG_ATOM_HULK_ATTACK, user) - if(does_attack_animation) - user.changeNext_move(CLICK_CD_MELEE) - log_combat(user, src, "punched", "hulk powers") - user.do_attack_animation(src, ATTACK_EFFECT_SMASH) - -/atom/proc/CheckParts(list/parts_list) - for(var/A in parts_list) - if(istype(A, /datum/reagent)) - if(!reagents) - reagents = new() - reagents.reagent_list.Add(A) - reagents.conditional_update() - else if(ismovableatom(A)) - var/atom/movable/M = A - if(isliving(M.loc)) - var/mob/living/L = M.loc - L.transferItemToLoc(M, src) - else - M.forceMove(src) - -//common name -/atom/proc/update_multiz(prune_on_fail = FALSE) - return FALSE - -/atom/proc/assume_air(datum/gas_mixture/giver) - qdel(giver) - return null - -/atom/proc/remove_air(amount) - return null - -/atom/proc/return_air() - if(loc) - return loc.return_air() - else - return null - -/atom/proc/check_eye(mob/user) - return - -/atom/proc/Bumped(atom/movable/AM) - set waitfor = FALSE - -// Convenience procs to see if a container is open for chemistry handling -/atom/proc/is_open_container() - return is_refillable() && is_drainable() - -/atom/proc/is_injectable(allowmobs = TRUE) - return reagents && (reagents.reagents_holder_flags & (INJECTABLE | REFILLABLE)) - -/atom/proc/is_drawable(allowmobs = TRUE) - return reagents && (reagents.reagents_holder_flags & (DRAWABLE | DRAINABLE)) - -/atom/proc/is_refillable() - return reagents && (reagents.reagents_holder_flags & REFILLABLE) - -/atom/proc/is_drainable() - return reagents && (reagents.reagents_holder_flags & DRAINABLE) - - -/atom/proc/AllowDrop() - return FALSE - -/atom/proc/CheckExit() - return TRUE - -/atom/proc/HasProximity(atom/movable/AM as mob|obj) - return - -/atom/proc/emp_act(severity) - var/protection = SEND_SIGNAL(src, COMSIG_ATOM_EMP_ACT, severity) - if(!(protection & EMP_PROTECT_WIRES) && istype(wires)) - wires.emp_pulse() - return protection // Pass the protection value collected here upwards - -/atom/proc/bullet_act(obj/item/projectile/P, def_zone) - SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, P, def_zone) - . = P.on_hit(src, 0, def_zone) - -//used on altdisarm() for special interactions between the shoved victim (target) and the src, with user being the one shoving the target on it. -// IMPORTANT: if you wish to add a new own shove_act() to a certain object, remember to add SHOVABLE_ONTO to its obj_flags bitfied var first. -/atom/proc/shove_act(mob/living/target, mob/living/user) - return FALSE - -/atom/proc/in_contents_of(container)//can take class or object instance as argument - if(ispath(container)) - if(istype(src.loc, container)) - return TRUE - else if(src in container) - return TRUE - return FALSE - -/atom/proc/update_overlaysb() - SHOULD_CALL_PARENT(1) - . = list() - SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_OVERLAYS, .) - -/atom/proc/update_iconb() - var/signalOut = SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_ICON) - - if(!(signalOut & COMSIG_ATOM_NO_UPDATE_ICON_STATE)) - update_icon_state() - - if(!(signalOut & COMSIG_ATOM_NO_UPDATE_OVERLAYS)) - var/list/new_overlays = update_overlaysb() - if(managed_overlays) - cut_overlay(managed_overlays) - managed_overlays = null - if(length(new_overlays)) - managed_overlays = new_overlays - add_overlay(new_overlays) - -/atom/proc/get_examine_name(mob/user) - . = "\a [src]" - var/list/override = list(gender == PLURAL ? "some" : "a", " ", "[name]") - if(article) - . = "[article] [src]" - override[EXAMINE_POSITION_ARTICLE] = article - - var/should_override = FALSE - - if(SEND_SIGNAL(src, COMSIG_ATOM_GET_EXAMINE_NAME, user, override) & COMPONENT_EXNAME_CHANGED) - should_override = TRUE - - - if(blood_DNA && !istype(src, /obj/effect/decal)) - override[EXAMINE_POSITION_BEFORE] = " blood-stained " - should_override = TRUE - - if(should_override) - . = override.Join("") - -///Generate the full examine string of this atom (including icon for goonchat) -/atom/proc/get_examine_string(mob/user, thats = FALSE) - return "[icon2html(src, user)] [thats? "That's ":""][get_examine_name(user)]" - -/atom/proc/examine(mob/user) - . = list("[get_examine_string(user, TRUE)].") - - if(!isliving(src)) - user.visible_message("[user] examines [src].",\ - "You examine [src].") - if(desc) - . += desc - - if(reagents) - if(reagents.reagents_holder_flags & TRANSPARENT) - . += "It contains:" - if(length(reagents.reagent_list)) - if(user.can_see_reagents()) //Show each individual reagent - for(var/datum/reagent/R in reagents.reagent_list) - . += "[R.volume] units of [R.name]" - else //Otherwise, just show the total volume - var/total_volume = 0 - for(var/datum/reagent/R in reagents.reagent_list) - total_volume += R.volume - . += "[total_volume] units of various reagents" - else - . += "Nothing." - else if(reagents.reagents_holder_flags & AMOUNT_VISIBLE) - if(reagents.total_volume) - . += "It has [reagents.total_volume] unit\s left." - else - . += "It's empty." - SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .) - -/// Updates the icon of the atom -/atom/proc/update_icon() - // I expect we're going to need more return flags and options in this proc - var/signalOut = SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_ICON) - - if(!(signalOut & COMSIG_ATOM_NO_UPDATE_ICON_STATE)) - update_icon_state() - - if(!(signalOut & COMSIG_ATOM_NO_UPDATE_OVERLAYS)) - var/list/new_overlays = update_overlays() - if(managed_overlays) - cut_overlay(managed_overlays) - managed_overlays = null - if(length(new_overlays)) - managed_overlays = new_overlays - add_overlay(new_overlays) - -/// Updates the icon state of the atom -/atom/proc/update_icon_state() - -/// Updates the overlays of the atom -/atom/proc/update_overlays() - SHOULD_CALL_PARENT(1) - . = list() - SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_OVERLAYS, .) - -/atom/proc/relaymove(mob/user) - if(buckle_message_cooldown <= world.time) - buckle_message_cooldown = world.time + 50 - to_chat(user, "You can't move while buckled to [src]!") - return - -/atom/proc/contents_explosion(severity, target) - return //For handling the effects of explosions on contents that would not normally be effected - -/atom/proc/ex_act(severity, target) - set waitfor = FALSE - contents_explosion(severity, target) - SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, severity, target) - -/atom/proc/blob_act(obj/structure/blob/B) - SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, B) - return - -/atom/proc/fire_act(exposed_temperature, exposed_volume) - SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume) - return - -/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) - if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). - addtimer(CALLBACK(src, .proc/hitby_react, AM), 2) - -/atom/proc/hitby_react(atom/movable/AM) - if(AM && isturf(AM.loc)) - step(AM, turn(AM.dir, 180)) - -/atom/proc/handle_slip(mob/living/carbon/C, knockdown_amount, obj/O, lube) - return - -//returns the mob's dna info as a list, to be inserted in an object's blood_DNA list -/mob/living/proc/get_blood_dna_list() - var/blood_id = get_blood_id() - if(!(blood_id in GLOB.blood_reagent_types)) - return - return list("ANIMAL DNA" = "Y-") - -/mob/living/carbon/get_blood_dna_list() - var/blood_id = get_blood_id() - if(!(blood_id in GLOB.blood_reagent_types)) - return - var/list/blood_dna = list() - if(dna) - blood_dna[dna.unique_enzymes] = dna.blood_type - else - blood_dna["UNKNOWN DNA"] = "X*" - return blood_dna - -/mob/living/carbon/alien/get_blood_dna_list() - return list("UNKNOWN DNA" = "X*") - -//to add a mob's dna info into an object's blood_DNA list. -/atom/proc/transfer_mob_blood_dna(mob/living/L) - // Returns 0 if we have that blood already - var/new_blood_dna = L.get_blood_dna_list() - if(!new_blood_dna) - return FALSE - LAZYINITLIST(blood_DNA) //if our list of DNA doesn't exist yet, initialise it. - var/old_length = blood_DNA.len - blood_DNA |= new_blood_dna - if(blood_DNA.len == old_length) - return FALSE - return TRUE - -//to add blood dna info to the object's blood_DNA list -/atom/proc/transfer_blood_dna(list/blood_dna, list/datum/disease/diseases) - LAZYINITLIST(blood_DNA) - var/old_length = blood_DNA.len - blood_DNA |= blood_dna - if(blood_DNA.len > old_length) - return TRUE - //some new blood DNA was added - -//to add blood from a mob onto something, and transfer their dna info -/atom/proc/add_mob_blood(mob/living/M) - var/list/blood_dna = M.get_blood_dna_list() - if(!blood_dna) - return FALSE - return add_blood_DNA(blood_dna, M.diseases) - -//to add blood onto something, with blood dna info to include. -/atom/proc/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) - return FALSE - -/obj/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) - return transfer_blood_dna(blood_dna, diseases) - -/obj/item/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) - . = ..() - if(!.) - return - add_blood_overlay() - -/obj/item/proc/add_blood_overlay() - if(!blood_DNA.len) - return - if(initial(icon) && initial(icon_state)) - blood_splatter_icon = icon(initial(icon), initial(icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object - blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent) - blood_splatter_icon.Blend(icon('icons/effects/blood.dmi', "itemblood"), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant - blood_splatter_icon.Blend(blood_DNA_to_color(), ICON_MULTIPLY) - add_overlay(blood_splatter_icon) - -/obj/item/clothing/gloves/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) - . = ..() - transfer_blood = rand(2, 4) - -/turf/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) - var/obj/effect/decal/cleanable/blood/splatter/B = locate() in src - if(!B) - B = new /obj/effect/decal/cleanable/blood/splatter(src, diseases) - B.transfer_blood_dna(blood_dna, diseases) //give blood info to the blood decal. - return TRUE //we bloodied the floor - -/mob/living/carbon/human/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) - if(head) - head.add_blood_DNA(blood_dna, diseases) - update_inv_head() - else if(wear_mask) - wear_mask.add_blood_DNA(blood_dna, diseases) - update_inv_wear_mask() - if(wear_neck) - wear_neck.add_blood_DNA(blood_dna, diseases) - update_inv_neck() - if(wear_suit) - wear_suit.add_blood_DNA(blood_dna, diseases) - update_inv_wear_suit() - else if(w_uniform) - w_uniform.add_blood_DNA(blood_dna, diseases) - update_inv_w_uniform() - if(gloves) - var/obj/item/clothing/gloves/G = gloves - G.add_blood_DNA(blood_dna, diseases) - else - transfer_blood_dna(blood_dna, diseases) - bloody_hands = rand(2, 4) - update_inv_gloves() //handles bloody hands overlays and updating - return TRUE - -/atom/proc/blood_DNA_to_color() - var/list/colors = list()//first we make a list of all bloodtypes present - for(var/bloop in blood_DNA) - if(colors[blood_DNA[bloop]]) - colors[blood_DNA[bloop]]++ - else - colors[blood_DNA[bloop]] = 1 - - var/final_rgb = BLOOD_COLOR_HUMAN //a default so we don't have white blood graphics if something messed up - - if(colors.len) - var/sum = 0 //this is all shitcode, but it works; trust me - final_rgb = bloodtype_to_color(colors[1]) - sum = colors[colors[1]] - if(colors.len > 1) - var/i = 2 - while(i <= colors.len) - var/tmp = colors[colors[i]] - final_rgb = BlendRGB(final_rgb, bloodtype_to_color(colors[i]), tmp/(tmp+sum)) - sum += tmp - i++ - - return final_rgb - -/atom/proc/clean_blood() - . = blood_DNA? TRUE : FALSE - blood_DNA = null - -/atom/proc/wash_cream() - return TRUE - -/atom/proc/isinspace() - if(isspaceturf(get_turf(src))) - return TRUE - else - return FALSE - -/atom/proc/handle_fall() - return - -/atom/proc/singularity_act() - return - -/atom/proc/singularity_pull(obj/singularity/S, current_size) - SEND_SIGNAL(src, COMSIG_ATOM_SING_PULL, S, current_size) - -/atom/proc/acid_act(acidpwr, acid_volume) - SEND_SIGNAL(src, COMSIG_ATOM_ACID_ACT, acidpwr, acid_volume) - -/atom/proc/emag_act() - SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT) - -/atom/proc/rad_act(strength) - if(istype(get_turf(src), /turf/open/pool)) - var/turf/open/pool/PL = get_turf(src) - if(PL.filled == TRUE) - strength *= 0.15 - SEND_SIGNAL(src, COMSIG_ATOM_RAD_ACT, strength) - -/atom/proc/narsie_act() - SEND_SIGNAL(src, COMSIG_ATOM_NARSIE_ACT) - -/atom/proc/ratvar_act() - SEND_SIGNAL(src, COMSIG_ATOM_RATVAR_ACT) - -/atom/proc/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd) - return FALSE - -/atom/proc/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode) - SEND_SIGNAL(src, COMSIG_ATOM_RCD_ACT, user, the_rcd, passed_mode) - return FALSE - -/atom/proc/storage_contents_dump_act(obj/item/storage/src_object, mob/user) - if(GetComponent(/datum/component/storage)) - return component_storage_contents_dump_act(src_object, user) - return FALSE - -/atom/proc/component_storage_contents_dump_act(datum/component/storage/src_object, mob/user) - var/list/things = src_object.contents() - var/datum/progressbar/progress = new(user, things.len, src) - var/datum/component/storage/STR = GetComponent(/datum/component/storage) - while (do_after(user, 10, TRUE, src, FALSE, CALLBACK(STR, /datum/component/storage.proc/handle_mass_item_insertion, things, src_object, user, progress))) - stoplag(1) - qdel(progress) - to_chat(user, "You dump as much of [src_object.parent]'s contents into [STR.insert_preposition]to [src] as you can.") - STR.orient2hud(user) - src_object.orient2hud(user) - if(user.active_storage) //refresh the HUD to show the transfered contents - user.active_storage.close(user) - user.active_storage.show_to(user) - return TRUE - -/atom/proc/get_dumping_location(obj/item/storage/source,mob/user) - return null - -//This proc is called on the location of an atom when the atom is Destroy()'d -/atom/proc/handle_atom_del(atom/A) - SEND_SIGNAL(src, COMSIG_ATOM_CONTENTS_DEL, A) - -//called when the turf the atom resides on is ChangeTurfed -/atom/proc/HandleTurfChange(turf/T) - for(var/a in src) - var/atom/A = a - A.HandleTurfChange(T) - -//the vision impairment to give to the mob whose perspective is set to that atom (e.g. an unfocused camera giving you an impaired vision when looking through it) -/atom/proc/get_remote_view_fullscreens(mob/user) - return - -//the sight changes to give to the mob whose perspective is set to that atom (e.g. A mob with nightvision loses its nightvision while looking through a normal camera) -/atom/proc/update_remote_sight(mob/living/user) - return - - -//Hook for running code when a dir change occurs -/atom/proc/setDir(newdir) - SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir) - dir = newdir - -/atom/proc/mech_melee_attack(obj/mecha/M) - return - -//If a mob logouts/logins in side of an object you can use this proc -/atom/proc/on_log(login) - if(loc) - loc.on_log(login) - - -/* - Atom Colour Priority System - A System that gives finer control over which atom colour to colour the atom with. - The "highest priority" one is always displayed as opposed to the default of - "whichever was set last is displayed" -*/ - - -/* - Adds an instance of colour_type to the atom's atom_colours list -*/ -/atom/proc/add_atom_colour(coloration, colour_priority) - if(!atom_colours || !atom_colours.len) - atom_colours = list() - atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. - if(!coloration) - return - if(colour_priority > atom_colours.len) - return - atom_colours[colour_priority] = coloration - update_atom_colour() - - -/* - Removes an instance of colour_type from the atom's atom_colours list -*/ -/atom/proc/remove_atom_colour(colour_priority, coloration) - if(!atom_colours) - atom_colours = list() - atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. - if(colour_priority > atom_colours.len) - return - if(coloration && atom_colours[colour_priority] != coloration) - return //if we don't have the expected color (for a specific priority) to remove, do nothing - atom_colours[colour_priority] = null - update_atom_colour() - - -/* - Resets the atom's color to null, and then sets it to the highest priority - colour available -*/ -/atom/proc/update_atom_colour() - if(!atom_colours) - atom_colours = list() - atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. - color = null - for(var/C in atom_colours) - if(islist(C)) - var/list/L = C - if(L.len) - color = L - return - else if(C) - color = C - return - -/atom/vv_edit_var(var_name, var_value) - if(!GLOB.Debug2) - flags_1 |= ADMIN_SPAWNED_1 - . = ..() - switch(var_name) - if("color") - add_atom_colour(color, ADMIN_COLOUR_PRIORITY) - -/atom/vv_get_dropdown() - . = ..() - . += "---" - var/turf/curturf = get_turf(src) - if (curturf) - .["Jump to"] = "?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]" - .["Modify Transform"] = "?_src_=vars;[HrefToken()];modtransform=[REF(src)]" - .["Add reagent"] = "?_src_=vars;[HrefToken()];addreagent=[REF(src)]" - .["Trigger EM pulse"] = "?_src_=vars;[HrefToken()];emp=[REF(src)]" - .["Trigger explosion"] = "?_src_=vars;[HrefToken()];explode=[REF(src)]" - -/atom/proc/drop_location() - var/atom/L = loc - if(!L) - return null - return L.AllowDrop() ? L : L.drop_location() - -/atom/Entered(atom/movable/AM, atom/oldLoc) - SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, AM, oldLoc) - -/atom/Exit(atom/movable/AM, atom/newLoc) - . = ..() - if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, AM, newLoc) & COMPONENT_ATOM_BLOCK_EXIT) - return FALSE - -/atom/Exited(atom/movable/AM, atom/newLoc) - SEND_SIGNAL(src, COMSIG_ATOM_EXITED, AM, newLoc) - -/atom/proc/return_temperature() - return - -// Tool behavior procedure. Redirects to tool-specific procs by default. -// You can override it to catch all tool interactions, for use in complex deconstruction procs. -// Just don't forget to return ..() in the end. -/atom/proc/tool_act(mob/living/user, obj/item/I, tool_type) - switch(tool_type) - if(TOOL_CROWBAR) - return crowbar_act(user, I) - if(TOOL_MULTITOOL) - return multitool_act(user, I) - if(TOOL_SCREWDRIVER) - return screwdriver_act(user, I) - if(TOOL_WRENCH) - return wrench_act(user, I) - if(TOOL_WIRECUTTER) - return wirecutter_act(user, I) - if(TOOL_WELDER) - return welder_act(user, I) - if(TOOL_ANALYZER) - return analyzer_act(user, I) - -// Tool-specific behavior procs. To be overridden in subtypes. -/atom/proc/crowbar_act(mob/living/user, obj/item/I) - return - -/atom/proc/multitool_act(mob/living/user, obj/item/I) - return - -/atom/proc/multitool_check_buffer(user, obj/item/I, silent = FALSE) - if(!istype(I, /obj/item/multitool)) - if(user && !silent) - to_chat(user, "[I] has no data buffer!") - return FALSE - return TRUE - -/atom/proc/screwdriver_act(mob/living/user, obj/item/I) - SEND_SIGNAL(src, COMSIG_ATOM_SCREWDRIVER_ACT, user, I) - -/atom/proc/wrench_act(mob/living/user, obj/item/I) - return - -/atom/proc/wirecutter_act(mob/living/user, obj/item/I) - return - -/atom/proc/welder_act(mob/living/user, obj/item/I) - return - -/atom/proc/analyzer_act(mob/living/user, obj/item/I) - return - -/atom/proc/GenerateTag() - return - -// Generic logging helper -/atom/proc/log_message(message, message_type, color=null, log_globally=TRUE) - if(!log_globally) - return - - var/log_text = "[key_name(src)] [message] [loc_name(src)]" - switch(message_type) - if(LOG_ATTACK) - log_attack(log_text) - if(LOG_SAY) - log_say(log_text) - if(LOG_WHISPER) - log_whisper(log_text) - if(LOG_EMOTE) - log_emote(log_text) - if(LOG_DSAY) - log_dsay(log_text) - if(LOG_PDA) - log_pda(log_text) - if(LOG_CHAT) - log_chat(log_text) - if(LOG_COMMENT) - log_comment(log_text) - if(LOG_TELECOMMS) - log_telecomms(log_text) - if(LOG_OOC) - log_ooc(log_text) - if(LOG_ADMIN) - log_admin(log_text) - if(LOG_ADMIN_PRIVATE) - log_admin_private(log_text) - if(LOG_ASAY) - log_adminsay(log_text) - if(LOG_OWNERSHIP) - log_game(log_text) - if(LOG_GAME) - log_game(log_text) - else - stack_trace("Invalid individual logging type: [message_type]. Defaulting to [LOG_GAME] (LOG_GAME).") - log_game(log_text) - -// Helper for logging chat messages or other logs with arbitrary inputs (e.g. announcements) -/atom/proc/log_talk(message, message_type, tag=null, log_globally=TRUE, forced_by=null) - var/prefix = tag ? "([tag]) " : "" - var/suffix = forced_by ? " FORCED by [forced_by]" : "" - log_message("[prefix]\"[message]\"[suffix]", message_type, log_globally=log_globally) - -// Helper for logging of messages with only one sender and receiver -/proc/log_directed_talk(atom/source, atom/target, message, message_type, tag) - if(!tag) - stack_trace("Unspecified tag for private message") - tag = "UNKNOWN" - - source.log_talk(message, message_type, tag="[tag] to [key_name(target)]") - if(source != target) - target.log_talk(message, message_type, tag="[tag] from [key_name(source)]", log_globally=FALSE) - -/* -Proc for attack log creation, because really why not -1 argument is the actor performing the action -2 argument is the target of the action -3 is a verb describing the action (e.g. punched, throwed, kicked, etc.) -4 is a tool with which the action was made (usually an item) -5 is any additional text, which will be appended to the rest of the log line -*/ - -/proc/log_combat(atom/user, atom/target, what_done, atom/object=null, addition=null) - var/ssource = key_name(user) - var/starget = key_name(target) - - var/mob/living/living_target = target - var/hp = istype(living_target) ? " (NEWHP: [living_target.health]) " : "" - - var/sobject = "" - if(object) - sobject = " with [key_name(object)]" - var/saddition = "" - if(addition) - saddition = " [addition]" - - var/postfix = "[sobject][saddition][hp]" - - var/message = "has [what_done] [starget][postfix]" - user.log_message(message, LOG_ATTACK, color="red") - - if(user != target) - var/reverse_message = "has been [what_done] by [ssource][postfix]" - target.log_message(reverse_message, LOG_ATTACK, color="orange", log_globally=FALSE) - -// Filter stuff -/atom/movable/proc/add_filter(name,priority,list/params) - LAZYINITLIST(filter_data) - var/list/p = params.Copy() - p["priority"] = priority - filter_data[name] = p - update_filters() - -/atom/movable/proc/update_filters() - filters = null - filter_data = sortTim(filter_data, /proc/cmp_filter_data_priority, TRUE) - for(var/f in filter_data) - var/list/data = filter_data[f] - var/list/arguments = data.Copy() - arguments -= "priority" - filters += filter(arglist(arguments)) - -/atom/movable/proc/get_filter(name) - if(filter_data) - if(filter_data[name]) - return filters[filter_data.Find(name)] - -/atom/movable/proc/remove_filter(name) - if(filter_data) - if(filter_data[name]) - filter_data -= name - update_filters() - return TRUE - -/atom/proc/intercept_zImpact(atom/movable/AM, levels = 1) - . |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, AM, levels) - -///Passes Stat Browser Panel clicks to the game and calls client click on an atom -/atom/Topic(href, list/href_list) - . = ..() - if(!usr?.client) - return - var/client/usr_client = usr.client - var/list/paramslist = list() - if(href_list["statpanel_item_shiftclick"]) - paramslist["shift"] = "1" - if(href_list["statpanel_item_ctrlclick"]) - paramslist["ctrl"] = "1" - if(href_list["statpanel_item_altclick"]) - paramslist["alt"] = "1" - if(href_list["statpanel_item_click"]) - // first of all make sure we valid - var/mouseparams = list2params(paramslist) - usr_client.Click(src, loc, null, mouseparams) +/atom + layer = TURF_LAYER + plane = GAME_PLANE + var/level = 2 + var/article // If non-null, overrides a/an/some in all cases + + var/flags_1 = NONE + var/interaction_flags_atom = NONE + var/datum/reagents/reagents = null + + //This atom's HUD (med/sec, etc) images. Associative list. + var/list/image/hud_list = null + //HUD images that this atom can provide. + var/list/hud_possible + + /// Last name used to calculate a color for the chatmessage overlays + var/chat_color_name + /// Last color calculated for the the chatmessage overlays + var/chat_color + /// A luminescence-shifted value of the last color calculated for chatmessage overlays + var/chat_color_darkened + + //Value used to increment ex_act() if reactionary_explosions is on + var/explosion_block = 0 + + var/list/atom_colours //used to store the different colors on an atom + //its inherent color, the colored paint applied on it, special color effect etc... + + var/list/priority_overlays //overlays that should remain on top and not normally removed when using cut_overlay functions, like c4. + var/list/remove_overlays // a very temporary list of overlays to remove + var/list/add_overlays // a very temporary list of overlays to add + + var/list/managed_vis_overlays //vis overlays managed by SSvis_overlays to automaticaly turn them like other overlays + ///overlays managed by update_overlays() to prevent removing overlays that weren't added by the same proc + var/list/managed_overlays + + var/datum/proximity_monitor/proximity_monitor + var/buckle_message_cooldown = 0 + var/fingerprintslast + + var/list/filter_data //For handling persistent filters + + var/datum/component/orbiter/orbiters + + var/rad_flags = NONE // Will move to flags_1 when i can be arsed to + var/rad_insulation = RAD_NO_INSULATION + + var/icon/blood_splatter_icon + var/icon/cum_splatter_icon + var/list/fingerprints + var/list/fingerprintshidden + var/list/blood_DNA + var/list/suit_fibers + +/atom/New(loc, ...) + //atom creation method that preloads variables at creation + if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New() + world.preloader_load(src) + + if(datum_flags & DF_USE_TAG) + GenerateTag() + + var/do_initialize = SSatoms.initialized + if(do_initialize != INITIALIZATION_INSSATOMS) + args[1] = do_initialize == INITIALIZATION_INNEW_MAPLOAD + if(SSatoms.InitAtom(src, args)) + //we were deleted + return + +//Called after New if the map is being loaded. mapload = TRUE +//Called from base of New if the map is not being loaded. mapload = FALSE +//This base must be called or derivatives must set initialized to TRUE +//must not sleep +//Other parameters are passed from New (excluding loc), this does not happen if mapload is TRUE +//Must return an Initialize hint. Defined in __DEFINES/subsystems.dm + +//Note: the following functions don't call the base for optimization and must copypasta: +// /turf/Initialize +// /turf/open/space/Initialize + +/atom/proc/Initialize(mapload, ...) + if(flags_1 & INITIALIZED_1) + stack_trace("Warning: [src]([type]) initialized multiple times!") + flags_1 |= INITIALIZED_1 + + //atom color stuff + if(color) + add_atom_colour(color, FIXED_COLOUR_PRIORITY) + + if (light_power && light_range) + update_light() + + if (opacity && isturf(loc)) + var/turf/T = loc + T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guaranteed to be on afterwards anyways. + + if (canSmoothWith) + canSmoothWith = typelist("canSmoothWith", canSmoothWith) + + ComponentInitialize() + + return INITIALIZE_HINT_NORMAL + +//called if Initialize returns INITIALIZE_HINT_LATELOAD +/atom/proc/LateInitialize() + return + +// Put your AddComponent() calls here +/atom/proc/ComponentInitialize() + return + +/atom/Destroy() + if(alternate_appearances) + for(var/K in alternate_appearances) + var/datum/atom_hud/alternate_appearance/AA = alternate_appearances[K] + AA.remove_from_hud(src) + + if(reagents) + qdel(reagents) + + LAZYCLEARLIST(overlays) + LAZYCLEARLIST(priority_overlays) + + QDEL_NULL(light) + + return ..() + +/atom/proc/handle_ricochet(obj/item/projectile/P) + return + +/atom/proc/CanPass(atom/movable/mover, turf/target) + return !density + +/atom/proc/onCentCom() + var/turf/T = get_turf(src) + if(!T) + return FALSE + + if(is_reserved_level(T.z)) + for(var/A in SSshuttle.mobile) + var/obj/docking_port/mobile/M = A + if(M.launch_status == ENDGAME_TRANSIT) + for(var/place in M.shuttle_areas) + var/area/shuttle/shuttle_area = place + if(T in shuttle_area) + return TRUE + + if(!is_centcom_level(T.z))//if not, don't bother + return FALSE + + //Check for centcom itself + if(istype(T.loc, /area/centcom)) + return TRUE + + //Check for centcom shuttles + for(var/A in SSshuttle.mobile) + var/obj/docking_port/mobile/M = A + if(M.launch_status == ENDGAME_LAUNCHED) + for(var/place in M.shuttle_areas) + var/area/shuttle/shuttle_area = place + if(T in shuttle_area) + return TRUE + +/atom/proc/onSyndieBase() + var/turf/T = get_turf(src) + if(!T) + return FALSE + + if(!is_centcom_level(T.z))//if not, don't bother + return FALSE + + if(istype(T.loc, /area/shuttle/syndicate) || istype(T.loc, /area/syndicate_mothership) || istype(T.loc, /area/shuttle/assault_pod)) + return TRUE + + return FALSE + +/atom/proc/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) + SEND_SIGNAL(src, COMSIG_ATOM_HULK_ATTACK, user) + if(does_attack_animation) + user.changeNext_move(CLICK_CD_MELEE) + log_combat(user, src, "punched", "hulk powers") + user.do_attack_animation(src, ATTACK_EFFECT_SMASH) + +/atom/proc/CheckParts(list/parts_list) + for(var/A in parts_list) + if(istype(A, /datum/reagent)) + if(!reagents) + reagents = new() + reagents.reagent_list.Add(A) + reagents.conditional_update() + else if(ismovableatom(A)) + var/atom/movable/M = A + if(isliving(M.loc)) + var/mob/living/L = M.loc + L.transferItemToLoc(M, src) + else + M.forceMove(src) + +//common name +/atom/proc/update_multiz(prune_on_fail = FALSE) + return FALSE + +/atom/proc/assume_air(datum/gas_mixture/giver) + qdel(giver) + return null + +/atom/proc/remove_air(amount) + return null + +/atom/proc/return_air() + if(loc) + return loc.return_air() + else + return null + +/atom/proc/check_eye(mob/user) + return + +/atom/proc/Bumped(atom/movable/AM) + set waitfor = FALSE + +// Convenience procs to see if a container is open for chemistry handling +/atom/proc/is_open_container() + return is_refillable() && is_drainable() + +/atom/proc/is_injectable(allowmobs = TRUE) + return reagents && (reagents.reagents_holder_flags & (INJECTABLE | REFILLABLE)) + +/atom/proc/is_drawable(allowmobs = TRUE) + return reagents && (reagents.reagents_holder_flags & (DRAWABLE | DRAINABLE)) + +/atom/proc/is_refillable() + return reagents && (reagents.reagents_holder_flags & REFILLABLE) + +/atom/proc/is_drainable() + return reagents && (reagents.reagents_holder_flags & DRAINABLE) + + +/atom/proc/AllowDrop() + return FALSE + +/atom/proc/CheckExit() + return TRUE + +/atom/proc/HasProximity(atom/movable/AM as mob|obj) + return + +/atom/proc/emp_act(severity) + var/protection = SEND_SIGNAL(src, COMSIG_ATOM_EMP_ACT, severity) + if(!(protection & EMP_PROTECT_WIRES) && istype(wires)) + wires.emp_pulse() + return protection // Pass the protection value collected here upwards + +/atom/proc/bullet_act(obj/item/projectile/P, def_zone) + SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, P, def_zone) + . = P.on_hit(src, 0, def_zone) + +//used on altdisarm() for special interactions between the shoved victim (target) and the src, with user being the one shoving the target on it. +// IMPORTANT: if you wish to add a new own shove_act() to a certain object, remember to add SHOVABLE_ONTO to its obj_flags bitfied var first. +/atom/proc/shove_act(mob/living/target, mob/living/user) + return FALSE + +/atom/proc/in_contents_of(container)//can take class or object instance as argument + if(ispath(container)) + if(istype(src.loc, container)) + return TRUE + else if(src in container) + return TRUE + return FALSE + +/atom/proc/update_overlaysb() + SHOULD_CALL_PARENT(1) + . = list() + SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_OVERLAYS, .) + +/atom/proc/update_iconb() + var/signalOut = SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_ICON) + + if(!(signalOut & COMSIG_ATOM_NO_UPDATE_ICON_STATE)) + update_icon_state() + + if(!(signalOut & COMSIG_ATOM_NO_UPDATE_OVERLAYS)) + var/list/new_overlays = update_overlaysb() + if(managed_overlays) + cut_overlay(managed_overlays) + managed_overlays = null + if(length(new_overlays)) + managed_overlays = new_overlays + add_overlay(new_overlays) + +/atom/proc/get_examine_name(mob/user) + . = "\a [src]" + var/list/override = list(gender == PLURAL ? "some" : "a", " ", "[name]") + if(article) + . = "[article] [src]" + override[EXAMINE_POSITION_ARTICLE] = article + + var/should_override = FALSE + + if(SEND_SIGNAL(src, COMSIG_ATOM_GET_EXAMINE_NAME, user, override) & COMPONENT_EXNAME_CHANGED) + should_override = TRUE + + + if(blood_DNA && !istype(src, /obj/effect/decal)) + override[EXAMINE_POSITION_BEFORE] = " blood-stained " + should_override = TRUE + + if(should_override) + . = override.Join("") + +///Generate the full examine string of this atom (including icon for goonchat) +/atom/proc/get_examine_string(mob/user, thats = FALSE) + return "[icon2html(src, user)] [thats? "That's ":""][get_examine_name(user)]" + +/atom/proc/examine(mob/user) + . = list("[get_examine_string(user, TRUE)].") + + if(!isliving(src)) + user.visible_message("[user] examines [src].",\ + "You examine [src].") + if(desc) + . += desc + + if(reagents) + if(reagents.reagents_holder_flags & TRANSPARENT) + . += "It contains:" + if(length(reagents.reagent_list)) + if(user.can_see_reagents()) //Show each individual reagent + for(var/datum/reagent/R in reagents.reagent_list) + . += "[R.volume] units of [R.name]" + else //Otherwise, just show the total volume + var/total_volume = 0 + for(var/datum/reagent/R in reagents.reagent_list) + total_volume += R.volume + . += "[total_volume] units of various reagents" + else + . += "Nothing." + else if(reagents.reagents_holder_flags & AMOUNT_VISIBLE) + if(reagents.total_volume) + . += "It has [reagents.total_volume] unit\s left." + else + . += "It's empty." + SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .) + +/// Updates the icon of the atom +/atom/proc/update_icon() + // I expect we're going to need more return flags and options in this proc + var/signalOut = SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_ICON) + + if(!(signalOut & COMSIG_ATOM_NO_UPDATE_ICON_STATE)) + update_icon_state() + + if(!(signalOut & COMSIG_ATOM_NO_UPDATE_OVERLAYS)) + var/list/new_overlays = update_overlays() + if(managed_overlays) + cut_overlay(managed_overlays) + managed_overlays = null + if(length(new_overlays)) + managed_overlays = new_overlays + add_overlay(new_overlays) + +/// Updates the icon state of the atom +/atom/proc/update_icon_state() + +/// Updates the overlays of the atom +/atom/proc/update_overlays() + SHOULD_CALL_PARENT(1) + . = list() + SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_OVERLAYS, .) + +/atom/proc/relaymove(mob/user) + if(buckle_message_cooldown <= world.time) + buckle_message_cooldown = world.time + 50 + to_chat(user, "You can't move while buckled to [src]!") + return + +/atom/proc/contents_explosion(severity, target) + return //For handling the effects of explosions on contents that would not normally be effected + +/atom/proc/ex_act(severity, target) + set waitfor = FALSE + contents_explosion(severity, target) + SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, severity, target) + +/atom/proc/blob_act(obj/structure/blob/B) + SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, B) + return + +/atom/proc/fire_act(exposed_temperature, exposed_volume) + SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume) + return + +/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) + if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). + addtimer(CALLBACK(src, .proc/hitby_react, AM), 2) + +/atom/proc/hitby_react(atom/movable/AM) + if(AM && isturf(AM.loc)) + step(AM, turn(AM.dir, 180)) + +/atom/proc/handle_slip(mob/living/carbon/C, knockdown_amount, obj/O, lube) + return + +//returns the mob's dna info as a list, to be inserted in an object's blood_DNA list +/mob/living/proc/get_blood_dna_list() + var/blood_id = get_blood_id() + if(!(blood_id in GLOB.blood_reagent_types)) + return + return list("ANIMAL DNA" = "Y-") + +/mob/living/carbon/get_blood_dna_list() + var/blood_id = get_blood_id() + if(!(blood_id in GLOB.blood_reagent_types)) + return + var/list/blood_dna = list() + if(dna) + blood_dna[dna.unique_enzymes] = dna.blood_type + else + blood_dna["UNKNOWN DNA"] = "X*" + return blood_dna + +/mob/living/carbon/alien/get_blood_dna_list() + return list("UNKNOWN DNA" = "X*") + +//to add a mob's dna info into an object's blood_DNA list. +/atom/proc/transfer_mob_blood_dna(mob/living/L) + // Returns 0 if we have that blood already + var/new_blood_dna = L.get_blood_dna_list() + if(!new_blood_dna) + return FALSE + LAZYINITLIST(blood_DNA) //if our list of DNA doesn't exist yet, initialise it. + var/old_length = blood_DNA.len + blood_DNA |= new_blood_dna + if(blood_DNA.len == old_length) + return FALSE + return TRUE + +//to add blood dna info to the object's blood_DNA list +/atom/proc/transfer_blood_dna(list/blood_dna, list/datum/disease/diseases) + LAZYINITLIST(blood_DNA) + var/old_length = blood_DNA.len + blood_DNA |= blood_dna + if(blood_DNA.len > old_length) + return TRUE + //some new blood DNA was added + +//to add blood from a mob onto something, and transfer their dna info +/atom/proc/add_mob_blood(mob/living/M) + var/list/blood_dna = M.get_blood_dna_list() + if(!blood_dna) + return FALSE + return add_blood_DNA(blood_dna, M.diseases) + +//to add blood onto something, with blood dna info to include. +/atom/proc/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) + return FALSE + +/obj/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) + return transfer_blood_dna(blood_dna, diseases) + +/obj/item/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) + . = ..() + if(!.) + return + add_blood_overlay() + +/obj/item/proc/add_blood_overlay() + if(!blood_DNA.len) + return + if(initial(icon) && initial(icon_state)) + blood_splatter_icon = icon(initial(icon), initial(icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object + blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent) + blood_splatter_icon.Blend(icon('icons/effects/blood.dmi', "itemblood"), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant + blood_splatter_icon.Blend(blood_DNA_to_color(), ICON_MULTIPLY) + add_overlay(blood_splatter_icon) + +/obj/item/clothing/gloves/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) + . = ..() + transfer_blood = rand(2, 4) + +/turf/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) + var/obj/effect/decal/cleanable/blood/splatter/B = locate() in src + if(!B) + B = new /obj/effect/decal/cleanable/blood/splatter(src, diseases) + B.transfer_blood_dna(blood_dna, diseases) //give blood info to the blood decal. + return TRUE //we bloodied the floor + +/mob/living/carbon/human/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) + if(head) + head.add_blood_DNA(blood_dna, diseases) + update_inv_head() + else if(wear_mask) + wear_mask.add_blood_DNA(blood_dna, diseases) + update_inv_wear_mask() + if(wear_neck) + wear_neck.add_blood_DNA(blood_dna, diseases) + update_inv_neck() + if(wear_suit) + wear_suit.add_blood_DNA(blood_dna, diseases) + update_inv_wear_suit() + else if(w_uniform) + w_uniform.add_blood_DNA(blood_dna, diseases) + update_inv_w_uniform() + if(gloves) + var/obj/item/clothing/gloves/G = gloves + G.add_blood_DNA(blood_dna, diseases) + else + transfer_blood_dna(blood_dna, diseases) + bloody_hands = rand(2, 4) + update_inv_gloves() //handles bloody hands overlays and updating + return TRUE + +/atom/proc/blood_DNA_to_color() + var/list/colors = list()//first we make a list of all bloodtypes present + for(var/bloop in blood_DNA) + if(colors[blood_DNA[bloop]]) + colors[blood_DNA[bloop]]++ + else + colors[blood_DNA[bloop]] = 1 + + var/final_rgb = BLOOD_COLOR_HUMAN //a default so we don't have white blood graphics if something messed up + + if(colors.len) + var/sum = 0 //this is all shitcode, but it works; trust me + final_rgb = bloodtype_to_color(colors[1]) + sum = colors[colors[1]] + if(colors.len > 1) + var/i = 2 + while(i <= colors.len) + var/tmp = colors[colors[i]] + final_rgb = BlendRGB(final_rgb, bloodtype_to_color(colors[i]), tmp/(tmp+sum)) + sum += tmp + i++ + + return final_rgb + +/atom/proc/clean_blood() + . = blood_DNA? TRUE : FALSE + blood_DNA = null + +/atom/proc/wash_cream() + return TRUE + +/atom/proc/isinspace() + if(isspaceturf(get_turf(src))) + return TRUE + else + return FALSE + +/atom/proc/handle_fall() + return + +/atom/proc/singularity_act() + return + +/atom/proc/singularity_pull(obj/singularity/S, current_size) + SEND_SIGNAL(src, COMSIG_ATOM_SING_PULL, S, current_size) + +/atom/proc/acid_act(acidpwr, acid_volume) + SEND_SIGNAL(src, COMSIG_ATOM_ACID_ACT, acidpwr, acid_volume) + +/atom/proc/emag_act() + SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT) + +/atom/proc/rad_act(strength) + if(istype(get_turf(src), /turf/open/pool)) + var/turf/open/pool/PL = get_turf(src) + if(PL.filled == TRUE) + strength *= 0.15 + SEND_SIGNAL(src, COMSIG_ATOM_RAD_ACT, strength) + +/atom/proc/narsie_act() + SEND_SIGNAL(src, COMSIG_ATOM_NARSIE_ACT) + +/atom/proc/ratvar_act() + SEND_SIGNAL(src, COMSIG_ATOM_RATVAR_ACT) + +/atom/proc/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd) + return FALSE + +/atom/proc/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode) + SEND_SIGNAL(src, COMSIG_ATOM_RCD_ACT, user, the_rcd, passed_mode) + return FALSE + +/atom/proc/storage_contents_dump_act(obj/item/storage/src_object, mob/user) + if(GetComponent(/datum/component/storage)) + return component_storage_contents_dump_act(src_object, user) + return FALSE + +/atom/proc/component_storage_contents_dump_act(datum/component/storage/src_object, mob/user) + var/list/things = src_object.contents() + var/datum/progressbar/progress = new(user, things.len, src) + var/datum/component/storage/STR = GetComponent(/datum/component/storage) + while (do_after(user, 10, TRUE, src, FALSE, CALLBACK(STR, /datum/component/storage.proc/handle_mass_item_insertion, things, src_object, user, progress))) + stoplag(1) + qdel(progress) + to_chat(user, "You dump as much of [src_object.parent]'s contents into [STR.insert_preposition]to [src] as you can.") + STR.orient2hud(user) + src_object.orient2hud(user) + if(user.active_storage) //refresh the HUD to show the transfered contents + user.active_storage.close(user) + user.active_storage.show_to(user) + return TRUE + +/atom/proc/get_dumping_location(obj/item/storage/source,mob/user) + return null + +//This proc is called on the location of an atom when the atom is Destroy()'d +/atom/proc/handle_atom_del(atom/A) + SEND_SIGNAL(src, COMSIG_ATOM_CONTENTS_DEL, A) + +//called when the turf the atom resides on is ChangeTurfed +/atom/proc/HandleTurfChange(turf/T) + for(var/a in src) + var/atom/A = a + A.HandleTurfChange(T) + +//the vision impairment to give to the mob whose perspective is set to that atom (e.g. an unfocused camera giving you an impaired vision when looking through it) +/atom/proc/get_remote_view_fullscreens(mob/user) + return + +//the sight changes to give to the mob whose perspective is set to that atom (e.g. A mob with nightvision loses its nightvision while looking through a normal camera) +/atom/proc/update_remote_sight(mob/living/user) + return + + +//Hook for running code when a dir change occurs +/atom/proc/setDir(newdir) + SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir) + dir = newdir + +/atom/proc/mech_melee_attack(obj/mecha/M) + return + +//If a mob logouts/logins in side of an object you can use this proc +/atom/proc/on_log(login) + if(loc) + loc.on_log(login) + + +/* + Atom Colour Priority System + A System that gives finer control over which atom colour to colour the atom with. + The "highest priority" one is always displayed as opposed to the default of + "whichever was set last is displayed" +*/ + + +/* + Adds an instance of colour_type to the atom's atom_colours list +*/ +/atom/proc/add_atom_colour(coloration, colour_priority) + if(!atom_colours || !atom_colours.len) + atom_colours = list() + atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. + if(!coloration) + return + if(colour_priority > atom_colours.len) + return + atom_colours[colour_priority] = coloration + update_atom_colour() + + +/* + Removes an instance of colour_type from the atom's atom_colours list +*/ +/atom/proc/remove_atom_colour(colour_priority, coloration) + if(!atom_colours) + atom_colours = list() + atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. + if(colour_priority > atom_colours.len) + return + if(coloration && atom_colours[colour_priority] != coloration) + return //if we don't have the expected color (for a specific priority) to remove, do nothing + atom_colours[colour_priority] = null + update_atom_colour() + + +/* + Resets the atom's color to null, and then sets it to the highest priority + colour available +*/ +/atom/proc/update_atom_colour() + if(!atom_colours) + atom_colours = list() + atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. + color = null + for(var/C in atom_colours) + if(islist(C)) + var/list/L = C + if(L.len) + color = L + return + else if(C) + color = C + return + +/atom/vv_edit_var(var_name, var_value) + if(!GLOB.Debug2) + flags_1 |= ADMIN_SPAWNED_1 + . = ..() + switch(var_name) + if("color") + add_atom_colour(color, ADMIN_COLOUR_PRIORITY) + +/atom/vv_get_dropdown() + . = ..() + VV_DROPDOWN_OPTION("", "---------") + if(!ismovableatom(src)) + var/turf/curturf = get_turf(src) + if(curturf) + . += "" + VV_DROPDOWN_OPTION(VV_HK_MODIFY_TRANSFORM, "Modify Transform") + VV_DROPDOWN_OPTION(VV_HK_ADD_REAGENT, "Add Reagent") + VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EMP, "EMP Pulse") + VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EXPLOSION, "Explosion") + +/atom/vv_do_topic(list/href_list) + . = ..() + if(href_list[VV_HK_ADD_REAGENT] && check_rights(R_VAREDIT)) + if(!reagents) + var/amount = input(usr, "Specify the reagent size of [src]", "Set Reagent Size", 50) as num + if(amount) + create_reagents(amount) + + if(reagents) + var/chosen_id = choose_reagent_id(usr) + if(chosen_id) + var/amount = input(usr, "Choose the amount to add.", "Choose the amount.", reagents.maximum_volume) as num + if(amount) + reagents.add_reagent(chosen_id, amount) + log_admin("[key_name(usr)] has added [amount] units of [chosen_id] to [src]") + message_admins("[key_name(usr)] has added [amount] units of [chosen_id] to [src]") + if(href_list[VV_HK_TRIGGER_EXPLOSION] && check_rights(R_FUN)) + usr.client.cmd_admin_explosion(src) + if(href_list[VV_HK_TRIGGER_EMP] && check_rights(R_FUN)) + usr.client.cmd_admin_emp(src) + if(href_list[VV_HK_MODIFY_TRANSFORM] && check_rights(R_VAREDIT)) + var/result = input(usr, "Choose the transformation to apply","Transform Mod") as null|anything in list("Scale","Translate","Rotate") + var/matrix/M = transform + switch(result) + if("Scale") + var/x = input(usr, "Choose x mod","Transform Mod") as null|num + var/y = input(usr, "Choose y mod","Transform Mod") as null|num + if(!isnull(x) && !isnull(y)) + transform = M.Scale(x,y) + if("Translate") + var/x = input(usr, "Choose x mod","Transform Mod") as null|num + var/y = input(usr, "Choose y mod","Transform Mod") as null|num + if(!isnull(x) && !isnull(y)) + transform = M.Translate(x,y) + if("Rotate") + var/angle = input(usr, "Choose angle to rotate","Transform Mod") as null|num + if(!isnull(angle)) + transform = M.Turn(angle) + if(href_list[VV_HK_AUTO_RENAME] && check_rights(R_VAREDIT)) + var/newname = input(usr, "What do you want to rename this to?", "Automatic Rename") as null|text + if(newname) + vv_auto_rename(newname) + +/atom/vv_get_header() + . = ..() + var/refid = REF(src) + . += "[VV_HREF_TARGETREF(refid, VV_HK_AUTO_RENAME, "[src]")]" + . += "
    << [dir2text(dir) || dir] >>" + +/atom/proc/drop_location() + var/atom/L = loc + if(!L) + return null + return L.AllowDrop() ? L : L.drop_location() + +/atom/proc/vv_auto_rename(newname) + name = newname + +/atom/Entered(atom/movable/AM, atom/oldLoc) + SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, AM, oldLoc) + +/atom/Exit(atom/movable/AM, atom/newLoc) + . = ..() + if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, AM, newLoc) & COMPONENT_ATOM_BLOCK_EXIT) + return FALSE + +/atom/Exited(atom/movable/AM, atom/newLoc) + SEND_SIGNAL(src, COMSIG_ATOM_EXITED, AM, newLoc) + +/atom/proc/return_temperature() + return + +// Tool behavior procedure. Redirects to tool-specific procs by default. +// You can override it to catch all tool interactions, for use in complex deconstruction procs. +// Just don't forget to return ..() in the end. +/atom/proc/tool_act(mob/living/user, obj/item/I, tool_type) + switch(tool_type) + if(TOOL_CROWBAR) + return crowbar_act(user, I) + if(TOOL_MULTITOOL) + return multitool_act(user, I) + if(TOOL_SCREWDRIVER) + return screwdriver_act(user, I) + if(TOOL_WRENCH) + return wrench_act(user, I) + if(TOOL_WIRECUTTER) + return wirecutter_act(user, I) + if(TOOL_WELDER) + return welder_act(user, I) + if(TOOL_ANALYZER) + return analyzer_act(user, I) + +// Tool-specific behavior procs. To be overridden in subtypes. +/atom/proc/crowbar_act(mob/living/user, obj/item/I) + return + +/atom/proc/multitool_act(mob/living/user, obj/item/I) + return + +/atom/proc/multitool_check_buffer(user, obj/item/I, silent = FALSE) + if(!istype(I, /obj/item/multitool)) + if(user && !silent) + to_chat(user, "[I] has no data buffer!") + return FALSE + return TRUE + +/atom/proc/screwdriver_act(mob/living/user, obj/item/I) + SEND_SIGNAL(src, COMSIG_ATOM_SCREWDRIVER_ACT, user, I) + +/atom/proc/wrench_act(mob/living/user, obj/item/I) + return + +/atom/proc/wirecutter_act(mob/living/user, obj/item/I) + return + +/atom/proc/welder_act(mob/living/user, obj/item/I) + return + +/atom/proc/analyzer_act(mob/living/user, obj/item/I) + return + +/atom/proc/GenerateTag() + return + +// Generic logging helper +/atom/proc/log_message(message, message_type, color=null, log_globally=TRUE) + if(!log_globally) + return + + var/log_text = "[key_name(src)] [message] [loc_name(src)]" + switch(message_type) + if(LOG_ATTACK) + log_attack(log_text) + if(LOG_SAY) + log_say(log_text) + if(LOG_WHISPER) + log_whisper(log_text) + if(LOG_EMOTE) + log_emote(log_text) + if(LOG_DSAY) + log_dsay(log_text) + if(LOG_PDA) + log_pda(log_text) + if(LOG_CHAT) + log_chat(log_text) + if(LOG_COMMENT) + log_comment(log_text) + if(LOG_TELECOMMS) + log_telecomms(log_text) + if(LOG_OOC) + log_ooc(log_text) + if(LOG_ADMIN) + log_admin(log_text) + if(LOG_ADMIN_PRIVATE) + log_admin_private(log_text) + if(LOG_ASAY) + log_adminsay(log_text) + if(LOG_OWNERSHIP) + log_game(log_text) + if(LOG_GAME) + log_game(log_text) + else + stack_trace("Invalid individual logging type: [message_type]. Defaulting to [LOG_GAME] (LOG_GAME).") + log_game(log_text) + +// Helper for logging chat messages or other logs with arbitrary inputs (e.g. announcements) +/atom/proc/log_talk(message, message_type, tag=null, log_globally=TRUE, forced_by=null) + var/prefix = tag ? "([tag]) " : "" + var/suffix = forced_by ? " FORCED by [forced_by]" : "" + log_message("[prefix]\"[message]\"[suffix]", message_type, log_globally=log_globally) + +// Helper for logging of messages with only one sender and receiver +/proc/log_directed_talk(atom/source, atom/target, message, message_type, tag) + if(!tag) + stack_trace("Unspecified tag for private message") + tag = "UNKNOWN" + + source.log_talk(message, message_type, tag="[tag] to [key_name(target)]") + if(source != target) + target.log_talk(message, message_type, tag="[tag] from [key_name(source)]", log_globally=FALSE) + +/* +Proc for attack log creation, because really why not +1 argument is the actor performing the action +2 argument is the target of the action +3 is a verb describing the action (e.g. punched, throwed, kicked, etc.) +4 is a tool with which the action was made (usually an item) +5 is any additional text, which will be appended to the rest of the log line +*/ + +/proc/log_combat(atom/user, atom/target, what_done, atom/object=null, addition=null) + var/ssource = key_name(user) + var/starget = key_name(target) + + var/mob/living/living_target = target + var/hp = istype(living_target) ? " (NEWHP: [living_target.health]) " : "" + + var/sobject = "" + if(object) + sobject = " with [key_name(object)]" + var/saddition = "" + if(addition) + saddition = " [addition]" + + var/postfix = "[sobject][saddition][hp]" + + var/message = "has [what_done] [starget][postfix]" + user.log_message(message, LOG_ATTACK, color="red") + + if(user != target) + var/reverse_message = "has been [what_done] by [ssource][postfix]" + target.log_message(reverse_message, LOG_ATTACK, color="orange", log_globally=FALSE) + +// Filter stuff +/atom/movable/proc/add_filter(name,priority,list/params) + LAZYINITLIST(filter_data) + var/list/p = params.Copy() + p["priority"] = priority + filter_data[name] = p + update_filters() + +/atom/movable/proc/update_filters() + filters = null + filter_data = sortTim(filter_data, /proc/cmp_filter_data_priority, TRUE) + for(var/f in filter_data) + var/list/data = filter_data[f] + var/list/arguments = data.Copy() + arguments -= "priority" + filters += filter(arglist(arguments)) + +/atom/movable/proc/get_filter(name) + if(filter_data) + if(filter_data[name]) + return filters[filter_data.Find(name)] + +/atom/movable/proc/remove_filter(name) + if(filter_data) + if(filter_data[name]) + filter_data -= name + update_filters() + return TRUE + +/atom/proc/intercept_zImpact(atom/movable/AM, levels = 1) + . |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, AM, levels) + +///Passes Stat Browser Panel clicks to the game and calls client click on an atom +/atom/Topic(href, list/href_list) + . = ..() + if(!usr?.client) + return + var/client/usr_client = usr.client + var/list/paramslist = list() + if(href_list["statpanel_item_shiftclick"]) + paramslist["shift"] = "1" + if(href_list["statpanel_item_ctrlclick"]) + paramslist["ctrl"] = "1" + if(href_list["statpanel_item_altclick"]) + paramslist["alt"] = "1" + if(href_list["statpanel_item_click"]) + // first of all make sure we valid + var/mouseparams = list2params(paramslist) + usr_client.Click(src, loc, null, mouseparams) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 3af57b72..0331e5aa 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -794,9 +794,8 @@ /atom/movable/vv_get_dropdown() . = ..() - . -= "Jump to" - .["Follow"] = "?_src_=holder;[HrefToken()];adminplayerobservefollow=[REF(src)]" - .["Get"] = "?_src_=holder;[HrefToken()];admingetmovable=[REF(src)]" + . += "" + . += "" /atom/movable/proc/ex_check(ex_id) if(!ex_id) diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 282311b5..fdad7fc9 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -1,252 +1,321 @@ - -/obj - var/crit_fail = FALSE - animate_movement = 2 - speech_span = SPAN_ROBOT - var/obj_flags = CAN_BE_HIT - var/set_obj_flags // ONLY FOR MAPPING: Sets flags from a string list, handled in Initialize. Usage: set_obj_flags = "EMAGGED;!CAN_BE_HIT" to set EMAGGED and clear CAN_BE_HIT. - - var/damtype = BRUTE - var/force = 0 - - var/datum/armor/armor - var/obj_integrity //defaults to max_integrity - var/max_integrity = 500 - var/integrity_failure = 0 //0 if we have no special broken behavior - - var/resistance_flags = NONE // INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ON_FIRE | UNACIDABLE | ACID_PROOF - - var/acid_level = 0 //how much acid is on that obj - - var/persistence_replacement //have something WAY too amazing to live to the next round? Set a new path here. Overuse of this var will make me upset. - var/current_skin //the item reskin - var/list/unique_reskin //List of options to reskin. - var/always_reskinnable = FALSE - - // Access levels, used in modules\jobs\access.dm - var/list/req_access - var/req_access_txt = "0" - var/list/req_one_access - var/req_one_access_txt = "0" - - var/renamedByPlayer = FALSE //set when a player uses a pen on a renamable object - - vis_flags = VIS_INHERIT_PLANE //when this be added to vis_contents of something it inherit something.plane, important for visualisation of obj in openspace. - -/obj/vv_edit_var(vname, vval) - switch(vname) - if("anchored") - setAnchored(vval) - return TRUE - if("obj_flags") - if ((obj_flags & DANGEROUS_POSSESSION) && !(vval & DANGEROUS_POSSESSION)) - return FALSE - if("control_object") - var/obj/O = vval - if(istype(O) && (O.obj_flags & DANGEROUS_POSSESSION)) - return FALSE - return ..() - -/obj/Initialize() - . = ..() - if (islist(armor)) - armor = getArmor(arglist(armor)) - else if (!armor) - armor = getArmor() - else if (!istype(armor, /datum/armor)) - stack_trace("Invalid type [armor.type] found in .armor during /obj Initialize()") - - if(obj_integrity == null) - obj_integrity = max_integrity - if (set_obj_flags) - var/flagslist = splittext(set_obj_flags,";") - var/list/string_to_objflag = GLOB.bitfields["obj_flags"] - for (var/flag in flagslist) - if(flag[1] == "!") - flag = copytext(flag, length(flag[1]) + 1) // Get all but the initial ! - obj_flags &= ~string_to_objflag[flag] - else - obj_flags |= string_to_objflag[flag] - if((obj_flags & ON_BLUEPRINTS) && isturf(loc)) - var/turf/T = loc - T.add_blueprints_preround(src) - - -/obj/Destroy(force=FALSE) - if(!ismachinery(src)) - STOP_PROCESSING(SSobj, src) // TODO: Have a processing bitflag to reduce on unnecessary loops through the processing lists - SStgui.close_uis(src) - . = ..() - -/obj/proc/setAnchored(anchorvalue) - SEND_SIGNAL(src, COMSIG_OBJ_SETANCHORED, anchorvalue) - anchored = anchorvalue - -/obj/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, messy_throw = TRUE) - . = ..() - if(obj_flags & FROZEN) - visible_message("[src] shatters into a million pieces!") - qdel(src) - - -/obj/assume_air(datum/gas_mixture/giver) - if(loc) - return loc.assume_air(giver) - else - return null - -/obj/remove_air(amount) - if(loc) - return loc.remove_air(amount) - else - return null - -/obj/return_air() - if(loc) - return loc.return_air() - else - return null - -/obj/proc/handle_internal_lifeform(mob/lifeform_inside_me, breath_request) - //Return: (NONSTANDARD) - // null if object handles breathing logic for lifeform - // datum/air_group to tell lifeform to process using that breath return - //DEFAULT: Take air from turf to give to have mob process - - if(breath_request>0) - var/datum/gas_mixture/environment = return_air() - var/breath_percentage = BREATH_VOLUME / environment.return_volume() - return remove_air(environment.total_moles() * breath_percentage) - else - return null - -/obj/proc/updateUsrDialog() - if((obj_flags & IN_USE) && !(obj_flags & USES_TGUI)) - var/is_in_use = FALSE - var/list/nearby = viewers(1, src) - for(var/mob/M in nearby) - if ((M.client && M.machine == src)) - is_in_use = TRUE - ui_interact(M) - if(isAI(usr) || iscyborg(usr) || IsAdminGhost(usr)) - if (!(usr in nearby)) - if (usr.client && usr.machine==src) // && M.machine == src is omitted because if we triggered this by using the dialog, it doesn't matter if our machine changed in between triggering it and this - the dialog is probably still supposed to refresh. - is_in_use = TRUE - ui_interact(usr) - - // check for TK users - - if(ishuman(usr)) - var/mob/living/carbon/human/H = usr - if(!(usr in nearby)) - if(usr.client && usr.machine==src) - if(H.dna.check_mutation(TK)) - is_in_use = TRUE - ui_interact(usr) - if (is_in_use) - obj_flags |= IN_USE - else - obj_flags &= ~IN_USE - -/obj/proc/updateDialog(update_viewers = TRUE,update_ais = TRUE) - // Check that people are actually using the machine. If not, don't update anymore. - if(obj_flags & IN_USE) - var/is_in_use = FALSE - if(update_viewers) - for(var/mob/M in viewers(1, src)) - if ((M.client && M.machine == src)) - is_in_use = TRUE - src.interact(M) - var/ai_in_use = FALSE - if(update_ais) - ai_in_use = AutoUpdateAI(src) - - if(update_viewers && update_ais) //State change is sure only if we check both - if(!ai_in_use && !is_in_use) - obj_flags &= ~IN_USE - - -/obj/attack_ghost(mob/user) - . = ..() - if(.) - return - ui_interact(user) - -/obj/proc/container_resist(mob/living/user) - return - -/mob/proc/unset_machine() - if(machine) - machine.on_unset_machine(src) - machine = null - -//called when the user unsets the machine. -/atom/movable/proc/on_unset_machine(mob/user) - return - -/mob/proc/set_machine(obj/O) - if(src.machine) - unset_machine() - src.machine = O - if(istype(O)) - O.obj_flags |= IN_USE - -/obj/item/proc/updateSelfDialog() - var/mob/M = src.loc - if(istype(M) && M.client && M.machine == src) - src.attack_self(M) - -/obj/proc/hide(h) - return - -/obj/singularity_pull(S, current_size) - ..() - if(!anchored || current_size >= STAGE_FIVE) - step_towards(src,S) - -/obj/get_dumping_location(datum/component/storage/source,mob/user) - return get_turf(src) - -/obj/proc/CanAStarPass() - . = !density - -/obj/proc/check_uplink_validity() - return 1 - -/obj/proc/intercept_user_move(dir, mob, newLoc, oldLoc) - return - -/obj/vv_get_dropdown() - . = ..() - .["Delete all of type"] = "?_src_=vars;[HrefToken()];delall=[REF(src)]" - .["Osay"] = "?_src_=vars;[HrefToken()];osay[REF(src)]" - .["Modify armor values"] = "?_src_=vars;[HrefToken()];modarmor=[REF(src)]" - -/obj/examine(mob/user) - . = ..() - if(obj_flags & UNIQUE_RENAME) - . += "Use a pen on it to rename it or change its description." - if(unique_reskin && (!current_skin || always_reskinnable)) - . += "Alt-click it to reskin it." - -/obj/AltClick(mob/user) - . = ..() - if(unique_reskin && (!current_skin || always_reskinnable) && user.canUseTopic(src, BE_CLOSE, NO_DEXTERY)) - reskin_obj(user) - return TRUE - -/obj/proc/reskin_obj(mob/M) - if(!LAZYLEN(unique_reskin)) - return - var/dat = "Reskin options for [name]:\n" - for(var/V in unique_reskin) - var/output = icon2html(src, M, unique_reskin[V]) - dat += "[V]: [output]\n" - to_chat(M, dat) - - var/choice = input(M, always_reskinnable ? "Choose the a reskin for [src]" : "Warning, you can only reskin [src] once!","Reskin Object") as null|anything in unique_reskin - if(QDELETED(src) || !choice || (current_skin && !always_reskinnable) || M.incapacitated() || !in_range(M,src) || !unique_reskin[choice] || unique_reskin[choice] == current_skin) - return - current_skin = choice - icon_state = unique_reskin[choice] - to_chat(M, "[src] is now skinned as '[choice]'.") + +/obj + var/crit_fail = FALSE + animate_movement = 2 + speech_span = SPAN_ROBOT + var/obj_flags = CAN_BE_HIT + var/set_obj_flags // ONLY FOR MAPPING: Sets flags from a string list, handled in Initialize. Usage: set_obj_flags = "EMAGGED;!CAN_BE_HIT" to set EMAGGED and clear CAN_BE_HIT. + + var/damtype = BRUTE + var/force = 0 + + var/datum/armor/armor + var/obj_integrity //defaults to max_integrity + var/max_integrity = 500 + var/integrity_failure = 0 //0 if we have no special broken behavior + + var/resistance_flags = NONE // INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ON_FIRE | UNACIDABLE | ACID_PROOF + + var/acid_level = 0 //how much acid is on that obj + + var/persistence_replacement //have something WAY too amazing to live to the next round? Set a new path here. Overuse of this var will make me upset. + var/current_skin //the item reskin + var/list/unique_reskin //List of options to reskin. + var/always_reskinnable = FALSE + + // Access levels, used in modules\jobs\access.dm + var/list/req_access + var/req_access_txt = "0" + var/list/req_one_access + var/req_one_access_txt = "0" + + var/renamedByPlayer = FALSE //set when a player uses a pen on a renamable object + + vis_flags = VIS_INHERIT_PLANE //when this be added to vis_contents of something it inherit something.plane, important for visualisation of obj in openspace. + +/obj/vv_edit_var(vname, vval) + switch(vname) + if("anchored") + setAnchored(vval) + return TRUE + if("obj_flags") + if ((obj_flags & DANGEROUS_POSSESSION) && !(vval & DANGEROUS_POSSESSION)) + return FALSE + if("control_object") + var/obj/O = vval + if(istype(O) && (O.obj_flags & DANGEROUS_POSSESSION)) + return FALSE + return ..() + +/obj/Initialize() + . = ..() + if (islist(armor)) + armor = getArmor(arglist(armor)) + else if (!armor) + armor = getArmor() + else if (!istype(armor, /datum/armor)) + stack_trace("Invalid type [armor.type] found in .armor during /obj Initialize()") + + if(obj_integrity == null) + obj_integrity = max_integrity + if (set_obj_flags) + var/flagslist = splittext(set_obj_flags,";") + var/list/string_to_objflag = GLOB.bitfields["obj_flags"] + for (var/flag in flagslist) + if(flag[1] == "!") + flag = copytext(flag, length(flag[1]) + 1) // Get all but the initial ! + obj_flags &= ~string_to_objflag[flag] + else + obj_flags |= string_to_objflag[flag] + if((obj_flags & ON_BLUEPRINTS) && isturf(loc)) + var/turf/T = loc + T.add_blueprints_preround(src) + + +/obj/Destroy(force=FALSE) + if(!ismachinery(src)) + STOP_PROCESSING(SSobj, src) // TODO: Have a processing bitflag to reduce on unnecessary loops through the processing lists + SStgui.close_uis(src) + . = ..() + +/obj/proc/setAnchored(anchorvalue) + SEND_SIGNAL(src, COMSIG_OBJ_SETANCHORED, anchorvalue) + anchored = anchorvalue + +/obj/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, messy_throw = TRUE) + . = ..() + if(obj_flags & FROZEN) + visible_message("[src] shatters into a million pieces!") + qdel(src) + + +/obj/assume_air(datum/gas_mixture/giver) + if(loc) + return loc.assume_air(giver) + else + return null + +/obj/remove_air(amount) + if(loc) + return loc.remove_air(amount) + else + return null + +/obj/return_air() + if(loc) + return loc.return_air() + else + return null + +/obj/proc/handle_internal_lifeform(mob/lifeform_inside_me, breath_request) + //Return: (NONSTANDARD) + // null if object handles breathing logic for lifeform + // datum/air_group to tell lifeform to process using that breath return + //DEFAULT: Take air from turf to give to have mob process + + if(breath_request>0) + var/datum/gas_mixture/environment = return_air() + var/breath_percentage = BREATH_VOLUME / environment.return_volume() + return remove_air(environment.total_moles() * breath_percentage) + else + return null + +/obj/proc/updateUsrDialog() + if((obj_flags & IN_USE) && !(obj_flags & USES_TGUI)) + var/is_in_use = FALSE + var/list/nearby = viewers(1, src) + for(var/mob/M in nearby) + if ((M.client && M.machine == src)) + is_in_use = TRUE + ui_interact(M) + if(isAI(usr) || iscyborg(usr) || IsAdminGhost(usr)) + if (!(usr in nearby)) + if (usr.client && usr.machine==src) // && M.machine == src is omitted because if we triggered this by using the dialog, it doesn't matter if our machine changed in between triggering it and this - the dialog is probably still supposed to refresh. + is_in_use = TRUE + ui_interact(usr) + + // check for TK users + + if(ishuman(usr)) + var/mob/living/carbon/human/H = usr + if(!(usr in nearby)) + if(usr.client && usr.machine==src) + if(H.dna.check_mutation(TK)) + is_in_use = TRUE + ui_interact(usr) + if (is_in_use) + obj_flags |= IN_USE + else + obj_flags &= ~IN_USE + +/obj/proc/updateDialog(update_viewers = TRUE,update_ais = TRUE) + // Check that people are actually using the machine. If not, don't update anymore. + if(obj_flags & IN_USE) + var/is_in_use = FALSE + if(update_viewers) + for(var/mob/M in viewers(1, src)) + if ((M.client && M.machine == src)) + is_in_use = TRUE + src.interact(M) + var/ai_in_use = FALSE + if(update_ais) + ai_in_use = AutoUpdateAI(src) + + if(update_viewers && update_ais) //State change is sure only if we check both + if(!ai_in_use && !is_in_use) + obj_flags &= ~IN_USE + + +/obj/attack_ghost(mob/user) + . = ..() + if(.) + return + ui_interact(user) + +/obj/proc/container_resist(mob/living/user) + return + +/mob/proc/unset_machine() + if(machine) + machine.on_unset_machine(src) + machine = null + +//called when the user unsets the machine. +/atom/movable/proc/on_unset_machine(mob/user) + return + +/mob/proc/set_machine(obj/O) + if(src.machine) + unset_machine() + src.machine = O + if(istype(O)) + O.obj_flags |= IN_USE + +/obj/item/proc/updateSelfDialog() + var/mob/M = src.loc + if(istype(M) && M.client && M.machine == src) + src.attack_self(M) + +/obj/proc/hide(h) + return + +/obj/singularity_pull(S, current_size) + ..() + if(!anchored || current_size >= STAGE_FIVE) + step_towards(src,S) + +/obj/get_dumping_location(datum/component/storage/source,mob/user) + return get_turf(src) + +/obj/proc/CanAStarPass() + . = !density + +/obj/proc/check_uplink_validity() + return 1 + +/obj/proc/intercept_user_move(dir, mob, newLoc, oldLoc) + return + +/obj/vv_get_dropdown() + . = ..() + VV_DROPDOWN_OPTION("", "---") + VV_DROPDOWN_OPTION(VV_HK_MASS_DEL_TYPE, "Delete all of type") + VV_DROPDOWN_OPTION(VV_HK_OSAY, "Object Say") + VV_DROPDOWN_OPTION(VV_HK_ARMOR_MOD, "Modify armor values") + +/obj/vv_do_topic(list/href_list) + if(!(. = ..())) + return + if(href_list[VV_HK_OSAY]) + if(check_rights(R_FUN, FALSE)) + usr.client.object_say(src) + if(href_list[VV_HK_ARMOR_MOD]) + var/list/pickerlist = list() + var/list/armorlist = armor.getList() + + for (var/i in armorlist) + pickerlist += list(list("value" = armorlist[i], "name" = i)) + + var/list/result = presentpicker(usr, "Modify armor", "Modify armor: [src]", Button1="Save", Button2 = "Cancel", Timeout=FALSE, inputtype = "text", values = pickerlist) + + if (islist(result)) + if (result["button"] != 2) // If the user pressed the cancel button + // text2num conveniently returns a null on invalid values + armor = armor.setRating(melee = text2num(result["values"]["melee"]),\ + bullet = text2num(result["values"]["bullet"]),\ + laser = text2num(result["values"]["laser"]),\ + energy = text2num(result["values"]["energy"]),\ + bomb = text2num(result["values"]["bomb"]),\ + bio = text2num(result["values"]["bio"]),\ + rad = text2num(result["values"]["rad"]),\ + fire = text2num(result["values"]["fire"]),\ + acid = text2num(result["values"]["acid"])) + log_admin("[key_name(usr)] modified the armor on [src] ([type]) to melee: [armor.melee], bullet: [armor.bullet], laser: [armor.laser], energy: [armor.energy], bomb: [armor.bomb], bio: [armor.bio], rad: [armor.rad], fire: [armor.fire], acid: [armor.acid]") + message_admins("[key_name_admin(usr)] modified the armor on [src] ([type]) to melee: [armor.melee], bullet: [armor.bullet], laser: [armor.laser], energy: [armor.energy], bomb: [armor.bomb], bio: [armor.bio], rad: [armor.rad], fire: [armor.fire], acid: [armor.acid]") + if(href_list[VV_HK_MASS_DEL_TYPE]) + if(check_rights(R_DEBUG|R_SERVER)) + var/action_type = alert("Strict type ([type]) or type and all subtypes?",,"Strict type","Type and subtypes","Cancel") + if(action_type == "Cancel" || !action_type) + return + + if(alert("Are you really sure you want to delete all objects of type [type]?",,"Yes","No") != "Yes") + return + + if(alert("Second confirmation required. Delete?",,"Yes","No") != "Yes") + return + + var/O_type = type + switch(action_type) + if("Strict type") + var/i = 0 + for(var/obj/Obj in world) + if(Obj.type == O_type) + i++ + qdel(Obj) + CHECK_TICK + if(!i) + to_chat(usr, "No objects of this type exist") + return + log_admin("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted) ") + message_admins("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted) ") + if("Type and subtypes") + var/i = 0 + for(var/obj/Obj in world) + if(istype(Obj,O_type)) + i++ + qdel(Obj) + CHECK_TICK + if(!i) + to_chat(usr, "No objects of this type exist") + return + log_admin("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) ") + message_admins("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) ") + +/obj/examine(mob/user) + . = ..() + if(obj_flags & UNIQUE_RENAME) + . += "Use a pen on it to rename it or change its description." + if(unique_reskin && (!current_skin || always_reskinnable)) + . += "Alt-click it to reskin it." + +/obj/AltClick(mob/user) + . = ..() + if(unique_reskin && (!current_skin || always_reskinnable) && user.canUseTopic(src, BE_CLOSE, NO_DEXTERY)) + reskin_obj(user) + return TRUE + +/obj/proc/reskin_obj(mob/M) + if(!LAZYLEN(unique_reskin)) + return + var/dat = "Reskin options for [name]:\n" + for(var/V in unique_reskin) + var/output = icon2html(src, M, unique_reskin[V]) + dat += "[V]: [output]\n" + to_chat(M, dat) + + var/choice = input(M, always_reskinnable ? "Choose the a reskin for [src]" : "Warning, you can only reskin [src] once!","Reskin Object") as null|anything in unique_reskin + if(QDELETED(src) || !choice || (current_skin && !always_reskinnable) || M.incapacitated() || !in_range(M,src) || !unique_reskin[choice] || unique_reskin[choice] == current_skin) + return + current_skin = choice + icon_state = unique_reskin[choice] + to_chat(M, "[src] is now skinned as '[choice]'.") diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 37373f49..149e020e 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -76,6 +76,7 @@ GLOBAL_LIST_INIT(admin_verbs_admin, world.AVerbsAdmin()) /client/proc/revokebunkerbypass, /client/proc/stop_sounds, /client/proc/debugstatpanel, + /client/proc/mark_datum_mapview, /client/proc/hide_verbs, /*hides all our adminverbs*/ /client/proc/hide_most_verbs, /*hides all our hideable adminverbs*/ /datum/admins/proc/open_borgopanel diff --git a/code/modules/admin/callproc/callproc.dm b/code/modules/admin/callproc/callproc.dm new file mode 100644 index 00000000..3de5e39f --- /dev/null +++ b/code/modules/admin/callproc/callproc.dm @@ -0,0 +1,208 @@ + +/client/proc/callproc() + set category = "Debug" + set name = "Advanced ProcCall" + set waitfor = FALSE + callproc_blocking() + +/client/proc/callproc_blocking(list/get_retval) + if(!check_rights(R_DEBUG)) + return + + var/datum/target + var/targetselected = FALSE + var/returnval + + switch(alert("Proc owned by something?",,"Yes","No")) + if("Yes") + targetselected = TRUE + var/list/value = vv_get_value(default_class = VV_ATOM_REFERENCE, classes = list(VV_ATOM_REFERENCE, VV_DATUM_REFERENCE, VV_MOB_REFERENCE, VV_CLIENT, VV_MARKED_DATUM, VV_TEXT_LOCATE, VV_PROCCALL_RETVAL)) + if (!value["class"] || !value["value"]) + return + target = value["value"] + if(!istype(target)) + to_chat(usr, "Invalid target.") + return + if("No") + target = null + targetselected = FALSE + + var/procpath = input("Proc path, eg: /proc/fake_blood","Path:", null) as text|null + if(!procpath) + return + + //strip away everything but the proc name + var/list/proclist = splittext(procpath, "/") + if (!length(proclist)) + return + + var/procname = proclist[proclist.len] + var/proctype = ("verb" in proclist) ? "verb" :"proc" + + if(targetselected) + if(!hascall(target, procname)) + to_chat(usr, "Error: callproc(): type [target.type] has no [proctype] named [procpath].") + return + else + procpath = "/[proctype]/[procname]" + if(!text2path(procpath)) + to_chat(usr, "Error: callproc(): [procpath] does not exist.") + return + + var/list/lst = get_callproc_args() + if(!lst) + return + + if(targetselected) + if(!target) + to_chat(usr, "Error: callproc(): owner of proc no longer exists.") + return + var/msg = "[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]." + log_admin(msg) + message_admins(msg) //Proccall announce removed. + admin_ticket_log(target, msg) + returnval = WrapAdminProcCall(target, procname, lst) // Pass the lst as an argument list to the proc + else + //this currently has no hascall protection. wasn't able to get it working. + log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") + message_admins("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") //Proccall announce removed. + returnval = WrapAdminProcCall(GLOBAL_PROC, procname, lst) // Pass the lst as an argument list to the proc + SSblackbox.record_feedback("tally", "admin_verb", 1, "Advanced ProcCall") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + if(get_retval) + get_retval += returnval + . = get_callproc_returnval(returnval, procname) + if(.) + to_chat(usr, .) + +GLOBAL_VAR(AdminProcCaller) +GLOBAL_PROTECT(AdminProcCaller) +GLOBAL_VAR_INIT(AdminProcCallCount, 0) +GLOBAL_PROTECT(AdminProcCallCount) +GLOBAL_VAR(LastAdminCalledTargetRef) +GLOBAL_PROTECT(LastAdminCalledTargetRef) +GLOBAL_VAR(LastAdminCalledTarget) +GLOBAL_PROTECT(LastAdminCalledTarget) +GLOBAL_VAR(LastAdminCalledProc) +GLOBAL_PROTECT(LastAdminCalledProc) +GLOBAL_LIST_EMPTY(AdminProcCallSpamPrevention) +GLOBAL_PROTECT(AdminProcCallSpamPrevention) + +/proc/WrapAdminProcCall(datum/target, procname, list/arguments) + if(target != GLOBAL_PROC && procname == "Del") + to_chat(usr, "Calling Del() is not allowed") + return + + if(target != GLOBAL_PROC && !target.CanProcCall(procname)) + to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!") + return + var/current_caller = GLOB.AdminProcCaller + var/ckey = usr ? usr.client.ckey : GLOB.AdminProcCaller + if(!ckey) + CRASH("WrapAdminProcCall with no ckey: [target] [procname] [english_list(arguments)]") + if(current_caller && current_caller != ckey) + if(!GLOB.AdminProcCallSpamPrevention[ckey]) + to_chat(usr, "Another set of admin called procs are still running, your proc will be run after theirs finish.") + GLOB.AdminProcCallSpamPrevention[ckey] = TRUE + UNTIL(!GLOB.AdminProcCaller) + to_chat(usr, "Running your proc") + GLOB.AdminProcCallSpamPrevention -= ckey + else + UNTIL(!GLOB.AdminProcCaller) + GLOB.LastAdminCalledProc = procname + if(target != GLOBAL_PROC) + GLOB.LastAdminCalledTargetRef = "[REF(target)]" + GLOB.AdminProcCaller = ckey //if this runtimes, too bad for you + ++GLOB.AdminProcCallCount + . = world.WrapAdminProcCall(target, procname, arguments) + if(--GLOB.AdminProcCallCount == 0) + GLOB.AdminProcCaller = null + +//adv proc call this, ya nerds +/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments) + if(target == GLOBAL_PROC) + return call(procname)(arglist(arguments)) + else if(target != world) + return call(target, procname)(arglist(arguments)) + else + log_admin_private("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]") + +/proc/IsAdminAdvancedProcCall() +#ifdef TESTING + return FALSE +#else + return usr && usr.client && GLOB.AdminProcCaller == usr.client.ckey +#endif + +/client/proc/callproc_datum(datum/A as null|area|mob|obj|turf) + set category = "Debug" + set name = "Atom ProcCall" + set waitfor = FALSE + + if(!check_rights(R_DEBUG)) + return + + var/procname = input("Proc name, eg: fake_blood","Proc:", null) as text|null + if(!procname) + return + if(!hascall(A,procname)) + to_chat(usr, "Error: callproc_datum(): type [A.type] has no proc named [procname].") + return + var/list/lst = get_callproc_args() + if(!lst) + return + + if(!A || !IsValidSrc(A)) + to_chat(usr, "Error: callproc_datum(): owner of proc no longer exists.") + return + var/msg = "[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]." + log_admin(msg) + message_admins(msg) + admin_ticket_log(A, msg) + SSblackbox.record_feedback("tally", "admin_verb", 1, "Atom ProcCall") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + var/returnval = WrapAdminProcCall(A, procname, lst) // Pass the lst as an argument list to the proc + . = get_callproc_returnval(returnval,procname) + if(.) + to_chat(usr, .) + +/client/proc/get_callproc_args() + var/argnum = input("Number of arguments","Number:",0) as num|null + if(isnull(argnum)) + return + + . = list() + var/list/named_args = list() + while(argnum--) + var/named_arg = input("Leave blank for positional argument. Positional arguments will be considered as if they were added first.", "Named argument") as text|null + var/value = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) + if (!value["class"]) + return + if(named_arg) + named_args[named_arg] = value["value"] + else + . += value["value"] + if(LAZYLEN(named_args)) + . += named_args + +/client/proc/get_callproc_returnval(returnval,procname) + . = "" + if(islist(returnval)) + var/list/returnedlist = returnval + . = "" + if(returnedlist.len) + var/assoc_check = returnedlist[1] + if(istext(assoc_check) && (returnedlist[assoc_check] != null)) + . += "[procname] returned an associative list:" + for(var/key in returnedlist) + . += "\n[key] = [returnedlist[key]]" + + else + . += "[procname] returned a list:" + for(var/elem in returnedlist) + . += "\n[elem]" + else + . = "[procname] returned an empty list" + . += "" + + else + . = "[procname] returned: [!isnull(returnval) ? returnval : "null"]" diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index ac028d5d..f6df2a5d 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -15,214 +15,6 @@ SSblackbox.record_feedback("tally", "admin_verb", 1, "Toggle Debug Two") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/* 21st Sept 2010 -Updated by Skie -- Still not perfect but better! -Stuff you can't do: -Call proc /mob/proc/Dizzy() for some player -Because if you select a player mob as owner it tries to do the proc for -/mob/living/carbon/human/ instead. And that gives a run-time error. -But you can call procs that are of type /mob/living/carbon/human/proc/ for that player. -*/ - -/client/proc/callproc() - set category = "Debug" - set name = "Advanced ProcCall" - set waitfor = FALSE - - if(!check_rights(R_DEBUG)) - return - - var/datum/target = null - var/targetselected = FALSE - var/returnval = null - - if(alert("Proc owned by something?",,"Yes","No") == "Yes") - targetselected = TRUE - var/list/value = vv_get_value(default_class = VV_ATOM_REFERENCE, classes = list(VV_ATOM_REFERENCE, VV_DATUM_REFERENCE, VV_MOB_REFERENCE, VV_CLIENT)) - if (!value["class"] || !value["value"]) - return - target = value["value"] - - var/procpath = input("Proc path, eg: /proc/fake_blood","Path:", null) as text|null - if(!procpath) - return - - //strip away everything but the proc name - var/list/proclist = splittext(procpath, "/") - if (!length(proclist)) - return - - var/procname = proclist[proclist.len] - var/proctype = ("verb" in proclist) ? "verb" :"proc" - - if(targetselected) - if(!hascall(target, procname)) - to_chat(usr, "Error: callproc(): type [target.type] has no [proctype] named [procpath].") - return - else - procpath = "/[proctype]/[procname]" - if(!text2path(procpath)) - to_chat(usr, "Error: callproc(): [procpath] does not exist.") - return - - var/list/lst = get_callproc_args() - if(!lst) - return - - if(targetselected) - if(!target) - to_chat(usr, "Error: callproc(): owner of proc no longer exists.") - return - var/msg = "[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no argument"]." - log_admin(msg) - message_admins(msg) - admin_ticket_log(target, msg) - returnval = WrapAdminProcCall(target, procname, lst) - else - var/msg = "[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no argument"]." - log_admin(msg) - message_admins(msg) - returnval = WrapAdminProcCall(GLOBAL_PROC, procpath, lst) //calling globals needs full qualified name (e.g /proc/foo) - . = get_callproc_returnval(returnval, procname) - if(.) - to_chat(usr, .) - SSblackbox.record_feedback("tally", "admin_verb", 1, "Advanced ProcCall") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -GLOBAL_VAR(AdminProcCaller) -GLOBAL_PROTECT(AdminProcCaller) -GLOBAL_VAR_INIT(AdminProcCallCount, 0) -GLOBAL_PROTECT(AdminProcCallCount) -GLOBAL_VAR(LastAdminCalledTargetRef) -GLOBAL_PROTECT(LastAdminCalledTargetRef) -GLOBAL_VAR(LastAdminCalledTarget) -GLOBAL_PROTECT(LastAdminCalledTarget) -GLOBAL_VAR(LastAdminCalledProc) -GLOBAL_PROTECT(LastAdminCalledProc) -GLOBAL_LIST_EMPTY(AdminProcCallSpamPrevention) -GLOBAL_PROTECT(AdminProcCallSpamPrevention) - -/proc/WrapAdminProcCall(datum/target, procname, list/arguments) - if(target != GLOBAL_PROC && procname == "Del") - to_chat(usr, "Calling Del() is not allowed") - return - - if(target != GLOBAL_PROC && !target.CanProcCall(procname)) - to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!") - return - var/current_caller = GLOB.AdminProcCaller - var/ckey = usr ? usr.client.ckey : GLOB.AdminProcCaller - if(!ckey) - CRASH("WrapAdminProcCall with no ckey: [target] [procname] [english_list(arguments)]") - if(current_caller && current_caller != ckey) - if(!GLOB.AdminProcCallSpamPrevention[ckey]) - to_chat(usr, "Another set of admin called procs are still running, your proc will be run after theirs finish.") - GLOB.AdminProcCallSpamPrevention[ckey] = TRUE - UNTIL(!GLOB.AdminProcCaller) - to_chat(usr, "Running your proc") - GLOB.AdminProcCallSpamPrevention -= ckey - else - UNTIL(!GLOB.AdminProcCaller) - GLOB.LastAdminCalledProc = procname - if(target != GLOBAL_PROC) - GLOB.LastAdminCalledTargetRef = "[REF(target)]" - GLOB.AdminProcCaller = ckey //if this runtimes, too bad for you - ++GLOB.AdminProcCallCount - . = world.WrapAdminProcCall(target, procname, arguments) - if(--GLOB.AdminProcCallCount == 0) - GLOB.AdminProcCaller = null - -//adv proc call this, ya nerds -/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments) - if(target == GLOBAL_PROC) - return call(procname)(arglist(arguments)) - else if(target != world) - return call(target, procname)(arglist(arguments)) - else - log_admin_private("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]") - -/proc/IsAdminAdvancedProcCall() -#ifdef TESTING - return FALSE -#else - return usr && usr.client && GLOB.AdminProcCaller == usr.client.ckey -#endif - -/client/proc/callproc_datum(datum/A as null|area|mob|obj|turf) - set category = "Debug" - set name = "Atom ProcCall" - set waitfor = FALSE - - if(!check_rights(R_DEBUG)) - return - - var/procname = input("Proc name, eg: fake_blood","Proc:", null) as text|null - if(!procname) - return - if(!hascall(A,procname)) - to_chat(usr, "Error: callproc_datum(): type [A.type] has no proc named [procname].") - return - var/list/lst = get_callproc_args() - if(!lst) - return - - if(!A || !IsValidSrc(A)) - to_chat(usr, "Error: callproc_datum(): owner of proc no longer exists.") - return - var/msg = "[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]." - log_admin(msg) - message_admins(msg) - admin_ticket_log(A, msg) - SSblackbox.record_feedback("tally", "admin_verb", 1, "Atom ProcCall") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - var/returnval = WrapAdminProcCall(A, procname, lst) // Pass the lst as an argument list to the proc - . = get_callproc_returnval(returnval,procname) - if(.) - to_chat(usr, .) - -/client/proc/get_callproc_args() - var/argnum = input("Number of arguments","Number:",0) as num|null - if(isnull(argnum)) - return - - . = list() - var/list/named_args = list() - while(argnum--) - var/named_arg = input("Leave blank for positional argument. Positional arguments will be considered as if they were added first.", "Named argument") as text|null - var/value = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) - if (!value["class"]) - return - if(named_arg) - named_args[named_arg] = value["value"] - else - . += value["value"] - if(LAZYLEN(named_args)) - . += named_args - -/client/proc/get_callproc_returnval(returnval,procname) - . = "" - if(islist(returnval)) - var/list/returnedlist = returnval - . = "" - if(returnedlist.len) - var/assoc_check = returnedlist[1] - if(istext(assoc_check) && (returnedlist[assoc_check] != null)) - . += "[procname] returned an associative list:" - for(var/key in returnedlist) - . += "\n[key] = [returnedlist[key]]" - - else - . += "[procname] returned a list:" - for(var/elem in returnedlist) - . += "\n[elem]" - else - . = "[procname] returned an empty list" - . += "" - - else - . = "[procname] returned: [!isnull(returnval) ? returnval : "null"]" - /client/proc/Cell() set category = "Debug" set name = "Air Status in Location" @@ -343,66 +135,6 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention) else alert("Invalid mob") -/proc/make_types_fancy(var/list/types) - if (ispath(types)) - types = list(types) - . = list() - for(var/type in types) - var/typename = "[type]" - var/static/list/TYPES_SHORTCUTS = list( - /obj/effect/decal/cleanable = "CLEANABLE", - /obj/item/radio/headset = "HEADSET", - /obj/item/clothing/head/helmet/space = "SPESSHELMET", - /obj/item/book/manual = "MANUAL", - /obj/item/reagent_containers/food/drinks = "DRINK", //longest paths comes first - /obj/item/reagent_containers/food = "FOOD", - /obj/item/reagent_containers = "REAGENT_CONTAINERS", - /obj/machinery/atmospherics = "ATMOS_MECH", - /obj/machinery/portable_atmospherics = "PORT_ATMOS", - /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack = "MECHA_MISSILE_RACK", - /obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP", - /obj/item/organ = "ORGAN", - /obj/item = "ITEM", - /obj/machinery = "MACHINERY", - /obj/effect = "EFFECT", - /obj = "O", - /datum = "D", - /turf/open = "OPEN", - /turf/closed = "CLOSED", - /turf = "T", - /mob/living/carbon = "CARBON", - /mob/living/simple_animal = "SIMPLE", - /mob/living = "LIVING", - /mob = "M" - ) - for (var/tn in TYPES_SHORTCUTS) - if (copytext(typename,1, length("[tn]/")+1)=="[tn]/" /*findtextEx(typename,"[tn]/",1,2)*/ ) - typename = TYPES_SHORTCUTS[tn]+copytext(typename,length("[tn]/")) - break - .[typename] = type - -/proc/get_fancy_list_of_atom_types() - var/static/list/pre_generated_list - if (!pre_generated_list) //init - pre_generated_list = make_types_fancy(typesof(/atom)) - return pre_generated_list - - -/proc/get_fancy_list_of_datum_types() - var/static/list/pre_generated_list - if (!pre_generated_list) //init - pre_generated_list = make_types_fancy(sortList(typesof(/datum) - typesof(/atom))) - return pre_generated_list - - -/proc/filter_fancy_list(list/L, filter as text) - var/list/matches = new - for(var/key in L) - var/value = L[key] - if(findtext("[key]", filter) || findtext("[value]", filter)) - matches[key] = value - return matches - //TODO: merge the vievars version into this or something maybe mayhaps /client/proc/cmd_debug_del_all(object as text) set category = "Debug" diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 01d1902f..e426765f 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -594,31 +594,6 @@ Traitors and the like can also be revived with the previous role mostly intact. admin_delete(A) -/client/proc/admin_delete(datum/D) - var/atom/A = D - var/coords = "" - var/jmp_coords = "" - if(istype(A)) - var/turf/T = get_turf(A) - if(T) - coords = "at [COORD(T)]" - jmp_coords = "at [ADMIN_COORDJMP(T)]" - else - jmp_coords = coords = "in nullspace" - - if (alert(src, "Are you sure you want to delete:\n[D]\n[coords]?", "Confirmation", "Yes", "No") == "Yes") - log_admin("[key_name(usr)] deleted [D] [coords]") - message_admins("[key_name_admin(usr)] deleted [D] [jmp_coords]") - SSblackbox.record_feedback("tally", "admin_verb", 1, "Delete") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - if(isturf(D)) - var/turf/T = D - T.ScrapeAway() - else - vv_update_display(D, "deleted", VV_MSG_DELETED) - qdel(D) - if(!QDELETED(D)) - vv_update_display(D, "deleted", "") - /client/proc/cmd_admin_list_open_jobs() set category = "Admin" set name = "Manage Job Slots" diff --git a/code/modules/admin/view_variables/admin_delete.dm b/code/modules/admin/view_variables/admin_delete.dm new file mode 100644 index 00000000..947ad5db --- /dev/null +++ b/code/modules/admin/view_variables/admin_delete.dm @@ -0,0 +1,24 @@ +/client/proc/admin_delete(datum/D) + var/atom/A = D + var/coords = "" + var/jmp_coords = "" + if(istype(A)) + var/turf/T = get_turf(A) + if(T) + coords = "at [COORD(T)]" + jmp_coords = "at [ADMIN_COORDJMP(T)]" + else + jmp_coords = coords = "in nullspace" + + if (alert(src, "Are you sure you want to delete:\n[D]\n[coords]?", "Confirmation", "Yes", "No") == "Yes") + log_admin("[key_name(usr)] deleted [D] [coords]") + message_admins("[key_name_admin(usr)] deleted [D] [jmp_coords]") + SSblackbox.record_feedback("tally", "admin_verb", 1, "Delete") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + if(isturf(D)) + var/turf/T = D + T.ScrapeAway() + else + vv_update_display(D, "deleted", VV_MSG_DELETED) + qdel(D) + if(!QDELETED(D)) + vv_update_display(D, "deleted", "") diff --git a/code/modules/admin/view_variables/debug_variables.dm b/code/modules/admin/view_variables/debug_variables.dm index e69de29b..23f85cba 100644 --- a/code/modules/admin/view_variables/debug_variables.dm +++ b/code/modules/admin/view_variables/debug_variables.dm @@ -0,0 +1,76 @@ +#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing ) +/proc/debug_variable(name, value, level, datum/D, sanitize = TRUE) //if D is a list, name will be index, and value will be assoc value. + var/header + if(D) + if(islist(D)) + var/index = name + if (value) + name = D[name] //name is really the index until this line + else + value = D[name] + header = "
  • ([VV_HREF_TARGET_1V(D, VV_HK_LIST_EDIT, "E", index)]) ([VV_HREF_TARGET_1V(D, VV_HK_LIST_CHANGE, "C", index)]) ([VV_HREF_TARGET_1V(D, VV_HK_LIST_REMOVE, "-", index)]) " + else + header = "
  • ([VV_HREF_TARGET_1V(D, VV_HK_BASIC_EDIT, "E", name)]) ([VV_HREF_TARGET_1V(D, VV_HK_BASIC_CHANGE, "C", name)]) ([VV_HREF_TARGET_1V(D, VV_HK_BASIC_MASSEDIT, "M", name)]) " + else + header = "
  • " + + var/item + if (isnull(value)) + item = "[VV_HTML_ENCODE(name)] = null" + + else if (istext(value)) + item = "[VV_HTML_ENCODE(name)] = \"[VV_HTML_ENCODE(value)]\"" + + else if (isicon(value)) + #ifdef VARSICON + var/icon/I = icon(value) + var/rnd = rand(1,10000) + var/rname = "tmp[REF(I)][rnd].png" + usr << browse_rsc(I, rname) + item = "[VV_HTML_ENCODE(name)] = ([value]) " + #else + item = "[VV_HTML_ENCODE(name)] = /icon ([value])" + #endif + + else if (isfile(value)) + item = "[VV_HTML_ENCODE(name)] = '[value]'" + + else if (istype(value, /datum)) + var/datum/DV = value + if ("[DV]" != "[DV.type]") //if the thing as a name var, lets use it. + item = "[VV_HTML_ENCODE(name)] [REF(value)] = [DV] [DV.type]" + else + item = "[VV_HTML_ENCODE(name)] [REF(value)] = [DV.type]" + + else if (islist(value)) + var/list/L = value + var/list/items = list() + + if (L.len > 0 && !(name == "underlays" || name == "overlays" || L.len > (IS_NORMAL_LIST(L) ? VV_NORMAL_LIST_NO_EXPAND_THRESHOLD : VV_SPECIAL_LIST_NO_EXPAND_THRESHOLD))) + for (var/i in 1 to L.len) + var/key = L[i] + var/val + if (IS_NORMAL_LIST(L) && !isnum(key)) + val = L[key] + if (isnull(val)) // we still want to display non-null false values, such as 0 or "" + val = key + key = i + + items += debug_variable(key, val, level + 1, sanitize = sanitize) + + item = "[VV_HTML_ENCODE(name)] = /list ([L.len])
      [items.Join()]
    " + else + item = "[VV_HTML_ENCODE(name)] = /list ([L.len])" + + else if (name in GLOB.bitfields) + var/list/flags = list() + for (var/i in GLOB.bitfields[name]) + if (value & GLOB.bitfields[name][i]) + flags += i + item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(jointext(flags, ", "))]" + else + item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(value)]" + + return "[header][item]
  • " + +#undef VV_HTML_ENCODE diff --git a/code/modules/admin/view_variables/get_variables.dm b/code/modules/admin/view_variables/get_variables.dm new file mode 100644 index 00000000..3f90002e --- /dev/null +++ b/code/modules/admin/view_variables/get_variables.dm @@ -0,0 +1,271 @@ +/client/proc/vv_get_class(var_name, var_value) + if(isnull(var_value)) + . = VV_NULL + + else if(isnum(var_value)) + if(var_name in GLOB.bitfields) + . = VV_BITFIELD + else + . = VV_NUM + + else if(istext(var_value)) + if(findtext(var_value, "\n")) + . = VV_MESSAGE + else + . = VV_TEXT + + else if(isicon(var_value)) + . = VV_ICON + + else if(ismob(var_value)) + . = VV_MOB_REFERENCE + + else if(isloc(var_value)) + . = VV_ATOM_REFERENCE + + else if(istype(var_value, /client)) + . = VV_CLIENT + + else if(istype(var_value, /datum)) + . = VV_DATUM_REFERENCE + + else if(ispath(var_value)) + if(ispath(var_value, /atom)) + . = VV_ATOM_TYPE + else if(ispath(var_value, /datum)) + . = VV_DATUM_TYPE + else + . = VV_TYPE + + else if(islist(var_value)) + . = VV_LIST + + else if(isfile(var_value)) + . = VV_FILE + else + . = VV_NULL + +/client/proc/vv_get_value(class, default_class, current_value, list/restricted_classes, list/extra_classes, list/classes, var_name) + . = list("class" = class, "value" = null) + if(!class) + if(!classes) + classes = list ( + VV_NUM, + VV_TEXT, + VV_MESSAGE, + VV_ICON, + VV_ATOM_REFERENCE, + VV_DATUM_REFERENCE, + VV_MOB_REFERENCE, + VV_CLIENT, + VV_ATOM_TYPE, + VV_DATUM_TYPE, + VV_TYPE, + VV_FILE, + VV_NEW_ATOM, + VV_NEW_DATUM, + VV_NEW_TYPE, + VV_NEW_LIST, + VV_NULL, + VV_RESTORE_DEFAULT, + VV_TEXT_LOCATE, + VV_PROCCALL_RETVAL, + ) + + var/markstring + if(!(VV_MARKED_DATUM in restricted_classes)) + markstring = "[VV_MARKED_DATUM] (CURRENT: [(istype(holder) && istype(holder.marked_datum))? holder.marked_datum.type : "NULL"])" + classes += markstring + + if(restricted_classes) + classes -= restricted_classes + + if(extra_classes) + classes += extra_classes + + .["class"] = input(src, "What kind of data?", "Variable Type", default_class) as null|anything in classes + if(holder && holder.marked_datum && .["class"] == markstring) + .["class"] = VV_MARKED_DATUM + + switch(.["class"]) + if(VV_TEXT) + .["value"] = input("Enter new text:", "Text", current_value) as null|text + if(.["value"] == null) + .["class"] = null + return + if(VV_MESSAGE) + .["value"] = input("Enter new text:", "Text", current_value) as null|message + if(.["value"] == null) + .["class"] = null + return + + + if(VV_NUM) + .["value"] = input("Enter new number:", "Num", current_value) as null|num + if(.["value"] == null) + .["class"] = null + return + + if(VV_BITFIELD) + .["value"] = input_bitfield(usr, "Editing bitfield: [var_name]", var_name, current_value) + if(.["value"] == null) + .["class"] = null + return + + if(VV_ATOM_TYPE) + .["value"] = pick_closest_path(FALSE) + if(.["value"] == null) + .["class"] = null + return + + if(VV_DATUM_TYPE) + .["value"] = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) + if(.["value"] == null) + .["class"] = null + return + + if(VV_TYPE) + var/type = current_value + var/error = "" + do + type = input("Enter type:[error]", "Type", type) as null|text + if(!type) + break + type = text2path(type) + error = "\nType not found, Please try again" + while(!type) + if(!type) + .["class"] = null + return + .["value"] = type + + if(VV_ATOM_REFERENCE) + var/type = pick_closest_path(FALSE) + var/subtypes = vv_subtype_prompt(type) + if(subtypes == null) + .["class"] = null + return + var/list/things = vv_reference_list(type, subtypes) + var/value = input("Select reference:", "Reference", current_value) as null|anything in things + if(!value) + .["class"] = null + return + .["value"] = things[value] + + if(VV_DATUM_REFERENCE) + var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) + var/subtypes = vv_subtype_prompt(type) + if(subtypes == null) + .["class"] = null + return + var/list/things = vv_reference_list(type, subtypes) + var/value = input("Select reference:", "Reference", current_value) as null|anything in things + if(!value) + .["class"] = null + return + .["value"] = things[value] + + if(VV_MOB_REFERENCE) + var/type = pick_closest_path(FALSE, make_types_fancy(typesof(/mob))) + var/subtypes = vv_subtype_prompt(type) + if(subtypes == null) + .["class"] = null + return + var/list/things = vv_reference_list(type, subtypes) + var/value = input("Select reference:", "Reference", current_value) as null|anything in things + if(!value) + .["class"] = null + return + .["value"] = things[value] + + if(VV_CLIENT) + .["value"] = input("Select reference:", "Reference", current_value) as null|anything in GLOB.clients + if(.["value"] == null) + .["class"] = null + return + + if(VV_FILE) + .["value"] = input("Pick file:", "File") as null|file + if(.["value"] == null) + .["class"] = null + return + + if(VV_ICON) + .["value"] = input("Pick icon:", "Icon") as null|icon + if(.["value"] == null) + .["class"] = null + return + + if(VV_MARKED_DATUM) + .["value"] = holder.marked_datum + if(.["value"] == null) + .["class"] = null + return + + if(VV_PROCCALL_RETVAL) + var/list/get_retval = list() + callproc_blocking(get_retval) + .["value"] = get_retval[1] //should have been set in proccall! + if(.["value"] == null) + .["class"] = null + return + + if(VV_NEW_ATOM) + var/type = pick_closest_path(FALSE) + if(!type) + .["class"] = null + return + .["type"] = type + var/atom/newguy = new type() + newguy.datum_flags |= DF_VAR_EDITED + .["value"] = newguy + + if(VV_NEW_DATUM) + var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) + if(!type) + .["class"] = null + return + .["type"] = type + var/datum/newguy = new type() + newguy.datum_flags |= DF_VAR_EDITED + .["value"] = newguy + + if(VV_NEW_TYPE) + var/type = current_value + var/error = "" + do + type = input("Enter type:[error]", "Type", type) as null|text + if(!type) + break + type = text2path(type) + error = "\nType not found, Please try again" + while(!type) + if(!type) + .["class"] = null + return + .["type"] = type + var/datum/newguy = new type() + if(istype(newguy)) + newguy.datum_flags |= DF_VAR_EDITED + .["value"] = newguy + + if(VV_NEW_LIST) + .["value"] = list() + .["type"] = /list + + if(VV_TEXT_LOCATE) + var/datum/D + do + var/ref = input("Enter reference:", "Reference") as null|text + if(!ref) + break + D = locate(ref) + if(!D) + alert("Invalid ref!") + continue + if(!D.can_vv_mark()) + alert("Datum can not be marked!") + continue + while(!D) + .["type"] = D.type + .["value"] = D diff --git a/code/modules/admin/view_variables/mark_datum.dm b/code/modules/admin/view_variables/mark_datum.dm new file mode 100644 index 00000000..9f1b3337 --- /dev/null +++ b/code/modules/admin/view_variables/mark_datum.dm @@ -0,0 +1,12 @@ +/client/proc/mark_datum(datum/D) + if(!holder) + return + if(holder.marked_datum) + vv_update_display(holder.marked_datum, "marked", "") + holder.marked_datum = D + vv_update_display(D, "marked", VV_MSG_MARKED) + +/client/proc/mark_datum_mapview(datum/D as mob|obj|turf|area in view(view)) + set category = "Debug" + set name = "Mark Object" + mark_datum(D) diff --git a/code/modules/admin/verbs/massmodvar.dm b/code/modules/admin/view_variables/mass_edit_variables.dm similarity index 94% rename from code/modules/admin/verbs/massmodvar.dm rename to code/modules/admin/view_variables/mass_edit_variables.dm index ba199350..4e78e1ae 100644 --- a/code/modules/admin/verbs/massmodvar.dm +++ b/code/modules/admin/view_variables/mass_edit_variables.dm @@ -197,7 +197,7 @@ log_admin("[key_name(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)") message_admins("[key_name_admin(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)") - +//not using global lists as vv is a debug function and debug functions should rely on as less things as possible. /proc/get_all_of_type(var/T, subtypes = TRUE) var/list/typecache = list() typecache[T] = 1 @@ -205,19 +205,25 @@ typecache = typecacheof(typecache) . = list() if (ispath(T, /mob)) - for(var/mob/thing in GLOB.mob_list) + for(var/mob/thing in world) if (typecache[thing.type]) . += thing CHECK_TICK else if (ispath(T, /obj/machinery/door)) - for(var/obj/machinery/door/thing in GLOB.airlocks) + for(var/obj/machinery/door/thing in world) if (typecache[thing.type]) . += thing CHECK_TICK else if (ispath(T, /obj/machinery)) - for(var/obj/machinery/thing in GLOB.machines) + for(var/obj/machinery/thing in world) + if (typecache[thing.type]) + . += thing + CHECK_TICK + + else if (ispath(T, /obj/item)) + for(var/obj/item/thing in world) if (typecache[thing.type]) . += thing CHECK_TICK @@ -247,7 +253,7 @@ CHECK_TICK else if (ispath(T, /client)) - for(var/client/thing in GLOB.clients) + for(var/client/thing in world) if (typecache[thing.type]) . += thing CHECK_TICK diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/view_variables/modify_variables.dm similarity index 60% rename from code/modules/admin/verbs/modifyvariables.dm rename to code/modules/admin/view_variables/modify_variables.dm index cc7bde68..1bf6b1df 100644 --- a/code/modules/admin/verbs/modifyvariables.dm +++ b/code/modules/admin/view_variables/modify_variables.dm @@ -1,264 +1,12 @@ -GLOBAL_LIST_INIT(VVlocked, list("vars", "datum_flags", "client", "virus", "viruses", "cuffed", "last_eaten", "unlock_content", "force_ending")) +GLOBAL_LIST_INIT(VVlocked, list("vars", "datum_flags", "client", "mob")) //Requires DEBUG GLOBAL_PROTECT(VVlocked) -GLOBAL_LIST_INIT(VVicon_edit_lock, list("icon", "icon_state", "overlays", "underlays", "resize")) +GLOBAL_LIST_INIT(VVicon_edit_lock, list("icon", "icon_state", "overlays", "underlays")) //Requires DEBUG or FUN GLOBAL_PROTECT(VVicon_edit_lock) -GLOBAL_LIST_INIT(VVckey_edit, list("key", "ckey")) +GLOBAL_LIST_INIT(VVckey_edit, list("key", "ckey")) //Requires DEBUG or SPAWN GLOBAL_PROTECT(VVckey_edit) -GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "bound_height", "bound_width", "bound_x", "bound_y")) +GLOBAL_LIST_INIT(VVpixelmovement, list("bound_x", "bound_y", "step_x", "step_y", "step_size", "bound_height", "bound_width", "bounds")) //No editing ever. GLOBAL_PROTECT(VVpixelmovement) - -/client/proc/vv_get_class(var/var_name, var/var_value) - if(isnull(var_value)) - . = VV_NULL - - else if (isnum(var_value)) - if (var_name in GLOB.bitfields) - . = VV_BITFIELD - else - . = VV_NUM - - else if (istext(var_value)) - if (findtext(var_value, "\n")) - . = VV_MESSAGE - else - . = VV_TEXT - - else if (isicon(var_value)) - . = VV_ICON - - else if (ismob(var_value)) - . = VV_MOB_REFERENCE - - else if (isloc(var_value)) - . = VV_ATOM_REFERENCE - - else if (istype(var_value, /client)) - . = VV_CLIENT - - else if (istype(var_value, /datum)) - . = VV_DATUM_REFERENCE - - else if (ispath(var_value)) - if (ispath(var_value, /atom)) - . = VV_ATOM_TYPE - else if (ispath(var_value, /datum)) - . = VV_DATUM_TYPE - else - . = VV_TYPE - - else if (islist(var_value)) - . = VV_LIST - - else if (isfile(var_value)) - . = VV_FILE - else - . = VV_NULL - -/client/proc/vv_get_value(class, default_class, current_value, list/restricted_classes, list/extra_classes, list/classes, var_name) - . = list("class" = class, "value" = null) - if (!class) - if (!classes) - classes = list ( - VV_NUM, - VV_TEXT, - VV_MESSAGE, - VV_ICON, - VV_ATOM_REFERENCE, - VV_DATUM_REFERENCE, - VV_MOB_REFERENCE, - VV_CLIENT, - VV_ATOM_TYPE, - VV_DATUM_TYPE, - VV_TYPE, - VV_FILE, - VV_NEW_ATOM, - VV_NEW_DATUM, - VV_NEW_TYPE, - VV_NEW_LIST, - VV_NULL, - VV_RESTORE_DEFAULT - ) - - if(holder && holder.marked_datum && !(VV_MARKED_DATUM in restricted_classes)) - classes += "[VV_MARKED_DATUM] ([holder.marked_datum.type])" - if (restricted_classes) - classes -= restricted_classes - - if (extra_classes) - classes += extra_classes - - .["class"] = input(src, "What kind of data?", "Variable Type", default_class) as null|anything in classes - if (holder && holder.marked_datum && .["class"] == "[VV_MARKED_DATUM] ([holder.marked_datum.type])") - .["class"] = VV_MARKED_DATUM - - - switch(.["class"]) - if (VV_TEXT) - .["value"] = input("Enter new text:", "Text", current_value) as null|text - if (.["value"] == null) - .["class"] = null - return - if (VV_MESSAGE) - .["value"] = input("Enter new text:", "Text", current_value) as null|message - if (.["value"] == null) - .["class"] = null - return - - - if (VV_NUM) - .["value"] = input("Enter new number:", "Num", current_value) as null|num - if (.["value"] == null) - .["class"] = null - return - - if (VV_BITFIELD) - .["value"] = input_bitfield(usr, "Editing bitfield: [var_name]", var_name, current_value) - if (.["value"] == null) - .["class"] = null - return - - if (VV_ATOM_TYPE) - .["value"] = pick_closest_path(FALSE) - if (.["value"] == null) - .["class"] = null - return - - if (VV_DATUM_TYPE) - .["value"] = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) - if (.["value"] == null) - .["class"] = null - return - - if (VV_TYPE) - var/type = current_value - var/error = "" - do - type = input("Enter type:[error]", "Type", type) as null|text - if (!type) - break - type = text2path(type) - error = "\nType not found, Please try again" - while(!type) - if (!type) - .["class"] = null - return - .["value"] = type - - - if (VV_ATOM_REFERENCE) - var/type = pick_closest_path(FALSE) - var/subtypes = vv_subtype_prompt(type) - if (subtypes == null) - .["class"] = null - return - var/list/things = vv_reference_list(type, subtypes) - var/value = input("Select reference:", "Reference", current_value) as null|anything in things - if (!value) - .["class"] = null - return - .["value"] = things[value] - - if (VV_DATUM_REFERENCE) - var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) - var/subtypes = vv_subtype_prompt(type) - if (subtypes == null) - .["class"] = null - return - var/list/things = vv_reference_list(type, subtypes) - var/value = input("Select reference:", "Reference", current_value) as null|anything in things - if (!value) - .["class"] = null - return - .["value"] = things[value] - - if (VV_MOB_REFERENCE) - var/type = pick_closest_path(FALSE, make_types_fancy(typesof(/mob))) - var/subtypes = vv_subtype_prompt(type) - if (subtypes == null) - .["class"] = null - return - var/list/things = vv_reference_list(type, subtypes) - var/value = input("Select reference:", "Reference", current_value) as null|anything in things - if (!value) - .["class"] = null - return - .["value"] = things[value] - - - - if (VV_CLIENT) - .["value"] = input("Select reference:", "Reference", current_value) as null|anything in GLOB.clients - if (.["value"] == null) - .["class"] = null - return - - - if (VV_FILE) - .["value"] = input("Pick file:", "File") as null|file - if (.["value"] == null) - .["class"] = null - return - - - if (VV_ICON) - .["value"] = input("Pick icon:", "Icon") as null|icon - if (.["value"] == null) - .["class"] = null - return - - - if (VV_MARKED_DATUM) - .["value"] = holder.marked_datum - if (.["value"] == null) - .["class"] = null - return - - - if (VV_NEW_ATOM) - var/type = pick_closest_path(FALSE) - if (!type) - .["class"] = null - return - .["type"] = type - var/atom/newguy = new type() - newguy.datum_flags |= DF_VAR_EDITED - .["value"] = newguy - - if (VV_NEW_DATUM) - var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) - if (!type) - .["class"] = null - return - .["type"] = type - var/datum/newguy = new type() - newguy.datum_flags |= DF_VAR_EDITED - .["value"] = newguy - - if (VV_NEW_TYPE) - var/type = current_value - var/error = "" - do - type = input("Enter type:[error]", "Type", type) as null|text - if (!type) - break - type = text2path(type) - error = "\nType not found, Please try again" - while(!type) - if (!type) - .["class"] = null - return - .["type"] = type - var/datum/newguy = new type() - if(istype(newguy)) - newguy.datum_flags |= DF_VAR_EDITED - .["value"] = newguy - - - if (VV_NEW_LIST) - .["value"] = list() - .["type"] = /list - /client/proc/vv_parse_text(O, new_var) if(O && findtext(new_var,"\[")) var/process_vars = alert(usr,"\[] detected in string, process as variables?","Process Variables?","Yes","No") @@ -372,13 +120,13 @@ GLOBAL_PROTECT(VVpixelmovement) if(confirm != "Continue") return - + var/is_normal_list = IS_NORMAL_LIST(L) var/list/names = list() for (var/i in 1 to L.len) var/key = L[i] var/value - if (IS_NORMAL_LIST(L) && !isnum(key)) + if (is_normal_list && !isnum(key)) value = L[key] if (value == null) value = "null" @@ -439,10 +187,17 @@ GLOBAL_PROTECT(VVpixelmovement) assoc_key = L[index] var/default var/variable - if (assoc) - variable = L[assoc_key] - else - variable = L[index] + var/old_assoc_value //EXPERIMENTAL - Keep old associated value while modifying key, if any + if(is_normal_list) + if (assoc) + variable = L[assoc_key] + else + variable = L[index] + //EXPERIMENTAL - Keep old associated value while modifying key, if any + var/found = L[variable] + if(!isnull(found)) + old_assoc_value = found + // default = vv_get_class(objectvar, variable) @@ -504,11 +259,13 @@ GLOBAL_PROTECT(VVpixelmovement) for(var/V in varsvars) new_var = replacetext(new_var,"\[[V]]","[O.vars[V]]") - - if(assoc) - L[assoc_key] = new_var - else - L[index] = new_var + if(is_normal_list) + if(assoc) + L[assoc_key] = new_var + else + L[index] = new_var + if(!isnull(old_assoc_value) && IS_VALID_ASSOC_KEY(new_var)) + L[new_var] = old_assoc_value if (O) if (O.vv_edit_var(objectvar, L) == FALSE) to_chat(src, "Your edit was rejected by the object.") @@ -527,15 +284,8 @@ GLOBAL_PROTECT(VVpixelmovement) if(param_var_name in GLOB.VVicon_edit_lock) if(!check_rights(R_FUN|R_DEBUG)) return FALSE - if(param_var_name in GLOB.VVpixelmovement) - if(!check_rights(R_DEBUG)) - return FALSE - var/prompt = alert(usr, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", "ABORT ", "Continue", " ABORT") - if (prompt != "Continue") - return FALSE return TRUE - /client/proc/modify_variables(atom/O, param_var_name = null, autodetect_class = 0) if(!check_rights(R_VAREDIT)) return @@ -567,11 +317,6 @@ GLOBAL_PROTECT(VVpixelmovement) var_value = O.vars[variable] if(!vv_varname_lockcheck(variable)) return - if(istype(O, /datum/armor)) - var/prompt = alert(src, "Editing this var changes this value on potentially thousands of items that share the same combination of armor values. If you want to edit the armor of just one item, use the \"Modify armor values\" dropdown item", "DANGER", "ABORT ", "Continue", " ABORT") - if (prompt != "Continue") - return - var/default = vv_get_class(variable, var_value) @@ -635,10 +380,9 @@ GLOBAL_PROTECT(VVpixelmovement) to_chat(src, "Your edit was rejected by the object.") return vv_update_display(O, "varedited", VV_MSG_EDITED) - SEND_GLOBAL_SIGNAL(COMSIG_GLOB_VAR_EDIT, args) log_world("### VarEdit by [key_name(src)]: [O.type] [variable]=[var_value] => [var_new]") log_admin("[key_name(src)] modified [original_name]'s [variable] from [html_encode("[var_value]")] to [html_encode("[var_new]")]") var/msg = "[key_name_admin(src)] modified [original_name]'s [variable] from [var_value] to [var_new]" message_admins(msg) admin_ticket_log(O, msg) - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/admin/view_variables/topic.dm b/code/modules/admin/view_variables/topic.dm new file mode 100644 index 00000000..626e75fa --- /dev/null +++ b/code/modules/admin/view_variables/topic.dm @@ -0,0 +1,128 @@ +//DO NOT ADD MORE TO THIS FILE. +//Use vv_do_topic() for datums! +/client/proc/view_var_Topic(href, href_list, hsrc) + if( (usr.client != src) || !src.holder || !holder.CheckAdminHref(href, href_list)) + return + var/target = GET_VV_TARGET + vv_do_basic(target, href_list, href) + if(istype(target, /datum)) + var/datum/D = target + D.vv_do_topic(href_list) + else if(islist(target)) + vv_do_list(target, href_list) + if(href_list["Vars"]) + debug_variables(locate(href_list["Vars"])) + +//Stuff below aren't in dropdowns/etc. + + if(check_rights(R_VAREDIT)) + + //~CARN: for renaming mobs (updates their name, real_name, mind.name, their ID/PDA and datacore records). + + if(href_list["rename"]) + if(!check_rights(NONE)) + return + + var/mob/M = locate(href_list["rename"]) in GLOB.mob_list + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + var/new_name = stripped_input(usr,"What would you like to name this mob?","Input a name",M.real_name,MAX_NAME_LEN) + if( !new_name || !M ) + return + + message_admins("Admin [key_name_admin(usr)] renamed [key_name_admin(M)] to [new_name].") + M.fully_replace_character_name(M.real_name,new_name) + vv_update_display(M, "name", new_name) + vv_update_display(M, "real_name", M.real_name || "No real name") + + else if(href_list["rotatedatum"]) + if(!check_rights(NONE)) + return + + var/atom/A = locate(href_list["rotatedatum"]) + if(!istype(A)) + to_chat(usr, "This can only be done to instances of type /atom") + return + + switch(href_list["rotatedir"]) + if("right") + A.setDir(turn(A.dir, -45)) + if("left") + A.setDir(turn(A.dir, 45)) + vv_update_display(A, "dir", dir2text(A.dir)) + + + else if(href_list["makehuman"]) + if(!check_rights(R_SPAWN)) + return + + var/mob/living/carbon/monkey/Mo = locate(href_list["makehuman"]) in GLOB.mob_list + if(!istype(Mo)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/monkey") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") + return + if(!Mo) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("humanone"=href_list["makehuman"])) + + else if(href_list["adjustDamage"] && href_list["mobToDamage"]) + if(!check_rights(NONE)) + return + + var/mob/living/L = locate(href_list["mobToDamage"]) in GLOB.mob_list + if(!istype(L)) + return + + var/Text = href_list["adjustDamage"] + + var/amount = input("Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) as num + + if(!L) + to_chat(usr, "Mob doesn't exist anymore") + return + + var/newamt + switch(Text) + if("brute") + L.adjustBruteLoss(amount) + newamt = L.getBruteLoss() + if("fire") + L.adjustFireLoss(amount) + newamt = L.getFireLoss() + if("toxin") + L.adjustToxLoss(amount) + newamt = L.getToxLoss() + if("oxygen") + L.adjustOxyLoss(amount) + newamt = L.getOxyLoss() + if("brain") + L.adjustOrganLoss(ORGAN_SLOT_BRAIN, amount) + newamt = L.getOrganLoss(ORGAN_SLOT_BRAIN) + if("clone") + L.adjustCloneLoss(amount) + newamt = L.getCloneLoss() + if("stamina") + L.adjustStaminaLoss(amount) + newamt = L.getStaminaLoss() + else + to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]") + return + + if(amount != 0) + var/log_msg = "[key_name(usr)] dealt [amount] amount of [Text] damage to [key_name(L)]" + message_admins("[key_name(usr)] dealt [amount] amount of [Text] damage to [ADMIN_LOOKUPFLW(L)]") + log_admin(log_msg) + admin_ticket_log(L, "[log_msg]") + vv_update_display(L, Text, "[newamt]") + + + //Finally, refresh if something modified the list. + if(href_list["datumrefresh"]) + var/datum/DAT = locate(href_list["datumrefresh"]) + if(istype(DAT, /datum) || istype(DAT, /client)) + debug_variables(DAT) diff --git a/code/modules/admin/view_variables/topic_basic.dm b/code/modules/admin/view_variables/topic_basic.dm new file mode 100644 index 00000000..2a0f3d73 --- /dev/null +++ b/code/modules/admin/view_variables/topic_basic.dm @@ -0,0 +1,51 @@ +//Not using datum.vv_do_topic for very basic/low level debug things, incase the datum's vv_do_topic is runtiming/whatnot. +/client/proc/vv_do_basic(datum/target, href_list) + var/target_var = GET_VV_VAR_TARGET + if(check_rights(R_VAREDIT)) + if(target_var) + if(href_list[VV_HK_BASIC_EDIT]) + if(!modify_variables(target, target_var, 1)) + return + switch(target_var) + if("name") + vv_update_display(target, "name", "[target]") + if("dir") + var/atom/A = target + if(istype(A)) + vv_update_display(target, "dir", dir2text(A.dir) || A.dir) + if("ckey") + var/mob/living/L = target + if(istype(L)) + vv_update_display(target, "ckey", L.ckey || "No ckey") + if("real_name") + var/mob/living/L = target + if(istype(L)) + vv_update_display(target, "real_name", L.real_name || "No real name") + if(href_list[VV_HK_BASIC_CHANGE]) + modify_variables(target, target_var, 0) + if(href_list[VV_HK_BASIC_MASSEDIT]) + cmd_mass_modify_object_variables(target, target_var) + if(check_rights(R_ADMIN, FALSE)) + if(href_list[VV_HK_EXPOSE]) + var/value = vv_get_value(VV_CLIENT) + if (value["class"] != VV_CLIENT) + return + var/client/C = value["value"] + if (!C) + return + if(!target) + to_chat(usr, "The object you tried to expose to [C] no longer exists (nulled or hard-deled)") + return + message_admins("[key_name_admin(usr)] Showed [key_name_admin(C)] a VV window") + log_admin("Admin [key_name(usr)] Showed [key_name(C)] a VV window of a [target]") + to_chat(C, "[holder.fakekey ? "an Administrator" : "[usr.client.key]"] has granted you access to view a View Variables window") + C.debug_variables(target) + if(check_rights(R_DEBUG)) + if(href_list[VV_HK_DELETE]) + usr.client.admin_delete(target) + if (isturf(src)) // show the turf that took its place + usr.client.debug_variables(src) + if(href_list[VV_HK_MARK]) + usr.client.mark_datum(target) + if(href_list[VV_HK_CALLPROC]) + usr.client.callproc_datum(target) diff --git a/code/modules/admin/view_variables/topic_list.dm b/code/modules/admin/view_variables/topic_list.dm new file mode 100644 index 00000000..349d9da6 --- /dev/null +++ b/code/modules/admin/view_variables/topic_list.dm @@ -0,0 +1,43 @@ +//LISTS - CAN NOT DO VV_DO_TOPIC BECAUSE LISTS AREN'T DATUMS :( +/client/proc/vv_do_list(list/target, href_list) + var/target_index = text2num(GET_VV_VAR_TARGET) + if(check_rights(R_VAREDIT)) + if(target_index) + if(href_list[VV_HK_LIST_EDIT]) + mod_list(target, null, "list", "contents", target_index, autodetect_class = TRUE) + if(href_list[VV_HK_LIST_CHANGE]) + mod_list(target, null, "list", "contents", target_index, autodetect_class = FALSE) + if(href_list[VV_HK_LIST_REMOVE]) + var/variable = target[target_index] + var/prompt = alert("Do you want to remove item number [target_index] from list?", "Confirm", "Yes", "No") + if (prompt != "Yes") + return + target.Cut(target_index, target_index+1) + log_world("### ListVarEdit by [src]: /list's contents: REMOVED=[html_encode("[variable]")]") + log_admin("[key_name(src)] modified list's contents: REMOVED=[variable]") + message_admins("[key_name_admin(src)] modified list's contents: REMOVED=[variable]") + if(href_list[VV_HK_LIST_ADD]) + mod_list_add(target, null, "list", "contents") + if(href_list[VV_HK_LIST_ERASE_DUPES]) + uniqueList_inplace(target) + log_world("### ListVarEdit by [src]: /list contents: CLEAR DUPES") + log_admin("[key_name(src)] modified list's contents: CLEAR DUPES") + message_admins("[key_name_admin(src)] modified list's contents: CLEAR DUPES") + if(href_list[VV_HK_LIST_ERASE_NULLS]) + listclearnulls(target) + log_world("### ListVarEdit by [src]: /list contents: CLEAR NULLS") + log_admin("[key_name(src)] modified list's contents: CLEAR NULLS") + message_admins("[key_name_admin(src)] modified list's contents: CLEAR NULLS") + if(href_list[VV_HK_LIST_SET_LENGTH]) + var/value = vv_get_value(VV_NUM) + if (value["class"] != VV_NUM || value["value"] > max(50000, target.len)) //safety - would rather someone not put an extra 0 and erase the server's memory lmao. + return + target.len = value["value"] + log_world("### ListVarEdit by [src]: /list len: [target.len]") + log_admin("[key_name(src)] modified list's len: [target.len]") + message_admins("[key_name_admin(src)] modified list's len: [target.len]") + if(href_list[VV_HK_LIST_SHUFFLE]) + shuffle_inplace(target) + log_world("### ListVarEdit by [src]: /list contents: SHUFFLE") + log_admin("[key_name(src)] modified list's contents: SHUFFLE") + message_admins("[key_name_admin(src)] modified list's contents: SHUFFLE") diff --git a/code/modules/admin/view_variables/view_variables.dm b/code/modules/admin/view_variables/view_variables.dm new file mode 100644 index 00000000..a85b4ff1 --- /dev/null +++ b/code/modules/admin/view_variables/view_variables.dm @@ -0,0 +1,269 @@ + +/client/proc/debug_variables(datum/D in world) + set category = "Debug" + set name = "View Variables" + //set src in world + var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round. + + if(!usr.client || !usr.client.holder) //This is usr because admins can call the proc on other clients, even if they're not admins, to show them VVs. + to_chat(usr, "You need to be an administrator to access this.") + return + + if(!D) + return + + var/islist = islist(D) + if(!islist && !istype(D)) + return + + var/title = "" + var/refid = REF(D) + var/icon/sprite + var/hash + + var/type = islist? /list : D.type + var/no_icon = FALSE + + if(istype(D, /atom)) + sprite = getFlatIcon(D) + hash = md5(sprite) + if(sprite) + hash = md5(sprite) + src << browse_rsc(sprite, "vv[hash].png") + else + no_icon = TRUE + + title = "[D] ([REF(D)]) = [type]" + var/formatted_type = replacetext("[type]", "/", "/") + + var/sprite_text + if(sprite) + sprite_text = no_icon? "\[NO ICON\]" : "" + var/list/header = islist(D)? list("/list") : D.vv_get_header() + + var/marked_line + if(holder && holder.marked_datum && holder.marked_datum == D) + marked_line = VV_MSG_MARKED + var/varedited_line + if(!islist && (D.datum_flags & DF_VAR_EDITED)) + varedited_line = VV_MSG_EDITED + var/deleted_line + if(!islist && D.gc_destroyed) + deleted_line = VV_MSG_DELETED + + var/list/dropdownoptions + if (islist) + dropdownoptions = list( + "---", + "Add Item" = VV_HREF_TARGETREF_INTERNAL(refid, VV_HK_LIST_ADD), + "Remove Nulls" = VV_HREF_TARGETREF_INTERNAL(refid, VV_HK_LIST_ERASE_NULLS), + "Remove Dupes" = VV_HREF_TARGETREF_INTERNAL(refid, VV_HK_LIST_ERASE_DUPES), + "Set len" = VV_HREF_TARGETREF_INTERNAL(refid, VV_HK_LIST_SET_LENGTH), + "Shuffle" = VV_HREF_TARGETREF_INTERNAL(refid, VV_HK_LIST_SHUFFLE), + "Show VV To Player" = VV_HREF_TARGETREF_INTERNAL(refid, VV_HK_EXPOSE), + "---" + ) + for(var/i in 1 to length(dropdownoptions)) + var/name = dropdownoptions[i] + var/link = dropdownoptions[name] + dropdownoptions[i] = "" + else + dropdownoptions = D.vv_get_dropdown() + + var/list/names = list() + if(!islist) + for(var/V in D.vars) + names += V + sleep(1) + + var/list/variable_html = list() + if(islist) + var/list/L = D + for(var/i in 1 to L.len) + var/key = L[i] + var/value + if(IS_NORMAL_LIST(L) && IS_VALID_ASSOC_KEY(key)) + value = L[key] + variable_html += debug_variable(i, value, 0, L) + else + names = sortList(names) + for(var/V in names) + if(D.can_vv_get(V)) + variable_html += D.vv_get_var(V) + + var/html = {" + + + + [title] + + + + +
    + + + + + +
    + + + + +
    + [sprite_text] +
    + [header.Join()] +
    +
    +
    + [formatted_type] + [marked_line] + [varedited_line] + [deleted_line] +
    +
    +
    + Refresh +
    + +
    +
    +
    +
    +
    + + E - Edit, tries to determine the variable type by itself.
    + C - Change, asks you for the var type first.
    + M - Mass modify: changes this variable for all objects of this type.
    +
    +
    + + + + + +
    +
    + Search: +
    +
    + +
    +
    +
      + [variable_html.Join()] +
    + + + +"} + src << browse(html, "window=variables[refid];size=475x650") + +/client/proc/vv_update_display(datum/D, span, content) + src << output("[span]:[content]", "variables[REF(D)].browser:replace_span") diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm index 97bfd1d1..d037adcc 100644 --- a/code/modules/events/spacevine.dm +++ b/code/modules/events/spacevine.dm @@ -393,17 +393,13 @@ /datum/spacevine_controller/vv_get_dropdown() . = ..() - . += "---" - .["Delete Vines"] = "?_src_=[REF(src)];[HrefToken()];purge_vines=1" + VV_DROPDOWN_OPTION(VV_HK_SPACEVINE_PURGE, "Delete Vines") -/datum/spacevine_controller/Topic(href, href_list) - if(..() || !check_rights(R_ADMIN, FALSE) || !usr.client.holder.CheckAdminHref(href, href_list)) - return - - if(href_list["purge_vines"]) - if(alert(usr, "Are you sure you want to delete this spacevine cluster?", "Delete Vines", "Yes", "No") != "Yes") - return - DeleteVines() +/datum/spacevine_controller/vv_do_topic(href_list) + . = ..() + if(href_list[VV_HK_SPACEVINE_PURGE]) + if(alert(usr, "Are you sure you want to delete this spacevine cluster?", "Delete Vines", "Yes", "No") == "Yes") + DeleteVines() /datum/spacevine_controller/proc/DeleteVines() //this is kill QDEL_LIST(vines) //this will also qdel us diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 8633ff39..168b3141 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -973,14 +973,121 @@ /mob/living/carbon/vv_get_dropdown() . = ..() - . += "---" - .["Make AI"] = "?_src_=vars;[HrefToken()];makeai=[REF(src)]" - .["Modify bodypart"] = "?_src_=vars;[HrefToken()];editbodypart=[REF(src)]" - .["Modify organs"] = "?_src_=vars;[HrefToken()];editorgans=[REF(src)]" - .["Hallucinate"] = "?_src_=vars;[HrefToken()];hallucinate=[REF(src)]" - .["Give martial arts"] = "?_src_=vars;[HrefToken()];givemartialart=[REF(src)]" - .["Give brain trauma"] = "?_src_=vars;[HrefToken()];givetrauma=[REF(src)]" - .["Cure brain traumas"] = "?_src_=vars;[HrefToken()];curetraumas=[REF(src)]" + VV_DROPDOWN_OPTION("", "---------") + VV_DROPDOWN_OPTION(VV_HK_MAKE_AI, "Make AI") + VV_DROPDOWN_OPTION(VV_HK_MODIFY_BODYPART, "Modify bodypart") + VV_DROPDOWN_OPTION(VV_HK_MODIFY_ORGANS, "Modify organs") + VV_DROPDOWN_OPTION(VV_HK_HALLUCINATION, "Hallucinate") + VV_DROPDOWN_OPTION(VV_HK_MARTIAL_ART, "Give Martial Arts") + VV_DROPDOWN_OPTION(VV_HK_GIVE_TRAUMA, "Give Brain Trauma") + VV_DROPDOWN_OPTION(VV_HK_CURE_TRAUMA, "Cure Brain Traumas") + +/mob/living/carbon/vv_do_topic(list/href_list) + . = ..() + if(href_list[VV_HK_MODIFY_BODYPART]) + if(!check_rights(R_SPAWN)) + return + var/edit_action = input(usr, "What would you like to do?","Modify Body Part") as null|anything in list("add","remove", "augment") + if(!edit_action) + return + var/list/limb_list = list() + if(edit_action == "remove" || edit_action == "augment") + for(var/obj/item/bodypart/B in bodyparts) + limb_list += B.body_zone + if(edit_action == "remove") + limb_list -= BODY_ZONE_CHEST + else + limb_list = list(BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) + for(var/obj/item/bodypart/B in bodyparts) + limb_list -= B.body_zone + var/result = input(usr, "Please choose which body part to [edit_action]","[capitalize(edit_action)] Body Part") as null|anything in limb_list + if(result) + var/obj/item/bodypart/BP = get_bodypart(result) + switch(edit_action) + if("remove") + if(BP) + BP.drop_limb() + else + to_chat(usr, "[src] doesn't have such bodypart.") + if("add") + if(BP) + to_chat(usr, "[src] already has such bodypart.") + else + if(!regenerate_limb(result)) + to_chat(usr, "[src] cannot have such bodypart.") + if("augment") + if(ishuman(src)) + if(BP) + BP.change_bodypart_status(BODYPART_ROBOTIC, TRUE, TRUE) + else + to_chat(usr, "[src] doesn't have such bodypart.") + else + to_chat(usr, "Only humans can be augmented.") + admin_ticket_log("[key_name_admin(usr)] has modified the bodyparts of [src]") + if(href_list[VV_HK_MAKE_AI]) + if(!check_rights(R_SPAWN)) + return + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") + return + usr.client.holder.Topic("vv_override", list("makeai"=href_list[VV_HK_TARGET])) + if(href_list[VV_HK_MODIFY_ORGANS]) + if(!check_rights(NONE)) + return + usr.client.manipulate_organs(src) + if(href_list[VV_HK_MARTIAL_ART]) + if(!check_rights(NONE)) + return + var/list/artpaths = subtypesof(/datum/martial_art) + var/list/artnames = list() + for(var/i in artpaths) + var/datum/martial_art/M = i + artnames[initial(M.name)] = M + var/result = input(usr, "Choose the martial art to teach","JUDO CHOP") as null|anything in artnames + if(!usr) + return + if(QDELETED(src)) + to_chat(usr, "Mob doesn't exist anymore") + return + if(result) + var/chosenart = artnames[result] + var/datum/martial_art/MA = new chosenart + MA.teach(src) + log_admin("[key_name(usr)] has taught [MA] to [key_name(src)].") + message_admins("[key_name_admin(usr)] has taught [MA] to [key_name_admin(src)].") + if(href_list[VV_HK_GIVE_TRAUMA]) + if(!check_rights(NONE)) + return + var/list/traumas = subtypesof(/datum/brain_trauma) + var/result = input(usr, "Choose the brain trauma to apply","Traumatize") as null|anything in traumas + if(!usr) + return + if(QDELETED(src)) + to_chat(usr, "Mob doesn't exist anymore") + return + if(!result) + return + var/datum/brain_trauma/BT = gain_trauma(result) + if(BT) + log_admin("[key_name(usr)] has traumatized [key_name(src)] with [BT.name]") + message_admins("[key_name_admin(usr)] has traumatized [key_name_admin(src)] with [BT.name].") + if(href_list[VV_HK_CURE_TRAUMA]) + if(!check_rights(NONE)) + return + cure_all_traumas(TRAUMA_RESILIENCE_ABSOLUTE) + log_admin("[key_name(usr)] has cured all traumas from [key_name(src)].") + message_admins("[key_name_admin(usr)] has cured all traumas from [key_name_admin(src)].") + if(href_list[VV_HK_HALLUCINATION]) + if(!check_rights(NONE)) + return + var/list/hallucinations = subtypesof(/datum/hallucination) + var/result = input(usr, "Choose the hallucination to apply","Send Hallucination") as null|anything in hallucinations + if(!usr) + return + if(QDELETED(src)) + to_chat(usr, "Mob doesn't exist anymore") + return + if(result) + new result(src, TRUE) /mob/living/carbon/can_resist() return bodyparts.len > 2 && ..() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 798a73f4..89b05771 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1,1141 +1,1221 @@ -/mob/living/carbon/human - name = "Unknown" - real_name = "Unknown" - icon = 'icons/mob/human.dmi' - icon_state = "caucasian_m" - appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE|LONG_GLIDE - -/mob/living/carbon/human/Initialize() - add_verb(src, /mob/living/proc/mob_sleep) - add_verb(src, /mob/living/proc/lay_down) - time_initialized = world.time - - //initialize limbs first - create_bodyparts() - - //initialize dna. for spawned humans; overwritten by other code - create_dna(src) - randomize_human(src) - dna.initialize_dna() - - if(dna.species) - set_species(dna.species.type) - - //initialise organs - create_internal_organs() //most of it is done in set_species now, this is only for parent call - physiology = new() - - AddComponent(/datum/component/personal_crafting) - - . = ..() - - RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, /mob/living/carbon/human/clean_blood) - - -/mob/living/carbon/human/ComponentInitialize() - . = ..() - if(!CONFIG_GET(flag/disable_human_mood)) - AddComponent(/datum/component/mood) - AddElement(/datum/element/mob_holder/micro, "micro") - AddElement(/datum/element/flavor_text/carbon, "", "Flavor Text", "", MAX_FLAVOR_LEN, FALSE, TRUE, "flavor_text") - AddElement(/datum/element/flavor_text, "", "Temporary Flavor Text", "This should be used only for things pertaining to the current round!") - -/mob/living/carbon/human/Destroy() - QDEL_NULL(physiology) - QDEL_NULL_LIST(vore_organs) // CITADEL EDIT belly stuff - return ..() - -/mob/living/carbon/human/prepare_data_huds() - //Update med hud images... - ..() - //...sec hud images... - sec_hud_set_ID() - sec_hud_set_implants() - sec_hud_set_security_status() - //...and display them. - add_to_all_human_data_huds() - - -/mob/living/carbon/human/get_status_tab_items() - . = ..() - . += "Intent: [a_intent]" - . += "Move Mode: [m_intent]" - if (internal) - if (!internal.air_contents) - qdel(internal) - else - . += "" - . += "Internal Atmosphere Info: [internal.name]" - . += "Tank Pressure: [internal.air_contents.return_pressure()]" - . += "Distribution Pressure: [internal.distribute_pressure]" - if(mind) - var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling) - if(changeling) - . += "" - . += "Chemical Storage: [changeling.chem_charges]/[changeling.chem_storage]" - . += "Absorbed DNA: [changeling.absorbedcount]" - - //NINJACODE - if(istype(wear_suit, /obj/item/clothing/suit/space/space_ninja)) //Only display if actually a ninja. - var/obj/item/clothing/suit/space/space_ninja/SN = wear_suit - . += "SpiderOS Status: [SN.s_initialized ? "Initialized" : "Disabled"]" - . += "Current Time: [station_time_timestamp()]" - if(SN.s_initialized) - //Suit gear - . += "Energy Charge: [round(SN.cell.charge/100)]%" - . += "Smoke Bombs: \Roman [SN.s_bombs]" - //Ninja status - . += "Fingerprints: [md5(dna.uni_identity)]" - . += "Unique Identity: [dna.unique_enzymes]" - . += "Overall Status: [stat > 1 ? "dead" : "[health]% healthy"]" - . += "Nutrition Status: [nutrition]" - . += "Oxygen Loss: [getOxyLoss()]" - . += "Toxin Levels: [getToxLoss()]" - . += "Burn Severity: [getFireLoss()]" - . += "Brute Trauma: [getBruteLoss()]" - . += "Radiation Levels: [radiation] rad" - . += "Body Temperature: [bodytemperature-T0C] degrees C ([bodytemperature*1.8-459.67] degrees F)" - - //Diseases - if(length(diseases)) - . += "Viruses:" - for(var/thing in diseases) - var/datum/disease/D = thing - . += "* [D.name], Type: [D.spread_text], Stage: [D.stage]/[D.max_stages], Possible Cure: [D.cure_text]" - - -/mob/living/carbon/human/show_inv(mob/user) - user.set_machine(src) - var/has_breathable_mask = istype(wear_mask, /obj/item/clothing/mask) - var/list/obscured = check_obscured_slots() - var/list/dat = list() - - dat += "" - for(var/i in 1 to held_items.len) - var/obj/item/I = get_item_for_held_index(i) - dat += "" - dat += "" - - dat += "" - - dat += "" - - if(SLOT_WEAR_MASK in obscured) - dat += "" - else - dat += "" - - if(SLOT_NECK in obscured) - dat += "" - else - dat += "" - - if(SLOT_GLASSES in obscured) - dat += "" - else - dat += "" - - if(SLOT_EARS in obscured) - dat += "" - else - dat += "" - - dat += "" - - dat += "" - if(wear_suit) - dat += "" - else - dat += "" - - if(SLOT_SHOES in obscured) - dat += "" - else - dat += "" - - if(SLOT_GLOVES in obscured) - dat += "" - else - dat += "" - - if(SLOT_W_UNIFORM in obscured) - dat += "" - else - dat += "" - - if((w_uniform == null && !(dna && dna.species.nojumpsuit)) || (SLOT_W_UNIFORM in obscured)) - dat += "" - dat += "" - dat += "" - else - dat += "" - dat += "" - dat += "" - - if(handcuffed) - dat += "" - if(legcuffed) - dat += "" - - dat += {"
    [get_held_index_name(i)]:[(I && !(I.item_flags & ABSTRACT)) ? I : "Empty"]
     
    Back:[(back && !(back.item_flags & ABSTRACT)) ? back : "Empty"]" - if(has_breathable_mask && istype(back, /obj/item/tank)) - dat += " [internal ? "Disable Internals" : "Set Internals"]" - - dat += "
     
    Head:[(head && !(head.item_flags & ABSTRACT)) ? head : "Empty"]
    Mask:Obscured
    Mask:[(wear_mask && !(wear_mask.item_flags & ABSTRACT)) ? wear_mask : "Empty"]
    Neck:Obscured
    Neck:[(wear_neck && !(wear_neck.item_flags & ABSTRACT)) ? wear_neck : "Empty"]
    Eyes:Obscured
    Eyes:[(glasses && !(glasses.item_flags & ABSTRACT)) ? glasses : "Empty"]
    Ears:Obscured
    Ears:[(ears && !(ears.item_flags & ABSTRACT)) ? ears : "Empty"]
     
    Exosuit:[(wear_suit && !(wear_suit.item_flags & ABSTRACT)) ? wear_suit : "Empty"]
     ↳Suit Storage:[(s_store && !(s_store.item_flags & ABSTRACT)) ? s_store : "Empty"]" - if(has_breathable_mask && istype(s_store, /obj/item/tank)) - dat += " [internal ? "Disable Internals" : "Set Internals"]" - dat += "
     ↳Suit Storage:
    Shoes:Obscured
    Shoes:[(shoes && !(shoes.item_flags & ABSTRACT)) ? shoes : "Empty"]
    Gloves:Obscured
    Gloves:[(gloves && !(gloves.item_flags & ABSTRACT)) ? gloves : "Empty"]
    Uniform:Obscured
    Uniform:[(w_uniform && !(w_uniform.item_flags & ABSTRACT)) ? w_uniform : "Empty"]
     ↳Pockets:
     ↳ID:
     ↳Belt:
     ↳Belt:[(belt && !(belt.item_flags & ABSTRACT)) ? belt : "Empty"]" - if(has_breathable_mask && istype(belt, /obj/item/tank)) - dat += " [internal ? "Disable Internals" : "Set Internals"]" - dat += "
     ↳Pockets:[(l_store && !(l_store.item_flags & ABSTRACT)) ? "Left (Full)" : "Left (Empty)"]" - dat += " [(r_store && !(r_store.item_flags & ABSTRACT)) ? "Right (Full)" : "Right (Empty)"]
     ↳ID:[(wear_id && !(wear_id.item_flags & ABSTRACT)) ? wear_id : "Empty"]
    Handcuffed: Remove
    Legcuffed
    - Close - "} - - var/datum/browser/popup = new(user, "mob[REF(src)]", "[src]", 440, 510) - popup.set_content(dat.Join()) - popup.open() - -// called when something steps onto a human -// this could be made more general, but for now just handle mulebot -/mob/living/carbon/human/Crossed(atom/movable/AM) - var/mob/living/simple_animal/bot/mulebot/MB = AM - if(istype(MB)) - MB.RunOver(src) - - //Hyper Change - Step on people - var/mob/living/carbon/human/H = AM - if(istype(H) && lying && H.a_intent != INTENT_HELP) - H.handle_micro_bump_other(src) - - spreadFire(AM) - -/mob/living/carbon/human/Topic(href, href_list) - if(usr.canUseTopic(src, BE_CLOSE, NO_DEXTERY)) - if(href_list["embedded_object"]) - var/obj/item/bodypart/L = locate(href_list["embedded_limb"]) in bodyparts - if(!L) - return - var/obj/item/I = locate(href_list["embedded_object"]) in L.embedded_objects - if(!I || I.loc != src) //no item, no limb, or item is not in limb or in the person anymore - return - var/time_taken = I.embedding.embedded_unsafe_removal_time/I.w_class //Citadel Change from * to / - usr.visible_message("[usr] attempts to remove [I] from [usr.p_their()] [L.name].","You attempt to remove [I] from your [L.name]... (It will take [DisplayTimeText(time_taken)].)") - if(do_after(usr, time_taken, needhand = 1, target = src)) - remove_embedded_unsafe(L, I, usr) - /* CITADEL EDIT: remove_embedded_unsafe replaces this code - if(!I || !L || I.loc != src || !(I in L.embedded_objects)) - return - L.embedded_objects -= I - L.receive_damage(I.embedding.embedded_unsafe_removal_pain_multiplier*I.w_class)//It hurts to rip it out, get surgery you dingus. - I.forceMove(get_turf(src)) - usr.put_in_hands(I) - usr.emote("scream") - usr.visible_message("[usr] successfully rips [I] out of [usr.p_their()] [L.name]!","You successfully remove [I] from your [L.name].") - if(!has_embedded_objects()) - clear_alert("embeddedobject") - SEND_SIGNAL(usr, COMSIG_CLEAR_MOOD_EVENT, "embedded") */ - return - - if(href_list["item"]) - var/slot = text2num(href_list["item"]) - if(slot in check_obscured_slots()) - to_chat(usr, "You can't reach that! Something is covering it.") - return - - if(href_list["pockets"]) - var/pocket_side = href_list["pockets"] - var/pocket_id = (pocket_side == "right" ? SLOT_R_STORE : SLOT_L_STORE) - var/obj/item/pocket_item = (pocket_id == SLOT_R_STORE ? r_store : l_store) - var/obj/item/place_item = usr.get_active_held_item() // Item to place in the pocket, if it's empty - - var/delay_denominator = 1 - if(pocket_item && !(pocket_item.item_flags & ABSTRACT)) - if(HAS_TRAIT(pocket_item, TRAIT_NODROP)) - to_chat(usr, "You try to empty [src]'s [pocket_side] pocket, it seems to be stuck!") - to_chat(usr, "You try to empty [src]'s [pocket_side] pocket.") - else if(place_item && place_item.mob_can_equip(src, usr, pocket_id, 1) && !(place_item.item_flags & ABSTRACT)) - to_chat(usr, "You try to place [place_item] into [src]'s [pocket_side] pocket.") - delay_denominator = 4 - else - return - - if(do_mob(usr, src, POCKET_STRIP_DELAY/delay_denominator, ignorehelditem = TRUE)) //placing an item into the pocket is 4 times faster - if(pocket_item) - if(pocket_item == (pocket_id == SLOT_R_STORE ? r_store : l_store)) //item still in the pocket we search - dropItemToGround(pocket_item) - if(!usr.can_hold_items() || !usr.put_in_hands(pocket_item)) - pocket_item.forceMove(drop_location()) - else - if(place_item) - if(place_item.mob_can_equip(src, usr, pocket_id, FALSE, TRUE)) - usr.temporarilyRemoveItemFromInventory(place_item, TRUE) - equip_to_slot(place_item, pocket_id, TRUE) - //do nothing otherwise - - // Update strip window - if(usr.machine == src && in_range(src, usr)) - show_inv(usr) - else - // Display a warning if the user mocks up - to_chat(src, "You feel your [pocket_side] pocket being fumbled with!") - - ..() //CITADEL CHANGE - removes a tab from behind this ..() so that flavortext can actually be examined - - -///////HUDs/////// - if(href_list["hud"]) - if(ishuman(usr)) - var/mob/living/carbon/human/H = usr - var/perpname = get_face_name(get_id_name("")) - if(istype(H.glasses, /obj/item/clothing/glasses/hud) || istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud)) - var/datum/data/record/R = find_record("name", perpname, GLOB.data_core.general) - if(href_list["photo_front"] || href_list["photo_side"]) - if(R) - if(!H.canUseHUD()) - return - else if(!istype(H.glasses, /obj/item/clothing/glasses/hud) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/medical)) - return - var/obj/item/photo/P = null - if(href_list["photo_front"]) - P = R.fields["photo_front"] - else if(href_list["photo_side"]) - P = R.fields["photo_side"] - if(P) - P.show(H) - - if(href_list["hud"] == "m") - if(istype(H.glasses, /obj/item/clothing/glasses/hud/health) || istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/medical)) - if(href_list["p_stat"]) - var/health_status = input(usr, "Specify a new physical status for this person.", "Medical HUD", R.fields["p_stat"]) in list("Active", "Physically Unfit", "*Unconscious*", "*Deceased*", "Cancel") - if(R) - if(!H.canUseHUD()) - return - else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/health) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/medical)) - return - if(health_status && health_status != "Cancel") - R.fields["p_stat"] = health_status - return - if(href_list["m_stat"]) - var/health_status = input(usr, "Specify a new mental status for this person.", "Medical HUD", R.fields["m_stat"]) in list("Stable", "*Watch*", "*Unstable*", "*Insane*", "Cancel") - if(R) - if(!H.canUseHUD()) - return - else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/health) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/medical)) - return - if(health_status && health_status != "Cancel") - R.fields["m_stat"] = health_status - return - if(href_list["evaluation"]) - if(!getBruteLoss() && !getFireLoss() && !getOxyLoss() && getToxLoss() < 20) - to_chat(usr, "No external injuries detected.
    ") - return - var/span = "notice" - var/status = "" - if(getBruteLoss()) - to_chat(usr, "Physical trauma analysis:") - for(var/X in bodyparts) - var/obj/item/bodypart/BP = X - var/brutedamage = BP.brute_dam - if(brutedamage > 0) - status = "received minor physical injuries." - span = "notice" - if(brutedamage > 20) - status = "been seriously damaged." - span = "danger" - if(brutedamage > 40) - status = "sustained major trauma!" - span = "userdanger" - if(brutedamage) - to_chat(usr, "[BP] appears to have [status]") - if(getFireLoss()) - to_chat(usr, "Analysis of skin burns:") - for(var/X in bodyparts) - var/obj/item/bodypart/BP = X - var/burndamage = BP.burn_dam - if(burndamage > 0) - status = "signs of minor burns." - span = "notice" - if(burndamage > 20) - status = "serious burns." - span = "danger" - if(burndamage > 40) - status = "major burns!" - span = "userdanger" - if(burndamage) - to_chat(usr, "[BP] appears to have [status]") - if(getOxyLoss()) - to_chat(usr, "Patient has signs of suffocation, emergency treatment may be required!") - if(getToxLoss() > 20) - to_chat(usr, "Gathered data is inconsistent with the analysis, possible cause: poisoning.") - - if(href_list["hud"] == "s") - if(istype(H.glasses, /obj/item/clothing/glasses/hud/security) || istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security)) - if(usr.stat || usr == src) //|| !usr.canmove || usr.restrained()) Fluff: Sechuds have eye-tracking technology and sets 'arrest' to people that the wearer looks and blinks at. - return //Non-fluff: This allows sec to set people to arrest as they get disarmed or beaten - // Checks the user has security clearence before allowing them to change arrest status via hud, comment out to enable all access - var/allowed_access = null - var/obj/item/clothing/glasses/G = H.glasses - if (!(G.obj_flags |= EMAGGED)) - if(H.wear_id) - var/list/access = H.wear_id.GetAccess() - if(ACCESS_SEC_DOORS in access) - allowed_access = H.get_authentification_name() - else - allowed_access = "@%&ERROR_%$*" - - - if(!allowed_access) - to_chat(H, "ERROR: Invalid Access") - return - - if(perpname) - R = find_record("name", perpname, GLOB.data_core.security) - if(R) - if(href_list["status"]) - var/setcriminal = input(usr, "Specify a new criminal status for this person.", "Security HUD", R.fields["criminal"]) in list("None", "*Arrest*", "Incarcerated", "Paroled", "Discharged", "Cancel") - if(setcriminal != "Cancel") - if(R) - if(H.canUseHUD()) - if(istype(H.glasses, /obj/item/clothing/glasses/hud/security) || istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security)) - investigate_log("[key_name(src)] has been set from [R.fields["criminal"]] to [setcriminal] by [key_name(usr)].", INVESTIGATE_RECORDS) - R.fields["criminal"] = setcriminal - sec_hud_set_security_status() - return - - if(href_list["view"]) - if(R) - if(!H.canUseHUD()) - return - else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security)) - return - to_chat(usr, "Name: [R.fields["name"]] Criminal Status: [R.fields["criminal"]]") - to_chat(usr, "Minor Crimes:") - for(var/datum/data/crime/c in R.fields["mi_crim"]) - to_chat(usr, "Crime: [c.crimeName]") - to_chat(usr, "Details: [c.crimeDetails]") - to_chat(usr, "Added by [c.author] at [c.time]") - to_chat(usr, "----------") - to_chat(usr, "Major Crimes:") - for(var/datum/data/crime/c in R.fields["ma_crim"]) - to_chat(usr, "Crime: [c.crimeName]") - to_chat(usr, "Details: [c.crimeDetails]") - to_chat(usr, "Added by [c.author] at [c.time]") - to_chat(usr, "----------") - to_chat(usr, "Notes: [R.fields["notes"]]") - return - - if(href_list["add_crime"]) - switch(alert("What crime would you like to add?","Security HUD","Minor Crime","Major Crime","Cancel")) - if("Minor Crime") - if(R) - var/t1 = stripped_input("Please input minor crime names:", "Security HUD", "", null) - var/t2 = stripped_multiline_input("Please input minor crime details:", "Security HUD", "", null) - if(R) - if (!t1 || !t2 || !allowed_access) - return - else if(!H.canUseHUD()) - return - else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security)) - return - var/crime = GLOB.data_core.createCrimeEntry(t1, t2, allowed_access, STATION_TIME_TIMESTAMP("hh:mm:ss")) - GLOB.data_core.addMinorCrime(R.fields["id"], crime) - investigate_log("New Minor Crime: [t1]: [t2] | Added to [R.fields["name"]] by [key_name(usr)]", INVESTIGATE_RECORDS) - to_chat(usr, "Successfully added a minor crime.") - return - if("Major Crime") - if(R) - var/t1 = stripped_input("Please input major crime names:", "Security HUD", "", null) - var/t2 = stripped_multiline_input("Please input major crime details:", "Security HUD", "", null) - if(R) - if (!t1 || !t2 || !allowed_access) - return - else if (!H.canUseHUD()) - return - else if (!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security)) - return - var/crime = GLOB.data_core.createCrimeEntry(t1, t2, allowed_access, STATION_TIME_TIMESTAMP("hh:mm:ss")) - GLOB.data_core.addMajorCrime(R.fields["id"], crime) - investigate_log("New Major Crime: [t1]: [t2] | Added to [R.fields["name"]] by [key_name(usr)]", INVESTIGATE_RECORDS) - to_chat(usr, "Successfully added a major crime.") - return - - if(href_list["view_comment"]) - if(R) - if(!H.canUseHUD()) - return - else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security)) - return - to_chat(usr, "Comments/Log:") - var/counter = 1 - while(R.fields[text("com_[]", counter)]) - to_chat(usr, R.fields[text("com_[]", counter)]) - to_chat(usr, "----------") - counter++ - return - - if(href_list["add_comment"]) - if(R) - var/t1 = stripped_multiline_input("Add Comment:", "Secure. records", null, null) - if(R) - if (!t1 || !allowed_access) - return - else if(!H.canUseHUD()) - return - else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security)) - return - var/counter = 1 - while(R.fields[text("com_[]", counter)]) - counter++ - R.fields[text("com_[]", counter)] = text("Made by [] on [] [], []
    []", allowed_access, STATION_TIME_TIMESTAMP("hh:mm:ss"), time2text(world.realtime, "MMM DD"), GLOB.year_integer+540, t1) - to_chat(usr, "Successfully added comment.") - return - to_chat(usr, "Unable to locate a data core entry for this person.") - -/mob/living/carbon/human/proc/canUseHUD() - return !(src.stat || IsKnockdown() || IsStun() || src.restrained()) - -/mob/living/carbon/human/can_inject(mob/user, error_msg, target_zone, penetrate_thick = FALSE, bypass_immunity = FALSE) - . = 1 // Default to returning true. - if(user && !target_zone) - target_zone = user.zone_selected - if(HAS_TRAIT(src, TRAIT_PIERCEIMMUNE) && !bypass_immunity) - . = 0 - // If targeting the head, see if the head item is thin enough. - // If targeting anything else, see if the wear suit is thin enough. - if (!penetrate_thick) - if(above_neck(target_zone)) - if(head && istype(head, /obj/item/clothing)) - var/obj/item/clothing/CH = head - if (CH.clothing_flags & THICKMATERIAL) - . = 0 - else - if(wear_suit && istype(wear_suit, /obj/item/clothing)) - var/obj/item/clothing/CS = wear_suit - if (CS.clothing_flags & THICKMATERIAL) - . = 0 - if(!. && error_msg && user) - // Might need re-wording. - to_chat(user, "There is no exposed flesh or thin material [above_neck(target_zone) ? "on [p_their()] head" : "on [p_their()] body"].") - -/mob/living/carbon/human/proc/check_obscured_slots() - var/list/obscured = list() - - if(wear_suit) - if(wear_suit.flags_inv & HIDEGLOVES) - obscured |= SLOT_GLOVES - if(wear_suit.flags_inv & HIDEJUMPSUIT) - obscured |= SLOT_W_UNIFORM - if(wear_suit.flags_inv & HIDESHOES) - obscured |= SLOT_SHOES - - if(head) - if(head.flags_inv & HIDEMASK) - obscured |= SLOT_WEAR_MASK - if(head.flags_inv & HIDEEYES) - obscured |= SLOT_GLASSES - if(head.flags_inv & HIDEEARS) - obscured |= SLOT_EARS - - if(wear_mask) - if(wear_mask.flags_inv & HIDEEYES) - obscured |= SLOT_GLASSES - - if(obscured.len) - return obscured - else - return null - -/mob/living/carbon/human/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null) - if(judgement_criteria & JUDGE_EMAGGED) - return 10 //Everyone is a criminal! - - var/threatcount = 0 - - //Lasertag bullshit - if(lasercolor) - if(lasercolor == "b")//Lasertag turrets target the opposing team, how great is that? -Sieve - if(istype(wear_suit, /obj/item/clothing/suit/redtag)) - threatcount += 4 - if(is_holding_item_of_type(/obj/item/gun/energy/laser/redtag)) - threatcount += 4 - if(istype(belt, /obj/item/gun/energy/laser/redtag)) - threatcount += 2 - - if(lasercolor == "r") - if(istype(wear_suit, /obj/item/clothing/suit/bluetag)) - threatcount += 4 - if(is_holding_item_of_type(/obj/item/gun/energy/laser/bluetag)) - threatcount += 4 - if(istype(belt, /obj/item/gun/energy/laser/bluetag)) - threatcount += 2 - - return threatcount - - //Check for ID - var/obj/item/card/id/idcard = get_idcard(FALSE) - if( (judgement_criteria & JUDGE_IDCHECK) && !idcard && name=="Unknown") - threatcount += 4 - - //Check for weapons - if( (judgement_criteria & JUDGE_WEAPONCHECK) && weaponcheck) - if(!idcard || !(ACCESS_WEAPONS in idcard.access)) - for(var/obj/item/I in held_items) //if they're holding a gun - if(weaponcheck.Invoke(I)) - threatcount += 4 - if(weaponcheck.Invoke(belt) || weaponcheck.Invoke(back)) //if a weapon is present in the belt or back slot - threatcount += 2 //not enough to trigger look_for_perp() on it's own unless they also have criminal status. - - //Check for arrest warrant - if(judgement_criteria & JUDGE_RECORDCHECK) - var/perpname = get_face_name(get_id_name()) - var/datum/data/record/R = find_record("name", perpname, GLOB.data_core.security) - if(R && R.fields["criminal"]) - switch(R.fields["criminal"]) - if("*Arrest*") - threatcount += 5 - if("Incarcerated") - threatcount += 2 - if("Paroled") - threatcount += 2 - - //Check for dresscode violations - if(istype(head, /obj/item/clothing/head/wizard) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/wizard) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/syndi) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/shielded/syndi)) - threatcount += 4 //fuk u antags <3 //no you - - //mindshield implants imply trustworthyness - if(HAS_TRAIT(src, TRAIT_MINDSHIELD)) - threatcount -= 1 - - //Agent cards lower threatlevel. - if(istype(idcard, /obj/item/card/id/syndicate)) - threatcount -= 2 - - return threatcount - - -//Used for new human mobs created by cloning/goleming/podding -/mob/living/carbon/human/proc/set_cloned_appearance() - if(gender == MALE) - facial_hair_style = "Full Beard" - else - facial_hair_style = "Shaved" - hair_style = pick("Bedhead", "Bedhead 2", "Bedhead 3") - underwear = "Nude" - undershirt = "Nude" - update_body() - update_hair() - update_genitals() - -/mob/living/carbon/human/singularity_pull(S, current_size) - ..() - if(current_size >= STAGE_THREE) - for(var/obj/item/hand in held_items) - if(prob(current_size * 5) && hand.w_class >= ((11-current_size)/2) && dropItemToGround(hand)) - step_towards(hand, src) - to_chat(src, "\The [S] pulls \the [hand] from your grip!") - rad_act(current_size * 3) - if(mob_negates_gravity()) - return - -/mob/living/carbon/human/proc/do_cpr(mob/living/carbon/C) - CHECK_DNA_AND_SPECIES(C) - - if(C.stat == DEAD || (HAS_TRAIT(C, TRAIT_FAKEDEATH))) - to_chat(src, "[C.name] is dead!") - return - if(is_mouth_covered()) - to_chat(src, "Remove your mask first!") - return 0 - if(C.is_mouth_covered()) - to_chat(src, "Remove [p_their()] mask first!") - return 0 - - if(C.cpr_time < world.time + 30) - visible_message("[src] is trying to perform CPR on [C.name]!", \ - "You try to perform CPR on [C.name]... Hold still!") - if(!do_mob(src, C)) - to_chat(src, "You fail to perform CPR on [C]!") - return 0 - - var/they_breathe = !HAS_TRAIT(C, TRAIT_NOBREATH) - var/they_lung = C.getorganslot(ORGAN_SLOT_LUNGS) - - if(C.health > C.crit_threshold) - return - - src.visible_message("[src] performs CPR on [C.name]!", "You perform CPR on [C.name].") - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "perform_cpr", /datum/mood_event/perform_cpr) - C.cpr_time = world.time - log_combat(src, C, "CPRed") - - if(they_breathe && they_lung) - var/suff = min(C.getOxyLoss(), 7) - C.adjustOxyLoss(-suff) - C.updatehealth() - to_chat(C, "You feel a breath of fresh air enter your lungs... It feels good...") - else if(they_breathe && !they_lung) - to_chat(C, "You feel a breath of fresh air... but you don't feel any better...") - else - to_chat(C, "You feel a breath of fresh air... which is a sensation you don't recognise...") - -/mob/living/carbon/human/cuff_resist(obj/item/I) - if(dna && dna.check_mutation(HULK)) - say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ), forced = "hulk") - if(..(I, cuff_break = FAST_CUFFBREAK)) - dropItemToGround(I) - else - if(..()) - dropItemToGround(I) - -/mob/living/carbon/human/clean_blood() - var/mob/living/carbon/human/H = src - if(H.gloves) - if(H.gloves.clean_blood()) - H.update_inv_gloves() - else - ..() // Clear the Blood_DNA list - if(H.bloody_hands) - H.bloody_hands = 0 - H.update_inv_gloves() - update_icons() //apply the now updated overlays to the mob - -/mob/living/carbon/human/wash_cream() - if(creamed) //clean both to prevent a rare bug - cut_overlay(mutable_appearance('icons/effects/creampie.dmi', "creampie_lizard")) - cut_overlay(mutable_appearance('icons/effects/creampie.dmi', "creampie_human")) - cut_overlay(mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi', "cum_normal")) - cut_overlay(mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi', "cum_large")) - creamed = FALSE - - -//Turns a mob black, flashes a skeleton overlay -//Just like a cartoon! -/mob/living/carbon/human/proc/electrocution_animation(anim_duration) - //Handle mutant parts if possible - if(dna && dna.species) - add_atom_colour("#000000", TEMPORARY_COLOUR_PRIORITY) - var/static/mutable_appearance/electrocution_skeleton_anim - if(!electrocution_skeleton_anim) - electrocution_skeleton_anim = mutable_appearance(icon, "electrocuted_base") - electrocution_skeleton_anim.appearance_flags |= RESET_COLOR|KEEP_APART - add_overlay(electrocution_skeleton_anim) - addtimer(CALLBACK(src, .proc/end_electrocution_animation, electrocution_skeleton_anim), anim_duration) - - else //or just do a generic animation - flick_overlay_view(image(icon,src,"electrocuted_generic",ABOVE_MOB_LAYER), src, anim_duration) - -/mob/living/carbon/human/proc/end_electrocution_animation(mutable_appearance/MA) - remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, "#000000") - cut_overlay(MA) - -/mob/living/carbon/human/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE) - if(incapacitated() || lying ) - to_chat(src, "You can't do that right now!") - return FALSE - if(!Adjacent(M) && (M.loc != src)) - if((be_close == 0) || (!no_tk && (dna.check_mutation(TK) && tkMaxRangeCheck(src, M)))) - return TRUE - to_chat(src, "You are too far away!") - return FALSE - return TRUE - -/mob/living/carbon/human/resist_restraints() - if(wear_suit && wear_suit.breakouttime) - changeNext_move(CLICK_CD_BREAKOUT) - last_special = world.time + CLICK_CD_BREAKOUT - cuff_resist(wear_suit) - else - ..() - -/mob/living/carbon/human/replace_records_name(oldname,newname) // Only humans have records right now, move this up if changed. - for(var/list/L in list(GLOB.data_core.general,GLOB.data_core.medical,GLOB.data_core.security,GLOB.data_core.locked)) - var/datum/data/record/R = find_record("name", oldname, L) - if(R) - R.fields["name"] = newname - -/mob/living/carbon/human/get_total_tint() - . = ..() - if(glasses) - . += glasses.tint - -/mob/living/carbon/human/update_health_hud() - if(!client || !hud_used) - return - if(dna.species.update_health_hud()) - return - else - if(hud_used.healths) - var/health_amount = health - CLAMP(getStaminaLoss()-50, 0, 80)//CIT CHANGE - makes staminaloss have less of an impact on the health hud - if(..(health_amount)) //not dead - switch(hal_screwyhud) - if(SCREWYHUD_CRIT) - hud_used.healths.icon_state = "health6" - if(SCREWYHUD_DEAD) - hud_used.healths.icon_state = "health7" - if(SCREWYHUD_HEALTHY) - hud_used.healths.icon_state = "health0" - if(hud_used.healthdoll) - hud_used.healthdoll.cut_overlays() - if(stat != DEAD) - hud_used.healthdoll.icon_state = "healthdoll_OVERLAY" - for(var/X in bodyparts) - var/obj/item/bodypart/BP = X - var/damage = BP.burn_dam + BP.brute_dam - var/comparison = (BP.max_damage/5) - var/icon_num = 0 - if(damage) - icon_num = 1 - if(damage > (comparison)) - icon_num = 2 - if(damage > (comparison*2)) - icon_num = 3 - if(damage > (comparison*3)) - icon_num = 4 - if(damage > (comparison*4)) - icon_num = 5 - if(hal_screwyhud == SCREWYHUD_HEALTHY) - icon_num = 0 - if(icon_num) - hud_used.healthdoll.add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[BP.body_zone][icon_num]")) - for(var/t in get_missing_limbs()) //Missing limbs - hud_used.healthdoll.add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[t]6")) - for(var/t in get_disabled_limbs()) //Disabled limbs - hud_used.healthdoll.add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[t]7")) - else - hud_used.healthdoll.icon_state = "healthdoll_DEAD" - -/mob/living/carbon/human/fully_heal(admin_revive = 0) - if(admin_revive) - regenerate_limbs() - regenerate_organs() - remove_all_embedded_objects() - set_heartattack(FALSE) - drunkenness = 0 - for(var/datum/mutation/human/HM in dna.mutations) - if(HM.quality != POSITIVE) - dna.remove_mutation(HM.name) - if(blood_volume < (BLOOD_VOLUME_NORMAL*blood_ratio)) - blood_volume = (BLOOD_VOLUME_NORMAL*blood_ratio) - ..() - -/mob/living/carbon/human/check_weakness(obj/item/weapon, mob/living/attacker) - . = ..() - if (dna && dna.species) - . += dna.species.check_weakness(weapon, attacker) - -/mob/living/carbon/human/is_literate() - return 1 - -/mob/living/carbon/human/can_hold_items() - return TRUE - -/mob/living/carbon/human/update_gravity(has_gravity,override = 0) - if(dna && dna.species) //prevents a runtime while a human is being monkeyfied - override = dna.species.override_float - ..() - -/mob/living/carbon/human/vomit(lost_nutrition = 10, blood = 0, stun = 1, distance = 0, message = 1, toxic = 0) - if(blood && (NOBLOOD in dna.species.species_traits)) - if(message) - visible_message("[src] dry heaves!", \ - "You try to throw up, but there's nothing in your stomach!") - if(stun) - Knockdown(200) - return 1 - ..() - -/mob/living/carbon/human/vv_get_dropdown() - . = ..() - . += "---" - .["Make monkey"] = "?_src_=vars;[HrefToken()];makemonkey=[REF(src)]" - .["Set Species"] = "?_src_=vars;[HrefToken()];setspecies=[REF(src)]" - .["Make cyborg"] = "?_src_=vars;[HrefToken()];makerobot=[REF(src)]" - .["Make alien"] = "?_src_=vars;[HrefToken()];makealien=[REF(src)]" - .["Make slime"] = "?_src_=vars;[HrefToken()];makeslime=[REF(src)]" - .["Toggle Purrbation"] = "?_src_=vars;[HrefToken()];purrbation=[REF(src)]" - .["Copy outfit"] = "?_src_=vars;[HrefToken()];copyoutfit=[REF(src)]" - .["Add/Remove Quirks"] = "?_src_=vars;[HrefToken()];modquirks=[REF(src)]" - -/mob/living/carbon/human/MouseDrop_T(mob/living/target, mob/living/user) - if(pulling == target && grab_state >= GRAB_AGGRESSIVE && stat == CONSCIOUS) - //If they dragged themselves and we're currently aggressively grabbing them try to piggyback - if(user == target && can_piggyback(target)) - piggyback(target) - return - //If you dragged them to you and you're aggressively grabbing try to fireman carry them - else if(user != target) - fireman_carry(target) - return - . = ..() - -//src is the user that will be carrying, target is the mob to be carried -/mob/living/carbon/human/proc/can_piggyback(mob/living/carbon/target) - return (istype(target) && target.stat == CONSCIOUS) - -/mob/living/carbon/human/proc/can_be_firemanned(mob/living/carbon/target) - return (ishuman(target) && target.lying) - -/mob/living/carbon/human/proc/fireman_carry(mob/living/carbon/target) - if(can_be_firemanned(target)) - visible_message("[src] starts lifting [target] onto their back...", - "You start lifting [target] onto your back...") - if(do_after(src, 30, TRUE, target)) - //Second check to make sure they're still valid to be carried - if(can_be_firemanned(target) && !incapacitated(FALSE, TRUE)) - target.resting = FALSE - buckle_mob(target, TRUE, TRUE, 90, 1, 0) - return - visible_message("[src] fails to fireman carry [target]!") - else - to_chat(src, "You can't fireman carry [target] while they're standing!") - -/mob/living/carbon/human/proc/piggyback(mob/living/carbon/target) - if(can_piggyback(target)) - visible_message("[target] starts to climb onto [src]...") - if(do_after(target, 15, target = src)) - if(can_piggyback(target)) - if(target.incapacitated(FALSE, TRUE) || incapacitated(FALSE, TRUE)) - target.visible_message("[target] can't hang onto [src]!") - return - buckle_mob(target, TRUE, TRUE, FALSE, 0, 2) - else - visible_message("[target] fails to climb onto [src]!") - else - to_chat(target, "You can't piggyback ride [src] right now!") - -/mob/living/carbon/human/buckle_mob(mob/living/target, force = FALSE, check_loc = TRUE, lying_buckle = FALSE, hands_needed = 0, target_hands_needed = 0) - if(!force)//humans are only meant to be ridden through piggybacking and special cases - return - if(!is_type_in_typecache(target, can_ride_typecache)) - target.visible_message("[target] really can't seem to mount [src]...") - return - buckle_lying = lying_buckle - var/datum/component/riding/human/riding_datum = LoadComponent(/datum/component/riding/human) - if(target_hands_needed) - riding_datum.ride_check_rider_restrained = TRUE - if(buckled_mobs && ((target in buckled_mobs) || (buckled_mobs.len >= max_buckled_mobs)) || buckled) - return - var/equipped_hands_self - var/equipped_hands_target - if(hands_needed) - equipped_hands_self = riding_datum.equip_buckle_inhands(src, hands_needed, target) - if(target_hands_needed) - equipped_hands_target = riding_datum.equip_buckle_inhands(target, target_hands_needed) - - if(hands_needed || target_hands_needed) - if(hands_needed && !equipped_hands_self) - src.visible_message("[src] can't get a grip on [target] because their hands are full!", - "You can't get a grip on [target] because your hands are full!") - return - else if(target_hands_needed && !equipped_hands_target) - target.visible_message("[target] can't get a grip on [src] because their hands are full!", - "You can't get a grip on [src] because your hands are full!") - return - - stop_pulling() - riding_datum.handle_vehicle_layer() - . = ..(target, force, check_loc) - -/mob/living/carbon/human/proc/is_shove_knockdown_blocked() //If you want to add more things that block shove knockdown, extend this - for(var/obj/item/clothing/C in get_equipped_items()) //doesn't include pockets - if(C.blocks_shove_knockdown) - return TRUE - return FALSE - -/mob/living/carbon/human/proc/clear_shove_slowdown() - remove_movespeed_modifier(SHOVE_SLOWDOWN_ID) - var/active_item = get_active_held_item() - if(is_type_in_typecache(active_item, GLOB.shove_disarming_types)) - visible_message("[src.name] regains their grip on \the [active_item]!", "You regain your grip on \the [active_item]", null, COMBAT_MESSAGE_RANGE) - -/mob/living/carbon/human/do_after_coefficent() - . = ..() - . *= physiology.do_after_speed - -/mob/living/carbon/human/species - var/race = null - -/mob/living/carbon/human/species/Initialize() - . = ..() - set_species(race) - -/mob/living/carbon/human/species/abductor - race = /datum/species/abductor - -/mob/living/carbon/human/species/android - race = /datum/species/android - -/mob/living/carbon/human/species/angel - race = /datum/species/angel - -/mob/living/carbon/human/species/corporate - race = /datum/species/corporate - -/mob/living/carbon/human/species/dullahan - race = /datum/species/dullahan - -/mob/living/carbon/human/species/felinid - race = /datum/species/human/felinid - -/mob/living/carbon/human/species/fly - race = /datum/species/fly - -/mob/living/carbon/human/species/golem - race = /datum/species/golem - -/mob/living/carbon/human/species/golem/random - race = /datum/species/golem/random - -/mob/living/carbon/human/species/golem/adamantine - race = /datum/species/golem/adamantine - -/mob/living/carbon/human/species/golem/plasma - race = /datum/species/golem/plasma - -/mob/living/carbon/human/species/golem/diamond - race = /datum/species/golem/diamond - -/mob/living/carbon/human/species/golem/gold - race = /datum/species/golem/gold - -/mob/living/carbon/human/species/golem/silver - race = /datum/species/golem/silver - -/mob/living/carbon/human/species/golem/plasteel - race = /datum/species/golem/plasteel - -/mob/living/carbon/human/species/golem/titanium - race = /datum/species/golem/titanium - -/mob/living/carbon/human/species/golem/plastitanium - race = /datum/species/golem/plastitanium - -/mob/living/carbon/human/species/golem/alien_alloy - race = /datum/species/golem/alloy - -/mob/living/carbon/human/species/golem/wood - race = /datum/species/golem/wood - -/mob/living/carbon/human/species/golem/uranium - race = /datum/species/golem/uranium - -/mob/living/carbon/human/species/golem/sand - race = /datum/species/golem/sand - -/mob/living/carbon/human/species/golem/glass - race = /datum/species/golem/glass - -/mob/living/carbon/human/species/golem/bluespace - race = /datum/species/golem/bluespace - -/mob/living/carbon/human/species/golem/bananium - race = /datum/species/golem/bananium - -/mob/living/carbon/human/species/golem/blood_cult - race = /datum/species/golem/runic - -/mob/living/carbon/human/species/golem/cloth - race = /datum/species/golem/cloth - -/mob/living/carbon/human/species/golem/plastic - race = /datum/species/golem/plastic - -/mob/living/carbon/human/species/golem/bronze - race = /datum/species/golem/bronze - -/mob/living/carbon/human/species/golem/cardboard - race = /datum/species/golem/cardboard - -/mob/living/carbon/human/species/golem/leather - race = /datum/species/golem/leather - -/mob/living/carbon/human/species/golem/bone - race = /datum/species/golem/bone - -/mob/living/carbon/human/species/golem/durathread - race = /datum/species/golem/durathread - -/mob/living/carbon/human/species/golem/clockwork - race = /datum/species/golem/clockwork - -/mob/living/carbon/human/species/golem/clockwork/no_scrap - race = /datum/species/golem/clockwork/no_scrap - -/mob/living/carbon/human/species/jelly - race = /datum/species/jelly - -/mob/living/carbon/human/species/jelly/slime - race = /datum/species/jelly/slime - -/mob/living/carbon/human/species/jelly/stargazer - race = /datum/species/jelly/stargazer - -/mob/living/carbon/human/species/jelly/luminescent - race = /datum/species/jelly/luminescent - -/mob/living/carbon/human/species/lizard - race = /datum/species/lizard - -/mob/living/carbon/human/species/lizard/ashwalker - race = /datum/species/lizard/ashwalker - -/mob/living/carbon/human/species/moth - race = /datum/species/moth - -/mob/living/carbon/human/species/mush - race = /datum/species/mush - -/mob/living/carbon/human/species/plasma - race = /datum/species/plasmaman - -/mob/living/carbon/human/species/pod - race = /datum/species/pod - -/mob/living/carbon/human/species/shadow - race = /datum/species/shadow - -/mob/living/carbon/human/species/shadow/nightmare - race = /datum/species/shadow/nightmare - -/mob/living/carbon/human/species/skeleton - race = /datum/species/skeleton - -/mob/living/carbon/human/species/synth - race = /datum/species/synth - -/mob/living/carbon/human/species/synth/military - race = /datum/species/synth/military - -/mob/living/carbon/human/species/vampire - race = /datum/species/vampire - -/mob/living/carbon/human/species/zombie - race = /datum/species/zombie - -/mob/living/carbon/human/species/zombie/infectious - race = /datum/species/zombie/infectious - -/mob/living/carbon/human/species/zombie/krokodil_addict - race = /datum/species/krokodil_addict - -/mob/living/carbon/human/species/mammal - race = /datum/species/mammal - -/mob/living/carbon/human/species/insect - race = /datum/species/insect - -/mob/living/carbon/human/species/xeno - race = /datum/species/xeno - -/mob/living/carbon/human/species/ipc - race = /datum/species/ipc - -/mob/living/carbon/human/species/roundstartslime - race = /datum/species/jelly/roundstartslime -//define holder_type on nerds we wanna commit scoop to -/* /mob/living/carbon/human - var/holder_type = /obj/item/clothing/head/mob_holder/micro - can_be_held = "micro" -*/ - +/mob/living/carbon/human + name = "Unknown" + real_name = "Unknown" + icon = 'icons/mob/human.dmi' + icon_state = "caucasian_m" + appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE|LONG_GLIDE + +/mob/living/carbon/human/Initialize() + add_verb(src, /mob/living/proc/mob_sleep) + add_verb(src, /mob/living/proc/lay_down) + time_initialized = world.time + + //initialize limbs first + create_bodyparts() + + //initialize dna. for spawned humans; overwritten by other code + create_dna(src) + randomize_human(src) + dna.initialize_dna() + + if(dna.species) + set_species(dna.species.type) + + //initialise organs + create_internal_organs() //most of it is done in set_species now, this is only for parent call + physiology = new() + + AddComponent(/datum/component/personal_crafting) + + . = ..() + + RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, /mob/living/carbon/human/clean_blood) + + +/mob/living/carbon/human/ComponentInitialize() + . = ..() + if(!CONFIG_GET(flag/disable_human_mood)) + AddComponent(/datum/component/mood) + AddElement(/datum/element/mob_holder/micro, "micro") + AddElement(/datum/element/flavor_text/carbon, "", "Flavor Text", "", MAX_FLAVOR_LEN, FALSE, TRUE, "flavor_text") + AddElement(/datum/element/flavor_text, "", "Temporary Flavor Text", "This should be used only for things pertaining to the current round!") + +/mob/living/carbon/human/Destroy() + QDEL_NULL(physiology) + QDEL_NULL_LIST(vore_organs) // CITADEL EDIT belly stuff + return ..() + +/mob/living/carbon/human/prepare_data_huds() + //Update med hud images... + ..() + //...sec hud images... + sec_hud_set_ID() + sec_hud_set_implants() + sec_hud_set_security_status() + //...and display them. + add_to_all_human_data_huds() + + +/mob/living/carbon/human/get_status_tab_items() + . = ..() + . += "Intent: [a_intent]" + . += "Move Mode: [m_intent]" + if (internal) + if (!internal.air_contents) + qdel(internal) + else + . += "" + . += "Internal Atmosphere Info: [internal.name]" + . += "Tank Pressure: [internal.air_contents.return_pressure()]" + . += "Distribution Pressure: [internal.distribute_pressure]" + if(mind) + var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling) + if(changeling) + . += "" + . += "Chemical Storage: [changeling.chem_charges]/[changeling.chem_storage]" + . += "Absorbed DNA: [changeling.absorbedcount]" + + //NINJACODE + if(istype(wear_suit, /obj/item/clothing/suit/space/space_ninja)) //Only display if actually a ninja. + var/obj/item/clothing/suit/space/space_ninja/SN = wear_suit + . += "SpiderOS Status: [SN.s_initialized ? "Initialized" : "Disabled"]" + . += "Current Time: [station_time_timestamp()]" + if(SN.s_initialized) + //Suit gear + . += "Energy Charge: [round(SN.cell.charge/100)]%" + . += "Smoke Bombs: \Roman [SN.s_bombs]" + //Ninja status + . += "Fingerprints: [md5(dna.uni_identity)]" + . += "Unique Identity: [dna.unique_enzymes]" + . += "Overall Status: [stat > 1 ? "dead" : "[health]% healthy"]" + . += "Nutrition Status: [nutrition]" + . += "Oxygen Loss: [getOxyLoss()]" + . += "Toxin Levels: [getToxLoss()]" + . += "Burn Severity: [getFireLoss()]" + . += "Brute Trauma: [getBruteLoss()]" + . += "Radiation Levels: [radiation] rad" + . += "Body Temperature: [bodytemperature-T0C] degrees C ([bodytemperature*1.8-459.67] degrees F)" + + //Diseases + if(length(diseases)) + . += "Viruses:" + for(var/thing in diseases) + var/datum/disease/D = thing + . += "* [D.name], Type: [D.spread_text], Stage: [D.stage]/[D.max_stages], Possible Cure: [D.cure_text]" + + +/mob/living/carbon/human/show_inv(mob/user) + user.set_machine(src) + var/has_breathable_mask = istype(wear_mask, /obj/item/clothing/mask) + var/list/obscured = check_obscured_slots() + var/list/dat = list() + + dat += "" + for(var/i in 1 to held_items.len) + var/obj/item/I = get_item_for_held_index(i) + dat += "" + dat += "" + + dat += "" + + dat += "" + + if(SLOT_WEAR_MASK in obscured) + dat += "" + else + dat += "" + + if(SLOT_NECK in obscured) + dat += "" + else + dat += "" + + if(SLOT_GLASSES in obscured) + dat += "" + else + dat += "" + + if(SLOT_EARS in obscured) + dat += "" + else + dat += "" + + dat += "" + + dat += "" + if(wear_suit) + dat += "" + else + dat += "" + + if(SLOT_SHOES in obscured) + dat += "" + else + dat += "" + + if(SLOT_GLOVES in obscured) + dat += "" + else + dat += "" + + if(SLOT_W_UNIFORM in obscured) + dat += "" + else + dat += "" + + if((w_uniform == null && !(dna && dna.species.nojumpsuit)) || (SLOT_W_UNIFORM in obscured)) + dat += "" + dat += "" + dat += "" + else + dat += "" + dat += "" + dat += "" + + if(handcuffed) + dat += "" + if(legcuffed) + dat += "" + + dat += {"
    [get_held_index_name(i)]:[(I && !(I.item_flags & ABSTRACT)) ? I : "Empty"]
     
    Back:[(back && !(back.item_flags & ABSTRACT)) ? back : "Empty"]" + if(has_breathable_mask && istype(back, /obj/item/tank)) + dat += " [internal ? "Disable Internals" : "Set Internals"]" + + dat += "
     
    Head:[(head && !(head.item_flags & ABSTRACT)) ? head : "Empty"]
    Mask:Obscured
    Mask:[(wear_mask && !(wear_mask.item_flags & ABSTRACT)) ? wear_mask : "Empty"]
    Neck:Obscured
    Neck:[(wear_neck && !(wear_neck.item_flags & ABSTRACT)) ? wear_neck : "Empty"]
    Eyes:Obscured
    Eyes:[(glasses && !(glasses.item_flags & ABSTRACT)) ? glasses : "Empty"]
    Ears:Obscured
    Ears:[(ears && !(ears.item_flags & ABSTRACT)) ? ears : "Empty"]
     
    Exosuit:[(wear_suit && !(wear_suit.item_flags & ABSTRACT)) ? wear_suit : "Empty"]
     ↳Suit Storage:[(s_store && !(s_store.item_flags & ABSTRACT)) ? s_store : "Empty"]" + if(has_breathable_mask && istype(s_store, /obj/item/tank)) + dat += " [internal ? "Disable Internals" : "Set Internals"]" + dat += "
     ↳Suit Storage:
    Shoes:Obscured
    Shoes:[(shoes && !(shoes.item_flags & ABSTRACT)) ? shoes : "Empty"]
    Gloves:Obscured
    Gloves:[(gloves && !(gloves.item_flags & ABSTRACT)) ? gloves : "Empty"]
    Uniform:Obscured
    Uniform:[(w_uniform && !(w_uniform.item_flags & ABSTRACT)) ? w_uniform : "Empty"]
     ↳Pockets:
     ↳ID:
     ↳Belt:
     ↳Belt:[(belt && !(belt.item_flags & ABSTRACT)) ? belt : "Empty"]" + if(has_breathable_mask && istype(belt, /obj/item/tank)) + dat += " [internal ? "Disable Internals" : "Set Internals"]" + dat += "
     ↳Pockets:[(l_store && !(l_store.item_flags & ABSTRACT)) ? "Left (Full)" : "Left (Empty)"]" + dat += " [(r_store && !(r_store.item_flags & ABSTRACT)) ? "Right (Full)" : "Right (Empty)"]
     ↳ID:[(wear_id && !(wear_id.item_flags & ABSTRACT)) ? wear_id : "Empty"]
    Handcuffed: Remove
    Legcuffed
    + Close + "} + + var/datum/browser/popup = new(user, "mob[REF(src)]", "[src]", 440, 510) + popup.set_content(dat.Join()) + popup.open() + +// called when something steps onto a human +// this could be made more general, but for now just handle mulebot +/mob/living/carbon/human/Crossed(atom/movable/AM) + var/mob/living/simple_animal/bot/mulebot/MB = AM + if(istype(MB)) + MB.RunOver(src) + + //Hyper Change - Step on people + var/mob/living/carbon/human/H = AM + if(istype(H) && lying && H.a_intent != INTENT_HELP) + H.handle_micro_bump_other(src) + + spreadFire(AM) + +/mob/living/carbon/human/Topic(href, href_list) + if(usr.canUseTopic(src, BE_CLOSE, NO_DEXTERY)) + if(href_list["embedded_object"]) + var/obj/item/bodypart/L = locate(href_list["embedded_limb"]) in bodyparts + if(!L) + return + var/obj/item/I = locate(href_list["embedded_object"]) in L.embedded_objects + if(!I || I.loc != src) //no item, no limb, or item is not in limb or in the person anymore + return + var/time_taken = I.embedding.embedded_unsafe_removal_time/I.w_class //Citadel Change from * to / + usr.visible_message("[usr] attempts to remove [I] from [usr.p_their()] [L.name].","You attempt to remove [I] from your [L.name]... (It will take [DisplayTimeText(time_taken)].)") + if(do_after(usr, time_taken, needhand = 1, target = src)) + remove_embedded_unsafe(L, I, usr) + /* CITADEL EDIT: remove_embedded_unsafe replaces this code + if(!I || !L || I.loc != src || !(I in L.embedded_objects)) + return + L.embedded_objects -= I + L.receive_damage(I.embedding.embedded_unsafe_removal_pain_multiplier*I.w_class)//It hurts to rip it out, get surgery you dingus. + I.forceMove(get_turf(src)) + usr.put_in_hands(I) + usr.emote("scream") + usr.visible_message("[usr] successfully rips [I] out of [usr.p_their()] [L.name]!","You successfully remove [I] from your [L.name].") + if(!has_embedded_objects()) + clear_alert("embeddedobject") + SEND_SIGNAL(usr, COMSIG_CLEAR_MOOD_EVENT, "embedded") */ + return + + if(href_list["item"]) + var/slot = text2num(href_list["item"]) + if(slot in check_obscured_slots()) + to_chat(usr, "You can't reach that! Something is covering it.") + return + + if(href_list["pockets"]) + var/pocket_side = href_list["pockets"] + var/pocket_id = (pocket_side == "right" ? SLOT_R_STORE : SLOT_L_STORE) + var/obj/item/pocket_item = (pocket_id == SLOT_R_STORE ? r_store : l_store) + var/obj/item/place_item = usr.get_active_held_item() // Item to place in the pocket, if it's empty + + var/delay_denominator = 1 + if(pocket_item && !(pocket_item.item_flags & ABSTRACT)) + if(HAS_TRAIT(pocket_item, TRAIT_NODROP)) + to_chat(usr, "You try to empty [src]'s [pocket_side] pocket, it seems to be stuck!") + to_chat(usr, "You try to empty [src]'s [pocket_side] pocket.") + else if(place_item && place_item.mob_can_equip(src, usr, pocket_id, 1) && !(place_item.item_flags & ABSTRACT)) + to_chat(usr, "You try to place [place_item] into [src]'s [pocket_side] pocket.") + delay_denominator = 4 + else + return + + if(do_mob(usr, src, POCKET_STRIP_DELAY/delay_denominator, ignorehelditem = TRUE)) //placing an item into the pocket is 4 times faster + if(pocket_item) + if(pocket_item == (pocket_id == SLOT_R_STORE ? r_store : l_store)) //item still in the pocket we search + dropItemToGround(pocket_item) + if(!usr.can_hold_items() || !usr.put_in_hands(pocket_item)) + pocket_item.forceMove(drop_location()) + else + if(place_item) + if(place_item.mob_can_equip(src, usr, pocket_id, FALSE, TRUE)) + usr.temporarilyRemoveItemFromInventory(place_item, TRUE) + equip_to_slot(place_item, pocket_id, TRUE) + //do nothing otherwise + + // Update strip window + if(usr.machine == src && in_range(src, usr)) + show_inv(usr) + else + // Display a warning if the user mocks up + to_chat(src, "You feel your [pocket_side] pocket being fumbled with!") + + ..() //CITADEL CHANGE - removes a tab from behind this ..() so that flavortext can actually be examined + + +///////HUDs/////// + if(href_list["hud"]) + if(ishuman(usr)) + var/mob/living/carbon/human/H = usr + var/perpname = get_face_name(get_id_name("")) + if(istype(H.glasses, /obj/item/clothing/glasses/hud) || istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud)) + var/datum/data/record/R = find_record("name", perpname, GLOB.data_core.general) + if(href_list["photo_front"] || href_list["photo_side"]) + if(R) + if(!H.canUseHUD()) + return + else if(!istype(H.glasses, /obj/item/clothing/glasses/hud) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/medical)) + return + var/obj/item/photo/P = null + if(href_list["photo_front"]) + P = R.fields["photo_front"] + else if(href_list["photo_side"]) + P = R.fields["photo_side"] + if(P) + P.show(H) + + if(href_list["hud"] == "m") + if(istype(H.glasses, /obj/item/clothing/glasses/hud/health) || istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/medical)) + if(href_list["p_stat"]) + var/health_status = input(usr, "Specify a new physical status for this person.", "Medical HUD", R.fields["p_stat"]) in list("Active", "Physically Unfit", "*Unconscious*", "*Deceased*", "Cancel") + if(R) + if(!H.canUseHUD()) + return + else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/health) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/medical)) + return + if(health_status && health_status != "Cancel") + R.fields["p_stat"] = health_status + return + if(href_list["m_stat"]) + var/health_status = input(usr, "Specify a new mental status for this person.", "Medical HUD", R.fields["m_stat"]) in list("Stable", "*Watch*", "*Unstable*", "*Insane*", "Cancel") + if(R) + if(!H.canUseHUD()) + return + else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/health) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/medical)) + return + if(health_status && health_status != "Cancel") + R.fields["m_stat"] = health_status + return + if(href_list["evaluation"]) + if(!getBruteLoss() && !getFireLoss() && !getOxyLoss() && getToxLoss() < 20) + to_chat(usr, "No external injuries detected.
    ") + return + var/span = "notice" + var/status = "" + if(getBruteLoss()) + to_chat(usr, "Physical trauma analysis:") + for(var/X in bodyparts) + var/obj/item/bodypart/BP = X + var/brutedamage = BP.brute_dam + if(brutedamage > 0) + status = "received minor physical injuries." + span = "notice" + if(brutedamage > 20) + status = "been seriously damaged." + span = "danger" + if(brutedamage > 40) + status = "sustained major trauma!" + span = "userdanger" + if(brutedamage) + to_chat(usr, "[BP] appears to have [status]") + if(getFireLoss()) + to_chat(usr, "Analysis of skin burns:") + for(var/X in bodyparts) + var/obj/item/bodypart/BP = X + var/burndamage = BP.burn_dam + if(burndamage > 0) + status = "signs of minor burns." + span = "notice" + if(burndamage > 20) + status = "serious burns." + span = "danger" + if(burndamage > 40) + status = "major burns!" + span = "userdanger" + if(burndamage) + to_chat(usr, "[BP] appears to have [status]") + if(getOxyLoss()) + to_chat(usr, "Patient has signs of suffocation, emergency treatment may be required!") + if(getToxLoss() > 20) + to_chat(usr, "Gathered data is inconsistent with the analysis, possible cause: poisoning.") + + if(href_list["hud"] == "s") + if(istype(H.glasses, /obj/item/clothing/glasses/hud/security) || istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security)) + if(usr.stat || usr == src) //|| !usr.canmove || usr.restrained()) Fluff: Sechuds have eye-tracking technology and sets 'arrest' to people that the wearer looks and blinks at. + return //Non-fluff: This allows sec to set people to arrest as they get disarmed or beaten + // Checks the user has security clearence before allowing them to change arrest status via hud, comment out to enable all access + var/allowed_access = null + var/obj/item/clothing/glasses/G = H.glasses + if (!(G.obj_flags |= EMAGGED)) + if(H.wear_id) + var/list/access = H.wear_id.GetAccess() + if(ACCESS_SEC_DOORS in access) + allowed_access = H.get_authentification_name() + else + allowed_access = "@%&ERROR_%$*" + + + if(!allowed_access) + to_chat(H, "ERROR: Invalid Access") + return + + if(perpname) + R = find_record("name", perpname, GLOB.data_core.security) + if(R) + if(href_list["status"]) + var/setcriminal = input(usr, "Specify a new criminal status for this person.", "Security HUD", R.fields["criminal"]) in list("None", "*Arrest*", "Incarcerated", "Paroled", "Discharged", "Cancel") + if(setcriminal != "Cancel") + if(R) + if(H.canUseHUD()) + if(istype(H.glasses, /obj/item/clothing/glasses/hud/security) || istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security)) + investigate_log("[key_name(src)] has been set from [R.fields["criminal"]] to [setcriminal] by [key_name(usr)].", INVESTIGATE_RECORDS) + R.fields["criminal"] = setcriminal + sec_hud_set_security_status() + return + + if(href_list["view"]) + if(R) + if(!H.canUseHUD()) + return + else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security)) + return + to_chat(usr, "Name: [R.fields["name"]] Criminal Status: [R.fields["criminal"]]") + to_chat(usr, "Minor Crimes:") + for(var/datum/data/crime/c in R.fields["mi_crim"]) + to_chat(usr, "Crime: [c.crimeName]") + to_chat(usr, "Details: [c.crimeDetails]") + to_chat(usr, "Added by [c.author] at [c.time]") + to_chat(usr, "----------") + to_chat(usr, "Major Crimes:") + for(var/datum/data/crime/c in R.fields["ma_crim"]) + to_chat(usr, "Crime: [c.crimeName]") + to_chat(usr, "Details: [c.crimeDetails]") + to_chat(usr, "Added by [c.author] at [c.time]") + to_chat(usr, "----------") + to_chat(usr, "Notes: [R.fields["notes"]]") + return + + if(href_list["add_crime"]) + switch(alert("What crime would you like to add?","Security HUD","Minor Crime","Major Crime","Cancel")) + if("Minor Crime") + if(R) + var/t1 = stripped_input("Please input minor crime names:", "Security HUD", "", null) + var/t2 = stripped_multiline_input("Please input minor crime details:", "Security HUD", "", null) + if(R) + if (!t1 || !t2 || !allowed_access) + return + else if(!H.canUseHUD()) + return + else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security)) + return + var/crime = GLOB.data_core.createCrimeEntry(t1, t2, allowed_access, STATION_TIME_TIMESTAMP("hh:mm:ss")) + GLOB.data_core.addMinorCrime(R.fields["id"], crime) + investigate_log("New Minor Crime: [t1]: [t2] | Added to [R.fields["name"]] by [key_name(usr)]", INVESTIGATE_RECORDS) + to_chat(usr, "Successfully added a minor crime.") + return + if("Major Crime") + if(R) + var/t1 = stripped_input("Please input major crime names:", "Security HUD", "", null) + var/t2 = stripped_multiline_input("Please input major crime details:", "Security HUD", "", null) + if(R) + if (!t1 || !t2 || !allowed_access) + return + else if (!H.canUseHUD()) + return + else if (!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security)) + return + var/crime = GLOB.data_core.createCrimeEntry(t1, t2, allowed_access, STATION_TIME_TIMESTAMP("hh:mm:ss")) + GLOB.data_core.addMajorCrime(R.fields["id"], crime) + investigate_log("New Major Crime: [t1]: [t2] | Added to [R.fields["name"]] by [key_name(usr)]", INVESTIGATE_RECORDS) + to_chat(usr, "Successfully added a major crime.") + return + + if(href_list["view_comment"]) + if(R) + if(!H.canUseHUD()) + return + else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security)) + return + to_chat(usr, "Comments/Log:") + var/counter = 1 + while(R.fields[text("com_[]", counter)]) + to_chat(usr, R.fields[text("com_[]", counter)]) + to_chat(usr, "----------") + counter++ + return + + if(href_list["add_comment"]) + if(R) + var/t1 = stripped_multiline_input("Add Comment:", "Secure. records", null, null) + if(R) + if (!t1 || !allowed_access) + return + else if(!H.canUseHUD()) + return + else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security)) + return + var/counter = 1 + while(R.fields[text("com_[]", counter)]) + counter++ + R.fields[text("com_[]", counter)] = text("Made by [] on [] [], []
    []", allowed_access, STATION_TIME_TIMESTAMP("hh:mm:ss"), time2text(world.realtime, "MMM DD"), GLOB.year_integer+540, t1) + to_chat(usr, "Successfully added comment.") + return + to_chat(usr, "Unable to locate a data core entry for this person.") + +/mob/living/carbon/human/proc/canUseHUD() + return !(src.stat || IsKnockdown() || IsStun() || src.restrained()) + +/mob/living/carbon/human/can_inject(mob/user, error_msg, target_zone, penetrate_thick = FALSE, bypass_immunity = FALSE) + . = 1 // Default to returning true. + if(user && !target_zone) + target_zone = user.zone_selected + if(HAS_TRAIT(src, TRAIT_PIERCEIMMUNE) && !bypass_immunity) + . = 0 + // If targeting the head, see if the head item is thin enough. + // If targeting anything else, see if the wear suit is thin enough. + if (!penetrate_thick) + if(above_neck(target_zone)) + if(head && istype(head, /obj/item/clothing)) + var/obj/item/clothing/CH = head + if (CH.clothing_flags & THICKMATERIAL) + . = 0 + else + if(wear_suit && istype(wear_suit, /obj/item/clothing)) + var/obj/item/clothing/CS = wear_suit + if (CS.clothing_flags & THICKMATERIAL) + . = 0 + if(!. && error_msg && user) + // Might need re-wording. + to_chat(user, "There is no exposed flesh or thin material [above_neck(target_zone) ? "on [p_their()] head" : "on [p_their()] body"].") + +/mob/living/carbon/human/proc/check_obscured_slots() + var/list/obscured = list() + + if(wear_suit) + if(wear_suit.flags_inv & HIDEGLOVES) + obscured |= SLOT_GLOVES + if(wear_suit.flags_inv & HIDEJUMPSUIT) + obscured |= SLOT_W_UNIFORM + if(wear_suit.flags_inv & HIDESHOES) + obscured |= SLOT_SHOES + + if(head) + if(head.flags_inv & HIDEMASK) + obscured |= SLOT_WEAR_MASK + if(head.flags_inv & HIDEEYES) + obscured |= SLOT_GLASSES + if(head.flags_inv & HIDEEARS) + obscured |= SLOT_EARS + + if(wear_mask) + if(wear_mask.flags_inv & HIDEEYES) + obscured |= SLOT_GLASSES + + if(obscured.len) + return obscured + else + return null + +/mob/living/carbon/human/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null) + if(judgement_criteria & JUDGE_EMAGGED) + return 10 //Everyone is a criminal! + + var/threatcount = 0 + + //Lasertag bullshit + if(lasercolor) + if(lasercolor == "b")//Lasertag turrets target the opposing team, how great is that? -Sieve + if(istype(wear_suit, /obj/item/clothing/suit/redtag)) + threatcount += 4 + if(is_holding_item_of_type(/obj/item/gun/energy/laser/redtag)) + threatcount += 4 + if(istype(belt, /obj/item/gun/energy/laser/redtag)) + threatcount += 2 + + if(lasercolor == "r") + if(istype(wear_suit, /obj/item/clothing/suit/bluetag)) + threatcount += 4 + if(is_holding_item_of_type(/obj/item/gun/energy/laser/bluetag)) + threatcount += 4 + if(istype(belt, /obj/item/gun/energy/laser/bluetag)) + threatcount += 2 + + return threatcount + + //Check for ID + var/obj/item/card/id/idcard = get_idcard(FALSE) + if( (judgement_criteria & JUDGE_IDCHECK) && !idcard && name=="Unknown") + threatcount += 4 + + //Check for weapons + if( (judgement_criteria & JUDGE_WEAPONCHECK) && weaponcheck) + if(!idcard || !(ACCESS_WEAPONS in idcard.access)) + for(var/obj/item/I in held_items) //if they're holding a gun + if(weaponcheck.Invoke(I)) + threatcount += 4 + if(weaponcheck.Invoke(belt) || weaponcheck.Invoke(back)) //if a weapon is present in the belt or back slot + threatcount += 2 //not enough to trigger look_for_perp() on it's own unless they also have criminal status. + + //Check for arrest warrant + if(judgement_criteria & JUDGE_RECORDCHECK) + var/perpname = get_face_name(get_id_name()) + var/datum/data/record/R = find_record("name", perpname, GLOB.data_core.security) + if(R && R.fields["criminal"]) + switch(R.fields["criminal"]) + if("*Arrest*") + threatcount += 5 + if("Incarcerated") + threatcount += 2 + if("Paroled") + threatcount += 2 + + //Check for dresscode violations + if(istype(head, /obj/item/clothing/head/wizard) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/wizard) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/syndi) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/shielded/syndi)) + threatcount += 4 //fuk u antags <3 //no you + + //mindshield implants imply trustworthyness + if(HAS_TRAIT(src, TRAIT_MINDSHIELD)) + threatcount -= 1 + + //Agent cards lower threatlevel. + if(istype(idcard, /obj/item/card/id/syndicate)) + threatcount -= 2 + + return threatcount + + +//Used for new human mobs created by cloning/goleming/podding +/mob/living/carbon/human/proc/set_cloned_appearance() + if(gender == MALE) + facial_hair_style = "Full Beard" + else + facial_hair_style = "Shaved" + hair_style = pick("Bedhead", "Bedhead 2", "Bedhead 3") + underwear = "Nude" + undershirt = "Nude" + update_body() + update_hair() + update_genitals() + +/mob/living/carbon/human/singularity_pull(S, current_size) + ..() + if(current_size >= STAGE_THREE) + for(var/obj/item/hand in held_items) + if(prob(current_size * 5) && hand.w_class >= ((11-current_size)/2) && dropItemToGround(hand)) + step_towards(hand, src) + to_chat(src, "\The [S] pulls \the [hand] from your grip!") + rad_act(current_size * 3) + if(mob_negates_gravity()) + return + +/mob/living/carbon/human/proc/do_cpr(mob/living/carbon/C) + CHECK_DNA_AND_SPECIES(C) + + if(C.stat == DEAD || (HAS_TRAIT(C, TRAIT_FAKEDEATH))) + to_chat(src, "[C.name] is dead!") + return + if(is_mouth_covered()) + to_chat(src, "Remove your mask first!") + return 0 + if(C.is_mouth_covered()) + to_chat(src, "Remove [p_their()] mask first!") + return 0 + + if(C.cpr_time < world.time + 30) + visible_message("[src] is trying to perform CPR on [C.name]!", \ + "You try to perform CPR on [C.name]... Hold still!") + if(!do_mob(src, C)) + to_chat(src, "You fail to perform CPR on [C]!") + return 0 + + var/they_breathe = !HAS_TRAIT(C, TRAIT_NOBREATH) + var/they_lung = C.getorganslot(ORGAN_SLOT_LUNGS) + + if(C.health > C.crit_threshold) + return + + src.visible_message("[src] performs CPR on [C.name]!", "You perform CPR on [C.name].") + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "perform_cpr", /datum/mood_event/perform_cpr) + C.cpr_time = world.time + log_combat(src, C, "CPRed") + + if(they_breathe && they_lung) + var/suff = min(C.getOxyLoss(), 7) + C.adjustOxyLoss(-suff) + C.updatehealth() + to_chat(C, "You feel a breath of fresh air enter your lungs... It feels good...") + else if(they_breathe && !they_lung) + to_chat(C, "You feel a breath of fresh air... but you don't feel any better...") + else + to_chat(C, "You feel a breath of fresh air... which is a sensation you don't recognise...") + +/mob/living/carbon/human/cuff_resist(obj/item/I) + if(dna && dna.check_mutation(HULK)) + say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ), forced = "hulk") + if(..(I, cuff_break = FAST_CUFFBREAK)) + dropItemToGround(I) + else + if(..()) + dropItemToGround(I) + +/mob/living/carbon/human/clean_blood() + var/mob/living/carbon/human/H = src + if(H.gloves) + if(H.gloves.clean_blood()) + H.update_inv_gloves() + else + ..() // Clear the Blood_DNA list + if(H.bloody_hands) + H.bloody_hands = 0 + H.update_inv_gloves() + update_icons() //apply the now updated overlays to the mob + +/mob/living/carbon/human/wash_cream() + if(creamed) //clean both to prevent a rare bug + cut_overlay(mutable_appearance('icons/effects/creampie.dmi', "creampie_lizard")) + cut_overlay(mutable_appearance('icons/effects/creampie.dmi', "creampie_human")) + cut_overlay(mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi', "cum_normal")) + cut_overlay(mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi', "cum_large")) + creamed = FALSE + + +//Turns a mob black, flashes a skeleton overlay +//Just like a cartoon! +/mob/living/carbon/human/proc/electrocution_animation(anim_duration) + //Handle mutant parts if possible + if(dna && dna.species) + add_atom_colour("#000000", TEMPORARY_COLOUR_PRIORITY) + var/static/mutable_appearance/electrocution_skeleton_anim + if(!electrocution_skeleton_anim) + electrocution_skeleton_anim = mutable_appearance(icon, "electrocuted_base") + electrocution_skeleton_anim.appearance_flags |= RESET_COLOR|KEEP_APART + add_overlay(electrocution_skeleton_anim) + addtimer(CALLBACK(src, .proc/end_electrocution_animation, electrocution_skeleton_anim), anim_duration) + + else //or just do a generic animation + flick_overlay_view(image(icon,src,"electrocuted_generic",ABOVE_MOB_LAYER), src, anim_duration) + +/mob/living/carbon/human/proc/end_electrocution_animation(mutable_appearance/MA) + remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, "#000000") + cut_overlay(MA) + +/mob/living/carbon/human/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE) + if(incapacitated() || lying ) + to_chat(src, "You can't do that right now!") + return FALSE + if(!Adjacent(M) && (M.loc != src)) + if((be_close == 0) || (!no_tk && (dna.check_mutation(TK) && tkMaxRangeCheck(src, M)))) + return TRUE + to_chat(src, "You are too far away!") + return FALSE + return TRUE + +/mob/living/carbon/human/resist_restraints() + if(wear_suit && wear_suit.breakouttime) + changeNext_move(CLICK_CD_BREAKOUT) + last_special = world.time + CLICK_CD_BREAKOUT + cuff_resist(wear_suit) + else + ..() + +/mob/living/carbon/human/replace_records_name(oldname,newname) // Only humans have records right now, move this up if changed. + for(var/list/L in list(GLOB.data_core.general,GLOB.data_core.medical,GLOB.data_core.security,GLOB.data_core.locked)) + var/datum/data/record/R = find_record("name", oldname, L) + if(R) + R.fields["name"] = newname + +/mob/living/carbon/human/get_total_tint() + . = ..() + if(glasses) + . += glasses.tint + +/mob/living/carbon/human/update_health_hud() + if(!client || !hud_used) + return + if(dna.species.update_health_hud()) + return + else + if(hud_used.healths) + var/health_amount = health - CLAMP(getStaminaLoss()-50, 0, 80)//CIT CHANGE - makes staminaloss have less of an impact on the health hud + if(..(health_amount)) //not dead + switch(hal_screwyhud) + if(SCREWYHUD_CRIT) + hud_used.healths.icon_state = "health6" + if(SCREWYHUD_DEAD) + hud_used.healths.icon_state = "health7" + if(SCREWYHUD_HEALTHY) + hud_used.healths.icon_state = "health0" + if(hud_used.healthdoll) + hud_used.healthdoll.cut_overlays() + if(stat != DEAD) + hud_used.healthdoll.icon_state = "healthdoll_OVERLAY" + for(var/X in bodyparts) + var/obj/item/bodypart/BP = X + var/damage = BP.burn_dam + BP.brute_dam + var/comparison = (BP.max_damage/5) + var/icon_num = 0 + if(damage) + icon_num = 1 + if(damage > (comparison)) + icon_num = 2 + if(damage > (comparison*2)) + icon_num = 3 + if(damage > (comparison*3)) + icon_num = 4 + if(damage > (comparison*4)) + icon_num = 5 + if(hal_screwyhud == SCREWYHUD_HEALTHY) + icon_num = 0 + if(icon_num) + hud_used.healthdoll.add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[BP.body_zone][icon_num]")) + for(var/t in get_missing_limbs()) //Missing limbs + hud_used.healthdoll.add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[t]6")) + for(var/t in get_disabled_limbs()) //Disabled limbs + hud_used.healthdoll.add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[t]7")) + else + hud_used.healthdoll.icon_state = "healthdoll_DEAD" + +/mob/living/carbon/human/fully_heal(admin_revive = 0) + if(admin_revive) + regenerate_limbs() + regenerate_organs() + remove_all_embedded_objects() + set_heartattack(FALSE) + drunkenness = 0 + for(var/datum/mutation/human/HM in dna.mutations) + if(HM.quality != POSITIVE) + dna.remove_mutation(HM.name) + if(blood_volume < (BLOOD_VOLUME_NORMAL*blood_ratio)) + blood_volume = (BLOOD_VOLUME_NORMAL*blood_ratio) + ..() + +/mob/living/carbon/human/check_weakness(obj/item/weapon, mob/living/attacker) + . = ..() + if (dna && dna.species) + . += dna.species.check_weakness(weapon, attacker) + +/mob/living/carbon/human/is_literate() + return 1 + +/mob/living/carbon/human/can_hold_items() + return TRUE + +/mob/living/carbon/human/update_gravity(has_gravity,override = 0) + if(dna && dna.species) //prevents a runtime while a human is being monkeyfied + override = dna.species.override_float + ..() + +/mob/living/carbon/human/vomit(lost_nutrition = 10, blood = 0, stun = 1, distance = 0, message = 1, toxic = 0) + if(blood && (NOBLOOD in dna.species.species_traits)) + if(message) + visible_message("[src] dry heaves!", \ + "You try to throw up, but there's nothing in your stomach!") + if(stun) + Knockdown(200) + return 1 + ..() + +/mob/living/carbon/human/vv_get_dropdown() + . = ..() + VV_DROPDOWN_OPTION("", "---------") + VV_DROPDOWN_OPTION(VV_HK_COPY_OUTFIT, "Copy Outfit") + VV_DROPDOWN_OPTION(VV_HK_MOD_QUIRKS, "Add/Remove Quirks") + VV_DROPDOWN_OPTION(VV_HK_MAKE_MONKEY, "Make Monkey") + VV_DROPDOWN_OPTION(VV_HK_MAKE_CYBORG, "Make Cyborg") + VV_DROPDOWN_OPTION(VV_HK_MAKE_SLIME, "Make Slime") + VV_DROPDOWN_OPTION(VV_HK_MAKE_ALIEN, "Make Alien") + VV_DROPDOWN_OPTION(VV_HK_SET_SPECIES, "Set Species") + VV_DROPDOWN_OPTION(VV_HK_PURRBATION, "Toggle Purrbation") + +/mob/living/carbon/human/vv_do_topic(list/href_list) + . = ..() + if(href_list[VV_HK_COPY_OUTFIT]) + if(!check_rights(R_SPAWN)) + return + copy_outfit() + if(href_list[VV_HK_MOD_QUIRKS]) + if(!check_rights(R_SPAWN)) + return + + var/list/options = list("Clear"="Clear") + for(var/x in subtypesof(/datum/quirk)) + var/datum/quirk/T = x + var/qname = initial(T.name) + options[has_quirk(T) ? "[qname] (Remove)" : "[qname] (Add)"] = T + + var/result = input(usr, "Choose quirk to add/remove","Quirk Mod") as null|anything in options + if(result) + if(result == "Clear") + for(var/datum/quirk/q in roundstart_quirks) + remove_quirk(q.type) + else + var/T = options[result] + if(has_quirk(T)) + remove_quirk(T) + else + add_quirk(T,TRUE) + if(href_list[VV_HK_MAKE_MONKEY]) + if(!check_rights(R_SPAWN)) + return + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") + return + usr.client.holder.Topic("vv_override", list("monkeyone"=href_list[VV_HK_TARGET])) + if(href_list[VV_HK_MAKE_CYBORG]) + if(!check_rights(R_SPAWN)) + return + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") + return + usr.client.holder.Topic("vv_override", list("makerobot"=href_list[VV_HK_TARGET])) + if(href_list[VV_HK_MAKE_ALIEN]) + if(!check_rights(R_SPAWN)) + return + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") + return + usr.client.holder.Topic("vv_override", list("makealien"=href_list[VV_HK_TARGET])) + if(href_list[VV_HK_MAKE_SLIME]) + if(!check_rights(R_SPAWN)) + return + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") + return + usr.client.holder.Topic("vv_override", list("makeslime"=href_list[VV_HK_TARGET])) + if(href_list[VV_HK_SET_SPECIES]) + if(!check_rights(R_SPAWN)) + return + var/result = input(usr, "Please choose a new species","Species") as null|anything in GLOB.species_list + if(result) + var/newtype = GLOB.species_list[result] + admin_ticket_log("[key_name_admin(usr)] has modified the bodyparts of [src] to [result]") + set_species(newtype) + if(href_list[VV_HK_PURRBATION]) + if(!check_rights(R_SPAWN)) + return + if(!ishumanbasic(src)) + to_chat(usr, "This can only be done to the basic human species at the moment.") + return + var/success = purrbation_toggle(src) + if(success) + to_chat(usr, "Put [src] on purrbation.") + log_admin("[key_name(usr)] has put [key_name(src)] on purrbation.") + var/msg = "[key_name_admin(usr)] has put [key_name(src)] on purrbation." + message_admins(msg) + admin_ticket_log(src, msg) + + else + to_chat(usr, "Removed [src] from purrbation.") + log_admin("[key_name(usr)] has removed [key_name(src)] from purrbation.") + var/msg = "[key_name_admin(usr)] has removed [key_name(src)] from purrbation." + message_admins(msg) + admin_ticket_log(src, msg) + +/mob/living/carbon/human/MouseDrop_T(mob/living/target, mob/living/user) + if(pulling == target && grab_state >= GRAB_AGGRESSIVE && stat == CONSCIOUS) + //If they dragged themselves and we're currently aggressively grabbing them try to piggyback + if(user == target && can_piggyback(target)) + piggyback(target) + return + //If you dragged them to you and you're aggressively grabbing try to fireman carry them + else if(user != target) + fireman_carry(target) + return + . = ..() + +//src is the user that will be carrying, target is the mob to be carried +/mob/living/carbon/human/proc/can_piggyback(mob/living/carbon/target) + return (istype(target) && target.stat == CONSCIOUS) + +/mob/living/carbon/human/proc/can_be_firemanned(mob/living/carbon/target) + return (ishuman(target) && target.lying) + +/mob/living/carbon/human/proc/fireman_carry(mob/living/carbon/target) + if(can_be_firemanned(target)) + visible_message("[src] starts lifting [target] onto their back...", + "You start lifting [target] onto your back...") + if(do_after(src, 30, TRUE, target)) + //Second check to make sure they're still valid to be carried + if(can_be_firemanned(target) && !incapacitated(FALSE, TRUE)) + target.resting = FALSE + buckle_mob(target, TRUE, TRUE, 90, 1, 0) + return + visible_message("[src] fails to fireman carry [target]!") + else + to_chat(src, "You can't fireman carry [target] while they're standing!") + +/mob/living/carbon/human/proc/piggyback(mob/living/carbon/target) + if(can_piggyback(target)) + visible_message("[target] starts to climb onto [src]...") + if(do_after(target, 15, target = src)) + if(can_piggyback(target)) + if(target.incapacitated(FALSE, TRUE) || incapacitated(FALSE, TRUE)) + target.visible_message("[target] can't hang onto [src]!") + return + buckle_mob(target, TRUE, TRUE, FALSE, 0, 2) + else + visible_message("[target] fails to climb onto [src]!") + else + to_chat(target, "You can't piggyback ride [src] right now!") + +/mob/living/carbon/human/buckle_mob(mob/living/target, force = FALSE, check_loc = TRUE, lying_buckle = FALSE, hands_needed = 0, target_hands_needed = 0) + if(!force)//humans are only meant to be ridden through piggybacking and special cases + return + if(!is_type_in_typecache(target, can_ride_typecache)) + target.visible_message("[target] really can't seem to mount [src]...") + return + buckle_lying = lying_buckle + var/datum/component/riding/human/riding_datum = LoadComponent(/datum/component/riding/human) + if(target_hands_needed) + riding_datum.ride_check_rider_restrained = TRUE + if(buckled_mobs && ((target in buckled_mobs) || (buckled_mobs.len >= max_buckled_mobs)) || buckled) + return + var/equipped_hands_self + var/equipped_hands_target + if(hands_needed) + equipped_hands_self = riding_datum.equip_buckle_inhands(src, hands_needed, target) + if(target_hands_needed) + equipped_hands_target = riding_datum.equip_buckle_inhands(target, target_hands_needed) + + if(hands_needed || target_hands_needed) + if(hands_needed && !equipped_hands_self) + src.visible_message("[src] can't get a grip on [target] because their hands are full!", + "You can't get a grip on [target] because your hands are full!") + return + else if(target_hands_needed && !equipped_hands_target) + target.visible_message("[target] can't get a grip on [src] because their hands are full!", + "You can't get a grip on [src] because your hands are full!") + return + + stop_pulling() + riding_datum.handle_vehicle_layer() + . = ..(target, force, check_loc) + +/mob/living/carbon/human/proc/is_shove_knockdown_blocked() //If you want to add more things that block shove knockdown, extend this + for(var/obj/item/clothing/C in get_equipped_items()) //doesn't include pockets + if(C.blocks_shove_knockdown) + return TRUE + return FALSE + +/mob/living/carbon/human/proc/clear_shove_slowdown() + remove_movespeed_modifier(SHOVE_SLOWDOWN_ID) + var/active_item = get_active_held_item() + if(is_type_in_typecache(active_item, GLOB.shove_disarming_types)) + visible_message("[src.name] regains their grip on \the [active_item]!", "You regain your grip on \the [active_item]", null, COMBAT_MESSAGE_RANGE) + +/mob/living/carbon/human/do_after_coefficent() + . = ..() + . *= physiology.do_after_speed + +/mob/living/carbon/human/species + var/race = null + +/mob/living/carbon/human/species/Initialize() + . = ..() + set_species(race) + +/mob/living/carbon/human/species/abductor + race = /datum/species/abductor + +/mob/living/carbon/human/species/android + race = /datum/species/android + +/mob/living/carbon/human/species/angel + race = /datum/species/angel + +/mob/living/carbon/human/species/corporate + race = /datum/species/corporate + +/mob/living/carbon/human/species/dullahan + race = /datum/species/dullahan + +/mob/living/carbon/human/species/felinid + race = /datum/species/human/felinid + +/mob/living/carbon/human/species/fly + race = /datum/species/fly + +/mob/living/carbon/human/species/golem + race = /datum/species/golem + +/mob/living/carbon/human/species/golem/random + race = /datum/species/golem/random + +/mob/living/carbon/human/species/golem/adamantine + race = /datum/species/golem/adamantine + +/mob/living/carbon/human/species/golem/plasma + race = /datum/species/golem/plasma + +/mob/living/carbon/human/species/golem/diamond + race = /datum/species/golem/diamond + +/mob/living/carbon/human/species/golem/gold + race = /datum/species/golem/gold + +/mob/living/carbon/human/species/golem/silver + race = /datum/species/golem/silver + +/mob/living/carbon/human/species/golem/plasteel + race = /datum/species/golem/plasteel + +/mob/living/carbon/human/species/golem/titanium + race = /datum/species/golem/titanium + +/mob/living/carbon/human/species/golem/plastitanium + race = /datum/species/golem/plastitanium + +/mob/living/carbon/human/species/golem/alien_alloy + race = /datum/species/golem/alloy + +/mob/living/carbon/human/species/golem/wood + race = /datum/species/golem/wood + +/mob/living/carbon/human/species/golem/uranium + race = /datum/species/golem/uranium + +/mob/living/carbon/human/species/golem/sand + race = /datum/species/golem/sand + +/mob/living/carbon/human/species/golem/glass + race = /datum/species/golem/glass + +/mob/living/carbon/human/species/golem/bluespace + race = /datum/species/golem/bluespace + +/mob/living/carbon/human/species/golem/bananium + race = /datum/species/golem/bananium + +/mob/living/carbon/human/species/golem/blood_cult + race = /datum/species/golem/runic + +/mob/living/carbon/human/species/golem/cloth + race = /datum/species/golem/cloth + +/mob/living/carbon/human/species/golem/plastic + race = /datum/species/golem/plastic + +/mob/living/carbon/human/species/golem/bronze + race = /datum/species/golem/bronze + +/mob/living/carbon/human/species/golem/cardboard + race = /datum/species/golem/cardboard + +/mob/living/carbon/human/species/golem/leather + race = /datum/species/golem/leather + +/mob/living/carbon/human/species/golem/bone + race = /datum/species/golem/bone + +/mob/living/carbon/human/species/golem/durathread + race = /datum/species/golem/durathread + +/mob/living/carbon/human/species/golem/clockwork + race = /datum/species/golem/clockwork + +/mob/living/carbon/human/species/golem/clockwork/no_scrap + race = /datum/species/golem/clockwork/no_scrap + +/mob/living/carbon/human/species/jelly + race = /datum/species/jelly + +/mob/living/carbon/human/species/jelly/slime + race = /datum/species/jelly/slime + +/mob/living/carbon/human/species/jelly/stargazer + race = /datum/species/jelly/stargazer + +/mob/living/carbon/human/species/jelly/luminescent + race = /datum/species/jelly/luminescent + +/mob/living/carbon/human/species/lizard + race = /datum/species/lizard + +/mob/living/carbon/human/species/lizard/ashwalker + race = /datum/species/lizard/ashwalker + +/mob/living/carbon/human/species/moth + race = /datum/species/moth + +/mob/living/carbon/human/species/mush + race = /datum/species/mush + +/mob/living/carbon/human/species/plasma + race = /datum/species/plasmaman + +/mob/living/carbon/human/species/pod + race = /datum/species/pod + +/mob/living/carbon/human/species/shadow + race = /datum/species/shadow + +/mob/living/carbon/human/species/shadow/nightmare + race = /datum/species/shadow/nightmare + +/mob/living/carbon/human/species/skeleton + race = /datum/species/skeleton + +/mob/living/carbon/human/species/synth + race = /datum/species/synth + +/mob/living/carbon/human/species/synth/military + race = /datum/species/synth/military + +/mob/living/carbon/human/species/vampire + race = /datum/species/vampire + +/mob/living/carbon/human/species/zombie + race = /datum/species/zombie + +/mob/living/carbon/human/species/zombie/infectious + race = /datum/species/zombie/infectious + +/mob/living/carbon/human/species/zombie/krokodil_addict + race = /datum/species/krokodil_addict + +/mob/living/carbon/human/species/mammal + race = /datum/species/mammal + +/mob/living/carbon/human/species/insect + race = /datum/species/insect + +/mob/living/carbon/human/species/xeno + race = /datum/species/xeno + +/mob/living/carbon/human/species/ipc + race = /datum/species/ipc + +/mob/living/carbon/human/species/roundstartslime + race = /datum/species/jelly/roundstartslime +//define holder_type on nerds we wanna commit scoop to +/* /mob/living/carbon/human + var/holder_type = /obj/item/clothing/head/mob_holder/micro + can_be_held = "micro" +*/ + diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index d457a3d5..5c6f1ecc 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1277,3 +1277,18 @@ if(healing_chems) reagents.add_reagent_list(healing_chems) +/mob/living/vv_get_header() + . = ..() + var/refid = REF(src) + . += {" +
    [VV_HREF_TARGETREF_1V(refid, VV_HK_BASIC_EDIT, "[ckey || "no ckey"]", NAMEOF(src, ckey))] / [VV_HREF_TARGETREF_1V(refid, VV_HK_BASIC_EDIT, "[real_name || "no real name"]", NAMEOF(src, real_name))] +
    + BRUTE:[getBruteLoss()] + FIRE:[getFireLoss()] + TOXIN:[getToxLoss()] + OXY:[getOxyLoss()] + CLONE:[getCloneLoss()] + BRAIN:[getOrganLoss(ORGAN_SLOT_BRAIN)] + STAMINA:[getStaminaLoss()] + + "} diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index fac2f8d3..f3ca2844 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -979,18 +979,65 @@ mob/visible_message(message, self_message, blind_message, vision_distance = DEFA /mob/vv_get_dropdown() . = ..() - . += "---" - .["Gib"] = "?_src_=vars;[HrefToken()];gib=[REF(src)]" - .["Give Spell"] = "?_src_=vars;[HrefToken()];give_spell=[REF(src)]" - .["Remove Spell"] = "?_src_=vars;[HrefToken()];remove_spell=[REF(src)]" - .["Give Disease"] = "?_src_=vars;[HrefToken()];give_disease=[REF(src)]" - .["Toggle Godmode"] = "?_src_=vars;[HrefToken()];godmode=[REF(src)]" - .["Drop Everything"] = "?_src_=vars;[HrefToken()];drop_everything=[REF(src)]" - .["Regenerate Icons"] = "?_src_=vars;[HrefToken()];regenerateicons=[REF(src)]" - .["Show player panel"] = "?_src_=vars;[HrefToken()];mob_player_panel=[REF(src)]" - .["Toggle Build Mode"] = "?_src_=vars;[HrefToken()];build_mode=[REF(src)]" - .["Assume Direct Control"] = "?_src_=vars;[HrefToken()];direct_control=[REF(src)]" - .["Offer Control to Ghosts"] = "?_src_=vars;[HrefToken()];offer_control=[REF(src)]" + 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_BUILDMODE)) + 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) /mob/vv_get_var(var_name) switch(var_name) @@ -998,6 +1045,10 @@ mob/visible_message(message, self_message, blind_message, vision_distance = DEFA 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) + /mob/verb/open_language_menu() set name = "Open Language Menu" set category = "IC" diff --git a/tgstation.dme b/tgstation.dme index cb439391..adcfeb8b 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -152,6 +152,7 @@ #include "code\__HELPERS\time.dm" #include "code\__HELPERS\type2type.dm" #include "code\__HELPERS\type2type_vr.dm" +#include "code\__HELPERS\type_processing.dm" #include "code\__HELPERS\typelists.dm" #include "code\__HELPERS\unsorted.dm" #include "code\__HELPERS\verbs.dm" @@ -1188,6 +1189,7 @@ #include "code\modules\admin\stickyban.dm" #include "code\modules\admin\topic.dm" #include "code\modules\admin\whitelist.dm" +#include "code\modules\admin\callproc\callproc.dm" #include "code\modules\admin\DB_ban\functions.dm" #include "code\modules\admin\verbs\adminhelp.dm" #include "code\modules\admin\verbs\adminjump.dm" @@ -1211,8 +1213,6 @@ #include "code\modules\admin\verbs\map_template_loadverb.dm" #include "code\modules\admin\verbs\mapping.dm" #include "code\modules\admin\verbs\maprotation.dm" -#include "code\modules\admin\verbs\massmodvar.dm" -#include "code\modules\admin\verbs\modifyvariables.dm" #include "code\modules\admin\verbs\one_click_antag.dm" #include "code\modules\admin\verbs\onlyone.dm" #include "code\modules\admin\verbs\panicbunker.dm" @@ -1226,7 +1226,16 @@ #include "code\modules\admin\verbs\SDQL2\SDQL_2.dm" #include "code\modules\admin\verbs\SDQL2\SDQL_2_parser.dm" #include "code\modules\admin\verbs\SDQL2\SDQL_2_wrappers.dm" +#include "code\modules\admin\view_variables\admin_delete.dm" #include "code\modules\admin\view_variables\debug_variables.dm" +#include "code\modules\admin\view_variables\get_variables.dm" +#include "code\modules\admin\view_variables\mark_datum.dm" +#include "code\modules\admin\view_variables\mass_edit_variables.dm" +#include "code\modules\admin\view_variables\modify_variables.dm" +#include "code\modules\admin\view_variables\topic.dm" +#include "code\modules\admin\view_variables\topic_basic.dm" +#include "code\modules\admin\view_variables\topic_list.dm" +#include "code\modules\admin\view_variables\view_variables.dm" #include "code\modules\antagonists\_common\antag_datum.dm" #include "code\modules\antagonists\_common\antag_helpers.dm" #include "code\modules\antagonists\_common\antag_hud.dm"