game.lisp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. (defvar *player-ship*)
  2. (defvar *sector*)
  3. (defun init-game-state ()
  4. (setq *sector* (make-sector :market (make-market :price-of-petrofuel 20
  5. :price-of-ammo 10
  6. :price-of-archeotech 1000
  7. :price-of-spice 50
  8. :price-of-gruel 5)
  9. :hazards NIL
  10. :boons NIL))
  11. (setq *player-ship* (make-player-ship :armor-val 10
  12. :rep-shield-val 10
  13. :warp-drive (list 1 5)
  14. :reactor-str 1
  15. :warp-field 1
  16. :weapons (list (make-weapon :name "Plamsa"
  17. :shield-dmg 3
  18. :hull-dmg 3
  19. :ammo-cost 5)
  20. (make-weapon :name "Mega Bolter"
  21. :shield-dmg 1
  22. :hull-dmg 2
  23. :ammo-cost 1)
  24. (make-weapon :name "Beam"
  25. :shield-dmg 1
  26. :hull-dmg 3
  27. :ammo-cost 3))
  28. :credits 1000
  29. :crew (make-crew :sanity-val 100
  30. :crew-members (loop for x in '(1 2 3 4)
  31. collect (make-uniq-crew-mem :name (make-crew-mem-name))))
  32. :inventory (make-player-inventory :petrofuel 20
  33. :gruel 20
  34. :spice 0
  35. :ammo 20
  36. :archeotech 0))))
  37. (defun new-game ()
  38. (init-game-state)
  39. (game-intro)
  40. (top-level-game-menu))
  41. (defun game-intro ()
  42. (cls)
  43. (format t "In the grim darkness of the far future, there is only COMMERCE...~%")
  44. (sleep 2)
  45. (format t "You embark across a bleak galaxy to ply your wares and discover untold riches!~%")
  46. (sleep 2)
  47. (format t *intro-ship*)
  48. (prompt-read ""))
  49. ;; Options for top level menu
  50. ;; This is the main "gameplay" interface
  51. (defvar *top-level-options-display* "
  52. Actions:
  53. 1 | Sector info | sei
  54. 2 | Ship info | si
  55. 3 | Trade | t
  56. 4 | Scout | s
  57. 5 | Leave | l
  58. ")
  59. (defun sector-info ()
  60. (format t "Called sector-info"))
  61. ;;; SHIP INFO ;;;
  62. (defun display-crew ()
  63. (let* ((crew-struct (crew-crew-members (player-ship-crew *player-ship*)))
  64. (crew-names
  65. (loop for member in crew-struct
  66. collect (list (uniq-crew-mem-name member) NIL NIL))))
  67. (format T "~%CREW DETAILS~%~%")
  68. (format-table T (list (list (crew-sanity-val (player-ship-crew *player-ship*)))) :column-label '("Sanity"))
  69. (format T "~%")
  70. (format-table T crew-names :column-label '("Name" "Buff" "Buff Amount"))))
  71. (defun display-inventory ()
  72. (let ((inventory-list (list
  73. (list "Credits" (player-ship-credits *player-ship*))
  74. (list "Petrofuel" (player-inventory-petrofuel (player-ship-inventory *player-ship*)))
  75. (list "Gruel" (player-inventory-gruel (player-ship-inventory *player-ship*)))
  76. (list "Spice" (player-inventory-spice (player-ship-inventory *player-ship*)))
  77. (list "Ammo" (player-inventory-ammo (player-ship-inventory *player-ship*)))
  78. (list "Archeotech" (player-inventory-archeotech (player-ship-inventory *player-ship*))))))
  79. (format T "~%INVENTORY~%")
  80. (format-table T inventory-list :column-label '("Resource" "Amount"))))
  81. (defun ship-info ()
  82. (display-crew)
  83. (display-inventory))
  84. ;;; SHIP INFO END ;;;
  85. ;;; TRADING ;;;
  86. (defparameter *trade-opt-lookup* (list (cons '1 'buy-menu)
  87. (cons 'b 'buy-menu)
  88. (cons '2 'sell-menu)
  89. (cons 's 'sell-menu)
  90. (cons '3 'display-prices)
  91. (cons 'd 'display-prices)
  92. (cons '4 'top-level-game-menu)
  93. (cons 'r 'top-level-game-menu)))
  94. (defvar *trade-menu-options-display* "
  95. Actions:
  96. 1 | Buy | b
  97. 2 | Sell | s
  98. 3 | Display Prices | d
  99. 4 | Return to top level | r
  100. ")
  101. (defun buy-transaction (resource quantity)
  102. "Do they actual purchase transaction, not intended to be called interactively"
  103. (let* ((available-player-funds (player-ship-credits *player-ship*))
  104. (inventory (player-ship-inventory *player-ship*))
  105. (price (funcall (symbol-function (find-symbol (string-upcase
  106. (concatenate 'string "market-price-of-" resource))))
  107. (sector-market *sector*)))
  108. (total-cost (* quantity price)))
  109. (if (> total-cost available-player-funds)
  110. (progn
  111. (format T "Not enough credits to buy ~A ~A at ~A credits~%" quantity resource price)
  112. (format T "~%PLAYER CREDITS: ~A~%" (player-ship-credits *player-ship*))
  113. (return-from buy-transaction NIL))
  114. (progn
  115. (let ((resource-sym (read-from-string resource))
  116. (minus-funds (lambda (amount)
  117. (let ((remainder (- available-player-funds amount)))
  118. (setf (player-ship-credits *player-ship*) remainder)))))
  119. (case resource-sym
  120. ;;; This is insanely annoying, and will need to be duplicated
  121. ;;; for the sell logic, but don't know how else to handle this here
  122. (gruel (progn
  123. (funcall minus-funds total-cost)
  124. (setf (player-inventory-gruel inventory)
  125. (+ (player-inventory-gruel inventory) quantity))))
  126. (archeotech (progn
  127. (funcall minus-funds total-cost)
  128. (setf (player-inventory-archeotech inventory)
  129. (+ (player-inventory-archeotech inventory) quantity))))
  130. (petrofuel (progn
  131. (funcall minus-funds total-cost)
  132. (setf (player-inventory-petrofuel inventory)
  133. (+ (player-inventory-petrofuel inventory) quantity))))
  134. (spice (progn
  135. (funcall minus-funds total-cost)
  136. (setf (player-inventory-spice inventory)
  137. (+ (player-inventory-spice inventory) quantity))))
  138. (ammo (progn
  139. (funcall minus-funds total-cost)
  140. (setf (player-inventory-ammo inventory)
  141. (+ (player-inventory-ammo inventory) quantity))))))
  142. (format T "Successfully purchased ~A ~A~%" quantity resource)))))
  143. (defun buy-menu ()
  144. (let ((item-to-buy (prompt-read "Enter a resource to buy: "))
  145. (quantity (parse-integer (prompt-read "Enter a quantity to buy: "))))
  146. (if (member item-to-buy '("gruel" "ammo" "petrofuel" "archeotech" "spice") :test #'string=)
  147. (progn
  148. (buy-transaction item-to-buy quantity)
  149. (trade-menu))))
  150. (defun sell-transaction (resource quantity)
  151. "Do the sale transaction, not intended to be called interactively"
  152. (let* ((available-player-funds (player-ship-credits *player-ship*))
  153. (inventory (player-ship-inventory *player-ship*))
  154. (available-player-resource (funcall (symbol-function (find-symbol (string-upcase
  155. (concatenate 'string "player-inventory-" resource))))
  156. inventory))
  157. (price (funcall (symbol-function (find-symbol (string-upcase
  158. (concatenate 'string "market-price-of-" resource))))
  159. (sector-market *sector*)))
  160. (total-profit (* quantity price)))
  161. (if (> quantity available-player-resource)
  162. (progn
  163. (format T "Not enough ~A to sell ~A. You have ~A~%" resource quantity available-player-resource)
  164. (return-from sell-transaction NIL))
  165. (progn
  166. (let ((resource-sym (read-from-string resource))
  167. (remove-resource (lambda (amount)
  168. (let ((new-credits (+ available-player-funds total-profit)))
  169. (setf (player-ship-credits *player-ship*) new-credits))
  170. (- available-player-resource amount)))) ; This is pretty convoluted
  171. ;;; remove-resource lambda is a pretty bad idea
  172. ;;; it is used to set the new credits amount and then return the amount needed to
  173. ;;; be removed from the resource in the player inventory. I did it this way
  174. ;;; to keep the logic concise, but it smells bad and is probably stupid
  175. (case resource-sym
  176. ;;; This is insanely annoying, and will need to be duplicated
  177. ;;; for the sell logic, but don't know how else to handle this here
  178. (gruel (progn
  179. (setf (player-inventory-gruel inventory) (funcall remove-resource quantity))))
  180. (petrofuel (progn
  181. (setf (player-inventory-petrofuel inventory) (funcall remove-resource quantity))))
  182. (spice (progn
  183. (setf (player-inventory-spice inventory) (funcall remove-resource quantity))))
  184. (ammo (progn
  185. (setf (player-inventory-ammo inventory) (funcall remove-resource quantity))))
  186. (archeotech (progn
  187. (setf (player-inventory-archeotech inventory) (funcall remove-resource quantity))))))
  188. (format T "Successfully sold ~A ~A~%" quantity resource)))))
  189. (defun sell-menu ()
  190. (let ((item-to-sell (prompt-read "Enter a resource to sell: "))
  191. (quantity (parse-integer (prompt-read "Enter a quantity to sell: "))))
  192. (if (member item-to-sell '("gruel" "ammo" "petrofuel" "archeotech" "spice") :test #'string=)
  193. (progn
  194. (sell-transaction item-to-sell quantity)
  195. (trade-menu))))
  196. (defun display-prices ()
  197. ;;; (funcall (symbol-function (find-symbol (string-upcase (concatenate 'string "market-price-of-" item)))) (sector-market *sector*)) ;;; A call by string reference method for function calls
  198. (let ((market-list (list
  199. (list "Petrofuel" (market-price-of-petrofuel (sector-market *sector*)))
  200. (list "Gruel" (market-price-of-gruel (sector-market *sector*)))
  201. (list "Spice" (market-price-of-spice (sector-market *sector*)))
  202. (list "Ammo" (market-price-of-ammo (sector-market *sector*)))
  203. (list "Archeotech" (market-price-of-archeotech (sector-market *sector*))))))
  204. (format T "~%PLAYER CREDITS: ~A~%" (player-ship-credits *player-ship*))
  205. (format T "~%MARKET PRICES~%")
  206. (format-table T market-list :column-label '("Resource" "Cost"))))
  207. (defun trade-menu ()
  208. (format t *trade-menu-options-display*)
  209. (let ((option (prompt-read "Enter an option: ")))
  210. (format t "~%")
  211. (handle-opt (read-from-string option) *trade-opt-lookup*))
  212. (trade-menu))
  213. ;;; END TRADING ;;;
  214. (defun scout ()
  215. (format t "Called scout"))
  216. (defun leave ()
  217. (format t "Called leave"))
  218. ;; Use lookup table to handle top level loop arguments
  219. ;; See: https://dnaeon.github.io/common-lisp-lookup-tables-alists-and-plists/
  220. (defparameter *opt-lookup* (list (cons 'sector-info 'sector-info)
  221. (cons '1 'sector-info)
  222. (cons 'sei 'sector-info)
  223. (cons 'ship-info 'ship-info)
  224. (cons 'si 'ship-info)
  225. (cons '2 'ship-info)
  226. (cons 'trade 'trade-menu)
  227. (cons 't 'trade-menu)
  228. (cons '3 'trade-menu)
  229. (cons 'scout 'scout)
  230. (cons 's 'scout)
  231. (cons '4 'scout)
  232. (cons 'leave 'leave)
  233. (cons 'l 'leave)
  234. (cons '5 'leave)))
  235. (defun handle-opt (opt lookup-table)
  236. (let ((handler (cdr (assoc opt lookup-table))))
  237. (if handler (funcall handler) (format t "Invalid opt~%~%"))))
  238. (defun top-level-game-menu ()
  239. (format t *top-level-options-display*)
  240. (let ((option (prompt-read "Enter an option: ")))
  241. (format t "~%")
  242. (handle-opt (read-from-string option) *opt-lookup*))
  243. (top-level-game-menu))