nicolealexawalter / Dungeons-and-Dragons-Utility-Tool

https://nicolealexawalter.github.io/Dungeons-and-Dragons-Utility-Tool/
0 stars 0 forks source link

Create Creature Searcher #3

Closed nicolealexawalter closed 3 years ago

nicolealexawalter commented 3 years ago

Need to be able to search all creatures, by:

  1. environment
    • need to be able multi-select
  2. name
    • fuzzy name search
  3. type
    • multi-select
  4. XP
    • multi-select on all possible XP values
    • greater than X value
    • less than X value
  5. book
    • multi-select

Must also be able to sort them by any field, in either ascending or descending order

nicolealexawalter commented 3 years ago

Close #4 when I close this issue

nicolealexawalter commented 3 years ago

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

nicolealexawalter commented 3 years ago

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
nicolealexawalter commented 3 years ago

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)
    )
  )
)
nicolealexawalter commented 3 years ago

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

nicolealexawalter commented 3 years ago

Take care of #6 with this as well

nicolealexawalter commented 3 years ago

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

nicolealexawalter commented 3 years ago

just committed with all needed filters & sorting