1;; Defines the basic stuff for the haxima quest system
2
3;; Create a new quest.
4;;
5;; title - a string that will be shown in the quest log listing and at the top
6;; of the quest pane
7;;
8;; tag - an optional tag (preferably unique) that can be used to retrieve the quest.
9;;
10;; descr - a list of strings (ie paragraph) that will be shown in the quest pane
11;;
12;; assign - an optional symbol[1] for a proc that will run when the quest is assigned;
13;; of the form (assign quest target), where 'quest' is the thing being created right
14;; here and 'target' is the (scheme) object the quest is being assigned to. Iff
15;; 'assign' returns #t then the quest will be added to the target's list of
16;; quests.
17;;
18;; status - an optional symbol [1] for a proc that will be called by the ztats pane, of
19;; the form (status quest), when the quest details are shown in the quest log.
20;; It is called before the description is written, so it may alter that if required.
21;; The method should return a list of strings to be appended to the description, or nil
22;; Note that this should not be used to update the icon or inprog/done/failed status, as
23;; they are used in the preceeding panel.
24;;
25;; icon - symbol [1] for sprite to use for the quest UI
26;;
27;; payload - whatever you want for your particular quest (this is an optional
28;; number of parms)
29;;
30;; (* optional = use nil to ignore)
31;;
32;; Example:
33;;
34;;   (qst-mk "Find 6 Foozles"
35;;           '(
36;;              "If you find 6 Foozles, Mr. Squeejie will give you an enchanted toothpick."
37;;				""
38;;              "Seek them out in distant Foozleburg"
39;;            )
40;;           'find-foozle-assign
41;;           'find-foozle-status
42;;			 's_quest_foozles
43;;           0 ; payload tracks num foozles found so far
44;;           )
45;;
46;; Notes:
47;;
48;; [1] The symbol of a proc named foo is 'foo. You must use a symbol because
49;; the name of the procedure must be saved as part of an object's gob. It would
50;; be nice if you could just pass in a lambda, but saving and reloading lambda
51;; closures is left as an exercise for the advanced reader. BTW, this rule
52;; applies within the payload lists as well.
53;;
54(define (qst-mk title tag descr assign status icon . payload)
55  (if (or (not (symbol? assign))
56          (not (symbol? status)))
57      (error "qst-mk: 'assign' and 'status' must be the symbols for procedures (ie, not the procedures themselves)"))
58  (list 'quest title tag descr assign status 'inprogress icon payload))
59
60(define (qst-title qst) (list-ref qst 1))
61
62(define (qst-tag qst) (list-ref qst 2))
63
64(define (qst-descr qst) (list-ref qst 3))
65
66(define (qst-assign qst target)
67  (println "qst-assign")
68  (apply (eval (list-ref qst 4))
69         (list qst target)))
70
71(define (qst-status qst)
72	(let ((statfn (list-ref qst 5)))
73		(if (not (null? statfn))
74			(apply (eval statfn) (list qst))
75		))
76)
77
78(define (qst-done? qst)
79  ;;(println "qst-done? qst=" qst)
80  (list-ref qst 6))
81
82(define (qst-done! qst result)
83	;;(kern-log-msg "^c+gYou have completed the quest ^c+w" (qst-title qst) "^c-!^c-")
84	(if (not (equal? (list-ref qst 6) result))
85		(begin
86		  	(list-set-ref! qst 6 result)
87		  	(qst-bump! qst)
88		)
89	))
90
91(define (qst-complete? qst)
92	(equal? (list-ref qst 6) 'complete))
93
94(define (qst-complete! qst)
95  (qst-done! qst 'complete))
96
97(define (qst-failed? qst)
98	(equal? (list-ref qst 6) 'failed))
99
100(define (qst-failed! qst)
101  (qst-done! qst 'failed))
102
103(define (qst-icon qst) (list-ref qst 7))
104
105(define (qst-payload qst) (list-ref qst 8))
106
107(define (quest-assign qst)
108  (println "quest-assign")
109  (let ((target (gob (kern-get-player))))
110    (if (and (notnull? qst)
111             (notnull? target)
112             (qst-assign qst target))
113        (begin
114        	(quest-insert qst)
115          ;;(tbl-append! target 'quests qst)
116          ;;(println "quest-assign: " target)
117          ;;(kern-log-msg "^c+gYou have a new quest: " (qst-title qst) "^c-")
118          ))))
119
120(define (quest-assigned? qst)
121	(println "quest-assigned?")
122	(let* ((target (gob (kern-get-player)))
123			(qstlist (tbl-get target 'quests))
124			)
125		(if (or (null? qst)
126				(null? qstlist)
127				)
128			#f
129			(in-list? qst qstlist)
130      	)
131	))
132
133;; first item, if any, else nil
134(define (safe-car alist)
135	(cond ((null? alist)
136		nil)
137		((pair? alist)
138		(car alist))
139		(#t alist)))
140
141(define (quest-get tag)
142	(safe-car
143		(filter
144			(lambda (quest) (eq? (qst-tag quest) tag))
145			(tbl-get (gob (kern-get-player)) 'quests)
146		)
147	))
148
149(define (quest-remove qst)
150	;; (cons a nil) = a; (cons nil b) != b;
151	(define (quest-remove-helper qstlist)
152		(if (null? qstlist) nil
153			(let ((qhead (safe-car qstlist)))
154				(println "rem? " (eq? qhead qst) " " )
155				(if (eq? qhead qst)
156					(cdr qstlist)
157					(cons
158						qhead
159						(quest-remove-helper (cdr qstlist))
160					)
161				)
162			)
163		))
164	(let* ((target (gob (kern-get-player)))
165			(trimmed  (quest-remove-helper (tbl-get target 'quests) qst))
166			)
167		(if (null? trimmed)
168			(tbl-rm! target 'quests)
169			(tbl-set! target 'quests trimmed)
170			)
171	))
172
173(define (qst-set-title! qst title) (list-set-ref! qst 1 title))
174(define (qst-set-descr! qst descr) (list-set-ref! qst 3 descr))
175(define (qst-set-icon! qst icon) (list-set-ref! qst 7 icon))
176
177;; bump the quest to the top of its appropriate list
178(define (qst-bump! quest)
179	(define (qst-bump-base! qst)
180		(if (quest-assigned? qst)
181			(begin
182				(quest-remove qst)
183				(quest-insert qst)
184			)
185		))
186	;; if we have a parent quest, bump that first
187	(let ((parent (quest-tbl-get quest 'qparent)))
188		(if (not (null? parent))
189			(let ((pqst (quest-get parent)))
190				(if (not (null? pqst))
191					(qst-bump! pqst)
192				))
193		))
194	(qst-bump-base! quest)
195	;; if we have children, bump them
196	(let ((childlist (quest-tbl-get quest 'qchildren)))
197		(println childlist)
198		(map (lambda (entry)
199				(let ((cqst (quest-get entry)))
200					(if (not (null? cqst))
201						(qst-bump-base! cqst)
202						)
203					))
204			 childlist)
205		)
206	)
207
208(define (quest-insert qst)
209	(let* ((target (gob (kern-get-player)))
210			(targlist (tbl-get target 'quests))
211			(inserttype (qst-done? qst))
212			(parent (quest-tbl-get qst 'qparent))
213			)
214		(define (insert-here? testee)
215			(cond ((eq? inserttype 'inprogress) #t)
216				((eq? inserttype (qst-done? testee)) #t)
217				((eq? 'failed (qst-done? testee)) #t)
218				(#t #f))
219			)
220		(define (quest-insert-helper qstlist)
221			(if (null? qstlist) (list qst)
222				(let ((qhead (safe-car qstlist)))
223					(if (insert-here? qhead)
224						(cons qst qstlist)
225						(cons
226							qhead
227							(quest-insert-helper (cdr qstlist))
228						)
229					)
230				)
231			))
232		(define (quest-insertchild-helper qstlist)
233			(if (null? qstlist) (list qst)
234				(let ((qhead (safe-car qstlist)))
235					(if (or (not (equal? parent (quest-tbl-get qhead 'qparent)))
236							(insert-here? qhead))
237						(cons qst qstlist)
238						(cons
239							qhead
240							(quest-insertchild-helper (cdr qstlist))
241						)
242					)
243				)
244			))
245		(define (quest-insert-findparent qstlist)
246			(if (null? qstlist) (nil)
247				(let ((qhead (safe-car qstlist)))
248					(if (equal? parent (qst-tag qhead))
249						(cons
250							qhead
251							(quest-insertchild-helper (cdr qstlist))
252						)
253						(cons
254							qhead
255							(quest-insert-findparent (cdr qstlist))
256						)
257					)
258				)
259			))
260		(cond ((null? targlist) (tbl-append! target 'quests qst))
261			((null? parent) (tbl-set! target 'quests (quest-insert-helper targlist)))
262			(#t (tbl-set! target 'quests (quest-insert-findparent targlist)))
263			)
264	))
265
266;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
267;; Some special handling for quests with tbl payloads
268
269(define (quest-tbl? quest)
270	(let ((qpayload (qst-payload quest)))
271		(cond ((not (pair? qpayload)) #f)
272			 ((not (pair? (car qpayload))) #f)
273			(#t (is-tbl? (car qpayload)))
274		)
275	))
276
277(define (quest-tbl-get quest tag)
278	(let ((qpayload (qst-payload quest)))
279		(cond ((not (pair? qpayload)) nil)
280			 ((not (pair? (car qpayload))) nil)
281			((not (is-tbl? (car qpayload))) nil)
282			(#t (tbl-get (car qpayload) tag))
283		)
284	))
285