summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source
diff options
context:
space:
mode:
Diffstat (limited to 'crawl-ref/source')
-rw-r--r--crawl-ref/source/debug.cc6
-rw-r--r--crawl-ref/source/externs.h13
-rw-r--r--crawl-ref/source/makefile.obj1
-rw-r--r--crawl-ref/source/mgrow.cc206
-rw-r--r--crawl-ref/source/mgrow.h37
-rw-r--r--crawl-ref/source/mon-util.cc10
-rw-r--r--crawl-ref/source/mon-util.h1
-rw-r--r--crawl-ref/source/monstuff.cc50
-rw-r--r--crawl-ref/source/tags.cc2
9 files changed, 297 insertions, 29 deletions
diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc
index 7b2c61cd97..6902c614b0 100644
--- a/crawl-ref/source/debug.cc
+++ b/crawl-ref/source/debug.cc
@@ -1416,8 +1416,10 @@ void stethoscope(int mwh)
// print stats and other info
mprf(MSGCH_DIAGNOSTICS,
- "HD=%d HP=%d/%d AC=%d EV=%d MR=%d SP=%d energy=%d num=%d flags=%04lx",
- menv[i].hit_dice,
+ "HD=%d (%lu) HP=%d/%d AC=%d EV=%d MR=%d SP=%d "
+ "energy=%d num=%d flags=%04lx",
+ menv[i].hit_dice,
+ menv[i].experience,
menv[i].hit_points, menv[i].max_hit_points,
menv[i].ac, menv[i].ev,
mons_resist_magic( &menv[i] ),
diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h
index 6edf258b14..e6097dbb07 100644
--- a/crawl-ref/source/externs.h
+++ b/crawl-ref/source/externs.h
@@ -961,12 +961,13 @@ public:
mon_attitude_type attitude;
beh_type behaviour;
unsigned int foe;
- char ench_countdown;
+ char ench_countdown;
mon_enchant_list enchantments;
unsigned long flags; // bitfield of boolean flags
- unsigned int number; // #heads (hydra), etc.
- int colour;
+ unsigned long experience;
+ unsigned int number; // #heads (hydra), etc.
+ int colour;
int foe_memory; // how long to 'remember' foe x,y
// once they go out of sight
@@ -979,6 +980,8 @@ public:
// AI_SEE_MONSTER
public:
+ void init_experience();
+
bool has_action_energy() const;
void check_redraw(const coord_def &oldpos) const;
void apply_location_effects();
@@ -999,6 +1002,7 @@ public:
bool lose_ench_levels(const mon_enchant &e, int lev);
void scale_hp(int num, int den);
+ void gain_exp(int xp);
void add_enchantment_effect(const mon_enchant &me, bool quiet = false);
void remove_enchantment_effect(const mon_enchant &me, bool quiet = false);
@@ -1154,7 +1158,8 @@ private:
void init_with(const monsters &mons);
void swap_slots(mon_inv_type a, mon_inv_type b);
bool need_message(int &near) const;
-
+ bool level_up();
+ bool level_up_change();
bool pickup(item_def &item, int slot, int near, bool force_merge = false);
void equip_weapon(item_def &item, int near);
void equip_armour(item_def &item, int near);
diff --git a/crawl-ref/source/makefile.obj b/crawl-ref/source/makefile.obj
index 170eb8b367..89784d01fd 100644
--- a/crawl-ref/source/makefile.obj
+++ b/crawl-ref/source/makefile.obj
@@ -44,6 +44,7 @@ mapmark.o \
maps.o \
menu.o \
message.o \
+mgrow.o \
misc.o \
monplace.o \
mon-pick.o \
diff --git a/crawl-ref/source/mgrow.cc b/crawl-ref/source/mgrow.cc
new file mode 100644
index 0000000000..a620132677
--- /dev/null
+++ b/crawl-ref/source/mgrow.cc
@@ -0,0 +1,206 @@
+/*
+ * File: mgrow.cc
+ * Summary: Monster level-up code.
+ *
+ * Modified for Crawl Reference by $Author: dshaligram $ on $Date: 2007-10-25T09:49:39.320031Z $
+ */
+
+#include "AppHdr.h"
+#include "enum.h"
+#include "mgrow.h"
+#include "mon-util.h"
+#include "monstuff.h"
+#include "stuff.h"
+
+// Base experience required by a monster to reach HD 1.
+const int monster_xp_base = 8;
+// Experience multiplier to determine the experience needed to gain levels.
+const int monster_xp_multiplier = 120;
+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 conjurers or summoners.
+ 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),
+};
+
+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),
+ 3000);
+ experience += delta;
+ }
+}
+
+static const monster_level_up *monster_level_up_target(
+ monster_type type, int hit_dice)
+{
+ for (unsigned i = 0; i < ARRAYSIZE(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
+ && random2(1000) < mlup.chance)
+ {
+ return (&mlup);
+ }
+ }
+ }
+ return (NULL);
+}
+
+bool monsters::level_up_change()
+{
+ if (const monster_level_up *lup =
+ monster_level_up_target(static_cast<monster_type>(type), hit_dice))
+ {
+ const monsterentry *orig = get_monster_data(type);
+ // Ta-da!
+ type = lup->after;
+
+ // Initialise a dummy monster to save work.
+ monsters dummy;
+ dummy.type = type;
+ define_monster(dummy);
+
+ colour = dummy.colour;
+ speed = dummy.speed;
+ spells = dummy.spells;
+ fix_speed();
+
+ const monsterentry *m = get_monster_data(type);
+ ac += m->AC - orig->AC;
+ ev += m->ev - orig->ev;
+
+ if (lup->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);
+ }
+ }
+ 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;
+ if (hpboost < 2)
+ hpboost = 2;
+ if (hpboost > 20)
+ hpboost = 20;
+
+ 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)];
+}
+
+void monsters::gain_exp(int exp)
+{
+ if (!alive())
+ return;
+
+ init_experience();
+ if (hit_dice >= MAX_MONS_HD)
+ return;
+
+ // Only natural monsters can level-up.
+ if (holiness() != MH_NATURAL)
+ return;
+
+ // Avoid wrap-around.
+ if (experience + exp < experience)
+ return;
+
+ 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(type) >= 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;
+}
diff --git a/crawl-ref/source/mgrow.h b/crawl-ref/source/mgrow.h
new file mode 100644
index 0000000000..ff558265d1
--- /dev/null
+++ b/crawl-ref/source/mgrow.h
@@ -0,0 +1,37 @@
+#ifndef __MGROW_H__
+#define __MGROW_H__
+
+#include "AppHdr.h"
+#include "FixVec.h"
+
+// Monster level-up data.
+
+struct monster_level_up
+{
+ monster_type before, after;
+ int chance; // Chance in 1000 of the monster growing up,
+ // defaults to 1000.
+
+ bool adjust_hp; // If hp post growing up is less than minimum, adjust it.
+
+ monster_level_up(monster_type _before, monster_type _after,
+ int _chance = 1000, bool _adjust = true)
+ : before(_before), after(_after), chance(_chance), adjust_hp(_adjust)
+ {
+ }
+};
+
+const int MAX_MONS_HD = 27;
+class mons_experience_levels
+{
+public:
+ mons_experience_levels();
+ unsigned operator [] (int xl) const
+ {
+ return mexp[xl];
+ }
+private:
+ FixedVector<unsigned, MAX_MONS_HD + 1> mexp;
+};
+
+#endif
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index 3ada5a9d14..2e6d55ce3a 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -1305,11 +1305,14 @@ void mons_load_spells( monsters *mon, mon_spellbook_type book )
mon->load_spells(book);
}
-// generate a shiny new and unscarred monster
void define_monster(int index)
{
- monsters &mons = menv[index];
+ define_monster(menv[index]);
+}
+// generate a shiny new and unscarred monster
+void define_monster(monsters &mons)
+{
int temp_rand = 0; // probability determination {dlb}
int mcls = mons.type;
int hd, hp, hp_max, ac, ev, speed;
@@ -2316,7 +2319,7 @@ monsters::monsters()
ac(0), ev(0), speed(0), speed_increment(0), x(0), y(0),
target_x(0), target_y(0), inv(), spells(), attitude(ATT_HOSTILE),
behaviour(BEH_WANDER), foe(MHITYOU), enchantments(), flags(0L),
- number(0), colour(BLACK), foe_memory(0), god(GOD_NO_GOD),
+ experience(0), number(0), colour(BLACK), foe_memory(0), god(GOD_NO_GOD),
ghost(), seen_context("")
{
}
@@ -2354,6 +2357,7 @@ void monsters::init_with(const monsters &mon)
foe = mon.foe;
enchantments = mon.enchantments;
flags = mon.flags;
+ experience = mon.experience;
number = mon.number;
colour = mon.colour;
foe_memory = mon.foe_memory;
diff --git a/crawl-ref/source/mon-util.h b/crawl-ref/source/mon-util.h
index 031f976bbe..988d13499b 100644
--- a/crawl-ref/source/mon-util.h
+++ b/crawl-ref/source/mon-util.h
@@ -522,6 +522,7 @@ void mons_load_spells( monsters *mon, mon_spellbook_type book );
* called from: dungeon - fight
* *********************************************************************** */
void define_monster(int mid);
+void define_monster(monsters &mons);
// last updated 4jan2001 (gdl)
/* ***********************************************************************
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index fdf88457fc..aee12608a2 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -375,17 +375,35 @@ static void check_kill_milestone(const monsters *mons,
}
#endif // DGL_MILESTONES
+static void give_monster_experience( int killer_index, int experience,
+ bool victim_was_born_friendly )
+{
+ if (killer_index < 0 || killer_index >= MAX_MONSTERS)
+ return;
+ monsters *mons = &menv[killer_index];
+ if (!mons->alive())
+ return;
+
+ if (mons_friendly(mons) != victim_was_born_friendly)
+ mons->gain_exp(experience);
+}
+
static void give_adjusted_experience(monsters *monster, killer_type killer,
- bool pet_kill, unsigned int *exp_gain,
+ bool pet_kill, int killer_index,
+ unsigned int *exp_gain,
unsigned int *avail_gain)
{
- if (testbits(monster->flags, MF_CREATED_FRIENDLY))
+ const int experience = exper_value(monster);
+ const bool created_friendly = testbits(monster->flags, MF_CREATED_FRIENDLY);
+ if (created_friendly)
; // No experience if monster was created friendly
else if (YOU_KILL(killer))
- gain_exp( exper_value( monster ), exp_gain, avail_gain );
+ gain_exp( experience, exp_gain, avail_gain );
else if (pet_kill)
- gain_exp( exper_value( monster ) / 2 + 1,
- exp_gain, avail_gain );
+ gain_exp( experience / 2 + 1, exp_gain, avail_gain );
+
+ if (MON_KILL(killer))
+ give_monster_experience( killer_index, experience, created_friendly );
}
static bool is_pet_kill(killer_type killer, int i)
@@ -580,12 +598,12 @@ void monster_die(monsters *monster, killer_type killer, int i, bool silent)
did_god_conduct(DID_KILL_DEMON,
monster->hit_dice);
- //jmf: Trog hates wizards
+ // jmf: Trog hates wizards
if (mons_is_magic_user(monster))
did_god_conduct(DID_KILL_WIZARD,
monster->hit_dice);
- //Beogh hates priests
+ // Beogh hates priests of other gods.
if (mons_class_flag(monster->type, M_PRIEST))
did_god_conduct(DID_KILL_PRIEST,
monster->hit_dice);
@@ -825,7 +843,7 @@ void monster_die(monsters *monster, killer_type killer, int i, bool silent)
KC_OTHER;
unsigned int exp_gain = 0, avail_gain = 0;
- give_adjusted_experience(monster, killer, pet_kill,
+ give_adjusted_experience(monster, killer, pet_kill, i,
&exp_gain, &avail_gain);
PlaceInfo& curr_PlaceInfo = you.get_place_info();
@@ -954,23 +972,15 @@ static bool jelly_divide(monsters * parent)
if (parent->hit_points > parent->max_hit_points)
parent->hit_points = parent->max_hit_points;
+ parent->init_experience();
+ parent->experience = parent->experience * 3 / 5 + 1;
+
// create child {dlb}:
// this is terribly partial and really requires
// more thought as to generation ... {dlb}
- child->type = parent->type;
- child->hit_dice = parent->hit_dice;
- child->hit_points = parent->hit_points;
+ *child = *parent;
child->max_hit_points = child->hit_points;
- child->ac = parent->ac;
- child->ev = parent->ev;
- child->speed = parent->speed;
child->speed_increment = 70 + random2(5);
- child->behaviour = parent->behaviour; /* Look at this! */
- child->foe = parent->foe;
- child->attitude = parent->attitude;
- child->colour = parent->colour;
- child->enchantments = parent->enchantments;
- child->ench_countdown = parent->ench_countdown;
child->x = parent->x + jex;
child->y = parent->y + jey;
diff --git a/crawl-ref/source/tags.cc b/crawl-ref/source/tags.cc
index 8aeabbf1c1..f00181319f 100644
--- a/crawl-ref/source/tags.cc
+++ b/crawl-ref/source/tags.cc
@@ -1641,6 +1641,7 @@ static void marshall_monster(tagHeader &th, const monsters &m)
marshallByte(th, m.target_x);
marshallByte(th, m.target_y);
marshallLong(th, m.flags);
+ marshallLong(th, m.experience);
marshallShort(th, m.enchantments.size());
for (mon_enchant_list::const_iterator i = m.enchantments.begin();
@@ -1804,6 +1805,7 @@ static void unmarshall_monster(tagHeader &th, monsters &m)
m.target_x = unmarshallByte(th);
m.target_y = unmarshallByte(th);
m.flags = unmarshallLong(th);
+ m.experience = static_cast<unsigned long>(unmarshallLong(th));
m.enchantments.clear();
const int nenchs = unmarshallShort(th);