Files
nethack/src/o_init.c
nethack.rankin 97928335ae C/#name menu, calling old discoveries [1 of 2] (trunk only)
Implement <Someone>'s menu-mode for #name, primarily because it
is the natural place to add [re]naming entries in the discoveries list,
something that was requested in the newsgroup ten or so years ago.  The
latter allows changing the type name of something which has previously
been named and is no longer being carried.

     This also makes the C command become a synonym for #name or vice
versa; one or the other could now be reassigned to something else.

#name
  What do you want to name?
  a - a monster
  b - a particular object in inventory
  c - a type of object in inventory
  d - a type of object on discoveries list

Menu group accelerators provide unseen alternate choices:  C for monster,
y for individual object, n for object type (and d for discoveries, but
that's only interesting if inventory is empty so that usual b & c are
omitted and discoveries entry moves up to b).  These alternates allow
`#name y' and `#name n' to work the same as before, for users who have
trouble retraining their fingers.  Using C to name a monster now takes an
extra keystroke, but using `C C' for it could make that be less annoying.
2007-05-25 02:01:54 +00:00

539 lines
14 KiB
C

/* SCCS Id: @(#)o_init.c 3.5 2007/05/21 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
#include "hack.h"
#include "lev.h" /* save & restore info */
STATIC_DCL void FDECL(setgemprobs, (d_level*));
STATIC_DCL void FDECL(shuffle,(int,int,BOOLEAN_P));
STATIC_DCL void NDECL(shuffle_all);
STATIC_DCL boolean FDECL(interesting_to_discover,(int));
static NEARDATA short disco[NUM_OBJECTS] = DUMMY;
#ifdef USE_TILES
STATIC_DCL void NDECL(shuffle_tiles);
extern short glyph2tile[]; /* from tile.c */
/* Shuffle tile assignments to match descriptions, so a red potion isn't
* displayed with a blue tile and so on.
*
* Tile assignments are not saved, and shouldn't be so that a game can
* be resumed on an otherwise identical non-tile-using binary, so we have
* to reshuffle the assignments from oc_descr_idx information when a game
* is restored. So might as well do that the first time instead of writing
* another routine.
*/
STATIC_OVL void
shuffle_tiles()
{
int i;
short tmp_tilemap[NUM_OBJECTS];
for (i = 0; i < NUM_OBJECTS; i++)
tmp_tilemap[i] =
glyph2tile[objects[i].oc_descr_idx + GLYPH_OBJ_OFF];
for (i = 0; i < NUM_OBJECTS; i++)
glyph2tile[i + GLYPH_OBJ_OFF] = tmp_tilemap[i];
}
#endif /* USE_TILES */
STATIC_OVL void
setgemprobs(dlev)
d_level *dlev;
{
int j, first, lev;
if (dlev)
lev = (ledger_no(dlev) > maxledgerno())
? maxledgerno() : ledger_no(dlev);
else
lev = 0;
first = bases[GEM_CLASS];
for(j = 0; j < 9-lev/3; j++)
objects[first+j].oc_prob = 0;
first += j;
if (first > LAST_GEM || objects[first].oc_class != GEM_CLASS ||
OBJ_NAME(objects[first]) == (char *)0) {
raw_printf("Not enough gems? - first=%d j=%d LAST_GEM=%d",
first, j, LAST_GEM);
wait_synch();
}
for (j = first; j <= LAST_GEM; j++)
objects[j].oc_prob = (171+j-first)/(LAST_GEM+1-first);
}
/* shuffle descriptions on objects o_low to o_high */
STATIC_OVL void
shuffle(o_low, o_high, domaterial)
int o_low, o_high;
boolean domaterial;
{
int i, j, num_to_shuffle;
short sw;
int color;
for (num_to_shuffle = 0, j=o_low; j <= o_high; j++)
if (!objects[j].oc_name_known) num_to_shuffle++;
if (num_to_shuffle < 2) return;
for (j=o_low; j <= o_high; j++) {
if (objects[j].oc_name_known) continue;
do
i = j + rn2(o_high-j+1);
while (objects[i].oc_name_known);
sw = objects[j].oc_descr_idx;
objects[j].oc_descr_idx = objects[i].oc_descr_idx;
objects[i].oc_descr_idx = sw;
sw = objects[j].oc_tough;
objects[j].oc_tough = objects[i].oc_tough;
objects[i].oc_tough = sw;
color = objects[j].oc_color;
objects[j].oc_color = objects[i].oc_color;
objects[i].oc_color = color;
/* shuffle material */
if (domaterial) {
sw = objects[j].oc_material;
objects[j].oc_material = objects[i].oc_material;
objects[i].oc_material = sw;
}
}
}
void
init_objects()
{
register int i, first, last, sum;
register char oclass;
#ifdef TEXTCOLOR
# define COPY_OBJ_DESCR(o_dst,o_src) \
o_dst.oc_descr_idx = o_src.oc_descr_idx,\
o_dst.oc_color = o_src.oc_color
#else
# define COPY_OBJ_DESCR(o_dst,o_src) o_dst.oc_descr_idx = o_src.oc_descr_idx
#endif
/* bug fix to prevent "initialization error" abort on Intel Xenix.
* reported by mikew@semike
*/
for (i = 0; i < MAXOCLASSES; i++)
bases[i] = 0;
/* initialize object descriptions */
for (i = 0; i < NUM_OBJECTS; i++)
objects[i].oc_name_idx = objects[i].oc_descr_idx = i;
/* init base; if probs given check that they add up to 1000,
otherwise compute probs */
first = 0;
while( first < NUM_OBJECTS ) {
oclass = objects[first].oc_class;
last = first+1;
while (last < NUM_OBJECTS && objects[last].oc_class == oclass) last++;
bases[(int)oclass] = first;
if (oclass == GEM_CLASS) {
setgemprobs((d_level *)0);
if (rn2(2)) { /* change turquoise from green to blue? */
COPY_OBJ_DESCR(objects[TURQUOISE],objects[SAPPHIRE]);
}
if (rn2(2)) { /* change aquamarine from green to blue? */
COPY_OBJ_DESCR(objects[AQUAMARINE],objects[SAPPHIRE]);
}
switch (rn2(4)) { /* change fluorite from violet? */
case 0: break;
case 1: /* blue */
COPY_OBJ_DESCR(objects[FLUORITE],objects[SAPPHIRE]);
break;
case 2: /* white */
COPY_OBJ_DESCR(objects[FLUORITE],objects[DIAMOND]);
break;
case 3: /* green */
COPY_OBJ_DESCR(objects[FLUORITE],objects[EMERALD]);
break;
}
}
check:
sum = 0;
for(i = first; i < last; i++) sum += objects[i].oc_prob;
if(sum == 0) {
for(i = first; i < last; i++)
objects[i].oc_prob = (1000+i-first)/(last-first);
goto check;
}
if(sum != 1000)
error("init-prob error for class %d (%d%%)", oclass, sum);
first = last;
}
/* shuffle descriptions */
shuffle_all();
#ifdef USE_TILES
shuffle_tiles();
#endif
}
/* retrieve the range of objects that otyp shares descriptions with */
void
obj_shuffle_range(otyp, lo_p, hi_p)
int otyp; /* input: representative item */
int *lo_p, *hi_p; /* output: range that item belongs among */
{
int i, ocls = objects[otyp].oc_class;
/* default is just the object itself */
*lo_p = *hi_p = otyp;
switch (ocls) {
case ARMOR_CLASS:
if (otyp >= HELMET && otyp <= HELM_OF_TELEPATHY)
*lo_p = HELMET, *hi_p = HELM_OF_TELEPATHY;
else if (otyp >= LEATHER_GLOVES && otyp <= GAUNTLETS_OF_DEXTERITY)
*lo_p = LEATHER_GLOVES, *hi_p = GAUNTLETS_OF_DEXTERITY;
else if (otyp >= CLOAK_OF_PROTECTION && otyp <= CLOAK_OF_DISPLACEMENT)
*lo_p = CLOAK_OF_PROTECTION, *hi_p = CLOAK_OF_DISPLACEMENT;
else if (otyp >= SPEED_BOOTS && otyp <= LEVITATION_BOOTS)
*lo_p = SPEED_BOOTS, *hi_p = LEVITATION_BOOTS;
break;
case POTION_CLASS:
/* potion of water has the only fixed description */
*lo_p = bases[POTION_CLASS];
*hi_p = POT_WATER - 1;
break;
case AMULET_CLASS:
case SCROLL_CLASS:
case SPBOOK_CLASS:
/* exclude non-magic types and also unique ones */
*lo_p = bases[ocls];
for (i = *lo_p; objects[i].oc_class == ocls; i++)
if (objects[i].oc_unique || !objects[i].oc_magic) break;
*hi_p = i - 1;
break;
case RING_CLASS:
case WAND_CLASS:
case VENOM_CLASS:
/* entire class */
*lo_p = bases[ocls];
for (i = *lo_p; objects[i].oc_class == ocls; i++) continue;
*hi_p = i - 1;
break;
}
/* artifact checking might ask about item which isn't part of any range
but fell within the classes that do have ranges specified above */
if (otyp < *lo_p || otyp > *hi_p) *lo_p = *hi_p = otyp;
return;
}
/* randomize object descriptions */
STATIC_OVL void
shuffle_all()
{
/* entire classes; obj_shuffle_range() handles their exceptions */
static char shuffle_classes[] = {
AMULET_CLASS,
POTION_CLASS,
RING_CLASS,
SCROLL_CLASS,
SPBOOK_CLASS,
WAND_CLASS,
VENOM_CLASS,
};
/* sub-class type ranges (one item from each group) */
static short shuffle_types[] = {
HELMET,
LEATHER_GLOVES,
CLOAK_OF_PROTECTION,
SPEED_BOOTS,
};
int first, last, idx;
/* do whole classes (amulets, &c) */
for (idx = 0; idx < SIZE(shuffle_classes); idx++) {
obj_shuffle_range(bases[(int) shuffle_classes[idx]], &first, &last);
shuffle(first, last, TRUE);
}
/* do type ranges (helms, &c) */
for (idx = 0; idx < SIZE(shuffle_types); idx++) {
obj_shuffle_range(shuffle_types[idx], &first, &last);
shuffle(first, last, FALSE);
}
return;
}
/* find the object index for snow boots; used [once] by slippery ice code */
int
find_skates()
{
register int i;
register const char *s;
for (i = SPEED_BOOTS; i <= LEVITATION_BOOTS; i++)
if ((s = OBJ_DESCR(objects[i])) != 0 && !strcmp(s, "snow boots"))
return i;
impossible("snow boots not found?");
return -1; /* not 0, or caller would try again each move */
}
void
oinit() /* level dependent initialization */
{
setgemprobs(&u.uz);
}
void
savenames(fd, mode)
int fd, mode;
{
register int i;
unsigned int len;
if (perform_bwrite(mode)) {
bwrite(fd, (genericptr_t)bases, sizeof bases);
bwrite(fd, (genericptr_t)disco, sizeof disco);
bwrite(fd, (genericptr_t)objects,
sizeof(struct objclass) * NUM_OBJECTS);
}
/* as long as we use only one version of Hack we
need not save oc_name and oc_descr, but we must save
oc_uname for all objects */
for (i = 0; i < NUM_OBJECTS; i++)
if (objects[i].oc_uname) {
if (perform_bwrite(mode)) {
len = strlen(objects[i].oc_uname)+1;
bwrite(fd, (genericptr_t)&len, sizeof len);
bwrite(fd, (genericptr_t)objects[i].oc_uname, len);
}
if (release_data(mode)) {
free((genericptr_t)objects[i].oc_uname);
objects[i].oc_uname = 0;
}
}
}
void
restnames(fd)
register int fd;
{
register int i;
unsigned int len;
mread(fd, (genericptr_t) bases, sizeof bases);
mread(fd, (genericptr_t) disco, sizeof disco);
mread(fd, (genericptr_t) objects, sizeof(struct objclass) * NUM_OBJECTS);
for (i = 0; i < NUM_OBJECTS; i++)
if (objects[i].oc_uname) {
mread(fd, (genericptr_t) &len, sizeof len);
objects[i].oc_uname = (char *) alloc(len);
mread(fd, (genericptr_t)objects[i].oc_uname, len);
}
#ifdef USE_TILES
shuffle_tiles();
#endif
}
void
discover_object(oindx, mark_as_known, credit_hero)
register int oindx;
boolean mark_as_known;
boolean credit_hero;
{
if (!objects[oindx].oc_name_known) {
register int dindx, acls = objects[oindx].oc_class;
/* Loop thru disco[] 'til we find the target (which may have been
uname'd) or the next open slot; one or the other will be found
before we reach the next class...
*/
for (dindx = bases[acls]; disco[dindx] != 0; dindx++)
if (disco[dindx] == oindx) break;
disco[dindx] = oindx;
if (mark_as_known) {
objects[oindx].oc_name_known = 1;
if (credit_hero) exercise(A_WIS, TRUE);
}
if (moves > 1L) update_inventory();
}
}
/* if a class name has been cleared, we may need to purge it from disco[] */
void
undiscover_object(oindx)
register int oindx;
{
if (!objects[oindx].oc_name_known) {
register int dindx, acls = objects[oindx].oc_class;
register boolean found = FALSE;
/* find the object; shift those behind it forward one slot */
for (dindx = bases[acls];
dindx < NUM_OBJECTS && disco[dindx] != 0
&& objects[dindx].oc_class == acls; dindx++)
if (found)
disco[dindx-1] = disco[dindx];
else if (disco[dindx] == oindx)
found = TRUE;
/* clear last slot */
if (found) disco[dindx-1] = 0;
else impossible("named object not in disco");
update_inventory();
}
}
STATIC_OVL boolean
interesting_to_discover(i)
register int i;
{
/* Pre-discovered objects are now printed with a '*' */
return((boolean)(objects[i].oc_uname != (char *)0 ||
(objects[i].oc_name_known && OBJ_DESCR(objects[i]) != (char *)0)));
}
/* items that should stand out once they're known */
static short uniq_objs[] = {
AMULET_OF_YENDOR,
SPE_BOOK_OF_THE_DEAD,
CANDELABRUM_OF_INVOCATION,
BELL_OF_OPENING,
};
int
dodiscovered() /* free after Robert Viduya */
{
register int i, dis;
int ct = 0;
char *s, oclass, prev_class, classes[MAXOCLASSES], buf[BUFSZ];
winid tmpwin;
tmpwin = create_nhwindow(NHW_MENU);
putstr(tmpwin, 0, "Discoveries");
putstr(tmpwin, 0, "");
/* gather "unique objects" into a pseudo-class; note that they'll
also be displayed individually within their regular class */
for (i = dis = 0; i < SIZE(uniq_objs); i++)
if (objects[uniq_objs[i]].oc_name_known) {
if (!dis++)
putstr(tmpwin, iflags.menu_headings, "Unique Items");
Sprintf(buf, " %s", OBJ_NAME(objects[uniq_objs[i]]));
putstr(tmpwin, 0, buf);
++ct;
}
/* display any known artifacts as another pseudo-class */
ct += disp_artifact_discoveries(tmpwin);
/* several classes are omitted from packorder; one is of interest here */
Strcpy(classes, flags.inv_order);
if (!index(classes, VENOM_CLASS)) {
s = eos(classes);
*s++ = VENOM_CLASS;
*s = '\0';
}
for (s = classes; *s; s++) {
oclass = *s;
prev_class = oclass + 1; /* forced different from oclass */
for (i = bases[(int)oclass];
i < NUM_OBJECTS && objects[i].oc_class == oclass; i++) {
if ((dis = disco[i]) != 0 && interesting_to_discover(dis)) {
ct++;
if (oclass != prev_class) {
putstr(tmpwin, iflags.menu_headings, let_to_name(oclass, FALSE));
prev_class = oclass;
}
Sprintf(buf, "%s %s",(objects[dis].oc_pre_discovered ? "*" : " "),
obj_typename(dis));
putstr(tmpwin, 0, buf);
}
}
}
if (ct == 0) {
You("haven't discovered anything yet...");
} else
display_nhwindow(tmpwin, TRUE);
destroy_nhwindow(tmpwin);
return 0;
}
/* put up nameable subset of discoveries list as a menu */
void
rename_disco()
{
register int i, dis;
int ct = 0, mn = 0, sl;
char *s, oclass, prev_class;
winid tmpwin;
anything any;
menu_item *selected = 0;
any = zeroany;
tmpwin = create_nhwindow(NHW_MENU);
start_menu(tmpwin);
/*
* Skip the "unique objects" section (each will appear within its
* regular class if it is nameable) and the artifacts section.
* We assume that classes omitted from packorder aren't nameable
* so we skip venom too.
*/
/* for each class, show discoveries in that class */
for (s = flags.inv_order; *s; s++) {
oclass = *s;
prev_class = oclass + 1; /* forced different from oclass */
for (i = bases[(int)oclass];
i < NUM_OBJECTS && objects[i].oc_class == oclass; i++) {
dis = disco[i];
if (!dis || !interesting_to_discover(dis)) continue;
ct++;
if (!objtyp_is_callable(dis)) continue;
mn++;
if (oclass != prev_class) {
any.a_int = 0;
add_menu(tmpwin, NO_GLYPH, &any, ' ', iflags.menu_headings,
ATR_NONE, let_to_name(oclass, FALSE), MENU_UNSELECTED);
prev_class = oclass;
}
any.a_int = dis;
add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
obj_typename(dis), MENU_UNSELECTED);
}
}
if (ct == 0) {
You("haven't discovered anything yet...");
} else if (mn == 0) {
pline("None of your discoveries can be assigned names...");
} else {
end_menu(tmpwin, "Pick an object type to name");
dis = STRANGE_OBJECT;
sl = select_menu(tmpwin, PICK_ONE, &selected);
if (sl > 0) {
dis = selected[0].item.a_int;
free((genericptr_t)selected);
}
if (dis != STRANGE_OBJECT) {
struct obj odummy;
odummy = zeroobj;
odummy.otyp = dis;
odummy.oclass = objects[dis].oc_class;
odummy.quan = 1L;
odummy.known = !objects[dis].oc_uses_known;
odummy.dknown = 1;
docall(&odummy);
}
}
destroy_nhwindow(tmpwin);
return;
}
/*o_init.c*/