src/robots/rpg-shopkeeper.lisp @ 1a365d4ae7bc

Just go ahead and start the shopkeeper

We'll work on making it better when I'm less drunk.
author Steve Losh <steve@stevelosh.com>
date Wed, 18 Jan 2017 00:32:30 +0000
parents (none)
children d5c110b938cb
(in-package :magitek.robots.rpg-shopkeeper)
(named-readtables:in-readtable :chancery)


(eval-when (:compile-toplevel :load-toplevel :execute)
  ;;; please just end my life
  (defmacro ea (&body body)
    `(eval-when (:compile-toplevel :load-toplevel :execute)
       ,@body)))


;;;; General ------------------------------------------------------------------
(define-rule (bonus :distribution (:zipf :exponent 1.8))
  1 2 3 4 5)


(ea (defun str (symbol)
      (string-downcase (symbol-name symbol))))


;;;; Materials ----------------------------------------------------------------
(defclass* material ()
  (name multiplier))

(defun make-material (name multiplier)
  (make-instance 'material :name name :multiplier multiplier))

(defmethod print-object ((o material) s)
  (print-unreadable-object (o s :type t)
    (princ (material-name o) s)))

(defmacro define-material (name multiplier)
  `(defparameter ,(symb '* name '*)
     (make-material ,(string-downcase (substitute #\space #\- (symbol-name name)))
                    ,multiplier)))


(define-material iron 1.0)
(define-material steel 1.5)
(define-material silver 2.0)
(define-material meteoric-iron 4.0)
(define-material mithril 10.0)
(define-material adamantine 11.0)


(define-material cloth 1.0)
(define-material leather 1.5)
(define-material silk 2.0)
(define-material spider-silk 6.0)


(define-material pine 1.0) ; http://www.wood-database.com/wood-articles/bow-woods/
(define-material poplar 1.2)
(define-material walnut 1.3)
(define-material maple 2.0)
(define-material oak 3.0)
(define-material yew 5.0)
(define-material rosewood 10.0)


(define-rule (metal :distribution :zipf)
  *iron*
  *steel*
  *silver*
  *meteoric-iron*
  *mithril*
  *adamantine*)

(define-rule (textile :distribution :weighted)
  (1.00 *cloth*)
  (1.00 *leather*)
  (0.80 *silk*)
  (0.01 *spider-silk*))

(define-rule (wood :distribution (:zipf :exponent 0.8))
  *pine*
  *poplar*
  *walnut*
  *maple*
  *oak*
  *yew*
  *rosewood*)


;;;; Monsters -----------------------------------------------------------------
(defclass* monster ()
  (singular multiplier plural adjective))

(defun make-monster (singular multiplier plural adjective)
  (make-instance 'monster
    :singular singular
    :multiplier multiplier
    :plural plural
    :adjective adjective))

(defmethod print-object ((o monster) s)
  (print-unreadable-object (o s :type t)
    (princ (monster-singular o) s)))

(defmacro define-monster (singular multiplier plural adjective)
  `(defparameter ,(symb '* singular '*)
     (make-monster
       ,(str singular)
       ,multiplier
       ,(str plural)
       ,(str adjective))))


(define-monster goblin   1.00 goblins   goblin)
(define-monster kobold   1.00 kobolds   kobold)
(define-monster elf      1.00 elves     elven)
(define-monster dwarf    1.00 dwarves   dwarven)
(define-monster halfling 1.00 halflings halfling)
(define-monster gnome    1.00 gnomes    gnomish)
(define-monster gnoll    1.10 gnolls    gnollish)
(define-monster ogre     1.20 ogres     ogre)
(define-monster troll    1.60 trolls    troll)
(define-monster vampire  5.00 vampires  vampire)
(define-monster dragon   9.00 dragons   dragon)


(define-rule (monster :distribution :weighted)
  (1.00 *goblin*)
  (1.00 *kobold*)
  (1.00 *elf*)
  (1.00 *dwarf*)
  (1.00 *halfling*)
  (1.00 *gnome*)
  (1.00 *ogre*)
  (1.00 *troll*)
  (1.00 *gnoll*)
  (0.10 *vampire*)
  (0.01 *dragon*))


;;;; Roles --------------------------------------------------------------------
(define-rule role
  "cleric"
  "warrior"
  "wizard"
  "rogue")


;;;; Elements -----------------------------------------------------------------
(define-rule element
  ("ice"         "icy")
  ("fire"        "flaming")
  ("electricity" "electrified")
  ("poison"      "venomous"))


;;;; Armor --------------------------------------------------------------------
(define-rule armor-piece
  "scale mail"
  "ring mail"
  "chain mail"
  "plate mail")

(define-rule clothing-piece
  "robe"
  "shirt"
  "pants"
  "dress"
  "skirt")


(define-rule armor-enchantment
  (:protection-from monster)
  (:resistance element)
  (:bonus bonus))

(define-rule vanilla-armor
  (metal armor-piece)
  (textile clothing-piece))

(define-rule (armor :distribution :weighted)
  (2 (vanilla-armor nil))
  (1 (vanilla-armor armor-enchantment)))


(defgeneric enchanted-armor-description
  (base enchantment-type enchantment-arguments))

(defmethod enchanted-armor-description
    (base (e (eql :protection-from)) enchantment-args)
  (destructuring-bind (monster) enchantment-args
    (format nil "~A of protection from ~A" base (monster-plural monster))))

(defmethod enchanted-armor-description
    (base (e (eql :resistance)) enchantment-args)
  (destructuring-bind ((noun adjective)) enchantment-args
    (declare (ignore adjective))
    (format nil "~A of ~A resistance" base noun)))

(defmethod enchanted-armor-description
    (base (e (eql :bonus)) enchantment-args)
  (destructuring-bind (val) enchantment-args
    (format nil "+~D ~A" val base)))


(defun vanilla-armor-description (vanilla-armor)
  (destructuring-bind (material piece) vanilla-weapon
    (format nil "~A ~A" (material-name material) piece)))


(defun armor-description (armor)
  (destructuring-bind (vanilla enchant) armor
    (let ((vanilla-description (vanilla-armor-description vanilla)))
      (if enchant
        (enchanted-armor-description vanilla-description
                                     (first enchant)
                                     (rest enchant))
        vanilla-description))))


;;;; Weapons ------------------------------------------------------------------
(define-rule melee-weapon
  "dagger"
  "longsword"
  "short sword"
  "hand axe"
  "battleaxe"
  "spear"
  "halberd"
  "scythe"
  "scimitar"
  "lance"
  "hammer"
  "staff"
  "mace"
  "flail")

(define-rule (bow-weapon :distribution :weighted)
  (2 "shortbow")
  (2 "longbow")
  (1 "crossbow")
  (1 "compound bow"))


(define-rule weapon-enchantment
  (:slaying monster)
  (:element element)
  (:bonus bonus))


(define-rule vanilla-weapon
  (metal melee-weapon)
  (wood bow-weapon))

(define-rule (weapon :distribution :weighted)
  (1 (vanilla-weapon nil))
  (1 (vanilla-weapon weapon-enchantment)))


(defgeneric enchanted-weapon-description
  (base enchantment-type enchantment-arguments))

(defmethod enchanted-weapon-description
    (base (e (eql :slaying)) enchantment-args)
  (destructuring-bind (monster) enchantment-args
    (format nil "~A of ~A-slaying" base (monster-singular monster))))

(defmethod enchanted-weapon-description
    (base (e (eql :element)) enchantment-args)
  (destructuring-bind (element) enchantment-args
    (format nil "~A ~A" (second element) base)))

(defmethod enchanted-weapon-description
    (base (e (eql :bonus)) enchantment-args)
  (destructuring-bind (val) enchantment-args
    (format nil "+~D ~A" val base)))


(defun vanilla-weapon-description (vanilla-weapon)
  (destructuring-bind (material piece) vanilla-weapon
    (format nil "~A ~A" (material-name material) piece)))


(defun weapon-description (weapon)
  (destructuring-bind (vanilla enchant) weapon
    (let ((vanilla-description (vanilla-weapon-description vanilla)))
      (if enchant
        (enchanted-weapon-description vanilla-description
                                      (first enchant)
                                      (rest enchant))
        vanilla-description))))


;;;; Flavor -------------------------------------------------------------------
(defparameter *monster* nil)

(define-string sales-pitch
  "Only used once!"
  "Brand new!"
  ("I bought it from" [(eval (monster-adjective (monster))) a] role :. ".")
  "This is gonna go fast!")


;;;; Main ---------------------------------------------------------------------
(define-rule item
  (eval (weapon-description (weapon)))
  (eval (armor-description (armor))))

(define-string offer
  ("FOR SALE:" #\newline :.
   [item cap] #\newline :.
   sales-pitch))


;;;; API ----------------------------------------------------------------------
(defun random-string ()
  (offer))