diff --git a/MODS/Enable_Mission_Board/Data/Misc/MonsterFeature.json b/MODS/Enable_Mission_Board/Data/Misc/MonsterFeature.json deleted file mode 100644 index af4e31a7db..0000000000 --- a/MODS/Enable_Mission_Board/Data/Misc/MonsterFeature.json +++ /dev/null @@ -1,13470 +0,0 @@ -{ -"Version": "0.8.0.0", -"Object": { -"$type": "PMDC.Data.MonsterFeatureData, PMDC", -"FeatureData": { -"abomasnow": { -"0": { -"Family": "snover", -"Stage": 4, -"Element1": "grass", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "snover", -"Stage": 4, -"Element1": "grass", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -} -}, -"abra": { -"0": { -"Family": "abra", -"Stage": 2, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"absol": { -"0": { -"Family": "absol", -"Stage": 1, -"Element1": "dark", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "absol", -"Stage": 1, -"Element1": "dark", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"accelgor": { -"0": { -"Family": "shelmet", -"Stage": 4, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"aegislash": { -"0": { -"Family": "honedge", -"Stage": 4, -"Element1": "steel", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "honedge", -"Stage": 4, -"Element1": "steel", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"aerodactyl": { -"0": { -"Family": "aerodactyl", -"Stage": 1, -"Element1": "rock", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "aerodactyl", -"Stage": 1, -"Element1": "rock", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"aggron": { -"0": { -"Family": "aron", -"Stage": 4, -"Element1": "steel", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "aron", -"Stage": 4, -"Element1": "steel", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"aipom": { -"0": { -"Family": "aipom", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"alakazam": { -"0": { -"Family": "abra", -"Stage": 4, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "abra", -"Stage": 4, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"alcremie": { -"0": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"4": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"5": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"6": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"7": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"8": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"9": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"10": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"11": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"12": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"13": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"14": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"15": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"16": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"17": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"18": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"19": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"20": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"21": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"22": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"23": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"24": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"25": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"26": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"27": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"28": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"29": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"30": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"31": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"32": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"33": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"34": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"35": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"36": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"37": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"38": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"39": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"40": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"41": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"42": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"43": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"44": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"45": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"46": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"47": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"48": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"49": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"50": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"51": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"52": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"53": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"54": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"55": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"56": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"57": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"58": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"59": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"60": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"61": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"62": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"63": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"64": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"65": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"66": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"67": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"68": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"69": { -"Family": "milcery", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"alomomola": { -"0": { -"Family": "alomomola", -"Stage": 1, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"altaria": { -"0": { -"Family": "swablu", -"Stage": 4, -"Element1": "dragon", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "swablu", -"Stage": 4, -"Element1": "dragon", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"amaura": { -"0": { -"Family": "amaura", -"Stage": 2, -"Element1": "rock", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ambipom": { -"0": { -"Family": "aipom", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"amoonguss": { -"0": { -"Family": "foongus", -"Stage": 4, -"Element1": "grass", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ampharos": { -"0": { -"Family": "mareep", -"Stage": 4, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "mareep", -"Stage": 4, -"Element1": "electric", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"annihilape": { -"0": { -"Family": "mankey", -"Stage": 4, -"Element1": "fighting", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"anorith": { -"0": { -"Family": "anorith", -"Stage": 2, -"Element1": "rock", -"Element2": "bug", -"BestStat": -1, -"WorstStat": -1 -} -}, -"appletun": { -"0": { -"Family": "applin", -"Stage": 4, -"Element1": "grass", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "applin", -"Stage": 4, -"Element1": "grass", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"applin": { -"0": { -"Family": "applin", -"Stage": 2, -"Element1": "grass", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"araquanid": { -"0": { -"Family": "dewpider", -"Stage": 4, -"Element1": "water", -"Element2": "bug", -"BestStat": -1, -"WorstStat": -1 -} -}, -"arbok": { -"0": { -"Family": "ekans", -"Stage": 4, -"Element1": "poison", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"arboliva": { -"0": { -"Family": "smoliv", -"Stage": 4, -"Element1": "grass", -"Element2": "normal", -"BestStat": -1, -"WorstStat": -1 -} -}, -"arcanine": { -"0": { -"Family": "growlithe", -"Stage": 4, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "growlithe", -"Stage": 4, -"Element1": "fire", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -} -}, -"arceus": { -"0": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"4": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"5": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"6": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"7": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"8": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"9": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"10": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"11": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"12": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"13": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"14": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"15": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"16": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"17": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"18": { -"Family": "arceus", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"archen": { -"0": { -"Family": "archen", -"Stage": 2, -"Element1": "rock", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"archeops": { -"0": { -"Family": "archen", -"Stage": 4, -"Element1": "rock", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"arctibax": { -"0": { -"Family": "frigibax", -"Stage": 8, -"Element1": "dragon", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -} -}, -"arctovish": { -"0": { -"Family": "arctovish", -"Stage": 1, -"Element1": "water", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -} -}, -"arctozolt": { -"0": { -"Family": "arctozolt", -"Stage": 1, -"Element1": "electric", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ariados": { -"0": { -"Family": "spinarak", -"Stage": 4, -"Element1": "bug", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"armaldo": { -"0": { -"Family": "anorith", -"Stage": 4, -"Element1": "rock", -"Element2": "bug", -"BestStat": -1, -"WorstStat": -1 -} -}, -"armarouge": { -"0": { -"Family": "charcadet", -"Stage": 4, -"Element1": "fire", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"aromatisse": { -"0": { -"Family": "spritzee", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"aron": { -"0": { -"Family": "aron", -"Stage": 2, -"Element1": "steel", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -} -}, -"arrokuda": { -"0": { -"Family": "arrokuda", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"articuno": { -"0": { -"Family": "articuno", -"Stage": 1, -"Element1": "ice", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "articuno", -"Stage": 1, -"Element1": "psychic", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"audino": { -"0": { -"Family": "audino", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "audino", -"Stage": 1, -"Element1": "normal", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"aurorus": { -"0": { -"Family": "amaura", -"Stage": 4, -"Element1": "rock", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -} -}, -"avalugg": { -"0": { -"Family": "bergmite", -"Stage": 4, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "bergmite", -"Stage": 4, -"Element1": "ice", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -} -}, -"axew": { -"0": { -"Family": "axew", -"Stage": 2, -"Element1": "dragon", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"azelf": { -"0": { -"Family": "azelf", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"azumarill": { -"0": { -"Family": "azurill", -"Stage": 4, -"Element1": "water", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"azurill": { -"0": { -"Family": "azurill", -"Stage": 2, -"Element1": "normal", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bagon": { -"0": { -"Family": "bagon", -"Stage": 2, -"Element1": "dragon", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"baltoy": { -"0": { -"Family": "baltoy", -"Stage": 2, -"Element1": "ground", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"banette": { -"0": { -"Family": "shuppet", -"Stage": 4, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "shuppet", -"Stage": 4, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"barbaracle": { -"0": { -"Family": "binacle", -"Stage": 4, -"Element1": "rock", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"barboach": { -"0": { -"Family": "barboach", -"Stage": 2, -"Element1": "water", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"barraskewda": { -"0": { -"Family": "arrokuda", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"basculegion": { -"0": { -"Family": "basculin", -"Stage": 4, -"Element1": "water", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "basculin", -"Stage": 4, -"Element1": "water", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"basculin": { -"0": { -"Family": "basculin", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "basculin", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "basculin", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bastiodon": { -"0": { -"Family": "shieldon", -"Stage": 4, -"Element1": "rock", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"baxcalibur": { -"0": { -"Family": "frigibax", -"Stage": 4, -"Element1": "dragon", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bayleef": { -"0": { -"Family": "chikorita", -"Stage": 8, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"beartic": { -"0": { -"Family": "cubchoo", -"Stage": 4, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"beautifly": { -"0": { -"Family": "wurmple", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"beedrill": { -"0": { -"Family": "weedle", -"Stage": 4, -"Element1": "bug", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "weedle", -"Stage": 4, -"Element1": "bug", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"beheeyem": { -"0": { -"Family": "elgyem", -"Stage": 4, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"beldum": { -"0": { -"Family": "beldum", -"Stage": 2, -"Element1": "steel", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bellibolt": { -"0": { -"Family": "tadbulb", -"Stage": 4, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bellossom": { -"0": { -"Family": "oddish", -"Stage": 4, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bellsprout": { -"0": { -"Family": "bellsprout", -"Stage": 2, -"Element1": "grass", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bergmite": { -"0": { -"Family": "bergmite", -"Stage": 2, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bewear": { -"0": { -"Family": "stufful", -"Stage": 4, -"Element1": "normal", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bibarel": { -"0": { -"Family": "bidoof", -"Stage": 4, -"Element1": "normal", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bidoof": { -"0": { -"Family": "bidoof", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"binacle": { -"0": { -"Family": "binacle", -"Stage": 2, -"Element1": "rock", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bisharp": { -"0": { -"Family": "pawniard", -"Stage": 8, -"Element1": "dark", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"blacephalon": { -"0": { -"Family": "blacephalon", -"Stage": 1, -"Element1": "fire", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"blastoise": { -"0": { -"Family": "squirtle", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "squirtle", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "squirtle", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"blaziken": { -"0": { -"Family": "torchic", -"Stage": 4, -"Element1": "fire", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "torchic", -"Stage": 4, -"Element1": "fire", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"blipbug": { -"0": { -"Family": "blipbug", -"Stage": 2, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"blissey": { -"0": { -"Family": "happiny", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"blitzle": { -"0": { -"Family": "blitzle", -"Stage": 2, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"boldore": { -"0": { -"Family": "roggenrola", -"Stage": 8, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"boltund": { -"0": { -"Family": "yamper", -"Stage": 4, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bombirdier": { -"0": { -"Family": "bombirdier", -"Stage": 1, -"Element1": "flying", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bonsly": { -"0": { -"Family": "bonsly", -"Stage": 2, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bouffalant": { -"0": { -"Family": "bouffalant", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bounsweet": { -"0": { -"Family": "bounsweet", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"braixen": { -"0": { -"Family": "fennekin", -"Stage": 8, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"brambleghast": { -"0": { -"Family": "bramblin", -"Stage": 4, -"Element1": "grass", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bramblin": { -"0": { -"Family": "bramblin", -"Stage": 2, -"Element1": "grass", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"braviary": { -"0": { -"Family": "rufflet", -"Stage": 4, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "rufflet", -"Stage": 4, -"Element1": "psychic", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"breloom": { -"0": { -"Family": "shroomish", -"Stage": 4, -"Element1": "grass", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"brionne": { -"0": { -"Family": "popplio", -"Stage": 8, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bronzong": { -"0": { -"Family": "bronzor", -"Stage": 4, -"Element1": "steel", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bronzor": { -"0": { -"Family": "bronzor", -"Stage": 2, -"Element1": "steel", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"brute_bonnet": { -"0": { -"Family": "brute_bonnet", -"Stage": 1, -"Element1": "grass", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bruxish": { -"0": { -"Family": "bruxish", -"Stage": 1, -"Element1": "water", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"budew": { -"0": { -"Family": "budew", -"Stage": 2, -"Element1": "grass", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"buizel": { -"0": { -"Family": "buizel", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bulbasaur": { -"0": { -"Family": "bulbasaur", -"Stage": 2, -"Element1": "grass", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"buneary": { -"0": { -"Family": "buneary", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"bunnelby": { -"0": { -"Family": "bunnelby", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"burmy": { -"0": { -"Family": "burmy", -"Stage": 2, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "burmy", -"Stage": 2, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "burmy", -"Stage": 2, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"butterfree": { -"0": { -"Family": "caterpie", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "caterpie", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"buzzwole": { -"0": { -"Family": "buzzwole", -"Stage": 1, -"Element1": "bug", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cacnea": { -"0": { -"Family": "cacnea", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cacturne": { -"0": { -"Family": "cacnea", -"Stage": 4, -"Element1": "grass", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"calyrex": { -"0": { -"Family": "calyrex", -"Stage": 1, -"Element1": "psychic", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "calyrex", -"Stage": 1, -"Element1": "psychic", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "calyrex", -"Stage": 1, -"Element1": "psychic", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"camerupt": { -"0": { -"Family": "numel", -"Stage": 4, -"Element1": "fire", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "numel", -"Stage": 4, -"Element1": "fire", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"capsakid": { -"0": { -"Family": "capsakid", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"carbink": { -"0": { -"Family": "carbink", -"Stage": 1, -"Element1": "rock", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"carkol": { -"0": { -"Family": "rolycoly", -"Stage": 8, -"Element1": "rock", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -} -}, -"carnivine": { -"0": { -"Family": "carnivine", -"Stage": 1, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"carracosta": { -"0": { -"Family": "tirtouga", -"Stage": 4, -"Element1": "water", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -} -}, -"carvanha": { -"0": { -"Family": "carvanha", -"Stage": 2, -"Element1": "water", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cascoon": { -"0": { -"Family": "wurmple", -"Stage": 8, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"castform": { -"0": { -"Family": "castform", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "castform", -"Stage": 1, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "castform", -"Stage": 1, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "castform", -"Stage": 1, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"caterpie": { -"0": { -"Family": "caterpie", -"Stage": 2, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"celebi": { -"0": { -"Family": "celebi", -"Stage": 1, -"Element1": "psychic", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"celesteela": { -"0": { -"Family": "celesteela", -"Stage": 1, -"Element1": "steel", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"centiskorch": { -"0": { -"Family": "sizzlipede", -"Stage": 4, -"Element1": "fire", -"Element2": "bug", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "sizzlipede", -"Stage": 4, -"Element1": "fire", -"Element2": "bug", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ceruledge": { -"0": { -"Family": "charcadet", -"Stage": 4, -"Element1": "fire", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cetitan": { -"0": { -"Family": "cetoddle", -"Stage": 4, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cetoddle": { -"0": { -"Family": "cetoddle", -"Stage": 2, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"chandelure": { -"0": { -"Family": "litwick", -"Stage": 4, -"Element1": "ghost", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -} -}, -"chansey": { -"0": { -"Family": "happiny", -"Stage": 8, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"charcadet": { -"0": { -"Family": "charcadet", -"Stage": 2, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"charizard": { -"0": { -"Family": "charmander", -"Stage": 4, -"Element1": "fire", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "charmander", -"Stage": 4, -"Element1": "fire", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "charmander", -"Stage": 4, -"Element1": "fire", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "charmander", -"Stage": 4, -"Element1": "fire", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"charjabug": { -"0": { -"Family": "grubbin", -"Stage": 8, -"Element1": "bug", -"Element2": "electric", -"BestStat": -1, -"WorstStat": -1 -} -}, -"charmander": { -"0": { -"Family": "charmander", -"Stage": 2, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"charmeleon": { -"0": { -"Family": "charmander", -"Stage": 8, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"chatot": { -"0": { -"Family": "chatot", -"Stage": 1, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cherrim": { -"0": { -"Family": "cherubi", -"Stage": 4, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "cherubi", -"Stage": 4, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cherubi": { -"0": { -"Family": "cherubi", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"chesnaught": { -"0": { -"Family": "chespin", -"Stage": 4, -"Element1": "grass", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"chespin": { -"0": { -"Family": "chespin", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"chewtle": { -"0": { -"Family": "chewtle", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"chi_yu": { -"0": { -"Family": "chi_yu", -"Stage": 1, -"Element1": "dark", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -} -}, -"chien_pao": { -"0": { -"Family": "chien_pao", -"Stage": 1, -"Element1": "dark", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -} -}, -"chikorita": { -"0": { -"Family": "chikorita", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"chimchar": { -"0": { -"Family": "chimchar", -"Stage": 2, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"chimecho": { -"0": { -"Family": "chingling", -"Stage": 4, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"chinchou": { -"0": { -"Family": "chinchou", -"Stage": 2, -"Element1": "water", -"Element2": "electric", -"BestStat": -1, -"WorstStat": -1 -} -}, -"chingling": { -"0": { -"Family": "chingling", -"Stage": 2, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cinccino": { -"0": { -"Family": "minccino", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cinderace": { -"0": { -"Family": "scorbunny", -"Stage": 4, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "scorbunny", -"Stage": 4, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"clamperl": { -"0": { -"Family": "clamperl", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"clauncher": { -"0": { -"Family": "clauncher", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"clawitzer": { -"0": { -"Family": "clauncher", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"claydol": { -"0": { -"Family": "baltoy", -"Stage": 4, -"Element1": "ground", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"clefable": { -"0": { -"Family": "cleffa", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"clefairy": { -"0": { -"Family": "cleffa", -"Stage": 8, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cleffa": { -"0": { -"Family": "cleffa", -"Stage": 2, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"clobbopus": { -"0": { -"Family": "clobbopus", -"Stage": 2, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"clodsire": { -"0": { -"Family": "wooper", -"Stage": 4, -"Element1": "poison", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cloyster": { -"0": { -"Family": "shellder", -"Stage": 4, -"Element1": "water", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -} -}, -"coalossal": { -"0": { -"Family": "rolycoly", -"Stage": 4, -"Element1": "rock", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "rolycoly", -"Stage": 4, -"Element1": "rock", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cobalion": { -"0": { -"Family": "cobalion", -"Stage": 1, -"Element1": "steel", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cofagrigus": { -"0": { -"Family": "yamask", -"Stage": 4, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"combee": { -"0": { -"Family": "combee", -"Stage": 2, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"combusken": { -"0": { -"Family": "torchic", -"Stage": 8, -"Element1": "fire", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"comfey": { -"0": { -"Family": "comfey", -"Stage": 1, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"conkeldurr": { -"0": { -"Family": "timburr", -"Stage": 4, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"copperajah": { -"0": { -"Family": "cufant", -"Stage": 4, -"Element1": "steel", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "cufant", -"Stage": 4, -"Element1": "steel", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"corphish": { -"0": { -"Family": "corphish", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"corsola": { -"0": { -"Family": "corsola", -"Stage": 2, -"Element1": "water", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "corsola", -"Stage": 2, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"corviknight": { -"0": { -"Family": "rookidee", -"Stage": 4, -"Element1": "flying", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "rookidee", -"Stage": 4, -"Element1": "flying", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"corvisquire": { -"0": { -"Family": "rookidee", -"Stage": 8, -"Element1": "flying", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cosmoem": { -"0": { -"Family": "cosmog", -"Stage": 8, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cosmog": { -"0": { -"Family": "cosmog", -"Stage": 2, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cottonee": { -"0": { -"Family": "cottonee", -"Stage": 2, -"Element1": "grass", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"crabominable": { -"0": { -"Family": "crabrawler", -"Stage": 4, -"Element1": "fighting", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -} -}, -"crabrawler": { -"0": { -"Family": "crabrawler", -"Stage": 2, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cradily": { -"0": { -"Family": "lileep", -"Stage": 4, -"Element1": "rock", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cramorant": { -"0": { -"Family": "cramorant", -"Stage": 1, -"Element1": "flying", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "cramorant", -"Stage": 1, -"Element1": "flying", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "cramorant", -"Stage": 1, -"Element1": "flying", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cranidos": { -"0": { -"Family": "cranidos", -"Stage": 2, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"crawdaunt": { -"0": { -"Family": "corphish", -"Stage": 4, -"Element1": "water", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cresselia": { -"0": { -"Family": "cresselia", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"croagunk": { -"0": { -"Family": "croagunk", -"Stage": 2, -"Element1": "poison", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"crobat": { -"0": { -"Family": "zubat", -"Stage": 4, -"Element1": "poison", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"crocalor": { -"0": { -"Family": "fuecoco", -"Stage": 8, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"croconaw": { -"0": { -"Family": "totodile", -"Stage": 8, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"crustle": { -"0": { -"Family": "dwebble", -"Stage": 4, -"Element1": "bug", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cryogonal": { -"0": { -"Family": "cryogonal", -"Stage": 1, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cubchoo": { -"0": { -"Family": "cubchoo", -"Stage": 2, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cubone": { -"0": { -"Family": "cubone", -"Stage": 2, -"Element1": "ground", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cufant": { -"0": { -"Family": "cufant", -"Stage": 2, -"Element1": "steel", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cursola": { -"0": { -"Family": "corsola", -"Stage": 4, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cutiefly": { -"0": { -"Family": "cutiefly", -"Stage": 2, -"Element1": "bug", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cyclizar": { -"0": { -"Family": "cyclizar", -"Stage": 1, -"Element1": "dragon", -"Element2": "normal", -"BestStat": -1, -"WorstStat": -1 -} -}, -"cyndaquil": { -"0": { -"Family": "cyndaquil", -"Stage": 2, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dachsbun": { -"0": { -"Family": "fidough", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"darkrai": { -"0": { -"Family": "darkrai", -"Stage": 1, -"Element1": "dark", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"darmanitan": { -"0": { -"Family": "darumaka", -"Stage": 4, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "darumaka", -"Stage": 4, -"Element1": "fire", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "darumaka", -"Stage": 4, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "darumaka", -"Stage": 4, -"Element1": "ice", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dartrix": { -"0": { -"Family": "rowlet", -"Stage": 8, -"Element1": "grass", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"darumaka": { -"0": { -"Family": "darumaka", -"Stage": 2, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "darumaka", -"Stage": 2, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"decidueye": { -"0": { -"Family": "rowlet", -"Stage": 4, -"Element1": "grass", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "rowlet", -"Stage": 4, -"Element1": "grass", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dedenne": { -"0": { -"Family": "dedenne", -"Stage": 1, -"Element1": "electric", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"deerling": { -"0": { -"Family": "deerling", -"Stage": 2, -"Element1": "normal", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "deerling", -"Stage": 2, -"Element1": "normal", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "deerling", -"Stage": 2, -"Element1": "normal", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "deerling", -"Stage": 2, -"Element1": "normal", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"deino": { -"0": { -"Family": "deino", -"Stage": 2, -"Element1": "dark", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"delcatty": { -"0": { -"Family": "skitty", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"delibird": { -"0": { -"Family": "delibird", -"Stage": 1, -"Element1": "ice", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"delphox": { -"0": { -"Family": "fennekin", -"Stage": 4, -"Element1": "fire", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"deoxys": { -"0": { -"Family": "deoxys", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "deoxys", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "deoxys", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "deoxys", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dewgong": { -"0": { -"Family": "seel", -"Stage": 4, -"Element1": "water", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dewott": { -"0": { -"Family": "oshawott", -"Stage": 8, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dewpider": { -"0": { -"Family": "dewpider", -"Stage": 2, -"Element1": "water", -"Element2": "bug", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dhelmise": { -"0": { -"Family": "dhelmise", -"Stage": 1, -"Element1": "ghost", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dialga": { -"0": { -"Family": "dialga", -"Stage": 1, -"Element1": "steel", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "dialga", -"Stage": 1, -"Element1": "steel", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"diancie": { -"0": { -"Family": "diancie", -"Stage": 1, -"Element1": "rock", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "diancie", -"Stage": 1, -"Element1": "rock", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"diggersby": { -"0": { -"Family": "bunnelby", -"Stage": 4, -"Element1": "normal", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"diglett": { -"0": { -"Family": "diglett", -"Stage": 2, -"Element1": "ground", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "diglett", -"Stage": 2, -"Element1": "ground", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ditto": { -"0": { -"Family": "ditto", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dodrio": { -"0": { -"Family": "doduo", -"Stage": 4, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"doduo": { -"0": { -"Family": "doduo", -"Stage": 2, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dolliv": { -"0": { -"Family": "smoliv", -"Stage": 8, -"Element1": "grass", -"Element2": "normal", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dondozo": { -"0": { -"Family": "dondozo", -"Stage": 1, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"donphan": { -"0": { -"Family": "phanpy", -"Stage": 4, -"Element1": "ground", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dottler": { -"0": { -"Family": "blipbug", -"Stage": 8, -"Element1": "bug", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"doublade": { -"0": { -"Family": "honedge", -"Stage": 8, -"Element1": "steel", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dracovish": { -"0": { -"Family": "dracovish", -"Stage": 1, -"Element1": "water", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dracozolt": { -"0": { -"Family": "dracozolt", -"Stage": 1, -"Element1": "electric", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dragalge": { -"0": { -"Family": "skrelp", -"Stage": 4, -"Element1": "poison", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dragapult": { -"0": { -"Family": "dreepy", -"Stage": 4, -"Element1": "dragon", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dragonair": { -"0": { -"Family": "dratini", -"Stage": 8, -"Element1": "dragon", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dragonite": { -"0": { -"Family": "dratini", -"Stage": 4, -"Element1": "dragon", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"drakloak": { -"0": { -"Family": "dreepy", -"Stage": 8, -"Element1": "dragon", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"drampa": { -"0": { -"Family": "drampa", -"Stage": 1, -"Element1": "normal", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"drapion": { -"0": { -"Family": "skorupi", -"Stage": 4, -"Element1": "poison", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dratini": { -"0": { -"Family": "dratini", -"Stage": 2, -"Element1": "dragon", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"drednaw": { -"0": { -"Family": "chewtle", -"Stage": 4, -"Element1": "water", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "chewtle", -"Stage": 4, -"Element1": "water", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dreepy": { -"0": { -"Family": "dreepy", -"Stage": 2, -"Element1": "dragon", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"drifblim": { -"0": { -"Family": "drifloon", -"Stage": 4, -"Element1": "ghost", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"drifloon": { -"0": { -"Family": "drifloon", -"Stage": 2, -"Element1": "ghost", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"drilbur": { -"0": { -"Family": "drilbur", -"Stage": 2, -"Element1": "ground", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"drizzile": { -"0": { -"Family": "sobble", -"Stage": 8, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"drowzee": { -"0": { -"Family": "drowzee", -"Stage": 2, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"druddigon": { -"0": { -"Family": "druddigon", -"Stage": 1, -"Element1": "dragon", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dubwool": { -"0": { -"Family": "wooloo", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ducklett": { -"0": { -"Family": "ducklett", -"Stage": 2, -"Element1": "water", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dudunsparce": { -"0": { -"Family": "dunsparce", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "dunsparce", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dugtrio": { -"0": { -"Family": "diglett", -"Stage": 4, -"Element1": "ground", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "diglett", -"Stage": 4, -"Element1": "ground", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dunsparce": { -"0": { -"Family": "dunsparce", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"duosion": { -"0": { -"Family": "solosis", -"Stage": 8, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"duraludon": { -"0": { -"Family": "duraludon", -"Stage": 1, -"Element1": "steel", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "duraludon", -"Stage": 1, -"Element1": "steel", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"durant": { -"0": { -"Family": "durant", -"Stage": 1, -"Element1": "bug", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dusclops": { -"0": { -"Family": "duskull", -"Stage": 8, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dusknoir": { -"0": { -"Family": "duskull", -"Stage": 4, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"duskull": { -"0": { -"Family": "duskull", -"Stage": 2, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dustox": { -"0": { -"Family": "wurmple", -"Stage": 4, -"Element1": "bug", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"dwebble": { -"0": { -"Family": "dwebble", -"Stage": 2, -"Element1": "bug", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -} -}, -"eelektrik": { -"0": { -"Family": "tynamo", -"Stage": 8, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"eelektross": { -"0": { -"Family": "tynamo", -"Stage": 4, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"eevee": { -"0": { -"Family": "eevee", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"eiscue": { -"0": { -"Family": "eiscue", -"Stage": 1, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "eiscue", -"Stage": 1, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ekans": { -"0": { -"Family": "ekans", -"Stage": 2, -"Element1": "poison", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"eldegoss": { -"0": { -"Family": "gossifleur", -"Stage": 4, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"electabuzz": { -"0": { -"Family": "elekid", -"Stage": 8, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"electivire": { -"0": { -"Family": "elekid", -"Stage": 4, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"electrike": { -"0": { -"Family": "electrike", -"Stage": 2, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"electrode": { -"0": { -"Family": "voltorb", -"Stage": 4, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "voltorb", -"Stage": 4, -"Element1": "electric", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"elekid": { -"0": { -"Family": "elekid", -"Stage": 2, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"elgyem": { -"0": { -"Family": "elgyem", -"Stage": 2, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"emboar": { -"0": { -"Family": "tepig", -"Stage": 4, -"Element1": "fire", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"emolga": { -"0": { -"Family": "emolga", -"Stage": 1, -"Element1": "electric", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"empoleon": { -"0": { -"Family": "piplup", -"Stage": 4, -"Element1": "water", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"enamorus": { -"0": { -"Family": "enamorus", -"Stage": 1, -"Element1": "fairy", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "enamorus", -"Stage": 1, -"Element1": "fairy", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"entei": { -"0": { -"Family": "entei", -"Stage": 1, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"escavalier": { -"0": { -"Family": "karrablast", -"Stage": 4, -"Element1": "bug", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"espathra": { -"0": { -"Family": "flittle", -"Stage": 4, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"espeon": { -"0": { -"Family": "eevee", -"Stage": 4, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"espurr": { -"0": { -"Family": "espurr", -"Stage": 2, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"eternatus": { -"0": { -"Family": "eternatus", -"Stage": 1, -"Element1": "poison", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "eternatus", -"Stage": 1, -"Element1": "poison", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"excadrill": { -"0": { -"Family": "drilbur", -"Stage": 4, -"Element1": "ground", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"exeggcute": { -"0": { -"Family": "exeggcute", -"Stage": 2, -"Element1": "grass", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"exeggutor": { -"0": { -"Family": "exeggcute", -"Stage": 4, -"Element1": "grass", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "exeggcute", -"Stage": 4, -"Element1": "grass", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"exploud": { -"0": { -"Family": "whismur", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"falinks": { -"0": { -"Family": "falinks", -"Stage": 1, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"farfetchd": { -"0": { -"Family": "farfetchd", -"Stage": 2, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "farfetchd", -"Stage": 2, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"farigiraf": { -"0": { -"Family": "girafarig", -"Stage": 4, -"Element1": "normal", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"fearow": { -"0": { -"Family": "spearow", -"Stage": 4, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"feebas": { -"0": { -"Family": "feebas", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"fennekin": { -"0": { -"Family": "fennekin", -"Stage": 2, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"feraligatr": { -"0": { -"Family": "totodile", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ferroseed": { -"0": { -"Family": "ferroseed", -"Stage": 2, -"Element1": "grass", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ferrothorn": { -"0": { -"Family": "ferroseed", -"Stage": 4, -"Element1": "grass", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"fidough": { -"0": { -"Family": "fidough", -"Stage": 2, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"finizen": { -"0": { -"Family": "finizen", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"finneon": { -"0": { -"Family": "finneon", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"flaaffy": { -"0": { -"Family": "mareep", -"Stage": 8, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"flabebe": { -"0": { -"Family": "flabebe", -"Stage": 2, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "flabebe", -"Stage": 2, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "flabebe", -"Stage": 2, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "flabebe", -"Stage": 2, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"4": { -"Family": "flabebe", -"Stage": 2, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"flamigo": { -"0": { -"Family": "flamigo", -"Stage": 1, -"Element1": "flying", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"flapple": { -"0": { -"Family": "applin", -"Stage": 4, -"Element1": "grass", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "applin", -"Stage": 4, -"Element1": "grass", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"flareon": { -"0": { -"Family": "eevee", -"Stage": 4, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"fletchinder": { -"0": { -"Family": "fletchling", -"Stage": 8, -"Element1": "fire", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"fletchling": { -"0": { -"Family": "fletchling", -"Stage": 2, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"flittle": { -"0": { -"Family": "flittle", -"Stage": 2, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"floatzel": { -"0": { -"Family": "buizel", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"floette": { -"0": { -"Family": "flabebe", -"Stage": 8, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "flabebe", -"Stage": 8, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "flabebe", -"Stage": 8, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "flabebe", -"Stage": 8, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"4": { -"Family": "flabebe", -"Stage": 8, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"5": { -"Family": "flabebe", -"Stage": 8, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"floragato": { -"0": { -"Family": "sprigatito", -"Stage": 8, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"florges": { -"0": { -"Family": "flabebe", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "flabebe", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "flabebe", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "flabebe", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"4": { -"Family": "flabebe", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"flutter_mane": { -"0": { -"Family": "flutter_mane", -"Stage": 1, -"Element1": "ghost", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"flygon": { -"0": { -"Family": "trapinch", -"Stage": 4, -"Element1": "ground", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"fomantis": { -"0": { -"Family": "fomantis", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"foongus": { -"0": { -"Family": "foongus", -"Stage": 2, -"Element1": "grass", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"forretress": { -"0": { -"Family": "pineco", -"Stage": 4, -"Element1": "bug", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"fraxure": { -"0": { -"Family": "axew", -"Stage": 8, -"Element1": "dragon", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"frigibax": { -"0": { -"Family": "frigibax", -"Stage": 2, -"Element1": "dragon", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -} -}, -"frillish": { -"0": { -"Family": "frillish", -"Stage": 2, -"Element1": "water", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"froakie": { -"0": { -"Family": "froakie", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"frogadier": { -"0": { -"Family": "froakie", -"Stage": 8, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"froslass": { -"0": { -"Family": "snorunt", -"Stage": 4, -"Element1": "ice", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"frosmoth": { -"0": { -"Family": "snom", -"Stage": 4, -"Element1": "ice", -"Element2": "bug", -"BestStat": -1, -"WorstStat": -1 -} -}, -"fuecoco": { -"0": { -"Family": "fuecoco", -"Stage": 2, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"furfrou": { -"0": { -"Family": "furfrou", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "furfrou", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "furfrou", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "furfrou", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"4": { -"Family": "furfrou", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"5": { -"Family": "furfrou", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"6": { -"Family": "furfrou", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"7": { -"Family": "furfrou", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"8": { -"Family": "furfrou", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"9": { -"Family": "furfrou", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"furret": { -"0": { -"Family": "sentret", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gabite": { -"0": { -"Family": "gible", -"Stage": 8, -"Element1": "dragon", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gallade": { -"0": { -"Family": "ralts", -"Stage": 4, -"Element1": "psychic", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "ralts", -"Stage": 4, -"Element1": "psychic", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"galvantula": { -"0": { -"Family": "joltik", -"Stage": 4, -"Element1": "bug", -"Element2": "electric", -"BestStat": -1, -"WorstStat": -1 -} -}, -"garbodor": { -"0": { -"Family": "trubbish", -"Stage": 4, -"Element1": "poison", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "trubbish", -"Stage": 4, -"Element1": "poison", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"garchomp": { -"0": { -"Family": "gible", -"Stage": 4, -"Element1": "dragon", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "gible", -"Stage": 4, -"Element1": "dragon", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gardevoir": { -"0": { -"Family": "ralts", -"Stage": 4, -"Element1": "psychic", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "ralts", -"Stage": 4, -"Element1": "psychic", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"garganacl": { -"0": { -"Family": "nacli", -"Stage": 4, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gastly": { -"0": { -"Family": "gastly", -"Stage": 2, -"Element1": "ghost", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gastrodon": { -"0": { -"Family": "shellos", -"Stage": 4, -"Element1": "water", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "shellos", -"Stage": 4, -"Element1": "water", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"genesect": { -"0": { -"Family": "genesect", -"Stage": 1, -"Element1": "bug", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "genesect", -"Stage": 1, -"Element1": "bug", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "genesect", -"Stage": 1, -"Element1": "bug", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "genesect", -"Stage": 1, -"Element1": "bug", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -}, -"4": { -"Family": "genesect", -"Stage": 1, -"Element1": "bug", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gengar": { -"0": { -"Family": "gastly", -"Stage": 4, -"Element1": "ghost", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "gastly", -"Stage": 4, -"Element1": "ghost", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "gastly", -"Stage": 4, -"Element1": "ghost", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"geodude": { -"0": { -"Family": "geodude", -"Stage": 2, -"Element1": "rock", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "geodude", -"Stage": 2, -"Element1": "rock", -"Element2": "electric", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gholdengo": { -"0": { -"Family": "gimmighoul", -"Stage": 4, -"Element1": "steel", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gible": { -"0": { -"Family": "gible", -"Stage": 2, -"Element1": "dragon", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gigalith": { -"0": { -"Family": "roggenrola", -"Stage": 4, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gimmighoul": { -"0": { -"Family": "gimmighoul", -"Stage": 2, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "gimmighoul", -"Stage": 2, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"girafarig": { -"0": { -"Family": "girafarig", -"Stage": 2, -"Element1": "normal", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"giratina": { -"0": { -"Family": "giratina", -"Stage": 1, -"Element1": "ghost", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "giratina", -"Stage": 1, -"Element1": "ghost", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"glaceon": { -"0": { -"Family": "eevee", -"Stage": 4, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"glalie": { -"0": { -"Family": "snorunt", -"Stage": 4, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "snorunt", -"Stage": 4, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"glameow": { -"0": { -"Family": "glameow", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"glastrier": { -"0": { -"Family": "glastrier", -"Stage": 1, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gligar": { -"0": { -"Family": "gligar", -"Stage": 2, -"Element1": "ground", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"glimmet": { -"0": { -"Family": "glimmet", -"Stage": 2, -"Element1": "rock", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"glimmora": { -"0": { -"Family": "glimmet", -"Stage": 4, -"Element1": "rock", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gliscor": { -"0": { -"Family": "gligar", -"Stage": 4, -"Element1": "ground", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gloom": { -"0": { -"Family": "oddish", -"Stage": 8, -"Element1": "grass", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gogoat": { -"0": { -"Family": "skiddo", -"Stage": 4, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"golbat": { -"0": { -"Family": "zubat", -"Stage": 8, -"Element1": "poison", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"goldeen": { -"0": { -"Family": "goldeen", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"golduck": { -"0": { -"Family": "psyduck", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"golem": { -"0": { -"Family": "geodude", -"Stage": 4, -"Element1": "rock", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "geodude", -"Stage": 4, -"Element1": "rock", -"Element2": "electric", -"BestStat": -1, -"WorstStat": -1 -} -}, -"golett": { -"0": { -"Family": "golett", -"Stage": 2, -"Element1": "ground", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"golisopod": { -"0": { -"Family": "wimpod", -"Stage": 4, -"Element1": "bug", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"golurk": { -"0": { -"Family": "golett", -"Stage": 4, -"Element1": "ground", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"goodra": { -"0": { -"Family": "goomy", -"Stage": 4, -"Element1": "dragon", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "goomy", -"Stage": 4, -"Element1": "steel", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"goomy": { -"0": { -"Family": "goomy", -"Stage": 2, -"Element1": "dragon", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gorebyss": { -"0": { -"Family": "clamperl", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gossifleur": { -"0": { -"Family": "gossifleur", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gothita": { -"0": { -"Family": "gothita", -"Stage": 2, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gothitelle": { -"0": { -"Family": "gothita", -"Stage": 4, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gothorita": { -"0": { -"Family": "gothita", -"Stage": 8, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gourgeist": { -"0": { -"Family": "pumpkaboo", -"Stage": 4, -"Element1": "ghost", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "pumpkaboo", -"Stage": 4, -"Element1": "ghost", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "pumpkaboo", -"Stage": 4, -"Element1": "ghost", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "pumpkaboo", -"Stage": 4, -"Element1": "ghost", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"grafaiai": { -"0": { -"Family": "shroodle", -"Stage": 4, -"Element1": "poison", -"Element2": "normal", -"BestStat": -1, -"WorstStat": -1 -} -}, -"granbull": { -"0": { -"Family": "snubbull", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"grapploct": { -"0": { -"Family": "clobbopus", -"Stage": 4, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"graveler": { -"0": { -"Family": "geodude", -"Stage": 8, -"Element1": "rock", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "geodude", -"Stage": 8, -"Element1": "rock", -"Element2": "electric", -"BestStat": -1, -"WorstStat": -1 -} -}, -"great_tusk": { -"0": { -"Family": "great_tusk", -"Stage": 1, -"Element1": "ground", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"greavard": { -"0": { -"Family": "greavard", -"Stage": 2, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"greedent": { -"0": { -"Family": "skwovet", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"greninja": { -"0": { -"Family": "froakie", -"Stage": 4, -"Element1": "water", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"grimer": { -"0": { -"Family": "grimer", -"Stage": 2, -"Element1": "poison", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "grimer", -"Stage": 2, -"Element1": "poison", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"grimmsnarl": { -"0": { -"Family": "impidimp", -"Stage": 4, -"Element1": "dark", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "impidimp", -"Stage": 4, -"Element1": "dark", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"grookey": { -"0": { -"Family": "grookey", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"grotle": { -"0": { -"Family": "turtwig", -"Stage": 8, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"groudon": { -"0": { -"Family": "groudon", -"Stage": 1, -"Element1": "ground", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "groudon", -"Stage": 1, -"Element1": "ground", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -} -}, -"grovyle": { -"0": { -"Family": "treecko", -"Stage": 8, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"growlithe": { -"0": { -"Family": "growlithe", -"Stage": 2, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "growlithe", -"Stage": 2, -"Element1": "fire", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -} -}, -"grubbin": { -"0": { -"Family": "grubbin", -"Stage": 2, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"grumpig": { -"0": { -"Family": "spoink", -"Stage": 4, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gulpin": { -"0": { -"Family": "gulpin", -"Stage": 2, -"Element1": "poison", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gumshoos": { -"0": { -"Family": "yungoos", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gurdurr": { -"0": { -"Family": "timburr", -"Stage": 8, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"guzzlord": { -"0": { -"Family": "guzzlord", -"Stage": 1, -"Element1": "dark", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"gyarados": { -"0": { -"Family": "magikarp", -"Stage": 4, -"Element1": "water", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "magikarp", -"Stage": 4, -"Element1": "water", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"hakamo_o": { -"0": { -"Family": "jangmo_o", -"Stage": 8, -"Element1": "dragon", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"happiny": { -"0": { -"Family": "happiny", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"hariyama": { -"0": { -"Family": "makuhita", -"Stage": 4, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"hatenna": { -"0": { -"Family": "hatenna", -"Stage": 2, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"hatterene": { -"0": { -"Family": "hatenna", -"Stage": 4, -"Element1": "psychic", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "hatenna", -"Stage": 4, -"Element1": "psychic", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"hattrem": { -"0": { -"Family": "hatenna", -"Stage": 8, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"haunter": { -"0": { -"Family": "gastly", -"Stage": 8, -"Element1": "ghost", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"hawlucha": { -"0": { -"Family": "hawlucha", -"Stage": 1, -"Element1": "fighting", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"haxorus": { -"0": { -"Family": "axew", -"Stage": 4, -"Element1": "dragon", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"heatmor": { -"0": { -"Family": "heatmor", -"Stage": 1, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"heatran": { -"0": { -"Family": "heatran", -"Stage": 1, -"Element1": "fire", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"heliolisk": { -"0": { -"Family": "helioptile", -"Stage": 4, -"Element1": "electric", -"Element2": "normal", -"BestStat": -1, -"WorstStat": -1 -} -}, -"helioptile": { -"0": { -"Family": "helioptile", -"Stage": 2, -"Element1": "electric", -"Element2": "normal", -"BestStat": -1, -"WorstStat": -1 -} -}, -"heracross": { -"0": { -"Family": "heracross", -"Stage": 1, -"Element1": "bug", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "heracross", -"Stage": 1, -"Element1": "bug", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"herdier": { -"0": { -"Family": "lillipup", -"Stage": 8, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"hippopotas": { -"0": { -"Family": "hippopotas", -"Stage": 2, -"Element1": "ground", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"hippowdon": { -"0": { -"Family": "hippopotas", -"Stage": 4, -"Element1": "ground", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"hitmonchan": { -"0": { -"Family": "tyrogue", -"Stage": 4, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"hitmonlee": { -"0": { -"Family": "tyrogue", -"Stage": 4, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"hitmontop": { -"0": { -"Family": "tyrogue", -"Stage": 4, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ho_oh": { -"0": { -"Family": "ho_oh", -"Stage": 1, -"Element1": "fire", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"honchkrow": { -"0": { -"Family": "murkrow", -"Stage": 4, -"Element1": "dark", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"honedge": { -"0": { -"Family": "honedge", -"Stage": 2, -"Element1": "steel", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"hoopa": { -"0": { -"Family": "hoopa", -"Stage": 1, -"Element1": "psychic", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "hoopa", -"Stage": 1, -"Element1": "psychic", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"hoothoot": { -"0": { -"Family": "hoothoot", -"Stage": 2, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"hoppip": { -"0": { -"Family": "hoppip", -"Stage": 2, -"Element1": "grass", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"horsea": { -"0": { -"Family": "horsea", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"houndoom": { -"0": { -"Family": "houndour", -"Stage": 4, -"Element1": "dark", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "houndour", -"Stage": 4, -"Element1": "dark", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -} -}, -"houndour": { -"0": { -"Family": "houndour", -"Stage": 2, -"Element1": "dark", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -} -}, -"houndstone": { -"0": { -"Family": "greavard", -"Stage": 4, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"huntail": { -"0": { -"Family": "clamperl", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"hydreigon": { -"0": { -"Family": "deino", -"Stage": 4, -"Element1": "dark", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"hypno": { -"0": { -"Family": "drowzee", -"Stage": 4, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"igglybuff": { -"0": { -"Family": "igglybuff", -"Stage": 2, -"Element1": "normal", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"illumise": { -"0": { -"Family": "illumise", -"Stage": 1, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"impidimp": { -"0": { -"Family": "impidimp", -"Stage": 2, -"Element1": "dark", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"incineroar": { -"0": { -"Family": "litten", -"Stage": 4, -"Element1": "fire", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"indeedee": { -"0": { -"Family": "indeedee", -"Stage": 1, -"Element1": "psychic", -"Element2": "normal", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "indeedee", -"Stage": 1, -"Element1": "psychic", -"Element2": "normal", -"BestStat": -1, -"WorstStat": -1 -} -}, -"infernape": { -"0": { -"Family": "chimchar", -"Stage": 4, -"Element1": "fire", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"inkay": { -"0": { -"Family": "inkay", -"Stage": 2, -"Element1": "dark", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"inteleon": { -"0": { -"Family": "sobble", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "sobble", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"iron_bundle": { -"0": { -"Family": "iron_bundle", -"Stage": 1, -"Element1": "ice", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"iron_hands": { -"0": { -"Family": "iron_hands", -"Stage": 1, -"Element1": "fighting", -"Element2": "electric", -"BestStat": -1, -"WorstStat": -1 -} -}, -"iron_jugulis": { -"0": { -"Family": "iron_jugulis", -"Stage": 1, -"Element1": "dark", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"iron_leaves": { -"0": { -"Family": "iron_leaves", -"Stage": 1, -"Element1": "grass", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"iron_moth": { -"0": { -"Family": "iron_moth", -"Stage": 1, -"Element1": "fire", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"iron_thorns": { -"0": { -"Family": "iron_thorns", -"Stage": 1, -"Element1": "rock", -"Element2": "electric", -"BestStat": -1, -"WorstStat": -1 -} -}, -"iron_treads": { -"0": { -"Family": "iron_treads", -"Stage": 1, -"Element1": "ground", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"iron_valiant": { -"0": { -"Family": "iron_valiant", -"Stage": 1, -"Element1": "fairy", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ivysaur": { -"0": { -"Family": "bulbasaur", -"Stage": 8, -"Element1": "grass", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"jangmo_o": { -"0": { -"Family": "jangmo_o", -"Stage": 2, -"Element1": "dragon", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"jellicent": { -"0": { -"Family": "frillish", -"Stage": 4, -"Element1": "water", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"jigglypuff": { -"0": { -"Family": "igglybuff", -"Stage": 8, -"Element1": "normal", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"jirachi": { -"0": { -"Family": "jirachi", -"Stage": 1, -"Element1": "steel", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"jolteon": { -"0": { -"Family": "eevee", -"Stage": 4, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"joltik": { -"0": { -"Family": "joltik", -"Stage": 2, -"Element1": "bug", -"Element2": "electric", -"BestStat": -1, -"WorstStat": -1 -} -}, -"jumpluff": { -"0": { -"Family": "hoppip", -"Stage": 4, -"Element1": "grass", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"jynx": { -"0": { -"Family": "smoochum", -"Stage": 4, -"Element1": "ice", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kabuto": { -"0": { -"Family": "kabuto", -"Stage": 2, -"Element1": "rock", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kabutops": { -"0": { -"Family": "kabuto", -"Stage": 4, -"Element1": "rock", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kadabra": { -"0": { -"Family": "abra", -"Stage": 8, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kakuna": { -"0": { -"Family": "weedle", -"Stage": 8, -"Element1": "bug", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kangaskhan": { -"0": { -"Family": "kangaskhan", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "kangaskhan", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"karrablast": { -"0": { -"Family": "karrablast", -"Stage": 2, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kartana": { -"0": { -"Family": "kartana", -"Stage": 1, -"Element1": "grass", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kecleon": { -"0": { -"Family": "kecleon", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"keldeo": { -"0": { -"Family": "keldeo", -"Stage": 1, -"Element1": "water", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "keldeo", -"Stage": 1, -"Element1": "water", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kilowattrel": { -"0": { -"Family": "wattrel", -"Stage": 4, -"Element1": "electric", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kingambit": { -"0": { -"Family": "pawniard", -"Stage": 4, -"Element1": "dark", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kingdra": { -"0": { -"Family": "horsea", -"Stage": 4, -"Element1": "water", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kingler": { -"0": { -"Family": "krabby", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "krabby", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kirlia": { -"0": { -"Family": "ralts", -"Stage": 8, -"Element1": "psychic", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"klang": { -"0": { -"Family": "klink", -"Stage": 8, -"Element1": "steel", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"klawf": { -"0": { -"Family": "klawf", -"Stage": 1, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kleavor": { -"0": { -"Family": "scyther", -"Stage": 4, -"Element1": "bug", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -} -}, -"klefki": { -"0": { -"Family": "klefki", -"Stage": 1, -"Element1": "steel", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"klink": { -"0": { -"Family": "klink", -"Stage": 2, -"Element1": "steel", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"klinklang": { -"0": { -"Family": "klink", -"Stage": 4, -"Element1": "steel", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"koffing": { -"0": { -"Family": "koffing", -"Stage": 2, -"Element1": "poison", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"komala": { -"0": { -"Family": "komala", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kommo_o": { -"0": { -"Family": "jangmo_o", -"Stage": 4, -"Element1": "dragon", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"koraidon": { -"0": { -"Family": "koraidon", -"Stage": 1, -"Element1": "fighting", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"krabby": { -"0": { -"Family": "krabby", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kricketot": { -"0": { -"Family": "kricketot", -"Stage": 2, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kricketune": { -"0": { -"Family": "kricketot", -"Stage": 4, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"krokorok": { -"0": { -"Family": "sandile", -"Stage": 8, -"Element1": "ground", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"krookodile": { -"0": { -"Family": "sandile", -"Stage": 4, -"Element1": "ground", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kubfu": { -"0": { -"Family": "kubfu", -"Stage": 2, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kyogre": { -"0": { -"Family": "kyogre", -"Stage": 1, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "kyogre", -"Stage": 1, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"kyurem": { -"0": { -"Family": "kyurem", -"Stage": 1, -"Element1": "dragon", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "kyurem", -"Stage": 1, -"Element1": "dragon", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "kyurem", -"Stage": 1, -"Element1": "dragon", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lairon": { -"0": { -"Family": "aron", -"Stage": 8, -"Element1": "steel", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lampent": { -"0": { -"Family": "litwick", -"Stage": 8, -"Element1": "ghost", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -} -}, -"landorus": { -"0": { -"Family": "landorus", -"Stage": 1, -"Element1": "ground", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "landorus", -"Stage": 1, -"Element1": "ground", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lanturn": { -"0": { -"Family": "chinchou", -"Stage": 4, -"Element1": "water", -"Element2": "electric", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lapras": { -"0": { -"Family": "lapras", -"Stage": 1, -"Element1": "water", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "lapras", -"Stage": 1, -"Element1": "water", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -} -}, -"larvesta": { -"0": { -"Family": "larvesta", -"Stage": 2, -"Element1": "bug", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -} -}, -"larvitar": { -"0": { -"Family": "larvitar", -"Stage": 2, -"Element1": "rock", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"latias": { -"0": { -"Family": "latias", -"Stage": 1, -"Element1": "dragon", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "latias", -"Stage": 1, -"Element1": "dragon", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"latios": { -"0": { -"Family": "latios", -"Stage": 1, -"Element1": "dragon", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "latios", -"Stage": 1, -"Element1": "dragon", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"leafeon": { -"0": { -"Family": "eevee", -"Stage": 4, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"leavanny": { -"0": { -"Family": "sewaddle", -"Stage": 4, -"Element1": "bug", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lechonk": { -"0": { -"Family": "lechonk", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ledian": { -"0": { -"Family": "ledyba", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ledyba": { -"0": { -"Family": "ledyba", -"Stage": 2, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lickilicky": { -"0": { -"Family": "lickitung", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lickitung": { -"0": { -"Family": "lickitung", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"liepard": { -"0": { -"Family": "purrloin", -"Stage": 4, -"Element1": "dark", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lileep": { -"0": { -"Family": "lileep", -"Stage": 2, -"Element1": "rock", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lilligant": { -"0": { -"Family": "petilil", -"Stage": 4, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "petilil", -"Stage": 4, -"Element1": "grass", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lillipup": { -"0": { -"Family": "lillipup", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"linoone": { -"0": { -"Family": "zigzagoon", -"Stage": 8, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "zigzagoon", -"Stage": 8, -"Element1": "dark", -"Element2": "normal", -"BestStat": -1, -"WorstStat": -1 -} -}, -"litleo": { -"0": { -"Family": "litleo", -"Stage": 2, -"Element1": "fire", -"Element2": "normal", -"BestStat": -1, -"WorstStat": -1 -} -}, -"litten": { -"0": { -"Family": "litten", -"Stage": 2, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"litwick": { -"0": { -"Family": "litwick", -"Stage": 2, -"Element1": "ghost", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lokix": { -"0": { -"Family": "nymble", -"Stage": 4, -"Element1": "bug", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lombre": { -"0": { -"Family": "lotad", -"Stage": 8, -"Element1": "water", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lopunny": { -"0": { -"Family": "buneary", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "buneary", -"Stage": 4, -"Element1": "normal", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lotad": { -"0": { -"Family": "lotad", -"Stage": 2, -"Element1": "water", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"loudred": { -"0": { -"Family": "whismur", -"Stage": 8, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lucario": { -"0": { -"Family": "riolu", -"Stage": 4, -"Element1": "fighting", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "riolu", -"Stage": 4, -"Element1": "fighting", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ludicolo": { -"0": { -"Family": "lotad", -"Stage": 4, -"Element1": "water", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lugia": { -"0": { -"Family": "lugia", -"Stage": 1, -"Element1": "psychic", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lumineon": { -"0": { -"Family": "finneon", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lunala": { -"0": { -"Family": "cosmog", -"Stage": 4, -"Element1": "psychic", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lunatone": { -"0": { -"Family": "lunatone", -"Stage": 1, -"Element1": "rock", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lurantis": { -"0": { -"Family": "fomantis", -"Stage": 4, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"luvdisc": { -"0": { -"Family": "luvdisc", -"Stage": 1, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"luxio": { -"0": { -"Family": "shinx", -"Stage": 8, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"luxray": { -"0": { -"Family": "shinx", -"Stage": 4, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"lycanroc": { -"0": { -"Family": "rockruff", -"Stage": 4, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "rockruff", -"Stage": 4, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "rockruff", -"Stage": 4, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mabosstiff": { -"0": { -"Family": "maschiff", -"Stage": 4, -"Element1": "dark", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"machamp": { -"0": { -"Family": "machop", -"Stage": 4, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "machop", -"Stage": 4, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"machoke": { -"0": { -"Family": "machop", -"Stage": 8, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"machop": { -"0": { -"Family": "machop", -"Stage": 2, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"magby": { -"0": { -"Family": "magby", -"Stage": 2, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"magcargo": { -"0": { -"Family": "slugma", -"Stage": 4, -"Element1": "fire", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -} -}, -"magearna": { -"0": { -"Family": "magearna", -"Stage": 1, -"Element1": "steel", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "magearna", -"Stage": 1, -"Element1": "steel", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"magikarp": { -"0": { -"Family": "magikarp", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"magmar": { -"0": { -"Family": "magby", -"Stage": 8, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"magmortar": { -"0": { -"Family": "magby", -"Stage": 4, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"magnemite": { -"0": { -"Family": "magnemite", -"Stage": 2, -"Element1": "electric", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"magneton": { -"0": { -"Family": "magnemite", -"Stage": 8, -"Element1": "electric", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"magnezone": { -"0": { -"Family": "magnemite", -"Stage": 4, -"Element1": "electric", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"makuhita": { -"0": { -"Family": "makuhita", -"Stage": 2, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"malamar": { -"0": { -"Family": "inkay", -"Stage": 4, -"Element1": "dark", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mamoswine": { -"0": { -"Family": "swinub", -"Stage": 4, -"Element1": "ice", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"manaphy": { -"0": { -"Family": "manaphy", -"Stage": 1, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mandibuzz": { -"0": { -"Family": "vullaby", -"Stage": 4, -"Element1": "dark", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"manectric": { -"0": { -"Family": "electrike", -"Stage": 4, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "electrike", -"Stage": 4, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mankey": { -"0": { -"Family": "mankey", -"Stage": 2, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mantine": { -"0": { -"Family": "mantyke", -"Stage": 4, -"Element1": "water", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mantyke": { -"0": { -"Family": "mantyke", -"Stage": 2, -"Element1": "water", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"maractus": { -"0": { -"Family": "maractus", -"Stage": 1, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mareanie": { -"0": { -"Family": "mareanie", -"Stage": 2, -"Element1": "poison", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mareep": { -"0": { -"Family": "mareep", -"Stage": 2, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"marill": { -"0": { -"Family": "azurill", -"Stage": 8, -"Element1": "water", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"marowak": { -"0": { -"Family": "cubone", -"Stage": 4, -"Element1": "ground", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "cubone", -"Stage": 4, -"Element1": "fire", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"marshadow": { -"0": { -"Family": "marshadow", -"Stage": 1, -"Element1": "fighting", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"marshtomp": { -"0": { -"Family": "mudkip", -"Stage": 8, -"Element1": "water", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"maschiff": { -"0": { -"Family": "maschiff", -"Stage": 2, -"Element1": "dark", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"masquerain": { -"0": { -"Family": "surskit", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"maushold": { -"0": { -"Family": "tandemaus", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "tandemaus", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mawile": { -"0": { -"Family": "mawile", -"Stage": 1, -"Element1": "steel", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "mawile", -"Stage": 1, -"Element1": "steel", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"medicham": { -"0": { -"Family": "meditite", -"Stage": 4, -"Element1": "fighting", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "meditite", -"Stage": 4, -"Element1": "fighting", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"meditite": { -"0": { -"Family": "meditite", -"Stage": 2, -"Element1": "fighting", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"meganium": { -"0": { -"Family": "chikorita", -"Stage": 4, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"melmetal": { -"0": { -"Family": "meltan", -"Stage": 4, -"Element1": "steel", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "meltan", -"Stage": 4, -"Element1": "steel", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"meloetta": { -"0": { -"Family": "meloetta", -"Stage": 1, -"Element1": "normal", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "meloetta", -"Stage": 1, -"Element1": "normal", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"meltan": { -"0": { -"Family": "meltan", -"Stage": 2, -"Element1": "steel", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"meowscarada": { -"0": { -"Family": "sprigatito", -"Stage": 4, -"Element1": "grass", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"meowstic": { -"0": { -"Family": "espurr", -"Stage": 4, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "espurr", -"Stage": 4, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"meowth": { -"0": { -"Family": "meowth", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "meowth", -"Stage": 2, -"Element1": "dark", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "meowth", -"Stage": 2, -"Element1": "steel", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "meowth", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mesprit": { -"0": { -"Family": "mesprit", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"metagross": { -"0": { -"Family": "beldum", -"Stage": 4, -"Element1": "steel", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "beldum", -"Stage": 4, -"Element1": "steel", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"metang": { -"0": { -"Family": "beldum", -"Stage": 8, -"Element1": "steel", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"metapod": { -"0": { -"Family": "caterpie", -"Stage": 8, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mew": { -"0": { -"Family": "mew", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mewtwo": { -"0": { -"Family": "mewtwo", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "mewtwo", -"Stage": 1, -"Element1": "psychic", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "mewtwo", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mienfoo": { -"0": { -"Family": "mienfoo", -"Stage": 2, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mienshao": { -"0": { -"Family": "mienfoo", -"Stage": 4, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mightyena": { -"0": { -"Family": "poochyena", -"Stage": 4, -"Element1": "dark", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"milcery": { -"0": { -"Family": "milcery", -"Stage": 2, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"milotic": { -"0": { -"Family": "feebas", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"miltank": { -"0": { -"Family": "miltank", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mime_jr": { -"0": { -"Family": "mime_jr", -"Stage": 2, -"Element1": "psychic", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mimikyu": { -"0": { -"Family": "mimikyu", -"Stage": 1, -"Element1": "ghost", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "mimikyu", -"Stage": 1, -"Element1": "ghost", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"minccino": { -"0": { -"Family": "minccino", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"minior": { -"0": { -"Family": "minior", -"Stage": 1, -"Element1": "rock", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "minior", -"Stage": 1, -"Element1": "rock", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "minior", -"Stage": 1, -"Element1": "rock", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "minior", -"Stage": 1, -"Element1": "rock", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"4": { -"Family": "minior", -"Stage": 1, -"Element1": "rock", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"5": { -"Family": "minior", -"Stage": 1, -"Element1": "rock", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"6": { -"Family": "minior", -"Stage": 1, -"Element1": "rock", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"7": { -"Family": "minior", -"Stage": 1, -"Element1": "rock", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"8": { -"Family": "minior", -"Stage": 1, -"Element1": "rock", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"9": { -"Family": "minior", -"Stage": 1, -"Element1": "rock", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"10": { -"Family": "minior", -"Stage": 1, -"Element1": "rock", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"11": { -"Family": "minior", -"Stage": 1, -"Element1": "rock", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"12": { -"Family": "minior", -"Stage": 1, -"Element1": "rock", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"13": { -"Family": "minior", -"Stage": 1, -"Element1": "rock", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"minun": { -"0": { -"Family": "minun", -"Stage": 1, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"miraidon": { -"0": { -"Family": "miraidon", -"Stage": 1, -"Element1": "electric", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"misdreavus": { -"0": { -"Family": "misdreavus", -"Stage": 2, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mismagius": { -"0": { -"Family": "misdreavus", -"Stage": 4, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"missingno": { -"0": { -"Family": "missingno", -"Stage": 1, -"Element1": "none", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "missingno", -"Stage": 1, -"Element1": "none", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"moltres": { -"0": { -"Family": "moltres", -"Stage": 1, -"Element1": "fire", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "moltres", -"Stage": 1, -"Element1": "dark", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"monferno": { -"0": { -"Family": "chimchar", -"Stage": 8, -"Element1": "fire", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"morelull": { -"0": { -"Family": "morelull", -"Stage": 2, -"Element1": "grass", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"morgrem": { -"0": { -"Family": "impidimp", -"Stage": 8, -"Element1": "dark", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"morpeko": { -"0": { -"Family": "morpeko", -"Stage": 1, -"Element1": "electric", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "morpeko", -"Stage": 1, -"Element1": "electric", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mothim": { -"0": { -"Family": "burmy", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mr_mime": { -"0": { -"Family": "mime_jr", -"Stage": 8, -"Element1": "psychic", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "mime_jr", -"Stage": 8, -"Element1": "ice", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mr_rime": { -"0": { -"Family": "mime_jr", -"Stage": 4, -"Element1": "ice", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mudbray": { -"0": { -"Family": "mudbray", -"Stage": 2, -"Element1": "ground", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mudkip": { -"0": { -"Family": "mudkip", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"mudsdale": { -"0": { -"Family": "mudbray", -"Stage": 4, -"Element1": "ground", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"muk": { -"0": { -"Family": "grimer", -"Stage": 4, -"Element1": "poison", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "grimer", -"Stage": 4, -"Element1": "poison", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"munchlax": { -"0": { -"Family": "munchlax", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"munna": { -"0": { -"Family": "munna", -"Stage": 2, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"murkrow": { -"0": { -"Family": "murkrow", -"Stage": 2, -"Element1": "dark", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"musharna": { -"0": { -"Family": "munna", -"Stage": 4, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"nacli": { -"0": { -"Family": "nacli", -"Stage": 2, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"naclstack": { -"0": { -"Family": "nacli", -"Stage": 8, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"naganadel": { -"0": { -"Family": "poipole", -"Stage": 4, -"Element1": "poison", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"natu": { -"0": { -"Family": "natu", -"Stage": 2, -"Element1": "psychic", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"necrozma": { -"0": { -"Family": "necrozma", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "necrozma", -"Stage": 1, -"Element1": "psychic", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "necrozma", -"Stage": 1, -"Element1": "psychic", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "necrozma", -"Stage": 1, -"Element1": "psychic", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"nickit": { -"0": { -"Family": "nickit", -"Stage": 2, -"Element1": "dark", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"nidoking": { -"0": { -"Family": "nidoran_m", -"Stage": 4, -"Element1": "poison", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"nidoqueen": { -"0": { -"Family": "nidoran_f", -"Stage": 4, -"Element1": "poison", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"nidoran_f": { -"0": { -"Family": "nidoran_f", -"Stage": 2, -"Element1": "poison", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"nidoran_m": { -"0": { -"Family": "nidoran_m", -"Stage": 2, -"Element1": "poison", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"nidorina": { -"0": { -"Family": "nidoran_f", -"Stage": 8, -"Element1": "poison", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"nidorino": { -"0": { -"Family": "nidoran_m", -"Stage": 8, -"Element1": "poison", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"nihilego": { -"0": { -"Family": "nihilego", -"Stage": 1, -"Element1": "rock", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"nincada": { -"0": { -"Family": "nincada", -"Stage": 2, -"Element1": "bug", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ninetales": { -"0": { -"Family": "vulpix", -"Stage": 4, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "vulpix", -"Stage": 4, -"Element1": "ice", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ninjask": { -"0": { -"Family": "nincada", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"noctowl": { -"0": { -"Family": "hoothoot", -"Stage": 4, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"noibat": { -"0": { -"Family": "noibat", -"Stage": 2, -"Element1": "flying", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"noivern": { -"0": { -"Family": "noibat", -"Stage": 4, -"Element1": "flying", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"nosepass": { -"0": { -"Family": "nosepass", -"Stage": 2, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"numel": { -"0": { -"Family": "numel", -"Stage": 2, -"Element1": "fire", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"nuzleaf": { -"0": { -"Family": "seedot", -"Stage": 8, -"Element1": "grass", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"nymble": { -"0": { -"Family": "nymble", -"Stage": 2, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"obstagoon": { -"0": { -"Family": "zigzagoon", -"Stage": 4, -"Element1": "dark", -"Element2": "normal", -"BestStat": -1, -"WorstStat": -1 -} -}, -"octillery": { -"0": { -"Family": "remoraid", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"oddish": { -"0": { -"Family": "oddish", -"Stage": 2, -"Element1": "grass", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"oinkologne": { -"0": { -"Family": "lechonk", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "lechonk", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"omanyte": { -"0": { -"Family": "omanyte", -"Stage": 2, -"Element1": "rock", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"omastar": { -"0": { -"Family": "omanyte", -"Stage": 4, -"Element1": "rock", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"onix": { -"0": { -"Family": "onix", -"Stage": 2, -"Element1": "rock", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"oranguru": { -"0": { -"Family": "oranguru", -"Stage": 1, -"Element1": "normal", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"orbeetle": { -"0": { -"Family": "blipbug", -"Stage": 4, -"Element1": "bug", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "blipbug", -"Stage": 4, -"Element1": "bug", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"oricorio": { -"0": { -"Family": "oricorio", -"Stage": 1, -"Element1": "fire", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "oricorio", -"Stage": 1, -"Element1": "electric", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "oricorio", -"Stage": 1, -"Element1": "psychic", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "oricorio", -"Stage": 1, -"Element1": "ghost", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"orthworm": { -"0": { -"Family": "orthworm", -"Stage": 1, -"Element1": "steel", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"oshawott": { -"0": { -"Family": "oshawott", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"overqwil": { -"0": { -"Family": "qwilfish", -"Stage": 4, -"Element1": "dark", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pachirisu": { -"0": { -"Family": "pachirisu", -"Stage": 1, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"palafin": { -"0": { -"Family": "finizen", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "finizen", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"palkia": { -"0": { -"Family": "palkia", -"Stage": 1, -"Element1": "water", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "palkia", -"Stage": 1, -"Element1": "water", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"palossand": { -"0": { -"Family": "sandygast", -"Stage": 4, -"Element1": "ghost", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"palpitoad": { -"0": { -"Family": "tympole", -"Stage": 8, -"Element1": "water", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pancham": { -"0": { -"Family": "pancham", -"Stage": 2, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pangoro": { -"0": { -"Family": "pancham", -"Stage": 4, -"Element1": "fighting", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"panpour": { -"0": { -"Family": "panpour", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pansage": { -"0": { -"Family": "pansage", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pansear": { -"0": { -"Family": "pansear", -"Stage": 2, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"paras": { -"0": { -"Family": "paras", -"Stage": 2, -"Element1": "bug", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"parasect": { -"0": { -"Family": "paras", -"Stage": 4, -"Element1": "bug", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"passimian": { -"0": { -"Family": "passimian", -"Stage": 1, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"patrat": { -"0": { -"Family": "patrat", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pawmi": { -"0": { -"Family": "pawmi", -"Stage": 2, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pawmo": { -"0": { -"Family": "pawmi", -"Stage": 8, -"Element1": "electric", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pawmot": { -"0": { -"Family": "pawmi", -"Stage": 4, -"Element1": "electric", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pawniard": { -"0": { -"Family": "pawniard", -"Stage": 2, -"Element1": "dark", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pelipper": { -"0": { -"Family": "wingull", -"Stage": 4, -"Element1": "water", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"perrserker": { -"0": { -"Family": "meowth", -"Stage": 4, -"Element1": "steel", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"persian": { -"0": { -"Family": "meowth", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "meowth", -"Stage": 4, -"Element1": "dark", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"petilil": { -"0": { -"Family": "petilil", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"phanpy": { -"0": { -"Family": "phanpy", -"Stage": 2, -"Element1": "ground", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"phantump": { -"0": { -"Family": "phantump", -"Stage": 2, -"Element1": "ghost", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pheromosa": { -"0": { -"Family": "pheromosa", -"Stage": 1, -"Element1": "bug", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"phione": { -"0": { -"Family": "phione", -"Stage": 1, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pichu": { -"0": { -"Family": "pichu", -"Stage": 2, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pidgeot": { -"0": { -"Family": "pidgey", -"Stage": 4, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "pidgey", -"Stage": 4, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pidgeotto": { -"0": { -"Family": "pidgey", -"Stage": 8, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pidgey": { -"0": { -"Family": "pidgey", -"Stage": 2, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pidove": { -"0": { -"Family": "pidove", -"Stage": 2, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pignite": { -"0": { -"Family": "tepig", -"Stage": 8, -"Element1": "fire", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pikachu": { -"0": { -"Family": "pichu", -"Stage": 8, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "pichu", -"Stage": 8, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pikipek": { -"0": { -"Family": "pikipek", -"Stage": 2, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"piloswine": { -"0": { -"Family": "swinub", -"Stage": 8, -"Element1": "ice", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pincurchin": { -"0": { -"Family": "pincurchin", -"Stage": 1, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pineco": { -"0": { -"Family": "pineco", -"Stage": 2, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pinsir": { -"0": { -"Family": "pinsir", -"Stage": 1, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "pinsir", -"Stage": 1, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"piplup": { -"0": { -"Family": "piplup", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"plusle": { -"0": { -"Family": "plusle", -"Stage": 1, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"poipole": { -"0": { -"Family": "poipole", -"Stage": 2, -"Element1": "poison", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"politoed": { -"0": { -"Family": "poliwag", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"poliwag": { -"0": { -"Family": "poliwag", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"poliwhirl": { -"0": { -"Family": "poliwag", -"Stage": 8, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"poliwrath": { -"0": { -"Family": "poliwag", -"Stage": 4, -"Element1": "water", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"polteageist": { -"0": { -"Family": "sinistea", -"Stage": 4, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "sinistea", -"Stage": 4, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ponyta": { -"0": { -"Family": "ponyta", -"Stage": 2, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "ponyta", -"Stage": 2, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"poochyena": { -"0": { -"Family": "poochyena", -"Stage": 2, -"Element1": "dark", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"popplio": { -"0": { -"Family": "popplio", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"porygon_z": { -"0": { -"Family": "porygon", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"porygon": { -"0": { -"Family": "porygon", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"porygon2": { -"0": { -"Family": "porygon", -"Stage": 8, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"primarina": { -"0": { -"Family": "popplio", -"Stage": 4, -"Element1": "water", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"primeape": { -"0": { -"Family": "mankey", -"Stage": 8, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"prinplup": { -"0": { -"Family": "piplup", -"Stage": 8, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"probopass": { -"0": { -"Family": "nosepass", -"Stage": 4, -"Element1": "rock", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"psyduck": { -"0": { -"Family": "psyduck", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pumpkaboo": { -"0": { -"Family": "pumpkaboo", -"Stage": 2, -"Element1": "ghost", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "pumpkaboo", -"Stage": 2, -"Element1": "ghost", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "pumpkaboo", -"Stage": 2, -"Element1": "ghost", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "pumpkaboo", -"Stage": 2, -"Element1": "ghost", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pupitar": { -"0": { -"Family": "larvitar", -"Stage": 8, -"Element1": "rock", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"purrloin": { -"0": { -"Family": "purrloin", -"Stage": 2, -"Element1": "dark", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"purugly": { -"0": { -"Family": "glameow", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pyroar": { -"0": { -"Family": "litleo", -"Stage": 4, -"Element1": "fire", -"Element2": "normal", -"BestStat": -1, -"WorstStat": -1 -} -}, -"pyukumuku": { -"0": { -"Family": "pyukumuku", -"Stage": 1, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"quagsire": { -"0": { -"Family": "wooper", -"Stage": 4, -"Element1": "water", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"quaquaval": { -"0": { -"Family": "quaxly", -"Stage": 4, -"Element1": "water", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"quaxly": { -"0": { -"Family": "quaxly", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"quaxwell": { -"0": { -"Family": "quaxly", -"Stage": 8, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"quilava": { -"0": { -"Family": "cyndaquil", -"Stage": 8, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"quilladin": { -"0": { -"Family": "chespin", -"Stage": 8, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"qwilfish": { -"0": { -"Family": "qwilfish", -"Stage": 2, -"Element1": "water", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "qwilfish", -"Stage": 2, -"Element1": "dark", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"raboot": { -"0": { -"Family": "scorbunny", -"Stage": 8, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"rabsca": { -"0": { -"Family": "rellor", -"Stage": 4, -"Element1": "bug", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"raichu": { -"0": { -"Family": "pichu", -"Stage": 4, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "pichu", -"Stage": 4, -"Element1": "electric", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"raikou": { -"0": { -"Family": "raikou", -"Stage": 1, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ralts": { -"0": { -"Family": "ralts", -"Stage": 2, -"Element1": "psychic", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"rampardos": { -"0": { -"Family": "cranidos", -"Stage": 4, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"rapidash": { -"0": { -"Family": "ponyta", -"Stage": 4, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "ponyta", -"Stage": 4, -"Element1": "psychic", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"raticate": { -"0": { -"Family": "rattata", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "rattata", -"Stage": 4, -"Element1": "dark", -"Element2": "normal", -"BestStat": -1, -"WorstStat": -1 -} -}, -"rattata": { -"0": { -"Family": "rattata", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "rattata", -"Stage": 2, -"Element1": "dark", -"Element2": "normal", -"BestStat": -1, -"WorstStat": -1 -} -}, -"rayquaza": { -"0": { -"Family": "rayquaza", -"Stage": 1, -"Element1": "dragon", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "rayquaza", -"Stage": 1, -"Element1": "dragon", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"regice": { -"0": { -"Family": "regice", -"Stage": 1, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"regidrago": { -"0": { -"Family": "regidrago", -"Stage": 1, -"Element1": "dragon", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"regieleki": { -"0": { -"Family": "regieleki", -"Stage": 1, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"regigigas": { -"0": { -"Family": "regigigas", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"regirock": { -"0": { -"Family": "regirock", -"Stage": 1, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"registeel": { -"0": { -"Family": "registeel", -"Stage": 1, -"Element1": "steel", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"relicanth": { -"0": { -"Family": "relicanth", -"Stage": 1, -"Element1": "water", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -} -}, -"rellor": { -"0": { -"Family": "rellor", -"Stage": 2, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"remoraid": { -"0": { -"Family": "remoraid", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"reshiram": { -"0": { -"Family": "reshiram", -"Stage": 1, -"Element1": "dragon", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -} -}, -"reuniclus": { -"0": { -"Family": "solosis", -"Stage": 4, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"revavroom": { -"0": { -"Family": "varoom", -"Stage": 4, -"Element1": "steel", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"rhydon": { -"0": { -"Family": "rhyhorn", -"Stage": 8, -"Element1": "ground", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -} -}, -"rhyhorn": { -"0": { -"Family": "rhyhorn", -"Stage": 2, -"Element1": "ground", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -} -}, -"rhyperior": { -"0": { -"Family": "rhyhorn", -"Stage": 4, -"Element1": "ground", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ribombee": { -"0": { -"Family": "cutiefly", -"Stage": 4, -"Element1": "bug", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"rillaboom": { -"0": { -"Family": "grookey", -"Stage": 4, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "grookey", -"Stage": 4, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"riolu": { -"0": { -"Family": "riolu", -"Stage": 2, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"roaring_moon": { -"0": { -"Family": "roaring_moon", -"Stage": 1, -"Element1": "dragon", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"rockruff": { -"0": { -"Family": "rockruff", -"Stage": 2, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"roggenrola": { -"0": { -"Family": "roggenrola", -"Stage": 2, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"rolycoly": { -"0": { -"Family": "rolycoly", -"Stage": 2, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"rookidee": { -"0": { -"Family": "rookidee", -"Stage": 2, -"Element1": "flying", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"roselia": { -"0": { -"Family": "budew", -"Stage": 8, -"Element1": "grass", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"roserade": { -"0": { -"Family": "budew", -"Stage": 4, -"Element1": "grass", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"rotom": { -"0": { -"Family": "rotom", -"Stage": 1, -"Element1": "electric", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "rotom", -"Stage": 1, -"Element1": "electric", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "rotom", -"Stage": 1, -"Element1": "electric", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "rotom", -"Stage": 1, -"Element1": "electric", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -}, -"4": { -"Family": "rotom", -"Stage": 1, -"Element1": "electric", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"5": { -"Family": "rotom", -"Stage": 1, -"Element1": "electric", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"rowlet": { -"0": { -"Family": "rowlet", -"Stage": 2, -"Element1": "grass", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"rufflet": { -"0": { -"Family": "rufflet", -"Stage": 2, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"runerigus": { -"0": { -"Family": "yamask", -"Stage": 4, -"Element1": "ground", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sableye": { -"0": { -"Family": "sableye", -"Stage": 1, -"Element1": "dark", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "sableye", -"Stage": 1, -"Element1": "dark", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"salamence": { -"0": { -"Family": "bagon", -"Stage": 4, -"Element1": "dragon", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "bagon", -"Stage": 4, -"Element1": "dragon", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"salandit": { -"0": { -"Family": "salandit", -"Stage": 2, -"Element1": "poison", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -} -}, -"salazzle": { -"0": { -"Family": "salandit", -"Stage": 4, -"Element1": "poison", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -} -}, -"samurott": { -"0": { -"Family": "oshawott", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "oshawott", -"Stage": 4, -"Element1": "water", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sandaconda": { -"0": { -"Family": "silicobra", -"Stage": 4, -"Element1": "ground", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "silicobra", -"Stage": 4, -"Element1": "ground", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sandile": { -"0": { -"Family": "sandile", -"Stage": 2, -"Element1": "ground", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sandshrew": { -"0": { -"Family": "sandshrew", -"Stage": 2, -"Element1": "ground", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "sandshrew", -"Stage": 2, -"Element1": "ice", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sandslash": { -"0": { -"Family": "sandshrew", -"Stage": 4, -"Element1": "ground", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "sandshrew", -"Stage": 4, -"Element1": "ice", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sandy_shocks": { -"0": { -"Family": "sandy_shocks", -"Stage": 1, -"Element1": "electric", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sandygast": { -"0": { -"Family": "sandygast", -"Stage": 2, -"Element1": "ghost", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sawk": { -"0": { -"Family": "sawk", -"Stage": 1, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sawsbuck": { -"0": { -"Family": "deerling", -"Stage": 4, -"Element1": "normal", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "deerling", -"Stage": 4, -"Element1": "normal", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "deerling", -"Stage": 4, -"Element1": "normal", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "deerling", -"Stage": 4, -"Element1": "normal", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"scatterbug": { -"0": { -"Family": "scatterbug", -"Stage": 2, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sceptile": { -"0": { -"Family": "treecko", -"Stage": 4, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "treecko", -"Stage": 4, -"Element1": "grass", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"scizor": { -"0": { -"Family": "scyther", -"Stage": 4, -"Element1": "bug", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "scyther", -"Stage": 4, -"Element1": "bug", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"scolipede": { -"0": { -"Family": "venipede", -"Stage": 4, -"Element1": "bug", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"scorbunny": { -"0": { -"Family": "scorbunny", -"Stage": 2, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"scovillain": { -"0": { -"Family": "capsakid", -"Stage": 4, -"Element1": "grass", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -} -}, -"scrafty": { -"0": { -"Family": "scraggy", -"Stage": 4, -"Element1": "dark", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"scraggy": { -"0": { -"Family": "scraggy", -"Stage": 2, -"Element1": "dark", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"scream_tail": { -"0": { -"Family": "scream_tail", -"Stage": 1, -"Element1": "fairy", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"scyther": { -"0": { -"Family": "scyther", -"Stage": 2, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"seadra": { -"0": { -"Family": "horsea", -"Stage": 8, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"seaking": { -"0": { -"Family": "goldeen", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sealeo": { -"0": { -"Family": "spheal", -"Stage": 8, -"Element1": "ice", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"seedot": { -"0": { -"Family": "seedot", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"seel": { -"0": { -"Family": "seel", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"seismitoad": { -"0": { -"Family": "tympole", -"Stage": 4, -"Element1": "water", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sentret": { -"0": { -"Family": "sentret", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"serperior": { -"0": { -"Family": "snivy", -"Stage": 4, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"servine": { -"0": { -"Family": "snivy", -"Stage": 8, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"seviper": { -"0": { -"Family": "seviper", -"Stage": 1, -"Element1": "poison", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sewaddle": { -"0": { -"Family": "sewaddle", -"Stage": 2, -"Element1": "bug", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sharpedo": { -"0": { -"Family": "carvanha", -"Stage": 4, -"Element1": "water", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "carvanha", -"Stage": 4, -"Element1": "water", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"shaymin": { -"0": { -"Family": "shaymin", -"Stage": 1, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "shaymin", -"Stage": 1, -"Element1": "grass", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"shedinja": { -"0": { -"Family": "nincada", -"Stage": 4, -"Element1": "bug", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"shelgon": { -"0": { -"Family": "bagon", -"Stage": 8, -"Element1": "dragon", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"shellder": { -"0": { -"Family": "shellder", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"shellos": { -"0": { -"Family": "shellos", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "shellos", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"shelmet": { -"0": { -"Family": "shelmet", -"Stage": 2, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"shieldon": { -"0": { -"Family": "shieldon", -"Stage": 2, -"Element1": "rock", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"shiftry": { -"0": { -"Family": "seedot", -"Stage": 4, -"Element1": "grass", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"shiinotic": { -"0": { -"Family": "morelull", -"Stage": 4, -"Element1": "grass", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"shinx": { -"0": { -"Family": "shinx", -"Stage": 2, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"shroodle": { -"0": { -"Family": "shroodle", -"Stage": 2, -"Element1": "poison", -"Element2": "normal", -"BestStat": -1, -"WorstStat": -1 -} -}, -"shroomish": { -"0": { -"Family": "shroomish", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"shuckle": { -"0": { -"Family": "shuckle", -"Stage": 1, -"Element1": "bug", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -} -}, -"shuppet": { -"0": { -"Family": "shuppet", -"Stage": 2, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sigilyph": { -"0": { -"Family": "sigilyph", -"Stage": 1, -"Element1": "psychic", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"silcoon": { -"0": { -"Family": "wurmple", -"Stage": 8, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"silicobra": { -"0": { -"Family": "silicobra", -"Stage": 2, -"Element1": "ground", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"silvally": { -"0": { -"Family": "type_null", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "type_null", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "type_null", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "type_null", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"4": { -"Family": "type_null", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"5": { -"Family": "type_null", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"6": { -"Family": "type_null", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"7": { -"Family": "type_null", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"8": { -"Family": "type_null", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"9": { -"Family": "type_null", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"10": { -"Family": "type_null", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"11": { -"Family": "type_null", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"12": { -"Family": "type_null", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"13": { -"Family": "type_null", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"14": { -"Family": "type_null", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"15": { -"Family": "type_null", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"16": { -"Family": "type_null", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"17": { -"Family": "type_null", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"simipour": { -"0": { -"Family": "panpour", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"simisage": { -"0": { -"Family": "pansage", -"Stage": 4, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"simisear": { -"0": { -"Family": "pansear", -"Stage": 4, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sinistea": { -"0": { -"Family": "sinistea", -"Stage": 2, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "sinistea", -"Stage": 2, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sirfetchd": { -"0": { -"Family": "farfetchd", -"Stage": 4, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sizzlipede": { -"0": { -"Family": "sizzlipede", -"Stage": 2, -"Element1": "fire", -"Element2": "bug", -"BestStat": -1, -"WorstStat": -1 -} -}, -"skarmory": { -"0": { -"Family": "skarmory", -"Stage": 1, -"Element1": "steel", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"skeledirge": { -"0": { -"Family": "fuecoco", -"Stage": 4, -"Element1": "fire", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"skiddo": { -"0": { -"Family": "skiddo", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"skiploom": { -"0": { -"Family": "hoppip", -"Stage": 8, -"Element1": "grass", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"skitty": { -"0": { -"Family": "skitty", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"skorupi": { -"0": { -"Family": "skorupi", -"Stage": 2, -"Element1": "poison", -"Element2": "bug", -"BestStat": -1, -"WorstStat": -1 -} -}, -"skrelp": { -"0": { -"Family": "skrelp", -"Stage": 2, -"Element1": "poison", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"skuntank": { -"0": { -"Family": "stunky", -"Stage": 4, -"Element1": "poison", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"skwovet": { -"0": { -"Family": "skwovet", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"slaking": { -"0": { -"Family": "slakoth", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"slakoth": { -"0": { -"Family": "slakoth", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sliggoo": { -"0": { -"Family": "goomy", -"Stage": 8, -"Element1": "dragon", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "goomy", -"Stage": 8, -"Element1": "steel", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"slither_wing": { -"0": { -"Family": "slither_wing", -"Stage": 1, -"Element1": "bug", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"slowbro": { -"0": { -"Family": "slowpoke", -"Stage": 4, -"Element1": "water", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "slowpoke", -"Stage": 4, -"Element1": "water", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "slowpoke", -"Stage": 4, -"Element1": "poison", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"slowking": { -"0": { -"Family": "slowpoke", -"Stage": 4, -"Element1": "water", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "slowpoke", -"Stage": 4, -"Element1": "poison", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"slowpoke": { -"0": { -"Family": "slowpoke", -"Stage": 2, -"Element1": "water", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "slowpoke", -"Stage": 2, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"slugma": { -"0": { -"Family": "slugma", -"Stage": 2, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"slurpuff": { -"0": { -"Family": "swirlix", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"smeargle": { -"0": { -"Family": "smeargle", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"smoliv": { -"0": { -"Family": "smoliv", -"Stage": 2, -"Element1": "grass", -"Element2": "normal", -"BestStat": -1, -"WorstStat": -1 -} -}, -"smoochum": { -"0": { -"Family": "smoochum", -"Stage": 2, -"Element1": "ice", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sneasel": { -"0": { -"Family": "sneasel", -"Stage": 2, -"Element1": "dark", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "sneasel", -"Stage": 2, -"Element1": "fighting", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sneasler": { -"0": { -"Family": "sneasel", -"Stage": 4, -"Element1": "fighting", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"snivy": { -"0": { -"Family": "snivy", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"snom": { -"0": { -"Family": "snom", -"Stage": 2, -"Element1": "ice", -"Element2": "bug", -"BestStat": -1, -"WorstStat": -1 -} -}, -"snorlax": { -"0": { -"Family": "munchlax", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "munchlax", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"snorunt": { -"0": { -"Family": "snorunt", -"Stage": 2, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"snover": { -"0": { -"Family": "snover", -"Stage": 2, -"Element1": "grass", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -} -}, -"snubbull": { -"0": { -"Family": "snubbull", -"Stage": 2, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sobble": { -"0": { -"Family": "sobble", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"solgaleo": { -"0": { -"Family": "cosmog", -"Stage": 4, -"Element1": "psychic", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"solosis": { -"0": { -"Family": "solosis", -"Stage": 2, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"solrock": { -"0": { -"Family": "solrock", -"Stage": 1, -"Element1": "rock", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"spearow": { -"0": { -"Family": "spearow", -"Stage": 2, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"spectrier": { -"0": { -"Family": "spectrier", -"Stage": 1, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"spewpa": { -"0": { -"Family": "scatterbug", -"Stage": 8, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"spheal": { -"0": { -"Family": "spheal", -"Stage": 2, -"Element1": "ice", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"spidops": { -"0": { -"Family": "tarountula", -"Stage": 4, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"spinarak": { -"0": { -"Family": "spinarak", -"Stage": 2, -"Element1": "bug", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"spinda": { -"0": { -"Family": "spinda", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"spiritomb": { -"0": { -"Family": "spiritomb", -"Stage": 1, -"Element1": "ghost", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"spoink": { -"0": { -"Family": "spoink", -"Stage": 2, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sprigatito": { -"0": { -"Family": "sprigatito", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"spritzee": { -"0": { -"Family": "spritzee", -"Stage": 2, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"squawkabilly": { -"0": { -"Family": "squawkabilly", -"Stage": 1, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "squawkabilly", -"Stage": 1, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "squawkabilly", -"Stage": 1, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "squawkabilly", -"Stage": 1, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"squirtle": { -"0": { -"Family": "squirtle", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"stakataka": { -"0": { -"Family": "stakataka", -"Stage": 1, -"Element1": "rock", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"stantler": { -"0": { -"Family": "stantler", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"staraptor": { -"0": { -"Family": "starly", -"Stage": 4, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"staravia": { -"0": { -"Family": "starly", -"Stage": 8, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"starly": { -"0": { -"Family": "starly", -"Stage": 2, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"starmie": { -"0": { -"Family": "staryu", -"Stage": 4, -"Element1": "water", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"staryu": { -"0": { -"Family": "staryu", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"steelix": { -"0": { -"Family": "onix", -"Stage": 4, -"Element1": "steel", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "onix", -"Stage": 4, -"Element1": "steel", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"steenee": { -"0": { -"Family": "bounsweet", -"Stage": 8, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"stonjourner": { -"0": { -"Family": "stonjourner", -"Stage": 1, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"stoutland": { -"0": { -"Family": "lillipup", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"stufful": { -"0": { -"Family": "stufful", -"Stage": 2, -"Element1": "normal", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"stunfisk": { -"0": { -"Family": "stunfisk", -"Stage": 1, -"Element1": "ground", -"Element2": "electric", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "stunfisk", -"Stage": 1, -"Element1": "ground", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"stunky": { -"0": { -"Family": "stunky", -"Stage": 2, -"Element1": "poison", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sudowoodo": { -"0": { -"Family": "bonsly", -"Stage": 4, -"Element1": "rock", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"suicune": { -"0": { -"Family": "suicune", -"Stage": 1, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sunflora": { -"0": { -"Family": "sunkern", -"Stage": 4, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sunkern": { -"0": { -"Family": "sunkern", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"surskit": { -"0": { -"Family": "surskit", -"Stage": 2, -"Element1": "bug", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"swablu": { -"0": { -"Family": "swablu", -"Stage": 2, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"swadloon": { -"0": { -"Family": "sewaddle", -"Stage": 8, -"Element1": "bug", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"swalot": { -"0": { -"Family": "gulpin", -"Stage": 4, -"Element1": "poison", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"swampert": { -"0": { -"Family": "mudkip", -"Stage": 4, -"Element1": "water", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "mudkip", -"Stage": 4, -"Element1": "water", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"swanna": { -"0": { -"Family": "ducklett", -"Stage": 4, -"Element1": "water", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"swellow": { -"0": { -"Family": "taillow", -"Stage": 4, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"swinub": { -"0": { -"Family": "swinub", -"Stage": 2, -"Element1": "ice", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"swirlix": { -"0": { -"Family": "swirlix", -"Stage": 2, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"swoobat": { -"0": { -"Family": "woobat", -"Stage": 4, -"Element1": "psychic", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"sylveon": { -"0": { -"Family": "eevee", -"Stage": 4, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tadbulb": { -"0": { -"Family": "tadbulb", -"Stage": 2, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"taillow": { -"0": { -"Family": "taillow", -"Stage": 2, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"talonflame": { -"0": { -"Family": "fletchling", -"Stage": 4, -"Element1": "fire", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tandemaus": { -"0": { -"Family": "tandemaus", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tangela": { -"0": { -"Family": "tangela", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tangrowth": { -"0": { -"Family": "tangela", -"Stage": 4, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tapu_bulu": { -"0": { -"Family": "tapu_bulu", -"Stage": 1, -"Element1": "grass", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tapu_fini": { -"0": { -"Family": "tapu_fini", -"Stage": 1, -"Element1": "water", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tapu_koko": { -"0": { -"Family": "tapu_koko", -"Stage": 1, -"Element1": "electric", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tapu_lele": { -"0": { -"Family": "tapu_lele", -"Stage": 1, -"Element1": "psychic", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tarountula": { -"0": { -"Family": "tarountula", -"Stage": 2, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tatsugiri": { -"0": { -"Family": "tatsugiri", -"Stage": 1, -"Element1": "dragon", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "tatsugiri", -"Stage": 1, -"Element1": "dragon", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "tatsugiri", -"Stage": 1, -"Element1": "dragon", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tauros": { -"0": { -"Family": "tauros", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "tauros", -"Stage": 1, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "tauros", -"Stage": 1, -"Element1": "fighting", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "tauros", -"Stage": 1, -"Element1": "fighting", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"teddiursa": { -"0": { -"Family": "teddiursa", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tentacool": { -"0": { -"Family": "tentacool", -"Stage": 2, -"Element1": "water", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tentacruel": { -"0": { -"Family": "tentacool", -"Stage": 4, -"Element1": "water", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tepig": { -"0": { -"Family": "tepig", -"Stage": 2, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"terrakion": { -"0": { -"Family": "terrakion", -"Stage": 1, -"Element1": "rock", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"thievul": { -"0": { -"Family": "nickit", -"Stage": 4, -"Element1": "dark", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"throh": { -"0": { -"Family": "throh", -"Stage": 1, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"thundurus": { -"0": { -"Family": "thundurus", -"Stage": 1, -"Element1": "electric", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "thundurus", -"Stage": 1, -"Element1": "electric", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"thwackey": { -"0": { -"Family": "grookey", -"Stage": 8, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"timburr": { -"0": { -"Family": "timburr", -"Stage": 2, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ting_lu": { -"0": { -"Family": "ting_lu", -"Stage": 1, -"Element1": "dark", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tinkatink": { -"0": { -"Family": "tinkatink", -"Stage": 2, -"Element1": "fairy", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tinkaton": { -"0": { -"Family": "tinkatink", -"Stage": 4, -"Element1": "fairy", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tinkatuff": { -"0": { -"Family": "tinkatink", -"Stage": 8, -"Element1": "fairy", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tirtouga": { -"0": { -"Family": "tirtouga", -"Stage": 2, -"Element1": "water", -"Element2": "rock", -"BestStat": -1, -"WorstStat": -1 -} -}, -"toedscool": { -"0": { -"Family": "toedscool", -"Stage": 2, -"Element1": "ground", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"toedscruel": { -"0": { -"Family": "toedscool", -"Stage": 4, -"Element1": "ground", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"togedemaru": { -"0": { -"Family": "togedemaru", -"Stage": 1, -"Element1": "electric", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"togekiss": { -"0": { -"Family": "togepi", -"Stage": 4, -"Element1": "fairy", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"togepi": { -"0": { -"Family": "togepi", -"Stage": 2, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"togetic": { -"0": { -"Family": "togepi", -"Stage": 8, -"Element1": "fairy", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"torchic": { -"0": { -"Family": "torchic", -"Stage": 2, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"torkoal": { -"0": { -"Family": "torkoal", -"Stage": 1, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tornadus": { -"0": { -"Family": "tornadus", -"Stage": 1, -"Element1": "flying", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "tornadus", -"Stage": 1, -"Element1": "flying", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"torracat": { -"0": { -"Family": "litten", -"Stage": 8, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"torterra": { -"0": { -"Family": "turtwig", -"Stage": 4, -"Element1": "grass", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"totodile": { -"0": { -"Family": "totodile", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"toucannon": { -"0": { -"Family": "pikipek", -"Stage": 4, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"toxapex": { -"0": { -"Family": "mareanie", -"Stage": 4, -"Element1": "poison", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"toxel": { -"0": { -"Family": "toxel", -"Stage": 2, -"Element1": "electric", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"toxicroak": { -"0": { -"Family": "croagunk", -"Stage": 4, -"Element1": "poison", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"toxtricity": { -"0": { -"Family": "toxel", -"Stage": 4, -"Element1": "electric", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "toxel", -"Stage": 4, -"Element1": "electric", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "toxel", -"Stage": 4, -"Element1": "electric", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "toxel", -"Stage": 4, -"Element1": "electric", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tranquill": { -"0": { -"Family": "pidove", -"Stage": 8, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"trapinch": { -"0": { -"Family": "trapinch", -"Stage": 2, -"Element1": "ground", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"treecko": { -"0": { -"Family": "treecko", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"trevenant": { -"0": { -"Family": "phantump", -"Stage": 4, -"Element1": "ghost", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tropius": { -"0": { -"Family": "tropius", -"Stage": 1, -"Element1": "grass", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"trubbish": { -"0": { -"Family": "trubbish", -"Stage": 2, -"Element1": "poison", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"trumbeak": { -"0": { -"Family": "pikipek", -"Stage": 8, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tsareena": { -"0": { -"Family": "bounsweet", -"Stage": 4, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"turtonator": { -"0": { -"Family": "turtonator", -"Stage": 1, -"Element1": "fire", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"turtwig": { -"0": { -"Family": "turtwig", -"Stage": 2, -"Element1": "grass", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tympole": { -"0": { -"Family": "tympole", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tynamo": { -"0": { -"Family": "tynamo", -"Stage": 2, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"type_null": { -"0": { -"Family": "type_null", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"typhlosion": { -"0": { -"Family": "cyndaquil", -"Stage": 4, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "cyndaquil", -"Stage": 4, -"Element1": "fire", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tyranitar": { -"0": { -"Family": "larvitar", -"Stage": 4, -"Element1": "rock", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "larvitar", -"Stage": 4, -"Element1": "rock", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tyrantrum": { -"0": { -"Family": "tyrunt", -"Stage": 4, -"Element1": "rock", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tyrogue": { -"0": { -"Family": "tyrogue", -"Stage": 2, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"tyrunt": { -"0": { -"Family": "tyrunt", -"Stage": 2, -"Element1": "rock", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"umbreon": { -"0": { -"Family": "eevee", -"Stage": 4, -"Element1": "dark", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"unfezant": { -"0": { -"Family": "pidove", -"Stage": 4, -"Element1": "normal", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"unown": { -"0": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"4": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"5": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"6": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"7": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"8": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"9": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"10": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"11": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"12": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"13": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"14": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"15": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"16": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"17": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"18": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"19": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"20": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"21": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"22": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"23": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"24": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"25": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"26": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"27": { -"Family": "unown", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ursaluna": { -"0": { -"Family": "teddiursa", -"Stage": 4, -"Element1": "ground", -"Element2": "normal", -"BestStat": -1, -"WorstStat": -1 -} -}, -"ursaring": { -"0": { -"Family": "teddiursa", -"Stage": 8, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"urshifu": { -"0": { -"Family": "kubfu", -"Stage": 4, -"Element1": "fighting", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "kubfu", -"Stage": 4, -"Element1": "fighting", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "kubfu", -"Stage": 4, -"Element1": "fighting", -"Element2": "dark", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "kubfu", -"Stage": 4, -"Element1": "fighting", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"uxie": { -"0": { -"Family": "uxie", -"Stage": 1, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"vanillish": { -"0": { -"Family": "vanillite", -"Stage": 8, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"vanillite": { -"0": { -"Family": "vanillite", -"Stage": 2, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"vanilluxe": { -"0": { -"Family": "vanillite", -"Stage": 4, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"vaporeon": { -"0": { -"Family": "eevee", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"varoom": { -"0": { -"Family": "varoom", -"Stage": 2, -"Element1": "steel", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"veluza": { -"0": { -"Family": "veluza", -"Stage": 1, -"Element1": "water", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"venipede": { -"0": { -"Family": "venipede", -"Stage": 2, -"Element1": "bug", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"venomoth": { -"0": { -"Family": "venonat", -"Stage": 4, -"Element1": "bug", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"venonat": { -"0": { -"Family": "venonat", -"Stage": 2, -"Element1": "bug", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"venusaur": { -"0": { -"Family": "bulbasaur", -"Stage": 4, -"Element1": "grass", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "bulbasaur", -"Stage": 4, -"Element1": "grass", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "bulbasaur", -"Stage": 4, -"Element1": "grass", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"vespiquen": { -"0": { -"Family": "combee", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"vibrava": { -"0": { -"Family": "trapinch", -"Stage": 8, -"Element1": "ground", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"victini": { -"0": { -"Family": "victini", -"Stage": 1, -"Element1": "psychic", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -} -}, -"victreebel": { -"0": { -"Family": "bellsprout", -"Stage": 4, -"Element1": "grass", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"vigoroth": { -"0": { -"Family": "slakoth", -"Stage": 8, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"vikavolt": { -"0": { -"Family": "grubbin", -"Stage": 4, -"Element1": "bug", -"Element2": "electric", -"BestStat": -1, -"WorstStat": -1 -} -}, -"vileplume": { -"0": { -"Family": "oddish", -"Stage": 4, -"Element1": "grass", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"virizion": { -"0": { -"Family": "virizion", -"Stage": 1, -"Element1": "grass", -"Element2": "fighting", -"BestStat": -1, -"WorstStat": -1 -} -}, -"vivillon": { -"0": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"4": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"5": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"6": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"7": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"8": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"9": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"10": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"11": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"12": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"13": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"14": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"15": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"16": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"17": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"18": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"19": { -"Family": "scatterbug", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"volbeat": { -"0": { -"Family": "volbeat", -"Stage": 1, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"volcanion": { -"0": { -"Family": "volcanion", -"Stage": 1, -"Element1": "fire", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"volcarona": { -"0": { -"Family": "larvesta", -"Stage": 4, -"Element1": "bug", -"Element2": "fire", -"BestStat": -1, -"WorstStat": -1 -} -}, -"voltorb": { -"0": { -"Family": "voltorb", -"Stage": 2, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "voltorb", -"Stage": 2, -"Element1": "electric", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"vullaby": { -"0": { -"Family": "vullaby", -"Stage": 2, -"Element1": "dark", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"vulpix": { -"0": { -"Family": "vulpix", -"Stage": 2, -"Element1": "fire", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "vulpix", -"Stage": 2, -"Element1": "ice", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"wailmer": { -"0": { -"Family": "wailmer", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"wailord": { -"0": { -"Family": "wailmer", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"walking_wake": { -"0": { -"Family": "walking_wake", -"Stage": 1, -"Element1": "water", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"walrein": { -"0": { -"Family": "spheal", -"Stage": 4, -"Element1": "ice", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"wartortle": { -"0": { -"Family": "squirtle", -"Stage": 8, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"watchog": { -"0": { -"Family": "patrat", -"Stage": 4, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"wattrel": { -"0": { -"Family": "wattrel", -"Stage": 2, -"Element1": "electric", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"weavile": { -"0": { -"Family": "sneasel", -"Stage": 4, -"Element1": "dark", -"Element2": "ice", -"BestStat": -1, -"WorstStat": -1 -} -}, -"weedle": { -"0": { -"Family": "weedle", -"Stage": 2, -"Element1": "bug", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"weepinbell": { -"0": { -"Family": "bellsprout", -"Stage": 8, -"Element1": "grass", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"weezing": { -"0": { -"Family": "koffing", -"Stage": 4, -"Element1": "poison", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "koffing", -"Stage": 4, -"Element1": "poison", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"whimsicott": { -"0": { -"Family": "cottonee", -"Stage": 4, -"Element1": "grass", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"whirlipede": { -"0": { -"Family": "venipede", -"Stage": 8, -"Element1": "bug", -"Element2": "poison", -"BestStat": -1, -"WorstStat": -1 -} -}, -"whiscash": { -"0": { -"Family": "barboach", -"Stage": 4, -"Element1": "water", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"whismur": { -"0": { -"Family": "whismur", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"wigglytuff": { -"0": { -"Family": "igglybuff", -"Stage": 4, -"Element1": "normal", -"Element2": "fairy", -"BestStat": -1, -"WorstStat": -1 -} -}, -"wiglett": { -"0": { -"Family": "wiglett", -"Stage": 2, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"wimpod": { -"0": { -"Family": "wimpod", -"Stage": 2, -"Element1": "bug", -"Element2": "water", -"BestStat": -1, -"WorstStat": -1 -} -}, -"wingull": { -"0": { -"Family": "wingull", -"Stage": 2, -"Element1": "water", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"wishiwashi": { -"0": { -"Family": "wishiwashi", -"Stage": 1, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "wishiwashi", -"Stage": 1, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"wo_chien": { -"0": { -"Family": "wo_chien", -"Stage": 1, -"Element1": "dark", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"wobbuffet": { -"0": { -"Family": "wynaut", -"Stage": 4, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"woobat": { -"0": { -"Family": "woobat", -"Stage": 2, -"Element1": "psychic", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"wooloo": { -"0": { -"Family": "wooloo", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"wooper": { -"0": { -"Family": "wooper", -"Stage": 2, -"Element1": "water", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "wooper", -"Stage": 2, -"Element1": "poison", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -}, -"wormadam": { -"0": { -"Family": "burmy", -"Stage": 4, -"Element1": "bug", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "burmy", -"Stage": 4, -"Element1": "bug", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "burmy", -"Stage": 4, -"Element1": "bug", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"wugtrio": { -"0": { -"Family": "wiglett", -"Stage": 4, -"Element1": "water", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"wurmple": { -"0": { -"Family": "wurmple", -"Stage": 2, -"Element1": "bug", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"wynaut": { -"0": { -"Family": "wynaut", -"Stage": 2, -"Element1": "psychic", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"wyrdeer": { -"0": { -"Family": "stantler", -"Stage": 4, -"Element1": "normal", -"Element2": "psychic", -"BestStat": -1, -"WorstStat": -1 -} -}, -"xatu": { -"0": { -"Family": "natu", -"Stage": 4, -"Element1": "psychic", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"xerneas": { -"0": { -"Family": "xerneas", -"Stage": 1, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "xerneas", -"Stage": 1, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"xurkitree": { -"0": { -"Family": "xurkitree", -"Stage": 1, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"yamask": { -"0": { -"Family": "yamask", -"Stage": 2, -"Element1": "ghost", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "yamask", -"Stage": 2, -"Element1": "ground", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"yamper": { -"0": { -"Family": "yamper", -"Stage": 2, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"yanma": { -"0": { -"Family": "yanma", -"Stage": 2, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"yanmega": { -"0": { -"Family": "yanma", -"Stage": 4, -"Element1": "bug", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"yungoos": { -"0": { -"Family": "yungoos", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"yveltal": { -"0": { -"Family": "yveltal", -"Stage": 1, -"Element1": "dark", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"zacian": { -"0": { -"Family": "zacian", -"Stage": 1, -"Element1": "fairy", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "zacian", -"Stage": 1, -"Element1": "fairy", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"zamazenta": { -"0": { -"Family": "zamazenta", -"Stage": 1, -"Element1": "fighting", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "zamazenta", -"Stage": 1, -"Element1": "fighting", -"Element2": "steel", -"BestStat": -1, -"WorstStat": -1 -} -}, -"zangoose": { -"0": { -"Family": "zangoose", -"Stage": 1, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"zapdos": { -"0": { -"Family": "zapdos", -"Stage": 1, -"Element1": "electric", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "zapdos", -"Stage": 1, -"Element1": "fighting", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"zarude": { -"0": { -"Family": "zarude", -"Stage": 1, -"Element1": "dark", -"Element2": "grass", -"BestStat": -1, -"WorstStat": -1 -} -}, -"zebstrika": { -"0": { -"Family": "blitzle", -"Stage": 4, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"zekrom": { -"0": { -"Family": "zekrom", -"Stage": 1, -"Element1": "dragon", -"Element2": "electric", -"BestStat": -1, -"WorstStat": -1 -} -}, -"zeraora": { -"0": { -"Family": "zeraora", -"Stage": 1, -"Element1": "electric", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -} -}, -"zigzagoon": { -"0": { -"Family": "zigzagoon", -"Stage": 2, -"Element1": "normal", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "zigzagoon", -"Stage": 2, -"Element1": "dark", -"Element2": "normal", -"BestStat": -1, -"WorstStat": -1 -} -}, -"zoroark": { -"0": { -"Family": "zorua", -"Stage": 4, -"Element1": "dark", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "zorua", -"Stage": 4, -"Element1": "normal", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"zorua": { -"0": { -"Family": "zorua", -"Stage": 2, -"Element1": "dark", -"Element2": "none", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "zorua", -"Stage": 2, -"Element1": "normal", -"Element2": "ghost", -"BestStat": -1, -"WorstStat": -1 -} -}, -"zubat": { -"0": { -"Family": "zubat", -"Stage": 2, -"Element1": "poison", -"Element2": "flying", -"BestStat": -1, -"WorstStat": -1 -} -}, -"zweilous": { -"0": { -"Family": "deino", -"Stage": 8, -"Element1": "dark", -"Element2": "dragon", -"BestStat": -1, -"WorstStat": -1 -} -}, -"zygarde": { -"0": { -"Family": "zygarde", -"Stage": 1, -"Element1": "dragon", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -}, -"1": { -"Family": "zygarde", -"Stage": 1, -"Element1": "dragon", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -}, -"2": { -"Family": "zygarde", -"Stage": 1, -"Element1": "dragon", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -}, -"3": { -"Family": "zygarde", -"Stage": 1, -"Element1": "dragon", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -}, -"4": { -"Family": "zygarde", -"Stage": 1, -"Element1": "dragon", -"Element2": "ground", -"BestStat": -1, -"WorstStat": -1 -} -} -} -} -} \ No newline at end of file diff --git a/MODS/Enable_Mission_Board/Data/Misc/Rarity.json b/MODS/Enable_Mission_Board/Data/Misc/Rarity.json deleted file mode 100644 index 26048acad2..0000000000 --- a/MODS/Enable_Mission_Board/Data/Misc/Rarity.json +++ /dev/null @@ -1,7157 +0,0 @@ -{ -"Version": "0.8.0.0", -"Object": { -"$type": "PMDC.Data.RarityData, PMDC", -"RarityMap": { -"abra": { -"3": [ -"xcl_family_abra_00" -], -"2": [ -"xcl_family_abra_01", -"xcl_family_abra_04" -], -"1": [ -"xcl_family_abra_02", -"xcl_family_abra_03" -] -}, -"kadabra": { -"3": [ -"xcl_family_abra_00" -], -"2": [ -"xcl_family_abra_01", -"xcl_family_abra_04" -], -"1": [ -"xcl_family_abra_02", -"xcl_family_abra_03" -] -}, -"alakazam": { -"3": [ -"xcl_family_abra_00" -], -"2": [ -"xcl_family_abra_01", -"xcl_family_abra_04" -], -"1": [ -"xcl_family_abra_02", -"xcl_family_abra_03" -] -}, -"absol": { -"3": [ -"xcl_family_absol_00" -], -"2": [ -"xcl_family_absol_01", -"xcl_family_absol_04" -], -"1": [ -"xcl_family_absol_02", -"xcl_family_absol_03" -] -}, -"aerodactyl": { -"3": [ -"xcl_family_aerodactyl_00" -], -"2": [ -"xcl_family_aerodactyl_01", -"xcl_family_aerodactyl_04" -], -"1": [ -"xcl_family_aerodactyl_02", -"xcl_family_aerodactyl_03" -] -}, -"aipom": { -"3": [ -"xcl_family_aipom_00" -], -"2": [ -"xcl_family_aipom_01" -], -"1": [ -"xcl_family_aipom_02", -"xcl_family_aipom_03" -] -}, -"ambipom": { -"3": [ -"xcl_family_aipom_00" -], -"2": [ -"xcl_family_aipom_01" -], -"1": [ -"xcl_family_aipom_02", -"xcl_family_aipom_03" -] -}, -"amaura": { -"3": [ -"xcl_family_amaura_00" -], -"2": [ -"xcl_family_amaura_01", -"xcl_family_amaura_04" -], -"1": [ -"xcl_family_amaura_02", -"xcl_family_amaura_03" -] -}, -"aurorus": { -"3": [ -"xcl_family_amaura_00" -], -"2": [ -"xcl_family_amaura_01", -"xcl_family_amaura_04" -], -"1": [ -"xcl_family_amaura_02", -"xcl_family_amaura_03" -] -}, -"anorith": { -"3": [ -"xcl_family_anorith_00" -], -"2": [ -"xcl_family_anorith_01", -"xcl_family_anorith_04" -], -"1": [ -"xcl_family_anorith_02", -"xcl_family_anorith_03" -] -}, -"armaldo": { -"3": [ -"xcl_family_anorith_00" -], -"2": [ -"xcl_family_anorith_01", -"xcl_family_anorith_04" -], -"1": [ -"xcl_family_anorith_02", -"xcl_family_anorith_03" -] -}, -"appletun": { -"3": [ -"xcl_family_applin_00" -], -"2": [ -"xcl_family_applin_01", -"xcl_family_applin_04" -], -"1": [ -"xcl_family_applin_05", -"xcl_family_applin_06" -] -}, -"applin": { -"2": [ -"xcl_family_applin_01", -"xcl_family_applin_03", -"xcl_family_applin_04" -], -"1": [ -"xcl_family_applin_05", -"xcl_family_applin_06" -] -}, -"flapple": { -"3": [ -"xcl_family_applin_02" -], -"2": [ -"xcl_family_applin_03", -"xcl_family_applin_04" -], -"1": [ -"xcl_family_applin_05", -"xcl_family_applin_06" -] -}, -"aron": { -"3": [ -"xcl_family_aron_00" -], -"2": [ -"xcl_family_aron_01", -"xcl_family_aron_04" -], -"1": [ -"xcl_family_aron_02", -"xcl_family_aron_03" -] -}, -"lairon": { -"3": [ -"xcl_family_aron_00" -], -"2": [ -"xcl_family_aron_01", -"xcl_family_aron_04" -], -"1": [ -"xcl_family_aron_02", -"xcl_family_aron_03" -] -}, -"aggron": { -"3": [ -"xcl_family_aron_00" -], -"2": [ -"xcl_family_aron_01", -"xcl_family_aron_04" -], -"1": [ -"xcl_family_aron_02", -"xcl_family_aron_03" -] -}, -"audino": { -"3": [ -"xcl_family_audino_00" -], -"2": [ -"xcl_family_audino_01", -"xcl_family_audino_04" -], -"1": [ -"xcl_family_audino_02", -"xcl_family_audino_03" -] -}, -"bagon": { -"3": [ -"xcl_family_bagon_00" -], -"2": [ -"xcl_family_bagon_01" -], -"1": [ -"xcl_family_bagon_02", -"xcl_family_bagon_03" -] -}, -"shelgon": { -"3": [ -"xcl_family_bagon_00" -], -"2": [ -"xcl_family_bagon_01" -], -"1": [ -"xcl_family_bagon_02", -"xcl_family_bagon_03" -] -}, -"salamence": { -"3": [ -"xcl_family_bagon_00" -], -"2": [ -"xcl_family_bagon_01" -], -"1": [ -"xcl_family_bagon_02", -"xcl_family_bagon_03" -] -}, -"baltoy": { -"3": [ -"xcl_family_baltoy_00" -], -"2": [ -"xcl_family_baltoy_01" -], -"1": [ -"xcl_family_baltoy_02", -"xcl_family_baltoy_03" -] -}, -"claydol": { -"3": [ -"xcl_family_baltoy_00" -], -"2": [ -"xcl_family_baltoy_01" -], -"1": [ -"xcl_family_baltoy_02", -"xcl_family_baltoy_03" -] -}, -"barboach": { -"3": [ -"xcl_family_barboach_00" -], -"2": [ -"xcl_family_barboach_01" -], -"1": [ -"xcl_family_barboach_02", -"xcl_family_barboach_03" -] -}, -"whiscash": { -"3": [ -"xcl_family_barboach_00" -], -"2": [ -"xcl_family_barboach_01" -], -"1": [ -"xcl_family_barboach_02", -"xcl_family_barboach_03" -] -}, -"beldum": { -"3": [ -"xcl_family_beldum_00" -], -"2": [ -"xcl_family_beldum_01" -], -"1": [ -"xcl_family_beldum_02", -"xcl_family_beldum_03" -] -}, -"metang": { -"3": [ -"xcl_family_beldum_00" -], -"2": [ -"xcl_family_beldum_01" -], -"1": [ -"xcl_family_beldum_02", -"xcl_family_beldum_03" -] -}, -"metagross": { -"3": [ -"xcl_family_beldum_00" -], -"2": [ -"xcl_family_beldum_01" -], -"1": [ -"xcl_family_beldum_02", -"xcl_family_beldum_03" -] -}, -"bellsprout": { -"3": [ -"xcl_family_bellsprout_00" -], -"2": [ -"xcl_family_bellsprout_01", -"xcl_family_bellsprout_04" -], -"1": [ -"xcl_family_bellsprout_02", -"xcl_family_bellsprout_03" -] -}, -"weepinbell": { -"3": [ -"xcl_family_bellsprout_00" -], -"2": [ -"xcl_family_bellsprout_01", -"xcl_family_bellsprout_04" -], -"1": [ -"xcl_family_bellsprout_02", -"xcl_family_bellsprout_03" -] -}, -"victreebel": { -"3": [ -"xcl_family_bellsprout_00" -], -"2": [ -"xcl_family_bellsprout_01", -"xcl_family_bellsprout_04" -], -"1": [ -"xcl_family_bellsprout_02", -"xcl_family_bellsprout_03" -] -}, -"bidoof": { -"3": [ -"xcl_family_bidoof_00" -], -"2": [ -"xcl_family_bidoof_01", -"xcl_family_bidoof_04" -], -"1": [ -"xcl_family_bidoof_02", -"xcl_family_bidoof_03" -] -}, -"bibarel": { -"3": [ -"xcl_family_bidoof_00" -], -"2": [ -"xcl_family_bidoof_01", -"xcl_family_bidoof_04" -], -"1": [ -"xcl_family_bidoof_02", -"xcl_family_bidoof_03" -] -}, -"articuno": { -"3": [ -"xcl_family_bird_trio_00" -], -"2": [ -"xcl_family_bird_trio_01", -"xcl_family_bird_trio_02", -"xcl_family_bird_trio_11" -], -"1": [ -"xcl_family_bird_trio_09", -"xcl_family_bird_trio_10" -] -}, -"zapdos": { -"3": [ -"xcl_family_bird_trio_03" -], -"2": [ -"xcl_family_bird_trio_04", -"xcl_family_bird_trio_05", -"xcl_family_bird_trio_11" -], -"1": [ -"xcl_family_bird_trio_09", -"xcl_family_bird_trio_10" -] -}, -"moltres": { -"3": [ -"xcl_family_bird_trio_06" -], -"2": [ -"xcl_family_bird_trio_07", -"xcl_family_bird_trio_08", -"xcl_family_bird_trio_11" -], -"1": [ -"xcl_family_bird_trio_09", -"xcl_family_bird_trio_10" -] -}, -"bronzor": { -"3": [ -"xcl_family_bronzor_00" -], -"2": [ -"xcl_family_bronzor_01" -], -"1": [ -"xcl_family_bronzor_02", -"xcl_family_bronzor_03" -] -}, -"bronzong": { -"3": [ -"xcl_family_bronzor_00" -], -"2": [ -"xcl_family_bronzor_01" -], -"1": [ -"xcl_family_bronzor_02", -"xcl_family_bronzor_03" -] -}, -"buizel": { -"3": [ -"xcl_family_buizel_00" -], -"2": [ -"xcl_family_buizel_01", -"xcl_family_buizel_04" -], -"1": [ -"xcl_family_buizel_02", -"xcl_family_buizel_03" -] -}, -"floatzel": { -"3": [ -"xcl_family_buizel_00" -], -"2": [ -"xcl_family_buizel_01", -"xcl_family_buizel_04" -], -"1": [ -"xcl_family_buizel_02", -"xcl_family_buizel_03" -] -}, -"bulbasaur": { -"3": [ -"xcl_family_bulbasaur_00" -], -"2": [ -"xcl_family_bulbasaur_01", -"xcl_family_bulbasaur_04" -], -"1": [ -"xcl_family_bulbasaur_02", -"xcl_family_bulbasaur_03" -] -}, -"ivysaur": { -"3": [ -"xcl_family_bulbasaur_00" -], -"2": [ -"xcl_family_bulbasaur_01", -"xcl_family_bulbasaur_04" -], -"1": [ -"xcl_family_bulbasaur_02", -"xcl_family_bulbasaur_03" -] -}, -"venusaur": { -"3": [ -"xcl_family_bulbasaur_00" -], -"2": [ -"xcl_family_bulbasaur_01", -"xcl_family_bulbasaur_04" -], -"1": [ -"xcl_family_bulbasaur_02", -"xcl_family_bulbasaur_03" -] -}, -"buneary": { -"3": [ -"xcl_family_buneary_00" -], -"2": [ -"xcl_family_buneary_01", -"xcl_family_buneary_04" -], -"1": [ -"xcl_family_buneary_02", -"xcl_family_buneary_03" -] -}, -"lopunny": { -"3": [ -"xcl_family_buneary_00" -], -"2": [ -"xcl_family_buneary_01", -"xcl_family_buneary_04" -], -"1": [ -"xcl_family_buneary_02", -"xcl_family_buneary_03" -] -}, -"burmy": { -"3": [ -"xcl_family_burmy_00", -"xcl_family_burmy_01" -], -"1": [ -"xcl_family_burmy_02", -"xcl_family_burmy_03" -], -"2": [ -"xcl_family_burmy_04" -] -}, -"wormadam": { -"3": [ -"xcl_family_burmy_00", -"xcl_family_burmy_01" -], -"1": [ -"xcl_family_burmy_02", -"xcl_family_burmy_03" -], -"2": [ -"xcl_family_burmy_04" -] -}, -"mothim": { -"3": [ -"xcl_family_burmy_00", -"xcl_family_burmy_01" -], -"1": [ -"xcl_family_burmy_02", -"xcl_family_burmy_03" -], -"2": [ -"xcl_family_burmy_04" -] -}, -"cacnea": { -"3": [ -"xcl_family_cacnea_00" -], -"2": [ -"xcl_family_cacnea_01" -], -"1": [ -"xcl_family_cacnea_02", -"xcl_family_cacnea_03" -] -}, -"cacturne": { -"3": [ -"xcl_family_cacnea_00" -], -"2": [ -"xcl_family_cacnea_01" -], -"1": [ -"xcl_family_cacnea_02", -"xcl_family_cacnea_03" -] -}, -"carbink": { -"3": [ -"xcl_family_carbink_00" -], -"2": [ -"xcl_family_carbink_01" -], -"1": [ -"xcl_family_carbink_02", -"xcl_family_carbink_03" -] -}, -"carnivine": { -"3": [ -"xcl_family_carnivine_00" -], -"2": [ -"xcl_family_carnivine_01", -"xcl_family_carnivine_04" -], -"1": [ -"xcl_family_carnivine_02", -"xcl_family_carnivine_03" -] -}, -"carvanha": { -"3": [ -"xcl_family_carvanha_00" -], -"2": [ -"xcl_family_carvanha_01", -"xcl_family_carvanha_04" -], -"1": [ -"xcl_family_carvanha_02", -"xcl_family_carvanha_03" -] -}, -"sharpedo": { -"3": [ -"xcl_family_carvanha_00" -], -"2": [ -"xcl_family_carvanha_01", -"xcl_family_carvanha_04" -], -"1": [ -"xcl_family_carvanha_02", -"xcl_family_carvanha_03" -] -}, -"castform": { -"3": [ -"xcl_family_castform_00" -], -"2": [ -"xcl_family_castform_01", -"xcl_family_castform_04" -], -"1": [ -"xcl_family_castform_02", -"xcl_family_castform_03" -] -}, -"caterpie": { -"3": [ -"xcl_family_caterpie_00" -], -"2": [ -"xcl_family_caterpie_01" -], -"1": [ -"xcl_family_caterpie_02", -"xcl_family_caterpie_03" -] -}, -"metapod": { -"3": [ -"xcl_family_caterpie_00" -], -"2": [ -"xcl_family_caterpie_01" -], -"1": [ -"xcl_family_caterpie_02", -"xcl_family_caterpie_03" -] -}, -"butterfree": { -"3": [ -"xcl_family_caterpie_00" -], -"2": [ -"xcl_family_caterpie_01" -], -"1": [ -"xcl_family_caterpie_02", -"xcl_family_caterpie_03" -] -}, -"celebi": { -"3": [ -"xcl_family_celebi_00" -], -"2": [ -"xcl_family_celebi_01", -"xcl_family_celebi_04" -], -"1": [ -"xcl_family_celebi_02", -"xcl_family_celebi_03" -] -}, -"happiny": { -"3": [ -"xcl_family_chansey_00" -], -"2": [ -"xcl_family_chansey_01", -"xcl_family_chansey_04" -], -"1": [ -"xcl_family_chansey_02", -"xcl_family_chansey_03" -] -}, -"chansey": { -"3": [ -"xcl_family_chansey_00" -], -"2": [ -"xcl_family_chansey_01", -"xcl_family_chansey_04" -], -"1": [ -"xcl_family_chansey_02", -"xcl_family_chansey_03" -] -}, -"blissey": { -"3": [ -"xcl_family_chansey_00" -], -"2": [ -"xcl_family_chansey_01", -"xcl_family_chansey_04" -], -"1": [ -"xcl_family_chansey_02", -"xcl_family_chansey_03" -] -}, -"charmander": { -"3": [ -"xcl_family_charmander_00" -], -"2": [ -"xcl_family_charmander_01", -"xcl_family_charmander_04" -], -"1": [ -"xcl_family_charmander_02", -"xcl_family_charmander_03" -] -}, -"charmeleon": { -"3": [ -"xcl_family_charmander_00" -], -"2": [ -"xcl_family_charmander_01", -"xcl_family_charmander_04" -], -"1": [ -"xcl_family_charmander_02", -"xcl_family_charmander_03" -] -}, -"charizard": { -"3": [ -"xcl_family_charmander_00" -], -"2": [ -"xcl_family_charmander_01", -"xcl_family_charmander_04" -], -"1": [ -"xcl_family_charmander_02", -"xcl_family_charmander_03" -] -}, -"chatot": { -"3": [ -"xcl_family_chatot_00" -], -"2": [ -"xcl_family_chatot_01" -], -"1": [ -"xcl_family_chatot_02", -"xcl_family_chatot_03" -] -}, -"cherubi": { -"3": [ -"xcl_family_cherubi_00" -], -"2": [ -"xcl_family_cherubi_01", -"xcl_family_cherubi_04" -], -"1": [ -"xcl_family_cherubi_02", -"xcl_family_cherubi_03" -] -}, -"cherrim": { -"3": [ -"xcl_family_cherubi_00" -], -"2": [ -"xcl_family_cherubi_01", -"xcl_family_cherubi_04" -], -"1": [ -"xcl_family_cherubi_02", -"xcl_family_cherubi_03" -] -}, -"chikorita": { -"3": [ -"xcl_family_chikorita_00" -], -"2": [ -"xcl_family_chikorita_01", -"xcl_family_chikorita_04" -], -"1": [ -"xcl_family_chikorita_02", -"xcl_family_chikorita_03" -] -}, -"bayleef": { -"3": [ -"xcl_family_chikorita_00" -], -"2": [ -"xcl_family_chikorita_01", -"xcl_family_chikorita_04" -], -"1": [ -"xcl_family_chikorita_02", -"xcl_family_chikorita_03" -] -}, -"meganium": { -"3": [ -"xcl_family_chikorita_00" -], -"2": [ -"xcl_family_chikorita_01", -"xcl_family_chikorita_04" -], -"1": [ -"xcl_family_chikorita_02", -"xcl_family_chikorita_03" -] -}, -"chimchar": { -"3": [ -"xcl_family_chimchar_00" -], -"2": [ -"xcl_family_chimchar_01", -"xcl_family_chimchar_04" -], -"1": [ -"xcl_family_chimchar_02", -"xcl_family_chimchar_03" -] -}, -"monferno": { -"3": [ -"xcl_family_chimchar_00" -], -"2": [ -"xcl_family_chimchar_01", -"xcl_family_chimchar_04" -], -"1": [ -"xcl_family_chimchar_02", -"xcl_family_chimchar_03" -] -}, -"infernape": { -"3": [ -"xcl_family_chimchar_00" -], -"2": [ -"xcl_family_chimchar_01", -"xcl_family_chimchar_04" -], -"1": [ -"xcl_family_chimchar_02", -"xcl_family_chimchar_03" -] -}, -"chingling": { -"3": [ -"xcl_family_chimecho_00" -], -"2": [ -"xcl_family_chimecho_01", -"xcl_family_chimecho_04" -], -"1": [ -"xcl_family_chimecho_02", -"xcl_family_chimecho_03" -] -}, -"chimecho": { -"3": [ -"xcl_family_chimecho_00" -], -"2": [ -"xcl_family_chimecho_01", -"xcl_family_chimecho_04" -], -"1": [ -"xcl_family_chimecho_02", -"xcl_family_chimecho_03" -] -}, -"chinchou": { -"3": [ -"xcl_family_chinchou_00" -], -"2": [ -"xcl_family_chinchou_01" -], -"1": [ -"xcl_family_chinchou_02", -"xcl_family_chinchou_03" -] -}, -"lanturn": { -"3": [ -"xcl_family_chinchou_00" -], -"2": [ -"xcl_family_chinchou_01" -], -"1": [ -"xcl_family_chinchou_02", -"xcl_family_chinchou_03" -] -}, -"clamperl": { -"3": [ -"xcl_family_clamperl_00", -"xcl_family_clamperl_01" -], -"1": [ -"xcl_family_clamperl_02", -"xcl_family_clamperl_03" -], -"2": [ -"xcl_family_clamperl_04" -] -}, -"huntail": { -"3": [ -"xcl_family_clamperl_00", -"xcl_family_clamperl_01" -], -"1": [ -"xcl_family_clamperl_02", -"xcl_family_clamperl_03" -], -"2": [ -"xcl_family_clamperl_04" -] -}, -"gorebyss": { -"3": [ -"xcl_family_clamperl_00", -"xcl_family_clamperl_01" -], -"1": [ -"xcl_family_clamperl_02", -"xcl_family_clamperl_03" -], -"2": [ -"xcl_family_clamperl_04" -] -}, -"cleffa": { -"3": [ -"xcl_family_clefairy_00" -], -"2": [ -"xcl_family_clefairy_01", -"xcl_family_clefairy_04" -], -"1": [ -"xcl_family_clefairy_02", -"xcl_family_clefairy_03" -] -}, -"clefairy": { -"3": [ -"xcl_family_clefairy_00" -], -"2": [ -"xcl_family_clefairy_01", -"xcl_family_clefairy_04" -], -"1": [ -"xcl_family_clefairy_02", -"xcl_family_clefairy_03" -] -}, -"clefable": { -"3": [ -"xcl_family_clefairy_00" -], -"2": [ -"xcl_family_clefairy_01", -"xcl_family_clefairy_04" -], -"1": [ -"xcl_family_clefairy_02", -"xcl_family_clefairy_03" -] -}, -"combee": { -"3": [ -"xcl_family_combee_00" -], -"2": [ -"xcl_family_combee_01", -"xcl_family_combee_04" -], -"1": [ -"xcl_family_combee_02", -"xcl_family_combee_03" -] -}, -"vespiquen": { -"3": [ -"xcl_family_combee_00" -], -"2": [ -"xcl_family_combee_01", -"xcl_family_combee_04" -], -"1": [ -"xcl_family_combee_02", -"xcl_family_combee_03" -] -}, -"comfey": { -"3": [ -"xcl_family_comfey_00" -], -"2": [ -"xcl_family_comfey_01", -"xcl_family_comfey_04" -], -"1": [ -"xcl_family_comfey_02", -"xcl_family_comfey_03" -] -}, -"corphish": { -"3": [ -"xcl_family_corphish_00" -], -"2": [ -"xcl_family_corphish_01" -], -"1": [ -"xcl_family_corphish_02", -"xcl_family_corphish_03" -] -}, -"crawdaunt": { -"3": [ -"xcl_family_corphish_00" -], -"2": [ -"xcl_family_corphish_01" -], -"1": [ -"xcl_family_corphish_02", -"xcl_family_corphish_03" -] -}, -"corsola": { -"3": [ -"xcl_family_corsola_00" -], -"2": [ -"xcl_family_corsola_01", -"xcl_family_corsola_04" -], -"1": [ -"xcl_family_corsola_02", -"xcl_family_corsola_03" -] -}, -"cranidos": { -"3": [ -"xcl_family_cranidos_00" -], -"2": [ -"xcl_family_cranidos_01", -"xcl_family_cranidos_04" -], -"1": [ -"xcl_family_cranidos_02", -"xcl_family_cranidos_03" -] -}, -"rampardos": { -"3": [ -"xcl_family_cranidos_00" -], -"2": [ -"xcl_family_cranidos_01", -"xcl_family_cranidos_04" -], -"1": [ -"xcl_family_cranidos_02", -"xcl_family_cranidos_03" -] -}, -"cresselia": { -"3": [ -"xcl_family_cresselia_00" -], -"2": [ -"xcl_family_cresselia_01", -"xcl_family_cresselia_04" -], -"1": [ -"xcl_family_cresselia_02", -"xcl_family_cresselia_03" -] -}, -"croagunk": { -"3": [ -"xcl_family_croagunk_00" -], -"2": [ -"xcl_family_croagunk_01" -], -"1": [ -"xcl_family_croagunk_02", -"xcl_family_croagunk_03" -] -}, -"toxicroak": { -"3": [ -"xcl_family_croagunk_00" -], -"2": [ -"xcl_family_croagunk_01" -], -"1": [ -"xcl_family_croagunk_02", -"xcl_family_croagunk_03" -] -}, -"cubone": { -"3": [ -"xcl_family_cubone_00" -], -"2": [ -"xcl_family_cubone_01" -], -"1": [ -"xcl_family_cubone_02", -"xcl_family_cubone_03" -] -}, -"marowak": { -"3": [ -"xcl_family_cubone_00" -], -"2": [ -"xcl_family_cubone_01" -], -"1": [ -"xcl_family_cubone_02", -"xcl_family_cubone_03" -] -}, -"cutiefly": { -"1": [ -"xcl_family_cutiefly_02", -"xcl_family_cutiefly_03" -], -"2": [ -"xcl_family_cutiefly_04" -] -}, -"ribombee": { -"1": [ -"xcl_family_cutiefly_02", -"xcl_family_cutiefly_03" -], -"2": [ -"xcl_family_cutiefly_04" -] -}, -"cyndaquil": { -"3": [ -"xcl_family_cyndaquil_00" -], -"2": [ -"xcl_family_cyndaquil_01", -"xcl_family_cyndaquil_04" -], -"1": [ -"xcl_family_cyndaquil_02", -"xcl_family_cyndaquil_03" -] -}, -"quilava": { -"3": [ -"xcl_family_cyndaquil_00" -], -"2": [ -"xcl_family_cyndaquil_01", -"xcl_family_cyndaquil_04" -], -"1": [ -"xcl_family_cyndaquil_02", -"xcl_family_cyndaquil_03" -] -}, -"typhlosion": { -"3": [ -"xcl_family_cyndaquil_00" -], -"2": [ -"xcl_family_cyndaquil_01", -"xcl_family_cyndaquil_04" -], -"1": [ -"xcl_family_cyndaquil_02", -"xcl_family_cyndaquil_03" -] -}, -"darkrai": { -"3": [ -"xcl_family_darkrai_00" -], -"2": [ -"xcl_family_darkrai_01", -"xcl_family_darkrai_04" -], -"1": [ -"xcl_family_darkrai_02", -"xcl_family_darkrai_03" -] -}, -"deerling": { -"3": [ -"xcl_family_deerling_00" -], -"2": [ -"xcl_family_deerling_01", -"xcl_family_deerling_04" -], -"1": [ -"xcl_family_deerling_02", -"xcl_family_deerling_03" -] -}, -"sawsbuck": { -"3": [ -"xcl_family_deerling_00" -], -"2": [ -"xcl_family_deerling_01", -"xcl_family_deerling_04" -], -"1": [ -"xcl_family_deerling_02", -"xcl_family_deerling_03" -] -}, -"delibird": { -"3": [ -"xcl_family_delibird_00" -], -"2": [ -"xcl_family_delibird_01", -"xcl_family_delibird_04" -], -"1": [ -"xcl_family_delibird_02", -"xcl_family_delibird_03" -] -}, -"deoxys": { -"3": [ -"xcl_family_deoxys_00" -], -"2": [ -"xcl_family_deoxys_01", -"xcl_family_deoxys_04" -], -"1": [ -"xcl_family_deoxys_02", -"xcl_family_deoxys_03" -] -}, -"dhelmise": { -"3": [ -"xcl_family_dhelmise_00" -], -"2": [ -"xcl_family_dhelmise_01", -"xcl_family_dhelmise_04" -], -"1": [ -"xcl_family_dhelmise_02", -"xcl_family_dhelmise_03" -] -}, -"dialga": { -"3": [ -"xcl_family_dialga_00" -], -"2": [ -"xcl_family_dialga_01", -"xcl_family_dialga_04" -], -"1": [ -"xcl_family_dialga_02", -"xcl_family_dialga_03" -] -}, -"diglett": { -"3": [ -"xcl_family_diglett_00" -], -"2": [ -"xcl_family_diglett_01" -], -"1": [ -"xcl_family_diglett_02", -"xcl_family_diglett_03" -] -}, -"dugtrio": { -"3": [ -"xcl_family_diglett_00" -], -"2": [ -"xcl_family_diglett_01" -], -"1": [ -"xcl_family_diglett_02", -"xcl_family_diglett_03" -] -}, -"ditto": { -"2": [ -"xcl_family_ditto_00", -"xcl_family_ditto_01", -"xcl_family_ditto_02", -"xcl_family_ditto_03", -"xcl_family_ditto_04", -"xcl_family_ditto_05" -] -}, -"doduo": { -"3": [ -"xcl_family_doduo_00" -], -"2": [ -"xcl_family_doduo_01", -"xcl_family_doduo_04" -], -"1": [ -"xcl_family_doduo_02", -"xcl_family_doduo_03" -] -}, -"dodrio": { -"3": [ -"xcl_family_doduo_00" -], -"2": [ -"xcl_family_doduo_01", -"xcl_family_doduo_04" -], -"1": [ -"xcl_family_doduo_02", -"xcl_family_doduo_03" -] -}, -"drampa": { -"3": [ -"xcl_family_drampa_00" -], -"2": [ -"xcl_family_drampa_01" -], -"1": [ -"xcl_family_drampa_02", -"xcl_family_drampa_03" -] -}, -"dratini": { -"3": [ -"xcl_family_dratini_00" -], -"2": [ -"xcl_family_dratini_01", -"xcl_family_dratini_04" -], -"1": [ -"xcl_family_dratini_02", -"xcl_family_dratini_03" -] -}, -"dragonair": { -"3": [ -"xcl_family_dratini_00" -], -"2": [ -"xcl_family_dratini_01", -"xcl_family_dratini_04" -], -"1": [ -"xcl_family_dratini_02", -"xcl_family_dratini_03" -] -}, -"dragonite": { -"3": [ -"xcl_family_dratini_00" -], -"2": [ -"xcl_family_dratini_01", -"xcl_family_dratini_04" -], -"1": [ -"xcl_family_dratini_02", -"xcl_family_dratini_03" -] -}, -"drifloon": { -"3": [ -"xcl_family_drifloon_00" -], -"2": [ -"xcl_family_drifloon_01", -"xcl_family_drifloon_04" -], -"1": [ -"xcl_family_drifloon_02", -"xcl_family_drifloon_03" -] -}, -"drifblim": { -"3": [ -"xcl_family_drifloon_00" -], -"2": [ -"xcl_family_drifloon_01", -"xcl_family_drifloon_04" -], -"1": [ -"xcl_family_drifloon_02", -"xcl_family_drifloon_03" -] -}, -"drowzee": { -"3": [ -"xcl_family_drowzee_00" -], -"2": [ -"xcl_family_drowzee_01" -], -"1": [ -"xcl_family_drowzee_02", -"xcl_family_drowzee_03" -] -}, -"hypno": { -"3": [ -"xcl_family_drowzee_00" -], -"2": [ -"xcl_family_drowzee_01" -], -"1": [ -"xcl_family_drowzee_02", -"xcl_family_drowzee_03" -] -}, -"druddigon": { -"3": [ -"xcl_family_druddigon_00" -], -"2": [ -"xcl_family_druddigon_01" -], -"1": [ -"xcl_family_druddigon_02", -"xcl_family_druddigon_03", -"xcl_family_druddigon_04" -] -}, -"dunsparce": { -"3": [ -"xcl_family_dunsparce_00" -], -"2": [ -"xcl_family_dunsparce_01", -"xcl_family_dunsparce_04" -], -"1": [ -"xcl_family_dunsparce_02", -"xcl_family_dunsparce_03" -] -}, -"duskull": { -"3": [ -"xcl_family_duskull_00" -], -"2": [ -"xcl_family_duskull_01" -], -"1": [ -"xcl_family_duskull_02", -"xcl_family_duskull_03" -] -}, -"dusclops": { -"3": [ -"xcl_family_duskull_00" -], -"2": [ -"xcl_family_duskull_01" -], -"1": [ -"xcl_family_duskull_02", -"xcl_family_duskull_03" -] -}, -"dusknoir": { -"3": [ -"xcl_family_duskull_00" -], -"2": [ -"xcl_family_duskull_01" -], -"1": [ -"xcl_family_duskull_02", -"xcl_family_duskull_03" -] -}, -"sylveon": { -"3": [ -"xcl_family_eevee_00" -], -"2": [ -"xcl_family_eevee_01", -"xcl_family_eevee_24" -], -"1": [ -"xcl_family_eevee_02", -"xcl_family_eevee_25", -"xcl_family_eevee_26" -] -}, -"eevee": { -"1": [ -"xcl_family_eevee_02", -"xcl_family_eevee_05", -"xcl_family_eevee_08", -"xcl_family_eevee_11", -"xcl_family_eevee_14", -"xcl_family_eevee_17", -"xcl_family_eevee_20", -"xcl_family_eevee_23", -"xcl_family_eevee_25", -"xcl_family_eevee_26" -], -"2": [ -"xcl_family_eevee_24" -] -}, -"glaceon": { -"3": [ -"xcl_family_eevee_03" -], -"2": [ -"xcl_family_eevee_04", -"xcl_family_eevee_24" -], -"1": [ -"xcl_family_eevee_05", -"xcl_family_eevee_25", -"xcl_family_eevee_26" -] -}, -"leafeon": { -"3": [ -"xcl_family_eevee_06" -], -"2": [ -"xcl_family_eevee_07", -"xcl_family_eevee_24" -], -"1": [ -"xcl_family_eevee_08", -"xcl_family_eevee_25", -"xcl_family_eevee_26" -] -}, -"umbreon": { -"3": [ -"xcl_family_eevee_09" -], -"2": [ -"xcl_family_eevee_10", -"xcl_family_eevee_24" -], -"1": [ -"xcl_family_eevee_11", -"xcl_family_eevee_25", -"xcl_family_eevee_26" -] -}, -"espeon": { -"3": [ -"xcl_family_eevee_12" -], -"2": [ -"xcl_family_eevee_13", -"xcl_family_eevee_24" -], -"1": [ -"xcl_family_eevee_14", -"xcl_family_eevee_25", -"xcl_family_eevee_26" -] -}, -"flareon": { -"3": [ -"xcl_family_eevee_15" -], -"2": [ -"xcl_family_eevee_16", -"xcl_family_eevee_24" -], -"1": [ -"xcl_family_eevee_17", -"xcl_family_eevee_25", -"xcl_family_eevee_26" -] -}, -"jolteon": { -"3": [ -"xcl_family_eevee_18" -], -"2": [ -"xcl_family_eevee_19", -"xcl_family_eevee_24" -], -"1": [ -"xcl_family_eevee_20", -"xcl_family_eevee_25", -"xcl_family_eevee_26" -] -}, -"vaporeon": { -"3": [ -"xcl_family_eevee_21" -], -"2": [ -"xcl_family_eevee_22", -"xcl_family_eevee_24" -], -"1": [ -"xcl_family_eevee_23", -"xcl_family_eevee_25", -"xcl_family_eevee_26" -] -}, -"eiscue": { -"3": [ -"xcl_family_eiscue_00" -], -"2": [ -"xcl_family_eiscue_01" -], -"1": [ -"xcl_family_eiscue_02", -"xcl_family_eiscue_03" -] -}, -"ekans": { -"3": [ -"xcl_family_ekans_00" -], -"2": [ -"xcl_family_ekans_01", -"xcl_family_ekans_04" -], -"1": [ -"xcl_family_ekans_02", -"xcl_family_ekans_03" -] -}, -"arbok": { -"3": [ -"xcl_family_ekans_00" -], -"2": [ -"xcl_family_ekans_01", -"xcl_family_ekans_04" -], -"1": [ -"xcl_family_ekans_02", -"xcl_family_ekans_03" -] -}, -"elekid": { -"3": [ -"xcl_family_electabuzz_00" -], -"2": [ -"xcl_family_electabuzz_01", -"xcl_family_electabuzz_04" -], -"1": [ -"xcl_family_electabuzz_02", -"xcl_family_electabuzz_03" -] -}, -"electabuzz": { -"3": [ -"xcl_family_electabuzz_00" -], -"2": [ -"xcl_family_electabuzz_01", -"xcl_family_electabuzz_04" -], -"1": [ -"xcl_family_electabuzz_02", -"xcl_family_electabuzz_03" -] -}, -"electivire": { -"3": [ -"xcl_family_electabuzz_00" -], -"2": [ -"xcl_family_electabuzz_01", -"xcl_family_electabuzz_04" -], -"1": [ -"xcl_family_electabuzz_02", -"xcl_family_electabuzz_03" -] -}, -"electrike": { -"3": [ -"xcl_family_electrike_00" -], -"2": [ -"xcl_family_electrike_01" -], -"1": [ -"xcl_family_electrike_02", -"xcl_family_electrike_03" -] -}, -"manectric": { -"3": [ -"xcl_family_electrike_00" -], -"2": [ -"xcl_family_electrike_01" -], -"1": [ -"xcl_family_electrike_02", -"xcl_family_electrike_03" -] -}, -"exeggcute": { -"3": [ -"xcl_family_exeggcute_00" -], -"2": [ -"xcl_family_exeggcute_01" -], -"1": [ -"xcl_family_exeggcute_02", -"xcl_family_exeggcute_03" -] -}, -"exeggutor": { -"3": [ -"xcl_family_exeggcute_00" -], -"2": [ -"xcl_family_exeggcute_01" -], -"1": [ -"xcl_family_exeggcute_02", -"xcl_family_exeggcute_03" -] -}, -"farfetchd": { -"3": [ -"xcl_family_farfetchd_00" -], -"2": [ -"xcl_family_farfetchd_01", -"xcl_family_farfetchd_04" -], -"1": [ -"xcl_family_farfetchd_02", -"xcl_family_farfetchd_03" -] -}, -"sirfetchd": { -"3": [ -"xcl_family_farfetchd_00" -], -"2": [ -"xcl_family_farfetchd_01", -"xcl_family_farfetchd_04" -], -"1": [ -"xcl_family_farfetchd_02", -"xcl_family_farfetchd_03" -] -}, -"feebas": { -"3": [ -"xcl_family_feebas_00" -], -"2": [ -"xcl_family_feebas_01", -"xcl_family_feebas_04" -], -"1": [ -"xcl_family_feebas_02", -"xcl_family_feebas_03" -] -}, -"milotic": { -"3": [ -"xcl_family_feebas_00" -], -"2": [ -"xcl_family_feebas_01", -"xcl_family_feebas_04" -], -"1": [ -"xcl_family_feebas_02", -"xcl_family_feebas_03" -] -}, -"fennekin": { -"3": [ -"xcl_family_fennekin_00" -], -"2": [ -"xcl_family_fennekin_01", -"xcl_family_fennekin_04" -], -"1": [ -"xcl_family_fennekin_02", -"xcl_family_fennekin_03" -] -}, -"braixen": { -"3": [ -"xcl_family_fennekin_00" -], -"2": [ -"xcl_family_fennekin_01", -"xcl_family_fennekin_04" -], -"1": [ -"xcl_family_fennekin_02", -"xcl_family_fennekin_03" -] -}, -"delphox": { -"3": [ -"xcl_family_fennekin_00" -], -"2": [ -"xcl_family_fennekin_01", -"xcl_family_fennekin_04" -], -"1": [ -"xcl_family_fennekin_02", -"xcl_family_fennekin_03" -] -}, -"ferroseed": { -"3": [ -"xcl_family_ferroseed_00" -], -"2": [ -"xcl_family_ferroseed_01", -"xcl_family_ferroseed_04" -], -"1": [ -"xcl_family_ferroseed_02", -"xcl_family_ferroseed_03" -] -}, -"ferrothorn": { -"3": [ -"xcl_family_ferroseed_00" -], -"2": [ -"xcl_family_ferroseed_01", -"xcl_family_ferroseed_04" -], -"1": [ -"xcl_family_ferroseed_02", -"xcl_family_ferroseed_03" -] -}, -"finneon": { -"3": [ -"xcl_family_finneon_00" -], -"2": [ -"xcl_family_finneon_01", -"xcl_family_finneon_04" -], -"1": [ -"xcl_family_finneon_02", -"xcl_family_finneon_03" -] -}, -"lumineon": { -"3": [ -"xcl_family_finneon_00" -], -"2": [ -"xcl_family_finneon_01", -"xcl_family_finneon_04" -], -"1": [ -"xcl_family_finneon_02", -"xcl_family_finneon_03" -] -}, -"flabebe": { -"3": [ -"xcl_family_flabebe_00" -], -"2": [ -"xcl_family_flabebe_01", -"xcl_family_flabebe_04" -], -"1": [ -"xcl_family_flabebe_02", -"xcl_family_flabebe_03" -] -}, -"floette": { -"3": [ -"xcl_family_flabebe_00" -], -"2": [ -"xcl_family_flabebe_01", -"xcl_family_flabebe_04" -], -"1": [ -"xcl_family_flabebe_02", -"xcl_family_flabebe_03" -] -}, -"florges": { -"3": [ -"xcl_family_flabebe_00" -], -"2": [ -"xcl_family_flabebe_01", -"xcl_family_flabebe_04" -], -"1": [ -"xcl_family_flabebe_02", -"xcl_family_flabebe_03" -] -}, -"gastly": { -"3": [ -"xcl_family_gastly_00" -], -"2": [ -"xcl_family_gastly_01" -], -"1": [ -"xcl_family_gastly_02", -"xcl_family_gastly_03" -] -}, -"haunter": { -"3": [ -"xcl_family_gastly_00" -], -"2": [ -"xcl_family_gastly_01" -], -"1": [ -"xcl_family_gastly_02", -"xcl_family_gastly_03" -] -}, -"gengar": { -"3": [ -"xcl_family_gastly_00" -], -"2": [ -"xcl_family_gastly_01" -], -"1": [ -"xcl_family_gastly_02", -"xcl_family_gastly_03" -] -}, -"geodude": { -"3": [ -"xcl_family_geodude_00" -], -"2": [ -"xcl_family_geodude_01", -"xcl_family_geodude_04" -], -"1": [ -"xcl_family_geodude_02", -"xcl_family_geodude_03" -] -}, -"graveler": { -"3": [ -"xcl_family_geodude_00" -], -"2": [ -"xcl_family_geodude_01", -"xcl_family_geodude_04" -], -"1": [ -"xcl_family_geodude_02", -"xcl_family_geodude_03" -] -}, -"golem": { -"3": [ -"xcl_family_geodude_00" -], -"2": [ -"xcl_family_geodude_01", -"xcl_family_geodude_04" -], -"1": [ -"xcl_family_geodude_02", -"xcl_family_geodude_03" -] -}, -"gible": { -"3": [ -"xcl_family_gible_00" -], -"2": [ -"xcl_family_gible_01", -"xcl_family_gible_04" -], -"1": [ -"xcl_family_gible_02", -"xcl_family_gible_03" -] -}, -"gabite": { -"3": [ -"xcl_family_gible_00" -], -"2": [ -"xcl_family_gible_01", -"xcl_family_gible_04" -], -"1": [ -"xcl_family_gible_02", -"xcl_family_gible_03" -] -}, -"garchomp": { -"3": [ -"xcl_family_gible_00" -], -"2": [ -"xcl_family_gible_01", -"xcl_family_gible_04" -], -"1": [ -"xcl_family_gible_02", -"xcl_family_gible_03" -] -}, -"girafarig": { -"3": [ -"xcl_family_girafarig_00" -], -"2": [ -"xcl_family_girafarig_01", -"xcl_family_girafarig_04" -], -"1": [ -"xcl_family_girafarig_02", -"xcl_family_girafarig_03" -] -}, -"farigiraf": { -"3": [ -"xcl_family_girafarig_00" -], -"2": [ -"xcl_family_girafarig_01", -"xcl_family_girafarig_04" -], -"1": [ -"xcl_family_girafarig_02", -"xcl_family_girafarig_03" -] -}, -"giratina": { -"3": [ -"xcl_family_giratina_00" -], -"2": [ -"xcl_family_giratina_01", -"xcl_family_giratina_04" -], -"1": [ -"xcl_family_giratina_02", -"xcl_family_giratina_03" -] -}, -"glameow": { -"3": [ -"xcl_family_glameow_00" -], -"2": [ -"xcl_family_glameow_01" -], -"1": [ -"xcl_family_glameow_02", -"xcl_family_glameow_03" -] -}, -"purugly": { -"3": [ -"xcl_family_glameow_00" -], -"2": [ -"xcl_family_glameow_01" -], -"1": [ -"xcl_family_glameow_02", -"xcl_family_glameow_03" -] -}, -"gligar": { -"3": [ -"xcl_family_gligar_00" -], -"2": [ -"xcl_family_gligar_01" -], -"1": [ -"xcl_family_gligar_02", -"xcl_family_gligar_03" -] -}, -"gliscor": { -"3": [ -"xcl_family_gligar_00" -], -"2": [ -"xcl_family_gligar_01" -], -"1": [ -"xcl_family_gligar_02", -"xcl_family_gligar_03" -] -}, -"glimmet": { -"3": [ -"xcl_family_glimmet_00" -], -"2": [ -"xcl_family_glimmet_01" -], -"1": [ -"xcl_family_glimmet_02", -"xcl_family_glimmet_03" -] -}, -"glimmora": { -"3": [ -"xcl_family_glimmet_00" -], -"2": [ -"xcl_family_glimmet_01" -], -"1": [ -"xcl_family_glimmet_02", -"xcl_family_glimmet_03" -] -}, -"goldeen": { -"3": [ -"xcl_family_goldeen_00" -], -"2": [ -"xcl_family_goldeen_01", -"xcl_family_goldeen_04" -], -"1": [ -"xcl_family_goldeen_02", -"xcl_family_goldeen_03" -] -}, -"seaking": { -"3": [ -"xcl_family_goldeen_00" -], -"2": [ -"xcl_family_goldeen_01", -"xcl_family_goldeen_04" -], -"1": [ -"xcl_family_goldeen_02", -"xcl_family_goldeen_03" -] -}, -"grimer": { -"3": [ -"xcl_family_grimer_00" -], -"2": [ -"xcl_family_grimer_01", -"xcl_family_grimer_04" -], -"1": [ -"xcl_family_grimer_02", -"xcl_family_grimer_03" -] -}, -"muk": { -"3": [ -"xcl_family_grimer_00" -], -"2": [ -"xcl_family_grimer_01", -"xcl_family_grimer_04" -], -"1": [ -"xcl_family_grimer_02", -"xcl_family_grimer_03" -] -}, -"groudon": { -"3": [ -"xcl_family_groudon_00" -], -"2": [ -"xcl_family_groudon_01" -], -"1": [ -"xcl_family_groudon_02", -"xcl_family_groudon_03" -] -}, -"growlithe": { -"3": [ -"xcl_family_growlithe_00" -], -"2": [ -"xcl_family_growlithe_01", -"xcl_family_growlithe_04" -], -"1": [ -"xcl_family_growlithe_02", -"xcl_family_growlithe_03" -] -}, -"arcanine": { -"3": [ -"xcl_family_growlithe_00" -], -"2": [ -"xcl_family_growlithe_01", -"xcl_family_growlithe_04" -], -"1": [ -"xcl_family_growlithe_02", -"xcl_family_growlithe_03" -] -}, -"gulpin": { -"3": [ -"xcl_family_gulpin_00" -], -"2": [ -"xcl_family_gulpin_01" -], -"1": [ -"xcl_family_gulpin_02", -"xcl_family_gulpin_03" -] -}, -"swalot": { -"3": [ -"xcl_family_gulpin_00" -], -"2": [ -"xcl_family_gulpin_01" -], -"1": [ -"xcl_family_gulpin_02", -"xcl_family_gulpin_03" -] -}, -"hatenna": { -"3": [ -"xcl_family_hatenna_00" -], -"2": [ -"xcl_family_hatenna_01", -"xcl_family_hatenna_04" -], -"1": [ -"xcl_family_hatenna_02", -"xcl_family_hatenna_03" -] -}, -"hattrem": { -"3": [ -"xcl_family_hatenna_00" -], -"2": [ -"xcl_family_hatenna_01", -"xcl_family_hatenna_04" -], -"1": [ -"xcl_family_hatenna_02", -"xcl_family_hatenna_03" -] -}, -"hatterene": { -"3": [ -"xcl_family_hatenna_00" -], -"2": [ -"xcl_family_hatenna_01", -"xcl_family_hatenna_04" -], -"1": [ -"xcl_family_hatenna_02", -"xcl_family_hatenna_03" -] -}, -"heatran": { -"3": [ -"xcl_family_heatran_00" -], -"2": [ -"xcl_family_heatran_01", -"xcl_family_heatran_04" -], -"1": [ -"xcl_family_heatran_02", -"xcl_family_heatran_03" -] -}, -"heracross": { -"3": [ -"xcl_family_heracross_00" -], -"2": [ -"xcl_family_heracross_01", -"xcl_family_heracross_04" -], -"1": [ -"xcl_family_heracross_02", -"xcl_family_heracross_03" -] -}, -"hippopotas": { -"3": [ -"xcl_family_hippopotas_00" -], -"2": [ -"xcl_family_hippopotas_01" -], -"1": [ -"xcl_family_hippopotas_02", -"xcl_family_hippopotas_03" -] -}, -"hippowdon": { -"3": [ -"xcl_family_hippopotas_00" -], -"2": [ -"xcl_family_hippopotas_01" -], -"1": [ -"xcl_family_hippopotas_02", -"xcl_family_hippopotas_03" -] -}, -"ho_oh": { -"3": [ -"xcl_family_ho_oh_beasts_00" -], -"2": [ -"xcl_family_ho_oh_beasts_01", -"xcl_family_ho_oh_beasts_04" -], -"1": [ -"xcl_family_ho_oh_beasts_02", -"xcl_family_ho_oh_beasts_03", -"xcl_family_ho_oh_beasts_05" -] -}, -"raikou": { -"3": [ -"xcl_family_ho_oh_beasts_06" -], -"2": [ -"xcl_family_ho_oh_beasts_07", -"xcl_family_ho_oh_beasts_18" -], -"1": [ -"xcl_family_ho_oh_beasts_08", -"xcl_family_ho_oh_beasts_09" -] -}, -"entei": { -"3": [ -"xcl_family_ho_oh_beasts_10" -], -"2": [ -"xcl_family_ho_oh_beasts_11", -"xcl_family_ho_oh_beasts_18" -], -"1": [ -"xcl_family_ho_oh_beasts_12", -"xcl_family_ho_oh_beasts_13" -] -}, -"suicune": { -"3": [ -"xcl_family_ho_oh_beasts_14" -], -"2": [ -"xcl_family_ho_oh_beasts_15", -"xcl_family_ho_oh_beasts_18" -], -"1": [ -"xcl_family_ho_oh_beasts_16", -"xcl_family_ho_oh_beasts_17" -] -}, -"honedge": { -"3": [ -"xcl_family_honedge_00" -], -"2": [ -"xcl_family_honedge_01" -], -"1": [ -"xcl_family_honedge_02", -"xcl_family_honedge_03" -] -}, -"doublade": { -"3": [ -"xcl_family_honedge_00" -], -"2": [ -"xcl_family_honedge_01" -], -"1": [ -"xcl_family_honedge_02", -"xcl_family_honedge_03" -] -}, -"aegislash": { -"3": [ -"xcl_family_honedge_00" -], -"2": [ -"xcl_family_honedge_01" -], -"1": [ -"xcl_family_honedge_02", -"xcl_family_honedge_03" -] -}, -"hoothoot": { -"3": [ -"xcl_family_hoothoot_00" -], -"2": [ -"xcl_family_hoothoot_01" -], -"1": [ -"xcl_family_hoothoot_02", -"xcl_family_hoothoot_03" -] -}, -"noctowl": { -"3": [ -"xcl_family_hoothoot_00" -], -"2": [ -"xcl_family_hoothoot_01" -], -"1": [ -"xcl_family_hoothoot_02", -"xcl_family_hoothoot_03" -] -}, -"hoppip": { -"3": [ -"xcl_family_hoppip_00" -], -"2": [ -"xcl_family_hoppip_01", -"xcl_family_hoppip_04" -], -"1": [ -"xcl_family_hoppip_02", -"xcl_family_hoppip_03" -] -}, -"skiploom": { -"3": [ -"xcl_family_hoppip_00" -], -"2": [ -"xcl_family_hoppip_01", -"xcl_family_hoppip_04" -], -"1": [ -"xcl_family_hoppip_02", -"xcl_family_hoppip_03" -] -}, -"jumpluff": { -"3": [ -"xcl_family_hoppip_00" -], -"2": [ -"xcl_family_hoppip_01", -"xcl_family_hoppip_04" -], -"1": [ -"xcl_family_hoppip_02", -"xcl_family_hoppip_03" -] -}, -"horsea": { -"3": [ -"xcl_family_horsea_00" -], -"2": [ -"xcl_family_horsea_01" -], -"1": [ -"xcl_family_horsea_02", -"xcl_family_horsea_03" -] -}, -"seadra": { -"3": [ -"xcl_family_horsea_00" -], -"2": [ -"xcl_family_horsea_01" -], -"1": [ -"xcl_family_horsea_02", -"xcl_family_horsea_03" -] -}, -"kingdra": { -"3": [ -"xcl_family_horsea_00" -], -"2": [ -"xcl_family_horsea_01" -], -"1": [ -"xcl_family_horsea_02", -"xcl_family_horsea_03" -] -}, -"houndour": { -"3": [ -"xcl_family_houndour_00" -], -"2": [ -"xcl_family_houndour_01" -], -"1": [ -"xcl_family_houndour_02", -"xcl_family_houndour_03" -] -}, -"houndoom": { -"3": [ -"xcl_family_houndour_00" -], -"2": [ -"xcl_family_houndour_01" -], -"1": [ -"xcl_family_houndour_02", -"xcl_family_houndour_03" -] -}, -"igglybuff": { -"3": [ -"xcl_family_jigglypuff_00" -], -"2": [ -"xcl_family_jigglypuff_01", -"xcl_family_jigglypuff_04" -], -"1": [ -"xcl_family_jigglypuff_02", -"xcl_family_jigglypuff_03" -] -}, -"jigglypuff": { -"3": [ -"xcl_family_jigglypuff_00" -], -"2": [ -"xcl_family_jigglypuff_01", -"xcl_family_jigglypuff_04" -], -"1": [ -"xcl_family_jigglypuff_02", -"xcl_family_jigglypuff_03" -] -}, -"wigglytuff": { -"3": [ -"xcl_family_jigglypuff_00" -], -"2": [ -"xcl_family_jigglypuff_01", -"xcl_family_jigglypuff_04" -], -"1": [ -"xcl_family_jigglypuff_02", -"xcl_family_jigglypuff_03" -] -}, -"jirachi": { -"3": [ -"xcl_family_jirachi_00" -], -"2": [ -"xcl_family_jirachi_01", -"xcl_family_jirachi_04" -], -"1": [ -"xcl_family_jirachi_02", -"xcl_family_jirachi_03" -] -}, -"smoochum": { -"3": [ -"xcl_family_jynx_00" -], -"2": [ -"xcl_family_jynx_01", -"xcl_family_jynx_04" -], -"1": [ -"xcl_family_jynx_02", -"xcl_family_jynx_03" -] -}, -"jynx": { -"3": [ -"xcl_family_jynx_00" -], -"2": [ -"xcl_family_jynx_01", -"xcl_family_jynx_04" -], -"1": [ -"xcl_family_jynx_02", -"xcl_family_jynx_03" -] -}, -"kabuto": { -"3": [ -"xcl_family_kabuto_00" -], -"2": [ -"xcl_family_kabuto_01" -], -"1": [ -"xcl_family_kabuto_02", -"xcl_family_kabuto_03" -] -}, -"kabutops": { -"3": [ -"xcl_family_kabuto_00" -], -"2": [ -"xcl_family_kabuto_01" -], -"1": [ -"xcl_family_kabuto_02", -"xcl_family_kabuto_03" -] -}, -"kangaskhan": { -"3": [ -"xcl_family_kangaskhan_00" -], -"2": [ -"xcl_family_kangaskhan_01", -"xcl_family_kangaskhan_04" -], -"1": [ -"xcl_family_kangaskhan_02", -"xcl_family_kangaskhan_03" -] -}, -"kecleon": { -"3": [ -"xcl_family_kecleon_00" -], -"2": [ -"xcl_family_kecleon_01", -"xcl_family_kecleon_04" -], -"1": [ -"xcl_family_kecleon_02", -"xcl_family_kecleon_03" -] -}, -"klefki": { -"3": [ -"xcl_family_klefki_00" -], -"2": [ -"xcl_family_klefki_01" -], -"1": [ -"xcl_family_klefki_02", -"xcl_family_klefki_03" -] -}, -"koffing": { -"3": [ -"xcl_family_koffing_00" -], -"2": [ -"xcl_family_koffing_01" -], -"1": [ -"xcl_family_koffing_02", -"xcl_family_koffing_03" -] -}, -"weezing": { -"3": [ -"xcl_family_koffing_00" -], -"2": [ -"xcl_family_koffing_01" -], -"1": [ -"xcl_family_koffing_02", -"xcl_family_koffing_03" -] -}, -"krabby": { -"3": [ -"xcl_family_krabby_00" -], -"2": [ -"xcl_family_krabby_01", -"xcl_family_krabby_04" -], -"1": [ -"xcl_family_krabby_02", -"xcl_family_krabby_03" -] -}, -"kingler": { -"3": [ -"xcl_family_krabby_00" -], -"2": [ -"xcl_family_krabby_01", -"xcl_family_krabby_04" -], -"1": [ -"xcl_family_krabby_02", -"xcl_family_krabby_03" -] -}, -"kricketot": { -"3": [ -"xcl_family_kricketot_00" -], -"2": [ -"xcl_family_kricketot_01" -], -"1": [ -"xcl_family_kricketot_02", -"xcl_family_kricketot_03" -] -}, -"kricketune": { -"3": [ -"xcl_family_kricketot_00" -], -"2": [ -"xcl_family_kricketot_01" -], -"1": [ -"xcl_family_kricketot_02", -"xcl_family_kricketot_03" -] -}, -"kyogre": { -"3": [ -"xcl_family_kyogre_00" -], -"2": [ -"xcl_family_kyogre_01" -], -"1": [ -"xcl_family_kyogre_02", -"xcl_family_kyogre_03" -] -}, -"uxie": { -"3": [ -"xcl_family_lake_trio_00", -"xcl_family_lake_trio_08" -], -"2": [ -"xcl_family_lake_trio_01", -"xcl_family_lake_trio_09" -], -"1": [ -"xcl_family_lake_trio_06", -"xcl_family_lake_trio_07" -] -}, -"mesprit": { -"3": [ -"xcl_family_lake_trio_02", -"xcl_family_lake_trio_08" -], -"2": [ -"xcl_family_lake_trio_03", -"xcl_family_lake_trio_09" -], -"1": [ -"xcl_family_lake_trio_06", -"xcl_family_lake_trio_07" -] -}, -"azelf": { -"3": [ -"xcl_family_lake_trio_04", -"xcl_family_lake_trio_08" -], -"2": [ -"xcl_family_lake_trio_05", -"xcl_family_lake_trio_09" -], -"1": [ -"xcl_family_lake_trio_06", -"xcl_family_lake_trio_07" -] -}, -"lapras": { -"3": [ -"xcl_family_lapras_00" -], -"2": [ -"xcl_family_lapras_01" -], -"1": [ -"xcl_family_lapras_02", -"xcl_family_lapras_03" -] -}, -"larvesta": { -"3": [ -"xcl_family_larvesta_00" -], -"2": [ -"xcl_family_larvesta_01", -"xcl_family_larvesta_04" -], -"1": [ -"xcl_family_larvesta_02", -"xcl_family_larvesta_03" -] -}, -"volcarona": { -"3": [ -"xcl_family_larvesta_00" -], -"2": [ -"xcl_family_larvesta_01", -"xcl_family_larvesta_04" -], -"1": [ -"xcl_family_larvesta_02", -"xcl_family_larvesta_03" -] -}, -"larvitar": { -"3": [ -"xcl_family_larvitar_00" -], -"2": [ -"xcl_family_larvitar_01", -"xcl_family_larvitar_04" -], -"1": [ -"xcl_family_larvitar_02", -"xcl_family_larvitar_03" -] -}, -"pupitar": { -"3": [ -"xcl_family_larvitar_00" -], -"2": [ -"xcl_family_larvitar_01", -"xcl_family_larvitar_04" -], -"1": [ -"xcl_family_larvitar_02", -"xcl_family_larvitar_03" -] -}, -"tyranitar": { -"3": [ -"xcl_family_larvitar_00" -], -"2": [ -"xcl_family_larvitar_01", -"xcl_family_larvitar_04" -], -"1": [ -"xcl_family_larvitar_02", -"xcl_family_larvitar_03" -] -}, -"latias": { -"3": [ -"xcl_family_lati_duo_00" -], -"2": [ -"xcl_family_lati_duo_01", -"xcl_family_lati_duo_06" -], -"1": [ -"xcl_family_lati_duo_04", -"xcl_family_lati_duo_05" -] -}, -"latios": { -"3": [ -"xcl_family_lati_duo_02" -], -"2": [ -"xcl_family_lati_duo_03", -"xcl_family_lati_duo_06" -], -"1": [ -"xcl_family_lati_duo_04", -"xcl_family_lati_duo_05" -] -}, -"ledyba": { -"3": [ -"xcl_family_ledyba_00" -], -"2": [ -"xcl_family_ledyba_01" -], -"1": [ -"xcl_family_ledyba_02", -"xcl_family_ledyba_03" -] -}, -"ledian": { -"3": [ -"xcl_family_ledyba_00" -], -"2": [ -"xcl_family_ledyba_01" -], -"1": [ -"xcl_family_ledyba_02", -"xcl_family_ledyba_03" -] -}, -"lickitung": { -"3": [ -"xcl_family_lickitung_00" -], -"2": [ -"xcl_family_lickitung_01" -], -"1": [ -"xcl_family_lickitung_02", -"xcl_family_lickitung_03" -] -}, -"lickilicky": { -"3": [ -"xcl_family_lickitung_00" -], -"2": [ -"xcl_family_lickitung_01" -], -"1": [ -"xcl_family_lickitung_02", -"xcl_family_lickitung_03" -] -}, -"lileep": { -"3": [ -"xcl_family_lileep_00" -], -"2": [ -"xcl_family_lileep_01" -], -"1": [ -"xcl_family_lileep_02", -"xcl_family_lileep_03" -] -}, -"cradily": { -"3": [ -"xcl_family_lileep_00" -], -"2": [ -"xcl_family_lileep_01" -], -"1": [ -"xcl_family_lileep_02", -"xcl_family_lileep_03" -] -}, -"lotad": { -"3": [ -"xcl_family_lotad_00" -], -"2": [ -"xcl_family_lotad_01" -], -"1": [ -"xcl_family_lotad_02", -"xcl_family_lotad_03" -] -}, -"lombre": { -"3": [ -"xcl_family_lotad_00" -], -"2": [ -"xcl_family_lotad_01" -], -"1": [ -"xcl_family_lotad_02", -"xcl_family_lotad_03" -] -}, -"ludicolo": { -"3": [ -"xcl_family_lotad_00" -], -"2": [ -"xcl_family_lotad_01" -], -"1": [ -"xcl_family_lotad_02", -"xcl_family_lotad_03" -] -}, -"lugia": { -"3": [ -"xcl_family_lugia_00" -], -"2": [ -"xcl_family_lugia_01", -"xcl_family_lugia_04" -], -"1": [ -"xcl_family_lugia_02", -"xcl_family_lugia_03" -] -}, -"lunatone": { -"3": [ -"xcl_family_lunatone_solrock_00" -], -"1": [ -"xcl_family_lunatone_solrock_02", -"xcl_family_lunatone_solrock_03" -], -"2": [ -"xcl_family_lunatone_solrock_04", -"xcl_family_lunatone_solrock_05" -] -}, -"solrock": { -"3": [ -"xcl_family_lunatone_solrock_01" -], -"1": [ -"xcl_family_lunatone_solrock_02", -"xcl_family_lunatone_solrock_03" -], -"2": [ -"xcl_family_lunatone_solrock_04", -"xcl_family_lunatone_solrock_05" -] -}, -"luvdisc": { -"3": [ -"xcl_family_luvdisc_00" -], -"2": [ -"xcl_family_luvdisc_01", -"xcl_family_luvdisc_04" -], -"1": [ -"xcl_family_luvdisc_02", -"xcl_family_luvdisc_03" -] -}, -"machop": { -"3": [ -"xcl_family_machop_00" -], -"2": [ -"xcl_family_machop_01" -], -"1": [ -"xcl_family_machop_02", -"xcl_family_machop_03" -] -}, -"machoke": { -"3": [ -"xcl_family_machop_00" -], -"2": [ -"xcl_family_machop_01" -], -"1": [ -"xcl_family_machop_02", -"xcl_family_machop_03" -] -}, -"machamp": { -"3": [ -"xcl_family_machop_00" -], -"2": [ -"xcl_family_machop_01" -], -"1": [ -"xcl_family_machop_02", -"xcl_family_machop_03" -] -}, -"magikarp": { -"3": [ -"xcl_family_magikarp_00" -], -"2": [ -"xcl_family_magikarp_01" -], -"1": [ -"xcl_family_magikarp_02", -"xcl_family_magikarp_03" -] -}, -"gyarados": { -"3": [ -"xcl_family_magikarp_00" -], -"2": [ -"xcl_family_magikarp_01" -], -"1": [ -"xcl_family_magikarp_02", -"xcl_family_magikarp_03" -] -}, -"magby": { -"3": [ -"xcl_family_magmar_00" -], -"2": [ -"xcl_family_magmar_01", -"xcl_family_magmar_04" -], -"1": [ -"xcl_family_magmar_02", -"xcl_family_magmar_03" -] -}, -"magmar": { -"3": [ -"xcl_family_magmar_00" -], -"2": [ -"xcl_family_magmar_01", -"xcl_family_magmar_04" -], -"1": [ -"xcl_family_magmar_02", -"xcl_family_magmar_03" -] -}, -"magmortar": { -"3": [ -"xcl_family_magmar_00" -], -"2": [ -"xcl_family_magmar_01", -"xcl_family_magmar_04" -], -"1": [ -"xcl_family_magmar_02", -"xcl_family_magmar_03" -] -}, -"magnemite": { -"3": [ -"xcl_family_magnemite_00" -], -"2": [ -"xcl_family_magnemite_01" -], -"1": [ -"xcl_family_magnemite_02", -"xcl_family_magnemite_03" -] -}, -"magneton": { -"3": [ -"xcl_family_magnemite_00" -], -"2": [ -"xcl_family_magnemite_01" -], -"1": [ -"xcl_family_magnemite_02", -"xcl_family_magnemite_03" -] -}, -"magnezone": { -"3": [ -"xcl_family_magnemite_00" -], -"2": [ -"xcl_family_magnemite_01" -], -"1": [ -"xcl_family_magnemite_02", -"xcl_family_magnemite_03" -] -}, -"makuhita": { -"3": [ -"xcl_family_makuhita_00" -], -"2": [ -"xcl_family_makuhita_01", -"xcl_family_makuhita_04" -], -"1": [ -"xcl_family_makuhita_02", -"xcl_family_makuhita_03" -] -}, -"hariyama": { -"3": [ -"xcl_family_makuhita_00" -], -"2": [ -"xcl_family_makuhita_01", -"xcl_family_makuhita_04" -], -"1": [ -"xcl_family_makuhita_02", -"xcl_family_makuhita_03" -] -}, -"manaphy": { -"3": [ -"xcl_family_manaphy_phione_00" -], -"2": [ -"xcl_family_manaphy_phione_02", -"xcl_family_manaphy_phione_05" -], -"1": [ -"xcl_family_manaphy_phione_03", -"xcl_family_manaphy_phione_04" -] -}, -"phione": { -"3": [ -"xcl_family_manaphy_phione_01" -], -"2": [ -"xcl_family_manaphy_phione_02", -"xcl_family_manaphy_phione_05" -], -"1": [ -"xcl_family_manaphy_phione_03", -"xcl_family_manaphy_phione_04" -] -}, -"mankey": { -"3": [ -"xcl_family_mankey_00" -], -"2": [ -"xcl_family_mankey_01" -], -"1": [ -"xcl_family_mankey_02", -"xcl_family_mankey_03" -] -}, -"primeape": { -"3": [ -"xcl_family_mankey_00" -], -"2": [ -"xcl_family_mankey_01" -], -"1": [ -"xcl_family_mankey_02", -"xcl_family_mankey_03" -] -}, -"mantyke": { -"3": [ -"xcl_family_mantine_00" -], -"2": [ -"xcl_family_mantine_01", -"xcl_family_mantine_04" -], -"1": [ -"xcl_family_mantine_02", -"xcl_family_mantine_03" -] -}, -"mantine": { -"3": [ -"xcl_family_mantine_00" -], -"2": [ -"xcl_family_mantine_01", -"xcl_family_mantine_04" -], -"1": [ -"xcl_family_mantine_02", -"xcl_family_mantine_03" -] -}, -"mareep": { -"3": [ -"xcl_family_mareep_00" -], -"2": [ -"xcl_family_mareep_01", -"xcl_family_mareep_04" -], -"1": [ -"xcl_family_mareep_02", -"xcl_family_mareep_03" -] -}, -"flaaffy": { -"3": [ -"xcl_family_mareep_00" -], -"2": [ -"xcl_family_mareep_01", -"xcl_family_mareep_04" -], -"1": [ -"xcl_family_mareep_02", -"xcl_family_mareep_03" -] -}, -"ampharos": { -"3": [ -"xcl_family_mareep_00" -], -"2": [ -"xcl_family_mareep_01", -"xcl_family_mareep_04" -], -"1": [ -"xcl_family_mareep_02", -"xcl_family_mareep_03" -] -}, -"azurill": { -"3": [ -"xcl_family_marill_00" -], -"2": [ -"xcl_family_marill_01", -"xcl_family_marill_04" -], -"1": [ -"xcl_family_marill_02", -"xcl_family_marill_03" -] -}, -"marill": { -"3": [ -"xcl_family_marill_00" -], -"2": [ -"xcl_family_marill_01", -"xcl_family_marill_04" -], -"1": [ -"xcl_family_marill_02", -"xcl_family_marill_03" -] -}, -"azumarill": { -"3": [ -"xcl_family_marill_00" -], -"2": [ -"xcl_family_marill_01", -"xcl_family_marill_04" -], -"1": [ -"xcl_family_marill_02", -"xcl_family_marill_03" -] -}, -"mawile": { -"3": [ -"xcl_family_mawile_00" -], -"2": [ -"xcl_family_mawile_01", -"xcl_family_mawile_04" -], -"1": [ -"xcl_family_mawile_02", -"xcl_family_mawile_03" -] -}, -"meditite": { -"3": [ -"xcl_family_meditite_00" -], -"2": [ -"xcl_family_meditite_01" -], -"1": [ -"xcl_family_meditite_02", -"xcl_family_meditite_03" -] -}, -"medicham": { -"3": [ -"xcl_family_meditite_00" -], -"2": [ -"xcl_family_meditite_01" -], -"1": [ -"xcl_family_meditite_02", -"xcl_family_meditite_03" -] -}, -"meowth": { -"3": [ -"xcl_family_meowth_00" -], -"2": [ -"xcl_family_meowth_01", -"xcl_family_meowth_04" -], -"1": [ -"xcl_family_meowth_02", -"xcl_family_meowth_03" -] -}, -"persian": { -"3": [ -"xcl_family_meowth_00" -], -"2": [ -"xcl_family_meowth_01", -"xcl_family_meowth_04" -], -"1": [ -"xcl_family_meowth_02", -"xcl_family_meowth_03" -] -}, -"mewtwo": { -"3": [ -"xcl_family_mew_duo_00" -], -"2": [ -"xcl_family_mew_duo_01", -"xcl_family_mew_duo_03" -], -"1": [ -"xcl_family_mew_duo_02", -"xcl_family_mew_duo_07" -] -}, -"mew": { -"3": [ -"xcl_family_mew_duo_05" -], -"2": [ -"xcl_family_mew_duo_06", -"xcl_family_mew_duo_09" -], -"1": [ -"xcl_family_mew_duo_07", -"xcl_family_mew_duo_08" -] -}, -"misdreavus": { -"3": [ -"xcl_family_misdreavus_00" -], -"2": [ -"xcl_family_misdreavus_01" -], -"1": [ -"xcl_family_misdreavus_02", -"xcl_family_misdreavus_03" -] -}, -"mismagius": { -"3": [ -"xcl_family_misdreavus_00" -], -"2": [ -"xcl_family_misdreavus_01" -], -"1": [ -"xcl_family_misdreavus_02", -"xcl_family_misdreavus_03" -] -}, -"mime_jr": { -"3": [ -"xcl_family_mr_mime_00" -], -"2": [ -"xcl_family_mr_mime_01", -"xcl_family_mr_mime_04" -], -"1": [ -"xcl_family_mr_mime_02", -"xcl_family_mr_mime_03" -] -}, -"mr_mime": { -"3": [ -"xcl_family_mr_mime_00" -], -"2": [ -"xcl_family_mr_mime_01", -"xcl_family_mr_mime_04" -], -"1": [ -"xcl_family_mr_mime_02", -"xcl_family_mr_mime_03" -] -}, -"mudkip": { -"3": [ -"xcl_family_mudkip_00" -], -"2": [ -"xcl_family_mudkip_01", -"xcl_family_mudkip_04" -], -"1": [ -"xcl_family_mudkip_02", -"xcl_family_mudkip_03" -] -}, -"marshtomp": { -"3": [ -"xcl_family_mudkip_00" -], -"2": [ -"xcl_family_mudkip_01", -"xcl_family_mudkip_04" -], -"1": [ -"xcl_family_mudkip_02", -"xcl_family_mudkip_03" -] -}, -"swampert": { -"3": [ -"xcl_family_mudkip_00" -], -"2": [ -"xcl_family_mudkip_01", -"xcl_family_mudkip_04" -], -"1": [ -"xcl_family_mudkip_02", -"xcl_family_mudkip_03" -] -}, -"murkrow": { -"3": [ -"xcl_family_murkrow_00" -], -"2": [ -"xcl_family_murkrow_01", -"xcl_family_murkrow_04" -], -"1": [ -"xcl_family_murkrow_02", -"xcl_family_murkrow_03" -] -}, -"honchkrow": { -"3": [ -"xcl_family_murkrow_00" -], -"2": [ -"xcl_family_murkrow_01", -"xcl_family_murkrow_04" -], -"1": [ -"xcl_family_murkrow_02", -"xcl_family_murkrow_03" -] -}, -"natu": { -"3": [ -"xcl_family_natu_00" -], -"2": [ -"xcl_family_natu_01" -], -"1": [ -"xcl_family_natu_02", -"xcl_family_natu_03" -] -}, -"xatu": { -"3": [ -"xcl_family_natu_00" -], -"2": [ -"xcl_family_natu_01" -], -"1": [ -"xcl_family_natu_02", -"xcl_family_natu_03" -] -}, -"nidoran_f": { -"3": [ -"xcl_family_nidoran_00" -], -"2": [ -"xcl_family_nidoran_01", -"xcl_family_nidoran_04", -"xcl_family_nidoran_05" -], -"1": [ -"xcl_family_nidoran_02", -"xcl_family_nidoran_03" -] -}, -"nidorina": { -"3": [ -"xcl_family_nidoran_00" -], -"2": [ -"xcl_family_nidoran_01", -"xcl_family_nidoran_04", -"xcl_family_nidoran_05" -], -"1": [ -"xcl_family_nidoran_02", -"xcl_family_nidoran_03" -] -}, -"nidoqueen": { -"3": [ -"xcl_family_nidoran_00" -], -"2": [ -"xcl_family_nidoran_01", -"xcl_family_nidoran_04", -"xcl_family_nidoran_05" -], -"1": [ -"xcl_family_nidoran_02", -"xcl_family_nidoran_03" -] -}, -"nidoran_m": { -"2": [ -"xcl_family_nidoran_01", -"xcl_family_nidoran_04", -"xcl_family_nidoran_05" -], -"1": [ -"xcl_family_nidoran_02", -"xcl_family_nidoran_03" -], -"3": [ -"xcl_family_nidoran_06" -] -}, -"nidorino": { -"2": [ -"xcl_family_nidoran_01", -"xcl_family_nidoran_04", -"xcl_family_nidoran_05" -], -"1": [ -"xcl_family_nidoran_02", -"xcl_family_nidoran_03" -], -"3": [ -"xcl_family_nidoran_06" -] -}, -"nidoking": { -"2": [ -"xcl_family_nidoran_01", -"xcl_family_nidoran_04", -"xcl_family_nidoran_05" -], -"1": [ -"xcl_family_nidoran_02", -"xcl_family_nidoran_03" -], -"3": [ -"xcl_family_nidoran_06" -] -}, -"nincada": { -"3": [ -"xcl_family_nincada_00" -], -"2": [ -"xcl_family_nincada_01", -"xcl_family_nincada_04" -], -"1": [ -"xcl_family_nincada_02", -"xcl_family_nincada_03" -] -}, -"ninjask": { -"3": [ -"xcl_family_nincada_00" -], -"2": [ -"xcl_family_nincada_01", -"xcl_family_nincada_04" -], -"1": [ -"xcl_family_nincada_02", -"xcl_family_nincada_03" -] -}, -"shedinja": { -"1": [ -"xcl_family_nincada_02", -"xcl_family_nincada_03" -], -"2": [ -"xcl_family_nincada_04", -"xcl_family_nincada_05" -], -"3": [ -"xcl_family_nincada_06" -] -}, -"noibat": { -"3": [ -"xcl_family_noibat_00" -], -"2": [ -"xcl_family_noibat_01", -"xcl_family_noibat_04" -], -"1": [ -"xcl_family_noibat_02", -"xcl_family_noibat_03" -] -}, -"noivern": { -"3": [ -"xcl_family_noibat_00" -], -"2": [ -"xcl_family_noibat_01", -"xcl_family_noibat_04" -], -"1": [ -"xcl_family_noibat_02", -"xcl_family_noibat_03" -] -}, -"nosepass": { -"3": [ -"xcl_family_nosepass_00" -], -"2": [ -"xcl_family_nosepass_01", -"xcl_family_nosepass_04" -], -"1": [ -"xcl_family_nosepass_02", -"xcl_family_nosepass_03" -] -}, -"probopass": { -"3": [ -"xcl_family_nosepass_00" -], -"2": [ -"xcl_family_nosepass_01", -"xcl_family_nosepass_04" -], -"1": [ -"xcl_family_nosepass_02", -"xcl_family_nosepass_03" -] -}, -"numel": { -"3": [ -"xcl_family_numel_00" -], -"2": [ -"xcl_family_numel_01", -"xcl_family_numel_04" -], -"1": [ -"xcl_family_numel_02", -"xcl_family_numel_03" -] -}, -"camerupt": { -"3": [ -"xcl_family_numel_00" -], -"2": [ -"xcl_family_numel_01", -"xcl_family_numel_04" -], -"1": [ -"xcl_family_numel_02", -"xcl_family_numel_03" -] -}, -"oddish": { -"3": [ -"xcl_family_oddish_00", -"xcl_family_oddish_01" -], -"1": [ -"xcl_family_oddish_02", -"xcl_family_oddish_03" -], -"2": [ -"xcl_family_oddish_04" -] -}, -"gloom": { -"3": [ -"xcl_family_oddish_00", -"xcl_family_oddish_01" -], -"1": [ -"xcl_family_oddish_02", -"xcl_family_oddish_03" -], -"2": [ -"xcl_family_oddish_04" -] -}, -"vileplume": { -"3": [ -"xcl_family_oddish_00", -"xcl_family_oddish_01" -], -"1": [ -"xcl_family_oddish_02", -"xcl_family_oddish_03" -], -"2": [ -"xcl_family_oddish_04" -] -}, -"bellossom": { -"3": [ -"xcl_family_oddish_00", -"xcl_family_oddish_01" -], -"1": [ -"xcl_family_oddish_02", -"xcl_family_oddish_03" -], -"2": [ -"xcl_family_oddish_04" -] -}, -"omanyte": { -"3": [ -"xcl_family_omanyte_00" -], -"2": [ -"xcl_family_omanyte_01" -], -"1": [ -"xcl_family_omanyte_02", -"xcl_family_omanyte_03" -] -}, -"omastar": { -"3": [ -"xcl_family_omanyte_00" -], -"2": [ -"xcl_family_omanyte_01" -], -"1": [ -"xcl_family_omanyte_02", -"xcl_family_omanyte_03" -] -}, -"onix": { -"3": [ -"xcl_family_onix_00" -], -"2": [ -"xcl_family_onix_01", -"xcl_family_onix_04" -], -"1": [ -"xcl_family_onix_02", -"xcl_family_onix_03" -] -}, -"steelix": { -"3": [ -"xcl_family_onix_00" -], -"2": [ -"xcl_family_onix_01", -"xcl_family_onix_04" -], -"1": [ -"xcl_family_onix_02", -"xcl_family_onix_03" -] -}, -"oshawott": { -"3": [ -"xcl_family_oshawott_00" -], -"2": [ -"xcl_family_oshawott_01", -"xcl_family_oshawott_04" -], -"1": [ -"xcl_family_oshawott_02", -"xcl_family_oshawott_03" -] -}, -"dewott": { -"3": [ -"xcl_family_oshawott_00" -], -"2": [ -"xcl_family_oshawott_01", -"xcl_family_oshawott_04" -], -"1": [ -"xcl_family_oshawott_02", -"xcl_family_oshawott_03" -] -}, -"samurott": { -"3": [ -"xcl_family_oshawott_00" -], -"2": [ -"xcl_family_oshawott_01", -"xcl_family_oshawott_04" -], -"1": [ -"xcl_family_oshawott_02", -"xcl_family_oshawott_03" -] -}, -"pachirisu": { -"3": [ -"xcl_family_pachirisu_00" -], -"2": [ -"xcl_family_pachirisu_01", -"xcl_family_pachirisu_04" -], -"1": [ -"xcl_family_pachirisu_02", -"xcl_family_pachirisu_03" -] -}, -"palkia": { -"3": [ -"xcl_family_palkia_00" -], -"2": [ -"xcl_family_palkia_01", -"xcl_family_palkia_04" -], -"1": [ -"xcl_family_palkia_02", -"xcl_family_palkia_03" -] -}, -"paras": { -"3": [ -"xcl_family_paras_00" -], -"2": [ -"xcl_family_paras_01" -], -"1": [ -"xcl_family_paras_02", -"xcl_family_paras_03" -] -}, -"parasect": { -"3": [ -"xcl_family_paras_00" -], -"2": [ -"xcl_family_paras_01" -], -"1": [ -"xcl_family_paras_02", -"xcl_family_paras_03" -] -}, -"pawniard": { -"3": [ -"xcl_family_pawniard_00" -], -"2": [ -"xcl_family_pawniard_01", -"xcl_family_pawniard_04" -], -"1": [ -"xcl_family_pawniard_02", -"xcl_family_pawniard_03" -] -}, -"bisharp": { -"3": [ -"xcl_family_pawniard_00" -], -"2": [ -"xcl_family_pawniard_01", -"xcl_family_pawniard_04" -], -"1": [ -"xcl_family_pawniard_02", -"xcl_family_pawniard_03" -] -}, -"kingambit": { -"3": [ -"xcl_family_pawniard_00" -], -"2": [ -"xcl_family_pawniard_01", -"xcl_family_pawniard_04" -], -"1": [ -"xcl_family_pawniard_02", -"xcl_family_pawniard_03" -] -}, -"petilil": { -"3": [ -"xcl_family_petilil_00" -], -"2": [ -"xcl_family_petilil_01", -"xcl_family_petilil_04" -], -"1": [ -"xcl_family_petilil_02", -"xcl_family_petilil_03" -] -}, -"lilligant": { -"3": [ -"xcl_family_petilil_00" -], -"2": [ -"xcl_family_petilil_01", -"xcl_family_petilil_04" -], -"1": [ -"xcl_family_petilil_02", -"xcl_family_petilil_03" -] -}, -"phanpy": { -"3": [ -"xcl_family_phanpy_00" -], -"2": [ -"xcl_family_phanpy_01", -"xcl_family_phanpy_04" -], -"1": [ -"xcl_family_phanpy_02", -"xcl_family_phanpy_03" -] -}, -"donphan": { -"3": [ -"xcl_family_phanpy_00" -], -"2": [ -"xcl_family_phanpy_01", -"xcl_family_phanpy_04" -], -"1": [ -"xcl_family_phanpy_02", -"xcl_family_phanpy_03" -] -}, -"pidgey": { -"3": [ -"xcl_family_pidgey_00" -], -"2": [ -"xcl_family_pidgey_01", -"xcl_family_pidgey_04" -], -"1": [ -"xcl_family_pidgey_02", -"xcl_family_pidgey_03" -] -}, -"pidgeotto": { -"3": [ -"xcl_family_pidgey_00" -], -"2": [ -"xcl_family_pidgey_01", -"xcl_family_pidgey_04" -], -"1": [ -"xcl_family_pidgey_02", -"xcl_family_pidgey_03" -] -}, -"pidgeot": { -"3": [ -"xcl_family_pidgey_00" -], -"2": [ -"xcl_family_pidgey_01", -"xcl_family_pidgey_04" -], -"1": [ -"xcl_family_pidgey_02", -"xcl_family_pidgey_03" -] -}, -"pichu": { -"3": [ -"xcl_family_pikachu_00" -], -"2": [ -"xcl_family_pikachu_01", -"xcl_family_pikachu_04" -], -"1": [ -"xcl_family_pikachu_02", -"xcl_family_pikachu_03" -] -}, -"pikachu": { -"3": [ -"xcl_family_pikachu_00" -], -"2": [ -"xcl_family_pikachu_01", -"xcl_family_pikachu_04" -], -"1": [ -"xcl_family_pikachu_02", -"xcl_family_pikachu_03" -] -}, -"raichu": { -"3": [ -"xcl_family_pikachu_00" -], -"2": [ -"xcl_family_pikachu_01", -"xcl_family_pikachu_04" -], -"1": [ -"xcl_family_pikachu_02", -"xcl_family_pikachu_03" -] -}, -"pineco": { -"3": [ -"xcl_family_pineco_00" -], -"2": [ -"xcl_family_pineco_01", -"xcl_family_pineco_04" -], -"1": [ -"xcl_family_pineco_02", -"xcl_family_pineco_03" -] -}, -"forretress": { -"3": [ -"xcl_family_pineco_00" -], -"2": [ -"xcl_family_pineco_01", -"xcl_family_pineco_04" -], -"1": [ -"xcl_family_pineco_02", -"xcl_family_pineco_03" -] -}, -"pinsir": { -"3": [ -"xcl_family_pinsir_00" -], -"2": [ -"xcl_family_pinsir_01" -], -"1": [ -"xcl_family_pinsir_02", -"xcl_family_pinsir_03" -] -}, -"piplup": { -"3": [ -"xcl_family_piplup_00" -], -"2": [ -"xcl_family_piplup_01", -"xcl_family_piplup_04" -], -"1": [ -"xcl_family_piplup_02", -"xcl_family_piplup_03" -] -}, -"prinplup": { -"3": [ -"xcl_family_piplup_00" -], -"2": [ -"xcl_family_piplup_01", -"xcl_family_piplup_04" -], -"1": [ -"xcl_family_piplup_02", -"xcl_family_piplup_03" -] -}, -"empoleon": { -"3": [ -"xcl_family_piplup_00" -], -"2": [ -"xcl_family_piplup_01", -"xcl_family_piplup_04" -], -"1": [ -"xcl_family_piplup_02", -"xcl_family_piplup_03" -] -}, -"plusle": { -"3": [ -"xcl_family_plusle_minun_00" -], -"1": [ -"xcl_family_plusle_minun_02", -"xcl_family_plusle_minun_03" -], -"2": [ -"xcl_family_plusle_minun_04", -"xcl_family_plusle_minun_05" -] -}, -"minun": { -"3": [ -"xcl_family_plusle_minun_01" -], -"1": [ -"xcl_family_plusle_minun_02", -"xcl_family_plusle_minun_03" -], -"2": [ -"xcl_family_plusle_minun_04", -"xcl_family_plusle_minun_05" -] -}, -"poliwag": { -"3": [ -"xcl_family_poliwag_00", -"xcl_family_poliwag_01" -], -"1": [ -"xcl_family_poliwag_02", -"xcl_family_poliwag_03" -], -"2": [ -"xcl_family_poliwag_04" -] -}, -"poliwhirl": { -"3": [ -"xcl_family_poliwag_00", -"xcl_family_poliwag_01" -], -"1": [ -"xcl_family_poliwag_02", -"xcl_family_poliwag_03" -], -"2": [ -"xcl_family_poliwag_04" -] -}, -"poliwrath": { -"3": [ -"xcl_family_poliwag_00", -"xcl_family_poliwag_01" -], -"1": [ -"xcl_family_poliwag_02", -"xcl_family_poliwag_03" -], -"2": [ -"xcl_family_poliwag_04" -] -}, -"politoed": { -"3": [ -"xcl_family_poliwag_00", -"xcl_family_poliwag_01" -], -"1": [ -"xcl_family_poliwag_02", -"xcl_family_poliwag_03" -], -"2": [ -"xcl_family_poliwag_04" -] -}, -"ponyta": { -"3": [ -"xcl_family_ponyta_00" -], -"2": [ -"xcl_family_ponyta_01" -], -"1": [ -"xcl_family_ponyta_02", -"xcl_family_ponyta_03" -] -}, -"rapidash": { -"3": [ -"xcl_family_ponyta_00" -], -"2": [ -"xcl_family_ponyta_01" -], -"1": [ -"xcl_family_ponyta_02", -"xcl_family_ponyta_03" -] -}, -"poochyena": { -"3": [ -"xcl_family_poochyena_00" -], -"2": [ -"xcl_family_poochyena_01" -], -"1": [ -"xcl_family_poochyena_02", -"xcl_family_poochyena_03" -] -}, -"mightyena": { -"3": [ -"xcl_family_poochyena_00" -], -"2": [ -"xcl_family_poochyena_01" -], -"1": [ -"xcl_family_poochyena_02", -"xcl_family_poochyena_03" -] -}, -"porygon": { -"3": [ -"xcl_family_porygon_00" -], -"2": [ -"xcl_family_porygon_01", -"xcl_family_porygon_04" -], -"1": [ -"xcl_family_porygon_02", -"xcl_family_porygon_03" -] -}, -"porygon2": { -"3": [ -"xcl_family_porygon_00" -], -"2": [ -"xcl_family_porygon_01", -"xcl_family_porygon_04" -], -"1": [ -"xcl_family_porygon_02", -"xcl_family_porygon_03" -] -}, -"porygon_z": { -"3": [ -"xcl_family_porygon_00" -], -"2": [ -"xcl_family_porygon_01", -"xcl_family_porygon_04" -], -"1": [ -"xcl_family_porygon_02", -"xcl_family_porygon_03" -] -}, -"psyduck": { -"3": [ -"xcl_family_psyduck_00" -], -"2": [ -"xcl_family_psyduck_01" -], -"1": [ -"xcl_family_psyduck_02", -"xcl_family_psyduck_03" -] -}, -"golduck": { -"3": [ -"xcl_family_psyduck_00" -], -"2": [ -"xcl_family_psyduck_01" -], -"1": [ -"xcl_family_psyduck_02", -"xcl_family_psyduck_03" -] -}, -"qwilfish": { -"3": [ -"xcl_family_qwilfish_00" -], -"2": [ -"xcl_family_qwilfish_01" -], -"1": [ -"xcl_family_qwilfish_02", -"xcl_family_qwilfish_03" -] -}, -"overqwil": { -"3": [ -"xcl_family_qwilfish_00" -], -"2": [ -"xcl_family_qwilfish_01" -], -"1": [ -"xcl_family_qwilfish_02", -"xcl_family_qwilfish_03" -] -}, -"ralts": { -"3": [ -"xcl_family_ralts_00", -"xcl_family_ralts_01" -], -"1": [ -"xcl_family_ralts_02", -"xcl_family_ralts_03" -], -"2": [ -"xcl_family_ralts_04" -] -}, -"kirlia": { -"3": [ -"xcl_family_ralts_00", -"xcl_family_ralts_01" -], -"1": [ -"xcl_family_ralts_02", -"xcl_family_ralts_03" -], -"2": [ -"xcl_family_ralts_04" -] -}, -"gardevoir": { -"3": [ -"xcl_family_ralts_00", -"xcl_family_ralts_01" -], -"1": [ -"xcl_family_ralts_02", -"xcl_family_ralts_03" -], -"2": [ -"xcl_family_ralts_04" -] -}, -"gallade": { -"3": [ -"xcl_family_ralts_00", -"xcl_family_ralts_01" -], -"1": [ -"xcl_family_ralts_02", -"xcl_family_ralts_03" -], -"2": [ -"xcl_family_ralts_04" -] -}, -"rattata": { -"3": [ -"xcl_family_rattata_00" -], -"2": [ -"xcl_family_rattata_01", -"xcl_family_rattata_04" -], -"1": [ -"xcl_family_rattata_02", -"xcl_family_rattata_03" -] -}, -"raticate": { -"3": [ -"xcl_family_rattata_00" -], -"2": [ -"xcl_family_rattata_01", -"xcl_family_rattata_04" -], -"1": [ -"xcl_family_rattata_02", -"xcl_family_rattata_03" -] -}, -"rayquaza": { -"3": [ -"xcl_family_rayquaza_00" -], -"2": [ -"xcl_family_rayquaza_01" -], -"1": [ -"xcl_family_rayquaza_02", -"xcl_family_rayquaza_03" -] -}, -"regigigas": { -"3": [ -"xcl_family_regi_trio_00", -"xcl_family_regi_trio_16" -], -"2": [ -"xcl_family_regi_trio_01" -], -"1": [ -"xcl_family_regi_trio_02", -"xcl_family_regi_trio_03" -] -}, -"regirock": { -"3": [ -"xcl_family_regi_trio_04", -"xcl_family_regi_trio_16" -], -"2": [ -"xcl_family_regi_trio_05" -], -"1": [ -"xcl_family_regi_trio_06", -"xcl_family_regi_trio_07" -] -}, -"regice": { -"3": [ -"xcl_family_regi_trio_08", -"xcl_family_regi_trio_16" -], -"2": [ -"xcl_family_regi_trio_09" -], -"1": [ -"xcl_family_regi_trio_10", -"xcl_family_regi_trio_11" -] -}, -"registeel": { -"3": [ -"xcl_family_regi_trio_12", -"xcl_family_regi_trio_16" -], -"2": [ -"xcl_family_regi_trio_13" -], -"1": [ -"xcl_family_regi_trio_14", -"xcl_family_regi_trio_15" -] -}, -"relicanth": { -"3": [ -"xcl_family_relicanth_00" -], -"2": [ -"xcl_family_relicanth_01", -"xcl_family_relicanth_04" -], -"1": [ -"xcl_family_relicanth_02", -"xcl_family_relicanth_03" -] -}, -"remoraid": { -"3": [ -"xcl_family_remoraid_00" -], -"2": [ -"xcl_family_remoraid_01" -], -"1": [ -"xcl_family_remoraid_02", -"xcl_family_remoraid_03" -] -}, -"octillery": { -"3": [ -"xcl_family_remoraid_00" -], -"2": [ -"xcl_family_remoraid_01" -], -"1": [ -"xcl_family_remoraid_02", -"xcl_family_remoraid_03" -] -}, -"rhyhorn": { -"3": [ -"xcl_family_rhyhorn_00" -], -"2": [ -"xcl_family_rhyhorn_01", -"xcl_family_rhyhorn_04" -], -"1": [ -"xcl_family_rhyhorn_02", -"xcl_family_rhyhorn_03" -] -}, -"rhydon": { -"3": [ -"xcl_family_rhyhorn_00" -], -"2": [ -"xcl_family_rhyhorn_01", -"xcl_family_rhyhorn_04" -], -"1": [ -"xcl_family_rhyhorn_02", -"xcl_family_rhyhorn_03" -] -}, -"rhyperior": { -"3": [ -"xcl_family_rhyhorn_00" -], -"2": [ -"xcl_family_rhyhorn_01", -"xcl_family_rhyhorn_04" -], -"1": [ -"xcl_family_rhyhorn_02", -"xcl_family_rhyhorn_03" -] -}, -"riolu": { -"3": [ -"xcl_family_riolu_00" -], -"2": [ -"xcl_family_riolu_01", -"xcl_family_riolu_04" -], -"1": [ -"xcl_family_riolu_02", -"xcl_family_riolu_03" -] -}, -"lucario": { -"3": [ -"xcl_family_riolu_00" -], -"2": [ -"xcl_family_riolu_01", -"xcl_family_riolu_04" -], -"1": [ -"xcl_family_riolu_02", -"xcl_family_riolu_03" -] -}, -"rockruff": { -"3": [ -"xcl_family_rockruff_00" -], -"2": [ -"xcl_family_rockruff_01", -"xcl_family_rockruff_04" -], -"1": [ -"xcl_family_rockruff_02", -"xcl_family_rockruff_03" -] -}, -"lycanroc": { -"3": [ -"xcl_family_rockruff_00" -], -"2": [ -"xcl_family_rockruff_01", -"xcl_family_rockruff_04" -], -"1": [ -"xcl_family_rockruff_02", -"xcl_family_rockruff_03" -] -}, -"budew": { -"3": [ -"xcl_family_roselia_00" -], -"2": [ -"xcl_family_roselia_01" -], -"1": [ -"xcl_family_roselia_02", -"xcl_family_roselia_03" -] -}, -"roselia": { -"3": [ -"xcl_family_roselia_00" -], -"2": [ -"xcl_family_roselia_01" -], -"1": [ -"xcl_family_roselia_02", -"xcl_family_roselia_03" -] -}, -"roserade": { -"3": [ -"xcl_family_roselia_00" -], -"2": [ -"xcl_family_roselia_01" -], -"1": [ -"xcl_family_roselia_02", -"xcl_family_roselia_03" -] -}, -"rotom": { -"3": [ -"xcl_family_rotom_00" -], -"2": [ -"xcl_family_rotom_01" -], -"1": [ -"xcl_family_rotom_02", -"xcl_family_rotom_03" -] -}, -"sableye": { -"3": [ -"xcl_family_sableye_00" -], -"2": [ -"xcl_family_sableye_01", -"xcl_family_sableye_04" -], -"1": [ -"xcl_family_sableye_02", -"xcl_family_sableye_03" -] -}, -"salandit": { -"3": [ -"xcl_family_salandit_00" -], -"2": [ -"xcl_family_salandit_01", -"xcl_family_salandit_04" -], -"1": [ -"xcl_family_salandit_02", -"xcl_family_salandit_03" -] -}, -"salazzle": { -"3": [ -"xcl_family_salandit_00" -], -"2": [ -"xcl_family_salandit_01", -"xcl_family_salandit_04" -], -"1": [ -"xcl_family_salandit_02", -"xcl_family_salandit_03" -] -}, -"sandshrew": { -"3": [ -"xcl_family_sandshrew_00" -], -"2": [ -"xcl_family_sandshrew_01" -], -"1": [ -"xcl_family_sandshrew_02", -"xcl_family_sandshrew_03" -] -}, -"sandslash": { -"3": [ -"xcl_family_sandshrew_00" -], -"2": [ -"xcl_family_sandshrew_01" -], -"1": [ -"xcl_family_sandshrew_02", -"xcl_family_sandshrew_03" -] -}, -"scraggy": { -"3": [ -"xcl_family_scraggy_00" -], -"2": [ -"xcl_family_scraggy_01", -"xcl_family_scraggy_04" -], -"1": [ -"xcl_family_scraggy_02", -"xcl_family_scraggy_03" -] -}, -"scrafty": { -"3": [ -"xcl_family_scraggy_00" -], -"2": [ -"xcl_family_scraggy_01", -"xcl_family_scraggy_04" -], -"1": [ -"xcl_family_scraggy_02", -"xcl_family_scraggy_03" -] -}, -"scyther": { -"3": [ -"xcl_family_scyther_00", -"xcl_family_scyther_01" -], -"1": [ -"xcl_family_scyther_02", -"xcl_family_scyther_03" -], -"2": [ -"xcl_family_scyther_04" -] -}, -"scizor": { -"3": [ -"xcl_family_scyther_00", -"xcl_family_scyther_01" -], -"1": [ -"xcl_family_scyther_02", -"xcl_family_scyther_03" -], -"2": [ -"xcl_family_scyther_04" -] -}, -"kleavor": { -"3": [ -"xcl_family_scyther_00", -"xcl_family_scyther_01" -], -"1": [ -"xcl_family_scyther_02", -"xcl_family_scyther_03" -], -"2": [ -"xcl_family_scyther_04" -] -}, -"seedot": { -"3": [ -"xcl_family_seedot_00" -], -"2": [ -"xcl_family_seedot_01", -"xcl_family_seedot_04" -], -"1": [ -"xcl_family_seedot_02", -"xcl_family_seedot_03" -] -}, -"nuzleaf": { -"3": [ -"xcl_family_seedot_00" -], -"2": [ -"xcl_family_seedot_01", -"xcl_family_seedot_04" -], -"1": [ -"xcl_family_seedot_02", -"xcl_family_seedot_03" -] -}, -"shiftry": { -"3": [ -"xcl_family_seedot_00" -], -"2": [ -"xcl_family_seedot_01", -"xcl_family_seedot_04" -], -"1": [ -"xcl_family_seedot_02", -"xcl_family_seedot_03" -] -}, -"seel": { -"3": [ -"xcl_family_seel_00" -], -"2": [ -"xcl_family_seel_01" -], -"1": [ -"xcl_family_seel_02", -"xcl_family_seel_03" -] -}, -"dewgong": { -"3": [ -"xcl_family_seel_00" -], -"2": [ -"xcl_family_seel_01" -], -"1": [ -"xcl_family_seel_02", -"xcl_family_seel_03" -] -}, -"sentret": { -"3": [ -"xcl_family_sentret_00" -], -"2": [ -"xcl_family_sentret_01" -], -"1": [ -"xcl_family_sentret_02", -"xcl_family_sentret_03" -] -}, -"furret": { -"3": [ -"xcl_family_sentret_00" -], -"2": [ -"xcl_family_sentret_01" -], -"1": [ -"xcl_family_sentret_02", -"xcl_family_sentret_03" -] -}, -"seviper": { -"3": [ -"xcl_family_seviper_00" -], -"2": [ -"xcl_family_seviper_01" -], -"1": [ -"xcl_family_seviper_02", -"xcl_family_seviper_03" -] -}, -"shaymin": { -"3": [ -"xcl_family_shaymin_00" -], -"2": [ -"xcl_family_shaymin_01", -"xcl_family_shaymin_04" -], -"1": [ -"xcl_family_shaymin_02", -"xcl_family_shaymin_03" -] -}, -"shellder": { -"3": [ -"xcl_family_shellder_00" -], -"2": [ -"xcl_family_shellder_01" -], -"1": [ -"xcl_family_shellder_02", -"xcl_family_shellder_03" -] -}, -"cloyster": { -"3": [ -"xcl_family_shellder_00" -], -"2": [ -"xcl_family_shellder_01" -], -"1": [ -"xcl_family_shellder_02", -"xcl_family_shellder_03" -] -}, -"shellos": { -"3": [ -"xcl_family_shellos_00" -], -"2": [ -"xcl_family_shellos_01" -], -"1": [ -"xcl_family_shellos_02", -"xcl_family_shellos_03" -] -}, -"gastrodon": { -"3": [ -"xcl_family_shellos_00" -], -"2": [ -"xcl_family_shellos_01" -], -"1": [ -"xcl_family_shellos_02", -"xcl_family_shellos_03" -] -}, -"shieldon": { -"3": [ -"xcl_family_shieldon_00" -], -"2": [ -"xcl_family_shieldon_01", -"xcl_family_shieldon_04" -], -"1": [ -"xcl_family_shieldon_02", -"xcl_family_shieldon_03" -] -}, -"bastiodon": { -"3": [ -"xcl_family_shieldon_00" -], -"2": [ -"xcl_family_shieldon_01", -"xcl_family_shieldon_04" -], -"1": [ -"xcl_family_shieldon_02", -"xcl_family_shieldon_03" -] -}, -"shinx": { -"3": [ -"xcl_family_shinx_00" -], -"2": [ -"xcl_family_shinx_01", -"xcl_family_shinx_04" -], -"1": [ -"xcl_family_shinx_02", -"xcl_family_shinx_03" -] -}, -"luxio": { -"3": [ -"xcl_family_shinx_00" -], -"2": [ -"xcl_family_shinx_01", -"xcl_family_shinx_04" -], -"1": [ -"xcl_family_shinx_02", -"xcl_family_shinx_03" -] -}, -"luxray": { -"3": [ -"xcl_family_shinx_00" -], -"2": [ -"xcl_family_shinx_01", -"xcl_family_shinx_04" -], -"1": [ -"xcl_family_shinx_02", -"xcl_family_shinx_03" -] -}, -"shroomish": { -"3": [ -"xcl_family_shroomish_00" -], -"2": [ -"xcl_family_shroomish_01" -], -"1": [ -"xcl_family_shroomish_02", -"xcl_family_shroomish_03" -] -}, -"breloom": { -"3": [ -"xcl_family_shroomish_00" -], -"2": [ -"xcl_family_shroomish_01" -], -"1": [ -"xcl_family_shroomish_02", -"xcl_family_shroomish_03" -] -}, -"shuckle": { -"3": [ -"xcl_family_shuckle_00" -], -"2": [ -"xcl_family_shuckle_01", -"xcl_family_shuckle_04" -], -"1": [ -"xcl_family_shuckle_02", -"xcl_family_shuckle_03" -] -}, -"shuppet": { -"3": [ -"xcl_family_shuppet_00" -], -"2": [ -"xcl_family_shuppet_01" -], -"1": [ -"xcl_family_shuppet_02", -"xcl_family_shuppet_03" -] -}, -"banette": { -"3": [ -"xcl_family_shuppet_00" -], -"2": [ -"xcl_family_shuppet_01" -], -"1": [ -"xcl_family_shuppet_02", -"xcl_family_shuppet_03" -] -}, -"skarmory": { -"3": [ -"xcl_family_skarmory_00" -], -"2": [ -"xcl_family_skarmory_01", -"xcl_family_skarmory_04" -], -"1": [ -"xcl_family_skarmory_02", -"xcl_family_skarmory_03" -] -}, -"skitty": { -"3": [ -"xcl_family_skitty_00" -], -"2": [ -"xcl_family_skitty_01", -"xcl_family_skitty_04" -], -"1": [ -"xcl_family_skitty_02", -"xcl_family_skitty_03" -] -}, -"delcatty": { -"3": [ -"xcl_family_skitty_00" -], -"2": [ -"xcl_family_skitty_01", -"xcl_family_skitty_04" -], -"1": [ -"xcl_family_skitty_02", -"xcl_family_skitty_03" -] -}, -"skorupi": { -"3": [ -"xcl_family_skorupi_00" -], -"2": [ -"xcl_family_skorupi_01" -], -"1": [ -"xcl_family_skorupi_02", -"xcl_family_skorupi_03" -] -}, -"drapion": { -"3": [ -"xcl_family_skorupi_00" -], -"2": [ -"xcl_family_skorupi_01" -], -"1": [ -"xcl_family_skorupi_02", -"xcl_family_skorupi_03" -] -}, -"slakoth": { -"3": [ -"xcl_family_slakoth_00" -], -"2": [ -"xcl_family_slakoth_01" -], -"1": [ -"xcl_family_slakoth_02", -"xcl_family_slakoth_03" -] -}, -"vigoroth": { -"3": [ -"xcl_family_slakoth_00" -], -"2": [ -"xcl_family_slakoth_01" -], -"1": [ -"xcl_family_slakoth_02", -"xcl_family_slakoth_03" -] -}, -"slaking": { -"3": [ -"xcl_family_slakoth_00" -], -"2": [ -"xcl_family_slakoth_01" -], -"1": [ -"xcl_family_slakoth_02", -"xcl_family_slakoth_03" -] -}, -"slowpoke": { -"3": [ -"xcl_family_slowpoke_00", -"xcl_family_slowpoke_01" -], -"1": [ -"xcl_family_slowpoke_02", -"xcl_family_slowpoke_03" -], -"2": [ -"xcl_family_slowpoke_04" -] -}, -"slowbro": { -"3": [ -"xcl_family_slowpoke_00", -"xcl_family_slowpoke_01" -], -"1": [ -"xcl_family_slowpoke_02", -"xcl_family_slowpoke_03" -], -"2": [ -"xcl_family_slowpoke_04" -] -}, -"slowking": { -"3": [ -"xcl_family_slowpoke_00", -"xcl_family_slowpoke_01" -], -"1": [ -"xcl_family_slowpoke_02", -"xcl_family_slowpoke_03" -], -"2": [ -"xcl_family_slowpoke_04" -] -}, -"slugma": { -"3": [ -"xcl_family_slugma_00" -], -"2": [ -"xcl_family_slugma_01", -"xcl_family_slugma_04" -], -"1": [ -"xcl_family_slugma_02", -"xcl_family_slugma_03" -] -}, -"magcargo": { -"3": [ -"xcl_family_slugma_00" -], -"2": [ -"xcl_family_slugma_01", -"xcl_family_slugma_04" -], -"1": [ -"xcl_family_slugma_02", -"xcl_family_slugma_03" -] -}, -"smeargle": { -"3": [ -"xcl_family_smeargle_00" -], -"2": [ -"xcl_family_smeargle_01", -"xcl_family_smeargle_04" -], -"1": [ -"xcl_family_smeargle_02", -"xcl_family_smeargle_03" -] -}, -"sneasel": { -"3": [ -"xcl_family_sneasel_00" -], -"2": [ -"xcl_family_sneasel_01" -], -"1": [ -"xcl_family_sneasel_02", -"xcl_family_sneasel_03" -] -}, -"weavile": { -"3": [ -"xcl_family_sneasel_00" -], -"2": [ -"xcl_family_sneasel_01" -], -"1": [ -"xcl_family_sneasel_02", -"xcl_family_sneasel_03" -] -}, -"snivy": { -"3": [ -"xcl_family_snivy_00" -], -"2": [ -"xcl_family_snivy_01", -"xcl_family_snivy_04" -], -"1": [ -"xcl_family_snivy_02", -"xcl_family_snivy_03" -] -}, -"servine": { -"3": [ -"xcl_family_snivy_00" -], -"2": [ -"xcl_family_snivy_01", -"xcl_family_snivy_04" -], -"1": [ -"xcl_family_snivy_02", -"xcl_family_snivy_03" -] -}, -"serperior": { -"3": [ -"xcl_family_snivy_00" -], -"2": [ -"xcl_family_snivy_01", -"xcl_family_snivy_04" -], -"1": [ -"xcl_family_snivy_02", -"xcl_family_snivy_03" -] -}, -"munchlax": { -"3": [ -"xcl_family_snorlax_00" -], -"2": [ -"xcl_family_snorlax_01", -"xcl_family_snorlax_04" -], -"1": [ -"xcl_family_snorlax_02", -"xcl_family_snorlax_03" -] -}, -"snorlax": { -"3": [ -"xcl_family_snorlax_00" -], -"2": [ -"xcl_family_snorlax_01", -"xcl_family_snorlax_04" -], -"1": [ -"xcl_family_snorlax_02", -"xcl_family_snorlax_03" -] -}, -"froslass": { -"3": [ -"xcl_family_snorunt_00" -], -"1": [ -"xcl_family_snorunt_02", -"xcl_family_snorunt_03" -], -"2": [ -"xcl_family_snorunt_04" -] -}, -"glalie": { -"3": [ -"xcl_family_snorunt_01" -], -"1": [ -"xcl_family_snorunt_02", -"xcl_family_snorunt_03" -], -"2": [ -"xcl_family_snorunt_04" -] -}, -"snorunt": { -"1": [ -"xcl_family_snorunt_02", -"xcl_family_snorunt_03" -], -"2": [ -"xcl_family_snorunt_04" -] -}, -"snover": { -"3": [ -"xcl_family_snover_00" -], -"2": [ -"xcl_family_snover_01", -"xcl_family_snover_04" -], -"1": [ -"xcl_family_snover_02", -"xcl_family_snover_03" -] -}, -"abomasnow": { -"3": [ -"xcl_family_snover_00" -], -"2": [ -"xcl_family_snover_01", -"xcl_family_snover_04" -], -"1": [ -"xcl_family_snover_02", -"xcl_family_snover_03" -] -}, -"snubbull": { -"3": [ -"xcl_family_snubbull_00" -], -"2": [ -"xcl_family_snubbull_01" -], -"1": [ -"xcl_family_snubbull_02", -"xcl_family_snubbull_03" -] -}, -"granbull": { -"3": [ -"xcl_family_snubbull_00" -], -"2": [ -"xcl_family_snubbull_01" -], -"1": [ -"xcl_family_snubbull_02", -"xcl_family_snubbull_03" -] -}, -"spearow": { -"3": [ -"xcl_family_spearow_00" -], -"2": [ -"xcl_family_spearow_01" -], -"1": [ -"xcl_family_spearow_02", -"xcl_family_spearow_03" -] -}, -"fearow": { -"3": [ -"xcl_family_spearow_00" -], -"2": [ -"xcl_family_spearow_01" -], -"1": [ -"xcl_family_spearow_02", -"xcl_family_spearow_03" -] -}, -"spheal": { -"3": [ -"xcl_family_spheal_00" -], -"2": [ -"xcl_family_spheal_01" -], -"1": [ -"xcl_family_spheal_02", -"xcl_family_spheal_03" -] -}, -"sealeo": { -"3": [ -"xcl_family_spheal_00" -], -"2": [ -"xcl_family_spheal_01" -], -"1": [ -"xcl_family_spheal_02", -"xcl_family_spheal_03" -] -}, -"walrein": { -"3": [ -"xcl_family_spheal_00" -], -"2": [ -"xcl_family_spheal_01" -], -"1": [ -"xcl_family_spheal_02", -"xcl_family_spheal_03" -] -}, -"spinarak": { -"3": [ -"xcl_family_spinarak_00" -], -"2": [ -"xcl_family_spinarak_01" -], -"1": [ -"xcl_family_spinarak_02", -"xcl_family_spinarak_03" -] -}, -"ariados": { -"3": [ -"xcl_family_spinarak_00" -], -"2": [ -"xcl_family_spinarak_01" -], -"1": [ -"xcl_family_spinarak_02", -"xcl_family_spinarak_03" -] -}, -"spinda": { -"3": [ -"xcl_family_spinda_00" -], -"2": [ -"xcl_family_spinda_01", -"xcl_family_spinda_04" -], -"1": [ -"xcl_family_spinda_02", -"xcl_family_spinda_03" -] -}, -"spiritomb": { -"3": [ -"xcl_family_spiritomb_00" -], -"2": [ -"xcl_family_spiritomb_01", -"xcl_family_spiritomb_04" -], -"1": [ -"xcl_family_spiritomb_02", -"xcl_family_spiritomb_03" -] -}, -"spoink": { -"3": [ -"xcl_family_spoink_00" -], -"2": [ -"xcl_family_spoink_01", -"xcl_family_spoink_04" -], -"1": [ -"xcl_family_spoink_02", -"xcl_family_spoink_03" -] -}, -"grumpig": { -"3": [ -"xcl_family_spoink_00" -], -"2": [ -"xcl_family_spoink_01", -"xcl_family_spoink_04" -], -"1": [ -"xcl_family_spoink_02", -"xcl_family_spoink_03" -] -}, -"squirtle": { -"3": [ -"xcl_family_squirtle_00" -], -"2": [ -"xcl_family_squirtle_01", -"xcl_family_squirtle_04" -], -"1": [ -"xcl_family_squirtle_02", -"xcl_family_squirtle_03" -] -}, -"wartortle": { -"3": [ -"xcl_family_squirtle_00" -], -"2": [ -"xcl_family_squirtle_01", -"xcl_family_squirtle_04" -], -"1": [ -"xcl_family_squirtle_02", -"xcl_family_squirtle_03" -] -}, -"blastoise": { -"3": [ -"xcl_family_squirtle_00" -], -"2": [ -"xcl_family_squirtle_01", -"xcl_family_squirtle_04" -], -"1": [ -"xcl_family_squirtle_02", -"xcl_family_squirtle_03" -] -}, -"stantler": { -"3": [ -"xcl_family_stantler_00" -], -"2": [ -"xcl_family_stantler_01", -"xcl_family_stantler_04" -], -"1": [ -"xcl_family_stantler_02", -"xcl_family_stantler_03" -] -}, -"starly": { -"3": [ -"xcl_family_starly_00" -], -"2": [ -"xcl_family_starly_01" -], -"1": [ -"xcl_family_starly_02", -"xcl_family_starly_03" -] -}, -"staravia": { -"3": [ -"xcl_family_starly_00" -], -"2": [ -"xcl_family_starly_01" -], -"1": [ -"xcl_family_starly_02", -"xcl_family_starly_03" -] -}, -"staraptor": { -"3": [ -"xcl_family_starly_00" -], -"2": [ -"xcl_family_starly_01" -], -"1": [ -"xcl_family_starly_02", -"xcl_family_starly_03" -] -}, -"staryu": { -"3": [ -"xcl_family_staryu_00" -], -"2": [ -"xcl_family_staryu_01", -"xcl_family_staryu_04" -], -"1": [ -"xcl_family_staryu_02", -"xcl_family_staryu_03" -] -}, -"starmie": { -"3": [ -"xcl_family_staryu_00" -], -"2": [ -"xcl_family_staryu_01", -"xcl_family_staryu_04" -], -"1": [ -"xcl_family_staryu_02", -"xcl_family_staryu_03" -] -}, -"stunky": { -"3": [ -"xcl_family_stunky_00" -], -"2": [ -"xcl_family_stunky_01", -"xcl_family_stunky_04" -], -"1": [ -"xcl_family_stunky_02", -"xcl_family_stunky_03" -] -}, -"skuntank": { -"3": [ -"xcl_family_stunky_00" -], -"2": [ -"xcl_family_stunky_01", -"xcl_family_stunky_04" -], -"1": [ -"xcl_family_stunky_02", -"xcl_family_stunky_03" -] -}, -"bonsly": { -"3": [ -"xcl_family_sudowoodo_00" -], -"2": [ -"xcl_family_sudowoodo_01", -"xcl_family_sudowoodo_04" -], -"1": [ -"xcl_family_sudowoodo_02", -"xcl_family_sudowoodo_03" -] -}, -"sudowoodo": { -"3": [ -"xcl_family_sudowoodo_00" -], -"2": [ -"xcl_family_sudowoodo_01", -"xcl_family_sudowoodo_04" -], -"1": [ -"xcl_family_sudowoodo_02", -"xcl_family_sudowoodo_03" -] -}, -"sunkern": { -"3": [ -"xcl_family_sunkern_00" -], -"2": [ -"xcl_family_sunkern_01", -"xcl_family_sunkern_04" -], -"1": [ -"xcl_family_sunkern_02", -"xcl_family_sunkern_03" -] -}, -"sunflora": { -"3": [ -"xcl_family_sunkern_00" -], -"2": [ -"xcl_family_sunkern_01", -"xcl_family_sunkern_04" -], -"1": [ -"xcl_family_sunkern_02", -"xcl_family_sunkern_03" -] -}, -"surskit": { -"3": [ -"xcl_family_surskit_00" -], -"2": [ -"xcl_family_surskit_01" -], -"1": [ -"xcl_family_surskit_02", -"xcl_family_surskit_03" -] -}, -"masquerain": { -"3": [ -"xcl_family_surskit_00" -], -"2": [ -"xcl_family_surskit_01" -], -"1": [ -"xcl_family_surskit_02", -"xcl_family_surskit_03" -] -}, -"swablu": { -"3": [ -"xcl_family_swablu_00" -], -"2": [ -"xcl_family_swablu_01" -], -"1": [ -"xcl_family_swablu_02", -"xcl_family_swablu_03" -] -}, -"altaria": { -"3": [ -"xcl_family_swablu_00" -], -"2": [ -"xcl_family_swablu_01" -], -"1": [ -"xcl_family_swablu_02", -"xcl_family_swablu_03" -] -}, -"swinub": { -"3": [ -"xcl_family_swinub_00" -], -"2": [ -"xcl_family_swinub_01" -], -"1": [ -"xcl_family_swinub_02", -"xcl_family_swinub_03" -] -}, -"piloswine": { -"3": [ -"xcl_family_swinub_00" -], -"2": [ -"xcl_family_swinub_01" -], -"1": [ -"xcl_family_swinub_02", -"xcl_family_swinub_03" -] -}, -"mamoswine": { -"3": [ -"xcl_family_swinub_00" -], -"2": [ -"xcl_family_swinub_01" -], -"1": [ -"xcl_family_swinub_02", -"xcl_family_swinub_03" -] -}, -"taillow": { -"3": [ -"xcl_family_taillow_00" -], -"2": [ -"xcl_family_taillow_01" -], -"1": [ -"xcl_family_taillow_02", -"xcl_family_taillow_03" -] -}, -"swellow": { -"3": [ -"xcl_family_taillow_00" -], -"2": [ -"xcl_family_taillow_01" -], -"1": [ -"xcl_family_taillow_02", -"xcl_family_taillow_03" -] -}, -"tangela": { -"3": [ -"xcl_family_tangela_00" -], -"2": [ -"xcl_family_tangela_01" -], -"1": [ -"xcl_family_tangela_02", -"xcl_family_tangela_03" -] -}, -"tangrowth": { -"3": [ -"xcl_family_tangela_00" -], -"2": [ -"xcl_family_tangela_01" -], -"1": [ -"xcl_family_tangela_02", -"xcl_family_tangela_03" -] -}, -"tauros": { -"3": [ -"xcl_family_tauros_miltank_00" -], -"1": [ -"xcl_family_tauros_miltank_02", -"xcl_family_tauros_miltank_03" -], -"2": [ -"xcl_family_tauros_miltank_04", -"xcl_family_tauros_miltank_05" -] -}, -"miltank": { -"3": [ -"xcl_family_tauros_miltank_01" -], -"1": [ -"xcl_family_tauros_miltank_02", -"xcl_family_tauros_miltank_03" -], -"2": [ -"xcl_family_tauros_miltank_04", -"xcl_family_tauros_miltank_05" -] -}, -"teddiursa": { -"3": [ -"xcl_family_teddiursa_00" -], -"2": [ -"xcl_family_teddiursa_01", -"xcl_family_teddiursa_04" -], -"1": [ -"xcl_family_teddiursa_02", -"xcl_family_teddiursa_03" -] -}, -"ursaring": { -"3": [ -"xcl_family_teddiursa_00" -], -"2": [ -"xcl_family_teddiursa_01", -"xcl_family_teddiursa_04" -], -"1": [ -"xcl_family_teddiursa_02", -"xcl_family_teddiursa_03" -] -}, -"tentacool": { -"3": [ -"xcl_family_tentacool_00" -], -"2": [ -"xcl_family_tentacool_01" -], -"1": [ -"xcl_family_tentacool_02", -"xcl_family_tentacool_03" -] -}, -"tentacruel": { -"3": [ -"xcl_family_tentacool_00" -], -"2": [ -"xcl_family_tentacool_01" -], -"1": [ -"xcl_family_tentacool_02", -"xcl_family_tentacool_03" -] -}, -"tepig": { -"3": [ -"xcl_family_tepig_00" -], -"2": [ -"xcl_family_tepig_01", -"xcl_family_tepig_04" -], -"1": [ -"xcl_family_tepig_02", -"xcl_family_tepig_03" -] -}, -"pignite": { -"3": [ -"xcl_family_tepig_00" -], -"2": [ -"xcl_family_tepig_01", -"xcl_family_tepig_04" -], -"1": [ -"xcl_family_tepig_02", -"xcl_family_tepig_03" -] -}, -"emboar": { -"3": [ -"xcl_family_tepig_00" -], -"2": [ -"xcl_family_tepig_01", -"xcl_family_tepig_04" -], -"1": [ -"xcl_family_tepig_02", -"xcl_family_tepig_03" -] -}, -"tinkatink": { -"3": [ -"xcl_family_tinkatink_00" -], -"2": [ -"xcl_family_tinkatink_01", -"xcl_family_tinkatink_04" -], -"1": [ -"xcl_family_tinkatink_02", -"xcl_family_tinkatink_03" -] -}, -"tinkatuff": { -"3": [ -"xcl_family_tinkatink_00" -], -"2": [ -"xcl_family_tinkatink_01", -"xcl_family_tinkatink_04" -], -"1": [ -"xcl_family_tinkatink_02", -"xcl_family_tinkatink_03" -] -}, -"tinkaton": { -"3": [ -"xcl_family_tinkatink_00" -], -"2": [ -"xcl_family_tinkatink_01", -"xcl_family_tinkatink_04" -], -"1": [ -"xcl_family_tinkatink_02", -"xcl_family_tinkatink_03" -] -}, -"togedemaru": { -"3": [ -"xcl_family_togedemaru_00" -], -"2": [ -"xcl_family_togedemaru_01" -], -"1": [ -"xcl_family_togedemaru_02", -"xcl_family_togedemaru_03" -] -}, -"togepi": { -"3": [ -"xcl_family_togepi_00" -], -"2": [ -"xcl_family_togepi_01", -"xcl_family_togepi_04" -], -"1": [ -"xcl_family_togepi_02", -"xcl_family_togepi_03" -] -}, -"togetic": { -"3": [ -"xcl_family_togepi_00" -], -"2": [ -"xcl_family_togepi_01", -"xcl_family_togepi_04" -], -"1": [ -"xcl_family_togepi_02", -"xcl_family_togepi_03" -] -}, -"togekiss": { -"3": [ -"xcl_family_togepi_00" -], -"2": [ -"xcl_family_togepi_01", -"xcl_family_togepi_04" -], -"1": [ -"xcl_family_togepi_02", -"xcl_family_togepi_03" -] -}, -"torchic": { -"3": [ -"xcl_family_torchic_00" -], -"2": [ -"xcl_family_torchic_01", -"xcl_family_torchic_04" -], -"1": [ -"xcl_family_torchic_02", -"xcl_family_torchic_03" -] -}, -"combusken": { -"3": [ -"xcl_family_torchic_00" -], -"2": [ -"xcl_family_torchic_01", -"xcl_family_torchic_04" -], -"1": [ -"xcl_family_torchic_02", -"xcl_family_torchic_03" -] -}, -"blaziken": { -"3": [ -"xcl_family_torchic_00" -], -"2": [ -"xcl_family_torchic_01", -"xcl_family_torchic_04" -], -"1": [ -"xcl_family_torchic_02", -"xcl_family_torchic_03" -] -}, -"torkoal": { -"3": [ -"xcl_family_torkoal_00" -], -"2": [ -"xcl_family_torkoal_01" -], -"1": [ -"xcl_family_torkoal_02", -"xcl_family_torkoal_03" -] -}, -"totodile": { -"3": [ -"xcl_family_totodile_00" -], -"2": [ -"xcl_family_totodile_01", -"xcl_family_totodile_04" -], -"1": [ -"xcl_family_totodile_02", -"xcl_family_totodile_03" -] -}, -"croconaw": { -"3": [ -"xcl_family_totodile_00" -], -"2": [ -"xcl_family_totodile_01", -"xcl_family_totodile_04" -], -"1": [ -"xcl_family_totodile_02", -"xcl_family_totodile_03" -] -}, -"feraligatr": { -"3": [ -"xcl_family_totodile_00" -], -"2": [ -"xcl_family_totodile_01", -"xcl_family_totodile_04" -], -"1": [ -"xcl_family_totodile_02", -"xcl_family_totodile_03" -] -}, -"trapinch": { -"3": [ -"xcl_family_trapinch_00" -], -"2": [ -"xcl_family_trapinch_01" -], -"1": [ -"xcl_family_trapinch_02", -"xcl_family_trapinch_03" -] -}, -"vibrava": { -"3": [ -"xcl_family_trapinch_00" -], -"2": [ -"xcl_family_trapinch_01" -], -"1": [ -"xcl_family_trapinch_02", -"xcl_family_trapinch_03" -] -}, -"flygon": { -"3": [ -"xcl_family_trapinch_00" -], -"2": [ -"xcl_family_trapinch_01" -], -"1": [ -"xcl_family_trapinch_02", -"xcl_family_trapinch_03" -] -}, -"treecko": { -"3": [ -"xcl_family_treecko_00" -], -"2": [ -"xcl_family_treecko_01", -"xcl_family_treecko_04" -], -"1": [ -"xcl_family_treecko_02", -"xcl_family_treecko_03" -] -}, -"grovyle": { -"3": [ -"xcl_family_treecko_00" -], -"2": [ -"xcl_family_treecko_01", -"xcl_family_treecko_04" -], -"1": [ -"xcl_family_treecko_02", -"xcl_family_treecko_03" -] -}, -"sceptile": { -"3": [ -"xcl_family_treecko_00" -], -"2": [ -"xcl_family_treecko_01", -"xcl_family_treecko_04" -], -"1": [ -"xcl_family_treecko_02", -"xcl_family_treecko_03" -] -}, -"tropius": { -"3": [ -"xcl_family_tropius_00" -], -"2": [ -"xcl_family_tropius_01", -"xcl_family_tropius_04" -], -"1": [ -"xcl_family_tropius_02", -"xcl_family_tropius_03" -] -}, -"turtwig": { -"3": [ -"xcl_family_turtwig_00" -], -"2": [ -"xcl_family_turtwig_01", -"xcl_family_turtwig_04" -], -"1": [ -"xcl_family_turtwig_02", -"xcl_family_turtwig_03" -] -}, -"grotle": { -"3": [ -"xcl_family_turtwig_00" -], -"2": [ -"xcl_family_turtwig_01", -"xcl_family_turtwig_04" -], -"1": [ -"xcl_family_turtwig_02", -"xcl_family_turtwig_03" -] -}, -"torterra": { -"3": [ -"xcl_family_turtwig_00" -], -"2": [ -"xcl_family_turtwig_01", -"xcl_family_turtwig_04" -], -"1": [ -"xcl_family_turtwig_02", -"xcl_family_turtwig_03" -] -}, -"hitmontop": { -"3": [ -"xcl_family_tyrogue_00" -], -"2": [ -"xcl_family_tyrogue_01", -"xcl_family_tyrogue_06" -], -"1": [ -"xcl_family_tyrogue_07", -"xcl_family_tyrogue_08" -] -}, -"tyrogue": { -"2": [ -"xcl_family_tyrogue_01", -"xcl_family_tyrogue_03", -"xcl_family_tyrogue_05", -"xcl_family_tyrogue_06" -], -"1": [ -"xcl_family_tyrogue_07", -"xcl_family_tyrogue_08" -] -}, -"hitmonchan": { -"3": [ -"xcl_family_tyrogue_02" -], -"2": [ -"xcl_family_tyrogue_03", -"xcl_family_tyrogue_06" -], -"1": [ -"xcl_family_tyrogue_07", -"xcl_family_tyrogue_08" -] -}, -"hitmonlee": { -"3": [ -"xcl_family_tyrogue_04" -], -"2": [ -"xcl_family_tyrogue_05", -"xcl_family_tyrogue_06" -], -"1": [ -"xcl_family_tyrogue_07", -"xcl_family_tyrogue_08" -] -}, -"unown": { -"1": [ -"xcl_family_unown_00", -"xcl_family_unown_01", -"xcl_family_unown_02", -"xcl_family_unown_03", -"xcl_family_unown_04", -"xcl_family_unown_05", -"xcl_family_unown_06", -"xcl_family_unown_07", -"xcl_family_unown_08", -"xcl_family_unown_09", -"xcl_family_unown_10", -"xcl_family_unown_11", -"xcl_family_unown_12", -"xcl_family_unown_13", -"xcl_family_unown_14", -"xcl_family_unown_15", -"xcl_family_unown_16", -"xcl_family_unown_17", -"xcl_family_unown_18", -"xcl_family_unown_19", -"xcl_family_unown_20", -"xcl_family_unown_21", -"xcl_family_unown_22", -"xcl_family_unown_23", -"xcl_family_unown_24", -"xcl_family_unown_25" -] -}, -"venonat": { -"3": [ -"xcl_family_venonat_00" -], -"2": [ -"xcl_family_venonat_01", -"xcl_family_venonat_04" -], -"1": [ -"xcl_family_venonat_02", -"xcl_family_venonat_03" -] -}, -"venomoth": { -"3": [ -"xcl_family_venonat_00" -], -"2": [ -"xcl_family_venonat_01", -"xcl_family_venonat_04" -], -"1": [ -"xcl_family_venonat_02", -"xcl_family_venonat_03" -] -}, -"volbeat": { -"3": [ -"xcl_family_volbeat_illumise_00" -], -"1": [ -"xcl_family_volbeat_illumise_02", -"xcl_family_volbeat_illumise_03" -], -"2": [ -"xcl_family_volbeat_illumise_04", -"xcl_family_volbeat_illumise_05" -] -}, -"illumise": { -"3": [ -"xcl_family_volbeat_illumise_01" -], -"1": [ -"xcl_family_volbeat_illumise_02", -"xcl_family_volbeat_illumise_03" -], -"2": [ -"xcl_family_volbeat_illumise_04", -"xcl_family_volbeat_illumise_05" -] -}, -"voltorb": { -"3": [ -"xcl_family_voltorb_00" -], -"2": [ -"xcl_family_voltorb_01", -"xcl_family_voltorb_04" -], -"1": [ -"xcl_family_voltorb_02", -"xcl_family_voltorb_03" -] -}, -"electrode": { -"3": [ -"xcl_family_voltorb_00" -], -"2": [ -"xcl_family_voltorb_01", -"xcl_family_voltorb_04" -], -"1": [ -"xcl_family_voltorb_02", -"xcl_family_voltorb_03" -] -}, -"vulpix": { -"3": [ -"xcl_family_vulpix_00" -], -"2": [ -"xcl_family_vulpix_01", -"xcl_family_vulpix_04" -], -"1": [ -"xcl_family_vulpix_02", -"xcl_family_vulpix_03" -] -}, -"ninetales": { -"3": [ -"xcl_family_vulpix_00" -], -"2": [ -"xcl_family_vulpix_01", -"xcl_family_vulpix_04" -], -"1": [ -"xcl_family_vulpix_02", -"xcl_family_vulpix_03" -] -}, -"wailmer": { -"3": [ -"xcl_family_wailmer_00" -], -"2": [ -"xcl_family_wailmer_01", -"xcl_family_wailmer_04" -], -"1": [ -"xcl_family_wailmer_02", -"xcl_family_wailmer_03" -] -}, -"wailord": { -"3": [ -"xcl_family_wailmer_00" -], -"2": [ -"xcl_family_wailmer_01", -"xcl_family_wailmer_04" -], -"1": [ -"xcl_family_wailmer_02", -"xcl_family_wailmer_03" -] -}, -"weedle": { -"3": [ -"xcl_family_weedle_00" -], -"2": [ -"xcl_family_weedle_01", -"xcl_family_weedle_04" -], -"1": [ -"xcl_family_weedle_02", -"xcl_family_weedle_03" -] -}, -"kakuna": { -"3": [ -"xcl_family_weedle_00" -], -"2": [ -"xcl_family_weedle_01", -"xcl_family_weedle_04" -], -"1": [ -"xcl_family_weedle_02", -"xcl_family_weedle_03" -] -}, -"beedrill": { -"3": [ -"xcl_family_weedle_00" -], -"2": [ -"xcl_family_weedle_01", -"xcl_family_weedle_04" -], -"1": [ -"xcl_family_weedle_02", -"xcl_family_weedle_03" -] -}, -"whismur": { -"3": [ -"xcl_family_whismur_00" -], -"2": [ -"xcl_family_whismur_01" -], -"1": [ -"xcl_family_whismur_02", -"xcl_family_whismur_03" -] -}, -"loudred": { -"3": [ -"xcl_family_whismur_00" -], -"2": [ -"xcl_family_whismur_01" -], -"1": [ -"xcl_family_whismur_02", -"xcl_family_whismur_03" -] -}, -"exploud": { -"3": [ -"xcl_family_whismur_00" -], -"2": [ -"xcl_family_whismur_01" -], -"1": [ -"xcl_family_whismur_02", -"xcl_family_whismur_03" -] -}, -"wingull": { -"3": [ -"xcl_family_wingull_00" -], -"2": [ -"xcl_family_wingull_01" -], -"1": [ -"xcl_family_wingull_02", -"xcl_family_wingull_03" -] -}, -"pelipper": { -"3": [ -"xcl_family_wingull_00" -], -"2": [ -"xcl_family_wingull_01" -], -"1": [ -"xcl_family_wingull_02", -"xcl_family_wingull_03" -] -}, -"wynaut": { -"3": [ -"xcl_family_wobbuffet_00" -], -"2": [ -"xcl_family_wobbuffet_01", -"xcl_family_wobbuffet_04" -], -"1": [ -"xcl_family_wobbuffet_02", -"xcl_family_wobbuffet_03" -] -}, -"wobbuffet": { -"3": [ -"xcl_family_wobbuffet_00" -], -"2": [ -"xcl_family_wobbuffet_01", -"xcl_family_wobbuffet_04" -], -"1": [ -"xcl_family_wobbuffet_02", -"xcl_family_wobbuffet_03" -] -}, -"woobat": { -"3": [ -"xcl_family_woobat_00" -], -"2": [ -"xcl_family_woobat_01", -"xcl_family_woobat_04" -], -"1": [ -"xcl_family_woobat_02", -"xcl_family_woobat_03" -] -}, -"swoobat": { -"3": [ -"xcl_family_woobat_00" -], -"2": [ -"xcl_family_woobat_01", -"xcl_family_woobat_04" -], -"1": [ -"xcl_family_woobat_02", -"xcl_family_woobat_03" -] -}, -"wooper": { -"3": [ -"xcl_family_wooper_00" -], -"2": [ -"xcl_family_wooper_01" -], -"1": [ -"xcl_family_wooper_02", -"xcl_family_wooper_03" -] -}, -"quagsire": { -"3": [ -"xcl_family_wooper_00" -], -"2": [ -"xcl_family_wooper_01" -], -"1": [ -"xcl_family_wooper_02", -"xcl_family_wooper_03" -] -}, -"wurmple": { -"3": [ -"xcl_family_wurmple_00", -"xcl_family_wurmple_01" -], -"2": [ -"xcl_family_wurmple_02", -"xcl_family_wurmple_03" -], -"1": [ -"xcl_family_wurmple_04", -"xcl_family_wurmple_05" -] -}, -"silcoon": { -"3": [ -"xcl_family_wurmple_00", -"xcl_family_wurmple_01" -], -"2": [ -"xcl_family_wurmple_02", -"xcl_family_wurmple_03" -], -"1": [ -"xcl_family_wurmple_04", -"xcl_family_wurmple_05" -] -}, -"beautifly": { -"3": [ -"xcl_family_wurmple_00", -"xcl_family_wurmple_01" -], -"2": [ -"xcl_family_wurmple_02", -"xcl_family_wurmple_03" -], -"1": [ -"xcl_family_wurmple_04", -"xcl_family_wurmple_05" -] -}, -"cascoon": { -"3": [ -"xcl_family_wurmple_00", -"xcl_family_wurmple_01" -], -"2": [ -"xcl_family_wurmple_02", -"xcl_family_wurmple_03" -], -"1": [ -"xcl_family_wurmple_04", -"xcl_family_wurmple_05" -] -}, -"dustox": { -"3": [ -"xcl_family_wurmple_00", -"xcl_family_wurmple_01" -], -"2": [ -"xcl_family_wurmple_02", -"xcl_family_wurmple_03" -], -"1": [ -"xcl_family_wurmple_04", -"xcl_family_wurmple_05" -] -}, -"yanma": { -"3": [ -"xcl_family_yanma_00" -], -"2": [ -"xcl_family_yanma_01" -], -"1": [ -"xcl_family_yanma_02", -"xcl_family_yanma_03" -] -}, -"yanmega": { -"3": [ -"xcl_family_yanma_00" -], -"2": [ -"xcl_family_yanma_01" -], -"1": [ -"xcl_family_yanma_02", -"xcl_family_yanma_03" -] -}, -"zangoose": { -"3": [ -"xcl_family_zangoose_00" -], -"2": [ -"xcl_family_zangoose_01" -], -"1": [ -"xcl_family_zangoose_02", -"xcl_family_zangoose_03" -] -}, -"zigzagoon": { -"3": [ -"xcl_family_zigzagoon_00" -], -"2": [ -"xcl_family_zigzagoon_01" -], -"1": [ -"xcl_family_zigzagoon_02", -"xcl_family_zigzagoon_03" -] -}, -"linoone": { -"3": [ -"xcl_family_zigzagoon_00" -], -"2": [ -"xcl_family_zigzagoon_01" -], -"1": [ -"xcl_family_zigzagoon_02", -"xcl_family_zigzagoon_03" -] -}, -"zorua": { -"3": [ -"xcl_family_zorua_00" -], -"2": [ -"xcl_family_zorua_01", -"xcl_family_zorua_03" -], -"1": [ -"xcl_family_zorua_02", -"xcl_family_zorua_04" -] -}, -"zoroark": { -"3": [ -"xcl_family_zorua_00" -], -"2": [ -"xcl_family_zorua_01", -"xcl_family_zorua_03" -], -"1": [ -"xcl_family_zorua_02", -"xcl_family_zorua_04" -] -}, -"zubat": { -"3": [ -"xcl_family_zubat_00" -], -"2": [ -"xcl_family_zubat_01", -"xcl_family_zubat_04" -], -"1": [ -"xcl_family_zubat_02", -"xcl_family_zubat_03" -] -}, -"golbat": { -"3": [ -"xcl_family_zubat_00" -], -"2": [ -"xcl_family_zubat_01", -"xcl_family_zubat_04" -], -"1": [ -"xcl_family_zubat_02", -"xcl_family_zubat_03" -] -}, -"crobat": { -"3": [ -"xcl_family_zubat_00" -], -"2": [ -"xcl_family_zubat_01", -"xcl_family_zubat_04" -], -"1": [ -"xcl_family_zubat_02", -"xcl_family_zubat_03" -] -} -} -} -} \ No newline at end of file diff --git a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/common.lua b/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/common.lua deleted file mode 100644 index f5fc8f9af4..0000000000 --- a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/common.lua +++ /dev/null @@ -1,603 +0,0 @@ ---[[ - common.lua - A collection of frequently used functions and values! -]]-- -require 'enable_mission_board.menu.MemberReturnMenu' -require 'enable_mission_board.common_vars' - -LoadGenType = luanet.import_type('RogueEssence.LevelGen.LoadGen') -ChanceFloorGenType = luanet.import_type('RogueEssence.LevelGen.ChanceFloorGen') - -COMMON.MISSION_TYPE_RESCUE = 0 -COMMON.MISSION_TYPE_ESCORT = 1 -COMMON.MISSION_TYPE_DELIVERY = 2 -COMMON.MISSION_TYPE_OUTLAW = 3 -COMMON.MISSION_TYPE_OUTLAW_ITEM = 4 -COMMON.MISSION_TYPE_OUTLAW_FLEE = 5 -COMMON.MISSION_TYPE_OUTLAW_MONSTER_HOUSE = 6 - -COMMON.MISSION_BOARD_MISSION = 0 -COMMON.MISSION_BOARD_OUTLAW = 1 -COMMON.MISSION_BOARD_TAKEN = 2 - -local characters = { - --Head of Police - Magnezone = { - species = "magnezone", - nickname = 'Magnezone', - instance = 'Magnezone', - gender = Gender.Male, - form = 0, - skin = "normal" - }, - - Magnemite_Left = { - species = "magnemite", - nickname = 'Magnemite', - instance = 'Magnemite_Left', - gender = Gender.Male, - form = 0, - skin = "normal" - }, - - Magnemite_Right = { - species = "magnemite", - nickname = 'Magnemite', - instance = 'Magnemite_Right', - gender = Gender.Female, - form = 0, - skin = "normal" - }, -} - -function COMMON.ShowDestinationMenu(dungeon_entrances, ground_entrances, force_list, speaker, confirm_msg) --get dungeons with a taken mission - local mission_dests = {} - - for i = 1, 8, 1 do - local zone = SV.TakenBoard[i].Zone - if zone ~= nil and zone ~= '' and SV.TakenBoard[i].Taken then - mission_dests[zone] = 1 - end - end - - local open_dests = {} - - --check for unlock of grounds - for ii = 1,#ground_entrances,1 do - if ground_entrances[ii].Flag then - local ground_id = ground_entrances[ii].Zone - local zone_summary = _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Zone]:Get(ground_id) - local ground = _DATA:GetGround(zone_summary.Grounds[ground_entrances[ii].ID]) - local ground_name = ground:GetColoredName() - table.insert(open_dests, { Name=ground_name, Dest=RogueEssence.Dungeon.ZoneLoc(ground_id, -1, ground_entrances[ii].ID, ground_entrances[ii].Entry) }) - end - end - - --check for unlock of dungeons - for ii = 1,#dungeon_entrances,1 do - local zone = dungeon_entrances[ii] - if GAME:DungeonUnlocked(zone) then - local zone_summary = _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Zone]:Get(zone) - if zone_summary.Released then - local zone_name = "" - if _DATA.Save:GetDungeonUnlock(zone) == RogueEssence.Data.GameProgress.UnlockState.Completed then - zone_name = zone_summary:GetColoredName() - else - zone_name = "[color=#00FFFF]"..zone_summary.Name:ToLocal().."[color]" - end - if SV.MissionsEnabled then - if mission_dests[zone] ~= nil then - zone_name = STRINGS:Format("\\uE10F") .. zone_name --open letter - elseif COMMON.HasSidequestInZone(zone) then - zone_name = STRINGS:Format("\\uE111") .. zone_name --exclamation point - end - end - table.insert(open_dests, { Name=zone_name, Dest=RogueEssence.Dungeon.ZoneLoc(zone, 0, 0, 0) }) - end - end - end - - local dest = RogueEssence.Dungeon.ZoneLoc.Invalid - if #open_dests > 1 or force_list then - if before_list ~= nil then - before_list(dest) - end - - SOUND:PlaySE("Menu/Skip") - default_choice = 1 - while true do - UI:ResetSpeaker() - UI:DestinationMenu(open_dests, default_choice) - UI:WaitForChoice() - default_choice = UI:ChoiceResult() - - if default_choice == nil then - break - end - ask_dest = open_dests[default_choice].Dest - if ask_dest.StructID.Segment >= 0 then - --chosen dungeon entry - if speaker ~= nil then - UI:SetSpeaker(speaker) - else - UI:ResetSpeaker(false) - end - UI:DungeonChoice(open_dests[default_choice].Name, ask_dest) - UI:WaitForChoice() - if UI:ChoiceResult() then - dest = ask_dest - break - end - else - dest = ask_dest - break - end - end - elseif #open_dests == 1 then - if open_dests[1].Dest.StructID.Segment < 0 then - --single ground entry - if speaker ~= nil then - UI:SetSpeaker(speaker) - else - UI:ResetSpeaker(false) - SOUND:PlaySE("Menu/Skip") - end - UI:ChoiceMenuYesNo(STRINGS:FormatKey("DLG_ASK_ENTER_GROUND", open_dests[1].Name)) - UI:WaitForChoice() - if UI:ChoiceResult() then - dest = open_dests[1].Dest - end - else - --single dungeon entry - if speaker ~= nil then - UI:SetSpeaker(speaker) - else - UI:ResetSpeaker(false) - SOUND:PlaySE("Menu/Skip") - end - UI:DungeonChoice(open_dests[1].Name, open_dests[1].Dest) - UI:WaitForChoice() - if UI:ChoiceResult() then - dest = open_dests[1].Dest - end - end - else - PrintInfo("No valid destinations found!") - end - - if dest:IsValid() then - if confirm_msg ~= nil then - UI:WaitShowDialogue(confirm_msg) - end - if dest.StructID.Segment > -1 then - --pre-loads the zone on a separate thread while we fade out, just for a little optimization - _DATA:PreLoadZone(dest.ID) - SOUND:PlayBGM("", true) - GAME:FadeOut(false, 20) - GAME:EnterDungeon(dest.ID, dest.StructID.Segment, dest.StructID.ID, dest.EntryPoint, RogueEssence.Data.GameProgress.DungeonStakes.Risk, true, false) - else - SOUND:PlayBGM("", true) - GAME:FadeOut(false, 20) - GAME:EnterZone(dest.ID, dest.StructID.Segment, dest.StructID.ID, dest.EntryPoint) - end - end -end - -function COMMON.EnterDungeonMissionCheck(zoneId, segmentID) - for name, mission in pairs(SV.TakenBoard) do - if mission.Taken and mission.Completion == COMMON.MISSION_INCOMPLETE and zoneId == mission.Zone and mission.Client ~= "" then - if mission.Type == COMMON.MISSION_TYPE_ESCORT or mission.Type == COMMON.MISSION_TYPE_EXPLORATION then -- escort - - local mon_id = RogueEssence.Dungeon.MonsterID(mission.Client, 0, "normal", COMMON.NumToGender(mission.ClientGender)) - -- set the escort level 20% less than the expected level - local level = math.floor(SV.ExpectedLevel[mission.Zone] * 0.80) - local new_mob = _DATA.Save.ActiveTeam:CreatePlayer(_DATA.Save.Rand, mon_id, level, "", -1) - local tactic = _DATA:GetAITactic("stick_together") - new_mob.Tactic = RogueEssence.Data.AITactic(tactic); - _DATA.Save.ActiveTeam.Guests:Add(new_mob) - local talk_evt = RogueEssence.Dungeon.BattleScriptEvent("EscortInteract") - new_mob.ActionEvents:Add(talk_evt) - - local tbl = LTBL(new_mob) - tbl.Escort = name - - UI:ResetSpeaker() - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("MISSION_ESCORT_ADD"):ToLocal(), new_mob.Name)) - end - end - end -end - -function COMMON.NumToGender(num) - local res = Gender.Unknown - if num == 0 then - res = Gender.Genderless - elseif num == 1 then - res = Gender.Male - elseif num == 2 then - res = Gender.Female - end - return res -end - -function COMMON.ResetAnims() - local player_count = GAME:GetPlayerPartyCount() - local guest_count = GAME:GetPlayerGuestCount() - for i = 0, player_count - 1, 1 do - local player = GAME:GetPlayerPartyMember(i) - if not player.Dead then - local anim = RogueEssence.Dungeon.CharAnimAction() - anim.BaseFrameType = 0 --none - anim.AnimLoc = player.CharLoc - anim.CharDir = player.CharDir - TASK:WaitTask(player:StartAnim(anim)) - end - end - - for i = 0, guest_count - 1, 1 do - local guest = GAME:GetPlayerGuestMember(i) - if not guest.Dead then - local anim = RogueEssence.Dungeon.CharAnimAction() - anim.BaseFrameType = 0 --none - anim.AnimLoc = guest.CharLoc - anim.CharDir = guest.CharDir - TASK:WaitTask(guest:StartAnim(anim)) - end - end -end - -function COMMON.InArray(value, array) - for index = 1, #array do - if array[index] == value then - return true - end - end - - return false -- We could ommit this part, as nil is like false -end - -function COMMON.EndDungeonDay(result, zoneId, structureId, mapId, entryId) - COMMON.EndDayCycle() - - COMMON.ResetAnims() - - GAME:EndDungeonRun(result, zoneId, structureId, mapId, entryId, true, true) - if GAME:InRogueMode() then - if result ~= RogueEssence.Data.GameProgress.ResultType.Cleared then - UI:ResetSpeaker() - local msg = STRINGS:FormatKey("DLG_TRY_AGAIN_ASK"); - UI:ChoiceMenuYesNo(msg, false) - UI:WaitForChoice() - result = UI:ChoiceResult() - GAME:WaitFrames(50); - if result then - local curConfig = _DATA.Save.Config - -- set current save file to main save file, this is to get the right starterlist - -- this is hacky since it sets the current save file in an inconsisten state with the lua, but it's technically the most accurate way in lua - -- also this state won't last long- it'll be cleared on our reset - _DATA.Save = _DATA:GetProgress() - local config = RogueEssence.Data.RogueConfig.RerollFromOther(curConfig) - GAME:RestartRogue(config) - else - GAME:RestartToTitle() - end - else - GAME:RestartToTitle() - end - else - GAME:EnterZone(zoneId, structureId, mapId, entryId) - end -end - -function COMMON.GenderToNum(gender) - local res = -1 - if gender == Gender.Genderless then - res = 0 - elseif gender == Gender.Male then - res = 1 - elseif gender == Gender.Female then - res = 2 - end - return res -end - -function COMMON.TableContains(table, val) - for i=1,#table do - if table[i] == val then - return true - end - end - return false -end - - ---called whenever to warp the party out, including guests -function COMMON.WarpOut() - local player_count = GAME:GetPlayerPartyCount() - local guest_count = GAME:GetPlayerGuestCount() - for i = 0, player_count - 1, 1 do - local player = GAME:GetPlayerPartyMember(i) - if not player.Dead then - GAME:WaitFrames(30) - local anim = RogueEssence.Dungeon.CharAbsentAnim(player.CharLoc, player.CharDir) - COMMON.RemoveCharEffects(player) - TASK:WaitTask(_DUNGEON:ProcessBattleFX(player, player, _DATA.SendHomeFX)) - TASK:WaitTask(player:StartAnim(anim)) - end - end - - for i = 0, guest_count - 1, 1 do - local guest = GAME:GetPlayerGuestMember(i) - if not guest.Dead then - GAME:WaitFrames(30) - local anim = RogueEssence.Dungeon.CharAbsentAnim(guest.CharLoc, guest.CharDir) - COMMON.RemoveCharEffects(guest) - TASK:WaitTask(_DUNGEON:ProcessBattleFX(guest, guest, _DATA.SendHomeFX)) - TASK:WaitTask(guest:StartAnim(anim)) - end - end -end - -function COMMON.ResetPose() - local player_count = GAME:GetPlayerPartyCount() - local guest_count = GAME:GetPlayerGuestCount() - for i = 0, player_count - 1, 1 do - local player = GAME:GetPlayerPartyMember(i) - if not player.Dead and player.CharLoc ~= nil and player.CharDir ~= nil then - local anim = RogueEssence.Dungeon.CharAnimIdle(player.CharLoc, player.CharDir) - TASK:WaitTask(player:StartAnim(anim)) - end - end - - for i = 0, guest_count - 1, 1 do - local guest = GAME:GetPlayerGuestMember(i) - if not guest.Dead and guest.CharLoc ~= nil and guest.CharDir ~= nil then - local anim = RogueEssence.Dungeon.CharAnimIdle(guest.CharLoc, guest.CharDir) - TASK:WaitTask(guest:StartAnim(anim)) - end - end -end - ---called whenever a mission is completed -function COMMON.AskMissionWarpOut() - local function MissionWarpOut() - COMMON.WarpOut() - GAME:WaitFrames(80) - --Set minimap state back to old setting - _DUNGEON.ShowMap = SV.TemporaryFlags.PriorMapSetting - SV.TemporaryFlags.PriorMapSetting = nil - TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Escaped)) - end - - local function SetMinimap() - --to prevent accidentally doing something by pressing the button to select yes - GAME:WaitFrames(10) - --Set minimap state back to old setting - _DUNGEON.ShowMap = SV.TemporaryFlags.PriorMapSetting - SV.TemporaryFlags.PriorMapSetting = nil - end - - local has_ongoing_mission = false - local curr_floor = GAME:GetCurrentFloor().ID + 1 - local curr_zone = _ZONE.CurrentZoneID - local curr_segment = _ZONE.CurrentMapID.Segment - - for _, mission in ipairs(SV.TakenBoard) do - if mission.Floor > curr_floor and mission.Taken and mission.Completion == COMMON.MISSION_INCOMPLETE and curr_zone == mission.Zone and curr_segment == mission.Segment then - has_ongoing_mission = true - break - end - end - - - UI:ResetSpeaker() - local state = 0 - while state > -1 do - if state == 0 then - if has_ongoing_mission then - UI:ChoiceMenuYesNo(STRINGS:Format(RogueEssence.StringKey("DLG_MISSION_CONTINUE_ONGOING"):ToLocal()), true) - UI:WaitForChoice() - local leave_dungeon = UI:ChoiceResult() - if leave_dungeon then - UI:ChoiceMenuYesNo(STRINGS:Format(RogueEssence.StringKey("DLG_MISSION_CONTINUE_CONFIRM"):ToLocal()), true) - UI:WaitForChoice() - local leave_confirm = UI:ChoiceResult() - if leave_confirm then - state = -1 - MissionWarpOut() - else - --pause between textboxes if player de-confirms - GAME:WaitFrames(20) - end - else - state = -1 - SetMinimap() - end - else - UI:ChoiceMenuYesNo(STRINGS:Format(RogueEssence.StringKey("DLG_MISSION_CONTINUE_NO_ONGOING"):ToLocal()), false) - UI:WaitForChoice() - local leave_dungeon = UI:ChoiceResult() - if leave_dungeon then - state = -1 - MissionWarpOut() - else - state = -1 - SetMinimap() - end - end - end - end -end - - -function COMMON.RemoveCharEffects(char) - char.StatusEffects:Clear(); - char.ProxyAtk = -1; - char.ProxyDef = -1; - char.ProxyMAtk = -1; - char.ProxyMDef = -1; - char.ProxySpeed = -1; -end - -function COMMON.MakeCharactersFromList(list, retTable) - retTable = retTable or false--return a table of chars rather than multiple chars if this is true - local charTable = {} - local chara = 0 - local length = 0 - for i = 1, #list, 1 do - local name = list[i][1] - length = #list[i] - if length == 1 then--this case is so we can reference characters that aren't on the map. Put them at 0, 0 and hide them - local monster = RogueEssence.Dungeon.MonsterID(characters[name].species, - characters[name].form, - characters[name].skin, - characters[name].gender) - chara = RogueEssence.Ground.GroundChar(monster, RogueElements.Loc(0, 0), Direction.Down, name, characters[name].instance) - chara:ReloadEvents() - GAME:GetCurrentGround():AddTempChar(chara) - GROUND:Hide(chara.EntName) - - elseif length == 2 then --may be inefficient to do a length lookup so often... - local marker = MRKR(list[i][2]) - local monster = RogueEssence.Dungeon.MonsterID(characters[name].species, - characters[name].form, - characters[name].skin, - characters[name].gender) - chara = RogueEssence.Ground.GroundChar(monster, RogueElements.Loc(marker.Position.X, marker.Position.Y), marker.Direction, name, characters[name].instance) - chara:ReloadEvents() - GAME:GetCurrentGround():AddTempChar(chara) - else - local x = list[i][2] - local y = list[i][3] - local direction = list[i][4] - local monster = RogueEssence.Dungeon.MonsterID(characters[name].species, - characters[name].form, - characters[name].skin, - characters[name].gender) - chara = RogueEssence.Ground.GroundChar(monster, RogueElements.Loc(x, y), direction, name, characters[name].instance) - chara:ReloadEvents() - GAME:GetCurrentGround():AddTempChar(chara) - - end - chara:OnMapInit() - local result = RogueEssence.Script.TriggerResult() - TASK:WaitTask(chara:RunEvent(RogueEssence.Script.LuaEngine.EEntLuaEventTypes.EntSpawned, result, chara)) - charTable[i] = chara - end - if retTable then - return charTable - else - return table.unpack(charTable) - end -end - -function COMMON.TeamTurnTo(char) - local player_count = GAME:GetPlayerPartyCount() - local guest_count = GAME:GetPlayerGuestCount() - for i = 0, player_count - 1, 1 do - local player = GAME:GetPlayerPartyMember(i) - if not player.Dead then - DUNGEON:CharTurnToChar(player, char) - end - end - - for i = 0, guest_count - 1, 1 do - local guest = GAME:GetPlayerGuestMember(i) - if not guest.Dead then - DUNGEON:CharTurnToChar(guest, char) - end - end -end - ---used to reward items to the player, sends the item to storage if inv is full -function COMMON.RewardItem(itemID, money, amount) - --if money is true, the itemID is instead the amount of money to award - if money == nil then money = false end - - UI:ResetSpeaker(false)--disable text noise - UI:SetCenter(true) - - - SOUND:PlayFanfare("Fanfare/Item") - - if money then - UI:WaitShowDialogue("Team " .. GAME:GetTeamName() .. " received " .. "[color=#00FFFF]" .. itemID .. "[color]" .. STRINGS:Format("\\uE024") .. ".[pause=40]") - GAME:AddToPlayerMoney(itemID) - else - local itemEntry = RogueEssence.Data.DataManager.Instance:GetItem(itemID) - - --for stackable items, always give 3 of them as a reward - - --give at least 1 item - if amount == nil then amount = 1 end - if itemEntry.MaxStack >= 3 then - --for stackable items, always give 3 of them as a reward - amount = 3 - end - - local item = RogueEssence.Dungeon.InvItem(itemID, false, amount) - - local article = "a" - - local first_letter = string.upper(string.sub(_DATA:GetItem(item.ID).Name:ToLocal(), 1, 1)) - - if first_letter == "A" or first_letter == 'E' or first_letter == 'I' or first_letter == 'O' or first_letter == 'U' then article = 'an' end - - UI:WaitShowDialogue("Team " .. GAME:GetTeamName() .. " received " .. article .. " " .. item:GetDisplayName() ..".[pause=40]") - - --bag is full - equipped count is separate from bag and must be included in the calc - if GAME:GetPlayerBagCount() + GAME:GetPlayerEquippedCount() >= GAME:GetPlayerBagLimit() then - UI:WaitShowDialogue("The " .. item:GetDisplayName() .. " was sent to storage.") - GAME:GivePlayerStorageItem(item.ID, amount) - else - GAME:GivePlayerItem(item.ID, amount) - end - - end - UI:SetCenter(false) - UI:ResetSpeaker() - - -end - ---Given a segment name string obtained by segment.ToString(), return a colored segment. ---This is obtained by removing the last part of the segment name as it is the floor indicator and wrapping yellow around it -function COMMON.CreateColoredSegmentString(segment_name) - local split_name = {} - - local index = 1 - - for str in string.gmatch(segment_name, "([^%s]+)") do - table.insert(split_name, index, str) - index = index + 1 - end - - local final_name = '' - - for i = 1, #split_name, 1 do - local cur_word = split_name[i] - - if i == 1 then - final_name = cur_word - elseif i ~= #split_name then - final_name = final_name .. ' ' .. cur_word - end - end - - return '[color=#FFC663]'..final_name..'[color]' -end - - -function COMMON.GetNoMissionFloors(current_segment) - local no_mission_floors = {} - local floor_count = current_segment.FloorCount - for i=0, floor_count - 1, 1 do - local map_gen = current_segment:GetMapGen(i) - local type = LUA_ENGINE:TypeOf(map_gen) - if type:IsAssignableTo(luanet.ctype(LoadGenType)) or type:IsAssignableTo(luanet.ctype(ChanceFloorGenType)) then - PrintInfo("Type is of "..type.FullName..", "..i.." is a no mission floor!") - no_mission_floors[i+1] = 1 - end - end - return no_mission_floors -end \ No newline at end of file diff --git a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/common_vars.lua b/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/common_vars.lua deleted file mode 100644 index 3a1bd4f2ca..0000000000 --- a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/common_vars.lua +++ /dev/null @@ -1,68 +0,0 @@ ---[[ - common_vars.lua - Save vars -]]-- - -function COMMON.CreateMission(key, mission) - SV.missions.Missions[key] = mission - - if mission.DestZone ~= nil then - for i = 1, 8, 1 do - local zone = SV.TakenBoard[i].Zone - if zone ~= nil and zone ~= '' and zone == mission.DestZone then - SV.TakenBoard[i].Taken = false - end - end - end -end - ---Check to see if there is an active sidequest in the current zone -function COMMON.HasSidequestInZone(zone) - for name, mission in pairs(SV.missions.Missions) do - if mission.DestZone == zone then - return true - end - end - - return false -end - -function COMMON.ExitDungeonMissionCheckEx(result, rescue, zoneId, segmentID) - --remove all guests from the dungeon - _DATA.Save.ActiveTeam.Guests:Clear() - - --Remove any lost/stolen items. If the item's ID starts with "mission" then delete it on exiting the dungeon. - local itemCount = GAME:GetPlayerBagCount() - local item - - local i = 0 - while i <= itemCount - 1 do - item = GAME:GetPlayerBagItem(i) - if string.sub(item.ID, 1, 7) == "mission" then - GAME:TakePlayerBagItem(i) - itemCount = itemCount - 1 - else - i = i + 1 - end - end - - --send equipped items to storage - for i = 1, GAME:GetPlayerPartyCount(), 1 do - item = GAME:GetPlayerEquippedItem(i-1) - if string.sub(item.ID, 1, 7) == "mission" then - GAME:TakePlayerEquippedItem(i-1) - end - end - - if SV.MissionsEnabled then - - MISSION_GEN.EndOfDay(result, segmentID) - - if SV.TemporaryFlags.MissionCompleted then - COMMON.EndDungeonDay(result, 'guildmaster_island', -1, 2, 0) - return true - end - end - - return false -end \ No newline at end of file diff --git a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/event.lua b/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/event.lua deleted file mode 100644 index ed7ba8ed80..0000000000 --- a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/event.lua +++ /dev/null @@ -1,5 +0,0 @@ -require 'enable_mission_board.event_single' -require 'enable_mission_board.event_battle' -require 'enable_mission_board.event_misc' -require 'enable_mission_board.event_mapgen' - diff --git a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/event_battle.lua b/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/event_battle.lua deleted file mode 100644 index a5aabf1dd1..0000000000 --- a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/event_battle.lua +++ /dev/null @@ -1,231 +0,0 @@ -require 'enable_mission_board.common' - -RedirectionType = luanet.import_type('PMDC.Dungeon.Redirected') -DmgMultType = luanet.import_type('PMDC.Dungeon.DmgMult') - -function BATTLE_SCRIPT.EscortInteract(owner, ownerChar, context, args) - context.CancelState.Cancel = true - local oldDir = context.Target.CharDir - DUNGEON:CharTurnToChar(context.Target, context.User) - UI:SetSpeaker(context.Target) - - --Basic for now, but choose a different line based on mission type/special - --Should be expanded on in the future to be more dynamic, and to have more special lines for special pairs - local tbl = LTBL(context.Target) - local mission_slot = tbl.Escort - local job = SV.TakenBoard[mission_slot] - - if job.Type == COMMON.MISSION_TYPE_EXPLORATION then - local zone_string = GAME:GetCurrentDungeon().Segments[job.Segment]:ToString() - zone_string = COMMON.CreateColoredSegmentString(zone_string) - local floor = SV.StairType[job.Zone] .. '[color=#00FFFF]' .. tostring(job.Floor) .. '[color]' .. "F" - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("MISSION_EXPLORATION_INTERACT"):ToLocal(), zone_string , floor)) - elseif job.Type == COMMON.MISSION_TYPE_ESCORT then - if job.Special == MISSION_GEN.SPECIAL_CLIENT_LOVER then - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("MISSION_ESCORT_LOVER_INTERACT"):ToLocal())) - else - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("MISSION_ESCORT_INTERACT"):ToLocal(), _DATA:GetMonster(job.Target):GetColoredName())) - end - end - context.Target.CharDir = oldDir -end - -function BATTLE_SCRIPT.RescueReached(owner, ownerChar, context, args) - -- Set the nickname of the target, removing the gender sign - local base_name = RogueEssence.Data.DataManager.Instance.DataIndices[RogueEssence.Data.DataManager.DataType.Monster]:Get(context.Target.BaseForm.Species).Name:ToLocal() - GAME:SetCharacterNickname(context.Target, base_name) - - context.CancelState.Cancel = false - context.TurnCancel.Cancel = true - - local targetName = _DATA:GetMonster(context.Target.BaseForm.Species):GetColoredName() - - local oldDir = context.Target.CharDir - - local tbl = LTBL(context.Target) - local mission = SV.TakenBoard[tbl.Mission] - DUNGEON:CharTurnToChar(context.Target, context.User) - UI:ResetSpeaker() - - if mission.Type == COMMON.MISSION_TYPE_RESCUE then - RescueCheck(context, targetName, mission) - elseif mission.Type == COMMON.MISSION_TYPE_DELIVERY then - DeliveryCheck(context, targetName, mission) - end -end - -function RescueCheck(context, targetName, mission) - UI:ChoiceMenuYesNo("Yes! You've found " .. targetName .. "!\nDo you want to use your badge to rescue " .. targetName .. "?", false) - UI:WaitForChoice() - local use_badge = UI:ChoiceResult() - if use_badge then - --Mark mission completion flags - SV.TemporaryFlags.MissionCompleted = true - --Clear but remember minimap state - SV.TemporaryFlags.PriorMapSetting = _DUNGEON.ShowMap - _DUNGEON.ShowMap = _DUNGEON.MinimapState.None - GAME:WaitFrames(20) - mission.Completion = 1 - UI:WaitShowDialogue("Your badge shines on " .. targetName .. ", and ".. targetName .. " is transported away magically!" ) - GAME:WaitFrames(20) - UI:SetSpeaker(context.Target) - - --different responses for special targets - if mission.Special == MISSION_GEN.SPECIAL_CLIENT_CHILD then - UI:SetSpeakerEmotion("Joyous") - UI:WaitShowDialogue("Thank you for rescuing me! This place was so scary! I can't wait to see my family again!") - elseif mission.Special == MISSION_GEN.SPECIAL_CLIENT_FRIEND then - UI:WaitShowDialogue("Oh, my friend sent you to rescue me? Thank goodness! We'll see you in town later to say thanks!") - elseif mission.Special == MISSION_GEN.SPECIAL_CLIENT_RIVAL then - UI:WaitShowDialogue("Tch, my rival sent you to rescue me, huh? Well, thank you. We'll reward you later in town.") - elseif mission.Special == MISSION_GEN.SPECIAL_CLIENT_LOVER then - UI:SetSpeakerEmotion("Joyous") - UI:WaitShowDialogue("Oh, my beloved " .. _DATA:GetMonster(mission.Client):GetColoredName() .. " sent you to rescue me? I can't wait to reunite with them!") - else - UI:WaitShowDialogue("Thanks for the rescue!\nI'll see you in town after with your reward!") - end - GAME:WaitFrames(20) - UI:ResetSpeaker() - UI:WaitShowDialogue(targetName .. " escaped from the dungeon!") - GAME:WaitFrames(20) - -- warp out - TASK:WaitTask(_DUNGEON:ProcessBattleFX(context.Target, context.Target, _DATA.SendHomeFX)) - _DUNGEON:RemoveChar(context.Target) - GAME:WaitFrames(50) - COMMON.AskMissionWarpOut() - else - --quickly hide the minimap for the 20 frame pause - local map_setting = _DUNGEON.ShowMap - _DUNGEON.ShowMap = _DUNGEON.MinimapState.None - GAME:WaitFrames(20) - UI:SetSpeaker(context.Target) - if mission.Special == MISSION_GEN.SPECIAL_CLIENT_CHILD then - UI:SetSpeakerEmotion("Crying") - UI:WaitShowDialogue("Waaah! It's s-scary here! P-please help me!") - elseif mission.Special == MISSION_GEN.SPECIAL_CLIENT_FRIEND then - UI:SetSpeakerEmotion("Surprised") - UI:WaitShowDialogue("Please don't leave me here! My friend is probably worried sick!") - elseif mission.Special == MISSION_GEN.SPECIAL_CLIENT_RIVAL then - UI:SetSpeakerEmotion("Worried") - UI:WaitShowDialogue("Woah, don't just leave me hanging here!") - elseif mission.Special == MISSION_GEN.SPECIAL_CLIENT_LOVER then - UI:SetSpeakerEmotion("Worried") - UI:WaitShowDialogue("Please, get me out of here! I just want to see my dear " .. _DATA:GetMonster(mission.Client):GetColoredName() .. " again!") - else - UI:SetSpeakerEmotion("Surprised") - UI:WaitShowDialogue("H-hey! Don't just leave me here!") - end - --change map setting back to what it was - _DUNGEON.ShowMap = map_setting - GAME:WaitFrames(20) - end -end - -function DeliveryCheck(context, targetName, mission) - local inv_slot = GAME:FindPlayerItem(mission.Item, false, true) - local team_slot = GAME:FindPlayerItem(mission.Item, true, false) - local has_item = inv_slot:IsValid() or team_slot:IsValid() - local item_name = RogueEssence.Dungeon.InvItem(mission.Item):GetDisplayName() - - if has_item then - UI:ChoiceMenuYesNo("Yes! You've located " .. targetName .. "!" .. " Do you want to deliver the requested " .. item_name .. " to " .. targetName .. "?") - UI:WaitForChoice() - local deliver_item = UI:ChoiceResult() - if deliver_item then - SV.TemporaryFlags.MissionCompleted = true - mission.Completion = 1 - --Clear but remember minimap state - SV.TemporaryFlags.PriorMapSetting = _DUNGEON.ShowMap - _DUNGEON.ShowMap = _DUNGEON.MinimapState.None - -- Take from inventory first before held items - if inv_slot:IsValid() then - GAME:TakePlayerBagItem(inv_slot.Slot) - else - GAME:TakePlayerEquippedItem(team_slot.Slot) - end - GAME:WaitFrames(20) - UI:SetSpeaker(context.Target) - UI:WaitShowDialogue("Thanks for the " .. item_name .. "!\n I'll see you in town after with your reward!") - GAME:WaitFrames(20) - UI:ResetSpeaker() - UI:WaitShowDialogue(targetName .. " escaped from the dungeon!") - GAME:WaitFrames(20) - TASK:WaitTask(_DUNGEON:ProcessBattleFX(context.Target, context.Target, _DATA.SendHomeFX)) - _DUNGEON:RemoveChar(context.Target) - GAME:WaitFrames(50) - COMMON.AskMissionWarpOut() - else --they are sad if you dont give them the item - --quickly hide the minimap for the 20 frame pause - local map_setting = _DUNGEON.ShowMap - _DUNGEON.ShowMap = _DUNGEON.MinimapState.None - GAME:WaitFrames(20) - UI:SetSpeaker(context.Target) - UI:SetSpeakerEmotion("Sad") - UI:WaitShowDialogue("Oh, please! I really need that " .. item_name .. "...") - --change map setting back to what it was - _DUNGEON.ShowMap = map_setting - GAME:WaitFrames(20) end - else - UI:WaitShowDialogue("The requested " .. item_name .. " isn't in the Treasure Bag.\nThere is nothing to deliver.") - --quickly hide the minimap for the 20 frame pause - local map_setting = _DUNGEON.ShowMap - _DUNGEON.ShowMap = _DUNGEON.MinimapState.None - GAME:WaitFrames(20) - UI:SetSpeaker(context.Target) - UI:SetSpeakerEmotion("Sad") - UI:WaitShowDialogue("Huh, you don't have the " .. item_name .. "? That's too bad...") - --change map setting back to what it was - _DUNGEON.ShowMap = map_setting - GAME:WaitFrames(20) - end -end - - -function BATTLE_SCRIPT.EscortReached(owner, ownerChar, context, args) - context.CancelState.Cancel = false - context.TurnCancel.Cancel = true - --Mark this as the last dungeon entered. - local tbl = LTBL(context.Target) - if tbl ~= nil and tbl.Mission ~= nil then - local mission = SV.TakenBoard[tbl.Mission] - local escort = COMMON.FindMissionEscort(tbl.Mission) - local escortName = _DATA:GetMonster(mission.Client):GetColoredName() - if escort and not escort.Dead then - local oldDir = context.Target.CharDir - DUNGEON:CharTurnToChar(context.Target, context.User) - UI:ResetSpeaker() - if math.abs(escort.CharLoc.X - context.Target.CharLoc.X) <= 4 and math.abs(escort.CharLoc.Y - context.Target.CharLoc.Y) <= 4 then - --Mark mission completion flags - SV.TemporaryFlags.MissionCompleted = true - mission.Completion = 1 - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("MISSION_ESCORT_REACHED"):ToLocal(), escortName)) - --Clear but remember minimap state - SV.TemporaryFlags.PriorMapSetting = _DUNGEON.ShowMap - _DUNGEON.ShowMap = _DUNGEON.MinimapState.None - GAME:WaitFrames(20) - - UI:SetSpeaker(escort) - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("MISSION_ESCORT_THANKS"):ToLocal(), _DATA:GetMonster(context.Target.CurrentForm.Species):GetColoredName())) - GAME:WaitFrames(20) - UI:ResetSpeaker() - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("MISSION_ESCORT_DEPART"):ToLocal(), escortName)) - GAME:WaitFrames(20) - - -- warp out - TASK:WaitTask(_DUNGEON:ProcessBattleFX(escort, escort, _DATA.SendHomeFX)) - _DUNGEON:RemoveChar(escort) - _ZONE.CurrentMap.DisplacedChars:Remove(escort) - GAME:WaitFrames(70) - TASK:WaitTask(_DUNGEON:ProcessBattleFX(context.Target, context.Target, _DATA.SendHomeFX)) - _DUNGEON:RemoveChar(context.Target) - - GAME:WaitFrames(50) - COMMON.AskMissionWarpOut() - else - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("MISSION_ESCORT_UNAVAILABLE"):ToLocal(), escortName)) - end - else - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("MISSION_ESCORT_UNAVAILABLE"):ToLocal(), escortName)) - end - end -end diff --git a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/event_mapgen.lua b/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/event_mapgen.lua deleted file mode 100644 index 73948b54be..0000000000 --- a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/event_mapgen.lua +++ /dev/null @@ -1,149 +0,0 @@ -require 'enable_mission_board.common' - -function ZONE_GEN_SCRIPT.GenerateMissionFromSV(zoneContext, context, queue, seed, args) - if _DATA.Save.Rescue ~= nil and _DATA.Save.Rescue.Rescuing then - return - end - - if not SV.MissionsEnabled then - return - end - - SV.DestinationFloorNotified = false - SV.MonsterHouseMessageNotified = false - SV.OutlawDefeated = false - SV.OutlawGoonsDefeated = false - SV.OutlawItemPickedUp = false - --local partner = GAME:GetPlayerPartyMember(1) - --local tbl = LTBL(partner) - --tbl.MissionNumber = nil - --tbl.MissionType = nil - --tbl.EscortMissionNum = nil - - local curMission = nil - local missionType = nil - local missionNum = nil - local escortMissionNum = nil - local destinationFloor = false - local outlawFloor = false - local activeEffect = RogueEssence.Data.ActiveEffect() - - for _, name in ipairs(COMMON.GetSortedKeys(SV.TakenBoard)) do - mission = SV.TakenBoard[name] - if mission.Taken and mission.Completion == COMMON.MISSION_INCOMPLETE and zoneContext.CurrentZone == mission.Zone and mission.BackReference ~= COMMON.FLEE_BACKREFERENCE then - if mission.Type == COMMON.MISSION_TYPE_ESCORT or mission.Type == COMMON.MISSION_TYPE_EXPLORATION then - escortMissionNum = name - end - - if zoneContext.CurrentSegment == mission.Segment and zoneContext.CurrentID + 1 == mission.Floor then - curMission = mission - missionNum = name - missionType = mission.Type - PrintInfo("Spawning Mission Goal") - local outlaw_arr = { - COMMON.MISSION_TYPE_OUTLAW, - COMMON.MISSION_TYPE_OUTLAW_ITEM, - COMMON.MISSION_TYPE_OUTLAW_FLEE, - COMMON.MISSION_TYPE_OUTLAW_MONSTER_HOUSE - } - - if COMMON.TableContains(outlaw_arr, mission.Type) then -- outlaw - outlawFloor = true - else - if mission.Type == COMMON.MISSION_TYPE_RESCUE or mission.Type == COMMON.MISSION_TYPE_DELIVERY or mission.Type == COMMON.MISSION_TYPE_ESCORT then - local specificTeam = RogueEssence.LevelGen.SpecificTeamSpawner() - local post_mob = RogueEssence.LevelGen.MobSpawn() - post_mob.BaseForm = RogueEssence.Dungeon.MonsterID(mission.Target, 0, "normal", COMMON.NumToGender(mission.TargetGender)) - post_mob.Tactic = "slow_wander" - post_mob.Level = RogueElements.RandRange(50) - if mission.Type == COMMON.MISSION_TYPE_RESCUE or mission.Type == COMMON.MISSION_TYPE_DELIVERY then -- rescue - local dialogue = RogueEssence.Dungeon.BattleScriptEvent("RescueReached") - post_mob.SpawnFeatures:Add(PMDC.LevelGen.MobSpawnInteractable(dialogue)) - post_mob.SpawnFeatures:Add(PMDC.LevelGen.MobSpawnLuaTable('{ Mission = '..name..' }')) - elseif mission.Type == COMMON.MISSION_TYPE_ESCORT then -- escort - local dialogue = RogueEssence.Dungeon.BattleScriptEvent("EscortReached") - post_mob.SpawnFeatures:Add(PMDC.LevelGen.MobSpawnInteractable(dialogue)) - post_mob.SpawnFeatures:Add(PMDC.LevelGen.MobSpawnLuaTable('{ Mission = '..name..' }')) - end - specificTeam.Spawns:Add(post_mob) - PrintInfo("Creating Spawn") - local picker = LUA_ENGINE:MakeGenericType(PresetMultiTeamSpawnerType, { MapGenContextType }, { }) - picker.Spawns:Add(specificTeam) - PrintInfo("Creating Step") - local mobPlacement = LUA_ENGINE:MakeGenericType(PlaceRandomMobsStepType, { MapGenContextType }, { picker }) - PrintInfo("Setting everything else") - - mobPlacement.Ally = true - mobPlacement.Filters:Add(PMDC.LevelGen.RoomFilterConnectivity(PMDC.LevelGen.ConnectivityRoom.Connectivity.Main)) - mobPlacement.ClumpFactor = 20 - PrintInfo("Enqueueing") - -- Priority 5.2.1 is for NPC spawning in PMDO, but any dev can choose to roll with their own standard of priority. - local priority = RogueElements.Priority(5, 2, 1) - queue:Enqueue(priority, mobPlacement) - elseif mission.Type == COMMON.MISSION_TYPE_LOST_ITEM then - local lost_item = RogueEssence.Dungeon.MapItem(mission.Item) - PrintInfo("Spawning Lost Item "..lost_item.Value) - local preset_picker = LUA_ENGINE:MakeGenericType(PresetPickerType, { MapItemType }, { lost_item }) - local multi_preset_picker = LUA_ENGINE:MakeGenericType(PresetMultiRandType, { MapItemType }, { preset_picker }) - local picker_spawner = LUA_ENGINE:MakeGenericType(PickerSpawnType, { MapGenContextType, MapItemType }, { multi_preset_picker }) - local random_room_spawn = LUA_ENGINE:MakeGenericType(RandomRoomSpawnStepType, { MapGenContextType, MapItemType }, { }) - random_room_spawn.Spawn = picker_spawner - random_room_spawn.Filters:Add(PMDC.LevelGen.RoomFilterConnectivity(PMDC.LevelGen.ConnectivityRoom.Connectivity.Main)) - local priority = RogueElements.Priority(5, 2, 1) - queue:Enqueue(priority, random_room_spawn) - end - destinationFloor = true - end - end - end - end - if missionNum ~= nil then - --tbl.MissionNumber = missionNum - end - - if destinationFloor then - -- add destination floor notification - activeEffect.OnMapStarts:Add(-6, RogueEssence.Dungeon.SingleCharScriptEvent("DestinationFloor", '{ Mission = '..missionNum..' }')) - - if missionType == COMMON.MISSION_TYPE_EXPLORATION and curMission ~= nil then --Exploration - local escort = COMMON.FindMissionEscort(missionNum) - local clientName = curMission.Client - if escort ~= nil then - PrintInfo("Client name: "..clientName) - activeEffect.OnMapStarts:Add(-6, RogueEssence.Dungeon.SingleCharScriptEvent("ExplorationReached", '{ Mission = '..escortMissionNum..' }')) - end - elseif missionType == COMMON.MISSION_TYPE_LOST_ITEM then - activeEffect.OnPickups:Add(-6, RogueEssence.Dungeon.ItemScriptEvent("MissionItemPickup", '{ Mission = '..missionNum..' }')) - end - - local npcMissions = { COMMON.MISSION_TYPE_DELIVERY, COMMON.MISSION_TYPE_ESCORT, COMMON.MISSION_TYPE_RESCUE } - if COMMON.TableContains(npcMissions, missionType) then - activeEffect.OnMapTurnEnds:Add(-6, RogueEssence.Dungeon.SingleCharScriptEvent("MobilityEndTurn", '{ Mission = '..missionNum..' }')) - end - --tbl.MissionType = COMMON.MISSION_BOARD_MISSION - end - if outlawFloor then - activeEffect.OnDeaths:Add(-6, RogueEssence.Dungeon.SingleCharScriptEvent("OnOutlawDeath", '{ Mission = '..missionNum..' }')) - if missionType == COMMON.MISSION_TYPE_OUTLAW then - activeEffect.OnTurnEnds:Add(-6, RogueEssence.Dungeon.SingleCharScriptEvent("OutlawCheck", '{ Mission = '..missionNum..' }')) - elseif missionType == COMMON.MISSION_TYPE_OUTLAW_FLEE then - activeEffect.OnMapTurnEnds:Add(-6, RogueEssence.Dungeon.SingleCharScriptEvent("OutlawFleeStairsCheck", '{ Mission = '..missionNum..' }')) - activeEffect.OnTurnEnds:Add(-6, RogueEssence.Dungeon.SingleCharScriptEvent("OutlawCheck", '{ Mission = '..missionNum..' }')) - elseif missionType == COMMON.MISSION_TYPE_OUTLAW_ITEM then - activeEffect.OnPickups:Add(-6, RogueEssence.Dungeon.ItemScriptEvent("OutlawItemPickup", '{ Mission = '..missionNum..' }')) - activeEffect.OnTurnEnds:Add(-6, RogueEssence.Dungeon.SingleCharScriptEvent("OutlawItemCheck", '{ Mission = '..missionNum..' }')) - elseif missionType == COMMON.MISSION_TYPE_OUTLAW_MONSTER_HOUSE then - activeEffect.OnTurnEnds:Add(-6, RogueEssence.Dungeon.SingleCharScriptEvent("OnMonsterHouseOutlawCheck", '{ Mission = '..missionNum..' }')) - end - - activeEffect.OnMapStarts:Add(-11, RogueEssence.Dungeon.SingleCharScriptEvent("SpawnOutlaw", '{ Mission = '..missionNum..' }')) - activeEffect.OnMapStarts:Add(-6, RogueEssence.Dungeon.SingleCharScriptEvent("OutlawFloor", '{ Mission = '..missionNum..' }')) - --tbl.MissionType = COMMON.MISSION_BOARD_MISSION - end - - if destinationFloor or outlawFloor then - local destNote = LUA_ENGINE:MakeGenericType( MapEffectStepType, { MapGenContextType }, { activeEffect }) - local priority = RogueElements.Priority(-6) - queue:Enqueue(priority, destNote) - end -end \ No newline at end of file diff --git a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/event_misc.lua b/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/event_misc.lua deleted file mode 100644 index 8b0052a82f..0000000000 --- a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/event_misc.lua +++ /dev/null @@ -1,28 +0,0 @@ -require 'enable_mission_board.common' - -function ITEM_SCRIPT.MissionItemPickup(owner, ownerChar, context, args) - local mission_num = args.Mission - local mission = SV.TakenBoard[mission_num] - if mission.Item == context.Item.Value then - mission.Completion = COMMON.MISSION_COMPLETE - SV.TemporaryFlags.MissionCompleted = true - GAME:WaitFrames(70) - UI:ResetSpeaker() - UI:WaitShowDialogue("Yes! You found " .. _DATA:GetMonster(mission.Client):GetColoredName() .. "'s " .. context.Item:GetDungeonName() .. "!") - --Clear but remember minimap state - SV.TemporaryFlags.PriorMapSetting = _DUNGEON.ShowMap - _DUNGEON.ShowMap = _DUNGEON.MinimapState.None - - --Slight pause before asking to warp out - GAME:WaitFrames(20) - COMMON.AskMissionWarpOut() - end -end - -function ITEM_SCRIPT.OutlawItemPickup(owner, ownerChar, context, args) - local mission_num = args.Mission - local mission = SV.TakenBoard[mission_num] - if mission.Item == context.Item.Value then - SV.OutlawItemPickedUp = true - end -end diff --git a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/event_single.lua b/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/event_single.lua deleted file mode 100644 index 6b966dbecc..0000000000 --- a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/event_single.lua +++ /dev/null @@ -1,462 +0,0 @@ -require 'enable_mission_board.common' - -ListType = luanet.import_type('System.Collections.Generic.List`1') -MobSpawnType = luanet.import_type('RogueEssence.LevelGen.MobSpawn') - - -function SINGLE_CHAR_SCRIPT.ExplorationReached(owner, ownerChar, context, args) - if context.User ~= nil then - return - end - local index = args.Mission - local mission = SV.TakenBoard[index] - local escort = COMMON.FindMissionEscort(index) - local escortName = _DATA:GetMonster(mission.Client):GetColoredName() - PrintInfo("Exploration reached at index "..index.." !") - if escort ~= nil and escortName ~= nil and not escort.Dead then - UI:ResetSpeaker() - SV.TemporaryFlags.MissionCompleted = true - mission.Completion = 1 - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("MISSION_EXPLORATION_REACHED"):ToLocal(), escortName)) - SV.TemporaryFlags.PriorMapSetting = _DUNGEON.ShowMap - _DUNGEON.ShowMap = _DUNGEON.MinimapState.None - GAME:WaitFrames(20) - UI:SetSpeaker(escort) - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("MISSION_EXPLORATION_THANKS"):ToLocal())) - GAME:WaitFrames(20) - UI:ResetSpeaker() - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("MISSION_EXPLORATION_DEPART"):ToLocal(), escortName)) - GAME:WaitFrames(20) - - -- warp out - TASK:WaitTask(_DUNGEON:ProcessBattleFX(escort, escort, _DATA.SendHomeFX)) - _DUNGEON:RemoveChar(escort) - _ZONE.CurrentMap.DisplacedChars:Remove(escort) - - GAME:WaitFrames(50) - COMMON.AskMissionWarpOut() - else - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("MISSION_ESCORT_UNAVAILABLE"):ToLocal(), escortName)) - end -end - - - -function SpawnOutlaw(origin, radius, mission_num) - local mission = SV.TakenBoard[mission_num] - local max_boost = 128 - local top_left = RogueElements.Loc(origin.X - radius, origin.Y - radius) - local bottom_right = RogueElements.Loc(origin.X + radius, origin.Y + radius) - - local rect_area = RogueElements.Loc(1) - local rect_area2 = RogueElements.Loc(3) - function checkBlock(loc) - - local result = _ZONE.CurrentMap:TileBlocked(loc) - return result - end - - function checkDiagBlock(loc) - return true - end - - local spawn_candidates = {} - for x = top_left.X, bottom_right.X, 1 do - for y = top_left.Y, bottom_right.Y, 1 do - local testLoc = RogueElements.Loc(x, y) - --local is_choke_point = RogueElements.Grid.IsChokePoint(testLoc - rect_area, rect_area2, testLoc, checkBlock, checkDiagBlock) - local tile_block = _ZONE.CurrentMap:TileBlocked(testLoc) - local char_at = _ZONE.CurrentMap:GetCharAtLoc(testLoc) - - --don't spawn the outlaw directly next to the player or their teammates - local next_to_player_units = false - for i = 1, GAME:GetPlayerPartyCount(), 1 do - local party_member = GAME:GetPlayerPartyMember(i-1) - if math.abs(party_member.CharLoc.X - x) <= 1 and math.abs(party_member.CharLoc.Y - y) <= 1 then - next_to_player_units = true - break - end - end - - --guests too! - for i = 1, GAME:GetPlayerGuestCount(), 1 do - local party_member = GAME:GetPlayerGuestMember(i-1) - if math.abs(party_member.CharLoc.X - x) <= 1 and math.abs(party_member.CharLoc.Y - y) <= 1 then - next_to_player_units = true - break - end - end - - if tile_block == false and char_at == nil and not next_to_player_units then - table.insert(spawn_candidates, testLoc) - end - end - end - - if #spawn_candidates < 1 then - PrintInfo("Error: Outlaw couldn't spawn for current floor, not enough spawn candidates.") - return - end - - local spawn_loc = spawn_candidates[_DATA.Save.Rand:Next(1, #spawn_candidates)] - local new_team = RogueEssence.Dungeon.MonsterTeam() - local mob_data = RogueEssence.Dungeon.CharData(true) - local base_form_idx = 0 - local form = _DATA:GetMonster(mission.Target).Forms[base_form_idx] - -- local gender = form:RollGender(RogueElements.MathUtils.Rand) - mob_data.BaseForm = RogueEssence.Dungeon.MonsterID(mission.Target, base_form_idx, "normal", COMMON.NumToGender(mission.TargetGender)) - mob_data.Level = math.floor(SV.ExpectedLevel[mission.Zone] * 1.15) - local ability = form:RollIntrinsic(_DATA.Save.Rand, 3) - mob_data.BaseIntrinsics[0] = ability - local new_mob = RogueEssence.Dungeon.Character(mob_data) - - - --TODO: Add logic to make sure outlaw has at least one decent attacking move based on their level. - --.Data.Category == RogueEssence.Data.BattleData.SkillCategory.Physical - --Pick 4 moves at random in the mon's level up table at that point. - --certain moves are blacklisted due to snaids. - local skill_candidates = {} - local skill_blacklist = {'teleport', 'gullotine', 'sheer_cold', 'horn_drill', 'fissure', 'memento', - 'healing_wish', 'lunar_dance', 'self_destruct', 'explosion', 'final_gambit', 'perish_song', - 'dragon_rage'} - - --print("Outlaw level is!: " .. tostring(mob_data.Level)) - --generate the skill candidate list based on level and the blacklist - local outlaw_form = _DATA:GetMonster(new_mob.BaseForm.Species).Forms[new_mob.BaseForm.Form] - for i = 0, outlaw_form.LevelSkills.Count - 1, 1 do - local skill = outlaw_form.LevelSkills[i].Skill - if outlaw_form.LevelSkills[i].Level <= new_mob.Level and not COMMON.InArray(skill, skill_blacklist) then - --print("new skill candidate!: " .. skill) - table.insert(skill_candidates, skill) - end - end - - --learn as many skills as we can from the candidate list. - local learn_count = 0 - while learn_count < 4 do - if #skill_candidates > 0 then - local randval = _DATA.Save.Rand:Next(1, #skill_candidates) - local learned_skill = skill_candidates[randval] - mob_data.BaseSkills[learn_count] = RogueEssence.Dungeon.SlotSkill(learned_skill) - new_mob:LearnSkill(learned_skill, true) - table.remove(skill_candidates, randval) - else - mob_data.BaseSkills[learn_count] = RogueEssence.Dungeon.SlotSkill() - end - learn_count = learn_count + 1 - end - - local tactic = nil - if mission.Type == COMMON.MISSION_TYPE_OUTLAW_FLEE then - local speedMin = math.floor(SV.ExpectedLevel[mission.Zone] / 1.5) - local speedMax = math.floor(SV.ExpectedLevel[mission.Zone] * 1.5) - new_mob.SpeedBonus = math.min(_DATA.Save.Rand:Next(speedMin, speedMax), 50) - tactic = _DATA:GetAITactic("super_flee_stairs") - else - tactic = _DATA:GetAITactic("boss") - end - - if mission.Type == COMMON.MISSION_TYPE_OUTLAW_ITEM then - new_mob.EquippedItem = RogueEssence.Dungeon.InvItem(mission.Item) - end - - new_mob.MaxHPBonus = math.min(SV.ExpectedLevel[mission.Zone] * 2, max_boost); - new_mob.HP = new_mob.MaxHP; - new_mob.Unrecruitable = true - new_mob.Tactic = tactic - new_mob.CharLoc = spawn_loc - new_team.Players:Add(new_mob) - - tbl = LTBL(new_mob) - tbl.Mission = mission_num - - _ZONE.CurrentMap.MapTeams:Add(new_team) - new_mob:RefreshTraits() - _ZONE.CurrentMap:UpdateExploration(new_mob) - - local base_name = RogueEssence.Data.DataManager.Instance.DataIndices[RogueEssence.Data.DataManager.DataType.Monster]:Get(new_mob.BaseForm.Species).Name:ToLocal() - GAME:SetCharacterNickname(new_mob, base_name) - return new_mob -end - - -function SINGLE_CHAR_SCRIPT.OutlawFloor(owner, ownerChar, context, args) - local outlaw = context.User - local tbl = LTBL(outlaw) - if tbl ~= nil and tbl.Mission then - local mission_num = args.Mission - local mission = SV.TakenBoard[mission_num] - outlaw.Nickname = RogueEssence.Dungeon.CharData.GetFullFormName( RogueEssence.Dungeon.MonsterID(mission.Target, 0, "normal", COMMON.NumToGender(mission.TargetGender))) - SOUND:PlayBGM("Outlaw.ogg", true, 20) - UI:ResetSpeaker() - DUNGEON:CharTurnToChar(outlaw, GAME:GetPlayerPartyMember(0)) - COMMON.TeamTurnTo(outlaw) - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("DLG_MISSION_OUTLAW"):ToLocal())) - - if mission.Type == COMMON.MISSION_TYPE_OUTLAW_FLEE then - GAME:WaitFrames(20) - UI:SetSpeaker(outlaw) - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("DLG_MISSION_OUTLAW_FLEE"):ToLocal())) - local leaderDir = _DUNGEON.ActiveTeam.Leader.CharDir - outlaw.CharDir = leaderDir - elseif mission.Type == COMMON.MISSION_TYPE_OUTLAW_MONSTER_HOUSE then - GAME:WaitFrames(20) - UI:SetSpeaker(outlaw) - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("DLG_MISSION_OUTLAW_TRAP"):ToLocal())) - SOUND:FadeOutBGM(20) - GAME:WaitFrames(20) - - -- ===========Monster house spawning logic============ - local rect_area = RogueElements.Loc(1) - local rect_area2 = RogueElements.Loc(3) - - function checkBlock(loc) - local result = _ZONE.CurrentMap:TileBlocked(loc) - return result - end - - function checkDiagBlock(loc) - return true - end - - local goon_spawn_radius = 5 - - local origin = _DUNGEON.ActiveTeam.Leader.CharLoc - - local leftmost_x = math.maxinteger - local rightmost_x = math.mininteger - - local downmost_y = math.mininteger - local upmost_y = math.maxinteger - - - local topLeft = RogueElements.Loc(origin.X - goon_spawn_radius, origin.Y - goon_spawn_radius) - local bottomRight = RogueElements.Loc(origin.X + goon_spawn_radius, origin.Y + goon_spawn_radius) - - PrintInfo("Spawning monster house with top left "..topLeft.X..", "..topLeft.Y.." and bottom right "..bottomRight.X..", "..bottomRight.Y) - - local valid_tile_total = 0 - for x = math.max(topLeft.X, 0), math.min(bottomRight.X, _ZONE.CurrentMap.Width - 1), 1 do - for y = math.max(topLeft.Y, 0), math.min(bottomRight.Y, _ZONE.CurrentMap.Height - 1), 1 do - local testLoc = RogueElements.Loc(x,y) - --local is_choke_point = RogueElements.Grid.IsChokePoint(testLoc - rect_area, rect_area2, testLoc, checkBlock, checkDiagBlock) - local tile_block = _ZONE.CurrentMap:TileBlocked(testLoc) - local char_at = _ZONE.CurrentMap:GetCharAtLoc(testLoc) - - if tile_block == false and char_at == nil then - valid_tile_total = valid_tile_total + 1 - leftmost_x = math.min(testLoc.X, leftmost_x) - rightmost_x = math.max(testLoc.X, rightmost_x) - downmost_y = math.max(testLoc.Y, downmost_y) - upmost_y = math.min(testLoc.Y, upmost_y) - end - end - end - - local house_event = PMDC.Dungeon.MonsterHouseMapEvent(); - - local tl = RogueElements.Loc(leftmost_x - 1, upmost_y - 1) - local br = RogueElements.Loc(rightmost_x + 1, downmost_y + 1) - - local bounds = RogueElements.Rect.FromPoints(tl, br) - house_event.Bounds = bounds - - local min_goons = math.floor(valid_tile_total / 5) - local max_goons = math.floor(valid_tile_total / 4) - local total_goons = _DATA.Save.Rand:Next(min_goons, max_goons) - - local all_spawns = LUA_ENGINE:MakeGenericType( ListType, { MobSpawnType }, { }) - for i = 0, _ZONE.CurrentMap.TeamSpawns.Count - 1, 1 do - local possible_spawns = _ZONE.CurrentMap.TeamSpawns:GetSpawn(i):GetPossibleSpawns() - for j = 0, possible_spawns.Count - 1, 1 do - local spawn = possible_spawns:GetSpawn(j):Copy() - all_spawns:Add(spawn) - end - end - - for _ = 1, total_goons, 1 do - local randint = _DATA.Save.Rand:Next(0, all_spawns.Count) - local spawn = all_spawns[randint] - spawn.SpawnFeatures:Add(PMDC.LevelGen.MobSpawnLuaTable('{ Goon = '..mission_num..' }')) - spawn.SpawnFeatures:Add(PMDC.LevelGen.MobSpawnUnrecruitable()) - house_event.Mobs:Add(spawn) - end - local charaContext = RogueEssence.Dungeon.SingleCharContext(_DUNGEON.ActiveTeam.Leader) - TASK:WaitTask(house_event:Apply(owner, ownerChar, charaContext)) - GAME:WaitFrames(20) - else - --to prevent accidental button mashing making you waste your turn - GAME:WaitFrames(20) - end - - --Starts the player in team mode which they likely want to be in, this can help prevent desyncs as well - _DUNGEON:SetTeamMode(true) - end -end - - -function SINGLE_CHAR_SCRIPT.OnOutlawDeath(owner, ownerChar, context, args) - local mission_num = args.Mission - local tbl = LTBL(context.User) - if tbl.Mission then - SV.OutlawDefeated = true - local curr_mission = SV.TakenBoard[mission_num] - if curr_mission.Type == COMMON.MISSION_TYPE_OUTLAW_ITEM then - SOUND:PlayBGM(_ZONE.CurrentMap.Music, true) - end - end - - tbl.Mission = nil - tbl.Goon = nil - - local found_goon = COMMON.FindNpcWithTable(true, "Goon", mission_num) - if not found_goon then - SV.OutlawGoonsDefeated = true - end -end - -function SINGLE_CHAR_SCRIPT.OnMonsterHouseOutlawCheck(owner, ownerChar, context, args) - local mission_number = args.Mission - local curr_mission = SV.TakenBoard[mission_number] - local outlaw_name = _DATA:GetMonster(curr_mission.Target):GetColoredName() - - if curr_mission.Completion == COMMON.MISSION_INCOMPLETE then - local found_outlaw = COMMON.FindNpcWithTable(true, "Mission", mission_number) - local found_goon = COMMON.FindNpcWithTable(true, "Goon", mission_number) - --print("found outlaw = " .. tostring(found_outlaw) .. ", found_goon = " .. tostring(found_goon)) - if not SV.MonsterHouseMessageNotified then - if found_goon and not found_outlaw then - GAME:WaitFrames(20) - UI:ResetSpeaker() - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("DLG_MISSION_OUTLAW_BOSS_DEFEATED"):ToLocal(), outlaw_name)) - SV.MonsterHouseMessageNotified = true - end - if not found_goon and found_outlaw then - GAME:WaitFrames(40) - UI:SetSpeaker(found_outlaw) - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("DLG_MISSION_OUTLAW_MINIONS_DEFEATED"):ToLocal())) - SV.MonsterHouseMessageNotified = true - end - end - - if not (found_goon or found_outlaw) then - SOUND:PlayBGM(_ZONE.CurrentMap.Music, true) - SV.TemporaryFlags.MissionCompleted = true - curr_mission.Completion = 1 - GAME:WaitFrames(20) - UI:ResetSpeaker() - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("DLG_MISSION_OUTLAW_HOUSE_DEFEATED"):ToLocal(), outlaw_name)) - SV.TemporaryFlags.PriorMapSetting = _DUNGEON.ShowMap - _DUNGEON.ShowMap = _DUNGEON.MinimapState.None - COMMON.AskMissionWarpOut() - end - end -end - -function SINGLE_CHAR_SCRIPT.SpawnOutlaw(owner, ownerChar, context, args) - if context.User ~= nil then - return - end - local mission_num = args.Mission - local curr_mission = SV.TakenBoard[mission_num] - if curr_mission.Completion == COMMON.MISSION_INCOMPLETE then - local origin = _DATA.Save.ActiveTeam.Leader.CharLoc - local radius = 3 - SpawnOutlaw(origin, radius, mission_num) - end -end - -function SINGLE_CHAR_SCRIPT.OutlawCheck(owner, ownerChar, context, args) - local mission_num = args.Mission - local curr_mission = SV.TakenBoard[mission_num] - if curr_mission.Completion == COMMON.MISSION_INCOMPLETE then - if SV.OutlawDefeated then - local curr_mission = SV.TakenBoard[mission_num] - local outlaw_name = _DATA:GetMonster(curr_mission.Target):GetColoredName() - SOUND:PlayBGM(_ZONE.CurrentMap.Music, true) - SV.TemporaryFlags.MissionCompleted = true - curr_mission.Completion = 1 - GAME:WaitFrames(50) - UI:ResetSpeaker() - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("DLG_MISSION_OUTLAW_DEFEATED"):ToLocal(), outlaw_name)) - --Clear but remember minimap state - SV.TemporaryFlags.PriorMapSetting = _DUNGEON.ShowMap - _DUNGEON.ShowMap = _DUNGEON.MinimapState.None - COMMON.AskMissionWarpOut() - end - end -end - -function SINGLE_CHAR_SCRIPT.OutlawItemCheck(owner, ownerChar, context, args) - local mission_num = args.Mission - local curr_mission = SV.TakenBoard[mission_num] - if curr_mission.Completion == COMMON.MISSION_INCOMPLETE then - if SV.OutlawDefeated and SV.OutlawItemPickedUp then - SV.TemporaryFlags.MissionCompleted = true - curr_mission.Completion = 1 - local item_name = RogueEssence.Dungeon.InvItem(curr_mission.Item):GetDisplayName() - SOUND:PlayBGM(_ZONE.CurrentMap.Music, true) - GAME:WaitFrames(50) - UI:ResetSpeaker() - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("DLG_MISSION_OUTLAW_ITEM_RETRIEVED"):ToLocal(), item_name)) - --Clear but remember minimap state - SV.TemporaryFlags.PriorMapSetting = _DUNGEON.ShowMap - _DUNGEON.ShowMap = _DUNGEON.MinimapState.None - COMMON.AskMissionWarpOut() - end - end -end - -function SINGLE_CHAR_SCRIPT.OutlawFleeStairsCheck(owner, ownerChar, context, args) - local stairs_arr = { - "stairs_back_down", "stairs_back_up", "stairs_exit_down", - "stairs_exit_up", "stairs_go_up", "stairs_go_down" - } - - local mission_num = args.Mission - local curr_mission = SV.TakenBoard[mission_num] - local found_outlaw = COMMON.FindNpcWithTable(true, "Mission", mission_num) - - if found_outlaw then - local targetName = found_outlaw:GetDisplayName(true) - local map = _ZONE.CurrentMap; - local charLoc = found_outlaw.CharLoc - local tile = map:GetTile(charLoc) - local tile_effect_id = tile.Effect.ID - if tile and COMMON.TableContains(stairs_arr, tile_effect_id) then - GAME:WaitFrames(20) - _DUNGEON:RemoveChar(found_outlaw) - GAME:WaitFrames(20) - UI:ResetSpeaker() - UI:WaitShowDialogue(targetName .. " escaped...") - curr_mission.BackReference = COMMON.FLEE_BACKREFERENCE - SOUND:PlayBGM(_ZONE.CurrentMap.Music, true) - GAME:WaitFrames(20) - end - end -end - - - - -function SINGLE_CHAR_SCRIPT.MissionGuestCheck(owner, ownerChar, context, args) - - if not context.User.Dead then - return - end - - local tbl = LTBL(context.User) - - if tbl ~= nil and tbl.Escort ~= nil then - local targetName = _DATA:GetMonster(context.User.BaseForm.Species):GetColoredName() - UI:ResetSpeaker() - UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("MISSION_ESCORT_FAINTED"):ToLocal(), targetName)) - GAME:WaitFrames(40) - --Set max team size to 4 as the guest is no longer "taking" up a party slot - RogueEssence.Dungeon.ExplorerTeam.MAX_TEAM_SLOTS = 4 - COMMON.WarpOut() - GAME:WaitFrames(80) - TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Failed)) - end -end - diff --git a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/ground/base_camp_2/init.lua b/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/ground/base_camp_2/init.lua deleted file mode 100644 index ff724071fc..0000000000 --- a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/ground/base_camp_2/init.lua +++ /dev/null @@ -1,398 +0,0 @@ -require 'enable_mission_board.common' -require 'enable_mission_board.mission_gen' - - -local base_camp_2_bulletin = {} -local MapStrings = {} - -local base_init = CURMAPSCR.Init -function base_camp_2_bulletin.Init(map) - MapStrings = COMMON.AutoLoadLocalizedStrings() - - base_init(map) -end - -local base_enter = CURMAPSCR.Enter -function base_camp_2_bulletin.Enter(map) - DEBUG.EnableDbgCoro() --Enable debugging this coroutine - - if SV.MissionsEnabled == true then - GROUND:Unhide("Mission_Board") - end - - if SV.TemporaryFlags.MissionCompleted then - base_camp_2_bulletin.Hand_In_Missions() - end - - base_enter(map) -end - - -function base_camp_2_bulletin.Mission_Board_Action(obj, activator) - DEBUG.EnableDbgCoro() --Enable debugging this coroutine - - local dungeons_needed = 3 --Number of dungeons needed to unlock the Mission Board - - local hero = CH('PLAYER') - GROUND:CharSetAnim(hero, 'None', true) - - if SV.MissionPrereq.NumDungeonsCompleted >= dungeons_needed then - local menu = BoardSelectionMenu:new(COMMON.MISSION_BOARD_MISSION) - UI:SetCustomMenu(menu.menu) - UI:WaitForChoice() - else - UI:ResetSpeaker() - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Mission_Board_Locked'])) - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Mission_Board_Locked_2'], dungeons_needed - SV.MissionPrereq.NumDungeonsCompleted)) - end - - GROUND:CharEndAnim(hero) -end - -function base_camp_2_bulletin.Hand_In_Missions() - for i = 8, 1, -1 do - if SV.TakenBoard[i].Client ~= "" and SV.TakenBoard[i].Completion == MISSION_GEN.COMPLETE then - if SV.TakenBoard[i].Type == COMMON.MISSION_TYPE_OUTLAW or SV.TakenBoard[i].Type == COMMON.MISSION_TYPE_OUTLAW_ITEM - or SV.TakenBoard[i].Type == COMMON.MISSION_TYPE_OUTLAW_FLEE or SV.TakenBoard[i].Type == COMMON.MISSION_TYPE_OUTLAW_MONSTER_HOUSE then - base_camp_2_bulletin.Outlaw_Job_Clear(SV.TakenBoard[i]) - else - base_camp_2_bulletin.Mission_Job_Clear(SV.TakenBoard[i]) - end - --short pause between fadeins - GAME:WaitFrames(20) - - --clear the job - SV.TakenBoard[i] = { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - } - end - end - --reset this flag - SV.TemporaryFlags.MissionCompleted = false - - GAME:MoveCamera(0, 0, 1, true) - SOUND:PlayBGM(SV.base_town.Song, true) - MISSION_GEN.RegenerateJobs(result) - - --sort taken jobs now that we're removed completed ones - MISSION_GEN.SortTaken() -end - ---takes a job and plays an outlaw reward scene depending on the job. -function base_camp_2_bulletin.Outlaw_Job_Clear(job) - local hero = CH('PLAYER') - GAME:CutsceneMode(true) - UI:ResetSpeaker() - - GROUND:TeleportTo(hero, 100, 600, Direction.Up) - GAME:MoveCamera(90, 565, 1, false) - - SOUND:StopBGM() - - local money = false - if job.Reward == 'money' then money = true end - - --client is magna, he and the magnemite take the outlaw away - if job.Client == 'magna' then - local magnemite_left, magnemite_right, magna = - COMMON.MakeCharactersFromList({ - --{'Sandile', 100, 555, Direction.Down}, - {'Magnemite_Left', 80, 555, Direction.Down}, - {'Magnemite_Right', 120, 555, Direction.Down}, - {'Magnezone', 100, 575, Direction.Down} - }) - - local outlaw_gender = job.TargetGender - outlaw_gender = COMMON.NumToGender(outlaw_gender) - - local outlaw_monster = RogueEssence.Dungeon.MonsterID(job.Target, 0, "normal", outlaw_gender) - - local outlaw = RogueEssence.Ground.GroundChar(outlaw_monster, RogueElements.Loc(100, 555), Direction.Down, outlaw_monster.Species, 'Outlaw') - outlaw:ReloadEvents() - GAME:GetCurrentGround():AddTempChar(outlaw) - - GAME:FadeIn(40) - SOUND:PlayBGM("Job Clear!.ogg", true) - UI:SetSpeaker(magna) - - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Outlaw_Capture_Cutscene_001'], _DATA:GetMonster(outlaw.CurrentForm.Species):GetColoredName())) - - GAME:WaitFrames(20) - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Outlaw_Capture_Cutscene_002'])) - GAME:WaitFrames(20) - - --reward the item - if money then - COMMON.RewardItem(MISSION_GEN.DIFF_TO_MONEY[job.Difficulty], true) - else - COMMON.RewardItem(job.Reward) - end - - - if job.BonusReward ~= '' then - UI:SetSpeaker(magna) - GAME:WaitFrames(20) - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Outlaw_Capture_Cutscene_003'])) - GAME:WaitFrames(20) - COMMON.RewardItem(job.BonusReward) - end - - GAME:WaitFrames(20) - base_camp_2_bulletin.RewardEXP(job) - - GAME:WaitFrames(20) - - UI:SetSpeaker(magna) - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Outlaw_Capture_Cutscene_004'])) - - GROUND:CharSetEmote(magnemite_left, "happy", 0) - GROUND:CharSetEmote(magnemite_right, "happy", 0) - local coro1 = TASK:BranchCoroutine(function() GROUND:CharSetAction(magna, RogueEssence.Ground.PoseGroundAction(magna.Position, magna.Direction, RogueEssence.Content.GraphicsManager.GetAnimIndex("Pose"))) end) - local coro2 = TASK:BranchCoroutine(function() GROUND:CharSetAction(magnemite_left, RogueEssence.Ground.PoseGroundAction(magnemite_left.Position, magnemite_left.Direction, RogueEssence.Content.GraphicsManager.GetAnimIndex("Pose"))) end) - local coro3 = TASK:BranchCoroutine(function() GROUND:CharSetAction(magnemite_right, RogueEssence.Ground.PoseGroundAction(magnemite_right.Position, magnemite_right.Direction, RogueEssence.Content.GraphicsManager.GetAnimIndex("Pose"))) end) - local coro4 = TASK:BranchCoroutine(function() GAME:WaitFrames(12) SOUND:PlayBattleSE('DUN_Magnet_Bomb') end) - - TASK:JoinCoroutines({coro1, coro2, coro3, coro4}) - GAME:WaitFrames(60) - - GROUND:CharEndAnim(magna) - GROUND:CharEndAnim(magnemite_left) - GROUND:CharEndAnim(magnemite_right) - GROUND:CharSetEmote(magnemite_left, "", 0) - GROUND:CharSetEmote(magnemite_right, "", 0) - GAME:WaitFrames(20) - - --fade out and clean up any temporary characters - SOUND:FadeOutBGM(40) - GAME:FadeOut(false, 40) - GAME:GetCurrentGround():RemoveTempChar(magna) - GAME:GetCurrentGround():RemoveTempChar(magnemite_left) - GAME:GetCurrentGround():RemoveTempChar(magnemite_right) - GAME:GetCurrentGround():RemoveTempChar(outlaw) - - - else--client is some random mon - local client_gender = job.ClientGender - client_gender = COMMON.NumToGender(client_gender) - client_gender = client_gender - - local client_monster = RogueEssence.Dungeon.MonsterID(job.Client, 0, "normal", client_gender) - - local client = RogueEssence.Ground.GroundChar(client_monster, RogueElements.Loc(100, 575), Direction.Down, job.Client:gsub("^%l", string.upper), client_monster.Species) - client:ReloadEvents() - GAME:GetCurrentGround():AddTempChar(client) - - GAME:FadeIn(40) - SOUND:PlayBGM("Job Clear!.ogg", true) - UI:SetSpeaker(client) - - - local item = RogueEssence.Dungeon.InvItem(job.Item) - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Outlaw_Retrieve_Cutscene'], item:GetDisplayName())) - GAME:WaitFrames(20) - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Mission_Generic_Reward'])) - GAME:WaitFrames(20) - - --reward the item - if money then - COMMON.RewardItem(MISSION_GEN.DIFF_TO_MONEY[job.Difficulty], true) - else - COMMON.RewardItem(job.Reward) - end - - if job.BonusReward ~= '' then - UI:SetSpeaker(client) - GAME:WaitFrames(20) - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Mission_Generic_Reward_2'])) - GAME:WaitFrames(20) - COMMON.RewardItem(job.BonusReward) - end - - GAME:WaitFrames(20) - base_camp_2_bulletin.RewardEXP(job) - GAME:WaitFrames(20) - - --fade out and clean up any temporary characters - SOUND:FadeOutBGM(40) - GAME:FadeOut(false, 40) - GAME:GetCurrentGround():RemoveTempChar(client) - end - - GAME:CutsceneMode(false) -end - ---takes a job and plays an regular mission reward scene depending on the job. -function base_camp_2_bulletin.Mission_Job_Clear(job) - local hero = CH('PLAYER') - GAME:CutsceneMode(true) - UI:ResetSpeaker() - - GROUND:TeleportTo(hero, 100, 600, Direction.Up) - GAME:MoveCamera(90, 565, 1, false) - SOUND:StopBGM() - - local money = false - if job.Reward == 'money' then money = true end - - --client is target. Check on escort is needed in case the escort is to the same species. - if job.Client == job.Target and job.Type ~= COMMON.MISSION_TYPE_ESCORT then - local client_gender = job.ClientGender - client_gender = COMMON.NumToGender(client_gender) - - local client_monster = RogueEssence.Dungeon.MonsterID(job.Client, 0, "normal", client_gender) - local client = RogueEssence.Ground.GroundChar(client_monster, RogueElements.Loc(100, 575), Direction.Down, job.Client:gsub("^%l", string.upper), client_monster.Species) - client:ReloadEvents() - GAME:GetCurrentGround():AddTempChar(client) - - GAME:FadeIn(40) - SOUND:PlayBGM("Job Clear!.ogg", true) - UI:SetSpeaker(client) - - --different thank you message depending on the job type - if job.Type == COMMON.MISSION_TYPE_RESCUE then - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Mission_Response_Rescue'])) - elseif job.Type == COMMON.MISSION_TYPE_EXPLORATION then - local zone = _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Zone]:Get(job.Zone) - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Mission_Response_Exploration'], zone:GetColoredName())) - elseif job.Type == COMMON.MISSION_TYPE_LOST_ITEM then - local item = RogueEssence.Dungeon.InvItem(job.Item) - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Mission_Response_Lost_Item'], item:GetDisplayName())) - else--delivery - local item = RogueEssence.Dungeon.InvItem(job.Item) - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Mission_Response_Delivery_Item'], item:GetDisplayName())) - end - - GAME:WaitFrames(20) - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Mission_Generic_Reward'])) - GAME:WaitFrames(20) - - --reward the item - if money then - COMMON.RewardItem(MISSION_GEN.DIFF_TO_MONEY[job.Difficulty], true) - else - COMMON.RewardItem(job.Reward) - end - - if job.BonusReward ~= '' then - UI:SetSpeaker(client) - GAME:WaitFrames(20) - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Mission_Generic_Reward_2'])) - GAME:WaitFrames(20) - COMMON.RewardItem(job.BonusReward) - end - - GAME:WaitFrames(20) - base_camp_2_bulletin.RewardEXP(job) - GAME:WaitFrames(20) - - - --fade out and clean up any temporary characters - SOUND:FadeOutBGM(40) - GAME:FadeOut(false, 40) - GAME:GetCurrentGround():RemoveTempChar(client) - - - - else--client not the target - local client_gender = job.ClientGender - client_gender = COMMON.NumToGender(client_gender) - - - local client_monster = RogueEssence.Dungeon.MonsterID(job.Client, 0, "normal", client_gender) - - local client = RogueEssence.Ground.GroundChar(client_monster, RogueElements.Loc(80, 575), Direction.Down, job.Client:gsub("^%l", string.upper), client_monster.Species) - client:ReloadEvents() - GAME:GetCurrentGround():AddTempChar(client) - - local target_gender = job.TargetGender - target_gender = COMMON.NumToGender(target_gender) - - local target_monster = RogueEssence.Dungeon.MonsterID(job.Target, 0, "normal", target_gender) - target_monster.Gender = _DATA:GetMonster(job.Target).Forms[0]:RollGender(_ZONE.CurrentGround.Rand) - - local target = RogueEssence.Ground.GroundChar(target_monster, RogueElements.Loc(120, 575), Direction.Down, job.Target:gsub("^%l", string.upper), target_monster.Species) - target:ReloadEvents() - GAME:GetCurrentGround():AddTempChar(target) - - - GAME:FadeIn(40) - SOUND:PlayBGM("Job Clear!.ogg", true) - UI:SetSpeaker(client) - - if job.Type == COMMON.MISSION_TYPE_ESCORT then - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Mission_Response_Escort'])) - else - if job.Special == MISSION_GEN.SPECIAL_CLIENT_LOVER then - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Mission_Response_Lover'])) - elseif job.Special == MISSION_GEN.SPECIAL_CLIENT_RIVAL then - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Mission_Response_Rival'])) - elseif job.Special == MISSION_GEN.SPECIAL_CLIENT_CHILD then - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Mission_Response_Child'])) - else - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Mission_Response_Rescue_Friend'])) - end - end - GAME:WaitFrames(20) - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Mission_Generic_Reward'])) - GAME:WaitFrames(20) - - --reward the item - if money then - COMMON.RewardItem(MISSION_GEN.DIFF_TO_MONEY[job.Difficulty], true) - else - COMMON.RewardItem(job.Reward) - end - - if job.BonusReward ~= '' then - UI:SetSpeaker(client) - GAME:WaitFrames(20) - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Mission_Generic_Reward_2'])) - GAME:WaitFrames(20) - COMMON.RewardItem(job.BonusReward) - end - - GAME:WaitFrames(20) - base_camp_2_bulletin.RewardEXP(job) - GAME:WaitFrames(20) - - - --fade out and clean up any temporary characters - SOUND:FadeOutBGM(40) - GAME:FadeOut(false, 40) - GAME:GetCurrentGround():RemoveTempChar(client) - GAME:GetCurrentGround():RemoveTempChar(target) - end - GAME:CutsceneMode(false) -end - -function base_camp_2_bulletin.RewardEXP(job) - --Reward EXP for your party - local exp_reward = MISSION_GEN.GetJobExpReward(job.Difficulty) - local exp_reward_string = "[color=#00FFFF]"..exp_reward.."[color]" - UI:ResetSpeaker() - UI:WaitShowDialogue(STRINGS:Format(MapStrings['Mission_Handout_EXP'], exp_reward_string)) - PrintInfo("Rewarding EXP for job with difficulty "..job.Difficulty.." and reward "..exp_reward_string) - local player_count = _DATA.Save.ActiveTeam.Players.Count - for player_idx = 0, player_count-1, 1 do - TASK:WaitTask(GROUND:_HandoutEXP(_DATA.Save.ActiveTeam.Players[player_idx], exp_reward)) - end -end - -return base_camp_2_bulletin \ No newline at end of file diff --git a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/menu/MemberReturnMenu.lua b/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/menu/MemberReturnMenu.lua deleted file mode 100644 index d4b93a2ca7..0000000000 --- a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/menu/MemberReturnMenu.lua +++ /dev/null @@ -1,75 +0,0 @@ -function CreateMemberReturnMenu() - -- equivalent to defining a class - local MemberReturnMenu = Class('MemberReturnMenu') - - function MemberReturnMenu:initialize() - assert(self, "MemberReturnMenu:initialize(): Error, self is nil!") - local player_count = GAME:GetPlayerPartyCount() - - self.members = {} - self.slots = {} - self.spacer = 12 - self.total_items = 0 - for i = 0, player_count - 1, 1 do - local player = GAME:GetPlayerPartyMember(i) - if not player.IsPartner then - self.members[self.total_items] = player - self.slots[self.total_items] = i - self.total_items = self.total_items + 1 - end - end - - local height = 16 + 8 + self.total_items * self.spacer + 8 + 12 - - self.menu = RogueEssence.Menu.ScriptableMenu(24, 24, 164, height, function(input) self:Update(input) end) - self.cursor = RogueEssence.Menu.MenuCursor(self.menu) - self.menu.Elements:Add(self.cursor) - - - self.menu.Elements:Add(RogueEssence.Menu.MenuText(GAME:GetTeamName(), RogueElements.Loc(16, 8))) - self.menu.Elements:Add(RogueEssence.Menu.MenuDivider(RogueElements.Loc(12, 8 + 12), self.menu.Bounds.Width - 12 * 2)); - - - -- Offset from menu divider and team name - local offset = 16 + 8 - - for i = 0, player_count - 1, 1 do - local player = GAME:GetPlayerPartyMember(i) - if not player.IsPartner then - self.menu.Elements:Add(RogueEssence.Menu.MenuText(player:GetDisplayName(true), RogueElements.Loc(16 + 8, offset))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(STRINGS:FormatKey("MENU_TEAM_LEVEL_SHORT"), RogueElements.Loc(16 + 8 + 100, offset), RogueElements.DirH.Right)) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(tostring(player.Level), RogueElements.Loc(16 + 8 + 100 + 18, offset), RogueElements.DirH.Right)) - offset = offset + self.spacer - end - end - - self.menu.Elements:Add(RogueEssence.Menu.MenuDivider(RogueElements.Loc(12, offset), self.menu.Bounds.Width - 12 * 2)); - self.current_item = 0 - --self.cursor.Loc = RogueElements.Loc(offset +) - self.cursor.Loc = RogueElements.Loc(16, 24 + self.spacer * self.current_item) - end - - function MemberReturnMenu:Update(input) - assert(self, "BaseState:Begin(): Error, self is nil!") - -- default does nothing - if input:JustPressed(RogueEssence.FrameInput.InputType.Confirm) then - _GAME:SE("Menu/Confirm") - _MENU:RemoveMenu() - end - moved = false - if RogueEssence.Menu.InteractableMenu.IsInputting(input, LUA_ENGINE:MakeLuaArray(Dir8, { Dir8.Down, Dir8.DownLeft, Dir8.DownRight })) then - moved = true - self.current_item = (self.current_item + 1) % self.total_items - elseif RogueEssence.Menu.InteractableMenu.IsInputting(input, LUA_ENGINE:MakeLuaArray(Dir8, { Dir8.Up, Dir8.UpLeft, Dir8.UpRight })) then - moved = true - self.current_item = (self.current_item + self.total_items - 1) % self.total_items - end - if moved then - _GAME:SE("Menu/Select") - self.cursor:ResetTimeOffset() - self.cursor.Loc = RogueElements.Loc(16, 24 + self.spacer * self.current_item) - end - end - - return MemberReturnMenu -end \ No newline at end of file diff --git a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/mission_gen.lua b/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/mission_gen.lua deleted file mode 100644 index 18e54a3040..0000000000 --- a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/mission_gen.lua +++ /dev/null @@ -1,3921 +0,0 @@ -require 'enable_mission_board.common' - ---Halcyon Custom work ported to PMDO Vanilla: ---Code in this folder is used to generate, display, and handle randomized missions ---and all that goes with that (rewards, rankups, etc) - - ---A job is saved as a list of variables, where each variable represents an attribute of the job, such as reward, client, destination floor, etc. ---There are 3 sets of lua lists then. One for the missions taken, one for the missions on the job board, and one for the missions on the outlaw board. ---Each of those lists can only have up to 8 jobs. - ---List of mission attributes: ---Client (the pokemon in need of escort, rescue, or the person asking for an outlaw capture). Given as species string ---Target (Pokemon in need of rescue, or the one to escort to, or the mon to be arrested). Given as species string ---Escort Species - should be given as a blank if this is not an escort mission. ---Zone (dungeon) ---Segment (part of the dungeon) - this is typically the default segment ---Floor ---Reward - given as item name's string. If money, should be given as "Money" and the amount will be based off the difficulty. ---Mission Type - outlaw, escort, or rescue ---Completion status - Incomplete or Complete. When a reward is handed out at the end of the day, any missions that are completed should be removed from the taken board. ---Taken - Was the mission on the board taken? This is also used to suspend missions that were taken off the board ---Difficulty - Letter rank that are hardcoded to represent certain number amounts ---Flavor - Flavor text for the mission, should be a string in strings.resx that can potentially be filled in by blanks. - ---Hardcoded number values. Adjust those sorts of things here. ---Difficulty's point ranks -MISSION_GEN = {} - ---MISSION_GEN.DUNGEON_LIST = {'tropical_path', 'faultline_ridge', 'tiny_tunnel', 'guildmaster_trail', --- 'lava_floe_island', 'castaway_cave', 'eon_island', 'lost_seas', 'inscribed_cave', 'prism_isles', --- 'faded_trail', 'bramble_woods', 'trickster_woods', 'overgrown_wilds', 'moonlit_courtyard', 'ambush_forest', 'energy_garden', 'sickly_hollow', 'secret_garden', --- 'flyaway_cliffs', 'fertile_valley', 'wayward_wetlands', 'deserted_fortress', 'bravery_road', 'geode_underpass', 'the_sky', --- 'copper_quarry', 'forsaken_desert', 'relic_tower', 'sleeping_caldera', 'royal_halls', 'starfall_heights', 'wisdom_road', 'sacred_tower', --- 'thunderstruck_pass', 'veiled_ridge', 'snowbound_path', 'treacherous_mountain', 'hope_road', 'cave_of_whispers', --- 'champions_road', 'barren_tundra', 'cave_of_solace', 'labyrinth_of_the_lost'} - ---UPDATE HERE when a new dungeon gets added- note, you MUST always have a [0] for the default difficulty ---REMOVE FROM HERE if you do not want a dungeon to appear in mission gen anymore -MISSION_GEN.DUNGEON_LIST = {} -MISSION_GEN.DUNGEON_LIST["tropical_path"] = { [0] = "F" } --Tropical Path -MISSION_GEN.DUNGEON_LIST["faultline_ridge"] = { [0] = "D" } --Faultline Ridge -MISSION_GEN.DUNGEON_LIST["guildmaster_trail"] = { [0] = "STAR_2" } --Guildmaster Trail -MISSION_GEN.DUNGEON_LIST["lava_floe_island"] = { [0] = "C", [1] = "STAR_1" } --Lava Floe Island, Abyssal Island -MISSION_GEN.DUNGEON_LIST["castaway_cave"] = { [0] = "B" } --Castaway Cave -MISSION_GEN.DUNGEON_LIST["faded_trail"] = { [0] = "E", [1] = "D" } --Faded Trail, Hidden Trail -MISSION_GEN.DUNGEON_LIST["bramble_woods"] = { [0] = "E", [1] = "D" } --Bramble Woods, Bramble Thicket -MISSION_GEN.DUNGEON_LIST["trickster_woods"] = { [0] = "C", [1] = "B" } --Trickster Woods, Trickster Maze -MISSION_GEN.DUNGEON_LIST["overgrown_wilds"] = { [0] = "C", [1] = "B" } --Overgrown Wilds, Exotic Wilds -MISSION_GEN.DUNGEON_LIST["moonlit_courtyard"] = { [0] = "C", [1] = "A" } --Moonlit Courtyard, Moonlit Temple -MISSION_GEN.DUNGEON_LIST["ambush_forest"] = { [0] = "B" } --Ambush Forest -MISSION_GEN.DUNGEON_LIST["tiny_tunnel"] = { [0] = "B" } --Tiny Tunnel -MISSION_GEN.DUNGEON_LIST["sickly_hollow"] = { [0] = "S" } --Sickly Hollow -MISSION_GEN.DUNGEON_LIST["secret_garden"] = { [0] = "STAR_3" } --Secret Garden -MISSION_GEN.DUNGEON_LIST["flyaway_cliffs"] = { [0] = "C" } --Flyaway Cliffs -MISSION_GEN.DUNGEON_LIST["fertile_valley"] = { [0] = "D", [1] = "C" } --Fertile Valley, Muddy Valley -MISSION_GEN.DUNGEON_LIST["geode_crevice"] = { [0] = "A" } --Geode Crevice -MISSION_GEN.DUNGEON_LIST["copper_quarry"] = { [0] = "C", [1] = "B" } --Copper Quarry, Lodestone Quarry -MISSION_GEN.DUNGEON_LIST["depleted_basin"] = { [0] = "C" } --Depleted Basin -MISSION_GEN.DUNGEON_LIST["forsaken_desert"] = { [0] = "A" } --Forsaken Desert -MISSION_GEN.DUNGEON_LIST["relic_tower"] = { [0] = "S" } --Relic Tower -MISSION_GEN.DUNGEON_LIST["sleeping_caldera"] = { [0] = "B" } --Sleeping Caldera -MISSION_GEN.DUNGEON_LIST["thunderstruck_pass"] = { [0] = "B" } --Thunderstruck Pass -MISSION_GEN.DUNGEON_LIST["veiled_ridge"] = { [0] = "B", [1] = "A" } --Veiled Ridge, Illusion Ridge -MISSION_GEN.DUNGEON_LIST["snowbound_path"] = { [0] = "A", [1] = "S" } --Snowbound Path, Glacial Path -MISSION_GEN.DUNGEON_LIST["treacherous_mountain"] = { [0] = "A" } --Treacherous Mountain -MISSION_GEN.DUNGEON_LIST["champions_road"] = { [0] = "S" } --Champion's Road (Clouded Road is not eligible for missions) - - ---This is mainly used for EXP rewards in base PMDO -MISSION_GEN.DIFFICULTY = {} -MISSION_GEN.DIFFICULTY[""] = 0 -MISSION_GEN.DIFFICULTY["F"] = 100 --below lv 10 dungeons generally -MISSION_GEN.DIFFICULTY["E"] = 200 --lv 10-14 dungeons -MISSION_GEN.DIFFICULTY["D"] = 400 --lv 15-20 dungeons -MISSION_GEN.DIFFICULTY["C"] = 800 --lv 21-29 dungeons -MISSION_GEN.DIFFICULTY["B"] = 1250 --lv 30-39 dungeons -MISSION_GEN.DIFFICULTY["A"] = 2500 --lv 40-49 dungeons -MISSION_GEN.DIFFICULTY["S"] = 5000 --lv 50-59 dungeons -MISSION_GEN.DIFFICULTY["STAR_1"] = 10000 --lv 60+ dungeons -MISSION_GEN.DIFFICULTY["STAR_2"] = 20000 --reserved for challenge dungeons -MISSION_GEN.DIFFICULTY["STAR_3"] = 30000 --reserved for challenge dungeons -MISSION_GEN.DIFFICULTY["STAR_4"] = 40000 --reserved for challenge dungeons -MISSION_GEN.DIFFICULTY["STAR_5"] = 50000 --reserved for challenge dungeons -MISSION_GEN.DIFFICULTY["STAR_6"] = 60000 --reserved for challenge dungeons -MISSION_GEN.DIFFICULTY["STAR_7"] = 70000 --reserved for challenge dungeons -MISSION_GEN.DIFFICULTY["STAR_8"] = 80000 --reserved for challenge dungeons -MISSION_GEN.DIFFICULTY["STAR_9"] = 90000 --reserved for challenge dungeons - ---order of difficulties. -MISSION_GEN.DIFF_TO_ORDER = {} -MISSION_GEN.DIFF_TO_ORDER[""] = 1 -MISSION_GEN.DIFF_TO_ORDER["F"] = 2 -MISSION_GEN.DIFF_TO_ORDER["E"] = 3 -MISSION_GEN.DIFF_TO_ORDER["D"] = 4 -MISSION_GEN.DIFF_TO_ORDER["C"] = 5 -MISSION_GEN.DIFF_TO_ORDER["B"] = 6 -MISSION_GEN.DIFF_TO_ORDER["A"] = 7 -MISSION_GEN.DIFF_TO_ORDER["S"] = 8 -MISSION_GEN.DIFF_TO_ORDER["STAR_1"] = 9 -MISSION_GEN.DIFF_TO_ORDER["STAR_2"] = 10 -MISSION_GEN.DIFF_TO_ORDER["STAR_3"] = 11 -MISSION_GEN.DIFF_TO_ORDER["STAR_4"] = 12 -MISSION_GEN.DIFF_TO_ORDER["STAR_5"] = 13 -MISSION_GEN.DIFF_TO_ORDER["STAR_6"] = 14 -MISSION_GEN.DIFF_TO_ORDER["STAR_7"] = 15 -MISSION_GEN.DIFF_TO_ORDER["STAR_8"] = 16 -MISSION_GEN.DIFF_TO_ORDER["STAR_9"] = 17 - ---use this to get back from above. -MISSION_GEN.ORDER_TO_DIFF = {"", "F", "E", "D", "C", "B", "A", "S", "STAR_1","STAR_2","STAR_3","STAR_4","STAR_5","STAR_6","STAR_7","STAR_8","STAR_9"} - ---mapping of difficulty to reward amounts for money -MISSION_GEN.DIFF_TO_MONEY = {} -MISSION_GEN.DIFF_TO_MONEY[""] = 0 -MISSION_GEN.DIFF_TO_MONEY["F"] = 100 -MISSION_GEN.DIFF_TO_MONEY["E"] = 200 -MISSION_GEN.DIFF_TO_MONEY["D"] = 400 -MISSION_GEN.DIFF_TO_MONEY["C"] = 600 -MISSION_GEN.DIFF_TO_MONEY["B"] = 800 -MISSION_GEN.DIFF_TO_MONEY["A"] = 1500 -MISSION_GEN.DIFF_TO_MONEY["S"] = 3000 -MISSION_GEN.DIFF_TO_MONEY["STAR_1"] = 6000 -MISSION_GEN.DIFF_TO_MONEY["STAR_2"] = 10000 -MISSION_GEN.DIFF_TO_MONEY["STAR_3"] = 15000 -MISSION_GEN.DIFF_TO_MONEY["STAR_4"] = 20000 -MISSION_GEN.DIFF_TO_MONEY["STAR_5"] = 25000 -MISSION_GEN.DIFF_TO_MONEY["STAR_6"] = 30000 -MISSION_GEN.DIFF_TO_MONEY["STAR_7"] = 35000 -MISSION_GEN.DIFF_TO_MONEY["STAR_8"] = 40000 -MISSION_GEN.DIFF_TO_MONEY["STAR_9"] = 45000 - ---color coding for mission difficulty letters -MISSION_GEN.DIFF_TO_COLOR = {} -MISSION_GEN.DIFF_TO_COLOR[""] = "[color=#000000]" -MISSION_GEN.DIFF_TO_COLOR["F"] = "[color=#A1A1A1]" -MISSION_GEN.DIFF_TO_COLOR["E"] = "[color=#F8F8F8]" -MISSION_GEN.DIFF_TO_COLOR["D"] = "[color=#F8C8C8]" -MISSION_GEN.DIFF_TO_COLOR["C"] = "[color=#40F840]" -MISSION_GEN.DIFF_TO_COLOR["B"] = "[color=#F8C060]" -MISSION_GEN.DIFF_TO_COLOR["A"] = "[color=#00F8F8]" -MISSION_GEN.DIFF_TO_COLOR["S"] = "[color=#F80000]" -MISSION_GEN.DIFF_TO_COLOR["STAR_1"] = "[color=#F8F800]" -MISSION_GEN.DIFF_TO_COLOR["STAR_2"] = "[color=#F8F800]" -MISSION_GEN.DIFF_TO_COLOR["STAR_3"] = "[color=#F8F800]" -MISSION_GEN.DIFF_TO_COLOR["STAR_4"] = "[color=#F8F800]" -MISSION_GEN.DIFF_TO_COLOR["STAR_5"] = "[color=#F8F800]" -MISSION_GEN.DIFF_TO_COLOR["STAR_6"] = "[color=#F8F800]" -MISSION_GEN.DIFF_TO_COLOR["STAR_7"] = "[color=#F8F800]" -MISSION_GEN.DIFF_TO_COLOR["STAR_8"] = "[color=#F8F800]" -MISSION_GEN.DIFF_TO_COLOR["STAR_9"] = "[color=#F8F800]" - - - -MISSION_GEN.COMPLETE = 1 -MISSION_GEN.INCOMPLETE = 0 - -SV.ExpectedLevel = {} - - -MISSION_GEN.TITLES = { - RESCUE_SELF = { - "MISSION_TITLE_RESCUE_SELF_001", - "MISSION_TITLE_RESCUE_SELF_002", - "MISSION_TITLE_RESCUE_SELF_003", - "MISSION_TITLE_RESCUE_SELF_004", - "MISSION_TITLE_RESCUE_SELF_005", - "MISSION_TITLE_RESCUE_SELF_006", - "MISSION_TITLE_RESCUE_SELF_007", - "MISSION_TITLE_RESCUE_SELF_008", - "MISSION_TITLE_RESCUE_SELF_009", - "MISSION_TITLE_RESCUE_SELF_010" - }, - RESCUE_FRIEND = { - "MISSION_TITLE_RESCUE_FRIEND_001", - "MISSION_TITLE_RESCUE_FRIEND_002", - "MISSION_TITLE_RESCUE_FRIEND_003", - "MISSION_TITLE_RESCUE_FRIEND_004", - "MISSION_TITLE_RESCUE_FRIEND_005", - "MISSION_TITLE_RESCUE_FRIEND_006", - "MISSION_TITLE_RESCUE_FRIEND_007", - "MISSION_TITLE_RESCUE_FRIEND_008", - "MISSION_TITLE_RESCUE_FRIEND_009", - "MISSION_TITLE_RESCUE_FRIEND_010" - }, - ESCORT = { - "MISSION_TITLE_ESCORT_001", - "MISSION_TITLE_ESCORT_002", - "MISSION_TITLE_ESCORT_003", - "MISSION_TITLE_ESCORT_004", - "MISSION_TITLE_ESCORT_005" - }, - EXPLORATION = { - "MISSION_TITLE_EXPLORATION_001", - "MISSION_TITLE_EXPLORATION_002", - "MISSION_TITLE_EXPLORATION_003", - "MISSION_TITLE_EXPLORATION_004", - "MISSION_TITLE_EXPLORATION_005" - }, - DELIVERY = { - "MISSION_TITLE_DELIVERY_001", - "MISSION_TITLE_DELIVERY_002", - "MISSION_TITLE_DELIVERY_003", - "MISSION_TITLE_DELIVERY_004", - "MISSION_TITLE_DELIVERY_005" - }, - LOST_ITEM = { - "MISSION_TITLE_LOST_ITEM_001", - "MISSION_TITLE_LOST_ITEM_002", - "MISSION_TITLE_LOST_ITEM_003", - "MISSION_TITLE_LOST_ITEM_004", - "MISSION_TITLE_LOST_ITEM_005" - }, - OUTLAW = { - "MISSION_TITLE_OUTLAW_001", - "MISSION_TITLE_OUTLAW_002", - "MISSION_TITLE_OUTLAW_003", - "MISSION_TITLE_OUTLAW_004", - "MISSION_TITLE_OUTLAW_005", - "MISSION_TITLE_OUTLAW_006", - "MISSION_TITLE_OUTLAW_007", - "MISSION_TITLE_OUTLAW_008", - "MISSION_TITLE_OUTLAW_009", - "MISSION_TITLE_OUTLAW_010", - }, - OUTLAW_ITEM = { - "MISSION_TITLE_OUTLAW_ITEM_001", - "MISSION_TITLE_OUTLAW_ITEM_002", - "MISSION_TITLE_OUTLAW_ITEM_003", - "MISSION_TITLE_OUTLAW_ITEM_004", - "MISSION_TITLE_OUTLAW_ITEM_005" - }, - OUTLAW_MONSTER_HOUSE = { - "MISSION_TITLE_OUTLAW_MONSTER_HOUSE_001", - "MISSION_TITLE_OUTLAW_MONSTER_HOUSE_002", - "MISSION_TITLE_OUTLAW_MONSTER_HOUSE_003", - "MISSION_TITLE_OUTLAW_MONSTER_HOUSE_004", - "MISSION_TITLE_OUTLAW_MONSTER_HOUSE_005" - }, - OUTLAW_FLEE = { - "MISSION_TITLE_OUTLAW_FLEE_001", - "MISSION_TITLE_OUTLAW_FLEE_002", - "MISSION_TITLE_OUTLAW_FLEE_003", - "MISSION_TITLE_OUTLAW_FLEE_004", - "MISSION_TITLE_OUTLAW_FLEE_005" - }, - - --For special client/targets - RIVAL = { - "MISSION_TITLE_SPECIAL_RIVAL_001", - "MISSION_TITLE_SPECIAL_RIVAL_002", - "MISSION_TITLE_SPECIAL_RIVAL_003" - }, - - CHILD = { - "MISSION_TITLE_SPECIAL_CHILD_001", - "MISSION_TITLE_SPECIAL_CHILD_002", - "MISSION_TITLE_SPECIAL_CHILD_003" - }, - FRIEND = { - "MISSION_TITLE_SPECIAL_FRIEND_001", - "MISSION_TITLE_SPECIAL_FRIEND_002", - "MISSION_TITLE_SPECIAL_FRIEND_003" - }, - LOVER = { - "MISSION_TITLE_SPECIAL_LOVER_001", - "MISSION_TITLE_SPECIAL_LOVER_002", - "MISSION_TITLE_SPECIAL_LOVER_003" - } -} - -MISSION_GEN.FLAVOR_TOP = { - RESCUE_SELF = { - "MISSION_BODY_TOP_RESCUE_SELF_001", - "MISSION_BODY_TOP_RESCUE_SELF_002", - "MISSION_BODY_TOP_RESCUE_SELF_003", - "MISSION_BODY_TOP_RESCUE_SELF_004", - "MISSION_BODY_TOP_RESCUE_SELF_005", - "MISSION_BODY_TOP_RESCUE_SELF_006", - "MISSION_BODY_TOP_RESCUE_SELF_007", - "MISSION_BODY_TOP_RESCUE_SELF_008", - "MISSION_BODY_TOP_RESCUE_SELF_009", - "MISSION_BODY_TOP_RESCUE_SELF_010" - }, - RESCUE_FRIEND = { - "MISSION_BODY_TOP_RESCUE_FRIEND_001", - "MISSION_BODY_TOP_RESCUE_FRIEND_002", - "MISSION_BODY_TOP_RESCUE_FRIEND_003", - "MISSION_BODY_TOP_RESCUE_FRIEND_004", - "MISSION_BODY_TOP_RESCUE_FRIEND_005", - "MISSION_BODY_TOP_RESCUE_FRIEND_006", - "MISSION_BODY_TOP_RESCUE_FRIEND_007", - "MISSION_BODY_TOP_RESCUE_FRIEND_008", - "MISSION_BODY_TOP_RESCUE_FRIEND_009", - "MISSION_BODY_TOP_RESCUE_FRIEND_010" - }, - ESCORT = { - "MISSION_BODY_TOP_ESCORT_001", - "MISSION_BODY_TOP_ESCORT_002", - "MISSION_BODY_TOP_ESCORT_003", - "MISSION_BODY_TOP_ESCORT_004", - "MISSION_BODY_TOP_ESCORT_005" - }, - EXPLORATION = { - "MISSION_BODY_TOP_EXPLORATION_001", - "MISSION_BODY_TOP_EXPLORATION_002", - "MISSION_BODY_TOP_EXPLORATION_003", - "MISSION_BODY_TOP_EXPLORATION_004", - "MISSION_BODY_TOP_EXPLORATION_005" - }, - DELIVERY = { - "MISSION_BODY_TOP_DELIVERY_001", - "MISSION_BODY_TOP_DELIVERY_002", - "MISSION_BODY_TOP_DELIVERY_003", - "MISSION_BODY_TOP_DELIVERY_004", - "MISSION_BODY_TOP_DELIVERY_005" - }, - LOST_ITEM = { - "MISSION_BODY_TOP_LOST_ITEM_001", - "MISSION_BODY_TOP_LOST_ITEM_002", - "MISSION_BODY_TOP_LOST_ITEM_003", - "MISSION_BODY_TOP_LOST_ITEM_004", - "MISSION_BODY_TOP_LOST_ITEM_005" - }, - OUTLAW = { - "MISSION_BODY_TOP_OUTLAW_001", - "MISSION_BODY_TOP_OUTLAW_002", - "MISSION_BODY_TOP_OUTLAW_003", - "MISSION_BODY_TOP_OUTLAW_004", - "MISSION_BODY_TOP_OUTLAW_005", - "MISSION_BODY_TOP_OUTLAW_006", - "MISSION_BODY_TOP_OUTLAW_007", - "MISSION_BODY_TOP_OUTLAW_008", - "MISSION_BODY_TOP_OUTLAW_009", - "MISSION_BODY_TOP_OUTLAW_010" - }, - OUTLAW_ITEM = { - "MISSION_BODY_TOP_OUTLAW_ITEM_001", - "MISSION_BODY_TOP_OUTLAW_ITEM_002", - "MISSION_BODY_TOP_OUTLAW_ITEM_003", - "MISSION_BODY_TOP_OUTLAW_ITEM_004", - "MISSION_BODY_TOP_OUTLAW_ITEM_005" - }, - OUTLAW_MONSTER_HOUSE = { - "MISSION_BODY_TOP_OUTLAW_MONSTER_HOUSE_001", - "MISSION_BODY_TOP_OUTLAW_MONSTER_HOUSE_002", - "MISSION_BODY_TOP_OUTLAW_MONSTER_HOUSE_003", - "MISSION_BODY_TOP_OUTLAW_MONSTER_HOUSE_004", - "MISSION_BODY_TOP_OUTLAW_MONSTER_HOUSE_005" - }, - OUTLAW_FLEE = { - "MISSION_BODY_TOP_OUTLAW_FLEE_001", - "MISSION_BODY_TOP_OUTLAW_FLEE_002", - "MISSION_BODY_TOP_OUTLAW_FLEE_003", - "MISSION_BODY_TOP_OUTLAW_FLEE_004", - "MISSION_BODY_TOP_OUTLAW_FLEE_005" - } -} - -MISSION_GEN.FLAVOR_BOTTOM = { - RESCUE_SELF = { - "MISSION_BODY_BOTTOM_RESCUE_SELF_001", - "MISSION_BODY_BOTTOM_RESCUE_SELF_002", - "MISSION_BODY_BOTTOM_RESCUE_SELF_003", - "MISSION_BODY_BOTTOM_RESCUE_SELF_004", - "MISSION_BODY_BOTTOM_RESCUE_SELF_005", - "MISSION_BODY_BOTTOM_RESCUE_SELF_006", - "MISSION_BODY_BOTTOM_RESCUE_SELF_007", - "MISSION_BODY_BOTTOM_RESCUE_SELF_008", - "MISSION_BODY_BOTTOM_RESCUE_SELF_009", - "MISSION_BODY_BOTTOM_RESCUE_SELF_010" - }, - RESCUE_FRIEND = { - "MISSION_BODY_BOTTOM_RESCUE_FRIEND_001", - "MISSION_BODY_BOTTOM_RESCUE_FRIEND_002", - "MISSION_BODY_BOTTOM_RESCUE_FRIEND_003", - "MISSION_BODY_BOTTOM_RESCUE_FRIEND_004", - "MISSION_BODY_BOTTOM_RESCUE_FRIEND_005", - "MISSION_BODY_BOTTOM_RESCUE_FRIEND_006", - "MISSION_BODY_BOTTOM_RESCUE_FRIEND_007", - "MISSION_BODY_BOTTOM_RESCUE_FRIEND_008", - "MISSION_BODY_BOTTOM_RESCUE_FRIEND_009", - "MISSION_BODY_BOTTOM_RESCUE_FRIEND_010" - }, - ESCORT = { - "MISSION_BODY_BOTTOM_ESCORT_001", - "MISSION_BODY_BOTTOM_ESCORT_002", - "MISSION_BODY_BOTTOM_ESCORT_003", - "MISSION_BODY_BOTTOM_ESCORT_004", - "MISSION_BODY_BOTTOM_ESCORT_005" - }, - EXPLORATION = { - "MISSION_BODY_BOTTOM_EXPLORATION_001", - "MISSION_BODY_BOTTOM_EXPLORATION_002", - "MISSION_BODY_BOTTOM_EXPLORATION_003", - "MISSION_BODY_BOTTOM_EXPLORATION_004", - "MISSION_BODY_BOTTOM_EXPLORATION_005" - }, - DELIVERY = { - "MISSION_BODY_BOTTOM_DELIVERY_001", - "MISSION_BODY_BOTTOM_DELIVERY_002", - "MISSION_BODY_BOTTOM_DELIVERY_003", - "MISSION_BODY_BOTTOM_DELIVERY_004", - "MISSION_BODY_BOTTOM_DELIVERY_005" - }, - LOST_ITEM = { - "MISSION_BODY_BOTTOM_LOST_ITEM_001", - "MISSION_BODY_BOTTOM_LOST_ITEM_002", - "MISSION_BODY_BOTTOM_LOST_ITEM_003", - "MISSION_BODY_BOTTOM_LOST_ITEM_004", - "MISSION_BODY_BOTTOM_LOST_ITEM_005" - }, - OUTLAW = { - "MISSION_BODY_BOTTOM_OUTLAW_001", - "MISSION_BODY_BOTTOM_OUTLAW_002", - "MISSION_BODY_BOTTOM_OUTLAW_003", - "MISSION_BODY_BOTTOM_OUTLAW_004", - "MISSION_BODY_BOTTOM_OUTLAW_005" - }, - OUTLAW_ITEM = { - "MISSION_BODY_BOTTOM_OUTLAW_ITEM_001", - "MISSION_BODY_BOTTOM_OUTLAW_ITEM_002", - "MISSION_BODY_BOTTOM_OUTLAW_ITEM_003", - "MISSION_BODY_BOTTOM_OUTLAW_ITEM_004", - "MISSION_BODY_BOTTOM_OUTLAW_ITEM_005" - }, - OUTLAW_MONSTER_HOUSE = { - "MISSION_BODY_BOTTOM_OUTLAW_MONSTER_HOUSE_001", - "MISSION_BODY_BOTTOM_OUTLAW_MONSTER_HOUSE_002", - "MISSION_BODY_BOTTOM_OUTLAW_MONSTER_HOUSE_003", - "MISSION_BODY_BOTTOM_OUTLAW_MONSTER_HOUSE_004", - "MISSION_BODY_BOTTOM_OUTLAW_MONSTER_HOUSE_005" - }, - OUTLAW_FLEE = { - "MISSION_BODY_BOTTOM_OUTLAW_FLEE_001", - "MISSION_BODY_BOTTOM_OUTLAW_FLEE_002", - "MISSION_BODY_BOTTOM_OUTLAW_FLEE_003", - "MISSION_BODY_BOTTOM_OUTLAW_FLEE_004", - "MISSION_BODY_BOTTOM_OUTLAW_FLEE_005" - } -} - -MISSION_GEN.POKEMON = { - --weak mons for easy missions - TIER_LOW = - {"abra","amaura","anorith","applin","archen","aron","arrokuda","axew","azurill", - "baltoy","bagon","barboach","bayleef","beldum","bellsprout","bidoof","bonsly","bounsweet","bronzor","budew","buizel","bulbasaur","buneary","burmy", - "cacnea","carvanha","cascoon","caterpie","charcadet","charmander","cherubi","chespin","chikorita","chimchar","chinchou","chingling","clamperl","clauncher","cleffa","clobbopus","combee","corphish","cottonee","cranidos","croagunk","cubchoo","cutiefly","cyndaquil", - "darumaka","deerling","deino","diglett","doduo","dratini","drifloon","dreepy","drowzee","duskull", - "ekans","electrike","elekid","elgyem","espurr","exeggcute", - "feebas","fennekin","ferroseed","fidough","finneon","flabebe","fletchling","fomantis","foongus","froakie","fuecoco", - "gastly","geodude","gible","glameow","goldeen","goomy","gothita","grimer","growlithe","gulpin", - "happiny","hatenna","helioptile","hippopotas","honedge","hoothoot","hoppip","horsea","houndour", - "igglybuff", - "jangmo_o","joltik", - "kabuto","kakuna","koffing","krabby","kricketot", - "larvesta","larvitar","ledyba","lileep","lillipup","litleo","litten","litwick","lotad","luvdisc", - "machop","magby","magikarp","magnemite","makuhita","mankey","mantyke","mareanie","mareep","meditite","metapod","mienfoo","mime_jr","minccino","morelull","mudbray","munna","mudkip","murkrow", - "natu","nickit","nincada","noibat","nosepass","numel","nymble", - "oddish","omanyte","onix","oshawott", - "pansear","paras","patrat","pawmi","pawniard","petilil","phantump","pidgey","pidove","pineco","piplup","poliwag","ponyta","poochyena","popplio","porygon","psyduck","pumpkaboo","purrloin", - "quaxly", - "ralts","rattata","remoraid","rhyhorn","roggenrola","rowlet", - "salandit","sandile","sandshrew","sandygast","scraggy","seedot","seel","sentret","sewaddle","shellder","shellos","shieldon","shinx","shroomish","shuppet","silcoon","sinistea","skorupi","slakoth","slowpoke","slugma","smoochum","sneasel","snivy","snom","snorunt","snover","snubbull","sobble","solosis","spearow","spheal","spinarak","spoink","sprigatito","spritzee","squirtle","starly","staryu","stufful","stunky","sunkern","surskit","swablu","swinub","swirlix", - "taillow","teddiursa","tentacool","tepig","tinkatink","torchic","togepi","totodile","trapinch","treecko","trubbish","turtwig","tympole","tyrogue", - - "vanillite","venonat","voltorb", - "wailmer","watchog","weedle","whismur","woobat","wooloo","wooper","wurmple","wynaut", - - - "zigzagoon","zubat"}, - - --middling mons for medium missions - TIER_MID = - {"aipom","arbok","ariados","audino", - "beautifly","beedrill","bibarel","bisharp","braixen","breloom","brionne","butterfree", - "carbink","carnivine","castform","chansey","charmeleon","chatot","cherrim","chimecho","clefairy","combusken","comfey","corsola","cramorant","croconaw", - "dartrix","delibird","dewott","dhelmise","ditto","dodrio","doublade","dragalge","dragonair","drakloak","dunsparce","duosion","dustox", - "eiscue","electabuzz","emboar","emolga", - "farfetchd","finizen","flaaffy","flapple","floette","fraxure","furret","furfrou", - "gabite","girafarig","gligar","gloom","golbat","gothorita","granbull","graveler","grotle","grovyle", - "hattrem","hakamo_o","haunter","herdier", - "illumise","indeedee","ivysaur", - "jigglypuff","jynx", - "kadabra","kangaskhan","kirlia","klefki","kricketune", - "lairon","lampent","ledian","liepard","linoone","lombre","loudred","lunatone","luxio", - "machoke","magmar","magneton","maractus","marill","marshtomp","masquerain","mawile","metang","mightyena","miltank","mimikyu","minior","minun","misdreavus","monferno","morgrem","morpeko","mothim","mr_mime","munchlax", - "nidorina","nidorino","ninjask","noctowl","nuzleaf", - - "pachirisu","palpitoad","parasect","pidgeotto","pignite","piloswine","plusle","poliwhirl","porygon2","prinplup","pupitar", - "quilava","qwilfish", - "raboot","raticate","relicanth","ribombee","roselia", - "sableye","scyther","seadra","sealeo","servine","seviper","shedinja","shelgon","shuckle","skiploom","sliggoo","smeargle","solrock","spinda","stantler","staravia","steenee","sudowoodo","sunflora","swadloon", - "tangela","thievul","tinkatuff","togedemaru","togetic","torkoal","tropius", - - "vanillish","venomoth","vespiquen","vibrava","vigoroth","volbeat", - "wartortle","weepinbell","wobbuffet","wormadam", - - "yanma", - "zangoose","zorua","zweilous"}, - - --strong pokemon for difficult missions - TIER_HIGH = - {"absol","aerodactyl","aegislash","aggron","alakazam","alcremie","altaria","ambipom","ampharos","appletun","arcanine","archeops","armaldo","aurorus","azumarill", - "banette","bastiodon","bellibolt","bellossom","blissey","blastoise","blaziken","braviary","bronzong", - "cacturne","camerupt","chandelure","charizard","cinderace","clawitzer","claydol","clefable","cloyster","corviknight","cradily","crawdaunt","crobat","cursola", - "decidueye","delphox","dewgong","donphan","dragapult","dragonite","drampa","drapion","drifblim","dugtrio","dusclops","dusknoir", - "eldegoss","electivire","empoleon","escavalier","exeggutor","exploud", - "fearow","feraligatr","ferrothorn","floatzel","florges","flygon","forretress","froslass","frosmoth", - "gallade","galvantula","garchomp","gardevoir","gastrodon","gengar","glalie","glimmora","gliscor","golduck","golem","golisopod","goodra","gorebyss","gothitelle","gourgeist","greninja","grumpig","gyarados", - "hariyama","hatterene","haxorus","heliolisk","heracross","hippowdon","hitmonchan","hitmonlee","hitmontop","honchkrow","houndoom","huntail","hydreigon","hypno", - "incineroar","infernape", - "jumpluff", - "kabutops","kingdra","kingambit","kingler","kommo_o", - "lanturn","lapras","leavanny","lilligant","lopunny","ludicolo","lumineon","lurantis","luxray","lycanroc", - "machamp","magcargo","magmortar","magnezone","mamoswine","mandibuzz","manectric","mantine","maushold","medicham","meganium","meowstic","metagross","mienshao","milotic","mismagius","muk","musharna", - "nidoking","nidoqueen","noivern", - "octillery","omastar","overqwil", - "pawmot","pidgeot","pinsir","politoed","poliwrath","porygon_z","primarina","primeape","probopass","purugly", - "quagsire", - "rampardos","rapidash","reuniclus","rhydon","rhyperior","roserade", - "salamence","salazzle","samurott","sandslash","sawsbuck","sceptile","scizor","scolipede","scorbunny","scrafty","seaking","serperior","sharpedo","shiftry","sirfetchd","skarmory","skuntank","slaking","slowbro","slowking","snorlax","spiritomb","staraptor","starmie","steelix","stoutland","swalot","swampert","swellow","swoobat", - "tangrowth","tauros","tentacruel","tinkaton","togekiss","torterra","toxicroak","toxtricity","tsareena","typhlosion","tyranitar", - "ursaring", - "vanilluxe","venusaur","victreebel","vileplume","volcanion", - "wailord","walrein","weezing","whimsicott","whiscash","wigglytuff", - "xatu", - "yamask","yanmega", - "zoroark"} -} - ---weighting of possible client/target pokemon based on difficulty of mission -MISSION_GEN.DIFF_POKEMON = { - F = { - {"TIER_LOW", 10}, - {"TIER_MID", 0}, - {"TIER_HIGH", 0} - }, - E = { - {"TIER_LOW", 9}, - {"TIER_MID", 1}, - {"TIER_HIGH", 0} - }, - D = { - {"TIER_LOW", 7}, - {"TIER_MID", 3}, - {"TIER_HIGH", 0} - }, - C = { - {"TIER_LOW", 6}, - {"TIER_MID", 4}, - {"TIER_HIGH", 0} - }, - B = { - {"TIER_LOW", 2}, - {"TIER_MID", 6}, - {"TIER_HIGH", 2} - }, - A = { - {"TIER_LOW", 1}, - {"TIER_MID", 5}, - {"TIER_HIGH", 4} - }, - S = { - {"TIER_LOW", 0}, - {"TIER_MID", 5}, - {"TIER_HIGH", 5} - }, - STAR_1 = { - {"TIER_LOW", 0}, - {"TIER_MID", 3}, - {"TIER_HIGH", 7} - }, - STAR_2 = { - {"TIER_LOW", 0}, - {"TIER_MID", 1}, - {"TIER_HIGH", 9} - }, - STAR_3 = { - {"TIER_LOW", 0}, - {"TIER_MID", 0}, - {"TIER_HIGH", 10} - }, - STAR_3 = { - {"TIER_LOW", 0}, - {"TIER_MID", 0}, - {"TIER_HIGH", 10} - }, - STAR_4 = { - {"TIER_LOW", 0}, - {"TIER_MID", 0}, - {"TIER_HIGH", 10} - }, - STAR_5 = { - {"TIER_LOW", 0}, - {"TIER_MID", 0}, - {"TIER_HIGH", 10} - }, - STAR_6 = { - {"TIER_LOW", 0}, - {"TIER_MID", 0}, - {"TIER_HIGH", 10} - }, - STAR_7 = { - {"TIER_LOW", 0}, - {"TIER_MID", 0}, - {"TIER_HIGH", 10} - }, - STAR_8 = { - {"TIER_LOW", 0}, - {"TIER_MID", 0}, - {"TIER_HIGH", 10} - }, - STAR_9 = { - {"TIER_LOW", 0}, - {"TIER_MID", 0}, - {"TIER_HIGH", 10} - } -} - ---weighting of each loot table based on difficulty of mission - -MISSION_GEN.DIFF_REWARDS = { - F = { - {"NECESSITIES", 10}, --Basic stuff (i.e. reviver seeds, escape orbs, leppa berries) * - {"AMMO_LOW", 0}, - {"AMMO_MID", 0}, - {"AMMO_HIGH", 0}, - {"APRICORN_GENERIC", 0}, - {"APRICORN_TYPED", 0}, - {"FOOD_LOW", 0}, - {"FOOD_MID", 0}, - {"FOOD_HIGH", 0}, - {"MEDICINE_LOW", 0}, - {"MEDICINE_HIGH", 0}, - {"SEED_LOW", 0}, - {"SEED_MID", 0}, - {"SEED_HIGH", 0}, - {"HELD_LOW", 0}, - {"HELD_MID", 0}, - {"HELD_HIGH", 0}, - {"HELD_TYPE", 0}, - {"HELD_PLATES", 0}, - {"LOOT_LOW", 0}, - {"LOOT_HIGH", 0}, - {"EVO_ITEMS", 0}, - {"ORBS_LOW", 0}, - {"ORBS_MID", 0}, - {"ORBS_HIGH", 0}, - {"WANDS_LOW", 0}, - {"WANDS_MID", 0}, - {"WANDS_HIGH", 0}, - {"TM_LOW", 0}, - {"TM_MID", 0}, - {"TM_HIGH", 0}, - {"SPECIAL", 0} - }, - E = { - {"NECESSITIES", 10}, --Basic stuff (i.e. reviver seeds, escape orbs, leppa berries) * - {"AMMO_LOW", 0}, - {"AMMO_MID", 0}, - {"AMMO_HIGH", 0}, - {"APRICORN_GENERIC", 10}, --Generic (non-typed) apricorns with a max catch bonus below 35 * - {"APRICORN_TYPED", 0}, - {"FOOD_LOW", 10}, --Basic food, small chance of gummis * - {"FOOD_MID", 0}, - {"FOOD_HIGH", 0}, - {"MEDICINE_LOW", 0}, - {"MEDICINE_HIGH", 0}, - {"SEED_LOW", 10}, --Basic seeds, berries, white herbs * - {"SEED_MID", 0}, - {"SEED_HIGH", 0}, - {"HELD_LOW", 3}, --Basic stat boosting held items and ones with a net drawback (Iron Ball, Flame Orb, etc.) - {"HELD_MID", 0}, - {"HELD_HIGH", 0}, - {"HELD_TYPE", 0}, - {"HELD_PLATES", 0}, - {"LOOT_LOW", 3}, --Keys, pearls, assembly boxes - {"LOOT_HIGH", 0}, - {"EVO_ITEMS", 1}, --Evolution items, high chance of link cables - {"ORBS_LOW", 0}, - {"ORBS_MID", 0}, - {"ORBS_HIGH", 0}, - {"WANDS_LOW", 10}, --Weak wands * - {"WANDS_MID", 0}, - {"WANDS_HIGH", 0}, - {"TM_LOW", 0}, - {"TM_MID", 0}, - {"TM_HIGH", 0}, - {"SPECIAL", 0} - }, - D = { - {"NECESSITIES", 5}, --Basic stuff (i.e. reviver seeds, escape orbs, leppa berries) - {"AMMO_LOW", 10}, --Mostly iron thorns, with some weaker ammo * - {"AMMO_MID", 0}, - {"AMMO_HIGH", 0}, - {"APRICORN_GENERIC", 10}, --Generic (non-typed) apricorns with a max catch bonus below 35 * - {"APRICORN_TYPED", 2}, --Type and glitter apricorns - {"FOOD_LOW", 4}, --Basic food, small chance of gummis - {"FOOD_MID", 0}, - {"FOOD_HIGH", 0}, - {"MEDICINE_LOW", 0}, - {"MEDICINE_HIGH", 0}, - {"SEED_LOW", 4}, --Basic seeds, berries, white herbs - {"SEED_MID", 10}, --Advanced seeds and type berries * - {"SEED_HIGH", 0}, - {"HELD_LOW", 10}, --Basic stat boosting held items and ones with a net drawback (Iron Ball, Flame Orb, etc.) * - {"HELD_MID", 0}, - {"HELD_HIGH", 0}, - {"HELD_TYPE", 3}, --Held items that boost a specific type - {"HELD_PLATES", 0}, - {"LOOT_LOW", 10}, --Keys, pearls, assembly boxes * - {"LOOT_HIGH", 0}, - {"EVO_ITEMS", 2}, --Evolution items, high chance of link cables - {"ORBS_LOW", 2}, --Weak wonder orbs - {"ORBS_MID", 0}, - {"ORBS_HIGH", 0}, - {"WANDS_LOW", 4}, --Weak wands - {"WANDS_MID", 10}, --Medium wands * - {"WANDS_HIGH", 0}, - {"TM_LOW", 0}, - {"TM_MID", 0}, - {"TM_HIGH", 0}, - {"SPECIAL", 0} - }, - C = { - {"NECESSITIES", 4}, --Basic stuff (i.e. reviver seeds, escape orbs, leppa berries) - {"AMMO_LOW", 4}, --Mostly iron thorns, with some weaker ammo - {"AMMO_MID", 2}, --Stronger generic ammo that you find in most dungeons - {"AMMO_HIGH", 0}, - {"APRICORN_GENERIC", 4}, --Generic (non-typed) apricorns with a max catch bonus below 35 - {"APRICORN_TYPED", 10}, --Type and glitter apricorns * - {"FOOD_LOW", 4}, --Basic food, small chance of gummis - {"FOOD_MID", 10}, --Big food, medium chance of gummis * - {"FOOD_HIGH", 0}, - {"MEDICINE_LOW", 0}, - {"MEDICINE_HIGH", 0}, - {"SEED_LOW", 4}, --Basic seeds, berries, white herbs - {"SEED_MID", 4}, --Advanced seeds and type berries - {"SEED_HIGH", 0}, - {"HELD_LOW", 4}, --Basic stat boosting held items and ones with a net drawback (Iron Ball, Flame Orb, etc.) - {"HELD_MID", 0}, - {"HELD_HIGH", 0}, - {"HELD_TYPE", 3}, --Held items that boost a specific type - {"HELD_PLATES", 10}, --Held items that reduce damage from a specific type * - {"LOOT_LOW", 4}, --Keys, pearls, assembly boxes - {"LOOT_HIGH", 0}, - {"EVO_ITEMS", 4}, --Evolution items, high chance of link cables - {"ORBS_LOW", 10}, --Weak wonder orbs * - {"ORBS_MID", 2}, --Medium wonder orbs, many can shut down a monster house - {"ORBS_HIGH", 0}, - {"WANDS_LOW", 4}, --Weak wands - {"WANDS_MID", 4}, --Medium wands - {"WANDS_HIGH", 0}, - {"TM_LOW", 10}, --TMs for weak moves * - {"TM_MID", 0}, - {"TM_HIGH", 0}, - {"SPECIAL", 0} - }, - B = { - {"NECESSITIES", 3}, --Basic stuff (i.e. reviver seeds, escape orbs, leppa berries) - {"AMMO_LOW", 0}, - {"AMMO_MID", 10}, --Stronger generic ammo that you find in most dungeons * - {"AMMO_HIGH", 2}, --Rare ammo that are hard to find in dungeons - {"APRICORN_GENERIC", 0}, - {"APRICORN_TYPED", 10}, --Type and glitter apricorns * - {"FOOD_LOW", 3}, --Basic food, small chance of gummis - {"FOOD_MID", 5}, --Big food, medium chance of gummis - {"FOOD_HIGH", 0}, - {"MEDICINE_LOW", 2}, --Weaker medicine that can't heal all PP or HP at once - {"MEDICINE_HIGH", 0}, - {"SEED_LOW", 0}, - {"SEED_MID", 4}, --Advanced seeds and type berries - {"SEED_HIGH", 3}, --Includes rare seeds and berries, skews to Pure Seeds - {"HELD_LOW", 3}, --Basic stat boosting held items and ones with a net drawback (Iron Ball, Flame Orb, etc.) - {"HELD_MID", 10}, --Held items very useful for a specific strategy * - {"HELD_HIGH", 0}, - {"HELD_TYPE", 3}, --Held items that boost a specific type - {"HELD_PLATES", 3}, --Held items that reduce damage from a specific type - {"LOOT_LOW", 4}, --Keys, pearls, assembly boxes - {"LOOT_HIGH", 0}, - {"EVO_ITEMS", 5}, --Evolution items, high chance of link cables - {"ORBS_LOW", 4}, --Weak wonder orbs - {"ORBS_MID", 10}, --Medium wonder orbs, many can shut down a monster house * - {"ORBS_HIGH", 0}, - {"WANDS_LOW", 0}, - {"WANDS_MID", 3}, --Medium wands - {"WANDS_HIGH", 10}, --Rare, specialty wands * - {"TM_LOW", 4}, --TMs for weak moves - {"TM_MID", 2}, --TMs for moderate moves - {"TM_HIGH", 0}, - {"SPECIAL", 0} - }, - A = { - {"NECESSITIES", 2}, --Basic stuff (i.e. reviver seeds, escape orbs, leppa berries) - {"AMMO_LOW", 0}, - {"AMMO_MID", 3}, --Stronger generic ammo that you find in most dungeons - {"AMMO_HIGH", 10}, --Rare ammo that are hard to find in dungeons * - {"APRICORN_GENERIC", 0}, - {"APRICORN_TYPED", 10}, --Type and glitter apricorns * - {"FOOD_LOW", 0}, - {"FOOD_MID", 4}, --Big food, medium chance of gummis - {"FOOD_HIGH", 2}, --Huge food with a high chance of wonder gummis and a chance for vitamins - {"MEDICINE_LOW", 10}, --Weaker medicine that can't heal all PP or HP at once * - {"MEDICINE_HIGH", 0}, - {"SEED_LOW", 0}, - {"SEED_MID", 3}, --Advanced seeds and type berries - {"SEED_HIGH", 10}, --Includes rare seeds and berries, skews to Pure Seeds * - {"HELD_LOW", 0}, - {"HELD_MID", 5}, --Held items very useful for a specific strategy - {"HELD_HIGH", 2}, --Held items useful for anyone - {"HELD_TYPE", 3}, --Held items that boost a specific type - {"HELD_PLATES", 3}, --Held items that reduce damage from a specific type - {"LOOT_LOW", 3}, --Keys, pearls, assembly boxes - {"LOOT_HIGH", 10}, --Rare loot, skews towards heart scales * - {"EVO_ITEMS", 10}, --Evolution items, high chance of link cables * - {"ORBS_LOW", 0}, - {"ORBS_MID", 4}, --Medium wonder orbs, many can shut down a monster house - {"ORBS_HIGH", 4}, --Rare, powerful wonder orbs often with map wide effects - {"WANDS_LOW", 0}, - {"WANDS_MID", 2}, --Medium wands - {"WANDS_HIGH", 5}, --Rare, specialty wands - {"TM_LOW", 2}, --TMs for weak moves - {"TM_MID", 10}, --TMs for moderate moves * - {"TM_HIGH", 0}, - {"SPECIAL", 0} - }, - S = { - {"NECESSITIES", 2}, --Basic stuff (i.e. reviver seeds, escape orbs, leppa berries) - {"AMMO_LOW", 0}, - {"AMMO_MID", 2}, --Stronger generic ammo that you find in most dungeons - {"AMMO_HIGH", 5}, --Rare ammo that are hard to find in dungeons - {"APRICORN_GENERIC", 0}, - {"APRICORN_TYPED", 5}, --Type and glitter apricorns - {"FOOD_LOW", 0}, - {"FOOD_MID", 2}, --Big food, medium chance of gummis - {"FOOD_HIGH", 10}, --Huge food with a high chance of wonder gummis and a chance for vitamins * - {"MEDICINE_LOW", 4}, --Weaker medicine that can't heal all PP or HP at once - {"MEDICINE_HIGH", 10}, --Powerful medicine that can heal everything * - {"SEED_LOW", 0}, - {"SEED_MID", 0}, - {"SEED_HIGH", 5}, --Includes rare seeds and berries, skews to Pure Seeds - {"HELD_LOW", 0}, - {"HELD_MID", 3}, --Held items very useful for a specific strategy - {"HELD_HIGH", 10}, --Held items useful for anyone * - {"HELD_TYPE", 3}, --Held items that boost a specific type - {"HELD_PLATES", 3}, --Held items that reduce damage from a specific type - {"LOOT_LOW", 2}, --Keys, pearls, assembly boxes - {"LOOT_HIGH", 10}, --Rare loot, skews towards heart scales * - {"EVO_ITEMS", 10}, --Evolution items, high chance of link cables * - {"ORBS_LOW", 0}, - {"ORBS_MID", 2}, --Medium wonder orbs, many can shut down a monster house - {"ORBS_HIGH", 5}, --Rare, powerful wonder orbs often with map wide effects - {"WANDS_LOW", 0}, - {"WANDS_MID", 2}, --Medium wands - {"WANDS_HIGH", 4}, --Rare, specialty wands - {"TM_LOW", 0}, - {"TM_MID", 4}, --TMs for moderate moves - {"TM_HIGH", 10}, --TMs for very powerful moves * - {"SPECIAL", 0} - }, - STAR_1 = { - {"NECESSITIES", 0}, - {"AMMO_LOW", 0}, - {"AMMO_MID", 2}, --Stronger generic ammo that you find in most dungeons - {"AMMO_HIGH", 5}, --Rare ammo that are hard to find in dungeons - {"APRICORN_GENERIC", 0}, - {"APRICORN_TYPED", 5}, --Type and glitter apricorns - {"FOOD_LOW", 0}, - {"FOOD_MID", 0}, - {"FOOD_HIGH", 5}, --Huge food with a high chance of wonder gummis and a chance for vitamins - {"MEDICINE_LOW", 2}, --Weaker medicine that can't heal all PP or HP at once - {"MEDICINE_HIGH", 5}, --Powerful medicine that can heal everything - {"SEED_LOW", 0}, - {"SEED_MID", 0}, - {"SEED_HIGH", 2}, --Includes rare seeds and berries, skews to Pure Seeds - {"HELD_LOW", 0}, - {"HELD_MID", 2}, --Held items very useful for a specific strategy - {"HELD_HIGH", 5}, --Held items useful for anyone - {"HELD_TYPE", 0}, - {"HELD_PLATES", 0}, - {"LOOT_LOW", 2}, --Keys, pearls, assembly boxes - {"LOOT_HIGH", 5}, --Rare loot, skews towards heart scales - {"EVO_ITEMS", 5}, --Evolution items, high chance of link cables - {"ORBS_LOW", 0}, - {"ORBS_MID", 2}, --Medium wonder orbs, many can shut down a monster house - {"ORBS_HIGH", 5}, --Rare, powerful wonder orbs often with map wide effects - {"WANDS_LOW", 0}, - {"WANDS_MID", 0}, - {"WANDS_HIGH", 3}, --Rare, specialty wands - {"TM_LOW", 0}, - {"TM_MID", 4}, --TMs for moderate moves - {"TM_HIGH", 5}, --TMs for very powerful moves - {"SPECIAL", 1} --Very rare, powerful treasures (Amber Tears, Ability Capsules, Golden Apples, etc.) - }, - STAR_2 = { - {"NECESSITIES", 0}, - {"AMMO_LOW", 0}, - {"AMMO_MID", 0}, - {"AMMO_HIGH", 5}, --Rare ammo that are hard to find in dungeons - {"APRICORN_GENERIC", 0}, - {"APRICORN_TYPED", 0}, - {"FOOD_LOW", 0}, - {"FOOD_MID", 0}, - {"FOOD_HIGH", 5}, --Huge food with a high chance of wonder gummis and a chance for vitamins - {"MEDICINE_LOW", 0}, - {"MEDICINE_HIGH", 5}, --Powerful medicine that can heal everything - {"SEED_LOW", 0}, - {"SEED_MID", 0}, - {"SEED_HIGH", 0}, - {"HELD_LOW", 0}, - {"HELD_MID", 0}, - {"HELD_HIGH", 5}, --Held items useful for anyone - {"HELD_TYPE", 0}, - {"HELD_PLATES", 0}, - {"LOOT_LOW", 0}, - {"LOOT_HIGH", 5}, --Rare loot, skews towards heart scales - {"EVO_ITEMS", 5}, --Evolution items, high chance of link cables - {"ORBS_LOW", 0}, - {"ORBS_MID", 0}, - {"ORBS_HIGH", 5}, --Rare, powerful wonder orbs often with map wide effects - {"WANDS_LOW", 0}, - {"WANDS_MID", 0}, - {"WANDS_HIGH", 0}, - {"TM_LOW", 0}, - {"TM_MID", 2}, --TMs for moderate moves - {"TM_HIGH", 5}, --TMs for very powerful moves - {"SPECIAL", 2} --Very rare, powerful treasures (Amber Tears, Ability Capsules, Golden Apples, etc.) - }, - STAR_3 = { - {"NECESSITIES", 0}, - {"AMMO_LOW", 0}, - {"AMMO_MID", 0}, - {"AMMO_HIGH", 5}, --Rare ammo that are hard to find in dungeons - {"APRICORN_GENERIC", 0}, - {"APRICORN_TYPED", 0}, - {"FOOD_LOW", 0}, - {"FOOD_MID", 0}, - {"FOOD_HIGH", 5}, --Huge food with a high chance of wonder gummis and a chance for vitamins - {"MEDICINE_LOW", 0}, - {"MEDICINE_HIGH", 5}, --Powerful medicine that can heal everything - {"SEED_LOW", 0}, - {"SEED_MID", 0}, - {"SEED_HIGH", 0}, - {"HELD_LOW", 0}, - {"HELD_MID", 0}, - {"HELD_HIGH", 5}, --Held items useful for anyone - {"HELD_TYPE", 0}, - {"HELD_PLATES", 0}, - {"LOOT_LOW", 0}, - {"LOOT_HIGH", 5}, --Rare loot, skews towards heart scales - {"EVO_ITEMS", 5}, --Evolution items, high chance of link cables - {"ORBS_LOW", 0}, - {"ORBS_MID", 0}, - {"ORBS_HIGH", 0}, - {"WANDS_LOW", 0}, - {"WANDS_MID", 0}, - {"WANDS_HIGH", 0}, - {"TM_LOW", 0}, - {"TM_MID", 0}, - {"TM_HIGH", 5}, --TMs for very powerful moves - {"SPECIAL", 3} --Very rare, powerful treasures (Amber Tears, Ability Capsules, Golden Apples, etc.) - }, - STAR_4 = { - {"NECESSITIES", 0}, - {"AMMO_LOW", 0}, - {"AMMO_MID", 0}, - {"AMMO_HIGH", 0}, - {"APRICORN_GENERIC", 0}, - {"APRICORN_TYPED", 0}, - {"FOOD_LOW", 0}, - {"FOOD_MID", 0}, - {"FOOD_HIGH", 5}, --Huge food with a high chance of wonder gummis and a chance for vitamins - {"MEDICINE_LOW", 0}, - {"MEDICINE_HIGH", 0}, - {"SEED_LOW", 0}, - {"SEED_MID", 0}, - {"SEED_HIGH", 0}, - {"HELD_LOW", 0}, - {"HELD_MID", 0}, - {"HELD_HIGH", 5}, --Held items useful for anyone - {"HELD_TYPE", 0}, - {"HELD_PLATES", 0}, - {"LOOT_LOW", 0}, - {"LOOT_HIGH", 5}, --Rare loot, skews towards heart scales - {"EVO_ITEMS", 5}, --Evolution items, high chance of link cables - {"ORBS_LOW", 0}, - {"ORBS_MID", 0}, - {"ORBS_HIGH", 0}, - {"WANDS_LOW", 0}, - {"WANDS_MID", 0}, - {"WANDS_HIGH", 0}, - {"TM_LOW", 0}, - {"TM_MID", 0}, - {"TM_HIGH", 5}, --TMs for very powerful moves - {"SPECIAL", 4} --Very rare, powerful treasures (Amber Tears, Ability Capsules, Golden Apples, etc.) - }, - STAR_5 = { - {"NECESSITIES", 0}, - {"AMMO_LOW", 0}, - {"AMMO_MID", 0}, - {"AMMO_HIGH", 0}, - {"APRICORN_GENERIC", 0}, - {"APRICORN_TYPED", 0}, - {"FOOD_LOW", 0}, - {"FOOD_MID", 0}, - {"FOOD_HIGH", 5}, --Huge food with a high chance of wonder gummis and a chance for vitamins - {"MEDICINE_LOW", 0}, - {"MEDICINE_HIGH", 0}, - {"SEED_LOW", 0}, - {"SEED_MID", 0}, - {"SEED_HIGH", 0}, - {"HELD_LOW", 0}, - {"HELD_MID", 0}, - {"HELD_HIGH", 0}, - {"HELD_TYPE", 0}, - {"HELD_PLATES", 0}, - {"LOOT_LOW", 0}, - {"LOOT_HIGH", 0}, - {"EVO_ITEMS", 0}, - {"ORBS_LOW", 0}, - {"ORBS_MID", 0}, - {"ORBS_HIGH", 0}, - {"WANDS_LOW", 0}, - {"WANDS_MID", 0}, - {"WANDS_HIGH", 0}, - {"TM_LOW", 0}, - {"TM_MID", 0}, - {"TM_HIGH", 5}, --TMs for very powerful moves - {"SPECIAL", 5} --Very rare, powerful treasures (Amber Tears, Ability Capsules, Golden Apples, etc.) - }, - STAR_6 = { - {"NECESSITIES", 0}, - {"AMMO_LOW", 0}, - {"AMMO_MID", 0}, - {"AMMO_HIGH", 0}, - {"APRICORN_GENERIC", 0}, - {"APRICORN_TYPED", 0}, - {"FOOD_LOW", 0}, - {"FOOD_MID", 0}, - {"FOOD_HIGH", 4}, --Huge food with a high chance of wonder gummis and a chance for vitamins - {"MEDICINE_LOW", 0}, - {"MEDICINE_HIGH", 0}, - {"SEED_LOW", 0}, - {"SEED_MID", 0}, - {"SEED_HIGH", 0}, - {"HELD_LOW", 0}, - {"HELD_MID", 0}, - {"HELD_HIGH", 0}, - {"HELD_TYPE", 0}, - {"HELD_PLATES", 0}, - {"LOOT_LOW", 0}, - {"LOOT_HIGH", 0}, - {"EVO_ITEMS", 0}, - {"ORBS_LOW", 0}, - {"ORBS_MID", 0}, - {"ORBS_HIGH", 0}, - {"WANDS_LOW", 0}, - {"WANDS_MID", 0}, - {"WANDS_HIGH", 0}, - {"TM_LOW", 0}, - {"TM_MID", 0}, - {"TM_HIGH", 4}, --TMs for very powerful moves - {"SPECIAL", 6} --Very rare, powerful treasures (Amber Tears, Ability Capsules, Golden Apples, etc.) - }, - STAR_7 = { - {"NECESSITIES", 0}, - {"AMMO_LOW", 0}, - {"AMMO_MID", 0}, - {"AMMO_HIGH", 0}, - {"APRICORN_GENERIC", 0}, - {"APRICORN_TYPED", 0}, - {"FOOD_LOW", 0}, - {"FOOD_MID", 0}, - {"FOOD_HIGH", 3}, --Huge food with a high chance of wonder gummis and a chance for vitamins - {"MEDICINE_LOW", 0}, - {"MEDICINE_HIGH", 0}, - {"SEED_LOW", 0}, - {"SEED_MID", 0}, - {"SEED_HIGH", 0}, - {"HELD_LOW", 0}, - {"HELD_MID", 0}, - {"HELD_HIGH", 0}, - {"HELD_TYPE", 0}, - {"HELD_PLATES", 0}, - {"LOOT_LOW", 0}, - {"LOOT_HIGH", 0}, - {"EVO_ITEMS", 0}, - {"ORBS_LOW", 0}, - {"ORBS_MID", 0}, - {"ORBS_HIGH", 0}, - {"WANDS_LOW", 0}, - {"WANDS_MID", 0}, - {"WANDS_HIGH", 0}, - {"TM_LOW", 0}, - {"TM_MID", 0}, - {"TM_HIGH", 3}, --TMs for very powerful moves - {"SPECIAL", 7} --Very rare, powerful treasures (Amber Tears, Ability Capsules, Golden Apples, etc.) - }, - STAR_8 = { - {"NECESSITIES", 0}, - {"AMMO_LOW", 0}, - {"AMMO_MID", 0}, - {"AMMO_HIGH", 0}, - {"APRICORN_GENERIC", 0}, - {"APRICORN_TYPED", 0}, - {"FOOD_LOW", 0}, - {"FOOD_MID", 0}, - {"FOOD_HIGH", 0}, - {"MEDICINE_LOW", 0}, - {"MEDICINE_HIGH", 0}, - {"SEED_LOW", 0}, - {"SEED_MID", 0}, - {"SEED_HIGH", 0}, - {"HELD_LOW", 0}, - {"HELD_MID", 0}, - {"HELD_HIGH", 0}, - {"HELD_TYPE", 0}, - {"HELD_PLATES", 0}, - {"LOOT_LOW", 0}, - {"LOOT_HIGH", 0}, - {"EVO_ITEMS", 0}, - {"ORBS_LOW", 0}, - {"ORBS_MID", 0}, - {"ORBS_HIGH", 0}, - {"WANDS_LOW", 0}, - {"WANDS_MID", 0}, - {"WANDS_HIGH", 0}, - {"TM_LOW", 0}, - {"TM_MID", 0}, - {"TM_HIGH", 3}, --TMs for very powerful moves - {"SPECIAL", 8} --Very rare, powerful treasures (Amber Tears, Ability Capsules, Golden Apples, etc.) - }, - STAR_9 = { - {"NECESSITIES", 0}, - {"AMMO_LOW", 0}, - {"AMMO_MID", 0}, - {"AMMO_HIGH", 0}, - {"APRICORN_GENERIC", 0}, - {"APRICORN_TYPED", 0}, - {"FOOD_LOW", 0}, - {"FOOD_MID", 0}, - {"FOOD_HIGH", 0}, - {"MEDICINE_LOW", 0}, - {"MEDICINE_HIGH", 0}, - {"SEED_LOW", 0}, - {"SEED_MID", 0}, - {"SEED_HIGH", 0}, - {"HELD_LOW", 0}, - {"HELD_MID", 0}, - {"HELD_HIGH", 0}, - {"HELD_TYPE", 0}, - {"HELD_PLATES", 0}, - {"LOOT_LOW", 0}, - {"LOOT_HIGH", 0}, - {"EVO_ITEMS", 0}, - {"ORBS_LOW", 0}, - {"ORBS_MID", 0}, - {"ORBS_HIGH", 0}, - {"WANDS_LOW", 0}, - {"WANDS_MID", 0}, - {"WANDS_HIGH", 0}, - {"TM_LOW", 0}, - {"TM_MID", 0}, - {"TM_HIGH", 0}, - {"SPECIAL", 9} --Very rare, powerful treasures (Amber Tears, Ability Capsules, Golden Apples, etc.) - }, -} - - - ---Weighted list of rewards to choose from for missions -MISSION_GEN.REWARDS = { - --Reward tables of high and low tier loot separated by category (TM, ammo, held items, etc) - --different mission difficulties have different chances to roll each table - - NECESSITIES = { - {"seed_reviver", 10}, - {"berry_leppa", 5}, - {"berry_oran", 5}, - {"berry_lum", 5}, - {"food_apple", 5}, - {"orb_escape", 5}, - {"apricorn_plain", 5}, - {"key", 2} - }, - - AMMO_LOW = { - {"ammo_iron_thorn", 5}, - {"ammo_geo_pebble", 1}, - {"ammo_stick", 1}, - }, - - AMMO_MID = { - {"ammo_geo_pebble", 5}, - {"ammo_gravelerock", 5}, - {"ammo_stick", 5}, - {"ammo_silver_spike", 5} - }, - - AMMO_HIGH = { - {"ammo_rare_fossil", 5}, - {"ammo_corsola_twig", 5}, - {"ammo_cacnea_spike", 5} - }, - - APRICORN_GENERIC = { - {"apricorn_plain", 12}, - {"apricorn_big", 4} - }, - - APRICORN_TYPED = { - {"apricorn_blue", 5}, - {"apricorn_green", 5}, - {"apricorn_brown", 5}, - {"apricorn_purple", 5}, - {"apricorn_red", 5}, - {"apricorn_white", 5}, - {"apricorn_yellow", 5}, - {"apricorn_black", 5}, - {"apricorn_glittery", 5} - }, - --Rare chance for gummis - FOOD_LOW = { - {"food_apple", 30}, - {"food_banana", 18}, - {"gummi_blue", 1}, - {"gummi_black", 1}, - {"gummi_clear", 1}, - {"gummi_grass", 1}, - {"gummi_green", 1}, - {"gummi_brown", 1}, - {"gummi_orange", 1}, - {"gummi_gold", 1}, - {"gummi_pink", 1}, - {"gummi_purple", 1}, - {"gummi_red", 1}, - {"gummi_royal", 1}, - {"gummi_silver", 1}, - {"gummi_white", 1}, - {"gummi_yellow", 1}, - {"gummi_sky", 1}, - {"gummi_gray", 1}, - {"gummi_magenta", 1} - }, - --Moderate chance of gummis, rare chance of wonder gummis - FOOD_MID = { - {"food_apple_big", 30}, - {"food_banana_big", 18}, - {"gummi_blue", 2}, - {"gummi_black", 2}, - {"gummi_clear", 2}, - {"gummi_grass", 2}, - {"gummi_green", 2}, - {"gummi_brown", 2}, - {"gummi_orange", 2}, - {"gummi_gold", 2}, - {"gummi_pink", 2}, - {"gummi_purple", 2}, - {"gummi_red", 2}, - {"gummi_royal", 2}, - {"gummi_silver", 2}, - {"gummi_white", 2}, - {"gummi_yellow", 2}, - {"gummi_sky", 2}, - {"gummi_gray", 2}, - {"gummi_magenta", 2}, - {"gummi_wonder", 1} - }, - --Small chance for vitamins - FOOD_HIGH = { - {"food_apple_huge", 30}, - {"food_apple_perfect", 18}, - {"food_banana_big", 18}, - {"gummi_wonder", 30}, - {"boost_calcium", 3}, - {"boost_protein", 3}, - {"boost_hp_up", 3}, - {"boost_zinc", 3}, - {"boost_carbos", 3}, - {"boost_iron", 3}, - {"boost_nectar", 5} - }, - - --Basic manufactured medicine - MEDICINE_LOW = { - {"medicine_potion", 20}, - {"medicine_elixir", 20}, - {"medicine_full_heal", 10}, - {"medicine_x_attack", 10}, - {"medicine_x_defense", 10}, - {"medicine_x_sp_atk", 10}, - {"medicine_x_sp_def", 10}, - {"medicine_x_speed", 10}, - {"medicine_x_accuracy", 10}, - {"medicine_dire_hit", 10} - }, - - --Advanced manufactued medicine - MEDICINE_HIGH = { - {"medicine_max_potion", 20}, - {"medicine_max_elixir", 20}, - {"medicine_full_heal", 10} - }, - - --includes seeds and berries, as well as white herbs - SEED_LOW = { - {'seed_blast', 5}, - {'seed_sleep', 5}, - {'seed_warp', 5}, - {'berry_oran', 5}, - {'berry_leppa', 5}, - {'berry_sitrus', 5}, - {'berry_lum', 5}, - {"herb_white", 10} - }, - --Includes advanced seeds, herbs, and type berries - SEED_MID = { - {'seed_reviver', 25}, - {'seed_decoy', 5}, - {'seed_blinker', 5}, - {'seed_last_chance', 5}, - {'seed_doom', 5}, - {'seed_ban', 5}, - {'seed_ice', 5}, - {'seed_vile', 5}, - {'berry_tanga', 2}, - {'berry_colbur', 2}, - {'berry_wacan', 2}, - {'berry_haban', 2}, - {'berry_chople', 2}, - {'berry_occa', 2}, - {'berry_coba', 2}, - {'berry_kasib', 2}, - {'berry_rindo', 2}, - {'berry_shuca', 2}, - {'berry_yache', 2}, - {'berry_chilan', 2}, - {'berry_kebia', 2}, - {'berry_payapa', 2}, - {'berry_charti', 2}, - {'berry_babiri', 2}, - {'berry_passho', 2}, - {'berry_roseli', 2}, - {"herb_power", 10}, - {"herb_mental", 10} - }, - - --includes rare seeds and berries - SEED_HIGH = { - {'seed_pure', 15}, - {'seed_joy', 1}, - {'berry_rowap', 5}, - {'berry_jaboca', 5}, - {'berry_liechi', 5}, - {'berry_ganlon', 5}, - {'berry_salac', 5}, - {'berry_petaya', 5}, - {'berry_apicot', 5}, - {'berry_micle', 5}, - {'berry_enigma', 5}, - {'berry_starf', 5} - }, - - HELD_LOW = { - {'held_power_band', 5}, - {'held_special_band', 5}, - {'held_defense_scarf', 5}, - {'held_zinc_band', 5}, - {'held_toxic_orb', 5}, - {'held_flame_orb', 5}, - {'held_iron_ball', 5}, - {'held_ring_target', 5} - }, - - HELD_MID = { - {'held_pierce_band', 5}, - {'held_warp_scarf', 5}, - {'held_scope_lens', 5}, - {'held_reunion_cape', 5}, - {'held_heal_ribbon', 5}, - {'held_twist_band', 5}, - {'held_grip_claw', 5}, - {'held_binding_band', 5}, - {'held_metronome', 5}, - {'held_shed_shell', 5}, - {'held_wide_lens', 5}, - {'held_sticky_barb', 5}, - {'held_choice_band', 5}, - {'held_choice_scarf', 5}, - {'held_choice_specs', 5} - }, - - HELD_HIGH = { - {'held_golden_mask', 5}, - {'held_friend_bow', 2}, - {'held_shell_bell', 5}, - {'held_mobile_scarf', 5}, - {'held_cover_band', 5}, - {'held_pass_scarf', 5}, - {'held_trap_scarf', 5}, - {'held_pierce_band', 5}, - {'held_goggle_specs', 5}, - {'held_x_ray_specs', 5}, - {'held_assault_vest', 5}, - {'held_life_orb', 5} - }, - - HELD_TYPE = { - {'held_silver_powder', 5}, - {'held_black_glasses', 5}, - {'held_dragon_scale', 5}, - {'held_magnet', 5}, - {'held_pink_bow', 5}, - {'held_black_belt', 5}, - {'held_charcoal', 5}, - {'held_sharp_beak', 5}, - {'held_spell_tag', 5}, - {'held_miracle_seed', 5}, - {'held_soft_sand', 5}, - {'held_never_melt_ice', 5}, - {'held_silk_scarf', 5}, - {'held_poison_barb', 5}, - {'held_twisted_spoon', 5}, - {'held_hard_stone', 5}, - {'held_metal_coat', 5}, - {'held_mystic_water', 5} - }, - - HELD_PLATES = { - {'held_insect_plate', 5}, - {'held_dread_plate', 5}, - {'held_draco_plate', 5}, - {'held_zap_plate', 5}, - {'held_pixie_plate', 5}, - {'held_fist_plate', 5}, - {'held_flame_plate', 5}, - {'held_sky_plate', 5}, - {'held_spooky_plate', 5}, - {'held_meadow_plate', 5}, - {'held_earth_plate', 5}, - {'held_icicle_plate', 5}, - {'held_blank_plate', 5}, - {'held_toxic_plate', 5}, - {'held_mind_plate', 5}, - {'held_stone_plate', 5}, - {'held_iron_plate', 5}, - {'held_splash_plate', 5} - }, - - --Spawns boxes, keys, heart scales, and loot - LOOT_LOW = { - {'loot_heart_scale', 5}, - {'loot_pearl', 10}, - {'machine_assembly_box', 10}, - {'key', 10} - }, - - LOOT_HIGH = { - {'loot_heart_scale', 20}, - {'loot_nugget', 5}, - {'machine_recall_box', 10}, - {'machine_storage_box', 10} - }, - - EVO_ITEMS = { - {'evo_link_cable', 30}, - {'evo_fire_stone', 5}, - {'evo_thunder_stone', 5}, - {'evo_water_stone', 5}, - {'evo_leaf_stone', 5}, - {'evo_moon_stone', 5}, - {'evo_sun_stone', 5}, - {'evo_magmarizer', 5}, - {'evo_electirizer', 5}, - {'evo_reaper_cloth', 5}, - {'evo_cracked_pot', 5}, - {'evo_chipped_pot', 5}, - {'evo_shiny_stone', 5}, - {'evo_dusk_stone', 5}, - {'evo_dawn_stone', 5}, - {'evo_up_grade', 5}, - {'evo_dubious_disc', 5}, - {'evo_razor_fang', 5}, - {'evo_razor_claw', 5}, - {'evo_protector', 5}, - {'evo_prism_scale', 5}, - {'evo_kings_rock', 5}, - {'evo_sun_ribbon', 5}, - {'evo_lunar_ribbon', 5}, - {'evo_ice_stone', 5} - }, - - ORBS_LOW = { - {"orb_escape", 5}, - {"orb_weather", 5}, - {"orb_cleanse", 5}, - {"orb_endure", 5}, - {"orb_trapbust", 5}, - {"orb_petrify", 5}, - {"orb_foe_hold", 5}, - {"orb_nullify", 5}, - {"orb_all_dodge", 5}, - {"orb_rebound", 5}, - {"orb_mirror", 5}, - {"orb_foe_seal", 5}, - {"orb_rollcall", 5}, - {"orb_mug", 5}, - }, - - ORBS_MID = { - {"orb_escape", 5}, - {"orb_mobile", 5}, - {"orb_invisify", 5}, - {"orb_all_aim", 5}, - {"orb_trawl", 5}, - {"orb_one_shot", 5}, - {"orb_pierce", 5}, - {"orb_all_protect", 5}, - {"orb_trap_see", 5}, - {"orb_slumber", 5}, - {"orb_totter", 5}, - {"orb_freeze", 5}, - {"orb_spurn", 5}, - {"orb_itemizer", 5}, - {"orb_halving", 5}, - }, - - ORBS_HIGH = { - {"orb_escape", 5}, - {"orb_luminous", 5}, - {"orb_invert", 5}, - {"orb_devolve", 5}, - {"orb_revival", 5}, - {"orb_scanner", 5}, - {"orb_stayaway", 5}, - {"orb_one_room", 5}, - }, - - WANDS_LOW = { - {"wand_pounce", 5}, - {"wand_slow", 5}, - {"wand_topsy_turvy", 5}, - {"wand_purge", 5} - }, - - WANDS_MID = { - {"wand_path", 5}, - {"wand_whirlwind", 5}, - {"wand_switcher", 5}, - {"wand_fear", 5}, - {"wand_warp", 5}, - {"wand_lob", 5} - }, - - WANDS_HIGH = { - {"wand_lure", 5}, - {"wand_stayaway", 5}, - {"wand_transfer", 5}, - {"wand_vanish", 5} - }, - - TM_LOW = { - {'tm_snatch', 5}, - {'tm_sunny_day', 5}, - {'tm_rain_dance', 5}, - {'tm_sandstorm', 5}, - {'tm_hail', 5}, - {'tm_taunt', 5}, - - {'tm_safeguard', 5}, - {'tm_light_screen', 5}, - {'tm_dream_eater', 5}, - {'tm_nature_power', 5}, - {'tm_swagger', 5}, - {'tm_captivate', 5}, - {'tm_fling', 5}, - {'tm_payback', 5}, - {'tm_reflect', 5}, - {'tm_rock_polish', 5}, - {'tm_pluck', 5}, - {'tm_psych_up', 5}, - {'tm_secret_power', 5}, - - {'tm_return', 5}, - {'tm_frustration', 5}, - {'tm_torment', 5}, - {'tm_endure', 5}, - {'tm_echoed_voice', 5}, - {'tm_gyro_ball', 5}, - {'tm_recycle', 5}, - {'tm_false_swipe', 5}, - {'tm_defog', 5}, - {'tm_telekinesis', 5}, - {'tm_double_team', 5}, - {'tm_thunder_wave', 5}, - {'tm_attract', 5}, - {'tm_smack_down', 5}, - {'tm_snarl', 5}, - {'tm_flame_charge', 5}, - - {'tm_protect', 5}, - {'tm_round', 5}, - {'tm_rest', 5}, - {'tm_thief', 5}, - {'tm_cut', 5}, - {'tm_whirlpool', 5}, - {'tm_infestation', 5}, - {'tm_roar', 5}, - {'tm_flash', 5}, - {'tm_embargo', 5}, - {'tm_struggle_bug', 5}, - {'tm_quash', 5}}, - - TM_MID = { - {'tm_explosion', 5}, - {'tm_will_o_wisp', 5}, - {'tm_facade', 5}, - {'tm_water_pulse', 5}, - {'tm_shock_wave', 5}, - {'tm_brick_break', 5}, - {'tm_calm_mind', 5}, - {'tm_charge_beam', 5}, - {'tm_retaliate', 5}, - {'tm_roost', 5}, - {'tm_acrobatics', 5}, - {'tm_bulk_up', 5}, - - - {'tm_shadow_claw', 5}, - - {'tm_steel_wing', 5}, - {'tm_snarl', 5}, - {'tm_bulldoze', 5}, - {'tm_substitute', 5}, - {'tm_brine', 5}, - {'tm_venoshock', 5}, - {'tm_u_turn', 5}, - {'tm_aerial_ace', 5}, - {'tm_hone_claws', 5}, - {'tm_rock_smash', 5}, - - {'tm_hidden_power', 5}, - {'tm_rock_tomb', 5}, - {'tm_strength', 5}, - {'tm_grass_knot', 5}, - {'tm_power_up_punch', 5}, - {'tm_work_up', 5}, - {'tm_incinerate', 5}, - {'tm_bullet_seed', 5}, - {'tm_low_sweep', 5}, - {'tm_volt_switch', 5}, - {'tm_avalanche', 5}, - {'tm_dragon_tail', 5}, - {'tm_silver_wind', 5}, - {'tm_frost_breath', 5}, - {'tm_sky_drop', 5} - }, - - TM_HIGH = { - {'tm_earthquake', 5}, - {'tm_hyper_beam', 5}, - {'tm_overheat', 5}, - {'tm_blizzard', 5}, - {'tm_swords_dance', 5}, - {'tm_surf', 5}, - {'tm_dark_pulse', 5}, - {'tm_psychic', 5}, - {'tm_thunder', 5}, - {'tm_shadow_ball', 5}, - {'tm_ice_beam', 5}, - {'tm_giga_impact', 5}, - {'tm_fire_blast', 5}, - {'tm_dazzling_gleam', 5}, - {'tm_flash_cannon', 5}, - {'tm_stone_edge', 5}, - {'tm_sludge_bomb', 5}, - {'tm_focus_blast', 5}, - - {'tm_x_scissor', 5}, - {'tm_wild_charge', 5}, - {'tm_focus_punch', 5}, - {'tm_psyshock', 5}, - {'tm_rock_slide', 5}, - {'tm_thunderbolt', 5}, - {'tm_flamethrower', 5}, - {'tm_energy_ball', 5}, - {'tm_scald', 5}, - {'tm_waterfall', 5}, - {'tm_rock_climb', 5}, - - {'tm_giga_drain', 5}, - {'tm_dive', 5}, - {'tm_poison_jab', 5}, - - {'tm_iron_tail', 5}, - - {'tm_dig', 5}, - {'tm_fly', 5}, - {'tm_dragon_claw', 5}, - {'tm_dragon_pulse', 5}, - {'tm_sludge_wave', 5}, - {'tm_drain_punch', 5}}, - - --special and unique rewards, very rare - SPECIAL = { - {'medicine_amber_tear', 1}, - {'machine_ability_capsule', 1}, - {'ammo_golden_thorn', 1}, - {'food_apple_golden', 1}, - {'seed_golden', 1}, - {'evo_harmony_scarf', 1}, - {'apricorn_perfect', 1} - } -} - -MISSION_GEN.SPECIAL_CLIENT_RIVAL = "RIVAL" -MISSION_GEN.SPECIAL_CLIENT_CHILD = "CHILD" -MISSION_GEN.SPECIAL_CLIENT_LOVER = "LOVER" -MISSION_GEN.SPECIAL_CLIENT_FRIEND = "FRIEND" - - -MISSION_GEN.SPECIAL_CLIENT_OPTIONS = { - MISSION_GEN.SPECIAL_CLIENT_LOVER, - MISSION_GEN.SPECIAL_CLIENT_RIVAL, - MISSION_GEN.SPECIAL_CLIENT_CHILD, - MISSION_GEN.SPECIAL_CLIENT_FRIEND -} - ---Order matters for these! First is the client, second is the target ---Titles are random from a small list. Each pair has a unique body text however included in the data below. ---Number represents that mon's gender, 1 for male, 2 for female, 0 for genderless -MISSION_GEN.SPECIAL_LOVER_PAIRS = { - TIER_LOW = { - {'volbeat', 1, 'illumise', 2, "MISSION_BODY_SPECIAL_LOVER_001"}, - {'minun', 1, 'plusle', 2, "MISSION_BODY_SPECIAL_LOVER_002"}, - {'mareep', 2, 'wooloo', 1, "MISSION_BODY_SPECIAL_LOVER_003"}, - {'luvdisc', 2, 'luvdisc', 2, "MISSION_BODY_SPECIAL_LOVER_004"} - }, - TIER_MID = { - {'miltank', 2, 'tauros', 1, "MISSION_BODY_SPECIAL_LOVER_005"}, - {'venomoth', 1, 'butterfree', 2, "MISSION_BODY_SPECIAL_LOVER_006"}, - {'liepard', 2, 'persian', 1, "MISSION_BODY_SPECIAL_LOVER_007"}, - {'dustox', 1, 'beautifly', 2, "MISSION_BODY_SPECIAL_LOVER_008"}, - {'glalie', 1, 'froslass', 2, "MISSION_BODY_SPECIAL_LOVER_009"}, - {'ribombee', 2, 'masquerain', 1, "MISSION_BODY_SPECIAL_LOVER_010"}, - {'maractus', 2, 'cacturne', 1, "MISSION_BODY_SPECIAL_LOVER_011"},---my prickly love! - {'lanturn', 1, 'lumineon', 2, "MISSION_BODY_SPECIAL_LOVER_012"} - }, - TIER_HIGH = { - {'tyranitar', 1, 'altaria', 2, "MISSION_BODY_SPECIAL_LOVER_013"},--reference to an old idea i had - {'gyarados', 1, 'milotic', 2, "MISSION_BODY_SPECIAL_LOVER_014"}, - {'gardevoir', 2, 'gallade', 1, "MISSION_BODY_SPECIAL_LOVER_015"} - } - -} - -MISSION_GEN.SPECIAL_CHILD_PAIRS = { - TIER_LOW = { - {'clefable', 2, 'cleffa', 2, "MISSION_BODY_SPECIAL_CHILD_001"}, - {'wigglytuff', 1, 'igglybuff', 1, "MISSION_BODY_SPECIAL_CHILD_002"}, - {'togekiss', 2, 'togepi', 1, "MISSION_BODY_SPECIAL_CHILD_003"}, - {'roserade', 2, 'budew', 2, "MISSION_BODY_SPECIAL_CHILD_004"}, - {'chimecho', 2, 'chingling', 1, "MISSION_BODY_SPECIAL_CHILD_005"}, - {'sudowoodo', 1, 'bonsly', 1, "MISSION_BODY_SPECIAL_CHILD_006"}, - {'mr_mime', 1, 'mime_jr', 1, "MISSION_BODY_SPECIAL_CHILD_007"}, - {'raticate', 1, 'rattata', 2, "MISSION_BODY_SPECIAL_CHILD_008"},--hes still not so good at gnawing! - {'leavanny', 2, 'sewaddle', 2, "MISSION_BODY_SPECIAL_CHILD_009"} - }, - TIER_MID = { - {'appletun', 2, 'applin', 1, "MISSION_BODY_SPECIAL_CHILD_010"}, - {'aggron', 1, 'aron', 1, "MISSION_BODY_SPECIAL_CHILD_011"},--probably munched too much metal! - {'jynx', 2, 'smoochum', 2, "MISSION_BODY_SPECIAL_CHILD_012"}, - {'magmortar', 2, 'magby', 2, "MISSION_BODY_SPECIAL_CHILD_013"}, - {'electivire', 1, 'elekid', 1, "MISSION_BODY_SPECIAL_CHILD_014"}, - {'tsareena', 2, 'bounsweet', 2, "MISSION_BODY_SPECIAL_CHILD_015"}, - {'hatterene', 2, 'hatenna', 2, "MISSION_BODY_SPECIAL_CHILD_016"}, - {'gothitelle', 2, 'gothita', 2, "MISSION_BODY_SPECIAL_CHILD_017"}, - {'dugtrio', 1, 'diglett', 1, "MISSION_BODY_SPECIAL_CHILD_018"} - }, - TIER_HIGH = { - {'tyranitar', 2, 'larvitar', 1, "MISSION_BODY_SPECIAL_CHILD_019"}, - {'salamence', 1, 'bagon', 2, "MISSION_BODY_SPECIAL_CHILD_020"}, - {'dragonite', 2, 'dratini', 2, "MISSION_BODY_SPECIAL_CHILD_021"}, - {'noivern', 1, 'noibat', 1, "MISSION_BODY_SPECIAL_CHILD_022"}, - {'goodra', 2, 'goomy', 1, "MISSION_BODY_SPECIAL_CHILD_023"} - } - - - - -} - -MISSION_GEN.SPECIAL_FRIEND_PAIRS = { - TIER_LOW = { - {'applin', 1, 'cherubi', 2, "MISSION_BODY_SPECIAL_FRIEND_001"},--We both get mistaken for fruit! What if someone ate him!? - {'mantyke', 2, 'remoraid', 1, "MISSION_BODY_SPECIAL_FRIEND_002"},--My best friend is missing! I'll never be able to evolve without him! - {'magikarp', 1, 'feebas', 2, "MISSION_BODY_SPECIAL_FRIEND_003"},--feebas is the only one who understands what it's like to be dogshit! - {'poliwag', 2, 'lotad', 1, "MISSION_BODY_SPECIAL_FRIEND_004"},--frog and his lilypad. I have no lilypad now, save him! - {'teddiursa', 1, 'combee', 2, "MISSION_BODY_SPECIAL_FRIEND_005"}, --Without Combee, I have no honey! Please find them! - {'woobat', 2, 'zubat', 1, "MISSION_BODY_SPECIAL_FRIEND_006"},--we both use ultrasonic waves to see! - {'trubbish', 1, 'grimer', 1, "MISSION_BODY_SPECIAL_FRIEND_007"},--we both love eating garbage! - {'shroomish', 1, 'paras', 1, "MISSION_BODY_SPECIAL_FRIEND_008"},--we both love to spread spores! - {'chansey', 2, 'togepi', 2, "MISSION_BODY_SPECIAL_FRIEND_009"},--cares for togepi because its an egg - {'salandit', 1, 'combee', 1, "MISSION_BODY_SPECIAL_FRIEND_010"}--they relate in being useless - }, - TIER_MID = { - {'lunatone', 0, 'solrock', 0, "MISSION_BODY_SPECIAL_FRIEND_011"}, - {'emolga', 2, 'pachirisu', 2, "MISSION_BODY_SPECIAL_FRIEND_012"}, - {'spinda', 2, 'hypno', 1, "MISSION_BODY_SPECIAL_FRIEND_013"}, --Hypno went missing; only he can help stop my dizziness - {'cramorant', 1, 'pelipper', 2, "MISSION_BODY_SPECIAL_FRIEND_014"}, - {'magnemite', 0, 'nosepass', 1, "MISSION_BODY_SPECIAL_FRIEND_015"},--We're both sensitive to magnetism! - {'dustox', 1, 'lampent', 2, "MISSION_BODY_SPECIAL_FRIEND_016"} - }, - TIER_HIGH = { - {'lilligant', 2, 'kricketune', 1, "MISSION_BODY_SPECIAL_FRIEND_017"},--I can't dance without Kricketune's music! - {'wigglytuff', 2, 'exploud', 1, "MISSION_BODY_SPECIAL_FRIEND_018"}, --we love making loud, silly noises together! - {'beedrill', 1, 'florges', 2, "MISSION_BODY_SPECIAL_FRIEND_019"},--without my flower, i have no meaning! - {'dunsparce', 1, 'dugtrio', 1, "MISSION_BODY_SPECIAL_FRIEND_020"},--we both love to burrow! - {'whimsicott', 2, 'jumpluff', 2, "MISSION_BODY_SPECIAL_FRIEND_021"} - } -} - -MISSION_GEN.SPECIAL_RIVAL_PAIRS = { - TIER_LOW = { - {'koffing', 1, 'stunky', 2, "MISSION_BODY_SPECIAL_RIVAL_001"},--they compete to see whose odor is stronger - {'krabby', 1, 'corphish', 1, "MISSION_BODY_SPECIAL_RIVAL_002"},--compare claw strength - {'shuppet', 2, 'duskull', 1, "MISSION_BODY_SPECIAL_RIVAL_003"},--we like to see who can pull better pranks! - {'pidgey', 2, 'spearow', 2, "MISSION_BODY_SPECIAL_RIVAL_004"},--we compete at flying! - {'kabuto', 1, 'omanyte', 1, "MISSION_BODY_SPECIAL_RIVAL_005"}, --we've been rivals since our ancestors' time! - {'joltik', 2, 'spinarak', 1, "MISSION_BODY_SPECIAL_RIVAL_006"},--We like to see who can spin the better web! - {'tyrogue', 1, 'makuhita', 1, "MISSION_BODY_SPECIAL_RIVAL_007"},--my punching bag training partner! - {'lillipup', 2, 'poochyena', 1, "MISSION_BODY_SPECIAL_RIVAL_008"} - - }, - - TIER_MID = { - {'vigoroth', 1, 'primeape', 1, "MISSION_BODY_SPECIAL_RIVAL_009"},--full of energy! - {'sawsbuck', 1, 'stantler', 1, "MISSION_BODY_SPECIAL_RIVAL_010"},--butt antlers! - {'jangmo_o', 1, 'axew', 1, "MISSION_BODY_SPECIAL_RIVAL_011"}, - {'mareanie', 2, 'corsola', 2, "MISSION_BODY_SPECIAL_RIVAL_012"} - - }, - - TIER_HIGH = { - {'heracross', 1, 'pinsir', 1, "MISSION_BODY_SPECIAL_RIVAL_013"}, - {'slowking', 1, 'slowbro', 1, "MISSION_BODY_SPECIAL_RIVAL_014"},--slowbro may not be as smart as me, but we're still great friends! - {'magmortar', 2, 'electivire', 1, "MISSION_BODY_SPECIAL_RIVAL_015"}, --we need to settle who is stronger! - {'cradily', 2, 'armaldo', 1, "MISSION_BODY_SPECIAL_RIVAL_016"}, --we've been rivals since our ancestors' time! - {'bastiodon', 2, 'rampardos', 1, "MISSION_BODY_SPECIAL_RIVAL_017"}, --we've been rivals since our ancestors' time! - {'archeops', 1, 'aerodactyl', 2, "MISSION_BODY_SPECIAL_RIVAL_018"}, --we've been rivals since our ancestors' time! - {'swellow', 2, 'staraptor', 1, "MISSION_BODY_SPECIAL_RIVAL_019"}--brave birds! - - } -} - - - -MISSION_GEN.SPECIAL_OUTLAW = { - -} - - -MISSION_GEN.LOST_ITEMS = { - "mission_lost_scarf", - "mission_lost_specs", - "mission_lost_band" -} - -MISSION_GEN.STOLEN_ITEMS = { - "mission_stolen_scarf", - "mission_stolen_band", - "mission_stolen_specs" -} - -MISSION_GEN.DELIVERABLE_ITEMS = { - "berry_oran", - "berry_leppa", - "food_apple", - "berry_lum", - "apricorn_plain" -} - ---"order" of dungeons -SV.DungeonOrder = {} -SV.DungeonOrder[""] = 99999--empty missions should get shoved towards the end - - - ---Do the stairs go up or down? Blank string if up, B if down -SV.StairType = {} -SV.StairType[""] = "" - -function MISSION_GEN.GetStairsType(zone_id) - if SV.StairType[zone_id] ~= nil then - return SV.StairType[zone_id] - end - - return '' -end - - -function MISSION_GEN.MissionBoardIsEmpty() - for k, v in pairs(SV.MissionBoard) do - if v.Client ~= '' then - return false - end - end - - return true -end - - -function MISSION_GEN.TakenBoardIsEmpty() - for k, v in pairs(SV.TakenBoard) do - if v.Client ~= '' then - return false - end - end - - return true -end - -function MISSION_GEN.WeightedRandom(weights) - local summ = 0 - for i, value in pairs (weights) do - summ = summ + value[2] - end - if summ == 0 then return end - -- local value = math.random (summ) -- for integer weights only - local rand = summ*math.random () - summ = 0 - for i, value in pairs (weights) do - summ = summ + value[2] - if rand <= summ then - return value[1]--, weight - end - end -end - - -function MISSION_GEN.has_value (tab, val) - for index, value in ipairs(tab) do - if value == val then - return true - end - end - - return false -end - - - -function MISSION_GEN.ResetBoards() - SV.ExpectedLevel = {} - SV.DungeonOrder = {} - SV.StairType = {} - - --jobs on the mission board. - SV.MissionBoard = - { - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - } - - } - - --Jobs on the outlaw board. - SV.OutlawBoard = - { - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - } - } - -end - -function MISSION_GEN.GetDifficultyString(difficulty_val) - local star_icon = STRINGS:Format("\\uE10C") --star icon - local difficulty_string = "" - local difficulty_text_strings = {} - for str in string.gmatch(difficulty_val, "([^_]+)") do - table.insert(difficulty_text_strings, str) - end - - if #difficulty_text_strings == 1 then - difficulty_string = difficulty_text_strings[1] - elseif #difficulty_text_strings == 2 then - --STAR - difficulty_string = difficulty_text_strings[2] .. star_icon - end - - return difficulty_string -end - ---Generate a board. Board_type should be given as "Mission" or "Outlaw". ---Job/Outlaw Boards should be cleared before being regenerated. -function MISSION_GEN.GenerateBoard(result, board_type) - local jobs_to_make = 8 - local assigned_combos = {}--floor/dungeon combinations that already have had missions genned for it. Need to consider already genned missions and missions on taken board. - - -- All seen Pokemon in the pokedex - --local seen_pokemon = {} - - --for entry in luanet.each(_DATA.Save.Dex) do - -- if entry.Value == RogueEssence.Data.GameProgress.UnlockState.Discovered then - -- table.insert(seen_pokemon, entry.Key) - -- end - --end - - --print( seen_pokemon[ math.random( #seen_pokemon ) ] ) - - --default to mission. - local mission_type = COMMON.MISSION_BOARD_MISSION - if board_type == COMMON.MISSION_BOARD_OUTLAW then mission_type = COMMON.MISSION_BOARD_OUTLAW end - - --get list of potential dungeons for missions, remove any that haven't been completed yet. - local dungeon_candidates = {} - local dungeon_segments = {} - local dungeon_candidate_index_cur = 1 - local dungeon_difficulties = MISSION_GEN.ShallowCopy(MISSION_GEN.DIFFICULTY) - for dungeon_id, cur_dungeon_segments in pairs(SV.MissionPrereq.DungeonsCompleted) do - local dungeon_summary = _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Zone]:Get(dungeon_id) - local dungeon_segment_index_cur = 1 - if MISSION_GEN.DUNGEON_LIST[dungeon_id] ~= nil then - --Add the expected level - SV.ExpectedLevel[dungeon_id] = dungeon_summary.Level - dungeon_candidates[dungeon_candidate_index_cur] = dungeon_id - local default_dungeon_candidate_needed = true - for dungeon_segment, value in pairs(cur_dungeon_segments) do - if MISSION_GEN.DUNGEON_LIST[dungeon_id][dungeon_segment] ~= nil then - local cur_difficulty = MISSION_GEN.DUNGEON_LIST[dungeon_id][dungeon_segment] - - if dungeon_segments[dungeon_candidate_index_cur] == nil then - dungeon_segments[dungeon_candidate_index_cur] = {} - end - - if dungeon_summary:GetFloorCount(dungeon_segment) > 1 then - dungeon_segments[dungeon_candidate_index_cur][dungeon_segment_index_cur] = dungeon_segment - end - default_dungeon_candidate_needed = false - dungeon_segment_index_cur = dungeon_segment_index_cur + 1 - end - end - - if default_dungeon_candidate_needed == true then - if MISSION_GEN.DUNGEON_LIST[dungeon_id][0] ~= nil then - local cur_difficulty = MISSION_GEN.DUNGEON_LIST[dungeon_id][0] - - if dungeon_segments[dungeon_candidate_index_cur] == nil then - dungeon_segments[dungeon_candidate_index_cur] = {} - end - - if dungeon_summary:GetFloorCount(0) > 1 then - dungeon_segments[dungeon_candidate_index_cur][dungeon_segment_index_cur] = 0 - end - end - end - dungeon_candidate_index_cur = dungeon_candidate_index_cur + 1 - end - SV.DungeonOrder[dungeon_id] = dungeon_candidate_index_cur - SV.StairType[dungeon_id] = "" - end - - --failsafe. Just quit if no dungeons are eligible. - if #dungeon_candidates == 0 then return end - - --generate jobs - for i = 1, jobs_to_make, 1 do - --choose a dungeon, client, target, item, etc - local dungeon_candidate_index = math.random(1, #dungeon_candidates) - local dungeon = dungeon_candidates[dungeon_candidate_index] - local client = "" - local item = "" - local special = "" - local title = "Default title." - local flavor = "Default flavor text." - - - --Parse through segments for the dungeon - local possible_segments = dungeon_segments[dungeon_candidate_index] - - local segment = 0 --Default to 0 segment if no other valid one has been unlocked - - if #possible_segments > 0 then - segment = possible_segments[math.random(1, #possible_segments)] - end - - - --generate the objective. - local objective - local missionOutlawRoll = math.random(3) - if missionOutlawRoll == 1 then - --1 in 3 chance of outlaw - local roll = math.random(1, 10) - if roll <= 5 then - --5/10 chance of regular outlaw - objective = COMMON.MISSION_TYPE_OUTLAW - elseif roll <= 8 then - --3/10 chance of outlaw with an item - objective = COMMON.MISSION_TYPE_OUTLAW_ITEM - elseif roll <= 9 then - --1/10 chance of outlaw monster house - objective = COMMON.MISSION_TYPE_OUTLAW_MONSTER_HOUSE - else - --1/10 chance of outlaw fleeing - objective = COMMON.MISSION_TYPE_OUTLAW_FLEE - end - else - --2 in 3 chance of normal - local roll = math.random(1, 10) - if roll <= 2 then - if roll == 1 then - --1/10 chance of exploration (becomes regular rescue mission) - objective = COMMON.MISSION_TYPE_EXPLORATION - else - --1/10 chance of escort (becomes regular rescue mission) - objective = COMMON.MISSION_TYPE_ESCORT - end - elseif roll <= 3 then - --1/10 chance of delivery - objective = COMMON.MISSION_TYPE_DELIVERY - elseif roll <= 5 then - --2/10 chance of lost item - objective = COMMON.MISSION_TYPE_LOST_ITEM - else - --5/10 chance of a normal rescue mission - objective = COMMON.MISSION_TYPE_RESCUE - end - end - - if objective == COMMON.MISSION_TYPE_DELIVERY then - item = MISSION_GEN.DELIVERABLE_ITEMS[math.random(1, #MISSION_GEN.DELIVERABLE_ITEMS)] - elseif objective == COMMON.MISSION_TYPE_OUTLAW_ITEM then - item = MISSION_GEN.STOLEN_ITEMS[math.random(1, #MISSION_GEN.STOLEN_ITEMS)] - elseif objective == COMMON.MISSION_TYPE_LOST_ITEM then - item = MISSION_GEN.LOST_ITEMS[math.random(1, #MISSION_GEN.LOST_ITEMS)] - end - - - - --get the zone, and max floors (counted floors of relevant segments, excluding the first LoadGen floor) - local zoneEntry = _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Zone]:Get(dungeon) - local zone = _DATA:GetZone(dungeon) - - - local difficulty = MISSION_GEN.DUNGEON_LIST[dungeon][segment] - local offset = 0 - --up the difficulty by 1 if its an outlaw or escort mission. - local difficult_objectives = { COMMON.MISSION_TYPE_ESCORT, COMMON.MISSION_TYPE_EXPLORATION, COMMON.MISSION_TYPE_OUTLAW, COMMON.MISSION_TYPE_OUTLAW_FLEE, COMMON.MISSION_TYPE_OUTLAW_ITEM } - if COMMON.TableContains(difficult_objectives, objective) then - offset = 1 - --up the difficulty by 2 if its an outlaw monster house - elseif objective == COMMON.MISSION_TYPE_OUTLAW_MONSTER_HOUSE then - offset = 2 - end - - --up the difficulty in a subsegment of the dungeon - if segment > 0 then - offset = offset + 1 - end - - local final_order = MISSION_GEN.DIFF_TO_ORDER[difficulty]+offset - - if final_order >= #MISSION_GEN.ORDER_TO_DIFF then - final_order = #MISSION_GEN.ORDER_TO_DIFF - 1 - end - - difficulty = MISSION_GEN.ORDER_TO_DIFF[final_order] - - --Generate a tier, then the client - local tier = MISSION_GEN.WeightedRandom(MISSION_GEN.DIFF_POKEMON[difficulty]) - local client_candidates = MISSION_GEN.POKEMON[tier] - client = client_candidates[math.random(1, #client_candidates)] - - --50% chance that the client and target are the same. Target is the escort if its an escort mission. - --It is possible for this to roll the same target as the client again, which is fine. - --Always give a target if objective is escort or a outlaw stole an item. - --Target should always be client for - local target = client - local target_candidates = MISSION_GEN.POKEMON[tier] - if math.random(1, 2) == 1 or objective == COMMON.MISSION_TYPE_ESCORT or objective == COMMON.MISSION_TYPE_OUTLAW_ITEM then - target = target_candidates[math.random(1, #target_candidates)] - --print(target_candidates[1]) --to give an idea of what tier we rolled - end - - --if its a generic outlaw mission, or a monster house / fleeing outlaw, Magna is the client. Normal mons only ask you to go after their stolen items. - if objective == COMMON.MISSION_TYPE_OUTLAW or objective == COMMON.MISSION_TYPE_OUTLAW_FLEE or objective == COMMON.MISSION_TYPE_OUTLAW_MONSTER_HOUSE then - client = "magna" - end - - --if it's a delivery, exploration, or lost item, target and client should match. - if objective == COMMON.MISSION_TYPE_EXPLORATION or objective == COMMON.MISSION_TYPE_DELIVERY or objective == COMMON.MISSION_TYPE_LOST_ITEM then - target = client - end - - - --Reroll target if target is ghost and target is a fleeing outlaw, that shit would be too obnoxious to deal with - local target_type_1 = _DATA:GetMonster(target).Forms[0].Element1 - local target_type_2 = _DATA:GetMonster(target).Forms[0].Element2 - while objective == COMMON.MISSION_TYPE_OUTLAW_FLEE and (target_type_1 == "ghost" or target_type_2 == "ghost") do - print(target .. ": Rerolling cowardly ghost outlaw!!!") - target = target_candidates[math.random(1, #target_candidates)] - target_type_1 = _DATA:GetMonster(target).Forms[0].Element1 - target_type_2 = _DATA:GetMonster(target).Forms[0].Element2 - print("new target is " .. target) - end - - --Roll for genders. Use base form because it PROBABLY won't ever matter. - --because Scriptvars doesnt like saving genders instead of regular structures, use 1/2/0 for m/f/genderless respectively, and convert when needed - local client_gender - - local rand = nil - - if _ZONE.CurrentMap ~= nil and _ZONE.CurrentMap.Rand ~= nil then - rand = _ZONE.CurrentMap.Rand - else - rand = GAME.Rand - end - - if client == "magna" then --Magna is a special exception - client_gender = 0 - else - client_gender = _DATA:GetMonster(client).Forms[0]:RollGender(rand) - client_gender = COMMON.GenderToNum(client_gender) - end - - local target_gender = _DATA:GetMonster(target).Forms[0]:RollGender(rand) - - target_gender = COMMON.GenderToNum(target_gender) - - --Special cases - --Roll for the main 3 rescue special cases - if objective == COMMON.MISSION_TYPE_RESCUE and math.random(1, 10) <= 2 then - local special_candidates = {} - special = MISSION_GEN.SPECIAL_CLIENT_OPTIONS[math.random(1, #MISSION_GEN.SPECIAL_CLIENT_OPTIONS)] - if special == MISSION_GEN.SPECIAL_CLIENT_CHILD then - special_candidates = MISSION_GEN.SPECIAL_CHILD_PAIRS[tier] - elseif special == MISSION_GEN.SPECIAL_CLIENT_LOVER then - special_candidates = MISSION_GEN.SPECIAL_LOVER_PAIRS[tier] - elseif special == MISSION_GEN.SPECIAL_CLIENT_RIVAL then - special_candidates = MISSION_GEN.SPECIAL_RIVAL_PAIRS[tier] - elseif special == MISSION_GEN.SPECIAL_CLIENT_FRIEND then - special_candidates = MISSION_GEN.SPECIAL_FRIEND_PAIRS[tier] - end - - - --Set variables with special client/target info - local special_choice = special_candidates[math.random(1, #special_candidates)] - client = special_choice[1] - client_gender = special_choice[2] - target = special_choice[3] - target_gender = special_choice[4] - - local special_title_candidates = MISSION_GEN.TITLES[special] - title = RogueEssence.StringKey(special_title_candidates[math.random(1, #special_title_candidates)]):ToLocal() - - flavor = RogueEssence.StringKey(special_choice[5]):ToLocal() - - - end - - - - - --generate reward with hardcoded list of weighted rewards - local reward = "money" - --1/4 chance you get money instead of an item - - if math.random(1, 4) > 1 then - local reward_pool = MISSION_GEN.REWARDS[MISSION_GEN.WeightedRandom(MISSION_GEN.DIFF_REWARDS[difficulty])] - reward = MISSION_GEN.WeightedRandom(reward_pool) - end - - --1/3 chance you get a bonus reward. Bonus reward is always an item, never money - local bonus_reward = "" - - if math.random(1,3) == 1 then - local reward_pool = MISSION_GEN.REWARDS[MISSION_GEN.WeightedRandom(MISSION_GEN.DIFF_REWARDS[difficulty])] - bonus_reward = MISSION_GEN.WeightedRandom(reward_pool) - end - - --Choose a random title that's appropriate. - local title_candidates = {} - - if special == "" then -- get title if special didn't already generate it - if objective == COMMON.MISSION_TYPE_RESCUE and client ~= target then - title_candidates = MISSION_GEN.TITLES["RESCUE_FRIEND"] - elseif objective == COMMON.MISSION_TYPE_RESCUE and client == target then - title_candidates = MISSION_GEN.TITLES["RESCUE_SELF"] - elseif objective == COMMON.MISSION_TYPE_ESCORT then - title_candidates = MISSION_GEN.TITLES["ESCORT"] - elseif objective == COMMON.MISSION_TYPE_EXPLORATION then - title_candidates = MISSION_GEN.TITLES["EXPLORATION"] - elseif objective == COMMON.MISSION_TYPE_LOST_ITEM then - title_candidates = MISSION_GEN.TITLES["LOST_ITEM"] - elseif objective == COMMON.MISSION_TYPE_DELIVERY then - title_candidates = MISSION_GEN.TITLES["DELIVERY"] - elseif objective == COMMON.MISSION_TYPE_OUTLAW then - title_candidates = MISSION_GEN.TITLES["OUTLAW"] - elseif objective == COMMON.MISSION_TYPE_OUTLAW_ITEM then - title_candidates = MISSION_GEN.TITLES["OUTLAW_ITEM"] - elseif objective == COMMON.MISSION_TYPE_OUTLAW_MONSTER_HOUSE then - title_candidates = MISSION_GEN.TITLES["OUTLAW_MONSTER_HOUSE"] - elseif objective == COMMON.MISSION_TYPE_OUTLAW_FLEE then - title_candidates = MISSION_GEN.TITLES["OUTLAW_FLEE"] - end - title = RogueEssence.StringKey(title_candidates[math.random(1, #title_candidates)]):ToLocal() - - --string substitutions, if needed. - if string.find(title, "%[target%]") then - title = string.gsub(title, "%[target%]", _DATA:GetMonster(target):GetColoredName()) - end - - if string.find(title, "%[dungeon%]") then - title = string.gsub(title, "%[dungeon%]", _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Zone]:Get(dungeon):GetColoredName()) - end - - if string.find(title, "%[item%]") then - title = string.gsub(title, "%[item%]", _DATA:GetItem(item):GetColoredName()) - end - end - - - - --Flavor text generation - local flavor_top_candidates = {} - local flavor_bottom_candidates = {} - - if special == "" then -- get flavor if special didn't already generate it - if objective == COMMON.MISSION_TYPE_RESCUE and client ~= target then - flavor_top_candidates = MISSION_GEN.FLAVOR_TOP["RESCUE_FRIEND"] - flavor_bottom_candidates = MISSION_GEN.FLAVOR_BOTTOM["RESCUE_FRIEND"] - elseif objective == COMMON.MISSION_TYPE_RESCUE and client == target then - flavor_top_candidates = MISSION_GEN.FLAVOR_TOP["RESCUE_SELF"] - flavor_bottom_candidates = MISSION_GEN.FLAVOR_BOTTOM["RESCUE_SELF"] - elseif objective == COMMON.MISSION_TYPE_ESCORT then - flavor_top_candidates = MISSION_GEN.FLAVOR_TOP["ESCORT"] - flavor_bottom_candidates = MISSION_GEN.FLAVOR_BOTTOM["ESCORT"] - elseif objective == COMMON.MISSION_TYPE_EXPLORATION then - flavor_top_candidates = MISSION_GEN.FLAVOR_TOP["EXPLORATION"] - flavor_bottom_candidates = MISSION_GEN.FLAVOR_BOTTOM["EXPLORATION"] - elseif objective == COMMON.MISSION_TYPE_LOST_ITEM then - flavor_top_candidates = MISSION_GEN.FLAVOR_TOP["LOST_ITEM"] - flavor_bottom_candidates = MISSION_GEN.FLAVOR_BOTTOM["LOST_ITEM"] - elseif objective == COMMON.MISSION_TYPE_DELIVERY then - flavor_top_candidates = MISSION_GEN.FLAVOR_TOP["DELIVERY"] - flavor_bottom_candidates = MISSION_GEN.FLAVOR_BOTTOM["DELIVERY"] - elseif objective == COMMON.MISSION_TYPE_OUTLAW then - flavor_top_candidates = MISSION_GEN.FLAVOR_TOP["OUTLAW"] - flavor_bottom_candidates = MISSION_GEN.FLAVOR_BOTTOM["OUTLAW"] - elseif objective == COMMON.MISSION_TYPE_OUTLAW_ITEM then - flavor_top_candidates = MISSION_GEN.FLAVOR_TOP["OUTLAW_ITEM"] - flavor_bottom_candidates = MISSION_GEN.FLAVOR_BOTTOM["OUTLAW_ITEM"] - elseif objective == COMMON.MISSION_TYPE_OUTLAW_MONSTER_HOUSE then - flavor_top_candidates = MISSION_GEN.FLAVOR_TOP["OUTLAW_MONSTER_HOUSE"] - flavor_bottom_candidates = MISSION_GEN.FLAVOR_BOTTOM["OUTLAW_MONSTER_HOUSE"] - elseif objective == COMMON.MISSION_TYPE_OUTLAW_FLEE then - flavor_top_candidates = MISSION_GEN.FLAVOR_TOP["OUTLAW_FLEE"] - flavor_bottom_candidates = MISSION_GEN.FLAVOR_BOTTOM["OUTLAW_FLEE"] - end - flavor = RogueEssence.StringKey(flavor_top_candidates[math.random(1, #flavor_top_candidates)]):ToLocal() .. '\n' .. RogueEssence.StringKey(flavor_bottom_candidates[math.random(1, #flavor_bottom_candidates)]):ToLocal() - - --string substitutions, if needed. - if string.find(flavor, "%[target%]") then - flavor = string.gsub(flavor, "%[target%]", _DATA:GetMonster(target):GetColoredName()) - end - - if string.find(flavor, "%[dungeon%]") then - flavor = string.gsub(flavor, "%[dungeon%]", _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Zone]:Get(dungeon):GetColoredName()) - end - - if string.find(flavor, "%[item%]") then - flavor = string.gsub(flavor, "%[item%]", _DATA:GetItem(item):GetColoredName()) - end - - end - - - - - --mission floor should be in last 45% of the dungeon - --don't pick a floor that's already been chosen for another mission in a dungeon - --It's smart; it'll only randomly choose floors that haven't been used up yet. If all floors are used up that are possible, only then is the job thrown out. - local used_floors = {} - for j = 1, 8, 1 do - if SV.OutlawBoard[j].Zone == dungeon then - table.insert(used_floors, 1, SV.OutlawBoard[j].Floor) - end - if SV.MissionBoard[j].Zone == dungeon then - table.insert(used_floors, 1, SV.MissionBoard[j].Floor) - end - if SV.TakenBoard[j].Zone == dungeon then - table.insert(used_floors, 1, SV.TakenBoard[j].Floor) - end - end - - local current_segment = zone.Segments[segment] - local floor_count = current_segment.FloorCount - - local floor_candidates = {} - if segment == 0 then - --The first segment should be the default one the player enters - floor_candidates = MISSION_GEN.Generate_List_Range(math.floor(floor_count * .55), floor_count) - else - floor_candidates = MISSION_GEN.Generate_List_Range(1, floor_count) - end - MISSION_GEN.array_sub(used_floors, floor_candidates) - - local floor_candidates_length = #floor_candidates - local current_index = 0 - local noMissionFloors = COMMON.GetNoMissionFloors(current_segment) - local valid_floor_candidates = {} - - --Make sure that the dungeon floor added is valid - for i = 1, floor_candidates_length, 1 do - local cur_candidate = floor_candidates[i] - if noMissionFloors[cur_candidate] == nil then - local has_sidequest_on_floor = false - - --Make sure none of the sidequests (EscortSister, OutlawForest) take place in the same floor - for name, mission in pairs(SV.missions.Missions) do - if mission.DestZone == dungeon and mission.DestSegment == current_segment and mission.DestFloor == cur_candidate - 1 then - has_sidequest_on_floor = true - break - end - end - - if has_sidequest_on_floor == false then - table.insert(valid_floor_candidates, cur_candidate) - end - end - end - - local mission_floor = -1 - if #valid_floor_candidates > 0 then - mission_floor = valid_floor_candidates[math.random(1, #valid_floor_candidates)] - end - - if mission_floor == -1 then PrintInfo("Can't generate job for index "..i.." and difficulty "..difficulty..", no more floors available!") end - - --don't generate this particular job slot if no more are available for the dungeon. - if mission_floor ~= -1 then - if mission_type == COMMON.MISSION_BOARD_OUTLAW then - SV.OutlawBoard[i].Client = client - SV.OutlawBoard[i].Target = target - SV.OutlawBoard[i].Flavor = flavor - SV.OutlawBoard[i].Title = title - SV.OutlawBoard[i].Zone = dungeon - SV.OutlawBoard[i].Segment = segment - SV.OutlawBoard[i].Reward = reward - SV.OutlawBoard[i].Floor = mission_floor - SV.OutlawBoard[i].Type = objective - SV.OutlawBoard[i].Completion = MISSION_GEN.INCOMPLETE - SV.OutlawBoard[i].Taken = false - SV.OutlawBoard[i].Difficulty = difficulty - SV.OutlawBoard[i].Item = item - SV.OutlawBoard[i].Special = special - SV.OutlawBoard[i].ClientGender = client_gender - SV.OutlawBoard[i].TargetGender = target_gender - SV.OutlawBoard[i].BonusReward = bonus_reward - else - PrintInfo("Creating new mission for index "..i.." with client "..client.." difficulty "..difficulty.." title "..title.." and dungeon "..dungeon.." and segment "..segment.." and floor "..mission_floor) - SV.MissionBoard[i].Client = client - SV.MissionBoard[i].Target = target - SV.MissionBoard[i].Flavor = flavor - SV.MissionBoard[i].Title = title - SV.MissionBoard[i].Zone = dungeon - SV.MissionBoard[i].Segment = segment - SV.MissionBoard[i].Reward = reward - SV.MissionBoard[i].Floor = mission_floor - SV.MissionBoard[i].Type = objective - SV.MissionBoard[i].Completion = MISSION_GEN.INCOMPLETE - SV.MissionBoard[i].Taken = false - SV.MissionBoard[i].Difficulty = difficulty - SV.MissionBoard[i].Item = item - SV.MissionBoard[i].Special = special - SV.MissionBoard[i].ClientGender = client_gender - SV.MissionBoard[i].TargetGender = target_gender - SV.MissionBoard[i].BonusReward = bonus_reward - end - end - - end - -end - -function MISSION_GEN.GetJobExpReward(difficulty) - return MISSION_GEN.DIFFICULTY[difficulty] -end - -function MISSION_GEN.JobSortFunction(j1, j2) - if (j2 == nil or j2.Zone == nil or j2.Zone == "") then - return false - end - if (j1 == nil or j1.Zone == nil or j1.Zone == "") then - return true - end - --if they're the same dungeon, then check floors. Otherwise, dungeon order takes presidence. - if SV.DungeonOrder[j1.Zone] == SV.DungeonOrder[j2.Zone] then - return j1.Floor > j2.Floor - else - return SV.DungeonOrder[j1.Zone] > SV.DungeonOrder[j2.Zone] - end -end - ---used to get the minus of one list minus another list -function MISSION_GEN.array_sub(t1, t2) - local t = {} - for i = 1, #t1 do - t[t1[i]] = true; - end - for i = #t2, 1, -1 do - if t[t2[i]] then - table.remove(t2, i); - end - end -end - ---used to get an array of a range. For figuring out floor candidates -function MISSION_GEN.Generate_List_Range(low, up) - local array = {} - local count = 1 - for i = low, up, 1 do - array[count] = i - count = count + 1 - end - return array -end - -function MISSION_GEN.SortTaken() - if #SV.TakenBoard > 1 then - table.sort(SV.TakenBoard, MISSION_GEN.JobSortFunction) - end -end - -function MISSION_GEN.SortMission() - if #SV.MissionBoard > 1 then - table.sort(SV.MissionBoard, MISSION_GEN.JobSortFunction) - end -end - -function MISSION_GEN.SortOutlaw() - if #SV.OutlawBoard > 1 then - table.sort(SV.OutlawBoard, MISSION_GEN.JobSortFunction) - end -end - -function MISSION_GEN.IsBoardFull(board) - for i=#board, 1, -1 do - if board[i].Client == "" then - return false - end - end - - return true -end - ---Finds the next free index in the board, returns -1 if it can't be found -function MISSION_GEN.FindFreeSpaceInBoard(board) - for i=#board, 1, -1 do - if board[i].Client == "" then - return i - end - end - - return -1 -end - ---Used to create a shallow copy of a provided table (mainly for taking jobs) -function MISSION_GEN.ShallowCopy(orig) - local orig_type = type(orig) - local copy - if orig_type == 'table' then - copy = {} - for orig_key, orig_value in pairs(orig) do - copy[orig_key] = orig_value - end - else -- number, string, boolean, etc - copy = orig - end - return copy -end - - -JobMenu = Class('JobMenu') - ---jobs is a job board ---job type should be taken, mission, or outlaw ---job number should be 1-8 -function JobMenu:initialize(job_type, job_number, parent_board_menu) - assert(self, "JobMenu:initialize(): Error, self is nil!") - self.menu = RogueEssence.Menu.ScriptableMenu(32, 32, 256, 176, function(input) self:Update(input) end) - --self.menu.Elements:Add(RogueEssence.Menu.MenuText(jobs[i], RogueElements.Loc(16, 8 + 14 * (i-1)))) - - local job - - self.job_number = job_number - - self.job_type = job_type - - self.parent_board_menu = parent_board_menu - - --get relevant board - local job - if job_type == COMMON.MISSION_BOARD_TAKEN then - job = SV.TakenBoard[job_number] - elseif job_type == COMMON.MISSION_BOARD_OUTLAW then - job = SV.OutlawBoard[job_number] - else --default to mission board - job = SV.MissionBoard[job_number] - end - - self.taken = job.Taken - - self.flavor = job.Flavor - --Magna is the only non-species name that'll show up here. So he is hardcoded in as an exception here. - --TODO: Unhardcode this by adding in a check if string is not empty and if its not a species name, then add the color coding around it for proper names. - self.client = "" - if job.Client == 'magna' then - self.client = '[color=#00FFFF]Magna[color]' - elseif job.Client ~= "" then - self.client = _DATA:GetMonster(job.Client):GetColoredName() - end - - self.target = "" - if job.Target ~= '' then self.target = _DATA:GetMonster(job.Target):GetColoredName() end - - self.item = "" - if job.Item ~= '' then self.item = _DATA:GetItem(job.Item):GetColoredName() end - - self.objective = "" - self.type = job.Type - self.taken_count = MISSION_GEN.GetTakenCount() - - if self.type == COMMON.MISSION_TYPE_RESCUE then - self.objective = Text.FormatKey("MISSION_OBJECTIVES_RESCUE", self.target) - elseif self.type == COMMON.MISSION_TYPE_ESCORT then - self.objective = Text.FormatKey("MISSION_OBJECTIVES_ESCORT", self.client, self.target) - elseif self.type == COMMON.MISSION_TYPE_EXPLORATION then - self.objective = Text.FormatKey("MISSION_OBJECTIVES_EXPLORATION", self.client) - elseif self.type == COMMON.MISSION_TYPE_OUTLAW then - self.objective = Text.FormatKey("MISSION_OBJECTIVES_OUTLAW", self.target) - elseif self.type == COMMON.MISSION_TYPE_OUTLAW_FLEE then - self.objective = Text.FormatKey("MISSION_OBJECTIVES_OUTLAW_FLEE", self.target) - elseif self.type == COMMON.MISSION_TYPE_OUTLAW_MONSTER_HOUSE then - self.objective = Text.FormatKey("MISSION_OBJECTIVES_OUTLAW_MONSTER_HOUSE", self.target) - elseif self.type == COMMON.MISSION_TYPE_LOST_ITEM then - self.objective = Text.FormatKey("MISSION_OBJECTIVES_LOST_ITEM", self.item, self.client) - elseif self.type == COMMON.MISSION_TYPE_DELIVERY then - self.objective = Text.FormatKey("MISSION_OBJECTIVES_DELIVERY", self.item, self.client) - elseif self.type == COMMON.MISSION_TYPE_OUTLAW_ITEM then - self.objective = Text.FormatKey("MISSION_OBJECTIVES_OUTLAW_ITEM", self.item, self.target) - end - - - self.zone = "" - self.zone_name = "" - if job.Zone ~= "" then - local zone_string = _DATA:GetZone(job.Zone).Segments[job.Segment]:ToString() - zone_string = COMMON.CreateColoredSegmentString(zone_string) - self.zone = zone_string - self.zone_name = job.Zone - end - - self.floor = "" - if job.Floor ~= -1 then self.floor = MISSION_GEN.GetStairsType(job.Zone) .. '[color=#00FFFF]' .. tostring(job.Floor) .. "[color]F" end - - self.difficulty = "" - if job.Difficulty ~= "" then self.difficulty = MISSION_GEN.DIFF_TO_COLOR[job.Difficulty] .. MISSION_GEN.GetDifficultyString(job.Difficulty) .. "[color] - " .. Text.FormatKey("MISSION_EXP_DESCRIPTION", tostring(MISSION_GEN.DIFFICULTY[job.Difficulty])) end - - - - - self.reward = "" - if job.Reward ~= '' then - --special case for money - if job.Reward == "money" then - self.reward = '[color=#00FFFF]' .. MISSION_GEN.DIFF_TO_MONEY[job.Difficulty] .. '[color]' .. STRINGS:Format("\\uE024") - else - local reward_amount = 1 - --Reward amount should be 3 for multi-stack items - if RogueEssence.Data.DataManager.Instance:GetItem(job.Reward).MaxStack >= 3 then - reward_amount = 3 - end - self.reward = RogueEssence.Dungeon.InvItem(job.Reward, false, reward_amount):GetDisplayName() - end - end - - --add in the ??? for a bonus reward if one exists - if job.BonusReward ~= "" then - self.reward = self.reward .. ' + ?' - end - - - self:DrawJob() - - -end - -function JobMenu:DrawJob() - --Standard menu divider. Reuse this whenever you need a menu divider at the top for a title. - self.menu.Elements:Add(RogueEssence.Menu.MenuDivider(RogueElements.Loc(8, 8 + 12), self.menu.Bounds.Width - 8 * 2)) - - --Standard title. Reuse this whenever a title is needed. - self.menu.Elements:Add(RogueEssence.Menu.MenuText(Text.FormatKey("MISSION_JOB_SUMMARY"), RogueElements.Loc(16, 8))) - - --Accepted element - self.menu.Elements:Add(RogueEssence.Menu.MenuDivider(RogueElements.Loc(8, self.menu.Bounds.Height - 24), self.menu.Bounds.Width - 8 * 2)) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(Text.FormatKey("MISSION_JOB_ACCEPTED") .. self.taken_count .. "/8", RogueElements.Loc(96, self.menu.Bounds.Height - 20))) - - - - self.menu.Elements:Add(RogueEssence.Menu.MenuText(self.flavor, RogueElements.Loc(16, 24))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(Text.FormatKey("MISSION_JOB_CLIENT"), RogueElements.Loc(16, 54))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(Text.FormatKey("MISSION_JOB_OBJECTIVE"), RogueElements.Loc(16, 68))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(Text.FormatKey("MISSION_JOB_PLACE"), RogueElements.Loc(16, 82))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(Text.FormatKey("MISSION_JOB_DIFFICULTY"), RogueElements.Loc(16, 96))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(Text.FormatKey("MISSION_JOB_REWARD"), RogueElements.Loc(16, 110))) - - local client = self.client - client = string.gsub(client, "Magna", "Magnezone") - - self.menu.Elements:Add(RogueEssence.Menu.MenuText(client, RogueElements.Loc(68, 54))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(self.objective, RogueElements.Loc(68, 68))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(self.zone .. " " .. self.floor, RogueElements.Loc(68, 82))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(self.difficulty, RogueElements.Loc(68, 96))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(self.reward, RogueElements.Loc(68, 110))) -end - - - ---for use with submenu -function JobMenu:DeleteJob() - local mission = SV.TakenBoard[self.job_number] - local back_ref = mission.BackReference - PrintInfo("Restoring taken from backref "..back_ref) - if back_ref > 0 and back_ref ~= nil then - local outlaw_arr = { - COMMON.MISSION_TYPE_OUTLAW, - COMMON.MISSION_TYPE_OUTLAW_ITEM, - COMMON.MISSION_TYPE_OUTLAW_FLEE, - COMMON.MISSION_TYPE_OUTLAW_MONSTER_HOUSE - } - - --Outlaw missions are now part of the normal board - SV.MissionBoard[back_ref].Taken = false - end - - SV.TakenBoard[self.job_number] = { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - } - - MISSION_GEN.SortTaken() - if self.parent_board_menu ~= nil then - --redraw board with potentially changed information from job board - self.parent_board_menu.menu.Elements:Clear() - self.parent_board_menu:RefreshSelf() - self.parent_board_menu:DrawBoard() - - --redraw selection board with potentially changed information - if self.parent_board_menu.parent_selection_menu ~= nil then - self.parent_board_menu.parent_selection_menu.menu.Elements:Clear() - self.parent_board_menu.parent_selection_menu:DrawMenu() - end - - end - _MENU:RemoveMenu() - - --If we accessed the job via the main menu, then close the main menu if we've deleted our last job. Only need it here because only on total job deletion should the main menu ever need to change. - if self.parent_board_menu.parent_main_menu ~= nil then - if self.taken_count == 1 then--1 instead of 0 as the taken_count of the last job that was just deleted would be 1 - _MENU:RemoveMenu() - end - end - -end - ---for use with submenu ---flips taken status of self, and also updates the appropriate SV var's taken value -function JobMenu:FlipTakenStatus() - self.taken = not self.taken - if self.job_type == COMMON.MISSION_BOARD_TAKEN then - SV.TakenBoard[self.job_number].Taken = self.taken - elseif self.job_type == COMMON.MISSION_BOARD_OUTLAW then - SV.OutlawBoard[self.job_number].Taken = self.taken - else - SV.MissionBoard[self.job_number].Taken = self.taken - end - if self.parent_board_menu ~= nil then - --redraw board with potentially changed information from job board - self.parent_board_menu.menu.Elements:Clear() - self.parent_board_menu:RefreshSelf() - self.parent_board_menu:DrawBoard() - end -end - ---for use with submenu ---adds the current job to the taken board, then sorts it. Then close the menu -function JobMenu:AddJobToTaken() - --find an empty job slot - local freeIndex = MISSION_GEN.FindFreeSpaceInBoard(SV.TakenBoard) - if freeIndex > -1 then - if self.job_type == COMMON.MISSION_BOARD_OUTLAW then - --Need to copy the table rather than just pass the pointer, or you can dupe missions which is not good - SV.TakenBoard[freeIndex] = MISSION_GEN.ShallowCopy(SV.OutlawBoard[self.job_number]) - elseif self.job_type == COMMON.MISSION_BOARD_MISSION then - SV.TakenBoard[freeIndex] = MISSION_GEN.ShallowCopy(SV.MissionBoard[self.job_number]) - end - - SV.TakenBoard[freeIndex].BackReference = self.job_number - - --Suspend the job if there is currently an active sidequest in that dungeon - if COMMON.HasSidequestInZone(SV.TakenBoard[freeIndex].Zone) then - SV.TakenBoard[freeIndex].Taken = false - end - - MISSION_GEN.SortTaken() - end - - if self.parent_board_menu ~= nil then - --redraw board with potentially changed information from job board - self.parent_board_menu.menu.Elements:Clear() - self.parent_board_menu:RefreshSelf() - self.parent_board_menu:DrawBoard() - - --redraw selection board with potentially changed information - if self.parent_board_menu.parent_selection_menu ~= nil then - self.parent_board_menu.parent_selection_menu.menu.Elements:Clear() - self.parent_board_menu.parent_selection_menu:DrawMenu() - end - end - - - _MENU:RemoveMenu() -end - -function JobMenu:OpenSubMenu() - if self.job_type ~= COMMON.MISSION_BOARD_TAKEN and self.taken then - --This is a job from the board that was already taken! - else - --create prompt menu - local choices = {} - --print(self.job_type .. " taken: " .. tostring(self.taken)) - if self.job_type == COMMON.MISSION_BOARD_TAKEN then - local choice_str = Text.FormatKey("MISSION_BOARD_TAKE_JOB") - local take_job = true - if self.taken then - choice_str = Text.FormatKey("MISSION_BOARD_SUSPEND") - take_job = false - end - choices = { {choice_str, not take_job or not COMMON.HasSidequestInZone(self.zone_name), function() self:FlipTakenStatus() _MENU:RemoveMenu() _MENU:RemoveMenu() end}, - {Text.FormatKey("MISSION_BOARD_DELETE"), true, function() self:DeleteJob() _MENU:RemoveMenu() end}, - {Text.FormatKey("MISSION_BOARD_CANCEL"), true, function() _MENU:RemoveMenu() _MENU:RemoveMenu() end} } - - else --outlaw/mission boards - --we already made a check above to see if this is a job board and not taken - --only selectable if there's room on the taken board for the job, there is no sidequest for the dungeon, and we haven't already taken this mission - - choices = {{Text.FormatKey("MISSION_BOARD_TAKE_JOB"), MISSION_GEN.IsBoardFull(SV.TakenBoard) == false and not self.taken, function() self:FlipTakenStatus() - self:AddJobToTaken() _MENU:RemoveMenu() end }, - {Text.FormatKey("MISSION_BOARD_CANCEL"), true, function() _MENU:RemoveMenu() _MENU:RemoveMenu() end} } - end - - submenu = RogueEssence.Menu.ScriptableSingleStripMenu(232, 138, 24, choices, 0, function() _MENU:RemoveMenu() _MENU:RemoveMenu() end) - _MENU:AddMenu(submenu, true) - - end -end - -function JobMenu:Update(input) - assert(self, "BaseState:Begin(): Error, self is nil!") - if input:JustPressed(RogueEssence.FrameInput.InputType.Confirm) then - if self.job_type ~= COMMON.MISSION_BOARD_TAKEN and self.taken then - --This is a job from the board that was already taken! Play a cancel noise. - _GAME:SE("Menu/Cancel") - else - --This job has not yet been taken. This block will never be hit because the submenu will automatically open. - end - elseif input:JustPressed(RogueEssence.FrameInput.InputType.Cancel) or input:JustPressed(RogueEssence.FrameInput.InputType.Menu) then - _GAME:SE("Menu/Cancel") - _MENU:RemoveMenu() - --open job menu for that particular job - else - - end -end - - - -BoardMenu = Class('BoardMenu') - ---board type should be taken, mission, or outlaw -function BoardMenu:initialize(board_type, parent_selection_menu, parent_main_menu) - assert(self, "BoardMenu:initialize(): Error, self is nil!") - - self.menu = RogueEssence.Menu.ScriptableMenu(32, 32, 256, 176, function(input) self:Update(input) end) - self.cursor = RogueEssence.Menu.MenuCursor(self.menu) - - self.board_type = board_type - - --For refreshing the parent selection menu - self.parent_selection_menu = parent_selection_menu - - --for refreshing the main menu (esc menu) if we accessed the board menu via that - self.parent_main_menu = parent_main_menu - - local source_board = SV.MissionBoard - - print("Debug: Refreshing self!") - if self.board_type == COMMON.MISSION_BOARD_TAKEN then - source_board = SV.TakenBoard - elseif self.board_type == COMMON.MISSION_BOARD_OUTLAW then - source_board = SV.OutlawBoard - end - print("Boardtype: " .. self.board_type) - - self.total_items = 0 - self.jobs = {} - self.original_menu_index = {} - - --get total job count and add jobs to self.jobs - for i = #source_board, 1, -1 do - if source_board[i].Client ~= "" then - self.total_items = self.total_items + 1 - self.jobs[self.total_items] = source_board[i] - self.original_menu_index[self.total_items] = i - end - end - - self.current_item = 0 - self.cursor.Loc = RogueElements.Loc(9, 27) - self.page = 1--1 or 2 - self.taken_count = MISSION_GEN.GetTakenCount() - self.total_pages = math.ceil(self.total_items / 4) - - - self:DrawBoard() - -end - ---refresh information from results of job menu -function BoardMenu:RefreshSelf() - local source_board = SV.MissionBoard - - if self.board_type == COMMON.MISSION_BOARD_TAKEN then - source_board = SV.TakenBoard - elseif self.board_type == COMMON.MISSION_BOARD_OUTLAW then - source_board = SV.OutlawBoard - end - PrintInfo("Boardtype: " .. self.board_type) - - self.total_items = 0 - self.jobs = {} - self.original_menu_index = {} - - --get total job count and add jobs to self.jobs - for i = #source_board, 1, -1 do - if source_board[i].Client ~= "" then - self.total_items = self.total_items + 1 - self.jobs[self.total_items] = source_board[i] - self.original_menu_index[self.total_items] = i - - local taken_string = 'false' - - if source_board[i].Taken then - taken_string = 'true' - end - PrintInfo("Debug: Refreshing from source board index "..i) - end - end - - --in the event of deleting the last item on the board, move the cursor to accomodate. - if self:GetSelectedJobIndex() > self.total_items then - print("On refresh self, needed to adjust current item!") - self.current_item = (self.total_items - 1) % 4 - - --move cursor to reflect new current item location - self.cursor:ResetTimeOffset() - self.cursor.Loc = RogueElements.Loc(9, 27 + 28 * self.current_item) - end - - self.total_pages = math.ceil(self.total_items / 4) - - --go to page 1 if we now only have 1 page - if self.page == 2 and self.total_pages == 1 then - self.page = 1 - end - - --refresh taken count - self.taken_count = MISSION_GEN.GetTakenCount() - - --if there are no more missions and we're on the taken screen, close the menu. - if MISSION_GEN.TakenBoardIsEmpty() and self.board_type == COMMON.MISSION_BOARD_TAKEN then - _MENU:RemoveMenu() - end -end - - ---NOTE: Board is hardcoded to have 4 items a page, and only to have up to 8 total items to display. ---If you want to edit this, you'll probably have to change most instances of the number 4 here and some references to page. Sorry! -function BoardMenu:DrawBoard() - --Standard menu divider. Reuse this whenever you need a menu divider at the top for a title. - self.menu.Elements:Add(RogueEssence.Menu.MenuDivider(RogueElements.Loc(8, 8 + 12), self.menu.Bounds.Width - 8 * 2)) - - --Standard title. Reuse this whenever a title is needed. - self.menu.Elements:Add(RogueEssence.Menu.MenuText(Text.FormatKey("MISSION_BOARD_NOTICE"), RogueElements.Loc(16, 8))) - - --page element - self.menu.Elements:Add(RogueEssence.Menu.MenuText("(" .. tostring(self.page) .. "/" .. tostring(self.total_pages) .. ")", RogueElements.Loc(self.menu.Bounds.Width - 35, 8))) - - - --Accepted element - self.menu.Elements:Add(RogueEssence.Menu.MenuDivider(RogueElements.Loc(8, self.menu.Bounds.Height - 24), self.menu.Bounds.Width - 8 * 2)) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(Text.FormatKey("MISSION_BOARD_ACCEPTED") .. tostring(self.taken_count) .. "/8", RogueElements.Loc(96, self.menu.Bounds.Height - 20))) - - - self.menu.Elements:Add(self.cursor) - - --populate 4 self.jobs on a page - for i = (4 * self.page) - 3, 4 * self.page, 1 do - if i > #self.jobs then break end - - if self.jobs[i].Client ~= "" then - local title = self.jobs[i].Title - - local taken_string = 'false' - - if self.jobs[i].Taken then - taken_string = 'true' - end - - local zone = _DATA:GetZone(self.jobs[i].Zone).Segments[self.jobs[i].Segment]:ToString() - zone = COMMON.CreateColoredSegmentString(zone) - local floor = MISSION_GEN.GetStairsType(self.jobs[i].Zone) ..'[color=#00FFFF]' .. tostring(self.jobs[i].Floor) .. "[color]F" - local difficulty = "" - - --create difficulty string - local difficult_string = MISSION_GEN.GetDifficultyString(self.jobs[i].Difficulty) - - difficulty = MISSION_GEN.DIFF_TO_COLOR[self.jobs[i].Difficulty] .. difficult_string .. "[color]" - - local icon = "" - if self.board_type == COMMON.MISSION_BOARD_TAKEN then - if self.jobs[i].Taken then - icon = STRINGS:Format("\\uE10F")--open letter - else - icon = STRINGS:Format("\\uE10E")--closed letter - end - else - if self.jobs[i].Taken then - icon = STRINGS:Format("\\uE10E")--closed letter - else - icon = STRINGS:Format("\\uE110")--paper - end - end - - local location = zone .. " " .. floor - - - --color everything red if job is taken and this is a job board - if self.jobs[i].Taken and self.board_type ~= COMMON.MISSION_BOARD_TAKEN then - location = string.gsub(location, '%b[]', '') - title = string.gsub(title, '%b[]', '') - difficulty = string.gsub(difficulty, '%b[]', '') - - difficulty = "[color=#FF0000]" .. difficulty .. "[color]" - title = "[color=#FF0000]" .. title .. "[color]" - location = "[color=#FF0000]" .. location .. "[color]" - end - - --modulo the iterator so that if we're on the 2nd page it goes to the right spot - - self.menu.Elements:Add(RogueEssence.Menu.MenuText(icon, RogueElements.Loc(21, 26 + 28 * ((i-1) % 4)))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(title, RogueElements.Loc(33, 26 + 28 * ((i-1) % 4)))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(location, RogueElements.Loc(33, 38 + 28 * ((i-1) % 4)))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(difficulty, RogueElements.Loc(self.menu.Bounds.Width - 33, 38 + 28 * ((i-1) % 4)))) - end - end -end - - -function BoardMenu:Update(input) - assert(self, "BaseState:Begin(): Error, self is nil!") - if input:JustPressed(RogueEssence.FrameInput.InputType.Confirm) then - --open the selected job menu - _GAME:SE("Menu/Confirm") - local job_menu = JobMenu:new(self.board_type, self:GetOriginalSelectedJobIndex(), self) - _MENU:AddMenu(job_menu.menu, false) - job_menu:OpenSubMenu() - elseif input:JustPressed(RogueEssence.FrameInput.InputType.Cancel) or input:JustPressed(RogueEssence.FrameInput.InputType.Menu) then - _GAME:SE("Menu/Cancel") - _MENU:RemoveMenu() - --open job menu for that particular job - else - moved = false - if RogueEssence.Menu.InteractableMenu.IsInputting(input, LUA_ENGINE:MakeLuaArray(Dir8, { Dir8.Down, Dir8.DownLeft, Dir8.DownRight })) then - moved = true - self.current_item = (self.current_item + 1) % 4 - - --if we try to move the cursor to an empty slot on a down press, then move it to the space for the first job on the page. - if self:GetSelectedJobIndex() > self.total_items then - local new_current = 0 - --undo moved flag if we didn't actually move - if new_current == (self.current_item - 1) % 4 then - moved = false - end - self.current_item = new_current - end - - elseif RogueEssence.Menu.InteractableMenu.IsInputting(input, LUA_ENGINE:MakeLuaArray(Dir8, { Dir8.Up, Dir8.UpLeft, Dir8.UpRight })) then - moved = true - self.current_item = (self.current_item - 1) % 4 - - --if we try to move the cursor to an empty slot on an up press, then move it to the space for the last job on the page. - if self:GetSelectedJobIndex() > self.total_items then - local new_current = (self.total_items % 4) - 1 - --undo moved flag if we didn't actually move - if new_current == (self.current_item + 1 ) % 4 then - moved = false - end - self.current_item = new_current - end - - elseif RogueEssence.Menu.InteractableMenu.IsInputting(input, LUA_ENGINE:MakeLuaArray(Dir8, {Dir8.Left, Dir8.Right})) then - --go to other menu if there are more options on the 2nd menu - if self.total_pages > 1 then - --change the page - if self.page == 1 then self.page = 2 else self.page = 1 end - moved = true - - --if we try to move the cursor to an empty slot on a side press, then move it to the space for the last job on the page. - if self:GetSelectedJobIndex() > self.total_items then - local new_current = (self.total_items % 4) - 1 - self.current_item = new_current - end - - - self.menu.Elements:Clear() - self:DrawBoard() - end - end - if moved then - _GAME:SE("Menu/Select") - self.cursor:ResetTimeOffset() - self.cursor.Loc = RogueElements.Loc(9, 27 + 28 * self.current_item) - end - end -end - ---gets current job index based on the current item and the page. if self.page is 2, and current item is 0, returned answer should be 5. -function BoardMenu:GetSelectedJobIndex() - return self.current_item + (4 * (self.page - 1) + 1) -end - ---gets current job index based on the current item and the page, then translates it to the correct index in its original menu -function BoardMenu:GetOriginalSelectedJobIndex() - return self.original_menu_index[self:GetSelectedJobIndex()] -end - - - - - - - ------------------------- --- Board Selection Menu ------------------------- -BoardSelectionMenu = Class('BoardSelectionMenu') - ---Used to choose between viewing the board, your job list, or to cancel -function BoardSelectionMenu:initialize(board_type) - assert(self, "BoardSelectionMenu:initialize(): Error, self is nil!") - - --I'm bad at this. Need different menu sizes depending on the board - if board_type == COMMON.MISSION_BOARD_OUTLAW then - self.menu = RogueEssence.Menu.ScriptableMenu(24, 22, 128, 60, function(input) self:Update(input) end) - else - self.menu = RogueEssence.Menu.ScriptableMenu(24, 22, 119, 60, function(input) self:Update(input) end) - - end - self.cursor = RogueEssence.Menu.MenuCursor(self.menu) - self.board_type = board_type - - self.current_item = 0 - self.cursor.Loc = RogueElements.Loc(9, 8) - - self:DrawMenu() - -end - ---refreshes information and draws to the menu. This is important in case there's a change to the taken board -function BoardSelectionMenu:DrawMenu() - - --color this red if there's no jobs and mark there's no jobs to view. - self.board_populated = true - local board_name = "" - if self.board_type == COMMON.MISSION_BOARD_OUTLAW then - if SV.OutlawBoard[1].Client == '' then - board_name = "[color=#FF0000]"..Text.FormatKey("MISSION_BOARD_NAME_OUTLAW").."[color]" - self.board_populated = false - else - board_name = Text.FormatKey("MISSION_BOARD_NAME_OUTLAW") - end - else - if MISSION_GEN.MissionBoardIsEmpty() then - board_name = "[color=#FF0000]"..Text.FormatKey("MISSION_BOARD_NAME_MISSION").."[color]" - self.board_populated = false - else - board_name = Text.FormatKey("MISSION_BOARD_NAME_MISSION") - end - end - - --color this red if there's no jobs, mark there's no jobs taken - self.job_list = Text.FormatKey("MISSION_BOARD_NAME_TAKEN") - self.taken_populated = true - if MISSION_GEN.TakenBoardIsEmpty() then - self.job_list = "[color=#FF0000]"..Text.FormatKey("MISSION_BOARD_NAME_TAKEN").."[color]" - self.taken_populated = false - end - - self.menu.Elements:Add(RogueEssence.Menu.MenuText(board_name, RogueElements.Loc(21, 8))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(self.job_list, RogueElements.Loc(21, 22))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(Text.FormatKey("MISSION_BOARD_EXIT"), RogueElements.Loc(21, 36))) - - self.menu.Elements:Add(self.cursor) -end - - -function BoardSelectionMenu:Update(input) - - if input:JustPressed(RogueEssence.FrameInput.InputType.Confirm) then - if self.current_item == 0 then --open relevant job menu - if self.board_populated then - _GAME:SE("Menu/Confirm") - local board_menu = BoardMenu:new(self.board_type, self) - _MENU:AddMenu(board_menu.menu, false) - else - _GAME:SE("Menu/Cancel") - end - elseif self.current_item == 1 then--open taken missions - if self.taken_populated then - _GAME:SE("Menu/Confirm") - local board_menu = BoardMenu:new(COMMON.MISSION_BOARD_TAKEN, self) - _MENU:AddMenu(board_menu.menu, false) - else - _GAME:SE("Menu/Cancel") - end - else - _GAME:SE("Menu/Cancel") - _MENU:RemoveMenu() - end - elseif input:JustPressed(RogueEssence.FrameInput.InputType.Cancel) or input:JustPressed(RogueEssence.FrameInput.InputType.Menu) then - _GAME:SE("Menu/Cancel") - _MENU:RemoveMenu() - --open job menu for that particular job - else - moved = false - if RogueEssence.Menu.InteractableMenu.IsInputting(input, LUA_ENGINE:MakeLuaArray(Dir8, { Dir8.Down, Dir8.DownLeft, Dir8.DownRight })) then - moved = true - self.current_item = (self.current_item + 1) % 3 - - elseif RogueEssence.Menu.InteractableMenu.IsInputting(input, LUA_ENGINE:MakeLuaArray(Dir8, { Dir8.Up, Dir8.UpLeft, Dir8.UpRight })) then - moved = true - self.current_item = (self.current_item - 1) % 3 - end - - if moved then - _GAME:SE("Menu/Select") - self.cursor:ResetTimeOffset() - self.cursor.Loc = RogueElements.Loc(9, 8 + 14 * self.current_item) - end - end -end - - - - ------------------------- --- DungeonJobList Menu ------------------------- -DungeonJobList = Class('DungeonJobList') - ---Used to see what jobs are in this dungeon -function DungeonJobList:initialize() - assert(self, "DungeonJobList:initialize(): Error, self is nil!") - - self.menu = RogueEssence.Menu.ScriptableMenu(32, 32, 256, 176, function(input) self:Update(input) end) - self.dungeon = "" - self.section = -1 - - --This menu should only be accessible from dungeons, but add this as a check just in case we somehow access this menu from outside a dungeon. - if RogueEssence.GameManager.Instance.CurrentScene == RogueEssence.Dungeon.DungeonScene.Instance then - self.dungeon = _ZONE.CurrentZoneID - self.section = _ZONE.CurrentMapID.Segment - end - - self.jobs = SV.TakenBoard - self.job_count = 0 - - for i = 1, 8, 1 do - if SV.TakenBoard[i].Client == "" then - break - elseif SV.TakenBoard[i].Zone ~= '' and SV.TakenBoard[i].Zone == self.dungeon then - self.job_count = self.job_count + 1 - end - end - self:DrawMenu() - -end - ---refreshes information and draws to the menu. This is important in case there's a change to the taken board -function DungeonJobList:DrawMenu() - --Standard menu divider. Reuse this whenever you need a menu divider at the top for a title. - self.menu.Elements:Add(RogueEssence.Menu.MenuDivider(RogueElements.Loc(8, 8 + 12), self.menu.Bounds.Width - 8 * 2)) - - --Standard title. Reuse this whenever a title is needed. - self.menu.Elements:Add(RogueEssence.Menu.MenuText("Mission Objectives", RogueElements.Loc(16, 8))) - - --how many jobs have we populated so far - local count = 0 - local side_dungeon_mission = false - local zone_string = '' - - --populate jobs that are in this dungeon - for i = 8, 1, -1 do - --skip all empty jobs - if self.jobs[i].Client ~= "" then - --only look at jobs in the current dungeon that aren't suspended - if self.jobs[i].Zone == self.dungeon and self.jobs[i].Taken then - if self.jobs[i].Segment == self.section then - local floor_num = MISSION_GEN.GetStairsType(self.jobs[i].Zone) ..'[color=#00FFFF]' .. tostring(self.jobs[i].Floor) .. "[color]F" - local objective = "" - local icon = "" - local goal = self.jobs[i].Type - - local target = _DATA:GetMonster(self.jobs[i].Target):GetColoredName() - - local client = "" - if self.jobs[i].Client == "magna" then - client = "[color=#00FFFF]Magna[color]" - else - client = _DATA:GetMonster(self.jobs[i].Client):GetColoredName() - end - - local item = "" - if self.jobs[i].Item ~= "" then - item = _DATA:GetItem(self.jobs[i].Item):GetColoredName() - end - - if goal == COMMON.MISSION_TYPE_RESCUE then - objective = Text.FormatKey("MISSION_OBJECTIVES_RESCUE", target) - elseif goal == COMMON.MISSION_TYPE_ESCORT then - objective = Text.FormatKey("MISSION_OBJECTIVES_ESCORT", client, target) - elseif goal == COMMON.MISSION_TYPE_OUTLAW then - objective = Text.FormatKey("MISSION_OBJECTIVES_OUTLAW", target) - elseif goal == COMMON.MISSION_TYPE_EXPLORATION then - objective = Text.FormatKey("MISSION_OBJECTIVES_EXPLORATION", client) - elseif goal == COMMON.MISSION_TYPE_LOST_ITEM then - objective = Text.FormatKey("MISSION_OBJECTIVES_LOST_ITEM", item, client) - elseif goal == COMMON.MISSION_TYPE_OUTLAW_ITEM then - objective = Text.FormatKey("MISSION_OBJECTIVES_OUTLAW_ITEM", item, target) - elseif goal == COMMON.MISSION_TYPE_OUTLAW_FLEE then - objective = Text.FormatKey("MISSION_OBJECTIVES_OUTLAW_FLEE", target) - elseif goal == COMMON.MISSION_TYPE_OUTLAW_MONSTER_HOUSE then - objective = Text.FormatKey("MISSION_OBJECTIVES_OUTLAW_MONSTER_HOUSE", target) - elseif goal == COMMON.MISSION_TYPE_DELIVERY then - objective = Text.FormatKey("MISSION_OBJECTIVES_DELIVERY", item, client) - end - - if self.jobs[i].Completion == COMMON.MISSION_INCOMPLETE then - icon = STRINGS:Format("\\uE10F")--open letter - else - icon = STRINGS:Format("\\uE10A")--check mark - end - - self.menu.Elements:Add(RogueEssence.Menu.MenuText(icon, RogueElements.Loc(16, 24 + 14 * count))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(floor_num, RogueElements.Loc(28, 24 + 14 * count))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(objective, RogueElements.Loc(60, 24 + 14 * count))) - - count = count + 1 - else - side_dungeon_mission = true - zone_string = _DATA:GetZone(self.jobs[i].Zone).Segments[self.jobs[i].Segment]:ToString() - zone_string = COMMON.CreateColoredSegmentString(zone_string) - end - - end - - end - - - end - - - --put a special message if no jobs dependent on story progression. - local message = "" - if side_dungeon_mission == true and self.section == 0 then - message = Text.FormatKey("MISSION_OBJECTIVES_SIDE", zone_string) - local yloc = 12 + 14 - if count > 0 then - yloc = 24 + 14 * count - end - self.menu.Elements:Add(RogueEssence.Menu.MenuText(message, RogueElements.Loc(16, yloc))) - elseif count == 0 then - if _DATA.Save.Rescue ~= nil and _DATA.Save.Rescue.Rescuing then - if self.section ~= _DATA.Save.Rescue.SOS.Goal.StructID.Segment then - zone_string = _DATA:GetZone(_DATA.Save.Rescue.SOS.Goal.ID).Segments[_DATA.Save.Rescue.SOS.Goal.StructID.Segment]:ToString() - zone_string = COMMON.CreateColoredSegmentString(zone_string) - - self.menu.Elements:Add(RogueEssence.Menu.MenuText(message, RogueElements.Loc(16, 12 + 14))) - else - local floor_num = MISSION_GEN.GetStairsType(_DATA.Save.Rescue.SOS.Goal.ID) ..'[color=#00FFFF]' .. tostring(_DATA.Save.Rescue.SOS.Goal.StructID.ID) .. "[color]F" - icon = STRINGS:Format("\\uE10F")--open letter - objective = Text.FormatKey("MISSION_OBJECTIVES_RESCUE", _DATA.Save.Rescue.SOS.TeamName) - - self.menu.Elements:Add(RogueEssence.Menu.MenuText(icon, RogueElements.Loc(16, 12 + 14))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(floor_num, RogueElements.Loc(28, 12 + 14))) - self.menu.Elements:Add(RogueEssence.Menu.MenuText(objective, RogueElements.Loc(60, 12 + 14))) - end - else - message = Text.FormatKey("MISSION_OBJECTIVES_DEFAULT") - self.menu.Elements:Add(RogueEssence.Menu.MenuText(message, RogueElements.Loc(16, 12 + 14))) - end - end -end - - -function DungeonJobList:Update(input) - - if input:JustPressed(RogueEssence.FrameInput.InputType.Confirm) then - _GAME:SE("Menu/Cancel") - _MENU:RemoveMenu() - elseif input:JustPressed(RogueEssence.FrameInput.InputType.Cancel) then - _GAME:SE("Menu/Cancel") - _MENU:RemoveMenu() - elseif input:JustPressed(RogueEssence.FrameInput.InputType.Menu) then - _GAME:SE("Menu/Cancel") - _MENU:RemoveMenu() - end -end - ---How many missions are taken? Probably shoulda just had a variable that kept track, but oh well... -function MISSION_GEN.GetTakenCount() - local count = 0 - for i = 1, 8, 1 do - if SV.TakenBoard[i].Client ~= "" then - count = count + 1 - end - end - - return count -end - -function MISSION_GEN.RemoveMissionBackReference() - for mission_num, _ in pairs(SV.TakenBoard) do - SV.TakenBoard[mission_num].BackReference = -1 - end -end - -function MISSION_GEN.EndOfDay(result, segmentID) - --Mark the current dungeon as visited - - local cur_zone_name = _ZONE.CurrentZoneID - - if result == RogueEssence.Data.GameProgress.ResultType.Cleared then - PrintInfo("Completed zone "..cur_zone_name.." with segment "..segmentID) - if SV.MissionPrereq.DungeonsCompleted[cur_zone_name] == nil then - SV.MissionPrereq.DungeonsCompleted[cur_zone_name] = { } - SV.MissionPrereq.DungeonsCompleted[cur_zone_name][segmentID] = 1 - SV.MissionPrereq.NumDungeonsCompleted = SV.MissionPrereq.NumDungeonsCompleted + 1 - elseif SV.MissionPrereq.DungeonsCompleted[cur_zone_name][segmentID] == nil then - SV.MissionPrereq.DungeonsCompleted[cur_zone_name][segmentID] = 1 - end - end - - MISSION_GEN.RegenerateJobs(result) -end - -function MISSION_GEN.RegenerateJobs(result) - --Regenerate jobs - MISSION_GEN.ResetBoards() - MISSION_GEN.RemoveMissionBackReference() - MISSION_GEN.GenerateBoard(result, COMMON.MISSION_BOARD_MISSION) - --MISSION_GEN.GenerateBoard(COMMON.MISSION_BOARD_OUTLAW) - MISSION_GEN.SortMission() - MISSION_GEN.SortOutlaw() -end - -function MISSION_GEN.GetDebugMissionInfo(board, slot) - if board == "outlaw" then - print("client = " .. SV.OutlawBoard[slot].Client) - print("target = " .. SV.OutlawBoard[slot].Target) - print("flavor = " .. SV.OutlawBoard[slot].Flavor) - print("title = " .. SV.OutlawBoard[slot].Title) - print("zone = " .. SV.OutlawBoard[slot].Zone) - print("segment = " .. SV.OutlawBoard[slot].Segment) - print("floor = " .. SV.OutlawBoard[slot].Floor) - print("reward = " .. SV.OutlawBoard[slot].Reward) - print("type = " .. SV.OutlawBoard[slot].Type) - print("Completion = " .. SV.OutlawBoard[slot].Completion) - print("Taken = " .. tostring(SV.OutlawBoard[slot].Taken)) - print("Difficulty = " .. SV.OutlawBoard[slot].Difficulty) - print("item = " .. SV.OutlawBoard[slot].Item) - print("Special = " .. SV.OutlawBoard[slot].Special) - local client_gender = SV.OutlawBoard[slot].ClientGender - if client_gender == 1 then - print("ClientGender = male") - elseif client_gender == 2 then - print("ClientGender = female") - elseif client_gender == 0 then - print("ClientGender = genderless") - else - print("Non valid gender!!!!!!") - end - - local target_Gender = SV.OutlawBoard[slot].ClientGender - if target_Gender == 1 then - print("TargetGender = male") - elseif target_Gender == 2 then - print("TargetGender = female") - elseif target_Gender == 0 then - print("TargetGender = genderless") - else - print("Non valid gender!!!!!!") - end - print("Bonus = " .. SV.OutlawBoard[slot].BonusReward) - - elseif board == "mission" then - print("client = " .. SV.MissionBoard[slot].Client) - print("target = " .. SV.MissionBoard[slot].Target) - print("flavor = " .. SV.MissionBoard[slot].Flavor) - print("title = " .. SV.MissionBoard[slot].Title) - print("zone = " .. SV.MissionBoard[slot].Zone) - print("segment = " .. SV.MissionBoard[slot].Segment) - print("floor = " .. SV.MissionBoard[slot].Floor) - print("reward = " .. SV.MissionBoard[slot].Reward) - print("type = " .. SV.MissionBoard[slot].Type) - print("Completion = " .. SV.MissionBoard[slot].Completion) - print("Taken = " .. tostring(SV.MissionBoard[slot].Taken)) - print("Difficulty = " .. SV.MissionBoard[slot].Difficulty) - print("item = " .. SV.MissionBoard[slot].Item) - print("Special = " .. SV.MissionBoard[slot].Special) - local client_gender = SV.MissionBoard[slot].ClientGender - if client_gender == 1 then - print("ClientGender = male") - elseif client_gender == 2 then - print("ClientGender = female") - elseif client_gender == 0 then - print("ClientGender = genderless") - else - print("Non valid gender!!!!!!") - end - - local target_Gender = SV.MissionBoard[slot].ClientGender - if target_Gender == 1 then - print("TargetGender = male") - elseif target_Gender == 2 then - print("TargetGender = female") - elseif target_Gender == 0 then - print("TargetGender = genderless") - else - print("Non valid gender!!!!!!") - end - print("Bonus = " .. SV.MissionBoard[slot].BonusReward) - else - print("client = " .. SV.TakenBoard[slot].Client) - print("target = " .. SV.TakenBoard[slot].Target) - print("flavor = " .. SV.TakenBoard[slot].Flavor) - print("title = " .. SV.TakenBoard[slot].Title) - print("zone = " .. SV.TakenBoard[slot].Zone) - print("segment = " .. SV.TakenBoard[slot].Segment) - print("floor = " .. SV.TakenBoard[slot].Floor) - print("reward = " .. SV.TakenBoard[slot].Reward) - print("type = " .. SV.TakenBoard[slot].Type) - print("Completion = " .. SV.TakenBoard[slot].Completion) - print("Taken = " .. tostring(SV.TakenBoard[slot].Taken)) - print("Difficulty = " .. SV.TakenBoard[slot].Difficulty) - print("item = " .. SV.TakenBoard[slot].Item) - print("Special = " .. SV.TakenBoard[slot].Special) - print("BackReference = " .. SV.TakenBoard[slot].BackReference) - local client_gender = SV.TakenBoard[slot].ClientGender - if client_gender == 1 then - print("ClientGender = male") - elseif client_gender == 2 then - print("ClientGender = female") - elseif client_gender == 0 then - print("ClientGender = genderless") - else - print("Non valid gender!!!!!!") - end - - local target_Gender = SV.TakenBoard[slot].ClientGender - if target_Gender == 1 then - print("TargetGender = male") - elseif target_Gender == 2 then - print("TargetGender = female") - elseif target_Gender == 0 then - print("TargetGender = genderless") - else - print("Non valid gender!!!!!!") - end - print("Bonus = " .. SV.TakenBoard[slot].BonusReward) - end -end \ No newline at end of file diff --git a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/scriptvars.lua b/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/scriptvars.lua deleted file mode 100644 index c4079aa3f1..0000000000 --- a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/scriptvars.lua +++ /dev/null @@ -1,523 +0,0 @@ ---[[ - scriptvars.lua - This file contains all the default values for the script variables. AKA on a new game this file is loaded! - Script variables are stored in a table that gets saved when the game is saved. - Its meant to be used for scripters to add data to be saved and loaded during a playthrough. - - You can simply refer to the "SV" global table like any other table in any scripts! - You don't need to write a default value in this lua script to add a new value. - However its good practice to set a default value when you can! - - --Examples: - SV.SomeVariable = "Smiles go for miles!" - SV.AnotherVariable = 2526 - SV.AnotherVariable = { something={somethingelse={} } } - SV.AnotherVariable = function() print('lmao') end -]]-- - -SV.MissionsEnabled = false - -SV.MissionPrereq = -{ - DungeonsCompleted = {}, --Uses a bitmap to determine which sections are complete ( - NumDungeonsCompleted = 0 -} - -SV.DestinationFloorNotified = false -SV.MonsterHouseMessageNotified = false -SV.OutlawDefeated = false -SV.OutlawGoonsDefeated = false -SV.MapTurnCounter = -1 - -SV.TemporaryFlags = -{ - MissionCompleted = false,--used to mark if there are any pending missions to hand in. - PriorMapSetting = nil,--Used to mark what the player had their minimap setting whenever the game needs to temporarily change it to something else. -} - ---empty string or a -1 indicates that there's nothing there currently. ---board of jobs you've actually taken. -SV.TakenBoard = -{ - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - } - -} - ---Needed to save data about dungeons -SV.ExpectedLevel = {} -SV.DungeonOrder = {} -SV.StairType = {} - ---jobs on the mission board. -SV.MissionBoard = -{ - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - } - -} - ---Jobs on the outlaw board. -SV.OutlawBoard = -{ - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = 1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - } -} \ No newline at end of file diff --git a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/services/mission_menu_tools/init.lua b/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/services/mission_menu_tools/init.lua deleted file mode 100644 index cd6dbabd4a..0000000000 --- a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/services/mission_menu_tools/init.lua +++ /dev/null @@ -1,70 +0,0 @@ -require 'enable_mission_board.common' -require 'origin.services.baseservice' -require 'origin.recruit_list' -require 'enable_mission_board.mission_gen' - - -local MissionMenuTools = Class('MissionMenuTools', BaseService) - ---[[--------------------------------------------------------------- - MissionMenuTools:initialize() - MissionMenuTools class constructor ----------------------------------------------------------------]] -function MissionMenuTools:initialize() - BaseService.initialize(self) - PrintInfo('MissionMenuTools:initialize()') -end - ---[[--------------------------------------------------------------- - MenuTools:OnAddMenu(menu) - When a menu is about to be added to the menu stack this is called! ----------------------------------------------------------------]] -function MissionMenuTools:OnAddMenu(menu) - local labels = RogueEssence.Menu.MenuLabel - if SV.MissionsEnabled and menu:HasLabel() then - if RogueEssence.GameManager.Instance.CurrentScene == RogueEssence.Dungeon.DungeonScene.Instance then - if menu.Label == labels.OTHERS_MENU then - local choices = menu:ExportChoices() - -- put right after Recruitment Search if present - local index = menu:GetChoiceIndexByLabel("OTH_RECRUIT")+1 - -- if failed, put right before Settings if present - if index <0 then index = menu:GetChoiceIndexByLabel(labels.OTH_SETTINGS) end - -- fall back to either 1 or choices count if both fail - if index <0 then index = math.min(1, menu.Choices.Count) end - choices:Insert(index, RogueEssence.Menu.MenuTextChoice("OTH_MISSION", "Mission Objectives", function () _MENU:AddMenu(DungeonJobList:new().menu, false) end)) - menu:ImportChoices(choices) - end - else - if menu.Label == labels.MAIN_MENU then - local choices = menu:ExportChoices() - local taken_count = MISSION_GEN.GetTakenCount() - local job_list_color = Color.Red - if taken_count > 0 then job_list_color = Color.White end - -- put right before Others if present - local index = menu:GetChoiceIndexByLabel(labels.MAIN_OTHERS) - -- fall back to either 1 or choices count if the check fails - if index <0 then - index = math.min(1, menu.Choices.Count) - end - choices:Insert(index, RogueEssence.Menu.MenuTextChoice("MAIN_MISSION", Text.FormatKey("MENU_JOBLIST_TITLE"), function () _MENU:AddMenu(BoardMenu:new(COMMON.MISSION_BOARD_TAKEN, nil, menu).menu, false) end, taken_count > 0, job_list_color)) - menu:ImportChoices(choices) - end - end - end -end - ----Summary --- Subscribe to all channels this service wants callbacks from -function MissionMenuTools:Subscribe(med) - med:Subscribe("MissionMenuTools", EngineServiceEvents.AddMenu, function(_, args) self.OnAddMenu(self, args[0]) end ) -end - ----Summary --- un-subscribe to all channels this service subscribed to -function MissionMenuTools:UnSubscribe(med) -end - - ---Add our service -SCRIPT:AddService("MissionMenuTools", MissionMenuTools:new()) -return MissionMenuTools \ No newline at end of file diff --git a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/services/mission_service/init.lua b/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/services/mission_service/init.lua deleted file mode 100644 index 17d34b645e..0000000000 --- a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/services/mission_service/init.lua +++ /dev/null @@ -1,1062 +0,0 @@ ---[[ - Example Service - - This is an example to demonstrate how to use the BaseService class to implement a game service. - - **NOTE:** After declaring you service, you have to include your package inside the main.lua file! -]]-- -require 'enable_mission_board.common' -require 'origin.services.baseservice' - ---Declare class MissionService -local MissionService = Class('MissionService', BaseService) - ---[[--------------------------------------------------------------- - MissionService:initialize() - MissionService class constructor ----------------------------------------------------------------]] -function MissionService:initialize() - BaseService.initialize(self) - self.mapname = "" - PrintInfo('MissionService:initialize()') -end - ---[[--------------------------------------------------------------- - MissionService:OnInit() - Called on initialization of the script engine by the game! ----------------------------------------------------------------]] -function MissionService:OnInit() - assert(self, 'MissionService:OnInit() : self is null!') - PrintInfo("\n ExampleSvc: Init..") -end - ---[[--------------------------------------------------------------- - MissionService:OnDeinit() - Called on de-initialization of the script engine by the game! ----------------------------------------------------------------]] -function MissionService:OnDeinit() - assert(self, 'MissionService:OnDeinit() : self is null!') - PrintInfo("\n ExampleSvc: Deinit..") -end - -function MissionService:OnNewGame() - assert(self, 'MissionService:OnNewGame() : self is null!') - - SV.MissionsEnabled = true - -end - -function MissionService:OnUpgrade() - assert(self, 'MissionService:OnUpgrade() : self is null!') - - local self_guid = System.Guid("55887EBE-E6E9-408B-A8DC-ADC9D6A5F67C") - local old_ver = _DATA.Save:GetVersion(self_guid) - local new_ver = RogueEssence.PathMod.GetVersion(self_guid) - - if SV.MissionsEnabled == nil then - SV.MissionPrereq = - { - DungeonsCompleted = {}, --Uses a bitmap to determine which sections are complete ( - NumDungeonsCompleted = 0 - } - SV.DestinationFloorNotified = false - SV.MonsterHouseMessageNotified = false - SV.OutlawDefeated = false - SV.OutlawGoonsDefeated = false - SV.MapTurnCounter = -1 - SV.TemporaryFlags = - { - MissionCompleted = false,--used to mark if there are any pending missions to hand in. - PriorMapSetting = nil,--Used to mark what the player had their minimap setting whenever the game needs to temporarily change it to something else. - } - --empty string or a -1 indicates that there's nothing there currently. - --board of jobs you've actually taken. - SV.TakenBoard = - { - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - } - } - --Needed to save data about dungeons - SV.ExpectedLevel = {} - SV.DungeonOrder = {} - SV.StairType = {} - --jobs on the mission board. - SV.MissionBoard = - { - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - } - } - --Jobs on the outlaw board. - SV.OutlawBoard = - { - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = 1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - } - } - end - - -- TODO: remove after release - if old_ver < System.Version("1.2") then - SV.TakenBoard = - { - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "", - BackReference = -1 - } - } - SV.MissionBoard = - { - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - } - } - --Jobs on the outlaw board. - SV.OutlawBoard = - { - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = 1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - }, - { - Client = "", - Target = "", - Flavor = "", - Title = "", - Zone = "", - Segment = -1, - Floor = -1, - Reward = "", - Type = -1, - Completion = -1, - Taken = false, - Difficulty = "", - Item = "", - Special = "", - ClientGender = -1, - TargetGender = -1, - BonusReward = "" - } - } - - end - - SV.MissionsEnabled = true - -end - ----Summary --- Subscribe to all channels this service wants callbacks from -function MissionService:Subscribe(med) - med:Subscribe("MissionService", EngineServiceEvents.Init, function() self.OnInit(self) end ) - med:Subscribe("MissionService", EngineServiceEvents.Deinit, function() self.OnDeinit(self) end ) - med:Subscribe("MissionService", EngineServiceEvents.NewGame, function() self.OnNewGame(self) end ) - med:Subscribe("MissionService", EngineServiceEvents.UpgradeSave, function() self.OnUpgrade(self) end ) -end - ----Summary --- un-subscribe to all channels this service subscribed to -function MissionService:UnSubscribe(med) -end - ----Summary --- The update method is run as a coroutine for each services. -function MissionService:Update(gtime) --- while(true) --- coroutine.yield() --- end -end - ---Add our service -SCRIPT:AddService("MissionService", MissionService:new()) -return MissionService \ No newline at end of file diff --git a/MODS/Enable_Mission_Board/Mod.xml b/MODS/Enable_Mission_Board/Mod.xml deleted file mode 100644 index 9135d86cd7..0000000000 --- a/MODS/Enable_Mission_Board/Mod.xml +++ /dev/null @@ -1,11 +0,0 @@ -
- Enable Mission Board - TouhouProject - Turning on this mod enables the mission board, allowing accepting random missions that provide EXP and valuable items! - enable_mission_board - 55887EBE-E6E9-408B-A8DC-ADC9D6A5F67C - 1.2 - 0.0 - Mod - -
\ No newline at end of file diff --git a/MODS/Enable_Mission_Board/Strings/strings.resx b/MODS/Enable_Mission_Board/Strings/strings.resx deleted file mode 100644 index 0457f3e4fd..0000000000 --- a/MODS/Enable_Mission_Board/Strings/strings.resx +++ /dev/null @@ -1,211 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Job List - - - - Reach the end of the dungeon. - - - Reach {0} to complete missions. - - - Rescue {0}. - - - Escort {0} to {1}. - - - Arrest {0}. - - - Explore with {0}. - - - Find {0} for {1}. - - - Reclaim {0} from {1}. - - - Arrest cowardly {0}. - - - Arrest big boss {0}. - - - Deliver {0} to {1}. - - - {0} Exp. Point Reward - For labeling an Experience point reward - - Job Summary - - - Accepted: - - - Client: - - - Objective: - - - Place: - - - Difficulty: - - - Reward: - - - Take Job - - - Suspend - - - Delete - - - Cancel - - - Job Bulletin Board - - - Outlaw Notice Board - - - Job List - - - Exit - - - Notice Board - - - Accepted: - - \ No newline at end of file diff --git a/MODS/Enable_Mission_Board/Content/Music/Job Clear!.ogg b/MODS/Nebula's Mission Board Mod/Content/Music/Job Clear!.ogg similarity index 100% rename from MODS/Enable_Mission_Board/Content/Music/Job Clear!.ogg rename to MODS/Nebula's Mission Board Mod/Content/Music/Job Clear!.ogg diff --git a/MODS/Enable_Mission_Board/Data/Item/index.idx b/MODS/Nebula's Mission Board Mod/Data/Item/index.idx similarity index 100% rename from MODS/Enable_Mission_Board/Data/Item/index.idx rename to MODS/Nebula's Mission Board Mod/Data/Item/index.idx diff --git a/MODS/Enable_Mission_Board/Data/Item/mission_lost_band.json b/MODS/Nebula's Mission Board Mod/Data/Item/mission_lost_band.json similarity index 100% rename from MODS/Enable_Mission_Board/Data/Item/mission_lost_band.json rename to MODS/Nebula's Mission Board Mod/Data/Item/mission_lost_band.json diff --git a/MODS/Enable_Mission_Board/Data/Item/mission_lost_scarf.json b/MODS/Nebula's Mission Board Mod/Data/Item/mission_lost_scarf.json similarity index 100% rename from MODS/Enable_Mission_Board/Data/Item/mission_lost_scarf.json rename to MODS/Nebula's Mission Board Mod/Data/Item/mission_lost_scarf.json diff --git a/MODS/Enable_Mission_Board/Data/Item/mission_lost_specs.json b/MODS/Nebula's Mission Board Mod/Data/Item/mission_lost_specs.json similarity index 100% rename from MODS/Enable_Mission_Board/Data/Item/mission_lost_specs.json rename to MODS/Nebula's Mission Board Mod/Data/Item/mission_lost_specs.json diff --git a/MODS/Enable_Mission_Board/Data/Item/mission_stolen_band.json b/MODS/Nebula's Mission Board Mod/Data/Item/mission_stolen_band.json similarity index 100% rename from MODS/Enable_Mission_Board/Data/Item/mission_stolen_band.json rename to MODS/Nebula's Mission Board Mod/Data/Item/mission_stolen_band.json diff --git a/MODS/Enable_Mission_Board/Data/Item/mission_stolen_scarf.json b/MODS/Nebula's Mission Board Mod/Data/Item/mission_stolen_scarf.json similarity index 100% rename from MODS/Enable_Mission_Board/Data/Item/mission_stolen_scarf.json rename to MODS/Nebula's Mission Board Mod/Data/Item/mission_stolen_scarf.json diff --git a/MODS/Enable_Mission_Board/Data/Item/mission_stolen_specs.json b/MODS/Nebula's Mission Board Mod/Data/Item/mission_stolen_specs.json similarity index 100% rename from MODS/Enable_Mission_Board/Data/Item/mission_stolen_specs.json rename to MODS/Nebula's Mission Board Mod/Data/Item/mission_stolen_specs.json diff --git a/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/LICENSE b/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/LICENSE new file mode 100644 index 0000000000..26ac72f45d --- /dev/null +++ b/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 MistressNebula + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/README.md b/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/README.md new file mode 100644 index 0000000000..2eff5289a4 --- /dev/null +++ b/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/README.md @@ -0,0 +1,16 @@ +# PMDO Mission Generation Library +This folder contains a job generation library for the PMDO engine, built with the express purpose of making job generation as easy to implement, but also customize, as possible for other mods. + +### How to import +Take this entire folder and move it inside your mod's Script folder. Use ``require("missiongen_lib.missiongen_lib")`` to store the library in +a global variable, and then write the name of that variable inside the missiongen_service.lua file, in the library_name variable situated at the top +of the file. Remember to also add ```require "missiongen_lib.missiongen_service"``` in your mod's main.lua file. + +You will then need to hook all the library's necessary events to your mod. To do that it would be best to look at this mod's code for reference. + +### How to customize +The missiongen_settings.lua file contains a table of settings that are used by the library to define its behavior. Every setting is documented individually, +describing its format, what it is used for and what kind of data it requires. + +If you need more info, please look at the [library's page](https://wiki.pmdo.pmdcollab.org/User:MistressNebula/Nebula's_Mission_Board_Mod) in PMDOWiki. +Feel free to leave issues on GitHub if you want to request some more specific guides that are not listed in the linked page yet! diff --git a/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/missiongen_devcheck.lua b/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/missiongen_devcheck.lua new file mode 100644 index 0000000000..a7539a8ad9 --- /dev/null +++ b/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/missiongen_devcheck.lua @@ -0,0 +1,1613 @@ +-- PMDO Mission Generation Library v1.0.3, by MistressNebula +-- Debug file +-- ----------------------------------------------------------------------------------------- -- +-- This file is a big debug function used by the library only when loading during dev mode. +-- It checks all settings in missiongen_settings.lua to make sure their data structures are +-- correct, and to ensure consistency in the provided data. +-- ----------------------------------------------------------------------------------------- -- +-- The library's loading routine tries to run this file in protected mode, so deleting it +-- does not cause any issues other than disable this debug routine altogether. + +local errors, warns + +local LogError = function(msg) + PrintInfo("[ERROR] " .. tostring(msg)) + errors = errors+1 +end +local LogWarn = function(msg) + PrintInfo("[WARN] " .. tostring(msg)) + warns = warns+1 +end +local LogInfo = function(msg) + PrintInfo("[INFO] " .. tostring(msg)) +end + +---@generic T:any +---@param value T +---@param allowed T[] +---@return boolean +local EqualToAny = function(value, allowed) + for _, val in ipairs(allowed) do + if val == value then return true end + end + return false +end + +local checker = {} + +---@param monster monsterIDTable +---@param print_path string +function checker.checkMonsterIDTable(monster, print_path) + if type(monster.Species) == "string" then + if _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Monster]:ContainsKey(monster.Species) then + if monster.Form then + if type(monster.Form) == "number" then + local species_data = _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Monster]:Get(monster + .Species) + if monster.Form < 0 then + LogError("\"" .. tostring(print_path) .. ".Form\" cannot be negative.") + elseif monster.Form >= species_data.Forms.Count then + LogError("\"" .. print_path .. ".Form\" must be lower than " .. species_data.Forms.Count .. " because \"" .. tostring(monster.Species) .. "\" only has " .. tostring(species_data.Forms.Count) .. " forms.") + end + else + LogError("\"" .. tostring(print_path) .. ".Form\" is not number or nil. This may cause the library to fail.") + end + end + if not EqualToAny(type(monster.Gender), { "number", "nil" }) then + LogError("\"" .. tostring(print_path) .. ".Gender\" is not number or nil. This may cause the library to fail.") + end + if monster.Skin then + if type(monster.Skin) == "string" then + if not _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Skin]:ContainsKey(monster.Skin) then + LogError("Value \"" .. monster.Skin .. "\" in \"" .. tostring(print_path) .. "\" is not a valid skin id. This may cause the library to fail.") + end + else + LogError("\"" .. tostring(print_path) .. ".Skin\" is not string or nil. This may cause the library to fail.") + end + end + if not EqualToAny(type(monster.Nickname), { "string", "nil" }) then + LogError("\"" .. tostring(print_path) .. ".Nickname\" is not string or nil. This may cause the library to fail.") + end + else + LogError("Value \"" .. monster.Species .. "\" in \"" .. tostring(print_path) .. ".Species\" is not a valid species id. This may cause the library to fail.") + end + else + LogError("\"" .. tostring(print_path) .. ".Species\" is not a string. This may cause the library to fail.") + end +end + +---@param loc {zone:string, map:integer} +---@param print_path string +function checker.checkGroundLocationTable(loc, print_path) + local can_check = true + if type(loc.zone) ~= "string" then + LogError("\"" .. tostring(print_path) .. ".zone\" is not a string. This will cause the library to fail.") + can_check = false + end + if type(loc.map) ~= "number" then + LogError("\"" .. tostring(print_path) .. ".map\" is not a number. This will cause the library to fail.") + can_check = false + end + if can_check then + if _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Zone]:ContainsKey(loc.zone) then + local zone_summary = _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Zone]:Get(loc.zone) + if loc.map < 0 then + LogError("\"" .. tostring(print_path) .. ".map\" cannot be negative.") + elseif loc.map >= zone_summary.Grounds.Count then + LogError("\"" .. tostring(print_path) .. ".map\" is higher than the number of ground maps registered to \"" .. tostring(loc.zone) .. "\". This will cause the library to fail.") + end + else + LogError("The value \"" .. tostring(loc.zone) .. "\" of \"" .. tostring(print_path) .. ".zone\" is not a valid zone id. This will cause the library to fail.") + end + end +end + +function checker.check(library) + local values = {} + ---@type table + values.difficulties = {} + ---@type table + values.dungeons = {} + ---@type table + values.job_types = {} + ---@type table + values.reward_pools = {} + ---@type table}> + values.data_tiers = {} + ---@type table + values.special_types = {} + values.special = false + + errors, warns = 0, 0 + + local settings = library.data --require("missiongen_lib.missiongen_settings") + LogInfo("Running sanity checker from missiongen_devcheck.lua") + + if not settings then + LogError("The base \"settings\" struct is nil. This will cause the library to fail. Was the return statement in \"missiongen_settings.lua\" removed?") + return + elseif type(settings) ~= "table" then + LogError("The base \"settings\" struct is not a table. This will cause the library to fail. Was the return statement in \"missiongen_settings.lua\" tampered with?") + return + end + + --validate root name + if not settings.sv_root_name then + LogError("\"sv_root_name\" is nil. This will cause the library to fail.") + elseif type(settings.sv_root_name) ~= "string" then + if type(settings.sv_root_name) == "table" then + for _, elem in ipairs(settings.sv_root_name --[[@as string[] ]]) do + if type(elem) == "string" then + LogWarn("All elements inside \"sv_root_name\" should be strings.") + break + end + end + else + LogError("\"sv_root_name\" is not a string or a list of strings. This will cause the library to fail.") + end + end + + if not settings.difficulty_list then + LogError("\"difficulty_list\" is nil. This will cause the library to fail.") + elseif type(settings.difficulty_list) == "table" then + for _, diff in ipairs(settings.difficulty_list) do + if type(diff) ~= "string" then + LogWarn("\"difficulty_list\" contains an id of type \"" .. + type(diff) .. "\". All difficulty ids should be strings.") + end + values.difficulties[diff] = true + end + else + LogError("\"difficulty_list\" is not a table. This will cause the library to fail.") + end + + if not settings.dungeons then + LogError("\"dungeons\" is nil. This will cause the library to fail.") + elseif type(settings.dungeons) == "table" then + for id, zone_data in pairs(settings.dungeons) do + if _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Zone]:ContainsKey(id) then + if type(zone_data) == "table" then + local zone = _DATA:GetZone(id) + for index, segment_data in pairs(zone_data) do + if index >= 0 and index < zone.Segments.Count then + if type(segment_data) == "table" then + if not type(segment_data.max_floor) == "number" then + LogError("\"dungeons[" .. + tostring(id) .. + "][" .. + tostring(index) .. + "].max_floor\" is not a number. This will cause the library to fail.") + end + if not EqualToAny(type(segment_data.must_end), { "boolean", "nil" }) then + LogWarn("\"dungeons[" .. + tostring(id) .. + "][" .. + tostring(index) .. + "].must_end\" is not boolean or nil. This may lead to unintended results.") + end + for idx, section in pairs(segment_data.sections) do + if type(section) == "table" then + if not type(section.start) == "number" then + LogError("\"dungeons[" .. + tostring(id) .. + "][" .. + tostring(index) .. + "].sections[" .. + tostring(idx) .. + "].start\" is not a number. This may cause the library to fail.") + end + if type(section.difficulty) == "string" then + if not values.difficulties[section.difficulty] then + LogWarn("Value " .. + tostring(section.difficulty) .. + " of \"dungeons[" .. + tostring(id) .. + "][" .. + tostring(index) .. + "].sections[" .. + tostring(idx) .. + "].difficulty\" does not correspond to any defined difficulty id. It will be treated as if it was the difficulty of index 1.") + end + else + LogWarn("\"dungeons[" .. + tostring(id) .. + "][" .. + tostring(index) .. + "].sections[" .. + tostring(idx) .. + "].difficulty\" is not a string. It will be treated as if it was the difficulty of index 1.") + end + else + LogError("\"dungeons[" .. + tostring(id) .. + "][" .. + tostring(index) .. + "].sections[" .. + tostring(idx) .. "]\" is not a table. This may cause the library to fail.") + end + end + else + LogError("\"dungeons[" .. + tostring(id) .. + "][" .. tostring(index) .. "]\" is not a table. This will cause the library to fail.") + end + else + LogError("\"" .. + tostring(id) .. + "\" does not contain a segment of index " .. + tostring(index) .. ". This may cause the library to fail.") + end + end + else + LogError("\"dungeons[" .. tostring(id) .. "]\" is not a table. This will cause the library to fail.") + end + values.dungeons[id] = true + else + LogError("\"" .. tostring(id) .. "\" is not a valid zone id. This may cause the library to fail.") + end + end + else + LogError("\"dungeons\" is not a table. This will cause the library to fail.") + end + + if not settings.dungeon_order then + LogError("\"dungeon_order\" is nil. This will cause the library to fail.") + elseif type(settings.dungeon_order) == "table" then + for id, val in pairs(settings.dungeon_order) do + if type(val) ~= "number" then + LogError("\"dungeon_order[" .. tostring(id) .. "]\" is not a number. This may cause the library to fail.") + end + end + else + LogError("\"dungeon_order\" is not a table. This will cause the library to fail.") + end + + if not settings.job_types then + LogError("\"job_types\" is nil. This will cause the library to fail.") + elseif type(settings.job_types) == "table" then + for id, val in pairs(settings.job_types) do + if type(id) == "string" and library.globals.job_types[id] then + if not EqualToAny(type(val.rank_modifier), { "number", "nil" }) then + LogWarn("\"job_types[" .. + tostring(id) .. "].rank_modifier\" is not number or nil. This will cause the library to fail.") + end + if val.min_rank then + if type(val.min_rank) == "string" then + if not values.difficulties[val.min_rank] then + LogWarn("Value " .. + tostring(val.min_rank) .. + " of \"job_types[" .. + tostring(id) .. + "].min_rank\" does not correspond to any defined difficulty id. It will be treated as if it was the difficulty of index 1.") + end + else + LogWarn("\"job_types[" .. + tostring(id) .. + "].min_rank\" is not a string. It will be treated as if it was the difficulty of index 1.") + end + end + values.job_types[id] = true + else + LogWarn("Invalid job id \"" .. tostring(id) .. "\" found inside \"job_types\".") + end + end + else + LogError("\"job_types\" is not a table. This will cause the library to fail.") + end + + if not settings.dungeon_job_modifiers then + LogError("\"dungeon_job_modifiers\" is nil. This will cause the library to fail.") + elseif type(settings.dungeon_job_modifiers) == "table" then + for id, tbl in pairs(settings.dungeon_job_modifiers) do + if type(tbl) == "table" then + for job, mult in pairs(tbl) do + if type(job) == "string" and library.globals.job_types[job] then + if values.job_types[job] then + if type(mult) ~= "number" then + LogError("\"dungeon_job_modifiers[" .. + tostring(id) .. + "][" .. tostring(job) .. "]\" is not a number. This may cause the library to fail.") + end + else + LogWarn("Unused job id \"" .. + tostring(job) .. "\" found inside \"dungeon_job_modifiers[" .. tostring(id) .. "]\"") + end + else + LogWarn("Invalid job id \"" .. + tostring(job) .. "\" found inside \"dungeon_job_modifiers[" .. tostring(id) .. "]\".") + end + end + else + LogError("\"dungeon_job_modifiers[" .. + tostring(id) .. "]\" is not a table. This may cause the library to fail.") + end + end + else + LogError("\"dungeon_job_modifiers\" is not a table. This will cause the library to fail.") + end + + if not settings.special_chance then + LogError("\"special_chance\" is nil. This will cause the library to fail.") + elseif type(settings.special_chance) == "number" then + values.special = settings.special_chance > 0 + else + LogError("\"special_chance\" is not a number. This will cause the library to fail.") + end + + if not settings.special_jobs then + if values.special then + LogError("\"special_jobs\" is nil. This will cause the library to fail.") + else + LogInfo("\"special_jobs\" is nil. This is acceptable, because \"special_chance\" is 0 or less.") + end + elseif type(settings.special_jobs) == "table" then + for id, ls in pairs(settings.special_jobs) do + if type(id) == "string" and library.globals.job_types[id] then + if values.job_types[id] then + if type(ls) == "table" then + for i, entry in pairs(ls) do + if type(entry) == "string" then + values.special_types[entry] = false + else + if values.special then + LogWarn("Index " .. + tostring(i) .. + " of \"special_jobs[" .. + tostring(id) .. "]\" is not a string. This may lead to unintended results.") + else + LogWarn("Index " .. + tostring(i) .. " of \"special_jobs[" .. tostring(id) .. "]\" is not a string.") + end + end + end + else + if values.special then + LogError("\"special_jobs[" .. + tostring(id) .. "]\" is not a table. This may cause the library to fail.") + else + LogWarn("\"special_jobs[" .. tostring(id) .. "]\" is not a table.") + end + end + else + LogWarn("Unused job id \"" .. tostring(id) .. "\" found inside \"special_jobs\"") + end + else + LogWarn("Invalid job id \"" .. tostring(id) .. "\" found inside \"special_jobs\".") + end + end + else + if values.special then + LogError("\"special_jobs\" is not a table. This will cause the library to fail.") + else + LogWarn("\"special_jobs\" is not a table.") + end + end + + if not settings.reward_types then + LogError("\"reward_types\" is nil. This will cause the library to fail.") + elseif type(settings.reward_types) == "table" then + for i, tbl in ipairs(settings.reward_types) do + if type(tbl) == "table" then + if type(tbl.id) ~= "string" or not library.globals.reward_types[tbl.id] then + LogWarn("Invalid reward type id \"" .. + tostring(tbl.id) .. "\" found inside \"reward_types[" .. tostring(i) .. "]\".") + end + if tbl.min_rank then + if type(tbl.min_rank) ~= "string" or not values.difficulties[tbl.min_rank] then + LogWarn("Value " .. + tostring(tbl.min_rank) .. + " of \"reward_types[" .. + tostring(i) .. + "].min_rank\" does not correspond to any defined difficulty id. It will be treated as if it was the difficulty of index 1.") + end + end + if tbl.weight then + if type(tbl.weight) == "number" then + if tbl.weight < 0 then + LogWarn("\"reward_types[" .. + tostring(i) .. "].weight\" is less than 0. This may lead to unintended results.") + end + else + LogError("\"reward_types[" .. + tostring(i) .. "].weight\" is not number or nil. This may cause the library to fail.") + end + end + else + LogError("\"reward_types[" .. tostring(i) .. "]\" is not a table. This may cause the library to fail.") + end + end + else + LogError("\"reward_types\" is not a table. This will cause the library to fail.") + end + + if not settings.rewards_per_difficulty then + LogError("\"rewards_per_difficulty\" is nil. This will cause the library to fail.") + elseif type(settings.rewards_per_difficulty) == "table" then + for diff in pairs(values.difficulties) do + local pools = settings.rewards_per_difficulty[diff] + if pools then + for i, pool in ipairs(pools) do + if type(pool) == "table" then + if type(pool.id) ~= "string" then + LogWarn("\"rewards_per_difficulty[" .. + tostring(diff) .. + "][" .. + tostring(i) .. + "].id\" is of type \"" .. type(pool.id) .. "\". All pool ids should be strings.") + end + if settings.reward_pools[pool.id] then + values.reward_pools[pool.id] = true + else + LogError("\"rewards_per_difficulty[" .. + tostring(diff) .. + "][" .. + tostring(i) .. + "]\" refers to undefined pool \"" .. + tostring(pool.id) .. ".This may cause the library to fail.") + end + if pool.weight then + if type(pool.weight) == "number" then + if pool.weight < 0 then + LogWarn("\"rewards_per_difficulty[" .. + tostring(diff) .. + "][" .. + tostring(i) .. "].weight\" is less than 0. This may lead to unintended results.") + end + else + LogError("\"rewards_per_difficulty[" .. + tostring(diff) .. + "][" .. + tostring(i) .. "].weight\" is not number or nil. This may cause the library to fail.") + end + end + else + LogError("\"rewards_per_difficulty[" .. + tostring(diff) .. + "][" .. tostring(i) .. "]\" is not a table. This may cause the library to fail.") + end + end + else + LogError("Difficulty id\"" .. + tostring(diff) .. + "\" has no reward pools assigned to it inside \"rewards_per_difficulty\". This may cause the library to fail.") + end + end + else + LogError("\"rewards_per_difficulty\" is not a table. This will cause the library to fail.") + end + for diff in pairs(settings.rewards_per_difficulty) do + if not values.difficulties then + LogWarn("Reward pool with difficulty id \"" .. + tostring(diff) .. "\" is unused because no such difficulty exists.") + end + end + + if not settings.reward_pools then + LogError("\"reward_pools\" is nil. This will cause the library to fail.") + elseif type(settings.reward_pools) == "table" then + for id, pool in pairs(settings.reward_pools) do + values.reward_pools[id] = values.reward_pools[id] or false + if type(id) ~= "string" then + LogWarn("\"reward_pools\" contains an id of type \"" .. + type(id) .. "\". All reward pool ids should be strings.") + end + if type(pool) == "table" then + for i, entry in ipairs(pool) do + if type(entry) == "table" then + if type(entry.id) ~= "string" then + LogWarn("\"reward_pools[" .. + tostring(id) .. + "][" .. + tostring(i) .. + "].id\" is of type \"" .. type(entry.id) .. "\". All pool ids should be strings.") + end + if settings.reward_pools[entry.id] then + values.reward_pools[entry.id] = true + else + if not _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Item]:ContainsKey(entry.id) then + LogError("Pool entry id \"" .. + tostring(entry.id) .. + "\" in \"reward_pools[" .. + tostring(id) .. + "][" .. + tostring(i) .. + "]\" does not correspond to an item nor another pool. This may cause the library to fail.") + end + end + if entry.weight then + if type(entry.weight) == "number" then + if entry.weight < 0 then + LogWarn("\"reward_pools[" .. + tostring(id) .. + "][" .. + tostring(i) .. "].weight\" is less than 0. This may lead to unintended results.") + end + else + LogError("\"reward_pools[" .. + tostring(id) .. + "][" .. + tostring(i) .. "].weight\" is not number or nil. This may cause the library to fail.") + end + end + if not EqualToAny(type(entry.count), { "number", "nil" }) then + LogError("\"reward_pools[" .. + tostring(id) .. + "][" .. tostring(i) .. "].count\" is not number or nil. This may cause the library to fail.") + end + if not EqualToAny(type(entry.hidden), { "string", "nil" }) then + LogError("\"reward_pools[" .. + tostring(id) .. + "][" .. tostring(i) .. "].hidden\" is not string or nil. This may cause the library to fail.") + end + else + LogError("\"reward_pools[" .. + tostring(id) .. "][" .. tostring(i) .. "]\" is not a table. This may cause the library to fail.") + end + end + else + LogError("\"reward_pools[" .. tostring(id) .. "]\" is not a table. This may cause the library to fail.") + end + end + else + LogError("\"reward_pools\" is not a table. This will cause the library to fail.") + end + for pool, used in pairs(values.reward_pools) do + if not used then + LogWarn("Reward pool with id \"" .. tostring(pool) .. "\" is unused.") + end + end + + if not settings.target_items then + LogError("\"target_items\" is nil. This will cause the library to fail.") + elseif type(settings.target_items) == "table" then + for job, data in pairs(library.globals.job_types) do + if data.req_target_item then + if settings.target_items[job] then + if type(settings.target_items[job]) == "table" then + for i, item in ipairs(settings.target_items[job]) do + if _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Item]:ContainsKey(item) then + local item_summary = _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Item]:Get( + item) + if item_summary.MaxStack > 1 then + LogWarn("Item \"" .. + tostring(item) .. + "\" in \"target_items[" .. + tostring(job) .. + "][" .. tostring(i) .. "]\" is stackable. This may lead to unintended results.") + end + else + LogError("Entry id \"" .. + tostring(item) .. + "\" in \"target_items[" .. + tostring(job) .. + "][" .. + tostring(i) .. "]\" does not correspond to an item. This may cause the library to fail.") + end + end + else + LogError("\"target_items[" .. + tostring(job) .. "]\" is not a table. This may cause the library to fail.") + end + else + LogError("Missing \"target_items\" table for job type \"" .. + tostring(job) .. "\". This may cause the library to fail.") + end + end + end + else + LogError("\"target_items\" is not a table. This will cause the library to fail.") + end + + if not settings.difficulty_to_tier then + LogError("\"difficulty_to_tier\" is nil. This will cause the library to fail.") + elseif type(settings.difficulty_to_tier) == "table" then + for diff in pairs(values.difficulties) do + local pools = settings.difficulty_to_tier[diff] + if pools then + for i, pool in ipairs(pools) do + if type(pool) == "table" then + if type(pool.id) ~= "string" then + LogWarn("\"rewards_per_difficulty[" .. + tostring(diff) .. + "][" .. + tostring(i) .. + "].id\" is of type \"" .. type(pool.id) .. "\". All pool ids should be strings.") + end + values.data_tiers[pool.id] = {} + if pool.weight then + if type(pool.weight) == "number" then + if pool.weight < 0 then + LogWarn("\"rewards_per_difficulty[" .. + tostring(diff) .. + "][" .. + tostring(i) .. "].weight\" is less than 0. This may lead to unintended results.") + end + else + LogError("\"rewards_per_difficulty[" .. + tostring(diff) .. + "][" .. + tostring(i) .. "].weight\" is not number or nil. This may cause the library to fail.") + end + end + else + LogError("\"difficulty_to_tier[" .. + tostring(diff) .. + "][" .. tostring(i) .. "]\" is not a table. This may cause the library to fail.") + end + end + else + LogError("Difficulty id\"" .. + tostring(diff) .. + "\" has no reward pools assigned to it inside \"difficulty_to_tier\". This may cause the library to fail.") + end + end + else + LogError("\"difficulty_to_tier\" is not a table. This will cause the library to fail.") + end + + if not settings.pokemon then + LogError("\"pokemon\" is nil. This will cause the library to fail.") + elseif type(settings.pokemon) == "table" then + for id, pool in pairs(settings.pokemon) do + if values.data_tiers[id] then + values.data_tiers[id].pokemon = true + else + LogWarn("Pokemon pool with id \"" .. tostring(id) .. "\" is unused.") + end + if type(id) ~= "string" then + LogWarn("\"pokemon\" contains an id of type \"" .. + type(id) .. "\". All pokemon pool ids should be strings.") + end + if type(pool) == "table" then + for i, entry in ipairs(pool) do + if type(entry) == "string" then + if not _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Monster]:ContainsKey(entry) then + LogError("Value \"" .. + tostring(entry) .. + "\" in \"pokemon[" .. + tostring(id) .. + "][" .. tostring(i) .. "]\" is not a valid species id. This may cause the library to fail.") + end + elseif type(entry) == "table" then + checker.checkMonsterIDTable(entry, "pokemon[" .. tostring(id) .. "][" .. tostring(i) .. "]") + else + LogError("\"pokemon[" .. + tostring(id) .. + "][" .. tostring(i) .. "]\" is not string or table. This may cause the library to fail.") + end + end + else + LogError("\"pokemon[" .. tostring(id) .. "]\" is not a table. This may cause the library to fail.") + end + end + else + LogError("\"pokemon\" is not a table. This will cause the library to fail.") + end + for pool, used in pairs(values.data_tiers) do + if not used.pokemon then + LogWarn("Pokemon pool with id \"" .. tostring(pool) .. "\" is unused.") + end + end + + if not settings.law_enforcement then + LogError("\"law_enforcement\" is nil. This will cause the library to fail.") + elseif type(settings.law_enforcement) == "table" then + if type(settings.law_enforcement.OFFICER) == "table" then + checker.checkMonsterIDTable(settings.law_enforcement.OFFICER, "settings.law_enforcement.OFFICER") + else + LogError("\"law_enforcement.OFFICER\" is not a table. This may cause the library to fail.") + end + if type(settings.law_enforcement.AGENT) == "table" then + for i, agent in ipairs(settings.law_enforcement.AGENT) do + if type(agent) == "table" then + checker.checkMonsterIDTable(agent --[[@as monsterIDTable]], + "law_enforcement.AGENT[" .. tostring(i) .. "]") + if not EqualToAny(type(agent.Unique), { "boolean", "nil" }) then + LogWarn("\"law_enforcement.AGENT[" .. + tostring(i) .. "].Unique\" is not boolean or nil. This may lead to unintended results.") + end + if agent.Weight then + if type(agent.Weight) == "number" then + if agent.Weight < 0 then + LogWarn("\"law_enforcement.AGENT[" .. + tostring(i) .. "].Weight\" is less than 0. This may lead to unintended results.") + end + else + LogError("\"law_enforcement.AGENT[" .. + tostring(i) .. "].Weight\" is not number or nil. This may cause the library to fail.") + end + end + else + LogError("\"law_enforcement.AGENT[" .. + tostring(i) .. "]\" is not a table. This may cause the library to fail.") + end + end + else + LogError("\"law_enforcement.AGENT\" is not a table. This may cause the library to fail.") + end + else + LogError("\"law_enforcement\" is not a table. This will cause the library to fail.") + end + + + if not settings.enforcer_chance then + LogError("\"enforcer_chance\" is nil. This will cause the library to fail.") + elseif type(settings.enforcer_chance) == "table" then + for id, pool in pairs(settings.enforcer_chance) do + if values.data_tiers[id] then + values.data_tiers[id].enforcer_chance = true + else + LogWarn("Enforcer chance pool with id \"" .. tostring(id) .. "\" is unused.") + end + if type(id) ~= "string" then + LogWarn("\"enforcer_chance\" contains an id of type \"" .. + type(id) .. "\". All enforcer chance pool ids should be strings.") + end + if type(pool) == "table" then + for i, entry in ipairs(pool) do + if type(entry) == "table" then + if not EqualToAny(entry.id, { "OFFICER", "AGENT" }) then + LogError("\"enforcer_chance[" .. + tostring(id) .. + "][" .. + tostring(i) .. "].id\" is not \"OFFICER\" or \"AGENT\". This may cause the library to fail.") + end + if entry.index and entry.id == "AGENT" then + if type(entry.index) == "number" then + if entry.index <= 0 then + LogError("\"enforcer_chance[" .. + tostring(id) .. "][" .. tostring(i) .. "].index\" cannot be 0 or negative.") + elseif settings.law_enforcement and settings.law_enforcement.AGENT and entry.index > #settings.law_enforcement.AGENT then + LogError("\"enforcer_chance[" .. + tostring(id) .. + "][" .. + tostring(i) .. + "].index\" is higher than the number of defined agents in \"law_enforcement[AGENT]\". This may cause the library to fail.") + end + else + LogError("\"pokemon[" .. + tostring(id) .. + "][" .. + tostring(i) .. "].index\" is not number or nil. This may cause the library to fail.") + end + end + if entry.weight then + if type(entry.weight) == "number" then + if entry.weight < 0 then + LogWarn("\"reward_types[" .. + tostring(i) .. "].weight\" is less than 0. This may lead to unintended results.") + end + else + LogError("\"reward_types[" .. + tostring(i) .. "].weight\" is not number or nil. This may cause the library to fail.") + end + end + else + LogError("\"enforcer_chance[" .. + tostring(id) .. "][" .. tostring(i) .. "]\" is not a table. This may cause the library to fail.") + end + end + else + LogError("\"enforcer_chance[" .. + tostring(id) .. "]\" is not a table. This may cause the library to fail.") + end + end + else + LogError("\"enforcer_chance\" is not a table. This will cause the library to fail.") + end + for pool, used in pairs(values.data_tiers) do + if not used.enforcer_chance then + LogWarn("Enforcer chance pool with id \"" .. tostring(pool) .. "\" is unused.") + end + end + + if not settings.special_data then + if values.special then + LogError("\"special_data\" is nil. This will cause the library to fail.") + else + LogInfo("\"special_data\" is nil. This is acceptable, because \"special_chance\" is 0 or less.") + end + elseif type(settings.special_data) == "table" then + for spec, tbl in pairs(settings.special_data) do + if type(spec) == "string" then + if values.special_types[spec] == nil then + LogWarn("Special data pool with id \"" .. tostring(spec) .. "\" is unused.") + else + values.special_types[spec] = true + end + if type(tbl) == "table" then + for t, list in pairs(tbl) do + if type(t) == "string" then + if values.data_tiers[t] then + values.data_tiers[t].special_data = values.data_tiers[t].special_data or {} + values.data_tiers[t].special_data[spec] = values.data_tiers[t].special_data[spec] or {} + values.data_tiers[t].special_data[spec] = true + else + LogWarn("Special data tier with id \"" .. + tostring(t) .. "\" defined inside pool \"" .. tostring(spec) .. "\" is unused.") + end + if type(list) == "table" then + for i, entry in ipairs(list) do + if type(entry.client) == "table" then + checker.checkMonsterIDTable(entry.client --[[@as monsterIDTable]], + "special_data[" .. + tostring(spec) .. "][" .. tostring(t) .. "][" .. tostring(i) .. "].client") + elseif type(entry.client) == "string" then + if entry.client ~= "ENFORCER" and entry.client ~= "OFFICER" and entry.client ~= "AGENT" then + LogError("\"special_data[" .. + tostring(spec) .. + "][" .. + tostring(t) .. + "][" .. + tostring(i) .. + "].client\" is set to an invalid keyword. This may cause the library to fail.\n" .. + "Valid keywords are: \"ENFORCER\",\"OFFICER\",\"AGENT\".") + end + else + LogError("\"special_data[" .. + tostring(spec) .. + "][" .. + tostring(t) .. + "][" .. + tostring(i) .. + "].client\" is not table or string. This may cause the library to fail.") + end + if type(entry.target) == "table" then + checker.checkMonsterIDTable(entry.target --[[@as monsterIDTable]], + "special_data[" .. + tostring(spec) .. "][" .. tostring(t) .. "][" .. tostring(i) .. "].target") + elseif type(entry.target) == "string" then + if entry.target ~= "ENFORCER" and entry.target ~= "OFFICER" and entry.target ~= "AGENT" then + LogError("\"special_data[" .. + tostring(spec) .. + "][" .. + tostring(t) .. + "][" .. + tostring(i) .. + "].target\" is set to an invalid keyword. This may cause the library to fail.\n" .. + "Valid keywords are: \"ENFORCER\",\"OFFICER\",\"AGENT\".") + end + else + LogError("\"special_data[" .. + tostring(spec) .. + "][" .. + tostring(t) .. + "][" .. + tostring(i) .. + "].target\" is not table or string. This may cause the library to fail.") + end + if entry.item then + if type(entry.item) == string then + if _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Item]:ContainsKey(entry.item) then + local item_summary = _DATA.DataIndices + [RogueEssence.Data.DataManager.DataType.Item]:Get(entry.item) + if item_summary.MaxStack > 1 then + LogWarn("Item \"" .. + tostring(entry.item) .. + "\" in \"special_data[" .. + tostring(spec) .. + "][" .. + tostring(t) .. + "][" .. + tostring(i) .. + "].item\" is stackable. This may lead to unintended results.") + end + else + LogError("Entry id \"" .. + tostring(entry.item) .. + "\" in \"special_data[" .. + tostring(spec) .. + "][" .. + tostring(t) .. + "][" .. + tostring(i) .. + "].item\" does not correspond to an item. This may cause the library to fail.") + end + else + LogError("\"special_data[" .. + tostring(spec) .. + "][" .. + tostring(t) .. + "][" .. + tostring(i) .. + "].target\" is not a string. This may cause the library to fail.") + end + end + if type(entry.flavor) == "string" then + if not RogueEssence.Text.Strings:ContainsKey(entry.flavor) then + LogWarn("\"special_data[" .. + tostring(spec) .. + "][" .. + tostring(t) .. + "][" .. + tostring(i) .. + "].flavor\" is not a valid strings.resx key. This will cause errors when opening menus.") + end + else + LogError("\"special_data[" .. + tostring(spec) .. + "][" .. + tostring(t) .. + "][" .. + tostring(i) .. "].flavor\" is not a string. This may cause the library to fail.") + end + end + else + if values.special then + LogError("\"special_data[" .. + tostring(spec) .. + "][" .. tostring(t) .. "]\" is not a table. This may cause the library to fail.") + else + LogWarn("\"special_data[" .. + tostring(spec) .. "][" .. tostring(t) .. "]\" is not a table.") + end + end + else + LogWarn("\"special_data[" .. + tostring(spec) .. + "]\" contains an id of type \"" .. + type(t) .. "\". All special data tier ids should be strings.") + end + end + else + if values.special then + LogError("\"special_data[" .. + tostring(spec) .. "]\" is not a table. This may cause the library to fail.") + else + LogWarn("\"special_data[" .. tostring(spec) .. "]\" is not a table.") + end + end + else + LogWarn("\"special_data\" contains an id of type \"" .. + type(spec) .. "\". All special data pool ids should be strings.") + end + end + else + if values.special then + LogError("\"special_data\" is not a table. This will cause the library to fail.") + else + LogWarn("\"special_data\" is not a table.") + end + end + for spec, defined in pairs(values.special_types) do + if not defined then + if values.special then + LogError("Special type \"" .. + tostring(spec) .. "\" is not defined in \"special_data\". This may cause the library to fail") + else + LogWarn("Special type \"" .. tostring(spec) .. "\" is not defined in \"special_data\".") + end + for t, tbl in pairs(values.data_tiers) do + if tbl.special_data then + if not tbl.special_data[spec] then + LogWarn("Special data pool of tier \"" .. + tostring(t) .. + "\" does not contain a list for special type \"" .. + tostring(spec) .. "\". This may cause the library to fail") + end + else + LogWarn("Special data pool of tier \"" .. + tostring(t) .. "\" does not contain any entries. This may cause the library to fail") + end + end + end + end + + if not settings.job_titles then + LogError("\"job_titles\" is nil. This will cause the library to fail.") + elseif type(settings.job_titles) == "table" then + for t, list in pairs(settings.job_titles) do + if values.job_types[t] or values.special_types[t] then + if type(list) == "table" then + for i, entry in ipairs(list) do + if type(entry) == "string" then + if not RogueEssence.Text.Strings:ContainsKey(entry) then + LogWarn("\"job_titles[" .. + tostring(t) .. + "][" .. + tostring(i) .. + "]\" is not a valid strings.resx key. This will cause errors when opening menus.") + end + else + LogError("\"job_titles[" .. + tostring(t) .. + "][" .. tostring(i) .. "]\" is not a string. This may cause the library to fail.") + end + end + else + LogError("\"job_titles[" .. tostring(t) .. "]\" is not a table. This will cause the library to fail.") + end + elseif library.globals.job_types[t] then + LogWarn("Unused job type id \"" .. tostring(t) .. "\" found inside \"job_titles\".") + else + LogWarn("Invalid job type id \"" .. tostring(t) .. "\" found inside \"job_titles\".") + end + end + else + LogError("\"job_titles\" is not a table. This will cause the library to fail.") + end + for t in pairs(values.job_types) do + if not settings.job_titles[t] then + LogError("Job type id\"" .. + tostring(t) .. + "\" has no job title pool assigned to it inside \"job_titles\". This may cause the library to fail.") + end + end + for t in pairs(values.special_types) do + if not settings.job_titles[t] then + if values.special then + LogError("Special type id\"" .. + tostring(t) .. + "\" has no job title pool assigned to it inside \"job_titles\". This may cause the library to fail.") + else + LogWarn("Special type id\"" .. + tostring(t) .. "\" has no job title pool assigned to it inside \"job_titles\".") + end + end + end + + if not settings.job_flavor then + LogError("\"job_flavor\" is nil. This will cause the library to fail.") + elseif type(settings.job_flavor) == "table" then + for t, list in pairs(settings.job_flavor) do + if values.job_types[t] then + if type(list) == "table" then + for i, entry in ipairs(list) do + if type(entry) == "table" then + for s = 1, 2, 1 do + if type(entry[s]) == "string" then + if not RogueEssence.Text.Strings:ContainsKey(entry[s]) then + LogWarn("\"job_flavor[" .. + tostring(t) .. + "][" .. + tostring(i) .. + "][" .. + tostring(s) .. + "]\" is not a valid strings.resx key. This will cause errors when opening menus.") + end + else + LogError("\"job_flavor[" .. + tostring(t) .. + "][" .. + tostring(i) .. + "][" .. tostring(s) .. "]\" is not a string. This may cause the library to fail.") + end + end + else + LogError("\"job_flavor[" .. + tostring(t) .. + "][" .. tostring(i) .. "]\" is not a table. This may cause the library to fail.") + end + end + else + LogError("\"job_flavor[" .. tostring(t) .. "]\" is not a table. This will cause the library to fail.") + end + elseif library.globals.job_types[t] then + LogWarn("Unused job type id \"" .. tostring(t) .. "\" found inside \"job_flavor\".") + else + LogWarn("Invalid job type id \"" .. tostring(t) .. "\" found inside \"job_flavor\".") + end + end + else + LogError("\"job_flavor\" is not a table. This will cause the library to fail.") + end + for t in pairs(values.job_types) do + if not settings.job_flavor[t] then + LogError("Job type id\"" .. + tostring(t) .. + "\" has no job flavor pool assigned to it inside \"job_flavor\". This may cause the library to fail.") + end + end + + if not settings.escort_talks then + LogError("\"escort_talks\" is nil. This will cause the library to fail.") + elseif type(settings.escort_talks) == "table" then + for t, list in pairs(settings.escort_talks) do + if values.job_types[t] or values.special_types[t] then + if type(list) == "table" then + for i, entry in ipairs(list) do + if type(entry) == "string" then + if not RogueEssence.Text.StringsEx:ContainsKey(entry) then + LogWarn("\"escort_talks[" .. + tostring(t) .. + "][" .. + tostring(i) .. + "]\" is not a valid stringsEx.resx key. This will cause errors when loading it as dialogue.") + end + else + LogError("\"escort_talks[" .. + tostring(t) .. + "][" .. tostring(i) .. "]\" is not a string. This may cause the library to fail.") + end + end + else + LogError("\"escort_talks[" .. + tostring(t) .. "]\" is not a table. This will cause the library to fail.") + end + elseif library.globals.job_types[t] then + LogWarn("Unused job type id \"" .. tostring(t) .. "\" found inside \"escort_talks\".") + else + LogWarn("Invalid job type id \"" .. tostring(t) .. "\" found inside \"escort_talks\".") + end + end + else + LogError("\"escort_talks\" is not a table. This will cause the library to fail.") + end + for t in pairs(values.job_types) do + if library.globals.job_types[t].has_guest then + if not settings.escort_talks[t] then + LogError("Job type id\"" .. + tostring(t) .. + "\" has no escort dialogue pool assigned to it inside \"escort_talks\". This may cause the library to fail.") + end + end + end + for t in pairs(values.job_types) do + if library.globals.job_types[t].has_guest and settings.special_jobs[t] and type(settings.special_jobs[t]) == "table" then + for _, spec in ipairs(settings.special_jobs[t]) do + if values.special then + if not settings.escort_talks[spec] then + LogError("Special type id \"" .. + tostring(spec) .. + "\" has no escort dialogue pool assigned to it inside \"escort_talks\". This may cause the library to fail.") + end + else + LogWarn("Special type id \"" .. + tostring(spec) .. "\" has no escort dialogue pool assigned to it inside \"escort_talks\".") + end + end + end + end + + if not settings.rescue_responses then + LogError("\"rescue_responses\" is nil. This will cause the library to fail.") + elseif type(settings.rescue_responses) == "table" then + local emotions = { "Normal", "Happy", "Pain", "Angry", "Worried", "Sad", "Crying", "Shouting", "Teary-Eyed", + "Determined", "Joyous", "Inspired", "Surprised", "Dizzy", "Special0", "Special1", "Sigh", "Stunned", + "Special2", "Special3" } + local emotions2 = {} + for _, e in ipairs(emotions) do table.insert(emotions2, "\"" .. tostring(e) .. "\"") end + for case, tbl in pairs(settings.rescue_responses) do + if case == "rescue_yes" or case == "rescue_no" then + if type(tbl) == "table" then + for t, list in pairs(tbl) do + if t == "_DEFAULT" or values.special_types[t] then + if type(list) == "table" then + for i, entry in ipairs(list) do + if type(entry) == "table" then + if type(entry.key) == "string" then + if not RogueEssence.Text.StringsEx:ContainsKey(entry.key) then + LogWarn("\"rescue_responses[" .. + tostring(case) .. + "][" .. + tostring(t) .. + "][" .. + tostring(i) .. + "].key\" is not a valid stringsEx.resx key. This will cause errors when loading it as dialogue.") + end + else + LogError("\"rescue_responses[" .. + tostring(case) .. + "][" .. + tostring(t) .. + "][" .. + tostring(i) .. "].key\" is not a string. This may cause the library to fail.") + end + if entry.emotion then + if type(entry.emotion) == "string" then + if not EqualToAny(entry.emotion, emotions) then + LogError("\"rescue_responses[" .. + tostring(case) .. + "][" .. + tostring(t) .. + "][" .. + tostring(i) .. + "].emotion\" is neither nil nor a valid emotion id.\n" .. + "Valid keywords are: " .. STRINGS:CreateList(emotions2)) + end + else + LogError("\"rescue_responses[" .. + tostring(case) .. + "][" .. + tostring(t) .. + "][" .. + tostring(i) .. + "].emotion\" is not a string. This may cause the library to fail.") + end + end + else + LogError("\"rescue_responses[" .. + tostring(case) .. + "][" .. + tostring(t) .. + "][" .. tostring(i) .. "]\" is not a table. This may cause the library to fail.") + end + end + else + LogError("\"rescue_responses[" .. + tostring(case) .. + "][" .. tostring(t) .. "]\" is not a table. This will cause the library to fail.") + end + elseif type(t) == "string" then + LogWarn("Unused special type id \"" .. + tostring(t) .. "\" found inside \"rescue_responses[" .. tostring(case) .. "]\".") + else + LogWarn("\"rescue_responses[" .. + tostring(case) .. + "]\" contains a special type id of type \"" .. + type(t) .. "\". All special type ids should be strings.") + end + end + else + LogError("\"rescue_responses[" .. + tostring(case) .. "]\" is not a table. This will cause the library to fail.") + end + else + LogWarn("Invalid response case \"" .. tostring(case) .. "\" found inside \"rescue_responses\".") + end + end + else + LogError("\"rescue_responses\" is not a table. This will cause the library to fail.") + end + + if not settings.difficulty_data then + LogError("\"difficulty_data\" is nil. This will cause the library to fail.") + elseif type(settings.difficulty_data) == "table" then + for diff, data in pairs(settings.difficulty_data) do + if values.difficulties[diff] then + if type(data) == "table" then + if type(data.display_key) == "string" then + if not RogueEssence.Text.Strings:ContainsKey(data.display_key) then + LogWarn("\"difficulty_data[" .. + tostring(diff) .. + "].display_key\" is not a valid strings.resx key. This will cause errors when opening menus.") + end + else + LogError("\"difficulty_data[" .. + tostring(diff) .. "].display_key\" is not a string. This may cause the library to fail.") + end + if type(data.money_reward) ~= "number" then + LogError("\"difficulty_data[" .. + tostring(diff) .. "].money_reward\" is not a number. This may cause the library to fail.") + end + if type(data.extra_reward) ~= "number" then + LogError("\"difficulty_data[" .. + tostring(diff) .. "].extra_reward\" is not a number. This may cause the library to fail.") + end + if not EqualToAny(type(data.escort_level), { "number", "nil" }) then + LogError("\"difficulty_data[" .. + tostring(diff) .. "].escort_level\" is not number or nil. This may cause the library to fail.") + end + if not EqualToAny(type(data.outlaw_level), { "number", "nil" }) then + LogError("\"difficulty_data[" .. + tostring(diff) .. "].outlaw_level\" is not number or nil. This may cause the library to fail.") + end + else + LogError("\"difficulty_data[" .. + tostring(diff) .. "]\" is not a table. This will cause the library to fail.") + end + elseif type(diff) == "string" then + LogWarn("Unused difficulty id \"" .. tostring(diff) .. "\" found inside \"difficulty_data\".") + else + LogWarn("\"difficulty_data\" contains a difficulty id of type \"" .. + type(diff) .. "\". All difficulty ids should be strings.") + end + end + else + LogError("\"difficulty_data\" is not a table. This will cause the library to fail.") + end + for diff in pairs(values.difficulties) do + if not settings.difficulty_data then + LogError("\"difficulty_data\" does not contain an entry for difficulty id \"" .. + tostring(diff) .. "\". This will cause the library to fail.") + end + end + + if not settings.hidden_floor_chance then + LogError("\"hidden_floor_chance\" is nil. This may cause the library to fail.") + elseif type(settings.hidden_floor_chance) ~= "number" then + LogError("\"hidden_floor_chance\" is not a number. This may cause the library to fail.") + end + + + if not settings.boards then + LogError("\"boards\" is nil. This may cause the library to fail.") + elseif type(settings.boards) == "table" then + for id, data in pairs(settings.boards) do + if type(id) ~= "string" then + LogWarn("\"boards\" contains an id of type \"" .. type(id) .. "\". All board ids should be strings.") + end + if type(data) == "table" then + if type(data.display_key) == "string" then + if not RogueEssence.Text.Strings:ContainsKey(data.display_key) then + LogWarn("\"boards[" .. + tostring(id) .. + "].display_key\" is not a valid strings.resx key. This will cause errors when opening menus.") + end + else + LogError("\"boards[" .. + tostring(id) .. "].display_key\" is not a string. This may cause the library to fail.") + end + if type(data.location) == "table" then + checker.checkGroundLocationTable(data.location, "boards[" .. tostring(id) .. "].location") + else + LogError("\"boards[" .. + tostring(id) .. "].location\" is not a table. This will cause the library to fail.") + end + if not data.size then + LogError("\"boards[" .. tostring(id) .. "].size\" is nil. This will cause the library to fail.") + elseif type(data.size) == "number" then + if data.size <= 0 then + LogWarn("\"boards[" .. tostring(id) .. "].size\" must be greater than 0.") + end + else + LogError("\"boards[" .. + tostring(id) .. "].size\" is not a number. This will cause the library to fail.") + end + if data.dungeons ~= nil then + if type(data.dungeons) == "table" then + if #data.dungeons > 0 then + for i, entry in ipairs(data.dungeons) do + if type(entry) ~= "string" then + LogError("\"boards[" .. + tostring(id) .. + "].dungeons[" .. + tostring(i) .. "]\" is not a valid zone id. This may cause the library to fail.") + end + end + else + LogWarn("\"boards[" .. tostring(id) .. "].dungeons\" is empty. This will make the board never generate anything.") + end + end + end + if type(data.job_types) == "table" then + for i, entry in ipairs(data.job_types) do + if type(entry) == "table" then + if not values.job_types[entry.id] then + LogError("\"boards[" .. + tostring(id) .. + "].job_types[" .. + tostring(i) .. "].id\" is not a valid job id. This may cause the library to fail.") + end + if entry.weight then + if type(entry.weight) == "number" then + if entry.weight < 0 then + LogWarn("\"boards[" .. + tostring(id) .. + "].job_types[" .. + tostring(i) .. "].weight\" is less than 0. This may lead to unintended results.") + end + else + LogError("\"boards[" .. + tostring(id) .. + "].job_types[" .. + tostring(i) .. "].weight\" is not number or nil. This may cause the library to fail.") + end + end + else + LogError("\"boards[" .. + tostring(id) .. + "].job_types[" .. tostring(i) .. "]\" is not a table. This will cause the library to fail.") + end + end + else + LogError("\"boards[" .. + tostring(id) .. "].job_types\" is not a table. This will cause the library to fail.") + end + if not EqualToAny(type(data.condition), { "function", "nil" }) then + LogError("\"boards[" .. + tostring(id) .. "].condition\" is not a function or nil. This may cause the library to fail.") + end + else + LogError("\"boards[" .. tostring(id) .. "]\" is not a table. This will cause the library to fail.") + end + end + else + LogError("\"boards\" is not a table. This will cause the library to fail.") + end + + if not settings.extra_reward_type then + LogWarn("\"extra_reward_type\" is nil. It will default to \"none\".") + elseif type(settings.extra_reward_type) == "string" then + if not EqualToAny(settings.extra_reward_type, { "exp", "rank", "none" }) then + LogWarn("Value " .. + tostring(settings.extra_reward_type) .. + " is not a valid \"extra_reward_type\" id. It will default to \"none\".\n" .. + "Valid keywords are: \"exp\", \"rank\", \"none\"") + end + else + LogWarn("\"extra_reward_type\" is not a string. It will default to \"none\".") + end + + if not settings.mission_callback_root then + LogWarn( + "\"hidden_floor_chance\" is nil. This will cause the library to fail if the callback functionality is used at any point.") + elseif type(settings.mission_callback_root) ~= "table" then + LogWarn( + "\"hidden_floor_chance\" is not a number. This will cause the library to fail if the callback functionality is used at any point.") + end + + if not settings.end_dungeon_day_destination then + LogError("\"end_dungeon_day_destination\" is nil. This will cause the library to fail.") + elseif type(settings.end_dungeon_day_destination) == "table" then + checker.checkGroundLocationTable(settings.end_dungeon_day_destination, "end_dungeon_day_destination") + else + LogError("\"end_dungeon_day_destination\" is not a table. This will cause the library to fail.") + end + + if not EqualToAny(type(settings.after_rewards_function), {"function", "nil"}) then + LogError("\"after_rewards_function\" is not function or nil. This will cause the library to fail.") + end + + if not settings.taken_limit then + LogError("\"taken_limit\" is nil. This will cause the library to fail.") + elseif type(settings.taken_limit) == "number" then + if settings.taken_limit <= 0 then + LogWarn("\"taken_limit\" must be greater than 0.") + end + else + LogError("\"taken_limit\" is not a number. This will cause the library to fail.") + end + + if settings.taken_jobs_start_active == nil then + LogWarn("\"taken_jobs_start_active\" is nil. This may lead to unintended results.") + elseif type(settings.taken_jobs_start_active) ~= "boolean" then + LogWarn("\"taken_jobs_start_active\" is not a boolean. This may lead to unintended results.") + end + + if not settings.max_guests then + LogError("\"taken_limit\" is nil. This will cause the library to fail.") + elseif type(settings.taken_limit) ~= "number" then + LogError("\"taken_limit\" is not a number. This will cause the library to fail.") + end + + if settings.guests_take_up_space == nil then + LogWarn("\"guests_take_up_space\" is nil. This may lead to unintended results.") + elseif type(settings.guests_take_up_space) ~= "boolean" then + LogWarn("\"guests_take_up_space\" is not a boolean. This may lead to unintended results.") + end + + if not settings.min_party_limit then + LogError("\"min_party_limit\" is nil. This will cause the library to fail.") + elseif type(settings.min_party_limit) == "number" then + if settings.min_party_limit <= 0 then + LogWarn("\"min_party_limit\" is 0 or less. It will default to 1.") + end + else + LogError("\"min_party_limit\" is not a number. This will cause the library to fail.") + end + + if settings.losing_guests_means_defeat == nil then + LogWarn("\"losing_guests_means_defeat\" is nil. This may lead to unintended results.") + elseif type(settings.losing_guests_means_defeat) ~= "boolean" then + LogWarn("\"losing_guests_means_defeat\" is not a boolean. This may lead to unintended results.") + end + + local priorities = { "dungeon_boss_steps_piority", "dungeon_gen_steps_piority", "dungeon_npc_steps_piority" } + for _, priority_id in ipairs(priorities) do + local priority_data = settings[priority_id] --[[@as (integer|integer[])]] + if not priority_data then + LogError("\"" .. tostring(priority_id) .. "\" is nil. This will cause the library to fail.") + elseif type(priority_data) == "table" then + for i, num in ipairs(priority_data) do + if type(num) ~= "number" then + LogError("\"" .. + tostring(priority_id) .. + "[" .. tostring(i) .. "]\" is not a number. This will cause the library to fail.") + end + end + elseif type(priority_data) ~= "number" then + LogError("\"" .. tostring(priority_id) .. "\" is not number or table. This will cause the library to fail.") + end + end + + if not settings.guest_level_scaling then + LogError("\"guest_level_scaling\" is nil. This will cause the library to fail.") + elseif type(settings.guest_level_scaling) ~= "function" then + LogError("\"guest_level_scaling\" is not a function. This will cause the library to fail.") + end + + if not settings.outlaw_level_scaling then + LogError("\"outlaw_level_scaling\" is nil. This will cause the library to fail.") + elseif type(settings.outlaw_level_scaling) ~= "function" then + LogError("\"outlaw_level_scaling\" is not a function. This will cause the library to fail.") + end + + if not settings.apply_outlaw_changes then + LogError("\"apply_outlaw_changes\" is nil. This will cause the library to fail.") + elseif type(settings.apply_outlaw_changes) ~= "function" then + LogError("\"apply_outlaw_changes\" is not a function. This will cause the library to fail.") + end + + if not settings.fleeing_outlaw_restrictions then + LogError("\"fleeing_outlaw_restrictions\" is nil. This will cause the library to fail.") + elseif type(settings.fleeing_outlaw_restrictions) == "table" then + for i, elem in ipairs(settings.fleeing_outlaw_restrictions) do + if type(elem) == "string" then + if not _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Element]:ContainsKey(elem) then + LogError("\"" .. + tostring(settings.fleeing_outlaw_restrictions) .. + "[" .. tostring(i) .. "]\" is not a valid element id and will therefore be ignored.") + end + else + LogError("\"" .. + tostring(settings.fleeing_outlaw_restrictions) .. + "[" .. tostring(i) .. "]\" is not a string. This will cause the library to fail.") + end + end + else + LogError("\"fleeing_outlaw_restrictions\" is not a function. This will cause the library to fail.") + end + + if not settings.outlaw_music_name then + LogError("\"outlaw_music_name\" is nil. This will cause the library to fail.") + elseif type(settings.outlaw_music_name) == "string" then + --testing if the music file exists through lua seems extremely excruciating and probably not worth it. Might as well try to run it directly at this point + local s = SOUND:GetCurrentSong() + if not pcall(function() + SOUND:PlayBGM(settings.outlaw_music_name, false, 1) + SOUND:SetBGMVolume(0) + end) then + LogWarn("Value \"" .. + tostring(settings.outlaw_music_name) .. + "\" of \"outlaw_music_name\" is not a valid BGM name. This will cause errors when trying to play outlaw music.") + end + SOUND:PlayBGM(s, false, 1) + else + LogError("\"outlaw_music_name\" is not a string. This will cause the library to fail.") + end + + + if not settings.external_events then + LogError("\"external_events\" is nil. This will cause the library to fail.") + elseif type(settings.external_events) == "table" then + for i, condition in ipairs(settings.external_events) do + if type(condition) == "table" then + if type(condition.condition) ~= "function" then + LogError("\"external_events[" .. + tostring(i) .. "].condition\" is not a function. This will cause the library to fail.") + end + if not EqualToAny(type(condition.icon), { "string", "nil" }) then + LogError("\"external_events[" .. + tostring(i) .. "].icon\" is not string or nil. This will cause the library to fail.") + end + if not EqualToAny(type(condition.message_key), { "string", "nil" }) then + LogError("\"external_events[" .. + tostring(i) .. "].message_key\" is not string or nil. This will cause the library to fail.") + end + if not EqualToAny(type(condition.message_args), { "function", "nil" }) then + LogError("\"external_events[" .. + tostring(i) .. "].message_args\" is not function or nil. This will cause the library to fail.") + end + else + LogError("\"dungeon_list_pattern\" is not a table. This will cause the library to fail.") + end + end + else + LogError("\"external_events\" is not a string. This will cause the library to fail.") + end + + if not settings.external_events_icon_mode then + LogWarn("\"external_events_icon_mode\" is nil. It will default to \"ALL\".") + elseif type(settings.external_events_icon_mode) == "string" then + if not EqualToAny(settings.external_events_icon_mode, { "FIRST", "ALL" }) then + LogWarn("Value " .. + tostring(settings.external_events_icon_mode) .. + " is not a valid \"external_events_icon_mode\" id. It will default to \"ALL\".\n" .. + "Valid keywords are: \"FIRST\", \"ALL\"") + end + else + LogWarn("\"external_events_icon_mode\" is not a string. It will default to \"none\".") + end + + if not settings.dungeon_list_pattern then + LogError("\"dungeon_list_pattern\" is nil. This will cause the library to fail.") + elseif type(settings.dungeon_list_pattern) ~= "string" then + LogError("\"dungeon_list_pattern\" is not a string. This will cause the library to fail.") + end + + + LogInfo("Sanity checker terminated with " .. tostring(warns) .. " warns and " .. tostring(errors) .. " errors.") +end + +return checker diff --git a/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/missiongen_lib.lua b/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/missiongen_lib.lua new file mode 100644 index 0000000000..0a47334c87 --- /dev/null +++ b/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/missiongen_lib.lua @@ -0,0 +1,4438 @@ +-- PMDO Mission Generation Library v1.0.3, by MistressNebula +-- Main file +-- ----------------------------------------------------------------------------------------- -- +-- This is the main library file containing all functions and callbacks. +-- If you need to configure your data, please refer to missiongen_settings.lua +-- ----------------------------------------------------------------------------------------- -- +-- You will need to load this file using the require() function, and save it in a +-- global variable. You should never load this file more than once. +-- ----------------------------------------------------------------------------------------- -- +-- This library's documentation has been written using intellij idea with the sumnekolua plugin. + +--- @alias referenceSet table a generic set whose keys are strings and whose values are always ``true`` +--- @alias LibraryRootStruct {boards:table,taken:jobTable[],dungeon_progress:table>,mission_flags:table,previous_limit:integer,escort_jobs:integer} the structure of library.root +--- @alias jobTable {Client:monsterIDTable, Target:monsterIDTable|nil, Flavor:{[1]:string, [2]:string}, Title:string, Zone:string, Segment:integer, +--- Floor:integer, RewardType:rewardType, Reward1:itemTable|nil, Reward2:itemTable|nil, Type:jobType, Completion:integer, Taken:boolean, BackReference:string|nil, +--- Difficulty:integer, Item:string|nil, Special:string|nil, HideFloor:boolean, Callbacks:table, MenuOverrides:table} A table containing all properties of a job +--- @alias itemTable {id:string, count:integer|nil, hidden:string|nil} A table describing an InvItem object +--- @alias monsterIDTable {Species:string, Form:integer|nil, Skin:string|nil, Gender:integer|nil, Nickname:string|nil} A table that can be converted into a MonsterID object +--- @alias eventTable {cancel:boolean, job:jobTable, data:table} A table used to handle job events +--- @alias MonsterID {Species:string, Form:integer, Skin:string, Gender:userdata} A MonsterID object +--- @alias destTable {destinations:string[], occupied:table>>, allowed:{segment:integer, floor:integer, difficulty:string}[]} table used for randomly selecting destinations for jobs. + +-- Types used for moveset generation +--- @alias categoryType "level"|"tm"|"tutor"|"egg" types of move pools +--- @alias slotType "stab"|"coverage"|"damage"|"status" types of slots that a move can be inserted into +--- @alias synergyEntry {ApplySynergy:referenceSet, RequestSynergy:referenceSet, Type:string} a table describing the state of a possible synergy in a moveset +--- @alias moveset_entry {ID:string, Type:string, Category:slotType[], Weight:integer, ApplySynergy: string[], RequestSynergy: string[]} a table describing a move and all the properties that may influence moveset generation choices +--- @alias moveset_supertable {stab:moveset_entry[],coverage:moveset_entry[],damage:moveset_entry[],status:moveset_entry[]} a table containing lists of moveset_entries, divided by slotType +--- @alias moveset_list table a backwards reference table that links a move to the supertables and subtables that contain it + +local library = { + --- Settings data imported from missiongen_settings.lua + data = require("missiongen_lib.missiongen_settings"), + --- shortcut to the root of the saved mission data, made accessible for quick reference. + --- NEVER. EVER. CHANGE THIS VALUE. + ---@type LibraryRootStruct + root = SV +} +local menus = require("missiongen_lib.missiongen_menus") + +--- Root for global data and enum values. +local globals = {} +---@type table Gender values +globals.gender = {} +globals.gender.Unknown = -1 +globals.gender.Genderless = 0 +globals.gender.Male = 1 +globals.gender.Female = 2 +---@type table Completion values +globals.completion = {} +globals.completion.Failed = -1 +globals.completion.NotCompleted = 0 +globals.completion.Completed = 1 +---@type table Imported C# types +globals.ctypes = {} +globals.ctypes.Integer = luanet.import_type('System.Int32') +globals.ctypes.List = luanet.import_type('System.Collections.Generic.List`1') +globals.ctypes.MobSpawn = luanet.import_type('RogueEssence.LevelGen.MobSpawn') +globals.ctypes.LoadGen = luanet.import_type('RogueEssence.LevelGen.LoadGen') +globals.ctypes.ChanceFloorGen = luanet.import_type('RogueEssence.LevelGen.ChanceFloorGen') +globals.ctypes.RarityData = luanet.import_type('PMDC.Data.RarityData') +globals.ctypes.FloorNameIDZoneStep = luanet.import_type('RogueEssence.LevelGen.FloorNameIDZoneStep') +globals.ctypes.StatusPowerEvent = luanet.import_type('PMDC.Dungeon.StatusPowerEvent') +globals.ctypes.StatusStackDifferentEvent = luanet.import_type('PMDC.Dungeon.StatusStackDifferentEvent') +globals.ctypes.MajorStatusPowerEvent = luanet.import_type('PMDC.Dungeon.StatusPowerEvent') +globals.ctypes.WeatherNeededEvent = luanet.import_type('PMDC.Dungeon.WeatherNeededEvent') +globals.ctypes.ChargeOrReleaseEvent = luanet.import_type('PMDC.Dungeon.ChargeOrReleaseEvent') +globals.ctypes.GiveMapStatusEvent = luanet.import_type('PMDC.Dungeon.GiveMapStatusEvent') +globals.ctypes.StatusBattleEvent = luanet.import_type('PMDC.Dungeon.StatusBattleEvent') +globals.ctypes.AdditionalEvent = luanet.import_type('PMDC.Dungeon.AdditionalEvent') +globals.ctypes.AddContextStateEvent = luanet.import_type('PMDC.Dungeon.AddContextStateEvent') +globals.ctypes.MajorStatusState = luanet.import_type('PMDC.Dungeon.MajorStatusState') +globals.ctypes.SleepAttack = luanet.import_type('PMDC.Dungeon.SleepAttack') +---Supported reward types data +---Hardcoded properties: item1, item2, exclusive, display_key_pointer +---@type table +globals.reward_types = {} +globals.reward_types.item = {true, false, false, "REWARD_SINGLE"} +globals.reward_types.money = {false, false, false, "REWARD_SINGLE"} +globals.reward_types.item_item = {true, true, false, "REWARD_DOUBLE"} +globals.reward_types.money_item = {false, true, false, "REWARD_DOUBLE"} +globals.reward_types.client = {false, false, false, "REWARD_UNKNOWN"} +globals.reward_types.exclusive = {true, false, true, "REWARD_UNKNOWN"} +--- Supported job types data +---@type table +globals.job_types = {} +globals.job_types.RESCUE_SELF = {req_target = false, req_target_item = false, has_guest = false, target_outlaw = false, law_enforcement = false, can_hide_floor = false, boss = false} +globals.job_types.RESCUE_FRIEND = {req_target = true, req_target_item = false, has_guest = false, target_outlaw = false, law_enforcement = false, can_hide_floor = true, boss = false} +globals.job_types.ESCORT = {req_target = true, req_target_item = false, has_guest = true, target_outlaw = false, law_enforcement = false, can_hide_floor = false, boss = false} +globals.job_types.EXPLORATION = {req_target = false, req_target_item = false, has_guest = true, target_outlaw = false, law_enforcement = false, can_hide_floor = true, boss = false} +globals.job_types.DELIVERY = {req_target = false, req_target_item = true, has_guest = false, target_outlaw = false, law_enforcement = false, can_hide_floor = false, boss = false} +globals.job_types.LOST_ITEM = {req_target = false, req_target_item = true, has_guest = false, target_outlaw = false, law_enforcement = false, can_hide_floor = false, boss = false} +globals.job_types.OUTLAW = {req_target = true, req_target_item = false, has_guest = false, target_outlaw = true, law_enforcement = true, can_hide_floor = true, boss = true} +globals.job_types.OUTLAW_ITEM = {req_target = true, req_target_item = true, has_guest = false, target_outlaw = true, law_enforcement = false, can_hide_floor = true, boss = true} +globals.job_types.OUTLAW_ITEM_UNK = {req_target = true, req_target_item = true, has_guest = false, target_outlaw = true, law_enforcement = false, can_hide_floor = true, boss = false} +globals.job_types.OUTLAW_MONSTER_HOUSE = {req_target = true, req_target_item = false, has_guest = false, target_outlaw = true, law_enforcement = true, can_hide_floor = false, boss = true} +globals.job_types.OUTLAW_FLEE = {req_target = true, req_target_item = false, has_guest = false, target_outlaw = true, law_enforcement = true, can_hide_floor = true, boss = true} +--- Keywords for client and target generation +---@type table +globals.keywords = {} +globals.keywords.ENFORCER = "ENFORCER" +globals.keywords.OFFICER = "OFFICER" +globals.keywords.AGENT = "AGENT" +--- Keys for MenuOverrides +---@type table +globals.overrides = {} +globals.overrides.OBJECTIVE = "OBJ" +globals.overrides.PLACE = "DUN" +globals.overrides.DIFFICULTY = "DIF" +globals.overrides.REWARD = "REW" +--- Error types +---@type table +globals.error_types = {} +globals.error_types.DATA = "DataError" +globals.error_types.ID = "IDError" +globals.error_types.SCENE = "WrongSceneError" +globals.error_types.MAPGEN = "MapgenError" +--- Warning types +---@type table +globals.warn_types = {} +globals.warn_types.DATA = "MissingData" +globals.warn_types.FLOOR_GEN = "InvalidFloor" +globals.warn_types.ID = "MissingID" +--- Default values for various things +---@type table +globals.defaults = {} +globals.defaults.item = "food_apple" +--- Static display keys that are fetched from strings.resx +--- Look at the comment beside each key for info on their supported placeholders +---@type table +globals.keys = {} +globals.keys.RESCUE_SELF = "MENU_JOB_OBJECTIVE_RESCUE_SELF" --Objective string for jobs where you rescue the client. {0} = Client +globals.keys.RESCUE_FRIEND = "MENU_JOB_OBJECTIVE_RESCUE_FRIEND" --Objective string for jobs where you rescue the target. {0} = Client, {1} = Target +globals.keys.ESCORT = "MENU_JOB_OBJECTIVE_ESCORT" --Objective string for escort jobs. {0} = Client, {1} = Target +globals.keys.EXPLORATION = "MENU_JOB_OBJECTIVE_EXPLORATION" --Objective string for exploration jobs. {0} = Client +globals.keys.DELIVERY = "MENU_JOB_OBJECTIVE_DELIVERY" --Objective string for delivery jobs. {0} = Client, {2} = Item +globals.keys.LOST_ITEM = "MENU_JOB_OBJECTIVE_LOST_ITEM" --Objective string for lost item jobs. {0} = Client, {2} = Item +globals.keys.OUTLAW = "MENU_JOB_OBJECTIVE_OUTLAW" --Objective string for regular outlaw jobs. {0} = Client, {1} = Target, +globals.keys.OUTLAW_ITEM = "MENU_JOB_OBJECTIVE_OUTLAW_ITEM" --Objective string for stolen item outlaw jobs. {0} = Client, {1} = Target, {2} = Item +globals.keys.OUTLAW_ITEM_UNK = "MENU_JOB_OBJECTIVE_OUTLAW_ITEM_UNK" --Objective string for stolen item outlaw jobs whose target is supposed to be hidden. {0} = Client, {1} = Target, {2} = Item +globals.keys.OUTLAW_MONSTER_HOUSE = "MENU_JOB_OBJECTIVE_OUTLAW_MONSTER_HOUSE" --Objective string for outlaw monster house jobs. {0} = Client, {1} = Target +globals.keys.OUTLAW_FLEE = "MENU_JOB_OBJECTIVE_OUTLAW_FLEE" --Objective string for fleeing outlaw jobs. {0} = Client, {1} = Target +globals.keys.OBJECTIVE_DEFAULT = "MENU_JOB_OBJECTIVE_DEFAULT" --Objective string displayed when there are no jobs. +globals.keys.REACH_SEGMENT = "MENU_JOB_OBJECTIVE_REACH_SEGMENT" --Objective string displayed when there are jobs in a different segment. {0} = Segment +globals.keys.OPTION_JOBLIST = "MENU_OPTION_JOBLIST" --Name of the main menu button that displays the taken list +globals.keys.OPTION_OBJECTIVES_LIST = "MENU_OPTION_OBJECTIVES_LIST" --Name of the main menu button that displays the current objectives +globals.keys.TAKEN_TITLE = "BOARD_TAKEN_TITLE" --Name of the taken board in the board menu +globals.keys.OBJECTIVES_TITLE = "MENU_JOB_OBJECTIVES_TITLE" --Label for the button used to view mission objectives in dungeons +globals.keys.JOB_ACCEPTED = "MENU_JOB_ACCEPTED" --Displayed when viewing boards. It shows how full the taken list is. {0} Current Taken Count, {1} = Taken Limit +globals.keys.JOB_SUMMARY = "MENU_JOB_SUMMARY" --Displays the Job Summary label text +globals.keys.JOB_CLIENT = "MENU_JOB_CLIENT" --Displays the Client label text +globals.keys.JOB_OBJECTIVE = "MENU_JOB_OBJECTIVE" --Displays the Objective label text +globals.keys.JOB_PLACE = "MENU_JOB_PLACE" --Displays the Place label text +globals.keys.JOB_DIFFICULTY = "MENU_JOB_DIFFICULTY" --Displays the Difficulty label text +globals.keys.JOB_REWARD = "MENU_JOB_REWARD" --Displays the Reward label text +globals.keys.EXTRA_REWARD = "EXTRA_REWARD_AMOUNT" --Displays the Extra Reward. {0} = Amount +globals.keys.REWARD_SINGLE = "MENU_JOB_REWARD_SINGLE" --Reward string for a job with 1 reward. {0} = Reward1 +globals.keys.REWARD_DOUBLE = "MENU_JOB_REWARD_DOUBLE" --Reward string for a job with 2 rewards. {0} = Reward1, {1} = Reward2 +globals.keys.REWARD_UNKNOWN = "MENU_JOB_REWARD_UNKNOWN" --Reward string for special rewards. Meant to be hidden. {0} = Reward1 +globals.keys.BUTTON_TAKE = "MENU_JOB_TAKE" --Label for the button used to take jobs or to activate them +globals.keys.BUTTON_DELETE = "MENU_JOB_DELETE" --Label for the button used to delete taken jobs +globals.keys.BUTTON_SUSPEND = "MENU_JOB_SUSPEND" --Label for the button used to suspend jobs +--- Static display keys that are fetched from stringsEx.resx +--- Look at the comment beside each key for info on their supported placeholders +---@type table +globals.keysEx = {} +globals.keysEx.RESCUE_FOUND = "DLG_MISSION_RESCUE_FOUND" --Message for finding a rescue target and asking whether or not to warp it out. {0} = pokémon +globals.keysEx.RESCUE_CONFIRM = "DLG_MISSION_RESCUE_CONFIRM" --Message for agreeing to warp out a rescue target. {0} = pokémon +globals.keysEx.DELIVERY_FOUND = "DLG_MISSION_DELIVERY_FOUND" --Message for finding a delivery target and asking whether or not to give it the requested item. {0} = pokémon, {1} = item +globals.keysEx.DELIVERY_CONFIRM = "DLG_MISSION_DELIVERY_CONFIRM" --Dialogue from the target thanking the player for the item. {0} = item +globals.keysEx.DELIVERY_DENY = "DLG_MISSION_DELIVERY_DENY" --Dialogue from the target being sad about the player refusing to give the item. {0} = item +globals.keysEx.DELIVERY_NO_ITEM = "DLG_MISSION_DELIVERY_NO_ITEM" --Message for not having the requested item. {0} = pokémon, {1} = item +globals.keysEx.DELIVERY_NO_ITEM_CHAR = "DLG_MISSION_DELIVERY_NO_ITEM_CHAR" --Dialogue from the target being sad about the player not having the item. {0} = item +globals.keysEx.TARGET_LEFT = "DLG_MISSION_TARGET_LEFT" --Message for the target warping out after completing its mission. {0} = pokémon +globals.keysEx.ESCORT_ADD = "MISSION_ESCORT_ADD" --Message for adding one escort guest to the party {0} = guest +globals.keysEx.ESCORT_ADD_PLURAL = "MISSION_ESCORT_ADD_PLURAL" --Message for adding multiple escort guests to the party. {0} = list of guests +globals.keysEx.ESCORT_REACHED = "MISSION_ESCORT_REACHED" --Message for finding an escort target. {0} = client, {1} = target +globals.keysEx.ESCORT_THANKS = "MISSION_ESCORT_THANKS" --Dialogue from the guest thanking the player. {0} = target +globals.keysEx.ESCORT_DEPART = "MISSION_ESCORT_DEPART" --Message for client and target leaving the dungeon. {0} = client, {1} = target +globals.keysEx.ESCORT_UNAVAILABLE = "MISSION_ESCORT_UNAVAILABLE" --Message for the guest being fainted or not close enough to the target. {0} = client, {1} = target +globals.keysEx.ESCORT_FAINTED = "MISSION_ESCORT_FAINTED" --Message for the escort getting knocked out +globals.keysEx.EXPLORATION_REACHED = "MISSION_EXPLORATION_REACHED" --Message for reaching an exploration target floor. {0} = client, {1} = dungeon +globals.keysEx.EXPLORATION_THANKS = "MISSION_EXPLORATION_THANKS" --Dialogue from the guest thanking the player. {1} = dungeon +globals.keysEx.EXPLORATION_DEPART = "MISSION_EXPLORATION_DEPART" --Message for the exploration client leaving the dungeon. {0} = client, {1} = dungeon +globals.keysEx.LOST_ITEM_RETRIEVED = "DLG_MISSION_LOST_ITEM_RETRIEVED" --Message for retrieving a lost item. {0} = client, {1} = item +globals.keysEx.OUTLAW_SPAWN_FAIL = "DLG_MISSION_OUTLAW_SPAWN_FAIL" --Message for whenever an outlaw fails to spawn +globals.keysEx.OUTLAW_REACHED = "DLG_MISSION_OUTLAW_REACHED" --Message for reaching an outlaw floor. {0} = outlaw +globals.keysEx.OUTLAW_FLEE = "DLG_MISSION_OUTLAW_FLEE" --Intro dialogue for a fleeing outlaw. +globals.keysEx.OUTLAW_FLED = "DLG_MISSION_OUTLAW_FLED" --Message for a fleeing outlaw managing to escape. {0} = outlaw +globals.keysEx.OUTLAW_MONSTER_HOUSE = "DLG_MISSION_OUTLAW_MONSTER_HOUSE" --Intro dialogue for an outlaw with monster house. +globals.keysEx.OUTLAW_DEFEATED = "DLG_MISSION_OUTLAW_DEFEATED" --Message for an outlaw being defeated. {0} = outlaw +globals.keysEx.OUTLAW_MINIONS_DEFEATED = "DLG_MISSION_OUTLAW_MINIONS_DEFEATED" --Dialogue from an outlaw thanking the player +globals.keysEx.OUTLAW_BOSS_DEFEATED = "DLG_MISSION_OUTLAW_BOSS_DEFEATED" --Message for an outlaw with monster house being defeated. {0} = outlaw +globals.keysEx.OUTLAW_HOUSE_DEFEATED = "DLG_MISSION_OUTLAW_HOUSE_DEFEATED" --Message for an outlaw monster house being fully defeated. {0} = outlaw +globals.keysEx.OUTLAW_ITEM_RETRIEVED = "DLG_MISSION_OUTLAW_ITEM_RETRIEVED" --Message for retrieving the stolen item from an outlaw. {0} = outlaw, {1} = item +globals.keysEx.OUTLAW_ITEM_UNK_RETRIEVED = "DLG_MISSION_OUTLAW_ITEM_UNK_RETRIEVED" --Message for retrieving the stolen item from an outlaw before defeating the outlaw. {0} = outlaw, {1} = item +globals.keysEx.OUTLAW_ITEM_UNK_DEFEATED = "DLG_MISSION_OUTLAW_ITEM_UNK_DEFEATED" --Message for defeating an outlaw whose identity was unknown and seeing it drop the stolen item. {0} = outlaw, {1} = item +globals.keysEx.CONTINUE_ONGOING = "DLG_MISSION_CONTINUE_ONGOING" --Message that asks whether or not to stay in a dungeon when there are still more jobs. +globals.keysEx.CONTINUE_CONFIRM = "DLG_MISSION_CONTINUE_CONFIRM" --Message that asks for confirmation before leaving a dungeon when there are still more jobs. +globals.keysEx.CONTINUE_NO_ONGOING = "DLG_MISSION_CONTINUE_NO_ONGOING" --Message that asks whether or not to stay in a dungeon when there are no more jobs. +globals.keysEx.CUTSCENE_AWARD_ITEM = "MISSION_COMPLETED_CUTSCENE_AWARD_ITEM" --Message used to notify the player about the item they were awarded. {0} = team, {1} = item +globals.keysEx.CUTSCENE_AWARD_ITEM_STORAGE = "MISSION_COMPLETED_CUTSCENE_AWARD_ITEM_STORAGE" --Message used to notify the player about fact that the item they were awarded was sent to storage. {0} = team, {1} = item +globals.keysEx.CUTSCENE_AWARD_MONEY = "MISSION_COMPLETED_CUTSCENE_AWARD_MONEY" --Message used to notify the player about the sum of money they were awarded. {0} = team, {1} = money +globals.keysEx.CUTSCENE_AWARD_CHAR = "MISSION_COMPLETED_CUTSCENE_AWARD_CHAR" --Dialogue from a client that wants to join the team as the reward. {0} = team +globals.keysEx.CUTSCENE_AWARD_CHAR_PROMPT = "MISSION_COMPLETED_CUTSCENE_AWARD_CHAR_PROMPT" --Message asking the player if they want the client as a team member. {0} = team, {1} = client +globals.keysEx.CUTSCENE_AWARD_EXTRA = "MISSION_COMPLETED_CUTSCENE_AWARD_EXTRA" --Message used to notify the player about the extra reward they were awarded. {0} = team, {1} = amount +globals.keysEx.CUTSCENE_AWARD_RANK_UP = "MISSION_COMPLETED_CUTSCENE_AWARD_RANK_UP" --Message used to notify the player about a rank up. {0} = team, {1} = original rank, {2} = new rank + +library.globals = globals + +-- ----------------------------------------------------------------------------------------- -- +-- #region Data generator +-- ----------------------------------------------------------------------------------------- -- +-- Here at the top for easy access and reference, so that it is possible for modders to +-- understand how the data is structured. + +--- Loads the root of the main data structure, generating the specified nodes if necessary. +function library:load() + if _DIAG.DevMode then + local succ, checker = pcall(require, "missiongen_lib.missiongen_devcheck") + if succ then checker.check(self) end + end + + local rootpath = self.data.sv_root_name + if type(rootpath) ~= "table" then + ---@cast rootpath string[] + rootpath = { self.data.sv_root_name --[[@as string]] } + end + self.root = SV + for _, id in ipairs(rootpath) do + self.root[id] = self.root[id] or {} + self.root = SV[id] + end + self.root.mission_flags = self.root.mission_flags or {} + self:loadDifficulties() + self:generateBoards() + self:loadDungeonTable() +end + +--- Loads the difficulty data and generates forward and backwards reference lists. +function library:loadDifficulties() + self.data.num_to_difficulty = self.data.difficulty_list + self.data.difficulty_to_num = {} + for i, diff in ipairs(self.data.num_to_difficulty) do + self.data.difficulty_to_num[diff] = i + end +end + +--- Generates the job board data structures inside the SV table. +function library:generateBoards() + self.root.taken = self.root.taken or {} + self.root.boards = self.root.boards or {} + for board_id in pairs(self.data.boards) do + self.root.boards[board_id] = self.root.boards[board_id] or {} + end +end + +--- Loads the dungeon progress table. Any completed dungeons that are missing from this table will have only +--- their segment 0 marked as completed. +--- In other words, when a mod with this library is loaded for the first time, it will assume, for all dungeons, +--- that only segment 0 has been completed +function library:loadDungeonTable() + self.root.dungeon_progress = self.root.dungeon_progress or {} + local UnlockState = RogueEssence.Data.GameProgress.UnlockState + for dungeon in pairs(self.data.dungeons) do + if not self.root.dungeon_progress[dungeon] and _DATA.Save.DungeonUnlocks:ContainsKey(dungeon) then + if _DATA.Save.DungeonUnlocks[dungeon] == UnlockState.Completed then + self.root.dungeon_progress[dungeon] = {[0] = true} --default to segment 0 completed + elseif _DATA.Save.DungeonUnlocks[dungeon] == UnlockState.Discovered then + self.root.dungeon_progress[dungeon] = {[0] = false} --default to segment 0 unlocked + end + end + end +end + +--- Returns an empty job table, also known as a job template. +--- @return jobTable #a new empty job template +local jobTemplate = function() + return { + ---@type monsterIDTable MonsterID style table describing the client + Client = nil, + ---@type monsterIDTable|nil MonsterID style table describing the target. Ignored if the job type expects no target + Target = nil, + ---@type {[1]:string, [2]:string} Pair of string keys displayed when the job details are displayed. The second key is optional + Flavor = {"", ""}, + ---@type string String key displayed when browsing quest boards + Title = "", + ---@type string The id of the zone this job takes place in + Zone = "", + ---@type integer The specific segment this job takes place in + Segment = -1, + ---@type integer The destination floor of this job + Floor = -1, + ---@type string The id of the combination of rewards that will be awarded by this job + RewardType = "", + ---@type itemTable|nil Data of the first item awarded by the job. Ignored if the reward type does not include a visible item reward + Reward1 = {}, + ---@type itemTable|nil Data of the second item awarded by the job. Ignored if the reward type does not include a hidden item reward + Reward2 = {}, + ---@type string The id of the type of this job + Type = "", + ---@type integer the state of the job. -1 means failed. 0 means not completed. 1 means completed. This is always reset to 0 on day end if the job rewards are not claimed (like when an adventure is failed). + Completion = 0, + ---@type boolean Taken list: if true, the job is active. Boards: if true, the job is inside the taken list + Taken = false, + ---@type string|nil Contains the name of the board it was in. It is used only in the taken list for discarding jobs and for routing the player during the reward process. + BackReference = nil, + ---@type integer The difficulty index of this job. + Difficulty = -1, + ---@type string|nil The id of the item this job requires. Ignored if the job type requires no items. + Item = "", + ---@type string|nil special jobs can be triggered sometimes. If so, this will contain the special job category id. + Special = "", + ---@type boolean Some job types have a chance to have their floor hidden. If this is true, hide the floor. + HideFloor = false, + ---@type table table of callbacks with their respective parameters + Callbacks = {}, + ---@type table String keys to be used instead of the normal ones in various places in the Job Menu + MenuOverrides = {} + } +end + +--- Returns an empty monster id table, also known as a monster id template. +--- @return monsterIDTable #an empty MonsterID-style table +local monsterIdTemplate = function() + ---@type monsterIDTable + return { + ---@type string|nil Optional. The nickname to apply to the character + Nickname = nil, + ---@type string The species of the character + Species = "", + ---@type integer|nil Optional. The form of the character. Defaults to 0 + Form = 0, + ---@type string|nil Optional. The skin to apply to the character. Defaults to "normal" + Skin = "normal", + ---@type integer|nil Optional. The gender of the character. If absent or equal to -1, it will be rolled upon job generation + Gender = -1 + } +end + +---Initializes a new event table and associates to a specific job +---@param job jobTable the job associated to this event +---@return eventTable #an event table associated to a specific job +local newEvent = function(job) + return { + cancel = false, + job = job, + data = {} + } +end +-- ----------------------------------------------------------------------------------------- -- +-- #region Logging +-- ----------------------------------------------------------------------------------------- -- +-- Functions that write to the output log + +--- Prints a message prefixed with an error type. +--- @param error_type string the error type prefix +--- @param message string the message of the error itself +local logError = function(error_type, message) + PrintInfo("["..error_type.."] "..message) +end + +--- Same as "logError" but does not print anything outside of dev mode. +--- Used for out-of-box events that may or may not be intentional, or for +--- low-priority problems that the system can handle by itself without much issue. +--- @param warn_type string the error type prefix +--- @param message string the message of the error itself +local logWarn = function(warn_type, message) + if _DIAG.DevMode then logError(warn_type, message) end +end + +--- Debug function that prints an entire table to console. +--- @param tabl table the table to print +function library:printall(tabl) + ---@type function + local printall + printall = function(tbl, level, root) + if root == nil then print(" ") end + + if tbl == nil then print("") return end + if level == nil then level = 0 end + for key, value in pairs(tbl) do + local spacing = "" + for _=1, level*2, 1 do + spacing = " "..spacing + end + if type(value) == 'table' then + print(spacing..tostring(key).." = {") + printall(value, level+1, false) + print(spacing.."}") + else + print(spacing..tostring(key).." = "..tostring(value)) + end + end + + if root == nil then print(" ") end + end + printall(tabl) +end + +-- ----------------------------------------------------------------------------------------- -- +-- #region Misc +-- ----------------------------------------------------------------------------------------- -- +-- Miscellaneous helper functions only callable from within this file + +---Takes a list and returns a new, shuffled version of the integer pairs in the list. +---The returned list is new, meaning that tbl is left unmodified, but the items inside are not copies. +---Use deepCopy afterwards if you need to edit them. +---@param tbl any[] a table to shuffle +---@return any[] a shuffled version of the table +local shuffleTable = function(tbl, replay_sensitive) + local indices = COMMON.GetSortedKeys(tbl, true) + local shuffled = {} + for _=1, #tbl, 1 do + local index, pos = library:WeightlessRandom(indices, replay_sensitive) + table.remove(indices, pos) + table.insert(shuffled, tbl[index]) + end + return shuffled +end + +--- Rolls a MonsterForm's gender and returns it as a number. +--- @param species string the id of the species to roll the gender of +--- @param form integer the index of the form to roll the gender of +--- @return integer #a gender index number +local rollMonsterGender = function(species, form) + return library:GenderToNumber(_DATA:GetMonster(species).Forms[form]:RollGender(_DATA.Save.Rand)) +end + +--- Creates a deep copy of a table and returns it. +--- This function checks for redundant paths to avoid infinite recursion. +--- @generic T:table +--- @param tbl T the table to deep copy +--- @return T #the copy +local deepCopy = function(tbl) + local deepcopy + deepcopy = function(orig, copies) + local orig_type = type(orig) + local copy + if orig_type == 'table' then + if copies[orig] then + copy = copies[orig] + else + copy = {} + copies[orig] = copy + for orig_key, orig_value in next, orig, nil do + copy[deepcopy(orig_key, copies)] = deepcopy(orig_value, copies) + end + setmetatable(copy, deepcopy(getmetatable(orig), copies)) + end + else -- number, string, boolean, etc + copy = orig + end + return copy + end + return deepcopy(tbl, {}) +end + + +--- Creates a shallow copy of a table by simply creating a new table and copying +--- all surface level key-value pairs into it from the original. +--- @generic T : table +--- @param tbl T the table to copy +--- @return T #the copy +local shallowCopy = function(tbl) + local copy = {} + for key, value in pairs(tbl) do + copy[key] = value + end + return copy +end + +--- Sorting function used for job lists. +--- @param j1 jobTable a job table +--- @param j2 jobTable another job table +--- @return boolean true if j1 goes after j2, false otherwise +local sortJobs = function(j1, j2) + -- If one is nil and the other is not, put the nil one at the end + if not library.data.dungeon_order[j1.Zone] and library.data.dungeon_order[j2.Zone] then return true end + if library.data.dungeon_order[j1.Zone] and not library.data.dungeon_order[j2.Zone] then return false end + -- Sort by dungeon order first and foremost + if library.data.dungeon_order[j1.Zone] == library.data.dungeon_order[j2.Zone] then + -- Sort by zone alphabetically + if j1.Zone == j2.Zone then + -- Sort by segment + if j1.Segment == j2.Segment then + -- Sort by floor + return j1.Floor < j2.Floor + else + return j1.Segment < j2.Segment + end + else + return j1.Zone < j2.Zone + end + else + return library.data.dungeon_order[j1.Zone] < library.data.dungeon_order[j2.Zone] + end +end + +--- Signals the triggering of an event. If the triggering job has a callback associated with it, it will be called as well. +--- @param job jobTable the job that triggered the event +--- @param event_id eventId the event that has been triggered +--- @param extra_data? table a table that will be attached to the base event table to pass more data to the callback +--- @return eventTable #the final state of the event table +local callEvent = function(job, event_id, extra_data) + if extra_data == nil then extra_data = {} end + local evt = newEvent(job) + evt.data = extra_data + local cb = job.Callbacks[event_id] + if cb then + library.data.mission_callback_root[cb.name](evt, shallowCopy(cb.args or {})) + end + return evt +end + +---Clears all effects from a Character. +---@param char Character the Character to remove effects from. +local RemoveCharEffects = function(char) + char.StatusEffects:Clear(); + char.ProxyAtk = -1; + char.ProxyDef = -1; + char.ProxyMAtk = -1; + char.ProxyMDef = -1; + char.ProxySpeed = -1; +end + +---Displays the warp out animation for the whole party, guests included. +local warpOut = function() + local player_count = GAME:GetPlayerPartyCount() + local guest_count = GAME:GetPlayerGuestCount() + for i = 0, player_count - 1, 1 do + local player = GAME:GetPlayerPartyMember(i) + if not player.Dead then + GAME:WaitFrames(30) + local anim = RogueEssence.Dungeon.CharAbsentAnim(player.CharLoc, player.CharDir) + RemoveCharEffects(player) + TASK:WaitTask(_DUNGEON:ProcessBattleFX(player, player, _DATA.SendHomeFX)) + TASK:WaitTask(player:StartAnim(anim)) + end + end + + for i = 0, guest_count - 1, 1 do + local guest = GAME:GetPlayerGuestMember(i) + if not guest.Dead then + GAME:WaitFrames(30) + local anim = RogueEssence.Dungeon.CharAbsentAnim(guest.CharLoc, guest.CharDir) + RemoveCharEffects(guest) + TASK:WaitTask(_DUNGEON:ProcessBattleFX(guest, guest, _DATA.SendHomeFX)) + TASK:WaitTask(guest:StartAnim(anim)) + end + end +end + +--- Flow of execution specific of rescue jobs. +--- @param context any c# BattleEvent context +--- @param job jobTable the job in question +local rescueReachedFlow = function(context, job) + local targetName = context.Target:GetDisplayName(true) + UI:ChoiceMenuYesNo(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.RESCUE_FOUND):ToLocal(), targetName), false) + UI:WaitForChoice() + local use_badge = UI:ChoiceResult() + if use_badge then + --Mark mission completion flags + library.root.mission_flags.MissionCompleted = true + --Clear but remember minimap state + library.root.mission_flags.PriorMapSetting = _DUNGEON.ShowMap + _DUNGEON.ShowMap = _DUNGEON.MinimapState.None + GAME:WaitFrames(20) + library:MarkJobCompleted(job) + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.RESCUE_CONFIRM):ToLocal(), targetName)) + GAME:WaitFrames(20) + UI:SetSpeaker(context.Target) + + --different responses for special targets + UI:SetSpeakerEmotion("Normal") + local case = "_DEFAULT" + if job.Special and library.data.rescue_responses.rescue_yes[job.Special] then case = job.Special end + local caseTable = library:WeightedRandom(library.data.rescue_responses.rescue_yes[case]) --[[@as {key:string, emotion:emotionType|nil}]] + if caseTable.emotion then UI:SetSpeakerEmotion(caseTable.emotion) end + UI:WaitShowDialogue(RogueEssence.StringKey(caseTable.key):ToLocal()) + + GAME:WaitFrames(20) + UI:ResetSpeaker() + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.TARGET_LEFT):ToLocal(), targetName)) + GAME:WaitFrames(20) + -- warp out + TASK:WaitTask(_DUNGEON:ProcessBattleFX(context.Target, context.Target, _DATA.SendHomeFX)) + _DUNGEON:RemoveChar(context.Target) + GAME:WaitFrames(50) + library:AskMissionWarpOut() + else + --quickly hide the minimap for the 20 frame pause + local map_setting = _DUNGEON.ShowMap + _DUNGEON.ShowMap = _DUNGEON.MinimapState.None + GAME:WaitFrames(20) + UI:SetSpeaker(context.Target) + UI:SetSpeakerEmotion("Normal") + local case = "_DEFAULT" + if job.Special and library.data.rescue_responses.rescue_no[job.Special] then case = job.Special end + local caseTable = library:WeightedRandom(library.data.rescue_responses.rescue_no[case]) --[[@as {key:string, emotion:emotionType|nil}]] + if caseTable.emotion then UI:SetSpeakerEmotion(caseTable.emotion) end + UI:WaitShowDialogue(RogueEssence.StringKey(caseTable.key):ToLocal()) + + --change map setting back to what it was + _DUNGEON.ShowMap = map_setting + GAME:WaitFrames(20) + end +end + +--- Flow of execution specific of delivery jobs. +--- @param context any c# BattleEvent context +--- @param job jobTable the job in question +local deliveryReachedFlow = function(context, job, oldDir) + local targetName = context.Target:GetDisplayName(true) + -- Take from inventory first before held items + local inv_slot = GAME:FindPlayerItem(job.Item, false, true) + if not inv_slot:IsValid() then inv_slot = GAME:FindPlayerItem(job.Item, true, false) end + local item_name = RogueEssence.Dungeon.InvItem(job.Item):GetDisplayName() + + if inv_slot:IsValid() then + UI:ChoiceMenuYesNo(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.DELIVERY_FOUND):ToLocal(), targetName, item_name)) + UI:WaitForChoice() + local deliver_item = UI:ChoiceResult() + if deliver_item then + library.root.mission_flags.MissionCompleted = true + library:MarkJobCompleted(job) + --Clear but remember minimap state + library.root.mission_flags.PriorMapSetting = _DUNGEON.ShowMap + _DUNGEON.ShowMap = _DUNGEON.MinimapState.None + if not inv_slot.IsEquipped then + GAME:TakePlayerBagItem(inv_slot.Slot) + else + GAME:TakePlayerEquippedItem(inv_slot.Slot) + end + GAME:WaitFrames(20) + UI:SetSpeaker(context.Target) + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.DELIVERY_CONFIRM):ToLocal(), item_name)) + GAME:WaitFrames(20) + UI:ResetSpeaker() + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.TARGET_LEFT):ToLocal(), targetName)) + GAME:WaitFrames(20) + TASK:WaitTask(_DUNGEON:ProcessBattleFX(context.Target, context.Target, _DATA.SendHomeFX)) + _DUNGEON:RemoveChar(context.Target) + GAME:WaitFrames(50) + library:AskMissionWarpOut() + else --they are sad if you dont give them the item + --quickly hide the minimap for the 20 frame pause + local map_setting = _DUNGEON.ShowMap + _DUNGEON.ShowMap = _DUNGEON.MinimapState.None + GAME:WaitFrames(20) + UI:SetSpeaker(context.Target) + UI:SetSpeakerEmotion("Sad") + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.DELIVERY_DENY):ToLocal(), item_name)) + --change map setting back to what it was + _DUNGEON.ShowMap = map_setting + GAME:WaitFrames(20) + end + else + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.DELIVERY_NO_ITEM):ToLocal(), targetName, item_name)) + --quickly hide the minimap for the 20 frame pause + local map_setting = _DUNGEON.ShowMap + _DUNGEON.ShowMap = _DUNGEON.MinimapState.None + GAME:WaitFrames(20) + UI:SetSpeaker(context.Target) + UI:SetSpeakerEmotion("Sad") + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.DELIVERY_NO_ITEM_CHAR):ToLocal(), item_name)) + --change map setting back to what it was + _DUNGEON.ShowMap = map_setting + GAME:WaitFrames(20) + context.Target.CharDir = oldDir + end +end + +--- Resets the animations of all characters in the party. +local resetAnims = function() + local player_count = GAME:GetPlayerPartyCount() + local guest_count = GAME:GetPlayerGuestCount() + for i = 0, player_count - 1, 1 do + local player = GAME:GetPlayerPartyMember(i) + if not player.Dead then + local anim = RogueEssence.Dungeon.CharAnimAction() + anim.BaseFrameType = 0 --none + anim.AnimLoc = player.CharLoc + anim.CharDir = player.CharDir + TASK:WaitTask(player:StartAnim(anim)) + end + end + + for i = 0, guest_count - 1, 1 do + local guest = GAME:GetPlayerGuestMember(i) + if not guest.Dead then + local anim = RogueEssence.Dungeon.CharAnimAction() + anim.BaseFrameType = 0 --none + anim.AnimLoc = guest.CharLoc + anim.CharDir = guest.CharDir + TASK:WaitTask(guest:StartAnim(anim)) + end + end +end + +--- Returns a set of all types that cover the weaknesses of a type combination. +--- @return referenceSet #a table whose keys are element ids and whose valòues are ``true`` +local getCoverageTypes = function(types) + local all_types = _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Element]:GetOrderedKeys(true) + local weaknesses = {} + local coverage = {} + for id in luanet.each(all_types) do + local matchup = 0 + for _, id2 in ipairs(types) do + matchup = matchup + PMDC.Dungeon.PreTypeEvent.CalculateTypeMatchup(id, id2) + end + if matchup >= PMDC.Dungeon.PreTypeEvent.S_E_2 then + table.insert(weaknesses, id) + end + end + for id in luanet.each(all_types) do + for _, id2 in ipairs(weaknesses) do + local matchup = PMDC.Dungeon.PreTypeEvent.CalculateTypeMatchup(id, id2) + if matchup >= PMDC.Dungeon.PreTypeEvent.S_E then + coverage[id] = true + break + end + end + end + return coverage +end + +--- Checks for synergies a move can make and stores them in its moveset entry +--- @param skill any a RogueEssence.Data.SkillData object +--- @param entry moveset_entry the moveset entry to store data in +local synergyLookup = function(skill, entry) + local data = skill.Data + + local memory = {} + for pair in luanet.each(LUA_ENGINE:MakeList(data.BeforeTryActions)) do + if LUA_ENGINE:TypeOf(pair.Value) == luanet.ctype(globals.ctypes.WeatherNeededEvent) then + --before try actions, WeatherNeededEvent and ChargeOrReleaseEvent, request "weather " + if memory.weather and memory.weather.chargerelease and not memory.weather.request then + table.insert(entry.RequestSynergy, "weather "..pair.Value.WeatherID) + end + memory.weather = memory.weather or {} + memory.weather.request = "weather "..pair.Value.WeatherID + elseif LUA_ENGINE:TypeOf(pair.Value) == luanet.ctype(globals.ctypes.ChargeOrReleaseEvent) then + --before try actions, WeatherNeededEvent and ChargeOrReleaseEvent, request "weather " + if memory.weather and memory.weather.request and not memory.weather.chargerelease then + table.insert(entry.RequestSynergy, memory.weather.request) + end + memory.weather = memory.weather or {} + memory.weather.chargerelease = true + end + end + for pair in luanet.each(LUA_ENGINE:MakeList(data.BeforeActions)) do + if LUA_ENGINE:TypeOf(pair.Value) == luanet.ctype(globals.ctypes.AddContextStateEvent) then + --before actions, AddContextStateEvent.SleepAttack, request "user sleep" + if not pair.Value.Global and LUA_ENGINE:TypeOf(pair.Value.AddedState) == luanet.ctype(globals.ctypes.SleepAttack) then + table.insert(entry.RequestSynergy, "user sleep") + end + end + end + for pair in luanet.each(LUA_ENGINE:MakeList(data.OnActions)) do + if LUA_ENGINE:TypeOf(pair.Value) == luanet.ctype(globals.ctypes.StatusStackDifferentEvent) then + --on actions, StatusStackDifferentEvent, request "user " + table.insert(entry.RequestSynergy, "user "..pair.Value.StatusID) + elseif LUA_ENGINE:TypeOf(pair.Value) == luanet.ctype(globals.ctypes.MajorStatusPowerEvent) then + --on actions, MajorStatusPowerEvent(num>den), request " MajorStatus" + if pair.Value.Numerator > pair.Value.Denominator then + local target = "user" + if pair.Value.AffectTarget == true then target = "target" end + table.insert(entry.RequestSynergy, target.." major status") + end + end + end + for pair in luanet.each(LUA_ENGINE:MakeList(data.BeforeHits)) do + if LUA_ENGINE:TypeOf(pair.Value) == luanet.ctype(globals.ctypes.StatusPowerEvent) then + --before hits, StatusPowerEvent, request " " + local target = "user" + if pair.Value.AffectTarget == true then target = "target" end + table.insert(entry.RequestSynergy, target.." "..pair.Value.StatusID) + end + end + for pair in luanet.each(LUA_ENGINE:MakeList(data.OnHits)) do + if LUA_ENGINE:TypeOf(pair.Value) == luanet.ctype(globals.ctypes.GiveMapStatusEvent) then + --on hits, GiveMapStatusEvent, apply "weather " + table.insert(entry.ApplySynergy, "weather "..pair.Value.StatusID) + elseif LUA_ENGINE:TypeOf(pair.Value) == luanet.ctype(globals.ctypes.StatusBattleEvent) then + --on hits, StatusBattleEvent, apply " " + local target = "user" + if pair.Value.AffectTarget == true then target = "target" end + local status = _DATA:GetStatus(pair.Value.StatusID) + for state in luanet.each(status.StatusStates) do + if LUA_ENGINE:TypeOf(state) == luanet.ctype(globals.ctypes.MajorStatusState) then + table.insert(entry.ApplySynergy, target.." major status") + end + end + table.insert(entry.ApplySynergy, target.." "..pair.Value.StatusID) + elseif LUA_ENGINE:TypeOf(pair.Value) == luanet.ctype(globals.ctypes.AdditionalEvent) then + --on hits, AdditionalEvent.StatusBattleEvent, apply " " + for event in luanet.each(pair.Value.BaseEvents) do + if LUA_ENGINE:TypeOf(event) == luanet.ctype(globals.ctypes.StatusBattleEvent) then + local target = "user" + if pair.Value.AffectTarget == true then target = "target" end + local status = _DATA:GetStatus(event.StatusID) + for state in luanet.each(status.StatusStates) do + if LUA_ENGINE:TypeOf(state) == luanet.ctype(globals.ctypes.MajorStatusState) then + table.insert(entry.ApplySynergy, target.." major status") + end + end + table.insert(entry.ApplySynergy, target.." "..event.StatusID) + end + end + end + end +end + +--- Generates a list of allowed moves for a specific character, taking into account how that move can be obtained. +--- @param chara any The character to check +--- @param tm_allowed boolean If true, the tm list will be populated +--- @param tutor_allowed boolean If true, the tutor list will be populated +--- @param egg_allowed boolean If true, the egg list will be populated +--- @param blacklist referenceSet a reference set of moves that must never be picked no matter what +--- @return {all:moveset_list, level:moveset_supertable,tm:moveset_supertable,tutor:moveset_supertable,egg:moveset_supertable} +local filterMoveset = function(chara, tm_allowed, tutor_allowed, egg_allowed, blacklist) + ---@type {all:moveset_list, level:moveset_supertable,tm:moveset_supertable,tutor:moveset_supertable,egg:moveset_supertable} + local moveset_table = { + all = { + }, + level = { + stab = {}, + coverage = {}, + damage = {}, + status = {} + }, + tm = { + stab = {}, + coverage = {}, + damage = {}, + status = {} + }, + tutor = { + stab = {}, + coverage = {}, + damage = {}, + status = {} + }, + egg = { + stab = {}, + coverage = {}, + damage = {}, + status = {} + } + } + local workPhases = { + {"level", true, function(form) return form.LevelSkills end}, + {"tm", tm_allowed, function(form) return form.TeachSkills end}, + {"tutor", tutor_allowed, function(form) return form.SecretSkills end}, + {"egg", egg_allowed, function(form) return form.SharedSkills end} + } + local stages = {} + local evolutionStage = chara.BaseForm + local stageData = _DATA:GetMonster(evolutionStage.Species) + local stageForm = stageData.Forms[evolutionStage.Form] + + local types = {stageForm.Element1, stageForm.Element1} + if stageForm.Element2 ~= "" then types[2] = stageForm.Element2 end + local coverage = getCoverageTypes(types) + local attackStats = {stageForm.BaseAtk, stageForm.BaseMAtk} + while (evolutionStage:IsValid()) do + stageData = _DATA:GetMonster(evolutionStage.Species) + stageForm = stageData.Forms[evolutionStage.Form] + table.insert(stages, stageForm) + evolutionStage = RogueEssence.Dungeon.MonsterID(stageData.PromoteFrom, stageForm.PromoteForm, evolutionStage.Skin, evolutionStage.Gender); + end + + for _, form in ipairs(stages) do + for _, phase in ipairs(workPhases) do + if phase[2] then + local supertable = phase[1] + local skillList = phase[3](form) + for i=0, skillList.Count-1, 1 do + if not skillList[i].Level or skillList[i].Level<=chara.Level then + local skill = skillList[i].Skill + local value = {ID = skill, Type = "", Category = "status", Weight = 1000, ApplySynergy = {}, RequestSynergy = {}} + if not moveset_table.all[skill] and not blacklist[skill] then + local skillData = _DATA:GetSkill(skill) + value.Type = skillData.Element + local category, subtables = -1, {} + if skillData.Data.Category == RogueEssence.Data.BattleData.SkillCategory.Status then + category, subtables = 0, {"status"} + synergyLookup(skillData, value) + table.insert(moveset_table[supertable].status, value) --we save now because the other categories are damage only anyway + elseif skillData.Data.Category == RogueEssence.Data.BattleData.SkillCategory.Physical then + category, subtables = 1, {"damage"} + elseif skillData.Data.Category == RogueEssence.Data.BattleData.SkillCategory.Magical then + category, subtables = 2, {"damage"} + end + if category > 0 then + value.Category = "damage" + synergyLookup(skillData, value) + local PowerStateType = luanet.import_type('RogueEssence.Dungeon.BasePowerState') + local power = skillData.Data.SkillStates:GetWithDefault(luanet.ctype(PowerStateType)) + if power and power.Power>0 then power = power.Power else power = 40 end + local weight = power * skillData.Strikes * attackStats[category] + if types[1] == skillData.Data.Element or types[2] == skillData.Data.Element then + weight = math.floor(weight*1.5) + table.insert(subtables, "stab") + end + if coverage[skillData.Data.Element] then + table.insert(subtables, "coverage") + end + for _, subtable in ipairs(subtables) do + value.Weight = weight + table.insert(moveset_table[supertable][subtable], value) + end + end + if #subtables>0 then + moveset_table.all[skill] = {cat = subtables, tables = {supertable}, value = value} + end + else + for _, subtable in ipairs(moveset_table.all[skill].cat) do + table.insert(moveset_table[supertable][subtable], moveset_table.all[skill].value) + end + table.insert(moveset_table.all[skill].tables, supertable) + end + end + end + end + end + end + + return moveset_table +end + +local moveSelection = {} +--- Picks one stab move if the list is not empty. Otherwise, coverage gets called. +--- @param moveset_table {all:moveset_list, level:moveset_supertable,tm:moveset_supertable,tutor:moveset_supertable,egg:moveset_supertable} the moveset table +--- @param synergies table<"_Completed"|integer,synergyEntry|referenceSet> the synergy table +--- @param allowed table number of allowed moves per category +function moveSelection.stab(moveset_table, synergies, allowed) + local fallback = function() return moveSelection.coverage(moveset_table, synergies, allowed) end + return moveSelection.selectMove(moveset_table, synergies, allowed, "stab", fallback) +end + +--- Picks one coverage move if the list is not empty. Otherwise, damage gets called. +--- @param moveset_table {all:moveset_list, level:moveset_supertable,tm:moveset_supertable,tutor:moveset_supertable,egg:moveset_supertable} the moveset table +--- @param synergies table<"_Completed"|integer,synergyEntry|referenceSet> the synergy table +--- @param allowed table number of allowed moves per category +function moveSelection.coverage(moveset_table, synergies, allowed) + local fallback = function() return moveSelection.damage(moveset_table, synergies, allowed) end + return moveSelection.selectMove(moveset_table, synergies, allowed, "coverage", fallback) +end + +--- Picks one damaging move if the list is not empty. Otherwise, it picks one status move. If that list is also empty, it selects nothing. +--- @param moveset_table {all:moveset_list, level:moveset_supertable,tm:moveset_supertable,tutor:moveset_supertable,egg:moveset_supertable} the moveset table +--- @param synergies table<"_Completed"|integer,synergyEntry|referenceSet> the synergy table +--- @param allowed table number of allowed moves per category +function moveSelection.damage(moveset_table, synergies, allowed) + local pick_none = function() return "" end + local fallback = function() return moveSelection.selectMove(moveset_table, synergies, allowed, "status", pick_none) end + return moveSelection.selectMove(moveset_table, synergies, allowed, "damage", fallback) +end + +--- Picks one status move if the list is not empty. Otherwise, it picks one damaging move. If that list is also empty, it selects nothing. +--- @param moveset_table {all:moveset_list, level:moveset_supertable,tm:moveset_supertable,tutor:moveset_supertable,egg:moveset_supertable} the moveset table +--- @param synergies table<"_Completed"|integer,synergyEntry|referenceSet> the synergy table +--- @param allowed table number of allowed moves per category +function moveSelection.status(moveset_table, synergies, allowed) + local pick_none = function() return "" end + local fallback = function() return moveSelection.selectMove(moveset_table, synergies, allowed, "damage", pick_none) end + return moveSelection.selectMove(moveset_table, synergies, allowed, "status", fallback) +end + +--- Scans for possible synergies and returns a weight multiplier based on them. +--- @param data moveset_entry the data associated to a specific move +--- @param synergies table<"_Completed"|integer,synergyEntry|referenceSet> the synergy table +--- @return number #the final weight multiplier for the move +local getSynergyMultiplier = function(data, synergies) + local mult = 1 + local fulfilled = false + for _, syn_data in ipairs(synergies) do + ---@cast syn_data synergyEntry + --move allows a synergy another move requests + for _, syn in ipairs(data.ApplySynergy) do + if syn_data.RequestSynergy[syn] then + mult = mult*1.75 + break + end + end + --move requests a synergy another move allows + for _, syn in ipairs(data.RequestSynergy) do + if syn_data.ApplySynergy[syn] then + mult = mult * 1.25 + break + end + end + --discourage repeated types + if data.Category ~= "status" and syn_data.Type == data.Type then + mult = mult * 0.85 + end + end + --no move allows this synergy and this is the last move + if #synergies == 3 and #data.RequestSynergy>0 and not fulfilled then + return 0 + end + return mult +end + +--- Updates the synergy table by adding the synergies of the newly selected move. +--- @param data moveset_entry the synergy data for a move +--- @param synergies table<"_Completed"|integer,synergyEntry|referenceSet> the synergy table +local updateSynergies = function(data, synergies) + ---@type synergyEntry + local newdata = { RequestSynergy = {}, ApplySynergy = {}, Type = data.Type } + for _, syn in ipairs(data.ApplySynergy) do + -- don't do anything if the synergy was already completed + if not synergies._Completed[syn] then + for _, syn_data in ipairs(synergies) do + ---@cast syn_data synergyEntry + if syn_data.RequestSynergy[syn] then + synergies._Completed[syn] = true -- Complete the synergy if it is in requested list + end + if synergies._Completed[syn] then + syn_data.RequestSynergy = {} -- Clear synergy list. Completing one requested synergy is enough 99% of the times + end + end + if not synergies._Completed[syn] then + newdata.ApplySynergy[syn] = true -- Add to applied synergies if no picked move completed it + end + end + end + for _, syn in ipairs(data.RequestSynergy) do + -- don't do anything if the synergy was already completed + if not synergies._Completed[syn] then + for _, syn_data in ipairs(synergies) do + if syn_data.ApplySynergy[syn] then + synergies._Completed[syn] = true -- Complete the synergy if it is in applied list + end + if synergies._Completed[syn] then + syn_data.ApplySynergy[syn] = nil -- Remove from apply list of all moves + end + end + end + if synergies._Completed[syn] then -- not an else case because it might change status during the above loop + newdata.RequestSynergy = {} -- Clean requested synergy list. Completing one requested synergy is enough 99% of the times + break --RequestedSynergy is empty and it should stay that way. end the loop + end + newdata.RequestSynergy[syn] = true -- Add to requested synergies if no picked move completed it + end + table.insert(synergies, newdata) +end + +---Picks one move from the subtable if the list is not empty. Otherwise, fallback gets called +---@param moveset_table {all:moveset_list, level:moveset_supertable,tm:moveset_supertable,tutor:moveset_supertable,egg:moveset_supertable} the moveset table +---@param synergies table<"_Completed"|integer,synergyEntry|referenceSet> the synergy table +---@param allowed table number of allowed moves per category +---@param subtable slotType the slot to pick a move for +---@param fallback function function to call if the current settings would return nothing +function moveSelection.selectMove(moveset_table, synergies, allowed, subtable, fallback) + ---@type moveset_entry[] + local data_list = {} + local id_list = {} + local optional = {"tm", "tutor", "egg"} + local phases = {"level"} + for i, phase in ipairs(optional) do + if allowed[phase]>0 then table.insert(phases, optional[i]) end + end + local maxweight = 0 + for _, tbl in ipairs(phases) do + ---@type moveset_entry[] + local list = deepCopy(moveset_table[tbl][subtable]) + for _, data in ipairs(list) do + if not id_list[data.ID] then + id_list[data.ID] = true + if moveset_table.all[data.ID] then + table.insert(data_list, data) + --boost moves that would complete a synergy + data.Weight = data.Weight * getSynergyMultiplier(data, synergies) + maxweight = math.max(maxweight, data.Weight or 0) + end + end + end + end + --heavily penalize lower weights, potentially removing them completely + if #data_list>0 then + local len = #data_list + for i=1, len, 1 do + local j = len -i + 1 + local data = data_list[j] + local new_wt = data.Weight - (maxweight-data.Weight) + if new_wt<=0 then table.remove(data_list, j) + else + data.Weight = math.floor(new_wt) + end + end + end + local result + if #data_list > 0 then + result = library:WeightedRandom(data_list, true) --[[@as moveset_entry]] + updateSynergies(result, synergies) + result = result.ID + local supertables = moveset_table.all[result].tables + allowed[supertables[1]] = allowed[supertables[1]]-1 + moveset_table.all[result] = nil + else + result = fallback() + end + return result +end + +--- Returns a 2d iterator function that operates in an outwards-going, square spiral. +--- Every iteration returns a RogueElements.Loc, and an integer that's equal to the distance of the square it belongs to from the center of the spiral. +--- The function returned by this can be used to supply elements to for loops. +--- @param center {X:integer, Y:integer} +--- @param end_radius integer +--- @param start_radius? integer +--- @return fun():{X:integer, Y:integer}, integer +local iter_spiral = function(center, end_radius, start_radius) + start_radius = start_radius or 0 + local phase_patterns = { + fixd = { { var = "Y", sign = -1 }, { var = "X", sign = 1 }, { var = "Y", sign = 1 }, { var = "X", sign = -1 } }, + edit = { { var = "X", sign = 1 }, { var = "Y", sign = 1 }, { var = "X", sign = -1 }, { var = "Y", sign = -1 } } + } + local radius = start_radius + local phase = 1 --1 = top, go right, 2 = right, go down, 3 = bottom, go left, 4 = left, go up + local offset = 1 - radius + return function() + if radius == 0 then + radius, offset = 1, 0 + return center, radius + end + + if offset > radius then + phase = phase + 1 + offset = 1 - radius + end + if phase > 4 then + radius = radius + 1 + phase = 1 + offset = 1 - radius + end + ---@diagnostic disable-next-line: missing-return-value + if radius > end_radius then return end + + local pos = { X = center.X, Y = center.Y } + pos[phase_patterns.fixd[phase].var] = pos[phase_patterns.fixd[phase].var] + + (phase_patterns.fixd[phase].sign * radius) + pos[phase_patterns.edit[phase].var] = pos[phase_patterns.edit[phase].var] + + (phase_patterns.edit[phase].sign * offset) + + offset = offset + 1 + return RogueElements.Loc(pos.X, pos.Y), radius + end +end + +--- Iterates through all enemies on the floor and calculates their average level, rounded up. +--- Used to generate the level of outlaws in reset dungeons. +--- @return integer #the average level of enemies on the floor +local getFloorAverage = function() + local foes = LUA_ENGINE:MakeList(_ZONE.CurrentMap:IterateCharacters(false, true)) + local total = 0 + if foes.Count > 0 then + for char in luanet.each(foes) do + total = total + char.Level + end + return math.ceil(total / math.max(1, foes.Count)) + end + --fallback for "no enemies around" case. Use players instead + local players = _DATA.Save.ActiveTeam.Players + for char in luanet.each(players) do + total = total + char.Level + end + return math.ceil(total / players.Count) +end + +--- Returns a list of valid forms for a species. +--- @param species string the id of the species to check +--- @param flee_check boolean if true, make sure that types considered a problem for fleeing outlaws are treated as non-valid +--- @return integer[] #a list of form indexes +local validForms = function(species, flee_check) + local valid_forms = {} + local forms = _DATA:GetMonster(species).Forms + for i = 0, forms.Count - 1, 1 do + if forms[i].Released and not forms[i].Temporary then + local match = false + if flee_check then + for _, type in ipairs(library.data.fleeing_outlaw_restrictions) do + if type == forms[i].Element1 or type == forms[i].Element2 then + match = true + break + end + end + end + if not match then + table.insert(valid_forms, i) + end + end + end + return valid_forms +end + +--- Checks if a species-form pair is valid. +--- @param species string the id of the species to check +--- @param form integer the index of the form to check +--- @param flee_check boolean if true, make sure that types considered a problem for fleeing outlaws are treated as non-valid +--- @return boolean #true if the form is valid, false otherwise +local isValidForm = function(species, form, flee_check) + local formData = _DATA:GetMonster(species).Forms[form] + if not formData.Released or formData.Temporary then return false end + if flee_check then + for _, type in ipairs(library.data.fleeing_outlaw_restrictions) do + if type == formData.Element1 or type == formData.Element2 then + return false + end + end + end + return true +end + +--- Generates all ground characters required for the reward cutscene, then passes them to the provided callback. +--- @param jobIndex integer the index of the job to call the reward cutscene for +--- @param callback fun(job:jobTable, chars:any[]) +local rewardCutscene = function(jobIndex, callback) + local job = library.root.taken[jobIndex] + + local npc_count = 1 + local npcs = {} + local enforcer_mode = globals.job_types[job.Type].law_enforcement + if enforcer_mode then + npc_count = 4 + elseif globals.job_types[job.Type].req_target and not globals.job_types[job.Type].target_outlaw then + npc_count = 2 + end + local unique_officers = {} + for i = 1, npc_count, 1 do + local monsterID + if i == 1 then + if enforcer_mode then + monsterID = library.data.law_enforcement.OFFICER + else + monsterID = job.Client + end + elseif i == 2 then + monsterID = job.Target + elseif i > 2 then + monsterID = library:WeightedRandomExclude(library.data.law_enforcement.AGENT, unique_officers, false, "") --[[@as AgentIDTable]] + if monsterID.Unique then + unique_officers[monsterID] = true + end + end + ---@cast monsterID monsterIDTable + local ID = library:TableToMonsterID(monsterID) + local char = RogueEssence.Ground.GroundChar(ID, + RogueElements.Loc(0, 0), + Direction.Down, library:GetCharacterName(monsterID, true), "Job_Cutscene_Char_" .. i) + table.insert(npcs, char) + char:ReloadEvents() + GAME:GetCurrentGround():AddTempChar(char) + end + + callback(job, npcs) + + for _, char in ipairs(npcs) do + GAME:GetCurrentGround():RemoveTempChar(char) + end +end + +-- ----------------------------------------------------------------------------------------- -- +-- #region Getters +-- ----------------------------------------------------------------------------------------- -- +-- Quickly get specific data regarding jobs or boards + +--- Checks whether or not a job board exists. +--- @param board_id string the board to check +--- @return boolean #true if there are settings data assigned to the given board id, false otherwise +function library:BoardExists(board_id) return self.data.boards[board_id] ~= nil end + +--- Checks if a board is empty or not. +--- @param board_id string the id of the board to check +--- @return boolean|nil #true if there are 0 jobs inside the board, false otherwise. Returns nil if the board does not exist +function library:IsBoardEmpty(board_id) + if self:BoardExists(board_id) then return self:GetBoardCount(board_id) <= 0 end + logWarn(globals.warn_types.ID, "Board table of id \"" .. board_id .. "\" does not exist. Cannot check fullness.") +end + +--- Checks if a board is full or not. +--- @param board_id string the id of the board to check +--- @return boolean|nil #true if there are no more free job slots inside the board, false otherwise. Returns nil if the board does not exist +function library:IsBoardFull(board_id) + if self:BoardExists(board_id) then return self:GetBoardCount(board_id) >= self:GetBoardSize(board_id) end + logWarn(globals.warn_types.ID, "Board table of id \"" .. board_id .. "\" does not exist. Cannot check fullness.") +end + +--- Retrieves the number of jobs inside a board. +--- @param board_id string the id of the board to check +--- @return integer|nil #The number of jobs in the board. Returns nil if the board does not exist +function library:GetBoardCount(board_id) + if self:BoardExists(board_id) then return #self.root.boards[board_id] end + logWarn(globals.warn_types.ID, "Board table of id \"" .. board_id .. "\" does not exist. Cannot check fullness.") +end + +--- Retrieves the maximum size of a board. +--- @param board_id string the id of the board to check +--- @return integer|nil #The maximum number of jobs in the board. Returns nil if the board does not exist +function library:GetBoardSize(board_id) + if self:BoardExists(board_id) then return self.data.boards[board_id].size end + logWarn(globals.warn_types.ID, "Board table of id \"" .. board_id .. "\" does not exist. Cannot check size.") +end + +--- Checks if a board is active by running its condition check. If a board has no check, it will be always active. +--- @param board_id string the id of the board to check +--- @return boolean|nil #true if the board's condition check passes, false otherwise. Returns nil if the board does not exist +function library:IsBoardActive(board_id) + if self:BoardExists(board_id) then + if self.data.boards[board_id].condition then return self.data.boards[board_id].condition(self) + else return true end + end + logWarn(globals.warn_types.ID, "Board table of id \"" .. board_id .. "\" does not exist. Cannot check state.") +end + +--- Checks if the player's taken job list is empty or not. +--- @return boolean #true if there are 0 jobs inside the taken list, false otherwise. +function library:IsTakenListEmpty() return #self.root.taken <= 0 end + +--- Checks if the player's taken job list is full or not. +--- @return boolean #true if there are no more free job slots inside the taken list, false otherwise. +function library:IsTakenListFull() return #self.root.taken >= self:GetTakenSize() end + +--- Retrieves the number of jobs inside the taken list. +--- @return integer #The number of jobs taken +function library:GetTakenCount() return #self.root.taken end + +--- Retrieves the maximum number of jobs that can be put inside the taken list. +--- @return integer #The maximum number of jobs that can be taken +function library:GetTakenSize() return self.data.taken_limit end + +--- Checks if the given board has a job in the requested slot. +--- @param board_id string the id of the board to check +--- @param index integer? The index of the job to check. If omitted, defaults to job 1 of the board. +--- @return boolean|nil #true if the job exists, false otherwise. Returns nil if the board does not exist +function library:BoardJobExists(board_id, index) + if self:BoardExists(board_id) then return self:GetBoardCount(board_id) >= (index or 1) end + logWarn(globals.warn_types.ID, "Board table of id \"" .. board_id .. "\" does not exist. Cannot check fullness.") +end + +--- Returns a job from a slot in a specific board. +--- @param board_id string the id of the board to check +--- @param index integer? The index of the job to fetch. +--- @return jobTable? #the data table of the job at that position, or nil if there is no job there or the board does not exist. +function library:GetBoardJob(board_id, index) + if self:BoardExists(board_id) then return self.root.boards[board_id][index] end + logWarn(globals.warn_types.ID, "Board table of id \"" .. board_id .. "\" does not exist. Cannot get job.") +end + +--- Returns a job from a slot in the taken list. +--- @param index integer|nil The index of the job to fetch. +--- @return jobTable #the data table of the job at that position, or nil if there is no job there. +function library:GetTakenJob( index) return self.root.taken[index] end + +--- Checks whether or not two jobs have the same destination. +--- @param job1 jobTable a job +--- @param job2 jobTable another job +--- @return boolean #true if the jobs have the same Zone, Segment and Floor, false otherwise +function library:IsJobDestinationEqual(job1, job2) + if job1.Zone == job2.Zone and job1.Segment == job2.Segment and job1.Floor == job2.Floor then + return true + end + return false +end + +--- Checks whether or not a job's destination is already occupied by another job in the taken list. +--- @param job jobTable a job +--- @param check_equal? boolean Optional. If true, it will only return true if the job found is not equal. Defaults to false +--- @return boolean #true if the job has the same Zone, Segment and Floor as another in the taken list, false otherwise +function library:IsJobDestinationInTaken(job, check_equal) + for _, job2 in ipairs(self.root.taken) do + if self:IsJobDestinationEqual(job, job2) and (not check_equal or not self:JobsEqual(job, job2)) then return true end + end + return false +end + +--- Checks if there are completed missions to hand in. +--- @return boolean #true if there are completed missions, false otherwise +function library:HasCompletedMissions() + return self.root.mission_flags.MissionCompleted --[[@as boolean]] or false +end + +--- Checks if the provided species is registered in the game's dex. +--- @param species string a species id +--- @param obtained? boolean if true, it will only return true if the pokémon has been obtained already +--- @return boolean #true if the species is registered, false otherwise +function library:IsSpeciesRegistered(species, obtained) + if _DATA.Save.Dex:ContainsKey(species) and _DATA.Save.Dex[species] ~= RogueEssence.Data.GameProgress.UnlockState.None then + return not obtained or _DATA.Save.Dex[species] ~= RogueEssence.Data.GameProgress.UnlockState.Completed + end + return false +end + +--- Checks if the provided job type requires a target. A job whose jobtype requires a target always has its Target field filled in. +--- @param jobType jobType a job type id string +--- @return boolean #true if the job type requires a target, false otherwise +function library:JobTypeHasTarget(jobType) + return globals.job_types[jobType].req_target +end + +--- Checks if the provided job type requires a target item. A job whose jobtype requires a target item always has its Item field filled in. +--- @param jobType jobType a job type id string +--- @return boolean #true if the job type requires a target item, false otherwise +function library:JobTypeHasTargetItem(jobType) + return globals.job_types[jobType].req_target_item +end + +--- Checks if the provided job type is marked as a guest job. A guest job will have its Client joining the player as a guest. +--- @param jobType jobType a job type id string +--- @return boolean #true if the job type is marked as a guest job, false otherwise +function library:JobTypeHasGuest(jobType) + return globals.job_types[jobType].has_guest +end + +--- Checks if the provided job type is marked as an outlaw job. These job types always have a Target. +--- A job whose jobtype is marked as an outlaw job will require the player to defeat its Target to be completed. +--- @param jobType jobType a job type id string +--- @return boolean #true if the job type is marked as an outlaw job, false otherwise +function library:JobTypeIsOutlaw(jobType) + return globals.job_types[jobType].target_outlaw +end + +--- Checks if the provided job type is marked as a law enforcement job. A job whose jobtype is marked as a law enforcement. +--- job will display the law enforcement characters when processing its job completion cutscene. +--- @param jobType jobType a job type id string +--- @return boolean #true if the job type requires is marked as a law enforcement job, false otherwise +function library:JobTypeIsLawEnforcement(jobType) + return globals.job_types[jobType].law_enforcement +end + +--- Checks if two jobs are equal. It compares location, job type, client and target data to do so. +--- @param j1 jobTable a job +--- @param j2 jobTable another job +--- @return boolean #true if the objects are considered equal, false otherwise +function library:JobsEqual(j1, j2) + if j1.Zone == j2.Zone and + j1.Segment == j2.Segment and + j1.Floor == j2.Floor and + j1.Type == j2.Type and + self:CharsEqual(j1.Client, j2.Client) and + self:CharsEqual(j1.Target, j2.Target) and + j1.Item == j2.Item then + return true + end + return false +end + +--- Checks if two character tables are equal. It also checks for nicknames. +--- @param c1 monsterIDTable a character +--- @param c2 monsterIDTable another character +--- @return boolean #true if the objects are considered equal, false otherwise +function library:CharsEqual(c1, c2) + if not c1 and not c2 then return true end + if (c1 and not c2) or (c2 and not c1) then return false end + if c1.Species == c2.Species and + c1.Form == c2.Form and + c1.Skin == c2.Skin and + c1.Gender == c2.Gender and + c1.Nickname == c2.Nickname then + return true + end + return false +end + +--- Looks for a job inside a specific board, using the same criteria as ``library.JobsEqual``. +--- @param job jobTable the job to look for +--- @param board_id string|nil the table to look in. If nil, it will immediately return -1. +--- @return integer #the index of the job, or -1 if it was not found. +function library:FindJobInBoard(job, board_id) + if not board_id then return -1 end + local board = self.root.boards[board_id] + + for i, job2 in ipairs(board) do + if self:JobsEqual(job, job2) then return i end + end + return -1 +end + +--- Looks for a job inside the taken list, using the same criteria as ``library.JobsEqual``. +--- @param job jobTable the job to look for +--- @return integer #the index of the job, or -1 if it was not found. +function library:FindJobInTaken(job) + for i, job2 in ipairs(self.root.taken) do + if self:JobsEqual(job, job2) then return i end + end + return -1 +end + +--- Given a zone id and segment index, return a colored segment name. +--- This is obtained by removing the part containing the string "{0}" and wrapping everything else in orange +--- The string containing "{0}" is then returned separately, with the floor placeholder specifically wrapped in cyan +--- @param zone_id string the id of the zone to generate the string of +--- @param segment_index? number the index of the segment to generate the string of. Defaults to 0. +--- @return string, string #the name of the zone and the string format containing the floor string +function library:GetSegmentName(zone_id, segment_index) +if not segment_index then segment_index = 0 end + local segment_name = zone_id.." {0}F" + local segment = _DATA:GetZone(zone_id).Segments[segment_index] + + for step in luanet.each(segment.ZoneSteps) do + if LUA_ENGINE:TypeOf(step):IsAssignableTo(luanet.ctype(globals.ctypes.FloorNameIDZoneStep)) then + segment_name = step.Name:ToLocal() + break + end + end + + local split_name = {} + for str in string.gmatch(segment_name, "([^%s]+)") do + table.insert(split_name, str) + end + + local final_name, floor_part = '', '' + + for i = 1, #split_name, 1 do + local cur_word = split_name[i] + + --save the "floor number" part somewhere else, reconstruct the rest + local s = string.find(cur_word, '{0}', 1, true) + if s then + floor_part = cur_word + -- assume the string is done if we found the floor part + break + else + if i > 1 then + final_name = final_name..' ' + end + final_name = final_name..cur_word + end + end + + --Attach color to floor number + if floor_part ~= "" then + floor_part = string.gsub(floor_part, '{0}', '[color=#00FFFF]{0}[color]', 1) + end + + return '[color=#FFC663]'..final_name..'[color]', floor_part +end + +--- Given a monsterIDTable, return a colored string. +--- If the monsterIDTable contains a Nickname, it will be used to generate the name, and will always be in cyan. +--- @param char monsterIDTable the character data to generate the string of +--- @param no_color? boolean Optional. If true, the return string will not be colored +--- @return string #the display name of the character +function library:GetCharacterName(char, no_color) + if char then + if char.Nickname then + if no_color then return char.Nickname end + return '[color=#00FFFF]'..char.Nickname..'[color]' + elseif char.Species ~= "" then + if no_color then return _DATA:GetMonster(char.Species).Name:ToLocal() end + return _DATA:GetMonster(char.Species):GetColoredName() + end + end + local errorCause = " nil" + if char then errorCause = "n invalid" end + logError(globals.error_types.DATA, "GetCharacterName was called using a"..errorCause.." monsterIDTable.") + if no_color then return "???" end + return "[color=#FF0000]???[color]" +end + +--- Given an item id or table, it returns its colored display name. +--- Invalid ids will be displayed in red, without changes. +--- @param item string|itemTable the id of the item to generate the string of +--- @param icon boolean? Optional. If true, the name will also include the item's icon. Defaults to false +--- @return string #the display name of the item +function library:GetItemName(item, icon) + if type(item) == "string" then item = { id = item, count = 1 } end + if not _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Item]:ContainsKey(item.id) then + return STRINGS:Format("[color=#FF0000]{0}[color]", item.id); + else + item.count = item.count or 1 + local data = _DATA:GetItem(item.id) + local itemname + if icon then itemname = data:GetIconName() + else itemname = data:GetColoredName() end + + if data.MaxStack > 1 then itemname = itemname.." (" .. item.count .. ")" end + if data.UsageType == RogueEssence.Data.ItemData.UseType.Treasure then + return STRINGS:Format("[color=#6384E6]{0}[color]", itemname); --needed to recolor the number + else + return STRINGS:Format("[color=#FFCEFF]{0}[color]", itemname); + end + end +end + +--- Given a job, it returns its objective string. +--- @param job jobTable the job to generate the objective string of +--- @return string #the objective string for the job +function library:GetObjectiveString(job) + local key, client = globals.keys[job.Type], self:GetCharacterName(job.Client) + local target, item = "[color=#FF0000]TARGET[color]", "[color=#FF0000]ITEM[color]" + if job.MenuOverrides and job.MenuOverrides[globals.overrides.OBJECTIVE] then key = job.MenuOverrides[globals.overrides.OBJECTIVE] end + if job.Target then target = self:GetCharacterName(job.Target) end + if job.Item and job.Item ~= "" then item = self:GetItemName(job.Item) end + return STRINGS:FormatKey(key, client, target, item) +end + +--- Given a job, it returns its location string, complete with floor if the job doesn't hide it. +--- @param job jobTable the job to generate the location string of +--- @return string #the location string for the job +function library:GetDestinationString(job) + local zone_string, floor_string = "", "" + if job.Zone ~= "" and job.Segment>=0 then + zone_string, floor_string = self:GetSegmentName(job.Zone, job.Segment) + floor_string = STRINGS:Format(floor_string, tostring(job.Floor)) + else + logWarn(globals.warn_types.DATA, "Could not generate location string for segment "..tostring(job.Segment).." of "..job.Zone.." because it has no display name") + zone_string, floor_string = job.Zone.."["..tostring(job.Segment).."]", tostring(job.Floor).."F" + end + if job.MenuOverrides and job.MenuOverrides[globals.overrides.PLACE] then + zone_string = STRINGS:FormatKey(job.MenuOverrides[globals.overrides.PLACE]) + end + + if job.HideFloor then + return zone_string + else + return zone_string .. " " .. floor_string + end +end + +--- Given a job, it returns its difficulty string. It can also include its extra reward. +--- @param job jobTable the job to generate the difficulty string of +--- @param include_extra? boolean if true, the extra reward string is included in the returned string. Defaults to false +--- @return string #the difficulty string for the job +function library:GetDifficultyString(job, include_extra) + if job.MenuOverrides and job.MenuOverrides[globals.overrides.DIFFICULTY] then + return STRINGS:FormatKey(job.MenuOverrides[globals.overrides.DIFFICULTY]) + end + local diff_id = self:NumToDifficulty(job.Difficulty) + local key = self.data.difficulty_data[diff_id].display_key + local str = STRINGS:FormatKey(key) + if include_extra and (self.data.extra_reward_type == "exp" or self.data.extra_reward_type == "rank") then + local reward_amount = self.data.difficulty_data[self:NumToDifficulty(job.Difficulty)].extra_reward + if reward_amount>0 then str = str .. STRINGS:Format(" ({0})", reward_amount) end + end + return str +end + +--- Given a job, it returns its reward string. +--- @param job jobTable the job to generate the reward string of +--- @return string #the reward string for the job +function library:GetRewardString(job) + local reward1, reward2 = "", "[color=#FF0000]REWARD2[color]" + if globals.reward_types[job.RewardType][1] then + reward1 = self:GetItemName(job.Reward1, true) + elseif job.RewardType ~= "client" then + local diff_id = self:NumToDifficulty(job.Difficulty) + local money = self.data.difficulty_data[diff_id].money_reward + reward1 = STRINGS:FormatKey("MONEY_AMOUNT", money) + else + reward1 = self:GetCharacterName(job.Client) + end + if globals.reward_types[job.RewardType][2] then + reward2 = self:GetItemName(job.Reward2) + end + local pointer = globals.reward_types[job.RewardType][4] + local key = globals.keys[pointer] + if job.MenuOverrides and job.MenuOverrides[globals.overrides.REWARD] then + key = STRINGS:FormatKey(job.MenuOverrides[globals.overrides.REWARD]) + end + return STRINGS:FormatKey(key, reward1, reward2) +end + +---Checks if there is at least one external event happening in the specified zone. +---@param zone string the zone to run the checks on +---@return boolean #true if any check returns true, false oterwise +function library:HasExternalEvents(zone) + for _, condition_data in ipairs(self.data.external_events) do + if condition_data.condition(zone) then return true end + end + return false +end + +---Checks for all external events and returns a list of all events that returned true. +---@param zone string the zone to run the checks on +---@return {condition:fun(zone:string):(boolean), message_key:string|nil, message_args:fun(zone:string):(string[])|nil, icon:string|nil}[] #a list of all checks whose condition is fulfilled +function library:GetExternalEvents(zone) + local conditions = {} + for _, condition_data in ipairs(self.data.external_events) do + if condition_data.condition(zone) then table.insert(conditions, condition_data) end + end + return conditions +end + +-- ----------------------------------------------------------------------------------------- -- +-- #region Data converters +-- ----------------------------------------------------------------------------------------- -- +-- Functions that convert data between C# and lua representation + +--- Converts a number to the Gender it represents. Invalid numbers will be marked as Unknown. +--- @param number integer a number from -1 to 2. +--- @return userdata a RogueEssence.Data.Gender object +function library:NumberToGender(number) + local res = Gender.Unknown + if number == globals.gender.Genderless then + res = Gender.Genderless + elseif number == globals.gender.Male then + res = Gender.Male + elseif number == globals.gender.Female then + res = Gender.Female + end + return res +end + +--- Converts a Gender object to its number representation. +--- @param gender userdata a RogueEssence.Data.Gender object +--- @return integer a number between -1 and 2 +function library:GenderToNumber(gender) + local res = globals.gender.Unknown + if gender == Gender.Genderless then + res = globals.gender.Genderless + elseif gender == Gender.Male then + res = globals.gender.Male + elseif gender == Gender.Female then + res = globals.gender.Female + end + return res +end + +--- Fills out a MonsterIDTable's missing properties. +--- Form, String and Gender are optional. If absent, Gender is rolled randomly, while the others will be set to 0 and "normal" respectively. +--- @param table monsterIDTable a table formatted like so: {Species = string, [Form = int], [Skin = string], [Gender = int]} +--- @return monsterIDTable #the same table, but with all required data filled out +function library:NormalizeMonsterIDTable(table) + table.Form = table.Form or 0 + table.Skin = table.Skin or "normal" + table.Gender = table.Gender or rollMonsterGender(table.Species, table.Form) + return table +end + +--- Converts a table formatted like a MonsterID object into its c# equivalent. +--- Form, String and Gender are optional. If absent, Gender is rolled randomly, while the others will be set to 0 and "normal" respectively. +--- @param table monsterIDTable a table formatted like so: {Species = string, [Form = int], [Skin = string], [Gender = int]} +--- @return MonsterID a fully formed RogueEssence.Data.MonsterID object +function library:TableToMonsterID(table) + table.Form = table.Form or 0 + table.Skin = table.Skin or "normal" + table.Gender = table.Gender or rollMonsterGender(table.Species, table.Form) + return RogueEssence.Dungeon.MonsterID(table.Species, table.Form, table.Skin, self:NumberToGender(table.Gender)) +end + +--- Converts a RogueEssence.Data.MonsterID object to a format that is more compatible with lua and the SV table. +--- You can include a nickname property if you so wish. +--- @param monsterId MonsterID a fully formed RogueEssence.Data.MonsterID object +--- @param nickname? string Optional. A nickname to store with the MonsterId data +--- @return monsterIDTable #a table formatted like so: {Nickname = string, Species = string, Form = int, Skin = string, Gender = int} +function library:MonsterIDToTable(monsterId, nickname) + local table = monsterIdTemplate() + table.Nickname = nickname + table.Species = monsterId.Species + table.Form = monsterId.Form + table.Skin = monsterId.Skin + table.Gender = self:GenderToNumber(monsterId.Gender) + return table +end + +--- Converts a difficulty id into a numerical difficulty level. +--- @param diff string the difficulty id +--- @return integer #the difficulty level +function library:DifficultyToNum(diff) + if diff and self.data.difficulty_to_num[diff] then return self.data.difficulty_to_num[diff] end + return 1 +end + +--- Converts a numerical difficulty level into a difficulty id. +--- A difficulty level outside of range will be clamped to bring it back inside the limits. +--- It only returns nil if there are no difficulties defined at all. +--- @param num integer the difficulty level +--- @return string #the difficulty id +function library:NumToDifficulty(num) + num = math.min(math.max(1,num), #self.data.num_to_difficulty) + return self.data.num_to_difficulty[num] +end + +-- ----------------------------------------------------------------------------------------- -- +-- #region Randomization +-- ----------------------------------------------------------------------------------------- -- +-- Functions for randomization purposes + +--- Returns a randomly chosen element of the given list. +--- Elements must have a "weight" property, otherwise their weight will default to 1. +--- @generic T:table +--- @param list T[] the list of elements to roll. +--- @param replay_sensitive? boolean if true, this function will use a replay-safe rng function. Defaults to false. +--- @return T|nil, number|nil #the element extracted and its index in the list, or nil, nil if the list was empty +function library:WeightedRandom(list, replay_sensitive) + local entry, index = self:WeightedRandomExclude(list, {}, replay_sensitive) + return entry, index +end + +--- Returns a randomly chosen element of the given list, excluding any key in the exclude table. +--- Elements must have a "weight" property, otherwise their weight will default to 1. +--- @generic T:table +--- @param list T[] the list of elements to roll. +--- @param exclude any[] a table whose keys are the ids of the elements to exclude from the roll, and the value can be anything except "nil" and "false" +--- @param replay_sensitive? boolean if true, this function will use a replay-safe rng function. Defaults to false. +--- @param alt_id? string name of the id property, in case "id" isn't good enough. Use an empty string to require the object itself to be equal instead. +--- @return T|nil, number|nil #the element extracted and its index in the list, or nil, nil if the final list was empty +function library:WeightedRandomExclude(list, exclude, replay_sensitive, alt_id) + local id = alt_id or "id" + local weight = 0 + for _, element in ipairs(list) do + local match = element + if id ~= "" then match = element[id] end + if not exclude[match] then + local elem_weight = element.weight or element.Weight + if elem_weight + then weight = weight + elem_weight + else weight = weight + 1 + end + end + end + if weight <= 0 then return end + local roll = -1 + if replay_sensitive + then roll = _DATA.Save.Rand:Next(weight)+1 --this rng getter includes 0 but doesn't include the max value so +1 it is + else roll = math.random(1, weight) + end + + weight = 0 + for i, element in ipairs(list) do + local match = element + if id ~= "" then match = element[id] end + if not exclude[match] then + if element.weight + then weight = weight + element.weight + else weight = weight + 1 + end + if weight >= roll then return element, i end + end + end + return list[#list], #list -- should never hit, but just in case, return last +end + +--- Returns a randomly chosen element of the given list. +--- All elements have the same chance of being returned. +--- @generic T:any +--- @param list T[] the list of elements to roll. +--- @param replay_sensitive? boolean if true, this function will use a replay-safe rng function. Defaults to false. +--- @return T|nil, number|nil #the element extracted and its index in the list, or nil, nil if the list was empty +function library:WeightlessRandom(list, replay_sensitive) + if #list == 0 then return end + local roll = -1 + if replay_sensitive + then roll = _DATA.Save.Rand:Next(#list)+1 --this one includes 0 but doesn't include the max value so +1 it is + else roll = math.random(1, #list) + end + return list[roll], roll +end + +--- Returns a randomly chosen element of the given list, excluding any key in the exclude table. +--- All elements have the same chance of being returned. +--- @generic T:any +--- @param list T[] the list of elements to roll. +--- @param exclude T[] a table whose keys are the the elements to exclude from the roll, and the value can be anything except "nil" and "false" +--- @param replay_sensitive? boolean if true, this function will use a replay-safe rng function. Defaults to false. +--- @return T|nil, number|nil #the element extracted and its index in the list, or nil, nil if the final list was empty +function library:WeightlessRandomExclude(list, exclude, replay_sensitive) + local num = 0 + for _, element in pairs(exclude) do + if not exclude[element] then + num = num + 1 + end + end + if num <= 0 then return nil, nil end + local roll = -1 + if replay_sensitive + then roll = math.random(1, num) + else roll = _DATA.Save.Rand:Next(num)+1 --this rng getter includes 0 but doesn't include the max value so +1 it is + end + + num = 0 + for i, element in ipairs(list) do + if not exclude[element] then + num= num + 1 + if num >= roll then return element, i end + end + end + return list[#list], #list -- should never hit, but just in case, return last +end + +-- ----------------------------------------------------------------------------------------- -- +-- #region Library Hooks +-- ----------------------------------------------------------------------------------------- -- +-- Core library functions, intended to be used in scripts + +--- Sorts the taken jobs list. +function library:SortTaken() + table.sort(self.root.taken, sortJobs) +end + +--- Sorts all job boards. +function library:SortBoards() + for board in pairs(self.root.boards) do self:SortBoard(board) end +end + +--- Sorts a specific job board. +--- @param board_id string the id of the board to be sorted +function library:SortBoard(board_id) + if not self.root.boards[board_id] then + logWarn(globals.warn_types.ID, "Board table of id \""..board_id.."\" does not exist. Cannot sort") + else + table.sort(self.root.boards[board_id], sortJobs) + end +end + +--- Resets all boards and regenerates their contents. +--- Called on day end. +function library:UpdateBoards() + self:loadDungeonTable() + self:FlushBoards() + self:PopulateBoards() + self:SortBoards() +end + +--- Clears all boards. Boards that are not supposed to exist will be deleted. +function library:FlushBoards() + for board in pairs(self.root.boards) do + self:FlushBoard(board) + end +end + +--- Fills all empty slots in all boards. +--- This function does not flush boards before it tries to fill them. +function library:PopulateBoards() + local dest_data = self:CompileDestinationData() + for board in pairs(self.data.boards) do + if library:IsBoardActive(board) then + self:PopulateBoard(board, dest_data) + end + end +end + +--- Clears the requested board. It will throw an error if the board does not exist. +--- @param board_id string the id of the board to be flushed +function library:FlushBoard(board_id) + if self.data.boards[board_id] == nil then + --delete because it shouldn't exist + self.root.boards[board_id] = nil + else + self.root.boards[board_id] = {} + end +end + +--- Checks if the floor requested is occupied by any already generated job. +--- It does so by checking a floors_occupied table, if provided, otherwise it checks the various job tables. +--- @param zone string the zone id +--- @param segment integer the 0-based segment index +--- @param floor integer the 1-based floor number +--- @param floors_occupied? table>> the destination map returned by ```library:GetFlorsOccupied()```. If missing, it will be generated on the fly. +--- @return boolean #true if the destination data is present in the occupied map, false otherwise +function library:IsDestinationOccupied(zone, segment, floor, floors_occupied) + if not floors_occupied then + for _, job in ipairs(self.root.taken) do + if self:IsJobDestinationEqual(job, { Zone = zone, Segment = segment, Floor = floor }) then return true end + end + for _, board in pairs(self.root.boards) do + for _, job in ipairs(board) do + if self:IsJobDestinationEqual(job, { Zone = zone, Segment = segment, Floor = floor }) then return true end + end + end + return false + end + if not floors_occupied[zone] or not floors_occupied[zone][segment] or not floors_occupied[zone][segment][floor] then + return false + end + return true +end + +--- Returns a multi-layer map of all destinations that have a job associated to them. +--- The resulting data structure must be accessed in the order of zone, segment and then floor. +---@return table>> #the destination map. +function library:GetFloorsOccupied() + local floors_occupied = {} + for _, job in ipairs(self.root.taken) do + floors_occupied[job.Zone] = floors_occupied[job.Zone] or {} + floors_occupied[job.Zone][job.Segment] = floors_occupied[job.Zone][job.Segment] or {} + floors_occupied[job.Zone][job.Segment][job.Floor] = true + end + for _, board in pairs(self.root.boards) do + for _, job in ipairs(board) do + floors_occupied[job.Zone] = floors_occupied[job.Zone] or {} + floors_occupied[job.Zone][job.Segment] = floors_occupied[job.Zone][job.Segment] or {} + floors_occupied[job.Zone][job.Segment][job.Floor] = true + end + end + return floors_occupied +end + +--- Compiles a list of valid destinations in the form of zone ids. This is more of an approximation than a perfect list, as it does not +--- take into account occupied and invalid floors. +--- @return string[] #a list of zone ids. +function library:GetValidZones() + local zones = {} + for zone, zone_data in pairs(self.data.dungeons) do + if self.root.dungeon_progress[zone] ~= nil and not self:HasExternalEvents(zone) then --skip segments that are locked by external conditons + for segment, segment_data in pairs(zone_data) do + if self.root.dungeon_progress[zone][segment] ~= nil and + (self.root.dungeon_progress[zone][segment] or not segment_data.must_end) then + table.insert(zones, zone) + break + end + end + end + end + return zones +end + +--- Checks if a zone has any valid destinations at all. Floors with fixed generation plans are ignored, as well as any floors already taken by another job. +--- You can pass a pre-generated floors_occupied table for greater efficiency in case you need to use it in multiple places. +--- @param zone string the zone to check for +--- @param floors_occupied? table>> the destination map returned by ```library:GetFlorsOccupied()```. If missing, it will be generated on the fly. +--- @return boolean #true if the dungeon has even just one valid floor destination, false otherwise. +function library:ZoneHasValidDestinations(zone, floors_occupied) + if not floors_occupied then + floors_occupied = self:GetFloorsOccupied() + end + local data_zone = _DATA:GetZone(zone) + local zone_data = self.data.dungeons[zone] + for segment, segment_data in pairs(zone_data) do + local zone_segment = data_zone.Segments[segment] + if self.root.dungeon_progress[zone][segment] ~= nil and + (self.root.dungeon_progress[zone][segment] or not segment_data.must_end) then + local sections = segment_data.sections + for i = sections[1].start, segment_data.max_floor, 1 do + if i > 0 and i <= zone_segment.FloorCount then + if not self:IsDestinationOccupied(zone, segment, i, floors_occupied) then + local map_gen = zone_segment:GetMapGen(i - 1) + local type = LUA_ENGINE:TypeOf(map_gen) + if not (type:IsAssignableTo(luanet.ctype(globals.ctypes.LoadGen)) or type:IsAssignableTo(luanet.ctype(globals.ctypes.ChanceFloorGen))) then + return true + end + end + end + end + end + end + return false +end + +--- Generates a list of all valid destinations within a zone. It takes into account all segments that have been registered for it in the settings file. +--- Floors with fixed generation plans are ignored, as well as any floors already taken by another job. +--- You can pass a pre-generated floors_occupied table for greater efficiency in case you need to use it in multiple places. +--- @param zone string the zone to compile the destination list for +--- @param floors_occupied? table>> the destination map returned by ```library:GetFlorsOccupied()```. If missing, it will be generated on the fly. +--- @return {segment:integer, floor:integer, difficulty:string}[] +function library:GetValidDestinationsInZone(zone, floors_occupied) + if not floors_occupied then + floors_occupied = self:GetFloorsOccupied() + end + local data_zone = _DATA:GetZone(zone) + local dests = {} + local zone_data = self.data.dungeons[zone] + for segment, segment_data in pairs(zone_data) do + local zone_segment = data_zone.Segments[segment] + if self.root.dungeon_progress[zone][segment] ~= nil and + (self.root.dungeon_progress[zone][segment] or not segment_data.must_end) then + local sections = segment_data.sections + local n, section = 1, sections[1] + for i = sections[1].start, segment_data.max_floor, 1 do + if i > 0 and i <= zone_segment.FloorCount then + while sections[n + 1] and sections[n + 1].start <= i do n, section = n + 1, sections[n + 1] end + if not self:IsDestinationOccupied(zone, segment, i, floors_occupied) then + local map_gen = zone_segment:GetMapGen(i - 1) + local type = LUA_ENGINE:TypeOf(map_gen) + if type:IsAssignableTo(luanet.ctype(globals.ctypes.LoadGen)) or type:IsAssignableTo(luanet.ctype(globals.ctypes.ChanceFloorGen)) then + logWarn(globals.warn_types.FLOOR_GEN, "Jobs cannot be generated on " .. zone .. ", " .. segment .. ", " .. i .. " because it is of type \"" .. type.FullName .. "\"") + else + table.insert(dests, { segment = segment, floor = i, difficulty = section.difficulty }) + end + end + end + end + end + end + return dests +end + +--- Initializes a destination table by calling ``library:GetValidZones`` and ``library.GetFloorsOccupied``, +--- but leaves the "allowed" table empty for more efficient use later on. +--- @return destTable the table that will be used for destination selection. +function library:CompileDestinationData() + return { + destinations = self:GetValidZones(), + occupied = self:GetFloorsOccupied(), + allowed = {} --[[@as table]] + } +end + +--- Given a destination data list and a list of zone ids, it returns the intersection between the valid zones and the requested ones. +--- If the zone id list is nil, it returns the list of valid zone indices, without filtering. +---@param dest_data destTable List of possible destination zones. +---@param zones string[]|table|nil List or set of zone ids to filter +---@return {zone:string, index:integer}[] the list of filtered zones, paired with their original dest_data index +function library:FilterDestinationData(dest_data, zones) + if not dest_data then return {} end + if zones == nil then zones = dest_data.destinations end + ---@type table + local indexer = {} + if zones[1] == nil then -- assume it's already a set if integer indexing doesn't work + --- @cast zones table + indexer = zones + else + --- @cast zones string[] + for _, zone in ipairs(zones) do indexer[zone] = true end + end + local filtered = {} + for i, zone_id in ipairs(dest_data.destinations) do + if indexer[zone_id] then + table.insert(filtered, {zone = zone_id, index = i}) + end + end + return filtered +end + +--- Counts how many jobs with guests have been generated for each dungeon. +--- It counts jobs in the taken list and in all boards. +--- @return table #a table whose keys are zone ids and whose values are the number of guest jobs present in that dungeon +function library:GetDungeonsGuestCount() + local guest_count = {} + for _, job in ipairs(self.root.taken) do + if globals.job_types[job.Type].has_guest then + guest_count[job.Zone] = guest_count[job.Zone] or 0 + guest_count[job.Zone] = guest_count[job.Zone] +1 + end + end + for _, board in pairs(self.root.boards) do + for _, job in ipairs(board) do + --taken jobs are gonna be in the taken list above already, so they shouldn't be counted again + if not job.Taken and globals.job_types[job.Type].has_guest then + guest_count[job.Zone] = guest_count[job.Zone] or 0 + guest_count[job.Zone] = guest_count[job.Zone] +1 + end + end + end + return guest_count +end + +--- Tries to fill up as many empty slots as possible in the given board. It will keep going from wherever the board was at when this function was called. +--- It is recommended to flush the board beforehand to ensure it is actually generating new jobs. +--- @param board_id string the id of the board to generate jobs for +--- @param dest_data destTable List of possible destination zones. It also gets updated in-place for coherent and quick reuse. If absent, it will be generated in-place. +function library:PopulateBoard(board_id, dest_data) + local data = self.data.boards[board_id] + if not data then + logError(globals.error_types.ID, "Board with id \"" .. board_id .. "\" does not exist. Cannot generate quests.") + return + end + self.root.boards[board_id] = self.root.boards[board_id] or {} + if not dest_data then dest_data = self:CompileDestinationData() end + local filtered_destinations = library:FilterDestinationData(dest_data, self.data.boards[board_id].dungeons) + local guest_count = self:GetDungeonsGuestCount() + + while (self:GetBoardCount(board_id) < self:GetBoardSize(board_id)) do + + -- choose destination + if #filtered_destinations <= 0 then break end + local zone_struct, struct_index = self:WeightlessRandom(filtered_destinations) + ---@cast zone_struct {zone:string, index:integer} because we already checked there is at least 1 possible result + local zone = zone_struct.zone + + if not dest_data.allowed[zone] then dest_data.allowed[zone] = self:GetValidDestinationsInZone(zone, dest_data.occupied) end + if #dest_data.allowed[zone] <= 0 then + --clean up and try again if there are no valid destinations in this dungeon + dest_data.allowed[zone] = nil + table.remove(dest_data.destinations, zone_struct.index) + table.remove(filtered_destinations, struct_index) + for _, struct in ipairs(filtered_destinations) do + if struct.index > zone_struct.index then struct.index = struct.index - 1 end + end + else + local dest2, dest2_index = self:WeightlessRandom(dest_data.allowed[zone]) + ---@cast dest2 {segment:integer, floor:integer, difficulty:string} because we already checked there is at least 1 possible result + local segment, floor = dest2.segment, dest2.floor + local difficulty = self:DifficultyToNum(dest2.difficulty) + + -- choose job type and finalize difficulty adjustments + local possible_job_types = {} + for i = #self.data.boards[board_id].job_types, 1, -1 do + local job_type_entry = self.data.boards[board_id].job_types[i] + local job_type_properties = self.data.job_types[job_type_entry.id] + if not globals.job_types[job_type_entry.id] then + logError(globals.error_types.DATA, "\"" .. job_type_entry.id .. "\" in job board \"" .. board_id .. "\" is not a valid job type and will be ignored.") + table.remove(self.data.boards[board_id].job_types, i) + elseif not job_type_properties then + logError(globals.error_types.DATA, "\"" .. job_type_entry.id .. "\" in job board \"" .. board_id .. "\" has no settings associated to it and will be ignored.") + table.remove(self.data.boards[board_id].job_types, i) + else + local min_rank = job_type_properties.min_rank + local min_difficulty + if min_rank then + min_difficulty = self:DifficultyToNum(min_rank) + else + min_difficulty = 0 + end + min_difficulty = min_difficulty - (job_type_properties.rank_modifier or 0) + local add = true + if min_difficulty > difficulty then add = false end + if globals.job_types[job_type_entry.id].has_guest and guest_count[zone] and guest_count[zone] >= self.data.max_guests then + add = false + end + if add then + local final_entry = { id = job_type_entry.id, weight = job_type_entry.weight } + if self.data.dungeon_job_modifiers[zone] and self.data.dungeon_job_modifiers[zone][job_type_entry.id] then + final_entry.weight = math.ceil(final_entry.weight * self.data.dungeon_job_modifiers[zone][job_type_entry.id]) + end + if final_entry.weight > 0 then + table.insert(possible_job_types, final_entry) + end + end + end + end + if #possible_job_types <= 0 then + --weed out any floor where no jobs can spawn EVER because of the difficulty constraints. + for i, elem in ipairs(dest_data.allowed[zone]) do + if self:DifficultyToNum(elem.difficulty) <= difficulty then + table.remove(dest_data.allowed[zone], i) + end + end + if #dest_data.allowed[zone] <= 0 then + --clean up if empty, then try again + dest_data.allowed[zone] = nil + table.remove(dest_data.destinations, zone_struct.index) + table.remove(filtered_destinations, struct_index) + for _, struct in ipairs(filtered_destinations) do + if struct.index > zone_struct.index then struct.index = struct.index - 1 end + end + end + else + local job_type = self:WeightedRandom(possible_job_types).id --[[@as string]] --it is safe because we already checked there is at least 1 possible result + local newJob = self:MakeNewJob(zone, segment, floor, job_type) + if not newJob then return end --abort if failed + self:AddJobToBoard(board_id, newJob) + if globals.job_types[newJob.Type].has_guest then guest_count[zone] = (guest_count[zone] or 0)+1 end + dest_data.occupied[zone] = dest_data.occupied[zone] or {} + dest_data.occupied[zone][segment] = dest_data.occupied[zone][segment] or {} + dest_data.occupied[zone][segment][floor] = true + table.remove(dest_data.allowed[zone], dest2_index) + if #dest_data.allowed[zone] <= 0 then + -- clean up if this dungeon is full + dest_data.allowed[zone] = nil + table.remove(dest_data.destinations, zone_struct.index) + table.remove(filtered_destinations, struct_index) + for _, struct in ipairs(filtered_destinations) do + if struct.index > zone_struct.index then struct.index = struct.index - 1 end + end + end + end + end + end +end + +--- Checks if the given job's type requires a target item. If that's the case, it rolls a target item and stores it in the job's table. +---@param job jobTable the job to give a target item to +local rollTargetItem = function(job) + if globals.job_types[job.Type].req_target_item then + if library.data.target_items[job.Type] and #library.data.target_items[job.Type] > 0 then + job.Item = library:WeightlessRandom(library.data.target_items[job.Type]) + else + logError(globals.error_types.DATA, "No target items associated to job type \"" .. job.Type .. "\". Setting target to \"" .. globals.defaults.item .. "\"") + job.Item = globals.defaults.item + end + end +end + +--- Checks if the given job's type is allowed to hide its target floor. If that's the case, it decides whether or not to do so and stores the result of the roll in the job's table. +---@param job jobTable the job to decide whether or not to hide the floor of +local rollHiddenFloor = function(job) + if globals.job_types[job.Type].can_hide_floor then + if math.random() < library.data.hidden_floor_chance then job.HideFloor = true end + end +end + +--- Checks if the given job's type has one or more special cases assigend to it. If that's the case, it decides whether or not to select one and then rolls on the available special case list. +--- Finally, it stores the resulting special type in the job's table. +---@param job jobTable the job to give a special type to +local rollSpecial = function(job) + if library.data.special_jobs[job.Type] and #library.data.special_jobs[job.Type]>0 then + if math.random() < library.data.special_chance then + job.Special = library:WeightlessRandom(library.data.special_jobs[job.Type]) + end + end +end + +--- Rolls the characters involved in the job, and normalizes their monsterid tables. It always chooses a client, but only selects a target if the job type requires it. +--- Law enforcement job types will always have an enforcer character as their client. +--- If the job has a special case assigned, this function will roll one of the scenarios assigned to it and store client, target and target item in the job's table, +--- ignoring the default values provided. +--- The values calculated ny this function are stored in the job's table. +--- @param job jobTable the job to assign characters to +--- @param client? string|monsterIDTable Optional default client +--- @param target? string|monsterIDTable Optional default target +local rollCharacters = function(job, client, target) + ---@type (string|monsterIDTable)[] + local characters = {} + local difficulty_id = library:NumToDifficulty(job.Difficulty) + local tier = library:WeightedRandom(library.data.difficulty_to_tier[difficulty_id]).id + + if job.Special and job.Special ~= "" then + local special_data = library:WeightlessRandom(library.data.special_data[job.Special--[[@as string]]][tier]) + if special_data then + if not job.Client or not job.Target then + characters[1] = special_data.client + characters[2] = special_data.target + end + if special_data.item then job.Item = special_data.item end --overwrite + job.Flavor[1] = special_data.flavor + end + end + + characters = { + characters[1] or client, + characters[2] or target, + } + + if not (characters[1] and characters[2]) then + local flee_check = job.Type == "OUTLAW_FLEE" + local pool = library.data.pokemon[tier] + + --roll on the extracted pool + local slots = { "client", "target" } + for i = 1, 2, 1 do + if i == 2 and not globals.job_types[job.Type].req_target then break end + while not characters[i] do + if i == 1 and globals.job_types[job.Type].law_enforcement then + characters[i] = globals.keywords.ENFORCER + else + local result, res_index = library:WeightlessRandom(pool) + if type(result) == "table" then + if isValidForm(result.Species, result.Form, flee_check) then + characters[i] = result + else + table.remove(pool, res_index) + end + elseif result then + ---@cast result string + if #validForms(result, flee_check) > 0 then + characters[i] = { + Species = result, + Form = library:WeightlessRandom(validForms(result, flee_check)) or 0 + } --[[@as monsterIDTable]] + else + table.remove(pool, res_index) + end + else + error("[" .. globals.error_types.DATA .. "] Could not select pokémon for tier \"" .. tier .. "\", job type \"" .. job.Type .. "\"") + end + end + end + end + for i = 1, 2, 1 do + --at the end of the last process we either filled all that needed to be filled, or we errored out + if characters[i] then + -- parse law enforcement keywords + if characters[i] == globals.keywords.ENFORCER then + local roll = library:WeightedRandom(library.data.enforcer_chance[tier]) + if not roll then + error("[" .. globals.error_types.DATA .. "] Setting \"enforcer_chance\" has no possible value for tier \"" .. tier .. "\"") + break + end + if roll.id == globals.keywords.OFFICER then + characters[i] = library.data.law_enforcement.OFFICER --[[@as monsterIDTable]] + elseif roll.id == globals.keywords.AGENT and roll.index and roll.index > 0 and roll.index <= #library.data.law_enforcement.AGENT then + characters[i] = library.data.law_enforcement.AGENT[roll.index] --[[@as monsterIDTable]] + else + characters[i] = roll.id + end + end + if characters[i] == globals.keywords.AGENT then + characters[i] = library:WeightedRandom(library.data.law_enforcement.AGENT) --[[@as monsterIDTable]] + end + ---@cast characters monsterIDTable[] + --finalize tables + if type(characters[i]) ~= "table" then + error("[" .. globals.error_types.DATA .. "] Could not generate job " .. slots[i] .. ". Its final value was: " .. tostring(characters[i])) + end + end + end + end + ---@cast characters monsterIDTable[] + job.Client = library:NormalizeMonsterIDTable(characters[1]) + if globals.job_types[job.Type].req_target then job.Target = library:NormalizeMonsterIDTable(characters[2]) end +end + +--- Rolls reward types and items for the given job. If a reward type is provided, it wll be used directly, and any reward list will be ignored. +--- If a reward item list is provided but not a reward type, the reward type will be inferred using the list. +--- The values calculated by this function are stored in the job's table. +--- @param job jobTable the job to assign reward data to +--- @param type? rewardType Optional default reward type +--- @param items? {[1]:string|itemTable, [2]:string|itemTable} Optional reward items list. Ignored if type is set +local setupRewards = function(job, type, items) + local xcl = false + if type then + job.RewardType = type + items = {} + elseif items then --and not type + local combo = 0 + if items[1] then combo = combo + 1 end + if items[2] then combo = combo + 2 end + if combo == 0 then + job.RewardType = "money" + elseif combo == 1 then + xcl = _DATA:GetItem(items[1].id).UsageType == RogueEssence.Data.ItemData.UseType.Treasure + if xcl then + job.RewardType = "exclusive" + else + job.RewardType = "item" + end + elseif combo == 2 then + job.RewardType = "money_item" + elseif combo == 3 then + job.RewardType = "item_item" + end + else + local reward_type + local possible_reward_types = {} + for i = #library.data.reward_types, 1, -1 do + local reward_type_entry = library.data.reward_types[i] + if not globals.reward_types[reward_type_entry.id] then + logError(globals.error_types.DATA, "\"" .. reward_type_entry.id .. "\" is not a valid reward type and will be ignored.") + table.remove(globals.reward_types, i) + elseif not reward_type_entry.min_rank or job.Difficulty >= library:DifficultyToNum(reward_type_entry.min_rank) + and (not library:JobTypeIsLawEnforcement(job.Type) or reward_type_entry.id ~= "client") + and ((reward_type_entry.id ~= "client" and reward_type_entry.id ~= "exclusive") or library:IsSpeciesRegistered(job.Client.Species)) then + table.insert(possible_reward_types, reward_type_entry) + end + end + if #possible_reward_types <= 0 then + logError(globals.error_types.DATA, "No possible reward types for quest difficulty \"" .. library:NumToDifficulty(job.Difficulty) .. "\". Setting to \"money\"") + reward_type = "money" + else + reward_type = library:WeightedRandom(possible_reward_types).id --[[@as string]] --it is safe because we already checked there is at least 1 possible result + end + job.RewardType = reward_type + items = {} + end + + --type has been set up. fill in item slots + local difficulty_id = library:NumToDifficulty(job.Difficulty) + for i = 1, 2, 1 do + if globals.reward_types[job.RewardType][i] then + if globals.reward_types[job.RewardType][3] and not xcl then -- if reward type specifies exclusive and it's not set yet + local rarityData = _DATA.UniversalData:Get(luanet.ctype(globals.ctypes.RarityData)) + local possibleItems = {} + local species = job.Client.Species + if globals.job_types[job.Type].law_enforcement then + species = job.Target.Species + end + if rarityData.RarityMap:ContainsKey(species) then + local rarityTable = rarityData.RarityMap[species] + if rarityTable:ContainsKey(1) then + for item_id in luanet.each(rarityTable[1]) do + if _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Item]:Get(item_id).Released then + table.insert(possibleItems, item_id) + end + end + end + if #possibleItems > 0 then + items[i] = {id = library:WeightlessRandom(possibleItems) --[[@as string]]} + end + else + if i == 1 then + job.RewardType = "item" + else + job.RewardType = "money_item" + end + end + end + if not items[i] then + if not library.data.rewards_per_difficulty or #library.data.rewards_per_difficulty[difficulty_id] <= 0 then + logError(globals.error_types.DATA, "No possible rewards for quest difficulty \"" .. difficulty_id .. "\". Setting to \"" .. globals.defaults.item .. "\"") + if i == 1 then + job.RewardType = "item" + else + job.RewardType = "money_item" + end + items[i] = { id = globals.defaults.item } + else + -- redundant path check + local checked = {} + ---@type table|nil + local result = {} + local pool = library:WeightedRandom(library.data.rewards_per_difficulty[difficulty_id]).id + repeat + if not pool or not library.data.reward_pools[pool] or #library.data.reward_pools[pool] <= 0 then + if not pool then + logError(globals.error_types.DATA, "No reward pools defined for quest difficulty \"" .. difficulty_id .. "\". Setting reward type to \"money\"") + else + logError(globals.error_types.DATA, "Reward pool \"" .. pool .. "\" does not exist. Setting reward type to \"money\"") + end + job.RewardType = "money" + result = nil + break + else + checked[pool] = true + result = library:WeightedRandomExclude(library.data.reward_pools[pool], checked) + if result then + pool = result.id + else + result = nil + end + end + until not library.data.reward_pools[pool] + if result then + items[i] = result + end -- no else because it would only happen if the loop fails, but in that case it would already be set + end + end + end + end + if globals.reward_types[job.RewardType][1] then job.Reward1 = { id = items[1].id, count = items[1].count, hidden = items[1].hidden } end + if globals.reward_types[job.RewardType][2] then job.Reward2 = { id = items[2].id, count = items[2].count, hidden = items[2].hidden } end +end + +--- Rolls flavor data for the given job and stores the result of the roll in the job's table. +--- @param job jobTable the job to store flevor data in +local rollFlavor = function(job) + if job.Flavor[1] == "" then + for i = 1, 2, 1 do + job.Flavor[i] = library:WeightlessRandom(library.data.job_flavor[job.Type][i]) + end + end + if not job.Flavor[1] then + logError(globals.error_types.DATA, "No possible flavor text for job type \"" .. job.Type .. "\"") + job.Flavor = { "", "" } + end +end + +--- Rolls a title for the given job and stores the result of the roll in the job's table. +--- @param job jobTable the job to assign the title to +local rollTitle = function(job) + local title_group = job.Type + if job.Special and job.Special ~= "" then + title_group = job.Special + end + local title = library:WeightlessRandom(library.data.job_titles[title_group]) + if not title then + logError(globals.error_types.DATA, "No possible titles for quest category \"" .. title_group .. "\"") + end + job.Title = title or "" +end + +--- Generates a new job with the specified data. Location and job type are mandatory. Any other missing parameter will be generated randomly. +--- @param zone string the id of the zone this job should take place in +--- @param segment integer the index of the segment this job should take place in +--- @param floor integer the floor this job should take place in, counting from 1 +--- @param job_type jobType the id of the job type to assign to this job +--- @param client? string|monsterIDTable data that will be used for the client of this job +--- @param target? string|monsterIDTable data that will be used for the target of this job. Only used if the job type requires a target +--- @param target_item? string the id of the item that will be used as target. Only used if the job type requires an item +--- @param reward_type? rewardType the combination of rewards that will be awarded for this job. If not set but rewards is, it will be inferred from the items used +--- @param rewards? {[1]:string|itemTable, [2]:string|itemTable} the item rewards that will be awarded for this job. If not set but reward_type is, they will be generated automatically. If the first item is nil, it will award money in its place. +--- @param title? string the title string key to use for this job. If not set, it will be chosen randomly. +--- @param flavor? string[] the flavor string key or keys to use for this job. If not set, they will be chosen randomly. +--- @param hide_floor? boolean if true, the job's floor will be hidden. +--- @return jobTable? #the new job, or nil if the job generation failed +function library:MakeNewJob(zone, segment, floor, job_type, client, target, target_item, reward_type, rewards, title, flavor, hide_floor) + local newJob = jobTemplate() + newJob.Zone = zone + newJob.Segment = segment + newJob.Floor = floor + newJob.Type = job_type + local sections = self.data.dungeons[newJob.Zone][newJob.Segment] + for _, section in ipairs(sections.sections) do + if section.start > newJob.Floor then break end + newJob.Difficulty = self:DifficultyToNum(section.difficulty) + end + if newJob.Difficulty < 0 then + newJob.Difficulty = self:DifficultyToNum(sections.sections[1].difficulty) - math.ceil((sections.sections[1].start - floor) // 3) + end + --calc difficulty + if self.data.job_types[newJob.Type].rank_modifier then + newJob.Difficulty = newJob.Difficulty + self.data.job_types[newJob.Type].rank_modifier + end + --calc target item + if target_item then newJob.Item = target_item + else rollTargetItem(newJob) end + --set hidden_floor depending on chance + if hide_floor ~= nil then newJob.HideFloor = not not hide_floor + else rollHiddenFloor(newJob) end + + if not client or (globals.job_types[job_type].req_target and not target) then rollSpecial(newJob) end + + local status, err = pcall(rollCharacters, newJob, client, target) + if not status then + PrintInfo(err) + return + end + + setupRewards(newJob, reward_type, rewards) + + if flavor then newJob.Flavor = flavor + else rollFlavor(newJob) end + + if title then newJob.Title = title + else rollTitle(newJob) end + + return newJob +end + +--- Sets the floor as hidden or unhidden. Convenience function to avoid writing nil 15 times. +--- @param job jobTable the job to be edited +--- @param state? boolean what new state to set. If omitted, toggles the current state +function library:SetFloorHidden(job, state) + if state == nil then state = not job.HideFloor end + job.HideFloor = state +end + +--- Sets a localization key that will override the given data in menus +--- @param job jobTable the job to add the override to +--- @param data_id string the data to override to set +--- @param string_key string the localization key to set +function library:SetMenuOverride(job, data_id, string_key) + job.MenuOverrides = job.MenuOverrides or {} + job.MenuOverrides[data_id] = string_key +end + +--- Sets a localization key that will override the client string +--- @param job jobTable the job to add the override to +--- @param string_key string the localization key to set +function library:SetClientOverride(job, string_key) + self:SetMenuOverride(job, globals.overrides.CLIENT, string_key) +end + +--- Sets a localization key that will override the objective string +--- Localization placeholders: +--- {0} = Client, {1} = Target, {2} = Item +--- @param job jobTable the job to add the override to +--- @param string_key string the localization key to set +function library:SetObjectiveOverride(job, string_key) + self:SetMenuOverride(job, globals.overrides.OBJECTIVE, string_key) +end + +--- Sets a localization key that will override the dungeon string (not affecting the floor) +--- @param job jobTable the job to add the override to +--- @param string_key string the localization key to set +function library:SetDungeonOverride(job, string_key) + self:SetMenuOverride(job, globals.overrides.PLACE, string_key) +end + +--- Sets a localization key that will override the difficulty string +--- @param job jobTable the job to add the override to +--- @param string_key string the localization key to set +function library:SetDifficultyOverride(job, string_key) + self:SetMenuOverride(job, globals.overrides.DIFFICULTY, string_key) +end + +--- Sets a localization key that will override the reward string +--- Localization placeholders: +--- {0} = Reward1 (or Client), {1} = Reward2 +--- @param job jobTable the job to add the override to +--- @param string_key string the localization key to set +function library:SetRewardOverride(job, string_key) + self:SetMenuOverride(job, globals.overrides.REWARD, string_key) +end + +--- Adds a job to a specific board, updating its BackReference in the process. This function ignores board limits, and fails if the board does not exists. +--- @param board_id string a board's string id +--- @param job jobTable the job to add to the board +--- @return boolean #true if the job was added correctly, false otherwise +function library:AddJobToBoard(board_id, job) + if self:BoardExists(board_id) then + self:SetBackReference(job, board_id) + table.insert(self.root.boards[board_id], job) + return true + end + logError(globals.error_types.ID, "Board with id \"" .. board_id .. "\" does not exist. Cannot add jobs to it.") + return false +end + +--- Sets a job's BackReference property to the given board id. Fails if the board does not exists. +--- @param job jobTable the job to add a BackReference to +--- @param board_id string the id of the board to link this job to +--- @return boolean #true if the BackReference was added correctly, false otherwise +function library:SetBackReference(job, board_id) + if self:BoardExists(board_id) then + job.BackReference = board_id + return true + end + logError(globals.error_types.ID, "Board with id \"" .. board_id .. "\" does not exist. Cannot set BackReference.") + return false +end + +--- Runs the script responsible for interacting with a quest board. +--- It will first display a menu where players can choose to check either the board or the taken list. +--- @param board_id string the id of the board to interact with +function library:BoardInteract(board_id) + if not self:BoardExists(board_id) then + logError(globals.error_types.ID, "Board with id \"" .. board_id .. "\" does not exist. Cannot interact.") + return + end + local choice = 1 + while choice > 0 do + local job_selected = 1 + choice = menus.BoardSelection.run(self, board_id, choice) + if choice == 1 then + self:OpenBoardMenu(board_id, job_selected) + elseif choice == 2 then + self:OpenTakenMenu(job_selected) + end + end +end + +--- Runs the script responsible for handling a specific board's BoardMenu. +--- @param board_id string the id of the board to interact with +--- @param default? integer the job that will be selected when first opening the menu. Defaults to 1. +function library:OpenBoardMenu(board_id, default) + if not self:BoardExists(board_id) then + logError(globals.error_types.ID, "Board with id \"" .. board_id .. "\" does not exist. Cannot interact.") + return + end + local job_selected = default or 1 + --exit board menu if the board is empty + while not self:IsBoardEmpty(board_id) do + job_selected = menus.Board.run(self, board_id, job_selected) + if job_selected > 0 then + local action = menus.Job.run(self, board_id, job_selected) + if action == 1 then + self:TakeJob(board_id, job_selected) + end + else + break + end + end +end + +--- Runs the script responsible for handling the taken list's BoardMenu. +--- @param default? integer the job that will be selected when first opening the menu. Defaults to 1. +function library:OpenTakenMenu(default) + local job_selected = default or 1 + --exit board menu if the taken list is empty + while not self:IsTakenListEmpty() do + job_selected = menus.Board.run(self, nil, job_selected) + if job_selected > 0 then + local action = menus.Job.run(self, nil, job_selected) + if action == 1 then + self:ToggleTakenJob(job_selected) + elseif action == 2 then + self:RemoveTakenJob(job_selected) + end + else + break + end + end +end + +--- Runs the callback script responsible for interacting with the taken list. +--- This function starts a coroutine as a way to mantain callback behavior consistecy. +--- When closing the taken menu, only the MainMenu will be reopened. +function library:OpenTakenMenuFromMain() + _MENU:ClearMenus() + TASK:StartScriptLocalCoroutine(function() + self:OpenTakenMenu(1) + TASK:WaitTask(LUA_ENGINE:OnMenuButtonPressed()) + end) +end + +--- Runs the script responsible for displaying one specific job. +--- This function does not check if the job exists. Please call library:BoardJobExists before this. +--- @param board_id string the id of the board to interact with +--- @param index integer? The index of the job to show. If omitted, defaults to 1. +--- @return boolean #true if the job was taken, false otherwise +function library:ShowSingularJob(board_id, index) + index = index or 1 + if not self:BoardExists(board_id) then + logError(globals.error_types.ID, "Board with id \"" .. board_id .. "\" does not exist. Cannot interact.") + return false + end + if self:GetBoardCount(board_id) < index then + logError(globals.error_types.ID, "Board with id \"" .. board_id .. "\" does not contain " .. index .. " jobs.") + return false + end + local action = menus.Job.run(self, board_id, index) + if action == 1 then + self:TakeJob(board_id, index) + return true + end + return false +end + +--- Runs the callback script responsible for displaying the list of current objectives. +--- This function only works if used while already inside a menu handling routine. +function library:OpenObjectivesMenu() + menus.Objectives.add(self) +end + +--- Marks the job as taken and adds a copy of it to the taken board. +--- The copy will have its BackReference set to board_id. +--- @param board_id string the id of the board the job is in +--- @param index integer? The index of the job to take. Defaults to 1 +function library:TakeJob(board_id, index) + index = index or 1 + if not self:BoardExists(board_id) then + logError(globals.error_types.ID, "Board with id \"" .. board_id .. "\" does not exist. Cannot take.") + return false + end + if self:GetBoardCount(board_id) < index then + logError(globals.error_types.ID, "Board with id \"" .. board_id .. "\" does not contain " .. index .. " jobs.") + return false + end + local job = self.root.boards[board_id][index] + local taken = shallowCopy(job) + local evt = callEvent(taken, "JobTake", {board = board_id}) + if evt.cancel then return end + job.Taken = true + taken.BackReference = board_id + self:AddJobToTaken(taken, self.data.taken_jobs_start_active and not self:IsJobDestinationInTaken(job)) + + +end + +--- Adds a new job to the taken list. Then orders the list. This function does not trigger the JobTake event. +--- @param job jobTable the job to add +--- @param startActive? boolean Optional. If true, the job will be set to active automatically. Defaults to false. +function library:AddJobToTaken(job, startActive) + job.Taken = startActive or false + table.insert(self.root.taken, job) + self:SortTaken() +end + +--- Removes a job from the taken list. +--- The original job, if it still exists, will be marked as not Taken. +--- @param index integer The index of the job to delete +function library:RemoveTakenJob(index) + if #self.root.taken < index then + logError(globals.error_types.ID, "Taken list does not contain " .. index .. " jobs. Cannot remove.") + return false + end + local job = self.root.taken[index] + local bRefIndex = self:FindJobInBoard(job, job.BackReference) + if bRefIndex > 0 then + self.root.boards[job.BackReference][bRefIndex].Taken = false + end + table.remove(self.root.taken, index) + -- no need to sort here because they are guaranteed to be in order thanks to sorting every time a job is taken +end + +--- Changes a taken job's active status. This function can trigger the ``JobDeactivate`` and``JobActivate`` events depending on the job's state. +--- @param index integer The index of the job to toggle +function library:ToggleTakenJob(index) + if #self.root.taken < index then + logError(globals.error_types.ID, "Taken list does not contain " .. index .. " jobs. Cannot toggle state.") + return false + end + local job = self.root.taken[index] + local evt + if self.root.taken[index].Taken then + evt = callEvent(job, "JobDeactivate") + else + evt = callEvent(job, "JobActivate") + end + if evt.cancel then return end + self.root.taken[index].Taken = not self.root.taken[index].Taken +end + +--- Marks a job as completed and activates the flag required for the job completion cutscene to run. This function triggers the ``JobComplete`` event. +--- @param job jobTable The job being completed +function library:MarkJobCompleted(job) + local evt = callEvent(job, "JobComplete") + if evt.cancel then return end + job.Completion = globals.completion.Completed + if RogueEssence.GameManager.Instance.CurrentScene == RogueEssence.Dungeon.DungeonScene.Instance then + self.root.mission_flags.MissionCompleted = true + end +end + +--- Marks a job as failed. This function triggers the ``JobFail`` event. +--- @param job jobTable The job being failed +function library:MarkJobFailed(job) + local evt = callEvent(job, "JobFail") + if evt.cancel then return end + job.Completion = globals.completion.Failed +end + +--- Registers a new callback inside a job's callback table. +---@param job jobTable the job to register the callback in +---@param event eventId the id of the event to subscribe to +---@param func string the name of the callback function. It must be stored inside the table set inside the ``mission_callback_root`` setting +---@param args? table the table of arguments to pass to this instance of the callback. Defaults to ``{}`` +function library:RegisterCallback(job, event, func, args) + job.Callbacks[event] = {name = func, args = args or {}} +end + +-- ----------------------------------------------------------------------------------------- -- +-- #region General support +-- ----------------------------------------------------------------------------------------- -- +-- Situational support functions normally called by events. Differently from the MISC functions, these functions are not private + +--- Dynamically builds a boss moveset and assigns it to a character. +--- The character's level should be assigned before running this script. Reduce the level afterwards if you want to be illegal about it. +--- The character is always allowed to have as many level-up moves as it can learn at its level. The number of other move types is arbitrary. +---@param chara any the Character to build. +---@param tm_allowed integer the maximum number of tm moves that the character is allowed to have. Defaults to 0 +---@param tutor_allowed integer the maximum number of tutor moves that the character is allowed to have. Defaults to 0 +---@param egg_allowed integer the maximum number of egg moves that the character is allowed to have. Defaults to 0 +---@param blacklist referenceSet list of move ids that must never be included in a moveset generated by this function +---@return slotType[] #the list of slot types chosen, in the order they were applied. Slot types are "stab", "coverage", "damage" and "status". Useful to apply changes later on. +function library:AssignBossMoves(chara, tm_allowed, tutor_allowed, egg_allowed, blacklist) + if RogueEssence.GameManager.Instance.CurrentScene ~= RogueEssence.Dungeon.DungeonScene.Instance then + logError(globals.error_types.SCENE, "This function can only be called inside dungeons.") + return {} + end + -- prepare lists + ---@type table + local allowed = { level = 4 } + allowed.tm, allowed.tutor, allowed.egg = tm_allowed or 0, tutor_allowed or 0, egg_allowed or 0 + local moveset_table = filterMoveset(chara, allowed.tm > 0, allowed.tutor > 0, allowed.egg > 0, blacklist) + ---@type table<"_Completed"|integer,synergyEntry|referenceSet> + local synergies = { _Completed = {} } + ---@type slotType[][] + local move_slot_options = { + { "stab", "coverage", "damage", "status" }, + { "stab", "coverage", "status", "status" } + } + local move_slots = self:WeightlessRandom(move_slot_options, true) --[[ @as slotType[] ]] + local shuffled_slots = shuffleTable(move_slots, true) --[[ @as slotType[] ]] + ---@type table keeps track of what moves are assigned to what slot type + local slot_to_moves = {} + + -- select the moves + for _, slot_type in pairs(shuffled_slots) do + slot_to_moves[slot_type] = slot_to_moves[slot_type] or {} + table.insert(slot_to_moves[slot_type], moveSelection[slot_type](moveset_table, synergies, allowed)) + end + -- apply the moves to the character in move_slot order + for i, slot_type in pairs(move_slots) do + local move = slot_to_moves[slot_type][1] --FIFO + if move == "" then + chara:DeleteSkill(i - 1) + else + chara:ReplaceSkill(move, i - 1, true) + end + table.remove(slot_to_moves[slot_type], 1) --the slot to moves list gets progressively emptied out + end + return shuffled_slots +end + +--- Function that asks the player if they want to warp out. It checks if the player has ongoing missions +--- and displays messages accordingly. +function library:AskMissionWarpOut() + if RogueEssence.GameManager.Instance.CurrentScene ~= RogueEssence.Dungeon.DungeonScene.Instance then + logError(globals.error_types.SCENE, "This function can only be called inside dungeons.") + return + end + local function MissionWarpOut() + warpOut() + GAME:WaitFrames(80) + --Set minimap state back to old setting + _DUNGEON.ShowMap = library.root.mission_flags.PriorMapSetting + library.root.mission_flags.PriorMapSetting = nil + TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Escaped)) + end + + local function SetMinimap() + --to prevent accidentally doing something by pressing the button to select yes + GAME:WaitFrames(10) + --Set minimap state back to old setting + _DUNGEON.ShowMap = library.root.mission_flags.PriorMapSetting + library.root.mission_flags.PriorMapSetting = nil + end + + local has_ongoing_jobs = false + local curr_floor = GAME:GetCurrentFloor().ID + 1 + local curr_zone = _ZONE.CurrentZoneID + local curr_segment = _ZONE.CurrentMapID.Segment + + for _, job in ipairs(self.root.taken) do + if job.Floor > curr_floor and job.Taken and job.Completion == globals.completion.NotCompleted and curr_zone == job.Zone and curr_segment == job.Segment then + has_ongoing_jobs = true + break + end + end + + UI:ResetSpeaker() + local state = 0 + while state > -1 do + if state == 0 then + if has_ongoing_jobs then + UI:ChoiceMenuYesNo(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.CONTINUE_ONGOING):ToLocal()), true) + UI:WaitForChoice() + local leave_dungeon = UI:ChoiceResult() + if leave_dungeon then + UI:ChoiceMenuYesNo(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.CONTINUE_CONFIRM):ToLocal()), true) + UI:WaitForChoice() + local leave_confirm = UI:ChoiceResult() + if leave_confirm then + state = -1 + MissionWarpOut() + else + --pause between textboxes if player de-confirms + GAME:WaitFrames(20) + end + else + state = -1 + SetMinimap() + end + else + UI:ChoiceMenuYesNo(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.CONTINUE_NO_ONGOING):ToLocal()), false) + UI:WaitForChoice() + local leave_dungeon = UI:ChoiceResult() + if leave_dungeon then + state = -1 + MissionWarpOut() + else + state = -1 + SetMinimap() + end + end + end + end +end + +--- Dungeon-only function that makes the entire team, guests included, turn towards a character. +--- @param char any the character to turn towards +function library:TeamTurnTo(char) + if RogueEssence.GameManager.Instance.CurrentScene ~= RogueEssence.Dungeon.DungeonScene.Instance then + logError(globals.error_types.SCENE, "This function can only be called inside dungeons.") + return + end + local player_count = GAME:GetPlayerPartyCount() + local guest_count = GAME:GetPlayerGuestCount() + for i = 0, player_count - 1, 1 do + local player = GAME:GetPlayerPartyMember(i) + if not player.Dead then + DUNGEON:CharTurnToChar(player, char) + end + end + + for i = 0, guest_count - 1, 1 do + local guest = GAME:GetPlayerGuestMember(i) + if not guest.Dead then + DUNGEON:CharTurnToChar(guest, char) + end + end +end + +-- ----------------------------------------------------------------------------------------- -- +-- #region COMMON support +-- ----------------------------------------------------------------------------------------- -- +-- API specific for functions that are meant for common.lua or one of its child files + +--- Meant to be called in ``COMMON.ShowDestinantonMenu`` +--- Creates a reference table whose keys are all the zones with at least one job in them. +---@return referenceSet +function library:LoadJobDestinations() + local mission_dests = {} + for _, job in ipairs(self.root.taken) do + if job.Taken then + if not self:HasExternalEvents(job.Zone) then + mission_dests[job.Zone] = true + else + job.Taken = false + end + end + end + return mission_dests +end + +--- Meant to be called in ``COMMON.ShowDestinantonMenu`` +--- Takes a zone name and its id and returns a name string that also contains any related job and event icons. +--- @param mission_dests referenceSet the set of job destinations +--- @param zone_id string the zone id string to format the name of +--- @param zone_name string the starting name of the zone, as extracted from its ZoneEntrySummary +--- @return string #the zone name string containing all the icons related to it. +function library:FormatDestinationMenuZoneName(mission_dests, zone_id, zone_name) + local mission_icon, external_icons, ext_set = "", "", {} + -- add open letter icon to dungeons with jobs + if mission_dests[zone_id] then mission_icon = "\\" end --open letter + -- add external icon to dungeons with external events + local ext_conds = self:GetExternalEvents(zone_id) + for _, ext in ipairs(ext_conds) do + if ext.icon and ext.icon ~= "" and not ext_set[ext.icon] then + ext_set[ext.icon] = true + external_icons = external_icons .. (ext.icon) + if self.data.external_events_icon_mode == "FIRST" then break end + end + end + return STRINGS:Format(self.data.dungeon_list_pattern, zone_name, STRINGS:Format(mission_icon), STRINGS:Format(external_icons)) +end + +--- Meant to be called in ``COMMON:EnterDungeonMissionCheck`` +--- Prepares the list of guests to be spawned and reduces the team limit accordingly if missiongen_settings.guests_take_up_space is set. +--- @param zone_id string the zone id string to prepare the guest data for +--- @return integer[], string[] #the list of indexes for jobs that have guests and the list of removed ally names. +function library:EnterDungeonPrepareParty(zone_id) + local escort_jobs = {} + + for idx, job in ipairs(library.root.taken) do + + if job.Taken and job.Completion < 1 and zone_id == job.Zone then + callEvent(job, "DungeonStart") + if globals.job_types[job.Type].has_guest then + --check to see if an escort is already in the team for this job. If so, don't include it in the guest list + local guest_found = false + for i = 0, _DATA.Save.ActiveTeam.Guests.Count - 1, 1 do + local guest_tbl = GAME:GetPlayerGuestMember(i).LuaData + if guest_tbl.JobReference == idx then + guest_found = true + end + end + if not guest_found then + table.insert(escort_jobs, idx) + end + end + end + end + + UI:ResetSpeaker() + -- if set to do so, remove as many characters from the team as necessary and reduce the team limit accordingly + local removed_names = {} + if self.data.guests_take_up_space then + self.root.previous_limit = RogueEssence.Dungeon.ExplorerTeam.MAX_TEAM_SLOTS + local base_max = RogueEssence.Dungeon.ExplorerTeam.MAX_TEAM_SLOTS + local zone_summary = _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Zone]:Get(zone_id) + if zone_summary.TeamSize > -1 and zone_summary.TeamSize < RogueEssence.Dungeon.ExplorerTeam.MAX_TEAM_SLOTS then + base_max = zone_summary.TeamSize + end + self.root.escort_jobs = #escort_jobs + RogueEssence.Dungeon.ExplorerTeam.MAX_TEAM_SLOTS = math.max(1, self.data.min_party_limit, + base_max - self.root.escort_jobs) + for i = _DATA.Save.ActiveTeam.Players.Count - 1, RogueEssence.Dungeon.ExplorerTeam.MAX_TEAM_SLOTS, -1 do + local char = GAME:GetPlayerPartyMember(i) + GAME:RemovePlayerTeam(i) + GAME:AddPlayerAssembly(char) + table.insert(removed_names, 1, char:GetDisplayName(true)) + end + end + if _DATA.Save.ActiveTeam.LeaderIndex >= RogueEssence.Dungeon.ExplorerTeam.MAX_TEAM_SLOTS then + _DATA.Save.ActiveTeam.LeaderIndex = 0 end + return escort_jobs, removed_names +end + + +--- Meant to be called in ``COMMON.EnterDungeonMissionCheck`` +--- Generates the clients of the provided jobs and adds them as guests. +--- @param zone_id string the zone id string to generate the guests for +--- @param escort_jobs integer[] the list of jobs to generate guests from +--- @return string[] #the list list of added guest names. +function library:EnterDungeonAddJobEscorts(zone_id, escort_jobs) + local zone_summary = _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Zone]:Get(zone_id) + + -- calculate party-related level parameters + local party_tot, party_avg, party_hst, party_count = 0, 0, 0, _DATA.Save.ActiveTeam.Players.Count + for i = 0, party_count - 1, 1 do + local char_lvl = _DATA.Save.ActiveTeam.Players[i].Level + party_tot = party_tot + char_lvl + party_hst = math.max(char_lvl, party_hst) + end + party_avg = party_tot // party_count + + -- add escorts to team + local added_names = {} + for _, idx in ipairs(escort_jobs) do + local job = self.root.taken[idx] + local nickname = job.Client.Nickname or "" + local mId = self:TableToMonsterID(job.Client) + local diff = self:NumToDifficulty(job.Difficulty) + local level = self.data.difficulty_data[diff].escort_level + local dungeon_default = not level + if dungeon_default then level = zone_summary.Level end + ---@cast level integer + if self.data.guest_level_scaling then + level = self.data.guest_level_scaling(level, dungeon_default, party_avg, party_hst, self.data) + end + + local new_mob = _DATA.Save.ActiveTeam:CreatePlayer(_DATA.Save.Rand, mId, level, "", -1) + new_mob.Nickname = nickname + local tactic = _DATA:GetAITactic("stick_together") + new_mob.Tactic = RogueEssence.Data.AITactic(tactic); + _DATA.Save.ActiveTeam.Guests:Add(new_mob) + local talk_evt = RogueEssence.Dungeon.BattleScriptEvent("EscortInteract") + new_mob.ActionEvents:Add(talk_evt) + local tbl = new_mob.LuaData + tbl.JobReference = idx + table.insert(added_names, "[color=#00FF00]" .. new_mob.Name .. "[color]") + end + return added_names +end + +--- Meant to be called in ``COMMON.EnterDungeonMissionCheck`` +--- Prints a SENT_HOME message using the provided list of formatted character display names. +--- @param removed_list string[] a list of character display names +function library:PrintSentHome(removed_list) + if #removed_list<1 then return end + UI:ResetSpeaker() + local list_removed = STRINGS:CreateList(removed_list) --[[@as string]] + if #removed_list > 1 then + UI:WaitShowDialogue(STRINGS:FormatKey("MSG_TEAM_SENT_HOME_PLURAL", list_removed)) + elseif #removed_list == 1 then + UI:WaitShowDialogue(STRINGS:FormatKey("MSG_TEAM_SENT_HOME", list_removed)) + end +end + +--- Meant to be called in ``COMMON.EnterDungeonMissionCheck`` +--- Prints an ESCORT_ADD message using the provided list of formatted character display names. +--- @param added_list string[] a list of character display names +function library:PrintEscortAdd(added_list) + if #added_list < 1 then return end + UI:ResetSpeaker() + local list_added = STRINGS:CreateList(added_list) --[[@as string]] + if #added_list > 1 then + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.ESCORT_ADD_PLURAL):ToLocal(), list_added)) + elseif #added_list == 1 then + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.ESCORT_ADD):ToLocal(), list_added)) + end +end + +--- Meant to be called in ``COMMON.ExitDungeonMissionCheckEx`` +--- Removes all target items from the inventory and requests a board update. +--- Its return value should itself be returned by COMMON.ExitDungeonMissionCheckEx +--- Please pass all of the function's parameters to this one, in order. +function library:ExitDungeonMissionCheckEx(result, _, zone, segment) + local resultTypes = RogueEssence.Data.GameProgress.ResultType + --reset the escort status + _DATA.Save.ActiveTeam.Guests:Clear() + if self.data.guests_take_up_space then + RogueEssence.Dungeon.ExplorerTeam.MAX_TEAM_SLOTS = self.root.previous_limit or RogueEssence.Dungeon.ExplorerTeam.MAX_TEAM_SLOTS + self.root.escort_jobs = 0 + end + + if result == resultTypes.Cleared then + MissionGen.root.dungeon_progress[zone] = MissionGen.root.dungeon_progress[zone] or {} + MissionGen.root.dungeon_progress[zone][segment] = true + end + + --Remove any lost/stolen items. If the item contains a numerical HiddenValue and that number + --corresponds to a job_index containing its id then delete it on exiting the dungeon. + local isItemJobTarget = function(item) + local num = tonumber(item.HiddenValue) + if num then + local job = MissionGen.root.taken[num] + if job.Item == item.ID then return true end + end + return false + end + + for i = GAME:GetPlayerBagCount() - 1, 0, -1 do + if isItemJobTarget(GAME:GetPlayerBagItem(i)) then + GAME:TakePlayerBagItem(i) + end + end + + --search equipped items as well + for i = 0, GAME:GetPlayerPartyCount() - 1, 1 do + if isItemJobTarget(GAME:GetPlayerEquippedItem(i)) then + GAME:TakePlayerEquippedItem(i) + end + end + + --reset all job completion data if exploration failed in some way + if result == resultTypes.Failed or result == resultTypes.Downed + or result == resultTypes.TimedOut or result == resultTypes.GaveUp then + MissionGen.root.mission_flags.MissionCompleted = false + for _, job in ipairs(self.root.taken) do + job.Completion = globals.completion.NotCompleted + end + end + + for _, job in ipairs(self.root.taken) do + callEvent(job, "DungeonEnd", {result = result}) + end + + if MissionGen.root.mission_flags.MissionCompleted then + -- PlayJobsCompletedCutscene will update the boards after it's done + resetAnims() + COMMON.EndDungeonDay(result, self.data.end_dungeon_day_destination.zone, -1, self.data.end_dungeon_day_destination.map, 0) + return true + end + -- PlayJobsCompletedCutscene will not be triggered, so the boards will be updated here + self:UpdateBoards() + return false +end + +-- ----------------------------------------------------------------------------------------- -- +-- #region GROUND support +-- ----------------------------------------------------------------------------------------- -- +-- API specific for functions that are meant for ground map scripting + +--- Meant to be called in all ground maps where there are job boards, and must be ran before they fade in. +--- It iterates through the taken list, checking if the next job is in this map and running the reward cutscene for it +--- until it either finds a job that needs to be awarded on a different map, in which case it moves the player there, +--- Or it runs out of jobs. +--- The game will be in a fade out state every time the callback is run. +---@param callback fun(job:jobTable, npcs:any[]) the reward cutscene callback function. +--- It takes the job data table and the GroundChar npcs as arguments. +function library:PlayJobsCompletedCutscene(callback) + local index = 1 + self.temp = self.temp or {} + if self.temp.reward_job_index then index = self.temp.reward_job_index end + local CurrentZone = _ZONE.CurrentZoneID + local CurrentMap = _ZONE.CurrentMapID.ID + + while index <= #self.root.taken do + local job = self.root.taken[index] + if job.Completion == globals.completion.Completed then + if job.BackReference and job.BackReference ~= "" then + local dest = self.data.boards[job.BackReference --[[@as string]] ].location + if CurrentZone ~= dest.zone or CurrentMap ~= dest.map then + self.temp.reward_job_index = index + GAME:EnterZone(dest.zone, -1, dest.map, 0) + return + end + end + GAME:FadeOut(false, 1) + local evt = callEvent(job, "BeforeReward", {zone = CurrentZone, map = CurrentMap}) + if not evt.cancel then + rewardCutscene(index, callback) + end + local evt2 = callEvent(job, "AfterReward", {zone = CurrentZone, map = CurrentMap}) + if not evt2.cancel then + self:RemoveTakenJob(index) + end + else + job.Completion = globals.completion.NotCompleted + index = index + 1 + end + end + self.root.mission_flags.MissionCompleted = false + self.temp.reward_job_index = nil + if self.data.after_rewards_function then + self.data.after_rewards_function() end + self:UpdateBoards() + GAME:CutsceneMode(false) +end + +--- Meant to be called as part of a job reward callback, in all ground maps where there are job boards. +--- Gives the player the rewards of a job. You may include a speaker and up to two dialogue lines to display as part of the routine. +--- This does not remove the job from the player. Another part of the job reward routine handles that already. +--- If the library is configured to award rank points, this function will also handle ranking up, and return the reached ranks if it happened. +---@param job jobTable the job to give the reward of +---@param speaker any|nil a GroundChar to use as speaker for the lines below. If nil, the lines will have no speaker. +---@param line1? string the line to use for the first reward. Unused if the reward is the client itself. Must be already formatted. +---@param line2? string the line to use for the second reward. Unused if there is no second reward. Must be already formatted. +---@return string[] #a list of ranks reached. If no rank-ups happened, the list will be empty +function library:RewardPlayer(job, speaker, line1, line2) + local difficulty_data = self.data.difficulty_data[self:NumToDifficulty(job.Difficulty)] + if job.RewardType == "client" then + GAME:WaitFrames(20) + self:AwardChar(job, speaker) + GAME:WaitFrames(20) + else + if line1 then + GAME:WaitFrames(20) + UI:SetSpeaker(speaker) + UI:WaitShowDialogue(line1) + end + + --reward the item or money sum + GAME:WaitFrames(20) + if globals.reward_types[job.RewardType][1] then + self:AwardItem(job.Reward1) + else + self:AwardMoney(difficulty_data.money_reward) + end + GAME:WaitFrames(20) + + if globals.reward_types[job.RewardType][2] then + if line2 then + UI:SetSpeaker(speaker) + UI:WaitShowDialogue(line2) + GAME:WaitFrames(20) + end + self:AwardItem(job.Reward2) + GAME:WaitFrames(20) + end + end + local ranks = self:AwardExtra(difficulty_data.extra_reward) + return ranks +end + +--- Offers the player a Character as reward. The character will be at the level it would've been if spawned now as a guest. +--- The player can refuse the new team member if the so choose. +---@param job jobTable the job to source the character from +---@param char Character the character that will be used as speaker for the cutscene +---@param talk? boolean if true, there will be a dialogue line before the prompt. If false, there won't be. Defaults to true. +---@return boolean true if the player accepted the new team member, false otherwise +function library:AwardChar(job, char, talk) + if talk == true or talk == nil then + UI:SetSpeaker(char) + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.CUTSCENE_AWARD_CHAR):ToLocal(), GAME:GetTeamName())) + end + GAME:WaitFrames(20) + UI:ResetSpeaker() + UI:ChoiceMenuYesNo( STRINGS:Format(RogueEssence.StringKey(globals.keysEx.CUTSCENE_AWARD_CHAR_PROMPT):ToLocal(), GAME:GetTeamName(), char:GetDisplayName()), true) + UI:WaitForChoice() + if UI:ChoiceResult() then + local zone_summary = _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Zone]:Get(job.Zone) + + -- calculate party-related level parameters + local party_tot, party_avg, party_hst, party_count = 0, 0, 0, _DATA.Save.ActiveTeam.Players.Count + for i = 0, party_count - 1, 1 do + local char_lvl = _DATA.Save.ActiveTeam.Players[i].Level + party_tot = party_tot + char_lvl + party_hst = math.max(char_lvl, party_hst) + end + party_avg = party_tot // party_count + + local nickname = job.Client.Nickname or "" + local mId = self:TableToMonsterID(job.Client) + local diff = self:NumToDifficulty(job.Difficulty) + local level = self.data.difficulty_data[diff].escort_level + local dungeon_default = not level + if dungeon_default then level = zone_summary.Level end + ---@cast level integer + if self.data.guest_level_scaling then + level = self.data.guest_level_scaling(level, dungeon_default, party_avg, party_hst, self.data) + end + + local new_mob = _DATA.Save.ActiveTeam:CreatePlayer(_DATA.Save.Rand, mId, level, "", -1) + new_mob.Nickname = nickname + _DATA.Save.ActiveTeam.Assembly:Add(new_mob) + + UI:ResetSpeaker(false) --disable text noise + UI:SetCenter(true) + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey("MSG_RECRUIT"):ToLocal(), self:GetCharacterName(job.Client), GAME:GetTeamName())) + UI:SetCenter(false) + UI:ResetSpeaker() + return true + end + return false +end + +--- Gives the player a sum of money as reward. +---@param amount integer the sum of money that will be given +function library:AwardMoney(amount) + if amount <= 0 then return end + UI:ResetSpeaker(false)--disable text noise + UI:SetCenter(true) + SOUND:PlayFanfare("Fanfare/Item") + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.CUTSCENE_AWARD_MONEY):ToLocal(), GAME:GetTeamName(), STRINGS:FormatKey("MONEY_AMOUNT", amount))) + GAME:AddToPlayerMoney(amount) + UI:SetCenter(false) + UI:ResetSpeaker() +end + +--- Gives the player an InvItem as reward. +---@param itemTable itemTable the item to give. If its count is nil, it will default to the item's max stack +function library:AwardItem(itemTable) + UI:ResetSpeaker(false) --disable text noise + UI:SetCenter(true) + + local itemEntry = RogueEssence.Data.DataManager.Instance:GetItem(itemTable.id) + + --give at least 1 item + if not itemTable.count then + if itemEntry.MaxStack > 1 then + itemTable.count = itemEntry.MaxStack + else + itemTable.count = 1 + end + end + itemTable.count = math.min(itemTable.count, itemEntry.MaxStack) + + local item = RogueEssence.Dungeon.InvItem(itemTable.id, false, itemTable.count) + if itemTable.hidden then item.HiddenValue = itemTable.hidden end + + SOUND:PlayFanfare("Fanfare/Item") + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.CUTSCENE_AWARD_ITEM):ToLocal(), GAME:GetTeamName(), item:GetDisplayName())) + + --bag is full - equipped count is separate from bag and must be included in the calc + if GAME:GetPlayerBagCount() + GAME:GetPlayerEquippedCount() >= GAME:GetPlayerBagLimit() then + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.CUTSCENE_AWARD_ITEM_STORAGE):ToLocal(), GAME:GetTeamName(), item:GetDisplayName())) + GAME:GivePlayerStorageItem(item.ID, itemTable.count) + else + GAME:GivePlayerItem(item.ID, itemTable.count) + end + UI:SetCenter(false) + UI:ResetSpeaker() +end + +--- Gives the player the job's extra reward. If the extra reward is set to "rank", then it will also rank the player team up and return the list of ranks reached. +---@param amount integer the amount of extra reward that will be given +---@return string[] #a list of ranks reached. If no rank-ups happened, the list will be empty +function library:AwardExtra(amount) + local ranks_reached = {} + if amount <= 0 then return ranks_reached end + UI:ResetSpeaker(false) --disable text noise + UI:SetCenter(true) + SOUND:PlayFanfare("Fanfare/Item") + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.CUTSCENE_AWARD_EXTRA):ToLocal(), GAME:GetTeamName(), STRINGS:FormatKey(globals.keys.EXTRA_REWARD, amount))) + if self.data.extra_reward_type == "exp" then + --Reward EXP for your party + local player_count = _DATA.Save.ActiveTeam.Players.Count + for player_idx = 0, player_count - 1, 1 do + TASK:WaitTask(GROUND:_HandoutEXP(_DATA.Save.ActiveTeam.Players[player_idx], amount)) + end + elseif self.data.extra_reward_type == "rank" then + + --check if a rank up is needed + local start_rank = _DATA.Save.ActiveTeam.Rank + local current_rank = start_rank + local end_rank = start_rank + local rankdata = _DATA:GetRank(current_rank) + local to_go = rankdata.FameToNext - _DATA.Save.ActiveTeam.Fame + --rank up if there's another rank to go. FameToNext will be 0 or -1 if there's no more ranks after. + while amount >= to_go and rankdata.FameToNext > 0 do + end_rank = rankdata.Next + amount = amount - to_go + if end_rank.FameToNext <= 0 then amount = 0 end + --reset fame, go to next rank + _DATA.Save.ActiveTeam:SetRank(end_rank) + _DATA.Save.ActiveTeam.Fame = 0 + + current_rank = end_rank + rankdata = _DATA:GetRank(current_rank) + table.insert(ranks_reached, current_rank) + _DATA.Save.ActiveTeam.Fame = _DATA.Save.ActiveTeam.Fame + amount + end + SOUND:PlayFanfare("Fanfare/RankUp") + UI:ResetSpeaker() + UI:SetCenter(true) + + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.CUTSCENE_AWARD_RANK_UP):ToLocal(), GAME:GetTeamName(), _DATA:GetRank(start_rank).Name:ToLocal(), _DATA:GetRank(end_rank).Name:ToLocal())) + end + UI:SetCenter(false) + UI:ResetSpeaker() + GAME:WaitFrames(20) + return ranks_reached +end + +-- ----------------------------------------------------------------------------------------- -- +-- #region BATTLE_SCRIPT support +-- ----------------------------------------------------------------------------------------- -- +-- API specific for functions that are meant for event_battle.lua + +--- Meant to be called in ``BATTLE_SCRIPT.EscortInteract``. It implements the entire EscortInteract event used by the library. +--- It displays a dialogue box containing one of the talk strings corresponding to the character's associated job type. +--- Please pass all of the event's parameters to this function, in order. +function library:EscortInteract(_, _, context, _) + context.CancelState.Cancel = true + local oldDir = context.Target.CharDir + DUNGEON:CharTurnToChar(context.Target, context.User) + UI:SetSpeaker(context.Target) + + local tbl = context.Target.LuaData + local job = self.root.taken[tbl.JobReference] --[[@as jobTable]] + + local target = "[color=#FF0000]???[color]" + if job.Target then target = self:GetCharacterName(job.Target) end + local dungeon = self:GetSegmentName(job.Zone, job.Segment) + local item = self:GetItemName(job.Item) + + local talk_options = self.data.escort_talks[job.Type] + if job.Special and self.data.escort_talks[job.Special] then + talk_options = self.data.escort_talks[job.Special] + end + local talk = self:WeightlessRandom(talk_options) + + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(talk):ToLocal(), target, dungeon, item)) + context.Target.CharDir = oldDir +end + +--- Meant to be called in ``BATTLE_SCRIPT.EscortReached``. It implements the entire EscortReached event used by the library. +--- It is called when interacting with an escort mission target. It checks for escort proximity and completes the job if possible. +--- Please pass all of the event's parameters to this function, in order. +function library:EscortReached(_, _, context, _) + context.CancelState.Cancel = false + context.TurnCancel.Cancel = true + --Mark this as the last dungeon entered. + local tbl = context.Target.LuaData + if tbl and tbl.JobReference then + local job = self.root.taken[tbl.JobReference] + local escort = nil + for i = 0, _DATA.Save.ActiveTeam.Guests.Count - 1, 1 do + local guest = GAME:GetPlayerGuestMember(i) + if guest.LuaData and guest.LuaData.JobReference == tbl.JobReference then + escort = guest + break + end + end + + if escort and not escort.Dead then + context.Target.Nickname = self:GetCharacterName(job.Target, true) + local escortName = escort:GetDisplayName(true) + local targetName = context.Target:GetDisplayName(true) + local oldDir = context.Target.CharDir + DUNGEON:CharTurnToChar(context.Target, context.User) + UI:ResetSpeaker() + if math.abs(escort.CharLoc.X - context.Target.CharLoc.X) <= 4 and math.abs(escort.CharLoc.Y - context.Target.CharLoc.Y) <= 4 then + --Mark mission completion flags + self.root.mission_flags.MissionCompleted = true + self:MarkJobCompleted(job) + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.ESCORT_REACHED):ToLocal(), escortName, targetName)) + --Clear but remember minimap state + self.root.mission_flags.PriorMapSetting = _DUNGEON.ShowMap + _DUNGEON.ShowMap = _DUNGEON.MinimapState.None + GAME:WaitFrames(20) + + UI:SetSpeaker(escort) + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.ESCORT_THANKS):ToLocal(), targetName)) + GAME:WaitFrames(20) + UI:ResetSpeaker() + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.ESCORT_DEPART):ToLocal(), escortName, targetName)) + GAME:WaitFrames(20) + + -- warp out + TASK:WaitTask(_DUNGEON:ProcessBattleFX(escort, escort, _DATA.SendHomeFX)) + _DUNGEON:RemoveChar(escort) + _ZONE.CurrentMap.DisplacedChars:Remove(escort) + GAME:WaitFrames(70) + TASK:WaitTask(_DUNGEON:ProcessBattleFX(context.Target, context.Target, _DATA.SendHomeFX)) + _DUNGEON:RemoveChar(context.Target) + + GAME:WaitFrames(50) + self:AskMissionWarpOut() + else + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.ESCORT_UNAVAILABLE):ToLocal(), escortName)) + context.Target.CharDir = oldDir + end + else + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.ESCORT_UNAVAILABLE):ToLocal(), self:GetCharacterName(job.Client))) + end + end +end + +--- Meant to be called in ``BATTLE_SCRIPT.RescueReached``. It implements the entire RescueReached event used by the library. +--- It is called when interacting with a rescue or delivery mission target. It checks for the delivery item if necessary and then +--- completes the job if possible. +--- Please pass all of the event's parameters to this function, in order. +function library:RescueReached(_, _, context, _) + -- Set the nickname of the target, removing the gender sign + local job = self.root.taken[context.Target.LuaData.JobReference] + local tbl = context.Target.LuaData + if tbl and tbl.JobReference then + local char = job.Target or job.Client + local base_name = self:GetCharacterName(char, true) + GAME:SetCharacterNickname(context.Target, base_name) + + context.CancelState.Cancel = false + context.TurnCancel.Cancel = true + + local oldDir = context.Target.CharDir + + DUNGEON:CharTurnToChar(context.Target, context.User) + UI:ResetSpeaker() + + if job.Type == "RESCUE_SELF" or job.Type == "RESCUE_FRIEND" then + rescueReachedFlow(context, job) + elseif job.Type == "DELIVERY" then + deliveryReachedFlow(context, job, oldDir) + end + end +end + +-- ----------------------------------------------------------------------------------------- -- +-- #region ZONE_GEN_SCRIPT support +-- ----------------------------------------------------------------------------------------- -- +-- API specific for functions that are meant for event_mapgen.lua + +--- Meant to be called in a ``ZONE_GEN_SCRIPT`` event. It handles checking for active jobs and generating every single event necessary for them to function. +--- Please pass all of the event's parameters to this function, in order. +--- +--- The event calling this function is the only one that needs to be added directly to the game via dev mode: +--- go to Constants>Universal and add it to the ZoneSteps list as a ScriptZoneStep. No arguments required. +function library:GenerateJobInFloor(zoneContext, context, queue, seed, _) + if _DATA.Save.Rescue ~= nil and _DATA.Save.Rescue.Rescuing then + return + end + self.root.mission_flags.PriorMapSetting = nil + self.root.mission_flags.MonsterHouseMessageNotified = false + self.root.mission_flags.OutlawItemState = 0 + self.root.mission_flags.OutlawDefeated = false + self.root.mission_flags.ItemPickedUp = false + + local job = nil + local jobIndex = nil + local escortMissions = false + local npcMission = false + local activeEffect = RogueEssence.Data.ActiveEffect() + + + for i, entry in ipairs(self.root.taken) do + if entry.Taken and entry.Completion == globals.completion.NotCompleted and zoneContext.CurrentZone == entry.Zone then + -- if this job is an escort mission + if self.globals.job_types[entry.Type].has_guest then escortMissions = true end + if zoneContext.CurrentSegment == entry.Segment and zoneContext.CurrentID + 1 == entry.Floor then + job, jobIndex = entry, i + break + end + end + end + + local prio_boss = self.data.dungeon_boss_steps_piority + local prio_gen = self.data.dungeon_gen_steps_piority + local prio_npc = self.data.dungeon_npc_steps_piority + if type(prio_boss) ~= "table" then prio_boss = {prio_boss} end + if type(prio_gen) ~= "table" then prio_gen = {prio_gen} end + if type(prio_npc) ~= "table" then prio_npc = {prio_npc} end + local priority_boss = RogueElements.Priority(LUA_ENGINE:MakeLuaArray(globals.ctypes.Integer, prio_boss)) + local priority_gen = RogueElements.Priority(LUA_ENGINE:MakeLuaArray(globals.ctypes.Integer, prio_gen)) + local priority_npc = RogueElements.Priority(LUA_ENGINE:MakeLuaArray(globals.ctypes.Integer, prio_npc)) + + if escortMissions then + activeEffect.OnDeaths:Add(priority_gen, RogueEssence.Dungeon.SingleCharScriptEvent("EscortDeathCheck", '{}')) + end + if job then + local evt = callEvent(job, "FloorStart", {zoneContext = zoneContext, context = context, queue = queue, seed = seed}) + if evt.cancel then return end + + ---@cast job jobTable + if globals.job_types[job.Type].target_outlaw then + activeEffect.OnDeaths:Add(priority_gen, + RogueEssence.Dungeon.SingleCharScriptEvent("OutlawDeathCheck", '{ JobReference = ' .. jobIndex .. ' }')) + if job.Type == "OUTLAW" then + activeEffect.OnTurnEnds:Add(priority_gen, + RogueEssence.Dungeon.SingleCharScriptEvent("OutlawCheck", '{ JobReference = ' .. jobIndex .. ' }')) + elseif job.Type == "OUTLAW_FLEE" then + activeEffect.OnMapTurnEnds:Add(priority_gen, + RogueEssence.Dungeon.SingleCharScriptEvent("OutlawFleeStairsCheck", + '{ JobReference = ' .. jobIndex .. ' }')) + activeEffect.OnTurnEnds:Add(priority_gen, + RogueEssence.Dungeon.SingleCharScriptEvent("OutlawCheck", '{ JobReference = ' .. jobIndex .. ' }')) + elseif job.Type == "OUTLAW_ITEM" or job.Type == "OUTLAW_ITEM_UNK" then + activeEffect.OnTurnEnds:Add(priority_gen, + RogueEssence.Dungeon.SingleCharScriptEvent("OutlawItemCheckItem", + '{ JobReference = ' .. jobIndex .. ' }')) + activeEffect.OnTurnEnds:Add(priority_gen, + RogueEssence.Dungeon.SingleCharScriptEvent("OutlawItemCheck", '{ JobReference = ' .. jobIndex .. ' }')) + elseif job.Type == "OUTLAW_MONSTER_HOUSE" then + activeEffect.OnTurnEnds:Add(priority_gen, + RogueEssence.Dungeon.SingleCharScriptEvent("MonsterHouseOutlawCheck", + '{ JobReference = ' .. jobIndex .. ' }')) + end + activeEffect.OnMapStarts:Add(priority_boss, + RogueEssence.Dungeon.SingleCharScriptEvent("SpawnOutlaw", '{ JobReference = ' .. jobIndex .. ' }')) + activeEffect.OnMapStarts:Add(priority_gen, + RogueEssence.Dungeon.SingleCharScriptEvent("OutlawFloor", '{ JobReference = ' .. jobIndex .. ' }')) + elseif job.Type == "RESCUE_SELF" or job.Type == "RESCUE_FRIEND" or job.Type == "DELIVERY" or job.Type == "ESCORT" then + npcMission = true + local char = job.Client + if job.Type == "RESCUE_FRIEND" or job.Type == "ESCORT" then char = job.Target --[[@as monsterIDTable]] end + local specificTeam = RogueEssence.LevelGen.SpecificTeamSpawner() + local post_mob = RogueEssence.LevelGen.MobSpawn() + post_mob.BaseForm = self:TableToMonsterID(char) + post_mob.Tactic = "slow_wander" + post_mob.Level = RogueElements.RandRange(50) + local dialogue + if job.Type == "RESCUE_SELF" or job.Type == "RESCUE_FRIEND" or job.Type == "DELIVERY" then + dialogue = RogueEssence.Dungeon.BattleScriptEvent("RescueReached") + elseif job.Type == "ESCORT" then + dialogue = RogueEssence.Dungeon.BattleScriptEvent("EscortReached") + end + post_mob.SpawnFeatures:Add(PMDC.LevelGen.MobSpawnInteractable(dialogue)) + post_mob.SpawnFeatures:Add(PMDC.LevelGen.MobSpawnLuaTable('{ JobReference = ' .. jobIndex .. ' }')) + post_mob.SpawnFeatures:Add(PMDC.LevelGen.MobSpawnUnrecruitable()) + --It is not possible to nickname in a spawn step. It'll happen on interact instead. + + specificTeam.Spawns:Add(post_mob) + PrintInfo("Creating Spawn") + local picker = LUA_ENGINE:MakeGenericType(PresetMultiTeamSpawnerType, { MapGenContextType }, {}) + picker.Spawns:Add(specificTeam) + PrintInfo("Creating Step") + local mobPlacement = LUA_ENGINE:MakeGenericType(PlaceRandomMobsStepType, { MapGenContextType }, { picker }) + + PrintInfo("Setting everything else") + mobPlacement.Ally = true + mobPlacement.Filters:Add(PMDC.LevelGen.RoomFilterConnectivity(PMDC.LevelGen.ConnectivityRoom.Connectivity.Main)) + mobPlacement.ClumpFactor = 20 + PrintInfo("Enqueueing") + queue:Enqueue(priority_npc, mobPlacement) + elseif job.Type == "LOST_ITEM" then + local lost_item = RogueEssence.Dungeon.MapItem(job.Item) + lost_item.HiddenValue = tostring(jobIndex) + lost_item.Cursed = true + PrintInfo("Spawning Lost Item " .. lost_item.Value) + local preset_picker = LUA_ENGINE:MakeGenericType(PresetPickerType, { MapItemType }, { lost_item }) + local multi_preset_picker = LUA_ENGINE:MakeGenericType(PresetMultiRandType, { MapItemType }, + { preset_picker }) + local picker_spawner = LUA_ENGINE:MakeGenericType(PickerSpawnType, { MapGenContextType, MapItemType }, + { multi_preset_picker }) + local random_room_spawn = LUA_ENGINE:MakeGenericType(RandomRoomSpawnStepType, + { MapGenContextType, MapItemType }, {}) + random_room_spawn.Spawn = picker_spawner + random_room_spawn.Filters:Add(PMDC.LevelGen.RoomFilterConnectivity(PMDC.LevelGen.ConnectivityRoom + .Connectivity.Main)) + queue:Enqueue(priority_npc, random_room_spawn) + end + + -- add destination floor notification, except for bossfight-style outlaws + if not globals.job_types[job.Type].target_outlaw or job.Type == "OUTLAW_ITEM_UNK" then + activeEffect.OnMapStarts:Add(priority_gen, RogueEssence.Dungeon.SingleCharScriptEvent("DestinationFloor", "{}")) + end + + if job.Type == "EXPLORATION" then + local escort = false + for i = 0, _DATA.Save.ActiveTeam.Guests.Count - 1, 1 do + local guest = GAME:GetPlayerGuestMember(i) + if guest.LuaData and guest.LuaData.JobReference == jobIndex then + escort = true + break + end + end + if escort then + activeEffect.OnMapStarts:Add(priority_gen, RogueEssence.Dungeon.SingleCharScriptEvent("ExplorationReached", '{ JobReference = ' .. jobIndex .. ' }')) + end + elseif job.Type == "LOST_ITEM" then + activeEffect.OnTurnEnds:Add(priority_gen, + RogueEssence.Dungeon.SingleCharScriptEvent("MissionItemCheck", '{ JobReference = ' .. jobIndex .. ' }')) + end + + if npcMission then + activeEffect.OnMapTurnEnds:Add(priority_gen, + RogueEssence.Dungeon.SingleCharScriptEvent("MobilityEndTurn", '{}')) + end + + local destNote = LUA_ENGINE:MakeGenericType(MapEffectStepType, { MapGenContextType }, { activeEffect }) + local priority = RogueElements.Priority(priority_gen) + queue:Enqueue(priority, destNote) + end +end + +-- ----------------------------------------------------------------------------------------- -- +-- #region SINGLE_CHAR_SCRIPT support +-- ----------------------------------------------------------------------------------------- -- +-- API specific for functions that are meant for event_single.lua + +--- Meant to be called in ``SINGLE_CHAR_SCRIPT.SpawnOutlaw``. It implements the entire SpawnOutlaw event used by the library. +--- It is called at the start of an outlaw job's destination floor. It scans for a spawn point for the outlaw, then generates +--- the character and adds it in that tile. +--- Please pass all of the event's parameters to this function, in order. +function library:SpawnOutlaw(_, _, context, args) + if context.User ~= nil then + return + end + local jobIndex = args.JobReference + local job = self.root.taken[jobIndex] + if job.Completion == globals.completion.NotCompleted then + local origins = { _DATA.Save.ActiveTeam.Leader.CharLoc } + local minRadius, maxRadius = 3, 5 + + local lastradius = 0 + local spawn_candidates = {} + + if job.Type == "OUTLAW_ITEM_UNK" then + minRadius = maxRadius + origins = {} + local diam = minRadius*2 +1 + for x = minRadius, _ZONE.CurrentMap.Width, diam do + for y = minRadius, _ZONE.CurrentMap.Height, diam do + table.insert(origins, RogueElements.Loc(x,y)) + end + end + end + + while #origins > 0 and #spawn_candidates<=0 do + local origin, oIndex = self:WeightlessRandom(origins, true) + ---@cast origin {X:integer, Y:integer} + table.remove(origins, oIndex) + for pos, radius in iter_spiral(origin, maxRadius, 2) do + if radius > minRadius and radius > lastradius and #spawn_candidates > 0 then break end + + + if not _ZONE.CurrentMap:TileBlocked(pos) and not _ZONE.CurrentMap:GetCharAtLoc(pos) then + + local next_to_player_units = false + --don't spawn the outlaw directly next to the player or their teammates + for i = 1, GAME:GetPlayerPartyCount(), 1 do + local party_member = GAME:GetPlayerPartyMember(i - 1) + if math.abs(party_member.CharLoc.X - pos.X) <= 1 and math.abs(party_member.CharLoc.Y - pos.Y) <= 1 then + next_to_player_units = true + break + end + end + --guests too! + if not next_to_player_units then + for i = 1, GAME:GetPlayerGuestCount(), 1 do + local party_member = GAME:GetPlayerGuestMember(i - 1) + if math.abs(party_member.CharLoc.X - pos.X) <= 1 and math.abs(party_member.CharLoc.Y - pos.Y) <= 1 then + next_to_player_units = true + break + end + end + end + if not next_to_player_units then + table.insert(spawn_candidates, pos) + end + end + end + end + + if #spawn_candidates < 1 then + logError(globals.error_types.MAPGEN, "Outlaw couldn't spawn for current floor, not enough spawn candidates.") + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.OUTLAW_SPAWN_FAIL):ToLocal(), self:GetCharacterName(job.Target))) + return + end + + -- Pick a valid space and generate the outlaw + local spawn_loc = self:WeightlessRandom(spawn_candidates, true) + local new_team = RogueEssence.Dungeon.MonsterTeam() + local mob_data = RogueEssence.Dungeon.CharData(true) + local form = _DATA:GetMonster(job.Target.Species).Forms[job.Target.Form] + mob_data.BaseForm = self:TableToMonsterID(job.Target) + + local party_tot, party_avg, party_hst, party_count = 0, 0, 0, _DATA.Save.ActiveTeam.Players.Count + for i = 0, party_count - 1, 1 do + local char_lvl = _DATA.Save.ActiveTeam.Players[i].Level + party_tot = party_tot + char_lvl + party_hst = math.max(char_lvl, party_hst) + end + party_avg = party_tot // party_count + + local diff = self:NumToDifficulty(job.Difficulty) + local orig_level = self.data.difficulty_data[diff].outlaw_level + local dungeon_default = not orig_level + local zone = _ZONE.CurrentZone + if dungeon_default then + if zone.LevelCap then + orig_level = getFloorAverage() --deal with level reset dungeon separately + else + orig_level = zone.Level + end + end + ---@cast orig_level integer + local level = orig_level + if self.data.outlaw_level_scaling then + self.data.outlaw_level_scaling(orig_level, dungeon_default, party_avg, party_hst, self.data) + end + local ability = form:RollIntrinsic(_DATA.Save.Rand, 3) + mob_data.BaseIntrinsics[0] = ability + local new_mob = RogueEssence.Dungeon.Character(mob_data) + new_mob.Level = level + + local skill_blacklist = {'teleport', 'guillotine', 'sheer_cold', 'horn_drill', 'fissure', 'memento', + 'healing_wish', 'lunar_dance', 'self_destruct', 'explosion', 'final_gambit', 'perish_song', + 'dragon_rage' } + local blacklist = {} + for _, skill_id in ipairs(skill_blacklist) do blacklist[skill_id] = true end + local eggMoveChance = { + {val=0, Weight=#self.data.difficulty_list-job.Difficulty}, + {val=1, Weight=#self.data.difficulty_list+job.Difficulty-2} + } + local tms = new_mob.Level//25 --1 every 25 levels + local tutors = math.max(0, new_mob.Level//20-2) --1 every 20 levels, starting from lv40 + local eggs = self:WeightedRandom(eggMoveChance, true).val --50% to 100% chance to have one, depending on job difficulty + self:AssignBossMoves(new_mob, tms, tutors, eggs, blacklist) + + local tactic = nil + if job.Type =="OUTLAW_FLEE" then + tactic = _DATA:GetAITactic("super_flee_stairs") + elseif job.Type == "OUTLAW_ITEM_UNK" then + tactic = _DATA:GetAITactic("wander_smart") + else + tactic = _DATA:GetAITactic("boss") + end + + if self.data.apply_outlaw_changes then + self.data.apply_outlaw_changes(new_mob, job) + end + + if job.Type == "OUTLAW_ITEM" or job.Type == "OUTLAW_ITEM_UNK" then + new_mob.EquippedItem = RogueEssence.Dungeon.InvItem(job.Item) + new_mob.EquippedItem.HiddenValue = tostring(jobIndex) + new_mob.EquippedItem.Cursed = true + end + + new_mob.HP = new_mob.MaxHP; + new_mob.Unrecruitable = true + new_mob.Tactic = tactic + new_mob.CharLoc = spawn_loc + new_team.Players:Add(new_mob) + + local tbl = new_mob.LuaData + tbl.OutlawReference = jobIndex + + _ZONE.CurrentMap.MapTeams:Add(new_team) + new_mob:RefreshTraits() + _ZONE.CurrentMap:UpdateExploration(new_mob) + + local base_name = RogueEssence.Data.DataManager.Instance.DataIndices[RogueEssence.Data.DataManager.DataType.Monster]:Get(new_mob.BaseForm.Species).Name:ToLocal() + GAME:SetCharacterNickname(new_mob, job.Client.Nickname or base_name) + return new_mob + end +end + +--- Meant to be called in ``SINGLE_CHAR_SCRIPT.OutlawFloor``. It implements the entire OutlawFloor event used by the library. +--- It is called at the start of an outlaw job's destination floor, only if the outlaw spawned successfully. +--- It displays different introduction messages depending on the job type, starts the outlaw music if the outlaw is not unknown, +--- and spawns a monster house if the job requires it. +--- Please pass all of the event's parameters to this function, in order. +function library:OutlawFloor(owner, ownerChar, context, args) + local outlaw = context.User + if outlaw == nil then + return + end + local tbl = outlaw.LuaData + if tbl and tbl.OutlawReference then + local jobIndex = args.JobReference + local job = self.root.taken[jobIndex] + if job.Type ~= "OUTLAW_ITEM_UNK" then + outlaw.Nickname = RogueEssence.Dungeon.CharData.GetFullFormName(self:TableToMonsterID(job.Target)) + SOUND:PlayBGM(self.data.outlaw_music_name, true, 20) + UI:ResetSpeaker() + DUNGEON:CharTurnToChar(outlaw, GAME:GetPlayerPartyMember(0)) + self:TeamTurnTo(outlaw) + if globals.job_types[job.Type].boss then + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.OUTLAW_REACHED):ToLocal())) + end + + GAME:WaitFrames(20) + UI:SetSpeaker(outlaw) + if job.Type == "OUTLAW_FLEE" then + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.OUTLAW_FLEE):ToLocal())) + outlaw.CharDir = _DUNGEON.ActiveTeam.Leader.CharDir + elseif job.Type == "OUTLAW_MONSTER_HOUSE" then + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.OUTLAW_MONSTER_HOUSE):ToLocal())) + SOUND:FadeOutBGM(20) + GAME:WaitFrames(20) + + -- ===========Monster house spawning logic============ + local goon_spawn_radius = 5 + + local origin = _DUNGEON.ActiveTeam.Leader.CharLoc + + local leftmost_x = math.maxinteger + local rightmost_x = math.mininteger + + local downmost_y = math.mininteger + local upmost_y = math.maxinteger + + + local topLeft = RogueElements.Loc(origin.X - goon_spawn_radius, origin.Y - goon_spawn_radius) + local bottomRight = RogueElements.Loc(origin.X + goon_spawn_radius, origin.Y + goon_spawn_radius) + + PrintInfo("Spawning monster house with top left " .. + topLeft.X .. ", " .. topLeft.Y .. " and bottom right " .. bottomRight.X .. ", " .. bottomRight.Y) + + local valid_tile_total = 0 + for x = math.max(topLeft.X, 0), math.min(bottomRight.X, _ZONE.CurrentMap.Width - 1), 1 do + for y = math.max(topLeft.Y, 0), math.min(bottomRight.Y, _ZONE.CurrentMap.Height - 1), 1 do + local testLoc = RogueElements.Loc(x, y) + local tile_block = _ZONE.CurrentMap:TileBlocked(testLoc) + local char_at = _ZONE.CurrentMap:GetCharAtLoc(testLoc) + + if tile_block == false and char_at == nil then + valid_tile_total = valid_tile_total + 1 + leftmost_x = math.min(testLoc.X, leftmost_x) + rightmost_x = math.max(testLoc.X, rightmost_x) + downmost_y = math.max(testLoc.Y, downmost_y) + upmost_y = math.min(testLoc.Y, upmost_y) + end + end + end + + local house_event = PMDC.Dungeon.MonsterHouseMapEvent(); + + local tl = RogueElements.Loc(leftmost_x - 1, upmost_y - 1) + local br = RogueElements.Loc(rightmost_x + 1, downmost_y + 1) + + local bounds = RogueElements.Rect.FromPoints(tl, br) + house_event.Bounds = bounds + + local min_goons = math.floor(valid_tile_total / 5) + local max_goons = math.floor(valid_tile_total / 4) + local total_goons = _DATA.Save.Rand:Next(min_goons, max_goons) + + local all_spawns = LUA_ENGINE:MakeGenericType(globals.ctypes.List, { globals.ctypes.MobSpawn }, {}) + for i = 0, _ZONE.CurrentMap.TeamSpawns.Count - 1, 1 do + local possible_spawns = _ZONE.CurrentMap.TeamSpawns:GetSpawn(i):GetPossibleSpawns() + for j = 0, possible_spawns.Count - 1, 1 do + local spawn = possible_spawns:GetSpawn(j):Copy() + all_spawns:Add(spawn) + end + end + + for _ = 1, total_goons, 1 do + local randint = _DATA.Save.Rand:Next(0, all_spawns.Count) + local spawn = all_spawns[randint] + spawn.SpawnFeatures:Add(PMDC.LevelGen.MobSpawnLuaTable('{ Goon = ' .. jobIndex .. ' }')) + spawn.SpawnFeatures:Add(PMDC.LevelGen.MobSpawnUnrecruitable()) + house_event.Mobs:Add(spawn) + end + local charaContext = RogueEssence.Dungeon.SingleCharContext(_DUNGEON.ActiveTeam.Leader) + TASK:WaitTask(house_event:Apply(owner, ownerChar, charaContext)) + GAME:WaitFrames(20) + else + --to prevent accidental button mashing making you waste your turn + GAME:WaitFrames(20) + end + + --Starts the player in team mode which they likely want to be in, this can help prevent desyncs as well + _DUNGEON:SetTeamMode(true) + else + + end + end +end + +--- Meant to be called in ``SINGLE_CHAR_SCRIPT.OutlawDeathCheck``. It implements the entire OutlawDeathCheck event used by the library. +--- It is called whenever a pokémon faints. If the pokémon is an outlaw, then this information will be saved for future handling. +--- Please pass all of the event's parameters to this function, in order. +function library:OutlawDeathCheck(_, _, context, _) + local subject = context.User + if subject.LuaData and subject.LuaData.OutlawReference then + self.root.mission_flags.OutlawDefeated = true + end + + subject.LuaData.OutlawReference = nil + subject.LuaData.Goon = nil +end + +--- Meant to be called in ``SINGLE_CHAR_SCRIPT.OutlawCheck``. It implements the entire OutlawCheck event used by the library. +--- It is called on turn end during a regular OUTLAW job, and checks whether the outlaw is marked as defeated or not. +--- If it is, the outlaw music will stop, and the player will be prompted to leave. +--- Please pass all of the event's parameters to this function, in order. +function library:OutlawCheck(_, _, _, args) + local jobIndex = args.JobReference + local job = self.root.taken[jobIndex] --[[@as jobTable]] + if job.Completion == globals.completion.NotCompleted and self.root.mission_flags.OutlawDefeated then + local outlaw_name = self:GetCharacterName(job.Target) + SOUND:PlayBGM(_ZONE.CurrentMap.Music, true) + self.root.mission_flags.MissionCompleted = true + library:MarkJobCompleted(job) + GAME:WaitFrames(50) + UI:ResetSpeaker() + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.OUTLAW_DEFEATED):ToLocal(), outlaw_name)) + + self.root.mission_flags.PriorMapSetting = _DUNGEON.ShowMap + _DUNGEON.ShowMap = _DUNGEON.MinimapState.None + self:AskMissionWarpOut() + end +end + +--- Meant to be called in ``SINGLE_CHAR_SCRIPT.OutlawFleeStairsCheck``. It implements the entire OutlawFleeStairsCheck event used by the library. +--- It is called on turn end during an OUTLAW_FLEE job, and checks whether the outlaw has reached the stairs or not. +--- If it has, the outlaw music will stop, the outlaw will disappear, and the job will be marked as failed. +--- Please pass all of the event's parameters to this function, in order. +function library:OutlawFleeStairsCheck(_, _, _, args) + local stairs_arr = { + "stairs_back_down", "stairs_back_up", "stairs_exit_down", + "stairs_exit_up", "stairs_go_up", "stairs_go_down" + } + local stairs_set = {} + for _, id in ipairs(stairs_arr) do stairs_set[id] = true end + + local jobIndex = args.JobReference + local job = self.root.taken[jobIndex] + + local outlaw + for char in luanet.each(LUA_ENGINE:MakeList(_ZONE.CurrentMap:IterateCharacters(false, true))) do + if char.LuaData and char.LuaData.OutlawReference == jobIndex then + outlaw = char + break + end + end + + if outlaw then + local targetName = outlaw:GetDisplayName(true) + local map = _ZONE.CurrentMap; + local charLoc = outlaw.CharLoc + local tile = map:GetTile(charLoc) + local tile_effect_id = tile.Effect.ID + if tile and stairs_set[tile_effect_id] then + GAME:WaitFrames(20) + _DUNGEON:RemoveChar(outlaw) + GAME:WaitFrames(20) + UI:ResetSpeaker() + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.OUTLAW_FLED):ToLocal(), targetName)) + library:MarkJobFailed(job) + SOUND:PlayBGM(_ZONE.CurrentMap.Music, true) + GAME:WaitFrames(20) + end + end +end + +--- Meant to be called in ``SINGLE_CHAR_SCRIPT.MonsterHouseOutlawCheck``. It implements the entire MonsterHouseOutlawCheck event used by the library. +--- It is called on turn end during an OUTLAW_MONSTER_HOUSE job, and looks for both the outlaw and its goons, checking how many of them are defeated. +--- It will prompt different messages depending on whether the outlaw or the monster house are defeated first. When all of them are fainted, +--- the monster house music will stop, and the player will be prompted to leave. +--- Please pass all of the event's parameters to this function, in order. +function library:MonsterHouseOutlawCheck(_, _, _, args) + local jobIndex = args.JobReference + local job = self.root.taken[jobIndex] + local outlaw_name = self:GetCharacterName(job.Target) + + if job.Completion == globals.completion.NotCompleted then + local found_outlaw, found_goon = false, false + local outlaw = nil + for char in luanet.each(LUA_ENGINE:MakeList(_ZONE.CurrentMap:IterateCharacters(false, true))) do + if char.LuaData then + if char.LuaData.OutlawReference == jobIndex then + found_outlaw = true + outlaw = char + elseif char.LuaData.Goon == jobIndex then + found_goon = true + end + end + if found_goon and found_outlaw then break end + end + + if not self.root.mission_flags.MonsterHouseMessageNotified then + if found_goon and not found_outlaw then + GAME:WaitFrames(20) + UI:ResetSpeaker() + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.OUTLAW_BOSS_DEFEATED):ToLocal(), outlaw_name)) + self.root.mission_flags.MonsterHouseMessageNotified = true + end + if not found_goon and found_outlaw then + GAME:WaitFrames(40) + UI:SetSpeaker(outlaw) + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.OUTLAW_MINIONS_DEFEATED):ToLocal())) + self.root.mission_flags.MonsterHouseMessageNotified = true + end + end + + if not (found_goon or found_outlaw) then + SOUND:PlayBGM(_ZONE.CurrentMap.Music, true) + self.root.mission_flags.MissionCompleted = true + self:MarkJobCompleted(job) + GAME:WaitFrames(20) + UI:ResetSpeaker() + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.OUTLAW_HOUSE_DEFEATED):ToLocal(), outlaw_name)) + self.root.mission_flags.PriorMapSetting = _DUNGEON.ShowMap + _DUNGEON.ShowMap = _DUNGEON.MinimapState.None + self:AskMissionWarpOut() + end + end +end + +--- Meant to be called in ``SINGLE_CHAR_SCRIPT.OutlawItemCheckItem``. It implements the entire OutlawItemCheckItem event used by the library. +--- It is called on turn end during an OUTLAW_ITEM or OUTLAW_ITEM_UNK job, and checks whether the outlaw item is in the player's inventory. +--- If it is, then this information will be saved for future handling. +--- Please pass all of the event's parameters to this function, in order. +function library:OutlawItemCheckItem(_, _, _, args) + if not self.root.mission_flags.ItemPickedUp then + local jobIndex = args.JobReference + local job = self.root.taken[jobIndex] + + for i = 0, GAME:GetPlayerBagCount()-1, 1 do + local invItem = GAME:GetPlayerBagItem(i) + if invItem.ID == job.Item and invItem.HiddenValue == tostring(jobIndex) then + self.root.mission_flags.ItemPickedUp = true + break + end + end + if not self.root.mission_flags.ItemPickedUp then + for i = 0, GAME:GetPlayerPartyCount()-1, 1 do + local equipItem = GAME:GetPlayerEquippedItem(i) + if equipItem and equipItem.ID == job.Item and equipItem.HiddenValue == tostring(jobIndex) then + self.root.mission_flags.ItemPickedUp = true + break + end + end + end + end +end + +--- Meant to be called in ``SINGLE_CHAR_SCRIPT.OutlawItemCheck``. It implements the entire OutlawItemCheck event used by the library. +--- It is called on turn end during an OUTLAW_ITEM or OUTLAW_ITEM_UNK job, and checks whether the outlaw is marked as defeated or not, +--- and if the item has been retrieved. +--- It will display different messages depending on the specific job type and its completion state. +--- When the outlaw is defeated and the item retrieved, the outlaw music will stop, and the player will be prompted to leave. +--- Please pass all of the event's parameters to this function, in order. +function library:OutlawItemCheck(_, _, _, args) + local jobIndex = args.JobReference + local job = self.root.taken[jobIndex] + local outlawName = self:GetCharacterName(job.Target) + local item_name = RogueEssence.Dungeon.InvItem(job.Item):GetDisplayName() + local complete_job = function(keyEx) + self.root.mission_flags.MissionCompleted = true + self:MarkJobCompleted(job) + SOUND:PlayBGM(_ZONE.CurrentMap.Music, true) + GAME:WaitFrames(50) + UI:ResetSpeaker() + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(keyEx):ToLocal(), outlawName, item_name)) + --Clear but remember minimap state + self.root.mission_flags.PriorMapSetting = _DUNGEON.ShowMap + _DUNGEON.ShowMap = _DUNGEON.MinimapState.None + self:AskMissionWarpOut() + end + + if job.Completion == globals.completion.NotCompleted then + if self.root.mission_flags.OutlawItemState == 0 then --nothing done yet + if self.root.mission_flags.OutlawDefeated then + self.root.mission_flags.OutlawItemState = 1 + if job.Type == "OUTLAW_ITEM_UNK" then + GAME:WaitFrames(50) + UI:ResetSpeaker() + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.OUTLAW_ITEM_UNK_DEFEATED):ToLocal(), outlawName,item_name)) + else + SOUND:PlayBGM(_ZONE.CurrentMap.Music, true) + end + elseif self.root.mission_flags.ItemPickedUp then + self.root.mission_flags.OutlawItemState = 2 + if job.Type == "OUTLAW_ITEM_UNK" then + GAME:WaitFrames(50) + UI:ResetSpeaker() + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.OUTLAW_ITEM_UNK_RETRIEVED):ToLocal(), outlawName, item_name)) + else + GAME:WaitFrames(50) + UI:ResetSpeaker() + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.OUTLAW_ITEM_RETRIEVED):ToLocal(), outlawName, item_name)) + end + end + end + if self.root.mission_flags.OutlawItemState == 1 and self.root.mission_flags.ItemPickedUp then --outlaw already defeated, item taken now + complete_job(globals.keysEx.OUTLAW_ITEM_RETRIEVED) + elseif self.root.mission_flags.OutlawItemState == 2 and self.root.mission_flags.OutlawDefeated then --item already taken, outlaw defeated now + complete_job(globals.keysEx.OUTLAW_DEFEATED) + end + end +end + +--- Meant to be called in ``SINGLE_CHAR_SCRIPT.ExplorationReached``. It implements the entire ExplorationReached event used by the library. +--- It is called on floor start during an EXPLORATION job. The guest will thank the party and leave, and then the player will be prompted to leave as well. +--- Please pass all of the event's parameters to this function, in order. +function library:ExplorationReached(_, _, context, args) + if context.User ~= nil then + return + end + local jobIndex = args.JobReference + local job = self.root.taken[jobIndex] + local escort + for i = 0, _DATA.Save.ActiveTeam.Guests.Count - 1, 1 do + local guest = GAME:GetPlayerGuestMember(i) + if guest.LuaData and guest.LuaData.JobReference == jobIndex then + escort = guest + break + end + end + PrintInfo("Exploration reached at index " .. jobIndex .. " !") + if escort and not escort.Dead then + local curr_zone = _ZONE.CurrentZoneID + local curr_segment = _ZONE.CurrentMapID.Segment + + local escortName = escort:GetDisplayName(true) + local dungeon_name = self:GetSegmentName(curr_zone, curr_segment) + UI:ResetSpeaker() + self.root.mission_flags.MissionCompleted = true + self:MarkJobCompleted(job) + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.EXPLORATION_REACHED):ToLocal(), escortName, dungeon_name)) + self.root.mission_flags.PriorMapSetting = _DUNGEON.ShowMap + _DUNGEON.ShowMap = _DUNGEON.MinimapState.None + GAME:WaitFrames(20) + UI:SetSpeaker(escort) + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.EXPLORATION_THANKS):ToLocal(), dungeon_name)) + GAME:WaitFrames(20) + UI:ResetSpeaker() + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.EXPLORATION_DEPART):ToLocal(), escortName, dungeon_name)) + GAME:WaitFrames(20) + + -- warp out + TASK:WaitTask(_DUNGEON:ProcessBattleFX(escort, escort, _DATA.SendHomeFX)) + _DUNGEON:RemoveChar(escort) + _ZONE.CurrentMap.DisplacedChars:Remove(escort) + self.root.escort_jobs = self.root.escort_jobs-1 + RogueEssence.Dungeon.ExplorerTeam.MAX_TEAM_SLOTS = math.max(1, self.data.min_party_limit, self.root.previous_limit - self.root.escort_jobs) + + GAME:WaitFrames(50) + self:AskMissionWarpOut() + else + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.ESCORT_UNAVAILABLE):ToLocal(), self:GetCharacterName(job.Client))) + end +end + +--- Meant to be called in ``SINGLE_CHAR_SCRIPT.MissionItemCheck``. It implements the entire MissionItemCheck event used by the library. +--- It is called on turn end during a LOST_ITEM job, and checks if item has been retrieved. +--- When it is, this function will display a message, and then the player will be prompted to leave. +--- Please pass all of the event's parameters to this function, in order. +function library:MissionItemCheck(_, _, _, args) + if not self.root.mission_flags.ItemPickedUp then + local jobIndex = args.JobReference + local job = self.root.taken[jobIndex] + local itemFound = function() + self:MarkJobCompleted(job) + self.root.mission_flags.ItemPickedUp = true + self.root.mission_flags.MissionCompleted = true + GAME:WaitFrames(70) + UI:ResetSpeaker() + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.LOST_ITEM_RETRIEVED):ToLocal(), self:GetCharacterName(job.Client), self:GetItemName(job.Item))) + --Clear but remember minimap state + self.root.mission_flags.PriorMapSetting = _DUNGEON.ShowMap + _DUNGEON.ShowMap = _DUNGEON.MinimapState.None + + --Slight pause before asking to warp out + GAME:WaitFrames(20) + self:AskMissionWarpOut() + end + + for i = 0, GAME:GetPlayerBagCount()-1, 1 do + local invItem = GAME:GetPlayerBagItem(i) + if invItem.ID == job.Item and invItem.HiddenValue == tostring(jobIndex) then + itemFound() + break + end + end + if not self.root.mission_flags.ItemPickedUp then + for i = 0, GAME:GetPlayerPartyCount()-1, 1 do + local equipItem = GAME:GetPlayerEquippedItem(i) + if equipItem and equipItem.ID == job.Item and equipItem.HiddenValue == tostring(jobIndex) then + itemFound() + break + end + end + end + end +end + +--- Meant to be called in ``SINGLE_CHAR_SCRIPT.MissionGuestCheck``. It implements the entire MissionGuestCheck event used by the library. +--- It is called on turn end during a LOST_ITEM job. It checks if the requested item is in the player's inventory, and, if it is, +--- the player will be prompted to leave. +--- Please pass all of the event's parameters to this function, in order. +function library:MissionGuestCheck(_, _, context, _) + if not context.User.Dead then + return + end + local tbl = context.User.LuaData + if tbl and tbl.JobReference then + local targetName = context.User:GetDisplayName(true) + UI:ResetSpeaker() + UI:WaitShowDialogue(STRINGS:Format(RogueEssence.StringKey(globals.keysEx.ESCORT_FAINTED):ToLocal(), targetName)) + GAME:WaitFrames(40) + self.root.escort_jobs = self.root.escort_jobs-1 + RogueEssence.Dungeon.ExplorerTeam.MAX_TEAM_SLOTS = math.max(1, self.data.min_party_limit, self.root.previous_limit - self.root.escort_jobs) + + if self.data.losing_guests_means_defeat then + warpOut() + GAME:WaitFrames(80) + TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Failed)) + end + end +end + +--- Meant to be called in ``SINGLE_CHAR_SCRIPT.MobilityEndTurn``. It implements the entire MobilityEndTurn event used by the library. +--- It is called on turn end during any job that has an Ally character somehwere on the floor. It constantly resets the character's movement +--- capabilities, ensuring it is always set to only walk on solid ground. +--- Please pass all of the event's parameters to this function, in order. +function library:MobilityEndTurn(_, _, _, _) + for char in luanet.each(LUA_ENGINE:MakeList(_ZONE.CurrentMap:IterateCharacters(true, false))) do + if char.MemberTeam ~= _DATA.Save.ActiveTeam and char.LuaData and char.LuaData.JobReference and self.root.taken[char.LuaData.JobReference].Completion == globals.completion.NotCompleted then + char.Mobility = RogueEssence.Data.TerrainData.Mobility.Passable + end + end +end + +return library diff --git a/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/missiongen_menus.lua b/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/missiongen_menus.lua new file mode 100644 index 0000000000..0659209d8f --- /dev/null +++ b/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/missiongen_menus.lua @@ -0,0 +1,591 @@ +-- PMDO Mission Generation Library v1.0.3, by MistressNebula +-- Menu file +-- ----------------------------------------------------------------------------------------- -- +-- This file contains all menus used by the library, separated from the main systems for +-- organization purposes. +-- If you are looking for the main systems, please refer to missiongen_lib.lua +-- ----------------------------------------------------------------------------------------- -- +-- This file is already loaded by missiongen_lib.lua. You don't need to require it +-- explicitly in your project unless you want to use the classes within in a more direct way. + + +local menus = { + LINE_HEIGHT = 12, + VERT_SPACE = 14, + TITLE_HEIGHT = 12 + RogueEssence.Content.GraphicsManager.MenuBG.TileHeight, + SCREEN_HEIGHT = RogueEssence.Content.GraphicsManager.ScreenHeight, + SCREEN_WIDTH = RogueEssence.Content.GraphicsManager.ScreenWidth, + BORDER_HEIGHT = RogueEssence.Content.GraphicsManager.MenuBG.TileHeight, + BORDER_WIDTH = RogueEssence.Content.GraphicsManager.MenuBG.TileWidth, +} + + + + +--- @class BoardSelectionMenu Menu that allows the player to open either a specific board or the taken job list +local BoardSelectionMenu = Class('BoardSelectionMenu') + +--- Creates the menu, using the provided data and callback +--- @param library table the entire job library structure +--- @param board_id string the id of the board to be prompted +--- @param callback function that handles the menu's output. The value is equal to the index selected +function BoardSelectionMenu:initialize(library, board_id, callback, start_choice) + assert(self, "BoardSelectionMenu:initialize(): Error, self is nil!") + self.callback = callback + start_choice = start_choice or 0 + + local choices = { + {STRINGS:FormatKey(library.data.boards[board_id].display_key), not library:IsBoardEmpty(board_id), function() self:choose(1) end}, + {STRINGS:FormatKey(library.globals.keys.TAKEN_TITLE), not library:IsTakenListEmpty(), function() self:choose(2) end}, + {STRINGS:FormatKey("MENU_EXIT"), true, function() self:choose(-1) end} + } + self.menu = RogueEssence.Menu.ScriptableSingleStripMenu(24, 22, 100, choices, start_choice, function() self:choose(-1) end) +end + +--- Confirmation function that runs the stored callback and closes the menu. +--- @param i number the index of the selected choice, or -1 if either the exit option was selected or nothing was. +function BoardSelectionMenu:choose(i) + self.callback(i) +end + +--- Runs a BoardSelectionMenu instance and returns its selected index +--- @param library table the entire job library structure +--- @param board_id string the id of a board. It will be used to display the first option. +--- @param start_choice? integer The 1-based index of the choice that will be selected when opening the menu. Defaults to 1. +--- @return integer #the index of the chosen option, or -1 if the menu was exited without selecting anything. +function BoardSelectionMenu.run(library, board_id, start_choice) + start_choice = start_choice or 1 + local ret = -1 + local cb = function(choice) + ret = choice + _MENU:RemoveMenu() + end + local menu = BoardSelectionMenu:new(library, board_id, cb, start_choice-1) + UI:SetCustomMenu(menu.menu) + UI:WaitForChoice() + return ret +end + + + + + +--- @class BoardMenu Menu that displays the contents of a board and allows the player to interact with it. +local BoardMenu = Class('BoardMenu') + +--- Creates the menu, using the provided data and callback +--- @param library table the entire job library structure +--- @param board_id string the id of the board to be prompted +--- @param callback function that handles the menu's output. The value is equal to the index selected +--- @param start_choice number the choice that should be selected first. Defaults to 1 +function BoardMenu:initialize(library, board_id, callback, start_choice) + assert(self, "BoardMenu:initialize(): Error, self is nil!") + self.MAX_ENTRIES = 4 + + start_choice = start_choice or 0 + self.library = library + self.board_id = board_id --if nil, then taken board + self.callback = callback + if board_id then + self.board_content = self.library.root.boards[board_id] + self.board_title = self.library.data.boards[board_id].display_key + else + self.board_content = self.library.root.taken + self.board_title = self.library.globals.keys.TAKEN_TITLE + end + + self.menu = RogueEssence.Menu.ScriptableMenu(32, 32, 256, 176, function(input) self:Update(input) end) + self.choices = self:GenerateOptions() + self.pages = math.ceil(#self.choices / self.MAX_ENTRIES) + self.taken_count = self.library:GetTakenCount() + if self.pages <= 0 then + _MENU:RemoveMenu() + return + end + + self:DrawBoardStatic() + self:Select(start_choice) +end + + +function BoardMenu:GenerateOptions() + local choices = {} + for i, job in pairs(self.board_content) do + ---@cast job jobTable + local zone_str, floor_pattern = self.library:GetSegmentName(job.Zone, job.Segment) + local target, item = "[color=#FF0000]TARGET[color]", "[color=#FF0000]ITEM[color]" + local client = self.library:GetCharacterName(job.Client) + if job.MenuOverrides and job.MenuOverrides[self.library.globals.overrides.CLIENT] then + client = STRINGS:FormatKey(job.MenuOverrides[self.library.globals.overrides.CLIENT]) end + if job.Target then target = self.library:GetCharacterName(job.Target) end + if job.Item and job.Item ~= "" then item = self.library:GetItemName(job.Item) end + + local title = STRINGS:FormatKey(job.Title, target, zone_str, item, client) + local taken_string = 'false' + + if job.Taken then + taken_string = 'true' + end + + local difficulty = self.library:GetDifficultyString(job) + -- This is the whole reason why we need a shallowcopy function for the mission data: we can't guarantee the structures point to the same job after save and load + local icon = "" + if not self.board_id then + if job.Taken and not self.library:HasExternalEvents(job.Zone) --if there is an eternal condition, show as not active + then icon = STRINGS:Format("\\uE10F")--open letter + else icon = STRINGS:Format("\\uE10E")--closed letter + end + else + if job.Taken + then icon = STRINGS:Format("\\uE10E")--closed letter + else icon = STRINGS:Format("\\uE110")--paper + end + end + local location = zone_str + if not job.HideFloor then location = location.. " " .. STRINGS:Format(floor_pattern, tostring(job.Floor)) end + + local color = Color.White + --color everything red if job is taken and this is a job board + local enabled = not (self.board_id and job.Taken) + if not enabled then + color = Color.Red + end + + + --modulo the iterator so that if we're on the 2nd page it goes to the right spot + local choice = RogueEssence.Menu.MenuElementChoice(function() self:choose(i) end, enabled) + local x, y0 = 17, menus.TITLE_HEIGHT + choice.Bounds = RogueElements.Rect(x, y0 + (menus.VERT_SPACE*2) * ((i-1) % self.MAX_ENTRIES), self.menu.Bounds.Width-x*2, menus.VERT_SPACE*2) + + choice.Elements:Add(RogueEssence.Menu.MenuText(icon, RogueElements.Loc(4, 8), color)) + choice.Elements:Add(RogueEssence.Menu.MenuText(title, RogueElements.Loc(16, 8), color)) + choice.Elements:Add(RogueEssence.Menu.MenuText(location, RogueElements.Loc(16, 8 + menus.LINE_HEIGHT), color)) + choice.Elements:Add(RogueEssence.Menu.MenuText(difficulty, RogueElements.Loc(choice.Bounds.Width - 4, 8 + menus.LINE_HEIGHT), RogueElements.DirV.Up, RogueElements.DirH.Right, color)) + table.insert(choices, choice) + end + return choices +end + +function BoardMenu:choose(i) + self.callback(i) +end + +function BoardMenu:DrawBoardStatic() + --Standard menu divider + self.menu.Elements:Add(RogueEssence.Menu.MenuDivider(RogueElements.Loc(menus.BORDER_WIDTH, menus.TITLE_HEIGHT), self.menu.Bounds.Width - menus.BORDER_WIDTH * 2)) + --Standard title + self.menu.Elements:Add(RogueEssence.Menu.MenuText(STRINGS:FormatKey(self.board_title), RogueElements.Loc(16, 8))) + --page number + self.page_num = RogueEssence.Menu.MenuText("(0/0)", RogueElements.Loc(self.menu.Bounds.Width - 17, menus.BORDER_HEIGHT), RogueElements.DirH.Right) + self.menu.Elements:Add(self.page_num) + --Another divider, plus accepted counter + self.menu.Elements:Add(RogueEssence.Menu.MenuDivider(RogueElements.Loc(8, self.menu.Bounds.Height - 24), self.menu.Bounds.Width - 8 * 2)) + self.menu.Elements:Add(RogueEssence.Menu.MenuText(STRINGS:FormatKey(self.library.globals.keys.JOB_ACCEPTED, #self.library.root.taken, self.library:GetTakenSize()), RogueElements.Loc(96, self.menu.Bounds.Height - 20))) + --Cursor + self.cursor = RogueEssence.Menu.MenuCursor(self.menu) + self.menu.Elements:Add(self.cursor) + --In total, 6 elements. Remember this for menu refresh +end + +function BoardMenu:Select(index) + self.current = math.max(1, math.max(index, #self.choices)) + self.cursor_pos = (index-1) % self.MAX_ENTRIES +1 + self.page = (index-1) // self.MAX_ENTRIES +1 + self:SelectPage(self.page) +end + +function BoardMenu:SelectPage(num) + num = (num-1) % self.pages +1 + while self.menu.Elements.Count > 6 do + self.menu.Elements:RemoveAt(6) + end + local start = (num-1) * self.MAX_ENTRIES +1 + local finish = math.min(start+3, #self.choices) + for i = start, finish, 1 do + self.menu.Elements:Add(self.choices[i]) + end + self.page_choices = finish-start+1 + self.page = num + self.page_num:SetText("(" .. tostring(self.page) .. "/" .. tostring(self.pages) .. ")") + self:MoveCursor(math.min(self.cursor_pos, self.page_choices)) +end + +function BoardMenu:MoveCursor(index) + local max_page_entries = math.min(self.MAX_ENTRIES, #self.choices - (self.page-1) * self.MAX_ENTRIES) + self.cursor_pos = (index-1) % max_page_entries +1 + self.current = self.cursor_pos + (self.page-1) * self.MAX_ENTRIES + + self.cursor:ResetTimeOffset() + self.cursor.Loc = RogueElements.Loc(9, 28 * self.cursor_pos -1) +end + +function BoardMenu:Update(input) + if self.pages <= 0 then + _MENU:RemoveMenu() + return + end + if input:JustPressed(RogueEssence.FrameInput.InputType.Confirm) then + --open the selected job menu + self.choices[self.current]:OnConfirm() + elseif input:JustPressed(RogueEssence.FrameInput.InputType.Cancel) or input:JustPressed(RogueEssence.FrameInput.InputType.Menu) then + _GAME:SE("Menu/Cancel") + self:choose(-1) + else + local start = self.current + if RogueEssence.Menu.InteractableMenu.IsInputting(input, LUA_ENGINE:MakeLuaArray(Dir8, { Dir8.Down, Dir8.DownLeft, Dir8.DownRight })) then + self:MoveCursor(self.cursor_pos+1) + elseif RogueEssence.Menu.InteractableMenu.IsInputting(input, LUA_ENGINE:MakeLuaArray(Dir8, { Dir8.Up, Dir8.UpLeft, Dir8.UpRight })) then + self:MoveCursor(self.cursor_pos-1) + elseif RogueEssence.Menu.InteractableMenu.IsInputting(input, LUA_ENGINE:MakeLuaArray(Dir8, {Dir8.Right})) then + self:SelectPage(self.page+1) + elseif RogueEssence.Menu.InteractableMenu.IsInputting(input, LUA_ENGINE:MakeLuaArray(Dir8, {Dir8.Left})) then + self:SelectPage(self.page-1) + end + + if start ~= self.current then + _GAME:SE("Menu/Select") + end + end +end + +--- Runs a BoardMenu instance and returns its selected index +--- @param library table the entire job library structure +--- @param board_id string|nil the id of the board to visualize. Set to nil to select the taken list. +--- @param start_choice|nil number the choice that should be selected first. Defaults to 1 +--- @return integer #the index of the chosen option, or -1 if the menu was exited without selecting anything. +function BoardMenu.run(library, board_id, start_choice) + local ret = -1 + local cb = function(choice) + ret = choice + _MENU:RemoveMenu() + end + local menu = BoardMenu:new(library, board_id, cb, start_choice or 1) + UI:SetCustomMenu(menu.menu) + UI:WaitForChoice() + return ret +end + +--- Adds a BoardMenu instance to the menu stack and then runs the given callback. +--- @param library table the entire job library structure. +--- @param board_id string|nil the id of the board to visualize. Set to nil to select the taken list. +--- @param callback fun(i:number):nil the callback to run when the menu needs to close. It will have a number passed to it that is equal to the selected index, or -1 if nothing was selected. +--- @param start_choice number|nil the choice that should be selected first. Defaults to 1 +function BoardMenu.add(library, board_id, callback, start_choice) + local menu = BoardMenu:new(library, board_id, callback, start_choice or 1) + _MENU:AddMenu(menu.menu, false) +end + +--- Replaces a previously opened BoardMenu with a fresh new instance and then runs the given callback. +--- @param library table the entire job library structure. +--- @param board_id string|nil the id of the board to visualize. Set to nil to select the taken list. +--- @param callback fun(i:number):nil the callback to run when the menu needs to close. It will have a number passed to it that is equal to the selected index, or -1 if nothing was selected. +--- @param start_choice number|nil the choice that should be selected first. Defaults to 1 +function BoardMenu.reset(library, board_id, callback, start_choice) + local menu = BoardMenu:new(library, board_id, callback, start_choice or 1) + _MENU:ReplaceMenu(menu.menu) +end + + + + +--- @class JobMenu Menu that allows the player to choose what to do with a specific job +local JobMenu = Class('JobMenu') + +--- Creates the menu, using the provided data and callback +--- @param library table the entire job library structure +--- @param board_id string the id of the board to be prompted +--- @param job_index number the index of the job to visualize +--- @param callback function that handles the menu's output. The value is equal to the index selected +function JobMenu:initialize(library, board_id, job_index, callback) + assert(self, "BoardSelectionMenu:initialize(): Error, self is nil!") + self.library = library + self.board = self.library.root.taken + if board_id then self.board = self.library.root.boards[board_id] end + self.job = self.board[job_index] --[[@as jobTable]] + self.callback = callback + + local choices = {} + local choice_str = STRINGS:FormatKey(self.library.globals.keys.BUTTON_TAKE) + if not board_id then + --if there is an external condition, lock quest and do not allow its activation + local enabled = not self.library:HasExternalEvents(self.job.Zone) and not self.library:IsJobDestinationInTaken(self.job, true) + if self.job.Taken and enabled then choice_str = STRINGS:FormatKey(self.library.globals.keys.BUTTON_SUSPEND) end + table.insert(choices, {choice_str, enabled, function() self:choose(1) end}) + table.insert(choices, {STRINGS:FormatKey(self.library.globals.keys.BUTTON_DELETE), true, function() self:choose(2) end}) + else + table.insert(choices, {STRINGS:FormatKey(self.library.globals.keys.BUTTON_TAKE), not self.library:IsTakenListFull() and not self.job.Taken, function() self:choose(1) end }) + end + table.insert(choices, {STRINGS:FormatKey("MENU_CANCEL"), true, function() self:choose(-1) end }) + self.menu = RogueEssence.Menu.ScriptableSingleStripMenu(232, 138, 24, choices, 0, function() self:choose(-1) end) + + self.job_window = self:GenerateSummary() + self.menu.LowerSummaryMenus:Add(self.job_window) +end + +--- Confirmation function that runs the stored callback and closes the menu. +--- @param i number the index of the selected choice, or -1 if nothing was selected. +function JobMenu:choose(i) + self.callback(i) +end + +function JobMenu:GenerateSummary() + local summary = RogueEssence.Menu.SummaryMenu(RogueElements.Rect(32, 32, 256, 176)) + local dungeon = self.library:GetSegmentName(self.job.Zone, self.job.Segment) + local target, item = "[color=#FF0000]TARGET[color]", "[color=#FF0000]ITEM[color]" + local client = self.library:GetCharacterName(self.job.Client) + if self.job.MenuOverrides and self.job.MenuOverrides[self.library.globals.overrides.CLIENT] then + client = STRINGS:FormatKey(self.job.MenuOverrides[self.library.globals.overrides.CLIENT]) end + if self.job.Target then target = self.library:GetCharacterName(self.job.Target) end + if self.job.Item and self.job.Item ~= "" then item = self.library:GetItemName(self.job.Item) end + + --Standard menu divider. Reuse this whenever you need a menu divider at the top for a title. + summary.Elements:Add(RogueEssence.Menu.MenuDivider(RogueElements.Loc(8, 8 + 12), summary.Bounds.Width - 8 * 2)) + + --Standard title. Reuse this whenever a title is needed. + summary.Elements:Add(RogueEssence.Menu.MenuText(Text.FormatKey(self.library.globals.keys.JOB_SUMMARY), RogueElements.Loc(16, 8))) + + --Accepted element + summary.Elements:Add(RogueEssence.Menu.MenuDivider(RogueElements.Loc(8, summary.Bounds.Height - 24), summary.Bounds.Width - 8 * 2)) + summary.Elements:Add(RogueEssence.Menu.MenuText(STRINGS:FormatKey(self.library.globals.keys.JOB_ACCEPTED, #self.library.root.taken, self.library:GetTakenSize()), RogueElements.Loc(96, summary.Bounds.Height - 20))) + + summary.Elements:Add(RogueEssence.Menu.MenuText(STRINGS:FormatKey(self.job.Flavor[1], target, dungeon, item, client), RogueElements.Loc(16, 24))) + if self.job.Flavor[2] and self.job.Flavor[2] ~= "" then + summary.Elements:Add(RogueEssence.Menu.MenuText(STRINGS:FormatKey(self.job.Flavor[2], target, dungeon, item, client), RogueElements.Loc(16, 36))) end + summary.Elements:Add(RogueEssence.Menu.MenuText(STRINGS:FormatKey(self.library.globals.keys.JOB_CLIENT), RogueElements.Loc(16, 54))) + summary.Elements:Add(RogueEssence.Menu.MenuText(STRINGS:FormatKey(self.library.globals.keys.JOB_OBJECTIVE), RogueElements.Loc(16, 68))) + summary.Elements:Add(RogueEssence.Menu.MenuText(STRINGS:FormatKey(self.library.globals.keys.JOB_PLACE), RogueElements.Loc(16, 82))) + summary.Elements:Add(RogueEssence.Menu.MenuText(STRINGS:FormatKey(self.library.globals.keys.JOB_DIFFICULTY), RogueElements.Loc(16, 96))) + summary.Elements:Add(RogueEssence.Menu.MenuText(STRINGS:FormatKey(self.library.globals.keys.JOB_REWARD), RogueElements.Loc(16, 110))) + + if self.job.MenuOverrides and self.job.MenuOverrides[self.library.globals.overrides.CLIENT] then + summary.Elements:Add(RogueEssence.Menu.MenuText(STRINGS:FormatKey(self.job.MenuOverrides[self.library.globals.overrides.CLIENT]), RogueElements.Loc(68, 54))) else + summary.Elements:Add(RogueEssence.Menu.MenuText(self.library:GetCharacterName(self.job.Client),RogueElements.Loc(68, 54))) end + summary.Elements:Add(RogueEssence.Menu.MenuText(self.library:GetObjectiveString(self.job), RogueElements.Loc(68, 68))) + summary.Elements:Add(RogueEssence.Menu.MenuText(self.library:GetDestinationString(self.job), RogueElements.Loc(68, 82))) + summary.Elements:Add(RogueEssence.Menu.MenuText(self.library:GetDifficultyString(self.job, true), RogueElements.Loc(68, 96))) + summary.Elements:Add(RogueEssence.Menu.MenuText(self.library:GetRewardString(self.job), RogueElements.Loc(68, 110))) + + return summary +end + +--- Runs a JobMenu instance and returns its selected index. +--- @param library table the entire job library structure. +--- @param board_id string|nil the id of the board this job is in. Set to nil to select the taken list. +--- @param job_index number the index of the job to visualize. +--- @return integer #the index of the chosen option, or -1 if the menu was exited without selecting anything. +function JobMenu.run(library, board_id, job_index) + local ret = -1 + local cb = function(choice) + ret = choice + _MENU:RemoveMenu() + end + local menu = JobMenu:new(library, board_id, job_index, cb) + UI:SetCustomMenu(menu.menu) + UI:WaitForChoice() + return ret +end + +--- Adds a JobMenu instance to the menu stack and then runs the given callback. +--- @param library table the entire job library structure +--- @param board_id string|nil the id of the board this job is in. Set to nil to select the taken list. +--- @param job_index number the index of the job to visualize. +--- @param callback fun(i:number):nil the callback to run when the menu needs to close. It will have a number passed to it that is equal to the selected index, or -1 if either the exit option was selected or nothing was. +function JobMenu.add(library, board_id, job_index, callback) + local menu = JobMenu:new(library, board_id, job_index, callback) + _MENU:AddMenu(menu.menu, false) +end + + +--- @class DungeonJobList Menu that allows the player to see what jobs are in the current dungeon. +local DungeonJobList = Class('DungeonJobList') + +--- Creates the menu, using the provided data if required +--- @param library table the entire job library structure +--- @param zone string|nil Optional. A zone id to display the jobs of. It will be ignored unless this menu is opened from a ground map. +--- @param segment string|nil Optional. A segment id to display the jobs of. It will be ignored unless this menu is opened from a ground map. +function DungeonJobList:initialize(library, zone, segment) + self.library = library + assert(self, "DungeonJobList:initialize(): Error, self is nil!") + self.MAX_ENTRIES = 10 + + self.zone = "" + self.segment = -1 + + if RogueEssence.GameManager.Instance.CurrentScene == RogueEssence.Dungeon.DungeonScene.Instance then + self.zone = _ZONE.CurrentZoneID + self.segment = _ZONE.CurrentMapID.Segment + elseif zone and segment then + self.zone = zone + self.segment = segment + end + + self.entries = self:GenerateEntries() + self.pages = math.max(0, #self.entries-1)//self.MAX_ENTRIES +1 + self.page = 1 + self.menu = RogueEssence.Menu.ScriptableMenu(32, 32, 256, 176, function(input) self:Update(input) end) + self:DrawMenu() +end + +function DungeonJobList:GenerateEntries() + local list = {} + local oth_segments = {} + local oth_segments_list = {} + local _, floor_pattern = self.library:GetSegmentName(self.zone, self.segment) + + local jobs = self.library.root.taken + for _, job in ipairs(jobs) do + ---@cast job jobTable + if job.Taken and job.Zone == self.zone then + if job.Segment == self.segment then + local icon = STRINGS:Format("\\uE10F") --open letter + if job.Completion>0 then icon = STRINGS:Format("\\uE10A") --check mark + elseif job.Completion<0 then icon = STRINGS:Format("\\uE10B") end -- X + + local floor = "" + if not job.HideFloor then + floor = STRINGS:Format(floor_pattern, job.Floor) + end + + local message = self.library:GetObjectiveString(job) + + table.insert(list, {icon = icon, floor = floor, message = message, floor_number = job.Floor}) + elseif self.segment and not oth_segments[job.Segment] and not job.Segment == 0 then + table.insert(oth_segments_list, job.Segment) + oth_segments[job.Segment] = true + end + end + end + table.sort(list, function (a, b) return a.floor_number < b.floor_number end) + if #oth_segments_list>0 then + table.sort(oth_segments_list) + for _, segment in ipairs(oth_segments_list) do + local seg_name = self.library:GetSegmentName(self.zone, segment) + local message = STRINGS:FormatKey(self.library.globals.keys.REACH_SEGMENT, seg_name) + table.insert(list, {icon = nil, floor = nil, message = message}) + end + end + if #list <= 0 then + if _DATA.Save.Rescue ~= nil and _DATA.Save.Rescue.Rescuing then + if self.segment ~= _DATA.Save.Rescue.SOS.Goal.StructID.Segment then + local seg_name = self.library:GetSegmentName(self.zone, _DATA.Save.Rescue.SOS.Goal.StructID.Segment) + local message = STRINGS:FormatKey(self.library.globals.keys.REACH_SEGMENT, seg_name) + table.insert(list, {icon = nil, floor = nil, message = message}) + else + local team_to_save = STRINGS:Format("[color=#FFA5FF]{0}[color]", _DATA.Save.Rescue.SOS.TeamName) + local message = STRINGS:FormatKey(self.library.globals.keys.RESCUE_SELF, team_to_save) + floor = STRINGS:Format(floor_pattern, _DATA.Save.Rescue.SOS.Goal.StructID.ID+1) + table.insert(list, {icon = STRINGS:Format("\\uE10F"), floor = floor, message = message}) + end + else + local missing = "[color=#FF0000]???[color]" + local conditions = self.library:GetExternalEvents(self.zone) + for _, condition in ipairs(conditions) do + if condition.message_key then + local icon = STRINGS:Format(condition.icon) or nil + local argtable = {} + if condition.message_args then argtable = condition.message_args(self.zone) end + table.insert(list, + { + icon = icon, + floor = nil, + message = STRINGS:FormatKey(condition.message_key, argtable[1] or missing, + argtable[2] or missing, argtable[3] or missing, argtable[4] or missing, argtable[5] or missing), + }) + end + end + if #list <= 0 then --default if it's still empty + table.insert(list, { icon = nil, floor = nil, message = STRINGS:FormatKey(self.library.globals.keys.OBJECTIVE_DEFAULT) }) + end + end + end + return list +end + +function DungeonJobList:DrawMenu() + self.menu.Elements:Clear() + + --Standard menu divider. Reuse this whenever you need a menu divider at the top for a title. + self.menu.Elements:Add(RogueEssence.Menu.MenuDivider(RogueElements.Loc(8, 8 + 12), self.menu.Bounds.Width - 8 * 2)) + + --Standard title. Reuse this whenever a title is needed. + self.menu.Elements:Add(RogueEssence.Menu.MenuText(STRINGS:FormatKey(self.library.globals.keys.OBJECTIVES_TITLE), RogueElements.Loc(16, 8))) + + --page number + if self.pages > 1 then + self.menu.Elements:Add(RogueEssence.Menu.MenuText("(" .. tostring(self.page) .. "/" .. tostring(self.pages) .. ")", RogueElements.Loc(self.menu.Bounds.Width - 17, menus.BORDER_HEIGHT), RogueElements.DirH.Right)) + end + + --how many jobs have we populated so far + local side_dungeon_mission = false + local zone_string = '' + + --populate jobs that are in this dungeon + local start = (self.page-1) * self.MAX_ENTRIES + 1 + local finish = math.min(self.page * self.MAX_ENTRIES, #self.entries) + local icon_x, floor_x, msg_x = 16, 16, 16 + for i = finish, start, -1 do + local count = (i-1) % self.MAX_ENTRIES + local entry = self.entries[i] + + if entry.icon then + if icon_x == floor_x then floor_x, msg_x = floor_x + 12, msg_x + 12 end + self.menu.Elements:Add(RogueEssence.Menu.MenuText(entry.icon, RogueElements.Loc(icon_x, 24 + 14 * count))) + end + if entry.floor then + if floor_x == msg_x then msg_x = msg_x + 32 end + self.menu.Elements:Add(RogueEssence.Menu.MenuText(entry.floor, RogueElements.Loc(floor_x, 24 + 14 * count))) + end + if entry.message then + self.menu.Elements:Add(RogueEssence.Menu.MenuText(entry.message, RogueElements.Loc(msg_x, 24 + 14 * count))) + end + end +end + +function DungeonJobList:Update(input) + if input:JustPressed(RogueEssence.FrameInput.InputType.Confirm) then + _GAME:SE("Menu/Cancel") + _MENU:RemoveMenu() + elseif input:JustPressed(RogueEssence.FrameInput.InputType.Cancel) then + _GAME:SE("Menu/Cancel") + _MENU:RemoveMenu() + elseif input:JustPressed(RogueEssence.FrameInput.InputType.Menu) then + _GAME:SE("Menu/Cancel") + _MENU:RemoveMenu() + elseif self.pages>1 and RogueEssence.Menu.InteractableMenu.IsInputting(input, LUA_ENGINE:MakeLuaArray(Dir8, {Dir8.Right})) then + self.page = (self.page) % self.pages + 1 + SOUND:PlaySE("Menu/Skip") + self:DrawMenu() + elseif self.pages>1 and RogueEssence.Menu.InteractableMenu.IsInputting(input, LUA_ENGINE:MakeLuaArray(Dir8, {Dir8.Left})) then + self.page = (self.page-2) % self.pages + 1 + SOUND:PlaySE("Menu/Skip") + self:DrawMenu() + end +end + +--- Runs a DungeonJobList instance. +--- @param library table the entire job library structure. +--- @param zone string|nil Optional. A zone id to display the jobs of. It will be ignored unless this menu is opened from a ground map. +--- @param segment string|nil Optional. A segment id to display the jobs of. It will be ignored unless this menu is opened from a ground map. +function DungeonJobList.run(library, zone, segment) + local menu = DungeonJobList:new(library, zone, segment) + UI:SetCustomMenu(menu.menu) + UI:WaitForChoice() +end + +--- Adds a DungeonJobList instance to the menu stack. +--- @param library table the entire job library structure +--- @param zone string|nil Optional. A zone id to display the jobs of. It will be ignored unless this menu is opened from a ground map. +--- @param segment string|nil Optional. A segment id to display the jobs of. It will be ignored unless this menu is opened from a ground map. +function DungeonJobList.add(library, zone, segment) + local menu = DungeonJobList:new(library, zone, segment) + _MENU:AddMenu(menu.menu, false) +end + +menus.BoardSelection = BoardSelectionMenu +menus.Board = BoardMenu +menus.Job = JobMenu +menus.Objectives = DungeonJobList + +return menus + + diff --git a/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/missiongen_service.lua b/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/missiongen_service.lua new file mode 100644 index 0000000000..6c329cdddb --- /dev/null +++ b/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/missiongen_service.lua @@ -0,0 +1,107 @@ +-- PMDO Mission Generation Library v1.0.3, by MistressNebula +-- Service file +-- ----------------------------------------------------------------------------------------- -- +-- This file contains the service routines required by the library, including loading the +-- library itself. +-- If you are looking for the main systems, please refer to missiongen_lib.lua +-- ----------------------------------------------------------------------------------------- -- +-- This is the only file outside of missiongen_lib.lua that must be loaded by modders. +-- More specifically, you should require this file from inside your mod's main.lua. +-- ----------------------------------------------------------------------------------------- -- +-- Make sure to write in the variable below, as a string, the name of the global variable you loaded the +-- missiongen_lib.lua file into: +local library_name = "MissionGen" + + +require 'nebula_mission_board.common' +require 'origin.services.baseservice' + +local MissionTools = Class('MissionTools', BaseService) + +--[[--------------------------------------------------------------- + MissionTools:initialize() + MissionTools class constructor +---------------------------------------------------------------]] +function MissionTools:initialize() + BaseService.initialize(self) + PrintInfo('MissionTools:initialize()') +end + +--[[--------------------------------------------------------------- + MissionTools:OnSaveLoad() + Called when a save is loaded or created for the first time. +---------------------------------------------------------------]] +function MissionTools:OnSaveLoad() + assert(self, 'MissionTools:OnSaveLoad() : self is null!') + assert(_G[library_name], 'MissionTools:OnSaveLoad() : '..library_name..' is null!') + _G[library_name]:load() +end + +--[[--------------------------------------------------------------- + RecruitTools:OnDungeonFloorEnd() + When leaving a dungeon floor this is called. +---------------------------------------------------------------]] +function MissionTools:OnDungeonFloorEnd() + assert(self, 'MissionTools:OnDungeonFloorEnd() : self is null!') + local zone, segment = _ZONE.CurrentZoneID, _ZONE.CurrentMapID.Segment + --Mark the current dungeon as visited + _G[library_name].root.dungeon_progress[zone] = _G[library_name].root.dungeon_progress[zone] or {} + _G[library_name].root.dungeon_progress[zone][segment] = _G[library_name].root.dungeon_progress[zone][segment] or false +end + +--[[--------------------------------------------------------------- + MissionTools:OnAddMenu(menu) + When a menu is about to be added to the menu stack this is called! +---------------------------------------------------------------]] +function MissionTools:OnAddMenu(menu) + local labels = RogueEssence.Menu.MenuLabel + if menu:HasLabel() then + if RogueEssence.GameManager.Instance.CurrentScene == RogueEssence.Dungeon.DungeonScene.Instance then + if menu.Label == labels.OTHERS_MENU then + local choices = menu:ExportChoices() + -- put right after Recruitment Search if present + local index = menu:GetChoiceIndexByLabel("OTH_RECRUIT")+1 + -- if failed, put right before Settings if present + if index <0 then index = menu:GetChoiceIndexByLabel(labels.OTH_SETTINGS) end + -- fall back to either 1 or choices count if both fail + if index <0 then index = math.min(1, menu.Choices.Count) end + choices:Insert(index, RogueEssence.Menu.MenuTextChoice("OTH_MISSION", STRINGS:FormatKey(_G[library_name].globals.keys.OPTION_OBJECTIVES_LIST), function () _G[library_name]:OpenObjectivesMenu() end)) + menu:ImportChoices(choices) + end + else + if menu.Label == labels.MAIN_MENU then + local choices = menu:ExportChoices() + local taken_count = #_G[library_name].root.taken + local job_list_color = Color.Red + if taken_count > 0 then job_list_color = Color.White end + -- put right before Others if present + local index = menu:GetChoiceIndexByLabel(labels.MAIN_OTHERS) + -- fall back to either 1 or choices count if the check fails + if index <0 then + index = math.min(1, menu.Choices.Count) + end + choices:Insert(index, RogueEssence.Menu.MenuTextChoice("MAIN_MISSION", STRINGS:FormatKey(_G[library_name].globals.keys.OPTION_JOBLIST), function () _G[library_name]:OpenTakenMenuFromMain() end, taken_count > 0, job_list_color)) + menu:ImportChoices(choices) + end + end + end +end + +---Summary +-- Subscribe to all channels this service wants callbacks from +function MissionTools:Subscribe(med) + med:Subscribe("MissionTools", EngineServiceEvents.NewGame, function(_, _) self.OnSaveLoad(self) end) + med:Subscribe("MissionTools", EngineServiceEvents.LoadSavedData, function(_, _) self.OnSaveLoad(self) end) + med:Subscribe("MissionTools", EngineServiceEvents.AddMenu, function(_, args) self.OnAddMenu(self, args[0]) end) + med:Subscribe("MissionTools", EngineServiceEvents.DungeonFloorExit, function(_, _) self.OnDungeonFloorEnd(self) end) +end + +---Summary +-- un-subscribe to all channels this service subscribed to +function MissionTools:UnSubscribe(med) +end + + +--Add our service +SCRIPT:AddService("MissionTools", MissionTools:new()) +return MissionTools diff --git a/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/missiongen_settings.lua b/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/missiongen_settings.lua new file mode 100644 index 0000000000..45cc02bff8 --- /dev/null +++ b/MODS/Nebula's Mission Board Mod/Data/Script/missiongen_lib/missiongen_settings.lua @@ -0,0 +1,2261 @@ +-- PMDO Mission Generation Library v1.0.3, by MistressNebula +-- Settings file +-- ----------------------------------------------------------------------------------------- -- +-- This file exists as a way to separate the library's configurable data from its functions. +-- If you are looking for the latter, please refer to missiongen_lib.lua +-- ----------------------------------------------------------------------------------------- -- +-- This file is already loaded by missiongen_lib.lua. You don't need to require it +-- explicitly in your project. +-- ----------------------------------------------------------------------------------------- -- +-- Make sure to also check the missiongen_service.lua file and ensure it will be able to +-- access the library by changing its "library_name" variable if necessary. + +--- @alias jobType "RESCUE_SELF"|"RESCUE_FRIEND"|"ESCORT"|"EXPLORATION"|"DELIVERY"|"LOST_ITEM"|"OUTLAW"|"OUTLAW_ITEM"|"OUTLAW_ITEM_UNK"|"OUTLAW_MONSTER_HOUSE"|"OUTLAW_FLEE" +--- @alias rewardType "money"|"item"|"money_item"|"item_item"|"client"|"exclusive" +--- @alias emotionType "Normal"|"Happy"|"Pain"|"Angry"|"Worried"|"Sad"|"Crying"|"Shouting"|"Teary-Eyed"|"Determined"|"Joyous"|"Inspired"|"Surprised"|"Dizzy"|"Special0"|"Special1"|"Sigh"|"Stunned"|"Special2"|"Special3" +--- @alias eventId "JobTake"|"JobActivate"|"JobDeactivate"|"DungeonStart"|"DungeonEnd"|"FloorStart"|"JobComplete"|"JobFail"|"BeforeReward"|"AfterReward" +--- @alias AgentIDTable {Species:string, Form:integer|nil, Skin:string|nil, Gender:integer, Weight:integer|nil, Unique:boolean|nil} +--- @alias Character any RogueEssence.Dungeon.Character +--- @alias MonsterTeam any RogueEssence.Dungeon.MonsterTeam + +local settings = { + --- Name of the SV table that will contain all stored data. Use a table to specify a deeper path. + --- If not present in the SV structure, the defined path will be generated automatically. + --- An empty list would just use SV as root. + --- Examples: + --- * "jobs" would use SV.jobs as its root. + --- * {"adventure", "jobs"} would use SV.adventure.jobs as its root. + ---@type string|string[] + sv_root_name = "jobs", + --- A list of board ids and the data necessary to define their behavior. + --- Format: = {display_key = string, size = integer, location = {zone = string, map = integer}, condition = function, job_types = {{id = string, weight = integer}}} + --- * : you can use any string in place of this. It will be used to identify the board in scripts + --- * display_key: the string key used when displaying the job menu for this board. The localized strings are fetched from the Menu Text list (strings.resx) + --- * size: the amount of quests this board can hold at a time + --- * location: a zone id and a map index. This data is where the player will be brought for the job completion cutscene. All of these maps must call library:PlayJobsCompletedCutscene before fade-in for the reward sequence to work correctly. + --- * condition: Optional. A function called to determine if library:PopulateBoards() should fill out this board. It receives the library object as an argument and returns a boolean. If it returns true, the board is filled out. If it returns false, it is left empty. If this property is missing, this board will always be active. + --- * job_types: a list of job types and their probability weight. Higher weight means more common. + ---@type table + boards = { + quest_board = { + display_key = "BOARD_QUEST_TITLE", + size = 8, + location = {zone = "guildmaster_island", map = 2}, + condition = function (library) + ---@type LibraryRootStruct + local root = library.root + local completed = 0 + for _, segments in pairs(root.dungeon_progress) do + for _, state in pairs(segments) do + if state then + completed = completed + 1 + if completed>=3 then return true end + break --count only once no matter how many segments are completed + end + end + end + return false + end, + job_types = { + {id = "RESCUE_SELF", weight = 5}, + {id = "RESCUE_FRIEND", weight = 5}, + {id = "ESCORT", weight = 2}, + {id = "EXPLORATION", weight = 2}, + {id = "DELIVERY", weight = 2}, + {id = "LOST_ITEM", weight = 4}, + {id = "OUTLAW", weight = 5}, + {id = "OUTLAW_ITEM", weight = 2}, + {id = "OUTLAW_ITEM_UNK", weight = 1}, + {id = "OUTLAW_MONSTER_HOUSE", weight = 1}, + {id = "OUTLAW_FLEE", weight = 1} + } + } + }, + --- Zone and map id of the ground map where players will be sent to when a day ends and there is at least one mission to hand in. + --- This map must contain a call to library:PlayJobsCompletedCutscene + --- Format: {zone = string, map = integer} + ---@type {zone:string, map:integer} + end_dungeon_day_destination = { zone = "guildmaster_island", map = 2 }, + --- Function ran after the game is done with the job completion cutscene. + --- It takes no arguments, and is not expected to return anything. + --- You can use it to fix up the game state however you like before returning control to the player. + ---@type fun() + after_rewards_function = function() + GAME:MoveCamera(0, 0, 1, true) + SOUND:PlayBGM(SV.base_town.Song, true) + end, + --- The maximum number of jobs that can be taken from job boards at a time. + ---@type integer + taken_limit = 8, + --- If true, taken jobs will be activated automatically. Players can always deactivate them manually if they so choose. + ---@type boolean + taken_jobs_start_active = true, + --- The maximum amount of guest-based jobs that can be generated in the same dungeon. + --- Guest-based jobs are: ESCORT, EXPLORATION + ---@type integer + max_guests = 1, + --- If true, guests will reduce the party size by 1 each. + --- This cannot lower the party size below 1, for obvious reasons. + --- Any excess guests will simply add to the party count. + ---@type boolean + guests_take_up_space = true, + --- Minimum number for the party limit when it is reduced because of guests. + --- This value cannot be lower than 1, for obvious reasons. + --- Any excess guests will simply add to the party count, without reducing the limit. + ---@type integer + min_party_limit = 1, + --- If true, losing any guest will count as a loss for the entire exploration. + --- If false, it will simply mark that specific job as failed. + ---@type boolean + losing_guests_means_defeat = false, + --- The priority of the SpawnOutlaw handler event. + --- This value should always be lower than dungeon_gen_steps_piority. + ---@type integer|integer[] + dungeon_boss_steps_piority = -11, + --- The priority that most of the job handlers will have when being added to the event list. + --- This value should always be equal to or higher than the priority set for the event that calls library:GenerateJobInFloor. + ---@type integer|integer[] + dungeon_gen_steps_piority = -6, + --- The priority that job npc spawners will have when being added to the event list. + --- This value should always be equal to or higher than dungeon_gen_steps_piority. + ---@type integer|integer[] + dungeon_npc_steps_piority = {5, 2, 1}, + --- Define here, in the order you want them to be in, the list of all difficulty rank ids you want to use. + ---@type string[] + difficulty_list = {"F", "E", "D", "C", "B", "A", "S", "STAR_1", "STAR_2", "STAR_3", "STAR_4", "STAR_5", "STAR_6", "STAR_7", "STAR_8", "STAR_9"}, + --- Clone of difficulty_list. Autogenerated on startup. Any value written here will be lost on load. + ---@type table + num_to_difficulty = {}, + --- Backwards reference for difficulty_list. Autogenerated on startup. Any value written here will be lost on load. + ---@type table + difficulty_to_num = {}, + --- All the data required to define difficulty rank behavior. There must be an entry for every rank defined in difficulty_list: + --- Format : = {display_key, money_reward, extra_reward, outlaw_level, escort_level} + --- * = one of the difficulties defined in "difficulty_list" + --- * display_key: string key used when displaying the name of the rank. The localized strings are fetched from the Menu Text list (strings.resx) + --- * money_reward: the amount of money awarded at the end of the job. If set to 0, money rewards will simply not award anything + --- * extra_reward: the points of extra reward awarded at the end of the job. What these points are depends on extra_reward_type. If set to 0, extra_reward_type will be considered to be "none" + --- * outlaw_level: Optional. The base level of all outlaws spawned by jobs with this difficulty. If omitted, it will be equal to the dungeon's expected level. + --- * escort_level: Optional. The level of all guests spawned by jobs with this difficulty. If omitted, it will be equal to the dungeon's expected level. + ---@type table + difficulty_data = { + F = {display_key = "RANK_STRING_F", money_reward = 100, extra_reward = 0}, + E = {display_key = "RANK_STRING_E", money_reward = 200, extra_reward = 100}, + D = {display_key = "RANK_STRING_D", money_reward = 400, extra_reward = 200}, + C = {display_key = "RANK_STRING_C", money_reward = 600, extra_reward = 400}, + B = {display_key = "RANK_STRING_B", money_reward = 700, extra_reward = 1250}, + A = {display_key = "RANK_STRING_A", money_reward = 1500, extra_reward = 2500}, + S = {display_key = "RANK_STRING_S", money_reward = 3000, extra_reward = 5000}, + STAR_1 = {display_key = "RANK_STRING_STAR_1", money_reward = 6000, extra_reward = 10000}, + STAR_2 = {display_key = "RANK_STRING_STAR_2", money_reward = 10000, extra_reward = 20000}, + STAR_3 = {display_key = "RANK_STRING_STAR_3", money_reward = 15000, extra_reward = 30000}, + STAR_4 = {display_key = "RANK_STRING_STAR_4", money_reward = 20000, extra_reward = 40000}, + STAR_5 = {display_key = "RANK_STRING_STAR_5", money_reward = 25000, extra_reward = 50000}, + STAR_6 = {display_key = "RANK_STRING_STAR_6", money_reward = 30000, extra_reward = 60000}, + STAR_7 = {display_key = "RANK_STRING_STAR_7", money_reward = 35000, extra_reward = 70000}, + STAR_8 = {display_key = "RANK_STRING_STAR_8", money_reward = 40000, extra_reward = 80000}, + STAR_9 = {display_key = "RANK_STRING_STAR_9", money_reward = 45000, extra_reward = 90000} + }, + --- Function that changes the level of guests based on the player's level. It must return the new level for the guest, or it will have no effect. + --- Arguments: + --- * lvl: base level of the guest. + --- * dungeon: if true, the level is the dungeon's recommended level. If false, it's taken from the difficulty data. + --- * avg_team_lvl: average level of the player team. It may or may not be an integer. + --- * hst_team_lvl: highest level in the player team. + --- * settings: the settings data structure itself + ---@type fun(lvl:integer, dungeon:boolean, avg_team_lvl:integer, hst_team_lvl:integer, settings: table): integer + guest_level_scaling = function(lvl, dungeon, avg_team_lvl, hst_team_lvl, settings) + local add = 0 + if dungeon then lvl = lvl*4//5 end -- set to 80% of dungeon default + if avg_team_lvl > lvl then add = (avg_team_lvl - lvl) // 10 end -- add 10% of the level difference between guest and average + return lvl + add + end, + --- Function that changes the level of outlaws based on the player's level. It must return the new level for the outlaw, or it will have no effect. + --- * lvl: base level of the outlaw. + --- * dungeon: if true, the level is the dungeon's recommended level. If false, it's taken from the difficulty data. + --- * avg_team_lvl: average level of the player team. It may or may not be an integer. + --- * hst_team_lvl: highest level in the player team. + --- * settings: the settings data structure itself + ---@type fun(lvl:integer, dungeon:boolean, avg_team_lvl:integer, hst_team_lvl:integer, settings: table): integer + outlaw_level_scaling = function(lvl, dungeon, avg_team_lvl, hst_team_lvl, settings) + local add = 0 + if dungeon then lvl = lvl*23//20 end -- set to 115% of dungeon default + if avg_team_lvl > lvl then add = (avg_team_lvl - lvl) // 8 end -- add 10% of the level difference between outlaw and average + add = add + (hst_team_lvl - avg_team_lvl) // 4 -- add 25% of the level difference between average and highest in the team + return lvl + add + end, + --- Function that allows you to edit an outlaw's data just before they are spawned. + --- You can change just about anything except its position and recruitability state. Its HP will also + --- always be automatically set to full after this function is called. + --- You will also be unable to change the EquippedItem of an "OUTLAW_ITEM" or "OUTLAW_ITEM_UNK" outlaw. + ---@type fun(outlaw:Character, job:jobTable) + apply_outlaw_changes = function(outlaw, job) + local max_boost = PMDC.Data.MonsterFormData.MAX_STAT_BOOST + outlaw.MaxHPBonus = math.min(outlaw.Level * max_boost // 64, max_boost); + if job.Type == "OUTLAW_FLEE" then + local speedMin = math.floor(outlaw.Level * 4 // 3) + local speedMax = math.floor(outlaw.Level * 6 // 2) + outlaw.SpeedBonus = math.min(_DATA.Save.Rand:Next(speedMin, speedMax), 100) + end + end, + --- A list of ids for types that are banned from ever being picked as fleeing outlaws. + ---@type string[] + fleeing_outlaw_restrictions = {"ghost"}, + --- The name of the outlaw music file. This file will be sourced from the Content/Music folder. + ---@type string + outlaw_music_name = "Outlaw.ogg", + --- This is where dungeon difficulty is set. Quests can only generate for dungeons inside this list. + --- Given the complexity of this structure, it is best generated using the "AddDungeonSection" function near the bottom of this file. + ---@type table> + dungeons = {}, + --- Jobs are sorted by dungeon, following this order. Missing dungeons are shoved at the bottom and sorted alphabetically. + --- This list is automatically populated in call order when using the "AddDungeonSection" function near the bottom of this file. + ---@type table + dungeon_order = {}, + --- A list of events unrelated to this library that can cause job generation to ignore a specific dungeon. + --- They can also be displayed in the objectives log if you so choose. + --- Format: {condition = function, message_key = string, message_args = function, icon = string} + --- * condition: a function that takes a zone and returns a boolean. If it returns true, + --- jobs will not be generated for this dungeon, and all already taken jobs will be automatically suspended. + --- * message_key: Optional. It will be used in the objectives log instead of the default message if the condition is true. + --- * message_args: Optional. If the message's localization string contains placeholders, this function is in charge of + --- taking a zone and returning a list of values that will be used for those placeholders in the form of a table array (Up to 5). + --- * icon: Optional. The icon that will be displayed beside the corresponding dungeon in the dungeon selection menu + --- if the condition is true for any of its segments. Defaults to "" + ---@type {condition:fun(zone:string):(boolean), message_key:string|nil, + --- message_args:fun(zone:string):(string[])|nil, icon:string|nil}[] + external_events = { + {condition = function(zone) return COMMON.HasSidequestInZone(zone) end, icon = "\\uE111", message_key = "MENU_JOB_OBJECTIVE_DEFAULT"} + }, + --- How to display external event icons in the dungeon list. This value can only be either "FIRST" or ALL". + --- * If set to "ALL", they will always be displayed in the order defined by external_events. + --- Only one copy of the same icon will be shown, no matter how many conditions require it. + --- * If set to "FIRST", only the first event in the list's order will be displayed, no matter how many events are actually there. + --- This setting does not prevent multiple messages from appearing in the objectives list. + ---@type "FIRST"|"ALL" + external_events_icon_mode = "FIRST", + --- The pattern that will define how to display entries in the dungeon list. + --- {0} is the placeholder for the dungeon name, {1} for pending job icon and {2} for external condition icons. + ---@type string + dungeon_list_pattern = "{2}{1}{0}", + --- Use this table to determine various properties regarding job types. + --- Remove a job type entirely to disable its generation altogether. + --- Format: = {rank_modifier = integer, min_rank = string} + --- * : one of RESCUE_SELF, RESCUE_FRIEND, ESCORT, EXPLORATION, DELIVERY, LOST_ITEM, OUTLAW, OUTLAW_ITEM, OUTLAW_ITEM_UNK, OUTLAW_MONSTER_HOUSE, OUTLAW_FLEE + --- * rank_modifier (optional): these jobs will always have this modifier applied to their rank. + --- * min_rank (optional): this type of jobs can never be of a rank lower than this. + --- This influences possible dungeon spawn: a rank_modifier 1 job with min_rank "C" can also spawn in "D" rank dungeons. + ---@type table + job_types = { + RESCUE_SELF = {rank_modifier = 0, min_rank = "F"}, + RESCUE_FRIEND = {rank_modifier = 0, min_rank = "E"}, + ESCORT = {rank_modifier = 1, min_rank = "C"}, + EXPLORATION = {rank_modifier = 1, min_rank = "C"}, + DELIVERY = {rank_modifier = 0, min_rank = "D"}, + LOST_ITEM = {rank_modifier = 0, min_rank = "D"}, + OUTLAW = {rank_modifier = 1, min_rank = "C"}, + OUTLAW_ITEM = {rank_modifier = 1, min_rank = "C"}, + OUTLAW_ITEM_UNK = {rank_modifier = 1, min_rank = "C"}, + OUTLAW_MONSTER_HOUSE = {rank_modifier = 2, min_rank = "S"}, + OUTLAW_FLEE = {rank_modifier = 1, min_rank = "B"} + }, + --- This is a special table where you can specify chance multipliers for job types depending on the dungeon. + --- Format: = { = \} + --- * : the string id of a dungeon + --- * : one of RESCUE_SELF, RESCUE_FRIEND, ESCORT, EXPLORATION, DELIVERY, LOST_ITEM, OUTLAW, OUTLAW_ITEM, OUTLAW_ITEM_UNK, OUTLAW_MONSTER_HOUSE, OUTLAW_FLEE + --- * \: a multiplier number. It doesn't need to be an integer. It will be multiplied with the board's base chances, rounding up, when choosing the type of a job. + --- Set it to 0 or less to disable the job type altogether. + --- @type table> + dungeon_job_modifiers = { + ambush_forest = { + OUTLAW = 3, + OUTLAW_ITEM = 3, + OUTLAW_ITEM_UNK = 3, + OUTLAW_MONSTER_HOUSE = 3, + OUTLAW_FLEE = 3 + }, + treacherous_mountain = { + OUTLAW = 3, + OUTLAW_ITEM = 3, + OUTLAW_ITEM_UNK = 3, + OUTLAW_MONSTER_HOUSE = 3, + OUTLAW_FLEE = 3 + } + }, + --- Chance of special missions to be generated. Special missions are just handcrafted scenarios with specific + --- client and target pairs and custom flavor texts. Set to 0 to disable altogether. + ---@type number + special_chance = 0.2, + --- This is where you can define special cases for specific types of jobs. + --- Format: = {} + --- * : one of RESCUE_SELF, RESCUE_FRIEND, ESCORT, EXPLORATION, DELIVERY, LOST_ITEM, OUTLAW, OUTLAW_ITEM, OUTLAW_ITEM_UNK, OUTLAW_MONSTER_HOUSE, OUTLAW_FLEE + --- * : any id of your choosing except RESCUE_SELF, RESCUE_FRIEND, ESCORT, EXPLORATION, DELIVERY, LOST_ITEM, OUTLAW, OUTLAW_ITEM, OUTLAW_ITEM_UNK, OUTLAW_MONSTER_HOUSE, OUTLAW_FLEE + ---@type table + special_jobs = { + RESCUE_FRIEND = {"CHILD", "LOVER", "RIVAL", "FRIEND"} + }, + --- Chance for supported quest types to have their destination floor hidden. Set to 0 to disable altogether. + --- Players will still be notified when reaching the floor. It just won't show in the quest description. + --- Job types that can have their floor hidden are: RESCUE_FRIEND, EXPLORATION, OUTLAW, OUTLAW_ITEM, OUTLAW_ITEM_UNK, OUTLAW_FLEE. + ---@type number + hidden_floor_chance = 0.15, + --- A list of all types of rewards to be offered to players. + --- Format: {id = string, weight = integer, min_rank = string} + --- * id: one of the supported reward types. You can use duplicate values to alter odds depending on job rank. + --- Supported reward types are: item, money, item_item, money_item, client, exclusive + --- * weight: Chance of appearing. Set to 0 or delete altogether to stop a type of reward from being offered. + --- * min_rank: optional. If set, this type of reward will only be offered if the job is this rank or higher. + ---@type {id:rewardType, weight:integer, min_rank:string|nil}[] + reward_types = { + {id = "item", weight = 6}, + {id = "money", weight = 2}, + {id = "item_item", weight = 3}, -- second item hidden + {id = "money_item", weight = 1}, -- item is hidden + {id = "client", weight = 1, min_rank = "STAR_1"}, -- appears as ??? + {id = "exclusive", weight = 1, min_rank = "STAR_4"} -- appears as ???. Award a 1* xcl item of client, or of target if law enforcement mission. + }, + --- The type of extra reward for all quests. It can be "none", "rank" or "exp". Any other value will result in "none". + ---@type "none"|"rank"|"exp" + extra_reward_type = "exp", + --- Use this table to assign different weights to reward pools depending on the difficulty rank of the mission. + --- = {{id = string, weight = integer}} + --- * = one of the difficulties defined in "difficulty_list" + --- * id = the id of one of the reward pools defined in "reward_pools" + --- * weight = Chance of appearing. Set to 0 or delete altogether to stop a reward pool from being picked. + --- @type table + rewards_per_difficulty = { + F = { + {id = "NECESSITIES", weight = 10}, --Basic stuff (i.e. reviver seeds, escape orbs, leppa berries) * + {id = "AMMO_LOW", weight = 0}, + {id = "AMMO_MID", weight = 0}, + {id = "AMMO_HIGH", weight = 0}, + {id = "APRICORN_GENERIC", weight = 0}, + {id = "APRICORN_TYPED", weight = 0}, + {id = "FOOD_LOW", weight = 0}, + {id = "FOOD_MID", weight = 0}, + {id = "FOOD_HIGH", weight = 0}, + {id = "MEDICINE_LOW", weight = 0}, + {id = "MEDICINE_HIGH", weight = 0}, + {id = "SEED_LOW", weight = 0}, + {id = "SEED_MID", weight = 0}, + {id = "SEED_HIGH", weight = 0}, + {id = "HELD_LOW", weight = 0}, + {id = "HELD_MID", weight = 0}, + {id = "HELD_HIGH", weight = 0}, + {id = "HELD_TYPE", weight = 0}, + {id = "HELD_PLATES", weight = 0}, + {id = "LOOT_LOW", weight = 0}, + {id = "LOOT_HIGH", weight = 0}, + {id = "EVO_ITEMS", weight = 0}, + {id = "ORBS_LOW", weight = 0}, + {id = "ORBS_MID", weight = 0}, + {id = "ORBS_HIGH", weight = 0}, + {id = "WANDS_LOW", weight = 0}, + {id = "WANDS_MID", weight = 0}, + {id = "WANDS_HIGH", weight = 0}, + {id = "TM_LOW", weight = 0}, + {id = "TM_MID", weight = 0}, + {id = "TM_HIGH", weight = 0}, + {id = "SPECIAL", weight = 0} + }, + E = { + {id = "NECESSITIES", weight = 10}, --Basic stuff (i.e. reviver seeds, escape orbs, leppa berries) * + {id = "AMMO_LOW", weight = 0}, + {id = "AMMO_MID", weight = 0}, + {id = "AMMO_HIGH", weight = 0}, + {id = "APRICORN_GENERIC", weight = 10}, --Generic (non-typed) apricorns with a max catch bonus below 35 * + {id = "APRICORN_TYPED", weight = 0}, + {id = "FOOD_LOW", weight = 10}, --Basic food, small chance of gummis * + {id = "FOOD_MID", weight = 0}, + {id = "FOOD_HIGH", weight = 0}, + {id = "MEDICINE_LOW", weight = 0}, + {id = "MEDICINE_HIGH", weight = 0}, + {id = "SEED_LOW", weight = 10}, --Basic seeds, berries, white herbs * + {id = "SEED_MID", weight = 0}, + {id = "SEED_HIGH", weight = 0}, + {id = "HELD_LOW", weight = 3}, --Basic stat boosting held items and ones with a net drawback (Iron Ball, Flame Orb, etc.) + {id = "HELD_MID", weight = 0}, + {id = "HELD_HIGH", weight = 0}, + {id = "HELD_TYPE", weight = 0}, + {id = "HELD_PLATES", weight = 0}, + {id = "LOOT_LOW", weight = 3}, --Keys, pearls, assembly boxes + {id = "LOOT_HIGH", weight = 0}, + {id = "EVO_ITEMS", weight = 1}, --Evolution items, high chance of link cables + {id = "ORBS_LOW", weight = 0}, + {id = "ORBS_MID", weight = 0}, + {id = "ORBS_HIGH", weight = 0}, + {id = "WANDS_LOW", weight = 10}, --Weak wands * + {id = "WANDS_MID", weight = 0}, + {id = "WANDS_HIGH", weight = 0}, + {id = "TM_LOW", weight = 0}, + {id = "TM_MID", weight = 0}, + {id = "TM_HIGH", weight = 0}, + {id = "SPECIAL", weight = 0} + }, + D = { + {id = "NECESSITIES", weight = 5}, --Basic stuff (i.e. reviver seeds, escape orbs, leppa berries) + {id = "AMMO_LOW", weight = 10}, --Mostly iron thorns, with some weaker ammo * + {id = "AMMO_MID", weight = 0}, + {id = "AMMO_HIGH", weight = 0}, + {id = "APRICORN_GENERIC", weight = 10}, --Generic (non-typed) apricorns with a max catch bonus below 35 * + {id = "APRICORN_TYPED", weight = 2}, --Type and glitter apricorns + {id = "FOOD_LOW", weight = 4}, --Basic food, small chance of gummis + {id = "FOOD_MID", weight = 0}, + {id = "FOOD_HIGH", weight = 0}, + {id = "MEDICINE_LOW", weight = 0}, + {id = "MEDICINE_HIGH", weight = 0}, + {id = "SEED_LOW", weight = 4}, --Basic seeds, berries, white herbs + {id = "SEED_MID", weight = 10}, --Advanced seeds and type berries * + {id = "SEED_HIGH", weight = 0}, + {id = "HELD_LOW", weight = 10}, --Basic stat boosting held items and ones with a net drawback (Iron Ball, Flame Orb, etc.) * + {id = "HELD_MID", weight = 0}, + {id = "HELD_HIGH", weight = 0}, + {id = "HELD_TYPE", weight = 3}, --Held items that boost a specific type + {id = "HELD_PLATES", weight = 0}, + {id = "LOOT_LOW", weight = 10}, --Keys, pearls, assembly boxes * + {id = "LOOT_HIGH", weight = 0}, + {id = "EVO_ITEMS", weight = 2}, --Evolution items, high chance of link cables + {id = "ORBS_LOW", weight = 2}, --Weak wonder orbs + {id = "ORBS_MID", weight = 0}, + {id = "ORBS_HIGH", weight = 0}, + {id = "WANDS_LOW", weight = 4}, --Weak wands + {id = "WANDS_MID", weight = 10}, --Medium wands * + {id = "WANDS_HIGH", weight = 0}, + {id = "TM_LOW", weight = 0}, + {id = "TM_MID", weight = 0}, + {id = "TM_HIGH", weight = 0}, + {id = "SPECIAL", weight = 0} + }, + C = { + {id = "NECESSITIES", weight = 4}, --Basic stuff (i.e. reviver seeds, escape orbs, leppa berries) + {id = "AMMO_LOW", weight = 4}, --Mostly iron thorns, with some weaker ammo + {id = "AMMO_MID", weight = 2}, --Stronger generic ammo that you find in most dungeons + {id = "AMMO_HIGH", weight = 0}, + {id = "APRICORN_GENERIC", weight = 4}, --Generic (non-typed) apricorns with a max catch bonus below 35 + {id = "APRICORN_TYPED", weight = 10}, --Type and glitter apricorns * + {id = "FOOD_LOW", weight = 4}, --Basic food, small chance of gummis + {id = "FOOD_MID", weight = 10}, --Big food, medium chance of gummis * + {id = "FOOD_HIGH", weight = 0}, + {id = "MEDICINE_LOW", weight = 0}, + {id = "MEDICINE_HIGH", weight = 0}, + {id = "SEED_LOW", weight = 4}, --Basic seeds, berries, white herbs + {id = "SEED_MID", weight = 4}, --Advanced seeds and type berries + {id = "SEED_HIGH", weight = 0}, + {id = "HELD_LOW", weight = 4}, --Basic stat boosting held items and ones with a net drawback (Iron Ball, Flame Orb, etc.) + {id = "HELD_MID", weight = 0}, + {id = "HELD_HIGH", weight = 0}, + {id = "HELD_TYPE", weight = 3}, --Held items that boost a specific type + {id = "HELD_PLATES", weight = 10}, --Held items that reduce damage from a specific type * + {id = "LOOT_LOW", weight = 4}, --Keys, pearls, assembly boxes + {id = "LOOT_HIGH", weight = 0}, + {id = "EVO_ITEMS", weight = 4}, --Evolution items, high chance of link cables + {id = "ORBS_LOW", weight = 10}, --Weak wonder orbs * + {id = "ORBS_MID", weight = 2}, --Medium wonder orbs, many can shut down a monster house + {id = "ORBS_HIGH", weight = 0}, + {id = "WANDS_LOW", weight = 4}, --Weak wands + {id = "WANDS_MID", weight = 4}, --Medium wands + {id = "WANDS_HIGH", weight = 0}, + {id = "TM_LOW", weight = 10}, --TMs for weak moves * + {id = "TM_MID", weight = 0}, + {id = "TM_HIGH", weight = 0}, + {id = "SPECIAL", weight = 0} + }, + B = { + {id = "NECESSITIES", weight = 3}, --Basic stuff (i.e. reviver seeds, escape orbs, leppa berries) + {id = "AMMO_LOW", weight = 0}, + {id = "AMMO_MID", weight = 10}, --Stronger generic ammo that you find in most dungeons * + {id = "AMMO_HIGH", weight = 2}, --Rare ammo that are hard to find in dungeons + {id = "APRICORN_GENERIC", weight = 0}, + {id = "APRICORN_TYPED", weight = 10}, --Type and glitter apricorns * + {id = "FOOD_LOW", weight = 3}, --Basic food, small chance of gummis + {id = "FOOD_MID", weight = 5}, --Big food, medium chance of gummis + {id = "FOOD_HIGH", weight = 0}, + {id = "MEDICINE_LOW", weight = 2}, --Weaker medicine that can't heal all PP or HP at once + {id = "MEDICINE_HIGH", weight = 0}, + {id = "SEED_LOW", weight = 0}, + {id = "SEED_MID", weight = 4}, --Advanced seeds and type berries + {id = "SEED_HIGH", weight = 3}, --Includes rare seeds and berries, skews to Pure Seeds + {id = "HELD_LOW", weight = 3}, --Basic stat boosting held items and ones with a net drawback (Iron Ball, Flame Orb, etc.) + {id = "HELD_MID", weight = 10}, --Held items very useful for a specific strategy * + {id = "HELD_HIGH", weight = 0}, + {id = "HELD_TYPE", weight = 3}, --Held items that boost a specific type + {id = "HELD_PLATES", weight = 3}, --Held items that reduce damage from a specific type + {id = "LOOT_LOW", weight = 4}, --Keys, pearls, assembly boxes + {id = "LOOT_HIGH", weight = 0}, + {id = "EVO_ITEMS", weight = 5}, --Evolution items, high chance of link cables + {id = "ORBS_LOW", weight = 4}, --Weak wonder orbs + {id = "ORBS_MID", weight = 10}, --Medium wonder orbs, many can shut down a monster house * + {id = "ORBS_HIGH", weight = 0}, + {id = "WANDS_LOW", weight = 0}, + {id = "WANDS_MID", weight = 3}, --Medium wands + {id = "WANDS_HIGH", weight = 10}, --Rare, specialty wands * + {id = "TM_LOW", weight = 4}, --TMs for weak moves + {id = "TM_MID", weight = 2}, --TMs for moderate moves + {id = "TM_HIGH", weight = 0}, + {id = "SPECIAL", weight = 0} + }, + A = { + {id = "NECESSITIES", weight = 2}, --Basic stuff (i.e. reviver seeds, escape orbs, leppa berries) + {id = "AMMO_LOW", weight = 0}, + {id = "AMMO_MID", weight = 3}, --Stronger generic ammo that you find in most dungeons + {id = "AMMO_HIGH", weight = 10}, --Rare ammo that are hard to find in dungeons * + {id = "APRICORN_GENERIC", weight = 0}, + {id = "APRICORN_TYPED", weight = 10}, --Type and glitter apricorns * + {id = "FOOD_LOW", weight = 0}, + {id = "FOOD_MID", weight = 4}, --Big food, medium chance of gummis + {id = "FOOD_HIGH", weight = 2}, --Huge food with a high chance of wonder gummis and a chance for vitamins + {id = "MEDICINE_LOW", weight = 10}, --Weaker medicine that can't heal all PP or HP at once * + {id = "MEDICINE_HIGH", weight = 0}, + {id = "SEED_LOW", weight = 0}, + {id = "SEED_MID", weight = 3}, --Advanced seeds and type berries + {id = "SEED_HIGH", weight = 10}, --Includes rare seeds and berries, skews to Pure Seeds * + {id = "HELD_LOW", weight = 0}, + {id = "HELD_MID", weight = 5}, --Held items very useful for a specific strategy + {id = "HELD_HIGH", weight = 2}, --Held items useful for anyone + {id = "HELD_TYPE", weight = 3}, --Held items that boost a specific type + {id = "HELD_PLATES", weight = 3}, --Held items that reduce damage from a specific type + {id = "LOOT_LOW", weight = 3}, --Keys, pearls, assembly boxes + {id = "LOOT_HIGH", weight = 10}, --Rare loot, skews towards heart scales * + {id = "EVO_ITEMS", weight = 10}, --Evolution items, high chance of link cables * + {id = "ORBS_LOW", weight = 0}, + {id = "ORBS_MID", weight = 4}, --Medium wonder orbs, many can shut down a monster house + {id = "ORBS_HIGH", weight = 4}, --Rare, powerful wonder orbs often with map wide effects + {id = "WANDS_LOW", weight = 0}, + {id = "WANDS_MID", weight = 2}, --Medium wands + {id = "WANDS_HIGH", weight = 5}, --Rare, specialty wands + {id = "TM_LOW", weight = 2}, --TMs for weak moves + {id = "TM_MID", weight = 10}, --TMs for moderate moves * + {id = "TM_HIGH", weight = 0}, + {id = "SPECIAL", weight = 0} + }, + S = { + {id = "NECESSITIES", weight = 2}, --Basic stuff (i.e. reviver seeds, escape orbs, leppa berries) + {id = "AMMO_LOW", weight = 0}, + {id = "AMMO_MID", weight = 2}, --Stronger generic ammo that you find in most dungeons + {id = "AMMO_HIGH", weight = 5}, --Rare ammo that are hard to find in dungeons + {id = "APRICORN_GENERIC", weight = 0}, + {id = "APRICORN_TYPED", weight = 5}, --Type and glitter apricorns + {id = "FOOD_LOW", weight = 0}, + {id = "FOOD_MID", weight = 2}, --Big food, medium chance of gummis + {id = "FOOD_HIGH", weight = 10}, --Huge food with a high chance of wonder gummis and a chance for vitamins * + {id = "MEDICINE_LOW", weight = 4}, --Weaker medicine that can't heal all PP or HP at once + {id = "MEDICINE_HIGH", weight = 10}, --Powerful medicine that can heal everything * + {id = "SEED_LOW", weight = 0}, + {id = "SEED_MID", weight = 0}, + {id = "SEED_HIGH", weight = 5}, --Includes rare seeds and berries, skews to Pure Seeds + {id = "HELD_LOW", weight = 0}, + {id = "HELD_MID", weight = 3}, --Held items very useful for a specific strategy + {id = "HELD_HIGH", weight = 10}, --Held items useful for anyone * + {id = "HELD_TYPE", weight = 3}, --Held items that boost a specific type + {id = "HELD_PLATES", weight = 3}, --Held items that reduce damage from a specific type + {id = "LOOT_LOW", weight = 2}, --Keys, pearls, assembly boxes + {id = "LOOT_HIGH", weight = 10}, --Rare loot, skews towards heart scales * + {id = "EVO_ITEMS", weight = 10}, --Evolution items, high chance of link cables * + {id = "ORBS_LOW", weight = 0}, + {id = "ORBS_MID", weight = 2}, --Medium wonder orbs, many can shut down a monster house + {id = "ORBS_HIGH", weight = 5}, --Rare, powerful wonder orbs often with map wide effects + {id = "WANDS_LOW", weight = 0}, + {id = "WANDS_MID", weight = 2}, --Medium wands + {id = "WANDS_HIGH", weight = 4}, --Rare, specialty wands + {id = "TM_LOW", weight = 0}, + {id = "TM_MID", weight = 4}, --TMs for moderate moves + {id = "TM_HIGH", weight = 10}, --TMs for very powerful moves * + {id = "SPECIAL", weight = 0} + }, + STAR_1 = { + {id = "NECESSITIES", weight = 0}, + {id = "AMMO_LOW", weight = 0}, + {id = "AMMO_MID", weight = 2}, --Stronger generic ammo that you find in most dungeons + {id = "AMMO_HIGH", weight = 5}, --Rare ammo that are hard to find in dungeons + {id = "APRICORN_GENERIC", weight = 0}, + {id = "APRICORN_TYPED", weight = 5}, --Type and glitter apricorns + {id = "FOOD_LOW", weight = 0}, + {id = "FOOD_MID", weight = 0}, + {id = "FOOD_HIGH", weight = 5}, --Huge food with a high chance of wonder gummis and a chance for vitamins + {id = "MEDICINE_LOW", weight = 2}, --Weaker medicine that can't heal all PP or HP at once + {id = "MEDICINE_HIGH", weight = 5}, --Powerful medicine that can heal everything + {id = "SEED_LOW", weight = 0}, + {id = "SEED_MID", weight = 0}, + {id = "SEED_HIGH", weight = 2}, --Includes rare seeds and berries, skews to Pure Seeds + {id = "HELD_LOW", weight = 0}, + {id = "HELD_MID", weight = 2}, --Held items very useful for a specific strategy + {id = "HELD_HIGH", weight = 5}, --Held items useful for anyone + {id = "HELD_TYPE", weight = 0}, + {id = "HELD_PLATES", weight = 0}, + {id = "LOOT_LOW", weight = 2}, --Keys, pearls, assembly boxes + {id = "LOOT_HIGH", weight = 5}, --Rare loot, skews towards heart scales + {id = "EVO_ITEMS", weight = 5}, --Evolution items, high chance of link cables + {id = "ORBS_LOW", weight = 0}, + {id = "ORBS_MID", weight = 2}, --Medium wonder orbs, many can shut down a monster house + {id = "ORBS_HIGH", weight = 5}, --Rare, powerful wonder orbs often with map wide effects + {id = "WANDS_LOW", weight = 0}, + {id = "WANDS_MID", weight = 0}, + {id = "WANDS_HIGH", weight = 3}, --Rare, specialty wands + {id = "TM_LOW", weight = 0}, + {id = "TM_MID", weight = 4}, --TMs for moderate moves + {id = "TM_HIGH", weight = 5}, --TMs for very powerful moves + {id = "SPECIAL", weight = 1} --Very rare, powerful treasures (Amber Tears, Ability Capsules, Golden Apples, etc.) + }, + STAR_2 = { + {id = "NECESSITIES", weight = 0}, + {id = "AMMO_LOW", weight = 0}, + {id = "AMMO_MID", weight = 0}, + {id = "AMMO_HIGH", weight = 5}, --Rare ammo that are hard to find in dungeons + {id = "APRICORN_GENERIC", weight = 0}, + {id = "APRICORN_TYPED", weight = 0}, + {id = "FOOD_LOW", weight = 0}, + {id = "FOOD_MID", weight = 0}, + {id = "FOOD_HIGH", weight = 5}, --Huge food with a high chance of wonder gummis and a chance for vitamins + {id = "MEDICINE_LOW", weight = 0}, + {id = "MEDICINE_HIGH", weight = 5}, --Powerful medicine that can heal everything + {id = "SEED_LOW", weight = 0}, + {id = "SEED_MID", weight = 0}, + {id = "SEED_HIGH", weight = 0}, + {id = "HELD_LOW", weight = 0}, + {id = "HELD_MID", weight = 0}, + {id = "HELD_HIGH", weight = 5}, --Held items useful for anyone + {id = "HELD_TYPE", weight = 0}, + {id = "HELD_PLATES", weight = 0}, + {id = "LOOT_LOW", weight = 0}, + {id = "LOOT_HIGH", weight = 5}, --Rare loot, skews towards heart scales + {id = "EVO_ITEMS", weight = 5}, --Evolution items, high chance of link cables + {id = "ORBS_LOW", weight = 0}, + {id = "ORBS_MID", weight = 0}, + {id = "ORBS_HIGH", weight = 5}, --Rare, powerful wonder orbs often with map wide effects + {id = "WANDS_LOW", weight = 0}, + {id = "WANDS_MID", weight = 0}, + {id = "WANDS_HIGH", weight = 0}, + {id = "TM_LOW", weight = 0}, + {id = "TM_MID", weight = 2}, --TMs for moderate moves + {id = "TM_HIGH", weight = 5}, --TMs for very powerful moves + {id = "SPECIAL", weight = 2} --Very rare, powerful treasures (Amber Tears, Ability Capsules, Golden Apples, etc.) + }, + STAR_3 = { + {id = "NECESSITIES", weight = 0}, + {id = "AMMO_LOW", weight = 0}, + {id = "AMMO_MID", weight = 0}, + {id = "AMMO_HIGH", weight = 5}, --Rare ammo that are hard to find in dungeons + {id = "APRICORN_GENERIC", weight = 0}, + {id = "APRICORN_TYPED", weight = 0}, + {id = "FOOD_LOW", weight = 0}, + {id = "FOOD_MID", weight = 0}, + {id = "FOOD_HIGH", weight = 5}, --Huge food with a high chance of wonder gummis and a chance for vitamins + {id = "MEDICINE_LOW", weight = 0}, + {id = "MEDICINE_HIGH", weight = 5}, --Powerful medicine that can heal everything + {id = "SEED_LOW", weight = 0}, + {id = "SEED_MID", weight = 0}, + {id = "SEED_HIGH", weight = 0}, + {id = "HELD_LOW", weight = 0}, + {id = "HELD_MID", weight = 0}, + {id = "HELD_HIGH", weight = 5}, --Held items useful for anyone + {id = "HELD_TYPE", weight = 0}, + {id = "HELD_PLATES", weight = 0}, + {id = "LOOT_LOW", weight = 0}, + {id = "LOOT_HIGH", weight = 5}, --Rare loot, skews towards heart scales + {id = "EVO_ITEMS", weight = 5}, --Evolution items, high chance of link cables + {id = "ORBS_LOW", weight = 0}, + {id = "ORBS_MID", weight = 0}, + {id = "ORBS_HIGH", weight = 0}, + {id = "WANDS_LOW", weight = 0}, + {id = "WANDS_MID", weight = 0}, + {id = "WANDS_HIGH", weight = 0}, + {id = "TM_LOW", weight = 0}, + {id = "TM_MID", weight = 0}, + {id = "TM_HIGH", weight = 5}, --TMs for very powerful moves + {id = "SPECIAL", weight = 3} --Very rare, powerful treasures (Amber Tears, Ability Capsules, Golden Apples, etc.) + }, + STAR_4 = { + {id = "NECESSITIES", weight = 0}, + {id = "AMMO_LOW", weight = 0}, + {id = "AMMO_MID", weight = 0}, + {id = "AMMO_HIGH", weight = 0}, + {id = "APRICORN_GENERIC", weight = 0}, + {id = "APRICORN_TYPED", weight = 0}, + {id = "FOOD_LOW", weight = 0}, + {id = "FOOD_MID", weight = 0}, + {id = "FOOD_HIGH", weight = 5}, --Huge food with a high chance of wonder gummis and a chance for vitamins + {id = "MEDICINE_LOW", weight = 0}, + {id = "MEDICINE_HIGH", weight = 0}, + {id = "SEED_LOW", weight = 0}, + {id = "SEED_MID", weight = 0}, + {id = "SEED_HIGH", weight = 0}, + {id = "HELD_LOW", weight = 0}, + {id = "HELD_MID", weight = 0}, + {id = "HELD_HIGH", weight = 5}, --Held items useful for anyone + {id = "HELD_TYPE", weight = 0}, + {id = "HELD_PLATES", weight = 0}, + {id = "LOOT_LOW", weight = 0}, + {id = "LOOT_HIGH", weight = 5}, --Rare loot, skews towards heart scales + {id = "EVO_ITEMS", weight = 5}, --Evolution items, high chance of link cables + {id = "ORBS_LOW", weight = 0}, + {id = "ORBS_MID", weight = 0}, + {id = "ORBS_HIGH", weight = 0}, + {id = "WANDS_LOW", weight = 0}, + {id = "WANDS_MID", weight = 0}, + {id = "WANDS_HIGH", weight = 0}, + {id = "TM_LOW", weight = 0}, + {id = "TM_MID", weight = 0}, + {id = "TM_HIGH", weight = 5}, --TMs for very powerful moves + {id = "SPECIAL", weight = 4} --Very rare, powerful treasures (Amber Tears, Ability Capsules, Golden Apples, etc.) + }, + STAR_5 = { + {id = "NECESSITIES", weight = 0}, + {id = "AMMO_LOW", weight = 0}, + {id = "AMMO_MID", weight = 0}, + {id = "AMMO_HIGH", weight = 0}, + {id = "APRICORN_GENERIC", weight = 0}, + {id = "APRICORN_TYPED", weight = 0}, + {id = "FOOD_LOW", weight = 0}, + {id = "FOOD_MID", weight = 0}, + {id = "FOOD_HIGH", weight = 5}, --Huge food with a high chance of wonder gummis and a chance for vitamins + {id = "MEDICINE_LOW", weight = 0}, + {id = "MEDICINE_HIGH", weight = 0}, + {id = "SEED_LOW", weight = 0}, + {id = "SEED_MID", weight = 0}, + {id = "SEED_HIGH", weight = 0}, + {id = "HELD_LOW", weight = 0}, + {id = "HELD_MID", weight = 0}, + {id = "HELD_HIGH", weight = 0}, + {id = "HELD_TYPE", weight = 0}, + {id = "HELD_PLATES", weight = 0}, + {id = "LOOT_LOW", weight = 0}, + {id = "LOOT_HIGH", weight = 0}, + {id = "EVO_ITEMS", weight = 0}, + {id = "ORBS_LOW", weight = 0}, + {id = "ORBS_MID", weight = 0}, + {id = "ORBS_HIGH", weight = 0}, + {id = "WANDS_LOW", weight = 0}, + {id = "WANDS_MID", weight = 0}, + {id = "WANDS_HIGH", weight = 0}, + {id = "TM_LOW", weight = 0}, + {id = "TM_MID", weight = 0}, + {id = "TM_HIGH", weight = 5}, --TMs for very powerful moves + {id = "SPECIAL", weight = 5} --Very rare, powerful treasures (Amber Tears, Ability Capsules, Golden Apples, etc.) + }, + STAR_6 = { + {id = "NECESSITIES", weight = 0}, + {id = "AMMO_LOW", weight = 0}, + {id = "AMMO_MID", weight = 0}, + {id = "AMMO_HIGH", weight = 0}, + {id = "APRICORN_GENERIC", weight = 0}, + {id = "APRICORN_TYPED", weight = 0}, + {id = "FOOD_LOW", weight = 0}, + {id = "FOOD_MID", weight = 0}, + {id = "FOOD_HIGH", weight = 4}, --Huge food with a high chance of wonder gummis and a chance for vitamins + {id = "MEDICINE_LOW", weight = 0}, + {id = "MEDICINE_HIGH", weight = 0}, + {id = "SEED_LOW", weight = 0}, + {id = "SEED_MID", weight = 0}, + {id = "SEED_HIGH", weight = 0}, + {id = "HELD_LOW", weight = 0}, + {id = "HELD_MID", weight = 0}, + {id = "HELD_HIGH", weight = 0}, + {id = "HELD_TYPE", weight = 0}, + {id = "HELD_PLATES", weight = 0}, + {id = "LOOT_LOW", weight = 0}, + {id = "LOOT_HIGH", weight = 0}, + {id = "EVO_ITEMS", weight = 0}, + {id = "ORBS_LOW", weight = 0}, + {id = "ORBS_MID", weight = 0}, + {id = "ORBS_HIGH", weight = 0}, + {id = "WANDS_LOW", weight = 0}, + {id = "WANDS_MID", weight = 0}, + {id = "WANDS_HIGH", weight = 0}, + {id = "TM_LOW", weight = 0}, + {id = "TM_MID", weight = 0}, + {id = "TM_HIGH", weight = 4}, --TMs for very powerful moves + {id = "SPECIAL", weight = 6} --Very rare, powerful treasures (Amber Tears, Ability Capsules, Golden Apples, etc.) + }, + STAR_7 = { + {id = "NECESSITIES", weight = 0}, + {id = "AMMO_LOW", weight = 0}, + {id = "AMMO_MID", weight = 0}, + {id = "AMMO_HIGH", weight = 0}, + {id = "APRICORN_GENERIC", weight = 0}, + {id = "APRICORN_TYPED", weight = 0}, + {id = "FOOD_LOW", weight = 0}, + {id = "FOOD_MID", weight = 0}, + {id = "FOOD_HIGH", weight = 3}, --Huge food with a high chance of wonder gummis and a chance for vitamins + {id = "MEDICINE_LOW", weight = 0}, + {id = "MEDICINE_HIGH", weight = 0}, + {id = "SEED_LOW", weight = 0}, + {id = "SEED_MID", weight = 0}, + {id = "SEED_HIGH", weight = 0}, + {id = "HELD_LOW", weight = 0}, + {id = "HELD_MID", weight = 0}, + {id = "HELD_HIGH", weight = 0}, + {id = "HELD_TYPE", weight = 0}, + {id = "HELD_PLATES", weight = 0}, + {id = "LOOT_LOW", weight = 0}, + {id = "LOOT_HIGH", weight = 0}, + {id = "EVO_ITEMS", weight = 0}, + {id = "ORBS_LOW", weight = 0}, + {id = "ORBS_MID", weight = 0}, + {id = "ORBS_HIGH", weight = 0}, + {id = "WANDS_LOW", weight = 0}, + {id = "WANDS_MID", weight = 0}, + {id = "WANDS_HIGH", weight = 0}, + {id = "TM_LOW", weight = 0}, + {id = "TM_MID", weight = 0}, + {id = "TM_HIGH", weight = 3}, --TMs for very powerful moves + {id = "SPECIAL", weight = 7} --Very rare, powerful treasures (Amber Tears, Ability Capsules, Golden Apples, etc.) + }, + STAR_8 = { + {id = "NECESSITIES", weight = 0}, + {id = "AMMO_LOW", weight = 0}, + {id = "AMMO_MID", weight = 0}, + {id = "AMMO_HIGH", weight = 0}, + {id = "APRICORN_GENERIC", weight = 0}, + {id = "APRICORN_TYPED", weight = 0}, + {id = "FOOD_LOW", weight = 0}, + {id = "FOOD_MID", weight = 0}, + {id = "FOOD_HIGH", weight = 0}, + {id = "MEDICINE_LOW", weight = 0}, + {id = "MEDICINE_HIGH", weight = 0}, + {id = "SEED_LOW", weight = 0}, + {id = "SEED_MID", weight = 0}, + {id = "SEED_HIGH", weight = 0}, + {id = "HELD_LOW", weight = 0}, + {id = "HELD_MID", weight = 0}, + {id = "HELD_HIGH", weight = 0}, + {id = "HELD_TYPE", weight = 0}, + {id = "HELD_PLATES", weight = 0}, + {id = "LOOT_LOW", weight = 0}, + {id = "LOOT_HIGH", weight = 0}, + {id = "EVO_ITEMS", weight = 0}, + {id = "ORBS_LOW", weight = 0}, + {id = "ORBS_MID", weight = 0}, + {id = "ORBS_HIGH", weight = 0}, + {id = "WANDS_LOW", weight = 0}, + {id = "WANDS_MID", weight = 0}, + {id = "WANDS_HIGH", weight = 0}, + {id = "TM_LOW", weight = 0}, + {id = "TM_MID", weight = 0}, + {id = "TM_HIGH", weight = 3}, --TMs for very powerful moves + {id = "SPECIAL", weight = 8} --Very rare, powerful treasures (Amber Tears, Ability Capsules, Golden Apples, etc.) + }, + STAR_9 = { + {id = "NECESSITIES", weight = 0}, + {id = "AMMO_LOW", weight = 0}, + {id = "AMMO_MID", weight = 0}, + {id = "AMMO_HIGH", weight = 0}, + {id = "APRICORN_GENERIC", weight = 0}, + {id = "APRICORN_TYPED", weight = 0}, + {id = "FOOD_LOW", weight = 0}, + {id = "FOOD_MID", weight = 0}, + {id = "FOOD_HIGH", weight = 0}, + {id = "MEDICINE_LOW", weight = 0}, + {id = "MEDICINE_HIGH", weight = 0}, + {id = "SEED_LOW", weight = 0}, + {id = "SEED_MID", weight = 0}, + {id = "SEED_HIGH", weight = 0}, + {id = "HELD_LOW", weight = 0}, + {id = "HELD_MID", weight = 0}, + {id = "HELD_HIGH", weight = 0}, + {id = "HELD_TYPE", weight = 0}, + {id = "HELD_PLATES", weight = 0}, + {id = "LOOT_LOW", weight = 0}, + {id = "LOOT_HIGH", weight = 0}, + {id = "EVO_ITEMS", weight = 0}, + {id = "ORBS_LOW", weight = 0}, + {id = "ORBS_MID", weight = 0}, + {id = "ORBS_HIGH", weight = 0}, + {id = "WANDS_LOW", weight = 0}, + {id = "WANDS_MID", weight = 0}, + {id = "WANDS_HIGH", weight = 0}, + {id = "TM_LOW", weight = 0}, + {id = "TM_MID", weight = 0}, + {id = "TM_HIGH", weight = 0}, + {id = "SPECIAL", weight = 9} --Very rare, powerful treasures (Amber Tears, Ability Capsules, Golden Apples, etc.) + } + }, + --- List of all reward pools and the items they contain. You must at least include all pools referenced in the rewards_per_difficulty table, but you can add more. + --- Entry ids may be either items or other pools. A pool doesn't have to only contain one of the two. + --- Format: = {id = string, count = integer, hidden = string, weight = integer} + --- * = the id of the pool. It should be used somewhere else either in this table or in rewards_per_difficulty. + --- * id = item_id or pool_id. If it matches a pool, count and hidden will be ignored. + --- * count = Optional. The amount of items awarded. It cannot be higher than the item's max stack. Defaults to the item's max stack. + --- * hidden = Optional. Hidden value that can be used for various purposes depending on the item. Defaults to "". + --- * weight = Chance of appearing. Set to 0 or delete altogether to stop an entry from being picked. + --- @type table + reward_pools = { + NECESSITIES = { + {id = "seed_reviver", weight = 10}, + {id = "berry_leppa", weight = 5}, + {id = "berry_oran", weight = 5}, + {id = "berry_lum", weight = 5}, + {id = "food_apple", weight = 5}, + {id = "orb_escape", weight = 5}, + {id = "apricorn_plain", weight = 5}, + {id = "key",count = 3, weight = 2} + }, + AMMO_LOW = { + {id = "ammo_iron_thorn", count = 3, weight = 5}, + {id = "ammo_geo_pebble", count = 3, weight = 1}, + {id = "ammo_stick", count = 3, weight = 1}, + }, + AMMO_MID = { + {id = "ammo_geo_pebble", count = 3, weight = 5}, + {id = "ammo_gravelerock", count = 3, weight = 5}, + {id = "ammo_stick", count = 3, weight = 5}, + {id = "ammo_silver_spike", count = 3, weight = 5} + }, + AMMO_HIGH = { + {id = "ammo_rare_fossil", count = 3, weight = 5}, + {id = "ammo_corsola_twig", count = 3, weight = 5}, + {id = "ammo_cacnea_spike", count = 3, weight = 5} + }, + APRICORN_GENERIC = { + {id = "apricorn_plain", weight = 12}, + {id = "apricorn_big", weight = 4} + }, + APRICORN_TYPED = { + {id = "apricorn_blue", weight = 5}, + {id = "apricorn_green", weight = 5}, + {id = "apricorn_brown", weight = 5}, + {id = "apricorn_purple", weight = 5}, + {id = "apricorn_red", weight = 5}, + {id = "apricorn_white", weight = 5}, + {id = "apricorn_yellow", weight = 5}, + {id = "apricorn_black", weight = 5}, + {id = "apricorn_glittery", weight = 5} + }, + --Rare chance for gummis + FOOD_LOW = { + {id = "food_apple", weight = 30}, + {id = "food_banana", weight = 18}, + {id = "gummi_blue", weight = 1}, + {id = "gummi_black", weight = 1}, + {id = "gummi_clear", weight = 1}, + {id = "gummi_grass", weight = 1}, + {id = "gummi_green", weight = 1}, + {id = "gummi_brown", weight = 1}, + {id = "gummi_orange", weight = 1}, + {id = "gummi_gold", weight = 1}, + {id = "gummi_pink", weight = 1}, + {id = "gummi_purple", weight = 1}, + {id = "gummi_red", weight = 1}, + {id = "gummi_royal", weight = 1}, + {id = "gummi_silver", weight = 1}, + {id = "gummi_white", weight = 1}, + {id = "gummi_yellow", weight = 1}, + {id = "gummi_sky", weight = 1}, + {id = "gummi_gray", weight = 1}, + {id = "gummi_magenta", weight = 1} + }, + --Moderate chance of gummis, rare chance of wonder gummis + FOOD_MID = { + {id = "food_apple_big", weight = 30}, + {id = "food_banana_big", weight = 18}, + {id = "gummi_blue", weight = 2}, + {id = "gummi_black", weight = 2}, + {id = "gummi_clear", weight = 2}, + {id = "gummi_grass", weight = 2}, + {id = "gummi_green", weight = 2}, + {id = "gummi_brown", weight = 2}, + {id = "gummi_orange", weight = 2}, + {id = "gummi_gold", weight = 2}, + {id = "gummi_pink", weight = 2}, + {id = "gummi_purple", weight = 2}, + {id = "gummi_red", weight = 2}, + {id = "gummi_royal", weight = 2}, + {id = "gummi_silver", weight = 2}, + {id = "gummi_white", weight = 2}, + {id = "gummi_yellow", weight = 2}, + {id = "gummi_sky", weight = 2}, + {id = "gummi_gray", weight = 2}, + {id = "gummi_magenta", weight = 2}, + {id = "gummi_wonder", weight = 1} + }, + --Small chance for vitamins + FOOD_HIGH = { + {id = "food_apple_huge", weight = 30}, + {id = "food_apple_perfect", weight = 18}, + {id = "food_banana_big", weight = 18}, + {id = "gummi_wonder", weight = 30}, + {id = "boost_calcium", weight = 3}, + {id = "boost_protein", weight = 3}, + {id = "boost_hp_up", weight = 3}, + {id = "boost_zinc", weight = 3}, + {id = "boost_carbos", weight = 3}, + {id = "boost_iron", weight = 3}, + {id = "boost_nectar", weight = 5} + }, + --Basic manufactured medicine + MEDICINE_LOW = { + {id = "medicine_potion", weight = 20}, + {id = "medicine_elixir", weight = 20}, + {id = "medicine_full_heal", weight = 10}, + {id = "medicine_x_attack", weight = 10}, + {id = "medicine_x_defense", weight = 10}, + {id = "medicine_x_sp_atk", weight = 10}, + {id = "medicine_x_sp_def", weight = 10}, + {id = "medicine_x_speed", weight = 10}, + {id = "medicine_x_accuracy", weight = 10}, + {id = "medicine_dire_hit", weight = 10} + }, + --Advanced manufactued medicine + MEDICINE_HIGH = { + {id = "medicine_max_potion", weight = 20}, + {id = "medicine_max_elixir", weight = 20}, + {id = "medicine_full_heal", weight = 10} + }, + --includes seeds and berries, as well as white herbs + SEED_LOW = { + {id = "seed_blast", weight = 5}, + {id = "seed_sleep", weight = 5}, + {id = "seed_warp", weight = 5}, + {id = "berry_oran", weight = 5}, + {id = "berry_leppa", weight = 5}, + {id = "berry_sitrus", weight = 5}, + {id = "berry_lum", weight = 5}, + {id = "herb_white", weight = 10} + }, + --Includes advanced seeds, herbs, and type berries + SEED_MID = { + {id = "seed_reviver", weight = 25}, + {id = "seed_decoy", weight = 5}, + {id = "seed_blinker", weight = 5}, + {id = "seed_last_chance", weight = 5}, + {id = "seed_doom", weight = 5}, + {id = "seed_ban", weight = 5}, + {id = "seed_ice", weight = 5}, + {id = "seed_vile", weight = 5}, + {id = "berry_tanga", weight = 2}, + {id = "berry_colbur", weight = 2}, + {id = "berry_wacan", weight = 2}, + {id = "berry_haban", weight = 2}, + {id = "berry_chople", weight = 2}, + {id = "berry_occa", weight = 2}, + {id = "berry_coba", weight = 2}, + {id = "berry_kasib", weight = 2}, + {id = "berry_rindo", weight = 2}, + {id = "berry_shuca", weight = 2}, + {id = "berry_yache", weight = 2}, + {id = "berry_chilan", weight = 2}, + {id = "berry_kebia", weight = 2}, + {id = "berry_payapa", weight = 2}, + {id = "berry_charti", weight = 2}, + {id = "berry_babiri", weight = 2}, + {id = "berry_passho", weight = 2}, + {id = "berry_roseli", weight = 2}, + {id = "herb_power", weight = 10}, + {id = "herb_mental", weight = 10} + }, + --includes rare seeds and berries + SEED_HIGH = { + {id = "seed_pure", weight = 15}, + {id = "seed_joy", weight = 1}, + {id = "berry_rowap", weight = 5}, + {id = "berry_jaboca", weight = 5}, + {id = "berry_liechi", weight = 5}, + {id = "berry_ganlon", weight = 5}, + {id = "berry_salac", weight = 5}, + {id = "berry_petaya", weight = 5}, + {id = "berry_apicot", weight = 5}, + {id = "berry_micle", weight = 5}, + {id = "berry_enigma", weight = 5}, + {id = "berry_starf", weight = 5} + }, + HELD_LOW = { + {id = "held_power_band", weight = 5}, + {id = "held_special_band", weight = 5}, + {id = "held_defense_scarf", weight = 5}, + {id = "held_zinc_band", weight = 5}, + {id = "held_toxic_orb", weight = 5}, + {id = "held_flame_orb", weight = 5}, + {id = "held_iron_ball", weight = 5}, + {id = "held_ring_target", weight = 5} + }, + HELD_MID = { + {id = "held_pierce_band", weight = 5}, + {id = "held_warp_scarf", weight = 5}, + {id = "held_scope_lens", weight = 5}, + {id = "held_reunion_cape", weight = 5}, + {id = "held_heal_ribbon", weight = 5}, + {id = "held_twist_band", weight = 5}, + {id = "held_grip_claw", weight = 5}, + {id = "held_binding_band", weight = 5}, + {id = "held_metronome", weight = 5}, + {id = "held_shed_shell", weight = 5}, + {id = "held_wide_lens", weight = 5}, + {id = "held_sticky_barb", weight = 5}, + {id = "held_choice_band", weight = 5}, + {id = "held_choice_scarf", weight = 5}, + {id = "held_choice_specs", weight = 5} + }, + HELD_HIGH = { + {id = "held_golden_mask", weight = 5}, + {id = "held_friend_bow", weight = 2}, + {id = "held_shell_bell", weight = 5}, + {id = "held_mobile_scarf", weight = 5}, + {id = "held_cover_band", weight = 5}, + {id = "held_pass_scarf", weight = 5}, + {id = "held_trap_scarf", weight = 5}, + {id = "held_pierce_band", weight = 5}, + {id = "held_goggle_specs", weight = 5}, + {id = "held_x_ray_specs", weight = 5}, + {id = "held_assault_vest", weight = 5}, + {id = "held_life_orb", weight = 5} + }, + HELD_TYPE = { + {id = "held_silver_powder", weight = 5}, + {id = "held_black_glasses", weight = 5}, + {id = "held_dragon_scale", weight = 5}, + {id = "held_magnet", weight = 5}, + {id = "held_pink_bow", weight = 5}, + {id = "held_black_belt", weight = 5}, + {id = "held_charcoal", weight = 5}, + {id = "held_sharp_beak", weight = 5}, + {id = "held_spell_tag", weight = 5}, + {id = "held_miracle_seed", weight = 5}, + {id = "held_soft_sand", weight = 5}, + {id = "held_never_melt_ice", weight = 5}, + {id = "held_silk_scarf", weight = 5}, + {id = "held_poison_barb", weight = 5}, + {id = "held_twisted_spoon", weight = 5}, + {id = "held_hard_stone", weight = 5}, + {id = "held_metal_coat", weight = 5}, + {id = "held_mystic_water", weight = 5} + }, + HELD_PLATES = { + {id = "held_insect_plate", weight = 5}, + {id = "held_dread_plate", weight = 5}, + {id = "held_draco_plate", weight = 5}, + {id = "held_zap_plate", weight = 5}, + {id = "held_pixie_plate", weight = 5}, + {id = "held_fist_plate", weight = 5}, + {id = "held_flame_plate", weight = 5}, + {id = "held_sky_plate", weight = 5}, + {id = "held_spooky_plate", weight = 5}, + {id = "held_meadow_plate", weight = 5}, + {id = "held_earth_plate", weight = 5}, + {id = "held_icicle_plate", weight = 5}, + {id = "held_blank_plate", weight = 5}, + {id = "held_toxic_plate", weight = 5}, + {id = "held_mind_plate", weight = 5}, + {id = "held_stone_plate", weight = 5}, + {id = "held_iron_plate", weight = 5}, + {id = "held_splash_plate", weight = 5} + }, + --Spawns boxes, keys, heart scales, and loot + LOOT_LOW = { + {id = "loot_heart_scale", count = 3, weight = 5}, + {id = "loot_pearl", count = 3, weight = 10}, + {id = "machine_assembly_box", weight = 10}, + {id = "key", count = 3, weight = 10} + }, + LOOT_HIGH = { + {id = "loot_heart_scale", count = 3, weight = 20}, + {id = "loot_nugget", weight = 5}, + {id = "machine_recall_box", weight = 10}, + {id = "machine_storage_box", count = 3, weight = 10} + }, + EVO_ITEMS = { + {id = "evo_link_cable", weight = 30}, + {id = "evo_fire_stone", weight = 5}, + {id = "evo_thunder_stone", weight = 5}, + {id = "evo_water_stone", weight = 5}, + {id = "evo_leaf_stone", weight = 5}, + {id = "evo_moon_stone", weight = 5}, + {id = "evo_sun_stone", weight = 5}, + {id = "evo_magmarizer", weight = 5}, + {id = "evo_electirizer", weight = 5}, + {id = "evo_reaper_cloth", weight = 5}, + {id = "evo_cracked_pot", weight = 5}, + {id = "evo_chipped_pot", weight = 5}, + {id = "evo_shiny_stone", weight = 5}, + {id = "evo_dusk_stone", weight = 5}, + {id = "evo_dawn_stone", weight = 5}, + {id = "evo_up_grade", weight = 5}, + {id = "evo_dubious_disc", weight = 5}, + {id = "evo_razor_fang", weight = 5}, + {id = "evo_razor_claw", weight = 5}, + {id = "evo_protector", weight = 5}, + {id = "evo_prism_scale", weight = 5}, + {id = "evo_kings_rock", weight = 5}, + {id = "evo_sun_ribbon", weight = 5}, + {id = "evo_lunar_ribbon", weight = 5}, + {id = "evo_ice_stone", weight = 5} + }, + ORBS_LOW = { + {id = "orb_escape", weight = 5}, + {id = "orb_weather", weight = 5}, + {id = "orb_cleanse", weight = 5}, + {id = "orb_endure", weight = 5}, + {id = "orb_trapbust", weight = 5}, + {id = "orb_petrify", weight = 5}, + {id = "orb_foe_hold", weight = 5}, + {id = "orb_nullify", weight = 5}, + {id = "orb_all_dodge", weight = 5}, + {id = "orb_rebound", weight = 5}, + {id = "orb_mirror", weight = 5}, + {id = "orb_foe_seal", weight = 5}, + {id = "orb_rollcall", weight = 5}, + {id = "orb_mug", weight = 5}, + }, + ORBS_MID = { + {id = "orb_escape", weight = 5}, + {id = "orb_mobile", weight = 5}, + {id = "orb_invisify", weight = 5}, + {id = "orb_all_aim", weight = 5}, + {id = "orb_trawl", weight = 5}, + {id = "orb_one_shot", weight = 5}, + {id = "orb_pierce", weight = 5}, + {id = "orb_all_protect", weight = 5}, + {id = "orb_trap_see", weight = 5}, + {id = "orb_slumber", weight = 5}, + {id = "orb_totter", weight = 5}, + {id = "orb_freeze", weight = 5}, + {id = "orb_spurn", weight = 5}, + {id = "orb_itemizer", weight = 5}, + {id = "orb_halving", weight = 5}, + }, + ORBS_HIGH = { + {id = "orb_escape", weight = 5}, + {id = "orb_luminous", weight = 5}, + {id = "orb_invert", weight = 5}, + {id = "orb_devolve", weight = 5}, + {id = "orb_revival", weight = 5}, + {id = "orb_scanner", weight = 5}, + {id = "orb_stayaway", weight = 5}, + {id = "orb_one_room", weight = 5}, + }, + WANDS_LOW = { + {id = "wand_pounce", count = 3, weight = 5}, + {id = "wand_slow", count = 3, weight = 5}, + {id = "wand_topsy_turvy", count = 3, weight = 5}, + {id = "wand_purge", count = 3, weight = 5} + }, + WANDS_MID = { + {id = "wand_path", count = 3, weight = 5}, + {id = "wand_whirlwind", count = 3, weight = 5}, + {id = "wand_switcher", count = 3, weight = 5}, + {id = "wand_fear", count = 3, weight = 5}, + {id = "wand_warp", count = 3, weight = 5}, + {id = "wand_lob", count = 3, weight = 5} + }, + WANDS_HIGH = { + {id = "wand_lure", count = 3, weight = 5}, + {id = "wand_stayaway", count = 3, weight = 5}, + {id = "wand_transfer", count = 3, weight = 5}, + {id = "wand_vanish", count = 3, weight = 5} + }, + TM_LOW = { + {id = "tm_snatch", weight = 5}, + {id = "tm_sunny_day", weight = 5}, + {id = "tm_rain_dance", weight = 5}, + {id = "tm_sandstorm", weight = 5}, + {id = "tm_hail", weight = 5}, + {id = "tm_taunt", weight = 5}, + {id = "tm_safeguard", weight = 5}, + {id = "tm_light_screen", weight = 5}, + {id = "tm_dream_eater", weight = 5}, + {id = "tm_nature_power", weight = 5}, + {id = "tm_swagger", weight = 5}, + {id = "tm_captivate", weight = 5}, + {id = "tm_fling", weight = 5}, + {id = "tm_payback", weight = 5}, + {id = "tm_reflect", weight = 5}, + {id = "tm_rock_polish", weight = 5}, + {id = "tm_pluck", weight = 5}, + {id = "tm_psych_up", weight = 5}, + {id = "tm_secret_power", weight = 5}, + {id = "tm_return", weight = 5}, + {id = "tm_frustration", weight = 5}, + {id = "tm_torment", weight = 5}, + {id = "tm_endure", weight = 5}, + {id = "tm_echoed_voice", weight = 5}, + {id = "tm_gyro_ball", weight = 5}, + {id = "tm_recycle", weight = 5}, + {id = "tm_false_swipe", weight = 5}, + {id = "tm_defog", weight = 5}, + {id = "tm_telekinesis", weight = 5}, + {id = "tm_double_team", weight = 5}, + {id = "tm_thunder_wave", weight = 5}, + {id = "tm_attract", weight = 5}, + {id = "tm_smack_down", weight = 5}, + {id = "tm_snarl", weight = 5}, + {id = "tm_flame_charge", weight = 5}, + {id = "tm_protect", weight = 5}, + {id = "tm_round", weight = 5}, + {id = "tm_rest", weight = 5}, + {id = "tm_thief", weight = 5}, + {id = "tm_cut", weight = 5}, + {id = "tm_whirlpool", weight = 5}, + {id = "tm_infestation", weight = 5}, + {id = "tm_roar", weight = 5}, + {id = "tm_flash", weight = 5}, + {id = "tm_embargo", weight = 5}, + {id = "tm_struggle_bug", weight = 5}, + {id = "tm_quash", weight = 5} + }, + TM_MID = { + {id = "tm_explosion", weight = 5}, + {id = "tm_will_o_wisp", weight = 5}, + {id = "tm_facade", weight = 5}, + {id = "tm_water_pulse", weight = 5}, + {id = "tm_shock_wave", weight = 5}, + {id = "tm_brick_break", weight = 5}, + {id = "tm_calm_mind", weight = 5}, + {id = "tm_charge_beam", weight = 5}, + {id = "tm_retaliate", weight = 5}, + {id = "tm_roost", weight = 5}, + {id = "tm_acrobatics", weight = 5}, + {id = "tm_bulk_up", weight = 5}, + {id = "tm_shadow_claw", weight = 5}, + {id = "tm_steel_wing", weight = 5}, + {id = "tm_snarl", weight = 5}, + {id = "tm_bulldoze", weight = 5}, + {id = "tm_substitute", weight = 5}, + {id = "tm_brine", weight = 5}, + {id = "tm_venoshock", weight = 5}, + {id = "tm_u_turn", weight = 5}, + {id = "tm_aerial_ace", weight = 5}, + {id = "tm_hone_claws", weight = 5}, + {id = "tm_rock_smash", weight = 5}, + {id = "tm_hidden_power", weight = 5}, + {id = "tm_rock_tomb", weight = 5}, + {id = "tm_strength", weight = 5}, + {id = "tm_grass_knot", weight = 5}, + {id = "tm_power_up_punch", weight = 5}, + {id = "tm_work_up", weight = 5}, + {id = "tm_incinerate", weight = 5}, + {id = "tm_bullet_seed", weight = 5}, + {id = "tm_low_sweep", weight = 5}, + {id = "tm_volt_switch", weight = 5}, + {id = "tm_avalanche", weight = 5}, + {id = "tm_dragon_tail", weight = 5}, + {id = "tm_silver_wind", weight = 5}, + {id = "tm_frost_breath", weight = 5}, + {id = "tm_sky_drop", weight = 5} + }, + TM_HIGH = { + {id = "tm_earthquake", weight = 5}, + {id = "tm_hyper_beam", weight = 5}, + {id = "tm_overheat", weight = 5}, + {id = "tm_blizzard", weight = 5}, + {id = "tm_swords_dance", weight = 5}, + {id = "tm_surf", weight = 5}, + {id = "tm_dark_pulse", weight = 5}, + {id = "tm_psychic", weight = 5}, + {id = "tm_thunder", weight = 5}, + {id = "tm_shadow_ball", weight = 5}, + {id = "tm_ice_beam", weight = 5}, + {id = "tm_giga_impact", weight = 5}, + {id = "tm_fire_blast", weight = 5}, + {id = "tm_dazzling_gleam", weight = 5}, + {id = "tm_flash_cannon", weight = 5}, + {id = "tm_stone_edge", weight = 5}, + {id = "tm_sludge_bomb", weight = 5}, + {id = "tm_focus_blast", weight = 5}, + {id = "tm_x_scissor", weight = 5}, + {id = "tm_wild_charge", weight = 5}, + {id = "tm_focus_punch", weight = 5}, + {id = "tm_psyshock", weight = 5}, + {id = "tm_rock_slide", weight = 5}, + {id = "tm_thunderbolt", weight = 5}, + {id = "tm_flamethrower", weight = 5}, + {id = "tm_energy_ball", weight = 5}, + {id = "tm_scald", weight = 5}, + {id = "tm_waterfall", weight = 5}, + {id = "tm_rock_climb", weight = 5}, + {id = "tm_giga_drain", weight = 5}, + {id = "tm_dive", weight = 5}, + {id = "tm_poison_jab", weight = 5}, + {id = "tm_iron_tail", weight = 5}, + {id = "tm_dig", weight = 5}, + {id = "tm_fly", weight = 5}, + {id = "tm_dragon_claw", weight = 5}, + {id = "tm_dragon_pulse", weight = 5}, + {id = "tm_sludge_wave", weight = 5}, + {id = "tm_drain_punch", weight = 5} + }, + --special and unique rewards, very rare + SPECIAL = { + {id = "medicine_amber_tear", count = 3, weight = 1}, + {id = "machine_ability_capsule", count = 3, weight = 1}, + {id = "ammo_golden_thorn", count = 3, weight = 1}, + {id = "food_apple_golden", weight = 1}, + {id = "seed_golden", weight = 1}, + {id = "evo_harmony_scarf", weight = 1}, + {id = "apricorn_perfect", weight = 1} + } + }, + --- Ids of items that can be used as targets, divided depending on the job type. + --- Make sure they're either easy enough to obtain or impossible to lose, depending on the job. + --- Using stackable items is highly discouraged for all job types, as it may result in odd behaviors. + --- Format: = {} + --- * = the id of a job type defined inside "job_types" + --- * = the id of the item to use + ---@type table + target_items = { + LOST_ITEM = { + "mission_lost_scarf", + "mission_lost_specs", + "mission_lost_band" + }, + OUTLAW_ITEM = { + "mission_stolen_scarf", + "mission_stolen_band", + "mission_stolen_specs" + }, + OUTLAW_ITEM_UNK = {}, --Filled at the end of the settings table + DELIVERY = { + "berry_oran", + "berry_leppa", + "food_apple", + "berry_lum", + "apricorn_plain" + } + }, + --- Weighted table that associates mission difficulty to a specific tier of characters and outlaws. + --- Format: = {{id = string, weight = integer}} + --- * = one of the difficulties defined in "difficulty_list" + --- * id = A string that defines a pokémon tier id. + --- * weight: Chance of being picked. Set to 0 or delete altogether to stop an entry from being picked. + --- @type table + difficulty_to_tier = { + F = { + {id = "TIER_LOW", weight = 10}, + {id = "TIER_MID", weight = 0}, + {id = "TIER_HIGH", weight = 0} + }, + E = { + {id = "TIER_LOW", weight = 9}, + {id = "TIER_MID", weight = 1}, + {id = "TIER_HIGH", weight = 0} + }, + D = { + {id = "TIER_LOW", weight = 7}, + {id = "TIER_MID", weight = 3}, + {id = "TIER_HIGH", weight = 0} + }, + C = { + {id = "TIER_LOW", weight = 6}, + {id = "TIER_MID", weight = 4}, + {id = "TIER_HIGH", weight = 0} + }, + B = { + {id = "TIER_LOW", weight = 2}, + {id = "TIER_MID", weight = 6}, + {id = "TIER_HIGH", weight = 2} + }, + A = { + {id = "TIER_LOW", weight = 1}, + {id = "TIER_MID", weight = 5}, + {id = "TIER_HIGH", weight = 4} + }, + S = { + {id = "TIER_LOW", weight = 0}, + {id = "TIER_MID", weight = 5}, + {id = "TIER_HIGH", weight = 5} + }, + STAR_1 = { + {id = "TIER_LOW", weight = 0}, + {id = "TIER_MID", weight = 3}, + {id = "TIER_HIGH", weight = 7} + }, + STAR_2 = { + {id = "TIER_LOW", weight = 0}, + {id = "TIER_MID", weight = 1}, + {id = "TIER_HIGH", weight = 9} + }, + STAR_3 = { + {id = "TIER_LOW", weight = 0}, + {id = "TIER_MID", weight = 0}, + {id = "TIER_HIGH", weight = 10} + }, + STAR_4 = { + {id = "TIER_LOW", weight = 0}, + {id = "TIER_MID", weight = 0}, + {id = "TIER_HIGH", weight = 10} + }, + STAR_5 = { + {id = "TIER_LOW", weight = 0}, + {id = "TIER_MID", weight = 0}, + {id = "TIER_HIGH", weight = 10} + }, + STAR_6 = { + {id = "TIER_LOW", weight = 0}, + {id = "TIER_MID", weight = 0}, + {id = "TIER_HIGH", weight = 10} + }, + STAR_7 = { + {id = "TIER_LOW", weight = 0}, + {id = "TIER_MID", weight = 0}, + {id = "TIER_HIGH", weight = 10} + }, + STAR_8 = { + {id = "TIER_LOW", weight = 0}, + {id = "TIER_MID", weight = 0}, + {id = "TIER_HIGH", weight = 10} + }, + STAR_9 = { + {id = "TIER_LOW", weight = 0}, + {id = "TIER_MID", weight = 0}, + {id = "TIER_HIGH", weight = 10} + } + }, + --- A list of Pokémon that will be used for job generation, sorted by arbitrary quest tiers. + --- Any of these entries may be picked when choosing client and target. + --- If the client picked is not in the dex, the reward type can never be "client" nor "exclusive" + --- Format: \ = {} + --- * \ = one of the tier ids used in difficulty_to_tier + --- * = either the species id of a pokémon or a monsterId table + --- @type table + pokemon = { + --weak mons for easy missions + TIER_LOW = + {"abra","amaura","anorith","applin","archen","aron","arrokuda","axew","azurill", + "baltoy","bagon","barboach","bayleef","beldum","bellsprout","bidoof","bonsly","bounsweet","bronzor","budew","buizel","bulbasaur","buneary","burmy", + "cacnea","carvanha","cascoon","caterpie","charcadet","charmander","cherubi","chespin","chikorita","chimchar","chinchou","chingling","clamperl","clauncher","cleffa","clobbopus","combee","corphish","cottonee","cranidos","croagunk","cubchoo","cutiefly","cyndaquil", + "darumaka","deerling","deino","diglett","doduo","dratini","drifloon","dreepy","drowzee","duskull", + "ekans","electrike","elekid","elgyem","espurr","exeggcute", + "feebas","fennekin","ferroseed","fidough","finneon","flabebe","fletchling","fomantis","foongus","froakie","fuecoco", + "gastly","geodude","gible","glameow","goldeen","goomy","gothita","grimer","growlithe","gulpin", + "happiny","hatenna","helioptile","hippopotas","honedge","hoothoot","hoppip","horsea","houndour", + "igglybuff", + "jangmo_o","joltik", + "kabuto","kakuna","koffing","krabby","kricketot", + "larvesta","larvitar","ledyba","lileep","lillipup","litleo","litten","litwick","lotad","luvdisc", + "machop","magby","magikarp","magnemite","makuhita","mankey","mantyke","mareanie","mareep","meditite","metapod","mienfoo","mime_jr","minccino","morelull","mudbray","munna","mudkip","murkrow", + "natu","nickit","nincada","noibat","nosepass","numel","nymble", + "oddish","omanyte","onix","oshawott", + "pansear","paras","patrat","pawmi","pawniard","petilil","phantump","pidgey","pidove","pineco","piplup","poliwag","ponyta","poochyena","popplio","porygon","psyduck","pumpkaboo","purrloin", + "quaxly", + "ralts","rattata","remoraid","rhyhorn","roggenrola","rowlet", + "salandit","sandile","sandshrew","sandygast","scraggy","seedot","seel","sentret","sewaddle","shellder","shellos","shieldon","shinx","shroomish","shuppet","silcoon","sinistea","skorupi","slakoth","slowpoke","slugma","smoochum","sneasel","snivy","snom","snorunt","snover","snubbull","sobble","solosis","spearow","spheal","spinarak","spoink","sprigatito","spritzee","squirtle","starly","staryu","stufful","stunky","sunkern","surskit","swablu","swinub","swirlix", + "taillow","teddiursa","tentacool","tepig","tinkatink","torchic","togepi","totodile","trapinch","treecko","trubbish","turtwig","tympole","tyrogue", + + "vanillite","venonat","voltorb", + "wailmer","watchog","weedle","whismur","woobat","wooloo","wooper","wurmple","wynaut", + + + "zigzagoon","zubat"}, + + --middling mons for medium missions + TIER_MID = + {"aipom","arbok","ariados","audino", + "beautifly","beedrill","bibarel","bisharp","braixen","breloom","brionne","butterfree", + "carbink","carnivine","castform","chansey","charmeleon","chatot","cherrim","chimecho","clefairy","combusken","comfey","corsola","cramorant","croconaw", + "dartrix","delibird","dewott","dhelmise","ditto","dodrio","doublade","dragalge","dragonair","drakloak","dunsparce","duosion","dustox", + "eiscue","electabuzz","emboar","emolga", + "farfetchd","finizen","flaaffy","flapple","floette","fraxure","furret","furfrou", + "gabite","girafarig","gligar","gloom","golbat","gothorita","granbull","graveler","grotle","grovyle", + "hattrem","hakamo_o","haunter","herdier", + "illumise","indeedee","ivysaur", + "jigglypuff","jynx", + "kadabra","kangaskhan","kirlia","klefki","kricketune", + "lairon","lampent","ledian","liepard","linoone","lombre","loudred","lunatone","luxio", + "machoke","magmar","magneton","maractus","marill","marshtomp","masquerain","mawile","metang","mightyena","miltank","mimikyu","minior","minun","misdreavus","monferno","morgrem","morpeko","mothim","mr_mime","munchlax", + "nidorina","nidorino","ninjask","noctowl","nuzleaf", + + "pachirisu","palpitoad","parasect","pidgeotto","pignite","piloswine","plusle","poliwhirl","porygon2","prinplup","pupitar", + "quilava","qwilfish", + "raboot","raticate","relicanth","ribombee","roselia", + "sableye","scyther","seadra","sealeo","servine","seviper","shedinja","shelgon","shuckle","skiploom","sliggoo","smeargle","solrock","spinda","stantler","staravia","steenee","sudowoodo","sunflora","swadloon", + "tangela","thievul","tinkatuff","togedemaru","togetic","torkoal","tropius", + + "vanillish","venomoth","vespiquen","vibrava","vigoroth","volbeat", + "wartortle","weepinbell","wobbuffet","wormadam", + + "yanma", + "zangoose","zorua","zweilous"}, + + --strong pokemon for difficult missions + TIER_HIGH = + {"absol","aerodactyl","aegislash","aggron","alakazam","alcremie","altaria","ambipom","ampharos","appletun","arcanine","archeops","armaldo","aurorus","azumarill", + "banette","bastiodon","bellibolt","bellossom","blissey","blastoise","blaziken","braviary","bronzong", + "cacturne","camerupt","chandelure","charizard","cinderace","clawitzer","claydol","clefable","cloyster","corviknight","cradily","crawdaunt","crobat","cursola", + "decidueye","delphox","dewgong","donphan","dragapult","dragonite","drampa","drapion","drifblim","dugtrio","dusclops","dusknoir", + "eldegoss","electivire","empoleon","escavalier","exeggutor","exploud", + "fearow","feraligatr","ferrothorn","floatzel","florges","flygon","forretress","froslass","frosmoth", + "gallade","galvantula","garchomp","gardevoir","gastrodon","gengar","glalie","glimmora","gliscor","golduck","golem","golisopod","goodra","gorebyss","gothitelle","gourgeist","greninja","grumpig","gyarados", + "hariyama","hatterene","haxorus","heliolisk","heracross","hippowdon","hitmonchan","hitmonlee","hitmontop","honchkrow","houndoom","huntail","hydreigon","hypno", + "incineroar","infernape", + "jumpluff", + "kabutops","kingdra","kingambit","kingler","kommo_o", + "lanturn","lapras","leavanny","lilligant","lopunny","ludicolo","lumineon","lurantis","luxray","lycanroc", + "machamp","magcargo","magmortar","magnezone","mamoswine","mandibuzz","manectric","mantine","maushold","medicham","meganium","meowstic","metagross","mienshao","milotic","mismagius","muk","musharna", + "nidoking","nidoqueen","noivern", + "octillery","omastar","overqwil", + "pawmot","pidgeot","pinsir","politoed","poliwrath","porygon_z","primarina","primeape","probopass","purugly", + "quagsire", + "rampardos","rapidash","reuniclus","rhydon","rhyperior","roserade", + "salamence","salazzle","samurott","sandslash","sawsbuck","sceptile","scizor","scolipede","scorbunny","scrafty","seaking","serperior","sharpedo","shiftry","sirfetchd","skarmory","skuntank","slaking","slowbro","slowking","snorlax","spiritomb","staraptor","starmie","steelix","stoutland","swalot","swampert","swellow","swoobat", + "tangrowth","tauros","tentacruel","tinkaton","togekiss","torterra","toxicroak","toxtricity","tsareena","typhlosion","tyranitar", + "ursaring", + "vanilluxe","venusaur","victreebel","vileplume","volcanion", + "wailord","walrein","weezing","whimsicott","whiscash","wigglytuff", + "xatu", + "yamask","yanmega", + "zoroark"} + }, + --- A list of all possible job display titles, divided by job type. + --- You must also include lists for any special job types you intend to use. + --- Format: = {} + --- * = either the id of a job type defined inside "job_types", or the id of a special job type + --- * = the localization key used for the title. The localized strings are fetched from the Menu Text list (strings.resx) + --- + --- Localization placeholders: + --- * {0}: target + --- * {1}: dungeon + --- * {2}: item + --- * {3}: client (almost never used, hence why at the end) + --- @type table + job_titles = { + RESCUE_SELF = { + "MISSION_TITLE_RESCUE_SELF_001", + "MISSION_TITLE_RESCUE_SELF_002", + "MISSION_TITLE_RESCUE_SELF_003", + "MISSION_TITLE_RESCUE_SELF_004", + "MISSION_TITLE_RESCUE_SELF_005", + "MISSION_TITLE_RESCUE_SELF_006", + "MISSION_TITLE_RESCUE_SELF_007", + "MISSION_TITLE_RESCUE_SELF_008", + "MISSION_TITLE_RESCUE_SELF_009", + "MISSION_TITLE_RESCUE_SELF_010" + }, + RESCUE_FRIEND = { + "MISSION_TITLE_RESCUE_FRIEND_001", + "MISSION_TITLE_RESCUE_FRIEND_002", + "MISSION_TITLE_RESCUE_FRIEND_003", + "MISSION_TITLE_RESCUE_FRIEND_004", + "MISSION_TITLE_RESCUE_FRIEND_005", + "MISSION_TITLE_RESCUE_FRIEND_006", + "MISSION_TITLE_RESCUE_FRIEND_007", + "MISSION_TITLE_RESCUE_FRIEND_008", + "MISSION_TITLE_RESCUE_FRIEND_009", + "MISSION_TITLE_RESCUE_FRIEND_010" + }, + ESCORT = { + "MISSION_TITLE_ESCORT_001", + "MISSION_TITLE_ESCORT_002", + "MISSION_TITLE_ESCORT_003", + "MISSION_TITLE_ESCORT_004", + "MISSION_TITLE_ESCORT_005" + }, + EXPLORATION = { + "MISSION_TITLE_EXPLORATION_001", + "MISSION_TITLE_EXPLORATION_002", + "MISSION_TITLE_EXPLORATION_003", + "MISSION_TITLE_EXPLORATION_004", + "MISSION_TITLE_EXPLORATION_005" + }, + DELIVERY = { + "MISSION_TITLE_DELIVERY_001", + "MISSION_TITLE_DELIVERY_002", + "MISSION_TITLE_DELIVERY_003", + "MISSION_TITLE_DELIVERY_004", + "MISSION_TITLE_DELIVERY_005" + }, + LOST_ITEM = { + "MISSION_TITLE_LOST_ITEM_001", + "MISSION_TITLE_LOST_ITEM_002", + "MISSION_TITLE_LOST_ITEM_003", + "MISSION_TITLE_LOST_ITEM_004", + "MISSION_TITLE_LOST_ITEM_005" + }, + OUTLAW = { + "MISSION_TITLE_OUTLAW_001", + "MISSION_TITLE_OUTLAW_002", + "MISSION_TITLE_OUTLAW_003", + "MISSION_TITLE_OUTLAW_004", + "MISSION_TITLE_OUTLAW_005", + "MISSION_TITLE_OUTLAW_006", + "MISSION_TITLE_OUTLAW_007", + "MISSION_TITLE_OUTLAW_008", + "MISSION_TITLE_OUTLAW_009", + "MISSION_TITLE_OUTLAW_010", + }, + OUTLAW_ITEM = { + "MISSION_TITLE_OUTLAW_ITEM_001", + "MISSION_TITLE_OUTLAW_ITEM_002", + "MISSION_TITLE_OUTLAW_ITEM_003", + "MISSION_TITLE_OUTLAW_ITEM_004", + "MISSION_TITLE_OUTLAW_ITEM_005" + }, + OUTLAW_ITEM_UNK = { + "MISSION_TITLE_OUTLAW_ITEM_UNK_001", + "MISSION_TITLE_OUTLAW_ITEM_UNK_002", + "MISSION_TITLE_OUTLAW_ITEM_UNK_003", + "MISSION_TITLE_OUTLAW_ITEM_UNK_004", + "MISSION_TITLE_OUTLAW_ITEM_UNK_005" + }, + OUTLAW_MONSTER_HOUSE = { + "MISSION_TITLE_OUTLAW_MONSTER_HOUSE_001", + "MISSION_TITLE_OUTLAW_MONSTER_HOUSE_002", + "MISSION_TITLE_OUTLAW_MONSTER_HOUSE_003", + "MISSION_TITLE_OUTLAW_MONSTER_HOUSE_004", + "MISSION_TITLE_OUTLAW_MONSTER_HOUSE_005" + }, + OUTLAW_FLEE = { + "MISSION_TITLE_OUTLAW_FLEE_001", + "MISSION_TITLE_OUTLAW_FLEE_002", + "MISSION_TITLE_OUTLAW_FLEE_003", + "MISSION_TITLE_OUTLAW_FLEE_004", + "MISSION_TITLE_OUTLAW_FLEE_005" + }, + + --For special client/targets + RIVAL = { + "MISSION_TITLE_SPECIAL_RIVAL_001", + "MISSION_TITLE_SPECIAL_RIVAL_002", + "MISSION_TITLE_SPECIAL_RIVAL_003" + }, + + CHILD = { + "MISSION_TITLE_SPECIAL_CHILD_001", + "MISSION_TITLE_SPECIAL_CHILD_002", + "MISSION_TITLE_SPECIAL_CHILD_003" + }, + FRIEND = { + "MISSION_TITLE_SPECIAL_FRIEND_001", + "MISSION_TITLE_SPECIAL_FRIEND_002", + "MISSION_TITLE_SPECIAL_FRIEND_003" + }, + LOVER = { + "MISSION_TITLE_SPECIAL_LOVER_001", + "MISSION_TITLE_SPECIAL_LOVER_002", + "MISSION_TITLE_SPECIAL_LOVER_003" + } + }, + --- A list of all possible job flavor text entries, divided by job type and by line. + --- Format: = {{},{}} + --- * = either the id of a job type defined inside "job_types", or the id of a special job type + --- * = the localization key used for the flavor text. Every job has 2 lists assigned: one for the top string and one for the bottom one. + --- If only the first list is specified, the second one will be left empty. + --- The localized strings are fetched from the Menu Text list (strings.resx) + --- + --- Localization placeholders: + --- * {0}: target + --- * {1}: dungeon + --- * {2}: item + --- * {3}: client (almost never used, hence why at the end) + --- @type table + job_flavor = { + RESCUE_SELF = { + { + "MISSION_BODY_TOP_RESCUE_SELF_001", + "MISSION_BODY_TOP_RESCUE_SELF_002", + "MISSION_BODY_TOP_RESCUE_SELF_003", + "MISSION_BODY_TOP_RESCUE_SELF_004", + "MISSION_BODY_TOP_RESCUE_SELF_005", + "MISSION_BODY_TOP_RESCUE_SELF_006", + "MISSION_BODY_TOP_RESCUE_SELF_007", + "MISSION_BODY_TOP_RESCUE_SELF_008", + "MISSION_BODY_TOP_RESCUE_SELF_009", + "MISSION_BODY_TOP_RESCUE_SELF_010" + }, + { + "MISSION_BODY_BOTTOM_RESCUE_SELF_001", + "MISSION_BODY_BOTTOM_RESCUE_SELF_002", + "MISSION_BODY_BOTTOM_RESCUE_SELF_003", + "MISSION_BODY_BOTTOM_RESCUE_SELF_004", + "MISSION_BODY_BOTTOM_RESCUE_SELF_005", + "MISSION_BODY_BOTTOM_RESCUE_SELF_006", + "MISSION_BODY_BOTTOM_RESCUE_SELF_007", + "MISSION_BODY_BOTTOM_RESCUE_SELF_008", + "MISSION_BODY_BOTTOM_RESCUE_SELF_009", + "MISSION_BODY_BOTTOM_RESCUE_SELF_010" + } + }, + RESCUE_FRIEND = { + { + "MISSION_BODY_TOP_RESCUE_FRIEND_001", + "MISSION_BODY_TOP_RESCUE_FRIEND_002", + "MISSION_BODY_TOP_RESCUE_FRIEND_003", + "MISSION_BODY_TOP_RESCUE_FRIEND_004", + "MISSION_BODY_TOP_RESCUE_FRIEND_005", + "MISSION_BODY_TOP_RESCUE_FRIEND_006", + "MISSION_BODY_TOP_RESCUE_FRIEND_007", + "MISSION_BODY_TOP_RESCUE_FRIEND_008", + "MISSION_BODY_TOP_RESCUE_FRIEND_009", + "MISSION_BODY_TOP_RESCUE_FRIEND_010" + }, + { + "MISSION_BODY_BOTTOM_RESCUE_FRIEND_001", + "MISSION_BODY_BOTTOM_RESCUE_FRIEND_002", + "MISSION_BODY_BOTTOM_RESCUE_FRIEND_003", + "MISSION_BODY_BOTTOM_RESCUE_FRIEND_004", + "MISSION_BODY_BOTTOM_RESCUE_FRIEND_005", + "MISSION_BODY_BOTTOM_RESCUE_FRIEND_006", + "MISSION_BODY_BOTTOM_RESCUE_FRIEND_007", + "MISSION_BODY_BOTTOM_RESCUE_FRIEND_008", + "MISSION_BODY_BOTTOM_RESCUE_FRIEND_009", + "MISSION_BODY_BOTTOM_RESCUE_FRIEND_010" + } + }, + ESCORT = { + { + "MISSION_BODY_TOP_ESCORT_001", + "MISSION_BODY_TOP_ESCORT_002", + "MISSION_BODY_TOP_ESCORT_003", + "MISSION_BODY_TOP_ESCORT_004", + "MISSION_BODY_TOP_ESCORT_005" + }, + { + "MISSION_BODY_BOTTOM_ESCORT_001", + "MISSION_BODY_BOTTOM_ESCORT_002", + "MISSION_BODY_BOTTOM_ESCORT_003", + "MISSION_BODY_BOTTOM_ESCORT_004", + "MISSION_BODY_BOTTOM_ESCORT_005" + } + }, + EXPLORATION = { + { + "MISSION_BODY_TOP_EXPLORATION_001", + "MISSION_BODY_TOP_EXPLORATION_002", + "MISSION_BODY_TOP_EXPLORATION_003", + "MISSION_BODY_TOP_EXPLORATION_004", + "MISSION_BODY_TOP_EXPLORATION_005" + }, + { + "MISSION_BODY_BOTTOM_EXPLORATION_001", + "MISSION_BODY_BOTTOM_EXPLORATION_002", + "MISSION_BODY_BOTTOM_EXPLORATION_003", + "MISSION_BODY_BOTTOM_EXPLORATION_004", + "MISSION_BODY_BOTTOM_EXPLORATION_005" + } + }, + DELIVERY = { + { + "MISSION_BODY_TOP_DELIVERY_001", + "MISSION_BODY_TOP_DELIVERY_002", + "MISSION_BODY_TOP_DELIVERY_003", + "MISSION_BODY_TOP_DELIVERY_004", + "MISSION_BODY_TOP_DELIVERY_005" + }, + { + "MISSION_BODY_BOTTOM_DELIVERY_001", + "MISSION_BODY_BOTTOM_DELIVERY_002", + "MISSION_BODY_BOTTOM_DELIVERY_003", + "MISSION_BODY_BOTTOM_DELIVERY_004", + "MISSION_BODY_BOTTOM_DELIVERY_005" + } + }, + LOST_ITEM = { + { + "MISSION_BODY_TOP_LOST_ITEM_001", + "MISSION_BODY_TOP_LOST_ITEM_002", + "MISSION_BODY_TOP_LOST_ITEM_003", + "MISSION_BODY_TOP_LOST_ITEM_004", + "MISSION_BODY_TOP_LOST_ITEM_005" + }, + { + "MISSION_BODY_BOTTOM_LOST_ITEM_001", + "MISSION_BODY_BOTTOM_LOST_ITEM_002", + "MISSION_BODY_BOTTOM_LOST_ITEM_003", + "MISSION_BODY_BOTTOM_LOST_ITEM_004", + "MISSION_BODY_BOTTOM_LOST_ITEM_005" + } + }, + OUTLAW = { + { + "MISSION_BODY_TOP_OUTLAW_001", + "MISSION_BODY_TOP_OUTLAW_002", + "MISSION_BODY_TOP_OUTLAW_003", + "MISSION_BODY_TOP_OUTLAW_004", + "MISSION_BODY_TOP_OUTLAW_005", + "MISSION_BODY_TOP_OUTLAW_006", + "MISSION_BODY_TOP_OUTLAW_007", + "MISSION_BODY_TOP_OUTLAW_008", + "MISSION_BODY_TOP_OUTLAW_009", + "MISSION_BODY_TOP_OUTLAW_010" + }, + { + "MISSION_BODY_BOTTOM_OUTLAW_001", + "MISSION_BODY_BOTTOM_OUTLAW_002", + "MISSION_BODY_BOTTOM_OUTLAW_003", + "MISSION_BODY_BOTTOM_OUTLAW_004", + "MISSION_BODY_BOTTOM_OUTLAW_005" + } + }, + OUTLAW_ITEM = { + { + "MISSION_BODY_TOP_OUTLAW_ITEM_001", + "MISSION_BODY_TOP_OUTLAW_ITEM_002", + "MISSION_BODY_TOP_OUTLAW_ITEM_003", + "MISSION_BODY_TOP_OUTLAW_ITEM_004", + "MISSION_BODY_TOP_OUTLAW_ITEM_005" + }, + { + "MISSION_BODY_BOTTOM_OUTLAW_ITEM_001", + "MISSION_BODY_BOTTOM_OUTLAW_ITEM_002", + "MISSION_BODY_BOTTOM_OUTLAW_ITEM_003", + "MISSION_BODY_BOTTOM_OUTLAW_ITEM_004", + "MISSION_BODY_BOTTOM_OUTLAW_ITEM_005" + } + }, + OUTLAW_ITEM_UNK = { + { + "MISSION_BODY_TOP_OUTLAW_ITEM_UNK_001", + "MISSION_BODY_TOP_OUTLAW_ITEM_UNK_002", + "MISSION_BODY_TOP_OUTLAW_ITEM_UNK_003", + "MISSION_BODY_TOP_OUTLAW_ITEM_UNK_004", + "MISSION_BODY_TOP_OUTLAW_ITEM_UNK_005" + }, + { + "MISSION_BODY_BOTTOM_OUTLAW_ITEM_UNK_001", + "MISSION_BODY_BOTTOM_OUTLAW_ITEM_UNK_002", + "MISSION_BODY_BOTTOM_OUTLAW_ITEM_UNK_003", + "MISSION_BODY_BOTTOM_OUTLAW_ITEM_UNK_004", + "MISSION_BODY_BOTTOM_OUTLAW_ITEM_UNK_005" + } + }, + OUTLAW_MONSTER_HOUSE = { + { + "MISSION_BODY_TOP_OUTLAW_MONSTER_HOUSE_001", + "MISSION_BODY_TOP_OUTLAW_MONSTER_HOUSE_002", + "MISSION_BODY_TOP_OUTLAW_MONSTER_HOUSE_003", + "MISSION_BODY_TOP_OUTLAW_MONSTER_HOUSE_004", + "MISSION_BODY_TOP_OUTLAW_MONSTER_HOUSE_005" + }, + { + "MISSION_BODY_BOTTOM_OUTLAW_MONSTER_HOUSE_001", + "MISSION_BODY_BOTTOM_OUTLAW_MONSTER_HOUSE_002", + "MISSION_BODY_BOTTOM_OUTLAW_MONSTER_HOUSE_003", + "MISSION_BODY_BOTTOM_OUTLAW_MONSTER_HOUSE_004", + "MISSION_BODY_BOTTOM_OUTLAW_MONSTER_HOUSE_005" + } + }, + OUTLAW_FLEE = { + { + "MISSION_BODY_TOP_OUTLAW_FLEE_001", + "MISSION_BODY_TOP_OUTLAW_FLEE_002", + "MISSION_BODY_TOP_OUTLAW_FLEE_003", + "MISSION_BODY_TOP_OUTLAW_FLEE_004", + "MISSION_BODY_TOP_OUTLAW_FLEE_005" + }, + { + "MISSION_BODY_BOTTOM_OUTLAW_FLEE_001", + "MISSION_BODY_BOTTOM_OUTLAW_FLEE_002", + "MISSION_BODY_BOTTOM_OUTLAW_FLEE_003", + "MISSION_BODY_BOTTOM_OUTLAW_FLEE_004", + "MISSION_BODY_BOTTOM_OUTLAW_FLEE_005" + } + } + }, + --- Law enforcement characters in your setting. All OUTLAW jobs except OUTLAW_ITEM + --- and OUTLAW_ITEM_UNK use these characters as job clients. + --- You may specify only 1 officer, and then a list containing any number of agents. 2 will be randomly + --- picked every time the job completion cutscene is played. + --- You may add a weight property to agents to specify a custom chance of being picked. Any without will + --- have a weight of 1. + --- Add "unique = true" to the MonsterIDTable to stop an agent from being picked twice. You must + --- have at least either 1 non-unique agent or 2 unique ones for this list table to be valid. + --- Officer format: OFFICER = MonsterIDTable + --- Agent format: AGENT = {MonsterIDTable} + --- Format of MonsterIDTable: see the "monsterIdTemplate" function in missiongen_lib.lua + ---@type {OFFICER:monsterIDTable, AGENT:AgentIDTable[]} + law_enforcement = { + OFFICER = {Species = "magnezone", Gender = 0}, + AGENT = { + {Species = "magnemite", Gender = 0, Weight = 31}, + {Species = "magnemite", Gender = 0, Skin = "shiny", Weight = 1, Unique = true} + } + }, + --- Defines the chance of either the officer or an agent being the client of a mission depending on + --- the character tier of the mission, as defined in difficulty_to_tier. + --- Format: \ = {id = string, weight = integer} + --- * \ = one of the tier ids used in difficulty_to_tier + --- * id = One of: OFFICER, AGENT + --- * index: Optional. Ignored if id is OFFICER. You can set it to specify a specific agent instead of rolling using their chance in "law_enforcement". + --- * weight: Chance of being picked. Set to 0 or delete altogether to stop an entry from being picked. + --- @type table + enforcer_chance = { + TIER_LOW = { + {id = "OFFICER", weight = 2}, + {id = "AGENT", weight = 8} + }, + TIER_MID = { + {id = "OFFICER", weight = 5}, + {id = "AGENT", weight = 5} + }, + TIER_HIGH = { + {id = "OFFICER", weight = 8}, + {id = "AGENT", weight = 2} + } + + }, + --- Data of characters and flavor key used for special jobs, divided by tier. + --- In outlaw related quests, you may use ENFORCER as a keyword for a random law enforcement character. + --- You may also use OFFICER or AGENT to specify a more specific selection. + --- These keywords can only be used in place of MonsterIDTables. + --- Format: = {\ = {client = MonsterIDTable, target = MonsterIDTable, item = string, flavor = string}} + --- * = an id of your choosing. It must be different from any basic job id + --- * \ = one of the tier ids used in difficulty_to_tier + --- * client = the data used to generate this Pokémon. + --- * target = the data used to generate this Pokémon. Only used in job types that require a target. + --- * item = the id of the item that will be used for this job. Only used in job types that require an item. + --- * flavor: flavor string key used for the job, sourced from the Menu Text list (strings.resx) + --- Format of MonsterIDTable: see the "monsterIdTemplate" function in missiongen_lib.lua + --- + --- Localization placeholders: + --- * {0}: target + --- * {1}: dungeon + --- * {2}: item + --- * {3}: client (almost never used, hence why at the end) + ---@type table> + special_data = { + LOVER = { + TIER_LOW = { + {client = {Species = "volbeat", Gender = 1}, target = {Species = "illumise", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_LOVER_001"}, + {client = {Species = "minun", Gender = 1}, target = {Species = "plusle", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_LOVER_002"}, + {client = {Species = "mareep", Gender = 2}, target = {Species = "wooloo", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_LOVER_003"}, + {client = {Species = "luvdisc", Gender = 2}, target = {Species = "luvdisc", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_LOVER_004"} + }, + TIER_MID = { + {client = {Species = "miltank", Gender = 2}, target = {Species = "tauros", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_LOVER_005"}, + {client = {Species = "venomoth", Gender = 1}, target = {Species = "butterfree", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_LOVER_006"}, + {client = {Species = "liepard", Gender = 2}, target = {Species = "persian", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_LOVER_007"}, + {client = {Species = "dustox", Gender = 1}, target = {Species = "beautifly", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_LOVER_008"}, + {client = {Species = "glalie", Gender = 1}, target = {Species = "froslass", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_LOVER_009"}, + {client = {Species = "ribombee", Gender = 2}, target = {Species = "masquerain", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_LOVER_010"}, + {client = {Species = "maractus", Gender = 2}, target = {Species = "cacturne", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_LOVER_011"},---my prickly love! + {client = {Species = "lanturn", Gender = 1}, target = {Species = "lumineon", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_LOVER_012"} + }, + TIER_HIGH = { + {client = {Species = "tyranitar", Gender = 1}, target = {Species = "altaria", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_LOVER_013"},--reference to an old idea palika had + {client = {Species = "gyarados", Gender = 1}, target = {Species = "milotic", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_LOVER_014"}, + {client = {Species = "gardevoir", Gender = 2}, target = {Species = "gallade", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_LOVER_015"} + } + }, + CHILD = { + TIER_LOW = { + {client = {Species = "clefable", Gender = 2}, target = {Species = "cleffa", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_CHILD_001"}, + {client = {Species = "wigglytuff", Gender = 1}, target = {Species = "igglybuff", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_CHILD_002"}, + {client = {Species = "togekiss", Gender = 2}, target = {Species = "togepi", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_CHILD_003"}, + {client = {Species = "roserade", Gender = 2}, target = {Species = "budew", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_CHILD_004"}, + {client = {Species = "chimecho", Gender = 2}, target = {Species = "chingling", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_CHILD_005"}, + {client = {Species = "sudowoodo", Gender = 1}, target = {Species = "bonsly", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_CHILD_006"}, + {client = {Species = "mr_mime", Gender = 1}, target = {Species = "mime_jr", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_CHILD_007"}, + {client = {Species = "raticate", Gender = 1}, target = {Species = "rattata", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_CHILD_008"},--hes still not so good at gnawing! + {client = {Species = "leavanny", Gender = 2}, target = {Species = "sewaddle", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_CHILD_009"} + }, + TIER_MID = { + {client = {Species = "appletun", Gender = 2}, target = {Species = "applin", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_CHILD_010"}, + {client = {Species = "aggron", Gender = 1}, target = {Species = "aron", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_CHILD_011"},--probably munched too much metal! + {client = {Species = "jynx", Gender = 2}, target = {Species = "smoochum", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_CHILD_012"}, + {client = {Species = "magmortar", Gender = 2}, target = {Species = "magby", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_CHILD_013"}, + {client = {Species = "electivire", Gender = 1}, target = {Species = "elekid", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_CHILD_014"}, + {client = {Species = "tsareena", Gender = 2}, target = {Species = "bounsweet", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_CHILD_015"}, + {client = {Species = "hatterene", Gender = 2}, target = {Species = "hatenna", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_CHILD_016"}, + {client = {Species = "gothitelle", Gender = 2}, target = {Species = "gothita", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_CHILD_017"}, + {client = {Species = "dugtrio", Gender = 1}, target = {Species = "diglett", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_CHILD_018"} + }, + TIER_HIGH = { + {client = {Species = "tyranitar", Gender = 2}, target = {Species = "larvitar", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_CHILD_019"}, + {client = {Species = "salamence", Gender = 1}, target = {Species = "bagon", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_CHILD_020"}, + {client = {Species = "dragonite", Gender = 2}, target = {Species = "dratini", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_CHILD_021"}, + {client = {Species = "noivern", Gender = 1}, target = {Species = "noibat", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_CHILD_022"}, + {client = {Species = "goodra", Gender = 2}, target = {Species = "goomy", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_CHILD_023"} + } + }, + FRIEND = { + TIER_LOW = { + {client = {Species = "applin", Gender = 1}, target = {Species = "cherubi", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_FRIEND_001"},--We both get mistaken for fruit! What if someone ate him!? + {client = {Species = "mantyke", Gender = 2}, target = {Species = "remoraid", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_FRIEND_002"},--My best friend is missing! I'll never be able to evolve without him! + {client = {Species = "magikarp", Gender = 1}, target = {Species = "feebas", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_FRIEND_003"},--feebas is the only one who understands what it's like to be dogshit! + {client = {Species = "poliwag", Gender = 2}, target = {Species = "lotad", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_FRIEND_004"},--frog and his lilypad. I have no lilypad now, save him! + {client = {Species = "teddiursa", Gender = 1}, target = {Species = "combee", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_FRIEND_005"}, --Without Combee, I have no honey! Please find them! + {client = {Species = "woobat", Gender = 2}, target = {Species = "zubat", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_FRIEND_006"},--we both use ultrasonic waves to see! + {client = {Species = "trubbish", Gender = 1}, target = {Species = "grimer", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_FRIEND_007"},--we both love eating garbage! + {client = {Species = "shroomish", Gender = 1}, target = {Species = "paras", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_FRIEND_008"},--we both love to spread spores! + {client = {Species = "chansey", Gender = 2}, target = {Species = "togepi", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_FRIEND_009"},--cares for togepi because its an egg + {client = {Species = "salandit", Gender = 1}, target = {Species = "combee", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_FRIEND_010"}--they relate in being useless + }, + TIER_MID = { + {client = {Species = "lunatone", Gender = 0}, target = {Species = "solrock", Gender = 0}, flavor = "MISSION_BODY_SPECIAL_FRIEND_011"}, + {client = {Species = "emolga", Gender = 2}, target = {Species = "pachirisu", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_FRIEND_012"}, + {client = {Species = "spinda", Gender = 2}, target = {Species = "hypno", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_FRIEND_013"}, --Hypno went missing; only he can help stop my dizziness + {client = {Species = "cramorant", Gender = 1}, target = {Species = "pelipper", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_FRIEND_014"}, + {client = {Species = "magnemite", Gender = 0}, target = {Species = "nosepass", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_FRIEND_015"},--We're both sensitive to magnetism! + {client = {Species = "dustox", Gender = 1}, target = {Species = "lampent", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_FRIEND_016"} + }, + TIER_HIGH = { + {client = {Species = "lilligant", Gender = 2}, target = {Species = "kricketune", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_FRIEND_017"},--I can't dance without Kricketune's music! + {client = {Species = "wigglytuff", Gender = 2}, target = {Species = "exploud", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_FRIEND_018"}, --we love making loud, silly noises together! + {client = {Species = "beedrill", Gender = 1}, target = {Species = "florges", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_FRIEND_019"},--without my flower, i have no meaning! + {client = {Species = "dunsparce", Gender = 1}, target = {Species = "dugtrio", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_FRIEND_020"},--we both love to burrow! + {client = {Species = "whimsicott", Gender = 2}, target = {Species = "jumpluff", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_FRIEND_021"} + } + }, + RIVAL = { + TIER_LOW = { + {client = {Species = "koffing", Gender = 1}, target = {Species = "stunky", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_RIVAL_001"},--they compete to see whose odor is stronger + {client = {Species = "krabby", Gender = 1}, target = {Species = "corphish", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_RIVAL_002"},--compare claw strength + {client = {Species = "shuppet", Gender = 2}, target = {Species = "duskull", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_RIVAL_003"},--we like to see who can pull better pranks! + {client = {Species = "pidgey", Gender = 2}, target = {Species = "spearow", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_RIVAL_004"},--we compete at flying! + {client = {Species = "kabuto", Gender = 1}, target = {Species = "omanyte", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_RIVAL_005"}, --we've been rivals since our ancestors' time! + {client = {Species = "joltik", Gender = 2}, target = {Species = "spinarak", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_RIVAL_006"},--We like to see who can spin the better web! + {client = {Species = "tyrogue", Gender = 1}, target = {Species = "makuhita", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_RIVAL_007"},--my punching bag training partner! + {client = {Species = "lillipup", Gender = 2}, target = {Species = "poochyena", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_RIVAL_008"} + }, + + TIER_MID = { + {client = {Species = "vigoroth", Gender = 1}, target = {Species = "primeape", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_RIVAL_009"},--full of energy! + {client = {Species = "sawsbuck", Gender = 1}, target = {Species = "stantler", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_RIVAL_010"},--butt antlers! + {client = {Species = "jangmo_o", Gender = 1}, target = {Species = "axew", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_RIVAL_011"}, + {client = {Species = "mareanie", Gender = 2}, target = {Species = "corsola", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_RIVAL_012"} + }, + + TIER_HIGH = { + {client = {Species = "heracross", Gender = 1}, target = {Species = "pinsir", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_RIVAL_013"}, + {client = {Species = "slowking", Gender = 1}, target = {Species = "slowbro", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_RIVAL_014"},--slowbro may not be as smart as me, but we're still great friends! + {client = {Species = "magmortar", Gender = 2}, target = {Species = "electivire", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_RIVAL_015"}, --we need to settle who is stronger! + {client = {Species = "cradily", Gender = 2}, target = {Species = "armaldo", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_RIVAL_016"}, --we've been rivals since our ancestors' time! + {client = {Species = "bastiodon", Gender = 2}, target = {Species = "rampardos", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_RIVAL_017"}, --we've been rivals since our ancestors' time! + {client = {Species = "archeops", Gender = 1}, target = {Species = "aerodactyl", Gender = 2}, flavor = "MISSION_BODY_SPECIAL_RIVAL_018"}, --we've been rivals since our ancestors' time! + {client = {Species = "swellow", Gender = 2}, target = {Species = "staraptor", Gender = 1}, flavor = "MISSION_BODY_SPECIAL_RIVAL_019"}--brave birds! + } + } + }, + --- String keys to be used whenever the player interacts with an escort mission client, depending on the job type. + --- You can also include tables for special escort jobs. If no such table exists for a specific case, the generic + --- table for the job type will be used. + --- Format: = {} + --- * = "ESCORT", "EXPLORATION", or the id of a special job type that corresponds to one of these job types + --- * = the localization key of possible messages. The localized strings are fetched from the Gameplay Text list (stringsEx.resx) + --- + --- Localization placeholders: + --- * {0}: target + --- * {1}: dungeon + --- * {2}: item + ---@type table + escort_talks = { + ESCORT = { + "MISSION_ESCORT_INTERACT" + }, + EXPLORATION = { + "MISSION_EXPLORATION_INTERACT" + } + }, + --- Strings keys to be used whenever the player interacts with a pokémon that needs rescue, depending on the job type and the response given. + --- You must include a "_DEFAULT" table, but you can also include tables for special job types. _DEFAULT will still be used for any special + --- case that doesn't have a table associated with it. + --- Format: {rescue_yes = { = {{key = string, emotion = string}}}, rescue_no = { = {{key = string, emotion = string}}}} + --- * = "_DEFAULT", or the id of a special job type that corresponds to either "RESCUE_SELF" or "RESCUE_FRIEND" + --- * key = the localization key of possible messages. The localized strings are fetched from the Gameplay Text list (stringsEx.resx) + --- * emotion = Optional. An emotion id to use with this specific message. Defaults to "Normal" + --- + --- Localization placeholders: + --- * {0}: client + --- * {1}: target + --- * {2}: dungeon + ---@type {rescue_yes:table<"_DEFAULT"|string,{key:string,emotion:emotionType|nil}[]>,rescue_no:table<"_DEFAULT"|string,{key:string,emotion:emotionType|nil}[]>} + rescue_responses = { + rescue_yes = { + _DEFAULT = { {key = "MISSION_RESCUE_CONFIRM_DEFAULT"} }, + CHILD = { {key = "MISSION_RESCUE_CONFIRM_CHILD", emotion = "Joyous"} }, + FRIEND = { {key = "MISSION_RESCUE_CONFIRM_FRIEND"} }, + RIVAL = { {key = "MISSION_RESCUE_CONFIRM_RIVAL"} }, + LOVER = { {key = "MISSION_RESCUE_CONFIRM_LOVER", emotion = "Joyous"} } + }, + rescue_no = { + _DEFAULT = { {key = "MISSION_RESCUE_DENY_DEFAULT", emotion = "Surprised"} }, + CHILD = { {key = "MISSION_RESCUE_DENY_CHILD", emotion = "Crying"} }, + FRIEND = { {key = "MISSION_RESCUE_DENY_FRIEND", emotion = "Surprised"} }, + RIVAL = { {key = "MISSION_RESCUE_DENY_RIVAL", emotion = "Stunned"} }, + LOVER = { {key = "MISSION_RESCUE_DENY_LOVER", emotion = "Stunned"} } + } + }, + --- The object that contains job callback functions. It is recommended to define this object somewhere else. + --- Job callbacks are stored inside jobs as strings. These strings are then used as an index in the callback root object to retrieve the + --- actual function. They will be passed two parameters, the first one being a table structured like so: + --- {cancel: boolean, job: jobTable} + --- * cancel: set this to true to stop the event for whatever reason. For example, canceling BeforeReward will stop a job from performing the normal reward cutscene. + --- * job: the job that requested this callback. + --- + --- The second parameter is the list of arguments defined during the callback registration. + --- + --- List of events: + --- * JobTake: Ran right before the job is taken from a board or otherwise obtained using ```library:TakeJob```. The job provided is the copy that would be added to the taken list. + --- * JobActivate: Ran right before the job is activated from the taken list menu or by calling ```library:ToggleTakenJob``` on an inactive job. Does NOT run when taken from a board while "taken_jobs_start_active" is true. + --- * JobDeactivate: Ran right before the job is deactivated from the taken list menu or by calling ```library:ToggleTakenJob``` on an active job. + --- * DungeonStart: Ran when entering the dungeon this job is located in. Canceling this event does nothing. + --- * FloorStart: Ran at the start of the target floor of the job. + --- * JobComplete: Ran when the job is marked as completed during an exploration or otherwised marked as such using ```library:MarkJobCompleted```. + --- * JobFail: Ran when the job is marked as failed during an exploration or otherwised marked as such using ```library:MarkJobFailed```. + --- * BeforeReward: Ran before this specific job's reward cutscene is started. Canceling this skips the entire reward routine and go straight to deleting the job from the taken list. + --- * AfterReward: Ran after this specific job's reward cutscene is started. Canceling this event will prevent the job from being removed from the taken list. + mission_callback_root = COMMON +} +settings.target_items.OUTLAW_ITEM_UNK = settings.target_items.OUTLAW_ITEM --copy list because it should be the same anyway + +--- Adds a new dungeon section to the list of possible job destinations. +--- Section start values must always be added in ascending order. Failing to do so may cause inconsistent behaviors. +--- @param zone string the string id of the dungeon zone. +--- @param segment integer the numeric id of the dungeon segment. +--- @param start integer the starting floor of this dungeon section (start counting from 1 for this). +--- @param difficulty string the string id of the difficulty assigned to this section. +--- @param finish? integer Only considered when first adding a segment to the list.Can be omitted otherwise. This will be the last floor of the segment where jobs can spawn (start counting from 1 for this). If higher than the dungeon floors, it will default to the full dungeon length. +--- @param must_end? boolean Only considered when first adding a segment to the list. Can be omitted otherwise. If true, this segment must be completed before jobs can spawn in it. If false, the segment must be accessed at least once, unless it's segment 0, which just needs to be unlocked. Defaults to true. +function settings.AddDungeonSection(zone, segment, start, difficulty, finish, must_end) + if must_end == nil then must_end = true end + if settings.dungeons[zone] == nil then + if not settings.dungeon_order._count then + settings.dungeon_order._count = 0 + for _, val in pairs(settings.dungeon_order) do settings.dungeon_order._count = math.max(settings.dungeon_order._count, val) end + end + settings.dungeon_order._count = settings.dungeon_order._count +1 + settings.dungeon_order[zone] = settings.dungeon_order._count + end + settings.dungeons[zone] = settings.dungeons[zone] or {} + settings.dungeons[zone][segment] = settings.dungeons[zone][segment] or {max_floor = finish, must_end = must_end, sections = {}} + table.insert(settings.dungeons[zone][segment].sections, {start = start, difficulty = difficulty}) +end + +settings.AddDungeonSection("tropical_path", 0, 3, "F", 4) +settings.AddDungeonSection("faultline_ridge", 0, 6, "D", 10) +settings.AddDungeonSection("guildmaster_trail", 0, 15, "STAR_2", 30, false) +settings.AddDungeonSection("guildmaster_trail", 0, 19, "STAR_3") +settings.AddDungeonSection("guildmaster_trail", 0, 23, "STAR_4") +settings.AddDungeonSection("guildmaster_trail", 0, 27, "STAR_5") +settings.AddDungeonSection("lava_floe_island", 0, 9, "C", 16) +settings.AddDungeonSection("lava_floe_island", 1, 1, "STAR_1", 9) +settings.AddDungeonSection("castaway_cave", 0, 7, "B", 12) +settings.AddDungeonSection("faded_trail", 0, 4, "E", 7) +settings.AddDungeonSection("faded_trail", 1, 1, "D", 3) +settings.AddDungeonSection("bramble_woods", 0, 4, "E", 7) +settings.AddDungeonSection("bramble_woods", 1, 1, "D", 3) +settings.AddDungeonSection("trickster_woods", 0, 6, "C", 10) +settings.AddDungeonSection("trickster_woods", 1, 1, "B", 4) +settings.AddDungeonSection("overgrown_wilds", 0, 7, "C", 12) +settings.AddDungeonSection("overgrown_wilds", 1, 1, "B", 4) +settings.AddDungeonSection("moonlit_courtyard", 0, 8, "C", 14) +settings.AddDungeonSection("moonlit_courtyard", 0, 12, "B") +settings.AddDungeonSection("moonlit_courtyard", 1, 1, "A", 6) +settings.AddDungeonSection("ambush_forest", 0, 11, "B", 20) +settings.AddDungeonSection("ambush_forest", 0, 16, "A") +settings.AddDungeonSection("sickly_hollow", 0, 9, "S", 16, false) +settings.AddDungeonSection("sickly_hollow", 0, 13, "STAR_1") +settings.AddDungeonSection("secret_garden", 0, 15, "STAR_3", 40, false) +settings.AddDungeonSection("secret_garden", 0, 20, "STAR_4") +settings.AddDungeonSection("secret_garden", 0, 25, "STAR_5") +settings.AddDungeonSection("secret_garden", 0, 30, "STAR_6") +settings.AddDungeonSection("secret_garden", 0, 34, "STAR_7") +settings.AddDungeonSection("secret_garden", 0, 38, "STAR_8") +settings.AddDungeonSection("flyaway_cliffs", 0, 6, "C", 10) +settings.AddDungeonSection("fertile_valley", 0, 5, "D", 8) +settings.AddDungeonSection("fertile_valley", 1, 1, "C", 5) +settings.AddDungeonSection("copper_quarry", 0, 7, "C", 11) +settings.AddDungeonSection("copper_quarry", 0, 1, "B", 4) +settings.AddDungeonSection("depleted_basin", 0, 5, "C", 9) +settings.AddDungeonSection("forsaken_desert", 0, 2, "S", 4) +settings.AddDungeonSection("relic_tower", 0, 8, "S", 13) +settings.AddDungeonSection("relic_tower", 0, 11, "STAR_1") +settings.AddDungeonSection("sleeping_caldera", 0, 10, "B", 18) +settings.AddDungeonSection("sleeping_caldera", 0, 15, "A") +settings.AddDungeonSection("thunderstruck_pass", 0, 8, "B", 14) +settings.AddDungeonSection("veiled_ridge", 0, 9, "B", 16) +settings.AddDungeonSection("veiled_ridge", 1, 1, "A", 6) +settings.AddDungeonSection("snowbound_path", 0, 10, "B", 18) +settings.AddDungeonSection("snowbound_path", 1, 1, "A", 6) +settings.AddDungeonSection("treacherous_mountain", 0, 11, "A", 20) +settings.AddDungeonSection("treacherous_mountain", 0, 16, "S") +settings.AddDungeonSection("champions_road", 0, 13, "S", 23) +settings.AddDungeonSection("champions_road", 0, 19, "STAR_1") + +return settings diff --git a/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/common.lua b/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/common.lua new file mode 100644 index 0000000000..d6d6289cff --- /dev/null +++ b/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/common.lua @@ -0,0 +1,161 @@ +--[[ + common.lua + A collection of frequently used functions and values! +]] -- +require 'nebula_mission_board.common_vars' +MissionGen = require("missiongen_lib.missiongen_lib") + +function COMMON.ShowDestinationMenu(dungeon_entrances, ground_entrances, force_list, speaker, confirm_msg) + local job_dests = MissionGen:LoadJobDestinations() + + local open_dests = {} + -- check for unlock of grounds + for ii = 1, #ground_entrances, 1 do + if ground_entrances[ii].Flag then + local ground_id = ground_entrances[ii].Zone + local zone_summary = _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Zone]:Get(ground_id) + local ground = _DATA:GetGround(zone_summary.Grounds[ground_entrances[ii].ID]) + local ground_name = ground:GetColoredName() + table.insert(open_dests, { + Name = ground_name, + Dest = RogueEssence.Dungeon.ZoneLoc(ground_id, -1, ground_entrances[ii].ID, ground_entrances[ii].Entry) + }) + end + end + + -- check for unlock of dungeons + for ii = 1, #dungeon_entrances, 1 do + if GAME:DungeonUnlocked(dungeon_entrances[ii]) then + local zone_summary = _DATA.DataIndices[RogueEssence.Data.DataManager.DataType.Zone]:Get(dungeon_entrances[ii]) + if zone_summary.Released then + local zone_name = "" + if _DATA.Save:GetDungeonUnlock(dungeon_entrances[ii]) == + RogueEssence.Data.GameProgress.UnlockState.Completed then + zone_name = zone_summary:GetColoredName() + else + zone_name = "[color=#00FFFF]" .. zone_summary.Name:ToLocal() .. "[color]" + end + zone_name = MissionGen:FormatDestinationMenuZoneName(job_dests, dungeon_entrances[ii], zone_name) -- missiongen_lib.lua file + + table.insert(open_dests, { + Name = zone_name, + Dest = RogueEssence.Dungeon.ZoneLoc(dungeon_entrances[ii], 0, 0, 0) + }) + end + end + end + + local dest = RogueEssence.Dungeon.ZoneLoc.Invalid + if #open_dests > 1 or force_list then + if before_list ~= nil then + before_list(dest) + end + + SOUND:PlaySE("Menu/Skip") + local default_choice = 1 + while true do + UI:ResetSpeaker() + UI:DestinationMenu(open_dests, default_choice) + UI:WaitForChoice() + default_choice = UI:ChoiceResult() + + if default_choice == nil then + break + end + local ask_dest = open_dests[default_choice].Dest + if ask_dest.StructID.Segment >= 0 then + -- chosen dungeon entry + if speaker ~= nil then + UI:SetSpeaker(speaker) + else + UI:ResetSpeaker(false) + end + UI:DungeonChoice(open_dests[default_choice].Name, ask_dest) + UI:WaitForChoice() + if UI:ChoiceResult() then + dest = ask_dest + break + end + else + dest = ask_dest + break + end + end + elseif #open_dests == 1 then + if open_dests[1].Dest.StructID.Segment < 0 then + -- single ground entry + if speaker ~= nil then + UI:SetSpeaker(speaker) + else + UI:ResetSpeaker(false) + SOUND:PlaySE("Menu/Skip") + end + UI:ChoiceMenuYesNo(STRINGS:FormatKey("DLG_ASK_ENTER_GROUND", open_dests[1].Name)) + UI:WaitForChoice() + if UI:ChoiceResult() then + dest = open_dests[1].Dest + end + else + -- single dungeon entry + if speaker ~= nil then + UI:SetSpeaker(speaker) + else + UI:ResetSpeaker(false) + SOUND:PlaySE("Menu/Skip") + end + UI:DungeonChoice(open_dests[1].Name, open_dests[1].Dest) + UI:WaitForChoice() + if UI:ChoiceResult() then + dest = open_dests[1].Dest + end + end + else + PrintInfo("No valid destinations found!") + end + + if dest:IsValid() then + if confirm_msg ~= nil then + UI:WaitShowDialogue(confirm_msg) + end + if dest.StructID.Segment > -1 then + -- pre-loads the zone on a separate thread while we fade out, just for a little optimization + _DATA:PreLoadZone(dest.ID) + SOUND:PlayBGM("", true) + GAME:FadeOut(false, 20) + GAME:EnterDungeon(dest.ID, dest.StructID.Segment, dest.StructID.ID, dest.EntryPoint, + RogueEssence.Data.GameProgress.DungeonStakes.Risk, true, false) + else + SOUND:PlayBGM("", true) + GAME:FadeOut(false, 20) + GAME:EnterZone(dest.ID, dest.StructID.Segment, dest.StructID.ID, dest.EntryPoint) + end + end +end + +function COMMON.EnterDungeonMissionCheck(zoneId, segmentID) + local escort_jobs, removed_names = MissionGen:EnterDungeonPrepareParty(zoneId) + MissionGen:PrintSentHome(removed_names) + + local added_names = MissionGen:EnterDungeonAddJobEscorts(zoneId, escort_jobs) + for _, name in ipairs(COMMON.GetSortedKeys(SV.missions.Missions)) do + local mission = SV.missions.Missions[name] + if mission.Complete == COMMON.MISSION_INCOMPLETE and zoneId == mission.DestZone and segmentID == + mission.DestSegment then + if mission.Type == 1 then -- escort + -- add escort to team + local mon_id = mission.EscortSpecies + local new_mob = _DATA.Save.ActiveTeam:CreatePlayer(_DATA.Save.Rand, mon_id, 50, "", -1) + _DATA.Save.ActiveTeam.Guests:Add(new_mob) + + local talk_evt = RogueEssence.Dungeon.BattleScriptEvent("EscortInteract") + new_mob.ActionEvents:Add(talk_evt) + + local tbl = LTBL(new_mob) + tbl.Escort = name + + table.insert(added_names, new_mob.Name) + end + end + end + MissionGen:PrintEscortAdd(added_names) +end \ No newline at end of file diff --git a/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/common_vars.lua b/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/common_vars.lua new file mode 100644 index 0000000000..de68dc41e3 --- /dev/null +++ b/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/common_vars.lua @@ -0,0 +1,20 @@ +--[[ + common_vars.lua + Save vars +]]-- + +---Check used in missiongen_settings to see if there is an active sidequest in the current zone +function COMMON.HasSidequestInZone(zone) + for _, mission in pairs(SV.missions.Missions) do + if mission.DestZone == zone then + return true + end + end + return false +end + +---Overrides origin function. +function COMMON.ExitDungeonMissionCheckEx(result, rescue, zoneId, segmentID) + --remove all guests from the dungeon + return MissionGen:ExitDungeonMissionCheckEx(result, rescue, zoneId, segmentID) +end \ No newline at end of file diff --git a/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/event.lua b/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/event.lua new file mode 100644 index 0000000000..1fb32ffaa3 --- /dev/null +++ b/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/event.lua @@ -0,0 +1,4 @@ +require 'nebula_mission_board.event_single' +require 'nebula_mission_board.event_battle' +require 'nebula_mission_board.event_mapgen' + diff --git a/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/event_battle.lua b/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/event_battle.lua new file mode 100644 index 0000000000..6b4fca9ecc --- /dev/null +++ b/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/event_battle.lua @@ -0,0 +1,17 @@ +--- Reuse origin code +local base_game_escort_interact = BATTLE_SCRIPT.EscortInteract +function BATTLE_SCRIPT.EscortInteract(owner, ownerChar, context, args) + if (context.Target.LuaData.JobReference) then + MissionGen:EscortInteract(owner, ownerChar, context, args) + else + base_game_escort_interact(owner, ownerChar, context, args) + end +end + +function BATTLE_SCRIPT.EscortReached(owner, ownerChar, context, args) + MissionGen:EscortReached(owner, ownerChar, context, args) +end + +function BATTLE_SCRIPT.RescueReached(owner, ownerChar, context, args) + MissionGen:RescueReached(owner, ownerChar, context, args) +end \ No newline at end of file diff --git a/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/event_mapgen.lua b/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/event_mapgen.lua new file mode 100644 index 0000000000..398995decf --- /dev/null +++ b/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/event_mapgen.lua @@ -0,0 +1,5 @@ +require 'nebula_mission_board.common' + +function ZONE_GEN_SCRIPT.GenerateJobInFloor(zoneContext, context, queue, seed, args) + MissionGen:GenerateJobInFloor(zoneContext, context, queue, seed, args) +end \ No newline at end of file diff --git a/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/event_single.lua b/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/event_single.lua new file mode 100644 index 0000000000..22409a447e --- /dev/null +++ b/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/event_single.lua @@ -0,0 +1,47 @@ +function SINGLE_CHAR_SCRIPT.SpawnOutlaw(owner, ownerChar, context, args) + MissionGen:SpawnOutlaw(owner, ownerChar, context, args) +end + +function SINGLE_CHAR_SCRIPT.OutlawFloor(owner, ownerChar, context, args) + MissionGen:OutlawFloor(owner, ownerChar, context, args) +end + +function SINGLE_CHAR_SCRIPT.OutlawDeathCheck(owner, ownerChar, context, args) + MissionGen:OutlawDeathCheck(owner, ownerChar, context, args) +end + +function SINGLE_CHAR_SCRIPT.OutlawCheck(owner, ownerChar, context, args) + MissionGen:OutlawCheck(owner, ownerChar, context, args) +end + +function SINGLE_CHAR_SCRIPT.MonsterHouseOutlawCheck(owner, ownerChar, context, args) + MissionGen:MonsterHouseOutlawCheck(owner, ownerChar, context, args) +end + +function SINGLE_CHAR_SCRIPT.OutlawItemCheckItem(owner, ownerChar, context, args) + MissionGen:OutlawItemCheckItem(owner, ownerChar, context, args) +end + +function SINGLE_CHAR_SCRIPT.OutlawItemCheck(owner, ownerChar, context, args) + MissionGen:OutlawItemCheck(owner, ownerChar, context, args) +end + +function SINGLE_CHAR_SCRIPT.OutlawFleeStairsCheck(owner, ownerChar, context, args) + MissionGen:OutlawFleeStairsCheck(owner, ownerChar, context, args) +end + +function SINGLE_CHAR_SCRIPT.ExplorationReached(owner, ownerChar, context, args) + MissionGen:ExplorationReached(owner, ownerChar, context, args) +end + +function SINGLE_CHAR_SCRIPT.MissionItemCheck(owner, ownerChar, context, args) + MissionGen:MissionItemCheck(owner, ownerChar, context, args) +end + +function SINGLE_CHAR_SCRIPT.MissionGuestCheck(owner, ownerChar, context, args) + MissionGen:MissionGuestCheck(owner, ownerChar, context, args) +end + +function SINGLE_CHAR_SCRIPT.MobilityEndTurn(owner, ownerChar, context, args) + MissionGen:MobilityEndTurn(owner, ownerChar, context, args) +end \ No newline at end of file diff --git a/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/ground/base_camp_2/init.lua b/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/ground/base_camp_2/init.lua new file mode 100644 index 0000000000..11babeb2e4 --- /dev/null +++ b/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/ground/base_camp_2/init.lua @@ -0,0 +1,165 @@ +require 'nebula_mission_board.common' + + +local base_camp_2_bulletin = {} + +local base_init = CURMAPSCR.Init +function base_camp_2_bulletin.Init(map) + base_init(map) +end + +local base_enter = CURMAPSCR.Enter +function base_camp_2_bulletin.Enter(map) + DEBUG.EnableDbgCoro() --Enable debugging this coroutine + GROUND:Unhide("Mission_Board") + if MissionGen:HasCompletedMissions() then + MissionGen:PlayJobsCompletedCutscene(base_camp_2_bulletin.Hand_In_Missions) + end + + base_enter(map) +end + + +function base_camp_2_bulletin.Mission_Board_Action(obj, activator) + DEBUG.EnableDbgCoro() --Enable debugging this coroutine + + local dungeons_needed = 3 --Number of dungeons needed to unlock the Mission Board + + local hero = CH('PLAYER') + GROUND:CharSetAnim(hero, 'None', true) + local required = 3 + + ---@type LibraryRootStruct + local root = MissionGen.root + for _, segments in pairs(root.dungeon_progress) do + for _, state in pairs(segments) do + if state then + required = required - 1 + break + end + end + if required < 1 then break end + end + + if required<1 then + _GAME:SE("Menu/Confirm") + MissionGen:BoardInteract("quest_board") + else + UI:ResetSpeaker() + UI:WaitShowDialogue(STRINGS:Format(STRINGS.MapStrings['Mission_Board_Locked'])) + UI:WaitShowDialogue(STRINGS:Format(STRINGS.MapStrings['Mission_Board_Locked_2'], required)) + end + + GROUND:CharEndAnim(hero) +end + +function base_camp_2_bulletin.Hand_In_Missions(job, npcs) + ---@cast job jobTable + local hero = CH('PLAYER') + GAME:CutsceneMode(true) + UI:ResetSpeaker() + + GROUND:TeleportTo(hero, 100, 600, Direction.Up) + GAME:MoveCamera(90, 565, 1, false) + + if #npcs == 4 then + GROUND:TeleportTo(npcs[1], 100, 575, Direction.Down) --client + GROUND:TeleportTo(npcs[2], 100, 555, Direction.Down) --outlaw + GROUND:TeleportTo(npcs[3], 80, 555, Direction.Down) --left + GROUND:TeleportTo(npcs[4], 120, 555, Direction.Down) --right + elseif #npcs == 2 then + GROUND:TeleportTo(npcs[1], 80, 575, Direction.Down) --client + GROUND:TeleportTo(npcs[2], 120, 575, Direction.Down) --target + else + GROUND:TeleportTo(npcs[1], 100, 575, Direction.Down) --client + end + + UI:ResetSpeaker() + GAME:MoveCamera(90, 565, 1, false) + SOUND:StopBGM() + + GAME:FadeIn(40) + SOUND:PlayBGM("Job Clear!.ogg", true) + UI:SetSpeaker(npcs[1]) + local reward_line1 = STRINGS:Format(STRINGS.MapStrings['Mission_Generic_Reward']) + local reward_line2 = STRINGS:Format(STRINGS.MapStrings['Mission_Generic_Reward_2']) + + if MissionGen:JobTypeIsLawEnforcement(job.Type) then + UI:WaitShowDialogue(STRINGS:Format(STRINGS.MapStrings['Outlaw_Capture_Cutscene_001'], MissionGen:GetCharacterName(job.Target))) + GAME:WaitFrames(20) + reward_line1 = STRINGS:Format(STRINGS.MapStrings['Outlaw_Capture_Cutscene_002']) + reward_line2 = STRINGS:Format(STRINGS.MapStrings['Outlaw_Capture_Cutscene_003']) + elseif MissionGen:JobTypeIsOutlaw(job.Type) then + UI:WaitShowDialogue(STRINGS:Format(STRINGS.MapStrings['Outlaw_Retrieve_Cutscene'], + MissionGen:GetItemName(job.Item))) + elseif job.Type == "RESCUE_SELF" then + UI:WaitShowDialogue(STRINGS:Format(STRINGS.MapStrings['Mission_Response_Rescue'])) + elseif job.Type == "EXPLORATION" then + UI:WaitShowDialogue(STRINGS:Format(STRINGS.MapStrings['Mission_Response_Exploration'], + MissionGen:GetSegmentName(job.Zone, job.Segment))) + elseif job.Type == "LOST_ITEM" then + UI:WaitShowDialogue(STRINGS:Format(STRINGS.MapStrings['Mission_Response_Lost_Item'], + MissionGen:GetItemName(job.Item))) + elseif job.Type == "DELIVERY" then + UI:WaitShowDialogue(STRINGS:Format(STRINGS.MapStrings['Mission_Response_Delivery_Item'], + MissionGen:GetItemName(job.Item))) + elseif job.Type == "ESCORT" then + UI:WaitShowDialogue(STRINGS:Format(STRINGS.MapStrings['Mission_Response_Escort'])) + else + ---RESCUE_FRIEND. this following check is fine because it's the only case with special jobs in this specific project + if job.Special == "LOVER" then + UI:WaitShowDialogue(STRINGS:Format(STRINGS.MapStrings['Mission_Response_Lover'])) + elseif job.Special == "RIVAL" then + UI:WaitShowDialogue(STRINGS:Format(STRINGS.MapStrings['Mission_Response_Rival'])) + elseif job.Special == "CHILD" then + UI:WaitShowDialogue(STRINGS:Format(STRINGS.MapStrings['Mission_Response_Child'])) + else --if job.Special == "FRIEND" or any other case + UI:WaitShowDialogue(STRINGS:Format(STRINGS.MapStrings['Mission_Response_Rescue_Friend'])) + end + end + + GAME:WaitFrames(20) + MissionGen:RewardPlayer(job, npcs[1], reward_line1, reward_line2) + GAME:WaitFrames(20) + + if MissionGen:JobTypeIsLawEnforcement(job.Type) then + UI:SetSpeaker(npcs[1]) + UI:WaitShowDialogue(STRINGS:Format(STRINGS.MapStrings['Outlaw_Capture_Cutscene_004'])) + + GROUND:CharSetEmote(npcs[3], "happy", 0) + GROUND:CharSetEmote(npcs[4], "happy", 0) + local coro1 = TASK:BranchCoroutine(function() + GROUND:CharSetAction(npcs[1], + RogueEssence.Ground.PoseGroundAction(npcs[1].Position, npcs[1].Direction, + RogueEssence.Content.GraphicsManager.GetAnimIndex("Pose"))) + end) + local coro2 = TASK:BranchCoroutine(function() + GROUND:CharSetAction(npcs[3], + RogueEssence.Ground.PoseGroundAction(npcs[3].Position, npcs[3].Direction, + RogueEssence.Content.GraphicsManager.GetAnimIndex("Pose"))) + end) + local coro3 = TASK:BranchCoroutine(function() + GROUND:CharSetAction(npcs[4], + RogueEssence.Ground.PoseGroundAction(npcs[4].Position, npcs[4].Direction, + RogueEssence.Content.GraphicsManager.GetAnimIndex("Pose"))) + end) + local coro4 = TASK:BranchCoroutine(function() + GAME:WaitFrames(12) + SOUND:PlayBattleSE('DUN_Magnet_Bomb') + end) + + TASK:JoinCoroutines({ coro1, coro2, coro3, coro4 }) + GAME:WaitFrames(60) + + GROUND:CharEndAnim(npcs[1]) + GROUND:CharEndAnim(npcs[3]) + GROUND:CharEndAnim(npcs[4]) + GROUND:CharSetEmote(npcs[3], "", 0) + GROUND:CharSetEmote(npcs[4], "", 0) + GAME:WaitFrames(20) + end + GAME:FadeOut(false, 20) + GAME:CutsceneMode(false) +end + +return base_camp_2_bulletin \ No newline at end of file diff --git a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/ground/base_camp_2/strings.resx b/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/ground/base_camp_2/strings.resx similarity index 97% rename from MODS/Enable_Mission_Board/Data/Script/enable_mission_board/ground/base_camp_2/strings.resx rename to MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/ground/base_camp_2/strings.resx index fcee1434ca..8cb18481b4 100644 --- a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/ground/base_camp_2/strings.resx +++ b/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/ground/base_camp_2/strings.resx @@ -157,7 +157,7 @@ Thank you for finding my {0}! - Thank you for delivering my {0} the me! + Thank you for delivering my {0} to me! Thank you for escorting me to my friend! @@ -171,7 +171,4 @@ Thank you so much for saving my child! - - Your team gained {0} EXP as a reward! - \ No newline at end of file diff --git a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/main.lua b/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/main.lua similarity index 78% rename from MODS/Enable_Mission_Board/Data/Script/enable_mission_board/main.lua rename to MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/main.lua index 230c116c44..4a56efcc61 100644 --- a/MODS/Enable_Mission_Board/Data/Script/enable_mission_board/main.lua +++ b/MODS/Nebula's Mission Board Mod/Data/Script/nebula_mission_board/main.lua @@ -9,5 +9,4 @@ -------------------------------------------------------------------------------------------------------------- -- Service Packages -------------------------------------------------------------------------------------------------------------- -require 'enable_mission_board.services.mission_menu_tools' -require 'enable_mission_board.services.mission_service' \ No newline at end of file +require 'missiongen_lib.missiongen_service' \ No newline at end of file diff --git a/MODS/Enable_Mission_Board/Data/Universal.jsonpatch b/MODS/Nebula's Mission Board Mod/Data/Universal.jsonpatch similarity index 82% rename from MODS/Enable_Mission_Board/Data/Universal.jsonpatch rename to MODS/Nebula's Mission Board Mod/Data/Universal.jsonpatch index ad6b7357a5..19a9536569 100644 --- a/MODS/Enable_Mission_Board/Data/Universal.jsonpatch +++ b/MODS/Nebula's Mission Board Mod/Data/Universal.jsonpatch @@ -5,7 +5,7 @@ "0": [ { "$type": "RogueEssence.LevelGen.ScriptZoneStep, RogueEssence", - "Script": "GenerateMissionFromSV", + "Script": "GenerateJobInFloor", "ArgTable": "{}" } ] diff --git a/MODS/Nebula's Mission Board Mod/LICENSE b/MODS/Nebula's Mission Board Mod/LICENSE new file mode 100644 index 0000000000..26ac72f45d --- /dev/null +++ b/MODS/Nebula's Mission Board Mod/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 MistressNebula + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/MODS/Nebula's Mission Board Mod/Mod.xml b/MODS/Nebula's Mission Board Mod/Mod.xml new file mode 100644 index 0000000000..691b5e4d7b --- /dev/null +++ b/MODS/Nebula's Mission Board Mod/Mod.xml @@ -0,0 +1,18 @@ +
+ Nebula's Mission Board Mod + MistressNebula + Adds a mission board to the base PMDO game, that generates random jobs that award items and exp. + This mod contains a job generation library that can be easily included in other mods, with credit. + nebula_mission_board + 3DAA1C36-9B30-4120-A786-86D7C907E4F6 + 1.0.3 + 0.8.10 + Mod + + + 55887EBE-E6E9-408B-A8DC-ADC9D6A5F67C + enable_mission_board + Incompatible + + +
\ No newline at end of file diff --git a/MODS/Nebula's Mission Board Mod/README.md b/MODS/Nebula's Mission Board Mod/README.md new file mode 100644 index 0000000000..b525a0b623 --- /dev/null +++ b/MODS/Nebula's Mission Board Mod/README.md @@ -0,0 +1,18 @@ +# Nebula's Mission Board Mod +This mod adds a mission board to the base PMDO game, allowing players to embark in missions to gain special items and money and to gain experience. + +Baked in this mod is a job generation library that can be easily extracted and included in other mods, with credits to MistressNebula. + +### How to import +Take the missiongen_lib folder and move it inside your mod's Script folder. Use ``require("missiongen_lib.missiongen_lib")`` to store the library in +a global variable, and then write the name of that variable inside the missiongen_service.lua file, in the library_name variable situated at the top +of the file. Remember to also add ```require "missiongen_lib.missiongen_service"``` in your mod's main.lua file. + +You will then need to hook all the library's necessary events to your mod. To do that it would be best to look at this mod's code for reference. + +### How to customize +The missiongen_settings.lua file contains a table of settings that are used by the library to define its behavior. Every setting is documented individually, +describing its format, what it is used for and what kind of data it requires. + +If you need more info, please look at the [library's page](https://wiki.pmdo.pmdcollab.org/Mod:Nebula%27s_Mission_Board) in PMDOWiki. +Feel free to leave issues on GitHub if you want to request some more specific guides that are not listed in the linked page yet! diff --git a/MODS/Enable_Mission_Board/Strings/stringsEx.resx b/MODS/Nebula's Mission Board Mod/Strings/strings.resx similarity index 81% rename from MODS/Enable_Mission_Board/Strings/stringsEx.resx rename to MODS/Nebula's Mission Board Mod/Strings/strings.resx index d90c422b13..0f7f1c8a63 100644 --- a/MODS/Enable_Mission_Board/Strings/stringsEx.resx +++ b/MODS/Nebula's Mission Board Mod/Strings/strings.resx @@ -117,34 +117,209 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Do you really want to leave? - - - You have no more ongoing missions beyond this point.\nWould you like to leave the dungeon now? - - - You have more ongoing missions, but would you like to leave the dungeon now? - - - Waah! A-adventurers! Run for it! - - - Yes!\nKnocked out outlaw {0}! - - - Yes!\nYou reclaimed the {0}! - - - Yes!\nKnocked out outlaw {0} and goons! - - - Yes! You defeated {0}! Defeat the rest of the goons! - - - Grr! You won't be able to defeat me! - - + + Notice Board + + + + Job List + + + + [color=#00FFFF]{0}[color] EXP + + + + {0} was added to the party as a guest. + + + + {0} were added to the party as guests. + + + + Accepted: {0}/{1} + + + + Client: + + + + Delete + + + Difficulty: + + + + Objective: + + + + Reach the end of the dungeon. + + + + Deliver {2} to {0}. + 0 is client. 1 is target. 2 is item. + + + Escort {0} to {1}. + 0 is client. 1 is target. 2 is item. + + + Explore with {0}. + 0 is client. 1 is target. 2 is item. + + + Find {2} for {0}. + 0 is client. 1 is target. 2 is item. + + + Reach {0} to complete missions. + + + + Rescue {1}. + 0 is client. 1 is target. 2 is item. + + + Rescue {0}. + 0 is client. 1 is target. 2 is item. + + + Arrest {1}. + 0 is client. 1 is target. 2 is item. + + + Arrest cowardly {1}. + 0 is client. 1 is target. 2 is item. + + + Reclaim {2} from {1}. + 0 is client. 1 is target. 2 is item. + + + Reclaim stolen {2}. + 0 is client. 1 is target. 2 is item. + + + Arrest big boss {1}. + 0 is client. 1 is target. 2 is item. + + + Mission Objectives + + + + Place: + + + + Reward: + + + + {0} + ??? + + + + {0} + + + + ??? + + + + Job Summary + + + + Suspend + + + + Take Job + + + + Job List + + + + Mission Objectives + + + + [color=#A1A1A1]F[color] + + + + [color=#F8F8F8]E[color] + + + + [color=#F8C8C8]D[color] + + + + [color=#40F840]C[color] + + + + [color=#F8C060]B[color] + + + + [color=#00F8F8]A[color] + + + + [color=#F80000]S[color] + + + + [color=#F8F800]\uE10C1[color] + + + + [color=#F8F800]\uE10C2[color] + + + + [color=#F8F800]\uE10C3[color] + + + + [color=#F8F800]\uE10C4[color] + + + + [color=#F8F800]\uE10C5[color] + + + + [color=#F8F800]\uE10C6[color] + + + + [color=#F8F800]\uE10C7[color] + + + + [color=#F8F800]\uE10C8[color] + + + + [color=#F8F800]\uE10C9[color] + + + + + + I'm too worn out to go on... Mission Title Text - Target is Client @@ -176,55 +351,52 @@ Mission Title Text - Target is Client - [target] needs help! + {0} needs help! Mission Title Text - Target is not Client - I'm worried about [target]! + I'm worried about {0}! Mission Title Text - Target is not Client A Pokémon's gone missing! Mission Title Text - Target is not Client - [target] hasn't returned yet! + {0} hasn't returned yet! Mission Title Text - Target is not Client Our adventure went wrong! Mission Title Text - Target is not Client - Please save [target]! + Please save {0}! Mission Title Text - Target is not Client - [target] might be lost! + {0} might be lost! Mission Title Text - Target is not Client - Please, rescue [target]! + Please, rescue {0}! Mission Title Text - Target is not Client A Pokémon failed to return! Mission Title Text - Target is not Client - [target] needs a rescue! + {0} needs a rescue! Mission Title Text - Target is not Client - Escort me! Mission Title Text - Escort - Please bring me to see [target]! + Please bring me to see {0}! Mission Title Text - Escort - Take me to [target]! + Take me to {0}! Mission Title Text - Escort I need a bodyguard! Mission Title Text - Escort - Please, I need to see [target]! + Please, I need to see {0}! Mission Title Text - Escort - - Seeking exploration! Mission Title Text - Exploration @@ -241,77 +413,69 @@ Help needed with this exploration! Mission Title Text - Exploration - - - Please deliver one [item]! + Please deliver one {2}! Mission Title Text - Delivery - In need of one [item]! + In need of one {2}! Mission Title Text - Delivery - Deliver one [item]. + Deliver one {2}. Mission Title Text - Delivery - I really need one [item]! + I really need one {2}! Mission Title Text - Delivery - I need one [item] delivered! + I need one {2} delivered! Mission Title Text - Delivery - - - Please recover my [item]! + Please recover my {2}! Mission Title Text - Lost Item - I can't find my [item]! + I can't find my {2}! Mission Title Text - Lost Item - Where's my [item]? + Where's my {2}? Mission Title Text - Lost Item - My precious [item] is gone! + My precious {2} is gone! Mission Title Text - Lost Item - Misplaced my [item]! + Misplaced my {2}! Mission Title Text - Lost Item - - - - Ruthless [target] + Ruthless {0} Mission Title Text - Regular Outlaw - Criminal [target] + Criminal {0} Mission Title Text - Regular Outlaw - Dastardly [target] + Dastardly {0} Mission Title Text - Regular Outlaw - Despicable [target] + Despicable {0} Mission Title Text - Regular Outlaw - Nasty [target] + Nasty {0} Mission Title Text - Regular Outlaw - [target]'s bounty! + {0}'s bounty! Mission Title Text - Regular Outlaw - Bounty for arrest of [target]! + Bounty for arrest of {0}! Mission Title Text - Regular Outlaw - [target] still at large! + {0} still at large! Mission Title Text - Regular Outlaw - Wanted: [target]! + Wanted: {0}! Mission Title Text - Regular Outlaw - Arrest [target]! + Arrest {0}! Mission Title Text - Regular Outlaw - Someone stole it! Mission Title Text - Outlaw Item @@ -319,7 +483,7 @@ A thief! Please help! Mission Title Text - Outlaw Item
- Waah! Who took it!? + I was robbed! Mission Title Text - Outlaw Item Grr... They stole my item! @@ -328,38 +492,52 @@ Please, catch this thief! Mission Title Text - Outlaw Item + + It was too dark! + Mission Title Text - Outlaw Item + + I couldn't see who it was... + Mission Title Text - Outlaw Item + + Waah! Who took it!? + Mission Title Text - Outlaw Item + + My item is gone! + Mission Title Text - Outlaw Item + + Please, find the thief! + Mission Title Text - Outlaw Item - [target]'s Gang + {0}'s Gang Mission Title Text - Outlaw Monster House - [target]'s Ambush + {0}'s Ambush Mission Title Text - Outlaw Monster House - [target]'s House of Monsters! + {0}'s House of Monsters! Mission Title Text - Outlaw Monster House - [target], Protected by Bodyguards! + {0}, Protected by Bodyguards! Mission Title Text - Outlaw Monster House - [target]'s Group! + {0}'s Group! Mission Title Text - Outlaw Monster House - - Hotfoot [target] + Hotfoot {0} Mission Title Text - Outlaw Flee - Cowardly [target] + Cowardly {0} Mission Title Text - Outlaw Flee - [target] will give you the slip! + {0} will give you the slip! Mission Title Text - Outlaw Flee - Don't let [target] run away! + Don't let {0} run away! Mission Title Text - Outlaw Flee - Slippery [target] + Slippery {0} Mission Title Text - Outlaw Flee @@ -395,7 +573,6 @@ Aargh, my head... Mission Body Text Top- Target is Client - Please, help me! Mission Body Text Bottom- Target is Client @@ -429,39 +606,37 @@ - - [target] might be in danger! + {0} might be in danger! Mission Body Top Text - Target is not Client - [target] hasn't returned from their adventure! + {0} hasn't returned from their adventure! Mission Body Top Text - Target is not Client - [target] may be hurt! + {0} may be hurt! Mission Body Top Text - Target is not Client - I haven't heard from [target]! + I haven't heard from {0}! Mission Body Top Text - Target is not Client - I got separated from [target]! + I got separated from {0}! Mission Body Top Text - Target is not Client - [target] can't escape the dungeon! + {0} can't escape the dungeon! Mission Body Top Text - Target is not Client - I think [target] is trapped! + I think {0} is trapped! Mission Body Top Text - Target is not Client - [target] may already have fainted! + {0} may already have fainted! Mission Body Top Text - Target is not Client - [target] is lost in the dungeon! + {0} is lost in the dungeon! Mission Body Top Text - Target is not Client - [target] hasn't come home yet... + {0} hasn't come home yet... Mission Body Top Text - Target is not Client - I'm offering a reward upon rescue! Mission Body Bottom Text - Target is not Client @@ -495,25 +670,22 @@ - - - I really need to see [target]! + I really need to see {0}! Mission Body Top Text - Escort - Please, I want to see [target]! + Please, I want to see {0}! Mission Body Top Text - Escort - [target] and I are great friends! + {0} and I are great friends! Mission Body Top Text - Escort - Please, take me to where [target] is! + Please, take me to where {0} is! Mission Body Top Text - Escort - I need help meeting [target]! + I need help meeting {0}! Mission Body Top Text - Escort - Please, take me there! Mission Body Bottom Text - Escort @@ -533,19 +705,19 @@ - I really want to explore [dungeon]! + I really want to explore {1}! Mission Body Top Text - Exploration - Please, I need help exploring [dungeon]! + Please, I need help exploring {1}! Mission Body Top Text - Exploration - I can't explore [dungeon] by myself! + I can't explore {1} by myself! Mission Body Top Text - Exploration - I'm too weak to go to [dungeon] alone... + I'm too weak to go to {1} alone... Mission Body Top Text - Exploration - It's my dream to explore [dungeon]! + It's my dream to explore {1}! Mission Body Top Text - Exploration @@ -565,20 +737,21 @@ Mission Body Bottom Text - Exploration + - Having [item] lets me feel confident! + Having {2} lets me feel confident! Mission Body Top Text - Delivery - [item] has such a lovely scent to it! + {2} has such a lovely scent to it! Mission Body Top Text - Delivery - [item] wanted as an fashion accessory. + {2} wanted as an fashion accessory. Mission Body Top Text - Delivery - [item] helps me sleep easy at night! + {2} helps me sleep easy at night! Mission Body Top Text - Delivery - I like having [item] for adventures! + I like having {2} for adventures! Mission Body Top Text - Delivery @@ -598,20 +771,21 @@ Mission Body Bottom Text - Delivery + - My [item]... Gone! + My {2}... Gone! Mission Body Top Text - Lost Item - My precious [item] is in this awful dungeon! + My precious {2} is in this awful dungeon! Mission Body Top Text - Lost Item - I dropped my [item] somewhere! + I dropped my {2} somewhere! Mission Body Top Text - Lost Item - My [item] is there... I just know it! + My {2} is there... I just know it! Mission Body Top Text - Lost Item - My [item] just has to be there! It must be! + My {2} just has to be there! It must be! Mission Body Top Text - Lost Item @@ -632,8 +806,6 @@ - - This crook is downright dastardly. Mission Body Top Text - Regular Outlaw @@ -682,24 +854,23 @@ Mission Body Bottom Text - Regular Outlaw + - [target] took my [item]! + {0} took my {2}! Mission Body Top Text - Outlaw Item - [target] ran off with my [item]! + {0} ran off with my {2}! Mission Body Top Text - Outlaw Item - [target] robbed my beloved [item]! + {0} took my beloved {2}! Mission Body Top Text - Outlaw Item - That [target]... They pilfered my [item]! + That {0}... They pilfered my {2}! Mission Body Top Text - Outlaw Item - Waah! [target] has my [item]! + Waah! {0} has my {2}! Mission Body Top Text - Outlaw Item - - Please get it back for me! Mission Body Bottom Text - Outlaw Item @@ -717,6 +888,41 @@ Mission Body Bottom Text - Outlaw Item + + + Someone took my {2}! + Mission Body Top Text - Outlaw Item + + Someone ran off with my {2}! + Mission Body Top Text - Outlaw Item + + Someone took my beloved {2}! + Mission Body Top Text - Outlaw Item + + A thief! They pilfered my {2}! + Mission Body Top Text - Outlaw Item + + Waah! my {2} was stolen! + Mission Body Top Text - Outlaw Item + + + Please get it back for me! + Mission Body Bottom Text - Outlaw Item + + I need it back! I'll reward you for its return! + Mission Body Bottom Text - Outlaw Item + + Please punish the thief and get it back for me! + Mission Body Bottom Text - Outlaw Item + + Beat that robber and bring it back, please! + Mission Body Bottom Text - Outlaw Item + + I absolutely need it back! + Mission Body Bottom Text - Outlaw Item + + + This no-good commands a group of henchmen. Mission Body Top Text - Outlaw Monster House @@ -751,7 +957,6 @@ - As soon as this thug is spotted, they'll run for it. Mission Body Top Text - Outlaw Flee @@ -794,14 +999,14 @@ Mission Title - Special Lover Please rescue my sweetheart! - Mission Title - Special Lover + Mission Title - Special Lover Please save my child! Mission Title - Special Parent Child My child is in danger! - Mission Title - Special Parent Child + Mission Title - Special Parent Child My child needs rescuing now! Mission Title - Special Parent Child @@ -861,21 +1066,21 @@ Mission Body - Lovers - Maractus and Cacturne The way she lights up the ocean depths takes my\nbreath away! Please, you have to save my love! - Mission Body - Lovers - Lanturn and Lumineon + Mission Body - Lovers - Lanturn and Lumineon She's the only one that can quell the sandstorm\nin my heart! Please, rescue her, somebody! - Mission Body - Lovers - Tyranitar and Altaria + Mission Body - Lovers - Tyranitar and Altaria Nothing compares to her beauty! I miss her so much!\nPlease, rescue her from that terrible dungeon! - Mission Body - Lovers - Gyarados and Milotic + Mission Body - Lovers - Gyarados and Milotic He's my knight in shining armor!\nPlease, save the love of my life! - Mission Body - Lovers - Gardevoir and Gallade + Mission Body - Lovers - Gardevoir and Gallade Please look for my baby! She doesn't even\nknow how to properly wag her fingers yet! Help! - Mission Body - Parent Child - Clefable and Cleffa + Mission Body - Parent Child - Clefable and Cleffa Please rescue my darling son!\nHe started bouncing and just couldn't stop! Please, help! Mission Body - Parent Child - Wigglytuff and Igglybuff @@ -1060,51 +1265,19 @@ For eons, we've been rivals with each other.\nI'd hate for it to end! Please, somebody, help! Mission Body - Rivals - Bastiodon and Rampardos - Please help [color=#00FF00]Aerodactyl[color]!\nShe's an irreplacable rival! I miss her! + Please help [color=#00FF00]Aerodactyl[color]!\nShe's an irreplaceable rival! I miss her! Mission Body - Rivals - Archeops and Aerodactyl Please rescue my rival! We haven't finished\nseeing who is the bravest! Please, please! Mission Body - Rivals - Swellow and Staraptor - - Added [color=#00FF00]{0}[color] to the party as a guest. - - - Please, take me to {0} {1}! - - - Please, bring me to my love! I'm counting on you! - - - I'm counting on you to bring me to {0}! - - - Have one of your team members leave the dungeon to make room for your client, {0}. - - - Have {0} leave the dungeon? - - - Oh, no! {0} fainted! - - - Yes! You completed {0}'s escort mission.\n{0} is delighted! - - - Thank you for escorting me to {0}! - - - {0}'s twosome left the dungeon! - - - ...{0} doesn't seem to be around. - - - Yes! You've reached the target floor with {0}! - - - Thank you for exploring the dungeon with me! - - - {0} left the dungeon! - + + + Find the legendary {2} + + + Retrieve the legendary {2}, which is\nsaid to be found in the depths of {1}! + + + Retrieve the {2} + \ No newline at end of file diff --git a/MODS/Nebula's Mission Board Mod/Strings/stringsEx.resx b/MODS/Nebula's Mission Board Mod/Strings/stringsEx.resx new file mode 100644 index 0000000000..8db37af9d7 --- /dev/null +++ b/MODS/Nebula's Mission Board Mod/Strings/stringsEx.resx @@ -0,0 +1,286 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Do you really want to leave? + + + You have no more ongoing missions beyond this point.\nWould you like to leave the dungeon now? + + + You have more ongoing missions, but would you like to leave the dungeon now? + + + But the outlaw doesn't seem to be around... + + + Wanted outlaw spotted! + + + Waah! A-adventurers! Run for it! + + + {0} escaped... + + + You've fallen into my trap! + + + Yes!\nKnocked out outlaw {0}! + + + Yes!\nYou reclaimed the {1}! + + + Huh?\n{0} dropped the {1}![br]Yes!\n{0} was the outlaw! + + + Huh?\n{0} was holding the {1}![br]Yes!\n{0} was the outlaw! + + + Yes!\nKnocked out outlaw {0} and goons! + + + Yes! You defeated {0}! Defeat the rest of the goons! + + + Grr! You won't be able to defeat me! + + + Yes! You've found {0}!\nDo you want to use your badge to rescue {0}? + + + Your badge shines on {0}, and {0} is transported away magically! + + + Yes! You've located {0}! Do you want to deliver the requested {1} to {0}? + + + Thanks for the {0}!\n I'll see you in town after with your reward! + + + Oh, please! I really need that {0}... + + + The requested {1} isn't in the Treasure Bag.\nThere is nothing to deliver. + + + Huh, you don't have the {0}? That's too bad... + + + {0} escaped from the dungeon! + + + Yes! You found {0}'s {1}! + + + + Please, take me to {0} {1}! + + + Please, bring me to my love! I'm counting on you! + + + I'm counting on you to bring me to {0}! + + + Added {0} to the party as a guest. + + + Added {0} to the party as guests. + + + Oh, no! {0} fainted! + + + Yes! You completed {0}'s escort mission.\n{0} is delighted! + + + Thank you for escorting me to {0}! + + + {0}'s twosome left the dungeon! + + + ...{0} doesn't seem to be around. + + + Yes! You've reached the target floor with {0}! + + + Thank you for exploring the dungeon with me! + + + {0} left the dungeon! + + + + Thanks for the rescue!\nI'll see you in town after with your reward! + Mission Dialogue - Rescue - Yes + + Thank you for rescuing me! This place was so scary! I can't wait to see my family again! + Mission Dialogue - Rescue Child - Yes + + Oh, my friend sent you to rescue me? Thank goodness! We'll see you in town later to say thanks! + Mission Dialogue - Rescue Friend - Yes + + Tch, my rival sent you to rescue me, huh? Well, thank you. We'll reward you later in town. + Mission Dialogue - Rescue Rival - Yes + + Oh, my beloved {0} sent you to rescue me? I can't wait to reunite with them! + Mission Dialogue - Rescue Lover - Yes + + + H-hey! Don't just leave me here! + Mission Dialogue - Rescue - No + + Waaah! It's s-scary here! P-please help me! + Mission Dialogue - Rescue Child - No + + Please don't leave me here! My friend is probably worried sick! + Mission Dialogue - Rescue Friend - No + + Woah, don't just leave me hanging here! + Mission Dialogue - Rescue Rival - No + + Please, get me out of here! I just want to see my dear {0} again! + Mission Dialogue - Rescue Lover - No + + + Team {0} received {1}.[pause=40] + + + Team {0} received [a/an] {1}.[pause=40] + + + The {1} was sent to storage. + + + The adventurous exploits of Team {0} are an inspiration to me![br]Please, let me become a member of your team! + + + Accept {1} as a team member? + + + Your team gained {1} as a reward![pause=40] + + + Congratulations![pause=0][br]Team {0} went up in rank from the\n[color=#FFA5FF]{1} Rank[color] to the [color=#FFA5FF]{2} Rank[color]! + + \ No newline at end of file