Esempio n. 1
0
void wr_monsters(void)
{
	int i, j;

	if (p_ptr->is_dead)
		return;

	/* Total monsters */
	wr_u16b(cave_monster_max(cave));

	/* Dump the monsters */
	for (i = 1; i < cave_monster_max(cave); i++) {
		byte unaware = 0;
	
		const monster_type *m_ptr = cave_monster(cave, i);

		wr_s16b(m_ptr->r_idx);
		wr_byte(m_ptr->fy);
		wr_byte(m_ptr->fx);
		wr_s16b(m_ptr->hp);
		wr_s16b(m_ptr->maxhp);
		wr_byte(m_ptr->mspeed);
		wr_byte(m_ptr->energy);
		wr_byte(MON_TMD_MAX);

		for (j = 0; j < MON_TMD_MAX; j++)
			wr_s16b(m_ptr->m_timed[j]);

		if (m_ptr->unaware) unaware |= 0x01;
		wr_byte(unaware);

		wr_byte(0);
	}
}
Esempio n. 2
0
/**
 * Calls a monster from the level and moves it to the desired spot
 */
int call_monster(int y, int x)
{
	int i, mon_count, choice;
	int oy, ox;
	int *mon_indices;
	struct monster *mon;

	mon_count = 0;

	for (i = 1; i < cave_monster_max(cave); i++) {
		mon = cave_monster(cave, i);

		/* Figure out how many good monsters there are */
		if (can_call_monster(y, x, mon)) mon_count++;
	}

	/* There were no good monsters on the level */
	if (mon_count == 0) return (0);

	/* Make the array */
	mon_indices = mem_zalloc(mon_count * sizeof(int));

	/* Reset mon_count */
	mon_count = 0;

	/* Now go through a second time and store the indices */
	for (i = 1; i < cave_monster_max(cave); i++) {
		mon = cave_monster(cave, i);
		
		/* Save the values of the good monster */
		if (can_call_monster(y, x, mon)){
			mon_indices[mon_count] = i;
			mon_count++;
		}
	}

	/* Pick one */
	choice = randint0(mon_count - 1);

	/* Get the lucky monster */
	mon = cave_monster(cave, mon_indices[choice]);
	mem_free(mon_indices);

	/* Extract monster location */
	oy = mon->fy;
	ox = mon->fx;

	/* Swap the moster */
	monster_swap(oy, ox, y, x);

	/* Wake it up */
	mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, false);

	/* Set it's energy to 0 */
	mon->energy = 0;

	return (mon->race->level);
}
Esempio n. 3
0
/**
 * Housekeeping after the processing of a player command
 */
static void process_player_cleanup(void)
{
	int i;

	/* Significant */
	if (player->upkeep->energy_use) {
		/* Use some energy */
		player->energy -= player->upkeep->energy_use;

		/* Increment the total energy counter */
		player->total_energy += player->upkeep->energy_use;

		/* Do nothing else if player has auto-dropped stuff */
		if (!player->upkeep->dropping) {
			/* Hack -- constant hallucination */
			if (player->timed[TMD_IMAGE])
				player->upkeep->redraw |= (PR_MAP);

			/* Shimmer multi-hued monsters */
			for (i = 1; i < cave_monster_max(cave); i++) {
				struct monster *mon = cave_monster(cave, i);
				if (!mon->race)
					continue;
				if (!rf_has(mon->race->flags, RF_ATTR_MULTI))
					continue;
				square_light_spot(cave, mon->fy, mon->fx);
			}

			/* Clear NICE flag, and show marked monsters */
			for (i = 1; i < cave_monster_max(cave); i++) {
				struct monster *mon = cave_monster(cave, i);
				mflag_off(mon->mflag, MFLAG_NICE);
				if (mflag_has(mon->mflag, MFLAG_MARK)) {
					if (!mflag_has(mon->mflag, MFLAG_SHOW)) {
						mflag_off(mon->mflag, MFLAG_MARK);
						update_mon(mon, cave, false);
					}
				}
			}
		}
	}

	/* Clear SHOW flag and player drop status */
	for (i = 1; i < cave_monster_max(cave); i++) {
		struct monster *mon = cave_monster(cave, i);
		mflag_off(mon->mflag, MFLAG_SHOW);
	}
	player->upkeep->dropping = false;

	/* Hack - update needed first because inventory may have changed */
	update_stuff(player);
	redraw_stuff(player);
}
Esempio n. 4
0
/**
 * Allow monsters on a frozen persistent level to recover
 */
