Exemple #1
0
static enum parser_error parse_summon_base(struct parser *p) {
	struct summon *s = parser_priv(p);
	struct monster_base *base;
	struct monster_base_list *b = mem_zalloc(sizeof(*b));
	assert(s);
	base = lookup_monster_base(parser_getsym(p, "base"));
	if (base == NULL) {
		mem_free(b);
		return PARSE_ERROR_INVALID_MONSTER_BASE;
	}
	b->base = base;
	b->next = s->bases;
	s->bases = b;
	return PARSE_ERROR_NONE;
}
Exemple #2
0
static enum parser_error parse_r_t(struct parser *p) {
	struct monster_race *r = parser_priv(p);

	r->base = lookup_monster_base(parser_getsym(p, "base"));
	if (r->base == NULL)
		/* Todo: make new error for this */
		return PARSE_ERROR_UNRECOGNISED_TVAL;

	/* The template sets the default display character */
	r->d_char = r->base->d_char;

	/* Give the monster its default flags */
	rf_union(r->flags, r->base->flags);

	return PARSE_ERROR_NONE;
}
int test_defaults(void *state) {
	size_t i;
	struct monster_base *mb = lookup_monster_base("giant");
	int tval = tval_find_idx("sword");

	/* Monster bases */
	eq(process_pref_file_command("monster-base:giant:3:3"), 0);

	for (i = 0; i < z_info->r_max; i++) {
		monster_race *race = &r_info[i];

		if (race->base != mb) continue;

		eq(monster_x_attr[race->ridx], 3);
		eq(monster_x_char[race->ridx], 3);
	}

	/* Object tvals */
	eq(process_pref_file_command("object:sword:*:3:3"), 0);

	for (i = 0; i < z_info->k_max; i++) {
		struct object_kind *kind = &k_info[i];

		if (kind->tval != tval)
			continue;

		eq(kind_x_attr[kind->kidx], 3);
		eq(kind_x_char[kind->kidx], 3);
	}

	/* Traps */
	eq(process_pref_file_command("trap:*:*:3:3"), 0);

	for (i = 0; i < z_info->trap_max; i++) {
		int light_idx;

		for (light_idx = 0; light_idx < LIGHTING_MAX; light_idx++) {
			eq(trap_x_attr[light_idx][i], 3);
			eq(trap_x_attr[light_idx][i], 3);
		}
	}

	ok;
}
Exemple #4
0
static enum parser_error parse_r_friends_base(struct parser *p) {
	struct monster_race *r = parser_priv(p);
	struct monster_friends_base *f;
	struct random number;
	
	if (!r)
		return PARSE_ERROR_MISSING_RECORD_HEADER;
	f = mem_zalloc(sizeof *f);
	number = parser_getrand(p, "number");
	f->number_dice = number.dice;
	f->number_side = number.sides;
	f->percent_chance = parser_getuint(p, "chance");
	f->base = lookup_monster_base(parser_getstr(p, "name"));
	if (!f->base) return PARSE_ERROR_UNRECOGNISED_TVAL;

	f->next = r->friends_base;
	r->friends_base = f;
	
	return PARSE_ERROR_NONE;
}		
Exemple #5
0
/**
 * Decreases a monster's hit points by `dam` and handle monster death.
 *
 * Hack -- we "delay" fear messages by passing around a "fear" flag.
 *
 * We announce monster death (using an optional "death message" (`note`)
 * if given, and a otherwise a generic killed/destroyed message).
 * 
 * Returns TRUE if the monster has been killed (and deleted).
 *
 * TODO: Consider decreasing monster experience over time, say, by using
 * "(m_exp * m_lev * (m_lev)) / (p_lev * (m_lev + n_killed))" instead
 * of simply "(m_exp * m_lev) / (p_lev)", to make the first monster
 * worth more than subsequent monsters.  This would also need to
 * induce changes in the monster recall code.  XXX XXX XXX
 **/
