Example #1
0
/**
 * Describe an object's effect, if any.
 */
static bool describe_effect(textblock *tb, const struct object *obj,
		bool only_artifacts, bool subjective)
{
	char desc[200];
	struct effect *effect = NULL;
	bool aimed = false;
	int min_time, max_time, failure_chance;

	/* Sometimes we only print artifact activation info */
	if (only_artifacts && !obj->artifact)
		return false;

	if (obj_known_effect(obj, &effect, &aimed, &min_time, &max_time,
							 &failure_chance) == false)
		return false;

	/* Effect not known, mouth platitudes */
	if (!effect && object_effect(obj)) {
		if (aimed)
			textblock_append(tb, "It can be aimed.\n");
		else if (tval_is_edible(obj))
			textblock_append(tb, "It can be eaten.\n");
		else if (tval_is_potion(obj))
			textblock_append(tb, "It can be drunk.\n");
		else if (tval_is_scroll(obj))
			textblock_append(tb, "It can be read.\n");
		else textblock_append(tb, "It can be activated.\n");

		return true;
	}

	/* Activations get a special message */
	if (obj->activation && obj->activation->desc) {
		textblock_append(tb, "When activated, it ");
		textblock_append(tb, obj->activation->desc);
	} else {
		int random_choices = 0;

		/* Get descriptions for all the effects */
		effect = object_effect(obj);
		if (!effect_desc(effect)) return false;

		if (aimed)
			textblock_append(tb, "When aimed, it ");
		else if (tval_is_edible(obj))
			textblock_append(tb, "When eaten, it ");
		else if (tval_is_potion(obj))
			textblock_append(tb, "When quaffed, it ");
		else if (tval_is_scroll(obj))
			textblock_append(tb, "When read, it ");
		else
			textblock_append(tb, "When activated, it ");

		/* Print a colourised description */
		while (effect) {
			char *next_char = desc;
			int roll = 0;
			random_value value = { 0, 0, 0, 0 };
			char dice_string[20];
			int boost, level = obj->kind->level;

			/* Get the level */
			if (obj->artifact)
				level = obj->artifact->level;
			else if (obj->ego)
				level = obj->ego->level;

			/* Get the boost */
			boost = MAX(player->state.skills[SKILL_DEVICE] - level, 0);			

			if (effect->dice != NULL)
				roll = dice_roll(effect->dice, &value);

			/* Deal with special random effect */
			if (effect->index == EF_RANDOM)
				random_choices = roll + 1;

			/* Get the possible dice strings */
			if (value.dice && value.base)
				strnfmt(dice_string, sizeof(dice_string), "%d+%dd%d",
						value.base, value.dice, value.sides);
			else if (value.dice)
				strnfmt(dice_string, sizeof(dice_string), "%dd%d",
						value.dice, value.sides);
			else
				strnfmt(dice_string, sizeof(dice_string), "%d", value.base);

			/* Check all the possible types of description format */
			switch (base_descs[effect->index].efinfo_flag) {
				/* Healing sometimes has a minimum percentage */
			case EFINFO_HEAL: {
				char min_string[50];
				if (value.m_bonus)
					strnfmt(min_string, sizeof(min_string),
							" (or %d%%, whichever is greater)", value.m_bonus);
				else
					strnfmt(min_string, sizeof(min_string), "");
				strnfmt(desc, sizeof(desc), effect_desc(effect), dice_string,
						min_string);
				break;
			}

			/* Nourishment is just a flat amount */
			case EFINFO_CONST: {
				strnfmt(desc, sizeof(desc), effect_desc(effect), value.base/2);
				break;
			}
			case EFINFO_CURE: {
				strnfmt(desc, sizeof(desc), effect_desc(effect),
							timed_idx_to_desc(effect->params[0]));
				break;
			}
			case EFINFO_TIMED: {
				strnfmt(desc, sizeof(desc), effect_desc(effect),
							timed_idx_to_desc(effect->params[0]), dice_string);
				break;
			}
			case EFINFO_STAT: {
				strnfmt(desc, sizeof(desc), effect_desc(effect),
							mod_flags[effect->params[0]].name);
				break;
			}
			case EFINFO_SEEN: {
				strnfmt(desc, sizeof(desc), effect_desc(effect),
						gf_desc(effect->params[0]));
				break;
			}
			case EFINFO_SUMM: {
				strnfmt(desc, sizeof(desc), effect_desc(effect),
						summon_desc(effect->params[0]));
				break;
			}

			/* Only currently used for the player, but can handle monsters */
			case EFINFO_TELE: {
				if (effect->params[0])
					strnfmt(desc, sizeof(desc), effect_desc(effect),
							"a monster", value.base);
				else
					strnfmt(desc, sizeof(desc), effect_desc(effect), "you",
							value.base);
				break;
			}
			case EFINFO_QUAKE: {
				strnfmt(desc, sizeof(desc), effect_desc(effect),
						effect->params[1]);
				break;
			}
			case EFINFO_LIGHT: {
				strnfmt(desc, sizeof(desc), effect_desc(effect), dice_string,
						effect->params[1]);
				break;
			}

			/* Object generated balls are elemental */
			case EFINFO_BALL: {
				strnfmt(desc, sizeof(desc), effect_desc(effect),
						elements[effect->params[0]].name, effect->params[1],
						dice_string);
				if (boost)
					my_strcat(desc, format(", which your device skill increases by %d per cent", boost),
							  sizeof(desc));
				break;
			}

			/* Bolts that inflict status */
			case EFINFO_BOLT: {
				strnfmt(desc, sizeof(desc), effect_desc(effect),
						gf_desc(effect->params[0]));
				break;
			}
			/* Bolts and beams that damage */
			case EFINFO_BOLTD: {
				strnfmt(desc, sizeof(desc), effect_desc(effect),
						gf_desc(effect->params[0]), dice_string);
				if (boost)
					my_strcat(desc, format(", which your device skill increases by %d per cent", boost),
							  sizeof(desc));
				break;
			}
			case EFINFO_TOUCH: {
				strnfmt(desc, sizeof(desc), effect_desc(effect),
						gf_desc(effect->params[0]));
				break;
			}
			case EFINFO_NONE: {
				strnfmt(desc, sizeof(desc), effect_desc(effect));
				break;
			}
			default: {
				msg("Bad effect description passed to describe_effect(). Please report this bug.");
				return false;
			}
			}

			do {
				if (isdigit((unsigned char) *next_char))
					textblock_append_c(tb, COLOUR_L_GREEN, "%c", *next_char);
				else
					textblock_append(tb, "%c", *next_char);
			} while (*next_char++);

			/* Random choices need special treatment - note that this code
			 * assumes that RANDOM and the random choices will be the last
			 * effect in the object/activation description */
			if (random_choices >= 1) {
				if (effect->index == EF_RANDOM)
					;
				else if (random_choices > 2)
					textblock_append(tb, ", ");
				else if (random_choices == 2)
					textblock_append(tb, " or ");
				random_choices--;
			} else if (effect->next) {
				if (effect->next->next && (effect->next->index != EF_RANDOM))
					textblock_append(tb, ", ");
				else
					textblock_append(tb, " and ");
			}
			effect = effect->next;
		}
	}

	textblock_append(tb, ".\n");

	if (min_time || max_time) {
		/* Sometimes adjust for player speed */
		int multiplier = turn_energy(player->state.speed);
		if (!subjective) multiplier = 10;

		textblock_append(tb, "Takes ");

		/* Correct for player speed */
		min_time = (min_time * multiplier) / 10;
		max_time = (max_time * multiplier) / 10;

		textblock_append_c(tb, COLOUR_L_GREEN, "%d", min_time);

		if (min_time != max_time) {
			textblock_append(tb, " to ");
			textblock_append_c(tb, COLOUR_L_GREEN, "%d", max_time);
		}

		textblock_append(tb, " turns to recharge");
		if (subjective && player->state.speed != 110)
			textblock_append(tb, " at your current speed");

		textblock_append(tb, ".\n");
	}

	if (failure_chance > 0) {
		textblock_append(tb, "Your chance of success is %d.%d%%\n", 
			(1000 - failure_chance) / 10, (1000 - failure_chance) % 10);
	}

	return true;
}
Example #2
0
/**
 * Describe an object's effect, if any.
 */
