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	))