/** * Attempts to place a monster of the given race at the given location. * * Note that certain monsters are placed with a large group of * identical or similar monsters. However, if `group_okay` is false, * then such monsters are placed by themselves. * * 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.) */ bool place_new_monster(struct chunk *c, int y, int x, struct monster_race *race, bool sleep, bool group_okay, byte origin) { struct monster_friends *friends; struct monster_friends_base *friends_base; int total; assert(c); assert(race); /* Place one monster, or fail */ if (!place_new_monster_one(c, y, x, race, sleep, origin)) return (false); /* We're done unless the group flag is set */ if (!group_okay) return (true); /* Go through friends flags */ for (friends = race->friends; friends; friends = friends->next) { if ((unsigned int)randint0(100) >= friends->percent_chance) continue; /* Calculate the base number of monsters to place */ total = damroll(friends->number_dice, friends->number_side); place_friends(c, y, x, race, friends->race, total, sleep, origin); } /* Go through the friends_base flags */ for (friends_base = race->friends_base; friends_base; friends_base = friends_base->next){ struct monster_race *friends_race; /* Check if we pass chance for the monster appearing */ if ((unsigned int)randint0(100) >= friends_base->percent_chance) continue; total = damroll(friends_base->number_dice, friends_base->number_side); /* Set the escort index base*/ place_monster_base = friends_base->base; /* Prepare allocation table */ get_mon_num_prep(place_monster_base_okay); /* Pick a random race */ friends_race = get_mon_num(race->level); /* Reset allocation table */ get_mon_num_prep(NULL); /* Handle failure */ if (!friends_race) break; place_friends(c, y, x, race, friends_race, total, sleep, origin); } /* Success */ return (true); }
/** * Helper function to place monsters that appear as friends or escorts */ bool place_friends(struct cave *c, int y, int x, monster_race *race, monster_race *friends_race, int total, bool sleep, byte origin) { int level_difference, extra_chance, nx, ny; bool is_unique, success = TRUE; /* Find the difference between current dungeon depth and monster level */ level_difference = p_ptr->depth - friends_race->level + 5; /* Handle unique monsters */ is_unique = rf_has(friends_race->flags, RF_UNIQUE); /* Make sure the unique hasn't been killed already */ if (is_unique){ total = friends_race->cur_num < friends_race->max_num ? 1 : 0; } /* More than 4 levels OoD, no groups allowed */ if (level_difference <= 0 && !is_unique){ return FALSE; } /* Reduce group size within 5 levels of natural depth*/ if (level_difference < 10 && !is_unique){ extra_chance = (total * level_difference) % 10; total = total * level_difference / 10; /* Instead of flooring the group value, we use the decimal place as a chance of an extra monster */ if (randint0(10) > extra_chance){ total += 1; } } /* No monsters in this group */ if (total <= 0){ return FALSE; } /* Handle friends same as original monster */ if (race->ridx == friends_race->ridx){ place_new_monster_group(c, y, x, race, sleep, total, origin); } /* Find a nearby place to put the other groups */ for (int j = 0; j < 50; j++){ scatter(&ny, &nx, y, x, GROUP_DISTANCE, FALSE); if (cave_isopen(cave, ny, nx)) break; } /* Place the monsters */ success = place_new_monster_one(ny, nx, friends_race, sleep, origin); if (total > 1){ success = place_new_monster_group(c, ny, nx, friends_race, sleep, total, origin); } return success; }
/** * 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); }
/** * Attempts to place a monster of the given race at the given location. * * Note that certain monsters are placed with a large group of * identical or similar monsters. However, if `group_okay` is false, * then such monsters are placed by themselves. * * 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.) * * Note the "bizarre" use of non-recursion to prevent annoying output * when running a code profiler. * * Note the use of the "monster allocation table" to restrict * the "get_mon_num()" function to "legal" escort types. */ bool place_new_monster(struct cave *c, int y, int x, int r_idx, bool sleep, bool group_okay, byte origin) { int i; monster_race *r_ptr; assert(c); assert(r_idx > 0); r_ptr = &r_info[r_idx]; /* Place one monster, or fail */ if (!place_new_monster_one(y, x, r_ptr, sleep, origin)) return (FALSE); /* We're done unless the group flag is set */ if (!group_okay) return (TRUE); /* Friends for certain monsters */ if (rf_has(r_ptr->flags, RF_FRIEND)) { int total = group_size_2(r_ptr); (void)place_new_monster_group(c, y, x, r_ptr, sleep, total, origin); } /* Friends for certain monsters */ if (rf_has(r_ptr->flags, RF_FRIENDS)) { int total = group_size_1(r_ptr); (void)place_new_monster_group(c, y, x, r_ptr, sleep, total, origin); } /* Escorts for certain monsters */ if (rf_has(r_ptr->flags, RF_ESCORT)) { /* Try to place several "escorts" */ for (i = 0; i < 50; i++) { int nx, ny, z, d = 3; monster_race *z_ptr; /* Pick a location */ scatter(&ny, &nx, y, x, d, 0); /* Require empty grids */ if (!cave_empty_bold(ny, nx)) continue; /* Set the escort index */ place_monster_idx = r_idx; /* Set the escort hook */ get_mon_num_hook = place_monster_okay; /* Prepare allocation table */ get_mon_num_prep(); /* Pick a random race */ z = get_mon_num(r_ptr->level); /* Remove restriction */ get_mon_num_hook = NULL; /* Prepare allocation table */ get_mon_num_prep(); /* Handle failure */ if (!z) break; /* Place a single escort */ z_ptr = &r_info[z]; (void)place_new_monster_one(ny, nx, z_ptr, sleep, origin); /* Place a "group" of escorts if needed */ if (rf_has(z_ptr->flags, RF_FRIEND)) { int total = group_size_2(z_ptr); (void)place_new_monster_group(c, ny, nx, z_ptr, sleep, total, origin); } if (rf_has(z_ptr->flags, RF_FRIENDS) || rf_has(r_ptr->flags, RF_ESCORTS)) { int total = group_size_1(z_ptr); (void)place_new_monster_group(c, ny, nx, z_ptr, sleep, total, origin); } } } /* Success */ return (TRUE); }