diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql
index 1069dd6b2d6..9bfc6fa90b0 100644
--- a/SQL/paradise_schema.sql
+++ b/SQL/paradise_schema.sql
@@ -140,6 +140,25 @@ CREATE TABLE `death` (
) ENGINE=MyISAM AUTO_INCREMENT=166546 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
+--
+-- Table structure for table `donators`
+--
+
+DROP TABLE IF EXISTS `donators`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `donators` (
+ `patreon_name` varchar(32) NOT NULL,
+ `tier` int(2),
+ `ckey` varchar(32) COMMENT 'Manual Field',
+ `start_date` datetime,
+ `end_date` datetime,
+ `active` boolean,
+ PRIMARY KEY (`patreon_name`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+
--
-- Table structure for table `admin`
--
diff --git a/SQL/paradise_schema_prefixed.sql b/SQL/paradise_schema_prefixed.sql
index 55e71aa2aab..c0cc8c86d6b 100644
--- a/SQL/paradise_schema_prefixed.sql
+++ b/SQL/paradise_schema_prefixed.sql
@@ -140,6 +140,24 @@ CREATE TABLE `SS13_death` (
) ENGINE=MyISAM AUTO_INCREMENT=166546 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
+--
+-- Table structure for table `donators`
+--
+
+DROP TABLE IF EXISTS `SS13_donators`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `donators` (
+ `patreon_name` varchar(32) NOT NULL,
+ `tier` int(2),
+ `ckey` varchar(32) COMMENT 'Manual Field',
+ `start_date` datetime,
+ `end_date` datetime,
+ `active` boolean,
+ PRIMARY KEY (`patreon_name`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
--
-- Table structure for table `SS13_admin`
--
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index dcb4501ce6e..049019a32d2 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -288,5 +288,10 @@
#define SHELTER_DEPLOY_BAD_AREA "bad area"
#define SHELTER_DEPLOY_ANCHORED_OBJECTS "anchored objects"
+// Client donator levels
+#define DONATOR_LEVEL_NONE 0
+#define DONATOR_LEVEL_ONE 1
+#define DONATOR_LEVEL_TWO 2
+
// The cooldown on OOC messages such as OOC, LOOC, praying and adminhelps
#define OOC_COOLDOWN 5
diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm
index 703b3e6b205..d39cc600221 100644
--- a/code/__DEFINES/preferences.dm
+++ b/code/__DEFINES/preferences.dm
@@ -24,5 +24,6 @@
#define DISABLE_KARMA_REMINDER 4096
#define MEMBER_PUBLIC 8192
#define CHAT_NO_ADMINLOGS 16384
+#define DONATOR_PUBLIC 32768
-#define TOGGLES_DEFAULT (CHAT_OOC|CHAT_DEAD|CHAT_GHOSTEARS|CHAT_GHOSTSIGHT|CHAT_PRAYER|CHAT_RADIO|CHAT_ATTACKLOGS|CHAT_LOOC|MEMBER_PUBLIC)
+#define TOGGLES_DEFAULT (CHAT_OOC|CHAT_DEAD|CHAT_GHOSTEARS|CHAT_GHOSTSIGHT|CHAT_PRAYER|CHAT_RADIO|CHAT_ATTACKLOGS|CHAT_LOOC|MEMBER_PUBLIC|DONATOR_PUBLIC)
diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm
index f209e652b38..38abe4b5fd0 100644
--- a/code/game/verbs/ooc.dm
+++ b/code/game/verbs/ooc.dm
@@ -61,16 +61,24 @@ var/global/admin_ooc_colour = "#b82e00"
for(var/client/C in clients)
if(C.prefs.toggles & CHAT_OOC)
var/display_name = src.key
+
if(prefs.unlock_content)
if(prefs.toggles & MEMBER_PUBLIC)
var/icon/byond = icon('icons/member_content.dmi', "blag")
display_name = "[bicon(byond)][display_name]"
+
+ if(donator_level >= DONATOR_LEVEL_ONE)
+ if((prefs.toggles & DONATOR_PUBLIC))
+ var/icon/donator = icon('icons/ooc_tag_16x.dmi', "donator")
+ display_name = "[bicon(donator)][display_name]"
+
if(holder)
if(holder.fakekey)
if(C.holder)
display_name = "[holder.fakekey]/([src.key])"
else
display_name = holder.fakekey
+
to_chat(C, "OOC: [display_name]: ")
/proc/toggle_ooc()
diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm
index 8f2adef5a1b..bb243c3e6aa 100644
--- a/code/modules/client/client defines.dm
+++ b/code/modules/client/client defines.dm
@@ -89,4 +89,7 @@
// Their chat window, sort of important.
// See /goon/code/datums/browserOutput.dm
- var/datum/chatOutput/chatOutput
\ No newline at end of file
+ var/datum/chatOutput/chatOutput
+
+ // Donator stuff.
+ var/donator_level = DONATOR_LEVEL_NONE
\ No newline at end of file
diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index 17c454bb7e2..0cf9b0a0915 100644
--- a/code/modules/client/client procs.dm
+++ b/code/modules/client/client procs.dm
@@ -305,6 +305,8 @@
admins += src
holder.owner = src
+ donator_check()
+
//preferences datum - also holds some persistant data for the client (because we may as well keep these datums to a minimum)
prefs = preferences_datums[ckey]
if(!prefs)
@@ -378,10 +380,27 @@
return ..()
+/client/proc/donator_check()
+ if(IsGuestKey(key))
+ return
+
+ establish_db_connection()
+ if(!dbcon.IsConnected())
+ return
+
+ //Donator stuff.
+ var/DBQuery/query_donor_select = dbcon.NewQuery("SELECT ckey, tier, active FROM `[format_table_name("donators")]` WHERE ckey = '[ckey]'")
+ query_donor_select.Execute()
+ while(query_donor_select.NextRow())
+ if(!text2num(query_donor_select.item[3]))
+ // Inactive donator.
+ donator_level = DONATOR_LEVEL_NONE
+ return
+ donator_level = text2num(query_donor_select.item[2])
+ break
/client/proc/log_client_to_db()
-
- if( IsGuestKey(src.key) )
+ if(IsGuestKey(key))
return
establish_db_connection()
diff --git a/code/modules/client/preference/loadout/loadout.dm b/code/modules/client/preference/loadout/loadout.dm
index 010fc54564c..b42c5d5abf1 100644
--- a/code/modules/client/preference/loadout/loadout.dm
+++ b/code/modules/client/preference/loadout/loadout.dm
@@ -4,6 +4,7 @@ var/list/gear_datums = list()
/datum/loadout_category
var/category = ""
var/list/gear = list()
+ var/donor_only = FALSE
/datum/loadout_category/New(cat)
category = cat
@@ -33,6 +34,8 @@ var/list/gear_datums = list()
if(!loadout_categories[use_category])
loadout_categories[use_category] = new /datum/loadout_category(use_category)
var/datum/loadout_category/LC = loadout_categories[use_category]
+ if(initial(G.donor_only))
+ LC.donor_only = TRUE
gear_datums[use_name] = new geartype
LC.gear[use_name] = gear_datums[use_name]
@@ -54,6 +57,7 @@ var/list/gear_datums = list()
var/list/gear_tweaks = list() //List of datums which will alter the item after it has been spawned.
var/subtype_path = /datum/gear //for skipping organizational subtypes (optional)
var/subtype_cost_overlap = TRUE //if subtypes can take points at the same time
+ var/donor_only = FALSE // if it's only available to donors
/datum/gear/New()
..()
@@ -76,4 +80,4 @@ var/list/gear_datums = list()
var/item = new gd.path(gd.location)
for(var/datum/gear_tweak/gt in gear_tweaks)
gt.tweak_item(item, metadata["[gt]"])
- return item
\ No newline at end of file
+ return item
diff --git a/code/modules/client/preference/loadout/loadout_donor.dm b/code/modules/client/preference/loadout/loadout_donor.dm
new file mode 100644
index 00000000000..edf7e173730
--- /dev/null
+++ b/code/modules/client/preference/loadout/loadout_donor.dm
@@ -0,0 +1,90 @@
+/datum/gear/donor
+ donor_only = TRUE
+ sort_category = "Donor"
+ subtype_path = /datum/gear/donor
+
+/datum/gear/donor/furgloves
+ display_name = "Fur Gloves"
+ path = /obj/item/clothing/gloves/furgloves
+
+/datum/gear/donor/furboots
+ display_name = "Fur Boots"
+ path = /obj/item/clothing/shoes/furboots
+
+/datum/gear/donor/noble_boot
+ display_name = "Noble Boots"
+ path = /obj/item/clothing/shoes/fluff/noble_boot
+
+/datum/gear/donor/furcape
+ display_name = "Fur Cape"
+ path = /obj/item/clothing/suit/furcape
+ cost = 2
+
+/datum/gear/donor/furcoat
+ display_name = "Fur Coat"
+ path = /obj/item/clothing/suit/furcoat
+ cost = 2
+
+/datum/gear/donor/lord_admiral
+ display_name = "Lord Admiral Coat"
+ path = /obj/item/clothing/suit/lordadmiral
+
+/datum/gear/donor/lord_admiral_hat
+ display_name = "Lord Admiral Hat"
+ path = /obj/item/clothing/head/lordadmiralhat
+
+/datum/gear/donor/kamina
+ display_name = "Spiky Orange-tinted Shades"
+ path = /obj/item/clothing/glasses/fluff/kamina
+
+/datum/gear/donor/green
+ display_name = "Spiky Green-tinted Shades"
+ path = /obj/item/clothing/glasses/fluff/kamina/green
+
+/datum/gear/donor/hipster
+ display_name = "Hipster Glasses"
+ path = /obj/item/clothing/glasses/regular/hipster
+
+/datum/gear/donor/threedglasses
+ display_name = "Threed Glasses"
+ path = /obj/item/clothing/glasses/threedglasses
+
+/datum/gear/donor/blacksombrero
+ display_name = "Black Sombrero"
+ path = /obj/item/clothing/head/fluff/blacksombrero
+
+/datum/gear/donor/guardhelm
+ display_name = "Plastic Guard helm"
+ path = /obj/item/clothing/head/fluff/guardhelm
+
+/datum/gear/donor/goldtophat
+ display_name = "Gold-trimmed Top Hat"
+ path = /obj/item/clothing/head/fluff/goldtophat
+
+/datum/gear/donor/goldtophat/red
+ display_name = "Red Gold-trimmed Top Hat"
+ path = /obj/item/clothing/head/fluff/goldtophat/red
+
+/datum/gear/donor/goldtophat/blue
+ display_name = "Blue Gold-trimmed Top Hat"
+ path = /obj/item/clothing/head/fluff/goldtophat/blue
+
+/datum/gear/donor/mushhat
+ display_name = "Mushroom Hat"
+ path = /obj/item/clothing/head/fluff/mushhat
+
+/datum/gear/donor/furcap
+ display_name = "Fur Cap"
+ path = /obj/item/clothing/head/furcap
+
+/datum/gear/donor/mouse
+ display_name = "Mouse Headband"
+ path = /obj/item/clothing/head/kitty/mouse
+
+/datum/gear/donor/fawkes
+ display_name = "Guy Fawkes mask"
+ path = /obj/item/clothing/mask/fawkes
+
+/datum/gear/donor/noble_clothes
+ display_name = "Noble Clothes"
+ path = /obj/item/clothing/under/noble_clothes
diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm
index 72ec8f101d2..415a6f05d3f 100644
--- a/code/modules/client/preference/preferences.dm
+++ b/code/modules/client/preference/preferences.dm
@@ -61,7 +61,6 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
#define MAX_SAVE_SLOTS 20 // Save slots for regular players
#define MAX_SAVE_SLOTS_MEMBER 20 // Save slots for BYOND members
-#define MAX_GEAR_COST config.max_loadout_points
#define TAB_CHAR 0
#define TAB_GAME 1
@@ -73,6 +72,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used
// var/savefile_version = 0
var/max_save_slots = MAX_SAVE_SLOTS
+ var/max_gear_slots = 0
//non-preference stuff
var/warns = 0
@@ -212,11 +212,17 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
/datum/preferences/New(client/C)
b_type = pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+")
+
if(istype(C))
if(!IsGuestKey(C.key))
unlock_content = C.IsByondMember()
if(unlock_content)
max_save_slots = MAX_SAVE_SLOTS_MEMBER
+
+ max_gear_slots = config.max_loadout_points
+ if(C.donator_level >= DONATOR_LEVEL_ONE)
+ max_gear_slots += 5
+
var/loaded_preferences_successfully = load_preferences(C)
if(loaded_preferences_successfully)
if(load_character(C))
@@ -428,6 +434,8 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
dat += "OOC notes: Edit
"
if(unlock_content)
dat += "BYOND Membership Publicity: [(toggles & MEMBER_PUBLIC) ? "Public" : "Hidden"]
"
+ if(user.client.donator_level >= DONATOR_LEVEL_ONE)
+ dat += "Donator Publicity: [(toggles & DONATOR_PUBLIC) ? "Public" : "Hidden"]
"
dat += "Randomized character slot: [randomslot ? "Yes" : "No"]
"
dat += "Ghost ears: [(toggles & CHAT_GHOSTEARS) ? "Nearest Creatures" : "All Speech"]
"
@@ -464,14 +472,18 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
total_cost += G.cost
var/fcolor = "#3366CC"
- if(total_cost < MAX_GEAR_COST)
+ if(total_cost < max_gear_slots)
fcolor = "#E67300"
dat += "