1 /**
2 * \file project-player.c
3 * \brief projection effects on the player
4 *
5 * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
6 *
7 * This work is free software; you can redistribute it and/or modify it
8 * under the terms of either:
9 *
10 * a) the GNU General Public License as published by the Free Software
11 * Foundation, version 2, or
12 *
13 * b) the "Angband licence":
14 * This software may be copied and distributed for educational, research,
15 * and not for profit purposes provided that this copyright and statement
16 * are included in all such copies. Other copyrights may also apply.
17 */
18
19 #include "angband.h"
20 #include "cave.h"
21 #include "effects.h"
22 #include "init.h"
23 #include "mon-desc.h"
24 #include "mon-predicate.h"
25 #include "mon-util.h"
26 #include "obj-desc.h"
27 #include "obj-gear.h"
28 #include "obj-knowledge.h"
29 #include "player-calcs.h"
30 #include "player-timed.h"
31 #include "player-util.h"
32 #include "project.h"
33 #include "source.h"
34 #include "trap.h"
35
36 /**
37 * Adjust damage according to resistance or vulnerability.
38 *
39 * \param p is the player
40 * \param type is the attack type we are checking.
41 * \param dam is the unadjusted damage.
42 * \param dam_aspect is the calc we want (min, avg, max, random).
43 * \param resist is the degree of resistance (-1 = vuln, 3 = immune).
44 */
adjust_dam(struct player * p,int type,int dam,aspect dam_aspect,int resist,bool actual)45 int adjust_dam(struct player *p, int type, int dam, aspect dam_aspect,
46 int resist, bool actual)
47 {
48 int i, denom = 0;
49
50 /* If an actual player exists, get their actual resist */
51 if (p && p->race) {
52 /* Ice is a special case */
53 int res_type = (type == PROJ_ICE) ? PROJ_COLD: type;
54 resist = res_type < ELEM_MAX ? p->state.el_info[res_type].res_level : 0;
55
56 /* Notice element stuff */
57 if (actual) {
58 equip_learn_element(p, res_type);
59 }
60 }
61
62 if (resist == 3) /* immune */
63 return 0;
64
65 /* Hack - acid damage is halved by armour */
66 if (type == PROJ_ACID && p && minus_ac(p))
67 dam = (dam + 1) / 2;
68
69 if (resist == -1) /* vulnerable */
70 return (dam * 4 / 3);
71
72 /* Variable resists vary the denominator, so we need to invert the logic
73 * of dam_aspect. (m_bonus is unused) */
74 switch (dam_aspect) {
75 case MINIMISE:
76 denom = randcalc(projections[type].denominator, 0, MAXIMISE);
77 break;
78 case MAXIMISE:
79 denom = randcalc(projections[type].denominator, 0, MINIMISE);
80 break;
81 case AVERAGE:
82 case EXTREMIFY:
83 case RANDOMISE:
84 denom = randcalc(projections[type].denominator, 0, dam_aspect);
85 break;
86 default:
87 assert(0);
88 }
89
90 for (i = resist; i > 0; i--)
91 if (denom)
92 dam = dam * projections[type].numerator / denom;
93
94 return dam;
95 }
96
97
98 /**
99 * ------------------------------------------------------------------------
100 * Player handlers
101 * ------------------------------------------------------------------------ */
102
103 /**
104 * Drain stats at random
105 *
106 * \param num is the number of points to drain
107 */
project_player_drain_stats(int num)108 static void project_player_drain_stats(int num)
109 {
110 int i, k = 0;
111 const char *act = NULL;
112
113 for (i = 0; i < num; i++) {
114 switch (randint1(5)) {
115 case 1: k = STAT_STR; act = "strong"; break;
116 case 2: k = STAT_INT; act = "bright"; break;
117 case 3: k = STAT_WIS; act = "wise"; break;
118 case 4: k = STAT_DEX; act = "agile"; break;
119 case 5: k = STAT_CON; act = "hale"; break;
120 }
121
122 msg("You're not as %s as you used to be...", act);
123 player_stat_dec(player, k, false);
124 }
125
126 return;
127 }
128
129 typedef struct project_player_handler_context_s {
130 /* Input values */
131 const struct source origin;
132 const int r;
133 const struct loc grid;
134 int dam; /* May need adjustment */
135 const int type;
136 const int power;
137
138 /* Return values */
139 bool obvious;
140 } project_player_handler_context_t;
141
142 typedef int (*project_player_handler_f)(project_player_handler_context_t *);
143
project_player_handler_ACID(project_player_handler_context_t * context)144 static int project_player_handler_ACID(project_player_handler_context_t *context)
145 {
146 if (player_is_immune(player, ELEM_ACID)) return 0;
147 inven_damage(player, PROJ_ACID, MIN(context->dam * 5, 300));
148 return 0;
149 }
150
project_player_handler_ELEC(project_player_handler_context_t * context)151 static int project_player_handler_ELEC(project_player_handler_context_t *context)
152 {
153 if (player_is_immune(player, ELEM_ELEC)) return 0;
154 inven_damage(player, PROJ_ELEC, MIN(context->dam * 5, 300));
155 return 0;
156 }
157
project_player_handler_FIRE(project_player_handler_context_t * context)158 static int project_player_handler_FIRE(project_player_handler_context_t *context)
159 {
160 if (player_is_immune(player, ELEM_FIRE)) return 0;
161 inven_damage(player, PROJ_FIRE, MIN(context->dam * 5, 300));
162
163 /* Occasional side-effects for powerful fire attacks */
164 if (context->power >= 80) {
165 if (randint0(context->dam) > 500) {
166 msg("The intense heat saps you.");
167 effect_simple(EF_DRAIN_STAT, source_none(), "0", STAT_STR, 0, 0, 0,
168 0, &context->obvious);
169 }
170 if (randint0(context->dam) > 500) {
171 if (player_inc_timed(player, TMD_BLIND,
172 randint1(context->dam / 100), true, true)) {
173 msg("Your eyes fill with smoke!");
174 }
175 }
176 if (randint0(context->dam) > 500) {
177 if (player_inc_timed(player, TMD_POISONED,
178 randint1(context->dam / 10), true, true)) {
179 msg("You are assailed by poisonous fumes!");
180 }
181 }
182 }
183 return 0;
184 }
185
project_player_handler_COLD(project_player_handler_context_t * context)186 static int project_player_handler_COLD(project_player_handler_context_t *context)
187 {
188 if (player_is_immune(player, ELEM_COLD)) return 0;
189 inven_damage(player, PROJ_COLD, MIN(context->dam * 5, 300));
190
191 /* Occasional side-effects for powerful cold attacks */
192 if (context->power >= 80) {
193 if (randint0(context->dam) > 500) {
194 msg("The cold seeps into your bones.");
195 effect_simple(EF_DRAIN_STAT, source_none(), "0", STAT_DEX, 0, 0, 0,
196 0, &context->obvious);
197 }
198 if (randint0(context->dam) > 500) {
199 if (player_of_has(player, OF_HOLD_LIFE)) {
200 equip_learn_flag(player, OF_HOLD_LIFE);
201 } else {
202 int drain = context->dam;
203 msg("The cold withers your life force!");
204 player_exp_lose(player, drain, false);
205 }
206 }
207 }
208 return 0;
209 }
210
project_player_handler_POIS(project_player_handler_context_t * context)211 static int project_player_handler_POIS(project_player_handler_context_t *context)
212 {
213 int xtra = 0;
214
215 if (!player_inc_timed(player, TMD_POISONED, 10 + randint1(context->dam),
216 true, true))
217 msg("You resist the effect!");
218
219 /* Occasional side-effects for powerful poison attacks */
220 if (context->power >= 60) {
221 if (randint0(context->dam) > 200) {
222 if (!player_is_immune(player, ELEM_ACID)) {
223 int dam = context->dam / 5;
224 msg("The venom stings your skin!");
225 inven_damage(player, PROJ_ACID, dam);
226 xtra += adjust_dam(player, PROJ_ACID, dam, RANDOMISE,
227 player->state.el_info[PROJ_ACID].res_level,
228 true);
229 }
230 }
231 if (randint0(context->dam) > 200) {
232 msg("The stench sickens you.");
233 effect_simple(EF_DRAIN_STAT, source_none(), "0", STAT_CON, 0, 0, 0,
234 0, &context->obvious);
235 }
236 }
237 return xtra;
238 }
239
project_player_handler_LIGHT(project_player_handler_context_t * context)240 static int project_player_handler_LIGHT(project_player_handler_context_t *context)
241 {
242 if (player_resists(player, ELEM_LIGHT)) {
243 msg("You resist the effect!");
244 return 0;
245 }
246
247 (void)player_inc_timed(player, TMD_BLIND, 2 + randint1(5), true, true);
248
249 /* Confusion for strong unresisted light */
250 if (context->dam > 300) {
251 msg("You are dazzled!");
252 (void)player_inc_timed(player, TMD_CONFUSED,
253 2 + randint1(context->dam / 100), true, true);
254 }
255 return 0;
256 }
257
project_player_handler_DARK(project_player_handler_context_t * context)258 static int project_player_handler_DARK(project_player_handler_context_t *context)
259 {
260 if (player_resists(player, ELEM_DARK)) {
261 msg("You resist the effect!");
262 return 0;
263 }
264
265 (void)player_inc_timed(player, TMD_BLIND, 2 + randint1(5), true, true);
266
267 /* Unresisted dark from powerful monsters is bad news */
268 if (context->power >= 70) {
269 /* Life draining */
270 if (randint0(context->dam) > 100) {
271 if (player_of_has(player, OF_HOLD_LIFE)) {
272 equip_learn_flag(player, OF_HOLD_LIFE);
273 } else {
274 int drain = context->dam;
275 msg("The darkness steals your life force!");
276 player_exp_lose(player, drain, false);
277 }
278 }
279
280 /* Slowing */
281 if (randint0(context->dam) > 200) {
282 msg("You feel unsure of yourself in the darkness.");
283 (void)player_inc_timed(player, TMD_SLOW, context->dam / 100, true,
284 false);
285 }
286
287 /* Amnesia */
288 if (randint0(context->dam) > 300) {
289 msg("Darkness penetrates your mind!");
290 (void)player_inc_timed(player, TMD_AMNESIA, context->dam / 100,
291 true, false);
292 }
293 }
294 return 0;
295 }
296
project_player_handler_SOUND(project_player_handler_context_t * context)297 static int project_player_handler_SOUND(project_player_handler_context_t *context)
298 {
299 if (player_resists(player, ELEM_SOUND)) {
300 msg("You resist the effect!");
301 return 0;
302 }
303
304 /* Stun */
305 if (!player_of_has(player, OF_PROT_STUN)) {
306 int duration = 5 + randint1(context->dam / 3);
307 if (duration > 35) duration = 35;
308 (void)player_inc_timed(player, TMD_STUN, duration, true, true);
309 } else {
310 equip_learn_flag(player, OF_PROT_STUN);
311 }
312
313 /* Confusion for strong unresisted sound */
314 if (context->dam > 300) {
315 msg("The noise disorients you.");
316 (void)player_inc_timed(player, TMD_CONFUSED,
317 2 + randint1(context->dam / 100), true, true);
318 }
319 return 0;
320 }
321
project_player_handler_SHARD(project_player_handler_context_t * context)322 static int project_player_handler_SHARD(project_player_handler_context_t *context)
323 {
324 if (player_resists(player, ELEM_SHARD)) {
325 msg("You resist the effect!");
326 return 0;
327 }
328
329 /* Cuts */
330 (void)player_inc_timed(player, TMD_CUT, randint1(context->dam), true,
331 false);
332 return 0;
333 }
334
project_player_handler_NEXUS(project_player_handler_context_t * context)335 static int project_player_handler_NEXUS(project_player_handler_context_t *context)
336 {
337 struct monster *mon = NULL;
338 if (context->origin.what == SRC_MONSTER) {
339 mon = cave_monster(cave, context->origin.which.monster);
340 }
341
342 if (player_resists(player, ELEM_NEXUS)) {
343 msg("You resist the effect!");
344 return 0;
345 }
346
347 /* Stat swap */
348 if (randint0(100) < player->state.skills[SKILL_SAVE]) {
349 msg("You avoid the effect!");
350 } else {
351 player_inc_timed(player, TMD_SCRAMBLE, randint0(20) + 20, true, true);
352 }
353
354 if (one_in_(3) && mon) { /* Teleport to */
355 effect_simple(EF_TELEPORT_TO, context->origin, "0", 0, 0, 0,
356 mon->grid.y, mon->grid.x, NULL);
357 } else if (one_in_(4)) { /* Teleport level */
358 if (randint0(100) < player->state.skills[SKILL_SAVE]) {
359 msg("You avoid the effect!");
360 return 0;
361 }
362 effect_simple(EF_TELEPORT_LEVEL, context->origin, "0", 0, 0, 0, 0, 0,
363 NULL);
364 } else { /* Teleport */
365 const char *miles = "200";
366 effect_simple(EF_TELEPORT, context->origin, miles, 1, 0, 0, 0, 0, NULL);
367 }
368 return 0;
369 }
370
project_player_handler_NETHER(project_player_handler_context_t * context)371 static int project_player_handler_NETHER(project_player_handler_context_t *context)
372 {
373 int drain = 200 + (player->exp / 100) * z_info->life_drain_percent;
374
375 if (player_resists(player, ELEM_NETHER) ||
376 player_of_has(player, OF_HOLD_LIFE)) {
377 msg("You resist the effect!");
378 equip_learn_flag(player, OF_HOLD_LIFE);
379 return 0;
380 }
381
382 /* Life draining */
383 msg("You feel your life force draining away!");
384 player_exp_lose(player, drain, false);
385
386 /* Powerful nether attacks have further side-effects */
387 if (context->power >= 80) {
388 /* Mana loss */
389 if ((randint0(context->dam) > 100) && player->msp) {
390 msg("Your mind is dulled.");
391 player->csp -= MIN(player->csp, context->dam / 10);
392 player->upkeep->redraw |= PR_MANA;
393 }
394
395 /* Loss of energy */
396 if (randint0(context->dam) > 200) {
397 msg("Your energy is sapped!");
398 player->energy = 0;
399 }
400 }
401 return 0;
402 }
403
project_player_handler_CHAOS(project_player_handler_context_t * context)404 static int project_player_handler_CHAOS(project_player_handler_context_t *context)
405 {
406 if (player_resists(player, ELEM_CHAOS)) {
407 msg("You resist the effect!");
408 return 0;
409 }
410
411 /* Hallucination */
412 (void)player_inc_timed(player, TMD_IMAGE, randint1(10), true, false);
413
414 /* Confusion */
415 (void)player_inc_timed(player, TMD_CONFUSED, 10 + randint0(20), true, true);
416
417 /* Life draining */
418 if (!player_of_has(player, OF_HOLD_LIFE)) {
419 int drain = ((player->exp * 3)/ (100 * 2)) * z_info->life_drain_percent;
420 msg("You feel your life force draining away!");
421 player_exp_lose(player, drain, false);
422 } else {
423 equip_learn_flag(player, OF_HOLD_LIFE);
424 }
425 return 0;
426 }
427
project_player_handler_DISEN(project_player_handler_context_t * context)428 static int project_player_handler_DISEN(project_player_handler_context_t *context)
429 {
430 if (player_resists(player, ELEM_DISEN)) {
431 msg("You resist the effect!");
432 return 0;
433 }
434
435 /* Disenchant gear */
436 effect_simple(EF_DISENCHANT, context->origin, "0", 0, 0, 0, 0, 0, NULL);
437 return 0;
438 }
439
project_player_handler_WATER(project_player_handler_context_t * context)440 static int project_player_handler_WATER(project_player_handler_context_t *context)
441 {
442 /* Confusion */
443 (void)player_inc_timed(player, TMD_CONFUSED, 5 + randint1(5), true, true);
444
445 /* Stun */
446 (void)player_inc_timed(player, TMD_STUN, randint1(40), true, true);
447 return 0;
448 }
449
project_player_handler_ICE(project_player_handler_context_t * context)450 static int project_player_handler_ICE(project_player_handler_context_t *context)
451 {
452 if (!player_is_immune(player, ELEM_COLD))
453 inven_damage(player, PROJ_COLD, MIN(context->dam * 5, 300));
454
455 /* Cuts */
456 if (!player_resists(player, ELEM_SHARD))
457 (void)player_inc_timed(player, TMD_CUT, damroll(5, 8), true, false);
458 else
459 msg("You resist the effect!");
460
461 /* Stun */
462 (void)player_inc_timed(player, TMD_STUN, randint1(15), true, true);
463 return 0;
464 }
465
project_player_handler_GRAVITY(project_player_handler_context_t * context)466 static int project_player_handler_GRAVITY(project_player_handler_context_t *context)
467 {
468 msg("Gravity warps around you.");
469
470 /* Blink */
471 if (randint1(127) > player->lev) {
472 const char *five = "5";
473 effect_simple(EF_TELEPORT, context->origin, five, 1, 0, 0, 0, 0, NULL);
474 }
475
476 /* Slow */
477 (void)player_inc_timed(player, TMD_SLOW, 4 + randint0(4), true, false);
478
479 /* Stun */
480 if (!player_of_has(player, OF_PROT_STUN)) {
481 int duration = 5 + randint1(context->dam / 3);
482 if (duration > 35) duration = 35;
483 (void)player_inc_timed(player, TMD_STUN, duration, true, true);
484 } else {
485 equip_learn_flag(player, OF_PROT_STUN);
486 }
487 return 0;
488 }
489
project_player_handler_INERTIA(project_player_handler_context_t * context)490 static int project_player_handler_INERTIA(project_player_handler_context_t *context)
491 {
492 /* Slow */
493 (void)player_inc_timed(player, TMD_SLOW, 4 + randint0(4), true, false);
494 return 0;
495 }
496
project_player_handler_FORCE(project_player_handler_context_t * context)497 static int project_player_handler_FORCE(project_player_handler_context_t *context)
498 {
499 struct loc centre = origin_get_loc(context->origin);
500
501 /* Player gets pushed in a random direction if on the trap */
502 if (context->origin.what == SRC_TRAP && loc_eq(player->grid, centre)) {
503 int d = randint0(8);
504 centre = loc_sum(centre, ddgrid_ddd[d]);
505 }
506
507 /* Stun */
508 (void)player_inc_timed(player, TMD_STUN, randint1(20), true, true);
509
510 /* Thrust player away. */
511 thrust_away(centre, context->grid, 3 + context->dam / 20);
512 return 0;
513 }
514
project_player_handler_TIME(project_player_handler_context_t * context)515 static int project_player_handler_TIME(project_player_handler_context_t *context)
516 {
517 if (one_in_(2)) {
518 /* Life draining */
519 int drain = 100 + (player->exp / 100) * z_info->life_drain_percent;
520 msg("You feel your life force draining away!");
521 player_exp_lose(player, drain, false);
522 } else if (!one_in_(5)) {
523 /* Drain some stats */
524 project_player_drain_stats(2);
525 } else {
526 /* Drain all stats */
527 int i;
528 msg("You're not as powerful as you used to be...");
529
530 for (i = 0; i < STAT_MAX; i++)
531 player_stat_dec(player, i, false);
532 }
533 return 0;
534 }
535
project_player_handler_PLASMA(project_player_handler_context_t * context)536 static int project_player_handler_PLASMA(project_player_handler_context_t *context)
537 {
538 /* Stun */
539 if (!player_of_has(player, OF_PROT_STUN)) {
540 int duration = 5 + randint1(context->dam * 3 / 4);
541 if (duration > 35) duration = 35;
542 (void)player_inc_timed(player, TMD_STUN, duration, true, true);
543 } else {
544 equip_learn_flag(player, OF_PROT_STUN);
545 }
546 return 0;
547 }
548
project_player_handler_METEOR(project_player_handler_context_t * context)549 static int project_player_handler_METEOR(project_player_handler_context_t *context)
550 {
551 return 0;
552 }
553
project_player_handler_MISSILE(project_player_handler_context_t * context)554 static int project_player_handler_MISSILE(project_player_handler_context_t *context)
555 {
556 return 0;
557 }
558
project_player_handler_MANA(project_player_handler_context_t * context)559 static int project_player_handler_MANA(project_player_handler_context_t *context)
560 {
561 return 0;
562 }
563
project_player_handler_HOLY_ORB(project_player_handler_context_t * context)564 static int project_player_handler_HOLY_ORB(project_player_handler_context_t *context)
565 {
566 return 0;
567 }
568
project_player_handler_ARROW(project_player_handler_context_t * context)569 static int project_player_handler_ARROW(project_player_handler_context_t *context)
570 {
571 return 0;
572 }
573
project_player_handler_LIGHT_WEAK(project_player_handler_context_t * context)574 static int project_player_handler_LIGHT_WEAK(project_player_handler_context_t *context)
575 {
576 return 0;
577 }
578
project_player_handler_DARK_WEAK(project_player_handler_context_t * context)579 static int project_player_handler_DARK_WEAK(project_player_handler_context_t *context)
580 {
581 if (player_resists(player, ELEM_DARK)) {
582 if (!player_has(player, PF_UNLIGHT)) {
583 msg("You resist the effect!");
584 }
585 return 0;
586 }
587
588 (void)player_inc_timed(player, TMD_BLIND, 3 + randint1(5), true, true);
589 return 0;
590 }
591
project_player_handler_KILL_WALL(project_player_handler_context_t * context)592 static int project_player_handler_KILL_WALL(project_player_handler_context_t *context)
593 {
594 return 0;
595 }
596
project_player_handler_KILL_DOOR(project_player_handler_context_t * context)597 static int project_player_handler_KILL_DOOR(project_player_handler_context_t *context)
598 {
599 return 0;
600 }
601
project_player_handler_KILL_TRAP(project_player_handler_context_t * context)602 static int project_player_handler_KILL_TRAP(project_player_handler_context_t *context)
603 {
604 return 0;
605 }
606
project_player_handler_MAKE_DOOR(project_player_handler_context_t * context)607 static int project_player_handler_MAKE_DOOR(project_player_handler_context_t *context)
608 {
609 return 0;
610 }
611
project_player_handler_MAKE_TRAP(project_player_handler_context_t * context)612 static int project_player_handler_MAKE_TRAP(project_player_handler_context_t *context)
613 {
614 return 0;
615 }
616
project_player_handler_AWAY_UNDEAD(project_player_handler_context_t * context)617 static int project_player_handler_AWAY_UNDEAD(project_player_handler_context_t *context)
618 {
619 return 0;
620 }
621
project_player_handler_AWAY_EVIL(project_player_handler_context_t * context)622 static int project_player_handler_AWAY_EVIL(project_player_handler_context_t *context)
623 {
624 return 0;
625 }
626
project_player_handler_AWAY_SPIRIT(project_player_handler_context_t * context)627 static int project_player_handler_AWAY_SPIRIT(project_player_handler_context_t *context)
628 {
629 return 0;
630 }
631
project_player_handler_AWAY_ALL(project_player_handler_context_t * context)632 static int project_player_handler_AWAY_ALL(project_player_handler_context_t *context)
633 {
634 return 0;
635 }
636
project_player_handler_TURN_UNDEAD(project_player_handler_context_t * context)637 static int project_player_handler_TURN_UNDEAD(project_player_handler_context_t *context)
638 {
639 return 0;
640 }
641
project_player_handler_TURN_EVIL(project_player_handler_context_t * context)642 static int project_player_handler_TURN_EVIL(project_player_handler_context_t *context)
643 {
644 return 0;
645 }
646
project_player_handler_TURN_LIVING(project_player_handler_context_t * context)647 static int project_player_handler_TURN_LIVING(project_player_handler_context_t *context)
648 {
649 return 0;
650 }
651
project_player_handler_TURN_ALL(project_player_handler_context_t * context)652 static int project_player_handler_TURN_ALL(project_player_handler_context_t *context)
653 {
654 return 0;
655 }
656
project_player_handler_DISP_UNDEAD(project_player_handler_context_t * context)657 static int project_player_handler_DISP_UNDEAD(project_player_handler_context_t *context)
658 {
659 return 0;
660 }
661
project_player_handler_DISP_EVIL(project_player_handler_context_t * context)662 static int project_player_handler_DISP_EVIL(project_player_handler_context_t *context)
663 {
664 return 0;
665 }
666
project_player_handler_DISP_ALL(project_player_handler_context_t * context)667 static int project_player_handler_DISP_ALL(project_player_handler_context_t *context)
668 {
669 return 0;
670 }
671
project_player_handler_SLEEP_UNDEAD(project_player_handler_context_t * context)672 static int project_player_handler_SLEEP_UNDEAD(project_player_handler_context_t *context)
673 {
674 return 0;
675 }
676
project_player_handler_SLEEP_EVIL(project_player_handler_context_t * context)677 static int project_player_handler_SLEEP_EVIL(project_player_handler_context_t *context)
678 {
679 return 0;
680 }
681
project_player_handler_SLEEP_ALL(project_player_handler_context_t * context)682 static int project_player_handler_SLEEP_ALL(project_player_handler_context_t *context)
683 {
684 return 0;
685 }
686
project_player_handler_MON_CLONE(project_player_handler_context_t * context)687 static int project_player_handler_MON_CLONE(project_player_handler_context_t *context)
688 {
689 return 0;
690 }
691
project_player_handler_MON_POLY(project_player_handler_context_t * context)692 static int project_player_handler_MON_POLY(project_player_handler_context_t *context)
693 {
694 return 0;
695 }
696
project_player_handler_MON_HEAL(project_player_handler_context_t * context)697 static int project_player_handler_MON_HEAL(project_player_handler_context_t *context)
698 {
699 return 0;
700 }
701
project_player_handler_MON_SPEED(project_player_handler_context_t * context)702 static int project_player_handler_MON_SPEED(project_player_handler_context_t *context)
703 {
704 return 0;
705 }
706
project_player_handler_MON_SLOW(project_player_handler_context_t * context)707 static int project_player_handler_MON_SLOW(project_player_handler_context_t *context)
708 {
709 return 0;
710 }
711
project_player_handler_MON_CONF(project_player_handler_context_t * context)712 static int project_player_handler_MON_CONF(project_player_handler_context_t *context)
713 {
714 return 0;
715 }
716
project_player_handler_MON_HOLD(project_player_handler_context_t * context)717 static int project_player_handler_MON_HOLD(project_player_handler_context_t *context)
718 {
719 return 0;
720 }
721
project_player_handler_MON_STUN(project_player_handler_context_t * context)722 static int project_player_handler_MON_STUN(project_player_handler_context_t *context)
723 {
724 return 0;
725 }
726
project_player_handler_MON_DRAIN(project_player_handler_context_t * context)727 static int project_player_handler_MON_DRAIN(project_player_handler_context_t *context)
728 {
729 return 0;
730 }
731
project_player_handler_MON_CRUSH(project_player_handler_context_t * context)732 static int project_player_handler_MON_CRUSH(project_player_handler_context_t *context)
733 {
734 return 0;
735 }
736
737 static const project_player_handler_f player_handlers[] = {
738 #define ELEM(a) project_player_handler_##a,
739 #include "list-elements.h"
740 #undef ELEM
741 #define PROJ(a) project_player_handler_##a,
742 #include "list-projections.h"
743 #undef PROJ
744 NULL
745 };
746
747 /**
748 * Called from project() to affect the player
749 *
750 * Called for projections with the PROJECT_PLAY flag set, which includes
751 * bolt, beam, ball and breath effects.
752 *
753 * \param src is the origin of the effect
754 * \param r is the distance from the centre of the effect
755 * \param y the coordinates of the grid being handled
756 * \param x the coordinates of the grid being handled
757 * \param dam is the "damage" from the effect at distance r from the centre
758 * \param typ is the projection (PROJ_) type
759 * \return whether the effects were obvious
760 *
761 * If "r" is non-zero, then the blast was centered elsewhere; the damage
762 * is reduced in project() before being passed in here. This can happen if a
763 * monster breathes at the player and hits a wall instead.
764 *
765 * We assume the player is aware of some effect, and always return "true".
766 */
project_p(struct source origin,int r,struct loc grid,int dam,int typ,int power,bool self)767 bool project_p(struct source origin, int r, struct loc grid, int dam, int typ,
768 int power, bool self)
769 {
770 bool blind = (player->timed[TMD_BLIND] ? true : false);
771 bool seen = !blind;
772 bool obvious = true;
773
774 /* Monster or trap name (for damage) */
775 char killer[80];
776
777 project_player_handler_f player_handler = player_handlers[typ];
778 project_player_handler_context_t context = {
779 origin,
780 r,
781 grid,
782 dam,
783 typ,
784 power,
785 obvious
786 };
787 int res_level = typ < ELEM_MAX ? player->state.el_info[typ].res_level : 0;
788
789 /* Decoy has been hit */
790 if (square_isdecoyed(cave, grid) && context.dam) {
791 square_destroy_decoy(cave, grid);
792 }
793
794 /* No player here */
795 if (!square_isplayer(cave, grid)) {
796 return false;
797 }
798
799 switch (origin.what) {
800 case SRC_PLAYER: {
801 /* Don't affect projector unless explicitly allowed */
802 if (!self) return false;
803
804 break;
805 }
806
807 case SRC_MONSTER: {
808 struct monster *mon = cave_monster(cave, origin.which.monster);
809
810 /* Check it is visible */
811 if (!monster_is_visible(mon))
812 seen = false;
813
814 /* Get the monster's real name */
815 monster_desc(killer, sizeof(killer), mon, MDESC_DIED_FROM);
816
817 /* Monster sees what is going on */
818 update_smart_learn(mon, player, 0, 0, typ);
819
820 break;
821 }
822
823 case SRC_TRAP: {
824 struct trap *trap = origin.which.trap;
825
826 /* Get the trap name */
827 strnfmt(killer, sizeof(killer), "a %s", trap->kind->desc);
828
829 break;
830 }
831
832 case SRC_OBJECT: {
833 struct object *obj = origin.which.object;
834 object_desc(killer, sizeof(killer), obj, ODESC_PREFIX | ODESC_BASE);
835 break;
836 }
837
838 case SRC_CHEST_TRAP: {
839 struct chest_trap *trap = origin.which.chest_trap;
840
841 /* Get the trap name */
842 strnfmt(killer, sizeof(killer), "%s", trap->msg_death);
843
844 break;
845 }
846
847 case SRC_NONE: {
848 /* Assume the caller has set the killer variable */
849 break;
850 }
851 }
852
853 /* Let player know what is going on */
854 if (!seen) {
855 msg("You are hit by %s!", projections[typ].blind_desc);
856 }
857
858 /* Adjust damage for resistance, immunity or vulnerability, and apply it */
859 context.dam = adjust_dam(player,
860 typ,
861 context.dam,
862 RANDOMISE,
863 res_level,
864 true);
865 if (context.dam) {
866 /* Self-inflicted damage is scaled down */
867 if (self) {
868 context.dam /= 10;
869 }
870 take_hit(player, context.dam, killer);
871 }
872
873 /* Handle side effects, possibly including extra damage */
874 if (player_handler != NULL && player->is_dead == false) {
875 int xtra = player_handler(&context);
876 if (xtra) take_hit(player, xtra, killer);
877 }
878
879 /* Disturb */
880 disturb(player);
881
882 /* Return "Anything seen?" */
883 return context.obvious;
884 }
885