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