After a night or two of late hacking...
I have a semblance of code giving me functions to help me run my D&D games. The README needs a lot more love, but the code is generally tidy. Time to add some tables...
This commit is contained in:
commit
9411e512ce
5 changed files with 977 additions and 0 deletions
183
rpgdm.el
Normal file
183
rpgdm.el
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
;;; rpgdm.el --- Support utilities for the RPG Game Master -*- lexical-binding: t; -*-
|
||||
;;
|
||||
;; Copyright (C) 2021 Howard X. Abrams
|
||||
;;
|
||||
;; Author: Howard X. Abrams <http://gitlab.com/howardabrams>
|
||||
;; Maintainer: Howard X. Abrams <howard.abrams@workday.com>
|
||||
;; Created: January 4, 2021
|
||||
;;
|
||||
;; This file is not part of GNU Emacs.
|
||||
;;
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; This package includes all the help and support I can think for the Game
|
||||
;; Master running a role-playing game.
|
||||
;;
|
||||
;; This include a minor mode, `rpgdm' that adds a few keybindings useful
|
||||
;; with either org-mode or markdown-formatted files.
|
||||
;;
|
||||
;;; Code:
|
||||
|
||||
(defconst rpgdm-base (file-name-directory load-file-name))
|
||||
(load-file (expand-file-name "rpgdm-dice.el" rpgdm-base))
|
||||
(load-file (expand-file-name "rpgdm-screen.el" rpgdm-base))
|
||||
(load-file (expand-file-name "rpgdm-tables.el" rpgdm-base))
|
||||
|
||||
(defgroup rpgdm nil
|
||||
"Customization for the Dungeon Master support package."
|
||||
:prefix "rpgdm-"
|
||||
:group 'applications
|
||||
:link '(url-link :tag "Github" "https://gitlab.com/howardabrams/emacs-rpgdm"))
|
||||
|
||||
|
||||
(defun rpgdm-yes-and-50/50 ()
|
||||
"Add spice to your 50/50 events (luck) with Yes/No+complications.
|
||||
|
||||
The Freeform Universal RPG has the idea that you could succeed or
|
||||
fail, but have extra complications or extra bonuses. This returns
|
||||
one of six answers with equal frequency:
|
||||
|
||||
- No, and ... in other words, no luck, plus a minor complication.
|
||||
- No. ... Nope, no luck at this time.
|
||||
- No, but ... in other words, no luck, but something else good.
|
||||
- Yes, but ... you got what you wanted, but with a complication.
|
||||
- Yes. ... Yup, luck is on your side.
|
||||
- Yes, and ... Yes, plus you get a little something-something.
|
||||
|
||||
https://www.drivethrurpg.com/product/89534/FU-The-Freeform-Universal-RPG-Classic-rules"
|
||||
(interactive)
|
||||
(let (rolled (rpgdm--roll-die 6))
|
||||
(cond ((= rolled 1) "No, and... (fails badly that you add a complication)")
|
||||
((= rolled 2) "No.")
|
||||
((= rolled 3) "No, but... (fails, but add a little bonus or consolation prize)")
|
||||
((= rolled 4) "Yes, but... (succeeds, but add a complication or caveat)")
|
||||
((= rolled 5) "Yes.")
|
||||
(t "Yes, and... (succeeds, plus add a litle extra something-something)"))))
|
||||
|
||||
;; ----------------------------------------------------------------------
|
||||
;; SKILL CHECKS
|
||||
;; ----------------------------------------------------------------------
|
||||
;; I would like to have a function that
|
||||
|
||||
(defun rpgdm--skill-level-dice (number-of-dice)
|
||||
"Return a random skill challenge level.
|
||||
The formula is based on the NUMBER-OF-DICE. According to the
|
||||
Players Handbook in Dungeons and Dragons, we have this table
|
||||
to determine difficulty skill check levels:
|
||||
|
||||
- Very easy 5
|
||||
- Easy 10
|
||||
- Medium 15
|
||||
- Hard 20
|
||||
- Very hard 25
|
||||
- Nearly impossible 30
|
||||
|
||||
But I read somewhere that you could roll some 6 sided die to help
|
||||
add a bit of randomness to the leve setting. Essentially, roll
|
||||
the 6d and add 7.
|
||||
|
||||
Easy -- Die: 1 Lowest: 8 Highest: 13 Average: 10
|
||||
Medium -- Die: 2 Lowest: 9 Highest: 19 Average: 14
|
||||
Hard -- Die: 3 Lowest: 10 Highest: 25 Average: 17
|
||||
Very hard -- Die: 4 Lowest: 11 Highest: 30 Average: 20
|
||||
Nearly Impossible -- Die: 5 Lowest: 14 Highest: 35 Average: 24"
|
||||
(rpgdm--sum
|
||||
(rpgdm--roll number-of-dice 6 7)))
|
||||
|
||||
;; Let's verify my assumptions:
|
||||
;; (dolist (die '(0 1 2 3 4 5))
|
||||
;; (let* ((rolls (repeatedly 'rpgdm--skill-level-dice (list die) 1000))
|
||||
;; (highest (apply 'max rolls))
|
||||
;; (lowest (apply 'min rolls))
|
||||
;; (average (/ (-sum rolls) 1000)))
|
||||
;; (message
|
||||
;; (format "Die: %d Lowest: %d Highest: %d Average: %d\n" die lowest highest average))))
|
||||
|
||||
(defun rpgdm--skill-level (target)
|
||||
"Return a skill challenge level by Interpreting TARGET.
|
||||
This parameter can be a symbol or string for 'easy', 'hard', etc.
|
||||
Or it can be an actual number."
|
||||
(when (symbolp target)
|
||||
(setq target (symbol-name target)))
|
||||
(cond ((string-prefix-p target "trivial" t) 5)
|
||||
((string-prefix-p target "easy" t) (rpgdm--skill-level-dice 1))
|
||||
((string-prefix-p target "moderate" t) (rpgdm--skill-level-dice 2))
|
||||
((string-prefix-p target "medium" t) (rpgdm--skill-level-dice 2))
|
||||
((string-prefix-p target "hard" t) (rpgdm--skill-level-dice 3))
|
||||
((string-prefix-p target "difficult" t) (rpgdm--skill-level-dice 4))
|
||||
((string-prefix-p target "very hard" t) (rpgdm--skill-level-dice 4))
|
||||
((string-prefix-p target "impossible" t) (rpgdm--skill-level-dice 5))
|
||||
((numberp target) target)
|
||||
(t (max (string-to-number target) 12))))
|
||||
|
||||
(defun rpgdm--yes-and (target rolled-results)
|
||||
"Instead of returning a pass/fail, return 'Yes, but' strings.
|
||||
|
||||
The Freeform Universal RPG has the idea that you could succeed or
|
||||
fail, but have extra complications or extra bonuses based on how
|
||||
high/low you pass/fail. I have expanded the idea with a d20, so
|
||||
given a TARGET number, like '12', and the ROLLED-RESULTS from the
|
||||
player, this returns a string based on a table.
|
||||
|
||||
https://www.drivethrurpg.com/product/89534/FU-The-Freeform-Universal-RPG-Classic-rules"
|
||||
(cond ((< rolled-results (- target 7)) "No, and... !!")
|
||||
((< rolled-results (- target 3)) "No.")
|
||||
((< rolled-results target) "No, but...")
|
||||
((< rolled-results (+ target 3)) "Yes, but...")
|
||||
((< rolled-results (+ target 7)) "Yes.")
|
||||
(t "Yes, and... !!")))
|
||||
|
||||
(ert-deftest rpgdm--yes-and-test ()
|
||||
(should (equal (rpgdm--yes-and 10 1) "No, and..."))
|
||||
(should (equal (rpgdm--yes-and 10 2) "No, and..."))
|
||||
(should (equal (rpgdm--yes-and 10 3) "No."))
|
||||
(should (equal (rpgdm--yes-and 10 4) "No."))
|
||||
(should (equal (rpgdm--yes-and 10 5) "No."))
|
||||
(should (equal (rpgdm--yes-and 10 6) "No."))
|
||||
(should (equal (rpgdm--yes-and 10 7) "No, but..."))
|
||||
(should (equal (rpgdm--yes-and 10 8) "No, but..."))
|
||||
(should (equal (rpgdm--yes-and 10 9) "No, but..."))
|
||||
(should (equal (rpgdm--yes-and 10 10) "Yes, but..."))
|
||||
(should (equal (rpgdm--yes-and 10 11) "Yes, but..."))
|
||||
(should (equal (rpgdm--yes-and 10 12) "Yes, but..."))
|
||||
(should (equal (rpgdm--yes-and 10 13) "Yes."))
|
||||
(should (equal (rpgdm--yes-and 10 14) "Yes."))
|
||||
(should (equal (rpgdm--yes-and 10 15) "Yes."))
|
||||
(should (equal (rpgdm--yes-and 10 16) "Yes."))
|
||||
(should (equal (rpgdm--yes-and 10 17) "Yes, and...")))
|
||||
|
||||
(defun rpgdm-skill-check (target rolled-results)
|
||||
"Given a TARGET skill check, and ROLLED-RESULTS, return pass/fail.
|
||||
The string can return a bit of complications, from `rpgdm--yes-and'."
|
||||
(interactive (list (completing-read "Target Level: "
|
||||
'(Trivial Easy Moderate Hard Difficult Impossible))
|
||||
(read-number "Rolled Results: ")))
|
||||
(message (rpgdm--yes-and target rolled-results)))
|
||||
|
||||
(defun rpgdm-skill-check-easy (rolled-results)
|
||||
"Return an embellished pass/fail from ROLLED-RESULTS for an easy skill check."
|
||||
(interactive "nRolled Results: ")
|
||||
(rpgdm-skill-check (rpgdm--skill-level 'easy) rolled-results))
|
||||
|
||||
(defun rpgdm-skill-check-moderate (rolled-results)
|
||||
"Return an embellished pass/fail from ROLLED-RESULTS for a moderately-difficult skill check."
|
||||
(interactive "nRolled Results: ")
|
||||
(rpgdm-skill-check (rpgdm--skill-level 'medium) rolled-results))
|
||||
|
||||
(defun rpgdm-skill-check-hard (rolled-results)
|
||||
"Return an embellished pass/fail from ROLLED-RESULTS for a hard skill check."
|
||||
(interactive "nRolled Results: ")
|
||||
(rpgdm-skill-check (rpgdm--skill-level 'hard) rolled-results))
|
||||
|
||||
(defun rpgdm-skill-check-difficult (rolled-results)
|
||||
"Return an embellished pass/fail from ROLLED-RESULTS for a difficult skill check."
|
||||
(interactive "nRolled Results: ")
|
||||
(rpgdm-skill-check (rpgdm--skill-level 'difficult) rolled-results))
|
||||
|
||||
(defun rpgdm-skill-check-impossible (rolled-results)
|
||||
"Return an embellished pass/fail from ROLLED-RESULTS for an almost impossible skill check."
|
||||
(interactive "nRolled Results: ")
|
||||
(rpgdm-skill-check (rpgdm--skill-level 'impossible) rolled-results))
|
||||
|
||||
(provide 'rpgdm)
|
||||
;;; rpgdm.el ends here
|
||||
Loading…
Add table
Add a link
Reference in a new issue