void restore_monsters(void)
{
	int i;
	struct monster *mon;

	/* Get the number of turns that have passed */
	int num_turns = turn - cave->turn;

	/* Process the monsters (backwards) */
	for (i = cave_monster_max(cave) - 1; i >= 1; i--) {
		int status, status_red;

		/* Access the monster */
		mon = cave_monster(cave, i);

		/* Regenerate */
		regen_monster(mon, num_turns / 100);

		/* Handle timed effects */
		status_red = num_turns * turn_energy(mon->mspeed) / z_info->move_energy;
		if (status_red > 0) {
			for (status = 0; status < MON_TMD_MAX; status++) {
				if (mon->m_timed[status]) {
					mon_dec_timed(mon, status, status_red, 0, false);
				}
			}
		}
	}
}
Esempio n. 5
0
/**
 * Display the monster list statically. This will force the list to be
 * displayed to the provided dimensions. Contents will be adjusted accordingly.
 *
 * In order to support more efficient monster flicker animations, this function
 * uses a shared list object so that it's not constantly allocating and freeing
 * the list.
 *
 * \param height is the height of the list.
 * \param width is the width of the list.
 */
void monster_list_show_subwindow(int height, int width)
{
	textblock *tb;
	monster_list_t *list;
	int i;

	if (height < 1 || width < 1)
		return;

	tb = textblock_new();
	list = monster_list_shared_instance();

	/* Force an update if detected monsters */
	for (i = 1; i < cave_monster_max(cave); i++) {
		if (mflag_has(cave_monster(cave, i)->mflag, MFLAG_MARK)) {
			list->creation_turn = -1;
			break;
		}
	}

	monster_list_reset(list);
	monster_list_collect(list);
	monster_list_get_glyphs(list);
	monster_list_sort(list, monster_list_standard_compare);

	/* Draw the list to exactly fit the subwindow. */
	monster_list_format_textblock(list, tb, height, width, NULL, NULL);
	textui_textblock_place(tb, SCREEN_REGION, NULL);

	textblock_free(tb);
}
Esempio n. 6
0
/**
 * Return TRUE if the list needs to be updated. Usually this is each turn or if
 * the number of cave monsters changes.
 */
static bool monster_list_needs_update(const monster_list_t *list)
{
	if (list == NULL || list->entries == NULL)
		return FALSE;

	return list->creation_turn != turn ||
		(int)list->entries_size < cave_monster_max(cave);
}
Esempio n. 7
0
/**
 * Returns the index of a "free" monster, or 0 if no slot is available.
 *
 * This routine should almost never fail, but it *can* happen.
 * The calling code must check for and handle a 0 return.
 */
static s16b mon_pop(void)
{
	int m_idx;

	/* Normal allocation */
	if (cave_monster_max(cave) < z_info->m_max) {
		/* Get the next hole */
		m_idx = cave_monster_max(cave);

		/* Expand the array */
		cave->mon_max++;

		/* Count monsters */
		cave->mon_cnt++;

		return m_idx;
	}

	/* Recycle dead monsters if we've run out of room */
	for (m_idx = 1; m_idx < cave_monster_max(cave); m_idx++) {
		monster_type *m_ptr;

		/* Get the monster */
		m_ptr = cave_monster(cave, m_idx);

		/* Skip live monsters */
		if (m_ptr->r_idx) continue;

		/* Count monsters */
		cave->mon_cnt++;

		/* Use this monster */
		return m_idx;
	}

	/* Warn the player if no index is available 
	 * (except during dungeon creation)
	 */
	if (character_dungeon)
		msg("Too many monsters!");

	/* Try not to crash */
	return 0;
}
Esempio n. 8
0
/**
 * Returns the index of a "free" monster, or 0 if no slot is available.
 *
 * This routine should almost never fail, but it *can* happen.
 * The calling code must check for and handle a 0 return.
 */