bool mon_take_hit(struct monster *m_ptr, int dam, bool *fear, const char *note)
{
	s32b div, new_exp, new_exp_frac;
	monster_lore *l_ptr = get_lore(m_ptr->race);


	/* Redraw (later) if needed */
	if (p_ptr->health_who == m_ptr) p_ptr->redraw |= (PR_HEALTH);

	/* Wake it up */
	mon_clear_timed(m_ptr, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, FALSE);

	/* Become aware of its presence */
	if (m_ptr->unaware)
		become_aware(m_ptr);

	/* Hurt it */
	m_ptr->hp -= dam;

	/* It is dead now */
	if (m_ptr->hp < 0) {
		char m_name[80];
		char buf[80];

		/* Assume normal death sound */
		int soundfx = MSG_KILL;

		/* Play a special sound if the monster was unique */
		if (rf_has(m_ptr->race->flags, RF_UNIQUE)) {
			if (m_ptr->race->base == lookup_monster_base("Morgoth"))
				soundfx = MSG_KILL_KING;
			else
				soundfx = MSG_KILL_UNIQUE;
		}

		/* Extract monster name */
		monster_desc(m_name, sizeof(m_name), m_ptr, 0);

		/* Death by Missile/Spell attack */
		if (note) {
			/* Hack -- allow message suppression */
			if (strlen(note) <= 1) {
				/* Be silent */
			} else {
				char *str = format("%s%s", m_name, note);
				my_strcap(str);
				msgt(soundfx, "%s", str);
			}
		}

		/* Death by physical attack -- invisible monster */
		else if (!m_ptr->ml)
			msgt(soundfx, "You have killed %s.", m_name);

		/* Death by Physical attack -- non-living monster */
		else if (monster_is_unusual(m_ptr->race))
			msgt(soundfx, "You have destroyed %s.", m_name);

		/* Death by Physical attack -- living monster */
		else
			msgt(soundfx, "You have slain %s.", m_name);

		/* Player level */
		div = p_ptr->lev;

		/* Give some experience for the kill */
		new_exp = ((long)m_ptr->race->mexp * m_ptr->race->level) / div;

		/* Handle fractional experience */
		new_exp_frac = ((((long)m_ptr->race->mexp * m_ptr->race->level) % div)
		                * 0x10000L / div) + p_ptr->exp_frac;

		/* Keep track of experience */
		if (new_exp_frac >= 0x10000L) {
			new_exp++;
			p_ptr->exp_frac = (u16b)(new_exp_frac - 0x10000L);
		}
		else
			p_ptr->exp_frac = (u16b)new_exp_frac;

		/* When the player kills a Unique, it stays dead */
		if (rf_has(m_ptr->race->flags, RF_UNIQUE)) {
			char unique_name[80];
			m_ptr->race->max_num = 0;

			/* 
			 * This gets the correct name if we slay an invisible 
			 * unique and don't have See Invisible.
			 */
			monster_desc(unique_name, sizeof(unique_name), m_ptr, 
					MDESC_SHOW | MDESC_IND2);

			/* Log the slaying of a unique */
			strnfmt(buf, sizeof(buf), "Killed %s", unique_name);
			history_add(buf, HISTORY_SLAY_UNIQUE, 0);
		}

		/* Gain experience */
		player_exp_gain(p_ptr, new_exp);

		/* Generate treasure */
		monster_death(m_ptr, FALSE);

		/* Recall even invisible uniques or winners */
		if (m_ptr->ml || rf_has(m_ptr->race->flags, RF_UNIQUE)) {
			/* Count kills this life */
			if (l_ptr->pkills < MAX_SHORT) l_ptr->pkills++;

			/* Count kills in all lives */
			if (l_ptr->tkills < MAX_SHORT) l_ptr->tkills++;

			/* Hack -- Auto-recall */
			monster_race_track(m_ptr->race);
		}

		/* Delete the monster */
		delete_monster_idx(m_ptr->midx);

		/* Not afraid */
		(*fear) = FALSE;

		/* Monster is dead */
		return (TRUE);
	}


	/* Mega-Hack -- Pain cancels fear */
	if (!(*fear) && m_ptr->m_timed[MON_TMD_FEAR] && (dam > 0)) {
		int tmp = randint1(dam);

		/* Cure a little fear */
		if (tmp < m_ptr->m_timed[MON_TMD_FEAR]) {
			/* Reduce fear */
			mon_dec_timed(m_ptr, MON_TMD_FEAR, tmp, MON_TMD_FLG_NOMESSAGE,
				FALSE);
		}

		/* Cure all the fear */
		else {
			/* Cure fear */
			mon_clear_timed(m_ptr, MON_TMD_FEAR, MON_TMD_FLG_NOMESSAGE, FALSE);

			/* No more fear */
			(*fear) = FALSE;
		}
	}

	/* Sometimes a monster gets scared by damage */
	if (!m_ptr->m_timed[MON_TMD_FEAR] && !rf_has(m_ptr->race->flags, RF_NO_FEAR) &&
		dam > 0) {
		int percentage;

		/* Percentage of fully healthy */
		percentage = (100L * m_ptr->hp) / m_ptr->maxhp;

		/*
		 * Run (sometimes) if at 10% or less of max hit points,
		 * or (usually) when hit for half its current hit points
		 */
		if ((randint1(10) >= percentage) ||
		    ((dam >= m_ptr->hp) && (randint0(100) < 80)))
		{
			int timer = randint1(10) + (((dam >= m_ptr->hp) && (percentage > 7)) ?
	                   20 : ((11 - percentage) * 5));

			/* Hack -- note fear */
			(*fear) = TRUE;

			mon_inc_timed(m_ptr, MON_TMD_FEAR, timer,
					MON_TMD_FLG_NOMESSAGE | MON_TMD_FLG_NOFAIL, FALSE);
		}
	}


	/* Not dead yet */
	return (FALSE);
}
/**
 * Show and delete the stacked monster messages.
 * Some messages are delayed so that they show up after everything else.
 * This is to avoid things like "The snaga dies. The snaga runs in fear!"
 * So we only flush messages matching the delay parameter.
 */
