1 /**
2  * \file mon-timed.c
3  * \brief Monster timed effects.
4  *
5  * Copyright (c) 1997-2007 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 "mon-desc.h"
21 #include "mon-lore.h"
22 #include "mon-msg.h"
23 #include "mon-predicate.h"
24 #include "mon-spell.h"
25 #include "mon-timed.h"
26 #include "mon-util.h"
27 #include "player-calcs.h"
28 
29 /**
30  * The different ways increases can stack - see mon_inc_timed()
31  */
32 enum stack_type {
33 	STACK_NO,
34 	STACK_INCR,
35 	STACK_MAX
36 };
37 
38 /**
39  * Monster timed effects.
40  */
41 static struct mon_timed_effect {
42 	const char *name;
43 	bool gets_save;
44 	enum stack_type stacking;
45 	int flag_resist;
46 	int max_timer;
47 	int message_begin;
48 	int message_end;
49 	int message_increase;
50 } effects[] = {
51 	#define MON_TMD(a, b, c, d, e, f, g, h) { #a, b, STACK_##c, d, e, f, g, h },
52 	#include "list-mon-timed.h"
53 	#undef MON_TMD
54 };
55 
56 /**
57  * Find the timed monster effect with the name `name`.
58  *
59  * Returns -1 on failure.
60  */
mon_timed_name_to_idx(const char * name)61 int mon_timed_name_to_idx(const char *name)
62 {
63     for (size_t i = 0; i < MON_TMD_MAX; i++) {
64         if (streq(name, effects[i].name))
65             return i;
66     }
67 
68     return -1;
69 }
70 
71 /**
72  * Roll the saving throw for monsters resisting a timed effect.
73  */
saving_throw(const struct monster * mon,int effect_type,int timer,int flag)74 static bool saving_throw(const struct monster *mon, int effect_type, int timer, int flag)
75 {
76 	int resist_chance = MIN(
77 								90,
78 								mon->race->level + MAX(0, 25 - timer / 2)
79 						   );
80 
81 	/* Give unique monsters a double check */
82 	if (rf_has(mon->race->flags, RF_UNIQUE) &&
83 			(randint0(100) < resist_chance)) {
84 		return true;
85 	}
86 
87 	return randint0(100) < resist_chance;
88 }
89 
90 /**
91  * Determines whether the given monster successfully resists the given effect.
92  */
does_resist(const struct monster * mon,int effect_type,int timer,int flag)93 static bool does_resist(const struct monster *mon, int effect_type, int timer, int flag)
94 {
95 	assert(mon != NULL);
96 	assert(effect_type >= 0);
97 	assert(effect_type < MON_TMD_MAX);
98 
99 	struct mon_timed_effect *effect = &effects[effect_type];
100 	struct monster_lore *lore = get_lore(mon->race);
101 
102 	/* Sometimes the game can override the monster's innate resistance */
103 	if (flag & MON_TMD_FLG_NOFAIL) {
104 		return false;
105 	}
106 
107 	/* Check resistances from monster flags */
108 	if (rf_has(mon->race->flags, effect->flag_resist)) {
109 		lore_learn_flag_if_visible(lore, mon, effect->flag_resist);
110 		return true;
111 	}
112 
113 	/* Some effects get a saving throw; others don't */
114 	if (effect->gets_save == true) {
115 		return saving_throw(mon, effect_type, timer, flag);
116 	} else {
117 		return false;
118 	}
119 }
120 
121 /**
122  * Attempts to set the timer of the given monster effect to `timer`.
123  *
124  * Checks to see if the monster resists the effect, using does_resist().
125  * If not, the effect is set to `timer` turns. If `timer` is 0, or if the
126  * effect timer was 0, or if MON_TMD_FLG_NOTIFY is set in `flag`, then a
127  * message is printed, unless MON_TMD_FLG_NOMESSAGE is set in `flag`.
128  *
129  * Set a timed monster event to 'v'.  Give messages if the right flags are set.
130  * Check if the monster is able to resist the spell.  Mark the lore.
131  *
132  * Returns true if the monster was affected, false if not.
133  */
mon_set_timed(struct monster * mon,int effect_type,int timer,int flag)134 static bool mon_set_timed(struct monster *mon,
135 		int effect_type,
136 		int timer,
137 		int flag)
138 {
139 	assert(mon != NULL);
140 	assert(mon->race != NULL);
141 	assert(effect_type >= 0);
142 	assert(effect_type < MON_TMD_MAX);
143 	assert(timer >= 0);
144 
145 	struct mon_timed_effect *effect = &effects[effect_type];
146 
147 	bool check_resist;
148 	bool resisted = false;
149 	bool update = false;
150 
151 	int m_note = 0;
152 	int old_timer = mon->m_timed[effect_type];
153 
154 	/* Limit time of effect */
155 	if (timer > effect->max_timer) {
156 		timer = effect->max_timer;
157 	}
158 
159 	/* No change */
160 	if (old_timer == timer) {
161 		return false;
162 	} else if (timer == 0) {
163 		/* Turning off, usually mention */
164 		m_note = effect->message_end;
165 		flag |= MON_TMD_FLG_NOTIFY;
166 		check_resist = false;
167 	} else if (old_timer == 0) {
168 		/* Turning on, usually mention */
169 		m_note = effect->message_begin;
170 		flag |= MON_TMD_FLG_NOTIFY;
171 		check_resist = true;
172 	} else if (timer > old_timer) {
173 		/* Different message for increases, but don't automatically mention. */
174 		m_note = effect->message_increase;
175 		check_resist = true;
176 	} else {
177 		/* Decreases don't get a message, but never resist them */
178 		check_resist = false;
179 	}
180 
181 	/* Determine if the monster resisted or not, if appropriate */
182 	if (check_resist && does_resist(mon, effect_type, timer, flag)) {
183 		resisted = true;
184 		m_note = MON_MSG_UNAFFECTED;
185 	} else {
186 		mon->m_timed[effect_type] = timer;
187 		update = true;
188 	}
189 
190 	/* Special case - deal with monster shapechanges */
191 	if (effect_type == MON_TMD_CHANGED) {
192 		if (timer > old_timer) {
193 			if (!monster_change_shape(mon)) {
194 				m_note = MON_MSG_SHAPE_FAIL;
195 				mon->m_timed[effect_type] = old_timer;
196 			}
197 		} else if (timer == 0) {
198 			if (!monster_revert_shape(mon)) {
199 				quit ("Monster shapechange reversion failed!");
200 			}
201 		}
202 	}
203 
204 	/* Print a message if there is one, if the effect allows for it, and if
205 	 * either the monster is visible, or we're trying to ID something */
206 	if (m_note &&
207 		!(flag & MON_TMD_FLG_NOMESSAGE) &&
208 		(flag & MON_TMD_FLG_NOTIFY)
209 		&& monster_is_obvious(mon)) {
210 			add_monster_message(mon, m_note, true);
211 	}
212 
213 	/* Update the visuals, as appropriate. */
214 	if (update) {
215 		if (player->upkeep->health_who == mon)
216 			player->upkeep->redraw |= (PR_HEALTH);
217 
218 		player->upkeep->redraw |= (PR_MONLIST);
219 	}
220 
221 	return !resisted;
222 }
223 
224 /** Minimum number of turns a new timed effect can last */
225 #define MON_INC_MIN_TURNS		2
226 
227 /**
228  * Increases the timed effect `effect_type` by `timer`.
229  *
230  * Calculates the new timer, then passes that to mon_set_timed().
231  * Note that each effect has a maximum number of turns it can be active for.
232  * If this function would put an effect timer over that cap, it sets it for
233  * that cap instead.
234  *
235  * Returns true if the monster's timer changed.
236  */
mon_inc_timed(struct monster * mon,int effect_type,int timer,int flag)237 bool mon_inc_timed(struct monster *mon, int effect_type, int timer, int flag)
238 {
239 	assert(effect_type >= 0);
240 	assert(effect_type < MON_TMD_MAX);
241 	assert(timer > 0); /* For negative amounts, we use mon_dec_timed instead */
242 
243 	struct mon_timed_effect *effect = &effects[effect_type];
244 	int new_value = timer;
245 
246 	/* Make it last for a mimimum # of turns if it is a new effect */
247 	if (mon->m_timed[effect_type] == 0 && timer < MON_INC_MIN_TURNS) {
248 		timer = MON_INC_MIN_TURNS;
249 	}
250 
251 	/* Stack effects correctly */
252 	switch (effect->stacking) {
253 		case STACK_NO: {
254 			new_value = mon->m_timed[effect_type];
255 			if (new_value == 0) {
256 				new_value = timer;
257 			}
258 			break;
259 		}
260 
261 		case STACK_MAX: {
262 			new_value = MAX(mon->m_timed[effect_type], timer);
263 			break;
264 		}
265 
266 		case STACK_INCR: {
267 			new_value = mon->m_timed[effect_type] + timer;
268 			break;
269 		}
270 	}
271 
272 	return mon_set_timed(mon, effect_type, new_value, flag);
273 }
274 
275 /**
276  * Decreases the timed effect `effect_type` by `timer`.
277  *
278  * Calculates the new timer, then passes that to mon_set_timed().
279  * If a timer would be set to a negative number, it is set to 0 instead.
280  * Note that decreasing a timed effect should never fail.
281  *
282  * Returns true if the monster's timer changed.
283  */
mon_dec_timed(struct monster * mon,int effect_type,int timer,int flag)284 bool mon_dec_timed(struct monster *mon, int effect_type, int timer, int flag)
285 {
286 	assert(effect_type >= 0);
287 	assert(effect_type < MON_TMD_MAX);
288 	assert(timer > 0); /* For negative amounts, we use mon_inc_timed instead */
289 
290 	int new_level = mon->m_timed[effect_type] - timer;
291 	if (new_level < 0) {
292 		new_level = 0;
293 	}
294 
295 	return mon_set_timed(mon, effect_type, new_level, flag);
296 }
297 
298 /**
299  * Clears the timed effect `effect_type`.
300  *
301  * Returns true if the monster's timer was changed.
302  */
mon_clear_timed(struct monster * mon,int effect_type,int flag)303 bool mon_clear_timed(struct monster *mon, int effect_type, int flag)
304 {
305 	assert(effect_type >= 0);
306 	assert(effect_type < MON_TMD_MAX);
307 
308 	if (mon->m_timed[effect_type] == 0) {
309 		return false;
310 	} else {
311 		return mon_set_timed(mon, effect_type, 0, flag);
312 	}
313 }
314 
315 /**
316  * The level at which an effect is affecting a monster.
317  * Levels range from 0 (unaffected) to 5 (maximum effect).
318  */
monster_effect_level(struct monster * mon,int effect_type)319 int monster_effect_level(struct monster *mon, int effect_type)
320 {
321 	struct mon_timed_effect *effect = &effects[effect_type];
322 	int divisor = MAX(effect->max_timer / 5, 1);
323 	return MIN((mon->m_timed[effect_type] + divisor - 1) / divisor, 5);
324 }
325