s16b mon_pop(struct chunk *c)
{
	int m_idx;

	/* Normal allocation */
	if (cave_monster_max(c) < z_info->level_monster_max) {
		/* Get the next hole */
		m_idx = cave_monster_max(c);

		/* Expand the array */
		c->mon_max++;

		/* Count monsters */
		c->mon_cnt++;

		return m_idx;
	}

	/* Recycle dead monsters if we've run out of room */
	for (m_idx = 1; m_idx < cave_monster_max(c); m_idx++) {
		struct monster *mon = cave_monster(c, m_idx);

		/* Skip live monsters */
		if (mon->race) continue;

		/* Count monsters */
		c->mon_cnt++;

		/* Use this monster */
		return m_idx;
	}

	/* Warn the player if no index is available 
	 * (except during dungeon creation)
	 */
	if (character_dungeon)
		msg("Too many monsters!");

	/* Try not to crash */
	return 0;
}
Esempio n. 9
0
/**
 * Zero out the contents of a monster list. If needed, this function will
 * reallocate the entry list if the number of monsters has changed.
 */
void monster_list_reset(monster_list_t *list)
{
	if (list == NULL || list->entries == NULL)
		return;

	if (!monster_list_needs_update(list))
		return;

	if ((int)list->entries_size < cave_monster_max(cave)) {
		list->entries = mem_realloc(list->entries, sizeof(list->entries[0])
									* cave_monster_max(cave));
		list->entries_size = cave_monster_max(cave);
	}

	memset(list->entries, 0, list->entries_size * sizeof(monster_list_entry_t));
	memset(list->total_entries, 0, MONSTER_LIST_SECTION_MAX * sizeof(u16b));
	memset(list->total_monsters, 0, MONSTER_LIST_SECTION_MAX * sizeof(u16b));
	list->distinct_entries = 0;
	list->creation_turn = 0;
	list->sorted = FALSE;
}
Esempio n. 10
0
/**
 * Clear 'moved' status from all monsters.
 *
 * Clear noise if appropriate.
 */
void reset_monsters(void)
{
	int i;
	struct monster *mon;

	/* Process the monsters (backwards) */
	for (i = cave_monster_max(cave) - 1; i >= 1; i--) {
		/* Access the monster */
		mon = cave_monster(cave, i);

		/* Monster is ready to go again */
		mflag_off(mon->mflag, MFLAG_HANDLED);
	}
}
Esempio n. 11
0
/**
 * This will scan the dungeon for monsters and then kill each
 * and every last one.
 */
static void scan_for_monsters(void)
{ 
	int i;

	/* Go through the monster list */
	for (i = 1; i < cave_monster_max(cave); i++) {
		struct monster *mon = cave_monster(cave, i);

		/* Skip dead monsters */
		if (!mon->race) continue;

		stats_monster(mon, i);
	}
}
Esempio n. 12
0
/**
 * Clear 'moved' status from all monsters.
 *
 * Clear noise if appropriate.
 */
void reset_monsters(void)
{
	int i;
	monster_type *m_ptr;

	/* Process the monsters (backwards) */
	for (i = cave_monster_max(cave) - 1; i >= 1; i--) {
		/* Access the monster */
		m_ptr = cave_monster(cave, i);

		/* Monster is ready to go again */
		mflag_off(m_ptr->mflag, MFLAG_HANDLED);
	}
}
Esempio n. 13
0
/**
 * Deletes all the monsters when the player leaves the level.
 *
 * This is an efficient method of simulating multiple calls to the
 * "delete_monster()" function, with no visual effects.
 *
 * Note that we must delete the objects the monsters are carrying, but we
 * do nothing with mimicked objects.
 */
