diff --git a/code/__DEFINES/interaction_flags.dm b/code/__DEFINES/interaction_flags.dm index df6ae2d899d..fb3a7439b10 100644 --- a/code/__DEFINES/interaction_flags.dm +++ b/code/__DEFINES/interaction_flags.dm @@ -38,3 +38,5 @@ #define INTERACT_MACHINE_SET_MACHINE (1<<6) /// the user must have vision to interact (blind people need not apply) #define INTERACT_MACHINE_REQUIRES_SIGHT (1<<7) +/// the user must be able to read to interact +#define INTERACT_MACHINE_REQUIRES_LITERACY (1<<8) diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index b5a457965b7..5690b8424fd 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -140,6 +140,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_INCAPACITATED "incapacitated" /// In some kind of critical condition. Is able to succumb. #define TRAIT_CRITICAL_CONDITION "critical-condition" +#define TRAIT_ILLITERATE "illiterate" #define TRAIT_BLIND "blind" #define TRAIT_MUTE "mute" #define TRAIT_EMOTEMUTE "emotemute" diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 80d4e8687ed..8f20ed4f0b7 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -155,6 +155,7 @@ DEFINE_BITFIELD(interaction_flags_machine, list( "INTERACT_MACHINE_OPEN" = INTERACT_MACHINE_OPEN, "INTERACT_MACHINE_OPEN_SILICON" = INTERACT_MACHINE_OPEN_SILICON, "INTERACT_MACHINE_REQUIRES_SIGHT" = INTERACT_MACHINE_REQUIRES_SIGHT, + "INTERACT_MACHINE_REQUIRES_LITERACY" = INTERACT_MACHINE_REQUIRES_LITERACY, "INTERACT_MACHINE_REQUIRES_SILICON" = INTERACT_MACHINE_REQUIRES_SILICON, "INTERACT_MACHINE_SET_MACHINE" = INTERACT_MACHINE_SET_MACHINE, "INTERACT_MACHINE_WIRES_IF_OPEN" = INTERACT_MACHINE_WIRES_IF_OPEN, diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index bee3dbf2760..6e7ffe5a38a 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -13,6 +13,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_RESTRAINED" = TRAIT_RESTRAINED, "TRAIT_INCAPACITATED" = TRAIT_INCAPACITATED, "TRAIT_CRITICAL_CONDITION" = TRAIT_CRITICAL_CONDITION, + "TRAIT_ILLITERATE" = TRAIT_ILLITERATE, "TRAIT_BLIND" = TRAIT_BLIND, "TRAIT_MUTE" = TRAIT_MUTE, "TRAIT_EMOTEMUTE " = TRAIT_EMOTEMUTE, diff --git a/code/datums/brain_damage/severe.dm b/code/datums/brain_damage/severe.dm index d68bb85732d..9aa71f91610 100644 --- a/code/datums/brain_damage/severe.dm +++ b/code/datums/brain_damage/severe.dm @@ -300,3 +300,18 @@ /datum/brain_trauma/severe/hypnotic_trigger/proc/hypnotrigger() to_chat(owner, span_warning("The words trigger something deep within you, and you feel your consciousness slipping away...")) owner.apply_status_effect(/datum/status_effect/trance, rand(100,300), FALSE) + +/datum/brain_trauma/severe/dyslexia + name = "Dyslexia" + desc = "Patient is unable to read or write." + scan_desc = "dyslexia" + gain_text = "You have trouble reading or writing..." + lose_text = "Your suddenly remember how to read and write." + +/datum/brain_trauma/severe/dyslexia/on_gain() + ADD_TRAIT(owner, TRAIT_ILLITERATE, TRAUMA_TRAIT) + ..() + +/datum/brain_trauma/severe/dyslexia/on_lose() + REMOVE_TRAIT(owner, TRAIT_ILLITERATE, TRAUMA_TRAIT) + ..() diff --git a/code/datums/quirks/negative.dm b/code/datums/quirks/negative.dm index 8670a025b9d..4b94351014b 100644 --- a/code/datums/quirks/negative.dm +++ b/code/datums/quirks/negative.dm @@ -895,3 +895,12 @@ return TRUE return FALSE + +/datum/quirk/illiterate + name = "Illiterate" + desc = "You dropped out of school and are unable to read or write. This affects reading, writing, using computers and other electronics." + icon = "graduation-cap" + value = -8 + mob_trait = TRAIT_ILLITERATE + medical_record_text = "Patient is not literate." + hardcore_value = 8 diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index 629aeb814ee..97b382e010d 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -130,7 +130,7 @@ var/subsystem_type = /datum/controller/subsystem/machines var/obj/item/circuitboard/circuit // Circuit to be created and inserted when the machinery is created - var/interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_SET_MACHINE + var/interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN|INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_OPEN_SILICON|INTERACT_MACHINE_SET_MACHINE var/fair_market_price = 69 var/market_verb = "Customer" var/payment_department = ACCOUNT_ENG @@ -561,6 +561,10 @@ to_chat(user, span_warning("This machine requires sight to use.")) return FALSE + // machines have their own lit up display screens and LED buttons so we don't need to check for light + if((interaction_flags_machine & INTERACT_MACHINE_REQUIRES_LITERACY) && !user.can_read(src, check_for_light = FALSE)) + return FALSE + if(panel_open && !(interaction_flags_machine & INTERACT_MACHINE_OPEN)) return FALSE diff --git a/code/game/machinery/computer/_computer.dm b/code/game/machinery/computer/_computer.dm index 0850164c06f..4f7c1aaf081 100644 --- a/code/game/machinery/computer/_computer.dm +++ b/code/game/machinery/computer/_computer.dm @@ -6,6 +6,7 @@ max_integrity = 200 integrity_failure = 0.5 armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, FIRE = 40, ACID = 20) + interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_SET_MACHINE|INTERACT_MACHINE_REQUIRES_LITERACY var/brightness_on = 1 var/icon_keyboard = "generic_key" var/icon_screen = "generic" diff --git a/code/game/machinery/computer/arcade/arcade.dm b/code/game/machinery/computer/arcade/arcade.dm index a5060704ce7..810bfd015aa 100644 --- a/code/game/machinery/computer/arcade/arcade.dm +++ b/code/game/machinery/computer/arcade/arcade.dm @@ -71,6 +71,7 @@ GLOBAL_LIST_INIT(arcade_prize_pool, list( icon_keyboard = null icon_screen = "invaders" light_color = LIGHT_COLOR_GREEN + interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_SET_MACHINE // we don't need to be literate to play video games fam var/list/prize_override /obj/machinery/computer/arcade/proc/Reset() diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index 37637187b7a..91fbd138091 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -21,7 +21,7 @@ var/list/cam_plane_masters var/atom/movable/screen/background/cam_background - interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_SET_MACHINE | INTERACT_MACHINE_REQUIRES_SIGHT + interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_SET_MACHINE|INTERACT_MACHINE_REQUIRES_SIGHT /obj/machinery/computer/security/Initialize(mapload) . = ..() diff --git a/code/game/machinery/newscaster/newscaster_machine.dm b/code/game/machinery/newscaster/newscaster_machine.dm index 0f4e29cbfe7..cd797900943 100644 --- a/code/game/machinery/newscaster/newscaster_machine.dm +++ b/code/game/machinery/newscaster/newscaster_machine.dm @@ -12,6 +12,7 @@ armor = list(MELEE = 50, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, FIRE = 50, ACID = 30) max_integrity = 200 integrity_failure = 0.25 + interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_SET_MACHINE|INTERACT_MACHINE_REQUIRES_LITERACY ///Reference to the currently logged in user. var/datum/bank_account/current_user ///Name of the logged in user. diff --git a/code/game/machinery/newscaster/newspaper.dm b/code/game/machinery/newscaster/newspaper.dm index f2b1128143a..2de86e1b5a5 100644 --- a/code/game/machinery/newscaster/newspaper.dm +++ b/code/game/machinery/newscaster/newspaper.dm @@ -33,10 +33,8 @@ return(TOXLOSS) /obj/item/newspaper/attack_self(mob/user) - if(!ishuman(user)) - to_chat(user, span_warning("The paper is full of unintelligible symbols!")) + if(!istype(user) || !user.can_read(src)) return - var/mob/living/carbon/human/human_user = user var/dat pages = 0 switch(screen) @@ -61,7 +59,7 @@ dat+="" if(scribble_page==curr_page) dat+="
There is a small scribble near the end of this page... It reads: \"[scribble]\"" - dat+= "
Next Page
Done reading
" + dat+= "
Next Page
Done reading
" if(1) // X channel pages inbetween. for(var/datum/feed_channel/NP in news_content) pages++ @@ -110,8 +108,8 @@ dat+="
There is a small scribble near the end of this page... It reads: \"[scribble]\"" dat+= "
Previous Page
" dat+="

