overworld.lisp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. ;;;; Copyright ยฉ 2023, Jaidyn Ann <jadedctrl@posteo.at>
  2. ;;;;
  3. ;;;; This program is free software: you can redistribute it and/or
  4. ;;;; modify it under the terms of the GNU General Public License as
  5. ;;;; published by the Free Software Foundation, either version 3 of
  6. ;;;; the License, or (at your option) any later version.
  7. ;;;;
  8. ;;;; This program is distributed in the hope that it will be useful,
  9. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. ;;;; GNU General Public License for more details.
  12. ;;;;
  13. ;;;; You should have received a copy of the GNU General Public License
  14. ;;;; along with this program. If not, see <https://www.gnu.org/licenses/>.
  15. ;;;; FLORA-SEARCH-AURORA.OVERWORLD ๐ŸŒ
  16. ;;;; All game-functions and data relating to the โ€œoverworldโ€ (that is,
  17. ;;;; the primary gameplay, the RPG-ish-ish bits).
  18. (in-package :flora-search-aurora.overworld)
  19. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  20. ;;; Misc. Utils
  21. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  22. (defun within-rectangle (point top-left-corner bottom-right-corner)
  23. "With three coordinate plists, determine whether or not POINT resides within a
  24. rectangle as defined by its TOP-LEFT-CORNER & BOTTOM-RIGHT-CORNER."
  25. (and (<= (getf point :x) (getf bottom-right-corner :x))
  26. (>= (getf point :x) (getf top-left-corner :x))
  27. (<= (getf point :y) (getf bottom-right-corner :y))
  28. (>= (getf point :y) (getf top-left-corner :y))))
  29. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  30. ;;; Accessors
  31. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  32. (defmacro getf-entity (map entity-id)
  33. "Get an entity from the map-data, using its ID."
  34. `(mapcan (lambda (chunk) (assoc ,entity-id (cdr chunk)))
  35. (gethash :entities ,map)))
  36. (defmacro getf-entity-data (map entity-id key)
  37. "Get a specific piece of data from the given entity's property-list."
  38. `(getf (cdr (mapcan (lambda (chunk) (assoc ,entity-id (cdr chunk)))
  39. (gethash :entities ,map)))
  40. ,key))
  41. (defun removef-entity (map entity-id)
  42. "Remove an entity of the given ID from the map entirely. Nuke โ€˜em!
  43. Literally kill them, show no mercy, dig your sharp nails into their fleshy
  44. stomache and PULL HARD, show NO REMORSE. RAAAAAA ๐Ÿ—ก๐Ÿฉธ"
  45. (mapcar (lambda (chunk-alist)
  46. (โ€ฆ:remove-from-alistf entity-id (cdr chunk-alist)))
  47. (gethash :entities map)))
  48. (defmacro aget-item (map item)
  49. "Get an item from the MAPโ€™s :ITEMS alist. That is, an item in userโ€™s inventory.
  50. Members of :ITEMS will not be persistent beween play-throughs; the user has to
  51. get everything again."
  52. `(assoc-utils:aget (gethash :items ,map) ,item))
  53. (defmacro getf-act (map act)
  54. "Get an ACT from the MAPโ€™s :ACTS plist. That is, some marker indicating that
  55. the user has done something. Just like :ITEMS, these are not persistent through
  56. replays of the game."
  57. `(getf (gethash :acts ,map) ,act))
  58. (defmacro getf-know (map idea)
  59. "Get an item from the MAPโ€™s :KNOWS plist. That is, some marker indicating that
  60. the user knows something. Unlike :ITEMS and :ACTS, these _are_ persistent through
  61. replays of the game."
  62. `(getf (gethash :knows ,map) ,idea))
  63. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  64. ;;; Item searching/testing
  65. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  66. (defun entities-near-coords (coords radius entities &key (x-radius radius) (y-radius radius))
  67. "Return a list of entity-plists that are near the given coordinates within the given RADIUS."
  68. (remove-if-not
  69. (lambda (test-entity)
  70. (let ((test-coords (getf (cdr test-entity) :coords)))
  71. (and (< (abs (- (getf coords :x)
  72. (getf test-coords :x)))
  73. x-radius)
  74. (< (abs (- (getf coords :y)
  75. (getf test-coords :y)))
  76. y-radius))))
  77. (cdr (assoc (world-coords-chunk coords) entities))))
  78. (defun entities-near-entity (entity entities)
  79. "Return a new list of entities near the given ENTITY โ€” that is, within touching-distance."
  80. (remove-if
  81. (lambda (test-entity)
  82. (โ€ฆ:plist= (cdr entity)
  83. (cdr test-entity)))
  84. (entities-near-coords (getf (cdr entity) :coords)
  85. (+ (length (getf (cdr entity) :face)) 6)
  86. entities
  87. :y-radius 4)))
  88. (defun cell-at-world-coords-p (map-chunks coords)
  89. "Return whether or not there is a cell at the given COORDS."
  90. (let ((chunk (world-coords-chunk coords)))
  91. (member 't (cdr (assoc chunk map-chunks))
  92. :test (lambda (ignored cell)
  93. (โ€ฆ:plist= (list :x (getf cell :x) :y (getf cell :y))
  94. coords)))))
  95. (defun walkable-tile-p (map x y)
  96. "Return whether or not the given coordinates on the MAP are traversable for an entity."
  97. (not (cell-at-world-coords-p (gethash :bump-map map)
  98. (list :x x :y y))))
  99. (defun trigger-at-coords (map world-coords)
  100. "Return a โ€œTriggerโ€-rectangle from MAP thatโ€™d be triggered at the given coords."
  101. (let ((chunk (world-coords-chunk world-coords)))
  102. (loop for trigger in (cdr (assoc chunk (gethash :triggers map)))
  103. do (when (within-rectangle world-coords
  104. (getf trigger :coords) (getf trigger :bottom-coords))
  105. (return trigger)))))
  106. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  107. ;;; Map conversions & manipulations
  108. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  109. (defun merge-maps (map-a map-b)
  110. "Copy data that should be persistent between maps from map-a to map-b.
  111. Used primarily in moving between different maps in an overworld state."
  112. ;; Copy over important game-data from map-a.
  113. (mapcar
  114. (lambda (map-key)
  115. (setf (gethash map-key map-b) (gethash map-key map-a)))
  116. '(:acts :knows :items :seconds :day))
  117. ;; Copy specific bits of player data from map-aโ€™s :ENTITIES.
  118. (mapcar
  119. (lambda (player-key)
  120. (setf (getf-entity-data map-b 'โœฟ:player player-key)
  121. (getf-entity-data map-a 'โœฟ:player player-key)))
  122. '(:face :normal-face :talking-face))
  123. map-b)
  124. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  125. ;;; Overworld logic
  126. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  127. (defun overworld-state-update (map ฮ”t)
  128. "Do nothing, lol. Core part of OVERWORLD-STATE.
  129. Returns parameters to be used in the next invocation of OVERWORLD-STATE."
  130. (let ((time-result (process-overworld-time map ฮ”t)))
  131. (if time-result
  132. time-result
  133. (process-overworld-input map))))
  134. (defun seconds->game-datetime (seconds &key (game-day-length 240))
  135. "Convert real-world SECONDS into a datetime plist, calculating with
  136. GAME-DAY-LENGTH as as the seconds-per-day.
  137. Returns a plist of properties :DAY, :HOUR, and :MINUTE, all numbers."
  138. (let* ((game-days (floor (/ seconds game-day-length))) ;; Days passed in game-time
  139. (seconds (floor (- seconds (* game-days game-day-length)))) ;; Keep hour below 24!
  140. (real-day-length 1440)) ;; You know what I mean <w<
  141. (multiple-value-bind (hour minutes-fraction)
  142. (floor (/ (* seconds (/ real-day-length game-day-length))
  143. 60))
  144. (list :day game-days :hour hour
  145. :minute (floor (* 60 minutes-fraction))))))
  146. (defun end-game-string (map)
  147. (str:concat
  148. (if (getf-act map :encourage-scientist)
  149. (โ€ฆ:getf-lang '(:en "The cities of Etteburg and Bigborough live in peace. Doctor Klara Tim reached new heights in her professional career."
  150. :eo "La urboj de Etburgo kaj Egburo apudvivas pace. Doktoro Klara Tim atingis altojn en sia kariero, plimemfide."))
  151. (โ€ฆ:getf-lang '(:en "The city of Etteburg was nearly completely destroyed by the neighboring city Bigborough's police force, which claimed the city as its own.")))
  152. " "
  153. (if (getf-act map :perfect-friendship)
  154. (โ€ฆ:getf-lang '(:en "Friendship with Sasha blossoms, and the two are closer than ever before. She no longer broods by the cliffside, but has reintegrated into society. What an impactful flower, huh?"))
  155. (if (getf-act map :encourage-friendship)
  156. (โ€ฆ:getf-lang '(:en "Friendship with Sasha is better than ever before, yet still somewhat distant. Often, Sasha returns to the cliffside."))
  157. (โ€ฆ:getf-lang '(:en "To this day, Sasha broods by the cliffside alone."))))))
  158. (defun end-game (map)
  159. (setf flora-search-aurora:*knows* (gethash :knows map))
  160. (๐ŸŽญ:make-intermission-state
  161. '(:eo "LUDO FINITA" :en "GAME OVER")
  162. '(:en "Where are they now?")
  163. (list :en (end-game-string map))
  164. (list :drop 3)))
  165. (defun process-overworld-time (map ฮ”t)
  166. "Do nothing, lol. Core part of OVERWORLD-STATE.
  167. Returns parameters to be used in the next invocation of OVERWORLD-STATE."
  168. (let* ((time (โ€ฆ:incf-0 (gethash :seconds map) ฮ”t))
  169. (game-datetime (seconds->game-datetime time)))
  170. (if (eq (gethash :day map) 3)
  171. (end-game map)
  172. (progn
  173. ;; Go through the day-update procedures!
  174. (when (not (eq (getf game-datetime :day)
  175. (gethash :day map)))
  176. (setf (gethash :day map) (getf game-datetime :day)))
  177. nil))))
  178. (defun process-overworld-input (map)
  179. "Get and process any keyboard input, modifying the map or entities as necessary."
  180. (if (listen)
  181. (let* ((input (โŒจ:read-gamefied-char-plist)))
  182. (case (getf input :semantic)
  183. ;; Interacting with nearby characters/entities
  184. ('โŒจ:๐Ÿ†—
  185. (let* ((player (getf-entity map 'โœฟ:player))
  186. (interactee (car (entities-near-entity player (gethash :entities map))))
  187. (interactee-id (car interactee))
  188. (interaction (getf (cdr interactee) :interact)))
  189. (if interaction
  190. (apply (โ€ฆ:string->symbol interaction) (list map interactee-id))
  191. (list :parameters (list :map map)))))
  192. ('โŒจ:โŽ
  193. (list :function
  194. (๐Ÿ“‹:make-menu-function
  195. `((:en "Continue" :eo "Malpaลญzigi"
  196. :parameters ,(list :map map)
  197. :drop 1 :selected t :selection 50)
  198. (:en "Backpack" :eo "Sako"
  199. :function ,(๐ŸŽ’:make-inventory-function map)
  200. :drop 1)
  201. (:en "Settings" :eo "Agordoj"
  202. :function ,(๐Ÿ”ง:make-settings-menu-function))
  203. (:en "Give up" :eo "Rezigni"
  204. :drop 3)))))
  205. ;; Simple up-down-left-right movements
  206. ('โŒจ:โ†’
  207. (move-player map :ฮ”x 1))
  208. ('โŒจ:โ†
  209. (move-player map :ฮ”x -1))
  210. ('โŒจ:โ†‘
  211. (move-player map :ฮ”y -1))
  212. ('โŒจ:โ†“
  213. (move-player map :ฮ”y 1))
  214. ('โŒจ:โ†ฐ
  215. (move-player map :ฮ”x -1 :ฮ”y -1))
  216. ('โŒจ:โ†ฑ
  217. (move-player map :ฮ”x 1 :ฮ”y -1))
  218. ('โŒจ:โ†ฒ
  219. (move-player map :ฮ”x -1 :ฮ”y 1))
  220. ('โŒจ:โ†ณ
  221. (move-player map :ฮ”x 1 :ฮ”y 1))
  222. (otherwise
  223. (list :parameters (list :map map)))))
  224. (list :parameters (list :map map))))
  225. (defun move-player (map &key (ฮ”x 0) (ฮ”y 0))
  226. "Moves the play by the given changes in x & y.
  227. Very kindly removes a list of parameters to be returned by the overworld state-function."
  228. (move-entity map 'โœฟ:player :ฮ”x ฮ”x :ฮ”y ฮ”y)
  229. (let* ((coords (getf-entity-data map 'โœฟ:player :coords))
  230. (trigger (trigger-at-coords map (list :x (getf coords :x) :y (getf coords :y)))))
  231. (if (and trigger (getf trigger :function))
  232. (apply (โ€ฆ:string->symbol (getf trigger :function))
  233. (list map trigger))
  234. (list :parameters (list :map map)))))
  235. (defun move-entity (map entity-id &key (ฮ”x 0) (ฮ”y 0))
  236. "Move an entity relative to its current position."
  237. (when (< ฮ”x 0)
  238. (setf (getf-entity-data map entity-id :facing-right) nil))
  239. (when (> ฮ”x 0)
  240. (setf (getf-entity-data map entity-id :facing-right) 't))
  241. (let ((coords (getf-entity-data map entity-id :coords)))
  242. (move-entity-to map entity-id
  243. :x (+ ฮ”x (getf coords :x))
  244. :y (+ ฮ”y (getf coords :y)))))
  245. (defun move-entity-to (map entity &key (x 0) (y 0))
  246. "Move the given entity to the given coordinates."
  247. (let ((old-chunk (world-coords-chunk (getf-entity-data map entity :coords)))
  248. (new-chunk (world-coords-chunk (list :x x :y y))))
  249. ;; Change the entityโ€™s world coordinatesโ€ฆ
  250. (when (walkable-tile-p map x y)
  251. (setf (getf (getf-entity-data map entity :coords) :x) x)
  252. (setf (getf (getf-entity-data map entity :coords) :y) y)
  253. ;; If the entityโ€™s moved into a different screen-chunk (and so into a different
  254. ;; sub-alist of MAP hash-tableโ€™s :entities), move its list into the new chunkโ€™s.
  255. (when (not (eq old-chunk new-chunk))
  256. ;; Add it to the new chunk listโ€ฆ
  257. (setf (assoc-utils:aget (assoc-utils:aget (gethash :entities map) new-chunk) entity)
  258. (cdr (getf-entity map entity)))
  259. ;; Delete it from the old listโ€ฆ
  260. (alexandria:deletef (assoc-utils:aget (gethash :entities map) old-chunk) entity
  261. :test (lambda (id alist) (eq id (car alist))))))))
  262. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  263. ;;; Overworld-drawing: Map-rendering
  264. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  265. (defun overworld-state-draw (matrix map)
  266. "Draw the overworld map to the given matrix.
  267. A core part of OVERWORLD-STATE."
  268. (let* ((chunk (world-coords-chunk (getf-entity-data map 'โœฟ:player :coords))))
  269. (matrix-write-tiles matrix (gethash :tiles map) chunk)
  270. (matrix-write-entities matrix map chunk)
  271. (when (gethash :seconds map)
  272. (matrix-write-datetime matrix (seconds->game-datetime (gethash :seconds map))))
  273. (matrix-write-tiles matrix (gethash :top-tiles map) chunk)))
  274. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  275. ;;; Overworld-drawing: Map-tiles
  276. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  277. (defun matrix-write-tiles (matrix tiles chunk
  278. &key (chunk-width 72) (chunk-height 20))
  279. "Draw a mapโ€™s specific chunk (by its ID) to the matrix."
  280. (mapcar (lambda (cell)
  281. (if (or (not (getf cell :lang))
  282. (eq (getf cell :lang) (โ€ฆ:system-language)))
  283. (matrix-write-cell matrix cell)))
  284. (cdr (assoc chunk tiles))))
  285. (defun matrix-write-cell (matrix cell)
  286. "Set a matrice's (2d array's) element corresponding to a โ€œcellโ€; that is, a
  287. plist containing a character (:CHAR) and :X & :Y coordinates."
  288. (let ((coords (world-coords->screen-coords (list :x (getf cell :x) :y (getf cell :y)))))
  289. (setf (aref matrix
  290. (getf coords :y)
  291. (getf coords :x))
  292. (getf cell :@))))
  293. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  294. ;;; Overworld-drawing: Person-rendering
  295. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  296. (defun matrix-write-entities (matrix map chunk)
  297. "Draw all entities from an alist of entities to the matrix."
  298. (mapcar (lambda (entity-assoc)
  299. (matrix-write-entity matrix (cdr entity-assoc)))
  300. (cdr (assoc chunk (gethash :entities map)))))
  301. (defun matrix-write-entity (matrix entity-plist)
  302. "Render an entity-plist to the matrix."
  303. (when (getf entity-plist :face)
  304. (matrix-write-entity-head matrix entity-plist)
  305. (matrix-write-entity-legs matrix entity-plist))
  306. (when (getf entity-plist :avatar)
  307. (matrix-write-entity-avatar matrix entity-plist)))
  308. (defun matrix-write-entity-avatar (matrix entity-plist)
  309. "Draw an โ€œavatarโ€ entity; that is, not a person, but a random item."
  310. (let* ((screen-coords (world-coords->screen-coords (getf entity-plist :coords)))
  311. (avatar (getf entity-plist :avatar))
  312. (width (length avatar))
  313. (y (getf screen-coords :y))
  314. (x (- (getf screen-coords :x) (floor (/ width 2)))))
  315. (โœŽ:render-line matrix avatar (list :x x :y y))))
  316. (defun matrix-write-entity-head (matrix entity-plist)
  317. "Draw an entityโ€™s head. There aren't any Mami Tomoes in this game, dang it!"
  318. (let* ((screen-coords (world-coords->screen-coords (getf entity-plist :coords)))
  319. (face (getf entity-plist :face))
  320. (width (+ (length face) 2)) ;; Face + |borders|
  321. (y (- (getf screen-coords :y) 1))
  322. (x (if (getf entity-plist :facing-right)
  323. (- (getf screen-coords :x) (floor (/ width 2)) 0)
  324. (- (getf screen-coords :x) (floor (/ width 2)) 0))))
  325. (โœŽ:render-line matrix face (list :x (+ x 1) :y y))
  326. (ignore-errors (setf (aref matrix y x) #\|))
  327. (ignore-errors (setf (aref matrix y (+ width x -1))
  328. #\|))))
  329. (defun matrix-write-entity-legs (matrix entity-plist)
  330. "Draw a bipdel entityโ€™s legs โ€” a surprisingly in-depth task!"
  331. (let* ((screen-coords (world-coords->screen-coords (getf entity-plist :coords)))
  332. (x (getf screen-coords :x))
  333. (y (getf screen-coords :y)))
  334. (cond ((getf entity-plist :facing-right)
  335. (ignore-errors (setf (aref matrix y x) #\|))
  336. (ignore-errors (setf (aref matrix y (- x 1)) #\|)))
  337. ('t
  338. (ignore-errors (setf (aref matrix y x) #\|))
  339. (ignore-errors (setf (aref matrix y (+ x 1)) #\|))))))
  340. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  341. ;;; Overworld-drawing: The date
  342. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  343. (defun game-datetime->string (date-plist &optional (year 2006))
  344. (format nil
  345. (โ€ฆ:getf-lang '(:en "~A ~A ~A ~2,'0d:~2,'0d"
  346. :eo "~A ~A ~Aa ~2,'0d:~2,'0d"))
  347. year
  348. (โ€ฆ:getf-lang '(:en "Jun" :eo "Jun"))
  349. (+ (getf date-plist :day) 3)
  350. (getf date-plist :hour)
  351. (getf date-plist :minute)))
  352. (defun matrix-write-datetime (matrix datetime)
  353. (let ((string (game-datetime->string datetime)))
  354. (โœŽ:render-line matrix string (list :x (- 71 (length string)) :y 19))))
  355. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  356. ;;; Overworld loop
  357. ;;; โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
  358. (defun overworld-state
  359. (matrix &key map (ฮ”t .02))
  360. "Render the given map to the MATRIX and take user-input โ€” for one frame.
  361. A state-function for use with STATE-LOOP."
  362. (sleep ฮ”t)
  363. (overworld-state-draw matrix map)
  364. (overworld-state-update map ฮ”t))
  365. (defun make-overworld-function (map)
  366. "Return a state-function for a a MAP, for use with STATE-LOOP."
  367. (lambda (matrix &key (map map))
  368. (apply #'๐ŸŒ:overworld-state
  369. (list matrix :map map))))
  370. (defun make-overworld-state (map)
  371. "Return a state-plist for a a MAP, for use with STATE-LOOP."
  372. (list :function (make-overworld-function map)))