void wipe_mon_list(struct chunk *c, struct player *p)
{
	int m_idx;

	/* Delete all the monsters */
	for (m_idx = cave_monster_max(c) - 1; m_idx >= 1; m_idx--) {
		struct monster *mon = cave_monster(c, m_idx);
		struct object *held_obj = mon ? mon->held_obj : NULL;

		/* Skip dead monsters */
		if (!mon->race) continue;

		/* Delete all the objects */
		if (held_obj) {
			/* Go through all held objects and check for artifacts */
			struct object *obj = held_obj;
			while (obj) {
				if (obj->artifact && !(obj->known && obj->known->artifact))
					obj->artifact->created = false;
				obj = obj->next;
			}
			object_pile_free(held_obj);
		}

		/* Reduce the racial counter */
		mon->race->cur_num--;

		/* Monster is gone */
		c->squares[mon->fy][mon->fx].mon = 0;

		/* Wipe the Monster */
		memset(mon, 0, sizeof(struct monster));
	}

	/* Reset "cave->mon_max" */
	c->mon_max = 1;

	/* Reset "mon_cnt" */
	c->mon_cnt = 0;

	/* Hack -- reset "reproducer" count */
	num_repro = 0;

	/* Hack -- no more target */
	target_set_monster(0);

	/* Hack -- no more tracking */
	health_track(p->upkeep, 0);
}
Esempio n. 14
0
/**
 * Allocate a new monster list based on the size of the current cave's monster
 * array.
 */
monster_list_t *monster_list_new(void)
{
	monster_list_t *list = mem_zalloc(sizeof(monster_list_t));
	size_t size = cave_monster_max(cave);

	if (list == NULL)
		return NULL;

	list->entries = mem_zalloc(size * sizeof(monster_list_entry_t));

	if (list->entries == NULL) {
		mem_free(list);
		return NULL;
	}

	list->entries_size = size;

	return list;
}
Esempio n. 15
0
/*
 * Hack -- Delete all nearby monsters
 */
static void do_cmd_wiz_zap(int d)
{
	int i;

	/* Banish everyone nearby */
	for (i = 1; i < cave_monster_max(cave); i++)
	{
		monster_type *m_ptr = cave_monster(cave, i);

		/* Skip dead monsters */
		if (!m_ptr->r_idx) continue;

		/* Skip distant monsters */
		if (m_ptr->cdis > d) continue;

		/* Delete the monster */
		delete_monster_idx(i);
	}

	/* Update monster list window */
	p_ptr->redraw |= PR_MONLIST;
}
Esempio n. 16
0
/**
 * Deletes all the monsters when the player leaves the level.
 *
 * This is an efficient method of simulating multiple calls to the
 * "delete_monster()" function, with no visual effects.
 *
 * Note that we do not delete the objects the monsters are carrying;
 * that must be taken care of separately via wipe_o_list().
 */
void wipe_mon_list(struct cave *c, struct player *p)
{
	int m_idx;

	/* Delete all the monsters */
	for (m_idx = cave_monster_max(cave) - 1; m_idx >= 1; m_idx--)
	{
		monster_type *m_ptr = cave_monster(cave, m_idx);

		monster_race *r_ptr = &r_info[m_ptr->r_idx];

		/* Skip dead monsters */
		if (!m_ptr->r_idx) continue;

		/* Hack -- Reduce the racial counter */
		r_ptr->cur_num--;

		/* Monster is gone */
		c->m_idx[m_ptr->fy][m_ptr->fx] = 0;

		/* Wipe the Monster */
		(void)WIPE(m_ptr, monster_type);
	}

	/* Reset "cave->mon_max" */
	cave->mon_max = 1;

	/* Reset "mon_cnt" */
	cave->mon_cnt = 0;

	/* Hack -- reset "reproducer" count */
	num_repro = 0;

	/* Hack -- no more target */
	target_set_monster(0);

	/* Hack -- no more tracking */
	health_track(p, 0);
}
Esempio n. 17
0
/**
 * Handle things that need updating once every 10 game turns
 */
