From 53433bfbd232d671ed1854eb4c9f174e3ecdaf76 Mon Sep 17 00:00:00 2001 From: skylord-a52 Date: Wed, 16 Nov 2022 00:50:47 -0800 Subject: [PATCH] Allows languages to weight the likelihood of certain syllables, refactors certain code related to pick_weight() (#71275) ## About The Pull Request This PR does the following: - Defines a new proc in __HELPERS/_lists.dm called `pick_weight_recursive()`. This is the code from `/obj/effect/spawner/random/` that allows for nested weighted lists, moved to its own proc. - Replaces explicit code in spawners/random.dm with calls to `pick_weight_recursive()` where appropriate - Deletes the redundant (and barely used) proc `/obj/item/loot_table_maker/proc/pick_loot`, as this was equivalent to `pick_weight_recursive()` - Moves the global proc `fill_with_ones()` from spawners/random.dm to __HELPERS/_lists.dm - Replaces `pick()` in language syllable selection with `pick_weight_recursive()`, allowing languages to define syllable weights or use nested lists of syllables. - Reformats Galactic Common to use nested lists of syllables, allowing English and Chinese syllables to occur at equal frequency despite having different numbers of each. ## Why It's Good For The Game Allowing languages to define syllable weights and nested groups of syllables is a relatively small change that greatly expands what you can do with them. In addition to making Galactic Common look nicer in code, this change also allows for the easy creation of languages with highly uneven syllable distributions (including ultra-rare secret syllables, perhaps) or the quick creation of pidgin languages that combine multiple syllable sets. Using a new proc simplifies spawner code by reducing repetition. Making it global allows for other code to easily implement the same flexible and elegant system of nested lists that spawners already use. ## Changelog :cl: refactor: defines a new global proc, pick_weight_recursive() code: languages can weight syllables, and galactic common's definition is easier to look at /:cl: Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com> --- code/__HELPERS/_lists.dm | 37 ++++++++ .../objects/effects/spawners/random/random.dm | 27 +----- code/modules/language/common.dm | 86 ++++++++----------- code/modules/language/language.dm | 4 +- 4 files changed, 77 insertions(+), 77 deletions(-) diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index 3cb39a1a5ac..f5c8c082f23 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -427,6 +427,43 @@ return null +/** + * Like pick_weight, but allowing for nested lists. + * + * For example, given the following list: + * list(A = 1, list(B = 1, C = 1)) + * A would have a 50% chance of being picked, + * and list(B, C) would have a 50% chance of being picked. + * If list(B, C) was picked, B and C would then each have a 50% chance of being picked. + * So the final probabilities would be 50% for A, 25% for B, and 25% for C. + * + * Weights should be integers. Entries without weights are assigned weight 1 (so unweighted lists can be used as well) + */ +/proc/pick_weight_recursive(list/list_to_pick) + var/result = pick_weight(fill_with_ones(list_to_pick)) + while(islist(result)) + result = pick_weight(fill_with_ones(result)) + return result + +/** + * Given a list, return a copy where values without defined weights are given weight 1. + * For example, fill_with_ones(list(A, B=2, C)) = list(A=1, B=2, C=1) + * Useful for weighted random choices (loot tables, syllables in languages, etc.) + */ +/proc/fill_with_ones(list/list_to_pad) + if (!islist(list_to_pad)) + return list_to_pad + + var/list/final_list = list() + + for (var/key in list_to_pad) + if (list_to_pad[key]) + final_list[key] = list_to_pad[key] + else + final_list[key] = 1 + + return final_list + /// Takes a weighted list (see above) and expands it into raw entries /// This eats more memory, but saves time when actually picking from it /proc/expand_weights(list/list_to_pick) diff --git a/code/game/objects/effects/spawners/random/random.dm b/code/game/objects/effects/spawners/random/random.dm index ece6656b4fa..e2977c540f6 100644 --- a/code/game/objects/effects/spawners/random/random.dm +++ b/code/game/objects/effects/spawners/random/random.dm @@ -53,9 +53,7 @@ if(loot?.len) var/loot_spawned = 0 while((spawn_loot_count-loot_spawned) && loot.len) - var/lootspawn = pick_weight(fill_with_ones(loot)) - while(islist(lootspawn)) - lootspawn = pick_weight(fill_with_ones(lootspawn)) + var/lootspawn = pick_weight_recursive(loot) if(!spawn_loot_double) loot.Remove(lootspawn) if(lootspawn && (spawn_scatter_radius == 0 || spawn_locations.len)) @@ -124,7 +122,7 @@ var/lootpool = spawner_to_table.loot qdel(spawner_to_table) for(var/i in 1 to loot_count) - var/loot_spawn = pick_loot(lootpool) + var/loot_spawn = pick_weight_recursive(lootpool) if(!(loot_spawn in spawned_table)) spawned_table[loot_spawn] = 1 else @@ -132,24 +130,3 @@ stat_table += spawned_table for(var/item in stat_table) stat_table[item] /= loot_count - -/obj/item/loot_table_maker/proc/pick_loot(lootpool) //selects path from loot table and returns it - var/lootspawn = pick_weight(fill_with_ones(lootpool)) - while(islist(lootspawn)) - lootspawn = pick_weight(fill_with_ones(lootspawn)) - return lootspawn - -// Lets loot tables be both list(a, b, c), as well as list(a = 3, b = 2, c = 2) -/proc/fill_with_ones(list/table) - if (!islist(table)) - return table - - var/list/final_table = list() - - for (var/key in table) - if (table[key]) - final_table[key] = table[key] - else - final_table[key] = 1 - - return final_table diff --git a/code/modules/language/common.dm b/code/modules/language/common.dm index e030c2bb6f4..2dc7294983c 100644 --- a/code/modules/language/common.dm +++ b/code/modules/language/common.dm @@ -18,53 +18,39 @@ http://www.chinahighlights.com/travelguide/learning-chinese/pinyin-syllables.htm */ /datum/language/common/syllables = list( -"a", "ai", "an", "ang", "ao", "ba", "bai", "ban", "bang", "bao", "bei", "ben", "beng", "bi", "bian", "biao", -"bie", "bin", "bing", "bo", "bu", "ca", "cai", "can", "cang", "cao", "ce", "cei", "cen", "ceng", "cha", "chai", -"chan", "chang", "chao", "che", "chen", "cheng", "chi", "chong", "chou", "chu", "chua", "chuai", "chuan", "chuang", "chui", "chun", -"chuo", "ci", "cong", "cou", "cu", "cuan", "cui", "cun", "cuo", "da", "dai", "dan", "dang", "dao", "de", "dei", -"den", "deng", "di", "dian", "diao", "die", "ding", "diu", "dong", "dou", "du", "duan", "dui", "dun", "duo", "e", -"ei", "en", "er", "fa", "fan", "fang", "fei", "fen", "feng", "fo", "fou", "fu", "ga", "gai", "gan", "gang", -"gao", "ge", "gei", "gen", "geng", "gong", "gou", "gu", "gua", "guai", "guan", "guang", "gui", "gun", "guo", "ha", -"hai", "han", "hang", "hao", "he", "hei", "hen", "heng", "hm", "hng", "hong", "hou", "hu", "hua", "huai", "huan", -"huang", "hui", "hun", "huo", "ji", "jia", "jian", "jiang", "jiao", "jie", "jin", "jing", "jiong", "jiu", "ju", "juan", -"jue", "jun", "ka", "kai", "kan", "kang", "kao", "ke", "kei", "ken", "keng", "kong", "kou", "ku", "kua", "kuai", -"kuan", "kuang", "kui", "kun", "kuo", "la", "lai", "lan", "lang", "lao", "le", "lei", "leng", "li", "lia", "lian", -"liang", "liao", "lie", "lin", "ling", "liu", "long", "lou", "lu", "luan", "lun", "luo", "ma", "mai", "man", "mang", -"mao", "me", "mei", "men", "meng", "mi", "mian", "miao", "mie", "min", "ming", "miu", "mo", "mou", "mu", "na", -"nai", "nan", "nang", "nao", "ne", "nei", "nen", "neng", "ng", "ni", "nian", "niang", "niao", "nie", "nin", "ning", -"niu", "nong", "nou", "nu", "nuan", "nuo", "o", "ou", "pa", "pai", "pan", "pang", "pao", "pei", "pen", "peng", -"pi", "pian", "piao", "pie", "pin", "ping", "po", "pou", "pu", "qi", "qia", "qian", "qiang", "qiao", "qie", "qin", -"qing", "qiong", "qiu", "qu", "quan", "que", "qun", "ran", "rang", "rao", "re", "ren", "reng", "ri", "rong", "rou", -"ru", "rua", "ruan", "rui", "run", "ruo", "sa", "sai", "san", "sang", "sao", "se", "sei", "sen", "seng", "sha", -"shai", "shan", "shang", "shao", "she", "shei", "shen", "sheng", "shi", "shou", "shu", "shua", "shuai", "shuan", "shuang", "shui", -"shun", "shuo", "si", "song", "sou", "su", "suan", "sui", "sun", "suo", "ta", "tai", "tan", "tang", "tao", "te", -"teng", "ti", "tian", "tiao", "tie", "ting", "tong", "tou", "tu", "tuan", "tui", "tun", "tuo", "wa", "wai", "wan", -"wang", "wei", "wen", "weng", "wo", "wu", "xi", "xia", "xian", "xiang", "xiao", "xie", "xin", "xing", "xiong", "xiu", -"xu", "xuan", "xue", "xun", "ya", "yan", "yang", "yao", "ye", "yi", "yin", "ying", "yong", "you", "yu", "yuan", -"yue", "yun", "za", "zai", "zan", "zang", "zao", "ze", "zei", "zen", "zeng", "zha", "zhai", "zhan", "zhang", "zhao", -"zhe", "zhei", "zhen", "zheng", "zhi", "zhong", "zhou", "zhu", "zhua", "zhuai", "zhuan", "zhuang", "zhui", "zhun", "zhuo", "zi", -"zong", "zou", "zuan", "zui", "zun", "zuo", "zu", -"al", "an", "ar", "as", "at", "ea", "ed", "en", "er", "es", "ha", "he", "hi", "in", "is", "it", -"le", "me", "nd", "ne", "ng", "nt", "on", "or", "ou", "re", "se", "st", "te", "th", "ti", "to", -"ve", "wa", "all", "and", "are", "but", "ent", "era", "ere", "eve", "for", "had", "hat", "hen", "her", "hin", -"his", "ing", "ion", "ith", "not", "ome", "oul", "our", "sho", "ted", "ter", "tha", "the", "thi", -"al", "an", "ar", "as", "at", "ea", "ed", "en", "er", "es", "ha", "he", "hi", "in", "is", "it", -"le", "me", "nd", "ne", "ng", "nt", "on", "or", "ou", "re", "se", "st", "te", "th", "ti", "to", -"ve", "wa", "all", "and", "are", "but", "ent", "era", "ere", "eve", "for", "had", "hat", "hen", "her", "hin", -"his", "ing", "ion", "ith", "not", "ome", "oul", "our", "sho", "ted", "ter", "tha", "the", "thi", -"al", "an", "ar", "as", "at", "ea", "ed", "en", "er", "es", "ha", "he", "hi", "in", "is", "it", -"le", "me", "nd", "ne", "ng", "nt", "on", "or", "ou", "re", "se", "st", "te", "th", "ti", "to", -"ve", "wa", "all", "and", "are", "but", "ent", "era", "ere", "eve", "for", "had", "hat", "hen", "her", "hin", -"his", "ing", "ion", "ith", "not", "ome", "oul", "our", "sho", "ted", "ter", "tha", "the", "thi", -"al", "an", "ar", "as", "at", "ea", "ed", "en", "er", "es", "ha", "he", "hi", "in", "is", "it", -"le", "me", "nd", "ne", "ng", "nt", "on", "or", "ou", "re", "se", "st", "te", "th", "ti", "to", -"ve", "wa", "all", "and", "are", "but", "ent", "era", "ere", "eve", "for", "had", "hat", "hen", "her", "hin", -"his", "ing", "ion", "ith", "not", "ome", "oul", "our", "sho", "ted", "ter", "tha", "the", "thi", -"al", "an", "ar", "as", "at", "ea", "ed", "en", "er", "es", "ha", "he", "hi", "in", "is", "it", -"le", "me", "nd", "ne", "ng", "nt", "on", "or", "ou", "re", "se", "st", "te", "th", "ti", "to", -"ve", "wa", "all", "and", "are", "but", "ent", "era", "ere", "eve", "for", "had", "hat", "hen", "her", "hin", -"his", "ing", "ion", "ith", "not", "ome", "oul", "our", "sho", "ted", "ter", "tha", "the", "thi", -"al", "an", "ar", "as", "at", "ea", "ed", "en", "er", "es", "ha", "he", "hi", "in", "is", "it", -"le", "me", "nd", "ne", "ng", "nt", "on", "or", "ou", "re", "se", "st", "te", "th", "ti", "to", -"ve", "wa", "all", "and", "are", "but", "ent", "era", "ere", "eve", "for", "had", "hat", "hen", "her", "hin", -"his", "ing", "ion", "ith", "not", "ome", "oul", "our", "sho", "ted", "ter", "tha", "the", "thi") + // each sublist has an equal chance of being picked, so each syllable has an equal chance of being english or chinese + list( + "a", "ai", "an", "ang", "ao", "ba", "bai", "ban", "bang", "bao", "bei", "ben", "beng", "bi", "bian", "biao", + "bie", "bin", "bing", "bo", "bu", "ca", "cai", "can", "cang", "cao", "ce", "cei", "cen", "ceng", "cha", "chai", + "chan", "chang", "chao", "che", "chen", "cheng", "chi", "chong", "chou", "chu", "chua", "chuai", "chuan", "chuang", "chui", "chun", + "chuo", "ci", "cong", "cou", "cu", "cuan", "cui", "cun", "cuo", "da", "dai", "dan", "dang", "dao", "de", "dei", + "den", "deng", "di", "dian", "diao", "die", "ding", "diu", "dong", "dou", "du", "duan", "dui", "dun", "duo", "e", + "ei", "en", "er", "fa", "fan", "fang", "fei", "fen", "feng", "fo", "fou", "fu", "ga", "gai", "gan", "gang", + "gao", "ge", "gei", "gen", "geng", "gong", "gou", "gu", "gua", "guai", "guan", "guang", "gui", "gun", "guo", "ha", + "hai", "han", "hang", "hao", "he", "hei", "hen", "heng", "hm", "hng", "hong", "hou", "hu", "hua", "huai", "huan", + "huang", "hui", "hun", "huo", "ji", "jia", "jian", "jiang", "jiao", "jie", "jin", "jing", "jiong", "jiu", "ju", "juan", + "jue", "jun", "ka", "kai", "kan", "kang", "kao", "ke", "kei", "ken", "keng", "kong", "kou", "ku", "kua", "kuai", + "kuan", "kuang", "kui", "kun", "kuo", "la", "lai", "lan", "lang", "lao", "le", "lei", "leng", "li", "lia", "lian", + "liang", "liao", "lie", "lin", "ling", "liu", "long", "lou", "lu", "luan", "lun", "luo", "ma", "mai", "man", "mang", + "mao", "me", "mei", "men", "meng", "mi", "mian", "miao", "mie", "min", "ming", "miu", "mo", "mou", "mu", "na", + "nai", "nan", "nang", "nao", "ne", "nei", "nen", "neng", "ng", "ni", "nian", "niang", "niao", "nie", "nin", "ning", + "niu", "nong", "nou", "nu", "nuan", "nuo", "o", "ou", "pa", "pai", "pan", "pang", "pao", "pei", "pen", "peng", + "pi", "pian", "piao", "pie", "pin", "ping", "po", "pou", "pu", "qi", "qia", "qian", "qiang", "qiao", "qie", "qin", + "qing", "qiong", "qiu", "qu", "quan", "que", "qun", "ran", "rang", "rao", "re", "ren", "reng", "ri", "rong", "rou", + "ru", "rua", "ruan", "rui", "run", "ruo", "sa", "sai", "san", "sang", "sao", "se", "sei", "sen", "seng", "sha", + "shai", "shan", "shang", "shao", "she", "shei", "shen", "sheng", "shi", "shou", "shu", "shua", "shuai", "shuan", "shuang", "shui", + "shun", "shuo", "si", "song", "sou", "su", "suan", "sui", "sun", "suo", "ta", "tai", "tan", "tang", "tao", "te", + "teng", "ti", "tian", "tiao", "tie", "ting", "tong", "tou", "tu", "tuan", "tui", "tun", "tuo", "wa", "wai", "wan", + "wang", "wei", "wen", "weng", "wo", "wu", "xi", "xia", "xian", "xiang", "xiao", "xie", "xin", "xing", "xiong", "xiu", + "xu", "xuan", "xue", "xun", "ya", "yan", "yang", "yao", "ye", "yi", "yin", "ying", "yong", "you", "yu", "yuan", + "yue", "yun", "za", "zai", "zan", "zang", "zao", "ze", "zei", "zen", "zeng", "zha", "zhai", "zhan", "zhang", "zhao", + "zhe", "zhei", "zhen", "zheng", "zhi", "zhong", "zhou", "zhu", "zhua", "zhuai", "zhuan", "zhuang", "zhui", "zhun", "zhuo", "zi", + "zong", "zou", "zuan", "zui", "zun", "zuo", "zu", + ), + list( + "al", "an", "ar", "as", "at", "ea", "ed", "en", "er", "es", "ha", "he", "hi", "in", "is", "it", + "le", "me", "nd", "ne", "ng", "nt", "on", "or", "ou", "re", "se", "st", "te", "th", "ti", "to", + "ve", "wa", "all", "and", "are", "but", "ent", "era", "ere", "eve", "for", "had", "hat", "hen", "her", "hin", + "his", "ing", "ion", "ith", "not", "ome", "oul", "our", "sho", "ted", "ter", "tha", "the", "thi", + ), +) diff --git a/code/modules/language/language.dm b/code/modules/language/language.dm index 2d87ee141cc..361d9efc675 100644 --- a/code/modules/language/language.dm +++ b/code/modules/language/language.dm @@ -47,7 +47,7 @@ new_name = "" var/Y = rand(FLOOR(syllable_count/syllable_divisor, 1), syllable_count) for(var/x in Y to 0) - new_name += pick(syllables) + new_name += pick_weight_recursive(syllables) full_name += " [capitalize(lowertext(new_name))]" return "[trim(full_name)]" @@ -80,7 +80,7 @@ var/capitalize = TRUE while(length_char(scrambled_text) < input_size) - var/next = pick(syllables) + var/next = pick_weight_recursive(syllables) if(capitalize) next = capitalize(next) capitalize = FALSE