Ejemplo n.º 1
0
/**
 * Melee effect handler: Eat the player's food.
 */
static void melee_effect_handler_EAT_FOOD(melee_effect_handler_context_t *context)
{
	/* Steal some food */
	int tries;

	/* Take damage */
	take_hit(context->p, context->damage, context->ddesc);

	/* Player is dead */
	if (context->p->is_dead)
		return;

	for (tries = 0; tries < 10; tries++) {
		/* Pick an item from the pack */
		int index = randint0(z_info->pack_size);
		struct object *obj, *eaten;
		char o_name[80];
		bool none_left = false;

		/* Get the item */
		obj = context->p->upkeep->inven[index];

		/* Skip non-objects */
		if (obj == NULL) continue;

		/* Skip non-food objects */
		if (!tval_is_edible(obj)) continue;

		if (obj->number == 1) {
			object_desc(o_name, sizeof(o_name), obj, ODESC_BASE);
			msg("Your %s (%c) was eaten!", o_name, I2A(index));
		} else {
			object_desc(o_name, sizeof(o_name), obj,
						ODESC_PREFIX | ODESC_BASE);
			msg("One of your %s (%c) was eaten!", o_name,
				I2A(index));
		}

		/* Steal and eat */
		eaten = gear_object_for_use(obj, 1, false, &none_left);
		if (eaten->known)
			object_delete(&eaten->known);
		object_delete(&eaten);

		/* Obvious */
		context->obvious = true;

		/* Done */
		break;
	}
}
Ejemplo n.º 2
0
/**
 * Gives the known effects of using the given item.
 *
 * Fills in:
 *  - the effect
 *  - whether the effect can be aimed
 *  -  the minimum and maximum time in game turns for the item to recharge 
 *     (or zero if it does not recharge)
 *  - the percentage chance of the effect failing when used
 *
 * Return false if the object has no effect.
 */
static bool obj_known_effect(const struct object *obj, struct effect **effect,
								 bool *aimed, int *min_recharge,
								 int *max_recharge, int *failure_chance)
{
	random_value timeout = {0, 0, 0, 0};

	*effect = 0;
	*min_recharge = 0;
	*max_recharge = 0;
	*failure_chance = 0;
	*aimed = false;

	if (object_effect_is_known(obj)) {
		*effect = object_effect(obj);
		timeout = obj->time;
		if (effect_aim(*effect))
			*aimed = true;;
	} else if (object_effect(obj)) {
		/* Don't know much - be vague */
		*effect = NULL;

		if (!obj->artifact && effect_aim(object_effect(obj)))
			*aimed = true;

		return true;
	} else {
		/* No effect - no info */
		return false;
	}

	if (randcalc(timeout, 0, MAXIMISE) > 0)	{
		*min_recharge = randcalc(timeout, 0, MINIMISE);
		*max_recharge = randcalc(timeout, 0, MAXIMISE);
	}

	if (tval_is_edible(obj) || tval_is_potion(obj) || tval_is_scroll(obj)) {
		*failure_chance = 0;
	} else {
		*failure_chance = get_use_device_chance(obj);
	}

	return true;
}
Ejemplo n.º 3
0
/**
 * Wipe an object clean and make it a standard object of the specified kind.
 */
void object_prep(struct object *obj, struct object_kind *k, int lev,
				 aspect rand_aspect)
{
	int i;

	/* Clean slate */
	memset(obj, 0, sizeof(*obj));

	/* Assign the kind and copy across data */
	obj->kind = k;
	obj->tval = k->tval;
	obj->sval = k->sval;
	obj->ac = k->ac;
	obj->dd = k->dd;
	obj->ds = k->ds;
	obj->weight = k->weight;
	obj->effect = k->effect;
	obj->time = k->time;

	/* Default number */
	obj->number = 1;

	/* Copy flags */
	of_copy(obj->flags, k->base->flags);
	of_copy(obj->flags, k->flags);

	/* Assign modifiers */
	for (i = 0; i < OBJ_MOD_MAX; i++)
		obj->modifiers[i] = randcalc(k->modifiers[i], lev, rand_aspect);

	/* Assign charges (wands/staves only) */
	if (tval_can_have_charges(obj))
		obj->pval = randcalc(k->charge, lev, rand_aspect);

	/* Assign pval for food, oil and launchers */
	if (tval_is_edible(obj) || tval_is_potion(obj) || tval_is_fuel(obj) ||
		tval_is_launcher(obj))
		obj->pval
			= randcalc(k->pval, lev, rand_aspect);

	/* Default fuel */
	if (tval_is_light(obj)) {
		if (of_has(obj->flags, OF_BURNS_OUT))
			obj->timeout = z_info->fuel_torch;
		else if (of_has(obj->flags, OF_TAKES_FUEL))
			obj->timeout = z_info->default_lamp;
	}

	/* Default magic */
	obj->to_h = randcalc(k->to_h, lev, rand_aspect);
	obj->to_d = randcalc(k->to_d, lev, rand_aspect);
	obj->to_a = randcalc(k->to_a, lev, rand_aspect);

	/* Default slays and brands */
	copy_slay(&obj->slays, k->slays);
	copy_brand(&obj->brands, k->brands);

	/* Default resists */
	for (i = 0; i < ELEM_MAX; i++) {
		obj->el_info[i].res_level = k->el_info[i].res_level;
		obj->el_info[i].flags = k->el_info[i].flags;
		obj->el_info[i].flags |= k->base->el_info[i].flags;
	}
}
Ejemplo n.º 4
0
/**
 * Determine if an item can "absorb" a second item
 *
 * See "object_absorb()" for the actual "absorption" code.
 *
 * If permitted, we allow weapons/armor to stack, if "known".
 *
 * Missiles will combine if both stacks have the same "known" status.
 * This is done to make unidentified stacks of missiles useful.
 *
 * Food, potions, scrolls, and "easy know" items always stack.
 *
 * Chests, and activatable items, except rods, never stack (for various
 * reasons).
 */