void process_world(struct chunk *c)
{
	int i, y, x;

	/* Compact the monster list if we're approaching the limit */
	if (cave_monster_count(cave) + 32 > z_info->level_monster_max)
		compact_monsters(64);

	/* Too many holes in the monster list - compress */
	if (cave_monster_count(cave) + 32 < cave_monster_max(cave))
		compact_monsters(0);

	/*** Check the Time ***/

	/* Play an ambient sound at regular intervals. */
	if (!(turn % ((10L * z_info->day_length) / 4)))
		play_ambient_sound();

	/*** Handle stores and sunshine ***/

	if (!player->depth) {
		/* Daybreak/Nighfall in town */
		if (!(turn % ((10L * z_info->day_length) / 2))) {
			bool dawn;

			/* Check for dawn */
			dawn = (!(turn % (10L * z_info->day_length)));

			/* Day breaks */
			if (dawn)
				msg("The sun has risen.");

			/* Night falls */
			else
				msg("The sun has fallen.");

			/* Illuminate */
			cave_illuminate(c, dawn);
		}
	} else {
		/* Update the stores once a day (while in the dungeon).
		   The changes are not actually made until return to town,
		   to avoid giving details away in the knowledge menu. */
		if (!(turn % (10L * z_info->store_turns))) daycount++;
	}


	/* Check for creature generation */
	if (one_in_(z_info->alloc_monster_chance))
		(void)pick_and_place_distant_monster(cave, player,
											 z_info->max_sight + 5, true,
											 player->depth);

	/*** Damage over Time ***/

	/* Take damage from poison */
	if (player->timed[TMD_POISONED])
		take_hit(player, 1, "poison");

	/* Take damage from cuts */
	if (player->timed[TMD_CUT]) {
		/* Mortal wound or Deep Gash */
		if (player->timed[TMD_CUT] > TMD_CUT_SEVERE)
			i = 3;

		/* Severe cut */
		else if (player->timed[TMD_CUT] > TMD_CUT_NASTY)
			i = 2;

		/* Other cuts */
		else
			i = 1;

		/* Take damage */
		take_hit(player, i, "a fatal wound");
	}


	/*** Check the Food, and Regenerate ***/

	/* Digest normally */
	if (!(turn % 100)) {
		/* Basic digestion rate based on speed */
		i = turn_energy(player->state.speed) * 2;

		/* Regeneration takes more food */
		if (player_of_has(player, OF_REGEN)) i += 30;

		/* Slow digestion takes less food */
		if (player_of_has(player, OF_SLOW_DIGEST)) i /= 5;

		/* Minimal digestion */
		if (i < 1) i = 1;

		/* Digest some food */
		player_set_food(player, player->food - i);
	}

	/* Getting Faint */
	if (player->food < PY_FOOD_FAINT) {
		/* Faint occasionally */
		if (!player->timed[TMD_PARALYZED] && one_in_(10)) {
			/* Message */
			msg("You faint from the lack of food.");
			disturb(player, 1);

			/* Faint (bypass free action) */
			(void)player_inc_timed(player, TMD_PARALYZED, 1 + randint0(5),
								   true, false);
		}
	}

	/* Starve to death (slowly) */
	if (player->food < PY_FOOD_STARVE) {
		/* Calculate damage */
		i = (PY_FOOD_STARVE - player->food) / 10;

		/* Take damage */
		take_hit(player, i, "starvation");
	}

	/* Regenerate Hit Points if needed */
	if (player->chp < player->mhp)
		player_regen_hp(player);

	/* Regenerate mana if needed */
	if (player->csp < player->msp)
		player_regen_mana(player);

	/* Timeout various things */
	decrease_timeouts();

	/* Process light */
	player_update_light(player);


	/*** Process Inventory ***/

	/* Handle experience draining */
	if (player_of_has(player, OF_DRAIN_EXP)) {
		if ((player->exp > 0) && one_in_(10)) {
			s32b d = damroll(10, 6) +
				(player->exp / 100) * z_info->life_drain_percent;
			player_exp_lose(player, d / 10, false);
		}

		equip_learn_flag(player, OF_DRAIN_EXP);
	}

	/* Recharge activatable objects and rods */
	recharge_objects();

	/* Notice things after time */
	if (!(turn % 100))
		equip_learn_after_time(player);

	/* Decrease trap timeouts */
	for (y = 0; y < cave->height; y++) {
		for (x = 0; x < cave->width; x++) {
			struct trap *trap = cave->squares[y][x].trap;
			while (trap) {
				if (trap->timeout) {
					trap->timeout--;
					if (!trap->timeout)
						square_light_spot(cave, y, x);
				}
				trap = trap->next;
			}
		}
	}


	/*** Involuntary Movement ***/

	/* Delayed Word-of-Recall */
	if (player->word_recall) {
		/* Count down towards recall */
		player->word_recall--;

		/* Activate the recall */
		if (!player->word_recall) {
			/* Disturbing! */
			disturb(player, 0);

			/* Determine the level */
			if (player->depth) {
				msgt(MSG_TPLEVEL, "You feel yourself yanked upwards!");
				dungeon_change_level(player, 0);
			} else {
				msgt(MSG_TPLEVEL, "You feel yourself yanked downwards!");
                
                /* Force descent to a lower level if allowed */
                if (OPT(player, birth_force_descend) &&
					player->max_depth < z_info->max_depth - 1 &&
					!is_quest(player->max_depth)) {
                    player->max_depth = dungeon_get_next_level(player->max_depth, 1);
                }

				/* New depth - back to max depth or 1, whichever is deeper */
				dungeon_change_level(player, player->max_depth < 1 ? 1: player->max_depth);
			}
		}
	}

	/* Delayed Deep Descent */
	if (player->deep_descent) {
		/* Count down towards recall */
		player->deep_descent--;

		/* Activate the recall */
		if (player->deep_descent == 0) {
			int target_increment;
			int target_depth = player->max_depth;

			/* Calculate target depth */
			target_increment = (4 / z_info->stair_skip) + 1;
			target_depth = dungeon_get_next_level(player->max_depth, target_increment);

			disturb(player, 0);

			/* Determine the level */
			if (target_depth > player->depth) {
				msgt(MSG_TPLEVEL, "The floor opens beneath you!");
				dungeon_change_level(player, target_depth);
			} else {
				/* Otherwise do something disastrous */
				msgt(MSG_TPLEVEL, "You are thrown back in an explosion!");
				effect_simple(EF_DESTRUCTION, "0", 0, 5, 0, NULL);
			}		
		}
	}
}
Esempio n. 18
0
/**
 * 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;
}
Esempio n. 19
0
/**
 * Collect monster information from the current cave's monster list.
 */
