diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 3c5dca2e33a..7dbe9ef83a2 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -47,6 +47,7 @@ var/global/floorIsLava = 0
body += "Mob type = [M.type]
"
body += "Kick | "
+ body += "Warn | "
body += "Ban | "
body += "Jobban | "
body += "Notes "
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index a6721af627f..754378ea112 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -418,30 +418,47 @@ var/list/admin_verbs_hideable = list(
message_admins("[key_name_admin(usr)] has turned stealth mode [holder.fakekey ? "ON" : "OFF"]", 1)
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
+#define AUTOBANTIME 10
-#define AUTOBATIME 10
-/client/proc/warn(var/mob/M in player_list)
- /*set category = "Special Verbs"
- set name = "Warn"
- set desc = "Warn a player"*/ //Based on the information I gathered via stat logging this verb was not used. Use the show player panel alternative. --erro
-
+/client/proc/warn(warned_ckey)
if(!check_rights(R_ADMIN)) return
- if(!M.client.warned)
- M << "\red You have been warned by an administrator. This is the only warning you will recieve."
- M.client.warned = 1
- message_admins("\blue [ckey] warned [M.ckey].")
- else
- AddBan(M.ckey, M.computer_id, "Autobanning due to previous warn", ckey, 1, AUTOBATIME)
- M << "\redYou have been autobanned by [ckey]. This is what we in the biz like to call a \"second warning\"."
- M << "\red This is a temporary ban; it will automatically be removed in [AUTOBATIME] minutes."
- log_admin("[ckey] warned [M.ckey], resulting in a [AUTOBATIME] minute autoban.")
- ban_unban_log_save("[ckey] warned [M.ckey], resulting in a [AUTOBATIME] minute autoban.")
- message_admins("\blue [ckey] warned [M.ckey], resulting in a [AUTOBATIME] minute autoban.")
- feedback_inc("ban_warn",1)
- del(M.client)
+ if(!warned_ckey || !istext(warned_ckey)) return
+ if(ckey in admin_datums)
+ usr << "Error: warn(): You can't warn admins."
+ return
+
+ var/datum/preferences/D
+ var/client/C = directory[warned_ckey]
+ if(C) D = C.prefs
+ else D = preferences_datums[warned_ckey]
+
+ if(!D)
+ src << "Error: warn(): No such ckey found."
+ return
+
+ if(++D.warns >= MAX_WARNS) //uh ohhhh...you'reee iiiiin trouuuubble O:)
+ ban_unban_log_save("[ckey] warned [warned_ckey], resulting in a [AUTOBANTIME] minute autoban.")
+ if(C)
+ message_admins("[key_name_admin(src)] has warned [key_name_admin(C)] resulting in a [AUTOBANTIME] minute ban.")
+ C << "You have been autobanned due to a warning by [ckey].
This is a temporary ban, it will be removed in [AUTOBANTIME] minutes."
+ del(C)
+ else
+ message_admins("[key_name_admin(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban.")
+ AddBan(warned_ckey, D.last_id, "Autobanning due to too many formal warnings", ckey, 1, AUTOBANTIME)
+ feedback_inc("ban_warn",1)
+ else
+ if(C)
+ C << "You have been formally warned by an administrator.
Further warnings will result in an autoban."
+ message_admins("[key_name_admin(src)] has warned [key_name_admin(C)]. They have [MAX_WARNS-D.warns] strikes remaining.")
+ else
+ message_admins("[key_name_admin(src)] has warned [warned_ckey] (DC). They have [MAX_WARNS-D.warns] strikes remaining.")
+
feedback_add_details("admin_verb","WARN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+#undef MAX_WARNS
+#undef AUTOBANTIME
/client/proc/drop_bomb() // Some admin dickery that can probably be done better -- TLE
set category = "Special Verbs"
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index ded6c1ac0c7..cd32c78eb00 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -294,6 +294,9 @@
alert(usr, "This ban has already been lifted / does not exist.", "Error", "Ok")
unbanpanel()
+ else if(href_list["warn"])
+ usr.client.warn(href_list["warn"])
+
else if(href_list["unbane"])
if(!check_rights(R_BAN)) return
diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm
index 69ef1d63aef..4afa1cf326a 100644
--- a/code/modules/client/client defines.dm
+++ b/code/modules/client/client defines.dm
@@ -10,8 +10,6 @@
var/last_message = "" //Contains the last message sent by this client - used to protect against copy-paste spamming.
var/last_message_count = 0 //contins a number of how many times a message identical to last_message was sent.
- var/warned = 0
-
/////////
//OTHER//
/////////
diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index f3bef9d8713..967220298e4 100644
--- a/code/modules/client/client procs.dm
+++ b/code/modules/client/client procs.dm
@@ -112,6 +112,8 @@
if(!prefs)
prefs = new /datum/preferences(src)
preferences_datums[ckey] = prefs
+ prefs.last_ip = address //these are gonna be used for banning
+ prefs.last_id = computer_id //these are gonna be used for banning
. = ..() //calls mob.Login()
diff --git a/code/modules/mob/new_player/preferences.dm b/code/modules/mob/new_player/preferences.dm
index 1db126daa6c..b4d3415edef 100644
--- a/code/modules/mob/new_player/preferences.dm
+++ b/code/modules/mob/new_player/preferences.dm
@@ -36,10 +36,17 @@ var/const/MAX_SAVE_SLOTS = 3
datum/preferences
+ //doohickeys for savefiles
var/path
var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used
var/savefile_version = 0
+ //non-preference stuff
+ var/warns = 0
+ var/last_ip
+ var/last_id
+
+ //game-preferences
var/lastchangelog = "" //Saved changlog filesize to detect if there was a change
var/ooccolor = "#b82e00"
var/midis = 1 //Play admin midis
@@ -50,37 +57,26 @@ datum/preferences
var/sound_adminhelp = 0
var/lobby_music = 1 //Whether or not to play the lobby music(Defaults yes)
-
- var/real_name
- var/be_random_name = 0
- var/gender = MALE
- var/age = 30.0
- var/b_type = "A+"
- //Just like it sounds
- var/underwear = 1
- var/backbag = 2
-
- //Hair type
- var/h_style = "Bald"
- //Hair color
- var/r_hair = 0
- var/g_hair = 0
- var/b_hair = 0
-
- //Face hair type
- var/f_style = "Shaved"
- //Face hair color
- var/r_facial = 0
- var/g_facial = 0
- var/b_facial = 0
-
- //Skin color
- var/s_tone = 0
-
- //Eye color
- var/r_eyes = 0
- var/g_eyes = 0
- var/b_eyes = 0
+ //character preferences
+ var/real_name //our character's name
+ var/be_random_name = 0 //whether we are a random name every round
+ var/gender = MALE //gender of character (well duh)
+ var/age = 30 //age of character
+ var/b_type = "A+" //blood type (not-chooseable)
+ var/underwear = 1 //underwear type
+ var/backbag = 2 //backpack type
+ var/h_style = "Bald" //Hair type
+ var/r_hair = 0 //Hair color
+ var/g_hair = 0 //Hair color
+ var/b_hair = 0 //Hair color
+ var/f_style = "Shaved" //Face hair type
+ var/r_facial = 0 //Face hair color
+ var/g_facial = 0 //Face hair color
+ var/b_facial = 0 //Face hair color
+ var/s_tone = 0 //Skin color
+ var/r_eyes = 0 //Eye color
+ var/g_eyes = 0 //Eye color
+ var/b_eyes = 0 //Eye color
//Mob preview
var/icon/preview_icon_front = null