1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: actmonster.cpp
5 	Desc: behavior function for monsters
6 
7 	Copyright 2013-2016 (c) Turning Wheel LLC, all rights reserved.
8 	See LICENSE for details.
9 
10 -------------------------------------------------------------------------------*/
11 
12 #include "main.hpp"
13 #include "game.hpp"
14 #include "stat.hpp"
15 #include "messages.hpp"
16 #include "entity.hpp"
17 #include "items.hpp"
18 #include "monster.hpp"
19 #include "sound.hpp"
20 #include "shops.hpp"
21 #include "interface/interface.hpp"
22 #include "magic/magic.hpp"
23 #include "net.hpp"
24 #include "paths.hpp"
25 #include "collision.hpp"
26 #include "player.hpp"
27 #include "colors.hpp"
28 #include "scores.hpp"
29 #include "mod_tools.hpp"
30 
31 float limbs[NUMMONSTERS][20][3];
32 
33 // determines which monsters fight which
34 bool swornenemies[NUMMONSTERS][NUMMONSTERS] =
35 {
36 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // NOTHING
37 	{ 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0 }, // HUMAN
38 	{ 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1 }, // RAT
39 	{ 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1 }, // GOBLIN
40 	{ 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1 }, // SLIME
41 	{ 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1 }, // TROLL
42 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, // OCTOPUS
43 	{ 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1 }, // SPIDER
44 	{ 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1 }, // GHOUL
45 	{ 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1 }, // SKELETON
46 	{ 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1 }, // SCORPION
47 	{ 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1 }, // IMP
48 	{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, // BUGBEAR
49 	{ 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1 }, // GNOME
50 	{ 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1 }, // DEMON
51 	{ 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1 }, // SUCCUBUS
52 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, // MIMIC
53 	{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1 }, // LICH
54 	{ 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1 }, // MINOTAUR
55 	{ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, // DEVIL
56 	{ 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // SHOPKEEPER
57 	{ 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1 }, // KOBOLD
58 	{ 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1 }, // SCARAB
59 	{ 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1 }, // CRYSTALGOLEM
60 	{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, // INCUBUS
61 	{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, // VAMPIRE
62 	{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, // SHADOW
63 	{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1 }, // COCKATRICE
64 	{ 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1 }, // INSECTOID
65 	{ 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1 }, // GOATMAN
66 	{ 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0 }, // AUTOMATON
67 	{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, // LICH_ICE
68 	{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, // LICH_FIRE
69 	{ 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0 }, // SENTRYBOT
70 	{ 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0 }, // SPELLBOT
71 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // GYROBOT
72 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }  // DUMMYBOT
73 };
74 
75 // determines which monsters come to the aid of other monsters
76 bool monsterally[NUMMONSTERS][NUMMONSTERS] =
77 {
78 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // NOTHING
79 	{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1 }, // HUMAN
80 	{ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // RAT
81 	{ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // GOBLIN
82 	{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // SLIME
83 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // TROLL
84 	{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // OCTOPUS
85 	{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, // SPIDER
86 	{ 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0 }, // GHOUL
87 	{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0 }, // SKELETON
88 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, // SCORPION
89 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0 }, // IMP
90 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // BUGBEAR
91 	{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // GNOME
92 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0 }, // DEMON
93 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0 }, // SUCCUBUS
94 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // MIMIC
95 	{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0 }, // LICH
96 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // MINOTAUR
97 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0 }, // DEVIL
98 	{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // SHOPKEEPER
99 	{ 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // KOBOLD
100 	{ 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, // SCARAB
101 	{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // CRYSTALGOLEM
102 	{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0 }, // INCUBUS
103 	{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, // VAMPIRE
104 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, // SHADOW
105 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // COCKATRICE
106 	{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, // INSECTOID
107 	{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0 }, // GOATMAN
108 	{ 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // AUTOMATON
109 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, // LICH_ICE
110 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, // LICH_FIRE
111 	{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1 }, // SENTRYBOT
112 	{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1 }, // SPELLBOT
113 	{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1 }, // GYROBOT
114 	{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1 }  // DUMMYBOT
115 };
116 
117 // monster sight ranges
118 double sightranges[NUMMONSTERS] =
119 {
120 	256,  // NOTHING
121 	256,  // HUMAN
122 	128,  // RAT
123 	256,  // GOBLIN
124 	80,   // SLIME
125 	32,   // TROLL
126 	0,    // OCTOPUS
127 	96,   // SPIDER
128 	128,  // GHOUL
129 	192,  // SKELETON
130 	96,   // SCORPION
131 	256,  // IMP
132 	0,    // BUGBEAR
133 	128,  // GNOME
134 	256,  // DEMON
135 	256,  // SUCCUBUS
136 	0,    // MIMIC
137 	512,  // LICH
138 	512,  // MINOTAUR
139 	1024, // DEVIL
140 	256,  // SHOPKEEPER
141 	192,  // KOBOLD
142 	512,  // SCARAB
143 	192,   // CRYSTALGOLEM
144 	256,  // INCUBUS
145 	192,  // VAMPIRE
146 	768,  // SHADOW
147 	256,  // COCKATRICE
148 	256,  // INSECTOID
149 	256,  // GOATMAN
150 	192,  // AUTOMATON
151 	512,  // LICH_ICE
152 	512,  // LICH_FIRE
153 	256,  // SENTRYBOT
154 	192,  // SPELLBOT
155 	256,  // GYROBOT
156 	32    // DUMMYBOT
157 };
158 
159 int monsterGlobalAnimationMultiplier = 10;
160 int monsterGlobalAttackTimeMultiplier = 1;
161 
162 /*-------------------------------------------------------------------------------
163 
164 	summonMonster
165 
166 	summons a monster near (but not at) the given location
167 
168 -------------------------------------------------------------------------------*/
169 
summonMonsterClient(Monster creature,long x,long y,Uint32 uid)170 void summonMonsterClient(Monster creature, long x, long y, Uint32 uid)
171 {
172 	Entity* entity = summonMonster(creature, x, y);
173 	entity->flags[INVISIBLE] = false;
174 	entity->setUID(uid);
175 }
176 
summonMonster(Monster creature,long x,long y,bool forceLocation)177 Entity* summonMonster(Monster creature, long x, long y, bool forceLocation)
178 {
179 	Entity* entity = newEntity(-1, 1, map.entities, map.creatures); //Monster entity.
180 	//Set the monster's variables.
181 	entity->sizex = 4;
182 	entity->sizey = 4;
183 	entity->x = x;
184 	entity->y = y;
185 	entity->z = 6;
186 	entity->yaw = (rand() % 360) * PI / 180.0;
187 	entity->behavior = &actMonster;
188 	entity->flags[UPDATENEEDED] = true;
189 	entity->flags[INVISIBLE] = true;
190 	entity->ranbehavior = true;
191 	entity->skill[5] = nummonsters;
192 
193 	Stat* myStats = nullptr;
194 	if ( multiplayer != CLIENT )
195 	{
196 		// Need to give the entity its list stuff.
197 		// create an empty first node for traversal purposes
198 		node_t* node = nullptr;
199 		node = list_AddNodeFirst(&entity->children);
200 		node->element = nullptr;
201 		node->deconstructor = &emptyDeconstructor;
202 
203 		myStats = new Stat(creature + 1000);
204 		node = list_AddNodeLast(&entity->children); //ASSUMING THIS ALREADY EXISTS WHEN THIS FUNCTION IS CALLED.
205 		node->element = myStats;
206 		node->size = sizeof(myStats);
207 		//node->deconstructor = myStats->~Stat;
208 		if ( entity->parent )
209 		{
210 			myStats->leader_uid = entity->parent;
211 			entity->parent = 0;
212 		}
213 		myStats->type = creature;
214 	}
215 	else
216 	{
217 		//Give dummy stats.
218 		entity->clientStats = new Stat(creature + 1000);
219 	}
220 
221 	// Find a free tile next to the source and then spawn it there.
222 	if ( multiplayer != CLIENT && !forceLocation )
223 	{
224 		if ( entityInsideSomething(entity) )
225 		{
226 			do
227 			{
228 				entity->x = x;
229 				entity->y = y - 16;
230 				if (!entityInsideSomething(entity))
231 				{
232 					break;    // north
233 				}
234 				entity->x = x;
235 				entity->y = y + 16;
236 				if (!entityInsideSomething(entity))
237 				{
238 					break;    // south
239 				}
240 				entity->x = x - 16;
241 				entity->y = y;
242 				if (!entityInsideSomething(entity))
243 				{
244 					break;    // west
245 				}
246 				entity->x = x + 16;
247 				entity->y = y;
248 				if (!entityInsideSomething(entity))
249 				{
250 					break;    // east
251 				}
252 				entity->x = x + 16;
253 				entity->y = y - 16;
254 				if (!entityInsideSomething(entity))
255 				{
256 					break;    // northeast
257 				}
258 				entity->x = x + 16;
259 				entity->y = y + 16;
260 				if (!entityInsideSomething(entity))
261 				{
262 					break;    // southeast
263 				}
264 				entity->x = x - 16;
265 				entity->y = y - 16;
266 				if (!entityInsideSomething(entity))
267 				{
268 					break;    // northwest
269 				}
270 				entity->x = x - 16;
271 				entity->y = y + 16;
272 				if (!entityInsideSomething(entity))
273 				{
274 					break;    // southwest
275 				}
276 
277 				// we can't have monsters in walls...
278 				list_RemoveNode(entity->mynode);
279 				entity = nullptr;
280 				break;
281 			}
282 			while ( 1 );
283 		}
284 	}
285 
286 	if ( entity )
287 	{
288 		switch ( creature )
289 		{
290 			case RAT:
291 				entity->focalx = limbs[RAT][0][0]; // 0
292 				entity->focaly = limbs[RAT][0][1]; // 0
293 				entity->focalz = limbs[RAT][0][2]; // 0
294 				break;
295 			case SCORPION:
296 				entity->focalx = limbs[SCORPION][0][0]; // 0
297 				entity->focaly = limbs[SCORPION][0][1]; // 0
298 				entity->focalz = limbs[SCORPION][0][2]; // 0
299 				break;
300 			case HUMAN:
301 				entity->z = -1;
302 				entity->focalx = limbs[HUMAN][0][0]; // 0
303 				entity->focaly = limbs[HUMAN][0][1]; // 0
304 				entity->focalz = limbs[HUMAN][0][2]; // -1.5
305 				break;
306 			case GOBLIN:
307 				entity->z = 0;
308 				entity->focalx = limbs[GOBLIN][0][0]; // 0
309 				entity->focaly = limbs[GOBLIN][0][1]; // 0
310 				entity->focalz = limbs[GOBLIN][0][2]; // -1.75
311 				break;
312 			case SLIME:
313 				if ( multiplayer != CLIENT )
314 				{
315 					myStats->LVL = 7;
316 				}
317 				break;
318 			case SUCCUBUS:
319 				entity->z = -1;
320 				entity->focalx = limbs[SUCCUBUS][0][0]; // 0
321 				entity->focaly = limbs[SUCCUBUS][0][1]; // 0
322 				entity->focalz = limbs[SUCCUBUS][0][2]; // -1.5
323 				break;
324 			case TROLL:
325 				entity->z = -1.5;
326 				entity->focalx = limbs[TROLL][0][0]; // 1
327 				entity->focaly = limbs[TROLL][0][1]; // 0
328 				entity->focalz = limbs[TROLL][0][2]; // -2
329 				break;
330 			case SHOPKEEPER:
331 				entity->z = -1;
332 				entity->focalx = limbs[SHOPKEEPER][0][0]; // 0
333 				entity->focaly = limbs[SHOPKEEPER][0][1]; // 0
334 				entity->focalz = limbs[SHOPKEEPER][0][2]; // -1.5
335 				break;
336 			case SKELETON:
337 				entity->z = -.5;
338 				entity->focalx = limbs[SKELETON][0][0]; // 0
339 				entity->focaly = limbs[SKELETON][0][1]; // 0
340 				entity->focalz = limbs[SKELETON][0][2]; // -1.5
341 				break;
342 			case MINOTAUR:
343 				entity->z = -6;
344 				entity->focalx = limbs[MINOTAUR][0][0]; // 0
345 				entity->focaly = limbs[MINOTAUR][0][1]; // 0
346 				entity->focalz = limbs[MINOTAUR][0][2]; // 0
347 				break;
348 			case GHOUL:
349 				entity->z = -.25;
350 				entity->focalx = limbs[GHOUL][0][0]; // 0
351 				entity->focaly = limbs[GHOUL][0][1]; // 0
352 				entity->focalz = limbs[GHOUL][0][2]; // -1.5
353 				break;
354 			case DEMON:
355 				entity->z = -8.5;
356 				entity->focalx = limbs[DEMON][0][0]; // -1
357 				entity->focaly = limbs[DEMON][0][1]; // 0
358 				entity->focalz = limbs[DEMON][0][2]; // -1.25
359 				break;
360 			case SPIDER:
361 				entity->z = 4.5;
362 				entity->focalx = limbs[SPIDER][0][0]; // -3
363 				entity->focaly = limbs[SPIDER][0][1]; // 0
364 				entity->focalz = limbs[SPIDER][0][2]; // -1
365 				break;
366 			case LICH:
367 				entity->focalx = limbs[LICH][0][0]; // -0.75
368 				entity->focaly = limbs[LICH][0][1]; // 0
369 				entity->focalz = limbs[LICH][0][2]; // 0
370 				entity->z = -2;
371 				entity->yaw = PI;
372 				entity->sprite = 274;
373 				entity->skill[29] = 120;
374 				break;
375 			case CREATURE_IMP:
376 				entity->z = -4.5;
377 				entity->focalx = limbs[CREATURE_IMP][0][0]; // 0
378 				entity->focaly = limbs[CREATURE_IMP][0][1]; // 0
379 				entity->focalz = limbs[CREATURE_IMP][0][2]; // -1.75
380 				break;
381 			case GNOME:
382 				entity->z = 2.25;
383 				entity->focalx = limbs[GNOME][0][0]; // 0
384 				entity->focaly = limbs[GNOME][0][1]; // 0
385 				entity->focalz = limbs[GNOME][0][2]; // -2
386 				break;
387 			case DEVIL:
388 				entity->focalx = limbs[DEVIL][0][0]; // 0
389 				entity->focaly = limbs[DEVIL][0][1]; // 0
390 				entity->focalz = limbs[DEVIL][0][2]; // 0
391 				entity->z = -4;
392 				entity->sizex = 20;
393 				entity->sizey = 20;
394 				entity->yaw = PI;
395 				break;
396 			case KOBOLD:
397 				entity->z = 2.25;
398 				entity->focalx = limbs[KOBOLD][0][0]; // 0
399 				entity->focaly = limbs[KOBOLD][0][1]; // 0
400 				entity->focalz = limbs[KOBOLD][0][2]; // -2
401 				break;
402 			case SCARAB:
403 				entity->focalx = limbs[SCARAB][0][0]; // 0
404 				entity->focaly = limbs[SCARAB][0][1]; // 0
405 				entity->focalz = limbs[SCARAB][0][2]; // 0
406 				break;
407 			case CRYSTALGOLEM:
408 				entity->z = -1.5;
409 				entity->focalx = limbs[CRYSTALGOLEM][0][0]; // 1
410 				entity->focaly = limbs[CRYSTALGOLEM][0][1]; // 0
411 				entity->focalz = limbs[CRYSTALGOLEM][0][2]; // -2
412 				break;
413 			case INCUBUS:
414 				entity->z = -1;
415 				entity->focalx = limbs[INCUBUS][0][0]; // 0
416 				entity->focaly = limbs[INCUBUS][0][1]; // 0
417 				entity->focalz = limbs[INCUBUS][0][2]; // -1.5
418 				break;
419 			case VAMPIRE:
420 				entity->z = -1;
421 				entity->focalx = limbs[HUMAN][0][0]; // 0
422 				entity->focaly = limbs[HUMAN][0][1]; // 0
423 				entity->focalz = limbs[HUMAN][0][2]; // -1.5
424 				break;
425 			case SHADOW:
426 				entity->z = -1;
427 				entity->focalx = limbs[SHADOW][0][0]; // 0
428 				entity->focaly = limbs[SHADOW][0][1]; // 0
429 				entity->focalz = limbs[SHADOW][0][2]; // -1.75
430 				break;
431 			case COCKATRICE:
432 				entity->z = -4.5;
433 				entity->focalx = limbs[COCKATRICE][0][0]; // 0
434 				entity->focaly = limbs[COCKATRICE][0][1]; // 0
435 				entity->focalz = limbs[COCKATRICE][0][2]; // -1.75
436 				break;
437 			case INSECTOID:
438 				entity->z = 0;
439 				entity->focalx = limbs[INSECTOID][0][0]; // 0
440 				entity->focaly = limbs[INSECTOID][0][1]; // 0
441 				entity->focalz = limbs[INSECTOID][0][2]; // -1.75
442 				if ( multiplayer != CLIENT )
443 				{
444 					if ( !strncmp(map.name, "Sokoban", 7) || !strncmp(map.name, "The Labyrinth", 13) )
445 					{
446 						strcpy(myStats->name, "lesser insectoid");
447 					}
448 				}
449 				break;
450 			case GOATMAN:
451 				entity->z = 0;
452 				entity->focalx = limbs[GOATMAN][0][0]; // 0
453 				entity->focaly = limbs[GOATMAN][0][1]; // 0
454 				entity->focalz = limbs[GOATMAN][0][2]; // -1.75
455 				break;
456 			case AUTOMATON:
457 				entity->z = -.5;
458 				entity->focalx = limbs[AUTOMATON][0][0]; // 0
459 				entity->focaly = limbs[AUTOMATON][0][1]; // 0
460 				entity->focalz = limbs[AUTOMATON][0][2]; // -1.5
461 				break;
462 			case LICH_ICE:
463 				entity->focalx = limbs[LICH_ICE][0][0]; // -0.75
464 				entity->focaly = limbs[LICH_ICE][0][1]; // 0
465 				entity->focalz = limbs[LICH_ICE][0][2]; // 0
466 				entity->z = -1.2;
467 				entity->yaw = PI;
468 				entity->sprite = 650;
469 				break;
470 			case LICH_FIRE:
471 				entity->focalx = limbs[LICH_FIRE][0][0]; // -0.75
472 				entity->focaly = limbs[LICH_FIRE][0][1]; // 0
473 				entity->focalz = limbs[LICH_FIRE][0][2]; // 0
474 				entity->z = -1.2;
475 				entity->yaw = PI;
476 				entity->sprite = 646;
477 				break;
478 			case SENTRYBOT:
479 				entity->z = 0;
480 				entity->focalx = limbs[SENTRYBOT][0][0];
481 				entity->focaly = limbs[SENTRYBOT][0][1];
482 				entity->focalz = limbs[SENTRYBOT][0][2];
483 				break;
484 			case SPELLBOT:
485 				entity->z = 0;
486 				entity->focalx = limbs[SPELLBOT][0][0];
487 				entity->focaly = limbs[SPELLBOT][0][1];
488 				entity->focalz = limbs[SPELLBOT][0][2];
489 				break;
490 			case GYROBOT:
491 				entity->z = 5;
492 				entity->focalx = limbs[GYROBOT][0][0];
493 				entity->focaly = limbs[GYROBOT][0][1];
494 				entity->focalz = limbs[GYROBOT][0][2];
495 				break;
496 			case DUMMYBOT:
497 				entity->z = 0;
498 				entity->focalx = limbs[DUMMYBOT][0][0];
499 				entity->focaly = limbs[DUMMYBOT][0][1];
500 				entity->focalz = limbs[DUMMYBOT][0][2];
501 				break;
502 			default:
503 				//Spawn a potato.
504 				list_RemoveNode(entity->mynode);
505 				return nullptr;
506 				break;
507 		}
508 		if ( entity )
509 		{
510 			nummonsters++;
511 		}
512 		if ( multiplayer == SERVER )
513 		{
514 			strcpy((char*)net_packet->data, "SUMM");
515 			SDLNet_Write32((Uint32)creature, &net_packet->data[4]);
516 			SDLNet_Write32((Uint32)entity->x, &net_packet->data[8]);
517 			SDLNet_Write32((Uint32)entity->y, &net_packet->data[12]);
518 			SDLNet_Write32(entity->getUID(), &net_packet->data[16]);
519 			net_packet->len = 20;
520 
521 			for ( int c = 1; c < MAXPLAYERS; c++ )
522 			{
523 				if ( client_disconnected[c] )
524 				{
525 					continue;
526 				}
527 				net_packet->address.host = net_clients[c - 1].host;
528 				net_packet->address.port = net_clients[c - 1].port;
529 				sendPacketSafe(net_sock, -1, net_packet, c - 1);
530 			}
531 		}
532 		return entity;
533 	}
534 	return nullptr;
535 }
536 
summonManyMonster(Monster creature)537 void summonManyMonster(Monster creature)
538 {
539 	for ( long x = 1; x < map.width - 1; ++x )
540 	{
541 		for ( long y = 1; y < map.height - 1; ++y )
542 		{
543 			summonMonster(creature, (x * 16) + 8, (y * 16) + 8);
544 		}
545 	}
546 }
547 
548 /*-------------------------------------------------------------------------------
549 
550 	monsterMoveAside
551 
552 	Causes the monster given in *entity to move aside from the entity given
553 	in *my
554 
555 -------------------------------------------------------------------------------*/
556 
monsterMoveAside(Entity * my,Entity * entity)557 bool monsterMoveAside(Entity* my, Entity* entity)
558 {
559 	if ( !my || !entity )
560 	{
561 		return false;
562 	}
563 
564 	if ( my->monsterState != 0 )
565 	{
566 		return false;
567 	}
568 
569 	int x = 0, y = 0;
570 	if ( cos(entity->yaw) > .4 )
571 	{
572 		y += 16;
573 		if ( checkObstacle(my->x, my->y + y, my, NULL) )
574 		{
575 			y -= 32;
576 			if ( checkObstacle(my->x, my->y + y, my, NULL) )
577 			{
578 				y = 0;
579 				x += 16;
580 			}
581 		}
582 	}
583 	else if ( cos(entity->yaw) < -.4 )
584 	{
585 		y -= 16;
586 		if ( checkObstacle(my->x, my->y + y, my, NULL) )
587 		{
588 			y += 32;
589 			if ( checkObstacle(my->x, my->y + y, my, NULL) )
590 			{
591 				y = 0;
592 				x -= 16;
593 			}
594 		}
595 	}
596 	if ( sin(entity->yaw) > .4 )
597 	{
598 		x -= 16;
599 		if ( checkObstacle(my->x + x, my->y, my, NULL) )
600 		{
601 			x += 32;
602 			if ( checkObstacle(my->x + x, my->y, my, NULL) )
603 			{
604 				x = 0;
605 				y += 16;
606 			}
607 		}
608 	}
609 	else if ( sin(entity->yaw) < -.4 )
610 	{
611 		x += 16;
612 		if ( checkObstacle(my->x + x, my->y, my, NULL) )
613 		{
614 			x -= 32;
615 			if ( checkObstacle(my->x + x, my->y, my, NULL) )
616 			{
617 				x = 0;
618 				y -= 16;
619 			}
620 		}
621 	}
622 
623 	// move away
624 	if ( x != 0 || y != 0 )
625 	{
626 		my->monsterState = MONSTER_STATE_PATH;
627 		my->monsterReleaseAttackTarget();
628 		my->monsterTargetX = my->x + x;
629 		my->monsterTargetY = my->y + y;
630 		serverUpdateEntitySkill(my, 0);
631 		return true;
632 	}
633 	return false;
634 }
635 
636 /*-------------------------------------------------------------------------------
637 
638 	act*
639 
640 	The following function describes an entity behavior. The function
641 	takes a pointer to the entity that uses it as an argument.
642 
643 -------------------------------------------------------------------------------*/
644 
645 int devilstate = 0;
646 int devilacted = 0;
647 int devilroar = 0;
648 int devilsummonedtimes = 0;
649 
makeFollower(int monsterclicked,bool ringconflict,char namesays[64],Entity * my,Stat * myStats)650 bool makeFollower(int monsterclicked, bool ringconflict, char namesays[64], Entity* my, Stat* myStats)
651 {
652 	if ( !myStats )
653 	{
654 		return false;
655 	}
656 	if ( ringconflict )
657 	{
658 		//Instant fail if ring of conflict is in effect. You have no allies!
659 		return false;
660 	}
661 
662 	if ( !players[monsterclicked] || !players[monsterclicked]->entity || !stats[monsterclicked] )
663 	{
664 		return false;
665 	}
666 
667 	Monster race = my->getRace();
668 
669 	if ( myStats->leader_uid != 0 )
670 	{
671 		//Handle the "I have a leader!" situation.
672 		if ( myStats->leader_uid == players[monsterclicked]->entity->getUID() )
673 		{
674 			//Follows this player already!
675 			if ( my->getINT() > -2 && race == HUMAN )
676 			{
677 				messagePlayer(monsterclicked, language[535], namesays, stats[monsterclicked]->name);
678 			}
679 			else
680 			{
681 				messagePlayer(monsterclicked, language[534], namesays);
682 			}
683 		}
684 		else
685 		{
686 			//Follows somebody else.
687 			if ( my->getINT() > -2 && race == HUMAN )
688 			{
689 				messagePlayer(monsterclicked, language[536], namesays, stats[monsterclicked]->name);
690 			}
691 			else
692 			{
693 				messagePlayer(monsterclicked, language[534], namesays);
694 			}
695 
696 			if ( my->checkFriend(players[monsterclicked]->entity) )
697 			{
698 				//If friendly, move aside.
699 				monsterMoveAside(my, players[monsterclicked]->entity);
700 			}
701 		}
702 
703 		return false;
704 	}
705 
706 	bool canAlly = false;
707 	if ( skillCapstoneUnlocked(monsterclicked, PRO_LEADERSHIP) )
708 	{
709 		int allowedFollowers = 8;
710 		int numFollowers = 0;
711 		for ( node_t* node = stats[monsterclicked]->FOLLOWERS.first; node; node = node->next )
712 		{
713 			Entity* follower = nullptr;
714 			if ( (Uint32*)node->element )
715 			{
716 				follower = uidToEntity(*((Uint32*)node->element));
717 			}
718 			if ( follower )
719 			{
720 				Stat* followerStats = follower->getStats();
721 				if ( followerStats )
722 				{
723 					if ( !(followerStats->type == SENTRYBOT || followerStats->type == GYROBOT
724 						|| followerStats->type == SPELLBOT || followerStats->type == DUMMYBOT) )
725 					{
726 						++numFollowers;
727 					}
728 				}
729 			}
730 		}
731 		if ( allowedFollowers > numFollowers )
732 		{
733 			//Can control humans & goblins & goatmen & insectoids.
734 			//TODO: Control humanoids in general? Or otherwise something from each tileset.
735 			if ( (stats[monsterclicked]->type == HUMAN && race == HUMAN)
736 				|| race == GOBLIN
737 				|| race == AUTOMATON
738 				|| race == GOATMAN
739 				|| race == INSECTOID
740 				|| race == GYROBOT )
741 			{
742 				canAlly = true;
743 			}
744 
745 			//TODO: If enemies (e.g. goblin or an angry human), require the player to be unseen by this creature to gain control of it.
746 
747 			if ( stats[monsterclicked]->type == SKELETON )
748 			{
749 				if ( race == GHOUL )
750 				{
751 					canAlly = true;
752 				}
753 			}
754 			else if ( stats[monsterclicked]->type == VAMPIRE )
755 			{
756 				if ( race == VAMPIRE && strncmp(myStats->name, "Bram Kindly", 11) )
757 				{
758 					canAlly = true;
759 				}
760 			}
761 			else if ( stats[monsterclicked]->type == INCUBUS || stats[monsterclicked]->type == SUCCUBUS )
762 			{
763 				if ( race == INCUBUS || race == SUCCUBUS )
764 				{
765 					canAlly = true;
766 				}
767 				else if ( race == HUMAN && (myStats->EFFECTS[EFF_DRUNK] || myStats->EFFECTS[EFF_CONFUSED])
768 					&& stats[monsterclicked]->type != INCUBUS )
769 				{
770 					canAlly = true;
771 					if ( myStats->EFFECTS[EFF_CONFUSED] )
772 					{
773 						my->setEffect(EFF_CONFUSED, false, 0, false);
774 					}
775 				}
776 			}
777 			else if ( stats[monsterclicked]->type == GOATMAN )
778 			{
779 				if ( race == GOATMAN )
780 				{
781 					canAlly = true;
782 				}
783 			}
784 			else if ( stats[monsterclicked]->type == GOBLIN )
785 			{
786 				if ( race == GOBLIN )
787 				{
788 					canAlly = true;
789 				}
790 			}
791 			else if ( stats[monsterclicked]->type == AUTOMATON )
792 			{
793 				if ( race == AUTOMATON || race == HUMAN )
794 				{
795 					canAlly = true;
796 				}
797 			}
798 			else if ( stats[monsterclicked]->type == RAT )
799 			{
800 				if ( race == RAT )
801 				{
802 					canAlly = true;
803 				}
804 			}
805 			else if ( stats[monsterclicked]->type == SPIDER )
806 			{
807 				if ( race == SPIDER	|| race == SCARAB || race == SCORPION )
808 				{
809 					canAlly = true;
810 				}
811 			}
812 			else if ( stats[monsterclicked]->type == INSECTOID )
813 			{
814 				if ( race == INSECTOID || race == SCARAB || race == SCORPION )
815 				{
816 					canAlly = true;
817 				}
818 			}
819 			else if ( stats[monsterclicked]->type == TROLL )
820 			{
821 				if ( race == TROLL )
822 				{
823 					canAlly = true;
824 				}
825 			}
826 			else if ( stats[monsterclicked]->type == CREATURE_IMP )
827 			{
828 				if ( race == CREATURE_IMP && !(!strncmp(map.name, "Boss", 4) || !strncmp(map.name, "Hell Boss", 9)) )
829 				{
830 					canAlly = true; // non-boss imps
831 				}
832 			}
833 			if ( myStats->monsterForceAllegiance == Stat::MONSTER_FORCE_PLAYER_RECRUITABLE )
834 			{
835 				canAlly = true;
836 			}
837 		}
838 		else
839 		{
840 			messagePlayer(monsterclicked, language[3482]);
841 		}
842 	}
843 	else
844 	{
845 		bool tryAlly = my->checkFriend(players[monsterclicked]->entity);
846 		if ( stats[monsterclicked]->type == SUCCUBUS )
847 		{
848 			if ( race == HUMAN && (myStats->EFFECTS[EFF_DRUNK] || myStats->EFFECTS[EFF_CONFUSED]) )
849 			{
850 				tryAlly = true;
851 			}
852 		}
853 		if ( strcmp(myStats->name, "") && !monsterNameIsGeneric(*myStats)
854 			&& ((stats[monsterclicked]->PROFICIENCIES[PRO_LEADERSHIP] + stats[monsterclicked]->CHR) < 60) )
855 		{
856 			if ( race != HUMAN )
857 			{
858 				tryAlly = false;
859 				messagePlayer(monsterclicked, language[3481], myStats->name);
860 			}
861 		}
862 		if ( tryAlly )
863 		{
864 			if ( myStats->leader_uid == 0 )
865 			{
866 				int allowedFollowers = std::min(8, std::max(4, 2 * (stats[monsterclicked]->PROFICIENCIES[PRO_LEADERSHIP] / 20)));
867 				int numFollowers = 0;
868 				for ( node_t* node = stats[monsterclicked]->FOLLOWERS.first; node; node = node->next )
869 				{
870 					Entity* follower = nullptr;
871 					if ( (Uint32*)node->element )
872 					{
873 						follower = uidToEntity(*((Uint32*)node->element));
874 					}
875 					if ( follower )
876 					{
877 						Stat* followerStats = follower->getStats();
878 						if ( followerStats )
879 						{
880 							if ( !(followerStats->type == SENTRYBOT || followerStats->type == GYROBOT
881 								|| followerStats->type == SPELLBOT || followerStats->type == DUMMYBOT) )
882 							{
883 								++numFollowers;
884 							}
885 						}
886 					}
887 				}
888 				if ( allowedFollowers > numFollowers )
889 				{
890 					if ( race == AUTOMATON || race == GYROBOT )
891 					{
892 						canAlly = true;
893 					}
894 					if ( stats[monsterclicked]->type == HUMAN )
895 					{
896 						canAlly = true;
897 					}
898 					else if ( stats[monsterclicked]->type == SKELETON )
899 					{
900 						if ( race == GHOUL )
901 						{
902 							canAlly = true;
903 						}
904 					}
905 					else if ( stats[monsterclicked]->type == VAMPIRE )
906 					{
907 						if ( race == VAMPIRE && strncmp(myStats->name, "Bram Kindly", 11) )
908 						{
909 							canAlly = true;
910 						}
911 					}
912 					else if ( stats[monsterclicked]->type == SUCCUBUS || stats[monsterclicked]->type == INCUBUS )
913 					{
914 						if ( race == INCUBUS || race == SUCCUBUS )
915 						{
916 							canAlly = true;
917 						}
918 						else if ( race == HUMAN && (myStats->EFFECTS[EFF_DRUNK] || myStats->EFFECTS[EFF_CONFUSED])
919 							&& stats[monsterclicked]->type != INCUBUS )
920 						{
921 							canAlly = true;
922 							if ( stats[monsterclicked]->type == SUCCUBUS )
923 							{
924 								steamAchievementClient(monsterclicked, "BARONY_ACH_TEMPTRESS");
925 							}
926 							if ( myStats->EFFECTS[EFF_CONFUSED] )
927 							{
928 								my->setEffect(EFF_CONFUSED, false, 0, false);
929 							}
930 						}
931 					}
932 					else if ( stats[monsterclicked]->type == GOATMAN )
933 					{
934 						if ( race == GOATMAN )
935 						{
936 							canAlly = true;
937 						}
938 					}
939 					else if ( stats[monsterclicked]->type == GOBLIN )
940 					{
941 						if ( race == GOBLIN )
942 						{
943 							canAlly = true;
944 						}
945 					}
946 					else if ( stats[monsterclicked]->type == RAT )
947 					{
948 						if ( race == RAT )
949 						{
950 							canAlly = true;
951 						}
952 					}
953 					else if ( stats[monsterclicked]->type == AUTOMATON )
954 					{
955 						if ( race == AUTOMATON || race == HUMAN )
956 						{
957 							canAlly = true;
958 						}
959 					}
960 					else if ( stats[monsterclicked]->type == SPIDER )
961 					{
962 						if ( race == SPIDER	|| race == SCARAB || race == SCORPION )
963 						{
964 							canAlly = true;
965 						}
966 					}
967 					else if ( stats[monsterclicked]->type == INSECTOID )
968 					{
969 						if ( race == INSECTOID || race == SCARAB || race == SCORPION )
970 						{
971 							canAlly = true;
972 						}
973 					}
974 					else if ( stats[monsterclicked]->type == TROLL )
975 					{
976 						if ( race == TROLL )
977 						{
978 							canAlly = true;
979 						}
980 					}
981 					else if ( stats[monsterclicked]->type == CREATURE_IMP )
982 					{
983 						if ( race == CREATURE_IMP && !(!strncmp(map.name, "Boss", 4) || !strncmp(map.name, "Hell Boss", 9)) )
984 						{
985 							canAlly = true; // non-boss imps
986 						}
987 					}
988 					if ( myStats->monsterForceAllegiance == Stat::MONSTER_FORCE_PLAYER_RECRUITABLE )
989 					{
990 						canAlly = true;
991 					}
992 				}
993 				else
994 				{
995 					if ( numFollowers >= 8 )
996 					{
997 						messagePlayer(monsterclicked, language[3482]);
998 					}
999 					else
1000 					{
1001 						messagePlayer(monsterclicked, language[3480]);
1002 					}
1003 				}
1004 			}
1005 		}
1006 	}
1007 
1008 	if ( !canAlly )
1009 	{
1010 		//This one does not want to join your ever-enlarging cult.
1011 		if ( my->getINT() > -2 && race == HUMAN )
1012 		{
1013 			//Human tells off the player.
1014 			messagePlayer(monsterclicked, language[530 + rand() % 4], namesays);
1015 			// move aside
1016 			monsterMoveAside(my, players[monsterclicked]->entity);
1017 		}
1018 		else
1019 		{
1020 			messagePlayer(monsterclicked, language[534], namesays);
1021 		}
1022 
1023 		return false;
1024 	}
1025 
1026 	node_t* newNode = list_AddNodeLast(&stats[monsterclicked]->FOLLOWERS);
1027 	newNode->deconstructor = &defaultDeconstructor;
1028 	Uint32* myuid = (Uint32*) (malloc(sizeof(Uint32)));
1029 	newNode->element = myuid;
1030 	*myuid = my->getUID();
1031 
1032 	if ( my->getINT() > -2 && race == HUMAN )
1033 	{
1034 		messagePlayer(monsterclicked, language[525 + rand() % 4], namesays, stats[monsterclicked]->name);
1035 	}
1036 	else
1037 	{
1038 		//This one can't speak, so generic "The %s decides to follow you!" message.
1039 		messagePlayerMonsterEvent(monsterclicked, 0xFFFFFFFF, *myStats, language[529], language[529], MSG_COMBAT);
1040 	}
1041 	spawnMagicEffectParticles(my->x, my->y, my->z, 685);
1042 	monsterMoveAside(my, players[monsterclicked]->entity);
1043 	players[monsterclicked]->entity->increaseSkill(PRO_LEADERSHIP);
1044 	my->monsterState = MONSTER_STATE_WAIT; // be ready to follow
1045 	myStats->leader_uid = players[monsterclicked]->entity->getUID();
1046 	my->monsterAllyIndex = monsterclicked;
1047 	if ( multiplayer == SERVER )
1048 	{
1049 		serverUpdateEntitySkill(my, 42); // update monsterAllyIndex for clients.
1050 	}
1051 	if ( monsterclicked > 0 && multiplayer == SERVER )
1052 	{
1053 		//Tell the client he suckered somebody into his cult.
1054 		strcpy((char*) (net_packet->data), "LEAD");
1055 		SDLNet_Write32((Uint32 )my->getUID(), &net_packet->data[4]);
1056 		strcpy((char*)(&net_packet->data[8]), myStats->name);
1057 		net_packet->data[8 + strlen(myStats->name)] = 0;
1058 		net_packet->address.host = net_clients[monsterclicked - 1].host;
1059 		net_packet->address.port = net_clients[monsterclicked - 1].port;
1060 		net_packet->len = 8 + strlen(myStats->name) + 1;
1061 		sendPacketSafe(net_sock, -1, net_packet, monsterclicked - 1);
1062 
1063 		serverUpdateAllyStat(monsterclicked, my->getUID(), myStats->LVL, myStats->HP, myStats->MAXHP, myStats->type);
1064 	}
1065 
1066 	// update flags for colors.
1067 	my->flags[USERFLAG2] = true;
1068 	serverUpdateEntityFlag(my, USERFLAG2);
1069 	if ( monsterChangesColorWhenAlly(myStats) )
1070 	{
1071 		int bodypart = 0;
1072 		for ( node_t* node = my->children.first; node != nullptr; node = node->next )
1073 		{
1074 			if ( bodypart >= LIMB_HUMANOID_TORSO )
1075 			{
1076 				Entity* tmp = (Entity*)node->element;
1077 				if ( tmp )
1078 				{
1079 					tmp->flags[USERFLAG2] = true;
1080 					//serverUpdateEntityFlag(tmp, USERFLAG2);
1081 				}
1082 			}
1083 			++bodypart;
1084 		}
1085 	}
1086 
1087 	for ( node_t* node = stats[monsterclicked]->FOLLOWERS.first; node != nullptr; node = node->next )
1088 	{
1089 		Uint32* c = (Uint32*)node->element;
1090 		Entity* entity = nullptr;
1091 		if ( c )
1092 		{
1093 			entity = uidToEntity(*c);
1094 		}
1095 		if ( entity && entity->monsterTarget == *myuid )
1096 		{
1097 			entity->monsterReleaseAttackTarget(); // followers stop punching the new target.
1098 		}
1099 	}
1100 
1101 	if ( !FollowerMenu.recentEntity && monsterclicked == clientnum )
1102 	{
1103 		FollowerMenu.recentEntity = my;
1104 	}
1105 
1106 	if ( (stats[monsterclicked]->type != HUMAN && stats[monsterclicked]->type != AUTOMATON) && myStats->type == HUMAN )
1107 	{
1108 		steamAchievementClient(monsterclicked, "BARONY_ACH_PITY_FRIEND");
1109 	}
1110 	if ( stats[monsterclicked]->type == VAMPIRE && myStats->type == VAMPIRE )
1111 	{
1112 		if ( !strncmp(myStats->name, "young vampire", strlen("young vampire")) )
1113 		{
1114 			steamAchievementClient(monsterclicked, "BARONY_ACH_YOUNG_BLOOD");
1115 		}
1116 	}
1117 	if ( myStats->type == HUMAN && stats[monsterclicked]->type == HUMAN && stats[monsterclicked]->appearance == 0
1118 		&& stats[monsterclicked]->playerRace == RACE_AUTOMATON )
1119 	{
1120 		achievementObserver.updatePlayerAchievement(monsterclicked, AchievementObserver::Achievement::BARONY_ACH_REAL_BOY,
1121 			AchievementObserver::AchievementEvent::REAL_BOY_HUMAN_RECRUIT);
1122 	}
1123 	if ( stats[monsterclicked]->type == TROLL && myStats->type == TROLL )
1124 	{
1125 		serverUpdatePlayerGameplayStats(monsterclicked, STATISTICS_FORUM_TROLL, AchievementObserver::FORUM_TROLL_RECRUIT_TROLL);
1126 	}
1127 	if ( stats[monsterclicked]->appearance == 0
1128 		&& (stats[monsterclicked]->playerRace == RACE_INCUBUS || stats[monsterclicked]->playerRace == RACE_SUCCUBUS) )
1129 	{
1130 		if ( myStats->type == HUMAN )
1131 		{
1132 			if ( stats[monsterclicked]->type == INCUBUS || stats[monsterclicked]->type == SUCCUBUS )
1133 			{
1134 				serverUpdatePlayerGameplayStats(monsterclicked, STATISTICS_PIMPING_AINT_EASY, 1);
1135 			}
1136 		}
1137 		else if ( myStats->type == INCUBUS || myStats->type == SUCCUBUS )
1138 		{
1139 			serverUpdatePlayerGameplayStats(monsterclicked, STATISTICS_PIMPING_AINT_EASY, 1);
1140 		}
1141 	}
1142 	if ( stats[monsterclicked]->appearance == 0
1143 		&& (stats[monsterclicked]->playerRace == RACE_GOBLIN) )
1144 	{
1145 		if ( myStats->type == GOBLIN )
1146 		{
1147 			serverUpdatePlayerGameplayStats(monsterclicked, STATISTICS_TRIBE_SUBSCRIBE, 1);
1148 		}
1149 	}
1150 	if ( client_classes[monsterclicked] == CLASS_SHAMAN )
1151 	{
1152 		if ( players[monsterclicked]->entity->effectPolymorph != 0 || players[monsterclicked]->entity->effectShapeshift != 0 )
1153 		{
1154 			achievementObserver.playerAchievements[monsterclicked].socialButterfly++;
1155 		}
1156 	}
1157 
1158 	return true;
1159 }
1160 
sentrybotPickSpotNoise(Entity * my,Stat * myStats)1161 void sentrybotPickSpotNoise(Entity* my, Stat* myStats)
1162 {
1163 	if ( !my || !myStats )
1164 	{
1165 		return;
1166 	}
1167 	bool doSpecialNoise = false;
1168 	switch ( my->getUID() % 3 )
1169 	{
1170 		case 0:
1171 			if ( my->ticks % 60 < 20 )
1172 			{
1173 				doSpecialNoise = true;
1174 			}
1175 		case 1:
1176 			if ( my->ticks % 60 >= 20 && my->ticks % 60 < 40 )
1177 			{
1178 				doSpecialNoise = true;
1179 			}
1180 		case 2:
1181 			if ( my->ticks % 60 >= 40 )
1182 			{
1183 				doSpecialNoise = true;
1184 			}
1185 			break;
1186 		default:
1187 			break;
1188 	}
1189 	if ( doSpecialNoise && rand() % 3 == 0 )
1190 	{
1191 		MONSTER_SOUND = playSoundEntity(my, 466 + rand() % 3, 64);
1192 	}
1193 	else
1194 	{
1195 		MONSTER_SOUND = playSoundEntity(my, MONSTER_SPOTSND + rand() % MONSTER_SPOTVAR, 128);
1196 	}
1197 }
1198 
actMonster(Entity * my)1199 void actMonster(Entity* my)
1200 {
1201 	if (!my)
1202 	{
1203 		return;
1204 	}
1205 
1206 	int x, y, c, i;
1207 	double dist, dist2;
1208 	list_t* path;
1209 	node_t* node, *node2;
1210 	pathnode_t* pathnode;
1211 	double dir;
1212 	double tangent;
1213 	Stat* myStats;
1214 	Entity* entity;
1215 	Stat* hitstats = NULL;
1216 	bool hasrangedweapon = false;
1217 	bool myReflex;
1218 	Sint32 previousMonsterState = my->monsterState;
1219 
1220 	// deactivate in menu
1221 	if ( intro )
1222 	{
1223 		return;
1224 	}
1225 
1226 	// this is mostly a SERVER function.
1227 	// however, there is a small part for clients:
1228 	if ( multiplayer == CLIENT )
1229 	{
1230 		if ( !MONSTER_INIT && my->sprite >= 100 && !(my->sprite >= 163 && my->sprite <= 166) )
1231 		{
1232 			MONSTER_INIT = 1;
1233 
1234 			// make two empty nodes
1235 			node = list_AddNodeLast(&my->children);
1236 			node->element = nullptr;
1237 			node->deconstructor = &emptyDeconstructor;
1238 			node->size = 0;
1239 			node = list_AddNodeLast(&my->children);
1240 			node->element = nullptr;
1241 			node->deconstructor = &emptyDeconstructor;
1242 			node->size = 0;
1243 			if ( my->isPlayerHeadSprite() )   // human heads
1244 			{
1245 				initHuman(my, nullptr);
1246 			}
1247 			else if ( my->sprite == 131 || my->sprite == 265 )     // rat
1248 			{
1249 				initRat(my, nullptr);
1250 			}
1251 			else if ( my->sprite == 180 )     // goblin head
1252 			{
1253 				initGoblin(my, nullptr);
1254 			}
1255 			else if ( my->sprite == 196 || my->sprite == 266 )     // scorpion body
1256 			{
1257 				initScorpion(my, nullptr);
1258 			}
1259 			else if ( my->sprite == 190 )     // succubus head
1260 			{
1261 				initSuccubus(my, nullptr);
1262 			}
1263 			else if ( my->sprite == 204 )     // troll head
1264 			{
1265 				initTroll(my, nullptr);
1266 			}
1267 			else if ( my->sprite == 217 )     // shopkeeper head
1268 			{
1269 				initShopkeeper(my, nullptr);
1270 			}
1271 			else if ( my->sprite == 229 )     // skeleton head
1272 			{
1273 				initSkeleton(my, nullptr);
1274 			}
1275 			else if ( my->sprite == 239 )     // minotaur waist
1276 			{
1277 				initMinotaur(my, nullptr);
1278 			}
1279 			else if ( my->sprite == 246 )     // ghoul head
1280 			{
1281 				initGhoul(my, nullptr);
1282 			}
1283 			else if ( my->sprite == 258 )     // demon head
1284 			{
1285 				initDemon(my, nullptr);
1286 			}
1287 			else if ( my->sprite == 267 )     // spider body
1288 			{
1289 				initSpider(my, nullptr);
1290 			}
1291 			else if ( my->sprite == 274 )     // lich body
1292 			{
1293 				initLich(my, nullptr);
1294 			}
1295 			else if ( my->sprite == 289 )     // imp head
1296 			{
1297 				initImp(my, nullptr);
1298 			}
1299 			else if ( my->sprite == 295 )     // gnome head
1300 			{
1301 				initGnome(my, nullptr);
1302 			}
1303 			else if ( my->sprite == 304 )     // devil torso
1304 			{
1305 				initDevil(my, nullptr);
1306 			}
1307 			else if ( my->sprite == 475 )     // crystal golem head
1308 			{
1309 				initCrystalgolem(my, nullptr);
1310 			}
1311 			else if ( my->sprite == 413 )     // cockatrice head
1312 			{
1313 				initCockatrice(my, nullptr);
1314 			}
1315 			else if ( my->sprite == 467 )     // automaton torso
1316 			{
1317 				initAutomaton(my, NULL);
1318 			}
1319 			else if ( my->sprite == 429 || my->sprite == 430 )     // scarab
1320 			{
1321 				initScarab(my, nullptr);
1322 			}
1323 			else if ( my->sprite == 421 )     // kobold head
1324 			{
1325 				initKobold(my, nullptr);
1326 			}
1327 			else if ( my->sprite == 481 )     // shadow head
1328 			{
1329 				initShadow(my, nullptr);
1330 			}
1331 			else if ( my->sprite == 437 )     // vampire head
1332 			{
1333 				initVampire(my, nullptr);
1334 			}
1335 			else if ( my->sprite == 445 )     // incubus head
1336 			{
1337 				initIncubus(my, nullptr);
1338 			}
1339 			else if ( my->sprite == 455 )     // insectoid head
1340 			{
1341 				initInsectoid(my, nullptr);
1342 			}
1343 			else if ( my->sprite == 463 )     // goatman head
1344 			{
1345 				initGoatman(my, nullptr);
1346 			}
1347 			else if ( my->sprite == 646 )     // lich body
1348 			{
1349 				initLichFire(my, nullptr);
1350 			}
1351 			else if ( my->sprite == 650 )     // lich body
1352 			{
1353 				initLichIce(my, nullptr);
1354 			}
1355 			else if ( my->sprite == 872 || my->sprite == 885 )     // sentrybot head
1356 			{
1357 				initSentryBot(my, nullptr);
1358 			}
1359 			else if ( my->sprite == 886 )     // gyrobot head
1360 			{
1361 				initGyroBot(my, nullptr);
1362 			}
1363 			else if ( my->sprite == 889 )     // dummybot head
1364 			{
1365 				initDummyBot(my, nullptr);
1366 			}
1367 		}
1368 		else
1369 		{
1370 			my->flags[BURNABLE] = true;
1371 			if ( my->isPlayerHeadSprite() )   // human heads
1372 			{
1373 				humanMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1374 			}
1375 			else if ( my->sprite == 131 || my->sprite == 265 )     // rat
1376 			{
1377 				ratAnimate(my, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1378 			}
1379 			else if ( my->sprite == 180 )     // goblin head
1380 			{
1381 				goblinMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1382 			}
1383 			else if ( my->sprite == 196 || my->sprite == 266 )     // scorpion body
1384 			{
1385 				scorpionAnimate(my, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1386 			}
1387 			else if ( my->sprite == 190 )     // succubus head
1388 			{
1389 				succubusMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1390 			}
1391 			else if ( my->sprite == 204 )     // troll head
1392 			{
1393 				trollMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1394 			}
1395 			else if ( my->sprite == 217 )     // shopkeeper head
1396 			{
1397 				shopkeeperMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1398 			}
1399 			else if ( my->sprite == 229 )     // skeleton head
1400 			{
1401 				my->flags[BURNABLE] = false;
1402 				skeletonMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1403 			}
1404 			else if ( my->sprite == 239 )     // minotaur waist
1405 			{
1406 				minotaurMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1407 				actMinotaurCeilingBuster(my);
1408 			}
1409 			else if ( my->sprite == 246 )     // ghoul head
1410 			{
1411 				ghoulMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1412 			}
1413 			else if ( my->sprite == 258 )     // demon head
1414 			{
1415 				my->flags[BURNABLE] = false;
1416 				demonMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1417 				actDemonCeilingBuster(my);
1418 			}
1419 			else if ( my->sprite == 267 )     // spider body
1420 			{
1421 				spiderMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1422 			}
1423 			else if ( my->sprite == 274 )     // lich body
1424 			{
1425 				my->flags[BURNABLE] = false;
1426 				lichAnimate(my, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1427 			}
1428 			else if ( my->sprite == 289 )     // imp head
1429 			{
1430 				my->flags[BURNABLE] = false;
1431 				impMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1432 			}
1433 			else if ( my->sprite == 295 )     // gnome head
1434 			{
1435 				gnomeMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1436 			}
1437 			else if ( my->sprite == 304 )     // devil torso
1438 			{
1439 				my->flags[BURNABLE] = false;
1440 				devilMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1441 			}
1442 			else if ( my->sprite == 421 )     // kobold head
1443 			{
1444 				koboldMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1445 			}
1446 			else if ( my->sprite == 429 || my->sprite == 430 )     // scarab
1447 			{
1448 				scarabAnimate(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1449 			}
1450 			else if ( my->sprite == 475 )     // crystal golem head
1451 			{
1452 				crystalgolemMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1453 			}
1454 			else if ( my->sprite == 445 )     // incubus head
1455 			{
1456 				incubusMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1457 			}
1458 			else if ( my->sprite == 437 )     // vampire head
1459 			{
1460 				vampireMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1461 			}
1462 			else if ( my->sprite == 481 )     // shadow head
1463 			{
1464 				shadowMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1465 			}
1466 			else if ( my->sprite == 413 )     // cockatrice head
1467 			{
1468 				cockatriceMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1469 			}
1470 			else if ( my->sprite == 455 )     // insectoid head
1471 			{
1472 				insectoidMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1473 			}
1474 			else if ( my->sprite == 463 )     // goatman head
1475 			{
1476 				goatmanMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1477 			}
1478 			else if ( my->sprite == 467 )     // automaton head
1479 			{
1480 				automatonMoveBodyparts(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1481 			}
1482 			else if ( my->sprite == 646 )     // lich body
1483 			{
1484 				my->flags[BURNABLE] = false;
1485 				lichFireAnimate(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1486 			}
1487 			else if ( my->sprite == 650 )     // lich body
1488 			{
1489 				my->flags[BURNABLE] = false;
1490 				lichIceAnimate(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1491 			}
1492 			else if ( my->sprite == 872 )     // sentrybot head
1493 			{
1494 				my->flags[BURNABLE] = false;
1495 				sentryBotAnimate(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1496 			}
1497 			else if ( my->sprite == 885 )     // sentrybot head
1498 			{
1499 				my->flags[BURNABLE] = false;
1500 				sentryBotAnimate(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1501 			}
1502 			else if ( my->sprite == 886 )     // gyrobot head
1503 			{
1504 				my->flags[BURNABLE] = false;
1505 				gyroBotAnimate(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1506 			}
1507 			else if ( my->sprite == 889 )     // dummybot head
1508 			{
1509 				dummyBotAnimate(my, NULL, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
1510 			}
1511 			else
1512 			{
1513 				my->flags[BURNABLE] = false;
1514 			}
1515 
1516 			if ( !intro )
1517 			{
1518 				my->handleEffectsClient();
1519 			}
1520 
1521 			// request entity update (check if I've been deleted)
1522 			if ( ticks % (TICKS_PER_SECOND * 5) == my->getUID() % (TICKS_PER_SECOND * 5) )
1523 			{
1524 				strcpy((char*)net_packet->data, "ENTE");
1525 				net_packet->data[4] = clientnum;
1526 				SDLNet_Write32(my->getUID(), &net_packet->data[5]);
1527 				net_packet->address.host = net_server.host;
1528 				net_packet->address.port = net_server.port;
1529 				net_packet->len = 9;
1530 				sendPacketSafe(net_sock, -1, net_packet, 0);
1531 			}
1532 		}
1533 		return;
1534 	}
1535 
1536 	if ( ticks % (TICKS_PER_SECOND) == my->getUID() % (TICKS_PER_SECOND / 2) )
1537 	{
1538 		myReflex = true;
1539 	}
1540 	else
1541 	{
1542 		myReflex = false;
1543 	}
1544 
1545 	// init
1546 	if ( MONSTER_INIT < 2 )   // 0 means no initialization, 1 means stats are initialized
1547 	{
1548 		my->skill[2] = -4; // tells clients to set this entity behavior to actMonster
1549 		myStats = my->getStats();
1550 		if (myStats)
1551 		{
1552 			myStats->monster_sound = NULL;
1553 			my->flags[BURNABLE] = true;
1554 			switch ( myStats->type )
1555 			{
1556 				case HUMAN:
1557 					initHuman(my, myStats);
1558 					break;
1559 				case RAT:
1560 					initRat(my, myStats);
1561 					break;
1562 				case GOBLIN:
1563 					initGoblin(my, myStats);
1564 					break;
1565 				case SLIME:
1566 					my->flags[BURNABLE] = false;
1567 					initSlime(my, myStats);
1568 					break;
1569 				case SCORPION:
1570 					initScorpion(my, myStats);
1571 					break;
1572 				case SUCCUBUS:
1573 					initSuccubus(my, myStats);
1574 					break;
1575 				case TROLL:
1576 					initTroll(my, myStats);
1577 					break;
1578 				case SHOPKEEPER:
1579 					initShopkeeper(my, myStats);
1580 					break;
1581 				case SKELETON:
1582 					my->flags[BURNABLE] = false;
1583 					initSkeleton(my, myStats);
1584 					break;
1585 				case MINOTAUR:
1586 					initMinotaur(my, myStats);
1587 					break;
1588 				case GHOUL:
1589 					initGhoul(my, myStats);
1590 					break;
1591 				case DEMON:
1592 					my->flags[BURNABLE] = false;
1593 					initDemon(my, myStats);
1594 					break;
1595 				case SPIDER:
1596 					initSpider(my, myStats);
1597 					break;
1598 				case LICH:
1599 					my->flags[BURNABLE] = false;
1600 					initLich(my, myStats);
1601 					break;
1602 				case CREATURE_IMP:
1603 					my->flags[BURNABLE] = false;
1604 					initImp(my, myStats);
1605 					break;
1606 				case GNOME:
1607 					initGnome(my, myStats);
1608 					break;
1609 				case DEVIL:
1610 					my->flags[BURNABLE] = false;
1611 					devilstate = 0;
1612 					devilacted = 0;
1613 					devilroar = 0;
1614 					devilsummonedtimes = 0;
1615 					initDevil(my, myStats);
1616 					break;
1617 				case KOBOLD:
1618 					initKobold (my, myStats);
1619 					break;
1620 				case SCARAB:
1621 					initScarab (my, myStats);
1622 					break;
1623 				case CRYSTALGOLEM:
1624 					initCrystalgolem (my, myStats);
1625 					break;
1626 				case INCUBUS:
1627 					initIncubus (my, myStats);
1628 					break;
1629 				case VAMPIRE:
1630 					initVampire (my, myStats);
1631 					break;
1632 				case SHADOW:
1633 					initShadow (my, myStats);
1634 					break;
1635 				case COCKATRICE:
1636 					initCockatrice (my, myStats);
1637 					break;
1638 				case INSECTOID:
1639 					initInsectoid (my, myStats);
1640 					break;
1641 				case GOATMAN:
1642 					initGoatman (my, myStats);
1643 					break;
1644 				case AUTOMATON:
1645 					my->flags[BURNABLE] = false;
1646 					initAutomaton (my, myStats);
1647 					break;
1648 				case LICH_ICE:
1649 					my->flags[BURNABLE] = false;
1650 					initLichIce (my, myStats);
1651 					my->monsterLichBattleState = LICH_BATTLE_IMMOBILE;
1652 					break;
1653 				case LICH_FIRE:
1654 					my->flags[BURNABLE] = false;
1655 					initLichFire (my, myStats);
1656 					my->monsterLichBattleState = LICH_BATTLE_IMMOBILE;
1657 					break;
1658 				case SENTRYBOT:
1659 					my->sprite = 872;
1660 					my->flags[BURNABLE] = false;
1661 					initSentryBot(my, myStats);
1662 					break;
1663 				case SPELLBOT:
1664 					my->sprite = 885;
1665 					my->flags[BURNABLE] = false;
1666 					initSentryBot(my, myStats);
1667 					break;
1668 				case GYROBOT:
1669 					my->flags[BURNABLE] = false;
1670 					initGyroBot(my, myStats);
1671 					break;
1672 				case DUMMYBOT:
1673 					initDummyBot(my, myStats);
1674 					break;
1675 				default:
1676 					break; //This should never be reached.
1677 			}
1678 		}
1679 
1680 		MONSTER_INIT = 2;
1681 		if ( myStats->type != LICH && myStats->type != DEVIL )
1682 		{
1683 			my->monsterLookDir = (rand() % 360) * PI / 180;
1684 		}
1685 		else
1686 		{
1687 			my->monsterLookDir = PI;
1688 		}
1689 		my->monsterLookTime = rand() % 120;
1690 		my->monsterMoveTime = rand() % 10;
1691 		MONSTER_SOUND = NULL;
1692 		if ( MONSTER_NUMBER == -1 )
1693 		{
1694 			MONSTER_NUMBER = nummonsters;
1695 			nummonsters++;
1696 		}
1697 		/*if( rand()%20==0 ) { // 20% chance
1698 			MONSTER_STATE = 2; // start hunting the player immediately
1699 			MONSTER_TARGET = rand()%numplayers;
1700 			MONSTER_TARGETX = players[MONSTER_TARGET]->x;
1701 			MONSTER_TARGETY = players[MONSTER_TARGET]->y;
1702 		} else {
1703 			MONSTER_TARGET = -1;
1704 		}*/
1705 
1706 		if ( uidToEntity(my->monsterTarget) == nullptr )
1707 		{
1708 			my->monsterTarget = 0;
1709 		}
1710 
1711 		/*// create an empty first node for traversal purposes //GOING TO ASSUME THIS ALREADY EXISTS WHEN THIS FUNCTION IS CALLED.
1712 		node = list_AddNodeFirst(my->children);
1713 		node->element = NULL;
1714 		node->deconstructor = &emptyDeconstructor;*/
1715 
1716 		// assign stats to the monster
1717 		//myStats = (Stat *) malloc(sizeof(Stat)); //GOING TO ASSUME THIS ALREADY EXISTS WHEN THIS FUNCTION IS CALLED.
1718 		//myStats->type = RAT; //GOING TO ASSUME THIS IS ALREADY PROPERLY SET WHEN THE FUNCTION IS CALLED.
1719 		//TODO: Move the rest of this into the monster specific init functions.
1720 		/*node = list_AddNodeLast(my->children); //ASSUMING THIS ALREADY EXISTS WHEN THIS FUNCTION IS CALLED.
1721 		node->element = myStats;
1722 		node->deconstructor = &defaultDeconstructor;*/
1723 
1724 		return;
1725 	}
1726 
1727 	myStats = my->getStats();
1728 	if ( myStats == NULL )
1729 	{
1730 		printlog("ERROR: monster entity at %p has no stats struct!", my);
1731 		return;
1732 	}
1733 	myStats->defending = false;
1734 	myStats->sneaking = 0;
1735 
1736 	// levitation
1737 	bool levitating = isLevitating(myStats);
1738 
1739 	if ( myStats->type == MINOTAUR )
1740 	{
1741 		int c;
1742 		for ( c = 0; c < MAXPLAYERS; c++ )
1743 		{
1744 			assailant[c] = true; // as long as this is active, combat music doesn't turn off
1745 			assailantTimer[c] = COMBAT_MUSIC_COOLDOWN;
1746 		}
1747 	}
1748 
1749 	if ( myStats->type == SHADOW && my->monsterTarget != 0 )
1750 	{
1751 		for ( int c = 0; c < MAXPLAYERS; ++c )
1752 		{
1753 			if ( players[c] && players[c]->entity && players[c]->entity->getUID() == my->monsterTarget )
1754 			{
1755 				assailant[c] = true; //Keeps combat music on as long as a shadow is hunting you down down down!
1756 				assailantTimer[c] = COMBAT_MUSIC_COOLDOWN;
1757 				break;
1758 			}
1759 		}
1760 	}
1761 
1762 	if ( my->ticks == 120 + MONSTER_NUMBER )
1763 	{
1764 		serverUpdateBodypartIDs(my);
1765 	}
1766 
1767 	// some special herx behavior
1768 	if ( myStats->type == LICH )
1769 	{
1770 		// destroying room lights
1771 		if ( myStats->HP <= myStats->MAXHP / 2 )
1772 		{
1773 			node_t* node, *nextnode;
1774 			bool foundlights = false;
1775 			for ( node = map.entities->first; node != nullptr; node = nextnode )
1776 			{
1777 				nextnode = node->next;
1778 				Entity* tempEntity = (Entity*)node->element;
1779 
1780 				if ( tempEntity->behavior == &actTorch || tempEntity->behavior == &actCampfire )
1781 				{
1782 					foundlights = true;
1783 					if ( tempEntity->light )
1784 					{
1785 						list_RemoveNode(tempEntity->light->node);
1786 						tempEntity->light = nullptr;
1787 					}
1788 					list_RemoveNode(tempEntity->mynode);
1789 				}
1790 			}
1791 			if ( foundlights )
1792 			{
1793 #ifdef USE_FMOD
1794 				if ( MONSTER_SOUND )
1795 				{
1796 					FMOD_Channel_Stop(MONSTER_SOUND);
1797 				}
1798 #elif defined USE_OPENAL
1799 				if ( MONSTER_SOUND )
1800 				{
1801 					OPENAL_Channel_Stop(MONSTER_SOUND);
1802 				}
1803 #endif
1804 				int c;
1805 				for ( c = 0; c < MAXPLAYERS; c++ )
1806 				{
1807 					MONSTER_SOUND = playSoundPlayer(c, 179, 128);
1808 					playSoundPlayer(c, 166, 128);
1809 					Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 255);
1810 					messagePlayerColor(c, color, language[512]);
1811 				}
1812 			}
1813 		}
1814 		// dodging away
1815 		if ( ( ( rand() % 4 == 0 && my->monsterState != 6 ) || ( rand() % 10 == 0 && my->monsterState == MONSTER_STATE_LICH_SUMMON) ) && myStats->OLDHP != myStats->HP )
1816 		{
1817 			playSoundEntity(my, 180, 128);
1818 			my->monsterState = MONSTER_STATE_LICH_DODGE; // dodge state
1819 			double dir = my->yaw - (PI / 2) + PI * (rand() % 2);
1820 			MONSTER_VELX = cos(dir) * 5;
1821 			MONSTER_VELY = sin(dir) * 5;
1822 			my->monsterSpecialTimer = 0;
1823 		}
1824 	}
1825 
1826 	if ( (myStats->type == LICH_FIRE && my->monsterState != MONSTER_STATE_LICHFIRE_DIE)
1827 		|| (myStats->type == LICH_ICE && my->monsterState != MONSTER_STATE_LICHICE_DIE )
1828 		&& myStats->HP > 0 )
1829 	{
1830 		//messagePlayer(0, "state: %d", my->monsterState);
1831 		if ( my->monsterLichBattleState >= LICH_BATTLE_READY )
1832 		{
1833 			for ( int c = 0; c < MAXPLAYERS; c++ )
1834 			{
1835 				assailant[c] = true; // as long as this is active, combat music doesn't turn off
1836 				assailantTimer[c] = COMBAT_MUSIC_COOLDOWN;
1837 			}
1838 		}
1839 		if ( my->monsterSpecialTimer > 0 )
1840 		{
1841 			--my->monsterSpecialTimer;
1842 		}
1843 		else
1844 		{
1845 			my->monsterSpecialTimer = 0;
1846 			if ( my->monsterState == MONSTER_STATE_LICH_CASTSPELLS )
1847 			{
1848 				my->monsterState = MONSTER_STATE_LICH_TELEPORT_ROAMING;
1849 				my->monsterSpecialTimer = 60;
1850 				if ( myStats->type == LICH_FIRE )
1851 				{
1852 					my->lichFireTeleport();
1853 				}
1854 				else
1855 				{
1856 					my->lichIceTeleport();
1857 				}
1858 			}
1859 		}
1860 
1861 		if ( my->monsterState != MONSTER_STATE_ATTACK && my->monsterState <= MONSTER_STATE_HUNT )
1862 		{
1863 			my->monsterHitTime = HITRATE * 2;
1864 		}
1865 		//messagePlayer(0, "Ally state: %d", my->monsterLichAllyStatus);
1866 		Entity* lichAlly = nullptr;
1867 		if ( my->ticks > (TICKS_PER_SECOND)
1868 			&& my->monsterLichAllyStatus == LICH_ALLY_ALIVE
1869 			&& ticks % (TICKS_PER_SECOND * 2) == 0 )
1870 		{
1871 			if ( myStats->type == LICH_ICE )
1872 			{
1873 				numMonsterTypeAliveOnMap(LICH_FIRE, lichAlly);
1874 				if ( lichAlly == nullptr )
1875 				{
1876 					//messagePlayer(0, "DEAD");
1877 					my->monsterLichAllyStatus = LICH_ALLY_DEAD;
1878 					my->monsterLichAllyUID = 0;
1879 					for ( int c = 0; c < MAXPLAYERS; c++ )
1880 					{
1881 						playSoundPlayer(c, 392, 128);
1882 						messagePlayerColor(c, uint32ColorBaronyBlue(*mainsurface), language[2647]);
1883 					}
1884 				}
1885 				else if ( lichAlly && my->monsterLichAllyUID == 0 )
1886 				{
1887 					my->monsterLichAllyUID = lichAlly->getUID();
1888 				}
1889 			}
1890 			else
1891 			{
1892 				numMonsterTypeAliveOnMap(LICH_ICE, lichAlly);
1893 				if ( lichAlly == nullptr )
1894 				{
1895 					//messagePlayer(0, "DEAD");
1896 					my->monsterLichAllyStatus = LICH_ALLY_DEAD;
1897 					my->monsterLichAllyUID = 0;
1898 					for ( int c = 0; c < MAXPLAYERS; c++ )
1899 					{
1900 						playSoundPlayer(c, 391, 128);
1901 						messagePlayerColor(c, uint32ColorOrange(*mainsurface), language[2649]);
1902 					}
1903 				}
1904 				else if ( lichAlly && my->monsterLichAllyUID == 0 )
1905 				{
1906 					my->monsterLichAllyUID = lichAlly->getUID();
1907 				}
1908 			}
1909 		}
1910 		real_t lichDist = 0.f;
1911 		Entity* target = uidToEntity(my->monsterTarget);
1912 
1913 		if ( myStats->OLDHP != myStats->HP && myStats->HP > 0 )
1914 		{
1915 			if ( my->monsterState == MONSTER_STATE_LICH_CASTSPELLS
1916 				&& my->monsterSpecialTimer < 250 )
1917 			{
1918 				if ( rand() % 8 == 0 )
1919 				{
1920 					my->monsterState = MONSTER_STATE_LICH_TELEPORT_ROAMING;
1921 					my->lichFireTeleport();
1922 					my->monsterSpecialTimer = 60;
1923 				}
1924 			}
1925 			if ( my->monsterState <= MONSTER_STATE_HUNT )
1926 			{
1927 				switch ( my->monsterLichBattleState )
1928 				{
1929 					// track when a teleport can happen, battleState needs to be odd numbered to allow stationary teleport
1930 					case 0:
1931 						if ( myStats->HP <= myStats->MAXHP * 0.9 )
1932 						{
1933 							my->monsterLichBattleState = 1;
1934 						}
1935 						break;
1936 					case 2:
1937 						if ( myStats->HP <= myStats->MAXHP * 0.7 )
1938 						{
1939 							my->monsterLichBattleState = 3;
1940 						}
1941 						break;
1942 					case 4:
1943 						if ( myStats->HP <= myStats->MAXHP * 0.5 )
1944 						{
1945 							my->monsterLichBattleState = 5;
1946 						}
1947 						break;
1948 					case 6:
1949 						if ( myStats->HP <= myStats->MAXHP * 0.3 )
1950 						{
1951 							my->monsterLichBattleState = 7;
1952 						}
1953 						break;
1954 					case 8:
1955 						if ( myStats->HP <= myStats->MAXHP * 0.1 )
1956 						{
1957 							my->monsterLichBattleState = 9;
1958 						}
1959 						break;
1960 					default:
1961 						break;
1962 				}
1963 				if ( my->monsterLichBattleState % 2 == 1
1964 					&& (rand() % 5 == 0
1965 						|| (rand() % 4 == 0 && my->monsterLichTeleportTimer > 0)
1966 						|| (rand() % 2 == 0 && my->monsterLichAllyStatus == LICH_ALLY_DEAD))
1967 					)
1968 				{
1969 					// chance to change state to teleport after being hit.
1970 					if ( my->monsterLichAllyUID != 0 )
1971 					{
1972 						lichAlly = uidToEntity(my->monsterLichAllyUID);
1973 					}
1974 					if ( myStats->type == LICH_FIRE )
1975 					{
1976 						if ( !myStats->EFFECTS[EFF_VAMPIRICAURA] )
1977 						{
1978 							if ( (lichAlly && lichAlly->monsterState != MONSTER_STATE_LICH_CASTSPELLS)
1979 								|| my->monsterLichAllyStatus == LICH_ALLY_DEAD
1980 								|| multiplayer != SINGLE )
1981 							{
1982 								// don't teleport if ally is casting spells. unless multiplayer, then go nuts!
1983 								my->monsterState = MONSTER_STATE_LICHFIRE_TELEPORT_STATIONARY;
1984 								my->lichFireTeleport();
1985 								my->monsterSpecialTimer = 80;
1986 								++my->monsterLichBattleState;
1987 							}
1988 						}
1989 					}
1990 					else if ( myStats->type == LICH_ICE )
1991 					{
1992 						if ( (lichAlly && lichAlly->monsterState != MONSTER_STATE_LICH_CASTSPELLS)
1993 							|| my->monsterLichAllyStatus == LICH_ALLY_DEAD
1994 							|| multiplayer != SINGLE )
1995 						{
1996 							// don't teleport if ally is casting spells. unless multiplayer, then go nuts!
1997 							my->monsterState = MONSTER_STATE_LICHICE_TELEPORT_STATIONARY;
1998 							my->lichIceTeleport();
1999 							my->monsterSpecialTimer = 80;
2000 							++my->monsterLichBattleState;
2001 						}
2002 					}
2003 				}
2004 			}
2005 		}
2006 		if ( my->monsterSpecialTimer == 0 && my->monsterAttack == 0 )
2007 		{
2008 			if ( my->monsterState <= MONSTER_STATE_HUNT && my->monsterTarget )
2009 			{
2010 				if ( target )
2011 				{
2012 					lichDist = sqrt(pow(my->x - target->x, 2) + pow(my->y - target->y, 2));
2013 				}
2014 				if ( ticks % 30 == 0 )
2015 				{
2016 					// check tiles around the monster.
2017 					int sides = 0;
2018 					int my_x = static_cast<int>(my->x) >> 4;
2019 					int my_y = static_cast<int>(my->y) >> 4;
2020 					int mapIndex = (my_y) * MAPLAYERS + (my_x + 1) * MAPLAYERS * map.height;
2021 					if ( map.tiles[OBSTACLELAYER + mapIndex] )   // wall
2022 					{
2023 						++sides;
2024 					}
2025 					mapIndex = (my_y) * MAPLAYERS + (my_x - 1) * MAPLAYERS * map.height;
2026 					if ( map.tiles[OBSTACLELAYER + mapIndex] )   // wall
2027 					{
2028 						++sides;
2029 					}
2030 					mapIndex = (my_y + 1) * MAPLAYERS + (my_x) * MAPLAYERS * map.height;
2031 					if ( map.tiles[OBSTACLELAYER + mapIndex] )   // wall
2032 					{
2033 						++sides;
2034 					}
2035 					mapIndex = (my_y - 1) * MAPLAYERS + (my_x) * MAPLAYERS * map.height;
2036 					if ( map.tiles[OBSTACLELAYER + mapIndex] )   // wall
2037 					{
2038 						++sides;
2039 					}
2040 					//messagePlayer(0, "sides: %d, timer %d", sides, my->monsterLichTeleportTimer);
2041 					if ( sides == 0 )
2042 					{
2043 						my->monsterLichTeleportTimer = 0;
2044 					}
2045 					else
2046 					{
2047 						if ( sides >= 2 )
2048 						{
2049 							my->monsterLichTeleportTimer++;
2050 						}
2051 						else
2052 						{
2053 							if ( rand() % 3 == 0 )
2054 							{
2055 								my->monsterLichTeleportTimer++;
2056 							}
2057 						}
2058 						if ( my->monsterLichTeleportTimer >= 3 )
2059 						{
2060 							// let's teleport, reset the counter inside the teleport functions.
2061 							if ( myStats->type == LICH_FIRE )
2062 							{
2063 								my->lichFireTeleport();
2064 							}
2065 							else
2066 							{
2067 								my->lichIceTeleport();
2068 							}
2069 							my->monsterSpecialTimer = 40;
2070 						}
2071 					}
2072 				}
2073 				if ( myStats->type == LICH_FIRE )
2074 				{
2075 					if ( (	my->monsterLichFireMeleePrev == LICH_ATK_RISING_SINGLE
2076 							|| my->monsterLichFireMeleePrev == LICH_ATK_HORIZONTAL_RETURN)
2077 							&& rand() % 4 == 0
2078 							&& ticks % 10 == 0
2079 						)
2080 					{
2081 						// chance to dodge immediately after the above 2 attacks
2082 						playSoundEntity(my, 180, 128);
2083 						dir = my->yaw - (PI / 2) + PI * (rand() % 2);
2084 						MONSTER_VELX = cos(dir) * 3;
2085 						MONSTER_VELY = sin(dir) * 3;
2086 						my->monsterState = MONSTER_STATE_LICHFIRE_DODGE;
2087 						my->monsterSpecialTimer = 20;
2088 						my->monsterLichFireMeleePrev = 0;
2089 						my->monsterLichFireMeleeSeq = LICH_ATK_BASICSPELL_SINGLE;
2090 					}
2091 					else if ( myStats->OLDHP != myStats->HP )
2092 					{
2093 						if ( rand() % 4 == 0 )
2094 						{
2095 							// chance to dodge on hp loss
2096 							playSoundEntity(my, 180, 128);
2097 							dir = my->yaw - (PI / 2) + PI * (rand() % 2);
2098 							MONSTER_VELX = cos(dir) * 3;
2099 							MONSTER_VELY = sin(dir) * 3;
2100 							my->monsterState = MONSTER_STATE_LICHFIRE_DODGE;
2101 							my->monsterSpecialTimer = 20;
2102 						}
2103 					}
2104 					else if ( lichDist > 64 )
2105 					{
2106 						if ( target && rand() % 100 == 0 )
2107 						{
2108 							// chance to dodge towards the target if distance is great enough.
2109 							playSoundEntity(my, 180, 128);
2110 							tangent = atan2(target->y - my->y, target->x - my->x);
2111 							dir = tangent;
2112 							while ( dir < 0 )
2113 							{
2114 								dir += 2 * PI;
2115 							}
2116 							while ( dir > 2 * PI )
2117 							{
2118 								dir -= 2 * PI;
2119 							}
2120 							MONSTER_VELX = cos(dir) * 3;
2121 							MONSTER_VELY = sin(dir) * 3;
2122 							my->monsterState = MONSTER_STATE_LICHFIRE_DODGE;
2123 							my->monsterSpecialTimer = 50;
2124 						}
2125 					}
2126 				}
2127 				else if ( myStats->type == LICH_ICE )
2128 				{
2129 					int enemiesInMelee = 0;
2130 					if ( ticks % 50 == 0 )
2131 					{
2132 						// grab enemies around the lich once per second to determine threat level.
2133 						enemiesInMelee = numTargetsAroundEntity(my, 32.0, PI, MONSTER_TARGET_ENEMY);
2134 					}
2135 					if ( myStats->OLDHP != myStats->HP )
2136 					{
2137 						if ( rand() % 3 == 0 )
2138 						{
2139 							// chance to dodge on hp loss
2140 							playSoundEntity(my, 180, 128);
2141 							dir = my->yaw - (PI / 2) + PI * (rand() % 2);
2142 							MONSTER_VELX = cos(dir) * 3;
2143 							MONSTER_VELY = sin(dir) * 3;
2144 							my->monsterState = MONSTER_STATE_LICHICE_DODGE;
2145 							my->monsterSpecialTimer = 30;
2146 							if ( rand() % 2 == 0 )
2147 							{
2148 								// prepare off-hand spell after dodging
2149 								my->monsterLichIceCastPrev = 0;
2150 								my->monsterLichIceCastSeq = LICH_ATK_BASICSPELL_SINGLE;
2151 							}
2152 						}
2153 					}
2154 					else if ( (lichDist < 32) || (enemiesInMelee > 1) )
2155 					{
2156 						if ( target && ticks % 10 == 0 && rand() % 100 == 0 || (enemiesInMelee > 1 && rand() % 8 == 0) )
2157 						{
2158 							// chance to dodge away from target if distance is low enough.
2159 							playSoundEntity(my, 180, 128);
2160 							tangent = atan2(target->y - my->y, target->x - my->x);
2161 							dir = tangent + PI;
2162 							while ( dir < 0 )
2163 							{
2164 								dir += 2 * PI;
2165 							}
2166 							while ( dir > 2 * PI )
2167 							{
2168 								dir -= 2 * PI;
2169 							}
2170 							MONSTER_VELX = cos(dir) * 3;
2171 							MONSTER_VELY = sin(dir) * 3;
2172 							my->monsterState = MONSTER_STATE_LICHICE_DODGE;
2173 							my->monsterSpecialTimer = 20;
2174 							if ( rand() % 2 == 0 )
2175 							{
2176 								// prepare off-hand spell after dodging
2177 								my->monsterLichIceCastPrev = 0;
2178 								my->monsterLichIceCastSeq = LICH_ATK_BASICSPELL_SINGLE;
2179 							}
2180 						}
2181 						else if ( (ticks % 50 == 0 && rand() % 10 == 0) || (enemiesInMelee > 1 && rand() % 4 == 0) )
2182 						{
2183 							my->monsterSpecialTimer = 100;
2184 							my->monsterLichIceCastPrev = 0;
2185 							my->monsterLichIceCastSeq = LICH_ATK_CHARGE_AOE;
2186 						}
2187 					}
2188 					else if ( lichDist > 64 )
2189 					{
2190 						// chance to dodge towards the target if distance is great enough.
2191 						if ( rand() % 100 == 0 )
2192 						{
2193 							if ( my->monsterLichAllyUID != 0 )
2194 							{
2195 								lichAlly = uidToEntity(my->monsterLichAllyUID);
2196 							}
2197 							if ( lichAlly && target )
2198 							{
2199 								// if ally is close to your target, then don't dodge towards.
2200 								if ( entityDist(lichAlly, target) > 48.0 )
2201 								{
2202 									playSoundEntity(my, 180, 128);
2203 									tangent = atan2(target->y - my->y, target->x - my->x);
2204 									dir = tangent;
2205 									while ( dir < 0 )
2206 									{
2207 										dir += 2 * PI;
2208 									}
2209 									while ( dir > 2 * PI )
2210 									{
2211 										dir -= 2 * PI;
2212 									}
2213 									MONSTER_VELX = cos(dir) * 3;
2214 									MONSTER_VELY = sin(dir) * 3;
2215 									my->monsterState = MONSTER_STATE_LICHICE_DODGE;
2216 									my->monsterSpecialTimer = 50;
2217 								}
2218 							}
2219 							if ( my->monsterState != MONSTER_STATE_LICHICE_DODGE )
2220 							{
2221 								// chance to dodge sideways if not set above
2222 								playSoundEntity(my, 180, 128);
2223 								dir = my->yaw - (PI / 2) + PI * (rand() % 2);
2224 								MONSTER_VELX = cos(dir) * 3;
2225 								MONSTER_VELY = sin(dir) * 3;
2226 								my->monsterState = MONSTER_STATE_LICHICE_DODGE;
2227 								my->monsterSpecialTimer = 30;
2228 								if ( rand() % 10 == 0 )
2229 								{
2230 									// prepare off-hand spell after dodging
2231 									my->monsterLichIceCastPrev = 0;
2232 									my->monsterLichIceCastSeq = LICH_ATK_BASICSPELL_SINGLE;
2233 								}
2234 							}
2235 						}
2236 					}
2237 					else if ( my->monsterLichMeleeSwingCount > 3 )
2238 					{
2239 						// reached x successive normal attacks, either move/teleport/dodge around the map
2240 						my->monsterLichMeleeSwingCount = 0;
2241 						if ( rand() % 10 > 0 )
2242 						{
2243 							if ( rand() % 2 == 0 )
2244 							{
2245 								my->monsterTarget = 0;
2246 								my->monsterTargetX = my->x - 50 + rand() % 100;
2247 								my->monsterTargetY = my->y - 50 + rand() % 100;
2248 								my->monsterState = MONSTER_STATE_PATH; // path state
2249 							}
2250 							else
2251 							{
2252 								// chance to dodge
2253 								playSoundEntity(my, 180, 128);
2254 								dir = my->yaw - (PI / 2) + PI * (rand() % 2);
2255 								MONSTER_VELX = cos(dir) * 3;
2256 								MONSTER_VELY = sin(dir) * 3;
2257 								my->monsterState = MONSTER_STATE_LICHICE_DODGE;
2258 								my->monsterSpecialTimer = 30;
2259 								if ( rand() % 2 == 0 )
2260 								{
2261 									// prepare off-hand spell after dodging
2262 									my->monsterLichIceCastPrev = 0;
2263 									my->monsterLichIceCastSeq = LICH_ATK_BASICSPELL_SINGLE;
2264 								}
2265 								else
2266 								{
2267 									my->monsterLichIceCastPrev = 0;
2268 									my->monsterLichIceCastSeq = LICH_ATK_FALLING_DIAGONAL;
2269 								}
2270 							}
2271 						}
2272 					}
2273 				}
2274 			}
2275 			else if ( my->monsterState == MONSTER_STATE_LICHFIRE_TELEPORT_STATIONARY
2276 				|| my->monsterState == MONSTER_STATE_LICHICE_TELEPORT_STATIONARY )
2277 			{
2278 				my->monsterState = MONSTER_STATE_LICH_CASTSPELLS;
2279 				my->monsterSpecialTimer = 500; // cast spells for 10 seconds.
2280 				my->monsterHitTime = 0;
2281 				my->monsterLichMagicCastCount = 0;
2282 				my->monsterLichFireMeleeSeq = 0;
2283 				// acquire a new target.
2284 				lichDist = 1024;
2285 				for ( node = map.creatures->first; node != nullptr; node = node->next ) //Only creatures need to be targetted.
2286 				{
2287 					Entity* tempEntity = (Entity*)node->element;
2288 					if ( tempEntity->behavior == &actPlayer
2289 						&& (sqrt(pow(my->x - tempEntity->x, 2) + pow(my->y - tempEntity->y, 2)) < lichDist)
2290 						)
2291 					{
2292 						lichDist = sqrt(pow(my->x - tempEntity->x, 2) + pow(my->y - tempEntity->y, 2));
2293 						target = tempEntity;
2294 					}
2295 				}
2296 				if ( target )
2297 				{
2298 					my->monsterAcquireAttackTarget(*target, MONSTER_STATE_LICH_CASTSPELLS);
2299 				}
2300 				my->castOrbitingMagicMissile(SPELL_BLEED, 16.0, 0.0, 500);
2301 				my->castOrbitingMagicMissile(SPELL_BLEED, 16.0, 2 * PI / 5, 500);
2302 				my->castOrbitingMagicMissile(SPELL_BLEED, 16.0, 4 * PI / 5, 500);
2303 				my->castOrbitingMagicMissile(SPELL_BLEED, 16.0, 6 * PI / 5, 500);
2304 				my->castOrbitingMagicMissile(SPELL_BLEED, 16.0, 8 * PI / 5, 500);
2305 			}
2306 			else if ( my->monsterState == MONSTER_STATE_LICH_TELEPORT_ROAMING )
2307 			{
2308 				my->monsterHitTime = 0;
2309 				my->monsterLichMagicCastCount = 0;
2310 				my->monsterLichFireMeleeSeq = 0;
2311 				// acquire a new target.
2312 				lichDist = 1024;
2313 				for ( node = map.creatures->first; node != nullptr; node = node->next ) //Only creatures need to be targetted.
2314 				{
2315 					Entity* tempEntity = (Entity*)node->element;
2316 					if ( tempEntity && tempEntity->behavior == &actPlayer
2317 						&& (sqrt(pow(my->x - tempEntity->x, 2) + pow(my->y - tempEntity->y, 2)) < lichDist)
2318 						)
2319 					{
2320 						lichDist = sqrt(pow(my->x - tempEntity->x, 2) + pow(my->y - tempEntity->y, 2));
2321 						target = tempEntity;
2322 					}
2323 				}
2324 				if ( target )
2325 				{
2326 					my->monsterAcquireAttackTarget(*target, MONSTER_STATE_PATH);
2327 					my->monsterState = MONSTER_STATE_PATH;
2328 				}
2329 				else
2330 				{
2331 					my->monsterState = MONSTER_STATE_WAIT;
2332 				}
2333 			}
2334 		}
2335 	}
2336 
2337 	// hunger, regaining hp/mp, poison, etc.
2338 	if ( !intro )
2339 	{
2340 		my->handleEffects(myStats);
2341 	}
2342 	if ( myStats->HP <= 0
2343 		&& my->monsterState != MONSTER_STATE_LICH_DEATH
2344 		&& my->monsterState != MONSTER_STATE_DEVIL_DEATH
2345 		&& my->monsterState != MONSTER_STATE_LICHFIRE_DIE
2346 		&& my->monsterState != MONSTER_STATE_LICHICE_DIE )
2347 	{
2348 		//TODO: Refactor die function.
2349 		// drop all equipment
2350 		entity = dropItemMonster(myStats->helmet, my, myStats);
2351 		if ( entity )
2352 		{
2353 			entity->flags[USERFLAG1] = true;
2354 		}
2355 		myStats->helmet = NULL;
2356 		entity = dropItemMonster(myStats->breastplate, my, myStats);
2357 		if ( entity )
2358 		{
2359 			entity->flags[USERFLAG1] = true;
2360 		}
2361 		myStats->breastplate = NULL;
2362 		entity = dropItemMonster(myStats->gloves, my, myStats);
2363 		if ( entity )
2364 		{
2365 			entity->flags[USERFLAG1] = true;
2366 		}
2367 		myStats->gloves = NULL;
2368 		entity = dropItemMonster(myStats->shoes, my, myStats);
2369 		if ( entity )
2370 		{
2371 			entity->flags[USERFLAG1] = true;
2372 		}
2373 		myStats->shoes = NULL;
2374 		entity = dropItemMonster(myStats->shield, my, myStats);
2375 		if ( entity )
2376 		{
2377 			entity->flags[USERFLAG1] = true;
2378 		}
2379 		myStats->shield = NULL;
2380 		if ( myStats->weapon )
2381 		{
2382 			if ( itemCategory(myStats->weapon) != SPELLBOOK )
2383 			{
2384 				entity = dropItemMonster(myStats->weapon, my, myStats);
2385 				if ( entity )
2386 				{
2387 					entity->flags[USERFLAG1] = true;
2388 				}
2389 			}
2390 			else
2391 			{
2392 				// spellbooks are not dropped
2393 				if ( myStats->weapon->node )
2394 				{
2395 					list_RemoveNode(myStats->weapon->node);
2396 				}
2397 				else
2398 				{
2399 					free(myStats->weapon);
2400 				}
2401 			}
2402 			myStats->weapon = NULL;
2403 		}
2404 		entity = dropItemMonster(myStats->cloak, my, myStats);
2405 		if ( entity )
2406 		{
2407 			entity->flags[USERFLAG1] = true;
2408 		}
2409 		myStats->cloak = NULL;
2410 		entity = dropItemMonster(myStats->amulet, my, myStats);
2411 		if ( entity )
2412 		{
2413 			entity->flags[USERFLAG1] = true;
2414 		}
2415 		myStats->amulet = NULL;
2416 		entity = dropItemMonster(myStats->ring, my, myStats);
2417 		if ( entity )
2418 		{
2419 			entity->flags[USERFLAG1] = true;
2420 		}
2421 		myStats->ring = NULL;
2422 		entity = dropItemMonster(myStats->mask, my, myStats);
2423 		if ( entity )
2424 		{
2425 			entity->flags[USERFLAG1] = true;
2426 		}
2427 		myStats->mask = NULL;
2428 		node_t* nextnode = NULL;
2429 
2430 		for ( node = myStats->inventory.first; node != NULL; node = nextnode )
2431 		{
2432 			nextnode = node->next;
2433 			Item* item = (Item*)node->element;
2434 			for ( c = item->count; c > 0; c-- )
2435 			{
2436 				bool wasQuiver = itemTypeIsQuiver(item->type);
2437 				entity = dropItemMonster(item, my, myStats);
2438 				if ( entity )
2439 				{
2440 					entity->flags[USERFLAG1] = true;    // makes items passable, improves performance
2441 					if ( wasQuiver )
2442 					{
2443 						break; // always drop the whole stack.
2444 					}
2445 				}
2446 			}
2447 		}
2448 
2449 		// broadcast my player allies about my death
2450 		int playerFollower = MAXPLAYERS;
2451 		for (c = 0; c < MAXPLAYERS; c++)
2452 		{
2453 			if (players[c] && players[c]->entity)
2454 			{
2455 				if (myStats->leader_uid == players[c]->entity->getUID())
2456 				{
2457 					playerFollower = c;
2458 					if ( stats[c] )
2459 					{
2460 						for ( node_t* allyNode = stats[c]->FOLLOWERS.first; allyNode != nullptr; allyNode = allyNode->next )
2461 						{
2462 							if ( *((Uint32*)allyNode->element) == my->getUID() )
2463 							{
2464 								list_RemoveNode(allyNode);
2465 								if ( myStats->monsterIsCharmed == 1 && client_classes[c] == CLASS_MESMER )
2466 								{
2467 									steamStatisticUpdateClient(c, STEAM_STAT_SURROGATES, STEAM_STAT_INT, 1);
2468 								}
2469 								if ( c != clientnum )
2470 								{
2471 									serverRemoveClientFollower(c, my->getUID());
2472 								}
2473 								else
2474 								{
2475 									if ( FollowerMenu.recentEntity && (FollowerMenu.recentEntity->getUID() == 0
2476 										|| FollowerMenu.recentEntity->getUID() == my->getUID()) )
2477 									{
2478 										FollowerMenu.recentEntity = nullptr;
2479 									}
2480 									if ( FollowerMenu.followerToCommand == my )
2481 									{
2482 										FollowerMenu.closeFollowerMenuGUI();
2483 									}
2484 								}
2485 								break;
2486 							}
2487 						}
2488 					}
2489 					break;
2490 				}
2491 			}
2492 		}
2493 
2494 		bool skipObituary = false;
2495 		if ( my->monsterAllySummonRank != 0 && myStats->MP > 0 )
2496 		{
2497 			skipObituary = true;
2498 		}
2499 
2500 		if ( playerFollower < MAXPLAYERS && !skipObituary )
2501 		{
2502 			messagePlayerMonsterEvent(c, 0xFFFFFFFF, *myStats, language[1499], language[2589], MSG_OBITUARY);
2503 		}
2504 
2505 		// drop gold
2506 		if ( gameplayCustomManager.inUse() )
2507 		{
2508 			int numGold = myStats->GOLD * (gameplayCustomManager.globalGoldPercent / 100.f);
2509 			myStats->GOLD = numGold;
2510 		}
2511 		if ( myStats->GOLD > 0 && myStats->monsterNoDropItems == 0 )
2512 		{
2513 			int x = std::min<int>(std::max(0, (int)(my->x / 16)), map.width - 1);
2514 			int y = std::min<int>(std::max(0, (int)(my->y / 16)), map.height - 1);
2515 
2516 			// check for floor to drop gold...
2517 			if ( map.tiles[y * MAPLAYERS + x * MAPLAYERS * map.height] )
2518 			{
2519 				entity = newEntity(130, 0, map.entities, nullptr); // 130 = goldbag model
2520 				entity->sizex = 4;
2521 				entity->sizey = 4;
2522 				entity->x = my->x;
2523 				entity->y = my->y;
2524 				entity->z = 6;
2525 				entity->yaw = (rand() % 360) * PI / 180.0;
2526 				entity->flags[PASSABLE] = true;
2527 				entity->flags[UPDATENEEDED] = true;
2528 				entity->behavior = &actGoldBag;
2529 				entity->skill[0] = myStats->GOLD; // amount
2530 			}
2531 		}
2532 
2533 		// die
2534 #ifdef USE_FMOD
2535 		if ( MONSTER_SOUND )
2536 		{
2537 			FMOD_Channel_Stop(MONSTER_SOUND);
2538 		}
2539 #elif defined USE_OPENAL
2540 		if ( MONSTER_SOUND )
2541 		{
2542 			OPENAL_Channel_Stop(MONSTER_SOUND);
2543 		}
2544 #endif
2545 		myStats = my->getStats();
2546 		switch ( myStats->type )
2547 		{
2548 			case HUMAN:
2549 				humanDie(my);
2550 				break;
2551 			case RAT:
2552 				ratDie(my);
2553 				break;
2554 			case GOBLIN:
2555 				goblinDie(my);
2556 				break;
2557 			case SLIME:
2558 				slimeDie(my);
2559 				break;
2560 			case SCORPION:
2561 				scorpionDie(my);
2562 				break;
2563 			case SUCCUBUS:
2564 				succubusDie(my);
2565 				break;
2566 			case TROLL:
2567 				trollDie(my);
2568 				break;
2569 			case SHOPKEEPER:
2570 				shopkeeperDie(my);
2571 				break;
2572 			case SKELETON:
2573 				skeletonDie(my);
2574 				break;
2575 			case MINOTAUR:
2576 				minotaurDie(my);
2577 				break;
2578 			case GHOUL:
2579 				ghoulDie(my);
2580 				break;
2581 			case DEMON:
2582 				demonDie(my);
2583 				break;
2584 			case SPIDER:
2585 				spiderDie(my);
2586 				break;
2587 			case LICH:
2588 				my->flags[PASSABLE] = true; // so I can't take any more hits
2589 				my->monsterState = MONSTER_STATE_LICH_DEATH; // lich death state
2590 				my->monsterSpecialTimer = 0;
2591 				MONSTER_ATTACK = 0;
2592 				MONSTER_ATTACKTIME = 0;
2593 				serverUpdateEntitySkill(my, 8);
2594 				serverUpdateEntitySkill(my, 9);
2595 				for ( c = 0; c < NUMEFFECTS; ++c )
2596 				{
2597 					myStats->EFFECTS[c] = false;
2598 					myStats->EFFECTS_TIMERS[c] = 0;
2599 				}
2600 				break;
2601 			case CREATURE_IMP:
2602 				impDie(my);
2603 				break;
2604 			case GNOME:
2605 				gnomeDie(my);
2606 				break;
2607 			case DEVIL:
2608 				my->flags[PASSABLE] = true; // so I can't take any more hits
2609 				my->monsterState = MONSTER_STATE_DEVIL_DEATH; // devil death state
2610 				my->monsterSpecialTimer = 0;
2611 				MONSTER_ATTACK = 0;
2612 				MONSTER_ATTACKTIME = 0;
2613 				MONSTER_ARMBENDED = 0;
2614 				serverUpdateEntitySkill(my, 8);
2615 				serverUpdateEntitySkill(my, 9);
2616 				serverUpdateEntitySkill(my, 10);
2617 				for ( c = 0; c < NUMEFFECTS; ++c )
2618 				{
2619 					myStats->EFFECTS[c] = false;
2620 					myStats->EFFECTS_TIMERS[c] = 0;
2621 				}
2622 				break;
2623 			case AUTOMATON:
2624 				automatonDie(my);
2625 				break;
2626 			case COCKATRICE:
2627 				cockatriceDie(my);
2628 				break;
2629 			case CRYSTALGOLEM:
2630 				crystalgolemDie(my);
2631 				break;
2632 			case SCARAB:
2633 				scarabDie(my);
2634 				break;
2635 			case KOBOLD:
2636 				koboldDie(my);
2637 				break;
2638 			case SHADOW:
2639 				shadowDie(my);
2640 				break;
2641 			case VAMPIRE:
2642 				vampireDie(my);
2643 				break;
2644 			case INCUBUS:
2645 				incubusDie(my);
2646 				break;
2647 			case INSECTOID:
2648 				insectoidDie(my);
2649 				break;
2650 			case GOATMAN:
2651 				goatmanDie(my);
2652 				break;
2653 			case LICH_FIRE:
2654 				my->flags[PASSABLE] = true; // so I can't take any more hits
2655 				my->monsterState = MONSTER_STATE_LICHFIRE_DIE; // lich death state
2656 				my->monsterSpecialTimer = 180;
2657 				my->monsterAttack = 0;
2658 				my->monsterAttackTime = 0;
2659 				serverUpdateEntitySkill(my, 8);
2660 				serverUpdateEntitySkill(my, 9);
2661 				serverUpdateEntitySkill(my, 0);
2662 				for ( c = 0; c < NUMEFFECTS; ++c )
2663 				{
2664 					myStats->EFFECTS[c] = false;
2665 					myStats->EFFECTS_TIMERS[c] = 0;
2666 				}
2667 				break;
2668 			case LICH_ICE:
2669 				my->flags[PASSABLE] = true; // so I can't take any more hits
2670 				my->monsterState = MONSTER_STATE_LICHICE_DIE; // lich death state
2671 				my->monsterSpecialTimer = 180;
2672 				my->monsterAttack = 0;
2673 				my->monsterAttackTime = 0;
2674 				serverUpdateEntitySkill(my, 8);
2675 				serverUpdateEntitySkill(my, 9);
2676 				serverUpdateEntitySkill(my, 0);
2677 				for ( c = 0; c < NUMEFFECTS; ++c )
2678 				{
2679 					myStats->EFFECTS[c] = false;
2680 					myStats->EFFECTS_TIMERS[c] = 0;
2681 				}
2682 				break;
2683 			case SENTRYBOT:
2684 			case SPELLBOT:
2685 				sentryBotDie(my);
2686 				break;
2687 			case GYROBOT:
2688 				gyroBotDie(my);
2689 				break;
2690 			case DUMMYBOT:
2691 				dummyBotDie(my);
2692 				break;
2693 			default:
2694 				break; //This should never be reached.
2695 		}
2696 		return;
2697 	}
2698 
2699 	if ( multiplayer != CLIENT )
2700 	{
2701 		my->effectTimes();
2702 	}
2703 
2704 	if ( ticks % TICKS_PER_SECOND == 0 )
2705 	{
2706 		my->checkGroundForItems();
2707 	}
2708 
2709 	// check to see if monster can scream again
2710 	if ( MONSTER_SOUND != NULL )
2711 	{
2712 #ifdef USE_FMOD
2713 		FMOD_BOOL playing;
2714 		FMOD_Channel_IsPlaying(MONSTER_SOUND, &playing);
2715 		if (!playing)
2716 		{
2717 			MONSTER_SOUND = NULL;
2718 		}
2719 		else
2720 		{
2721 			for ( c = 0; c < numsounds; c++ )
2722 			{
2723 				/*if( sounds[c] == Mix_GetChunk(MONSTER_SOUND) && ( c<MONSTER_SPOTSND || c>=MONSTER_SPOTSND+MONSTER_SPOTVAR ) ) { //TODO: Is this necessary? If so, port it to FMOD or find a workaround.
2724 					MONSTER_SOUND = -1;
2725 					break;
2726 				}*/
2727 				FMOD_BOOL playing = true;
2728 				FMOD_Channel_IsPlaying(MONSTER_SOUND, &playing);
2729 				if (!playing)
2730 				{
2731 					MONSTER_SOUND = NULL;
2732 					break;
2733 				}
2734 			}
2735 		}
2736 #elif defined USE_OPENAL
2737 		ALboolean playing;
2738 		OPENAL_Channel_IsPlaying(MONSTER_SOUND, &playing);
2739 		if (!playing)
2740 		{
2741 			MONSTER_SOUND = NULL;
2742 		}
2743 		else
2744 		{
2745 			for ( c = 0; c < numsounds; c++ )
2746 			{
2747 				ALboolean playing = true;
2748 				OPENAL_Channel_IsPlaying(MONSTER_SOUND, &playing);
2749 				if (!playing)
2750 				{
2751 					MONSTER_SOUND = NULL;
2752 					break;
2753 				}
2754 			}
2755 		}
2756 #endif
2757 	}
2758 
2759 	// remove broken equipment
2760 	if ( myStats->helmet != NULL )
2761 	{
2762 		if ( myStats->helmet->status == BROKEN )
2763 		{
2764 			free(myStats->helmet);
2765 			myStats->helmet = NULL;
2766 		}
2767 	}
2768 	if ( myStats->breastplate != NULL )
2769 	{
2770 		if ( myStats->breastplate->status == BROKEN )
2771 		{
2772 			free(myStats->breastplate);
2773 			myStats->breastplate = NULL;
2774 		}
2775 	}
2776 	if ( myStats->gloves != NULL )
2777 	{
2778 		if ( myStats->gloves->status == BROKEN )
2779 		{
2780 			free(myStats->gloves);
2781 			myStats->gloves = NULL;
2782 		}
2783 	}
2784 	if ( myStats->shoes != NULL )
2785 	{
2786 		if ( myStats->shoes->status == BROKEN )
2787 		{
2788 			free(myStats->shoes);
2789 			myStats->shoes = NULL;
2790 		}
2791 	}
2792 	if ( myStats->shield != NULL )
2793 	{
2794 		if ( myStats->shield->status == BROKEN )
2795 		{
2796 			free(myStats->shield);
2797 			myStats->shield = NULL;
2798 		}
2799 	}
2800 	if ( myStats->weapon != NULL )
2801 	{
2802 		if ( myStats->weapon->status == BROKEN )
2803 		{
2804 			free(myStats->weapon);
2805 			myStats->weapon = NULL;
2806 		}
2807 	}
2808 	if ( myStats->cloak != NULL )
2809 	{
2810 		if ( myStats->cloak->status == BROKEN )
2811 		{
2812 			free(myStats->cloak);
2813 			myStats->cloak = NULL;
2814 		}
2815 	}
2816 	if ( myStats->amulet != NULL )
2817 	{
2818 		if ( myStats->amulet->status == BROKEN )
2819 		{
2820 			free(myStats->amulet);
2821 			myStats->amulet = NULL;
2822 		}
2823 	}
2824 	if ( myStats->ring != NULL )
2825 	{
2826 		if ( myStats->ring->status == BROKEN )
2827 		{
2828 			free(myStats->ring);
2829 			myStats->ring = NULL;
2830 		}
2831 	}
2832 	if ( myStats->mask != NULL )
2833 	{
2834 		if ( myStats->mask->status == BROKEN )
2835 		{
2836 			free(myStats->mask);
2837 			myStats->mask = NULL;
2838 		}
2839 	}
2840 
2841 	// calculate weight
2842 	Sint32 weight = 0;
2843 	if ( myStats->helmet != NULL )
2844 	{
2845 		weight += items[myStats->helmet->type].weight * myStats->helmet->count;
2846 	}
2847 	if ( myStats->breastplate != NULL )
2848 	{
2849 		weight += items[myStats->breastplate->type].weight * myStats->breastplate->count;
2850 	}
2851 	if ( myStats->gloves != NULL )
2852 	{
2853 		weight += items[myStats->gloves->type].weight * myStats->gloves->count;
2854 	}
2855 	if ( myStats->shoes != NULL )
2856 	{
2857 		weight += items[myStats->shoes->type].weight * myStats->shoes->count;
2858 	}
2859 	if ( myStats->shield != NULL )
2860 	{
2861 		if ( itemTypeIsQuiver(myStats->shield->type) )
2862 		{
2863 			weight += std::max(1, items[myStats->shield->type].weight * myStats->shield->count / 5);
2864 		}
2865 		else
2866 		{
2867 			weight += items[myStats->shield->type].weight * myStats->shield->count;
2868 		}
2869 	}
2870 	if ( myStats->weapon != NULL )
2871 	{
2872 		weight += items[myStats->weapon->type].weight * myStats->weapon->count;
2873 	}
2874 	if ( myStats->cloak != NULL )
2875 	{
2876 		weight += items[myStats->cloak->type].weight * myStats->cloak->count;
2877 	}
2878 	if ( myStats->amulet != NULL )
2879 	{
2880 		weight += items[myStats->amulet->type].weight * myStats->amulet->count;
2881 	}
2882 	if ( myStats->ring != NULL )
2883 	{
2884 		weight += items[myStats->ring->type].weight * myStats->ring->count;
2885 	}
2886 	if ( myStats->mask != NULL )
2887 	{
2888 		weight += items[myStats->mask->type].weight * myStats->mask->count;
2889 	}
2890 	weight += myStats->GOLD / 100;
2891 	weight /= 2; // on monsters weight shouldn't matter so much
2892 	double weightratio = (1000 + my->getSTR() * 100 - weight) / (double)(1000 + my->getSTR() * 100);
2893 	weightratio = fmin(fmax(0, weightratio), 1);
2894 	// determine if I have a ranged weapon or not
2895 	hasrangedweapon = my->hasRangedWeapon();
2896 
2897 	// effect of a ring of conflict
2898 	bool ringconflict = false;
2899 	Entity* ringConflictHolder = nullptr;
2900 	if ( myStats->type != LICH_ICE && myStats->type != LICH_FIRE )
2901 	{
2902 		for ( node = map.creatures->first; node != nullptr; node = node->next ) //Only creatures can wear rings, so don't search map.entities.
2903 		{
2904 			Entity* tempentity = (Entity*)node->element;
2905 			if ( tempentity != nullptr && tempentity != my )
2906 			{
2907 				Stat* tempstats = tempentity->getStats();
2908 				if ( tempstats && tempstats->ring && tempstats->ring->type == RING_CONFLICT )
2909 				{
2910 					int conflictRange = 5 * TOUCHRANGE;
2911 					if ( sqrt(pow(my->x - tempentity->x, 2) + pow(my->y - tempentity->y, 2)) < conflictRange )
2912 					{
2913 						tangent = atan2(tempentity->y - my->y, tempentity->x - my->x);
2914 						lineTrace(my, my->x, my->y, tangent, conflictRange, 0, false);
2915 						ringconflict = true;
2916 						if ( hit.entity == tempentity )
2917 						{
2918 							ringConflictHolder = tempentity;
2919 						}
2920 						break;
2921 					}
2922 				}
2923 			}
2924 		}
2925 	}
2926 
2927 	// invisibility
2928 	bool handleinvisible = true;
2929 	switch ( myStats->type )
2930 	{
2931 		case HUMAN:
2932 		case GOBLIN:
2933 		case SKELETON:
2934 		case GNOME:
2935 		case KOBOLD:
2936 		case AUTOMATON:
2937 		case INSECTOID:
2938 		case GOATMAN:
2939 		case INCUBUS:
2940 		case SHADOW:
2941 		case VAMPIRE:
2942 		case SUCCUBUS:
2943 		case SHOPKEEPER:
2944 		case LICH_FIRE:
2945 		case LICH_ICE:
2946 		case SENTRYBOT:
2947 		case SPELLBOT:
2948 		case GYROBOT:
2949 		case DUMMYBOT:
2950 			handleinvisible = false;
2951 			break;
2952 		default:
2953 			break;
2954 	}
2955 	if ( handleinvisible )
2956 	{
2957 		//TODO: Should this use isInvisible()?
2958 		if ( myStats->EFFECTS[EFF_INVISIBLE] )
2959 		{
2960 			my->flags[INVISIBLE] = true;
2961 			for ( node = list_Node(&my->children, 2); node != NULL; node = node->next )
2962 			{
2963 				Entity* entity = (Entity*)node->element;
2964 				entity->flags[INVISIBLE] = true;
2965 			}
2966 		}
2967 		else
2968 		{
2969 			my->flags[INVISIBLE] = false;
2970 			for ( node = list_Node(&my->children, 2); node != NULL; node = node->next )
2971 			{
2972 				Entity* entity = (Entity*)node->element;
2973 				entity->flags[INVISIBLE] = false;
2974 			}
2975 		}
2976 	}
2977 
2978 	// chatting
2979 	char namesays[64];
2980 	if ( !strcmp(myStats->name, "") || monsterNameIsGeneric(*myStats) )
2981 	{
2982 		if ( monsterNameIsGeneric(*myStats) )
2983 		{
2984 			snprintf(namesays, 63, language[1302], myStats->name);
2985 		}
2986 		else if ( myStats->type < KOBOLD ) //Original monster count
2987 		{
2988 			snprintf(namesays, 63, language[513], language[90 + myStats->type]);
2989 		}
2990 		else if ( myStats->type >= KOBOLD ) //New monsters
2991 		{
2992 			snprintf(namesays, 63, language[513], language[2000 + myStats->type - KOBOLD]);
2993 		}
2994 	}
2995 	else
2996 	{
2997 		snprintf(namesays, 63, language[1302], myStats->name);
2998 	}
2999 	int monsterclicked = -1;
3000 	for (i = 0; i < MAXPLAYERS; i++)
3001 	{
3002 		if ( (i == 0 && selectedEntity == my) || (client_selected[i] == my) )
3003 		{
3004 			if (inrange[i])
3005 			{
3006 				monsterclicked = i;
3007 			}
3008 		}
3009 	}
3010 	if ( MONSTER_CLICKED )
3011 	{
3012 		monsterclicked = MONSTER_CLICKED - 1;
3013 		MONSTER_CLICKED = 0;
3014 	}
3015 	if ( monsterclicked >= 0 && monsterclicked < MAXPLAYERS )
3016 	{
3017 		if ( !my->isMobile() )
3018 		{
3019 			// message the player, "the %s doesn't respond"
3020 			messagePlayerMonsterEvent(monsterclicked, 0xFFFFFFFF, *myStats, language[514], language[515], MSG_COMBAT);
3021 		}
3022 		else
3023 		{
3024 			if (my->monsterTarget == players[monsterclicked]->entity->getUID() && my->monsterState != 4)
3025 			{
3026 				// angry at the player, "En Guarde!"
3027 				switch (myStats->type)
3028 				{
3029 					case HUMAN:
3030 						messagePlayer(monsterclicked, language[516 + rand() % 4], namesays);
3031 						break;
3032 					case SHOPKEEPER:
3033 						if ( stats[monsterclicked] )
3034 						{
3035 							if ( stats[monsterclicked]->type != HUMAN )
3036 							{
3037 								if ( stats[monsterclicked]->type < KOBOLD ) //Original monster count
3038 								{
3039 									messagePlayer(monsterclicked, language[3243],
3040 										namesays, language[90 + stats[monsterclicked]->type]);
3041 								}
3042 								else if ( stats[monsterclicked]->type >= KOBOLD ) //New monsters
3043 								{
3044 									messagePlayer(monsterclicked, language[3243], namesays,
3045 										language[2000 + (stats[monsterclicked]->type - KOBOLD)]);
3046 								}
3047 							}
3048 							else
3049 							{
3050 								messagePlayer(monsterclicked, language[516 + rand() % 4], namesays);
3051 							}
3052 						}
3053 						else
3054 						{
3055 							messagePlayer(monsterclicked, language[516 + rand() % 4], namesays);
3056 						}
3057 						break;
3058 					default:
3059 						break;
3060 				}
3061 			}
3062 			else if (my->monsterState == MONSTER_STATE_TALK)
3063 			{
3064 				// for shopkeepers trading with a player, "I am somewhat busy now."
3065 				if (my->monsterTarget != players[monsterclicked]->entity->getUID())
3066 				{
3067 					switch (myStats->type)
3068 					{
3069 						case SHOPKEEPER:
3070 						case HUMAN:
3071 							messagePlayer(monsterclicked, language[520 + rand() % 4], namesays);
3072 							break;
3073 						default:
3074 							messagePlayer(monsterclicked, language[524], namesays);
3075 							break;
3076 					}
3077 				}
3078 			}
3079 			else
3080 			{
3081 				// handle followers/trading
3082 				if ( myStats->type != SHOPKEEPER )
3083 				{
3084 					if ( myStats->MISC_FLAGS[STAT_FLAG_NPC] == 0 )
3085 					{
3086 						makeFollower(monsterclicked, ringconflict, namesays, my, myStats);
3087 					}
3088 					else
3089 					{
3090 						handleMonsterChatter(monsterclicked, ringconflict, namesays, my, myStats);
3091 					}
3092 					my->lookAtEntity(*players[monsterclicked]->entity);
3093 				}
3094 				else
3095 				{
3096 					if ( myStats->MISC_FLAGS[STAT_FLAG_MYSTERIOUS_SHOPKEEP] > 0 ) // mysterious merchant
3097 					{
3098 						bool hasOrb = false;
3099 						for ( node_t* node = myStats->inventory.first; node; node = node->next )
3100 						{
3101 							Item* item = (Item*)node->element;
3102 							if ( item && (item->type == ARTIFACT_ORB_BLUE
3103 									|| item->type == ARTIFACT_ORB_GREEN
3104 									|| item->type == ARTIFACT_ORB_RED)
3105 								)
3106 							{
3107 								hasOrb = true;
3108 								break;
3109 							}
3110 						}
3111 						if ( !hasOrb )
3112 						{
3113 							handleMonsterChatter(monsterclicked, ringconflict, namesays, my, myStats);
3114 							my->lookAtEntity(*players[monsterclicked]->entity);
3115 						}
3116 						else
3117 						{
3118 							// shopkeepers start trading
3119 							startTradingServer(my, monsterclicked);
3120 						}
3121 					}
3122 					else if ( players[monsterclicked] && players[monsterclicked]->entity )
3123 					{
3124 						if ( !my->checkEnemy(players[monsterclicked]->entity) )
3125 						{
3126 							// shopkeepers start trading
3127 							startTradingServer(my, monsterclicked);
3128 							if ( stats[monsterclicked] && stats[monsterclicked]->type == HUMAN && stats[monsterclicked]->appearance == 0
3129 								&& stats[monsterclicked]->playerRace == RACE_AUTOMATON )
3130 							{
3131 								achievementObserver.updatePlayerAchievement(monsterclicked, AchievementObserver::Achievement::BARONY_ACH_REAL_BOY,
3132 									AchievementObserver::AchievementEvent::REAL_BOY_SHOP);
3133 							}
3134 						}
3135 					}
3136 				}
3137 			}
3138 		}
3139 	}
3140 
3141 	bool isIllusionTaunt = false;
3142 	if ( myStats->type == INCUBUS && !strncmp(myStats->name, "inner demon", strlen("inner demon")) )
3143 	{
3144 		isIllusionTaunt = true;
3145 		hasrangedweapon = false;
3146 		Entity* myTarget = uidToEntity(static_cast<Uint32>(my->monsterIllusionTauntingThisUid));
3147 		if ( myTarget )
3148 		{
3149 			if ( my->ticks % 50 == 0 )
3150 			{
3151 				if ( myTarget->monsterTarget != my->getUID() )
3152 				{
3153 					switch ( myTarget->getRace() )
3154 					{
3155 						case LICH:
3156 						case DEVIL:
3157 						case LICH_FIRE:
3158 						case LICH_ICE:
3159 						case MINOTAUR:
3160 							break;
3161 						default:
3162 							myTarget->monsterAcquireAttackTarget(*my, MONSTER_STATE_PATH);
3163 							break;
3164 					}
3165 				}
3166 			}
3167 			if ( my->isMobile() && my->ticks > 10 )
3168 			{
3169 				if ( (my->monsterState != MONSTER_STATE_WAIT && my->monsterHitTime >= 30 && my->monsterHitTime <= 40)
3170 					|| (my->ticks >= 100 && my->monsterAttack == 0) )
3171 				{
3172 					my->monsterReleaseAttackTarget();
3173 					my->attack(MONSTER_POSE_INCUBUS_TAUNT, 0, nullptr);
3174 				}
3175 				else if ( my->monsterState == MONSTER_STATE_WAIT )
3176 				{
3177 					my->monsterHitTime = HITRATE - 3;
3178 					if ( entityDist(my, myTarget) > STRIKERANGE * 1.5 )
3179 					{
3180 						my->monsterState = MONSTER_STATE_PATH;
3181 						my->monsterTarget = myTarget->getUID();
3182 						my->monsterTargetX = myTarget->x;
3183 						my->monsterTargetY = myTarget->y;
3184 					}
3185 					else
3186 					{
3187 						my->monsterState = MONSTER_STATE_ATTACK;
3188 						my->monsterTarget = myTarget->getUID();
3189 						my->monsterTargetX = myTarget->x;
3190 						my->monsterTargetY = myTarget->y;
3191 					}
3192 				}
3193 			}
3194 		}
3195 		else
3196 		{
3197 			my->modHP(-9999);
3198 		}
3199 	}
3200 
3201 	if ( my->isMobile() )
3202 	{
3203 		// ghouls rise out of the dirt :O
3204 		if ( myStats->type == GHOUL )
3205 		{
3206 			if ( my->z > -.25 )
3207 			{
3208 				my->z -= .25;
3209 				if ( my->z < -.25 )
3210 				{
3211 					my->z = -.25;
3212 				}
3213 				ghoulMoveBodyparts(my, myStats, 0);
3214 				return;
3215 			}
3216 		}
3217 
3218 		// being bumped by someone friendly
3219 		std::vector<list_t*> entLists = TileEntityList.getEntitiesWithinRadiusAroundEntity(my, 2);
3220 		for ( std::vector<list_t*>::iterator it = entLists.begin(); it != entLists.end(); ++it )
3221 		{
3222 			list_t* currentList = *it;
3223 			for ( node2 = currentList->first; node2 != nullptr; node2 = node2->next ) //Can't convert to map.creatures because of doorframes.
3224 			{
3225 				entity = (Entity*)node2->element;
3226 				if ( entity == my )
3227 				{
3228 					continue;
3229 				}
3230 				if ( entity->behavior != &actMonster && entity->behavior != &actPlayer && entity->behavior != &actDoorFrame )
3231 				{
3232 					continue;
3233 				}
3234 				if ( entityInsideEntity(my, entity) && entity->getRace() != GYROBOT )
3235 				{
3236 					if ( entity->behavior != &actDoorFrame )
3237 					{
3238 						double tangent = atan2(my->y - entity->y, my->x - entity->x);
3239 						MONSTER_VELX = cos(tangent) * .1;
3240 						MONSTER_VELY = sin(tangent) * .1;
3241 					}
3242 					else
3243 					{
3244 						if ( entity->yaw >= -0.1 && entity->yaw <= 0.1 )
3245 						{
3246 							// east/west doorway
3247 							if ( my->y < floor(my->y / 16) * 16 + 8 )
3248 							{
3249 								// slide south
3250 								MONSTER_VELX = 0;
3251 								MONSTER_VELY = .25;
3252 							}
3253 							else
3254 							{
3255 								// slide north
3256 								MONSTER_VELX = 0;
3257 								MONSTER_VELY = -.25;
3258 							}
3259 						}
3260 						else
3261 						{
3262 							// north/south doorway
3263 							if ( my->x < floor(my->x / 16) * 16 + 8 )
3264 							{
3265 								// slide east
3266 								MONSTER_VELX = .25;
3267 								MONSTER_VELY = 0;
3268 							}
3269 							else
3270 							{
3271 								// slide west
3272 								MONSTER_VELX = -.25;
3273 								MONSTER_VELY = 0;
3274 							}
3275 						}
3276 						//messagePlayer(0, "path: %d", my->monsterPathCount);
3277 						++my->monsterPathCount;
3278 						if ( my->monsterPathCount > 50 )
3279 						{
3280 							my->monsterPathCount = 0;
3281 							monsterMoveAside(my, my);
3282 						}
3283 					}
3284 
3285 
3286 					if ( (entity->sprite == 274 || entity->sprite == 646
3287 						|| entity->sprite == 650 || entity->sprite == 304)
3288 						&& entity->flags[PASSABLE] == true )
3289 					{
3290 						// LICH/LICH_FIRE/LICH_ICE/DEVIL
3291 						// If these guys are PASSABLE then they're either dying or some other animation
3292 						// Move the monster inside the boss, but don't set PASSABLE to false again.
3293 						clipMove(&my->x, &my->y, MONSTER_VELX, MONSTER_VELY, my);
3294 					}
3295 					else
3296 					{
3297 						entity->flags[PASSABLE] = true;
3298 						clipMove(&my->x, &my->y, MONSTER_VELX, MONSTER_VELY, my);
3299 						entity->flags[PASSABLE] = false;
3300 					}
3301 				}
3302 			}
3303 		}
3304 
3305 		if ( myStats->type != LICH
3306 			&& myStats->type != DEVIL
3307 			&& myStats->type != LICH_ICE
3308 			&& myStats->type != LICH_FIRE
3309 			&& my->monsterSpecialTimer > 0 )
3310 		{
3311 			--my->monsterSpecialTimer;
3312 		}
3313 
3314 		if ( my->monsterAllySpecialCooldown > 0 )
3315 		{
3316 			--my->monsterAllySpecialCooldown;
3317 		}
3318 
3319 		if ( myStats->type == AUTOMATON )
3320 		{
3321 			my->automatonRecycleItem();
3322 		}
3323 
3324 		if ( myStats->EFFECTS[EFF_PACIFY] || myStats->EFFECTS[EFF_FEAR] )
3325 		{
3326 			my->monsterHitTime = HITRATE / 2; // stop this incrementing to HITRATE but leave monster ready to strike shortly after.
3327 		}
3328 
3329 		if ( my->monsterDefend != MONSTER_DEFEND_NONE )
3330 		{
3331 			if ( my->monsterState != MONSTER_STATE_ATTACK
3332 				|| myStats->shield == nullptr )
3333 			{
3334 				myStats->defending = false;
3335 				my->monsterDefend = 0;
3336 				serverUpdateEntitySkill(my, 47);
3337 			}
3338 			else if ( my->monsterAttack == 0 )
3339 			{
3340 				myStats->defending = true;
3341 			}
3342 		}
3343 		else
3344 		{
3345 			myStats->defending = false;
3346 		}
3347 
3348 		/*if ( myStats->defending )
3349 		{
3350 			messagePlayer(0, "defending!");
3351 		}*/
3352 
3353 		//if ( myStats->type == DEVIL )
3354 		//{
3355 		//	std::string state_string;
3356 
3357 		//	switch(my->monsterState)
3358 		//	{
3359 		//	case MONSTER_STATE_WAIT:
3360 		//		state_string = "WAIT";
3361 		//		break;
3362 		//	case MONSTER_STATE_ATTACK:
3363 		//		state_string = "CHARGE";
3364 		//		break;
3365 		//	case MONSTER_STATE_PATH:
3366 		//		state_string = "PATH";
3367 		//		break;
3368 		//	case MONSTER_STATE_HUNT:
3369 		//		state_string = "HUNT";
3370 		//		break;
3371 		//	case MONSTER_STATE_TALK:
3372 		//		state_string = "TALK";
3373 		//		break;
3374 		//	default:
3375 		//		state_string = std::to_string(my->monsterState);
3376 		//		//state_string = "Unknown state";
3377 		//		break;
3378 		//	}
3379 
3380 		//	messagePlayer(0, "%s, ATK: %d hittime:%d, atktime:%d, (%d|%d), timer:%d",
3381 		//		state_string.c_str(), my->monsterAttack, my->monsterHitTime, MONSTER_ATTACKTIME, devilstate, devilacted, my->monsterSpecialTimer); //Debug message.
3382 		//}
3383 
3384 		//Begin state machine
3385 		if ( my->monsterState == MONSTER_STATE_WAIT ) //Begin wait state
3386 		{
3387 			//my->monsterTarget = -1; //TODO: Setting it to -1 = Bug? -1 may not work properly for cases such as: if ( !my->monsterTarget )
3388 			my->monsterReleaseAttackTarget();
3389 			if ( !myStats->EFFECTS[EFF_KNOCKBACK] )
3390 			{
3391 				MONSTER_VELX = 0;
3392 				MONSTER_VELY = 0;
3393 			}
3394 			else
3395 			{
3396 				// do knockback movement
3397 				my->monsterHandleKnockbackVelocity(my->monsterKnockbackTangentDir, weightratio);
3398 				if ( abs(MONSTER_VELX) > 0.01 || abs(MONSTER_VELY) > 0.01 )
3399 				{
3400 					dist2 = clipMove(&my->x, &my->y, MONSTER_VELX, MONSTER_VELY, my);
3401 					my->handleKnockbackDamage(*myStats, hit.entity);
3402 				}
3403 			}
3404 			if ( myReflex && !myStats->EFFECTS[EFF_DISORIENTED] && !isIllusionTaunt )
3405 			{
3406 				if ( myStats->EFFECTS[EFF_FEAR] && my->monsterFearfulOfUid != 0 )
3407 				{
3408 					Entity* scaryEntity = uidToEntity(my->monsterFearfulOfUid);
3409 					if ( scaryEntity )
3410 					{
3411 						my->monsterAcquireAttackTarget(*scaryEntity, MONSTER_STATE_PATH);
3412 						my->lookAtEntity(*scaryEntity);
3413 						if ( previousMonsterState != my->monsterState )
3414 						{
3415 							serverUpdateEntitySkill(my, 0);
3416 						}
3417 						return;
3418 					}
3419 				}
3420 
3421 				for ( node2 = map.creatures->first; node2 != nullptr; node2 = node2->next ) //So my concern is that this never explicitly checks for actMonster or actPlayer, instead it relies on there being stats. Now, only monsters and players have stats, so that's not a problem, except...actPlayerLimb can still return a stat from getStat()! D: Meh, if you can find the player's hand, you can find the actual player too, so it shouldn't be an issue.
3422 				{
3423 					entity = (Entity*)node2->element;
3424 					if ( entity == my || entity->flags[PASSABLE] )
3425 					{
3426 						continue;
3427 					}
3428 					hitstats = entity->getStats();
3429 					if ( hitstats != nullptr )
3430 					{
3431 						if ( (my->checkEnemy(entity) || my->monsterTarget == entity->getUID() || ringconflict) )
3432 						{
3433 							tangent = atan2( entity->y - my->y, entity->x - my->x );
3434 							dir = my->yaw - tangent;
3435 							while ( dir >= PI )
3436 							{
3437 								dir -= PI * 2;
3438 							}
3439 							while ( dir < -PI )
3440 							{
3441 								dir += PI * 2;
3442 							}
3443 
3444 							// skip if light level is too low and distance is too high
3445 							int light = entity->entityLightAfterReductions(*hitstats, my);
3446 							if ( (myStats->type >= LICH && myStats->type < KOBOLD) || myStats->type == LICH_FIRE || myStats->type == LICH_ICE || myStats->type == SHADOW )
3447 							{
3448 								//See invisible.
3449 								light = 1000;
3450 							}
3451 							else if ( myStats->type == SENTRYBOT || myStats->type == SPELLBOT )
3452 							{
3453 								light += 150;
3454 							}
3455 							double targetdist = sqrt( pow(my->x - entity->x, 2) + pow(my->y - entity->y, 2) );
3456 
3457 							real_t monsterVisionRange = sightranges[myStats->type];
3458 							if ( hitstats->type == DUMMYBOT )
3459 							{
3460 								monsterVisionRange = std::min(monsterVisionRange, 96.0);
3461 							}
3462 
3463 							if ( targetdist > monsterVisionRange )
3464 							{
3465 								continue;
3466 							}
3467 							if ( targetdist > TOUCHRANGE && targetdist > light )
3468 							{
3469 								if ( !levitating )
3470 								{
3471 									lineTrace(my, my->x, my->y, tangent, monsterVisionRange, 0, true);
3472 								}
3473 								else
3474 								{
3475 									lineTrace(my, my->x, my->y, tangent, monsterVisionRange, 0, false);
3476 								}
3477 								if ( hit.entity == entity )
3478 									if ( rand() % 100 == 0 )
3479 									{
3480 										entity->increaseSkill(PRO_STEALTH);
3481 									}
3482 								continue;
3483 							}
3484 							bool visiontest = false;
3485 							if ( hitstats->type == DUMMYBOT || myStats->type == SENTRYBOT || myStats->type == SPELLBOT
3486 								|| (ringConflictHolder && ringConflictHolder == entity) )
3487 							{
3488 								if ( dir >= -13 * PI / 16 && dir <= 13 * PI / 16 )
3489 								{
3490 									visiontest = true;
3491 								}
3492 							}
3493 							else if ( myStats->type != SPIDER )
3494 							{
3495 								if ( dir >= -7 * PI / 16 && dir <= 7 * PI / 16 )
3496 								{
3497 									visiontest = true;
3498 								}
3499 							}
3500 							else
3501 							{
3502 								if ( dir >= -13 * PI / 16 && dir <= 13 * PI / 16 )
3503 								{
3504 									visiontest = true;
3505 								}
3506 							}
3507 
3508 							if ( visiontest )   // vision cone
3509 							{
3510 								if ( (myStats->type >= LICH && myStats->type < KOBOLD) || myStats->type == LICH_FIRE || myStats->type == LICH_ICE || myStats->type == SHADOW )
3511 								{
3512 									//See invisible
3513 									lineTrace(my, my->x, my->y, tangent, monsterVisionRange, 0, false);
3514 								}
3515 								else
3516 								{
3517 									lineTrace(my, my->x, my->y, tangent, monsterVisionRange, IGNORE_ENTITIES, false);
3518 								}
3519 								if ( !hit.entity )
3520 								{
3521 									lineTrace(my, my->x, my->y, tangent, TOUCHRANGE, 0, false);
3522 								}
3523 								if ( hit.entity == entity )
3524 								{
3525 									// charge state
3526 									Entity& attackTarget = *hit.entity;
3527 									my->monsterAcquireAttackTarget(attackTarget, MONSTER_STATE_ATTACK);
3528 
3529 									if ( MONSTER_SOUND == nullptr )
3530 									{
3531 										if ( myStats->type != MINOTAUR )
3532 										{
3533 											if ( myStats->type == SENTRYBOT || myStats->type == SPELLBOT )
3534 											{
3535 												sentrybotPickSpotNoise(my, myStats);
3536 											}
3537 											else
3538 											{
3539 												MONSTER_SOUND = playSoundEntity(my, MONSTER_SPOTSND + rand() % MONSTER_SPOTVAR, 128);
3540 											}
3541 										}
3542 										else
3543 										{
3544 											int c;
3545 											for ( c = 0; c < MAXPLAYERS; ++c )
3546 											{
3547 												if ( c == 0 )
3548 												{
3549 													MONSTER_SOUND = playSoundPlayer( c, MONSTER_SPOTSND + rand() % MONSTER_SPOTVAR, 128 );
3550 												}
3551 												else
3552 												{
3553 													playSoundPlayer( c, MONSTER_SPOTSND + rand() % MONSTER_SPOTVAR, 128 );
3554 												}
3555 											}
3556 										}
3557 									}
3558 
3559 									if ( entity != nullptr )
3560 									{
3561 										if ( entity->behavior == &actPlayer && myStats->type != DUMMYBOT )
3562 										{
3563 											assailant[entity->skill[2]] = true;  // as long as this is active, combat music doesn't turn off
3564 											assailantTimer[entity->skill[2]] = COMBAT_MUSIC_COOLDOWN;
3565 										}
3566 									}
3567 
3568 									// alert other monsters of this enemy's presence //TODO: Refactor into its own function.
3569 									for ( node = map.creatures->first; node != nullptr; node = node->next )
3570 									{
3571 										entity = (Entity*)node->element;
3572 										if ( entity->behavior == &actMonster )
3573 										{
3574 											hitstats = entity->getStats();
3575 											if ( hitstats != nullptr )
3576 											{
3577 												if ( entity->checkFriend(my) )
3578 												{
3579 													if ( entity->skill[0] == MONSTER_STATE_WAIT )   // monster is waiting
3580 													{
3581 														tangent = atan2( entity->y - my->y, entity->x - my->x );
3582 														lineTrace(my, my->x, my->y, tangent, monsterVisionRange, 0, false);
3583 														if ( hit.entity == entity )
3584 														{
3585 															entity->monsterAcquireAttackTarget(attackTarget, MONSTER_STATE_PATH);
3586 														}
3587 													}
3588 												}
3589 											}
3590 										}
3591 									}
3592 									break;
3593 								}
3594 							}
3595 						}
3596 					}
3597 				}
3598 			}
3599 
3600 			// minotaurs and liches chase players relentlessly.
3601 			if (myReflex)
3602 			{
3603 				if (myStats->type == MINOTAUR
3604 					|| myStats->type == LICH
3605 					|| myStats->type == LICH_FIRE
3606 					|| myStats->type == LICH_ICE
3607 					|| (myStats->type == CREATURE_IMP && strstr(map.name, "Boss") && !my->monsterAllyGetPlayerLeader())
3608 					|| (myStats->type == AUTOMATON && strstr(myStats->name, "corrupted automaton"))
3609 					|| (myStats->type == SHADOW && !strncmp(map.name, "Hell Boss", 9) && uidToEntity(my->parent) && uidToEntity(my->parent)->getRace() == DEVIL) )
3610 				{
3611 					double distToPlayer = 0;
3612 					int c, playerToChase = -1;
3613 					for (c = 0; c < MAXPLAYERS; c++)
3614 					{
3615 						if (players[c] && players[c]->entity)
3616 						{
3617 							list_t* playerPath = generatePath((int)floor(my->x / 16), (int)floor(my->y / 16),
3618 								(int)floor(players[c]->entity->x / 16), (int)floor(players[c]->entity->y / 16), my, players[c]->entity);
3619 							if ( playerPath == NULL )
3620 							{
3621 								continue;
3622 							}
3623 							else
3624 							{
3625 								list_FreeAll(playerPath);
3626 								free(playerPath);
3627 							}
3628 							if (!distToPlayer)
3629 							{
3630 								distToPlayer = sqrt(pow(my->x - players[c]->entity->x, 2) + pow(my->y - players[c]->entity->y, 2));
3631 								playerToChase = c;
3632 							}
3633 							else
3634 							{
3635 								double newDistToPlayer = sqrt(pow(my->x - players[c]->entity->x, 2) + pow(my->y - players[c]->entity->y, 2));
3636 								if (newDistToPlayer < distToPlayer)
3637 								{
3638 									distToPlayer = newDistToPlayer;
3639 									playerToChase = c;
3640 								}
3641 							}
3642 						}
3643 					}
3644 					if ( playerToChase >= 0 && players[playerToChase] && players[playerToChase]->entity )
3645 					{
3646 						if ( myStats->type == SHADOW )
3647 						{
3648 							my->monsterAcquireAttackTarget(*players[playerToChase]->entity, MONSTER_STATE_ATTACK);
3649 						}
3650 						else
3651 						{
3652 							my->monsterAcquireAttackTarget(*players[playerToChase]->entity, MONSTER_STATE_PATH);
3653 						}
3654 						if ( previousMonsterState != my->monsterState )
3655 						{
3656 							serverUpdateEntitySkill(my, 0);
3657 						}
3658 						return;
3659 					}
3660 				}
3661 				else if ( myStats->type == SHADOW && my->monsterTarget && my->monsterState != MONSTER_STATE_ATTACK )
3662 				{
3663 					//Fix shadow state.
3664 					my->monsterState = MONSTER_STATE_PATH;
3665 					//my->monsterTargetX = my->monsterTarget.x;
3666 					//my->monsterTargetY = my->monsterTarget.y;
3667 					serverUpdateEntitySkill(my, 0); //Update monster state because it changed.
3668 					return;
3669 				}
3670 			}
3671 
3672 			// follow the leader :)
3673 			if ( myStats->leader_uid != 0
3674 				&& my->monsterAllyState == ALLY_STATE_DEFAULT
3675 				&& my->getUID() % TICKS_PER_SECOND == ticks % TICKS_PER_SECOND
3676 				&& !myStats->EFFECTS[EFF_FEAR]
3677 				&& !myStats->EFFECTS[EFF_DISORIENTED]
3678 				&& !isIllusionTaunt
3679 				&& !monsterIsImmobileTurret(my, myStats) )
3680 			{
3681 				Entity* leader = uidToEntity(myStats->leader_uid);
3682 				if ( leader )
3683 				{
3684 					real_t followx = leader->x;
3685 					real_t followy = leader->y;
3686 					if ( myStats->type == GYROBOT )
3687 					{
3688 						// follow ahead of the leader.
3689 						real_t startx = leader->x;
3690 						real_t starty = leader->y;
3691 						// draw line from the leaders direction until we hit a wall or 48 dist
3692 						real_t previousx = startx;
3693 						real_t previousy = starty;
3694 						for ( int iterations = 0; iterations < 16; ++iterations)
3695 						{
3696 							startx += 4 * cos(leader->yaw);
3697 							starty += 4 * sin(leader->yaw);
3698 							int index = (static_cast<int>(starty + 16 * sin(leader->yaw)) >> 4) * MAPLAYERS + (static_cast<int>(startx + 16 * cos(leader->yaw)) >> 4) * MAPLAYERS * map.height;
3699 							if ( !map.tiles[OBSTACLELAYER + index] )
3700 							{
3701 								// store the last known good coordinate
3702 								previousx = startx;
3703 								previousy = starty;
3704 							}
3705 							else if ( map.tiles[OBSTACLELAYER + index] )
3706 							{
3707 								// hit a wall.
3708 								break;
3709 							}
3710 							if ( sqrt(pow(leader->x - previousx, 2) + pow(leader->y - previousy, 2)) > WAIT_FOLLOWDIST )
3711 							{
3712 								break;
3713 							}
3714 						}
3715 						followx = previousx;
3716 						followy = previousy;
3717 						// createParticleFollowerCommand(previousx, previousy, 0, 174); debug particle
3718 					}
3719 					double dist = sqrt(pow(my->x - followx, 2) + pow(my->y - followy, 2));
3720 
3721 					if ( dist > WAIT_FOLLOWDIST )
3722 					{
3723 						bool doFollow = true;
3724 						if ( my->monsterTarget != 0 )
3725 						{
3726 							doFollow = my->isFollowerFreeToPathToPlayer(myStats);
3727 						}
3728 
3729 						if ( doFollow )
3730 						{
3731 							my->monsterReleaseAttackTarget();
3732 							if ( my->monsterSetPathToLocation(static_cast<int>(followx) / 16, static_cast<int>(followy) / 16, 2) )
3733 							{
3734 								my->monsterState = MONSTER_STATE_HUNT; // hunt state
3735 							}
3736 							if ( previousMonsterState != my->monsterState )
3737 							{
3738 								serverUpdateEntitySkill(my, 0);
3739 								if ( my->monsterAllyIndex > 0 && my->monsterAllyIndex < MAXPLAYERS )
3740 								{
3741 									serverUpdateEntitySkill(my, 1); // update monsterTarget for player leaders.
3742 								}
3743 							}
3744 							return;
3745 						}
3746 					}
3747 					else if ( myStats->type != GYROBOT )
3748 					{
3749 						tangent = atan2( leader->y - my->y, leader->x - my->x );
3750 						lineTrace(my, my->x, my->y, tangent, sightranges[myStats->type], 0, true);
3751 						if ( hit.entity != leader )
3752 						{
3753 							bool doFollow = true;
3754 							if ( my->monsterTarget != 0 )
3755 							{
3756 								doFollow = my->isFollowerFreeToPathToPlayer(myStats);
3757 							}
3758 							if ( doFollow )
3759 							{
3760 								my->monsterReleaseAttackTarget();
3761 								if ( my->monsterSetPathToLocation(static_cast<int>(leader->x) / 16, static_cast<int>(leader->y) / 16, 1) )
3762 								{
3763 									my->monsterState = MONSTER_STATE_HUNT; // hunt state
3764 								}
3765 								if ( previousMonsterState != my->monsterState )
3766 								{
3767 									serverUpdateEntitySkill(my, 0);
3768 									if ( my->monsterAllyIndex > 0 && my->monsterAllyIndex < MAXPLAYERS )
3769 									{
3770 										serverUpdateEntitySkill(my, 1); // update monsterTarget for player leaders.
3771 									}
3772 								}
3773 								return;
3774 							}
3775 						}
3776 					}
3777 				}
3778 			}
3779 
3780 			// look
3781 			my->monsterLookTime++;
3782 			if ( my->monsterLookTime >= 120
3783 				&& myStats->type != LICH
3784 				&& myStats->type != DEVIL
3785 				&& myStats->type != LICH_FIRE
3786 				&& myStats->type != LICH_ICE )
3787 			{
3788 				my->monsterLookTime = 0;
3789 				my->monsterMoveTime--;
3790 				if ( myStats->type != GHOUL && (myStats->type != SPIDER || (myStats->type == SPIDER && my->monsterAllyGetPlayerLeader()))
3791 					&& !myStats->EFFECTS[EFF_FEAR] && !isIllusionTaunt )
3792 				{
3793 					if ( monsterIsImmobileTurret(my, myStats) )
3794 					{
3795 						if ( abs(my->monsterSentrybotLookDir) > 0.001 )
3796 						{
3797 							my->monsterLookDir = my->monsterSentrybotLookDir + (-30 + rand() % 61) * PI / 180;
3798 						}
3799 						else
3800 						{
3801 							my->monsterLookDir = (rand() % 360) * PI / 180;
3802 						}
3803 					}
3804 					else
3805 					{
3806 						my->monsterLookDir = (rand() % 360) * PI / 180;
3807 					}
3808 				}
3809 				if ( !myStats->EFFECTS[EFF_FEAR] && my->monsterTarget == 0 && my->monsterState == MONSTER_STATE_WAIT && my->monsterAllyGetPlayerLeader() )
3810 				{
3811 					// allies should try intelligently scan for enemies in radius.
3812 					if ( monsterIsImmobileTurret(my, myStats) && myStats->LVL < 5 )
3813 					{
3814 						// don't scan cause dumb robot.
3815 					}
3816 					else
3817 					{
3818 						real_t dist = sightranges[myStats->type];
3819 						for ( node = map.creatures->first; node != nullptr; node = node->next )
3820 						{
3821 							Entity* target = (Entity*)node->element;
3822 							if ( target->behavior == &actMonster && my->checkEnemy(target) )
3823 							{
3824 								real_t oldDist = dist;
3825 								dist = sqrt(pow(my->x - target->x, 2) + pow(my->y - target->y, 2));
3826 								if ( dist < sightranges[myStats->type] && dist <= oldDist )
3827 								{
3828 									double tangent = atan2(target->y - my->y, target->x - my->x);
3829 									lineTrace(my, my->x, my->y, tangent, sightranges[myStats->type], 0, false);
3830 									if ( hit.entity == target )
3831 									{
3832 										//my->monsterLookTime = 1;
3833 										//my->monsterMoveTime = rand() % 10 + 1;
3834 										my->monsterLookDir = tangent;
3835 										if ( monsterIsImmobileTurret(my, myStats) )
3836 										{
3837 											if ( myStats->LVL >= 10 )
3838 											{
3839 												my->monsterHitTime = HITRATE * 2 - 20;
3840 											}
3841 										}
3842 										break;
3843 									}
3844 								}
3845 							}
3846 						}
3847 					}
3848 				}
3849 				if ( rand() % 3 == 0 && !isIllusionTaunt )
3850 				{
3851 					if ( !MONSTER_SOUND )
3852 					{
3853 						if ( myStats->type != MINOTAUR )
3854 						{
3855 							if ( !my->monsterAllyGetPlayerLeader() || (my->monsterAllyGetPlayerLeader() && rand() % 3 == 0) || myStats->type == DUMMYBOT )
3856 							{
3857 								// idle sounds. if player follower, reduce noise frequency by 66%.
3858 								MONSTER_SOUND = playSoundEntity(my, MONSTER_IDLESND + (rand() % MONSTER_IDLEVAR), 128);
3859 							}
3860 						}
3861 						else
3862 						{
3863 							int c;
3864 							for ( c = 0; c < MAXPLAYERS; c++ )
3865 							{
3866 								if ( c == 0 )
3867 								{
3868 									MONSTER_SOUND = playSoundPlayer( c, MONSTER_SPOTSND + rand() % MONSTER_SPOTVAR, 128 );
3869 								}
3870 								else
3871 								{
3872 									playSoundPlayer( c, MONSTER_SPOTSND + rand() % MONSTER_SPOTVAR, 128 );
3873 								}
3874 							}
3875 						}
3876 					}
3877 				}
3878 			}
3879 			if ( my->monsterMoveTime == 0
3880 				&& (uidToEntity(myStats->leader_uid) == NULL || my->monsterAllyState == ALLY_STATE_DEFEND)
3881 				&& !myStats->EFFECTS[EFF_FEAR]
3882 				&& !myStats->EFFECTS[EFF_DISORIENTED]
3883 				&& !isIllusionTaunt
3884 				&& !(monsterIsImmobileTurret(my, myStats))
3885 				&& myStats->type != DEVIL )
3886 			{
3887 				std::vector<std::pair<int, int>> possibleCoordinates;
3888 				my->monsterMoveTime = rand() % 30;
3889 				int goodspots = 0;
3890 				int centerX = static_cast<int>(my->x / 16); // grab the coordinates in small form.
3891 				int centerY = static_cast<int>(my->y / 16); // grab the coordinates in small form.
3892 				int lowerX = std::max<int>(0, centerX - (map.width / 2)); // assigned upper/lower x coords from entity start position.
3893 				int upperX = std::min<int>(centerX + (map.width / 2), map.width);
3894 
3895 				int lowerY = std::max<int>(0, centerY - (map.height / 2)); // assigned upper/lower y coords from entity start position.
3896 				int upperY = std::min<int>(centerY + (map.height / 2), map.height);
3897 				//messagePlayer(0, "my x: %d, my y: %d, rangex: (%d-%d), rangey: (%d-%d)", centerX, centerY, lowerX, upperX, lowerY, upperY);
3898 
3899 				if ( myStats->type != SHOPKEEPER && (myStats->MISC_FLAGS[STAT_FLAG_NPC] == 0 && my->monsterAllyState == ALLY_STATE_DEFAULT) )
3900 				{
3901 					for ( x = lowerX; x < upperX; x++ )
3902 					{
3903 						for ( y = lowerY; y < upperY; y++ )
3904 						{
3905 							if ( !checkObstacle(x << 4, y << 4, my, NULL) )
3906 							{
3907 								goodspots++;
3908 								possibleCoordinates.push_back(std::make_pair(x, y));
3909 							}
3910 						}
3911 					}
3912 				}
3913 				else
3914 				{
3915 					for ( x = 0; x < map.width; x++ )
3916 					{
3917 						for ( y = 0; y < map.height; y++ )
3918 						{
3919 							if ( x << 4 >= my->monsterPathBoundaryXStart && x << 4 <= my->monsterPathBoundaryXEnd
3920 								&& y << 4 >= my->monsterPathBoundaryYStart && y << 4 <= my->monsterPathBoundaryYEnd )
3921 								if ( !checkObstacle(x << 4, y << 4, my, NULL) )
3922 								{
3923 									goodspots++;
3924 									possibleCoordinates.push_back(std::make_pair(x, y));
3925 								}
3926 						}
3927 					}
3928 				}
3929 				if ( goodspots )
3930 				{
3931 					int chosenspot = rand() % goodspots;
3932 					int currentspot = 0;
3933 					bool foundit = false;
3934 					x = possibleCoordinates.at(chosenspot).first;
3935 					y = possibleCoordinates.at(chosenspot).second;
3936 					//messagePlayer(0, "Chose distance: %.1fpercent", 100 * (sqrt(pow(my->x / 16 - (x), 2) + pow(my->y / 16 - (y), 2))) / sqrt(pow(map.height, 2) + pow(map.width, 2)));
3937 					/*for ( x = 0; x < map.width; x++ )
3938 					{
3939 						for ( y = 0; y < map.height; y++ )
3940 						{
3941 							if ( !checkObstacle(x << 4, y << 4, my, NULL) )
3942 							{
3943 								if ( currentspot == chosenspot )
3944 								{
3945 									foundit = true;
3946 									break;
3947 								}
3948 								else
3949 								{
3950 									currentspot++;
3951 								}
3952 							}
3953 						}
3954 						if ( foundit )
3955 						{
3956 							break;
3957 						}
3958 					}*/
3959 					path = generatePath( (int)floor(my->x / 16), (int)floor(my->y / 16), x, y, my, NULL );
3960 					if ( my->children.first != NULL )
3961 					{
3962 						list_RemoveNode(my->children.first);
3963 					}
3964 					node = list_AddNodeFirst(&my->children);
3965 					node->element = path;
3966 					node->deconstructor = &listDeconstructor;
3967 					my->monsterState = MONSTER_STATE_HUNT; // hunt state
3968 				}
3969 			}
3970 
3971 			// rotate monster
3972 			dir = my->monsterRotate();
3973 
3974 			if ( myStats->type == SHADOW && !uidToEntity(my->monsterTarget) && my->monsterSpecialTimer == 0 && my->monsterSpecialState == 0 && ticks%500 == 0 && rand()%5 == 0 )
3975 			{
3976 				//Random chance for a shadow to teleport around the map if it has nothing better to do.
3977 				//messagePlayer(0, "Shadow idle telepotty.");
3978 				my->monsterSpecialState = SHADOW_TELEPORT_ONLY;
3979 				my->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_SHADOW_PASIVE_TELEPORT;
3980 				my->shadowTeleportToTarget(nullptr, 3); // teleport in closer range
3981 				my->monsterState = MONSTER_STATE_WAIT;
3982 			}
3983 		} //End wait state
3984 		else if ( my->monsterState == MONSTER_STATE_ATTACK ) //Begin charge state
3985 		{
3986 			entity = uidToEntity(my->monsterTarget);
3987 			if ( entity == nullptr )
3988 			{
3989 				my->monsterState = MONSTER_STATE_WAIT;
3990 				if ( previousMonsterState != my->monsterState )
3991 				{
3992 					serverUpdateEntitySkill(my, 0);
3993 					if ( my->monsterAllyIndex > 0 && my->monsterAllyIndex < MAXPLAYERS )
3994 					{
3995 						serverUpdateEntitySkill(my, 1); // update monsterTarget for player leaders.
3996 					}
3997 				}
3998 				if ( myStats->type == SHADOW )
3999 				{
4000 					//messagePlayer(0, "DEBUG: Shadow lost entity.");
4001 					my->monsterReleaseAttackTarget(true);
4002 					my->monsterState = MONSTER_STATE_WAIT;
4003 					serverUpdateEntitySkill(my, 0); //Update state.
4004 				}
4005 				return;
4006 			}
4007 			if ( entity != nullptr )
4008 			{
4009 				if ( entity->behavior == &actPlayer && myStats->type != DUMMYBOT )
4010 				{
4011 					assailant[entity->skill[2]] = true;  // as long as this is active, combat music doesn't turn off
4012 					assailantTimer[entity->skill[2]] = COMBAT_MUSIC_COOLDOWN;
4013 				}
4014 			}
4015 			my->monsterTargetX = entity->x;
4016 			my->monsterTargetY = entity->y;
4017 			hitstats = entity->getStats();
4018 
4019 			if ( myStats->type == SHOPKEEPER && strncmp(map.name, "Mages Guild", 11) )
4020 			{
4021 				// shopkeepers hold a grudge against players
4022 				for ( c = 0; c < MAXPLAYERS; ++c )
4023 				{
4024 					if ( players[c] && players[c]->entity )
4025 					{
4026 						if ( my->monsterTarget == players[c]->entity->getUID() )
4027 						{
4028 							if ( stats[c] && stats[c]->type == HUMAN && !stats[c]->EFFECTS[EFF_POLYMORPH] )
4029 							{
4030 								swornenemies[SHOPKEEPER][HUMAN] = true;
4031 								monsterally[SHOPKEEPER][HUMAN] = false;
4032 							}
4033 							else if ( stats[c] && stats[c]->type == AUTOMATON && !stats[c]->EFFECTS[EFF_POLYMORPH] )
4034 							{
4035 								swornenemies[SHOPKEEPER][AUTOMATON] = true;
4036 								monsterally[SHOPKEEPER][AUTOMATON] = false;
4037 							}
4038 							break;
4039 						}
4040 					}
4041 				}
4042 			}
4043 
4044 			if ( myStats->type != DEVIL )
4045 			{
4046 				// skip if light level is too low and distance is too high
4047 				int light = entity->entityLightAfterReductions(*hitstats, my);
4048 				if ( (myStats->type >= LICH && myStats->type < KOBOLD) || myStats->type == LICH_FIRE || myStats->type == LICH_ICE || myStats->type == SHADOW )
4049 				{
4050 					//See invisible.
4051 					light = 1000;
4052 				}
4053 				else if ( myStats->type == SENTRYBOT || myStats->type == SPELLBOT )
4054 				{
4055 					light += 150;
4056 				}
4057 				double targetdist = sqrt( pow(my->x - entity->x, 2) + pow(my->y - entity->y, 2) );
4058 
4059 				real_t monsterVisionRange = sightranges[myStats->type];
4060 				if ( hitstats && hitstats->type == DUMMYBOT )
4061 				{
4062 					monsterVisionRange = std::min(monsterVisionRange, 96.0);
4063 				}
4064 				if ( myStats->EFFECTS[EFF_FEAR] )
4065 				{
4066 					targetdist = 0.0; // so we can always see our scary target.
4067 				}
4068 
4069 				if ( targetdist > monsterVisionRange )
4070 				{
4071 					// if target has left my sight, decide whether or not to path or retreat (stay put).
4072 					if ( my->shouldRetreat(*myStats) && !myStats->EFFECTS[EFF_FEAR] )
4073 					{
4074 						my->monsterMoveTime = 0;
4075 						my->monsterState = MONSTER_STATE_WAIT; // wait state
4076 					}
4077 					else
4078 					{
4079 						my->monsterState = MONSTER_STATE_PATH; // path state
4080 					}
4081 				}
4082 				else
4083 				{
4084 					if ( targetdist > TOUCHRANGE && targetdist > light && myReflex )
4085 					{
4086 						tangent = atan2( my->monsterTargetY - my->y, my->monsterTargetX - my->x );
4087 						if ( !levitating )
4088 						{
4089 							lineTrace(my, my->x, my->y, tangent, monsterVisionRange, 0, true);
4090 						}
4091 						else
4092 						{
4093 							lineTrace(my, my->x, my->y, tangent, monsterVisionRange, 0, false);
4094 						}
4095 						if ( hit.entity == entity )
4096 						{
4097 							if ( rand() % 100 == 0 )
4098 							{
4099 								entity->increaseSkill(PRO_STEALTH);
4100 							}
4101 						}
4102 						// if target is within sight range but light level is too low and out of melee range.
4103 						// decide whether or not to path or retreat (stay put).
4104 						if ( my->shouldRetreat(*myStats) && !myStats->EFFECTS[EFF_FEAR] )
4105 						{
4106 							my->monsterMoveTime = 0;
4107 							my->monsterState = MONSTER_STATE_WAIT; // wait state
4108 						}
4109 						else
4110 						{
4111 							my->monsterState = MONSTER_STATE_PATH; // path state
4112 						}
4113 					}
4114 					else
4115 					{
4116 						if ( myStats->EFFECTS[EFF_FEAR] )
4117 						{
4118 							myReflex = false; // don't determine if you lost sight of the scary monster.
4119 						}
4120 
4121 						if ( myReflex )
4122 						{
4123 							tangent = atan2( my->monsterTargetY - my->y, my->monsterTargetX - my->x );
4124 
4125 							if ( myStats->MISC_FLAGS[STAT_FLAG_MONSTER_CAST_INVENTORY_SPELLBOOKS] > 0 && !hasrangedweapon )
4126 							{
4127 								if ( rand() % 10 == 0 )
4128 								{
4129 									node_t* node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), SPELLBOOK);
4130 									if ( node != nullptr )
4131 									{
4132 										bool swapped = swapMonsterWeaponWithInventoryItem(my, myStats, node, true, true);
4133 										if ( swapped )
4134 										{
4135 											my->monsterSpecialState = MONSTER_SPELLCAST_GENERIC;
4136 											int timer = (myStats->MISC_FLAGS[STAT_FLAG_MONSTER_CAST_INVENTORY_SPELLBOOKS] >> 4) & 0xFFFF;
4137 											my->monsterSpecialTimer = timer > 0 ? timer : 250;
4138 											hasrangedweapon = true;
4139 										}
4140 									}
4141 								}
4142 							}
4143 
4144 							if ( !levitating )
4145 							{
4146 								if ( hasrangedweapon )
4147 								{
4148 									dist = lineTrace(my, my->x, my->y, tangent, monsterVisionRange, 0, false);
4149 								}
4150 								else
4151 								{
4152 									dist = lineTrace(my, my->x, my->y, tangent, monsterVisionRange, 0, true);
4153 								}
4154 							}
4155 							else
4156 							{
4157 								dist = lineTrace(my, my->x, my->y, tangent, monsterVisionRange, 0, false);
4158 							}
4159 						}
4160 						else
4161 						{
4162 							dist = sqrt( pow(my->x - entity->x, 2) + pow(my->y - entity->y, 2) );
4163 						}
4164 
4165 						if ( hit.entity != entity && myReflex )
4166 						{
4167 							// if I currently lost sight of my target in a straight line in front of me
4168 							// decide whether or not to path or retreat (stay put).
4169 							if ( my->shouldRetreat(*myStats) && !myStats->EFFECTS[EFF_FEAR] )
4170 							{
4171 								my->monsterMoveTime = 0;
4172 								my->monsterState = MONSTER_STATE_WAIT; // wait state
4173 							}
4174 							else
4175 							{
4176 								my->monsterState = MONSTER_STATE_PATH; // path state
4177 							}
4178 						}
4179 						else
4180 						{
4181 							// chaaaarge
4182 							tangent = atan2( entity->y - my->y, entity->x - my->x );
4183 							double tangent2 = tangent;
4184 
4185 							// get movement dir
4186 							int goAgain = 0;
4187 timeToGoAgain:
4188 							if ( targetdist > TOUCHRANGE * 1.5 && !hasrangedweapon && !my->shouldRetreat(*myStats) && my->getINT() > -2 )
4189 							{
4190 								if ( MONSTER_FLIPPEDANGLE < 5 )
4191 								{
4192 									if ( (my->ticks + my->getUID()) % (TICKS_PER_SECOND * 4) > TICKS_PER_SECOND * 2 )
4193 									{
4194 										tangent2 += PI / 6;
4195 									}
4196 									else
4197 									{
4198 										tangent2 -= PI / 6;
4199 									}
4200 								}
4201 								else
4202 								{
4203 									if ( (my->ticks + my->getUID()) % (TICKS_PER_SECOND * 4) > TICKS_PER_SECOND * 2 )
4204 									{
4205 										tangent2 += PI / 6;
4206 									}
4207 									else
4208 									{
4209 										tangent2 -= PI / 6;
4210 									}
4211 								}
4212 
4213 								Entity* tempHitEntity = hit.entity;
4214 								if ( lineTrace(my, my->x, my->x, tangent2, TOUCHRANGE, 1, false) < TOUCHRANGE )
4215 								{
4216 									MONSTER_FLIPPEDANGLE = (MONSTER_FLIPPEDANGLE < 5) * 10;
4217 									goAgain++;
4218 									if ( goAgain < 2 )
4219 									{
4220 										hit.entity = tempHitEntity;
4221 										goto timeToGoAgain;
4222 									}
4223 									else
4224 									{
4225 										tangent2 = tangent;
4226 									}
4227 								}
4228 								hit.entity = tempHitEntity;
4229 							}
4230 							else
4231 							{
4232 								tangent2 = tangent;
4233 							}
4234 
4235 							int myDex = my->monsterGetDexterityForMovement();
4236 							real_t maxVelX = cos(tangent2) * .045 * (myDex + 10) * weightratio;
4237 							real_t maxVelY = sin(tangent2) * .045 * (myDex + 10) * weightratio;
4238 							if ( !myStats->EFFECTS[EFF_KNOCKBACK] )
4239 							{
4240 								MONSTER_VELX = maxVelX;
4241 								MONSTER_VELY = maxVelY;
4242 							}
4243 
4244 							int rangedWeaponDistance = 160;
4245 							if ( hasrangedweapon )
4246 							{
4247 								int effectiveDistance = my->getMonsterEffectiveDistanceOfRangedWeapon(myStats->weapon);
4248 								if ( effectiveDistance < rangedWeaponDistance )
4249 								{
4250 									// shorter range xbows etc should advance at a little less than the extremity.
4251 									rangedWeaponDistance = effectiveDistance - 10;
4252 								}
4253 								if ( myStats->weapon && myStats->weapon->type == SPELLBOOK_DASH )
4254 								{
4255 									rangedWeaponDistance = TOUCHRANGE;
4256 								}
4257 							}
4258 
4259 							if ( monsterIsImmobileTurret(my, myStats) )
4260 							{
4261 								// this is just so that the monster rotates. it doesn't actually move
4262 								MONSTER_VELX = maxVelX * 0.01;
4263 								MONSTER_VELY = maxVelY * 0.01;
4264 							}
4265 							else if ( !myStats->EFFECTS[EFF_KNOCKBACK] &&
4266 								((dist > 16 && !hasrangedweapon && !my->shouldRetreat(*myStats))
4267 									|| (hasrangedweapon && dist > rangedWeaponDistance)) )
4268 							{
4269 								if ( my->shouldRetreat(*myStats) )
4270 								{
4271 									MONSTER_VELX *= -.5;
4272 									MONSTER_VELY *= -.5;
4273 									dist2 = clipMove(&my->x, &my->y, MONSTER_VELX, MONSTER_VELY, my);
4274 								}
4275 								else
4276 								{
4277 									dist2 = clipMove(&my->x, &my->y, MONSTER_VELX, MONSTER_VELY, my);
4278 								}
4279 								if ( hit.entity != NULL )
4280 								{
4281 									if ( hit.entity->behavior == &actDoor )
4282 									{
4283 										// opens the door if unlocked and monster can do it
4284 										if ( !hit.entity->doorLocked && my->getINT() > -2 )
4285 										{
4286 											if ( !hit.entity->doorDir && !hit.entity->doorStatus )
4287 											{
4288 												hit.entity->doorStatus = 1 + (my->x > hit.entity->x);
4289 												playSoundEntity(hit.entity, 21, 96);
4290 											}
4291 											else if ( hit.entity->doorDir && !hit.entity->doorStatus )
4292 											{
4293 												hit.entity->doorStatus = 1 + (my->y < hit.entity->y);
4294 												playSoundEntity(hit.entity, 21, 96);
4295 											}
4296 										}
4297 										else
4298 										{
4299 											// can't open door, so break it down
4300 											my->monsterHitTime++;
4301 											if ( my->monsterHitTime >= HITRATE )
4302 											{
4303 												my->monsterAttack = my->getAttackPose(); // random attack motion
4304 												my->monsterHitTime = 0;
4305 												hit.entity->doorHealth--; // decrease door health
4306 												if ( myStats->STR > 20 )
4307 												{
4308 													hit.entity->doorHealth -= static_cast<int>(std::max((myStats->STR - 20), 0) / 3); // decrease door health
4309 													hit.entity->doorHealth = std::max(hit.entity->doorHealth, 0);
4310 												}
4311 												if ( myStats->type == MINOTAUR )
4312 												{
4313 													hit.entity->doorHealth = 0;    // minotaurs smash doors instantly
4314 												}
4315 												playSoundEntity(hit.entity, 28, 64);
4316 												if ( hit.entity->doorHealth <= 0 )
4317 												{
4318 													// set direction of splinters
4319 													if ( !hit.entity->doorDir )
4320 													{
4321 														hit.entity->doorSmacked = (my->x > hit.entity->x);
4322 													}
4323 													else
4324 													{
4325 														hit.entity->doorSmacked = (my->y < hit.entity->y);
4326 													}
4327 												}
4328 											}
4329 										}
4330 									}
4331 									else if ( hit.entity->behavior == &actFurniture )
4332 									{
4333 										// break it down!
4334 										my->monsterHitTime++;
4335 										if ( my->monsterHitTime >= HITRATE )
4336 										{
4337 											my->monsterAttack = my->getAttackPose(); // random attack motion
4338 											my->monsterHitTime = HITRATE / 4;
4339 											hit.entity->furnitureHealth--; // decrease door health
4340 											if ( myStats->STR > 20 )
4341 											{
4342 												hit.entity->furnitureHealth -= static_cast<int>(std::max((myStats->STR - 20), 0) / 3); // decrease door health
4343 												hit.entity->furnitureHealth = std::max(hit.entity->furnitureHealth, 0);
4344 											}
4345 											if ( myStats->type == MINOTAUR )
4346 											{
4347 												hit.entity->furnitureHealth = 0;    // minotaurs smash furniture instantly
4348 											}
4349 											playSoundEntity(hit.entity, 28, 64);
4350 										}
4351 									}
4352 									else
4353 									{
4354 										if ( my->shouldRetreat(*myStats) && !myStats->EFFECTS[EFF_FEAR] )
4355 										{
4356 											my->monsterMoveTime = 0;
4357 											my->monsterState = MONSTER_STATE_WAIT; // wait state
4358 										}
4359 										else
4360 										{
4361 											my->monsterState = MONSTER_STATE_PATH; // path state
4362 										}
4363 									}
4364 								}
4365 								else
4366 								{
4367 									if ( my->shouldRetreat(*myStats) && !myStats->EFFECTS[EFF_FEAR] )
4368 									{
4369 										my->monsterMoveTime = 0;
4370 										my->monsterState = MONSTER_STATE_WAIT; // wait state
4371 									}
4372 									else if ( dist2 <= 0.1 && myStats->HP > myStats->MAXHP / 3 )
4373 									{
4374 										my->monsterState = MONSTER_STATE_PATH; // path state
4375 									}
4376 								}
4377 							}
4378 							else
4379 							{
4380 								if ( my->backupWithRangedWeapon(*myStats, dist, hasrangedweapon) || my->shouldRetreat(*myStats) )
4381 								{
4382 									// injured monsters or monsters with ranged weapons back up
4383 									if ( myStats->type == LICH_ICE )
4384 									{
4385 										double strafeTangent = tangent2;
4386 										//messagePlayer(0, "strafe: %d", my->monsterStrafeDirection);
4387 										if ( ticks % 10 == 0 && my->monsterStrafeDirection != 0 && rand() % 10 == 0 )
4388 										{
4389 											Entity* lichAlly = nullptr;
4390 											if ( my->monsterLichAllyUID != 0 )
4391 											{
4392 												lichAlly = uidToEntity(my->monsterLichAllyUID);
4393 											}
4394 											Entity* tmpEntity = hit.entity;
4395 											lineTrace(my, my->x, my->y, tangent, monsterVisionRange, 0, false);
4396 											if ( hit.entity != tmpEntity )
4397 											{
4398 												// sight is blocked, keep strafing.
4399 											}
4400 											else
4401 											{
4402 												my->monsterStrafeDirection = 0;
4403 											}
4404 											hit.entity = tmpEntity;
4405 										}
4406 										if ( dist < 64 )
4407 										{
4408 											// move diagonally
4409 											strafeTangent -= ((PI / 4) * my->monsterStrafeDirection);
4410 										}
4411 										else
4412 										{
4413 											// move sideways (dist between 64 and 100 from backupWithRangedWeapon)
4414 											strafeTangent -= ((PI / 2) * my->monsterStrafeDirection);
4415 										}
4416 										MONSTER_VELX = cos(strafeTangent) * .045 * (my->getDEX() + 10) * weightratio * -.5;
4417 										MONSTER_VELY = sin(strafeTangent) * .045 * (my->getDEX() + 10) * weightratio * -.5;
4418 									}
4419 									else
4420 									{
4421 										int myDex = my->monsterGetDexterityForMovement();
4422 										real_t maxVelX = cos(tangent2) * .045 * (myDex + 10) * weightratio * -.5;
4423 										real_t maxVelY = sin(tangent2) * .045 * (myDex + 10) * weightratio * -.5;
4424 										if ( myStats->EFFECTS[EFF_KNOCKBACK] )
4425 										{
4426 											my->monsterHandleKnockbackVelocity(tangent2, weightratio);
4427 										}
4428 										else
4429 										{
4430 											MONSTER_VELX = maxVelX;
4431 											MONSTER_VELY = maxVelY;
4432 										}
4433 									}
4434 									dist2 = clipMove(&my->x, &my->y, MONSTER_VELX, MONSTER_VELY, my);
4435 									my->handleKnockbackDamage(*myStats, hit.entity);
4436 								}
4437 								else
4438 								{
4439 									// this is just so that the monster rotates. it doesn't actually move
4440 									int myDex = my->monsterGetDexterityForMovement();
4441 									MONSTER_VELX = cos(tangent) * .02 * .045 * (myDex + 10) * weightratio;
4442 									MONSTER_VELY = sin(tangent) * .02 * .045 * (myDex + 10) * weightratio;
4443 								}
4444 							}
4445 
4446 							my->handleMonsterAttack(myStats, entity, dist);
4447 
4448 							// bust ceilings
4449 							/*if( myStats->type == MINOTAUR ) {
4450 								if( my->x>=0 && my->y>=0 && my->x<map.width<<4 && my->y<map.height<<4 ) {
4451 									if( map.tiles[MAPLAYERS+(int)(my->y/16)*MAPLAYERS+(int)(my->x/16)*MAPLAYERS*map.height] )
4452 										map.tiles[MAPLAYERS+(int)(my->y/16)*MAPLAYERS+(int)(my->x/16)*MAPLAYERS*map.height] = 0;
4453 								}
4454 							}*/
4455 
4456 							// rotate monster
4457 							if ( my->backupWithRangedWeapon(*myStats, dist, hasrangedweapon) || my->shouldRetreat(*myStats) )
4458 							{
4459 								int myDex = my->getDEX();
4460 								if ( my->monsterAllyGetPlayerLeader() )
4461 								{
4462 									myDex = std::min(myDex, MONSTER_ALLY_DEXTERITY_SPEED_CAP);
4463 								}
4464 								real_t tempVelX = cos(tangent2) * .045 * (myDex + 10) * weightratio * -.5;
4465 								real_t tempVelY = sin(tangent2) * .045 * (myDex + 10) * weightratio * -.5;
4466 								if ( myStats->type == LICH_ICE )
4467 								{
4468 									// override if we're strafing, keep facing the target
4469 									dir = my->yaw - atan2(-tempVelY, -tempVelX);
4470 								}
4471 								else if ( myStats->EFFECTS[EFF_KNOCKBACK] )
4472 								{
4473 									// in knockback, the velocitys change sign from negative/positive or positive/negative.
4474 									// this makes monsters moonwalk if the direction to rotate is assumed the same.
4475 									// so we compare goal velocity direction (sin or cos(tangent)) and see if we've reached that sign.
4476 
4477 									int multX = 1;
4478 									int multY = 1;
4479 									if ( cos(tangent2) >= 0 == MONSTER_VELX > 0 )
4480 									{
4481 										// same sign.
4482 										multX = 1;
4483 									}
4484 									else
4485 									{
4486 										// opposite signed.
4487 										multX = -1;
4488 									}
4489 									if ( sin(tangent2) >= 0 == MONSTER_VELY > 0 )
4490 									{
4491 										// same sign.
4492 										multY = 1;
4493 									}
4494 									else
4495 									{
4496 										// opposite signed.
4497 										multY = -1;
4498 									}
4499 									dir = my->yaw - atan2(multY * MONSTER_VELY, multX * MONSTER_VELX);
4500 								}
4501 								else
4502 								{
4503 									dir = my->yaw - atan2( -MONSTER_VELY, -MONSTER_VELX );
4504 								}
4505 							}
4506 							else
4507 							{
4508 								dir = my->yaw - atan2( MONSTER_VELY, MONSTER_VELX );
4509 							}
4510 							while ( dir >= PI )
4511 							{
4512 								dir -= PI * 2;
4513 							}
4514 							while ( dir < -PI )
4515 							{
4516 								dir += PI * 2;
4517 							}
4518 							my->yaw -= dir / 2;
4519 							while ( my->yaw < 0 )
4520 							{
4521 								my->yaw += 2 * PI;
4522 							}
4523 							while ( my->yaw >= 2 * PI )
4524 							{
4525 								my->yaw -= 2 * PI;
4526 							}
4527 						}
4528 					}
4529 				}
4530 			}
4531 			else
4532 			{
4533 				// devil specific code
4534 				if ( !MONSTER_ATTACK || MONSTER_ATTACK == 4 )
4535 				{
4536 					my->monsterSpecialTimer++;
4537 					int difficulty = 40;
4538 					int numPlayers = 0;
4539 
4540 					for ( int c = 0; c < MAXPLAYERS; ++c )
4541 					{
4542 						if ( players[c] && players[c]->entity )
4543 						{
4544 							++numPlayers;
4545 						}
4546 					}
4547 					if ( numPlayers > 0 )
4548 					{
4549 						difficulty /= numPlayers; // 40/20/13/10 - basically how long you get to wail on Baphy. Shorter is harder.
4550 					}
4551 
4552 					if ( my->monsterSpecialTimer > 60 || (devilstate == 72 && my->monsterSpecialTimer > difficulty))
4553 					{
4554 						if ( !devilstate ) // devilstate is 0 at the start of the fight and doesn't return to 0.
4555 						{
4556 							if ( !MONSTER_ATTACK )
4557 							{
4558 								int c;
4559 								for ( c = 0; c < MAXPLAYERS; c++ )
4560 								{
4561 									playSoundPlayer(c, 204, 64);
4562 								}
4563 								playSoundEntity(my, 204, 128);
4564 								MONSTER_ATTACK = 4;
4565 								MONSTER_ATTACKTIME = 0;
4566 								MONSTER_ARMBENDED = 1;
4567 								serverUpdateEntitySkill(my, 8);
4568 								serverUpdateEntitySkill(my, 9);
4569 								serverUpdateEntitySkill(my, 10);
4570 								for ( int c = 0; c < MAXPLAYERS; ++c )
4571 								{
4572 									if ( players[c] && players[c]->entity )
4573 									{
4574 										my->devilSummonMonster(nullptr, SHADOW, 5, c);
4575 									}
4576 								}
4577 								my->devilSummonMonster(nullptr, DEMON, 5);
4578 							}
4579 							else if ( MONSTER_ATTACKTIME > 90 )
4580 							{
4581 								my->monsterState = MONSTER_STATE_DEVIL_TELEPORT; // devil teleport state
4582 							}
4583 						}
4584 						else
4585 						{
4586 							if ( !devilacted )
4587 							{
4588 								switch ( devilstate )
4589 								{
4590 									case 72:
4591 										my->monsterState = MONSTER_STATE_DEVIL_SUMMON; // devil summoning state
4592 										break;
4593 									case 73:
4594 										MONSTER_ATTACK = 5 + rand() % 2; // fireballs
4595 										break;
4596 									case 74:
4597 										my->monsterState = MONSTER_STATE_DEVIL_BOULDER; // devil boulder drop
4598 										break;
4599 								}
4600 								devilacted = 1;
4601 							}
4602 							else
4603 							{
4604 								if ( rand() % 2 && devilstate == 73 )
4605 								{
4606 									MONSTER_ATTACK = 5 + rand() % 2; // more fireballs
4607 								}
4608 								else
4609 								{
4610 									my->monsterState = MONSTER_STATE_DEVIL_TELEPORT; // devil teleport state
4611 								}
4612 							}
4613 						}
4614 						my->monsterSpecialTimer = 0;
4615 					}
4616 				}
4617 				else if ( MONSTER_ATTACK == 5 || MONSTER_ATTACK == 6 )
4618 				{
4619 					// throw fireballs
4620 					my->yaw = my->yaw + MONSTER_WEAPONYAW;
4621 					castSpell(my->getUID(), &spell_fireball, true, false);
4622 					my->yaw = my->yaw - MONSTER_WEAPONYAW;
4623 
4624 					// let's throw one specifically aimed at our players to be mean.
4625 					if ( MONSTER_ATTACKTIME == 10 )
4626 					{
4627 						real_t oldYaw = my->yaw;
4628 						tangent = atan2(entity->y - my->y, entity->x - my->x);
4629 						my->yaw = tangent;
4630 						Entity* fireball = castSpell(my->getUID(), &spell_fireball, true, false);
4631 						for ( c = 0; c < MAXPLAYERS; ++c )
4632 						{
4633 							if ( players[c] && players[c]->entity && entity != players[c]->entity )
4634 							{
4635 								tangent = atan2(entity->y - my->y, entity->x - my->x);
4636 								real_t dir = oldYaw - tangent;
4637 								while ( dir >= PI )
4638 								{
4639 									dir -= PI * 2;
4640 								}
4641 								while ( dir < -PI )
4642 								{
4643 									dir += PI * 2;
4644 								}
4645 								if ( dir >= -7 * PI / 16 && dir <= 7 * PI / 16 )
4646 								{
4647 									my->yaw = tangent;
4648 									Entity* fireball = castSpell(my->getUID(), &spell_fireball, true, false);
4649 								}
4650 							}
4651 						}
4652 						my->yaw = oldYaw;
4653 					}
4654 				}
4655 
4656 				// rotate monster
4657 				tangent = atan2( entity->y - my->y, entity->x - my->x );
4658 				MONSTER_VELX = cos(tangent);
4659 				MONSTER_VELY = sin(tangent);
4660 				dir = my->yaw - atan2( MONSTER_VELY, MONSTER_VELX );
4661 				while ( dir >= PI )
4662 				{
4663 					dir -= PI * 2;
4664 				}
4665 				while ( dir < -PI )
4666 				{
4667 					dir += PI * 2;
4668 				}
4669 				my->yaw -= dir / 2;
4670 				while ( my->yaw < 0 )
4671 				{
4672 					my->yaw += 2 * PI;
4673 				}
4674 				while ( my->yaw >= 2 * PI )
4675 				{
4676 					my->yaw -= 2 * PI;
4677 				}
4678 			}
4679 		} //End charge state
4680 		else if ( my->monsterState == MONSTER_STATE_PATH )     //Begin path state
4681 		{
4682 			if ( myStats->type == DEVIL )
4683 			{
4684 				my->monsterState = MONSTER_STATE_ATTACK;
4685 				if ( previousMonsterState != my->monsterState )
4686 				{
4687 					serverUpdateEntitySkill(my, 0);
4688 				}
4689 				return;
4690 			}
4691 			else if ( myStats->type == DUMMYBOT )
4692 			{
4693 				my->monsterState = MONSTER_STATE_WAIT;
4694 				my->monsterMoveTime = 0;
4695 				return;
4696 			}
4697 			else if ( monsterIsImmobileTurret(my, myStats) )
4698 			{
4699 				my->monsterState = MONSTER_STATE_WAIT;
4700 				if ( previousMonsterState != my->monsterState )
4701 				{
4702 					serverUpdateEntitySkill(my, 0);
4703 				}
4704 				return;
4705 			}
4706 
4707 			//Don't path if your target dieded!
4708 			if ( uidToEntity(my->monsterTarget) == nullptr && my->monsterTarget != 0 )
4709 			{
4710 				my->monsterReleaseAttackTarget(true);
4711 				my->monsterState = MONSTER_STATE_WAIT; // wait state
4712 				if ( previousMonsterState != my->monsterState )
4713 				{
4714 					serverUpdateEntitySkill(my, 0);
4715 				}
4716 				return;
4717 			}
4718 
4719 			entity = uidToEntity(my->monsterTarget);
4720 			if ( entity != nullptr )
4721 			{
4722 				if ( entity->behavior == &actPlayer )
4723 				{
4724 					assailant[entity->skill[2]] = true;  // as long as this is active, combat music doesn't turn off
4725 					assailantTimer[entity->skill[2]] = COMBAT_MUSIC_COOLDOWN;
4726 				}
4727 				my->monsterTargetX = entity->x;
4728 				my->monsterTargetY = entity->y;
4729 			}
4730 			x = ((int)floor(my->monsterTargetX)) >> 4;
4731 			y = ((int)floor(my->monsterTargetY)) >> 4;
4732 			path = generatePath( (int)floor(my->x / 16), (int)floor(my->y / 16), x, y, my, uidToEntity(my->monsterTarget) );
4733 			if ( my->children.first != nullptr )
4734 			{
4735 				list_RemoveNode(my->children.first);
4736 			}
4737 			node = list_AddNodeFirst(&my->children);
4738 			node->element = path;
4739 			node->deconstructor = &listDeconstructor;
4740 			my->monsterState = MONSTER_STATE_HUNT; // hunt state
4741 			/*if ( myStats->type == SHADOW && entity )
4742 			{
4743 				if ( path == nullptr )
4744 				{
4745 					messagePlayer(0, "Warning: Shadow failed to generate a path to its target.");
4746 				}
4747 			}*/
4748 		} //End path state.
4749 		else if ( my->monsterState == MONSTER_STATE_HUNT ) //Begin hunt state
4750 		{
4751 			if ( myStats->type == SHADOW && my->monsterSpecialState == SHADOW_TELEPORT_ONLY )
4752 			{
4753 				//messagePlayer(0, "Shadow in special state teleport only! Aborting hunt state.");
4754 				my->monsterState = MONSTER_STATE_WAIT;
4755 				return; //Don't do anything, yer casting a spell!
4756 			}
4757 			//Do the shadow's passive teleport to catch up to their target..
4758 			if ( myStats->type == SHADOW && my->monsterSpecialTimer == 0 && my->monsterTarget )
4759 			{
4760 				Entity* target = uidToEntity(my->monsterTarget);
4761 				if ( !target )
4762 				{
4763 					my->monsterReleaseAttackTarget(true);
4764 					my->monsterState = MONSTER_STATE_WAIT;
4765 					serverUpdateEntitySkill(my, 0); //Update state.
4766 					return;
4767 				}
4768 
4769 				//If shadow has no path to target, then should do the passive teleport.
4770 				bool passiveTeleport = false;
4771 				if ( my->children.first ) //First child is the path.
4772 				{
4773 					if ( !my->children.first->element )
4774 					{
4775 						//messagePlayer(0, "No path for shadow!");
4776 						passiveTeleport = true;
4777 					}
4778 				}
4779 				else
4780 				{
4781 					//messagePlayer(0, "No path for shadow!");
4782 					passiveTeleport = true; //Path is saved as first child. If no first child, no path!
4783 				}
4784 
4785 				//Shadow has path to target, but still passive teleport if far enough away.
4786 				if ( !passiveTeleport )
4787 				{
4788 					int specialRoll = rand() % 50;
4789 					//messagePlayer(0, "roll %d", specialRoll);
4790 					double targetdist = sqrt(pow(my->x - entity->x, 2) + pow(my->y - entity->y, 2));
4791 					if ( specialRoll <= (2 + (targetdist > 80 ? 4 : 0)) )
4792 					{
4793 						passiveTeleport = true;
4794 					}
4795 				}
4796 
4797 				if ( passiveTeleport )
4798 				{
4799 					//messagePlayer(0, "Shadow is doing a passive tele.");
4800 					my->monsterSpecialState = SHADOW_TELEPORT_ONLY;
4801 					my->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_SHADOW_PASIVE_TELEPORT;
4802 					my->shadowTeleportToTarget(target, 3); // teleport in closer range
4803 					my->monsterState = MONSTER_STATE_WAIT;
4804 					if ( target && target->behavior == actPlayer )
4805 					{
4806 						messagePlayer(target->skill[2], language[2518]);
4807 					}
4808 					return;
4809 				}
4810 			}
4811 
4812 			if ( myReflex && (myStats->type != LICH || my->monsterSpecialTimer <= 0) )
4813 			{
4814 				for ( node2 = map.creatures->first; node2 != nullptr; node2 = node2->next ) //Stats only exist on a creature, so don't iterate all map.entities.
4815 				{
4816 					entity = (Entity*)node2->element;
4817 					if ( entity == my || entity->flags[PASSABLE] )
4818 					{
4819 						continue;
4820 					}
4821 					hitstats = entity->getStats();
4822 					if ( hitstats != nullptr )
4823 					{
4824 						if ( (my->checkEnemy(entity) || my->monsterTarget == entity->getUID() || ringconflict) )
4825 						{
4826 							tangent = atan2( entity->y - my->y, entity->x - my->x );
4827 							dir = my->yaw - tangent;
4828 							while ( dir >= PI )
4829 							{
4830 								dir -= PI * 2;
4831 							}
4832 							while ( dir < -PI )
4833 							{
4834 								dir += PI * 2;
4835 							}
4836 
4837 							// skip if light level is too low and distance is too high
4838 							int light = entity->entityLightAfterReductions(*hitstats, my);
4839 							if ( (myStats->type >= LICH && myStats->type < KOBOLD) || myStats->type == LICH_FIRE || myStats->type == LICH_ICE || myStats->type == SHADOW )
4840 							{
4841 								//See invisible.
4842 								light = 1000;
4843 							}
4844 							double targetdist = sqrt( pow(my->x - entity->x, 2) + pow(my->y - entity->y, 2) );
4845 
4846 							real_t monsterVisionRange = sightranges[myStats->type];
4847 							if ( hitstats->type == DUMMYBOT )
4848 							{
4849 								monsterVisionRange = std::min(monsterVisionRange, 96.0);
4850 							}
4851 
4852 							if ( targetdist > monsterVisionRange )
4853 							{
4854 								continue;
4855 							}
4856 							if ( targetdist > TOUCHRANGE && targetdist > light )
4857 							{
4858 								if ( !levitating )
4859 								{
4860 									lineTrace(my, my->x, my->y, tangent, monsterVisionRange, 0, true);
4861 								}
4862 								else
4863 								{
4864 									lineTrace(my, my->x, my->y, tangent, monsterVisionRange, 0, false);
4865 								}
4866 								if ( hit.entity == entity )
4867 								{
4868 									if ( rand() % 100 == 0 )
4869 									{
4870 										entity->increaseSkill(PRO_STEALTH);
4871 									}
4872 								}
4873 								continue;
4874 							}
4875 							bool visiontest = false;
4876 							if ( hitstats->type == DUMMYBOT || myStats->type == SENTRYBOT || myStats->type == SPELLBOT )
4877 							{
4878 								if ( dir >= -13 * PI / 16 && dir <= 13 * PI / 16 )
4879 								{
4880 									visiontest = true;
4881 								}
4882 							}
4883 							else if ( myStats->type != SPIDER )
4884 							{
4885 								if ( my->monsterAllyGetPlayerLeader() )
4886 								{
4887 									if ( dir >= -13 * PI / 16 && dir <= 13 * PI / 16 )
4888 									{
4889 										visiontest = true; // increase ally vision when hunting.
4890 									}
4891 								}
4892 								else
4893 								{
4894 									if ( dir >= -7 * PI / 16 && dir <= 7 * PI / 16 )
4895 									{
4896 										visiontest = true;
4897 									}
4898 								}
4899 							}
4900 							else
4901 							{
4902 								if ( dir >= -13 * PI / 16 && dir <= 13 * PI / 16 )
4903 								{
4904 									visiontest = true;
4905 								}
4906 							}
4907 							if ( visiontest )   // vision cone
4908 							{
4909 								lineTrace(my, my->x + 1, my->y, tangent, monsterVisionRange, 0, (levitating == false));
4910 								if ( hit.entity == entity )
4911 								{
4912 									lineTrace(my, my->x - 1, my->y, tangent, monsterVisionRange, 0, (levitating == false));
4913 									if ( hit.entity == entity )
4914 									{
4915 										lineTrace(my, my->x, my->y + 1, tangent, monsterVisionRange, 0, (levitating == false));
4916 										if ( hit.entity == entity )
4917 										{
4918 											lineTrace(my, my->x, my->y - 1, tangent, monsterVisionRange, 0, (levitating == false));
4919 											if ( hit.entity == entity )
4920 											{
4921 												Entity& attackTarget = *hit.entity;
4922 												// charge state
4923 												if ( my->monsterTarget == entity->getUID() )
4924 												{
4925 													// this is when a monster is chasing it's known target.
4926 													// let's to be ready to strike.
4927 													// otherwise, we bumped into a new unexpected target, don't modify hitTime
4928 													if ( hasrangedweapon )
4929 													{
4930 														// 120 ms reaction time
4931 														if ( my->monsterHitTime < HITRATE )
4932 														{
4933 															if ( myStats->weapon && itemCategory(myStats->weapon) == SPELLBOOK )
4934 															{
4935 																my->monsterHitTime = std::max(HITRATE, my->monsterHitTime);
4936 															}
4937 															else
4938 															{
4939 																my->monsterHitTime = std::max(HITRATE - 6, my->monsterHitTime);
4940 															}
4941 														}
4942 														else
4943 														{
4944 															// bows have 2x hitrate time compared to standard weapons.
4945 															my->monsterHitTime = std::max(2 * HITRATE - 6, my->monsterHitTime);
4946 														}
4947 													}
4948 													else
4949 													{
4950 														// melee 240ms
4951 														my->monsterHitTime = std::max(HITRATE - 12, my->monsterHitTime);
4952 													}
4953 												}
4954 												//messagePlayer(0, "hunt -> attack, %d", my->monsterHitTime);
4955 												my->monsterAcquireAttackTarget(attackTarget, MONSTER_STATE_ATTACK);
4956 
4957 												if ( MONSTER_SOUND == NULL )
4958 												{
4959 													if ( myStats->type != MINOTAUR )
4960 													{
4961 														if ( myStats->type != LICH || rand() % 3 == 0 )
4962 														{
4963 															if ( myStats->type == SENTRYBOT || myStats->type == SPELLBOT )
4964 															{
4965 																sentrybotPickSpotNoise(my, myStats);
4966 															}
4967 															else
4968 															{
4969 																MONSTER_SOUND = playSoundEntity(my, MONSTER_SPOTSND + rand() % MONSTER_SPOTVAR, 128);
4970 															}
4971 														}
4972 													}
4973 													else
4974 													{
4975 														int c;
4976 														for ( c = 0; c < MAXPLAYERS; c++ )
4977 														{
4978 															if ( c == 0 )
4979 															{
4980 																MONSTER_SOUND = playSoundPlayer( c, MONSTER_SPOTSND + rand() % MONSTER_SPOTVAR, 128 );
4981 															}
4982 															else
4983 															{
4984 																playSoundPlayer( c, MONSTER_SPOTSND + rand() % MONSTER_SPOTVAR, 128 );
4985 															}
4986 														}
4987 													}
4988 												}
4989 
4990 												if ( entity != nullptr )
4991 												{
4992 													if ( entity->behavior == &actPlayer && myStats->type != DUMMYBOT )
4993 													{
4994 														assailant[entity->skill[2]] = true;  // as long as this is active, combat music doesn't turn off
4995 														assailantTimer[entity->skill[2]] = COMBAT_MUSIC_COOLDOWN;
4996 													}
4997 												}
4998 												break;
4999 											}
5000 										}
5001 									}
5002 								}
5003 							}
5004 						}
5005 					}
5006 				}
5007 			}
5008 
5009 			// minotaurs and liches chase players relentlessly.
5010 			if ( myStats->type == MINOTAUR
5011 				|| (myStats->type == LICH && my->monsterSpecialTimer <= 0)
5012 				|| ((myStats->type == LICH_FIRE || myStats->type == LICH_ICE) && my->monsterSpecialTimer <= 0 )
5013 				|| (myStats->type == CREATURE_IMP && strstr(map.name, "Boss") && !my->monsterAllyGetPlayerLeader())
5014 				|| (myStats->type == AUTOMATON && strstr(myStats->name, "corrupted automaton")) )
5015 			{
5016 				bool shouldHuntPlayer = false;
5017 				Entity* playerOrNot = uidToEntity(my->monsterTarget);
5018 				if (playerOrNot)
5019 				{
5020 					if (ticks % 180 == 0 && playerOrNot->behavior == &actPlayer)
5021 					{
5022 						shouldHuntPlayer = true;
5023 					}
5024 				}
5025 				else if (ticks % 180 == 0)
5026 				{
5027 					shouldHuntPlayer = true;
5028 				}
5029 				if (shouldHuntPlayer)
5030 				{
5031 					double distToPlayer = 0;
5032 					int c, playerToChase = -1;
5033 					for (c = 0; c < MAXPLAYERS; c++)
5034 					{
5035 						if (players[c] && players[c]->entity)
5036 						{
5037 							list_t* playerPath = generatePath((int)floor(my->x / 16), (int)floor(my->y / 16),
5038 								(int)floor(players[c]->entity->x / 16), (int)floor(players[c]->entity->y / 16), my, players[c]->entity);
5039 							if ( playerPath == NULL )
5040 							{
5041 								continue;
5042 							}
5043 							else
5044 							{
5045 								list_FreeAll(playerPath);
5046 								free(playerPath);
5047 							}
5048 							if (!distToPlayer)
5049 							{
5050 								distToPlayer = sqrt(pow(my->x - players[c]->entity->x, 2) + pow(my->y - players[c]->entity->y, 2));
5051 								playerToChase = c;
5052 							}
5053 							else
5054 							{
5055 								double newDistToPlayer = sqrt(pow(my->x - players[c]->entity->x, 2) + pow(my->y - players[c]->entity->y, 2));
5056 								if (newDistToPlayer < distToPlayer)
5057 								{
5058 									distToPlayer = newDistToPlayer;
5059 									playerToChase = c;
5060 								}
5061 							}
5062 						}
5063 					}
5064 					if (playerToChase >= 0)
5065 					{
5066 						// path state
5067 						if ( players[playerToChase] && players[playerToChase]->entity )
5068 						{
5069 							my->monsterAcquireAttackTarget(*players[playerToChase]->entity, MONSTER_STATE_PATH);
5070 						}
5071 						if ( previousMonsterState != my->monsterState )
5072 						{
5073 							serverUpdateEntitySkill(my, 0);
5074 						}
5075 						return;
5076 					}
5077 				}
5078 			}
5079 			else if ( myStats->type == SHADOW && my->monsterTarget && (ticks % 180 == 0) )
5080 			{
5081 				if ( !uidToEntity(my->monsterTarget) )
5082 				{
5083 					my->monsterReleaseAttackTarget(true);
5084 					my->monsterState = MONSTER_STATE_WAIT;
5085 					serverUpdateEntitySkill(my, 0); //Update state.
5086 					if ( my->monsterAllyIndex > 0 && my->monsterAllyIndex < MAXPLAYERS )
5087 					{
5088 						serverUpdateEntitySkill(my, 1); // update monsterTarget for player leaders.
5089 					}
5090 					return;
5091 				}
5092 				my->monsterState = MONSTER_STATE_PATH;
5093 				serverUpdateEntitySkill(my, 0); //Update state.
5094 				if ( my->monsterAllyIndex > 0 && my->monsterAllyIndex < MAXPLAYERS )
5095 				{
5096 					serverUpdateEntitySkill(my, 1); // update monsterTarget for player leaders.
5097 				}
5098 				return;
5099 			}
5100 
5101 			// lich cooldown
5102 			if ( myStats->type == LICH )
5103 			{
5104 				if ( my->monsterSpecialTimer > 0 )
5105 				{
5106 					my->monsterSpecialTimer--;
5107 				}
5108 			}
5109 
5110 			// follow the leader :)
5111 			if ( uidToEntity(my->monsterTarget) == nullptr
5112 				&& myStats->leader_uid != 0 && my->monsterAllyState == ALLY_STATE_DEFAULT && my->getUID() % TICKS_PER_SECOND == ticks % TICKS_PER_SECOND
5113 				&& !monsterIsImmobileTurret(my, myStats) )
5114 			{
5115 				Entity* leader = uidToEntity(myStats->leader_uid);
5116 				if ( leader )
5117 				{
5118 					real_t followx = leader->x;
5119 					real_t followy = leader->y;
5120 					if ( myStats->type == GYROBOT )
5121 					{
5122 						// follow ahead of the leader.
5123 						real_t startx = leader->x;
5124 						real_t starty = leader->y;
5125 						// draw line from the leaders direction until we hit a wall or 48 dist
5126 						real_t previousx = startx;
5127 						real_t previousy = starty;
5128 						real_t leadDistance = HUNT_FOLLOWDIST;
5129 						for ( int iterations = 0; iterations < 16; ++iterations )
5130 						{
5131 							startx += 4 * cos(leader->yaw);
5132 							starty += 4 * sin(leader->yaw);
5133 							int index = (static_cast<int>(starty + 16 * sin(leader->yaw)) >> 4) * MAPLAYERS + (static_cast<int>(startx + 16 * cos(leader->yaw)) >> 4) * MAPLAYERS * map.height;
5134 							if ( !map.tiles[OBSTACLELAYER + index] )
5135 							{
5136 								// store the last known good coordinate
5137 								previousx = startx;
5138 								previousy = starty;
5139 							}
5140 							else if ( map.tiles[OBSTACLELAYER + index] )
5141 							{
5142 								break;
5143 							}
5144 							if ( sqrt(pow(leader->x - previousx, 2) + pow(leader->y - previousy, 2)) > HUNT_FOLLOWDIST )
5145 							{
5146 								break;
5147 							}
5148 						}
5149 						followx = previousx;
5150 						followy = previousy;
5151 						//createParticleFollowerCommand(previousx, previousy, 0, 175); debug particle
5152 					}
5153 					double dist = sqrt(pow(my->x - followx, 2) + pow(my->y - followy, 2));
5154 					if ( dist > HUNT_FOLLOWDIST  )
5155 					{
5156 						bool doFollow = true;
5157 						if ( my->monsterTarget != 0 )
5158 						{
5159 							doFollow = my->isFollowerFreeToPathToPlayer(myStats);
5160 						}
5161 
5162 						if ( doFollow )
5163 						{
5164 							x = ((int)floor(followx)) >> 4;
5165 							y = ((int)floor(followy)) >> 4;
5166 							int u, v;
5167 							bool foundplace = false;
5168 							for ( u = x - 1; u <= x + 1; u++ )
5169 							{
5170 								for ( v = y - 1; v <= y + 1; v++ )
5171 								{
5172 									if ( !checkObstacle((u << 4) + 8, (v << 4) + 8, my, leader) )
5173 									{
5174 										x = u;
5175 										y = v;
5176 										foundplace = true;
5177 										break;
5178 									}
5179 								}
5180 								if ( foundplace )
5181 								{
5182 									break;
5183 								}
5184 							}
5185 							path = generatePath( (int)floor(my->x / 16), (int)floor(my->y / 16), x, y, my, leader );
5186 							if ( my->children.first != NULL )
5187 							{
5188 								list_RemoveNode(my->children.first);
5189 							}
5190 							node = list_AddNodeFirst(&my->children);
5191 							node->element = path;
5192 							node->deconstructor = &listDeconstructor;
5193 							my->monsterState = MONSTER_STATE_HUNT; // hunt state
5194 							if ( previousMonsterState != my->monsterState )
5195 							{
5196 								serverUpdateEntitySkill(my, 0);
5197 							}
5198 							return;
5199 						}
5200 					}
5201 					else if ( myStats->type != GYROBOT )
5202 					{
5203 						bool doFollow = true;
5204 						if ( my->monsterTarget != 0 )
5205 						{
5206 							doFollow = my->isFollowerFreeToPathToPlayer(myStats);
5207 						}
5208 
5209 						if ( doFollow )
5210 						{
5211 							double tangent = atan2( leader->y - my->y, leader->x - my->x );
5212 							Entity* ohitentity = hit.entity;
5213 							lineTrace(my, my->x, my->y, tangent, sightranges[myStats->type], 0, true);
5214 							if ( hit.entity != leader )
5215 							{
5216 								my->monsterReleaseAttackTarget();
5217 								int x = ((int)floor(leader->x)) >> 4;
5218 								int y = ((int)floor(leader->y)) >> 4;
5219 								int u, v;
5220 								bool foundplace = false;
5221 								for ( u = x - 1; u <= x + 1; u++ )
5222 								{
5223 									for ( v = y - 1; v <= y + 1; v++ )
5224 									{
5225 										if ( !checkObstacle((u << 4) + 8, (v << 4) + 8, my, leader) )
5226 										{
5227 											x = u;
5228 											y = v;
5229 											foundplace = true;
5230 											break;
5231 										}
5232 									}
5233 									if ( foundplace )
5234 									{
5235 										break;
5236 									}
5237 								}
5238 								path = generatePath( (int)floor(my->x / 16), (int)floor(my->y / 16), x, y, my, leader );
5239 								if ( my->children.first != NULL )
5240 								{
5241 									list_RemoveNode(my->children.first);
5242 								}
5243 								node = list_AddNodeFirst(&my->children);
5244 								node->element = path;
5245 								node->deconstructor = &listDeconstructor;
5246 								my->monsterState = MONSTER_STATE_HUNT; // hunt state
5247 								if ( previousMonsterState != my->monsterState )
5248 								{
5249 									serverUpdateEntitySkill(my, 0);
5250 								}
5251 								return;
5252 							}
5253 							hit.entity = ohitentity;
5254 						}
5255 					}
5256 				}
5257 			}
5258 
5259 			entity = uidToEntity(my->monsterTarget);
5260 			if ( entity != NULL )
5261 			{
5262 				if ( entity->behavior == &actPlayer && myStats->type != DUMMYBOT )
5263 				{
5264 					assailant[entity->skill[2]] = true; // as long as this is active, combat music doesn't turn off
5265 					assailantTimer[entity->skill[2]] = COMBAT_MUSIC_COOLDOWN;
5266 				}
5267 			}
5268 			if ( my->children.first != NULL )
5269 			{
5270 				if ( my->children.first->element != NULL )
5271 				{
5272 					path = (list_t*)my->children.first->element;
5273 					if ( path->first != NULL )
5274 					{
5275 						pathnode = (pathnode_t*)path->first->element;
5276 						dist = sqrt( pow(pathnode->y * 16 + 8 - my->y, 2) + pow(pathnode->x * 16 + 8 - my->x, 2) );
5277 						if ( dist <= 2 )
5278 						{
5279 							list_RemoveNode(pathnode->node);
5280 							if ( rand() % 8 == 0 )
5281 							{
5282 								if ( !MONSTER_SOUND )
5283 								{
5284 									if ( myStats->type != MINOTAUR )
5285 									{
5286 										if ( !my->monsterAllyGetPlayerLeader() || (my->monsterAllyGetPlayerLeader() && rand() % 3 == 0) )
5287 										{
5288 											MONSTER_SOUND = playSoundEntity(my, MONSTER_IDLESND + (rand() % MONSTER_IDLEVAR), 128);
5289 										}
5290 									}
5291 									else
5292 									{
5293 										int c;
5294 										for ( c = 0; c < MAXPLAYERS; c++ )
5295 										{
5296 											if ( c == 0 )
5297 											{
5298 												MONSTER_SOUND = playSoundPlayer( c, MONSTER_IDLESND + (rand() % MONSTER_IDLEVAR), 128 );
5299 											}
5300 											else
5301 											{
5302 												playSoundPlayer( c, MONSTER_IDLESND + (rand() % MONSTER_IDLEVAR), 128 );
5303 											}
5304 										}
5305 									}
5306 								}
5307 							}
5308 						}
5309 						else
5310 						{
5311 							// move monster
5312 							tangent = atan2( pathnode->y * 16 + 8 - my->y, pathnode->x * 16 + 8 - my->x );
5313 							int myDex = my->getDEX();
5314 							if ( my->monsterAllyGetPlayerLeader() )
5315 							{
5316 								myDex = std::min(myDex, MONSTER_ALLY_DEXTERITY_SPEED_CAP);
5317 							}
5318 							real_t maxVelX = cos(tangent) * .045 * (myDex + 10) * weightratio;
5319 							real_t maxVelY = sin(tangent) * .045 * (myDex + 10) * weightratio;
5320 							if ( myStats->EFFECTS[EFF_KNOCKBACK] )
5321 							{
5322 								my->monsterHandleKnockbackVelocity(tangent, weightratio);
5323 							}
5324 							else
5325 							{
5326 								MONSTER_VELX = maxVelX;
5327 								MONSTER_VELY = maxVelY;
5328 							}
5329 							dist2 = clipMove(&my->x, &my->y, MONSTER_VELX, MONSTER_VELY, my);
5330 							my->handleKnockbackDamage(*myStats, hit.entity);
5331 							if ( hit.entity != NULL )
5332 							{
5333 								if ( hit.entity->behavior == &actDoor )
5334 								{
5335 									// opens the door if unlocked and monster can do it
5336 									if ( !hit.entity->doorLocked && my->getINT() > -2 )
5337 									{
5338 										if ( !hit.entity->doorDir && !hit.entity->doorStatus )
5339 										{
5340 											hit.entity->doorStatus = 1 + (my->x > hit.entity->x);
5341 											playSoundEntity(hit.entity, 21, 96);
5342 										}
5343 										else if ( hit.entity->doorDir && !hit.entity->doorStatus )
5344 										{
5345 											hit.entity->doorStatus = 1 + (my->y < hit.entity->y);
5346 											playSoundEntity(hit.entity, 21, 96);
5347 										}
5348 									}
5349 									else
5350 									{
5351 										// can't open door, so break it down
5352 										my->monsterHitTime++;
5353 										if ( my->monsterHitTime >= HITRATE )
5354 										{
5355 											my->monsterAttack = my->getAttackPose(); // random attack motion
5356 											my->monsterHitTime = 0;
5357 											hit.entity->doorHealth--; // decrease door health
5358 											if ( myStats->STR > 20 )
5359 											{
5360 												hit.entity->doorHealth -= static_cast<int>(std::max((myStats->STR - 20), 0) / 3); // decrease door health
5361 												hit.entity->doorHealth = std::max(hit.entity->doorHealth, 0);
5362 											}
5363 											if ( myStats->type == MINOTAUR )
5364 											{
5365 												hit.entity->doorHealth = 0;    // minotaurs smash doors instantly
5366 											}
5367 											playSoundEntity(hit.entity, 28, 64);
5368 											if ( hit.entity->doorHealth <= 0 )
5369 											{
5370 												// set direction of splinters
5371 												if ( !hit.entity->doorDir )
5372 												{
5373 													hit.entity->doorSmacked = (my->x > hit.entity->x);
5374 												}
5375 												else
5376 												{
5377 													hit.entity->doorSmacked = (my->y < hit.entity->y);
5378 												}
5379 											}
5380 										}
5381 									}
5382 								}
5383 								else if ( hit.entity->behavior == &actFurniture )
5384 								{
5385 									// break it down!
5386 									my->monsterHitTime++;
5387 									if ( my->monsterHitTime >= HITRATE )
5388 									{
5389 										my->monsterAttack = my->getAttackPose(); // random attack motion
5390 										my->monsterHitTime = HITRATE / 4;
5391 										hit.entity->furnitureHealth--; // decrease door health
5392 										if ( myStats->STR > 20 )
5393 										{
5394 											hit.entity->furnitureHealth -= static_cast<int>(std::max((myStats->STR - 20), 0) / 3); // decrease door health
5395 											hit.entity->furnitureHealth = std::max(hit.entity->furnitureHealth, 0);
5396 										}
5397 										if ( myStats->type == MINOTAUR )
5398 										{
5399 											hit.entity->furnitureHealth = 0;    // minotaurs smash furniture instantly
5400 										}
5401 										playSoundEntity(hit.entity, 28, 64);
5402 									}
5403 								}
5404 								else if ( hit.entity->behavior == &actMonster )
5405 								{
5406 									Stat* yourStats = hit.entity->getStats();
5407 									if ( hit.entity->getUID() == my->monsterTarget )
5408 									{
5409 										//TODO: Refactor with setMonsterStateAttack().
5410 										my->monsterState = MONSTER_STATE_ATTACK; // charge state
5411 
5412 										// this is when a monster is bumps into it's known target.
5413 										// let's to be ready to strike.
5414 										// otherwise, we bumped into a new unexpected target, don't modify hitTime
5415 										if ( hasrangedweapon )
5416 										{
5417 											// 120 ms reaction time
5418 											if ( my->monsterHitTime < HITRATE )
5419 											{
5420 												if ( myStats->weapon && itemCategory(myStats->weapon) == SPELLBOOK )
5421 												{
5422 													my->monsterHitTime = std::max(HITRATE, my->monsterHitTime);
5423 												}
5424 												else
5425 												{
5426 													my->monsterHitTime = std::max(HITRATE - 6, my->monsterHitTime);
5427 												}
5428 											}
5429 											else
5430 											{
5431 												// bows have 2x hitrate time compared to standard weapons.
5432 												my->monsterHitTime = std::max(2 * HITRATE - 6, my->monsterHitTime);
5433 											}
5434 										}
5435 										else
5436 										{
5437 											// melee 240ms
5438 											my->monsterHitTime = std::max(HITRATE - 12, my->monsterHitTime);
5439 										}
5440 										//messagePlayer(0, "bump1 -> attack, %d", my->monsterHitTime);
5441 									}
5442 									else if ( yourStats )
5443 									{
5444 										if ( !my->checkEnemy(hit.entity) )
5445 										{
5446 											// would you kindly move out of the way, sir?
5447 											if ( !monsterMoveAside(hit.entity, my) )
5448 											{
5449 												my->monsterState = MONSTER_STATE_PATH;    // try something else and remake path
5450 											}
5451 											++my->monsterPathCount;
5452 											if ( my->monsterPathCount > 100 )
5453 											{
5454 												my->monsterPathCount = 0;
5455 												//messagePlayer(0, "running into monster like a fool!");
5456 												my->monsterMoveBackwardsAndPath();
5457 											}
5458 										}
5459 										else if ( my->checkEnemy(hit.entity) )
5460 										{
5461 											// charge state
5462 											Entity& attackTarget = *hit.entity;
5463 											my->monsterAcquireAttackTarget(attackTarget, MONSTER_STATE_ATTACK);
5464 										}
5465 									}
5466 								}
5467 								else if ( hit.entity->behavior == &actPlayer )
5468 								{
5469 									if ( my->checkEnemy(hit.entity) )
5470 									{
5471 										// charge state
5472 										Entity& attackTarget = *hit.entity;
5473 										if ( my->monsterTarget == hit.entity->getUID() )
5474 										{
5475 											// this is when a monster is bumps into it's known target.
5476 											// let's to be ready to strike.
5477 											// otherwise, we bumped into a new unexpected target, don't modify hitTime
5478 											if ( hasrangedweapon )
5479 											{
5480 												// 120 ms reaction time
5481 												if ( my->monsterHitTime < HITRATE )
5482 												{
5483 													if ( myStats->weapon && itemCategory(myStats->weapon) == SPELLBOOK )
5484 													{
5485 														my->monsterHitTime = std::max(HITRATE, my->monsterHitTime);
5486 													}
5487 													else
5488 													{
5489 														my->monsterHitTime = std::max(HITRATE - 6, my->monsterHitTime);
5490 													}
5491 												}
5492 												else
5493 												{
5494 													// bows have 2x hitrate time compared to standard weapons.
5495 													my->monsterHitTime = std::max(2 * HITRATE - 6, my->monsterHitTime);
5496 												}
5497 											}
5498 											else
5499 											{
5500 												// melee 240ms
5501 												my->monsterHitTime = std::max(HITRATE - 12, my->monsterHitTime);
5502 											}
5503 										}
5504 										//messagePlayer(0, "bump2 -> attack, %d", my->monsterHitTime);
5505 										my->monsterAcquireAttackTarget(attackTarget, MONSTER_STATE_ATTACK);
5506 									}
5507 									else
5508 									{
5509 										my->monsterState = MONSTER_STATE_PATH; // try something else and remake path
5510 									}
5511 								}
5512 								else
5513 								{
5514 									my->monsterState = MONSTER_STATE_PATH; // remake path
5515 									if ( myStats->type != LICH_FIRE && myStats->type != LICH_ICE )
5516 									{
5517 										if ( hit.entity->behavior == &actGate || hit.entity->behavior == &actBoulder
5518 											 )
5519 										{
5520 											++my->monsterPathCount;
5521 											if ( hit.entity->behavior == &actBoulder )
5522 											{
5523 												my->monsterPathCount += 5;
5524 											}
5525 											if ( my->monsterPathCount > 100 )
5526 											{
5527 												my->monsterPathCount = 0;
5528 												//messagePlayer(0, "remaking path!");
5529 												my->monsterMoveBackwardsAndPath();
5530 											}
5531 										}
5532 										else
5533 										{
5534 											my->monsterPathCount = 0;
5535 										}
5536 									}
5537 								}
5538 							}
5539 							else
5540 							{
5541 								if ( dist2 <= 0.1 )
5542 								{
5543 									my->monsterState = MONSTER_STATE_PATH;    // remake path
5544 								}
5545 							}
5546 
5547 							// rotate monster
5548 							if ( myStats->EFFECTS[EFF_KNOCKBACK] )
5549 							{
5550 								// in knockback, the velocitys change sign from negative/positive or positive/negative.
5551 								// this makes monsters moonwalk if the direction to rotate is assumed the same.
5552 								// so we compare goal velocity direction (sin or cos(tangent)) and see if we've reached that sign.
5553 
5554 								int multX = 1;
5555 								int multY = 1;
5556 								if ( cos(tangent) >= 0 == MONSTER_VELX > 0 )
5557 								{
5558 									// same sign.
5559 									multX = 1;
5560 								}
5561 								else
5562 								{
5563 									// opposite signed.
5564 									multX = -1;
5565 								}
5566 								if ( sin(tangent) >= 0 == MONSTER_VELY > 0 )
5567 								{
5568 									// same sign.
5569 									multY = 1;
5570 								}
5571 								else
5572 								{
5573 									// opposite signed.
5574 									multY = -1;
5575 								}
5576 								dir = my->yaw - atan2(multY * MONSTER_VELY, multX * MONSTER_VELX);
5577 							}
5578 							else
5579 							{
5580 								dir = my->yaw - atan2(MONSTER_VELY, MONSTER_VELX);
5581 							}
5582 							while ( dir >= PI )
5583 							{
5584 								dir -= PI * 2;
5585 							}
5586 							while ( dir < -PI )
5587 							{
5588 								dir += PI * 2;
5589 							}
5590 							my->yaw -= dir / 2;
5591 							while ( my->yaw < 0 )
5592 							{
5593 								my->yaw += 2 * PI;
5594 							}
5595 							while ( my->yaw >= 2 * PI )
5596 							{
5597 								my->yaw -= 2 * PI;
5598 							}
5599 						}
5600 					}
5601 					else
5602 					{
5603 						Entity* target = uidToEntity(my->monsterTarget);
5604 						if ( target )
5605 						{
5606 							my->lookAtEntity(*target);
5607 							/*if ( myStats->type == SHADOW )
5608 							{
5609 								messagePlayer(0, "[SHADOW] No path #1: Resetting to wait state.");
5610 							}*/
5611 						}
5612 						my->monsterState = MONSTER_STATE_WAIT; // no path, return to wait state
5613 						if ( my->monsterAllyState == ALLY_STATE_MOVETO )
5614 						{
5615 							if ( my->monsterAllyInteractTarget != 0 )
5616 							{
5617 								//messagePlayer(0, "Interacting with a target!");
5618 								if ( my->monsterAllySetInteract() )
5619 								{
5620 									if ( myStats->type == GYROBOT )
5621 									{
5622 										my->monsterSpecialState = GYRO_INTERACT_LANDING;
5623 										my->monsterState = MONSTER_STATE_WAIT;
5624 										serverUpdateEntitySkill(my, 33); // for clients to keep track of animation
5625 									}
5626 									else
5627 									{
5628 										if ( FollowerMenu.entityToInteractWith && FollowerMenu.entityToInteractWith->behavior == &actItem )
5629 										{
5630 											//my->handleNPCInteractDialogue(*myStats, ALLY_EVENT_INTERACT_ITEM);
5631 										}
5632 										else
5633 										{
5634 											my->handleNPCInteractDialogue(*myStats, ALLY_EVENT_INTERACT_OTHER);
5635 										}
5636 										my->monsterAllyInteractTarget = 0;
5637 										my->monsterAllyState = ALLY_STATE_DEFAULT;
5638 									}
5639 								}
5640 							}
5641 							else
5642 							{
5643 								//messagePlayer(0, "Sent a move to command, defending here!");
5644 								// scan for enemies after reaching move point.
5645 								real_t dist = sightranges[myStats->type];
5646 								for ( node = map.creatures->first; node != nullptr; node = node->next )
5647 								{
5648 									Entity* target = (Entity*)node->element;
5649 									if ( target->behavior == &actMonster && my->checkEnemy(target) )
5650 									{
5651 										real_t oldDist = dist;
5652 										dist = sqrt(pow(my->x - target->x, 2) + pow(my->y - target->y, 2));
5653 										if ( dist < sightranges[myStats->type] && dist <= oldDist )
5654 										{
5655 											double tangent = atan2(target->y - my->y, target->x - my->x);
5656 											lineTrace(my, my->x, my->y, tangent, sightranges[myStats->type], 0, false);
5657 											if ( hit.entity == target )
5658 											{
5659 												my->monsterLookTime = 1;
5660 												my->monsterMoveTime = rand() % 10 + 1;
5661 												my->monsterLookDir = tangent;
5662 												break;
5663 											}
5664 										}
5665 									}
5666 								}
5667 								my->monsterAllyState = ALLY_STATE_DEFEND;
5668 								my->createPathBoundariesNPC(5);
5669 								if ( myStats->type == GYROBOT && my->monsterSpecialState == GYRO_RETURN_PATHING )
5670 								{
5671 									my->monsterSpecialState = GYRO_RETURN_LANDING;
5672 									my->monsterState = MONSTER_STATE_WAIT;
5673 									serverUpdateEntitySkill(my, 33); // for clients to keep track of animation
5674 									playSoundEntity(my, 449, 128);
5675 								}
5676 							}
5677 						}
5678 						else if ( !target && my->monsterAllyGetPlayerLeader() )
5679 						{
5680 							// scan for enemies after reaching move point.
5681 							real_t dist = sightranges[myStats->type];
5682 							for ( node = map.creatures->first; node != nullptr; node = node->next )
5683 							{
5684 								Entity* target = (Entity*)node->element;
5685 								if ( target->behavior == &actMonster && my->checkEnemy(target) )
5686 								{
5687 									real_t oldDist = dist;
5688 									dist = sqrt(pow(my->x - target->x, 2) + pow(my->y - target->y, 2));
5689 									if ( dist < sightranges[myStats->type] && dist <= oldDist )
5690 									{
5691 										double tangent = atan2(target->y - my->y, target->x - my->x);
5692 										lineTrace(my, my->x, my->y, tangent, sightranges[myStats->type], 0, false);
5693 										if ( hit.entity == target )
5694 										{
5695 											my->monsterLookTime = 1;
5696 											my->monsterMoveTime = rand() % 10 + 1;
5697 											my->monsterLookDir = tangent;
5698 											break;
5699 										}
5700 									}
5701 								}
5702 							}
5703 						}
5704 					}
5705 				}
5706 				else
5707 				{
5708 					Entity* target = uidToEntity(my->monsterTarget);
5709 					if ( target )
5710 					{
5711 						double tangent = atan2( target->y - my->y, target->x - my->x );
5712 						my->monsterLookTime = 1;
5713 						my->monsterMoveTime = rand() % 10 + 1;
5714 						my->monsterLookDir = tangent;
5715 						/*if ( myStats->type == SHADOW )
5716 						{
5717 							messagePlayer(0, "[SHADOW] No path #2: Resetting to wait state.");
5718 						}*/
5719 					}
5720 					my->monsterState = MONSTER_STATE_WAIT; // no path, return to wait state
5721 					if ( my->monsterAllyState == ALLY_STATE_MOVETO )
5722 					{
5723 						//messagePlayer(0, "Couldn't reach, retrying.");
5724 						if ( target )
5725 						{
5726 							if ( my->monsterAllySetInteract() )
5727 							{
5728 								if ( myStats->type == GYROBOT )
5729 								{
5730 									my->monsterSpecialState = GYRO_INTERACT_LANDING;
5731 									my->monsterState = MONSTER_STATE_WAIT;
5732 									serverUpdateEntitySkill(my, 33); // for clients to keep track of animation
5733 								}
5734 								else
5735 								{
5736 									// we found our interactable within distance.
5737 									//messagePlayer(0, "Found my interactable.");
5738 									if ( FollowerMenu.entityToInteractWith && FollowerMenu.entityToInteractWith->behavior == &actItem )
5739 									{
5740 										//my->handleNPCInteractDialogue(*myStats, ALLY_EVENT_INTERACT_ITEM);
5741 									}
5742 									else
5743 									{
5744 										my->handleNPCInteractDialogue(*myStats, ALLY_EVENT_INTERACT_OTHER);
5745 									}
5746 									my->monsterAllyInteractTarget = 0;
5747 									my->monsterAllyState = ALLY_STATE_DEFAULT;
5748 								}
5749 							}
5750 							else if ( my->monsterSetPathToLocation(static_cast<int>(target->x / 16), static_cast<int>(target->y / 16), 2) )
5751 							{
5752 								my->monsterState = MONSTER_STATE_HUNT;
5753 								my->monsterAllyState = ALLY_STATE_MOVETO;
5754 								//messagePlayer(0, "Moving to my interactable!.");
5755 								my->handleNPCInteractDialogue(*myStats, ALLY_EVENT_MOVETO_REPATH);
5756 							}
5757 							else
5758 							{
5759 								// no path possible, give up.
5760 								//messagePlayer(0, "I can't get to my target.");
5761 								my->handleNPCInteractDialogue(*myStats, ALLY_EVENT_MOVETO_FAIL);
5762 								my->monsterAllyInteractTarget = 0;
5763 								my->monsterAllyState = ALLY_STATE_DEFAULT;
5764 							}
5765 						}
5766 						else
5767 						{
5768 							//messagePlayer(0, "Issued move command, defending here.");
5769 							// scan for enemies after reaching move point.
5770 							real_t dist = sightranges[myStats->type];
5771 							for ( node = map.creatures->first; node != nullptr; node = node->next )
5772 							{
5773 								Entity* target = (Entity*)node->element;
5774 								if ( target->behavior == &actMonster && my->checkEnemy(target) )
5775 								{
5776 									real_t oldDist = dist;
5777 									dist = sqrt(pow(my->x - target->x, 2) + pow(my->y - target->y, 2));
5778 									if ( dist < sightranges[myStats->type] && dist <= oldDist )
5779 									{
5780 										double tangent = atan2(target->y - my->y, target->x - my->x);
5781 										lineTrace(my, my->x, my->y, tangent, sightranges[myStats->type], 0, false);
5782 										if ( hit.entity == target )
5783 										{
5784 											my->monsterLookTime = 1;
5785 											my->monsterMoveTime = rand() % 10 + 1;
5786 											my->monsterLookDir = tangent;
5787 											break;
5788 										}
5789 									}
5790 								}
5791 							}
5792 							my->monsterAllyState = ALLY_STATE_DEFEND;
5793 							my->createPathBoundariesNPC(5);
5794 						}
5795 					}
5796 				}
5797 			}
5798 			else
5799 			{
5800 				Entity* target = uidToEntity(my->monsterTarget);
5801 				if ( target )
5802 				{
5803 					double tangent = atan2( target->y - my->y, target->x - my->x );
5804 					my->monsterLookTime = 1;
5805 					my->monsterMoveTime = rand() % 10 + 1;
5806 					my->monsterLookDir = tangent;
5807 					/*if ( myStats->type == SHADOW )
5808 					{
5809 						messagePlayer(0, "[SHADOW] No path #3: Resetting to wait state.");
5810 					}*/
5811 				}
5812 				my->monsterState = MONSTER_STATE_WAIT; // no path, return to wait state
5813 				//TODO: Replace with lookAtEntity();
5814 			}
5815 		}
5816 		else if ( my->monsterState == MONSTER_STATE_TALK )     //Begin talk state
5817 		{
5818 			MONSTER_VELX = 0;
5819 			MONSTER_VELY = 0;
5820 
5821 			// turn towards target
5822 			Entity* target = uidToEntity(my->monsterTarget);
5823 			if ( target != NULL )
5824 			{
5825 				dir = my->yaw - atan2( target->y - my->y, target->x - my->x );
5826 				while ( dir >= PI )
5827 				{
5828 					dir -= PI * 2;
5829 				}
5830 				while ( dir < -PI )
5831 				{
5832 					dir += PI * 2;
5833 				}
5834 				my->yaw -= dir / 2;
5835 				while ( my->yaw < 0 )
5836 				{
5837 					my->yaw += 2 * PI;
5838 				}
5839 				while ( my->yaw >= 2 * PI )
5840 				{
5841 					my->yaw -= 2 * PI;
5842 				}
5843 
5844 				// abandon conversation if distance is too great
5845 				if ( sqrt( pow(my->x - target->x, 2) + pow(my->y - target->y, 2) ) > TOUCHRANGE )
5846 				{
5847 					my->monsterState = MONSTER_STATE_WAIT;
5848 					my->monsterTarget = 0;
5849 					int player = -1;
5850 					if ( target->behavior == &actPlayer )
5851 					{
5852 						player = target->skill[2];
5853 					}
5854 					if ( player == 0 )
5855 					{
5856 						closeAllGUIs(CLOSEGUI_ENABLE_SHOOTMODE, CLOSEGUI_CLOSE_ALL);
5857 					}
5858 					else if ( player > 0 )
5859 					{
5860 						// inform client of abandonment
5861 						strcpy((char*)net_packet->data, "SHPC");
5862 						net_packet->address.host = net_clients[player - 1].host;
5863 						net_packet->address.port = net_clients[player - 1].port;
5864 						net_packet->len = 4;
5865 						sendPacketSafe(net_sock, -1, net_packet, player - 1);
5866 					}
5867 					monsterMoveAside(my, target);
5868 				}
5869 			}
5870 			else
5871 			{
5872 				// abandon conversation
5873 				my->monsterState = MONSTER_STATE_WAIT;
5874 				my->monsterTarget = 0;
5875 			}
5876 		} //End talk state
5877 		else if ( my->monsterState == MONSTER_STATE_LICH_DODGE )     // dodge state (herx)
5878 		{
5879 			double dist = 0;
5880 			dist = clipMove(&my->x, &my->y, MONSTER_VELX, MONSTER_VELY, my);
5881 			if ( dist != sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY) )   // hit obstacle
5882 			{
5883 				my->monsterSpecialTimer = 60;
5884 				if ( rand() % 2 )
5885 				{
5886 					my->monsterState = MONSTER_STATE_WAIT; // wait state
5887 				}
5888 				else
5889 				{
5890 					my->monsterState = MONSTER_STATE_LICH_SUMMON; // summoning state
5891 				}
5892 			}
5893 			else
5894 			{
5895 				my->monsterSpecialTimer++;
5896 				if ( my->monsterSpecialTimer > 20 )
5897 				{
5898 					my->monsterSpecialTimer = 60;
5899 					if ( rand() % 2 )
5900 					{
5901 						my->monsterState = MONSTER_STATE_WAIT; // wait state
5902 					}
5903 					else
5904 					{
5905 						my->monsterState = MONSTER_STATE_LICH_SUMMON; // summoning state
5906 					}
5907 				}
5908 			}
5909 		}
5910 		else if ( my->monsterState == MONSTER_STATE_LICH_SUMMON )     // summoning state (herx)
5911 		{
5912 			MONSTER_ATTACK = 1;
5913 			MONSTER_ATTACKTIME = 0;
5914 			if ( my->monsterSpecialTimer )
5915 			{
5916 				my->monsterSpecialTimer--;
5917 			}
5918 			else
5919 			{
5920 				my->monsterSpecialTimer = 60;
5921 				my->monsterState = MONSTER_STATE_WAIT; // wait state
5922 				playSoundEntity(my, 166, 128);
5923 
5924 				Monster creature = NOTHING;
5925 				switch ( rand() % 5 )
5926 				{
5927 					case 0:
5928 					case 1:
5929 						creature = CREATURE_IMP;
5930 						break;
5931 					case 2:
5932 					case 3:
5933 					case 4:
5934 						creature = DEMON;
5935 						break;
5936 				}
5937 				if ( creature != DEMON )
5938 				{
5939 					summonMonster(creature, ((int)(my->x / 16)) * 16 + 8, ((int)(my->y / 16)) * 16 + 8);
5940 				}
5941 				summonMonster(creature, ((int)(my->x / 16)) * 16 + 8, ((int)(my->y / 16)) * 16 + 8);
5942 			}
5943 		}
5944 		else if ( my->monsterState == MONSTER_STATE_LICH_DEATH )     // lich death state
5945 		{
5946 			my->yaw += .5; // rotate
5947 			if ( my->yaw >= PI * 2 )
5948 			{
5949 				my->yaw -= PI * 2;
5950 			}
5951 			MONSTER_ATTACK = 1;
5952 			MONSTER_ATTACKTIME = 0;
5953 			if ( my->monsterSpecialTimer == 0 )
5954 			{
5955 				serverUpdateEntitySkill(my, 8);
5956 				serverUpdateEntitySkill(my, 9);
5957 				int c;
5958 				for ( c = 0; c < MAXPLAYERS; c++ )
5959 				{
5960 					playSoundPlayer(c, 186, 128);
5961 				}
5962 			}
5963 			if ( my->monsterSpecialTimer % 10 == 0 )
5964 			{
5965 				spawnExplosion(my->x - 8 + rand() % 16, my->y - 8 + rand() % 16, -4 + rand() % 8);
5966 			}
5967 			my->monsterSpecialTimer++;
5968 			if ( my->monsterSpecialTimer > 180 )
5969 			{
5970 				lichDie(my);
5971 				return;
5972 			}
5973 		}
5974 		else if ( my->monsterState == MONSTER_STATE_LICHFIRE_DIE
5975 			|| my->monsterState == MONSTER_STATE_LICHICE_DIE )     // lich death state
5976 		{
5977 			if ( my->monsterSpecialTimer < 100 )
5978 			{
5979 				my->yaw += .5; // rotate
5980 				if ( my->yaw >= PI * 2 )
5981 				{
5982 					my->yaw -= PI * 2;
5983 				}
5984 			}
5985 			//messagePlayer(0, "timer: %d", my->monsterSpecialTimer);
5986 			if ( my->monsterSpecialTimer == 180 )
5987 			{
5988 				if ( myStats->type == LICH_FIRE )
5989 				{
5990 					my->monsterAttack = MONSTER_POSE_SPECIAL_WINDUP1;
5991 				}
5992 				else if ( myStats->type == LICH_ICE )
5993 				{
5994 					my->monsterAttack = MONSTER_POSE_SPECIAL_WINDUP2;
5995 				}
5996 				my->monsterAttackTime = 0;
5997 				serverUpdateEntitySkill(my, 8);
5998 				serverUpdateEntitySkill(my, 9);
5999 				for ( int c = 0; c < MAXPLAYERS; c++ )
6000 				{
6001 					if ( myStats->type == LICH_FIRE )
6002 					{
6003 						playSoundPlayer(c, 376, 128);
6004 						messagePlayerColor(c, uint32ColorOrange(*mainsurface), language[2646]);
6005 					}
6006 					else if ( myStats->type == LICH_ICE )
6007 					{
6008 						playSoundPlayer(c, 381, 128);
6009 						messagePlayerColor(c, uint32ColorBaronyBlue(*mainsurface), language[2648]);
6010 					}
6011 				}
6012 			}
6013 			if ( my->monsterSpecialTimer % 15 == 0 )
6014 			{
6015 				spawnExplosion(my->x - 8 + rand() % 16, my->y - 8 + rand() % 16, my->z -4 + rand() % 8);
6016 			}
6017 			--my->monsterSpecialTimer;
6018 			if ( my->monsterSpecialTimer <= 0 )
6019 			{
6020 				if ( myStats->type == LICH_FIRE )
6021 				{
6022 					lichFireDie(my);
6023 					return;
6024 				}
6025 				else if ( myStats->type == LICH_ICE )
6026 				{
6027 					lichIceDie(my);
6028 					return;
6029 				}
6030 			}
6031 		}
6032 		else if ( my->monsterState == MONSTER_STATE_DEVIL_DEATH )     // devil death state
6033 		{
6034 			my->z += .5; // descend slowly
6035 			MONSTER_ATTACK = 4;
6036 			MONSTER_ATTACKTIME = 0;
6037 			/*if( MONSTER_SPECIAL==0 ) {
6038 				int c;
6039 				for( c=0; c<MAXPLAYERS; c++ )
6040 					playSoundPlayer(c,186,128);
6041 			}*/
6042 			if ( my->monsterSpecialTimer == 0 )
6043 			{
6044 				serverUpdateEntitySkill(my, 8);
6045 				serverUpdateEntitySkill(my, 9);
6046 				my->x += cos(my->yaw + PI / 2) * 2;
6047 				my->y += sin(my->yaw + PI / 2) * 2;
6048 			}
6049 			else if ( my->monsterSpecialTimer % 2 == 0 )
6050 			{
6051 				my->x += cos(my->yaw + PI / 2) * 4;
6052 				my->y += sin(my->yaw + PI / 2) * 4;
6053 			}
6054 			else
6055 			{
6056 				my->x -= cos(my->yaw + PI / 2) * 4;
6057 				my->y -= sin(my->yaw + PI / 2) * 4;
6058 			}
6059 			if ( my->monsterSpecialTimer % 10 == 0 )
6060 			{
6061 				spawnExplosion(my->x - 24 + rand() % 48, my->y - 24 + rand() % 48, -16 + rand() % 32);
6062 			}
6063 			my->monsterSpecialTimer++;
6064 			if ( my->z > 96 )
6065 			{
6066 				devilDie(my);
6067 				return;
6068 			}
6069 		}
6070 		else if ( my->monsterState == MONSTER_STATE_DEVIL_TELEPORT )     // devil teleport state
6071 		{
6072 			my->flags[PASSABLE] = true;
6073 			my->yaw += .1; // rotate
6074 			if ( my->yaw >= PI * 2 )
6075 			{
6076 				my->yaw -= PI * 2;
6077 			}
6078 			my->z = std::min<int>(my->z + 1, 64); // descend
6079 			MONSTER_ATTACK = 4;
6080 			MONSTER_ATTACKTIME = 0;
6081 			MONSTER_ARMBENDED = 1;
6082 			if ( my->monsterSpecialTimer == 0 )
6083 			{
6084 				serverUpdateEntitySkill(my, 8);
6085 				serverUpdateEntitySkill(my, 9);
6086 				serverUpdateEntitySkill(my, 10);
6087 			}
6088 			++my->monsterSpecialTimer;
6089 			if ( my->z >= 64 )
6090 			{
6091 				node_t* node;
6092 				int c = 0;
6093 				for ( node = map.entities->first; node != nullptr; node = node->next )
6094 				{
6095 					Entity* entity = (Entity*)node->element;
6096 					if ( entity->behavior == &actDevilTeleport )
6097 					{
6098 						if ( entity->x == my->x && entity->y == my->y )
6099 						{
6100 							continue;
6101 						}
6102 						switch ( entity->sprite )
6103 						{
6104 							case 72:
6105 								if ( devilstate == 74 )
6106 								{
6107 									c++;
6108 								}
6109 								continue;
6110 							case 73:
6111 								if ( devilstate == 0 || devilstate == 72 )
6112 								{
6113 									c++;
6114 								}
6115 								continue;
6116 							case 74:
6117 								if ( devilstate == 73 )
6118 								{
6119 									c++;
6120 								}
6121 								continue;
6122 							default:
6123 								continue;
6124 						}
6125 					}
6126 				}
6127 				if ( c )
6128 				{
6129 					int i = rand() % c;
6130 					c = 0;
6131 					for ( node = map.entities->first; node != nullptr; node = node->next )
6132 					{
6133 						Entity* entity = (Entity*)node->element;
6134 						if ( entity->behavior == &actDevilTeleport )
6135 						{
6136 							if ( entity->x == my->x && entity->y == my->y )
6137 							{
6138 								continue;
6139 							}
6140 							switch ( entity->sprite )
6141 							{
6142 								case 72:
6143 									if ( devilstate == 74 )
6144 									{
6145 										if ( c == i )
6146 										{
6147 											break;
6148 										}
6149 										else
6150 										{
6151 											c++;
6152 											continue;
6153 										}
6154 									}
6155 									continue;
6156 								case 73:
6157 									if ( devilstate == 0 || devilstate == 72 )
6158 									{
6159 										if ( c == i )
6160 										{
6161 											break;
6162 										}
6163 										else
6164 										{
6165 											c++;
6166 											continue;
6167 										}
6168 									}
6169 									continue;
6170 								case 74:
6171 									if ( devilstate == 73 )
6172 									{
6173 										if ( c == i )
6174 										{
6175 											break;
6176 										}
6177 										else
6178 										{
6179 											c++;
6180 											continue;
6181 										}
6182 									}
6183 									continue;
6184 								default:
6185 									continue;
6186 							}
6187 							my->x = entity->x;
6188 							my->y = entity->y;
6189 							devilstate = entity->sprite;
6190 							devilacted = 0;
6191 							break;
6192 						}
6193 					}
6194 				}
6195 				my->monsterSpecialTimer = 30;
6196 				my->monsterState = MONSTER_STATE_DEVIL_RISING;
6197 			}
6198 		}
6199 		else if ( my->monsterState == MONSTER_STATE_DEVIL_RISING )     // devil rising state (post-teleport)
6200 		{
6201 			if ( my->monsterSpecialTimer <= 0 )
6202 			{
6203 				my->z = std::max<int>(my->z - 1, -4); // ascend
6204 			}
6205 			else
6206 			{
6207 				--my->monsterSpecialTimer;
6208 				if ( my->monsterSpecialTimer <= 0 )
6209 				{
6210 					if ( myStats->HP > 0 )
6211 					{
6212 						my->flags[PASSABLE] = false;
6213 					}
6214 					node_t* node;
6215 					for ( node = map.creatures->first; node != nullptr; node = node->next ) //Since it only looks at entities that have stats, only creatures can have stats; don't iterate map.entities.
6216 					{
6217 						Entity* entity = (Entity*)node->element;
6218 						if ( entity == my )
6219 						{
6220 							continue;
6221 						}
6222 						if ( entityInsideEntity(my, entity) )
6223 						{
6224 							Stat* stats = entity->getStats();
6225 							if ( stats )
6226 							{
6227 								if ( stats->HP > 0 )
6228 								{
6229 									stats->HP = 0;
6230 								}
6231 							}
6232 						}
6233 					}
6234 				}
6235 			}
6236 			if ( !devilroar )
6237 			{
6238 				if ( my->z <= -4 )
6239 				{
6240 					int j = rand() % 5;
6241 					int c;
6242 					for ( c = 0; c < MAXPLAYERS; c++ )
6243 					{
6244 						playSoundPlayer(c, 204 + j, 64);
6245 					}
6246 					playSoundEntity(my, 204 + j, 128);
6247 					devilroar = 1;
6248 					MONSTER_ATTACK = 4;
6249 					MONSTER_ATTACKTIME = 0;
6250 					MONSTER_ARMBENDED = 1;
6251 					serverUpdateEntitySkill(my, 8);
6252 					serverUpdateEntitySkill(my, 9);
6253 					serverUpdateEntitySkill(my, 10);
6254 				}
6255 				else
6256 				{
6257 					my->yaw += .1; // rotate
6258 					if ( my->yaw >= PI * 2 )
6259 					{
6260 						my->yaw -= PI * 2;
6261 					}
6262 				}
6263 			}
6264 			else
6265 			{
6266 				node_t* tempNode;
6267 				Entity* playertotrack = nullptr;
6268 				for ( tempNode = map.creatures->first; tempNode != nullptr; tempNode = tempNode->next ) //Only inspects players, so don't iterate map.entities.
6269 				{
6270 					Entity* tempEntity = (Entity*)tempNode->element;
6271 					double lowestdist = 5000;
6272 					if ( tempEntity->behavior == &actPlayer )
6273 					{
6274 						double disttoplayer = entityDist(my, tempEntity);
6275 						if ( disttoplayer < lowestdist )
6276 						{
6277 							playertotrack = tempEntity;
6278 						}
6279 					}
6280 				}
6281 				if ( playertotrack )
6282 				{
6283 					my->monsterTarget = playertotrack->getUID();
6284 					my->monsterTargetX = playertotrack->x;
6285 					my->monsterTargetY = playertotrack->y;
6286 					MONSTER_VELX = my->monsterTargetX - my->x;
6287 					MONSTER_VELY = my->monsterTargetY - my->y;
6288 				}
6289 				else
6290 				{
6291 					MONSTER_VELX = 0;
6292 					MONSTER_VELY = 0;
6293 				}
6294 
6295 				// rotate monster
6296 				dir = my->yaw - atan2( MONSTER_VELY, MONSTER_VELX );
6297 
6298 				// To prevent the Entity's position from being updated by dead reckoning on the CLient, set the velocity to 0 after usage
6299 				MONSTER_VELX = 0.0;
6300 				MONSTER_VELY = 0.0;
6301 
6302 				while ( dir >= PI )
6303 				{
6304 					dir -= PI * 2;
6305 				}
6306 				while ( dir < -PI )
6307 				{
6308 					dir += PI * 2;
6309 				}
6310 				my->yaw -= dir / 2;
6311 				while ( my->yaw < 0 )
6312 				{
6313 					my->yaw += 2 * PI;
6314 				}
6315 				while ( my->yaw >= 2 * PI )
6316 				{
6317 					my->yaw -= 2 * PI;
6318 				}
6319 
6320 				if ( MONSTER_ATTACKTIME > 60 )
6321 				{
6322 					my->monsterState = MONSTER_STATE_ATTACK;
6323 					MONSTER_ATTACK = 0;
6324 					MONSTER_ATTACKTIME = 0;
6325 					MONSTER_ARMBENDED = 0;
6326 					serverUpdateEntitySkill(my, 8);
6327 					serverUpdateEntitySkill(my, 9);
6328 					serverUpdateEntitySkill(my, 10);
6329 					devilroar = 0;
6330 					MONSTER_VELX = 0;
6331 					MONSTER_VELY = 0;
6332 				}
6333 			}
6334 		}
6335 		else if ( my->monsterState == MONSTER_STATE_DEVIL_SUMMON )     // devil summoning state
6336 		{
6337 			MONSTER_ATTACK = 4;
6338 			MONSTER_ATTACKTIME = 0;
6339 			if ( my->monsterSpecialTimer == 0 )
6340 			{
6341 				serverUpdateEntitySkill(my, 8);
6342 				serverUpdateEntitySkill(my, 9);
6343 			}
6344 			++my->monsterSpecialTimer;
6345 			if ( my->monsterSpecialTimer == 20 ) // start the spawn animations
6346 			{
6347 				Monster creature = NOTHING;
6348 				int numToSpawn = 3;
6349 				int numPlayers = 1;
6350 				std::vector<int> alivePlayers;
6351 				for ( int c = 1; c < MAXPLAYERS; ++c )
6352 				{
6353 					if ( !client_disconnected[c] )
6354 					{
6355 						++numToSpawn;
6356 						++numPlayers;
6357 						if ( players[c] && players[c]->entity )
6358 						{
6359 							alivePlayers.push_back(c);
6360 						}
6361 					}
6362 				}
6363 
6364 				int spawnedShadows = 0;
6365 				while ( numToSpawn > 0 )
6366 				{
6367 					if ( devilsummonedtimes % 2 == 1 && my->devilGetNumMonstersInArena(SHADOW) + spawnedShadows < numPlayers )
6368 					{
6369 						// odd numbered spawns.
6370 						// let's make some shadows.
6371 						if ( !alivePlayers.empty() )
6372 						{
6373 							int vectorEntry = rand() % alivePlayers.size();
6374 							my->devilSummonMonster(nullptr, SHADOW, 5, alivePlayers[vectorEntry]);
6375 							alivePlayers.erase(alivePlayers.begin() + vectorEntry);
6376 						}
6377 						else
6378 						{
6379 							my->devilSummonMonster(nullptr, SHADOW, 9);
6380 						}
6381 						++spawnedShadows;
6382 					}
6383 					else
6384 					{
6385 						switch ( rand() % 5 )
6386 						{
6387 							case 0:
6388 							case 1:
6389 								my->devilSummonMonster(nullptr, CREATURE_IMP, 9);
6390 								break;
6391 							case 2:
6392 							case 3:
6393 							case 4:
6394 								my->devilSummonMonster(nullptr, DEMON, 7);
6395 								break;
6396 						}
6397 					}
6398 					--numToSpawn;
6399 				}
6400 				++devilsummonedtimes;
6401 			}
6402 			else if ( my->monsterSpecialTimer > 100 ) // end this state.
6403 			{
6404 				MONSTER_ATTACK = 0;
6405 				MONSTER_ATTACKTIME = 0;
6406 				serverUpdateEntitySkill(my, 8);
6407 				serverUpdateEntitySkill(my, 9);
6408 				my->monsterSpecialTimer = 0;
6409 				my->monsterState = MONSTER_STATE_ATTACK;
6410 				node_t* tempNode;
6411 				Entity* playertotrack = nullptr;
6412 				for ( tempNode = map.creatures->first; tempNode != nullptr; tempNode = tempNode->next ) //Only inspects players, so don't iterate map.entities. Technically, only needs to iterate through the players[] array, eh?
6413 				{
6414 					Entity* tempEntity = (Entity*)tempNode->element;
6415 					double lowestdist = 5000;
6416 					if ( tempEntity->behavior == &actPlayer )
6417 					{
6418 						double disttoplayer = entityDist(my, tempEntity);
6419 						if ( disttoplayer < lowestdist )
6420 						{
6421 							playertotrack = tempEntity;
6422 						}
6423 					}
6424 				}
6425 				if ( playertotrack )
6426 				{
6427 					my->monsterTarget = playertotrack->getUID();
6428 					my->monsterTargetX = playertotrack->x;
6429 					my->monsterTargetY = playertotrack->y;
6430 				}
6431 			}
6432 		}
6433 		else if ( my->monsterState == MONSTER_STATE_DEVIL_BOULDER )     // devil boulder spawn state
6434 		{
6435 			int angle = -1;
6436 			if ( (int)(my->x / 16) == 14 && (int)(my->y / 16) == 32 )
6437 			{
6438 				angle = 0;
6439 			}
6440 			else if ( (int)(my->x / 16) == 32 && (int)(my->y / 16) == 14 )
6441 			{
6442 				angle = 1;
6443 			}
6444 			else if ( (int)(my->x / 16) == 50 && (int)(my->y / 16) == 32 )
6445 			{
6446 				angle = 2;
6447 			}
6448 			else if ( (int)(my->x / 16) == 32 && (int)(my->y / 16) == 50 )
6449 			{
6450 				angle = 3;
6451 			}
6452 			std::unordered_set<int> lavalLocationsXY = { 22,23,24,31,32,33,40,41,42 };
6453 			int numLavaBoulders = 0;
6454 
6455 			my->yaw = angle * PI / 2;
6456 			my->monsterSpecialTimer++;
6457 			if ( my->monsterSpecialTimer == 10 )
6458 			{
6459 				MONSTER_ATTACK = 1;
6460 				MONSTER_ATTACKTIME = 0;
6461 				serverUpdateEntitySkill(my, 8);
6462 				serverUpdateEntitySkill(my, 9);
6463 
6464 				my->castOrbitingMagicMissile(SPELL_BLEED, 32.0, 0.0, 300);
6465 				my->castOrbitingMagicMissile(SPELL_BLEED, 32.0, 2 * PI / 5, 300);
6466 				my->castOrbitingMagicMissile(SPELL_BLEED, 32.0, 4 * PI / 5, 300);
6467 				my->castOrbitingMagicMissile(SPELL_BLEED, 32.0, 6 * PI / 5, 300);
6468 				my->castOrbitingMagicMissile(SPELL_BLEED, 32.0, 8 * PI / 5, 300);
6469 			}
6470 			if ( my->monsterSpecialTimer == 40 )
6471 			{
6472 				int c;
6473 				double oyaw = my->yaw;
6474 				for ( c = 0; c < 12; c++ )
6475 				{
6476 					my->yaw = ((double)c + ((rand() % 100) / 100.f)) * (PI * 2) / 12.f;
6477 					castSpell(my->getUID(), &spell_fireball, true, false);
6478 				}
6479 				my->yaw = oyaw;
6480 				for ( c = 0; c < 7; ++c )
6481 				{
6482 					if ( c == 6 && (angle == 1 || angle == 2) )
6483 					{
6484 						continue;
6485 					}
6486 					Entity* entity = newEntity(245, 1, map.entities, nullptr); // boulder
6487 					entity->parent = my->getUID();
6488 					if ( angle == 0 )
6489 					{
6490 						entity->x = (20 << 4) + 8;
6491 						entity->y = (32 << 4) + 8 + 32 * c;
6492 					}
6493 					else if ( angle == 1 )
6494 					{
6495 						entity->x = (20 << 4) + 8 + 32 * c;
6496 						entity->y = (20 << 4) + 8;
6497 					}
6498 					else if ( angle == 2 )
6499 					{
6500 						entity->x = (44 << 4) + 8;
6501 						entity->y = (20 << 4) + 8 + 32 * c;
6502 					}
6503 					else if ( angle == 3 )
6504 					{
6505 						entity->x = (32 << 4) + 8 + 32 * c;
6506 						entity->y = (44 << 4) + 8;
6507 					}
6508 
6509 					if ( lavalLocationsXY.find(static_cast<int>(entity->x / 16)) != lavalLocationsXY.end()
6510 						|| lavalLocationsXY.find(static_cast<int>(entity->y / 16)) != lavalLocationsXY.end()
6511 						|| myStats->HP < myStats->MAXHP * 0.5 )
6512 					{
6513 						// will roll over lava or Baphy < 50% HP
6514 						int chance = 4;
6515 						if ( myStats->HP < myStats->MAXHP * 0.25 )
6516 						{
6517 							chance = 1;
6518 						}
6519 						else if ( myStats->HP < myStats->MAXHP * 0.5 )
6520 						{
6521 							chance = 2;
6522 						}
6523 						else if ( myStats->HP < myStats->MAXHP * 0.75 )
6524 						{
6525 							chance = 3;
6526 						}
6527 
6528 						if ( rand() % chance == 0 || (numLavaBoulders < 2 && rand() % 2) )
6529 						{
6530 							entity->sprite = 989; // lava boulder.
6531 							++numLavaBoulders;
6532 						}
6533 					}
6534 
6535 					entity->z = -64;
6536 					entity->yaw = angle * (PI / 2.f);
6537 					entity->sizex = 7;
6538 					entity->sizey = 7;
6539 					entity->behavior = &actBoulder;
6540 					entity->flags[UPDATENEEDED] = true;
6541 					entity->flags[PASSABLE] = true;
6542 				}
6543 			}
6544 			if ( my->monsterSpecialTimer == 60 )
6545 			{
6546 				MONSTER_ATTACK = 2;
6547 				MONSTER_ATTACKTIME = 0;
6548 				serverUpdateEntitySkill(my, 8);
6549 				serverUpdateEntitySkill(my, 9);
6550 			}
6551 			if ( my->monsterSpecialTimer == 90 )
6552 			{
6553 				int c;
6554 				double oyaw = my->yaw;
6555 				for ( c = 0; c < 12; ++c )
6556 				{
6557 					my->yaw = ((double)c + ((rand() % 100) / 100.f)) * (PI * 2) / 12.f;
6558 					castSpell(my->getUID(), &spell_fireball, true, false);
6559 				}
6560 				for ( c = 0; c < MAXPLAYERS; ++c )
6561 				{
6562 					my->devilBoulderSummonIfPlayerIsHiding(c);
6563 				}
6564 				my->yaw = oyaw;
6565 
6566 				for ( c = 0; c < 7; ++c )
6567 				{
6568 					if ( c == 6 && (angle == 0 || angle == 3) )
6569 					{
6570 						continue;
6571 					}
6572 					Entity* entity = newEntity(245, 1, map.entities, nullptr); // boulder
6573 					entity->parent = my->getUID();
6574 					if ( angle == 0 )
6575 					{
6576 						entity->x = (20 << 4) + 8;
6577 						entity->y = (20 << 4) + 8 + 32 * c;
6578 					}
6579 					else if ( angle == 1 )
6580 					{
6581 						entity->x = (32 << 4) + 8 + 32 * c;
6582 						entity->y = (20 << 4) + 8;
6583 					}
6584 					else if ( angle == 2 )
6585 					{
6586 						entity->x = (44 << 4) + 8;
6587 						entity->y = (32 << 4) + 8 + 32 * c;
6588 					}
6589 					else if ( angle == 3 )
6590 					{
6591 						entity->x = (20 << 4) + 8 + 32 * c;
6592 						entity->y = (44 << 4) + 8;
6593 					}
6594 
6595 					if ( lavalLocationsXY.find(static_cast<int>(entity->x / 16)) != lavalLocationsXY.end()
6596 						|| lavalLocationsXY.find(static_cast<int>(entity->y / 16)) != lavalLocationsXY.end()
6597 						|| myStats->HP < myStats->MAXHP * 0.5 )
6598 					{
6599 						// will roll over lava or Baphy < 50% HP
6600 						int chance = 4;
6601 						if ( myStats->HP < myStats->MAXHP * 0.25 )
6602 						{
6603 							chance = 1;
6604 						}
6605 						else if ( myStats->HP < myStats->MAXHP * 0.5 )
6606 						{
6607 							chance = 2;
6608 						}
6609 						else if ( myStats->HP < myStats->MAXHP * 0.75 )
6610 						{
6611 							chance = 3;
6612 						}
6613 
6614 						if ( rand() % chance == 0 || (numLavaBoulders < 2 && rand() % 2) )
6615 						{
6616 							entity->sprite = 989; // lava boulder.
6617 							++numLavaBoulders;
6618 						}
6619 					}
6620 
6621 					entity->z = -64;
6622 					entity->yaw = angle * (PI / 2.f);
6623 					entity->sizex = 7;
6624 					entity->sizey = 7;
6625 					entity->behavior = &actBoulder;
6626 					entity->flags[UPDATENEEDED] = true;
6627 					entity->flags[PASSABLE] = true;
6628 				}
6629 			}
6630 			if ( my->monsterSpecialTimer == 180 )
6631 			{
6632 				MONSTER_ATTACK = 3;
6633 				MONSTER_ATTACKTIME = 0;
6634 				serverUpdateEntitySkill(my, 8);
6635 				serverUpdateEntitySkill(my, 9);
6636 			}
6637 			if ( my->monsterSpecialTimer == 210 )
6638 			{
6639 				int c;
6640 				double oyaw = my->yaw;
6641 				for ( c = 0; c < 12; ++c )
6642 				{
6643 					my->yaw = ((double)c + ((rand() % 100) / 100.f)) * (PI * 2) / 12.f;
6644 					castSpell(my->getUID(), &spell_fireball, true, false);
6645 				}
6646 				for ( c = 0; c < MAXPLAYERS; ++c )
6647 				{
6648 					my->devilBoulderSummonIfPlayerIsHiding(c);
6649 				}
6650 				my->yaw = oyaw;
6651 				for ( c = 0; c < 12; ++c )
6652 				{
6653 					Entity* entity = newEntity(245, 1, map.entities, nullptr); // boulder
6654 					entity->parent = my->getUID();
6655 					if ( angle == 0 )
6656 					{
6657 						entity->x = (20 << 4) + 8;
6658 						entity->y = (21 << 4) + 8 + 32 * c;
6659 					}
6660 					else if ( angle == 1 )
6661 					{
6662 						entity->x = (21 << 4) + 8 + 32 * c;
6663 						entity->y = (20 << 4) + 8;
6664 					}
6665 					else if ( angle == 2 )
6666 					{
6667 						entity->x = (44 << 4) + 8;
6668 						entity->y = (21 << 4) + 8 + 32 * c;
6669 					}
6670 					else if ( angle == 3 )
6671 					{
6672 						entity->x = (21 << 4) + 8 + 32 * c;
6673 						entity->y = (44 << 4) + 8;
6674 					}
6675 
6676 					if ( lavalLocationsXY.find(static_cast<int>(entity->x / 16)) != lavalLocationsXY.end()
6677 						|| lavalLocationsXY.find(static_cast<int>(entity->y / 16)) != lavalLocationsXY.end()
6678 						|| myStats->HP < myStats->MAXHP * 0.5 )
6679 					{
6680 						// will roll over lava or Baphy < 50% HP
6681 						int chance = 4;
6682 						if ( myStats->HP < myStats->MAXHP * 0.25 )
6683 						{
6684 							chance = 1;
6685 						}
6686 						else if ( myStats->HP < myStats->MAXHP * 0.5 )
6687 						{
6688 							chance = 2;
6689 						}
6690 						else if ( myStats->HP < myStats->MAXHP * 0.75 )
6691 						{
6692 							chance = 3;
6693 						}
6694 
6695 						if ( rand() % chance == 0 || (numLavaBoulders < 3 && rand() % 2) )
6696 						{
6697 							entity->sprite = 989; // lava boulder.
6698 							++numLavaBoulders;
6699 						}
6700 					}
6701 
6702 					entity->z = -64;
6703 					entity->yaw = angle * (PI / 2.f);
6704 					entity->sizex = 7;
6705 					entity->sizey = 7;
6706 					entity->behavior = &actBoulder;
6707 					entity->flags[UPDATENEEDED] = true;
6708 					entity->flags[PASSABLE] = true;
6709 				}
6710 			}
6711 			if ( my->monsterSpecialTimer == 300 )   // 300 blaze it I guess
6712 			{
6713 				MONSTER_ATTACK = 0;
6714 				MONSTER_ATTACKTIME = 0;
6715 				serverUpdateEntitySkill(my, 8);
6716 				serverUpdateEntitySkill(my, 9);
6717 				my->monsterSpecialTimer = 0;
6718 				my->monsterState = MONSTER_STATE_ATTACK;
6719 				node_t* tempNode;
6720 				Entity* playertotrack = nullptr;
6721 				for ( tempNode = map.creatures->first; tempNode != nullptr; tempNode = tempNode->next ) //Iterate map.creatures, since only inspecting players, not all entities. Technically should just iterate over players[]?
6722 				{
6723 					Entity* tempEntity = (Entity*)tempNode->element;
6724 					double lowestdist = 5000;
6725 					if ( tempEntity->behavior == &actPlayer )
6726 					{
6727 						double disttoplayer = entityDist(my, tempEntity);
6728 						if ( disttoplayer < lowestdist )
6729 						{
6730 							playertotrack = tempEntity;
6731 						}
6732 					}
6733 				}
6734 				if ( playertotrack )
6735 				{
6736 					my->monsterTarget = playertotrack->getUID();
6737 					my->monsterTargetX = playertotrack->x;
6738 					my->monsterTargetY = playertotrack->y;
6739 				}
6740 			}
6741 		}
6742 		else if ( my->monsterState == MONSTER_STATE_LICHFIRE_DODGE
6743 			|| my->monsterState == MONSTER_STATE_LICHICE_DODGE )
6744 		{
6745 			dist = clipMove(&my->x, &my->y, MONSTER_VELX, MONSTER_VELY, my);
6746 			Entity* target = uidToEntity(my->monsterTarget);
6747 			if ( dist != sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY) )
6748 			{
6749 				my->monsterSpecialTimer = 0; // hit obstacle
6750 			}
6751 			if ( target && my->monsterSpecialTimer != 0 && myStats->type == LICH_FIRE )
6752 			{
6753 				dist = sqrt(pow(my->x - target->x, 2) + pow(my->y - target->y, 2));
6754 				if ( dist < STRIKERANGE && rand () % 20 == 0 )
6755 				{
6756 					my->monsterSpecialTimer = 0; // close enough to target, chance to stop early
6757 				}
6758 			}
6759 			if ( my->monsterSpecialTimer == 0 )
6760 			{
6761 				my->monsterState = MONSTER_STATE_WAIT;
6762 				MONSTER_VELX = 0;
6763 				MONSTER_VELY = 0;
6764 				if ( target )
6765 				{
6766 					my->monsterAcquireAttackTarget(*target, MONSTER_STATE_PATH);
6767 					if ( myStats->type == LICH_FIRE )
6768 					{
6769 						my->monsterHitTime = HITRATE * 2;
6770 						if ( sqrt(pow(my->x - target->x, 2) + pow(my->y - target->y, 2)) < STRIKERANGE )
6771 						{
6772 							if ( rand() % 2 == 0 )
6773 							{
6774 								my->monsterLichFireMeleeSeq = LICH_ATK_RISING_RAIN;
6775 								my->handleMonsterAttack(myStats, target, 0.f);
6776 							}
6777 							else
6778 							{
6779 								my->monsterHitTime = 25;
6780 							}
6781 						}
6782 					}
6783 					else if ( myStats->type == LICH_ICE )
6784 					{
6785 						my->monsterHitTime = HITRATE * 2;
6786 						if ( sqrt(pow(my->x - target->x, 2) + pow(my->y - target->y, 2)) < STRIKERANGE * 2 )
6787 						{
6788 							my->monsterLichIceCastSeq = LICH_ATK_CHARGE_AOE;
6789 							my->handleMonsterAttack(myStats, target, 0.f);
6790 						}
6791 					}
6792 				}
6793 			}
6794 		}
6795 		else if ( my->monsterState == MONSTER_STATE_LICH_CASTSPELLS )
6796 		{
6797 			++my->monsterHitTime;
6798 			if ( myStats->type == LICH_FIRE )
6799 			{
6800 				if ( my->monsterLichFireMeleeSeq == 0 )
6801 				{
6802 					if ( my->monsterHitTime >= 60 || my->monsterLichAllyStatus == LICH_ALLY_DEAD && my->monsterHitTime >= 45 )
6803 					{
6804 						Entity* target = uidToEntity(my->monsterTarget);
6805 						if ( target )
6806 						{
6807 							tangent = atan2(target->y - my->y, target->x - my->x);
6808 							lineTrace(my, my->x, my->y, tangent, sightranges[myStats->type], 0, false);
6809 							/*if ( hit.entity )
6810 							{
6811 								messagePlayer(0, "sprite: %d", hit.entity->sprite);
6812 							}*/
6813 							if ( hit.entity == target && rand() % 5 > 0 )
6814 							{
6815 								switch ( rand() % 3 )
6816 								{
6817 									case 0:
6818 										my->monsterLichFireMeleeSeq = LICH_ATK_BASICSPELL_SINGLE;
6819 										my->handleMonsterAttack(myStats, target, entityDist(my, target));
6820 										my->monsterLichFireMeleeSeq = 0;
6821 										break;
6822 									case 1:
6823 										my->monsterLichFireMeleeSeq = LICH_ATK_RISING_SINGLE;
6824 										break;
6825 									case 2:
6826 										my->monsterLichFireMeleeSeq = LICH_ATK_HORIZONTAL_SINGLE;
6827 										break;
6828 									default:
6829 										break;
6830 								}
6831 								my->monsterLichMagicCastCount = 0;
6832 								//my->handleMonsterAttack(myStats, target, entityDist(my, target));
6833 							}
6834 							else
6835 							{
6836 								my->monsterLichFireMeleeSeq = LICH_ATK_RISING_RAIN;
6837 								my->handleMonsterAttack(myStats, target, entityDist(my, target));
6838 								my->monsterLichFireMeleeSeq = 0;
6839 							}
6840 							my->monsterHitTime = 0;
6841 						}
6842 						else
6843 						{
6844 							real_t distToPlayer = 0.f;
6845 							int playerToChase = -1;
6846 							for ( c = 0; c < MAXPLAYERS; c++ )
6847 							{
6848 								if ( players[c] && players[c]->entity )
6849 								{
6850 									if ( !distToPlayer )
6851 									{
6852 										distToPlayer = sqrt(pow(my->x - players[c]->entity->x, 2) + pow(my->y - players[c]->entity->y, 2));
6853 										playerToChase = c;
6854 									}
6855 									else
6856 									{
6857 										double newDistToPlayer = sqrt(pow(my->x - players[c]->entity->x, 2) + pow(my->y - players[c]->entity->y, 2));
6858 										if ( newDistToPlayer < distToPlayer )
6859 										{
6860 											distToPlayer = newDistToPlayer;
6861 											playerToChase = c;
6862 										}
6863 									}
6864 								}
6865 							}
6866 							if ( playerToChase >= 0 && players[playerToChase] && players[playerToChase]->entity )
6867 							{
6868 								my->monsterAcquireAttackTarget(*players[playerToChase]->entity, MONSTER_STATE_PATH);
6869 							}
6870 						}
6871 					}
6872 				}
6873 
6874 				if ( my->monsterLichFireMeleeSeq != 0
6875 					&& my->monsterHitTime >= 0
6876 					&& my->monsterHitTime % 10 == 0 )
6877 				{
6878 					if ( my->monsterLichFireMeleeSeq == LICH_ATK_RISING_SINGLE )
6879 					{
6880 						if ( my->monsterLichMagicCastCount < 3 + rand() % 2 )
6881 						{
6882 							if ( my->monsterLichMagicCastCount == 0 )
6883 							{
6884 								my->attack(MONSTER_POSE_MELEE_WINDUP3, 0, nullptr);
6885 							}
6886 							else
6887 							{
6888 								Entity* spell = castSpell(my->getUID(), getSpellFromID(SPELL_FIREBALL), true, false);
6889 								spell->yaw += (PI / 64) * (-1 + rand() % 3);
6890 								spell->vel_x = cos(spell->yaw) * 4;
6891 								spell->vel_y = sin(spell->yaw) * 4;
6892 							}
6893 							++my->monsterLichMagicCastCount;
6894 						}
6895 						else
6896 						{
6897 							my->monsterLichFireMeleeSeq = 0;
6898 							my->monsterHitTime = 0;
6899 						}
6900 					}
6901 					else if ( my->monsterLichFireMeleeSeq == LICH_ATK_HORIZONTAL_SINGLE )
6902 					{
6903 						if ( my->monsterLichMagicCastCount < 2 )
6904 						{
6905 							if ( my->monsterLichMagicCastCount == 0 )
6906 							{
6907 								my->attack(MONSTER_POSE_MELEE_WINDUP2, 0, nullptr);
6908 							}
6909 							else
6910 							{
6911 								Entity* spell = castSpell(my->getUID(), getSpellFromID(SPELL_FIREBALL), true, false);
6912 								spell->yaw += PI / 16;
6913 								spell->vel_x = cos(spell->yaw) * 4;
6914 								spell->vel_y = sin(spell->yaw) * 4;
6915 								spell = castSpell(my->getUID(), getSpellFromID(SPELL_FIREBALL), true, false);
6916 								spell->yaw -= PI / 16;
6917 								spell->vel_x = cos(spell->yaw) * 4;
6918 								spell->vel_y = sin(spell->yaw) * 4;
6919 								spell = castSpell(my->getUID(), getSpellFromID(SPELL_FIREBALL), true, false);
6920 							}
6921 							++my->monsterLichMagicCastCount;
6922 						}
6923 						else
6924 						{
6925 							my->monsterLichFireMeleeSeq = 0;
6926 							my->monsterHitTime = 0;
6927 						}
6928 					}
6929 				}
6930 			}
6931 			else if ( myStats->type == LICH_ICE )
6932 			{
6933 				if ( my->monsterLichIceCastSeq == 0 )
6934 				{
6935 					if ( my->monsterHitTime >= 60 || (my->monsterLichAllyStatus == LICH_ALLY_DEAD && my->monsterHitTime >= 45) )
6936 					{
6937 						Entity* target = uidToEntity(my->monsterTarget);
6938 						if ( target )
6939 						{
6940 							tangent = atan2(target->y - my->y, target->x - my->x);
6941 							lineTrace(my, my->x, my->y, tangent, sightranges[myStats->type], 0, false);
6942 							switch ( rand() % 4 )
6943 							{
6944 								case 0:
6945 								case 1:
6946 									my->monsterLichIceCastSeq = LICH_ATK_HORIZONTAL_SINGLE;
6947 									break;
6948 								case 2:
6949 								case 3:
6950 									if ( my->monsterLichAllyStatus == LICH_ALLY_DEAD )
6951 									{
6952 										Entity* dummyEntity = nullptr;
6953 										if ( numMonsterTypeAliveOnMap(AUTOMATON, dummyEntity) <= 1 )
6954 										{
6955 											my->monsterLichIceCastSeq = LICH_ATK_SUMMON;
6956 										}
6957 										else
6958 										{
6959 											my->monsterLichIceCastSeq = LICH_ATK_RISING_SINGLE;
6960 										}
6961 									}
6962 									else
6963 									{
6964 										my->monsterLichIceCastSeq = LICH_ATK_RISING_SINGLE;
6965 									}
6966 									break;
6967 								default:
6968 									break;
6969 							}
6970 							my->monsterHitTime = 0;
6971 							my->monsterLichMagicCastCount = 0;
6972 						}
6973 						else
6974 						{
6975 							real_t distToPlayer = 0.f;
6976 							int playerToChase = -1;
6977 							for ( c = 0; c < MAXPLAYERS; c++ )
6978 							{
6979 								if ( players[c] && players[c]->entity )
6980 								{
6981 									if ( !distToPlayer )
6982 									{
6983 										distToPlayer = sqrt(pow(my->x - players[c]->entity->x, 2) + pow(my->y - players[c]->entity->y, 2));
6984 										playerToChase = c;
6985 									}
6986 									else
6987 									{
6988 										double newDistToPlayer = sqrt(pow(my->x - players[c]->entity->x, 2) + pow(my->y - players[c]->entity->y, 2));
6989 										if ( newDistToPlayer < distToPlayer )
6990 										{
6991 											distToPlayer = newDistToPlayer;
6992 											playerToChase = c;
6993 										}
6994 									}
6995 								}
6996 							}
6997 							if ( playerToChase >= 0 && players[playerToChase] && players[playerToChase]->entity )
6998 							{
6999 								my->monsterAcquireAttackTarget(*players[playerToChase]->entity, MONSTER_STATE_PATH);
7000 							}
7001 						}
7002 					}
7003 				}
7004 
7005 				if ( my->monsterLichIceCastSeq != 0
7006 					&& my->monsterHitTime >= 0
7007 					&& my->monsterHitTime % 10 == 0 )
7008 				{
7009 					if ( my->monsterLichIceCastSeq == LICH_ATK_RISING_SINGLE
7010 						|| my->monsterLichIceCastSeq == LICH_ATK_HORIZONTAL_SINGLE
7011 						|| my->monsterLichIceCastSeq == LICH_ATK_SUMMON )
7012 					{
7013 						int castLimit = 6;
7014 						if ( my->monsterLichIceCastSeq == LICH_ATK_SUMMON )
7015 						{
7016 							castLimit = 2 + rand() % 2;
7017 						}
7018 						if ( my->monsterLichMagicCastCount < castLimit )
7019 						{
7020 							if ( my->monsterLichMagicCastCount == 0 )
7021 							{
7022 								my->attack(my->getAttackPose(), 0, nullptr);
7023 							}
7024 							else
7025 							{
7026 								if ( my->monsterLichIceCastSeq == LICH_ATK_SUMMON )
7027 								{
7028 									my->lichIceSummonMonster(AUTOMATON);
7029 								}
7030 								else
7031 								{
7032 									Entity* spell = castSpell(my->getUID(), getSpellFromID(SPELL_MAGICMISSILE), true, false);
7033 									real_t horizontalSpeed = 4.0;
7034 									Entity* target = uidToEntity(my->monsterTarget);
7035 									if ( target )
7036 									{
7037 										real_t spellDistance = sqrt(pow(spell->x - target->x, 2) + pow(spell->y - target->y, 2));
7038 										spell->vel_z = 22.0 / (spellDistance / horizontalSpeed);
7039 										if ( rand() % 3 >= 1 )
7040 										{
7041 											// spells will track the velocity of the target.
7042 											real_t ticksToHit = (spellDistance / horizontalSpeed);
7043 											real_t predictx = target->x + (target->vel_x * ticksToHit);
7044 											real_t predicty = target->y + (target->vel_y * ticksToHit);
7045 											tangent = atan2(predicty - spell->y, predictx - spell->x); // assume target will be here when spell lands.
7046 											//messagePlayer(0, "x: %f->%f, y: %f->%f, angle offset: %f", target->x, predictx, target->y, predicty, spell->yaw - tangent);
7047 											spell->yaw = tangent;
7048 										}
7049 										else
7050 										{
7051 											// do some minor variations in spell angle
7052 											spell->yaw += ((PI * (-4 + rand() % 9)) / 100);
7053 										}
7054 									}
7055 									else
7056 									{
7057 										spell->vel_z = 1.6;
7058 										// do some minor variations in spell angle
7059 										spell->yaw += ((PI * (-4 + rand() % 9)) / 100);
7060 									}
7061 									spell->vel_x = horizontalSpeed * cos(spell->yaw);
7062 									spell->vel_y = horizontalSpeed * sin(spell->yaw);
7063 									spell->actmagicIsVertical = MAGIC_ISVERTICAL_XYZ;
7064 									spell->z = -22.0;
7065 									spell->pitch = atan2(spell->vel_z, horizontalSpeed);
7066 								}
7067 							}
7068 							++my->monsterLichMagicCastCount;
7069 						}
7070 						else
7071 						{
7072 							if ( my->monsterLichIceCastSeq == LICH_ATK_SUMMON )
7073 							{
7074 								my->monsterHitTime = 45;
7075 							}
7076 							else
7077 							{
7078 								my->monsterHitTime = 0;
7079 							}
7080 							my->monsterLichIceCastSeq = 0;
7081 						}
7082 					}
7083 				}
7084 			}
7085 		}
7086 		else if ( my->monsterState == MONSTER_STATE_LICHFIRE_TELEPORT_STATIONARY
7087 			|| my->monsterState == MONSTER_STATE_LICHICE_TELEPORT_STATIONARY )
7088 		{
7089 			MONSTER_VELX = 0;
7090 			MONSTER_VELY = 0;
7091 		}
7092 		//End state machine.
7093 
7094 	}
7095 	else
7096 	{
7097 		if ( myStats->EFFECTS[EFF_KNOCKBACK] )
7098 		{
7099 			my->monsterHandleKnockbackVelocity(my->monsterKnockbackTangentDir, weightratio);
7100 			if ( abs(MONSTER_VELX) > 0.01 || abs(MONSTER_VELY) > 0.01 )
7101 			{
7102 				dist2 = clipMove(&my->x, &my->y, MONSTER_VELX, MONSTER_VELY, my);
7103 				my->handleKnockbackDamage(*myStats, hit.entity);
7104 			}
7105 		}
7106 		else
7107 		{
7108 			MONSTER_VELX = 0;
7109 			MONSTER_VELY = 0;
7110 		}
7111 	}
7112 
7113 	if ( previousMonsterState != my->monsterState )
7114 	{
7115 		serverUpdateEntitySkill(my, 0);
7116 		if ( my->monsterAllyIndex > 0 && my->monsterAllyIndex < MAXPLAYERS )
7117 		{
7118 			serverUpdateEntitySkill(my, 1); // update monsterTarget for player leaders.
7119 		}
7120 	}
7121 
7122 	// move body parts
7123 	myStats = my->getStats();
7124 	if ( myStats != NULL )
7125 	{
7126 		if ( myStats->type == HUMAN )
7127 		{
7128 			humanMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7129 		}
7130 		else if ( myStats->type == RAT )
7131 		{
7132 			ratAnimate(my, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7133 		}
7134 		else if ( myStats->type == GOBLIN )
7135 		{
7136 			goblinMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7137 		}
7138 		else if ( myStats->type == SLIME )
7139 		{
7140 			slimeAnimate(my, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7141 		}
7142 		else if ( myStats->type == SCORPION )
7143 		{
7144 			scorpionAnimate(my, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7145 		}
7146 		else if ( myStats->type == SUCCUBUS )
7147 		{
7148 			succubusMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7149 		}
7150 		else if ( myStats->type == TROLL )
7151 		{
7152 			trollMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7153 		}
7154 		else if ( myStats->type == SHOPKEEPER )
7155 		{
7156 			shopkeeperMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7157 		}
7158 		else if ( myStats->type == SKELETON )
7159 		{
7160 			skeletonMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7161 		}
7162 		else if ( myStats->type == MINOTAUR )
7163 		{
7164 			minotaurMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7165 			actMinotaurCeilingBuster(my);
7166 		}
7167 		else if ( myStats->type == GHOUL )
7168 		{
7169 			ghoulMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7170 		}
7171 		else if ( myStats->type == DEMON )
7172 		{
7173 			demonMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7174 			actDemonCeilingBuster(my);
7175 		}
7176 		else if ( myStats->type == SPIDER )
7177 		{
7178 			spiderMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7179 		}
7180 		else if ( myStats->type == LICH )
7181 		{
7182 			lichAnimate(my, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7183 		}
7184 		else if ( myStats->type == CREATURE_IMP )
7185 		{
7186 			impMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7187 		}
7188 		else if ( myStats->type == GNOME )
7189 		{
7190 			gnomeMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7191 		}
7192 		else if ( myStats->type == DEVIL )
7193 		{
7194 			devilMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7195 		}
7196 		else if ( myStats->type == COCKATRICE )
7197 		{
7198 			cockatriceMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7199 		}
7200 		else if ( myStats->type == AUTOMATON )
7201 		{
7202 			automatonMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7203 		}
7204 		else if ( myStats->type == CRYSTALGOLEM )
7205 		{
7206 			crystalgolemMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7207 		}
7208 		else if ( myStats->type == SCARAB )
7209 		{
7210 			scarabAnimate(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7211 		}
7212 		else if ( myStats->type == KOBOLD )
7213 		{
7214 			koboldMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7215 		}
7216 		else if ( myStats->type == SHADOW )
7217 		{
7218 			shadowMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7219 		}
7220 		else if ( myStats->type == GOATMAN )
7221 		{
7222 			goatmanMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7223 		}
7224 		else if ( myStats->type == INSECTOID )
7225 		{
7226 			insectoidMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7227 		}
7228 		else if ( myStats->type == INCUBUS )
7229 		{
7230 			incubusMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7231 		}
7232 		else if ( myStats->type == VAMPIRE )
7233 		{
7234 			vampireMoveBodyparts(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7235 		}
7236 		else if ( myStats->type == LICH_FIRE )
7237 		{
7238 			lichFireAnimate(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7239 		}
7240 		else if ( myStats->type == LICH_ICE )
7241 		{
7242 			lichIceAnimate(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7243 		}
7244 		else if ( myStats->type == SENTRYBOT || myStats->type == SPELLBOT )
7245 		{
7246 			sentryBotAnimate(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7247 		}
7248 		else if ( myStats->type == GYROBOT )
7249 		{
7250 			gyroBotAnimate(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7251 		}
7252 		else if ( myStats->type == DUMMYBOT )
7253 		{
7254 			dummyBotAnimate(my, myStats, sqrt(MONSTER_VELX * MONSTER_VELX + MONSTER_VELY * MONSTER_VELY));
7255 		}
7256 	}
7257 }
7258 
handleMonsterAttack(Stat * myStats,Entity * target,double dist)7259 void Entity::handleMonsterAttack(Stat* myStats, Entity* target, double dist)
7260 {
7261 	node_t* node = nullptr;
7262 	Entity* entity = nullptr;
7263 	Stat* hitstats = nullptr;
7264 	int charge = 1;
7265 
7266 	//TODO: I don't like this function getting called every frame. Find a better place to put it.
7267 	chooseWeapon(target, dist);
7268 	bool hasrangedweapon = this->hasRangedWeapon();
7269 	bool lichRangeCheckOverride = false;
7270 	if ( myStats->type == LICH_FIRE)
7271 	{
7272 		if ( monsterLichFireMeleeSeq == LICH_ATK_BASICSPELL_SINGLE )
7273 		{
7274 			hasrangedweapon = true;
7275 		}
7276 		if ( monsterState == MONSTER_STATE_LICH_CASTSPELLS )
7277 		{
7278 			lichRangeCheckOverride = true;
7279 		}
7280 	}
7281 	else if ( myStats->type == LICH_ICE )
7282 	{
7283 		// lichice todo
7284 		if ( monsterLichIceCastSeq == LICH_ATK_BASICSPELL_SINGLE )
7285 		{
7286 			hasrangedweapon = true;
7287 		}
7288 		if ( monsterStrafeDirection == 0 && rand() % 10 == 0 && ticks % 10 == 0 )
7289 		{
7290 			monsterStrafeDirection = -1 + ((rand() % 2 == 0) ? 2 : 0);
7291 		}
7292 	}
7293 	else if ( myStats->type == DUMMYBOT )
7294 	{
7295 		return;
7296 	}
7297 	else
7298 	{
7299 		if ( myStats->MISC_FLAGS[STAT_FLAG_MONSTER_CAST_INVENTORY_SPELLBOOKS] > 0 )
7300 		{
7301 			if ( monsterSpecialTimer == 0 && monsterSpecialState == 0 && (this->monsterHitTime >= HITRATE / 2) )
7302 			{
7303 				if ( rand() % 50 == 0 )
7304 				{
7305 					node_t* node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), SPELLBOOK);
7306 					if ( node != nullptr )
7307 					{
7308 						bool swapped = swapMonsterWeaponWithInventoryItem(this, myStats, node, true, true);
7309 						if ( swapped )
7310 						{
7311 							monsterSpecialState = MONSTER_SPELLCAST_GENERIC;
7312 							int timer = (myStats->MISC_FLAGS[STAT_FLAG_MONSTER_CAST_INVENTORY_SPELLBOOKS] >> 4) & 0xFFFF;
7313 							monsterSpecialTimer = timer > 0 ? timer : 250;
7314 							hasrangedweapon = true;
7315 						}
7316 					}
7317 				}
7318 			}
7319 		}
7320 	}
7321 
7322 	// check the range to the target, depending on ranged weapon or melee.
7323 	if ( (dist < STRIKERANGE && !hasrangedweapon) || (hasrangedweapon && dist < getMonsterEffectiveDistanceOfRangedWeapon(myStats->weapon)) || lichRangeCheckOverride )
7324 	{
7325 		// increment the hit time, don't attack until this reaches the hitrate of the weapon
7326 		this->monsterHitTime++;
7327 		real_t bow = 1;
7328 		if ( hasrangedweapon && myStats->weapon )
7329 		{
7330 			if ( (myStats->weapon->type == SLING
7331 					|| myStats->weapon->type == SHORTBOW
7332 					|| myStats->weapon->type == ARTIFACT_BOW
7333 					|| myStats->weapon->type == LONGBOW
7334 					|| myStats->weapon->type == COMPOUND_BOW) )
7335 			{
7336 				bow = 2;
7337 				if ( myStats->weapon->type == COMPOUND_BOW )
7338 				{
7339 					bow = 1.5;
7340 				}
7341 				if ( myStats->shield && itemTypeIsQuiver(myStats->shield->type) )
7342 				{
7343 					if ( myStats->shield->type == QUIVER_LIGHTWEIGHT )
7344 					{
7345 						bow -= 0.5;
7346 					}
7347 				}
7348 			}
7349 			else if ( myStats->weapon->type == CROSSBOW )
7350 			{
7351 				if ( myStats->shield && itemTypeIsQuiver(myStats->shield->type) )
7352 				{
7353 					if ( myStats->shield->type == QUIVER_LIGHTWEIGHT )
7354 					{
7355 						bow = 0.8;
7356 					}
7357 				}
7358 			}
7359 		}
7360 		if ( monsterIsImmobileTurret(this, myStats) )
7361 		{
7362 			bow = 2;
7363 			if ( myStats->type == SPELLBOT )
7364 			{
7365 				if ( myStats->LVL >= 15 )
7366 				{
7367 					bow = 1.2;
7368 				}
7369 				else if ( myStats->LVL >= 10 )
7370 				{
7371 					bow = 1.5;
7372 				}
7373 				else if ( myStats->LVL >= 5 )
7374 				{
7375 					bow = 1.8;
7376 				}
7377 				else
7378 				{
7379 					bow = 2;
7380 				}
7381 			}
7382 		}
7383 		// check if ready to attack
7384 		if ( (this->monsterHitTime >= static_cast<int>(HITRATE * monsterGlobalAttackTimeMultiplier * bow)
7385 				&& (myStats->type != LICH && myStats->type != LICH_ICE))
7386 			|| (this->monsterHitTime >= 5 && myStats->type == LICH)
7387 			|| (this->monsterHitTime >= HITRATE * 2 && myStats->type == LICH_ICE)
7388 			)
7389 		{
7390 			bool shouldAttack = this->handleMonsterSpecialAttack(myStats, nullptr, dist);
7391 			if ( !shouldAttack )
7392 			{
7393 				// handleMonsterSpecialAttack processed an action where the monster should not try to attack this frame.
7394 				// e.g unequipping/swapping from special weapon, stops punching the air after casting a spell.
7395 				return;
7396 			}
7397 
7398 			if ( myStats->type == LICH )
7399 			{
7400 				this->monsterSpecialTimer++;
7401 				if ( this->monsterSpecialTimer >= 5 )
7402 				{
7403 					this->monsterSpecialTimer = 90;
7404 					this->monsterTarget = 0;
7405 					this->monsterTargetX = this->x - 50 + rand() % 100;
7406 					this->monsterTargetY = this->y - 50 + rand() % 100;
7407 					this->monsterState = MONSTER_STATE_PATH; // path state
7408 				}
7409 			}
7410 
7411 			// reset the hit timer
7412 			this->monsterHitTime = 0;
7413 			int tracedist = 0;
7414 			if ( lichRangeCheckOverride )
7415 			{
7416 				tracedist = 1024;
7417 			}
7418 			else if ( hasrangedweapon )
7419 			{
7420 				tracedist = 160;
7421 				if ( myStats->weapon )
7422 				{
7423 					tracedist = getMonsterEffectiveDistanceOfRangedWeapon(myStats->weapon);
7424 				}
7425 			}
7426 			else
7427 			{
7428 				tracedist = STRIKERANGE;
7429 			}
7430 
7431 			// check again for the target in attack range. return the result into hit.entity.
7432 			double newTangent = atan2(target->y - this->y, target->x - this->x);
7433 			if ( lichRangeCheckOverride )
7434 			{
7435 				hit.entity = uidToEntity(monsterTarget);
7436 			}
7437 			else
7438 			{
7439 				lineTrace(this, this->x, this->y, newTangent, tracedist, 0, false);
7440 			}
7441 			if ( hit.entity != nullptr )
7442 			{
7443 				// found the target in range
7444 				hitstats = hit.entity->getStats();
7445 				if ( hit.entity->behavior == &actMonster && !hasrangedweapon )
7446 				{
7447 					// alert the monster!
7448 					if ( hit.entity->skill[0] != MONSTER_STATE_ATTACK )
7449 					{
7450 						hit.entity->monsterAcquireAttackTarget(*this, MONSTER_STATE_PATH);
7451 					}
7452 				}
7453 				if ( hitstats != nullptr )
7454 				{
7455 					// prepare attack, set the animation of the attack based on the current weapon.
7456 					int pose = this->getAttackPose();
7457 
7458 					int oldDefend = monsterDefend;
7459 					monsterDefend = shouldMonsterDefend(*myStats, *hit.entity, *hitstats, dist, hasrangedweapon);
7460 					if ( oldDefend != monsterDefend )
7461 					{
7462 						serverUpdateEntitySkill(this, 47);
7463 					}
7464 
7465 					// turn to the target, then reset my yaw.
7466 					double oYaw = this->yaw;
7467 					this->yaw = newTangent;
7468 					if ( myStats->type == LICH_FIRE )
7469 					{
7470 						if ( monsterState != MONSTER_STATE_LICH_CASTSPELLS )
7471 						{
7472 							lichFireSetNextAttack(*myStats);
7473 							//messagePlayer(0, "previous %d, next is %d", monsterLichFireMeleePrev, monsterLichFireMeleeSeq);
7474 						}
7475 					}
7476 					else if ( myStats->type == LICH_ICE )
7477 					{
7478 						lichIceSetNextAttack(*myStats);
7479 						//messagePlayer(0, "previous %d, next is %d", monsterLichIceCastPrev, monsterLichIceCastSeq);
7480 						if ( monsterLichIceCastPrev == LICH_ATK_BASICSPELL_SINGLE )
7481 						{
7482 							monsterHitTime = HITRATE;
7483 						}
7484 						if ( monsterSpecialState == LICH_ICE_ATTACK_COMBO )
7485 						{
7486 							monsterHitTime = HITRATE * 2 - 25;
7487 							if ( monsterLichMeleeSwingCount > 1 )
7488 							{
7489 								monsterSpecialState = 0;
7490 								monsterSpecialTimer = 100;
7491 							}
7492 						}
7493 					}
7494 
7495 					if ( monsterDefend == MONSTER_DEFEND_HOLD )
7496 					{
7497 						// skip attack, continue defending. offset the hit time to allow for timing variation.
7498 						monsterHitTime = HITRATE / 4;
7499 					}
7500 					else
7501 					{
7502 						this->attack(pose, charge, nullptr); // attacku! D:<
7503 					}
7504 					this->yaw = oYaw;
7505 				}
7506 			}
7507 		}
7508 	}
7509 	else
7510 	{
7511 		if ( ticks % (90 + getUID() % 10) == 0 )
7512 		{
7513 			if ( !hasrangedweapon && dist > TOUCHRANGE && target && target->hasRangedWeapon() )
7514 			{
7515 				int oldDefend = monsterDefend;
7516 				monsterDefend = shouldMonsterDefend(*myStats, *target, *target->getStats(), dist, hasrangedweapon);
7517 				if ( oldDefend != monsterDefend )
7518 				{
7519 					serverUpdateEntitySkill(this, 47);
7520 				}
7521 			}
7522 		}
7523 	}
7524 
7525 	return;
7526 }
7527 
limbAnimateWithOvershoot(Entity * limb,int axis,double setpointRate,double setpoint,double endpointRate,double endpoint,int dir)7528 int limbAnimateWithOvershoot(Entity* limb, int axis, double setpointRate, double setpoint, double endpointRate, double endpoint, int dir)
7529 {
7530 	double speedMultiplier = 1.0;
7531 
7532 	if ( monsterGlobalAnimationMultiplier != 10 )
7533 	{
7534 		speedMultiplier = monsterGlobalAnimationMultiplier / 10.0;
7535 		setpointRate = setpointRate * speedMultiplier;
7536 		endpointRate = endpointRate * speedMultiplier;
7537 	}
7538 
7539 	if ( axis == 0 || limb->monsterAnimationLimbOvershoot == ANIMATE_OVERSHOOT_NONE || dir == ANIMATE_DIR_NONE )
7540 	{
7541 		if ( axis == ANIMATE_PITCH )
7542 		{
7543 			limb->pitch = endpoint;
7544 		}
7545 		else if ( axis == ANIMATE_ROLL )
7546 		{
7547 			limb->roll = endpoint;
7548 		}
7549 		else if ( axis == ANIMATE_YAW )
7550 		{
7551 			limb->yaw = endpoint;
7552 		}
7553 		// no animation required.
7554 		return -1;
7555 	}
7556 
7557 	if ( axis == ANIMATE_PITCH )
7558 	{
7559 		if ( limb->monsterAnimationLimbOvershoot == ANIMATE_OVERSHOOT_TO_SETPOINT )
7560 		{
7561 			limb->pitch += setpointRate * dir;
7562 			while ( limb->pitch < 0 )
7563 			{
7564 				limb->pitch += 2 * PI;
7565 			}
7566 			while ( limb->pitch >= 2 * PI )
7567 			{
7568 				limb->pitch -= 2 * PI;
7569 			}
7570 
7571 			if ( limbAngleWithinRange(limb->pitch, setpointRate, setpoint) )
7572 			{
7573 				limb->pitch = setpoint;
7574 				limb->monsterAnimationLimbOvershoot = ANIMATE_OVERSHOOT_TO_ENDPOINT;
7575 				return ANIMATE_OVERSHOOT_TO_SETPOINT; //reached setpoint
7576 			}
7577 		}
7578 		else if ( limb->monsterAnimationLimbOvershoot == ANIMATE_OVERSHOOT_TO_ENDPOINT )
7579 		{
7580 			limb->pitch -= endpointRate * dir;
7581 			while ( limb->pitch < 0 )
7582 			{
7583 				limb->pitch += 2 * PI;
7584 			}
7585 			while ( limb->pitch >= 2 * PI )
7586 			{
7587 				limb->pitch -= 2 * PI;
7588 			}
7589 
7590 			if ( limbAngleWithinRange(limb->pitch, endpointRate, endpoint) )
7591 			{
7592 				limb->pitch = endpoint;
7593 				limb->monsterAnimationLimbOvershoot = ANIMATE_OVERSHOOT_NONE;
7594 				return ANIMATE_OVERSHOOT_TO_ENDPOINT; //reached endpoint.
7595 			}
7596 		}
7597 	}
7598 	else if ( axis == ANIMATE_ROLL )
7599 	{
7600 		if ( limb->monsterAnimationLimbOvershoot == ANIMATE_OVERSHOOT_TO_SETPOINT )
7601 		{
7602 			limb->roll += setpointRate * dir;
7603 			while ( limb->roll < 0 )
7604 			{
7605 				limb->roll += 2 * PI;
7606 			}
7607 			while ( limb->roll >= 2 * PI )
7608 			{
7609 				limb->roll -= 2 * PI;
7610 			}
7611 
7612 			if ( limbAngleWithinRange(limb->roll, setpointRate, setpoint) )
7613 			{
7614 				limb->roll = setpoint;
7615 				limb->monsterAnimationLimbOvershoot = ANIMATE_OVERSHOOT_TO_ENDPOINT;
7616 				return ANIMATE_OVERSHOOT_TO_SETPOINT; //reached setpoint
7617 			}
7618 		}
7619 		else if ( limb->monsterAnimationLimbOvershoot == ANIMATE_OVERSHOOT_TO_ENDPOINT )
7620 		{
7621 			limb->roll -= endpointRate * dir;
7622 			while ( limb->roll < 0 )
7623 			{
7624 				limb->roll += 2 * PI;
7625 			}
7626 			while ( limb->roll >= 2 * PI )
7627 			{
7628 				limb->roll -= 2 * PI;
7629 			}
7630 
7631 			if ( limbAngleWithinRange(limb->roll, endpointRate, endpoint) )
7632 			{
7633 				limb->roll = endpoint;
7634 				limb->monsterAnimationLimbOvershoot = ANIMATE_OVERSHOOT_NONE;
7635 				return ANIMATE_OVERSHOOT_TO_ENDPOINT; //reached endpoint.
7636 			}
7637 		}
7638 	}
7639 	else if ( axis == ANIMATE_YAW )
7640 	{
7641 		if ( limb->monsterAnimationLimbOvershoot == ANIMATE_OVERSHOOT_TO_SETPOINT )
7642 		{
7643 			limb->yaw += setpointRate * dir;
7644 			while ( limb->yaw < 0 )
7645 			{
7646 				limb->yaw += 2 * PI;
7647 			}
7648 			while ( limb->yaw >= 2 * PI )
7649 			{
7650 				limb->yaw -= 2 * PI;
7651 			}
7652 
7653 			if ( limbAngleWithinRange(limb->yaw, setpointRate, setpoint) )
7654 			{
7655 				limb->yaw = setpoint;
7656 				limb->monsterAnimationLimbOvershoot = ANIMATE_OVERSHOOT_TO_ENDPOINT;
7657 				return ANIMATE_OVERSHOOT_TO_SETPOINT; //reached setpoint
7658 			}
7659 		}
7660 		else if ( limb->monsterAnimationLimbOvershoot == ANIMATE_OVERSHOOT_TO_ENDPOINT )
7661 		{
7662 			limb->yaw -= endpointRate * dir;
7663 			while ( limb->yaw < 0 )
7664 			{
7665 				limb->yaw += 2 * PI;
7666 			}
7667 			while ( limb->yaw >= 2 * PI )
7668 			{
7669 				limb->yaw -= 2 * PI;
7670 			}
7671 
7672 			if ( limbAngleWithinRange(limb->yaw, endpointRate, endpoint) )
7673 			{
7674 				limb->yaw = endpoint;
7675 				limb->monsterAnimationLimbOvershoot = ANIMATE_OVERSHOOT_NONE;
7676 				return ANIMATE_OVERSHOOT_TO_ENDPOINT; //reached endpoint.
7677 			}
7678 		}
7679 	}
7680 	else if ( axis == ANIMATE_Z )
7681 	{
7682 		if ( limb->monsterAnimationLimbOvershoot == ANIMATE_OVERSHOOT_TO_SETPOINT )
7683 		{
7684 			limb->z += setpointRate * dir;
7685 
7686 			if ( limbAngleWithinRange(limb->z, setpointRate, setpoint) )
7687 			{
7688 				limb->z = setpoint;
7689 				limb->monsterAnimationLimbOvershoot = ANIMATE_OVERSHOOT_TO_ENDPOINT;
7690 				return ANIMATE_OVERSHOOT_TO_SETPOINT; //reached setpoint
7691 			}
7692 		}
7693 		else if ( limb->monsterAnimationLimbOvershoot == ANIMATE_OVERSHOOT_TO_ENDPOINT )
7694 		{
7695 			limb->z -= endpointRate * dir;
7696 
7697 			if ( limbAngleWithinRange(limb->z, endpointRate, endpoint) )
7698 			{
7699 				limb->z = endpoint;
7700 				limb->monsterAnimationLimbOvershoot = ANIMATE_OVERSHOOT_NONE;
7701 				return ANIMATE_OVERSHOOT_TO_ENDPOINT; //reached endpoint.
7702 			}
7703 		}
7704 	}
7705 
7706 	return -1;
7707 }
7708 
limbAnimateToLimit(Entity * limb,int axis,double rate,double setpoint,bool shake,double shakerate)7709 int limbAnimateToLimit(Entity* limb, int axis, double rate, double setpoint, bool shake, double shakerate)
7710 {
7711 	if ( axis == 0 )
7712 	{
7713 		return 0;
7714 	}
7715 
7716 	double speedMultiplier = 1.0;
7717 
7718 	if ( monsterGlobalAnimationMultiplier != 10 )
7719 	{
7720 		speedMultiplier = monsterGlobalAnimationMultiplier / 10.0;
7721 		rate = rate * speedMultiplier;
7722 		shakerate = shakerate * speedMultiplier;
7723 	}
7724 
7725 	if ( axis == ANIMATE_YAW )
7726 	{
7727 		while ( limb->yaw < 0 )
7728 		{
7729 			limb->yaw += 2 * PI;
7730 		}
7731 		while ( limb->yaw >= 2 * PI )
7732 		{
7733 			limb->yaw -= 2 * PI;
7734 		}
7735 
7736 		if ( limbAngleWithinRange(limb->yaw, rate, setpoint) )
7737 		{
7738 			limb->yaw = setpoint;
7739 			if ( shake )
7740 			{
7741 				if ( limb->monsterAnimationLimbDirection == ANIMATE_DIR_NONE )
7742 				{
7743 					// no direction for shake is set.
7744 					limb->monsterAnimationLimbDirection = ANIMATE_DIR_POSITIVE;
7745 				}
7746 				if ( limb->monsterAnimationLimbDirection == ANIMATE_DIR_POSITIVE )
7747 				{
7748 					limb->yaw += shakerate;
7749 					limb->monsterAnimationLimbDirection = ANIMATE_DIR_NEGATIVE;
7750 				}
7751 				else if ( limb->monsterAnimationLimbDirection == ANIMATE_DIR_NEGATIVE )
7752 				{
7753 					limb->yaw -= shakerate;
7754 					limb->monsterAnimationLimbDirection = ANIMATE_DIR_POSITIVE;
7755 				}
7756 			}
7757 			return 1; //reached setpoint
7758 		}
7759 		limb->yaw += rate;
7760 	}
7761 	else if ( axis == ANIMATE_PITCH )
7762 	{
7763 		while ( limb->pitch < 0 )
7764 		{
7765 			limb->pitch += 2 * PI;
7766 		}
7767 		while ( limb->pitch >= 2 * PI )
7768 		{
7769 			limb->pitch -= 2 * PI;
7770 		}
7771 
7772 		if ( limbAngleWithinRange(limb->pitch, rate, setpoint) )
7773 		{
7774 			limb->pitch = setpoint;
7775 			if ( shake )
7776 			{
7777 				if ( limb->monsterAnimationLimbDirection == ANIMATE_DIR_NONE )
7778 				{
7779 					// no direction for shake is set.
7780 					limb->monsterAnimationLimbDirection = ANIMATE_DIR_POSITIVE;
7781 				}
7782 				if ( limb->monsterAnimationLimbDirection == ANIMATE_DIR_POSITIVE )
7783 				{
7784 					limb->pitch += shakerate;
7785 					limb->monsterAnimationLimbDirection = ANIMATE_DIR_NEGATIVE;
7786 				}
7787 				else if ( limb->monsterAnimationLimbDirection == ANIMATE_DIR_NEGATIVE )
7788 				{
7789 					limb->pitch -= shakerate;
7790 					limb->monsterAnimationLimbDirection = ANIMATE_DIR_POSITIVE;
7791 				}
7792 			}
7793 			return 1; //reached setpoint
7794 		}
7795 		limb->pitch += rate;
7796 	}
7797 	else if ( axis == ANIMATE_ROLL )
7798 	{
7799 		while ( limb->roll < 0 )
7800 		{
7801 			limb->roll += 2 * PI;
7802 		}
7803 		while ( limb->roll >= 2 * PI )
7804 		{
7805 			limb->roll -= 2 * PI;
7806 		}
7807 
7808 		if ( limbAngleWithinRange(limb->roll, rate, setpoint) )
7809 		{
7810 			limb->roll = setpoint;
7811 			if ( shake )
7812 			{
7813 				if ( limb->monsterAnimationLimbDirection == ANIMATE_DIR_NONE )
7814 				{
7815 					// no direction for shake is set.
7816 					limb->monsterAnimationLimbDirection = ANIMATE_DIR_POSITIVE;
7817 				}
7818 				if ( limb->monsterAnimationLimbDirection == ANIMATE_DIR_POSITIVE )
7819 				{
7820 					limb->roll += shakerate;
7821 					limb->monsterAnimationLimbDirection = ANIMATE_DIR_NEGATIVE;
7822 				}
7823 				else if ( limb->monsterAnimationLimbDirection == ANIMATE_DIR_NEGATIVE )
7824 				{
7825 					limb->roll -= shakerate;
7826 					limb->monsterAnimationLimbDirection = ANIMATE_DIR_POSITIVE;
7827 				}
7828 			}
7829 			return 1; //reached setpoint
7830 		}
7831 		limb->roll += rate;
7832 	}
7833 	else if ( axis == ANIMATE_Z )
7834 	{
7835 		if ( limbAngleWithinRange(limb->z, rate, setpoint) )
7836 		{
7837 			limb->z = setpoint;
7838 			if ( shake )
7839 			{
7840 				if ( limb->monsterAnimationLimbDirection == ANIMATE_DIR_NONE )
7841 				{
7842 					// no direction for shake is set.
7843 					limb->monsterAnimationLimbDirection = ANIMATE_DIR_POSITIVE;
7844 				}
7845 				if ( limb->monsterAnimationLimbDirection == ANIMATE_DIR_POSITIVE )
7846 				{
7847 					limb->z += shakerate;
7848 					limb->monsterAnimationLimbDirection = ANIMATE_DIR_NEGATIVE;
7849 				}
7850 				else if ( limb->monsterAnimationLimbDirection == ANIMATE_DIR_NEGATIVE )
7851 				{
7852 					limb->z -= shakerate;
7853 					limb->monsterAnimationLimbDirection = ANIMATE_DIR_POSITIVE;
7854 				}
7855 			}
7856 			return 1; //reached setpoint
7857 		}
7858 		limb->z += rate;
7859 	}
7860 	else if ( axis == ANIMATE_WEAPON_YAW )
7861 	{
7862 		while ( limb->fskill[5] < 0 )
7863 		{
7864 			limb->fskill[5] += 2 * PI;
7865 		}
7866 		while ( limb->fskill[5] >= 2 * PI )
7867 		{
7868 			limb->fskill[5] -= 2 * PI;
7869 		}
7870 
7871 		if ( limbAngleWithinRange(limb->fskill[5], rate, setpoint) )
7872 		{
7873 			limb->fskill[5] = setpoint;
7874 			return 1; //reached setpoint
7875 		}
7876 		limb->fskill[5] += rate;
7877 	}
7878 
7879 	return 0;
7880 }
7881 
limbAngleWithinRange(real_t angle,double rate,double setpoint)7882 int limbAngleWithinRange(real_t angle, double rate, double setpoint)
7883 {
7884 	if ( rate > 0 )
7885 	{
7886 		if ( (angle <= (setpoint + rate)) && (angle >= (setpoint - rate)) )
7887 		{
7888 			return 1;
7889 		}
7890 	}
7891 	else if ( rate < 0 )
7892 	{
7893 		if ( (angle >= (setpoint + rate)) && (angle <= (setpoint - rate)) )
7894 		{
7895 			return 1;
7896 		}
7897 	}
7898 
7899 	return 0;
7900 }
7901 
normaliseAngle2PI(real_t angle)7902 real_t normaliseAngle2PI(real_t angle)
7903 {
7904 	while ( angle >= 2 * PI )
7905 	{
7906 		angle -= 2 * PI;
7907 	}
7908 	while ( angle < 0 )
7909 	{
7910 		angle += 2 * PI;
7911 	}
7912 
7913 	return angle;
7914 }
7915 
forceFollower(Entity & leader,Entity & follower)7916 bool forceFollower(Entity& leader, Entity& follower)
7917 {
7918 	Stat* leaderStats = leader.getStats();
7919 	Stat* followerStats = follower.getStats();
7920 	if ( !leaderStats || !followerStats )
7921 	{
7922 		printlog("[forceFollower] Error: Either leader or follower did not have stats.");
7923 		return false;
7924 	}
7925 
7926 	Uint32* myuid = (Uint32*) (malloc(sizeof(Uint32)));
7927 	*myuid = follower.getUID();
7928 
7929 	//Deal with the old leader.
7930 	if ( followerStats->leader_uid != 0 )
7931 	{
7932 		Entity* oldLeader = uidToEntity(followerStats->leader_uid);
7933 		if ( oldLeader )
7934 		{
7935 			Stat* oldLeaderStats = oldLeader->getStats();
7936 			if ( oldLeaderStats )
7937 			{
7938 				if ( leader.behavior == &actPlayer
7939 					&& oldLeader == &leader )
7940 				{
7941 					steamAchievementClient(leader.skill[2], "BARONY_ACH_CONFESSOR");
7942 				}
7943 				list_RemoveNodeWithElement<Uint32>(oldLeaderStats->FOLLOWERS, *myuid);
7944 				if ( oldLeader->behavior == &actPlayer )
7945 				{
7946 					serverRemoveClientFollower(oldLeader->skill[2], *myuid);
7947 				}
7948 			}
7949 		}
7950 	}
7951 
7952 	node_t* newNode = list_AddNodeLast(&leaderStats->FOLLOWERS);
7953 	newNode->deconstructor = &defaultDeconstructor;
7954 	newNode->element = myuid;
7955 
7956 	follower.monsterState = 0;
7957 	follower.monsterTarget = 0;
7958 	followerStats->leader_uid = leader.getUID();
7959 
7960 	for ( node_t* node = leaderStats->FOLLOWERS.first; node != nullptr; node = node->next )
7961 	{
7962 		Uint32* c = (Uint32*)node->element;
7963 		Entity* entity = nullptr;
7964 		if ( c )
7965 		{
7966 			entity = uidToEntity(*c);
7967 		}
7968 		if ( entity && entity->monsterTarget == *myuid )
7969 		{
7970 			entity->monsterReleaseAttackTarget(); // followers stop punching the new target.
7971 		}
7972 	}
7973 
7974 	int player = leader.isEntityPlayer();
7975 	if ( player > 0 && multiplayer == SERVER )
7976 	{
7977 		//Tell the client he suckered somebody into his cult.
7978 		strcpy((char*) (net_packet->data), "LEAD");
7979 		SDLNet_Write32((Uint32 )follower.getUID(), &net_packet->data[4]);
7980 		strcpy((char*)(&net_packet->data[8]), followerStats->name);
7981 		net_packet->data[8 + strlen(followerStats->name)] = 0;
7982 		net_packet->address.host = net_clients[player - 1].host;
7983 		net_packet->address.port = net_clients[player - 1].port;
7984 		net_packet->len = 8 + strlen(followerStats->name) + 1;
7985 		sendPacketSafe(net_sock, -1, net_packet, player - 1);
7986 
7987 		serverUpdateAllyStat(player, follower.getUID(), followerStats->LVL, followerStats->HP, followerStats->MAXHP, followerStats->type);
7988 	}
7989 
7990 	if ( !FollowerMenu.recentEntity && player == clientnum )
7991 	{
7992 		FollowerMenu.recentEntity = &follower;
7993 	}
7994 
7995 	if ( player >= 0 )
7996 	{
7997 		if ( leaderStats->type != HUMAN && followerStats->type == HUMAN )
7998 		{
7999 			steamAchievementClient(player, "BARONY_ACH_PITY_FRIEND");
8000 		}
8001 		else if ( leaderStats->type == VAMPIRE && followerStats->type == VAMPIRE )
8002 		{
8003 			if ( !strncmp(followerStats->name, "young vampire", strlen("young vampire")) )
8004 			{
8005 				steamAchievementClient(player, "BARONY_ACH_YOUNG_BLOOD");
8006 			}
8007 		}
8008 	}
8009 
8010 	return true;
8011 }
8012 
handleMonsterSpecialAttack(Stat * myStats,Entity * target,double dist)8013 bool Entity::handleMonsterSpecialAttack(Stat* myStats, Entity* target, double dist)
8014 {
8015 	int specialRoll = 0;
8016 	node_t* node = nullptr;
8017 	int enemiesNearby = 0;
8018 	int bonusFromHP = 0;
8019 	bool hasrangedweapon = this->hasRangedWeapon();
8020 
8021 	if ( myStats != nullptr )
8022 	{
8023 		if ( myStats->type == LICH
8024 			|| myStats->type == DEVIL
8025 			|| myStats->type == SHOPKEEPER
8026 			|| myStats->type == LICH_FIRE
8027 			|| myStats->type == LICH_ICE )
8028 		{
8029 			// monster should attack after this function is called.
8030 			return true;
8031 		}
8032 
8033 		if ( this->monsterSpecialTimer == 0 )
8034 		{
8035 			if ( myStats->MISC_FLAGS[STAT_FLAG_MONSTER_CAST_INVENTORY_SPELLBOOKS] > 0
8036 				&& (monsterSpecialState == MONSTER_SPELLCAST_GENERIC || monsterSpecialState == MONSTER_SPELLCAST_GENERIC2) )
8037 			{
8038 				monsterSpecialState = 0;
8039 				if ( myStats->weapon && itemCategory(myStats->weapon) == SPELLBOOK )
8040 				{
8041 					node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), WEAPON); // find weapon to re-equip
8042 					if ( node != nullptr )
8043 					{
8044 						swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8045 						return true;
8046 					}
8047 					node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), MAGICSTAFF); // find weapon to re-equip
8048 					if ( node != nullptr )
8049 					{
8050 						swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8051 						return true;
8052 					}
8053 					else
8054 					{
8055 						monsterUnequipSlotFromCategory(myStats, &myStats->weapon, SPELLBOOK);
8056 					}
8057 				}
8058 				return true;
8059 			}
8060 
8061 			switch ( myStats->type )
8062 			{
8063 				case KOBOLD:
8064 					if ( hasrangedweapon || myStats->weapon == nullptr )
8065 					{
8066 						specialRoll = rand() % 20;
8067 						//messagePlayer(0, "Rolled: %d", specialRoll);
8068 						if ( myStats->HP < myStats->MAXHP / 2 )
8069 						{
8070 							if ( (dist < 40 && specialRoll < 10) || (dist < 100 && specialRoll < 5) ) // 50%/25% chance
8071 							{
8072 								node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), SPELLBOOK);
8073 								if ( node != nullptr )
8074 								{
8075 									swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8076 									this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_KOBOLD;
8077 								}
8078 							}
8079 						}
8080 						else if ( myStats->HP < (0.8 * myStats->MAXHP) )
8081 						{
8082 							if ( (dist < 40 && specialRoll < 5) || (dist < 100 && specialRoll < 2) ) // 25%/10% chance
8083 							{
8084 								node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), SPELLBOOK);
8085 								if ( node != nullptr )
8086 								{
8087 									swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8088 									this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_KOBOLD;
8089 								}
8090 							}
8091 						}
8092 					}
8093 					break;
8094 				case SUCCUBUS:
8095 					if ( monsterSpecialState == SUCCUBUS_CHARM )
8096 					{
8097 						// special handled in succubusChooseWeapon()
8098 						this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_SUCCUBUS_CHARM;
8099 						break;
8100 					}
8101 					break;
8102 				case CRYSTALGOLEM:
8103 					specialRoll = rand() % 20;
8104 					enemiesNearby = numTargetsAroundEntity(this, STRIKERANGE, PI, MONSTER_TARGET_ENEMY);
8105 					if ( enemiesNearby > 1 )
8106 					{
8107 						enemiesNearby = std::min(enemiesNearby, 4);
8108 						if ( specialRoll < enemiesNearby * 2 ) // 10% for each enemy > 1, capped at 40%
8109 						{
8110 							this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_GOLEM;
8111 							break;
8112 						}
8113 					}
8114 
8115 					specialRoll = rand() % 20;
8116 					if ( myStats->HP > myStats->MAXHP * 0.8 )
8117 					{
8118 						if ( specialRoll < 2 ) // 10%
8119 						{
8120 							this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_GOLEM;
8121 						}
8122 					}
8123 					else if ( myStats->HP > myStats->MAXHP * 0.6 )
8124 					{
8125 						if ( specialRoll < 3 ) // 15%
8126 						{
8127 							this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_GOLEM;
8128 						}
8129 					}
8130 					else if ( myStats->HP > myStats->MAXHP * 0.4 )
8131 					{
8132 						if ( specialRoll < 4 ) // 20%
8133 						{
8134 							this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_GOLEM;
8135 						}
8136 					}
8137 					else if ( myStats->HP > myStats->MAXHP * 0.2 )
8138 					{
8139 						if ( specialRoll < 5 ) // 25%
8140 						{
8141 							this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_GOLEM;
8142 						}
8143 					}
8144 					else if ( myStats->HP > myStats->MAXHP * 0.2 )
8145 					{
8146 						if ( specialRoll < 5 ) // 25%
8147 						{
8148 							this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_GOLEM;
8149 						}
8150 					}
8151 					break;
8152 				case COCKATRICE:
8153 					specialRoll = rand() % 20;
8154 					//specialRoll = 0;
8155 					// check for paralyze first
8156 					enemiesNearby = std::min(numTargetsAroundEntity(this, STRIKERANGE * 2, PI, MONSTER_TARGET_ENEMY), 4);
8157 
8158 					if ( myStats->HP <= myStats->MAXHP * 0.5 )
8159 					{
8160 						bonusFromHP = 4; // +20% chance if on low health
8161 					}
8162 					if ( specialRoll < (enemiesNearby * 2 + bonusFromHP) ) // +10% for each enemy, capped at 40%
8163 					{
8164 						node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), SPELLBOOK);
8165 						if ( node != nullptr )
8166 						{
8167 							swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8168 							this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_COCKATRICE_STONE;
8169 						}
8170 						break;
8171 					}
8172 
8173 					// nothing selected, look for double attack.
8174 					specialRoll = rand() % 20;
8175 					if ( myStats->HP > myStats->MAXHP * 0.8 )
8176 					{
8177 						if ( specialRoll < 2 ) // 10%
8178 						{
8179 							this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_COCKATRICE_ATK;
8180 						}
8181 					}
8182 					else if ( myStats->HP > myStats->MAXHP * 0.6 )
8183 					{
8184 						if ( specialRoll < 2 ) // 10%
8185 						{
8186 							this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_COCKATRICE_ATK;
8187 						}
8188 					}
8189 					else if ( myStats->HP > myStats->MAXHP * 0.4 )
8190 					{
8191 						if ( specialRoll < 3 ) // 15%
8192 						{
8193 							this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_COCKATRICE_ATK;
8194 						}
8195 					}
8196 					else if ( myStats->HP > myStats->MAXHP * 0.2 )
8197 					{
8198 						if ( specialRoll < 4 ) // 20%
8199 						{
8200 							this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_COCKATRICE_ATK;
8201 						}
8202 					}
8203 					else if ( myStats->HP <= myStats->MAXHP * 0.2 )
8204 					{
8205 						if ( specialRoll < 5 ) // 25%
8206 						{
8207 							this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_COCKATRICE_ATK;
8208 						}
8209 					}
8210 					break;
8211 				case INSECTOID:
8212 					if ( monsterSpecialState == INSECTOID_DOUBLETHROW_FIRST || monsterSpecialState == INSECTOID_DOUBLETHROW_SECOND )
8213 					{
8214 						this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_INSECTOID_THROW;
8215 						break;
8216 					}
8217 
8218 					// spray acid
8219 					if ( dist < STRIKERANGE * 2 )
8220 					{
8221 						specialRoll = rand() % 20;
8222 						enemiesNearby = std::min(numTargetsAroundEntity(this, STRIKERANGE * 2, PI, MONSTER_TARGET_ENEMY), 4);
8223 						//messagePlayer(0, "insectoid roll %d", specialRoll);
8224 						if ( myStats->HP <= myStats->MAXHP * 0.8 )
8225 						{
8226 							bonusFromHP = 4; // +20% chance if on low health
8227 						}
8228 						if ( specialRoll < (enemiesNearby * 2 + bonusFromHP) ) // +10% for each enemy, capped at 40%
8229 						{
8230 							node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), SPELLBOOK);
8231 							if ( node != nullptr )
8232 							{
8233 								monsterSpecialState = INSECTOID_ACID;
8234 								swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8235 								this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_INSECTOID_ACID;
8236 								serverUpdateEntitySkill(this, 33); // for clients to handle animation
8237 							}
8238 							else
8239 							{
8240 								if ( myStats->weapon && itemCategory(myStats->weapon) == SPELLBOOK )
8241 								{
8242 									monsterSpecialState = INSECTOID_ACID;
8243 									this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_INSECTOID_ACID;
8244 									serverUpdateEntitySkill(this, 33); // for clients to handle animation
8245 								}
8246 							}
8247 							break;
8248 						}
8249 					}
8250 					// throwing weapon special handled in insectoidChooseWeapon()
8251 					break;
8252 				case INCUBUS:
8253 					if ( monsterSpecialState == INCUBUS_CONFUSION )
8254 					{
8255 						// throwing weapon special handled in incubusChooseWeapon()
8256 						this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_INCUBUS_CONFUSION;
8257 						break;
8258 					}
8259 					else if ( monsterSpecialState == INCUBUS_STEAL )
8260 					{
8261 						// special handled in incubusChooseWeapon()
8262 						this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_INCUBUS_STEAL;
8263 						break;
8264 					}
8265 					else if ( monsterSpecialState == INCUBUS_TELEPORT )
8266 					{
8267 						// special handled in incubusChooseWeapon()
8268 						this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_INCUBUS_TELEPORT_TARGET;
8269 						break;
8270 					}
8271 					else if ( monsterSpecialState == INCUBUS_CHARM )
8272 					{
8273 						// special handled in incubusChooseWeapon()
8274 						this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_INCUBUS_CHARM;
8275 						break;
8276 					}
8277 					break;
8278 				case VAMPIRE:
8279 					if ( monsterSpecialState == VAMPIRE_CAST_AURA )
8280 					{
8281 						// special handled in vampireChooseWeapon()
8282 						this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_VAMPIRE_AURA;
8283 					}
8284 					else if ( monsterSpecialState == VAMPIRE_CAST_DRAIN )
8285 					{
8286 						// special handled in vampireChooseWeapon()
8287 						this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_VAMPIRE_DRAIN;
8288 					}
8289 					break;
8290 				case SHADOW:
8291 					if ( monsterSpecialState == SHADOW_SPELLCAST )
8292 					{
8293 						monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_SHADOW_SPELLCAST;
8294 					}
8295 					else if ( monsterSpecialState == SHADOW_TELEPORT_ONLY )
8296 					{
8297 						// special handled in shadowChooseWeapon(), teleport code in path state.
8298 						this->monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_SHADOW_TELEPORT;
8299 						break;
8300 					}
8301 				case GOATMAN:
8302 					if ( monsterSpecialState == GOATMAN_POTION )
8303 					{
8304 						monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_GOATMAN_DRINK;
8305 					}
8306 					else if ( monsterSpecialState == GOATMAN_THROW )
8307 					{
8308 						monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_GOATMAN_THROW;
8309 					}
8310 					break;
8311 				default:
8312 					break;
8313 			}
8314 		}
8315 		else if ( this->monsterSpecialTimer > 0 )
8316 		{
8317 			bool shouldAttack = true;
8318 
8319 			if ( myStats->MISC_FLAGS[STAT_FLAG_MONSTER_CAST_INVENTORY_SPELLBOOKS] > 0 )
8320 			{
8321 				if ( monsterSpecialState == MONSTER_SPELLCAST_GENERIC )
8322 				{
8323 					monsterSpecialState = MONSTER_SPELLCAST_GENERIC2;
8324 					return true;
8325 				}
8326 				else if ( monsterSpecialState == MONSTER_SPELLCAST_GENERIC2 )
8327 				{
8328 					monsterSpecialState = 0;
8329 					node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), WEAPON); // find weapon to re-equip
8330 					if ( node != nullptr )
8331 					{
8332 						swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8333 						return true;
8334 					}
8335 					node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), MAGICSTAFF); // find weapon to re-equip
8336 					if ( node != nullptr )
8337 					{
8338 						swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8339 						return true;
8340 					}
8341 					else
8342 					{
8343 						monsterUnequipSlotFromCategory(myStats, &myStats->weapon, SPELLBOOK);
8344 					}
8345 					return true;
8346 				}
8347 			}
8348 
8349 			switch ( myStats->type )
8350 			{
8351 				case KOBOLD:
8352 					node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), WEAPON); // find weapon to re-equip
8353 					if ( node != nullptr )
8354 					{
8355 						swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8356 					}
8357 					else
8358 					{
8359 						monsterUnequipSlotFromCategory(myStats, &myStats->weapon, SPELLBOOK);
8360 					}
8361 					break;
8362 				case SUCCUBUS:
8363 					if ( monsterSpecialState == SUCCUBUS_CHARM )
8364 					{
8365 						node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), WEAPON); // find weapon to re-equip
8366 						if ( node != nullptr )
8367 						{
8368 							swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8369 						}
8370 						else
8371 						{
8372 							monsterUnequipSlotFromCategory(myStats, &myStats->weapon, SPELLBOOK);
8373 						}
8374 						shouldAttack = false;
8375 						monsterSpecialState = 0;
8376 					}
8377 					break;
8378 				case INSECTOID:
8379 					if ( monsterSpecialState == INSECTOID_ACID )
8380 					{
8381 						monsterSpecialState = 0;
8382 						serverUpdateEntitySkill(this, 33); // for clients to handle animation
8383 						node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), WEAPON); // find weapon to re-equip
8384 						if ( node != nullptr )
8385 						{
8386 							swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8387 						}
8388 						else
8389 						{
8390 							monsterUnequipSlotFromCategory(myStats, &myStats->weapon, SPELLBOOK);
8391 						}
8392 						shouldAttack = false;
8393 					}
8394 					else if ( monsterSpecialState == INSECTOID_DOUBLETHROW_SECOND )
8395 					{
8396 						monsterSpecialState = 0;
8397 						node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), WEAPON); // find weapon to re-equip
8398 						if ( node != nullptr )
8399 						{
8400 							swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8401 						}
8402 						else
8403 						{
8404 							monsterUnequipSlotFromCategory(myStats, &myStats->weapon, THROWN);
8405 						}
8406 						shouldAttack = false;
8407 					}
8408 					break;
8409 				case COCKATRICE:
8410 					monsterUnequipSlotFromCategory(myStats, &myStats->weapon, SPELLBOOK);
8411 					break;
8412 				case INCUBUS:
8413 					if ( monsterSpecialState == INCUBUS_CONFUSION )
8414 					{
8415 						node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), WEAPON); // find weapon to re-equip
8416 						if ( node != nullptr )
8417 						{
8418 							swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8419 						}
8420 						else
8421 						{
8422 							monsterUnequipSlotFromCategory(myStats, &myStats->weapon, POTION);
8423 						}
8424 						shouldAttack = false;
8425 						monsterSpecialState = 0;
8426 					}
8427 					else if ( monsterSpecialState == INCUBUS_STEAL )
8428 					{
8429 						node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), WEAPON); // find weapon to re-equip
8430 						if ( node != nullptr )
8431 						{
8432 							swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8433 						}
8434 						else
8435 						{
8436 							monsterUnequipSlotFromCategory(myStats, &myStats->weapon, SPELLBOOK);
8437 						}
8438 						shouldAttack = false;
8439 						monsterSpecialState = 0;
8440 					}
8441 					else if ( monsterSpecialState == INCUBUS_TELEPORT_STEAL )
8442 					{
8443 						// this flag will be cleared in incubusChooseWeapon
8444 					}
8445 					else if ( monsterSpecialState == INCUBUS_TELEPORT )
8446 					{
8447 						// this flag will be cleared in incubusChooseWeapon
8448 					}
8449 					else if ( monsterSpecialState == INCUBUS_CHARM )
8450 					{
8451 						node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), WEAPON); // find weapon to re-equip
8452 						if ( node != nullptr )
8453 						{
8454 							swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8455 						}
8456 						else
8457 						{
8458 							monsterUnequipSlotFromCategory(myStats, &myStats->weapon, SPELLBOOK);
8459 						}
8460 						shouldAttack = false;
8461 						monsterSpecialState = 0;
8462 					}
8463 					serverUpdateEntitySkill(this, 33); // for clients to keep track of animation
8464 					break;
8465 				case VAMPIRE:
8466 					if ( monsterSpecialState == VAMPIRE_CAST_AURA )
8467 					{
8468 						node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), WEAPON); // find weapon to re-equip
8469 						if ( node != nullptr )
8470 						{
8471 							swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8472 						}
8473 						else
8474 						{
8475 							monsterUnequipSlotFromCategory(myStats, &myStats->weapon, SPELLBOOK);
8476 						}
8477 						shouldAttack = false;
8478 						monsterSpecialState = 0;
8479 					}
8480 					else if ( monsterSpecialState == VAMPIRE_CAST_DRAIN )
8481 					{
8482 						node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), WEAPON); // find weapon to re-equip
8483 						if ( node != nullptr )
8484 						{
8485 							swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8486 						}
8487 						else
8488 						{
8489 							monsterUnequipSlotFromCategory(myStats, &myStats->weapon, SPELLBOOK);
8490 						}
8491 						shouldAttack = false;
8492 						monsterSpecialState = 0;
8493 					}
8494 					serverUpdateEntitySkill(this, 33); // for clients to keep track of animation
8495 					break;
8496 				case SHADOW:
8497 					if ( monsterSpecialState == SHADOW_SPELLCAST ) //TODO: This code is destroying spells?
8498 					{
8499 						//TODO: Nope, this code isn't destroying spells. Something *before* this code is.
8500 						//messagePlayer(clientnum, "[DEBUG: handleMonsterSpecialAttack()] Resolving shadow's spellcast.");
8501 						node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), WEAPON); // find weapon to re-equip
8502 						if ( node != nullptr )
8503 						{
8504 							swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8505 						}
8506 						else
8507 						{
8508 							monsterUnequipSlotFromCategory(myStats, &myStats->weapon, SPELLBOOK);
8509 						}
8510 						/*Item *spellbook = newItem(static_cast<ItemType>(0), static_cast<Status>(0), 0, 1, rand(), 0, &myStats->inventory);
8511 						copyItem(spellbook, myStats->weapon);
8512 						dropItemMonster(myStats->weapon, this, myStats, 1);*/
8513 						shouldAttack = false;
8514 						monsterSpecialState = 0;
8515 						monsterSpecialTimer = MONSTER_SPECIAL_COOLDOWN_SHADOW_SPELLCAST;
8516 					}
8517 					serverUpdateEntitySkill(this, 33); // for clients to keep track of animation
8518 					break;
8519 				case GOATMAN:
8520 					if ( monsterSpecialState == GOATMAN_POTION )
8521 					{
8522 						node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), WEAPON); // find weapon to re-equip
8523 						if ( node != nullptr )
8524 						{
8525 							swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8526 						}
8527 						else
8528 						{
8529 							monsterUnequipSlotFromCategory(myStats, &myStats->weapon, POTION);
8530 						}
8531 						shouldAttack = false;
8532 						monsterSpecialState = 0;
8533 					}
8534 					else if ( monsterSpecialState == GOATMAN_THROW )
8535 					{
8536 						node = itemNodeInInventory(myStats, static_cast<ItemType>(-1), WEAPON); // find weapon to re-equip
8537 						if ( node != nullptr )
8538 						{
8539 							swapMonsterWeaponWithInventoryItem(this, myStats, node, false, true);
8540 						}
8541 						else
8542 						{
8543 							monsterUnequipSlotFromCategory(myStats, &myStats->weapon, THROWN);
8544 						}
8545 						shouldAttack = false;
8546 						monsterSpecialState = 0;
8547 					}
8548 					serverUpdateEntitySkill(this, 33); // for clients to keep track of animation
8549 					break;
8550 				default:
8551 					break;
8552 			}
8553 			// Whether monster should attack following the unequip action.
8554 			return shouldAttack;
8555 		}
8556 	}
8557 	// monster should attack after this function is called.
8558 	return true;
8559 }
8560 
getTargetsAroundEntity(Entity * my,Entity * originalTarget,double distToFind,real_t angleToSearch,int searchType,list_t ** list)8561 void getTargetsAroundEntity(Entity* my, Entity* originalTarget, double distToFind, real_t angleToSearch, int searchType, list_t** list)
8562 {
8563 	Entity* entity = nullptr;
8564 	node_t* node = nullptr;
8565 	node_t* node2 = nullptr;
8566 
8567 	// aoe
8568 	for ( node = map.creatures->first; node != nullptr; node = node->next ) //Only looks at monsters and players, don't iterate all entities (map.entities).
8569 	{
8570 		entity = (Entity*)node->element;
8571 		if ( (entity->behavior == &actMonster || entity->behavior == &actPlayer) && entity != originalTarget && entity != my )
8572 		{
8573 			if ( searchType == MONSTER_TARGET_ENEMY )
8574 			{
8575 				if ( !my->checkEnemy(entity) )
8576 				{
8577 					continue;
8578 				}
8579 			}
8580 			else if ( searchType == MONSTER_TARGET_FRIEND )
8581 			{
8582 				if ( !my->checkFriend(entity) )
8583 				{
8584 					continue;
8585 				}
8586 			}
8587 			else if ( searchType == MONSTER_TARGET_PLAYER )
8588 			{
8589 				if ( !(entity->behavior == &actPlayer) )
8590 				{
8591 					continue;
8592 				}
8593 			}
8594 			else if ( searchType == MONSTER_TARGET_ALL )
8595 			{
8596 			}
8597 
8598 			double aoeTangent = atan2(entity->y - my->y, entity->x - my->x);
8599 			real_t angle = my->yaw - aoeTangent;
8600 			while ( angle >= PI )
8601 			{
8602 				angle -= PI * 2;
8603 			}
8604 			while ( angle < -PI )
8605 			{
8606 				angle += PI * 2;
8607 			}
8608 			if ( abs(angle) <= angleToSearch ) // searches in 2x the given angle, +/- from yaw.
8609 			{
8610 				double dist = sqrt(pow(my->x - entity->x, 2) + pow(my->y - entity->y, 2));
8611 				if ( dist < distToFind )
8612 				{
8613 					//If this is the first entity found, the list needs to be created.
8614 					if ( !(*list) )
8615 					{
8616 						*list = (list_t*)malloc(sizeof(list_t));
8617 						(*list)->first = nullptr;
8618 						(*list)->last = nullptr;
8619 					}
8620 					node2 = list_AddNodeLast(*list);
8621 					node2->element = entity;
8622 					node2->deconstructor = &emptyDeconstructor;
8623 					node2->size = sizeof(Entity*);
8624 				}
8625 			}
8626 		}
8627 	}
8628 	return;
8629 }
8630 
numTargetsAroundEntity(Entity * my,double distToFind,real_t angleToSearch,int searchType)8631 int numTargetsAroundEntity(Entity* my, double distToFind, real_t angleToSearch, int searchType)
8632 {
8633 	list_t* aoeTargets = nullptr;
8634 	int count = 0;
8635 	getTargetsAroundEntity(my, nullptr, distToFind, angleToSearch, searchType, &aoeTargets);
8636 	if ( aoeTargets )
8637 	{
8638 		count = list_Size(aoeTargets);
8639 		//messagePlayer(0, "found %d targets", count);
8640 		//Free the list.
8641 		list_FreeAll(aoeTargets);
8642 		free(aoeTargets);
8643 	}
8644 	return count;
8645 }
8646 
handleMonsterChatter(int monsterclicked,bool ringconflict,char namesays[64],Entity * my,Stat * myStats)8647 bool handleMonsterChatter(int monsterclicked, bool ringconflict, char namesays[64], Entity* my, Stat* myStats)
8648 {
8649 	if ( ringconflict || myStats->MISC_FLAGS[STAT_FLAG_NPC] == 0 )
8650 	{
8651 		//Instant fail if ring of conflict is in effect/not NPC
8652 		return false;
8653 	}
8654 
8655 	int NPCtype = myStats->MISC_FLAGS[STAT_FLAG_NPC] & 0xFF; // get NPC type, lowest 8 bits.
8656 	int NPClastLine = (myStats->MISC_FLAGS[STAT_FLAG_NPC] & 0xFF00) >> 8; // get last line said, next 8 bits.
8657 
8658 	int numLines = 0;
8659 	int startLine = 2700 + (NPCtype - 1) * MONSTER_NPC_DIALOGUE_LINES; // lang line to start from.
8660 	int currentLine = startLine + 1;
8661 
8662 	bool isSequential = false;
8663 
8664 	if ( !strcmp(language[startLine], "type:seq") )
8665 	{
8666 		isSequential = true;
8667 	}
8668 
8669 	for ( int i = 1; i < MONSTER_NPC_DIALOGUE_LINES; ++i )
8670 	{
8671 		// find the next 9 lines if available.
8672 		if ( !strcmp(language[currentLine], "") )
8673 		{
8674 			break;
8675 		}
8676 		++currentLine;
8677 		++numLines;
8678 	}
8679 
8680 	// choose a dialogue line.
8681 	if ( numLines > 0 )
8682 	{
8683 		if ( isSequential )
8684 		{
8685 			// say the next line in series.
8686 			if ( NPClastLine != 0 )
8687 			{
8688 				++NPClastLine;
8689 				if ( (NPClastLine) > numLines )
8690 				{
8691 					// reset to beginning
8692 					NPClastLine = 1;
8693 				}
8694 			}
8695 			else
8696 			{
8697 				// first line being said, choose the first.
8698 				NPClastLine = 1;
8699 			}
8700 		}
8701 		else if ( !isSequential )
8702 		{
8703 			// choose randomly
8704 			NPClastLine = 1 + rand() % numLines;
8705 		}
8706 		messagePlayer(monsterclicked, language[startLine + NPClastLine], namesays, stats[monsterclicked]->name);
8707 		myStats->MISC_FLAGS[STAT_FLAG_NPC] = NPCtype + (NPClastLine << 8);
8708 	}
8709 	return true;
8710 }
8711 
numMonsterTypeAliveOnMap(Monster creature,Entity * & lastMonster)8712 int numMonsterTypeAliveOnMap(Monster creature, Entity*& lastMonster)
8713 {
8714 	node_t* node = nullptr;
8715 	Entity* entity = nullptr;
8716 	int monsterCount = 0;
8717 	for ( node = map.creatures->first; node != nullptr; node = node->next )
8718 	{
8719 		entity = (Entity*)node->element;
8720 		if ( entity )
8721 		{
8722 			if ( entity->getRace() == creature )
8723 			{
8724 				lastMonster = entity;
8725 				++monsterCount;
8726 			}
8727 		}
8728 	}
8729 	return monsterCount;
8730 }
8731 
monsterMoveBackwardsAndPath()8732 void Entity::monsterMoveBackwardsAndPath()
8733 {
8734 	while ( yaw < 0 )
8735 	{
8736 		yaw += 2 * PI;
8737 	}
8738 	while ( yaw >= 2 * PI )
8739 	{
8740 		yaw -= 2 * PI;
8741 	}
8742 	int x1 = ((int)floor(x)) >> 4;
8743 	int y1 = ((int)floor(y)) >> 4;
8744 	int u, v;
8745 	std::vector<std::pair<int, int>> areaToTry;
8746 	if ( yaw <= PI / 4 || yaw > 7 * PI / 4 )
8747 	{
8748 		areaToTry.push_back(std::pair<int, int>(x1 - 1, y1));
8749 		areaToTry.push_back(std::pair<int, int>(x1 - 2, y1));
8750 		areaToTry.push_back(std::pair<int, int>(x1 - 2, y1 + 1));
8751 		areaToTry.push_back(std::pair<int, int>(x1 - 2, y1 - 1));
8752 		areaToTry.push_back(std::pair<int, int>(x1 - 3, y1));
8753 	}
8754 	else if ( yaw > PI / 4 && yaw <= 3 * PI / 4 )
8755 	{
8756 		areaToTry.push_back(std::pair<int, int>(x1, y1 - 1));
8757 		areaToTry.push_back(std::pair<int, int>(x1, y1 - 2));
8758 		areaToTry.push_back(std::pair<int, int>(x1 - 1, y1 - 2));
8759 		areaToTry.push_back(std::pair<int, int>(x1 + 1, y1 - 2));
8760 		areaToTry.push_back(std::pair<int, int>(x1, y1 - 3));
8761 	}
8762 	else if ( yaw > 3 * PI / 4 && yaw <= 5 * PI / 4 )
8763 	{
8764 		areaToTry.push_back(std::pair<int, int>(x1 + 1, y1));
8765 		areaToTry.push_back(std::pair<int, int>(x1 + 2, y1));
8766 		areaToTry.push_back(std::pair<int, int>(x1 + 2, y1 + 1));
8767 		areaToTry.push_back(std::pair<int, int>(x1 + 2, y1 - 1));
8768 		areaToTry.push_back(std::pair<int, int>(x1 + 3, y1));
8769 	}
8770 	else if ( yaw > 5 * PI / 4 && yaw <= 7 * PI / 4 )
8771 	{
8772 		areaToTry.push_back(std::pair<int, int>(x1, y1 + 1));
8773 		areaToTry.push_back(std::pair<int, int>(x1, y1 + 2));
8774 		areaToTry.push_back(std::pair<int, int>(x1 - 1, y1 + 2));
8775 		areaToTry.push_back(std::pair<int, int>(x1 + 1, y1 + 2));
8776 		areaToTry.push_back(std::pair<int, int>(x1, y1 + 3));
8777 	}
8778 	bool foundplace = false;
8779 	for ( int tries = 0; tries < areaToTry.size() && !foundplace; ++tries )
8780 	{
8781 		std::pair<int, int> tmpPair = areaToTry[rand() % areaToTry.size()];
8782 		u = tmpPair.first;
8783 		v = tmpPair.second;
8784 		if ( !checkObstacle((u << 4) + 8, (v << 4) + 8, this, nullptr) )
8785 		{
8786 			x1 = u;
8787 			y1 = v;
8788 			foundplace = true;
8789 		}
8790 	}
8791 	path = generatePath((int)floor(x / 16), (int)floor(y / 16), x1, y1, this, this);
8792 	if ( children.first != NULL )
8793 	{
8794 		list_RemoveNode(children.first);
8795 	}
8796 	node_t* node = list_AddNodeFirst(&this->children);
8797 	node->element = path;
8798 	node->deconstructor = &listDeconstructor;
8799 	monsterState = MONSTER_STATE_HUNT; // hunt state
8800 }
8801 
monsterHasLeader()8802 bool Entity::monsterHasLeader()
8803 {
8804 	Stat* myStats = this->getStats();
8805 	if ( myStats )
8806 	{
8807 		if ( myStats->leader_uid != 0 )
8808 		{
8809 			return true;
8810 		}
8811 	}
8812 	return false;
8813 }
8814 
monsterAllySendCommand(int command,int destX,int destY,Uint32 uid)8815 void Entity::monsterAllySendCommand(int command, int destX, int destY, Uint32 uid)
8816 {
8817 	if ( multiplayer == CLIENT )
8818 	{
8819 		return;
8820 	}
8821 	if ( command == -1 || command == ALLY_CMD_CANCEL || monsterAllyIndex == -1 || monsterAllyIndex > MAXPLAYERS )
8822 	{
8823 		return;
8824 	}
8825 	if ( !players[monsterAllyIndex] )
8826 	{
8827 		return;
8828 	}
8829 	if ( !players[monsterAllyIndex]->entity )
8830 	{
8831 		return;
8832 	}
8833 	if ( monsterTarget == players[monsterAllyIndex]->entity->getUID() )
8834 	{
8835 		// angry at owner.
8836 		return;
8837 	}
8838 
8839 	Stat* myStats = getStats();
8840 	if ( !myStats )
8841 	{
8842 		return;
8843 	}
8844 
8845 	if ( !isMobile() )
8846 	{
8847 		// doesn't respond.
8848 		if ( monsterAllySpecial == ALLY_SPECIAL_CMD_REST && myStats->EFFECTS[EFF_ASLEEP]
8849 			&& (command == ALLY_CMD_MOVETO_CONFIRM || command == ALLY_CMD_ATTACK_CONFIRM
8850 				|| command == ALLY_CMD_MOVEASIDE) )
8851 		{
8852 			myStats->EFFECTS[EFF_ASLEEP] = false; // wake up
8853 			myStats->EFFECTS_TIMERS[EFF_ASLEEP] = 0;
8854 			myStats->EFFECTS[EFF_HP_REGEN] = false; // stop regen
8855 			myStats->EFFECTS_TIMERS[EFF_HP_REGEN] = 0;
8856 			monsterAllySpecial = ALLY_SPECIAL_CMD_NONE;
8857 		}
8858 		else
8859 		{
8860 			messagePlayerMonsterEvent(monsterAllyIndex, 0xFFFFFFFF, *myStats, language[514], language[515], MSG_COMBAT);
8861 		}
8862 		return;
8863 	}
8864 
8865 	bool isTinkeringFollower = FollowerMenu.isTinkeringFollower(myStats->type);
8866 	int tinkeringLVL = 0;
8867 	int skillLVL = 0;
8868 	if ( stats[monsterAllyIndex] )
8869 	{
8870 		tinkeringLVL = stats[monsterAllyIndex]->PROFICIENCIES[PRO_LOCKPICKING] + statGetPER(stats[monsterAllyIndex], players[monsterAllyIndex]->entity);
8871 		skillLVL = stats[clientnum]->PROFICIENCIES[PRO_LEADERSHIP] + statGetCHR(stats[clientnum], players[monsterAllyIndex]->entity);
8872 		if ( isTinkeringFollower )
8873 		{
8874 			skillLVL = tinkeringLVL;
8875 		}
8876 	}
8877 
8878 	if ( myStats->type != GYROBOT )
8879 	{
8880 		if ( FollowerMenu.monsterGyroBotOnlyCommand(command) )
8881 		{
8882 			return;
8883 		}
8884 	}
8885 	else if ( myStats->type == GYROBOT )
8886 	{
8887 		if ( FollowerMenu.monsterGyroBotDisallowedCommands(command) )
8888 		{
8889 			return;
8890 		}
8891 	}
8892 
8893 	// do a final check if player can use this command.
8894 	/*if ( FollowerMenu.optionDisabledForCreature(skillLVL, myStats->type, command) != 0 )
8895 	{
8896 		messagePlayerMonsterEvent(monsterAllyIndex, 0xFFFFFFFF,
8897 			*myStats, language[3638], language[3639], MSG_COMBAT);
8898 		return;
8899 	}*/
8900 
8901 	switch ( command )
8902 	{
8903 		case ALLY_CMD_RETURN_SOUL:
8904 			if ( monsterAllySummonRank != 0 )
8905 			{
8906 				float manaToRefund = myStats->MAXMP * (myStats->HP / static_cast<float>(myStats->MAXHP));
8907 				setMP(static_cast<int>(manaToRefund));
8908 				setHP(0);
8909 				if ( stats[monsterAllyIndex] && stats[monsterAllyIndex]->MP == 0 )
8910 				{
8911 					steamAchievementClient(monsterAllyIndex, "BARONY_ACH_EXTERNAL_BATTERY");
8912 				}
8913 			}
8914 			break;
8915 		case ALLY_CMD_ATTACK_CONFIRM:
8916 			if ( uid != 0 && uid != getUID() )
8917 			{
8918 				Entity* target = uidToEntity(uid);
8919 				if ( target )
8920 				{
8921 					if ( target->behavior == &actMonster || target->behavior == &actPlayer )
8922 					{
8923 						if ( stats[monsterAllyIndex] ) // check owner's proficiency.
8924 						{
8925 							if ( skillLVL >= SKILL_LEVEL_MASTER || myStats->type != HUMAN )
8926 							{
8927 								// attack anything except if FF is off + friend.
8928 								if ( !(svFlags & SV_FLAG_FRIENDLYFIRE) )
8929 								{
8930 									if ( !checkFriend(target) )
8931 									{
8932 										monsterAcquireAttackTarget(*target, MONSTER_STATE_ATTACK);
8933 										handleNPCInteractDialogue(*myStats, ALLY_EVENT_ATTACK);
8934 									}
8935 									else
8936 									{
8937 										// messagePlayer(0, "Friendly fire is on!");
8938 										handleNPCInteractDialogue(*myStats, ALLY_EVENT_ATTACK_FRIENDLY_FIRE);
8939 									}
8940 								}
8941 								else
8942 								{
8943 									monsterAcquireAttackTarget(*target, MONSTER_STATE_ATTACK);
8944 									handleNPCInteractDialogue(*myStats, ALLY_EVENT_ATTACK);
8945 								}
8946 							}
8947 							else if ( skillLVL >= AllyNPCSkillRequirements[ALLY_CMD_ATTACK_CONFIRM] )
8948 							{
8949 								if ( !checkFriend(target) )
8950 								{
8951 									monsterAcquireAttackTarget(*target, MONSTER_STATE_ATTACK);
8952 									handleNPCInteractDialogue(*myStats, ALLY_EVENT_ATTACK);
8953 								}
8954 								else
8955 								{
8956 									handleNPCInteractDialogue(*myStats, ALLY_EVENT_ATTACK_FRIENDLY_FIRE);
8957 								}
8958 							}
8959 						}
8960 						monsterAllyInteractTarget = 0;
8961 					}
8962 					else
8963 					{
8964 						monsterAcquireAttackTarget(*target, MONSTER_STATE_PATH);
8965 						monsterAllyState = ALLY_STATE_MOVETO;
8966 					}
8967 				}
8968 			}
8969 			break;
8970 		case ALLY_CMD_MOVEASIDE:
8971 			monsterMoveAside(this, players[monsterAllyIndex]->entity);
8972 			handleNPCInteractDialogue(*myStats, ALLY_EVENT_MOVEASIDE);
8973 			break;
8974 		case ALLY_CMD_DEFEND:
8975 			monsterAllyState = ALLY_STATE_DEFEND;
8976 			if ( myStats->type == SENTRYBOT || myStats->type == SPELLBOT )
8977 			{
8978 				monsterSentrybotLookDir = monsterLookDir;
8979 				handleNPCInteractDialogue(*myStats, ALLY_EVENT_WAIT);
8980 			}
8981 			else
8982 			{
8983 				createPathBoundariesNPC(5);
8984 				// stop in your tracks!
8985 				handleNPCInteractDialogue(*myStats, ALLY_EVENT_WAIT);
8986 				monsterState = MONSTER_STATE_WAIT; // wait state
8987 				serverUpdateEntitySkill(this, 0);
8988 			}
8989 			break;
8990 		case ALLY_CMD_MOVETO_SELECT:
8991 			break;
8992 		case ALLY_CMD_FOLLOW:
8993 			monsterAllyState = ALLY_STATE_DEFAULT;
8994 			if ( myStats->type == SENTRYBOT || myStats->type == SPELLBOT )
8995 			{
8996 				monsterSentrybotLookDir = 0.0;
8997 				handleNPCInteractDialogue(*myStats, ALLY_EVENT_FOLLOW);
8998 			}
8999 			else
9000 			{
9001 				handleNPCInteractDialogue(*myStats, ALLY_EVENT_FOLLOW);
9002 			}
9003 			break;
9004 		case ALLY_CMD_CLASS_TOGGLE:
9005 			++monsterAllyClass;
9006 			if ( monsterAllyClass > ALLY_CLASS_RANGED )
9007 			{
9008 				monsterAllyClass = ALLY_CLASS_MIXED;
9009 			}
9010 			myStats->allyClass = monsterAllyClass;
9011 			serverUpdateEntitySkill(this, 46);
9012 			break;
9013 		case ALLY_CMD_PICKUP_TOGGLE:
9014 			++monsterAllyPickupItems;
9015 			if ( monsterAllyPickupItems > ALLY_PICKUP_ALL )
9016 			{
9017 				monsterAllyPickupItems = ALLY_PICKUP_NONPLAYER;
9018 			}
9019 			myStats->allyItemPickup = monsterAllyPickupItems;
9020 			serverUpdateEntitySkill(this, 44);
9021 			break;
9022 		case ALLY_CMD_GYRO_LIGHT_TOGGLE:
9023 			++monsterAllyClass;
9024 			if ( monsterAllyClass >= ALLY_GYRO_LIGHT_END )
9025 			{
9026 				monsterAllyClass = ALLY_GYRO_LIGHT_NONE;
9027 			}
9028 			myStats->allyClass = monsterAllyClass;
9029 			serverUpdateEntitySkill(this, 46);
9030 			break;
9031 		case ALLY_CMD_GYRO_DETECT_TOGGLE:
9032 		{
9033 			++monsterAllyPickupItems;
9034 			bool failQuality = false;
9035 			bool failSkillRequirement = false;
9036 			if ( monsterAllyPickupItems >= ALLY_GYRO_DETECT_END )
9037 			{
9038 				monsterAllyPickupItems = ALLY_GYRO_DETECT_NONE;
9039 			}
9040 			else if ( monsterAllyPickupItems == ALLY_GYRO_DETECT_ITEMS_VALUABLE )
9041 			{
9042 				if ( myStats->LVL < 15 )
9043 				{
9044 					failQuality = true;
9045 				}
9046 				if ( skillLVL < SKILL_LEVEL_MASTER )
9047 				{
9048 					failSkillRequirement = true;
9049 				}
9050 			}
9051 			else if ( monsterAllyPickupItems == ALLY_GYRO_DETECT_MONSTERS )
9052 			{
9053 				if ( myStats->LVL < 10 )
9054 				{
9055 					failQuality = true;
9056 				}
9057 				if ( skillLVL < SKILL_LEVEL_EXPERT )
9058 				{
9059 					failSkillRequirement = true;
9060 				}
9061 			}
9062 			else if ( monsterAllyPickupItems == ALLY_GYRO_DETECT_TRAPS
9063 				|| monsterAllyPickupItems == ALLY_GYRO_DETECT_EXITS )
9064 			{
9065 				if ( myStats->LVL < 5 )
9066 				{
9067 					failQuality = true;
9068 				}
9069 				if ( skillLVL < SKILL_LEVEL_SKILLED )
9070 				{
9071 					failSkillRequirement = true;
9072 				}
9073 			}
9074 			else if ( monsterAllyPickupItems == ALLY_GYRO_DETECT_ITEMS_METAL
9075 				|| monsterAllyPickupItems == ALLY_GYRO_DETECT_ITEMS_MAGIC )
9076 			{
9077 				if ( skillLVL < SKILL_LEVEL_BASIC )
9078 				{
9079 					failSkillRequirement = true;
9080 				}
9081 			}
9082 
9083 			if ( failSkillRequirement || failQuality )
9084 			{
9085 				if ( skillLVL < SKILL_LEVEL_BASIC )
9086 				{
9087 					messagePlayerColor(monsterAllyIndex, 0xFFFFFFFF, language[3680]);
9088 				}
9089 				else
9090 				{
9091 					messagePlayerColor(monsterAllyIndex, 0xFFFFFFFF, language[3679]);
9092 				}
9093 				monsterAllyPickupItems = ALLY_GYRO_DETECT_NONE;
9094 			}
9095 			myStats->allyItemPickup = monsterAllyPickupItems;
9096 			serverUpdateEntitySkill(this, 44);
9097 			break;
9098 		}
9099 		case ALLY_CMD_DROP_EQUIP:
9100 			if ( strcmp(myStats->name, "") && myStats->type == HUMAN )
9101 			{
9102 				// named humans refuse to drop equipment.
9103 				handleNPCInteractDialogue(*myStats, ALLY_EVENT_DROP_HUMAN_REFUSE);
9104 			}
9105 			else if ( myStats->type == GYROBOT )
9106 			{
9107 				bool droppedSomething = false;
9108 				node_t* nextnode = nullptr;
9109 				for ( node_t* node = myStats->inventory.first; node; node = nextnode )
9110 				{
9111 					nextnode = node->next;
9112 					Item* item = (Item*)node->element;
9113 					if ( item )
9114 					{
9115 						if ( item->type == TOOL_TELEPORT_BOMB || item->type == TOOL_FREEZE_BOMB
9116 							|| item->type == TOOL_BOMB || item->type == TOOL_SLEEP_BOMB )
9117 						{
9118 							int count = item->count;
9119 							this->monsterEquipItem(*item, &myStats->weapon);
9120 							this->attack(0, 0, nullptr);
9121 							if ( count > 1 )
9122 							{
9123 								myStats->weapon = nullptr;
9124 							}
9125 							droppedSomething = true;
9126 							break;
9127 						}
9128 						else
9129 						{
9130 							for ( int c = item->count; c > 0; --c )
9131 							{
9132 								Entity* dropped = dropItemMonster(item, this, myStats, item->count);
9133 								if ( dropped )
9134 								{
9135 									c = 0;
9136 									droppedSomething = true;
9137 								}
9138 							}
9139 						}
9140 					}
9141 				}
9142 
9143 				if ( !droppedSomething )
9144 				{
9145 					messagePlayer(monsterAllyIndex, language[3868]);
9146 				}
9147 			}
9148 			else if ( stats[monsterAllyIndex] )
9149 			{
9150 				Entity* dropped = nullptr;
9151 				bool confirmDropped = false;
9152 				bool dropWeaponOnly = false;
9153 				bool unableToDrop = false;
9154 				Uint32 owner = players[monsterAllyIndex]->entity->getUID();
9155 				if ( skillLVL >= SKILL_LEVEL_MASTER )
9156 				{
9157 					if ( myStats->helmet )
9158 					{
9159 						if ( myStats->helmet->canUnequip(myStats) )
9160 						{
9161 							dropped = dropItemMonster(myStats->helmet, this, myStats);
9162 						}
9163 						else
9164 						{
9165 							unableToDrop = true;
9166 						}
9167 					}
9168 					if ( dropped )
9169 					{
9170 						confirmDropped = true;
9171 						dropped->itemOriginalOwner = owner;
9172 					}
9173 					if ( myStats->breastplate )
9174 					{
9175 						if ( myStats->breastplate->canUnequip(myStats) )
9176 						{
9177 							dropped = dropItemMonster(myStats->breastplate, this, myStats);
9178 						}
9179 						else
9180 						{
9181 							unableToDrop = true;
9182 						}
9183 					}
9184 					if ( dropped )
9185 					{
9186 						confirmDropped = true;
9187 						dropped->itemOriginalOwner = owner;
9188 					}
9189 					if ( myStats->shoes )
9190 					{
9191 						if ( myStats->shoes->canUnequip(myStats) )
9192 						{
9193 							dropped = dropItemMonster(myStats->shoes, this, myStats);
9194 						}
9195 						else
9196 						{
9197 							unableToDrop = true;
9198 						}
9199 					}
9200 					if ( dropped )
9201 					{
9202 						confirmDropped = true;
9203 						dropped->itemOriginalOwner = owner;
9204 					}
9205 					if ( myStats->shield )
9206 					{
9207 						if ( myStats->shield->canUnequip(myStats) )
9208 						{
9209 							dropped = dropItemMonster(myStats->shield, this, myStats, myStats->shield->count);
9210 						}
9211 						else
9212 						{
9213 							unableToDrop = true;
9214 						}
9215 					}
9216 					if ( dropped )
9217 					{
9218 						confirmDropped = true;
9219 						dropped->itemOriginalOwner = owner;
9220 					}
9221 
9222 					if ( skillLVL >= SKILL_LEVEL_LEGENDARY )
9223 					{
9224 						if ( myStats->ring )
9225 						{
9226 							if ( myStats->ring->canUnequip(myStats) )
9227 							{
9228 								dropped = dropItemMonster(myStats->ring, this, myStats);
9229 							}
9230 							else
9231 							{
9232 								unableToDrop = true;
9233 							}
9234 						}
9235 						if ( dropped )
9236 						{
9237 							confirmDropped = true;
9238 							dropped->itemOriginalOwner = owner;
9239 						}
9240 						if ( myStats->amulet )
9241 						{
9242 							if ( myStats->amulet->canUnequip(myStats) )
9243 							{
9244 								dropped = dropItemMonster(myStats->amulet, this, myStats);
9245 							}
9246 							else
9247 							{
9248 								unableToDrop = true;
9249 							}
9250 						}
9251 						if ( dropped )
9252 						{
9253 							confirmDropped = true;
9254 							dropped->itemOriginalOwner = owner;
9255 						}
9256 						if ( myStats->cloak )
9257 						{
9258 							if ( myStats->cloak->canUnequip(myStats) )
9259 							{
9260 								dropped = dropItemMonster(myStats->cloak, this, myStats);
9261 							}
9262 							else
9263 							{
9264 								unableToDrop = true;
9265 							}
9266 						}
9267 						if ( dropped )
9268 						{
9269 							confirmDropped = true;
9270 							dropped->itemOriginalOwner = owner;
9271 						}
9272 						if ( confirmDropped )
9273 						{
9274 							handleNPCInteractDialogue(*myStats, ALLY_EVENT_DROP_ALL);
9275 						}
9276 					}
9277 					else
9278 					{
9279 						if ( confirmDropped )
9280 						{
9281 							handleNPCInteractDialogue(*myStats, ALLY_EVENT_DROP_EQUIP);
9282 						}
9283 					}
9284 				}
9285 				else
9286 				{
9287 					if ( myStats->weapon )
9288 					{
9289 						dropWeaponOnly = true;
9290 					}
9291 				}
9292 				if ( myStats->weapon )
9293 				{
9294 					if ( myStats->weapon->canUnequip(myStats) )
9295 					{
9296 						dropped = dropItemMonster(myStats->weapon, this, myStats, myStats->weapon->count);
9297 					}
9298 					else
9299 					{
9300 						unableToDrop = true;
9301 					}
9302 				}
9303 				if ( dropped )
9304 				{
9305 					dropped->itemOriginalOwner = owner;
9306 					if ( dropWeaponOnly )
9307 					{
9308 						handleNPCInteractDialogue(*myStats, ALLY_EVENT_DROP_WEAPON);
9309 					}
9310 				}
9311 				/*if ( unableToDrop )
9312 				{
9313 					handleNPCInteractDialogue(*myStats, ALLY_EVENT_DROP_FAILED);
9314 				}*/
9315 			}
9316 			break;
9317 		case ALLY_CMD_MOVETO_CONFIRM:
9318 		{
9319 			if ( myStats->type == SENTRYBOT || myStats->type == SPELLBOT )
9320 			{
9321 				real_t floatx = destX * 16 + 8;
9322 				real_t floaty = destY * 16 + 8;
9323 				double tangent = atan2(floaty - y, floatx - x);
9324 				monsterLookTime = 1;
9325 				monsterMoveTime = rand() % 10 + 1;
9326 				monsterLookDir = tangent;
9327 				monsterSentrybotLookDir = monsterLookDir;
9328 				if ( monsterAllyState != ALLY_STATE_DEFEND )
9329 				{
9330 					handleNPCInteractDialogue(*myStats, ALLY_EVENT_WAIT);
9331 				}
9332 				monsterAllyState = ALLY_STATE_DEFEND;
9333 			}
9334 			else if ( monsterSetPathToLocation(destX, destY, 1) )
9335 			{
9336 				monsterState = MONSTER_STATE_HUNT; // hunt state
9337 				monsterAllyState = ALLY_STATE_MOVETO;
9338 				serverUpdateEntitySkill(this, 0);
9339 				handleNPCInteractDialogue(*myStats, ALLY_EVENT_MOVETO_BEGIN);
9340 			}
9341 			else
9342 			{
9343 				//messagePlayer(0, "no path to destination");
9344 				handleNPCInteractDialogue(*myStats, ALLY_EVENT_MOVETO_FAIL);
9345 			}
9346 			break;
9347 		}
9348 		case ALLY_CMD_GYRO_RETURN:
9349 		{
9350 			if ( players[monsterAllyIndex]->entity )
9351 			{
9352 				destX = static_cast<int>(players[monsterAllyIndex]->entity->x) >> 4;
9353 				destY = static_cast<int>(players[monsterAllyIndex]->entity->y) >> 4;
9354 				if ( entityDist(this, players[monsterAllyIndex]->entity) < TOUCHRANGE * 2 )
9355 				{
9356 					monsterSpecialState = GYRO_RETURN_LANDING;
9357 					serverUpdateEntitySkill(this, 33); // for clients to keep track of animation
9358 					playSoundEntity(this, 449, 128);
9359 				}
9360 				else if ( monsterSetPathToLocation(destX, destY, 2) )
9361 				{
9362 					monsterState = MONSTER_STATE_HUNT; // hunt state
9363 					monsterAllyState = ALLY_STATE_MOVETO;
9364 					serverUpdateEntitySkill(this, 0);
9365 					handleNPCInteractDialogue(*myStats, ALLY_EVENT_MOVETO_BEGIN);
9366 					monsterSpecialState = GYRO_RETURN_PATHING;
9367 					serverUpdateEntitySkill(this, 33); // for clients to keep track of animation
9368 				}
9369 				else
9370 				{
9371 					//messagePlayer(0, "no path to destination");
9372 					handleNPCInteractDialogue(*myStats, ALLY_EVENT_MOVETO_FAIL);
9373 				}
9374 			}
9375 			break;
9376 		}
9377 		case ALLY_CMD_SPECIAL:
9378 		{
9379 			if ( monsterAllySpecialCooldown == 0 )
9380 			{
9381 				int duration = TICKS_PER_SECOND * (60);
9382 				if ( myStats->HP < myStats->MAXHP && setEffect(EFF_ASLEEP, true, duration, false) ) // 60 seconds of sleep.
9383 				{
9384 					setEffect(EFF_HP_REGEN, true, duration, false);
9385 					monsterAllySpecial = ALLY_SPECIAL_CMD_REST;
9386 					monsterAllySpecialCooldown = -1; // locked out until next floor.
9387 					serverUpdateEntitySkill(this, 49);
9388 					messagePlayerMonsterEvent(monsterAllyIndex, 0xFFFFFF, *myStats, language[398], language[397], MSG_COMBAT);
9389 					if ( players[monsterAllyIndex] && players[monsterAllyIndex]->entity
9390 						&& myStats->HP < myStats->MAXHP && rand() % 3 == 0 )
9391 					{
9392 						players[monsterAllyIndex]->entity->increaseSkill(PRO_LEADERSHIP);
9393 					}
9394 				}
9395 				else
9396 				{
9397 					messagePlayerMonsterEvent(monsterAllyIndex, 0xFFFFFF, *myStats, language[3880], language[3880], MSG_GENERIC);
9398 				}
9399 			}
9400 			break;
9401 		}
9402 		case ALLY_CMD_DUMMYBOT_RETURN:
9403 			if ( myStats->type == DUMMYBOT )
9404 			{
9405 				monsterSpecialState = DUMMYBOT_RETURN_FORM;
9406 				serverUpdateEntitySkill(this, 33);
9407 			}
9408 			else if ( myStats->type == SENTRYBOT || myStats->type == SPELLBOT )
9409 			{
9410 				monsterSpecialState = DUMMYBOT_RETURN_FORM;
9411 				serverUpdateEntitySkill(this, 33);
9412 				playSoundEntity(this, 469 + rand() % 3, 92);
9413 			}
9414 			break;
9415 		default:
9416 			break;
9417 	}
9418 	serverUpdateEntitySkill(this, 43);
9419 	//messagePlayer(0, "received: %d", command);
9420 }
9421 
monsterAllySetInteract()9422 bool Entity::monsterAllySetInteract()
9423 {
9424 	if ( multiplayer == CLIENT )
9425 	{
9426 		return false;
9427 	}
9428 	if ( monsterAllyInteractTarget == 0 )
9429 	{
9430 		return false;
9431 	}
9432 	Entity* target = uidToEntity(monsterAllyInteractTarget);
9433 	if ( !target )
9434 	{
9435 		monsterAllyState = ALLY_STATE_DEFAULT;
9436 		monsterAllyInteractTarget = 0;
9437 		return false;
9438 	}
9439 	// check distance to interactable.
9440 	double range = pow(y - target->y, 2) + pow(x - target->x, 2);
9441 	if ( range < 576 ) // 24 squared
9442 	{
9443 		if ( getMonsterTypeFromSprite() == GYROBOT
9444 			&& monsterSpecialState != GYRO_INTERACT_LANDING
9445 			&& z < -0.1 )
9446 		{
9447 			// don't set interact yet.
9448 			return true;
9449 		}
9450 		else
9451 		{
9452 			FollowerMenu.entityToInteractWith = target; // set followerInteractedEntity to the mechanism/item/gold etc.
9453 			FollowerMenu.entityToInteractWith->interactedByMonster = getUID(); // set the remote entity to this monster's uid to lookup later.
9454 		}
9455 	}
9456 	else
9457 	{
9458 		return false;
9459 	}
9460 
9461 	return true;
9462 }
9463 
isInteractWithMonster()9464 bool Entity::isInteractWithMonster()
9465 {
9466 	if ( FollowerMenu.entityToInteractWith == nullptr )
9467 	{
9468 		return false; // entity is not set to interact with any monster.
9469 	}
9470 	if ( FollowerMenu.entityToInteractWith->interactedByMonster == 0 )
9471 	{
9472 		return false; // recent monster is not set to interact.
9473 	}
9474 	if ( FollowerMenu.entityToInteractWith == this )
9475 	{
9476 		return true; // a monster is set to interact with myself.
9477 	}
9478 	return false;
9479 }
9480 
clearMonsterInteract()9481 void Entity::clearMonsterInteract()
9482 {
9483 	FollowerMenu.entityToInteractWith = nullptr;
9484 	interactedByMonster = 0;
9485 }
9486 
monsterSetPathToLocation(int destX,int destY,int adjacentTilesToCheck,bool tryRandomSpot)9487 bool Entity::monsterSetPathToLocation(int destX, int destY, int adjacentTilesToCheck, bool tryRandomSpot)
9488 {
9489 	int u, v;
9490 	bool foundplace = false;
9491 	int pathToX = destX;
9492 	int pathToY = destY;
9493 
9494 	if ( static_cast<int>(x / 16) == destX && static_cast<int>(y / 16) == destY )
9495 	{
9496 		return true; // we're trying to move to the spot we're already at!
9497 	}
9498 	else if ( !checkObstacle((destX << 4) + 8, (destY << 4) + 8, this, nullptr) )
9499 	{
9500 		if ( !tryRandomSpot )
9501 		{
9502 			foundplace = true; // we can path directly to the destination specified.
9503 		}
9504 	}
9505 
9506 	std::vector<std::pair<int, std::pair<int, int>>> possibleDestinations; // store distance and the x, y coordinates in each element.
9507 
9508 	if ( !foundplace )
9509 	{
9510 		for ( u = destX - adjacentTilesToCheck; u <= destX + adjacentTilesToCheck; u++ )
9511 		{
9512 			for ( v = destY - adjacentTilesToCheck; v <= destY + adjacentTilesToCheck; v++ )
9513 			{
9514 				if ( static_cast<int>(x / 16) == u && static_cast<int>(y / 16) == v )
9515 				{
9516 					// we're trying to move to the spot we're already at!
9517 				}
9518 				else if ( !checkObstacle((u << 4) + 8, (v << 4) + 8, this, nullptr) )
9519 				{
9520 					int distance = pow(x / 16 - u, 2) + pow(y / 16 - v, 2);
9521 					possibleDestinations.push_back(std::make_pair(distance, std::make_pair(u, v)));
9522 				}
9523 			}
9524 		}
9525 	}
9526 
9527 	if ( !possibleDestinations.empty() )
9528 	{
9529 		// sort by distance from monster, first result is shortest path.
9530 		std::sort(possibleDestinations.begin(), possibleDestinations.end());
9531 		pathToX = possibleDestinations.at(0).second.first;
9532 		pathToY = possibleDestinations.at(0).second.second;
9533 		foundplace = true;
9534 	}
9535 
9536 	path = generatePath(static_cast<int>(floor(x / 16)), static_cast<int>(floor(y / 16)), pathToX, pathToY, this, nullptr);
9537 	if ( children.first != NULL )
9538 	{
9539 		list_RemoveNode(children.first);
9540 	}
9541 	node_t* node = list_AddNodeFirst(&children);
9542 	node->element = path;
9543 	node->deconstructor = &listDeconstructor;
9544 
9545 	if ( path == nullptr || !foundplace )
9546 	{
9547 		return false;
9548 	}
9549 	return true;
9550 }
9551 
handleNPCInteractDialogue(Stat & myStats,AllyNPCChatter event)9552 void Entity::handleNPCInteractDialogue(Stat& myStats, AllyNPCChatter event)
9553 {
9554 	if ( multiplayer == CLIENT )
9555 	{
9556 		return;
9557 	}
9558 	if ( !isMobile() )
9559 	{
9560 		return;
9561 	}
9562 	if ( monsterAllyIndex < 0 || monsterAllyIndex >= MAXPLAYERS )
9563 	{
9564 		return;
9565 	}
9566 	if ( !stats[monsterAllyIndex] )
9567 	{
9568 		return;
9569 	}
9570 
9571 	char namesays[32];
9572 	if ( !strcmp(myStats.name, "") )
9573 	{
9574 		if ( myStats.type < KOBOLD ) //Original monster count
9575 		{
9576 			snprintf(namesays, 31, language[513], language[90 + myStats.type]); // The %s says
9577 		}
9578 		else if ( myStats.type >= KOBOLD ) //New monsters
9579 		{
9580 			snprintf(namesays, 31, language[513], language[2000 + myStats.type - KOBOLD]); // The %s says
9581 		}
9582 	}
9583 	else
9584 	{
9585 		snprintf(namesays, 31, language[1302], myStats.name); // %s says
9586 	}
9587 
9588 	std::string message;
9589 
9590 	if ( myStats.type == HUMAN )
9591 	{
9592 		switch ( event )
9593 		{
9594 			case ALLY_EVENT_MOVEASIDE:
9595 				message = language[535];
9596 				break;
9597 			case ALLY_EVENT_MOVETO_BEGIN:
9598 				if ( rand() % 10 == 0 )
9599 				{
9600 					message = language[3079 + rand() % 2];
9601 				}
9602 				break;
9603 			case ALLY_EVENT_MOVETO_FAIL:
9604 				message = language[3077 + rand() % 2];
9605 				break;
9606 			case ALLY_EVENT_INTERACT_ITEM_CURSED:
9607 				if ( FollowerMenu.entityToInteractWith && FollowerMenu.entityToInteractWith->behavior == &actItem )
9608 				{
9609 					Item* item = newItemFromEntity(FollowerMenu.entityToInteractWith);
9610 					if ( item )
9611 					{
9612 						char fullmsg[256] = "";
9613 						switch ( itemCategory(item) )
9614 						{
9615 							case WEAPON:
9616 							case MAGICSTAFF:
9617 								snprintf(fullmsg, 63, language[3071], namesays, language[3107]);
9618 								break;
9619 							case ARMOR:
9620 							case TOOL:
9621 								switch ( checkEquipType(item) )
9622 								{
9623 									case TYPE_OFFHAND:
9624 										snprintf(fullmsg, 63, language[3071], namesays, language[3112]);
9625 										break;
9626 									case TYPE_HELM:
9627 									case TYPE_HAT:
9628 									case TYPE_BREASTPIECE:
9629 									case TYPE_BOOTS:
9630 									case TYPE_SHIELD:
9631 									case TYPE_GLOVES:
9632 									case TYPE_CLOAK:
9633 										snprintf(fullmsg, 63, language[3071], namesays, language[3107 + checkEquipType(item)]);
9634 										break;
9635 									default:
9636 										snprintf(fullmsg, 63, language[3071], namesays, language[3117]);
9637 										break;
9638 								}
9639 								break;
9640 							case RING:
9641 								snprintf(fullmsg, 63, language[3071], namesays, language[3107 + TYPE_RING]);
9642 								break;
9643 							case AMULET:
9644 								snprintf(fullmsg, 63, language[3071], namesays, language[3107 + TYPE_AMULET]);
9645 								break;
9646 							default:
9647 								break;
9648 						}
9649 						if ( strcmp(fullmsg, "") )
9650 						{
9651 							messagePlayer(monsterAllyIndex, fullmsg);
9652 						}
9653 					}
9654 					if ( item )
9655 					{
9656 						free(item);
9657 					}
9658 				}
9659 				return;
9660 				break;
9661 			case ALLY_EVENT_INTERACT_ITEM_NOUSE:
9662 				message = language[3074];
9663 				break;
9664 			case ALLY_EVENT_INTERACT_ITEM_FOOD_BAD:
9665 				message = language[3088];
9666 				break;
9667 			case ALLY_EVENT_INTERACT_ITEM_FOOD_GOOD:
9668 				if ( myStats.HUNGER > 800 )
9669 				{
9670 					message = language[3076];
9671 				}
9672 				else
9673 				{
9674 					message = language[3075];
9675 				}
9676 				break;
9677 			case ALLY_EVENT_INTERACT_ITEM_FOOD_ROTTEN:
9678 				message = language[3091];
9679 				break;
9680 			case ALLY_EVENT_INTERACT_ITEM_FOOD_FULL:
9681 				message = language[3089 + rand() % 2];
9682 				break;
9683 			case ALLY_EVENT_INTERACT_OTHER:
9684 				break;
9685 			case ALLY_EVENT_ATTACK:
9686 			case ALLY_EVENT_SPOT_ENEMY:
9687 				message = language[516 + rand() % 3];
9688 				break;
9689 			case ALLY_EVENT_ATTACK_FRIENDLY_FIRE:
9690 				message = language[3084 + rand() % 2];
9691 				break;
9692 			case ALLY_EVENT_DROP_HUMAN_REFUSE:
9693 				message = language[3135];
9694 				break;
9695 			case ALLY_EVENT_DROP_WEAPON:
9696 			case ALLY_EVENT_DROP_EQUIP:
9697 			case ALLY_EVENT_DROP_ALL:
9698 				if ( rand() % 2 )
9699 				{
9700 					if ( rand() % 2 && event == ALLY_EVENT_DROP_ALL )
9701 					{
9702 						message = language[3083];
9703 					}
9704 					else
9705 					{
9706 						message = language[3072 + rand() % 2];
9707 					}
9708 				}
9709 				else
9710 				{
9711 					message = language[3081 + rand() % 2];
9712 				}
9713 				break;
9714 			case ALLY_EVENT_WAIT:
9715 				message = language[3069 + rand() % 2];
9716 				break;
9717 			case ALLY_EVENT_FOLLOW:
9718 				message = language[526 + rand() % 3];
9719 				break;
9720 			case ALLY_EVENT_MOVETO_REPATH:
9721 				if ( rand() % 20 == 0 )
9722 				{
9723 					message = language[3086 + rand() % 2];
9724 				}
9725 				break;
9726 			default:
9727 				break;
9728 		}
9729 	}
9730 	else
9731 	{
9732 		char genericStr[128] = "";
9733 		char namedStr[128] = "";
9734 
9735 		switch ( event )
9736 		{
9737 			case ALLY_EVENT_MOVEASIDE:
9738 				messagePlayerMonsterEvent(monsterAllyIndex, 0xFFFFFFFF,
9739 					myStats, language[3129], language[3130], MSG_COMBAT);
9740 				break;
9741 			case ALLY_EVENT_MOVETO_BEGIN:
9742 				/*if ( rand() % 10 == 0 )
9743 				{
9744 					message = language[3079 + rand() % 2];
9745 				}*/
9746 				break;
9747 			case ALLY_EVENT_MOVETO_FAIL:
9748 				if ( rand() % 2 == 0 )
9749 				{
9750 					messagePlayerMonsterEvent(monsterAllyIndex, 0xFFFFFFFF,
9751 						myStats, language[3131], language[3132], MSG_COMBAT);
9752 				}
9753 				else
9754 				{
9755 					messagePlayerMonsterEvent(monsterAllyIndex, 0xFFFFFFFF,
9756 						myStats, language[3133], language[3134], MSG_COMBAT);
9757 				}
9758 				break;
9759 			case ALLY_EVENT_INTERACT_ITEM_CURSED:
9760 				if ( FollowerMenu.entityToInteractWith && FollowerMenu.entityToInteractWith->behavior == &actItem )
9761 				{
9762 					Item* item = newItemFromEntity(FollowerMenu.entityToInteractWith);
9763 					if ( item )
9764 					{
9765 						char fullmsg[256] = "";
9766 						switch ( itemCategory(item) )
9767 						{
9768 							case WEAPON:
9769 							case MAGICSTAFF:
9770 								snprintf(fullmsg, 63, language[3118], language[3107]);
9771 								break;
9772 							case ARMOR:
9773 							case TOOL:
9774 								switch ( checkEquipType(item) )
9775 								{
9776 									case TYPE_OFFHAND:
9777 										snprintf(fullmsg, 63, language[3118], language[3112]);
9778 										break;
9779 									case TYPE_HELM:
9780 									case TYPE_HAT:
9781 									case TYPE_BREASTPIECE:
9782 									case TYPE_BOOTS:
9783 									case TYPE_SHIELD:
9784 									case TYPE_GLOVES:
9785 									case TYPE_CLOAK:
9786 										snprintf(fullmsg, 63, language[3118], language[3107 + checkEquipType(item)]);
9787 										break;
9788 									default:
9789 										snprintf(fullmsg, 63, language[3118], language[3117]);
9790 										break;
9791 								}
9792 								break;
9793 							case RING:
9794 								snprintf(fullmsg, 63, language[3118], language[3107 + TYPE_RING]);
9795 								break;
9796 							case AMULET:
9797 								snprintf(fullmsg, 63, language[3118], language[3107 + TYPE_AMULET]);
9798 								break;
9799 							default:
9800 								break;
9801 						}
9802 						if ( strcmp(fullmsg, "") )
9803 						{
9804 							char genericStr[128];
9805 							strcpy(genericStr, language[3119]);
9806 							strcat(genericStr, fullmsg);
9807 							char namedStr[128];
9808 							strcpy(namedStr, language[3120]);
9809 							strcat(namedStr, fullmsg);
9810 							messagePlayerMonsterEvent(monsterAllyIndex, 0xFFFFFFFF,
9811 								myStats, genericStr, namedStr, MSG_COMBAT);
9812 						}
9813 					}
9814 					if ( item )
9815 					{
9816 						free(item);
9817 					}
9818 				}
9819 				break;
9820 			case ALLY_EVENT_INTERACT_ITEM_NOUSE:
9821 			case ALLY_EVENT_INTERACT_ITEM_FOOD_FULL:
9822 			{
9823 				Item* item = newItemFromEntity(FollowerMenu.entityToInteractWith);
9824 				if ( item )
9825 				{
9826 					char itemString[64] = "";
9827 					snprintf(itemString, 63, language[3123], items[item->type].name_unidentified);
9828 					strcpy(genericStr, language[3121]);
9829 					strcat(genericStr, itemString);
9830 					strcpy(namedStr, language[3122]);
9831 					strcat(namedStr, itemString);
9832 					messagePlayerMonsterEvent(monsterAllyIndex, 0xFFFFFFFF,
9833 						myStats, genericStr, namedStr, MSG_COMBAT);
9834 					free(item);
9835 				}
9836 				break;
9837 			}
9838 			case ALLY_EVENT_INTERACT_ITEM_FOOD_BAD:
9839 			case ALLY_EVENT_INTERACT_ITEM_FOOD_GOOD:
9840 			case ALLY_EVENT_INTERACT_ITEM_FOOD_ROTTEN:
9841 			{
9842 				Item* item = newItemFromEntity(FollowerMenu.entityToInteractWith);
9843 				if ( item )
9844 				{
9845 					char itemString[64] = "";
9846 					snprintf(itemString, 63, language[3124], items[item->type].name_unidentified);
9847 					strcpy(genericStr, language[3121]);
9848 					strcat(genericStr, itemString);
9849 					strcpy(namedStr, language[3122]);
9850 					strcat(namedStr, itemString);
9851 					messagePlayerMonsterEvent(monsterAllyIndex, 0xFFFFFFFF,
9852 						myStats, genericStr, namedStr, MSG_COMBAT);
9853 					free(item);
9854 				}
9855 				break;
9856 			}
9857 			case ALLY_EVENT_INTERACT_OTHER:
9858 				break;
9859 			case ALLY_EVENT_ATTACK:
9860 			case ALLY_EVENT_SPOT_ENEMY:
9861 				//message = language[516 + rand() % 3];
9862 				break;
9863 			case ALLY_EVENT_ATTACK_FRIENDLY_FIRE:
9864 				//message = language[3084 + rand() % 2];
9865 				break;
9866 			case ALLY_EVENT_DROP_WEAPON:
9867 				strcpy(genericStr, language[3121]);
9868 				strcat(genericStr, language[3125]);
9869 				strcpy(namedStr, language[3122]);
9870 				strcat(namedStr, language[3125]);
9871 				messagePlayerMonsterEvent(monsterAllyIndex, 0xFFFFFFFF,
9872 					myStats, genericStr, namedStr, MSG_COMBAT);
9873 				break;
9874 			case ALLY_EVENT_DROP_EQUIP:
9875 				strcpy(genericStr, language[3121]);
9876 				strcat(genericStr, language[3126]);
9877 				strcpy(namedStr, language[3122]);
9878 				strcat(namedStr, language[3126]);
9879 				messagePlayerMonsterEvent(monsterAllyIndex, 0xFFFFFFFF,
9880 					myStats, genericStr, namedStr, MSG_COMBAT);
9881 				break;
9882 			case ALLY_EVENT_DROP_ALL:
9883 				strcpy(genericStr, language[3121]);
9884 				strcat(genericStr, language[3127]);
9885 				strcpy(namedStr, language[3122]);
9886 				strcat(namedStr, language[3127]);
9887 				messagePlayerMonsterEvent(monsterAllyIndex, 0xFFFFFFFF,
9888 					myStats, genericStr, namedStr, MSG_COMBAT);
9889 				break;
9890 			case ALLY_EVENT_WAIT:
9891 				if ( myStats.type == SENTRYBOT || myStats.type == SPELLBOT )
9892 				{
9893 					messagePlayerColor(monsterAllyIndex, 0xFFFFFFFF, language[3676], monstertypename[myStats.type]);
9894 				}
9895 				else
9896 				{
9897 					messagePlayerMonsterEvent(monsterAllyIndex, 0xFFFFFFFF,
9898 						myStats, language[3105], language[3106], MSG_COMBAT);
9899 				}
9900 				break;
9901 			case ALLY_EVENT_FOLLOW:
9902 				if ( myStats.type == SENTRYBOT || myStats.type == SPELLBOT )
9903 				{
9904 					messagePlayerColor(monsterAllyIndex, 0xFFFFFFFF, language[3677], monstertypename[myStats.type]);
9905 				}
9906 				else
9907 				{
9908 					messagePlayerMonsterEvent(monsterAllyIndex, 0xFFFFFFFF,
9909 						myStats, language[529], language[3128], MSG_COMBAT);
9910 				}
9911 				break;
9912 			case ALLY_EVENT_MOVETO_REPATH:
9913 				/*if ( rand() % 20 == 0 )
9914 				{
9915 					message = language[3086 + rand() % 2];
9916 				}*/
9917 				break;
9918 			default:
9919 				break;
9920 		}
9921 		return;
9922 	}
9923 	char fullmsg[256] = "";
9924 	strcpy(fullmsg, message.c_str());
9925 	if ( strcmp(fullmsg, "") )
9926 	{
9927 		messagePlayer(monsterAllyIndex, fullmsg, namesays, stats[monsterAllyIndex]->name);
9928 	}
9929 }
9930 
shouldMonsterDefend(Stat & myStats,const Entity & target,const Stat & targetStats,int targetDist,bool hasrangedweapon)9931 int Entity::shouldMonsterDefend(Stat& myStats, const Entity& target, const Stat& targetStats, int targetDist, bool hasrangedweapon)
9932 {
9933 	if ( behavior != &actMonster )
9934 	{
9935 		return MONSTER_DEFEND_NONE;
9936 	}
9937 
9938 	if ( !myStats.shield )
9939 	{
9940 		return MONSTER_DEFEND_NONE;
9941 	}
9942 
9943 	if ( itemTypeIsQuiver(myStats.shield->type) )
9944 	{
9945 		return MONSTER_DEFEND_NONE;
9946 	}
9947 
9948 	if ( monsterSpecialState > 0 )
9949 	{
9950 		return MONSTER_DEFEND_NONE;
9951 	}
9952 
9953 	if ( myStats.type == LICH
9954 		|| myStats.type == DEVIL
9955 		|| myStats.type == LICH_ICE
9956 		|| myStats.type == LICH_FIRE
9957 		|| myStats.type == SHOPKEEPER
9958 		)
9959 	{
9960 		return MONSTER_DEFEND_NONE;
9961 	}
9962 
9963 	bool isPlayerAlly = (monsterAllyIndex >= 0 && monsterAllyIndex < MAXPLAYERS);
9964 
9965 	if ( !(isPlayerAlly || myStats.type == HUMAN) )
9966 	{
9967 		return MONSTER_DEFEND_NONE;
9968 	}
9969 
9970 	int blockChance = 2; // 10%
9971 	bool targetHasRangedWeapon = target.hasRangedWeapon();
9972 
9973 	if ( isPlayerAlly )
9974 	{
9975 		if ( stats[monsterAllyIndex] && players[monsterAllyIndex] && players[monsterAllyIndex]->entity )
9976 		{
9977 			int leaderSkill = std::max(players[monsterAllyIndex]->entity->getCHR(), 0) + stats[monsterAllyIndex]->PROFICIENCIES[PRO_LEADERSHIP];
9978 			blockChance += std::max(0, (leaderSkill / 20) * 2); // 0-25% bonus to blockchance.
9979 		}
9980 	}
9981 
9982 	if ( myStats.PROFICIENCIES[PRO_SHIELD] > 0 )
9983 	{
9984 		blockChance += std::min(myStats.PROFICIENCIES[PRO_SHIELD] / 10, 5); // 0-25% bonus to blockchance.
9985 	}
9986 
9987 	bool retreatBonus = false;
9988 	if ( backupWithRangedWeapon(myStats, targetDist, hasrangedweapon) )
9989 	{
9990 		if ( targetHasRangedWeapon )
9991 		{
9992 			retreatBonus = true;
9993 		}
9994 		else if ( !targetHasRangedWeapon && targetDist < TOUCHRANGE )
9995 		{
9996 			retreatBonus = true;
9997 		}
9998 	}
9999 	if ( shouldRetreat(myStats) )
10000 	{
10001 		retreatBonus = true;
10002 	}
10003 
10004 	if ( retreatBonus )
10005 	{
10006 		blockChance += 2; // 10% bonus to blockchance.
10007 	}
10008 
10009 	blockChance = std::min(blockChance, 12); // 60% block hard cap.
10010 
10011 	if ( targetHasRangedWeapon && targetStats.weapon )
10012 	{
10013 		if ( itemCategory(targetStats.weapon) == MAGICSTAFF )
10014 		{
10015 			if ( myStats.shield->type != MIRROR_SHIELD )
10016 			{
10017 				// no point defending!
10018 				blockChance = 0;
10019 			}
10020 		}
10021 	}
10022 
10023 	if ( rand() % 20 < blockChance )
10024 	{
10025 		if ( isPlayerAlly || myStats.type == HUMAN )
10026 		{
10027 			if ( rand() % 4 == 0 )
10028 			{
10029 				return MONSTER_DEFEND_HOLD;
10030 			}
10031 			else
10032 			{
10033 				return MONSTER_DEFEND_ALLY;
10034 			}
10035 		}
10036 		else
10037 		{
10038 			return MONSTER_DEFEND_HOLD;
10039 		}
10040 	}
10041 
10042 	return false;
10043 }
10044 
monsterConsumeFoodEntity(Entity * food,Stat * myStats)10045 bool Entity::monsterConsumeFoodEntity(Entity* food, Stat* myStats)
10046 {
10047 	if ( !myStats )
10048 	{
10049 		return false;
10050 	}
10051 
10052 	if ( !food )
10053 	{
10054 		return false;
10055 	}
10056 
10057 	Item* item = newItemFromEntity(food);
10058 	if ( !item || item->type == FOOD_TIN )
10059 	{
10060 		return false;
10061 	}
10062 
10063 	if ( !FollowerMenu.allowedInteractFood(myStats->type) )
10064 	{
10065 		handleNPCInteractDialogue(*myStats, ALLY_EVENT_INTERACT_ITEM_NOUSE);
10066 		return false;
10067 	}
10068 
10069 	if ( myStats->HUNGER >= 800 )
10070 	{
10071 		handleNPCInteractDialogue(*myStats, ALLY_EVENT_INTERACT_ITEM_FOOD_FULL);
10072 		return false;
10073 	}
10074 
10075 	int buffDuration = item->status * TICKS_PER_SECOND * 2; // (2 - 8 seconds)
10076 	bool puking = false;
10077 	int pukeChance = 100;
10078 	// chance of rottenness
10079 	if ( myStats->type == HUMAN )
10080 	{
10081 		switch ( item->status )
10082 		{
10083 			case EXCELLENT:
10084 				pukeChance = 100;
10085 				break;
10086 			case SERVICABLE:
10087 				pukeChance = 25;
10088 				break;
10089 			case WORN:
10090 				pukeChance = 10;
10091 				break;
10092 			case DECREPIT:
10093 				pukeChance = 4;
10094 				break;
10095 			default:
10096 				pukeChance = 100;
10097 				break;
10098 		}
10099 	}
10100 
10101 	if ( rand() % pukeChance == 0 && pukeChance < 100 )
10102 	{
10103 		buffDuration = 0;
10104 		this->char_gonnavomit = 40 + rand() % 10;
10105 		puking = true;
10106 	}
10107 	else
10108 	{
10109 		if ( item->beatitude >= 0 )
10110 		{
10111 			if ( item->status > WORN )
10112 			{
10113 				buffDuration -= rand() % ((buffDuration / 2) + 1); // 50-100% duration
10114 			}
10115 			else
10116 			{
10117 				buffDuration -= rand() % ((buffDuration / 4) + 1); // 75-100% duration
10118 			}
10119 		}
10120 		else
10121 		{
10122 			buffDuration = 0;
10123 		}
10124 	}
10125 
10126 	if ( puking )
10127 	{
10128 		handleNPCInteractDialogue(*myStats, ALLY_EVENT_INTERACT_ITEM_FOOD_ROTTEN);
10129 	}
10130 	else if ( item->status <= WORN )
10131 	{
10132 		handleNPCInteractDialogue(*myStats, ALLY_EVENT_INTERACT_ITEM_FOOD_BAD);
10133 	}
10134 	else
10135 	{
10136 		handleNPCInteractDialogue(*myStats, ALLY_EVENT_INTERACT_ITEM_FOOD_GOOD);
10137 	}
10138 
10139 	int heal = 0;
10140 	switch ( item->type )
10141 	{
10142 		case FOOD_APPLE:
10143 			heal = 5 + item->beatitude;
10144 			buffDuration = std::min(buffDuration, 2 * TICKS_PER_SECOND);
10145 			myStats->HUNGER += 200;
10146 			break;
10147 		case FOOD_BREAD:
10148 			heal = 7 + item->beatitude;
10149 			buffDuration = std::min(buffDuration, 6 * TICKS_PER_SECOND);
10150 			myStats->HUNGER += 400;
10151 			break;
10152 		case FOOD_CHEESE:
10153 			heal = 3 + item->beatitude;
10154 			buffDuration = std::min(buffDuration, 2 * TICKS_PER_SECOND);
10155 			myStats->HUNGER += 100;
10156 			break;
10157 		case FOOD_CREAMPIE:
10158 			heal = 7 + item->beatitude;
10159 			buffDuration = std::min(buffDuration, 6 * TICKS_PER_SECOND);
10160 			myStats->HUNGER += 200;
10161 			break;
10162 		case FOOD_FISH:
10163 			heal = 7 + item->beatitude;
10164 			myStats->HUNGER += 500;
10165 			break;
10166 		case FOOD_MEAT:
10167 			heal = 9 + item->beatitude;
10168 			myStats->HUNGER += 600;
10169 			break;
10170 		case FOOD_TOMALLEY:
10171 			heal = 7 + item->beatitude;
10172 			buffDuration = std::min(buffDuration, 4 * TICKS_PER_SECOND);
10173 			myStats->HUNGER += 400;
10174 			break;
10175 		default:
10176 			free(item);
10177 			handleNPCInteractDialogue(*myStats, ALLY_EVENT_INTERACT_ITEM_NOUSE);
10178 			return false;
10179 			break;
10180 	}
10181 
10182 	myStats->HUNGER = std::min(myStats->HUNGER, 1500); // range checking max of 1500 hunger points.
10183 
10184 	Entity* leader = monsterAllyGetPlayerLeader();
10185 	if ( puking )
10186 	{
10187 		heal -= 5;
10188 		if ( rand() % 4 == 0 )
10189 		{
10190 			// angry at owner.
10191 			if ( leader )
10192 			{
10193 				//monsterAcquireAttackTarget(*leader, MONSTER_STATE_ATTACK);
10194 			}
10195 		}
10196 	}
10197 	this->modHP(heal);
10198 
10199 	if ( !puking && leader && rand() % 2 == 0 )
10200 	{
10201 		leader->increaseSkill(PRO_LEADERSHIP);
10202 	}
10203 
10204 	if ( buffDuration > 0 )
10205 	{
10206 		myStats->EFFECTS[EFF_HP_REGEN] = true;
10207 		myStats->EFFECTS_TIMERS[EFF_HP_REGEN] = buffDuration;
10208 	}
10209 
10210 	bool foodEntityConsumed = false;
10211 
10212 	if ( item->count > 1 )
10213 	{
10214 		--food->skill[13]; // update the entity on ground item count.
10215 	}
10216 	else
10217 	{
10218 		food->removeLightField();
10219 		list_RemoveNode(food->mynode);
10220 		foodEntityConsumed = true;
10221 	}
10222 	free(item);
10223 
10224 	// eating sound
10225 	playSoundEntity(this, 50 + rand() % 2, 64);
10226 
10227 	return foodEntityConsumed;
10228 }
10229 
monsterAllyGetPlayerLeader()10230 Entity* Entity::monsterAllyGetPlayerLeader()
10231 {
10232 	if ( behavior != &actMonster )
10233 	{
10234 		return nullptr;
10235 	}
10236 	if ( monsterAllyIndex >= 0 && monsterAllyIndex < MAXPLAYERS )
10237 	{
10238 		if ( players[monsterAllyIndex] )
10239 		{
10240 			return players[monsterAllyIndex]->entity;
10241 		}
10242 	}
10243 	return nullptr;
10244 }
10245 
monsterAllyEquipmentInClass(const Item & item) const10246 bool Entity::monsterAllyEquipmentInClass(const Item& item) const
10247 {
10248 	Stat* myStats = getStats();
10249 	if ( !myStats )
10250 	{
10251 		return false;
10252 	}
10253 
10254 	if ( monsterAllyIndex >= 0 && monsterAllyIndex < MAXPLAYERS )
10255 	{
10256 		// player ally.
10257 		bool hats = true;
10258 		bool helm = true;
10259 		bool gloves = false;
10260 		bool breastplate = true;
10261 		if ( myStats->type == HUMAN || myStats->type == VAMPIRE )
10262 		{
10263 			gloves = true;
10264 		}
10265 		if ( myStats->type == GOATMAN || myStats->type == GNOME || myStats->type == INCUBUS
10266 			|| myStats->type == SUCCUBUS || myStats->type == INSECTOID || myStats->type == VAMPIRE
10267 			|| myStats->type == KOBOLD )
10268 		{
10269 			hats = false;
10270 			helm = false;
10271 		}
10272 		if ( myStats->type == VAMPIRE )
10273 		{
10274 			breastplate = false;
10275 		}
10276 		if ( myStats->type == AUTOMATON )
10277 		{
10278 			hats = false;
10279 		}
10280 
10281 		if ( item.interactNPCUid == getUID() )
10282 		{
10283 			// monster was set to interact with this item, force want it.
10284 			switch ( itemCategory(&item) )
10285 			{
10286 				case ARMOR:
10287 					if ( checkEquipType(&item) == TYPE_HAT )
10288 					{
10289 						if ( myStats->type == KOBOLD && item.type == HAT_HOOD )
10290 						{
10291 							return true;
10292 						}
10293 						return hats;
10294 					}
10295 					else if ( checkEquipType(&item) == TYPE_HELM )
10296 					{
10297 						return helm;
10298 					}
10299 					else if ( checkEquipType(&item) == TYPE_BREASTPIECE )
10300 					{
10301 						return breastplate;
10302 					}
10303 					else if ( checkEquipType(&item) == TYPE_GLOVES )
10304 					{
10305 						return gloves;
10306 					}
10307 					return true;
10308 					break;
10309 				case WEAPON:
10310 				case RING:
10311 				case AMULET:
10312 				case MAGICSTAFF:
10313 				case CLOAK:
10314 					return true;
10315 					break;
10316 				case TOOL:
10317 					if ( item.type == TOOL_TORCH || item.type == TOOL_LANTERN || item.type == TOOL_CRYSTALSHARD )
10318 					{
10319 						return true;
10320 					}
10321 					else if ( itemTypeIsQuiver(item.type) )
10322 					{
10323 						return true;
10324 					}
10325 					else
10326 					{
10327 						return false;
10328 					}
10329 					break;
10330 				default:
10331 					return false;
10332 					break;
10333 			}
10334 		}
10335 		else if ( monsterAllyClass == ALLY_CLASS_MIXED )
10336 		{
10337 			// pick up all default items.
10338 		}
10339 		else if ( monsterAllyClass == ALLY_CLASS_RANGED )
10340 		{
10341 			if ( itemTypeIsQuiver(item.type) )
10342 			{
10343 				return true;
10344 			}
10345 			switch ( itemCategory(&item) )
10346 			{
10347 				case WEAPON:
10348 					return isRangedWeapon(item);
10349 				case ARMOR:
10350 					switch ( item.type )
10351 					{
10352 						case CRYSTAL_BREASTPIECE:
10353 						case CRYSTAL_HELM:
10354 						case CRYSTAL_SHIELD:
10355 						case STEEL_BREASTPIECE:
10356 						case STEEL_HELM:
10357 						case STEEL_SHIELD:
10358 						case IRON_BREASTPIECE:
10359 						case IRON_HELM:
10360 						case IRON_SHIELD:
10361 							return false;
10362 							break;
10363 						default:
10364 							break;
10365 					}
10366 					if ( checkEquipType(&item) == TYPE_HAT )
10367 					{
10368 						return hats;
10369 					}
10370 					else if ( checkEquipType(&item) == TYPE_HELM )
10371 					{
10372 						return helm;
10373 					}
10374 					break;
10375 				case MAGICSTAFF:
10376 					return false;
10377 					break;
10378 				case THROWN:
10379 					if ( myStats->type == GOATMAN )
10380 					{
10381 						return true;
10382 					}
10383 					return false;
10384 					break;
10385 				case POTION:
10386 					if ( myStats->type == GOATMAN )
10387 					{
10388 						switch ( item.type )
10389 						{
10390 							case POTION_BOOZE:
10391 								return true;
10392 							case POTION_HEALING:
10393 								return true;
10394 							default:
10395 								return false;
10396 						}
10397 					}
10398 					return false;
10399 					break;
10400 				case TOOL:
10401 					if ( itemTypeIsQuiver(item.type) )
10402 					{
10403 						return true;
10404 					}
10405 					break;
10406 				default:
10407 					return false;
10408 					break;
10409 			}
10410 		}
10411 		else if ( monsterAllyClass == ALLY_CLASS_MELEE )
10412 		{
10413 			switch ( itemCategory(&item) )
10414 			{
10415 				case WEAPON:
10416 					return !isRangedWeapon(item);
10417 				case ARMOR:
10418 					if ( checkEquipType(&item) == TYPE_HAT )
10419 					{
10420 						return hats;
10421 					}
10422 					else if ( checkEquipType(&item) == TYPE_HELM )
10423 					{
10424 						return helm;
10425 					}
10426 					return true;
10427 				case MAGICSTAFF:
10428 					return false;
10429 				case THROWN:
10430 					return false;
10431 				case TOOL:
10432 					if ( itemTypeIsQuiver(item.type) )
10433 					{
10434 						return false;
10435 					}
10436 					break;
10437 				default:
10438 					return false;
10439 			}
10440 		}
10441 	}
10442 	return false;
10443 }
10444 
monsterIsTinkeringCreation()10445 bool Entity::monsterIsTinkeringCreation()
10446 {
10447 	int race = this->getMonsterTypeFromSprite();
10448 	if ( behavior != &actMonster )
10449 	{
10450 		return false;
10451 	}
10452 	if ( race == GYROBOT || race == DUMMYBOT || race == SENTRYBOT || race == SPELLBOT )
10453 	{
10454 		return true;
10455 	}
10456 	return false;
10457 }
10458 
monsterHandleKnockbackVelocity(real_t monsterFacingTangent,real_t weightratio)10459 void Entity::monsterHandleKnockbackVelocity(real_t monsterFacingTangent, real_t weightratio)
10460 {
10461 	// this function makes the monster accelerate to running forwards or 0 movement speed after being knocked back.
10462 	// vel_x, vel_y are set on knockback impact and this slowly accumulates speed from the knocked back movement by a factor of monsterKnockbackVelocity.
10463 	real_t maxVelX = cos(monsterFacingTangent) * .045 * (monsterGetDexterityForMovement() + 10) * weightratio;
10464 	real_t maxVelY = sin(monsterFacingTangent) * .045 * (monsterGetDexterityForMovement() + 10) * weightratio;
10465 	bool mobile = ((monsterState == MONSTER_STATE_WAIT) || isMobile()); // if immobile, the intended max speed is 0 (stopped).
10466 
10467 	if ( maxVelX > 0 )
10468 	{
10469 		this->vel_x = std::min(this->vel_x + (this->monsterKnockbackVelocity * maxVelX), mobile ? maxVelX : 0.0);
10470 	}
10471 	else
10472 	{
10473 		this->vel_x = std::max(this->vel_x + (this->monsterKnockbackVelocity * maxVelX), mobile ? maxVelX : 0.0);
10474 	}
10475 	if ( maxVelY > 0 )
10476 	{
10477 		this->vel_y = std::min(this->vel_y + (this->monsterKnockbackVelocity * maxVelY), mobile ? maxVelY : 0.0);
10478 	}
10479 	else
10480 	{
10481 		this->vel_y = std::max(this->vel_y + (this->monsterKnockbackVelocity * maxVelY), mobile ? maxVelY : 0.0);
10482 	}
10483 	this->monsterKnockbackVelocity *= 1.1;
10484 }
10485 
monsterGetDexterityForMovement()10486 int Entity::monsterGetDexterityForMovement()
10487 {
10488 	int myDex = this->getDEX();
10489 	if ( this->monsterAllyGetPlayerLeader() )
10490 	{
10491 		myDex = std::min(myDex, MONSTER_ALLY_DEXTERITY_SPEED_CAP);
10492 	}
10493 	if ( this->getStats()->EFFECTS[EFF_DASH] )
10494 	{
10495 		myDex += 30;
10496 	}
10497 	return myDex;
10498 }
10499 
monsterGenerateQuiverItem(Stat * myStats,bool lesserMonster)10500 void Entity::monsterGenerateQuiverItem(Stat* myStats, bool lesserMonster)
10501 {
10502 	if ( !myStats )
10503 	{
10504 		return;
10505 	}
10506 	int ammo = 5;
10507 	if ( currentlevel >= 15 )
10508 	{
10509 		ammo += 5 + rand() % 16;
10510 	}
10511 	else
10512 	{
10513 		ammo += rand() % 11;
10514 	}
10515 	switch ( myStats->type )
10516 	{
10517 		case HUMAN:
10518 			switch ( rand() % 5 )
10519 			{
10520 				case 0:
10521 				case 1:
10522 					myStats->shield = newItem(QUIVER_LIGHTWEIGHT, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10523 					break;
10524 				case 2:
10525 				case 3:
10526 					myStats->shield = newItem(QUIVER_SILVER, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10527 					break;
10528 				case 4:
10529 					if ( currentlevel >= 18 )
10530 					{
10531 						if ( rand() % 2 )
10532 						{
10533 							myStats->shield = newItem(QUIVER_CRYSTAL, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10534 						}
10535 						else
10536 						{
10537 							myStats->shield = newItem(QUIVER_LIGHTWEIGHT, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10538 						}
10539 					}
10540 					else
10541 					{
10542 						myStats->shield = newItem(QUIVER_LIGHTWEIGHT, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10543 					}
10544 					break;
10545 				default:
10546 					break;
10547 			}
10548 			break;
10549 		case GOBLIN:
10550 			switch ( rand() % 5 )
10551 			{
10552 				case 0:
10553 				case 1:
10554 					myStats->shield = newItem(QUIVER_FIRE, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10555 					break;
10556 				case 2:
10557 				case 3:
10558 					myStats->shield = newItem(QUIVER_KNOCKBACK, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10559 					break;
10560 				case 4:
10561 					if ( currentlevel >= 18 )
10562 					{
10563 						if ( rand() % 2 )
10564 						{
10565 							myStats->shield = newItem(QUIVER_FIRE, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10566 						}
10567 						else
10568 						{
10569 							myStats->shield = newItem(QUIVER_KNOCKBACK, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10570 						}
10571 					}
10572 					else
10573 					{
10574 						myStats->shield = newItem(QUIVER_KNOCKBACK, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10575 					}
10576 					break;
10577 				default:
10578 					break;
10579 			}
10580 			break;
10581 		case INSECTOID:
10582 			switch ( rand() % 5 )
10583 			{
10584 				case 0:
10585 				case 1:
10586 					myStats->shield = newItem(QUIVER_PIERCE, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10587 					break;
10588 				case 2:
10589 				case 3:
10590 					myStats->shield = newItem(QUIVER_HUNTING, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10591 					break;
10592 				case 4:
10593 					if ( currentlevel >= 18 )
10594 					{
10595 						if ( rand() % 2 )
10596 						{
10597 							myStats->shield = newItem(QUIVER_CRYSTAL, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10598 						}
10599 						else
10600 						{
10601 							myStats->shield = newItem(QUIVER_PIERCE, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10602 						}
10603 					}
10604 					else
10605 					{
10606 						myStats->shield = newItem(QUIVER_PIERCE, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10607 					}
10608 					break;
10609 				default:
10610 					break;
10611 			}
10612 			break;
10613 		case KOBOLD:
10614 			switch ( rand() % 5 )
10615 			{
10616 				case 0:
10617 				case 1:
10618 					myStats->shield = newItem(QUIVER_FIRE, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10619 					break;
10620 				case 2:
10621 				case 3:
10622 					myStats->shield = newItem(QUIVER_KNOCKBACK, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10623 					break;
10624 				case 4:
10625 					if ( currentlevel >= 28 )
10626 					{
10627 						if ( rand() % 2 )
10628 						{
10629 							myStats->shield = newItem(QUIVER_CRYSTAL, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10630 						}
10631 						else
10632 						{
10633 							myStats->shield = newItem(QUIVER_FIRE, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10634 						}
10635 					}
10636 					else
10637 					{
10638 						myStats->shield = newItem(QUIVER_FIRE, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10639 					}
10640 					break;
10641 				default:
10642 					break;
10643 			}
10644 			break;
10645 		case INCUBUS:
10646 			switch ( rand() % 5 )
10647 			{
10648 				case 0:
10649 				case 1:
10650 					myStats->shield = newItem(QUIVER_LIGHTWEIGHT, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10651 					break;
10652 				case 2:
10653 				case 3:
10654 					myStats->shield = newItem(QUIVER_PIERCE, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10655 					break;
10656 				case 4:
10657 					if ( currentlevel >= 18 )
10658 					{
10659 						if ( rand() % 2 )
10660 						{
10661 							myStats->shield = newItem(QUIVER_CRYSTAL, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10662 						}
10663 						else
10664 						{
10665 							myStats->shield = newItem(QUIVER_PIERCE, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10666 						}
10667 					}
10668 					else
10669 					{
10670 						myStats->shield = newItem(QUIVER_PIERCE, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10671 					}
10672 					break;
10673 				default:
10674 					break;
10675 			}
10676 			break;
10677 		case SKELETON:
10678 			switch ( rand() % 5 )
10679 			{
10680 				case 0:
10681 				case 1:
10682 					myStats->shield = newItem(QUIVER_KNOCKBACK, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10683 					break;
10684 				case 2:
10685 				case 3:
10686 					myStats->shield = newItem(QUIVER_LIGHTWEIGHT, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10687 					break;
10688 				case 4:
10689 					if ( currentlevel >= 18 )
10690 					{
10691 						if ( rand() % 2 )
10692 						{
10693 							myStats->shield = newItem(QUIVER_FIRE, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10694 						}
10695 						else
10696 						{
10697 							myStats->shield = newItem(QUIVER_PIERCE, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10698 						}
10699 					}
10700 					else
10701 					{
10702 						myStats->shield = newItem(QUIVER_KNOCKBACK, SERVICABLE, 0, ammo, ITEM_GENERATED_QUIVER_APPEARANCE, false, nullptr);
10703 					}
10704 					break;
10705 				default:
10706 					break;
10707 			}
10708 			break;
10709 		case AUTOMATON:
10710 			myStats->shield = newItem(QUIVER_PIERCE, SERVICABLE, 0, ammo, MONSTER_ITEM_UNDROPPABLE_APPEARANCE, false, nullptr);
10711 			break;
10712 		default:
10713 			break;
10714 	}
10715 }
10716 
getMonsterEffectiveDistanceOfRangedWeapon(Item * weapon)10717 int Entity::getMonsterEffectiveDistanceOfRangedWeapon(Item* weapon)
10718 {
10719 	if ( !weapon )
10720 	{
10721 		return 160;
10722 	}
10723 
10724 	if ( getMonsterTypeFromSprite() == SENTRYBOT )
10725 	{
10726 		return sightranges[SENTRYBOT];
10727 	}
10728 	else if ( getMonsterTypeFromSprite() == SPELLBOT )
10729 	{
10730 		return sightranges[SPELLBOT];
10731 	}
10732 
10733 	int distance = 160;
10734 	switch ( weapon->type )
10735 	{
10736 		case SLING:
10737 		case CROSSBOW:
10738 		case HEAVY_CROSSBOW:
10739 			distance = 100;
10740 			break;
10741 		case LONGBOW:
10742 			distance = 200;
10743 			break;
10744 		default:
10745 			break;
10746 	}
10747 	return distance;
10748 }
10749 
isFollowerFreeToPathToPlayer(Stat * myStats)10750 bool Entity::isFollowerFreeToPathToPlayer(Stat* myStats)
10751 {
10752 	Entity* currentTarget = uidToEntity(monsterTarget);
10753 	if ( currentTarget )
10754 	{
10755 		if ( monsterState == MONSTER_STATE_ATTACK )
10756 		{
10757 			// fighting something.
10758 			return false;
10759 		}
10760 		else if ( entityDist(this, currentTarget) < TOUCHRANGE * 2 )
10761 		{
10762 			// in the vicinity of our target
10763 			return false;
10764 		}
10765 	}
10766 	return true;
10767 }