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