Commit | Line | Data |
---|---|---|
46d528a4 MW |
1 | ;;; |
2 | ||
3 | (defun d (n &optional (k 1) &key (bias 0) best worst) | |
4 | (let ((rolls (sort (loop repeat k | |
5 | collect (1+ (max 0 (min (1- n) | |
6 | (+ (random n) bias))))) | |
7 | #'<))) | |
8 | (reduce #'+ (cond (best (subseq rolls (- k best))) | |
9 | (worst (subseq rolls 0 worst)) | |
10 | (t rolls))))) | |
11 | ||
12 | (defvar *dnd-alist* nil) | |
13 | ||
14 | (defun do-lookup (name default defaultp) | |
15 | (let ((item (assoc name *dnd-alist*))) | |
16 | (cond (item (cdr item)) | |
17 | (defaultp default) | |
18 | (t (error "Missing required item ~S." name))))) | |
19 | ||
20 | (defun lookup-list (name &optional (default nil defaultp)) | |
21 | (do-lookup name default defaultp)) | |
22 | ||
23 | (defun lookup (name &optional (default nil defaultp)) | |
24 | (car (do-lookup name (list default) defaultp))) | |
25 | ||
26 | (defun hp-from-hd (&optional (hd (lookup-list :hit-dice))) | |
27 | (destructuring-bind (dice &key (plus 0) (stars 0)) hd | |
28 | (declare (ignore stars)) | |
29 | (+ (cond ((zerop dice) 0) | |
30 | ((= dice 1/8) 1) | |
31 | ((= dice 1/4) (d 2)) | |
32 | ((= dice 1/2) (d 4)) | |
33 | ((and (integerp dice) (plusp dice)) (d 8 dice)) | |
34 | (t (error "Bad hit dice ~S." hd))) | |
35 | plus))) | |
36 | ||
37 | (defun hd-table-lookup (hd table) | |
38 | (flet ((hd<= (a b) | |
39 | (let ((aa (if (consp a) (car a) a)) | |
40 | (bb (if (consp b) (car b) b))) | |
41 | (or (< aa bb) | |
42 | (and (= aa bb) | |
43 | (or (consp a) | |
44 | (not (consp b)))))))) | |
45 | (loop for ((lo . hi) . rest) in table | |
46 | when (and (hd<= lo hd) | |
47 | (hd<= hd hi)) | |
48 | return rest | |
49 | finally (return nil)))) | |
50 | ||
51 | (let ((xp-table '((( 0 . (0 +)) 5 1) | |
52 | (( 1 . 1) 10 3) | |
53 | ((( 1 +) . (1 +)) 15 4) | |
54 | (( 2 . 2) 20 5) | |
55 | ((( 2 +) . (2 +)) 25 10) | |
56 | (( 3 . 3) 30 15) | |
57 | ((( 3 +) . (3 +)) 50 25) | |
58 | (( 4 . 4) 75 50) | |
59 | ((( 4 +) . (4 +)) 125 75) | |
60 | (( 5 . 5) 175 125) | |
61 | ((( 5 +) . (5 +)) 225 175) | |
62 | (( 6 . 6) 275 225) | |
63 | ((( 6 +) . (6 +)) 350 300) | |
64 | (( 7 . 7) 450 400) | |
65 | ((( 7 +) . (7 +)) 550 475) | |
66 | (( 8 . 8) 650 550) | |
67 | ((( 8 +) . (8 +)) 775 625) | |
68 | (( 9 . 9) 900 700) | |
69 | ((( 9 +) . 10) 1000 750) | |
70 | (((10 +) . 11) 1100 800) | |
71 | (((11 +) . 12) 1250 875) | |
72 | (((12 +) . 13) 1350 950) | |
73 | (((13 +) . 14) 1500 1000) | |
74 | (((14 +) . 15) 1650 1050) | |
75 | (((15 +) . 16) 1850 1100) | |
76 | (((16 +) . 17) 2000 1150) | |
77 | (((17 +) . 18) 2125 1350) | |
78 | (((18 +) . 19) 2250 1550) | |
79 | (((19 +) . 20) 2375 1800) | |
80 | (((20 +) . 21) 2500 2000)))) | |
81 | (defun xp-from-hd (&optional (hd (lookup-list :hit-dice))) | |
82 | (destructuring-bind (dice &key (plus 0) (stars 0)) hd | |
83 | (multiple-value-bind (hd-base hd-plus) | |
84 | (cond ((zerop plus) (values dice 0)) | |
85 | ((plusp plus) (values dice 1)) | |
86 | ((minusp plus) (values (1- dice) 1))) | |
87 | (let ((result (hd-table-lookup (if (zerop hd-plus) | |
88 | hd-base | |
89 | (list hd-base '+)) | |
90 | xp-table))) | |
91 | (if result | |
92 | (destructuring-bind (base bonus) result | |
93 | (+ base (* stars bonus))) | |
94 | (let ((steps (+ hd-base -21 hd-plus))) | |
95 | (+ 2500 | |
96 | (* 250 steps) | |
97 | (* (+ 2000 (* 250 steps)) stars))))))))) | |
98 | ||
99 | (let ((thac0-table '((( 0 . 1) . 19) | |
100 | ((( 1 . +) . 2) . 18) | |
101 | ((( 2 . +) . 3) . 17) | |
102 | ((( 3 . +) . 4) . 16) | |
103 | ((( 4 . +) . 5) . 15) | |
104 | ((( 5 . +) . 6) . 14) | |
105 | ((( 6 . +) . 7) . 13) | |
106 | ((( 7 . +) . 8) . 12) | |
107 | ((( 8 . +) . 9) . 11) | |
108 | ((( 9 . +) . 11) . 10) | |
109 | (((11 . +) . 13) . 9) | |
110 | (((13 . +) . 15) . 8) | |
111 | (((15 . +) . 17) . 7) | |
112 | (((17 . +) . 19) . 6) | |
113 | (((19 . +) . 21) . 5) | |
114 | (((21 . +) . 23) . 4) | |
115 | (((23 . +) . 25) . 3) | |
116 | (((25 . +) . 27) . 2) | |
117 | (((27 . +) . 29) . 1) | |
118 | (((29 . +) . 31) . 0) | |
119 | (((31 . +) . 33) . -1) | |
120 | (((33 . +) . 35) . -2)))) | |
121 | (defun thac0-from-hd (&optional (hd (lookup-list :hit-dice))) | |
122 | (destructuring-bind (dice &key (plus 0) (stars 0)) hd | |
123 | (declare (ignore stars)) | |
124 | (multiple-value-bind (hd-base hd-plus) | |
125 | (cond ((zerop plus) (values dice 0)) | |
126 | ((plusp plus) (values dice 1)) | |
127 | ((minusp plus) (values (1- dice) 1))) | |
128 | (or (hd-table-lookup (if (zerop hd-plus) | |
129 | hd-base | |
130 | (list hd-base '+)) | |
131 | thac0-table) | |
132 | -3))))) | |
133 | ||
134 | (defparameter monster-template | |
135 | `((:hit-dice :required) | |
136 | (:thac0 :list ,#'thac0-from-hd) | |
137 | (:hit-points :list ,#'hp-from-hd) | |
138 | (:experience-points :list ,#'xp-from-hd))) | |
139 | ||
140 | (defun apply-template (def tpl) | |
141 | (flet ((run (tag func) | |
142 | (unless (assoc tag *dnd-alist*) | |
143 | (push (cons tag (funcall func)) *dnd-alist*)))) | |
144 | (loop with *dnd-alist* = def | |
145 | for (tag key . tail) in tpl do | |
146 | (case key | |
147 | (:required (lookup-list tag)) | |
148 | (:eval (run tag (car tail))) | |
149 | (:list (run tag (lambda () (list (funcall (car tail)))))) | |
150 | (t (run tag key))) | |
151 | finally (return *dnd-alist*)))) | |
152 |