This page looks best with JavaScript enabled

The Beauty of Classic D&D and NPC Generation

 ·  ☕ 4 min read

With Heroes of the Borderlands recently coming out, I’ve been hyper-fixated on reading it over and preparing the adventure for friends. With that preparation, I’ve been reading over the original Basic Set version of Keep on the Borderlands and In Search of the Unknown for further inspiration and amazement of what early tabletop RPGs were.

One of the things that’s been almost entirely removed from more modern adventures— outside of The Dragon of Icespire Peak’s sidekicks— was hiring henchmen. In the early days of D&D, most of that fantasy world was unforgiving. Going into a dungeon unprepared meant almost guaranteed defeat, and there were no death saving throws. It only made sense that as the treasures you accumulated grew, some of that wealth would need to be spent to upgrade yourself (obviously), but also spent on hiring people.

Page 27 of *In Search of the Unknown*. The page has two headings: one for 'Character Classes of Henchmen/Hirelings' and 'Non-Player Character Personality'

Page 27 of In Search of the Unknown

The idea of hiring henchmen wasn’t what really caught my interest though. With how things are balance nowadays, players don’t need that kind of assistance, especially as the heroes of the story. No, what stood out was to the right of that, Non-Player Character Personality. A nifty little means for quickly assigning the personality of a someone the players meets that I didn’t think about before (i.e. most of them). Assigning things like their Attitude and Disposition is particular can be such a good way of figuring whether or not the players would be lied to and why.

The only problem for me, a less experienced DM, was swiftly understanding what those tables gave me, so I tackled it like the more experienced geek that I am. I made a bash script that essentially takes a roll from each of these tables and prints it to the terminal with a color to communicate the help or trouble that that NPC might bring.

This one is super simple. I wrote it like a typical, procedural script: starting from the top, it generates a random number from 1-6 using the shuf command; then, it returns a personality based on that roll relative to what trait we were on. I added some gum to stylize the text in a css fashion, and now I’ve got a new way to get idea of the NPC right when my players start asking questions. The above is a JS implementation of the same of the same idea, and I have the code for both right here. ⬇️

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#!/usr/bin/env bash

# Define ANSI color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
PURPLE='\033[0;35m'
GRAY='\033[0;37m'
BOLD='\033[1m'
RESET='\033[0m'
characteristic='${BOLD}${title}:${RESET}'

# Style the words
Helpful="${GREEN}Helpful / Cooperative${RESET}"
Courageous="${GREEN}Courageous${RESET}"
Unselfish="${GREEN}Unselfish${RESET}"
Loyal="${GREEN}Loyal${RESET}"
Apathetic="${PURPLE}Apathetic / Lazy${RESET}"
Reckless="${PURPLE}Reckless / Daring${RESET}"
Unreliable="${GRAY}Unreliable${RESET}"
Normal="${GRAY}Normal${RESET}"
Argumentative="${RED}Obstinate / Argumentative / Domineering${RESET}"
Hesitant="${RED}Hesitant${RESET}"
Cowardly="${RED}Cowardly${RESET}"
Greedy="${RED}Greedy${RESET}"
Fickle="${RED}Fickle${RESET}"

rollNum=$(shuf -i 1-6 -n 1)
characteristic=$(echo '{{ Bold "Attitude:"}}' | gum format -t template)
case "$rollNum" in
  1|2|3)
    output=${Helpful} ;;
  4)
    output=${Apathetic} ;;
  5)
    output=${Unreliable} ;;
  6)
    output=${Argumentative} ;;
esac
echo -e "${characteristic} ${output}"

rollNum=$(shuf -i 1-6 -n 1)
case "$rollNum" in
  1)
    output=${Reckless} ;;
  2)
    output=${Courageous} ;;
  3|4)
    output=${Normal} ;;
  5)
    output=${Hesitant} ;;
  6)
    output=${Cowardly} ;;
esac
echo -e "${characteristic} ${output}"

rollNum=$(shuf -i 1-6 -n 1)
case "$rollNum" in
  1)
    output=${Greedy} ;;
  2|3|4|5)
    output=${Normal} ;;
  6)
    output=${Unselfish} ;;
esac
echo -e "${characteristic} ${output}"

rollNum=$(shuf -i 1-6 -n 1)
case "$rollNum" in
  1|2)
    output=${Loyal} ;;
  3|4)
    output=${Normal} ;;
  5|6)
    output=${Fickle} ;;
esac
echo -e "${characteristic} ${output}"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html>
<html>
<head>
    <style>
        body { margin: 0; font-family: Arial; background: #f0f0f0; }
        .npc-gen { width: 250px; padding: 10px; background: white; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
        .btn { background: #2d5c7f; color: white; border: none; padding: 8px; border-radius: 4px; cursor: pointer; width: 67%; margin: 5px 0; }
        .trait { margin: 6px 0; font-size: 12px; }
        .label { font-weight: bold; color: #333; }
        .green { color: #080; } .purple { color: #80f; } .gray { color: #666; } .red { color: #c00; }
    </style>
</head>
<body>
    <div class="npc-gen">
        <h4 style="margin:0 0 8px 0;">NPC Generator</h4>
        <button class="btn" onclick="generate()">Generate NPC</button>
        <div class="trait"><span class="label">Attitude:</span> <span id="a" class="gray">-</span></div>
        <div class="trait"><span class="label">Courage:</span> <span id="c" class="gray">-</span></div>
        <div class="trait"><span class="label">Disposition:</span> <span id="d" class="gray">-</span></div>
        <div class="trait"><span class="label">Loyalty:</span> <span id="l" class="gray">-</span></div>
    </div>
    <script>
        const t = {
            a: [['Helpful','green'],['Helpful','green'],['Helpful','green'],['Apathetic','purple'],['Unreliable','gray'],['Argumentative','red']],
            c: [['Reckless','purple'],['Courageous','green'],['Normal','gray'],['Normal','gray'],['Hesitant','red'],['Cowardly','red']],
            d: [['Greedy','red'],['Normal','gray'],['Normal','gray'],['Normal','gray'],['Normal','gray'],['Unselfish','green']],
            l: [['Loyal','green'],['Loyal','green'],['Normal','gray'],['Normal','gray'],['Fickle','red'],['Fickle','red']]
        };
        function generate() { 
            ['a','c','d','l'].forEach(id => { 
                let r = t[id][Math.floor(Math.random()*6)]; 
                document.getElementById(id).textContent = r[0]; 
                document.getElementById(id).className = r[1]; 
            }); 
        }
        generate();
    </script>
</body>
</html>
    ```

    
Share on

Zachary Burkey
WRITTEN BY
Zachary Burkey
Freelane Web Developer