diff --git a/code/__DEFINES/keybinding.dm b/code/__DEFINES/keybinding.dm index eacb3a02714..2fc4f935809 100644 --- a/code/__DEFINES/keybinding.dm +++ b/code/__DEFINES/keybinding.dm @@ -14,6 +14,7 @@ #define COMSIG_KB_ADMIN_INVISIMINTOGGLE_DOWN "keybinding_admin_invisimintoggle_down" #define COMSIG_KB_ADMIN_DEADMIN_DOWN "keybinding_admin_deadmin_down" #define COMSIG_KB_ADMIN_READMIN_DOWN "keybinding_admin_readmin_down" +#define COMSIG_KB_ADMIN_VIEWTAGS_DOWN "keybinding_admin_viewtags_down" //Carbon #define COMSIG_KB_CARBON_HOLDRUNMOVEINTENT_DOWN "keybinding_carbon_holdrunmoveintent_down" diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm index 85d6b5483d4..23da32cc827 100644 --- a/code/__DEFINES/vv.dm +++ b/code/__DEFINES/vv.dm @@ -21,6 +21,7 @@ #define VV_INFINITY "Infinity" #define VV_RESTORE_DEFAULT "Restore to Default" #define VV_MARKED_DATUM "Marked Datum" +#define VV_TAGGED_DATUM "Tagged Datum" #define VV_BITFIELD "Bitfield" #define VV_TEXT_LOCATE "Custom Reference Locate" #define VV_PROCCALL_RETVAL "Return Value of Proccall" @@ -76,6 +77,7 @@ #define VV_HK_EXPOSE "expose" #define VV_HK_CALLPROC "proc_call" #define VV_HK_MARK "mark" +#define VV_HK_TAG "tag" #define VV_HK_ADDCOMPONENT "addcomponent" #define VV_HK_REMOVECOMPONENT "removecomponent" #define VV_HK_MASS_REMOVECOMPONENT "massremovecomponent" diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 7295da34a61..cda883bb946 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -90,7 +90,10 @@ ShiftClickOn(A) return if(LAZYACCESS(modifiers, MIDDLE_CLICK)) - MiddleClickOn(A, params) + if(LAZYACCESS(modifiers, CTRL_CLICK)) + CtrlMiddleClickOn(A) + else + MiddleClickOn(A, params) return if(LAZYACCESS(modifiers, ALT_CLICK)) // alt and alt-gr (rightalt) if(LAZYACCESS(modifiers, RIGHT_CLICK)) @@ -381,6 +384,13 @@ return ..() +/mob/proc/CtrlMiddleClickOn(atom/A) + if(check_rights_for(client, R_ADMIN)) + client.toggle_tag_datum(A) + else + A.CtrlClick(src) + return + /** * Alt click * Unused except for AI @@ -414,6 +424,9 @@ return FALSE if(SEND_SIGNAL(src, COMSIG_CLICK_ALT_SECONDARY, user) & COMPONENT_CANCEL_CLICK_ALT_SECONDARY) return + if(isobserver(user) && user.client && check_rights_for(user.client, R_DEBUG)) + user.client.toggle_tag_datum(src) + return /// Use this instead of [/mob/proc/AltClickOn] where you only want turf content listing without additional atom alt-click interaction /atom/proc/AltClickNoInteract(mob/user, atom/A) diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm index c54c130ee2d..005e869e32e 100644 --- a/code/_onclick/observer.dm +++ b/code/_onclick/observer.dm @@ -31,7 +31,10 @@ ShiftClickOn(A) return if(LAZYACCESS(modifiers, MIDDLE_CLICK)) - MiddleClickOn(A, params) + if(LAZYACCESS(modifiers, CTRL_CLICK)) + CtrlMiddleClickOn(A) + else + MiddleClickOn(A, params) return if(LAZYACCESS(modifiers, ALT_CLICK)) AltClickNoInteract(src, A) diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index 17f6581ab77..d35c562fc1f 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -27,6 +27,7 @@ VV_DROPDOWN_OPTION("", "---") VV_DROPDOWN_OPTION(VV_HK_CALLPROC, "Call Proc") VV_DROPDOWN_OPTION(VV_HK_MARK, "Mark Object") + VV_DROPDOWN_OPTION(VV_HK_TAG, "Tag Datum") VV_DROPDOWN_OPTION(VV_HK_DELETE, "Delete") VV_DROPDOWN_OPTION(VV_HK_EXPOSE, "Show VV To Player") VV_DROPDOWN_OPTION(VV_HK_ADDCOMPONENT, "Add Component/Element") diff --git a/code/datums/keybinding/admin.dm b/code/datums/keybinding/admin.dm index 10d9722eaa8..39fa27f40c3 100644 --- a/code/datums/keybinding/admin.dm +++ b/code/datums/keybinding/admin.dm @@ -130,3 +130,17 @@ return user.readmin() return TRUE + +/datum/keybinding/admin/view_tags + hotkey_keys = list("F9") + name = "view_tags" + full_name = "View Tags" + description = "Open the View-Tags menu" + keybind_signal = COMSIG_KB_ADMIN_VIEWTAGS_DOWN + +/datum/keybinding/admin/view_tags/down(client/user) + . = ..() + if(.) + return + user.holder?.display_tags() + return TRUE diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 529a45a32e5..27c746b392e 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -17,6 +17,7 @@ GLOBAL_PROTECT(admin_verbs_default) /client/proc/cmd_admin_pm_panel, /*admin-pm list*/ /client/proc/stop_sounds, /client/proc/mark_datum_mapview, + /client/proc/tag_datum_mapview, /client/proc/debugstatpanel, /client/proc/fix_air, /*resets air in designated radius to its default atmos composition*/ /client/proc/requests @@ -77,6 +78,7 @@ GLOBAL_PROTECT(admin_verbs_admin) /datum/admins/proc/view_all_sdql_spells, /datum/admins/proc/known_alts_panel, /datum/admins/proc/paintings_manager, + /datum/admins/proc/display_tags, ) GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/ban_panel, /client/proc/stickybanpanel)) GLOBAL_PROTECT(admin_verbs_ban) diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index d8a1bf0a334..a8172670a0a 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -39,6 +39,9 @@ GLOBAL_PROTECT(href_token) /// Whether or not this user can bypass 2FA var/bypass_2fa = FALSE + /// A lazylist of tagged datums, for quick reference with the View Tags verb + var/list/tagged_datums + /datum/admins/New(datum/admin_rank/R, ckey, force_active = FALSE, protected) if(IsAdminAdvancedProcCall()) var/msg = " has tried to elevate permissions!" diff --git a/code/modules/admin/tag.dm b/code/modules/admin/tag.dm new file mode 100644 index 00000000000..c0c22806636 --- /dev/null +++ b/code/modules/admin/tag.dm @@ -0,0 +1,108 @@ +/** + * Inserts the target_datum into [/datum/admins/var/tagged_datums], for later reference. + * + * Arguments: + * * target_datum - The datum you want to create a tag for + */ +/datum/admins/proc/add_tagged_datum(datum/target_datum) + if(LAZYFIND(tagged_datums, target_datum)) + to_chat(owner, span_warning("[target_datum] is already tagged!")) + return + + LAZYADD(tagged_datums, target_datum) + RegisterSignal(target_datum, COMSIG_PARENT_QDELETING, .proc/handle_tagged_del, override = TRUE) + to_chat(owner, span_notice("[target_datum] has been tagged.")) + +/// Get ahead of the curve with deleting +/datum/admins/proc/handle_tagged_del(datum/source) + SIGNAL_HANDLER + + if(owner) + to_chat(owner, span_boldnotice("Tagged datum [source] ([source.type]) has been deleted.")) + remove_tagged_datum(source, silent = TRUE) + +/** + * Attempts to remove the specified datum from [/datum/admins/var/tagged_datums] if it exists + * + * Arguments: + * * target_datum - The datum you want to remove from the tagged_datums list + * * silent - If TRUE, won't print messages to the owner's chat + */ +/datum/admins/proc/remove_tagged_datum(datum/target_datum, silent=FALSE) + if(!istype(target_datum)) + return + + if(LAZYFIND(tagged_datums, target_datum)) + LAZYREMOVE(tagged_datums, target_datum) + if(!silent) + to_chat(owner, span_notice("[target_datum] has been untagged.")) + else if(!silent) + to_chat(owner, span_warning("[target_datum] was not already tagged.")) + +/// Quick define for readability +#define TAG_DEL(X) "(UNTAG)" +#define TAG_MARK(X) "(MARK)" +#define TAG_SIMPLE_HEALTH(X) "Health: [X.health]" +#define TAG_CARBON_HEALTH(X) "Health: [X.health] (\ + [X.getBruteLoss()] \ + [X.getFireLoss()] \ + [X.getToxLoss()] \ + [X.getOxyLoss()]\ + [X.getCloneLoss() ? " [X.getCloneLoss()]" : ""])" + +/// Display all of the tagged datums +/datum/admins/proc/display_tags() + set category = "Admin.Game" + set name = "View Tags" + + if (!istype(src, /datum/admins)) + src = usr.client.holder + if (!istype(src, /datum/admins)) + to_chat(usr, "Error: you are not an admin!", confidential = TRUE) + return + + var/index = 0 + var/list/dat = list("
Tag Menu