bool object_stackable(const struct object *obj1, const struct object *obj2,
					  object_stack_t mode)
{
	int i;

	/* Equipment items don't stack */
	if (object_is_equipped(player->body, obj1))
		return false;
	if (object_is_equipped(player->body, obj2))
		return false;

	/* If either item is unknown, do not stack */
	if (mode & OSTACK_LIST && obj1->kind != obj1->known->kind) return false;
	if (mode & OSTACK_LIST && obj2->kind != obj2->known->kind) return false;

	/* Hack -- identical items cannot be stacked */
	if (obj1 == obj2) return false;

	/* Require identical object kinds */
	if (obj1->kind != obj2->kind) return false;

	/* Different flags don't stack */
	if (!of_is_equal(obj1->flags, obj2->flags)) return false;

	/* Different elements don't stack */
	for (i = 0; i < ELEM_MAX; i++) {
		if (obj1->el_info[i].res_level != obj2->el_info[i].res_level)
			return false;
		if ((obj1->el_info[i].flags & (EL_INFO_HATES | EL_INFO_IGNORE)) !=
			(obj2->el_info[i].flags & (EL_INFO_HATES | EL_INFO_IGNORE)))
			return false;
	}

	/* Artifacts never stack */
	if (obj1->artifact || obj2->artifact) return false;

	/* Analyze the items */
	if (tval_is_chest(obj1)) {
		/* Chests never stack */
		return false;
	}
	else if (tval_is_edible(obj1) || tval_is_potion(obj1) ||
		tval_is_scroll(obj1) || tval_is_rod(obj1)) {
		/* Food, potions, scrolls and rods all stack nicely,
		   since the kinds are identical, either both will be
		   aware or both will be unaware */
	} else if (tval_can_have_charges(obj1) || tval_is_money(obj1)) {
		/* Gold, staves and wands stack most of the time */
		/* Too much gold or too many charges */
		if (obj1->pval + obj2->pval > MAX_PVAL)
			return false;

		/* ... otherwise ok */
	} else if (tval_is_weapon(obj1) || tval_is_armor(obj1) ||
		tval_is_jewelry(obj1) || tval_is_light(obj1)) {
		bool obj1_is_known = object_fully_known((struct object *)obj1);
		bool obj2_is_known = object_fully_known((struct object *)obj2);

		/* Require identical values */
		if (obj1->ac != obj2->ac) return false;
		if (obj1->dd != obj2->dd) return false;
		if (obj1->ds != obj2->ds) return false;

		/* Require identical bonuses */
		if (obj1->to_h != obj2->to_h) return false;
		if (obj1->to_d != obj2->to_d) return false;
		if (obj1->to_a != obj2->to_a) return false;

		/* Require all identical modifiers */
		for (i = 0; i < OBJ_MOD_MAX; i++)
			if (obj1->modifiers[i] != obj2->modifiers[i])
				return (false);

		/* Require identical ego-item types */
		if (obj1->ego != obj2->ego) return false;

		/* Require identical curses */
		if (!curses_are_equal(obj1->curses, obj2->curses)) return false;

		/* Hack - Never stack recharging wearables ... */
		if ((obj1->timeout || obj2->timeout) &&
			!tval_is_light(obj1)) return false;

		/* ... and lights must have same amount of fuel */
		else if ((obj1->timeout != obj2->timeout) &&
				 tval_is_light(obj1)) return false;

		/* Prevent unIDd items stacking with IDd items in the object list */
		if (mode & OSTACK_LIST && (obj1_is_known != obj2_is_known))
			return false;
	} else {
		/* Anything else probably okay */
	}

	/* Require compatible inscriptions */
	if (obj1->note && obj2->note && (obj1->note != obj2->note))
		return false;

	/* They must be similar enough */
	return true;
}
Ejemplo n.º 5
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;
}
Ejemplo n.º 6
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;
}