commit 81e11966696be67dae50646a4a7bb44d9f47eadd Author: Kim ravn Hansen Date: Sun Aug 31 10:08:06 2025 +0200 stuff 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