1 /**
2 * \file project-obj.c
3 * \brief projection effects on objects
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 "mon-util.h"
22 #include "obj-chest.h"
23 #include "obj-desc.h"
24 #include "obj-gear.h"
25 #include "obj-ignore.h"
26 #include "obj-pile.h"
27 #include "obj-tval.h"
28 #include "obj-util.h"
29 #include "player-calcs.h"
30 #include "source.h"
31
32
33 /**
34 * Destroys a type of item on a given percent chance.
35 * The chance 'cperc' is in hundredths of a percent (1-in-10000)
36 * Note that missiles are no longer necessarily all destroyed
37 *
38 * Returns number of items destroyed.
39 */
inven_damage(struct player * p,int type,int cperc)40 int inven_damage(struct player *p, int type, int cperc)
41 {
42 int j, k, amt;
43 struct object *obj = p->gear;
44 char o_name[80];
45 bool damage;
46
47 /* No chance means no damage */
48 if (cperc <= 0)
49 return 0;
50
51 /* Count the casualties */
52 k = 0;
53
54 /* Scan through the gear */
55 while (obj) {
56 struct object *next = obj->next;
57 if (object_is_equipped(p->body, obj)) {
58 obj = next;
59 continue;
60 }
61
62 /* Hack -- for now, skip artifacts */
63 if (obj->artifact) {
64 obj = next;
65 continue;
66 }
67
68 /* Give this item slot a shot at death if it is vulnerable */
69 if ((obj->el_info[type].flags & EL_INFO_HATES) &&
70 !(obj->el_info[type].flags & EL_INFO_IGNORE)) {
71 /* Chance to destroy this item */
72 int chance = cperc;
73
74 /* Track if it is damaged instead of destroyed */
75 damage = false;
76
77 /* Analyze the type to see if we just damage it
78 * - we also check for rods to reduce chance */
79 if (tval_is_weapon(obj) && !tval_is_ammo(obj)) {
80 /* Chance to damage it */
81 if (randint0(10000) < cperc) {
82 /* Damage the item */
83 obj->to_h--;
84 if (p->obj_k->to_h)
85 obj->known->to_h = obj->to_h;
86 obj->to_d--;
87 if (p->obj_k->to_d)
88 obj->known->to_d = obj->to_d;
89
90 /* Damaged! */
91 damage = true;
92 } else {
93 obj = next;
94 continue;
95 }
96 } else if (tval_is_armor(obj)) {
97 /* Chance to damage it */
98 if (randint0(10000) < cperc) {
99 /* Damage the item */
100 obj->to_a--;
101 if (p->obj_k->to_a)
102 obj->known->to_a = obj->to_a;
103
104 /* Damaged! */
105 damage = true;
106 } else {
107 obj = next;
108 continue;
109 }
110 } else if (tval_is_rod(obj)) {
111 chance = (chance / 4);
112 }
113
114
115 /* Damage instead of destroy */
116 if (damage) {
117 p->upkeep->update |= (PU_BONUS);
118 p->upkeep->redraw |= (PR_EQUIP);
119
120 /* Casualty count */
121 amt = obj->number;
122 } else
123 /* ... or count the casualties */
124 for (amt = j = 0; j < obj->number; ++j)
125 if (randint0(10000) < chance) amt++;
126
127 /* Some casualities */
128 if (amt) {
129 struct object *destroyed;
130 bool none_left = false;
131
132 /* Get a description */
133 object_desc(o_name, sizeof(o_name), obj, ODESC_BASE);
134
135 /* Message */
136 msgt(MSG_DESTROY, "%sour %s (%c) %s %s!",
137 ((obj->number > 1) ?
138 ((amt == obj->number) ? "All of y" :
139 (amt > 1 ? "Some of y" : "One of y")) : "Y"),
140 o_name, gear_to_label(obj),
141 ((amt > 1) ? "were" : "was"),
142 (damage ? "damaged" : "destroyed"));
143
144 /* Damage already done? */
145 if (damage)
146 continue;
147
148 /* Destroy "amt" items */
149 destroyed = gear_object_for_use(obj, amt, false, &none_left);
150 if (destroyed->known)
151 object_delete(&destroyed->known);
152 object_delete(&destroyed);
153
154 /* Count the casualties */
155 k += amt;
156 }
157 }
158 obj = next;
159 }
160
161 /* Return the casualty count */
162 return (k);
163 }
164
165 /**
166 * ------------------------------------------------------------------------
167 * Object handlers
168 * ------------------------------------------------------------------------ */
169
170 typedef struct project_object_handler_context_s {
171 const struct source origin;
172 const int r;
173 const struct loc grid;
174 const int dam;
175 const int type;
176 const struct object *obj;
177 bool obvious;
178 bool do_kill;
179 bool ignore;
180 const char *note_kill;
181 } project_object_handler_context_t;
182 typedef void (*project_object_handler_f)(project_object_handler_context_t *);
183
184 /**
185 * Project an effect onto an object.
186 *
187 * \param context is the project_o context.
188 * \param element is for elements that will destroy an object, or that it will
189 * ignore.
190 * \param singular_verb is the verb that is displayed when one object is
191 * destroyed.
192 * \param plural_verb is the verb that is displayed in multiple objects are
193 * destroyed.
194 */
project_object_elemental(project_object_handler_context_t * context,int element,const char * singular_verb,const char * plural_verb)195 static void project_object_elemental(project_object_handler_context_t *context,
196 int element, const char *singular_verb,
197 const char *plural_verb)
198 {
199 if (context->obj->el_info[element].flags & EL_INFO_HATES) {
200 context->do_kill = true;
201 context->note_kill = VERB_AGREEMENT(context->obj->number,
202 singular_verb, plural_verb);
203 context->ignore = (context->obj->el_info[element].flags &
204 EL_INFO_IGNORE) ? true : false;
205 }
206 }
207
208 /* Acid -- Lots of things */
project_object_handler_ACID(project_object_handler_context_t * context)209 static void project_object_handler_ACID(project_object_handler_context_t *context)
210 {
211 project_object_elemental(context, ELEM_ACID, "melts", "melt");
212 }
213
214 /* Elec -- Rings and Wands */
project_object_handler_ELEC(project_object_handler_context_t * context)215 static void project_object_handler_ELEC(project_object_handler_context_t *context)
216 {
217 project_object_elemental(context, ELEM_ELEC, "is destroyed", "are destroyed");
218 }
219
220 /* Fire -- Flammable objects */
project_object_handler_FIRE(project_object_handler_context_t * context)221 static void project_object_handler_FIRE(project_object_handler_context_t *context)
222 {
223 project_object_elemental(context, ELEM_FIRE, "burns up", "burn up");
224 }
225
226 /* Cold -- potions and flasks */
project_object_handler_COLD(project_object_handler_context_t * context)227 static void project_object_handler_COLD(project_object_handler_context_t *context)
228 {
229 project_object_elemental(context, ELEM_COLD, "shatters", "shatter");
230 }
231
project_object_handler_POIS(project_object_handler_context_t * context)232 static void project_object_handler_POIS(project_object_handler_context_t *context)
233 {
234 }
235
project_object_handler_LIGHT(project_object_handler_context_t * context)236 static void project_object_handler_LIGHT(project_object_handler_context_t *context)
237 {
238 }
239
project_object_handler_DARK(project_object_handler_context_t * context)240 static void project_object_handler_DARK(project_object_handler_context_t *context)
241 {
242 }
243
244 /* Sound -- potions and flasks */
project_object_handler_SOUND(project_object_handler_context_t * context)245 static void project_object_handler_SOUND(project_object_handler_context_t *context)
246 {
247 project_object_elemental(context, ELEM_SOUND, "shatters", "shatter");
248 }
249
250 /* Shards -- potions and flasks */
project_object_handler_SHARD(project_object_handler_context_t * context)251 static void project_object_handler_SHARD(project_object_handler_context_t *context)
252 {
253 project_object_elemental(context, ELEM_SHARD, "shatters", "shatter");
254 }
255
project_object_handler_NEXUS(project_object_handler_context_t * context)256 static void project_object_handler_NEXUS(project_object_handler_context_t *context)
257 {
258 }
259
project_object_handler_NETHER(project_object_handler_context_t * context)260 static void project_object_handler_NETHER(project_object_handler_context_t *context)
261 {
262 }
263
project_object_handler_CHAOS(project_object_handler_context_t * context)264 static void project_object_handler_CHAOS(project_object_handler_context_t *context)
265 {
266 }
267
project_object_handler_DISEN(project_object_handler_context_t * context)268 static void project_object_handler_DISEN(project_object_handler_context_t *context)
269 {
270 }
271
project_object_handler_WATER(project_object_handler_context_t * context)272 static void project_object_handler_WATER(project_object_handler_context_t *context)
273 {
274 }
275
276 /* Ice -- potions and flasks */
project_object_handler_ICE(project_object_handler_context_t * context)277 static void project_object_handler_ICE(project_object_handler_context_t *context)
278 {
279 project_object_elemental(context, ELEM_ICE, "shatters", "shatter");
280 }
281
project_object_handler_GRAVITY(project_object_handler_context_t * context)282 static void project_object_handler_GRAVITY(project_object_handler_context_t *context)
283 {
284 }
285
project_object_handler_INERTIA(project_object_handler_context_t * context)286 static void project_object_handler_INERTIA(project_object_handler_context_t *context)
287 {
288 }
289
290 /* Force -- potions and flasks */
project_object_handler_FORCE(project_object_handler_context_t * context)291 static void project_object_handler_FORCE(project_object_handler_context_t *context)
292 {
293 project_object_elemental(context, ELEM_FORCE, "shatters", "shatter");
294 }
295
project_object_handler_TIME(project_object_handler_context_t * context)296 static void project_object_handler_TIME(project_object_handler_context_t *context)
297 {
298 }
299
300 /* Fire + Elec */
project_object_handler_PLASMA(project_object_handler_context_t * context)301 static void project_object_handler_PLASMA(project_object_handler_context_t *context)
302 {
303 project_object_elemental(context, ELEM_FIRE, "burns up", "burn up");
304 project_object_elemental(context, ELEM_ELEC, "is destroyed", "are destroyed");
305 }
306
307 /* Fire + Cold */
project_object_handler_METEOR(project_object_handler_context_t * context)308 static void project_object_handler_METEOR(project_object_handler_context_t *context)
309 {
310 project_object_elemental(context, ELEM_FIRE, "burns up", "burn up");
311 project_object_elemental(context, ELEM_COLD, "shatters", "shatter");
312 }
313
project_object_handler_MISSILE(project_object_handler_context_t * context)314 static void project_object_handler_MISSILE(project_object_handler_context_t *context)
315 {
316 }
317
318 /* Mana -- destroys everything */
project_object_handler_MANA(project_object_handler_context_t * context)319 static void project_object_handler_MANA(project_object_handler_context_t *context)
320 {
321 context->do_kill = true;
322 context->note_kill = VERB_AGREEMENT(context->obj->number, "is destroyed", "are destroyed");
323 }
324
325 /* Holy Orb -- destroys cursed non-artifacts */
project_object_handler_HOLY_ORB(project_object_handler_context_t * context)326 static void project_object_handler_HOLY_ORB(project_object_handler_context_t *context)
327 {
328 }
329
project_object_handler_ARROW(project_object_handler_context_t * context)330 static void project_object_handler_ARROW(project_object_handler_context_t *context)
331 {
332 }
333
project_object_handler_LIGHT_WEAK(project_object_handler_context_t * context)334 static void project_object_handler_LIGHT_WEAK(project_object_handler_context_t *context)
335 {
336 }
337
project_object_handler_DARK_WEAK(project_object_handler_context_t * context)338 static void project_object_handler_DARK_WEAK(project_object_handler_context_t *context)
339 {
340 }
341
project_object_handler_KILL_WALL(project_object_handler_context_t * context)342 static void project_object_handler_KILL_WALL(project_object_handler_context_t *context)
343 {
344 }
345
project_object_handler_KILL_DOOR(project_object_handler_context_t * context)346 static void project_object_handler_KILL_DOOR(project_object_handler_context_t *context)
347 {
348 }
349
350 /* Unlock chests */
project_object_handler_KILL_TRAP(project_object_handler_context_t * context)351 static void project_object_handler_KILL_TRAP(project_object_handler_context_t *context)
352 {
353 /* Chests are noticed only if trapped or locked */
354 if (is_locked_chest(context->obj)) {
355 /* Disarm or Unlock */
356 unlock_chest((struct object * const)context->obj);
357
358 /* Notice */
359 if (context->obj->known && !ignore_item_ok(context->obj)) {
360 context->obj->known->pval = context->obj->pval;
361 msg("Click!");
362 context->obvious = true;
363 }
364 }
365 }
366
project_object_handler_MAKE_DOOR(project_object_handler_context_t * context)367 static void project_object_handler_MAKE_DOOR(project_object_handler_context_t *context)
368 {
369 }
370
project_object_handler_MAKE_TRAP(project_object_handler_context_t * context)371 static void project_object_handler_MAKE_TRAP(project_object_handler_context_t *context)
372 {
373 }
374
project_object_handler_AWAY_UNDEAD(project_object_handler_context_t * context)375 static void project_object_handler_AWAY_UNDEAD(project_object_handler_context_t *context)
376 {
377 }
378
project_object_handler_AWAY_EVIL(project_object_handler_context_t * context)379 static void project_object_handler_AWAY_EVIL(project_object_handler_context_t *context)
380 {
381 }
382
project_object_handler_AWAY_SPIRIT(project_object_handler_context_t * context)383 static void project_object_handler_AWAY_SPIRIT(project_object_handler_context_t *context)
384 {
385 }
386
project_object_handler_AWAY_ALL(project_object_handler_context_t * context)387 static void project_object_handler_AWAY_ALL(project_object_handler_context_t *context)
388 {
389 }
390
project_object_handler_TURN_UNDEAD(project_object_handler_context_t * context)391 static void project_object_handler_TURN_UNDEAD(project_object_handler_context_t *context)
392 {
393 }
394
project_object_handler_TURN_EVIL(project_object_handler_context_t * context)395 static void project_object_handler_TURN_EVIL(project_object_handler_context_t *context)
396 {
397 }
398
project_object_handler_TURN_LIVING(project_object_handler_context_t * context)399 static void project_object_handler_TURN_LIVING(project_object_handler_context_t *context)
400 {
401 }
402
project_object_handler_TURN_ALL(project_object_handler_context_t * context)403 static void project_object_handler_TURN_ALL(project_object_handler_context_t *context)
404 {
405 }
406
project_object_handler_DISP_UNDEAD(project_object_handler_context_t * context)407 static void project_object_handler_DISP_UNDEAD(project_object_handler_context_t *context)
408 {
409 }
410
project_object_handler_DISP_EVIL(project_object_handler_context_t * context)411 static void project_object_handler_DISP_EVIL(project_object_handler_context_t *context)
412 {
413 }
414
project_object_handler_DISP_ALL(project_object_handler_context_t * context)415 static void project_object_handler_DISP_ALL(project_object_handler_context_t *context)
416 {
417 }
418
project_object_handler_SLEEP_UNDEAD(project_object_handler_context_t * context)419 static void project_object_handler_SLEEP_UNDEAD(project_object_handler_context_t *context)
420 {
421 }
422
project_object_handler_SLEEP_EVIL(project_object_handler_context_t * context)423 static void project_object_handler_SLEEP_EVIL(project_object_handler_context_t *context)
424 {
425 }
426
project_object_handler_SLEEP_ALL(project_object_handler_context_t * context)427 static void project_object_handler_SLEEP_ALL(project_object_handler_context_t *context)
428 {
429 }
430
project_object_handler_MON_CLONE(project_object_handler_context_t * context)431 static void project_object_handler_MON_CLONE(project_object_handler_context_t *context)
432 {
433 }
434
project_object_handler_MON_POLY(project_object_handler_context_t * context)435 static void project_object_handler_MON_POLY(project_object_handler_context_t *context)
436 {
437 }
438
project_object_handler_MON_HEAL(project_object_handler_context_t * context)439 static void project_object_handler_MON_HEAL(project_object_handler_context_t *context)
440 {
441 }
442
project_object_handler_MON_SPEED(project_object_handler_context_t * context)443 static void project_object_handler_MON_SPEED(project_object_handler_context_t *context)
444 {
445 }
446
project_object_handler_MON_SLOW(project_object_handler_context_t * context)447 static void project_object_handler_MON_SLOW(project_object_handler_context_t *context)
448 {
449 }
450
project_object_handler_MON_CONF(project_object_handler_context_t * context)451 static void project_object_handler_MON_CONF(project_object_handler_context_t *context)
452 {
453 }
454
project_object_handler_MON_HOLD(project_object_handler_context_t * context)455 static void project_object_handler_MON_HOLD(project_object_handler_context_t *context)
456 {
457 }
458
project_object_handler_MON_STUN(project_object_handler_context_t * context)459 static void project_object_handler_MON_STUN(project_object_handler_context_t *context)
460 {
461 }
462
project_object_handler_MON_DRAIN(project_object_handler_context_t * context)463 static void project_object_handler_MON_DRAIN(project_object_handler_context_t *context)
464 {
465 }
466
project_object_handler_MON_CRUSH(project_object_handler_context_t * context)467 static void project_object_handler_MON_CRUSH(project_object_handler_context_t *context)
468 {
469 }
470
471 static const project_object_handler_f object_handlers[] = {
472 #define ELEM(a) project_object_handler_##a,
473 #include "list-elements.h"
474 #undef ELEM
475 #define PROJ(a) project_object_handler_##a,
476 #include "list-projections.h"
477 #undef PROJ
478 NULL
479 };
480
481 /**
482 * Called from project() to affect objects
483 *
484 * Called for projections with the PROJECT_ITEM flag set, which includes
485 * beam, ball and breath effects.
486 *
487 * \param origin is the origin of the effect
488 * \param r is the distance from the centre of the effect
489 * \param y the coordinates of the grid being handled
490 * \param x the coordinates of the grid being handled
491 * \param dam is the "damage" from the effect at distance r from the centre
492 * \param typ is the projection (PROJ_) type
493 * \param protected_obj is an object that should not be affected by the
494 * projection, typically the object that created it
495 * \return whether the effects were obvious
496 *
497 * Note that this function determines if the player can see anything that
498 * happens by taking into account: blindness, line-of-sight, and illumination.
499 *
500 * Hack -- effects on objects which are memorized but not in view are also seen.
501 */
project_o(struct source origin,int r,struct loc grid,int dam,int typ,const struct object * protected_obj)502 bool project_o(struct source origin, int r, struct loc grid, int dam, int typ,
503 const struct object *protected_obj)
504 {
505 struct object *obj = square_object(cave, grid);
506 bool obvious = false;
507
508 /* Scan all objects in the grid */
509 while (obj) {
510 bool ignore = false;
511 bool do_kill = false;
512 const char *note_kill = NULL;
513 struct object *next = obj->next;
514
515 project_object_handler_f object_handler = object_handlers[typ];
516 project_object_handler_context_t context = {
517 origin,
518 r,
519 grid,
520 dam,
521 typ,
522 obj,
523 obvious,
524 do_kill,
525 ignore,
526 note_kill,
527 };
528
529 if (object_handler != NULL)
530 object_handler(&context);
531
532 obvious = context.obvious;
533 do_kill = context.do_kill && (obj != protected_obj);
534 ignore = context.ignore;
535 note_kill = context.note_kill;
536
537 /* Attempt to destroy the object */
538 if (do_kill) {
539 char o_name[80];
540
541 /* Effect observed */
542 if (obj->known && !ignore_item_ok(obj) &&
543 square_isseen(cave, grid)) {
544 obvious = true;
545 object_desc(o_name, sizeof(o_name), obj, ODESC_BASE);
546 }
547
548 /* Artifacts, and other objects, get to resist */
549 if (obj->artifact || ignore) {
550 /* Observe the resist */
551 if (obvious && obj->known && !ignore_item_ok(obj))
552 msg("The %s %s unaffected!", o_name,
553 VERB_AGREEMENT(obj->number, "is", "are"));
554 } else if (obj->mimicking_m_idx) {
555 /* Reveal mimics */
556 if (obvious)
557 become_aware(cave_monster(cave, obj->mimicking_m_idx));
558 } else {
559 /* Describe if needed */
560 if (obvious && obj->known && note_kill && !ignore_item_ok(obj))
561 msgt(MSG_DESTROY, "The %s %s!", o_name, note_kill);
562
563 /* Delete the object */
564 square_delete_object(cave, grid, obj, true, true);
565 }
566 }
567
568 /* Next object */
569 obj = next;
570 }
571
572 /* Return "Anything seen?" */
573 return (obvious);
574 }
575