1;; ---------------------------------------------------------------------------- 2;; effects.scm - generic effects procedures used in multiple places 3;; ---------------------------------------------------------------------------- 4 5;; Hook ids. These must agree with the kernel. 6(define start-of-turn-hook 0) 7(define add-hook-hook 1) 8(define on-damage-hook 2) 9(define keystroke-hook 3) 10(define nil-hook 4) 11(define on-death-hook 5) 12(define ready-equip-hook 6) 13(define unready-equip-hook 7) 14(define move-done-hook 8) 15(define attack-done-hook 9) 16(define cast-done-hook 10) 17(define drop-done-hook 11) 18(define yuse-done-hook 12) 19(define get-done-hook 13) 20(define handle-done-hook 14) 21(define open-done-hook 15) 22(define ready-done-hook 16) 23(define talk-done-hook 17) 24(define use-done-hook 18) 25(define mix-done-hook 19) 26(define kamp-start-hook 20) 27 28(define (mk-effect tag name sprite exec apply rm restart hook sym ddc cum dur) 29 (kern-mk-effect tag 30 name 31 sprite 32 exec 33 apply 34 rm 35 restart 36 hook 37 ddc 38 cum 39 dur 40 )) 41 42;; apply-time-scaled damage account for damaging effects applied at wilderness 43;; scale or when camping or loitering. At higher time scales I think it's not 44;; so nice to kill characters in one turn by applying the full damage for the 45;; time scale. However, you have to apply *some* extra damage or its 46;; incongruent. 47(define (time-scaled-damage-factor) 48 (if (> (kern-ticks-per-turn) 1) 49 10 50 1)) 51 52(define (poison-exec fgob obj) 53 (if (obj-is-char? obj) 54 (kern-obj-apply-damage obj "poisoned" (* 1 (time-scaled-damage-factor))))) 55 56;; ------------------------------------------------------------------ 57;; Accumulating duration effects support 58;; Should probably have a 'remove from list', but 59;; it might be a better way elsewhere anyway 60;; ------------------------------------------------------------------- 61 62(define (effect-list-lookup-loop fxlist target) 63 (if (null? fxlist) 64 nil 65 (if (equal? (caar fxlist) target) 66 (cadar fxlist) 67 (effect-list-lookup-loop (tail fxlist) target) 68 ))) 69 70(define (effect-list-lookup fxlist target) 71 (let ((result (effect-list-lookup-loop (tail fxlist) target))) 72 (if (null? result) 73 (car (cdr (car (tail (set-cdr! fxlist (append (list (list target (list 0))) (tail fxlist))))))) 74 result 75 ))) 76 77;; ---------------------------------------------------------------------------- 78;; Poison & Disease Immunities 79;; 80;; These work by attaching an effect to the "add-hook-hook", which runs 81;; whenever any new effect is applied. If anything tries to apply a poison 82;; effect, for example, the poison immunity effect will catch it and block the 83;; application. 84;; ---------------------------------------------------------------------------- 85(define (poison-immunity-exec fgob effect) 86 (if (eqv? effect ef_poison) #t #f)) 87 88(define (disease-immunity-exec fgob effect) 89 (if (eqv? effect ef_disease) #t #f)) 90 91(define (paralysis-immunity-exec fgob effect) 92 (if (eqv? effect ef_paralyze) #t #f)) 93 94(define (charm-immunity-exec fgob effect) 95 (if (eqv? effect ef_charm) #t #f)) 96 97(define (sleep-immunity-exec fgob effect) 98 (if (eqv? effect ef_sleep) #t #f)) 99 100;; ---------------------------------------------------------------------------- 101;; sleep 102;; 103;; The sleep effect is largely implemented in the kernel, and applies only to 104;; character types. It expires naturally when the character makes a saving 105;; throw. Note that this sleep effect is completely different than camping or 106;; resting, which is managed entirely by the kernel. 107;; ---------------------------------------------------------------------------- 108(define (sleep-exec fgob kobj) 109 (if (not (obj-is-char? kobj)) 110 (kern-obj-remove-effect kobj ef_sleep) 111 (let ((kchar kobj)) 112 (if (> (kern-dice-roll "1d20") 113 19) 114 (begin 115 (kern-obj-remove-effect kchar ef_sleep) 116 (kern-char-set-sleep kchar #t) ;; shouldn't this be #f? 117 ))))) 118 119(define (sleep-reset fgob kobj) 120 (if (obj-is-char? kobj) 121 (kern-char-set-sleep kobj #t))) 122 123(define (sleep-rm fgob kobj) 124 (if (obj-is-char? kobj) 125 (kern-char-set-sleep kobj #f))) 126 127;; ---------------------------------------------------------------------------- 128;; paralyze 129;; 130;; The paralyze effect rolls to expire each turn. If the roll fails, the 131;; character loses its turn. If it succeeds, the effect removes itself from the 132;; character. Treats a natural roll of 20 as success. 133;; ---------------------------------------------------------------------------- 134(define (can-paralyze? kobj) 135 (and (obj-is-char? kobj) 136 (not (species-is-immune-to-paralyze? (kern-char-get-species kobj))))) 137 138(define (paralyze-apply fgob kobj) 139 (kern-log-msg (kern-obj-get-name kobj) " paralyzed!")) 140 141(define (paralyze-exec fgob kobj) 142 (if (not (obj-is-char? kobj)) 143 (kern-obj-remove-effect kobj ef_paralyze) 144 (let ((kchar kobj) 145 (droll (kern-dice-roll "1d20"))) 146 (if (or (= droll 20) 147 (> droll 148 dc-escape-paralyze)) 149 (begin 150 (kern-log-msg "Paralysis wears off of " (kern-obj-get-name kchar)) 151 (kern-obj-remove-effect kchar ef_paralyze) 152 (if (is-player-party-member? kobj) 153 (kern-char-set-player-controlled kobj #t)) 154 #f) 155 (begin 156 (kern-log-msg "** " (kern-obj-get-name kchar) " remains paralyzed! **") 157 (kern-obj-set-ap kchar 0) 158 #f))))) 159 160 161(define (paralyze kobj) 162 (if (can-paralyze? kobj) 163 (begin 164 (kern-obj-add-effect kobj ef_paralyze nil) 165 (kern-char-set-player-controlled kobj #f) 166 ))) 167 168;;---------------------------------------------------------------------------- 169;; disease 170;; 171;; Drains life until victim is near death 172;;---------------------------------------------------------------------------- 173(define (disease-exec fgob kobj) 174 (if (not (obj-is-char? kobj)) 175 (kern-obj-remove-effect kobj ef_disease) 176 (let ((kchar kobj)) 177 (let ((dmgroll (* (time-scaled-damage-factor) 178 (kern-dice-roll "1d5"))) 179 (maxdmg (- (kern-char-get-hp kchar) 180 (kern-dice-roll "1d10")))) 181 (cond ((> dmgroll maxdmg) 182 (kern-log-msg (kern-obj-get-name kchar) " fights off Disease") 183 (kern-obj-apply-damage kchar "disease" maxdmg) 184 (kern-obj-remove-effect kchar ef_disease) 185 ) 186 (else 187 (kern-obj-apply-damage kchar "disease" dmgroll))))))) 188 189;; ---------------------------------------------------------------------------- 190;; ensnare 191;; 192;; The ensnare effect rolls against a character's strength each turn. If the 193;; roll fails, the character loses its turn. If it succeeds, the effect removes 194;; itself from the character. Also treat a natural roll of 20 as success. 195;; ---------------------------------------------------------------------------- 196(define (can-ensnare? kobj) 197 (and (obj-is-char? kobj) 198 (not (species-is-immune-to-ensnare? (kern-char-get-species kobj))))) 199 200(define (ensnare-apply fgob kobj) 201 (kern-log-msg (kern-obj-get-name kobj) " stuck in web!")) 202 203(define (ensnare-exec fgob kobj) 204 (println "ensnare-exec") 205 (if (not (can-ensnare? kobj)) 206 (kern-obj-remove-effect ef_ensnare) 207 (let ((kchar kobj) 208 (droll (kern-dice-roll "1d20"))) 209 ;; special case -- paralysis prevents struggling against the ensnare 210 (if (not (is-paralyzed? kchar)) 211 (if (or (= droll 20) 212 (> (+ (kern-char-get-strength kchar) 213 droll) 214 dc-escape-ensnare)) 215 (let ((loc (kern-obj-get-location kobj))) 216 (kern-log-msg (kern-obj-get-name kchar) " breaks free of web!") 217 (kern-obj-remove-effect kchar ef_ensnare) 218 (map kern-obj-remove-web (find-object-types-at loc web-type)) 219 (map kern-obj-remove-web (find-object-types-at loc F_web_perm)) 220 #t) 221 (begin 222 (kern-log-msg (kern-obj-get-name kchar) " struggles in the web!") 223 (kern-obj-set-ap kchar 0) 224 #f)))))) 225 226(define (is-ensnared? kobj) 227 (in-list? ef_ensnare (kern-obj-get-effects kobj))) 228 229(define (ensnare kobj) 230 (if (can-ensnare? kobj) 231 (begin 232 (kern-obj-add-effect kobj ef_ensnare nil)))) 233 234;; ---------------------------------------------------------------------------- 235;; Stuck 236;; 237;; Like ensnare, but no webs involved, and the thiefly ability is used to roll 238;; free. This was added as a risk balance for the wriggle skill. 239;; ---------------------------------------------------------------------------- 240(define (stuck-apply fgob kobj) 241 (kern-log-msg (kern-obj-get-name kobj) " stuck!")) 242 243(define (stuck-exec fgob kobj) 244 (cond ((check-roll dc-escape-stuck (occ-thief-dice-roll kobj)) 245 (kern-log-msg (kern-obj-get-name kobj) " wriggles free!") 246 (kern-obj-remove-effect kobj ef_stuck) 247 #t 248 ) 249 (else 250 (kern-log-msg (kern-obj-get-name kobj) " struggles!") 251 (kern-obj-set-ap kobj 0) 252 #f 253 ))) 254 255(define (is-stuckd? kobj) 256 (in-list? ef_stuck (kern-obj-get-effects kobj))) 257 258(define (stuck kobj) 259 (if (can-stuck? kobj) 260 (begin 261 (kern-obj-add-effect kobj ef_stuck nil)))) 262 263 264;;---------------------------------------------------------------------------- 265;; poison immunity 266(define (has-poison-immunity? kobj) 267 (let ((effects (kern-obj-get-effects kobj))) 268 (or (in-list? ef_poison_immunity effects) 269 (in-list? ef_temporary_poison_immunity effects)))) 270 271;;---------------------------------------------------------------------------- 272;; fire immunity 273(define (has-fire-immunity? kobj) 274 (let ((effects (kern-obj-get-effects kobj))) 275 (or (in-list? ef_fire_immunity effects) 276 (in-list? ef_temporary_fire_immunity effects)))) 277 278;;---------------------------------------------------------------------------- 279;; magical kill immunity 280(define (has-magical-kill-immunity? kobj) 281 (let ((effects (kern-obj-get-effects kobj))) 282 (or (in-list? ef_magical_kill_immunity effects) 283 (in-list? ef_temporary_magical_kill_immunity effects)))) 284 285(define (has-sleep-immunity? kobj) 286 (let ((effects (kern-obj-get-effects kobj))) 287 (or (in-list? ef_sleep_immunity effects) 288 (in-list? ef_temporary_sleep_immunity effects)))) 289 290(define (has-charm-immunity? kobj) 291 (let ((effects (kern-obj-get-effects kobj))) 292 (or (in-list? ef_charm_immunity effects) 293 (in-list? ef_temporary_charm_immunity effects)))) 294 295;; ---------------------------------------------------------------------------- 296;; light 297;; 298;; Light works by increasing the effected object's light value when the effect 299;; is applied, and decreasing it when the effect is removed. It does this in a 300;; two-step process. The first step is an effect which runs on the special 301;; ---------------------------------------------------------------------------- 302 303(define temp-light-power (list 0)) 304 305(define (temp-light-power-set power) 306 (set-car! temp-light-power power)) 307 308(define (light-rm fgob kobj) 309 (kern-log-msg "Light spell wore off") 310 (kern-obj-dec-light kobj (caar fgob)) 311 (temp-light-power-set (caar fgob))) 312 313(define (light-apply fgob kobj) 314 (kern-obj-inc-light kobj (caar fgob)) 315 ) 316 317;a function with a working power->time calculation would be nicer 318(define (light-effect-getdecr current) 319 (if (< current 300) 320 50 321 (if (< current 600) 322 5 323 (floor (/ current 20)) 324 ))) 325 326(define (light-dim power light-time current-time kobj) 327 (if (<= current-time light-time) 328 power 329 (let ((decrlight (light-effect-getdecr power))) 330 (if (> decrlight power) 331 0 332 (light-dim (- power decrlight) (+ light-time 1) current-time kobj) 333 )))) 334 335(define (light-exec fgob kobj) 336 (let* ((light-time (cadar fgob)) 337 (current-time (kern-get-total-minutes)) 338 (power (caar fgob)) 339 (newpower (light-dim power light-time current-time kobj))) 340 (cond ((= newpower power) nil) 341 ((<= newpower 0) (kern-obj-remove-effect kobj ef_light)) 342 (else 343 (set-car! fgob (list newpower (kern-get-total-minutes))) 344 (kern-obj-dec-light kobj (- power newpower)) 345 )))) 346 347(define (light-apply-new target power) 348 (temp-light-power-set 0) 349 (kern-log-enable #f) 350 (kern-obj-remove-effect target ef_light) 351 (kern-log-enable #t) 352 (let ((fxgob (list (list (+ power (car temp-light-power)) (kern-get-total-minutes))))) 353 (kern-obj-add-effect target ef_light fxgob) 354 )) 355 356;; ---------------------------------------------------------------------------- 357;; torchlight 358;; 359;; This is just like light but it's called out especially because it's 360;; vulnerable to the douse effect, where as normal light is not. 361;; ---------------------------------------------------------------------------- 362(define torchlight-amount 1024) 363 364(define (torchlight-rm fgob kobj) 365 (kern-log-msg "A torch flickers out!") 366 (kern-obj-dec-light kobj torchlight-amount)) 367 368(define (torchlight-apply fgob kobj) 369 (kern-obj-inc-light kobj torchlight-amount) 370 ;; Lighting up a torch will undo stealth mode 371 (kern-obj-remove-effect kobj ef_stealth) 372 ) 373 374;; ---------------------------------------------------------------------------- 375;; Weak light 376;; 377;; A silent, weak version of torchlight for NPCs. 378;; ---------------------------------------------------------------------------- 379(define weaklight-amount 256) 380 381(define (weaklight-rm fgob kobj) 382 (kern-obj-dec-light kobj weaklight-amount)) 383 384(define (weaklight-apply fgob kobj) 385 (kern-obj-inc-light kobj weaklight-amount)) 386 387;; ---------------------------------------------------------------------------- 388;; Protection 389;; 390;; Used by the In Sanct spell. 391;; ---------------------------------------------------------------------------- 392(define (protection-rm fgob kobj) 393 (if (obj-is-char? kobj) 394 (kern-char-add-defense kobj -10))) 395 396(define (protection-apply fgob kobj) 397 (if (obj-is-char? kobj) 398 (kern-char-add-defense kobj 10))) 399 400;; ---------------------------------------------------------------------------- 401;; Charm 402;; 403;; Used by the An Xen Ex spell. 404;; ---------------------------------------------------------------------------- 405(define (charm-mk faction) (list faction)) 406 407(define (charm-faction charm) (car charm)) 408 409(define (charm-rm charm kchar) 410 (cond ((obj-is-char? kchar) 411 (kern-being-set-current-faction kchar (kern-being-get-base-faction kchar)) 412 (if (is-player-party-member? kchar) 413 (kern-char-set-player-controlled kchar #t)) 414 (kern-log-msg (kern-obj-get-name kchar) " recovers from charm!") 415 ))) 416 417(define (charm-apply charm kchar) 418 (cond ((obj-is-char? kchar) 419 (kern-log-msg (kern-obj-get-name kchar) " is charmed!") 420 (kern-char-set-player-controlled kchar #f) 421 (kern-being-set-current-faction kchar (charm-faction charm)) 422 ))) 423 424;; ---------------------------------------------------------------------------- 425;; Loot Drop 426;; 427;; Used to generate loot when an NPC is killed. The hook-fx given to the gob is 428;; executed when the effect runs, taking the unfortunate npc as its parm. 429;; ---------------------------------------------------------------------------- 430(define (loot-drop-mk hook-fx . hook-fx-parms) (list 'loot-drop-gob hook-fx hook-fx-parms)) 431 432(define (loot-drop-hook-fx gob) (cadr gob)) 433(define (loot-drop-hook-fx-parms gob) (caddr gob)) 434 435(define (loot-drop-exec fgob kobj) 436 (if (not (obj-is-char? kobj)) 437 (kern-obj-remove-effect kobj ef_loot_drop) 438 (let ((kchar kobj)) 439 (apply (eval (loot-drop-hook-fx fgob)) 440 (cons kchar 441 (loot-drop-hook-fx-parms fgob)))))) 442 443;; ---------------------------------------------------------------------- 444;; Generic death 445;; Just does some arbitary named closure 446;; (kern-obj-add-effect npc 447;; ef_generic_death 448;; 'a_closure_name) 449;; -------------------------------------------------------------------- 450 451(define (generic-death-exec fgob kobj) 452 (if (not (obj-is-char? kobj)) 453 (kern-obj-remove-effect kobj ef_loot_drop) 454 ((eval fgob) kobj) 455 )) 456 457;; ---------------------------------------------------------------------------- 458;; Invisibility 459;; 460;; Used by the Sanct Lor spell. Note: the kernel's kern-obj-set-visible proc 461;; increments/decrements a visibility counter, naturally handling cumulative 462;; invisibility effects. 463;; ---------------------------------------------------------------------------- 464(define (invisibility-rm fgob kobj) 465 (kern-obj-set-visible kobj #t)) 466 467(define (invisibility-apply fgob kobj) 468 (kern-obj-set-visible kobj #f)) 469 470;; ---------------------------------------------------------------------------- 471;; Stealth 472;; 473;; Used by the Stealth skill. Similar to invisibility, but it decrements MP on 474;; every turn. Also, on anything that involves movement it rolls to remove 475;; itself. 476;; ---------------------------------------------------------------------------- 477(define (stealth-apply fgob kobj) 478 (kern-obj-set-visible kobj #f) 479 (map (lambda (x) (kern-obj-add-effect kobj x nil)) 480 stealth-co-effects)) 481 482;; Helper for the misc stealth effects 483(define (stealth-exec-generic kobj dc) 484 (let ((roll (kern-dice-roll "1d20")) 485 (bonus (occ-thief-dice-roll kobj)) 486 (bonus2 (kern-char-get-level kobj)) 487 ) 488 ;;(println "stealth:" roll "+" bonus "+" bonus2 ">?" dc) 489 (if (< (+ roll bonus bonus2) dc) 490 (kern-obj-remove-effect kobj ef_stealth) 491 ))) 492 493(define (stealth-exec fgob kobj) 494 ;; hack -- add the yuse-done hook now instead of in stealth-apply 495 ;; application. Otherwise, as soon as the player y)uses stealth, the 496 ;; yuse-done hook immediately runs and potentially removes stealth mode. 497 (stealth-exec-generic kobj dc-nontrivial) 498 (if (not (has-effect? kobj ef_stealth_yuse)) 499 (kern-obj-add-effect kobj ef_stealth_yuse nil)) 500 ) 501 502(define (stealth-rm fgob kobj) 503 (kern-obj-set-visible kobj #t) 504 (map (lambda (x) (kern-obj-remove-effect kobj x)) 505 stealth-co-effects) 506 ;; And treat yuse as a special case 507 (kern-obj-remove-effect kobj ef_stealth_yuse) 508 (kern-log-msg (kern-obj-get-name kobj) " goes out of stealth mode!") 509 ) 510 511;; At basic skill levels any movement will undo stealth mode. As the character 512;; advances its ability to remain hidden while moving increases naturally. With 513;; a DC of 16, An L3 wrogue with dexterity 10 will give itself away with 514;; movement about 50% of the time. 515(define (stealth-do-simple-exec fgob kobj) 516 (stealth-exec-generic kobj dc-nontrivial)) 517 518(define (stealth-move-exec fgob kobj kplace x y) 519 (stealth-exec-generic kobj dc-normal)) 520 521(define (stealth-do-challenging-exec fgob kobj) 522 (stealth-exec-generic kobj dc-challenging)) 523 524(define (stealth-do-masterful-exec fgob kobj) 525 (stealth-exec-generic kobj dc-masterful)) 526 527(define (stealth-attack-exec fgob kobj kweap ktarg) 528 (stealth-exec-generic kobj dc-challenging)) 529 530(define (stealth-do-impossible-exec fgob kobj) 531 (kern-obj-remove-effect kobj ef_stealth)) 532 533;; ---------------------------------------------------------------------------- 534;; Slime Split 535;; 536;; A special feature of the slime species. When a slime takes damage it rolls 537;; to clone itself. 538;; ---------------------------------------------------------------------------- 539(define (split-gob-mk npc-type-tag) (list npc-type-tag)) 540(define (split-gob-npc-type-tag gob) (car gob)) 541 542(define (split-exec fgob kobj) 543 (let ((loc (kern-obj-get-location kobj))) 544 (if (not (kern-place-is-wilderness? (loc-place loc))) 545 (begin 546 ;; eight is too low 547 (if (> (kern-dice-roll "1d20") 10) 548 (let* ( 549 (orighp (kern-char-get-hp kobj)) 550 (orighproll (string-append "1d" (number->string (kern-char-get-max-hp kobj)))) 551 (hurtclone (< (kern-dice-roll orighproll) orighp)) 552 (origlevel (kern-char-get-level kobj)) 553 (clonelevel 554 (if (= origlevel 1) 555 1 556 (if hurtclone 557 origlevel 558 (- origlevel 1)))) 559 (clone (mk-npc (split-gob-npc-type-tag fgob) 560 clonelevel))) 561 (kern-being-set-base-faction clone 562 (kern-being-get-base-faction kobj)) 563 (if hurtclone 564 (kern-char-set-hp clone orighp)) 565 (kern-log-msg (kern-obj-get-name kobj) " divides!") 566 (kern-obj-put-at clone (pick-loc loc clone))) 567 ))))) 568 569;; ---------------------------------------------------------------------------- 570;; Grow Head 571;; 572;; A special feature of the hydra species. When a hydra takes damage it gains 573;; experience, accelerating its advancement. 574;; ---------------------------------------------------------------------------- 575(define (grow-head-exec fgob kobj) 576 (if (obj-is-char? kobj) 577 (kern-char-add-experience kobj (kern-dice-roll "2d20")))) 578 579;; ---------------------------------------------------------------------------- 580;; Spider Calm 581;; 582;; Used by the An Xen Bet spell to prevent spiders from attacking. 583;; ---------------------------------------------------------------------------- 584(define (spider-calm-rm fgob kchar) 585 (kern-dtable-dec (kern-being-get-current-faction kchar) 586 faction-spider) 587 (kern-log-msg (kern-obj-get-name kchar) " seems less friendly to spiders")) 588 589(define (spider-calm-apply fgob kchar) 590 (kern-dtable-inc (kern-being-get-current-faction kchar) 591 faction-spider) 592 (kern-log-msg (kern-obj-get-name kchar) " makes spiders seem friendlier")) 593 594;;---------------------------------------------------------------------------- 595;; Drunk 596;; 597;; Every keystroke at start-of-turn, roll to make the victim move in a random 598;; direction as if staggering. If roll succeeds end the victim's turn. Ending 599;; the turn prevents cumulative drinks from causing more than one "stagger" per 600;; turn. 601;;---------------------------------------------------------------------------- 602(define (drunk-exec fgob kchar) 603 (if (> (kern-dice-roll "1d20") 16) 604 (if (stagger kchar) 605 (begin 606 (kern-log-msg (kern-obj-get-name kchar) " staggers!") 607 (end-turn kchar))))) 608 609(define (drunk-apply fgob kchar) 610 (kern-log-msg (kern-obj-get-name kchar) " feels tipsy!")) 611 612(define (drunk-rm fgob kchar) 613 (kern-log-msg (kern-obj-get-name kchar) " has a hangover!")) 614 615;;----------------------------------------------------------------- 616;; Graphics update 617;; for stuff that changes appearance with time 618;; requires update-gfx ifc 619;;----------------------------------------------------------- 620 621(define (update-graphics fgob kobj) 622 (if (kobj-can? kobj 'update-gfx) 623 (send-signal nil kobj 'update-gfx) 624 )) 625 626;;---------------------------------------------------------------------------- 627;; Ready/Unready hooks 628;;---------------------------------------------------------------------------- 629(define (uses-paper-doll? kobj) 630 (and (obj-is-char? kobj) 631 (eqv? (kern-char-get-species kobj) 632 sp_human))) 633 634(define (ktype-get-sprite ktype) 635 (let ((gob (kern-type-get-gob ktype))) 636 (if (null? gob) 637 nil 638 gob))) 639 640(define (rebuild-humanoid-sprite khum) 641 (re-mk-composite-sprite (cons (kern-sprite-strip-decorations 642 (kern-obj-get-sprite khum)) 643 (filter notnull? 644 (map ktype-get-sprite 645 (kern-char-get-arms khum)))))) 646 647(define (ready-equip fgob kobj karms slot) 648 (if (uses-paper-doll? kobj) 649 (begin 650 (kern-obj-set-sprite kobj (rebuild-humanoid-sprite kobj)) 651 (kern-map-set-dirty)))) 652 653(define (unready-equip fgob kobj karms slot) 654 (ready-equip fgob kobj karms slot)) 655 656;; ---------------------------------------------------------------------------- 657;; Cleanup tentacles (for sludge krakens when they die). Note that this is a 658;; hack, in that ALL tentacles in the current place are cleaned up, whether 659;; they "belong" to the dying sludge kraken or not. 660;; ---------------------------------------------------------------------------- 661(define (cleanup-tentacles fgob kobj) 662 (map kern-char-kill 663 (filter is-sludge-tentacle? 664 (kern-place-get-beings (loc-place (kern-obj-get-location kobj)))))) 665 666;; ---------------------------------------------------------------------------- 667;; Unrest-Curses. These replace the normal camping proc with one that always creates 668;; an ambush. The 'fgob' of the effect is the tag of the npc party to 669;; generate. This is specified when the unrest-curse effect is added to the target. 670;; ---------------------------------------------------------------------------- 671(define (unrest-camping-proc kplayer kplace fgob) 672 (println "unrest-camping-proc") 673 (kern-ambush-while-camping (mk-npc-party fgob) kplace) 674 (kern-ambush-while-camping (mk-npc-party fgob) kplace) 675 ) 676 677(define (unrest-curse-apply fgob kobj) 678 (println "unrest-curse-apply " fgob) 679 (kern-add-hook 'camping_turn_start_hook 'unrest-camping-proc fgob) 680 ) 681 682(define (unrest-curse-rm fgob kobj) 683 (println "unrest-curse-rm " fgob) 684 (kern-rm-hook 'camping_turn_start_hook 'unrest-camping-proc) 685 ) 686 687(define (unrest-curse-apply-new ktarg party-tag) 688 (kern-obj-add-effect ktarg ef_unrest_curse party-tag) 689 ) 690 691;; ---------------------------------------------------------------------------- 692;; Effects Table 693;; ---------------------------------------------------------------------------- 694 695;; Start-of-turn hooks 696(mk-effect 'ef_poison "Poison" s_poison 'poison-exec nil nil nil start-of-turn-hook "P" 0 #f -1) 697(mk-effect 'ef_sleep "Sleep" s_sleep 'sleep-exec nil 'sleep-rm 'sleep-reset start-of-turn-hook "S" 0 #f 60) 698(mk-effect 'ef_light "Magical light" s_light 'light-exec 'light-apply 'light-rm 'light-apply start-of-turn-hook "L" 0 #t -2) 699(mk-effect 'ef_torchlight "Torchlight" s_torchlight nil 'torchlight-apply 'torchlight-rm 'torchlight-apply start-of-turn-hook "T" 0 #f 60) 700(mk-effect 'ef_weaklight "Torchlight" s_torchlight nil 'weaklight-apply 'weaklight-rm 'weaklight-apply start-of-turn-hook "T" 0 #f 60) 701(mk-effect 'ef_protection "Protection" s_protect nil 'protection-apply 'protection-rm 'protection-apply start-of-turn-hook "p" 0 #f 10) 702(mk-effect 'ef_charm "Charm" s_charm nil 'charm-apply 'charm-rm 'charm-apply start-of-turn-hook "C" 0 #f 5) 703(mk-effect 'ef_invisibility "Invisible" s_invis nil 'invisibility-apply 'invisibility-rm 'invisibility-apply start-of-turn-hook "N" 0 #t 10) 704(mk-effect 'ef_permanent_invisibility "Invisible" s_invis nil 'invisibility-apply 'invisibility-rm 'invisibility-apply start-of-turn-hook "N" 0 #t -1) 705(mk-effect 'ef_spider_calm "Spider calm" s_spider_calm nil 'spider-calm-apply 'spider-calm-rm nil start-of-turn-hook "" 0 #f 60) 706(mk-effect 'ef_disease "Diseased" s_disease 'disease-exec nil nil nil start-of-turn-hook "D" 0 #f -2) 707(mk-effect 'ef_graphics_update nil nil 'update-graphics nil nil 'update-graphics start-of-turn-hook "" 0 #f -1) 708(mk-effect 'ef_stealth "Stealth" nil 'stealth-exec 'stealth-apply 'stealth-rm 'stealth-apply start-of-turn-hook "" 0 #f -1) 709 710;; Add-hook hooks 711(mk-effect 'ef_poison_immunity "Poison immunity" s_im_poison 'poison-immunity-exec nil nil nil add-hook-hook "I" 0 #f -1) 712(mk-effect 'ef_temporary_poison_immunity "Poison immunity" s_im_poison 'poison-immunity-exec nil nil nil add-hook-hook "I" 0 #f 60) 713(mk-effect 'ef_disease_immunity "Disease immunity" s_im_disease 'disease-immunity-exec nil nil nil add-hook-hook "E" 0 #f -1) 714(mk-effect 'ef_temporary_disease_immunity "Disease immunity" s_im_disease 'disease-immunity-exec nil nil nil add-hook-hook "E" 0 #f 60) 715(mk-effect 'ef_paralysis_immunity "Paralysis immunity" s_im_paralyse 'paralysis-immunity-exec nil nil nil add-hook-hook "z" 0 #f -1) 716(mk-effect 'ef_temporary_paralysis_immunity "Paralysis immunity" s_im_paralyse 'paralysis-immunity-exec nil nil nil add-hook-hook "z" 0 #f 60) 717(mk-effect 'ef_charm_immunity "Charm immunity" s_im_charm 'charm-immunity-exec nil nil nil add-hook-hook "c" 0 #f -1) 718(mk-effect 'ef_temporary_charm_immunity "Charm immunity" s_im_charm 'charm-immunity-exec nil nil nil add-hook-hook "c" 0 #f 60) 719(mk-effect 'ef_sleep_immunity "Sleep immunity" s_im_sleep 'sleep-immunity-exec nil nil nil add-hook-hook "s" 0 #f -1) 720(mk-effect 'ef_temporary_sleep_immunity "Sleep immunity" s_im_sleep 'sleep-immunity-exec nil nil nil add-hook-hook "s" 0 #f 60) 721 722;; Nil hooks 723(mk-effect 'ef_fire_immunity "Fire immunity" s_im_fire nil nil nil nil nil-hook "F" 0 #f -1) 724(mk-effect 'ef_temporary_fire_immunity "Fire immunity" s_im_fire nil nil nil nil nil-hook "F" 0 #f 15) 725(mk-effect 'ef_magical_kill_immunity "Magic kill immunity" s_im_death nil nil nil nil nil-hook "K" 0 #f -1) 726(mk-effect 'ef_temporary_magical_kill_immunity "Magic kill immunity" s_im_death nil nil nil nil nil-hook "K" 0 #f 15) 727(mk-effect 'ef_fatigue "Fatigue" s_unrest nil nil nil nil nil-hook "F" 0 #f 1) 728(mk-effect 'ef_unrest_curse "Curse of Unrest" s_unrest nil 'unrest-curse-apply 'unrest-curse-rm 'unrest-curse-apply nil-hook "P" 0 #f (* 60 24)) 729 730;; Keystroke hooks 731(mk-effect 'ef_drunk "Drunk" s_drunk 'drunk-exec 'drunk-apply 'drunk-rm nil keystroke-hook "A" 0 #t 60) 732(mk-effect 'ef_paralyze "Paralyzed" s_paralyse 'paralyze-exec 'paralyze-apply nil 'paralyze-apply start-of-turn-hook "Z" 0 #f 15) 733(mk-effect 'ef_ensnare "Ensnared" s_tangle 'ensnare-exec 'ensnare-apply nil 'ensnare-apply keystroke-hook "E" 0 #f 15) 734(mk-effect 'ef_stuck "Stuck" s_tangle 'stuck-exec 'stuck-apply nil 'stuck-apply keystroke-hook "E" 0 #f 15) 735 736;; On-damage hooks 737(mk-effect 'ef_split "Split" nil 'split-exec nil nil nil on-damage-hook "" 0 #f -1) 738(mk-effect 'ef_grow_head "XP from damage" nil 'grow-head-exec nil nil 'grow-head-exec on-damage-hook "H" 0 #f -1) 739(mk-effect 'ef_temporary_grow_head "XP from damage" nil 'grow-head-exec nil nil 'grow-head-exec on-damage-hook "H" 0 #f 15) 740 741 742;; Ready-equip hooks 743(mk-effect 'ef_ready_equip nil nil 'ready-equip nil nil nil ready-equip-hook "" 0 #f -1) 744 745;; Unready-equip hooks 746(mk-effect 'ef_unready_equip nil nil 'unready-equip nil nil nil unready-equip-hook "" 0 #f -1) 747 748;; On-death hooks 749(mk-effect 'ef_loot_drop nil nil 'loot-drop-exec nil nil nil on-death-hook "" 0 #f -1) 750(mk-effect 'ef_cleanup_tentacles nil nil 'cleanup-tentacles nil nil nil on-death-hook "" 0 #f -1) 751(mk-effect 'ef_generic_death nil nil 'generic-death-exec nil nil nil on-death-hook "" 0 #f -1) 752 753;; Move-done hooks 754(mk-effect 'ef_stealth_move nil nil 'stealth-move-exec nil nil nil move-done-hook "" 0 #t -1) 755 756;; Attack-done hooks 757(mk-effect 'ef_stealth_attack nil nil 'stealth-attack-exec nil nil nil attack-done-hook "" 0 #t -1) 758 759;; Bunch of almost-generic co-effects for stealth 760(map (lambda (x) 761 (mk-effect (car x) nil nil (caddr x) nil nil nil (cadr x) "" 0 #t -1)) 762 (list 763 (list 'ef_stealth_cast cast-done-hook 'stealth-do-masterful-exec) 764 (list 'ef_stealth_yuse yuse-done-hook 'stealth-do-masterful-exec) 765 (list 'ef_stealth_get get-done-hook 'stealth-do-simple-exec) 766 (list 'ef_stealth_handle handle-done-hook 'stealth-do-challenging-exec) 767 (list 'ef_stealth_mix mix-done-hook 'stealth-do-challenging-exec) 768 (list 'ef_stealth_open open-done-hook 'stealth-do-challenging-exec) 769 (list 'ef_stealth_ready ready-done-hook 'stealth-do-challenging-exec) 770 (list 'ef_stealth_drop drop-done-hook 'stealth-do-simple-exec) 771 (list 'ef_stealth_use use-done-hook 'stealth-do-simple-exec) 772 )) 773 774(define stealth-co-effects 775 (list 776 ef_stealth_move 777 ef_stealth_attack 778 ef_stealth_cast 779 ;; ef_stealth_yuse -- nope, needs to be a special case 780 ef_stealth_get 781 ef_stealth_handle 782 ef_stealth_mix 783 ef_stealth_open 784 ef_stealth_ready 785 ef_stealth_drop 786 ef_stealth_use 787 )) 788 789;;---------------------------------------------------------------------------- 790;; Effect Test Procedures 791;;---------------------------------------------------------------------------- 792 793(define (has-effect? kobj kef) 794 (in-list? kef (kern-obj-get-effects kobj))) 795 796(define (is-poisoned? kobj) 797 (in-list? ef_poison (kern-obj-get-effects kobj))) 798 799(define (is-paralyzed? kobj) 800 (in-list? ef_paralyze (kern-obj-get-effects kobj))) 801 802(define (is-diseased? kobj) 803 (in-list? ef_disease (kern-obj-get-effects kobj))) 804 805(define (is-asleep? kobj) 806 (in-list? ef_sleep (kern-obj-get-effects kobj))) 807 808(define (is-charmed? kobj) 809 (in-list? ef_charm (kern-obj-get-effects kobj))) 810 811(define (is-invisible? kobj) 812 (in-list? ef_invisibility (kern-obj-get-effects kobj))) 813 814(define (is-disabled? kobj) 815 (let ((effects (kern-obj-get-effects kobj))) 816 (if (null? effects) 817 #f 818 (foldr (lambda (x effect) 819 (or x 820 (in-list? effect effects))) 821 #f 822 (list ef_paralyze ef_sleep ef_charm ef_ensnare))))) 823 824(define (not-disabled? kobj) 825 (not (is-disabled? kobj))) 826 827;; ---------------------------------------------------------------------------- 828;; Effect Application Procedures 829;; ---------------------------------------------------------------------------- 830 831;; Used by spells: 832(define (apply-poison obj) 833 (if (obj-is-char? obj) 834 (cond ((not (has-poison-immunity? obj)) 835 (kern-log-msg (kern-obj-get-name obj) " poisoned!") 836 (kern-obj-add-effect obj ef_poison nil)) 837 (else 838 (kern-log-msg (kern-obj-get-name obj) " immune to poison!")))) 839 obj) 840 841;; Used by species that are inherently immune: 842(define (apply-poison-immunity kobj) 843 (kern-obj-add-effect kobj ef_poison_immunity nil) 844 kobj) 845 846(define (apply-sleep kobj) 847 (cond ((and (obj-is-char? kobj) 848 (not (has-sleep-immunity? kobj))) 849 (kern-char-set-sleep kobj #t) 850 (kern-obj-add-effect kobj ef_sleep nil))) 851 kobj) 852 853(define (make-invisible kobj) 854 (kern-obj-add-effect kobj ef_permanent_invisibility nil) 855 kobj) 856 857(define (apply-acid kchar) 858 (if (obj-is-char? kchar) 859 (let ((arms (kern-char-get-arms kchar))) 860 (if (null? arms) 861 (kern-log-msg "Acid has no effect!") 862 (let ((ktype (random-select arms))) 863 (if (in-list? ktype arms-immune-to-acid) 864 (kern-log-msg "Acid hits " (kern-type-get-name ktype) " but has no effect!") 865 (begin 866 (kern-log-msg "Acid dissolves 1 " (kern-type-get-name ktype) 867 " held by " (kern-obj-get-name kchar)) 868 (kern-char-unready kchar ktype) 869 (kern-obj-remove-from-inventory kchar ktype 1)))))))) 870 871;; TODO: multiply damage by kern-ticks-per-turn? 872;; TODO: define ifc for objects taking damage from fire 873(define (generic-burn obj dice) 874 (let ((damage (kern-dice-roll dice))) 875 (if (and (> damage 0) 876 (or (not (kern-obj-is-being? obj)) 877 (not (has-fire-immunity? obj)) 878 )) 879 (begin 880 (if (kern-obj-is-being? obj) 881 (begin 882 (kern-log-msg (kern-obj-get-name obj) " burned!") 883 (kern-obj-apply-damage obj "burning" damage) 884 ) 885 )) 886 ))) 887 888(define (burn obj) 889 (generic-burn obj "2d3+2")) 890 891(define (great-burn obj) 892 (generic-burn obj "10d8+20")) 893 894;; fixme: what about the player party? probably not safe to just remove it from 895;; the map... 896;; player party seems to work fine. (sigh. another mighty adventurer bites the dust) 897(define (chasm-fall kobj) 898 (cond ((and (not (can-fly? kobj)) 899 (not (is-abstract? kobj)) 900 (or (not (ship-at? (kern-obj-get-location kobj))) 901 (not (kern-obj-is-being? kobj))) 902 (not (and (obj-is-char? kobj) (kchar-in-vehicle? kobj))) 903 (eqv? pclass-space (kern-terrain-get-pclass (kern-place-get-terrain (kern-obj-get-location kobj))))) 904 (kern-log-msg (kern-obj-get-name kobj) " drops into the abyss!") 905 (if (obj-is-char? kobj) 906 (kern-char-kill kobj) 907 (kern-obj-remove kobj))))) 908 909(define (magical-kill obj) 910 (if (and (kern-obj-is-char? obj) 911 (not (has-magical-kill-immunity? obj))) 912 (kern-char-kill obj))) 913 914 915(define (slip obj) 916 (let ((mmode (kern-obj-get-mmode obj))) 917 (if (eqv? mmode mmode-walk) 918 (if (< (kern-dice-roll "1d20") 5) 919 (let ((dir (kern-obj-get-dir obj))) 920 (if (not (and (= 0 (car dir)) 921 (= 0 (cadr dir)))) 922 (begin 923 (kern-obj-move obj (- (car dir)) (- (cadr dir))) 924 (kern-log-msg "Slipped!") 925 (kern-obj-apply-damage obj "slipped" (kern-dice-roll "1d4"))))))))) 926 927;; TODO: multiply damage by kern-ticks-per-turn? 928;; TODO: define ifc for objects taking damage from shock 929(define (apply-lightning obj) 930 (if (kern-obj-is-being? obj) 931 (begin 932 (kern-log-msg (kern-obj-get-name obj) " shocked!") 933 (kern-obj-apply-damage obj "shocked" (kern-dice-roll "2d8"))) 934 )) 935 936;; Drop a random temporary field on the object's location 937(define (apply-random-field kobj) 938 (kern-obj-put-at (kern-mk-obj (random-select (list F_fire 939 F_poison 940 F_sleep 941 F_energy)) 942 1) 943 (kern-obj-get-location kobj))) 944 945;; Prismatic -- pick a random effect. This isn't quite what I want, I'd rather 946;; go through the powers layer, but that requires me to know who my caster 947;; is. This was written to be used by a weapon like a prismatic wand, and the 948;; missile procedures don't get the user/caster as a parm (yet). 949(define (apply-prismatic kobj) 950 (if (or (not (kern-obj-is-being? kobj)) 951 (contest-of-skill 8 (occ-ability-magicdef kobj))) 952 (let ((selection (random-select (list 'paralyze 953 'apply-acid 954 'apply-poison 955 'burn 956 'slip 957 'apply-lightning 958 'apply-random-field 959 )))) 960 (apply (eval selection) 961 (list kobj))))) 962 963 964 965;;---------------------------------------------------------------------------- 966;; Misc stuff -- not sure where to put this 967(define (douse ktarg) 968 (kern-obj-remove-effect ktarg ef_torchlight)) 969 970(define (wind-trap ktarg) 971 (kern-log-msg "A gust of wind!") 972 (douse ktarg) 973 #f ;; prevents removal of trigger 974 ) 975 976 977;;-------------------------------------------------------------------------- 978;; Dispell Magic effects 979 980(define (effects-dispel-magic ktarg) 981 (map (lambda (effect) 982 (kern-obj-remove-effect ktarg effect) 983 ) 984 (list 985 ef_sleep 986 ef_light 987 ef_protection 988 ef_charm 989 ef_invisibility 990 ef_spider_calm 991 ef_temporary_poison_immunity 992 ef_temporary_disease_immunity 993 ef_temporary_paralysis_immunity 994 ef_temporary_charm_immunity 995 ef_temporary_sleep_immunity 996 ef_temporary_fire_immunity 997 ef_temporary_magical_kill_immunity 998 ef_paralyze 999 ef_temporary_grow_head 1000 ) 1001 ))