initial import

This commit is contained in:
evilchili
2023-11-24 08:48:03 -05:00
commit 45f4d6e401
66 changed files with 3601 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
import importlib
import pkgutil
import sys
def import_submodules(module):
pkgs = pkgutil.iter_modules(module.__path__)
for loader, module_name, is_pkg in pkgs:
yield importlib.import_module(f"{module.__name__}.{module_name}")
supported_languages = dict(
(module.__name__.split(".")[-1], module) for module in list(import_submodules(sys.modules[__name__]))
)
+16
View File
@@ -0,0 +1,16 @@
### Abyssal
Low-ranking demons speak in a guttural, broken dialect that is louder and more
consonant, like dogs barking. The written language and naming conventions
reflect the more refined language of demons, a quiet, sibilant susurrus that
sounds at times like waves, at others like buzzing, keening insects.
*Whuewhezoawh ..uesewhaza whs..uwhiezowhisoi aizi..wheuz, whoshesowhewh
she..iwhzzos..ozi iuzu..ueshes izsha..ua..uashezshuesas, ..awho....uza
whuashoowhsz!*
**Abyssal Names:**
* Aa..Whaazwhis
* Az..Zazzou
* Uzeushsoshe..Osezawhwh
+7
View File
@@ -0,0 +1,7 @@
"""
Abyssal
"""
from .base import Language
from .names import Name, NobleName
__all__ = [Language, Name, NobleName]
+18
View File
@@ -0,0 +1,18 @@
from language import defaults, types
vowels = types.equal_weights(["a", "e", "i"], 1.0, blank=False)
consonants = types.equal_weights(["z", "sh", "s", "wh", ".."], 1.0, blank=False)
Language = types.Language(
name="celstial",
vowels=defaults.vowels,
consonants=consonants,
prefixes=None,
suffixes=None,
rules=[],
syllables=types.SyllableSet(
(types.Syllable(template="vowel|consonant") * 10, 1.0),
(types.Syllable(template="vowel|consonant") * 15, 1.0),
(types.Syllable(template="vowel|consonant") * 20, 1.0),
),
)
+11
View File
@@ -0,0 +1,11 @@
from language import types
from language.languages.abyssal import Language
Name = types.NameGenerator(
language=Language,
templates=types.NameSet(
(types.NameTemplate("name"), 1.0),
),
)
NobleName = Name
+15
View File
@@ -0,0 +1,15 @@
### Celestial
Celestial is sung, not spoken, and sounds to mortal ears like stacked harmonies
of an impossible choir. Written Celestial is akin to sheet music; it records
the grammar and vocabulary, but cannot convey meaning.
*U̇ôeȧäuueüiäûoîịėeöäueaȯịo äuäuiaîiüoûoėeäoîeȧü öoaịäu̇ôuüöeiääoȧėu
ôaîaêaịeiîäoöiôoėöi, ueûuêûiüȧuuȧaüeeêeuȯ oääuêaüâiîȧiûeu̇ėaâaȧ
uîoėu̇au̇oîäeiäüuȧoêaiêu̇u uaäeîeoäuiaịôoėȯîiäiȧė îûȧäuaûėuoäôiûäuaȧ!
Âȧuȯüiêịiäaiėöaöoi.*
**Celestial Names:**
* Ịaêäuuȧoeėuäuoaịooȯîoäu̇Au̇Aû
* Ûịịeịuȧoȯuȯaäâeeu̇Ėuiüaị
* Au̇Üaoịėuüöaâaėoöȧeäėu
+7
View File
@@ -0,0 +1,7 @@
"""
Celestial
"""
from .base import Language
from .names import Name, NobleName
__all__ = [Language, Name, NobleName]
+21
View File
@@ -0,0 +1,21 @@
from language import defaults, types
vowels = types.equal_weights(["a", "e", "i", "o", "u"], 1.0, blank=False)
consonants = types.equal_weights(
["î", "ê", "â", "û", "ô", "ä", "ö", "ü", "äu", "ȧ", "ė", "", "ȯ", ""], 1.0, blank=False
)
Language = types.Language(
name="celstial",
vowels=defaults.vowels,
consonants=consonants,
prefixes=None,
suffixes=None,
rules=[],
syllables=types.SyllableSet(
(types.Syllable(template="vowel|consonant") * 20, 1.0),
(types.Syllable(template="vowel|consonant") * 25, 1.0),
(types.Syllable(template="vowel|consonant") * 30, 1.0),
(types.Syllable(template="vowel|consonant") * 35, 1.0),
),
)
+11
View File
@@ -0,0 +1,11 @@
from language import types
from language.languages.celestial import Language
Name = types.NameGenerator(
language=Language,
templates=types.NameSet(
(types.NameTemplate("name"), 1.0),
),
)
NobleName = Name
+16
View File
@@ -0,0 +1,16 @@
### Common
Common is a complicated pidgin of influences with multiple regional dialects. Written Common is the language of
traders, and can be relied upon to be understood by most peoples to a greater or lesser degree.
*Proitsiiiy be itkif eesof detytaen. Ojaot tyskuaz apsoo nirtoet prenao.*
**Common Names:**
* Rubi Ca Momaman
* "Quiet" Gushi Murk Lirpusome
* Fewse Kerloborg
**Noble Common Names:**
* Lord Pasti Quusi Maghiheim
* Lady Gotki Lane Lopileigh III
* Dame Cu Lehaberry IX
+7
View File
@@ -0,0 +1,7 @@
"""
Common
"""
from .base import Language
from .names import Name, NobleName
__all__ = [Language, Name, NobleName]
+158
View File
@@ -0,0 +1,158 @@
from language import types
from .rules import rules
vowels = types.WeightedSet(
("a", 1.0),
("e", 1.0),
("i", 1.0),
("o", 0.8),
("u", 0.7),
("y", 0.01),
)
consonants = types.WeightedSet(
("b", 0.5),
("c", 0.5),
("d", 0.5),
("f", 0.3),
("g", 0.3),
("h", 0.5),
("j", 0.2),
("k", 0.3),
("l", 1.0),
("m", 0.5),
("n", 1.0),
("p", 0.5),
("q", 0.05),
("r", 1.0),
("s", 1.0),
("t", 1.0),
("v", 0.3),
("w", 0.2),
("x", 0.2),
("y", 0.01),
("z", 0.1),
("bs", 0.3),
("ct", 0.4),
("ch", 0.4),
("ck", 0.4),
("dd", 0.2),
("ff", 0.2),
("gh", 0.3),
("gs", 0.2),
("ms", 0.4),
("ns", 0.4),
("ps", 0.3),
("qu", 0.2),
("rb", 0.1),
("rd", 0.2),
("rf", 0.1),
("rk", 0.2),
("rl", 0.2),
("rm", 0.2),
("rn", 0.2),
("rp", 0.1),
("rs", 0.75),
("rt", 0.75),
("ry", 0.5),
("sh", 1.0),
("sk", 0.5),
("ss", 0.75),
("st", 1.0),
("sy", 0.5),
("th", 1.0),
("tk", 0.5),
("ts", 1.0),
("tt", 1.0),
("ty", 1.0),
("ws", 0.5),
)
prefixes = types.equal_weights(["ex", "re", "pre", "de", "pro", "anti"], 0.05)
suffixes = types.equal_weights(
[
"ad",
"ed",
"id",
"od",
"ud",
"af",
"ef",
"if",
"of",
"uf",
"ah",
"eh",
"ih",
"oh",
"uh",
"al",
"el",
"il",
"ol",
"ul",
"am",
"em",
"im",
"om",
"um",
"an",
"en",
"in",
"on",
"un",
"ar",
"er",
"ir",
"or",
"ur",
"as",
"es",
"is",
"os",
"us",
"at",
"et",
"it",
"ot",
"ut",
"ax",
"ex",
"ix",
"ox",
"ux",
"ay",
"ey",
"iy",
"oy",
"uy",
"az",
"ez",
"iz",
"oz",
"uz",
"ing",
],
0.05,
)
Language = types.Language(
name="common",
vowels=vowels,
consonants=consonants,
prefixes=prefixes,
suffixes=suffixes,
syllables=types.SyllableSet(
(types.Syllable(template="vowel|consonant"), 0.01),
(types.Syllable(template="vowel|consonant") * 2, 0.2),
(types.Syllable(template="vowel|consonant") * 3, 0.4),
(types.Syllable(template="vowel|consonant") * 3, 0.5),
(types.Syllable(template="vowel|consonant") * 4, 1.0),
(types.Syllable(template="vowel|consonant") * 5, 0.3),
(types.Syllable(template="vowel|consonant") * 6, 0.2),
(types.Syllable(template="vowel|consonant") * 7, 0.05),
),
rules=rules,
minimum_grapheme_count=1,
)
+82
View File
@@ -0,0 +1,82 @@
from language import defaults, types
from language.languages.common import Language
suffixes = types.equal_weights(
[
"berg",
"borg",
"borough",
"bury",
"berry",
"by",
"ford",
"gard",
"grave",
"grove",
"gren",
"hardt",
"hart",
"heim",
"holm",
"land",
"leigh",
"ley",
"ly",
"lof",
"love",
"lund",
"man",
"mark",
"ness",
"olf",
"olph",
"quist",
"rop",
"rup",
"stad",
"stead",
"stein",
"strom",
"thal",
"thorpe",
"ton",
"vall",
"wich",
"win",
"some",
"smith",
"bridge",
"cope",
"town",
"er",
"don",
"den",
"dell",
"son",
]
)
Name = types.NameGenerator(
language=Language,
syllables=types.SyllableSet(
(types.Syllable(template="vowel|consonant"), 0.01),
(types.Syllable(template="consonant,vowel"), 0.2),
(types.Syllable(template="consonant,vowel") * 2, 1.0),
),
templates=types.NameSet(
(types.NameTemplate("adjective,title,name,surname,count"), 1.0),
(types.NameTemplate("title,name,name,surname,count"), 1.0),
(types.NameTemplate("title,name,name,surname,surname,count"), 1.0),
),
names=None,
surnames=None,
nicknames=None,
adjectives=defaults.adjectives,
titles=defaults.titles,
counts=defaults.counts,
suffixes=suffixes,
)
Name.language.prefixes = None
Name.language.suffixes = None
NobleName = Name
+115
View File
@@ -0,0 +1,115 @@
import logging
import re
from language.rules import default_rules
from language.types import Language
logger = logging.getLogger()
permitted_starting_clusters = [
"bh",
"bl",
"br",
"bw",
"by",
"ch",
"cl",
"cr",
"cw",
"cy",
"dh",
"dj",
"dr",
"dw",
"dy",
"fl",
"fn",
"fr",
"fw",
"fy",
"gh",
"gl",
"gn",
"gr",
"gw",
"gy",
"hy",
"jh",
"jy",
"kh",
"kl",
"kr",
"kw",
"ky",
"ll",
"ly",
"mw",
"my",
"ny",
"ph",
"pl",
"pn",
"pr",
"pw",
"py",
"rh",
"ry",
"sb",
"sc",
"sd",
"sf",
"sg",
"sh",
"sj",
"sk",
"sl",
"sm",
"sn",
"sp",
"sr",
"st",
"sv",
"sw",
"sy",
"th",
"tr",
"tw",
"ty",
"vy",
"wh",
"wr",
"wy",
"xh",
"xy",
"yh",
"zb",
"zc",
"zd",
"zh",
"zl",
"zm",
"zn",
"zr",
"zw",
"zy",
]
def cannot_start_with_two_consonants(language: Language, word: str) -> bool:
found = re.compile(r"(^[bcdfghjklmnpqrstvwxz]{2})").search(word)
if not found:
return True
first, second = found.group(1)
if first == second:
logger.debug(f"{word} starts with a repeated consonant.")
return False
return found in permitted_starting_clusters
rules = default_rules.union(
{
cannot_start_with_two_consonants,
}
)
+23
View File
@@ -0,0 +1,23 @@
### Draconic
The tongue of dragons and the Dragonborn. It is punctuated by low, gutteral
grunts denoted by an apostrophe ('), the particular inflections of which can
change the meaning of an utterance dramatically. When shouting, a speaker of
Draconic should accentuate these grunts with spurts of flame, acid, ice, and so
on.
The high form is reserved for the names of nobility -- that is, dragons -- who
typically choose an appelation for themselves in the common tongue, the better
to spread the terror of their name.
*Sey'so'jsii ysuut 'sei'gk gf'seiv 'se'su'rx 'se'yv hsi', sahsei' k'si'saa' f'si''see?*
**Draconic Names:**
* V'Sei'Sey'a
* Sir''Seytza
* 'Soh'Suutus
**Noble (Dragon) Names:**
* Seytthix the Willful
* Saseezex the Enervated
* Sahkzis the Miserly
+7
View File
@@ -0,0 +1,7 @@
"""
Draconic
"""
from .base import Language
from .names import Name, NobleName
__all__ = [Language, Name, NobleName]
+47
View File
@@ -0,0 +1,47 @@
from language import types
from .rules import rules
vowels = types.equal_weights(
["sa", "saa", "sah", "se", "see", "sei", "sey", "si", "sii", "sir", "so", "su", "suu"], 1.0, blank=False
)
consonants = types.WeightedSet(
("d", 1.0),
("f", 0.5),
("g", 0.5),
("h", 1.0),
("j", 1.0),
("k", 1.0),
("l", 0.3),
("n", 0.3),
("r", 0.2),
("t", 1.0),
("v", 1.0),
("x", 1.0),
("y", 1.0),
("z", 1.0),
)
class DraconicLanguage(types.Language):
stops = types.equal_weights(["'"], 1.0)
def get_grapheme_vowel(self) -> str:
return self.stops.random() + self.vowels.random() + self.stops.random()
Language = DraconicLanguage(
name="draconic",
vowels=vowels,
consonants=consonants,
prefixes=None,
suffixes=None,
syllables=types.SyllableSet(
(types.Syllable(template="consonant|vowel") * 2, 0.2),
(types.Syllable(template="consonant|vowel") * 3, 1.0),
(types.Syllable(template="consonant|vowel") * 4, 1.0),
),
rules=rules,
minimum_grapheme_count=2,
)
+88
View File
@@ -0,0 +1,88 @@
from language import defaults, types
from language.languages.draconic import Language
# dragon_titles = types.equal_weights([
# ], 1.0)
class DraconicNameGenerator(types.NameGenerator):
def __init__(self):
super().__init__(
language=Language,
templates=types.NameSet(
(types.NameTemplate("name"), 1.0),
(types.NameTemplate("adjective,name"), 1.0),
),
adjectives=defaults.adjectives,
)
self.language.minimum_grapheme_count = 2
self.suffixes = types.equal_weights(["us", "ius", "eus", "a", "an", "is"], 1.0, blank=False)
def get_name(self) -> str:
return super().get_name() + self.suffixes.random()
class NobleDraconicNameGenerator(types.NameGenerator):
def __init__(self):
super().__init__(
language=Language,
templates=types.NameSet(
(types.NameTemplate("surname,the,title"), 1.0),
),
# titles=dragon_titles,
syllables=types.SyllableSet(
(types.Syllable(template="consonant|vowel") * 2, 1.0),
),
)
self.language.minimum_grapheme_count = 2
self.suffixes = types.equal_weights(
[
"thus",
"thux",
"thas",
"thax",
"this",
"thix",
"thes",
"thex",
"xus",
"xux",
"xas",
"xax",
"xis",
"xix",
"xes",
"xex",
"ssus",
"ssux",
"ssas",
"ssax",
"ssis",
"ssix",
"sses",
"ssex",
"zus",
"zux",
"zas",
"zax",
"zis",
"zix",
"zes",
"zex",
],
1.0,
blank=False,
)
def get_title(self) -> str:
p = ""
while not p:
p = defaults.personality.random()
return p
def get_surname(self) -> str:
return super().get_name().replace("'", "").title() + self.suffixes.random()
Name = DraconicNameGenerator()
NobleName = NobleDraconicNameGenerator()
+7
View File
@@ -0,0 +1,7 @@
import logging
from language.rules import default_rules
logger = logging.getLogger("draconic-rules")
rules = default_rules
+14
View File
@@ -0,0 +1,14 @@
### Dwarvish
Dwarvish words are short, sharp, and to the point. Much like the Dwarves
themselves. This is "Low Dwarvish"; an ancient form of High Dwarvish still
exists in some regions, notably the Dewa Q'Asos area of southern Vosh, but it
is considered "dead" by most scholars and is reserved for arcane legal
contracts (and those signifying a high social status).
*Dowêchâ khâ, natu phe tu, futê dêdachî pê she wu?*
**Dwarvish Names:**
* "Black" Yzh Se
* Ka Shidothir
* Zhâ Syzhon
+7
View File
@@ -0,0 +1,7 @@
"""
Dwarvish
"""
from .base import Language
from .names import Name, NobleName
__all__ = [Language, Name, NobleName]
+54
View File
@@ -0,0 +1,54 @@
from language import types
from .rules import rules
vowels = types.WeightedSet(
("a", 1.0),
("e", 1.0),
("i", 0.3),
("o", 0.8),
("u", 0.7),
("y", 0.3),
("j", 0.05),
("î", 0.3),
("ê", 1.0),
("â", 1.0),
("û", 1.0),
)
consonants = types.WeightedSet(
("b", 0.3),
("c", 0.5),
("d", 1.0),
("f", 0.5),
("k", 1.0),
("l", 0.3),
("m", 0.3),
("n", 0.3),
("p", 1.0),
("s", 1.0),
("t", 1.0),
("v", 0.5),
("w", 0.5),
("y", 0.3),
("ph", 1.0),
("th", 1.0),
("ch", 1.0),
("kh", 1.0),
("zh", 1.0),
("sh", 1.0),
)
Language = types.Language(
name="dwarvish",
vowels=vowels,
consonants=consonants,
prefixes=None,
suffixes=None,
syllables=types.SyllableSet(
(types.Syllable(template="consonant,vowel|consonant") * 1, 1.0),
(types.Syllable(template="consonant,vowel|consonant") * 2, 0.5),
(types.Syllable(template="consonant,vowel|consonant") * 3, 0.2),
),
rules=rules,
minimum_grapheme_count=1,
)
+25
View File
@@ -0,0 +1,25 @@
from language import defaults, types
from language.languages.dwarvish import Language
class DwarvishNameGenerator(types.NameGenerator):
def __init__(self):
super().__init__(
language=Language,
templates=types.NameSet(
# (types.NameTemplate("adjective,name,nickname,surname"), 1.0),
(types.NameTemplate("adjective,name,surname"), 1.0),
),
affixes=None,
adjectives=defaults.adjectives,
titles=defaults.titles,
)
self.language.minimum_grapheme_count = 2
self.suffixes = types.equal_weights(["son", "sson", "zhon", "dottir", "dothir", "dottyr"], 1.0)
def get_surname(self) -> str:
return super().get_surname() + self.suffixes.random()
Name = DwarvishNameGenerator()
NobleName = Name
+24
View File
@@ -0,0 +1,24 @@
import logging
import re
from language.rules import default_rules
from language.types import Language
logger = logging.getLogger("dwarvish-rules")
def cannot_start_with_repeated_consonants(language: Language, word: str) -> bool:
found = re.compile(r"(^[bcdfghklmnpqrstvwxz]{2})").search(word)
if not found:
return True
first, second = found.group(1)
if first == second:
logger.debug(f"{word} starts with a repeated consonant.")
return False
return True
rules = default_rules
rules.add(cannot_start_with_repeated_consonants)
+20
View File
@@ -0,0 +1,20 @@
### Elvish
> The Elvish tongue flows like a river and pools upon the lips like sweetest wine. -- Anwin am Vakaralithien
Elvish is an ornate, multisyllabic language featuring a complex and nuanced set
of vowel sounds that can take non-native speakers decades to master. Surnames
are typically derived from place names; the more noble the name, the more
baroque its construction.
*Aaclysmior uewhön fyaeiath nöagefniath iedyglion ryavies, eamwien acyöe, uëstr udrocrem!*
**Elvish Names:**
* Afydyioth um Envyien
* Wjillias am Aofll
* Anyir um Edyoieth
**Noble Elvish Names:**
* Uebs Sviiies um Udjsmierios
* Nytyuia Oudryhn um Ioltier
* Emyea Whonoel an Ofyediar
+7
View File
@@ -0,0 +1,7 @@
"""
Elvish
"""
from .base import Language
from .names import Name, NobleName
__all__ = [Language, Name, NobleName]
+129
View File
@@ -0,0 +1,129 @@
from language import types
from .rules import rules
vowels = types.WeightedSet(
("a", 1.0),
("e", 1.0),
("i", 0.3),
("o", 0.8),
("u", 0.7),
("y", 0.3),
("j", 0.05),
("ä", 0.1),
("ë", 0.1),
("ö", 0.1),
("ü", 0.1),
)
consonants = types.WeightedSet(
("b", 0.5),
("c", 0.3),
("d", 0.3),
("f", 0.4),
("g", 0.3),
("h", 0.5),
("k", 0.05),
("l", 1.0),
("m", 1.0),
("n", 1.0),
("p", 0.5),
("r", 1.0),
("s", 1.0),
("t", 0.7),
("v", 0.3),
("w", 0.2),
("y", 0.3),
) + types.equal_weights(
[
"ch",
"cl",
"cr",
"cw",
"cy",
"dh",
"dj",
"dr",
"dw",
"dy",
"fl",
"fn",
"fr",
"fw",
"fy",
"gl",
"ll",
"ly",
"ml",
"mw",
"my",
"ny",
"rh",
"ry",
"sh",
"sl",
"sm",
"sn",
"st",
"sv",
"sw",
"sy",
"th",
"tr",
"tw",
"ty",
"vy",
"wh",
"wr",
"wy",
"yh",
]
)
suffixes = types.equal_weights(
[
"a",
"i",
"e",
"t",
"s",
"m",
"n",
"l",
"r",
"d",
"a",
"th",
"ss",
"ieth",
"ies",
"ier",
"ien",
"iath",
"ias",
"iar",
"ian",
"ioth",
"ios",
"ior",
"ion",
],
weight=1.0,
)
Language = types.Language(
name="elvish",
vowels=vowels,
consonants=consonants,
prefixes=None,
suffixes=suffixes,
syllables=types.SyllableSet(
(types.Syllable(template="vowel|consonant") * 3, 0.4),
(types.Syllable(template="vowel|consonant") * 3, 0.5),
(types.Syllable(template="vowel|consonant") * 4, 1.0),
(types.Syllable(template="vowel|consonant") * 5, 0.5),
(types.Syllable(template="vowel|consonant") * 6, 0.1),
(types.Syllable(template="vowel|consonant") * 7, 0.1),
),
rules=rules,
minimum_grapheme_count=3,
)
+77
View File
@@ -0,0 +1,77 @@
from language import defaults, types
from language.languages.elvish import Language
from language.languages.elvish.base import suffixes
PlaceName = types.NameGenerator(
language=Language,
syllables=types.SyllableSet(
(types.Syllable(template="vowel,vowel|consonant,vowel|consonant"), 1.0),
(types.Syllable(template="consonant,vowel|consonant,vowel|consonant"), 0.3),
),
templates=types.NameSet(
(types.NameTemplate("affix,name"), 1.0),
),
affixes=types.WeightedSet(("el", 1.0)),
adjectives=defaults.adjectives,
suffixes=suffixes,
)
class ElvishNameGenerator(types.NameGenerator):
def __init__(self):
super().__init__(
language=Language,
syllables=Language.syllables,
templates=types.NameSet(
(types.NameTemplate("name,affix,surname"), 1.0),
),
affixes=types.equal_weights(["am", "an", "al", "um"], weight=1.0, blank=False),
adjectives=defaults.adjectives,
titles=defaults.titles,
suffixes=suffixes,
)
self.language.minimum_grapheme_count = 2
self.place_generator = PlaceName
def get_surname(self) -> str:
return self.place_generator.name()[0]["name"][0]
class NobleElvishNameGenerator(types.NameGenerator):
def __init__(self):
super().__init__(
language=Language,
syllables=Language.syllables,
templates=types.NameSet(
(types.NameTemplate("name,name,affix,surname"), 1.0),
),
affixes=types.equal_weights(["am", "an", "al", "um"], weight=1.0, blank=False),
adjectives=defaults.adjectives,
titles=defaults.titles,
)
self.language.minimum_grapheme_count = 2
self.place_generator = PlaceName
self.suffixes = types.equal_weights(
[
"ieth",
"ies",
"ier",
"ien",
"iath",
"ias",
"iar",
"ian",
"ioth",
"ios",
"ior",
"ion",
],
1.0,
)
def get_surname(self) -> str:
return self.place_generator.name()[0]["name"][0] + self.suffixes.random()
Name = ElvishNameGenerator()
NobleName = NobleElvishNameGenerator()
+109
View File
@@ -0,0 +1,109 @@
import logging
import re
from language.types import Language
logger = logging.getLogger("elvish-rules")
permitted_starting_clusters = [
"ch",
"cl",
"cr",
"cw",
"cy",
"dh",
"dj",
"dr",
"dw",
"dy",
"fl",
"fn",
"fr",
"fw",
"fy",
"gl",
"ll",
"ly",
"ml",
"mw",
"my",
"ny",
"rh",
"ry",
"sh",
"sl",
"sm",
"sn",
"st",
"sv",
"sw",
"sy",
"th",
"tr",
"tw",
"ty",
"vy",
"wh",
"wr",
"wy",
"yh",
]
def cannot_start_with_two_consonants(language: Language, word: str) -> bool:
found = re.compile(r"(^[bcdfghklmnpqrstvwxz]{2})").search(word)
if not found:
return True
first, second = found.group(1)
if first == second and first != "l":
logger.debug(f"{word} starts with a repeated consonant.")
return False
if found.group(1) not in permitted_starting_clusters:
logger.debug("f{word} cannot start with {first}{second}")
return False
return True
def too_many_vowels(language: Language, word: str) -> bool:
found = re.compile(r"[" + "".join(language.vowels.members) + "]{4}").findall(word)
if found == []:
return True
logger.debug(f"{word} has too many contiguous vowels: {found}")
return False
def too_many_consonants(language: Language, word: str) -> bool:
found = re.compile(r"[bcdfghklmnprstvw]{3}").findall(word)
if found == []:
return True
logger.debug(f"{word} has too many contiguous consonants: {found}")
return False
def cannot_have_just_repeated_vowels(language: Language, word: str) -> bool:
if len(word) == 1:
return True
uniq = {letter for letter in word}
if len(uniq) > 1:
return True
logger.debug(f"{word} consists of only one repeated letter.")
return False
def must_have_a_vowel(language: Language, word: str) -> bool:
for vowel in language.vowels.members:
if vowel in word:
return True
logger.debug(f"{word} does not contain a vowel.")
return False
rules = {
must_have_a_vowel,
too_many_vowels,
too_many_consonants,
cannot_have_just_repeated_vowels,
cannot_start_with_two_consonants,
}
+13
View File
@@ -0,0 +1,13 @@
### Gnomish
Several distinct dialects of Gnomish exist; a provincial Rock Gnome may not
understand a Forest Gnome at all! This is "Golden Gnomish," the mother tongue
of these localized variants, and functions much like Common -- a trader's
tongue of common understanding.
*Nuio duy lao guey houe lo nouo, pia roa tai.*
**Gnomish Names:**
* Fuey Voii
* Juio See
* Tuai Reaa
+7
View File
@@ -0,0 +1,7 @@
"""
Gnomish
"""
from .base import Language
from .names import Name, NobleName
__all__ = [Language, Name, NobleName]
+48
View File
@@ -0,0 +1,48 @@
from language import defaults, types
from .rules import rules
consonants = types.WeightedSet(
("b", 0.5),
("d", 0.5),
("f", 0.3),
("g", 0.3),
("h", 0.5),
("j", 0.2),
("l", 1.0),
("m", 0.5),
("n", 1.0),
("p", 0.5),
("r", 1.0),
("s", 1.0),
("t", 1.0),
("v", 0.3),
("w", 0.2),
("z", 0.1),
)
suffixes = types.equal_weights(
[
"a",
"e",
"i",
"o",
"y",
],
1.0,
blank=False,
)
Language = types.Language(
name="gnomish",
vowels=defaults.vowels,
consonants=consonants,
prefixes=None,
suffixes=suffixes,
syllables=types.SyllableSet(
(types.Syllable(template="consonant,vowel,vowel"), 1.0),
(types.Syllable(template="consonant,vowel"), 0.33),
),
rules=rules,
minimum_grapheme_count=2,
)
+10
View File
@@ -0,0 +1,10 @@
from language import types
from language.languages.gnomish import Language
Name = types.NameGenerator(
language=Language,
templates=types.NameSet(
(types.NameTemplate("name,surname"), 1.0),
),
)
NobleName = Name
+7
View File
@@ -0,0 +1,7 @@
import logging
from language.rules import default_rules
logger = logging.getLogger()
rules = default_rules
+13
View File
@@ -0,0 +1,13 @@
### Halfling
Halfings speak a trilling, lilting language with meaning and inflection rooted
in meter. It has proven to be nearly impossisble for most non-Halflings to
master, to the point that most cosmopolitan Halfings choose a Common nickname
by which they are known.
*Siheme'li'ba'e lo'neno'ne'o, mo'tepa'ga'i wezyropi'a lo'se'ni'metyo zevypiwee vyta'mo'a, da'na'jo'lee ti'soe fe'soe!*
**Halfling Names:**
* Hi'rina'pa'no'y Nylao Pe'lawyle'lo'a "Temperate"
* Jo'la'rogi'o Mi'li're'ne'pe'y Terinite've'e "Wise"
* Sa'la're'ta'je'o Re'dya Fetobo'a "Meritorious"
+7
View File
@@ -0,0 +1,7 @@
"""
Halfling
"""
from .base import Language
from .names import Name, NobleName
__all__ = [Language, Name, NobleName]
+54
View File
@@ -0,0 +1,54 @@
from language import types
from .rules import rules
vowels = types.equal_weights(["a'", "e'", "i'", "o'"], 1.0, blank=False) + types.equal_weights(
["a", "e", "i", "o", "y"], 0.5, blank=False
)
consonants = types.WeightedSet(
("b", 0.5),
("d", 0.5),
("f", 0.3),
("g", 0.3),
("h", 0.5),
("j", 0.2),
("l", 1.0),
("m", 0.5),
("n", 1.0),
("p", 0.5),
("r", 1.0),
("s", 1.0),
("t", 1.0),
("v", 0.3),
("w", 0.2),
("z", 0.1),
)
suffixes = types.equal_weights(
[
"a",
"e",
"i",
"o",
"y",
],
1.0,
blank=False,
)
Language = types.Language(
name="halfling",
vowels=vowels,
consonants=consonants,
prefixes=None,
suffixes=suffixes,
syllables=types.SyllableSet(
(types.Syllable(template="consonant,vowel") * 2, 0.5),
(types.Syllable(template="consonant,vowel") * 3, 0.75),
(types.Syllable(template="consonant,vowel") * 4, 1.0),
(types.Syllable(template="consonant,vowel") * 5, 1.0),
),
rules=rules,
minimum_grapheme_count=2,
)
+17
View File
@@ -0,0 +1,17 @@
from language import defaults, types
from language.languages.halfling import Language
class HalflingNameGenerator(types.NameGenerator):
def get_name(self) -> str:
return super().get_name().lower().capitalize()
Name = HalflingNameGenerator(
language=Language,
nicknames=defaults.positive_adjectives,
templates=types.NameSet(
(types.NameTemplate("name,name,name,nickname"), 1.0),
),
)
NobleName = Name
+7
View File
@@ -0,0 +1,7 @@
import logging
from language.rules import default_rules
logger = logging.getLogger()
rules = default_rules
+19
View File
@@ -0,0 +1,19 @@
### Infernal
The language of devils is a collection of gutteral barks that is unmistakable,
and unmistakably terrifying, to the ear of common folk. Traditional names
typically begin with the name of the ruler of whatever circle of Hell the devil
calls home, but some Tieflings (and others of Infernal origin) reject their
Infernal legacies and choose less noble names.
*Z'zait cocf k'd z'xz t'ugoo! Yipd k'j x'euy, z'edu k'oczu!*
**Infernal Names:**
* P'Utufus
* Golden Bdaeus
* Timeless Ujius
**Noble Infernal Names:**
* Mephistopheles T'Xieis
* Asmodeus T'Opakan
* Mammon K'Nusan
+7
View File
@@ -0,0 +1,7 @@
"""
Infernal
"""
from .base import Language
from .names import Name, NobleName
__all__ = [Language, Name, NobleName]
+52
View File
@@ -0,0 +1,52 @@
from language import defaults, types
from .rules import rules
consonants = types.WeightedSet(
("b", 1.0),
("c", 1.0),
("d", 1.0),
("f", 0.5),
("g", 0.5),
("j", 1.0),
("k", 1.0),
("l", 0.3),
("m", 0.3),
("n", 0.3),
("p", 1.0),
("r", 0.2),
("s", 0.1),
("t", 1.0),
("v", 1.0),
("x", 1.0),
("y", 1.0),
("z", 1.0),
)
prefixes = types.equal_weights(
[
"t'",
"x'",
"k'",
"p'",
"z'",
],
0.5,
)
Language = types.Language(
name="infernal",
vowels=defaults.vowels,
consonants=consonants,
prefixes=prefixes,
suffixes=None,
syllables=types.SyllableSet(
(types.Syllable(template="consonant|vowel") * 2, 0.05),
(types.Syllable(template="consonant|vowel") * 3, 1.0),
(types.Syllable(template="consonant|vowel") * 4, 0.75),
(types.Syllable(template="consonant|vowel") * 5, 0.5),
(types.Syllable(template="consonant|vowel") * 6, 0.25),
),
rules=rules,
minimum_grapheme_count=2,
)
+87
View File
@@ -0,0 +1,87 @@
from language import types
from language.languages.infernal import Language
adjectives = types.equal_weights(
[
"eternal",
"wondrous",
"luminous",
"perfect",
"essential",
"golden",
"unfailing",
"perpetual",
"infinite",
"exquisite",
"sinless",
"ultimate",
"flawless",
"timeless",
"glorious",
"absolute",
"boundless",
"true",
"incredible",
"virtuous",
"supreme",
"enchanted",
"magnificent",
"superior",
"spectacular",
"divine",
],
0.25,
)
bloodlines = types.equal_weights(
[
"Asmodeus",
"Baalzebul",
"Rimmon",
"Dispater",
"Fierna",
"Glasya",
"Levistus",
"Mammon",
"Mephistopheles",
"Zariel",
]
)
class InfernalNameGenerator(types.NameGenerator):
def __init__(self):
super().__init__(
language=Language,
templates=types.NameSet(
# (types.NameTemplate("adjective,name,nickname,surname"), 1.0),
(types.NameTemplate("name"), 0.25),
(types.NameTemplate("adjective,name"), 0.25),
),
adjectives=adjectives,
)
self.language.minimum_grapheme_count = 2
self.suffixes = types.equal_weights(["us", "ius", "eus", "a", "an", "is"], 1.0, blank=False)
def get_name(self) -> str:
return super().get_name() + self.suffixes.random()
class NobleInfernalNameGenerator(types.NameGenerator):
def __init__(self):
super().__init__(
language=Language,
templates=types.NameSet(
(types.NameTemplate("adjective,name"), 1.0),
),
adjectives=bloodlines,
)
self.language.minimum_grapheme_count = 2
self.suffixes = types.equal_weights(["us", "ius", "to", "tro", "eus", "a", "an", "is"], 1.0, blank=False)
def get_name(self) -> str:
return super().get_name() + self.suffixes.random()
Name = InfernalNameGenerator()
NobleName = NobleInfernalNameGenerator()
+7
View File
@@ -0,0 +1,7 @@
import logging
from language.rules import default_rules
logger = logging.getLogger("infernal-rules")
rules = default_rules
+14
View File
@@ -0,0 +1,14 @@
### Lizardfolk
The Lizardfolk language is a unique form of telepathy borne by pheramonne
exchange. It is literally impossible for non-Lizardfolk to speak it, though
with practice they can learn to recognize particular smells. Lizardfolk names
are collections of scent pattterns, the first of which denotes the Lizardfolk's
family origins.
*[No written language]*
**Lizardfolk Name Scents:**
* Carmelized Roast Pomegranate
* Rotting Hazelnut Citric
* Pepper Broth Grape
@@ -0,0 +1,7 @@
"""
Lizardfolk
"""
from .base import Language
from .names import Name, NobleName
__all__ = [Language, Name, NobleName]
+118
View File
@@ -0,0 +1,118 @@
from language import types
scents = types.equal_weights(
[
"honey",
"caramel",
"maple syrup",
"molasses",
"dark chocolate",
"chocolate",
"almond",
"hazelnut",
"peanut",
"clove",
"cinnamon",
"nutmeg",
"anise",
"malt",
"grain",
"roast",
"smoke",
"ash",
"acrid",
"rubber",
"skunk",
"petroleum",
"medicine",
"salt",
"bitter",
"phrenolic",
"meat",
"broth",
"animal",
"musty",
"earth",
"mould",
"damp",
"wood",
"paper",
"cardboard",
"stale",
"herb",
"hay",
"grass",
"peapod",
"whisky",
"wine",
"malic",
"citric",
"isovaleric",
"butyric",
"acetic",
"lime",
"lemon",
"orange",
"grapefruit",
"pear",
"peach",
"apple",
"grape",
"pineapple",
"pomegranate",
"cherry",
"coconut",
"prune",
"raisin",
"strawberry",
"blueberry",
"raspberry",
"blackberry",
"jasmine",
"rose",
"camomile",
"tobacco",
],
1.0,
blank=False,
)
family = types.equal_weights(
[
"sweet",
"floral",
"fruity",
"sour",
"fermented",
"green",
"vegetal",
"old",
"roasted",
"spiced",
"nutty",
"cocoa",
"pepper",
"pungent",
"burnt",
"carmelized",
"raw",
"rotting",
"dead",
"young",
],
1.0,
blank=False,
)
Language = types.Language(
name="lizardfolk",
vowels=scents,
consonants=family,
prefixes=None,
suffixes=None,
syllables=types.SyllableSet(
(types.Syllable(template="vowel"), 1.0),
),
rules=(),
minimum_grapheme_count=1,
)
+19
View File
@@ -0,0 +1,19 @@
from language import types
from language.languages.lizardfolk import Language
class LizardfolkNameGenerator(types.NameGenerator):
def __init__(self):
super().__init__(
language=Language,
templates=types.NameSet(
(types.NameTemplate("surname,name,name"), 1.0),
),
)
def get_surname(self) -> str:
return self.language.consonants.random().title()
Name = LizardfolkNameGenerator()
NobleName = Name
+13
View File
@@ -0,0 +1,13 @@
### Orcish
Spoken by Full- and Half-Orcs alike, Orcish bears some similarlities to
Dwarvish's clipped, mono-syllablic construction. They differ in Half-Orc's
prominent use of sibilants, perhaps as a result of being spoken by people with
large, protruding tusks.
*Dbod hanot neb, hushi dupo shiza fesha keke fucha tpu.*
**Orcish Names:**
* "Mad" Nha Pazk
* Zashi Dizh
* Pik Decht
+7
View File
@@ -0,0 +1,7 @@
"""
Orcish
"""
from .base import Language
from .names import Name, NobleName
__all__ = [Language, Name, NobleName]
+36
View File
@@ -0,0 +1,36 @@
from language import defaults, types
from .rules import rules
consonants = types.WeightedSet(
("b", 1.0),
("c", 1.0),
("d", 1.0),
("f", 0.5),
("h", 1.0),
("k", 1.0),
("m", 0.3),
("n", 0.3),
("p", 1.0),
("r", 0.2),
("s", 0.1),
("t", 1.0),
("z", 1.0),
("ch", 1.0),
("sh", 0.7),
("br", 1.0),
)
Language = types.Language(
name="orcish",
vowels=defaults.vowels,
consonants=consonants,
prefixes=None,
suffixes=None,
syllables=types.SyllableSet(
(types.Syllable(template="consonant,vowel") * 2, 1.0),
(types.Syllable(template="consonant,vowel,consonant,vowel,consonant"), 0.5),
),
rules=rules,
minimum_grapheme_count=1,
)
+60
View File
@@ -0,0 +1,60 @@
from language import defaults, types
from language.languages.orcish import Language
class OrcishNameGenerator(types.NameGenerator):
def __init__(self):
super().__init__(
language=Language,
templates=types.NameSet(
# (types.NameTemplate("adjective,name,nickname,surname"), 1.0),
(types.NameTemplate("adjective,name,surname"), 1.0),
),
affixes=None,
adjectives=defaults.adjectives,
titles=defaults.titles,
)
self.language.minimum_grapheme_count = 2
self.suffixes = types.equal_weights(
[
"acht",
"echt",
"icht",
"ocht",
"ucht",
"ak",
"ek",
"ik",
"ok",
"uk",
"ach",
"ech",
"ich",
"och",
"uch",
"atch",
"etch",
"itch",
"otch",
"utch",
"azk",
"ezk",
"izk",
"ozk",
"uzk",
"azh",
"ezh",
"izh",
"ozh",
"uzh",
],
1.0,
blank=False,
)
def get_surname(self) -> str:
return self.language.add_grapheme(word="", template="consonant").strip().title() + self.suffixes.random()
Name = OrcishNameGenerator()
NobleName = Name
+24
View File
@@ -0,0 +1,24 @@
import logging
import re
from language.rules import default_rules
from language.types import Language
logger = logging.getLogger("orcish-rules")
def cannot_start_with_repeated_consonants(language: Language, word: str) -> bool:
found = re.compile(r"(^[bcdfghklmnpqrstvwxz]{3})").search(word)
if not found:
return True
first, second, third = found.group(1)
if first == second == third:
logger.debug(f"{word} starts with a repeated consonant.")
return False
return True
rules = default_rules
rules.add(cannot_start_with_repeated_consonants)
+18
View File
@@ -0,0 +1,18 @@
### Undercommon
The language of the Drow is the defacto language of the Underdark, and is
spoken by most peoples there. Like the Drow themselves, Undercommon diverged
from an Elvish dialect in ancient times. The two still bear some resemblance,
notably in the construction of names.
*Okvösaa licopod lohiwia uüyötüe uywubit uegäsit uäpisoa, caquyee redoxöd, mraomoa.*
**Undercommon Names:**
* Tfubam Umaiuhüen
* Nanosd Aneedaöbaas
* Rerwus Amzuremolr
**Noble Undercommon (Drow) Names:**
* Uopiyie Alzejakäurn
* Opyäulol Elchöläss
* Iqyäuwed Anlcoduir
@@ -0,0 +1,7 @@
"""
Undercommon
"""
from .base import Language
from .names import Name, NobleName
__all__ = [Language, Name, NobleName]
+21
View File
@@ -0,0 +1,21 @@
from language import defaults, types
from .rules import rules
vowels = defaults.vowels + types.equal_weights(["ä", "ö", "ü", "äu"], 0.5, blank=False)
prefixes = defaults.vowels + types.equal_weights(["c", "g", "l", "m", "n", "r", "s", "t", "v", "z"], 1.0, blank=False)
suffixes = types.equal_weights(["a", "e", "i", "t", "s", "m", "n", "l", "r", "d", "a", "th"], 1.0, blank=False)
Language = types.Language(
name="undercommon",
vowels=vowels,
consonants=defaults.consonants,
prefixes=prefixes,
suffixes=suffixes,
syllables=types.SyllableSet(
(types.Syllable(template="vowel,consonant,vowel") * 2, 0.15),
(types.Syllable(template="consonant|vowel,consonant,vowel,consonant,vowel"), 1.0),
),
rules=rules,
minimum_grapheme_count=2,
)
+37
View File
@@ -0,0 +1,37 @@
import random
from language import defaults, types
from language.languages.undercommon import Language
PlaceName = types.NameGenerator(
language=Language,
syllables=Language.syllables,
templates=types.NameSet(
(types.NameTemplate("affix,name"), 1.0),
),
affixes=types.WeightedSet(("el", 1.0)),
adjectives=defaults.adjectives,
suffixes=Language.suffixes,
)
class DrowName(types.NameGenerator):
def __init__(self):
super().__init__(
language=Language,
syllables=Language.syllables,
templates=types.NameSet(
(types.NameTemplate("name,surname"), 1.0),
),
)
self.language.minimum_grapheme_count = 2
self.place_generator = PlaceName
self.affixes = types.equal_weights(["am", "an", "al", "um"], weight=1.0, blank=False)
def get_surname(self) -> str:
name = self.place_generator.name()[0]["name"][0]
return (self.affixes.random() + name + random.choice(["th", "s", "r", "n"])).title()
Name = DrowName()
NobleName = Name
+88
View File
@@ -0,0 +1,88 @@
import logging
import re
from language.rules import default_rules
from language.types import Language
logger = logging.getLogger()
valid_consonant_sequences = [
"cc",
"ht",
"kd",
"kl",
"km",
"kp",
"kt",
"kv",
"kw",
"ky",
"lc",
"ld",
"lf",
"ll",
"lm",
"lp",
"lt",
"lv",
"lw",
"ly",
"mb",
"mm",
"mp",
"my",
"nc",
"nd",
"ng",
"nn",
"nt",
"nw",
"ny",
"ps",
"pt",
"rc",
"rd",
"rm",
"rn",
"rp",
"rr",
"rs",
"rt",
"rw",
"ry",
"sc",
"ss",
"ts",
"tt",
"th",
"tw",
"ty",
]
def valid_sequences(language: Language, word: str) -> bool:
found = re.compile(r"([bcdfghjklmnpqrstvwxz]{2})").findall(word)
if not found:
return True
invalid = [seq for seq in found if seq not in valid_consonant_sequences]
if invalid:
logger.debug(f"{word} contains invalid consonant sequences: {invalid}")
return False
return True
def too_many_vowels(language: Language, word: str) -> bool:
found = re.compile(r"[" + "".join(language.vowels.members) + r"]{3}").findall(word)
if found == []:
return True
logger.debug(f"{word} has too many contiguous vowels: {found}")
return False
rules = default_rules.union(
{
valid_sequences,
too_many_vowels,
}
)