diff --git a/code/datums/antagonists/datum_cult.dm b/code/datums/antagonists/datum_cult.dm
index 9016331440..346c88e6b1 100644
--- a/code/datums/antagonists/datum_cult.dm
+++ b/code/datums/antagonists/datum_cult.dm
@@ -76,6 +76,7 @@
if(mob_override)
current = mob_override
current.faction |= "cult"
+ current.grant_language(/datum/language/narsie)
current.verbs += /mob/living/proc/cult_help
if(!GLOB.cult_mastered)
current.verbs += /mob/living/proc/cult_master
@@ -88,6 +89,7 @@
if(mob_override)
current = mob_override
current.faction -= "cult"
+ current.remove_language(/datum/language/narsie)
current.verbs -= /mob/living/proc/cult_help
communion.Remove(current)
owner.current.verbs -= /mob/living/proc/cult_master
diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm
index 4573b460da..fca7d1ac1c 100644
--- a/code/game/gamemodes/cult/runes.dm
+++ b/code/game/gamemodes/cult/runes.dm
@@ -136,7 +136,7 @@ structure_check() searches for nearby cultist structures required for the invoca
if(invocation)
for(var/M in invokers)
var/mob/living/L = M
- L.say(invocation)
+ L.say(invocation, language = /datum/language/common)
do_invoke_glow()
/obj/effect/rune/proc/do_invoke_glow()
@@ -516,7 +516,7 @@ structure_check() searches for nearby cultist structures required for the invoca
/obj/effect/rune/raise_dead
cultist_name = "Raise Dead"
cultist_desc = "requires the corpse of a cultist placed upon the rune. Provided there have been sufficient sacrifices, they will be revived."
- invocation = null //Depends on the name of the user - see below
+ invocation = "Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!" //Depends on the name of the user - see below
icon_state = "1"
color = "#C80000"
var/static/revives_used = 0
@@ -556,9 +556,9 @@ structure_check() searches for nearby cultist structures required for the invoca
return
rune_in_use = 1
if(user.name == "Herbert West")
- user.say("To life, to life, I bring them!")
+ invocation = "To life, to life, I bring them!"
else
- user.say("Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!")
+ invocation = initial(invocation)
..()
revives_used++
mob_to_revive.revive(1, 1) //This does remove disabilities and such, but the rune might actually see some use because of it!
diff --git a/code/game/gamemodes/cult/runes.dm.rej b/code/game/gamemodes/cult/runes.dm.rej
new file mode 100644
index 0000000000..cd7962db1e
--- /dev/null
+++ b/code/game/gamemodes/cult/runes.dm.rej
@@ -0,0 +1,10 @@
+diff a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm (rejected hunks)
+@@ -516,7 +516,7 @@ structure_check() searches for nearby cultist structures required for the invoca
+ /obj/effect/rune/raise_dead
+ cultist_name = "Resurrect Cultist"
+ cultist_desc = "requires the corpse of a cultist placed upon the rune. Provided there have been sufficient sacrifices, they will be revived."
+- invocation = null //Depends on the name of the user - see below
++ invocation = "Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!" //Depends on the name of the user - see below
+ icon_state = "1"
+ color = "#C80000"
+ var/static/revives_used = 0
diff --git a/code/game/gamemodes/cult/talisman.dm b/code/game/gamemodes/cult/talisman.dm
index 15b850eb33..027df31acd 100644
--- a/code/game/gamemodes/cult/talisman.dm
+++ b/code/game/gamemodes/cult/talisman.dm
@@ -28,7 +28,7 @@
. = successfuluse
if(successfuluse) //if the calling whatever says we succeed, do the fancy stuff
if(invocation)
- user.whisper(invocation)
+ user.whisper(invocation, language = /datum/language/common)
if(health_cost && iscarbon(user))
var/mob/living/carbon/C = user
C.apply_damage(health_cost, BRUTE, pick("l_arm", "r_arm"))
diff --git a/code/modules/language/language.dm b/code/modules/language/language.dm
index efeabfe73d..5354532cfa 100644
--- a/code/modules/language/language.dm
+++ b/code/modules/language/language.dm
@@ -1,27 +1,27 @@
-#define SCRAMBLE_CACHE_LEN 20
-
-/*
- Datum based languages. Easily editable and modular.
-*/
-
-/datum/language
- var/name = "an unknown language" // Fluff name of language if any.
- var/desc = "A language." // Short description for 'Check Languages'.
- var/speech_verb = "says" // 'says', 'hisses', 'farts'.
- var/ask_verb = "asks" // Used when sentence ends in a ?
- var/exclaim_verb = "exclaims" // Used when sentence ends in a !
- var/whisper_verb = "whispers" // Optional. When not specified speech_verb + quietly/softly is used instead.
- var/list/signlang_verb = list("signs", "gestures") // list of emotes that might be displayed if this language has NONVERBAL or SIGNLANG flags
+#define SCRAMBLE_CACHE_LEN 50 //maximum of 50 specific scrambled lines per language
+
+/*
+ Datum based languages. Easily editable and modular.
+*/
+
+/datum/language
+ var/name = "an unknown language" // Fluff name of language if any.
+ var/desc = "A language." // Short description for 'Check Languages'.
+ var/speech_verb = "says" // 'says', 'hisses', 'farts'.
+ var/ask_verb = "asks" // Used when sentence ends in a ?
+ var/exclaim_verb = "exclaims" // Used when sentence ends in a !
+ var/whisper_verb = "whispers" // Optional. When not specified speech_verb + quietly/softly is used instead.
+ var/list/signlang_verb = list("signs", "gestures") // list of emotes that might be displayed if this language has NONVERBAL or SIGNLANG flags
var/key // Character used to speak in language
// If key is null, then the language isn't real or learnable.
- var/flags // Various language flags.
- var/list/syllables // Used when scrambling text for a non-speaker.
- var/list/sentence_chance = 5 // Likelihood of making a new sentence after each syllable.
- var/list/space_chance = 55 // Likelihood of getting a space in the random scramble string
- var/list/spans = list()
- var/static/list/scramble_cache = list()
- var/default_priority = 0 // the language that an atom knows with the highest "default_priority" is selected by default.
-
+ var/flags // Various language flags.
+ var/list/syllables // Used when scrambling text for a non-speaker.
+ var/list/sentence_chance = 5 // Likelihood of making a new sentence after each syllable.
+ var/list/space_chance = 55 // Likelihood of getting a space in the random scramble string
+ var/list/spans = list()
+ var/list/scramble_cache = list()
+ var/default_priority = 0 // the language that an atom knows with the highest "default_priority" is selected by default.
+
// if you are seeing someone speak popcorn language, then something is wrong.
var/icon = 'icons/misc/language.dmi'
var/icon_state = "popcorn"
@@ -37,75 +37,75 @@
/datum/language/proc/get_icon()
return "
"
-/datum/language/proc/get_random_name(gender, name_count=2, syllable_count=4, syllable_divisor=2)
- if(!syllables || !syllables.len)
- if(gender==FEMALE)
- return capitalize(pick(GLOB.first_names_female)) + " " + capitalize(pick(GLOB.last_names))
- else
- return capitalize(pick(GLOB.first_names_male)) + " " + capitalize(pick(GLOB.last_names))
-
- var/full_name = ""
- var/new_name = ""
-
- for(var/i in 0 to name_count)
- new_name = ""
- var/Y = rand(Floor(syllable_count/syllable_divisor), syllable_count)
- for(var/x in Y to 0)
- new_name += pick(syllables)
- full_name += " [capitalize(lowertext(new_name))]"
-
- return "[trim(full_name)]"
-
-/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]
- if(lookup)
- scramble_cache -= input
- scramble_cache[input] = lookup
- return lookup
-
- var/input_size = length(input)
- var/scrambled_text = ""
- var/capitalize = TRUE
-
- while(length(scrambled_text) < input_size)
- var/next = pick(syllables)
- if(capitalize)
- next = capitalize(next)
- capitalize = FALSE
- scrambled_text += next
- var/chance = rand(100)
- if(chance <= sentence_chance)
- scrambled_text += ". "
- capitalize = TRUE
- else if(chance > sentence_chance && chance <= space_chance)
- scrambled_text += " "
-
- scrambled_text = trim(scrambled_text)
- var/ending = copytext(scrambled_text, length(scrambled_text))
- if(ending == ".")
- scrambled_text = copytext(scrambled_text,1,length(scrambled_text)-1)
- var/input_ending = copytext(input, input_size)
- 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)
-
- return scrambled_text
-
-/datum/language/proc/get_spoken_verb(msg_end)
- switch(msg_end)
- if("!")
- return exclaim_verb
- if("?")
- return ask_verb
- return speech_verb
-
-#undef SCRAMBLE_CACHE_LEN
+/datum/language/proc/get_random_name(gender, name_count=2, syllable_count=4, syllable_divisor=2)
+ if(!syllables || !syllables.len)
+ if(gender==FEMALE)
+ return capitalize(pick(GLOB.first_names_female)) + " " + capitalize(pick(GLOB.last_names))
+ else
+ return capitalize(pick(GLOB.first_names_male)) + " " + capitalize(pick(GLOB.last_names))
+
+ var/full_name = ""
+ var/new_name = ""
+
+ for(var/i in 0 to name_count)
+ new_name = ""
+ var/Y = rand(Floor(syllable_count/syllable_divisor), syllable_count)
+ for(var/x in Y to 0)
+ new_name += pick(syllables)
+ full_name += " [capitalize(lowertext(new_name))]"
+
+ return "[trim(full_name)]"
+
+/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]
+ if(lookup)
+ scramble_cache -= input
+ scramble_cache[input] = lookup
+ return lookup
+
+ var/input_size = length(input)
+ var/scrambled_text = ""
+ var/capitalize = TRUE
+
+ while(length(scrambled_text) < input_size)
+ var/next = pick(syllables)
+ if(capitalize)
+ next = capitalize(next)
+ capitalize = FALSE
+ scrambled_text += next
+ var/chance = rand(100)
+ if(chance <= sentence_chance)
+ scrambled_text += ". "
+ capitalize = TRUE
+ else if(chance > sentence_chance && chance <= space_chance)
+ scrambled_text += " "
+
+ scrambled_text = trim(scrambled_text)
+ var/ending = copytext(scrambled_text, length(scrambled_text))
+ if(ending == ".")
+ scrambled_text = copytext(scrambled_text,1,length(scrambled_text)-1)
+ var/input_ending = copytext(input, input_size)
+ 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)
+
+ return scrambled_text
+
+/datum/language/proc/get_spoken_verb(msg_end)
+ switch(msg_end)
+ if("!")
+ return exclaim_verb
+ if("?")
+ return ask_verb
+ return speech_verb
+
+#undef SCRAMBLE_CACHE_LEN
diff --git a/code/modules/language/language.dm.rej b/code/modules/language/language.dm.rej
new file mode 100644
index 0000000000..c7f2ac173f
--- /dev/null
+++ b/code/modules/language/language.dm.rej
@@ -0,0 +1,15 @@
+diff a/code/modules/language/language.dm b/code/modules/language/language.dm (rejected hunks)
+@@ -16,10 +16,10 @@
+ // If key is null, then the language isn't real or learnable.
+ var/flags // Various language flags.
+ var/list/syllables // Used when scrambling text for a non-speaker.
+- var/list/sentence_chance = 5 // Likelihood of making a new sentence after each syllable.
+- var/list/space_chance = 55 // Likelihood of getting a space in the random scramble string
++ var/sentence_chance = 5 // Likelihood of making a new sentence after each syllable.
++ var/space_chance = 55 // Likelihood of getting a space in the random scramble string
+ var/list/spans = list()
+- var/static/list/scramble_cache = list()
++ var/list/scramble_cache = list()
+ var/default_priority = 0 // the language that an atom knows with the highest "default_priority" is selected by default.
+
+ // if you are seeing someone speak popcorn language, then something is wrong.
diff --git a/code/modules/language/language_holder.dm b/code/modules/language/language_holder.dm
index cd0ce23b23..c8ba509400 100644
--- a/code/modules/language/language_holder.dm
+++ b/code/modules/language/language_holder.dm
@@ -87,6 +87,10 @@
languages = list(/datum/language/common, /datum/language/ratvar)
only_speaks_language = /datum/language/ratvar
+/datum/language_holder/construct
+ languages = list(/datum/language/common, /datum/language/narsie)
+ only_speaks_language = /datum/language/narsie
+
/datum/language_holder/drone
languages = list(/datum/language/common, /datum/language/drone, /datum/language/machine)
only_speaks_language = /datum/language/drone
diff --git a/code/modules/language/narsian.dm b/code/modules/language/narsian.dm
new file mode 100644
index 0000000000..70966ad880
--- /dev/null
+++ b/code/modules/language/narsian.dm
@@ -0,0 +1,43 @@
+/datum/language/narsie
+ name = "Nar-Sian"
+ desc = "The ancient, blood-soaked, impossibly complex language of Nar-Sian cultists."
+ speech_verb = "intones"
+ ask_verb = "inquires"
+ exclaim_verb = "invokes"
+ key = "n"
+ sentence_chance = 8
+ space_chance = 95 //very high due to the potential length of each syllable
+ var/static/list/base_syllables = list(
+ "h", "v", "c", "e", "g", "d", "r", "n", "h", "o", "p",
+ "ra", "so", "at", "il", "ta", "gh", "sh", "ya", "te", "sh", "ol", "ma", "om", "ig", "ni", "in",
+ "sha", "mir", "sas", "mah", "zar", "tok", "lyr", "nqa", "nap", "olt", "val", "qha",
+ "fwe", "ath", "yro", "eth", "gal", "gib", "bar", "jin", "kla", "atu", "kal", "lig",
+ "yoka", "drak", "loso", "arta", "weyh", "ines", "toth", "fara", "amar", "nyag", "eske", "reth", "dedo", "btoh", "nikt", "neth",
+ "kanas", "garis", "uloft", "tarat", "khari", "thnor", "rekka", "ragga", "rfikk", "harfr", "andid", "ethra", "dedol", "totum",
+ "ntrath", "keriam"
+ ) //the list of syllables we'll combine with itself to get a larger list of syllables
+ syllables = list(
+ "sha", "mir", "sas", "mah", "hra", "zar", "tok", "lyr", "nqa", "nap", "olt", "val",
+ "yam", "qha", "fel", "det", "fwe", "mah", "erl", "ath", "yro", "eth", "gal", "mud",
+ "gib", "bar", "tea", "fuu", "jin", "kla", "atu", "kal", "lig",
+ "yoka", "drak", "loso", "arta", "weyh", "ines", "toth", "fara", "amar", "nyag", "eske", "reth", "dedo", "btoh", "nikt", "neth", "abis",
+ "kanas", "garis", "uloft", "tarat", "khari", "thnor", "rekka", "ragga", "rfikk", "harfr", "andid", "ethra", "dedol", "totum",
+ "verbot", "pleggh", "ntrath", "barhah", "pasnar", "keriam", "usinar", "savrae", "amutan", "tannin", "remium", "barada",
+ "forbici"
+ ) //the base syllables, which include a few rare ones that won't appear in the mixed syllables
+ icon_state = "narsie"
+ default_priority = 10
+
+/datum/language/narsie/New()
+ for(var/syllable in base_syllables) //we only do this once, since there's only ever a single one of each language datum.
+ for(var/target_syllable in base_syllables)
+ if(syllable != target_syllable) //don't combine with yourself
+ if(length(syllable) + length(target_syllable) > 8) //if the resulting syllable would be very long, don't put anything between it
+ syllables += "[syllable][target_syllable]"
+ else if(prob(80)) //we'll be minutely different each round.
+ syllables += "[syllable]'[target_syllable]"
+ else if(prob(25)) //5% chance of - instead of '
+ syllables += "[syllable]-[target_syllable]"
+ else //15% chance of no ' or - at all
+ syllables += "[syllable][target_syllable]"
+ ..()
diff --git a/code/modules/language/ratvar.dm b/code/modules/language/ratvarian.dm
similarity index 51%
rename from code/modules/language/ratvar.dm
rename to code/modules/language/ratvarian.dm
index cef4a3164e..b924d2fc79 100644
--- a/code/modules/language/ratvar.dm
+++ b/code/modules/language/ratvarian.dm
@@ -1,14 +1,19 @@
/datum/language/ratvar
name = "Ratvarian"
desc = "A timeless language full of power and incomprehensible to the unenlightened."
- speech_verb = "clinks"
- ask_verb = "clunks"
- exclaim_verb = "clanks"
+ var/static/random_speech_verbs = list("clanks", "clinks", "clunks", "clangs")
+ ask_verb = "requests"
+ exclaim_verb = "proclaims"
+ whisper_verb = "imparts"
key = "r"
default_priority = 10
spans = list(SPAN_ROBOT)
-
icon_state = "ratvar"
/datum/language/ratvar/scramble(var/input)
. = text2ratvar(input)
+
+/datum/language/ratvar/get_spoken_verb(msg_end)
+ if(!msg_end)
+ return pick(random_speech_verbs)
+ return ..()
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm
index a1253f27b4..304eb31e11 100644
--- a/code/modules/mob/living/simple_animal/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs.dm
@@ -27,7 +27,8 @@
unique_name = 1
AIStatus = AI_OFF //normal constructs don't have AI
loot = list(/obj/item/weapon/ectoplasm)
- del_on_death = 1
+ del_on_death = TRUE
+ initial_language_holder = /datum/language_holder/construct
deathmessage = "collapses in a shattered heap."
var/list/construct_spells = list()
var/playstyle_string = "You are a generic construct! Your job is to not exist, and you should probably adminhelp this."
diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm
index 8c08263aa7..4eaa10195b 100644
--- a/code/modules/mob/living/simple_animal/shade.dm
+++ b/code/modules/mob/living/simple_animal/shade.dm
@@ -28,7 +28,8 @@
status_flags = CANPUSH
movement_type = FLYING
loot = list(/obj/item/weapon/ectoplasm)
- del_on_death = 1
+ del_on_death = TRUE
+ initial_language_holder = /datum/language_holder/construct
/mob/living/simple_animal/shade/death()
deathmessage = "lets out a contented sigh as [p_their()] form unwinds."
diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm
index 575a634a11..5a3555c8f9 100644
--- a/code/modules/surgery/organs/tongue.dm
+++ b/code/modules/surgery/organs/tongue.dm
@@ -15,7 +15,7 @@
/datum/language/common,
/datum/language/draconic,
/datum/language/monkey,
- /datum/language/ratvar
+ /datum/language/narsie,
))
/obj/item/organ/tongue/get_spans()
diff --git a/code/modules/surgery/organs/tongue.dm.rej b/code/modules/surgery/organs/tongue.dm.rej
new file mode 100644
index 0000000000..c1f978600b
--- /dev/null
+++ b/code/modules/surgery/organs/tongue.dm.rej
@@ -0,0 +1,9 @@
+diff a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm (rejected hunks)
+@@ -15,6 +15,7 @@
+ /datum/language/common,
+ /datum/language/draconic,
+ /datum/language/monkey,
++ /datum/language/narsie,
+ /datum/language/ratvar
+ ))
+
diff --git a/icons/misc/language.dmi b/icons/misc/language.dmi
index e961c35191..081bbf1aa3 100644
Binary files a/icons/misc/language.dmi and b/icons/misc/language.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index c4b2676f1d..95c2255dfa 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1432,7 +1432,8 @@
#include "code\modules\language\language_menu.dm"
#include "code\modules\language\machine.dm"
#include "code\modules\language\monkey.dm"
-#include "code\modules\language\ratvar.dm"
+#include "code\modules\language\narsian.dm"
+#include "code\modules\language\ratvarian.dm"
#include "code\modules\language\slime.dm"
#include "code\modules\language\swarmer.dm"
#include "code\modules\language\xenocommon.dm"