1 2;; init.scm -- contains lots of common scheme utilities 3(load "init.scm") 4 5;; Result codes (these belong here because they are tied to kernel values, see 6;; result.h) 7(define result-ok 0) 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) 16 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))) 21 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) 25 (kern-interp-error x) 26 (apply throw x)) 27(define *error-hook* my-error-hook) 28 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) 32 (kern-include fname) 33 (load fname)) 34 35(define nil '()) 36 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) '()) 41 ((symbol? expr) 42 (if (defined? expr) 43 (eval expr) 44 '())) 45 (eval expr))) 46 47;; filter -- filter-in elements from a list 48(define (filter pred seq) 49 (cond ((null? seq) nil) 50 ((pred (car seq)) 51 (cons (car seq) 52 (filter pred (cdr seq)))) 53 (else (filter pred (cdr seq))))) 54 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))) 61 (if (here? loc) loc 62 (do-search (append (cdr queue) 63 (filter (lambda (v) (not (member v visited))) 64 (next loc))) 65 (append (list loc) visited) 66 (- depth 1)))))) 67 (do-search (list start) nil maxdepth)) 68 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) 73 (if (null? queue) nil 74 (let ((loc (car queue))) 75 (proc loc) 76 (do-search (append (cdr queue) 77 (filter (lambda (v) (not (member v visited))) 78 (next loc))) 79 (append (list loc) visited))))) 80 (do-search (list start) nil)) 81 82;; Set element k of list x to val (zero-indexed) 83(define (list-set-ref! x k val) 84 (if (zero? k) 85 (set-car! x val) 86 (list-set-ref! (cdr x) (- k 1) val))) 87 88;; Check if a list contains an element. 89(define (in-list? elem lst) 90 (foldr (lambda (a b) (or a (eqv? b elem))) 91 #f 92 lst)) 93 94(define (in-text-list? elem lst) 95 (foldr (lambda (a b) (or a (equal? b elem))) 96 #f 97 lst)) 98 99;; Check if a location is passable to a character 100(define (passable? loc kobj) 101 (kern-place-is-passable loc kobj)) 102 103(define (obj-is-char? kobj) (kern-obj-is-being? kobj)) 104(define (is-being? kobj) (kern-obj-is-being? kobj)) 105 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))) 109 #f 110 (kern-get-objects-at loc))) 111 112(define (get-beings-at loc) 113 (filter kern-obj-is-being? 114 (kern-get-objects-at loc))) 115 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) 120 (passable? loc char) 121 (not (occupied? loc)))) 122 neighbors 123 origin 124 10)) 125 126;; Generic proc to summon other beings. Used by spells and some special 127;; effects. 128(define (summon origin mk-critter faction count) 129 (define (run-loop n) 130 (if (= n 0) nil 131 (let* ((critter (kern-obj-set-temporary (kern-being-set-base-faction 132 (mk-critter) 133 faction) 134 #t)) 135 (loc (pick-loc origin critter))) 136 (cond ((null? loc) nil) 137 (else 138 (kern-obj-put-at critter loc) 139 (run-loop (- n 1))))))) 140 (run-loop count)) 141 142;; Like summon but the beings are permanent, not temporary. 143(define (psummon origin mk-critter count) 144 ;;;(display "psummon")(newline) 145 (define (run-loop n) 146 (if (= n 0) nil 147 (let* ((critter (kern-obj-inc-ref (mk-critter))) 148 (loc (pick-loc origin critter))) 149 (cond ((null? loc) (kern-obj-dec-ref critter)) 150 (else 151 (kern-obj-put-at critter loc) 152 (kern-obj-dec-ref critter) 153 (run-loop (- n 1))))))) 154 (run-loop count)) 155 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)))) 164 165;; check if klooker can can see anything in the list kobs 166(define (can-see-any? klooker kobjs) 167 (if (null? kobjs) 168 #f 169 (or (can-see? klooker (car kobjs)) 170 (can-see-any? klooker (cdr kobjs))))) 171 172;; check if knpc can see any of the player party members 173(define (any-player-party-member-visible? knpc) 174 (can-see-any? knpc 175 (kern-party-get-members (kern-get-player)))) 176 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)))))) 180 (if (null? loc) 181 nil 182 (loc-place loc)))) 183 184(define (num-player-party-members) 185 ;;(display "num-player-party-members")(newline) 186 (length (kern-party-get-members (kern-get-player)))) 187 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)) 193 (or found 194 (and (not (eqv? kchar kchar2)) 195 (is-alive? kchar2)))) 196 #f 197 (kern-party-get-members (kern-get-player)))) 198 )) 199 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))) 204 205;; Check if an object is allied with a character 206(define (is-ally? kbeing kobj) 207 (kern-being-is-ally? kbeing kobj)) 208 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))))) 213 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))))) 218 219 220;; Count the number of hostiles 221(define (num-hostiles kchar) 222 (length (all-hostiles kchar))) 223 224;; Count the number of friendlies 225(define (num-allies kchar) 226 (length (all-allies kchar))) 227 228;; Find all beings hostile 229(define (all-visible-hostiles kbeing) 230 (kern-being-get-visible-hostiles kbeing)) 231 232(define (any-visible-hostiles? kchar) 233 (> (length (all-visible-hostiles kchar)) 0)) 234 235(define (nearest-visible-hostile kchar) 236 (nearest-obj kchar (all-visible-hostiles kchar))) 237 238;; Find all allies 239(define (all-visible-allies kbeing) 240 (kern-being-get-visible-allies kbeing)) 241 242;; Count the number of visible friendlies 243(define (num-visible-allies kchar) 244 (length (all-visible-allies kchar))) 245 246;; Count the number of hostiles 247(define (num-visible-hostiles kchar) 248 (length (all-visible-hostiles kchar))) 249 250 251;; Find all the characters in a place 252(define (all-chars kplace) 253 (kern-place-get-beings kplace)) 254 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)) 259 radius)) 260 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) 265 range 266 ktarg)) 267 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)) 273 radius)) 274 objlst)) 275 276;; Return a list of all hostiles in range of the given location 277(define (get-hostiles-in-range-of-loc kchar range loc) 278 (all-in-range loc 279 range 280 (kern-being-get-visible-hostiles kchar))) 281 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 285 range 286 (kern-obj-get-location kchar))) 287 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))) 291 (all-in-range loc 292 range 293 (kern-place-get-beings (loc-place loc))))) 294 295;; Convenience proc for rolling dtables by hand 296(define (dtable-row . cols) cols) 297 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))) 302 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)))) 308 309;; Inefficient code to find nearest location from a list 310(define (nearest-loc kobj klist) 311 (println "nearest-loc: " klist) 312 (if (null? klist) 313 nil 314 (let ((kloc (kern-obj-get-location kobj))) 315 (foldr (lambda (a b) 316 (if (< (loc-city-block-distance kloc a) 317 (loc-city-block-distance kloc b)) 318 a 319 b)) 320 (car klist) 321 (cdr klist))))) 322 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))))) 339 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))) 346 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))))) 352 353(define (notnull? val) (not (null? val))) 354 355(define (being-at? loc) 356 (not (null? (filter kern-obj-is-being? (kern-get-objects-at loc))))) 357 358(define (get-being-at loc) 359 (let ((beings (filter kern-obj-is-being? (kern-get-objects-at loc)))) 360 (if (null? beings) 361 nil 362 (car beings)))) 363 364(define (is-dead? kchar) 365 (kern-char-is-dead? kchar)) 366 367(define (is-alive? kchar) 368 (not (is-dead? kchar))) 369 370(define (has-ap? kobj) 371 (> (kern-obj-get-ap kobj) 0)) 372 373(define (has-ap-debt? kobj) 374 (< (kern-obj-get-ap kobj) 0)) 375 376(define (has-skill? kchar kskill) 377 (in-list? kskill 378 (kern-char-get-skills kchar))) 379 380(define (flee kchar) 381 ;;;(display "flee")(newline) 382 (kern-char-set-fleeing kchar #t)) 383 384(define (wander kchar) 385 (kern-obj-wander kchar)) 386 387(define (weakest kchar-a kchar-b) 388 (if (< (kern-char-get-hp kchar-a) 389 (kern-char-get-hp kchar-b)) 390 a 391 b)) 392 393(define (join-player kchar) 394 (kern-char-join-player kchar)) 395 396(define (random-select list) 397 (if (or (null? list) 398 (= 0 (length list))) 399 nil 400 (list-ref list (modulo (random-next) (length list))))) 401 402(define (taunt kchar ktarg taunts) 403 (say kchar (random-select taunts))) 404 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)))) 411 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 415;; filter. 416;; ---------------------------------------------------------------------------- 417(define (foldr-rect kplace x y w h proc ival) 418 (foldr proc ival (loc-enum-rect kplace x y w h))) 419 420;;---------------------------------------------------------------------------- 421;; Return a list of locations with matching terrain 422;;---------------------------------------------------------------------------- 423(define (find-terrain kplace x y w h kter) 424 (define (check loc) 425 (if (eqv? (kern-place-get-terrain loc) kter) 426 loc 427 nil)) 428 (search-rect kplace x y w h check)) 429 430(define (on-terrain? kobj kter) 431 (eqv? kter (kern-place-get-terrain (kern-obj-get-location kobj)))) 432 433(define (all-visible-terrain-of-type kobj kter) 434 (filter (lambda (x) 435 (eqv? kter 436 (kern-place-get-terrain x))) 437 (kern-being-get-visible-tiles kobj))) 438 439(define (find-nearest-visible-terrain-of-type kobj kter) 440 (nearest-loc kobj (all-visible-terrain-of-type kobj kter))) 441 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) 448 3 3 449 (lambda (val neighbor) 450 ;;(println neighbor " neighbor? " (equal? neighbor loc) " blocks? " (kern-place-blocks-los? neighbor)) 451 (and val 452 (or (eq? neighbor loc) 453 (kern-place-blocks-los? neighbor)))) 454 #t 455 ))) 456 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) 460 ktype)) 461 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))) 467 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) 472 (define (check loc) 473 (define (scanobjlst lst) 474 (foldr (lambda (a b) 475 (or a (kobj-is-type? b ktype))) 476 #f 477 lst)) 478 (if (scanobjlst (kern-get-objects-at loc)) 479 loc 480 nil)) 481 (search-rect kplace x y w h check)) 482 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) 488 (else 489 ;;(println " " (kern-type-get-name (car (car inv)))) 490 (hasit? item (cdr inv))))) 491 (hasit? ktype (kern-char-get-inventory kchar))) 492 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))) 500 501(define (any-in-inventory? kchar lst) 502 (foldr (lambda (v k) 503 (or v 504 (in-inventory? kchar k))) 505 #f 506 lst)) 507 508(define (all-in-inventory? kchar lst) 509 (foldr (lambda (v k) 510 (and v 511 (in-inventory? kchar k))) 512 #t 513 lst)) 514 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) 522 " uses 1 " 523 (kern-type-get-name ktype)) 524 #t) 525 526;;============================================================================ 527;; Modulo system procedures -- useful on wrapping maps 528;;============================================================================ 529(define (madd a b R) (modulo (+ a b) R)) 530(define (msub a b R) (modulo (- a b) R)) 531(define (minc a R) (modulo (+ a 1) R)) 532(define (mdec a R) (modulo (- a 1) R)) 533 534;;---------------------------------------------------------------------------- 535;; mdist - find the distance between two numbers in a modulo system. There are 536;; always 2 distances (additive and subtractive). This picks the shortest 537;; distance.. 538;;---------------------------------------------------------------------------- 539(define (mdist a b R) (min (msub a b R) (msub b a R))) 540 541;; ---------------------------------------------------------------------------- 542;; Turn on/off verbose scheme garbage collection. Useful if you think scheme is 543;; gc'ing some of your code behind your back. 544;; ---------------------------------------------------------------------------- 545(gc-verbose #t) 546 547(define (profile proc . args) 548 (let ((t (kern-get-ticks)) 549 (result (apply proc args))) 550 ;;(display "*** TIME: ");;(display (- (kern-get-ticks) t)) ;;(display " ms") 551 (newline) 552 result)) 553 554;; ---------------------------------------------------------------------------- 555;; find-object-types-at -- return a list of objects of the given type which can 556;; be found at the given location 557;; ---------------------------------------------------------------------------- 558(define (find-object-types-at loc ktype) 559 (filter (lambda (a) (kobj-is-type? a ktype)) 560 (kern-get-objects-at loc))) 561 562;; ---------------------------------------------------------------------------- 563;; is-object-type-at? -- check for an object (by type) at a location 564;; ---------------------------------------------------------------------------- 565(define (is-object-type-at? loc ktype) 566 (foldr (lambda (a b) (or a (kobj-is-type? b ktype))) 567 #f 568 (kern-get-objects-at loc))) 569 570;; ---------------------------------------------------------------------------- 571;; any-object-types-at? -- returns #t iff one or more objects at loc is of one 572;; of the given types 573;; ---------------------------------------------------------------------------- 574(define (any-object-types-at? loc ktypes) 575 (foldr (lambda (a b) (or a (is-object-type-at? loc b))) 576 #f 577 ktypes)) 578 579;; is-player-party-member? -- #t iff kchar is in player party 580(define (is-player-party-member? kchar) 581 (in-list? kchar 582 (kern-party-get-members (kern-get-player)))) 583 584;; ---------------------------------------------------------------------------- 585;; kobj-get -- remove an object from the map and put it into another object 586;; ---------------------------------------------------------------------------- 587(define (kobj-get kobj kchar) 588 (if (kern-obj-put-into kobj kchar) 589 (begin 590 (if (not (is-player-party-member? kchar)) 591 (kern-log-msg (kern-obj-get-name kchar) 592 " gets " 593 (kern-obj-get-name kobj))) 594 (kern-obj-inc-ref kobj) 595 (kern-obj-remove kobj) 596 (kern-obj-dec-ref kobj) 597 (kern-obj-dec-ap kchar (/ norm 5)) 598 (kern-map-repaint)))) 599 600;; ---------------------------------------------------------------------------- 601;; kobj-get-at -- get an object of a specific type from the location 602;; ---------------------------------------------------------------------------- 603(define (kobj-get-at kchar loc ktype) 604 (let ((objs (find-object-types-at loc ktype))) 605 (if (notnull? objs) 606 (kobj-get (car objs) kchar)))) 607 608;; ---------------------------------------------------------------------------- 609;; place-random-corner -- randomly select a corner and return it as a location 610;; ---------------------------------------------------------------------------- 611(define (place-random-corner kplace) 612 (case (kern-dice-roll "1d4") 613 ((1) (mk-loc kplace 0 0)) 614 ((2) (mk-loc kplace 0 (- (kern-place-get-width kplace 1)))) 615 ((3) (mk-loc kplace (- (kern-place-get-height kplace) 1) 0)) 616 ((4) (mk-loc kplace 617 (- (kern-place-get-height kplace) 1) 618 (- (kern-place-get-width kplace) 1))))) 619 620;; ---------------------------------------------------------------------------- 621;; do-or-goto -- if the location is adjacent then the proc, otherwise have 622;; the char pathfind to it 623;; ---------------------------------------------------------------------------- 624(define (do-or-goto kchar coords proc) 625 ;;;(display "do-or-goto")(newline) 626 (if (or (loc-4-adjacent? (kern-obj-get-location kchar) coords) 627 (eq? coords (kern-obj-get-location kchar))) 628 (proc kchar coords) 629 (pathfind kchar coords))) 630 631;; ---------------------------------------------------------------------------- 632;; evade -- simple alg for evading melee foes 633;; 634;; Simple approach: each foe's coordinates forms a vector to the char's 635;; coordinates. Take the sum of these coordinates to get the evasion 636;; vector. "Normalize" the vector components by rounding them to the nearest 0, 637;; 1 or -1. This is the dx/dy to move. If the terrain is impassable in the 638;; preferred direction then try zeroing out the non-zero components and 639;; moving. This will give two backup vectors to try. 640;; 641;; ADDENDUM: I don't want to allow diagonal evasion, so the "normalized" vector 642;; must be skipped if it's a diagonal, thus causing us to try the fallbak 643;; vector(s). 644;; 645;; Now allowing diagonals, since that factor has changed 646;; 647;; TODO: probably shouldnt flee over dangerous terrains 648;; 649;; ---------------------------------------------------------------------------- 650(define (evade kchar foes) 651 (let* ((tloc (kern-obj-get-location kchar)) 652 (v (loc-canonical 653 (foldr 654 (lambda (accum thisfoe) 655 (loc-sum accum 656 (loc-diff (kern-obj-get-location thisfoe) tloc) 657 )) 658 (mk-loc (loc-place tloc) 0 0) 659 foes) 660 )) 661 ) 662 (define (move dx dy) 663 (if (kern-place-is-passable 664 (loc-sum 665 (mk-loc (loc-place tloc) dx dy) 666 tloc) 667 kchar) 668 (kern-obj-move kchar dx dy) 669 #f)) 670 (define (evade-on-normal) 671 (move (loc-x v) (loc-y v))) 672 673 (or (evade-on-normal) 674 (and (not (eq? 0 (loc-y v))) 675 (move (loc-x v) 0)) 676 (and (not (eq? 0 (loc-x v))) 677 (move 0 (loc-y v)))) 678 )) 679 680 681;; ---------------------------------------------------------------------------- 682;; closest-obj -- given an origin and a list of objects, return the object from 683;; the list that is closest (in city-block distance) to the origin 684;; ---------------------------------------------------------------------------- 685(define (closest-obj origin lst) 686 (if (null? lst) nil 687 (foldr (lambda (a b) 688 (if (loc-closer? (kern-obj-get-location a) 689 (kern-obj-get-location b) 690 origin) 691 a 692 b)) 693 (car lst) 694 (cdr lst)))) 695 696;; ---------------------------------------------------------------------------- 697;; blit-maps -- blit multiple maps to a single target map 698;; --------------------------------------------------------------------------- 699(define (blit-maps kmap . blits) 700 (define (blit dstx dsty srcmap srcx srcy w h) 701 (kern-blit-map kmap dstx dsty srcmap srcx srcy w h)) 702 (foldr (lambda (a b) (apply blit b)) 703 kmap 704 blits)) 705 706(define (fill-terrain-prob kter kplace ox oy ow oh prob) 707 (define (fill x y w h) 708 (if (> h 0) 709 (if (> w 0) 710 (begin 711 (if (<= (modulo (random-next) 712 100) 713 prob) 714 (kern-place-set-terrain (list kplace x y) kter)) 715 (fill (+ x 1) y (- w 1) h)) 716 (fill ox (+ y 1) ow (- h 1))))) 717 (fill ox oy ow oh)) 718 719(define (fill-terrain kter kplace ox oy ow oh) 720 (fill-terrain-prob kter kplace ox oy ow oh 100)) 721 722;;============================================================================ 723;; rect 724;;============================================================================ 725(define (mk-rect x y w h) (list x y w h)) 726(define (rect-x r) (car r)) 727(define (rect-y r) (cadr r)) 728(define (rect-w r) (caddr r)) 729(define (rect-h r) (cadddr r)) 730(define (rect-ex r) (+ (rect-x r) (rect-w r))) 731(define (rect-ey r) (+ (rect-y r) (rect-h r))) 732(define (x-in-rect? x r) 733 (and (>= x (rect-x r)) 734 (< x (rect-ex r)))) 735(define (y-in-rect? y r) 736 (and (>= y (rect-y r)) 737 (< y (rect-ey r)))) 738(define (xy-in-rect? x y r) 739 (and (x-in-rect? x r) 740 (y-in-rect? y r))) 741(define (rect-in-rect? a b) 742 (and (xy-in-rect? (rect-x a) (rect-y a) b) 743 (xy-in-rect? (rect-ex a) (rect-ey a) b))) 744(define (loc-in-rect? loc rect) 745 (xy-in-rect? (loc-x loc) 746 (loc-y loc) 747 rect)) 748(define (rect-random rect) 749 (list (+ (rect-x rect) (modulo (random-next) (rect-w rect))) 750 (+ (rect-y rect) (modulo (random-next) (rect-h rect))))) 751 752;;;; (define original-load load) 753;;;; (define (load file) 754;;;; (display (kern-get-ticks)) 755;;;; (display " loading ") 756;;;; (display file)(newline) 757;;;; (original-load file)) 758 759(define (put obj x y) (list obj x y)) 760 761;; lookup-spell-by-handler -- find a spell in the list of all spells 762(define (lookup-spell handler) 763 (define (search-spells slist) 764 (if (null? slist) 765 nil 766 (let ((spell (car slist))) 767 (if (eqv? (spell-handler spell) 768 handler) 769 spell 770 (search-spells (cdr slist)))))) 771 (search-spells spells)) 772 773;; generic lookup 774(define (lookup this? slist) 775 (if (null? slist) 776 nil 777 (if (this? (car slist)) 778 (car slist) 779 (lookup this? (cdr slist))))) 780 781;; can-cast -- check if a char has enough mana to cast a spell 782(define (can-cast? kchar handler) 783 (let ((spell (lookup-spell handler))) 784 (if (null? spell) 785 #f 786 (and (>= (kern-char-get-mana kchar) 787 (spell-cost spell)) 788 (>= (kern-char-get-level kchar) 789 (spell-level spell)))))) 790 791;; cast0 - cast a spell which requires no args if possible, assumes kchar has 792;; enough mana 793(define (cast0 kchar spell) 794 (apply (spell-handler spell) (list kchar)) 795 (kern-char-dec-mana kchar (spell-cost spell)) 796 (kern-obj-dec-ap kchar (spell-ap spell)) 797 (kern-log-msg (kern-obj-get-name kchar) 798 " casts " 799 (spell-name spell))) 800 801;; cast1 - cast a spell which requires one arg if possible, assumes kchar has 802;; enough mana 803(define (cast1 kchar spell ktarg) 804 ;;;(display "cast1: ");;(display spell)(newline) 805 (apply (spell-handler spell) (list kchar ktarg)) 806 (kern-char-dec-mana kchar (spell-cost spell)) 807 (kern-obj-dec-ap kchar (spell-ap spell)) 808 (kern-log-msg (kern-obj-get-name kchar) 809 " casts " 810 (spell-name spell) 811 " on " 812 (kern-obj-get-name ktarg) 813 "!")) 814 815;; ---------------------------------------------------------------------------- 816;; terrain-ok-for-field? -- check if the terrain at a given location will allow 817;; a field to be dropped on it. Terrains with passability class equivalent to 818;; Grass, trees and forest are ok, everything else is not. 819;; ---------------------------------------------------------------------------- 820(define (terrain-ok-for-field? loc) 821 (let ((kter (kern-place-get-terrain loc))) 822 (println "kter: " kter) 823 (if (null? kter) 824 #f 825 (let ((pclass (kern-terrain-get-pclass kter))) 826 (foldr (lambda (a b) (or a (= pclass b))) 827 #f 828 (list pclass-grass pclass-trees pclass-forest)))))) 829 830(define (get-8-neighboring-tiles loc) 831 (let ((kplace (loc-place loc)) 832 (x (loc-x loc)) 833 (y (loc-y loc))) 834 (filter kern-is-valid-location? 835 (map (lambda (offset) (mk-loc kplace 836 (+ (car offset) x) 837 (+ (cdr offset) y))) 838 (list (cons -1 -1) 839 (cons 0 -1) 840 (cons 1 -1) 841 (cons -1 0) 842 (cons 1 0) 843 (cons -1 1) 844 (cons 0 1) 845 (cons 1 1)))))) 846 847(define (get-4-neighboring-tiles loc) 848 (let ((kplace (loc-place loc)) 849 (x (loc-x loc)) 850 (y (loc-y loc))) 851 (filter kern-is-valid-location? 852 (map (lambda (offset) (mk-loc kplace 853 (+ (car offset) x) 854 (+ (cdr offset) y))) 855 (list (cons 0 -1) 856 (cons -1 0) 857 (cons 1 0) 858 (cons 0 1) 859 ))))) 860 861(define (shake-map dur) 862 (if (> dur 0) 863 (begin 864 (kern-map-set-jitter #t) 865 (kern-map-repaint) 866 (shake-map (- dur 1))) 867 (begin 868 (kern-map-set-jitter #f) 869 (kern-map-repaint)))) 870 871(define (random-vdir) 872 (random-select (list (cons -1 0) 873 (cons 1 0) 874 (cons 0 -1) 875 (cons 0 1)))) 876 877(define (random-neighbor-loc kobj) 878 (let ((vdir (random-vdir))) 879 (loc-sum (kern-obj-get-location kobj) 880 (mk-loc nil (car vdir) (cdr vdir))))) 881 882(define (push kobj dx dy dist) 883 (let* ((loc (loc-sum (kern-obj-get-location kobj) 884 (mk-loc nil dx dy)))) 885 (if (and (kern-place-is-passable loc kobj) 886 (not (occupied? loc))) 887 (begin 888 (kern-obj-relocate kobj loc nil) 889 #t) 890 #f))) 891 892(define (stagger kchar) 893 (let ((vdir (random-vdir))) 894 (push kchar (car vdir) (cdr vdir) 1))) 895 896(define (end-turn kobj)(kern-obj-set-ap kobj 0)) 897 898(define (add-effect-multiple kobj keff fgob q) 899 (if (> q 0) 900 (begin 901 (kern-obj-add-effect kobj keff fgob) 902 (add-effect-multiple kobj keff fgob (- q 1))))) 903 904;; time procs for use with return value from kern-get-time: 905(define (time-mk yr mo we da hr mi) 906 (list yr mo we da hr mi)) 907(define (time-year time) (list-ref time 0)) 908(define (time-month time) (list-ref time 1)) 909(define (time-week time) (list-ref time 2)) 910(define (time-day time) (list-ref time 3)) 911(define (time-hour time) (list-ref time 4)) 912(define (time-minute time) (list-ref time 5)) 913 914;; wants-healing? -- check if a char is <= 50% max hp 915(define (wants-healing? kchar) 916 (<= (kern-char-get-hp kchar) 917 (/ (kern-char-get-max-hp kchar) 2))) 918 919;; wants-healing? -- check if a char is <= 25% max hp 920(define (wants-great-healing? kchar) 921 (<= (kern-char-get-hp kchar) 922 (/ (kern-char-get-max-hp kchar) 4))) 923 924;; wants-mana? -- check if a char is <= 50% max mana 925(define (wants-mana? kchar) 926 (<= (kern-char-get-mana kchar) 927 (/ (kern-char-get-max-mana kchar) 2))) 928 929;; has-mana-potion? -- check if a char has a mana potion in inventory 930(define (has-mana-potion? kchar) 931 (in-inventory? kchar t_mana_potion)) 932 933;; drink-mana-potion -- use a mana potion from inventory 934(define (drink-mana-potion kchar) 935 (use-item-from-inventory-on-self kchar t_mana_potion)) 936 937;; has-heal-potion? -- check if a char has a heal potion in inventory 938(define (has-heal-potion? kchar) 939 (in-inventory? kchar t_heal_potion)) 940 941;; drink-heal-potion -- use a heal potion from inventory 942(define (drink-heal-potion kchar) 943 (use-item-from-inventory-on-self kchar t_heal_potion)) 944 945(define (set-max-hp kchar) 946 (kern-char-set-hp kchar 947 (kern-char-get-max-hp kchar))) 948 949;; max-hp -- calc max hp given species, level and occ 950(define (max-hp sp occ lvl mod mult) 951 (+ (kern-species-get-hp-mod sp) 952 (if (null? occ) 0 (kern-occ-get-hp-mod occ)) 953 mod 954 (* lvl 955 (+ (kern-species-get-hp-mult sp) 956 (if (null? occ) 0 (kern-occ-get-hp-mult occ)) 957 mult)))) 958 959;; max-mp -- calc max mp given species, level and occ 960(define (max-mp sp occ lvl mod mult) 961 (+ (kern-species-get-mp-mod sp) 962 (if (null? occ) 0 (kern-occ-get-mp-mod occ)) 963 mod 964 (* lvl 965 (+ (kern-species-get-mp-mult sp) 966 (if (null? occ) 0 (kern-occ-get-mp-mult occ)) 967 mult)))) 968 969 970;; set-level -- set character to level and max out hp and mana (intended for 971;; new npc creation) 972(define (set-level kchar lvl) 973 (kern-char-set-level kchar lvl)) 974 975;; use-potion? -- use potion on self if desired and available 976(define (use-potion? kchar) 977 (or (and (wants-healing? kchar) 978 (has-heal-potion? kchar) 979 (drink-heal-potion kchar)) 980 (and (wants-mana? kchar) 981 (has-mana-potion? kchar) 982 (drink-mana-potion kchar)))) 983 984(define (use-heal-spell-on-self? kchar) 985 ;;;;(display "use-heal-spell-on-self?")(newline) 986 (and (wants-healing? kchar) 987 (can-use-ability? heal-ability kchar) 988 (use-ability heal-ability kchar kchar))) 989 990(define (use-great-heal-spell-on-self? kchar) 991 ;;;;(display "use-great-heal-spell-on-self?")(newline) 992 (and (wants-great-healing? kchar) 993 (can-use-ability? great-heal-ability kchar) 994 (use-ability great-heal-ability kchar kchar))) 995 996(define (use-spell-on-self? kchar) 997 ;;;;(display "use-spell-on-self?")(newline) 998 (or (use-great-heal-spell-on-self? kchar) 999 (use-heal-spell-on-self? kchar))) 1000 1001(define (avoid-melee? kchar) 1002 ;;;;(display "avoid-melee? kchar")(newline) 1003 (let ((nearby-foes (get-hostiles-in-range kchar 1))) 1004 (if (null? nearby-foes) 1005 #f 1006 (evade kchar nearby-foes)))) 1007 1008(define (dump-char kchar) 1009 (if (null? kchar) 1010 (println "nil") 1011 (begin 1012 (println "npc: " (kern-obj-get-name kchar) 1013 "[" (kern-char-get-level kchar) "]" 1014 " hp=" (kern-char-get-hp kchar) "/" (kern-char-get-max-hp kchar) 1015 " mp=" (kern-char-get-mana kchar) "/" (kern-char-get-max-mana kchar) 1016 " @[" (loc-x (kern-obj-get-location kchar)) 1017 "," (loc-y (kern-obj-get-location kchar)) "]" 1018 )))) 1019 1020 1021(define (get-nearest-patient kchar) 1022 (let ((kloc (kern-obj-get-location kchar))) 1023 (foldr (lambda (kpatient ktarg) 1024 ;;(display " checking ")(dump-char ktarg) 1025 (if (and (wants-healing? ktarg) 1026 (or (null? kpatient) 1027 (< (kern-get-distance kloc 1028 (kern-obj-get-location ktarg)) 1029 (kern-get-distance kloc 1030 (kern-obj-get-location kpatient))))) 1031 ktarg 1032 kpatient)) 1033 nil 1034 (all-visible-allies kchar)))) 1035 1036;; This is for medics. A patient is an ally that needs healing. If a patient is 1037;; less than 2 tiles away then do nothing. If a patient is more than 2 tiles 1038;; away then pathfind toward it. 1039(define (move-toward-patient? kchar) 1040 (let ((patient (get-nearest-patient kchar))) 1041 (if (null? patient) 1042 #f 1043 (begin 1044 ;;(display "selected ")(dump-char patient) 1045 (if (in-range? (kern-obj-get-location kchar) 1046 2 1047 patient) 1048 #f 1049 (pathfind kchar (kern-obj-get-location patient))))))) 1050 1051(define (prompt-for-key) 1052 (kern-log-msg "<Hit any key to continue>") 1053 (kern-ui-waitkey)) 1054 1055(define (ship-at? loc) (not (null? (kern-place-get-vehicle loc)))) 1056 1057(define (take-player-gold q) 1058 (kern-player-set-gold (- (kern-player-get-gold) q))) 1059 1060(define (give-player-gold q) 1061 (kern-player-set-gold (+ (kern-player-get-gold) q))) 1062 1063(define (player-has-gold? q) 1064 (>= (kern-player-get-gold) q)) 1065 1066;; services -- used with trade-service below 1067(define (svc-mk name price proc) (list name price proc)) 1068(define (svc-name svc) (car svc)) 1069(define (svc-price svc) (cadr svc)) 1070(define (svc-proc svc) (caddr svc)) 1071 1072;; some standard healer services 1073(define (heal-service kchar knpc) 1074 ;;(display "heal-service")(newline) 1075 (let ((hp (- (kern-char-get-max-hp kchar) 1076 (kern-char-get-hp kchar)))) 1077 (if (> hp 0) 1078 (begin 1079 (say knpc "VAS MANI! Be healed, " 1080 (kern-obj-get-name kchar)) 1081 (kern-map-flash hp) 1082 (kern-obj-heal kchar hp) 1083 #t) 1084 (begin 1085 (say knpc (kern-obj-get-name kchar) 1086 " is not wounded!") 1087 (prompt-for-key) 1088 #f)))) 1089 1090(define (cure-service kchar knpc) 1091 ;;(display "cure-service")(newline) 1092 (if (is-poisoned? kchar) 1093 (begin 1094 (say knpc "AN NOX! You are cured, " 1095 (kern-obj-get-name kchar)) 1096 (kern-map-flash 1) 1097 (kern-obj-remove-effect kchar ef_poison)) 1098 (begin 1099 (say knpc (kern-obj-get-name kchar) 1100 " is not poisoned!") 1101 (prompt-for-key) 1102 #f))) 1103 1104(define (resurrect-service kchar knpc) 1105 ;;(display "resurrect-service")(newline) 1106 (if (is-dead? kchar) 1107 (begin 1108 (say knpc "IN MANI CORP! Arise, " 1109 (kern-obj-get-name kchar)) 1110 (kern-map-flash 500) 1111 (resurrect kchar) 1112 (kern-obj-heal kchar 10)) 1113 (begin 1114 (say knpc (kern-obj-get-name kchar) 1115 " is not dead!") 1116 (prompt-for-key) 1117 #f))) 1118 1119;; trade-services -- take a list of services which operate on a party member 1120;; and prompt the player, check prices, and otherwise handle the transaction 1121(define (trade-services knpc kpc services) 1122 1123 (define (list-services) 1124 (map (lambda (svc) 1125 (string-append (svc-name svc) 1126 "..." 1127 (number->string (svc-price svc)) 1128 " gold")) 1129 services)) 1130 1131 ;; line-name - convert a string like "Heal...30 gold" to "Heal" 1132 (define (line-name line) 1133 (define (extract l) 1134 (if (null? l) 1135 nil 1136 (if (char=? (car l) #\.) 1137 nil 1138 (cons (car l) (extract (cdr l)))))) 1139 (if (null? line) 1140 nil 1141 (list->string (extract (string->list line))))) 1142 1143 (define (lookup-svc line) 1144 (let ((name (line-name line))) 1145 (if (null? name) 1146 nil 1147 (lookup (lambda (svc) 1148 (string=? name 1149 (svc-name svc))) 1150 services)))) 1151 1152 (define (choose-svc) 1153 (lookup-svc (apply kern-ui-select-from-list (list-services)))) 1154 1155 (let ((svc (choose-svc))) 1156 1157 (define (can-pay?) 1158 (if (player-has-gold? (svc-price svc)) 1159 #t 1160 (begin 1161 (say knpc "You don't have enough gold!") 1162 #f))) 1163 1164 (define (apply-svc) 1165 (let ((kchar (kern-ui-select-party-member))) 1166 (if (null? kchar) 1167 #f 1168 (if (apply (svc-proc svc) (list kchar knpc)) 1169 (begin 1170 (take-player-gold (svc-price svc)) 1171 #t))))) 1172 1173 (and (not (null? svc)) 1174 (can-pay?) 1175 (apply-svc)))) 1176 1177;; player-out-of-sight -- no LOS between kobj and any party member 1178(define (player-out-of-sight? kobj) 1179 (define (can-see? members) 1180 (if (null? members) 1181 #f 1182 (or (kern-in-los? (kern-obj-get-location (car members)) 1183 (kern-obj-get-location kobj)) 1184 (can-see? (cdr members))))) 1185 (not (can-see? (kern-party-get-members (kern-get-player))))) 1186 1187(define (improve-relations kb1 kb2) 1188 (kern-dtable-inc (kern-being-get-current-faction kb1) 1189 (kern-being-get-current-faction kb2))) 1190 1191(define (harm-relations kb1 kb2) 1192 (kern-dtable-dec (kern-being-get-current-faction kb1) 1193 (kern-being-get-current-faction kb2))) 1194 1195(define (make-enemies kb1 kb2) 1196 (harm-relations kb1 kb2) 1197 (harm-relations kb1 kb2) 1198 (harm-relations kb1 kb2) 1199 (harm-relations kb1 kb2) 1200 ) 1201 1202(define (make-allies kb1 kb2) 1203 (improve-relations kb1 kb2) 1204 (improve-relations kb1 kb2) 1205 (improve-relations kb1 kb2) 1206 (improve-relations kb1 kb2) 1207 ) 1208 1209(define (is-bad-terrain-at? loc) 1210 (is-bad-terrain? (kern-place-get-terrain loc))) 1211 1212;; put-random-stuff -- randomly generate locations within the given rectangle 1213;; and, if pred? is satisfied, pass the loc to ctor. 1214(define (put-random-stuff place rect pred? ctor n) 1215 (if (> n 0) 1216 (let ((loc (cons place (rect-random rect)))) 1217 (if (pred? loc) 1218 (begin 1219 (ctor loc) 1220 (put-random-stuff place rect pred? ctor (- n 1))) 1221 (put-random-stuff place rect pred? ctor n))))) 1222 1223(define (drop-random-corpses kplace n) 1224 (put-random-stuff kplace 1225 (mk-rect 0 0 1226 (kern-place-get-width kplace) 1227 (kern-place-get-height kplace)) 1228 (lambda (loc) 1229 (eqv? (kern-place-get-terrain loc) 1230 t_grass)) 1231 (lambda (loc) 1232 (kern-obj-put-at (mk-corpse-with-loot) 1233 loc)) 1234 n)) 1235 1236(define (webify kplace x y w h) 1237 (define (drop-web x loc) 1238 (let ((kter (kern-place-get-terrain loc))) 1239 (if (or (eqv? kter t_grass) 1240 (eqv? kter t_boulder)) 1241 (kern-obj-put-at (kern-mk-obj F_web_perm 1) 1242 loc)))) 1243 (foldr-rect kplace x y w h drop-web nil)) 1244 1245;; Fill the rectangle with objects of the given type. If pred? is not null use 1246;; it to filter out unsuitable locations. 1247(define (rect-fill-with-npc kplace x y w h npct pred?) 1248 (define (drop-obj x loc) 1249 (if (or (null? pred?) 1250 (pred? loc)) 1251 (kern-obj-put-at (kern-mk-obj ktype 1) 1252 loc))) 1253(foldr-rect kplace x y w h drop-obj #f)) 1254 1255;; on-entry-to-dungeon-room -- generic place on-enty procedure for dungeon 1256;; rooms. When the player enters (or re-enters) a dungeon this looks for a 1257;; monster manager object and triggers it. 1258(define (on-entry-to-dungeon-room kplace kplayer) 1259 ;;(println "on-entry-to-dungeon-room") 1260 (map (lambda (kmm) 1261 ;;(println " signal") 1262 (signal-kobj kmm 'on kmm nil)) 1263 (kplace-get-objects-of-type kplace t_monman)) 1264 ) 1265 1266;; trigger anything with an 'on-entry' ifc 1267(define (on-entry-trigger-all kplace kplayer) 1268 (map (lambda (kobj) 1269 (signal-kobj kobj 'on-entry kobj)) 1270 (kern-place-get-objects kplace)) 1271 ) 1272 1273 1274;; mk-dungeon-room -- make a 19x19 dungeon room (simplified form of 1275;; kern-mk-place) 1276(define (mk-dungeon-room tag name terrain . objects) 1277 (kern-mk-place tag 1278 name 1279 nil ; sprite 1280 (kern-mk-map nil 19 19 pal_expanded terrain) 1281 #f ; wraps 1282 #t ; underground 1283 #f ; large-scale (wilderness) 1284 #f ; tmp combat place 1285 nil ; subplaces 1286 nil ; neighbors 1287 1288 ;; objects -- automatically add a monster manager 1289 (cons (put (mk-monman) 0 0) 1290 objects) 1291 (list 'on-entry-to-dungeon-room) ; hooks 1292 nil ; edge entrances 1293 )) 1294 1295(define (mk-combat-map tag . terrain) 1296 (kern-mk-map tag 19 19 pal_expanded terrain)) 1297 1298(define (mk-tower tag name terrain entrances . objects) 1299 (kern-mk-place tag 1300 name 1301 s_keep ; sprite 1302 (kern-mk-map nil 19 19 pal_expanded terrain) 1303 #f ; wraps 1304 #f ; underground 1305 #f ; large-scale (wilderness) 1306 #f ; tmp combat place 1307 nil ; subplaces 1308 nil ; neighbors 1309 1310 ;; objects -- automatically add a monster manager 1311 (cons (put (mk-monman) 0 0) 1312 objects) 1313 (list 'on-entry-to-dungeon-room) ; hooks 1314 entrances ; edge entrances 1315 )) 1316 1317;; Just like mk-tower but make the sprite configurable 1318(define (mk-19x19-town tag name sprite terrain entrances . objects) 1319 (kern-mk-place tag 1320 name 1321 sprite 1322 (kern-mk-map nil 19 19 pal_expanded terrain) 1323 #f ; wraps 1324 #f ; underground 1325 #f ; large-scale (wilderness) 1326 #f ; tmp combat place 1327 nil ; subplaces 1328 nil ; neighbors 1329 ;; objects -- automatically add a monster manager 1330 (cons (put (mk-monman) 0 0) objects) 1331 (list 'on-entry-to-dungeon-room 'on-entry-trigger-all) ; hooks 1332 entrances ; edge entrances 1333 )) 1334 1335 1336;; mk-dungeon-level -- given a 2d list of rooms, connect them up as neighbors 1337(define (mk-dungeon-level . rooms) 1338 (define (bind-dir r1 r2 dir) 1339 (if (and (not (null? r1)) 1340 (not (null? r2))) 1341 (kern-place-set-neighbor dir r1 r2))) 1342 (define (bind-row top bot) 1343 (if (not (null? top)) 1344 (begin 1345 (if (not (null? (cdr top))) 1346 (bind-dir (car top) (cadr top) east)) 1347 (if (null? bot) 1348 (bind-row (cdr top) nil) 1349 (begin 1350 (bind-dir (car top) (car bot) south) 1351 (if (not (null? (cdr bot))) 1352 (bind-dir (car top) (cadr bot) southeast)) 1353 (if (not (null? (cdr top))) 1354 (bind-dir (cadr top) (car bot) southwest)) 1355 (bind-row (cdr top) (cdr bot))))))) 1356 (define (bind-rooms rooms) 1357 (if (not (null? rooms)) 1358 (begin 1359 (bind-row (car rooms) 1360 (if (null? (cdr rooms)) 1361 nil 1362 (cadr rooms))) 1363 (bind-rooms (cdr rooms))))) 1364 (bind-rooms rooms)) 1365 1366 1367(define (println . args) 1368 (map display args) 1369 (newline)) 1370 1371 1372(define (is-bad-field-at? kchar loc) 1373 (define (is-bad-field? val ktype) 1374 (or val 1375 (and (is-field? ktype) 1376 (not (is-immune-to-field? kchar ktype))))) 1377 (foldr is-bad-field? 1378 #f 1379 (kern-get-objects-at loc))) 1380 1381(define (is-bad-loc? kchar loc) 1382 (or 1383 (is-bad-terrain-at? loc) 1384 (is-bad-field-at? kchar loc) 1385 )) 1386 1387(define (is-good-loc? kchar loc) 1388 ;;(println "is-good-loc?") 1389 (and (passable? loc kchar) 1390 (not (occupied? loc)) 1391 (not (is-bad-loc? kchar loc)))) 1392 1393(define (get-off-bad-tile? kchar) 1394 ;;(println "get-off-bad-tile") 1395 1396 (define (choose-good-tile tiles) 1397 ;;(display "choose-good-tile")(newline) 1398 (if (null? tiles) 1399 nil 1400 (if (is-good-loc? kchar (car tiles)) 1401 (car tiles) 1402 (choose-good-tile (cdr tiles))))) 1403 1404 (define (move-to-good-tile) 1405 ;;(display "move-to-good-tile")(newline) 1406 (let* ((curloc (kern-obj-get-location kchar)) 1407 (tiles (get-4-neighboring-tiles curloc)) 1408 (newloc (choose-good-tile tiles))) 1409 (if (null? newloc) 1410 #f 1411 (begin 1412 ;;(display "moving")(newline) 1413 (kern-obj-move kchar 1414 (- (loc-x newloc) (loc-x curloc)) 1415 (- (loc-y newloc) (loc-y curloc))) 1416 #t)))) 1417 1418 (and 1419 (is-bad-loc? kchar (kern-obj-get-location kchar)) 1420 (move-to-good-tile))) 1421 1422(define (move-away-from-foes? kchar) 1423 ;;(println "move-away-from-foes?") 1424 (evade kchar (all-visible-hostiles kchar))) 1425 1426;; random-loc -- choose a random location 1427(define (random-loc kplace x y w h) 1428 (mk-loc kplace 1429 (+ x (modulo (random-next) w)) 1430 (+ y (modulo (random-next) h)))) 1431 1432;; random-loc -- choose a random location anywhere in the given place 1433(define (random-loc-in-place kplace) 1434 (random-loc kplace 1435 0 1436 0 1437 (kern-place-get-width kplace) 1438 (kern-place-get-height kplace))) 1439 1440;; random-loc-place-iter -- try up to n times to find a random location which 1441;; satisfies pred? 1442(define (random-loc-place-iter kplace pred? n) 1443 (if (<= n 0) 1444 nil 1445 (let ((loc (random-loc-in-place kplace))) 1446 (if (pred? loc) 1447 loc 1448 (random-loc-place-iter kplace pred? (- n 1)))))) 1449 1450(define (is-floor? loc) 1451 (let ((kter (kern-place-get-terrain loc))) 1452 (or (eqv? kter t_flagstones) 1453 (eqv? kter t_cobblestone)))) 1454 1455(define (loc-is-empty? loc) 1456 (null? (kern-get-objects-at loc))) 1457 1458(define (mean-player-party-level) 1459 (let ((members (kern-party-get-members (kern-get-player)))) 1460 (if (= 0 (length members)) 1461 1 1462 (/ (foldr (lambda (sum kchar) 1463 ;;(println "level:" (kern-char-get-level kchar)) 1464 (+ sum (kern-char-get-level kchar))) 1465 0 1466 members) 1467 (length members))))) 1468 1469(define (calc-level) 1470 (max 1 1471 (+ (mean-player-party-level) 1472 (num-player-party-members) 1473 (kern-dice-roll "1d5-3")))) 1474 1475(define (get-mech-at loc) 1476 (let ((mechs (filter kern-obj-is-mech? 1477 (kern-get-objects-at loc)))) 1478 (if (null? mechs) 1479 nil 1480 (car mechs)))) 1481 1482(define (handle-mech-at loc kchar) 1483 (let ((kmech (get-mech-at loc))) 1484 (if (null? kmech) 1485 #f 1486 (signal-kobj kmech 'handle kmech kchar)))) 1487 1488(define (get-place kobj) 1489 (loc-place (kern-obj-get-location kobj))) 1490 1491;; xp to reach the given level 1492(define (power base exp) 1493 (if (= 0 exp) 1494 1 1495 (* base (power base (- exp 1))))) 1496 1497(define (lvl-xp lvl) 1498 (power 2 (+ 5 lvl))) 1499 1500(define (random-faction) 1501 (modulo (random-next) faction-num)) 1502 1503(define (get-target-loc caster range) 1504 (kern-ui-target (kern-obj-get-location caster) 1505 range)) 1506 1507;;---------------------------------------------------------------------------- 1508;; code for opening a moongate, warping in a monster, and re-closing it 1509(define (open-moongate loc) 1510 (let ((kgate (mk-moongate nil))) 1511 (kern-obj-relocate kgate loc nil) 1512 (moongate-animate kgate moongate-stages) 1513 kgate)) 1514(define (close-moongate kgate) 1515 (moongate-animate kgate (reverse moongate-stages)) 1516 (moongate-destroy kgate)) 1517(define (warp-in kchar loc dir faction) 1518 (display "warp-in")(newline) 1519 (kern-char-set-schedule kchar nil) 1520 (kern-obj-inc-ref kchar) 1521 (kern-obj-remove kchar) 1522 (kern-obj-relocate kchar loc nil) 1523 (kern-obj-dec-ref kchar) 1524 (kern-map-repaint) 1525 (kern-sleep 250) 1526 (kern-obj-relocate kchar (loc-offset loc dir) nil) 1527 (kern-being-set-base-faction kchar faction) 1528 (kern-map-repaint)) 1529 1530;;----------------------------------------------------------------------------- 1531;; re-mk-composite-sprite -- combine all the sprites into one layered sprite, 1532;; cloning ALL BUT the first sprite. Useful for re-decorating base sprites that 1533;; have already been cloned. 1534(define (re-mk-composite-sprite sprites) 1535 (foldr (lambda (s1 s2) (kern-sprite-append-decoration s1 s2)) 1536 (car sprites) 1537 (cdr sprites))) 1538 1539;;----------------------------------------------------------------------------- 1540;; mk-composite-sprite -- combine all the sprites into one composite sprite, 1541;; cloning all the sprites in the list. 1542(define (mk-composite-sprite sprites) 1543 (re-mk-composite-sprite (cons (kern-sprite-clone (car sprites) 1544 nil) 1545 (cdr sprites)))) 1546 1547; (foldr (lambda (s1 s2) (kern-sprite-append-decoration s1 s2)) 1548; (kern-sprite-clone (car sprites) nil) 1549; (cdr sprites))) 1550 1551(define (kchar-in-vehicle? kchar) 1552 (let ((kparty (kern-char-get-party kchar))) 1553 (if (null? kparty) 1554 #f 1555 (not (null? (kern-party-get-vehicle kparty)))))) 1556 1557;; is-in-darkness? -- #t iff light on this object's tile is less than the 1558;; threshold for "dark" 1559(define (is-in-darkness? kobj) 1560 (< (kern-place-get-light (kern-obj-get-location kobj)) 1561 64)) 1562 1563;; Convenience wrapper for kern-obj-add-to-inventory 1564(define (give kpc ktype quantity) 1565 (kern-obj-add-to-inventory kpc ktype quantity)) 1566 1567;; Convenience wrapper for kern-obj-remove-from-inventory 1568(define (take kobj ktype quantity) 1569 (kern-obj-remove-from-inventory kobj ktype quantity)) 1570 1571;; Return #t iff object has at least that many in inventory 1572(define (has? kobj ktype quantity) 1573 (>= (num-in-inventory kobj ktype) quantity)) 1574 1575;; Safely if a character is in the player party. char-tag should be the 1576;; character's quoted scheme variable name, for example 'ch_dude. 1577(define (in-player-party? kchar-tag) 1578 (println "in-player-party? " kchar-tag) 1579 (and (defined? kchar-tag) 1580 (let ((kchar (eval kchar-tag))) 1581 (and (is-alive? kchar) 1582 (is-player-party-member? kchar))))) 1583 1584(define (set-wind-north) 1585 (println "set-wind-north") 1586 (kern-set-wind north 10)) 1587 1588;; block-teleporting takes a place and a list of strings that looks 1589;; suspiciously like a terrain map, and uses the map to apply blocking 1590;; mechanisms to the place. Every "x#" entry in the map will cause a blocking 1591;; mechanism to be placed on that location. All other entries are ignored. The 1592;; blocking mechanisms prevent spells like blink from letting the player break 1593;; the fiction of a simulated multi-story place. 1594(define (block-teleporting kplace map) 1595 (define (doline y lines) 1596 (define (docol x tokens) 1597 (cond ((null? tokens) nil) 1598 (else 1599 (if (and (char=? #\x (car tokens)) 1600 (char=? #\# (cadr tokens))) 1601 (begin 1602 (kern-obj-put-at (mk-blocker) (list kplace x y)) 1603 )) 1604 (docol (+ x 1) (cdddr tokens))))) 1605 (cond ((null? lines) nil) 1606 (else 1607 (docol 0 (string->list (car lines))) 1608 (doline (+ y 1) (cdr lines))))) 1609 (doline 0 map)) 1610 1611;; Find the visible object of the given type nearest to the kchar. 1612(define (find-nearest kchar ktype) 1613 (let ((objects (filter (lambda (kobj) 1614 (and (kobj-is-type? kobj ktype) 1615 (can-see? kchar kobj))) 1616 (kern-place-get-objects (loc-place (kern-obj-get-location kchar)))))) 1617 (cond ((null? objects) nil) 1618 (else 1619 (nearest-obj kchar objects))))) 1620 1621;; Return an integer describing the sign of x 1622(define (sgn x) 1623 (cond ((> x 0) 1) 1624 ((< x 0) -1) 1625 (else 0))) 1626 1627;; Return a list of (x . y) pairs that constitute a line between two 1628;; points. Uses Bresenhaum's line-drawing algorithm. 1629(define (line x1 y1 x2 y2) 1630 (let* ((dx (- x2 x1)) 1631 (dy (- y2 y1)) 1632 (adx (abs dx)) 1633 (ady (abs dy)) 1634 (sdx (sgn dx)) 1635 (sdy (sgn dy)) 1636 (x (/ ady 2)) 1637 (y (/ adx 2)) 1638 (px x1) 1639 (py y1)) 1640 (define (f1 i) 1641 ;;(println "f1 i=" i " px=" px " py=" py) 1642 (cond ((>= i adx) 1643 nil) 1644 (else 1645 (set! y (+ y ady)) 1646 (cond ((>= y adx) 1647 (set! y (- y adx)) 1648 (set! py (+ py sdy)))) 1649 (set! px (+ px sdx)) 1650 (cons (cons px py) 1651 (f1 (+ 1 i)))))) 1652 (define (f2 i) 1653 ;;(println "f2 i=" i " px=" px " py=" py) 1654 (cond ((>= i ady) 1655 nil) 1656 (else 1657 (set! x (+ x adx)) 1658 (cond ((>= x ady) 1659 (set! x (- x ady)) 1660 (set! px (+ px sdx)))) 1661 (set! py (+ py sdy)) 1662 (cons (cons px py) 1663 (f2 (+ 1 i)))))) 1664 (cond ((>= adx ady) 1665 (cons (cons x1 y1) (f1 0))) 1666 (else 1667 (cons (cons x1 y1) (f2 0)))))) 1668 1669;; Utility for generating dice from numbers easily 1670;; 1671(define (mkdice dice size) 1672 (let ((numstr (if (number? dice) 1673 (number->string dice) 1674 dice)) 1675 (sizestr (if (number? size) 1676 (number->string size) 1677 size))) 1678 (string-append numstr "d" sizestr))) 1679 1680;; output for effects that should only be noted if visible 1681 1682(define (msg-log-visible loc . args) 1683 (if (kern-place-is-visible? loc) 1684 (apply kern-log-msg args) 1685 ) 1686 ) 1687 1688;; Print dots across the console (similar to the u4 shrine meditation) 1689(define (log-dots n delay) 1690 (define (dots n) 1691 (cond ((> n 0) 1692 (kern-log-continue ".") 1693 (kern-log-flush) 1694 (kern-sleep delay) 1695 (dots (- n 1))))) 1696 (kern-log-begin) 1697 (dots n) 1698 (kern-log-end) 1699 ) 1700 1701(define (find-first fn? lst) 1702 (if (null? lst) 1703 nil 1704 (if (fn? (car lst)) 1705 (car lst) 1706 (find-first fn? (cdr lst))))) 1707 1708(define (append! lst val) 1709 (cond ((null? lst) nil) 1710 ((null? (cdr lst)) (set-cdr! lst val)) 1711 (else (append! (cdr lst) val)))) 1712 1713(define (repeat fn n) 1714 (if (> n 0) 1715 (begin 1716 (fn) 1717 (repeat fn (- n 1))))) 1718 1719(define (string-lower str) 1720 (list->string (map char-downcase (string->list str)))) 1721 1722(define (!= a b) 1723 (not (= a b))) 1724 1725(define (rect-x r) (car r)) 1726(define (rect-y r) (cadr r)) 1727(define (rect-w r) (caddr r)) 1728(define (rect-h r) (cadddr r)) 1729 1730(define (rect-down r v) 1731 (list (rect-x r) (+ v (rect-y r)) (rect-w r) (rect-h r))) 1732 1733(define (rect-crop-down r v) 1734 (list (rect-x r) (+ v (rect-y r)) (rect-w r) (- (rect-h r) v))) 1735 1736(define (rect-offset r x y) 1737 (list (+ x (rect-x r)) (+ y (rect-y r)) (rect-w r) (rect-h r))) 1738 1739(define (rect-crop-offset r x y) 1740 (list (+ x (rect-x r)) (+ y (rect-y r)) (- (rect-w r) x) (- (rect-h r) y))) 1741 1742(define (1- x) (- x 1)) 1743(define (1+ x) (+ x 1)) 1744 1745;; Standard dc vs 1d20 + bonus, with a perfect roll granting automatic success. 1746(define (check-roll dc bonus) 1747 (let ((roll (kern-dice-roll "1d20"))) 1748 (or (= 20 roll) 1749 (> (+ roll bonus) dc)))) 1750 1751