void monster_list_collect(monster_list_t *list)
{
	int i;

	if (list == NULL || list->entries == NULL)
		return;

	/* Use cave_monster_max() here in case the monster list isn't compacted. */
	for (i = 1; i < cave_monster_max(cave); i++) {
		struct monster *mon = cave_monster(cave, i);
		monster_list_entry_t *entry = NULL;
		int j, field;
		bool los = FALSE;

		/* Only consider visible, known monsters */
		if (!mflag_has(mon->mflag, MFLAG_VISIBLE) ||
			mflag_has(mon->mflag, MFLAG_UNAWARE))
			continue;

		/* Find or add a list entry. */
		for (j = 0; j < (int)list->entries_size; j++) {
			if (list->entries[j].race == NULL) {
				/* We found an empty slot, so add this race here. */
				entry = &list->entries[j];
				memset(entry, 0, sizeof(monster_list_entry_t));
				entry->race = mon->race;

				break;
			}
			else if (list->entries[j].race == mon->race) {
				/* We found a matching race and we'll use that. */
				entry = &list->entries[j];
				break;
			}
		}

		if (entry == NULL)
			continue;

		/* Always collect the latest monster attribute so that flicker
		 * animation works. If this is 0, it needs to be replaced by 
		 * the standard glyph in the UI */
		entry->attr = mon->attr;

		/* Skip the projection and location checks if nothing has changed. */
		if (!monster_list_needs_update(list))
			continue;

		/*
		 * Check for LOS
		 * Hack - we should use (mon->mflag & (MFLAG_VIEW)) here,
		 * but this does not catch monsters detected by ESP which are
		 * targetable, so we cheat and use projectable() instead
		 */
		los = projectable(cave, player->py, player->px, mon->fy, mon->fx,
						  PROJECT_NONE);
		field = (los) ? MONSTER_LIST_SECTION_LOS : MONSTER_LIST_SECTION_ESP;
		entry->count[field]++;

		if (mon->m_timed[MON_TMD_SLEEP] > 0)
			entry->asleep[field]++;

		/* Store the location offset from the player; this is only used for
		 * monster counts of 1 */
		entry->dx = mon->fx - player->px;
		entry->dy = mon->fy - player->py;
	}

	/* Skip calculations if nothing has changed, otherwise this will yield
	 * incorrect numbers. */
	if (!monster_list_needs_update(list))
		return;

	/* Collect totals for easier calculations of the list. */
	for (i = 0; i < (int)list->entries_size; i++) {
		if (list->entries[i].race == NULL)
			continue;

		if (list->entries[i].count[MONSTER_LIST_SECTION_LOS] > 0)
			list->total_entries[MONSTER_LIST_SECTION_LOS]++;

		if (list->entries[i].count[MONSTER_LIST_SECTION_ESP] > 0)
			list->total_entries[MONSTER_LIST_SECTION_ESP]++;

		list->total_monsters[MONSTER_LIST_SECTION_LOS] +=
			list->entries[i].count[MONSTER_LIST_SECTION_LOS];
		list->total_monsters[MONSTER_LIST_SECTION_ESP] +=
			list->entries[i].count[MONSTER_LIST_SECTION_ESP];
		list->distinct_entries++;
	}

	list->creation_turn = turn;
	list->sorted = FALSE;
}
Esempio n. 20
0
/**
 * Generate a random level.
 *
 * Confusingly, this function also generate the town level (level 0).
 * \param c is the level we're going to end up with, in practice the global cave
 * \param p is the current player struct, in practice the global player
 */
