From 81e11966696be67dae50646a4a7bb44d9f47eadd Mon Sep 17 00:00:00 2001 From: Kim ravn Hansen Date: Sun, 31 Aug 2025 10:08:06 +0200 Subject: [PATCH] stuff --- .gitignore | 2 + Makefile | 12 + ideas.adoc | 393 ++++++++++ main.adoc | 958 ++++++++++++++++++++++++ monsters.adoc | 187 +++++ readme.adoc | 14 + sheet.adoc | 37 + style.css | 1687 ++++++++++++++++++++++++++++++++++++++++++ tooling/mathD100.lua | 249 +++++++ tooling/mathD20.lua | 217 ++++++ tooling/process.lua | 68 ++ tooling/sorter.lua | 190 +++++ 12 files changed, 4014 insertions(+) create mode 100644 .gitignore create mode 100755 Makefile create mode 100755 ideas.adoc create mode 100755 main.adoc create mode 100755 monsters.adoc create mode 100755 readme.adoc create mode 100755 sheet.adoc create mode 100755 style.css create mode 100755 tooling/mathD100.lua create mode 100755 tooling/mathD20.lua create mode 100755 tooling/process.lua create mode 100755 tooling/sorter.lua diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1be530a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +out/* +.DS_* diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..fc77642 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +sort: + cd tooling && lua process.lua ../main.adoc --overwrite + +html: + asciidoctor -b html main.adoc -o out/main.html && open out/main.html + +pdf: + asciidoctor-pdf main.adoc -o out/main.pdf && open out/main.pdf + +d20: + echo "" > out/statsD20.csv + lua tooling/mathD20.lua > out/statsD20.csv && open out/statsD20.csv diff --git a/ideas.adoc b/ideas.adoc new file mode 100755 index 0000000..37215fa --- /dev/null +++ b/ideas.adoc @@ -0,0 +1,393 @@ += Ideas + +== MANIFESTO + +* Quick, easy, simple. +* Fun, laughter, satire, irreverent. + +=== THINGS WE WANT +* Fun, laughter, humor, satire, informal rules. +* Fast & easy to get into. +* Fast & smooth gameplay (not too much math, not many exceptions). +* Should feel a bit like a good, but very immersive, board game. +* Combat that is FUN, no matter the outcome. +* Teamwork, problem solving, resource anxiety. + + +=== THINGS WE DON'T WANT +* Arguing over technicalities. +* Spending game time looking at the rules. +* Internal strife or competition to be best or mow down the most monsters. +* Power gaming and min/maxing. +* People getting bored during combat. +* Undetectable, unperceivable, or unstoppable events that kill adventurers + without player input or agency. + +=== UNIMPORTANT +* "Good" roleplay. +* Balance. Not all characters should be equally "good" on the battlefield. + + +== IDEAS + +=== Adventurer's Blessing +* All Adventurers have The Adventurer's Blessing. +* They have a different eye color. +* They have access to racial perks (ancient blood that awakens as they gain levels). +* They don't get PTSD or any other mundane mental illnesses, get common colds + and other mundane diseases, they have very high pain tolerance, and they are + generally awesome. +* Their gift can burn out if they do evil stuff such as: +** Kill for fun or sadism, or killing the truly innocent, such as kids (100% + burnout). +** Kill beings that aren't monsters or villains (high risk of burnout). +** Harm beings that aren't monsters or villains (moderate risk of burnout). +** Steal from or swindle beings that aren't monsters or villains (low risk of + burnout). +** Stop actively adventuring (burnout risk increases over time). +** Become parents (100%) +** All these risks are cumulative. + + +=== MAX (for d20 system) and 80 (for d100 system) + +No matter how skilled you are, your target number can’t be higher than a +certain limit Any roll above that limit is an automatic failure. + +However, your skill level itself isn't capped. This matters when you score a +critical hit. For example, if your attack skill is 22, you'll still fail on +rolls of 16 to 20—but if you roll a 15 (a critical hit), your damage will be +22. + +=== ATTACK & DAMAGE + +Attacks always deal at least a bit of damage. + +* Glancing Blow (rolled above your TN) - deals damage equal to your weapon's Base + Damage, and no special effects can apply to the attack. +* Hit (rolled below your TN) Deals damage based on your roll plus the weapon's Base Damage. +** Hits MAY trigger ONE Hit Effect. +*** Some weapons have Hit Effects. +*** Most Hit Effects cost a number of Hit Points to trigger. +*** Many Hit Effects require that the attacker be of a certain level. +*** Only one Hit Effect may be triggered per Hit. +* Crit (roll equal to your TN) Deals damage equal to TWICE your SKILL LEVEL + plus weapon's Base Damage. +** Crits MAY trigger ANY available Hit Effects +*** The player chooses which Hit Effects to trigger. +*** Level Requirements are ignored (I think) +*** They will not drain any Hit Points. + +** When it comes to Hit Effects, Crits count as Hits, except for the fact that +certain classes/abilities have Crit Effects that only triggers when you Crit. +** Usually only one Effect applies - and the player gets to choose. + +Monster Crits:: Boss Monsters (and certain Skirmisher monsters) can crit too - +and they have random Crit Effects - each Crit-Capable monster has its own Crit +Table of nasty effects that can be triggered on a Crit. + + +=== MAGIC && SPELLS + +You use Wands to cast Spells. + +You roll your Magic skill. + +* Each spell should take up one page. +* Each spell should have a description of what happens when you succeed, + fizzle, and crit. Also, each WAND (not spell) should have a quality rating, + indicating the max target number available. Here are some PARTIAL EXAMPLES: +** Fizzle (roll above TN) - the spell fizzles, but you may try again next + round. +** Success (rolled below TN) - the spell takes effect -look up the effect in + the spell's description. Each spell has a table where you can look up the + effect, based on what you rolled. The wand is usually drained after + successfully casting a spell. +** Crit (roll equal TN) - The spell takes effect, but you get to choose the + effect; you may choose any effect tier up to, and including, one tier higher + than what your roll would normally grant. The wand is usually NOT drained + after a Crit. + + +==== SPELLS + +Fireballs:: +Causes insane pain in the testicles of the target. Can only target testicle-havers. +Higher effect tiers actually set fire to the balls. + +Revive Adventurer:: +* Only adventurers can be resurrected. +* They can be resurrected within 1 day of their death per adventurer level. +* When using a wand to resurrect an adventurer, you must always roll higher + than their level. +* There is a high chance that the wand can never be used again. +* There is a small risk that the adventurer will be turned into an intelligent + undead of a type that is determined by their stats, level, and some + randomness (they will typically rise as a revenant, but death knight, + vampire, and lich are also in play). +* There is a risk that the adventurer will gain some otherworldly traits that + have followed them from the other world. These traits can be demonic, + angelic, necromantic, sylvan, elemental, etc. + + +=== DEFENSE AND ARMOR + +==== ARMOR +When an adventurer dons armor, their max HP and current HP increases by a value +that depends on the value. When they doff the armor, their max- and current HP +is lowered by the same amount. + +[WARNING] +You can die (or at least reach Zero Hit Points) if you doff armor while close +to death. + + +==== ITEM SLOTS +Adventurers begin play with 4d4 Item Slots. Item Slots are used to carry stuff, +and they also grant you Hit Points. + +* Normal Items take up a single Item Slot. +* Large Items - like armors - take up more than one Item Slot. +* Small Items - like gold and gems - take up so little space that you can can + squeeze a number of them into one Item Slot. +* If you have unused Item Slots, you get extra Hit Points. You get one HP per + unused slot, multiplied by your level. For instance, a level 3 character with + 4 unused Item Slots gets 3*4=12 extra Hit Points. + +[WARNING] +You can die (or at least reach Zero Hit Points) if you are low on Hit Points +and pick up heavy stuff. + +=== DIFFICULTY (advantage/disadvantage) +Works just like DnD and all the others. + + +=== SKILLS (THERE ARE NO ATTRIBUTES) + +These are the base skills that all adventurers have to begin with, and as the +character progresses, they can attain new skills that can allow them to do more +and fancy stuff. + +[%header,cols="3,9"] +|=== +| Basic Skills | Description +//----------------------|------------------------------------------------------ +| Melee Attack | Attacks against baddies in melee range. +| Ranged Attack | Attacks against baddies outside melee range. +| Magic | Using wands to cast spells. +// | Skulduggery | Stealth & stuff. +// | Knowledge | History, geography, lore. +// | Grit | Toughness, raw strength, willpower. +// | Awareness | Perception, comprehension, alertness, insight. +//----------------------|------------------------------------------------------ +|=== + + + +==== Skill Burning + +D20 Version:: +You can burn one of your skills (permanently reducing its score by 1) before +you roll, and you can force the die roll to become whatever you want. If your +skill score is currently 13, you can burn one point, reducing it to 12, +but then you have the power to force the d20 to become whatever you want (in +this case a 12 would usually be the best option). + +You can also do this retroactively, after you've rolled, but you must burn +three skill points. + +D100 Version:: +Burn 5 skill points before you roll to force a roll to become whatever you want. +Burn 5 skill points after you've rolled to make a reroll. 10 skill points on your +next reroll, 15 on the next, and so on. +Burn 2 skill points before you roll to gain advantage. + + +=== ADVANCEMENT +Advancement incentivise fast and dangerous fun. +We want fun, fast, and toe-curlingly dangerous dungeon crawls. +We want lots silly and "active" downtime (i.e. all the players do fun stuff). + +Fast-Paced Dungeon Crawls:: +We award fast-paced dungeon crawls, so +* Each monster "defeated" (killed, pacified, or routed) gives XP. +* Each encounter has a "bonus time" — if you defeat the encounter within that + time, you get bonus XP. +** Each monster has a "bonus time" attribute - typically a number of minutes. +** An encounter's "bonus time" is calculated by adding up all "bonus time" + attributes. + +Fast-Paced Almost-Always-Successful Environmental Encounters:: +The PCs gain experience for _cleverly_ or _luckily_ overcoming environmental +challenges such as traps, cliffs, pits, and puzzles. + +All challenges should be solvable or even have an obvious/naive brute force +solution - but using the naive solution does not grant XP - you gain XP by +daring and by being clever. + +* If the PCs scale a wall by being clever (using their brains) or daring + (successful skill rolls), they gain XP. But even if they aren't clever or + lucky, the GM should consider letting them scale the wall anyway, simply by + brute forcing the problem. +* If the PCs cleverly disable or daringly circumvent a trap, they should get XP + - but the PCs could brute force the problem, but not gaining XP. **On that + note** - most traps + should be visible - invisible traps that deals automatic damage are not fun. +* If the PCs open a door by picking a lock, social engineering, or otherwise + being clever, they gain XP. But if they simply bash it to pieces, they don't + get XP. + +Fun and Silly Downtime:: +* PCs get lots XP for carousing. Especially if they get up to mischief - but + only if they do so as a party, and not individuals. +** Gambling is an option +** Prize fighting is an option. +* PCs should only level up during downtime. +* PCs can gain a bit of XP if they donate money to charity or invest in a + house, fortress, etc. + +Ad hoc Progress:: +* (d100) Maybe 1 XP could be converted directly into a +1 in a skill. Or maybe + it'll cost [current value / 10 rounded down] XP to increment a skill. +* (d20) Maybe it'll cost [current value] XP to increment a skill. + + + +=== PERKS/ABILITIES/STUFF + +Monstrous Gourmand:: If a dead monster has fresh meat on it, you can probably +eat it without issue. You can spend 1 hour preparing the meat, then you have 1 +daily ration of food. Large monsters can yield more than one ration. Be aware +that only you can eat those rations safely - normal people could get sick from +eating monster meat. + +Dwarf (level 1):: You have one extra item slot (maybe for a later dwarf perk). +You have a lot of facial hair, no matter what gender you are. Your tolerance +for alcohol is quite high. + +Assassinate (d100):: Works better for the d100 version of this game. Instead of +rolling "attack", you roll Skulduggery. If you hit, you deal the same amount of +damage as what you rolled on your d100, but if you fail, you miss completely, +dealing no damage, and no special effect. *NOTE:* Assassination should have its +own type of special effect, if any at all. +== Stats +* Skills - not attributes +* Item Slots +* Hit Points +* Level +* Dodge/Defense Points ??? + +== Attack (Ideas) + +=== D20 Always Damage, Sometimes Effect + +---- +LET roll = [roll d20] + +// Crit +IF roll == [Skill Level] THEN + damage = [weapon base damage] * 2 + roll + effect = [weapon's special effect, if available] + +// Success +ELSE IF roll < [Skill Level] THEN + damage = [weapon base damage] + roll + effect = [weapon's special effect, if available] + +// Minimal Success +ELSE + damage = [weapon base damage] + effect = none +---- + +=== D100 Always Damage, Sometimes Effect + +When you attack, roll a d100 and use the relevant skill (Melee Attack or Ranged Attack) as the +Target Number. + +*Hits* happen when you roll _lower_ than your Target Number. Hits deal damage +equal to your weapon's Base Damage plus the __digit sum__ of your roll (9 if +you rolled 18 and 15 if you rolled 69). + +*Glancing Blows* happen when you roll _higher_ than your Target Number. In this +case you deal damage equal to your weapon's Base Damage plus the _lowest digit_ +of your roll (2 if you rolled 72 or 0 if you rolled 90). Glancing blows usually +only deal damage and don't have any fancy side effects. + +*Critical Hits* happen when your roll is exactly the same as your Target Number. +Critical Hits deal damage equal to your weapon's Base Damage plus the number +you rolled. + + +=== Dual Wielding + +Dual weapons should be a Package-thing. +If using two weapons, you use your best weapon first. + +a. If you failed your roll, (but didn't roll a 20???) you get to make an attack + with your off hand weapon. +b. You can spend HP to attack (once per round) with your off-hand weapon. + +==== Sword & Board + +If using sword'n'board, you make an attack with your main weapon first, if you roll at/under +your TN and under your damage cap you +* Make a shield bash??? +* Regain some Hit Points?? + +=== Resistance + +We should have a skill that allows us to resist AoE effects such as sleep or +fireball. Even though the caster has already rolled to cast the spell, targets +of AoE or certain effects should have a chance to avoid or reduce the effects +(unless the casting was so powerful that the target does not get a roll). + + + +=== Exhaustion + +Maybe exhaustion takes up time slots. If you become overburdened, you take +penalties. + +=== Consumables + +Torches:: +2 per Item Slot. Lasts 69 real life minutes. + +Rations:: +3 per Item Slot. Lasts an entire day, and includes both food and water. + +Money:: +50 Coins per Item Slot. + +Gems:: +2 Gems per Item Slot. (Gems scratch each other if not packed correctly). + + +=== Monsters & Combat + +Many monsters (the majority) should have special abilities that make combat +more interesting. + +* Set the adventurer on fire that must be put out. +* Push the adventurer away, possibly knocking them on their ass. +* Spray itching powder that must be dealt with. +* Making an area of the battlefield uninhabitable with fire, webs, etc. +* Temporarily able to fly or jump stupid high, possibly spending their next + turn up in the air. +* Fart so badly that the adventurer MUST run away on their next turn. +* Hover. +* Cause adventurer to hover (or become weightless), making them unable to move, + and making them very queasy. +* Randomly interrupt combat, even when it's not their turn. +* Make opportunity attacks. +* Randomly heal an adventurer because they can't really control their magic. + + +=== Gooblin +Slimy goblin + + +== PLACES + +=== Ballrocks Gates diff --git a/main.adoc b/main.adoc new file mode 100755 index 0000000..d3c43d6 --- /dev/null +++ b/main.adoc @@ -0,0 +1,958 @@ += The Rules Doom & Destruction & Stuff + +// {{{ VARIABLES +// :stylesheet: style.css +:stylesheet: style.css +:doctype: article +:icons: font +:sectlinks: +:toc: +:toclevels: 1 +:toc-placement!: +:experimental: +:stem: +:xrefstyle: full +:author: Kim Ravn Hansen +:email: moccalotto@gmail.com +:pdf-page-size: a5 +:reproducible: +:table-stripes: even + + +:x: × +:one: <> +:twenty: <> +:DM: Destruction Manager +// VARIABLES }}} + +== Creating Adventurers + +Each player should begin play with TWO adventurers. Both adventurers should be +created by following these steps. + +=== {counter:creation}. Skills + +Adventurers have the following skills + +|=== +//--------------|--------------------------------------------------------- +| Awareness | Attention, Alertness, Perception, Comprehension, Insight +| Grit | Toughness Strength, Willpower +| Knowledge | History, Geography, Lore +| Magic | Using wands, scrolls, and magical artifacts +| Melee Combat | Attacking nearby opponents +| Ranged Combat | Attacking opponents at range +| Skulduggery | Stealth, deception +//--------------|--------------------------------------------------------- +|=== + +For each skill, roll 1d6, add 6, and assign that value to the skill. Each skill +should now have a score between 7 and 12. + +TIP: The skill scores are likely to change before you're done creating the adventurer, +so write them on a piece of paper rather than on the adventurer's sheet for now. + +[[ancestry,ancestry]] +=== {counter:creation}. Ancestry + +All adventurers are human. However, the adventurers blessing may have triggered +some ancient dormant blood from distant nonhuman ancestors. + +Ancestries have some effects now, but they also allow you to access certain +Perks when you level up later on. + +[%header,cols="^1,^2,9"] +|=== +| d8 | Ancestry | Description +//---|---------------|-------------------------------------- +| 1 | Human | +1 to all skills. +| 2 | Dwarven | Raise your Melee Combat to 10 +| 3 | Elven | Raise your Ranged Combat to 10 +| 4 | Gigantic | Raise your Grit to 10 +| 5 | Gnomish | Raise your Awareness to 10 +| 6 | Primordial | Raise your Magic to 10 +| 7 | Draconic | Raise your Knowledge to 10 +| 8 | Demonic | Raise your Skulduggery to 10 +//---|---------------|-------------------------------------- +|=== + +NOTE: Raising a skill that is already 10 or higher has no effect. + +[[foundation,foundation]] +=== {counter:creation}. Foundation + +An adventurer's foundation is a set of gear the they start with and they are +proficient with. Your foundation may also affect your skill scores. + + +Roll on the table below to find your Foundation. Note that you may need to reroll. +[%header,cols="^1,^2s,3a,3a,5a",grid="rows"] +|=== +| d20 | Foundation | Proficiencies | Gear | Effects +//------|-------------------|---------------+-----------+-------------------------------+ +//START_SORT //HEADLINE: + +// +//--------------------------------------------------------------------------------------- +//HEADLINE: BRAWLER +//--------------------------------------------------------------------------------------- +| {counter:foundation} + +|Brawler + +|[unstyled] +* Light Armor + +|[unstyled] +* Studded Leather +* Spiked Gauntlets +* 40 Silver Pieces + +|[unstyled] +* 15 Hit Points +* 7 Item Slots +* Melee Combat raised to 10 +* Knowledge limited to 10 + +// +//--------------------------------------------------------------------------------------- +//HEADLINE: DRUID +//--------------------------------------------------------------------------------------- +| {counter:foundation} + +|Druid + +|[unstyled] +* Proficient with any type of armor, provided it's made exclusively of natural + materials like leather, hide, wood, wool, etc. + +|[unstyled] +* Sickle +* Poisoner's Kit +* Healer's Kit +* 10 Silver Pieces + +|[unstyled] +* 10 Hit Points +* 5 Item Slots + +// +//--------------------------------------------------------------------------------------- +//HEADLINE: Fencer +//--------------------------------------------------------------------------------------- +| {counter:foundation} + +|Fencer + +|[unstyled] +* Light Armor +* Advanced Weapons + +|[unstyled] +* Leather +* Rapier +* Dagger +* 40 Silver Pieces + +|[unstyled] +* 15 Hit Points +* 5 Item Slots +* The TWO WEAPONS perk. +* Melee Combat raised to 10 +* Magic limited to 10 + +// +//--------------------------------------------------------------------------------------- +//HEADLINE: GUARD +//--------------------------------------------------------------------------------------- +| {counter:foundation} + +| Guard + +|[unstyled] +* Medium Armor +* Advanced Weapons + +|[unstyled] +* Halberd +* Bull's Eye Lantern +* Signal Whistle +* Map of Local Area +* 50 Silver Pieces + +|[unstyled] +* 10 Hit Points +* 5 Item Slots +* Awareness raised to 10 +* Melee Combat raised to 10 +* Skulduggery limited to 10 + +// +//--------------------------------------------------------------------------------------- +//HEADLINE: MAGICIAN +//--------------------------------------------------------------------------------------- +| {counter:foundation} + +| Magician + +|[unstyled] +* None + +|[unstyled] +* Tier 2 Wand with random spell. +* Tier 1 Wand with random spell. +* 10 Silver Pieces + +|[unstyled] +* 10 Hit Points +* 6 Item Slots +* Melee Combat limited to 10 +* Ranged Combat limited to 5 +* Magic raised to 10 +* Grit limited to 10 + +// +//--------------------------------------------------------------------------------------- +//HEADLINE: MEDIC +//--------------------------------------------------------------------------------------- +| {counter:foundation} + +|Medic + +|[unstyled] +* Light Armor +* Medium Armor + +|[unstyled] +* Club +* Sling +* 3 Daggers +* Healer's Kit +* 40 Silver Pieces + +|[unstyled] +* 10 Hit Points +* 6 Item Slots + +// +//--------------------------------------------------------------------------------------- +//HEADLINE: RECKLESS +//--------------------------------------------------------------------------------------- +| {counter:foundation} + +| Reckless + +|[unstyled] +* Advanced Weapons + +|[unstyled] +* Great Axe +* 50 Silver Pieces + +|[unstyled] +* 20 Hit Points +* 7 Item Slots +* Melee Combat raised to 10 +* Awareness raised to 10 +* Grit raised to 10 +* Magic limited to 10 + +// +//--------------------------------------------------------------------------------------- +//HEADLINE: ROVER +//--------------------------------------------------------------------------------------- +| {counter:foundation} + +| Rover + +|[unstyled] +* Light Armor +* Advanced Weapons + +|[unstyled] +* Leather Armor +* Short Sword +* Longbow +* Snare Maker's Kit +* 25 Silver Pieces + +|[unstyled] +* 10 Hit Points +* 5 Item Slots +* Magic Reduced to 10 +* Awareness raised to 10 +* Ranged Combat raised to 10 + +// +//--------------------------------------------------------------------------------------- +//HEADLINE: SKIRMISHER +//--------------------------------------------------------------------------------------- +| {counter:foundation} + +| Skirmisher + +|[unstyled] +* Light Armor +* Shields +* Advanced Weapons + +|[unstyled] +* Spear +* Small Shield +* 50 Silver Pieces + + +|[unstyled] +* 15 Hit Points +* 6 Item Slots +* Melee Combat raised to 10 +* Awareness raised to 10 +* Skulduggery raised to 10 +* Grit raised to 10 + +// +//--------------------------------------------------------------------------------------- +//HEADLINE: SNEAK +//--------------------------------------------------------------------------------------- +| {counter:foundation} + +| Sneak + +|[unstyled] +* Light Armor + +|[unstyled] +* 3 daggers +* Small Crossbow +* Poisoner's Kit +* 30 Silver Pieces + + +|[unstyled] +* 10 Hit Points a +* 6 Item Slots b ==> +* Melee Combat raised to 10 +* Awareness raised to 10 +* Skulduggery raised to 10 +* Grit raised to 10 + +// +//--------------------------------------------------------------------------------------- +//HEADLINE: SPELLSWORD +//--------------------------------------------------------------------------------------- +| {counter:foundation} + +| Spellsword + +|[unstyled] +* Advanced Weapons + +|[unstyled] +* Tier 1 Wand with random spell. +* Longsword +* 30 Silver Pieces + +|[unstyled] +* 12 Hit Points +* 5 Item Slots +* Melee Combat raised to 10 +* Ranged Combat limited to 10 +* Magic raised to 10 +* Skulduggery limited to 10 +* Grit raised to 10 + +// +//--------------------------------------------------------------------------------------- +//HEADLINE: SPELUNKER +//--------------------------------------------------------------------------------------- +| {counter:foundation} + +| Spelunker + +|[unstyled] +* None + +|[unstyled] +* Spear +* Caltrops +* Bull's Eye Lantern +* Map Maker's Kit +* Chalk +* Caltrops +* 5 Silver Pieces + +|[unstyled] +* 10 Hit Points +* 4 Item Slots +* Awareness raised to 10 +* Melee Combat raised to 10 +* Skulduggery raised to 10 +* Magic limited to 10 + +// +//--------------------------------------------------------------------------------------- +//HEADLINE: SPIT'N'POLISH +//--------------------------------------------------------------------------------------- +| {counter:foundation} + +| Spit'n' Polish + +|[unstyled] +* Heavy Armor +* Shield +* Advanced Weapons + +|[unstyled] +* Half-Plate +* Large Shield +* Long Sword +* 10 Silver Pieces + +|[unstyled] +* 10 Hit Points +* 2 Item Slots +* Melee Combat raised to 10 +* Magic Reduced to 6 +* Awareness Reduced to 10 + +// +//--------------------------------------------------------------------------------------- +//HEADLINE: STILETTO +//--------------------------------------------------------------------------------------- +| {counter:foundation} + +| Stiletto + +|[unstyled] +* Light Armor + +|[unstyled] +* Padded Armor +* 3 Daggers +* Small Crossbow +* Poisoner's Kit +* 20 Silver Pieces + +|[unstyled] +* 10 Hit Points +* 5 Item Slots +* Melee Combat raised to 10 +* Ranged Combat raised to 10 +* Awareness raised to 10 +* Magic limited to 6 +* Knowledge limited to 10 + +// +//--------------------------------------------------------------------------------------- +//HEADLINE: Tinkerer +//--------------------------------------------------------------------------------------- +| {counter:foundation} + +|Tinkerer + +|[unstyled] +* Light Armor + +|[unstyled] +* Studded Leather +* Wrench (club) +* Tinkerer's Kit +* 30 Silver Pieces + +|[unstyled] +* 10 Hit Points +* 5 Item Slots +* Awareness raised to 10 +* Knowledge raised to 10 + +//END_SORT + +// +//--------------------------------------------------------------------------------------- +// REROLL +//--------------------------------------------------------------------------------------- +| {counter:foundation}+ +4+| Reroll, and continue rerolling results of {foundation} and higher. + + +|=== + +=== {counter:creation}. Gear + +Roll two d8s; one for the left-hand table, and once for the right-hand table + +[%unbreakable] +.Utility Gear +[%header,cols="^2,4,^2,4"] +|=== +| First d8 | Item 1 | Second d8 |Item 2 +//---------|-------------------------|-----------|--------------------------------- +| 1 | 3 Torches | 1 | Grappling hook + 10 meter rope +| 2 | 3 Spikes | 2 | Storm Lantern +| 3 | Poisoner's Kit | 3 | Rope, 25 meters +| 4 | 100 Ball bearings | 4 | Pole, 3m, foldable +| 5 | Chalk | 5 | Shovel +| 6 | 3 Common Rations Pack | 6 | Tinkerer's Kit +| 7 | Bull's Eye Lantern | 7 | Oil Lamp + 3 units of oil +| 8 | 2d10 Silver Pieces | 8 | Map Maker's Kit +//---------|-------------------------|-----------|--------------------------------- +|=== + + +=== {counter:creation}. Background + +Your background is what your adventurer used to do before they received the +Adventurer's Blessing. + +If the DM makes you roll for something that is directly related to your +background, let the DM know - they might just give you an advantage. + +Roll a d20 and a d6 and look at the table below to find your background. + +[%header,cols="^1,5,^1,5"] +|=== +//---|----------------------|----|---------------------------- +| d20| d6 rolled 1, 3, or 5 | d20| d6 rolled 2, 4, or 6 +| 1 | Apothecary | 1 | Hunter +| 2 | Baker | 2 | Hustler +| 3 | Banker | 3 | Laborer +| 4 | Bartender | 4 | Lazy spouse +| 5 | Butcher | 5 | Magician’s apprentice +| 6 | Carter | 6 | Merchant +| 7 | Chandler | 7 | Monestarian +| 8 | Chef | 8 | Musician +| 9 | Clergy | 9 | Notary +| 12 | Cook | 12 | Royalty +| 13 | Courier | 13 | Sailor +| 14 | Courtier | 14 | Scribe +| 15 | Doctor | 15 | Smith +| 16 | Entertainer | 16 | Solder +| 17 | Farmer | 17 | Squire +| 18 | Fisherman | 18 | Street urchin +| 19 | Foreigner | 19 | Thief +| 20 | Gambler | 20 | Gold Smith +//---|----------------------|----|---------------------------- +|=== + +=== {counter:creation}. Appearance + +Roll d12 twice on the table below to find your body type. If you roll the same +value both times, the trait is extra pronounced. + +.Body traits +[%header,cols="^1,11"] +|=== +| d12 | Body traits +//------|------------------- +| 1 | Tall +| 2 | Short +| 3 | Muscular +| 4 | Stocky +| 5 | Lanky +| 6 | Graceful +| 7 | Slender +| 8 | Wiry +| 9 | Broad +| 10 | Round belly +| 11 | Awkward +| 12 | Athletic +//------|------------------- +|=== + +Roll d20 once for facial traits. + +.Facial traits +[%header,cols="^1,11"] +|=== +| d20 | Facial trait +//------|------------------------------------------- +| 1 | Sharp cheekbones and hollow eyes +| 2 | Crooked nose broken long ago +| 3 | Piercing eyes that seem to glow +| 4 | Full lips and a cleft chin +| 5 | Long, braided beard or mustache +| 6 | High forehead with deep worry lines +| 7 | One eye covered by a patch +| 8 | Elaborate facial tattoos or markings +| 9 | Eyebrows like wings—thick and dramatic +| 10 | Jawline strong enough to cut stone +| 11 | Burn scar across one cheek +| 12 | Missing ear or visible notch in it +| 13 | Eyes of two different colors (pupils are still bronze-coloured) +| 14 | Prominent dimples when smiling +| 15 | Nose like a hawk’s beak +| 16 | Gnarled, bushy eyebrows +| 17 | Lips always curled in a smirk +| 18 | Eyes that never blink (or seem not to) +| 19 | Crystal embedded in the forehead +| 20 | Completely smooth, expressionless face +//------|------------------------------------------- +|=== + +.Hair traits +[%header,cols="^1,11"] +|=== +| d10 | Hair Trait +//------|------------------------------------------------ +| 1 | Long and silky, flows like water +| 2 | Short and spiky like a hedgehog +| 3 | Thick dreadlocks tied with beads +| 4 | Curly and untamed, a wild mane +| 5 | Slicked back with oils or grease +| 6 | Braided with ribbons or charms +| 7 | Patchy or thinning with bald spots +| 8 | Voluminous afro that defies helmets +| 9 | Snow-white despite a youthful face +| 10 | Hair constantly shifts color like a mood ring +//------|------------------------------------------------ +|=== + + +=== {counter:creation}. Personality + +Your personality consists of a vice, a virtue, and a quirk. + +At their discretion,the GM can award XP when you play out your personality +traits — especially your vice, and only if you don't overdo it. + +==== Vice + +Roll once to find your vice. + +[%header,cols="^1,11"] +|=== +| d12 | Vice +//------|-------------------------------------------- +| 1 | Loves owning and interacting with treasure +| 2 | Addicted to Snarf - magical angel dust +| 3 | Gets a kick out of telling outrageous lies +| 4 | Solves problems with fists +| 5 | Obsessive collector of bizarre items +| 6 | Always flirting, even at the worst times +| 7 | Gambler who can’t resist a wager +| 8 | Glutton for food, drink, or pleasure +| 9 | Reckless thrill-seeker, craves danger +| 10 | Talks to self constantly +| 11 | Talks to invisible friend +| 11 | Addicted to forbidden knowledge or magic +| 12 | Obsessed with cleanliness +//------|-------------------------------------------- +|=== + +==== Virtue + +Roll once to find your virtue. The DM might give you extra +XP if your virtues come into play. + +[%header,cols="^1,11"] +|=== +| d20 | Virtue +//------|-------------------------------------------- +| 1 | Loyal to a fault, never breaks a promise +| 2 | Courageous in the face of danger +| 3 | Generous with time, wealth, and effort +| 4 | Honest, even when it hurts +| 5 | Protective of the weak and voiceless +| 6 | Wise beyond their years +| 7 | Forgives easily, even enemies +| 8 | Hardworking and persistent +| 9 | Pious or deeply spiritual +| 10 | Self-sacrificing for the good of others +| 11 | Finds beauty in all things +| 12 | Curious and open-minded +| 13 | Kind-hearted, even to the undeserving +| 14 | Inspires courage in others +| 15 | Always seeks the truth +| 16 | Disciplined and self-controlled +| 17 | Brave enough to admit weakness +| 18 | Empathic, feels others' pain +| 19 | Just and fair, upholds law or balance +| 20 | Keeps hope alive, even in darkness +//------|-------------------------------------------- +|=== + +==== Quirk + +Roll once to find your quirk. The DM might give you extra XP if your quirks +come into play. + +[%header,cols="^1,11"] +|=== +| d20 | Quirk +//----|---------------------------------------------- +| 1 | Likes to rhyme +| 2 | Believes animals understand them +| 3 | Eats only one type of food +| 4 | Names all their weapons and gear +| 5 | Laughs at inappropriate moments +| 6 | Deep dislike of mirrors +| 7 | Collects bones, teeth, or pebbles +| 8 | Speaks in third person +| 9 | Talks to their pet stone +| 10 | Always wears gloves +| 11 | Whistles when nervous +| 12 | Believes in bizarre superstitions +| 13 | Refuses to walk through doorways first +| 14 | Smells everything before touching it +| 15 | Never says their real name +| 16 | Only speaks in third person +| 17 | Claims to have lived many past lives +| 18 | Counts out their coins slowly and meticulously +| 19 | Hates silence, starts singing or humming +| 20 | Gassy, and proud of it +//----|---------------------------------------------- +|=== + + +=== {counter:creation}. Fill Out The Adventurer's Sheet + +Now you fill in all the details you've rolled. All you need to do is to choose +a gender and a name. + +Your adventurer has a number of Item Slots, determined by their Foundation. The +sheet provides space for 10 slots, but you may have fewer. Shade, hatch, or +cross out any unusable Item Slots. == Perks + +== Items + +=== Weapons + + +//--------------- +// SIMPLE WEAPONS +//--------------- +==== Simple Weapons +[%unbreakable] +-- +All adventurers can use simple weapons, regardless of <>, +<>, or <>. + +[%header,cols="^2,^3,^2,^2,^2,6"] +|=== + | Range | Weapon | Hands | IS | Damage | Special Effect +//----------|-------------------|-------|----|--------|---------------- +| Any | Dagger | 1 | ⅓ | 4 | +| Any | Spear | 1 | 2 | 5 | +| Melee | Club | 1 | 1 | 4 | +| Melee | Flail | 1 | 1 | 6 | +| Melee | Handaxe | 1 | 1 | 6 | +| Melee | Longspear | 2 | 3 | 9 | +| Melee | Mace | 1 | 2 | 7 | +| Melee | Morning star | 1 | 2 | 8 | +| Melee | Quarterstaff | 2 | 2 | 6 | +| Melee | Sickle | 1 | 1 | 4 | +| Melee | Warhammer | 1 | 2 | 6 | +| Ranged | Sling | 2 | 1 | 4 | +| Ranged | Small Crossbow | 2 | 2 | 8 | +//----------|-------------------|-------|----|--------|---------------- +|=== +-- + +==== Advanced Weapons +[%unbreakable] +-- +Any adventurer who has a <>, <>, or <> that allows +them to use advanced weapons can use the weapons on this list. Adventurers who +haven't got such an ability has <> when attacking with advanced +weapons, and can never use their special abilities. + +[%header,cols="^2,^3,^2,^2,6"] +|=== +| Range | Weapon | Hands | Damage | Special Effect +//---------|-------------------|-------|------------------------- +| Any | Javelin | 1 | | Better than spear, somehow +| Melee | Battleaxe | 1 | 8 | Mini cleave? +| Melee | Claymore | 2 | 8 | Something cool that the Zweihänder doesn't have +| Melee | Falchion | 2 | | +| Melee | Greataxe | 2 | 10 | Cleave +| Melee | Halberd | 1 | 11 | Pull +| Melee | Longsword | 1 | 8 | +| Melee | Rapier | 1 | 6 | Fast: attack again? +| Melee | Scimitar | 1 | 7 | +| Melee | Short Sword | 1 | 6 | +| Melee | Stiletto | 1 | 4 | Fast: attack again? +| Melee | Zweihänder | 1 | 10 | Push +| Ranged | Large Crossbow | 1 | 10 | +| Ranged | Longbow | 1 | | +| Ranged | Shortbow | 1 | | +//---------|-------------------|-------|------------------------- +|=== +-- + + +==== Weird Weapons +[%unbreakable] +-- +In order to be able to use one of the weird weapons on this list, an adventurer +must have a <>, <>, or <> that allows them to use that +specific weapon. For instance, an adventurer who has an ability to use the Kosh +can use koshes, but not any of the other weird weapons. + + +[%header,cols="^2,^3,^2,^2,6"] +|=== +| Range | Weapon | Hands | Damage | Special Effect +//---------|-------------------|--------|--------|---------------- +| Melee | Bastard Sword | 1 | 10 | None - high base damage +| Ranged | Blowgun | 1 | | +| Ranged | Bolas | 1 | 8 | +| Melee | Kosh | 1 | 8 | Knock-out +//---------|-------------------|--------|--------|---------------- +|=== +-- + +=== Armors + +.Light Armor +[%header,cols="2,^1,^1,2"] +|=== +//----------------------|-----|-------|--------------------------------- +| Armor | HP | Sneak | Notes +//----------------------|-----|-------|--------------------------------- +| Padded Armor | 1 | Yes | Cheap, bulky cloth layers +| Leather Armor | 2 | No | Flexible leather +| Studded Leather | 3 | No | Reinforced with metal studs +| Hide Shirt | 2 | Yes | Made from thick animal hide +| Gambeson | 2 | Yes | Quilted cloth and fibres +| Traveler's Coat | 1 | Yes | Light and practical +//----------------------|-----|-------|--------------------------------- +|=== + +.Medium Armor +[%header,cols="2,^1,^1,2"] +|=== +//----------------------|-----|-------|--------------------------------- +| Armor | HP | Sneak | Notes +//----------------------|-----|-------|--------------------------------- +| Scale Mail | 4 | Yes | Metal scales, noisy +| Chain Shirt | 4 | Yes | Metal rings, worn under clothes +| Brigandine | 5 | Yes | Plates riveted inside a jacket +| Lamellar Armor | 5 | Yes | Laced metal or leather plates +| Ring Mail | 4 | Yes | Rings over padded base +| Hardened Leather | 3 | Yes | Boiled and shaped for stiffness +//----------------------|-----|-------|--------------------------------- +|=== + +.Heavy Armor +[%header,cols="2,^1,^1,2"] +|=== +//----------------------|-----|-------|--------------------------------- +| Armor | HP | Sneak | Notes +//----------------------|-----|-------|--------------------------------- +| Chainmail | 6 | No | Full-body chain coverage +| Splint Mail | 7 | No | Metal strips on a backing +| Banded Mail | 7 | No | Horizontal bands over padding +| Half-Plate | 8 | No | Partial plate, better mobility +| Full Plate | 9 | No | Maximum protection, very heavy +| Coat of Plates | 8 | No | Metal plates sewn into a coat +//----------------------|-----|-------|--------------------------------- +|=== + +== Perks +This chapter is not for + +* Racial. +** There should be a racial perk tree that can be unlocked. Only adventurers + can access the blood of the ancients (elf, dwarf, etc.) +** No races should get dark vision unless they are at MAX level. +** Dwarves should get an extra item slot (possibly more if you take this perk + multiple times). +** Dwarves are good in dungeons (know cardinal directions, head-map, ability to + "smell" metals and ores). +** Elves are good on the surface (navigating by stars, being difficult to + surprise, requiring less sleep). +** Gnomes??? +** Primordials/primals (ancient humans) +** Prim((anus)) +* Monstrous Gourmand. +* Unlock medium (and later on heavy) armor. +* Unlock Special Effects for common melee weapons. +* Exotic Weapon Perk Tree +** Initially you get access to a "simple" exotic weapon. +** Later on you can use certain two-handed weapons in one hand. +* Multi-attack if you roll higher than X. Possibly play into what happens when + attack skill is higher than MAX TN. + +== Gear + +* Weapons +** Melee Weapons +** Ranged Weapons +** Special Weapons?? +*** Bolas +*** Kosh +*** Spiked Chain +*** Meteor Hammer +*** Bastard Sword (can be used one handed if adventurer has the right perk) +* Armors +* Misc +** Food +** Dungeon Spelunking Gear +** Wilderness Survival Gear +** Transport, Wagons, Beasts +* Valuables +** Coins +** Gems +** Misc (art, etc.) + +== Campaign +* Map +** The world should be weird and funny, but not ridiculous. +** ShipWorld: the world, when seen from above, looks like the outline of a + ship, that's seen from above. But instead of being made of wooden planks, + there is real terrain and flora. But there should/could be elevation changes + where the different decks (such as the Poop Deck) are. +*** What's around the ship? +*** What happens if you dig? +*** Can dungeons reach into the inner hull/decks of the ship? +** DeadWorld: Maybe the world is the surface of some gigantic dead creature. +*** What's around the creature? +*** What happens if you dig? +*** Do dungeons reach into the inner parts of the corpse? Like the brain? +** FloorWorld: The world is the floor of a mage's study. She accidentally + created this world, but destroyed everything else. +* Cities and Towns +** Capita (the Capital) +* Prominent People +** Master Baator +** Richard Tator (dictator) +** Laika Bors +* History + +== Doom Manager's Rules +* Advancement +** XP for killing Monsters. +** XP for uncovering Secret Knowledge and Locations. +** XP for spending money. +** XP for spending carousing. + +* Adventuring +** Splitting the party: It is a viable strategy to split the party, and send in + a sacrificial team in advance - but the GM can and should require that each + present player send in one of their adventurers so that all players are + engaged at the same time. + +== Spells +* A note on which types of magical artifacts can carry spells (wands, books, + and reusable scrolls). +* Alphabetized list of spells +* One spell = One page (a5) - but very special spells (like Wish) may take up + two pages, but keep in mind that the two pages must still be arranged + prettily. + +== Magical Items +* Magical Weapons +* Magical Armors +* Special Wands/Scrolls/Books +* Wondrous Items +** Naïvitea (tea that makes you blonde) + +== Monsters +* One page = one (typical) baddy. +* Baddies should have special TWISTS. When spawning baddies, the DM should roll + random TWISTS for each baddy or group of baddies. +* Monsters +** Mandag: a really bad male hag +** Halvbitter: more badass than Djinn +** Gooblin +** Cowlbear (a bear in a cowl?) diff --git a/monsters.adoc b/monsters.adoc new file mode 100755 index 0000000..c8c1e4e --- /dev/null +++ b/monsters.adoc @@ -0,0 +1,187 @@ += Monster's Guide To Doom & Destruction & Stuff + + +== WEIRDS + +Mondays:: A type of monster invented by Fred after he'd had a really bad day. +He called it, a Monday, and it is almost impossible to describe. + +* It is bestowed with an extreme, but unknowable, intelligence. +* It is extremely large, but is able to be wholly or partially ephemeral to + suit their needs. +* It can take the shape of forests, castles, large bodies of water, airships, + or similar. +* It can coalesce into a smaller, more substantial monster, but it rarely does. +* Its heart is located somewhere inside it, but it can be hard to find, and it + never looks like a heart. +* It eats happiness and shits out bad health. + +== OILS + +Garg Oil:: + * Oily slime monster with wings. + * Can fly. + * Related to Slime. + * Drown attack. + * Oil splash attack. + * Resistant to piercing weapons. + +Gargle Oil:: + * Thinner version of Garg Oil. + * Very slippery. + * Flammable. + * Resistant to piercing weapons. + * Resistant to slashing weapons. + * Can turn into slime puddle. + * Spray attack. + * Organic Compound Spray Attack. + +Targ Oil:: + * Thicker and sticker version of Garg Oil. + * Stronger. + * Sticky attacks. + * Cannot fly, but wings are considered armor pieces. + +Farg Oil:: + * Gargle Oil with powerful ranged attacks and spells. + +Gelatinous Pube:: + * Disgustingly smelly seaweed thing + +Slime:: +A type of Oil, a slime is a semi-intelligent monster that can be made out of +anything from rainwater to lava, these monsters are often highly caustic, and +can constrict their opponents. + +When a slime is born, it is usually a few cubic centimeters in size, but as it +consumes more and more material, it grows larger. Many slimes like to feed on +living organic matter (people, plants, animals, etc.). + +Pooh-ma:: +A vicious wolf-like oily monster born in the sewers and waste pits of cities, +military camps, and similar concentrations of feces. + +* Basically a dire-wolf made of feces. +* Is technically a slime, but can also be classified as an undead, an orc, + or even a golem, depending on its composition. +* Strong. +* Spreads diseases that can affect adventurers. +* Stink breath weapon. +* Claws. +* Pooh-fling (throws own body parts at opponent, high damage, but loses own HP). +* Resistance to piercing weapons (roll damage with disadvantage). +* Resistance to fire (roll damage with disadvantage). +* Usually antisocial, but can hunt in packs with its brothers. +* It does not like the light (all checks are difficult when in bright light). + +B'ooze:: +* An ooze made of booze. + +== ORCS + +[quote,Oralix LeDique] +Orcs are to trees what undeads to the Enlightened. + +Green and brown skinned creatures that are created when trees and large plants +die from unnatural causes. + +* Orcs are a category containing orcs, trolls, cyclops +* Not gnolls, goblins or hobs. +* Created completely from plant matter => green and brown skins. +* They exude a kind of blight that slowly degenerates vegetation, which makes + the orcs stronger. +* Boss Orcs can reverse their blight, killing off a few orcs and rapidly + increasing plant growth. +* Continue to grow as long as there is nourishment (plant matter). +* Sometimes destroy forests to create more orcs. +* Only become undead if some outside force (such as an evil mage) turns them. +* Tough, extreme pain tolerance, and high morale. +* Individual orcs have animalistic intelligence, but sometimes a Great Leader + that can control or direct the entire army. +* Die if they are kept away from an open sky for too long; they cannot be + imprisoned in a donjon or inside any kind of building - they'll rot. +* Breeder orcs are a kind of "queen" orc that can charm enlightened males into + mating with them, creating hybrid orc spawn. + +Hybrid Orcs:: +* Like nephilim +* Intelligent +* Independent +* Very Powerful + +== UNDEADS + +* Undead creatures rise automatically. Especially if they have died from + traumatic events such as war, assault, and magical accidents. +* Once slain, some undeads can rise again. Usually as less powerful beings. +* Undeads almost always have worse stats than when they were alive. +* Generally immune to fear. +* Generally slower. +* Generally able to see/sense in darkness. +* Powerful undeads, such as liches, generally do not rise spontaneously. + +Mummy Troll:: +A troll (who is technically an Orc) that has been magically preserved, wrapped +in magical bandages, and turned undead. + +Undeads of the Sea:: +Sailors and enlightened sea creatures can become undead. +Sailors tend to walk towards shore and become a menace on the beaches and +harbours of the world. Sea creatures tend to patrol their old territories and +make a mess there. + +Crack-Ann:: Female undead Kraken. Rotten and spiteful. Poisonous and stinky +bodily fluids ad nauseam. + +Old George:: +Male version of Crack-Ann. He's an elderly dude, who is slow and not as young +as he was. Sailors have learned to navigate around him, and generally avoid +him. + +== Bug Bears +Combine a bug and a bear. + +* Many varieties +* Some are mostly bug, some are mostly bear. +* Some have wings, some have mandibles, some have cute bear ears on exoskeletal + head. + +== GOBLIN + +* Evil type of gnome +* A bit like Rask Gar. +* Dark vision. +* Not very fond of gnomes. +* Cowards. +* Power cycles: +** Males are intelligent, cunning, and fierce. +** Females are physically very strong and tough. +** Goblins are less powerful during 12 "daytime" hours. +** ?? On odd years males get a boost to their mental stats, and + on even years females get a boost to their physical stats. + This is universal. It means that, no matter where you are, + you either have clever males or strong females, it varies from year to year. +* Specializes in: +** Extremely unsafe mechanical engineering. +** Narcotics and similar poisons. +** Unstable blast powder. +** Traps that are unfair, unstable, and unnecessarily gruesome. + + +== AUTOMATA +Golems + +== MIMIC + +* House Mimic: A mimic the size and shape of a house. +* Fortress Mimic: A mimic the size and shape of a fortress, and with all + of the armaments too. + +== MISC + +Haggis (Hag, slime ??):: +Disgusting smelly blob of vile spellcasting. + + +Cognac:: +Much more awesome than a gin (djinn) diff --git a/readme.adoc b/readme.adoc new file mode 100755 index 0000000..35d5108 --- /dev/null +++ b/readme.adoc @@ -0,0 +1,14 @@ += Yet Another Reduced Instruction Set for Doom & Destruction & Stuff. +Kim Ravn Hansen +:stylesheet: style.css +:sectlinks: +:toc: +:toclevels: 1 +:toc-placement!: +:experimental: +:stem: + + +* xref:adventurer#[Adventurers’s Guide to Doom & Destruction & Stuff]. +* xref:hero#[Hero’s Guide to Doom & Destruction & Stuff]. +* xref:elite#[Elite’s Guide to Doom & Destruction & Stuff]. diff --git a/sheet.adoc b/sheet.adoc new file mode 100755 index 0000000..f923b52 --- /dev/null +++ b/sheet.adoc @@ -0,0 +1,37 @@ + +[%header,cols="6, 3, 3"] +|=== +| Skill | Max Score | Current Score +//----------------------|-----------|------------------------------------------ +| Melee Attack | | +| Ranged Attack | | +| Magic | | +| Skulduggery | | +| Grit | | +| Knowledge | | +| Awareness | | +//----------------------|-----------|------------------------------------------ +|=== + +[%header,cols="6, 3, 3"] +|=== +| Weapon | Damage | Effects +//----------------------|-----------|------------------------------------------ +| Unarmed Combat | 4 | None +| Short Sword | 6 | Light (if you roll lower than X, you get an extra attack) +| | | +| | | +//----------------------|-----------|------------------------------------------ +|=== + +[%header,cols="6, 3, 3"] +|=== +| Wand | Quality | Spent +//----------------------|-----------|------------------------------------------ +| | | +| | | +| | | +| | | +//----------------------|-----------|------------------------------------------ +|=== + diff --git a/style.css b/style.css new file mode 100755 index 0000000..67e8ce3 --- /dev/null +++ b/style.css @@ -0,0 +1,1687 @@ +/* this is my custom css */ + + +@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"; + +/* fonts */ +@import url('https://fonts.cdnfonts.com/css/gloria-hallelujah'); +@import url('https://fonts.cdnfonts.com/css/shadows-into-light'); +@import url('https://fonts.cdnfonts.com/css/shadows-into-light-two'); +@import url('https://fonts.cdnfonts.com/css/spectral'); + +:root { + --text-color: rgb(0, 0, 0, 0.8); +} + +/* MUFASSA */ + +html{ + font-family: 'Spectral', sans-serif; + -webkit-text-size-adjust:100% +} + a{ + background:none +} + a:focus{ + outline:thin dotted +} + a:active,a:hover{ + outline:0 +} + h1{ + font-size:2em; + margin:.67em 0 +} + b,strong{ + font-weight:bold; + font-family: 'Shadows Into Light Two', sans-serif; + letter-spacing:0.1em !important; +} + abbr{ + font-size:.9em +} + abbr[title]{ + cursor:help; + border-bottom:1px dotted #dddddf; + text-decoration:none +} + dfn{ + font-style:italic +} + hr{ + height:0 +} + mark{ + background:#ff0; + color:#000 +} + code,kbd,pre,samp{ + font-family:monospace; + font-size:1em +} + pre{ + white-space:pre-wrap +} + q{ + quotes:"\201C" "\201D" "\2018" "\2019" +} + small{ + font-size:80% +} + sub,sup{ + font-size:75%; + line-height:0; + position:relative; + vertical-align:baseline +} + sup{ + top:-.5em +} + sub{ + bottom:-.25em +} + img{ + border:0 +} + svg:not(:root){ + overflow:hidden +} + figure{ + margin:0 +} + audio,video{ + display:inline-block +} + audio:not([controls]){ + display:none; + height:0 +} + fieldset{ + border:1px solid silver; + margin:0 2px; + padding:.35em .625em .75em +} + legend{ + border:0; + padding:0 +} + button,input,select,textarea{ + font-family:inherit; + font-size:100%; + margin:0 +} + button,input{ + line-height:normal +} + button,select{ + text-transform:none +} + button,html input[type=button],input[type=reset],input[type=submit]{ + -webkit-appearance:button; + cursor:pointer +} + button[disabled],html input[disabled]{ + cursor:default +} + input[type=checkbox],input[type=radio]{ + padding:0 +} + button::-moz-focus-inner,input::-moz-focus-inner{ + border:0; + padding:0 +} + textarea{ + overflow:auto; + vertical-align:top +} + table{ + border-collapse:collapse; + border-spacing:0 +} + *,::before,::after{ + box-sizing:border-box +} + html,body{ + font-size:100% +} + body{ + background:#fff; + color:var(--text-color); + padding:0; + margin:0; + font-family: 'Spectral', sans-serif; + line-height:1; + position:relative; + cursor:auto; + -moz-tab-size:4; + -o-tab-size:4; + tab-size:4; + word-wrap:anywhere; + -moz-osx-font-smoothing:grayscale; + -webkit-font-smoothing:antialiased +} + a:hover{ + cursor:pointer +} + img,object,embed{ + max-width:100%; + height:auto +} + object,embed{ + height:100% +} + img{ + -ms-interpolation-mode:bicubic +} + .left{ + float:left!important +} + .right{ + float:right!important +} + .text-left{ + text-align:left!important +} + .text-right{ + text-align:right!important +} + .text-center{ + text-align:center!important +} + .text-justify{ + text-align:justify!important +} + .hide{ + display:none +} + img,object,svg{ + display:inline-block; + vertical-align:middle +} + textarea{ + height:auto; + min-height:50px +} + select{ + width:100% +} + .subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{ + line-height:1.45; + color:#7a2518; + font-weight:400; + margin-top:0; + margin-bottom:.25em; +} + div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{ + margin:0; + padding:0 +} + a{ + /* color:#2156a5; */ + color:var(--text-color); + /* text-decoration:underline; */ + font-style:italic + line-height:inherit +} + a:hover,a:focus{ + color:#1d4b8f +} + a img{ + border:0 +} + p{ + line-height:1.6; + margin-bottom:1.25em; + text-rendering:optimizeLegibility; +} + p aside{ + font-size:.875em; + line-height:1.35; + font-style:italic +} + h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{ + font-family: 'Shadows Into Light Two', sans-serif; + font-weight:300; + font-style:normal; + color:#ba3925; + text-rendering:optimizeLegibility; + margin-top:1em; + margin-bottom:.5em; + line-height:1.0125em +} + h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{ + font-size:60%; + color:#e99b8f; + line-height:0 +} + h1{ + font-family: 'Shadows Into Light', sans-serif !important; + font-size:2.125em; +} + h2{ + font-size:1.6875em +} + h3,#toctitle,.sidebarblock>.content>.title{ + font-size:1.375em +} + h4,h5{ + font-size:1.125em +} + h6{ + font-size:1em +} + hr{ + border:solid #dddddf; + border-width:1px 0 0; + clear:both; + margin:1.25em 0 1.1875em +} + em,i{ + font-style:italic; + line-height:inherit +} + strong,b{ + font-weight:bold; + line-height:inherit +} + small{ + font-size:60%; + line-height:inherit +} + code{ + font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace; + font-weight:400; + color:rgba(0,0,0,.9) +} + ul,ol,dl{ + line-height:1.6; + margin-bottom:1.25em; + list-style-position:outside; + font-family:inherit +} + ul,ol{ + margin-left:1.5em +} + ul li ul,ul li ol{ + margin-left:1.25em; + margin-bottom:0 +} + ul.circle{ + list-style-type:circle +} + ul.disc{ + list-style-type:disc +} + ul.square{ + list-style-type:square +} + ul.circle ul:not([class]),ul.disc ul:not([class]),ul.square ul:not([class]){ + list-style:inherit +} + ol li ul,ol li ol{ + margin-left:1.25em; + margin-bottom:0 +} + dl dt{ + margin-bottom:.3125em; + font-weight:bold; + font-family:"Shadows Into Light"; + font-size:1em; +} + dl dd{ + margin-bottom:1.25em +} + blockquote{ + margin:0 0 1.25em; + padding:.5625em 1.25em 0 1.1875em; + border-left:1px solid #ddd; +} + blockquote,blockquote p{ + line-height:1.6; + color:rgba(0,0,0,.85) +} + @media screen and (min-width:768px){ + h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{ + line-height:1.2 + } + h1{ + font-size:2.75em + } + h2{ + font-size:2.3125em + } + h3,#toctitle,.sidebarblock>.content>.title{ + font-size:1.6875em + } + h4{ + font-size:1.4375em + } +} + table{ + background:#fff; + margin-bottom:1.25em; + border:1px solid #dedede; + word-wrap:normal +} + table thead,table tfoot{ + background:#f7f8f7 +} + table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{ + padding:.5em .625em .625em; + font-size:inherit; + color:rgba(0,0,0,.8); + text-align:left +} + table tr th,table tr td{ + padding:.5625em .625em; + font-size:inherit; + color:rgba(0,0,0,.8) +} + table tr.even,table tr.alt{ + background:#f8f8f7 +} + table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{ + line-height:1.6 +} + h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{ + line-height:1.2; + word-spacing:-.05em +} + h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{ + font-weight:400 +} + .center{ + margin-left:auto; + margin-right:auto +} + .stretch{ + width:100% +} + .clearfix::before,.clearfix::after,.float-group::before,.float-group::after{ + content:" "; + display:table +} + .clearfix::after,.float-group::after{ + clear:both +} + :not(pre).nobreak{ + word-wrap:normal +} + :not(pre).nowrap{ + white-space:nowrap +} + :not(pre).pre-wrap{ + white-space:pre-wrap +} + :not(pre):not([class^=L])>code{ + font-size:.9375em; + font-style:normal!important; + letter-spacing:0; + padding:.1em .5ex; + word-spacing:-.15em; + background:#f7f7f8; + border-radius:4px; + line-height:1.45; + text-rendering:optimizeSpeed +} + pre{ + color:rgba(0,0,0,.9); + font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace; + line-height:1.45; + text-rendering:optimizeSpeed +} + pre code,pre pre{ + color:inherit; + font-size:inherit; + line-height:inherit +} + pre>code{ + display:block +} + pre.nowrap,pre.nowrap pre{ + white-space:pre; + word-wrap:normal +} + em em{ + font-style:normal +} + strong strong{ + font-weight:400 +} + .keyseq{ + color:rgba(51,51,51,.8) +} + kbd{ + font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace; + display:inline-block; + color:rgba(0,0,0,.8); + font-size:.65em; + line-height:1.45; + background:#f7f7f7; + border:1px solid #ccc; + border-radius:3px; + box-shadow:0 1px 0 rgba(0,0,0,.2),inset 0 0 0 .1em #fff; + margin:0 .15em; + padding:.2em .5em; + vertical-align:middle; + position:relative; + top:-.1em; + white-space:nowrap +} + .keyseq kbd:first-child{ + margin-left:0 +} + .keyseq kbd:last-child{ + margin-right:0 +} + .menuseq,.menuref{ + color:#000 +} + .menuseq b:not(.caret),.menuref{ + font-weight:inherit +} + .menuseq{ + word-spacing:-.02em +} + .menuseq b.caret{ + font-size:1.25em; + line-height:.8 +} + .menuseq i.caret{ + font-weight:bold; + text-align:center; + width:.45em +} + b.button::before,b.button::after{ + position:relative; + top:-1px; + font-weight:400 +} + b.button::before{ + content:"["; + padding:0 3px 0 2px +} + b.button::after{ + content:"]"; + padding:0 2px 0 3px +} + p a>code:hover{ + color:rgba(0,0,0,.9) +} + #header,#content,#footnotes,#footer{ + width:100%; + margin:0 auto; + max-width:62.5em; + *zoom:1; + position:relative; + padding-left:.9375em; + padding-right:.9375em +} + #header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{ + content:" "; + display:table +} + #header::after,#content::after,#footnotes::after,#footer::after{ + clear:both +} + #content{ + margin-top:1.25em +} + #content::before{ + content:none +} + #header>h1:first-child{ + color:rgba(0,0,0,.85); + margin-top:2.25rem; + margin-bottom:0 +} + #header>h1:first-child+#toc{ + margin-top:8px; + border-top:1px solid #dddddf +} + #header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){ + border-bottom:1px solid #dddddf; + padding-bottom:8px +} + #header .details{ + border-bottom:1px solid #dddddf; + line-height:1.45; + padding-top:.25em; + padding-bottom:.25em; + padding-left:.25em; + color:rgba(0,0,0,.6); + display:flex; + flex-flow:row wrap +} + #header .details span:first-child{ + margin-left:-.125em +} + #header .details span.email a{ + color:rgba(0,0,0,.85) +} + #header .details br{ + display:none +} + #header .details br+span::before{ + content:"\00a0\2013\00a0" +} + #header .details br+span.author::before{ + content:"\00a0\22c5\00a0"; + color:rgba(0,0,0,.85) +} + #header .details br+span#revremark::before{ + content:"\00a0|\00a0" +} + #header #revnumber{ + text-transform:capitalize +} + #header #revnumber::after{ + content:"\00a0" +} + #content>h1:first-child:not([class]){ + color:rgba(0,0,0,.85); + border-bottom:1px solid #dddddf; + padding-bottom:8px; + margin-top:0; + padding-top:1rem; + margin-bottom:1.25rem +} + #toc{ + border-bottom:1px solid #e7e7e9; + padding-bottom:.5em +} + #toc>ul{ + margin-left:.125em +} + #toc ul.sectlevel0>li>a{ + font-style:italic +} + #toc ul.sectlevel0 ul.sectlevel1{ + margin:.5em 0 +} + #toc ul{ + font-family:"Open Sans","DejaVu Sans",sans-serif; + list-style-type:none +} + #toc li{ + line-height:1.3334; + margin-top:.3334em +} + #toc a{ + text-decoration:none +} + #toc a:active{ + text-decoration:underline +} + #toctitle{ + color:#7a2518; + font-size:1.2em +} + @media screen and (min-width:768px){ + #toctitle{ + font-size:1.375em + } + body.toc2{ + padding-left:15em; + padding-right:0 + } + #toc.toc2{ + margin-top:0!important; + background:#f8f8f7; + position:fixed; + width:15em; + left:0; + top:0; + border-right:1px solid #e7e7e9; + border-top-width:0!important; + border-bottom-width:0!important; + z-index:1000; + padding:1.25em 1em; + height:100%; + overflow:auto + } + #toc.toc2 #toctitle{ + margin-top:0; + margin-bottom:.8rem; + font-size:1.2em + } + #toc.toc2>ul{ + font-size:.9em; + margin-bottom:0 + } + #toc.toc2 ul ul{ + margin-left:0; + padding-left:1em + } + #toc.toc2 ul.sectlevel0 ul.sectlevel1{ + padding-left:0; + margin-top:.5em; + margin-bottom:.5em + } + body.toc2.toc-right{ + padding-left:0; + padding-right:15em + } + body.toc2.toc-right #toc.toc2{ + border-right-width:0; + border-left:1px solid #e7e7e9; + left:auto; + right:0 + } +} + @media screen and (min-width:1280px){ + body.toc2{ + padding-left:20em; + padding-right:0 + } + #toc.toc2{ + width:20em + } + #toc.toc2 #toctitle{ + font-size:1.375em + } + #toc.toc2>ul{ + font-size:.95em + } + #toc.toc2 ul ul{ + padding-left:1.25em + } + body.toc2.toc-right{ + padding-left:0; + padding-right:20em + } +} + #content #toc{ + border:1px solid #e0e0dc; + margin-bottom:1.25em; + padding:1.25em; + background:#f8f8f7; + border-radius:4px +} + #content #toc>:first-child{ + margin-top:0 +} + #content #toc>:last-child{ + margin-bottom:0 +} + #footer{ + max-width:none; + background:rgba(0,0,0,.8); + padding:1.25em +} + #footer-text{ + color:hsla(0,0%,100%,.8); + line-height:1.44 +} + #content{ + margin-bottom:.625em +} + .sect1{ + padding-bottom:.625em +} + @media screen and (min-width:768px){ + #content{ + margin-bottom:1.25em + } + .sect1{ + padding-bottom:1.25em + } +} + .sect1:last-child{ + padding-bottom:0 +} + .sect1+.sect1{ + border-top:1px solid #e7e7e9 +} + #content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{ + position:absolute; + z-index:1001; + width:1.5ex; + margin-left:-1.5ex; + display:block; + text-decoration:none!important; + visibility:hidden; + text-align:center; + font-weight:400 +} + #content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{ + content:"\00A7"; + font-size:.85em; + display:block; + padding-top:.1em +} + #content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{ + visibility:visible +} + #content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{ + color:#ba3925; + text-decoration:none +} + #content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{ + color:#a53221 +} + details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{ + margin-bottom:1.25em +} + details{ + margin-left:1.25rem +} + details>summary{ + cursor:pointer; + display:block; + position:relative; + line-height:1.6; + margin-bottom:.625rem; + outline:none; + -webkit-tap-highlight-color:transparent +} + details>summary::-webkit-details-marker{ + display:none +} + details>summary::before{ + content:""; + border:solid transparent; + border-left:solid; + border-width:.3em 0 .3em .5em; + position:absolute; + top:.5em; + left:-1.25rem; + transform:translateX(15%) +} + details[open]>summary::before{ + border:solid transparent; + border-top:solid; + border-width:.5em .3em 0; + transform:translateY(15%) +} + details>summary::after{ + content:""; + width:1.25rem; + height:1em; + position:absolute; + top:.3em; + left:-1.25rem +} + .admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{ + text-rendering:optimizeLegibility; + text-align:left; + font-family:"Noto Serif","DejaVu Serif",serif; + font-size:1rem; + font-style:italic +} + table.tableblock.fit-content>caption.title{ + white-space:nowrap; + width:0 +} + .paragraph.lead>p,#preamble>.sectionbody>[class=paragraph]:first-of-type p{ + font-size:1.21875em; + line-height:1.6; + color:rgba(0,0,0,.85) +} + .admonitionblock>table{ + border-collapse:separate; + border:0; + background:none; + width:100% +} + .admonitionblock>table td.icon{ + text-align:center; + width:80px +} + .admonitionblock>table td.icon img{ + max-width:none +} + .admonitionblock>table td.icon .title{ + font-weight:bold; + font-family:"Open Sans","DejaVu Sans",sans-serif; + text-transform:uppercase +} + .admonitionblock>table td.content{ + padding-left:1.125em; + padding-right:1.25em; + border-left:1px solid #dddddf; + color:rgba(0,0,0,.6); + word-wrap:anywhere +} + .admonitionblock>table td.content>:last-child>:last-child{ + margin-bottom:0 +} + .exampleblock>.content{ + border:1px solid #e6e6e6; + margin-bottom:1.25em; + padding:1.25em; + background:#fff; + border-radius:4px +} + .sidebarblock{ + border:1px solid #dbdbd6; + margin-bottom:1.25em; + padding:1.25em; + background:#f3f3f2; + border-radius:4px +} + .sidebarblock>.content>.title{ + color:#7a2518; + margin-top:0; + text-align:center +} + .exampleblock>.content>:first-child,.sidebarblock>.content>:first-child{ + margin-top:0 +} + .exampleblock>.content>:last-child,.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{ + margin-bottom:0 +} + .literalblock pre,.listingblock>.content>pre{ + border-radius:4px; + overflow-x:auto; + padding:1em; + font-size:.8125em +} + @media screen and (min-width:768px){ + .literalblock pre,.listingblock>.content>pre{ + font-size:.90625em + } +} + @media screen and (min-width:1280px){ + .literalblock pre,.listingblock>.content>pre{ + font-size:1em + } +} + .literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class=highlight],.listingblock>.content>pre[class^="highlight "]{ + background:#f7f7f8 +} + .literalblock.output pre{ + color:#f7f7f8; + background:rgba(0,0,0,.9) +} + .listingblock>.content{ + position:relative +} + .listingblock code[data-lang]::before{ + display:none; + content:attr(data-lang); + position:absolute; + font-size:.75em; + top:.425rem; + right:.5rem; + line-height:1; + text-transform:uppercase; + color:inherit; + opacity:.5 +} + .listingblock:hover code[data-lang]::before{ + display:block +} + .listingblock.terminal pre .command::before{ + content:attr(data-prompt); + padding-right:.5em; + color:inherit; + opacity:.5 +} + .listingblock.terminal pre .command:not([data-prompt])::before{ + content:"$" +} + .listingblock pre.highlightjs{ + padding:0 +} + .listingblock pre.highlightjs>code{ + padding:1em; + border-radius:4px +} + .listingblock pre.prettyprint{ + border-width:0 +} + .prettyprint{ + background:#f7f7f8 +} + pre.prettyprint .linenums{ + line-height:1.45; + margin-left:2em +} + pre.prettyprint li{ + background:none; + list-style-type:inherit; + padding-left:0 +} + pre.prettyprint li code[data-lang]::before{ + opacity:1 +} + pre.prettyprint li:not(:first-child) code[data-lang]::before{ + display:none +} + table.linenotable{ + border-collapse:separate; + border:0; + margin-bottom:0; + background:none +} + table.linenotable td[class]{ + color:inherit; + vertical-align:top; + padding:0; + line-height:inherit; + white-space:normal +} + table.linenotable td.code{ + padding-left:.75em +} + table.linenotable td.linenos,pre.pygments .linenos{ + border-right:1px solid; + opacity:.35; + padding-right:.5em; + -webkit-user-select:none; + -moz-user-select:none; + -ms-user-select:none; + user-select:none +} + pre.pygments span.linenos{ + display:inline-block; + margin-right:.75em +} + .quoteblock{ + margin:0 1em 1.25em 1.5em; + display:table +} + .quoteblock:not(.excerpt)>.title{ + margin-left:-1.5em; + margin-bottom:.75em +} + .quoteblock blockquote,.quoteblock p{ + color:rgba(0,0,0,.85); + font-size:1.15rem; + line-height:1.75; + word-spacing:.1em; + letter-spacing:0; + font-style:italic; + text-align:justify +} + .quoteblock blockquote{ + margin:0; + padding:0; + border:0 +} + .quoteblock blockquote::before{ + content:"\201c"; + float:left; + font-size:2.75em; + font-weight:bold; + line-height:.6em; + margin-left:-.6em; + color:#7a2518; + text-shadow:0 1px 2px rgba(0,0,0,.1) +} + .quoteblock blockquote>.paragraph:last-child p{ + margin-bottom:0 +} + .quoteblock .attribution{ + margin-top:.75em; + margin-right:.5ex; + text-align:right +} + .verseblock{ + margin:0 1em 1.25em +} + .verseblock pre{ + font-family:"Open Sans","DejaVu Sans",sans-serif; + font-size:1.15rem; + color:rgba(0,0,0,.85); + font-weight:300; + text-rendering:optimizeLegibility +} + .verseblock pre strong{ + font-weight:400 +} + .verseblock .attribution{ + margin-top:1.25rem; + margin-left:.5ex +} + .quoteblock .attribution,.verseblock .attribution{ + font-size:.9375em; + line-height:1.45; + font-style:italic +} + .quoteblock .attribution br,.verseblock .attribution br{ + display:none +} + .quoteblock .attribution cite,.verseblock .attribution cite{ + display:block; + letter-spacing:-.025em; + color:rgba(0,0,0,.6) +} + .quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{ + display:none +} + .quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{ + line-height:1.6; + word-spacing:0 +} + .quoteblock.abstract{ + margin:0 1em 1.25em; + display:block +} + .quoteblock.abstract>.title{ + margin:0 0 .375em; + font-size:1.15em; + text-align:center +} + .quoteblock.excerpt>blockquote,.quoteblock .quoteblock{ + padding:0 0 .25em 1em; + border-left:.25em solid #dddddf +} + .quoteblock.excerpt,.quoteblock .quoteblock{ + margin-left:0 +} + .quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{ + color:inherit; + font-size:1.0625rem +} + .quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{ + color:inherit; + font-size:.85rem; + text-align:left; + margin-right:0 +} + p.tableblock:last-child{ + margin-bottom:0 +} + td.tableblock>.content{ + margin-bottom:1.25em; + word-wrap:anywhere +} + td.tableblock>.content>:last-child{ + margin-bottom:-1.25em +} + table.tableblock,th.tableblock,td.tableblock{ + border:0 solid #dedede +} + table.grid-all>*>tr>*{ + border-width:1px +} + table.grid-cols>*>tr>*{ + border-width:0 1px +} + table.grid-rows>*>tr>*{ + border-width:1px 0 +} + table.frame-all{ + border-width:1px +} + table.frame-ends{ + border-width:1px 0 +} + table.frame-sides{ + border-width:0 1px +} + table.frame-none>colgroup+*>:first-child>*,table.frame-sides>colgroup+*>:first-child>*{ + border-top-width:0 +} + table.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{ + border-bottom-width:0 +} + table.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{ + border-left-width:0 +} + table.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{ + border-right-width:0 +} + table.stripes-all>*>tr,table.stripes-odd>*>tr:nth-of-type(odd),table.stripes-even>*>tr:nth-of-type(even),table.stripes-hover>*>tr:hover{ + background:#f8f8f7 +} + th.halign-left,td.halign-left{ + text-align:left !important +} + th.halign-right,td.halign-right{ + text-align:right !important +} + th.halign-center,td.halign-center{ + text-align:center !important +} + th.valign-top,td.valign-top{ + vertical-align:top +} + th.valign-bottom,td.valign-bottom{ + vertical-align:bottom +} + th.valign-middle,td.valign-middle{ + vertical-align:middle +} + table thead th,table tfoot th{ + font-weight:bold +} + tbody tr th{ + background:#f7f8f7 +} + tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{ + color:rgba(0,0,0,.8); + font-weight:bold +} + p.tableblock>code:only-child{ + background:none; + padding:0 +} + p.tableblock{ + font-size:1em +} + ol{ + margin-left:1.75em +} + ul li ol{ + margin-left:1.5em +} + dl dd{ + margin-left:1.125em +} + dl dd:last-child,dl dd:last-child>:last-child{ + margin-bottom:0 +} + li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{ + margin-bottom:.250em +} + ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{ + list-style-type:none +} + ul.no-bullet,ol.no-bullet,ol.unnumbered{ + margin-left:.625em +} + ul.unstyled,ol.unstyled{ + margin-left:0 +} + li>p:empty:only-child::before{ + content:""; + display:inline-block +} + ul.checklist>li>p:first-child{ + margin-left:-1em +} + ul.checklist>li>p:first-child>.fa-square-o:first-child,ul.checklist>li>p:first-child>.fa-check-square-o:first-child{ + width:1.25em; + font-size:.8em; + position:relative; + bottom:.125em +} + ul.checklist>li>p:first-child>input[type=checkbox]:first-child{ + margin-right:.25em +} + ul.inline{ + display:flex; + flex-flow:row wrap; + list-style:none; + margin:0 0 .625em -1.25em +} + ul.inline>li{ + margin-left:1.25em +} + .unstyled dl dt{ + font-weight:400; + font-style:normal +} + ol.arabic{ + list-style-type:decimal +} + ol.decimal{ + list-style-type:decimal-leading-zero +} + ol.loweralpha{ + list-style-type:lower-alpha +} + ol.upperalpha{ + list-style-type:upper-alpha +} + ol.lowerroman{ + list-style-type:lower-roman +} + ol.upperroman{ + list-style-type:upper-roman +} + ol.lowergreek{ + list-style-type:lower-greek +} + .hdlist>table,.colist>table{ + border:0; + background:none +} + .hdlist>table>tbody>tr,.colist>table>tbody>tr{ + background:none +} + td.hdlist1,td.hdlist2{ + vertical-align:top; + padding:0 .625em +} + td.hdlist1{ + font-weight:bold; + padding-bottom:1.25em +} + td.hdlist2{ + word-wrap:anywhere +} + .literalblock+.colist,.listingblock+.colist{ + margin-top:-.5em +} + .colist td:not([class]):first-child{ + padding:.4em .75em 0; + line-height:1; + vertical-align:top +} + .colist td:not([class]):first-child img{ + max-width:none +} + .colist td:not([class]):last-child{ + padding:.25em 0 +} + .thumb,.th{ + line-height:0; + display:inline-block; + border:4px solid #fff; + box-shadow:0 0 0 1px #ddd +} + .imageblock.left{ + margin:.25em .625em 1.25em 0 +} + .imageblock.right{ + margin:.25em 0 1.25em .625em +} + .imageblock>.title{ + margin-bottom:0 +} + .imageblock.thumb,.imageblock.th{ + border-width:6px +} + .imageblock.thumb>.title,.imageblock.th>.title{ + padding:0 .125em +} + .image.left,.image.right{ + margin-top:.25em; + margin-bottom:.25em; + display:inline-block; + line-height:0 +} + .image.left{ + margin-right:.625em +} + .image.right{ + margin-left:.625em +} + a.image{ + text-decoration:none; + display:inline-block +} + a.image object{ + pointer-events:none +} + sup.footnote,sup.footnoteref{ + font-size:.875em; + position:static; + vertical-align:super +} + sup.footnote a,sup.footnoteref a{ + text-decoration:none +} + sup.footnote a:active,sup.footnoteref a:active{ + text-decoration:underline +} + #footnotes{ + padding-top:.75em; + padding-bottom:.75em; + margin-bottom:.625em +} + #footnotes hr{ + width:20%; + min-width:6.25em; + margin:-.25em 0 .75em; + border-width:1px 0 0 +} + #footnotes .footnote{ + padding:0 .375em 0 .225em; + line-height:1.3334; + font-size:.875em; + margin-left:1.2em; + margin-bottom:.2em +} + #footnotes .footnote a:first-of-type{ + font-weight:bold; + text-decoration:none; + margin-left:-1.05em +} + #footnotes .footnote:last-of-type{ + margin-bottom:0 +} + #content #footnotes{ + margin-top:-.625em; + margin-bottom:0; + padding:.75em 0 +} + div.unbreakable{ + page-break-inside:avoid +} + .big{ + font-size:larger +} + .small{ + font-size:smaller +} + .underline{ + text-decoration:underline +} + .overline{ + text-decoration:overline +} + .line-through{ + text-decoration:line-through +} + .aqua{ + color:#00bfbf +} + .aqua-background{ + background:#00fafa +} + .black{ + color:#000 +} + .black-background{ + background:#000 +} + .blue{ + color:#0000bf +} + .blue-background{ + background:#0000fa +} + .fuchsia{ + color:#bf00bf +} + .fuchsia-background{ + background:#fa00fa +} + .gray{ + color:#606060 +} + .gray-background{ + background:#7d7d7d +} + .green{ + color:#006000 +} + .green-background{ + background:#007d00 +} + .lime{ + color:#00bf00 +} + .lime-background{ + background:#00fa00 +} + .maroon{ + color:#600000 +} + .maroon-background{ + background:#7d0000 +} + .navy{ + color:#000060 +} + .navy-background{ + background:#00007d +} + .olive{ + color:#606000 +} + .olive-background{ + background:#7d7d00 +} + .purple{ + color:#600060 +} + .purple-background{ + background:#7d007d +} + .red{ + color:#bf0000 +} + .red-background{ + background:#fa0000 +} + .silver{ + color:#909090 +} + .silver-background{ + background:#bcbcbc +} + .teal{ + color:#006060 +} + .teal-background{ + background:#007d7d +} + .white{ + color:#bfbfbf +} + .white-background{ + background:#fafafa +} + .yellow{ + color:#bfbf00 +} + .yellow-background{ + background:#fafa00 +} + span.icon>.fa{ + cursor:default +} + a span.icon>.fa{ + cursor:inherit +} + .admonitionblock td.icon [class^="fa icon-"]{ + font-size:2.5em; + text-shadow:1px 1px 2px rgba(0,0,0,.5); + cursor:default +} + .admonitionblock td.icon .icon-note::before{ + content:"\f05a"; + color:#19407c +} + .admonitionblock td.icon .icon-tip::before{ + content:"\f0eb"; + text-shadow:1px 1px 2px rgba(155,155,0,.8); + color:#111 +} + .admonitionblock td.icon .icon-warning::before{ + content:"\f071"; + color:#bf6900 +} + .admonitionblock td.icon .icon-caution::before{ + content:"\f06d"; + color:#bf3400 +} + .admonitionblock td.icon .icon-important::before{ + content:"\f06a"; + color:#bf0000 +} + .conum[data-value]{ + display:inline-block; + color:#fff!important; + background:rgba(0,0,0,.8); + border-radius:50%; + text-align:center; + font-size:.75em; + width:1.67em; + height:1.67em; + line-height:1.67em; + font-family:"Open Sans","DejaVu Sans",sans-serif; + font-style:normal; + font-weight:bold +} + .conum[data-value] *{ + color:#fff!important +} + .conum[data-value]+b{ + display:none +} + .conum[data-value]::after{ + content:attr(data-value) +} + pre .conum[data-value]{ + position:relative; + top:-.125em +} + b.conum *{ + color:inherit!important +} + .conum:not([data-value]):empty{ + display:none +} + dt,th.tableblock,td.content,div.footnote{ + text-rendering:optimizeLegibility +} + h1,h2,p,td.content,span.alt,summary{ + letter-spacing:-.01em +} + p strong,td.content strong,div.footnote strong{ + letter-spacing:-.005em +} + p,blockquote,dt,td.content,td.hdlist1,span.alt,summary{ + font-size:1.0625rem +} + p{ + margin-bottom:1.25rem +} + .sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{ + font-size:1em +} + .exampleblock>.content{ + background:#fffef7; + border-color:#e0e0dc; + box-shadow:0 1px 4px #e0e0dc +} + .print-only{ + display:none!important +} + @page{ + margin:1.25cm .75cm +} + @media print{ + *{ + box-shadow:none!important; + text-shadow:none!important + } + html{ + font-size:80% + } + a{ + color:inherit!important; + text-decoration:underline!important + } + a.bare,a[href^="#"],a[href^="mailto:"]{ + text-decoration:none!important + } + a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{ + content:"(" attr(href) ")"; + display:inline-block; + font-size:.875em; + padding-left:.25em + } + abbr[title]{ + border-bottom:1px dotted + } + abbr[title]::after{ + content:" (" attr(title) ")" + } + pre,blockquote,tr,img,object,svg{ + page-break-inside:avoid + } + thead{ + display:table-header-group + } + svg{ + max-width:100% + } + p,blockquote,dt,td.content{ + font-size:1em; + orphans:3; + widows:3 + } + h2,h3,#toctitle,.sidebarblock>.content>.title{ + page-break-after:avoid + } + #header,#content,#footnotes,#footer{ + max-width:none + } + #toc,.sidebarblock,.exampleblock>.content{ + background:none!important + } + #toc{ + border-bottom:1px solid #dddddf!important; + padding-bottom:0!important + } + body.book #header{ + text-align:center + } + body.book #header>h1:first-child{ + border:0!important; + margin:2.5em 0 1em + } + body.book #header .details{ + border:0!important; + display:block; + padding:0!important + } + body.book #header .details span:first-child{ + margin-left:0!important + } + body.book #header .details br{ + display:block + } + body.book #header .details br+span::before{ + content:none!important + } + body.book #toc{ + border:0!important; + text-align:left!important; + padding:0!important; + margin:0!important + } + body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{ + page-break-before:always + } + .listingblock code[data-lang]::before{ + display:block + } + #footer{ + padding:0 .9375em + } + .hide-on-print{ + display:none!important + } + .print-only{ + display:block!important + } + .hide-for-print{ + display:none!important + } + .show-for-print{ + display:inherit!important + } +} + @media amzn-kf8,print{ + #header>h1:first-child{ + margin-top:1.25rem + } + .sect1{ + padding:0!important + } + .sect1+.sect1{ + border:0 + } + #footer{ + background:none + } + #footer-text{ + color:rgba(0,0,0,.6); + font-size:.9em + } +} + @media amzn-kf8{ + #header,#content,#footnotes,#footer{ + padding:0 + } +} + diff --git a/tooling/mathD100.lua b/tooling/mathD100.lua new file mode 100755 index 0000000..b6fdd38 --- /dev/null +++ b/tooling/mathD100.lua @@ -0,0 +1,249 @@ +-- +-- {{{ Digit Functions +--- Get sorted base-10 digits of a number +---@param n integer +---@return integer[] +local function get_sorted_digits(n) + local digits = {} + for d in tostring(math.abs(n)):gmatch("%d") do + table.insert(digits, tonumber(d)) + end + table.sort(digits) + return digits +end +-- }}} + +--{{{ print_r +local function print_r(t) + local print_r_cache = {} + local function sub_print_r(t, indent) + if (print_r_cache[tostring(t)]) then + print(indent .. "*" .. tostring(t)) + else + print_r_cache[tostring(t)] = true + if (type(t) == "table") then + local tLen = #t + for i = 1, tLen do + local val = t[i] + if (type(val) == "table") then + print(indent .. "#[" .. i .. "] => " .. tostring(t) .. " {") + sub_print_r(val, indent .. string.rep(" ", string.len(i) + 8)) + print(indent .. string.rep(" ", string.len(i) + 6) .. "}") + elseif (type(val) == "string") then + print(indent .. "#[" .. i .. '] => "' .. val .. '"') + else + print(indent .. "#[" .. i .. "] => " .. tostring(val)) + end + end + for pos, val in pairs(t) do + if type(pos) ~= "number" or math.floor(pos) ~= pos or (pos < 1 or pos > tLen) then + if (type(val) == "table") then + print(indent .. "[" .. pos .. "] => " .. tostring(t) .. " {") + sub_print_r(val, indent .. string.rep(" ", string.len(pos) + 8)) + print(indent .. string.rep(" ", string.len(pos) + 6) .. "}") + elseif (type(val) == "string") then + print(indent .. "[" .. pos .. '] => "' .. val .. '"') + else + print(indent .. "[" .. pos .. "] => " .. tostring(val)) + end + end + end + else + print(indent .. tostring(t)) + end + end + end + + if (type(t) == "table") then + print(tostring(t) .. " {") + sub_print_r(t, " ") + print("}") + else + sub_print_r(t, " ") + end + + print() +end +--}}} + +--{{{ table_count +function table_count(t) + local count = 0 + for _ in pairs(t) do count = count + 1 end + return count +end + +--}}} + +--{{{ range(low, high, n) +---Create a table with integers in a given range +---@param low integer Lowest number +---@param high integer Highest number +---@param n integer Increment - the array will contain every nth number between [low..high] +---@return table +local function range(low, high, n) + local t = {} + for i = low, high, n do + table.insert(t, i) + end + + return t +end +---}}} + +--{{{ sortMapByValueDesc(m) +---Sort the table m by value. +---@param m table +---@return table +local function sortMapByValueDesc(m) + local totalCount = 0 + local tmp = {} + + -- create a temporary table that is easier to sort + for damage, occurrences in pairs(m) do + local entry = { + damage = damage, + count = occurrences, + } + totalCount = totalCount + occurrences + table.insert(tmp, entry) + end + + table.sort(tmp, function(a, b) + return a.count > b.count + end) + + return tmp + + -- + -- local result = {} + -- for _, v in pairs(tmp) do + -- result[v.damage] = v.count + -- end + -- + -- + -- if table_count(result) ~= table_count(m) then + -- error("I couldn't sort!!") + -- end + -- + -- return result +end +--}}} + +--{{{table_sum(t) +---numeric sum of table values +---@param t integer[] +---@return integer +local function table_sum(t) + local result = 0 + for _, v in pairs(t) do + result = result + v + end + + return result +end +--}}} + +--{{{mostCommonDamageValues(instances, ratio) +--- Calculate the most likely values +---@param instances table A map of [damage score] => [occurrences] +---@param ratio number Include most common damage until the sum of the probabilities of the damage scores are at least this number. +---@return table +local function mostCommonDamageValues(instances, ratio) + if ratio == nil then + ratio = 0.5 + end + + local sortedInstances = sortMapByValueDesc(instances) + local totalOccurrences = table_sum(instances) + + local result = {} + local occurrenceAccumulator = 0 + + for _, instance in pairs(sortedInstances) do + occurrenceAccumulator = occurrenceAccumulator + instance.count + table.insert(result, instance.damage) + + if occurrenceAccumulator / totalOccurrences >= ratio then + return result + end + end + + return result +end +--}}} + +--{{{calculateDamageD100(skill, base_damage) +---Calculate the average of the d100 attack method +---@param skill integer The skill level of the attacker (range of 1 to 100, but will be capped to 20) +---@param base_damage integer The base damage of the attacker's weapon. +---@return number +---@return table mostRolled the damage values that are most likely to be rolled +local function calculateDamageD100(skill, base_damage) + local sum = 0 + local n = 0 + local tn = math.min(skill, 80) + local damages = {} + + for rolled = 1, 100, 1 do + local digits = get_sorted_digits(rolled) + + local damage = 0 + + if rolled > tn then + -- glancing blow: damage equal to weapon's base damage. + damage = base_damage + end + if rolled < tn then + -- normal hit: damage equal digit sum of what is rolled plus base damage. + damage = table_sum(digits) + base_damage + end + if rolled == tn then + -- Critical Hit: damage equal to what you rolled. + damage = rolled + end + + local key = tostring(damage) + n = n + 1 + sum = sum + damage + if not damages[key] then + damages[key] = 1 + else + damages[key] = damages[key] + 1 + end + end + + return sum / n, mostCommonDamageValues(damages, 0.5) +end +--}}} + +local skill_min = 30 +local skill_max = 80 +local skill_inc = 5 +local skills_table = range(skill_min, skill_max, skill_inc) + +local damage_min = 0 +local damage_max = 10 +local damage_inc = 3 +local damages_table = range(damage_min, damage_max, damage_inc) + +io.write("skill\\damage", ";") +for i, dmg in pairs(damages_table) do + io.write(string.format("%d (avg);%d (typical)", dmg, dmg)) + if i < #damages_table then + io.write(";") + end +end +print("") + + +for _, skill in pairs(skills_table) do + io.write(skill, ";") + + local tmp = {} + for key, damage in ipairs(damages_table) do + local avg, dmg = calculateDamageD100(skill, damage) + tmp[key] = string.format("%.2f;%s", avg, table.concat(dmg, ", ")) + end + print(table.concat(tmp, ";")) +end diff --git a/tooling/mathD20.lua b/tooling/mathD20.lua new file mode 100755 index 0000000..5a096f9 --- /dev/null +++ b/tooling/mathD20.lua @@ -0,0 +1,217 @@ +-- +--{{{ print_r(t) +local function print_r(t) + local print_r_cache = {} + local function sub_print_r(t, indent) + if (print_r_cache[tostring(t)]) then + print(indent .. "*" .. tostring(t)) + else + print_r_cache[tostring(t)] = true + if (type(t) == "table") then + local tLen = #t + for i = 1, tLen do + local val = t[i] + if (type(val) == "table") then + print(indent .. "#[" .. i .. "] => " .. tostring(t) .. " {") + sub_print_r(val, indent .. string.rep(" ", string.len(i) + 8)) + print(indent .. string.rep(" ", string.len(i) + 6) .. "}") + elseif (type(val) == "string") then + print(indent .. "#[" .. i .. '] => "' .. val .. '"') + else + print(indent .. "#[" .. i .. "] => " .. tostring(val)) + end + end + for pos, val in pairs(t) do + if type(pos) ~= "number" or math.floor(pos) ~= pos or (pos < 1 or pos > tLen) then + if (type(val) == "table") then + print(indent .. "[" .. pos .. "] => " .. tostring(t) .. " {") + sub_print_r(val, indent .. string.rep(" ", string.len(pos) + 8)) + print(indent .. string.rep(" ", string.len(pos) + 6) .. "}") + elseif (type(val) == "string") then + print(indent .. "[" .. pos .. '] => "' .. val .. '"') + else + print(indent .. "[" .. pos .. "] => " .. tostring(val)) + end + end + end + else + print(indent .. tostring(t)) + end + end + end + + if (type(t) == "table") then + print(tostring(t) .. " {") + sub_print_r(t, " ") + print("}") + else + sub_print_r(t, " ") + end + + print() +end +--}}} + +--{{{ table_count(t) +function table_count(t) + local count = 0 + for _ in pairs(t) do count = count + 1 end + return count +end + +--}}} + +--{{{ range(low, high, n) +---Create a table with integers in a given range +---@param low integer Lowest number +---@param high integer Highest number +---@param n integer Increment - the array will contain every nth number between [low..high] +---@return table +local function range(low, high, n) + local t = {} + for i = low, high, n do + table.insert(t, i) + end + + return t +end +---}}} + +-- {{{ sortMapByValueDesc(m) +---Sort the table m by value. +---@param m table +---@return table +local function sortMapByValueDesc(m) + local tmp = {} + + -- create a temporary table that is easier to sort + for damage, occurrences in pairs(m) do + local entry = { + damage = damage, + count = occurrences, + } + table.insert(tmp, entry) + end + + table.sort(tmp, function(a, b) + return a.count > b.count + end) + + return tmp +end +--}}} + +-- {{{ arraySum(t) +local function table_sum(t) + local accumulator = 0 + + for _, v in pairs(t) do + accumulator = accumulator + v + end + + return accumulator +end +---}}} + +--{{{ mostCommonDamageValues(instances, ratio) +--- Calculate the most likely values +---@param instances table A map of [damage score] => [occurrences] +---@param ratio number Include most common damage until the sum of the probabilities of the damage scores are at least this number. +---@return table +local function mostCommonDamageValues(instances, ratio) + if ratio == nil then + ratio = 0.25 + end + + local sortedInstances = sortMapByValueDesc(instances) + local totalOccurrences = table_sum(instances) + + local result = {} + local occurrenceAccumulator = 0 + + for _, instance in pairs(sortedInstances) do + occurrenceAccumulator = occurrenceAccumulator + instance.count + table.insert(result, instance.damage) + + if occurrenceAccumulator / totalOccurrences > ratio then + return result + end + end + + return result +end +--}}} + +-- {{{ calculateDamageD20(skill, base_damage) +---@param skill integer +---@param base_damage integer +---@return number average The average damage +---@return table typical The damage numbers that, together, are likely to occur more than 50% of the time. +local function calculateDamageD20(skill, base_damage) + local sum = 0 + local n = 0 + local tn = math.min(skill, 15) + + local outcomes = {} + + for rolled = 1, 20, 1 do + local damage = 0 + + if rolled > tn then + -- glancing blow + damage = base_damage + end + if rolled == tn then + -- crit + damage = 2 * (skill + base_damage) + end + if rolled < tn then + -- hit + damage = rolled + base_damage + end + + local key = tostring(damage) + + if outcomes[key] == nil then + outcomes[key] = 1 + else + outcomes[key] = outcomes[key] + 1 + end + + sum = sum + damage + n = n + 1 + end + + return sum / n, mostCommonDamageValues(outcomes, 0.5) +end +-- }}} + +local skill_min = 5 +local skill_max = 15 +local skill_inc = 1 +local skills_table = range(skill_min, skill_max, skill_inc) + +local damage_min = 0 +local damage_max = 10 +local damage_inc = 3 +local damages_table = range(damage_min, damage_max, damage_inc) + +io.write("skill\\damage", ";") +for i, dmg in pairs(damages_table) do + io.write(string.format("%d (avg);%d (typical)", dmg, dmg)) + if i < #damages_table then + io.write(";") + end +end +print("") + +for _, skill in pairs(skills_table) do + io.write(skill, ";") + + local tmp = {} + for key, damage in ipairs(damages_table) do + local avg, dmg = calculateDamageD20(skill, damage) + tmp[key] = string.format("%.2f;%s", avg, table.concat(dmg, ", ")) + end + print(table.concat(tmp, ";")) +end diff --git a/tooling/process.lua b/tooling/process.lua new file mode 100755 index 0000000..c39063d --- /dev/null +++ b/tooling/process.lua @@ -0,0 +1,68 @@ +-- Sort selected areas of a text file +-- +-- Each area has a number of sections, +-- and each section has a headline that +-- is used as a key. +-- +-- The program simply treats each section as a string, and treats each area as a +-- collection of strings. The strings are sorted such that the sections in +-- the area will appear in order. +-- +-- For now, only asciidoc (and markdown) headlines are supported, but +-- description lists are on the way +-- +-- TODO: +-- Variables and substitutions with evaluation via dostring +-- Syntax for settings vars could be +-- {{? mufassa = "tjams" }} +-- {{? mufassa tjams }} +-- +-- Syntax for inserting vars could be +-- {{:: string.upper(mufassa) }} +-- my little pony is {{: mufassa .. " næsehorn "}} +-- +-- Can assist in streamlining data across the three guides. +-- Can ease links. +-- +-- Maybe a quick var insert: +-- @@@mufassa (inserts the contents of mufassa variable) +-- + +local filename = arg[1] + +-- Check that a filename was given +if arg[1] == nil then + print(string.format("usage: %s [filename]", arg[0])) + os.exit(0) +end + +-- Check that file exists +local f = io.open(filename, "r+") +if not f then + print(string.format("file '%s' does not exist", filename)) + os.exit(1) +end + +local sorter = require("sorter") +local sorted = sorter(f:lines()) + +if arg[2] == "--overwrite" then + -- + -- Check if there were any changes to the file. + f:seek("set") + if sorted == f:read("a*") then + print("No sorting needed. Closing file") + f:close() + os.exit(0) + end + + f:seek("set") + f:write(sorted) + f:flush() + f:close() + os.exit(0) +end + +f:close() + +print(sorted) diff --git a/tooling/sorter.lua b/tooling/sorter.lua new file mode 100755 index 0000000..5f53548 --- /dev/null +++ b/tooling/sorter.lua @@ -0,0 +1,190 @@ +-- This program scans a text for REGIONS to sort. +-- +-- Each REGION contains the following: +-- A "START_SORT:" string command that also tells the sorter which types of HEADLINES the REGION contains. +-- (optionally) a number of PREFACE lines. +-- A number of SECTIONS to sort +-- An "END_SORT" command +-- +-- Each SECTION contains the following +-- A HEADLINE +-- A number of lines of body text. +-- +-- A HEADLINE can (currently) be one of: +-- - An asciidoc headline: a line that always begins with a number of '=' characters, followed by at least one normal text character. +-- These headlines are convenient because it allows us to easily alphabetize headlines in certain chapters +-- (for instance in chapters where spells, items, or abilities each have their own headline). +-- - A hidden headline. A text they always begins "//HEADLINE:", followed by a number of normal text characters. +-- These hidden headlines are useful when we want to alphabetize tables, lists, and other sections that do not contain +-- explicit headlines. +-- +-- +-- Each REGION has a number of SECTIONS, and each SECTION +-- has a HEADLINE. HEADLINES can be actual asciidoc headlines (strings beginning with a number of "=" characters) +-- or they can be pseudo-headlines that exist as comments inside the asciidoc file. +-- +-- +-- {{{ Some variables to help us +local output = "" -- Output buffer +local STATE_SCANNING = 0 -- State machine: scanning for START_SORT commands +local MODE_SORTING = 1 -- State machine: sorting, and looking for END_SORT commands. +local state = STATE_SCANNING -- State machine: current mode +local headlinePattern = "" -- If a line matches this pattern, is a section headline (a sort key) +local sections = {} -- The sections we're currently sorting. +local preface = "" -- contains the text in a chunk that comes before the first section headline +--}}} + +--{{{ _start_sort(line) +---Check if a line starts with START_SORT and then a key to look for. +---@param line string The line we're checking +--- +---@return boolean success Was the line correctly formatted? +---@return boolean start_sort Did this line contain a correct START_SORT command? +---@return string pattern The regex pattern to scan for to find the headline/key that each section starts with. +--- +local function _is_start_sort_command(line) + local match = string.match(line, "^//%s*START_SORT%s*(%S*)%s*$") + + if not match then + return true, false, "" + end + + -- + -- ASCIIDOC HEADLINE + -- + -- These types of headlines start with a number of "=" characters. + -- They sort REGIONS of text where each SECTION begins with an asciidoc headline + if string.sub(match, 1, 1) == "=" then + -- We are sorting by section names + -- The type of section we're sorting (i.e. its depth) is denoted by the given number '=' characters + -- Every time we encounter a section with the corresponding number of equal signs, we consider that a sorting key. + return true, true, "^" .. match .. "%s+" + end + + -- + -- HIDDEN HEADLINE + -- + -- These headlines begin with "//HEADLINE:" + -- They are comments inside the asciidoc source code so they do not show up in the rendered text. + -- Thus they sort REGIONS of text where each SECTION begins with a hidden headline + if match == "//HEADLINE:" then + -- We are sorting by custom keys. + -- This means that every time we encounter the string "HEADLINE:" we consider the next line a sorting key. + return true, true, "^%s*//HEADLINE:" + end + + return false, false, match +end +--}}} + +-- {{{ _state_machine_scan(line) +local function _state_machine_scan(line) + local success, is_start_sort_command, _headline_pattern = _is_start_sort_command(line) + + if not success then + local errMsg = string.format("Failed to parse sort key: '%s'", _headline_pattern) + print(errMsg) + error(errMsg) + end + + if is_start_sort_command then + state = MODE_SORTING + headlinePattern = _headline_pattern + sections = {} + preface = "" + end + + output = output .. line +end +--}}} + +--{{{ _state_machine_sort(line) +local function _state_machine_sort(line) + -- + -- NEW SECTION STARTED + -- + if string.match(line, headlinePattern) then -- We found a new section headline/sortkey + -- Start a new section + sections[#sections + 1] = line + + return + end + + -- + -- SORTED REGION ENDED + -- + if string.match(line, "^//%s*END_SORT") then + -- We have encountered a command to stop sorting. So we should stop sorting. + -- Each section within the current sorting area is a single string + -- Sort those strings to sort the entire area, + -- and compile/combine/join the strings into the output. + + table.sort(sections) + local sorted = table.concat(sections, "") + + -- Compile the resulting sorted text. + output = output .. preface .. sorted .. line + + -- Reset the state machine to start looking for new areas to sort. + state = STATE_SCANNING + sections = {} + preface = "" + + return + end + + -- SECTION PREFACE + -- + -- This happens if we have preface text before the first heading/sortkey + -- Preface text is paragraph text that comes after START_SORT, but before the + -- first sorting key/section is encountered. + if nil == sections[#sections] then + preface = preface .. line + + return + end + + -- This line is a normal line within the section. + -- Add it to the current section. + sections[#sections] = sections[#sections] .. line +end +-- }}} + +--{{{ _state_machine_process_single_line(line) +local function _state_machine_process_single_line(line) + line = line .. "\n" + + -- SCANNING + if state == STATE_SCANNING then + _state_machine_scan(line) + return + end + + -- + -- SORTING + -- + if state == MODE_SORTING then + _state_machine_sort(line) + return + end +end +--}}} + +--{{{ sorter(lines) +---Scan a file and sort the headings of the chunks marked for sorting +---@param lines Iterator +---@return string output sorted string +local function sorter(lines) + for line in lines do + _state_machine_process_single_line(line) + end + + if not (state == STATE_SCANNING and #sections == 0) then + error("you must be missing an END_SORT or similar") + end + + return output +end +--}}} + +return sorter