/** * 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 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); }