/*
* File: mon-grow.cc
* Summary: Monster level-up code.
* Written by: dshaligram on Fri Oct 26 08:33:37 2007 UTC
*/
#include "AppHdr.h"
#include "enum.h"
#include "mon-grow.h"
#include "mon-util.h"
#include "mon-place.h"
#include "mon-stuff.h"
#include "random.h"
// Base experience required by a monster to reach HD 1.
const int monster_xp_base = 15;
// Experience multiplier to determine the experience needed to gain levels.
const int monster_xp_multiplier = 150;
const mons_experience_levels mexplevs;
// Monster growing-up sequences. You can specify a chance to indicate that
// the monster only has a chance of changing type, otherwise the monster
// will grow up when it reaches the HD of the target form.
//
// No special cases are in place: make sure source and target forms are similar.
// If the target form requires special handling of some sort, add the handling
// to level_up_change().
//
static const monster_level_up mon_grow[] =
{
monster_level_up(MONS_ORC, MONS_ORC_WARRIOR),
monster_level_up(MONS_ORC_WARRIOR, MONS_ORC_KNIGHT),
monster_level_up(MONS_ORC_KNIGHT, MONS_ORC_WARLORD),
monster_level_up(MONS_ORC_PRIEST, MONS_ORC_HIGH_PRIEST),
monster_level_up(MONS_ORC_WIZARD, MONS_ORC_SORCERER),
monster_level_up(MONS_KOBOLD, MONS_BIG_KOBOLD),
monster_level_up(MONS_UGLY_THING, MONS_VERY_UGLY_THING),
monster_level_up(MONS_ANT_LARVA, MONS_GIANT_ANT),
monster_level_up(MONS_KILLER_BEE_LARVA, MONS_KILLER_BEE),
monster_level_up(MONS_CENTAUR, MONS_CENTAUR_WARRIOR),
monster_level_up(MONS_YAKTAUR, MONS_YAKTAUR_CAPTAIN),
monster_level_up(MONS_NAGA, MONS_NAGA_WARRIOR),
monster_level_up(MONS_NAGA_MAGE, MONS_GREATER_NAGA),
monster_level_up(MONS_DEEP_ELF_SOLDIER, MONS_DEEP_ELF_FIGHTER),
monster_level_up(MONS_DEEP_ELF_FIGHTER, MONS_DEEP_ELF_KNIGHT),
// Deep elf magi can become either summoners or conjurers.
monster_level_up(MONS_DEEP_ELF_MAGE, MONS_DEEP_ELF_SUMMONER, 500),
monster_level_up(MONS_DEEP_ELF_MAGE, MONS_DEEP_ELF_CONJURER),
monster_level_up(MONS_DEEP_ELF_PRIEST, MONS_DEEP_ELF_HIGH_PRIEST),
monster_level_up(MONS_DEEP_ELF_SUMMONER, MONS_DEEP_ELF_DEMONOLOGIST),
monster_level_up(MONS_DEEP_ELF_CONJURER, MONS_DEEP_ELF_SORCERER),
};
mons_experience_levels::mons_experience_levels()
{
int experience = monster_xp_base;
for (int i = 1; i <= MAX_MONS_HD; ++i)
{
mexp[i] = experience;
int delta =
(monster_xp_base + experience) * 2 * monster_xp_multiplier
/ 500;
delta =
std::min(
std::max(delta, monster_xp_base * monster_xp_multiplier / 100),
40000);
experience += delta;
}
}
static const monster_level_up *_monster_level_up_target(monster_type type,
int hit_dice)
{
for (unsigned i = 0; i < ARRAYSZ(mon_grow); ++i)
{
const monster_level_up &mlup(mon_grow[i]);
if (mlup.before == type)
{
const monsterentry *me = get_monster_data(mlup.after);
if (static_cast<int>(me->hpdice[0]) == hit_dice
&& x_chance_in_y(mlup.chance, 1000))
{
return (&mlup);
}
}
}
return (NULL);
}
void monsters::upgrade_type(monster_type after, bool adjust_hd,
bool adjust_hp)
{
const monsterentry *orig = get_monster_data(type);
// Ta-da!
type = after;
// Initialise a dummy monster to save work.
monsters dummy;
dummy.type = after;
define_monster(dummy);
colour = dummy.colour;
speed = dummy.speed;
spells = dummy.spells;
fix_speed();
const monsterentry *m = get_monster_data(after);
ac += m->AC - orig->AC;
ev += m->ev - orig->ev;
if (adjust_hd)
{
const int minhd = dummy.hit_dice;
if (hit_dice < minhd)
hit_dice += minhd - hit_dice;
}
if (adjust_hp)
{
const int minhp = dummy.max_hit_points;
if (max_hit_points < minhp)
{
hit_points += minhp - max_hit_points;
max_hit_points = minhp;
hit_points = std::min(hit_points, max_hit_points);
}
}
// An ugly thing is the only ghost demon monster that can level up.
// If one has leveled up to a very ugly thing, upgrade it properly.
if (type == MONS_VERY_UGLY_THING)
uglything_upgrade();
}
bool monsters::level_up_change()
{
if (const monster_level_up *lup =
_monster_level_up_target(static_cast<monster_type>(type), hit_dice))
{
upgrade_type(lup->after, false, lup->adjust_hp);
return (true);
}
return (false);
}
bool monsters::level_up()
{
if (hit_dice >= MAX_MONS_HD)
return (false);
++hit_dice;
// A little maxhp boost.
if (max_hit_points < 1000)
{
int hpboost =
(hit_dice > 3? max_hit_points / 8 : max_hit_points / 4)
+ random2(5);
// Not less than 3 hp, not more than 25.
hpboost = std::min(std::max(hpboost, 3), 25);
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS, "%s: HD: %d, maxhp: %d, boost: %d",
name(DESC_PLAIN).c_str(), hit_dice, max_hit_points, hpboost);
#endif
max_hit_points += hpboost;
hit_points += hpboost;
hit_points = std::min(hit_points, max_hit_points);
}
level_up_change();
return (true);
}
void monsters::init_experience()
{
if (experience || !alive())
return;
hit_dice = std::max(hit_dice, 1);
experience = mexplevs[std::min(hit_dice, MAX_MONS_HD)];
}
bool monsters::gain_exp(int exp)
{
if (!alive())
return (false);
init_experience();
if (hit_dice >= MAX_MONS_HD)
return (false);
// Only natural monsters can level-up.
if (holiness() != MH_NATURAL)
return (false);
// Only monsters that you can gain XP from can level-up.
if (mons_class_flag(type, M_NO_EXP_GAIN))
return (false);
// Avoid wrap-around.
if (experience + exp < experience)
return (false);
experience += exp;
const monsters mcopy(*this);
int levels_gained = 0;
// Monsters can gain a maximum of two levels from one kill.
while (hit_dice < MAX_MONS_HD
&& experience >= mexplevs[hit_dice + 1]
&& level_up()
&& ++levels_gained < 2);
if (levels_gained)
{
if (mons_intel(this) >= I_NORMAL)
simple_monster_message(&mcopy, " looks more experienced.");
else
simple_monster_message(&mcopy, " looks stronger.");
}
if (hit_dice < MAX_MONS_HD && experience >= mexplevs[hit_dice + 1])
experience = (mexplevs[hit_dice] + mexplevs[hit_dice + 1]) / 2;
// If the monster has leveled up to a monster that will be angered
// by the player, handle it properly.
player_angers_monster(this);
return (levels_gained > 0);
}