void cave_generate(struct chunk **c, struct player *p) {
	const char *error = "no generation";
	int y, x, tries = 0;
	struct chunk *chunk;

	assert(c);

	/* Generate */
	for (tries = 0; tries < 100 && error; tries++) {
		struct dun_data dun_body;

		error = NULL;

		/* Mark the dungeon as being unready (to avoid artifact loss, etc) */
		character_dungeon = FALSE;

		/* Allocate global data (will be freed when we leave the loop) */
		dun = &dun_body;
		dun->cent = mem_zalloc(z_info->level_room_max * sizeof(struct loc));
		dun->door = mem_zalloc(z_info->level_door_max * sizeof(struct loc));
		dun->wall = mem_zalloc(z_info->wall_pierce_max * sizeof(struct loc));
		dun->tunn = mem_zalloc(z_info->tunn_grid_max * sizeof(struct loc));

		/* Choose a profile and build the level */
		dun->profile = choose_profile(p->depth);
		chunk = dun->profile->builder(p);
		if (!chunk) {
			error = "Failed to find builder";
			mem_free(dun->cent);
			mem_free(dun->door);
			mem_free(dun->wall);
			mem_free(dun->tunn);
			continue;
		}

		/* Ensure quest monsters */
		if (is_quest(chunk->depth)) {
			int i;
			for (i = 1; i < z_info->r_max; i++) {
				monster_race *r_ptr = &r_info[i];
				int y, x;
				
				/* The monster must be an unseen quest monster of this depth. */
				if (r_ptr->cur_num > 0) continue;
				if (!rf_has(r_ptr->flags, RF_QUESTOR)) continue;
				if (r_ptr->level != chunk->depth) continue;
	
				/* Pick a location and place the monster */
				find_empty(chunk, &y, &x);
				place_new_monster(chunk, y, x, r_ptr, TRUE, TRUE, ORIGIN_DROP);
			}
		}

		/* Clear generation flags. */
		for (y = 0; y < chunk->height; y++) {
			for (x = 0; x < chunk->width; x++) {
				sqinfo_off(chunk->squares[y][x].info, SQUARE_WALL_INNER);
				sqinfo_off(chunk->squares[y][x].info, SQUARE_WALL_OUTER);
				sqinfo_off(chunk->squares[y][x].info, SQUARE_WALL_SOLID);
				sqinfo_off(chunk->squares[y][x].info, SQUARE_MON_RESTRICT);
			}
		}

		/* Regenerate levels that overflow their maxima */
		if (cave_monster_max(chunk) >= z_info->level_monster_max)
			error = "too many monsters";

		if (error) ROOM_LOG("Generation restarted: %s.", error);

		mem_free(dun->cent);
		mem_free(dun->door);
		mem_free(dun->wall);
		mem_free(dun->tunn);
	}

	if (error) quit_fmt("cave_generate() failed 100 times!");

	/* Free the old cave, use the new one */
	if (*c)
		cave_free(*c);
	*c = chunk;

	/* Place dungeon squares to trigger feeling (not in town) */
	if (player->depth)
		place_feeling(*c);

	/* Save the town */
	else if (!chunk_find_name("Town")) {
		struct chunk *town = chunk_write(0, 0, z_info->town_hgt,
										 z_info->town_wid, FALSE, FALSE, FALSE);
		town->name = string_make("Town");
		chunk_list_add(town);
	}

	(*c)->feeling = calc_obj_feeling(*c) + calc_mon_feeling(*c);

	/* Validate the dungeon (we could use more checks here) */
	chunk_validate_objects(*c);

	/* The dungeon is ready */
	character_dungeon = TRUE;

	/* Free old and allocate new known level */
	if (cave_k)
		cave_free(cave_k);
	cave_k = cave_new(cave->height, cave->width);
	if (!cave->depth)
		cave_known();

	(*c)->created_at = turn;
}
Esempio n. 21
0
/**
 * Compacts and reorders the monster list.
 *
 * This function can be very dangerous, use with caution!
 *
 * When `num_to_compact` is 0, we just reorder the monsters into a more compact
 * order, eliminating any "holes" left by dead monsters. If `num_to_compact` is
 * positive, then we delete at least that many monsters and then reorder.
 * We try not to delete monsters that are high level or close to the player.
 * Each time we make a full pass through the monster list, if we haven't
 * deleted enough monsters, we relax our bounds a little to accept
 * monsters of a slightly higher level, and monsters slightly closer to
 * the player.
 */
