summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/monstuff.cc
diff options
context:
space:
mode:
authorzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2007-10-13 05:17:12 +0000
committerzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2007-10-13 05:17:12 +0000
commita3ba5a8ee29de19bb37c6718f56f5f184d58be6a (patch)
treee2ee6a6a9fb04218050fafcc263739d34c9a4b16 /crawl-ref/source/monstuff.cc
parent79c8875dd3844dcc3a027013d536b6d10ddc8480 (diff)
downloadcrawl-ref-a3ba5a8ee29de19bb37c6718f56f5f184d58be6a.tar.gz
crawl-ref-a3ba5a8ee29de19bb37c6718f56f5f184d58be6a.zip
This change breaks savefile compatibility.
Removed speed_inc field from monsterentry struct and added field energy_usage, which lets a monster take different amounts of time for different types of actions, defaulting to 10 points of energy for everything but picking up an object, which defaults to 100% of the monster's speed (the same as before this change). This is currently only used to make curse toes move at half the speed with which they attack (which was previously hard-coded), but is could be used for a lot of interesting things. A related change is that the manner in which apply_enchantment() deals with a monster moving at a different rate than the player has changed for monsters with non-zero-speed. As an example, giant bats have a speed of 30, which means that apply_enchantments() gets called three times per player move if the player is at normal speed; apply_enchantment() has to compensate for this so that a poisoned bat doesn't suffer three times the damage as a monster with speed 10. Monsters being able to do things in energy increments of other than 10 complicated this, and the easiest way to deal with the complication was to keep track of how much energy a monster had recently expended with the new monsters class member ench_countdown, and use that to call apply_enchantments() once for every normal-speed player move (zero-speed monsters are still dealt with in the same manner as before). Because of this, the "spd" variable in apply_enchantment() and decay_enchantment() is normalized to 10 for all non-zero-speed monsters. This doesn't work for ENCH_HELD, since a fast monster should be able to escape from a net more quickly than a slow monster. I've tried to compensate for this, but I'm not sure if I've done it right. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@2450 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref/source/monstuff.cc')
-rw-r--r--crawl-ref/source/monstuff.cc268
1 files changed, 203 insertions, 65 deletions
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index 1d9084efe8..322cc9163c 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -74,7 +74,7 @@ static bool handle_pickup(monsters *monster);
static void handle_behaviour(monsters *monster);
static void set_nearest_monster_foe(monsters *monster);
static void mons_in_cloud(monsters *monster);
-static void monster_move(monsters *monster);
+static bool monster_move(monsters *monster);
static bool plant_spit(monsters *monster, bolt &pbolt);
static int map_wand_to_mspell(int wand_type);
@@ -970,6 +970,7 @@ static bool jelly_divide(monsters * parent)
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;
@@ -1167,6 +1168,8 @@ bool monster_polymorph( monsters *monster, monster_type targetc,
const int old_hp = monster->hit_points;
const int old_hp_max = monster->max_hit_points;
const bool old_mon_caught = mons_is_caught(monster);
+ const char old_ench_countdown = monster->ench_countdown;
+ const int old_speed = monster->speed;
/* deal with mons_sec */
monster->type = targetc;
@@ -1182,6 +1185,9 @@ bool monster_polymorph( monsters *monster, monster_type targetc,
monster->add_ench(abj);
monster->add_ench(shifter);
+ monster->ench_countdown = old_ench_countdown * old_speed
+ / monster->speed;
+
if (mons_class_flag( monster->type, M_INVIS ))
monster->add_ench(ENCH_INVIS);
@@ -2138,30 +2144,30 @@ static bool handle_enchantment(monsters *monster)
// and (2) the monster cannot move (speed == 0) and the monster loop
// is running.
//
- // In the first case we don't have to figure in the player's time,
- // since the rate of call to this function already does that (ie.
- // a bat would get here 6 times in 2 normal player turns, and if
- // the player was twice as fast it would be 6 times every four player
- // moves. So the only speed we care about is the monster vs the
- // absolute time frame.
+ // In the first case we in the player's time by keeping track of
+ // how much energy the monster has expended since the last time
+ // apply_enchantments() was called, and calling it each time that
+ // it equals or exceeds the monsters speed. For example, a bat
+ // gets 30 energy points for every 10 the player gets, and each
+ // time it spends 30 energy points apply_enchantments() is called.
//
// In the second case, we're hacking things so that plants can suffer
// from sticky flame. The rate of call in this case is once every
// player action... so the time_taken by the player is the ratio to
// the absolute time frame.
//
- // This will be used below for poison and sticky flame so that the
- // damage is apparently in the absolute time frame. This is done
- // by scaling the damage and the chance that the effect goes away.
- // The result is that poison on a regular monster will be doing
- // 1d3 damage every two rounds, and last eight rounds, and on
- // a bat the same poison will be doing 1/3 the damage each action
- // it gets (the mod fractions are randomized in), will have three
- // turns to the other monster's one, and the effect will survive
- // 3 times as many calls to this function (ie 8 rounds * 3 calls).
- //
// -- bwr
- monster->apply_enchantments();
+ if (monster->speed == 0)
+ monster->apply_enchantments();
+ else
+ {
+ while (monster->ench_countdown <= 0)
+ {
+ monster->apply_enchantments();
+ monster->ench_countdown += monster->speed;
+ }
+ }
+
return (!monster->alive());
} // end handle_enchantment()
@@ -2666,6 +2672,12 @@ static bool handle_special_ability(monsters *monster, bolt & beem)
break;
}
+ if (used)
+ {
+ monsterentry *entry = get_monster_data(monster->type);
+ monster->speed_increment -= entry->energy_usage.special;
+ }
+
return (used);
} // end handle_special_ability()
@@ -2767,6 +2779,9 @@ static bool handle_potion(monsters *monster, bolt & beem)
if (ident != ID_UNKNOWN_TYPE && was_visible)
set_ident_type(OBJ_POTIONS, potion_type, ident);
+
+ monsterentry *entry = get_monster_data(monster->type);
+ monster->speed_increment -= entry->energy_usage.item;
}
return (imbibed);
@@ -2907,6 +2922,9 @@ static bool handle_scroll(monsters *monster)
if (ident != ID_UNKNOWN_TYPE && was_visible)
set_ident_type(OBJ_SCROLLS, scroll_type, ident);
+
+ monsterentry *entry = get_monster_data(monster->type);
+ monster->speed_increment -= entry->energy_usage.item;
}
return read;
@@ -3077,6 +3095,9 @@ static bool handle_wand(monsters *monster, bolt &beem)
wand.plus2++;
}
+ monsterentry *entry = get_monster_data(monster->type);
+ monster->speed_increment -= entry->energy_usage.item;
+
return (true);
}
}
@@ -3580,6 +3601,9 @@ static bool handle_spell( monsters *monster, bolt & beem )
{
simple_monster_message(monster, " blinks!");
monster_blink(monster);
+
+ monsterentry *entry = get_monster_data(monster->type);
+ monster->speed_increment -= entry->energy_usage.spell;
}
else
return (false);
@@ -3589,6 +3613,8 @@ static bool handle_spell( monsters *monster, bolt & beem )
mons_cast(monster, beem, spell_cast);
mmov_x = 0;
mmov_y = 0;
+ monsterentry *entry = get_monster_data(monster->type);
+ monster->speed_increment -= entry->energy_usage.spell;
}
} // end "if mons_class_flag(monster->type, M_SPELLCASTER) ...
@@ -3815,6 +3841,46 @@ static void monster_regenerate(monsters *monster)
}
}
+static void handle_ench_countdown(monsters *monster, int &old_energy)
+{
+ // Paranoia
+ if (monster->speed_increment >= old_energy)
+ {
+#if DEBUG
+ if (monster->speed_increment == old_energy)
+ mprf(MSGCH_DIAGNOSTICS,
+ "Monster '%s' has same energy as last iteration.",
+ monster->name(DESC_PLAIN).c_str(), true);
+ else
+ mprf(MSGCH_DIAGNOSTICS,
+ "Monster '%s' has MORE energy than last iteration.",
+ monster->name(DESC_PLAIN).c_str(), true);
+#endif
+
+ monster->speed_increment = old_energy - 10;
+ monster->ench_countdown -= 10;
+ }
+ else
+ {
+ if (old_energy != INT_MAX)
+ {
+ int energy_spent = old_energy - monster->speed_increment;
+ monster->ench_countdown -= energy_spent;
+ }
+ }
+ old_energy = monster->speed_increment;
+}
+
+#if DEBUG
+# define DEBUG_ENERGY_USE(problem) \
+ if (monster->speed_increment == old_energy) \
+ mprf(MSGCH_DIAGNOSTICS, \
+ problem " for monster '%s' consumed no energy", \
+ monster->name(DESC_PLAIN).c_str(), true);
+#else
+# define DEBUG_ENERGY_USE(problem) ((void) 0)
+#endif
+
static void handle_monster_move(int i, monsters *monster)
{
bool brkk = false;
@@ -3870,21 +3936,36 @@ static void handle_monster_move(int i, monsters *monster)
}
monster->check_speed();
-
+
+ monsterentry* entry = get_monster_data(monster->type);
+
+ int old_energy = INT_MAX;
+ int non_move_energy = std::min(entry->energy_usage.move,
+ entry->energy_usage.swim);
+
while (monster->has_action_energy())
{ // The continues & breaks are WRT this.
if (!monster->alive())
break;
- monster->speed_increment -= 10;
+ if (handle_enchantment(monster))
+ break;
+
+ handle_ench_countdown(monster, old_energy);
if (env.cgrid[monster->x][monster->y] != EMPTY_CLOUD)
{
if (monster->has_ench(ENCH_SUBMERGED))
+ {
+ monster->speed_increment -= entry->energy_usage.swim;
break;
+ }
if (monster->type == -1)
+ {
+ monster->speed_increment -= entry->energy_usage.move;
break; // problem with vortices
+ }
mons_in_cloud(monster);
@@ -3902,13 +3983,13 @@ static void handle_monster_move(int i, monsters *monster)
monster->colour = newcol;
}
- if (handle_enchantment(monster))
- break;
-
monster_regenerate(monster);
if (mons_is_paralysed(monster))
+ {
+ monster->speed_increment -= non_move_energy;
continue;
+ }
handle_behaviour(monster);
@@ -3942,7 +4023,10 @@ static void handle_monster_move(int i, monsters *monster)
if (monster->attitude != ATT_FRIENDLY)
{
if (handle_pickup(monster))
+ {
+ DEBUG_ENERGY_USE("handle_pickup()");
continue;
+ }
}
}
@@ -4003,10 +4087,11 @@ static void handle_monster_move(int i, monsters *monster)
monsters_fight(
i,
mgrd[monster->x + mmov_x][monster->y + mmov_y]);
-
+
brkk = true;
mmov_x = 0;
mmov_y = 0;
+ DEBUG_ENERGY_USE("monsters_fight()");
}
}
@@ -4039,24 +4124,40 @@ static void handle_monster_move(int i, monsters *monster)
: handle_monster_spell(monster, beem)
|| handle_special_ability(monster, beem))
{
+ DEBUG_ENERGY_USE("spell or special");
continue;
}
if (handle_potion(monster, beem))
+ {
+ DEBUG_ENERGY_USE("handle_potion()");
continue;
+ }
if (handle_scroll(monster))
+ {
+ DEBUG_ENERGY_USE("handle_scroll()");
continue;
+ }
if (handle_wand(monster, beem))
+ {
+ DEBUG_ENERGY_USE("handle_wand()");
continue;
+ }
if (handle_reaching(monster))
+ {
+ DEBUG_ENERGY_USE("handle_reaching()");
continue;
+ }
}
if (handle_throw(monster, beem))
+ {
+ DEBUG_ENERGY_USE("handle_throw()");
continue;
+ }
}
if (!mons_is_caught(monster))
@@ -4081,6 +4182,7 @@ static void handle_monster_move(int i, monsters *monster)
mmov_x = 0;
mmov_y = 0;
brkk = true;
+ DEBUG_ENERGY_USE("monsters_fight()");
}
}
@@ -4104,6 +4206,7 @@ static void handle_monster_move(int i, monsters *monster)
monster->target_x = 10 + random2(GXM - 10);
monster->target_y = 10 + random2(GYM - 10);
}
+ DEBUG_ENERGY_USE("monster_attack()");
}
if ((monster->type == MONS_GIANT_SPORE
@@ -4129,9 +4232,14 @@ static void handle_monster_move(int i, monsters *monster)
}
if (invalid_monster(monster) || mons_is_stationary(monster))
+ {
+ if (monster->speed_increment == old_energy)
+ monster->speed_increment -= non_move_energy;
continue;
+ }
- monster_move(monster);
+ if (!monster_move(monster))
+ monster->speed_increment -= non_move_energy;
}
// reevaluate behaviour, since the monster's
// surroundings have changed (it may have moved,
@@ -4142,24 +4250,30 @@ static void handle_monster_move(int i, monsters *monster)
} // end while
- if (monster->type != -1 && monster->hit_points < 1)
- {
- if (monster->type == MONS_GIANT_SPORE
- || monster->type == MONS_BALL_LIGHTNING)
- {
- // detach monster from the grid first, so it
- // doesn't get hit by its own explosion (GDL)
- mgrd[monster->x][monster->y] = NON_MONSTER;
- spore_goes_pop( monster );
- monster_cleanup( monster );
- return;
- }
+ if (monster->type != -1)
+ {
+ if ( monster->hit_points >= 1)
+ handle_ench_countdown(monster, old_energy);
else
{
- monster_die( monster, KILL_MISC, 0 );
+ if (monster->type == MONS_GIANT_SPORE
+ || monster->type == MONS_BALL_LIGHTNING)
+ {
+ // detach monster from the grid first, so it
+ // doesn't get hit by its own explosion (GDL)
+ mgrd[monster->x][monster->y] = NON_MONSTER;
+
+ spore_goes_pop( monster );
+ monster_cleanup( monster );
+ return;
+ }
+ else
+ {
+ monster_die( monster, KILL_MISC, 0 );
+ }
}
- }
+ }
}
//---------------------------------------------------------------
@@ -4445,6 +4559,16 @@ static bool monster_swaps_places( monsters *mon, int mx, int my )
return (false);
// Okay, do the swap!
+ monsterentry *entry = get_monster_data(mon->type);
+ dungeon_feature_type feat = grd[mon->x][mon->y];
+ if (feat >= DNGN_LAVA && feat <= DNGN_SHALLOW_WATER
+ && !mon->airborne())
+ {
+ mon->speed_increment -= entry->energy_usage.swim;
+ }
+ else
+ mon->speed_increment -= entry->energy_usage.move;
+
mon->x = nx;
mon->y = ny;
mgrd[nx][ny] = monster_index(mon);
@@ -4463,55 +4587,60 @@ static bool monster_swaps_places( monsters *mon, int mx, int my )
return (false);
}
-static void do_move_monster(monsters *monster, int xi, int yi)
+static bool do_move_monster(monsters *monster, int xi, int yi)
{
const int fx = monster->x + xi,
fy = monster->y + yi;
if (!in_bounds(fx, fy))
- return;
+ return false;
if (fx == you.x_pos && fy == you.y_pos)
{
monster_attack( monster_index(monster) );
- return;
+ return true;
}
if (!xi && !yi)
{
const int mx = monster_index(monster);
monsters_fight( mx, mx );
- return;
+ return true;
}
if (mgrd[fx][fy] != NON_MONSTER)
{
monsters_fight( monster_index(monster), mgrd[fx][fy] );
- return;
+ return true;
}
if (!xi && !yi)
- return;
+ return false;
+
+ /* this appears to be the real one, ie where the movement occurs: */
+ monsterentry *entry = get_monster_data(monster->type);
+ dungeon_feature_type feat = grd[monster->x][monster->y];
+ if (feat >= DNGN_LAVA && feat <= DNGN_SHALLOW_WATER
+ && !monster->airborne())
+ {
+ monster->speed_increment -= entry->energy_usage.swim;
+ }
+ else
+ monster->speed_increment -= entry->energy_usage.move;
mgrd[monster->x][monster->y] = NON_MONSTER;
- /* this appears to be the real one, ie where the movement occurs: */
monster->x = fx;
monster->y = fy;
- if (monster->type == MONS_CURSE_TOE)
- {
- // Curse toes are a special case; they can only move at half their
- // attack rate. To simulate that, the toe loses more energy.
- monster->speed_increment -= 5;
- }
-
/* need to put in something so that monster picks up multiple
items (eg ammunition) identical to those it's carrying. */
mgrd[monster->x][monster->y] = monster_index(monster);
monster->check_redraw(monster->pos() - coord_def(xi, yi));
monster->apply_location_effects();
+
+ return true;
}
void mons_check_pool(monsters *mons, killer_type killer, int killnum)
@@ -4594,14 +4723,17 @@ static bool is_trap_safe(const monsters *monster, const trap_struct &trap)
return (!mechanical || mons_flies(monster));
}
-static void mons_open_door(const coord_def &pos)
+static void mons_open_door(monsters* monster, const coord_def &pos)
{
if (grd(pos) == DNGN_SECRET_DOOR && !see_grid(pos))
set_terrain_changed(pos);
grd(pos) = DNGN_OPEN_DOOR;
+
+ monsterentry *entry = get_monster_data(monster->type);
+ monster->speed_increment -= entry->energy_usage.move;
}
-static void monster_move(monsters *monster)
+static bool monster_move(monsters *monster)
{
FixedArray < bool, 3, 3 > good_move;
int count_x, count_y, count;
@@ -4643,15 +4775,15 @@ static void monster_move(monsters *monster)
&& (monster_habitat(monster->type) == DNGN_FLOOR
|| monster_habitable_grid(monster, grd(newpos))))
{
- do_move_monster(monster, mmov_x, mmov_y);
+ return do_move_monster(monster, mmov_x, mmov_y);
}
}
- return;
+ return false;
}
// let's not even bother with this if mmov_x and mmov_y are zero.
if (mmov_x == 0 && mmov_y == 0)
- return;
+ return false;
// effectively slows down monster movement across water.
// Fire elementals can't cross at all.
@@ -4887,14 +5019,14 @@ static void monster_move(monsters *monster)
// for zombies, monster type is kept in mon->number
if (mons_itemuse(monster->number) >= MONUSE_OPEN_DOORS)
{
- mons_open_door(newpos);
- return;
+ mons_open_door(monster, newpos);
+ return true;
}
}
else if (mons_itemuse(monster->type) >= MONUSE_OPEN_DOORS)
{
- mons_open_door(newpos);
- return;
+ mons_open_door(monster, newpos);
+ return true;
}
} // endif - secret/closed doors
@@ -5064,6 +5196,7 @@ forget_it:
}
}
+ bool ret = false;
if (good_move[mmov_x + 1][mmov_y + 1] && !(mmov_x == 0 && mmov_y == 0))
{
// check for attacking player
@@ -5071,6 +5204,7 @@ forget_it:
&& monster->y + mmov_y == you.y_pos)
{
monster_attack( monster_index(monster) );
+ ret = true;
mmov_x = 0;
mmov_y = 0;
}
@@ -5088,18 +5222,19 @@ forget_it:
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS,
"BUG: %s was marked as follower when not following!",
- monster->name(DESC_PLAIN).c_str());
+ monster->name(DESC_PLAIN).c_str(), true);
#endif
}
else
{
+ ret = true;
mmov_x = 0;
mmov_y = 0;
#if DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS,
"%s is skipping movement in order to follow.",
- monster->name(DESC_CAP_THE).c_str() );
+ monster->name(DESC_CAP_THE).c_str(), true );
#endif
}
}
@@ -5114,6 +5249,7 @@ forget_it:
monsters_fight(monster_index(monster), targmon);
// If the monster swapped places, the work's already done.
+ ret = true;
mmov_x = 0;
mmov_y = 0;
}
@@ -5143,7 +5279,9 @@ forget_it:
}
if (mmov_x || mmov_y || (monster->confused() && one_chance_in(6)))
- do_move_monster(monster, mmov_x, mmov_y);
+ return do_move_monster(monster, mmov_x, mmov_y);
+
+ return ret;
} // end monster_move()
static void setup_plant_spit(monsters *monster, bolt &pbolt)