From bf8bffcbe0163a4da52f90755b23e5af49534eef Mon Sep 17 00:00:00 2001 From: Jack Edge Date: Fri, 28 Apr 2017 15:26:14 +0100 Subject: [PATCH] Codespeak language and manual :cl: coiax add: Syndicate agents can purchase a "codespeak manual", that teaches them a language that sounds like a series of codewords. You can also hit other people with the manual to teach them. One use per manual. add: Nuclear operatives have access to a deluxe manual that is more expensive but has unlimited uses. /:cl: - DNM until we have language icons - Syndicate codespeak only displays the language icon if you KNOW the language, so you cannot tell the difference between someone just shouting random codewords on the radio, and someone actually using the language. - Modifies generate_code_phrase to optionally return a list of words, rather than a preformatted string. - Codespeak manuals turn into random books after being used. --- code/__HELPERS/names.dm | 46 +++++++++++--------- code/modules/language/codespeak.dm | 69 ++++++++++++++++++++++++++++++ code/modules/language/language.dm | 22 +++++++--- code/modules/uplink/uplink_item.dm | 13 ++++++ tgstation.dme | 1 + 5 files changed, 123 insertions(+), 28 deletions(-) create mode 100644 code/modules/language/codespeak.dm diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm index 49be0a12c3b..58b8fbaa0f0 100644 --- a/code/__HELPERS/names.dm +++ b/code/__HELPERS/names.dm @@ -167,9 +167,13 @@ GLOBAL_VAR(syndicate_code_response) //Code response for traitors. /N */ -/proc/generate_code_phrase()//Proc is used for phrase and response in master_controller.dm +/proc/generate_code_phrase(return_list=FALSE)//Proc is used for phrase and response in master_controller.dm + + if(!return_list) + . = "" + else + . = list() - var/code_phrase = ""//What is returned when the proc finishes. var/words = pick(//How many words there will be. Minimum of two. 2, 4 and 5 have a lesser chance of being selected. 3 is the most likely. 50; 2, 200; 3, @@ -204,39 +208,39 @@ GLOBAL_VAR(syndicate_code_response) //Code response for traitors. switch(rand(1,2))//Mainly to add more options later. if(1) if(names.len&&prob(70)) - code_phrase += pick(names) + . += pick(names) else if(prob(10)) - code_phrase += pick(lizard_name(MALE),lizard_name(FEMALE)) + . += pick(lizard_name(MALE),lizard_name(FEMALE)) else - code_phrase += pick(pick(GLOB.first_names_male,GLOB.first_names_female)) - code_phrase += " " - code_phrase += pick(GLOB.last_names) + var/new_name = pick(pick(GLOB.first_names_male,GLOB.first_names_female)) + new_name += " " + new_name += pick(GLOB.last_names) + . += new_name if(2) - code_phrase += pick(get_all_jobs())//Returns a job. + . += pick(get_all_jobs())//Returns a job. safety -= 1 if(2) switch(rand(1,3))//Food, drinks, or things. Only selectable once. if(1) - code_phrase += lowertext(pick(drinks)) + . += lowertext(pick(drinks)) if(2) - code_phrase += lowertext(pick(foods)) + . += lowertext(pick(foods)) if(3) - code_phrase += lowertext(pick(locations)) + . += lowertext(pick(locations)) safety -= 2 if(3) switch(rand(1,4))//Abstract nouns, objects, adjectives, threats. Can be selected more than once. if(1) - code_phrase += lowertext(pick(nouns)) + . += lowertext(pick(nouns)) if(2) - code_phrase += lowertext(pick(objects)) + . += lowertext(pick(objects)) if(3) - code_phrase += lowertext(pick(adjectives)) + . += lowertext(pick(adjectives)) if(4) - code_phrase += lowertext(pick(threats)) - if(words==1) - code_phrase += "." - else - code_phrase += ", " - - return code_phrase + . += lowertext(pick(threats)) + if(!return_list) + if(words==1) + . += "." + else + . += ", " diff --git a/code/modules/language/codespeak.dm b/code/modules/language/codespeak.dm new file mode 100644 index 00000000000..da915435e5c --- /dev/null +++ b/code/modules/language/codespeak.dm @@ -0,0 +1,69 @@ +/datum/language/common/codespeak + name = "Codespeak" + desc = "Syndicate operatives can use a series of codewords to convey complex information, while sounding like random concepts and drinks to anyone listening in." + key = "t" + default_priority = 0 + +/datum/language/common/codespeak/scramble(input) + var/lookup = check_cache(input) + if(lookup) + return lookup + + . = "" + var/list/words = list() + while(length(.) < length(input)) + words += generate_code_phrase(return_list=TRUE) + . = jointext(words, ", ") + + add_to_cache(input, .) + +/obj/item/weapon/codespeak_manual + name = "codespeak manual" + desc = "The book's cover reads: \"Codespeak(tm) - Secure your communication with metaphors so elaborate, they seem randomly generated!\"" + icon = 'icons/obj/library.dmi' + icon_state = "book2" + var/charges = 1 + +/obj/item/weapon/codespeak_manual/attack_self(mob/living/user) + if(!isliving(user)) + return + + if(user.has_language(/datum/language/common/codespeak)) + to_chat(user, "You start skimming through [src], but you already know Codespeak.") + return + + to_chat(user, "You start skimming through [src], and suddenly your mind is filled with codewords and responses.") + user.grant_language(/datum/language/common/codespeak) + + use_charge() + +/obj/item/weapon/codespeak_manual/attack(mob/living/M, mob/living/user) + if(!istype(M) || !istype(user)) + return + playsound(loc, "punch", 25, 1, -1) + + if(M.stat == DEAD) + M.visible_message("[user] smacks [M]'s lifeless corpse with [src].", "[user] smacks your lifeless corpse with [src].", "You hear smacking.") + else if(M.has_language(/datum/language/common/codespeak)) + M.visible_message("[user] beats [M] over the head with [src]!", "[user] beats you over the head with [src]!", "You hear smacking.") + else + M.visible_message("[user] teaches [M] by beating them over the head with [src]!", "As [user] hits you with [src], codewords and responses flow through your mind.", "You hear smacking.") + M.grant_language(/datum/language/common/codespeak) + +/obj/item/weapon/codespeak_manual/proc/use_charge() + charges-- + if(!charges) + say("Automatic data erasure in progress!") + visible_message("The cover and contents of [src] start shifting and changing!") + + var/obj/item/weapon/book/random/newbook = new(get_turf(src)) + qdel(src) + + if(ismob(loc)) + var/mob/M = loc + if(M.is_holding(src)) + M.put_in_active_hand(newbook) + +/obj/item/weapon/codespeak_manual/unlimited + name = "deluxe codespeak manual" + charges = INFINITY diff --git a/code/modules/language/language.dm b/code/modules/language/language.dm index 55a57de9941..7e7e5bc20d2 100644 --- a/code/modules/language/language.dm +++ b/code/modules/language/language.dm @@ -56,16 +56,27 @@ return "[trim(full_name)]" +/datum/language/proc/check_cache(input) + var/lookup = scramble_cache[input] + if(lookup) + scramble_cache -= input + scramble_cache[input] = lookup + . = lookup + +/datum/language/proc/add_to_cache(input, scrambled_text) + // Add it to cache, cutting old entries if the list is too long + scramble_cache[input] = scrambled_text + if(scramble_cache.len > SCRAMBLE_CACHE_LEN) + scramble_cache.Cut(1, scramble_cache.len-SCRAMBLE_CACHE_LEN-1) + /datum/language/proc/scramble(input) if(!syllables || !syllables.len) return stars(input) // If the input is cached already, move it to the end of the cache and return it - var/lookup = scramble_cache[input] + var/lookup = check_cache(input) if(lookup) - scramble_cache -= input - scramble_cache[input] = lookup return lookup var/input_size = length(input) @@ -93,10 +104,7 @@ if(input_ending in list("!","?",".")) scrambled_text += input_ending - // Add it to cache, cutting old entries if the list is too long - scramble_cache[input] = scrambled_text - if(scramble_cache.len > SCRAMBLE_CACHE_LEN) - scramble_cache.Cut(1, scramble_cache.len-SCRAMBLE_CACHE_LEN-1) + add_to_cache(input, scrambled_text) return scrambled_text diff --git a/code/modules/uplink/uplink_item.dm b/code/modules/uplink/uplink_item.dm index 71a530a8fb9..5141cda8970 100644 --- a/code/modules/uplink/uplink_item.dm +++ b/code/modules/uplink/uplink_item.dm @@ -1127,6 +1127,19 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once. item = /obj/item/device/jammer cost = 5 +/datum/uplink_item/device_tools/codespeak_manual + name = "Codespeak Manual" + desc = "Syndicate agents can be trained to use a series of codewords to comvey complex information, which sounds like random concepts and drinks to anyone listening. This manual teaches you this Codespeak. You can also hit someone else with the manual in order to teach them. One use." + item = /obj/item/weapon/codespeak_manual + cost = 2 + exclude_modes = list(/datum/game_mode/nuclear) + +/datum/uplink_item/device_tools/codespeak_manual_deluxe + name = "Deluxe Codespeak Manual" + desc = "Syndicate agents can be trained to use a series of codewords to comvey complex information, which sounds like random concepts and drinks to anyone listening. This manual teaches you this Codespeak. You can also hit someone else with the manual in order to teach them. This is the deluxe edition, which has unlimited uses." + cost = 8 + include_modes = list(/datum/game_mode/nuclear) + // Implants /datum/uplink_item/implants category = "Implants" diff --git a/tgstation.dme b/tgstation.dme index dbf28d5ceb0..f4c0a14b3ee 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1379,6 +1379,7 @@ #include "code\modules\jobs\job_types\security.dm" #include "code\modules\jobs\job_types\silicon.dm" #include "code\modules\jobs\map_changes\map_changes.dm" +#include "code\modules\language\codespeak.dm" #include "code\modules\language\common.dm" #include "code\modules\language\draconic.dm" #include "code\modules\language\drone.dm"