diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 4b2d36ab229..efdae5c8da7 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -513,6 +513,7 @@ text = replacetext(text, "\[cell\]", "
")
text = replacetext(text, "\[time\]", "[station_time_timestamp()]") // TO DO
+ text = replacetext(text, "\[date\]", "[GLOB.current_date_string]")
if(!no_font)
if(P)
text = "[text]"
@@ -651,3 +652,22 @@
// return the split html object to the caller
return s
+
+/**
+ * Proc to generate a "rank colour" from a client
+ *
+ * This takes the client and looks at various factors in order, such as patreon status, staff rank, and more
+ * Arguments:
+ * * C - The client were looking up
+ */
+/proc/client2rankcolour(client/C)
+ // First check if end user is an admin
+ if(C.holder)
+ if(C.holder.rank in GLOB.rank_colour_map)
+ // Return their rank colour if they are in here
+ return GLOB.rank_colour_map[C.holder.rank]
+
+ // If they arent an admin, see if they are a patreon. Just accept any level
+ if(C.donator_level)
+ return "#e67e22" // Patreon orange
+ return null
diff --git a/code/_globalvars/lists/misc.dm b/code/_globalvars/lists/misc.dm
index ebdf2017900..b5fd84ef6d6 100644
--- a/code/_globalvars/lists/misc.dm
+++ b/code/_globalvars/lists/misc.dm
@@ -54,4 +54,7 @@ GLOBAL_LIST_INIT(cooking_recipes, list(RECIPE_MICROWAVE = list(), RECIPE_OVEN =
GLOBAL_LIST_INIT(cooking_ingredients, list(RECIPE_MICROWAVE = list(), RECIPE_OVEN = list(), RECIPE_GRILL = list(), RECIPE_CANDY = list()))
GLOBAL_LIST_INIT(cooking_reagents, list(RECIPE_MICROWAVE = list(), RECIPE_OVEN = list(), RECIPE_GRILL = list(), RECIPE_CANDY = list()))
+/// Associative list of admin rank to colour. Set in config/rank_colours.txt
+GLOBAL_LIST_EMPTY(rank_colour_map)
+
#define EGG_LAYING_MESSAGES list("lays an egg.", "squats down and croons.", "begins making a huge racket.", "begins clucking raucously.")
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index 1636385006b..ec449fa86aa 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -622,3 +622,34 @@
runnable_modes[M] = probabilities[M.config_tag]
// to_chat(world, "DEBUG: runnable_mode\[[runnable_modes.len]\] = [M.config_tag]")
return runnable_modes
+
+/datum/configuration/proc/load_rank_colour_map()
+ var/list/lines = file2list("config/rank_colours.txt")
+
+ for(var/line in lines)
+ // Skip newlines
+ if(!length(line))
+ continue
+ // Skip comments
+ if(line[1] == "#")
+ continue
+
+ //Split the line at every " - "
+ var/list/split_holder = splittext(line, " - ")
+ if(!length(split_holder))
+ continue
+
+ // Rank is before the " - "
+ var/rank = split_holder[1]
+ if(!rank)
+ continue
+
+ // Color is after the " - "
+ var/colour = ""
+ if(length(split_holder) >= 2)
+ colour = split_holder[2]
+
+ if(rank && colour)
+ GLOB.rank_colour_map[rank] = colour
+ else
+ stack_trace("Invalid colour for rank '[rank]' in config/rank_colours.txt")
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/chaplain.dm b/code/game/objects/structures/crates_lockers/closets/secure/chaplain.dm
index ef80d50371a..55e44163a06 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/chaplain.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/chaplain.dm
@@ -22,6 +22,7 @@
new /obj/item/storage/backpack/cultpack(src)
new /obj/item/clothing/head/helmet/riot/knight/templar(src)
new /obj/item/clothing/suit/armor/riot/knight/templar(src)
+ new /obj/item/clothing/suit/storage/labcoat(src)
new /obj/item/soulstone/anybody/purified/chaplain(src)
new /obj/item/storage/fancy/candle_box/eternal(src)
new /obj/item/storage/fancy/candle_box/eternal(src)
diff --git a/code/game/verbs/who.dm b/code/game/verbs/who.dm
index 4a0429dd576..4c21afebdd2 100644
--- a/code/game/verbs/who.dm
+++ b/code/game/verbs/who.dm
@@ -1,63 +1,98 @@
-
/client/verb/who()
set name = "Who"
set category = "OOC"
- var/msg = "Current Players:\n"
+ var/list/lines = list()
+ var/list/temp = list()
+ for(var/client/C in GLOB.clients)
+ if(C.holder && C.holder.big_brother) // BB doesn't show up at all
+ continue
+
+ if(C.holder && C.holder.fakekey)
+ temp += C.holder.fakekey
+ else
+ temp += C.key
+
+ temp = sortList(temp) // Sort it. We dont do this above because fake keys would be out of order, which would be a giveaway
+
+ var/list/output_players = list()
+
+ // Now go over it again to apply colours.
+ for(var/p in temp)
+ var/client/C = GLOB.directory[ckey(p)]
+ if(!C)
+ // This should NEVER happen, but better to be safe
+ continue
+ // Get the colour
+ var/colour = client2rankcolour(C)
+ var/out = "[p]"
+ if(C.holder)
+ out = "[out]"
+ if(colour)
+ out = "[out]"
+
+ output_players += out
+
+ lines += "Current Players ([length(output_players)]): "
+ lines += output_players.Join(", ") // Turn players into a comma separated list
+
+ if(check_rights(R_ADMIN, FALSE))
+ lines += "Click here for detailed (old) who."
+
+ var/msg = lines.Join("\n")
+
+ to_chat(src, msg)
+
+// Advanced version of `who` to show player age, antag status and more. Lags the chat when loading, so its in its own proc
+/client/proc/who_advanced()
+ if(!check_rights(R_ADMIN))
+ return
var/list/Lines = list()
- if(check_rights(R_ADMIN,0))
- for(var/client/C in GLOB.clients)
- if(C.holder && C.holder.big_brother && !check_rights(R_PERMISSIONS, 0)) // need PERMISSIONS to see BB
- continue
+ for(var/client/C in GLOB.clients)
+ if(C.holder && C.holder.big_brother && !check_rights(R_PERMISSIONS, FALSE)) // need PERMISSIONS to see BB
+ continue
- var/entry = "\t[C.key]"
- if(C.holder && C.holder.fakekey)
- entry += " (as [C.holder.fakekey])"
- entry += " - Playing as [C.mob.real_name]"
- switch(C.mob.stat)
- if(UNCONSCIOUS)
- entry += " - Unconscious"
- if(DEAD)
- if(isobserver(C.mob))
- var/mob/dead/observer/O = C.mob
- if(O.started_as_observer)
- entry += " - Observing"
- else
- entry += " - DEAD"
- else if(isnewplayer(C.mob))
- entry += " - New Player"
+ var/entry = "\t[C.key]"
+ if(C.holder && C.holder.fakekey)
+ entry += " (as [C.holder.fakekey])"
+ entry += " - Playing as [C.mob.real_name]"
+ switch(C.mob.stat)
+ if(UNCONSCIOUS)
+ entry += " - Unconscious"
+ if(DEAD)
+ if(isobserver(C.mob))
+ var/mob/dead/observer/O = C.mob
+ if(O.started_as_observer)
+ entry += " - Observing"
else
entry += " - DEAD"
+ else if(isnewplayer(C.mob))
+ entry += " - New Player"
+ else
+ entry += " - DEAD"
- var/age
- if(isnum(C.player_age))
- age = C.player_age
- else
- age = 0
+ var/age
+ if(isnum(C.player_age))
+ age = C.player_age
+ else
+ age = 0
- if(age <= 1)
- age = "[age]"
- else if(age < 10)
- age = "[age]"
+ if(age <= 1)
+ age = "[age]"
+ else if(age < 10)
+ age = "[age]"
- entry += " - [age]"
+ entry += " - [age]"
- if(is_special_character(C.mob))
- entry += " - Antagonist"
- entry += " ([ADMIN_QUE(C.mob,"?")])"
- Lines += entry
- else
- for(var/client/C in GLOB.clients)
- if(C.holder && C.holder.big_brother) // BB doesn't show up at all
- continue
+ if(is_special_character(C.mob))
+ entry += " - Antagonist"
+ entry += " ([ADMIN_QUE(C.mob, "?")])"
+ Lines += entry
- if(C.holder && C.holder.fakekey)
- Lines += C.holder.fakekey
- else
- Lines += C.key
+ var/msg = ""
for(var/line in sortList(Lines))
msg += "[line]\n"
@@ -83,7 +118,12 @@
if(C.holder.big_brother && !check_rights(R_PERMISSIONS, 0)) // normal admins can't see BB
continue
- msg += "\t[C] is a [C.holder.rank]"
+ // Their rank may not have a defined colour, only set colour if so
+ var/rank_colour = client2rankcolour(C)
+ if(rank_colour)
+ msg += "[C] is a [C.holder.rank]"
+ else
+ msg += "[C] is a [C.holder.rank]"
if(C.holder.fakekey)
msg += " (as [C.holder.fakekey])"
@@ -102,7 +142,12 @@
num_admins_online++
else if(check_rights(R_MENTOR|R_MOD, 0, C.mob))
- modmsg += "\t[C] is a [C.holder.rank]"
+ // Their rank may not have a defined colour, only set colour if so
+ var/rank_colour = client2rankcolour(C)
+ if(rank_colour)
+ modmsg += "[C] is a [C.holder.rank]"
+ else
+ modmsg += "[C] is a [C.holder.rank]"
if(isobserver(C.mob))
modmsg += " - Observing"
@@ -120,10 +165,20 @@
if(check_rights(R_ADMIN, 0, C.mob))
if(!C.holder.fakekey)
- msg += "\t[C] is a [C.holder.rank]\n"
+ var/rank_colour = client2rankcolour(C)
+ if(rank_colour)
+ msg += "[C] is a [C.holder.rank]"
+ else
+ msg += "[C] is a [C.holder.rank]"
+ msg += "\n"
num_admins_online++
else if(check_rights(R_MOD|R_MENTOR, 0, C.mob) && !check_rights(R_ADMIN, 0, C.mob))
- modmsg += "\t[C] is a [C.holder.rank]\n"
+ var/rank_colour = client2rankcolour(C)
+ if(rank_colour)
+ modmsg += "[C] is a [C.holder.rank]"
+ else
+ modmsg += "[C] is a [C.holder.rank]"
+ modmsg += "\n"
num_mods_online++
var/noadmins_info = "\nIf no admins or mentors are online, make a ticket anyways. Adminhelps and mentorhelps will be relayed to discord, and staff will still be informed."
diff --git a/code/game/world.dm b/code/game/world.dm
index d327057082b..9386f856dac 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -196,6 +196,7 @@ GLOBAL_LIST_EMPTY(world_topic_handlers)
config = new /datum/configuration()
config.load("config/config.txt")
config.load("config/game_options.txt","game_options")
+ config.load_rank_colour_map()
// apply some settings from config..
/world/proc/update_status()
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 754b4c45924..e1c3b3c4e97 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -3556,6 +3556,8 @@
var/datum/browser/popup = new(usr, "view_karma", "Karma stats for [target_ckey]", 600, 300)
popup.set_content(dat)
popup.open(FALSE)
+ else if(href_list["who_advanced"])
+ usr.client.who_advanced()
/client/proc/create_eventmob_for(mob/living/carbon/human/H, killthem = 0)
if(!check_rights(R_EVENT))
diff --git a/code/modules/food_and_drinks/food/condiment.dm b/code/modules/food_and_drinks/food/condiment.dm
index 898d3f3cc71..897e11443ff 100644
--- a/code/modules/food_and_drinks/food/condiment.dm
+++ b/code/modules/food_and_drinks/food/condiment.dm
@@ -23,6 +23,7 @@
"sodiumchloride" = list("saltshakersmall", "salt shaker", "Salt. From space oceans, presumably"),
"blackpepper" = list("peppermillsmall", "pepper mill", "Often used to flavor food or make people sneeze"),
"cornoil" = list("oliveoil", "corn oil bottle", "A delicious oil used in cooking. Made from corn"),
+ "wasabi" = list("wasabitube", "wasabi bottle", "A pungent paste commonly served in tiny amounts with sushi. Spicy!"),
"sugar" = list("emptycondiment", "sugar bottle", "Tasty spacey sugar!"))
var/originalname = "condiment" //Can't use initial(name) for this. This stores the name set by condimasters.
diff --git a/code/modules/hydroponics/grown/nettle.dm b/code/modules/hydroponics/grown/nettle.dm
index 97d35aa4eed..da7c63393b2 100644
--- a/code/modules/hydroponics/grown/nettle.dm
+++ b/code/modules/hydroponics/grown/nettle.dm
@@ -11,7 +11,7 @@
growthstages = 5
genes = list(/datum/plant_gene/trait/repeated_harvest, /datum/plant_gene/trait/plant_type/weed_hardy)
mutatelist = list(/obj/item/seeds/nettle/death)
- reagents_add = list("sacid" = 0.5)
+ reagents_add = list("wasabi" = 0.15)
/obj/item/seeds/nettle/death
name = "pack of death-nettle seeds"
diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm
index 85efba9e5dd..543989bb146 100644
--- a/code/modules/projectiles/guns/energy/laser.dm
+++ b/code/modules/projectiles/guns/energy/laser.dm
@@ -1,6 +1,6 @@
/obj/item/gun/energy/laser
name = "laser gun"
- desc = "A basic energy-based laser gun that fires concentrated beams of light which pass through glass and thin metal."
+ desc = "A WT-650 'Sentinel' laser carbine manufactured by Warp-Tac Inc. The golden shield of Nanotrasen Security is visible."
icon_state = "laser"
item_state = "laser"
w_class = WEIGHT_CLASS_NORMAL
@@ -8,7 +8,7 @@
origin_tech = "combat=4;magnets=2"
ammo_type = list(/obj/item/ammo_casing/energy/lasergun)
ammo_x_offset = 1
- shaded_charge = 1
+ shaded_charge = TRUE
/obj/item/gun/energy/laser/practice
name = "practice laser gun"
diff --git a/code/modules/projectiles/guns/energy/nuclear.dm b/code/modules/projectiles/guns/energy/nuclear.dm
index 3d9925da974..a39e018909a 100644
--- a/code/modules/projectiles/guns/energy/nuclear.dm
+++ b/code/modules/projectiles/guns/energy/nuclear.dm
@@ -1,15 +1,15 @@
/obj/item/gun/energy/gun
name = "energy gun"
- desc = "A basic energy-based gun with two settings: kill and disable."
+ desc = "An E-07 energy gun manufactured by Shellguard Munitions. The fire selector has 'kill' and 'disable' settings."
icon_state = "energy"
item_state = null //so the human update icon uses the icon_state instead.
ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser)
origin_tech = "combat=4;magnets=3"
modifystate = 2
can_flashlight = 1
- ammo_x_offset = 3
- flight_x_offset = 15
+ flight_x_offset = 20
flight_y_offset = 10
+ shaded_charge = TRUE
/obj/item/gun/energy/gun/cyborg
desc = "An energy-based laser gun that draws power from the cyborg's internal energy cell directly. So this is what freedom looks like?"
@@ -30,6 +30,7 @@
charge_sections = 3
can_flashlight = 0 // Can't attach or detach the flashlight, and override it's icon update
actions_types = list(/datum/action/item_action/toggle_gunlight)
+ shaded_charge = FALSE
/obj/item/gun/energy/gun/mini/Initialize(mapload, ...)
gun_light = new /obj/item/flashlight/seclite(src)
@@ -51,6 +52,7 @@
ammo_type = list(/obj/item/ammo_casing/energy/electrode/hos, /obj/item/ammo_casing/energy/laser/hos, /obj/item/ammo_casing/energy/disabler)
ammo_x_offset = 4
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+ shaded_charge = FALSE
/obj/item/gun/energy/gun/blueshield
name = "advanced stun revolver"
@@ -60,7 +62,7 @@
force = 7
ammo_type = list(/obj/item/ammo_casing/energy/electrode/hos, /obj/item/ammo_casing/energy/laser/hos)
ammo_x_offset = 1
- shaded_charge = 1
+ shaded_charge = TRUE
/obj/item/gun/energy/gun/blueshield/pdw9
name = "PDW-9 taser pistol"
@@ -79,6 +81,7 @@
can_flashlight = 0
trigger_guard = TRIGGER_GUARD_NONE
ammo_x_offset = 2
+ shaded_charge = FALSE
/obj/item/gun/energy/gun/nuclear
name = "advanced energy gun"
@@ -92,3 +95,4 @@
ammo_x_offset = 1
ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser, /obj/item/ammo_casing/energy/disabler)
selfcharge = 1
+ shaded_charge = FALSE
diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm
index 141366dd5d9..2247a1a6c9c 100644
--- a/code/modules/projectiles/guns/energy/stun.dm
+++ b/code/modules/projectiles/guns/energy/stun.dm
@@ -15,7 +15,7 @@
origin_tech = "combat=4;materials=4;powerstorage=4"
ammo_type = list(/obj/item/ammo_casing/energy/shock_revolver)
can_flashlight = 0
- shaded_charge = 1
+ shaded_charge = FALSE
/obj/item/gun/energy/gun/advtaser
name = "hybrid taser"
@@ -24,6 +24,8 @@
ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/disabler)
origin_tech = "combat=4"
ammo_x_offset = 2
+ flight_x_offset = 15
+ shaded_charge = FALSE
/obj/item/gun/energy/gun/advtaser/cyborg
name = "cyborg taser"
diff --git a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
index 2422c7a143d..1c6981ff0b5 100644
--- a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
+++ b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
@@ -30,7 +30,7 @@
/obj/item/stack/sheet/mineral/tranquillite = list("nothing" = 20),
/obj/item/stack/sheet/mineral/silver = list("silver" = 20),
/obj/item/stack/sheet/mineral/gold = list("gold" = 20),
- /obj/item/grown/nettle/basic = list("sacid" = 0),
+ /obj/item/grown/nettle/basic = list("wasabi" = 0),
/obj/item/grown/nettle/death = list("facid" = 0, "sacid" = 0),
/obj/item/grown/novaflower = list("capsaicin" = 0, "condensedcapsaicin" = 0),
diff --git a/code/modules/reagents/chemistry/reagents/food.dm b/code/modules/reagents/chemistry/reagents/food.dm
index d22adff060d..38430bc3dce 100644
--- a/code/modules/reagents/chemistry/reagents/food.dm
+++ b/code/modules/reagents/chemistry/reagents/food.dm
@@ -794,6 +794,21 @@
color = "#B4641B"
taste_description = "gravy"
+/datum/reagent/consumable/wasabi
+ name = "Wasabi"
+ id = "wasabi"
+ description = "A pungent green paste often served with sushi. Consuming too much causes an uncomfortable burning sensation in the nostrils."
+ reagent_state = LIQUID
+ color = "#80942F"
+ taste_description = "pungency"
+
+/datum/reagent/consumable/wasabi/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume)
+ if(method == REAGENT_INGEST)
+ if(volume <= 1)
+ to_chat(M, "Your nostrils tingle briefly.")
+ else
+ to_chat(M, "Your nostrils burn uncomfortably!")
+ M.adjustFireLoss(1)
///Food Related, but non-nutritious
diff --git a/config/example/rank_colours.txt b/config/example/rank_colours.txt
new file mode 100644
index 00000000000..b35641c39f4
--- /dev/null
+++ b/config/example/rank_colours.txt
@@ -0,0 +1,11 @@
+# Use this file to denote colours for ingame admin ranks, using the following format
+# Rankname - #12A5F4
+# Colours dont have to be in hex, since this colour string is thrown into the style -aa
+Head of Staff - #e74c3c
+Maintainer - #992d22
+Server Dev - #1abc9c
+Community Manager - #e91e63
+Game Admin - #238afa
+Trial Admin - #7fb6fc
+PR Reviewer - #c27c0e
+Mentor - #f1c40f
diff --git a/icons/mob/inhands/guns_lefthand.dmi b/icons/mob/inhands/guns_lefthand.dmi
index 52a1adef4b2..d69dd8a988b 100644
Binary files a/icons/mob/inhands/guns_lefthand.dmi and b/icons/mob/inhands/guns_lefthand.dmi differ
diff --git a/icons/mob/inhands/guns_righthand.dmi b/icons/mob/inhands/guns_righthand.dmi
index 9a1ee42f32b..44968461e65 100644
Binary files a/icons/mob/inhands/guns_righthand.dmi and b/icons/mob/inhands/guns_righthand.dmi differ
diff --git a/icons/obj/food/containers.dmi b/icons/obj/food/containers.dmi
index ed71b4d3c1d..8a317b9edb0 100644
Binary files a/icons/obj/food/containers.dmi and b/icons/obj/food/containers.dmi differ
diff --git a/icons/obj/guns/energy.dmi b/icons/obj/guns/energy.dmi
index a0c6981f7c8..812b0bfba23 100644
Binary files a/icons/obj/guns/energy.dmi and b/icons/obj/guns/energy.dmi differ