From 9985960e5b33d1ce182d5cd5639f4034b9df7821 Mon Sep 17 00:00:00 2001 From: Karolis Date: Sun, 4 Aug 2019 21:13:20 +0300 Subject: [PATCH] Adds modern player panel for staff needs. (#6795) --- code/controllers/subsystems/garbage-debug.dm | 1 + code/modules/admin/admin_verbs.dm | 22 +- code/modules/admin/player_panel.dm | 474 ++++-------------- .../karolis-modern playerpannel.yml | 4 + interface/skin.dmf | 8 +- vueui/README.md | 24 +- vueui/src/assets/global.scss | 18 + vueui/src/assets/theme-basic.scss | 14 + vueui/src/assets/theme-nano.scss | 14 + .../components/view/admin/player-panel.vue | 88 ++++ vueui/src/components/vui/button.vue | 7 + vueui/src/components/vui/tooltip.vue | 33 ++ vueui/src/store.js | 5 +- vueui/src/utils.js | 22 +- 14 files changed, 317 insertions(+), 417 deletions(-) create mode 100644 html/changelogs/karolis-modern playerpannel.yml create mode 100644 vueui/src/components/view/admin/player-panel.vue create mode 100644 vueui/src/components/vui/tooltip.vue diff --git a/code/controllers/subsystems/garbage-debug.dm b/code/controllers/subsystems/garbage-debug.dm index 774c2d54070..7d81e3fb6c3 100644 --- a/code/controllers/subsystems/garbage-debug.dm +++ b/code/controllers/subsystems/garbage-debug.dm @@ -871,5 +871,6 @@ SearchVar(mark) SearchVar(contamination_overlay) SearchVar(vsc) + SearchVar(global_player_panel) #endif diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index bcabfae87fb..aeb90e4df37 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -1,7 +1,7 @@ //admin verb groups - They can overlap if you so wish. Only one of each verb will exist in the verbs list regardless var/list/admin_verbs_default = list( /datum/admins/proc/show_player_panel, //shows an interface for individual players, with various links (links require additional flags, - /client/proc/player_panel, + /client/proc/player_panel_modern, /client/proc/toggleadminhelpsound, /*toggles whether we hear a sound when adminhelps/PMs are used*/ /client/proc/deadmin_self, /*destroys our own admin datum so we can play as a regular player*/ /client/proc/hide_verbs, /*hides all our adminverbs*/ @@ -11,7 +11,6 @@ var/list/admin_verbs_default = list( ) var/list/admin_verbs_admin = list( /client/proc/debug_variables, /*allows us to -see- the variables of any instance in the game.*/ - /client/proc/player_panel_new, /*shows an interface for all players, with links to various panels*/ /client/proc/invisimin, /*allows our mob to go invisible/visible*/ // /datum/admins/proc/show_traitor_panel, /*interface which shows a mob's mind*/ -Removed due to rare practical use. Moved to debug verbs ~Errorage */ /datum/admins/proc/show_game_mode, /*Configuration window for the current game mode.*/ @@ -327,7 +326,6 @@ var/list/admin_verbs_mod = list( /client/proc/admin_ghost, // allows us to ghost/reenter body at will, /client/proc/cmd_mod_say, /datum/admins/proc/show_player_info, - /client/proc/player_panel_new, /client/proc/dsay, /datum/admins/proc/show_skills, /datum/admins/proc/show_player_panel, @@ -371,7 +369,7 @@ var/list/admin_verbs_dev = list( //will need to be altered - Ryan784 /client/proc/hide_most_verbs, /client/proc/kill_air, /client/proc/kill_airgroup, - /client/proc/player_panel, + /client/proc/player_panel_modern, /client/proc/togglebuildmodeself, /client/proc/toggledebuglogs, /client/proc/ZASSettings, @@ -515,22 +513,16 @@ var/list/admin_verbs_cciaa = list( to_chat(mob, "Invisimin on. You are now as invisible as a ghost.") mob.alpha = max(mob.alpha - 100, 0) - -/client/proc/player_panel() +/client/proc/player_panel_modern() set name = "Player Panel" set category = "Admin" if(holder) - holder.player_panel_old() - feedback_add_details("admin_verb","PP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + if(!global_player_panel) + global_player_panel = new() + global_player_panel.ui_interact(usr) + feedback_add_details("admin_verb","PPM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return -/client/proc/player_panel_new() - set name = "Player Panel New" - set category = "Admin" - if(holder) - holder.player_panel_new() - feedback_add_details("admin_verb","PPN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return /client/proc/check_antagonists() set name = "Check Antagonists" diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm index 97bb3d693f8..c0e6d3a1a8a 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -1,393 +1,3 @@ - -/datum/admins/proc/player_panel_new()//The new one - if (!usr.client.holder) - return - var/dat = "Admin Player Panel" - - //javascript, the part that does most of the work~ - dat += {" - - - - - - - "} - - //body tag start + onload and onkeypress (onkeyup) javascript event calls - dat += "" - - //title + search bar - dat += {" - - - - - - - - -
- Player panel
- Hover over a line to see more information - Check antagonists -

-

- Search: -
- - "} - - //player table header - dat += {" - - "} - - var/list/mobs = sortmobs() - var/i = 1 - for(var/mob/M in mobs) - if(M.ckey) - - var/color = "#e6e6e6" - if(i%2 == 0) - color = "#f2f2f2" - var/is_antagonist = is_special_character(M) - - var/M_job = "" - - if(isliving(M)) - - if(iscarbon(M)) //Carbon stuff - if(ishuman(M)) - M_job = M.job - else if(isslime(M)) - M_job = "slime" - else if(issmall(M)) - M_job = "Monkey" - else if(isalien(M)) - M_job = "Alien" - else - M_job = "Carbon-based" - - else if(issilicon(M)) //silicon - if(isAI(M)) - M_job = "AI" - else if(ispAI(M)) - M_job = "pAI" - else if(isrobot(M)) - M_job = "Cyborg" - else - M_job = "Silicon-based" - - else if(isanimal(M)) //simple animals - if(iscorgi(M)) - M_job = "Corgi" - else - M_job = "Animal" - - else - M_job = "Living" - - else if(istype(M,/mob/abstract/new_player)) - M_job = "New player" - - else if(isobserver(M)) - M_job = "Ghost" - - M_job = replacetext(M_job, "'", "") - M_job = replacetext(M_job, "\"", "") - M_job = replacetext(M_job, "\\", "") - - var/M_name = M.name - M_name = replacetext(M_name, "'", "") - M_name = replacetext(M_name, "\"", "") - M_name = replacetext(M_name, "\\", "") - var/M_rname = M.real_name - M_rname = replacetext(M_rname, "'", "") - M_rname = replacetext(M_rname, "\"", "") - M_rname = replacetext(M_rname, "\\", "") - - var/M_key = M.key - M_key = replacetext(M_key, "'", "") - M_key = replacetext(M_key, "\"", "") - M_key = replacetext(M_key, "\\", "") - - //output for each mob - dat += {" - - - - - - "} - - i++ - - - //player table ending - dat += {" -
- - - [M_name] - [M_rname] - [M_key] ([M_job]) - -
-
-
- - - - "} - - usr << browse(dat, "window=players;size=600x480") - -//The old one -/datum/admins/proc/player_panel_old() - if (!usr.client.holder) - return - - var/dat = "Player Menu" - dat += "" - //add to this if wanting to add back in IP checking - //add if you want to know their ip to the lists below - var/list/mobs = sortmobs() - - for(var/mob/M in mobs) - if(!M.ckey) continue - - dat += "" - if(isAI(M)) - dat += "" - else if(isrobot(M)) - dat += "" - else if(ishuman(M)) - dat += "" - else if(istype(M, /mob/living/silicon/pai)) - dat += "" - else if(istype(M, /mob/abstract/new_player)) - dat += "" - else if(isobserver(M)) - dat += "" - else if(issmall(M)) - dat += "" - else if(isalien(M)) - dat += "" - else - dat += "" - - - if(istype(M,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if(H.mind && H.mind.assigned_role) - dat += "" - else - dat += "" - - - dat += {" - - - "} - - - - if(usr.client) - if(!check_rights(R_MOD|R_ADMIN, 0)) - dat += {""} - else - switch(is_special_character(M)) - if(0) - dat += {""} - if(1) - dat += {""} - if(2) - dat += {""} - else - dat += {""} - - - - dat += "
NameReal NameAssigned JobKeyOptionsPMTraitor?
IP:(IP: [M.lastKnownIP])
[M.name]AICyborg[M.real_name]pAINew PlayerGhostMonkeyAlienUnknown[H.mind.assigned_role]NA[M.key ? (M.client ? M.key : "[M.key] (DC)") : "No key"]XPM N/A Traitor?Traitor?Traitor? N/A
" - - usr << browse(dat, "window=players;size=640x480") - - - /datum/admins/proc/check_antagonists() if (SSticker.current_state >= GAME_STATE_PLAYING) var/dat = "Round Status

Round Status

" @@ -418,3 +28,87 @@ usr << browse(dat, "window=roundstatus;size=400x500") else alert("The game hasn't started yet!") + +var/datum/vueui_module/player_panel/global_player_panel +/datum/vueui_module/player_panel + var/something = TRUE + +/datum/vueui_module/player_panel/ui_interact(var/mob/user) + if (!usr.client.holder) + return + var/datum/vueui/ui = SSvueui.get_open_ui(user, src) + if(!ui) + ui = new(user, src, "admin-player-panel", 640, 480, "Modern player panel", nstate = interactive_state) + ui.header = "minimal" + ui.auto_update_content = TRUE + + ui.open() + +/datum/vueui_module/player_panel/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui) + if(!data) + . = data = list() + if(!user.client.holder) + return + var/isMod = check_rights(R_MOD|R_ADMIN, 0, user) + VUEUI_SET_CHECK(data["holder_ref"], "\ref[user.client.holder]", ., data) + VUEUI_SET_CHECK(data["ismod"], isMod, ., data) + + + var/list/mobs = sortmobs() + + LAZYINITLIST(data["players"]) + if(data["players"].len != mobs.len) + data["players"].Cut() + for(var/mob/M in mobs) + var/ref = "\ref[M]" + if(!M.ckey) + data["players"][ref] = FALSE + continue + LAZYINITLIST(data["players"][ref]) + VUEUI_SET_CHECK(data["players"][ref]["ref"], ref, ., data) + VUEUI_SET_CHECK(data["players"][ref]["name"], M.name, ., data) + var/real_name = GetMobRealName(M) + VUEUI_SET_CHECK(data["players"][ref]["real_name"], real_name, ., data) + if(istype(M,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + if(H.mind?.assigned_role) + VUEUI_SET_CHECK(data["players"][ref]["assigment"], H.mind.assigned_role, ., data) + else + VUEUI_SET_CHECK(data["players"][ref]["assigment"], "NA", ., data) + VUEUI_SET_CHECK(data["players"][ref]["key"], M.key, ., data) + if(isMod) + VUEUI_SET_CHECK(data["players"][ref]["ip"], M.lastKnownIP, ., data) + else + VUEUI_SET_CHECK(data["players"][ref]["ip"], FALSE, ., data) + VUEUI_SET_CHECK(data["players"][ref]["connected"], !!M.client, ., data) + if(isMod) + var/special_char = is_special_character(M) + VUEUI_SET_CHECK(data["players"][ref]["antag"], special_char, ., data) + else + VUEUI_SET_CHECK(data["players"][ref]["antag"], -1, ., data) + if(isMod && M.client?.player_age) + var/age = M.client.player_age + if(age == "Requires database") + age = "NA" + VUEUI_SET_CHECK(data["players"][ref]["age"], age, ., data) + else + VUEUI_SET_CHECK(data["players"][ref]["age"], FALSE, ., data) + +/datum/vueui_module/player_panel/proc/GetMobRealName(var/mob/M) + if(isAI(M)) + return "AI" + if(isrobot(M)) + return "Cyborg" + if(ishuman(M)) + return M.real_name + if(istype(M, /mob/living/silicon/pai)) + return "pAI" + if(istype(M, /mob/abstract/new_player)) + return "New Player" + if(isobserver(M)) + return "Ghost" + if(issmall(M)) + return "Monkey" + if(isalien(M)) + return "Alien" + return "Unknown" \ No newline at end of file diff --git a/html/changelogs/karolis-modern playerpannel.yml b/html/changelogs/karolis-modern playerpannel.yml new file mode 100644 index 00000000000..33109114dbf --- /dev/null +++ b/html/changelogs/karolis-modern playerpannel.yml @@ -0,0 +1,4 @@ +author: Karolis2011 +delete-after: True +changes: + - rscadd: "Added new player panel to make player management easier." \ No newline at end of file diff --git a/interface/skin.dmf b/interface/skin.dmf index d8d04231449..e27b6bf0b37 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -187,7 +187,7 @@ macro "borghotkeymode" command = "asay" elem name = "F6" - command = "Player-Panel-New" + command = "Player-Panel" elem name = "F7" command = "Admin-PM" @@ -357,7 +357,7 @@ macro "macro" command = "asay" elem name = "F6" - command = "Player-Panel-New" + command = "Player-Panel" elem name = "F7" command = "Admin-PM" @@ -590,7 +590,7 @@ macro "hotkeymode" command = "asay" elem name = "F6" - command = "Player-Panel-New" + command = "Player-Panel" elem name = "F7" command = "Admin-PM" @@ -760,7 +760,7 @@ macro "borgmacro" command = "asay" elem name = "F6" - command = "Player-Panel-New" + command = "Player-Panel" elem name = "F7" command = "Admin-PM" diff --git a/vueui/README.md b/vueui/README.md index 9d308fd4672..57fd04c4283 100644 --- a/vueui/README.md +++ b/vueui/README.md @@ -185,10 +185,14 @@ Example: ``` Parameters: - `$slot` - Contents of button. - - `params` - key value pairs to send to `Topic` of object. + - `params` - key value pairs to send to `Topic` of object. Can contain objects or arrays (DM keyed lists and lists respectively). + - `unsafe-params` - Used to execute a generic `Topic()` call to the game. Requires that you specify the `src` object as a valid reference, otherwise it will not function. **Should not generally be used**, primary use-case is backwards compatibility with older APIs that are spread out over multiple objects. - `icon` - icon that should be used in that button. For available icons look at `\vueui\styles\icons.scss` - `push-state` - Boolean determining if current ui state should be pushed on button click. This often results in `vueui_data_change` call right before `Topic` call. +Events: + - `click` - Fires when button is clicked. + ### VuiProgress `` Simple progress bar for representing progress of a process or indicate status. @@ -235,6 +239,20 @@ Example: ``` Parameters: - `label` - Label to display next to contents. + +### VuiTooltip `` +Helper to getting nice tooltips when you hover over text. Works with buttons. + +Example: +```Vue +VueUi UI element +Advanced use of tooltips +``` +Parameters: + - `$slot` - Contents of tooltip + - `$slot:label` - Actual content that is always shown. If slot is set, `label` parameter is ignored. + - `label` - Actual text that is always shown + ### VuiInputNumeric `` Numeric input helper to help inputting large and small numbers. @@ -250,6 +268,7 @@ Parameters: - `push-state` - Boolean determining if current ui state should be pushed on input change. - `width` - Determines width of input text field. - `decimal-places` - How many decimal places are allowed. + Events: - `input` - Fires when value changes. Value is number currently entered. @@ -263,5 +282,6 @@ Example: Parameters: - `input` - Initial array with elements to search. - `keys` - Array of strings listing keys to be searched. + Events: - - `input` - Fires when search text changes. Event value is new sorted array. \ No newline at end of file + - `input` - Fires when search text changes. Event value is new sorted array. diff --git a/vueui/src/assets/global.scss b/vueui/src/assets/global.scss index 4c98e65ffd1..86899614379 100644 --- a/vueui/src/assets/global.scss +++ b/vueui/src/assets/global.scss @@ -93,4 +93,22 @@ input, textarea, button, select, .button { .csserror { display: none; +} + +.tooltip { + &.label { + text-decoration: underline; + } +} + +.red { + color: red; +} + +.green { + color: green; +} + +.blue { + color: green; } \ No newline at end of file diff --git a/vueui/src/assets/theme-basic.scss b/vueui/src/assets/theme-basic.scss index 7d45f5aa8c7..fda8fddc32a 100644 --- a/vueui/src/assets/theme-basic.scss +++ b/vueui/src/assets/theme-basic.scss @@ -17,6 +17,13 @@ border-bottom:1px solid #ee0000; } } + + .tooltip { + &.content { + background: #ffffff; + border: 1px solid darkgrey; + } + } } .theme-basic-dark { @@ -38,4 +45,11 @@ .uiTitleWrapper { background-color: #464646; } + + .tooltip { + &.content { + background: #000000; + border: 1px solid darkgrey; + } + } } \ No newline at end of file diff --git a/vueui/src/assets/theme-nano.scss b/vueui/src/assets/theme-nano.scss index 25ba4190bb8..2f78dc2021d 100644 --- a/vueui/src/assets/theme-nano.scss +++ b/vueui/src/assets/theme-nano.scss @@ -186,6 +186,13 @@ vertical-align: top; margin: 0 2px 2px 0; } + + .tooltip { + &.content { + background: #272727; + border: 1px solid darkgrey; + } + } } .theme-nano-light { @@ -275,4 +282,11 @@ } } } + .tooltip { + &.content { + color: black; + background: #c9c9c9; + border: 1px solid darkgrey; + } + } } diff --git a/vueui/src/components/view/admin/player-panel.vue b/vueui/src/components/view/admin/player-panel.vue new file mode 100644 index 00000000000..26265781b85 --- /dev/null +++ b/vueui/src/components/view/admin/player-panel.vue @@ -0,0 +1,88 @@ + + + + + + diff --git a/vueui/src/components/vui/button.vue b/vueui/src/components/vui/button.vue index 0a97aa2203c..9fe5bb8ebb0 100644 --- a/vueui/src/components/vui/button.vue +++ b/vueui/src/components/vui/button.vue @@ -18,6 +18,10 @@ export default { type: Object, default: null }, + unsafeParams: { + type: Object, + default: null + }, pushState: { type: Boolean, default: false @@ -33,6 +37,9 @@ export default { return } this.$emit('click') + if(this.unsafeParams) { + Utils.sendToTopicRaw(this.unsafeParams) + } if(!this.params) { if (this.pushState) { Store.pushState() diff --git a/vueui/src/components/vui/tooltip.vue b/vueui/src/components/vui/tooltip.vue new file mode 100644 index 00000000000..1fedab417f9 --- /dev/null +++ b/vueui/src/components/vui/tooltip.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/vueui/src/store.js b/vueui/src/store.js index af93935abe7..2792f477451 100644 --- a/vueui/src/store.js +++ b/vueui/src/store.js @@ -26,7 +26,10 @@ export default { isUpdating: false, lastUpdateTime: null, getStatePushString () { - return "vueuistateupdate=" + encodeURIComponent(JSON.stringify(this.state)) + return "vueuistateupdate=" + encodeURIComponent(this.getStatePushDataString()) + }, + getStatePushDataString () { + return JSON.stringify(this.state) }, pushState() { if (this.isUpdating || (Date.now() - this.lastUpdateTime) < 100 ) { diff --git a/vueui/src/utils.js b/vueui/src/utils.js index a46fa899736..0a997c57447 100644 --- a/vueui/src/utils.js +++ b/vueui/src/utils.js @@ -1,14 +1,26 @@ import Store from './store.js' +import store from './store.js'; export default { - sendToTopic(data, pushState = false) { - var r = new XMLHttpRequest() - var sendUrl = "?src=" + Store.state.uiref + "&vueuihrefjson=" + encodeURIComponent(JSON.stringify(data)) - if (pushState) { - sendUrl += "&" + Store.getStatePushString() + sendToTopicRaw(data) { + var sendparams = [] + for(var val in data) { + sendparams.push(encodeURIComponent(val) + "=" + encodeURIComponent(data[val])) } + var r = new XMLHttpRequest() + var sendUrl = "?" + sendparams.join("&") r.open("GET", sendUrl, true); r.send() }, + sendToTopic(data, pushState = false) { + var pushData = { + src: Store.state.uiref, + vueuihrefjson: JSON.stringify(data) + } + if(pushState) { + pushData["vueuistateupdate"] = store.getStatePushDataString() + } + this.sendToTopicRaw(pushData) + }, dotNotationRead(object, key) { return key.split('.').reduce((a, b) => a[b], object); }