/** * 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; }
/** * 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; }