2 ;; init.scm -- contains lots of common scheme utilities
5 ;; Result codes (these belong here because they are tied to kernel values, see
8 (define result-no-target 1)
9 (define result-no-effect 2)
10 (define result-no-hostiles 3)
11 (define result-lacks-skill 4)
12 (define result-failed 5)
13 (define result-not-here 6)
14 (define result-critical-fail 7)
15 (define result-not-now 8)
17 ;; Test if a result code indicates that the action was not completed
18 (define (abortive-result? result)
19 (or (eq? result result-no-target)
20 (eq? result result-not-here)))
22 ;; Override the default error hook to warn the loader when the interpreter
23 ;; encounters any errors during evaluation.
24 (define (my-error-hook . x)
27 (define *error-hook* my-error-hook)
29 ;; kern-load -- loads a file but also tells the kernel to make a note of it so
30 ;; that saved sessions will know to use the file, too.
31 (define (kern-load fname)
37 ;; safe-eval -- evaluates an expression; bad expressions evaluate to '()
38 ;; instead of causing an interpreter error
39 (define (safe-eval expr)
40 (cond ((null? expr) '())
47 ;; filter -- filter-in elements from a list
48 (define (filter pred seq)
49 (cond ((null? seq) nil)
52 (filter pred (cdr seq))))
53 (else (filter pred (cdr seq)))))
55 ;; simple, non-prioritized, generic search (not very efficient, so don't try
56 ;; it on large search spaces)
57 (define (search here? next start maxdepth)
58 (define (do-search queue visited depth)
59 (if (or (= depth 0) (null? queue)) nil
60 (let ((loc (car queue)))
62 (do-search (append (cdr queue)
63 (filter (lambda (v) (not (member v visited)))
65 (append (list loc) visited)
67 (do-search (list start) nil maxdepth))
69 ;; similar to search, but a) it's exhaustive and b) it runs a procedure on
70 ;; every entry (warning: not sure if or how well this works)
71 (define (bfs-apply start next proc)
72 (define (do-search queue visited)
74 (let ((loc (car queue)))
76 (do-search (append (cdr queue)
77 (filter (lambda (v) (not (member v visited)))
79 (append (list loc) visited)))))
80 (do-search (list start) nil))
82 ;; Set element k of list x to val (zero-indexed)
83 (define (list-set-ref! x k val)
86 (list-set-ref! (cdr x) (- k 1) val)))
88 ;; Check if a list contains an element.
89 (define (in-list? elem lst)
90 (foldr (lambda (a b) (or a (eqv? b elem)))
94 (define (in-text-list? elem lst)
95 (foldr (lambda (a b) (or a (equal? b elem)))
99 ;; Check if a location is passable to a character
100 (define (passable? loc kobj)
101 (kern-place-is-passable loc kobj))
103 (define (obj-is-char? kobj) (kern-obj-is-being? kobj))
104 (define (is-being? kobj) (kern-obj-is-being? kobj))
106 ;; Check if a location is occupied by a character or party
107 (define (occupied? loc)
108 (foldr (lambda (val kobj) (or val (obj-is-char? kobj)))
110 (kern-get-objects-at loc)))
112 (define (get-beings-at loc)
113 (filter kern-obj-is-being?
114 (kern-get-objects-at loc)))
116 ;; Given a starting location, search outward for a passable, unoccupied place
117 ;; to put a character.
118 (define (pick-loc origin char)
119 (search (lambda (loc) (and (kern-is-valid-location? loc)
121 (not (occupied? loc))))
126 ;; Generic proc to summon other beings. Used by spells and some special
128 (define (summon origin mk-critter faction count)
131 (let* ((critter (kern-obj-set-temporary (kern-being-set-base-faction
135 (loc (pick-loc origin critter)))
136 (cond ((null? loc) nil)
138 (kern-obj-put-at critter loc)
139 (run-loop (- n 1)))))))
142 ;; Like summon but the beings are permanent, not temporary.
143 (define (psummon origin mk-critter count)
144 ;;;(display "psummon")(newline)
147 (let* ((critter (kern-obj-inc-ref (mk-critter)))
148 (loc (pick-loc origin critter)))
149 (cond ((null? loc) (kern-obj-dec-ref critter))
151 (kern-obj-put-at critter loc)
152 (kern-obj-dec-ref critter)
153 (run-loop (- n 1)))))))
156 ;; check if klooker can see kobj
157 (define (can-see? klooker kobj)
158 (let ((from (kern-obj-get-location klooker))
159 (to (kern-obj-get-location kobj)))
160 (and (kern-in-los? from to)
161 (<= (kern-get-distance from to)
162 (kern-obj-get-vision-radius klooker))
163 (kern-obj-is-visible? kobj))))
165 ;; check if klooker can can see anything in the list kobs
166 (define (can-see-any? klooker kobjs)
169 (or (can-see? klooker (car kobjs))
170 (can-see-any? klooker (cdr kobjs)))))
172 ;; check if knpc can see any of the player party members
173 (define (any-player-party-member-visible? knpc)
175 (kern-party-get-members (kern-get-player))))
177 ;; gets location of player character (not party- ie 'works' in temporary map)
178 (define (player-member-loc)
179 (let ((loc (kern-obj-get-location (car (kern-party-get-members (kern-get-player))))))
184 (define (num-player-party-members)
185 ;;(display "num-player-party-members")(newline)
186 (length (kern-party-get-members (kern-get-player))))
188 (define (is-only-living-party-member? kchar)
189 (and (is-alive? kchar)
190 (is-player-party-member? kchar)
191 (not (foldr (lambda (found kchar2)
192 (println found " " (kern-obj-get-name kchar2))
194 (and (not (eqv? kchar kchar2))
195 (is-alive? kchar2))))
197 (kern-party-get-members (kern-get-player))))
200 ;; Check if an object is hostile toward a character
201 (define (is-hostile? kbeing kobj)
202 (and (is-being? kobj)
203 (kern-being-is-hostile? kbeing kobj)))
205 ;; Check if an object is allied with a character
206 (define (is-ally? kbeing kobj)
207 (kern-being-is-ally? kbeing kobj))
209 ;; Find all characters hostile to the given character
210 (define (all-hostiles kchar)
211 (filter (lambda (kobj) (is-hostile? kchar kobj))
212 (kern-place-get-beings (loc-place (kern-obj-get-location kchar)))))
214 ;; Find all friendlies
215 (define (all-allies kchar)
216 (filter (lambda (kobj) (is-ally? kchar kobj))
217 (kern-place-get-beings (loc-place (kern-obj-get-location kchar)))))
220 ;; Count the number of hostiles
221 (define (num-hostiles kchar)
222 (length (all-hostiles kchar)))
224 ;; Count the number of friendlies
225 (define (num-allies kchar)
226 (length (all-allies kchar)))
228 ;; Find all beings hostile
229 (define (all-visible-hostiles kbeing)
230 (kern-being-get-visible-hostiles kbeing))
232 (define (any-visible-hostiles? kchar)
233 (> (length (all-visible-hostiles kchar)) 0))
235 (define (nearest-visible-hostile kchar)
236 (nearest-obj kchar (all-visible-hostiles kchar)))
239 (define (all-visible-allies kbeing)
240 (kern-being-get-visible-allies kbeing))
242 ;; Count the number of visible friendlies
243 (define (num-visible-allies kchar)
244 (length (all-visible-allies kchar)))
246 ;; Count the number of hostiles
247 (define (num-visible-hostiles kchar)
248 (length (all-visible-hostiles kchar)))
251 ;; Find all the characters in a place
252 (define (all-chars kplace)
253 (kern-place-get-beings kplace))
255 ;; Check if an object is in the given range of the origin point
256 (define (in-range? origin radius kobj)
257 (<= (kern-get-distance origin
258 (kern-obj-get-location kobj))
261 ;; Check if a character's target is in range
262 (define (can-hit? kchar ktarg range)
263 ;;(println "can-hit: " range)
264 (in-range? (kern-obj-get-location kchar)
268 ;; Filter objects out of range
269 (define (all-in-range origin radius objlst)
270 (filter (lambda (kobj)
271 (<= (kern-get-distance origin
272 (kern-obj-get-location kobj))
276 ;; Return a list of all hostiles in range of the given location
277 (define (get-hostiles-in-range-of-loc kchar range loc)
280 (kern-being-get-visible-hostiles kchar)))
282 ;; Return a list of all hostiles in range of the kchar's current location
283 (define (get-hostiles-in-range kchar range)
284 (get-hostiles-in-range-of-loc kchar
286 (kern-obj-get-location kchar)))
288 ;; Return a list of beings within the given range
289 (define (get-beings-in-range kobj range)
290 (let ((loc (kern-obj-get-location kobj)))
293 (kern-place-get-beings (loc-place loc)))))
295 ;; Convenience proc for rolling dtables by hand
296 (define (dtable-row . cols) cols)
298 (define (distance kobj-a kobj-b)
299 (let ((loc-a (kern-obj-get-location kobj-a))
300 (loc-b (kern-obj-get-location kobj-b)))
301 (kern-get-distance loc-a loc-b)))
303 ;; Inefficient code to find nearest obj from a list
304 (define (nearest-obj kobj klist)
305 (if (null? klist) nil
306 (foldr (lambda (a b) (if (< (distance kobj a) (distance kobj b)) a b))
307 (car klist) (cdr klist))))
309 ;; Inefficient code to find nearest location from a list
310 (define (nearest-loc kobj klist)
311 (println "nearest-loc: " klist)
314 (let ((kloc (kern-obj-get-location kobj)))
316 (if (< (loc-city-block-distance kloc a)
317 (loc-city-block-distance kloc b))
323 ;; Move an object one step along a path to a destination.
324 (define (old-pathfind kobj dest)
325 ;;;;(display "pathfind")(newline)
326 (define (follow-path path)
327 (if (not (null? path))
328 (let ((coords (car path))
329 (origin (kern-obj-get-location kobj)))
330 ;;;;(display "pathfind:coords=");;(display coords)(newline)
331 (let ((dx (- (car coords) (loc-x origin)))
332 (dy (- (cdr coords) (loc-y origin))))
333 (kern-obj-move kobj dx dy)))))
334 (let ((path (kern-obj-find-path kobj dest)))
335 ;;;;(display "pathfind:path=");;(display path)(newline)
336 (if (not (null? path))
337 ;; skip the first location in the path
338 (follow-path (cdr path)))))
340 ;; pathfind - use the built-in kernel call that uses cached paths and tries to
341 ;; handle blocking mechanisms
342 (define (pathfind kobj kdest)
343 ;;(println "pathfind(" (kern-obj-get-name kobj) "," kdest ")")
344 (and (kern-obj-is-being? kobj)
345 (kern-being-pathfind-to kobj kdest)))
347 (define (can-pathfind? kobj dest)
348 (println "can-pathfind?")
349 (or (loc-8-adjacent? dest
350 (kern-obj-get-location kobj))
351 (not (null? (kern-obj-find-path kobj dest)))))
353 (define (notnull? val) (not (null? val)))
355 (define (being-at? loc)
356 (not (null? (filter kern-obj-is-being? (kern-get-objects-at loc)))))
358 (define (get-being-at loc)
359 (let ((beings (filter kern-obj-is-being? (kern-get-objects-at loc))))
364 (define (is-dead? kchar)
365 (kern-char-is-dead? kchar))
367 (define (is-alive? kchar)
368 (not (is-dead? kchar)))
370 (define (has-ap? kobj)
371 (> (kern-obj-get-ap kobj) 0))
373 (define (has-ap-debt? kobj)
374 (< (kern-obj-get-ap kobj) 0))
376 (define (has-skill? kchar kskill)
378 (kern-char-get-skills kchar)))
381 ;;;(display "flee")(newline)
382 (kern-char-set-fleeing kchar #t))
384 (define (wander kchar)
385 (kern-obj-wander kchar))
387 (define (weakest kchar-a kchar-b)
388 (if (< (kern-char-get-hp kchar-a)
389 (kern-char-get-hp kchar-b))
393 (define (join-player kchar)
394 (kern-char-join-player kchar))
396 (define (random-select list)
400 (list-ref list (modulo (random-next) (length list)))))
402 (define (taunt kchar ktarg taunts)
403 (say kchar (random-select taunts)))
405 ;; ----------------------------------------------------------------------------
406 ;; search-rect -- apply a procedure to every location in a rectangular region
407 ;; and return a list of its non-nil results.
408 ;; ----------------------------------------------------------------------------
409 (define (search-rect kplace x y w h proc)
410 (filter notnull? (map proc (loc-enum-rect kplace x y w h))))
412 ;; ----------------------------------------------------------------------------
413 ;; foldr-rect -- similar to search-rect above, but the procedure must
414 ;; accumulate its own results. Faster because it doesn't have to run the
416 ;; ----------------------------------------------------------------------------
417 (define (foldr-rect kplace x y w h proc ival)
418 (foldr proc ival (loc-enum-rect kplace x y w h)))
420 ;;----------------------------------------------------------------------------
421 ;; Return a list of locations with matching terrain
422 ;;----------------------------------------------------------------------------
423 (define (find-terrain kplace x y w h kter)
425 (if (eqv? (kern-place-get-terrain loc) kter)
428 (search-rect kplace x y w h check))
430 (define (on-terrain? kobj kter)
431 (eqv? kter (kern-place-get-terrain (kern-obj-get-location kobj))))
433 (define (all-visible-terrain-of-type kobj kter)
436 (kern-place-get-terrain x)))
437 (kern-being-get-visible-tiles kobj)))
439 (define (find-nearest-visible-terrain-of-type kobj kter)
440 (nearest-loc kobj (all-visible-terrain-of-type kobj kter)))
442 (define (hidden? kchar)
443 ;;(println "hidden?")
444 ;; Just check if the 8 neighbors are all los-blocking
445 (let ((loc (kern-obj-get-location kchar)))
446 (foldr-rect (loc-place loc)
447 (- (loc-x loc) 1) (- (loc-y loc) 1)
449 (lambda (val neighbor)
450 ;;(println neighbor " neighbor? " (equal? neighbor loc) " blocks? " (kern-place-blocks-los? neighbor))
452 (or (eq? neighbor loc)
453 (kern-place-blocks-los? neighbor))))
457 ;; kobj-is-type -- check if the object is of the given type
458 (define (kobj-is-type? kobj ktype)
459 (eqv? (kern-obj-get-type kobj)
462 ;; kplace-get-objects-of-type -- return a list of all objects of the given type
463 ;; in the given place
464 (define (kplace-get-objects-of-type kplace ktype)
465 (filter (lambda (kobj) (kobj-is-type? kobj ktype))
466 (kern-place-get-objects kplace)))
468 ;;----------------------------------------------------------------------------
469 ;; find-objects -- return a list of locations with the given object on them
470 ;;----------------------------------------------------------------------------
471 (define (find-objects kplace x y w h ktype)
473 (define (scanobjlst lst)
475 (or a (kobj-is-type? b ktype)))
478 (if (scanobjlst (kern-get-objects-at loc))
481 (search-rect kplace x y w h check))
483 (define (in-inventory? kchar ktype)
484 ;;(println (kern-type-get-name ktype))
485 (define (hasit? item inv)
486 (cond ((null? inv) #f)
487 ((eqv? item (car (car inv))) #t)
489 ;;(println " " (kern-type-get-name (car (car inv))))
490 (hasit? item (cdr inv)))))
491 (hasit? ktype (kern-char-get-inventory kchar)))
493 (define (num-in-inventory kchar ktype)
494 (define (count-em item inv)
495 ;;;(display "inv: ");;(display inv)(newline)
496 (cond ((null? inv) 0)
497 ((eqv? item (car (car inv))) (cdr (car inv)))
498 (else (count-em item (cdr inv)))))
499 (count-em ktype (kern-char-get-inventory kchar)))
501 (define (any-in-inventory? kchar lst)
504 (in-inventory? kchar k)))
508 (define (all-in-inventory? kchar lst)
511 (in-inventory? kchar k)))
515 ;; Note: I commented out the remove-from-inventory call because the things
516 ;; should remove themselves (eg, potions do)
517 (define (use-item-from-inventory-on-self kchar ktype)
518 ;;(kern-obj-remove-from-inventory kchar ktype 1)
519 ;;;(display "using")(newline)
520 (apply (kern-type-get-gifc ktype) (list 'use ktype kchar))
521 (kern-log-msg (kern-obj-get-name kchar)
523 (kern-type-get-name ktype)
527 ;;============================================================================
528 ;; Modulo system procedures -- useful on wrapping maps
529 ;;============================================================================
530 (define (madd a b R) (modulo (+ a b) R))
531 (define (msub a b R) (modulo (- a b) R))
532 (define (minc a R) (modulo (+ a 1) R))
533 (define (mdec a R) (modulo (- a 1) R))
535 ;;----------------------------------------------------------------------------
536 ;; mdist - find the distance between two numbers in a modulo system. There are
537 ;; always 2 distances (additive and subtractive). This picks the shortest
539 ;;----------------------------------------------------------------------------
540 (define (mdist a b R) (min (msub a b R) (msub b a R)))
542 ;; ----------------------------------------------------------------------------
543 ;; Turn on/off verbose scheme garbage collection. Useful if you think scheme is
544 ;; gc'ing some of your code behind your back.
545 ;; ----------------------------------------------------------------------------
548 (define (profile proc . args)
549 (let ((t (kern-get-ticks))
550 (result (apply proc args)))
551 ;;(display "*** TIME: ");;(display (- (kern-get-ticks) t)) ;;(display " ms")
555 ;; ----------------------------------------------------------------------------
556 ;; find-object-types-at -- return a list of objects of the given type which can
557 ;; be found at the given location
558 ;; ----------------------------------------------------------------------------
559 (define (find-object-types-at loc ktype)
560 (filter (lambda (a) (kobj-is-type? a ktype))
561 (kern-get-objects-at loc)))
563 ;; ----------------------------------------------------------------------------
564 ;; is-object-type-at? -- check for an object (by type) at a location
565 ;; ----------------------------------------------------------------------------
566 (define (is-object-type-at? loc ktype)
567 (foldr (lambda (a b) (or a (kobj-is-type? b ktype)))
569 (kern-get-objects-at loc)))
571 ;; ----------------------------------------------------------------------------
572 ;; any-object-types-at? -- returns #t iff one or more objects at loc is of one
573 ;; of the given types
574 ;; ----------------------------------------------------------------------------
575 (define (any-object-types-at? loc ktypes)
576 (foldr (lambda (a b) (or a (is-object-type-at? loc b)))
580 ;; is-player-party-member? -- #t iff kchar is in player party
581 (define (is-player-party-member? kchar)
583 (kern-party-get-members (kern-get-player))))
585 ;; ----------------------------------------------------------------------------
586 ;; kobj-get -- remove an object from the map and put it into another object
587 ;; ----------------------------------------------------------------------------
588 (define (kobj-get kobj kchar)
589 (if (kern-obj-put-into kobj kchar)
591 (if (not (is-player-party-member? kchar))
592 (kern-log-msg (kern-obj-get-name kchar)
594 (kern-obj-get-name kobj)
596 (kern-obj-inc-ref kobj)
597 (kern-obj-remove kobj)
598 (kern-obj-dec-ref kobj)
599 (kern-obj-dec-ap kchar (/ norm 5))
600 (kern-map-repaint))))
602 ;; ----------------------------------------------------------------------------
603 ;; kobj-get-at -- get an object of a specific type from the location
604 ;; ----------------------------------------------------------------------------
605 (define (kobj-get-at kchar loc ktype)
606 (let ((objs (find-object-types-at loc ktype)))
608 (kobj-get (car objs) kchar))))
610 ;; ----------------------------------------------------------------------------
611 ;; place-random-corner -- randomly select a corner and return it as a location
612 ;; ----------------------------------------------------------------------------
613 (define (place-random-corner kplace)
614 (case (kern-dice-roll "1d4")
615 ((1) (mk-loc kplace 0 0))
616 ((2) (mk-loc kplace 0 (- (kern-place-get-width kplace 1))))
617 ((3) (mk-loc kplace (- (kern-place-get-height kplace) 1) 0))
619 (- (kern-place-get-height kplace) 1)
620 (- (kern-place-get-width kplace) 1)))))
622 ;; ----------------------------------------------------------------------------
623 ;; do-or-goto -- if the location is adjacent then the proc, otherwise have
624 ;; the char pathfind to it
625 ;; ----------------------------------------------------------------------------
626 (define (do-or-goto kchar coords proc)
627 ;;;(display "do-or-goto")(newline)
628 (if (or (loc-4-adjacent? (kern-obj-get-location kchar) coords)
629 (eq? coords (kern-obj-get-location kchar)))
631 (pathfind kchar coords)))
633 ;; ----------------------------------------------------------------------------
634 ;; evade -- simple alg for evading melee foes
636 ;; Simple approach: each foe's coordinates forms a vector to the char's
637 ;; coordinates. Take the sum of these coordinates to get the evasion
638 ;; vector. "Normalize" the vector components by rounding them to the nearest 0,
639 ;; 1 or -1. This is the dx/dy to move. If the terrain is impassable in the
640 ;; preferred direction then try zeroing out the non-zero components and
641 ;; moving. This will give two backup vectors to try.
643 ;; ADDENDUM: I don't want to allow diagonal evasion, so the "normalized" vector
644 ;; must be skipped if it's a diagonal, thus causing us to try the fallbak
647 ;; Now allowing diagonals, since that factor has changed
649 ;; TODO: probably shouldnt flee over dangerous terrains
651 ;; ----------------------------------------------------------------------------
652 (define (evade kchar foes)
653 (let* ((tloc (kern-obj-get-location kchar))
656 (lambda (accum thisfoe)
658 (loc-diff (kern-obj-get-location thisfoe) tloc)
660 (mk-loc (loc-place tloc) 0 0)
665 (if (kern-place-is-passable
667 (mk-loc (loc-place tloc) dx dy)
670 (kern-obj-move kchar dx dy)
672 (define (evade-on-normal)
673 (move (loc-x v) (loc-y v)))
675 (or (evade-on-normal)
676 (and (not (eq? 0 (loc-y v)))
678 (and (not (eq? 0 (loc-x v)))
683 ;; ----------------------------------------------------------------------------
684 ;; closest-obj -- given an origin and a list of objects, return the object from
685 ;; the list that is closest (in city-block distance) to the origin
686 ;; ----------------------------------------------------------------------------
687 (define (closest-obj origin lst)
690 (if (loc-closer? (kern-obj-get-location a)
691 (kern-obj-get-location b)
698 ;; ----------------------------------------------------------------------------
699 ;; blit-maps -- blit multiple maps to a single target map
700 ;; ---------------------------------------------------------------------------
701 (define (blit-maps kmap . blits)
702 (define (blit dstx dsty srcmap srcx srcy w h)
703 (kern-blit-map kmap dstx dsty srcmap srcx srcy w h))
704 (foldr (lambda (a b) (apply blit b))
708 (define (fill-terrain-prob kter kplace ox oy ow oh prob)
709 (define (fill x y w h)
713 (if (<= (modulo (random-next)
716 (kern-place-set-terrain (list kplace x y) kter))
717 (fill (+ x 1) y (- w 1) h))
718 (fill ox (+ y 1) ow (- h 1)))))
721 (define (fill-terrain kter kplace ox oy ow oh)
722 (fill-terrain-prob kter kplace ox oy ow oh 100))
724 ;;============================================================================
726 ;;============================================================================
727 (define (mk-rect x y w h) (list x y w h))
728 (define (rect-x r) (car r))
729 (define (rect-y r) (cadr r))
730 (define (rect-w r) (caddr r))
731 (define (rect-h r) (cadddr r))
732 (define (rect-ex r) (+ (rect-x r) (rect-w r)))
733 (define (rect-ey r) (+ (rect-y r) (rect-h r)))
734 (define (x-in-rect? x r)
735 (and (>= x (rect-x r))
737 (define (y-in-rect? y r)
738 (and (>= y (rect-y r))
740 (define (xy-in-rect? x y r)
741 (and (x-in-rect? x r)
743 (define (rect-in-rect? a b)
744 (and (xy-in-rect? (rect-x a) (rect-y a) b)
745 (xy-in-rect? (rect-ex a) (rect-ey a) b)))
746 (define (loc-in-rect? loc rect)
747 (xy-in-rect? (loc-x loc)
750 (define (rect-random rect)
751 (list (+ (rect-x rect) (modulo (random-next) (rect-w rect)))
752 (+ (rect-y rect) (modulo (random-next) (rect-h rect)))))
754 ;;;; (define original-load load)
755 ;;;; (define (load file)
756 ;;;; (display (kern-get-ticks))
757 ;;;; (display " loading ")
758 ;;;; (display file)(newline)
759 ;;;; (original-load file))
761 (define (put obj x y) (list obj x y))
763 ;; lookup-spell-by-handler -- find a spell in the list of all spells
764 (define (lookup-spell handler)
765 (define (search-spells slist)
768 (let ((spell (car slist)))
769 (if (eqv? (spell-handler spell)
772 (search-spells (cdr slist))))))
773 (search-spells spells))
776 (define (lookup this? slist)
779 (if (this? (car slist))
781 (lookup this? (cdr slist)))))
783 ;; can-cast -- check if a char has enough mana to cast a spell
784 (define (can-cast? kchar handler)
785 (let ((spell (lookup-spell handler)))
788 (and (>= (kern-char-get-mana kchar)
790 (>= (kern-char-get-level kchar)
791 (spell-level spell))))))
793 ;; cast0 - cast a spell which requires no args if possible, assumes kchar has
795 (define (cast0 kchar spell)
796 (apply (spell-handler spell) (list kchar))
797 (kern-char-dec-mana kchar (spell-cost spell))
798 (kern-obj-dec-ap kchar (spell-ap spell))
799 (kern-log-msg (kern-obj-get-name kchar)
804 ;; cast1 - cast a spell which requires one arg if possible, assumes kchar has
806 (define (cast1 kchar spell ktarg)
807 ;;;(display "cast1: ");;(display spell)(newline)
808 (apply (spell-handler spell) (list kchar ktarg))
809 (kern-char-dec-mana kchar (spell-cost spell))
810 (kern-obj-dec-ap kchar (spell-ap spell))
811 (kern-log-msg (kern-obj-get-name kchar)
815 (kern-obj-get-name ktarg)
818 ;; ----------------------------------------------------------------------------
819 ;; terrain-ok-for-field? -- check if the terrain at a given location will allow
820 ;; a field to be dropped on it. Terrains with passability class equivalent to
821 ;; Grass, trees and forest are ok, everything else is not.
822 ;; ----------------------------------------------------------------------------
823 (define (terrain-ok-for-field? loc)
824 (let ((kter (kern-place-get-terrain loc)))
825 (println "kter: " kter)
828 (let ((pclass (kern-terrain-get-pclass kter)))
829 (foldr (lambda (a b) (or a (= pclass b)))
831 (list pclass-grass pclass-trees pclass-forest))))))
833 (define (get-8-neighboring-tiles loc)
834 (let ((kplace (loc-place loc))
837 (filter kern-is-valid-location?
838 (map (lambda (offset) (mk-loc kplace
850 (define (get-4-neighboring-tiles loc)
851 (let ((kplace (loc-place loc))
854 (filter kern-is-valid-location?
855 (map (lambda (offset) (mk-loc kplace
864 (define (shake-map dur)
867 (kern-map-set-jitter #t)
869 (shake-map (- dur 1)))
871 (kern-map-set-jitter #f)
872 (kern-map-repaint))))
874 (define (random-vdir)
875 (random-select (list (cons -1 0)
880 (define (random-neighbor-loc kobj)
881 (let ((vdir (random-vdir)))
882 (loc-sum (kern-obj-get-location kobj)
883 (mk-loc nil (car vdir) (cdr vdir)))))
885 (define (push kobj dx dy dist)
886 (let* ((loc (loc-sum (kern-obj-get-location kobj)
887 (mk-loc nil dx dy))))
888 (if (and (kern-place-is-passable loc kobj)
889 (not (occupied? loc)))
891 (kern-obj-relocate kobj loc nil)
895 (define (stagger kchar)
896 (let ((vdir (random-vdir)))
897 (push kchar (car vdir) (cdr vdir) 1)))
899 (define (end-turn kobj)(kern-obj-set-ap kobj 0))
901 (define (add-effect-multiple kobj keff fgob q)
904 (kern-obj-add-effect kobj keff fgob)
905 (add-effect-multiple kobj keff fgob (- q 1)))))
907 ;; time procs for use with return value from kern-get-time:
908 (define (time-mk yr mo we da hr mi)
909 (list yr mo we da hr mi))
910 (define (time-year time) (list-ref time 0))
911 (define (time-month time) (list-ref time 1))
912 (define (time-week time) (list-ref time 2))
913 (define (time-day time) (list-ref time 3))
914 (define (time-hour time) (list-ref time 4))
915 (define (time-minute time) (list-ref time 5))
917 ;; wants-healing? -- check if a char is <= 50% max hp
918 (define (wants-healing? kchar)
919 (<= (kern-char-get-hp kchar)
920 (/ (kern-char-get-max-hp kchar) 2)))
922 ;; wants-healing? -- check if a char is <= 25% max hp
923 (define (wants-great-healing? kchar)
924 (<= (kern-char-get-hp kchar)
925 (/ (kern-char-get-max-hp kchar) 4)))
927 ;; wants-mana? -- check if a char is <= 50% max mana
928 (define (wants-mana? kchar)
929 (<= (kern-char-get-mana kchar)
930 (/ (kern-char-get-max-mana kchar) 2)))
932 ;; has-mana-potion? -- check if a char has a mana potion in inventory
933 (define (has-mana-potion? kchar)
934 (in-inventory? kchar t_mana_potion))
936 ;; drink-mana-potion -- use a mana potion from inventory
937 (define (drink-mana-potion kchar)
938 (use-item-from-inventory-on-self kchar t_mana_potion))
940 ;; has-heal-potion? -- check if a char has a heal potion in inventory
941 (define (has-heal-potion? kchar)
942 (in-inventory? kchar t_heal_potion))
944 ;; drink-heal-potion -- use a heal potion from inventory
945 (define (drink-heal-potion kchar)
946 (use-item-from-inventory-on-self kchar t_heal_potion))
948 (define (set-max-hp kchar)
949 (kern-char-set-hp kchar
950 (kern-char-get-max-hp kchar)))
952 ;; max-hp -- calc max hp given species, level and occ
953 (define (max-hp sp occ lvl mod mult)
954 (+ (kern-species-get-hp-mod sp)
955 (if (null? occ) 0 (kern-occ-get-hp-mod occ))
958 (+ (kern-species-get-hp-mult sp)
959 (if (null? occ) 0 (kern-occ-get-hp-mult occ))
962 ;; max-mp -- calc max mp given species, level and occ
963 (define (max-mp sp occ lvl mod mult)
964 (+ (kern-species-get-mp-mod sp)
965 (if (null? occ) 0 (kern-occ-get-mp-mod occ))
968 (+ (kern-species-get-mp-mult sp)
969 (if (null? occ) 0 (kern-occ-get-mp-mult occ))
973 ;; set-level -- set character to level and max out hp and mana (intended for
975 (define (set-level kchar lvl)
976 (kern-char-set-level kchar lvl))
978 ;; use-potion? -- use potion on self if desired and available
979 (define (use-potion? kchar)
980 (or (and (wants-healing? kchar)
981 (has-heal-potion? kchar)
982 (drink-heal-potion kchar))
983 (and (wants-mana? kchar)
984 (has-mana-potion? kchar)
985 (drink-mana-potion kchar))))
987 (define (use-heal-spell-on-self? kchar)
988 ;;;;(display "use-heal-spell-on-self?")(newline)
989 (and (wants-healing? kchar)
990 (can-use-ability? heal-ability kchar)
991 (use-ability heal-ability kchar kchar)))
993 (define (use-great-heal-spell-on-self? kchar)
994 ;;;;(display "use-great-heal-spell-on-self?")(newline)
995 (and (wants-great-healing? kchar)
996 (can-use-ability? great-heal-ability kchar)
997 (use-ability great-heal-ability kchar kchar)))
999 (define (use-spell-on-self? kchar)
1000 ;;;;(display "use-spell-on-self?")(newline)
1001 (or (use-great-heal-spell-on-self? kchar)
1002 (use-heal-spell-on-self? kchar)))
1004 (define (avoid-melee? kchar)
1005 ;;;;(display "avoid-melee? kchar")(newline)
1006 (let ((nearby-foes (get-hostiles-in-range kchar 1)))
1007 (if (null? nearby-foes)
1009 (evade kchar nearby-foes))))
1011 (define (dump-char kchar)
1015 (println "npc: " (kern-obj-get-name kchar)
1016 "[" (kern-char-get-level kchar) "]"
1017 " hp=" (kern-char-get-hp kchar) "/" (kern-char-get-max-hp kchar)
1018 " mp=" (kern-char-get-mana kchar) "/" (kern-char-get-max-mana kchar)
1019 " @[" (loc-x (kern-obj-get-location kchar))
1020 "," (loc-y (kern-obj-get-location kchar)) "]"
1024 (define (get-nearest-patient kchar)
1025 (let ((kloc (kern-obj-get-location kchar)))
1026 (foldr (lambda (kpatient ktarg)
1027 ;;(display " checking ")(dump-char ktarg)
1028 (if (and (wants-healing? ktarg)
1029 (or (null? kpatient)
1030 (< (kern-get-distance kloc
1031 (kern-obj-get-location ktarg))
1032 (kern-get-distance kloc
1033 (kern-obj-get-location kpatient)))))
1037 (all-visible-allies kchar))))
1039 ;; This is for medics. A patient is an ally that needs healing. If a patient is
1040 ;; less than 2 tiles away then do nothing. If a patient is more than 2 tiles
1041 ;; away then pathfind toward it.
1042 (define (move-toward-patient? kchar)
1043 (let ((patient (get-nearest-patient kchar)))
1047 ;;(display "selected ")(dump-char patient)
1048 (if (in-range? (kern-obj-get-location kchar)
1052 (pathfind kchar (kern-obj-get-location patient)))))))
1054 (define (prompt-for-key)
1055 (kern-log-msg "<²¿¤«¥¡¼¤ò²¡¤¹¤È³¤¯>")
1058 (define (ship-at? loc) (not (null? (kern-place-get-vehicle loc))))
1060 (define (take-player-gold q)
1061 (kern-player-set-gold (- (kern-player-get-gold) q)))
1063 (define (give-player-gold q)
1064 (kern-player-set-gold (+ (kern-player-get-gold) q)))
1066 (define (player-has-gold? q)
1067 (>= (kern-player-get-gold) q))
1069 ;; services -- used with trade-service below
1070 (define (svc-mk name price proc) (list name price proc))
1071 (define (svc-name svc) (car svc))
1072 (define (svc-price svc) (cadr svc))
1073 (define (svc-proc svc) (caddr svc))
1075 ;; some standard healer services
1076 (define (heal-service kchar knpc)
1077 ;;(display "heal-service")(newline)
1078 (let ((hp (- (kern-char-get-max-hp kchar)
1079 (kern-char-get-hp kchar))))
1082 (say knpc "¥ô¥¡¥¹¡¦¥Þ¥Ë<VAS MANI>¡ª"
1083 (kern-obj-get-name kchar) "¤ËÌþ¤·¤ò¡ª")
1085 (kern-obj-heal kchar hp)
1088 (say knpc (kern-obj-get-name kchar)
1089 "¤Ï½ý¤Ä¤¤¤Æ¤¤¤Ê¤¤¡ª")
1093 (define (cure-service kchar knpc)
1094 ;;(display "cure-service")(newline)
1095 (if (is-poisoned? kchar)
1097 (say knpc "¥¢¥ó¡¦¥Î¥¯¥¹<AN NOX>¡ª"
1098 (kern-obj-get-name kchar) "¤Ï¼£Ìþ¤µ¤ì¤¿¡£")
1100 (kern-obj-remove-effect kchar ef_poison))
1102 (say knpc (kern-obj-get-name kchar)
1103 "¤ÏÆǤËÈȤµ¤ì¤Æ¤¤¤Ê¤¤¡ª")
1107 (define (resurrect-service kchar knpc)
1108 ;;(display "resurrect-service")(newline)
1109 (if (is-dead? kchar)
1111 (say knpc "¥¤¥ó¡¦¥Þ¥Ë¡¦¥³¡¼¥×<IN MANI CORP>¡ª"
1112 (kern-obj-get-name kchar) "¤è¡¢¤è¤ß¤¬¤¨¤ì¡ª")
1113 (kern-map-flash 500)
1115 (kern-obj-heal kchar 10))
1117 (say knpc (kern-obj-get-name kchar)
1122 ;; trade-services -- take a list of services which operate on a party member
1123 ;; and prompt the player, check prices, and otherwise handle the transaction
1124 (define (trade-services knpc kpc services)
1126 (define (list-services)
1128 (string-append (svc-name svc)
1130 (number->string (svc-price svc))
1134 ;; line-name - convert a string like "Heal...30 gold" to "Heal"
1135 (define (line-name line)
1139 (if (char=? (car l) #\.)
1141 (cons (car l) (extract (cdr l))))))
1144 (list->string (extract (string->list line)))))
1146 (define (lookup-svc line)
1147 (let ((name (line-name line)))
1150 (lookup (lambda (svc)
1155 (define (choose-svc)
1156 (lookup-svc (apply kern-ui-select-from-list (list-services))))
1158 (let ((svc (choose-svc)))
1161 (if (player-has-gold? (svc-price svc))
1164 (say knpc "ÎÁ¶â¤¬Â¤ê¤Ê¤¤¡ª")
1168 (let ((kchar (kern-ui-select-party-member)))
1171 (if (apply (svc-proc svc) (list kchar knpc))
1173 (take-player-gold (svc-price svc))
1176 (and (not (null? svc))
1180 ;; player-out-of-sight -- no LOS between kobj and any party member
1181 (define (player-out-of-sight? kobj)
1182 (define (can-see? members)
1185 (or (kern-in-los? (kern-obj-get-location (car members))
1186 (kern-obj-get-location kobj))
1187 (can-see? (cdr members)))))
1188 (not (can-see? (kern-party-get-members (kern-get-player)))))
1190 (define (improve-relations kb1 kb2)
1191 (kern-dtable-inc (kern-being-get-current-faction kb1)
1192 (kern-being-get-current-faction kb2)))
1194 (define (harm-relations kb1 kb2)
1195 (kern-dtable-dec (kern-being-get-current-faction kb1)
1196 (kern-being-get-current-faction kb2)))
1198 (define (make-enemies kb1 kb2)
1199 (harm-relations kb1 kb2)
1200 (harm-relations kb1 kb2)
1201 (harm-relations kb1 kb2)
1202 (harm-relations kb1 kb2)
1205 (define (make-allies kb1 kb2)
1206 (improve-relations kb1 kb2)
1207 (improve-relations kb1 kb2)
1208 (improve-relations kb1 kb2)
1209 (improve-relations kb1 kb2)
1212 (define (is-bad-terrain-at? loc)
1213 (is-bad-terrain? (kern-place-get-terrain loc)))
1215 ;; put-random-stuff -- randomly generate locations within the given rectangle
1216 ;; and, if pred? is satisfied, pass the loc to ctor.
1217 (define (put-random-stuff place rect pred? ctor n)
1219 (let ((loc (cons place (rect-random rect))))
1223 (put-random-stuff place rect pred? ctor (- n 1)))
1224 (put-random-stuff place rect pred? ctor n)))))
1226 (define (drop-random-corpses kplace n)
1227 (put-random-stuff kplace
1229 (kern-place-get-width kplace)
1230 (kern-place-get-height kplace))
1232 (eqv? (kern-place-get-terrain loc)
1235 (kern-obj-put-at (mk-corpse-with-loot)
1239 (define (webify kplace x y w h)
1240 (define (drop-web x loc)
1241 (let ((kter (kern-place-get-terrain loc)))
1242 (if (or (eqv? kter t_grass)
1243 (eqv? kter t_boulder))
1244 (kern-obj-put-at (kern-mk-obj F_web_perm 1)
1246 (foldr-rect kplace x y w h drop-web nil))
1248 ;; Fill the rectangle with objects of the given type. If pred? is not null use
1249 ;; it to filter out unsuitable locations.
1250 (define (rect-fill-with-npc kplace x y w h npct pred?)
1251 (define (drop-obj x loc)
1252 (if (or (null? pred?)
1254 (kern-obj-put-at (kern-mk-obj ktype 1)
1256 (foldr-rect kplace x y w h drop-obj #f))
1258 ;; on-entry-to-dungeon-room -- generic place on-enty procedure for dungeon
1259 ;; rooms. When the player enters (or re-enters) a dungeon this looks for a
1260 ;; monster manager object and triggers it.
1261 (define (on-entry-to-dungeon-room kplace kplayer)
1262 ;;(println "on-entry-to-dungeon-room")
1264 ;;(println " signal")
1265 (signal-kobj kmm 'on kmm nil))
1266 (kplace-get-objects-of-type kplace t_monman))
1269 ;; trigger anything with an 'on-entry' ifc
1270 (define (on-entry-trigger-all kplace kplayer)
1272 (signal-kobj kobj 'on-entry kobj))
1273 (kern-place-get-objects kplace))
1277 ;; mk-dungeon-room -- make a 19x19 dungeon room (simplified form of
1279 (define (mk-dungeon-room tag name terrain . objects)
1283 (kern-mk-map nil 19 19 pal_expanded terrain)
1286 #f ; large-scale (wilderness)
1287 #f ; tmp combat place
1291 ;; objects -- automatically add a monster manager
1292 (cons (put (mk-monman) 0 0)
1294 (list 'on-entry-to-dungeon-room) ; hooks
1295 nil ; edge entrances
1298 (define (mk-combat-map tag . terrain)
1299 (kern-mk-map tag 19 19 pal_expanded terrain))
1301 (define (mk-tower tag name terrain entrances . objects)
1305 (kern-mk-map nil 19 19 pal_expanded terrain)
1308 #f ; large-scale (wilderness)
1309 #f ; tmp combat place
1313 ;; objects -- automatically add a monster manager
1314 (cons (put (mk-monman) 0 0)
1316 (list 'on-entry-to-dungeon-room) ; hooks
1317 entrances ; edge entrances
1320 ;; Just like mk-tower but make the sprite configurable
1321 (define (mk-19x19-town tag name sprite terrain entrances . objects)
1325 (kern-mk-map nil 19 19 pal_expanded terrain)
1328 #f ; large-scale (wilderness)
1329 #f ; tmp combat place
1332 ;; objects -- automatically add a monster manager
1333 (cons (put (mk-monman) 0 0) objects)
1334 (list 'on-entry-to-dungeon-room 'on-entry-trigger-all) ; hooks
1335 entrances ; edge entrances
1339 ;; mk-dungeon-level -- given a 2d list of rooms, connect them up as neighbors
1340 (define (mk-dungeon-level . rooms)
1341 (define (bind-dir r1 r2 dir)
1342 (if (and (not (null? r1))
1344 (kern-place-set-neighbor dir r1 r2)))
1345 (define (bind-row top bot)
1346 (if (not (null? top))
1348 (if (not (null? (cdr top)))
1349 (bind-dir (car top) (cadr top) east))
1351 (bind-row (cdr top) nil)
1353 (bind-dir (car top) (car bot) south)
1354 (if (not (null? (cdr bot)))
1355 (bind-dir (car top) (cadr bot) southeast))
1356 (if (not (null? (cdr top)))
1357 (bind-dir (cadr top) (car bot) southwest))
1358 (bind-row (cdr top) (cdr bot)))))))
1359 (define (bind-rooms rooms)
1360 (if (not (null? rooms))
1362 (bind-row (car rooms)
1363 (if (null? (cdr rooms))
1366 (bind-rooms (cdr rooms)))))
1370 (define (println . args)
1375 (define (is-bad-field-at? kchar loc)
1376 (define (is-bad-field? val ktype)
1378 (and (is-field? ktype)
1379 (not (is-immune-to-field? kchar ktype)))))
1380 (foldr is-bad-field?
1382 (kern-get-objects-at loc)))
1384 (define (is-bad-loc? kchar loc)
1386 (is-bad-terrain-at? loc)
1387 (is-bad-field-at? kchar loc)
1390 (define (is-good-loc? kchar loc)
1391 ;;(println "is-good-loc?")
1392 (and (passable? loc kchar)
1393 (not (occupied? loc))
1394 (not (is-bad-loc? kchar loc))))
1396 (define (get-off-bad-tile? kchar)
1397 ;;(println "get-off-bad-tile")
1399 (define (choose-good-tile tiles)
1400 ;;(display "choose-good-tile")(newline)
1403 (if (is-good-loc? kchar (car tiles))
1405 (choose-good-tile (cdr tiles)))))
1407 (define (move-to-good-tile)
1408 ;;(display "move-to-good-tile")(newline)
1409 (let* ((curloc (kern-obj-get-location kchar))
1410 (tiles (get-4-neighboring-tiles curloc))
1411 (newloc (choose-good-tile tiles)))
1415 ;;(display "moving")(newline)
1416 (kern-obj-move kchar
1417 (- (loc-x newloc) (loc-x curloc))
1418 (- (loc-y newloc) (loc-y curloc)))
1422 (is-bad-loc? kchar (kern-obj-get-location kchar))
1423 (move-to-good-tile)))
1425 (define (move-away-from-foes? kchar)
1426 ;;(println "move-away-from-foes?")
1427 (evade kchar (all-visible-hostiles kchar)))
1429 ;; random-loc -- choose a random location
1430 (define (random-loc kplace x y w h)
1432 (+ x (modulo (random-next) w))
1433 (+ y (modulo (random-next) h))))
1435 ;; random-loc -- choose a random location anywhere in the given place
1436 (define (random-loc-in-place kplace)
1440 (kern-place-get-width kplace)
1441 (kern-place-get-height kplace)))
1443 ;; random-loc-place-iter -- try up to n times to find a random location which
1445 (define (random-loc-place-iter kplace pred? n)
1448 (let ((loc (random-loc-in-place kplace)))
1451 (random-loc-place-iter kplace pred? (- n 1))))))
1453 (define (is-floor? loc)
1454 (let ((kter (kern-place-get-terrain loc)))
1455 (or (eqv? kter t_flagstones)
1456 (eqv? kter t_cobblestone))))
1458 (define (loc-is-empty? loc)
1459 (null? (kern-get-objects-at loc)))
1461 (define (mean-player-party-level)
1462 (let ((members (kern-party-get-members (kern-get-player))))
1463 (if (= 0 (length members))
1465 (/ (foldr (lambda (sum kchar)
1466 ;;(println "level:" (kern-char-get-level kchar))
1467 (+ sum (kern-char-get-level kchar)))
1470 (length members)))))
1472 (define (calc-level)
1474 (+ (mean-player-party-level)
1475 (num-player-party-members)
1476 (kern-dice-roll "1d5-3"))))
1478 (define (get-mech-at loc)
1479 (let ((mechs (filter kern-obj-is-mech?
1480 (kern-get-objects-at loc))))
1485 (define (handle-mech-at loc kchar)
1486 (let ((kmech (get-mech-at loc)))
1489 (signal-kobj kmech 'handle kmech kchar))))
1491 (define (get-place kobj)
1492 (loc-place (kern-obj-get-location kobj)))
1494 ;; xp to reach the given level
1495 (define (power base exp)
1498 (* base (power base (- exp 1)))))
1500 (define (lvl-xp lvl)
1501 (power 2 (+ 5 lvl)))
1503 (define (random-faction)
1504 (modulo (random-next) faction-num))
1506 (define (get-target-loc caster range)
1507 (kern-ui-target (kern-obj-get-location caster)
1510 ;;----------------------------------------------------------------------------
1511 ;; code for opening a moongate, warping in a monster, and re-closing it
1512 (define (open-moongate loc)
1513 (let ((kgate (mk-moongate nil)))
1514 (kern-obj-relocate kgate loc nil)
1515 (moongate-animate kgate moongate-stages)
1517 (define (close-moongate kgate)
1518 (moongate-animate kgate (reverse moongate-stages))
1519 (moongate-destroy kgate))
1520 (define (warp-in kchar loc dir faction)
1521 (display "warp-in")(newline)
1522 (kern-char-set-schedule kchar nil)
1523 (kern-obj-inc-ref kchar)
1524 (kern-obj-remove kchar)
1525 (kern-obj-relocate kchar loc nil)
1526 (kern-obj-dec-ref kchar)
1529 (kern-obj-relocate kchar (loc-offset loc dir) nil)
1530 (kern-being-set-base-faction kchar faction)
1533 ;;-----------------------------------------------------------------------------
1534 ;; re-mk-composite-sprite -- combine all the sprites into one layered sprite,
1535 ;; cloning ALL BUT the first sprite. Useful for re-decorating base sprites that
1536 ;; have already been cloned.
1537 (define (re-mk-composite-sprite sprites)
1538 (foldr (lambda (s1 s2) (kern-sprite-append-decoration s1 s2))
1542 ;;-----------------------------------------------------------------------------
1543 ;; mk-composite-sprite -- combine all the sprites into one composite sprite,
1544 ;; cloning all the sprites in the list.
1545 (define (mk-composite-sprite sprites)
1546 (re-mk-composite-sprite (cons (kern-sprite-clone (car sprites)
1550 ; (foldr (lambda (s1 s2) (kern-sprite-append-decoration s1 s2))
1551 ; (kern-sprite-clone (car sprites) nil)
1554 (define (kchar-in-vehicle? kchar)
1555 (let ((kparty (kern-char-get-party kchar)))
1558 (not (null? (kern-party-get-vehicle kparty))))))
1560 ;; is-in-darkness? -- #t iff light on this object's tile is less than the
1561 ;; threshold for "dark"
1562 (define (is-in-darkness? kobj)
1563 (< (kern-place-get-light (kern-obj-get-location kobj))
1566 ;; Convenience wrapper for kern-obj-add-to-inventory
1567 (define (give kpc ktype quantity)
1568 (kern-obj-add-to-inventory kpc ktype quantity))
1570 ;; Convenience wrapper for kern-obj-remove-from-inventory
1571 (define (take kobj ktype quantity)
1572 (kern-obj-remove-from-inventory kobj ktype quantity))
1574 ;; Return #t iff object has at least that many in inventory
1575 (define (has? kobj ktype quantity)
1576 (>= (num-in-inventory kobj ktype) quantity))
1578 ;; Safely if a character is in the player party. char-tag should be the
1579 ;; character's quoted scheme variable name, for example 'ch_dude.
1580 (define (in-player-party? kchar-tag)
1581 (println "in-player-party? " kchar-tag)
1582 (and (defined? kchar-tag)
1583 (let ((kchar (eval kchar-tag)))
1584 (and (is-alive? kchar)
1585 (is-player-party-member? kchar)))))
1587 (define (set-wind-north)
1588 (println "set-wind-north")
1589 (kern-set-wind north 10))
1591 ;; block-teleporting takes a place and a list of strings that looks
1592 ;; suspiciously like a terrain map, and uses the map to apply blocking
1593 ;; mechanisms to the place. Every "x#" entry in the map will cause a blocking
1594 ;; mechanism to be placed on that location. All other entries are ignored. The
1595 ;; blocking mechanisms prevent spells like blink from letting the player break
1596 ;; the fiction of a simulated multi-story place.
1597 (define (block-teleporting kplace map)
1598 (define (doline y lines)
1599 (define (docol x tokens)
1600 (cond ((null? tokens) nil)
1602 (if (and (char=? #\x (car tokens))
1603 (char=? #\# (cadr tokens)))
1605 (kern-obj-put-at (mk-blocker) (list kplace x y))
1607 (docol (+ x 1) (cdddr tokens)))))
1608 (cond ((null? lines) nil)
1610 (docol 0 (string->list (car lines)))
1611 (doline (+ y 1) (cdr lines)))))
1614 ;; Find the visible object of the given type nearest to the kchar.
1615 (define (find-nearest kchar ktype)
1616 (let ((objects (filter (lambda (kobj)
1617 (and (kobj-is-type? kobj ktype)
1618 (can-see? kchar kobj)))
1619 (kern-place-get-objects (loc-place (kern-obj-get-location kchar))))))
1620 (cond ((null? objects) nil)
1622 (nearest-obj kchar objects)))))
1624 ;; Return an integer describing the sign of x
1630 ;; Return a list of (x . y) pairs that constitute a line between two
1631 ;; points. Uses Bresenhaum's line-drawing algorithm.
1632 (define (line x1 y1 x2 y2)
1633 (let* ((dx (- x2 x1))
1644 ;;(println "f1 i=" i " px=" px " py=" py)
1651 (set! py (+ py sdy))))
1652 (set! px (+ px sdx))
1656 ;;(println "f2 i=" i " px=" px " py=" py)
1663 (set! px (+ px sdx))))
1664 (set! py (+ py sdy))
1668 (cons (cons x1 y1) (f1 0)))
1670 (cons (cons x1 y1) (f2 0))))))
1672 ;; Utility for generating dice from numbers easily
1674 (define (mkdice dice size)
1675 (let ((numstr (if (number? dice)
1676 (number->string dice)
1678 (sizestr (if (number? size)
1679 (number->string size)
1681 (string-append numstr "d" sizestr)))
1683 ;; output for effects that should only be noted if visible
1685 (define (msg-log-visible loc . args)
1686 (if (kern-place-is-visible? loc)
1687 (apply kern-log-msg args)
1691 ;; Print dots across the console (similar to the u4 shrine meditation)
1692 (define (log-dots n delay)
1695 (kern-log-continue ".")
1704 (define (find-first fn? lst)
1709 (find-first fn? (cdr lst)))))
1711 (define (append! lst val)
1712 (cond ((null? lst) nil)
1713 ((null? (cdr lst)) (set-cdr! lst val))
1714 (else (append! (cdr lst) val))))
1716 (define (repeat fn n)
1720 (repeat fn (- n 1)))))
1722 (define (string-lower str)
1723 (list->string (map char-downcase (string->list str))))
1728 (define (rect-x r) (car r))
1729 (define (rect-y r) (cadr r))
1730 (define (rect-w r) (caddr r))
1731 (define (rect-h r) (cadddr r))
1733 (define (rect-down r v)
1734 (list (rect-x r) (+ v (rect-y r)) (rect-w r) (rect-h r)))
1736 (define (rect-crop-down r v)
1737 (list (rect-x r) (+ v (rect-y r)) (rect-w r) (- (rect-h r) v)))
1739 (define (rect-offset r x y)
1740 (list (+ x (rect-x r)) (+ y (rect-y r)) (rect-w r) (rect-h r)))
1742 (define (rect-crop-offset r x y)
1743 (list (+ x (rect-x r)) (+ y (rect-y r)) (- (rect-w r) x) (- (rect-h r) y)))
1745 (define (1- x) (- x 1))
1746 (define (1+ x) (+ x 1))
1748 ;; Standard dc vs 1d20 + bonus, with a perfect roll granting automatic success.
1749 (define (check-roll dc bonus)
1750 (let ((roll (kern-dice-roll "1d20")))
1752 (> (+ roll bonus) dc))))