Adds colourblind mode and a colour matrix editor (#17933)

* Colourblind mode + Matrix editor

* increase that

* Protanopia filter

* Tritanopia filter

* The SQL part

* Refactor

* you saw nothing
This commit is contained in:
AffectedArc07
2022-06-11 00:52:28 +01:00
committed by GitHub
parent 0d465cdd6f
commit 7d5a7ad85d
20 changed files with 266 additions and 61 deletions
+1
View File
@@ -284,6 +284,7 @@ CREATE TABLE `player` (
`screentip_mode` tinyint(1) DEFAULT '8',
`screentip_color` varchar(7) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '#ffd391',
`ghost_darkness_level` tinyint(1) UNSIGNED NOT NULL DEFAULT '255',
`colourblind_mode` VARCHAR(48) NOT NULL DEFAULT 'None' COLLATE 'utf8mb4_general_ci',
PRIMARY KEY (`id`),
UNIQUE KEY `ckey` (`ckey`),
KEY `lastseen` (`lastseen`),
+4
View File
@@ -0,0 +1,4 @@
# Updates DB from 36 to 37 -AffectedArc07
# New preferences column for colourblind mode
ALTER TABLE `player` ADD COLUMN `colourblind_mode` VARCHAR(48) NOT NULL DEFAULT 'None' AFTER `ghost_darkness_level`;
+1 -1
View File
@@ -371,7 +371,7 @@
#define INVESTIGATE_BOMB "bombs"
// The SQL version required by this version of the code
#define SQL_VERSION 36
#define SQL_VERSION 37
// Vending machine stuff
#define CAT_NORMAL 1
+6
View File
@@ -121,3 +121,9 @@
#define TAB_CHAR 0
#define TAB_GAME 1
#define TAB_GEAR 2
// Colourblind modes
#define COLOURBLIND_MODE_NONE "None"
#define COLOURBLIND_MODE_DEUTER "Red-green (green weak, deuteranopia)"
#define COLOURBLIND_MODE_PROT "Red-green (red weak, protanopia)"
#define COLOURBLIND_MODE_TRIT "Blue-yellow (tritanopia)"
+14
View File
@@ -69,6 +69,7 @@
/client/vv_get_dropdown()
. = list()
.["Manipulate Colour Matrix"] = "?_src_=vars;manipcolours=[UID()]"
. += "---"
.["Call Proc"] = "?_src_=vars;proc_call=[UID()]"
.["Mark Object"] = "?_src_=vars;mark_object=[UID()]"
@@ -1336,6 +1337,19 @@
if(istype(H))
H.copy_outfit()
if(href_list["manipcolours"])
if(!check_rights(R_DEBUG))
return
var/datum/target = locateUID(href_list["manipcolours"])
if(!(isatom(target) || isclient(target)))
to_chat(usr, "This can only be used on atoms and clients")
return
message_admins("[key_name_admin(usr)] is manipulating the colour matrix for [target]")
var/datum/ui_module/colour_matrix_tester/CMT = new(target=target)
CMT.ui_interact(usr)
/client/proc/view_var_Topic_list(href, href_list, hsrc)
if(href_list["VarsList"])
debug_variables(locate(href_list["VarsList"]))
+1
View File
@@ -1013,6 +1013,7 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
/atom/vv_get_dropdown()
. = ..()
.["Manipulate Colour Matrix"] = "?_src_=vars;manipcolours=[UID()]"
var/turf/curturf = get_turf(src)
if(curturf)
.["Jump to turf"] = "?_src_=holder;adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]"
+2 -6
View File
@@ -131,9 +131,7 @@
return
to_chat(victim, "<span class='reallybig redtext'>RIP AND TEAR</span>")
SEND_SOUND(victim, sound('sound/misc/e1m1.ogg'))
var/old_color = victim.client.color
var/red_splash = list(1,0,0,0.8,0.2,0, 0.8,0,0.2,0.1,0,0)
var/pure_red = list(0,0,0,0,0,0,0,0,0,1,0,0)
new /obj/effect/hallucination/delusion(get_turf(victim), victim, 'icons/mob/mob.dmi', "daemon")
@@ -146,10 +144,8 @@
chainsaw.wield(victim)
victim.reagents.add_reagent("adminordrazine", 25)
victim.client.color = pure_red
animate(victim.client,color = red_splash, time = 10, easing = SINE_EASING|EASE_OUT)
spawn(10)
animate(victim.client,color = old_color, time = duration)//, easing = SINE_EASING|EASE_OUT)
victim.flash_screen_color(red_splash, 10)
spawn(duration)
to_chat(victim, "<span class='notice'>Your bloodlust seeps back into the bog of your subconscious and you regain self control.</span>")
qdel(chainsaw)
+2
View File
@@ -332,6 +332,8 @@
. = ..() //calls mob.Login()
mob.update_client_colour(0) // Activate colourblind mode if they have one set
if(ckey in GLOB.clientmessages)
for(var/message in GLOB.clientmessages[ckey])
@@ -22,7 +22,8 @@
2fa_status,
screentip_mode,
screentip_color,
ghost_darkness_level
ghost_darkness_level,
colourblind_mode
FROM player
WHERE ckey=:ckey"}, list(
"ckey" = C.ckey
@@ -875,6 +875,12 @@
else
switch(href_list["preference"])
if("cbmode")
var/cb_mode = input(user, "Select a colourblind mode\nNote this will disable special screen effects such as the cursed heart warnings!") as null|anything in list(COLOURBLIND_MODE_NONE, COLOURBLIND_MODE_DEUTER, COLOURBLIND_MODE_PROT, COLOURBLIND_MODE_TRIT)
if(cb_mode)
colourblind_mode = cb_mode
user.update_client_colour(0)
if("publicity")
if(unlock_content)
toggles ^= PREFTOGGLE_MEMBER_PUBLIC
@@ -115,6 +115,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
var/datum/character_save/active_character
/// How dark things are if client is a ghost, 0-255
var/ghost_darkness_level = LIGHTING_PLANE_ALPHA_VISIBLE
/// Colourblind mode
var/colourblind_mode = COLOURBLIND_MODE_NONE
/datum/preferences/New(client/C, datum/db_query/Q) // Process our query
parent = C
@@ -363,6 +365,7 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
dat += "<b>Attack Animations:</b> <a href='?_src_=prefs;preference=ghost_att_anim'>[(toggles2 & PREFTOGGLE_2_ITEMATTACK) ? "Yes" : "No"]</a><br>"
if(unlock_content)
dat += "<b>BYOND Membership Publicity:</b> <a href='?_src_=prefs;preference=publicity'><b>[(toggles & PREFTOGGLE_MEMBER_PUBLIC) ? "Public" : "Hidden"]</b></a><br>"
dat += "<b>Colourblind Mode:</b> <a href='?_src_=prefs;preference=cbmode'>[colourblind_mode]</a><br>"
dat += "<b>Custom UI settings:</b><br>"
dat += " - <b>Alpha (transparency):</b> <a href='?_src_=prefs;preference=UIalpha'><b>[UI_style_alpha]</b></a><br>"
dat += " - <b>Color:</b> <a href='?_src_=prefs;preference=UIcolor'><b>[UI_style_color]</b></a> <span style='border: 1px solid #161616; background-color: [UI_style_color];'>&nbsp;&nbsp;&nbsp;</span><br>"
@@ -477,7 +480,7 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
dat += "<a href='?_src_=prefs;preference=reset_all'>Reset Setup</a>"
dat += "</center>"
var/datum/browser/popup = new(user, "preferences", "<div align='center'>Character Setup</div>", 820, 660)
var/datum/browser/popup = new(user, "preferences", "<div align='center'>Character Setup</div>", 820, 720)
popup.set_content(dat)
popup.open(0)
@@ -24,6 +24,7 @@
screentip_mode = query.item[18]
screentip_color = query.item[19]
ghost_darkness_level = query.item[20]
colourblind_mode = query.item[21]
//Sanitize
ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor))
@@ -43,10 +44,10 @@
screentip_mode = sanitize_integer(screentip_mode, 0, 20, initial(screentip_mode))
screentip_color = sanitize_hexcolor(screentip_color, initial(screentip_color))
ghost_darkness_level = sanitize_integer(ghost_darkness_level, 0, 255, initial(ghost_darkness_level))
colourblind_mode = sanitize_inlist(colourblind_mode, list(COLOURBLIND_MODE_NONE, COLOURBLIND_MODE_DEUTER, COLOURBLIND_MODE_PROT, COLOURBLIND_MODE_TRIT), COLOURBLIND_MODE_NONE)
return TRUE
/datum/preferences/proc/save_preferences(client/C)
// Might as well scrub out any malformed be_special list entries while we're here
for(var/role in be_special)
if(!(role in GLOB.special_roles))
@@ -58,57 +59,57 @@
deltimer(volume_mixer_saving)
volume_mixer_saving = null
var/datum/db_query/query = SSdbcore.NewQuery({"UPDATE player
SET
ooccolor=:ooccolour,
UI_style=:ui_style,
UI_style_color=:ui_colour,
UI_style_alpha=:ui_alpha,
be_role=:berole,
default_slot=:defaultslot,
toggles=:toggles,
toggles_2=:toggles2,
atklog=:atklog,
sound=:sound,
volume_mixer=:volume_mixer,
lastchangelog=:lastchangelog,
clientfps=:clientfps,
parallax=:parallax,
2fa_status=:_2fa_status,
screentip_mode=:screentip_mode,
screentip_color=:screentip_color,
ghost_darkness_level=:ghost_darkness_level
WHERE ckey=:ckey"}, list(
// OH GOD THE PARAMETERS
"ooccolour" = ooccolor,
"ui_style" = UI_style,
"ui_colour" = UI_style_color,
"ui_alpha" = UI_style_alpha,
"berole" = list2params(be_special),
"defaultslot" = default_slot,
// Even though its a number in the DB, you have to use num2text here, otherwise byond adds scientific notation to the number
"toggles" = num2text(toggles, CEILING(log(10, (TOGGLES_TOTAL)), 1)),
"toggles2" = num2text(toggles2, CEILING(log(10, (TOGGLES_2_TOTAL)), 1)),
"atklog" = atklog,
"sound" = sound,
"volume_mixer" = serialize_volume_mixer(volume_mixer),
"lastchangelog" = lastchangelog,
"clientfps" = clientfps,
"parallax" = parallax,
"_2fa_status" = _2fa_status,
"screentip_mode" = screentip_mode,
"screentip_color" = screentip_color,
"ghost_darkness_level" = ghost_darkness_level,
"ckey" = C.ckey,
)
)
var/datum/db_query/query = SSdbcore.NewQuery({"UPDATE player SET
ooccolor=:ooccolour,
UI_style=:ui_style,
UI_style_color=:ui_colour,
UI_style_alpha=:ui_alpha,
be_role=:berole,
default_slot=:defaultslot,
toggles=:toggles,
toggles_2=:toggles2,
atklog=:atklog,
sound=:sound,
volume_mixer=:volume_mixer,
lastchangelog=:lastchangelog,
clientfps=:clientfps,
parallax=:parallax,
2fa_status=:_2fa_status,
screentip_mode=:screentip_mode,
screentip_color=:screentip_color,
ghost_darkness_level=:ghost_darkness_level,
colourblind_mode=:colourblind_mode
WHERE ckey=:ckey"}, list(
// OH GOD THE PARAMETERS
"ooccolour" = ooccolor,
"ui_style" = UI_style,
"ui_colour" = UI_style_color,
"ui_alpha" = UI_style_alpha,
"berole" = list2params(be_special),
"defaultslot" = default_slot,
// Even though its a number in the DB, you have to use num2text here, otherwise byond adds scientific notation to the number
"toggles" = num2text(toggles, CEILING(log(10, (TOGGLES_TOTAL)), 1)),
"toggles2" = num2text(toggles2, CEILING(log(10, (TOGGLES_2_TOTAL)), 1)),
"atklog" = atklog,
"sound" = sound,
"volume_mixer" = serialize_volume_mixer(volume_mixer),
"lastchangelog" = lastchangelog,
"clientfps" = clientfps,
"parallax" = parallax,
"_2fa_status" = _2fa_status,
"screentip_mode" = screentip_mode,
"screentip_color" = screentip_color,
"ghost_darkness_level" = ghost_darkness_level,
"colourblind_mode" = colourblind_mode,
"ckey" = C.ckey,
))
if(!query.warn_execute())
qdel(query)
return
qdel(query)
return 1
return TRUE
/datum/preferences/proc/load_random_character_slot(client/C)
var/list/datum/character_save/valid_slots = list()
@@ -370,7 +370,9 @@
/mob/living/simple_animal/hostile/floor_cluwne/proc/Kill(mob/living/carbon/human/H)
playsound(H, 'sound/spookoween/scary_horn2.ogg', 100, 0)
var/old_color = H.client?.color
client_kill_animation(H)
if(H?.client?.prefs.colourblind_mode == COLOURBLIND_MODE_NONE)
client_kill_animation(H)
for(var/turf/T in orange(H, 4))
H.add_splatter_floor(T)
+53
View File
@@ -13,6 +13,57 @@
return 0
/mob/proc/get_screen_colour()
SHOULD_CALL_PARENT(TRUE)
// OOC Colourblind setting takes priority over everything else.
if(client?.prefs)
switch(client.prefs.colourblind_mode)
if(COLOURBLIND_MODE_NONE)
. = null
/*
Also it goes without saying
For the love of god, do NOT mess with the matricies below.
The values may look arbitrary as hell, but they follow colour filtering rules
to accent specific colours and block out others, which helps different
forms of colourblindness. Its not perfect but it helps.
If you ever want to modify these matricies, test them with someone who
suffers that form of colourblindness, and ask if its an improvement or a hinderance.
I cannot stress this enough
-aa07
*/
if(COLOURBLIND_MODE_DEUTER)
// Red-green (green weak, deuteranopia)
// Below is a colour matrix to account for that
. = list(
1.8, 0, -0.14, 0,
-1.05, 1, 0.1, 0,
0.3, 0, 1, 0,
0, 0, 0, 1
) // Time spent creating this matrix: 1 hour 32 minutes
if(COLOURBLIND_MODE_PROT)
// Red-green (red weak, protanopia)
// Below is a colour matrix to account for that
. = list(
1, 0.475, 0.594, 0,
0, 0.482, -0.68, 0,
0, 0.044, 1.087, 0,
0, 0, 0, 1
) // Time spent creating this matrix: 57 minutes
if(COLOURBLIND_MODE_TRIT)
// Blue-yellow (tritanopia)
// Below is a colour matrix to account for that
. = list(
0.74, 0.07, 0, 0,
-0.405, 0.593, 0, 0,
0.665, 0.335, 1, 0,
0, 0, 0, 1
) // Time spent creating this matrix: 34 minutes
return
/mob/proc/update_client_colour(time = 10) //Update the mob's client.color with an animation the specified time in length.
@@ -42,6 +93,8 @@
/mob/proc/flash_screen_color(flash_color, flash_time)
if(!client)
return
if(client?.prefs.colourblind_mode != COLOURBLIND_MODE_NONE)
return
client.color = flash_color
INVOKE_ASYNC(client, /client/.proc/colour_transition, get_screen_colour(), flash_time)
+2 -2
View File
@@ -102,7 +102,7 @@
if(!(NO_BLOOD in H.dna.species.species_traits))
H.blood_volume = max(H.blood_volume - blood_loss, 0)
to_chat(H, "<span class='userdanger'>You have to keep pumping your blood!</span>")
if(H.client)
if(H?.client?.prefs.colourblind_mode == COLOURBLIND_MODE_NONE)
H.client.color = "red" //bloody screen so real
else
last_pump = world.time //lets be extra fair *sigh*
@@ -133,7 +133,7 @@
if(istype(H))
if(!(NO_BLOOD in H.dna.species.species_traits))
H.blood_volume = min(H.blood_volume + cursed_heart.blood_loss * 0.5, BLOOD_VOLUME_NORMAL)
if(owner.client)
if(owner?.client?.prefs.colourblind_mode == COLOURBLIND_MODE_NONE)
owner.client.color = ""
H.adjustBruteLoss(-cursed_heart.heal_brute)
@@ -0,0 +1,43 @@
/datum/ui_module/colour_matrix_tester
name = "Colour Matrix Tester"
/// The datum we are modifying. This will almost always be an atom, but clients have colours too
var/datum/target_datum
// Target colour matrix to make applying easier
var/target_matrix = list(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
)
/datum/ui_module/colour_matrix_tester/New(datum/_host, datum/target)
..()
if(!(isatom(target) || isclient(target)))
stack_trace("Attempted to create a colour matrix tester on a non atom/client!")
qdel(src)
return
target_datum = target
/datum/ui_module/colour_matrix_tester/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "ColourMatrixTester", name, 350, 170, master_ui, state)
ui.autoupdate = TRUE
ui.open()
/datum/ui_module/colour_matrix_tester/ui_data(mob/user)
var/list/data = list()
data["colour_data"] = target_matrix
return data
/datum/ui_module/colour_matrix_tester/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
if(..())
return
switch(action)
if("setvalue")
target_matrix[text2num(params["idx"])] = text2num(params["value"])
target_datum:color = target_matrix // Force apply
return TRUE
+1 -1
View File
@@ -145,7 +145,7 @@ ipc_screens = [
# Enable/disable the database on a whole
sql_enabled = false
# SQL version. If this is a mismatch, round start will be delayed
sql_version = 36
sql_version = 37
# SQL server address. Can be an IP or DNS name
sql_address = "127.0.0.1"
# SQL server port
+1
View File
@@ -2527,6 +2527,7 @@
#include "code\modules\tgui\modules\_base.dm"
#include "code\modules\tgui\modules\appearance_changer.dm"
#include "code\modules\tgui\modules\atmos_control.dm"
#include "code\modules\tgui\modules\colour_matrix_tester.dm"
#include "code\modules\tgui\modules\crew_monitor.dm"
#include "code\modules\tgui\modules\ert_manager.dm"
#include "code\modules\tgui\modules\generic_crew_manifest.dm"
@@ -0,0 +1,71 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import {
Box,
Section,
NumberInput,
} from '../components';
import { Window } from '../layouts';
export const ColourMatrixTester = (props, context) => {
const { act, data } = useBackend(context);
const {
colour_data,
} = data;
const matrix_entries = [
[
{ name: "RR", idx: 0 },
{ name: "RG", idx: 1 },
{ name: "RB", idx: 2 },
{ name: "RA", idx: 3 },
],
[
{ name: "GR", idx: 4 },
{ name: "GG", idx: 5 },
{ name: "GB", idx: 6 },
{ name: "GA", idx: 7 },
],[
{ name: "BR", idx: 8 },
{ name: "BG", idx: 9 },
{ name: "BB", idx: 10 },
{ name: "BA", idx: 11 },
],[
{ name: "AR", idx: 12 },
{ name: "AG", idx: 13 },
{ name: "AB", idx: 14 },
{ name: "AA", idx: 15 },
],
]
return (
<Window>
<Window.Content>
<Section title="Modify Matrix">
{matrix_entries.map(k => (
<Box key={k} textAlign="center">
{k.map(k2 => (
<Fragment key={k2.name}>
{k2.name}&nbsp;
<NumberInput
value={colour_data[k2.idx]}
step={0.05}
minValue={-5}
maxValue={5}
onDrag={(e, value) =>
act('setvalue', {
/* +1 to account for BYOND lists */
idx: k2.idx + 1,
value: value,
})
}
/>
</Fragment>
))}
</Box>
))}
</Section>
</Window.Content>
</Window>
);
};
File diff suppressed because one or more lines are too long