") + + dat += "
Refresh
" + if(LAZYLEN(tagged_datums)) + for(var/datum/iter_datum as anything in tagged_datums) + index++ + var/specific_info + + if(isnull(iter_datum)) + dat += "\t[index]: Null reference - Check runtime logs!" + stack_trace("Null datum found in tagged datum menu! User: [usr]") + continue + else if(iscarbon(iter_datum)) + var/mob/living/carbon/resolved_carbon = iter_datum + specific_info = "[TAG_CARBON_HEALTH(resolved_carbon)] | [AREACOORD(resolved_carbon)] [ADMIN_PP(iter_datum)] [ADMIN_FLW(iter_datum)]" + else if(isliving(iter_datum)) + var/mob/living/resolved_living = iter_datum + specific_info = "[TAG_SIMPLE_HEALTH(resolved_living)] | [AREACOORD(resolved_living)] [ADMIN_PP(iter_datum)] [ADMIN_FLW(iter_datum)]" + else if(ismob(iter_datum)) + var/atom/resolved_atom = iter_datum // needed for ADMIN_JMP + specific_info = "[AREACOORD(resolved_atom)] [ADMIN_PP(iter_datum)] [ADMIN_FLW(iter_datum)]" + else if(ismovable(iter_datum)) + var/atom/resolved_atom = iter_datum // needed for ADMIN_JMP + specific_info = "[AREACOORD(resolved_atom)] [ADMIN_FLW(iter_datum)]" + else if(isatom(iter_datum)) + var/atom/resolved_atom = iter_datum // needed for ADMIN_JMP + specific_info = "[AREACOORD(resolved_atom)] [ADMIN_JMP(resolved_atom)]" + else if(istype(iter_datum, /datum/controller/subsystem)) + var/datum/controller/subsystem/resolved_subsystem = iter_datum + specific_info = "[resolved_subsystem.stat_entry()]" + // else, it's just a /datum + + dat += "\t[index]: [iter_datum] | [specific_info] | [ADMIN_VV(iter_datum)]| [TAG_DEL(iter_datum)] | [iter_datum == marked_datum ? "Marked" : TAG_MARK(iter_datum)] " + dat += "\t([iter_datum.type])" + else + dat += "No datums tagged :(" + + dat = dat.Join("
") + usr << browse(dat, "window=tag;size=800x480") + +#undef TAG_DEL +#undef TAG_MARK +#undef TAG_SIMPLE_HEALTH +#undef TAG_CARBON_HEALTH diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index f309aac633c..9be48936be5 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1926,3 +1926,24 @@ if(!check_rights(R_ADMIN)) return GLOB.interviews.ui_interact(usr) + + else if(href_list["del_tag"]) + if(!check_rights(R_ADMIN)) + return + var/datum/datum_to_remove = locate(href_list["del_tag"]) + if(!datum_to_remove) + return + return remove_tagged_datum(datum_to_remove) + + else if(href_list["show_tags"]) + if(!check_rights(R_ADMIN)) + return + return display_tags() + + else if(href_list["mark_datum"]) + if(!check_rights(R_ADMIN)) + return + var/datum/datum_to_mark = locate(href_list["mark_datum"]) + if(!datum_to_mark) + return + return usr.client?.mark_datum(datum_to_mark) diff --git a/code/modules/admin/view_variables/get_variables.dm b/code/modules/admin/view_variables/get_variables.dm index 2e9c0c75f03..9dd0083898a 100644 --- a/code/modules/admin/view_variables/get_variables.dm +++ b/code/modules/admin/view_variables/get_variables.dm @@ -85,6 +85,15 @@ markstring = "[VV_MARKED_DATUM] (CURRENT: [(istype(holder) && istype(holder.marked_datum))? holder.marked_datum.type : "NULL"])" classes += markstring + var/list/tagstrings = new + if(!(VV_TAGGED_DATUM in restricted_classes) && holder && LAZYLEN(holder.tagged_datums)) + var/i = 0 + for(var/datum/iter_tagged_datum as anything in holder.tagged_datums) + i++ + var/new_tagstring = "[VV_TAGGED_DATUM] #[i]: [iter_tagged_datum.type])" + tagstrings[new_tagstring] = iter_tagged_datum + classes += new_tagstring + if(restricted_classes) classes -= restricted_classes @@ -95,6 +104,12 @@ if(holder && holder.marked_datum && .["class"] == markstring) .["class"] = VV_MARKED_DATUM + if(holder && tagstrings[.["class"]]) + var/datum/chosen_datum = tagstrings[.["class"]] + .["value"] = chosen_datum + .["class"] = VV_TAGGED_DATUM + + switch(.["class"]) if(VV_TEXT) .["value"] = input("Enter new text:", "Text", current_value) as null|text @@ -210,6 +225,11 @@ .["class"] = null return + if(VV_TAGGED_DATUM) + if(.["value"] == null) + .["class"] = null + return + if(VV_PROCCALL_RETVAL) var/list/get_retval = list() callproc_blocking(get_retval) diff --git a/code/modules/admin/view_variables/tag_datum.dm b/code/modules/admin/view_variables/tag_datum.dm new file mode 100644 index 00000000000..3b611e3cdf9 --- /dev/null +++ b/code/modules/admin/view_variables/tag_datum.dm @@ -0,0 +1,18 @@ +/client/proc/tag_datum(datum/target_datum) + if(!holder || QDELETED(target_datum)) + return + holder.add_tagged_datum(target_datum) + +/client/proc/toggle_tag_datum(datum/target_datum) + if(!holder || !target_datum) + return + + if(LAZYFIND(holder.tagged_datums, target_datum)) + holder.remove_tagged_datum(target_datum) + else + holder.add_tagged_datum(target_datum) + +/client/proc/tag_datum_mapview(datum/target_datum as mob|obj|turf|area in view(view)) + set category = "Debug" + set name = "Tag Datum" + tag_datum(target_datum) diff --git a/code/modules/admin/view_variables/topic_basic.dm b/code/modules/admin/view_variables/topic_basic.dm index 141310f9fd3..c5a68dc0eeb 100644 --- a/code/modules/admin/view_variables/topic_basic.dm +++ b/code/modules/admin/view_variables/topic_basic.dm @@ -50,6 +50,8 @@ if(href_list[VV_HK_MARK]) usr.client.mark_datum(target) + if(href_list[VV_HK_TAG]) + usr.client.tag_datum(target) if(href_list[VV_HK_ADDCOMPONENT]) if(!check_rights(NONE)) return diff --git a/tgstation.dme b/tgstation.dme index 8c34dda1472..bf24cf2a4f8 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1752,6 +1752,7 @@ #include "code\modules\admin\sql_ban_system.dm" #include "code\modules\admin\sql_message_system.dm" #include "code\modules\admin\stickyban.dm" +#include "code\modules\admin\tag.dm" #include "code\modules\admin\team_panel.dm" #include "code\modules\admin\topic.dm" #include "code\modules\admin\whitelist.dm" @@ -1840,6 +1841,7 @@ #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\reference_tracking.dm" +#include "code\modules\admin\view_variables\tag_datum.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"