Idraluna Archives

The Great Antarctic Hexcrawl Pt. 3 - He Did The Mash (Random Monsters)

This is my own version of lore24, an admittedly over-ambitious attempt to procedurally generate a 128,000-hex crawl for my homebrewed far-future Antarctica, Antibor. Part 1 here. Part 2 here.


Before I populate Antibor with lairs and keyed dungeons, I need a list of monsters and foes. I've been adding piecemeal to a personal GM-binder bestiary for a while, but it needed some expansion and revision.

Antiborean monsters fall into a few categories:

The Monster Table

I'm keeping all the monsters and relevant stats in a .csv table. (See this post about inserting information from a csv into a LaTeX file). Since Archons & Armigers is OD&D-based, stats are written accordingly. Each monster has a column where a reference to a .tex file with additional description can be stored.

Aside from stats, the csv also has a OD&D-style no. appearing, no. in lair, and lair nearby columns, and a column for each biome storing the relative likelihood of that creature appearing as an integer. There are also three dungeon depth levels (Limnetic, Profundal, and Abyssal) that function the same way. These will be used to stock lairs and assemble encounter tables.

Generating Monsters

I'm finding that my creative process often begins with a catchy name that I then subject to perform a vibes-based extrapolation. (More random generators & GM tools should spit out evocative and ill-defined names rather than things). Jack Vance's Dying Earth series and the Appendix N Podcast episode on Cugel's Saga helped to crystalize this approach in my mind. Vance alludes to monsters like the 'Erb', 'Grue', 'Pelgrane', 'Deodand', and 'Leucomorph' without ever describing them in more detail than is necessary for the immediate situation; the effect is at once whimsical and sinister, naturalizing them as an ever-present threat that his characters are used to living with. I hope to capture a little bit of that feeling.

My list of made-up names is below. They're extremely silly, but (for me at least) they have a sense of personality that makes me want to discover their story. (Those italicized are homages in some way.)

It's a long list, many will surely end up as cruft, but I'd rather go to big and downsize than introduce an arbitrary limit. The goal is to have a solid roster of common foes that can be richly developed when stocking the campaign, but also a diverse menagerie of ultra-rare monsters to keep players on their toes & add a sense of alien unknowability to the world.

Stats

I used R to quickly generate stats for the new monsters. HD was rolled randomly on a d12. AC on a d9. For speed I made a vector of options including fly and burrow speeds and just picked randomly. And for number appearing & no. in lair I had it select randomly from the list of entries recorded for other monsters. Biome preference levels were randomly drawn from (0,0,1,2,3). Some incongruous results were generated, but imo that's just another hook to extrapolate on.

Descriptions

I got hung up on this step because I have persnickety, exacting standards for how weird and alien I want my bestiary to be.

I initially had ambitious plans to write something like the demon generator in AD&D or the one-roll monster page in Worlds Without Number. But I decided these would overdetermine the monsters and stifle later creativity (and clutter up statblocks with extraneous formulaic text). I'd rather build up from an interesting prompt than pare down from a boring result.

So just as Vance was parsimonious with his monster descriptions, I opted to simply roll thrice on a table of adjectives I threw together one morning. This gives me a good starting point for a more detailed description if I ever get inspired later, and just enough to improvise with if I don't.

Monster Adjectives

  1. Putrescent
  2. Lumbering
  3. Nimble
  4. Glistening
  5. Alluring
  6. Segmented
  7. Luminous
  8. Anodic
  9. Voracious
  10. Blighted
  11. Corrosive
  12. Incandescent
  13. Vengeful
  14. Psychic
  15. Plumed
  16. Faceted
  17. Iridescent
  18. Prickly
  19. Garrulous
  20. Tormented
  21. Sadistic
  22. Mechanical
  23. Avaricious
  24. Blasphemous
  25. Reptilian
  26. Feline
  27. Ursine
  28. Canine
  29. Amorphous
  30. Anneloid
  31. Molluscoid
  32. Humanoid
  33. Avian
  34. Arthropoid
  35. Cunning
  36. Instinctive
  37. Wheeled
  38. Whirling
  39. Constricting
  40. Crushing
  41. Adhesive
  42. Insane
  43. Rational
  44. Mocking
  45. Inquisitive
  46. Elusive
  47. Stealthy
  48. Explosive
  49. Venomous
  50. Sorcerous
  51. Demonic
  52. Coiled
  53. Hirsute
  54. Methodical
  55. Necrotic
  56. Leathery
  57. Colorless
  58. Visceral
  59. Cybernetic
  60. Geometric
  61. Taurine
  62. Helical
  63. Chimerical
  64. Enthralling
  65. Humanoid
  66. Telekinetic

For example

