1 ;;Common Lisp Interactive Fiction Library
\r
3 ;;if-lib module: contains various things that IF library should contain.
\r
5 ;;See license.txt for licensing information
\r
7 ;; Table of contents:
\r
9 ;; SECTION 1: Global parameters and definitions
\r
10 ;; SECTION 2: Library-defined classes and objects
\r
11 ;; SECTION 3: Scope rules
\r
12 ;; SECTION 4: Printing objects
\r
13 ;; SECTION 5: Default parser
\r
14 ;; SECTION 6: Tokens
\r
15 ;; SECTION 7: Action helpers
\r
16 ;; SECTION 8: Main loop
\r
17 ;; SECTION 9: Other stuff
\r
20 (in-package :cl-user)
\r
23 (:use :common-lisp :if-basic-lib :if-console)
\r
24 (:export :container :room :item :clothing :capacity
\r
26 :n-to :ne-to :e-to :se-to :s-to :sw-to :w-to :nw-to :in-to :out-to
\r
27 :u-to :d-to :cant-go
\r
28 :*intscope* :*outscope* :*location* :*trace-light* :*vowels*
\r
29 :*score* :*gamestate* :*turns* :*dark*
\r
30 :add-to-scope :add-to-outscope :found-in :seen-from
\r
31 :compass :dir-n :dir-ne :dir-e :dir-se :dir-s
\r
32 :dir-sw :dir-w :dir-nw :dir-u :dir-d :dir-in :dir-out
\r
33 :darkness :lit :transparent :passable
\r
34 :reachp :seep :global-reachp :global-seep :seep1 :reachp1
\r
35 :darkness :actor :selfobj :*player* :find-location
\r
36 :deduce-article :print-property :print-name :list-contents
\r
37 :reset-scope :look :words2dic-first :disambig :normal-token-scope
\r
38 :input-quit-loop :quit-game :prompt :prompt-read :input-loop-step
\r
39 :go-to-room :property :init :test-seq
\r
40 :heldp :the-name :each-turn :daemon :time-left :time-out
\r
41 :start-daemon :stop-daemon :start-timer :stop-timer
\r
42 :supporter :animate :scenery
\r
43 :afterlife :print-gamestate :end-game
\r
44 :repl-mode :compile-lib :free-symbol
\r
48 (in-package :if-lib)
\r
50 ;;Access to shadowed room function
\r
52 (defun room (&optional (arg :default))
\r
55 (define-compiler-macro room (&whole whole &optional arg)
\r
56 (declare (ignore arg))
\r
57 `(cl:room ,@(cdr whole)))
\r
61 ;;SECTION 1: Global parameters and definitions
\r
63 (defparameter *vowels* "aeiouy"
\r
64 "A string containing all English vowels")
\r
66 (defparameter *intscope* nil
\r
67 "`Internal' scope, i.e. reachable by hand")
\r
68 (defparameter *outscope* nil
\r
69 "`Outside' scope, i.e. everything that is visible")
\r
70 (defparameter *location* nil
\r
71 "Current location of the player")
\r
72 (defparameter *dark* nil
\r
73 "Whether it is dark in the current location")
\r
74 (defparameter *score* 0
\r
75 "Current score of the player")
\r
76 (defparameter *gamestate* 0
\r
77 "Current gamestate: if not zero at the end of turn game ends")
\r
78 (defparameter *turns* 0
\r
79 "Turns passed since beginning of the game")
\r
81 (defparameter *player* nil
\r
82 "Current player object (will be initialised later")
\r
84 (declare-predicate add-to-scope add-to-outscope found-in seen-from)
\r
86 ;;SECTION 2: Library-defined classes and objects
\r
88 (ifclass container () (capacity integer) (has :container))
\r
89 (ifclass supporter () (capacity integer) (has :supporter))
\r
91 (ifclass room () (description string)
\r
92 (n-to object) (ne-to object) (e-to object) (se-to object)
\r
93 (s-to object) (sw-to object) (w-to object) (nw-to object)
\r
94 (u-to object) (d-to object) (in-to object) (out-to object)
\r
96 (has :light :enterable))
\r
98 (ifclass item () (description string) (article string)
\r
101 (ifclass clothing (item) (has :clothing))
\r
103 (ifclass scenery () (has :scenery))
\r
105 (ifclass food (item) (has :edible))
\r
107 (ifclass switchable () (has :switchable))
\r
109 (object darkness (room) "Darkness"
\r
110 (description "It's pitch black. You can't see a thing.")
\r
112 (look (look self)))
\r
115 ;;Compass directions
\r
116 (object compass ())
\r
117 (object dir-n () "north" (name "north" "n") compass (property 'n-to))
\r
118 (object dir-ne () "northeast" (name "northeast" "ne") compass
\r
120 (object dir-e () "east" (name "east" "e") compass (property 'e-to))
\r
121 (object dir-se () "southeast" (name "southeast" "se") compass
\r
123 (object dir-s () "south" (name "south" "s") compass (property 's-to))
\r
124 (object dir-sw () "southwest" (name "southwest" "sw") compass
\r
126 (object dir-w () "west" (name "west" "w") compass (property 'w-to))
\r
127 (object dir-nw () "northwest" (name "northwest" "nw") compass
\r
129 (object dir-u () "up" (name "up" "u") compass (property 'u-to))
\r
130 (object dir-d () "down" (name "down" "d") compass (property 'd-to))
\r
131 (object dir-in () "in" compass (property 'in-to))
\r
132 (object dir-out () "out" compass (property 'out-to))
\r
134 (ifclass actor () (reachp function nil) (seep function nil))
\r
135 (ifclass animate () (has :animate))
\r
137 (object selfobj (actor animate) "me"
\r
141 (defparameter *player* selfobj
\r
142 "This time it's initialised properly")
\r
144 ;;SECTION 3: Scope rules
\r
146 (defun find-location (obj)
\r
147 "Find a top-level object that contains obj"
\r
148 (loop for o = obj then (parent o) while (parent o) finally (return o)))
\r
150 (defun transparent (obj)
\r
151 "Whether the object is transparent"
\r
152 (or (has obj :container :open)
\r
153 (has obj :supporter)
\r
154 (has obj :transparent)
\r
155 (eql obj *player*)))
\r
157 (defun lit-down (obj)
\r
158 "Lighting recursion down the object tree"
\r
159 (if (has obj :light) t
\r
160 (some #'(lambda (x) (or (has x :light)
\r
161 (and (transparent x) (lit-down x))))
\r
165 "Whether an object is lit"
\r
166 (or (lit-down obj)
\r
167 (when (parent obj)
\r
168 (or (has (parent obj) :light)
\r
169 (lit (parent obj))))))
\r
172 (defun seep-down (actor obj)
\r
173 "Looking recursion down the object tree"
\r
174 (if (eql obj actor) t
\r
175 (some #'(lambda (x) (or (eql x actor)
\r
176 (and (transparent x) (seep-down actor x))))
\r
180 (defun seep2 (actor obj)
\r
181 "First approximation of looking function"
\r
182 (or (seep-down actor obj)
\r
183 (when (parent obj)
\r
184 (or (eql actor (parent obj)) (seep2 actor (parent obj))))))
\r
186 (defun seep1 (actor obj)
\r
187 "Second approximation of looking function"
\r
188 (and (lit actor) (lit obj) (or (in obj compass) (seep2 actor obj))))
\r
190 (defun global-seep (actor obj)
\r
191 "Tests whether an object is seen by actor"
\r
192 (if (and (typep actor 'actor) (seep actor))
\r
193 (read-property actor 'seep obj)
\r
194 (seep1 actor obj)))
\r
196 (defun passable (obj)
\r
197 (or (has obj :container :open)
\r
198 (has obj :supporter)
\r
199 (eql obj *player*)))
\r
202 (defun reachp-down (actor obj)
\r
203 "Reaching recursion down the object tree"
\r
204 (if (eql obj actor) t
\r
205 (some #'(lambda (x) (or (eql x actor)
\r
206 (and (passable x) (reachp-down actor x))))
\r
210 (defun reachp2 (actor obj)
\r
211 "First approximation of reaching function"
\r
212 (or (reachp-down actor obj)
\r
213 (when (parent obj)
\r
214 (or (eql (parent obj) actor)
\r
215 (reachp2 actor (parent obj))))))
\r
218 (defun reachp1 (actor obj)
\r
219 "Second approximation of reaching function"
\r
220 (and (lit obj) (lit actor) (reachp2 actor obj)))
\r
222 (defun global-reachp (actor obj)
\r
223 "Tests whether an object is reachable by actor"
\r
224 (if (and (typep actor 'actor) (reachp actor))
\r
225 (read-property actor 'reachp obj)
\r
226 (reachp1 actor obj)))
\r
228 (defun reset-scope (&aux location)
\r
229 "Update scope for the new location"
\r
230 (setf location *location*)
\r
231 (unless location (setf *intscope* nil *outscope* nil)
\r
232 (return-from reset-scope))
\r
233 (setf *dark* (not (and (lit *player*) (lit *location*))))
\r
234 (setf *outscope* (loop for x in *allobjects* ;unless (eql x location)
\r
235 when (global-seep *player* x) collect x))
\r
236 (setf *intscope* (loop for x in *allobjects* ;unless (eql x location)
\r
237 when (global-reachp *player* x) collect x))
\r
238 (let ((int (loop for x in *intscope*
\r
239 append (add-to-scope x)))
\r
240 (out (loop for x in *outscope*
\r
241 append (add-to-scope x)
\r
242 append (add-to-outscope x))))
\r
243 (setf *outscope* (nconc *outscope* out))
\r
244 (setf *intscope* (nconc *intscope* int)))
\r
245 (objectloop (x) (when (and (seen-from x *location*) (hasnt x :absent))
\r
246 (push x *outscope*))))
\r
248 ;;SECTION 4: Printing objects
\r
250 (defun deduce-article (name)
\r
251 "Tries to guess an article for the object"
\r
252 (let ((firstchar (aref name 0)))
\r
253 (cond ((char= firstchar (char-upcase firstchar)) "the")
\r
254 ((find firstchar *vowels* :test #'char=) "an")
\r
257 (defun print-property (obj property)
\r
258 "Print a property of object"
\r
259 (multiple-value-bind (value printp) (read-property obj property)
\r
260 (if (and (stringp value) (not printp)) (progn (sprint value) t) printp)))
\r
262 (defgeneric print-name (obj &key article capital)
\r
263 (:documentation "Returns a string containing the name of object"))
\r
264 (register-generic 'print-name)
\r
267 (defgeneric the-name (obj &key article capital)
\r
268 (:documentation "Returns a string containing _the_ name of
\r
269 object (with definite article, and fluff stripped off)"))
\r
270 (register-generic 'the-name)
\r
272 (defmethod print-name ((obj abstractobject) &key (article nil) (capital nil))
\r
274 (with-output-to-string (out)
\r
275 (let ((*standard-output* out)
\r
276 (name (read-property obj 'name)))
\r
278 (unless (zerop (length article)) (format t "~a " article))
\r
279 (if (provides obj 'article)
\r
280 (print-property obj 'article)
\r
281 (progn (princ (deduce-article name)) (princ " "))))
\r
283 (when capital (setf (aref outstr 0) (char-upcase (aref outstr 0))))
\r
286 (defmethod print-name ((obj clothing) &key &allow-other-keys)
\r
287 (if (has obj :worn) (concatenate 'string (call-next-method) " (worn)")
\r
288 (call-next-method)))
\r
290 (defmethod print-name ((obj item) &key &allow-other-keys)
\r
291 (if (has obj :light) (concatenate 'string (call-next-method)
\r
292 " (providing light)")
\r
293 (call-next-method)))
\r
295 (defmethod print-name ((obj animate) &key (article nil) (capital nil))
\r
296 (call-next-method obj :article (or article "") :capital capital))
\r
298 (defmethod the-name ((obj abstractobject) &key (article nil) (capital nil))
\r
300 (with-output-to-string (out)
\r
301 (let ((*standard-output* out)
\r
302 (name (read-property obj 'name)))
\r
304 (unless (zerop (length article)) (format t "~a " article))
\r
305 (if (provides obj 'article)
\r
306 (print-property obj 'article)
\r
307 (progn (princ "the "))))
\r
309 (when capital (setf (aref outstr 0) (char-upcase (aref outstr 0))))
\r
312 (defmethod the-name ((obj animate) &key (article nil) (capital nil))
\r
313 (call-next-method obj :article (or article "") :capital capital))
\r
315 (defun print-inside (obj stream)
\r
316 "Return the string containing the status of contents of the object"
\r
317 (when (has obj :container)
\r
318 (if (or (has obj :open) (has obj :transparent))
\r
320 (progn (princ " (containing " stream)
\r
321 (princ (list-contents obj) stream)
\r
322 (princ ")" stream))
\r
323 (princ " (empty)" stream))
\r
324 (princ " (closed)" stream)))
\r
325 (when (has obj :supporter)
\r
326 (when (children obj)
\r
327 (progn (princ " (on top of which are " stream)
\r
328 (princ (list-contents obj) stream)
\r
329 (princ ")" stream)))))
\r
331 (defun list-contents (obj)
\r
332 "Return the string containing the contents of the object"
\r
333 (with-output-to-string (out)
\r
335 (when (children obj)
\r
336 (objectloop (in x obj)
\r
337 (if (and !last! commaflag) (princ " and " out)
\r
338 (when commaflag (princ ", " out)))
\r
340 (princ (print-name x) out)
\r
341 (print-inside x out))))))
\r
344 (defun default-glance (obj)
\r
345 "Default initial description of object"
\r
346 (format t "[Default glance for ~a]~%" obj)
\r
348 (with-output-to-string (out)
\r
349 (princ "There is " out) (princ (print-name obj) out)
\r
350 (print-inside obj out)
\r
354 ;;SECTION 5: Default parser
\r
356 (defun greedy-match-noun (obj words)
\r
357 "Finds how many words obj matches"
\r
358 (loop for w in words
\r
359 collecting w into ww
\r
360 when (< (parser obj ww) 1) return (length (butlast ww))
\r
361 finally (return (length words))))
\r
364 (defun find-best-match (words scope)
\r
365 "Returns a list of objects that match words the best"
\r
366 (loop with bestvalue = 0
\r
367 with bestnouns = nil
\r
369 for y = (greedy-match-noun x words)
\r
370 when (> y bestvalue) do (setf bestvalue y) (setf bestnouns (list x))
\r
371 else when (and (= y bestvalue) (> bestvalue 0)) do (push x bestnouns)
\r
372 finally (return (values bestnouns bestvalue))))
\r
374 (defun words2dic-first (words)
\r
375 "Returns a list of words while they are in the dictionary"
\r
376 (loop for w in words
\r
377 for n = (handler-case (word2dic w) (nosuchword () nil))
\r
378 while n collect n))
\r
380 (defgeneric parser-score (obj)
\r
381 (:documentation "Should return the score used when sorting
\r
382 through ambiguous input")
\r
383 (:method (obj) (declare (ignore obj)) 100))
\r
385 (defmethod parser-score ((room room))
\r
386 (declare (ignore room)) 10)
\r
388 (define-condition disambig ()
\r
389 ((what :initarg :what :initform nil :reader disambig-what)
\r
390 (words :initarg :words :initform nil :reader disambig-words))
\r
391 (:report (lambda (condition stream)
\r
392 (format stream "Cannot decide between the objects: ~A"
\r
393 (disambig-what condition)))))
\r
395 (defun handle-disambig (c)
\r
396 "Disambiguation handle"
\r
397 (let*((dlist (disambig-what c))
\r
398 (bestvalue (loop for x in dlist
\r
399 maximizing (parser-score x)))
\r
400 (list (delete-if (lambda (item) (< (parser-score item) bestvalue))
\r
403 (return-from handle-disambig (first list)))
\r
404 (sprint "I can't understand what do you mean by:~{ ~a~}."
\r
405 (disambig-words c))
\r
407 (sprint "Choose one:~%")
\r
410 (sprint "~a: ~a~%" (incf i) (print-name l)))
\r
412 (force-output *outstream*)
\r
413 (nth (loop for x = (parse-integer
\r
414 (get-input *instream* *outstream*)
\r
416 until (and (numberp x) (<= 1 x (length list)))
\r
417 finally (return (1- x)))
\r
420 ;;SECTION 6: Tokens
\r
423 (defun normal-token-scope (wordlist scope)
\r
424 "The main token function"
\r
425 (multiple-value-bind (bnouns bvalue)
\r
426 (find-best-match (words2dic-first wordlist) scope)
\r
427 (case (length bnouns)
\r
428 (1 (values t (car bnouns)
\r
429 (last wordlist (- (length wordlist) bvalue))))
\r
431 (t (let ((choosewhat (handler-case
\r
432 (signal 'disambig :what bnouns
\r
433 :words (butlast wordlist
\r
434 (- (length wordlist) bvalue)))
\r
435 (disambig (condition)
\r
436 (handle-disambig condition)))))
\r
438 (values t choosewhat
\r
439 (last wordlist (- (length wordlist) bvalue)))))))))
\r
441 ;;Token definitions
\r
443 (deftoken :noun ;matches the given scope (intscope by default)
\r
444 (let ((scope (if (eql *tokenpar* :unspecified) *intscope* *tokenpar*)))
\r
445 (normal-token-scope wordlist scope)))
\r
447 (deftoken :seen ;matches outscope
\r
448 (normal-token-scope wordlist *outscope*))
\r
450 (deftoken :or ;An ugly hack - matches either of supported words
\r
451 (let ((word (car (member (car wordlist) *tokenpar* :test #'string==))))
\r
452 (if word (values t word (cdr wordlist)) nil)))
\r
454 (deftoken :has ;has flag, in outscope
\r
455 (normal-token-scope wordlist
\r
456 (remove-if #'(lambda (x) (hasnt x *tokenpar*)) *outscope*)))
\r
458 (deftoken :direction ;compass direction
\r
459 (normal-token-scope wordlist (children compass)))
\r
462 (if (eql obj *player*) t
\r
463 (and (parent obj) (transparent (parent obj)) (heldp (parent obj)))))
\r
466 (normal-token-scope wordlist
\r
467 (remove-if (complement #'heldp) *outscope*)))
\r
470 (setf *meta* t) (values t t wordlist))
\r
473 ;;SECTION 7: Action helpers
\r
475 ;;Most of the actions are defined in verbs module.
\r
478 (defgeneric look (obj)
\r
479 (:documentation "Used for looking in rooms and containers"))
\r
480 (register-generic 'look)
\r
482 (defmethod look ((room room))
\r
483 (sprint "~a~%~%" (read-property room 'name))
\r
484 (when (provides room 'description) (print-property room 'description))
\r
486 (objectloop (in x room)
\r
487 (when (hasnt x :scenery :hidden)
\r
489 (if (provides x 'glance)
\r
490 (unless (print-property x 'glance) (default-glance x))
\r
491 (default-glance x))))
\r
494 (defun go-to-room (room)
\r
495 "Player moves into room"
\r
496 (when (typep room 'abstractobject)
\r
497 (setf *location* room)
\r
498 (move *player* *location*)
\r
499 (give *location* :visited)
\r
500 (objectloop (x) (when (and (found-in x *location*) (hasnt x :absent))
\r
501 (move x *location*)))
\r
503 (run-action 'look *location* :time 0)))
\r
506 ;;SECTION 8: Main loop
\r
508 (supply turn-passing (&optional time)
\r
510 (incf *turns* time)
\r
511 (loop for x in *allobjects*
\r
512 if (and (has x :daemon) (provides x 'daemon))
\r
513 do (read-property- :execute x 'daemon)
\r
514 if (and (has x :timer) (provides x 'time-left)
\r
515 (provides x 'time-out))
\r
516 do (if (zerop (slot-value x 'time-left))
\r
517 (read-property- :execute x 'time-out)
\r
518 (decf (slot-value x 'time-left))))
\r
519 (loop for x in *outscope*
\r
520 if (provides x 'each-turn)
\r
521 do (read-property- :execute x 'each-turn))
\r
522 (call-next-method))
\r
524 (defun start-daemon (obj)
\r
525 (give obj :daemon))
\r
527 (defun stop-daemon (obj)
\r
528 (give obj :~daemon))
\r
530 (defun start-timer (obj time)
\r
531 (assert (provides obj 'time-left))
\r
532 (setf (slot-value obj 'time-left) time)
\r
535 (defun stop-timer (obj)
\r
536 (give obj :~timer))
\r
538 (defgeneric before-special-rule (location)
\r
539 (:documentation "Runs on location before other before effects are runned")
\r
540 (:method (location) (declare (ignore location)) nil))
\r
541 (register-generic 'before-special-rule)
\r
543 (defmethod before-special-rule ((location room))
\r
544 (or (and *dark* (before darkness))
\r
545 (unless (eql *noun* *location*) (before location))))
\r
547 (defgeneric after-special-rule (location)
\r
548 (:documentation "Runs on location before other after effects are runned")
\r
549 (:method (location) (declare (ignore location)) nil))
\r
550 (register-generic 'after-special-rule)
\r
552 (defmethod after-special-rule ((location room))
\r
553 (or (and *dark* (after darkness))
\r
554 (unless (eql *noun* *location*) (after location))))
\r
556 (supply before-hook ()
\r
557 "Allows for react-before and location interventions"
\r
558 (or (loop for x in *outscope*
\r
559 thereis (react-before x))
\r
560 (before-special-rule *location*)))
\r
562 (supply after-hook ()
\r
563 "Allows for react-after and location interventions"
\r
564 (or (loop for x in *outscope*
\r
565 thereis (react-after x))
\r
566 (after-special-rule *location*)))
\r
568 (define-condition input-quit-loop () ())
\r
570 (defun quit-game ()
\r
571 "Guess what it does?"
\r
572 (signal 'input-quit-loop))
\r
575 "Display the prompt for input"
\r
576 (princ "> " *outstream*))
\r
578 (defun prompt-read ()
\r
579 "Read input from user"
\r
580 (newline *outstream*) (prompt)
\r
581 (force-output *outstream*)
\r
582 (get-input *instream* *outstream*)
\r
583 (let ((result (read-line *instream*)))
\r
584 (newline *outstream*) result))
\r
586 ;(defun input-loop ()
\r
588 ; (loop (unless (zerop *gamestate*) (end-game))
\r
589 ; (parse-command (prompt-read)))
\r
590 ; (input-quit-loop () nil)))
\r
592 (defun input-loop-step ()
\r
593 "One step of the input loop"
\r
596 (unless (zerop *gamestate*) (end-game))
\r
597 (let ((input (prompt-read)))
\r
598 (when (equal input "!quit") (signal 'input-quit-loop))
\r
599 (parse-command input))
\r
600 (ltk-after 500 #'input-loop-step))
\r
601 (input-quit-loop () (close-console) nil)))
\r
604 "Called at the beginning of the game"
\r
605 "Warning: no init routine detected!")
\r
607 (defstub afterlife ()
\r
608 "Called when the player dies" t)
\r
610 (defstub print-gamestate ()
\r
611 "Called when gamestate is >2"
\r
614 (defun print-gamestate-default ()
\r
615 "Prints default end game messages"
\r
617 (1 "You have died")
\r
619 (t (print-gamestate))))
\r
622 "Called when the game ends"
\r
624 (sprint "~%~%~%***~a***~%~%~%" (print-gamestate-default))
\r
625 (sprint "Score:~a Turns:~a" *score* *turns*)
\r
630 "Load game sequence"
\r
631 (exec init ()) (go-to-room *location*)
\r
634 (defun lib (file dir)
\r
635 "Reloads the IF library"
\r
636 (format t "[DIRECTORY: ~a]~%" dir)
\r
637 (print-message "Loading if.fas...")
\r
638 (load (merge-pathnames dir "if.fas"))
\r
639 (print-message "Loading iflib.fas...")
\r
640 (load (merge-pathnames dir "iflib.fas"))
\r
641 (print-message "Loading verbs.fas...")
\r
642 (load (merge-pathnames dir "verbs.fas"))
\r
643 (print-message "Loading game module...")
\r
645 (print-message "Module is successfully loaded."))
\r
647 (defun test-seq (&optional (rm *repl-mode*))
\r
648 "Test sequence emulating interactive fiction interpreter"
\r
649 ;;(load-cfg "iflib.cfg")
\r
654 (run-console #'seq #'lib))
\r
656 (defun load-cfg (file)
\r
657 (when (probe-file file)
\r
658 (format t "Loading config file...~%")
\r
659 (with-open-file (s file)
\r
660 (loop for x in (read s)
\r
661 do (setf (symbol-value (car x)) (cdr x))))))
\r
663 (defun interactive-start ()
\r
664 "Function intended to be used by user"
\r
665 (load-cfg "iflib.cfg")
\r
666 (unless *repl-mode*
\r
667 (run-console #'seq #'lib :interactive t)
\r
668 (when *hard-quit* (quit-lisp))))
\r
670 ;;SECTION 9: Other stuff
\r
672 ;;Simpler defpackage for easy using
\r
674 (defmacro load-libs (name &rest other-packages)
\r
676 (:use :common-lisp :if-basic-lib :if-lib :verb-lib ,@other-packages)
\r
677 (:shadowing-import-from :if-lib :room)
\r
678 (:shadowing-import-from :verb-lib :listen :fill)))
\r
680 (defmacro free-symbol (id)
\r
681 "Frees a symbol from current package using shadow"
\r
682 `(eval-when (:compile-toplevel :load-toplevel :execute)
\r
686 (defun repl-mode (&optional (mode :unspecified))
\r
687 "Flip the using of REPL for input and output (as opposed to
\r
688 graphical interface)"
\r
689 (setf *repl-mode* (if (eql mode :unspecified) (not *repl-mode*) mode)))
\r
691 (defun compile-lib ()
\r
692 "Recompile the library. Useful to refresh old fasls."
\r
693 (compile-file "if.lisp")
\r
694 (compile-file "iflib.lisp")
\r
695 (compile-file "verbs.lisp"))
\r
698 (defun deliver-me ()
\r
699 (ext:saveinitmem "lifp.exe" :quiet t :norc t
\r
700 :init-function #'interactive-start
\r
701 :start-package :if-lib
\r