[curr_page+1]
" - human_user << browse(dat, "window=newspaper_main;size=300x400") - onclose(human_user, "newspaper_main") + user << browse(dat, "window=newspaper_main;size=300x400") + onclose(user, "newspaper_main") /obj/item/newspaper/proc/notContent(list/L) if(!L.len) @@ -160,8 +158,7 @@ return if(istype(W, /obj/item/pen)) - if(!user.is_literate()) - to_chat(user, span_notice("You scribble illegibly on [src]!")) + if(!user.can_write(W)) return if(scribble_page == curr_page) to_chat(user, span_warning("There's already a scribble in this page... You wouldn't want to make things too cluttered, would you?")) diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm index 9edd39344f8..837af93188b 100644 --- a/code/game/objects/items/cards_ids.dm +++ b/code/game/objects/items/cards_ids.dm @@ -681,6 +681,9 @@ /obj/item/card/id/examine(mob/user) . = ..() + if(!user.can_read(src)) + return + if(registered_account) . += "The account linked to the ID belongs to '[registered_account.account_holder]' and reports a balance of [registered_account.account_balance] cr." if((ACCESS_COMMAND in access) || (ACCESS_QM in access)) @@ -692,6 +695,9 @@ . += span_notice("There's more information below, you can look again to take a closer look...") /obj/item/card/id/examine_more(mob/user) + if(!user.can_read(src)) + return + . = ..() . += span_notice("You examine [src] closer, and note the following...") @@ -1171,6 +1177,9 @@ /obj/item/card/id/advanced/prisoner/examine(mob/user) . = ..() + if(!.) + return + if(timed) if(time_left <= 0) . += span_notice("The digital timer on the card has zero seconds remaining. You leave a changed man, but a free man nonetheless.") diff --git a/code/game/objects/items/devices/scanners/gas_analyzer.dm b/code/game/objects/items/devices/scanners/gas_analyzer.dm index 8d5b05c82da..2b1a22190f9 100644 --- a/code/game/objects/items/devices/scanners/gas_analyzer.dm +++ b/code/game/objects/items/devices/scanners/gas_analyzer.dm @@ -38,46 +38,48 @@ /obj/item/analyzer/AltClick(mob/user) //Barometer output for measuring when the next storm happens ..() - if(user.canUseTopic(src, BE_CLOSE)) - if(cooldown) - to_chat(user, span_warning("[src]'s barometer function is preparing itself.")) + if(!user.canUseTopic(src, BE_CLOSE) || !user.can_read(src)) + return + + if(cooldown) + to_chat(user, span_warning("[src]'s barometer function is preparing itself.")) + return + + var/turf/T = get_turf(user) + if(!T) + return + + playsound(src, 'sound/effects/pop.ogg', 100) + var/area/user_area = T.loc + var/datum/weather/ongoing_weather = null + + if(!user_area.outdoors) + to_chat(user, span_warning("[src]'s barometer function won't work indoors!")) + return + + for(var/V in SSweather.processing) + var/datum/weather/W = V + if(W.barometer_predictable && (T.z in W.impacted_z_levels) && W.area_type == user_area.type && !(W.stage == END_STAGE)) + ongoing_weather = W + break + + if(ongoing_weather) + if((ongoing_weather.stage == MAIN_STAGE) || (ongoing_weather.stage == WIND_DOWN_STAGE)) + to_chat(user, span_warning("[src]'s barometer function can't trace anything while the storm is [ongoing_weather.stage == MAIN_STAGE ? "already here!" : "winding down."]")) return - var/turf/T = get_turf(user) - if(!T) - return - - playsound(src, 'sound/effects/pop.ogg', 100) - var/area/user_area = T.loc - var/datum/weather/ongoing_weather = null - - if(!user_area.outdoors) - to_chat(user, span_warning("[src]'s barometer function won't work indoors!")) - return - - for(var/V in SSweather.processing) - var/datum/weather/W = V - if(W.barometer_predictable && (T.z in W.impacted_z_levels) && W.area_type == user_area.type && !(W.stage == END_STAGE)) - ongoing_weather = W - break - - if(ongoing_weather) - if((ongoing_weather.stage == MAIN_STAGE) || (ongoing_weather.stage == WIND_DOWN_STAGE)) - to_chat(user, span_warning("[src]'s barometer function can't trace anything while the storm is [ongoing_weather.stage == MAIN_STAGE ? "already here!" : "winding down."]")) - return - - to_chat(user, span_notice("The next [ongoing_weather] will hit in [butchertime(ongoing_weather.next_hit_time - world.time)].")) - if(ongoing_weather.aesthetic) - to_chat(user, span_warning("[src]'s barometer function says that the next storm will breeze on by.")) + to_chat(user, span_notice("The next [ongoing_weather] will hit in [butchertime(ongoing_weather.next_hit_time - world.time)].")) + if(ongoing_weather.aesthetic) + to_chat(user, span_warning("[src]'s barometer function says that the next storm will breeze on by.")) + else + var/next_hit = SSweather.next_hit_by_zlevel["[T.z]"] + var/fixed = next_hit ? timeleft(next_hit) : -1 + if(fixed < 0) + to_chat(user, span_warning("[src]'s barometer function was unable to trace any weather patterns.")) else - var/next_hit = SSweather.next_hit_by_zlevel["[T.z]"] - var/fixed = next_hit ? timeleft(next_hit) : -1 - if(fixed < 0) - to_chat(user, span_warning("[src]'s barometer function was unable to trace any weather patterns.")) - else - to_chat(user, span_warning("[src]'s barometer function says a storm will land in approximately [butchertime(fixed)].")) - cooldown = TRUE - addtimer(CALLBACK(src,/obj/item/analyzer/proc/ping), cooldown_time) + to_chat(user, span_warning("[src]'s barometer function says a storm will land in approximately [butchertime(fixed)].")) + cooldown = TRUE + addtimer(CALLBACK(src,/obj/item/analyzer/proc/ping), cooldown_time) /obj/item/analyzer/proc/ping() if(isliving(loc)) @@ -112,17 +114,13 @@ return list("gasmixes" = last_gasmix_data) /obj/item/analyzer/attack_self(mob/user, modifiers) - // Check if it requires visibility and if the user is you know, blind. - if (user.stat != CONSCIOUS || user.is_blind()) - to_chat(user, span_warning("You're unable to see [src]'s results!")) + if(user.stat != CONSCIOUS || !user.can_read(src) || user.is_blind()) return atmos_scan(user=user, target=get_turf(src), tool=src, silent=FALSE) on_analyze(source=src, target=get_turf(src)) /obj/item/analyzer/attack_self_secondary(mob/user, modifiers) - // Check if it requires visibility and if the user is you know, blind. - if (user.stat != CONSCIOUS || user.is_blind()) - to_chat(user, span_warning("You're unable to see [src]'s results!")) + if(user.stat != CONSCIOUS || !user.can_read(src) || user.is_blind()) return ui_interact(user) diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm index d20bb61b3a3..6aa456eb316 100644 --- a/code/game/objects/items/devices/scanners/health_analyzer.dm +++ b/code/game/objects/items/devices/scanners/health_analyzer.dm @@ -29,7 +29,6 @@ /obj/item/healthanalyzer/Initialize(mapload) . = ..() - register_item_context() /obj/item/healthanalyzer/examine(mob/user) @@ -41,6 +40,9 @@ return BRUTELOSS /obj/item/healthanalyzer/attack_self(mob/user) + if(!user.can_read(src) || user.is_blind()) + return + scanmode = (scanmode + 1) % SCANMODE_COUNT switch(scanmode) if(SCANMODE_HEALTH) @@ -49,6 +51,9 @@ to_chat(user, span_notice("You switch the health analyzer to report extra info on wounds.")) /obj/item/healthanalyzer/attack(mob/living/M, mob/living/carbon/human/user) + if(!user.can_read(src) || user.is_blind()) + return + flick("[icon_state]-scan", src) //makes it so that it plays the scan animation upon scanning, including clumsy scanning // Clumsiness/brain damage check @@ -77,6 +82,9 @@ add_fingerprint(user) /obj/item/healthanalyzer/attack_secondary(mob/living/victim, mob/living/user, params) + if(!user.can_read(src) || user.is_blind()) + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + chemscan(user, victim) return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN @@ -103,10 +111,6 @@ if(user.incapacitated()) return - if(user.is_blind()) - to_chat(user, span_warning("You realize that your scanner has no accessibility support for the blind!")) - return - // the final list of strings to render var/render_list = list() @@ -378,10 +382,6 @@ if(user.incapacitated()) return - if(user.is_blind()) - to_chat(user, span_warning("You realize that your scanner has no accessibility support for the blind!")) - return - if(istype(target) && target.reagents) var/render_list = list() @@ -438,7 +438,7 @@ /obj/item/healthanalyzer/AltClick(mob/user) ..() - if(!user.canUseTopic(src, BE_CLOSE)) + if(!user.canUseTopic(src, BE_CLOSE) || !user.can_read(src) || user.is_blind()) return mode = !mode @@ -455,10 +455,6 @@ if(!istype(patient) || user.incapacitated()) return - if(user.is_blind()) - to_chat(user, span_warning("You realize that your scanner has no accessibility support for the blind!")) - return - var/render_list = "" for(var/i in patient.get_wounded_bodyparts()) var/obj/item/bodypart/wounded_part = i @@ -505,6 +501,9 @@ L.dropItemToGround(src) /obj/item/healthanalyzer/wound/attack(mob/living/carbon/patient, mob/living/carbon/human/user) + if(!user.can_read(src) || user.is_blind()) + return + add_fingerprint(user) user.visible_message(span_notice("[user] scans [patient] for serious injuries."), span_notice("You scan [patient] for serious injuries.")) diff --git a/code/game/objects/items/devices/scanners/sequence_scanner.dm b/code/game/objects/items/devices/scanners/sequence_scanner.dm index 7150cf883e1..f74c82c0faf 100644 --- a/code/game/objects/items/devices/scanners/sequence_scanner.dm +++ b/code/game/objects/items/devices/scanners/sequence_scanner.dm @@ -70,7 +70,7 @@ if(LAZYFIND(active_mutations, mutation)) to_chat(user, span_boldnotice("[get_display_name(mutation)]")) else - to_chat(user, span_notice("[get_display_name(mutation)]")) + to_chat(user, span_notice("[get_display_name(mutation)]")) /obj/item/sequence_scanner/proc/display_sequence(mob/living/user) if(!LAZYLEN(buffer) || !ready) @@ -82,25 +82,27 @@ var/answer = tgui_input_list(user, "Analyze Potential", "Sequence Analyzer", sort_list(options)) if(isnull(answer)) return - if(ready && user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) - var/sequence - for(var/mutation in buffer) //this physically hurts but i dont know what anything else short of an assoc list - if(get_display_name(mutation) == answer) - sequence = buffer[mutation] - break + if(!ready || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) || !user.can_read(src)) + return - if(sequence) - var/display - for(var/i in 0 to length_char(sequence) / DNA_MUTATION_BLOCKS-1) - if(i) - display += "-" - display += copytext_char(sequence, 1 + i*DNA_MUTATION_BLOCKS, DNA_MUTATION_BLOCKS*(1+i) + 1) + var/sequence + for(var/mutation in buffer) //this physically hurts but i dont know what anything else short of an assoc list + if(get_display_name(mutation) == answer) + sequence = buffer[mutation] + break - to_chat(user, "[span_boldnotice("[display]")]
") + if(sequence) + var/display + for(var/i in 0 to length_char(sequence) / DNA_MUTATION_BLOCKS-1) + if(i) + display += "-" + display += copytext_char(sequence, 1 + i*DNA_MUTATION_BLOCKS, DNA_MUTATION_BLOCKS*(1+i) + 1) - ready = FALSE - icon_state = "[icon_state]_recharging" - addtimer(CALLBACK(src, .proc/recharge), cooldown, TIMER_UNIQUE) + to_chat(user, "[span_boldnotice("[display]")]
") + + ready = FALSE + icon_state = "[icon_state]_recharging" + addtimer(CALLBACK(src, .proc/recharge), cooldown, TIMER_UNIQUE) /obj/item/sequence_scanner/proc/recharge() icon_state = initial(icon_state) diff --git a/code/game/objects/items/devices/scanners/slime_scanner.dm b/code/game/objects/items/devices/scanners/slime_scanner.dm index 5b97ac3d75a..502a345a558 100644 --- a/code/game/objects/items/devices/scanners/slime_scanner.dm +++ b/code/game/objects/items/devices/scanners/slime_scanner.dm @@ -14,7 +14,7 @@ custom_materials = list(/datum/material/iron=30, /datum/material/glass=20) /obj/item/slime_scanner/attack(mob/living/M, mob/living/user) - if(user.stat || user.is_blind()) + if(user.stat || !user.can_read(src) || user.is_blind()) return if (!isslime(M)) to_chat(user, span_warning("This device can only scan slimes!")) diff --git a/code/modules/hydroponics/hydroitemdefines.dm b/code/modules/hydroponics/hydroitemdefines.dm index 612c043f5e0..19ba2b4377f 100644 --- a/code/modules/hydroponics/hydroitemdefines.dm +++ b/code/modules/hydroponics/hydroitemdefines.dm @@ -51,14 +51,14 @@ /// When we attack something, first - try to scan something we hit with left click. Left-clicking uses scans for stats /obj/item/plant_analyzer/pre_attack(atom/target, mob/living/user) . = ..() - if(user.combat_mode) + if(user.combat_mode || !user.can_read(src)) return return do_plant_stats_scan(target, user) /// Same as above, but with right click. Right-clicking scans for chemicals. /obj/item/plant_analyzer/pre_attack_secondary(atom/target, mob/living/user) - if(user.combat_mode) + if(user.combat_mode || !user.can_read(src)) return SECONDARY_ATTACK_CONTINUE_CHAIN return do_plant_chem_scan(target, user) ? SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN : SECONDARY_ATTACK_CONTINUE_CHAIN diff --git a/code/modules/library/book.dm b/code/modules/library/book.dm index bc7af7917aa..05343ec27cb 100644 --- a/code/modules/library/book.dm +++ b/code/modules/library/book.dm @@ -129,25 +129,24 @@ /obj/item/book/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/pen)) + if(!user.canUseTopic(src, BE_CLOSE) || !user.can_write(I)) + return if(user.is_blind()) to_chat(user, span_warning("As you are trying to write on the book, you suddenly feel very stupid!")) return if(unique) to_chat(user, span_warning("These pages don't seem to take the ink well! Looks like you can't modify it.")) return - var/literate = user.is_literate() - if(!literate) - to_chat(user, span_notice("You scribble illegibly on the cover of [src]!")) - return + var/choice = tgui_input_list(usr, "What would you like to change?", "Book Alteration", list("Title", "Contents", "Author", "Cancel")) if(isnull(choice)) return - if(!user.canUseTopic(src, BE_CLOSE, literate)) + if(!user.canUseTopic(src, BE_CLOSE) || !user.can_write(I)) return switch(choice) if("Title") var/newtitle = reject_bad_text(tgui_input_text(user, "Write a new title", "Book Title", max_length = 30)) - if(!user.canUseTopic(src, BE_CLOSE, literate)) + if(!user.canUseTopic(src, BE_CLOSE) || !user.can_write(I)) return if (length_char(newtitle) > 30) to_chat(user, span_warning("That title won't fit on the cover!")) @@ -159,7 +158,7 @@ book_data.set_title(html_decode(newtitle)) //Don't want to double encode here if("Contents") var/content = tgui_input_text(user, "Write your book's contents (HTML NOT allowed)", "Book Contents", multiline = TRUE) - if(!user.canUseTopic(src, BE_CLOSE, literate)) + if(!user.canUseTopic(src, BE_CLOSE) || !user.can_write(I)) return if(!content) to_chat(user, span_warning("The content is invalid.")) @@ -167,7 +166,7 @@ book_data.set_content(html_decode(content)) if("Author") var/author = tgui_input_text(user, "Write the author's name", "Author Name") - if(!user.canUseTopic(src, BE_CLOSE, literate)) + if(!user.canUseTopic(src, BE_CLOSE) || !user.can_write(I)) return if(!author) to_chat(user, span_warning("The name is invalid.")) diff --git a/code/modules/library/bookcase.dm b/code/modules/library/bookcase.dm index 356cb2024dd..ae45b5b5861 100644 --- a/code/modules/library/bookcase.dm +++ b/code/modules/library/bookcase.dm @@ -119,11 +119,10 @@ to_chat(user, span_notice("You empty \the [I] into \the [src].")) update_appearance() else if(istype(I, /obj/item/pen)) - if(!user.is_literate()) - to_chat(user, span_notice("You scribble illegibly on the side of [src]!")) + if(!user.canUseTopic(src, BE_CLOSE) || !user.can_write(I)) return var/newname = tgui_input_text(user, "What would you like to title this bookshelf?", "Bookshelf Renaming", max_length = MAX_NAME_LEN) - if(!user.canUseTopic(src, BE_CLOSE)) + if(!user.canUseTopic(src, BE_CLOSE) || !user.can_write(I)) return if(!newname) return diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm index 7326cbc4da2..fbaa883b461 100644 --- a/code/modules/mining/mine_items.dm +++ b/code/modules/mining/mine_items.dm @@ -89,6 +89,30 @@ to_chat(user, span_warning("You get the feeling you shouldn't mess with this.")) return + if(HAS_TRAIT(user, TRAIT_ILLITERATE)) + to_chat(user, span_warning("You start mashing buttons at random!")) + if(do_after(user, 10 SECONDS, target = src)) + var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId) + if(no_destination_swap) + if(M.mode == SHUTTLE_RECHARGING) + to_chat(usr, span_warning("Shuttle engines are not ready for use.")) + return + if(M.mode != SHUTTLE_IDLE) + to_chat(usr, span_warning("Shuttle already in transit.")) + return + var/destionation = M.getDockedId() == "mining_home" ? "mining_away" : "mining_home" + switch(SSshuttle.moveShuttle(shuttleId, destionation, 1)) + if(0) + say("Shuttle departing. Please stand away from the doors.") + log_shuttle("[key_name(usr)] has sent shuttle \"[M]\" towards \"[destionation]\", using [src].") + return TRUE + if(1) + to_chat(usr, span_warning("Invalid shuttle requested.")) + else + to_chat(usr, span_warning("Unable to comply.")) + + return + return ..() /obj/machinery/computer/shuttle/mining/common diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 2734ccb3b53..bf0912e70cf 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -845,8 +845,8 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp /mob/dead/observer/is_literate() return TRUE -/mob/dead/observer/can_read(obj/O) - return TRUE +/mob/dead/observer/can_read(obj/O, check_for_light = FALSE) + return TRUE // we want to bypass all the checks /mob/dead/observer/vv_edit_var(var_name, var_value) . = ..() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index f6110b756cf..1dcc00a9eee 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -773,7 +773,7 @@ return ..() /mob/living/carbon/human/is_literate() - return TRUE + return !HAS_TRAIT(src, TRAIT_ILLITERATE) /mob/living/carbon/human/vomit(lost_nutrition = 10, blood = FALSE, stun = TRUE, distance = 1, message = TRUE, vomit_type = VOMIT_TOXIC, harm = TRUE, force = FALSE, purge_ratio = 0.1) if(blood && (NOBLOOD in dna.species.species_traits) && !HAS_TRAIT(src, TRAIT_TOXINLOVER)) diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index f86ebf17444..c649d741fe6 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -86,3 +86,4 @@ var/hal_screwydoll /// When an braindead player has their equipment fiddled with, we log that info here for when they come back so they know who took their ID while they were DC'd for 30 seconds var/list/afk_thefts + diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm index 4844add6702..67516ae893e 100644 --- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm @@ -187,6 +187,7 @@ Lizard subspecies: ASHWALKERS TRAIT_CAN_STRIP, TRAIT_CHUNKYFINGERS, TRAIT_VIRUSIMMUNE, + TRAIT_ILLITERATE, ) species_language_holder = /datum/language_holder/lizard/ash digitigrade_customization = DIGITIGRADE_FORCED diff --git a/code/modules/mob/living/living_fov.dm b/code/modules/mob/living/living_fov.dm index cb2b7f78a48..669dff63c89 100644 --- a/code/modules/mob/living/living_fov.dm +++ b/code/modules/mob/living/living_fov.dm @@ -13,10 +13,10 @@ if(fov_view) if(rel_x >= -1 && rel_x <= 1 && rel_y >= -1 && rel_y <= 1) //Cheap way to check inside that 3x3 box around you return TRUE //Also checks if both are 0 to stop division by zero - + // Get the vector length so we can create a good directional vector var/vector_len = sqrt(abs(rel_x) ** 2 + abs(rel_y) ** 2) - + /// Getting a direction vector var/dir_x var/dir_y @@ -33,10 +33,10 @@ if(WEST) dir_x = -vector_len dir_y = 0 - + ///Calculate angle var/angle = arccos((dir_x * rel_x + dir_y * rel_y) / (sqrt(dir_x**2 + dir_y**2) * sqrt(rel_x**2 + rel_y**2))) - + /// Calculate vision angle and compare var/vision_angle = (360 - fov_view) / 2 if(angle < vision_angle) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 3ee150c7ea6..bb88f2f91bf 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1160,7 +1160,11 @@ to_chat(src, span_warning("You can't write with the [writing_instrument]!")) if(!is_literate()) - to_chat(src, span_warning("You don't know how to write.")) + to_chat(src, span_warning("You try to write, but don't know how to spell anything!")) + return FALSE + + if(!has_light_nearby() && !has_nightvision()) + to_chat(src, span_warning("It's too dark in here to write anything!")) return FALSE var/obj/item/pen/pen = writing_instrument @@ -1188,12 +1192,12 @@ return mob_location.get_lumcount() > light_amount /// Can this mob read -/mob/proc/can_read(obj/O) +/mob/proc/can_read(obj/O, check_for_light = TRUE) if(!is_literate()) to_chat(src, span_warning("You try to read [O], but can't comprehend any of it.")) return FALSE - if(!has_light_nearby() && !has_nightvision()) + if(check_for_light && !has_light_nearby() && !has_nightvision()) to_chat(src, span_warning("It's too dark in here to read!")) return FALSE diff --git a/code/modules/mod/modules/modules_medical.dm b/code/modules/mod/modules/modules_medical.dm index 9436271222e..19284a966fc 100644 --- a/code/modules/mod/modules/modules_medical.dm +++ b/code/modules/mod/modules/modules_medical.dm @@ -36,7 +36,7 @@ . = ..() if(!.) return - if(!isliving(target)) + if(!isliving(target) || !mod.wearer.can_read(src)) return switch(mode) if(HEALTH_SCAN) diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm index 76d1b15d02d..5a6d7803882 100644 --- a/code/modules/modular_computers/computers/item/computer_ui.dm +++ b/code/modules/modular_computers/computers/item/computer_ui.dm @@ -13,6 +13,9 @@ ui.close() return + if(!user.can_read(src, check_for_light = FALSE)) + return + if(HAS_TRAIT(user, TRAIT_CHUNKYFINGERS) && !allow_chunky) to_chat(user, span_warning("Your fingers are too big to use this right now!")) return diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 3faee2819e9..77249df9ee9 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -192,14 +192,11 @@ return UI_INTERACTIVE return ..() - - /obj/item/paper/can_interact(mob/user) if(in_contents_of(/obj/machinery/door/airlock)) return TRUE return ..() - /obj/item/proc/burn_paper_product_attackby_check(obj/item/I, mob/living/user, bypass_clumsy) var/ignition_message = I.ignition_effect(src, user) if(!ignition_message) @@ -270,7 +267,6 @@ return ..() - /obj/item/paper/fire_act(exposed_temperature, exposed_volume) . = ..() if(.) @@ -313,7 +309,6 @@ .["paper_state"] = icon_state /// TODO: show the sheet will bloodied or crinkling? .["stamps"] = stamps - /obj/item/paper/ui_data(mob/user) var/list/data = list() data["edit_usr"] = "[user.real_name]"