1 /**
2 * @file
3 * @brief Monster level-up code.
4 **/
5
6 #include "AppHdr.h"
7
8 #include "mon-grow.h"
9
10 #include "message.h"
11 #include "mon-place.h"
12 #include "monster.h"
13 #include "mon-util.h"
14
15 // Base experience required by a monster to reach HD 1.
16 const int monster_xp_base = 15;
17 // Experience multiplier to determine the experience needed to gain levels.
18 const int monster_xp_multiplier = 150;
19 const mons_experience_levels mexplevs;
20
21 // Monster growing-up sequences, for Beogh orcs. You can specify a chance to
22 // indicate that the monster only has a chance of changing type, otherwise the
23 // monster will grow up when it reaches the HD of the target form.
24 //
25 // No special cases are in place: make sure source and target forms are similar.
26 // If the target form requires special handling of some sort, add the handling
27 // to level_up_change().
28
29 static const monster_level_up mon_grow[] =
30 {
31 monster_level_up(MONS_ORC, MONS_ORC_WARRIOR),
32 monster_level_up(MONS_ORC_WARRIOR, MONS_ORC_KNIGHT),
33 monster_level_up(MONS_ORC_KNIGHT, MONS_ORC_WARLORD),
34 monster_level_up(MONS_ORC_PRIEST, MONS_ORC_HIGH_PRIEST),
35 monster_level_up(MONS_ORC_WIZARD, MONS_ORC_SORCERER),
36 };
37
mons_experience_levels()38 mons_experience_levels::mons_experience_levels()
39 {
40 int experience = monster_xp_base;
41 for (int i = 1; i <= MAX_MONS_HD; ++i)
42 {
43 mexp[i] = experience;
44
45 int delta =
46 (monster_xp_base + experience) * 2 * monster_xp_multiplier
47 / 500;
48 delta =
49 min(max(delta, monster_xp_base * monster_xp_multiplier / 100),
50 40000);
51 experience += delta;
52 }
53 }
54
_monster_level_up_target(monster_type type,int hit_dice)55 static const monster_level_up *_monster_level_up_target(monster_type type,
56 int hit_dice)
57 {
58 for (const monster_level_up &mlup : mon_grow)
59 {
60 if (mlup.before == type)
61 {
62 const monsterentry *me = get_monster_data(mlup.after);
63 if (me->HD == hit_dice && x_chance_in_y(mlup.chance, 1000))
64 return &mlup;
65 }
66 }
67 return nullptr;
68 }
69
upgrade_type(monster_type after,bool adjust_hd,bool adjust_hp)70 void monster::upgrade_type(monster_type after, bool adjust_hd,
71 bool adjust_hp)
72 {
73 // Ta-da!
74 type = after;
75
76 // Initialise a dummy monster to save work.
77 monster dummy;
78 dummy.type = after;
79 dummy.base_monster = base_monster;
80 dummy.number = number;
81 define_monster(dummy); // assumes after is not a zombie
82
83 colour = dummy.colour;
84 speed = dummy.speed;
85 spells = dummy.spells;
86 calc_speed();
87
88 if (adjust_hd)
89 set_hit_dice(max(get_experience_level(), dummy.get_hit_dice()));
90
91 if (adjust_hp)
92 {
93 const int minhp = dummy.max_hit_points;
94 if (max_hit_points < minhp)
95 {
96 hit_points += minhp - max_hit_points;
97 max_hit_points = minhp;
98 hit_points = min(hit_points, max_hit_points);
99 }
100 }
101 }
102
level_up_change()103 bool monster::level_up_change()
104 {
105 const monster_level_up *lup =
106 _monster_level_up_target(type, get_experience_level());
107 if (lup)
108 {
109 upgrade_type(lup->after, false, lup->adjust_hp);
110 return true;
111 }
112 return false;
113 }
114
level_up()115 bool monster::level_up()
116 {
117 if (get_experience_level() >= MAX_MONS_HD)
118 return false;
119
120 set_hit_dice(get_experience_level() + 1);
121
122 // A little maxhp boost.
123 if (max_hit_points < 1000)
124 {
125 int hpboost =
126 (get_experience_level() > 3? max_hit_points / 8 : max_hit_points / 4)
127 + random2(5);
128
129 // Not less than 3 hp, not more than 25.
130 hpboost = min(max(hpboost, 3), 25);
131
132 dprf("%s: HD: %d, maxhp: %d, boost: %d",
133 name(DESC_PLAIN).c_str(), get_experience_level(),
134 max_hit_points, hpboost);
135
136 max_hit_points += hpboost;
137 hit_points += hpboost;
138 hit_points = min(hit_points, max_hit_points);
139 }
140
141 level_up_change();
142
143 return true;
144 }
145
init_experience()146 void monster::init_experience()
147 {
148 if (experience || !alive())
149 return;
150 set_hit_dice(max(get_experience_level(), 1));
151 experience = mexplevs[min(get_experience_level(), MAX_MONS_HD)];
152 }
153
gain_exp(int exp,int max_levels_to_gain)154 bool monster::gain_exp(int exp, int max_levels_to_gain)
155 {
156 if (!alive())
157 return false;
158
159 init_experience();
160 if (get_experience_level() >= MAX_MONS_HD)
161 return false;
162
163 // Only natural monsters can level-up.
164 if (!(holiness() & MH_NATURAL))
165 return false;
166
167 // Only monsters that you can gain XP from can level-up.
168 if (!mons_class_gives_xp(type) || is_summoned())
169 return false;
170
171 // Avoid wrap-around.
172 if (experience + exp < experience)
173 return false;
174
175 experience += exp;
176
177 const monster mcopy(*this);
178 int levels_gained = 0;
179 // Monsters can normally gain a maximum of two levels from one kill.
180 while (get_experience_level() < MAX_MONS_HD
181 && experience >= mexplevs[get_experience_level() + 1]
182 && level_up()
183 && ++levels_gained < max_levels_to_gain);
184
185 if (levels_gained)
186 {
187 if (mons_intel(*this) >= I_HUMAN)
188 simple_monster_message(mcopy, " looks more experienced.");
189 else
190 simple_monster_message(mcopy, " looks stronger.");
191 }
192
193 if (get_experience_level() < MAX_MONS_HD
194 && experience >= mexplevs[get_experience_level() + 1])
195 {
196 experience = (mexplevs[get_experience_level()]
197 + mexplevs[get_experience_level() + 1]) / 2;
198 }
199
200 return levels_gained > 0;
201 }
202