1 /*
2  * monsters.c
3  * Copyright (C) 2009-2020 Joachim de Groot <jdegroot@web.de>
4  *
5  * NLarn is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * NLarn is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <glib.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include "display.h"
24 #include "fov.h"
25 #include "game.h"
26 #include "items.h"
27 #include "map.h"
28 #include "monsters.h"
29 #include "extdefs.h"
30 #include "pathfinding.h"
31 #include "random.h"
32 
33 DEFINE_ENUM(monster_flag, MONSTER_FLAG_ENUM)
34 DEFINE_ENUM(monster_t, MONSTER_TYPE_ENUM)
35 
36 /* local monster definition for data storage */
37 typedef struct {
38     const char *name;        /* monster's name */
39     const char *plural_name;
40     const char glyph;
41     int colour;
42     int exp;                 /* xp granted to player */
43     int gold_chance;
44     int gold;
45     int reroll_chance;
46     int ac;
47     int hp_max;
48     int level;
49     int intelligence;        /* used to choose movement */
50     speed speed;
51     size size;
52     int flags;
53     attack attacks[2];
54     monster_action_t default_ai;
55     const char *sound;
56 } monster_data_t;
57 
58 /* monster information hiding */
59 struct _monster
60 {
61     monster_t type;
62     gpointer oid;            /* monsters id inside the monster hash */
63     gint32 hp_max;
64     gint32 hp;
65     position pos;
66     fov *fv;
67     int movement;
68     monster_action_t action; /* current action */
69     guint32 lastseen;        /* number of turns since when player was last seen; 0 = never */
70     position player_pos;     /* last known position of player */
71     inventory *inv;
72     item *eq_weapon;
73     GPtrArray *effects;
74     guint number;        /* random value for some monsters */
75     gpointer leader;    /* for pack monsters: ID of the leader */
76     guint32
77         unknown: 1;      /* monster is unknown (mimic) */
78 };
79 
80 const char *monster_ai_desc[] =
81 {
82     NULL,               /* MA_NONE */
83     "fleeing",          /* MA_FLEE */
84     "idling",           /* MA_REMAIN */
85     "wandering",        /* MA_WANDER */
86     "attacking",        /* MA_ATTACK */
87     "puzzled",          /* MA_CONFUSION */
88     "obedient",         /* MA_SERVE */
89     "working",          /* MA_CIVILIAN */
90 };
91 
92 const char *monster_attack_verb[] =
93 {
94     NULL,
95     "hits",         /* ATT_WEAPON */
96     "points at",    /* ATT_MAGIC */
97     "claws",        /* ATT_CLAW */
98     "bites",        /* ATT_BITE */
99     "stings",       /* ATT_STING */
100     "slams",        /* ATT_SLAM */
101     "kicks",        /* ATT_KICK */
102     "touches",      /* ATT_TOUCH */
103     "breathes at",  /* ATT_BREATH */
104     "gazes at",     /* ATT_GAZE */
105 };
106 
107 static struct _monster_breath_data
108 {
109     const char *desc;
110     const char glyph;
111     int colour;
112 } monster_breath_data[] =
113 {
114     { NULL, 0, 0 },                                 /* DAM_NONE */
115     { NULL, 0, 0 },                                 /* DAM_PHYSICAL */
116     { "psionic blast", '*', WHITE },             /* DAM_MAGICAL */
117     { "burst of fire", '~', RED },               /* DAM_FIRE */
118     { "beam of frost", '*', LIGHTCYAN },         /* DAM_COLD */
119     { "gush of acid", '*', LIGHTGREEN },         /* DAM_ACID */
120     { "flood of water", '~', BLUE },             /* DAM_WATER */
121     { "ray of lightning", '*', YELLOW },         /* DAM_ELECTRICITY */
122     { "burst of noxious fumes", '%', GREEN },    /* DAM_POISON */
123 };
124 
125 monster_data_t monster_data[] = {
126     { /* MT_GIANT_BAT */
127         .name = "giant bat", .glyph = 'b', .colour = RED,
128         .exp = 1, .ac = 0, .hp_max = 2,
129         .level = 1, .intelligence = 3, .speed = XFAST, .size = SMALL,
130         .flags = HEAD | FLY | INFRAVISION,
131         .attacks = {
132             { .type = ATT_BITE, .base = 1, .damage = DAM_PHYSICAL },
133         }, .default_ai = MA_WANDER
134     },
135     { /* MT_GNOME */
136         .name = "gnome", .glyph = 'g', .colour = BROWN,
137         .exp = 2, .gold_chance = 80, .gold = 30, .ac = 0, .hp_max = 6,
138         .level = 1, .intelligence = 8, .speed = NORMAL, .size = SMALL,
139         .flags = HEAD | HANDS,
140         .attacks = {
141             { .type = ATT_WEAPON, .damage = DAM_PHYSICAL },
142         }, .default_ai = MA_WANDER
143     },
144     { /* MT_HOBGOBLIN */
145         .name = "hobgoblin", .glyph = 'H', .colour = BROWN,
146         .exp = 2, .gold_chance = 30, .gold = 40, .ac = 1, .hp_max = 8,
147         .level = 1, .intelligence = 5, .speed = SLOW, .size = MEDIUM,
148         .flags = HEAD | HANDS | INFRAVISION,
149         .attacks = {
150             { .type = ATT_WEAPON, .damage = DAM_PHYSICAL },
151         }, .default_ai = MA_WANDER
152     },
153     { /* MT_JACKAL */
154         .name = "jackal", .glyph = 'J', .colour = BROWN,
155         .exp = 1, .ac = 0, .hp_max = 3,
156         .level = 1, .intelligence = 4, .speed = FAST, .size = SMALL,
157         .flags = HEAD,
158         .attacks = {
159             { .type = ATT_BITE, .base = 1, .damage = DAM_PHYSICAL },
160         }, .default_ai = MA_WANDER, .sound = "growl"
161     },
162     { /* MT_KOBOLD */
163         .name = "kobold", .glyph = 'k', .colour = BROWN,
164         .exp = 1, .gold_chance = 10, .gold = 100, .ac = 0, .hp_max = 4,
165         .level = 1, .intelligence = 7, .speed = NORMAL, .size = SMALL,
166         .flags = HEAD | HANDS | INFRAVISION,
167         .attacks = {
168             { .type = ATT_WEAPON, .damage = DAM_PHYSICAL },
169         }, .default_ai = MA_WANDER
170     },
171     { /* MT_ORC */
172         .name = "orc", .glyph = 'O', .colour = RED,
173         .exp = 2, .gold_chance = 50, .gold = 80, .ac = 3, .hp_max = 12,
174         .level = 2, .intelligence = 9, .speed = NORMAL, .size = MEDIUM,
175         .flags = HEAD | HANDS | INFRAVISION | PACK,
176         .attacks = {
177             { .type = ATT_WEAPON, .damage = DAM_PHYSICAL },
178         }, .default_ai = MA_WANDER, .sound = "shout"
179     },
180     { /* MT_SNAKE */
181         .name = "snake", .glyph = 'S', .colour = LIGHTGREEN,
182         .exp = 1, .ac = 1, .hp_max = 3,
183         .level = 2, .intelligence = 3, .speed = NORMAL, .size = TINY,
184         .flags = HEAD,
185         .attacks = {
186             { .type = ATT_BITE, .base = 1, .damage = DAM_PHYSICAL },
187             { .type = ATT_BITE, .base = 2, .damage = DAM_POISON },
188         }, .default_ai = MA_WANDER, .sound = "hiss"
189     },
190     { /* MT_CENTIPEDE */
191         .name = "giant centipede", .glyph = 'c', .colour = YELLOW,
192         .exp = 2, .ac = 1, .hp_max = 5,
193         .level = 2, .intelligence = 2, .speed = NORMAL, .size = SMALL,
194         .flags = HEAD,
195         .attacks = {
196             { .type = ATT_BITE, .base = 50, .damage = DAM_DEC_STR },
197             { .type = ATT_BITE, .base = 1, .damage = DAM_PHYSICAL },
198         }, .default_ai = MA_WANDER
199     },
200     { /* MT_JACULUS */
201         .name = "jaculus", .plural_name = "jaculi", .glyph = 'j', .colour = GREEN,
202         .exp = 1, .ac = 3, .hp_max = 8,
203         .level = 2, .intelligence = 3, .speed = XFAST, .size = MEDIUM,
204         .flags = HEAD | FLY,
205         .attacks = {
206             { .type = ATT_BITE, .base = 2, .damage = DAM_PHYSICAL },
207             { .type = ATT_CLAW, .base = 2, .damage = DAM_PHYSICAL },
208         }, .default_ai = MA_WANDER
209     },
210     { /* MT_TROGLODYTE */
211         .name = "troglodyte", .glyph = 't', .colour = BROWN,
212         .exp = 3, .gold_chance = 25, .gold = 320, .ac = 4, .hp_max = 10,
213         .level = 2, .intelligence = 5, .speed = NORMAL, .size = MEDIUM,
214         .flags = HEAD | HANDS,
215         .attacks = {
216             { .type = ATT_WEAPON, .damage = DAM_PHYSICAL },
217         }, .default_ai = MA_WANDER
218     },
219     { /* MT_GIANT_ANT */
220         .name = "giant ant", .glyph = 'A', .colour = BROWN,
221         .exp = 5, .ac = 2, .hp_max = 6,
222         .level = 2, .intelligence = 3, .speed = NORMAL, .size = SMALL,
223         .flags = HEAD | PACK,
224         .attacks = {
225             { .type = ATT_BITE, .base = 75, .damage = DAM_DEC_STR },
226             { .type = ATT_BITE, .base = 1, .damage = DAM_PHYSICAL },
227         }, .default_ai = MA_WANDER
228     },
229     { /* MT_FLOATING_EYE */
230         .name = "floating eye", .glyph = 'E', .colour = BLUE,
231         .exp = 2, .ac = 2, .hp_max = 12,
232         .level = 3, .intelligence = 3, .speed = XSLOW, .size = MEDIUM,
233         .flags = FLY | INFRAVISION | RES_CONF,
234         .attacks = {
235             { .type = ATT_GAZE, .base = 66, .damage = DAM_PARALYSIS },
236         }, .default_ai = MA_WANDER
237     },
238     { /* MT_LEPRECHAUN */
239         .name = "leprechaun", .glyph = 'L', .colour = GREEN,
240         .exp = 45, .gold = 1500, .ac = 6, .hp_max = 13,
241         .level = 3, .intelligence = 6, .speed = NORMAL, .size = SMALL,
242         .flags = HEAD | HANDS,
243         .attacks = {
244             { .type = ATT_CLAW, .base = 2, .damage = DAM_PHYSICAL },
245             { .type = ATT_TOUCH, .damage = DAM_STEAL_GOLD },
246         }, .default_ai = MA_WANDER
247     },
248     { /* MT_NYMPH */
249         .name = "nymph", .glyph = 'n', .colour = RED,
250         .exp = 45, .ac = 5, .hp_max = 18,
251         .level = 3, .intelligence = 9, .speed = NORMAL, .size = MEDIUM,
252         .flags = HEAD | HANDS,
253         .attacks = {
254             { .type = ATT_TOUCH, .damage = DAM_STEAL_ITEM },
255         }, .default_ai = MA_WANDER
256     },
257     { /* MT_QUASIT */
258         .name = "quasit", .glyph = 'Q', .colour = BLUE,
259         .exp = 15, .ac = 6, .hp_max = 10,
260         .level = 3, .intelligence = 3, .speed = FAST, .size = SMALL,
261         .flags = HEAD | HANDS | DEMON,
262         .attacks = {
263             { .type = ATT_BITE, .base = 3, .damage = DAM_PHYSICAL },
264             { .type = ATT_CLAW, .base = 66, .damage = DAM_DEC_DEX },
265         }, .default_ai = MA_WANDER
266     },
267     { /* MT_RUST_MONSTER */
268         .name = "rust monster", .glyph = 'R', .colour = BROWN,
269         .exp = 25, .ac = 6, .hp_max = 18,
270         .level = 3, .intelligence = 3, .speed = NORMAL, .size = MEDIUM,
271         .flags = HEAD | METALLIVORE,
272         .attacks = {
273             { .type = ATT_BITE, .base = 3, .damage = DAM_PHYSICAL },
274             { .type = ATT_TOUCH, .base = 1, .damage = DAM_RUST },
275         }, .default_ai = MA_WANDER
276     },
277     { /* MT_ZOMBIE */
278         .name = "zombie", .glyph = 'Z', .colour = LIGHTGRAY,
279         .exp = 7, .ac = 2, .hp_max = 12,
280         .level = 3, .intelligence = 3, .speed = VSLOW, .size = MEDIUM,
281         .flags = HEAD | HANDS | UNDEAD | RES_SLEEP | RES_POISON | RES_CONF,
282         .attacks = {
283             { .type = ATT_BITE, .base = 2, .damage = DAM_PHYSICAL },
284             { .type = ATT_CLAW, .base = 2, .damage = DAM_PHYSICAL },
285         }, .default_ai = MA_WANDER, .sound = "groan"
286     },
287     { /* MT_ASSASSIN_BUG */
288         .name = "assassin bug", .glyph = 'a', .colour = GREEN,
289         .exp = 15, .ac = 1, .hp_max = 20,
290         .level = 4, .intelligence = 3, .speed = FAST, .size = TINY,
291         .flags = HEAD | RES_POISON,
292         .attacks = {
293             { .type = ATT_BITE, .base = 3, .damage = DAM_PHYSICAL },
294             { .type = ATT_BITE, .base = 3, .damage = DAM_POISON },
295         }, .default_ai = MA_WANDER
296     },
297     { /* MT_BUGBEAR */
298         .name = "bugbear", .glyph = 'B', .colour = BROWN,
299         .exp = 35, .gold_chance = 10, .gold = 400, .ac = 5, .hp_max = 20,
300         .level = 4, .intelligence = 5, .speed = SLOW, .size = MEDIUM,
301         .flags = HEAD | HANDS | INFRAVISION,
302         .attacks = {
303             { .type = ATT_BITE, .base = 5, .damage = DAM_PHYSICAL, .rand = 10 },
304             { .type = ATT_WEAPON, .damage = DAM_PHYSICAL },
305         }, .default_ai = MA_WANDER
306     },
307     { /* MT_HELLHOUND */
308         .name = "hell hound", .glyph = 'h', .colour = LIGHTRED,
309         .exp = 35, .ac = 5, .hp_max = 16,
310         .level = 4, .intelligence = 6, .speed = FAST, .size = SMALL,
311         .flags = HEAD | RES_FIRE | RES_MAGIC,
312         .attacks = {
313             { .type = ATT_BITE, .base = 2, .damage = DAM_PHYSICAL },
314             { .type = ATT_BREATH, .base = 8, .damage = DAM_FIRE, .rand = 15 },
315         }, .default_ai = MA_WANDER, .sound = "bark"
316     },
317     { /* MT_ICE_LIZARD */
318         .name = "ice lizard", .glyph = 'i', .colour = LIGHTCYAN,
319         .exp = 25, .ac = 4, .hp_max = 20,
320         .level = 4, .intelligence = 6, .speed = SLOW, .size = MEDIUM,
321         .flags = HEAD | SWIM | RES_COLD,
322         .attacks = {
323             { .type = ATT_CLAW, .base = 2, .damage = DAM_PHYSICAL },
324             { .type = ATT_SLAM, .base = 14, .damage = DAM_PHYSICAL },
325         }, .default_ai = MA_WANDER
326     },
327     { /* MT_CENTAUR */
328         .name = "centaur", .glyph = 'C', .colour = BROWN,
329         .exp = 45, .gold_chance = 50, .gold = 80, .ac = 6, .hp_max = 24,
330         .level = 4, .intelligence = 10, .speed = FAST, .size = LARGE,
331         .flags = HEAD | HANDS | PACK,
332         .attacks = {
333             { .type = ATT_KICK, .base = 6, .damage = DAM_PHYSICAL },
334             { .type = ATT_WEAPON, .damage = DAM_PHYSICAL },
335         }, .default_ai = MA_WANDER
336     },
337     { /* MT_TROLL */
338         .name = "troll", .glyph = 'T', .colour = BROWN,
339         .exp = 300, .gold_chance = 20, .gold = 400, .ac = 8, .hp_max = 50,
340         .level = 5, .intelligence = 9, .speed = SLOW, .size = LARGE,
341         .flags = HEAD | HANDS | REGENERATE,
342         .attacks = {
343             { .type = ATT_CLAW, .base = 5, .damage = DAM_PHYSICAL },
344             { .type = ATT_WEAPON, .damage = DAM_PHYSICAL },
345         }, .default_ai = MA_WANDER, .sound = "grunt"
346     },
347     { /* MT_YETI */
348         .name = "yeti", .glyph = 'Y', .colour = WHITE,
349         .exp = 100, .gold_chance = 10, .gold = 200, .ac = 4, .hp_max = 35,
350         .level = 5, .intelligence = 5, .speed = NORMAL, .size = LARGE,
351         .flags = HEAD | HANDS | RES_COLD,
352         .attacks = {
353             { .type = ATT_CLAW, .base = 4, .damage = DAM_PHYSICAL },
354         }, .default_ai = MA_WANDER
355     },
356     { /* MT_ELF */
357         .name = "elf", .plural_name = "elves", .glyph = 'e', .colour = WHITE,
358         .exp = 35, .gold_chance = 50, .gold = 150, .ac = 6, .hp_max = 22,
359         .level = 5, .intelligence = 15, .speed = FAST, .size = MEDIUM,
360         .flags = HEAD | HANDS | INFRAVISION,
361         .attacks = {
362             { .type = ATT_WEAPON, .damage = DAM_PHYSICAL },
363         }, .default_ai = MA_WANDER
364     },
365     { /* MT_GELATINOUSCUBE */
366         .name = "gelatinous cube", .glyph = 'g', .colour = CYAN,
367         .exp = 45, .ac = 1, .hp_max = 22,
368         .level = 5, .intelligence = 3, .speed = XSLOW, .size = LARGE,
369         .flags = METALLIVORE | RES_SLEEP | RES_POISON | RES_CONF,
370         .attacks = {
371             { .type = ATT_SLAM, .base = 1, .damage = DAM_ACID },
372         }, .default_ai = MA_WANDER
373     },
374     { /* MT_WHITE_DRAGON */
375         .name = "white dragon", .glyph = 'd', .colour = WHITE,
376         .exp = 1000, .gold = 500, .ac = 8, .hp_max = 55,
377         .level = 5, .intelligence = 16, .speed = NORMAL, .size = GIANT,
378         .flags = HEAD | DRAGON | RES_COLD,
379         .attacks = {
380             { .type = ATT_BITE, .base = 4, .damage = DAM_PHYSICAL },
381             { .type = ATT_CLAW, .base = 4, .damage = DAM_PHYSICAL },
382         }, .default_ai = MA_WANDER
383     },
384     { /* MT_METAMORPH */
385         .name = "metamorph", .glyph = 'm', .colour = WHITE,
386         .exp = 40, .ac = 3, .hp_max = 30,
387         .level = 6, .intelligence = 3, .speed = NORMAL, .size = MEDIUM,
388         .flags = 0,
389         .attacks = {
390             { .type = ATT_WEAPON, .base = 3, .damage = DAM_PHYSICAL },
391         }, .default_ai = MA_WANDER
392     },
393     { /* MT_VORTEX */
394         .name = "vortex", .plural_name = "vortexes", .glyph = 'v', .colour = CYAN,
395         .exp = 55, .ac = 6, .hp_max = 30,
396         .level = 6, .intelligence = 3, .speed = VFAST, .size = TINY,
397         .flags = RES_SLEEP | RES_POISON | FLY | RES_ELEC | RES_CONF,
398         .attacks = {
399             { .type = ATT_SLAM, .base = 3, .damage = DAM_PHYSICAL },
400         }, .default_ai = MA_WANDER
401     },
402     { /* MT_ZILLER */
403         .name = "ziller", .glyph = 'z', .colour = CYAN,
404         .exp = 35, .ac = 8, .hp_max = 30,
405         .level = 6, .intelligence = 3, .speed = SLOW, .size = MEDIUM,
406         .flags = HEAD | RES_CONF,
407         .attacks = {
408             { .type = ATT_CLAW, .base = 3, .damage = DAM_PHYSICAL },
409             { .type = ATT_CLAW, .base = 70, .damage = DAM_DEC_RND },
410         }, .default_ai = MA_WANDER
411     },
412     { /* MT_VIOLET_FUNGUS */
413         .name = "violet fungus", .plural_name = "violet fungi", .glyph = 'F', .colour = MAGENTA,
414         .exp = 100, .ac = 0, .hp_max = 38,
415         .level = 6, .intelligence = 3, .speed = XSLOW, .size = MEDIUM,
416         .flags = RES_SLEEP | RES_POISON | RES_CONF,
417         .attacks = {
418             { .type = ATT_SLAM, .base = 3, .damage = DAM_PHYSICAL },
419             { .type = ATT_SLAM, .base = 4, .damage = DAM_POISON },
420         }, .default_ai = MA_WANDER
421     },
422     { /* MT_WRAITH */
423         .name = "wraith", .glyph = 'W', .colour = LIGHTGRAY,
424         .exp = 325, .ac = 7, .hp_max = 30,
425         .level = 6, .intelligence = 3, .speed = NORMAL, .size = MEDIUM,
426         .flags = HEAD | HANDS | UNDEAD | RES_SLEEP | RES_POISON | RES_CONF,
427         .attacks = {
428             { .type = ATT_TOUCH, .base = 50, .damage = DAM_DRAIN_LIFE },
429         }, .default_ai = MA_WANDER
430     },
431     { /* MT_FORVALAKA */
432         .name = "forvalaka", .glyph = 'f', .colour = LIGHTGRAY,
433         .exp = 280, .ac = 4, .hp_max = 50,
434         .level = 6, .intelligence = 7, .speed = DOUBLE, .size = MEDIUM,
435         .flags = HEAD | UNDEAD | INFRAVISION | RES_POISON,
436         .attacks = {
437             { .type = ATT_BITE, .base = 5, .damage = DAM_PHYSICAL },
438         }, .default_ai = MA_WANDER
439     },
440     { /* MT_LAMA_NOBE */
441         .name = "lama nobe", .glyph = 'l', .colour = RED,
442         .exp = 80, .ac = 3, .hp_max = 35,
443         .level = 7, .intelligence = 6, .speed = NORMAL, .size = MEDIUM,
444         .flags = HEAD,
445         .attacks = {
446             { .type = ATT_BITE, .base = 3, .damage = DAM_PHYSICAL },
447             { .type = ATT_GAZE, .base = 25, .damage = DAM_BLINDNESS },
448         }, .default_ai = MA_WANDER
449     },
450     { /* MT_OSQUIP */
451         .name = "osquip", .glyph = 'o', .colour = BROWN,
452         .exp = 100, .ac = 5, .hp_max = 35,
453         .level = 7, .intelligence = 4, .speed = VFAST, .size = SMALL,
454         .flags = HEAD | PACK,
455         .attacks = {
456             { .type = ATT_BITE, .base = 10, .damage = DAM_PHYSICAL, .rand = 15 },
457         }, .default_ai = MA_WANDER
458     },
459     { /* MT_ROTHE */
460         .name = "rothe", .glyph = 'r', .colour = BROWN,
461         .exp = 250, .ac = 5, .hp_max = 50,
462         .level = 7, .intelligence = 5, .speed = VFAST, .size = LARGE,
463         .flags = HEAD | INFRAVISION | PACK,
464         .attacks = {
465             { .type = ATT_BITE, .base = 5, .damage = DAM_PHYSICAL },
466             { .type = ATT_CLAW, .base = 3, .damage = DAM_PHYSICAL },
467         }, .default_ai = MA_WANDER
468     },
469     { /* MT_XORN */
470         .name = "xorn", .glyph = 'X', .colour = BROWN,
471         .exp = 300, .ac = 10, .hp_max = 60,
472         .level = 7, .intelligence = 13, .speed = NORMAL, .size = MEDIUM,
473         .flags = INFRAVISION | RES_COLD | RES_FIRE,
474         .attacks = {
475             { .type = ATT_BITE, .base = 6, .damage = DAM_PHYSICAL },
476         }, .default_ai = MA_WANDER
477     },
478     { /* MT_VAMPIRE */
479         .name = "vampire", .glyph = 'V', .colour = RED,
480         .exp = 1000, .ac = 7, .hp_max = 50,
481         .level = 7, .intelligence = 17, .speed = NORMAL, .size = MEDIUM,
482         .flags = HEAD | HANDS | FLY | UNDEAD | INFRAVISION | REGENERATE | RES_SLEEP | RES_POISON | RES_CONF,
483         .attacks = {
484             { .type = ATT_BITE, .base = 75, .damage = DAM_DRAIN_LIFE },
485             { .type = ATT_WEAPON, .damage = DAM_PHYSICAL },
486         }, .default_ai = MA_WANDER
487     },
488     { /* MT_STALKER */
489         .name = "invisible stalker", .glyph = 'I', .colour = LIGHTGRAY,
490         .exp = 350, .ac = 7, .hp_max = 50,
491         .level = 7, .intelligence = 14, .speed = FAST, .size = MEDIUM,
492         .flags = HEAD | FLY | INVISIBLE,
493         .attacks = {
494             { .type = ATT_SLAM, .base = 6, .damage = DAM_PHYSICAL },
495         }, .default_ai = MA_WANDER
496     },
497     { /* MT_POLTERGEIST */
498         .name = "poltergeist", .glyph = 'p', .colour = WHITE,
499         .exp = 450, .ac = 6, .hp_max = 50,
500         .level = 8, .intelligence = 5, .speed = NORMAL, .size = MEDIUM,
501         .flags = FLY | UNDEAD | INVISIBLE | RES_SLEEP | RES_POISON,
502         .attacks = {
503             { .type = ATT_WEAPON, .damage = DAM_PHYSICAL },
504         }, .default_ai = MA_WANDER, .sound = "laugh"
505     },
506     { /* MT_DISENCHANTRESS */
507         .name = "disenchantress", .plural_name = "disenchantresses", .glyph = 'q', .colour = WHITE,
508         .exp = 500, .ac = 7, .hp_max = 50,
509         .level = 8, .intelligence = 5, .speed = NORMAL, .size = MEDIUM,
510         .flags = HEAD | HANDS | METALLIVORE,
511         .attacks = {
512             { .type = ATT_TOUCH, .damage = DAM_REM_ENCH },
513         }, .default_ai = MA_WANDER
514     },
515     { /* MT_SHAMBLINGMOUND */
516         .name = "shambling mound", .glyph = 's', .colour = GREEN,
517         .exp = 400, .ac = 8, .hp_max = 45,
518         .level = 8, .intelligence = 6, .speed = VSLOW, .size = GIANT,
519         .flags = RES_SLEEP | RES_POISON | RES_ELEC,
520         .attacks = {
521             { .type = ATT_SLAM, .base = 5, .damage = DAM_PHYSICAL },
522         }, .default_ai = MA_WANDER
523     },
524     { /* MT_YELLOW_MOLD */
525         .name = "yellow mold", .glyph = 'y', .colour = YELLOW,
526         .exp = 250, .ac = 0, .hp_max = 35,
527         .level = 8, .intelligence = 3, .speed = XSLOW, .size = SMALL,
528         .flags = RES_SLEEP | RES_POISON | RES_CONF,
529         .attacks = {
530             { .type = ATT_TOUCH, .base = 4, .damage = DAM_PHYSICAL },
531         }, .default_ai = MA_WANDER
532     },
533     { /* MT_UMBER_HULK */
534         .name = "umber hulk", .glyph = 'U', .colour = YELLOW,
535         .exp = 600, .ac = 12, .hp_max = 65,
536         .level = 8, .intelligence = 14, .speed = SLOW, .size = GIANT,
537         .flags = HEAD | HANDS | INFRAVISION | RES_CONF,
538         .attacks = {
539             { .type = ATT_CLAW, .base = 7, .damage = DAM_PHYSICAL },
540             { .type = ATT_GAZE, .base = 75, .damage = DAM_CONFUSION },
541         }, .default_ai = MA_WANDER
542     },
543     { /* MT_GNOME_KING */
544         .name = "gnome king", .glyph = 'G', .colour = RED,
545         .exp = 3000, .gold = 2000, .reroll_chance = 80, .ac = 11, .hp_max = 100,
546         .level = 9, .intelligence = 18, .speed = NORMAL, .size = SMALL,
547         .flags = HEAD | HANDS | RES_SLEEP,
548         .attacks = {
549             { .type = ATT_WEAPON, .damage = DAM_PHYSICAL },
550         }, .default_ai = MA_WANDER
551     },
552     { /* MT_MIMIC */
553         .name = "mimic", .glyph = 'M', .colour = BROWN,
554         .exp = 99, .ac = 5, .hp_max = 55,
555         .level = 9, .intelligence = 8, .speed = SLOW, .size = MEDIUM,
556         .flags = MIMIC,
557         .attacks = {
558             { .type = ATT_SLAM, .base = 6, .damage = DAM_PHYSICAL },
559         }, .default_ai = MA_WANDER
560     },
561     { /* MT_WATER_LORD */
562         .name = "water lord", .glyph = 'w', .colour = LIGHTBLUE,
563         .exp = 15000, .ac = 12, .hp_max = 150,
564         .level = 9, .intelligence = 20, .speed = NORMAL, .size = LARGE,
565         .flags = HEAD | NOBEHEAD | HANDS | RES_SLEEP | SWIM,
566         .attacks = {
567             { .type = ATT_WEAPON, .damage = DAM_PHYSICAL },
568         }, .default_ai = MA_WANDER
569     },
570     { /* MT_PURPLE_WORM */
571         .name = "purple worm", .glyph = 'P', .colour = MAGENTA,
572         .exp = 15000, .ac = 12, .hp_max = 120,
573         .level = 9, .intelligence = 3, .speed = VSLOW, .size = GARGANTUAN,
574         .flags = HEAD | RES_POISON,
575         .attacks = {
576             { .type = ATT_BITE, .base = 11, .damage = DAM_PHYSICAL },
577             { .type = ATT_STING, .base = 6, .damage = DAM_POISON },
578         }, .default_ai = MA_WANDER
579     },
580     { /* MT_XVART */
581         .name = "xvart", .glyph = 'x', .colour = LIGHTGRAY,
582         .exp = 1000, .ac = 11, .hp_max = 90,
583         .level = 9, .intelligence = 13, .speed = NORMAL, .size = SMALL,
584         .flags = HEAD | HANDS | INFRAVISION,
585         .attacks = {
586             { .type = ATT_WEAPON, .damage = DAM_PHYSICAL },
587         }, .default_ai = MA_WANDER
588     },
589     { /* MT_BRONZE_DRAGON */
590         .name = "bronze dragon", .glyph = 'D', .colour = BROWN,
591         .exp = 4000, .gold = 300, .ac = 10, .hp_max = 80,
592         .level = 9, .intelligence = 16, .speed = NORMAL, .size = GIANT,
593         .flags = HEAD | FLY | DRAGON,
594         .attacks = {
595             { .type = ATT_BITE, .base = 9, .damage = DAM_PHYSICAL },
596             { .type = ATT_CLAW, .base = 9, .damage = DAM_PHYSICAL },
597         }, .default_ai = MA_WANDER
598     },
599     { /* MT_GREEN_DRAGON */
600         .name = "green dragon", .glyph = 'D', .colour = LIGHTGREEN,
601         .exp = 2500, .gold = 200, .ac = 12, .hp_max = 70,
602         .level = 9, .intelligence = 15, .speed = NORMAL, .size = GIANT,
603         .flags = HEAD | FLY | DRAGON | RES_POISON,
604         .attacks = {
605             { .type = ATT_BREATH, .base = 8, .damage = DAM_POISON },
606             { .type = ATT_SLAM, .base = 25, .damage = DAM_PHYSICAL },
607         }, .default_ai = MA_WANDER
608     },
609     { /* MT_SILVER_DRAGON */
610         .name = "silver dragon", .glyph = 'D', .colour = LIGHTGRAY,
611         .exp = 10000, .gold = 700, .ac = 13, .hp_max = 100,
612         .level = 10, .intelligence = 20, .speed = NORMAL, .size = GIANT,
613         .flags = HEAD | FLY | DRAGON,
614         .attacks = {
615             { .type = ATT_BITE, .base = 12, .damage = DAM_PHYSICAL },
616             { .type = ATT_CLAW, .base = 12, .damage = DAM_PHYSICAL },
617         }, .default_ai = MA_WANDER
618     },
619     { /* MT_PLATINUM_DRAGON */
620         .name = "platinum dragon", .glyph = 'D', .colour = WHITE,
621         .exp = 24000, .gold = 1000, .ac = 15, .hp_max = 130,
622         .level = 11, .intelligence = 22, .speed = NORMAL, .size = GIANT,
623         .flags = HEAD | FLY | DRAGON | RES_CONF,
624         .attacks = {
625             { .type = ATT_BITE, .base = 15, .damage = DAM_PHYSICAL },
626             { .type = ATT_BREATH, .base = 15, .damage = DAM_MAGICAL, .rand = 30 },
627         }, .default_ai = MA_WANDER
628     },
629     { /* MT_RED_DRAGON */
630         .name = "red dragon", .glyph = 'D', .colour = LIGHTRED,
631         .exp = 14000, .gold = 800, .ac = 14, .hp_max = 110,
632         .level = 11, .intelligence = 19, .speed = NORMAL, .size = GIANT,
633         .flags = HEAD | FLY | DRAGON | RES_FIRE,
634         .attacks = {
635             { .type = ATT_BREATH, .base = 20, .damage = DAM_FIRE, .rand = 25 },
636             { .type = ATT_CLAW, .base = 13, .damage = DAM_PHYSICAL },
637         }, .default_ai = MA_WANDER
638     },
639     { /* MT_SPIRIT_NAGA */
640         .name = "spirit naga", .glyph = 'N', .colour = MAGENTA,
641         .exp = 20000, .ac = 16, .hp_max = 95,
642         .level = 11, .intelligence = 23, .speed = FAST, .size = LARGE,
643         .flags = HEAD | NOBEHEAD | FLY | SPIRIT | INFRAVISION | RES_SLEEP | RES_POISON | RES_CONF | RES_MAGIC,
644         .attacks = {
645             { .type = ATT_BITE, .base = 12, .damage = DAM_PHYSICAL },
646             { .type = ATT_MAGIC, .base = 1, .damage = DAM_RANDOM },
647         }, .default_ai = MA_WANDER
648     },
649     { /* MT_GREEN_URCHIN */
650         .name = "green urchin", .glyph = 'u', .colour = GREEN,
651         .exp = 5000, .ac = 17, .hp_max = 85,
652         .level = 10, .intelligence = 3, .speed = SLOW, .size = SMALL,
653         .flags = 0,
654         .attacks = {
655             { .type = ATT_STING, .base = 12, .damage = DAM_PHYSICAL },
656             { .type = ATT_STING, .base = 50, .damage = DAM_BLINDNESS },
657         }, .default_ai = MA_WANDER
658     },
659     { /* MT_DEMONLORD_I */
660         .name = "type I demon lord", .glyph = '&', .colour = LIGHTRED,
661         .exp = 50000, .ac = 17, .hp_max = 140,
662         .level = 12, .intelligence = 20, .speed = FAST, .size = MEDIUM,
663         .flags = HEAD | NOBEHEAD | HANDS | FLY | INVISIBLE | INFRAVISION | DEMON | RES_POISON | RES_MAGIC,
664         .attacks = {
665             { .type = ATT_BITE, .base = 18, .damage = DAM_PHYSICAL },
666             { .type = ATT_CLAW, .base = 18, .damage = DAM_PHYSICAL },
667         }, .default_ai = MA_WANDER
668     },
669     { /* MT_DEMONLORD_II */
670         .name = "type II demon lord", .glyph = '&', .colour = LIGHTRED,
671         .exp = 75000, .ac = 18, .hp_max = 160,
672         .level = 13, .intelligence = 21, .speed = FAST, .size = MEDIUM,
673         .flags = HEAD | NOBEHEAD | HANDS | FLY | INVISIBLE | INFRAVISION | DEMON | RES_POISON | RES_MAGIC,
674         .attacks = {
675             { .type = ATT_BITE, .base = 18, .damage = DAM_PHYSICAL },
676             { .type = ATT_CLAW, .base = 18, .damage = DAM_PHYSICAL },
677         }, .default_ai = MA_WANDER
678     },
679     { /* MT_DEMONLORD_III */
680         .name = "type III demon lord", .glyph = '&', .colour = LIGHTRED,
681         .exp = 100000, .ac = 19, .hp_max = 180,
682         .level = 14, .intelligence = 22, .speed = FAST, .size = MEDIUM,
683         .flags = HEAD | NOBEHEAD | HANDS | FLY | INVISIBLE | INFRAVISION | DEMON | RES_POISON | RES_MAGIC,
684         .attacks = {
685             { .type = ATT_BITE, .base = 18, .damage = DAM_PHYSICAL },
686             { .type = ATT_CLAW, .base = 18, .damage = DAM_PHYSICAL },
687         }, .default_ai = MA_WANDER
688     },
689     { /* MT_DEMONLORD_IV */
690         .name = "type IV demon lord", .glyph = '&', .colour = LIGHTRED,
691         .exp = 125000, .ac = 20, .hp_max = 200,
692         .level = 15, .intelligence = 23, .speed = FAST, .size = MEDIUM,
693         .flags = HEAD | NOBEHEAD | HANDS | FLY | INVISIBLE | INFRAVISION | DEMON | RES_POISON | RES_MAGIC,
694         .attacks = {
695             { .type = ATT_BITE, .base = 20, .damage = DAM_PHYSICAL },
696             { .type = ATT_CLAW, .base = 20, .damage = DAM_PHYSICAL },
697         }, .default_ai = MA_WANDER
698     },
699     { /* MT_DEMONLORD_V */
700         .name = "type V demon lord", .glyph = '&', .colour = LIGHTRED,
701         .exp = 150000, .ac = 21, .hp_max = 220,
702         .level = 16, .intelligence = 24, .speed = FAST, .size = MEDIUM,
703         .flags = HEAD | NOBEHEAD | HANDS | FLY | INVISIBLE | INFRAVISION | DEMON | RES_POISON | RES_MAGIC,
704         .attacks = {
705             { .type = ATT_BITE, .base = 22, .damage = DAM_PHYSICAL },
706             { .type = ATT_CLAW, .base = 22, .damage = DAM_PHYSICAL },
707         }, .default_ai = MA_WANDER
708     },
709     { /* MT_DEMONLORD_VI */
710         .name = "type VI demon lord", .glyph = '&', .colour = LIGHTRED,
711         .exp = 175000, .ac = 22, .hp_max = 240,
712         .level = 17, .intelligence = 25, .speed = FAST, .size = LARGE,
713         .flags = HEAD | NOBEHEAD | HANDS | FLY | INVISIBLE | INFRAVISION | DEMON | RES_POISON | RES_MAGIC,
714         .attacks = {
715             { .type = ATT_BITE, .base = 24, .damage = DAM_PHYSICAL },
716             { .type = ATT_CLAW, .base = 24, .damage = DAM_PHYSICAL },
717         }, .default_ai = MA_WANDER
718     },
719     { /* MT_DEMONLORD_VII */
720         .name = "type VII demon lord", .glyph = '&', .colour = LIGHTRED,
721         .exp = 200000, .ac = 23, .hp_max = 260,
722         .level = 18, .intelligence = 26, .speed = FAST, .size = GIANT,
723         .flags = HEAD | NOBEHEAD | HANDS | FLY | INVISIBLE | INFRAVISION | DEMON | RES_POISON | RES_CONF | RES_MAGIC,
724         .attacks = {
725             { .type = ATT_BITE, .base = 27, .damage = DAM_PHYSICAL },
726             { .type = ATT_CLAW, .base = 27, .damage = DAM_PHYSICAL },
727         }, .default_ai = MA_WANDER
728     },
729     { /* MT_DEMON_PRINCE */
730         .name = "demon prince", .glyph = '&', .colour = RED,
731         .exp = 300000, .ac = 25, .hp_max = 345,
732         .level = 25, .intelligence = 28, .speed = FAST, .size = GIANT,
733         .flags = HEAD | NOBEHEAD | HANDS | FLY | INVISIBLE | INFRAVISION | DEMON | RES_FIRE | RES_SLEEP | RES_POISON | RES_CONF | RES_MAGIC,
734         .attacks = {
735             { .type = ATT_BITE, .base = 30, .damage = DAM_PHYSICAL },
736             { .type = ATT_CLAW, .base = 30, .damage = DAM_PHYSICAL },
737         }, .default_ai = MA_WANDER
738     },
739     { /* MT_TOWN_PERSON */
740         .name = "human", .glyph = '@', .colour = BROWN,
741         .exp = 0, .ac = 0, .hp_max = 10,
742         .level = 1, .intelligence = 10, .speed = NORMAL, .size = MEDIUM,
743         .flags = HEAD | HANDS,
744         .attacks = {
745             { .type = ATT_SLAM, .base = 0, .damage = DAM_PHYSICAL },
746         }, .default_ai = MA_CIVILIAN
747     },
748 };
749 
750 static inline monster_action_t monster_default_ai(monster *m);
751 static gboolean monster_player_visible(monster *m);
752 static gboolean monster_attack_available(monster *m, attack_t type);
753 static item *monster_weapon_select(monster *m);
754 static void monster_weapon_wield(monster *m, item *weapon);
755 static gboolean monster_item_disenchant(monster *m, struct player *p);
756 static gboolean monster_item_rust(monster *m, struct player *p);
757 static gboolean monster_player_rob(monster *m, struct player *p, item_t item_type);
758 
759 static position monster_find_next_pos_to(monster *m, position dest);
760 static position monster_move_wander(monster *m, struct player *p);
761 static position monster_move_attack(monster *m, struct player *p);
762 static position monster_move_confused(monster *m, struct player *p);
763 static position monster_move_flee(monster *m, struct player *p);
764 static position monster_move_serve(monster *m, struct player *p);
765 static position monster_move_civilian(monster *m, struct player *p);
766 
767 static gboolean monster_breath_hit(const GList *traj,
768         const damage_originator *damo,
769         gpointer data1, gpointer data2);
770 
monster_new(monster_t type,position pos,gpointer leader)771 monster *monster_new(monster_t type, position pos, gpointer leader)
772 {
773     g_assert(type < MT_MAX && pos_valid(pos));
774 
775     monster *nmonster;
776     item_t itype;      /* item type */
777 
778     /* check if supplied position is suitable for a monster */
779     if (!map_pos_validate(game_map(nlarn, Z(pos)), pos, LE_MONSTER, FALSE))
780     {
781         return NULL;
782     }
783 
784     /* don't create genocided monsters */
785     if (monster_is_genocided(type))
786     {
787         /* try to find a replacement for the demon prince */
788         if (type == MT_DEMON_PRINCE)
789             return monster_new(MT_DEMONLORD_I + rand_0n(7), pos, NULL);
790         else
791             return NULL;
792     }
793 
794     /* make room for monster */
795     nmonster = g_malloc0(sizeof(monster));
796 
797     nmonster->type = type;
798 
799     /* determine max hp; prevent the living dead */
800     nmonster->hp_max = nmonster->hp = max(1, divert(monster_type_hp_max(type), 10));
801 
802     nmonster->effects = g_ptr_array_new();
803     nmonster->inv = inv_new(nmonster);
804 
805     /* fill monsters inventory */
806     if (monster_gold_amount(nmonster) > 0)
807     {
808         const int gold_chance = monster_gold_chance(nmonster);
809         if (gold_chance == 0 || chance(gold_chance))
810         {
811             /* add gold to monster's inventory, randomize the amount */
812             int gcount = max(divert(monster_gold_amount(nmonster), 30), 1);
813             inv_add(&nmonster->inv, item_new(IT_GOLD, gcount));
814         }
815     }
816 
817     /* add special items */
818     switch (type)
819     {
820     case MT_LEPRECHAUN:
821         if (chance(25))
822         {
823             inv_add(&nmonster->inv, item_new_random(IT_GEM, FALSE));
824         }
825         break;
826 
827     case MT_TROGLODYTE:
828     case MT_NYMPH:
829     case MT_PLATINUM_DRAGON:
830     case MT_RED_DRAGON:
831     case MT_GNOME_KING:
832         /* add something that is not a container */
833         do
834         {
835             itype = rand_1n(IT_MAX);
836         }
837         while (itype == IT_CONTAINER);
838 
839         inv_add(&nmonster->inv, item_new_by_level(itype, Z(pos)));
840         break;
841 
842     case MT_TOWN_PERSON:
843         /* initialize name counter */
844         nmonster->number = rand_1n(40);
845         break;
846 
847     default:
848         /* no fancy stuff... */
849         break;
850     }
851 
852     /* generate a weapon if monster can use it */
853     if (monster_attack_available(nmonster, ATT_WEAPON))
854     {
855         int weapon_count = 3;
856         int wpns[3]; /* choice of weapon types */
857         item *weapon;
858 
859         /* preset weapon types */
860         switch (type)
861         {
862         case MT_HOBGOBLIN:
863         case MT_ORC:
864             wpns[0] = WT_ODAGGER;
865             wpns[1] = WT_OSHORTSWORD;
866             wpns[2] = WT_OSPEAR;
867             break;
868 
869         case MT_TROLL:
870             wpns[0] = WT_CLUB;
871             wpns[1] = WT_CLUB;
872             wpns[2] = WT_BATTLEAXE;
873             break;
874 
875         case MT_ELF:
876             wpns[0] = WT_ESHORTSWORD;
877             wpns[1] = WT_ESPEAR;
878             weapon_count = 2;
879             break;
880 
881         case MT_BUGBEAR:
882         case MT_CENTAUR:
883         case MT_POLTERGEIST:
884             wpns[0] = WT_MACE;
885             wpns[1] = WT_FLAIL;
886             wpns[2] = WT_BATTLEAXE;
887             break;
888 
889         case MT_VAMPIRE:
890         case MT_GNOME_KING:
891         case MT_WATER_LORD:
892         case MT_XVART:
893             wpns[0] = WT_LONGSWORD;
894             wpns[1] = WT_2SWORD;
895             wpns[2] = WT_SWORDSLASHING;
896             break;
897 
898         default:
899             wpns[0] = WT_DAGGER;
900             wpns[1] = WT_SPEAR;
901             wpns[2] = WT_SHORTSWORD;
902             break;
903         }
904 
905         weapon = item_new(IT_WEAPON, wpns[rand_0n(weapon_count)]);
906         item_new_finetouch(weapon);
907 
908         inv_add(&nmonster->inv, weapon);
909 
910         /* wield the new weapon */
911         monster_weapon_wield(nmonster, weapon);
912     } /* finished initializing weapons */
913 
914     /* initialize mimics */
915     if (monster_flags(nmonster, MIMIC))
916     {
917         const int possible_types[] = { IT_AMULET, IT_GOLD, IT_RING, IT_GEM,
918                                        IT_CONTAINER, IT_BOOK, IT_POTION,
919                                        IT_SCROLL
920                                      };
921 
922         /* put mimicked item into monster inventory */
923         const int chosen_type = possible_types[rand_0n(8)];
924         item *itm = item_new_by_level(chosen_type, Z(nmonster->pos));
925         inv_add(&nmonster->inv, itm);
926 
927         /* the mimic is not known to be a monster */
928         nmonster->unknown = TRUE;
929     }
930 
931     /* initialize AI */
932     nmonster->action = monster_default_ai(nmonster);
933     nmonster->player_pos = pos_invalid;
934     nmonster->leader = leader;
935 
936     /* register monster with game */
937     nmonster->oid = game_monster_register(nlarn, nmonster);
938 
939     /* set position */
940     nmonster->pos = pos;
941 
942     /* link monster to tile */
943     map_set_monster_at(game_map(nlarn, Z(pos)), pos, nmonster);
944 
945     /* add some members to the pack if we created a pack monster */
946     if (monster_flags(nmonster, PACK) && !leader)
947     {
948         guint count = rand_1n(5);
949         while (count > 0)
950         {
951             position mpos = map_find_space_in(game_map(nlarn, Z(pos)),
952                                     rect_new_sized(pos, 4), LE_MONSTER, FALSE);
953             /* no space left? */
954             if (!pos_valid(mpos)) break;
955 
956             /* valid position returned, place a pack member there */
957             monster_new(type, mpos, nmonster->oid);
958             count--;
959         }
960     }
961 
962     /* increment monster count */
963     game_map(nlarn, Z(pos))->mcount++;
964 
965     return nmonster;
966 }
967 
monster_new_by_level(position pos)968 monster *monster_new_by_level(position pos)
969 {
970     g_assert(pos_valid(pos));
971 
972     const int mlevel[] = {
973         MT_KOBOLD,           // D1:   5
974         MT_GIANT_ANT,        // D2:  11
975         MT_ZOMBIE,           // D3:  17
976         MT_CENTAUR,          // D4:  22
977         MT_WHITE_DRAGON,     // D5:  27
978         MT_FORVALAKA,        // D6:  33
979         MT_STALKER,          // D7:  39
980         MT_SHAMBLINGMOUND,   // D8:  42
981         MT_MIMIC,            // D9:  46
982         MT_BRONZE_DRAGON,    // D10: 50
983         MT_PLATINUM_DRAGON,  // V1:  53
984         MT_GREEN_URCHIN,     // V2:  56
985         MT_DEMON_PRINCE      // V3
986     };
987 
988     const int nlevel = Z(pos);
989     int monster_id;
990 
991     if (nlevel == 0)
992     {
993         /* only town persons in town */
994         monster_id = MT_TOWN_PERSON;
995     }
996     else
997     {
998         /* everything else in the caverns */
999         int minstep = nlevel - 4;
1000         int maxstep = nlevel - 1;
1001 
1002         int monster_id_min;
1003         int monster_id_max;
1004 
1005         if (chance(2 * game_difficulty(nlarn)))
1006             maxstep += 2;
1007         else if (chance(7 * (game_difficulty(nlarn) + 1)))
1008             maxstep++;
1009         else if (chance(10))
1010             minstep--;
1011 
1012         if (minstep < 0)
1013             monster_id_min = MT_GIANT_BAT;
1014         else
1015             monster_id_min = mlevel[minstep] + 1;
1016 
1017         if (maxstep < 0)
1018             maxstep = 0;
1019         else if (maxstep > MAP_MAX - 2)
1020             maxstep = MAP_MAX - 2;
1021 
1022         monster_id_max = mlevel[maxstep];
1023 
1024         do
1025         {
1026             monster_id = rand_m_n(monster_id_min, monster_id_max);
1027         }
1028         while ((monster_id < 0)
1029                 || (monster_id >= MT_MAX)
1030                 || nlarn->monster_genocided[monster_id]
1031                 || chance(monster_type_reroll_chance(monster_id)));
1032     }
1033 
1034     return monster_new(monster_id, pos, NULL);
1035 }
1036 
monster_destroy(monster * m)1037 void monster_destroy(monster *m)
1038 {
1039     g_assert(m != NULL && m->type < MT_MAX);
1040 
1041     /* free effects */
1042     while (m->effects->len > 0)
1043     {
1044         gpointer effect_id = g_ptr_array_remove_index(m->effects, m->effects->len - 1);
1045         effect *e = game_effect_get(nlarn, effect_id);
1046         effect_destroy(e);
1047     }
1048 
1049     g_ptr_array_free(m->effects, TRUE);
1050 
1051     /* free inventory */
1052     if (m->inv)
1053         inv_destroy(m->inv, TRUE);
1054 
1055     /* unregister monster */
1056     game_monster_unregister(nlarn, m->oid);
1057 
1058     /* decrement monster count */
1059     game_map(nlarn, Z(m->pos))->mcount--;
1060 
1061     /* free monster's FOV if existing */
1062     if (m->fv)
1063         fov_free(m->fv);
1064 
1065     g_free(m);
1066 }
1067 
monster_serialize(gpointer oid,monster * m,cJSON * root)1068 void monster_serialize(gpointer oid, monster *m, cJSON *root)
1069 {
1070     cJSON *mval;
1071 
1072     cJSON_AddItemToArray(root, mval = cJSON_CreateObject());
1073     cJSON_AddNumberToObject(mval, "type", monster_type(m));
1074     cJSON_AddNumberToObject(mval, "oid", GPOINTER_TO_UINT(oid));
1075     cJSON_AddNumberToObject(mval, "hp_max", m->hp_max);
1076     cJSON_AddNumberToObject(mval, "hp", m->hp);
1077     cJSON_AddNumberToObject(mval,"pos", pos_val(m->pos));
1078     cJSON_AddNumberToObject(mval, "movement", m->movement);
1079     cJSON_AddNumberToObject(mval, "action", m->action);
1080 
1081     if (m->eq_weapon != NULL)
1082         cJSON_AddNumberToObject(mval, "eq_weapon",
1083                         GPOINTER_TO_UINT(m->eq_weapon->oid));
1084 
1085     if (m->number)
1086         cJSON_AddNumberToObject(mval, "number", m->number);
1087 
1088     if (m->leader)
1089         cJSON_AddNumberToObject(mval, "leader", GPOINTER_TO_UINT(m->leader));
1090 
1091     if (m->unknown)
1092         cJSON_AddTrueToObject(mval, "unknown");
1093 
1094     if (m->lastseen != 0)
1095     {
1096         cJSON_AddNumberToObject(mval,"lastseen", m->lastseen);
1097         cJSON_AddNumberToObject(mval,"player_pos", pos_val(m->player_pos));
1098     }
1099 
1100     /* inventory */
1101     if (inv_length(m->inv) > 0)
1102     {
1103         cJSON_AddItemToObject(mval, "inventory", inv_serialize(m->inv));
1104     }
1105 
1106     /* effects */
1107     if (m->effects->len > 0)
1108     {
1109         cJSON_AddItemToObject(mval, "effects", effects_serialize(m->effects));
1110     }
1111 }
1112 
monster_deserialize(cJSON * mser,game * g)1113 void monster_deserialize(cJSON *mser, game *g)
1114 {
1115     cJSON *obj;
1116     guint oid;
1117     monster *m = g_malloc0(sizeof(monster));
1118 
1119     m->type = cJSON_GetObjectItem(mser, "type")->valueint;
1120     oid = cJSON_GetObjectItem(mser, "oid")->valueint;
1121     m->oid = GUINT_TO_POINTER(oid);
1122     m->hp_max = cJSON_GetObjectItem(mser, "hp_max")->valueint;
1123     m->hp = cJSON_GetObjectItem(mser, "hp")->valueint;
1124     pos_val(m->pos) = cJSON_GetObjectItem(mser, "pos")->valueint;
1125     m->movement = cJSON_GetObjectItem(mser, "movement")->valueint;
1126     m->action = cJSON_GetObjectItem(mser, "action")->valueint;
1127 
1128     if ((obj = cJSON_GetObjectItem(mser, "eq_weapon")))
1129         m->eq_weapon = game_item_get(nlarn, GUINT_TO_POINTER(obj->valueint));
1130 
1131     if ((obj = cJSON_GetObjectItem(mser, "number")))
1132         m->number = obj->valueint;
1133 
1134     if ((obj = cJSON_GetObjectItem(mser, "leader")))
1135     {
1136         guint leader = obj->valueint;
1137         m->leader = GUINT_TO_POINTER(leader);
1138     }
1139 
1140     if ((obj = cJSON_GetObjectItem(mser, "unknown")))
1141         m->unknown = obj->valueint;
1142 
1143     if ((obj = cJSON_GetObjectItem(mser, "lastseen")))
1144         m->lastseen = obj->valueint;
1145 
1146     if ((obj = cJSON_GetObjectItem(mser, "player_pos")))
1147         pos_val(m->player_pos) = obj->valueint;
1148 
1149     /* inventory */
1150     if ((obj = cJSON_GetObjectItem(mser, "inventory")))
1151         m->inv = inv_deserialize(obj);
1152     else
1153         m->inv = inv_new(m);
1154 
1155     /* effects */
1156     if ((obj = cJSON_GetObjectItem(mser, "effects")))
1157         m->effects = effects_deserialize(obj);
1158     else
1159         m->effects = g_ptr_array_new();
1160 
1161     /* add monster to game */
1162     g_hash_table_insert(g->monsters, m->oid, m);
1163 
1164     /* increase max_id to match used ids */
1165     if (oid > g->monster_max_id)
1166         g->monster_max_id = oid;
1167 
1168     /* increment the count of monsters of the map the monster is on */
1169     game_map(g, Z(m->pos))->mcount++;
1170 }
1171 
monster_hp_max(monster * m)1172 int monster_hp_max(monster *m)
1173 {
1174     g_assert(m != NULL && m->type < MT_MAX);
1175     return m->hp_max;
1176 }
1177 
monster_hp(monster * m)1178 int monster_hp(monster *m)
1179 {
1180     g_assert(m != NULL && m->type < MT_MAX);
1181     return m->hp;
1182 }
1183 
monster_hp_inc(monster * m,int amount)1184 void monster_hp_inc(monster *m, int amount)
1185 {
1186     g_assert(m != NULL && m->type < MT_MAX);
1187     m->hp = min(m->hp + amount, m->hp_max);
1188 }
1189 
monster_oid(monster * m)1190 gpointer monster_oid(monster *m)
1191 {
1192     g_assert (m != NULL);
1193     return m->oid;
1194 }
1195 
monster_pos(monster * m)1196 position monster_pos(monster *m)
1197 {
1198     g_assert(m != NULL && m->type < MT_MAX);
1199     return m->pos;
1200 }
1201 
monster_map_element(monster * m)1202 static int monster_map_element(monster *m)
1203 {
1204     if (monster_type(m) == MT_XORN)
1205         return LE_XORN;
1206 
1207     if (monster_flags(m, FLY))
1208         return LE_FLYING_MONSTER;
1209 
1210     if (monster_flags(m, SWIM))
1211         return LE_SWIMMING_MONSTER;
1212 
1213     return LE_MONSTER;
1214 }
1215 
monster_valid_dest(map * m,position pos,int map_elem)1216 int monster_valid_dest(map *m, position pos, int map_elem)
1217 {
1218     /* only civilians use LE_GROUND and can't move through the player */
1219     if (map_elem == LE_GROUND && pos_identical(pos, nlarn->p->pos))
1220         return FALSE;
1221 
1222     switch (map_tiletype_at(m, pos))
1223     {
1224     case LT_WALL:
1225         return (map_elem == LE_XORN);
1226 
1227     case LT_DEEPWATER:
1228         if (map_elem == LE_SWIMMING_MONSTER)
1229             return TRUE;
1230         // else fall through
1231     case LT_LAVA:
1232         return (map_elem == LE_FLYING_MONSTER);
1233 
1234     default:
1235         /* the map tile must be passable */
1236         return map_pos_passable(m, pos);
1237     }
1238 }
1239 
monster_pos_set(monster * m,map * mp,position target)1240 int monster_pos_set(monster *m, map *mp, position target)
1241 {
1242     g_assert(m != NULL && mp != NULL && pos_valid(target));
1243 
1244     if (map_pos_validate(mp, target, monster_map_element(m), FALSE))
1245     {
1246         /* remove current reference to monster from tile */
1247         map_set_monster_at(monster_map(m), m->pos, NULL);
1248 
1249         /* set new position */
1250         m->pos = target;
1251 
1252         /* set reference to monster on tile */
1253         map_set_monster_at(mp, target, m);
1254 
1255         return TRUE;
1256     }
1257 
1258     return FALSE;
1259 }
1260 
monster_type(monster * m)1261 monster_t monster_type(monster *m)
1262 {
1263     g_assert(m != NULL);
1264     return m->type;
1265 }
1266 
monster_unknown(monster * m)1267 gboolean monster_unknown(monster *m)
1268 {
1269     g_assert (m != NULL);
1270     return m->unknown;
1271 }
1272 
monster_unknown_set(monster * m,gboolean what)1273 void monster_unknown_set(monster *m, gboolean what)
1274 {
1275     g_assert (m != NULL);
1276     m->unknown = what;
1277 }
1278 
monster_inv(monster * m)1279 inventory **monster_inv(monster *m)
1280 {
1281     g_assert (m != NULL);
1282     return &m->inv;
1283 }
1284 
monster_nearby(monster * m)1285 static gboolean monster_nearby(monster *m)
1286 {
1287     /* different level */
1288     if (Z(m->pos) != Z(nlarn->p->pos))
1289         return FALSE;
1290 
1291     return fov_get(nlarn->p->fv, m->pos);
1292 }
1293 
monster_in_sight(monster * m)1294 gboolean monster_in_sight(monster *m)
1295 {
1296     g_assert (m != NULL);
1297 
1298     /* player is blind */
1299     if (player_effect(nlarn->p, ET_BLINDNESS))
1300         return FALSE;
1301 
1302     /* different level */
1303     if (Z(m->pos) != Z(nlarn->p->pos))
1304         return FALSE;
1305 
1306     /* invisible monster, player has no infravision */
1307     if (monster_flags(m, INVISIBLE) && !player_effect(nlarn->p, ET_INFRAVISION))
1308         return FALSE;
1309 
1310     return fov_get(nlarn->p->fv, m->pos);
1311 }
1312 
get_town_person_name(int value)1313 static const char *get_town_person_name(int value)
1314 {
1315     // various jobs
1316     const char *npc_desc[] = { "peasant woman", "old man", "old woman",
1317                                "little boy", "young girl", "fisherman",
1318                                "midwife", "errand boy", "bar maid",
1319                                "stable-lad", "innkeeper", "woodcutter",
1320                                "carpenter", "clerk", "barber",
1321                                "teacher", "town guard", "postman",
1322                                "cobbler", "baker", "merchant",
1323                                "clergyman", "student", "blacksmith",
1324                                "nurse", "seamstress", "cartwright",
1325                                "student", "sales clerk", "miller"
1326                              };
1327     if (value >= 30)
1328         return "peasant";
1329 
1330     return npc_desc[value];
1331 }
1332 
monster_action(monster * m)1333 monster_action_t monster_action(monster *m)
1334 {
1335     g_assert (m != NULL);
1336 
1337     return m->action;
1338 }
1339 
1340 // Takes visibility into account.
1341 // For the real name, use monster_name() directly.
monster_get_name(monster * m)1342 const char *monster_get_name(monster *m)
1343 {
1344     /* only show real names of invisible monsters in
1345      * wizard mode when full visibility is enabled */
1346     if (!monster_in_sight(m) && !game_fullvis(nlarn))
1347         return ("unseen monster");
1348 
1349     if (monster_type(m) == MT_TOWN_PERSON)
1350         return get_town_person_name(m->number);
1351 
1352     return (monster_name(m));
1353 }
1354 
monster_type_plural_name(monster_t mt,const int count)1355 const char* monster_type_plural_name(monster_t mt, const int count)
1356 {
1357     if (count > 1)
1358     {
1359         if (monster_data[mt].plural_name == NULL)
1360         {
1361             /* need a static buffer to return to calling functions */
1362             static char buf[61] = { 0 };
1363             g_snprintf(buf, 60, "%ss", monster_type_name(mt));
1364             return buf;
1365         }
1366         else
1367         {
1368             return monster_data[mt].plural_name;
1369         }
1370     }
1371 
1372     return monster_type_name(mt);
1373 }
1374 
monster_die(monster * m,struct player * p)1375 void monster_die(monster *m, struct player *p)
1376 {
1377     g_assert(m != NULL);
1378 
1379     /* if the player can see the monster describe the event */
1380     /* Also give a message for invisible monsters you killed yourself
1381        (the xp gain gives this away anyway). */
1382     if (monster_in_sight(m)
1383             || (p != NULL && map_pos_is_visible(monster_map(m),
1384                     p->pos, monster_pos(m))))
1385     {
1386         const char *message;
1387 
1388         /* give a different message if a servant is expired */
1389         if (monster_action(m) == MA_SERVE && m->number == 0)
1390             message = "The %s disappears!";
1391         else
1392             message = "The %s dies!";
1393 
1394         log_add_entry(nlarn->log, message, monster_get_name(m));
1395     }
1396 
1397     /* make sure mimics never leave the mimicked item behind */
1398     if (monster_flags(m, MIMIC) && inv_length(m->inv) > 0)
1399     {
1400         inv_del(&m->inv, 0);
1401     }
1402 
1403     /* drop stuff the monster carries */
1404     if (inv_length(m->inv))
1405     {
1406         /* Did it fall into water? */
1407         const int tile = map_tiletype_at(monster_map(m), monster_pos(m));
1408         if (tile == LT_DEEPWATER || tile == LT_LAVA)
1409         {
1410             int count = 0;
1411             while (inv_length(m->inv) > 0)
1412             {
1413                 item *it = inv_get(m->inv, 0);
1414                 if (item_is_unique(it))
1415                 {
1416                     /* teleport the item to safety */
1417                     inv_del_element(&m->inv, it);
1418                     map_item_add(game_map(nlarn, Z(nlarn->p->pos)), it);
1419                 }
1420                 else
1421                 {
1422                     inv_del(&m->inv, 0);
1423                     count++;
1424                 }
1425             }
1426             if (count && monster_nearby(m))
1427                 log_add_entry(nlarn->log, "You hear a splash!");
1428         }
1429         else
1430         {
1431             /* dump items on the floor */
1432             inventory **floor = map_ilist_at(monster_map(m), monster_pos(m));
1433             while (inv_length(m->inv) > 0)
1434             {
1435                 inv_add(floor, inv_get(m->inv, 0));
1436                 inv_del(&m->inv, 0);
1437             }
1438         }
1439     }
1440 
1441     /* reward experience, but not for summoned monsters */
1442     if (p != NULL && (monster_action(m) != MA_SERVE))
1443     {
1444         player_exp_gain(p, monster_exp(m));
1445         p->stats.monsters_killed[m->type] += 1;
1446     }
1447 
1448     /* unlink the monster from its map */
1449     map_set_monster_at(monster_map(m), m->pos, NULL);
1450 
1451     /* assure that the monster's hp indicates that the monster is dead */
1452     if (m->hp > 0)
1453         m->hp = 0;
1454 
1455     /* add the monster to the list of dead monsters */
1456     g_ptr_array_add(nlarn->dead_monsters, m);
1457 }
1458 
monster_level_enter(monster * m,struct map * l)1459 void monster_level_enter(monster *m, struct map *l)
1460 {
1461     g_assert (m != NULL && l != NULL);
1462 
1463     sobject_t source = map_sobject_at(monster_map(m), m->pos);
1464     sobject_t target;
1465     position npos;
1466     const char *what = NULL;
1467     const char *how  = "comes";
1468 
1469     /* check if the monster used the stairs */
1470     switch (source)
1471     {
1472     case LS_CAVERNS_EXIT:
1473         target = LS_CAVERNS_ENTRY;
1474         what = "through";
1475         break;
1476 
1477     case LS_CAVERNS_ENTRY:
1478         target = LS_CAVERNS_EXIT;
1479         what = "through";
1480         break;
1481 
1482     case LS_STAIRSDOWN:
1483         target = LS_STAIRSUP;
1484         what = "down";
1485         break;
1486 
1487     case LS_STAIRSUP:
1488         target = LS_STAIRSDOWN;
1489         what = "up";
1490         break;
1491 
1492     case LS_ELEVATORDOWN:
1493         target = LS_ELEVATORUP;
1494         what = "down";
1495         break;
1496 
1497     case LS_ELEVATORUP:
1498         target = LS_ELEVATORDOWN;
1499         what = "up";
1500         break;
1501 
1502     default:
1503         target = LS_NONE;
1504     }
1505 
1506     /* determine new position */
1507     if (target)
1508     {
1509         /* monster came through a map entrance */
1510         npos = map_find_sobject(l, target);
1511     }
1512     else
1513     {
1514         /* monster fell through a trap door */
1515         npos = map_find_space(l, LE_MONSTER, FALSE);
1516     }
1517 
1518     /* validate new position */
1519     if (pos_identical(nlarn->p->pos, npos))
1520     {
1521         /* player is standing at the target position */
1522         how = "squeezes past";
1523         npos = map_find_space_in(l, rect_new_sized(npos, 1), LE_MONSTER, FALSE);
1524     }
1525     else
1526 
1527     if (!map_pos_validate(l, npos, LE_MONSTER, FALSE))
1528     {
1529         /* the position somehow isn't valid */
1530         return;
1531     }
1532 
1533     /* remove monster from old map  */
1534     map *oldmap = game_map(nlarn, Z(m->pos));
1535     map_set_monster_at(oldmap, m->pos, NULL);
1536 
1537     /* put monster into map */
1538     monster_pos_set(m, l, npos);
1539 
1540     /* reset the information of the player's last known position */
1541     m->lastseen = 0;
1542 
1543     /* log the event */
1544     if (monster_in_sight(m) && target)
1545     {
1546         log_add_entry(nlarn->log, "The %s %s %s %s.", monster_get_name(m),
1547                       how, what, so_get_desc(target));
1548     }
1549 }
1550 
monster_move(gpointer * oid,monster * m,game * g)1551 void monster_move(gpointer *oid __attribute__((unused)), monster *m, game *g)
1552 {
1553     /* monster's new position */
1554     position m_npos;
1555 
1556     /* expire summoned monsters */
1557     if (monster_action(m) == MA_SERVE
1558             && !monster_effect(m, ET_CHARM_MONSTER))
1559     {
1560         m->number--;
1561 
1562         if (m->number == 0)
1563         {
1564             /* expired */
1565             monster_die(m, g->p);
1566             return;
1567         }
1568     }
1569 
1570     if (monster_hp(m) < 1)
1571         /* Monster is already dead. */
1572         return;
1573 
1574     position mpos = monster_pos(m);
1575 
1576     /* modify effects */
1577     monster_effects_expire(m);
1578 
1579     /* regenerate / inflict poison upon monster. */
1580     if (!monster_regenerate(m, g->gtime, g->difficulty))
1581         /* the monster died */
1582         return;
1583 
1584     /* damage caused by map effects */
1585     damage *dam = map_tile_damage(monster_map(m), monster_pos(m),
1586                                   monster_flags(m, FLY)
1587                                   || monster_effect(m, ET_LEVITATION));
1588 
1589     /* deal damage caused by floor effects */
1590     if ((dam != NULL) && !(m = monster_damage_take(m, dam)))
1591         /* the monster died */
1592         return;
1593 
1594     /* move the monster only if it is on the same map as the player or
1595        an adjacent map */
1596     gboolean map_adjacent = (Z(mpos) == Z(g->p->pos)
1597                              || (Z(mpos) == Z(g->p->pos) - 1)
1598                              || (Z(mpos) == Z(g->p->pos) + 1)
1599                              || (Z(mpos) == MAP_CMAX && Z(g->p->pos) == 0)
1600                             );
1601     if (!map_adjacent)
1602         return;
1603 
1604     /* Update the monster's knowledge of player's position.
1605        Not for civilians or servants: the first don't care,
1606        the latter just know. This allows to use player_pos
1607        and lastseen for other purposes. */
1608     monster_action_t ma = monster_action(m);
1609 
1610     if ((ma != MA_SERVE && ma != MA_CIVILIAN)
1611         && (monster_player_visible(m)
1612             || (player_effect(g->p, ET_AGGRAVATE_MONSTER)
1613                 && pos_distance(m->pos, g->p->pos) < 15)))
1614     {
1615         monster_update_player_pos(m, g->p->pos);
1616     }
1617 
1618     /* add the monster's speed to the monster's movement points */
1619     m->movement += monster_speed(m);
1620 
1621     /* let the monster make a move as long it has movement points left */
1622     while (m->movement >= NORMAL)
1623     {
1624         /* reduce the monster's movement points */
1625         m->movement -= NORMAL;
1626 
1627         /* update monsters action */
1628         if (monster_update_action(m, MA_NONE) && monster_in_sight(m))
1629         {
1630             /* the monster has chosen a new action and the player
1631                can see the new action, so let's describe it */
1632 
1633             if (m->action == MA_ATTACK && monster_sound(m))
1634             {
1635                 const char *sound = monster_sound(m);
1636                 log_add_entry(g->log, "The %s %s%ss!",
1637                               monster_name(m), sound,
1638                               sound[strlen(sound) - 1] == 's' ? "e": "");
1639             }
1640             else if (m->action == MA_FLEE)
1641             {
1642                 log_add_entry(g->log, "The %s turns to flee!",
1643                         monster_get_name(m));
1644             }
1645         }
1646 
1647         /* let the monster have a look at the items at it's current position
1648            if it chose to pick up something, the turn is over */
1649         if (monster_items_pickup(m))
1650             return;
1651 
1652         /* determine monster's next move */
1653         m_npos = monster_pos(m);
1654 
1655         switch (m->action)
1656         {
1657         case MA_FLEE:
1658             m_npos = monster_move_flee(m, g->p);
1659             break;
1660 
1661         case MA_REMAIN:
1662             /* Sgt. Stan Still - do nothing */
1663             break;
1664 
1665         case MA_WANDER:
1666             m_npos = monster_move_wander(m, g->p);
1667             break;
1668 
1669         case MA_ATTACK:
1670             /* monster tries a ranged attack */
1671             if (monster_player_visible(m)
1672                     && monster_player_ranged_attack(m, g->p))
1673                 return;
1674 
1675             m_npos = monster_move_attack(m, g->p);
1676             break;
1677 
1678         case MA_CONFUSION:
1679             m_npos = monster_move_confused(m, g->p);
1680             break;
1681 
1682         case MA_SERVE:
1683             m_npos = monster_move_serve(m, g->p);
1684             break;
1685 
1686         case MA_CIVILIAN:
1687             m_npos = monster_move_civilian(m, g->p);
1688             break;
1689 
1690         case MA_NONE:
1691             /* possibly a bug */
1692             break;
1693         }
1694 
1695         /* ******** if new position has been found - move the monster ********* */
1696         if (!pos_identical(m_npos, monster_pos(m)))
1697         {
1698             /* get the monster's current map */
1699             map *mmap = monster_map(m);
1700 
1701             /* get stationary object at the monster's target position */
1702             sobject_t target_st = map_sobject_at(mmap, m_npos);
1703 
1704             /* vampires won't step onto mirrors */
1705             if ((m->type == MT_VAMPIRE) && (target_st == LS_MIRROR))
1706             {
1707                 /* No movement - FIXME: should try to move around it */
1708             }
1709 
1710             else if (pos_identical(g->p->pos, m_npos))
1711             {
1712                 /* The monster bumps into the player who is invisible to the
1713                    monster. Thus the monster gains knowledge over the player's
1714                    current position. */
1715                 monster_update_player_pos(m, g->p->pos);
1716 
1717                 log_add_entry(g->log, "The %s bumps into you.", monster_get_name(m));
1718             }
1719 
1720             /* check for door */
1721             else if ((target_st == LS_CLOSEDDOOR) && monster_flags(m, HANDS))
1722             {
1723                 /* dim-witted or confused monster are unable to open doors */
1724                 if (monster_int(m) < 4 || monster_effect_get(m, ET_CONFUSION))
1725                 {
1726                     /* notify the player if the door is visible */
1727                     if (monster_in_sight(m))
1728                     {
1729                         log_add_entry(g->log, "The %s bumps into the door.",
1730                                       monster_get_name(m));
1731                     }
1732                 }
1733                 else
1734                 {
1735                     /* the monster is capable of opening the door */
1736                     map_sobject_set(mmap, m_npos, LS_OPENDOOR);
1737 
1738                     /* notify the player if the door is visible */
1739                     if (monster_in_sight(m))
1740                     {
1741                         log_add_entry(g->log, "The %s opens the door.",
1742                                       monster_get_name(m));
1743                     }
1744                 }
1745             }
1746 
1747             /* set the monsters new position */
1748             else
1749             {
1750                 /* check if the new position is valid for this monster */
1751                 if (map_pos_validate(mmap, m_npos, monster_map_element(m), FALSE))
1752                 {
1753                     /* the new position is valid -> reposition the monster */
1754                     monster_pos_set(m, mmap, m_npos);
1755                 }
1756                 else
1757                 {
1758                     /* the new position is invalid */
1759                     map_tile_t nle = map_tiletype_at(mmap, m_npos);
1760 
1761                     switch (nle)
1762                     {
1763                         case LT_TREE:
1764                         case LT_WALL:
1765                             if (monster_in_sight(m))
1766                             {
1767                                 log_add_entry(g->log, "The %s bumps into %s.",
1768                                         monster_get_name(m), mt_get_desc(nle));
1769                             }
1770                             break;
1771 
1772                         case LT_LAVA:
1773                         case LT_DEEPWATER:
1774                             if (monster_in_sight(m)) {
1775                                 log_add_entry(g->log, "The %s sinks into %s.",
1776                                         monster_get_name(m), mt_get_desc(nle));
1777                             }
1778                             monster_die(m, g->p);
1779                             break;
1780 
1781                         default:
1782                             /* just do not move.. */
1783                             break;
1784                     }
1785                 }
1786 
1787                 /* check for traps */
1788                 if (map_trap_at(mmap, monster_pos(m)))
1789                 {
1790                     if (!monster_trap_trigger(m))
1791                         return; /* trap killed the monster */
1792                 }
1793 
1794             } /* end new position */
1795         } /* end monster repositioning */
1796     } /* while movement >= NORMAL */
1797 
1798     /* increment count of turns since when player was last seen */
1799     if (m->lastseen) m->lastseen++;
1800 }
1801 
monster_polymorph(monster * m)1802 void monster_polymorph(monster *m)
1803 {
1804     g_assert (m != NULL);
1805 
1806     /* make sure mimics never leave the mimicked item behind */
1807     if (monster_flags(m, MIMIC) && inv_length(m->inv) > 0)
1808     {
1809         inv_del(&m->inv, 0);
1810     }
1811 
1812     const map_element_t old_elem = monster_map_element(m);
1813     do
1814     {
1815         m->type = rand_1n(MT_DEMON_PRINCE);
1816     }
1817     while (monster_is_genocided(m->type));
1818 
1819     /* if the new monster can't survive in this terrain, kill it */
1820     const map_element_t new_elem = monster_map_element(m);
1821 
1822     /* We need to temporarily remove the monster from it's tile
1823        as monster_valid_dest() tests if there is a monster on
1824        the tile and hence would always return false. */
1825     map_set_monster_at(monster_map(m), m->pos, NULL);
1826 
1827     /* check if the position would be valid.. */
1828     gboolean valid_pos = monster_valid_dest(monster_map(m), m->pos, new_elem);
1829 
1830     /* ..and restore the monster to it's position */
1831     map_set_monster_at(monster_map(m), m->pos, m);
1832 
1833     if (!valid_pos)
1834     {
1835         if (monster_in_sight(m))
1836         {
1837             /* briefly display the new monster before it dies */
1838             display_paint_screen(nlarn->p);
1839             g_usleep(250000);
1840 
1841             switch (old_elem)
1842             {
1843             case LE_FLYING_MONSTER:
1844                 log_add_entry(nlarn->log, "The %s falls into the %s!",
1845                               monster_get_name(m),
1846                               mt_get_desc(map_tiletype_at(monster_map(m), m->pos)));
1847                 break;
1848             case LE_SWIMMING_MONSTER:
1849                 log_add_entry(nlarn->log, "The %s sinks like a rock!",
1850                               monster_get_name(m));
1851                 break;
1852             case LE_XORN:
1853                 log_add_entry(nlarn->log, "The %s is trapped in the wall!",
1854                               monster_get_name(m));
1855                 break;
1856             default:
1857                 break;
1858             }
1859         }
1860         monster_die(m, nlarn->p);
1861     }
1862     else
1863     {
1864         /* get the relative amount of hp left */
1865         float relative_hp = (float)m->hp / (float)m->hp_max;
1866 
1867         /* Determine the new maximum hitpoints for the new monster
1868            type and set the monster's current hit points to the
1869            relative value of the monster's remaining hit points. */
1870         m->hp_max = divert(monster_type_hp_max(m->type), 10);
1871         m->hp = (int)(m->hp_max * relative_hp);
1872     }
1873 }
1874 
monster_items_pickup(monster * m)1875 int monster_items_pickup(monster *m)
1876 {
1877     g_assert(m != NULL);
1878 
1879     // The town people never take your stuff.
1880     if (monster_type(m) == MT_TOWN_PERSON)
1881         return FALSE;
1882 
1883     /* monsters affected by levitation can't pick up stuff */
1884     if (monster_effect(m, ET_LEVITATION))
1885         return FALSE;
1886 
1887     /* TODO: gelatinous cube digests items, rust monster eats metal stuff */
1888     /* FIXME: time management */
1889 
1890     gboolean pick_up = FALSE;
1891     item *it;
1892 
1893     for (guint idx = 0; idx < inv_length(*map_ilist_at(monster_map(m), m->pos)); idx++)
1894     {
1895         it = inv_get(*map_ilist_at(monster_map(m), m->pos), idx);
1896 
1897         if (m->type == MT_LEPRECHAUN
1898                 && ((it->type == IT_GEM) || (it->type == IT_GOLD)))
1899         {
1900             /* leprechauns collect treasures */
1901             pick_up = TRUE;
1902         }
1903         else if (it->type == IT_WEAPON && monster_attack_available(m, ATT_WEAPON))
1904         {
1905             /* monster can attack with weapons */
1906             if (m->eq_weapon == NULL)
1907                 pick_up = TRUE;
1908             else
1909             {
1910                 /* compare this weapon with the weapon the monster wields */
1911                 if (m->eq_weapon == NULL || (weapon_damage(m->eq_weapon)
1912                                         < weapon_damage(it)))
1913                     pick_up = TRUE;
1914             }
1915         }
1916 
1917         if (pick_up)
1918         {
1919             /* The monster has picked up the item.
1920 
1921                Determine if the item is a weapon.
1922                This has to be done before adding the item to the monster's
1923                inventory as the item might be destroyed after calling inv_add().
1924                (Stackable items get destroyed if an item of the kind exists
1925                 in the target inventory!).
1926             */
1927             gboolean new_weapon = (it->type == IT_WEAPON);
1928 
1929             if (monster_in_sight(m))
1930             {
1931                 gchar *buf = item_describe(it, player_item_identified(nlarn->p, it),
1932                                            FALSE, FALSE);
1933 
1934                 log_add_entry(nlarn->log, "The %s picks up %s.",
1935                         monster_get_name(m), buf);
1936                 g_free(buf);
1937             }
1938 
1939             inv_del_element(map_ilist_at(monster_map(m), m->pos), it);
1940             inv_add(&m->inv, it);
1941 
1942             if (new_weapon)
1943             {
1944                 /* find out if the new weapon is better than the old one */
1945                 item *best = monster_weapon_select(m);
1946 
1947                 /* If the new item is a weapon, 'it' is still a valid pointer
1948                    to the item picked up at this point as weapons are not
1949                    stackable. */
1950                 if (it == best)
1951                 {
1952                     monster_weapon_wield(m, best);
1953                 }
1954             }
1955             /* finish this turn after picking up an item */
1956             return TRUE;
1957         } /* end if pick_up */
1958     } /* end foreach item */
1959 
1960     return FALSE;
1961 }
1962 
monster_attack_count(monster * m)1963 guint monster_attack_count(monster *m)
1964 {
1965     guint count = 0;
1966 
1967     while (count < G_N_ELEMENTS(monster_data[m->type].attacks)
1968             && monster_data[m->type].attacks[count].type != ATT_NONE)
1969     {
1970         count++;
1971     }
1972 
1973     return count;
1974 }
1975 
monster_attack(monster * m,guint num)1976 attack monster_attack(monster *m, guint num)
1977 {
1978     g_assert (m != NULL && num <= monster_attack_count(m));
1979 
1980     return monster_data[m->type].attacks[num - 1];
1981 }
1982 
monster_breath_attack(monster * m,player * p,attack att)1983 static int monster_breath_attack(monster *m, player *p, attack att)
1984 {
1985     g_assert(att.type == ATT_BREATH);
1986 
1987     /* generate damage */
1988     damage *dam = damage_new(att.damage, att.type, att.base + game_difficulty(nlarn),
1989                              DAMO_MONSTER, m);
1990 
1991     /* the attack might have a random amount */
1992     if (att.rand > 0)
1993         dam->amount += rand_0n(att.rand);
1994 
1995     if (monster_in_sight(m))
1996     {
1997         log_add_entry(nlarn->log, "The %s breathes a %s!", monster_get_name(m),
1998                       monster_breath_data[att.damage].desc);
1999     }
2000     else
2001     {
2002         log_add_entry(nlarn->log, "A %s spews forth from nowhere!",
2003                       monster_breath_data[att.damage].desc);
2004     }
2005 
2006     /* handle the breath */
2007     map_trajectory(m->pos, p->pos, &(dam->dam_origin),
2008                    monster_breath_hit, dam, NULL, TRUE,
2009                    monster_breath_data[att.damage].glyph,
2010                    monster_breath_data[att.damage].colour, TRUE);
2011 
2012     /* the damage is copied in monster_breath_hit(), thus destroy the
2013        original damage here */
2014     damage_free(dam);
2015 
2016     return FALSE;
2017 }
2018 
modified_attack_amount(int amount,int damage_type)2019 static int modified_attack_amount(int amount, int damage_type)
2020 {
2021     if (damage_type == DAM_POISON)
2022         return amount + (game_difficulty(nlarn) + 1)/2;
2023 
2024     return amount + game_difficulty(nlarn)/2;
2025 }
2026 
monster_player_attack(monster * m,player * p)2027 void monster_player_attack(monster *m, player *p)
2028 {
2029     g_assert(m != NULL && p != NULL);
2030 
2031     map *mmap = game_map(nlarn, Z(m->pos));
2032 
2033     /* the player is invisible and the monster bashes into thin air */
2034     if (!pos_identical(m->player_pos, p->pos))
2035     {
2036         if (!map_is_monster_at(mmap, p->pos) && monster_in_sight(m))
2037         {
2038             log_add_entry(nlarn->log, "The %s bashes into thin air.",
2039                     monster_get_name(m));
2040         }
2041 
2042         m->lastseen++;
2043 
2044         return;
2045     }
2046 
2047     /* player is invisible and monster tries to hit player */
2048     if (player_effect(p, ET_INVISIBILITY) && !(monster_flags(m, INFRAVISION)
2049                                                || monster_effect(m, ET_INFRAVISION))
2050             && chance(65))
2051     {
2052         if (monster_in_sight(m))
2053         {
2054             log_add_entry(nlarn->log, "The %s misses wildly.",
2055                           monster_get_name(m));
2056         }
2057         return;
2058     }
2059 
2060     /* choose a random attack type */
2061     attack att = monster_attack(m, rand_1n(monster_attack_count(m) + 1));
2062 
2063     /* No attack has been found. Return to calling function. */
2064     if (att.type == ATT_NONE) return;
2065 
2066     /* handle breath attacks separately */
2067     if (att.type == ATT_BREATH)
2068     {
2069         monster_breath_attack(m, p, att);
2070         return;
2071     }
2072 
2073     /* generate damage */
2074     damage *dam = damage_new(att.damage, att.type,
2075                         modified_attack_amount(att.base, att.damage),
2076                         DAMO_MONSTER, m);
2077 
2078     /* deal with random damage (spirit naga) */
2079     if (dam->type == DAM_RANDOM)
2080         dam->type = rand_1n(DAM_MAX);
2081 
2082     if (dam->type == DAM_DEC_RND)
2083         dam->type = rand_m_n(DAM_DEC_CON, DAM_DEC_RND);
2084 
2085     /* set damage for weapon attacks */
2086     if (att.type == ATT_WEAPON)
2087     {
2088         /* make monster size affect weapon damage */
2089         /* FIXME: handle the vorpal blade */
2090         dam->amount  = (m->eq_weapon != NULL) ? weapon_damage(m->eq_weapon) : 1
2091                         + (int)rand_0n(game_difficulty(nlarn) + 2)
2092                         + monster_level(m)
2093                         + 2 * ((monster_size(m) - MEDIUM)) / 25;
2094     }
2095     else if (dam->type == DAM_PHYSICAL)
2096     {
2097         /* increase damage with difficulty */
2098         dam->amount = att.base
2099                       + monster_level(m)
2100                       + rand_0n(game_difficulty(nlarn) + 2);
2101     }
2102 
2103     /* add variable damage */
2104     if (att.rand) dam->amount += rand_1n(att.rand);
2105 
2106     /* half damage if player is protected against spirits */
2107     if (player_effect(p, ET_SPIRIT_PROTECTION) && monster_flags(m, SPIRIT))
2108     {
2109         if (dam->type == DAM_PHYSICAL)
2110         {
2111             /* halve physical damage */
2112             dam->amount >>= 1;
2113         }
2114         else
2115         {
2116             /* FIXME: give log message */
2117             damage_free(dam);
2118 
2119             return;
2120         }
2121     }
2122 
2123     /* handle some damage types here */
2124     switch (dam->type)
2125     {
2126     case DAM_STEAL_GOLD:
2127     case DAM_STEAL_ITEM:
2128         if (monster_player_rob(m, p, (dam->type == DAM_STEAL_GOLD) ? IT_GOLD : IT_ALL))
2129         {
2130             /* teleport away */
2131             monster_pos_set(m, mmap, map_find_space(mmap, LE_MONSTER, FALSE));
2132         }
2133 
2134         damage_free(dam);
2135         break;
2136 
2137     case DAM_RUST:
2138         log_add_entry(nlarn->log, "The %s %s you.", monster_get_name(m),
2139                       monster_attack_verb[att.type]);
2140 
2141         monster_item_rust(m, p);
2142         p->attacked = TRUE;
2143         damage_free(dam);
2144         break;
2145 
2146     case DAM_REM_ENCH:
2147         monster_item_disenchant(m, p);
2148         p->attacked = TRUE;
2149         damage_free(dam);
2150         break;
2151 
2152     default:
2153         if (att.type != ATT_GAZE || !player_effect(p, ET_BLINDNESS))
2154         {
2155             /* log the attack */
2156             log_add_entry(nlarn->log, "The %s %s you.", monster_get_name(m),
2157                           monster_attack_verb[att.type]);
2158         }
2159         /* 50% chance of reflecting adjacent gazes */
2160         if (att.type == ATT_GAZE && player_effect(p, ET_REFLECTION)
2161                 && chance(50))
2162         {
2163             if (!player_effect(p, ET_BLINDNESS))
2164                 log_add_entry(nlarn->log, "The gaze is reflected harmlessly.");
2165         }
2166         else
2167             player_damage_take(p, dam, PD_MONSTER, m->type);
2168         break;
2169     }
2170 }
2171 
monster_player_ranged_attack(monster * m,player * p)2172 int monster_player_ranged_attack(monster *m, player *p)
2173 {
2174     g_assert(m != NULL && p != NULL);
2175 
2176     /* choose a random attack type */
2177     attack att = monster_attack(m, rand_1n(monster_attack_count(m) + 1));
2178     if (att.type == ATT_GAZE && chance(att.base/3))
2179     {
2180         if (!player_effect(p, ET_BLINDNESS))
2181         {
2182             log_add_entry(nlarn->log, "The %s %s you.", monster_get_name(m),
2183                           monster_attack_verb[att.type]);
2184         }
2185         if (player_effect(p, ET_REFLECTION))
2186         {
2187             if (!player_effect(p, ET_BLINDNESS))
2188                 log_add_entry(nlarn->log, "The gaze is reflected harmlessly.");
2189         }
2190         else
2191         {
2192             damage *dam = damage_new(att.damage, att.type,
2193                     att.base + game_difficulty(nlarn), DAMO_MONSTER, m);
2194 
2195             player_damage_take(p, dam, PD_MONSTER, m->type);
2196         }
2197         return TRUE;
2198     }
2199     if (att.type != ATT_BREATH)
2200         return FALSE;
2201 
2202     return monster_breath_attack(m, p, att);
2203 }
2204 
monster_damage_take(monster * m,damage * dam)2205 monster *monster_damage_take(monster *m, damage *dam)
2206 {
2207     struct player *p = NULL;
2208 
2209     g_assert(m != NULL && dam != NULL);
2210 
2211     if (dam->dam_origin.ot == DAMO_PLAYER)
2212         p = (player *)dam->dam_origin.originator;
2213 
2214     if (game_wizardmode(nlarn) && fov_get(nlarn->p->fv, m->pos))
2215         log_add_entry(nlarn->log, damage_to_str(dam));
2216 
2217     int hp_orig = m->hp;
2218 
2219     switch (dam->type)
2220     {
2221     case DAM_PHYSICAL:
2222         dam->amount -= monster_ac(m);
2223         if (dam->amount < 1 && monster_in_sight(m))
2224         {
2225             log_add_entry(nlarn->log, "The %s isn't hurt.",
2226                     monster_get_name(m));
2227         }
2228         break;
2229 
2230     case DAM_MAGICAL:
2231         if (monster_flags(m, RES_MAGIC))
2232         {
2233             dam->amount /= monster_level(m);
2234             if (monster_in_sight(m))
2235             {
2236                 log_add_entry(nlarn->log, "The %s %sresists the magic.",
2237                         monster_get_name(m), dam->amount > 0 ? "partly " : "");
2238             }
2239         }
2240         break;
2241 
2242     case DAM_FIRE:
2243         if (monster_flags(m, RES_FIRE))
2244         {
2245             /*
2246              * The monster's fire resistance reduces the damage taken
2247              * by 10% per monster level
2248              */
2249             dam->amount -= (guint)(((float)dam->amount / 100) *
2250                  /* prevent uint wrap around for monsters with level > 10 */
2251                  (min(monster_level(m), 10) * 10));
2252             if (monster_in_sight(m))
2253             {
2254                 log_add_entry(nlarn->log, "The %s %sresists the flames.",
2255                         monster_get_name(m), dam->amount > 0 ? "partly " : "");
2256             }
2257         }
2258         break;
2259 
2260     case DAM_COLD:
2261         if (monster_flags(m, RES_COLD))
2262         {
2263             dam->amount = 0;
2264             if (monster_in_sight(m))
2265             {
2266                 log_add_entry(nlarn->log, "The %s loves the cold!",
2267                         monster_get_name(m));
2268             }
2269         }
2270         break;
2271 
2272     case DAM_WATER:
2273         if (monster_flags(m, SWIM))
2274             dam->amount = 0;
2275         break;
2276 
2277     case DAM_ELECTRICITY:
2278         if (monster_flags(m, RES_ELEC))
2279         {
2280             dam->amount = 0;
2281             if (monster_in_sight(m))
2282             {
2283                 log_add_entry(nlarn->log, "The %s is not affected!",
2284                         monster_get_name(m));
2285             }
2286         }
2287         /* double damage for flying monsters */
2288         else if (monster_flags(m, FLY) || monster_effect(m, ET_LEVITATION))
2289         {
2290             dam->amount *= 2;
2291             // special message?
2292         }
2293         break;
2294 
2295     default:
2296         break;
2297     }
2298 
2299     /* subtract damage from HP;
2300      * prevent adding to HP after resistance has lowered damage amount */
2301     m->hp -= max(0, dam->amount);
2302 
2303     if (game_wizardmode(nlarn) && fov_get(nlarn->p->fv, m->pos))
2304         log_add_entry(nlarn->log, "[applied: %d]", hp_orig - m->hp);
2305 
2306     if (m->hp < hp_orig)
2307     {
2308         /* monster has been hit */
2309         if (m->type == MT_METAMORPH)
2310         {
2311             /*
2312              * The metamorph transforms if HP is low.
2313              * Get the percentage of hitpoints the metamorph has left.
2314              * If this is less than 80%, the metamorph will turn into
2315              * another monster that will usually be more dangerous.
2316              */
2317             float relative_hp = (float)m->hp / (float)m->hp_max;
2318 
2319             if ((m->hp > 0) && (relative_hp < 0.8))
2320             {
2321                 char *wdesc = NULL;
2322                 const char *old_name = monster_name(m);
2323                 gboolean seen_old = monster_in_sight(m);
2324                 m->type = MT_BRONZE_DRAGON + rand_0n(9);
2325                 gboolean seen_new = monster_in_sight(m);
2326 
2327                 /* Determine the new maximum hitpoints for the new monster
2328                    type and set the monster's current hit points to the
2329                    relative value of the metamorphs remaining hit points. */
2330                 if (relative_hp < 0.1) relative_hp = 0.1;
2331                 m->hp_max = divert(monster_type_hp_max(m->type), 10);
2332                 m->hp = (int)(m->hp_max * relative_hp);
2333 
2334                 /* Drop the weapon if the monster the metamorph turned
2335                    into can not handle weapons. */
2336                 if (m->eq_weapon && !monster_attack_available(m, ATT_WEAPON))
2337                 {
2338                     /* If the monster stepped on a trap p is NULL, thus we
2339                        need to use nlarn->p here. */
2340                     wdesc = item_describe(m->eq_weapon,
2341                                     player_item_known(nlarn->p, m->eq_weapon),
2342                                     FALSE, FALSE);
2343 
2344                     inv_del_element(&m->inv, m->eq_weapon);
2345                     inv_add(map_ilist_at(monster_map(m), m->pos), m->eq_weapon);
2346                     m->eq_weapon = NULL;
2347                 }
2348 
2349                 if (p && (seen_old || seen_new))
2350                 {
2351                     if (seen_old && wdesc != NULL)
2352                     {
2353                         log_add_entry(nlarn->log, "The %s drops %s.",
2354                                         old_name, wdesc);
2355                     }
2356 
2357                     if (seen_old && seen_new)
2358                     {
2359                         log_add_entry(nlarn->log, "The %s turns into a %s!",
2360                                 old_name, monster_get_name(m));
2361                     }
2362                     else if (seen_old)
2363                     {
2364                         log_add_entry(nlarn->log, "The %s vanishes!",
2365                                         old_name);
2366                     }
2367                     else
2368                     {
2369                         log_add_entry(nlarn->log, "A %s suddenly appears!",
2370                                 monster_get_name(m));
2371                     }
2372                 }
2373 
2374                 if (wdesc != NULL) g_free(wdesc);
2375             }
2376         }
2377     }
2378 
2379     if (m->hp < 1)
2380     {
2381         /* monster dies */
2382         monster_die(m, p);
2383         m = NULL;
2384     }
2385 
2386     g_free(dam);
2387 
2388     return m;
2389 }
2390 
monster_update_action(monster * m,monster_action_t override)2391 gboolean monster_update_action(monster *m, monster_action_t override)
2392 {
2393     monster_action_t naction; /* new action */
2394     guint mtime; /* max. number of turns a monster will look for the player */
2395     gboolean low_hp;
2396 
2397     if (override > MA_NONE)
2398     {
2399         /* set the monster's action to the requested value */
2400         m->action = override;
2401 
2402         /* if the monster is to be a servant, set its lifetime */
2403         if (override == MA_SERVE)
2404         {
2405             /* FIXME: it would be nice to have a variable amount of turns */
2406             m->number = 100;
2407         }
2408         return TRUE;
2409     }
2410 
2411     /* handle some easy action updates before the more difficult decisions */
2412     switch (m->action)
2413     {
2414         case MA_SERVE:     /* once servant, forever servant */
2415         case MA_CIVILIAN:  /* town people never change their behaviour */
2416         case MA_CONFUSION: /* confusion is removed by monster_effect_del() */
2417         case MA_REMAIN:    /* status set by hold monster/sleep/being trapped */
2418             return FALSE;
2419             break;
2420 
2421         default:
2422             /* continue to evaluate... */
2423             break;
2424     }
2425 
2426     mtime  = monster_int(m) + 25 + (5 * game_difficulty(nlarn));
2427     low_hp = (m->hp < (monster_hp_max(m) / 4 ));
2428 
2429     if (monster_flags(m, MIMIC) && m->unknown)
2430     {
2431         /* stationary monsters */
2432         naction = MA_REMAIN;
2433     }
2434     else if ((low_hp && (monster_int(m) > 4)) || monster_effect(m, ET_SCARED))
2435     {
2436         /* low HP or very scared => FLEE from player */
2437         naction = MA_FLEE;
2438     }
2439     else if (m->lastseen && (m->lastseen < mtime))
2440     {
2441         /* after having spotted the player, aggressive monster will follow
2442            the player for a certain amount of time turns, afterwards loose
2443            interest. More peaceful monsters will do something else. */
2444         /* TODO: need to test for aggressiveness */
2445         naction = MA_ATTACK;
2446     }
2447     else
2448     {
2449         /* if no action could be found, return to original behaviour */
2450         naction = MA_WANDER;
2451     }
2452 
2453     if (naction != m->action)
2454     {
2455         m->action = naction;
2456         return TRUE;
2457     }
2458 
2459     return FALSE;
2460 }
2461 
monster_update_player_pos(monster * m,position ppos)2462 void monster_update_player_pos(monster *m, position ppos)
2463 {
2464     g_assert (m != NULL);
2465 
2466     m->player_pos = ppos;
2467     m->lastseen = 1;
2468 }
2469 
monster_regenerate(monster * m,time_t gtime,int difficulty)2470 gboolean monster_regenerate(monster *m, time_t gtime, int difficulty)
2471 {
2472     /* number of turns between occasions */
2473     int frequency;
2474 
2475     /* temporary var for effect */
2476     effect *e;
2477 
2478     g_assert(m != NULL);
2479 
2480     /* modify frequency by difficulty: more regeneration, less poison */
2481     frequency = difficulty << 1;
2482 
2483     /* handle regeneration */
2484     if (monster_flags(m, REGENERATE) && (m->hp < monster_hp_max(m)))
2485     {
2486         /* regenerate every (10 - difficulty) turns */
2487         if (gtime % (10 - difficulty) == 0)
2488             m->hp++;
2489     }
2490 
2491     /* handle poison */
2492     if ((e = monster_effect_get(m, ET_POISON)))
2493     {
2494         if ((gtime - e->start) % (22 + frequency) == 0)
2495         {
2496             m->hp -= e->amount;
2497         }
2498 
2499         if (m->hp < 1)
2500         {
2501             /* monster died from poison */
2502             monster_die(m, NULL);
2503             return FALSE;
2504         }
2505     }
2506 
2507     return TRUE;
2508 }
2509 
get_mimic_item(monster * m)2510 item *get_mimic_item(monster *m)
2511 {
2512     g_assert(m && monster_flags(m, MIMIC));
2513 
2514     /* polymorphed mimics may not pose as items */
2515     if (inv_length(m->inv) > 0)
2516         return inv_get(m->inv, 0);
2517 
2518     return NULL;
2519 }
2520 
monster_desc(monster * m)2521 char *monster_desc(monster *m)
2522 {
2523     int hp_rel;
2524     GString *desc;
2525     const char *injury = NULL;
2526 
2527     g_assert (m != NULL);
2528 
2529     desc = g_string_new(NULL);
2530 
2531     /* describe mimic as mimicked item */
2532     if (monster_unknown(m) && inv_length(m->inv) > 0)
2533     {
2534         item *it = get_mimic_item(m);
2535         gchar *item_desc= item_describe(it, player_item_known(nlarn->p, it),
2536                                         FALSE, FALSE);
2537 
2538         g_string_append_printf(desc, "You see %s there", item_desc);
2539         g_free(item_desc);
2540 
2541         return g_string_free(desc, FALSE);
2542     }
2543 
2544     hp_rel = (((float)m->hp / (float)monster_hp_max(m)) * 100);
2545 
2546     /* prepare health status description */
2547     if (m->hp == monster_hp_max(m))
2548         injury = "uninjured";
2549     else if (hp_rel > 80)
2550         injury = "slightly injured";
2551     else if (hp_rel > 20)
2552         injury = "injured";
2553     else if (hp_rel > 10)
2554         injury = "heavily injured";
2555     else
2556         injury = "critically injured";
2557 
2558     g_string_append_printf(desc, "%s %s, %s %s", a_an(injury), injury,
2559                            monster_ai_desc[m->action], monster_get_name(m));
2560 
2561     if (game_wizardmode(nlarn))
2562     {
2563         /* show monster's hp and max hp in wizard mode */
2564         g_string_append_printf(desc, " %s(%d/%d hp)",
2565             m->leader ? "(pack member) " : "",
2566             m->hp, m->hp_max);
2567     }
2568 
2569     if (m->eq_weapon != NULL)
2570     {
2571         /* describe the weapon the monster wields */
2572         gchar *weapon_desc = item_describe(m->eq_weapon,
2573                         player_item_known(nlarn->p, m->eq_weapon),
2574                         TRUE, FALSE);
2575 
2576         g_string_append_printf(desc, ", armed with %s", weapon_desc);
2577         g_free(weapon_desc);
2578     }
2579 
2580     /* add effect description */
2581     if (m->effects->len > 0)
2582     {
2583         char **desc_list = strv_new();
2584 
2585         for (guint i = 0; i < m->effects->len; i++)
2586         {
2587             effect *e = game_effect_get(nlarn, g_ptr_array_index(m->effects, i));
2588 
2589             if (effect_get_desc(e))
2590             {
2591                 strv_append_unique(&desc_list, effect_get_desc(e));
2592             }
2593         }
2594 
2595         char *effects = g_strjoinv(", ", desc_list);
2596         g_strfreev(desc_list);
2597 
2598         g_string_append_printf(desc, " (%s)", effects);
2599 
2600         g_free(effects);
2601     }
2602 
2603     return g_string_free(desc, FALSE);
2604 }
2605 
monster_glyph(monster * m)2606 char monster_glyph(monster *m)
2607 {
2608     g_assert (m != NULL);
2609 
2610     if (m->unknown && inv_length(m->inv) > 0)
2611     {
2612         item *it = inv_get(m->inv, 0);
2613         return item_glyph(it->type);
2614     }
2615     else
2616     {
2617         return monster_data[m->type].glyph;
2618     }
2619 }
2620 
monster_color(monster * m)2621 int monster_color(monster *m)
2622 {
2623     g_assert (m != NULL);
2624 
2625     if (m->unknown && inv_length(m->inv) > 0)
2626     {
2627         item *it = inv_get(m->inv, 0);
2628         return item_colour(it);
2629     }
2630     else
2631     {
2632         return monster_data[m->type].colour;
2633     }
2634 }
2635 
monster_genocide(monster_t monster_id)2636 void monster_genocide(monster_t monster_id)
2637 {
2638     GList *mlist;
2639 
2640     g_assert(monster_id < MT_MAX);
2641 
2642     nlarn->monster_genocided[monster_id] = TRUE;
2643     mlist = g_hash_table_get_values(nlarn->monsters);
2644 
2645     /* purge genocided monsters */
2646     for (GList *iter = mlist; iter != NULL; iter = iter->next)
2647     {
2648         monster *monst = (monster *)iter->data;
2649         if (monster_is_genocided(monst->type))
2650         {
2651             /* add the monster to the game's list of dead monsters */
2652             g_ptr_array_add(nlarn->dead_monsters, monst);
2653         }
2654     }
2655 
2656     /* free the memory returned by g_hash_table_get_values() */
2657     g_list_free(mlist);
2658 
2659     /* destroy all monsters that have been genocided */
2660     game_remove_dead_monsters(nlarn);
2661 }
2662 
monster_is_genocided(monster_t monster_id)2663 int monster_is_genocided(monster_t monster_id)
2664 {
2665     g_assert(monster_id < MT_MAX);
2666     return nlarn->monster_genocided[monster_id];
2667 }
2668 
monster_effect_add(monster * m,effect * e)2669 effect *monster_effect_add(monster *m, effect *e)
2670 {
2671     g_assert(m != NULL && e != NULL);
2672     gboolean vis_effect = FALSE;
2673 
2674     if (e->type == ET_SLEEP && monster_flags(m, RES_SLEEP))
2675     {
2676         /* the monster is resistant to sleep */
2677         effect_destroy(e);
2678         e = NULL;
2679     }
2680     else if (e->type == ET_POISON && monster_flags(m, RES_POISON))
2681     {
2682         /* the monster is poison resistant */
2683         effect_destroy(e);
2684         e = NULL;
2685     }
2686     else if (e->type == ET_LEVITATION && monster_flags(m, FLY))
2687     {
2688         /* levitation has no effect on flying monsters */
2689         effect_destroy(e);
2690         e = NULL;
2691     }
2692     else if (e->type == ET_CONFUSION && monster_flags(m, RES_CONF))
2693     {
2694         /* the monster is resistant against confusion */
2695         effect_destroy(e);
2696         e = NULL;
2697     }
2698 
2699     /* one time effects */
2700     if (e && e->turns == 1)
2701     {
2702         switch (e->type)
2703         {
2704         case ET_INC_HP:
2705             {
2706                 int hp_orig = m->hp;
2707                 m->hp += min ((m->hp_max  * e->amount) / 100, m->hp_max);
2708 
2709                 if (m->hp > hp_orig)
2710                     vis_effect = TRUE;
2711             }
2712 
2713             break;
2714 
2715         case ET_MAX_HP:
2716             if (m->hp < m->hp_max)
2717             {
2718                 m->hp = m->hp_max;
2719                 vis_effect = TRUE;
2720             }
2721             break;
2722 
2723         default:
2724             /* nothing happens.. */
2725             break;
2726         }
2727     }
2728     else if (e)
2729     {
2730         /* multi-turn effects */
2731         e = effect_add(m->effects, e);
2732 
2733         /* if it's confusion, set the monster's "AI" accordingly */
2734         if (e && e->type == ET_CONFUSION) {
2735             monster_update_action(m, MA_CONFUSION);
2736         }
2737 
2738         /* charm monster turns monsters into servants */
2739         if (e && e->type == ET_CHARM_MONSTER) {
2740             monster_update_action(m, MA_SERVE);
2741         }
2742 
2743         /* no action if monster is held or sleeping */
2744         if (e && (e->type == ET_HOLD_MONSTER || e->type == ET_SLEEP || e->type == ET_TRAPPED))
2745         {
2746             monster_update_action(m, MA_REMAIN);
2747         }
2748     }
2749 
2750     /* show message if monster is visible */
2751     if (e && monster_in_sight(m)
2752         && effect_get_msg_m_start(e)
2753         && (e->turns > 0 || vis_effect))
2754     {
2755         log_add_entry(nlarn->log, effect_get_msg_m_start(e),
2756                       monster_get_name(m));
2757     }
2758 
2759     /* clean up one-time effects */
2760     if (e && e->turns == 1)
2761     {
2762         effect_destroy(e);
2763         e = NULL;
2764     }
2765 
2766     return e;
2767 }
2768 
monster_effect_del(monster * m,effect * e)2769 int monster_effect_del(monster *m, effect *e)
2770 {
2771     int result;
2772 
2773     g_assert(m != NULL && e != NULL);
2774 
2775     /* log info if the player can see the monster */
2776     if (monster_in_sight(m) && effect_get_msg_m_stop(e))
2777     {
2778         log_add_entry(nlarn->log, effect_get_msg_m_stop(e), monster_get_name(m));
2779     }
2780 
2781     if ((result = effect_del(m->effects, e)))
2782     {
2783         /* if confusion or charm is finished, set the AI back to the default */
2784         if (e->type == ET_CONFUSION || e->type == ET_CHARM_MONSTER) {
2785             monster_update_action(m, monster_default_ai(m));
2786         }
2787 
2788         /* end of holding, sleeping or being trapped */
2789         if (e->type == ET_HOLD_MONSTER || e->type == ET_SLEEP || e->type == ET_TRAPPED)
2790         {
2791             /* only reset the AI when no other "idling" effect is left */
2792             if (!monster_effect(m, ET_HOLD_MONSTER)
2793                     && !monster_effect(m, ET_SLEEP)
2794                     && !monster_effect(m, ET_TRAPPED))
2795             {
2796                 monster_update_action(m, monster_default_ai(m));
2797             }
2798         }
2799 
2800         effect_destroy(e);
2801     }
2802 
2803     return result;
2804 }
2805 
monster_effect_get(monster * m,effect_t type)2806 effect *monster_effect_get(monster *m , effect_t type)
2807 {
2808     g_assert(m != NULL && type < ET_MAX);
2809     return effect_get(m->effects, type);
2810 }
2811 
monster_effect(monster * m,effect_t type)2812 int monster_effect(monster *m, effect_t type)
2813 {
2814     g_assert(m != NULL && type < ET_MAX);
2815     return effect_query(m->effects, type);
2816 }
2817 
monster_effects_expire(monster * m)2818 void monster_effects_expire(monster *m)
2819 {
2820     guint idx = 0;
2821 
2822     g_assert(m != NULL);
2823 
2824     while (idx < m->effects->len)
2825     {
2826         gpointer effect_id = g_ptr_array_index(m->effects, idx);;
2827         effect *e = game_effect_get(nlarn, effect_id);
2828 
2829         if (e->type == ET_TRAPPED)
2830         {
2831             /* if the monster is incapable of movement don't decrease
2832                trapped counter */
2833             if (monster_effect(m, ET_HOLD_MONSTER)
2834                     || monster_effect(m, ET_SLEEP))
2835             {
2836                 idx++;
2837             }
2838         }
2839 
2840         if (effect_expire(e) == -1)
2841         {
2842             /* effect has expired */
2843             monster_effect_del(m, e);
2844         }
2845         else
2846         {
2847             idx++;
2848         }
2849     }
2850 }
2851 
monster_default_ai(monster * m)2852 static inline monster_action_t monster_default_ai(monster *m)
2853 {
2854     return monster_data[m->type].default_ai;
2855 }
2856 
monster_player_visible(monster * m)2857 static gboolean monster_player_visible(monster *m)
2858 {
2859     /* monster is blinded */
2860     if (monster_effect(m, ET_BLINDNESS))
2861         return FALSE;
2862 
2863     /* FIXME: this ought to be different per monster type */
2864     int monster_visrange = 7;
2865 
2866     if (player_effect(nlarn->p, ET_STEALTH))
2867     {
2868         /* if the player is stealthy monsters will only recognize him when
2869            standing next to him */
2870         monster_visrange = 1;
2871     }
2872     else if (monster_effect(m, ET_TRAPPED))
2873         monster_visrange = 2;
2874 
2875     /* determine if the monster can see the player */
2876     if (pos_distance(monster_pos(m), nlarn->p->pos) > monster_visrange)
2877         return FALSE;
2878 
2879     /* check if the player is invisible and if the monster has infravision */
2880     if (player_effect(nlarn->p, ET_INVISIBILITY)
2881         && !(monster_flags(m, INFRAVISION) || monster_effect(m, ET_INFRAVISION)))
2882         return FALSE;
2883 
2884     /* determine if player's position is visible from monster's position */
2885     return map_pos_is_visible(monster_map(m), m->pos, nlarn->p->pos);
2886 }
2887 
monster_attack_available(monster * m,attack_t type)2888 static gboolean monster_attack_available(monster *m, attack_t type)
2889 {
2890     gboolean available = FALSE;
2891     int pos = 1;
2892     int c = monster_attack_count(m);
2893 
2894     while (pos <= c)
2895     {
2896         attack att = monster_attack(m, pos);
2897 
2898         if (att.type == type)
2899         {
2900             available = TRUE;
2901             break;
2902         }
2903 
2904         pos++;
2905     }
2906 
2907     return available;
2908 }
2909 
monster_weapon_select(monster * m)2910 static item *monster_weapon_select(monster *m)
2911 {
2912     item *best = NULL;
2913 
2914     for (guint idx = 0; idx < inv_length(m->inv); idx++)
2915     {
2916         item *curr = inv_get(m->inv, idx);
2917 
2918         if (curr->type == IT_WEAPON)
2919         {
2920             if (best == NULL)
2921             {
2922                 best = curr;
2923             }
2924             else if (weapon_damage(curr) > weapon_damage(best))
2925             {
2926                 best = curr;
2927             }
2928         }
2929     }
2930 
2931     return best;
2932 }
2933 
monster_weapon_wield(monster * m,item * weapon)2934 static void monster_weapon_wield(monster *m, item *weapon)
2935 {
2936     /* FIXME: time management */
2937     /* FIXME: weapon effects */
2938     m->eq_weapon = weapon;
2939 
2940     /* show message if monster is visible */
2941     if (monster_in_sight(m))
2942     {
2943         gchar *buf = item_describe(weapon, player_item_identified(nlarn->p,
2944                                 weapon), TRUE, FALSE);
2945 
2946         log_add_entry(nlarn->log, "The %s wields %s.", monster_get_name(m), buf);
2947         g_free(buf);
2948     }
2949 }
2950 
monster_item_disenchant(monster * m,struct player * p)2951 static gboolean monster_item_disenchant(monster *m, struct player *p)
2952 {
2953     item *it;
2954 
2955     g_assert (m != NULL && p != NULL);
2956 
2957     /* disenchant random item */
2958     if (!inv_length(p->inventory))
2959     {
2960         /* empty inventory */
2961         return FALSE;
2962     }
2963 
2964     it = inv_get(p->inventory, rand_0n(inv_length(p->inventory)));
2965 
2966     /* log the attack */
2967     log_add_entry(nlarn->log, "The %s hits you.",
2968                   monster_get_name(m));
2969 
2970     /* Don't destroy the potion of cure dianthroritis. */
2971     if (it->type == IT_POTION && it->id == PO_CURE_DIANTHR)
2972         return (inv_length(p->inventory) > 1);
2973 
2974     // Blessed items have a 50% chance of resisting the disenchantment.
2975     if (it->blessed && chance(50))
2976     {
2977         gchar *desc = item_describe(it, player_item_known(nlarn->p, it),
2978                                     (it->count == 1), TRUE);
2979 
2980         desc[0] = g_ascii_toupper(desc[0]);
2981         log_add_entry(nlarn->log, "%s resist%s the attack.",
2982                       desc, (it->count == 1) ? "s" : "");
2983 
2984         it->blessed_known = TRUE;
2985         g_free(desc);
2986         return TRUE;
2987     }
2988     log_add_entry(nlarn->log, "You feel a sense of loss.");
2989 
2990     if (item_is_optimizable(it->type))
2991     {
2992         item_disenchant(it);
2993     }
2994     else
2995     {
2996         player_item_destroy(p, it);
2997     }
2998 
2999     return TRUE;
3000 }
3001 
3002 /**
3003  * Special monster attack: rust players armour.
3004  *
3005  * @param the attacking monster
3006  * @param the player
3007  *
3008  */
monster_item_rust(monster * m,struct player * p)3009 static gboolean monster_item_rust(monster *m __attribute__((unused)), struct player *p)
3010 {
3011     item **it;
3012 
3013     g_assert(m != NULL && p != NULL);
3014 
3015     /* get a random piece of armour to damage */
3016     if ((it = player_get_random_armour(p, FALSE)))
3017     {
3018         *it = item_erode(&p->inventory, *it, IET_RUST, TRUE);
3019         return TRUE;
3020     }
3021     else
3022     {
3023         log_add_entry(nlarn->log, "Nothing happens.");
3024         return FALSE;
3025     }
3026 }
3027 
monster_player_rob(monster * m,struct player * p,item_t item_type)3028 static gboolean monster_player_rob(monster *m, struct player *p, item_t item_type)
3029 {
3030     item *it = NULL;
3031 
3032     g_assert (m != NULL && p != NULL);
3033 
3034     /* if player has a device of no theft abort the theft */
3035     if (player_effect(p, ET_NOTHEFT))
3036         return FALSE;
3037 
3038     /* Leprechaun robs only gold */
3039     if (item_type == IT_GOLD)
3040     {
3041         /* get amount of gold pieces carried by the player */
3042         guint player_gold = player_get_gold(p);
3043 
3044         if (player_gold > 0)
3045         {
3046             /* steel gold carried by the player */
3047             it = item_new(IT_GOLD, rand_1n(1 + (player_gold >> 1)));
3048             player_remove_gold(p, it->count);
3049 
3050             log_add_entry(nlarn->log, "The %s picks your pocket. " \
3051                           "Your purse feels lighter.", monster_get_name(m));
3052         }
3053         else
3054         {
3055             /* grab gold at player's position */
3056             inventory **floor = map_ilist_at(monster_map(m), p->pos);
3057             if (inv_length_filtered(*floor, item_filter_gold))
3058             {
3059                 it = inv_get_filtered(*floor, 0, item_filter_gold);
3060                 inv_del_element(floor, it);
3061 
3062                 if (monster_in_sight(m))
3063                 {
3064                     log_add_entry(nlarn->log,
3065                             "The %s picks up some gold at your feet.",
3066                             monster_get_name(m));
3067                 }
3068             }
3069         }
3070     }
3071     else if (item_type == IT_ALL) /* must be the nymph */
3072     {
3073         if (inv_length(p->inventory))
3074         {
3075             gboolean was_equipped = FALSE;
3076 
3077             it = inv_get(p->inventory, rand_0n(inv_length(p->inventory)));
3078             gchar *buf = item_describe(it, player_item_known(p, it), FALSE, FALSE);
3079 
3080             if ((was_equipped = player_item_is_equipped(p, it)))
3081             {
3082                 if (it->cursed)
3083                 {
3084                     /* cursed items can't be stolen.. */
3085                     log_add_entry(nlarn->log, "The %s tries to steal %s but fails.",
3086                                   monster_get_name(m), buf);
3087 
3088                     it->blessed_known = TRUE;
3089                     g_free(buf);
3090 
3091                     /* return true as there are things to steal */
3092                     return TRUE;
3093                 }
3094 
3095                 player_item_unequip(p, NULL, it, TRUE);
3096             }
3097 
3098             if (it->count > 1)
3099             {
3100                 /* the player has multiple items. Steal only one. */
3101                 it = item_split(it, rand_1n(it->count));
3102                 g_free(buf);
3103                 buf = item_describe(it, player_item_known(p, it), FALSE, FALSE);
3104             }
3105             else
3106             {
3107                 /* this item's count is one. Steal exactly this one. */
3108                 inv_del_element(&p->inventory, it);
3109             }
3110 
3111             if (was_equipped)
3112             {
3113                 log_add_entry(nlarn->log, "The %s nimbly removes %s and steals it.",
3114                               monster_get_name(m), buf);
3115             }
3116             else
3117             {
3118                 log_add_entry(nlarn->log, "The %s picks your pocket and steals %s.",
3119                               monster_get_name(m), buf);
3120             }
3121 
3122             g_free(buf);
3123         }
3124     }
3125 
3126     /* if item / gold has been stolen, add it to the monster's inventory */
3127     if (it)
3128     {
3129         inv_add(&m->inv, it);
3130         return TRUE;
3131     }
3132     else
3133     {
3134         log_add_entry(nlarn->log, "The %s couldn't find anything to steal.",
3135                       monster_get_name(m));
3136 
3137         return FALSE;
3138     }
3139 }
3140 
monster_get_fortune(const char * fortune_file)3141 static char *monster_get_fortune(const char *fortune_file)
3142 {
3143     /* array of pointers to fortunes */
3144     static GPtrArray *fortunes = NULL;
3145 
3146     if (!fortunes)
3147     {
3148         /* read in the fortunes */
3149         char buffer[80];
3150         FILE *fortune_fd;
3151 
3152         /* open the file */
3153         fortune_fd = fopen(fortune_file, "r");
3154         if (fortune_fd == NULL)
3155         {
3156             /* can't find file */
3157             return "Help me! I can't find the fortune file!";
3158         }
3159 
3160         fortunes = g_ptr_array_new();
3161 
3162         /* read in the entire fortune file */
3163         while((fgets(buffer, 79, fortune_fd)))
3164         {
3165             /* replace EOL with \0 */
3166             size_t len = (size_t)(strchr(buffer, '\n') - (char *)&buffer);
3167             buffer[len] = '\0';
3168 
3169             /* keep the line */
3170             char *tmp = g_malloc((len + 1) * sizeof(char));
3171             memcpy(tmp, &buffer, (len + 1));
3172             g_ptr_array_add(fortunes, tmp);
3173         }
3174 
3175         fclose(fortune_fd);
3176     }
3177 
3178     return g_ptr_array_index(fortunes, rand_0n(fortunes->len));
3179 }
3180 
monster_find_next_pos_to(monster * m,position dest)3181 static position monster_find_next_pos_to(monster *m, position dest)
3182 {
3183     g_assert(m != NULL);
3184     g_assert(pos_valid(dest));
3185 
3186     /* next position */
3187     position npos = monster_pos(m);
3188 
3189     /* find the next step in the direction of dest */
3190     path *path = path_find(monster_map(m), monster_pos(m), dest,
3191                            monster_map_element(m));
3192 
3193     if (path && !g_queue_is_empty(path->path))
3194     {
3195         path_element *el = g_queue_pop_head(path->path);
3196         npos = el->pos;
3197     }
3198 
3199     /* clean up */
3200     if (path) path_destroy(path);
3201 
3202     return npos;
3203 }
3204 
monster_move_wander(monster * m,struct player * p)3205 static position monster_move_wander(monster *m, struct player *p __attribute__((unused)))
3206 {
3207     int tries = 0;
3208     position npos;
3209 
3210     if (m->leader)
3211     {
3212         /* monster is part of a pack */
3213         monster *leader = game_monster_get(nlarn, m->leader);
3214 
3215         if (!leader)
3216         {
3217             /* is seems that the leader was killed.
3218                From now on, wander aimlessly */
3219             m->leader = NULL;
3220         } else {
3221             /* stay close to the pack leader */
3222             if (pos_distance(monster_pos(m), monster_pos(leader)) > 4)
3223                 return monster_find_next_pos_to(m, monster_pos(leader));
3224         }
3225     }
3226 
3227     do
3228     {
3229         npos = pos_move(m->pos, rand_1n(GD_MAX));
3230         tries++;
3231     }
3232     while (tries < GD_MAX
3233             && !map_pos_validate(monster_map(m), npos, monster_map_element(m),
3234                                  FALSE));
3235 
3236     /* new position has not been found, reset to current position */
3237     if (tries == GD_MAX) npos = monster_pos(m);
3238 
3239     return npos;
3240 }
3241 
monster_move_attack(monster * m,struct player * p)3242 static position monster_move_attack(monster *m, struct player *p)
3243 {
3244     position npos = monster_pos(m);
3245 
3246     /* monster is standing next to player */
3247     if (pos_adjacent(monster_pos(m), m->player_pos) && (m->lastseen == 1))
3248     {
3249         monster_player_attack(m, p);
3250 
3251         /* monster's position might have changed (teleport) */
3252         if (!pos_identical(npos, monster_pos(m)))
3253             log_add_entry(nlarn->log, "The %s vanishes.", monster_name(m));
3254 
3255         return monster_pos(m);
3256     }
3257 
3258     /* monster is standing on a map exit and the player has left the map */
3259     if (pos_identical(monster_pos(m), m->player_pos)
3260             && map_is_exit_at(monster_map(m), monster_pos(m)))
3261     {
3262         int newmap;
3263 
3264         switch (map_sobject_at(monster_map(m), monster_pos(m)))
3265         {
3266         case LS_STAIRSDOWN:
3267         case LS_CAVERNS_ENTRY:
3268             newmap = Z(m->pos) + 1;
3269             break;
3270 
3271         case LS_STAIRSUP:
3272         case LS_CAVERNS_EXIT:
3273             newmap = Z(m->pos) - 1;
3274             break;
3275 
3276         case LS_ELEVATORDOWN:
3277             /* move into the volcano from the town */
3278             newmap = MAP_CMAX + 1;
3279             break;
3280 
3281         case LS_ELEVATORUP:
3282             /* volcano monster enters the town */
3283             newmap = 0;
3284             break;
3285 
3286         default:
3287             newmap = Z(m->pos);
3288             break;
3289         }
3290 
3291         /* change the map */
3292         monster_level_enter(m, game_map(nlarn, newmap));
3293 
3294         return monster_pos(m);
3295     }
3296 
3297     /* monster heads into the direction of the player. */
3298     npos = monster_find_next_pos_to(m, m->player_pos);
3299 
3300     /* No path found. Stop following player */
3301     if (!pos_valid(npos)) m->lastseen = 0;
3302 
3303     return npos;
3304 }
3305 
monster_move_confused(monster * m,struct player * p)3306 static position monster_move_confused(monster *m,
3307                                       __attribute__ ((unused)) struct player *p)
3308 {
3309     /* as the monster is confused, choose a random movement direction */
3310     return pos_move(monster_pos(m), rand_1n(GD_MAX));
3311 }
3312 
monster_move_flee(monster * m,struct player * p)3313 static position monster_move_flee(monster *m, struct player *p)
3314 {
3315     int dist = 0;
3316     position npos_tmp;
3317     position npos = monster_pos(m);
3318 
3319     for (int tries = 1; tries < GD_MAX; tries++)
3320     {
3321         /* try all fields surrounding the monster if the
3322          * distance between monster & player is greater */
3323         if (tries == GD_CURR)
3324             continue;
3325 
3326         npos_tmp = pos_move(monster_pos(m), tries);
3327 
3328         if (map_pos_validate(monster_map(m), npos_tmp, monster_map_element(m),
3329                              FALSE)
3330                 && pos_distance(p->pos, npos_tmp) > dist)
3331         {
3332             /* distance is bigger than current distance */
3333             npos = npos_tmp;
3334             dist = pos_distance(m->player_pos, npos_tmp);
3335         }
3336     }
3337 
3338     return npos;
3339 }
3340 
monster_move_serve(monster * m,struct player * p)3341 static position monster_move_serve(monster *m, struct player *p)
3342 {
3343     /* If a new position cannot be found, keep the current position */
3344     position npos = m->pos;
3345 
3346     /* generate a fov structure if not yet available */
3347     if (!m->fv)
3348         m->fv = fov_new();
3349 
3350     /* calculate the monster's fov */
3351     /* the monster gets a fov radius of 6 for now*/
3352     fov_calculate(m->fv, monster_map(m), m->pos, 6,
3353                   monster_flags(m, INFRAVISION)
3354                   || monster_effect(m, ET_INFRAVISION));
3355 
3356     /* a good servant always knows the masters position */
3357     if (pos_distance(monster_pos(m), p->pos) > 5)
3358     {
3359          /* if the distance to the player is too large, follow */
3360         npos = monster_find_next_pos_to(m, p->pos);
3361     }
3362     else
3363     {
3364         /* look for worthy foes */
3365         /* TODO: implement */
3366     }
3367 
3368     return npos;
3369 }
3370 
monster_move_civilian(monster * m,struct player * p)3371 static position monster_move_civilian(monster *m, struct player *p)
3372 {
3373     position npos = m->pos;
3374 
3375     /* civilians will pick a random location on the map, travel and remain
3376        there for the number of turns that is determined by their town
3377        person number. */
3378 
3379     if (!pos_valid(m->player_pos))
3380     {
3381         /* No target set -> find a new location to travel to.
3382            Civilians stay inside the town area. */
3383         rectangle town = rect_new(3, 4, MAP_MAX_X - 25, MAP_MAX_Y - 3);
3384         do
3385         {
3386             /* Ensure that the townsfolk do not loiter in locations
3387                important for the player. */
3388             m->player_pos = map_find_space_in(monster_map(m), town, LE_GROUND, FALSE);
3389         } while (map_sobject_at(monster_map(m), m->player_pos) != LS_NONE);
3390     }
3391 
3392     if (pos_identical(m->pos, m->player_pos)
3393         && m->lastseen >= (m->number + 25))
3394     {
3395         /* The person has stayed at the target position for long
3396            enough, thus reset the target position. */
3397         m->player_pos = pos_invalid;
3398     }
3399     else if (pos_valid(m->player_pos) && !pos_identical(m->pos, m->player_pos))
3400     {
3401         /* travel to the selected location */
3402         npos = monster_find_next_pos_to(m, m->player_pos);
3403 
3404         if (pos_identical(npos, m->player_pos))
3405         {
3406             /* arrived at target, thus reset the lastseen counter */
3407             m->lastseen = 1;
3408         }
3409         else if (pos_identical(npos, monster_pos(m)) || map_is_monster_at(monster_map(m), npos))
3410         {
3411             /* it seems there is no path to the target, or the destianation
3412                is blocked by another townsperson, thus get a new destination */
3413             m->player_pos = pos_invalid;
3414         }
3415     }
3416 
3417     /* check if the player is next to the civilian and not inside a building */
3418     if (pos_adjacent(m->pos, p->pos) && chance(40)
3419         && so_is_transparent(map_sobject_at(monster_map(m), p->pos)))
3420     {
3421         /* talk */
3422         log_add_entry(nlarn->log, "The %s says, \"%s\"",
3423                       monster_get_name(m),
3424                       monster_get_fortune(nlarn_fortunes));
3425     }
3426 
3427     /* change the town person's name from time to time */
3428     if (!fov_get(p->fv, m->pos) && chance(10))
3429     {
3430         m->number = rand_1n(40);
3431     }
3432 
3433     return npos;
3434 }
3435 
monster_is_carrying_item(monster * m,item_t type)3436 int monster_is_carrying_item(monster *m, item_t type)
3437 {
3438     inventory *inv = m->inv;
3439 
3440     for (guint idx = 0; idx < inv_length(inv); idx++)
3441     {
3442         item *it = inv_get(inv, idx);
3443         if (it->type == type)
3444             return TRUE;
3445     }
3446     return FALSE;
3447 }
3448 
monster_name(monster * m)3449 inline const char *monster_name(monster *m) {
3450     return monster_data[m->type].name;
3451 }
3452 
monster_level(monster * m)3453 inline int monster_level(monster *m)
3454 {
3455     return monster_data[m->type].level;
3456 }
3457 
monster_ac(monster * m)3458 inline int monster_ac(monster *m)
3459 {
3460     return monster_data[m->type].ac;
3461 }
3462 
monster_int(monster * m)3463 inline guint monster_int(monster *m)
3464 {
3465     return monster_data[m->type].intelligence
3466             + monster_effect(m, ET_HEROISM)
3467             - monster_effect(m, ET_DIZZINESS);
3468 }
3469 
monster_gold_chance(monster * m)3470 inline int monster_gold_chance(monster *m)
3471 {
3472     return monster_data[m->type].gold_chance;
3473 }
3474 
monster_gold_amount(monster * m)3475 inline int monster_gold_amount(monster *m)
3476 {
3477     return monster_data[m->type].gold;
3478 }
3479 
monster_exp(monster * m)3480 inline int monster_exp(monster *m)
3481 {
3482     return monster_data[m->type].exp;
3483 }
3484 
monster_size(monster * m)3485 inline int monster_size(monster *m)
3486 {
3487     return monster_data[m->type].size;
3488 }
3489 
monster_speed(monster * m)3490 inline int monster_speed(monster *m)
3491 {
3492     return monster_data[m->type].speed
3493             + monster_effect(m, ET_SPEED)
3494             + (monster_effect(m, ET_HEROISM) * 5)
3495             - monster_effect(m, ET_SLOWNESS)
3496             - (monster_effect(m, ET_DIZZINESS) * 5);
3497 }
3498 
monster_flags(monster * m,monster_flag f)3499 inline int monster_flags(monster *m, monster_flag f)
3500 {
3501     return monster_data[m->type].flags & f;
3502 }
3503 
monster_sound(monster * m)3504 inline const char *monster_sound(monster *m) {
3505     return monster_data[m->type].sound;
3506 }
3507 
monster_type_hp_max(monster_t type)3508 inline int monster_type_hp_max(monster_t type)
3509 {
3510     return monster_data[type].hp_max;
3511 }
3512 
monster_type_glyph(monster_t type)3513 inline char monster_type_glyph(monster_t type)
3514 {
3515     return monster_data[type].glyph;
3516 }
3517 
monster_type_name(monster_t type)3518 inline const char *monster_type_name(monster_t type)
3519 {
3520     return monster_data[type].name;
3521 }
3522 
monster_type_reroll_chance(monster_t type)3523 inline int monster_type_reroll_chance(monster_t type)
3524 {
3525     return monster_data[type].reroll_chance;
3526 }
3527 
3528 
monster_breath_hit(const GList * traj,const damage_originator * damo,gpointer data1,gpointer data2)3529 static gboolean monster_breath_hit(const GList *traj,
3530                                    const damage_originator *damo __attribute__((unused)),
3531                                    gpointer data1,
3532                                    gpointer data2 __attribute__((unused)))
3533 {
3534     monster *m;
3535     damage *dam = (damage *)data1;
3536     item_erosion_type iet;
3537     gboolean terminated = FALSE;
3538     position pos; pos_val(pos) = GPOINTER_TO_UINT(traj->data);
3539     map *mp = game_map(nlarn, Z(pos));
3540 
3541     /* determine if items should be eroded */
3542     switch (dam->type)
3543     {
3544     case DAM_FIRE:
3545         iet = IET_BURN;
3546         break;
3547 
3548     case DAM_ACID:
3549         iet = IET_CORRODE;
3550         break;
3551 
3552     case DAM_WATER:
3553         iet = IET_RUST;
3554         break;
3555 
3556     default:
3557         iet = IET_NONE;
3558         break;
3559     }
3560 
3561     if ((m = map_get_monster_at(mp, pos)))
3562     {
3563         /* The breath hit a monster. */
3564         if (monster_in_sight(m))
3565             log_add_entry(nlarn->log, "The %s hits the %s.",
3566                           monster_breath_data[dam->type].desc,
3567                           monster_get_name(m));
3568 
3569         /* erode the monster's inventory */
3570         if (iet && monster_inv(m))
3571             inv_erode(monster_inv(m), iet, FALSE, NULL);
3572 
3573         monster_damage_take(m, damage_copy(dam));
3574 
3575         /* the breath will sweep over small monsters */
3576         if (monster_size(m) >= LARGE)
3577             terminated = TRUE;
3578     }
3579 
3580     if (pos_identical(pos, nlarn->p->pos))
3581     {
3582         /* The breath hit the player */
3583         if (player_effect(nlarn->p, ET_REFLECTION))
3584         {
3585             /* The player reflects the breath. Actual handling of the reflection
3586                is done in map_trajectory, just give a message here. */
3587             log_add_entry(nlarn->log, "Your amulet reflects the %s!",
3588                           monster_breath_data[dam->type].desc);
3589         }
3590         else
3591         {
3592             /* No reflection -> player takes the damage */
3593             /* TODO: evasion!!! */
3594             log_add_entry(nlarn->log, "The %s hits you!",
3595                           monster_breath_data[dam->type].desc);
3596             player_damage_take(nlarn->p, damage_copy(dam), PD_MONSTER,
3597                                monster_type(dam->dam_origin.originator));
3598 
3599             /* erode the player's inventory */
3600             if (iet != IET_NONE)
3601             {
3602                 /*
3603                  * Filter equipped and exposed items, e.g.
3604                  * a body armour will not be affected by erosion
3605                  * when the player wears a cloak over it.
3606                  */
3607                 inv_erode(&nlarn->p->inventory, iet, TRUE,
3608                         player_item_filter_unequippable);
3609             }
3610 
3611             terminated = TRUE;
3612         }
3613     }
3614 
3615     if (iet > IET_NONE && map_ilist_at(mp, pos))
3616     {
3617         /* erode affected items */
3618         inv_erode(map_ilist_at(mp, pos), iet, fov_get(nlarn->p->fv, pos), NULL);
3619     }
3620 
3621 
3622     if (map_sobject_at(mp, pos) == LS_MIRROR && fov_get(nlarn->p->fv, pos))
3623     {
3624         /* A mirror will reflect the breath. Actual handling of the reflection
3625            is done in map_trajectory, just give a message here if the
3626            mirror is visible by the player. */
3627         log_add_entry(nlarn->log, "The mirror reflects the %s!",
3628                       monster_breath_data[dam->type].desc);
3629     }
3630 
3631     return terminated;
3632 }
3633