static void flush_monster_messages(bool delay, byte delay_tag)
{
    const monster_race *r_ptr;
    int i, count;
    char buf[512];
    char *action;
    bool action_only;

    /* Show every message */
    for (i = 0; i < size_mon_msg; i++) {
        int type = MSG_GENERIC;

        if (mon_msg[i].delay != delay) continue;

        /* Skip if we are delaying and the tags don't match */
        if (mon_msg[i].delay && mon_msg[i].delay_tag != delay_tag) continue;

        /* Cache the monster count */
        count = mon_msg[i].mon_count;

        /* Paranoia */
        if (count < 1) continue;

        /* Start with an empty string */
        buf[0] = '\0';

        /* Cache the race index */
        r_ptr = mon_msg[i].race;

        /* Get the proper message action */
        action = get_mon_msg_action(mon_msg[i].msg_code, (count > 1), r_ptr);

        /* Monster is marked as invisible */
        if (mon_msg[i].mon_flags & MON_MSG_FLAG_INVISIBLE) r_ptr = NULL;

        /* Special message? */
        action_only = (*action == '~');

        /* Format the proper message depending on type, number and visibility */
        if (r_ptr && !action_only) {
            char race_name[80];

            /* Get the race name */
            my_strcpy(race_name, r_ptr->name, sizeof(race_name));

            /* Uniques, multiple monsters, or just one */
            if (rf_has(r_ptr->flags, RF_UNIQUE)) {
                /* Just copy the race name */
                my_strcpy(buf, (r_ptr->name), sizeof(buf));
            } else if (count > 1) {
                /* Get the plural of the race name */
                if (r_ptr->plural != NULL) {
                    my_strcpy(race_name, r_ptr->plural, sizeof(race_name));
                } else {
                    plural_aux(race_name, sizeof(race_name));
                }

                /* Put the count and the race name together */
                strnfmt(buf, sizeof(buf), "%d %s", count, race_name);
            } else {
                /* Just add a slight flavor */
                strnfmt(buf, sizeof(buf), "the %s", race_name);
            }
        } else if (!r_ptr && !action_only) {
            if (count > 1) {
                /* Show the counter */
                strnfmt(buf, sizeof(buf), "%d monsters", count);
            } else {
                /* Just one non-visible monster */
                my_strcpy(buf, "it", sizeof(buf));
            }
        }

        /* Special message. Nuke the mark */
        if (action_only)
            ++action;
        /* Regular message */
        else {
            /* Add special mark. Monster is offscreen */
            if (mon_msg[i].mon_flags & MON_MSG_FLAG_OFFSCREEN)
                my_strcat(buf, " (offscreen)", sizeof(buf));

            /* Add the separator */
            my_strcat(buf, " ", sizeof(buf));
        }

        /* Append the action to the message */
        my_strcat(buf, action, sizeof(buf));

        /* Capitalize the message */
        *buf = toupper((unsigned char)*buf);

        switch (mon_msg[i].msg_code) {
        case MON_MSG_FLEE_IN_TERROR:
            type = MSG_FLEE;
            break;

        case MON_MSG_MORIA_DEATH:
        case MON_MSG_DESTROYED:
        case MON_MSG_DIE:
        case MON_MSG_SHRIVEL_LIGHT:
        case MON_MSG_DISENTEGRATES:
        case MON_MSG_FREEZE_SHATTER:
        case MON_MSG_DISSOLVE:
        {
            /* Assume normal death sound */
            type = MSG_KILL;

            /* Play a special sound if the monster was unique */
            if (r_ptr != NULL && rf_has(r_ptr->flags, RF_UNIQUE)) {
                if (r_ptr->base == lookup_monster_base("Morgoth"))
                    type = MSG_KILL_KING;
                else
                    type = MSG_KILL_UNIQUE;
            }
            break;
        }

        }

        /* Show the message */
        msgt(type, "%s", buf);
    }
}