void compact_monsters(int num_to_compact)
{
	int m_idx, num_compacted, iter;

	int max_lev, min_dis, chance;


	/* Message (only if compacting) */
	if (num_to_compact)
		msg("Compacting monsters...");


	/* Compact at least 'num_to_compact' objects */
	for (num_compacted = 0, iter = 1; num_compacted < num_to_compact; iter++) {
		/* Get more vicious each iteration */
		max_lev = 5 * iter;

		/* Get closer each iteration */
		min_dis = 5 * (20 - iter);

		/* Check all the monsters */
		for (m_idx = 1; m_idx < cave_monster_max(cave); m_idx++) {
			struct monster *mon = cave_monster(cave, m_idx);

			/* Skip "dead" monsters */
			if (!mon->race) continue;

			/* High level monsters start out "immune" */
			if (mon->race->level > max_lev) continue;

			/* Ignore nearby monsters */
			if ((min_dis > 0) && (mon->cdis < min_dis)) continue;

			/* Saving throw chance */
			chance = 90;

			/* Only compact "Quest" Monsters in emergencies */
			if (rf_has(mon->race->flags, RF_QUESTOR) && (iter < 1000))
				chance = 100;

			/* Try not to compact Unique Monsters */
			if (rf_has(mon->race->flags, RF_UNIQUE)) chance = 99;

			/* All monsters get a saving throw */
			if (randint0(100) < chance) continue;

			/* Delete the monster */
			delete_monster(mon->fy, mon->fx);

			/* Count the monster */
			num_compacted++;
		}
	}


	/* Excise dead monsters (backwards!) */
	for (m_idx = cave_monster_max(cave) - 1; m_idx >= 1; m_idx--) {
		struct monster *mon = cave_monster(cave, m_idx);

		/* Skip real monsters */
		if (mon->race) continue;

		/* Move last monster into open hole */
		compact_monsters_aux(cave_monster_max(cave) - 1, m_idx);

		/* Compress "cave->mon_max" */
		cave->mon_max--;
	}
}