You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

430 lines
14KB

  1. (module world (adventure create-object move-object add-alias set-hidden toggle-fixed set-enter-message set-destination get-container print-room-description)
  2. (import scheme)
  3. (import chicken.base)
  4. (import chicken.file)
  5. (import srfi-1)
  6. (import matchable)
  7. (import fmt)
  8. (import ansi-escape-sequences)
  9. (import util)
  10. (import database)
  11. (import io)
  12. (define +cardinal-sets+
  13. '((north n)
  14. (northeast ne north-east)
  15. (east e)
  16. (southeast se south-east)
  17. (south s)
  18. (southwest sw south-west)
  19. (west w)
  20. (northwest nw north-west)
  21. (up u)
  22. (down d)))
  23. (define +cardinal-opposites+
  24. '((north . south)
  25. (northeast . southwest)
  26. (east . west)
  27. (southeast . northwest)
  28. (south . north)
  29. (southwest . northeast)
  30. (west . east)
  31. (northwest . southeast)
  32. (up . down)
  33. (down . up)))
  34. (define *exit-adventure* #f)
  35. (define (set-name object name)
  36. (database-set object 'name name))
  37. (define (set-description object description)
  38. (database-set object 'description description))
  39. (define (get-name object)
  40. (database-get object 'name (symbol->string object)))
  41. (define (get-description object)
  42. (database-get object 'description "You see the swirling void of creation."))
  43. (define (get-container object)
  44. (database-get object 'container #f))
  45. (define (get-contents object)
  46. (database-get object 'contents '()))
  47. (define (set-destination object destination)
  48. (database-set object 'destination destination))
  49. (define (get-destination object)
  50. (database-get object 'destination #f))
  51. (define (set-enter-message object msg)
  52. (database-set object 'enter-message msg))
  53. (define (get-enter-message object)
  54. (database-get object 'enter-message #f))
  55. (define (get-aliases object)
  56. (database-get object 'aliases '()))
  57. (define (set-aliases object alias-list)
  58. (database-set object 'aliases alias-list))
  59. (define (add-alias object alias)
  60. (let ((aliases (get-aliases object)))
  61. (if (not (member alias aliases))
  62. (set-aliases object (cons alias aliases)))))
  63. (define (remove-alias object alias)
  64. (let ((aliases (get-aliases object)))
  65. (if (member alias aliases)
  66. (set-aliases object (remove (curry eq? alias) aliases)))))
  67. (define (set-hidden object value)
  68. (database-set object 'hidden value))
  69. (define (get-hidden object)
  70. (database-get object 'hidden #f))
  71. (define (set-fixed object value)
  72. (database-set object 'fixed value))
  73. (define (get-fixed object value)
  74. (database-get object 'hidden #f))
  75. (define (get-put-message object)
  76. (database-get object 'put-message "You put the ~a into the ~a."))
  77. ;; Is development mode enabled?
  78. (define (devmode-enabled?)
  79. (has-property? 'you 'devmode))
  80. (define (toggle-devmode)
  81. (toggle-flag 'you 'devmode))
  82. ;; Is an object fixed in place (e.g. cannot be picked up?)
  83. (define (fixed? object)
  84. (has-property? object 'fixed))
  85. (define (toggle-fixed object)
  86. (toggle-flag object 'fixed))
  87. ;; Match a tag against a list of objects, checking for its tag and its aliases.
  88. (define (match-object tag objects)
  89. (let loop ((objects objects))
  90. (if (null? objects)
  91. #f
  92. (let ((taglist (cons (car objects) (get-aliases (car objects)))))
  93. (if (member tag taglist)
  94. (car objects)
  95. (loop (cdr objects)))))))
  96. (define (create-object tag name description)
  97. (set-name tag name)
  98. (set-description tag description))
  99. (define (move-object object container)
  100. (let ((prev-container (get-container object)))
  101. (database-set object 'container container)
  102. (let ((contents (get-contents container)))
  103. (if (not (member object contents))
  104. (begin
  105. (database-set container 'contents (cons object contents))
  106. (database-set prev-container 'contents (remove (curry eq? object) (get-contents prev-container))))))))
  107. ;; Determine the objects visible to a source object, zork-style
  108. (define (visible-objects source)
  109. (let ((result (get-container source)))
  110. (if (and result (object-exists? result))
  111. (cons (get-container source) (get-contents (get-container source)))
  112. (error "Tried to determine visible objects for object without a container."))))
  113. (define (print-room-description room)
  114. (newline)
  115. (display (set-text '(bold) (get-name room)))
  116. (if (devmode-enabled?) (display (set-text '(bold fg-green) (string-append " [" (symbol->string room) "]"))))
  117. (newline)
  118. (display " ")
  119. (fmt #t (dsp (wrap-lines (get-description room))))
  120. (newline)
  121. (display "You see: ")
  122. (map (lambda (n) (if (not (get-hidden n)) (begin (display (get-name n)) (display " ") (if (devmode-enabled?) (begin (display (set-text '(bold fg-green) (string-append "[" (symbol->string n) "] ")))))))) (remove (curry eq? 'you) (get-contents room)))
  123. (newline))
  124. (define (do-command-enter tag)
  125. (let ((object (match-object tag (visible-objects 'you))))
  126. (if (not object)
  127. (show "You cannot go that way.")
  128. (let ((destination (get-destination object)))
  129. (if (not destination)
  130. (show "You cannot enter that.")
  131. (begin
  132. (move-object 'you destination)
  133. (perhaps show (get-enter-message object))
  134. (print-room-description (get-container 'you))))))))
  135. (define (do-command-save)
  136. (let ((save-name (prompt-default "Enter save name: " "kekkonen.sav")))
  137. (if (or (not (file-exists? save-name)) (prompt-yn "That file already exists. Overwrite? "))
  138. (begin
  139. (show "Saving database, please wait...")
  140. (database-save save-name)
  141. (show "Done.")))))
  142. (define (do-command-load)
  143. (let ((save-name (prompt-default "Enter save file name to load: " "kekkonen.sav")))
  144. (if (not (file-exists? save-name))
  145. (show "That file does not exist.")
  146. (begin
  147. (show "Loading database, please wait...")
  148. (database-load save-name)
  149. (show "Done.")))))
  150. (define (do-command-look)
  151. (print-room-description (get-container 'you)))
  152. (define (do-command-examine tag)
  153. (let ((object (match-object tag (visible-objects 'you))))
  154. (if (not object)
  155. (show "You cannot see that here.")
  156. (show (get-description object)))))
  157. (define (do-command-inventory)
  158. (map (compose show get-name) (get-contents 'you)))
  159. (define (do-command-take tag)
  160. (if (not (symbol? tag))
  161. (show "I didn't quite understand that.")
  162. (let ((object (match-object tag (if (devmode-enabled?)
  163. (get-all-objects)
  164. (visible-objects 'you)))))
  165. (if (or (not object) (and (fixed? object) (not (devmode-enabled?))))
  166. (if object
  167. (show "That is fixed in place.")
  168. (show "You cannot see that here."))
  169. (begin
  170. (show (string-append "You get " (get-name object) "."))
  171. (move-object object 'you))))))
  172. (define (do-command-drop tag)
  173. (if (not (symbol? tag))
  174. (show "I didn't quite understand that.")
  175. (let ((object (match-object tag (get-contents 'you))))
  176. (if (not object)
  177. (show "You are not carrying that.")
  178. (begin
  179. (show (string-append "You drop " (get-name object) "."))
  180. (move-object object (get-container 'you)))))))
  181. (define (do-command-put tag destination-tag)
  182. (let ((object (match-object tag (get-contents 'you))))
  183. (if (not object)
  184. (show "You are not carrying that.")
  185. (let ((destination-object (match-object destination-tag (visible-objects 'you))))
  186. (if (not destination-object)
  187. (show "You cannot see that here.")
  188. (move-object object (get-destination destination-object)))))))
  189. (define (do-command-devmode)
  190. (toggle-devmode)
  191. (if (devmode-enabled?)
  192. (show "Development mode enabled.")
  193. (show "Development mode disabled.")))
  194. (define (do-command-create tag name description)
  195. (if (not (and (symbol? tag) (string? name) (string? description)))
  196. (show "I didn't quite understand that.")
  197. (if (object-exists? tag)
  198. (show "That object already exists.")
  199. (begin
  200. (create-object tag name description)
  201. (move-object tag (get-container 'you))))))
  202. (define (do-setter-command tag value type? setter)
  203. (if (not (and (symbol? tag) (type? value)))
  204. (show "I didn't quite understand that.")
  205. (let ((object (match-object tag (visible-objects 'you))))
  206. (if (not object)
  207. (show "You can't see that here.")
  208. (begin
  209. (setter object value)
  210. (show "You set a value."))))))
  211. (define (do-command-rename tag name)
  212. (do-setter-command tag name string? set-name))
  213. (define (do-command-describe tag description)
  214. (do-setter-command tag description string? set-description))
  215. (define (do-command-flag tag flag)
  216. (if (not (and (symbol? tag) (symbol? flag)))
  217. (show "I didn't quite understand that.")
  218. (let ((object (match-object tag (visible-objects 'you))))
  219. (if (not object)
  220. (show "You can't see that here.")
  221. (begin
  222. (case flag
  223. ((fixed) (set-fixed object #t))
  224. ((hidden) (set-hidden object #t))
  225. (else (show "Invalid flag name."))))))))
  226. (define (do-command-unflag tag flag)
  227. (if (not (and (symbol? tag) (symbol? flag)))
  228. (show "I didn't quite understand that.")
  229. (let ((object (match-object tag (visible-objects 'you))))
  230. (if (not object)
  231. (show "You can't see that here.")
  232. (begin
  233. (case flag
  234. ((fixed) (set-fixed object #f))
  235. ((hidden) (set-hidden object #f))
  236. (else (show "Invalid flag name."))))))))
  237. (define (do-command-alias tag alias)
  238. (if (not (and (symbol? tag) (symbol? alias)))
  239. (show "I didn't quite understand that.")
  240. (let ((object (match-object tag (visible-objects 'you))))
  241. (if (not object)
  242. (show "You can't see that here.")
  243. (begin
  244. (add-alias object alias)
  245. (show "You add an alias."))))))
  246. (define (do-command-unalias tag alias)
  247. (if (not (and (symbol? tag) (symbol? alias)))
  248. (show "I didn't quite understand that.")
  249. (let ((object (match-object tag (visible-objects 'you))))
  250. (if (not object)
  251. (show "You can't see that here.")
  252. (begin
  253. (remove-alias object alias)
  254. (show "You remove an alias."))))))
  255. (define (do-command-destroy tag)
  256. (if (not (symbol? tag))
  257. (show "I didn't quite understand that.")
  258. (database-remove tag)))
  259. (define (do-command-aliases tag)
  260. (if (not (symbol? tag))
  261. (show "I didn't quite understand that.")
  262. (let ((object (match-object tag (visible-objects 'you))))
  263. (if (not object)
  264. (show "You can't see that here.")
  265. (begin
  266. (newline)
  267. (map (lambda (x) (display x) (display " ")) (get-aliases object))
  268. (newline))))))
  269. (define (do-command-message tag message-tag message)
  270. (if (not (and (symbol? tag) (symbol? message-tag) (string? message)))
  271. (show "I didn't quite understand that.")
  272. (let ((object (match-object tag (visible-objects 'you))))
  273. (if (not object)
  274. (show "You can't see that here.")
  275. (case message-tag
  276. ((enter) (set-enter-message object message))
  277. (else (show "Invalid message name.")))))))
  278. (define (do-command-goto tag)
  279. (if (not (symbol? tag))
  280. (show "I didn't quite understand that.")
  281. (begin
  282. (move-object 'you tag)
  283. (print-room-description (get-container 'you)))))
  284. (define (get-cardinal-set direction)
  285. (find (curry member direction) +cardinal-sets+))
  286. (define (get-cardinal-aliases direction)
  287. (perhaps (curry remove (curry eq? direction)) (get-cardinal-set direction)))
  288. (define (cardinal-direction? direction)
  289. (list? (member direction (flatten +cardinal-sets+))))
  290. (define (get-inverse-direction direction)
  291. (perhaps cdr (assoc direction +cardinal-opposites+)))
  292. (define (get-canonical-cardinal-direction-name direction)
  293. (perhaps car (get-cardinal-set direction)))
  294. (define (do-command-dig direction destination)
  295. (if (not (and (symbol? direction) (symbol? destination)))
  296. (show "I didn't quite understand that.")
  297. (if (not (cardinal-direction? direction))
  298. (show "You must specify a compass rose direction or up and down.")
  299. (let ((canonical-direction (get-canonical-cardinal-direction-name direction)))
  300. (let ((exit-tag (compose-symbols canonical-direction (get-container 'you) destination)))
  301. (if (object-exists? exit-tag)
  302. (show "An exit like that already exists.")
  303. (begin
  304. (move-object exit-tag (get-container 'you))
  305. (set-hidden exit-tag #t)
  306. (set-destination exit-tag destination)
  307. (map (curry add-alias exit-tag) (get-cardinal-set direction))
  308. (show "You create a passage."))))))))
  309. (define (do-command-exit)
  310. (show "Goodbye, see you later...")
  311. (set! *exit-adventure* #t))
  312. (define (alias-transform input)
  313. (match input
  314. (('quit) '(exit))
  315. (('i) '(inventory))
  316. (('inv) '(inventory))
  317. (('look x) `(examine ,x))
  318. (('go x) `(enter ,x))
  319. (('get x) `(take ,x))
  320. ((x) (if (cardinal-direction? x)
  321. `(enter ,x)
  322. input))
  323. (_ input)))
  324. (define (dispatch-command input)
  325. (let ((success #t))
  326. (match input
  327. (('look) (do-command-look))
  328. (('save) (do-command-save))
  329. (('load) (do-command-load))
  330. (('devmode) (do-command-devmode))
  331. (('exit) (do-command-exit))
  332. (('enter x) (do-command-enter x))
  333. (('take x) (do-command-take x))
  334. (('drop x) (do-command-drop x))
  335. (('inventory) (do-command-inventory))
  336. (('examine x) (do-command-examine x))
  337. (('put x y) (do-command-put x y))
  338. (_ (if (devmode-enabled?)
  339. (match input
  340. (('create x y z) (do-command-create x y z))
  341. (('rename x y) (do-command-rename x y))
  342. (('describe x y) (do-command-describe x y))
  343. (('dig x y) (do-command-dig x y))
  344. (('flag x y) (do-command-flag x y))
  345. (('unflag x y) (do-command-unflag x y))
  346. (('alias x y) (do-command-alias x y))
  347. (('unalias x y) (do-command-unalias x y))
  348. (('destroy x) (do-command-destroy x))
  349. (('aliases x) (do-command-aliases x))
  350. (('message x y z) (do-command-message x y z))
  351. (('goto x) (do-command-goto x))
  352. (_ (set! success #f)))
  353. (set! success #f))))
  354. success))
  355. (define (adventure)
  356. (let ((success (dispatch-command (alias-transform (adventure-prompt)))))
  357. (if (not success)
  358. (begin
  359. (show "I didn't quite understand that.")
  360. (adventure))
  361. (if *exit-adventure*
  362. (show "Exiting..."); (display *database*)
  363. (adventure))))))