/** * Determine whether a monster is active or passive */ static bool monster_check_active(struct chunk *c, struct monster *mon) { /* Character is inside scanning range */ if (mon->cdis <= mon->race->aaf) mflag_on(mon->mflag, MFLAG_ACTIVE); /* Monster is hurt */ else if (mon->hp < mon->maxhp) mflag_on(mon->mflag, MFLAG_ACTIVE); /* Monster can "see" the player (checked backwards) */ else if (square_isview(c, mon->fy, mon->fx)) mflag_on(mon->mflag, MFLAG_ACTIVE); /* Monster can "smell" the player from far away (flow) */ else if (monster_can_flow(c, mon)) mflag_on(mon->mflag, MFLAG_ACTIVE); /* Otherwise go passive */ else mflag_off(mon->mflag, MFLAG_ACTIVE); return mflag_has(mon->mflag, MFLAG_ACTIVE) ? true : false; }
/** * Determine whether a monster is active or passive */ static bool monster_check_active(struct chunk *c, struct monster *mon) { if ((mon->cdis <= mon->race->hearing) && monster_passes_walls(mon)) { /* Character is inside scanning range, monster can go straight there */ mflag_on(mon->mflag, MFLAG_ACTIVE); } else if (mon->hp < mon->maxhp) { /* Monster is hurt */ mflag_on(mon->mflag, MFLAG_ACTIVE); } else if (square_isview(c, mon->fy, mon->fx)) { /* Monster can "see" the player (checked backwards) */ mflag_on(mon->mflag, MFLAG_ACTIVE); } else if (monster_can_hear(c, mon)) { /* Monster can hear the player */ mflag_on(mon->mflag, MFLAG_ACTIVE); } else if (monster_can_smell(c, mon)) { /* Monster can smell the player */ mflag_on(mon->mflag, MFLAG_ACTIVE); } else { /* Otherwise go passive */ mflag_off(mon->mflag, MFLAG_ACTIVE); } return mflag_has(mon->mflag, MFLAG_ACTIVE) ? true : false; }
/** * Process all the "live" monsters, once per game turn. * * During each game turn, we scan through the list of all the "live" monsters, * (backwards, so we can excise any "freshly dead" monsters), energizing each * monster, and allowing fully energized monsters to move, attack, pass, etc. * * This function and its children are responsible for a considerable fraction * of the processor time in normal situations, greater if the character is * resting. */ void process_monsters(struct chunk *c, int minimum_energy) { int i; int mspeed; /* Only process some things every so often */ bool regen = false; /* Regenerate hitpoints and mana every 100 game turns */ if (turn % 100 == 0) regen = true; /* Process the monsters (backwards) */ for (i = cave_monster_max(c) - 1; i >= 1; i--) { struct monster *mon; bool moving; /* Handle "leaving" */ if (player->is_dead || player->upkeep->generate_level) break; /* Get a 'live' monster */ mon = cave_monster(c, i); if (!mon->race) continue; /* Ignore monsters that have already been handled */ if (mflag_has(mon->mflag, MFLAG_HANDLED)) continue; /* Not enough energy to move yet */ if (mon->energy < minimum_energy) continue; /* Does this monster have enough energy to move? */ moving = mon->energy >= z_info->move_energy ? true : false; /* Prevent reprocessing */ mflag_on(mon->mflag, MFLAG_HANDLED); /* Handle monster regeneration if requested */ if (regen) regen_monster(mon); /* Calculate the net speed */ mspeed = mon->mspeed; if (mon->m_timed[MON_TMD_FAST]) mspeed += 10; if (mon->m_timed[MON_TMD_SLOW]) mspeed -= 10; /* Give this monster some energy */ mon->energy += turn_energy(mspeed); /* End the turn of monsters without enough energy to move */ if (!moving) continue; /* Use up "some" energy */ mon->energy -= z_info->move_energy; /* Mimics lie in wait */ if (is_mimicking(mon)) continue; /* Check if the monster is active */ if (monster_check_active(c, mon)) { /* Process timed effects - skip turn if necessary */ if (process_monster_timed(c, mon)) continue; /* Set this monster to be the current actor */ c->mon_current = i; /* Process the monster */ process_monster(c, mon); /* Monster is no longer current */ c->mon_current = -1; } } /* Update monster visibility after this */ /* XXX This may not be necessary */ player->upkeep->update |= PU_MONSTERS; }
/** * Attempts to place a monster of the given race at the given location. * * 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.) * * To give the player a sporting chance, some especially dangerous * monsters are marked as "FORCE_SLEEP" in monster.txt, which will * cause them to be placed with low energy. This helps ensure that * if such a monster suddenly appears in line-of-sight (due to a * summon, for instance), the player gets a chance to move before * they do. * * This routine refuses to place out-of-depth "FORCE_DEPTH" monsters. * * This is the only function which may place a monster in the dungeon, * except for the savefile loading code, which calls place_monster() * directly. */ static bool place_new_monster_one(struct chunk *c, int y, int x, struct monster_race *race, bool sleep, byte origin) { int i; struct monster *mon; struct monster monster_body; assert(square_in_bounds(c, y, x)); assert(race && race->name); /* Not where monsters already are */ if (square_monster(c, y, x)) return false; /* Not where the player already is */ if ((player->py == y) && (player->px == x)) return false; /* Prevent monsters from being placed where they cannot walk, but allow other feature types */ if (!square_is_monster_walkable(c, y, x)) return false; /* No creation on glyph of warding */ if (square_iswarded(c, y, x)) return false; /* "unique" monsters must be "unique" */ if (rf_has(race->flags, RF_UNIQUE) && race->cur_num >= race->max_num) return (false); /* Depth monsters may NOT be created out of depth */ if (rf_has(race->flags, RF_FORCE_DEPTH) && player->depth < race->level) return (false); /* Add to level feeling, note uniques for cheaters */ c->mon_rating += race->level * race->level; /* Check out-of-depth-ness */ if (race->level > c->depth) { if (rf_has(race->flags, RF_UNIQUE)) { /* OOD unique */ if (OPT(player, cheat_hear)) msg("Deep unique (%s).", race->name); } else { /* Normal monsters but OOD */ if (OPT(player, cheat_hear)) msg("Deep monster (%s).", race->name); } /* Boost rating by power per 10 levels OOD */ c->mon_rating += (race->level - c->depth) * race->level * race->level; } else if (rf_has(race->flags, RF_UNIQUE) && OPT(player, cheat_hear)) msg("Unique (%s).", race->name); /* Get local monster */ mon = &monster_body; /* Clean out the monster */ memset(mon, 0, sizeof(struct monster)); /* Save the race */ mon->race = race; /* Enforce sleeping if needed */ if (sleep && race->sleep) { int val = race->sleep; mon->m_timed[MON_TMD_SLEEP] = ((val * 2) + randint1(val * 10)); } /* Uniques get a fixed amount of HP */ if (rf_has(race->flags, RF_UNIQUE)) mon->maxhp = race->avg_hp; else { mon->maxhp = mon_hp(race, RANDOMISE); mon->maxhp = MAX(mon->maxhp, 1); } /* And start out fully healthy */ mon->hp = mon->maxhp; /* Extract the monster base speed */ mon->mspeed = race->speed; /* Hack -- small racial variety */ if (!rf_has(race->flags, RF_UNIQUE)) { /* Allow some small variation per monster */ i = turn_energy(race->speed) / 10; if (i) mon->mspeed += rand_spread(0, i); } /* Give a random starting energy */ mon->energy = (byte)randint0(50); /* Force monster to wait for player */ if (rf_has(race->flags, RF_FORCE_SLEEP)) mflag_on(mon->mflag, MFLAG_NICE); /* Radiate light? */ if (rf_has(race->flags, RF_HAS_LIGHT)) player->upkeep->update |= PU_UPDATE_VIEW; /* Is this obviously a monster? (Mimics etc. aren't) */ if (rf_has(race->flags, RF_UNAWARE)) mflag_on(mon->mflag, MFLAG_CAMOUFLAGE); else mflag_off(mon->mflag, MFLAG_CAMOUFLAGE); /* Set the color if necessary */ if (rf_has(race->flags, RF_ATTR_RAND)) mon->attr = randint1(BASIC_COLORS - 1); /* Place the monster in the dungeon */ if (!place_monster(c, y, x, mon, origin)) return (false); /* Success */ return (true); }