/* * desc.c: handle object descriptions, mostly string handling code * * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "constant.h" #include "config.h" #include "types.h" #include "externs.h" #ifdef USG #ifndef ATARIST_MWC #include #endif #else #include #endif /* Lets do all prototypes correctly.... -CWS */ #ifndef NO_LINT_ARGS #ifdef __STDC__ static void unsample(inven_type *); #else static void unsample(); #endif #endif char titles[MAX_TITLES][10]; /* Object descriptor routines */ int is_a_vowel(ch) int ch; { switch (ch & 127) { case 'a': case 'e': case 'i': case 'o': case 'u': case 'A': case 'E': case 'I': case 'O': case 'U': return (TRUE); default: return (FALSE); } } /* Initialize all Potions, wands, staves, scrolls, etc. */ void magic_init() { register int h, i, j, k; register const char *tmp; vtype string; set_seed(randes_seed); /* The first 3 entries for colors are fixed, (slime & apple juice, water) */ for (i = 3; i < MAX_COLORS; i++) { j = randint(MAX_COLORS - 3) + 2; tmp = colors[i]; colors[i] = colors[j]; colors[j] = tmp; } for (i = 0; i < MAX_WOODS; i++) { j = randint(MAX_WOODS) - 1; tmp = woods[i]; woods[i] = woods[j]; woods[j] = tmp; } for (i = 0; i < MAX_METALS; i++) { j = randint(MAX_METALS) - 1; tmp = metals[i]; metals[i] = metals[j]; metals[j] = tmp; } for (i = 0; i < MAX_ROCKS; i++) { j = randint(MAX_ROCKS) - 1; tmp = rocks[i]; rocks[i] = rocks[j]; rocks[j] = tmp; } for (i = 0; i < MAX_AMULETS; i++) { j = randint(MAX_AMULETS) - 1; tmp = amulets[i]; amulets[i] = amulets[j]; amulets[j] = tmp; } for (i = 0; i < MAX_MUSH; i++) { j = randint(MAX_MUSH) - 1; tmp = mushrooms[i]; mushrooms[i] = mushrooms[j]; mushrooms[j] = tmp; } for (h = 0; h < MAX_TITLES; h++) { string[0] = '\0'; k = randint(2) + 1; for (i = 0; i < k; i++) { for (j = randint(2); j > 0; j--) (void)strcat(string, syllables[randint(MAX_SYLLABLES) - 1]); if (i < k - 1) (void)strcat(string, " "); } if (string[8] == ' ') string[8] = '\0'; else string[9] = '\0'; (void)strcpy(titles[h], string); } reset_seed(); } int16 object_offset(t_ptr) inven_type *t_ptr; { switch (t_ptr->tval) { case TV_ROD: return (7); /* -CFT */ case TV_AMULET: return (0); case TV_RING: return (1); case TV_STAFF: return (2); case TV_WAND: return (3); case TV_SCROLL1: case TV_SCROLL2: return (4); case TV_POTION1: case TV_POTION2: return (5); case TV_FOOD: if ((t_ptr->subval & (ITEM_SINGLE_STACK_MIN - 1)) < MAX_MUSH) return (6); return (-1); default: return (-1); } } /* Remove "Secret" symbol for identity of object */ void known1(i_ptr) inven_type *i_ptr; { int16 offset; int8u indexx; if ((offset = object_offset(i_ptr)) < 0) return; offset <<= 6; indexx = i_ptr->subval & (ITEM_SINGLE_STACK_MIN - 1); object_ident[offset + indexx] |= OD_KNOWN1; /* clear the tried flag, since it is now known */ object_ident[offset + indexx] &= ~OD_TRIED; } int known1_p(i_ptr) inven_type *i_ptr; { int16 offset; int8u indexx; /* Items which don't have a 'color' are always known1, so that they can be * carried in order in the inventory. */ if ((offset = object_offset(i_ptr)) < 0) return OD_KNOWN1; if (store_bought_p(i_ptr)) return OD_KNOWN1; offset <<= 6; indexx = i_ptr->subval & (ITEM_SINGLE_STACK_MIN - 1); return (object_ident[offset + indexx] & OD_KNOWN1); } /* Remove "Secret" symbol for identity of plusses */ void known2(i_ptr) inven_type *i_ptr; { unsample(i_ptr); i_ptr->ident |= ID_KNOWN2; } int known2_p(i_ptr) inven_type *i_ptr; { return (i_ptr->ident & ID_KNOWN2); } void clear_known2(i_ptr) inven_type *i_ptr; { i_ptr->ident &= ~ID_KNOWN2; } void clear_empty(i_ptr) inven_type *i_ptr; { i_ptr->ident &= ~ID_EMPTY; } void store_bought(i_ptr) inven_type *i_ptr; { i_ptr->ident |= ID_STOREBOUGHT; known2(i_ptr); } int store_bought_p(i_ptr) inven_type *i_ptr; { return (i_ptr->ident & ID_STOREBOUGHT); } /* Remove an automatically generated inscription. -CJS- */ static void unsample(i_ptr) inven_type *i_ptr; { int16 offset; int8u indexx; /* used to clear ID_DAMD flag, but I think it should remain set */ i_ptr->ident &= ~(ID_MAGIK | ID_EMPTY); if ((offset = object_offset(i_ptr)) < 0) return; offset <<= 6; indexx = i_ptr->subval & (ITEM_SINGLE_STACK_MIN - 1); object_ident[offset + indexx] &= ~OD_TRIED; } /* unquote() is no longer needed */ /* Somethings been sampled -CJS- */ void sample(i_ptr) inven_type *i_ptr; { int16 offset; int8u indexx; if ((offset = object_offset(i_ptr)) < 0) return; offset <<= 6; indexx = i_ptr->subval & (ITEM_SINGLE_STACK_MIN - 1); object_ident[offset + indexx] |= OD_TRIED; } /* Somethings been identified */ /* * extra complexity by CJS so that it can merge store/dungeon objects when * appropriate */ void identify(item) int *item; { register int i, x1, x2; int j; register inven_type *i_ptr, *t_ptr; i_ptr = &inventory[*item]; if ((i_ptr->flags & TR_CURSED) && (i_ptr->tval != TV_MAGIC_BOOK) && (i_ptr->tval != TV_PRAYER_BOOK)) add_inscribe(i_ptr, ID_DAMD); if (!known1_p(i_ptr)) { known1(i_ptr); x1 = i_ptr->tval; x2 = i_ptr->subval; if (x2 < ITEM_SINGLE_STACK_MIN || x2 >= ITEM_GROUP_MIN) /* no merging possible */ ; else for (i = 0; i < inven_ctr; i++) { t_ptr = &inventory[i]; if (t_ptr->tval == x1 && t_ptr->subval == x2 && i != *item && ((int)t_ptr->number + (int)i_ptr->number < 256)) { /* make *item the smaller number */ if (*item > i) { j = *item; *item = i; i = j; } msg_print("You combine similar objects from the shop and dungeon."); inventory[*item].number += inventory[i].number; inven_ctr--; for (j = i; j < inven_ctr; j++) inventory[j] = inventory[j + 1]; invcopy(&inventory[j], OBJ_NOTHING); } } } } /* * If an object has lost magical properties, remove the appropriate portion * of the name. -CJS- */ void unmagic_name(i_ptr) inven_type *i_ptr; { i_ptr->name2 = SN_NULL; } /* defines for p1_use, determine how the p1 field is printed */ #define IGNORED 0 /* never show (+x) */ #define CHARGES 1 /* show p1 as charges */ #define PLUSSES 2 /* show p1 as (+x) only */ #define LIGHT 3 /* show p1 as turns of light */ #define FLAGS 4 /* show p1 as (+x of yyy) */ #define Z_PLUSSES 5 /* always show p1 as (+x), even if x==0 -CWS */ /* Returns a description of item for inventory * pref indicates that there should be an article added (prefix) * * note that since out_val can easily exceed 80 characters, objdes must * always be called with a bigvtype as the first paramter ***** * Note that objdes now never returns a description ending with punctuation * (ie, "."'s) -CWS */ void objdes(out_val, i_ptr, pref) char *out_val; register inven_type *i_ptr; int pref; { /* base name, modifier string */ register const char *basenm, *modstr; bigvtype tmp_val; vtype tmp_str, damstr; int indexx, p1_use, modify, append_name; indexx = i_ptr->subval & (ITEM_SINGLE_STACK_MIN - 1); basenm = object_list[i_ptr->index].name; modstr = NULL; damstr[0] = '\0'; p1_use = IGNORED; modify = (known1_p(i_ptr) ? FALSE : TRUE); append_name = FALSE; switch (i_ptr->tval) { case TV_MISC: case TV_CHEST: break; case TV_SLING_AMMO: case TV_BOLT: case TV_ARROW: (void)sprintf(damstr, " (%dd%d)", i_ptr->damage[0], i_ptr->damage[1]); break; case TV_LIGHT: p1_use = LIGHT; if (!stricmp("The Phial of Galadriel", basenm) && !known2_p(i_ptr)) basenm = "a Shining Phial"; if (!stricmp("The Star of Elendil", basenm) && !known2_p(i_ptr)) basenm = "a Shining Gem"; if (!stricmp("The Arkenstone of Thrain", basenm) && !known2_p(i_ptr)) basenm = "a Shining Gem"; break; case TV_SPIKE: break; case TV_BOW: switch(i_ptr->subval) { /* whole new code -CFT */ case 20: case 1: /* sling, sh. bow */ strcpy(damstr, " (x2)"); break; case 21: case 2: case 10: /* sling of M, s bow of M, l bow, l xbow */ strcpy(damstr, " (x3)"); break; case 3: case 11: /* l bow of M, l xbow of M, h xbow, BARD, CUBRAGOL */ strcpy(damstr, " (x4)"); break; case 4: case 12: /* h xbow of M, BELEG */ strcpy(damstr, " (x5)"); break; default: /* just in case... */ strcpy(damstr, " (unknown mult.)"); } if (i_ptr->flags2 & TR_ARTIFACT) /* only show p1 for artifacts... */ p1_use = FLAGS; break; case TV_HAFTED: case TV_POLEARM: case TV_SWORD: (void)sprintf(damstr, " (%dd%d)", i_ptr->damage[0], i_ptr->damage[1]); p1_use = FLAGS; break; case TV_DIGGING: p1_use = Z_PLUSSES; (void)sprintf(damstr, " (%dd%d)", i_ptr->damage[0], i_ptr->damage[1]); break; case TV_BOOTS: case TV_GLOVES: case TV_CLOAK: case TV_HELM: case TV_SHIELD: case TV_HARD_ARMOR: case TV_SOFT_ARMOR: p1_use = FLAGS; break; case TV_AMULET: p1_use = FLAGS; if (modify || !(plain_descriptions || store_bought_p(i_ptr))) { basenm = "& %s Amulet"; modstr = amulets[indexx]; if (!modify) append_name = TRUE; } else { basenm = "& Amulet"; append_name = TRUE; } break; case TV_RING: if (!stricmp("Power", basenm)) { /* name this "the One Ring" -CWS */ append_name = FALSE; if (!known2_p(i_ptr)) basenm = "a plain gold Ring"; else basenm = "The One Ring"; } else if (modify || !(plain_descriptions || store_bought_p(i_ptr))) { basenm = "& %s Ring"; modstr = rocks[indexx]; if (!modify) append_name = TRUE; } else { basenm = "& Ring"; append_name = TRUE; } p1_use = PLUSSES; break; case TV_STAFF: if (modify || !(plain_descriptions || store_bought_p(i_ptr))) { basenm = "& %s Staff"; modstr = woods[indexx]; if (!modify) append_name = TRUE; } else { basenm = "& Staff"; append_name = TRUE; } p1_use = CHARGES; break; case TV_WAND: if (modify || !(plain_descriptions || store_bought_p(i_ptr))) { basenm = "& %s Wand"; modstr = metals[indexx]; if (!modify) append_name = TRUE; } else { basenm = "& Wand"; append_name = TRUE; } p1_use = CHARGES; break; case TV_ROD: if (modify || !(plain_descriptions || store_bought_p(i_ptr))) { basenm = "& %s Rod"; modstr = metals[indexx]; if (!modify) append_name = TRUE; } else { basenm = "& Rod"; append_name = TRUE; } break; case TV_SCROLL1: case TV_SCROLL2: if (modify || !(plain_descriptions || store_bought_p(i_ptr))) { basenm = "& Scroll~ titled \"%s\""; modstr = titles[indexx]; if (!modify) append_name = TRUE; } else { basenm = "& Scroll~"; append_name = TRUE; } break; case TV_POTION1: case TV_POTION2: if (modify || !(plain_descriptions || store_bought_p(i_ptr))) { basenm = "& %s Potion~"; modstr = colors[indexx]; if (!modify) append_name = TRUE; } else { basenm = "& Potion~"; append_name = TRUE; } break; case TV_FLASK: break; case TV_FOOD: if (modify || !(plain_descriptions || store_bought_p(i_ptr))) { if (!modify) append_name = TRUE; if (indexx <= 15) basenm = "& %s Mushroom~"; else if (indexx <= 20) basenm = "& Hairy %s Mold~"; else append_name = FALSE; /* Ordinary food has no name appended. */ if (indexx <= 20) modstr = mushrooms[indexx]; } else { append_name = TRUE; if (indexx <= 15) basenm = "& Mushroom~"; else if (indexx <= 20) basenm = "& Hairy Mold~"; else /* Ordinary food does not have a name appended. */ append_name = FALSE; } break; case TV_MAGIC_BOOK: modstr = basenm; basenm = "& Book~ of Magic Spells %s"; break; case TV_PRAYER_BOOK: modstr = basenm; basenm = "& Holy Book~ of Prayers %s"; break; case TV_OPEN_DOOR: case TV_CLOSED_DOOR: case TV_SECRET_DOOR: case TV_RUBBLE: break; case TV_GOLD: case TV_INVIS_TRAP: case TV_VIS_TRAP: case TV_UP_STAIR: case TV_DOWN_STAIR: (void)strcpy(out_val, object_list[i_ptr->index].name); /* (void) strcat(out_val, "."); avoid ".." bug -CWS */ return; case TV_STORE_DOOR: (void)sprintf(out_val, "the entrance to the %s", object_list[i_ptr->index].name); return; default: (void)strcpy(out_val, "Error in objdes()"); return; } if (modstr != NULL) (void)sprintf(tmp_val, basenm, modstr); else (void)strcpy(tmp_val, basenm); if (append_name) { (void)strcat(tmp_val, " of "); (void)strcat(tmp_val, object_list[i_ptr->index].name); } if (i_ptr->number != 1) { insert_str(tmp_val, "ch~", "ches"); insert_str(tmp_val, "~", "s"); } else insert_str(tmp_val, "~", NULL); if (!pref) { if (!strncmp("some", tmp_val, 4)) (void)strcpy(out_val, &tmp_val[5]); else if (tmp_val[0] == '&') /* eliminate the '& ' at the beginning */ (void)strcpy(out_val, &tmp_val[2]); else (void)strcpy(out_val, tmp_val); } else { if (i_ptr->name2 != SN_NULL && known2_p(i_ptr)) { (void)strcat(tmp_val, " "); (void)strcat(tmp_val, special_names[i_ptr->name2]); } if (damstr[0] != '\0') (void)strcat(tmp_val, damstr); if (known2_p(i_ptr)) { /* originally used %+d, but several machines don't support it */ if (i_ptr->ident & ID_SHOW_HITDAM) (void)sprintf(tmp_str, " (%c%d,%c%d)", (i_ptr->tohit < 0) ? '-' : '+', MY_ABS( i_ptr->tohit), (i_ptr->todam < 0) ? '-' : '+', MY_ABS(i_ptr->todam)); else if (i_ptr->tohit != 0) (void)sprintf(tmp_str, " (%c%d)", (i_ptr->tohit < 0) ? '-' : '+', MY_ABS(i_ptr->tohit)); else if (i_ptr->todam != 0) (void)sprintf(tmp_str, " (%c%d)", (i_ptr->todam < 0) ? '-' : '+', MY_ABS(i_ptr->todam)); else tmp_str[0] = '\0'; (void)strcat(tmp_val, tmp_str); } /* Crowns have a zero base AC, so make a special test for them. */ if (i_ptr->ac != 0 || (i_ptr->tval == TV_HELM)) { (void)sprintf(tmp_str, " [%d", i_ptr->ac); (void)strcat(tmp_val, tmp_str); if (known2_p(i_ptr)) { /* originally used %+d, but several machines don't support it */ (void)sprintf(tmp_str, ",%c%d", (i_ptr->toac < 0) ? '-' : '+', MY_ABS(i_ptr->toac)); (void)strcat(tmp_val, tmp_str); } (void)strcat(tmp_val, "]"); } else if ((i_ptr->toac != 0) && known2_p(i_ptr)) { /* originally used %+d, but several machines don't support it */ (void)sprintf(tmp_str, " [%c%d]", (i_ptr->toac < 0) ? '-' : '+', MY_ABS(i_ptr->toac)); (void)strcat(tmp_val, tmp_str); } tmp_str[0] = '\0'; /* override defaults, check for p1 flags in the ident field */ if (p1_use != IGNORED) { if (p1_use == LIGHT); else if (i_ptr->ident & ID_NOSHOW_P1) p1_use = IGNORED; else if (i_ptr->ident & ID_NOSHOW_TYPE) p1_use = PLUSSES; } if (p1_use == IGNORED); else if ((p1_use == LIGHT) && !(i_ptr->flags2 & TR_ARTIFACT)) (void)sprintf(tmp_str, " with %d turns of light", i_ptr->p1); else if (known2_p(i_ptr)) { if (p1_use == CHARGES) (void)sprintf(tmp_str, " (%d charge%s", i_ptr->p1, (i_ptr->p1 == 1 ? ")" : "s)")); else if (p1_use == Z_PLUSSES) /* (+0) digging implements -CWS */ (void)sprintf(tmp_str, " (%c%d)", (i_ptr->p1 < 0) ? '-' : '+', MY_ABS(i_ptr->p1)); else if (i_ptr->p1 != 0) { if (p1_use == PLUSSES) (void)sprintf(tmp_str, " (%c%d)", (i_ptr->p1 < 0) ? '-' : '+', MY_ABS(i_ptr->p1)); else if (i_ptr->ident & ID_NOSHOW_TYPE) (void)sprintf(tmp_str, " (%c%d)", (i_ptr->p1 < 0) ? '-' : '+', MY_ABS(i_ptr->p1)); else if (p1_use == FLAGS) { if ((i_ptr->flags & TR_SPEED) && (i_ptr->name2 != SN_SPEED)) (void)sprintf(tmp_str, " (%c%d to speed)", (i_ptr->p1 < 0) ? '-' : '+', MY_ABS(i_ptr->p1)); else if (i_ptr->flags & TR_SEARCH) /* && (i_ptr->name2 != SN_SEARCH)) */ (void)sprintf(tmp_str, " (%c%d to searching)", (i_ptr->p1 < 0) ? '-' : '+', MY_ABS(i_ptr->p1)); else if ((i_ptr->flags & TR_STEALTH) && (i_ptr->name2 != SN_STEALTH)) (void)sprintf(tmp_str, " (%c%d to stealth)", (i_ptr->p1 < 0) ? '-' : '+', MY_ABS(i_ptr->p1)); else if ((i_ptr->flags & TR_INFRA) && (i_ptr->name2 != SN_INFRAVISION)) (void)sprintf(tmp_str, " (%c%d to infravision)", (i_ptr->p1 < 0) ? '-' : '+', MY_ABS(i_ptr->p1)); else if (i_ptr->flags2 & TR_ATTACK_SPD) { if (MY_ABS(i_ptr->p1) == 1) (void)sprintf(tmp_str, " (%c%d attack)", (i_ptr->p1 < 0) ? '-' : '+', MY_ABS(i_ptr->p1)); else (void)sprintf(tmp_str, " (%c%d attacks)", (i_ptr->p1 < 0) ? '-' : '+', MY_ABS(i_ptr->p1)); } /* attack speed */ else (void)sprintf(tmp_str, " (%c%d)", (i_ptr->p1 < 0) ? '-' : '+', MY_ABS(i_ptr->p1)); } /* p1_use == FLAGS */ } /* p1 != 0 */ } /* if known2_p (fully identified) */ (void)strcat(tmp_val, tmp_str); /* ampersand is always the first character */ if (tmp_val[0] == '&') { /* use &tmp_val[1], so that & does not appear in output */ if (i_ptr->number > 1) (void)sprintf(out_val, "%d%s", (int)i_ptr->number, &tmp_val[1]); else if (i_ptr->number < 1) (void)sprintf(out_val, "%s%s", "no more", &tmp_val[1]); else if (known2_p(i_ptr) && (i_ptr->tval >= TV_MIN_WEAR) && (i_ptr->tval <= TV_MAX_WEAR) && (i_ptr->flags2 & TR_ARTIFACT)) (void)sprintf(out_val, "The%s", &tmp_val[1]); else if (is_a_vowel(tmp_val[2])) (void)sprintf(out_val, "an%s", &tmp_val[1]); else (void)sprintf(out_val, "a%s", &tmp_val[1]); } /* handle 'no more' case specially */ else if (i_ptr->number < 1) { /* check for "some" at start */ if (!strncmp("some", tmp_val, 4)) (void)sprintf(out_val, "no more %s", &tmp_val[5]); /* here if no article */ else (void)sprintf(out_val, "no more %s", tmp_val); } else (void)strcpy(out_val, tmp_val); tmp_str[0] = '\0'; if ((indexx = object_offset(i_ptr)) >= 0) { indexx = (indexx <<= 6) + (i_ptr->subval & (ITEM_SINGLE_STACK_MIN - 1)); /* don't print tried string for store bought items */ if ((object_ident[indexx] & OD_TRIED) && !store_bought_p(i_ptr)) (void)strcat(tmp_str, "tried "); } if ((i_ptr->ident & (ID_MAGIK | ID_EMPTY | ID_DAMD)) && i_ptr->tval != TV_MAGIC_BOOK && i_ptr->tval != TV_PRAYER_BOOK) { if (i_ptr->ident & ID_MAGIK) (void)strcat(tmp_str, "blessed "); if (i_ptr->ident & ID_EMPTY) (void)strcat(tmp_str, "empty "); if (i_ptr->ident & ID_DAMD) (void)strcat(tmp_str, "cursed "); } if ((known2_p(i_ptr) || store_bought_p(i_ptr)) && ((!strncmp(i_ptr->inscrip, "average", 7)) || (!strncmp(i_ptr->inscrip, "good", 4)) || (!strncmp(i_ptr->inscrip, "excellent", 9)) || (!strncmp(i_ptr->inscrip, "special", 7)))) i_ptr->inscrip[0] = '\0'; if (i_ptr->inscrip[0] != '\0') (void)strcat(tmp_str, i_ptr->inscrip); else if ((indexx = strlen(tmp_str)) > 0) /* remove the extra blank at the end */ tmp_str[indexx - 1] = '\0'; if (tmp_str[0]) { (void)sprintf(tmp_val, " {%s}", tmp_str); (void)strcat(out_val, tmp_val); } /* (void) strcat(out_val, "."); avoid ".." bug -CWS */ } } void invcopy(to, from_index) register inven_type *to; int from_index; { register treasure_type *from; from = &object_list[from_index]; to->index = from_index; to->name2 = SN_NULL; to->inscrip[0] = '\0'; to->flags = from->flags; to->flags2 = from->flags2; to->tval = from->tval; to->tchar = from->tchar; to->p1 = from->p1; to->cost = from->cost; to->subval = from->subval; to->number = from->number; to->weight = from->weight; to->tohit = from->tohit; to->todam = from->todam; to->ac = from->ac; to->toac = from->toac; to->damage[0] = from->damage[0]; to->damage[1] = from->damage[1]; to->level = from->level; to->ident = 0; } /* Describe number of remaining charges. -RAK- */ void desc_charges(item_val) int item_val; { register int rem_num; vtype out_val; if (known2_p(&inventory[item_val])) { rem_num = inventory[item_val].p1; (void)sprintf(out_val, "You have %d charges remaining.", rem_num); msg_print(out_val); } } /* Describe amount of item remaining. -RAK- */ void desc_remain(item_val) int item_val; { bigvtype out_val, tmp_str; register inven_type *i_ptr; i_ptr = &inventory[item_val]; i_ptr->number--; objdes(tmp_str, i_ptr, TRUE); i_ptr->number++; (void)sprintf(out_val, "You have %s.", tmp_str); msg_print(out_val); }