diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql
index 5e275853b95..1dfba24e77f 100644
--- a/SQL/paradise_schema.sql
+++ b/SQL/paradise_schema.sql
@@ -255,7 +255,6 @@ CREATE TABLE `player` (
`nanoui_fancy` smallint(4) DEFAULT '1',
`show_ghostitem_attack` smallint(4) DEFAULT '1',
`lastchangelog` varchar(32) NOT NULL DEFAULT '0',
- `lastchangelog` varchar(32) NOT NULL,
`space_parallax` smallint(4) DEFAULT '1',
`space_dust` smallint(4) DEFAULT '1',
`parallax_speed` float(24) DEFAULT '2',
diff --git a/SQL/paradise_schema_prefixed.sql b/SQL/paradise_schema_prefixed.sql
index 0eea48c2064..2d51e7babf1 100644
--- a/SQL/paradise_schema_prefixed.sql
+++ b/SQL/paradise_schema_prefixed.sql
@@ -255,7 +255,6 @@ CREATE TABLE `SS13_player` (
`nanoui_fancy` smallint(4) DEFAULT '1',
`show_ghostitem_attack` smallint(4) DEFAULT '1',
`lastchangelog` varchar(32) NOT NULL DEFAULT '0',
- `lastchangelog` varchar(32) NOT NULL,
`space_parallax` smallint(4) DEFAULT '1',
`space_dust` smallint(4) DEFAULT '1',
`parallax_speed` float(24) DEFAULT '2',
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index e1965cf701e..889a0824f35 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -270,4 +270,75 @@ proc/add_logs(mob/target, mob/user, what_done, var/object=null, var/addition=nul
. = 0
break
if(progress)
- qdel(progbar)
\ No newline at end of file
+ qdel(progbar)
+
+/proc/admin_mob_info(mob/M, mob/user = usr)
+ if(!ismob(M))
+ to_chat(user, "This can only be used on instances of type /mob")
+ return
+
+ var/location_description = ""
+ var/special_role_description = ""
+ var/health_description = ""
+ var/gender_description = ""
+ var/turf/T = get_turf(M)
+
+ //Location
+ if(isturf(T))
+ if(isarea(T.loc))
+ location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z] in area [T.loc])"
+ else
+ location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z])"
+
+ //Job + antagonist
+ if(M.mind)
+ special_role_description = "Role: [M.mind.assigned_role]; Antagonist: [M.mind.special_role]; Has been rev: [(M.mind.has_been_rev)?"Yes":"No"]"
+ else
+ special_role_description = "Role: Mind datum missing Antagonist: Mind datum missing; Has been rev: Mind datum missing;"
+
+ //Health
+ if(isliving(M))
+ var/mob/living/L = M
+ var/status
+ switch(M.stat)
+ if(CONSCIOUS)
+ status = "Alive"
+ if(UNCONSCIOUS)
+ status = "Unconscious"
+ if(DEAD)
+ status = "Dead"
+ health_description = "Status = [status]"
+ health_description += "
Oxy: [L.getOxyLoss()] - Tox: [L.getToxLoss()] - Fire: [L.getFireLoss()] - Brute: [L.getBruteLoss()] - Clone: [L.getCloneLoss()] - Brain: [L.getBrainLoss()]"
+ else
+ health_description = "This mob type has no health to speak of."
+
+ //Gener
+ switch(M.gender)
+ if(MALE, FEMALE)
+ gender_description = "[M.gender]"
+ else
+ gender_description = "[M.gender]"
+
+ to_chat(user, "Info about [M.name]: ")
+ to_chat(user, "Mob type = [M.type]; Gender = [gender_description] Damage = [health_description]")
+ to_chat(user, "Name = [M.name]; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = [M.key];")
+ to_chat(user, "Location = [location_description];")
+ to_chat(user, "[special_role_description]")
+ to_chat(user, "(PM) (PP) (VV) (SM) (FLW) (CA)")
+
+// Gets the first mob contained in an atom, and warns the user if there's not exactly one
+/proc/get_mob_in_atom_with_warning(atom/A, mob/user = usr)
+ if(!istype(A))
+ return null
+ if(ismob(A))
+ return A
+
+ . = null
+ for(var/mob/M in A)
+ if(!.)
+ . = M
+ else
+ to_chat(user, "Multiple mobs in [A], using first mob found...")
+ break
+ if(!.)
+ to_chat(user, "No mob located in [A].")
diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm
index 869ee83ff9c..d04abfe5a87 100644
--- a/code/_onclick/observer.dm
+++ b/code/_onclick/observer.dm
@@ -25,6 +25,21 @@
return
var/list/modifiers = params2list(params)
+ if(check_rights(R_ADMIN, 0)) // Admin click shortcuts
+ var/mob/M
+ if(modifiers["shift"] && modifiers["ctrl"])
+ client.debug_variables(A)
+ return
+ if(modifiers["ctrl"])
+ M = get_mob_in_atom_with_warning(A)
+ if(M)
+ client.holder.show_player_panel(M)
+ return
+ if(modifiers["shift"] && modifiers["middle"])
+ M = get_mob_in_atom_with_warning(A)
+ if(M)
+ admin_mob_info(M)
+ return
if(modifiers["shift"])
ShiftClickOn(A)
return
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index a7fc809aa88..6dacca13d12 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -1565,53 +1565,7 @@
else if(href_list["adminmoreinfo"])
var/mob/M = locate(href_list["adminmoreinfo"])
- if(!ismob(M))
- to_chat(usr, "This can only be used on instances of type /mob")
- return
-
- var/location_description = ""
- var/special_role_description = ""
- var/health_description = ""
- var/gender_description = ""
- var/turf/T = get_turf(M)
-
- //Location
- if(isturf(T))
- if(isarea(T.loc))
- location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z] in area [T.loc])"
- else
- location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z])"
-
- //Job + antagonist
- if(M.mind)
- special_role_description = "Role: [M.mind.assigned_role]; Antagonist: [M.mind.special_role]; Has been rev: [(M.mind.has_been_rev)?"Yes":"No"]"
- else
- special_role_description = "Role: Mind datum missing Antagonist: Mind datum missing; Has been rev: Mind datum missing;"
-
- //Health
- if(isliving(M))
- var/mob/living/L = M
- var/status
- switch(M.stat)
- if(0) status = "Alive"
- if(1) status = "Unconscious"
- if(2) status = "Dead"
- health_description = "Status = [status]"
- health_description += "
Oxy: [L.getOxyLoss()] - Tox: [L.getToxLoss()] - Fire: [L.getFireLoss()] - Brute: [L.getBruteLoss()] - Clone: [L.getCloneLoss()] - Brain: [L.getBrainLoss()]"
- else
- health_description = "This mob type has no health to speak of."
-
- //Gener
- switch(M.gender)
- if(MALE,FEMALE) gender_description = "[M.gender]"
- else gender_description = "[M.gender]"
-
- to_chat(src.owner, "Info about [M.name]: ")
- to_chat(src.owner, "Mob type = [M.type]; Gender = [gender_description] Damage = [health_description]")
- to_chat(src.owner, "Name = [M.name]; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = [M.key];")
- to_chat(src.owner, "Location = [location_description];")
- to_chat(src.owner, "[special_role_description]")
- to_chat(src.owner, "(PM) (PP) (VV) (SM) (FLW) (CA)")
+ admin_mob_info(M)
else if(href_list["adminspawncookie"])
if(!check_rights(R_ADMIN|R_EVENT)) return
diff --git a/html/changelogs/AutoChangeLog-pr-5203.yml b/html/changelogs/AutoChangeLog-pr-5203.yml
new file mode 100644
index 00000000000..6ddee474950
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-5203.yml
@@ -0,0 +1,4 @@
+author: Krausus
+delete-after: True
+changes:
+ - rscadd: "Admins now have click shortcuts for opening a player panel, showing mob info, and viewing variables. Specifics are in Hotkey Help."
diff --git a/interface/interface.dm b/interface/interface.dm
index f144b225160..cf3419cc1a7 100644
--- a/interface/interface.dm
+++ b/interface/interface.dm
@@ -89,6 +89,11 @@ Admin:
\tF7 = Player Panel
\tF8 = Admin PM
\tF9 = Invisimin
+
+Admin ghost:
+\tCtrl+Click = Player Panel
+\tCtrl+Shift+Click = View Variables
+\tShift+Middle Click = Mob Info
"}
mob.hotkey_help()