The Ambulatory Diatomarl is 'vengeful, prickly, and ursine'. I'm imagining a crystalline hedgehog-bear that hunts down anyone who disturbs its (inevitably inconvenient) slumber.

The Goxipline is 'Molluscoid, Lumbering, Psychic' (and has 9 HD) so I'll say it's an elephant-sized (and I guess vaguely phallic?) land squid with psionic powers. I'm still working out what I want these to be; I think the ability to selectively edit itself out of others perceptions would be funny -- like, a giant elephant squid is rampaging through the village but nobody is able to integrate seeing it into their memories; chaos ensues, and adventurers are needed to devise some creative way to deduce its existence.

The Yarb is 'avaricious, plumed, instinctive', and 2 HD - perhaps a toothy little pterodactyl, that (driven mad by jealousy at its avian cousins) adorns itself with colorful scraps of cloth, paper, leaves etc. Acquiring something like a spell scroll would be a literal feather in its cap.


The current WIP version of my bestiary can be viewed here.


Code

library(tidyverse)

### Create placeholder 
monsters <- read.csv(file.path(chapterdir, 'Bestiary.csv'))

for (i in 1:nrow(monsters)){
  if (monsters$Description[i] == 'blank.tex'){
    outfile <- file.path(chapterdir, 'Bestiary_desc', paste0(monsters$Name[i], '.tex'))
    cat(' ', file=outfile, append=T)
    monsters$Description[i] <- paste0(monsters$Name[i], '.tex')
  }

  if (monsters$r_desc[i] == 1){
    ## random HD
    if(monsters$HD[i] == 0){
      monsters$HD[i] <- sample(1:12, 1)
    }

    ## random AC
    if(monsters$Armor[i] == -5|is.na(monsters$Armor[i])){
      monsters$Armor[i] <- sample(1:9, 1)
    }

    ## random attack power
    if(monsters$AADamage1[i] == ''){
      monsters$AADamage1[i] <- sample(c('Heavy', 'Standard', 'Light'), 1)
    }

    ## move
    if(monsters$Move_in[i]==''){
      monsters$Move_in[i] <- sample(c('3', '6', '9', '9', '9', '12', '12', '12', '12', '15', '15', '18', '9/18F', '6/21F', '12/21F', '9/6B'), 1)
    }

    ## morale
    if(monsters$ML[i]==''){
      monsters$ML[i] <- sample(4:12, 1)
    }

    ## n_appearing, n_lair
    if(monsters$n_Appearing[i]==''){
      # just pick an existing combo
      roll <- sample(1:nrow(monsters[monsters$n_Appearing!='',]), 1)
      monsters$n_Appearing[i] <- monsters[monsters$n_Appearing!='','n_Appearing'][roll]
      monsters$n_Lair[i] <- monsters[monsters$n_Appearing!='','n_Lair'][roll]
    }    

    ## lair nearby
    if(monsters$Lair_near[i]==''){
      monsters$Lair_near[i] <- sample(c(1,2,3,4,5,6,7,8,9,10), 1)*5
    } 
    if(monsters$n_Lair[i]=='n/a'){
      monsters$Lair_near[i] <- 'n/a'
    }

    ## treasure
    if(monsters$Treasure[i]==''){
      monsters$Treasure[i] <- sample(treasure_tables$Type, 1)
    } 

    ## biomes
    biomelist <- c("Alpine", "Arid", "Savannah", "Chaparral", "Steppe", "Jungle", "Taiga", "Arratu", "Urban", "Necrozone", "Sea", "Limnetic", "Profundal", "Abyssal")
    if (sum(as.integer(monsters[i,biomelist]))<1|sum(is.na(monsters[i,biomelist]))){
      for (biome in biomelist){
        if (biome=='Urban'){
          monsters[i, biome] <- min(sample(0:1, 1), sample(0:1, 1), sample(0:1, 1))
        } else {
          monsters[i, biome] <- min(sample(0:3, 1), sample(0:3, 1))
        }

      }
    }
  }

}

write.csv(monsters, file.path(chapterdir, 'Bestiary.csv'))

############################ monster generation (description)

for (i in 1:nrow(monsters)){
  if (monsters$r_desc[i] == 1){
    outfile <- file.path(chapterdir, 'Bestiary_desc', paste0(monsters$Name[i], '.tex'))
    cat('\n', file=outfile, append=T)

    if (grepl("^\\s*$", read_lines(file.path(chapterdir, 'Bestiary_desc', paste0(monsters$Name[i], '.tex')))[1])){
      outfile <- file.path(chapterdir, 'Bestiary_desc', paste0(monsters$Name[i], '.tex'))
      monst_desc <- r_gen_eval('[Monster_spark], [Monster_spark], [Monster_spark]')

      cat(monst_desc, file=outfile, append=T)
    }
  }
}

Leave a comment on Reddit


#DIY #antibor #bestiary #lore24