<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Blog</title><link>/tools/</link><description>Recent content on Blog</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Wed, 01 Jul 2026 13:09:50 -0600</lastBuildDate><atom:link href="/tools/index.xml" rel="self" type="application/rss+xml"/><item><title>URL Adjust</title><link>/tools/url-adjust/</link><pubDate>Wed, 01 Jul 2026 13:09:50 -0600</pubDate><guid>/tools/url-adjust/</guid><description>&lt;p&gt;Sometimes adjusting URLs sucks.&lt;/p&gt;
&lt;p&gt;Rather opinionated as well.&lt;/p&gt;
&lt;form onsubmit="return false;"&gt;
 &lt;input type="text" id="url" placeholder="https://example.com"&gt;
 &lt;input type="submit" id="btnSubmit" value="Submit"&gt;
 
 &lt;button onclick="copyFunction()"&gt;Copy Text&lt;/button&gt;
 
&lt;/form&gt;
&lt;p&gt;&lt;input type="radio" id=markdown name="formatStyle" value=markdown checked &gt;
&lt;label for=markdown&gt;Markdown&lt;/label&gt;


&lt;input type="radio" id=html name="formatStyle" value=html &gt;
&lt;label for=html&gt;Html&lt;/label&gt;


&lt;input type="radio" id=raw name="formatStyle" value=raw &gt;
&lt;label for=raw&gt;Raw&lt;/label&gt;

&lt;/p&gt;
&lt;input type="checkbox" id=strip-params name=strip-params value="true" checked="true" &gt;
&lt;label for=strip-params&gt;Strip Params&lt;/label&gt;
&lt;pre&gt;&lt;code&gt;&lt;div id=output &gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;


