From e570e9ccd02d46e2c6fb8dbe72bdf729cb1a5d17 Mon Sep 17 00:00:00 2001 From: Howard Abrams Date: Sat, 5 Feb 2022 21:38:53 -0800 Subject: [PATCH] Pulling the Ironsworn changes to its own repo --- docs/ironsworn-rpg.org | 429 ----------------------------------------- 1 file changed, 429 deletions(-) delete mode 100644 docs/ironsworn-rpg.org diff --git a/docs/ironsworn-rpg.org b/docs/ironsworn-rpg.org deleted file mode 100644 index ec7d274..0000000 --- a/docs/ironsworn-rpg.org +++ /dev/null @@ -1,429 +0,0 @@ -#+TITLE: Ironsworn -#+AUTHOR: Howard X. Abrams -#+DATE: 2021-12-21 December -#+TAGS: rpg -#+PROPERTY: header-args:sh :tangle no -#+PROPERTY: header-args:emacs-lisp :tangle ../rpgdm-ironsworn.el -#+PROPERTY: header-args :results none :eval no-export :comments no - -#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil -#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil -#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js - -Can I make Ironsworn-specific interface and dice rolls? - -First let's load my previous code, assuming that we have added this directory to =load-path=: -#+BEGIN_SRC emacs-lisp -(require 'rpgdm) -#+END_SRC - -* Dice Roller -How should the results look like when rolling? Perhaps like this formatted string: - -#+BEGIN_SRC emacs-lisp - (defun rpgdm-ironsworn--results (action modifier one-challenge two-challenge) - (let* ((action-results (+ action modifier)) - (str-results (cond - ((and (> action-results one-challenge) (> action-results two-challenge)) - (propertize "Strong hit" 'face '(:foreground "green"))) - ((or (> action-results one-challenge) (> action-results two-challenge)) - (propertize "Weak hit" 'face '(:foreground "yellow"))) - (t (propertize "Miss" 'face '(:foreground "red"))))) - (matched-msg (if (= one-challenge two-challenge) - (propertize " ← Create a Twist" 'face '(:foreground "orange")) - ""))) - (format "%s %s %d %s %d %s %d %s %d %s" str-results - (propertize "::" 'face '(:foreground "#888")) - action - (propertize "+" 'face '(:foreground "#888")) - modifier - (propertize "→" 'face '(:foreground "#888")) - one-challenge - (propertize "/" 'face '(:foreground "#888")) - two-challenge matched-msg))) -#+END_SRC - -So the following should work: - -#+BEGIN_SRC emacs-lisp :tangle no -(rpgdm-ironsworn--results 3 2 4 1) -(rpgdm-ironsworn--results 3 2 8 1) -(rpgdm-ironsworn--results 3 2 8 6) -(rpgdm-ironsworn--results 3 2 6 6) -#+END_SRC - -The basic interface will query for a modifer, and then display the results: -#+BEGIN_SRC emacs-lisp :results silent - (defun rpgdm-ironsworn-roll (modifier) - "Display a Hit/Miss message based on comparing a d6 action - roll (added to MODIFIER) vs. two d10 challenge dice." - (interactive "nModifier: ") - (let ((one-challenge (rpgdm--roll-die 10)) - (two-challenge (rpgdm--roll-die 10)) - (action-roll (rpgdm--roll-die 6))) - (rpgdm-message (rpgdm-ironsworn--results action-roll modifier - one-challenge two-challenge)))) -#+END_SRC - -Rolling against the progress just means we need to request that value instead of rolling the d6: - -#+BEGIN_SRC emacs-lisp - (defun rpgdm-ironsworn-progress-roll (progress-value) - "Display a Hit/Miss message based on comparing the PROGRESS-VALUE - to rolling two d10 challenge dice." - (interactive "nCurrent Progress Value: ") - (let ((one-challenge (rpgdm--roll-die 10)) - (two-challenge (rpgdm--roll-die 10))) - (rpgdm-message (rpgdm-ironsworn--results progress-value 0 - one-challenge two-challenge)))) -#+END_SRC -* Characters -But what might be nice is to have a character sheet that has the default modifiers: - -Internal representation of a character should be pretty simple: -#+BEGIN_SRC emacs-lisp - (define-hash-table-test 'str-or-keys - (lambda (a b) - (string-equal - (downcase - (if (symbolp a) (symbol-name a) a)) - (downcase - (if (symbolp b) (symbol-name b) b)))) - - (lambda (s) (sxhash-equal (downcase - (if (symbolp s) (symbol-name s) s))))) - - (defvar rpgdm-ironsworn-character (make-hash-table :test 'str-or-keys) - "Stats and attributes for the currently loaded character") -#+END_SRC -And a function to set them all. We may want another function that could parse an Org table or something. -#+BEGIN_SRC emacs-lisp :results silent - (cl-defun rpgdm-ironsworn-character (&key edge heart iron shadow wits - (health 5) (spirit 5) (supply 5) - (momentum 2)) - "Store the player character's stats, as well as set up the defaults for the others." - (clrhash rpgdm-ironsworn-character) - ;; (setq rpgdm-ironsworn-character (make-hash-table :test 'str-or-keys)) - (puthash 'edge edge rpgdm-ironsworn-character) - (puthash 'heart heart rpgdm-ironsworn-character) - (puthash 'iron iron rpgdm-ironsworn-character) - (puthash 'shadow shadow rpgdm-ironsworn-character) - (puthash 'wits wits rpgdm-ironsworn-character) - - (puthash 'health health rpgdm-ironsworn-character) - (puthash 'spirit spirit rpgdm-ironsworn-character) - (puthash 'supply supply rpgdm-ironsworn-character) - (puthash 'momentum momentum rpgdm-ironsworn-character)) -#+END_SRC - -This allows us to define our character: - -#+BEGIN_SRC emacs-lisp :tangle no :results silent -(rpgdm-ironsworn-character :edge 1 :heart 2 :iron 1 :shadow 2 :wits 3) -#+END_SRC - -We need to modify the values stored. -#+BEGIN_SRC emacs-lisp :results silent - (defun rpgdm-ironsworn-adjust-health (health-adj) - "Increase or decrease the current character's health by HEALTH-ADJ." - (interactive "nHealth Adjustment: ") - (puthash 'health - (+ (gethash 'health rpgdm-ironsworn-character 5) health-adj) - rpgdm-ironsworn-character)) - - (defun rpgdm-ironsworn-adjust-spirit (spirit-adj) - "Increase or decrease the current character's spirit by SPIRIT-ADJ." - (interactive "nSpirit Adjustment: ") - (puthash 'spirit - (+ (gethash 'spirit rpgdm-ironsworn-character 5) spirit-adj) - rpgdm-ironsworn-character)) - - (defun rpgdm-ironsworn-adjust-supply (supply-adj) - "Increase or decrease the current character's supply by SUPPLY-ADJ." - (interactive "nSupply Adjustment: ") - (puthash 'supply - (+ (gethash 'supply rpgdm-ironsworn-character 5) supply-adj) - rpgdm-ironsworn-character)) - - (defun rpgdm-ironsworn-adjust-momentum (momentum-adj) - "Increase or decrease the current character's momentum by MOMENTUM-ADJ." - (interactive "nMomentum Adjustment: ") - (puthash 'momentum - (+ (gethash 'momentum rpgdm-ironsworn-character 5) momentum-adj) - rpgdm-ironsworn-character)) -#+END_SRC - -Perhaps we need a special way to display these changing stats? - -#+BEGIN_SRC emacs-lisp - (defun rpgdm-ironsworn--display-stat (stat) - (let* ((value (gethash stat rpgdm-ironsworn-character 5)) - (s-val (number-to-string value)) - (color (cond - ((< value 1) "red") - ((< value 3) "orange") - ((< value 4) "yellow") - (t "green")))) - (propertize s-val 'face `(:foreground ,color)))) - - (defun rpgdm-ironsworn-character-display () - "Easily display the character's stats and other things." - (interactive) - (rpgdm-message "Edge: %d Heart: %d Iron: %d Shadow: %d Wits: %d - Health: %s Spirit: %s Supply: %s Momentum: %d" - (gethash 'edge rpgdm-ironsworn-character 0) - (gethash 'heart rpgdm-ironsworn-character 0) - (gethash 'iron rpgdm-ironsworn-character 0) - (gethash 'shadow rpgdm-ironsworn-character 0) - (gethash 'wits rpgdm-ironsworn-character 0) - - (rpgdm-ironsworn--display-stat 'health) - (rpgdm-ironsworn--display-stat 'spirit) - (rpgdm-ironsworn--display-stat 'supply) - - (gethash 'momentum rpgdm-ironsworn-character 5))) -#+END_SRC -* Roll against Character Stats -Which allows us to create helper rolling functions: - -#+BEGIN_SRC emacs-lisp - (defun rpgdm-ironsworn-roll-stat (stat modifier) - "Roll an action based on a loaded character's STAT with a MODIFIER." - (interactive (list (completing-read "Stat Modifier: " '(Edge Heart Iron Shadow Wits)) - (read-string "Other Modifier: "))) - (let ((all-mods (+ (gethash stat rpgdm-ironsworn-character) - (string-to-number modifier)))) - (rpgdm-ironsworn-roll all-mods))) -#+END_SRC - -And we could have a function for each: -#+BEGIN_SRC emacs-lisp :results silent - (defun rpgdm-ironsworn-roll-edge (modifier) - "Roll an action based on a loaded character's Edge stat with a MODIFIER." - (interactive (list (read-string "Edge + Modifier: "))) - (rpgdm-ironsworn-roll-stat 'edge modifier)) - - (defun rpgdm-ironsworn-roll-heart (modifier) - "Roll an action based on a loaded character's Heart stat with a MODIFIER." - (interactive (list (read-string "Heart + Modifier: "))) - (rpgdm-ironsworn-roll-stat 'heart modifier)) - - (defun rpgdm-ironsworn-roll-iron (modifier) - "Roll an action based on a loaded character's Iron stat with a MODIFIER." - (interactive (list (read-string "Iron + Modifier: "))) - (rpgdm-ironsworn-roll-stat 'iron modifier)) - - (defun rpgdm-ironsworn-roll-shadow (modifier) - "Roll an action based on a loaded character's Shadow stat with a MODIFIER." - (interactive (list (read-string "Shadow + Modifier: "))) - (rpgdm-ironsworn-roll-stat 'shadow modifier)) - - (defun rpgdm-ironsworn-roll-wits (modifier) - "Roll an action based on a loaded character's Wits stat with a MODIFIER." - (interactive (list (read-string "Wits + Modifier: "))) - (rpgdm-ironsworn-roll-stat 'wits modifier)) -#+END_SRC -* Oracles -** Action-Theme -#+BEGIN_SRC emacs-lisp - (defun rpgdm-ironsworn-oracle-action-theme () - "Rolls on two tables at one time." - (interactive) - (let ((action (rpgdm-tables-choose "actions")) - (theme (rpgdm-tables-choose "themes"))) - (rpgdm-message "%s / %s" action theme))) -#+END_SRC -** Character -#+BEGIN_SRC emacs-lisp - (defun rpgdm-ironsworn-oracle-npc () - (interactive) - (let ((name (rpgdm-tables-choose "names-ironlander")) - (goal (rpgdm-tables-choose "character-goal")) - (role (rpgdm-tables-choose "character-role")) - (activity (rpgdm-tables-choose "character-activity")) - (description (rpgdm-tables-choose "character-descriptor")) - (disposition (rpgdm-tables-choose "character-disposition"))) - (rpgdm-message "%s, %s %s (Activity: %s Disposition: %s Goal: %s)" - name description role activity disposition goal))) - -#+END_SRC -** Combat Action -#+BEGIN_SRC emacs-lisp - (defun rpgdm-ironsworn-oracle-combat () - (interactive) - (let ((action (rpgdm-tables-choose "combat-action")) - (method (rpgdm-tables-choose "combat-event-method")) - (target (rpgdm-tables-choose "combat-event-target"))) - (rpgdm-message "%s %s or %s" method target action))) -#+END_SRC -** Feature -#+BEGIN_SRC emacs-lisp - (defun rpgdm-ironsworn-oracle-feature () - "Rolls on two tables at one time for a Site's feature." - (interactive) - (let ((aspect (rpgdm-tables-choose "feature-aspect")) - (focus (rpgdm-tables-choose "feature-focus"))) - (rpgdm-message "%s / %s" aspect focus))) -#+END_SRC -** Site Nature -#+BEGIN_SRC emacs-lisp - (defun rpgdm-ironsworn-oracle-site-nature () - "Rolls on two tables at one time for a random Site." - (interactive) - (let* ((theme (rpgdm-tables-choose "site-theme")) - (domain (rpgdm-tables-choose "site-domain")) - (place (downcase domain)) - (name (rpgdm-ironsworn-oracle-site-name place))) - (rpgdm-message "%s %s :: %s" theme domain name))) -#+END_SRC -** Site Name -Using the interesting random name generator from the Ironsworn: Delve source book. -Requires a =place-type= to help limit the values that can be in /place/ and then looks up the details on the tables in the =ironsworn= directory. - -#+BEGIN_SRC emacs-lisp - (defun rpgdm-ironsworn-oracle-site-name (&optional place-type) - "Rolling on multiple tables to return a random site name." - (interactive (list (completing-read "Place type: " - '(barrow cavern icereach mine pass ruin - sea-cave shadowfen stronghold - tanglewood underkeep)))) - (unless place-type - (setq place-type "unknown")) - (let ((description (rpgdm-tables-choose "site-name-description")) - (detail (rpgdm-tables-choose "site-name-detail")) - (namesake (rpgdm-tables-choose "site-name-namesake")) - (place (rpgdm-tables-choose (format "site-name-place-%s" place-type))) - (roll (rpgdm--roll-die 100))) - (rpgdm-message - (cond - ((<= roll 25) (format "%s %s" description place)) - ((<= roll 50) (format "%s of %s" place detail)) - ((<= roll 70) (format "%s of %s %s" place description detail)) - ((<= roll 80) (format "%s of %s's %s" place namesake detail)) - ((<= roll 85) (format "%s's %s" namesake place)) - ((<= roll 95) (format "%s %s of %s" description place namesake)) - (t (format "%s of %s" place namesake)))))) -#+END_SRC - -So, let's generate some random place names: -#+BEGIN_SRC emacs-lisp :tangle no -(rpgdm-ironsworn-oracle-site-name "barrow") ; "Tomb of Storms" -(rpgdm-ironsworn-oracle-site-name "cavern") ; "Lair of Khulan’s Truth" -(rpgdm-ironsworn-oracle-site-name "icereach") ; "Barrens of Erisia’s Winter" -(rpgdm-ironsworn-oracle-site-name "mine") ; "Lode of Ashen Lament" -(rpgdm-ironsworn-oracle-site-name "pass") ; "Sunken Highlands" -(rpgdm-ironsworn-oracle-site-name "ruin") ; "Sanctum of Khulan’s Truth" -(rpgdm-ironsworn-oracle-site-name "sea-cave") ; "Silent Caves" -(rpgdm-ironsworn-oracle-site-name "shadowfen") ; "Floodlands of Nightmare Despair" -(rpgdm-ironsworn-oracle-site-name "stronghold") ; "Crumbling Bastion" -(rpgdm-ironsworn-oracle-site-name "tanglewood") ; "Bramble of Endless Strife" -(rpgdm-ironsworn-oracle-site-name "underkeep") ; "Underkeep of Lament" -(rpgdm-ironsworn-oracle-site-name) ; "Sundered Mists of Khulan" -#+END_SRC -** Threat -Generate a random threat and its motivations. -#+BEGIN_SRC emacs-lisp - (defvar rpgdm-ironsworn-oracle-threats '("Burgeoning Conflict" "Ravaging Horde" - "Cursed Site" "Malignant Plague" - "Scheming Leader" "Zealous Cult" - "Environmental Calamity" "Power-Hungry Mystic" - "Rampaging Creature") - "A list of threats that correspond to tables") - - (defun rpgdm-ironsworn-oracle-threat-goal (&optional category) - "Given a CATEGORY, display a threat goal." - (interactive (list (completing-read "Threat: " rpgdm-ironsworn-oracle-threats))) - (unless category - (setq category (seq-random-elt rpgdm-ironsworn-oracle-threats))) - (let ((table-name (format "threat-%s" (downcase (string-replace " " "-" category))))) - (rpgdm-message "%s: %s" category (rpgdm-tables-choose table-name)))) -#+END_SRC - -And we can have a random threat: -#+BEGIN_SRC emacs-lisp -(rpgdm-ironsworn-oracle-threat-goal) -#+END_SRC -* Ironsworn Interface -Ironsworn introduces a simplified /flip-a-coin/ oracle, that might be nice to integrate. - -#+BEGIN_SRC emacs-lisp - (defun rpgdm-ironsworn-oracle () - "Given a LIKLIHOOD as a single character, return weighted coin flip." - (interactive) - (let* ((prompt "What are the odds? - c) Almost Certain l) Likely h) 50/50 u) Unlikely n) Small Chance ") - (odds (read-char prompt)) - (roll (rpgdm--roll-die 100)) - (yes! (when (or (and (= roll 11) (eq odds ?c)) - (and (= roll 26) (eq odds ?l)) - (and (= roll 51) (eq odds ?h)) - (and (= roll 76) (eq odds ?u)) - (and (= roll 91) (eq odds ?n))) - t)) - (yes (when (or (and (> roll 11) (eq odds ?c)) - (and (> roll 26) (eq odds ?l)) - (and (> roll 51) (eq odds ?h)) - (and (> roll 76) (eq odds ?u)) - (and (> roll 91) (eq odds ?n))) - t))) - (rpgdm-message "%s %s %s" - (if yes! "Extreme" "") - (if yes "Yes" "No") - (if yes! "or a twist." "")))) -#+END_SRC -I'd like the Hydra to be more specific to Ironsworn: - -#+BEGIN_SRC emacs-lisp :results silent - (defhydra hydra-rpgdm (:color blue :hint nil) - " - ^Dice^ ^Adjust^ ^Oracles/Tables^ ^Moving^ ^Messages^ - ---------------------------------------------------------------------------------------------------------------------------------------------------- - _d_: Roll Dice _D_: Progress Dice _H_: Health _z_: Yes/No Oracle _a_: Action/Theme _n_: NPC _o_: Links ⌘-‿: Show Stats - _e_: Roll Edge _s_: Roll Shadow _S_: Spirit _c_: Show Oracle _A_: Combat Action _f_: Feature _J_/_K_: Page up/dn ⌘-l: Last Results - _h_: Roll Heart _w_: Roll Wits _G_: Supply _O_: Other Oracle _p_: Place Name _P_: Place _N_/_W_: Narrow/Widen ⌘-k: ↑ Previous - _i_: Roll Iron _x_: Roll Stat _M_: Momentum _T_: Load Oracles _t_: Threat Goal ⌘-j: ↓ Next " - ("d" rpgdm-ironsworn-roll) ("D" rpgdm-ironsworn-progress-roll) - ("z" rpgdm-ironsworn-oracle) ("O" rpgdm-oracle) - - ("a" rpgdm-ironsworn-oracle-action-theme) - ("n" rpgdm-ironsworn-oracle-npc) - ("A" rpgdm-ironsworn-oracle-combat) - ("f" rpgdm-ironsworn-oracle-feature) - ("P" rpgdm-ironsworn-oracle-site-nature) - ("p" rpgdm-ironsworn-oracle-site-name) - ("t" rpgdm-ironsworn-oracle-threat-goal) - - ("e" rpgdm-ironsworn-roll-edge) - ("h" rpgdm-ironsworn-roll-heart) - ("i" rpgdm-ironsworn-roll-iron) - ("s" rpgdm-ironsworn-roll-shadow) - ("w" rpgdm-ironsworn-roll-wits) - ("x" rpgdm-ironsworn-roll-stat :color pink) - - ("H" rpgdm-ironsworn-adjust-health :color pink) - ("S" rpgdm-ironsworn-adjust-spirit :color pink) - ("G" rpgdm-ironsworn-adjust-supply :color pink) - ("M" rpgdm-ironsworn-adjust-momentum :color pink) - - ("T" rpgdm-tables-load) ("c" rpgdm-tables-choose) ("C" rpgdm-tables-choose :color pink) - ("o" ace-link) ("N" org-narrow-to-subtree) ("W" widen) - ("K" scroll-down :color pink) ("J" scroll-up :color pink) - - ("s-SPC" rpgdm-ironsworn-character-display) - ("C-m" rpgdm-last-results :color pink) - ("C-n" rpgdm-last-results-next :color pink) - ("C-p" rpgdm-last-results-previous :color pink) - ("s-l" rpgdm-last-results :color pink) - ("s-j" rpgdm-last-results-next :color pink) - ("s-k" rpgdm-last-results-previous :color pink) - - ("q" nil "quit") ("" nil)) -#+END_SRC - -* Summary -Funny that I wrote the code here before even playing the game. Hope I like playing it as much as hacking this out. - -#+BEGIN_SRC emacs-lisp -(provide 'rpgdm-ironsworn) -;;; rpgdm-ironsworn.el ends here -#+END_SRC