From 76310c6448af1f390cceaade2dc2999d2c6254ed Mon Sep 17 00:00:00 2001
From: CHOMPStation2StaffMirrorBot
<94713762+CHOMPStation2StaffMirrorBot@users.noreply.github.com>
Date: Sat, 5 Jul 2025 19:08:25 -0700
Subject: [PATCH] [MIRROR] View Variables Update (2) (#11149)
Co-authored-by: Selis <12716288+ItsSelis@users.noreply.github.com>
Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
---
code/__defines/_helpers.dm | 12 +-
code/__defines/_lists.dm | 5 -
code/__defines/admin.dm | 51 +-
code/__defines/admin_vr.dm | 24 -
.../dcs/{flags.dm => declarations.dm} | 4 +-
code/__defines/dcs/signals.dm | 22 -
.../signals/signals_atom/signals_atom_main.dm | 6 +
code/__defines/dcs/signals/signals_datum.dm | 32 +
.../signals/signals_datum/signals_datum.dm | 2 -
.../signals_mob_main.dm | 0
code/__defines/generators.dm | 13 +
code/__defines/is_helpers.dm | 6 +
code/__defines/matrices.dm | 39 ++
code/__defines/span.dm | 10 +
code/__defines/tgui.dm | 8 +
code/__defines/vv.dm | 116 ++-
code/_global_vars/__unsorted.dm | 2 -
code/_global_vars/admin.dm | 15 +
code/_global_vars/colorvars.dm | 1 +
code/_helpers/_lists.dm | 77 +-
code/_helpers/datums.dm | 5 +
.../{visual_filters.dm => filters.dm} | 132 ++--
code/_helpers/generators.dm | 11 +
code/_helpers/icons.dm | 55 ++
code/_helpers/matrices.dm | 7 +
code/_helpers/type2type.dm | 4 +-
code/controllers/hooks-defs.dm | 7 -
code/controllers/subsystems/overlays.dm | 97 +++
code/controllers/subsystems/statpanel.dm | 2 +-
code/datums/browser.dm | 8 +-
code/datums/datum.dm | 165 +++++
code/datums/datumvars.dm | 43 +-
code/game/atom/atom_vv.dm | 253 +++++++
code/game/atoms.dm | 74 --
code/game/atoms_movable.dm | 53 +-
code/game/atoms_movable_vr.dm | 12 -
code/game/mecha/medical/odysseus.dm | 4 +-
code/game/objects/objs.dm | 52 ++
code/game/world.dm | 170 +----
code/matrices/color_matrix.dm | 18 +-
code/modules/admin/admin.dm | 207 +++---
code/modules/admin/admin_verb_lists_vr.dm | 24 -
code/modules/admin/admin_verbs.dm | 87 ++-
code/modules/admin/callproc/callproc.dm | 275 +++++---
code/modules/admin/holder2.dm | 20 +-
code/modules/admin/modify_robot.dm | 2 +-
code/modules/admin/permissionedit.dm | 6 +-
code/modules/admin/player_effects.dm | 4 +-
code/modules/admin/tag.dm | 102 +++
code/modules/admin/topic.dm | 6 +-
code/modules/admin/verbs/adminfun.dm | 75 ++
code/modules/admin/verbs/change_appearance.dm | 2 +-
code/modules/admin/verbs/debug.dm | 27 +-
code/modules/admin/verbs/entity_narrate.dm | 2 +-
code/modules/admin/verbs/fps.dm | 2 +-
code/modules/admin/verbs/mapping.dm | 1 -
code/modules/admin/verbs/randomverbs.dm | 114 +--
code/modules/admin/verbs/secrets.dm | 4 +-
.../admin/view_variables/admin_delete.dm | 26 +-
.../view_variables/color_matrix_editor.dm | 85 +++
.../debug_variable_appearance.dm | 79 +++
.../admin/view_variables/debug_variables.dm | 149 ++--
.../admin/view_variables/filterrific.dm | 97 +++
.../admin/view_variables/get_variables.dm | 239 +++++--
code/modules/admin/view_variables/helpers.dm | 199 ------
.../admin/view_variables/mark_datum.dm | 17 +
.../view_variables/mass_edit_variables.dm | 104 +--
.../admin/view_variables/modify_variables.dm | 143 ++--
.../nobody_wants_to_learn_matrix_math.dm | 80 +++
.../admin/view_variables/particle_editor.dm | 212 ++++++
.../view_variables/reference_tracking.dm} | 7 +-
.../modules/admin/view_variables/tag_datum.dm | 16 +
code/modules/admin/view_variables/topic.dm | 662 +++---------------
.../admin/view_variables/topic_basic.dm | 145 ++++
.../admin/view_variables/topic_list.dm | 81 ++-
.../admin/view_variables/view_variables.dm | 537 +++++++-------
.../view_variables/view_variables_global.dm | 86 ---
.../asset_cache/assets/particle_editor.dm | 17 +
code/modules/client/client procs.dm | 11 +
.../clothing/spacesuits/void/zaddat.dm | 4 +-
.../eventkit/gm_interfaces/mob_spawner.dm | 2 +-
code/modules/mob/living/carbon/carbon.dm | 115 +++
code/modules/mob/living/carbon/human/human.dm | 146 ++++
code/modules/mob/living/living.dm | 20 +-
.../silicon/robot/subtypes/boozeborg.dm | 2 +-
code/modules/mob/mob.dm | 106 +++
code/modules/mob/new_player/login.dm | 5 +-
code/modules/rogueminer_vr/zonemaster.dm | 2 +
.../tgui/modules/admin/player_notes.dm | 4 +-
.../tgui/modules/admin_shuttle_controller.dm | 2 +-
code/modules/tgui/modules/law_manager.dm | 2 +-
code/modules/tgui/modules/overmap.dm | 2 +-
code/modules/tgui/states/admin.dm | 22 +-
code/modules/tickets/tickets.dm | 22 +-
code/modules/tickets/tickets_player_ui.dm | 2 +-
code/modules/webhooks/webhook_roundend.dm | 2 +-
html/admin/view_variables.css | 177 ++++-
html/browser/common.css | 25 +
icons/ui/particle_editor/box_gen.png | Bin 0 -> 13357 bytes
icons/ui/particle_editor/circle_gen.png | Bin 0 -> 19186 bytes
icons/ui/particle_editor/cube_gen.png | Bin 0 -> 19984 bytes
icons/ui/particle_editor/linear_rand.png | Bin 0 -> 3125 bytes
icons/ui/particle_editor/motion.png | Bin 0 -> 38493 bytes
icons/ui/particle_editor/normal_rand.png | Bin 0 -> 11397 bytes
icons/ui/particle_editor/num_gen.png | Bin 0 -> 13052 bytes
icons/ui/particle_editor/sphere_gen.png | Bin 0 -> 15528 bytes
icons/ui/particle_editor/square_gen.png | Bin 0 -> 14031 bytes
icons/ui/particle_editor/square_rand.png | Bin 0 -> 10478 bytes
icons/ui/particle_editor/uniform_rand.png | Bin 0 -> 7085 bytes
icons/ui/particle_editor/vector_gen.png | Bin 0 -> 11676 bytes
interface/stylesheet.dm | 6 +
.../tgui-panel/styles/tgchat/chat-dark.scss | 20 +
.../tgui-panel/styles/tgchat/chat-light.scss | 20 +
.../styles/tgchat/chat-vchatdark.scss | 20 +
.../styles/tgchat/chat-vchatlight.scss | 20 +
.../tgui/interfaces/ColorMatrixEditor.tsx | 89 +++
.../interfaces/Filteriffic/FilterEntry.tsx | 84 +++
.../FilterTypes/FilterColorEntry.tsx | 34 +
.../FilterTypes/FilterDataEntry.tsx | 58 ++
.../FilterTypes/FilterEnumEntry.tsx | 27 +
.../FilterTypes/FilterFlagsEntry.tsx | 28 +
.../FilterTypes/FilterFloatEntry.tsx | 47 ++
.../FilterTypes/FilterIconEntry.tsx | 24 +
.../FilterTypes/FilterIntegerEntry.tsx | 27 +
.../FilterTypes/FilterTextEntry.tsx | 24 +
.../FilterTypes/FilterTransformEntry.tsx | 33 +
.../tgui/interfaces/Filteriffic/index.tsx | 90 +++
.../tgui/interfaces/Filteriffic/types.ts | 164 +++++
.../tgui/interfaces/MatrixMathTester.tsx | 251 +++++++
.../interfaces/ParticleEdit/EntriesBasic.tsx | 456 ++++++++++++
.../ParticleEdit/EntriesGenerators.tsx | 255 +++++++
.../interfaces/ParticleEdit/Generators.tsx | 228 ++++++
.../tgui/interfaces/ParticleEdit/Tutorial.tsx | 434 ++++++++++++
.../tgui/interfaces/ParticleEdit/data.ts | 146 ++++
.../tgui/interfaces/ParticleEdit/helpers.ts | 55 ++
.../tgui/interfaces/ParticleEdit/index.tsx | 216 ++++++
vorestation.dme | 32 +-
137 files changed, 6803 insertions(+), 2316 deletions(-)
delete mode 100644 code/__defines/admin_vr.dm
rename code/__defines/dcs/{flags.dm => declarations.dm} (91%)
create mode 100644 code/__defines/dcs/signals/signals_atom/signals_atom_main.dm
create mode 100644 code/__defines/dcs/signals/signals_datum.dm
delete mode 100644 code/__defines/dcs/signals/signals_datum/signals_datum.dm
rename code/__defines/dcs/signals/{signals_mobs => signals_mob}/signals_mob_main.dm (100%)
create mode 100644 code/__defines/generators.dm
create mode 100644 code/__defines/matrices.dm
create mode 100644 code/_global_vars/admin.dm
create mode 100644 code/_global_vars/colorvars.dm
create mode 100644 code/_helpers/datums.dm
rename code/_helpers/{visual_filters.dm => filters.dm} (77%)
create mode 100644 code/_helpers/generators.dm
create mode 100644 code/game/atom/atom_vv.dm
delete mode 100644 code/game/atoms_movable_vr.dm
create mode 100644 code/modules/admin/tag.dm
create mode 100644 code/modules/admin/verbs/adminfun.dm
create mode 100644 code/modules/admin/view_variables/color_matrix_editor.dm
create mode 100644 code/modules/admin/view_variables/debug_variable_appearance.dm
create mode 100644 code/modules/admin/view_variables/filterrific.dm
delete mode 100644 code/modules/admin/view_variables/helpers.dm
create mode 100644 code/modules/admin/view_variables/mark_datum.dm
create mode 100644 code/modules/admin/view_variables/nobody_wants_to_learn_matrix_math.dm
create mode 100644 code/modules/admin/view_variables/particle_editor.dm
rename code/{datums/reference_tracking_new.dm => modules/admin/view_variables/reference_tracking.dm} (97%)
create mode 100644 code/modules/admin/view_variables/tag_datum.dm
create mode 100644 code/modules/admin/view_variables/topic_basic.dm
delete mode 100644 code/modules/admin/view_variables/view_variables_global.dm
create mode 100644 code/modules/asset_cache/assets/particle_editor.dm
create mode 100644 icons/ui/particle_editor/box_gen.png
create mode 100644 icons/ui/particle_editor/circle_gen.png
create mode 100644 icons/ui/particle_editor/cube_gen.png
create mode 100644 icons/ui/particle_editor/linear_rand.png
create mode 100644 icons/ui/particle_editor/motion.png
create mode 100644 icons/ui/particle_editor/normal_rand.png
create mode 100644 icons/ui/particle_editor/num_gen.png
create mode 100644 icons/ui/particle_editor/sphere_gen.png
create mode 100644 icons/ui/particle_editor/square_gen.png
create mode 100644 icons/ui/particle_editor/square_rand.png
create mode 100644 icons/ui/particle_editor/uniform_rand.png
create mode 100644 icons/ui/particle_editor/vector_gen.png
create mode 100644 tgui/packages/tgui/interfaces/ColorMatrixEditor.tsx
create mode 100644 tgui/packages/tgui/interfaces/Filteriffic/FilterEntry.tsx
create mode 100644 tgui/packages/tgui/interfaces/Filteriffic/FilterTypes/FilterColorEntry.tsx
create mode 100644 tgui/packages/tgui/interfaces/Filteriffic/FilterTypes/FilterDataEntry.tsx
create mode 100644 tgui/packages/tgui/interfaces/Filteriffic/FilterTypes/FilterEnumEntry.tsx
create mode 100644 tgui/packages/tgui/interfaces/Filteriffic/FilterTypes/FilterFlagsEntry.tsx
create mode 100644 tgui/packages/tgui/interfaces/Filteriffic/FilterTypes/FilterFloatEntry.tsx
create mode 100644 tgui/packages/tgui/interfaces/Filteriffic/FilterTypes/FilterIconEntry.tsx
create mode 100644 tgui/packages/tgui/interfaces/Filteriffic/FilterTypes/FilterIntegerEntry.tsx
create mode 100644 tgui/packages/tgui/interfaces/Filteriffic/FilterTypes/FilterTextEntry.tsx
create mode 100644 tgui/packages/tgui/interfaces/Filteriffic/FilterTypes/FilterTransformEntry.tsx
create mode 100644 tgui/packages/tgui/interfaces/Filteriffic/index.tsx
create mode 100644 tgui/packages/tgui/interfaces/Filteriffic/types.ts
create mode 100644 tgui/packages/tgui/interfaces/MatrixMathTester.tsx
create mode 100644 tgui/packages/tgui/interfaces/ParticleEdit/EntriesBasic.tsx
create mode 100644 tgui/packages/tgui/interfaces/ParticleEdit/EntriesGenerators.tsx
create mode 100644 tgui/packages/tgui/interfaces/ParticleEdit/Generators.tsx
create mode 100644 tgui/packages/tgui/interfaces/ParticleEdit/Tutorial.tsx
create mode 100644 tgui/packages/tgui/interfaces/ParticleEdit/data.ts
create mode 100644 tgui/packages/tgui/interfaces/ParticleEdit/helpers.ts
create mode 100644 tgui/packages/tgui/interfaces/ParticleEdit/index.tsx
diff --git a/code/__defines/_helpers.dm b/code/__defines/_helpers.dm
index bb8c3b3c5a..b7cf727299 100644
--- a/code/__defines/_helpers.dm
+++ b/code/__defines/_helpers.dm
@@ -1,5 +1,3 @@
-/// Takes a datum as input, returns its ref string
-#define text_ref(datum) ref(datum)
// Stuff that is relatively "core" and is used in other defines/helpers
/**
@@ -15,4 +13,14 @@
/// The Y/Height dimension of ICON_SIZE. This will more than likely be the smaller axis.
#define ICON_SIZE_Y 32
+/// Takes a datum as input, returns its ref string
+#define text_ref(datum) ref(datum)
+
+// Refs contain a type id within their string that can be used to identify byond types.
+// Custom types that we define don't get a unique id, but this is useful for identifying
+// types that don't normally have a way to run istype() on them.
+#define TYPEID(thing) copytext(REF(thing), 4, 6)
+
+/// A null statement to guard against EmptyBlock lint without necessitating the use of pass()
+/// Used to avoid proc-call overhead. But use sparingly. Probably pointless in most places.
#define EMPTY_BLOCK_GUARD ;
diff --git a/code/__defines/_lists.dm b/code/__defines/_lists.dm
index f27069902a..2533653138 100644
--- a/code/__defines/_lists.dm
+++ b/code/__defines/_lists.dm
@@ -4,9 +4,6 @@
///Picks from the list, with some safeties, and returns the "default" arg if it fails
#define DEFAULTPICK(L, default) ((islist(L) && length(L)) ? pick(L) : default)
-// Ensures L is initailized after this point
-#define LAZYINITLIST(L) if (!L) { L = list(); }
-
// Sets a L back to null iff it is empty
#define UNSETEMPTY(L) if (L && !length(L)) L = null
@@ -21,8 +18,6 @@
// Adds I to L, initalizing L if necessary, if I is not already in L
#define LAZYDISTINCTADD(L, I) if(!L) { L = list(); } L |= I;
-#define LAZYFIND(L, V) (L ? L.Find(V) : 0)
-
// Reads I from L safely - Works with both associative and traditional lists.
#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= length(L) ? L[I] : null) : L[I]) : null)
diff --git a/code/__defines/admin.dm b/code/__defines/admin.dm
index cdf3cc1b60..1b8faa64f8 100644
--- a/code/__defines/admin.dm
+++ b/code/__defines/admin.dm
@@ -53,6 +53,31 @@
#define SMITE_SPONTANEOUSCOMBUSTION "Spontaneous Combustion"
#define SMITE_LIGHTNINGBOLT "Lightning Bolt"
#define SMITE_TERROR "Terrify"
+#define SMITE_SHADEKIN_ATTACK "Shadekin (Attack)"
+#define SMITE_SHADEKIN_NOMF "Shadekin (Devour)"
+#define SMITE_REDSPACE_ABDUCT "Redspace Abduction"
+#define SMITE_AD_SPAM "Ad Spam"
+#define SMITE_AUTOSAVE "10 Second Autosave"
+#define SMITE_AUTOSAVE_WIDE "10 Second Autosave (AoE)"
+#define SMITE_SPICEREQUEST "Give Them Spice (Harmless)"
+#define SMITE_PEPPERNADE "Give Them Spice (Extra Spicy)"
+
+#define MODIFIY_ROBOT_MODULE_ADD "Add a Module"
+#define MODIFIY_ROBOT_MODULE_REMOVE "Remove a Module"
+#define MODIFIY_ROBOT_APPLY_UPGRADE "Apply an Upgrade"
+#define MODIFIY_ROBOT_SUPP_ADD "Add Upgrade Support"
+#define MODIFIY_ROBOT_SUPP_REMOVE "Remove Upgrade Support"
+#define MODIFIY_ROBOT_RADIOC_ADD "Add a Radio Channel"
+#define MODIFIY_ROBOT_RADIOC_REMOVE "Remove a Radio Channel"
+#define MODIFIY_ROBOT_COMP_ADD "Replace a Component"
+#define MODIFIY_ROBOT_COMP_REMOVE "Remove a Component"
+#define MODIFIY_ROBOT_SWAP_MODULE "Swap a Robot Module"
+#define MODIFIY_ROBOT_RESET_MODULE "Fully Reset Robot Module"
+#define MODIFIY_ROBOT_TOGGLE_ERT "Toggle ERT Module Overwrite"
+#define MODIFIY_ROBOT_LIMIT_MODULES_ADD "Restrict Modules to"
+#define MODIFIY_ROBOT_LIMIT_MODULES_REMOVE "Remove from restricted Modules"
+#define MODIFIY_ROBOT_TOGGLE_STATION_ACCESS "Toggle All Station Access Codes"
+#define MODIFIY_ROBOT_TOGGLE_CENT_ACCESS "Toggle Central Access Codes"
#define ADMIN_QUE(user) "(? )"
#define ADMIN_FLW(user) "(FLW )"
@@ -70,11 +95,31 @@
#define ADMIN_LOOKUPFLW(user) "[key_name_admin(user)][ADMIN_QUE(user)] [ADMIN_FLW(user)]"
#define ADMIN_FULLMONTY_NONAME(user) "[ADMIN_QUE(user)] [ADMIN_PP(user)] [ADMIN_VV(user)] [ADMIN_SM(user)] [ADMIN_FLW(user)] [ADMIN_TP(user)]"
#define ADMIN_FULLMONTY(user) "[key_name_admin(user)] [ADMIN_FULLMONTY_NONAME(user)]"
-#define ADMIN_JMP(src) "(JMP )"
-#define COORD(src) "[src ? "([src.x],[src.y],[src.z])" : "nonexistent location"]"
-#define ADMIN_COORDJMP(src) "[src ? "[COORD(src)] [ADMIN_JMP(src)]" : "nonexistent location"]"
+#define ADMIN_JMP(src) "(JMP )"
+#define COORD(src) "[src ? src.Admin_Coordinates_Readable() : "nonexistent location"]"
+#define AREACOORD(src) "[src ? src.Admin_Coordinates_Readable(TRUE) : "nonexistent location"]"
+#define ADMIN_COORDJMP(src) "[src ? src.Admin_Coordinates_Readable(FALSE, TRUE) : "nonexistent location"]"
+#define ADMIN_VERBOSEJMP(src) "[src ? src.Admin_Coordinates_Readable(TRUE, TRUE) : "nonexistent location"]"
#define ADMIN_CA(user) "(? )"
+/atom/proc/Admin_Coordinates_Readable(area_name, admin_jump_ref)
+ var/turf/turf_at_coords = Safe_COORD_Location()
+ return turf_at_coords ? "[area_name ? "[get_area_name(turf_at_coords, TRUE)] " : " "]([turf_at_coords.x],[turf_at_coords.y],[turf_at_coords.z])[admin_jump_ref ? " [ADMIN_JMP(turf_at_coords)]" : ""]" : "nonexistent location"
+
+/atom/proc/Safe_COORD_Location()
+ var/atom/drop_atom = drop_location()
+ if(!drop_atom)
+ return //not a valid atom.
+ var/turf/drop_turf = get_step(drop_atom, 0) //resolve where the thing is.
+ if(!drop_turf) //incase it's inside a valid drop container, inside another container. ie if a mech picked up a closet and has it inside its internal storage.
+ var/atom/last_try = drop_atom.loc?.drop_location() //one last try, otherwise fuck it.
+ if(last_try)
+ drop_turf = get_step(last_try, 0)
+ return drop_turf
+
+/turf/Safe_COORD_Location()
+ return src
+
#define AHELP_ACTIVE 1
#define AHELP_CLOSED 2
#define AHELP_RESOLVED 3
diff --git a/code/__defines/admin_vr.dm b/code/__defines/admin_vr.dm
deleted file mode 100644
index b4893b9434..0000000000
--- a/code/__defines/admin_vr.dm
+++ /dev/null
@@ -1,24 +0,0 @@
-#define SMITE_SHADEKIN_ATTACK "Shadekin (Attack)"
-#define SMITE_SHADEKIN_NOMF "Shadekin (Devour)"
-#define SMITE_REDSPACE_ABDUCT "Redspace Abduction"
-#define SMITE_AD_SPAM "Ad Spam"
-#define SMITE_AUTOSAVE "10 Second Autosave"
-#define SMITE_AUTOSAVE_WIDE "10 Second Autosave (AoE)"
-#define SMITE_SPICEREQUEST "Give Them Spice (Harmless)"
-#define SMITE_PEPPERNADE "Give Them Spice (Extra Spicy)"
-#define MODIFIY_ROBOT_MODULE_ADD "Add a Module"
-#define MODIFIY_ROBOT_MODULE_REMOVE "Remove a Module"
-#define MODIFIY_ROBOT_APPLY_UPGRADE "Apply an Upgrade"
-#define MODIFIY_ROBOT_SUPP_ADD "Add Upgrade Support"
-#define MODIFIY_ROBOT_SUPP_REMOVE "Remove Upgrade Support"
-#define MODIFIY_ROBOT_RADIOC_ADD "Add a Radio Channel"
-#define MODIFIY_ROBOT_RADIOC_REMOVE "Remove a Radio Channel"
-#define MODIFIY_ROBOT_COMP_ADD "Replace a Component"
-#define MODIFIY_ROBOT_COMP_REMOVE "Remove a Component"
-#define MODIFIY_ROBOT_SWAP_MODULE "Swap a Robot Module"
-#define MODIFIY_ROBOT_RESET_MODULE "Fully Reset Robot Module"
-#define MODIFIY_ROBOT_TOGGLE_ERT "Toggle ERT Module Overwrite"
-#define MODIFIY_ROBOT_LIMIT_MODULES_ADD "Restrict Modules to"
-#define MODIFIY_ROBOT_LIMIT_MODULES_REMOVE "Remove from restricted Modules"
-#define MODIFIY_ROBOT_TOGGLE_STATION_ACCESS "Toggle All Station Access Codes"
-#define MODIFIY_ROBOT_TOGGLE_CENT_ACCESS "Toggle Central Access Codes"
diff --git a/code/__defines/dcs/flags.dm b/code/__defines/dcs/declarations.dm
similarity index 91%
rename from code/__defines/dcs/flags.dm
rename to code/__defines/dcs/declarations.dm
index b69d74571c..5a529b1ad2 100644
--- a/code/__defines/dcs/flags.dm
+++ b/code/__defines/dcs/declarations.dm
@@ -4,6 +4,8 @@
#define COMPONENT_INCOMPATIBLE 1
/// Returned in PostTransfer to prevent transfer, similar to `COMPONENT_INCOMPATIBLE`
#define COMPONENT_NOTRANSFER 2
+/// Deletes the component silently. This is for valid, non-error cases where you still want to execute some of the component's logic.
+#define COMPONENT_REDUNDANT 3
/// Return value to cancel attaching
#define ELEMENT_INCOMPATIBLE 1
@@ -30,7 +32,7 @@
#define ELEMENT_DONT_SORT_LIST_ARGS (1<<3)
/**
* Elements with this flag will be ignored by the dcs_check_list_arguments test.
- * A good example is connect_loc, for which it's pratically undoable unless we force every signal proc to have a different name.
+ * A good example is connect_loc, for which it's practically undoable unless we force every signal proc to have a different name.
*/
#define ELEMENT_NO_LIST_UNIT_TEST (1<<4)
diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm
index 0bfc1855b8..015727c1a8 100644
--- a/code/__defines/dcs/signals.dm
+++ b/code/__defines/dcs/signals.dm
@@ -34,28 +34,6 @@
/// Signal that gets sent when a ghost query is completed
#define COMSIG_GHOST_QUERY_COMPLETE "ghost_query_complete"
-// /datum signals
-/// when a component is added to a datum: (/datum/component)
-#define COMSIG_COMPONENT_ADDED "component_added"
-/// before a component is removed from a datum because of RemoveComponent: (/datum/component)
-#define COMSIG_COMPONENT_REMOVING "component_removing"
-/// before a datum's Destroy() is called: (force), returning a nonzero value will cancel the qdel operation
-#define COMSIG_PARENT_PREQDELETED "parent_preqdeleted"
-/// just before a datum's Destroy() is called: (force), at this point none of the other components chose to interrupt qdel and Destroy will be called
-#define COMSIG_PARENT_QDELETING "parent_qdeleting"
-/// generic topic handler (usr, href_list)
-#define COMSIG_TOPIC "handle_topic"
-/// from datum tgui_act (usr, action)
-#define COMSIG_UI_ACT "COMSIG_UI_ACT"
-/// from datum tgui_fallback (payload)
-#define COMSIG_UI_FALLBACK "COMSIG_UI_FALLBACK"
-
-
-/// fires on the target datum when an element is attached to it (/datum/element)
-#define COMSIG_ELEMENT_ATTACH "element_attach"
-/// fires on the target datum when an element is attached to it (/datum/element)
-#define COMSIG_ELEMENT_DETACH "element_detach"
-
// /atom signals
///from base of atom/proc/Initialize(): sent any time a new atom is created
#define COMSIG_ATOM_CREATED "atom_created"
diff --git a/code/__defines/dcs/signals/signals_atom/signals_atom_main.dm b/code/__defines/dcs/signals/signals_atom/signals_atom_main.dm
new file mode 100644
index 0000000000..06d8b734a4
--- /dev/null
+++ b/code/__defines/dcs/signals/signals_atom/signals_atom_main.dm
@@ -0,0 +1,6 @@
+// Main atom signals. Format:
+// When the signal is called: (signal arguments)
+// All signals send the source datum of the signal as the first argument
+
+///When the transform or an atom is varedited through vv topic.
+#define COMSIG_ATOM_VV_MODIFY_TRANSFORM "atom_vv_modify_transform"
diff --git a/code/__defines/dcs/signals/signals_datum.dm b/code/__defines/dcs/signals/signals_datum.dm
new file mode 100644
index 0000000000..eb7a00aac5
--- /dev/null
+++ b/code/__defines/dcs/signals/signals_datum.dm
@@ -0,0 +1,32 @@
+// Datum signals. Format:
+// When the signal is called: (signal arguments)
+// All signals send the source datum of the signal as the first argument
+
+// /datum signals
+/// when a component is added to a datum: (/datum/component)
+#define COMSIG_COMPONENT_ADDED "component_added"
+/// before a component is removed from a datum because of ClearFromParent: (/datum/component)
+#define COMSIG_COMPONENT_REMOVING "component_removing"
+/// before a datum's Destroy() is called: (force), returning a nonzero value will cancel the qdel operation
+#define COMSIG_PARENT_PREQDELETED "parent_preqdeleted"
+/// just before a datum's Destroy() is called: (force), at this point none of the other components chose to interrupt qdel and Destroy will be called
+#define COMSIG_PARENT_QDELETING "parent_qdeleting"
+/// Called whenever an admin manually deletes an object, via the "Delete" verb, before qdel() is called: (client/deleting_admin)
+#define COMSIG_ADMIN_DELETING "parent_admin_deleting"
+/// generic topic handler (usr, href_list)
+#define COMSIG_TOPIC "handle_topic"
+/// handler for vv_do_topic (usr, href_list)
+#define COMSIG_VV_TOPIC "vv_topic"
+ #define COMPONENT_VV_HANDLED (1<<0)
+/// from datum ui_act (usr, action)
+#define COMSIG_UI_ACT "COMSIG_UI_ACT"
+/// from datum tgui_fallback (payload)
+#define COMSIG_UI_FALLBACK "COMSIG_UI_FALLBACK"
+
+/// fires on the target datum when an element is attached to it (/datum/element)
+#define COMSIG_ELEMENT_ATTACH "element_attach"
+/// fires on the target datum when an element is detached from it (/datum/element)
+#define COMSIG_ELEMENT_DETACH "element_detach"
+
+///Called on an object to "clean it", such as removing blood decals/overlays, etc. The clean types bitfield is sent with it. Return TRUE if any cleaning was necessary and thus performed.
+#define COMSIG_COMPONENT_CLEAN_ACT "clean_act"
diff --git a/code/__defines/dcs/signals/signals_datum/signals_datum.dm b/code/__defines/dcs/signals/signals_datum/signals_datum.dm
deleted file mode 100644
index 0215cb7cc4..0000000000
--- a/code/__defines/dcs/signals/signals_datum/signals_datum.dm
+++ /dev/null
@@ -1,2 +0,0 @@
-///Called on an object to "clean it", such as removing blood decals/overlays, etc. The clean types bitfield is sent with it. Return TRUE if any cleaning was necessary and thus performed.
-#define COMSIG_COMPONENT_CLEAN_ACT "clean_act"
diff --git a/code/__defines/dcs/signals/signals_mobs/signals_mob_main.dm b/code/__defines/dcs/signals/signals_mob/signals_mob_main.dm
similarity index 100%
rename from code/__defines/dcs/signals/signals_mobs/signals_mob_main.dm
rename to code/__defines/dcs/signals/signals_mob/signals_mob_main.dm
diff --git a/code/__defines/generators.dm b/code/__defines/generators.dm
new file mode 100644
index 0000000000..8a5c06a28c
--- /dev/null
+++ b/code/__defines/generators.dm
@@ -0,0 +1,13 @@
+//generator types
+#define GEN_NUM "num"
+#define GEN_VECTOR "vector"
+#define GEN_BOX "box"
+#define GEN_CIRCLE "circle"
+#define GEN_SPHERE "sphere"
+
+///particle editor var modifiers
+#define P_DATA_GENERATOR "generator"
+#define P_DATA_ICON_ADD "icon_add"
+#define P_DATA_ICON_REMOVE "icon_remove"
+#define P_DATA_ICON_WEIGHT "icon_edit"
+#define P_DATA_GRADIENT "gradient"
diff --git a/code/__defines/is_helpers.dm b/code/__defines/is_helpers.dm
index 1e353b786b..503e45bfd7 100644
--- a/code/__defines/is_helpers.dm
+++ b/code/__defines/is_helpers.dm
@@ -8,6 +8,12 @@
GLOBAL_VAR_INIT(magic_appearance_detecting_image, new /image) // appearances are awful to detect safely, but this seems to be the best way ~ninjanomnom
#define isappearance(thing) (!isimage(thing) && !ispath(thing) && istype(GLOB.magic_appearance_detecting_image, thing))
+// The filters list has the same ref type id as a filter, but isnt one and also isnt a list, so we have to check if the thing has Cut() instead
+GLOBAL_VAR_INIT(refid_filter, TYPEID(filter(type="angular_blur")))
+#define isfilter(thing) (!hascall(thing, "Cut") && TYPEID(thing) == GLOB.refid_filter)
+
+#define isgenerator(A) (istype(A, /generator))
+
//---------------
#define isatom(D) istype(D, /atom)
#define isclient(D) istype(D, /client)
diff --git a/code/__defines/matrices.dm b/code/__defines/matrices.dm
new file mode 100644
index 0000000000..a4410e135a
--- /dev/null
+++ b/code/__defines/matrices.dm
@@ -0,0 +1,39 @@
+/// Helper macro for creating a matrix at the given offsets.
+/// Works at compile time.
+#define TRANSLATE_MATRIX(offset_x, offset_y) matrix(1, 0, (offset_x), 0, 1, (offset_y))
+/// The color matrix of an image which colors haven't been altered. Does nothing.
+#define COLOR_MATRIX_IDENTITY list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0)
+/// Color inversion
+#define COLOR_MATRIX_INVERT list(-1,0,0,0, 0,-1,0,0, 0,0,-1,0, 0,0,0,1, 1,1,1,0)
+///Sepiatone
+#define COLOR_MATRIX_SEPIATONE list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0,0,0)
+///Grayscale
+#define COLOR_MATRIX_GRAYSCALE list(0.33,0.33,0.33,0, 0.59,0.59,0.59,0, 0.11,0.11,0.11,0, 0,0,0,1, 0,0,0,0)
+///Polaroid colors
+#define COLOR_MATRIX_POLAROID list(1.438,-0.062,-0.062,0, -0.122,1.378,-0.122,0, -0.016,-0.016,1.483,0, 0,0,0,1, 0,0,0,0)
+/// Converts reds to blue, green to red and blue to green.
+#define COLOR_MATRIX_BRG list(0,0,1,0, 0,1,0,0, 1,0,0,0, 0,0,0,1, 0,0,0,0)
+/// Black & White
+#define COLOR_MATRIX_BLACK_WHITE list(1.5,1.5,1.5,0, 1.5,1.5,1.5,0, 1.5,1.5,1.5,0, 0,0,0,1, -1,-1,-1,0)
+/**
+ * Adds/subtracts overall lightness
+ * 0 is identity, 1 makes everything white, -1 makes everything black
+ */
+#define COLOR_MATRIX_LIGHTNESS(power) list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, power,power,power,0)
+/**
+ * Changes distance colors have from rgb(127,127,127) grey
+ * 1 is identity. 0 makes everything grey >1 blows out colors and greys
+ */
+#define COLOR_MATRIX_CONTRAST(val) list(val,0,0,0, 0,val,0,0, 0,0,val,0, 0,0,0,1, (1-val)*0.5,(1-val)*0.5,(1-val)*0.5,0)
+
+/// Identity transform matrix (2d) with 6 values
+/// list(ax,by,c, dx,dy,f)
+#define TRANSFORM_MATRIX_IDENTITY list(1,0,0, 0,1,0)
+
+/// Identity transform matrix (xyz + translation) with 12 values
+/// list(xx,xy,xz, yx,yy,yz, zx,zy,zz, cx,cy,cz)
+#define TRANSFORM_COMPLEX_MATRIX_IDENTITY list(1,0,0, 0,1,0, 0,0,1, 0,0,0)
+
+/// Identity transform matrix (xyzw + projection) with 16 values exclusive to particles
+/// list(xx,xy,xz,xw, yx,yy,yz,yw, zx,zy,zz,zw, wx,wy,wz,ww)
+#define TRANSFORM_PROJECTION_MATRIX_IDENTITY list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1)
diff --git a/code/__defines/span.dm b/code/__defines/span.dm
index 430efef23e..1b41f3ca4d 100644
--- a/code/__defines/span.dm
+++ b/code/__defines/span.dm
@@ -256,3 +256,13 @@
// special spans
#define span_spoiler(str) ("" + str + " ")
+
+// damage type spans, mostly used for admin for now
+#define span_brute(str) ("" + str + " ")
+#define span_burn(str) ("" + str + " ")
+#define span_tox(str) ("" + str + " ")
+#define span_oxy(str) ("" + str + " ")
+#define span_clone(str) ("" + str + " ")
+
+// Admin VV
+#define span_value(str) ("" + str + " ")
diff --git a/code/__defines/tgui.dm b/code/__defines/tgui.dm
index 127f7859ec..ffb780f3c5 100644
--- a/code/__defines/tgui.dm
+++ b/code/__defines/tgui.dm
@@ -47,3 +47,11 @@
#define TGUI_MODAL_DELEGATE 2
#define TGUI_MODAL_ANSWER 3
#define TGUI_MODAL_CLOSE 4
+
+/**
+ * Gets a ui_state that checks to see if the user has specific admin permissions.
+ *
+ * Arguments:
+ * * required_perms: Which admin permission flags to check the user for, such as [R_ADMIN]
+ */
+#define ADMIN_STATE(required_perms) (GLOB.admin_states[required_perms] ||= new /datum/tgui_state/admin_state(required_perms))
diff --git a/code/__defines/vv.dm b/code/__defines/vv.dm
index 13b7f2eb98..63d10717d9 100644
--- a/code/__defines/vv.dm
+++ b/code/__defines/vv.dm
@@ -1,6 +1,8 @@
#define VV_NUM "Number"
#define VV_TEXT "Text"
-#define VV_MESSAGE "Mutiline Text"
+#define VV_MESSAGE "Multiline Text"
+#define VV_COLOR "Color"
+#define VV_COLOR_MATRIX "Color Matrix"
#define VV_ICON "Icon"
#define VV_ATOM_REFERENCE "Atom Reference"
#define VV_DATUM_REFERENCE "Datum Reference"
@@ -16,25 +18,50 @@
#define VV_NEW_TYPE "New Custom Typepath"
#define VV_NEW_LIST "New List"
#define VV_NULL "NULL"
+#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"
+#define VV_WEAKREF "Weak Reference Datum"
#define VV_MSG_MARKED " " + span_small(span_red(span_bold("Marked Object")))
+#define VV_MSG_TAGGED(num) " " + span_small(span_red(span_bold("Tagged Datum #[num]")))
#define VV_MSG_EDITED " " + span_small(span_red(span_bold("Var Edited")))
#define VV_MSG_DELETED " " + span_small(span_red(span_bold("Deleted")))
#define VV_NORMAL_LIST_NO_EXPAND_THRESHOLD 50
#define VV_SPECIAL_LIST_NO_EXPAND_THRESHOLD 150
-#define IS_VALID_ASSOC_KEY(V) (!isnum(V))
+//#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) "byond://?_src_=vars;[HrefToken()];[href_key]=TRUE;[VV_HK_TARGET]=[REF(target)]"
+#define VV_HREF_TARGETREF_INTERNAL(targetref, href_key) "byond://?_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) . += "[name] "
+#define VV_DROPDOWN_OPTION(href_key, name) . += "[name] "
//Helpers for vv_do_topic(list/href_list)
#define IF_VV_OPTION(href_key) if(href_list[href_key])
+// VV HREF KEYS
+#define VV_HK_TARGET "target"
+#define VV_HK_VARNAME "targetvar" //name or index of var for 1 variable targeting hrefs.
+
// vv_do_list() keys
#define VV_HK_LIST_ADD "listadd"
#define VV_HK_LIST_EDIT "listedit"
@@ -45,15 +72,98 @@
#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_TAG "tag"
#define VV_HK_ADDCOMPONENT "addcomponent"
+#define VV_HK_REMOVECOMPONENT "removecomponent"
+#define VV_HK_MASS_REMOVECOMPONENT "massremovecomponent"
+#define VV_HK_MODIFY_TRAITS "modtraits"
// /atom
+#define VV_HK_MODIFY_TRANSFORM "atom_transform"
+#define VV_HK_SPIN_ANIMATION "atom_spin"
+#define VV_HK_STOP_ALL_ANIMATIONS "stop_animations"
+#define VV_HK_MODIFY_GREYSCALE "modify_greyscale"
+#define VV_HK_ADD_REAGENT "addreagent"
+#define VV_HK_SHOW_HIDDENPRINTS "show_hiddenprints"
+#define VV_HK_TRIGGER_EMP "empulse"
+#define VV_HK_TRIGGER_EXPLOSION "explode"
+#define VV_HK_AUTO_RENAME "auto_rename"
+#define VV_HK_EDIT_FILTERS "edit_filters"
+#define VV_HK_EDIT_PARTICLES "edit_particles"
+#define VV_HK_EDIT_COLOR_MATRIX "edit_color_matrix"
+#define VV_HK_TEST_MATRIXES "test_matrixes"
+#define VV_HK_ADD_AI "add_ai"
+#define VV_HK_ARMOR_MOD "mod_obj_armor"
#define VV_HK_ATOM_EXPLODE "turf_explode"
#define VV_HK_ATOM_EMP "turf_emp"
+// /atom/movable
+#define VV_HK_OBSERVE_FOLLOW "observe_follow"
+#define VV_HK_GET_MOVABLE "get_movable"
+#define VV_HK_DEADCHAT_PLAYS "deadchat_plays"
+
+// /obj
+#define VV_HK_OSAY "osay"
+#define VV_HK_MASS_DEL_TYPE "mass_delete_type"
+
+// /mob
+#define VV_HK_GIB "gib"
+#define VV_HK_GIVE_MOB_ACTION "give_mob_action"
+#define VV_HK_REMOVE_MOB_ACTION "remove_mob_action"
+#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_REGEN_ICONS_FULL "regen_icons_full"
+#define VV_HK_PLAYER_PANEL "player_panel"
+#define VV_HK_BUILDMODE "buildmode"
+#define VV_HK_DIRECT_CONTROL "direct_control"
+#define VV_HK_GIVE_DIRECT_CONTROL "give_direct_control"
+#define VV_HK_OFFER_GHOSTS "offer_ghosts"
+#define VV_HK_VIEW_PLANES "view_planes"
+#define VV_HK_GIVE_AI "give_ai"
+#define VV_HK_GIVE_AI_SPEECH "give_ai_speech"
+
+// /mob/living
+#define VV_HK_GIVE_SPEECH_IMPEDIMENT "impede_speech"
+#define VV_HK_ADMIN_RENAME "admin_rename"
+#define VV_HK_ADD_MOOD "addmood"
+#define VV_HK_REMOVE_MOOD "removemood"
+#define VV_HK_GIVE_HALLUCINATION "give_hallucination"
+#define VV_HK_GIVE_DELUSION_HALLUCINATION "give_hallucination_delusion"
+#define VV_HK_GIVE_GUARDIAN_SPIRIT "give_guardian_spirit"
+
+// /mob/living/carbon
+#define VV_HK_MODIFY_BODYPART "mod_bodypart"
+#define VV_HK_MODIFY_ORGANS "organs_modify"
+#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_MUTATIONS "quirkmut"
+#define VV_HK_MOD_QUIRKS "quirkmod"
+#define VV_HK_SET_SPECIES "setspecies"
+#define VV_HK_PURRBATION "purrbation"
+#define VV_HK_APPLY_DNA_INFUSION "apply_dna_infusion"
+#define VV_HK_TURN_INTO_MMI "turn_into_mmi"
+
#define VV_HK_WEAKREF_RESOLVE "weakref_resolve"
+
+// Flags for debug_variable() that do little things to what we end up rendering
+
+/// ALWAYS render a reduced list, useful for fuckoff big datums that need to be condensed for the sake of client load
+#define VV_ALWAYS_CONTRACT_LIST (1<<0)
diff --git a/code/_global_vars/__unsorted.dm b/code/_global_vars/__unsorted.dm
index 2f45f9460a..4c6cd331e2 100644
--- a/code/_global_vars/__unsorted.dm
+++ b/code/_global_vars/__unsorted.dm
@@ -37,8 +37,6 @@ GLOBAL_DATUM_INIT(mods, /datum/moduletypes, new())
GLOBAL_VAR_INIT(gravity_is_on, TRUE)
-GLOBAL_VAR(join_motd)
-
// Metric datum, used to keep track of the round.
GLOBAL_DATUM_INIT(metric, /datum/metric, new())
diff --git a/code/_global_vars/admin.dm b/code/_global_vars/admin.dm
new file mode 100644
index 0000000000..157d7151c8
--- /dev/null
+++ b/code/_global_vars/admin.dm
@@ -0,0 +1,15 @@
+// A list of all the special byond lists that need to be handled different by vv
+GLOBAL_LIST_INIT(vv_special_lists, init_special_list_names())
+
+/proc/init_special_list_names()
+ var/list/output = list()
+ var/obj/sacrifice = new
+ for(var/varname in sacrifice.vars)
+ var/value = sacrifice.vars[varname]
+ if(!islist(value))
+ if(!isdatum(value) && hascall(value, "Cut"))
+ output += varname
+ continue
+ if(isnull(locate(REF(value))))
+ output += varname
+ return output
diff --git a/code/_global_vars/colorvars.dm b/code/_global_vars/colorvars.dm
new file mode 100644
index 0000000000..c49a3d90df
--- /dev/null
+++ b/code/_global_vars/colorvars.dm
@@ -0,0 +1 @@
+GLOBAL_LIST_INIT(color_vars, list("color"))
diff --git a/code/_helpers/_lists.dm b/code/_helpers/_lists.dm
index dd5d39a08a..ef8a8a8a8d 100644
--- a/code/_helpers/_lists.dm
+++ b/code/_helpers/_lists.dm
@@ -1,41 +1,56 @@
/*
* Holds procs to help with list operations
* Contains groups:
- * Misc
- * Sorting
+ * Misc
+ * Sorting
*/
-// Determiner constants
-#define DET_NONE 0x00
-#define DET_DEFINITE 0x01 // the
-#define DET_INDEFINITE 0x02 // a, an, some
-#define DET_AUTO 0x04
/*
* Misc
*/
-//CHOMPEdit Begin
-///compare two lists, returns TRUE if they are the same
-/proc/compare_list(list/l,list/d)
- if(!islist(l) || !islist(d))
- return FALSE
+/*
+ * ## Lazylists
+ *
+ * * What is a lazylist?
+ *
+ * True to its name a lazylist is a lazy instantiated list.
+ * It is a list that is only created when necessary (when it has elements) and is null when empty.
+ *
+ * * Why use a lazylist?
+ *
+ * Lazylists save memory - an empty list that is never used takes up more memory than just `null`.
+ *
+ * * When to use a lazylist?
+ *
+ * Lazylists are best used on hot types when making lists that are not always used.
+ *
+ * For example, if you were adding a list to all atoms that tracks the names of people who touched it,
+ * you would want to use a lazylist because most atoms will never be touched by anyone.
+ *
+ * * How do I use a lazylist?
+ *
+ * A lazylist is just a list you defined as `null` rather than `list()`.
+ * Then, you use the LAZY* macros to interact with it, which are essentially null-safe ways to interact with a list.
+ *
+ * Note that you probably should not be using these macros if your list is not a lazylist.
+ * This will obfuscate the code and make it a bit harder to read and debug.
+ *
+ * Generally speaking you shouldn't be checking if your lazylist is `null` yourself, the macros will do that for you.
+ * Remember that LAZYLEN (and by extension, length) will return 0 if the list is null.
+ */
- if(l.len != d.len)
- return FALSE
-
- for(var/i in 1 to l.len)
- if(l[i] != d[i])
- return FALSE
-
- return TRUE
+///Initialize the lazylist
+#define LAZYINITLIST(L) if (!L) { L = list(); }
+///Returns the key of the submitted item in the list
+#define LAZYFIND(L, V) (L ? L.Find(V) : 0)
/// Returns the top (last) element from the list, does not remove it from the list. Stack functionality.
/proc/peek(list/target_list)
var/list_length = length(target_list)
if(list_length != 0)
return target_list[list_length]
-//CHOMPEdit End
//Returns a list in plain english as a string
/proc/english_list(var/list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = ",")
@@ -47,6 +62,12 @@
if(2) return "[input[1]][and_text][input[2]]"
else return "[jointext(input, comma_text, 1, -1)][final_comma_text][and_text][input[input.len]]"
+// Determiner constants
+#define DET_NONE 0x00
+#define DET_DEFINITE 0x01 // the
+#define DET_INDEFINITE 0x02 // a, an, some
+#define DET_AUTO 0x04
+
//Returns a newline-separated list that counts equal-ish items, outputting count and item names, optionally with icons and specific determiners
/proc/counting_english_list(var/list/input, var/mob/user, output_icons = TRUE, determiners = DET_NONE, nothing_text = "nothing", line_prefix = "\t", first_item_prefix = "\n", last_item_suffix = "\n", and_text = "\n", comma_text = "\n", final_comma_text = ",") //CHOMPEdit
var/list/counts = list() // counted input items
@@ -1015,4 +1036,18 @@ var/global/list/json_cache = list()
return_list += bit
return return_list
+
+///compare two lists, returns TRUE if they are the same
+/proc/compare_list(list/l,list/d)
+ if(!islist(l) || !islist(d))
+ return FALSE
+
+ if(l.len != d.len)
+ return FALSE
+
+ for(var/i in 1 to l.len)
+ if(l[i] != d[i])
+ return FALSE
+
+ return TRUE
//CHOMPAdd end
diff --git a/code/_helpers/datums.dm b/code/_helpers/datums.dm
new file mode 100644
index 0000000000..bb4c8b4e77
--- /dev/null
+++ b/code/_helpers/datums.dm
@@ -0,0 +1,5 @@
+///Check if a datum has not been deleted and is a valid source
+/proc/is_valid_src(datum/source_datum)
+ if(istype(source_datum))
+ return !QDELETED(source_datum)
+ return FALSE
diff --git a/code/_helpers/visual_filters.dm b/code/_helpers/filters.dm
similarity index 77%
rename from code/_helpers/visual_filters.dm
rename to code/_helpers/filters.dm
index cc3b02dc60..2b82eb4d7b 100644
--- a/code/_helpers/visual_filters.dm
+++ b/code/_helpers/filters.dm
@@ -1,4 +1,5 @@
#define ICON_NOT_SET "Not Set"
+
//This is stored as a nested list instead of datums or whatever because it json encodes nicely for usage in tgui
GLOBAL_LIST_INIT(master_filter_info, list(
"alpha" = list(
@@ -21,20 +22,34 @@ GLOBAL_LIST_INIT(master_filter_info, list(
"size" = 1
)
),
- /* Not supported because making a proper matrix editor on the frontend would be a huge dick pain.
- Uncomment if you ever implement it
+ "bloom" = list(
+ "defaults" = list(
+ "threshold" = COLOR_BLACK,
+ "size" = 1,
+ "offset" = 0,
+ "alpha" = 255
+ )
+ ),
+ // Needs either a proper matrix editor, or just a hook to our existing one
+ // Issue is filterrific assumes variables will have the same value type if they share the same name, which this violates
+ // Gotta refactor this sometime
"color" = list(
"defaults" = list(
"color" = matrix(),
"space" = FILTER_COLOR_RGB
+ ),
+ "enums" = list(
+ "FILTER_COLOR_RGB" = FILTER_COLOR_RGB,
+ "FILTER_COLOR_HSV" = FILTER_COLOR_HSV,
+ "FILTER_COLOR_HSL" = FILTER_COLOR_HSL,
+ "FILTER_COLOR_HCY" = FILTER_COLOR_HCY
)
),
- */
"displace" = list(
"defaults" = list(
"x" = 0,
"y" = 0,
- "size" = null,
+ "size" = 1,
"icon" = ICON_NOT_SET,
"render_source" = ""
)
@@ -61,8 +76,20 @@ GLOBAL_LIST_INIT(master_filter_info, list(
"render_source" = "",
"flags" = FILTER_OVERLAY,
"color" = "",
- "transform" = null,
+ "transform" = TRANSFORM_MATRIX_IDENTITY,
"blend_mode" = BLEND_DEFAULT
+ ),
+ "flags" = list(
+ "FILTER_OVERLAY" = FILTER_OVERLAY,
+ "FILTER_UNDERLAY" = FILTER_UNDERLAY
+ ),
+ "enums" = list(
+ "BLEND_DEFAULT" = BLEND_DEFAULT,
+ "BLEND_OVERLAY" = BLEND_OVERLAY,
+ "BLEND_ADD" = BLEND_ADD,
+ "BLEND_SUBTRACT" = BLEND_SUBTRACT,
+ "BLEND_MULTIPLY" = BLEND_MULTIPLY,
+ "BLEND_INSET_OVERLAY" = BLEND_INSET_OVERLAY
)
),
"motion_blur" = list(
@@ -167,7 +194,7 @@ GLOBAL_LIST_INIT(master_filter_info, list(
if(!isnull(space))
.["space"] = space
-/proc/displacement_map_filter(icon, render_source, x, y, size = 32)
+/proc/displacement_map_filter(icon, render_source, x, y, size = ICON_SIZE_ALL)
. = list("type" = "displace")
if(!isnull(icon))
.["icon"] = icon
@@ -198,6 +225,17 @@ GLOBAL_LIST_INIT(master_filter_info, list(
if(!isnull(size))
.["size"] = size
+/proc/bloom_filter(threshold, size, offset, alpha)
+ . = list("type" = "bloom")
+ if(!isnull(threshold))
+ .["threshold"] = threshold
+ if(!isnull(size))
+ .["size"] = size
+ if(!isnull(offset))
+ .["offset"] = offset
+ if(!isnull(alpha))
+ .["alpha"] = alpha
+
/proc/layering_filter(icon, render_source, x, y, flags, color, transform, blend_mode)
. = list("type" = "layer")
if(!isnull(icon))
@@ -293,71 +331,6 @@ GLOBAL_LIST_INIT(master_filter_info, list(
if(!isnull(flags))
.["flags"] = flags
-/atom/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/proc/update_filters()
- filters = null
- filter_data = sortTim(filter_data, GLOBAL_PROC_REF(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))
- UNSETEMPTY(filter_data)
-
-/atom/proc/transition_filter(name, time, list/new_params, easing, loop)
- var/filter = get_filter(name)
- if(!filter)
- return
-
- var/list/old_filter_data = filter_data[name]
-
- var/list/params = old_filter_data.Copy()
- for(var/thing in new_params)
- params[thing] = new_params[thing]
-
- animate(filter, new_params, time = time, easing = easing, loop = loop)
- for(var/param in params)
- filter_data[name][param] = params[param]
-
-/atom/proc/change_filter_priority(name, new_priority)
- if(!filter_data || !filter_data[name])
- return
-
- filter_data[name]["priority"] = new_priority
- update_filters()
-
-/obj/item/update_filters()
- . = ..()
- /* Will port this from TG
- for(var/datum/action/A as anything in actions)
- A.UpdateButtonIcon()
- */
-
-/atom/proc/get_filter(name)
- if(filter_data && filter_data[name])
- return filters[filter_data.Find(name)]
-
-/atom/proc/remove_filter(name_or_names)
- if(!filter_data)
- return
-
- var/list/names = islist(name_or_names) ? name_or_names : list(name_or_names)
-
- for(var/name in names)
- if(filter_data[name])
- filter_data -= name
- update_filters()
-
-/atom/proc/clear_filters()
- filter_data = null
- filters = null
-
/proc/apply_wibbly_filters(atom/in_atom, length)
for(var/i in 1 to 7)
//This is a very baffling and strange way of doing this but I am just preserving old functionality
@@ -375,9 +348,20 @@ GLOBAL_LIST_INIT(master_filter_info, list(
animate(filter, offset = random_roll, time = 0, loop = -1, flags = ANIMATION_PARALLEL)
animate(offset = random_roll - 1, time = rand() * 20 + 10)
-/proc/remove_wibbly_filters(atom/in_atom)
+/proc/remove_wibbly_filters(atom/in_atom, remove_duration = 0)
+ if(QDELETED(in_atom))
+ return
var/filter
for(var/i in 1 to 7)
filter = in_atom.get_filter("wibbly-[i]")
- animate(filter)
- in_atom.remove_filter("wibbly-[i]")
+ if(remove_duration == 0)
+ animate(filter)
+ in_atom.remove_filter("wibbly-[i]")
+ continue
+ animate(filter, x = 0, y = 0, size = 0, offset = 0, time = remove_duration)
+ addtimer(CALLBACK(in_atom, TYPE_PROC_REF(/datum, remove_filter), "wibbly-[i]"), remove_duration)
+
+/proc/convert_list_to_filter(list/list_filter)
+ var/list/arguments = list_filter.Copy()
+ arguments -= "priority"
+ return filter(arglist(arguments))
diff --git a/code/_helpers/generators.dm b/code/_helpers/generators.dm
new file mode 100644
index 0000000000..85f6de3a09
--- /dev/null
+++ b/code/_helpers/generators.dm
@@ -0,0 +1,11 @@
+/**
+ * returns the arguments given to a generator and manually extracts them from the internal byond object
+ * returns:
+ * * flat list of strings for args given to the generator.
+ * * Note: this means things like "list(1,2,3)" will need to be processed
+ */
+/proc/return_generator_args(generator/target)
+ var/string_repr = "[target]" //the name of the generator is the string representation of its _binobj, which also contains its args
+ string_repr = copytext(string_repr, 11, length(string_repr)) // strips extraneous data
+ string_repr = replacetext(string_repr, "\"", "") // removes the " around the type
+ return splittext(string_repr, ", ")
diff --git a/code/_helpers/icons.dm b/code/_helpers/icons.dm
index 3db67a1eb5..af1dd3fc55 100644
--- a/code/_helpers/icons.dm
+++ b/code/_helpers/icons.dm
@@ -621,6 +621,61 @@ GLOBAL_LIST_EMPTY(cached_examine_icons)
return FALSE
+/// Asks the user for an icon (either from file or as a path) and offers to customize it if possible (e.g. setting icon_state)
+/proc/pick_and_customize_icon(mob/user, pick_only=FALSE)
+ var/icon/icon_result = null
+ if(!user)
+ user = usr
+
+ var/icon_from_file = tgui_alert(user, "Do you wish to pick an icon from file?", "File picker icon", list("Yes", "No"))
+ if(isnull(icon_from_file))
+ return null
+ if(icon_from_file == "Yes")
+ icon_result = input(user, "Pick icon:", "Icon") as null|icon
+ if(!icon_result)
+ return null
+ else if(icon_from_file == "No")
+ var/new_icon = tgui_input_text(user, "Pick icon path", "icon path")
+ if(isnull(new_icon))
+ return null
+ var/regex/regex = regex(@"^.+icons/")
+ new_icon = regex.Replace(replacetext(new_icon, @"\", "/"), "icons/")
+ icon_result = fcopy_rsc(new_icon)
+ if(!icon_result)
+ to_chat(user, span_warning("'[new_icon]' is an invalid icon path!"))
+ return null
+
+ var/dmi_path = get_icon_dmi_path(icon_result)
+ if(!dmi_path || pick_only)
+ return icon_result
+
+ var/custom = tgui_alert(user, "Do you wish to specify any arguments for the icon?", "Customize Icon", list("Yes", "No"))
+ if(isnull(custom))
+ return null
+ if(custom == "Yes")
+ var/new_icon_state = tgui_input_text(user, "Pick icon_state", "icon_state")
+ if(isnull(new_icon_state))
+ return null
+ var/new_icon_dir = tgui_input_list(user, "Pick icon dir", "dir", list("North", "East", "South", "West"), default="South")
+ if(isnull(new_icon_dir))
+ return null
+ var/new_icon_frame = tgui_input_number(user, "Pick icon frame", "frame", min_value=0, round_value=TRUE)
+ if(isnull(new_icon_frame))
+ return null
+ var/new_icon_moving = tgui_input_list(user, "Pick icon moving", "moving", list("Both", "Movement only", "Non-Movement Only"), default="Both")
+ switch(new_icon_moving)
+ if("Both")
+ new_icon_moving = null
+ if("Movement only")
+ new_icon_moving = 1
+ if("Non-Movement Only")
+ new_icon_moving = 0
+ else
+ return null
+ icon_result = new(dmi_path, new_icon_state, text2dir(new_icon_dir), new_icon_frame, new_icon_moving)
+
+ return icon_result
+
/**
* generate an asset for the given icon or the icon of the given appearance for [thing], and send it to any clients in target.
* Arguments:
diff --git a/code/_helpers/matrices.dm b/code/_helpers/matrices.dm
index 530e97ff3f..5a89e934b3 100644
--- a/code/_helpers/matrices.dm
+++ b/code/_helpers/matrices.dm
@@ -2,6 +2,13 @@
. = new_angle - old_angle
Turn(.) //BYOND handles cases such as -270, 360, 540 etc. DOES NOT HANDLE 180 TURNS WELL, THEY TWEEN AND LOOK LIKE SHIT
+/**
+ * Shear the transform on either or both axes.
+ * * x - X axis shearing
+ * * y - Y axis shearing
+ */
+/matrix/proc/Shear(x, y)
+ return Multiply(matrix(1, x, 0, y, 1, 0))
/atom/proc/SpinAnimation(speed = 10, loops = -1, clockwise = 1, segments = 3)
if(!segments)
diff --git a/code/_helpers/type2type.dm b/code/_helpers/type2type.dm
index 43aab83243..cd107c6092 100644
--- a/code/_helpers/type2type.dm
+++ b/code/_helpers/type2type.dm
@@ -364,7 +364,7 @@
/proc/color_hex2color_matrix(string)
var/length = length(string)
if((length != 7 && length != 9) || length != length_char(string))
- return color_matrix_identity()
+ return COLOR_MATRIX_IDENTITY
var/r = hex2num(copytext(string, 2, 4)) / 255
var/g = hex2num(copytext(string, 4, 6)) / 255
var/b = hex2num(copytext(string, 6, 8)) / 255
@@ -372,7 +372,7 @@
if(length == 9)
a = hex2num(copytext(string, 8, 10)) / 255
if(!isnum(r) || !isnum(g) || !isnum(b) || !isnum(a))
- return color_matrix_identity()
+ return COLOR_MATRIX_IDENTITY
return list(
r,0,0,0,0,
g,0,0,0,0,
diff --git a/code/controllers/hooks-defs.dm b/code/controllers/hooks-defs.dm
index cb2a96b25a..b333f96ec9 100644
--- a/code/controllers/hooks-defs.dm
+++ b/code/controllers/hooks-defs.dm
@@ -44,13 +44,6 @@
*/
/hook/borgify
-/**
- * Podman hook.
- * Called in podmen.dm when someone is brought back as a Diona.
- * Parameters: var/mob/living/carbon/alien/diona
- */
-/hook/harvest_podman
-
/**
* Payroll revoked hook.
* Called in Accounts_DB.dm when someone's payroll is stolen at the Accounts terminal.
diff --git a/code/controllers/subsystems/overlays.dm b/code/controllers/subsystems/overlays.dm
index ae66d3d09f..54df55cf2e 100644
--- a/code/controllers/subsystems/overlays.dm
+++ b/code/controllers/subsystems/overlays.dm
@@ -273,3 +273,100 @@ SUBSYSTEM_DEF(overlays)
overlays |= cached_other
else if(cut_old)
cut_overlays()
+
+// Debug procs
+
+/atom
+ /// List of overlay "keys" (info about the appearance) -> mutable versions of static appearances
+ /// Drawn from the overlays list
+ var/list/realized_overlays
+ /// List of underlay "keys" (info about the appearance) -> mutable versions of static appearances
+ /// Drawn from the underlays list
+ var/list/realized_underlays
+
+/image
+ /// List of overlay "keys" (info about the appearance) -> mutable versions of static appearances
+ /// Drawn from the overlays list
+ var/list/realized_overlays
+ /// List of underlay "keys" (info about the appearance) -> mutable versions of static appearances
+ /// Drawn from the underlays list
+ var/list/realized_underlays
+
+/// Takes the atoms's existing overlays and underlays, and makes them mutable so they can be properly vv'd in the realized_overlays/underlays list
+/atom/proc/realize_overlays()
+ realized_overlays = realize_appearance_queue(overlays)
+ realized_underlays = realize_appearance_queue(underlays)
+
+/// Takes the image's existing overlays, and makes them mutable so they can be properly vv'd in the realized_overlays list
+/image/proc/realize_overlays()
+ realized_overlays = realize_appearance_queue(overlays)
+ realized_underlays = realize_appearance_queue(underlays)
+
+/// Takes a list of appearnces, makes them mutable so they can be properly vv'd and inspected
+/proc/realize_appearance_queue(list/appearances)
+ var/list/real_appearances = list()
+ var/list/queue = appearances.Copy()
+ var/queue_index = 0
+ while(queue_index < length(queue))
+ queue_index++
+ // If it's not a command, we assert that it's an appearance
+ var/mutable_appearance/appearance = queue[queue_index]
+ if(!appearance) // Who fucking adds nulls to their sublists god you people are the worst
+ continue
+
+ var/mutable_appearance/new_appearance = new /mutable_appearance()
+ new_appearance.appearance = appearance
+ var/key = "[appearance.icon]-[appearance.icon_state]-[appearance.plane]-[appearance.layer]-[appearance.dir]-[appearance.color]"
+ var/tmp_key = key
+ var/appearance_indx = 1
+ while(real_appearances[tmp_key])
+ tmp_key = "[key]-[appearance_indx]"
+ appearance_indx++
+
+ real_appearances[tmp_key] = new_appearance
+ var/add_index = queue_index
+ // Now check its children
+ for(var/mutable_appearance/child_appearance as anything in appearance.overlays)
+ add_index++
+ queue.Insert(add_index, child_appearance)
+ for(var/mutable_appearance/child_appearance as anything in appearance.underlays)
+ add_index++
+ queue.Insert(add_index, child_appearance)
+ return real_appearances
+
+/// Takes two appearances as args, prints out, logs, and returns a text representation of their differences
+/// Including suboverlays
+/proc/diff_appearances(mutable_appearance/first, mutable_appearance/second, iter = 0)
+ var/list/diffs = list()
+ var/list/firstdeet = first.vars
+ var/list/seconddeet = second.vars
+ var/diff_found = FALSE
+ for(var/name in first.vars)
+ var/firstv = firstdeet[name]
+ var/secondv = seconddeet[name]
+ if(firstv ~= secondv)
+ continue
+ if((islist(firstv) || islist(secondv)) && length(firstv) == 0 && length(secondv) == 0)
+ continue
+ if(name == "vars") // Go away
+ continue
+ if(name == "_listen_lookup") // This is just gonna happen with marked datums, don't care
+ continue
+ if(name == "overlays")
+ first.realize_overlays()
+ second.realize_overlays()
+ var/overlays_differ = FALSE
+ for(var/i in 1 to length(first.realized_overlays))
+ if(diff_appearances(first.realized_overlays[i], second.realized_overlays[i], iter + 1))
+ overlays_differ = TRUE
+
+ if(!overlays_differ)
+ continue
+
+ diff_found = TRUE
+ diffs += "Diffs detected at [name]: First ([firstv]), Second ([secondv])"
+
+ var/text = "Depth of: [iter]\n\t[diffs.Join("\n\t")]"
+ message_admins(text)
+ log_world(text)
+ return diff_found
diff --git a/code/controllers/subsystems/statpanel.dm b/code/controllers/subsystems/statpanel.dm
index f580eb5d81..ad76988b5d 100644
--- a/code/controllers/subsystems/statpanel.dm
+++ b/code/controllers/subsystems/statpanel.dm
@@ -65,7 +65,7 @@ SUBSYSTEM_DEF(statpanels)
if(check_rights_for(target, R_MENTOR))
target.stat_panel.send_message("add_tickets_tabs", target.holder.href_token)
- else if(!("MC" in target.panel_tabs) || !("Tickets" in target.panel_tabs))
+ if(check_rights_for(target, R_HOLDER) && (!("MC" in target.panel_tabs) || !("Tickets" in target.panel_tabs)))
target.stat_panel.send_message("add_admin_tabs", target.holder.href_token)
//if(target.stat_tab == "MC" && ((num_fires % mc_wait == 0) || target?.prefs.read_preference(/datum/preference/toggle/fast_mc_refresh)))
diff --git a/code/datums/browser.dm b/code/datums/browser.dm
index 7ba9e80e72..2bfeab604d 100644
--- a/code/datums/browser.dm
+++ b/code/datums/browser.dm
@@ -422,15 +422,15 @@
setting["value"] = new_value
if ("string")
- setting["value"] = stripped_input(user, "Enter new value for [setting["desc"]]", "Enter new value for [setting["desc"]]", setting["value"])
+ setting["value"] = tgui_input_text(user, "Enter new value for [setting["desc"]]", "Enter new value for [setting["desc"]]", setting["value"], encode = TRUE)
if ("number")
- setting["value"] = input(user, "Enter new value for [setting["desc"]]", "Enter new value for [setting["desc"]]") as num
+ setting["value"] = tgui_input_number(user, "Enter new value for [setting["desc"]]", "Enter new value for [setting["desc"]]")
if ("color")
- setting["value"] = input(user, "Enter new value for [setting["desc"]]", "Enter new value for [setting["desc"]]", setting["value"]) as color
+ setting["value"] = tgui_color_picker(user, "Enter new value for [setting["desc"]]", "Enter new value for [setting["desc"]]", setting["value"])
if ("boolean")
setting["value"] = (setting["value"] == "Yes") ? "No" : "Yes"
if ("ckey")
- setting["value"] = input(user, "[setting["desc"]]?") in (list("none") + GLOB.directory)
+ setting["value"] = tgui_input_list(user, "[setting["desc"]]?", (list("none") + GLOB.directory))
if (setting["callback"])
var/datum/callback/callback = setting["callback"]
settings = callback.Invoke(settings)
diff --git a/code/datums/datum.dm b/code/datums/datum.dm
index d786255207..334502de2d 100644
--- a/code/datums/datum.dm
+++ b/code/datums/datum.dm
@@ -57,6 +57,10 @@
*/
var/list/cooldowns
+
+ /// List for handling persistent filters.
+ var/list/filter_data
+
#ifdef REFERENCE_TRACKING
var/tmp/running_find_references
var/tmp/last_find_references = 0
@@ -71,6 +75,15 @@
// Create and destroy is weird and I wanna cover my bases
var/harddel_deets_dumped = FALSE
+/**
+ * Called when a href for this datum is clicked
+ *
+ * Sends a [COMSIG_TOPIC] signal
+ */
+/datum/Topic(href, href_list[])
+ ..()
+ SEND_SIGNAL(src, COMSIG_TOPIC, usr, href_list)
+
/**
* Default implementation of clean-up code.
*
@@ -185,6 +198,158 @@
SEND_SIGNAL(source, COMSIG_CD_RESET(index), S_TIMER_COOLDOWN_TIMELEFT(source, index))
TIMER_COOLDOWN_END(source, index)
+/** Add a filter to the datum.
+ * This is on datum level, despite being most commonly / primarily used on atoms, so that filters can be applied to images / mutable appearances.
+ * Can also be used to assert a filter's existence. I.E. update a filter regardless if it exists or not.
+ *
+ * Arguments:
+ * * name - Filter name
+ * * priority - Priority used when sorting the filter.
+ * * params - Parameters of the filter.
+ */
+/datum/proc/add_filter(name, priority, list/params)
+ LAZYINITLIST(filter_data)
+ var/list/copied_parameters = params.Copy()
+ copied_parameters["priority"] = priority
+ filter_data[name] = copied_parameters
+ update_filters()
+
+///A version of add_filter that takes a list of filters to add rather than being individual, to limit calls to update_filters().
+/datum/proc/add_filters(list/list/filters)
+ LAZYINITLIST(filter_data)
+ for(var/list/individual_filter as anything in filters)
+ var/list/params = individual_filter["params"]
+ var/list/copied_parameters = params.Copy()
+ copied_parameters["priority"] = individual_filter["priority"]
+ filter_data[individual_filter["name"]] = copied_parameters
+ update_filters()
+
+/// Reapplies all the filters.
+/datum/proc/update_filters()
+ ASSERT(isatom(src) || isimage(src))
+ var/atom/atom_cast = src // filters only work with images or atoms.
+ atom_cast.filters = null
+ sortTim(filter_data, GLOBAL_PROC_REF(cmp_filter_data_priority), TRUE)
+ for(var/filter_raw in filter_data)
+ var/list/data = filter_data[filter_raw]
+ var/list/arguments = data.Copy()
+ arguments -= "priority"
+ atom_cast.filters += filter(arglist(arguments))
+ UNSETEMPTY(filter_data)
+
+/** Update a filter's parameter to the new one. If the filter doesn't exist we won't do anything.
+ *
+ * Arguments:
+ * * name - Filter name
+ * * new_params - New parameters of the filter
+ * * overwrite - TRUE means we replace the parameter list completely. FALSE means we only replace the things on new_params.
+ */
+/datum/proc/modify_filter(name, list/new_params, overwrite = FALSE)
+ var/filter = get_filter(name)
+ if(!filter)
+ return
+ if(overwrite)
+ filter_data[name] = new_params
+ else
+ for(var/thing in new_params)
+ filter_data[name][thing] = new_params[thing]
+ update_filters()
+
+/** Update a filter's parameter and animate this change. If the filter doesn't exist we won't do anything.
+ * Basically a [datum/proc/modify_filter] call but with animations. Unmodified filter parameters are kept.
+ *
+ * Arguments:
+ * * name - Filter name
+ * * new_params - New parameters of the filter
+ * * time - time arg of the BYOND animate() proc.
+ * * easing - easing arg of the BYOND animate() proc.
+ * * loop - loop arg of the BYOND animate() proc.
+ */
+/datum/proc/transition_filter(name, list/new_params, time, easing, loop)
+ var/filter = get_filter(name)
+ if(!filter)
+ return
+ // This can get injected by the filter procs, we want to support them so bye byeeeee
+ new_params -= "type"
+ animate(filter, new_params, time = time, easing = easing, loop = loop)
+ modify_filter(name, new_params)
+
+/** Keeps the steps in the correct order.
+* Arguments:
+* * params - the parameters you want this step to animate to
+* * duration - the time it takes to animate this step
+* * easing - the type of easing this step has
+*/
+/proc/FilterChainStep(params, duration, easing)
+ params -= "type"
+ return list("params"= params, "duration"=duration, "easing"=easing)
+
+/** Similar to transition_filter(), except it creates an animation chain that moves between a list of states.
+ * Arguments:
+ * * name - Filter name
+ * * num_loops - Amount of times the chain loops. INDEFINITE = Infinite
+ * * ... - a list of each link in the animation chain. Use FilterChainStep(params, duration, easing) for each link
+ * Example use:
+ * * add_filter("blue_pulse", 1, color_matrix_filter(COLOR_WHITE))
+ * * transition_filter_chain(src, "blue_pulse", INDEFINITE,\
+ * * FilterChainStep(color_matrix_filter(COLOR_BLUE), 10 SECONDS, CUBIC_EASING),\
+ * * FilterChainStep(color_matrix_filter(COLOR_WHITE), 10 SECONDS, CUBIC_EASING))
+ * The above code would edit a color_matrix_filter() to slowly turn blue over 10 seconds before returning back to white 10 seconds after, repeating this chain forever.
+ */
+/datum/proc/transition_filter_chain(name, num_loops, ...)
+ var/list/transition_steps = args.Copy(3)
+ var/filter = get_filter(name)
+ if(!filter)
+ return
+ var/list/first_step = transition_steps[1]
+ animate(filter, first_step["params"], time = first_step["duration"], easing = first_step["easing"], loop = num_loops)
+ for(var/transition_step in 2 to length(transition_steps))
+ var/list/this_step = transition_steps[transition_step]
+ animate(this_step["params"], time = this_step["duration"], easing = this_step["easing"])
+
+/// Updates the priority of the passed filter key
+/datum/proc/change_filter_priority(name, new_priority)
+ if(!filter_data || !filter_data[name])
+ return
+
+ filter_data[name]["priority"] = new_priority
+ update_filters()
+
+/// Returns the filter associated with the passed key
+/datum/proc/get_filter(name)
+ ASSERT(isatom(src) || isimage(src))
+ if(filter_data && filter_data[name])
+ var/atom/atom_cast = src // filters only work with images or atoms.
+ return atom_cast.filters[filter_data.Find(name)]
+
+/// Returns the indice in filters of the given filter name.
+/// If it is not found, returns null.
+/datum/proc/get_filter_index(name)
+ return filter_data?.Find(name)
+
+/// Removes the passed filter, or multiple filters, if supplied with a list.
+/datum/proc/remove_filter(name_or_names)
+ if(!filter_data)
+ return
+
+ var/list/names = islist(name_or_names) ? name_or_names : list(name_or_names)
+
+ . = FALSE
+ for(var/name in names)
+ if(filter_data[name])
+ filter_data -= name
+ . = TRUE
+
+ if(.)
+ update_filters()
+ return .
+
+/datum/proc/clear_filters()
+ ASSERT(isatom(src) || isimage(src))
+ var/atom/atom_cast = src // filters only work with images or atoms.
+ filter_data = null
+ atom_cast.filters = null
+
/// Return text from this proc to provide extra context to hard deletes that happen to it
/// Optional, you should use this for cases where replication is difficult and extra context is required
/// Can be called more then once per object, use harddel_deets_dumped to avoid duplicate calls (I am so sorry)
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index 49f267e296..e1b99ccfa1 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -2,10 +2,13 @@
return TRUE
/datum/proc/can_vv_get(var_name)
+ if(var_name == NAMEOF(src, vars))
+ return FALSE
return TRUE
-/datum/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited
- if(var_name == NAMEOF(src, vars) || var_name == NAMEOF(src, parent_type))
+/// Called when a var is edited with the new value to change to
+/datum/proc/vv_edit_var(var_name, var_value)
+ if(var_name == NAMEOF(src, vars))
return FALSE
vars[var_name] = var_value
datum_flags |= DF_VAR_EDITED
@@ -13,23 +16,50 @@
/datum/proc/vv_get_var(var_name)
switch(var_name)
- if ("vars")
+ if (NAMEOF(src, vars))
return debug_variable(var_name, list(), 0, src)
- return debug_variable(var_name, get_variable_value(var_name), 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/can_vv_mark()
+ return TRUE
+
+/**
+ * Gets all the dropdown options in the vv menu.
+ * When overriding, make sure to call . = ..() first and append to the result, that way parent items are always at the top and child items are further down.
+ * Add separators by doing VV_DROPDOWN_OPTION("", "---")
+ */
/datum/proc/vv_get_dropdown()
+ SHOULD_CALL_PARENT(TRUE)
+
. = 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_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")
+ VV_DROPDOWN_OPTION(VV_HK_REMOVECOMPONENT, "Remove Component/Element")
+ VV_DROPDOWN_OPTION(VV_HK_MASS_REMOVECOMPONENT, "Mass Remove Component/Element")
+ //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 in case this runtimes.
+ */
+/datum/proc/vv_do_topic(list/href_list)
+ if(!usr || !usr.client || !usr.client.holder || !check_rights(R_VAREDIT))
+ return FALSE //This is VV, not to be called by anything else.
+ if(SEND_SIGNAL(src, COMSIG_VV_TOPIC, usr, href_list) & COMPONENT_VV_HANDLED)
+ return FALSE
+ //if(href_list[VV_HK_MODIFY_TRAITS])
+ // usr.client.holder.modify_traits(src)
+ return TRUE
//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!
+/*
/datum/proc/vv_do_topic(list/href_list)
if(!usr || !usr.client.holder)
return //This is VV, not to be called by anything else.
@@ -87,6 +117,7 @@
_AddElement(lst)
log_admin("[key_name(usr)] has added [result] [datumname] to [key_name(src)].")
message_admins(span_notice("[key_name_admin(usr)] has added [result] [datumname] to [key_name_admin(src)]."))
+*/
/datum/proc/vv_get_header()
. = list()
diff --git a/code/game/atom/atom_vv.dm b/code/game/atom/atom_vv.dm
new file mode 100644
index 0000000000..71c39a82e0
--- /dev/null
+++ b/code/game/atom/atom_vv.dm
@@ -0,0 +1,253 @@
+/**
+ * Return the markup to for the dropdown list for the VV panel for this atom
+ *
+ * Override in subtypes to add custom VV handling in the VV panel
+ */
+/atom/vv_get_dropdown()
+ . = ..()
+ VV_DROPDOWN_OPTION("", "---------")
+ if(!ismovable(src))
+ var/turf/curturf = get_turf(src)
+ if(curturf)
+ . += "Jump To "
+ VV_DROPDOWN_OPTION(VV_HK_MODIFY_TRANSFORM, "Modify Transform")
+ VV_DROPDOWN_OPTION(VV_HK_SPIN_ANIMATION, "SpinAnimation")
+ VV_DROPDOWN_OPTION(VV_HK_STOP_ALL_ANIMATIONS, "Stop All Animations")
+ VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EMP, "EMP Pulse")
+ VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EXPLOSION, "Explosion")
+ VV_DROPDOWN_OPTION(VV_HK_EDIT_FILTERS, "Edit Filters")
+ //VV_DROPDOWN_OPTION(VV_HK_EDIT_COLOR_MATRIX, "Edit Color as Matrix")
+ VV_DROPDOWN_OPTION(VV_HK_TEST_MATRIXES, "Test Matrices")
+ //if(greyscale_colors)
+ // VV_DROPDOWN_OPTION(VV_HK_MODIFY_GREYSCALE, "Modify greyscale colors")
+
+/atom/vv_do_topic(list/href_list)
+ . = ..()
+
+ if(!.)
+ return
+
+ if(href_list[VV_HK_TRIGGER_EXPLOSION])
+ return SSadmin_verbs.dynamic_invoke_verb(usr, /datum/admin_verb/admin_explosion, src)
+
+ if(href_list[VV_HK_TRIGGER_EMP])
+ return SSadmin_verbs.dynamic_invoke_verb(usr, /datum/admin_verb/admin_emp, src)
+
+ if(href_list[VV_HK_MODIFY_TRANSFORM])
+ if(!check_rights(R_VAREDIT))
+ return
+ var/result = tgui_input_list(usr, "Choose the transformation to apply","Transform Mod", list("Scale","Translate","Rotate","Shear"))
+ var/matrix/M = transform
+ if(!result)
+ return
+ switch(result)
+ if("Scale")
+ var/x = tgui_input_number(usr, "Choose x mod","Transform Mod")
+ var/y = tgui_input_number(usr, "Choose y mod","Transform Mod")
+ if(isnull(x) || isnull(y))
+ return
+ transform = M.Scale(x,y)
+ if("Translate")
+ var/x = tgui_input_number(usr, "Choose x mod (negative = left, positive = right)","Transform Mod")
+ var/y = tgui_input_number(usr, "Choose y mod (negative = down, positive = up)","Transform Mod")
+ if(isnull(x) || isnull(y))
+ return
+ transform = M.Translate(x,y)
+ if("Shear")
+ var/x = tgui_input_number(usr, "Choose x mod","Transform Mod")
+ var/y = tgui_input_number(usr, "Choose y mod","Transform Mod")
+ if(isnull(x) || isnull(y))
+ return
+ transform = M.Shear(x,y)
+ if("Rotate")
+ var/angle = tgui_input_number(usr, "Choose angle to rotate","Transform Mod")
+ if(isnull(angle))
+ return
+ transform = M.Turn(angle)
+ SEND_SIGNAL(src, COMSIG_ATOM_VV_MODIFY_TRANSFORM)
+
+ if(href_list[VV_HK_SPIN_ANIMATION])
+ if(!check_rights(R_VAREDIT))
+ return
+ var/num_spins = tgui_alert(usr, "Do you want infinite spins?", "Spin Animation", list("Yes", "No"))
+ if(num_spins == "No")
+ num_spins = tgui_input_number(usr, "How many spins?", "Spin Animation")
+ else
+ num_spins = -1
+ if(!num_spins)
+ return
+ var/spins_per_sec = tgui_input_number(usr, "How many spins per second?", "Spin Animation")
+ if(!spins_per_sec)
+ return
+ var/direction = tgui_alert(usr, "Which direction?", "Spin Animation", list("Clockwise", "Counter-clockwise"))
+ switch(direction)
+ if("Clockwise")
+ direction = 1
+ if("Counter-clockwise")
+ direction = 0
+ else
+ return
+ SpinAnimation(1 SECONDS / spins_per_sec, num_spins, direction)
+
+ if(href_list[VV_HK_STOP_ALL_ANIMATIONS])
+ if(!check_rights(R_VAREDIT))
+ return
+ var/result = tgui_alert(usr, "Are you sure?", "Stop Animating", list("Yes", "No"))
+ if(result == "Yes")
+ animate(src, transform = null, flags = ANIMATION_END_NOW) // Literally just fucking stop animating entirely because admin said so
+ return
+
+ if(href_list[VV_HK_AUTO_RENAME])
+ if(!check_rights(R_VAREDIT))
+ return
+ var/newname = tgui_input_text(usr, "What do you want to rename this to?", "Automatic Rename")
+ // Check the new name against the chat filter. If it triggers the IC chat filter, give an option to confirm.
+ //if(newname && !(is_ic_filtered(newname) || is_soft_ic_filtered(newname) && tgui_alert(usr, "Your selected name contains words restricted by IC chat filters. Confirm this new name?", "IC Chat Filter Conflict", list("Confirm", "Cancel")) != "Confirm"))
+ if(newname)
+ vv_auto_rename(newname)
+
+ if(href_list[VV_HK_EDIT_FILTERS])
+ if(!check_rights(R_VAREDIT))
+ return
+ usr.client?.open_filter_editor(src)
+
+ //if(href_list[VV_HK_EDIT_COLOR_MATRIX])
+ // if(!check_rights(R_VAREDIT))
+ // return
+ // usr.client?.open_color_matrix_editor(src)
+
+ if(href_list[VV_HK_TEST_MATRIXES])
+ if(!check_rights(R_VAREDIT))
+ return
+ usr.client?.open_matrix_tester(src)
+
+/atom/vv_get_header()
+ . = ..()
+ var/refid = REF(src)
+ . += "[VV_HREF_TARGETREF(refid, VV_HK_AUTO_RENAME, span_bold("[src] "))]"
+ . += " " + span_small("<< [dir2text(dir) || dir] >> ")
+
+/**
+ * call back when a var is edited on this atom
+ *
+ * Can be used to implement special handling of vars
+ *
+ * At the atom level, if you edit a var named "color" it will add the atom colour with
+ * admin level priority to the atom colours list
+ *
+ * Also, if GLOB.Debug2 is FALSE, it sets the [ADMIN_SPAWNED_1] flag on [flags_1][/atom/var/flags_1], which signifies
+ * the object has been admin edited
+ */
+/atom/vv_edit_var(var_name, var_value)
+ //var/old_light_flags = light_flags
+ // Disable frozen lights for now, so we can actually modify it
+ /*
+ light_flags &= ~LIGHT_FROZEN
+ switch(var_name)
+ if(NAMEOF(src, light_range))
+ if(light_system == COMPLEX_LIGHT)
+ set_light(l_range = var_value)
+ else
+ set_light_range(var_value)
+ . = TRUE
+ if(NAMEOF(src, light_power))
+ if(light_system == COMPLEX_LIGHT)
+ set_light(l_power = var_value)
+ else
+ set_light_power(var_value)
+ . = TRUE
+ if(NAMEOF(src, light_color))
+ if(light_system == COMPLEX_LIGHT)
+ set_light(l_color = var_value)
+ else
+ set_light_color(var_value)
+ . = TRUE
+ if(NAMEOF(src, light_angle))
+ if(light_system == COMPLEX_LIGHT)
+ set_light(l_angle = var_value)
+ . = TRUE
+ if(NAMEOF(src, light_dir))
+ if(light_system == COMPLEX_LIGHT)
+ set_light(l_dir = var_value)
+ . = TRUE
+ if(NAMEOF(src, light_height))
+ if(light_system == COMPLEX_LIGHT)
+ set_light(l_height = var_value)
+ . = TRUE
+ if(NAMEOF(src, light_on))
+ if(light_system == COMPLEX_LIGHT)
+ set_light(l_on = var_value)
+ else
+ set_light_on(var_value)
+ . = TRUE
+ if(NAMEOF(src, light_flags))
+ set_light_flags(var_value)
+ // I'm sorry
+ old_light_flags = var_value
+ . = TRUE
+ if(NAMEOF(src, smoothing_junction))
+ set_smoothed_icon_state(var_value)
+ . = TRUE
+ if(NAMEOF(src, opacity))
+ set_opacity(var_value)
+ . = TRUE
+ if(NAMEOF(src, base_pixel_x))
+ set_base_pixel_x(var_value)
+ . = TRUE
+ if(NAMEOF(src, base_pixel_y))
+ set_base_pixel_y(var_value)
+ . = TRUE
+ if(NAMEOF(src, material_flags))
+ toggle_material_flags(var_value)
+ . = TRUE
+ if(NAMEOF(src, material_modifier))
+ change_material_modifier(var_value)
+ . = TRUE
+ */
+ switch(var_name)
+ if(NAMEOF(src, light_range))
+ if(light_system == STATIC_LIGHT)
+ set_light(l_range = var_value)
+ else
+ set_light_range(var_value)
+ . = TRUE
+ if(NAMEOF(src, light_power))
+ if(light_system == STATIC_LIGHT)
+ set_light(l_power = var_value)
+ else
+ set_light_power(var_value)
+ . = TRUE
+ if(NAMEOF(src, light_color))
+ if(light_system == STATIC_LIGHT)
+ set_light(l_color = var_value)
+ else
+ set_light_color(var_value)
+ . = TRUE
+ if(NAMEOF(src, light_on))
+ set_light_on(var_value)
+ . = TRUE
+ if(NAMEOF(src, light_flags))
+ set_light_flags(var_value)
+ . = TRUE
+ if(NAMEOF(src, opacity))
+ set_opacity(var_value)
+ . = TRUE
+
+ //light_flags = old_light_flags
+ if(!isnull(.))
+ datum_flags |= DF_VAR_EDITED
+ return
+
+ //if(!GLOB.Debug2)
+ // flags_1 |= ADMIN_SPAWNED_1
+
+ . = ..()
+
+ switch(var_name)
+ if(NAMEOF(src, color))
+ add_atom_colour(color, ADMIN_COLOUR_PRIORITY)
+ //update_appearance()
+ update_icon()
+
+/atom/proc/vv_auto_rename(newname)
+ name = newname
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index f9121f22ab..bdc9ee5455 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -30,9 +30,6 @@
///vis overlays managed by SSvis_overlays to automaticaly turn them like other overlays
var/list/managed_vis_overlays
- ///Our local copy of filter data so we can add/remove it
- var/list/filter_data
-
//Detective Work, used for the duplicate data points kept in the scanners
var/list/original_atom
// Track if we are already had initialize() called to prevent double-initialization.
@@ -397,77 +394,6 @@
/atom/proc/get_nametag_desc(mob/user)
return "" //Desc itself is often too long to use
-/atom/vv_get_dropdown()
- . = ..()
- VV_DROPDOWN_OPTION(VV_HK_ATOM_EXPLODE, "Explosion")
- VV_DROPDOWN_OPTION(VV_HK_ATOM_EMP, "Emp Pulse")
-
-/atom/vv_do_topic(list/href_list)
- . = ..()
- IF_VV_OPTION(VV_HK_ATOM_EXPLODE)
- if(!check_rights(R_DEBUG|R_FUN))
- return
- usr.client.cmd_admin_explosion(src)
- href_list["datumrefresh"] = "\ref[src]"
- IF_VV_OPTION(VV_HK_ATOM_EMP)
- if(!check_rights(R_DEBUG|R_FUN))
- return
- usr.client.cmd_admin_emp(src)
- href_list["datumrefresh"] = "\ref[src]"
-
-/atom/vv_get_header()
- . = ..()
- var/custom_edit_name
- if(!isliving(src))
- custom_edit_name = "[src] "
- . += {"
- [custom_edit_name]
- "}
- var/content = {"
- <<
- [dir2text(dir)]
- >>
- "}
- . += span_small(content)
- var/turf/T = get_turf(src)
- . += " " + span_small("[ADMIN_COORDJMP(T)]")
-
-/atom/vv_edit_var(var_name, var_value)
- switch(var_name)
- if(NAMEOF(src, light_range))
- if(light_system == STATIC_LIGHT)
- set_light(l_range = var_value)
- else
- set_light_range(var_value)
- . = TRUE
- if(NAMEOF(src, light_power))
- if(light_system == STATIC_LIGHT)
- set_light(l_power = var_value)
- else
- set_light_power(var_value)
- . = TRUE
- if(NAMEOF(src, light_color))
- if(light_system == STATIC_LIGHT)
- set_light(l_color = var_value)
- else
- set_light_color(var_value)
- . = TRUE
- if(NAMEOF(src, light_on))
- set_light_on(var_value)
- . = TRUE
- if(NAMEOF(src, light_flags))
- set_light_flags(var_value)
- . = TRUE
- if(NAMEOF(src, opacity))
- set_opacity(var_value)
- . = TRUE
-
- if(!isnull(.))
- datum_flags |= DF_VAR_EDITED
- return
-
- . = ..()
-
/atom/proc/atom_say(message)
if(!message)
return
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index ae9d44ea83..39cde66ee6 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -92,11 +92,6 @@
QDEL_NULL(riding_datum)
set_listening(NON_LISTENING_ATOM)
-/atom/movable/vv_edit_var(var_name, var_value)
- if(var_name in GLOB.VVpixelmovement) //Pixel movement is not yet implemented, changing this will break everything irreversibly.
- return FALSE
- return ..()
-
////////////////////////////////////////
/atom/movable/Move(atom/newloc, direct = 0, movetime)
// Didn't pass enough info
@@ -723,3 +718,51 @@
/atom/movable/proc/show_message(msg, type, alt, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2)
return
+
+/atom/movable/proc/Bump_vr(var/atom/A, yes)
+ return
+
+/atom/movable/vv_get_dropdown()
+ . = ..()
+ VV_DROPDOWN_OPTION("", "---------")
+ //VV_DROPDOWN_OPTION(VV_HK_OBSERVE_FOLLOW, "Observe Follow")
+ VV_DROPDOWN_OPTION(VV_HK_GET_MOVABLE, "Get Movable")
+ VV_DROPDOWN_OPTION(VV_HK_EDIT_PARTICLES, "Edit Particles")
+ //VV_DROPDOWN_OPTION(VV_HK_DEADCHAT_PLAYS, "Start/Stop Deadchat Plays")
+ //VV_DROPDOWN_OPTION(VV_HK_ADD_FANTASY_AFFIX, "Add Fantasy Affix")
+
+/atom/movable/vv_do_topic(list/href_list)
+ . = ..()
+
+ if(!.)
+ return
+
+ //if(href_list[VV_HK_OBSERVE_FOLLOW])
+ // if(!check_rights(R_ADMIN))
+ // return
+ // usr.client?.admin_follow(src)
+
+ if(href_list[VV_HK_GET_MOVABLE])
+ if(!check_rights(R_ADMIN))
+ return
+ if(QDELETED(src))
+ return
+ forceMove(get_turf(usr))
+
+ if(href_list[VV_HK_EDIT_PARTICLES] && check_rights(R_VAREDIT))
+ var/client/C = usr.client
+ C?.open_particle_editor(src)
+
+ //if(href_list[VV_HK_DEADCHAT_PLAYS] && check_rights(R_FUN))
+ // if(tgui_alert(usr, "Allow deadchat to control [src] via chat commands?", "Deadchat Plays [src]", list("Allow", "Cancel")) != "Allow")
+ // return
+ // // Alert is async, so quick sanity check to make sure we should still be doing this.
+ // if(QDELETED(src))
+ // return
+ // // This should never happen, but if it does it should not be silent.
+ // if(deadchat_plays() == COMPONENT_INCOMPATIBLE)
+ // to_chat(usr, span_warning("Deadchat control not compatible with [src]."))
+ // CRASH("deadchat_control component incompatible with object of type: [type]")
+ // to_chat(usr, span_notice("Deadchat now control [src]."))
+ // log_admin("[key_name(usr)] has added deadchat control to [src]")
+ // message_admins(span_notice("[key_name(usr)] has added deadchat control to [src]"))
diff --git a/code/game/atoms_movable_vr.dm b/code/game/atoms_movable_vr.dm
deleted file mode 100644
index 94f01d7b7f..0000000000
--- a/code/game/atoms_movable_vr.dm
+++ /dev/null
@@ -1,12 +0,0 @@
-/atom/movable/proc/Bump_vr(var/atom/A, yes)
- return
-
-/atom/movable/vv_get_dropdown()
- . = ..()
- VV_DROPDOWN_OPTION("move_atom", "Move To Coordinate")
-
-/atom/vv_do_topic(list/href_list)
- . = ..()
- IF_VV_OPTION("move_atom")
- usr.client.cmd_admin_move_atom(src)
- href_list["datumrefresh"] = "\ref[src]"
diff --git a/code/game/mecha/medical/odysseus.dm b/code/game/mecha/medical/odysseus.dm
index d6dd6cce3e..16ade2a469 100644
--- a/code/game/mecha/medical/odysseus.dm
+++ b/code/game/mecha/medical/odysseus.dm
@@ -48,9 +48,9 @@
set name = "Set client perspective."
set category = "Exosuit Interface"
set src = usr.loc
- var/perspective = input(usr, "Select a perspective type.",
+ var/perspective = tgui_input_list(usr, "Select a perspective type.",
"Client perspective",
- occupant.client.perspective) in list(MOB_PERSPECTIVE,EYE_PERSPECTIVE)
+ list(MOB_PERSPECTIVE,EYE_PERSPECTIVE), occupant.client.perspective)
to_world("[perspective]")
occupant.client.perspective = perspective
return
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index b57e5d8b55..fc4b7e489c 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -231,3 +231,55 @@
if(contaminated && clean_types & CLEAN_RAD) // Phoron and stuff, washing machine needed
contaminated = FALSE
cut_overlay(contamination_overlay)
+
+/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")
+
+/obj/vv_do_topic(list/href_list)
+ . = ..()
+
+ if(!.)
+ return
+
+ //if(href_list[VV_HK_OSAY])
+ // return SSadmin_verbs.dynamic_invoke_verb(usr, /datum/admin_verb/object_say, src)
+
+ if(href_list[VV_HK_MASS_DEL_TYPE])
+ if(!check_rights(R_DEBUG|R_SERVER))
+ return
+ var/action_type = tgui_alert(usr, "Strict type ([type]) or type and all subtypes?",,list("Strict type","Type and subtypes","Cancel"))
+ if(action_type == "Cancel" || !action_type)
+ return
+ if(tgui_alert(usr, "Are you really sure you want to delete all objects of type [type]?",,list("Yes","No")) != "Yes")
+ return
+ if(tgui_alert(usr, "Second confirmation required. Delete?",,list("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(span_notice("[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(span_notice("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) "))
diff --git a/code/game/world.dm b/code/game/world.dm
index fc0f79f6fc..e6f88adc33 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -184,13 +184,13 @@ var/world_topic_spam_protect_time = world.timeofday
active++
s["players"] = players.len
- s["playerlist"] = list2params(players)
+ //s["playerlist"] = list2params(players)
s["active_players"] = active
var/list/adm = get_admin_counts()
var/list/presentmins = adm["present"]
var/list/afkmins = adm["afk"]
s["admins"] = presentmins.len + afkmins.len //equivalent to the info gotten from adminwho
- s["adminlist"] = list2params(admins)
+ //s["adminlist"] = list2params(admins)
else // Legacy.
var/n = 0
var/admins = 0
@@ -291,84 +291,6 @@ var/world_topic_spam_protect_time = world.timeofday
else
return "unknown"
- else if(copytext(T,1,5) == "info")
- var/input[] = params2list(T)
- var/password = CONFIG_GET(string/comms_password)
- if(!password || input["key"] != password)
- if(world_topic_spam_protect_ip == addr && abs(world_topic_spam_protect_time - world.time) < 50)
-
- spawn(50)
- world_topic_spam_protect_time = world.time
- return
-
- world_topic_spam_protect_time = world.time
- world_topic_spam_protect_ip = addr
-
- return "Bad Key"
-
- var/list/search = params2list(input["info"])
- var/list/ckeysearch = list()
- for(var/text in search)
- ckeysearch += ckey(text)
-
- var/list/match = list()
-
- for(var/mob/M in mob_list)
- var/strings = list(M.name, M.ckey)
- if(M.mind)
- strings += M.mind.assigned_role
- strings += M.mind.special_role
- for(var/text in strings)
- if(ckey(text) in ckeysearch)
- match[M] += 10 // an exact match is far better than a partial one
- else
- for(var/searchstr in search)
- if(findtext(text, searchstr))
- match[M] += 1
-
- var/maxstrength = 0
- for(var/mob/M in match)
- maxstrength = max(match[M], maxstrength)
- for(var/mob/M in match)
- if(match[M] < maxstrength)
- match -= M
-
- if(!match.len)
- return "No matches"
- else if(match.len == 1)
- var/mob/M = match[1]
- var/info = list()
- info["key"] = M.key
- info["name"] = M.name == M.real_name ? M.name : "[M.name] ([M.real_name])"
- info["role"] = M.mind ? (M.mind.assigned_role ? M.mind.assigned_role : "No role") : "No mind"
- var/turf/MT = get_turf(M)
- info["loc"] = M.loc ? "[M.loc]" : "null"
- info["turf"] = MT ? "[MT] @ [MT.x], [MT.y], [MT.z]" : "null"
- info["area"] = MT ? "[MT.loc]" : "null"
- info["antag"] = M.mind ? (M.mind.special_role ? M.mind.special_role : "Not antag") : "No mind"
- info["hasbeenrev"] = M.mind ? M.mind.has_been_rev : "No mind"
- info["stat"] = M.stat
- info["type"] = M.type
- if(isliving(M))
- var/mob/living/L = M
- info["damage"] = list2params(list(
- oxy = L.getOxyLoss(),
- tox = L.getToxLoss(),
- fire = L.getFireLoss(),
- brute = L.getBruteLoss(),
- clone = L.getCloneLoss(),
- brain = L.getBrainLoss()
- ))
- else
- info["damage"] = "non-living"
- info["gender"] = M.gender
- return list2params(info)
- else
- var/list/ret = list()
- for(var/mob/M in match)
- ret[M.key] = M.name
- return list2params(ret)
-
else if(copytext(T,1,9) == "adminmsg")
/*
We got an adminmsg from IRC bot lets split the input then validate the input.
@@ -424,50 +346,6 @@ var/world_topic_spam_protect_time = world.timeofday
return "Message Successful"
- else if(copytext(T,1,6) == "notes")
- /*
- We got a request for notes from the IRC Bot
- expected output:
- 1. notes = ckey of person the notes lookup is for
- 2. validationkey = the key the bot has, it should match the gameservers commspassword in it's configuration.
- */
- var/input[] = params2list(T)
- var/password = CONFIG_GET(string/comms_password)
- if(!password || input["key"] != password)
- if(world_topic_spam_protect_ip == addr && abs(world_topic_spam_protect_time - world.time) < 50)
-
- spawn(50)
- world_topic_spam_protect_time = world.time
- return
-
- world_topic_spam_protect_time = world.time
- world_topic_spam_protect_ip = addr
- return "Bad Key"
-
- return show_player_info_irc(ckey(input["notes"]))
-
- else if(copytext(T,1,4) == "age")
- var/input[] = params2list(T)
- var/password = CONFIG_GET(string/comms_password)
- if(!password || input["key"] != password)
- if(world_topic_spam_protect_ip == addr && abs(world_topic_spam_protect_time - world.time) < 50)
- spawn(50)
- world_topic_spam_protect_time = world.time
- return
-
- world_topic_spam_protect_time = world.time
- world_topic_spam_protect_ip = addr
- return "Bad Key"
-
- var/age = get_player_age(input["age"])
- if(isnum(age))
- if(age >= 0)
- return "[age]"
- else
- return "Ckey not found"
- else
- return "Database connection failed or not set up"
-
/// Returns TRUE if the world should do a TGS hard reboot.
/world/proc/check_hard_reboot()
if(!TgsAvailable())
@@ -539,23 +417,6 @@ var/world_topic_spam_protect_time = world.timeofday
fdel(F)
F << the_mode
-
-/hook/startup/proc/loadMOTD()
- world.load_motd()
- return 1
-
-/world/proc/load_motd()
- GLOB.join_motd = GLOB.is_valid_url.Replace(file2text("config/motd.txt"), span_linkify("$1"))
-
-/* Replaced with configuration controller
-/proc/load_configuration()
- config = new /datum/configuration()
- config.load("config/config.txt")
- config.load("config/game_options.txt","game_options")
- config.loadsql("config/dbconfig.txt")
- config.loadforumsql("config/forumdbconfig.txt")
-*/
-
/hook/startup/proc/loadMods()
world.load_mods()
return 1
@@ -771,33 +632,6 @@ var/failed_old_db_connections = 0
else
. += "[world.address]:[world.port]"
-var/global/game_id = null
-
-/hook/startup/proc/generate_gameid()
- if(game_id != null)
- return
- game_id = ""
-
- var/list/c = list(
- "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
- "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
- "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
- "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
- "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"
- )
- var/l = c.len
-
- var/t = world.timeofday
- for(var/_ = 1 to 4)
- game_id = "[c[(t % l) + 1]][game_id]"
- t = round(t / l)
- game_id = "-[game_id]"
- t = round(world.realtime / (10 * 60 * 60 * 24))
- for(var/_ = 1 to 3)
- game_id = "[c[(t % l) + 1]][game_id]"
- t = round(t / l)
- return 1
-
/proc/auxtools_stack_trace(msg)
CRASH(msg)
diff --git a/code/matrices/color_matrix.dm b/code/matrices/color_matrix.dm
index 1e2988ea9b..77cd796233 100644
--- a/code/matrices/color_matrix.dm
+++ b/code/matrices/color_matrix.dm
@@ -13,10 +13,6 @@ list(-1,0,0,0, 0,-1,0,0, 0,0,-1,0, 0,0,0,1, 1,1,1,0)
list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0,0,0)
*/
-/// Does nothing.
-/proc/color_matrix_identity()
- return list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0)
-
/**
* Adds/subtracts overall lightness.
* 0 is identity, 1 makes everything white, -1 makes everything black.
@@ -41,7 +37,7 @@ list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0
*/
/proc/color_matrix_saturation_percent(percent)
if(percent == 0)
- return color_matrix_identity()
+ return COLOR_MATRIX_IDENTITY
percent = clamp(percent, -100, 100)
if(percent > 0)
percent *= 3
@@ -85,7 +81,7 @@ list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0
10.0)
percent = clamp(percent, -100, 100)
if(percent == 0)
- return color_matrix_identity()
+ return COLOR_MATRIX_IDENTITY
var/x = 0
if (percent < 0)
@@ -127,7 +123,7 @@ list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0
*/
/proc/color_matrix_rotation(angle)
if(angle == 0)
- return color_matrix_identity()
+ return COLOR_MATRIX_IDENTITY
angle = clamp(angle, -180, 180)
var/cos = cos(angle)
var/sin = sin(angle)
@@ -185,9 +181,9 @@ list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0
*/
/proc/color_matrix_add(list/A, list/B)
if(!istype(A) || !istype(B))
- return color_matrix_identity()
+ return COLOR_MATRIX_IDENTITY
if(A.len != 20 || B.len != 20)
- return color_matrix_identity()
+ return COLOR_MATRIX_IDENTITY
var/list/output = list()
output.len = 20
for(var/value in 1 to 20)
@@ -199,9 +195,9 @@ list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0
*/
/proc/color_matrix_multiply(list/A, list/B)
if(!istype(A) || !istype(B))
- return color_matrix_identity()
+ return COLOR_MATRIX_IDENTITY
if(A.len != 20 || B.len != 20)
- return color_matrix_identity()
+ return COLOR_MATRIX_IDENTITY
var/list/output = list()
output.len = 20
var/x = 1
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index e6c5e5ab26..25085f7176 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -39,112 +39,105 @@ GLOBAL_VAR_INIT(floorIsLava, 0)
///////////////////////////////////////////////////////////////////////////////////////////////Panels
-/datum/admins/proc/show_player_panel(var/mob/M in mob_list)
- set category = "Admin.Game"
- set name = "Show Player Panel"
- set desc="Edit player (respawn, ban, heal, etc)"
+ADMIN_VERB_ONLY_CONTEXT_MENU(show_player_panel, R_HOLDER, "Show Player Panel", mob/player in world)
+ log_admin("[key_name(user)] checked the individual player panel for [key_name(player)][isobserver(user.mob)?"":" while in game"].")
- if(!M)
- to_chat(usr, "You seem to be selecting a mob that doesn't exist anymore.")
- return
- if (!istype(src,/datum/admins))
- src = usr.client.holder
- if (!istype(src,/datum/admins))
- to_chat(usr, "Error: you are not an admin!")
+ if(!player)
+ to_chat(user, "You seem to be selecting a mob that doesn't exist anymore.")
return
- var/body = "Options panel for " + span_bold("[M]")
- if(M.client)
- body += " played by " + span_bold("[M.client]")
- body += "\[[M.client.holder ? M.client.holder.rank_names() : "Player"] \]"
+ var/body = "Options panel for " + span_bold("[player]")
+ if(player.client)
+ body += " played by " + span_bold("[player.client]")
+ body += "\[[player.client.holder ? player.client.holder.rank_names() : "Player"] \]"
- if(isnewplayer(M))
- body += span_bold(" Hasn't Entered Game")
+ if(isnewplayer(player))
+ body += span_bold("Hasn't Entered Game")
else
- body += " \[Heal \] "
+ body += " \[Heal \] "
- if(M.client)
- body += " " + span_bold("First connection:") + "[M.client.player_age] days ago"
- body += " " + span_bold("BYOND account created:") + "[M.client.account_join_date]"
- body += " " + span_bold("BYOND account age (days):") + "[M.client.account_age]"
+ if(player.client)
+ body += " " + span_bold("First connection:") + "[player.client.player_age] days ago"
+ body += " " + span_bold("BYOND account created:") + "[player.client.account_join_date]"
+ body += " " + span_bold("BYOND account age (days):") + "[player.client.account_age]"
body += {"
\[
- VV -
- TP -
- PM -
- SM -
- [admin_jump_link(M, src)]\]
- "} + span_bold("Mob type:") + {"[M.type]
- "} + span_bold("Inactivity time:") + {" [M.client ? "[M.client.inactivity/600] minutes" : "Logged out"]
- Kick |
- Warn |
- Ban |
- Jobban |
- Notes
+ VV -
+ TP -
+ PM -
+ SM -
+ [admin_jump_link(player, src)]\]
+ "} + span_bold("Mob type:") + {"[player.type]
+ "} + span_bold("Inactivity time:") + {" [player.client ? "[player.client.inactivity/600] minutes" : "Logged out"]
+ Kick |
+ Warn |
+ Ban |
+ Jobban |
+ Notes
"}
- if(M.client)
- body += "| Prison | "
- body += "\ Send back to Lobby | "
- var/muted = M.client.prefs.muted
+ if(player.client)
+ body += "| Prison | "
+ body += "\ Send back to Lobby | "
+ var/muted = player.client.prefs.muted
body += {" "} + span_bold("Mute: ") + {"
- \[[(muted & MUTE_IC) ? span_red("IC") : span_blue("IC")] |
- [(muted & MUTE_OOC) ? span_red("OOC") : span_blue("OOC")] |
- [(muted & MUTE_LOOC) ? span_red("LOOC") : span_blue("LOOC")] |
- [(muted & MUTE_PRAY) ? span_red("PRAY") : span_blue("PRAY")] |
- [(muted & MUTE_ADMINHELP) ? span_red("ADMINHELP") : span_blue("ADMINHELP")] |
- [(muted & MUTE_DEADCHAT) ? span_red("DEADCHAT") : span_blue("DEADCHAT")] \]
- ([(muted & MUTE_ALL) ? span_red("toggle all") : span_blue("toggle all")] )
+ \[[(muted & MUTE_IC) ? span_red("IC") : span_blue("IC")] |
+ [(muted & MUTE_OOC) ? span_red("OOC") : span_blue("OOC")] |
+ [(muted & MUTE_LOOC) ? span_red("LOOC") : span_blue("LOOC")] |
+ [(muted & MUTE_PRAY) ? span_red("PRAY") : span_blue("PRAY")] |
+ [(muted & MUTE_ADMINHELP) ? span_red("ADMINHELP") : span_blue("ADMINHELP")] |
+ [(muted & MUTE_DEADCHAT) ? span_red("DEADCHAT") : span_blue("DEADCHAT")] \]
+ ([(muted & MUTE_ALL) ? span_red("toggle all") : span_blue("toggle all")] )
"}
body += {"
- "} + span_bold("Jump to") + {" |
- Get |
- Send To
+ "} + span_bold("Jump to") + {" |
+ Get |
+ Send To
- [check_rights(R_ADMIN|R_MOD|R_EVENT,0) ? "Traitor panel | " : "" ]
- Narrate to |
- Subtle message
+ [check_rights(R_ADMIN|R_MOD|R_EVENT,0) ? "Traitor panel | " : "" ]
+ Narrate to |
+ Subtle message
"}
- if (M.client)
- if(!isnewplayer(M))
+ if (player.client)
+ if(!isnewplayer(player))
body += " "
body += span_bold("Transformation:")
body += " "
//Monkey
- if(issmall(M))
+ if(issmall(player))
body += span_bold("Monkeyized") + " | "
else
- body += "Monkeyize | "
+ body += "Monkeyize | "
//Corgi
- if(iscorgi(M))
+ if(iscorgi(player))
body += span_bold("Corgized") + " | "
else
- body += "Corgize | "
+ body += "Corgize | "
//AI / Cyborg
- if(isAI(M))
+ if(isAI(player))
body += span_bold("Is an AI ")
- else if(ishuman(M))
- body += {"Make AI |
- Make Robot |
- Make Alien
+ else if(ishuman(player))
+ body += {"Make AI |
+ Make Robot |
+ Make Alien
"}
//Simple Animals
- if(isanimal(M))
- body += "Re-Animalize | "
+ if(isanimal(player))
+ body += "Re-Animalize | "
else
- body += "Animalize | "
+ body += "Animalize | "
- body += "Respawn | "
+ body += "Respawn | "
// DNA2 - Admin Hax
- if(M.dna && iscarbon(M))
+ if(player.dna && iscarbon(player))
body += " "
body += span_bold("DNA Blocks:") + "
1 2 3 4 5 "
var/bname
@@ -165,7 +158,7 @@ GLOBAL_VAR_INIT(floorIsLava, 0)
bname = ""
body += ""
if(bname)
- var/bstate=(bname in M.active_genes) // Traitgenes more reliable way to check gene states
+ var/bstate=(bname in player.active_genes) // Traitgenes more reliable way to check gene states
// Traitgenes show trait linked names on mouseover
var/tname = bname
if(istype(gene,/datum/gene/trait))
@@ -173,11 +166,11 @@ GLOBAL_VAR_INIT(floorIsLava, 0)
tname = T.get_name()
if(bstate)
bname = span_green(bname)
- else if(!bstate && M.dna.GetSEState(block)) // Gene isn't active, but the dna says it is... Was blocked by another gene!
+ else if(!bstate && player.dna.GetSEState(block)) // Gene isn't active, but the dna says it is... Was blocked by another gene!
bname = span_orange(bname)
else
bname = span_red(bname)
- body += "[bname] [block] " // Traitgenes edit - show trait linked names on mouseover
+ body += "[bname] [block] " // Traitgenes edit - show trait linked names on mouseover
else
body += "[block]"
body+=" "
@@ -185,45 +178,45 @@ GLOBAL_VAR_INIT(floorIsLava, 0)
body += {"
"} + span_bold("Rudimentary transformation:") + span_normal(" These transformations only create a new mob type and copy stuff over. They do not take into account MMIs and similar mob-specific things. The buttons in 'Transformations' are preferred, when possible.") + {"
- Observer |
- \[ Xenos: Larva
- Drone
- Hunter
- Sentinel
- Queen \] |
- \[ Crew: Human
- Unathi
- Tajaran
- Skrell \] | \[
- Nymph
- Diona \] |
- \[ slime: Baby ,
- Adult \]
- Monkey |
- Cyborg |
- Cat |
- Runtime |
- Corgi |
- Ian |
- Crab |
- Coffee |
- \[ Construct: Armoured ,
- Builder ,
- Wraith \]
- Shade
+ Observer |
+ \[ Xenos: Larva
+ Drone
+ Hunter
+ Sentinel
+ Queen \] |
+ \[ Crew: Human
+ Unathi
+ Tajaran
+ Skrell \] | \[
+ Nymph
+ Diona \] |
+ \[ slime: Baby ,
+ Adult \]
+ Monkey |
+ Cyborg |
+ Cat |
+ Runtime |
+ Corgi |
+ Ian |
+ Crab |
+ Coffee |
+ \[ Construct: Armoured ,
+ Builder ,
+ Wraith \]
+ Shade
"}
body += {"
"} + span_bold("Other actions:") + {"
- Forcesay
+ Forcesay
"}
- if (M.client)
+ if (player.client)
body += {" |
- Thunderdome 1 |
- Thunderdome 2 |
- Thunderdome Admin |
- Thunderdome Observer |
+ Thunderdome 1 |
+ Thunderdome 2 |
+ Thunderdome Admin |
+ Thunderdome Observer |
"}
// language toggles
body += " " + span_bold("Languages:") + " "
@@ -233,17 +226,17 @@ GLOBAL_VAR_INIT(floorIsLava, 0)
if(!(L.flags & INNATE))
if(!f) body += " | "
else f = 0
- if(L in M.languages)
+ if(L in player.languages)
k = span_green(k)
- body += "[k] "
+ body += "[k] "
else
k = span_red(k)
- body += "[k] "
+ body += "[k] "
body += {" "}
- var/datum/browser/popup = new(owner, "adminplayeropts", "Edit Player", 550, 515)
- popup.add_head_content("Options for [M.key] ")
+ var/datum/browser/popup = new(user, "adminplayeropts", "Edit Player", 550, 515)
+ popup.add_head_content("Options for [player.key] ")
popup.set_content(body)
popup.open()
diff --git a/code/modules/admin/admin_verb_lists_vr.dm b/code/modules/admin/admin_verb_lists_vr.dm
index 1a0cf10609..009f51b1b3 100644
--- a/code/modules/admin/admin_verb_lists_vr.dm
+++ b/code/modules/admin/admin_verb_lists_vr.dm
@@ -14,13 +14,11 @@ var/list/admin_verbs_admin = list(
/datum/admins/proc/intercom, //send a fake intercom message, like an arrivals announcement,
/datum/admins/proc/intercom_convo, //send a fake intercom conversation, like an ATC exchange,
/client/proc/admin_ghost, //allows us to ghost/reenter body at will,
- /datum/admins/proc/show_player_panel, //shows an interface for individual players, with various links (links require additional flags, //VOREStation Add,
/client/proc/player_panel_new, //shows an interface for all players, with links to various panels, //VOREStation Add,
/client/proc/player_panel, //VOREStation Add,
/client/proc/hide_verbs, //hides all our adminverbs, //VOREStation Add,
/client/proc/hide_most_verbs, //hides all our hideable adminverbs, //VOREStation Add,
/client/proc/debug_variables, //allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify, //VOREStation Add,
- /client/proc/mark_datum_mapview, //VOREStation Add,
/client/proc/cmd_check_new_players, //allows us to see every new player, //VOREStation Add,
/client/proc/toggle_view_range, //changes how far we can see,
/client/proc/cmd_admin_pm_context, //right-click adminPM interface,
@@ -106,7 +104,6 @@ var/list/admin_verbs_sounds = list(
var/list/admin_verbs_fun = list(
/client/proc/object_talk,
/datum/admins/proc/cmd_admin_dress,
- /client/proc/cmd_admin_gib_self,
/client/proc/drop_bomb,
/client/proc/everyone_random,
/client/proc/cinematic,
@@ -210,20 +207,16 @@ var/list/admin_verbs_debug = list(
/client/proc/delete_random_map,
/client/proc/show_plant_genes,
/client/proc/enable_debug_verbs,
- /client/proc/callproc,
- /client/proc/callproc_datum,
/client/proc/Jump,
/client/proc/jumptomob,
/client/proc/jumptocoord,
/client/proc/dsay,
/client/proc/admin_ghost, //allows us to ghost/reenter body at will,
- /datum/admins/proc/show_player_panel, //shows an interface for individual players, with various links (links require additional flags, //VOREStation Add,
/client/proc/player_panel_new, //shows an interface for all players, with links to various panels, //VOREStation Add,
/client/proc/player_panel, //VOREStation Add,
/client/proc/hide_verbs, //hides all our adminverbs, //VOREStation Add,
/client/proc/hide_most_verbs, //hides all our hideable adminverbs, //VOREStation Add,
/client/proc/debug_variables, //allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify, //VOREStation Add,
- /client/proc/mark_datum_mapview, //VOREStation Add,
/client/proc/cmd_check_new_players, //allows us to see every new player, //VOREStation Add,
/datum/admins/proc/view_runtimes,
// /client/proc/show_gm_status, // VOREStation Edit - We don't use SSgame_master yet.
@@ -244,11 +237,6 @@ var/list/admin_verbs_debug = list(
/client/proc/reload_configuration //CHOMPAdd
)
-var/list/admin_verbs_paranoid_debug = list(
- /client/proc/callproc,
- /client/proc/callproc_datum,
- )
-
//verbs which can be hidden - needs work
var/list/admin_verbs_hideable = list(
// /client/proc/deadchat,
@@ -274,7 +262,6 @@ var/list/admin_verbs_hideable = list(
/client/proc/play_web_sound,
/client/proc/object_talk,
/datum/admins/proc/cmd_admin_dress,
- /client/proc/cmd_admin_gib_self,
/client/proc/drop_bomb,
/client/proc/cinematic,
/datum/admins/proc/toggle_aliens,
@@ -299,8 +286,6 @@ var/list/admin_verbs_hideable = list(
/datum/admins/proc/adspawn,
/datum/admins/proc/adjump,
/client/proc/cmd_admin_list_open_jobs,
- /client/proc/callproc,
- /client/proc/callproc_datum,
/client/proc/Debug2,
/client/proc/reload_admins,
/client/proc/kill_air,
@@ -326,19 +311,16 @@ var/list/admin_verbs_mod = list(
/client/proc/debug_variables, //allows us to -see- the variables of any instance in the game.,
/datum/admins/proc/PlayerNotes,
/client/proc/admin_ghost, //allows us to ghost/reenter body at will,
- /datum/admins/proc/show_player_panel, //shows an interface for individual players, with various links (links require additional flags, //VOREStation Add,
/client/proc/player_panel_new, //shows an interface for all players, with links to various panels, //VOREStation Add,
/client/proc/player_panel, //VOREStation Add,
/client/proc/hide_verbs, //hides all our adminverbs, //VOREStation Add,
/client/proc/hide_most_verbs, //hides all our hideable adminverbs, //VOREStation Add,
/client/proc/debug_variables, //allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify, //VOREStation Add,
- /client/proc/mark_datum_mapview, //VOREStation Add,
/client/proc/cmd_check_new_players, //allows us to see every new player, //VOREStation Add,
/datum/admins/proc/show_player_info,
/datum/admins/proc/show_traitor_panel,
/client/proc/player_panel_new,
/client/proc/dsay,
- /datum/admins/proc/show_player_panel,
/client/proc/check_antagonists,
/client/proc/aooc,
/client/proc/cmd_admin_subtle_message, //send an message to somebody as a 'voice in their head',
@@ -357,13 +339,11 @@ var/list/admin_verbs_event_manager = list(
/client/proc/cmd_admin_pm_context,
/client/proc/cmd_admin_pm_panel,
/client/proc/admin_ghost,
- /datum/admins/proc/show_player_panel, //shows an interface for individual players, with various links (links require additional flags, //VOREStation Add,
/client/proc/player_panel_new, //shows an interface for all players, with links to various panels, //VOREStation Add,
/client/proc/player_panel, //VOREStation Add,
/client/proc/hide_verbs, //hides all our adminverbs, //VOREStation Add,
/client/proc/hide_most_verbs, //hides all our hideable adminverbs, //VOREStation Add,
/client/proc/debug_variables, //allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify, //VOREStation Add,
- /client/proc/mark_datum_mapview, //VOREStation Add,
/client/proc/cmd_check_new_players, //allows us to see every new player, //VOREStation Add,
/datum/admins/proc/show_player_info,
/client/proc/dsay,
@@ -390,7 +370,6 @@ var/list/admin_verbs_event_manager = list(
/client/proc/make_sound,
/client/proc/toggle_random_events,
/datum/admins/proc/cmd_admin_dress,
- /client/proc/cmd_admin_gib_self,
/client/proc/drop_bomb,
/client/proc/cmd_admin_add_freeform_ai_law,
/client/proc/cmd_admin_add_random_ai_law,
@@ -401,8 +380,6 @@ var/list/admin_verbs_event_manager = list(
/datum/admins/proc/call_supply_drop,
/datum/admins/proc/call_drop_pod,
/datum/admins/proc/PlayerNotes,
- /client/proc/callproc,
- /client/proc/callproc_datum,
/datum/admins/proc/change_weather,
/datum/admins/proc/change_time,
/client/proc/cmd_regenerate_asset_cache,
@@ -410,7 +387,6 @@ var/list/admin_verbs_event_manager = list(
/client/proc/cmd_reload_robot_sprite_test,
/client/proc/admin_give_modifier,
/datum/admins/proc/cmd_admin_dress,
- /client/proc/cmd_admin_gib_self,
/datum/admins/proc/set_tcrystals,
/datum/admins/proc/add_tcrystals,
/client/proc/invisimin, //allows our mob to go invisible/visible,
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 37dea3e358..6a87e5f0df 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -2,19 +2,15 @@
// OLD ADMIN VERB SYSTEM
var/rights = holder.rank_flags()
if(rights & R_HOLDER)
- if(rights & R_BUILDMODE) add_verb(src, /client/proc/togglebuildmodeself)
- if(rights & R_ADMIN) add_verb(src, admin_verbs_admin)
+ if(rights & R_BUILDMODE) add_verb(src, /client/proc/togglebuildmodeself)
+ if(rights & R_ADMIN) add_verb(src, admin_verbs_admin)
if(rights & R_FUN) add_verb(src, admin_verbs_fun)
if(rights & R_SERVER) add_verb(src, admin_verbs_server)
- if(rights & R_DEBUG)
- add_verb(src, admin_verbs_debug)
- if(CONFIG_GET(flag/debugparanoid) && !(rights & R_ADMIN))
- remove_verb(src, admin_verbs_paranoid_debug) //Right now it's just callproc but we can easily add others later on.
- if(rights & R_STEALTH) add_verb(src, /client/proc/stealth)
+ if(rights & R_DEBUG) add_verb(src, admin_verbs_debug)
if(rights & R_SOUNDS) add_verb(src, admin_verbs_sounds)
- if(rights & R_SPAWN) add_verb(src, admin_verbs_spawn)
+ if(rights & R_SPAWN) add_verb(src, admin_verbs_spawn)
if(rights & R_MOD) add_verb(src, admin_verbs_mod)
- if(rights & R_EVENT) add_verb(src, admin_verbs_event_manager)
+ if(rights & R_EVENT) add_verb(src, admin_verbs_event_manager)
// NEW ADMIN VERBS SYSTEM
SSadmin_verbs.assosciate_admin(src)
@@ -27,7 +23,6 @@
admin_verbs_fun,
admin_verbs_server,
admin_verbs_debug,
- /client/proc/stealth,
admin_verbs_sounds,
admin_verbs_spawn,
debug_verbs
@@ -217,25 +212,22 @@ ADMIN_VERB(game_panel, R_ADMIN|R_SERVER|R_FUN, "Game Panel", "Look at the state
i = 0
GLOB.stealthminID["[ckey]"] = "@[num2text(num)]"
-/client/proc/stealth()
- set category = "Admin.Game"
- set name = "Stealth Mode"
- if(check_rights(R_HOLDER))
- if(holder.fakekey)
- holder.fakekey = null
- if(isnewplayer(src.mob))
- mob.name = capitalize(ckey)
- else
- var/new_key = ckeyEx(tgui_input_text(usr, "Enter your desired display name.", "Fake Key", key))
- if(!new_key)
- return
- if(length(new_key) >= 26)
- new_key = copytext(new_key, 1, 26)
- holder.fakekey = new_key
- createStealthKey()
- if(isnewplayer(mob))
- mob.name = new_key
- log_and_message_admins("has turned stealth mode [holder.fakekey ? "ON" : "OFF"]", usr)
+ADMIN_VERB(stealth, R_STEALTH, "Stealth Mode", "Toggle stealth.", "Admin.Game")
+ if(user.holder.fakekey)
+ user.holder.fakekey = null
+ if(isnewplayer(user.mob))
+ user.mob.name = capitalize(user.ckey)
+ else
+ var/new_key = ckeyEx(tgui_input_text(user, "Enter your desired display name.", "Fake Key", user.key))
+ if(!new_key)
+ return
+ if(length(new_key) >= 26)
+ new_key = copytext(new_key, 1, 26)
+ user.holder.fakekey = new_key
+ user.createStealthKey()
+ if(isnewplayer(user.mob))
+ user.mob.name = new_key
+ log_and_message_admins("has turned stealth mode [user.holder.fakekey ? "ON" : "OFF"]", usr)
feedback_add_details("admin_verb","SM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
#define MAX_WARNS 3
@@ -532,16 +524,35 @@ ADMIN_VERB(deadmin, R_NONE, "DeAdmin", "Shed your admin powers.", ADMIN_CATEGORY
log_admin("[key_name(usr)] told everyone to man up and deal with it.")
message_admins(span_blue("[key_name_admin(usr)] told everyone to man up and deal with it."), 1)
-/client/proc/give_spell(mob/T as mob in mob_list) // -- Urist
- set category = "Fun.Event Kit"
- set name = "Give Spell"
- set desc = "Gives a spell to a mob."
- var/spell/S = tgui_input_list(usr, "Choose the spell to give to that guy", "ABRAKADABRA", spells)
- if(!S) return
- T.spell_list += new S
+ADMIN_VERB(give_spell, R_FUN, "Give Spell", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, mob/spell_recipient)
+ var/spell/S = tgui_input_list(user, "Choose the spell to give to that guy", "ABRAKADABRA", spells)
+ if(!S)
+ return
+ spell_recipient.spell_list += new S
feedback_add_details("admin_verb","GS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- log_admin("[key_name(usr)] gave [key_name(T)] the spell [S].")
- message_admins(span_blue("[key_name_admin(usr)] gave [key_name(T)] the spell [S]."), 1)
+ log_admin("[key_name(usr)] gave [key_name(spell_recipient)] the spell [S].")
+ message_admins(span_blue("[key_name_admin(usr)] gave [key_name(spell_recipient)] the spell [S]."), 1)
+
+ADMIN_VERB(remove_spell, R_FUN, "Remove Spell", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, mob/removal_target)
+ var/list/target_spell_list = list()
+ for(var/spell/spell in removal_target.spell_list)
+ target_spell_list[spell.name] = spell
+
+ if(!length(target_spell_list))
+ return
+
+ var/chosen_spell = tgui_input_list(user, "Choose the spell to remove from [removal_target]", "ABRAKADABRA", sortList(target_spell_list))
+ if(isnull(chosen_spell))
+ return
+ var/spell/to_remove = target_spell_list[chosen_spell]
+ if(!istype(to_remove))
+ return
+
+ qdel(to_remove)
+ log_admin("[key_name(user)] removed the spell [chosen_spell] from [key_name(removal_target)].")
+ message_admins("[key_name_admin(user)] removed the spell [chosen_spell] from [key_name_admin(removal_target)].")
+ feedback_add_details("admin_verb","RS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ //BLACKBOX_LOG_ADMIN_VERB("Remove Spell")
ADMIN_VERB(debug_statpanel, R_DEBUG, "Debug Stat Panel", "Toggles local debug of the stat panel", "Debug.Misc")
user.stat_panel.send_message("create_debug")
diff --git a/code/modules/admin/callproc/callproc.dm b/code/modules/admin/callproc/callproc.dm
index d5cc688c26..e080a1f535 100644
--- a/code/modules/admin/callproc/callproc.dm
+++ b/code/modules/admin/callproc/callproc.dm
@@ -1,75 +1,169 @@
-/client/proc/callproc()
- set category = "Debug.Events"
- set name = "Advanced ProcCall"
- set waitfor = 0
+GLOBAL_DATUM_INIT(AdminProcCallHandler, /mob/proccall_handler, new())
+GLOBAL_PROTECT(AdminProcCallHandler)
+
+/// Used to handle proccalls called indirectly by an admin (e.g. tgs, circuits).
+/// Has to be a mob because IsAdminAdvancedProcCall() checks usr, which is a mob variable.
+/// So usr is set to this for any proccalls that don't have any usr mob/client to refer to.
+/mob/proccall_handler
+ name = "ProcCall Handler"
+ desc = "If you are seeing this, tell a coder."
+
+ var/list/callers = list()
+
+ invisibility = INVISIBILITY_ABSTRACT
+ density = FALSE
+
+/// Adds a caller.
+/mob/proccall_handler/proc/add_caller(caller_name)
+ callers += caller_name
+ name = "[initial(name)] ([callers.Join(") (")])"
+
+/// Removes a caller.
+/mob/proccall_handler/proc/remove_caller(caller_name)
+ callers -= caller_name
+ name = "[initial(name)] ([callers.Join(") (")])"
+
+/mob/proccall_handler/Initialize(mapload)
+ . = ..()
+ if(GLOB.AdminProcCallHandler && GLOB.AdminProcCallHandler != src)
+ return INITIALIZE_HINT_QDEL
+ GLOB.AdminProcCallHandler = src
+
+/mob/proccall_handler/vv_edit_var(var_name, var_value)
+ if(GLOB.AdminProcCallHandler != src)
+ return ..()
+ return FALSE
+
+/mob/proccall_handler/vv_do_topic(list/href_list)
+ if(GLOB.AdminProcCallHandler != src)
+ return ..()
+ return FALSE
+
+/mob/proccall_handler/CanProcCall(procname)
+ if(GLOB.AdminProcCallHandler != src)
+ return ..()
+ return FALSE
+
+// Shit will break if this is allowed to be deleted
+/mob/proccall_handler/Destroy(force)
+ if(GLOB.AdminProcCallHandler != src)
+ return ..()
+ if(!force)
+ stack_trace("Attempted deletion on [type] - [name], aborting.")
+ return QDEL_HINT_LETMELIVE
+ return ..()
+
+/**
+ * Handles a userless proccall, used by circuits.
+ *
+ * Arguments:
+ * * user - a string used to identify the user
+ * * target - the target to proccall on
+ * * proc - the proc to call
+ * * arguments - any arguments
+ */
+/proc/HandleUserlessProcCall(user, datum/target, procname, list/arguments)
+ if(IsAdminAdvancedProcCall())
+ return
+ var/mob/proccall_handler/handler = GLOB.AdminProcCallHandler
+ handler.add_caller(user)
+ var/lastusr = usr
+ usr = handler
+ . = WrapAdminProcCall(target, procname, arguments)
+ usr = lastusr
+ handler.remove_caller(user)
+
+/**
+ * Handles a userless sdql, used by circuits and tgs.
+ *
+ * Arguments:
+ * * user - a string used to identify the user
+ * * query_text - the query text
+ */
+/proc/HandleUserlessSDQL(user, query_text)
+ if(IsAdminAdvancedProcCall())
+ return
+ var/mob/proccall_handler/handler = GLOB.AdminProcCallHandler
+ handler.add_caller(user)
+ var/lastusr = usr
+ usr = handler
+ . = world.SDQL2_query(query_text, user, user)
+ usr = lastusr
+ handler.remove_caller(user)
+
+ADMIN_VERB(advanced_proc_call, R_DEBUG, "Advanced ProcCall", "Call a proc on any datum in the server.", ADMIN_CATEGORY_DEBUG)
+ user.callproc_blocking()
+
+/client/proc/callproc_blocking(list/get_retval)
if(!check_rights(R_DEBUG))
return
- var/datum/target = null
- var/targetselected = 0
- var/returnval = null
+ var/datum/target
+ var/targetselected = FALSE
+ var/returnval
- switch(tgui_alert(usr, "Proc owned by something?","Call Proc",list("Yes","No")))
- if(null)
- return
+ switch(tgui_alert(usr, "Proc owned by something?",,list("Yes","No")))
if("Yes")
- targetselected = 1
- var/list/value = vv_get_value(default_class = VV_ATOM_REFERENCE, classes = list(VV_ATOM_REFERENCE, VV_DATUM_REFERENCE, VV_MOB_REFERENCE, VV_CLIENT))
+ 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, span_danger("Invalid target."), confidential = TRUE)
+ return
if("No")
target = null
- targetselected = 0
+ targetselected = FALSE
- var/procname = tgui_input_text(usr, "Proc path, eg: /proc/fake_blood","Path:", null)
- if(!procname)
+ var/procpath = tgui_input_text(usr, "Proc path, eg: /proc/fake_blood","Path:", null)
+ if(!procpath)
return
- //hascall() doesn't support proc paths (eg: /proc/gib(), it only supports "gib")
- var/testname = procname
+ //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)
- //Find one of the 3 possible ways they could have written /proc/PROCNAME
- if(findtext(procname, "/proc/"))
- testname = replacetext(procname, "/proc/", "")
- else if(findtext(procname, "/proc"))
- testname = replacetext(procname, "/proc", "")
- else if(findtext(procname, "proc/"))
- testname = replacetext(procname, "proc/", "")
- //Clear out any parenthesis if they're a dummy
- testname = replacetext(testname, "()", "")
-
- if(targetselected && !hascall(target,testname))
- to_chat(usr, span_filter_adminlog(span_red("Error: callproc(): type [target.type] has no proc named [procname].")))
- return
- else
- var/procpath = text2path(procname)
- if (!procpath)
- to_chat(usr, span_filter_adminlog(span_red("Error: callproc(): proc [procname] does not exist. (Did you forget the /proc/ part?)")))
+ if(!hascall(target, procname))
+ to_chat(usr, span_warning("Error: callproc(): type [target.type] has no [proctype] named [procpath]."), confidential = TRUE)
return
+ else
+ procpath = "/[proctype]/[procname]"
+ if(!text2path(procpath))
+ to_chat(usr, span_warning("Error: callproc(): [procpath] does not exist."), confidential = TRUE)
+ return
+
var/list/lst = get_callproc_args()
if(!lst)
return
if(targetselected)
if(!target)
- to_chat(usr, span_filter_adminlog(span_red("Error: callproc(): owner of proc no longer exists.")))
+ to_chat(usr, span_red("Error: callproc(): owner of proc no longer exists."), confidential = TRUE)
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.
+ 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.
+ 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
+ feedback_add_details("admin_verb","APC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ //BLACKBOX_LOG_ADMIN_VERB("Advanced ProcCall")
+ if(get_retval)
+ get_retval += returnval
. = get_callproc_returnval(returnval, procname)
if(.)
- to_chat(usr, .)
- feedback_add_details("admin_verb","APC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ to_chat(usr, ., confidential = TRUE)
GLOBAL_VAR(AdminProcCaller)
GLOBAL_PROTECT(AdminProcCaller)
@@ -81,43 +175,47 @@ GLOBAL_VAR(LastAdminCalledTarget)
GLOBAL_PROTECT(LastAdminCalledTarget)
GLOBAL_VAR(LastAdminCalledProc)
GLOBAL_PROTECT(LastAdminCalledProc)
-GLOBAL_LIST_EMPTY(AdminProcCallSpamPrevention)
-GLOBAL_PROTECT(AdminProcCallSpamPrevention)
+/// Wrapper for proccalls where the datum is flagged as vareditted
/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
if(target && procname == "Del")
- to_chat(usr, span_filter_adminlog("Calling Del() is not allowed"))
+ to_chat(usr, "Calling Del() is not allowed", confidential = TRUE)
return
if(target != GLOBAL_PROC && !target.CanProcCall(procname))
- to_chat(usr, span_filter_adminlog("Proccall on [target.type]/proc/[procname] is disallowed!"))
+ to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!", confidential = TRUE)
return
var/current_caller = GLOB.AdminProcCaller
- var/ckey = usr ? usr.client.ckey : GLOB.AdminProcCaller
- if(!ckey)
+ var/user_identifier = usr ? usr.client?.ckey : GLOB.AdminProcCaller
+ var/is_remote_handler = usr == GLOB.AdminProcCallHandler
+ if(is_remote_handler)
+ user_identifier = GLOB.AdminProcCallHandler.name
+
+ if(!user_identifier)
CRASH("WrapAdminProcCall with no ckey: [target] [procname] [english_list(arguments)]")
- if(current_caller && current_caller != ckey)
- if(!GLOB.AdminProcCallSpamPrevention[ckey])
- to_chat(usr, span_adminnotice("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, span_adminnotice("Running your proc"))
- GLOB.AdminProcCallSpamPrevention -= ckey
- else
- UNTIL(!GLOB.AdminProcCaller)
+
+ if(!is_remote_handler && current_caller && current_caller != user_identifier)
+ to_chat(usr, span_adminnotice("Another set of admin called procs are still running. Try again later."), confidential = TRUE)
+ return
+
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
+ GLOB.LastAdminCalledTargetRef = REF(target)
+
+ if(!is_remote_handler)
+ GLOB.AdminProcCaller = user_identifier //if this runtimes, too bad for you
+ ++GLOB.AdminProcCallCount
+ . = world.WrapAdminProcCall(target, procname, arguments)
+ GLOB.AdminProcCallCount--
+ if(GLOB.AdminProcCallCount == 0)
+ GLOB.AdminProcCaller = null
+ else
+ . = world.WrapAdminProcCall(target, procname, arguments)
//adv proc call this, ya nerds
/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
if(target == GLOBAL_PROC)
- return call(procname)(arglist(arguments))
+ return call("/proc/[procname]")(arglist(arguments))
else if(target != world)
return call(target, procname)(arglist(arguments))
else
@@ -127,66 +225,53 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
#ifdef TESTING
return FALSE
#else
- return usr && usr.client && GLOB.AdminProcCaller == usr.client.ckey
+ return (GLOB.AdminProcCaller && GLOB.AdminProcCaller == usr?.client?.ckey) || (GLOB.AdminProcCallHandler && usr == GLOB.AdminProcCallHandler)
#endif
-/client/proc/callproc_datum(datum/A as null|area|mob|obj|turf)
- set category = "Debug.Events"
- set name = "Atom ProcCall"
- set waitfor = 0
-
- if(!check_rights(R_DEBUG))
- return
-
- var/procname = tgui_input_text(usr, "Proc name, eg: fake_blood","Proc:")
+ADMIN_VERB_ONLY_CONTEXT_MENU(call_proc_datum, R_DEBUG, "Atom ProcCall", datum/thing as null|area|mob|obj|turf)
+ var/procname = tgui_input_text(user, "Proc name, eg: fake_blood","Proc:", null)
if(!procname)
return
- if(!hascall(A,procname))
- to_chat(usr, span_filter_adminlog(span_red("Error: callproc_datum(): type [A.type] has no proc named [procname].")))
+ if(!hascall(thing, procname))
+ to_chat(user, span_red("Error: callproc_datum(): type [thing.type] has no proc named [procname]."), confidential = TRUE)
return
- var/list/lst = get_callproc_args()
+ var/list/lst = user.get_callproc_args()
if(!lst)
return
- if(!A || !IsValidSrc(A))
- to_chat(usr, span_warning("Error: callproc_datum(): owner of proc no longer exists."))
+ if(!thing || !is_valid_src(thing))
+ to_chat(user, span_warning("Error: callproc_datum(): owner of proc no longer exists."), confidential = TRUE)
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)
+ log_admin("[key_name(user)] called [thing]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
+ var/msg = "[key_name(user)] called [thing]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]."
+ message_admins(msg)
+ admin_ticket_log(thing, msg)
feedback_add_details("admin_verb","TPC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ //BLACKBOX_LOG_ADMIN_VERB("Atom ProcCall")
- var/returnval = WrapAdminProcCall(A, procname, lst) // Pass the lst as an argument list to the proc
- . = get_callproc_returnval(returnval,procname)
+ var/returnval = WrapAdminProcCall(thing, procname, lst) // Pass the lst as an argument list to the proc
+ . = user.get_callproc_returnval(returnval,procname)
if(.)
- to_chat(usr, .)
+ to_chat(user, ., confidential = TRUE)
/client/proc/get_callproc_args()
var/argnum = tgui_input_number(usr, "Number of arguments","Number:",0)
if(isnull(argnum))
- return null //Cancel
+ return
. = list()
- //var/list/named_args = list() //Named arguments are removed, due to them making proccalling take too long.
+ var/list/named_args = list()
while(argnum--)
- /* //Named arguments are removed, due to them making proccalling take too long.
- var/named_arg = tgui_input_text(usr,"Leave blank for positional argument. Positional arguments will be considered as if they were added first.", "Named argument")
- if(isnull(named_arg))
- return null //Cancel
- */
+ var/named_arg = tgui_input_text(usr, "Leave blank for positional argument. Positional arguments will be considered as if they were added first.", "Named argument")
var/value = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT))
if (!value["class"])
- return null //Cancel
- /* //Named arguments are removed, due to them making proccalling take too long.
+ return
if(named_arg)
named_args[named_arg] = value["value"]
else
- . += value["value"]
+ . += LIST_VALUE_WRAP_LISTS(value["value"])
if(LAZYLEN(named_args))
. += named_args
- */
- . += value["value"]
/client/proc/get_callproc_returnval(returnval,procname)
. = ""
diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm
index 626feb5369..b4006982ca 100644
--- a/code/modules/admin/holder2.dm
+++ b/code/modules/admin/holder2.dm
@@ -21,6 +21,7 @@ GLOBAL_PROTECT(href_token)
var/datum/feed_channel/admincaster_feed_channel = new /datum/feed_channel
var/admincaster_signature //What you'll sign the newsfeeds as
+ /// Code security critcal token used for authorizing href topic calls
var/href_token
/// Link from the database pointing to the admin's feedback forum
@@ -28,6 +29,12 @@ GLOBAL_PROTECT(href_token)
var/deadmined
+ var/datum/filter_editor/filteriffic
+ var/datum/particle_editor/particle_test
+
+ /// A lazylist of tagged datums, for quick reference with the View Tags verb
+ var/list/tagged_datums
+
var/given_profiling = FALSE
@@ -250,19 +257,6 @@ you will have to do something like if(client.rights & R_ADMIN) yourself.
return subject.holder.check_for_rights(rights_required)
return FALSE
-/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.Game"
- set name = "Mark Object"
- mark_datum(D)
-
/proc/GenerateToken()
. = ""
for(var/I in 1 to 32)
diff --git a/code/modules/admin/modify_robot.dm b/code/modules/admin/modify_robot.dm
index 14a94da98e..2e974ebaa8 100644
--- a/code/modules/admin/modify_robot.dm
+++ b/code/modules/admin/modify_robot.dm
@@ -160,7 +160,7 @@
/datum/eventkit/modify_robot/tgui_state(mob/user)
- return GLOB.tgui_admin_state
+ return ADMIN_STATE(R_ADMIN|R_EVENT|R_DEBUG)
/datum/eventkit/modify_robot/tgui_act(action, params, datum/tgui/ui)
. = ..()
diff --git a/code/modules/admin/permissionedit.dm b/code/modules/admin/permissionedit.dm
index 7b0bad2904..e5d3c9a3cc 100644
--- a/code/modules/admin/permissionedit.dm
+++ b/code/modules/admin/permissionedit.dm
@@ -223,7 +223,7 @@ ADMIN_VERB(edit_admin_permissions, R_PERMISSIONS, "Permissions Panel", "Edit adm
if(admin_ckey)
. = admin_ckey
else
- admin_key = input("New admin's key","Admin key") as text|null
+ admin_key = tgui_input_text(usr, "New admin's key","Admin key")
. = ckey(admin_key)
if(!.)
return FALSE
@@ -333,7 +333,7 @@ ADMIN_VERB(edit_admin_permissions, R_PERMISSIONS, "Permissions Panel", "Edit adm
if (!(rank_name in display_rank_names))
display_rank_names += rank_name
- var/next_rank = input("Please select a rank, or select [RANK_DONE] if you are finished.") as null|anything in display_rank_names
+ var/next_rank = tgui_input_list(usr, "Please select a rank, or select [RANK_DONE] if you are finished.", "Pick Rank", display_rank_names)
if (isnull(next_rank))
return
@@ -350,7 +350,7 @@ ADMIN_VERB(edit_admin_permissions, R_PERMISSIONS, "Permissions Panel", "Edit adm
continue
if (next_rank == "*New Rank*")
- var/new_rank_name = input("Please input a new rank", "New custom rank") as text|null
+ var/new_rank_name = tgui_input_text(usr, "Please input a new rank", "New custom rank")
if (!new_rank_name)
return
diff --git a/code/modules/admin/player_effects.dm b/code/modules/admin/player_effects.dm
index b4ad787391..b557600ae6 100644
--- a/code/modules/admin/player_effects.dm
+++ b/code/modules/admin/player_effects.dm
@@ -37,7 +37,7 @@
return data
/datum/eventkit/player_effects/tgui_state(mob/user)
- return GLOB.tgui_admin_state
+ return ADMIN_STATE(R_ADMIN|R_EVENT|R_DEBUG)
/datum/eventkit/player_effects/tgui_act(action, list/params, datum/tgui/ui)
. = ..()
@@ -724,7 +724,7 @@
user.client.cmd_admin_direct_narrate(target)
if("player_panel")
- user.client.holder.show_player_panel(target)
+ SSadmin_verbs.dynamic_invoke_verb(user, /datum/admin_verb/show_player_panel, target)
if("view_variables")
user.client.debug_variables(target)
diff --git a/code/modules/admin/tag.dm b/code/modules/admin/tag.dm
new file mode 100644
index 0000000000..37a455327d
--- /dev/null
+++ b/code/modules/admin/tag.dm
@@ -0,0 +1,102 @@
+/**
+ * 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_REF(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) span_bold("(UNTAG )")
+#define TAG_MARK(X) span_bold("(MARK )")
+#define TAG_SIMPLE_HEALTH(X) span_red(span_bold("Health: [X.health]"))
+#define TAG_CARBON_HEALTH(X) span_red(span_bold("Health: [X.health]")) +" (\
+ " + span_brute("[X.getBruteLoss()]") + " \
+ " + span_burn("[X.getFireLoss()]") + " \
+ " + span_tox("[X.getToxLoss()]") + " \
+ " + span_oxy("[X.getOxyLoss()]") + " \
+ " + span_clone("[X.getCloneLoss()]")
+
+ADMIN_VERB(display_tags, R_ADMIN, "View Tags", "Display all of the tagged datums.", ADMIN_CATEGORY_GAME)
+ var/index = 0
+ var/list/dat = list()
+
+ var/list/tagged_datums = user.holder.tagged_datums
+ var/list/marked_datum = user.holder.marked_datum
+
+ 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: [user]")
+ 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(" + span_bold(span_normal("[iter_datum.type])"))
+ else
+ dat += "No datums tagged :("
+
+ var/datum/browser/browser = new(user.mob, "tag", "Tag Menu", 800, 480)
+ browser.set_content(dat.Join(" "))
+ browser.open()
+
+#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 102ea86368..4e2d1fe2bf 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -1245,11 +1245,11 @@
return
var/block=text2num(href_list["block"])
usr.client.cmd_admin_toggle_block(H,block)
- show_player_panel(H)
+ SSadmin_verbs.dynamic_invoke_verb(usr.client, /datum/admin_verb/show_player_panel, H)
else if(href_list["adminplayeropts"])
var/mob/M = locate(href_list["adminplayeropts"])
- show_player_panel(M)
+ SSadmin_verbs.dynamic_invoke_verb(usr.client, /datum/admin_verb/show_player_panel, M)
else if(href_list["adminplayerobservejump"])
if(!check_rights(R_MOD|R_ADMIN|R_SERVER)) return //VOREStation Edit
@@ -1958,7 +1958,7 @@
if(!M.add_language(lang2toggle))
to_chat(usr, span_filter_adminlog("Failed to add language '[lang2toggle]' from \the [M]!"))
- show_player_panel(M)
+ SSadmin_verbs.dynamic_invoke_verb(usr.client, /datum/admin_verb/show_player_panel, M)
else if(href_list["cryoplayer"])
if(!check_rights(R_ADMIN|R_EVENT)) return
diff --git a/code/modules/admin/verbs/adminfun.dm b/code/modules/admin/verbs/adminfun.dm
new file mode 100644
index 0000000000..32e53dffdb
--- /dev/null
+++ b/code/modules/admin/verbs/adminfun.dm
@@ -0,0 +1,75 @@
+ADMIN_VERB(admin_explosion, R_ADMIN|R_FUN, "Explosion", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, atom/orignator as obj|mob|turf)
+ var/devastation = tgui_input_number(user, "Range of total devastation. -1 to none", text("Input"), min_value=-1)
+ if(devastation == null)
+ return
+ var/heavy = tgui_input_number(user, "Range of heavy impact. -1 to none", text("Input"), min_value=-1)
+ if(heavy == null)
+ return
+ var/light = tgui_input_number(user, "Range of light impact. -1 to none", text("Input"), min_value=-1)
+ if(light == null)
+ return
+ var/flash = tgui_input_number(user, "Range of flash. -1 to none", text("Input"), min_value=-1)
+ if(flash == null)
+ return
+
+ if ((devastation != -1) || (heavy != -1) || (light != -1) || (flash != -1))
+ if ((devastation > 20) || (heavy > 20) || (light > 20))
+ if (tgui_alert(user, "Are you sure you want to do this? It will laaag.", "Confirmation", list("Yes", "No")) != "Yes")
+ return
+
+ explosion(orignator, devastation, heavy, light, flash)
+ log_admin("[key_name(user)] created an explosion ([devastation],[heavy],[light],[flash]) at ([orignator.x],[orignator.y],[orignator.z])")
+ message_admins("[key_name_admin(user)] created an explosion ([devastation],[heavy],[light],[flash]) at ([orignator.x],[orignator.y],[orignator.z])", 1)
+ feedback_add_details("admin_verb","EXPL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+ADMIN_VERB(admin_emp, R_ADMIN|R_FUN, "EM Pulse", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, atom/orignator as obj|mob|turf)
+ var/heavy = tgui_input_number(user, "Range of heavy pulse.", text("Input"))
+ if(heavy == null)
+ return
+ var/med = tgui_input_number(user, "Range of medium pulse.", text("Input"))
+ if(med == null)
+ return
+ var/light = tgui_input_number(user, "Range of light pulse.", text("Input"))
+ if(light == null)
+ return
+ var/long = tgui_input_number(user, "Range of long pulse.", text("Input"))
+ if(long == null)
+ return
+
+ if (heavy || med || light || long)
+ empulse(orignator, heavy, med, light, long)
+ log_admin("[key_name(user)] created an EM Pulse ([heavy],[med],[light],[long]) at ([orignator.x],[orignator.y],[orignator.z])")
+ message_admins("[key_name_admin(user)] created an EM PUlse ([heavy],[med],[light],[long]) at ([orignator.x],[orignator.y],[orignator.z])", 1)
+ feedback_add_details("admin_verb","EMP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+ADMIN_VERB(gib_them, (R_ADMIN|R_FUN), "Gib", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, mob/victim in mob_list)
+ var/confirm = tgui_alert(user, "You sure?", "Confirm", list("Yes", "No"))
+ if(confirm != "Yes")
+ return
+ //Due to the delay here its easy for something to have happened to the mob
+ if(!victim)
+ return
+
+ log_admin("[key_name(user)] has gibbed [key_name(victim)]")
+ message_admins("[key_name_admin(user)] has gibbed [key_name_admin(victim)]", 1)
+
+ if(isobserver(victim))
+ gibs(victim.loc)
+ return
+
+ victim.gib()
+ feedback_add_details("admin_verb","GIB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+ADMIN_VERB(gib_self, R_HOLDER, "Gibself", "Give yourself the same treatment you give others.", ADMIN_CATEGORY_FUN)
+ var/confirm = tgui_alert(user, "You sure?", "Confirm", list("Yes", "No"))
+ if(!confirm)
+ return
+ if(confirm == "Yes")
+ if (isobserver(user.mob)) // so they don't spam gibs everywhere
+ return
+ else
+ user.mob.gib()
+
+ log_admin("[key_name(user)] used gibself.")
+ message_admins(span_blue("[key_name_admin(user)] used gibself."), 1)
+ feedback_add_details("admin_verb","GIBS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/admin/verbs/change_appearance.dm b/code/modules/admin/verbs/change_appearance.dm
index b796fdba9c..2e191cc1a3 100644
--- a/code/modules/admin/verbs/change_appearance.dm
+++ b/code/modules/admin/verbs/change_appearance.dm
@@ -9,7 +9,7 @@
if(!H) return
log_and_message_admins("is altering the appearance of [H].")
- H.change_appearance(APPEARANCE_ALL, usr, check_species_whitelist = 0, state = GLOB.tgui_admin_state)
+ H.change_appearance(APPEARANCE_ALL, usr, check_species_whitelist = 0, state = ADMIN_STATE(R_FUN))
feedback_add_details("admin_verb","CHAA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/change_human_appearance_self()
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index a90ff5152b..c67c40b6c6 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -304,22 +304,21 @@
log_admin("[key_name(src)] has granted [M.key] full access.")
message_admins(span_blue("[key_name_admin(usr)] has granted [M.key] full access."), 1)
-/client/proc/cmd_assume_direct_control(var/mob/M in mob_list)
- set category = "Admin.Game"
- set name = "Assume direct control"
- set desc = "Direct intervention"
-
- if(!check_rights(R_DEBUG|R_ADMIN|R_EVENT)) return
+ADMIN_VERB(cmd_assume_direct_control, (R_DEBUG|R_ADMIN|R_EVENT), "Assume Direct Control", "Assume direct control of a mob.", "Admin.Game", mob/M)
if(M.ckey)
- if(tgui_alert(usr, "This mob is being controlled by [M.ckey]. Are you sure you wish to assume control of it? [M.ckey] will be made a ghost.","Confirmation",list("Yes","No")) != "Yes")
+ if(tgui_alert(user, "This mob is being controlled by [M.ckey]. Are you sure you wish to assume control of it? [M.ckey] will be made a ghost.","Confirmation",list("Yes","No")) != "Yes")
return
- else
- var/mob/observer/dead/ghost = new/mob/observer/dead(M,1)
- ghost.ckey = M.ckey
- message_admins(span_blue("[key_name_admin(usr)] assumed direct control of [M]."), 1)
- log_admin("[key_name(usr)] assumed direct control of [M].")
- var/mob/adminmob = src.mob
- M.ckey = src.ckey
+ if(!M || QDELETED(M))
+ to_chat(user, span_warning("The target mob no longer exists."))
+ return
+
+ var/mob/observer/dead/ghost = new/mob/observer/dead(M,1)
+ ghost.ckey = M.ckey
+
+ message_admins(span_blue("[key_name_admin(user)] assumed direct control of [M]."), 1)
+ log_admin("[key_name(user)] assumed direct control of [M].")
+ var/mob/adminmob = user.mob
+ M.ckey = user.ckey
if( isobserver(adminmob) )
qdel(adminmob)
feedback_add_details("admin_verb","ADC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/admin/verbs/entity_narrate.dm b/code/modules/admin/verbs/entity_narrate.dm
index c8b96ec26b..6b983e64bb 100644
--- a/code/modules/admin/verbs/entity_narrate.dm
+++ b/code/modules/admin/verbs/entity_narrate.dm
@@ -172,7 +172,7 @@ ADMIN_VERB(narrate_mob_args, R_FUN, "Narrate Entity", "Narrate entities using po
/datum/entity_narrate/tgui_state(mob/user)
- return GLOB.tgui_admin_state
+ return ADMIN_STATE(R_FUN)
/datum/entity_narrate/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
diff --git a/code/modules/admin/verbs/fps.dm b/code/modules/admin/verbs/fps.dm
index e97bc166fc..cfd840879c 100644
--- a/code/modules/admin/verbs/fps.dm
+++ b/code/modules/admin/verbs/fps.dm
@@ -1,7 +1,7 @@
ADMIN_VERB_VISIBILITY(set_server_fps, ADMIN_VERB_VISIBLITY_FLAG_MAPPING_DEBUG)
ADMIN_VERB(set_server_fps, R_DEBUG, "Set Server FPS", "Sets game speed in frames-per-second. Can potentially break the game", ADMIN_CATEGORY_DEBUG)
var/cfg_fps = CONFIG_GET(number/fps)
- var/new_fps = round(input(user, "Sets game frames-per-second. Can potentially break the game (default: [cfg_fps])","FPS", world.fps) as num|null)
+ var/new_fps = round(tgui_input_number(user, "Sets game frames-per-second. Can potentially break the game (default: [cfg_fps])","FPS", world.fps))
if(new_fps <= 0)
to_chat(user, span_danger("Error: set_server_fps(): Invalid world.fps value. No changes made."), confidential = TRUE)
diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm
index cc46b0ab37..82aedfe622 100644
--- a/code/modules/admin/verbs/mapping.dm
+++ b/code/modules/admin/verbs/mapping.dm
@@ -138,7 +138,6 @@ var/list/debug_verbs = list (
,/client/proc/powerdebug
,/client/proc/count_objects_on_z_level
,/client/proc/count_objects_all
- ,/client/proc/cmd_assume_direct_control
,/client/proc/jump_to_dead_group
,/client/proc/startSinglo
,/client/proc/cmd_admin_grantfullaccess
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 67d9614a00..aee7076944 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -1,22 +1,20 @@
GLOBAL_VAR_INIT(global_vantag_hud, 0)
-/client/proc/cmd_admin_drop_everything(mob/M as mob in mob_list)
- set category = null
- set name = "Drop Everything"
- if(!holder)
- return
-
- var/confirm = tgui_alert(src, "Make [M] drop everything?", "Message", list("Yes", "No"))
+ADMIN_VERB(drop_everything, R_ADMIN, "Drop Everything", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, mob/living/dropee in mob_list)
+ var/confirm = tgui_alert(src, "Make [dropee] drop everything?", "Message", list("Yes", "No"))
if(confirm != "Yes")
return
- for(var/obj/item/W in M)
+ for(var/obj/item/W in dropee)
if(istype(W, /obj/item/implant/backup) || istype(W, /obj/item/nif)) //There's basically no reason to remove either of these
continue
- M.drop_from_inventory(W)
+ dropee.drop_from_inventory(W)
- log_admin("[key_name(usr)] made [key_name(M)] drop everything!")
- message_admins("[key_name_admin(usr)] made [key_name_admin(M)] drop everything!", 1)
+ dropee.regenerate_icons()
+
+ log_admin("[key_name(user)] made [key_name(dropee)] drop everything!")
+ var/msg = "[key_name_admin(user)] made [ADMIN_LOOKUPFLW(dropee)] drop everything!"
+ message_admins(msg)
feedback_add_details("admin_verb","DEVR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_prison(mob/M as mob in mob_list)
@@ -715,100 +713,6 @@ ADMIN_VERB(respawn_character, (R_ADMIN|R_REJUVINATE), "Spawn Character", "(Re)Sp
to_chat(src, "[job.title]: [job.total_positions]")
feedback_add_details("admin_verb","LFS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/client/proc/cmd_admin_explosion(atom/O as obj|mob|turf in world)
- set category = "Fun.Do Not"
- set name = "Explosion"
-
- if(!check_rights(R_DEBUG|R_FUN)) return
-
- var/devastation = tgui_input_number(usr, "Range of total devastation. -1 to none", text("Input"), min_value=-1)
- if(devastation == null) return
- var/heavy = tgui_input_number(usr, "Range of heavy impact. -1 to none", text("Input"), min_value=-1)
- if(heavy == null) return
- var/light = tgui_input_number(usr, "Range of light impact. -1 to none", text("Input"), min_value=-1)
- if(light == null) return
- var/flash = tgui_input_number(usr, "Range of flash. -1 to none", text("Input"), min_value=-1)
- if(flash == null) return
-
- if ((devastation != -1) || (heavy != -1) || (light != -1) || (flash != -1))
- if ((devastation > 20) || (heavy > 20) || (light > 20))
- if (tgui_alert(src, "Are you sure you want to do this? It will laaag.", "Confirmation", list("Yes", "No")) != "Yes")
- return
-
- explosion(O, devastation, heavy, light, flash)
- log_admin("[key_name(usr)] created an explosion ([devastation],[heavy],[light],[flash]) at ([O.x],[O.y],[O.z])")
- message_admins("[key_name_admin(usr)] created an explosion ([devastation],[heavy],[light],[flash]) at ([O.x],[O.y],[O.z])", 1)
- feedback_add_details("admin_verb","EXPL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- return
- else
- return
-
-/client/proc/cmd_admin_emp(atom/O as obj|mob|turf in world)
- set category = "Fun.Do Not"
- set name = "EM Pulse"
-
- if(!check_rights(R_DEBUG|R_FUN)) return
-
- var/heavy = tgui_input_number(usr, "Range of heavy pulse.", text("Input"))
- if(heavy == null) return
- var/med = tgui_input_number(usr, "Range of medium pulse.", text("Input"))
- if(med == null) return
- var/light = tgui_input_number(usr, "Range of light pulse.", text("Input"))
- if(light == null) return
- var/long = tgui_input_number(usr, "Range of long pulse.", text("Input"))
- if(long == null) return
-
- if (heavy || med || light || long)
-
- empulse(O, heavy, med, light, long)
- log_admin("[key_name(usr)] created an EM Pulse ([heavy],[med],[light],[long]) at ([O.x],[O.y],[O.z])")
- message_admins("[key_name_admin(usr)] created an EM PUlse ([heavy],[med],[light],[long]) at ([O.x],[O.y],[O.z])", 1)
- feedback_add_details("admin_verb","EMP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
- return
- else
- return
-
-/client/proc/cmd_admin_gib(mob/M as mob in mob_list)
- set category = "Fun.Do Not"
- set name = "Gib"
-
- if(!check_rights(R_ADMIN|R_FUN)) return
-
- var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No"))
- if(confirm != "Yes") return
- //Due to the delay here its easy for something to have happened to the mob
- if(!M) return
-
- log_admin("[key_name(usr)] has gibbed [key_name(M)]")
- message_admins("[key_name_admin(usr)] has gibbed [key_name_admin(M)]", 1)
-
- if(isobserver(M))
- gibs(M.loc)
- return
-
- M.gib()
- feedback_add_details("admin_verb","GIB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/client/proc/cmd_admin_gib_self()
- set name = "Gibself"
- set category = "Fun.Do Not"
-
- if(!holder)
- return
-
- var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No"))
- if(!confirm)
- return
- if(confirm == "Yes")
- if (isobserver(mob)) // so they don't spam gibs everywhere
- return
- else
- mob.gib()
-
- log_admin("[key_name(usr)] used gibself.")
- message_admins(span_blue("[key_name_admin(usr)] used gibself."), 1)
- feedback_add_details("admin_verb","GIBS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/*
/client/proc/cmd_manual_ban()
set name = "Manual Ban"
diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm
index c48aaecb42..4aa89f0b13 100644
--- a/code/modules/admin/verbs/secrets.dm
+++ b/code/modules/admin/verbs/secrets.dm
@@ -21,7 +21,7 @@ ADMIN_VERB(secrets, R_HOLDER, "Secrets", "Abuse harder than you ever have before
is_funmin = check_rights(R_FUN)
/datum/secrets_menu/tgui_state(mob/user)
- return GLOB.tgui_admin_state// TGUI_ADMIN_STATE(R_NONE)
+ return ADMIN_STATE(R_HOLDER)
/datum/secrets_menu/tgui_close()
qdel(src)
@@ -368,7 +368,7 @@ ADMIN_VERB(secrets, R_HOLDER, "Secrets", "Abuse harder than you ever have before
return
//SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Bomb Cap"))
- //var/newBombCap = input(holder,"What would you like the new bomb cap to be. (entered as the light damage range (the 3rd number in common (1,2,3) notation)) Must be above 4)", "New Bomb Cap", GLOB.MAX_EX_LIGHT_RANGE) as num|null
+ //var/newBombCap = tgui_input_list(holder,"What would you like the new bomb cap to be. (entered as the light damage range (the 3rd number in common (1,2,3) notation)) Must be above 4)", "New Bomb Cap", GLOB.MAX_EX_LIGHT_RANGE)
//if (!CONFIG_SET(number/bombcap, newBombCap))
// return
diff --git a/code/modules/admin/view_variables/admin_delete.dm b/code/modules/admin/view_variables/admin_delete.dm
index 4f9c048fe6..55265b27ae 100644
--- a/code/modules/admin/view_variables/admin_delete.dm
+++ b/code/modules/admin/view_variables/admin_delete.dm
@@ -5,27 +5,25 @@
if(istype(A))
var/turf/T = get_turf(A)
if(T)
- coords = "at [COORD(T)]"
- jmp_coords = "at [ADMIN_COORDJMP(T)]"
+ var/atom/a_loc = A.loc
+ var/is_turf = isturf(a_loc)
+ coords = "[is_turf ? "at" : "from [a_loc] at"] [AREACOORD(T)]"
+ jmp_coords = "[is_turf ? "at" : "from [a_loc] at"] [ADMIN_VERBOSEJMP(T)]"
else
jmp_coords = coords = "in nullspace"
- if (tgui_alert(src, "Are you sure you want to delete:\n[D]\n[coords]?", "Confirmation", list("Yes", "No")) == "Yes")
+ if (tgui_alert(usr, "Are you sure you want to delete:\n[D]\n[coords]?", "Confirmation", list("Yes", "No")) == "Yes")
log_admin("[key_name(usr)] deleted [D] [coords]")
message_admins("[key_name_admin(usr)] deleted [D] [jmp_coords]")
+ //BLACKBOX_LOG_ADMIN_VERB("Delete")
feedback_add_details("admin_verb","ADEL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- /*if(isturf(D)) //Polaris doesn't support baseturfs yet.
- var/turf/T = D
- T.ScrapeAway()
- else*/
- vv_update_display(D, "deleted", VV_MSG_DELETED)
-
- // turfs are special snowflakes that'll explode if qdel'd outside ChangeTurf
- if (isturf(D))
+ SEND_SIGNAL(D, COMSIG_ADMIN_DELETING, src)
+ if(isturf(D))
var/turf/T = D
+ //T.ScrapeAway()
T.ChangeTurf(world.turf)
else
+ vv_update_display(D, "deleted", VV_MSG_DELETED)
qdel(D)
-
- if(!QDELETED(D))
- vv_update_display(D, "deleted", "")
+ if(!QDELETED(D))
+ vv_update_display(D, "deleted", "")
diff --git a/code/modules/admin/view_variables/color_matrix_editor.dm b/code/modules/admin/view_variables/color_matrix_editor.dm
new file mode 100644
index 0000000000..3295ac41d0
--- /dev/null
+++ b/code/modules/admin/view_variables/color_matrix_editor.dm
@@ -0,0 +1,85 @@
+/datum/color_matrix_editor
+ var/client/owner
+ var/datum/weakref/target
+ var/atom/movable/screen/map_view/proxy_view
+ var/list/current_color
+ var/closed
+
+/datum/color_matrix_editor/New(user, atom/_target = null)
+ owner = CLIENT_FROM_VAR(user)
+ if(islist(_target?.color))
+ current_color = _target.color
+ else if(istext(_target?.color))
+ current_color = color_hex2color_matrix(_target.color)
+ else
+ current_color = COLOR_MATRIX_IDENTITY
+
+ var/mutable_appearance/view = image('icons/testing/colortest.dmi', "colors")
+ if(_target)
+ target = WEAKREF(_target)
+ if(!(_target.appearance_flags & PLANE_MASTER))
+ view = image(_target)
+
+ proxy_view = new
+ proxy_view.generate_view("color_matrix_proxy_[REF(src)]")
+
+ proxy_view.appearance = view
+ proxy_view.color = current_color
+
+/datum/color_matrix_editor/Destroy(force)
+ QDEL_NULL(proxy_view)
+ return ..()
+
+/datum/color_matrix_editor/tgui_state(mob/user)
+ return ADMIN_STATE(R_VAREDIT)
+
+/datum/color_matrix_editor/tgui_static_data(mob/user)
+ var/list/data = list()
+ data["mapRef"] = proxy_view.assigned_map
+
+ return data
+
+/datum/color_matrix_editor/tgui_data(mob/user)
+ var/list/data = list()
+ data["currentColor"] = current_color
+
+ return data
+
+/datum/color_matrix_editor/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "ColorMatrixEditor")
+ ui.open()
+ proxy_view.display_to(owner.mob, ui.window)
+
+/datum/color_matrix_editor/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
+ . = ..()
+ if(.)
+ return
+ switch(action)
+ if("transition_color")
+ current_color = params["color"]
+ animate(proxy_view, time = 4, color = current_color)
+ if("confirm")
+ on_confirm()
+ SStgui.close_uis(src)
+
+/datum/color_matrix_editor/tgui_close(mob/user)
+ . = ..()
+ closed = TRUE
+
+/datum/color_matrix_editor/proc/on_confirm()
+ var/atom/target_atom = target?.resolve()
+ if(istype(target_atom))
+ target_atom.vv_edit_var("color", current_color)
+
+/datum/color_matrix_editor/proc/wait()
+ while(!closed)
+ stoplag(1)
+
+/client/proc/open_color_matrix_editor(atom/in_atom)
+ var/datum/color_matrix_editor/editor = new /datum/color_matrix_editor(src, in_atom)
+ editor.tgui_interact(mob)
+ editor.wait()
+ . = editor.current_color
+ qdel(editor)
diff --git a/code/modules/admin/view_variables/debug_variable_appearance.dm b/code/modules/admin/view_variables/debug_variable_appearance.dm
new file mode 100644
index 0000000000..dfa590fd43
--- /dev/null
+++ b/code/modules/admin/view_variables/debug_variable_appearance.dm
@@ -0,0 +1,79 @@
+/// Shows a header name on top when you investigate an appearance/image
+/image/vv_get_header()
+ . = list()
+ var/icon_name = "[icon || "null"] "
+ . += replacetext(icon_name, "icons/obj", "") // shortens the name. We know the path already.
+ if(icon)
+ . += icon_state ? "\"[icon_state]\"" : "(icon_state = null)"
+
+/// Makes nice short vv names for images
+/image/debug_variable_value(name, level, datum/owner, sanitize, display_flags)
+ var/display_name = "[type]"
+ if("[src]" != "[type]") // If we have a name var, let's use it.
+ display_name = "[src] [type]"
+
+ var/display_value
+ var/list/icon_file_name = splittext("[icon]", "/")
+ if(length(icon_file_name))
+ display_value = icon_file_name[length(icon_file_name)]
+ else
+ display_value = "null"
+
+ if(icon_state)
+ display_value = "[display_value]:[icon_state]"
+
+ var/display_ref = get_vv_link_ref()
+ return "[display_name] ([display_value] ) [display_ref] "
+
+/// Returns the ref string to use when displaying this image in the vv menu of something else
+/image/proc/get_vv_link_ref()
+ return REF(src)
+
+// It is endlessly annoying to display /appearance directly for stupid byond reasons, so we copy everything we care about into a holder datum
+// That we can override procs on and store other vars on and such.
+/mutable_appearance/appearance_mirror
+ // So people can see where it came from
+ var/appearance_ref
+
+// arg is actually an appearance, typed as mutable_appearance as closest mirror
+/mutable_appearance/appearance_mirror/New(mutable_appearance/appearance_father)
+ . = ..() // /mutable_appearance/New() copies over all the appearance vars MAs care about by default
+ appearance_ref = REF(appearance_father)
+
+// This means if the appearance loses refs before a click it's gone, but that's consistent to other datums so it's fine
+// Need to ref the APPEARANCE because we just free on our own, which sorta fucks this operation up you know?
+/mutable_appearance/appearance_mirror/get_vv_link_ref()
+ return appearance_ref
+
+/mutable_appearance/appearance_mirror/can_vv_get(var_name)
+ var/static/datum/beloved = new()
+ if(beloved.vars.Find(var_name)) // If datums have it, get out
+ return FALSE
+ // If it is one of the two args on /image, yeet (I am sorry)
+ if(var_name == NAMEOF(src, realized_overlays))
+ return FALSE
+ if(var_name == NAMEOF(src, realized_underlays))
+ return FALSE
+ // Could make an argument for this but I think they will just confuse people, so yeeet
+ if(var_name == NAMEOF(src, vis_contents))
+ return FALSE
+ return ..()
+
+/mutable_appearance/appearance_mirror/vv_get_var(var_name)
+ // No editing for you
+ var/value = vars[var_name]
+ return "(READ ONLY) [var_name] = [_debug_variable_value(var_name, value, 0, src, sanitize = TRUE, display_flags = NONE)] "
+
+/mutable_appearance/appearance_mirror/vv_get_dropdown()
+ SHOULD_CALL_PARENT(FALSE)
+
+ . = 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_TAG, "Tag Datum")
+ VV_DROPDOWN_OPTION(VV_HK_DELETE, "Delete")
+ VV_DROPDOWN_OPTION(VV_HK_EXPOSE, "Show VV To Player")
+
+/proc/get_vv_appearance(mutable_appearance/appearance) // actually appearance yadeeyada
+ return new /mutable_appearance/appearance_mirror(appearance)
diff --git a/code/modules/admin/view_variables/debug_variables.dm b/code/modules/admin/view_variables/debug_variables.dm
index 0a4a36ceb5..3226941a14 100644
--- a/code/modules/admin/view_variables/debug_variables.dm
+++ b/code/modules/admin/view_variables/debug_variables.dm
@@ -1,77 +1,128 @@
#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/list/data_list = DA
+/// Get displayed variable in VV variable list
+/proc/debug_variable(name, value, level, datum/owner, sanitize = TRUE, display_flags = NONE) //if D is a list, name will be index, and value will be assoc value.
+ if(owner)
+ if(islist(owner))
+ var/list/list_owner = owner
var/index = name
- if (value)
- name = data_list[name] //name is really the index until this line
+ if (isnull(value))
+ value = list_owner[name]
else
- value = data_list[name]
- header = "(E ) (C ) (- ) "
+ name = list_owner[name] //name is really the index until this line
+ . = " ([VV_HREF_TARGET_1V(owner, VV_HK_LIST_EDIT, "E", index)]) ([VV_HREF_TARGET_1V(owner, VV_HK_LIST_CHANGE, "C", index)]) ([VV_HREF_TARGET_1V(owner, VV_HK_LIST_REMOVE, "-", index)]) "
else
- header = " (E ) (C ) (M ) "
+ . = " ([VV_HREF_TARGET_1V(owner, VV_HK_BASIC_EDIT, "E", name)]) ([VV_HREF_TARGET_1V(owner, VV_HK_BASIC_CHANGE, "C", name)]) ([VV_HREF_TARGET_1V(owner, VV_HK_BASIC_MASSEDIT, "M", name)]) "
else
- header = " "
+ . = " "
- var/item
- if (isnull(value))
- item = "[VV_HTML_ENCODE(name)] = null "
+ var/name_part = VV_HTML_ENCODE(name)
+ if(level > 0 || islist(owner)) //handling keys in assoc lists
+ if(istype(name,/datum))
+ name_part = "[VV_HTML_ENCODE(name)] [REF(name)] "
+ else if(islist(name))
+ var/list/list_value = name
+ name_part = " /list ([length(list_value)]) [REF(name)] "
- else if (istext(value))
- item = "[VV_HTML_ENCODE(name)] = \"[VV_HTML_ENCODE(value)]\" "
+ . = "[.][name_part] = "
- else if (isicon(value))
+ var/item = _debug_variable_value(name, value, level, owner, sanitize, display_flags)
+
+ return "[.][item] "
+
+// This is split into a separate proc mostly to make errors that happen not break things too much
+/proc/_debug_variable_value(name, value, level, datum/owner, sanitize, display_flags)
+ if(isappearance(value))
+ value = get_vv_appearance(value)
+
+ . = span_red("DISPLAY_ERROR:") + " ([value] [REF(value)])" // Make sure this line can never runtime
+
+ if(isnull(value))
+ return span_value("null")
+
+ if(istext(value))
+ return span_value("\"[VV_HTML_ENCODE(value)]\"")
+
+ if(isicon(value))
#ifdef VARSICON
- var/icon/I = new/icon(value)
+ var/icon/icon_value = 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] ) "
+ var/rname = "tmp[REF(icon_value)][rnd].png"
+ usr << browse_rsc(icon_value, rname)
+ return "(" + span_value("[value]") + ") "
#else
- item = "[VV_HTML_ENCODE(name)] = /icon ([value] )"
+ return "/icon (" + span_value("[value]") + ")"
#endif
- else if (isfile(value))
- item = "[VV_HTML_ENCODE(name)] = '[value]' "
+ if(isfilter(value))
+ var/datum/filter_value = value
+ return "/filter (" + span_value("[filter_value.type] [REF(filter_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]"
+ if(isfile(value))
+ return span_value("'[value]'")
- else if (islist(value))
- var/list/L = value
+ if(isdatum(value))
+ var/datum/datum_value = value
+ return datum_value.debug_variable_value(name, level, owner, sanitize, display_flags)
+
+ if(islist(value) || (name in GLOB.vv_special_lists)) // Some special lists aren't detectable as a list through istype
+ var/list/list_value = 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]
+ // This is because some lists either don't count as lists or a locate on their ref will return null
+ var/link_vars = "Vars=[REF(value)]"
+ if(name in GLOB.vv_special_lists)
+ link_vars = "Vars=[REF(owner)];special_varname=[name]"
+
+ if (!(display_flags & VV_ALWAYS_CONTRACT_LIST) && list_value.len > 0 && list_value.len <= (IS_NORMAL_LIST(list_value) ? VV_NORMAL_LIST_NO_EXPAND_THRESHOLD : VV_SPECIAL_LIST_NO_EXPAND_THRESHOLD))
+ for (var/i in 1 to list_value.len)
+ var/key = list_value[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 ""
+ if (IS_NORMAL_LIST(list_value) && !isnum(key))
+ val = list_value[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]) "
- else
- item = "[VV_HTML_ENCODE(name)] = /list ([L.len]) "
+ return "/list ([list_value.len]) "
+ return "/list ([list_value.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, ", "))]"
+ // if it's a number, is it a bitflag?
+ var/list/valid_bitflags
+ if(!isnum(name))
+ valid_bitflags = get_valid_bitflags(name)
+
+ if(!length(valid_bitflags))
+ return span_value("[VV_HTML_ENCODE(value)]")
+
+ var/list/flags = list()
+ for (var/bit_name in valid_bitflags)
+ if (value & valid_bitflags[bit_name])
+ flags += bit_name
+ if(length(flags))
+ return "[VV_HTML_ENCODE(flags.Join(", "))]"
+ return "NONE"
+
+/datum/proc/debug_variable_value(name, level, datum/owner, sanitize, display_flags)
+ if("[src]" != "[type]") // If we have a name var, let's use it.
+ return "[src] [type] [REF(src)] "
else
- item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(value)] "
+ return "[type] [REF(src)] "
- return "[header][item]"
+/datum/weakref/debug_variable_value(name, level, datum/owner, sanitize, display_flags)
+ . = ..()
+ return "[.] (Resolve) "
+
+/matrix/debug_variable_value(name, level, datum/owner, sanitize, display_flags)
+ return span_value("\
+ \
+ \
+ \
+ [a] [d] 0 \
+ [b] [e] 0 \
+ [c] [f] 1 \
+ \
+
") //TODO link to modify_transform wrapper for all matrices
#undef VV_HTML_ENCODE
diff --git a/code/modules/admin/view_variables/filterrific.dm b/code/modules/admin/view_variables/filterrific.dm
new file mode 100644
index 0000000000..6c7256a926
--- /dev/null
+++ b/code/modules/admin/view_variables/filterrific.dm
@@ -0,0 +1,97 @@
+/datum/filter_editor
+ var/atom/target
+
+/datum/filter_editor/New(atom/target)
+ src.target = target
+
+/datum/filter_editor/tgui_state(mob/user)
+ return ADMIN_STATE(R_VAREDIT)
+
+/datum/filter_editor/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "Filteriffic")
+ ui.open()
+
+/datum/filter_editor/tgui_static_data(mob/user)
+ var/list/data = list()
+ data["filter_info"] = GLOB.master_filter_info
+ return data
+
+/datum/filter_editor/tgui_data()
+ var/list/data = list()
+ data["target_name"] = target.name
+ data["target_filter_data"] = target.filter_data
+ return data
+
+/datum/filter_editor/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("add_filter")
+ var/target_name = params["name"]
+ while(target.filter_data && target.filter_data[target_name])
+ target_name = "[target_name]-dupe"
+ target.add_filter(target_name, params["priority"], list("type" = params["type"]))
+ . = TRUE
+ if("remove_filter")
+ target.remove_filter(params["name"])
+ . = TRUE
+ if("rename_filter")
+ var/list/filter_data = target.filter_data[params["name"]]
+ target.remove_filter(params["name"])
+ target.add_filter(params["new_name"], filter_data["priority"], filter_data)
+ . = TRUE
+ if("edit_filter")
+ target.remove_filter(params["name"])
+ target.add_filter(params["name"], params["priority"], params["new_filter"])
+ . = TRUE
+ if("change_priority")
+ var/new_priority = params["new_priority"]
+ target.change_filter_priority(params["name"], new_priority)
+ . = TRUE
+ if("transition_filter_value")
+ target.transition_filter(params["name"], params["new_data"], 4)
+ . = TRUE
+ if("modify_filter_value")
+ var/list/old_filter_data = target.filter_data[params["name"]]
+ var/list/new_filter_data = old_filter_data.Copy()
+ for(var/entry in params["new_data"])
+ new_filter_data[entry] = params["new_data"][entry]
+ for(var/entry in new_filter_data)
+ if(entry == GLOB.master_filter_info[old_filter_data["type"]]["defaults"][entry])
+ new_filter_data.Remove(entry)
+ target.remove_filter(params["name"])
+ target.add_filter(params["name"], old_filter_data["priority"], new_filter_data)
+ . = TRUE
+ if("modify_color_value")
+ var/new_color = tgui_color_picker(usr, "Pick new filter color", "Filteriffic Colors!")
+ if(new_color)
+ target.transition_filter(params["name"], list("color" = new_color), 4)
+ . = TRUE
+ if("modify_icon_value")
+ var/icon/new_icon = pick_and_customize_icon()
+ if(new_icon)
+ target.filter_data[params["name"]]["icon"] = new_icon
+ target.update_filters()
+ . = TRUE
+ if("mass_apply")
+ if(!check_rights_for(usr.client, R_FUN))
+ to_chat(usr, span_userdanger("Stay in your lane, jannie."))
+ return
+ var/target_path = text2path(params["path"])
+ if(!target_path)
+ return
+ var/filters_to_copy = target.filters
+ var/filter_data_to_copy = target.filter_data
+ var/count = 0
+ for(var/thing in world.contents)
+ if(istype(thing, target_path))
+ var/atom/thing_at = thing
+ thing_at.filters = filters_to_copy
+ thing_at.filter_data = filter_data_to_copy
+ count += 1
+ message_admins("LOCAL CLOWN [usr.ckey] JUST MASS FILTER EDITED [count] WITH PATH OF [params["path"]]!")
+ log_admin("LOCAL CLOWN [usr.ckey] JUST MASS FILTER EDITED [count] WITH PATH OF [params["path"]]!")
diff --git a/code/modules/admin/view_variables/get_variables.dm b/code/modules/admin/view_variables/get_variables.dm
index daac9c3574..062e8e003b 100644
--- a/code/modules/admin/view_variables/get_variables.dm
+++ b/code/modules/admin/view_variables/get_variables.dm
@@ -1,59 +1,69 @@
-/client/proc/vv_get_class(var/var_name, var/var_value)
+/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)
+ else if(isnum(var_value))
+ if(length(get_valid_bitflags(var_name)))
. = VV_BITFIELD
else
. = VV_NUM
- else if (istext(var_value))
- if (findtext(var_value, "\n"))
+ else if(istext(var_value))
+ if(findtext(var_value, "\n"))
. = VV_MESSAGE
+ else if(findtext(var_value, GLOB.is_color))
+ . = VV_COLOR
else
. = VV_TEXT
- else if (isicon(var_value))
+ else if(isicon(var_value))
. = VV_ICON
- else if (ismob(var_value))
+ else if(ismob(var_value))
. = VV_MOB_REFERENCE
- else if (isloc(var_value))
+ else if(isloc(var_value))
. = VV_ATOM_REFERENCE
- else if (istype(var_value, /client))
+ else if(istype(var_value, /client))
. = VV_CLIENT
- else if (istype(var_value, /datum))
+ else if(isweakref(var_value))
+ . = VV_WEAKREF
+
+ else if(isdatum(var_value))
. = VV_DATUM_REFERENCE
- else if (ispath(var_value))
- if (ispath(var_value, /atom))
+ else if(ispath(var_value))
+ if(ispath(var_value, /atom))
. = VV_ATOM_TYPE
- else if (ispath(var_value, /datum))
+ else if(ispath(var_value, /datum))
. = VV_DATUM_TYPE
else
. = VV_TYPE
- else if (islist(var_value))
- . = VV_LIST
+ else if(islist(var_value))
+ if(var_name in GLOB.color_vars)
+ . = VV_COLOR_MATRIX
+ else
+ . = VV_LIST
- else if (isfile(var_value))
+ 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)
+ if(!class)
+ if(!classes)
classes = list (
VV_NUM,
VV_TEXT,
VV_MESSAGE,
VV_ICON,
+ VV_COLOR,
+ //VV_COLOR_MATRIX,
VV_ATOM_REFERENCE,
VV_DATUM_REFERENCE,
VV_MOB_REFERENCE,
@@ -67,147 +77,187 @@
VV_NEW_TYPE,
VV_NEW_LIST,
VV_NULL,
- VV_RESTORE_DEFAULT
+ VV_INFINITY,
+ VV_RESTORE_DEFAULT,
+ VV_TEXT_LOCATE,
+ VV_PROCCALL_RETVAL,
+ VV_WEAKREF,
)
- if(holder && holder.marked_datum && !(VV_MARKED_DATUM in restricted_classes))
- classes += "[VV_MARKED_DATUM] ([holder.marked_datum.type])"
- if (restricted_classes)
+ 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
+
+ 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
- if (extra_classes)
+ if(extra_classes)
classes += extra_classes
.["class"] = tgui_input_list(src, "What kind of data?", "Variable Type", classes, default_class)
- if (holder && holder.marked_datum && .["class"] == "[VV_MARKED_DATUM] ([holder.marked_datum.type])")
+ 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)
+ if(VV_TEXT)
.["value"] = tgui_input_text(usr, "Enter new text:", "Text", current_value)
- if (.["value"] == null)
+ if(.["value"] == null)
.["class"] = null
return
- if (VV_MESSAGE)
+ if(VV_MESSAGE)
.["value"] = tgui_input_text(usr, "Enter new text:", "Text", current_value, multiline = TRUE)
- if (.["value"] == null)
+ if(.["value"] == null)
.["class"] = null
return
- if (VV_NUM)
+ if(VV_NUM)
.["value"] = tgui_input_number(usr, "Enter new number:", "Num", current_value, INFINITY, -INFINITY, round_value = FALSE)
- if (.["value"] == null)
+ if(.["value"] == null)
.["class"] = null
return
- if (VV_BITFIELD)
+ if(VV_BITFIELD)
.["value"] = input_bitfield(usr, "Editing bitfield: [var_name]", var_name, current_value)
- if (.["value"] == null)
+ if(.["value"] == null)
.["class"] = null
return
- if (VV_ATOM_TYPE)
+ if(VV_ATOM_TYPE)
.["value"] = pick_closest_path(FALSE)
- if (.["value"] == null)
+ if(.["value"] == null)
.["class"] = null
return
- if (VV_DATUM_TYPE)
+ if(VV_DATUM_TYPE)
.["value"] = pick_closest_path(FALSE, get_fancy_list_of_datum_types())
- if (.["value"] == null)
+ if(.["value"] == null)
.["class"] = null
return
- if (VV_TYPE)
+ if(VV_TYPE)
var/type = current_value
var/error = ""
do
type = tgui_input_text(usr, "Enter type:[error]", "Type", type)
- if (!type)
+ if(!type)
break
type = text2path(type)
error = "\nType not found, Please try again"
while(!type)
- if (!type)
+ if(!type)
.["class"] = null
return
.["value"] = type
-
- if (VV_ATOM_REFERENCE)
+ if(VV_ATOM_REFERENCE)
var/type = pick_closest_path(FALSE)
var/subtypes = vv_subtype_prompt(type)
- if (subtypes == null)
+ if(subtypes == null)
.["class"] = null
return
var/list/things = vv_reference_list(type, subtypes)
var/value = tgui_input_list(usr, "Select reference:", "Reference", things, current_value)
- if (!value)
+ if(!value)
.["class"] = null
return
.["value"] = things[value]
- if (VV_DATUM_REFERENCE)
+ 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)
+ if(subtypes == null)
.["class"] = null
return
var/list/things = vv_reference_list(type, subtypes)
var/value = tgui_input_list(usr, "Select reference:", "Reference", things, current_value)
- if (!value)
+ if(!value)
.["class"] = null
return
.["value"] = things[value]
- if (VV_MOB_REFERENCE)
+ if(VV_MOB_REFERENCE)
var/type = pick_closest_path(FALSE, make_types_fancy(typesof(/mob)))
var/subtypes = vv_subtype_prompt(type)
- if (subtypes == null)
+ if(subtypes == null)
.["class"] = null
return
var/list/things = vv_reference_list(type, subtypes)
var/value = tgui_input_list(usr, "Select reference:", "Reference", things, current_value)
- if (!value)
+ if(!value)
.["class"] = null
return
.["value"] = things[value]
+ if(VV_WEAKREF)
+ 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 = tgui_input_list(usr, "Select reference:", "Reference", things, current_value)
+ if(!value)
+ .["class"] = null
+ return
+ .["value"] = WEAKREF(things[value])
-
- if (VV_CLIENT)
+ if(VV_CLIENT)
.["value"] = tgui_input_list(usr, "Select reference:", "Reference", GLOB.clients, current_value)
- if (.["value"] == null)
+ if(.["value"] == null)
.["class"] = null
return
-
- if (VV_FILE)
+ if(VV_FILE)
.["value"] = input(usr, "Pick file:", "File") as null|file
- if (.["value"] == null)
+ if(.["value"] == null)
.["class"] = null
return
-
- if (VV_ICON)
- .["value"] = input(usr, "Pick icon:", "Icon") as null|icon
- if (.["value"] == null)
+ if(VV_ICON)
+ .["value"] = pick_and_customize_icon(pick_only=TRUE)
+ if(.["value"] == null)
.["class"] = null
return
-
- if (VV_MARKED_DATUM)
+ if(VV_MARKED_DATUM)
.["value"] = holder.marked_datum
- if (.["value"] == null)
+ if(.["value"] == null)
.["class"] = null
return
+ if(VV_TAGGED_DATUM)
+ if(.["value"] == null)
+ .["class"] = null
+ return
- if (VV_NEW_ATOM)
+ 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)
+ if(!type)
.["class"] = null
return
.["type"] = type
@@ -215,9 +265,9 @@
newguy.datum_flags |= DF_VAR_EDITED
.["value"] = newguy
- if (VV_NEW_DATUM)
+ if(VV_NEW_DATUM)
var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types())
- if (!type)
+ if(!type)
.["class"] = null
return
.["type"] = type
@@ -225,17 +275,17 @@
newguy.datum_flags |= DF_VAR_EDITED
.["value"] = newguy
- if (VV_NEW_TYPE)
+ if(VV_NEW_TYPE)
var/type = current_value
var/error = ""
do
type = tgui_input_text(usr, "Enter type:[error]", "Type", type)
- if (!type)
+ if(!type)
break
type = text2path(type)
error = "\nType not found, Please try again"
while(!type)
- if (!type)
+ if(!type)
.["class"] = null
return
.["type"] = type
@@ -244,7 +294,52 @@
newguy.datum_flags |= DF_VAR_EDITED
.["value"] = newguy
-
- if (VV_NEW_LIST)
- .["value"] = list()
+ if(VV_NEW_LIST)
.["type"] = /list
+ var/list/value = list()
+
+ var/expectation = alert("Would you like to populate the list", "Populate List?", "Yes", "No")
+ if(!expectation || expectation == "No")
+ .["value"] = value
+ return .
+
+ var/list/insert = null
+ while(TRUE)
+ insert = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT))
+ if(!insert["class"])
+ break
+ value += LIST_VALUE_WRAP_LISTS(insert["value"])
+
+
+ .["value"] = value
+
+ if(VV_TEXT_LOCATE)
+ var/datum/D
+ do
+ var/ref = tgui_input_text(usr, "Enter reference:", "Reference")
+ if(!ref)
+ break
+ D = locate(ref)
+ if(!D)
+ tgui_alert(usr,"Invalid ref!")
+ continue
+ if(!D.can_vv_mark())
+ tgui_alert(usr,"Datum can not be marked!")
+ continue
+ while(!D)
+ .["type"] = D.type
+ .["value"] = D
+
+ if(VV_COLOR)
+ .["value"] = tgui_color_picker(src, "Enter new color:", "Color", current_value)
+ if(.["value"] == null)
+ .["class"] = null
+ return
+
+ //if(VV_COLOR_MATRIX)
+ // .["value"] = open_color_matrix_editor()
+ // if(.["value"] == COLOR_MATRIX_IDENTITY) //identity is equivalent to null
+ // .["class"] = null
+
+ if(VV_INFINITY)
+ .["value"] = INFINITY
diff --git a/code/modules/admin/view_variables/helpers.dm b/code/modules/admin/view_variables/helpers.dm
deleted file mode 100644
index 3770f59bc1..0000000000
--- a/code/modules/admin/view_variables/helpers.dm
+++ /dev/null
@@ -1,199 +0,0 @@
-
-// Keep these two together, they *must* be defined on both
-// If /client ever becomes /datum/client or similar, they can be merged
-/datum/proc/get_view_variables_header()
- return span_bold("[src]")
-
-/atom/get_view_variables_header()
- return {"
- "} + span_bold("[src]") + {"
-
- "} + span_small("<< ") + {"
- "} + span_small("[dir2text(dir)] ") + {"
- "} + span_small(">> ") + {"
- "}
-
-/mob/living/get_view_variables_header()
- return {"
- "} + span_bold("[src]") + {"
- "} + span_small("<< [dir2text(dir)] >> ") + {"
- "} + span_small("[ckey ? ckey : "No ckey"] / [real_name ? real_name : "No real name"] ") + {"
-
- "} + span_small("BRUTE:[getBruteLoss()] ") + {"
- "} + span_small("FIRE:[getFireLoss()] ") + {"
- "} + span_small("TOXIN:[getToxLoss()] ") + {"
- "} + span_small("OXY:[getOxyLoss()] ") + {"
- "} + span_small("CLONE:[getCloneLoss()] ") + {"
- "} + span_small("BRAIN:[getBrainLoss()] ") + {"
- "}
-
-//This entire file needs to be removed eventually
-/datum/proc/get_view_variables_options()
- return ""
-
-/mob/get_view_variables_options()
- return ..() + {"
- Show player panel
- ---
- Give Modifier
- Give Internal Bleeding
- Give Spell
- Give Disease
- Give TG-style Disease
- Toggle Godmode
- Toggle Build Mode
-
- Make Space Ninja
- Make 2spooky
-
- Assume Direct Control
- Enable/Modify A.I
- Drop Everything
-
- Regenerate Icons
- Add Language
- Remove Language
- Add Organ
- Remove Organ
-
- Fix NanoUI
-
- Add Verb
- Remove Verb
- ---
- Gib
- "}
-
-/mob/living/carbon/human/get_view_variables_options()
- return ..() + {"
- Set Species
- Make AI
- Make cyborg
- Make monkey
- Make alien
- "}
-
-/obj/get_view_variables_options()
- return ..() + {"
- Delete all of type
- "}
-
-/turf/get_view_variables_options()
- return ..() + {"
- Trigger explosion
- Trigger EM pulse
- "}
-
-/obj/item/pda/get_view_variables_options()
- return ..() + {"
- Add Fake Prop Conversation
- "}
-
-/datum/proc/get_variables()
- . = vars - VV_hidden()
- if(!usr || !check_rights(R_ADMIN|R_DEBUG, FALSE))
- . -= VV_secluded()
-
-/datum/proc/get_variable_value(varname)
- return vars[varname]
-
-/datum/proc/set_variable_value(varname, value)
- vars[varname] = value
-
-/datum/proc/get_initial_variable_value(varname)
- return initial(vars[varname])
-
-/datum/proc/make_view_variables_variable_entry(var/varname, var/value, var/hide_watch = 0)
- return {"
- (E )
- (C )
- (M )
- [hide_watch ? "" : "(W )"]
- "}
-
-// No mass editing of clients
-/client/make_view_variables_variable_entry(var/varname, var/value, var/hide_watch = 0)
- return {"
- (E )
- (C )
- [hide_watch ? "" : "(W )"]
- "}
-
-// These methods are all procs and don't use stored lists to avoid VV exploits
-
-// The following vars cannot be viewed by anyone
-/datum/proc/VV_hidden()
- return list()
-
-// The following vars can only be viewed by R_ADMIN|R_DEBUG
-/datum/proc/VV_secluded()
- return list()
-
-/datum/configuration/VV_secluded()
- return vars
-
-// The following vars cannot be edited by anyone
-/datum/proc/VV_static()
- return list("parent_type")
-
-/atom/VV_static()
- return ..() + list("bound_x", "bound_y", "bound_height", "bound_width", "bounds", "step_x", "step_y", "step_size")
-
-/client/VV_static()
- return ..() + list("holder", "prefs")
-
-/datum/admins/VV_static()
- return vars
-
-// The following vars require R_DEBUG to edit
-/datum/proc/VV_locked()
- return list("vars", "cuffed")
-
-/client/VV_locked()
- return list("vars", "mob")
-
-/mob/VV_locked()
- return ..() + list("client")
-
-// The following vars require R_FUN|R_DEBUG to edit
-/datum/proc/VV_icon_edit_lock()
- return list()
-
-/atom/VV_icon_edit_lock()
- return ..() + list("icon", "icon_state", "overlays", "underlays")
-
-// The following vars require R_SPAWN|R_DEBUG to edit
-/datum/proc/VV_ckey_edit()
- return list()
-
-/mob/VV_ckey_edit()
- return list("key", "ckey")
-
-/client/VV_ckey_edit()
- return list("key", "ckey")
-
-/datum/proc/may_edit_var(var/user, var/var_to_edit) //User must be a CLIENT that is passed to this.
- if(!user)
- return FALSE
- if(ismob(user)) //Failsafe catch in case someone feeds a mob into us.
- var/mob/living = user
- user = living.client
- if(!(var_to_edit in vars))
- to_chat(user, span_warning("\The [src] does not have a var '[var_to_edit]'"))
- return FALSE
- if(var_to_edit in VV_static())
- return FALSE
- if((var_to_edit in VV_secluded()) && !check_rights_for(user, R_ADMIN|R_DEBUG))
- return FALSE
- if((var_to_edit in VV_locked()) && !check_rights_for(user, R_DEBUG))
- return FALSE
- if((var_to_edit in VV_ckey_edit()) && !check_rights_for(user, R_SPAWN|R_DEBUG))
- return FALSE
- if((var_to_edit in VV_icon_edit_lock()) && !check_rights_for(user, R_FUN|R_DEBUG))
- return FALSE
- return TRUE
-
-/proc/forbidden_varedit_object_types()
- return list(
- /datum/admins //Admins editing their own admin-power object? Yup, sounds like a good idea.
- )
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 0000000000..d519fedc41
--- /dev/null
+++ b/code/modules/admin/view_variables/mark_datum.dm
@@ -0,0 +1,17 @@
+/client/proc/mark_datum(datum/D)
+ if(!holder)
+ return
+ if(holder.marked_datum)
+ holder.UnregisterSignal(holder.marked_datum, COMSIG_PARENT_QDELETING)
+ vv_update_display(holder.marked_datum, "marked", "")
+ holder.marked_datum = D
+ holder.RegisterSignal(holder.marked_datum, COMSIG_PARENT_QDELETING, TYPE_PROC_REF(/datum/admins, handle_marked_del))
+ vv_update_display(D, "marked", VV_MSG_MARKED)
+
+ADMIN_VERB_ONLY_CONTEXT_MENU(mark_datum, R_HOLDER, "Mark Object", datum/target as mob|obj|turf|area in view())
+ user.mark_datum(target)
+
+/datum/admins/proc/handle_marked_del(datum/source)
+ SIGNAL_HANDLER
+ UnregisterSignal(marked_datum, COMSIG_PARENT_QDELETING)
+ marked_datum = null
diff --git a/code/modules/admin/view_variables/mass_edit_variables.dm b/code/modules/admin/view_variables/mass_edit_variables.dm
index f494f103ed..ff7c4cc928 100644
--- a/code/modules/admin/view_variables/mass_edit_variables.dm
+++ b/code/modules/admin/view_variables/mass_edit_variables.dm
@@ -1,29 +1,29 @@
-/client/proc/cmd_mass_modify_object_variables(atom/A, var_name)
- set category = "Debug"
- set name = "Mass Edit Variables"
- set desc="(target) Edit all instances of a target item's variables"
-
- var/method = 0 //0 means strict type detection while 1 means this type and all subtypes (IE: /obj/item with this set to 1 will set it to ALL items)
+/client/proc/cmd_mass_modify_object_variables(datum/target, var_name)
+ if(tgui_alert(src, "Are you sure you'd like to mass-modify every instance of the [var_name] variable? This can break everything if you do not know what you are doing.", "Slow down, chief!", list("Yes", "No"), 60 SECONDS) != "Yes")
+ return
if(!check_rights(R_VAREDIT))
return
- if(A && A.type)
- method = vv_subtype_prompt(A.type)
+ /// if false get only the strict type, get all subtypes too otherwise
+ var/strict_type = FALSE
+ if(target?.type)
+ strict_type = vv_subtype_prompt(target.type)
- src.massmodify_variables(A, var_name, method)
+ massmodify_variables(target, var_name, strict_type)
feedback_add_details("admin_verb","MVV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ //BLACKBOX_LOG_ADMIN_VERB("Mass Edit Variables")
-/client/proc/massmodify_variables(datum/O, var_name = "", method = 0)
+/client/proc/massmodify_variables(datum/target, var_name = "", strict_type = FALSE)
if(!check_rights(R_VAREDIT))
return
- if(!istype(O))
+ if(!istype(target))
return
var/variable = ""
if(!var_name)
var/list/names = list()
- for (var/V in O.vars)
+ for (var/V in target.vars)
names += V
names = sortList(names)
@@ -32,13 +32,13 @@
else
variable = var_name
- if(!variable || !O.can_vv_get(variable))
+ if(!variable || !target.can_vv_get(variable))
return
var/default
- var/var_value = O.vars[variable]
+ var/var_value = target.vars[variable]
if(variable in GLOB.VVckey_edit)
- to_chat(src, "It's forbidden to mass-modify ckeys. It'll crash everyone's client you dummy.")
+ to_chat(src, "It's forbidden to mass-modify ckeys. It'll crash everyone's client you dummy.", confidential = TRUE)
return
if(variable in GLOB.VVlocked)
if(!check_rights(R_DEBUG))
@@ -49,18 +49,18 @@
if(variable in GLOB.VVpixelmovement)
if(!check_rights(R_DEBUG))
return
- var/prompt = tgui_alert(src, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", list("ABORT","Continue","ABORT"))
+ var/prompt = tgui_alert(src, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", list("ABORT ", "Continue", " ABORT"))
if (prompt != "Continue")
return
default = vv_get_class(variable, var_value)
if(isnull(default))
- to_chat(src, "Unable to determine variable type.")
+ to_chat(src, "Unable to determine variable type.", confidential = TRUE)
else
- to_chat(src, "Variable appears to be [uppertext(default)] .")
+ to_chat(src, "Variable appears to be " + span_bold("[uppertext(default)]") + ".", confidential = TRUE)
- to_chat(src, "Variable contains: [var_value]")
+ to_chat(src, "Variable contains: [var_value]", confidential = TRUE)
if(default == VV_NUM)
var/dir_text = ""
@@ -75,7 +75,7 @@
dir_text += "WEST"
if(dir_text)
- to_chat(src, "If a direction, direction is: [dir_text]")
+ to_chat(src, "If a direction, direction is: [dir_text]", confidential = TRUE)
var/value = vv_get_value(default_class = default)
var/new_value = value["value"]
@@ -90,16 +90,16 @@
if (value["type"])
class = VV_NEW_TYPE
- var/original_name = "[O]"
+ var/original_name = "[target]"
var/rejected = 0
var/accepted = 0
switch(class)
if(VV_RESTORE_DEFAULT)
- to_chat(src, "Finding items...")
- var/list/items = get_all_of_type(O.type, method)
- to_chat(src, "Changing [items.len] items...")
+ to_chat(src, "Finding items...", confidential = TRUE)
+ var/list/items = get_all_of_type(target.type, strict_type)
+ to_chat(src, "Changing [items.len] items...", confidential = TRUE)
for(var/thing in items)
if (!thing)
continue
@@ -111,23 +111,21 @@
CHECK_TICK
if(VV_TEXT)
- var/list/varsvars = vv_parse_text(O, new_value)
+ var/list/varsvars = vv_parse_text(target, new_value)
var/pre_processing = new_value
var/unique
- if (varsvars && varsvars.len)
- unique = tgui_alert(usr, "Process vars unique to each instance, or same for all?", "Variable Association", list("Unique", "Same"))
- if(!unique)
- return
+ if (varsvars?.len)
+ unique = tgui_alert(src, "Process vars unique to each instance, or same for all?", "Variable Association", list("Unique", "Same"))
if(unique == "Unique")
unique = TRUE
else
unique = FALSE
for(var/V in varsvars)
- new_value = replacetext(new_value,"\[[V]]","[O.vars[V]]")
+ new_value = replacetext(new_value,"\[[V]]","[target.vars[V]]")
- to_chat(src, "Finding items...")
- var/list/items = get_all_of_type(O.type, method)
- to_chat(src, "Changing [items.len] items...")
+ to_chat(src, "Finding items...", confidential = TRUE)
+ var/list/items = get_all_of_type(target.type, strict_type)
+ to_chat(src, "Changing [items.len] items...", confidential = TRUE)
for(var/thing in items)
if (!thing)
continue
@@ -145,7 +143,7 @@
if (VV_NEW_TYPE)
var/many = tgui_alert(src, "Create only one [value["type"]] and assign each or a new one for each thing", "How Many", list("One", "Many", "Cancel"))
- if (!many || many == "Cancel")
+ if (many == "Cancel")
return
if (many == "Many")
many = TRUE
@@ -153,9 +151,9 @@
many = FALSE
var/type = value["type"]
- to_chat(src, "Finding items...")
- var/list/items = get_all_of_type(O.type, method)
- to_chat(src, "Changing [items.len] items...")
+ to_chat(src, "Finding items...", confidential = TRUE)
+ var/list/items = get_all_of_type(target.type, strict_type)
+ to_chat(src, "Changing [items.len] items...", confidential = TRUE)
for(var/thing in items)
if (!thing)
continue
@@ -171,9 +169,9 @@
CHECK_TICK
else
- to_chat(src, "Finding items...")
- var/list/items = get_all_of_type(O.type, method)
- to_chat(src, "Changing [items.len] items...")
+ to_chat(src, "Finding items...", confidential = TRUE)
+ var/list/items = get_all_of_type(target.type, strict_type)
+ to_chat(src, "Changing [items.len] items...", confidential = TRUE)
for(var/thing in items)
if (!thing)
continue
@@ -187,27 +185,27 @@
var/count = rejected+accepted
if (!count)
- to_chat(src, "No objects found")
+ to_chat(src, "No objects found", confidential = TRUE)
return
if (!accepted)
- to_chat(src, "Every object rejected your edit")
+ to_chat(src, "Every object rejected your edit", confidential = TRUE)
return
if (rejected)
- to_chat(src, "[rejected] out of [count] objects rejected your edit")
+ to_chat(src, "[rejected] out of [count] objects rejected your edit", confidential = TRUE)
- log_world("### MassVarEdit by [src]: [O.type] (A/R [accepted]/[rejected]) [variable]=[html_encode("[O.vars[variable]]")]([list2params(value)])")
- 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)")
+ log_world("### MassVarEdit by [src]: [target.type] (A/R [accepted]/[rejected]) [variable]=[html_encode("[target.vars[variable]]")]([list2params(value)])")
+ log_admin("[key_name(src)] mass modified [original_name]'s [variable] to [target.vars[variable]] ([accepted] objects modified)")
+ message_admins("[key_name_admin(src)] mass modified [original_name]'s [variable] to [target.vars[variable]] ([accepted] objects modified)")
-
-/proc/get_all_of_type(var/T, subtypes = TRUE)
+//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(T, subtypes = TRUE)
var/list/typecache = list()
typecache[T] = 1
if (subtypes)
typecache = typecacheof(typecache)
. = list()
if (ispath(T, /mob))
- for(var/mob/thing in mob_list)
+ for(var/mob/thing in world)
if (typecache[thing.type])
. += thing
CHECK_TICK
@@ -219,7 +217,13 @@
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
diff --git a/code/modules/admin/view_variables/modify_variables.dm b/code/modules/admin/view_variables/modify_variables.dm
index eb840d7ea5..d2021dc47b 100644
--- a/code/modules/admin/view_variables/modify_variables.dm
+++ b/code/modules/admin/view_variables/modify_variables.dm
@@ -1,10 +1,10 @@
-GLOBAL_LIST_INIT(VVlocked, list("vars", "datum_flags", "client", "mob")) //Requires DEBUG
+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")) //Requires DEBUG or FUN
+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")) //Requires DEBUG or SPAWN
+GLOBAL_LIST_INIT(VVckey_edit, list("key", "ckey")) //Requires DEBUG or SPAWN
GLOBAL_PROTECT(VVckey_edit)
-GLOBAL_LIST_INIT(VVpixelmovement, list("bound_x", "bound_y", "step_x", "step_y", "step_size", "bound_height", "bound_width", "bounds")) //No editing ever.
+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_parse_text(O, new_var)
@@ -17,14 +17,14 @@ GLOBAL_PROTECT(VVpixelmovement)
//FALSE = no subtypes, strict exact type pathing (or the type doesn't have subtypes)
//TRUE = Yes subtypes
//NULL = User cancelled at the prompt or invalid type given
-/client/proc/vv_subtype_prompt(var/type)
+/client/proc/vv_subtype_prompt(type)
if (!ispath(type))
return
var/list/subtypes = subtypesof(type)
if (!subtypes || !subtypes.len)
return FALSE
- if (subtypes && subtypes.len)
- switch(tgui_alert(usr, "Strict object type detection?", "Type detection", list("Strictly this type", "This type and subtypes", "Cancel")))
+ if (subtypes?.len)
+ switch(tgui_alert(usr,"Strict object type detection?", "Type detection", list("Strictly this type","This type and subtypes", "Cancel")))
if("Strictly this type")
return FALSE
if("This type and subtypes")
@@ -46,25 +46,25 @@ GLOBAL_PROTECT(VVpixelmovement)
var/things = get_all_of_type(type, subtypes)
var/i = 0
- for(var/datum/D as anything in things)
+ for(var/thing in things)
+ var/datum/D = thing
i++
//try one of 3 methods to shorten the type text:
- // fancy type,
- // fancy type with the base type removed from the begaining,
- // the type with the base type removed from the begaining
+ // fancy type,
+ // fancy type with the base type removed from the begaining,
+ // the type with the base type removed from the begaining
var/fancytype = types[D.type]
if (findtext(fancytype, types[type]))
- fancytype = copytext(fancytype, length(types[type])+1)
- var/shorttype = copytext("[D.type]", length("[type]")+1)
- if (length(shorttype) > length(fancytype))
+ fancytype = copytext(fancytype, length(types[type]) + 1)
+ var/shorttype = copytext("[D.type]", length("[type]") + 1)
+ if (length_char(shorttype) > length_char(fancytype))
shorttype = fancytype
if (!length(shorttype))
shorttype = "/"
- .["[D]([shorttype])\ref[D]#[i]"] = D
+ .["[D]([shorttype])[REF(D)]#[i]"] = D
/client/proc/mod_list_add_ass(atom/O) //hehe
-
var/list/L = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT))
var/class = L["class"]
if (!class)
@@ -94,15 +94,14 @@ GLOBAL_PROTECT(VVpixelmovement)
if (O)
L = L.Copy()
- L += var_value
+ L += list(var_value) //var_value could be a list
- if(IS_VALID_ASSOC_KEY(var_value))
- switch(tgui_alert(usr, "Would you like to associate a value with the list entry?","List VV",list("Yes","No")))
- if("Yes")
- L[var_value] = mod_list_add_ass(O) //hehe
+ switch(tgui_alert(usr,"Would you like to associate a value with the list entry?",,list("Yes","No")))
+ if("Yes")
+ L[var_value] = mod_list_add_ass(O) //hehe
if (O)
if (O.vv_edit_var(objectvar, L) == FALSE)
- to_chat(src, "Your edit was rejected by the object.")
+ to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: ADDED=[var_value]")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]")
@@ -112,27 +111,26 @@ GLOBAL_PROTECT(VVpixelmovement)
if(!check_rights(R_VAREDIT))
return
if(!istype(L, /list))
- to_chat(src, "Not a List.")
+ to_chat(src, "Not a List.", confidential = TRUE)
return
if(L.len > 1000)
- var/confirm = tgui_alert(src, "The list you're trying to edit is very long, continuing may crash the server.", "Long List!", list("Warning", "Continue", "Abort"))
+ var/confirm = tgui_alert(usr, "The list you're trying to edit is very long, continuing may crash the server.", "Warning", list("Continue", "Abort"))
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"
names["#[i] [key] = [value]"] = i
if (!index)
- var/variable = tgui_input_list(usr, "Which var?","Var", names + "(ADD VAR)" + "(CLEAR NULLS)" + "(CLEAR DUPES)" + "(SHUFFLE)")
+ var/variable = tgui_input_list(usr, "Which var?", "Var", names + "(ADD VAR)" + "(CLEAR NULLS)" + "(CLEAR DUPES)" + "(SHUFFLE)")
if(variable == null)
return
@@ -143,9 +141,9 @@ GLOBAL_PROTECT(VVpixelmovement)
if(variable == "(CLEAR NULLS)")
L = L.Copy()
- listclearnulls(L)
+ list_clear_nulls(L)
if (!O.vv_edit_var(objectvar, L))
- to_chat(src, "Your edit was rejected by the object.")
+ to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR NULLS")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR NULLS")
@@ -155,7 +153,7 @@ GLOBAL_PROTECT(VVpixelmovement)
if(variable == "(CLEAR DUPES)")
L = uniqueList(L)
if (!O.vv_edit_var(objectvar, L))
- to_chat(src, "Your edit was rejected by the object.")
+ to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR DUPES")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR DUPES")
@@ -165,7 +163,7 @@ GLOBAL_PROTECT(VVpixelmovement)
if(variable == "(SHUFFLE)")
L = shuffle(L)
if (!O.vv_edit_var(objectvar, L))
- to_chat(src, "Your edit was rejected by the object.")
+ to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: SHUFFLE")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: SHUFFLE")
@@ -179,32 +177,32 @@ GLOBAL_PROTECT(VVpixelmovement)
if (index == null)
return
var/assoc = 0
- if(IS_VALID_ASSOC_KEY(L[index]))
- var/prompt = tgui_alert(src, "Do you want to edit the key or its assigned value?", "Associated List", list("Key", "Assigned Value", "Cancel"))
- if (!prompt || prompt == "Cancel")
- return
- if (prompt == "Assigned Value")
- assoc = 1
- assoc_key = L[index]
+ var/prompt = tgui_alert(usr, "Do you want to edit the key or its assigned value?", "Associated List", list("Key", "Assigned Value", "Cancel"))
+ if (prompt == "Cancel")
+ return
+ if (prompt == "Assigned Value")
+ assoc = 1
+ assoc_key = L[index]
var/default
var/variable
- var/old_assoc_value //EXPERIMENTAL - Keep old associated value while modifying key, if any
- if (assoc)
- variable = L[assoc_key]
- else
- variable = L[index]
- //EXPERIMENTAL - Keep old associated value while modifying key, if any
- if(IS_VALID_ASSOC_KEY(variable))
- var/found = L[variable]
- if(!isnull(found))
- old_assoc_value = found
- //
+ 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
+ if(IS_VALID_ASSOC_KEY(variable))
+ var/found = L[variable]
+ if(!isnull(found))
+ old_assoc_value = found
+ //
default = vv_get_class(objectvar, variable)
- to_chat(src, "Variable appears to be [uppertext(default)] .")
+ to_chat(src, "Variable appears to be " + span_bold("[uppertext(default)]") + ".", confidential = TRUE)
- to_chat(src, "Variable contains: [variable]")
+ to_chat(src, "Variable contains: [variable]", confidential = TRUE)
if(default == VV_NUM)
var/dir_text = ""
@@ -220,7 +218,7 @@ GLOBAL_PROTECT(VVpixelmovement)
dir_text += "WEST"
if(dir_text)
- to_chat(usr, "If a direction, direction is: [dir_text]")
+ to_chat(usr, "If a direction, direction is: [dir_text]", confidential = TRUE)
var/original_var = variable
@@ -248,7 +246,7 @@ GLOBAL_PROTECT(VVpixelmovement)
L.Cut(index, index+1)
if (O)
if (O.vv_edit_var(objectvar, L))
- to_chat(src, "Your edit was rejected by the object.")
+ to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: REMOVED=[html_encode("[original_var]")]")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]")
@@ -261,15 +259,16 @@ GLOBAL_PROTECT(VVpixelmovement)
new_var = replacetext(new_var,"\[[V]]","[O.vars[V]]")
- 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(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.")
+ to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: [original_var]=[new_var]")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: [original_var]=[new_var]")
@@ -297,7 +296,7 @@ GLOBAL_PROTECT(VVpixelmovement)
if(param_var_name)
if(!(param_var_name in O.vars))
- to_chat(src, "A variable with this name ([param_var_name]) doesn't exist in this datum ([O])")
+ to_chat(src, "A variable with this name ([param_var_name]) doesn't exist in this datum ([O])", confidential = TRUE)
return
variable = param_var_name
@@ -308,17 +307,10 @@ GLOBAL_PROTECT(VVpixelmovement)
names = sortList(names)
- variable = tgui_input_list(usr, "Which var?","Var", names)
+ variable = tgui_input_list(usr, "Which var?", "Var", names)
if(!variable)
return
- if(variable in GLOB.VVpixelmovement)
- if(!check_rights(R_DEBUG))
- return
- var/prompt = tgui_alert(src, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", list("ABORT","Continue","ABORT"))
- if (prompt != "Continue")
- return
-
if(!O.can_vv_get(variable))
return
@@ -329,11 +321,11 @@ GLOBAL_PROTECT(VVpixelmovement)
var/default = vv_get_class(variable, var_value)
if(isnull(default))
- to_chat(src, "Unable to determine variable type.")
+ to_chat(src, "Unable to determine variable type.", confidential = TRUE)
else
- to_chat(src, "Variable appears to be [uppertext(default)] .")
+ to_chat(src, "Variable appears to be " + span_bold("[uppertext(default)]") + ".", confidential = TRUE)
- to_chat(src, "Variable contains: [var_value]")
+ to_chat(src, "Variable contains: [var_value]", confidential = TRUE)
if(default == VV_NUM)
var/dir_text = ""
@@ -348,7 +340,7 @@ GLOBAL_PROTECT(VVpixelmovement)
dir_text += "WEST"
if(dir_text)
- to_chat(src, "If a direction, direction is: [dir_text]")
+ to_chat(src, "If a direction, direction is: [dir_text]", confidential = TRUE)
if(autodetect_class && default != VV_NULL)
if (default == VV_TEXT)
@@ -385,9 +377,10 @@ GLOBAL_PROTECT(VVpixelmovement)
if (O.vv_edit_var(variable, var_new) == FALSE)
- to_chat(src, "Your edit was rejected by the object.")
+ to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
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]"
diff --git a/code/modules/admin/view_variables/nobody_wants_to_learn_matrix_math.dm b/code/modules/admin/view_variables/nobody_wants_to_learn_matrix_math.dm
new file mode 100644
index 0000000000..9dd013db13
--- /dev/null
+++ b/code/modules/admin/view_variables/nobody_wants_to_learn_matrix_math.dm
@@ -0,0 +1,80 @@
+
+/**
+ * ## nobody wants to learn matrix math!
+ *
+ * More than just a completely true statement, this datum is created as a tgui interface
+ * allowing you to modify each vector until you know what you're doing.
+ * Much like filteriffic, 'nobody wants to learn matrix math' is meant for developers like you and I
+ * to implement interesting matrix transformations without the hassle if needing to know... algebra? Damn, i'm stupid.
+ */
+/datum/nobody_wants_to_learn_matrix_math
+ var/atom/target
+ var/matrix/testing_matrix
+
+/datum/nobody_wants_to_learn_matrix_math/New(atom/target)
+ src.target = target
+ testing_matrix = matrix(target.transform)
+
+/datum/nobody_wants_to_learn_matrix_math/Destroy(force)
+ QDEL_NULL(testing_matrix)
+ return ..()
+
+/datum/nobody_wants_to_learn_matrix_math/tgui_state(mob/user)
+ return ADMIN_STATE(R_VAREDIT)
+
+/datum/nobody_wants_to_learn_matrix_math/tgui_close(mob/user)
+ qdel(src)
+
+/datum/nobody_wants_to_learn_matrix_math/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "MatrixMathTester")
+ ui.open()
+
+/datum/nobody_wants_to_learn_matrix_math/tgui_data()
+ var/list/data = list()
+ data["matrix_a"] = testing_matrix.a
+ data["matrix_b"] = testing_matrix.b
+ data["matrix_c"] = testing_matrix.c
+ data["matrix_d"] = testing_matrix.d
+ data["matrix_e"] = testing_matrix.e
+ data["matrix_f"] = testing_matrix.f
+ data["pixelated"] = target.appearance_flags & PIXEL_SCALE
+ return data
+
+/datum/nobody_wants_to_learn_matrix_math/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("change_var")
+ var/matrix_var_name = params["var_name"]
+ var/matrix_var_value = params["var_value"]
+ if(testing_matrix.vv_edit_var(matrix_var_name, matrix_var_value) == FALSE)
+ to_chat(src, "Your edit was rejected by the object. This is a bug with the matrix tester, not your fault, so report it on GitHub.", confidential = TRUE)
+ return
+ set_transform()
+ if("scale")
+ testing_matrix.Scale(params["x"], params["y"])
+ set_transform()
+ if("translate")
+ testing_matrix.Translate(params["x"], params["y"])
+ set_transform()
+ if("shear")
+ testing_matrix.Shear(params["x"], params["y"])
+ set_transform()
+ if("turn")
+ testing_matrix.Turn(params["angle"])
+ set_transform()
+ if("toggle_pixel")
+ target.appearance_flags ^= PIXEL_SCALE
+
+/datum/nobody_wants_to_learn_matrix_math/proc/set_transform()
+ animate(target, transform = testing_matrix, time = 0.5 SECONDS)
+ testing_matrix = matrix(target.transform)
+
+/client/proc/open_matrix_tester(atom/in_atom)
+ if(holder)
+ var/datum/nobody_wants_to_learn_matrix_math/matrix_tester = new(in_atom)
+ matrix_tester.tgui_interact(mob)
diff --git a/code/modules/admin/view_variables/particle_editor.dm b/code/modules/admin/view_variables/particle_editor.dm
new file mode 100644
index 0000000000..b841e0ef2c
--- /dev/null
+++ b/code/modules/admin/view_variables/particle_editor.dm
@@ -0,0 +1,212 @@
+/datum/particle_editor
+ /// movable whose particles we want to be editing
+ var/atom/movable/target
+
+/datum/particle_editor/New(atom/target)
+ src.target = target
+
+/datum/particle_editor/tgui_state(mob/user)
+ return ADMIN_STATE(R_VAREDIT)
+
+/datum/particle_editor/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "ParticleEdit")
+ ui.open()
+
+/datum/particle_editor/ui_assets(mob/user)
+ . = ..()
+ . += get_asset_datum(/datum/asset/simple/particle_editor)
+
+///returns ui_data values for the particle editor
+/particles/proc/return_ui_representation(mob/user)
+ var/data = list()
+ //affect entire set: no generators
+ data["width"] = width //float
+ data["height"] = height //float
+ data["count"] = count //float
+ data["spawning"] = spawning //float
+ data["bound1"] = islist(bound1) ? bound1 : list(bound1,bound1,bound1) //float OR list(x, y, z)
+ data["bound2"] = islist(bound2) ? bound2 : list(bound2,bound2,bound2) //float OR list(x, y, z)
+ data["gravity"] = gravity //list(x, y, z)
+
+ var/list/tgui_grad_list = list()
+ for(var/entry in gradient)
+ if(entry == "space")
+ tgui_grad_list += list(list("[entry]" = gradient[entry])) // package this thing else json encoding breaks
+ continue
+ tgui_grad_list += entry
+ data["gradient"] = tgui_grad_list //gradient array list(number, string, number, string, "loop", "space"=COLORSPACE_RGB)
+
+ data["transform"] = transform //list(a, b, c, d, e, f) OR list(xx,xy,xz, yx,yy,yz, zx,zy,zz) OR list(xx,xy,xz, yx,yy,yz, zx,zy,zz, cx,cy,cz) OR list(xx,xy,xz,xw, yx,yy,yz,yw, zx,zy,zz,zw, wx,wy,wz,ww)
+
+ //applied on spawn
+ if(islist(icon))
+ var/list/icon_data = list()
+ for(var/file in icon)
+ icon_data["[file]"] = icon[file]
+ data["icon"] = icon_data
+ else
+ data["icon"] = "[icon]" //list(icon = weight) OR file reference
+ data["icon_state"] = icon_state // list(icon_state = weight) OR string
+ if(isgenerator(lifespan))
+ data["lifespan"] = return_generator_args(lifespan)
+ else
+ data["lifespan"] = lifespan //float
+ if(isgenerator(fade))
+ data["fade"] = return_generator_args(fade)
+ else
+ data["fade"] = fade //float
+ if(isgenerator(fadein))
+ data["fadein"] = return_generator_args(fadein)
+ else
+ data["fadein"] = fadein //float
+ if(isgenerator(color))
+ data["color"] = return_generator_args(color)
+ else
+ data["color"] = color //float OR string
+ if(isgenerator(color_change))
+ data["color_change"] = return_generator_args(color_change)
+ else
+ data["color_change"] = color_change //float
+ if(isgenerator(position))
+ data["position"] = return_generator_args(position)
+ else
+ data["position"] = position //list(x,y) OR list(x,y,z)
+ if(isgenerator(velocity))
+ data["velocity"] = return_generator_args(velocity)
+ else
+ data["velocity"] = velocity //list(x,y) OR list(x,y,z)
+ if(isgenerator(scale))
+ data["scale"] = return_generator_args(scale)
+ else
+ data["scale"] = scale
+ if(isgenerator(grow))
+ data["grow"] = return_generator_args(grow)
+ else
+ data["grow"] = grow //float OR list(x,y)
+ if(isgenerator(rotation))
+ data["rotation"] = return_generator_args(rotation)
+ else
+ data["rotation"] = rotation
+ if(isgenerator(spin))
+ data["spin"] = return_generator_args(spin)
+ else
+ data["spin"] = spin //float
+ if(isgenerator(friction))
+ data["friction"] = return_generator_args(friction)
+ else
+ data["friction"] = friction //float: 0-1
+
+ //evaluated every tick
+ if(isgenerator(drift))
+ data["drift"] = return_generator_args(drift)
+ else
+ data["drift"] = drift
+ return data
+
+/datum/particle_editor/tgui_data(mob/user)
+ var/list/data = list()
+ data["target_name"] = target.name
+ if(!target.particles)
+ target.particles = new /particles
+ data["particle_data"] = target.particles.return_ui_representation(user)
+ return data
+
+/datum/particle_editor/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("delete_and_close")
+ ui.close()
+ target.particles = null
+ target = null
+ . = FALSE
+ if("new_type")
+ var/new_type = pick_closest_path(/particles, make_types_fancy(typesof(/particles)))
+ if(!new_type)
+ return FALSE
+ target.particles = new new_type
+ target.particles.datum_flags |= DF_VAR_EDITED
+ . = TRUE
+ if("transform_size")
+ var/list/matrix_size = list("Simple Matrix" = 6, "Complex Matrix" = 12, "Projection Matrix" = 16)
+ var/new_size = matrix_size[params["new_value"]]
+ if(!new_size)
+ return FALSE
+ . = TRUE
+ target.particles.datum_flags |= DF_VAR_EDITED
+ if(!target.particles.transform || length(target.particles.transform) != new_size)
+ switch(new_size)
+ if(6)
+ target.particles.transform = list(1,0,0, 1,0,0) // TRANSFORM_MATRIX_IDENTITY seems wrong for only particles?
+ if(12)
+ target.particles.transform = TRANSFORM_COMPLEX_MATRIX_IDENTITY
+ if(16)
+ target.particles.transform = TRANSFORM_PROJECTION_MATRIX_IDENTITY
+ return
+ if("edit")
+ var/particles/owner = target.particles
+ var/param_var_name = params["var"]
+ if(!(param_var_name in owner.vars))
+ return FALSE
+ var/var_value = params["new_value"]
+ var/var_mod = params["var_mod"]
+ // we can only return arrays from tgui so lets convert it to something usable if needed
+ switch(var_mod)
+ if(P_DATA_GENERATOR)
+ //these MUST be vectors and the others MUST be floats
+ if(var_value[1] in list(GEN_VECTOR, GEN_BOX))
+ if(!islist(var_value[2]))
+ var_value[2] = list(var_value[2],var_value[2],var_value[2])
+ if(!islist(var_value[3]))
+ var_value[3] = list(var_value[3],var_value[3],var_value[3])
+ //this means we just switched off a vector-requiring generator type
+ else if(islist(var_value[2]) && islist(var_value[3]))
+ var_value[2] = var_value[1]
+ var_value[3] = var_value[1]
+ var_value = generator(arglist(var_value))
+ if(P_DATA_ICON_ADD)
+ var_value = pick_and_customize_icon(ui.user, pick_only=TRUE)
+ if(!var_value)
+ return FALSE
+ var/list/new_values = list()
+ new_values += var_value
+ new_values[var_value] = 1
+ if(isicon(owner.icon))
+ new_values[owner.icon] = 1
+ owner.icon = new_values
+ else if(islist(owner.icon))
+ owner.icon[var_value] = 1
+ else
+ owner.icon = new_values
+ target.particles.datum_flags |= DF_VAR_EDITED
+ return TRUE
+ if(P_DATA_ICON_REMOVE)
+ for(var/file in owner.icon)
+ if("[file]" == var_value)
+ owner.icon -= file
+ UNSETEMPTY(owner.icon)
+ target.particles.datum_flags |= DF_VAR_EDITED
+ return TRUE
+ if(P_DATA_ICON_WEIGHT)
+ // [filename, new_weight]
+ var/list/mod_data = var_value
+ for(var/file in owner.icon)
+ if("[file]" == mod_data[1])
+ owner.icon[file] = mod_data[2]
+ target.particles.datum_flags |= DF_VAR_EDITED
+ return TRUE
+ if(P_DATA_GRADIENT)
+ var/list/new_grad_list = list()
+ for(var/entry in var_value)
+ new_grad_list += entry // Unpackage nested lists
+ owner.gradient = new_grad_list
+ target.particles.datum_flags |= DF_VAR_EDITED
+ return TRUE
+
+ owner.vars[param_var_name] = var_value
+ target.particles.datum_flags |= DF_VAR_EDITED
+ return TRUE
diff --git a/code/datums/reference_tracking_new.dm b/code/modules/admin/view_variables/reference_tracking.dm
similarity index 97%
rename from code/datums/reference_tracking_new.dm
rename to code/modules/admin/view_variables/reference_tracking.dm
index 56200a7d4e..b9fd0e6d2a 100644
--- a/code/datums/reference_tracking_new.dm
+++ b/code/modules/admin/view_variables/reference_tracking.dm
@@ -1,10 +1,6 @@
-// Drop in replacement for reference tracking
#ifdef REFERENCE_TRACKING
#define REFSEARCH_RECURSE_LIMIT 64
-/datum
- var/references_to_clear
-
/datum/proc/find_references(references_to_clear = INFINITY)
if(usr?.client)
if(tgui_alert(usr,"Running this will lock everything up for about 5 minutes. Would you like to begin the search?", "Find References", list("Yes", "No")) != "Yes")
@@ -17,8 +13,7 @@
_search_references()
//restart the garbage collector
SSgarbage.can_fire = TRUE
- SSgarbage.next_fire = world.time + world.tick_lag
- //SSgarbage.update_nextfire(reset_time = TRUE)
+ SSgarbage.update_nextfire(reset_time = TRUE)
/datum/proc/_search_references()
log_reftracker("Beginning search for references to a [type], looking for [references_to_clear] refs.")
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 0000000000..b4ca42860c
--- /dev/null
+++ b/code/modules/admin/view_variables/tag_datum.dm
@@ -0,0 +1,16 @@
+/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)
+
+ADMIN_VERB_ONLY_CONTEXT_MENU(tag_datum, R_NONE, "Tag Datum", datum/target_datum as mob|obj|turf|area in view())
+ user.tag_datum(target_datum)
diff --git a/code/modules/admin/view_variables/topic.dm b/code/modules/admin/view_variables/topic.dm
index 22b1dbe4ef..3d82c7b6bf 100644
--- a/code/modules/admin/view_variables/topic.dm
+++ b/code/modules/admin/view_variables/topic.dm
@@ -1,587 +1,157 @@
//DO NOT ADD MORE TO THIS FILE.
-//Use vv_do_topic()!
+//Use vv_do_topic() for datums!
/client/proc/view_var_Topic(href, href_list, hsrc)
- if((usr.client != src) || !check_rights(R_HOLDER))
+ if(!check_rights_for(src, R_VAREDIT) || !holder.CheckAdminHref(href, href_list))
return
- var/datum/target = locate(href_list["target"])
- if(istype(target))
- target.vv_do_topic(href_list)
+ var/target = GET_VV_TARGET
+ vv_do_basic(target, href_list, href)
+ if(isdatum(target))
+ 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"]))
+ var/datum/vars_target = locate(href_list["Vars"])
+ if(href_list["special_varname"]) // Some special vars can't be located even if you have their ref, you have to use this instead
+ vars_target = vars_target.vars[href_list["special_varname"]]
+ debug_variables(vars_target)
- //~CARN: for renaming mobs (updates their name, real_name, mind.name, their ID/PDA and datacore records).
- else if(href_list["rename"])
- if(!check_rights(R_VAREDIT)) return
+//~CARN: for renaming mobs (updates their name, real_name, mind.name, their ID/PDA and datacore records).
+ if(href_list["rename"])
- var/mob/M = locate(href_list["rename"])
+ var/mob/M = locate(href_list["rename"]) in mob_list
if(!istype(M))
- to_chat(src, "This can only be used on instances of type /mob")
+ to_chat(usr, "This can only be used on instances of type /mob", confidential = TRUE)
return
- var/new_name = sanitize(tgui_input_text(src,"What would you like to name this mob?","Input a name",M.real_name,MAX_NAME_LEN), MAX_NAME_LEN)
- if( !new_name || !M ) 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 the new name is something that would be restricted by IC chat filters,
+ // give the admin a warning but allow them to do it anyway if they want.
+ //if(is_ic_filtered(new_name) || is_soft_ic_filtered(new_name) && tgui_alert(usr, "Your selected name contains words restricted by IC chat filters. Confirm this new name?", "IC Chat Filter Conflict", list("Confirm", "Cancel")) == "Cancel")
+ // return
+
+ 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)
- href_list["datumrefresh"] = href_list["rename"]
-
- else if(href_list["varnameedit"] && href_list["datumedit"])
- if(!check_rights(R_VAREDIT)) return
-
- var/D = locate(href_list["datumedit"])
- if(!istype(D,/datum) && !istype(D,/client))
- to_chat(src, "This can only be used on instances of types /client or /datum")
- return
-
- modify_variables(D, href_list["varnameedit"], 1)
-
- else if(href_list["varnamechange"] && href_list["datumchange"])
- if(!check_rights(R_VAREDIT)) return
-
- var/D = locate(href_list["datumchange"])
- if(!istype(D,/datum) && !istype(D,/client))
- to_chat(src, "This can only be used on instances of types /client or /datum")
- return
-
- modify_variables(D, href_list["varnamechange"], 0)
-
- else if(href_list["varnamemass"] && href_list["datummass"])
- if(!check_rights(R_VAREDIT)) return
-
- var/atom/A = locate(href_list["datummass"])
- if(!istype(A))
- to_chat(src, "This can only be used on instances of type /atom")
- return
-
- cmd_mass_modify_object_variables(A, href_list["varnamemass"])
-
- else if(href_list["mob_player_panel"])
- if(!check_rights(0)) return
-
- var/mob/M = locate(href_list["mob_player_panel"])
- if(!istype(M))
- to_chat(src, "This can only be used on instances of type /mob")
- return
-
- src.holder.show_player_panel(M)
- href_list["datumrefresh"] = href_list["mob_player_panel"]
-
- else if(href_list["give_spell"])
- if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return
-
- var/mob/M = locate(href_list["give_spell"])
- if(!istype(M))
- to_chat(src, "This can only be used on instances of type /mob")
- return
-
- src.give_spell(M)
- href_list["datumrefresh"] = href_list["give_spell"]
-
- else if(href_list["give_modifier"])
- if(!check_rights(R_ADMIN|R_FUN|R_DEBUG|R_EVENT))
- return
-
- var/mob/living/M = locate(href_list["give_modifier"])
- if(!istype(M))
- to_chat(src, "This can only be used on instances of type /mob/living")
- return
-
- src.admin_give_modifier(M)
- href_list["datumrefresh"] = href_list["give_modifier"]
-
- else if(href_list["give_wound_internal"])
- if(!check_rights(R_ADMIN|R_FUN|R_DEBUG|R_EVENT))
- return
-
- var/mob/living/carbon/human/H = locate(href_list["give_wound_internal"])
- if(!istype(H))
- to_chat(src, span_notice("This can only be used on instances of type /mob/living/carbon/human"))
- return
-
- var/severity = tgui_input_number(src, "How much damage should the bleeding internal wound cause? \
- Bleed timer directly correlates with this. 0 cancels. Input is rounded to nearest integer.",
- "Wound Severity", 0)
- if(!severity) return
-
- var/obj/item/organ/external/chosen_organ = tgui_input_list(src, "Choose an external organ to inflict IB on!", "Organ Choice", H.organs)
- if(!chosen_organ || !istype(chosen_organ))
- to_chat(usr, span_notice("The chosen organ is of inappropriate type or no longer exists."))
- return
-
- var/datum/wound/internal_bleeding/I = new /datum/wound/internal_bleeding(severity)
- if(!I || !istype(I))
- to_chat(src, span_notice("Could not initialize internal wound"))
- log_debug("[usr] attempted to create an internal bleeding wound on [H]'s [chosen_organ] of [severity] damage \
- and wound initialization failed")
-
- chosen_organ.wounds += I
- chosen_organ.update_wounds()
- chosen_organ.update_damages()
- H.bad_external_organs += chosen_organ
- H.handle_organs()
-
- if(H.client)
- H.custom_pain("You feel a throbbing pain inside your [chosen_organ]", severity, force=TRUE)
- log_and_message_admins("created an Internal Bleeding wound on [H.ckey]'s mob [H] on [chosen_organ] of [severity] damage", usr)
-
- href_list["datumrefresh"] = href_list["give_wound_internal"]
-
- else if(href_list["godmode"])
- if(!check_rights(R_REJUVINATE)) return
-
- var/mob/M = locate(href_list["godmode"])
- if(!istype(M))
- to_chat(src, "This can only be used on instances of type /mob")
- return
-
- src.cmd_admin_godmode(M)
- href_list["datumrefresh"] = href_list["godmode"]
-
- else if(href_list["gib"])
- if(!check_rights(0)) return
-
- var/mob/M = locate(href_list["gib"])
- if(!istype(M))
- to_chat(src, "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"])
- if(!istype(M))
- to_chat(src, "This can only be used on instances of type /mob")
- return
-
- togglebuildmode(M)
- href_list["datumrefresh"] = href_list["build_mode"]
-
- else if(href_list["drop_everything"])
- if(!check_rights(R_DEBUG|R_ADMIN|R_EVENT)) return
-
- var/mob/M = locate(href_list["drop_everything"])
- if(!istype(M))
- to_chat(src, "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(0)) return
-
- var/mob/M = locate(href_list["direct_control"])
- if(!istype(M))
- to_chat(src, "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["give_ai"])
- if(!check_rights(0)) return
-
- var/mob/M = locate(href_list["give_ai"])
- if(!isliving(M))
- to_chat(src, span_notice("This can only be used on instances of type /mob/living"))
- return
- var/mob/living/L = M
- if(L.client || L.teleop)
- to_chat(src, span_warning("This cannot be used on player mobs!"))
- return
-
- if(L.ai_holder) //Cleaning up the original ai
- var/ai_holder_old = L.ai_holder
- L.ai_holder = null
- qdel(ai_holder_old) //Only way I could make #TESTING - Unable to be GC'd to stop. del() logs show it works.
- L.ai_holder_type = tgui_input_list(src, "Choose AI holder", "AI Type", typesof(/datum/ai_holder/))
- L.initialize_ai_holder()
- L.faction = sanitize(tgui_input_text(src, "Please input AI faction", "AI faction", "neutral"))
- L.a_intent = tgui_input_list(src, "Please choose AI intent", "AI intent", list(I_HURT, I_HELP))
- if(tgui_alert(src, "Make mob wake up? This is needed for carbon mobs.", "Wake mob?", list("Yes", "No")) == "Yes")
- L.AdjustSleeping(-100)
-
- else if(href_list["make_skeleton"])
- if(!check_rights(R_FUN)) return
-
- var/mob/living/carbon/human/H = locate(href_list["make_skeleton"])
- if(!istype(H))
- to_chat(src, "This can only be used on instances of type /mob/living/carbon/human")
- return
-
- H.ChangeToSkeleton()
- href_list["datumrefresh"] = href_list["make_skeleton"]
-
- 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(src, "This can only be used on instances of type /obj")
- return
-
- var/action_type = tgui_alert(src, "Strict type ([O.type]) or type and all subtypes?","Type Selection",list("Strict type","Type and subtypes","Cancel"))
- if(action_type == "Cancel" || !action_type)
- return
-
- if(tgui_alert(src, "Are you really sure you want to delete all objects of type [O.type]?","Delete All?",list("Yes","No")) != "Yes")
- return
-
- if(tgui_alert(src, "Second confirmation required. Delete?","REALLY?",list("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(src, "No objects of this type exist")
- return
- log_admin("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted) ")
- message_admins(span_notice("[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(src, "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(span_notice("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) "))
- else if(href_list["fakepdapropconvo"])
- if(!check_rights(R_FUN)) return
-
- var/obj/item/pda/P = locate(href_list["fakepdapropconvo"])
- if(!istype(P))
- to_chat(src, span_warning("This can only be done to instances of type /pda"))
- return
-
- P.createPropFakeConversation_admin(usr)
+ 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(0)) return
var/atom/A = locate(href_list["rotatedatum"])
if(!istype(A))
- to_chat(src, "This can only be done to instances of type /atom")
+ to_chat(usr, "This can only be done to instances of type /atom", confidential = TRUE)
return
switch(href_list["rotatedir"])
- if("right") A.set_dir(turn(A.dir, -45))
- if("left") A.set_dir(turn(A.dir, 45))
- href_list["datumrefresh"] = href_list["rotatedatum"]
+ if("right")
+ A.set_dir(turn(A.dir, -45))
+ if("left")
+ A.set_dir(turn(A.dir, 45))
+ vv_update_display(A, "dir", dir2text(A.dir))
- else if(href_list["makemonkey"])
- if(!check_rights(R_SPAWN)) return
-
- var/mob/living/carbon/human/H = locate(href_list["makemonkey"])
- if(!istype(H))
- to_chat(src, "This can only be done to instances of type /mob/living/carbon/human")
- return
-
- if(tgui_alert(src, "Confirm mob type change?","Confirm",list("Transform","Cancel")) != "Transform") return
- if(!H)
- to_chat(src, "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"])
- if(!istype(H))
- to_chat(src, "This can only be done to instances of type /mob/living/carbon/human")
- return
-
- if(tgui_alert(src, "Confirm mob type change?","Confirm",list("Transform","Cancel")) != "Transform") return
- if(!H)
- to_chat(src, "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"])
- if(!istype(H))
- to_chat(src, "This can only be done to instances of type /mob/living/carbon/human")
- return
-
- if(tgui_alert(src, "Confirm mob type change?","Confirm",list("Transform","Cancel")) != "Transform") return
- if(!H)
- to_chat(src, "Mob doesn't exist anymore")
- return
- holder.Topic(href, list("makealien"=href_list["makealien"]))
-
- else if(href_list["makeai"])
- if(!check_rights(R_SPAWN)) return
-
- var/mob/living/carbon/human/H = locate(href_list["makeai"])
- if(!istype(H))
- to_chat(src, "This can only be done to instances of type /mob/living/carbon/human")
- return
-
- if(tgui_alert(src, "Confirm mob type change?","Confirm",list("Transform","Cancel")) != "Transform") return
- if(!H)
- to_chat(src, "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"])
- if(!istype(H))
- to_chat(src, "This can only be done to instances of type /mob/living/carbon/human")
- return
-
- var/new_species = tgui_input_list(src, "Please choose a new species.","Species", GLOB.all_species)
-
- if(!H)
- to_chat(src, "Mob doesn't exist anymore")
- return
-
- if(H.set_species(new_species))
- to_chat(src, "Set species of [H] to [H.species].")
- else
- to_chat(src, "Failed! Something went wrong.")
-
- else if(href_list["addlanguage"])
- if(!check_rights(R_SPAWN)) return
-
- var/mob/H = locate(href_list["addlanguage"])
- if(!istype(H))
- to_chat(src, "This can only be done to instances of type /mob")
- return
-
- var/new_language = tgui_input_list(src, "Please choose a language to add.","Language", GLOB.all_languages)
-
- if(!new_language)
- return
-
- if(!H)
- to_chat(src, "Mob doesn't exist anymore")
- return
-
- if(H.add_language(new_language))
- to_chat(src, "Added [new_language] to [H].")
- else
- to_chat(src, "Mob already knows that language.")
-
- else if(href_list["remlanguage"])
- if(!check_rights(R_SPAWN)) return
-
- var/mob/H = locate(href_list["remlanguage"])
- if(!istype(H))
- to_chat(src, "This can only be done to instances of type /mob")
- return
-
- if(!H.languages.len)
- to_chat(src, "This mob knows no languages.")
- return
-
- var/datum/language/rem_language = tgui_input_list(src, "Please choose a language to remove.","Language", H.languages)
-
- if(!rem_language)
- return
-
- if(!H)
- to_chat(src, "Mob doesn't exist anymore")
- return
-
- if(H.remove_language(rem_language.name))
- to_chat(src, "Removed [rem_language] from [H].")
- else
- to_chat(src, "Mob doesn't know that language.")
-
- else if(href_list["addverb"])
- if(!check_rights(R_DEBUG)) return
-
- var/mob/H = locate(href_list["addverb"])
-
- if(!ismob(H))
- to_chat(src, "This can only be done to instances of type /mob")
- return
- var/list/possibleverbs = list()
- possibleverbs += "Cancel" // One for the top...
- possibleverbs += typesof(/mob/proc, /mob/verb)
- if(isobserver(H))
- possibleverbs += typesof(/mob/observer/dead/proc,/mob/observer/dead/verb)
- if(isliving(H))
- possibleverbs += typesof(/mob/living/proc,/mob/living/verb)
- if(ishuman(H))
- possibleverbs += typesof(/mob/living/carbon/proc,/mob/living/carbon/verb,/mob/living/carbon/human/verb,/mob/living/carbon/human/proc)
- if(isrobot(H))
- possibleverbs += typesof(/mob/living/silicon/proc,/mob/living/silicon/robot/proc,/mob/living/silicon/robot/verb)
- if(isAI(H))
- possibleverbs += typesof(/mob/living/silicon/proc,/mob/living/silicon/ai/proc,/mob/living/silicon/ai/verb)
- if(isanimal(H))
- possibleverbs += typesof(/mob/living/simple_mob/proc)
- possibleverbs -= H.verbs
- possibleverbs += "Cancel" // ...And one for the bottom
-
- var/verb = tgui_input_list(src, "Select a verb!", "Verbs", possibleverbs)
- if(!H)
- to_chat(src, "Mob doesn't exist anymore")
- return
- if(!verb || verb == "Cancel")
- return
- else
- add_verb(H, verb)
-
- else if(href_list["remverb"])
- if(!check_rights(R_DEBUG)) return
-
- var/mob/H = locate(href_list["remverb"])
-
- if(!istype(H))
- to_chat(src, "This can only be done to instances of type /mob")
- return
- var/verb = tgui_input_list(src, "Please choose a verb to remove.","Verbs", H.verbs)
- if(!H)
- to_chat(src, "Mob doesn't exist anymore")
- return
- if(!verb)
- return
- else
- remove_verb(H, verb)
-
- else if(href_list["addorgan"])
- if(!check_rights(R_SPAWN)) return
-
- var/mob/living/carbon/M = locate(href_list["addorgan"])
- if(!istype(M))
- to_chat(src, "This can only be done to instances of type /mob/living/carbon")
- return
-
- var/new_organ = tgui_input_list(src, "Please choose an organ to add.","Organ", subtypesof(/obj/item/organ))
- if(!new_organ) return
-
- if(!M)
- to_chat(src, "Mob doesn't exist anymore")
- return
-
- if(locate(new_organ) in M.internal_organs)
- to_chat(src, "Mob already has that organ.")
- return
-
- new new_organ(M)
-
-
- else if(href_list["remorgan"])
- if(!check_rights(R_SPAWN)) return
-
- var/mob/living/carbon/M = locate(href_list["remorgan"])
- if(!istype(M))
- to_chat(src, "This can only be done to instances of type /mob/living/carbon")
- return
-
- var/obj/item/organ/rem_organ = tgui_input_list(src, "Please choose an organ to remove.","Organ", M.internal_organs)
-
- if(!M)
- to_chat(src, "Mob doesn't exist anymore")
- return
-
- if(!(locate(rem_organ) in M.internal_organs))
- to_chat(src, "Mob does not have that organ.")
- return
-
- to_chat(src, "Removed [rem_organ] from [M].")
- rem_organ.removed()
- qdel(rem_organ)
-
- else if(href_list["fix_nano"])
- if(!check_rights(R_DEBUG)) return
-
- var/mob/H = locate(href_list["fix_nano"])
-
- if(!istype(H) || !H.client)
- to_chat(src, "This can only be done on mobs with clients")
- return
-
- H.client.send_resources()
-
- to_chat(src, "Resource files sent")
- to_chat(H, "Your NanoUI Resource files have been refreshed")
-
- log_admin("[key_name(usr)] resent the NanoUI resource files to [key_name(H)] ")
-
- else if(href_list["regenerateicons"])
- if(!check_rights(0)) return
-
- var/mob/M = locate(href_list["regenerateicons"])
- if(!ismob(M))
- to_chat(src, "This can only be done to instances of type /mob")
- return
- M.regenerate_icons()
else if(href_list["adjustDamage"] && href_list["mobToDamage"])
- if(!check_rights(R_DEBUG|R_ADMIN|R_FUN|R_EVENT)) return
- var/mob/living/L = locate(href_list["mobToDamage"])
- if(!istype(L)) return
+ var/mob/living/L = locate(href_list["mobToDamage"]) in mob_list
+ if(!istype(L))
+ return
var/Text = href_list["adjustDamage"]
- var/amount = tgui_input_number(src, "Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0, min_value=-INFINITY, round_value=FALSE)
+ var/amount = tgui_input_number(src, "Deal how much damage to mob? (Negative values here heal)", "Adjust [Text]loss", 0, min_value=-INFINITY, round_value=FALSE)
- if(!L)
- to_chat(src, "Mob doesn't exist anymore")
+ if (isnull(amount))
return
+ if(!L)
+ to_chat(usr, "Mob doesn't exist anymore", confidential = TRUE)
+ return
+
+ var/newamt
switch(Text)
- if("brute") L.adjustBruteLoss(amount)
- if("fire") L.adjustFireLoss(amount)
- if("toxin") L.adjustToxLoss(amount)
- if("oxygen")L.adjustOxyLoss(amount)
- if("brain") L.adjustBrainLoss(amount)
- if("clone") L.adjustCloneLoss(amount)
+ 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.adjustBrainLoss(amount)
+ newamt = L.getBrainLoss()
+ if("clone")
+ L.adjustCloneLoss(amount)
+ newamt = L.getCloneLoss()
+ //if("brain")
+ // L.adjustOrganLoss(ORGAN_SLOT_BRAIN, amount)
+ // newamt = L.get_organ_loss(ORGAN_SLOT_BRAIN)
+ //if("stamina")
+ // L.adjustStaminaLoss(amount, forced = TRUE)
+ // newamt = L.getStaminaLoss()
else
- to_chat(src, "You caused an error. DEBUG: Text:[Text] Mob:[L]")
+ to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]", confidential = TRUE)
return
if(amount != 0)
- log_admin("[key_name(usr)] dealt [amount] amount of [Text] damage to [L]")
- message_admins(span_notice("[key_name(usr)] dealt [amount] amount of [Text] damage to [L]"))
- href_list["datumrefresh"] = href_list["mobToDamage"]
- else if(href_list["expose"])
- if(!check_rights(R_ADMIN, FALSE))
- return
- var/thing = locate(href_list["expose"])
- if(!thing) //Do NOT QDELETED check!
- return
- var/value = vv_get_value(VV_CLIENT)
- if (value["class"] != VV_CLIENT)
- return
- var/client/C = value["value"]
- if (!C)
- return
- var/prompt = tgui_alert(src, "Do you want to grant [C] access to view this VV window? (they will not be able to edit or change anysrc nor open nested vv windows unless they themselves are an admin)", "Confirm", list("Yes", "No"))
- if (prompt != "Yes")
- return
- if(!thing)
- to_chat(src, span_warning("The object you tried to expose to [C] no longer exists (GC'd)"))
- 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 [src]")
- to_chat(C, "[holder.fakekey ? "an Administrator" : "[usr.client.key]"] has granted you access to view a View Variables window")
- C.debug_variables(thing)
+ 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]")
+ else if(href_list["item_to_tweak"] && href_list["var_tweak"])
+
+ var/obj/item/editing = locate(href_list["item_to_tweak"])
+ if(!istype(editing) || QDELING(editing))
+ return
+
+ var/existing_val = -1
+ switch(href_list["var_tweak"])
+ if("damtype")
+ existing_val = editing.damtype
+ if("force")
+ existing_val = editing.force
+ //if("wound")
+ // existing_val = editing.wound_bonus
+ //if("bare wound")
+ // existing_val = editing.exposed_wound_bonus
+ else
+ CRASH("Invalid var_tweak passed to item vv set var: [href_list["var_tweak"]]")
+
+ var/new_val
+ if(href_list["var_tweak"] == "damtype")
+ //new_val = tgui_input_list(usr, "Enter the new damage type for [editing]", "Set Damtype", list(BRUTE, BURN, TOX, OXY, STAMINA, BRAIN), existing_val)
+ new_val = tgui_input_list(usr, "Enter the new damage type for [editing]","Set Damtype", list(BRUTE, BURN, TOX, OXY, CLONE, HALLOSS, ELECTROCUTE, BIOACID, SEARING, ELECTROMAG), existing_val)
+ else
+ new_val = tgui_input_number(usr, "Enter the new value for [editing]'s [href_list["var_tweak"]]","Set [href_list["var_tweak"]]", existing_val)
+ if(isnull(new_val) || new_val == existing_val || QDELETED(editing) || !check_rights(R_VAREDIT))
+ return
+
+ switch(href_list["var_tweak"])
+ if("damtype")
+ editing.damtype = new_val
+ if("force")
+ editing.force = new_val
+ //if("wound")
+ // editing.wound_bonus = new_val
+ //if("bare wound")
+ // editing.exposed_wound_bonus = new_val
+
+ message_admins("[key_name(usr)] set [editing]'s [href_list["var_tweak"]] to [new_val] (was [existing_val])")
+ log_admin("[key_name(usr)] set [editing]'s [href_list["var_tweak"]] to [new_val] (was [existing_val])")
+ vv_update_display(editing, href_list["var_tweak"], istext(new_val) ? uppertext(new_val) : new_val)
+
+ //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) || islist(DAT))
+ if(isdatum(DAT) || istype(DAT, /client) || islist(DAT))
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 0000000000..679eb97fff
--- /dev/null
+++ b/code/modules/admin/view_variables/topic_basic.dm
@@ -0,0 +1,145 @@
+//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, span_warning("The object you tried to expose to [C] no longer exists (nulled or hard-deled)"), confidential = TRUE)
+ 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", confidential = TRUE)
+ C.debug_variables(target)
+ if(check_rights(R_DEBUG))
+ if(href_list[VV_HK_DELETE])
+ usr.client.admin_delete(target)
+ if (isturf(target)) // show the turf that took its place
+ usr.client.debug_variables(target)
+ return
+
+ 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
+ var/list/names = list()
+ var/list/componentsubtypes = sortList(subtypesof(/datum/component), GLOBAL_PROC_REF(cmp_typepaths_asc))
+ names += "---Components---"
+ names += componentsubtypes
+ names += "---Elements---"
+ names += sortList(subtypesof(/datum/element), GLOBAL_PROC_REF(cmp_typepaths_asc))
+
+ var/result = tgui_input_list(usr, "Choose a component/element to add", "Add Component", names)
+ if(isnull(result))
+ return
+ if(!usr || result == "---Components---" || result == "---Elements---")
+ return
+
+ if(QDELETED(src))
+ to_chat(usr, "That thing doesn't exist anymore!", confidential = TRUE)
+ return
+
+ var/add_source
+ if(ispath(result, /datum/component))
+ var/datum/component/comp_path = result
+ if(initial(comp_path.dupe_mode) == COMPONENT_DUPE_SOURCES)
+ add_source = tgui_input_text(usr, "Enter a source for the component", "Add Component", "ADMIN-ABUSE")
+ if(isnull(add_source))
+ return
+
+ var/list/lst = get_callproc_args()
+ if(!lst)
+ return
+
+ var/datumname = "error"
+ lst.Insert(1, result)
+ if(result in componentsubtypes)
+ datumname = "component"
+ target._AddComponent(lst, add_source)
+ else
+ datumname = "element"
+ target._AddElement(lst)
+ log_admin("[key_name(usr)] has added [result] [datumname] to [key_name(target)].")
+ message_admins(span_notice("[key_name_admin(usr)] has added [result] [datumname] to [key_name_admin(target)]."))
+ if(href_list[VV_HK_REMOVECOMPONENT] || href_list[VV_HK_MASS_REMOVECOMPONENT])
+ if(!check_rights(NONE))
+ return
+ var/mass_remove = href_list[VV_HK_MASS_REMOVECOMPONENT]
+ var/list/components = target._datum_components.Copy()
+ var/list/names = list()
+ names += "---Components---"
+ if(length(components))
+ names += sortList(components, GLOBAL_PROC_REF(cmp_typepaths_asc))
+ names += "---Elements---"
+ // We have to list every element here because there is no way to know what element is on this object without doing some sort of hack.
+ names += sortList(subtypesof(/datum/element), GLOBAL_PROC_REF(cmp_typepaths_asc))
+ var/path = tgui_input_list(usr, "Choose a component/element to remove. All elements listed here may not be on the datum.", "Remove element", names)
+ if(isnull(path))
+ return
+ if(!usr || path == "---Components---" || path == "---Elements---")
+ return
+ if(QDELETED(src))
+ to_chat(usr, "That thing doesn't exist anymore!")
+ return
+ var/list/targets_to_remove_from = list(target)
+ if(mass_remove)
+ var/method = vv_subtype_prompt(target.type)
+ targets_to_remove_from = get_all_of_type(target.type, method)
+
+ if(alert(usr, "Are you sure you want to mass-delete [path] on [target.type]?", "Mass Remove Confirmation", "Yes", "No") == "No")
+ return
+
+ for(var/datum/target_to_remove_from as anything in targets_to_remove_from)
+ if(ispath(path, /datum/element))
+ var/list/lst = get_callproc_args()
+ if(!lst)
+ lst = list()
+ lst.Insert(1, path)
+ target._RemoveElement(lst)
+ else
+ var/list/components_actual = target_to_remove_from.GetComponents(path)
+ for(var/to_delete in components_actual)
+ qdel(to_delete)
+
+ message_admins(span_notice("[key_name_admin(usr)] has [mass_remove? "mass" : ""] removed [path] component from [mass_remove? target.type : key_name_admin(target)]."))
+ //if(href_list[VV_HK_MODIFY_GREYSCALE])
+ // if(!check_rights(NONE))
+ // return
+ // var/datum/greyscale_modify_menu/menu = new(target, usr, SSgreyscale.configurations, unlocked = TRUE)
+ // menu.ui_interact(usr)
+
+ if(href_list[VV_HK_CALLPROC])
+ return SSadmin_verbs.dynamic_invoke_verb(usr, /datum/admin_verb/call_proc_datum, target)
diff --git a/code/modules/admin/view_variables/topic_list.dm b/code/modules/admin/view_variables/topic_list.dm
index 1f535f2b42..b699dfdc0b 100644
--- a/code/modules/admin/view_variables/topic_list.dm
+++ b/code/modules/admin/view_variables/topic_list.dm
@@ -1,44 +1,43 @@
//LISTS - CAN NOT DO VV_DO_TOPIC BECAUSE LISTS AREN'T DATUMS :(
/client/proc/vv_do_list(list/target, href_list)
- if(!check_rights(R_VAREDIT))
- return
- var/target_index = text2num(href_list["index"])
- 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 = tgui_alert(usr, "Do you want to remove item number [target_index] from list?", "Confirm", list("Yes", "No"))
- if (prompt != "Yes")
+ 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 = tgui_alert(usr,"Do you want to remove item number [target_index] from list?", "Confirm", list("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])
+ list_clear_nulls(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.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(5000, 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")
+ 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
index 98dc6b89e7..e477ad7bc3 100644
--- a/code/modules/admin/view_variables/view_variables.dm
+++ b/code/modules/admin/view_variables/view_variables.dm
@@ -5,17 +5,17 @@ ADMIN_VERB_AND_CONTEXT_MENU(debug_variables, (R_DEBUG|R_SERVER|R_ADMIN|R_SPAWN|R
user.debug_variables(thing)
// This is kept as a separate proc because admins are able to show VV to non-admins
-/client/proc/debug_variables(datum/D in world)
+/client/proc/debug_variables(datum/thing in world)
set category = "Debug.Investigate"
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, span_danger("You need to be an administrator to access this."))
+ 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, span_danger("You need to be an administrator to access this."), confidential = TRUE)
return
- if(!D)
+ if(!thing)
return
var/dark = usr.client.prefs ? usr.client.prefs.read_preference(/datum/preference/toggle/holder/vv_dark) : TRUE
@@ -24,305 +24,306 @@ ADMIN_VERB_AND_CONTEXT_MENU(debug_variables, (R_DEBUG|R_SERVER|R_ADMIN|R_SPAWN|R
var/datum/asset/asset_cache_datum = get_asset_datum(/datum/asset/simple/vv)
asset_cache_datum.send(usr)
- var/islist = islist(D) || (!isdatum(D) && hascall(D, "Cut")) // Some special lists don't count as lists, but can be detected by if they have list procs
- if(!islist && !isdatum(D))
+ if(isappearance(thing))
+ thing = get_vv_appearance(thing) // this is /mutable_appearance/our_bs_subtype
+ var/islist = islist(thing) || (!isdatum(thing) && hascall(thing, "Cut")) // Some special lists don't count as lists, but can be detected by if they have list procs
+ if(!islist && !isdatum(thing))
return
- //VOREStation Edit Start - the rest of this proc in a spawn
- spawn(0)
- var/title = ""
- var/refid = "\ref[D]"
- var/icon/sprite
- var/hash
+ var/title = ""
+ var/refid = REF(thing)
+ var/icon/sprite
+ var/hash
- var/type = islist ? /list : D.type
- var/no_icon = FALSE
+ var/type = islist ? /list : thing.type
+ var/no_icon = FALSE
- if(isatom(D))
- var/atom/AT = D
- if(use_gfi)
- sprite = getFlatIcon(D)
- if(!sprite)
- no_icon = TRUE
- else if(AT.icon && AT.icon_state)
- sprite = new /icon(AT.icon, AT.icon_state)
- else
+ if(isatom(thing))
+ var/atom/AT = thing
+ if(use_gfi)
+ sprite = getFlatIcon(thing)
+ if(!sprite)
no_icon = TRUE
+ else if(AT.icon && AT.icon_state)
+ sprite = new /icon(AT.icon, AT.icon_state)
+ else
+ no_icon = TRUE
- else if(isimage(D))
- // icon_state=null shows first image even if dmi has no icon_state for null name.
- // This list remembers which dmi has null icon_state, to determine if icon_state=null should display a sprite
- // (NOTE: icon_state="" is correct, but saying null is obvious)
- var/static/list/dmi_nullstate_checklist = list()
- var/image/image_object = D
- var/icon_filename_text = "[image_object.icon]" // "icon(null)" type can exist. textifying filters it.
- if(icon_filename_text)
- if(image_object.icon_state)
+ else if(isimage(thing))
+ // icon_state=null shows first image even if dmi has no icon_state for null name.
+ // This list remembers which dmi has null icon_state, to determine if icon_state=null should display a sprite
+ // (NOTE: icon_state="" is correct, but saying null is obvious)
+ var/static/list/dmi_nullstate_checklist = list()
+ var/image/image_object = thing
+ var/icon_filename_text = "[image_object.icon]" // "icon(null)" type can exist. textifying filters it.
+ if(icon_filename_text)
+ if(image_object.icon_state)
+ sprite = icon(image_object.icon, image_object.icon_state)
+
+ else // it means: icon_state=""
+ if(!dmi_nullstate_checklist[icon_filename_text])
+ dmi_nullstate_checklist[icon_filename_text] = ICON_STATE_CHECKED
+ if(icon_exists(image_object.icon, ""))
+ // this dmi has nullstate. We'll allow "icon_state=null" to show image.
+ dmi_nullstate_checklist[icon_filename_text] = ICON_STATE_NULL
+
+ if(dmi_nullstate_checklist[icon_filename_text] == ICON_STATE_NULL)
sprite = icon(image_object.icon, image_object.icon_state)
- else // it means: icon_state=""
- if(!dmi_nullstate_checklist[icon_filename_text])
- dmi_nullstate_checklist[icon_filename_text] = ICON_STATE_CHECKED
- if(icon_exists(image_object.icon, ""))
- // this dmi has nullstate. We'll allow "icon_state=null" to show image.
- dmi_nullstate_checklist[icon_filename_text] = ICON_STATE_NULL
+ var/sprite_text
+ if(sprite)
+ hash = md5(sprite)
+ src << browse_rsc(sprite, "vv[hash].png")
+ sprite_text = no_icon ? "\[NO ICON\]" : ""
- if(dmi_nullstate_checklist[icon_filename_text] == ICON_STATE_NULL)
- sprite = icon(image_object.icon, image_object.icon_state)
+ title = "[thing] ([REF(thing)]) = [type]"
+ var/formatted_type = replacetext("[type]", "/", "/")
- var/sprite_text
- if(sprite)
- hash = md5(sprite)
- src << browse_rsc(sprite, "vv[hash].png")
- sprite_text = no_icon ? "\[NO ICON\]" : " "
+ var/list/header = islist ? list("/list ") : thing.vv_get_header()
- title = "[D] (\ref[D]) = [type]"
- var/formatted_type = replacetext("[type]", "/", "/")
+ var/ref_line = "@[copytext(refid, 2, -1)]" // get rid of the brackets, add a @ prefix for copy pasting in asay
- var/list/header = islist(D)? list(span_bold("/list")) : D.vv_get_header()
+ var/marked_line
+ if(holder && holder.marked_datum && holder.marked_datum == thing)
+ marked_line = VV_MSG_MARKED
+ var/tagged_line
+ if(holder && LAZYFIND(holder.tagged_datums, thing))
+ var/tag_index = LAZYFIND(holder.tagged_datums, thing)
+ tagged_line = VV_MSG_TAGGED(tag_index)
+ var/varedited_line
+ if(!islist && (thing.datum_flags & DF_VAR_EDITED))
+ varedited_line = VV_MSG_EDITED
+ var/deleted_line
+ if(!islist && thing.gc_destroyed)
+ deleted_line = VV_MSG_DELETED
- var/ref_line = "@[copytext(refid, 2, -1)]" // get rid of the brackets, add a @ prefix for copy pasting in asay
+ 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] = "[name] "
+ else
+ dropdownoptions = thing.vv_get_dropdown()
- 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/names = list()
+ if(!islist)
+ for(var/varname in thing.vars)
+ names += varname
- var/list/dropdownoptions = list()
- var/autoconvert_dropdown = FALSE
- if (islist)
- dropdownoptions = list(
- "---",
- "Add Item" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_ADD]=TRUE;target=[refid]",
- "Remove Nulls" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_ERASE_NULLS]=TRUE;target=[refid]",
- "Remove Dupes" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_ERASE_DUPES]=TRUE;target=[refid]",
- "Set len" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_SET_LENGTH]=TRUE;target=[refid]",
- "Shuffle" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_SHUFFLE]=TRUE;target=[refid]",
- // "Show VV To Player" = "?_src_=vars;[HrefToken()];[VV_HK_EXPOSE]=TRUE;target=[refid]" // TODO - Not yet implemented for lists
- )
- autoconvert_dropdown = TRUE
- else
- dropdownoptions = D.vv_get_dropdown()
- var/list/dropdownoptions_html = list()
- if(autoconvert_dropdown)
- for (var/name in dropdownoptions)
- var/link = dropdownoptions[name]
- if (link)
- dropdownoptions_html += "[name] "
- else
- dropdownoptions_html += "[name] "
- else
- dropdownoptions_html = dropdownoptions + D.get_view_variables_options()
+ sleep(1 TICKS)
- var/list/names = list()
- if (!islist)
- names = D.get_variables()
- //sleep(1)//For some reason, without this sleep, VVing will cause client to disconnect on certain objects. //VOREStation edit - commented out, replaced with spawn(0) above
+ var/ui_scale = prefs?.read_preference(/datum/preference/toggle/ui_scale)
- var/ui_scale = prefs?.read_preference(/datum/preference/toggle/ui_scale)
+ var/list/variable_html = list()
+ if(islist)
+ var/list/list_value = thing
+ for(var/i in 1 to list_value.len)
+ var/key = list_value[i]
+ var/value
+ if(IS_NORMAL_LIST(list_value) && IS_VALID_ASSOC_KEY(key))
+ value = list_value[key]
+ variable_html += debug_variable(i, value, 0, list_value)
+ else
+ names = sortList(names)
+ for(var/varname in names)
+ if(thing.can_vv_get(varname))
+ variable_html += thing.vv_get_var(varname)
- 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, 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]
-
- [!ui_scale && window_scaling ? "" : ""]
-
-
-
-
-
-
-
-
-
-
- [sprite_text]
-
- [header.Join()]
-
-
-
-
-
- [formatted_type]
- [ref_line]
- [marked_line]
- [varedited_line]
- [deleted_line]
-
-
-
-
-
-
-
-
-
- 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.
-
+ return true;
+ }
+
+ // onkeyup
+ function handle_keyup() {
+ updateSearch();
+ }
+
+ // onchange
+ function handle_dropdown(list) {
+ var value = list.options\[list.selectedIndex].value;
+ if (value !== "") {
+ location.href = value;
+ }
+ list.selectedIndex = 0;
+ document.getElementById('filter').focus();
+ }
+
+ // byjax
+ function replace_span(what) {
+ var idx = what.indexOf(':');
+ document.getElementById(what.substr(0, idx)).innerHTML = what.substr(idx + 1);
+ }
+
+
-
-
- [variable_html.Join()]
-
-
-
-
- "}
- var/size_string = "size=475x650";
- if(ui_scale && window_scaling)
- size_string = "size=[475 * window_scaling]x[650 * window_scaling]"
+
+
+
+ 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.
+
+
+
+
+
+ [variable_html.Join()]
+
+
+