/** * Place 'num' sleeping monsters near (x, y). * \param c the current chunk * \param y1 co-ordinates to place the monsters near * \param x1 co-ordinates to place the monsters near * \param depth generation depth * \param num number of monsters to place */ void vault_monsters(struct chunk *c, int y1, int x1, int depth, int num) { int k, i, y, x; /* If the starting location is illegal, don't even start */ if (!square_in_bounds(c, y1, x1)) return; /* Try to summon "num" monsters "near" the given location */ for (k = 0; k < num; k++) { /* Try nine locations */ for (i = 0; i < 9; i++) { int d = 1; /* Pick a nearby location */ scatter(c, &y, &x, y1, x1, d, true); /* Require "empty" floor grids */ if (!square_isempty(c, y, x)) continue; /* Place the monster (allow groups) */ pick_and_place_monster(c, y, x, depth, true, true, ORIGIN_DROP_SPECIAL); break; } } }
/** * Lets the given monster attempt to reproduce. * * Note that "reproduction" REQUIRES empty space. * * Returns true if the monster successfully reproduced. */ bool multiply_monster(const struct monster *mon) { int i, y, x; bool result = false; /* Try up to 18 times */ for (i = 0; i < 18; i++) { int d = 1; /* Pick a location */ scatter(cave, &y, &x, mon->fy, mon->fx, d, true); /* Require an "empty" floor grid */ if (!square_isempty(cave, y, x)) continue; /* Create a new monster (awake, no groups) */ result = place_new_monster(cave, y, x, mon->race, false, false, ORIGIN_DROP_BREED); /* Done */ break; } /* Result */ return (result); }
/** * Choose a good hiding place near a monster for it to run toward. * * Pack monsters will use this to "ambush" the player and lure him out * of corridors into open space so they can swarm him. * * Return true if a good location is available. */ static bool get_move_find_hiding(struct chunk *c, struct monster *mon) { struct loc mon_grid = loc(mon->fx, mon->fy); struct loc player_grid = loc(player->px, player->py); int i, y, x, dy, dx, d, dis; int gy = 0, gx = 0, gdis = 999, min; const int *y_offsets, *x_offsets; /* Closest distance to get */ min = distance(player_grid, mon_grid) * 3 / 4 + 2; /* Start with adjacent locations, spread further */ for (d = 1; d < 10; d++) { /* Get the lists of points with a distance d from monster */ y_offsets = dist_offsets_y[d]; x_offsets = dist_offsets_x[d]; /* Check the locations */ for (i = 0, dx = x_offsets[0], dy = y_offsets[0]; dx != 0 || dy != 0; i++, dx = x_offsets[i], dy = y_offsets[i]) { y = mon_grid.y + dy; x = mon_grid.x + dx; /* Skip illegal locations */ if (!square_in_bounds_fully(c, y, x)) continue; /* Skip occupied locations */ if (!square_isempty(c, y, x)) continue; /* Check for hidden, available grid */ if (!square_isview(c, y, x) && projectable(c, mon_grid.y, mon_grid.x, y, x, PROJECT_STOP)) { /* Calculate distance from player */ dis = distance(loc(x, y), player_grid); /* Remember if closer than previous */ if (dis < gdis && dis >= min) { gy = y; gx = x; gdis = dis; } } } /* Check for success */ if (gdis < 999) { /* Good location */ mon->target.grid = loc(gx, gy); return (true); } } /* No good place */ return (false); }
/** * Place monsters, up to the number asked for, in a rectangle centered on * y0, x0. Accept values for monster depth, symbol, and maximum vertical * and horizontal displacement. Call monster restriction functions if * needed. * \param c the current chunk being generated * \param type the type of monster (see comments to mon_restrict()) * \param depth selection depth * \param num the number of monsters to try and place - inexact due to groups * \param y0 * \param x0 the centre of the rectangle for monster placement * \param dy * \param dx the dimensions of the rectangle * \param origin the origin for monster drops * * Return prematurely if the code starts looping too much (this may happen * if y0 or x0 are out of bounds, or the area is already occupied). */ void spread_monsters(struct chunk *c, const char *type, int depth, int num, int y0, int x0, int dy, int dx, byte origin) { int i, j; /* Limits on loops */ int count; int y = y0, x = x0; int start_mon_num = c->mon_max; /* Restrict monsters. Allow uniques. Leave area empty if none found. */ if (!mon_restrict(type, depth, true)) return; /* Build the monster probability table. */ if (!get_mon_num(depth)) return; /* Try to summon monsters within our rectangle of effect. */ for (count = 0, i = 0; ((count < num) && (i < 50)); i++) { /* Get a location */ if ((dy == 0) && (dx == 0)) { y = y0; x = x0; if (!square_in_bounds(c, y, x)) return; } else { for (j = 0; j < 10; j++) { y = rand_spread(y0, dy); x = rand_spread(x0, dx); if (!square_in_bounds(c, y, x)) { if (j < 9) continue; else return; } break; } } /* Require "empty" floor grids */ if (!square_isempty(c, y, x)) continue; /* Place the monster (sleeping, allow groups) */ pick_and_place_monster(c, y, x, depth, true, true, origin); /* Rein in monster groups and escorts a little. */ if (c->mon_max - start_mon_num > num * 2) break; /* Count the monster(s), reset the loop count */ count++; i = 0; } /* Remove monster restrictions. */ (void) mon_restrict(NULL, depth, true); }
/** * Place a trap near (x, y), with a given displacement. * \param c the current chunk * \param y co-ordinates to place the trap near * \param x co-ordinates to place the trap near * \param yd how far afield to look for a place * \param xd how far afield to look for a place */ static void vault_trap_aux(struct chunk *c, int y, int x, int yd, int xd) { int tries, y1, x1; /* Find a nearby empty grid and place a trap */ for (tries = 0; tries <= 5; tries++) { find_nearby_grid(c, &y1, y, yd, &x1, x, xd); if (!square_isempty(c, y1, x1)) continue; square_add_trap(c, y1, x1); break; } }
/* Make traps */ static void project_feature_handler_MAKE_TRAP(project_feature_handler_context_t *context) { const int x = context->x; const int y = context->y; /* Require an "empty", non-warded floor grid */ if (!square_isempty(cave, y, x)) return; if (square_iswarded(cave, y, x)) return; /* Create a trap */ square_add_trap(cave, y, x); context->obvious = true; }
/** * Attempts to place a group of monsters of race `r_idx` around * the given location. The number of monsters to place is `total`. * * If `sleep` is true, the monster is placed with its default sleep value, * which is given in monster.txt. * * `origin` is the item origin to use for any monster drops (e.g. ORIGIN_DROP, * ORIGIN_DROP_PIT, etc.) */ static bool place_new_monster_group(struct chunk *c, int y, int x, struct monster_race *race, bool sleep, int total, byte origin) { int n, i; int hack_n; /* x and y coordinates of the placed monsters */ byte hack_y[GROUP_MAX]; byte hack_x[GROUP_MAX]; assert(race); /* Start on the monster */ hack_n = 1; hack_x[0] = x; hack_y[0] = y; /* Puddle monsters, breadth first, up to total */ for (n = 0; (n < hack_n) && (hack_n < total); n++) { /* Grab the location */ int hx = hack_x[n]; int hy = hack_y[n]; /* Check each direction, up to total */ for (i = 0; (i < 8) && (hack_n < total); i++) { int mx = hx + ddx_ddd[i]; int my = hy + ddy_ddd[i]; /* Walls and Monsters block flow */ if (!square_isempty(c, my, mx)) continue; /* Attempt to place another monster */ if (place_new_monster_one(c, my, mx, race, sleep, origin)) { /* Add it to the "hack" set */ hack_y[hack_n] = my; hack_x[hack_n] = mx; hack_n++; } } } /* Success */ return (true); }
/** * Picks a monster race, makes a new monster of that race, then attempts to * place it in the dungeon at least `dis` away from the player. The monster * race chosen will be appropriate for dungeon level equal to `depth`. * * If `sleep` is true, the monster is placed with its default sleep value, * which is given in monster.txt. * * Returns true if we successfully place a monster. */ bool pick_and_place_distant_monster(struct chunk *c, struct loc loc, int dis, bool sleep, int depth) { int py = loc.y; int px = loc.x; int y = 0, x = 0; int attempts_left = 10000; assert(c); /* Find a legal, distant, unoccupied, space */ while (--attempts_left) { /* Pick a location */ y = randint0(c->height); x = randint0(c->width); /* Require "naked" floor grid */ if (!square_isempty(c, y, x)) continue; /* Do not put random monsters in marked rooms. */ if ((!character_dungeon) && square_ismon_restrict(c, y, x)) continue; /* Accept far away grids */ if (distance(y, x, py, px) > dis) break; } if (!attempts_left) { if (OPT(cheat_xtra) || OPT(cheat_hear)) msg("Warning! Could not allocate a new monster."); return false; } /* Attempt to place the monster, allow groups */ if (pick_and_place_monster(c, y, x, depth, sleep, true, ORIGIN_DROP)) return (true); /* Nope */ return (false); }
/** * Determine if there is a space near the selected spot in which * a summoned creature can appear */ static bool summon_possible(int y1, int x1) { int y, x; /* Start at the location, and check 2 grids in each dir */ for (y = y1 - 2; y <= y1 + 2; y++) { for (x = x1 - 2; x <= x1 + 2; x++) { /* Ignore illegal locations */ if (!square_in_bounds(cave, y, x)) continue; /* Only check a circular area */ if (distance(y1, x1, y, x) > 2) continue; /* Hack: no summon on glyph of warding */ if (square_iswarded(cave, y, x)) continue; /* If it's empty floor grid in line of sight, we're good */ if (square_isempty(cave, y, x) && los(cave, y1, x1, y, x)) return (true); } } return false; }
/** * Funtion for placing appropriate monsters in a room of chambers * * \param c the current chunk being generated * \param y1 the limits of the vault * \param x1 the limits of the vault * \param y2 the limits of the vault * \param x2 the limits of the vault * \param name the name of the monster type for use in mon_select() * \param area the total room area, used for scaling monster quantity */ void get_chamber_monsters(struct chunk *c, int y1, int x1, int y2, int x2, char *name, int area) { int i, y, x; s16b monsters_left, depth; bool random = one_in_(20); /* Get a legal depth. */ depth = c->depth + randint0(11) - 5; /* Choose a pit profile, using that depth. */ if (!random) set_pit_type(depth, 0); /* Allow (slightly) tougher monsters. */ depth = c->depth + (c->depth < 60 ? c->depth / 12 : 5); /* Set monster generation restrictions. Occasionally random. */ if (random) { if (!mon_restrict("random", depth, true)) return; my_strcpy(name, "random", sizeof(name)); } else { if (!mon_restrict(dun->pit_type->name, depth, true)) return; my_strcpy(name, dun->pit_type->name, sizeof(name)); } /* Build the monster probability table. */ if (!get_mon_num(depth)) { (void) mon_restrict(NULL, depth, false); name = NULL; return; } /* No normal monsters. */ generate_mark(c, y1, x1, y2, x2, SQUARE_MON_RESTRICT); /* Allow about a monster every 20-30 grids. */ monsters_left = area / (30 - c->depth / 10); /* Place the monsters. */ for (i = 0; i < 300; i++) { /* Check for early completion. */ if (!monsters_left) break; /* Pick a random in-room square. */ y = y1 + randint0(1 + ABS(y2 - y1)); x = x1 + randint0(1 + ABS(x2 - x1)); /* Require a passable square with no monster in it already. */ if (!square_isempty(c, y, x)) continue; /* Place a single monster. Sleeping 2/3rds of the time. */ pick_and_place_monster(c, y, x, c->depth, (randint0(3) != 0), false, ORIGIN_DROP_SPECIAL); /* One less monster to place. */ monsters_left--; } /* Remove our restrictions. */ (void) mon_restrict(NULL, depth, false); }
/** * Choose "logical" directions for monster movement */ static bool get_moves(struct chunk *c, struct monster *mon, int *dir) { int py = player->py; int px = player->px; int y, x; /* Monsters will run up to z_info->flee_range grids out of sight */ int flee_range = z_info->max_sight + z_info->flee_range; bool done = false; /* Calculate range */ find_range(mon); /* Flow towards the player */ if (get_moves_flow(c, mon)) { /* Extract the "pseudo-direction" */ y = mon->ty - mon->fy; x = mon->tx - mon->fx; } else { /* Head straight for the player */ y = player->py - mon->fy; x = player->px - mon->fx; } /* Normal animal packs try to get the player out of corridors. */ if (rf_has(mon->race->flags, RF_GROUP_AI) && !flags_test(mon->race->flags, RF_SIZE, RF_PASS_WALL, RF_KILL_WALL, FLAG_END)) { int i, open = 0; /* Count empty grids next to player */ for (i = 0; i < 8; i++) { int ry = py + ddy_ddd[i]; int rx = px + ddx_ddd[i]; /* Check grid around the player for room interior (room walls count) * or other empty space */ if (square_ispassable(c, ry, rx) || square_isroom(c, ry, rx)) { /* One more open grid */ open++; } } /* Not in an empty space and strong player */ if ((open < 7) && (player->chp > player->mhp / 2)) { /* Find hiding place */ if (find_hiding(c, mon)) { done = true; y = mon->ty - mon->fy; x = mon->tx - mon->fx; } } } /* Apply fear */ if (!done && (mon->min_range == flee_range)) { /* Try to find safe place */ if (!find_safety(c, mon)) { /* Just leg it away from the player */ y = (-y); x = (-x); } else { /* Set a course for the safe place */ get_moves_fear(c, mon); y = mon->ty - mon->fy; x = mon->tx - mon->fx; } done = true; } /* Monster groups try to surround the player */ if (!done && rf_has(mon->race->flags, RF_GROUP_AI)) { int i, yy = mon->ty, xx = mon->tx; /* If we are not already adjacent */ if (mon->cdis > 1) { /* Find an empty square near the player to fill */ int tmp = randint0(8); for (i = 0; i < 8; i++) { /* Pick squares near player (pseudo-randomly) */ yy = py + ddy_ddd[(tmp + i) & 7]; xx = px + ddx_ddd[(tmp + i) & 7]; /* Ignore filled grids */ if (!square_isempty(cave, yy, xx)) continue; /* Try to fill this hole */ break; } } /* Extract the new "pseudo-direction" */ y = yy - mon->fy; x = xx - mon->fx; } /* Check for no move */ if (!x && !y) return (false); /* Pick the correct direction */ *dir = choose_direction(y, x); /* Want to move */ return (true); }
/** * Choose a good hiding place near a monster for it to run toward. * * Pack monsters will use this to "ambush" the player and lure him out * of corridors into open space so they can swarm him. * * Return true if a good location is available. */ static bool find_hiding(struct chunk *c, struct monster *mon) { int fy = mon->fy; int fx = mon->fx; int py = player->py; int px = player->px; int i, y, x, dy, dx, d, dis; int gy = 0, gx = 0, gdis = 999, min; const int *y_offsets, *x_offsets; /* Closest distance to get */ min = distance(py, px, fy, fx) * 3 / 4 + 2; /* Start with adjacent locations, spread further */ for (d = 1; d < 10; d++) { /* Get the lists of points with a distance d from (fx, fy) */ y_offsets = dist_offsets_y[d]; x_offsets = dist_offsets_x[d]; /* Check the locations */ for (i = 0, dx = x_offsets[0], dy = y_offsets[0]; dx != 0 || dy != 0; i++, dx = x_offsets[i], dy = y_offsets[i]) { y = fy + dy; x = fx + dx; /* Skip illegal locations */ if (!square_in_bounds_fully(c, y, x)) continue; /* Skip occupied locations */ if (!square_isempty(c, y, x)) continue; /* Check for hidden, available grid */ if (!square_isview(c, y, x) && projectable(c, fy, fx, y, x, PROJECT_STOP)) { /* Calculate distance from player */ dis = distance(y, x, py, px); /* Remember if closer than previous */ if (dis < gdis && dis >= min) { gy = y; gx = x; gdis = dis; } } } /* Check for success */ if (gdis < 999) { /* Good location */ mon->ty = gy; mon->tx = gx; /* Found good place */ return (true); } } /* No good place */ return (false); }
/** * Places a monster (of the specified "type") near the given * location. Return the siummoned monster's level iff a monster was * actually summoned. * * We will attempt to place the monster up to 10 times before giving up. * * This function takes the "monster level" * of the summoning monster as a parameter, and use that, along with * the current dungeon level, to help determine the level of the * desired monster. Note that this is an upper bound, and also * tends to "prefer" monsters of that level. Currently, we use * the average of the dungeon and monster levels, and then add * five to allow slight increases in monster power. * * Note that we use the new "monster allocation table" creation code * to restrict the "get_mon_num()" function to the set of "legal" * monsters, making this function much faster and more reliable. * * Note that this function may not succeed, though this is very rare. */ int summon_specific(int y1, int x1, int lev, int type, bool delay, bool call) { int i, x = 0, y = 0; struct monster *mon; struct monster_race *race; /* Look for a location, allow up to 4 squares away */ for (i = 0; i < 60; ++i) { /* Pick a distance */ int d = (i / 15) + 1; /* Pick a location */ scatter(cave, &y, &x, y1, x1, d, true); /* Require "empty" floor grid */ if (!square_isempty(cave, y, x)) continue; /* No summon on glyphs */ if (square_iswarded(cave, y, x) || square_isdecoyed(cave, y, x)) { continue; } /* Okay */ break; } /* Failure */ if (i == 60) return (0); /* Save the "summon" type */ summon_specific_type = type; /* Use the new calling scheme if requested */ if (call && (type != summon_name_to_idx("UNIQUE")) && (type != summon_name_to_idx("WRAITH"))) { return (call_monster(y, x)); } /* Prepare allocation table */ get_mon_num_prep(summon_specific_okay); /* Pick a monster, using the level calculation */ race = get_mon_num((player->depth + lev) / 2 + 5); /* Prepare allocation table */ get_mon_num_prep(NULL); /* Handle failure */ if (!race) return (0); /* Attempt to place the monster (awake, don't allow groups) */ if (!place_new_monster(cave, y, x, race, false, false, ORIGIN_DROP_SUMMON)) return (0); /* Success, return the level of the monster */ mon = square_monster(cave, y, x); /* If delay, try to let the player act before the summoned monsters, * including slowing down faster monsters for one turn */ /* XXX should this now be hold monster for a turn? */ if (delay) { mon->energy = 0; if (mon->race->speed > player->state.speed) mon_inc_timed(mon, MON_TMD_SLOW, 1, MON_TMD_FLG_NOMESSAGE, false); } return (mon->race->level); }
/** * Choose "logical" directions for monster movement */ static bool get_move(struct chunk *c, struct monster *mon, int *dir, bool *good) { struct loc decoy = cave_find_decoy(c); struct loc target = (decoy.y && decoy.x) ? decoy : loc(player->px, player->py); int y, x; /* Monsters will run up to z_info->flee_range grids out of sight */ int flee_range = z_info->max_sight + z_info->flee_range; bool done = false; /* Calculate range */ get_move_find_range(mon); /* Assume we're heading towards the player */ if (get_move_advance(c, mon)) { /* Extract the "pseudo-direction" */ y = mon->target.grid.y - mon->fy; x = mon->target.grid.x - mon->fx; *good = true; } else { /* Head blindly straight for the "player" if there's no better idea */ y = target.y - mon->fy; x = target.x - mon->fx; } /* Normal animal packs try to get the player out of corridors. */ if (rf_has(mon->race->flags, RF_GROUP_AI) && !flags_test(mon->race->flags, RF_SIZE, RF_PASS_WALL, RF_KILL_WALL, FLAG_END)) { int i, open = 0; /* Count empty grids next to player */ for (i = 0; i < 8; i++) { int ry = target.y + ddy_ddd[i]; int rx = target.x + ddx_ddd[i]; /* Check grid around the player for room interior (room walls count) * or other empty space */ if (square_ispassable(c, ry, rx) || square_isroom(c, ry, rx)) { /* One more open grid */ open++; } } /* Not in an empty space and strong player */ if ((open < 5) && (player->chp > player->mhp / 2)) { /* Find hiding place */ if (get_move_find_hiding(c, mon)) { done = true; y = mon->target.grid.y - mon->fy; x = mon->target.grid.x - mon->fx; } } } /* Apply fear */ if (!done && (mon->min_range == flee_range)) { /* Try to find safe place */ if (!get_move_find_safety(c, mon)) { /* Just leg it away from the player */ y = (-y); x = (-x); } else { /* Set a course for the safe place */ get_move_flee(c, mon); y = mon->target.grid.y - mon->fy; x = mon->target.grid.x - mon->fx; } done = true; } /* Monster groups try to surround the player */ if (!done && rf_has(mon->race->flags, RF_GROUP_AI) && square_isview(c, mon->fy, mon->fx)) { int i, yy = mon->target.grid.y, xx = mon->target.grid.x; /* If we are not already adjacent */ if (mon->cdis > 1) { /* Find an empty square near the player to fill */ int tmp = randint0(8); for (i = 0; i < 8; i++) { /* Pick squares near player (pseudo-randomly) */ yy = target.y + ddy_ddd[(tmp + i) & 7]; xx = target.x + ddx_ddd[(tmp + i) & 7]; /* Ignore filled grids */ if (!square_isempty(c, yy, xx)) continue; /* Try to fill this hole */ break; } } /* Extract the new "pseudo-direction" */ y = yy - mon->fy; x = xx - mon->fx; } /* Check if the monster has already reached its target */ if (!x && !y) return (false); /* Pick the correct direction */ *dir = get_move_choose_direction(y, x); /* Want to move */ return (true); }
/** * Determine whether the given coordinate is a valid starting location. * \param c current chunk * \param y * \param x co-ordinates * \return success */ static bool square_isstart(struct chunk *c, int y, int x) { if (!square_isempty(c, y, x)) return FALSE; if (square_isvault(c, y, x)) return FALSE; return TRUE; }
/** * Determine whether the given coordinate is a valid starting location. * \param c current chunk * \param y * \param x co-ordinates * \return success */ static bool square_isstart(struct chunk *c, int y, int x) { if (!square_isempty(c, y, x)) return false; if (square_isvault(c, y, x)) return false; return true; }