Closed nicolealexawalter closed 3 years ago
Close #4 when I close this issue
Go through each book and record every creature into a master file in the following format:
~~Name,Type,XP,Book,Page,Env-Env-Env-Env~~
eg
~~Gbahali,Beast,2300,tob,109,Mountain-Hill-Swamp-etc~~
All creatures will be split into their own env file, and searching multiple environments will simply search multiple files
First, you need to set up the infrastructure to get this done. Create an example file with some BS items in it, then set up the code to load it in, parse it, and return items based on search criteria.
Then, go through every single book and record these details. VGTM/TOF both have crtr/env lists already, there are likely lists for the MM / TOB online
tob: https://koboldpress.com/kpstore/product/monsters-by-terrain-pdf/
mm: DMG . Appendix B page 302
Alternatively, everything but TOB should be on donjon: https://donjon.bin.sh/5e/monsters/
Perhaps I could DL it & write up some code to quickly parse it?
TOB creatures: https://aversten.obsidianportal.com/wiki_pages/tome-of-beasts
How to get these files in the quickest way possible:
Copy out of Kobold Fight Club (https://kobold.club/fight/#/encounter-builder) for a given env type
Paste into google sheets
Build a function to:
1. clean it up so no cell has a value after the newline character (we dont need second row in the name or book/page)
2. convert the CR to an XP value
Then convert it to a CSV, clean it up in notepad++ a bit (eg remove double quotes)
Finally, paste it into the relevant environment file in the db
ended up formatting it in appian like a basic ass bitch. code to do so as follows:
load(
/*
start with an array of strings - raw output of KFC,
with /r/n -> ",\r\n" && tab spaces -> _ && +" @ B/E of doc
*/
local!strings: {
"Morkoth",
"11_Medium_Aberration_chaotic evil_",
"Volo's Guide to Monsters p.177",
"Chuul",
"4_Large_Aberration_chaotic evil_",
"Monster Manual p.40",
"Aboleth",
"10_Large_Aberration_lawful evil_",
"Monster Manual p.13",
"Wharfling Swarm",
"4_Large_Beast (Swarm)_unaligned_",
"Tome of Beasts p.407",
"Wharfling",
"1/8_Tiny_Beast_unaligned_",
"Tome of Beasts p.407",
"Swarm of Quippers",
"Misc Creatures",
"1_Medium_Beast (Swarm)_unaligned_",
"Monster Manual p.338",
"Sea Horse",
"Misc Creatures",
"0_Tiny_Beast_unaligned_",
"Monster Manual p.337",
"Reef Shark",
"Misc Creatures",
"1/2_Medium_Beast_unaligned_",
"Monster Manual p.336",
"Player's Handbook p.309",
"Quipper",
"Misc Creatures",
"0_Tiny_Beast_unaligned_",
"Monster Manual p.335",
"Plesiosaurus",
"Dinosaurs",
"2_Large_Beast_unaligned_",
"Monster Manual p.80",
"Octopus",
"Misc Creatures",
"0_Small_Beast_unaligned_",
"Monster Manual p.333",
"Mbielu",
"Dinosaurs",
"3_Huge_Beast_unaligned_",
"Tome of Beasts p.114",
"Killer Whale",
"Misc Creatures",
"3_Huge_Beast_unaligned_",
"Monster Manual p.331",
"Hunter Shark",
"Misc Creatures",
"2_Large_Beast_unaligned_",
"Monster Manual p.330",
"Glass Gator",
"1_Large_Beast_unaligned_",
"Tome of Beasts p.228",
"Giant Toad",
"Misc Creatures",
"1_Large_Beast_unaligned_",
"Monster Manual p.329",
"Giant Shark",
"Misc Creatures",
"5_Huge_Beast_unaligned_",
"Monster Manual p.328",
"Giant Sea Horse",
"Misc Creatures",
"1/2_Large_Beast_unaligned_",
"Monster Manual p.328",
"Giant Octopus",
"Misc Creatures",
"1_Large_Beast_unaligned_",
"Monster Manual p.326",
"Giant Frog",
"Misc Creatures",
"1/4_Medium_Beast_unaligned_",
"Monster Manual p.325",
"Giant Crocodile",
"Misc Creatures",
"5_Huge_Beast_unaligned_",
"Monster Manual p.324",
"Giant Crab",
"Misc Creatures",
"1/8_Medium_Beast_unaligned_",
"Monster Manual p.324",
"Garroter Crab",
"1/4_Tiny_Beast_unaligned_",
"Tome of Beasts p.208",
"Frog",
"Misc Creatures",
"0_Tiny_Beast_unaligned_",
"Monster Manual p.322",
"Player's Handbook p.305",
"Dolphin",
"1/8_Medium_Beast_unaligned_",
"Volo's Guide to Monsters p.208",
"Crocodile",
"Misc Creatures",
"1/2_Large_Beast_unaligned_",
"Monster Manual p.320",
"Player's Handbook p.305",
"Crab",
"Misc Creatures",
"0_Tiny_Beast_unaligned_",
"Monster Manual p.320",
"Bone Crab",
"1/2_Small_Beast_neutral_",
"Tome of Beasts p.40",
"Salt Golem",
"Golems",
"10_Large_Construct_unaligned_",
"Tome of Beasts p.235",
"Young Sea Dragon",
"Dragons",
"9_Large_Dragon_neutral evil_",
"Tome of Beasts p.136",
"Sea Dragon Wyrmling",
"Dragons",
"2_Medium_Dragon_neutral evil_",
"Tome of Beasts p.136",
"Dragon Turtle",
"17_Gargantuan_Dragon_neutral_",
"Monster Manual p.119",
"Dragon Eel",
"12_Huge_Dragon_neutral_",
"Tome of Beasts p.146",
"Coral Drake",
"7_Medium_Dragon_neutral evil_",
"Tome of Beasts p.150",
"Ancient Sea Dragon",
"Dragons",
"22_Gargantuan_Dragon_neutral evil_",
"Tome of Beasts p.135",
"Adult Sea Dragon",
"Dragons",
"16_Huge_Dragon_neutral evil_",
"Tome of Beasts p.135",
"Water Elemental Myrmidon",
"Elder Myrmidons",
"7_Medium_Elemental_neutral_",
"Mordenkainen's Tome of Foes p.203",
"Water Elemental",
"Elementals",
"5_Large_Elemental_neutral_",
"Monster Manual p.125",
"Marid",
"Genies",
"11_Large_Elemental_chaotic neutral_",
"Monster Manual p.146",
"Leviathan",
"Elder Elementals",
"20_Gargantuan_Elemental_neutral_",
"Mordenkainen's Tome of Foes p.198",
"Sea Hag (coven)",
"Hags",
"4_Medium_Fey_chaotic evil_",
"Monster Manual p.179",
"Sea Hag",
"Hags",
"2_Medium_Fey_chaotic evil_",
"Monster Manual p.179",
"Rum Gremlin",
"1/2_Tiny_Fey_chaotic evil_",
"Tome of Beasts p.239",
"River King",
"Fey Lords and Ladies",
"16_Medium_Fey_chaotic neutral_",
"Tome of Beasts p.196",
"Lorelei",
"5_Medium_Fey_chaotic evil_",
"Tome of Beasts p.279",
"Gerridae",
"1_Large_Fey_neutral_",
"Tome of Beasts p.212",
"Eel Hound",
"2_Medium_Fey_neutral_",
"Tome of Beasts p.166",
"Wastrilith",
"Demons",
"13_Large_Fiend (Demon)_chaotic evil_",
"Mordenkainen's Tome of Foes p.139",
"Storm Giant Quintessent",
"Giants",
"16_Huge_Giant (Storm Giant)_chaotic good_",
"Volo's Guide to Monsters p.151",
"Lake Troll",
"7_Large_Giant_chaotic evil_",
"Tome of Beasts p.389",
"Subek",
"5_Large_Humanoid (Subek)_lawful neutral_",
"Tome of Beasts p.371",
"Sea Spawn",
"1_Medium_Humanoid_neutral evil_",
"Volo's Guide to Monsters p.189",
"Sahuagin Priestess",
"Sahuagin",
"2_Medium_Humanoid (Sahuagin)_lawful evil_",
"Monster Manual p.264",
"Sahuagin Baron",
"Sahuagin",
"5_Large_Humanoid (Sahuagin)_lawful evil_",
"Monster Manual p.264",
"Sahuagin",
"Sahuagin",
"1/2_Medium_Humanoid (Sahuagin)_lawful evil_",
"Monster Manual p.263",
"Merfolk",
"1/8_Medium_Humanoid (Merfolk)_neutral_",
"Monster Manual p.218",
"Kuo-toa Whip",
"Kuo-toa",
"1_Medium_Humanoid (Kuo-toa)_neutral evil_",
"Monster Manual p.200",
"Kuo-toa Monitor",
"Kuo-toa",
"3_Medium_Humanoid (Kuo-toa)_neutral evil_",
"Monster Manual p.198",
"Kuo-toa Archpriest",
"Kuo-toa",
"6_Medium_Humanoid (Kuo-toa)_neutral evil_",
"Monster Manual p.200",
"Kuo-toa",
"Kuo-toa",
"1/4_Medium_Humanoid (Kuo-toa)_neutral evil_",
"Monster Manual p.199",
"Kraken Priest",
"NPCs",
"5_Medium_Humanoid (Any Race)_any_",
"Volo's Guide to Monsters p.215",
"Deep Scion",
"3_Medium_Humanoid (Shapechanger)_neutral evil_",
"Volo's Guide to Monsters p.135",
"Deep One Hybrid Priest",
"4_Medium_Humanoid (Deep One)_chaotic evil_",
"Tome of Beasts p.73",
"Deep One Archimandrite",
"8_Large_Humanoid (Deep One)_chaotic evil_",
"Tome of Beasts p.74",
"Deep One",
"2_Medium_Humanoid (Deep One)_chaotic evil_",
"Tome of Beasts p.73",
"Archdruid",
"NPCs",
"12_Medium_Humanoid (Any Race)_any_",
"Volo's Guide to Monsters p.210",
"Zaratan",
"26_Gargantuan_Monstrosity_unaligned_",
"Tome of Beasts p.414",
"Water Leaper",
"4_Large_Monstrosity_unaligned_",
"Tome of Beasts p.406",
"Tarrasque",
"30_Gargantuan_Monstrosity (Titan)_unaligned_",
"Monster Manual p.286",
"Merrow",
"2_Large_Monstrosity_chaotic evil_",
"Monster Manual p.219",
"Mahoru",
"3_Large_Monstrosity_unaligned_",
"Tome of Beasts p.281",
"Kraken",
"23_Gargantuan_Monstrosity (Titan)_chaotic evil_",
"Monster Manual p.197",
"Krake Spawn",
"9_Huge_Monstrosity_neutral evil_",
"Tome of Beasts p.269",
"Isonade",
"14_Gargantuan_Monstrosity_chaotic neutral_",
"Tome of Beasts p.257",
"Hydra",
"8_Huge_Monstrosity_unaligned_",
"Monster Manual p.190",
"Zombie",
"Zombies",
"1/4_Medium_Undead_neutral evil_",
"Monster Manual p.316",
"Player's Handbook p.311",
"Rusalka",
"6_Medium_Undead_chaotic evil_",
"Tome of Beasts p.331",
"Nihilethic Zombie",
"1_Medium_Undead_neutral evil_",
"Tome of Beasts p.9",
"Drowned Maiden",
"5_Medium_Undead_neutral evil_",
"Tome of Beasts p.159"
},
/*
our array is split into a series of creatures
theoretically composed of any number of rows, split into three sections (x,y,z)
the x sections are the names. The first name is the real name,
all other names are the unreal. The y section has the _ delimited
creature data. The z section is the entries in the books
*/
/*
our goal is to grab the first name;
each item from the delimited section;
convert the CR into XP;
grab the first book entry;
*/
/*
we need the index of every single y row to know which name is the first name in
any given x section
*/
local!y_indices: a!flatten(
a!forEach(
items: local!strings,
expression: if(
length(local!split(fv!item, "_")) < 2,
{},
fv!index
)
)
),
local!cr_xp_dicts: {
{ CR: "0", XP: "10" },
{ CR: "1/8", XP: "25" },
{ CR: "1/4", XP: "50" },
{ CR: "1/2", XP: "100" },
{ CR: "1", XP: "200" },
{ CR: "2", XP: "450" },
{ CR: "3", XP: "700" },
{ CR: "4", XP: "1,100" },
{ CR: "5", XP: "1,800" },
{ CR: "6", XP: "2,300" },
{ CR: "7", XP: "2,900" },
{ CR: "8", XP: "3,900" },
{ CR: "9", XP: "5,000" },
{ CR: "10", XP: "5,900" },
{ CR: "11", XP: "7,200" },
{ CR: "12", XP: "8,400" },
{ CR: "13", XP: "10,000" },
{ CR: "14", XP: "11,500" },
{ CR: "15", XP: "13,000" },
{ CR: "16", XP: "15,000" },
{ CR: "17", XP: "18,000" },
{ CR: "18", XP: "20,000" },
{ CR: "19", XP: "22,000" },
{ CR: "20", XP: "25,000" },
{ CR: "21", XP: "33,000" },
{ CR: "22", XP: "41,000" },
{ CR: "23", XP: "50,000" },
{ CR: "24", XP: "62,000" },
{ CR: "25", XP: "75,000" },
{ CR: "26", XP: "90,000" },
{ CR: "27", XP: "105,000" },
{ CR: "28", XP: "120,000" },
{ CR: "29", XP: "135,000" },
{ CR: "30", XP: "155,000" }
},
local!creature_dicts: a!flatten(
a!forEach(
items: local!strings,
expression: /*all operations happen on data rows*/
with(
local!split: split(fv!item, "_"),
if(
length(local!split) < 2,
{},
with(
/*
first we grab the y data
*/
local!cr: local!split[1],
local!size: local!split[2],
local!type: local!split[3],
local!alignment: local!split[4],
/*
then we get the name. to start, we need to find the last y row before our current one
this index will be the index before our current y index
*/
local!last_y_index_array: index(
local!y_indices,
wherecontains(
tostring(fv!index),
touniformstring(local!y_indices)
) - 1,
0
),
/*
above evaluates to a list w/ a single item in some cases, so we get around that by
indexing into first item or just returning it if it is not an array
*/
local!last_y_index: index(
local!last_y_index_array,
1,
local!last_y_index_array
),
/*
after the last y index, there are any number of rows in the following z section
lucky for us, every row in a z section has the string p.#
and the invalid creature names would never have a lowercase p followed by a period!
so all we need to do is search all rows between the last y index and the current y index
for the first non p-dot row to follow a p-dot row
*/
/*
to start, we find the max of a list of indices between last-y & curr-y w/ a p-dot value
to do this, we begin by iterating over a list of indices between our curr/last y indices
*/
local!search_indices: local!last_y_index + enumerate(fv!index - local!last_y_index - 1) + 1,
local!bookrows: a!flatten(
a!forEach(
items: local!search_indices,
expression: if(
search("p.", index(local!strings, fv!item, {})),
fv!item,
{}
)
)
),
local!lastbookrow_index: max(local!bookrows),
/*
the row after our last book row should be our first name row
*/
local!name: index(
local!strings,
local!lastbookrow_index + 1,
"NAME NOT FOUND"
),
/*
then the book should always be row after current row
*/
local!book: index(
local!strings,
fv!index + 1,
"BOOK NOT FOUND"
),
/*
then we format CR into XP
*/
local!xp_array: index(
index(
local!cr_xp_dicts,
wherecontains(
tostring(local!cr),
touniformstring(index(local!cr_xp_dicts, "CR", {}))
),
{}
),
"XP",
local!cr
),
local!xp: index(
local!xp_array,
1,
local!xp_array
),
{
name: local!name,
xp: local!xp,
size: local!size,
type: local!type,
alignment: local!alignment,
book: local!book
}
)
)
)
)
),
joinarray(
a!foreach(
items: local!creature_dicts,
expression: fv!item.name & "|" & fv!item.size & "|" & fv!item.type & "|" & fv!item.alignment & "|" & fv!item.xp & "|" & fv!item.book & char(10)
)
)
)
Now have files succesfully loading in & displaying -
Take a look at this to see about how I can dynamically build/load a grid from a list of text strings
Once I can build a grid around my array of strings, set up code to: a) build all filters b) upon search, grab all creatures from all documents corresponding to selected env's (or all docs if no env selected) c) remove duplicate creatures d) filter list by all other relevant filters e) feed list into grid for display
also, it would be ideal if grid could be sortable (instead of sorting by XP in filters, you sort by clicking the grid header or some little icon or something?) & ideally it would be nice if I could copy & paste out of it as well
Take care of #6 with this as well
1) build all filter sections
1a) environment (multiselect dropdown)
- Aquatic
- Arctic
- Cave
- Coast
- Desert
- Dungeon
- Forest
- Grassland
- Mountain
- Planar
- Ruins
- Swamp
- Underground
- Urban
1b) creature type (multiselect dropdpown)
- Aberration
- Beast
- Celestial
- Construct
- Dragon
- Elemental
- Fey
- Fiend
- Giant
- Humanoid
- Monstrosity
- Ooze
- Plant
- Undead
1c) XP range ANY to ANY (number entry)
1d) book (multiselect dropdown)
- Tome of Beasts
- Monster Manual
- Volo's Guide to Monsters
- Mordenkainen's Tome of Foes
2) upon search, grab all creatures from all documents corresponding to selected env's (or all docs if no env selected)
3) remove duplicate creatures
4) filter list by all other relevant filters
4a) return lists to output field
4b) also add filters for size / alignment
5) set up sorting options for each field in both directions
7) take care of issue #6
just committed with all needed filters & sorting
Need to be able to search all creatures, by:
Must also be able to sort them by any field, in either ascending or descending order