&lt;script&gt;
 
 function copyFunction() {
 var copyText = document.getElementById("output");
 navigator.clipboard.writeText(copyText.innerText);
 }

 function stripParams(url) {
 const result = url.split('?')[0];
 return result;
 }

 function formatMarkdown(url) {
 let linkText = url.replace(/\:\/\/www./i, "\:\/\/")
 .replace(/https\:\/\/|http\:\/\//i, "")
 .replace(/reddit.com|old.reddit.com/i, "");
 linkText = stripParams(linkText);
 // [example.com](https://example.com)
 let returnValue = "[" + (linkText) + "](" + (url) + ")";
 document.getElementById("output").innerHTML = returnValue;
 }

 function formatHTML(url) {
 let linkText = url.replace(/\:\/\/www./i, "\:\/\/")
 .replace(/https\:\/\/|http\:\/\//i, "")
 .replace(/reddit.com|old.reddit.com/i, "");
 linkText = stripParams(linkText);
 // &lt;a href="https://www.example.com/"&gt;example.com&lt;/a&gt; 
 let returnValue = "&amp;lt;a href=\"" + url + "\"&amp;gt;" + linkText + "&amp;lt;/a&amp;gt;";
 document.getElementById("output").innerHTML = returnValue;
 }

 function formatRaw(url) {
 let returnValue = url;
 document.getElementById("output").innerHTML = returnValue;
 }

 function formatSelection() {
 let formatSelect;
 const radioButtons = document.querySelectorAll('input[name="formatStyle"]');
 for (const radioButton of radioButtons) {
 if (radioButton.checked) {
 formatSelect = radioButton.value;
 break;
 }
 }
 let url = document.getElementById('url').value;
 if (url == "") {
 return;
 }
 if (document.getElementById('strip-params').checked) {
 url = stripParams(url)
 }
 if ( formatSelect === "markdown"){
 formatMarkdown(url);
 }
 if ( formatSelect === "html"){
 formatHTML(url);
 }
 if ( formatSelect === "raw"){
 formatRaw(url);
 }
 }

 let urlForm = document.getElementById('url');
 urlForm.addEventListener("keyup", (event) =&gt; {
 if (event.code !== 'Enter' &amp;&amp; event.code !== 'NumpadEnter')
 {
 return;
 }
 formatSelection();
 });

 let buttonSubmit = document.getElementById('btnSubmit');
 buttonSubmit.addEventListener("click", (event) =&gt; {
 formatSelection();
 });

&lt;/script&gt;</description></item><item><title>Town Names</title><link>/tools/town-names/</link><pubDate>Wed, 01 Jul 2026 13:08:45 -0600</pubDate><guid>/tools/town-names/</guid><description>&lt;p&gt;&lt;div id=output &gt;&lt;/div&gt;


&lt;script&gt;
 
 const prefixes = ["Ahn\'", "Ains", "Al", "Alb", "Ald", "Alla", "Alt", "Alta", "Amar", "Amber", "Amyr", "Ander", "Anti", "Apple", "Ar", "Arbor", "Ard", "Arrow", "Ash", "Ashen", "Asta", "Auburn", "Aura", "Aus", "Autumn", "Ava", "Avon", "Back", "Bad", "Bain", "Bal", "Bal\'", "Bald", "Baldr", "Baldur", "Bane", "Baron", "Barron", "Barrow", "Basalt", "Beach", "Bear", "Bedd", "Beech", "Bell", "Beetle", "Bele", "Beryl", "Big", "Birch", "Bitter", "Black", "Blade", "Blue", "Bog", "Bone", "Border", "Boulder", "Brav", "Break", "Breeze", "Briar", "Bridge", "Brier", "Bright", "Brim", "Bronze", "Buck", "Bug", "Burn", "By", "Cael", "Caer", "Carn", "Cassel", "Caster", "Castle", "Cedar", "Chamber", "Cherry", "Cheyd", "Chill", "Cinder", "Clam", "Clay", "Cloud", "Cobble", "Cold", "Con", "Condor", "Corner", "Crab", "Crag", "Craw", "Crow", "Crown", "Crystal", "Cumber", "Dag", "Dagger", "Dark", "Dart", "Dawn", "Daven", "Day", "Dead", "Deep", "Demon", "Destin", "Devil", "Dire", "Do\'", "Dorn", "Doth", "Dragon", "Drake", "Draken", "Dread", "Dreth", "Dro", "Dun", "Dusk", "Dust", "Eagle", "East", "Echo", "Ebon", "Eden", "Edge", "Edin", "Elder", "Elm", "Ember", "Emer", "Ess", "Ever", "Ex", "Fae", "Fair", "Faire", "Fal", "Falcon", "Fall", "Fate", "Fel", "Fell", "Fey", "Fir", "Fire", "Flea", "Flint", "Flood", "Fog", "For", "Fort", "Fow", "Fowl", "Franken", "Free", "Full", "Fyr", "Gan", "Gar", "Gat", "Gay", "Gem", "Giant", "Gib", "Glib", "Glow", "Gnoll", "Goblin", "Gold", "Good", "Grand", "Granite", "Grass", "Grave", "Gray", "Green", "Grey", "Griffon", "Gryphon", "Guild", "Gun", "Half", "Hali", "Hallow", "Hammer", "Han", "Hard", "Haver", "Hawk", "Heart", "Hearth", "Heath", "Hedge", "Hei", "Hem", "High", "Hill", "Hollow", "Home", "Honey", "Hope", "Horn", "Iron", "Jade", "Jasper", "Jour", "K\'", "Karth", "Ken", "Kent", "Key", "King", "Kirk", "Kol", "Kraig", "Ky", "Kyn", "Lady", "Lake", "Lang", "Larch", "Law", "Leaf", "Ledge", "Len", "Ley", "Lime", "Lind", "Lion", "Little", "Liver", "Loch", "Lone", "Long", "Love", "Low", "Lower", "Lycan", "Lyre", "Lys", "Lyst", "Maiden", "Main", "Mala", "Man", "Mans", "Maple", "Marble", "Marsh", "Mason", "Mass", "May", "Mead", "Meadow", "Meksic", "Mer", "Mercy", "Mid", "Middle", "Mine", "Mir", "Mist", "Mithril", "Moll", "Moon", "Mor", "Mord", "Morrowind", "Moss", "Mourn", "Mud", "Muddle", "Murk", "Myr", "Myst", "Mythril", "Nan", "Nettle", "Never", "New", "Night", "Noble", "Nor", "Nord", "North", "Noth", "Oak", "Oat", "Old", "Onyx", "Opal", "Ord", "Pale", "Para", "Peace", "Pen", "Pender", "Penter", "Phal", "Phyr", "Pike", "Pine", "Plum", "Point", "Pond", "Port", "Pride", "Pyp", "Pyre", "Queen", "Quiet", "Rain", "Ram", "Rat", "Rath", "Raven", "Ray", "Red", "Ren", "Rend", "Reef", "Rex", "Rich", "Ridge", "Ring", "River", "Ro", "Rock", "Rose", "Ruby", "Rune", "Sack", "Saf", "Sage", "Sand", "Saph", "Satyr", "Scan", "Scholo", "Schu", "Sea", "Sedge", "Shade", "Shae", "Shan", "Sharp", "Sharpe", "Shatter", "Shaw", "Shay", "Sheff", "Shell", "Sher", "Shield", "Shire", "Shore", "Short", "Sieg", "Silver", "Skin", "Skull", "Sky", "Slade", "Sneak", "Snow", "Solsth", "Sorrow", "Sound", "South", "Spine", "Spring", "Stan", "Star", "Stark", "Still", "Stock", "Stoke", "Stone", "Stony", "Stor", "Storm", "Stout", "Strath", "Strom", "Summer", "Sun", "Sunder", "Sunny", "Sword", "Tangle", "Teak", "Tel", "Tel\'", "Ten", "Than", "Temple", "Timber", "Toads", "Tol", "Topaz", "Tor", "Torch", "Tower", "Tree", "True", "Tumble", "Twin", "Two", "Tyr", "Ult", "Umber", "Umbur", "Un", "Under", "Up", "Upper", "Val", "Van", "Vassel", "Vax", "Vec", "Ven", "Venom", "Vex", "Var", "Vir", "Ver", "Vor", "Vur", "Vyr", "Wain", "Wake", "Wall", "Walt", "War", "Water", "Wave", "Waver", "Way", "Wedge", "Wend", "West", "Wet", "Wheat", "Whet", "Whisper", "Whit", "White", "Wild", "Will", "Willow", "Win", "Wind", "Winter", "Wolf", "Wolfen", "Wood", "Wrath", "Wren", "Wyn", "Yew", "York", "Zan", "Zel", "Zen", "Zeno", "Zeph", "Zul", "Zwei"] 
 const suffixes = ["\'s Arc", "\'s Arch", "\'s Blade", "\'s Call", "\'s Castle", "\'s Cross", "\'s Crossing", "\'s Dawn", "\'s Demise", "\'s Descent", "\'s Embrace", "\'s Encampment", "\'s End", "\'s Fall", "\'s Fate", "\'s Fief", "\'s Fire", "\'s Fist", "\'s Fort", "\'s Fortress", "\'s Ghost", "\'s Glory", "\'s Harbour", "\'s Hill", "\'s Hunt", "\'s Hope", "\'s Inlet", "\'s Isle", "\'s Junction", "\'s Lake", "\'s Last Stand", "\'s Mill", "\'s Order", "\'s Path", "\'s Peak", "\'s Rapids", "\'s Resolve", "\'s Rest", "\'s River", "\'s Road", "\'s Rock", "\'s Settlement", "\'s Stand", "\'s Stop", "\'s Strength", "\'s Stronghold", "\'s Summit", "\'s Tear", "\'s Vanguard", "\'s Village", "\'s Watch", "a", "ace", "adia", "adora", "age", "alia", "ance", "ard", "arg", "aria", "ark", "atos", "axia", "baal", "back", "bach", "ball", "bank", "badd", "bar", "bard", "bark", "baron", "barron", "bay", "beach", "beck", "bell", "ben", "bend", "berg", "berry", "bert", "berth", "bia", "black", "blanc", "blight", "blood", "bluff", "blum", "bob", "bog", "bolg", "border", "borg", "borough", "bourg", "bourne", "bottom", "brad", "brake", "branch", "brand", "breach", "breeze", "bridge", "brook", "burg", "burn", "bury", "burst", "by", "call", "camp", "cant", "cast", "chester", "cer", "city", "cleef", "cleft", "cor", "corus", "cost", "cott", "court", "cove", "crag", "creek", "cress", "crest", "cross", "crown", "cut", "dag", "dain", "\'dal", "dale", "dance", "dane", "dark", "day", "deep", "del", "dell", "den", "dera", "desa", "dessia", "di", "dick", "digm", "dike", "do", "donis", "dorf", "drake", "drassil", "drop", "dune", "edge", "efia", "emar", "emir", "end", "ender", "erea", "eria", "eros", "esius", "fair", "faire", "fal", "fall", "falls", "fang", "fast", "fax", "fear", "fel", "feldt", "fell", "felt", "field", "fire", "fish", "flame", "flank", "flare", "flint", "fly", "font", "ford", "forge", "forks", "forth", "fried", "frost", "full", "furt", "gal", "gard", "garde", "garden", "gardr", "gart", "gash", "gast", "gate", "geist", "gem", "ger", "gill", "glen", "glow", "gon", "gonish", "gorash", "gorath", "grad", "grade", "grasp", "grass", "grave", "grip", "grila", "grove", "guard", "guild", "hagen", "hall", "halla", "hallow", "halt", "ham", "hand", "haunt", "haven", "head", "heart", "heed", "heim", "helm", "hen", "hill", "hold", "hollow", "holm", "holme", "holt", "home", "hood", "hope", "horse", "hunt", "ia", "iacke", "ice", "ich", "ilton", "inhal", "inger", "ington", "ion", "ish", "ister", "ivar", "ival", "jorn", "joy", "jyl", "kaar", "kay", "keel", "keep", "kill", "kol", "kraig", "l", "la", "lach", "lain", "lake", "lan", "lance", "land", "lark", "lashar", "law", "leaf", "leap", "ledge", "leg", "lest", "leth", "letter", "ley", "light", "line", "link", "loch", "lock", "lode", "lodge", "loft", "lon", "lorn", "maal", "macher", "maiden", "mal", "man", "mance", "mane", "march", "marsh", "mask", "mass", "maul", "maw", "mead", "mere", "mesa", "mile", "mill", "mine", "mira", "mol", "mole", "mond", "mont", "moon", "moor", "moot", "mora", "more", "moria", "morra", "moth", "mound", "mount", "mouth", "muhd", "mund", "murk", "na", "nar", "nathy", "naught", "ness", "nest", "night", "nite", "nook", "noon", "noth", "nova", "obia", "oe", "oland", "on", "ontay", "oon", "othos", "ourn", "over", "pad", "pain", "park", "pass", "path", "payne", "peak", "pere", "phere", "perch", "pier", "pike", "pine", "pines", "place", "point", "pond", "pool", "poole", "port", "quarry", "quil", "quill", "quin", "quinn", "r", "rain", "rast", "ray", "reach", "reath", "reign", "rest", "ridge", "right", "ring", "river", "roar", "rock", "ron", "roost", "ros", "rose", "rot", "roth", "rott", "row", "rowe", "run", "rune", "rust", "ruun", "s", "sand", "scale", "sfall", "shadow", "sharp", "sharpe", "shaw", "shield", "shine", "ship", "shire", "shore", "side", "smite", "smith", "snow", "sol", "son", "sor", "sound", "source", "spear", "spire", "spite", "sport", "spot", "square", "stable", "stad", "stain", "stane", "star", "stead", "sted", "steed", "stein", "steppe", "stock", "stol", "stoll", "stop", "ston", "stone", "stow", "strand", "strike", "summit", "sword", "sworth", "t", "tarn", "tay", "tear", "tei", "tennia", "theon", "thor", "thorn", "thorne", "thorpe", "throw", "ton", "top", "tower", "town", "trail", "tucket", "tun", "tung", "ulthor", "ulton", "umbra", "val", "vale", "valley", "valt", "vault", "veldt", "view", "vila", "ville", "wake", "walk", "wall", "wallow", "ward", "warden", "wash", "watch", "water", "way", "weald", "well", "wharf", "wheel", "wich", "wick", "wild", "wile", "will", "wind", "wing", "wock", "wolf", "wolfe", "woll", "wood", "wool", "worth", "wright", "wyn", "yllis", "york", "zieg", "zen", "zor"] 

function generateTownName() {
 const prefix = prefixes[Math.floor(Math.random() * prefixes.length)];
 const suffix = suffixes[Math.floor(Math.random() * suffixes.length)];
 return `${prefix}${suffix}`;
}

document.getElementById("output").innerHTML = generateTownName();

&lt;/script&gt;

&lt;/p&gt;</description></item><item><title>Random IP</title><link>/tools/random-ip/</link><pubDate>Wed, 01 Jul 2026 13:06:00 -0600</pubDate><guid>/tools/random-ip/</guid><description>&lt;p&gt;Generate a random IP address with a subnet mask from 0 to 33.
After doing the subnet math, click the link to check your work.&lt;/p&gt;
&lt;p&gt;&lt;div id=output &gt;&lt;/div&gt;


&lt;script&gt;
 
 function generateRandomIP() {
 const octets = Array.from({ length: 4 }, () =&gt; Math.floor(Math.random() * 256));
 const ip = octets.join(".");
 const cidr = Math.floor(Math.random() * 33);
 // https://www.calculator.net/ip-subnet-calculator.html?cclass=any&amp;csubnet=${cidr}&amp;cip=${ip}&amp;ctype=ipv4&amp;printit=0&amp;x=94&amp;y=15
 return `&lt;a href="https://www.calculator.net/ip-subnet-calculator.html?cclass=any&amp;csubnet=${cidr}&amp;cip=${ip}&amp;ctype=ipv4&amp;printit=0&amp;x=94&amp;y=15"&gt;${ip}/${cidr}&lt;/a&gt;`;
 }

 document.getElementById("output").innerHTML = generateRandomIP();

&lt;/script&gt;

&lt;/p&gt;</description></item><item><title>Mythic Fate</title><link>/tools/mythic-fate/</link><pubDate>Wed, 01 Jul 2026 11:21:12 -0600</pubDate><guid>/tools/mythic-fate/</guid><description>&lt;p&gt;&lt;div id=output &gt;&lt;/div&gt;
&lt;div id=randomNumber &gt;&lt;/div&gt;&lt;/p&gt;


&lt;script&gt;
 
 // Initially we just want to assume a 50/50
 // Don't really need chaos, assuming a chaos of 5

 const fateOdds = [
 ["Impossible", 1, 5, 82],
 ["No way", 3, 15, 84],
 ["Very unlikely", 5, 25, 86],
 ["Unlikely", 7, 35, 88],
 ["50/50", 10, 50, 91],
 ["Somewhat likely", 13, 65, 94],
 ["Likely", 15, 75, 96],
 ["Very likely", 16, 85, 97],
 ["Near sure thing", 18, 90, 99],
 ["A sure thing", 18, 90, 99],
 ["Has to be", 19, 95, 100]
 ];

 function generateRandomNumber() {
 const randNumber = Math.floor(Math.random() * 100);
 return `${randNumber}`;
 }

 function yesno(value, odds) {
 if (value &lt;= odds[1]) return `Exceptional Yes`
 if (value &lt;= odds[2]) return `Yes`;
 if (value &lt;= odds[3]) return `No`;
 return `Exceptional No`;
 }

 function resultLoop(value, odds) {
 let resultStr = "&lt;ul&gt;";

 for (const element of odds) {
 let tempStr = `&lt;li&gt;&lt;strong&gt;${element[0]}:&lt;/strong&gt; ${yesno(value, element)}&lt;/li&gt;`
 resultStr += tempStr + '\n';
 }
 resultStr += '&lt;/ul&gt;'
 return resultStr;
 }

 const generatedNumber = generateRandomNumber();

 document.getElementById("randomNumber").innerHTML = `&lt;strong&gt;Rolled number:&lt;/strong&gt; ${generatedNumber}`;
 document.getElementById("output").innerHTML = resultLoop(generatedNumber, fateOdds);

&lt;/script&gt;</description></item><item><title>Helm Repo Parse</title><link>/tools/helm-repo-parse/</link><pubDate>Wed, 01 Jul 2026 11:01:58 -0600</pubDate><guid>/tools/helm-repo-parse/</guid><description>&lt;form onsubmit="return false;"&gt;
 &lt;input type="text" id="url" placeholder="https://example.com"&gt;
 &lt;input type="submit" id="btnSubmit" value="Submit"&gt;
 
&lt;/form&gt;
&lt;table id="helmTable"&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Name&lt;/th&gt;
 &lt;th&gt;Chart Version&lt;/th&gt;
 &lt;th&gt;App Version&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;&lt;/tbody&gt;
&lt;/table&gt;
&lt;script type="text/javascript" src=https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js&gt;&lt;/script&gt;
&lt;p&gt;&lt;div id=output &gt;&lt;/div&gt;


&lt;script&gt;
 
 let urlForm = document.getElementById('url');
 urlForm.addEventListener("keyup", (event) =&gt; {
 if (event.code !== 'Enter' &amp;&amp; event.code !== 'NumpadEnter')
 {
 return;
 }
 parseYaml();
 });

 let buttonSubmit = document.getElementById('btnSubmit');
 buttonSubmit.addEventListener("click", (event) =&gt; {
 parseYaml();
 });
 function parseYaml() {
 const urlInput = document.getElementById('url');
 let urlValue = urlInput.value.trim();

 if (urlValue === "") {
 return;
 }

 // Logic: Append /index.yaml if it's missing
 if (!urlValue.endsWith('/index.yaml')) {
 // Ensure there's a slash between the base URL and index.yaml
 urlValue = urlValue.replace(/\/$/, "") + '/index.yaml';
 }

 fetch(urlValue)
 .then(r =&gt; {
 if (!r.ok) throw new Error("Network response was not ok");
 return r.text();
 })
 .then(r =&gt; {
 const yaml = jsyaml.load(r);
 const tableBody = document.querySelector('#helmTable tbody');
 
 // Clear existing rows before adding new ones
 tableBody.innerHTML = "";

 for (let entry in yaml['entries']) {
 // Helm index files usually store the latest version at index 0
 let temp = yaml['entries'][entry][0];
 let row = tableBody.insertRow();
 row.insertCell().textContent = temp.name || '';
 row.insertCell().textContent = temp.version || '';
 row.insertCell().textContent = temp.appVersion || '';
 row.insertCell().textContent = temp.description || '';
 }
 })
 .catch(err =&gt; {
 console.error(err);
 alert("Failed to fetch or parse the YAML file. Check the console for details.");
 });
 }

&lt;/script&gt;

&lt;/p&gt;</description></item><item><title>Hackmaster Event Table</title><link>/tools/hackmaster-event-table/</link><pubDate>Wed, 01 Jul 2026 11:00:41 -0600</pubDate><guid>/tools/hackmaster-event-table/</guid><description>&lt;p&gt;&lt;div id=output &gt;&lt;/div&gt;


&lt;script&gt;
 
const eventTable = ["Spell dissolves in harmless puff of smoke",
"Ears turn color - Temporarily",
"Ears turn color - Permanently",
"Nost turns color - Temporarily",
"Nose turns color - Permanently",
"Neck turns color - Temporarily",
"Neck turns color - Permanently",
"Hands turn color - Temporarily",
"Hands turn color - Permanently",
"Eyes changes color temporarily",
"Hair changes color temporarily",
"Skin changes color temporarily",
"Biting fingernails",
"Hair grows 1d4 feet in one round",
"Chews own hair",
"Burst of soot in face",
"Affected by random cantrip",
"Becomes chronic nagger",
"Skin complaint (unpleasant rash) temporarily",
"Skin complaint (unpleasant rash) permanently",
"Suffer 1 point of damage",
"1 random memorized spell goes off",
"Choke for 1d4 rounds",
"Temporary rash (-1 dex)",
"Suffer 1d4-2 points of damage",
"Lose sense of touch in fingers temporarily",
"Arm goes numb temporarily (50% left, 50% right)",
"Leg goes numb temporarily (50% left, 50% right)",
"Constantly gasping for air (slows speech by half, doubles casting times)",
"Personal cloudburst",
"2 random memorized spells go off simultaneously",
"Suffer 1d4 points of damage",
"Temporary loss of one spell slot",
"Suffer 1d6+1 points of damage",
"Spell dissolves in minor explosion: 1d6-2 points of damage in 5-foot radius",
"Temporary loss of two spell slots",
"Cannot memorize that spell again - temporarily",
"Eyes change color permanently",
"Hair changes color permanently",
"Skin changes color permanently",
"Skin changes color permanently",
"Polymorphed to Amphibian - temporarily",
"Skin covered with large blotches (-3 charisma) - Temporarily",
"Skin covered with large blotches (-3 charisma) - Permanently",
"Now talks to self",
"Fingernails change color temporarily",
"Fingernails change color permanently",
"Tingling in fingers (+25% chance of spell mishap for somatic components) - temporarily",
"Temporary ringing in ears",
"Narcissism",
"Contracts the flu",
"Becomes convinced he is a clone of his original self",
"Tinnitus - Permanent ringing in ears",
"Vision blurred (reduced 50%) temporarily",
"Amnesia back 1 day",
"Amnesia back 2 days",
"Enlarge random object (as spell)",
"Reduce random object (as spell)",
"Enlarge self",
"Reduce self",
"Roll once on PHB Table 6F Minor mental quirks - quirk is temporary",
"Roll once on PHB table 6F Minor mental quirks - quirk is permanent",
"Sibling (or parent) rolls once on PHB Table 6F Minor mental quirks - quirk is temporary",
"Sibling (or parent) rolls once on PHB table 6F Minor mental quirks - quirk is permanent",
"Roll once on PHB Table 6G Major mental quirks - quirk is temporary",
"Roll once on PHB table 6G Major mental quirks - quirk is permanent",
"Sibling (or parent) rolls once on PHB Table 6G Major mental quirks - quirk is temporary",
"Sibling (or parent) rolls once on PHB table 6G Major mental quirks - quirk is permanent",
"Roll once on PHB Table 6H Minor Personality quirks - quirk is temporary",
"Roll once on PHB table 6H Minor Personality quirks - quirk is permanent",
"Sibling (or parent) rolls once on PHB Table 6H Minor Personality quirks - quirk is temporary",
"Sibling (or parent) rolls once on PHB table 6H Minor Personality quirks - quirk is permanent",
"Roll once on PHB Table 6I Major Personality quirks - quirk is temporary",
"Roll once on PHB table 6I Major Personality quirks - quirk is permanent",
"Sibling (or parent) rolls once on PHB Table 6I Major Personality quirks - quirk is temporary",
"Sibling (or parent) rolls once on PHB table 6I Major Personality quirks - quirk is permanent",
"Wandering eye - temporary",
"Gain 1 alignment infraction point",
"Blinks (as per the spell)",
"Unquenchable thirst - temporary",
"Entire body glows as per Light spell",
"Continual Light spell on tongue",
"Gain 2 alignment infraction points",
"Temporarily emit unpleasant odor (-1 to dexterity rolls)",
"Temporarily emit vile odor (-3 to dexterity rolls)",
"Sibling (or parent) temporarily emits unpleasant odor (-1 to dexterity rolls)",
"Sibling (or parent) temporarily emits vile odor (-3 to dexterity rolls)",
"Must memorize all spells as if they were one level higher than actual",
"Temporary compulsion to become a mime",
"Teleport 5 feet in random direction",
"Teleport 10 feet in random direction",
"Teleport 50 feet in random direction",
"Wandering eye - permanent",
"Cannot memorize that spell again - permanently",
"Becomes center of Stinking Cloud spell",
"Roll once on PHB Table 6B minor Physical Flaws - flaw is temporary",
"Roll once on PHB Table 6B minor Physical Flaws - flaw is permanent",
"Roll once on PHB Table 6C minor Physical Flaws - flaw is temporary",
"Roll once on PHB Table 6C minor Physical Flaws - flaw is permanent",
"Roll once on PHB Table 6D minor Physical Flaws - flaw is temporary",
"Roll once on PHB Table 6D minor Physical Flaws - flaw is permanent",
"Teleport 5 feet straight up",
"Teleport 10 feet straight up",
"Teleport 50 feet straight up",
"Needs 1 extra hour of sleep temporarily",
"Temporarily loses all tattoos",
"Permanently loses all tattoos",
"Shaking (-1 to hit, -1 to damage, +3 segments to casting times for spells with somatic components) - temporary",
"Needs 2 hours of extra sleep - temporarily",
"Conversations with self (thinks others respond) - temporary",
"Is convinced he has a long lost sibling",
"Polymorphed to Primate temporarily",
"Suffer 2d6 points of damage",
"Suffer permanent loss of 1 hit point",
"Constant thirst (must drink 3 times normal volume per day) permanently",
"Permanently emits unpleasant odor (-1 to dexterity rolls)",
"Permanently emits vile odor (-3 to dexterity rolls)",
"Roll once on PHB table 6E Major Physical flaws - flaw is temporary ('It'll grow back right?')",
"Roll once on PHB table 6E Major Physical flaws - flaw is permanent",
"Immediate alignment audit",
"Summon monsters (as MS 1 - hostile to caster or party members if applicable)",
"Summon monsters (as MS 2 - hostile to caster or party members if applicable)",
"Struck by lightning from above",
"Switch gender temporarily",
"Change races (1 dwarf, 2 elf, 3 half-elf, 4 halfling, 5 half-ogre, 6 half-orc, 7 human, 8 gnome, 9 gnomeling, 10 pixie fairy) temporarily",
"Shaking (-1 to hit, -1 to damage, +3 segments to casting times for spells with somatic components) - permanent",
"Uncontrollable falling down at random (1d100 minutes) intervals - temporary",
"Uncontrollable falling down at random (1d100 minutes) intervals - permanent",
"Enters HackFrenzy immediately (once, roll 2d20 for 'effective' points of damage)",
"Enters HackLust immediately (once, roll 2d20 for 'effective' points of damage)",
"Becomes misanthrope temporarily",
"Lose one talent temporarily",
"Lose two talents temporarily",
"Suffer 3d6 points of damage",
"Summon monsters (as MS 3 - hostile to caster or party members if applicable)",
"Lower temperature 10 degrees in 5 foot radius",
"Lower temperature 25 degrees in 5 foot radius",
"Lower temperature 50 degrees in 5 foot radius",
"Raise temperature 10 degrees in 5 foot radius",
"Raise temperature 25 degrees in 5 foot radius",
"Raise temperature 50 degrees in 5 foot radius",
"Summon monsters (as MS 4 - hostile to caster or party members if applicable)",
"Lose one class-specific ability temporarily",
"Lose two class-specific abilities temporarily",
"Sibling (or parent) contracts the flu",
"Sibling (or parent) contracts leprosy",
"Introversion",
"Suffer 3d10 points of damage",
"Suffer 2d10 points of damage",
"Slowed (as spell)",
"Hasted (as spell)",
"Temporary -1 to hit",
"Temporary -1 to damage rolls",
"Hatred of one gender",
"Temporary -2 to hit",
"Temporary -1 to all rolls",
"Summon monsters (as MS 5 - hostile to caster or party members if applicable)",
"Suffer permanent loss of 2 hit points",
"Switch gender permanently",
"Sibling (or parent) contracts malaria",
"Permanent compulsion to become a mime",
"Temporary -2 to damage rolls",
"ages 1 day",
"temporary -2 to all rolls",
"Becomes center of FIreball",
"Ages 2d6 days",
"Ages 1d4 weeks",
"Ages 1d3 months",
"Ages 1d6 months",
"Ages 2d6 months",
"Ages 1 year",
"Ages 1d4 years",
"Ages 2d4 years",
"Ages 2d6 Years",
"Tingling in fingers (+25% chance of spell mishap for somatic components) - permanently",
"Permanent rash (-2 dex)",
"Lose ability to cast spells temporarily",
"Permanent -1 to all damage rolls",
"Permanent -1 to hit",
"Permanent -2 to damage rolls",
"Drug addiction (GM chooses substance)",
"Temporary -50 fractional points to random Ability Score",
"Temporary -1 to random ability score",
"Temporary -2 to random ability score",
"Temporary -50 fractional points to each fractional Ability score",
"Temporary -1 to each Ability score",
"Temporary -2 to each ability score",
"Permanent -2 to hit",
"All memorized spells go off simultaneously",
"Summon monsters (as MS 6 - hostile to caster or party members if applicable)",
"Summon monsters (as MS 7 - hostile to caster or party members if applicable)",
"Lose sense of touch in fingers permanently (x3 casting time, -4 to hit)",
"Arm goes permanently numb (50% left, 50% right), becoming useless",
"Leg goes permanently numb (50% left, 50% right), becoming useless",
"Permanent loss of one random spell",
"Permanent -1 to all rolls",
"Permanent loss of one spell slot",
"Aphasia (speaks random meaningless phrases instead of desired words)",
"Contracts leprosy",
"Contracts malaria",
"Permanent 02 to all rolls",
"Permanent loss of two spell slots",
"Alignment change: chaotic/ lawful axis 1 step (50% either direction)",
"Alignment change: Good/ Evil axis 1 step (50% either direction)",
"Becomes misanthrope permanently",
"Sibling (or parent) permanently emits unpleasant odor (-1 to dexterity rolls)",
"Sibling (or parent) permanently emits vile odor (-3 to dexterity rolls)",
"Needs 1 extra hour of sleep - permanently",
"Needs 2 extra hours of sleep - permanently",
"Conversations with self (things others respond) - permanent",
"Vision blurred (reduced 50%) - permanently",
"Lose one talent permanently",
"Lose two talents permanently",
"Suffer permanent loss of 1d6+1 hit points",
"Gain enmity of nefarion",
"Is Harmed (as spell)",
"Polymorphed to amphibian - permanently",
"Roll on insanity sub-table",
"Now hates one sibling (or parent)",
"Now hated by one sibling (or parent)",
"Change races (1 dwarf, 2 elf, 3 half-elf, 4 halfling, 5 half-ogre, 6 half-orc, 7 human, 8 gnome, 9 gnomeling, 10 pixie fairy) permanently",
"Becomes 10-40 years younger",
"Enervated - temporary loss of 1 experience level",
"Enervated - temporary loss of 2 experience levels",
"Amnesia back 1 week",
"Amnesia back 1 month",
"Permanent -1d20% to one skill (determine randomly)",
"Permanent -1d100% to one skill (determine randomly)",
"Suffer permanent loss of 1d4 hit points",
"Temporary dyslexia (x3 time to read anything, including spellbooks)",
"Temporary -1d20% to one skill (determine randomly)",
"Temporary -1d100% to one skill (determine randomly)",
"Temporary lethargy (-2 to hit, -2 to damage, double all initiative times, movement is halved)",
"Amnesia back 6 months",
"Amnesia back 1 year",
"Amnesia back 2 years",
"Amnesia back 5 years",
"Amnesia back 10 years",
"Decides he must switch classes",
"Temporarily unable to sleep",
"Uncontrollable weight gain - temporary (1 pound per week)",
"Temporary bulimia",
"Permanently unable to sleep",
"Permanent lethargy (-2 to hit, -2 to damage, double all initiative times, movement is halved)",
"Permanent -50 fractional points to random Ability Score",
"Permanent -1 to random ability score",
"Permanent -2 to random ability score",
"Spontaneous combustion! Bursts into flame and suffers 6d8 points of damage",
"Sibling (or parent) suffers uncontrollable weight gain - temporary (1 pound per week)",
"permanent -50 fractional points to each fractional Ability score",
"permanent -1 to each Ability score",
"permanent -2 to each ability score",
"Suffer permanent loss of 2d6 hit points",
"Anorexia (lose 1-2 pounds per week) [permanent until cured or death]",
"Polymorphed to Primate permanently",
"Permanent bulimia",
"Suffer permanent loss of 3d6 hit points",
"Permanent dyslexia (x3 casting time to read anything, including spell books)",
"Enters temporary coma",
"Lose one class-specific ability permanently",
"Lose two class-specific abilities permanently",
"Lose ability to cast spells permanently",
"All magic items on person are disjoined (as per Hyptor's Disjunction)",
"Enters permanent coma",
"Gains appearance of undead (-15 comeliness, -5 charisma) permanently",
"Energy drain lost 1 experience level",
"Energy Drain: lose 2 experience levels",
"Spontaneous combustion! bursts into flame and immediately dies",
"Chokes to death in 1 round",
"Roll twice more on this table (do NOT ignore this result if rolled again!)",];

function generateEvent() {
 const eventSelection = eventTable[Math.floor(Math.random() * eventTable.length)];
 return `&lt;strong&gt;Event:&lt;/strong&gt; ${eventSelection}`;
}

document.getElementById("output").innerHTML = generateEvent();

&lt;/script&gt;

&lt;/p&gt;</description></item><item><title>Basic Weather</title><link>/tools/basic-weather/</link><pubDate>Wed, 01 Jul 2026 10:59:17 -0600</pubDate><guid>/tools/basic-weather/</guid><description>&lt;p&gt;&lt;div id=output &gt;&lt;/div&gt;


&lt;script&gt;
 
const weatherTable = ["Sunny", "Cloudy", "Partly Cloudy", "Rainy", "Snowy", "Sleet", "Stormy", "Lightning", "Thunder", "Hail", "Windy", "Foggy", "Ice", "Tornado", "Rainbows", "Clear Sky"];

function generateWeather() {
 const weatherSelection = weatherTable[Math.floor(Math.random() * weatherTable.length)];
 return `&lt;strong&gt;Weather:&lt;/strong&gt; ${weatherSelection}`;
}

document.getElementById("output").innerHTML = generateWeather();

&lt;/script&gt;

&lt;/p&gt;</description></item><item><title>Action Theme</title><link>/tools/action-theme/</link><pubDate>Wed, 01 Jul 2026 10:48:25 -0600</pubDate><guid>/tools/action-theme/</guid><description>&lt;p&gt;&lt;div id=output &gt;&lt;/div&gt;


&lt;script&gt;
 
const actionTable = ["Scheme", "Clash", "Weaken", "Initiate", "Create", "Swear", "Avenge", "Guard", "Defeat", "Control", "Break", "Risk", "Surrender", "Inspect", "Raid", "Evade", "Assault", "Deflect", "Threaten", "Attack", "Leave", "Preserve", "Manipulate", "Remove", "Eliminate", "Withdraw", "Abandon", "Investigate", "Hold", "Focus", "Uncover", "Breach", "Aid", "Uphold", "Falter", "Suppress", "Hunt", "Share", "Destroy", "Avoid", "Reject", "Demand", "Explore", "Bolster", "Seize", "Mourn", "Reveal", "Gather", "Defy", "Transform", "Persevere", "Serve", "Begin", "Move", "Coordinate", "Resist", "Await", "Impress", "Take", "Oppose", "Capture", "Overwhelm", "Challenge", "Acquire", "Protect", "Finish", "Strengthen", "Restore", "Advance", "Command", "Refuse", "Find", "Deliver", "Hide", "Fortify", "Betray", "Secure", "Arrive", "Affect", "Change", "Defend", "Debate", "Support", "Follow", "Construct", "Locate", "Endure", "Release", "Lose", "Reduce", "Escalate", "Distract", "Journey", "Escort", "Learn", "Communicate", "Depart", "Search", "Charge", "Summon", ];
const themeTable = ["Risk","Ability","Price","Ally","Battle","Safety","Survival","Weapon","Wound","Shelter","Leader","Fear","Time","Duty","Secret","Innocence","Renown","Direction","Death","Honor","Labor","Solution","Tool","Balance","Love","Barrier","Creation","Decay","Trade","Bond","Hope","Superstition","Peace","Deception","History","World","Vow","Protection","Nature","Opinion","Burden","Vengeance","Opportunity","Faction","Danger","Corruption","Freedom","Debt","Hate","Possession","Stranger","Passage","Land","Creature","Disease","Advantage","Blood","Language","Rumor","Weakness","Greed","Family","Resource","Structure","Dream","Community","War","Portent","Prize","Destiny","Momentum","Power","Memory","Ruin","Mysticism","Rival","Problem","Idea","Revenge","Health","Fellowship","Enemy","Religion","Spirit","Fame","Desolation","Strength","Knowledge","Truth","Quest","Pride","Loss","Law","Path","Warning","Relationship","Wealth","Home","Strategy","Supply",];

function generateActionTheme() {
 const actionSelection = actionTable[Math.floor(Math.random() * actionTable.length)];
 const themeSelection = themeTable[Math.floor(Math.random() * themeTable.length)];
 return `&lt;strong&gt;Action:&lt;/strong&gt; ${actionSelection}&lt;br&gt; &lt;strong&gt;Theme:&lt;/strong&gt; ${themeSelection}`;
}

document.getElementById("output").innerHTML = generateActionTheme();

&lt;/script&gt;

&lt;/p&gt;</description></item><item><title>Advanced Weather</title><link>/tools/advanced-weather/</link><pubDate>Wed, 01 Jul 2026 10:48:25 -0600</pubDate><guid>/tools/advanced-weather/</guid><description>&lt;div id=output &gt;&lt;/div&gt;
&lt;p&gt;Random Starting weather, then we advance to weather that&amp;rsquo;s more likely to happen next via steps.&lt;/p&gt;


&lt;script&gt;
 
const weatherTable = ["Clear", "Partly Cloudy", "Mostly Cloudy", "Cloudy", "Precipitation", ];

function setCookie(cname, cvalue, exdays) {
 const d = new Date();
 d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
 let expires = "expires=" + d.toUTCString();
 if (typeof exdays !== "undefined") {
 document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/" + ";SameSite=Strict";
 } else {
 document.cookie = cname + "=" + cvalue + ";" + ";path=/" + ";SameSite=Strict";
 }
}

function getCookie(cname) {
 let name = cname + "=";
 let decodedCookie = decodeURIComponent(document.cookie);
 let ca = decodedCookie.split(';');
 for (let i = 0; i &lt; ca.length; i++) {
 let c = ca[i];
 while (c.charAt(0) == ' ') {
 c = c.substring(1);
 }
 if (c.indexOf(name) == 0) {
 return c.substring(name.length, c.length);
 }
 }
 return "";
}

function advanceWeather(weatherVar) {
 if (weatherVar &gt;= weatherTable.length - 1) {
 weatherVar = -1;
 }
 return ++weatherVar;
}

function generateCookieWeather() {
 let weather = Math.floor(Math.random() * weatherTable.length);
 if (getCookie("weather") != "") {
 weather = getCookie("weather");
 }
 const weatherSelection = weather;
 setCookie("weather", advanceWeather(weather));
 const severity = Math.floor(Math.random() * 8) + 1;
 return `&lt;strong&gt;Weather:&lt;/strong&gt; ${severity === 8 ? "Severe" : "" } ${weatherTable[weatherSelection]}`;
}

document.getElementById("output").innerHTML = generateCookieWeather();

&lt;/script&gt;</description></item></channel></rss>