static bool describe_effect(textblock *tb, const struct object *obj,
		bool only_artifacts, bool subjective)
{
	char desc[200];
	struct effect *e;

	int effect = 0;
	bool aimed = FALSE;
	int min_time, max_time, failure_chance;

	/* Sometimes we only print artifact activation info */
	if (only_artifacts && !obj->artifact)
		return FALSE;

	if (obj_known_effect(obj, &effect, &aimed, &min_time, &max_time, &failure_chance) == FALSE)
		return FALSE;

	/* We don't know much */
	if (effect == OBJ_KNOWN_PRESENT) {
		if (aimed)
			textblock_append(tb, "It can be aimed.\n");
		else if (tval_is_edible(obj))
			textblock_append(tb, "It can be eaten.\n");
		else if (tval_is_potion(obj))
			textblock_append(tb, "It can be drunk.\n");
		else if (tval_is_scroll(obj))
			textblock_append(tb, "It can be read.\n");
		else textblock_append(tb, "It can be activated.\n");

		return TRUE;
	}

	/* Obtain the description */
	e = obj->effect;
	if (!effect_desc(e)) return FALSE;

	if (aimed)
		textblock_append(tb, "When aimed, it ");
	else if (tval_is_edible(obj))
		textblock_append(tb, "When eaten, it ");
	else if (tval_is_potion(obj))
		textblock_append(tb, "When quaffed, it ");
	else if (tval_is_scroll(obj))
	    textblock_append(tb, "When read, it ");
	else
	    textblock_append(tb, "When activated, it ");

	/* Print a colourised description */
	while (e) {
		char *next_char = desc;
		random_value value = { 0, 0, 0, 0 };
		char dice_string[20];
		if (e->dice != NULL)
			(void) dice_roll(e->dice, &value);

		/* Get the possible dice strings */
		if (value.dice && value.base)
			strnfmt(dice_string, sizeof(dice_string), "%d+%dd%d",
					value.base, value.dice, value.sides);
		else if (value.dice)
			strnfmt(dice_string, sizeof(dice_string), "%dd%d",
					value.dice, value.sides);
		else
			strnfmt(dice_string, sizeof(dice_string), "%d", value.base);

		/* Check all the possible types of description format */
		switch (base_descs[e->index].efinfo_flag) {
			/* Healing sometimes has a minimum percentage */
			case EFINFO_HEAL: {
				char min_string[50];
				if (value.m_bonus)
					strnfmt(min_string, sizeof(min_string),
							" (or %d%%, whichever is greater)", value.m_bonus);
				else
					strnfmt(min_string, sizeof(min_string), "");
				strnfmt(desc, sizeof(desc), effect_desc(e), dice_string,
						min_string);
				break;
			}

			/* Nourishment is just a flat amount */
			case EFINFO_FEED: {
				strnfmt(desc, sizeof(desc), effect_desc(e), value.base);
				break;
			}
			case EFINFO_CURE: {
				strnfmt(desc, sizeof(desc), effect_desc(e),
							timed_idx_to_desc(e->params[0]));
				break;
			}
			case EFINFO_TIMED: {
				strnfmt(desc, sizeof(desc), effect_desc(e),
							timed_idx_to_desc(e->params[0]), dice_string);
				break;
			}
			case EFINFO_STAT: {
				strnfmt(desc, sizeof(desc), effect_desc(e),
							mod_flags[e->params[0]].name);
				break;
			}
			case EFINFO_SEEN: {
				strnfmt(desc, sizeof(desc), effect_desc(e),
						gf_desc(e->params[0]));
				break;
			}
			case EFINFO_SUMM: {
				strnfmt(desc, sizeof(desc), effect_desc(e),
						summon_desc(e->params[0]));
				break;
			}

			/* Only currently used for the player, but can handle monsters */
			case EFINFO_TELE: {
				if (e->params[0])
					strnfmt(desc, sizeof(desc), effect_desc(e), "a monster", 
						value.base);
				else
					strnfmt(desc, sizeof(desc), effect_desc(e), "you",
							value.base);
				break;
			}
			case EFINFO_QUAKE: {
				strnfmt(desc, sizeof(desc), effect_desc(e), e->params[1]);
				break;
			}
			case EFINFO_LIGHT: {
				strnfmt(desc, sizeof(desc), effect_desc(e), dice_string,
						e->params[1]);
				break;
			}

			/* Object generated balls are elemental */
			case EFINFO_BALL: {
				strnfmt(desc, sizeof(desc), effect_desc(e),
						elements[e->params[0]].name, e->params[1], dice_string);
				break;
			}

			/* Bolts that inflict status */
			case EFINFO_BOLT: {
				strnfmt(desc, sizeof(desc), effect_desc(e),
						gf_desc(e->params[0]));
				break;
			}
			/* Bolts and beams that damage */
			case EFINFO_BOLTD: {
				strnfmt(desc, sizeof(desc), effect_desc(e),
						gf_desc(e->params[0]), dice_string);
				break;
			}
			case EFINFO_TOUCH: {
				strnfmt(desc, sizeof(desc), effect_desc(e),
						gf_desc(e->params[0]));
				break;
			}
			default:strnfmt(desc, sizeof(desc), effect_desc(e)); break;
		}
		do {
			if (isdigit((unsigned char) *next_char))
				textblock_append_c(tb, COLOUR_L_GREEN, "%c", *next_char);
			else
				textblock_append(tb, "%c", *next_char);
		} while (*next_char++);
		if (e->next) {
			if (e->next->next)
				textblock_append(tb, ", ");
			else
				textblock_append(tb, " and ");
		}
		e = e->next;
	}

	textblock_append(tb, ".\n");

	if (min_time || max_time) {
		/* Sometimes adjust for player speed */
		int multiplier = turn_energy(player->state.speed);
		if (!subjective) multiplier = 10;

		textblock_append(tb, "Takes ");

		/* Correct for player speed */
		min_time *= multiplier / 10;
		max_time *= multiplier / 10;

		textblock_append_c(tb, COLOUR_L_GREEN, "%d", min_time);

		if (min_time != max_time) {
			textblock_append(tb, " to ");
			textblock_append_c(tb, COLOUR_L_GREEN, "%d", max_time);
		}

		textblock_append(tb, " turns to recharge");
		if (subjective && player->state.speed != 110)
			textblock_append(tb, " at your current speed");

		textblock_append(tb, ".\n");
	}

	if (failure_chance > 0) {
		textblock_append(tb, "Your chance of success is %d.%d%%\n", 
			(1000 - failure_chance) / 10, (1000 - failure_chance) % 10);
	}

	return TRUE;
}