The Great Antarctic Hexcrawl pt. 6b - Hexfill Addendum
This is my own version of lore24, an admittedly over-ambitious attempt to procedurally generate a 43,000-hex crawl for my homebrewed far-future Antarctica, Antibor. Part 1, Part 2, Part 3, Part 4 and 4b, Part 5, Part 6.
In the previous installment, I described a basic hexfill procedure inspired by the one Luke Gearing used for Wolves Upon The Coast & applied it to all 43,000 hexes in Antibor. The procedure assigned four categories: 'Lair', 'Settlement', 'Weird', and 'Flavor'. I also wrote a bit about stylistic inspirations & how I was planning to use my nested random generator script to generate multiple hexes per prompt.
While compiling a list of prompts, however, I ran into some issues:
- Hexes that are recorded solely as a prose description are hard to tie to anything else -- some 'weird' hexes should contribute entries to the local encounter table despite not being lairs per se
- Some hexfills should be more likely to appear in some biomes than others, with some overlap (an entry about magic trees could go in forest, jungle, taiga, or even chaparral); just making separate lists for each biome doesn't work well
- I want all hex descriptions to appear at least once, but also want to limit how many times certain prompts get placed.
Hexfill Redux
Thus, I decided to move from a single big list of prompts to a table where I could store 'metadata' for each prompt. Here are the 'knobs' I want to be able to control for each hexfill:
- Category (Lair/weird/settlement)
- Subcategory (e.g., 'Fort' as a subcategory of 'settlement')
- Unique entry name or ID (used to link)
- Biome weights (Relative likelihood of appearing in each biome type)
- Weight (Relative likelihood of appearing overall)
- Valid cultural regions (usually blank, but this allows me to restrict, say, Xikai villages to Lon Galo, or Riluvii to Riliu Goltha)
- A prompt to randomly generate a description for the key
- A prompt to randomly generate an entry for the regional encounter table
- % in lair (essentially determines the weight given to any encounter table entry)
- Minimum required (the hexfilling algorithm will place up to this number for each entry first)
- Maximum allowed (the hexfilling algorithm will stop placing the entry after this is reached)
- Current number placed (keeping track of this will help if I end up adding entries and re-running)
I chose to omit treasure and NPCs -- I'd prefer to just bake treasure into hex key description prompts, and tracking NPCs separately (so far) introduces a level of complication I want to avoid. (Though I may reverse this decision in a future outpouring of manic ambition, tbd).
This solves a few problems:
- The Category, Subcategory, and Unique entry name fields give me much more taxonomic flexibility. In the future, I could write a script that references 'Settlements', or specifically 'Forts', or drills all the way down to 'Watchtowers'. It also enables me to group common subcategories of 'weird' hexes like shipwrecks, ghost towns, mysterious towers, etc.
- The relative biome weights solve the issue of fuzzy biome categories mentioned above
- I can store monster lairs, settlements, and 'weird' hexfills on the same table.
- I can easily insert references to pre-written modules I want to insert somewhere and ensure they are only placed once (and in a roughly appropriate area).
- I can use the 'minimum required' value to start filling hexes without committing to fill every single keyed hex.
The hexfilling algorithm I have in mind works like this:
- Pre-process the list hexfill entries by normalizing all biome weights so that sum = 1
- Pick a random hex with a hexfill category assigned
- Filter the table of hexes to only include eligible hexes
- Weight in hex's biome > 0
- Category = hex's category
- Region in region restrictions, otherwise ignore
- Current number placed < max allowed
- If any entries have current_assigned < min required, filter to only those entries, otherwise move on
- Calculate a weight for each entry based on its overall weight score and weight for the given biome
- Do a weighted sample to pick an entry for the hex, assign the entry ID value to the hex.
- Filter the table of hexes to only include eligible hexes
Placing Dungeons
I wrote a quick script to randomly plunk down 333 dungeons into open landmark or hidden hexfill slots:
### Place Dungeons
hexes <- vect(file.path(mapdir, 'Full_hexes.gpkg'))
avail_landmark <- as.data.frame(hexes) %>%
filter(!Biome_majority%in%c(0, 14, 15)) %>%
filter(is.na(Hexfill)) %>% select(id) %>% mutate(type='Hexfill')
avail_hidden <- as.data.frame(hexes) %>%
filter(!Biome_majority%in%c(0, 14, 15)) %>%
filter(is.na(Hexfill_hidden)) %>% select(id) %>% mutate(type='Hexfill_hidden')
dungeon_hexes <- bind_rows(avail_landmark, avail_hidden) %>%
slice_sample(n=256)
for(hex in dungeon_hexes$id){
print(hex)
type = dungeon_hexes[dungeon_hexes$id==hex, 'type']
hexes[hexes$id==hex, type] <- '[Dungeon]'
}
nrow(hexes[hexes$Hexfill == '[Dungeon]'])
nrow(hexes[hexes$Hexfill_hidden == '[Dungeon]'])
writeVector(hexes, file.path(mapdir, 'Full_hexes.gpkg'), overwrite=T)
I wound up placing 80 non-'hidden' dungeons and 176 'hidden' dungeons, which seems appropriate.
I think the plan is going to be to gradually assign 'handmade' dungeons (by me or others) to these hexes and then to use the dungeon generator to fill in the rest, ideally replacing the pointcrawl maps with hand-drawn maps if/when I get to run them.