summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/monplace.cc
diff options
context:
space:
mode:
authordshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2008-05-05 13:33:29 +0000
committerdshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2008-05-05 13:33:29 +0000
commitff2f267821153758bcd691e73b1b409c62f0c4e0 (patch)
tree11f18df98aad81ddfa9798dafd67eceb3b4ed510 /crawl-ref/source/monplace.cc
parent3423bbab5a0f024eeafffe6b8260c8c088647db1 (diff)
downloadcrawl-ref-ff2f267821153758bcd691e73b1b409c62f0c4e0.tar.gz
crawl-ref-ff2f267821153758bcd691e73b1b409c62f0c4e0.zip
Cleaned up monster generation functions, separate monster zombie type from monster number. May be buggy.
Allow hydra zombies (they currently do not get the right number of heads). git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@4872 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref/source/monplace.cc')
-rw-r--r--crawl-ref/source/monplace.cc780
1 files changed, 460 insertions, 320 deletions
diff --git a/crawl-ref/source/monplace.cc b/crawl-ref/source/monplace.cc
index c74d4cdb2a..2bcc8bc2a2 100644
--- a/crawl-ref/source/monplace.cc
+++ b/crawl-ref/source/monplace.cc
@@ -44,11 +44,16 @@
#define BIG_BAND 20
-static int band_member(band_type band, int power);
+static void _define_zombie( int mid, monster_type ztype,
+ monster_type cs, int power );
+static monster_type _band_member(band_type band, int power);
static band_type choose_band(int mon_type, int power, int &band_size );
-static int place_monster_aux(int mon_type, beh_type behaviour, int target,
- int px, int py, int power, int extra,
- bool first_band_member, int dur = 0);
+// static int _place_monster_aux(int mon_type, beh_type behaviour, int target,
+// int px, int py, int power, int extra,
+// bool first_band_member, int dur = 0);
+
+static int _place_monster_aux(const mgen_data &mg,
+ bool first_band_member);
// Returns whether actual_grid is compatible with grid_wanted for monster
// movement (or for monster generation, if generation is true).
@@ -181,10 +186,10 @@ static void hell_spawn_random_monsters()
if (one_chance_in(genodds))
{
- proximity_type prox =
+ mgen_data mg(WANDERING_MONSTER);
+ mg.proximity =
(one_chance_in(10) ? PROX_NEAR_STAIRS : PROX_AWAY_FROM_PLAYER);
- mons_place( WANDERING_MONSTER, BEH_HOSTILE, MHITNOT, false,
- 50, 50, LEVEL_DUNGEON, prox );
+ mons_place(mg);
viewwindow(true, false);
}
}
@@ -209,16 +214,16 @@ void spawn_random_monsters()
if (you.char_direction == GDT_ASCENDING)
prox = (one_chance_in(6) ? PROX_CLOSE_TO_PLAYER : PROX_ANYWHERE);
- mons_place( WANDERING_MONSTER, BEH_HOSTILE, MHITNOT, false,
- 50, 50, LEVEL_DUNGEON, prox );
+ mgen_data mg(WANDERING_MONSTER);
+ mg.proximity = prox;
+ mons_place( mg );
viewwindow(true, false);
}
// place Abyss monsters.
if (you.level_type == LEVEL_ABYSS && one_chance_in(5))
{
- mons_place( WANDERING_MONSTER, BEH_HOSTILE, MHITNOT, false,
- 50, 50, LEVEL_ABYSS, PROX_ANYWHERE );
+ mons_place(mgen_data(WANDERING_MONSTER));
viewwindow(true, false);
}
@@ -356,28 +361,37 @@ bool drac_colour_incompatible(int drac, int colour)
return (drac == MONS_DRACONIAN_SCORCHER && colour == MONS_WHITE_DRACONIAN);
}
-static int resolve_monster_type(int mon_type, proximity_type proximity,
- int num, int px, int py, unsigned mmask,
- dungeon_char_type *stair_type,
- int *lev_mons)
+static monster_type resolve_monster_type(monster_type mon_type,
+ proximity_type proximity,
+ monster_type base_type,
+ coord_def &pos,
+ unsigned mmask,
+ dungeon_char_type *stair_type,
+ int *lev_mons)
{
if (mon_type == RANDOM_DRACONIAN)
{
// Pick any random drac, constrained by colour if requested.
do
mon_type =
- random_range(MONS_BLACK_DRACONIAN, MONS_DRACONIAN_SCORCHER);
- while (num != MONS_PROGRAM_BUG
- && mon_type != num
+ static_cast<monster_type>(
+ random_range(MONS_BLACK_DRACONIAN,
+ MONS_DRACONIAN_SCORCHER));
+ while (base_type != MONS_PROGRAM_BUG
+ && mon_type != base_type
&& (mons_species(mon_type) == mon_type
- || drac_colour_incompatible(mon_type, num)));
+ || drac_colour_incompatible(mon_type, base_type)));
}
else if (mon_type == RANDOM_BASE_DRACONIAN)
- mon_type = random_range(MONS_BLACK_DRACONIAN, MONS_PALE_DRACONIAN);
+ mon_type =
+ static_cast<monster_type>(
+ random_range(MONS_BLACK_DRACONIAN, MONS_PALE_DRACONIAN));
else if (mon_type == RANDOM_NONBASE_DRACONIAN)
- mon_type = random_range(MONS_DRACONIAN_CALLER, MONS_DRACONIAN_SCORCHER);
+ mon_type =
+ static_cast<monster_type>(
+ random_range(MONS_DRACONIAN_CALLER, MONS_DRACONIAN_SCORCHER));
- // (2) take care of random monsters
+ // (2) take care of non-drac random monsters
if (mon_type == RANDOM_MONSTER)
{
level_id place = level_id::current();
@@ -388,21 +402,17 @@ static int resolve_monster_type(int mon_type, proximity_type proximity,
int pval = 0;
while (++tries <= 320)
{
- px = 5 + random2(GXM - 10);
- py = 5 + random2(GYM - 10);
+ pos = random_in_bounds();
- if (mgrd[px][py] != NON_MONSTER
- || (px == you.x_pos && py == you.y_pos))
- {
+ if (mgrd(pos) != NON_MONSTER || pos == you.pos())
continue;
- }
// Is the grid verboten?
- if (!unforbidden( coord_def(px, py), mmask ))
+ if (!unforbidden( pos, mmask ))
continue;
// don't generate monsters on top of teleport traps
- int trap = trap_at_xy(px, py);
+ int trap = trap_at_xy(pos.x, pos.y);
if (trap >= 0)
{
if (!can_place_on_trap(mon_type, env.trap[trap].type))
@@ -411,7 +421,7 @@ static int resolve_monster_type(int mon_type, proximity_type proximity,
// check whether there's a stair
// and whether it leads to another branch
- pval = near_stairs(coord_def(px, py), 1,
+ pval = near_stairs(pos, 1,
*stair_type, place.branch);
// no monsters spawned in the Temple
@@ -455,57 +465,49 @@ static int resolve_monster_type(int mon_type, proximity_type proximity,
return (mon_type);
}
-bool place_monster(int &id, int mon_type, int power, beh_type behaviour,
- int target, bool summoned, int px, int py, bool allow_bands,
- proximity_type proximity, int extra, int dur,
- unsigned mmask)
+int place_monster(mgen_data mg)
{
int band_size = 0;
- int band_monsters[BIG_BAND]; // band monster types
- int lev_mons = power; // final 'power'
- int i;
+ monster_type band_monsters[BIG_BAND]; // band monster types
int tries = 0;
int pval = 0;
dungeon_char_type stair_type = NUM_DCHAR_TYPES;
-
- // set initial id to -1 (unsuccessful create)
- id = -1;
+ int id = -1;
// (1) early out (summoned to occupied grid)
- if (summoned && mgrd[px][py] != NON_MONSTER)
+ if (mg.use_position() && mgrd(mg.pos) != NON_MONSTER)
return (false);
- mon_type =
- resolve_monster_type(mon_type, proximity, extra,
- px, py, mmask, &stair_type,
- &lev_mons);
+ mg.cls =
+ resolve_monster_type(mg.cls, mg.proximity, mg.base_type,
+ mg.pos, mg.map_mask,
+ &stair_type, &mg.power);
- if (mon_type == MONS_PROGRAM_BUG || mon_type == -1)
+ if (mg.cls == MONS_PROGRAM_BUG)
return (false);
// (3) decide on banding (good lord!)
band_size = 1;
- band_monsters[0] = mon_type;
+ band_monsters[0] = mg.cls;
- if (allow_bands)
+ if (mg.permit_bands())
{
- const band_type band = choose_band(mon_type, power, band_size);
+ const band_type band = choose_band(mg.cls, mg.power, band_size);
band_size ++;
-
- for (i = 1; i < band_size; i++)
- band_monsters[i] = band_member( band, power );
+ for (int i = 1; i < band_size; i++)
+ band_monsters[i] = _band_member( band, mg.power );
}
- if (proximity == PROX_NEAR_STAIRS)
+ if (mg.proximity == PROX_NEAR_STAIRS)
{
// for some cases disallow monsters on stairs
- if (mons_class_is_stationary( mon_type )
- || pval == 2
- && (mons_speed(mon_type) == 0 || grd[px][py] == DNGN_LAVA
- || grd[px][py] == DNGN_DEEP_WATER))
+ if (mons_class_is_stationary( mg.cls )
+ || (pval == 2
+ && (mons_speed(mg.cls) == 0 || grd(mg.pos) == DNGN_LAVA
+ || grd(mg.pos) == DNGN_DEEP_WATER)))
{
- proximity = PROX_AWAY_FROM_PLAYER;
+ mg.proximity = PROX_AWAY_FROM_PLAYER;
}
}
@@ -516,7 +518,7 @@ bool place_monster(int &id, int mon_type, int power, beh_type behaviour,
// player shoved out of the way?
bool shoved = false;
- if (!summoned)
+ if (!mg.use_position())
{
tries = 0;
@@ -525,104 +527,90 @@ bool place_monster(int &id, int mon_type, int power, beh_type behaviour,
// b) compatible
// c) in the 'correct' proximity to the player
dungeon_feature_type grid_wanted =
- habitat2grid( mons_habitat_by_type(mon_type) );
+ habitat2grid( mons_habitat_by_type(mg.cls) );
while (true)
{
- // handled above, won't change anymore
- if (proximity == PROX_NEAR_STAIRS && tries)
- {
- proximity = PROX_AWAY_FROM_PLAYER;
- tries = 0;
- }
// Dropped number of tries from 60.
- else if (tries >= 45)
+ if (tries++ >= 45)
return (false);
- tries ++;
-
// placement already decided for PROX_NEAR_STAIRS
- if (proximity != PROX_NEAR_STAIRS)
- {
- px = 5 + random2(GXM - 10);
- py = 5 + random2(GYM - 10);
- }
+ if (mg.proximity != PROX_NEAR_STAIRS)
+ mg.pos = random_in_bounds();
// Let's recheck these even for PROX_NEAR_STAIRS, just in case
-
// occupied?
- if (mgrd[px][py] != NON_MONSTER
- || px == you.x_pos && py == you.y_pos)
- {
+ if (mgrd(mg.pos) != NON_MONSTER || mg.pos == you.pos())
continue;
- }
// Is the monster happy where we want to put it?
- if (!grid_compatible(grid_wanted, grd[px][py], true))
+ if (!grid_compatible(grid_wanted, grd(mg.pos), true))
continue;
// Is the grid verboten?
- if (!unforbidden( coord_def(px, py), mmask ))
+ if (!unforbidden( mg.pos, mg.map_mask ))
continue;
// don't generate monsters on top of teleport traps
// (how did they get there?)
- int trap = trap_at_xy(px, py);
+ int trap = trap_at_xy(mg.pos.x, mg.pos.y);
if (trap >= 0)
{
- if (!can_place_on_trap(mon_type, env.trap[trap].type))
+ if (!can_place_on_trap(mg.cls, env.trap[trap].type))
continue;
}
// check proximity to player
proxOK = true;
- switch (proximity)
+ switch (mg.proximity)
{
- case PROX_ANYWHERE:
- if (grid_distance( you.x_pos, you.y_pos, px, py ) < 2 + random2(3))
- {
- proxOK = false;
- }
- break;
+ case PROX_ANYWHERE:
+ if (grid_distance( you.x_pos, you.y_pos,
+ mg.pos.x, mg.pos.y ) < 2 + random2(3))
+ {
+ proxOK = false;
+ }
+ break;
- case PROX_CLOSE_TO_PLAYER:
- case PROX_AWAY_FROM_PLAYER:
- close_to_player = (distance(you.x_pos, you.y_pos, px, py) < 64);
+ case PROX_CLOSE_TO_PLAYER:
+ case PROX_AWAY_FROM_PLAYER:
+ close_to_player =
+ (distance(you.x_pos, you.y_pos,
+ mg.pos.x, mg.pos.y) < 64);
+
+ if (mg.proximity == PROX_CLOSE_TO_PLAYER && !close_to_player
+ || mg.proximity == PROX_AWAY_FROM_PLAYER && close_to_player)
+ {
+ proxOK = false;
+ }
+ break;
- if (proximity == PROX_CLOSE_TO_PLAYER && !close_to_player
- || proximity == PROX_AWAY_FROM_PLAYER && close_to_player)
+ case PROX_NEAR_STAIRS:
+ if (pval == 2) // player on stairs
+ {
+ // 0 speed monsters can't shove player out of their way.
+ if (mons_speed(mg.cls) == 0)
{
proxOK = false;
+ break;
}
- break;
-
- case PROX_NEAR_STAIRS:
- if (pval == 2) // player on stairs
+ // swap the monster and the player spots, unless the
+ // monster was generated in lava or deep water.
+ if (grd(mg.pos) == DNGN_LAVA ||
+ grd(mg.pos) == DNGN_DEEP_WATER)
{
- // 0 speed monsters can't shove player out of their way.
- if (mons_speed(mon_type) == 0)
- {
- proxOK = false;
- break;
- }
- // swap the monster and the player spots, unless the
- // monster was generated in lava or deep water.
- if (grd[px][py] == DNGN_LAVA || grd[px][py] == DNGN_DEEP_WATER)
- {
- proxOK = false;
- break;
- }
- shoved = true;
- int tpx = px;
- int tpy = py;
- px = you.x_pos;
- py = you.y_pos;
- you.moveto(tpx, tpy);
+ proxOK = false;
+ break;
}
-
- proxOK = (pval > 0);
- break;
+ shoved = true;
+ coord_def mpos = mg.pos;
+ mg.pos = you.pos();
+ you.moveto(mpos);
+ }
+ proxOK = (pval > 0);
+ break;
}
if (!proxOK)
@@ -633,16 +621,14 @@ bool place_monster(int &id, int mon_type, int power, beh_type behaviour,
} // end while.. place first monster
}
- id = place_monster_aux( mon_type, behaviour, target, px, py, lev_mons,
- extra, true);
+ id = _place_monster_aux(mg, true);
- // now, forget about banding if the first placement failed, or there's too
- // many monsters already, or we successfully placed by stairs
- if (id < 0 || id+30 > MAX_MONSTERS)
- return false;
+ // Bail out now if we failed.
+ if (id == -1)
+ return (id);
// message to player from stairwell/gate appearance?
- if (see_grid(px, py) && proximity == PROX_NEAR_STAIRS)
+ if (see_grid(mg.pos) && mg.proximity == PROX_NEAR_STAIRS)
{
std::string msg;
@@ -672,41 +658,43 @@ bool place_monster(int &id, int mon_type, int power, beh_type behaviour,
}
// special case: must update the view for monsters created in player LOS
- viewwindow(1, false);
+ viewwindow(true, false);
}
- // monsters placed by stairs don't bring the whole band up/down/through
- // with them
- if (proximity == PROX_NEAR_STAIRS)
- return (true);
+ // now, forget about banding if the first placement failed, or there's too
+ // many monsters already, or we successfully placed by stairs
+ if (id >= MAX_MONSTERS - 30 || mg.proximity == PROX_NEAR_STAIRS)
+ return (id);
// Not PROX_NEAR_STAIRS, so will it will be part of a band, if there
// is any.
if (band_size > 1)
menv[id].flags |= MF_BAND_MEMBER;
+ mgen_data band_template = mg;
// (5) for each band monster, loop call to place_monster_aux().
- for (i = 1; i < band_size; i++)
+ for (int i = 1; i < band_size; i++)
{
+ if (band_monsters[i] == MONS_PROGRAM_BUG)
+ break;
+
+ band_template.cls = band_monsters[i];
const int band_id =
- place_monster_aux( band_monsters[i], behaviour, target, px, py,
- lev_mons, extra, false, dur);
-
+ _place_monster_aux( band_template, false );
if (band_id != -1 && band_id != NON_MONSTER)
menv[band_id].flags |= MF_BAND_MEMBER;
}
// placement of first monster, at least, was a success.
- return (true);
+ return (id);
}
-static int place_monster_aux( int mon_type, beh_type behaviour, int target,
- int px, int py, int power, int extra,
- bool first_band_member, int dur )
+static int _place_monster_aux( const mgen_data &mg,
+ bool first_band_member )
{
- int id, i;
+ int id = -1;
dungeon_feature_type grid_wanted = DNGN_UNSEEN;
- int fx = 0, fy = 0; // final x,y
+ coord_def fpos;
// gotta be able to pick an ID
for (id = 0; id < MAX_MONSTERS; id++)
@@ -714,42 +702,39 @@ static int place_monster_aux( int mon_type, beh_type behaviour, int target,
break;
if (id == MAX_MONSTERS)
- return -1;
+ return (-1);
menv[id].reset();
// setup habitat and placement
// If the space is occupied, try some neighbouring square instead.
- if (first_band_member && mgrd[px][py] == NON_MONSTER
- && (px != you.x_pos || py != you.y_pos))
+ if (first_band_member && mgrd(mg.pos) == NON_MONSTER
+ && mg.pos != you.pos())
{
- fx = px;
- fy = py;
+ fpos = mg.pos;
}
else
{
- grid_wanted = habitat2grid( mons_habitat_by_type(mon_type) );
+ grid_wanted = habitat2grid( mons_habitat_by_type(mg.cls) );
+ int i = 0;
// we'll try 1000 times for a good spot
- for (i = 0; i < 1000; i++)
+ for ( ; i < 1000; i++)
{
- fx = px + random2(7) - 3;
- fy = py + random2(7) - 3;
+ fpos = mg.pos + coord_def( random_range(-3, 3),
+ random_range(-3, 3) );
// occupied?
- if (mgrd[fx][fy] != NON_MONSTER
- || fx == you.x_pos && fy == you.y_pos)
- {
+ if (mgrd(fpos) != NON_MONSTER || fpos == you.pos())
continue;
- }
- if (!grid_compatible(grid_wanted, grd[fx][fy], true))
+ if (!grid_compatible(grid_wanted, grd(fpos), true))
continue;
// don't generate monsters on top of teleport traps
// (how do they get there?)
- int trap = trap_at_xy(fx, fy);
- if (trap >= 0 && !can_place_on_trap(mon_type, env.trap[trap].type))
+ int trap = trap_at_xy(fpos.x, fpos.y);
+ if (trap >= 0 && !can_place_on_trap(mg.cls, env.trap[trap].type))
continue;
// cool.. passes all tests
@@ -762,47 +747,48 @@ static int place_monster_aux( int mon_type, beh_type behaviour, int target,
}
// now, actually create the monster (wheeee!)
- menv[id].type = mon_type;
+ menv[id].type = mg.cls;
+ menv[id].base_monster = mg.base_type;
- menv[id].number = extra;
+ menv[id].number = mg.number;
- menv[id].x = fx;
- menv[id].y = fy;
+ menv[id].x = fpos.x;
+ menv[id].y = fpos.y;
// link monster into monster grid
- mgrd[fx][fy] = id;
+ mgrd(fpos) = id;
// generate a brand shiny new monster, or zombie
- if (mons_class_is_zombified(mon_type))
- define_zombie( id, extra, mon_type, power );
+ if (mons_class_is_zombified(mg.cls))
+ _define_zombie( id, mg.base_type, mg.cls, mg.power );
else
define_monster(id);
// The return of Boris is now handled in monster_die()...
// not setting this for Boris here allows for multiple Borises
// in the dungeon at the same time. -- bwr
- if (mons_is_unique(mon_type))
- you.unique_creatures[mon_type] = true;
+ if (mons_is_unique(mg.cls))
+ you.unique_creatures[mg.cls] = true;
- if (mons_class_flag(mon_type, M_INVIS))
+ if (mons_class_flag(mg.cls, M_INVIS))
menv[id].add_ench(ENCH_INVIS);
- if (mons_class_flag(mon_type, M_CONFUSED))
+ if (mons_class_flag(mg.cls, M_CONFUSED))
menv[id].add_ench(ENCH_CONFUSION);
- if (mon_type == MONS_SHAPESHIFTER)
+ if (mg.cls == MONS_SHAPESHIFTER)
menv[id].add_ench(ENCH_SHAPESHIFTER);
- if (mon_type == MONS_GLOWING_SHAPESHIFTER)
+ if (mg.cls == MONS_GLOWING_SHAPESHIFTER)
menv[id].add_ench(ENCH_GLOWING_SHAPESHIFTER);
- if (mon_type == MONS_GIANT_BAT || mon_type == MONS_UNSEEN_HORROR
- || mon_type == MONS_GIANT_BLOWFLY)
+ if (mg.cls == MONS_GIANT_BAT || mg.cls == MONS_UNSEEN_HORROR
+ || mg.cls == MONS_GIANT_BLOWFLY)
{
menv[id].flags |= MF_BATTY;
}
- if (monster_can_submerge(&menv[id], grd[fx][fy])
+ if (monster_can_submerge(&menv[id], grd(fpos))
&& !one_chance_in(5))
{
menv[id].add_ench(ENCH_SUBMERGED);
@@ -810,34 +796,33 @@ static int place_monster_aux( int mon_type, beh_type behaviour, int target,
menv[id].flags |= MF_JUST_SUMMONED;
- if (mon_type == MONS_DANCING_WEAPON && extra != 1) // ie not from spell
+ if (mg.cls == MONS_DANCING_WEAPON && mg.number != 1) // ie not from spell
{
- give_item( id, power );
+ give_item( id, mg.power );
if (menv[id].inv[MSLOT_WEAPON] != NON_ITEM)
menv[id].colour = mitm[ menv[id].inv[MSLOT_WEAPON] ].colour;
}
- else if (mons_itemuse(mon_type) >= MONUSE_STARTING_EQUIPMENT)
+ else if (mons_itemuse(mg.cls) >= MONUSE_STARTING_EQUIPMENT)
{
- give_item( id, power );
+ give_item( id, mg.power );
// Give these monsters a second weapon -- bwr
- if (mons_wields_two_weapons(static_cast<monster_type>(mon_type)))
- give_item( id, power );
+ if (mons_wields_two_weapons(mg.cls))
+ give_item( id, mg.power );
unwind_var<int> save_speedinc(menv[id].speed_increment);
menv[id].wield_melee_weapon(false);
}
// give manticores 8 to 16 spike volleys.
- // they're not spellcasters so this doesn't screw anything up.
- if (mon_type == MONS_MANTICORE)
+ if (mg.cls == MONS_MANTICORE)
menv[id].number = 8 + random2(9);
// set attitude, behaviour and target
menv[id].attitude = ATT_HOSTILE;
- menv[id].behaviour = behaviour;
+ menv[id].behaviour = mg.behaviour;
- if (mons_is_statue(mon_type))
+ if (mons_is_statue(mg.cls))
menv[id].behaviour = BEH_WANDER;
menv[id].foe_memory = 0;
@@ -846,22 +831,22 @@ static int place_monster_aux( int mon_type, beh_type behaviour, int target,
// monster wander.. if you want sleeping
// hostiles, use BEH_SLEEP since the default
// attitude is hostile.
- if (behaviour > NUM_BEHAVIOURS)
+ if (mg.behaviour > NUM_BEHAVIOURS)
{
- if (behaviour == BEH_FRIENDLY || behaviour == BEH_GOD_GIFT)
+ if (mg.behaviour == BEH_FRIENDLY || mg.behaviour == BEH_GOD_GIFT)
menv[id].attitude = ATT_FRIENDLY;
- if (behaviour == BEH_NEUTRAL)
+ if (mg.behaviour == BEH_NEUTRAL)
menv[id].attitude = ATT_NEUTRAL;
menv[id].behaviour = BEH_WANDER;
}
// dur should always be 1-6 for monsters that can be abjured.
- if (dur >= 1 && dur <= 6)
- menv[id].mark_summoned( dur, true );
+ if (mg.abjuration_duration >= 1 && mg.abjuration_duration <= 6)
+ menv[id].mark_summoned( mg.abjuration_duration, true );
- menv[id].foe = target;
+ menv[id].foe = mg.foe;
// Initialise pandemonium demons
if (menv[id].type == MONS_PANDEMONIUM_DEMON)
@@ -872,14 +857,224 @@ static int place_monster_aux( int mon_type, beh_type behaviour, int target,
menv[id].pandemon_init();
}
- mark_interesting_monst(&menv[id], behaviour);
+ mark_interesting_monst(&menv[id], mg.behaviour);
if (player_monster_visible(&menv[id]) && mons_near(&menv[id]))
seen_monster(&menv[id]);
return (id);
-} // end place_monster_aux()
+}
+
+static monster_type _pick_random_zombie()
+{
+ static std::vector<monster_type> zombifiable;
+ if (zombifiable.empty())
+ {
+ for (int i = 0; i < NUM_MONSTERS; ++i)
+ {
+ if (mons_species(i) != i || i == MONS_PROGRAM_BUG)
+ continue;
+
+ const monster_type mcls = static_cast<monster_type>(i);
+ if (!mons_zombie_size(mcls))
+ continue;
+
+ zombifiable.push_back(mcls);
+ }
+ }
+ return (zombifiable[ random2(zombifiable.size()) ]);
+}
+
+static void _define_zombie( int mid, monster_type ztype,
+ monster_type cs, int power )
+{
+ monster_type mons_sec2 = MONS_PROGRAM_BUG;
+ int zombie_size = 0;
+ bool ignore_rarity = false;
+ monster_type cls = MONS_PROGRAM_BUG;
+
+ if (power > 27)
+ power = 27;
+
+ // set size based on zombie class (cs)
+ switch (cs)
+ {
+ case MONS_ZOMBIE_SMALL:
+ case MONS_SIMULACRUM_SMALL:
+ case MONS_SKELETON_SMALL:
+ zombie_size = Z_SMALL;
+ break;
+
+ case MONS_ZOMBIE_LARGE:
+ case MONS_SIMULACRUM_LARGE:
+ case MONS_SKELETON_LARGE:
+ zombie_size = Z_BIG;
+ break;
+
+ case MONS_SPECTRAL_THING:
+ zombie_size = -1;
+ break;
+
+ default:
+ // this should NEVER happen.
+ perror("\ncreate_zombie() got passed incorrect zombie type!\n");
+ end(0);
+ break;
+ }
+
+ // that is, random creature from which to fashion undead
+ if (ztype == MONS_PROGRAM_BUG)
+ {
+ // how OOD this zombie can be.
+ int relax = 5;
+
+ // pick an appropriate creature to make a zombie out of,
+ // levelwise. The old code was generating absolutely
+ // incredible OOD zombies.
+ while (true)
+ {
+ cls = _pick_random_zombie();
+
+ // on certain branches, zombie creation will fail if we use
+ // the mons_rarity() functions, because (for example) there
+ // are NO zombifiable "native" abyss creatures. Other branches
+ // where this is a problem are hell levels and the crypt.
+ // we have to watch for summoned zombies on other levels, too,
+ // such as the Temple, HoB, and Slime Pits.
+ if (you.level_type != LEVEL_DUNGEON
+ || player_in_hell()
+ || player_in_branch( BRANCH_HALL_OF_ZOT )
+ || player_in_branch( BRANCH_VESTIBULE_OF_HELL )
+ || player_in_branch( BRANCH_ECUMENICAL_TEMPLE )
+ || player_in_branch( BRANCH_CRYPT )
+ || player_in_branch( BRANCH_TOMB )
+ || player_in_branch( BRANCH_HALL_OF_BLADES )
+ || player_in_branch( BRANCH_SNAKE_PIT )
+ || player_in_branch( BRANCH_SLIME_PITS )
+ || one_chance_in(1000))
+ {
+ ignore_rarity = true;
+ }
+
+ // don't make out-of-rarity zombies when we don't have to
+ if (!ignore_rarity && mons_rarity(cls) == 0)
+ continue;
+
+ // if skeleton, monster must have a skeleton
+ if ((cs == MONS_SKELETON_SMALL || cs == MONS_SKELETON_LARGE)
+ && !mons_skeleton(cls))
+ {
+ continue;
+ }
+
+ // size must match, but you can make a spectral thing out
+ // of anything.
+ if (mons_zombie_size(cls) != zombie_size && zombie_size != -1)
+ continue;
+
+ // hack -- non-dungeon zombies are always made out of nastier
+ // monsters
+ if (you.level_type != LEVEL_DUNGEON && mons_power(cls) > 8)
+ break;
+
+ // check for rarity.. and OOD - identical to mons_place()
+ int level, diff, chance;
+
+ level = mons_level( cls ) - 4;
+ diff = level - power;
+
+ chance = (ignore_rarity) ? 100
+ : mons_rarity(cls) - (diff * diff) / 2;
+
+ if (power > level - relax && power < level + relax
+ && random2avg(100, 2) <= chance)
+ {
+ break;
+ }
+
+ // every so often, we'll relax the OOD restrictions. Avoids
+ // infinite loops (if we don't do this, things like creating
+ // a large skeleton on level 1 may hang the game!)
+ if (one_chance_in(5))
+ relax++;
+ }
+
+ // set type and secondary appropriately
+ menv[mid].base_monster = cls;
+ mons_sec2 = cls;
+ }
+ else
+ {
+ menv[mid].base_monster = mons_species(ztype);
+ mons_sec2 = menv[mid].base_monster;
+ }
+
+ menv[mid].type = menv[mid].base_monster;
+
+ define_monster(mid);
+
+ menv[mid].hit_points = hit_points( menv[mid].hit_dice, 6, 5 );
+ menv[mid].max_hit_points = menv[mid].hit_points;
+
+ menv[mid].ac -= 2;
+
+ if (menv[mid].ac < 0)
+ menv[mid].ac = 0;
+
+ menv[mid].ev -= 5;
+
+ if (menv[mid].ev < 0)
+ menv[mid].ev = 0;
+ menv[mid].speed -= 2;
+
+ if (menv[mid].speed < 3)
+ menv[mid].speed = 3;
+
+ menv[mid].speed_increment = 70;
+
+ if (cs == MONS_ZOMBIE_SMALL || cs == MONS_ZOMBIE_LARGE)
+ {
+ menv[mid].type = ((mons_zombie_size(menv[mid].base_monster) == Z_BIG)
+ ? MONS_ZOMBIE_LARGE : MONS_ZOMBIE_SMALL);
+ }
+ else if (cs == MONS_SKELETON_SMALL || cs == MONS_SKELETON_LARGE)
+ {
+ menv[mid].hit_points = hit_points( menv[mid].hit_dice, 5, 4 );
+ menv[mid].max_hit_points = menv[mid].hit_points;
+
+ menv[mid].ac -= 4;
+
+ if (menv[mid].ac < 0)
+ menv[mid].ac = 0;
+
+ menv[mid].ev -= 2;
+
+ if (menv[mid].ev < 0)
+ menv[mid].ev = 0;
+
+ menv[mid].type = ((mons_zombie_size( menv[mid].base_monster ) == Z_BIG)
+ ? MONS_SKELETON_LARGE : MONS_SKELETON_SMALL);
+ }
+ else if (cs == MONS_SIMULACRUM_SMALL || cs == MONS_SIMULACRUM_LARGE)
+ {
+ // Simulacrum aren't tough, but you can create piles of them. -- bwr
+ menv[mid].hit_points = hit_points( menv[mid].hit_dice, 1, 4 );
+ menv[mid].max_hit_points = menv[mid].hit_points;
+ menv[mid].type = ((mons_zombie_size( menv[mid].base_monster ) == Z_BIG)
+ ? MONS_SIMULACRUM_LARGE : MONS_SIMULACRUM_SMALL);
+ }
+ else if (cs == MONS_SPECTRAL_THING)
+ {
+ menv[mid].hit_points = hit_points( menv[mid].hit_dice, 4, 4 );
+ menv[mid].max_hit_points = menv[mid].hit_points;
+ menv[mid].ac += 4;
+ menv[mid].type = MONS_SPECTRAL_THING;
+ }
+
+ menv[mid].base_monster = mons_sec2;
+ menv[mid].colour = mons_class_colour(cs);
+}
static band_type choose_band( int mon_type, int power, int &band_size )
{
@@ -1177,13 +1372,13 @@ static band_type choose_band( int mon_type, int power, int &band_size )
return (band);
}
-static int band_member(band_type band, int power)
+static monster_type _band_member(band_type band, int power)
{
- int mon_type = -1;
+ monster_type mon_type = MONS_PROGRAM_BUG;
int temp_rand;
if (band == BAND_NO_BAND)
- return -1;
+ return (MONS_PROGRAM_BUG);
switch (band)
{
@@ -1281,14 +1476,16 @@ static int band_member(band_type band, int power)
case BAND_PANDEMONIUM_DEMON:
if (one_chance_in(7))
- mon_type = random_choose_weighted(50, MONS_LICH,
- 10, MONS_ANCIENT_LICH,
- 0);
+ mon_type = static_cast<monster_type>(
+ random_choose_weighted(50, MONS_LICH,
+ 10, MONS_ANCIENT_LICH,
+ 0));
else if (one_chance_in(6))
- mon_type = random_choose_weighted(50, MONS_ABOMINATION_SMALL,
- 40, MONS_ABOMINATION_LARGE,
- 10, MONS_TENTACLED_MONSTROSITY,
- 0);
+ mon_type = static_cast<monster_type>(
+ random_choose_weighted(50, MONS_ABOMINATION_SMALL,
+ 40, MONS_ABOMINATION_LARGE,
+ 10, MONS_TENTACLED_MONSTROSITY,
+ 0));
else
mon_type =
summon_any_demon(
@@ -1493,7 +1690,7 @@ void mark_interesting_monst(struct monsters* monster, beh_type behaviour)
// PUBLIC FUNCTION -- mons_place().
-static int pick_zot_exit_defender()
+static monster_type pick_zot_exit_defender()
{
if (one_chance_in(11))
return (MONS_PANDEMONIUM_DEMON);
@@ -1508,12 +1705,13 @@ static int pick_zot_exit_defender()
(temp_rand > 13) ? MONS_RED_DEVIL // 7.25%
: MONS_PIT_FIEND); // 5.07%
- return (mon_type);
+ return static_cast<monster_type>(mon_type);
}
-int mons_place( int mon_type, beh_type behaviour, int target, bool summoned,
- int px, int py, int level_type, proximity_type proximity,
- int extra, int dur, bool permit_bands )
+int mons_place( mgen_data mg )
+ // int mon_type, beh_type behaviour, int target, bool summoned,
+ // int px, int py, int level_type, proximity_type proximity,
+ // int extra, int dur, bool permit_bands )
{
int mon_count = 0;
for (int il = 0; il < MAX_MONSTERS; il++)
@@ -1522,85 +1720,80 @@ int mons_place( int mon_type, beh_type behaviour, int target, bool summoned,
mon_count++;
}
- if (mon_type == WANDERING_MONSTER)
+ if (mg.cls == WANDERING_MONSTER)
{
- if (mon_count > 150)
+ if (mon_count > MAX_MONSTERS - 50)
return (-1);
- mon_type = RANDOM_MONSTER;
+ mg.cls = RANDOM_MONSTER;
}
// all monsters have been assigned? {dlb}
- if (mon_count > MAX_MONSTERS - 2)
+ if (mon_count >= MAX_MONSTERS - 1)
return (-1);
// this gives a slight challenge to the player as they ascend the
// dungeon with the Orb
- if (you.char_direction == GDT_ASCENDING && mon_type == RANDOM_MONSTER
- && you.level_type == LEVEL_DUNGEON && !summoned)
+ if (you.char_direction == GDT_ASCENDING && mg.cls == RANDOM_MONSTER
+ && you.level_type == LEVEL_DUNGEON && !mg.summoned())
{
- mon_type = pick_zot_exit_defender();
- permit_bands = true;
+ mg.cls = pick_zot_exit_defender();
+ mg.flags |= MG_PERMIT_BANDS;
}
- if (mon_type == RANDOM_MONSTER
- || level_type == LEVEL_PANDEMONIUM)
- {
- permit_bands = true;
- }
+ if (mg.cls == RANDOM_MONSTER || mg.level_type == LEVEL_PANDEMONIUM)
+ mg.flags |= MG_PERMIT_BANDS;
int mid = -1;
// translate level_type
- int power;
-
- switch (level_type)
+ switch (mg.level_type)
{
case LEVEL_PANDEMONIUM:
- power = 52; // sigh..
+ mg.power = 52; // sigh..
break;
case LEVEL_ABYSS:
- power = 51;
+ mg.power = 51;
break;
- case LEVEL_DUNGEON: // intentional fallthrough
+ case LEVEL_DUNGEON:
default:
- power = you.your_level;
+ mg.power = you.your_level;
break;
}
- if (!place_monster( mid, mon_type, power, behaviour, target, summoned,
- px, py, permit_bands, proximity, extra, dur ))
- {
+ mid = place_monster(mg);
+ if (mid == -1)
return (-1);
- }
+
+ monsters *creation = &menv[mid];
- if (mid != -1)
+ // look at special cases: CHARMED, FRIENDLY, HOSTILE, GOD_GIFT
+ // alert summoned being to player's presence
+ if (mg.behaviour > NUM_BEHAVIOURS)
{
- struct monsters *const creation = &menv[mid];
-
- // look at special cases: CHARMED, FRIENDLY, HOSTILE, GOD_GIFT
- // alert summoned being to player's presence
- if (behaviour > NUM_BEHAVIOURS)
- {
- if (behaviour == BEH_FRIENDLY || behaviour == BEH_GOD_GIFT)
- creation->flags |= MF_CREATED_FRIENDLY;
+ if (mg.behaviour == BEH_FRIENDLY || mg.behaviour == BEH_GOD_GIFT)
+ creation->flags |= MF_CREATED_FRIENDLY;
- if (behaviour == BEH_GOD_GIFT)
- creation->flags |= MF_GOD_GIFT;
+ if (mg.behaviour == BEH_GOD_GIFT)
+ creation->flags |= MF_GOD_GIFT;
- if (behaviour == BEH_CHARMED)
- {
- creation->attitude = ATT_HOSTILE;
- creation->add_ench(ENCH_CHARM);
- }
+ if (!(mg.flags & MG_FORCE_BEH) && player_angers_monster(creation))
+ creation->attitude = ATT_HOSTILE;
- // make summoned being aware of player's presence
- behaviour_event(creation, ME_ALERT, MHITYOU);
+ if (mg.behaviour == BEH_CHARMED)
+ {
+ creation->attitude = ATT_HOSTILE;
+ creation->add_ench(ENCH_CHARM);
}
- }
+ // make summoned being aware of player's presence
+ behaviour_event(creation, ME_ALERT, MHITYOU);
+
+ if (creation->type == MONS_RAKSHASA_FAKE && !one_chance_in(3))
+ creation->add_ench(ENCH_INVIS);
+ }
return (mid);
-} // end mons_place()
+}
static dungeon_feature_type _monster_habitat_feature(int mtype)
{
@@ -1680,7 +1873,7 @@ coord_def find_newmons_square_contiguous(monster_type mons_class,
return (in_bounds(p)? p : coord_def(-1, -1));
}
-coord_def find_newmons_square(int mons_class, int x, int y)
+coord_def find_newmons_square(int mons_class, const coord_def &p)
{
FixedVector < char, 2 > empty;
coord_def pos(-1, -1);
@@ -1696,7 +1889,7 @@ coord_def find_newmons_square(int mons_class, int x, int y)
// Might be better if we chose a space and tried to match the monster
// to it in the case of RANDOM_MONSTER, that way if the target square
// is surrounded by water of lava this function would work. -- bwr
- if (empty_surrounds( x, y, spcw, 2, true, empty ))
+ if (empty_surrounds( p.x, p.y, spcw, 2, true, empty ))
{
pos.x = empty[0];
pos.y = empty[1];
@@ -1727,81 +1920,28 @@ bool player_angers_monster(monsters *creation)
return (false);
}
-int create_monster( int cls, int dur, beh_type beha, int cr_x, int cr_y,
- int hitting, int zsec, bool permit_bands,
- bool force_place, bool force_behaviour,
- bool player_made )
+int create_monster( mgen_data mg )
{
int summd = -1;
- coord_def pos;
- if (force_place && mons_class_can_pass(cls, grd[cr_x][cr_y])
- && mgrd[cr_x][cr_y] == NON_MONSTER
- && (cr_x != you.x_pos || cr_y != you.y_pos))
- {
- pos.x = cr_x;
- pos.y = cr_y;
- }
- else
- pos = find_newmons_square(cls, cr_x, cr_y);
+ if (!(mg.force_place()
+ && in_bounds(mg.pos)
+ && mons_class_can_pass(mg.cls, grd(mg.pos))
+ && mgrd(mg.pos) == NON_MONSTER
+ && mg.pos != you.pos()))
+ mg.pos = find_newmons_square(mg.cls, mg.pos);
- if (pos.x != -1 && pos.y != -1)
- {
- summd = mons_place( cls, beha, hitting, true, pos.x, pos.y,
- you.level_type, PROX_ANYWHERE, zsec,
- dur, permit_bands );
- }
+ if (in_bounds(mg.pos))
+ summd = mons_place( mg );
// determine whether creating a monster is successful (summd != -1) {dlb}:
// then handle the outcome {dlb}:
- if (summd == -1)
- {
- if (see_grid( cr_x, cr_y ))
- mpr("You see a puff of smoke.");
- }
- else
- {
- monsters *const creation = &menv[summd];
-
- // dur should always be ENCH_ABJ_xx
- if (dur >= 1 && dur <= 6)
- creation->mark_summoned( dur, true );
-
- // player summons do not give XP or other bonuses
- // (you can still train skills on them though)
- if ( player_made )
- creation->flags |= MF_CREATED_FRIENDLY;
-
- // look at special cases: CHARMED, FRIENDLY, HOSTILE, GOD_GIFT
- // alert summoned being to player's presence
- if (beha > NUM_BEHAVIOURS)
- {
- if (beha == BEH_FRIENDLY || beha == BEH_GOD_GIFT)
- creation->flags |= MF_CREATED_FRIENDLY;
-
- if (beha == BEH_GOD_GIFT)
- creation->flags |= MF_GOD_GIFT;
-
- if (!force_behaviour && player_angers_monster(creation))
- beha = BEH_HOSTILE;
-
- if (beha == BEH_CHARMED)
- {
- creation->attitude = ATT_HOSTILE;
- creation->add_ench(ENCH_CHARM);
- }
-
- // make summoned being aware of player's presence
- behaviour_event(creation, ME_ALERT, MHITYOU);
- }
-
- if (creation->type == MONS_RAKSHASA_FAKE && !one_chance_in(3))
- creation->add_ench(ENCH_INVIS);
- }
+ if (summd == -1 && see_grid( mg.pos ))
+ mpr("You see a puff of smoke.");
// the return value is either -1 (failure of some sort)
// or the index of the monster placed (if I read things right) {dlb}
return (summd);
-} // end create_monster()
+}
bool empty_surrounds(int emx, int emy, dungeon_feature_type spc_wanted,