From f9af42a2b77495472a0ab16b78310574da052f9f Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 29 Jun 2017 16:14:27 -0700 Subject: [PATCH 01/39] eating 1 of N floor tins in shop Reported directly to devteam, eating 1 of 2 tins of spinach from a shop's floor forced the hero to buy both. (1 was gone, the other was intact and now owned by the hero rather than the shop. Tins of other contents behaved similarly.) The bug is easy to fix but not so easy to explain: eating split one from the stack but passed the remaining stack to useupf(obj,1) which also split one off and treated that as used up. The second one was billed as used up and the first one was added to the bill--as being subjected to a costly modification made by the hero--and kept intact, now marked no-charge with hero obligated to pay for it. Eating 1 of N tins, for N greater than 2, billed for two, one gone and the other now in a separate stack and marked no-charge. The remaining N-2 stayed as normal shop goods in their original stack. The fix is communicate the splitobj() for costly modification up-call so that the use_up_tin code operates on the one which was split off the stack rather than on the remainder of the original stack. --- doc/fixes36.1 | 1 + src/eat.c | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 30ffed891..7c70a7ab3 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -404,6 +404,7 @@ fix the 'A' command to have the 'D' command's fix for C331-1 (quirk for menustyle:Combination; if user included 'a' in "which object classes?" response, to operate on applicable all items, there would still be a followup menu asking to choose specific items) +eating 1 tin from stack of N (for N >= 2) on shop's floor forced hero to buy 2 Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/eat.c b/src/eat.c index 2c748ef06..4e011899d 100644 --- a/src/eat.c +++ b/src/eat.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 eat.c $NHDT-Date: 1470272344 2016/08/04 00:59:04 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.172 $ */ +/* NetHack 3.6 eat.c $NHDT-Date: 1498778062 2017/06/29 23:14:22 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.178 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -6,7 +6,7 @@ STATIC_PTR int NDECL(eatmdone); STATIC_PTR int NDECL(eatfood); -STATIC_PTR void FDECL(costly_tin, (int)); +STATIC_PTR struct obj *FDECL(costly_tin, (int)); STATIC_PTR int NDECL(opentin); STATIC_PTR int NDECL(unfaint); @@ -1152,7 +1152,7 @@ violated_vegetarian() /* common code to check and possibly charge for 1 context.tin.tin, * will split() context.tin.tin if necessary */ -STATIC_PTR void +STATIC_PTR struct obj * costly_tin(alter_type) int alter_type; /* COST_xxx */ { @@ -1166,6 +1166,7 @@ int alter_type; /* COST_xxx */ } costly_alteration(tin, alter_type); } + return tin; } int @@ -1293,7 +1294,7 @@ const char *mesg; r = tin_variety(tin, FALSE); if (tin->otrapped || (tin->cursed && r != HOMEMADE_TIN && !rn2(8))) { b_trapped("tin", 0); - costly_tin(COST_DSTROY); + tin = costly_tin(COST_DSTROY); goto use_up_tin; } @@ -1304,7 +1305,7 @@ const char *mesg; if (mnum == NON_PM) { pline("It turns out to be empty."); tin->dknown = tin->known = 1; - costly_tin(COST_OPEN); + tin = costly_tin(COST_OPEN); goto use_up_tin; } @@ -1333,7 +1334,7 @@ const char *mesg; You("discard the open tin."); if (!Hallucination) tin->dknown = tin->known = 1; - costly_tin(COST_OPEN); + tin = costly_tin(COST_OPEN); goto use_up_tin; } @@ -1352,7 +1353,7 @@ const char *mesg; cpostfx(mnum); /* charge for one at pre-eating cost */ - costly_tin(COST_OPEN); + tin = costly_tin(COST_OPEN); if (tintxts[r].nut < 0) /* rotten */ make_vomiting((long) rn1(15, 10), FALSE); @@ -1378,7 +1379,7 @@ const char *mesg; if (yn("Eat it?") == 'n') { if (flags.verbose) You("discard the open tin."); - costly_tin(COST_OPEN); + tin = costly_tin(COST_OPEN); goto use_up_tin; } @@ -1386,14 +1387,13 @@ const char *mesg; * Same order as with non-spinach above: * conduct update, side-effects, shop handling, and nutrition. */ - u.uconduct - .food++; /* don't need vegan/vegetarian checks for spinach */ + u.uconduct.food++; /* don't need vegetarian checks for spinach */ if (!tin->cursed) pline("This makes you feel like %s!", Hallucination ? "Swee'pea" : "Popeye"); gainstr(tin, 0, FALSE); - costly_tin(COST_OPEN); + tin = costly_tin(COST_OPEN); lesshungry(tin->blessed ? 600 /* blessed */ From ff6139c6c55b579788a2596f2f109613dabb6c46 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 29 Jun 2017 17:54:01 -0700 Subject: [PATCH 02/39] robustness of #timeout and #wizintrinsic Remove the assumption of property index values from the list of property names. Move the properties that can't have timed values in normal play to after those which can. (Mainly only matters for the #wizintrinsic command.) Bug fix: #wizintrinsic variable 'any' wasn't initialized properly if 'a_int' is smaller than a long or a pointer. The separator line I've added was ending up as a menu choice. --- include/prop.h | 2 +- src/cmd.c | 31 ++++++++---- src/timeout.c | 132 +++++++++++++++++++++++++++++++++---------------- 3 files changed, 113 insertions(+), 52 deletions(-) diff --git a/include/prop.h b/include/prop.h index cb4cd3fde..3bbc7182f 100644 --- a/include/prop.h +++ b/include/prop.h @@ -7,7 +7,7 @@ /*** What the properties are *** * - * note: propertynames[] array in timeout.c must be kept in synch with these. + * note: propertynames[] array in timeout.c has string values for these. * Property #0 is not used. */ /* Resistances to troubles */ diff --git a/src/cmd.c b/src/cmd.c index 2ec304a8a..ade3352cd 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -135,6 +135,7 @@ STATIC_PTR int NDECL(wiz_level_change); STATIC_PTR int NDECL(wiz_show_seenv); STATIC_PTR int NDECL(wiz_show_vision); STATIC_PTR int NDECL(wiz_smell); +STATIC_PTR int NDECL(wiz_intrinsic); STATIC_PTR int NDECL(wiz_mon_polycontrol); STATIC_PTR int NDECL(wiz_show_wmodes); STATIC_DCL void NDECL(wiz_map_levltyp); @@ -1157,29 +1158,37 @@ STATIC_PTR int wiz_intrinsic(VOID_ARGS) { if (wizard) { - extern const char *const propertynames[]; /* timeout.c */ + extern const struct propname { + int prop_num; + const char *prop_name; + } propertynames[]; /* timeout.c */ static const char wizintrinsic[] = "#wizintrinsic"; static const char fmt[] = "You are%s %s."; winid win; anything any; char buf[BUFSZ]; - int i, n, p, amt, typ; + int i, j, n, p, amt, typ; long oldtimeout, newtimeout; const char *propname; menu_item *pick_list = (menu_item *) 0; + any = zeroany; win = create_nhwindow(NHW_MENU); start_menu(win); - - for (i = 1; (propname = propertynames[i]) != 0; ++i) { - if (i == HALLUC_RES) { + for (i = 0; (propname = propertynames[i].prop_name) != 0; ++i) { + p = propertynames[i].prop_num; + if (p == HALLUC_RES) { /* Grayswandir vs hallucination; ought to be redone to use u.uprops[HALLUC].blocked instead of being treated as a separate property; letting in be manually toggled even only in wizard mode would be asking for trouble... */ continue; } - any.a_int = i; + if (p == FIRE_RES) { + any.a_int = 0; + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "--", FALSE); + } + any.a_int = i + 1; /* +1: avoid 0 */ add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, propname, FALSE); } end_menu(win, "Which intrinsics?"); @@ -1187,8 +1196,9 @@ wiz_intrinsic(VOID_ARGS) destroy_nhwindow(win); amt = 30; /* TODO: prompt for duration */ - for (i = 0; i < n; ++i) { - p = pick_list[i].item.a_int; + for (j = 0; j < n; ++j) { + i = pick_list[j].item.a_int - 1; /* -1: reverse +1 above */ + p = propertynames[i].prop_num; oldtimeout = u.uprops[p].intrinsic & TIMEOUT; newtimeout = oldtimeout + (long) amt; switch (p) { @@ -1204,9 +1214,12 @@ wiz_intrinsic(VOID_ARGS) case BLINDED: make_blinded(newtimeout, TRUE); break; +#if 0 /* make_confused() only gives feedback when confusion is + * ending so use the 'default' case for it instead */ case CONFUSION: make_confused(newtimeout, TRUE); break; +#endif /*0*/ case DEAF: make_deaf(newtimeout, TRUE); break; @@ -1236,7 +1249,7 @@ wiz_intrinsic(VOID_ARGS) pline1(buf); break; default: - pline("Timeout for %s %s %d.", propertynames[p], + pline("Timeout for %s %s %d.", propertynames[i].prop_name, oldtimeout ? "increased by" : "set to", amt); incr_itimeout(&u.uprops[p].intrinsic, amt); break; diff --git a/src/timeout.c b/src/timeout.c index 66add4ec8..81285fc37 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -15,43 +15,83 @@ STATIC_DCL void FDECL(see_lamp_flicker, (struct obj *, const char *)); STATIC_DCL void FDECL(lantern_message, (struct obj *)); STATIC_DCL void FDECL(cleanup_burn, (ANY_P *, long)); -/* the order of these must match their numerical sequence in prop.h - because the property number is inferred from the array index; - used by wizard mode #timeout and #wizintrinsic */ -const char *const propertynames[] = { - "0: not used", -/* Resistances */ - /* 1..2 */ "fire resistance", "cold resistance", - /* 3..4 */ "sleep resistance", "disintegration resistance", - /* 5..6 */ "shock resistance", "poison resistance", - /* 7..8 */ "acid resistance", "stoning resistance", - /* 9..10 */ "drain resistance", "sickness resistance", - /* 11..12 */ "invulnerable", "magic resistance", -/* Troubles */ - /* 13..16 */ "stunned", "confused", "blinded", "deafness", - /* 17..20 */ "fatally sick", "petrifying", "strangling", "vomiting", - /* 21..22 */ "slippery fingers", "becoming slime", - /* 23..24 */ "hallucinating", "halluicination resistance", - /* 25..28 */ "fumbling", "wounded legs", "sleepy", "voracious hunger", -/* Vision and senses */ - /* 29..30 */ "see invisible", "telepathic", - /* 31..33 */ "warning", "warn:monster", "warn:undead", - /* 34..37 */ "searching", "clairvoyant", "infravision", "monster detection", -/* Appearance and behavior */ - /* 38..41 */ "adorned (+/-Cha)", "invisible", "displaced", "stealthy", - /* 42..43 */ "monster aggrevation", "conflict", -/* Transportation */ - /* 44..46 */ "jumping", "teleporting", "teleport control", - /* 47..49 */ "levitating", "flying", "water walking", - /* 50..52 */ "swimming", "magical breathing", "pass thru walls", -/* Physical attributes */ - /* 53..55 */ "slow digestion", "half spell damage", "half physical damage", - /* 56..58 */ "HP regeneration", "energy regeneration", "extra protection", - /* 59 */ "protection from shape changers", - /* 60..62 */ "polymorphing", "polymorph control", "unchanging", - /* 63..66 */ "fast", "reflecting", "free action", "fixed abilites", - /* 67 */ "life will be saved", - 0 /* sentinel */ +/* used by wizard mode #timeout and #wizintrinsic; order by 'interest' + for timeout countdown, where most won't occur in normal play */ +const struct propname { + int prop_num; + const char *prop_name; +} propertynames[] = { + { INVULNERABLE, "invulnerable" }, + { STONED, "petrifying" }, + { SLIMED, "becoming slime" }, + { STRANGLED, "strangling" }, + { SICK, "fatally sick" }, + { STUNNED, "stunned" }, + { CONFUSION, "confused" }, + { HALLUC, "hallucinating" }, + { BLINDED, "blinded" }, + { DEAF, "deafness" }, + { VOMITING, "vomiting" }, + { GLIB, "slippery fingers" }, + { WOUNDED_LEGS, "wounded legs" }, + { SLEEPY, "sleepy" }, + { TELEPORT, "teleporting" }, + { POLYMORPH, "polymorphing" }, + { LEVITATION, "levitating" }, + { FAST, "very fast" }, /* timed 'FAST' is very fast */ + { CLAIRVOYANT, "clairvoyant" }, + { DETECT_MONSTERS, "monster detection" }, + { SEE_INVIS, "see invisible" }, + { INVIS, "invisible" }, + /* properties beyond here don't have timed values during normal play, + so there's no much point in trying to order them sensibly; + they're either on or off based on equipment, role, actions, &c */ + { FIRE_RES, "fire resistance" }, + { COLD_RES, "cold resistance" }, + { SLEEP_RES, "sleep resistance" }, + { DISINT_RES, "disintegration resistance" }, + { SHOCK_RES, "shock resistance" }, + { POISON_RES, "poison resistance" }, + { ACID_RES, "acid resistance" }, + { STONE_RES, "stoning resistance" }, + { DRAIN_RES, "drain resistance" }, + { SICK_RES, "sickness resistance" }, + { ANTIMAGIC, "magic resistance" }, + { HALLUC_RES, "hallucination resistance" }, + { FUMBLING, "fumbling" }, + { HUNGER, "voracious hunger" }, + { TELEPAT, "telepathic" }, + { WARNING, "warning" }, + { WARN_OF_MON, "warn: monster type or class" }, + { WARN_UNDEAD, "warn: undead" }, + { SEARCHING, "searching" }, + { INFRAVISION, "infravision" }, + { ADORNED, "adorned (+/- Cha)" }, + { DISPLACED, "displaced" }, + { STEALTH, "stealthy" }, + { AGGRAVATE_MONSTER, "monster aggravation" }, + { CONFLICT, "conflict" }, + { JUMPING, "jumping" }, + { TELEPORT_CONTROL, "teleport control" }, + { FLYING, "flying" }, + { WWALKING, "water walking" }, + { SWIMMING, "swimming" }, + { MAGICAL_BREATHING, "magical breathing" }, + { PASSES_WALLS, "pass thru walls" }, + { SLOW_DIGESTION, "slow digestion" }, + { HALF_SPDAM, "half spell damage" }, + { HALF_PHDAM, "half physical damage" }, + { REGENERATION, "HP regeneration" }, + { ENERGY_REGENERATION, "energy regeneration" }, + { PROTECTION, "extra protection" }, + { PROT_FROM_SHAPE_CHANGERS, "protection from shape changers" }, + { POLYMORPH_CONTROL, "polymorph control" }, + { UNCHANGING, "unchanging" }, + { REFLECTING, "reflecting" }, + { FREE_ACTION, "free action" }, + { FIXED_ABIL, "fixed abilites" }, + { LIFESAVED, "life will be saved" }, + { 0, 0 }, }; /* He is being petrified - dialogue by inmet!tower */ @@ -1544,7 +1584,7 @@ wiz_timeout_queue() char buf[BUFSZ]; const char *propname; long intrinsic; - int i, count, longestlen, ln; + int i, p, count, longestlen, ln, specindx = 0; win = create_nhwindow(NHW_MENU); /* corner text window */ if (win == WIN_ERR) @@ -1562,13 +1602,16 @@ wiz_timeout_queue() * normal play but those can be forced via the #wizintrinsic command. */ count = longestlen = 0; - for (i = 1; (propname = propertynames[i]) != 0; ++i) { /* [0] not used */ - intrinsic = u.uprops[i].intrinsic; + for (i = 0; (propname = propertynames[i].prop_name) != 0; ++i) { + p = propertynames[i].prop_num; + intrinsic = u.uprops[p].intrinsic; if (intrinsic & TIMEOUT) { ++count; if ((ln = (int) strlen(propname)) > longestlen) longestlen = ln; } + if (specindx == 0 && p == FIRE_RES) + specindx = i; } putstr(win, 0, ""); if (!count) { @@ -1576,9 +1619,14 @@ wiz_timeout_queue() } else { putstr(win, 0, "Timed properties:"); putstr(win, 0, ""); - for (i = 1; (propname = propertynames[i]) != 0; ++i) { - intrinsic = u.uprops[i].intrinsic; + for (i = 0; (propname = propertynames[i].prop_name) != 0; ++i) { + p = propertynames[i].prop_num; + intrinsic = u.uprops[p].intrinsic; if (intrinsic & TIMEOUT) { + if (specindx > 0 && i >= specindx) { + putstr(win, 0, " -- settable via #wizinstrinc only --"); + specindx = 0; + } /* timeout value can be up to 16777215 (0x00ffffff) but width of 4 digits should result in values lining up almost all the time (if/when they don't, it won't From 67c1325a8187d72cf91c7500ead24a14da32bfb7 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 30 Jun 2017 02:09:56 -0700 Subject: [PATCH 03/39] tribute: The Amazing Maurice and His Educated Rodents. The last Discworld book with only one or two passages to choose from when reading. --- dat/tribute | 224 +++++++++++++++++++++++++++++++++++++++++++++++++- src/do_name.c | 2 +- 2 files changed, 222 insertions(+), 4 deletions(-) diff --git a/dat/tribute b/dat/tribute index 05165e5e3..64746b8b7 100644 --- a/dat/tribute +++ b/dat/tribute @@ -5907,13 +5907,231 @@ betray 'em, quick as a wink. 'Cos that's villaining.' %e title # # +# _The_Amazing_Maurice_and_His_Educated_Rodents_ (sometimes spelled with +# "his" uncapitalized--the book itself uses all uppercase on both the +# cover and the title page so doesn't help resolve which is correct...) +# was the first of six Discworld books marketed for "Young Adults" (at +# least in the US), ages 12 to 16 give or take, so tended to be stocked +# on different shelves from the rest of Discworld in book stores and +# libraries. In the UK, _The_Amazing_Maurice..._ won the Carnegie Medal +# which is awarded for best children's book of the year. +# (The other Young Adult Discworld books are the five Tiffany Aching ones.) # -%title The Amazing Maurice and his Educated Rodents (1) +# _The_Amazing_Maurice..._ may well be the most serious Discworld book. +# (Don't worry, it has lots of humor/humour in it....) +# +%title The Amazing Maurice and His Educated Rodents (10) +# p. 68 (Harperteen edition; _Mr._Bunnsy_Has_an_Adventure_ is a book +# within the book, and a brief quote is shown at the beginning +# of each chapter. This one is from the start of chapter 4.) %passage 1 -The important thing about adventures, thought Mr Bunnsy, was that they +The important thing about adventures, thought Mr. Bunnsy, was that they shouldn't be so long as to make you miss mealtimes. - [The Amazing Maurice and his Educated Rodents, by Terry Pratchett] + [The Amazing Maurice and His Educated Rodents, by Terry Pratchett] +%e passage +# p. 9 (passage starts mid-paragraph) +%passage 2 +"Stealing from a thief isn't stealing, 'cos it cancels out." + + [The Amazing Maurice and His Educated Rodents, by Terry Pratchett] +%e passage +# pp. 11-12 (rats became intelligent from eating wizards' trash just outside +# one of the walls of Unseen University; Maurice insists that he +# never did that, implying that he'd eaten some of the rats instead +# [never explicitly stated] before he became intelligent himself) +%passage 3 +They said he was amazing. The Amazing Maurice, they said. He'd never +meant to be amazing. It just happened. + +He's realized something was odd that day, just after lunch, when he'd +looked into a reflection in a puddle and thought, /that's me/. He'd never +been /aware/ of himself before. Of course it was hard to remember /how/ +he'd thought before becoming amazing. It seemed to him that his mind had +been just a kind of soup. + +And then there had been the rats, who lived under the rubbish heap in one +corner of his territory. He'd realized there was something educated +about the rats when he'd jumped on one and it'd said, "Can we talk about +this?" and part of his amazing new brain had told him you couldn't eat +someone who could talk. At least, not until you'd heard what it'd got +to say. + + [The Amazing Maurice and His Educated Rodents, by Terry Pratchett] +%e passage +# p. 32 (Maurice the cat, Keith the human boy, and the intelligent rat clan are +# in the town of Bad Blintz; people are queuing up for rationed food) +%passage 4 +"Shall we line up too?" asked the kid. + +"I shouldn't think so," said Maurice carefully. + +"Why not?" + +"See those men on the door? They look like the Watch. They've got big +truncheons. And everyone's showing them a bit of paper as they go past. +I don't like the look of that," said Maurice. "That looks like +/government/ to me." + +"We haven't done anything wrong," said the kid. "Not here, anyway." + +"You never know, with governments," said Maurice. "Just stay here kid. +I'll take a look." + + [The Amazing Maurice and His Educated Rodents, by Terry Pratchett] +%e passage +# p. 52 (speaker is Darktan, the traps expert; "Number One" platoon seems +# like an obvious joke here--missed deliberately or accidentally?) +%passage 5 +"All right, Number Three platoon, you're on widdling duty," he said. "Go +and have a good drink." + + [The Amazing Maurice and His Educated Rodents, by Terry Pratchett] +%e passage +# p. 110 (opening quote for chapter 6) +%passage 6 +There were big adventures and small adventures, Mr. Bunnsy knew. You +didn't get told what size they were going to be before you started. +Sometimes you could have a big adventure even when you were standing +still. + + [The Amazing Maurice and His Educated Rodents, by Terry Pratchett] +%e passage +# pp. 127-128 (searching for a secret door...) +%passage 7 +Malicia leaned against the wall with incredible nonchalance. + +There was not a click. A panel in the floor did not slide back. + +"Probably the wrong place," she said. "I'll just rest my arm innocently +on this coat hook." + +A sudden door in the wall completely failed to happen. + +"Of course, it'd help if there was an ornate candlestick," said Malicia. +"They're always a surefire secret-passage lever. Every adventurer knows +that." + +"There isn't a candlestick," said Maurice. + +"I know. Some people totally fail to have any /idea/ of how to design a +proper secret passage," said Malicia. She leaned against another piece +of wall, which had no affect whatsoever. + +"I don't think you'll find it that way," said Keith, who was carefully +examining a trap. + +"Oh? Won't I?" said Malicia. "Well at least I'm being /constructive/ +about things! Where would you look, if you're such an expert?" + +"Why is there a rat hole in a rat catcher's shed?" said Keith. "It smells +of dead rats and wet dogs and poison. I wouldn't come near this place, +if I was a rat." + +Malicia glared at him. Then her face wrapped itself in an expression of +acute concentration, as if she was trying out several ideas in her head. + +"Ye-es," she said. "That usually works, in stories. It's often the stupid +person who comes up with the good idea by accident." + +She crouched down and peered into the hole. + +"There's a sort of little lever," she said. "I'll just give it a little +push...." + +There was a /clonk/ under the floor, part of it swung back, and Keith +dropped out of sight. + +"Oh, yes," said Malicia. "I thought something like that would probably +happen." + + [The Amazing Maurice and His Educated Rodents, by Terry Pratchett] +%e passage +# p. 231 (passage ends mid-paragraph) +%passage 8 +He had to admit that he was cleverer at plans than at underground +navigation. He wasn't exactly lost, because cats never get lost. He +merely didn't know where everything else was. + + [The Amazing Maurice and His Educated Rodents, by Terry Pratchett] +%e passage +# pp. 298-300 (Keith has challanged the professional rat piper and offered +# to rid the town of rats for a much lower price; Sardines is +# one of the Educated Rodents, known for dancing all the time; +# "hwun/two/three/four/" is run-together "one /two/ three /four/"; +# quite a long passage primarily for the 'a bit more grimy' gag) +%passage 9 +[...] +"But first I shall need to borrow a pipe," Keith went on. + +"You haven't got one?" asked the mayor. + +"It got broken." + +Corporal Knopf nudged the mayor. "I've got a trombone from when I was in +the army," he said. "It won't take a moment to get it." + +The rat piper burst out laughing. + +"Doesn't that count?" asked the mayor, as Corporal Knopf hurried off. + +"What? A trombone for charming rats? No, no, let him try. Can't blame +a kid for trying. Good with a trombone, are you?" + +"I don't know," said Keith. + +"What do you mean, you don't know?" + +"I meant I've never played one. I'd be a lot happier with a flute, +trumpet, piccolo, cornet, or Lancre bagpipe, but I've seen people playing +the trombone, and it doesn't look too difficult. It's only an overgrown +trumpet, really." + +"Hah!" said the piper. "This I'd like to see--but not hear." + +The Watch came running back, rubbing a battered trombone with his sleave +and therefore making it just a bit more grimy. Keith took it, wiped the +mouthpiece, put it to his mouth, moved the slide a few times, and then +blew one long note. + +"Seems to work," he said. "I expect I can learn as I go along." He gave +the rat piper a brief smile. "Do you want to go first?" + +"You won't charm one rat with that mess, kid," said the piper, "but I'm +glad I'm here to see you try." + +Keith gave him a smile again, took a breath, and played. + +There was a tune there. The instrument squeaked and wheezed, because +Corporal Knopf had occasionally used the thing as a hammer, but there was +a tune, quite fast, almost jaunty. You could tap your feet to it. + +Someone tapped his feet to it. + +Sardines emerged from a crack in a nearby wall, going "hwun/two/three/four/" +under his breath. The crowd watched him dance ferociously across the +cobbles until he disappeared into a drain. Then they broke into applause. + +The piper looked at Keith. + +"Did that one have a /hat/ on?" he asked. + +"I didn't notice," said Keith. "Your go." + + [The Amazing Maurice and His Educated Rodents, by Terry Pratchett] +%e passage +# pp. 309-310 +%passage 10 +"You really /can/ talk? You can think?" asked the mayor. + +Darktan looked up at him. It had been a long night. He didn't want to +remember any of it. And now it was going to be a longer, harder day. +He took a deep breath. + +"Here's what I suggest," he said. "You pretend that rats can think, and +I'll promise to pretend that humans can think, too." + + [The Amazing Maurice and His Educated Rodents, by Terry Pratchett] %e passage %e title # diff --git a/src/do_name.c b/src/do_name.c index 452f6c555..a7db8f4f3 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -1880,7 +1880,7 @@ static const char *const sir_Terry_novels[] = { "Lords and Ladies", "Men at Arms", "Soul Music", "Interesting Times", "Maskerade", "Feet of Clay", "Hogfather", "Jingo", "The Last Continent", "Carpe Jugulum", "The Fifth Elephant", "The Truth", "Thief of Time", - "The Last Hero", "The Amazing Maurice and his Educated Rodents", + "The Last Hero", "The Amazing Maurice and His Educated Rodents", "Night Watch", "The Wee Free Men", "Monstrous Regiment", "A Hat Full of Sky", "Going Postal", "Thud!", "Wintersmith", "Making Money", "Unseen Academicals", "I Shall Wear Midnight", "Snuff", From a45ac9955fee7f18346b07d24a5bcb4d5d76eb7a Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 30 Jun 2017 16:31:54 -0700 Subject: [PATCH 04/39] fixes36.1 update: #H4725 - repeated shortcut keys Mark an old 'unconfirmed' bug as 'fixed'. The report was for win32gui and I can't reproduce the problem or verify that it's gone, but I'm sure enough about the cause and long-ago fix to put #H4725 to rest.... Report was that the menu for the name-from-discoveries-list command was reusing a set of punctuation characters for each class of objects. The key was that '!' was always first, and it is (' ' + 1) so the core bug of erroneously specifying space for a selector on object class header/separator lines was being used as the start of the selector sequence. (Report that led to the fix was that typing space on that menu always made it finish instead of advance to the next page.) --- doc/fixes36.1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 7c70a7ab3..d47896d54 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -493,6 +493,9 @@ unix: Makefile.{src,utl} ignored CPPFLAGS which is expanded by the default .c win32gui: getversionstring() was overflowing the provided Help About buffer win32gui: guard against buffer overflow in in mswin_getlin() win32gui: handle menu_color attribute +win32gui: name-from-discoveries list menu selector characters were punctuation + instead of letters, repeated for each class; caused by the core bug + of erroneously specifying space as a selector for class header lines MacOSX: initial binary release was built from out of date source code that had 'BETA' and 'DEBUG' inappropriately enabled MacOSX: force TIMED_DELAY build option on so that 'runmode' run-time option From 36a921ca3b5b8fae547ac44894639e042001c7cd Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 30 Jun 2017 23:51:14 -0700 Subject: [PATCH 05/39] more obj filtering Add support for filtering by unpaid status for container-in and container-out actions. When taking out of a container, it works as expected if you're carrying the container while in a shop, but won't find any unpaid items if the container is on the floor. That's because they're only flagged as unpaid while in the hero's inventory. (And when it doesn't find any unpaid items it won't list 'unpaid' as a category of item to manipulate, so while that might be suboptimal for taking items out of shop containers, it shouldn't be a problem. Typically all the contents are shop-owned anyway, so using unpaid as a filter wouldn't gain any advantage over just taking stuff out.) --- src/invent.c | 3 ++- src/pickup.c | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/invent.c b/src/invent.c index cb1cfb2d0..b5ed81c6d 100644 --- a/src/invent.c +++ b/src/invent.c @@ -1747,7 +1747,8 @@ int FDECL((*fn), (OBJ_P)), FDECL((*ckfn), (OBJ_P)); nodot = (!strcmp(word, "nodot") || !strcmp(word, "drop") || ident || takeoff || take_out || put_in); ininv = (*objchn == invent); - bycat = (menu_class_present('B') || menu_class_present('U') + bycat = (menu_class_present('u') + || menu_class_present('B') || menu_class_present('U') || menu_class_present('C') || menu_class_present('X')); /* someday maybe we'll sort by 'olets' too (temporarily replace diff --git a/src/pickup.c b/src/pickup.c index 2395c4110..0b91a4bb4 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -177,6 +177,8 @@ int *menu_on_demand; } if (itemcount && menu_on_demand) ilets[iletct++] = 'm'; + if (count_unpaid(objs)) + ilets[iletct++] = 'u'; tally_BUCX(objs, here, &bcnt, &ucnt, &ccnt, &xcnt, &ocnt); if (bcnt) @@ -222,8 +224,8 @@ int *menu_on_demand; goto ask_again; } else if (sym == 'm') { m_seen = TRUE; - } else if (index("BUCX", sym)) { - add_valid_menu_class(sym); /* 'B','U','C',or 'X' */ + } else if (index("uBUCX", sym)) { + add_valid_menu_class(sym); /* 'u' or 'B','U','C',or 'X' */ filtered = TRUE; } else { oc_of_sym = def_char_to_objclass(sym); @@ -2666,7 +2668,7 @@ boolean put_in; } else if (flags.menu_style == MENU_FULL) { all_categories = FALSE; Sprintf(buf, "%s what type of objects?", action); - mflags = (ALL_TYPES | BUCX_TYPES); + mflags = (ALL_TYPES | UNPAID_TYPES | BUCX_TYPES); if (put_in) mflags |= CHOOSE_ALL; n = query_category(buf, put_in ? invent : current_container->cobj, From d7e7c620f510ec95acd772fe8f08de29af7cc994 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 1 Jul 2017 14:22:42 -0700 Subject: [PATCH 06/39] wishing for tins A couple of days ago when verifying the report about being forced to pay for a second tin when eating one from a stack on a shop's floor, wishing for 'tins' (rather than 'tins of foo meat') was repeatedly producing tin wands instead. The name "tin" and the wand description "tin" in objects[] were being given 50:50 chance for either one by the 3.6.1 wishing code. Wishing for "tins of spinach" also gave me a tin wand on the first attempt. Handle 'tin(s)' more explicitly. This also does some reformatting. --- doc/fixes36.1 | 1 + src/objnam.c | 97 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 58 insertions(+), 40 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index d47896d54..03781df5f 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -454,6 +454,7 @@ attempting to name an item as an artifact and failing via hand slip violates illiterate conduct crashes for 'A' above were downgraded to impossible "cursed without otmp" wizhelp: ^O is #overview in wizard mode too; #wizwhere shows dungeon layout +wishing for tins sometimes yielded a tin wand Platform- and/or Interface-Specific Fixes diff --git a/src/objnam.c b/src/objnam.c index bb0c91a44..e5cbbc009 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -2010,10 +2010,14 @@ char *str; return 0; } -/* Plural routine; chiefly used for user-defined fruits. We have to try to - * account for everything reasonable the player has; something unreasonable - * can still break the code. However, it's still a lot more accurate than - * "just add an s at the end", which Rogue uses... +/* Plural routine; once upon a time it may have been chiefly used for + * user-defined fruits, but it is now used extensively throughout the + * program. + * + * For fruit, we have to try to account for everything reasonable the + * player has; something unreasonable can still break the code. + * However, it's still a lot more accurate than "just add an 's' at the + * end", which Rogue uses... * * Also used for plural monster names ("Wiped out all homunculi." or the * vanquished monsters list) and body parts. A lot of unique monsters have @@ -2847,9 +2851,14 @@ struct obj *no_wish; if (!strstri(bp, "wand ") && !strstri(bp, "spellbook ") && !strstri(bp, "finger ")) { if ((p = strstri(bp, "tin of ")) != 0) { - tmp = tin_variety_txt(p + 7, &tinv); - tvariety = tinv; - mntmp = name_to_mon(p + 7 + tmp); + if (!strcmpi(p + 7, "spinach")) { + contents = SPINACH; + mntmp = NON_PM; + } else { + tmp = tin_variety_txt(p + 7, &tinv); + tvariety = tinv; + mntmp = name_to_mon(p + 7 + tmp); + } typ = TIN; goto typfnd; } else if ((p = strstri(bp, " of ")) != 0 @@ -2858,35 +2867,32 @@ struct obj *no_wish; } } /* Find corpse type w/o "of" (red dragon scale mail, yeti corpse) */ - if (strncmpi(bp, "samurai sword", 13)) /* not the "samurai" monster! */ - if (strncmpi(bp, "wizard lock", 11)) /* not the "wizard" monster! */ - if (strncmpi(bp, "ninja-to", 8)) /* not the "ninja" rank */ - if (strncmpi(bp, "master key", - 10)) /* not the "master" rank */ - if (strncmpi(bp, "magenta", 7)) /* not the "mage" rank */ - if (mntmp < LOW_PM && strlen(bp) > 2 - && (mntmp = name_to_mon(bp)) >= LOW_PM) { - int mntmptoo, - mntmplen; /* double check for rank title */ - char *obp = bp; - mntmptoo = title_to_mon(bp, (int *) 0, &mntmplen); - bp += mntmp != mntmptoo - ? (int) strlen(mons[mntmp].mname) + if (strncmpi(bp, "samurai sword", 13) /* not the "samurai" monster! */ + && strncmpi(bp, "wizard lock", 11) /* not the "wizard" monster! */ + && strncmpi(bp, "ninja-to", 8) /* not the "ninja" rank */ + && strncmpi(bp, "master key", 10) /* not the "master" rank */ + && strncmpi(bp, "magenta", 7)) { /* not the "mage" rank */ + if (mntmp < LOW_PM && strlen(bp) > 2 + && (mntmp = name_to_mon(bp)) >= LOW_PM) { + int mntmptoo, mntmplen; /* double check for rank title */ + char *obp = bp; + + mntmptoo = title_to_mon(bp, (int *) 0, &mntmplen); + bp += (mntmp != mntmptoo) ? (int) strlen(mons[mntmp].mname) : mntmplen; - if (*bp == ' ') - bp++; - else if (!strncmpi(bp, "s ", 2)) - bp += 2; - else if (!strncmpi(bp, "es ", 3)) - bp += 3; - else if (!*bp && !actualn && !dn && !un - && !oclass) { - /* no referent; they don't really mean a - * monster type */ - bp = obp; - mntmp = NON_PM; - } - } + if (*bp == ' ') { + bp++; + } else if (!strncmpi(bp, "s ", 2)) { + bp += 2; + } else if (!strncmpi(bp, "es ", 3)) { + bp += 3; + } else if (!*bp && !actualn && !dn && !un && !oclass) { + /* no referent; they don't really mean a monster type */ + bp = obp; + mntmp = NON_PM; + } + } + } /* first change to singular if necessary */ if (*bp) { @@ -2952,7 +2958,8 @@ struct obj *no_wish; * gold/money concept. Maybe we want to add other monetary units as * well in the future. (TH) */ - if (!BSTRCMPI(bp, p - 10, "gold piece") || !BSTRCMPI(bp, p - 7, "zorkmid") + if (!BSTRCMPI(bp, p - 10, "gold piece") + || !BSTRCMPI(bp, p - 7, "zorkmid") || !strcmpi(bp, "gold") || !strcmpi(bp, "money") || !strcmpi(bp, "coin") || *bp == GOLD_SYM) { if (cnt > 5000 && !wizard) @@ -2975,15 +2982,19 @@ struct obj *no_wish; /* Search for class names: XXXXX potion, scroll of XXXXX. Avoid */ /* false hits on, e.g., rings for "ring mail". */ - if (strncmpi(bp, "enchant ", 8) && strncmpi(bp, "destroy ", 8) + if (strncmpi(bp, "enchant ", 8) + && strncmpi(bp, "destroy ", 8) && strncmpi(bp, "detect food", 11) - && strncmpi(bp, "food detection", 14) && strncmpi(bp, "ring mail", 9) + && strncmpi(bp, "food detection", 14) + && strncmpi(bp, "ring mail", 9) && strncmpi(bp, "studded leather armor", 21) && strncmpi(bp, "leather armor", 13) - && strncmpi(bp, "tooled horn", 11) && strncmpi(bp, "food ration", 11) + && strncmpi(bp, "tooled horn", 11) + && strncmpi(bp, "food ration", 11) && strncmpi(bp, "meat ring", 9)) for (i = 0; i < (int) (sizeof wrpsym); i++) { register int j = strlen(wrp[i]); + if (!strncmpi(bp, wrp[i], j)) { oclass = wrpsym[i]; if (oclass != AMULET_CLASS) { @@ -3099,11 +3110,17 @@ srch: for (i = bases[GEM_CLASS]; i <= LAST_GEM; i++) { register const char *zn; - if ((zn = OBJ_NAME(objects[i])) && !strcmpi(actualn, zn)) { + if ((zn = OBJ_NAME(objects[i])) != 0 && !strcmpi(actualn, zn)) { typ = i; goto typfnd; } } + /* "tin of foo" would be caught above, but plain "tin" has + a random chance of yielding "tin wand" unless we do this */ + if (!strcmpi(actualn, "tin")) { + typ = TIN; + goto typfnd; + } } if (((typ = rnd_otyp_by_namedesc(actualn, oclass)) != STRANGE_OBJECT) From fa2fbb64c84f8ce019d44f339ade68da54ca8001 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 2 Jul 2017 14:36:01 -0700 Subject: [PATCH 07/39] tribute typo --- dat/tribute | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dat/tribute b/dat/tribute index 64746b8b7..0b6f58fd6 100644 --- a/dat/tribute +++ b/dat/tribute @@ -5944,7 +5944,7 @@ shouldn't be so long as to make you miss mealtimes. They said he was amazing. The Amazing Maurice, they said. He'd never meant to be amazing. It just happened. -He's realized something was odd that day, just after lunch, when he'd +He'd realized something was odd that day, just after lunch, when he'd looked into a reflection in a puddle and thought, /that's me/. He'd never been /aware/ of himself before. Of course it was hard to remember /how/ he'd thought before becoming amazing. It seemed to him that his mind had From ca84c8e0ffc8b54163d5249fa6a3983497b652e8 Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 3 Jul 2017 18:57:50 -0700 Subject: [PATCH 08/39] named-fruit manipulation Add some new routines for dealing with fruit. I had hoped they would let the existing fruit handling be simplified quite a bit, but the improvement wasn't great. However, they're also groundwork for fixing an old bug. --- include/extern.h | 3 + src/bones.c | 10 +--- src/cmd.c | 11 ++-- src/objnam.c | 152 +++++++++++++++++++++++++++++++++++++++++++---- src/options.c | 76 +++++++++++------------- 5 files changed, 187 insertions(+), 65 deletions(-) diff --git a/include/extern.h b/include/extern.h index 705b19cdc..10e8ea54a 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1621,6 +1621,9 @@ E char *FDECL(simple_typename, (int)); E boolean FDECL(obj_is_pname, (struct obj *)); E char *FDECL(distant_name, (struct obj *, char *(*)(OBJ_P))); E char *FDECL(fruitname, (BOOLEAN_P)); +E struct fruit *FDECL(fruit_from_indx, (int)); +E struct fruit *FDECL(fruit_from_name, (const char *, BOOLEAN_P, int *)); +E void FDECL(reorder_fruit, (BOOLEAN_P)); E char *FDECL(xname, (struct obj *)); E char *FDECL(mshot_xname, (struct obj *)); E boolean FDECL(the_unique_obj, (struct obj *)); diff --git a/src/bones.c b/src/bones.c index 6784259c6..6f35d2631 100644 --- a/src/bones.c +++ b/src/bones.c @@ -45,14 +45,10 @@ STATIC_OVL void goodfruit(id) int id; { - register struct fruit *f; + struct fruit *f = fruit_from_indx(-id); - for (f = ffruit; f; f = f->nextf) { - if (f->fid == -id) { - f->fid = id; - return; - } - } + if (f) + f->fid = id; } STATIC_OVL void diff --git a/src/cmd.c b/src/cmd.c index ade3352cd..6d1a0f113 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -2471,14 +2471,15 @@ int final; /* named fruit debugging (doesn't really belong here...); to enable, include 'fruit' in DEBUGFILES list (even though it isn't a file...) */ if (wizard && explicitdebug("fruit")) { - int fcount = 0; struct fruit *f; - char buf2[BUFSZ]; + reorder_fruit(TRUE); /* sort by fruit index, from low to high; + * this modifies the ffruit chain, so could + * possibly mask or even introduce a problem, + * but it does useful sanity checking */ for (f = ffruit; f; f = f->nextf) { - Sprintf(buf, "Fruit %d ", ++fcount); - Sprintf(buf2, "%s (id %d)", f->fname, f->fid); - enl_msg(buf, "is ", "was ", buf2, ""); + Sprintf(buf, "Fruit #%d ", f->fid); + enl_msg(buf, "is ", "was ", f->fname, ""); } enl_msg("The current fruit ", "is ", "was ", pl_fruit, ""); Sprintf(buf, "%d", flags.made_fruit); diff --git a/src/objnam.c b/src/objnam.c index e5cbbc009..e583d7a5c 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -251,6 +251,135 @@ boolean juice; /* whether or not to append " juice" to the name */ return buf; } +/* look up a named fruit by index (1..127) */ +struct fruit * +fruit_from_indx(indx) +int indx; +{ + struct fruit *f; + + for (f = ffruit; f; f = f->nextf) + if (f->fid == indx) + break; + return f; +} + +/* look up a named fruit by name */ +struct fruit * +fruit_from_name(fname, exact, highest_fid) +const char *fname; +boolean exact; /* False => prefix or exact match, True = exact match only */ +int *highest_fid; /* optional output; only valid if 'fname' isn't found */ +{ + struct fruit *f, *tentativef; + char *altfname; + unsigned k; + /* + * note: named fruits are case-senstive... + */ + + if (highest_fid) + *highest_fid = 0; + /* first try for an exact match */ + for (f = ffruit; f; f = f->nextf) + if (!strcmp(f->fname, fname)) + return f; + else if (highest_fid && f->fid > *highest_fid) + *highest_fid = f->fid; + + /* didn't match as-is; if caller is willing to accept a prefix + match, try to find one; we want to find the longest prefix that + matches, not the first */ + if (!exact) { + tentativef = 0; + for (f = ffruit; f; f = f->nextf) { + k = strlen(f->fname); + if (!strncmp(f->fname, fname, k) + && (!fname[k] || fname[k] == ' ') + && (!tentativef || k > strlen(tentativef->fname))) + tentativef = f; + } + f = tentativef; + } + /* if we still don't have a match, try singularizing the target; + for exact match, that's trivial, but for prefix, it's hard */ + if (!f) { + altfname = makesingular(fname); + for (f = ffruit; f; f = f->nextf) { + if (!strcmp(f->fname, altfname)) + break; + } + releaseobuf(altfname); + } + if (!f && !exact) { + char fnamebuf[BUFSZ], *p; + unsigned fname_k = strlen(fname); /* length of assumed plural fname */ + + tentativef = 0; + for (f = ffruit; f; f = f->nextf) { + k = strlen(f->fname); + /* reload fnamebuf[] each iteration in case it gets modified; + there's no need to recalculate fname_k */ + Strcpy(fnamebuf, fname); + /* bug? if singular of fname is longer than plural, + failing the 'fname_k > k' test could skip a viable + candidate; unfortunately, we can't singularize until + after stripping off trailing stuff and we can't get + accurate fname_k until fname has been singularized; + compromise and use 'fname_k >= k' instead of '>', + accepting 1 char length discrepancy without risking + false match (I hope...) */ + if (fname_k >= k && (p = index(&fnamebuf[k], ' ')) != 0) { + *p = '\0'; /* truncate at 1st space past length of f->fname */ + altfname = makesingular(fnamebuf); + k = strlen(altfname); /* actually revised 'fname_k' */ + if (!strcmp(f->fname, altfname) + && (!tentativef || k > strlen(tentativef->fname))) + tentativef = f; + releaseobuf(altfname); /* avoid churning through all obufs */ + } + } + f = tentativef; + } + return f; +} + +/* sort the named-fruit linked list by fruit index number */ +void +reorder_fruit(forward) +boolean forward; +{ + struct fruit *f, *allfr[1 + 127]; + int i, j, k = SIZE(allfr); + + for (i = 0; i < k; ++i) + allfr[i] = (struct fruit *) 0; + for (f = ffruit; f; f = f->nextf) { + /* without sanity checking, this would reduce to 'allfr[f->fid]=f' */ + j = f->fid; + if (j < 1 || j >= k) { + impossible("reorder_fruit: fruit index (%d) out of range", j); + return; /* don't sort after all; should never happen... */ + } else if (allfr[j]) { + impossible("reorder_fruit: duplicate fruit index (%d)", j); + return; + } + allfr[j] = f; + } + ffruit = 0; /* reset linked list; we're rebuilding it from scratch */ + /* slot [0] will always be empty; must start 'i' at 1 to avoid + [k - i] being out of bounds during first iteration */ + for (i = 1; i < k; ++i) { + /* for forward ordering, go through indices from high to low; + for backward ordering, go from low to high */ + j = forward ? (k - i) : i; + if (allfr[j]) { + allfr[j]->nextf = ffruit; + ffruit = allfr[j]; + } + } +} + char * xname(obj) struct obj *obj; @@ -388,23 +517,20 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */ break; case FOOD_CLASS: if (typ == SLIME_MOLD) { - register struct fruit *f; + struct fruit *f = fruit_from_indx(obj->spe); - for (f = ffruit; f; f = f->nextf) { - if (f->fid == obj->spe) { - Strcpy(buf, f->fname); - break; - } - } if (!f) { impossible("Bad fruit #%d?", obj->spe); Strcpy(buf, "fruit"); - } else if (pluralize) { - /* ick; already pluralized fruit names - are allowed--we want to try to avoid - adding a redundant plural suffix */ - Strcpy(buf, makeplural(makesingular(buf))); - pluralize = FALSE; + } else { + Strcpy(buf, f->fname); + if (pluralize) { + /* ick; already pluralized fruit names + are allowed--we want to try to avoid + adding a redundant plural suffix */ + Strcpy(buf, makeplural(makesingular(buf))); + pluralize = FALSE; + } } break; } diff --git a/src/options.c b/src/options.c index b7ee5f0ba..38d258c6f 100644 --- a/src/options.c +++ b/src/options.c @@ -2341,32 +2341,32 @@ boolean tinitial, tfrom_file; return; if (!initial) { struct fruit *f; + int fnum = 0; - num = 0; - for (f = ffruit; f; f = f->nextf) { - if (!strcmp(op, f->fname)) - break; - num++; - } - if (!flags.made_fruit) { - for (forig = ffruit; forig; forig = forig->nextf) { - if (!strcmp(pl_fruit, forig->fname)) { - break; - } + /* count number of named fruits; if 'op' is found among them, + then the count doesn't matter because we won't be adding it */ + f = fruit_from_name(op, FALSE, &fnum); + if (!f) { + if (!flags.made_fruit) + forig = fruit_from_name(pl_fruit, FALSE, (int *) 0); + + if (!forig && fnum >= 100) { + pline("Doing that so many times isn't very fruitful."); + return; } } - if (!forig && num >= 100) { - pline("Doing that so many times isn't very fruitful."); - return; - } } goodfruit: nmcpy(pl_fruit, op, PL_FSIZ); sanitize_name(pl_fruit); - /* OBJ_NAME(objects[SLIME_MOLD]) won't work after initialization */ + /* OBJ_NAME(objects[SLIME_MOLD]) won't work for this after + initialization; it gets changed to generic "fruit" */ if (!*pl_fruit) nmcpy(pl_fruit, "slime mold", PL_FSIZ); if (!initial) { + /* if 'forig' is nonNull, we replace it rather than add + a new fruit; it can only be nonNull if no fruits have + been created since the previous name was put in place */ (void) fruitadd(pl_fruit, forig); pline("Fruit is now \"%s\".", pl_fruit); } @@ -5574,7 +5574,6 @@ struct fruit *replace_fruit; /* disallow naming after other foods (since it'd be impossible * to tell the difference) */ - for (i = bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS; i++) { if (!strcmp(OBJ_NAME(objects[i]), pl_fruit)) { found = TRUE; @@ -5584,15 +5583,15 @@ struct fruit *replace_fruit; { char *c; - c = pl_fruit; - for (c = pl_fruit; *c >= '0' && *c <= '9'; c++) - ; + continue; if (isspace((uchar) *c) || *c == 0) numeric = TRUE; } - if (found || numeric || !strncmp(str, "cursed ", 7) - || !strncmp(str, "uncursed ", 9) || !strncmp(str, "blessed ", 8) + if (found || numeric + || !strncmp(str, "cursed ", 7) + || !strncmp(str, "uncursed ", 9) + || !strncmp(str, "blessed ", 8) || !strncmp(str, "partly eaten ", 13) || (!strncmp(str, "tin of ", 7) && (!strcmp(str + 7, "spinach") @@ -5615,42 +5614,39 @@ struct fruit *replace_fruit; */ flags.made_fruit = FALSE; if (replace_fruit) { - for (f = ffruit; f; f = f->nextf) { - if (f == replace_fruit) { - copynchars(f->fname, str, PL_FSIZ - 1); - goto nonew; - } - } + /* replace_fruit is already part of the fruit chain; + update it in place rather than looking it up again */ + f = replace_fruit; + copynchars(f->fname, str, PL_FSIZ - 1); + goto nonew; } } else { /* not user_supplied, so assumed to be from bones */ copynchars(altname, str, PL_FSIZ - 1); sanitize_name(altname); flags.made_fruit = TRUE; /* for safety. Any fruit name added from a - bones level should exist anyway. */ + * bones level should exist anyway. */ } - for (f = ffruit; f; f = f->nextf) { - if (f->fid > highest_fruit_id) - highest_fruit_id = f->fid; - if (!strncmp(str, f->fname, PL_FSIZ - 1) - || (*altname && !strcmp(altname, f->fname))) - goto nonew; - } - /* if adding another fruit would overflow spe, use a random - fruit instead... we've got a lot to choose from. + f = fruit_from_name(*altname ? altname : str, FALSE, &highest_fruit_id); + if (f) + goto nonew; + + /* Maximum number of named fruits is 127, even if obj->spe can + handle bigger values. If adding another fruit would overflow, + use a random fruit instead... we've got a lot to choose from. current_fruit remains as is. */ if (highest_fruit_id >= 127) return rnd(127); f = newfruit(); - (void) memset((genericptr_t)f, 0, sizeof(struct fruit)); + (void) memset((genericptr_t) f, 0, sizeof (struct fruit)); copynchars(f->fname, *altname ? altname : str, PL_FSIZ - 1); f->fid = ++highest_fruit_id; /* we used to go out of our way to add it at the end of the list, but the order is arbitrary so use simpler insertion at start */ f->nextf = ffruit; ffruit = f; -nonew: + nonew: if (user_specified) context.current_fruit = f->fid; return f->fid; From 7a8559a34d238ccfd44f44f5dadbd11c1d0bec58 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 4 Jul 2017 14:47:00 -0700 Subject: [PATCH 09/39] releaseobuf() fix The object name formatting routines operate using a pool of buffers to hold intermediate and/or final result. Some routines consume multiple intermediate buffers, so use releaseobj() to try to reuse just one in order to avoid churning through too many and maybe clobbering live data. It worked as intended for routines that use nextobuf() directly but wouldn't haved worked right for xname(), also doname() and other xname() callers. This fixes that. There have never been any reports of garbled messages which could be traced to clobbering of formatted object names, so this fix is mostly academic. --- src/objnam.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/objnam.c b/src/objnam.c index e583d7a5c..4648649e3 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -90,8 +90,12 @@ releaseobuf(bufp) char *bufp; { /* caller may not know whether bufp is the most recently allocated - buffer; if it isn't, do nothing */ - if (bufp == obufs[obufidx]) + buffer; if it isn't, do nothing; note that because of the somewhat + obscure PREFIX handling for object name formatting by xname(), + the pointer our caller has and is passing to us might be into the + middle of an obuf rather than the address returned by nextobuf() */ + if (bufp >= obufs[obufidx] + && bufp < obufs[obufidx] + sizeof obufs[obufidx]) /* obufs[][BUFSZ] */ obufidx = (obufidx - 1 + NUMOBUF) % NUMOBUF; } @@ -100,11 +104,11 @@ obj_typename(otyp) register int otyp; { char *buf = nextobuf(); - register struct objclass *ocl = &objects[otyp]; - register const char *actualn = OBJ_NAME(*ocl); - register const char *dn = OBJ_DESCR(*ocl); - register const char *un = ocl->oc_uname; - register int nn = ocl->oc_name_known; + struct objclass *ocl = &objects[otyp]; + const char *actualn = OBJ_NAME(*ocl); + const char *dn = OBJ_DESCR(*ocl); + const char *un = ocl->oc_uname; + int nn = ocl->oc_name_known; if (Role_if(PM_SAMURAI) && Japanese_item_name(otyp)) actualn = Japanese_item_name(otyp); From a573134d7e037b6896fef574d5ef04e06c28856a Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 4 Jul 2017 18:44:03 -0700 Subject: [PATCH 10/39] fix #H3013 - grammar bug with named fruit Reported nearly four years ago for 3.4.3, original subject was \#H3013: NetHack grammar bug when taking unpaid fruit from chest Player used OPTIONS=fruit:Quorn and the capitalized value confuses the() into thinking it's a proper name which shouldn't be preceded by an article, resulting in "Quorn will cost you N zorkmids" when removing it from a chest in a shop, followed by "X - a Quorn (unpaid)" as it went into inventory. It is a product name, but when used as a fruit it shouldn't be treated as a proper name. (Quorn is a meat substitute rather than anything related to fruit.) Teach the() about named-fruits, so that we'll get "The Quorn will cost you N zorkmids." Unfortunately, it means that someone who names their fruit after a proper name used by the program, for example Mjollnir, can probably induce other poorly worded messages (about the item rather than the named-fruit). the() is used all over the place and all it has to work with is text, not the object whose formatted name produced that text. I looked through a bunch of old cvs log messages last night, and spotted one I wrote (in objnam.c) a dozen years ago where I suggested forcing named-fruit values into lower case as they're being set up. I don't remember that, but if we'd done it, this bug would have been avoided. --- src/objnam.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/objnam.c b/src/objnam.c index 4648649e3..ec7c0e70e 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -1623,7 +1623,10 @@ const char *str; buf[0] = lowc(*str); Strcpy(&buf[1], str + 1); return buf; - } else if (*str < 'A' || *str > 'Z') { + } else if (*str < 'A' || *str > 'Z' + /* treat named fruit as not a proper name, even if player + has assigned a capitalized proper name as his/her fruit */ + || fruit_from_name(str, TRUE, (int *) 0)) { /* not a proper name, needs an article */ insert_the = TRUE; } else { From c0bb25b3884fdc9ce83571fc6769b3c746888047 Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 12 Jul 2017 17:53:04 -0700 Subject: [PATCH 11/39] fix commit ebd6bb62f58a : #H4383 - blasted twice by Excalibur. Noticed on Reddit by Alex, the attempt to fix being blasted twice by wielded artifact weapon when changing alignment ended up preventing wielding other role's quest weapons. At the moment I can't even see how it prevented the double-blast.... This backs out that change and fixes the double-blasting correctly. When uwep and uswapwep are tested in advance of the rest of invent, mark them as already processed before entering the loop that checks all not-yet-processed inventory. --- doc/fixes36.1 | 2 ++ src/artifact.c | 10 +++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 03781df5f..e4a415273 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -455,6 +455,8 @@ attempting to name an item as an artifact and failing via hand slip violates crashes for 'A' above were downgraded to impossible "cursed without otmp" wizhelp: ^O is #overview in wizard mode too; #wizwhere shows dungeon layout wishing for tins sometimes yielded a tin wand +replace the fix for preventing putting on a helm of opposite alignment from + causing wielded Excalibur from blasting hero twice Platform- and/or Interface-Specific Fixes diff --git a/src/artifact.c b/src/artifact.c index 113843e78..6f8326df7 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -1888,7 +1888,7 @@ boolean loseit; /* whether to drop it if hero can longer touch it */ bane = bane_applies(get_artifact(obj), &youmonst); /* nothing else to do if hero can successfully handle this object */ - if (!ag && !bane && !touch_blasted) + if (!ag && !bane) return 1; /* hero can't handle this object, but didn't get touch_artifact()'s @@ -2008,11 +2008,15 @@ int dropflag; /* 0==don't drop, 1==drop all, 2==drop weapon */ dropit = (dropflag > 0); /* drop all or drop weapon */ /* check secondary weapon first, before possibly unwielding primary */ - if (u.twoweap) + if (u.twoweap) { + bypass_obj(uswapwep); /* so loop below won't process it again */ (void) untouchable(uswapwep, dropit); + } /* check primary weapon next so that they're handled together */ - if (uwep) + if (uwep) { + bypass_obj(uwep); /* so loop below won't process it again */ (void) untouchable(uwep, dropit); + } /* in case someone is daft enough to add artifact or silver saddle */ if (u.usteed && (obj = which_armor(u.usteed, W_SADDLE)) != 0) { From fe4583fc881555273ebd479db98bb1405aa3ac53 Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 12 Jul 2017 18:02:52 -0700 Subject: [PATCH 12/39] some artifact.c formatting I managed to separate these bits from the touch/retouch fix. I almost threw then away, but one's a spelling fix in a comment. --- src/artifact.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/artifact.c b/src/artifact.c index 6f8326df7..bfd576ff4 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -653,16 +653,17 @@ struct monst *mon; return 1; yours = (mon == &youmonst); - /* all quest artifacts are self-willed; it this ever changes, `badclass' + /* all quest artifacts are self-willed; if this ever changes, `badclass' will have to be extended to explicitly include quest artifacts */ self_willed = ((oart->spfx & SPFX_INTEL) != 0); if (yours) { badclass = self_willed && ((oart->role != NON_PM && !Role_if(oart->role)) || (oart->race != NON_PM && !Race_if(oart->race))); - badalign = - (oart->spfx & SPFX_RESTR) && oart->alignment != A_NONE - && (oart->alignment != u.ualign.type || u.ualign.record < 0); + badalign = ((oart->spfx & SPFX_RESTR) != 0 + && oart->alignment != A_NONE + && (oart->alignment != u.ualign.type + || u.ualign.record < 0)); } else if (!is_covetous(mon->data) && !is_mplayer(mon->data)) { badclass = self_willed && oart->role != NON_PM && oart != &artilist[ART_EXCALIBUR]; @@ -1493,8 +1494,8 @@ struct obj *obj; obj->age = 0; return 0; } - b_effect = - obj->blessed && (Role_switch == oart->role || !oart->role); + b_effect = (obj->blessed + && (Role_switch == oart->role || !oart->role)); recharge(otmp, b_effect ? 1 : obj->cursed ? -1 : 0); update_inventory(); break; @@ -1583,9 +1584,8 @@ struct obj *obj; } else otmp->quan += rnd(5); otmp->owt = weight(otmp); - otmp = - hold_another_object(otmp, "Suddenly %s out.", - aobjnam(otmp, "fall"), (const char *) 0); + otmp = hold_another_object(otmp, "Suddenly %s out.", + aobjnam(otmp, "fall"), (char *) 0); break; } } @@ -1883,8 +1883,7 @@ boolean loseit; /* whether to drop it if hero can longer touch it */ if (touch_artifact(obj, &youmonst)) { char buf[BUFSZ]; int dmg = 0, tmp; - boolean ag = - (objects[obj->otyp].oc_material == SILVER && Hate_silver), + boolean ag = (objects[obj->otyp].oc_material == SILVER && Hate_silver), bane = bane_applies(get_artifact(obj), &youmonst); /* nothing else to do if hero can successfully handle this object */ From 1c026727fbefd20b10b6cff10175dc65c21cbef9 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sat, 15 Jul 2017 01:34:41 +0100 Subject: [PATCH 13/39] Remove obsolete code allowing 1-indexing the monster list Having selectable base indexes for the monster list doesn't seem likely to be needed in the future any more, now that the code for the monster list is stable. Additionally, the functionality in question has bitrotted heavily (e.g. many "loops over all permonsts" start at a hardcoded 0, which wouldn't work with a 1-indexed monster list). As a result, removing the relevant code in makedefs makes it clearer what can and can't be assumed about the code, reducing the risk of bugs in the future. Thanks to FIQ for mentioning that this could be an issue. --- include/permonst.h | 2 +- util/makedefs.c | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/include/permonst.h b/include/permonst.h index a5c4555c0..f70db03f0 100644 --- a/include/permonst.h +++ b/include/permonst.h @@ -70,7 +70,7 @@ extern NEARDATA struct permonst mons[]; /* the master list of monster types */ #define FAST_SPEED 15 #define VERY_FAST 24 -#define NON_PM PM_PLAYERMON /* "not a monster" */ +#define NON_PM (-1) /* "not a monster" */ #define LOW_PM (NON_PM + 1) /* first monster in mons[] */ #define SPECIAL_PM PM_LONG_WORM_TAIL /* [normal] < ~ < [special] */ /* mons[SPECIAL_PM] through mons[NUMMONS-1], inclusive, are diff --git a/util/makedefs.c b/util/makedefs.c index 7ebd5136b..92d1feb89 100644 --- a/util/makedefs.c +++ b/util/makedefs.c @@ -2224,9 +2224,6 @@ do_permonst() Fprintf(ofp, "%s", Dont_Edit_Code); Fprintf(ofp, "#ifndef PM_H\n#define PM_H\n"); - if (strcmp(mons[0].mname, "playermon") != 0) - Fprintf(ofp, "\n#define\tPM_PLAYERMON\t(-1)"); - for (i = 0; mons[i].mlet; i++) { SpinCursor(3); From d17858f9479603db62107346a0edb7ba875ea355 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 15 Jul 2017 18:24:56 -0700 Subject: [PATCH 14/39] health recovery This started out as just an attempt to remove some duplicated code, but mutated. Move health recovery into a separate routine to streamline moveloop(). Intentional changes: 1) when poly'd hero is at max u.mh (hit points as a monster), do not recover lost u.uhp (hit points when in normal form). That was caused by a missing !Upolyd check in the long if..elseif.. elseif..endif logic. If we want to make it deliberate, I think some u.uhp recovery in rehumanize() would be the way to go. 2) regeneration for poly'd hero in sea monster form (ring worn on left or right pectoral fin) now counteracts the loss of hit points for turns spent out of water. [Do eels even have fins?] 3) poly'd hero with moderate or worse encumbrance and lacking regeneration wouldn't recover any health. Now he/she will do so if not moving [on the magic (moves%20 == 0) turn when u.mh recovery takes place]. If there are any other changes in behavior, they're unintentional. --- src/allmain.c | 139 +++++++++++++++++++++++++++++--------------------- 1 file changed, 82 insertions(+), 57 deletions(-) diff --git a/src/allmain.c b/src/allmain.c index 1ca917a74..2b006bf63 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -13,6 +13,7 @@ #ifdef POSITIONBAR STATIC_DCL void NDECL(do_positionbar); #endif +STATIC_DCL void FDECL(regen_hp, (int)); STATIC_DCL void FDECL(interrupt_multi, (const char *)); void @@ -185,66 +186,20 @@ boolean resuming; /* One possible result of prayer is healing. Whether or * not you get healed depends on your current hit points. - * If you are allowed to regenerate during the prayer, the - * end-of-prayer calculation messes up on this. + * If you are allowed to regenerate during the prayer, + * the end-of-prayer calculation messes up on this. * Another possible result is rehumanization, which - * requires - * that encumbrance and movement rate be recalculated. + * requires that encumbrance and movement rate be + * recalculated. */ if (u.uinvulnerable) { /* for the moment at least, you're in tiptop shape */ wtcap = UNENCUMBERED; - } else if (Upolyd && youmonst.data->mlet == S_EEL - && !is_pool(u.ux, u.uy) - && !Is_waterlevel(&u.uz)) { - /* eel out of water loses hp, same as for monsters; - as hp gets lower, rate of further loss slows down - */ - if (u.mh > 1 && rn2(u.mh) > rn2(8) - && (!Half_physical_damage || !(moves % 2L))) { - u.mh--; - context.botl = 1; - } else if (u.mh < 1) - rehumanize(); - } else if (Upolyd && u.mh < u.mhmax) { - if (u.mh < 1) - rehumanize(); - else if (Regeneration - || (wtcap < MOD_ENCUMBER && !(moves % 20))) { - context.botl = 1; - u.mh++; - if (u.mh >= u.mhmax) - interrupt_multi("You are in full health."); - } - } else if (u.uhp < u.uhpmax - && (wtcap < MOD_ENCUMBER || !u.umoved - || Regeneration)) { - if (u.ulevel > 9 && !(moves % 3)) { - int heal, Con = (int) ACURR(A_CON); - - if (Con <= 12) { - heal = 1; - } else { - heal = rnd(Con); - if (heal > u.ulevel - 9) - heal = u.ulevel - 9; - } - context.botl = 1; - u.uhp += heal; - if (u.uhp > u.uhpmax) - u.uhp = u.uhpmax; - if (u.uhp >= u.uhpmax) - interrupt_multi("You are in full health."); - } else if (Regeneration - || (u.ulevel <= 9 - && !(moves - % ((MAXULEV + 12) / (u.ulevel + 2) - + 1)))) { - context.botl = 1; - u.uhp++; - if (u.uhp >= u.uhpmax) - interrupt_multi("You are in full health."); - } + } else if (!Upolyd ? (u.uhp < u.uhpmax) + : (u.mh < u.mhmax + || youmonst.data->mlet == S_EEL)) { + /* maybe heal */ + regen_hp(wtcap); } /* moving around while encumbered is hard work */ @@ -263,7 +218,7 @@ boolean resuming; } } - if ((u.uen < u.uenmax) + if (u.uen < u.uenmax && ((wtcap < MOD_ENCUMBER && (!(moves % ((MAXULEV + 8 - u.ulevel) * (Role_if(PM_WIZARD) ? 3 : 4) @@ -273,7 +228,7 @@ boolean resuming; if (u.uen > u.uenmax) u.uen = u.uenmax; context.botl = 1; - if (u.uen >= u.uenmax) + if (u.uen == u.uenmax) interrupt_multi("You feel full of energy."); } @@ -485,6 +440,76 @@ boolean resuming; } } +/* maybe recover some lost health (or lose some when an eel out of water) */ +STATIC_OVL void +regen_hp(wtcap) +int wtcap; +{ + int heal = 0; + boolean reached_full = FALSE, + encumbrance_ok = (wtcap < MOD_ENCUMBER || !u.umoved); + + if (Upolyd) { + if (u.mh < 1) { /* shouldn't happen... */ + rehumanize(); + } else if (youmonst.data->mlet == S_EEL + && !is_pool(u.ux, u.uy) && !Is_waterlevel(&u.uz)) { + /* eel out of water loses hp, similar to monster eels; + as hp gets lower, rate of further loss slows down */ + if (u.mh > 1 && !Regeneration && rn2(u.mh) > rn2(8) + && (!Half_physical_damage || !(moves % 2L))) + heal = -1; + } else if (u.mh < u.mhmax) { + if (Regeneration || (encumbrance_ok && !(moves % 20L))) + heal = 1; + } + if (heal) { + context.botl = 1; + u.mh += heal; + reached_full = (u.mh == u.mhmax); + } + + /* !Upolyd */ + } else { + /* [when this code was in-line within moveloop(), there was + no !Upolyd check here, so poly'd hero recovered lost u.uhp + once u.mh reached u.mhmax; that may have been convenient + for the player, but it didn't make sense for gameplay...] */ + if (u.uhp < u.uhpmax && (encumbrance_ok || Regeneration)) { + if (u.ulevel > 9) { + if (!(moves % 3L)) { + int Con = (int) ACURR(A_CON); + + if (Con <= 12) { + heal = 1; + } else { + heal = rnd(Con); + if (heal > u.ulevel - 9) + heal = u.ulevel - 9; + } + } + } else { /* u.ulevel <= 9 */ + if (!(moves % (long) ((MAXULEV + 12) / (u.ulevel + 2) + 1))) + heal = 1; + } + if (Regeneration && !heal) + heal = 1; + + if (heal) { + context.botl = 1; + u.uhp += heal; + if (u.uhp > u.uhpmax) + u.uhp = u.uhpmax; + /* stop voluntary multi-turn activity if now fully healed */ + reached_full = (u.uhp == u.uhpmax); + } + } + } + + if (reached_full) + interrupt_multi("You are in full health."); +} + void stop_occupation() { From 27f9a83a0b9055073533d8f638342be7ff2faf36 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 16 Jul 2017 15:28:44 -0700 Subject: [PATCH 15/39] mons[0] Alex mentioned that loops over mons[] were starting at [0], which should be [LOW_PM] instead. I only found two, and the mvitals[] one was benign. The special level one might have been too, depending upon spec_lev's thoroughness--I didn't attempt to check. Once upon a time there was a possibility of moving 'playermon' from a separate variable to mons[0], so LOW_PM became the index of the first valid monster. Instead, 'playermon' went away altogether. LOW_PM (and NON_PM) could go away too, but I don't see how reverting to hardcoded 0 and -1 would be an improvement. We have enough problems as it is with "giant ant" turning up in unexpected places because someone used 0 instead of NON_PM to mean "none of the above". --- src/allmain.c | 2 +- src/sp_lev.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/allmain.c b/src/allmain.c index 2b006bf63..fa9e03ac6 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -578,7 +578,7 @@ newgame() context.tribute.enabled = TRUE; /* turn on 3.6 tributes */ context.tribute.tributesz = sizeof(struct tribute_info); - for (i = 0; i < NUMMONS; i++) + for (i = LOW_PM; i < NUMMONS; i++) mvitals[i].mvflags = mons[i].geno & G_NOCORPSE; init_objects(); /* must be before u_init() */ diff --git a/src/sp_lev.c b/src/sp_lev.c index 179d45fa3..9ef1dcf46 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -3050,6 +3050,7 @@ struct sp_coder *coder; while ((nparams++ < (SP_O_V_END + 1)) && (OV_typ(varparam) == SPOVAR_INT) && (OV_i(varparam) >= 0) && (OV_i(varparam) < SP_O_V_END)) { struct opvar *parm; + OV_pop(parm); switch (OV_i(varparam)) { case SP_O_V_NAME: @@ -3061,11 +3062,12 @@ struct sp_coder *coder; char monclass = SP_MONST_CLASS(OV_i(parm)); int monid = SP_MONST_PM(OV_i(parm)); - if (monid >= 0 && monid < NUMMONS) { + if (monid >= LOW_PM && monid < NUMMONS) { tmpobj.corpsenm = monid; break; /* we're done! */ } else { struct permonst *pm = (struct permonst *) 0; + if (def_char_to_monclass(monclass) != MAXMCLASSES) { pm = mkclass(def_char_to_monclass(monclass), G_NOGEN); } else { From e3c4ac730d6663079b57431113556db792fe8004 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 30 Jul 2017 17:34:52 -0700 Subject: [PATCH 16/39] util updates: lev_main.c and recover.c Change lev_comp's add_opvars() to be like pline(), where there's a single visible opening brace and a second one hidden in VA_DECL2 that introduces a nested block, plus a single visible closing brace with a hidden one in VA_END() to close the nested block. This addresses the erroneous report (sent directly to devteam, so no #H number, subject "missing '{' in util/lev_main.c:634") that the code for !USE_STDARG/!USE_VARARGS in add_opvars() wouldn't compile. Also, fix the part of "#H5778: file descriptor leaks" dealing with util/recover.c -- an open file not being closed after various errors. I didn't take responsibility for this entry in the bugzilla list since the report includes similar problems in other code that's not addressed here. And a blast from the past: some reformatting fixups in recover.c. The most interesting bit is for a block of dead code.... --- util/lev_main.c | 14 +++++++++----- util/recover.c | 50 +++++++++++++++++++++++++++---------------------- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/util/lev_main.c b/util/lev_main.c index 3dce1a720..773c9480b 100644 --- a/util/lev_main.c +++ b/util/lev_main.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 lev_main.c $NHDT-Date: 1448074107 2015/11/21 02:48:27 $ $NHDT-Branch: master $:$NHDT-Revision: 1.43 $ */ +/* NetHack 3.6 lev_main.c $NHDT-Date: 1501461281 2017/07/31 00:34:41 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.46 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -615,14 +615,14 @@ VA_DECL2(sp_lev *, sp, const char *, fmt) #ifdef USE_STDARG static void vadd_opvars(sp_lev *sp, const char *fmt, va_list the_args) -{ + #else static void vadd_opvars(sp, fmt, the_args) sp_lev *sp; const char *fmt; va_list the_args; -{ + #endif #else /* USE_STDARG | USE_VARARG */ @@ -632,7 +632,7 @@ va_list the_args; void add_opvars VA_DECL2(sp_lev *, sp, const char *, fmt) #endif /* USE_STDARG | USE_VARARG */ - +{ const char *p, *lp; long la; /* Do NOT use VA_START and VA_END in here... see above */ @@ -710,7 +710,11 @@ VA_DECL2(sp_lev *, sp, const char *, fmt) break; } } - return; + +#if !(defined(USE_STDARG) || defined(USE_VARARGS)) + /* provide closing brace for USE_OLDARGS nested block from VA_DECL2() */ + VA_END(); +#endif } void diff --git a/util/recover.c b/util/recover.c index 5c1c27f1f..27c58e709 100644 --- a/util/recover.c +++ b/util/recover.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 recover.c $NHDT-Date: 1432512785 2015/05/25 00:13:05 $ $NHDT-Branch: master $:$NHDT-Revision: 1.15 $ */ +/* NetHack 3.6 recover.c $NHDT-Date: 1501461282 2017/07/31 00:34:42 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.16 $ */ /* Copyright (c) Janet Walz, 1992. */ /* NetHack may be freely redistributed. See license for details. */ @@ -245,7 +245,7 @@ char *basename; errno); #endif Fprintf(stderr, "Cannot open level 0 for %s.\n", basename); - return (-1); + return -1; } if (read(gfd, (genericptr_t) &hpid, sizeof hpid) != sizeof hpid) { Fprintf( @@ -253,7 +253,7 @@ char *basename; "Checkpoint data incompletely written or subsequently clobbered;", "recovery for \"", basename, "\" impossible."); Close(gfd); - return (-1); + return -1; } if (read(gfd, (genericptr_t) &savelev, sizeof(savelev)) != sizeof(savelev)) { @@ -261,7 +261,7 @@ char *basename; "impossible.\n", basename); Close(gfd); - return (-1); + return -1; } if ((read(gfd, (genericptr_t) savename, sizeof savename) != sizeof savename) @@ -273,7 +273,7 @@ char *basename; || (read(gfd, (genericptr_t) &plbuf, pltmpsiz) != pltmpsiz)) { Fprintf(stderr, "Error reading %s -- can't recover.\n", lock); Close(gfd); - return (-1); + return -1; } /* save file should contain: @@ -288,7 +288,7 @@ char *basename; if (sfd < 0) { Fprintf(stderr, "Cannot create savefile %s.\n", savename); Close(gfd); - return (-1); + return -1; } lfd = open_levelfile(savelev); @@ -296,7 +296,7 @@ char *basename; Fprintf(stderr, "Cannot open level of save for %s.\n", basename); Close(gfd); Close(sfd); - return (-1); + return -1; } if (write(sfd, (genericptr_t) &version_data, sizeof version_data) @@ -304,7 +304,8 @@ char *basename; Fprintf(stderr, "Error writing %s; recovery failed.\n", savename); Close(gfd); Close(sfd); - return (-1); + Close(lfd); + return -1; } if (write(sfd, (genericptr_t) &sfi, sizeof sfi) != sizeof sfi) { @@ -313,7 +314,8 @@ char *basename; savename); Close(gfd); Close(sfd); - return (-1); + Close(lfd); + return -1; } if (write(sfd, (genericptr_t) &pltmpsiz, sizeof pltmpsiz) @@ -323,7 +325,8 @@ char *basename; savename); Close(gfd); Close(sfd); - return (-1); + Close(lfd); + return -1; } if (write(sfd, (genericptr_t) &plbuf, pltmpsiz) != pltmpsiz) { @@ -331,7 +334,8 @@ char *basename; savename); Close(gfd); Close(sfd); - return (-1); + Close(lfd); + return -1; } copy_bytes(lfd, sfd); @@ -364,25 +368,27 @@ char *basename; #if 0 /* OBSOLETE, HackWB is no longer in use */ #ifdef AMIGA - /* we need to create an icon for the saved game - * or HackWB won't notice the file. - */ - { + { + /* we need to create an icon for the saved game + * or HackWB won't notice the file. + */ char iconfile[FILENAME]; int in, out; (void) sprintf(iconfile, "%s.info", savename); in = open("NetHack:default.icon", O_RDONLY); out = open(iconfile, O_WRONLY | O_TRUNC | O_CREAT); - if(in > -1 && out > -1){ - copy_bytes(in,out); - } - if(in > -1)close(in); - if(out > -1)close(out); + if (in > -1 && out > -1) { + copy_bytes(in, out); } + if (in > -1) + close(in); + if (out > -1) + close(out); + } +#endif /*AMIGA*/ #endif -#endif - return (0); + return 0; } #ifdef EXEPATH From b4240e177240f6ed415d24062298a0c14955d95d Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 30 Jul 2017 18:17:00 -0700 Subject: [PATCH 17/39] fix #H5780 - file pointer freed twice win/share/tileset.c seems only to be used by the MSDOS port, but it compiles cleanly on OSX after these changes. A file pointer was passed to fclose() twice, second time potentially causing problems. There were cases of potentially null pointers being passed to free() too. That should be safe these days, but it's something we've tried to hard to avoid and would probably trigger complaints from our own MONITOR_HEAP code if that ever got applied here. --- win/share/tileset.c | 61 ++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/win/share/tileset.c b/win/share/tileset.c index 1b65c32b0..d37b91281 100644 --- a/win/share/tileset.c +++ b/win/share/tileset.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 tileset.c $NHDT-Date: 1457207053 2016/03/05 19:44:13 $ $NHDT-Branch: chasonr $:$NHDT-Revision: 1.0 $ */ +/* NetHack 3.6 tileset.c $NHDT-Date: 1501463811 2017/07/31 01:16:51 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.0 $ */ /* Copyright (c) Ray Chason, 2016. */ /* NetHack may be freely redistributed. See license for details. */ @@ -25,7 +25,7 @@ boolean true_color; 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }; struct TileSetImage image; - FILE *fp = NULL; /* custodial */ + FILE *fp; char header[16]; boolean ok; @@ -40,10 +40,11 @@ boolean true_color; /* Identify the image type */ fp = fopen(filename, "rb"); - if (fp == NULL) goto error; + if (!fp) + goto error; memset(header, 0, sizeof(header)); fread(header, 1, sizeof(header), fp); - fclose(fp); + fclose(fp), fp = (FILE *) 0; /* Call the loader appropriate for the image */ if (memcmp(header, "BM", 2) == 0) { @@ -56,11 +57,13 @@ boolean true_color; } else { ok = FALSE; } - if (!ok) goto error; + if (!ok) + goto error; /* Reject if the interface cannot handle direct color and the image does not use a palette */ - if (!true_color && image.indexes == NULL) goto error; + if (!true_color && image.indexes == NULL) + goto error; /* Save the palette if present */ have_palette = image.indexes != NULL; @@ -87,12 +90,12 @@ boolean true_color; /* Split the image into tiles */ split_tiles(&image); - fclose(fp); free_image(&image); return TRUE; error: - if (fp) fclose(fp); + if (fp) + fclose(fp); free_image(&image); return FALSE; } @@ -108,6 +111,7 @@ static void get_tile_map(image_desc) const char *image_desc; { + return; } void @@ -115,43 +119,40 @@ free_tiles() { unsigned i; - if (tiles != NULL) { + if (tiles) { for (i = 0; i < num_tiles; ++i) { - free(tiles[i].pixels); - free(tiles[i].indexes); + free((genericptr_t) tiles[i].pixels); + free((genericptr_t) tiles[i].indexes); } + free((genericptr_t) tiles), tiles = NULL; } - free(tiles); - tiles = NULL; num_tiles = 0; - free(blank_tile.pixels); - blank_tile.pixels = NULL; - free(blank_tile.indexes); - blank_tile.indexes = NULL; + if (blank_tile.pixels) + free((genericptr_t) blank_tile.pixels), blank_tile.pixels = NULL; + if (blank_tile.indexes) + free((genericptr_t) blank_tile.indexes), blank_tile.indexes = NULL; } static void free_image(image) struct TileSetImage *image; { - free(image->pixels); - image->pixels = NULL; - free(image->indexes); - image->indexes = NULL; - free(image->image_desc); - image->image_desc = NULL; + if (image->pixels) + free((genericptr_t) image->pixels), image->pixels = NULL; + if (image->indexes) + free((genericptr_t) image->indexes), image->indexes = NULL; + if (image->image_desc) + free((genericptr_t) image->image_desc), image->image_desc = NULL; } const struct TileImage * get_tile(tile_index) unsigned tile_index; { - if (tile_index >= num_tiles) { + if (tile_index >= num_tiles) return &blank_tile; - } else { - return &tiles[tile_index]; - } + return &tiles[tile_index]; } static void @@ -176,10 +177,11 @@ const struct TileSetImage *image; for (y1 = 0; y1 < tile_rows; ++y1) { for (x1 = 0; x1 < tile_cols; ++x1) { struct TileImage *tile = &tiles[y1 * tile_cols + x1]; + tile->width = iflags.wc_tile_width; tile->height = iflags.wc_tile_height; tile->pixels = (struct Pixel *) - alloc(tile_size * sizeof(struct Pixel)); + alloc(tile_size * sizeof (struct Pixel)); if (image->indexes != NULL) { tile->indexes = (unsigned char *) alloc(tile_size); } @@ -187,6 +189,7 @@ const struct TileSetImage *image; for (x2 = 0; x2 < iflags.wc_tile_width; ++x2) { unsigned x = x1 * iflags.wc_tile_width + x2; unsigned y = y1 * iflags.wc_tile_height + y2; + i = y * image->width + x; j = y2 * tile->width + x2; tile->pixels[j] = image->pixels[i]; @@ -202,7 +205,7 @@ const struct TileSetImage *image; blank_tile.width = iflags.wc_tile_width; blank_tile.height = iflags.wc_tile_height; blank_tile.pixels = (struct Pixel *) - alloc(tile_size * sizeof(struct Pixel)); + alloc(tile_size * sizeof (struct Pixel)); for (i = 0; i < tile_size; ++i) { blank_tile.pixels[i].r = 0; blank_tile.pixels[i].g = 0; From e4d4becfa23c1a439a73cb29ee5d3e5a14e61666 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 30 Jul 2017 18:33:28 -0700 Subject: [PATCH 18/39] fix #H5781 - missing fprintf args in wc_trace.c This compiles cleanly after the fixes, but is otherwise untested. --- win/chain/wc_trace.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/win/chain/wc_trace.c b/win/chain/wc_trace.c index 9073e062a..b3d21e7ea 100644 --- a/win/chain/wc_trace.c +++ b/win/chain/wc_trace.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 wc_trace.c $NHDT-Date: 1433806611 2015/06/08 23:36:51 $ $NHDT-Branch: master $:$NHDT-Revision: 1.7 $ */ +/* NetHack 3.6 wc_trace.c $NHDT-Date: 1501464799 2017/07/31 01:33:19 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.8 $ */ /* Copyright (c) Kenneth Lorber, 2012 */ /* NetHack may be freely redistributed. See license for details. */ @@ -10,7 +10,7 @@ #include FILE *wc_tracelogf; /* Should be static, but it's just too useful to have - * access to this logfile from arbitrary other files. */ + * access to this logfile from arbitrary other files. */ static unsigned int indent_level; /* Some winfuncs call other winfuncs, so * we need to support nesting. */ @@ -560,7 +560,7 @@ char *posbar; fprintf(wc_tracelogf, "%supdate_positionbar('%s'(%d))\n", INDENT, posbar, (int) strlen(posbar)); } else { - fprintf(wc_tracelogf, "%supdate_positionbar(NULL)\n"); + fprintf(wc_tracelogf, "%supdate_positionbar(NULL)\n", INDENT); } PRE; (*tdp->nprocs->win_update_positionbar)(tdp->ndata, posbar); @@ -901,7 +901,7 @@ void *vp; struct trace_data *tdp = vp; char *rv; - fprintf(wc_tracelogf, "%sget_color_string()\n"); + fprintf(wc_tracelogf, "%sget_color_string()\n", INDENT); PRE; rv = (*tdp->nprocs->win_get_color_string)(tdp->ndata); @@ -911,7 +911,7 @@ void *vp; fprintf(wc_tracelogf, "%s=> '%s'(%d)\n", INDENT, rv, (int) strlen(rv)); } else { - fprintf(wc_tracelogf, "%s=> NULL\n"); + fprintf(wc_tracelogf, "%s=> NULL\n", INDENT); } return rv; From 6b851e05033f329287f38e3a228d6042f0d881b0 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 30 Jul 2017 18:43:47 -0700 Subject: [PATCH 19/39] fix misplaced #if in sys/msdos/pckeys.c Reported directly to devteam, so no #H number. If SIMULATE_CURSOR isn't defined, there would be no switch statement to attach the subsequent cases to. The suggested fix is obviously correct, but untested.... --- sys/msdos/pckeys.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sys/msdos/pckeys.c b/sys/msdos/pckeys.c index 352c916e8..ec59b999d 100644 --- a/sys/msdos/pckeys.c +++ b/sys/msdos/pckeys.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 pckeys.c $NHDT-Date: 1457207039 2016/03/05 19:43:59 $ $NHDT-Branch: chasonr $:$NHDT-Revision: 1.11 $ */ +/* NetHack 3.6 pckeys.c $NHDT-Date: 1501465420 2017/07/31 01:43:40 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.12 $ */ /* Copyright (c) NetHack PC Development Team 1996 */ /* NetHack may be freely redistributed. See license for details. */ @@ -39,8 +39,8 @@ unsigned char shift; boolean opening_dialog; opening_dialog = pl_character[0] ? FALSE : TRUE; -#ifdef SIMULATE_CURSOR switch (scancode) { +#ifdef SIMULATE_CURSOR case 0x3d: /* F3 = toggle cursor type */ HideCursor(); cursor_type += 1; @@ -53,7 +53,6 @@ unsigned char shift; if ((shift & CTRL) && iflags.tile_view && !opening_dialog) userpan(1); break; - case 0x73: /* Control-left_arrow = scroll horizontal to left */ if ((shift & CTRL) && iflags.tile_view && !opening_dialog) userpan(0); From 439028dcaeaefeceed5bcc4a6b0381eeb4f1cb56 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 31 Jul 2017 16:58:23 +0300 Subject: [PATCH 20/39] Add whatis_filter option to filter eligible map locations for travel Compound option whatis_filter, filters the eligible map locations when getting a cursor location for targeting. Accepts 'n' (none), 'v' (map locations in view), or 'a' (map locations in the same area, eg. room or corridor). --- dat/opthelp | 6 ++ doc/Guidebook.mn | 28 ++++++++-- doc/Guidebook.tex | 31 +++++++++-- doc/fixes36.1 | 2 + include/decl.h | 8 +++ include/extern.h | 7 ++- include/flag.h | 10 +++- include/rm.h | 6 ++ include/sp_lev.h | 8 --- src/cmd.c | 8 ++- src/do_name.c | 136 ++++++++++++++++++++++++++++++++++++++++++---- src/options.c | 68 ++++++++++++++++++++++- src/sp_lev.c | 10 +--- 13 files changed, 285 insertions(+), 43 deletions(-) diff --git a/dat/opthelp b/dat/opthelp index 06fbf092d..eb9aeeb84 100644 --- a/dat/opthelp +++ b/dat/opthelp @@ -177,6 +177,12 @@ whatis_coord controls whether to include map coordinates when [n] map -- (map column x=0 is not used) screen -- [row,column] (row is offset to match tty usage) none -- no coordinates shown. +whatis_filter controls how to filter eligible map coordinates when [n] + getting a map location for eg. the travel command. + Value is the one of + n - no filtering + v - locations in view only + a - locations in same area (room, corridor, etc) Compound options which may be set only on startup are: diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 32e71245f..0de4d48e2 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -2927,10 +2927,23 @@ The option is also used with the `/m', `/M', `/o', and `/O' sub-commands of `/', where the `none' setting is overridden with `map'. -.lp whatis_inview +.lp whatis_filter When getting a location on the map, and using the keys to cycle through -next and previous targets, limit the possible targets to those in view. -(default off) +next and previous targets, allows filtering the possible targets. +.lp "" +.sd +.si +.CC n "no filtering [default]" +.CC v "in view only" +.CC a "in same area only" +.ei +.ed +.lp "" +The area-filter tries to be slightly predictive - if you're standing on a doorway, +it will consider the area on the side of the door you were last moving towards. +.lp "" +Filtering can also be changed when getting a location with the ``getpos.filter'' +key. .lp whatis_menu When getting a location on the map, and using a key to cycle through next and previous targets, use a menu instead to pick a target. @@ -3246,8 +3259,10 @@ When asked for a location, the key to go to next closest object. Default is 'o'. When asked for a location, the key to go to previous closest object. Default is 'O'. .lp getpos.menu When asked for a location, and using one of the next or previous keys to cycle through targets, toggle showing a menu instead. Default is '!'. -.lp getpos.inview -When asked for a location, and using one of the next or previous keys to cycle through targets, toggle limiting possible targets to those in view only. Default is '"'. +.lp getpos.filter +When asked for a location, change the filtering mode when using one of the next +or previous keys to cycle through targets. Toggles between no filtering, in view +only, and in the same area only. Default is '"'. .lp getpos.pick When asked for a location, the key to choose the location, and possibly ask for more info. Default is '.'. .lp getpos.pick.once @@ -3724,6 +3739,9 @@ was interrupted. .lp whatis_coord:compass When targeting with cursor, describe the cursor position with coordinates relative to your character. +.lp whatis_filter:area +When targeting with cursor, filter possible locations so only those in +the same area (eg. same room, or same corridor) are considered. .lp nostatus_updates Prevent updates to the status lines at the bottom of the screen, if your screen-reader reads those lines. The same information can be diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 07cc90964..c33e5b095 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -3561,10 +3561,27 @@ the `{\tt /m}', `{\tt /M}', `{\tt /o}', and `{\tt /O}' sub-commands of `{\tt /}', where the `{\it none\/}' setting is overridden with `{\it map}'. %.lp -\item[\ib{whatis\verb+_+inview}] +\item[\ib{whatis\verb+_+filter}] When getting a location on the map, and using the keys to cycle through -next and previous targets, limit the possible targets to those in view. -(default off) +next and previous targets, allows filtering the possible targets. +(default none) +%.lp "" +The possible settings are: + +%.sd +%.si +{\tt n} --- \verb#no filtering#;\\ +{\tt v} --- \verb#in view only#;\\ +{\tt a} --- \verb#in same area (room, corridor, etc)#. +%.ei +%.ed +%.lp "" +The area-filter tries to be slightly predictive - if you're standing on a doorway, +it will consider the area on the side of the door you were last moving towards. +%.lp "" +Filtering can also be changed when getting a location with the ``getpos.filter'' +key. +%.lp \item[\ib{whatis\verb+_+menu}] When getting a location on the map, and using a key to cycle through next and previous targets, use a menu instead to pick a target. @@ -3974,8 +3991,8 @@ When asked for a location, the key to go to previous closest object. Default is \item{\bb{getpos.menu}} When asked for a location, and using one of the next or previous keys to cycle through targets, toggle showing a menu instead. Default is '{\tt !}'. %.lp -\item{\bb{getpos.inview}} -When asked for a location, and using one of the next or previous keys to cycle through targets, toggle limiting possible targets to those in view only. Default is '{\tt "}'. +\item{\bb{getpos.filter}} +When asked for a location, change the filtering mode when using one of the next or previous keys to cycle through targets. Toggles between no filtering, in view only, and in the same area only. Default is '{\tt "}'. %.lp \item{\bb{getpos.pick}} When asked for a location, the key to choose the location, and possibly ask for more info. Default is ``{\tt .}''. @@ -4528,6 +4545,10 @@ was interrupted. When targeting with cursor, describe the cursor position with coordinates relative to your character. %.lp +\item[\ib{whatis\verb+_+filter:area}] +When targeting with cursor, filter possible locations so only those in +the same area (eg. same room, or same corridor) are considered. +%.lp \item[\ib{nostatus\verb+_+updates}] Prevent updates to the status lines at the bottom of the screen, if your screen-reader reads those lines. The same information can be diff --git a/doc/fixes36.1 b/doc/fixes36.1 index e4a415273..518d16b33 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -600,6 +600,8 @@ for menustyle:Traditional and Combination, support BUCX filtering for item pick-up and container put-in and take-out; also for object IDing for menustyle:Full and Traditional and Combination, support BUCX filtering for the 'A' command +option whatis_filter to set filtering for eligible map locations when cursor + positioning Platform- and/or Interface-Specific New Features diff --git a/include/decl.h b/include/decl.h index 811324abb..b289b83cb 100644 --- a/include/decl.h +++ b/include/decl.h @@ -389,6 +389,14 @@ E char *fqn_prefix_names[PREFIX_COUNT]; E NEARDATA struct savefile_info sfcap, sfrestinfo, sfsaveinfo; +struct opvar { + xchar spovartyp; /* one of SPOVAR_foo */ + union { + char *str; + long l; + } vardata; +}; + struct autopickup_exception { struct nhregex *regex; char *pattern; diff --git a/include/extern.h b/include/extern.h index 10e8ea54a..c92f34106 100644 --- a/include/extern.h +++ b/include/extern.h @@ -384,7 +384,7 @@ E void NDECL(heal_legs); /* ### do_name.c ### */ E char *FDECL(coord_desc, (int, int, char *, CHAR_P)); -E boolean FDECL(getpos_menu, (coord *, BOOLEAN_P, int)); +E boolean FDECL(getpos_menu, (coord *, int)); E int FDECL(getpos, (coord *, BOOLEAN_P, const char *)); E void FDECL(getpos_sethilite, (void (*f)(int))); E void FDECL(new_mname, (struct monst *, int)); @@ -2250,6 +2250,11 @@ E boolean FDECL(dig_corridor, (coord *, coord *, BOOLEAN_P, SCHAR_P, SCHAR_P)); E void FDECL(fill_room, (struct mkroom *, BOOLEAN_P)); E boolean FDECL(load_special, (const char *)); +E xchar FDECL(selection_getpoint, (int, int, struct opvar *)); +E struct opvar *FDECL(selection_opvar, (char *)); +E void FDECL(opvar_free_x, (struct opvar *)); +E void FDECL(set_selection_floodfillchk, (int FDECL((*), (int,int)))); +E void FDECL(selection_floodfill, (struct opvar *, int, int, BOOLEAN_P)); /* ### spell.c ### */ diff --git a/include/flag.h b/include/flag.h index 668f9cce7..f4f4ac69e 100644 --- a/include/flag.h +++ b/include/flag.h @@ -176,6 +176,14 @@ struct sysflag { #define GPCOORDS_COMFULL 'f' #define GPCOORDS_SCREEN 's' +enum getloc_filters { + GFILTER_NONE = 0, + GFILTER_VIEW, + GFILTER_AREA, + + NUM_GFILTER +}; + struct instance_flags { /* stuff that really isn't option or platform related. They are * set and cleared during the game to control the internal @@ -194,7 +202,7 @@ struct instance_flags { #define TER_MON 0x08 #define TER_DETECT 0x10 /* detect_foo magic rather than #terrain */ boolean getloc_travelmode; - boolean getloc_limitview; + int getloc_filter; /* GFILTER_foo */ boolean getloc_usemenu; coord travelcc; /* coordinates for travel_cache */ boolean window_inited; /* true if init_nhwindows() completed */ diff --git a/include/rm.h b/include/rm.h index abcdd8c08..2c68e6515 100644 --- a/include/rm.h +++ b/include/rm.h @@ -235,6 +235,12 @@ enum screen_symbols { #define is_cmap_drawbridge(i) ((i) >= S_vodbridge && (i) <= S_hcdbridge) #define is_cmap_door(i) ((i) >= S_vodoor && (i) <= S_hcdoor) #define is_cmap_wall(i) ((i) >= S_stone && (i) <= S_trwall) +#define is_cmap_room(i) ((i) >= S_room && (i) <= S_darkroom) +#define is_cmap_corr(i) ((i) >= S_corr && (i) <= S_litcorr) +#define is_cmap_furniture(i) ((i) >= S_upstair && (i) <= S_fountain) +#define is_cmap_water(i) ((i) == S_pool || (i) == S_water) +#define is_cmap_lava(i) ((i) == S_lava) + struct symdef { uchar sym; diff --git a/include/sp_lev.h b/include/sp_lev.h index be3e92164..97af34dd6 100644 --- a/include/sp_lev.h +++ b/include/sp_lev.h @@ -256,14 +256,6 @@ enum opcode_defs { #define SP_MAPCHAR_LIT(l) ((((l) >> 8) & 0xffff) - 10) #define SP_MAPCHAR_PACK(typ, lit) (((10 + (lit)) << 8) | ((typ) & 0xff)) -struct opvar { - xchar spovartyp; /* one of SPOVAR_foo */ - union { - char *str; - long l; - } vardata; -}; - struct splev_var { struct splev_var *next; char *name; diff --git a/src/cmd.c b/src/cmd.c index 6d1a0f113..415c77f3a 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -3741,7 +3741,7 @@ struct { { NHKF_GETPOS_INTERESTING_NEXT, 'a', "getpos.all.next" }, { NHKF_GETPOS_INTERESTING_PREV, 'A', "getpos.all.prev" }, { NHKF_GETPOS_HELP, '?', "getpos.help" }, - { NHKF_GETPOS_LIMITVIEW, '"', "getpos.inview" }, + { NHKF_GETPOS_LIMITVIEW, '"', "getpos.filter" }, { NHKF_GETPOS_MENU, '!', "getpos.menu" } }; @@ -5030,10 +5030,14 @@ dotravel(VOID_ARGS) } iflags.getloc_travelmode = TRUE; if (iflags.menu_requested) { - if (!getpos_menu(&cc, TRUE, GLOC_INTERESTING)) { + int gf = iflags.getloc_filter; + iflags.getloc_filter = GFILTER_VIEW; + if (!getpos_menu(&cc, GLOC_INTERESTING)) { + iflags.getloc_filter = gf; iflags.getloc_travelmode = FALSE; return 0; } + iflags.getloc_filter = gf; } else { pline("Where do you want to travel to?"); if (getpos(&cc, TRUE, "the desired destination") < 0) { diff --git a/src/do_name.c b/src/do_name.c index a7db8f4f3..d79968778 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -54,6 +54,11 @@ const char *const gloc_descr[NUM_GLOCS][4] = { "anything interesting" } }; +const char *const gloc_filtertxt[NUM_GFILTER] = { + "", + " in view", + " in this area" +}; void getpos_help_keyxhelp(tmpwin, k1, k2, gloc) @@ -69,7 +74,7 @@ int gloc; iflags.getloc_usemenu ? "get a menu of " : "move the cursor to ", gloc_descr[gloc][2 + iflags.getloc_usemenu], - iflags.getloc_limitview ? " in view" : ""); + gloc_filtertxt[iflags.getloc_filter]); putstr(tmpwin, 0, sbuf); } @@ -125,7 +130,7 @@ const char *goal; visctrl(Cmd.spkeys[NHKF_GETPOS_MENU])); putstr(tmpwin, 0, sbuf); Sprintf(sbuf, - "Use '%s' to toggle limiting possible targets to in view only.", + "Use '%s' to change the mode of limiting possible targets.", visctrl(Cmd.spkeys[NHKF_GETPOS_LIMITVIEW])); putstr(tmpwin, 0, sbuf); if (!iflags.terrainmode) { @@ -214,6 +219,99 @@ const void *b; && glyph_to_cmap(levl[(x)][(y)].glyph) == S_stone \ && !levl[(x)][(y)].seenv) +static struct opvar *gloc_filter_map = (struct opvar *) 0; + +#define GLOC_SAME_AREA(x,y) \ + (isok((x), (y)) \ + && (selection_getpoint((x),(y), gloc_filter_map))) + +static int gloc_filter_floodfill_match_glyph; + +int +gloc_filter_classify_glyph(glyph) +int glyph; +{ + int c; + + if (!glyph_is_cmap(glyph)) + return 0; + + c = glyph_to_cmap(glyph); + + if (is_cmap_room(c) || is_cmap_furniture(c)) + return 1; + else if (is_cmap_wall(c) || c == S_tree) + return 2; + else if (is_cmap_corr(c)) + return 3; + else if (is_cmap_water(c)) + return 4; + else if (is_cmap_lava(c)) + return 5; + return 0; +} + +STATIC_OVL int +gloc_filter_floodfill_matcharea(x,y) +int x,y; +{ + int glyph = back_to_glyph(x, y); + + if (!levl[x][y].seenv) + return FALSE; + + if (glyph == gloc_filter_floodfill_match_glyph) + return TRUE; + + if (gloc_filter_classify_glyph(glyph) == gloc_filter_classify_glyph(gloc_filter_floodfill_match_glyph)) + return TRUE; + + return FALSE; +} + +void +gloc_filter_floodfill(x,y) +int x,y; +{ + gloc_filter_floodfill_match_glyph = back_to_glyph(x,y); + + set_selection_floodfillchk(gloc_filter_floodfill_matcharea); + selection_floodfill(gloc_filter_map, x,y, FALSE); +} + +void +gloc_filter_init() +{ + if (iflags.getloc_filter == GFILTER_AREA) { + if (!gloc_filter_map) { + gloc_filter_map = selection_opvar(NULL); + } + /* special case: if we're in a doorway, try to figure out which + direction we're moving, and use that side of the doorway */ + if (IS_DOOR(levl[u.ux][u.uy].typ)) { + if (u.dx || u.dy) { + gloc_filter_floodfill(u.ux + u.dx, u.uy + u.dy); + } else { + /* TODO: maybe add both sides of the doorway? */ + } + } else { + gloc_filter_floodfill(u.ux, u.uy); + } + + + } +} + +void +gloc_filter_done() +{ + if (gloc_filter_map) { + opvar_free_x(gloc_filter_map); + gloc_filter_map = NULL; + } +} + + STATIC_OVL boolean gather_locs_interesting(x,y, gloc) int x,y, gloc; @@ -223,7 +321,13 @@ int x,y, gloc; */ int glyph = glyph_at(x, y); - if (iflags.getloc_limitview && !cansee(x,y)) + if (iflags.getloc_filter == GFILTER_VIEW && !cansee(x,y)) + return FALSE; + if (iflags.getloc_filter == GFILTER_AREA && !GLOC_SAME_AREA(x,y) + && !GLOC_SAME_AREA(x-1,y) + && !GLOC_SAME_AREA(x,y-1) + && !GLOC_SAME_AREA(x+1,y) + && !GLOC_SAME_AREA(x,y+1)) return FALSE; switch (gloc) { @@ -297,6 +401,9 @@ int gloc; * Hero's spot will always sort to array[0] because it will always * be the shortest distance (namely, 0 units) away from . */ + + gloc_filter_init(); + *cnt_p = idx = 0; for (pass = 0; pass < 2; pass++) { for (x = 1; x < COLNO; x++) @@ -318,6 +425,8 @@ int gloc; else /* end of second pass */ qsort(*arr_p, *cnt_p, sizeof (coord), cmp_coord_distu); } /* pass */ + + gloc_filter_done(); } char * @@ -422,9 +531,8 @@ int cx, cy; } boolean -getpos_menu(ccp, fovonly, gloc) +getpos_menu(ccp, gloc) coord *ccp; -boolean fovonly; int gloc; { coord *garr = DUMMY; @@ -440,7 +548,8 @@ int gloc; if (gcount < 2) { /* gcount always includes the hero */ free((genericptr_t) garr); You("cannot %s %s.", - fovonly ? "see" : "detect", gloc_descr[gloc][0]); + iflags.getloc_filter == GFILTER_VIEW ? "see" : "detect", + gloc_descr[gloc][0]); return FALSE; } @@ -466,7 +575,8 @@ int gloc; } Sprintf(tmpbuf, "Pick a target %s%s%s", - gloc_descr[gloc][1], fovonly ? " in view" : "", + gloc_descr[gloc][1], + gloc_filtertxt[iflags.getloc_filter], iflags.getloc_travelmode ? " for travel" : ""); end_menu(tmpwin, tmpbuf); pick_cnt = select_menu(tmpwin, PICK_ONE, &picks); @@ -646,7 +756,12 @@ const char *goal; msg_given = TRUE; goto nxtc; } else if (c == Cmd.spkeys[NHKF_GETPOS_LIMITVIEW]) { - iflags.getloc_limitview = !iflags.getloc_limitview; + const char *const view_filters[NUM_GFILTER] = { + "Not limiting targets", + "Limiting targets to in sight", + "Limiting targets to in same area" + }; + iflags.getloc_filter = (iflags.getloc_filter + 1) % NUM_GFILTER; for (i = 0; i < NUM_GLOCS; i++) { if (garr[i]) { free((genericptr_t) garr[i]); @@ -654,8 +769,7 @@ const char *goal; } gidx[i] = gcount[i] = 0; } - pline("%s possible targets to those in sight only.", - iflags.getloc_limitview ? "Limiting" : "Not limiting"); + pline("%s.", view_filters[iflags.getloc_filter]); msg_given = TRUE; goto nxtc; } else if (c == Cmd.spkeys[NHKF_GETPOS_MENU]) { @@ -679,7 +793,7 @@ const char *goal; if (iflags.getloc_usemenu) { coord tmpcrd; - if (getpos_menu(&tmpcrd, iflags.getloc_limitview, gloc)) { + if (getpos_menu(&tmpcrd, gloc)) { cx = tmpcrd.x; cy = tmpcrd.y; } diff --git a/src/options.c b/src/options.c index 38d258c6f..e4c878dd0 100644 --- a/src/options.c +++ b/src/options.c @@ -233,7 +233,6 @@ static struct Bool_Opt { { "vt_tiledata", (boolean *) 0, FALSE, SET_IN_FILE }, #endif { "whatis_menu", &iflags.getloc_usemenu, FALSE, SET_IN_GAME }, - { "whatis_inview", &iflags.getloc_limitview, FALSE, SET_IN_GAME }, { "wizweight", &iflags.wizweight, FALSE, SET_IN_WIZGAME }, { "wraptext", &iflags.wc2_wraptext, FALSE, SET_IN_GAME }, #ifdef ZEROCOMP @@ -403,6 +402,8 @@ static struct Comp_Opt { #endif { "whatis_coord", "show coordinates when auto-describing cursor position", 1, SET_IN_GAME }, + { "whatis_filter", "filter coordinate locations when targeting next or previous", + 1, SET_IN_GAME }, { "windowcolors", "the foreground/background colors of windows", /*WC*/ 80, DISP_IN_GAME }, { "windowtype", "windowing system to use", WINTYPELEN, DISP_IN_GAME }, @@ -2379,7 +2380,7 @@ boolean tinitial, tfrom_file; } fullname = "whatis_coord"; - if (match_optname(opts, fullname, 6, TRUE)) { + if (match_optname(opts, fullname, 8, TRUE)) { if (duplicate) complain_about_duplicate(opts, 1); if (negated) { @@ -2399,6 +2400,33 @@ boolean tinitial, tfrom_file; return; } + fullname = "whatis_filter"; + if (match_optname(opts, fullname, 8, TRUE)) { + if (duplicate) + complain_about_duplicate(opts, 1); + if (negated) { + iflags.getloc_filter = GFILTER_NONE; + return; + } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) { + char c = lowc(*op); + + switch (c) { + case 'n': + iflags.getloc_filter = GFILTER_NONE; + break; + case 'v': + iflags.getloc_filter = GFILTER_VIEW; + break; + case 'a': + iflags.getloc_filter = GFILTER_AREA; + break; + default: + badoption(opts); + } + } + return; + } + fullname = "warnings"; if (match_optname(opts, fullname, 5, TRUE)) { if (duplicate) @@ -4283,6 +4311,37 @@ boolean setinitial, setfromfile; free((genericptr_t) window_pick); } destroy_nhwindow(tmpwin); + } else if (!strcmp("whatis_filter", optname)) { + menu_item *window_pick = (menu_item *) 0; + int pick_cnt; + char gf = iflags.getloc_filter; + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + any = zeroany; + any.a_char = (GFILTER_NONE + 1); + add_menu(tmpwin, NO_GLYPH, &any, 'n', + 0, ATR_NONE, "no filtering", + (gf == GFILTER_NONE) ? MENU_SELECTED : MENU_UNSELECTED); + any.a_char = (GFILTER_VIEW + 1); + add_menu(tmpwin, NO_GLYPH, &any, 'v', + 0, ATR_NONE, "in view only", + (gf == GFILTER_VIEW) ? MENU_SELECTED : MENU_UNSELECTED); + any.a_char = (GFILTER_AREA + 1); + add_menu(tmpwin, NO_GLYPH, &any, 'a', + 0, ATR_NONE, "in same area", + (gf == GFILTER_AREA) ? MENU_SELECTED : MENU_UNSELECTED); + end_menu(tmpwin, + "Select location filtering when going for next/previous map position:"); + if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &window_pick)) > 0) { + iflags.getloc_filter = (window_pick[0].item.a_char - 1); + /* PICK_ONE doesn't unselect preselected entry when + selecting another one */ + if (pick_cnt > 1 && iflags.getloc_filter == gf) + iflags.getloc_filter = (window_pick[1].item.a_char - 1); + free((genericptr_t) window_pick); + } + destroy_nhwindow(tmpwin); } else if (!strcmp("msg_window", optname)) { #ifdef TTY_GRAPHICS /* by Christian W. Cooper */ @@ -5057,6 +5116,11 @@ char *buf; : (iflags.getpos_coords == GPCOORDS_COMFULL) ? "full compass" : (iflags.getpos_coords == GPCOORDS_SCREEN) ? "screen" : "none"); + } else if (!strcmp(optname, "whatis_filter")) { + Sprintf(buf, "%s", + (iflags.getloc_filter == GFILTER_VIEW) ? "view" + : (iflags.getloc_filter == GFILTER_AREA) ? "area" + : "none"); } else if (!strcmp(optname, "scores")) { Sprintf(buf, "%d top/%d around%s", flags.end_top, flags.end_around, flags.end_own ? "/own" : ""); diff --git a/src/sp_lev.c b/src/sp_lev.c index 9ef1dcf46..448907f63 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -36,7 +36,6 @@ STATIC_DCL struct opvar *FDECL(opvar_new_coord, (int, int)); #if 0 STATIC_DCL struct opvar * FDECL(opvar_new_region, (int,int, int,int)); #endif /*0*/ -STATIC_DCL void FDECL(opvar_free_x, (struct opvar *)); STATIC_DCL struct opvar *FDECL(opvar_clone, (struct opvar *)); STATIC_DCL struct opvar *FDECL(opvar_var_conversion, (struct sp_coder *, struct opvar *)); @@ -114,8 +113,6 @@ STATIC_DCL void FDECL(spo_altar, (struct sp_coder *)); STATIC_DCL void FDECL(spo_trap, (struct sp_coder *)); STATIC_DCL void FDECL(spo_gold, (struct sp_coder *)); STATIC_DCL void FDECL(spo_corridor, (struct sp_coder *)); -STATIC_DCL struct opvar *FDECL(selection_opvar, (char *)); -STATIC_DCL xchar FDECL(selection_getpoint, (int, int, struct opvar *)); STATIC_DCL void FDECL(selection_setpoint, (int, int, struct opvar *, XCHAR_P)); STATIC_DCL struct opvar *FDECL(selection_not, (struct opvar *)); STATIC_DCL struct opvar *FDECL(selection_logical_oper, (struct opvar *, @@ -126,11 +123,8 @@ STATIC_DCL void FDECL(selection_filter_percent, (struct opvar *, int)); STATIC_DCL int FDECL(selection_rndcoord, (struct opvar *, schar *, schar *, BOOLEAN_P)); STATIC_DCL void FDECL(selection_do_grow, (struct opvar *, int)); -STATIC_DCL void FDECL(set_selection_floodfillchk, (int FDECL((*), (int,int)))); STATIC_DCL int FDECL(floodfillchk_match_under, (int, int)); STATIC_DCL int FDECL(floodfillchk_match_accessible, (int, int)); -STATIC_DCL void FDECL(selection_floodfill, (struct opvar *, int, int, - BOOLEAN_P)); STATIC_DCL void FDECL(selection_do_ellipse, (struct opvar *, int, int, int, int, int)); STATIC_DCL long FDECL(line_dist_coord, (long, long, long, long, long, long)); @@ -3776,7 +3770,7 @@ int dir; STATIC_VAR int FDECL((*selection_flood_check_func), (int, int)); STATIC_VAR schar floodfillchk_match_under_typ; -STATIC_OVL void +void set_selection_floodfillchk(f) int FDECL((*f), (int, int)); { @@ -3799,7 +3793,7 @@ int x, y; || levl[x][y].typ == SCORR); } -STATIC_OVL void +void selection_floodfill(ov, x, y, diagonals) struct opvar *ov; int x, y; From b13bae91dc14157d6a38f7d09250c973389bceb5 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 31 Jul 2017 19:10:26 +0300 Subject: [PATCH 21/39] Add way to cycle through valid locations for polearm or jump target --- doc/Guidebook.mn | 4 ++++ doc/Guidebook.tex | 6 ++++++ doc/fixes36.1 | 4 ++++ include/extern.h | 2 +- include/flag.h | 3 +++ src/apply.c | 30 ++++++++++++++++++++++-------- src/cmd.c | 2 ++ src/do_name.c | 24 ++++++++++++++++++++---- src/read.c | 15 ++++++++++++--- 9 files changed, 74 insertions(+), 16 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 0de4d48e2..7b70d6dfa 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -3279,6 +3279,10 @@ When asked for a location, the key to go to next closest unexplored location. De When asked for a location, the key to go to previous closest unexplored location. Default is 'X'. .lp getpos.valid When asked for a location, the key to go to show valid target locations. Default is '$'. +.lp getpos.valid.next +When asked for a location, the key to go to next closest valid location. Default is 'z'. +.lp getpos.valid.prev +When asked for a location, the key to go to previous closest valid location. Default is 'Z'. .lp nopickup Prefix key to move without picking up items. Default is 'm'. .lp redraw diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index c33e5b095..7162b909c 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -4018,6 +4018,12 @@ When asked for a location, the key to go to previous closest unexplored location \item{\bb{getpos.valid}} When asked for a location, the key to go to show valid target locations. Default is ``{\tt \$}''. %.lp +\item{\bb{getpos.valid.next}} +When asked for a location, the key to go to next closest valid location. Default is ``{\tt z}''. +%.lp +\item{\bb{getpos.valid.prev}} +When asked for a location, the key to go to previous closest valid location. Default is ``{\tt Z}''. +%.lp \item{\bb{nopickup}} Prefix key to move without picking up items. Default is ``{\tt m}''. %.lp diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 518d16b33..73803752a 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -592,6 +592,10 @@ random horses have a tiny chance of being generated saddled give feedback just before timed levitation runs out travel accepts 'm' (request menu) prefix pressing a or A when cursor positioning shows menu of "interesting" features +pressing z or Z when cursor positioning cycles through valid locations for + jumping, hitting with polearm, or casting a stinking cloud +when moving a cursor for a jump, polearm, or stinking cloud targeting, show + if the location is illegal, if "autodescribe" is on wizard-mode command #wizmakemap to recreate the current level 'goldX' boolean option to treat gold pieces as X (vs U) during BUCX filtering (should be persistent but is reset each save/restore cycle in order diff --git a/include/extern.h b/include/extern.h index c92f34106..36252bf46 100644 --- a/include/extern.h +++ b/include/extern.h @@ -386,7 +386,7 @@ E void NDECL(heal_legs); E char *FDECL(coord_desc, (int, int, char *, CHAR_P)); E boolean FDECL(getpos_menu, (coord *, int)); E int FDECL(getpos, (coord *, BOOLEAN_P, const char *)); -E void FDECL(getpos_sethilite, (void (*f)(int))); +E void FDECL(getpos_sethilite, (void (*f)(int), boolean (*d)(int,int))); E void FDECL(new_mname, (struct monst *, int)); E void FDECL(free_mname, (struct monst *)); E void FDECL(new_oname, (struct obj *, int)); diff --git a/include/flag.h b/include/flag.h index f4f4ac69e..6d5e438b5 100644 --- a/include/flag.h +++ b/include/flag.h @@ -491,6 +491,8 @@ enum nh_keyfunc { NHKF_GETPOS_UNEX_PREV, NHKF_GETPOS_INTERESTING_NEXT, NHKF_GETPOS_INTERESTING_PREV, + NHKF_GETPOS_VALID_NEXT, + NHKF_GETPOS_VALID_PREV, NHKF_GETPOS_HELP, NHKF_GETPOS_MENU, NHKF_GETPOS_LIMITVIEW, @@ -504,6 +506,7 @@ enum gloctypes { GLOC_DOOR, GLOC_EXPLORE, GLOC_INTERESTING, + GLOC_VALID, NUM_GLOCS }; diff --git a/src/apply.c b/src/apply.c index 546d2cefd..fca3f8e36 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1564,6 +1564,15 @@ boolean showmsg; static int jumping_is_magic; +boolean +get_valid_jump_position(x,y) +int x,y; +{ + return (isok(x, y) + && (ACCESSIBLE(levl[x][y].typ) || Passes_walls) + && is_valid_jump_pos(x, y, jumping_is_magic, FALSE)); +} + void display_jump_positions(state) int state; @@ -1577,9 +1586,7 @@ int state; for (dy = -4; dy <= 4; dy++) { x = dx + (int) u.ux; y = dy + (int) u.uy; - if (isok(x, y) - && (ACCESSIBLE(levl[x][y].typ) || Passes_walls) - && is_valid_jump_pos(x, y, jumping_is_magic, FALSE)) + if (get_valid_jump_position(x, y)) tmp_at(x, y); } } else { @@ -1678,7 +1685,7 @@ int magic; /* 0=Physical, otherwise skill level */ cc.x = u.ux; cc.y = u.uy; jumping_is_magic = magic; - getpos_sethilite(display_jump_positions); + getpos_sethilite(display_jump_positions, get_valid_jump_position); if (getpos(&cc, TRUE, "the desired position") < 0) return 0; /* user pressed ESC */ if (!is_valid_jump_pos(cc.x, cc.y, magic, TRUE)) { @@ -2851,6 +2858,15 @@ int min_range, max_range; static int polearm_range_min = -1; static int polearm_range_max = -1; +boolean +get_valid_polearm_position(x,y) +int x,y; +{ + return (isok(x, y) && ACCESSIBLE(levl[x][y].typ) + && distu(x, y) >= polearm_range_min + && distu(x, y) <= polearm_range_max); +} + void display_polearm_positions(state) int state; @@ -2864,9 +2880,7 @@ int state; for (dy = -4; dy <= 4; dy++) { x = dx + (int) u.ux; y = dy + (int) u.uy; - if (isok(x, y) && ACCESSIBLE(levl[x][y].typ) - && distu(x, y) >= polearm_range_min - && distu(x, y) <= polearm_range_max) { + if (get_valid_polearm_position(x, y)) { tmp_at(x, y); } } @@ -2936,7 +2950,7 @@ struct obj *obj; cc.x = hitm->mx; cc.y = hitm->my; } - getpos_sethilite(display_polearm_positions); + getpos_sethilite(display_polearm_positions, get_valid_polearm_position); if (getpos(&cc, TRUE, "the spot to hit") < 0) return res; /* ESC; uses turn iff polearm became wielded */ diff --git a/src/cmd.c b/src/cmd.c index 415c77f3a..719c4902e 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -3738,6 +3738,8 @@ struct { { NHKF_GETPOS_DOOR_PREV, 'D', "getpos.door.prev" }, { NHKF_GETPOS_UNEX_NEXT, 'x', "getpos.unexplored.next" }, { NHKF_GETPOS_UNEX_PREV, 'X', "getpos.unexplored.prev" }, + { NHKF_GETPOS_VALID_NEXT, 'z', "getpos.valid.next" }, + { NHKF_GETPOS_VALID_PREV, 'Z', "getpos.valid.prev" }, { NHKF_GETPOS_INTERESTING_NEXT, 'a', "getpos.all.next" }, { NHKF_GETPOS_INTERESTING_PREV, 'A', "getpos.all.prev" }, { NHKF_GETPOS_HELP, '?', "getpos.help" }, diff --git a/src/do_name.c b/src/do_name.c index d79968778..16818c0d9 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -36,12 +36,15 @@ nextmbuf() * parameter value 0 = initialize, 1 = highlight, 2 = done */ static void FDECL((*getpos_hilitefunc), (int)) = (void FDECL((*), (int))) 0; +static boolean FDECL((*getpos_getvalid), (int,int)) = (boolean FDECL((*), (int,int))) 0; void -getpos_sethilite(f) +getpos_sethilite(f, d) void FDECL((*f), (int)); +boolean FDECL((*d), (int,int)); { getpos_hilitefunc = f; + getpos_getvalid = d; } const char *const gloc_descr[NUM_GLOCS][4] = { @@ -135,6 +138,12 @@ const char *goal; putstr(tmpwin, 0, sbuf); if (!iflags.terrainmode) { char kbuf[BUFSZ]; + if (getpos_getvalid) { + Sprintf(sbuf, "Use '%s' or '%s' to move to valid locations.", + visctrl(Cmd.spkeys[NHKF_GETPOS_VALID_NEXT]), + visctrl(Cmd.spkeys[NHKF_GETPOS_VALID_PREV])); + putstr(tmpwin, 0, sbuf); + } if (getpos_hilitefunc) { Sprintf(sbuf, "Use '%s' to display valid locations.", visctrl(Cmd.spkeys[NHKF_GETPOS_SHOWVALID])); @@ -376,6 +385,8 @@ int x,y, gloc; || glyph_to_cmap(glyph) == S_darkroom || glyph_to_cmap(glyph) == S_corr || glyph_to_cmap(glyph) == S_litcorr)); + case GLOC_VALID: + return (getpos_getvalid && getpos_getvalid(x,y)); } /*NOTREACHED*/ return FALSE; @@ -522,7 +533,9 @@ int cx, cy; if (do_screen_description(cc, TRUE, sym, tmpbuf, &firstmatch)) { (void) coord_desc(cx, cy, tmpbuf, iflags.getpos_coords); custompline(SUPPRESS_HISTORY, - "%s%s%s%s", firstmatch, *tmpbuf ? " " : "", tmpbuf, + "%s%s%s%s%s", firstmatch, *tmpbuf ? " " : "", tmpbuf, + (iflags.autodescribe && getpos_getvalid && !getpos_getvalid(cx,cy)) + ? " (illegal)" : "", (iflags.getloc_travelmode && !is_valid_travelpt(cx, cy)) ? " (no travel path)" : ""); curs(WIN_MAP, cx, cy); @@ -615,10 +628,12 @@ const char *goal; NHKF_GETPOS_UNEX_NEXT, NHKF_GETPOS_UNEX_PREV, NHKF_GETPOS_INTERESTING_NEXT, - NHKF_GETPOS_INTERESTING_PREV + NHKF_GETPOS_INTERESTING_PREV, + NHKF_GETPOS_VALID_NEXT, + NHKF_GETPOS_VALID_PREV }; char pick_chars[6]; - char mMoOdDxX[11]; + char mMoOdDxX[13]; int result = 0; int cx, cy, i, c; int sidx, tx, ty; @@ -922,6 +937,7 @@ const char *goal; if (garr[i]) free((genericptr_t) garr[i]); getpos_hilitefunc = (void FDECL((*), (int))) 0; + getpos_getvalid = (boolean FDECL((*), (int,int))) 0; return result; } diff --git a/src/read.c b/src/read.c index efc36e3cb..46f838426 100644 --- a/src/read.c +++ b/src/read.c @@ -915,12 +915,21 @@ struct obj *sobj; return 0; } +boolean +get_valid_stinking_cloud_pos(x,y) +int x,y; +{ + return (!(!isok(x,y) || !cansee(x, y) + || !ACCESSIBLE(levl[x][y].typ) + || distu(x, y) >= 32)); +} + boolean is_valid_stinking_cloud_pos(x, y, showmsg) int x, y; boolean showmsg; { - if (!cansee(x, y) || !ACCESSIBLE(levl[x][y].typ) || distu(x, y) >= 32) { + if (get_valid_stinking_cloud_pos(x,y)) { if (showmsg) You("smell rotten eggs."); return FALSE; @@ -942,7 +951,7 @@ int state; for (dy = -dist; dy <= dist; dy++) { x = u.ux + dx; y = u.uy + dy; - if (isok(x, y) && is_valid_stinking_cloud_pos(x, y, FALSE)) + if (get_valid_stinking_cloud_pos(x,y)) tmp_at(x, y); } } else { @@ -1633,7 +1642,7 @@ struct obj *sobj; /* scroll, or fake spellbook object for scroll-like spell */ already_known ? "stinking " : ""); cc.x = u.ux; cc.y = u.uy; - getpos_sethilite(display_stinking_cloud_positions); + getpos_sethilite(display_stinking_cloud_positions, get_valid_stinking_cloud_pos); if (getpos(&cc, TRUE, "the desired position") < 0) { pline1(Never_mind); break; From b4e4d700081697f2f65558dd6fb1c170a7737599 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 2 Aug 2017 19:48:44 +0300 Subject: [PATCH 22/39] Fix couple static code analyzer warnings --- src/artifact.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/artifact.c b/src/artifact.c index bfd576ff4..13e8be981 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -498,7 +498,7 @@ long wp_mask; for (obj = invent; obj; obj = obj->nobj) { if (obj != otmp && obj->oartifact) { art = get_artifact(obj); - if (art->cary.adtyp == dtyp) { + if (art && art->cary.adtyp == dtyp) { mask = (long *) 0; break; } @@ -519,7 +519,8 @@ long wp_mask; for (obj = invent; obj; obj = obj->nobj) if (obj != otmp && obj->oartifact) { art = get_artifact(obj); - spfx &= ~art->cspfx; + if (art) + spfx &= ~art->cspfx; } } From 64f284dd61a77d596ed7567f7098686b32bf354a Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 2 Aug 2017 18:23:42 -0700 Subject: [PATCH 23/39] USE_OLDARGS update (1 of 2) Files modified: include/tradstdc.h, sp_lev.h, system.h util/lev_main.c Silence a bunch of warnings generated by recent gcc which weren't there with whatever version I had when 3.6.0 was being readied for release. For lev_main, there were two basic types: not enough arguments in calls to lc_pline, lc_warning, and lc_error (since we weren't passing dummy arguments as is done for add_opvars), and conversion from 'int' or narrower to 'char *' (from -Wint-to-pointer-cast, which either wasn't there yet in the older gcc, or wasn't included in -Wall back then). [Note that for any configuration decrepit enough to actually need USE_OLDARGS, such conversions will either work fine or else nethack simply won't be viable.] src/pline.c generates a bunch of warnings (for USE_OLDARGS). The fix for that will be (2 of 2). To test, instead of mucking about with CFLAGS or sys/unix/hints, I've been temporarily adding unconditional |#undef USE_STDARG |#undef USE_VARARGS |#define USE_OLDARGS to the end of config1.h and then doing my normal build--which is why -Wall (or possibly -W) is drawing -Wint-to-pointer-cast warnings. --- include/sp_lev.h | 64 ++++++++++++++++-------------------- include/system.h | 11 ++++++- include/tradstdc.h | 24 +++++++++----- util/lev_main.c | 81 +++++++++++++++++++++++++++------------------- 4 files changed, 100 insertions(+), 80 deletions(-) diff --git a/include/sp_lev.h b/include/sp_lev.h index 97af34dd6..9befcd669 100644 --- a/include/sp_lev.h +++ b/include/sp_lev.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 sp_lev.h $NHDT-Date: 1470212260 2016/08/03 08:17:40 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.17 $ */ +/* NetHack 3.6 sp_lev.h $NHDT-Date: 1501723399 2017/08/03 01:23:19 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.20 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -484,61 +484,51 @@ struct lc_breakdef { #ifdef SPEC_LEV /* compiling lev_comp rather than nethack */ #ifdef USE_OLDARGS -#undef VA_ARGS -#undef VA_DECL -#undef VA_DECL2 -#undef VA_SHIFT +#ifndef VA_TYPE +typedef const char *vA; +#define VA_TYPE +#endif +#undef VA_ARGS /* redefine with the maximum number actually used */ +#undef VA_SHIFT /* ditto */ #define VA_ARGS \ arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, \ arg12, arg13, arg14 -#define VA_DECL(typ1, var1) \ - (var1, VA_ARGS) typ1 var1; \ - char *arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7, *arg8, *arg9, \ - *arg10, *arg11, *arg12, *arg13, *arg14; \ - { -#define VA_DECL2(typ1, var1, typ2, var2) \ - (var1, var2, VA_ARGS) typ1 var1; \ - typ2 var2; \ - char *arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7, *arg8, *arg9, \ - *arg10, *arg11, *arg12, *arg13, *arg14; \ - { -/* unlike in the core, lev_comp's VA_SHIFT is completely safe, - because callers always pass all these arguments */ +/* Unlike in the core, lev_comp's VA_SHIFT should be completely safe, + because callers always pass all these arguments. */ #define VA_SHIFT() \ (arg1 = arg2, arg2 = arg3, arg3 = arg4, arg4 = arg5, arg5 = arg6, \ arg6 = arg7, arg7 = arg8, arg8 = arg9, arg9 = arg10, arg10 = arg11, \ arg11 = arg12, arg12 = arg13, arg13 = arg14, arg14 = 0) /* standard NULL may be either (void *)0 or plain 0, both of which would need to be explicitly cast to (char *) here */ -typedef char *Va; #define VA_PASS1(a1) \ - (Va) a1, (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0, \ - (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0 + (vA) a1, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, \ + (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0 #define VA_PASS2(a1, a2) \ - (Va) a1, (Va) a2, (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0, \ - (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0 + (vA) a1, (vA) a2, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, \ + (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0 #define VA_PASS3(a1, a2, a3) \ - (Va) a1, (Va) a2, (Va) a3, (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0, \ - (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0 + (vA) a1, (vA) a2, (vA) a3, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, \ + (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0 #define VA_PASS4(a1, a2, a3, a4) \ - (Va) a1, (Va) a2, (Va) a3, (Va) a4, (Va) 0, (Va) 0, (Va) 0, (Va) 0, \ - (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0 + (vA) a1, (vA) a2, (vA) a3, (vA) a4, (vA) 0, (vA) 0, (vA) 0, (vA) 0, \ + (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0 #define VA_PASS5(a1, a2, a3, a4, a5) \ - (Va) a1, (Va) a2, (Va) a3, (Va) a4, (Va) a5, (Va) 0, (Va) 0, (Va) 0, \ - (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0 + (vA) a1, (vA) a2, (vA) a3, (vA) a4, (vA) a5, (vA) 0, (vA) 0, (vA) 0, \ + (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0 #define VA_PASS7(a1, a2, a3, a4, a5, a6, a7) \ - (Va) a1, (Va) a2, (Va) a3, (Va) a4, (Va) a5, (Va) a6, (Va) a7, (Va) 0, \ - (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0 + (vA) a1, (vA) a2, (vA) a3, (vA) a4, (vA) a5, (vA) a6, (vA) a7, (vA) 0, \ + (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0 #define VA_PASS8(a1, a2, a3, a4, a5, a6, a7, a8) \ - (Va) a1, (Va) a2, (Va) a3, (Va) a4, (Va) a5, (Va) a6, (Va) a7, (Va) a8, \ - (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0 + (vA) a1, (vA) a2, (vA) a3, (vA) a4, (vA) a5, (vA) a6, (vA) a7, (vA) a8, \ + (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0 #define VA_PASS9(a1, a2, a3, a4, a5, a6, a7, a8, a9) \ - (Va) a1, (Va) a2, (Va) a3, (Va) a4, (Va) a5, (Va) a6, (Va) a7, (Va) a8, \ - (Va) a9, (Va) 0, (Va) 0, (Va) 0, (Va) 0, (Va) 0 + (vA) a1, (vA) a2, (vA) a3, (vA) a4, (vA) a5, (vA) a6, (vA) a7, (vA) a8, \ + (vA) a9, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0 #define VA_PASS14(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, \ a14) \ - (Va) a1, (Va) a2, (Va) a3, (Va) a4, (Va) a5, (Va) a6, (Va) a7, (Va) a8, \ - (Va) a9, (Va) a10, (Va) a11, (Va) a12, (Va) a13, (Va) a14 + (vA) a1, (vA) a2, (vA) a3, (vA) a4, (vA) a5, (vA) a6, (vA) a7, (vA) a8, \ + (vA) a9, (vA) a10, (vA) a11, (vA) a12, (vA) a13, (vA) a14 #else /*!USE_OLDARGS*/ /* USE_STDARG and USE_VARARGS don't need to pass dummy arguments or cast real ones */ diff --git a/include/system.h b/include/system.h index 21ccf8b72..f7a3d2606 100644 --- a/include/system.h +++ b/include/system.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 system.h $NHDT-Date: 1449269772 2015/12/04 22:56:12 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.12 $ */ +/* NetHack 3.6 system.h $NHDT-Date: 1501723401 2017/08/03 01:23:21 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.13 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -489,8 +489,17 @@ E int FDECL(vprintf, (const char *, va_list)); #endif #endif #else +#ifdef vprintf +#undef vprintf +#endif #define vprintf printf +#ifdef vfprintf +#undef vfprintf +#endif #define vfprintf fprintf +#ifdef vsprintf +#undef vsprintf +#endif #define vsprintf sprintf #endif #endif /* NEED_VARARGS */ diff --git a/include/tradstdc.h b/include/tradstdc.h index 26ce81ad5..ddd7d2037 100644 --- a/include/tradstdc.h +++ b/include/tradstdc.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 tradstdc.h $NHDT-Date: 1448210011 2015/11/22 16:33:31 $ $NHDT-Branch: master $:$NHDT-Revision: 1.27 $ */ +/* NetHack 3.6 tradstdc.h $NHDT-Date: 1501723401 2017/08/03 01:23:21 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.28 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -128,15 +128,23 @@ #else /*USE_OLDARGS*/ +/* + * CAVEAT: passing double (including float promoted to double) will + * almost certainly break this, as would any integer type bigger than + * sizeof (char *). + * NetHack avoids floating point, and any configuration able to use + * 'long long int' or I64P32 or the like should be using USE_STDARG. + */ +#ifndef VA_TYPE +typedef const char *vA; +#define VA_TYPE +#endif #define VA_ARGS arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 #define VA_DECL(typ1, var1) \ - (var1, VA_ARGS) typ1 var1; \ - char *arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7, *arg8, *arg9; \ + (var1, VA_ARGS) typ1 var1; vA VA_ARGS; \ { #define VA_DECL2(typ1, var1, typ2, var2) \ - (var1, var2, VA_ARGS) typ1 var1; \ - typ2 var2; \ - char *arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7, *arg8, *arg9; \ + (var1, var2, VA_ARGS) typ1 var1; typ2 var2; vA VA_ARGS; \ { #define VA_START(x) #define VA_INIT(var1, typ1) @@ -151,7 +159,7 @@ */ #define VA_SHIFT() \ (arg1 = arg2, arg2 = arg3, arg3 = arg4, arg4 = arg5, arg5 = arg6, \ - arg6 = arg7, arg7 = arg8, arg8 = arg9) + arg6 = arg7, arg7 = arg8, arg8 = arg9, arg9 = 0) #define VA_NEXT(var1, typ1) ((var1 = (typ1) arg1), VA_SHIFT(), var1) #define VA_END() } #endif @@ -380,7 +388,7 @@ typedef genericptr genericptr_t; /* (void *) or (char *) */ * append this to a prototype declaration (see pline() in extern.h). */ #ifdef __GNUC__ -#if __GNUC__ >= 2 +#if (__GNUC__ >= 2) && !defined(USE_OLDARGS) #define PRINTF_F(f, v) __attribute__((format(printf, f, v))) #endif #if __GNUC__ >= 3 diff --git a/util/lev_main.c b/util/lev_main.c index 773c9480b..ecf7a444c 100644 --- a/util/lev_main.c +++ b/util/lev_main.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 lev_main.c $NHDT-Date: 1501461281 2017/07/31 00:34:41 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.46 $ */ +/* NetHack 3.6 lev_main.c $NHDT-Date: 1501723418 2017/08/03 01:23:38 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.47 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -281,7 +281,7 @@ char **argv; } fin = freopen(fname, "r", stdin); if (!fin) { - lc_pline("Can't open \"%s\" for input.\n", fname); + lc_pline("Can't open \"%s\" for input.\n", VA_PASS1(fname)); perror(fname); errors_encountered = TRUE; } else { @@ -644,56 +644,56 @@ VA_DECL2(sp_lev *, sp, const char *, fmt) case 'i': /* integer */ { struct opvar *ov = New(struct opvar); - set_opvar_int(ov, VA_NEXT(la, long) ); + set_opvar_int(ov, VA_NEXT(la, long)); add_opcode(sp, SPO_PUSH, ov); break; } case 'c': /* coordinate */ { struct opvar *ov = New(struct opvar); - set_opvar_coord(ov, VA_NEXT(la, long) ); + set_opvar_coord(ov, VA_NEXT(la, long)); add_opcode(sp, SPO_PUSH, ov); break; } case 'r': /* region */ { struct opvar *ov = New(struct opvar); - set_opvar_region(ov, VA_NEXT(la, long) ); + set_opvar_region(ov, VA_NEXT(la, long)); add_opcode(sp, SPO_PUSH, ov); break; } case 'm': /* mapchar */ { struct opvar *ov = New(struct opvar); - set_opvar_mapchar(ov, VA_NEXT(la, long) ); + set_opvar_mapchar(ov, VA_NEXT(la, long)); add_opcode(sp, SPO_PUSH, ov); break; } case 'M': /* monster */ { struct opvar *ov = New(struct opvar); - set_opvar_monst(ov, VA_NEXT(la, long) ); + set_opvar_monst(ov, VA_NEXT(la, long)); add_opcode(sp, SPO_PUSH, ov); break; } case 'O': /* object */ { struct opvar *ov = New(struct opvar); - set_opvar_obj(ov, VA_NEXT(la, long) ); + set_opvar_obj(ov, VA_NEXT(la, long)); add_opcode(sp, SPO_PUSH, ov); break; } case 's': /* string */ { struct opvar *ov = New(struct opvar); - set_opvar_str(ov, VA_NEXT(lp, const char *) ); + set_opvar_str(ov, VA_NEXT(lp, const char *)); add_opcode(sp, SPO_PUSH, ov); break; } case 'v': /* variable */ { struct opvar *ov = New(struct opvar); - set_opvar_var(ov, VA_NEXT(lp, const char *) ); + set_opvar_var(ov, VA_NEXT(lp, const char *)); add_opcode(sp, SPO_PUSH, ov); break; } @@ -701,12 +701,13 @@ VA_DECL2(sp_lev *, sp, const char *, fmt) { long i = VA_NEXT(la, int); if (i < 0 || i >= MAX_SP_OPCODES) - lc_pline("add_opvars: unknown opcode '%ld'.", i); + lc_pline("add_opvars: unknown opcode '%ld'.", VA_PASS1(i)); add_opcode(sp, i, NULL); break; } default: - lc_pline("add_opvars: illegal format character '%c'.", *p); + lc_pline("add_opvars: illegal format character '%ld'.", + VA_PASS1((long) *p)); break; } } @@ -774,7 +775,8 @@ char *name; struct lc_funcdefs *f = New(struct lc_funcdefs); if (!f) { - lc_error("Could not alloc function definition for '%s'.", name); + lc_error("Could not alloc function definition for '%s'.", + VA_PASS1(name)); return NULL; } f->next = NULL; @@ -855,7 +857,8 @@ char *name; struct lc_vardefs *f = New(struct lc_vardefs); if (!f) { - lc_error("Could not alloc variable definition for '%s'.", name); + lc_error("Could not alloc variable definition for '%s'.", + VA_PASS1(name)); return NULL; } f->next = NULL; @@ -874,7 +877,7 @@ struct lc_vardefs *fchain; while (tmp) { if (be_verbose && (tmp->n_used == 0)) - lc_warning("Unused variable '%s'", tmp->name); + lc_warning("Unused variable '%s'", VA_PASS1(tmp->name)); nxt = tmp->next; Free(tmp->name); Free(tmp); @@ -913,7 +916,7 @@ long spovar; spovar &= ~SPOVAR_ARRAY; switch (spovar) { default: - lc_error("spovar2str(%ld)", spovar); + lc_error("spovar2str(%ld)", VA_PASS1(spovar)); break; case SPOVAR_INT: n = "integer"; @@ -969,9 +972,11 @@ long vartype; if ((tmp = vardef_defined(vd, varname, 1)) != 0) { if (tmp->var_type != vartype) lc_error("Trying to use variable '%s' as %s, when it is %s.", - varname, spovar2str(vartype), spovar2str(tmp->var_type)); + VA_PASS3(varname, + spovar2str(vartype), + spovar2str(tmp->var_type))); } else - lc_error("Variable '%s' not defined.", varname); + lc_error("Variable '%s' not defined.", VA_PASS1(varname)); } struct lc_vardefs * @@ -985,7 +990,9 @@ long vartype; if ((tmp = vardef_defined(vd, varname, 1)) != 0) { if (tmp->var_type != vartype) lc_error("Trying to redefine variable '%s' as %s, when it is %s.", - varname, spovar2str(vartype), spovar2str(tmp->var_type)); + VA_PASS3(varname, + spovar2str(vartype), + spovar2str(tmp->var_type))); } else { tmp = vardef_new(vartype, varname); tmp->next = vd; @@ -1012,7 +1019,8 @@ int opcode; case SPO_JGE: return SPO_JL; default: - lc_error("Cannot reverse comparison jmp opcode %d.", opcode); + lc_error("Cannot reverse comparison jmp opcode %ld.", + VA_PASS1((long) opcode)); return SPO_NULL; } } @@ -1054,7 +1062,8 @@ struct opvar *ov; tmpov->vardata.str[len] = '\0'; } break; default: { - lc_error("Unknown opvar_clone value type (%d)!", ov->spovartyp); + lc_error("Unknown opvar_clone value type (%ld)!", + VA_PASS1((long) ov->spovartyp)); } /* default */ } /* switch */ return tmpov; @@ -1083,9 +1092,10 @@ char *ldfname; struct lc_funcdefs *f; if (index(ldfname, '.')) - lc_error("Invalid dot ('.') in level name '%s'.", ldfname); + lc_error("Invalid dot ('.') in level name '%s'.", VA_PASS1(ldfname)); if ((int) strlen(ldfname) > 14) - lc_error("Level names limited to 14 characters ('%s').", ldfname); + lc_error("Level names limited to 14 characters ('%s').", + VA_PASS1(ldfname)); f = function_definitions; while (f) { f->n_called = 0; @@ -1173,8 +1183,8 @@ char c; if (!class || class == mons[i].mlet) if (!case_insensitive_comp(s, mons[i].mname)) { if (be_verbose) - lc_warning("Monster type \"%s\" matches \"%s\".", s, - mons[i].mname); + lc_warning("Monster type \"%s\" matches \"%s\".", + VA_PASS2(s, mons[i].mname)); return i; } return ERR; @@ -1209,7 +1219,8 @@ char c; /* class */ objname = obj_descr[i].oc_name; if (objname && !case_insensitive_comp(s, objname)) { if (be_verbose) - lc_warning("Object type \"%s\" matches \"%s\".", s, objname); + lc_warning("Object type \"%s\" matches \"%s\".", + VA_PASS2(s, objname)); return i; } } @@ -1318,14 +1329,14 @@ genericptr_t dat; _opcode *tmp; if ((opc < 0) || (opc >= MAX_SP_OPCODES)) - lc_error("Unknown opcode '%d'", opc); + lc_error("Unknown opcode '%ld'", VA_PASS1((long) opc)); tmp = (_opcode *) alloc(sizeof(_opcode) * (nop + 1)); if (!tmp) { /* lint suppression */ /*NOTREACHED*/ #if 0 /* not possible; alloc() never returns Null */ - lc_error("Could not alloc opcode space"); + lc_error("%s", VA_PASS1("Could not alloc opcode space")); #endif return; } @@ -1396,9 +1407,9 @@ sp_lev *sp; for (i = 0; i < len; i++) if ((tmpmap[max_hig][i] = what_map_char(map[i])) == INVALID_TYPE) { - lc_warning("Invalid character '%c' @ (%d, %d) - replacing " - "with stone", - map[i], max_hig, i); + lc_warning( + "Invalid character '%ld' @ (%ld, %ld) - replacing with stone", + VA_PASS3((long) map[i], (long) max_hig, (long) i)); tmpmap[max_hig][i] = STONE; } while (i < max_len) @@ -1413,8 +1424,9 @@ sp_lev *sp; max_y_map = max_hig - 1; if (max_len > MAP_X_LIM || max_hig > MAP_Y_LIM) { - lc_error("Map too large at (%d x %d), max is (%d x %d)", max_len, - max_hig, MAP_X_LIM, MAP_Y_LIM); + lc_error("Map too large at (%ld x %ld), max is (%ld x %ld)", + VA_PASS4((long) max_len, (long) max_hig, + (long) MAP_X_LIM, (long) MAP_Y_LIM)); } mbuf = (char *) alloc(((max_hig - 1) * max_len) + (max_len - 1) + 2); @@ -1424,7 +1436,8 @@ sp_lev *sp; mbuf[((max_hig - 1) * max_len) + (max_len - 1) + 1] = '\0'; - add_opvars(sp, "siio", VA_PASS4(mbuf, max_hig, max_len, SPO_MAP)); + add_opvars(sp, "siio", VA_PASS4(mbuf, (long) max_hig, (long) max_len, + SPO_MAP)); for (dy = 0; dy < max_hig; dy++) Free(tmpmap[dy]); From 3b675c5b49b6a8be6558f4ab4986136ce318234c Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 2 Aug 2017 18:56:54 -0700 Subject: [PATCH 24/39] USE_OLDARGS update (2 of 2) Files modified: include/extern.h src/pline.c, priest.c, potion.c, mkobj.c A bunch of calls to pline() in pline.c started triggering warnings either as-is or possibly after the changes to tradstdc.h. Fixing them in place would include intrusive VA_PASSx() like in lev_main.c. Moving them to other files is much simpler (and they didn't particularly belong in pline.c in the first place, although I didn't actually find any better place for them....). The probing/stethoscope feedback went to priest.c, where there's a comment stating that it should move to wherever englightenment ends up once that is moved out of its completely inappropriate current home in cmd.c. (Holdover from when ^X was wizard-mode only but even the other wizard mode commands don't really belong with the command processing code.) --- include/extern.h | 14 +-- src/mkobj.c | 33 +++++- src/pline.c | 280 +++-------------------------------------------- src/potion.c | 12 +- src/priest.c | 240 +++++++++++++++++++++++++++++++++++++++- 5 files changed, 302 insertions(+), 277 deletions(-) diff --git a/include/extern.h b/include/extern.h index 36252bf46..c65148d27 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1496959470 2017/06/08 22:04:30 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.591 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1501725402 2017/08/03 01:56:42 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.598 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1309,6 +1309,7 @@ E struct obj *FDECL(obj_nexto, (struct obj *)); E struct obj *FDECL(obj_nexto_xy, (struct obj *, int, int, BOOLEAN_P)); E struct obj *FDECL(obj_absorb, (struct obj **, struct obj **)); E struct obj *FDECL(obj_meld, (struct obj **, struct obj **)); +E void FDECL(pudding_merge_message, (struct obj *, struct obj *)); /* ### mkroom.c ### */ @@ -1833,12 +1834,6 @@ E void VDECL(There, (const char *, ...)) PRINTF_F(1, 2); E void VDECL(verbalize, (const char *, ...)) PRINTF_F(1, 2); E void VDECL(raw_printf, (const char *, ...)) PRINTF_F(1, 2); E void VDECL(impossible, (const char *, ...)) PRINTF_F(1, 2); -E const char *FDECL(align_str, (ALIGNTYP_P)); -E void FDECL(mstatusline, (struct monst *)); -E void NDECL(ustatusline); -E void NDECL(self_invis_message); -E char *FDECL(piousness, (BOOLEAN_P, const char *)); -E void FDECL(pudding_merge_message, (struct obj *, struct obj *)); /* ### polyself.c ### */ @@ -1876,6 +1871,7 @@ E void FDECL(make_stoned, (long, const char *, int, const char *)); E void FDECL(make_vomiting, (long, BOOLEAN_P)); E boolean FDECL(make_hallucinated, (long, BOOLEAN_P, long)); E void FDECL(make_deaf, (long, BOOLEAN_P)); +E void NDECL(self_invis_message); E int NDECL(dodrink); E int FDECL(dopotion, (struct obj *)); E int FDECL(peffects, (struct obj *)); @@ -1932,6 +1928,10 @@ E void NDECL(clearpriests); E void FDECL(restpriest, (struct monst *, BOOLEAN_P)); E void FDECL(newepri, (struct monst *)); E void FDECL(free_epri, (struct monst *)); +E const char *FDECL(align_str, (ALIGNTYP_P)); +E char *FDECL(piousness, (BOOLEAN_P, const char *)); +E void FDECL(mstatusline, (struct monst *)); +E void NDECL(ustatusline); /* ### quest.c ### */ diff --git a/src/mkobj.c b/src/mkobj.c index aa1778a1d..78bb826f1 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mkobj.c $NHDT-Date: 1462067745 2016/05/01 01:55:45 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.122 $ */ +/* NetHack 3.6 mkobj.c $NHDT-Date: 1501725405 2017/08/03 01:56:45 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.124 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2720,4 +2720,35 @@ struct obj **obj1, **obj2; return (struct obj *) 0; } +/* give a message if hero notices two globs merging [used to be in pline.c] */ +void +pudding_merge_message(otmp, otmp2) +struct obj *otmp; +struct obj *otmp2; +{ + boolean visible = (cansee(otmp->ox, otmp->oy) + || cansee(otmp2->ox, otmp2->oy)), + onfloor = (otmp->where == OBJ_FLOOR || otmp2->where == OBJ_FLOOR), + inpack = (carried(otmp) || carried(otmp2)); + + /* the player will know something happened inside his own inventory */ + if ((!Blind && visible) || inpack) { + if (Hallucination) { + if (onfloor) { + You_see("parts of the floor melting!"); + } else if (inpack) { + Your("pack reaches out and grabs something!"); + } + /* even though we can see where they should be, + * they'll be out of our view (minvent or container) + * so don't actually show anything */ + } else if (onfloor || inpack) { + pline("The %s coalesce%s.", makeplural(obj_typename(otmp->otyp)), + inpack ? " inside your pack" : ""); + } + } else { + You_hear("a faint sloshing sound."); + } +} + /*mkobj.c*/ diff --git a/src/pline.c b/src/pline.c index 2c174e672..745385ada 100644 --- a/src/pline.c +++ b/src/pline.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 pline.c $NHDT-Date: 1490908465 2017/03/30 21:14:25 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.58 $ */ +/* NetHack 3.6 pline.c $NHDT-Date: 1501725406 2017/08/03 01:56:46 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.60 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -435,276 +435,22 @@ VA_DECL(const char *, s) Vsprintf(pbuf, s, VA_ARGS); pbuf[BUFSZ - 1] = '\0'; /* sanity */ paniclog("impossible", pbuf); - pline("%s", pbuf); - pline("Program in disorder - perhaps you'd better #quit."); +#ifndef USE_OLDARGS +#define DUMMY_PLINE_ARGS /*empty*/ +#else /* needed because we follow the definition of pline() itself; + * passing 1 arg, pline takes 9 (for USE_OLDARGS) so add 8 dummies */ +#define DUMMY_PLINE_ARGS , (vA) 0, (vA) 0, (vA) 0, (vA) 0, \ + (vA) 0, (vA) 0, (vA) 0, (vA) 0 +#endif + pline("%s", pbuf /* no comma here; when needed, it's in DUMMY_ARGS */ + DUMMY_PLINE_ARGS); + pline("%s", "Program in disorder - perhaps you'd better #quit." + DUMMY_PLINE_ARGS); +#undef DUMMY_PLINE_ARGS program_state.in_impossible = 0; VA_END(); } -const char * -align_str(alignment) -aligntyp alignment; -{ - switch ((int) alignment) { - case A_CHAOTIC: - return "chaotic"; - case A_NEUTRAL: - return "neutral"; - case A_LAWFUL: - return "lawful"; - case A_NONE: - return "unaligned"; - } - return "unknown"; -} - -void -mstatusline(mtmp) -register struct monst *mtmp; -{ - aligntyp alignment = mon_aligntyp(mtmp); - char info[BUFSZ], monnambuf[BUFSZ]; - - info[0] = 0; - if (mtmp->mtame) { - Strcat(info, ", tame"); - if (wizard) { - Sprintf(eos(info), " (%d", mtmp->mtame); - if (!mtmp->isminion) - Sprintf(eos(info), "; hungry %ld; apport %d", - EDOG(mtmp)->hungrytime, EDOG(mtmp)->apport); - Strcat(info, ")"); - } - } else if (mtmp->mpeaceful) - Strcat(info, ", peaceful"); - - if (mtmp->data == &mons[PM_LONG_WORM]) { - int segndx, nsegs = count_wsegs(mtmp); - - /* the worm code internals don't consider the head of be one of - the worm's segments, but we count it as such when presenting - worm feedback to the player */ - if (!nsegs) { - Strcat(info, ", single segment"); - } else { - ++nsegs; /* include head in the segment count */ - segndx = wseg_at(mtmp, bhitpos.x, bhitpos.y); - Sprintf(eos(info), ", %d%s of %d segments", - segndx, ordin(segndx), nsegs); - } - } - if (mtmp->cham >= LOW_PM && mtmp->data != &mons[mtmp->cham]) - /* don't reveal the innate form (chameleon, vampire, &c), - just expose the fact that this current form isn't it */ - Strcat(info, ", shapechanger"); - /* pets eating mimic corpses mimic while eating, so this comes first */ - if (mtmp->meating) - Strcat(info, ", eating"); - /* a stethoscope exposes mimic before getting here so this - won't be relevant for it, but wand of probing doesn't */ - if (mtmp->mundetected || mtmp->m_ap_type) - mhidden_description(mtmp, TRUE, eos(info)); - if (mtmp->mcan) - Strcat(info, ", cancelled"); - if (mtmp->mconf) - Strcat(info, ", confused"); - if (mtmp->mblinded || !mtmp->mcansee) - Strcat(info, ", blind"); - if (mtmp->mstun) - Strcat(info, ", stunned"); - if (mtmp->msleeping) - Strcat(info, ", asleep"); -#if 0 /* unfortunately mfrozen covers temporary sleep and being busy \ - (donning armor, for instance) as well as paralysis */ - else if (mtmp->mfrozen) - Strcat(info, ", paralyzed"); -#else - else if (mtmp->mfrozen || !mtmp->mcanmove) - Strcat(info, ", can't move"); -#endif - /* [arbitrary reason why it isn't moving] */ - else if (mtmp->mstrategy & STRAT_WAITMASK) - Strcat(info, ", meditating"); - if (mtmp->mflee) - Strcat(info, ", scared"); - if (mtmp->mtrapped) - Strcat(info, ", trapped"); - if (mtmp->mspeed) - Strcat(info, mtmp->mspeed == MFAST ? ", fast" : mtmp->mspeed == MSLOW - ? ", slow" - : ", ???? speed"); - if (mtmp->minvis) - Strcat(info, ", invisible"); - if (mtmp == u.ustuck) - Strcat(info, sticks(youmonst.data) - ? ", held by you" - : !u.uswallow ? ", holding you" - : attacktype_fordmg(u.ustuck->data, - AT_ENGL, AD_DGST) - ? ", digesting you" - : is_animal(u.ustuck->data) - ? ", swallowing you" - : ", engulfing you"); - if (mtmp == u.usteed) - Strcat(info, ", carrying you"); - - /* avoid "Status of the invisible newt ..., invisible" */ - /* and unlike a normal mon_nam, use "saddled" even if it has a name */ - Strcpy(monnambuf, x_monnam(mtmp, ARTICLE_THE, (char *) 0, - (SUPPRESS_IT | SUPPRESS_INVISIBLE), FALSE)); - - pline("Status of %s (%s): Level %d HP %d(%d) AC %d%s.", monnambuf, - align_str(alignment), mtmp->m_lev, mtmp->mhp, mtmp->mhpmax, - find_mac(mtmp), info); -} - -void -ustatusline() -{ - char info[BUFSZ]; - - info[0] = '\0'; - if (Sick) { - Strcat(info, ", dying from"); - if (u.usick_type & SICK_VOMITABLE) - Strcat(info, " food poisoning"); - if (u.usick_type & SICK_NONVOMITABLE) { - if (u.usick_type & SICK_VOMITABLE) - Strcat(info, " and"); - Strcat(info, " illness"); - } - } - if (Stoned) - Strcat(info, ", solidifying"); - if (Slimed) - Strcat(info, ", becoming slimy"); - if (Strangled) - Strcat(info, ", being strangled"); - if (Vomiting) - Strcat(info, ", nauseated"); /* !"nauseous" */ - if (Confusion) - Strcat(info, ", confused"); - if (Blind) { - Strcat(info, ", blind"); - if (u.ucreamed) { - if ((long) u.ucreamed < Blinded || Blindfolded - || !haseyes(youmonst.data)) - Strcat(info, ", cover"); - Strcat(info, "ed by sticky goop"); - } /* note: "goop" == "glop"; variation is intentional */ - } - if (Stunned) - Strcat(info, ", stunned"); - if (!u.usteed && Wounded_legs) { - const char *what = body_part(LEG); - if ((Wounded_legs & BOTH_SIDES) == BOTH_SIDES) - what = makeplural(what); - Sprintf(eos(info), ", injured %s", what); - } - if (Glib) - Sprintf(eos(info), ", slippery %s", makeplural(body_part(HAND))); - if (u.utrap) - Strcat(info, ", trapped"); - if (Fast) - Strcat(info, Very_fast ? ", very fast" : ", fast"); - if (u.uundetected) - Strcat(info, ", concealed"); - if (Invis) - Strcat(info, ", invisible"); - if (u.ustuck) { - if (sticks(youmonst.data)) - Strcat(info, ", holding "); - else - Strcat(info, ", held by "); - Strcat(info, mon_nam(u.ustuck)); - } - - pline("Status of %s (%s): Level %d HP %d(%d) AC %d%s.", plname, - piousness(FALSE, align_str(u.ualign.type)), - Upolyd ? mons[u.umonnum].mlevel : u.ulevel, Upolyd ? u.mh : u.uhp, - Upolyd ? u.mhmax : u.uhpmax, u.uac, info); -} - -void -self_invis_message() -{ - pline("%s %s.", - Hallucination ? "Far out, man! You" : "Gee! All of a sudden, you", - See_invisible ? "can see right through yourself" - : "can't see yourself"); -} - -char * -piousness(showneg, suffix) -boolean showneg; -const char *suffix; -{ - static char buf[32]; /* bigger than "insufficiently neutral" */ - const char *pio; - - /* note: piousness 20 matches MIN_QUEST_ALIGN (quest.h) */ - if (u.ualign.record >= 20) - pio = "piously"; - else if (u.ualign.record > 13) - pio = "devoutly"; - else if (u.ualign.record > 8) - pio = "fervently"; - else if (u.ualign.record > 3) - pio = "stridently"; - else if (u.ualign.record == 3) - pio = ""; - else if (u.ualign.record > 0) - pio = "haltingly"; - else if (u.ualign.record == 0) - pio = "nominally"; - else if (!showneg) - pio = "insufficiently"; - else if (u.ualign.record >= -3) - pio = "strayed"; - else if (u.ualign.record >= -8) - pio = "sinned"; - else - pio = "transgressed"; - - Sprintf(buf, "%s", pio); - if (suffix && (!showneg || u.ualign.record >= 0)) { - if (u.ualign.record != 3) - Strcat(buf, " "); - Strcat(buf, suffix); - } - return buf; -} - -void -pudding_merge_message(otmp, otmp2) -struct obj *otmp; -struct obj *otmp2; -{ - boolean visible = - cansee(otmp->ox, otmp->oy) || cansee(otmp2->ox, otmp2->oy); - boolean onfloor = otmp->where == OBJ_FLOOR || otmp2->where == OBJ_FLOOR; - boolean inpack = carried(otmp) || carried(otmp2); - - /* the player will know something happened inside his own inventory */ - if ((!Blind && visible) || inpack) { - if (Hallucination) { - if (onfloor) { - You_see("parts of the floor melting!"); - } else if (inpack) { - Your("pack reaches out and grabs something!"); - } - /* even though we can see where they should be, - * they'll be out of our view (minvent or container) - * so don't actually show anything */ - } else if (onfloor || inpack) { - pline("The %s coalesce%s.", makeplural(obj_typename(otmp->otyp)), - inpack ? " inside your pack" : ""); - } - } else { - You_hear("a faint sloshing sound."); - } -} - #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__)) static boolean use_pline_handler = TRUE; static void diff --git a/src/potion.c b/src/potion.c index 38f5c6553..fd1161e3a 100644 --- a/src/potion.c +++ b/src/potion.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 potion.c $NHDT-Date: 1455407631 2016/02/13 23:53:51 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.129 $ */ +/* NetHack 3.6 potion.c $NHDT-Date: 1501725406 2017/08/03 01:56:46 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.137 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -408,6 +408,16 @@ boolean talk; } } +void +self_invis_message() +{ + pline("%s %s.", + Hallucination ? "Far out, man! You" + : "Gee! All of a sudden, you", + See_invisible ? "can see right through yourself" + : "can't see yourself"); +} + STATIC_OVL void ghost_from_bottle() { diff --git a/src/priest.c b/src/priest.c index 0cce5cf36..4a3dd185d 100644 --- a/src/priest.c +++ b/src/priest.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 priest.c $NHDT-Date: 1446892452 2015/11/07 10:34:12 $ $NHDT-Branch: master $:$NHDT-Revision: 1.41 $ */ +/* NetHack 3.6 priest.c $NHDT-Date: 1501725407 2017/08/03 01:56:47 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.44 $ */ /* Copyright (c) Izchak Miller, Steve Linhart, 1989. */ /* NetHack may be freely redistributed. See license for details. */ @@ -852,4 +852,242 @@ boolean ghostly; } } +/* + * align_str(), piousness(), mstatusline() and ustatusline() used to be + * in pline.c, presumeably because the latter two generate one line of + * output. The USE_OLDARGS config gets warnings from 2016ish-vintage + * gcc (for -Wint-to-pointer-cast, activated by -Wall or -W) when they + * follow pline() itself. Fixing up the variadic calls like is done for + * lev_comp would be needlessly messy there. + * + * They don't belong here. If/when enlightenment ever gets split off + * from cmd.c (which definitely doesn't belong there), they should go + * with it. + */ + +const char * +align_str(alignment) +aligntyp alignment; +{ + switch ((int) alignment) { + case A_CHAOTIC: + return "chaotic"; + case A_NEUTRAL: + return "neutral"; + case A_LAWFUL: + return "lawful"; + case A_NONE: + return "unaligned"; + } + return "unknown"; +} + +/* used for self-probing */ +char * +piousness(showneg, suffix) +boolean showneg; +const char *suffix; +{ + static char buf[32]; /* bigger than "insufficiently neutral" */ + const char *pio; + + /* note: piousness 20 matches MIN_QUEST_ALIGN (quest.h) */ + if (u.ualign.record >= 20) + pio = "piously"; + else if (u.ualign.record > 13) + pio = "devoutly"; + else if (u.ualign.record > 8) + pio = "fervently"; + else if (u.ualign.record > 3) + pio = "stridently"; + else if (u.ualign.record == 3) + pio = ""; + else if (u.ualign.record > 0) + pio = "haltingly"; + else if (u.ualign.record == 0) + pio = "nominally"; + else if (!showneg) + pio = "insufficiently"; + else if (u.ualign.record >= -3) + pio = "strayed"; + else if (u.ualign.record >= -8) + pio = "sinned"; + else + pio = "transgressed"; + + Sprintf(buf, "%s", pio); + if (suffix && (!showneg || u.ualign.record >= 0)) { + if (u.ualign.record != 3) + Strcat(buf, " "); + Strcat(buf, suffix); + } + return buf; +} + +/* stethoscope or probing applied to monster -- one-line feedback */ +void +mstatusline(mtmp) +struct monst *mtmp; +{ + aligntyp alignment = mon_aligntyp(mtmp); + char info[BUFSZ], monnambuf[BUFSZ]; + + info[0] = 0; + if (mtmp->mtame) { + Strcat(info, ", tame"); + if (wizard) { + Sprintf(eos(info), " (%d", mtmp->mtame); + if (!mtmp->isminion) + Sprintf(eos(info), "; hungry %ld; apport %d", + EDOG(mtmp)->hungrytime, EDOG(mtmp)->apport); + Strcat(info, ")"); + } + } else if (mtmp->mpeaceful) + Strcat(info, ", peaceful"); + + if (mtmp->data == &mons[PM_LONG_WORM]) { + int segndx, nsegs = count_wsegs(mtmp); + + /* the worm code internals don't consider the head of be one of + the worm's segments, but we count it as such when presenting + worm feedback to the player */ + if (!nsegs) { + Strcat(info, ", single segment"); + } else { + ++nsegs; /* include head in the segment count */ + segndx = wseg_at(mtmp, bhitpos.x, bhitpos.y); + Sprintf(eos(info), ", %d%s of %d segments", + segndx, ordin(segndx), nsegs); + } + } + if (mtmp->cham >= LOW_PM && mtmp->data != &mons[mtmp->cham]) + /* don't reveal the innate form (chameleon, vampire, &c), + just expose the fact that this current form isn't it */ + Strcat(info, ", shapechanger"); + /* pets eating mimic corpses mimic while eating, so this comes first */ + if (mtmp->meating) + Strcat(info, ", eating"); + /* a stethoscope exposes mimic before getting here so this + won't be relevant for it, but wand of probing doesn't */ + if (mtmp->mundetected || mtmp->m_ap_type) + mhidden_description(mtmp, TRUE, eos(info)); + if (mtmp->mcan) + Strcat(info, ", cancelled"); + if (mtmp->mconf) + Strcat(info, ", confused"); + if (mtmp->mblinded || !mtmp->mcansee) + Strcat(info, ", blind"); + if (mtmp->mstun) + Strcat(info, ", stunned"); + if (mtmp->msleeping) + Strcat(info, ", asleep"); +#if 0 /* unfortunately mfrozen covers temporary sleep and being busy \ + (donning armor, for instance) as well as paralysis */ + else if (mtmp->mfrozen) + Strcat(info, ", paralyzed"); +#else + else if (mtmp->mfrozen || !mtmp->mcanmove) + Strcat(info, ", can't move"); +#endif + /* [arbitrary reason why it isn't moving] */ + else if (mtmp->mstrategy & STRAT_WAITMASK) + Strcat(info, ", meditating"); + if (mtmp->mflee) + Strcat(info, ", scared"); + if (mtmp->mtrapped) + Strcat(info, ", trapped"); + if (mtmp->mspeed) + Strcat(info, (mtmp->mspeed == MFAST) ? ", fast" + : (mtmp->mspeed == MSLOW) ? ", slow" + : ", [? speed]"); + if (mtmp->minvis) + Strcat(info, ", invisible"); + if (mtmp == u.ustuck) + Strcat(info, sticks(youmonst.data) ? ", held by you" + : !u.uswallow ? ", holding you" + : attacktype_fordmg(u.ustuck->data, AT_ENGL, AD_DGST) + ? ", digesting you" + : is_animal(u.ustuck->data) ? ", swallowing you" + : ", engulfing you"); + if (mtmp == u.usteed) + Strcat(info, ", carrying you"); + + /* avoid "Status of the invisible newt ..., invisible" */ + /* and unlike a normal mon_nam, use "saddled" even if it has a name */ + Strcpy(monnambuf, x_monnam(mtmp, ARTICLE_THE, (char *) 0, + (SUPPRESS_IT | SUPPRESS_INVISIBLE), FALSE)); + + pline("Status of %s (%s): Level %d HP %d(%d) AC %d%s.", monnambuf, + align_str(alignment), mtmp->m_lev, mtmp->mhp, mtmp->mhpmax, + find_mac(mtmp), info); +} + +/* stethoscope or probing applied to hero -- one-line feedback */ +void +ustatusline() +{ + char info[BUFSZ]; + + info[0] = '\0'; + if (Sick) { + Strcat(info, ", dying from"); + if (u.usick_type & SICK_VOMITABLE) + Strcat(info, " food poisoning"); + if (u.usick_type & SICK_NONVOMITABLE) { + if (u.usick_type & SICK_VOMITABLE) + Strcat(info, " and"); + Strcat(info, " illness"); + } + } + if (Stoned) + Strcat(info, ", solidifying"); + if (Slimed) + Strcat(info, ", becoming slimy"); + if (Strangled) + Strcat(info, ", being strangled"); + if (Vomiting) + Strcat(info, ", nauseated"); /* !"nauseous" */ + if (Confusion) + Strcat(info, ", confused"); + if (Blind) { + Strcat(info, ", blind"); + if (u.ucreamed) { + if ((long) u.ucreamed < Blinded || Blindfolded + || !haseyes(youmonst.data)) + Strcat(info, ", cover"); + Strcat(info, "ed by sticky goop"); + } /* note: "goop" == "glop"; variation is intentional */ + } + if (Stunned) + Strcat(info, ", stunned"); + if (!u.usteed && Wounded_legs) { + const char *what = body_part(LEG); + if ((Wounded_legs & BOTH_SIDES) == BOTH_SIDES) + what = makeplural(what); + Sprintf(eos(info), ", injured %s", what); + } + if (Glib) + Sprintf(eos(info), ", slippery %s", makeplural(body_part(HAND))); + if (u.utrap) + Strcat(info, ", trapped"); + if (Fast) + Strcat(info, Very_fast ? ", very fast" : ", fast"); + if (u.uundetected) + Strcat(info, ", concealed"); + if (Invis) + Strcat(info, ", invisible"); + if (u.ustuck) { + if (sticks(youmonst.data)) + Strcat(info, ", holding "); + else + Strcat(info, ", held by "); + Strcat(info, mon_nam(u.ustuck)); + } + + pline("Status of %s (%s): Level %d HP %d(%d) AC %d%s.", plname, + piousness(FALSE, align_str(u.ualign.type)), + Upolyd ? mons[u.umonnum].mlevel : u.ulevel, Upolyd ? u.mh : u.uhp, + Upolyd ? u.mhmax : u.uhpmax, u.uac, info); +} + /*priest.c*/ From b90c5d5c4fd6bb22530bab31f9fc778318e36ba1 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 3 Aug 2017 16:31:55 -0700 Subject: [PATCH 25/39] USE_OLDARGS update (3 of 2 :-) Handle the few variadic calls in pline.c a better way. --- include/sp_lev.h | 3 ++- include/tradstdc.h | 7 ++++++- src/pline.c | 22 ++++++---------------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/include/sp_lev.h b/include/sp_lev.h index 9befcd669..da6424c56 100644 --- a/include/sp_lev.h +++ b/include/sp_lev.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 sp_lev.h $NHDT-Date: 1501723399 2017/08/03 01:23:19 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.20 $ */ +/* NetHack 3.6 sp_lev.h $NHDT-Date: 1501803105 2017/08/03 23:31:45 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.21 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -490,6 +490,7 @@ typedef const char *vA; #endif #undef VA_ARGS /* redefine with the maximum number actually used */ #undef VA_SHIFT /* ditto */ +#undef VA_PASS1 #define VA_ARGS \ arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, \ arg12, arg13, arg14 diff --git a/include/tradstdc.h b/include/tradstdc.h index ddd7d2037..8ab152b52 100644 --- a/include/tradstdc.h +++ b/include/tradstdc.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 tradstdc.h $NHDT-Date: 1501723401 2017/08/03 01:23:21 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.28 $ */ +/* NetHack 3.6 tradstdc.h $NHDT-Date: 1501803107 2017/08/03 23:31:47 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.29 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -100,6 +100,7 @@ #define VA_END() \ va_end(the_args); \ } +#define VA_PASS1(a1) a1 #if defined(ULTRIX_PROTO) && !defined(_VA_LIST_) #define _VA_LIST_ /* prevents multiple def in stdio.h */ #endif @@ -125,6 +126,7 @@ #define VA_END() \ va_end(the_args); \ } +#define VA_PASS1(a1) a1 #else /*USE_OLDARGS*/ @@ -162,6 +164,9 @@ typedef const char *vA; arg6 = arg7, arg7 = arg8, arg8 = arg9, arg9 = 0) #define VA_NEXT(var1, typ1) ((var1 = (typ1) arg1), VA_SHIFT(), var1) #define VA_END() } +/* needed in pline.c, where full number of arguments is known and expected */ +#define VA_PASS1(a1) \ + (vA) a1, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0, (vA) 0 #endif #endif diff --git a/src/pline.c b/src/pline.c index 745385ada..07e55e679 100644 --- a/src/pline.c +++ b/src/pline.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 pline.c $NHDT-Date: 1501725406 2017/08/03 01:56:46 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.60 $ */ +/* NetHack 3.6 pline.c $NHDT-Date: 1501803108 2017/08/03 23:31:48 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.61 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -435,18 +435,8 @@ VA_DECL(const char *, s) Vsprintf(pbuf, s, VA_ARGS); pbuf[BUFSZ - 1] = '\0'; /* sanity */ paniclog("impossible", pbuf); -#ifndef USE_OLDARGS -#define DUMMY_PLINE_ARGS /*empty*/ -#else /* needed because we follow the definition of pline() itself; - * passing 1 arg, pline takes 9 (for USE_OLDARGS) so add 8 dummies */ -#define DUMMY_PLINE_ARGS , (vA) 0, (vA) 0, (vA) 0, (vA) 0, \ - (vA) 0, (vA) 0, (vA) 0, (vA) 0 -#endif - pline("%s", pbuf /* no comma here; when needed, it's in DUMMY_ARGS */ - DUMMY_PLINE_ARGS); - pline("%s", "Program in disorder - perhaps you'd better #quit." - DUMMY_PLINE_ARGS); -#undef DUMMY_PLINE_ARGS + pline("%s", VA_PASS1(pbuf)); + pline("%s", VA_PASS1("Program in disorder - perhaps you'd better #quit.")); program_state.in_impossible = 0; VA_END(); } @@ -478,16 +468,16 @@ const char *line; (void) setuid(getuid()); (void) execv(args[0], (char *const *) args); perror((char *) 0); - (void) fprintf(stderr, "Exec to message handler %s failed.\n", - env); + (void) fprintf(stderr, "Exec to message handler %s failed.\n", env); terminate(EXIT_FAILURE); } else if (f > 0) { int status; + waitpid(f, &status, 0); } else if (f == -1) { perror((char *) 0); use_pline_handler = FALSE; - pline("Fork to message handler failed."); + pline("%s", VA_PASS1("Fork to message handler failed.")); } } #endif /* defined(POSIX_TYPES) || defined(__GNUC__) */ From e2904012b7f36870f5ff54bad66fb916edf81b9a Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 4 Aug 2017 21:41:04 +0300 Subject: [PATCH 26/39] Show current timeouts in #wizintrinsic --- src/cmd.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/cmd.c b/src/cmd.c index 719c4902e..53a800bd9 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1189,7 +1189,12 @@ wiz_intrinsic(VOID_ARGS) add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "--", FALSE); } any.a_int = i + 1; /* +1: avoid 0 */ - add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, propname, FALSE); + oldtimeout = u.uprops[p].intrinsic & TIMEOUT; + if (oldtimeout) + Sprintf(buf, "%-27s [%li]", propname, oldtimeout); + else + Sprintf(buf, "%s", propname); + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); } end_menu(win, "Which intrinsics?"); n = select_menu(win, PICK_ANY, &pick_list); From b6d1ee3320777b6569f7ca9b93f1edc5e80bc2f2 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 4 Aug 2017 22:41:26 +0300 Subject: [PATCH 27/39] Using a cursed tin whistle in a vault summons the guard ...since guards respond to shrill whistles. Based on a patch by Leon Arnott. --- doc/fixes36.1 | 1 + include/extern.h | 1 + include/hack.h | 3 +++ src/apply.c | 2 ++ src/vault.c | 10 +++++++++- 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 73803752a..c29309ad3 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -405,6 +405,7 @@ fix the 'A' command to have the 'D' command's fix for C331-1 (quirk for response, to operate on applicable all items, there would still be a followup menu asking to choose specific items) eating 1 tin from stack of N (for N >= 2) on shop's floor forced hero to buy 2 +using a cursed whistle in a vault will summon the guard immediately Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index c65148d27..7d49b29d1 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2510,6 +2510,7 @@ E int FDECL(hide_privileges, (BOOLEAN_P)); E void FDECL(newegd, (struct monst *)); E void FDECL(free_egd, (struct monst *)); E boolean FDECL(grddead, (struct monst *)); +E void NDECL(vault_summon_gd); E char FDECL(vault_occupied, (char *)); E void NDECL(invault); E int FDECL(gd_move, (struct monst *)); diff --git a/include/hack.h b/include/hack.h index dffb26c5c..c4691ae26 100644 --- a/include/hack.h +++ b/include/hack.h @@ -34,6 +34,9 @@ enum encumbrance_types { /* weight increment of heavy iron ball */ #define IRON_BALL_W_INCR 160 +/* number of turns it takes for vault guard to show up */ +#define VAULT_GUARD_TIME 30 + /* hunger states - see hu_stat in eat.c */ enum hunger_state_types { SATIATED = 0, diff --git a/src/apply.c b/src/apply.c index fca3f8e36..6337eb49d 100644 --- a/src/apply.c +++ b/src/apply.c @@ -445,6 +445,8 @@ struct obj *obj; } else { You(whistle_str, obj->cursed ? "shrill" : "high"); wake_nearby(); + if (obj->cursed) + vault_summon_gd(); } } diff --git a/src/vault.c b/src/vault.c index f076986f5..296116c53 100644 --- a/src/vault.c +++ b/src/vault.c @@ -191,6 +191,13 @@ findgd() return (struct monst *) 0; } +void +vault_summon_gd() +{ + if (vault_occupied(u.urooms) && !findgd()) + u.uinvault = (VAULT_GUARD_TIME - 1); +} + char vault_occupied(array) char *array; @@ -221,7 +228,8 @@ invault() vaultroom -= ROOMOFFSET; guard = findgd(); - if (++u.uinvault % 30 == 0 && !guard) { /* if time ok and no guard now. */ + if (++u.uinvault % VAULT_GUARD_TIME == 0 && !guard) { + /* if time ok and no guard now. */ char buf[BUFSZ]; register int x, y, dd, gx, gy; int lx = 0, ly = 0; From 4d7d63814a99b14acf0bcc96ddf567e966064b78 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 4 Aug 2017 23:15:30 +0300 Subject: [PATCH 28/39] Throne room's throne is occupied by a king Based on a patch by Leon Arnott --- doc/fixes36.1 | 1 + src/mkroom.c | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index c29309ad3..d2fb9b2ca 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -406,6 +406,7 @@ fix the 'A' command to have the 'D' command's fix for C331-1 (quirk for followup menu asking to choose specific items) eating 1 tin from stack of N (for N >= 2) on shop's floor forced hero to buy 2 using a cursed whistle in a vault will summon the guard immediately +throne room's throne is occupied by a king Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/mkroom.c b/src/mkroom.c index c76531b52..ff074cd5c 100644 --- a/src/mkroom.c +++ b/src/mkroom.c @@ -239,6 +239,26 @@ int type; } } +void +mk_zoo_thronemon(x,y) +int x,y; +{ + int i = rnd(level_difficulty()); + int pm = (i > 9) ? PM_OGRE_KING + : (i > 5) ? PM_ELVENKING + : (i > 2) ? PM_DWARF_KING + : PM_GNOME_KING; + struct monst *mon = makemon(&mons[pm], x, y, NO_MM_FLAGS); + + if (mon) { + mon->msleeping = 1; + mon->mpeaceful = 0; + set_malign(mon); + /* Give him a sceptre to pound in judgment */ + (void) mongets(mon, MACE); + } +} + void fill_zoo(sroom) struct mkroom *sroom; @@ -265,7 +285,7 @@ struct mkroom *sroom; ty = mm.y; } while (occupied((xchar) tx, (xchar) ty) && --i > 0); throne_placed: - /* TODO: try to ensure the enthroned monster is an M2_PRINCE */ + mk_zoo_thronemon(tx, ty); break; case BEEHIVE: tx = sroom->lx + (sroom->hx - sroom->lx + 1) / 2; From 0d7101cd291fb352e8a35c7c1fb6bf2d6e1398f0 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 5 Aug 2017 17:29:24 -0700 Subject: [PATCH 29/39] fix part of #H5778 - file descriptor leaks Handle the part of #H5778 in sys/atari/tos.c. The part in util/recover.c has already been fixed; the part in src/files.c still needs to be fixed. --- sys/atari/tos.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/sys/atari/tos.c b/sys/atari/tos.c index eee7bd40f..9bf36a81b 100644 --- a/sys/atari/tos.c +++ b/sys/atari/tos.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 tos.c $NHDT-Date: 1432512796 2015/05/25 00:13:16 $ $NHDT-Branch: master $:$NHDT-Revision: 1.7 $ */ +/* NetHack 3.6 tos.c $NHDT-Date: 1501979358 2017/08/06 00:29:18 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.8 $ */ /* NetHack may be freely redistributed. See license for details. */ /* @@ -271,17 +271,20 @@ char *from, *to; int fromfd, tofd, r; char *buf; - if ((fromfd = open(from, O_RDONLY | O_BINARY, 0)) < 0) + fromfd = open(from, O_RDONLY | O_BINARY, 0); + if (fromfd < 0) return -1; - if ((tofd = open(to, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, FCMASK)) - < 0) + tofd = open(to, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, FCMASK); + if (tofd < 0) { + close(fromfd); return -1; - buf = (char *) alloc((size_t) BIGBUF); + } + buf = (char *) alloc((unsigned) BIGBUF); while ((r = read(fromfd, buf, BIGBUF)) > 0) write(tofd, buf, r); close(fromfd); close(tofd); - free(buf); + free((genericptr_t) buf); return 0; /* successful */ } From 1614f8f5ee919248d9b8de3198166e981150749c Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 5 Aug 2017 17:58:20 -0700 Subject: [PATCH 30/39] fix brace mismatch in winami.c Reported directly to devteam, so no #H number. Conditional code in amii_get_ext_cmd() included an unmatched '{', which would break compilation (at least if EXTMENU was enabled) and also resulted in the remainder of winami.c being mis-formatted (the functions there were treated as being inside a block rather than at file level). This is completely untested. There's some code suppressed via '#if 0' with a comment "fix for PL2". We haven't used the patch-level nomenclature since version 3.0! --- sys/amiga/winami.c | 1274 ++++++++++++++++++++++---------------------- 1 file changed, 635 insertions(+), 639 deletions(-) diff --git a/sys/amiga/winami.c b/sys/amiga/winami.c index 5f32f7711..daabd859d 100644 --- a/sys/amiga/winami.c +++ b/sys/amiga/winami.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 winami.c $NHDT-Date: 1432512794 2015/05/25 00:13:14 $ $NHDT-Branch: master $:$NHDT-Revision: 1.19 $ */ +/* NetHack 3.6 winami.c $NHDT-Date: 1501981093 2017/08/06 00:58:13 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.20 $ */ /* Copyright (c) Gregg Wonderly, Naperville, Illinois, 1991,1992,1993,1996. */ /* NetHack may be freely redistributed. See license for details. */ @@ -807,8 +807,8 @@ amii_get_ext_cmd(void) } return (-1); - } else { -#else + } +#endif amii_clear_nhwindow(WIN_MESSAGE); /* Was NHW_MESSAGE */ if (scrollmsg) { @@ -932,58 +932,61 @@ amii_get_ext_cmd(void) DisplayBeep(NULL); } return (-1); -#endif - } +} - static int put_ext_cmd(obufp, colx, cw, bottom) char * obufp; - int colx, bottom; - struct amii_WinDesc *cw; - { - struct Window *w = cw->win; - char *t; +static int +put_ext_cmd(obufp, colx, cw, bottom) +char * obufp; +int colx, bottom; +struct amii_WinDesc *cw; +{ + struct Window *w = cw->win; + char *t; - t = (char *) alloc(strlen(obufp) + 7); - if (t != NULL) { - if (scrollmsg) { - sprintf(t, "xxx%s", obufp); - t[0] = 1; - t[1] = 1; - t[2] = '#'; - amii_curs(WIN_MESSAGE, 0, bottom); - SetAPen(w->RPort, C_WHITE); - Text(w->RPort, "># ", 3); - /* SetAPen( w->RPort, C_BLACK ); */ /* Black text on black - screen doesn't look too - well ... -jhsa */ - Text(w->RPort, t + 3, strlen(t) - 3); - } else { - sprintf(t, "# %s", obufp); - amii_curs(WIN_MESSAGE, 0, bottom); - SetAPen(w->RPort, C_WHITE); - Text(w->RPort, t, strlen(t)); - } - if (scrollmsg) - SetAPen(w->RPort, C_WHITE); - if (cw->data[cw->maxrow - 1]) - free(cw->data[cw->maxrow - 1]); - cw->data[cw->maxrow - 1] = t; - } else { + t = (char *) alloc(strlen(obufp) + 7); + if (t != NULL) { + if (scrollmsg) { + sprintf(t, "xxx%s", obufp); + t[0] = 1; + t[1] = 1; + t[2] = '#'; amii_curs(WIN_MESSAGE, 0, bottom); SetAPen(w->RPort, C_WHITE); - Text(w->RPort, "# ", 2); - /* SetAPen( w->RPort, C_BLACK ); */ /* Black on black ... -jhsa */ - Text(w->RPort, obufp, strlen(obufp)); + Text(w->RPort, "># ", 3); + /* SetAPen( w->RPort, C_BLACK ); */ /* Black text on black + * screen doesn't look + * too well ... -jhsa */ + Text(w->RPort, t + 3, strlen(t) - 3); + } else { + sprintf(t, "# %s", obufp); + amii_curs(WIN_MESSAGE, 0, bottom); SetAPen(w->RPort, C_WHITE); + Text(w->RPort, t, strlen(t)); } - amii_curs(WIN_MESSAGE, colx = strlen(obufp) + 3 + (scrollmsg != 0), - bottom); - return (colx); + if (scrollmsg) + SetAPen(w->RPort, C_WHITE); + if (cw->data[cw->maxrow - 1]) + free(cw->data[cw->maxrow - 1]); + cw->data[cw->maxrow - 1] = t; + } else { + amii_curs(WIN_MESSAGE, 0, bottom); + SetAPen(w->RPort, C_WHITE); + Text(w->RPort, "# ", 2); + /* SetAPen( w->RPort, C_BLACK ); */ /* Black on black ... -jhsa */ + Text(w->RPort, obufp, strlen(obufp)); + SetAPen(w->RPort, C_WHITE); } + amii_curs(WIN_MESSAGE, colx = strlen(obufp) + 3 + (scrollmsg != 0), + bottom); + return colx; +} - /* Ask a question and get a response */ - - char amii_yn_function(query, resp, def) const char * query, *resp; - char def; +/* Ask a question and get a response */ +char +amii_yn_function(query, resp, def) +const char * query, *resp; +char def; +{ /* * Generic yes/no function. 'def' is the default (returned by space or * return; 'esc' returns 'q', or 'n', or the default, depending on @@ -995,155 +998,158 @@ amii_get_ext_cmd(void) * are allowed); if it includes an , anything beyond that won't * be shown in the prompt to the user but will be acceptable as input. */ - { - register char q; - char rtmp[40]; - boolean digit_ok, allow_num; - char prompt[BUFSZ]; - register struct amii_WinDesc *cw; + register char q; + char rtmp[40]; + boolean digit_ok, allow_num; + char prompt[BUFSZ]; + register struct amii_WinDesc *cw; - if (cw = amii_wins[WIN_MESSAGE]) - cw->disprows = 0; - if (resp) { - char *rb, respbuf[QBUFSZ]; + if (cw = amii_wins[WIN_MESSAGE]) + cw->disprows = 0; + if (resp) { + char *rb, respbuf[QBUFSZ]; - allow_num = (index(resp, '#') != 0); - Strcpy(respbuf, resp); - /* any acceptable responses that follow aren't displayed */ - if ((rb = index(respbuf, '\033')) != 0) - *rb = '\0'; - (void) strncpy(prompt, query, QBUFSZ - 1); - prompt[QBUFSZ - 1] = '\0'; - Sprintf(eos(prompt), " [%s]", respbuf); - if (def) - Sprintf(eos(prompt), " (%c)", def); - Strcat(prompt, " "); - pline("%s", prompt); - } else { - amii_putstr(WIN_MESSAGE, 0, query); - cursor_on(WIN_MESSAGE); - q = WindowGetchar(); - cursor_off(WIN_MESSAGE); - *rtmp = q; - rtmp[1] = 0; - amii_addtopl(rtmp); - goto clean_up; - } - - do { /* loop until we get valid input */ - cursor_on(WIN_MESSAGE); - q = lowc(WindowGetchar()); - cursor_off(WIN_MESSAGE); -#if 0 -/* fix for PL2 */ - if (q == '\020') { /* ctrl-P */ - if(!doprev) (void) tty_doprev_message(); /* need two initially */ - (void) tty_doprev_message(); - q = (char)0; - doprev = 1; - continue; - } else if(doprev) { - tty_clear_nhwindow(WIN_MESSAGE); - cw->maxcol = cw->maxrow; - doprev = 0; - amii_addtopl(prompt); - continue; - } -#endif - digit_ok = allow_num && isdigit(q); - if (q == '\033') { - if (index(resp, 'q')) - q = 'q'; - else if (index(resp, 'n')) - q = 'n'; - else - q = def; - break; - } else if (index(quitchars, q)) { - q = def; - break; - } - if (!index(resp, q) && !digit_ok) { - amii_bell(); - q = (char) 0; - } else if (q == '#' || digit_ok) { - char z, digit_string[2]; - int n_len = 0; - long value = 0; - amii_addtopl("#"), n_len++; - digit_string[1] = '\0'; - if (q != '#') { - digit_string[0] = q; - amii_addtopl(digit_string), n_len++; - value = q - '0'; - q = '#'; - } - do { /* loop until we get a non-digit */ - cursor_on(WIN_MESSAGE); - z = lowc(WindowGetchar()); - cursor_off(WIN_MESSAGE); - if (isdigit(z)) { - value = (10 * value) + (z - '0'); - if (value < 0) - break; /* overflow: try again */ - digit_string[0] = z; - amii_addtopl(digit_string), n_len++; - } else if (z == 'y' || index(quitchars, z)) { - if (z == '\033') - value = -1; /* abort */ - z = '\n'; /* break */ - } else if (z == '\b') { - if (n_len <= 1) { - value = -1; - break; - } else { - value /= 10; - removetopl(1), n_len--; - } - } else { - value = -1; /* abort */ - amii_bell(); - break; - } - } while (z != '\n'); - if (value > 0) - yn_number = value; - else if (value == 0) - q = 'n'; /* 0 => "no" */ - else { /* remove number from top line, then try again */ - removetopl(n_len), n_len = 0; - q = '\0'; - } - } - } while (!q); - - if (q != '#' && q != '\033') { - Sprintf(rtmp, "%c", q); - amii_addtopl(rtmp); - } - clean_up: + allow_num = (index(resp, '#') != 0); + Strcpy(respbuf, resp); + /* any acceptable responses that follow aren't displayed */ + if ((rb = index(respbuf, '\033')) != 0) + *rb = '\0'; + (void) strncpy(prompt, query, QBUFSZ - 1); + prompt[QBUFSZ - 1] = '\0'; + Sprintf(eos(prompt), " [%s]", respbuf); + if (def) + Sprintf(eos(prompt), " (%c)", def); + Strcat(prompt, " "); + pline("%s", prompt); + } else { + amii_putstr(WIN_MESSAGE, 0, query); + cursor_on(WIN_MESSAGE); + q = WindowGetchar(); cursor_off(WIN_MESSAGE); - clear_nhwindow(WIN_MESSAGE); - return q; + *rtmp = q; + rtmp[1] = '\0'; + amii_addtopl(rtmp); + goto clean_up; } - void amii_display_file(fn, complain) const char * fn; - boolean complain; - { - register struct amii_WinDesc *cw; - register int win; - register dlb *fp; - register char *t; - register char buf[200]; + do { /* loop until we get valid input */ + cursor_on(WIN_MESSAGE); + q = lowc(WindowGetchar()); + cursor_off(WIN_MESSAGE); +#if 0 +/* fix for PL2 */ + if (q == '\020') { /* ctrl-P */ + if(!doprev) + (void) tty_doprev_message(); /* need two initially */ + (void) tty_doprev_message(); + q = (char)0; + doprev = 1; + continue; + } else if (doprev) { + tty_clear_nhwindow(WIN_MESSAGE); + cw->maxcol = cw->maxrow; + doprev = 0; + amii_addtopl(prompt); + continue; + } +#endif /*0*/ + digit_ok = allow_num && isdigit(q); + if (q == '\033') { + if (index(resp, 'q')) + q = 'q'; + else if (index(resp, 'n')) + q = 'n'; + else + q = def; + break; + } else if (index(quitchars, q)) { + q = def; + break; + } + if (!index(resp, q) && !digit_ok) { + amii_bell(); + q = (char) 0; + } else if (q == '#' || digit_ok) { + char z, digit_string[2]; + int n_len = 0; + long value = 0; - if (fn == NULL) - panic("NULL file name in display_file()"); + amii_addtopl("#"), n_len++; + digit_string[1] = '\0'; + if (q != '#') { + digit_string[0] = q; + amii_addtopl(digit_string), n_len++; + value = q - '0'; + q = '#'; + } + do { /* loop until we get a non-digit */ + cursor_on(WIN_MESSAGE); + z = lowc(WindowGetchar()); + cursor_off(WIN_MESSAGE); + if (isdigit(z)) { + value = (10 * value) + (z - '0'); + if (value < 0) + break; /* overflow: try again */ + digit_string[0] = z; + amii_addtopl(digit_string), n_len++; + } else if (z == 'y' || index(quitchars, z)) { + if (z == '\033') + value = -1; /* abort */ + z = '\n'; /* break */ + } else if (z == '\b') { + if (n_len <= 1) { + value = -1; + break; + } else { + value /= 10; + removetopl(1), n_len--; + } + } else { + value = -1; /* abort */ + amii_bell(); + break; + } + } while (z != '\n'); + if (value > 0) + yn_number = value; + else if (value == 0) + q = 'n'; /* 0 => "no" */ + else { /* remove number from top line, then try again */ + removetopl(n_len), n_len = 0; + q = '\0'; + } + } + } while (!q); - if ((fp = dlb_fopen(fn, RDTMODE)) == (dlb *) NULL) { - if (complain) { - sprintf(buf, "Can't display %s: %s", fn, + if (q != '#' && q != '\033') { + Sprintf(rtmp, "%c", q); + amii_addtopl(rtmp); + } + clean_up: + cursor_off(WIN_MESSAGE); + clear_nhwindow(WIN_MESSAGE); + return q; +} + +void +amii_display_file(fn, complain) +const char * fn; +boolean complain; +{ + register struct amii_WinDesc *cw; + register int win; + register dlb *fp; + register char *t; + register char buf[200]; + + if (fn == NULL) + panic("NULL file name in display_file()"); + + if ((fp = dlb_fopen(fn, RDTMODE)) == (dlb *) NULL) { + if (complain) { + sprintf(buf, "Can't display %s: %s", fn, #if defined(_DCC) || defined(__GNUC__) - strerror(errno) + strerror(errno) #else #ifdef __SASC_60 __sys_errlist[errno] @@ -1151,520 +1157,510 @@ amii_get_ext_cmd(void) sys_errlist[errno] #endif #endif - ); - amii_addtopl(buf); - } - return; + ); + amii_addtopl(buf); } - win = amii_create_nhwindow(NHW_TEXT); + return; + } + win = amii_create_nhwindow(NHW_TEXT); - /* Set window title to file name */ - if (cw = amii_wins[win]) - cw->morestr = (char *) fn; + /* Set window title to file name */ + if (cw = amii_wins[win]) + cw->morestr = (char *) fn; - while (dlb_fgets(buf, sizeof(buf), fp) != NULL) { - if (t = index(buf, '\n')) - *t = 0; - amii_putstr(win, 0, buf); + while (dlb_fgets(buf, sizeof(buf), fp) != NULL) { + if (t = index(buf, '\n')) + *t = 0; + amii_putstr(win, 0, buf); + } + dlb_fclose(fp); + + /* If there were lines in the file, display those lines */ + if (amii_wins[win]->cury > 0) + amii_display_nhwindow(win, TRUE); + + amii_wins[win]->morestr = NULL; /* don't free title string */ + amii_destroy_nhwindow(win); +} + +/* Put a 3-D motif border around the gadget. String gadgets or those + * which do not have highlighting are rendered down. Boolean gadgets + * are rendered in the up position by default. + */ +void +SetBorder(gd) +register struct Gadget * gd; +{ + register struct Border *bp; + register short *sp; + register int i, inc = -1, dec = -1; + int borders = 6; + int hipen = sysflags.amii_dripens[SHINEPEN], + shadowpen = sysflags.amii_dripens[SHADOWPEN]; +#ifdef INTUI_NEW_LOOK + struct DrawInfo *dip; +#endif + +#ifdef INTUI_NEW_LOOK + if (IntuitionBase->LibNode.lib_Version >= 37) { + if ((dip = GetScreenDrawInfo(HackScreen)) != 0) { + hipen = dip->dri_Pens[SHINEPEN]; + shadowpen = dip->dri_Pens[SHADOWPEN]; + FreeScreenDrawInfo(HackScreen, dip); } - dlb_fclose(fp); - - /* If there were lines in the file, display those lines */ - - if (amii_wins[win]->cury > 0) - amii_display_nhwindow(win, TRUE); - - amii_wins[win]->morestr = NULL; /* don't free title string */ - amii_destroy_nhwindow(win); + } +#endif + /* Allocate two border structures one for up image and one for down + * image, plus vector arrays for the border lines. + */ + if (gd->GadgetType == STRGADGET) + borders = 12; + if ((bp = (struct Border *) alloc(((sizeof (struct Border) * 2) + + (sizeof (short) * borders)) * 2)) + == NULL) { + return; } - /* Put a 3-D motif border around the gadget. String gadgets or those - * which do not have highlighting are rendered down. Boolean gadgets - * are rendered in the up position by default. + /* For a string gadget, we expand the border beyond the area where + * the text will be entered. */ - void SetBorder(gd) register struct Gadget * gd; - { - register struct Border *bp; - register short *sp; - register int i, inc = -1, dec = -1; - int borders = 6; - int hipen = sysflags.amii_dripens[SHINEPEN], - shadowpen = sysflags.amii_dripens[SHADOWPEN]; -#ifdef INTUI_NEW_LOOK - struct DrawInfo *dip; -#endif + /* Remove any special rendering flags to avoid confusing intuition */ + gd->Flags &= ~(GADGHIGHBITS | GADGIMAGE); -#ifdef INTUI_NEW_LOOK - if (IntuitionBase->LibNode.lib_Version >= 37) { - if (dip = GetScreenDrawInfo(HackScreen)) { - hipen = dip->dri_Pens[SHINEPEN]; - shadowpen = dip->dri_Pens[SHADOWPEN]; - FreeScreenDrawInfo(HackScreen, dip); - } + sp = (short *) (bp + 4); + if (gd->GadgetType == STRGADGET + || (gd->GadgetType == BOOLGADGET + && (gd->Flags & GADGHIGHBITS) == GADGHNONE)) { + sp[0] = -1; + sp[1] = gd->Height - 1; + sp[2] = -1; + sp[3] = -1; + sp[4] = gd->Width - 1; + sp[5] = -1; + + sp[6] = gd->Width + 1; + sp[7] = -2; + sp[8] = gd->Width + 1; + sp[9] = gd->Height + 1; + sp[10] = -2; + sp[11] = gd->Height + 1; + + sp[12] = -2; + sp[13] = gd->Height; + sp[14] = -2; + sp[15] = -2; + sp[16] = gd->Width; + sp[17] = -2; + sp[18] = gd->Width; + sp[19] = gd->Height; + sp[20] = -2; + sp[21] = gd->Height; + + for (i = 0; i < 3; ++i) { + bp[i].LeftEdge = bp[i].TopEdge = -1; + bp[i].FrontPen = (i == 0 || i == 1) ? shadowpen : hipen; + + /* Have to use JAM2 so that the old colors disappear. */ + bp[i].BackPen = C_BLACK; + bp[i].DrawMode = JAM2; + bp[i].Count = (i == 0 || i == 1) ? 3 : 5; + bp[i].XY = &sp[i * 6]; + bp[i].NextBorder = (i == 2) ? NULL : &bp[i + 1]; } -#endif - /* Allocate two border structures one for up image and one for down - * image, plus vector arrays for the border lines. + + /* bp[0] and bp[1] two pieces for the up image */ + gd->GadgetRender = (APTR) bp; + + /* No image change for select */ + gd->SelectRender = (APTR) bp; + + gd->LeftEdge++; + gd->TopEdge++; + gd->Flags |= GADGHCOMP; + } else { + /* Create the border vector values for up and left side, and + * also the lower and right side. */ + sp[0] = dec; + sp[1] = gd->Height + inc; + sp[2] = dec; + sp[3] = dec; + sp[4] = gd->Width + inc; + sp[5] = dec; - if (gd->GadgetType == STRGADGET) - borders = 12; + sp[6] = gd->Width + inc; + sp[7] = dec; + sp[8] = gd->Width + inc; + sp[9] = gd->Height + inc; + sp[10] = dec; + sp[11] = gd->Height + inc; - if ((bp = (struct Border *) alloc(((sizeof(struct Border) * 2) - + (sizeof(short) * borders)) * 2)) - == NULL) { + /* We are creating 4 sets of borders, the two sides of the + * rectangle share the border vectors with the opposite image, + * but specify different colors. + */ + for (i = 0; i < 4; ++i) { + bp[i].TopEdge = bp[i].LeftEdge = 0; + + /* A GADGHNONE is always down */ + if (gd->GadgetType == BOOLGADGET + && (gd->Flags & GADGHIGHBITS) != GADGHNONE) { + bp[i].FrontPen = (i == 1 || i == 2) ? shadowpen : hipen; + } else { + bp[i].FrontPen = (i == 1 || i == 3) ? hipen : shadowpen; + } + + /* Have to use JAM2 so that the old colors disappear. */ + bp[i].BackPen = C_BLACK; + bp[i].DrawMode = JAM2; + bp[i].Count = 3; + bp[i].XY = &sp[6 * ((i & 1) != 0)]; + bp[i].NextBorder = (i == 1 || i == 3) ? NULL : &bp[i + 1]; + } + + /* bp[0] and bp[1] two pieces for the up image */ + gd->GadgetRender = (APTR) bp; + + /* bp[2] and bp[3] two pieces for the down image */ + gd->SelectRender = (APTR)(bp + 2); + gd->Flags |= GADGHIMAGE; + } +} + +/* Following function copied from wintty.c; + Modified slightly to fit amiga needs */ +void +amii_player_selection() +{ + int i, k, n; + char pick4u = 'n', thisch, lastch = 0; + char pbuf[QBUFSZ], plbuf[QBUFSZ], rolenamebuf[QBUFSZ]; + winid win; + anything any; + menu_item *selected = 0; + + rigid_role_checks(); + + /* Should we randomly pick for the player? */ + if (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE + || flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE) { + char *prompt; + + prompt = build_plselection_prompt(pbuf, QBUFSZ, + flags.initrole, flags.initrace, + flags.initgend, flags.initalign); + pline("%s", prompt); + do { /* loop until we get valid input */ + cursor_on(WIN_MESSAGE); + pick4u = lowc(WindowGetchar()); + cursor_off(WIN_MESSAGE); + if (index(quitchars, pick4u)) + pick4u = 'y'; + } while (!index(ynqchars, pick4u)); + pbuf[0] = pick4u; + pbuf[1] = 0; + amii_addtopl(pbuf); + + if (pick4u != 'y' && pick4u != 'n') { + give_up: /* Quit */ + if (selected) + free((genericptr_t) selected); + clearlocks(); + exit_nhwindows(NULL); + terminate(0); + /*NOTREACHED*/ return; } - - /* For a string gadget, we expand the border beyond the area where - * the text will be entered. - */ - - /* Remove any special rendering flags to avoid confusing intuition - */ - - gd->Flags &= ~(GADGHIGHBITS | GADGIMAGE); - - sp = (short *) (bp + 4); - if (gd->GadgetType == STRGADGET - || (gd->GadgetType == BOOLGADGET - && (gd->Flags & GADGHIGHBITS) == GADGHNONE)) { - sp[0] = -1; - sp[1] = gd->Height - 1; - sp[2] = -1; - sp[3] = -1; - sp[4] = gd->Width - 1; - sp[5] = -1; - - sp[6] = gd->Width + 1; - sp[7] = -2; - sp[8] = gd->Width + 1; - sp[9] = gd->Height + 1; - sp[10] = -2; - sp[11] = gd->Height + 1; - - sp[12] = -2; - sp[13] = gd->Height; - sp[14] = -2; - sp[15] = -2; - sp[16] = gd->Width; - sp[17] = -2; - sp[18] = gd->Width; - sp[19] = gd->Height; - sp[20] = -2; - sp[21] = gd->Height; - - for (i = 0; i < 3; ++i) { - bp[i].LeftEdge = bp[i].TopEdge = -1; - bp[i].FrontPen = (i == 0 || i == 1) ? shadowpen : hipen; - - /* Have to use JAM2 so that the old colors disappear. */ - bp[i].BackPen = C_BLACK; - bp[i].DrawMode = JAM2; - bp[i].Count = (i == 0 || i == 1) ? 3 : 5; - bp[i].XY = &sp[i * 6]; - bp[i].NextBorder = (i == 2) ? NULL : &bp[i + 1]; - } - - /* bp[0] and bp[1] two pieces for the up image */ - gd->GadgetRender = (APTR) bp; - - /* No image change for select */ - gd->SelectRender = (APTR) bp; - - gd->LeftEdge++; - gd->TopEdge++; - gd->Flags |= GADGHCOMP; - } else { - /* Create the border vector values for up and left side, and - * also the lower and right side. - */ - - sp[0] = dec; - sp[1] = gd->Height + inc; - sp[2] = dec; - sp[3] = dec; - sp[4] = gd->Width + inc; - sp[5] = dec; - - sp[6] = gd->Width + inc; - sp[7] = dec; - sp[8] = gd->Width + inc; - sp[9] = gd->Height + inc; - sp[10] = dec; - sp[11] = gd->Height + inc; - - /* We are creating 4 sets of borders, the two sides of the - * rectangle share the border vectors with the opposite image, - * but specify different colors. - */ - - for (i = 0; i < 4; ++i) { - bp[i].TopEdge = bp[i].LeftEdge = 0; - - /* A GADGHNONE is always down */ - - if (gd->GadgetType == BOOLGADGET - && (gd->Flags & GADGHIGHBITS) != GADGHNONE) { - bp[i].FrontPen = (i == 1 || i == 2) ? shadowpen : hipen; - } else { - bp[i].FrontPen = (i == 1 || i == 3) ? hipen : shadowpen; - } - - /* Have to use JAM2 so that the old colors disappear. */ - bp[i].BackPen = C_BLACK; - bp[i].DrawMode = JAM2; - bp[i].Count = 3; - bp[i].XY = &sp[6 * ((i & 1) != 0)]; - bp[i].NextBorder = (i == 1 || i == 3) ? NULL : &bp[i + 1]; - } - - /* bp[0] and bp[1] two pieces for the up image */ - gd->GadgetRender = (APTR) bp; - - /* bp[2] and bp[3] two pieces for the down image */ - gd->SelectRender = (APTR)(bp + 2); - gd->Flags |= GADGHIMAGE; - } } - /* Following function copied from wintty.c */ - /* Modified slightly to fit amiga needs */ + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, + flags.initgend, flags.initalign); - void amii_player_selection() - { - int i, k, n; - char pick4u = 'n', thisch, lastch = 0; - char pbuf[QBUFSZ], plbuf[QBUFSZ], rolenamebuf[QBUFSZ]; - winid win; - anything any; - menu_item *selected = 0; - - rigid_role_checks(); - - /* Should we randomly pick for the player? */ - if (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE - || flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE) { - char *prompt = build_plselection_prompt( - pbuf, QBUFSZ, flags.initrole, flags.initrace, flags.initgend, - flags.initalign); - pline("%s", prompt); - do { /* loop until we get valid input */ - cursor_on(WIN_MESSAGE); - pick4u = lowc(WindowGetchar()); - cursor_off(WIN_MESSAGE); - if (index(quitchars, pick4u)) - pick4u = 'y'; - } while (!index(ynqchars, pick4u)); - pbuf[0] = pick4u; - pbuf[1] = 0; - amii_addtopl(pbuf); - - if (pick4u != 'y' && pick4u != 'n') { - give_up: /* Quit */ - if (selected) - free((genericptr_t) selected); - clearlocks(); - exit_nhwindows(NULL); - terminate(0); - /*NOTREACHED*/ - return; + /* Select a role, if necessary */ + /* we'll try to be compatible with pre-selected race/gender/alignment, + * but may not succeed */ + if (flags.initrole < 0) { + /* Process the choice */ + if (pick4u == 'y' || flags.initrole == ROLE_RANDOM + || flags.randomall) { + /* Pick a random role */ + flags.initrole = pick_role(flags.initrace, flags.initgend, + flags.initalign, PICK_RANDOM); + if (flags.initrole < 0) { + amii_putstr(WIN_MESSAGE, 0, "Incompatible role!"); + flags.initrole = randrole(); } - } - - (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole, - flags.initrace, flags.initgend, - flags.initalign); - - /* Select a role, if necessary */ - /* we'll try to be compatible with pre-selected race/gender/alignment, - * but may not succeed */ - if (flags.initrole < 0) { - /* Process the choice */ - if (pick4u == 'y' || flags.initrole == ROLE_RANDOM - || flags.randomall) { - /* Pick a random role */ - flags.initrole = pick_role(flags.initrace, flags.initgend, - flags.initalign, PICK_RANDOM); - if (flags.initrole < 0) { - amii_putstr(WIN_MESSAGE, 0, "Incompatible role!"); - flags.initrole = randrole(); + } else { + /* Prompt for a role */ + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; roles[i].name.m; i++) { + if (ok_role(i, flags.initrace, flags.initgend, + flags.initalign)) { + any.a_int = i + 1; /* must be non-zero */ + thisch = lowc(roles[i].name.m[0]); + if (thisch == lastch) + thisch = highc(thisch); + if (flags.initgend != ROLE_NONE + && flags.initgend != ROLE_RANDOM) { + if (flags.initgend == 1 && roles[i].name.f) + Strcpy(rolenamebuf, roles[i].name.f); + else + Strcpy(rolenamebuf, roles[i].name.m); + } else { + if (roles[i].name.f) { + Strcpy(rolenamebuf, roles[i].name.m); + Strcat(rolenamebuf, "/"); + Strcat(rolenamebuf, roles[i].name.f); + } else + Strcpy(rolenamebuf, roles[i].name.m); + } + add_menu(win, NO_GLYPH, &any, thisch, 0, ATR_NONE, + an(rolenamebuf), MENU_UNSELECTED); + lastch = thisch; } - } else { - /* Prompt for a role */ + } + any.a_int = pick_role(flags.initrace, flags.initgend, + flags.initalign, PICK_RANDOM) + 1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randrole() + 1; + add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random", + MENU_UNSELECTED); + any.a_int = i + 1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit", + MENU_UNSELECTED); + Sprintf(pbuf, "Pick a role for your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + + /* Process the choice */ + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + flags.initrole = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, + flags.initgend, flags.initalign); + } + + /* Select a race, if necessary */ + /* force compatibility with role, try for compatibility with + * pre-selected gender/alignment */ + if (flags.initrace < 0 + || !validrace(flags.initrole, flags.initrace)) { + /* pre-selected race not valid */ + if (pick4u == 'y' || flags.initrace == ROLE_RANDOM + || flags.randomall) { + flags.initrace = pick_race(flags.initrole, flags.initgend, + flags.initalign, PICK_RANDOM); + if (flags.initrace < 0) { + amii_putstr(WIN_MESSAGE, 0, "Incompatible race!"); + flags.initrace = randrace(flags.initrole); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid races */ + n = 0; /* number valid */ + k = 0; /* valid race */ + for (i = 0; races[i].noun; i++) { + if (ok_race(flags.initrole, i, flags.initgend, + flags.initalign)) { + n++; + k = i; + } + } + if (n == 0) { + for (i = 0; races[i].noun; i++) { + if (validrace(flags.initrole, i)) { + n++; + k = i; + } + } + } + + /* Permit the user to pick, if there is more than one */ + if (n > 1) { win = create_nhwindow(NHW_MENU); start_menu(win); any.a_void = 0; /* zero out all bits */ - for (i = 0; roles[i].name.m; i++) { - if (ok_role(i, flags.initrace, flags.initgend, + for (i = 0; races[i].noun; i++) + if (ok_race(flags.initrole, i, flags.initgend, flags.initalign)) { any.a_int = i + 1; /* must be non-zero */ - thisch = lowc(roles[i].name.m[0]); - if (thisch == lastch) - thisch = highc(thisch); - if (flags.initgend != ROLE_NONE - && flags.initgend != ROLE_RANDOM) { - if (flags.initgend == 1 && roles[i].name.f) - Strcpy(rolenamebuf, roles[i].name.f); - else - Strcpy(rolenamebuf, roles[i].name.m); - } else { - if (roles[i].name.f) { - Strcpy(rolenamebuf, roles[i].name.m); - Strcat(rolenamebuf, "/"); - Strcat(rolenamebuf, roles[i].name.f); - } else - Strcpy(rolenamebuf, roles[i].name.m); - } - add_menu(win, NO_GLYPH, &any, thisch, 0, ATR_NONE, - an(rolenamebuf), MENU_UNSELECTED); - lastch = thisch; + add_menu(win, NO_GLYPH, &any, races[i].noun[0], 0, + ATR_NONE, races[i].noun, + MENU_UNSELECTED); } - } - any.a_int = pick_role(flags.initrace, flags.initgend, + any.a_int = pick_race(flags.initrole, flags.initgend, flags.initalign, PICK_RANDOM) + 1; if (any.a_int == 0) /* must be non-zero */ - any.a_int = randrole() + 1; + any.a_int = randrace(flags.initrole) + 1; add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random", MENU_UNSELECTED); any.a_int = i + 1; /* must be non-zero */ add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit", MENU_UNSELECTED); - Sprintf(pbuf, "Pick a role for your %s", plbuf); + Sprintf(pbuf, "Pick the race of your %s", plbuf); end_menu(win, pbuf); n = select_menu(win, PICK_ONE, &selected); destroy_nhwindow(win); - - /* Process the choice */ if (n != 1 || selected[0].item.a_int == any.a_int) goto give_up; /* Selected quit */ - flags.initrole = selected[0].item.a_int - 1; + k = selected[0].item.a_int - 1; free((genericptr_t) selected), selected = 0; } - (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole, - flags.initrace, flags.initgend, - flags.initalign); + flags.initrace = k; } + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, + flags.initgend, flags.initalign); + } - /* Select a race, if necessary */ - /* force compatibility with role, try for compatibility with - * pre-selected gender/alignment */ - if (flags.initrace < 0 - || !validrace(flags.initrole, flags.initrace)) { - /* pre-selected race not valid */ - if (pick4u == 'y' || flags.initrace == ROLE_RANDOM - || flags.randomall) { - flags.initrace = pick_race(flags.initrole, flags.initgend, - flags.initalign, PICK_RANDOM); - if (flags.initrace < 0) { - amii_putstr(WIN_MESSAGE, 0, "Incompatible race!"); - flags.initrace = randrace(flags.initrole); + /* Select a gender, if necessary */ + /* force compatibility with role/race, try for compatibility with + * pre-selected alignment */ + if (flags.initgend < 0 + || !validgend(flags.initrole, flags.initrace, flags.initgend)) { + /* pre-selected gender not valid */ + if (pick4u == 'y' || flags.initgend == ROLE_RANDOM + || flags.randomall) { + flags.initgend = pick_gend(flags.initrole, flags.initrace, + flags.initalign, PICK_RANDOM); + if (flags.initgend < 0) { + amii_putstr(WIN_MESSAGE, 0, "Incompatible gender!"); + flags.initgend = randgend(flags.initrole, flags.initrace); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid genders */ + n = 0; /* number valid */ + k = 0; /* valid gender */ + for (i = 0; i < ROLE_GENDERS; i++) { + if (ok_gend(flags.initrole, flags.initrace, i, + flags.initalign)) { + n++; + k = i; } - } else { /* pick4u == 'n' */ - /* Count the number of valid races */ - n = 0; /* number valid */ - k = 0; /* valid race */ - for (i = 0; races[i].noun; i++) { - if (ok_race(flags.initrole, i, flags.initgend, - flags.initalign)) { + } + if (n == 0) { + for (i = 0; i < ROLE_GENDERS; i++) { + if (validgend(flags.initrole, flags.initrace, i)) { n++; k = i; } } - if (n == 0) { - for (i = 0; races[i].noun; i++) { - if (validrace(flags.initrole, i)) { - n++; - k = i; - } - } - } - - /* Permit the user to pick, if there is more than one */ - if (n > 1) { - win = create_nhwindow(NHW_MENU); - start_menu(win); - any.a_void = 0; /* zero out all bits */ - for (i = 0; races[i].noun; i++) - if (ok_race(flags.initrole, i, flags.initgend, - flags.initalign)) { - any.a_int = i + 1; /* must be non-zero */ - add_menu(win, NO_GLYPH, &any, races[i].noun[0], 0, - ATR_NONE, races[i].noun, - MENU_UNSELECTED); - } - any.a_int = pick_race(flags.initrole, flags.initgend, - flags.initalign, PICK_RANDOM) + 1; - if (any.a_int == 0) /* must be non-zero */ - any.a_int = randrace(flags.initrole) + 1; - add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random", - MENU_UNSELECTED); - any.a_int = i + 1; /* must be non-zero */ - add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit", - MENU_UNSELECTED); - Sprintf(pbuf, "Pick the race of your %s", plbuf); - end_menu(win, pbuf); - n = select_menu(win, PICK_ONE, &selected); - destroy_nhwindow(win); - if (n != 1 || selected[0].item.a_int == any.a_int) - goto give_up; /* Selected quit */ - - k = selected[0].item.a_int - 1; - free((genericptr_t) selected), selected = 0; - } - flags.initrace = k; } - (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole, - flags.initrace, flags.initgend, - flags.initalign); - } - /* Select a gender, if necessary */ - /* force compatibility with role/race, try for compatibility with - * pre-selected alignment */ - if (flags.initgend < 0 - || !validgend(flags.initrole, flags.initrace, flags.initgend)) { - /* pre-selected gender not valid */ - if (pick4u == 'y' || flags.initgend == ROLE_RANDOM - || flags.randomall) { - flags.initgend = pick_gend(flags.initrole, flags.initrace, - flags.initalign, PICK_RANDOM); - if (flags.initgend < 0) { - amii_putstr(WIN_MESSAGE, 0, "Incompatible gender!"); - flags.initgend = randgend(flags.initrole, flags.initrace); - } - } else { /* pick4u == 'n' */ - /* Count the number of valid genders */ - n = 0; /* number valid */ - k = 0; /* valid gender */ - for (i = 0; i < ROLE_GENDERS; i++) { + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; i < ROLE_GENDERS; i++) if (ok_gend(flags.initrole, flags.initrace, i, flags.initalign)) { + any.a_int = i + 1; + add_menu(win, NO_GLYPH, &any, genders[i].adj[0], 0, + ATR_NONE, genders[i].adj, MENU_UNSELECTED); + } + any.a_int = pick_gend(flags.initrole, flags.initrace, + flags.initalign, PICK_RANDOM) + 1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randgend(flags.initrole, flags.initrace) + 1; + add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random", + MENU_UNSELECTED); + any.a_int = i + 1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit", + MENU_UNSELECTED); + Sprintf(pbuf, "Pick the gender of your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + k = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + flags.initgend = k; + } + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, + flags.initgend, flags.initalign); + } + + /* Select an alignment, if necessary */ + /* force compatibility with role/race/gender */ + if (flags.initalign < 0 + || !validalign(flags.initrole, flags.initrace, flags.initalign)) { + /* pre-selected alignment not valid */ + if (pick4u == 'y' || flags.initalign == ROLE_RANDOM + || flags.randomall) { + flags.initalign = pick_align(flags.initrole, flags.initrace, + flags.initgend, PICK_RANDOM); + if (flags.initalign < 0) { + amii_putstr(WIN_MESSAGE, 0, "Incompatible alignment!"); + flags.initalign = randalign(flags.initrole, flags.initrace); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid alignments */ + n = 0; /* number valid */ + k = 0; /* valid alignment */ + for (i = 0; i < ROLE_ALIGNS; i++) { + if (ok_align(flags.initrole, flags.initrace, + flags.initgend, i)) { + n++; + k = i; + } + } + if (n == 0) { + for (i = 0; i < ROLE_ALIGNS; i++) { + if (validalign(flags.initrole, flags.initrace, i)) { n++; k = i; } } - if (n == 0) { - for (i = 0; i < ROLE_GENDERS; i++) { - if (validgend(flags.initrole, flags.initrace, i)) { - n++; - k = i; - } - } - } - - /* Permit the user to pick, if there is more than one */ - if (n > 1) { - win = create_nhwindow(NHW_MENU); - start_menu(win); - any.a_void = 0; /* zero out all bits */ - for (i = 0; i < ROLE_GENDERS; i++) - if (ok_gend(flags.initrole, flags.initrace, i, - flags.initalign)) { - any.a_int = i + 1; - add_menu(win, NO_GLYPH, &any, genders[i].adj[0], - 0, ATR_NONE, genders[i].adj, - MENU_UNSELECTED); - } - any.a_int = pick_gend(flags.initrole, flags.initrace, - flags.initalign, PICK_RANDOM) + 1; - if (any.a_int == 0) /* must be non-zero */ - any.a_int = - randgend(flags.initrole, flags.initrace) + 1; - add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random", - MENU_UNSELECTED); - any.a_int = i + 1; /* must be non-zero */ - add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit", - MENU_UNSELECTED); - Sprintf(pbuf, "Pick the gender of your %s", plbuf); - end_menu(win, pbuf); - n = select_menu(win, PICK_ONE, &selected); - destroy_nhwindow(win); - if (n != 1 || selected[0].item.a_int == any.a_int) - goto give_up; /* Selected quit */ - - k = selected[0].item.a_int - 1; - free((genericptr_t) selected), selected = 0; - } - flags.initgend = k; } - (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole, - flags.initrace, flags.initgend, - flags.initalign); - } - /* Select an alignment, if necessary */ - /* force compatibility with role/race/gender */ - if (flags.initalign < 0 - || !validalign(flags.initrole, flags.initrace, flags.initalign)) { - /* pre-selected alignment not valid */ - if (pick4u == 'y' || flags.initalign == ROLE_RANDOM - || flags.randomall) { - flags.initalign = pick_align(flags.initrole, flags.initrace, - flags.initgend, PICK_RANDOM); - if (flags.initalign < 0) { - amii_putstr(WIN_MESSAGE, 0, "Incompatible alignment!"); - flags.initalign = - randalign(flags.initrole, flags.initrace); - } - } else { /* pick4u == 'n' */ - /* Count the number of valid alignments */ - n = 0; /* number valid */ - k = 0; /* valid alignment */ - for (i = 0; i < ROLE_ALIGNS; i++) { + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; i < ROLE_ALIGNS; i++) if (ok_align(flags.initrole, flags.initrace, flags.initgend, i)) { - n++; - k = i; + any.a_int = i + 1; + add_menu(win, NO_GLYPH, &any, aligns[i].adj[0], 0, + ATR_NONE, aligns[i].adj, MENU_UNSELECTED); } - } - if (n == 0) { - for (i = 0; i < ROLE_ALIGNS; i++) { - if (validalign(flags.initrole, flags.initrace, i)) { - n++; - k = i; - } - } - } + any.a_int = pick_align(flags.initrole, flags.initrace, + flags.initgend, PICK_RANDOM) + 1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randalign(flags.initrole, flags.initrace) + 1; + add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random", + MENU_UNSELECTED); + any.a_int = i + 1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit", + MENU_UNSELECTED); + Sprintf(pbuf, "Pick the alignment of your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ - /* Permit the user to pick, if there is more than one */ - if (n > 1) { - win = create_nhwindow(NHW_MENU); - start_menu(win); - any.a_void = 0; /* zero out all bits */ - for (i = 0; i < ROLE_ALIGNS; i++) - if (ok_align(flags.initrole, flags.initrace, - flags.initgend, i)) { - any.a_int = i + 1; - add_menu(win, NO_GLYPH, &any, aligns[i].adj[0], 0, - ATR_NONE, aligns[i].adj, - MENU_UNSELECTED); - } - any.a_int = pick_align(flags.initrole, flags.initrace, - flags.initgend, PICK_RANDOM) + 1; - if (any.a_int == 0) /* must be non-zero */ - any.a_int = - randalign(flags.initrole, flags.initrace) + 1; - add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random", - MENU_UNSELECTED); - any.a_int = i + 1; /* must be non-zero */ - add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit", - MENU_UNSELECTED); - Sprintf(pbuf, "Pick the alignment of your %s", plbuf); - end_menu(win, pbuf); - n = select_menu(win, PICK_ONE, &selected); - destroy_nhwindow(win); - if (n != 1 || selected[0].item.a_int == any.a_int) - goto give_up; /* Selected quit */ - - k = selected[0].item.a_int - 1; - free((genericptr_t) selected), selected = 0; - } - flags.initalign = k; + k = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; } + flags.initalign = k; } - /* Success! */ } + /* Success! */ +} #endif /* AMIGA_INTUITION */ From 4e05de9bff4028926850edf219e6fb2637a71cea Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 6 Aug 2017 15:41:31 +0300 Subject: [PATCH 31/39] Add whatis_moveskip option to change fast-moving cursor Previously the "fast-moving" when getting a target location was always by 8 units. If this option is on, fast-moving will instead skip the same map glyphs. This should be much more useful for blind players. --- doc/Guidebook.mn | 10 ++++++++++ doc/Guidebook.tex | 13 +++++++++++++ include/flag.h | 2 ++ src/cmd.c | 1 + src/do_name.c | 36 ++++++++++++++++++++++++++++++++---- src/options.c | 1 + 6 files changed, 59 insertions(+), 4 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 7b70d6dfa..9805e32c0 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -2948,6 +2948,11 @@ key. When getting a location on the map, and using a key to cycle through next and previous targets, use a menu instead to pick a target. (default off) +.lp whatis_moveskip +When getting a location on the map, and using shifted movement keys or +meta-digit keys to fast-move, instead of moving 8 units at a time, +move by skipping the same glyphs. +(default off) .lp windowtype Select which windowing system to use, such as ``tty'' or ``X11'' (default depends on version). @@ -3259,6 +3264,8 @@ When asked for a location, the key to go to next closest object. Default is 'o'. When asked for a location, the key to go to previous closest object. Default is 'O'. .lp getpos.menu When asked for a location, and using one of the next or previous keys to cycle through targets, toggle showing a menu instead. Default is '!'. +.lp getpos.moveskip +When asked for a location, and using the shifted movement keys or meta-digit keys to fast-move around, move by skipping the same glyphs instead of by 8 units. Default is '*'. .lp getpos.filter When asked for a location, change the filtering mode when using one of the next or previous keys to cycle through targets. Toggles between no filtering, in view @@ -3746,6 +3753,9 @@ relative to your character. .lp whatis_filter:area When targeting with cursor, filter possible locations so only those in the same area (eg. same room, or same corridor) are considered. +.lp whatis_moveskip +When targeting with cursor and using fast-move, skip the same glyphs instead +of moving 8 units at a time. .lp nostatus_updates Prevent updates to the status lines at the bottom of the screen, if your screen-reader reads those lines. The same information can be diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 7162b909c..c09cc7d1e 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -3587,6 +3587,12 @@ When getting a location on the map, and using a key to cycle through next and previous targets, use a menu instead to pick a target. (default off) %.lp +\item[\ib{whatis\verb+_+moveskip}] +When getting a location on the map, and using shifted movement keys or +meta-digit keys to fast-move, instead of moving 8 units at a time, +move by skipping the same glyphs. +(default off) +%.lp \item[\ib{windowtype}] Select which windowing system to use, such as ``{\tt tty}'' or ``{\tt X11}'' (default depends on version). @@ -3991,6 +3997,9 @@ When asked for a location, the key to go to previous closest object. Default is \item{\bb{getpos.menu}} When asked for a location, and using one of the next or previous keys to cycle through targets, toggle showing a menu instead. Default is '{\tt !}'. %.lp +\item{\bb{getpos.moveskip}} +When asked for a location, and using the shifted movement keys or meta-digit keys to fast-move around, move by skipping the same glyphs instead of by 8 units. Default is ``{\tt *}''. +%.lp \item{\bb{getpos.filter}} When asked for a location, change the filtering mode when using one of the next or previous keys to cycle through targets. Toggles between no filtering, in view only, and in the same area only. Default is '{\tt "}'. %.lp @@ -4555,6 +4564,10 @@ relative to your character. When targeting with cursor, filter possible locations so only those in the same area (eg. same room, or same corridor) are considered. %.lp +\item[\ib{whatis\verb+_+moveskip}] +When targeting with cursor and using fast-move, skip the same glyphs instead +of moving 8 units at a time. +%.lp \item[\ib{nostatus\verb+_+updates}] Prevent updates to the status lines at the bottom of the screen, if your screen-reader reads those lines. The same information can be diff --git a/include/flag.h b/include/flag.h index 6d5e438b5..ac3844fc3 100644 --- a/include/flag.h +++ b/include/flag.h @@ -204,6 +204,7 @@ struct instance_flags { boolean getloc_travelmode; int getloc_filter; /* GFILTER_foo */ boolean getloc_usemenu; + boolean getloc_moveskip; coord travelcc; /* coordinates for travel_cache */ boolean window_inited; /* true if init_nhwindows() completed */ boolean vision_inited; /* true if vision is ready */ @@ -496,6 +497,7 @@ enum nh_keyfunc { NHKF_GETPOS_HELP, NHKF_GETPOS_MENU, NHKF_GETPOS_LIMITVIEW, + NHKF_GETPOS_MOVESKIP, NUM_NHKF }; diff --git a/src/cmd.c b/src/cmd.c index 53a800bd9..cd7faedc8 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -3749,6 +3749,7 @@ struct { { NHKF_GETPOS_INTERESTING_PREV, 'A', "getpos.all.prev" }, { NHKF_GETPOS_HELP, '?', "getpos.help" }, { NHKF_GETPOS_LIMITVIEW, '"', "getpos.filter" }, + { NHKF_GETPOS_MOVESKIP, '*', "getpos.moveskip" }, { NHKF_GETPOS_MENU, '!', "getpos.menu" } }; diff --git a/src/do_name.c b/src/do_name.c index 16818c0d9..7dec3fb7d 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -90,13 +90,17 @@ const char *goal; char sbuf[BUFSZ]; boolean doing_what_is; winid tmpwin = create_nhwindow(NHW_MENU); + const char *const fastmovemode[2] = { "8 units at a time", + "skipping same glyphs" }; Sprintf(sbuf, "Use '%c', '%c', '%c', '%c' to move the cursor to %s.", /* hjkl */ Cmd.move_W, Cmd.move_S, Cmd.move_N, Cmd.move_E, goal); putstr(tmpwin, 0, sbuf); - putstr(tmpwin, 0, - "Use 'H', 'J', 'K', 'L' to move the cursor 8 units at a time."); + Sprintf(sbuf, + "Use 'H', 'J', 'K', 'L' to fast-move the cursor, %s.", + fastmovemode[iflags.getloc_moveskip]); + putstr(tmpwin, 0, sbuf); putstr(tmpwin, 0, "Or enter a background symbol (ex. '<')."); Sprintf(sbuf, "Use '%s' to move the cursor on yourself.", visctrl(Cmd.spkeys[NHKF_GETPOS_SELF])); @@ -129,6 +133,11 @@ const char *goal; visctrl(Cmd.spkeys[NHKF_GETPOS_INTERESTING_PREV]), GLOC_INTERESTING); } + Sprintf(sbuf, "Use '%s' to change fast-move mode to %s.", + visctrl(Cmd.spkeys[NHKF_GETPOS_MOVESKIP]), + fastmovemode[!iflags.getloc_moveskip]); + putstr(tmpwin, 0, sbuf); + Sprintf(sbuf, "Use '%s' to toggle menu listing for possible targets.", visctrl(Cmd.spkeys[NHKF_GETPOS_MENU])); putstr(tmpwin, 0, sbuf); @@ -720,8 +729,23 @@ const char *goal; } else if (Cmd.alphadirchars[i] == lowc((char) c) || (Cmd.num_pad && Cmd.dirchars[i] == (c & 0177))) { /* a shifted movement letter or Meta-digit */ - dx = 8 * xdir[i]; - dy = 8 * ydir[i]; + if (iflags.getloc_moveskip) { + /* skip same glyphs */ + int glyph = glyph_at(cx, cy); + + dx = xdir[i]; + dy = ydir[i]; + while (isok(cx + dx, cy + dy) + && glyph == glyph_at(cx + dx, cy + dy) + && isok(cx + dx+xdir[i], cy+dy+ydir[i]) + && glyph == glyph_at(cx + dx+xdir[i], cy + dy+ydir[i])) { + dx += xdir[i]; + dy += ydir[i]; + } + } else { + dx = 8 * xdir[i]; + dy = 8 * ydir[i]; + } } else continue; @@ -801,6 +825,10 @@ const char *goal; cx = u.ux; cy = u.uy; goto nxtc; + } else if (c == Cmd.spkeys[NHKF_GETPOS_MOVESKIP]) { + iflags.getloc_moveskip = !iflags.getloc_moveskip; + pline("%skipping over similar terrain when fastmoving the cursor.", + iflags.getloc_moveskip ? "S" : "Not s"); } else if ((cp = index(mMoOdDxX, c)) != 0) { /* 'm|M', 'o|O', &c */ /* nearest or farthest monster or object or door or unexplored */ int gtmp = (int) (cp - mMoOdDxX), /* 0..7 */ diff --git a/src/options.c b/src/options.c index e4c878dd0..0bfacc09d 100644 --- a/src/options.c +++ b/src/options.c @@ -233,6 +233,7 @@ static struct Bool_Opt { { "vt_tiledata", (boolean *) 0, FALSE, SET_IN_FILE }, #endif { "whatis_menu", &iflags.getloc_usemenu, FALSE, SET_IN_GAME }, + { "whatis_moveskip", &iflags.getloc_moveskip, FALSE, SET_IN_GAME }, { "wizweight", &iflags.wizweight, FALSE, SET_IN_WIZGAME }, { "wraptext", &iflags.wc2_wraptext, FALSE, SET_IN_GAME }, #ifdef ZEROCOMP From 1f671b962c14a98c6bd45a807d38ec233eb55eda Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 6 Aug 2017 20:27:48 +0300 Subject: [PATCH 32/39] Demangle clang formatted code --- include/winprocs.h | 155 ++++++++++++++++++++------------------------- 1 file changed, 68 insertions(+), 87 deletions(-) diff --git a/include/winprocs.h b/include/winprocs.h index b76d9e1de..c12f2c5d5 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -177,91 +177,70 @@ extern * Window port preference capability bits. * Some day this might be better in its own wincap.h file. */ -#define WC_COLOR 0x01L /* 01 Port can display things in color */ -#define WC_HILITE_PET 0x02L /* 02 supports hilite pet */ -#define WC_ASCII_MAP 0x04L /* 03 supports an ascii map */ -#define WC_TILED_MAP 0x08L /* 04 supports a tiled map */ -#define WC_PRELOAD_TILES 0x10L /* 05 supports pre-loading tiles */ -#define WC_TILE_WIDTH 0x20L /* 06 prefer this width of tile */ -#define WC_TILE_HEIGHT 0x40L /* 07 prefer this height of tile */ -#define WC_TILE_FILE 0x80L /* 08 alternative tile file name */ -#define WC_INVERSE 0x100L /* 09 Port supports inverse video */ -#define WC_ALIGN_MESSAGE \ - 0x200L /* 10 supports message alignmt top|b|l|r */ -#define WC_ALIGN_STATUS 0x400L /* 11 supports status alignmt top|b|l|r */ -#define WC_VARY_MSGCOUNT \ - 0x800L /* 12 supports varying message window */ -#define WC_FONT_MAP 0x1000L /* 13 supports specification of map win font */ -#define WC_FONT_MESSAGE \ - 0x2000L /* 14 supports specification of msg win font */ -#define WC_FONT_STATUS 0x4000L /* 15 supports specification of sts win font \ - */ -#define WC_FONT_MENU 0x8000L /* 16 supports specification of mnu win font */ -#define WC_FONT_TEXT 0x10000L /* 17 supports specification of txt win font \ - */ -#define WC_FONTSIZ_MAP \ - 0x20000L /* 18 supports specification of map win font */ -#define WC_FONTSIZ_MESSAGE \ - 0x40000L /* 19 supports specification of msg win font */ -#define WC_FONTSIZ_STATUS \ - 0x80000L /* 20 supports specification of sts win font */ -#define WC_FONTSIZ_MENU \ - 0x100000L /* 21 supports specification of mnu win font */ -#define WC_FONTSIZ_TEXT \ - 0x200000L /* 22 supports specification of txt win font */ -#define WC_SCROLL_MARGIN \ - 0x400000L /* 23 supports setting scroll margin for map */ -#define WC_SPLASH_SCREEN \ - 0x800000L /* 24 supports display of splash screen */ -#define WC_POPUP_DIALOG \ - 0x1000000L /* 25 supports queries in pop dialogs */ -#define WC_SCROLL_AMOUNT \ - 0x2000000L /* 26 scroll this amount at scroll margin */ -#define WC_EIGHT_BIT_IN \ - 0x4000000L /* 27 8-bit character input */ -#define WC_PERM_INVENT \ - 0x8000000L /* 28 8-bit character input */ -#define WC_MAP_MODE \ - 0x10000000L /* 29 map_mode option */ -#define WC_WINDOWCOLORS \ - 0x20000000L /* 30 background color for message window */ -#define WC_PLAYER_SELECTION \ - 0x40000000L /* 31 background color for message window */ +/* clang-format off */ +#define WC_COLOR 0x01L /* 01 Port can display things in color */ +#define WC_HILITE_PET 0x02L /* 02 supports hilite pet */ +#define WC_ASCII_MAP 0x04L /* 03 supports an ascii map */ +#define WC_TILED_MAP 0x08L /* 04 supports a tiled map */ +#define WC_PRELOAD_TILES 0x10L /* 05 supports pre-loading tiles */ +#define WC_TILE_WIDTH 0x20L /* 06 prefer this width of tile */ +#define WC_TILE_HEIGHT 0x40L /* 07 prefer this height of tile */ +#define WC_TILE_FILE 0x80L /* 08 alternative tile file name */ +#define WC_INVERSE 0x100L /* 09 Port supports inverse video */ +#define WC_ALIGN_MESSAGE 0x200L /* 10 supports message alignmt top|b|l|r */ +#define WC_ALIGN_STATUS 0x400L /* 11 supports status alignmt top|b|l|r */ +#define WC_VARY_MSGCOUNT 0x800L /* 12 supports varying message window */ +#define WC_FONT_MAP 0x1000L /* 13 supports specification of map win font */ +#define WC_FONT_MESSAGE 0x2000L /* 14 supports specification of msg win font */ +#define WC_FONT_STATUS 0x4000L /* 15 supports specification of sts win font */ +#define WC_FONT_MENU 0x8000L /* 16 supports specification of mnu win font */ +#define WC_FONT_TEXT 0x10000L /* 17 supports specification of txt win font */ +#define WC_FONTSIZ_MAP 0x20000L /* 18 supports specification of map win font */ +#define WC_FONTSIZ_MESSAGE 0x40000L /* 19 supports specification of msg win font */ +#define WC_FONTSIZ_STATUS 0x80000L /* 20 supports specification of sts win font */ +#define WC_FONTSIZ_MENU 0x100000L /* 21 supports specification of mnu win font */ +#define WC_FONTSIZ_TEXT 0x200000L /* 22 supports specification of txt win font */ +#define WC_SCROLL_MARGIN 0x400000L /* 23 supports setting scroll margin for map */ +#define WC_SPLASH_SCREEN 0x800000L /* 24 supports display of splash screen */ +#define WC_POPUP_DIALOG 0x1000000L /* 25 supports queries in pop dialogs */ +#define WC_SCROLL_AMOUNT 0x2000000L /* 26 scroll this amount at scroll margin */ +#define WC_EIGHT_BIT_IN 0x4000000L /* 27 8-bit character input */ +#define WC_PERM_INVENT 0x8000000L /* 28 8-bit character input */ +#define WC_MAP_MODE 0x10000000L /* 29 map_mode option */ +#define WC_WINDOWCOLORS 0x20000000L /* 30 background color for message window */ +#define WC_PLAYER_SELECTION 0x40000000L /* 31 background color for message window */ #ifdef NHSTDC -#define WC_MOUSE_SUPPORT \ - 0x80000000UL /* 32 mouse support */ +#define WC_MOUSE_SUPPORT 0x80000000UL /* 32 mouse support */ #else -#define WC_MOUSE_SUPPORT \ - 0x80000000L /* 32 mouse support */ +#define WC_MOUSE_SUPPORT 0x80000000L /* 32 mouse support */ #endif -/* no free bits */ + /* no free bits */ -#define WC2_FULLSCREEN 0x01L /* 01 display full screen */ -#define WC2_SOFTKEYBOARD 0x02L /* 02 software keyboard */ -#define WC2_WRAPTEXT 0x04L /* 03 wrap long lines of text */ -#define WC2_HILITE_STATUS \ - 0x08L /* 04 hilite fields in status */ -#define WC2_SELECTSAVED 0x10L /* 05 saved game selection menu */ -#define WC2_DARKGRAY 0x20L /* 06 use bold black for black glyphs */ - /* 26 free bits */ +#define WC2_FULLSCREEN 0x01L /* 01 display full screen */ +#define WC2_SOFTKEYBOARD 0x02L /* 02 software keyboard */ +#define WC2_WRAPTEXT 0x04L /* 03 wrap long lines of text */ +#define WC2_HILITE_STATUS 0x08L /* 04 hilite fields in status */ +#define WC2_SELECTSAVED 0x10L /* 05 saved game selection menu */ +#define WC2_DARKGRAY 0x20L /* 06 use bold black for black glyphs */ + /* 26 free bits */ -#define ALIGN_LEFT 1 -#define ALIGN_RIGHT 2 -#define ALIGN_TOP 3 +#define ALIGN_LEFT 1 +#define ALIGN_RIGHT 2 +#define ALIGN_TOP 3 #define ALIGN_BOTTOM 4 /* player_selection */ -#define VIA_DIALOG 0 +#define VIA_DIALOG 0 #define VIA_PROMPTS 1 /* map_mode settings - deprecated */ -#define MAP_MODE_TILES 0 -#define MAP_MODE_ASCII4x6 1 -#define MAP_MODE_ASCII6x8 2 -#define MAP_MODE_ASCII8x8 3 -#define MAP_MODE_ASCII16x8 4 -#define MAP_MODE_ASCII7x12 5 -#define MAP_MODE_ASCII8x12 6 +#define MAP_MODE_TILES 0 +#define MAP_MODE_ASCII4x6 1 +#define MAP_MODE_ASCII6x8 2 +#define MAP_MODE_ASCII8x8 3 +#define MAP_MODE_ASCII16x8 4 +#define MAP_MODE_ASCII7x12 5 +#define MAP_MODE_ASCII8x12 6 #define MAP_MODE_ASCII16x12 7 #define MAP_MODE_ASCII12x16 8 #define MAP_MODE_ASCII10x18 9 @@ -269,13 +248,13 @@ extern #define MAP_MODE_TILES_FIT_TO_SCREEN 11 #if 0 -#define WC_SND_SOUND 0x01L /* 01 Port has some sound capabilities */ +#define WC_SND_SOUND 0x01L /* 01 Port has some sound capabilities */ #define WC_SND_SPEAKER 0x02L /* 02 Sound supported via built-in speaker */ -#define WC_SND_STEREO 0x04L /* 03 Stereo sound supported */ -#define WC_SND_RAW 0x08L /* 04 Raw sound supported */ -#define WC_SND_WAVE 0x10L /* 05 Wave support */ -#define WC_SND_MIDI 0x20L /* 06 Midi support */ - /* 26 free bits */ +#define WC_SND_STEREO 0x04L /* 03 Stereo sound supported */ +#define WC_SND_RAW 0x08L /* 04 Raw sound supported */ +#define WC_SND_WAVE 0x10L /* 05 Wave support */ +#define WC_SND_MIDI 0x20L /* 06 Midi support */ + /* 26 free bits */ #endif struct wc_Opt { @@ -284,17 +263,17 @@ struct wc_Opt { }; /* role selection by player_selection(); this ought to be in the core... */ -#define RS_NAME 0 -#define RS_ROLE 1 -#define RS_RACE 2 -#define RS_GENDER 3 +#define RS_NAME 0 +#define RS_ROLE 1 +#define RS_RACE 2 +#define RS_GENDER 3 #define RS_ALGNMNT 4 -#define RS_filter 5 +#define RS_filter 5 #define RS_menu_arg(x) (ROLE_RANDOM - ((x) + 1)) /* 0..5 -> -3..-8 */ /* Choose_windows() may be called multiple times; these constants tell the * init function whether the window system is coming or going. */ -#define WININIT 0 +#define WININIT 0 #define WININIT_UNDO 1 #ifdef WINCHAIN @@ -310,7 +289,9 @@ struct wc_Opt { nextdata is the Xprivate* for the next link in the chain */ #define WINCHAIN_ALLOC 0 -#define WINCHAIN_INIT 1 +#define WINCHAIN_INIT 1 + +/* clang-format on */ #define CARGS void * From 152ddf4a4b0e1db4794e6902baf253d90f0e627f Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 6 Aug 2017 22:56:07 +0300 Subject: [PATCH 33/39] Unify strength string --- src/botl.c | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/botl.c b/src/botl.c index e6a771bdc..e28e8cc8b 100644 --- a/src/botl.c +++ b/src/botl.c @@ -13,6 +13,25 @@ const char *const enc_stat[] = { "", "Burdened", "Stressed", STATIC_OVL NEARDATA int mrank_sz = 0; /* loaded by max_rank_sz (from u_init) */ STATIC_DCL const char *NDECL(rank); +static char * +get_strength_str() +{ + static char buf[32]; + int st = ACURR(A_STR); + + if (st > 18) { + if (st > STR18(100)) + Sprintf(buf, "%2d", st - 100); + else if (st < STR18(100)) + Sprintf(buf, "18/%02d", st - 18); + else + Sprintf(buf, "18/**"); + } else + Sprintf(buf, "%-1d", st); + + return buf; +} + #if !defined(STATUS_VIA_WINDOWPORT) || defined(DUMPLOG) char * @@ -48,16 +67,9 @@ do_statusline1() j = (int) ((nb + 2) - newbot1); /* strlen(newbot1) but less computation */ if ((i - j) > 0) Sprintf(nb = eos(nb), "%*s", i - j, " "); /* pad with spaces */ - if (ACURR(A_STR) > 18) { - if (ACURR(A_STR) > STR18(100)) - Sprintf(nb = eos(nb), "St:%2d ", ACURR(A_STR) - 100); - else if (ACURR(A_STR) < STR18(100)) - Sprintf(nb = eos(nb), "St:18/%02d ", ACURR(A_STR) - 18); - else - Sprintf(nb = eos(nb), "St:18/** "); - } else - Sprintf(nb = eos(nb), "St:%-1d ", ACURR(A_STR)); - Sprintf(nb = eos(nb), "Dx:%-1d Co:%-1d In:%-1d Wi:%-1d Ch:%-1d", + + Sprintf(nb = eos(nb), "St:%s Dx:%-1d Co:%-1d In:%-1d Wi:%-1d Ch:%-1d", + get_strength_str(), ACURR(A_DEX), ACURR(A_CON), ACURR(A_INT), ACURR(A_WIS), ACURR(A_CHA)); Sprintf(nb = eos(nb), @@ -470,18 +482,8 @@ bot() valset[BL_TITLE] = TRUE; /* indicate val already set */ /* Strength */ - buf[0] = '\0'; blstats[idx][BL_STR].a.a_int = ACURR(A_STR); - if (ACURR(A_STR) > 18) { - if (ACURR(A_STR) > STR18(100)) - Sprintf(buf, "%2d", ACURR(A_STR) - 100); - else if (ACURR(A_STR) < STR18(100)) - Sprintf(buf, "18/%02d", ACURR(A_STR) - 18); - else - Sprintf(buf, "18/**"); - } else - Sprintf(buf, "%-1d", ACURR(A_STR)); - Strcpy(blstats[idx][BL_STR].val, buf); + Strcpy(blstats[idx][BL_STR].val, get_strength_str()); valset[BL_STR] = TRUE; /* indicate val already set */ /* Dexterity, constitution, intelligence, wisdom, charisma. */ From 1e15fdcb7739c3c27cdb3017ea9a679080589f14 Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 7 Aug 2017 14:27:15 -0700 Subject: [PATCH 34/39] rereformat winprocs.h Overriding clang-format was justified, but the result was too wide. Make the lines less that 80 characters. Also, WC_PERM_INVENT and WC_PLAYER_SELECTION had comments cloned from the preceding line. --- include/winprocs.h | 104 ++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 53 deletions(-) diff --git a/include/winprocs.h b/include/winprocs.h index c12f2c5d5..f3f4e870f 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 winprocs.h $NHDT-Date: 1433806582 2015/06/08 23:36:22 $ $NHDT-Branch: master $:$NHDT-Revision: 1.36 $ */ +/* NetHack 3.6 winprocs.h $NHDT-Date: 1502141230 2017/08/07 21:27:10 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.38 $ */ /* Copyright (c) David Cohrs, 1992 */ /* NetHack may be freely redistributed. See license for details. */ @@ -13,8 +13,7 @@ struct window_procs { * not start with '-'. Names starting with * '+' are reserved for processors. */ unsigned long wincap; /* window port capability options supported */ - unsigned long - wincap2; /* additional window port capability options supported */ + unsigned long wincap2; /* additional window port capability options */ void FDECL((*win_init_nhwindows), (int *, char **)); void NDECL((*win_player_selection)); void NDECL((*win_askname)); @@ -178,51 +177,51 @@ extern * Some day this might be better in its own wincap.h file. */ /* clang-format off */ -#define WC_COLOR 0x01L /* 01 Port can display things in color */ -#define WC_HILITE_PET 0x02L /* 02 supports hilite pet */ -#define WC_ASCII_MAP 0x04L /* 03 supports an ascii map */ -#define WC_TILED_MAP 0x08L /* 04 supports a tiled map */ -#define WC_PRELOAD_TILES 0x10L /* 05 supports pre-loading tiles */ -#define WC_TILE_WIDTH 0x20L /* 06 prefer this width of tile */ -#define WC_TILE_HEIGHT 0x40L /* 07 prefer this height of tile */ -#define WC_TILE_FILE 0x80L /* 08 alternative tile file name */ -#define WC_INVERSE 0x100L /* 09 Port supports inverse video */ -#define WC_ALIGN_MESSAGE 0x200L /* 10 supports message alignmt top|b|l|r */ -#define WC_ALIGN_STATUS 0x400L /* 11 supports status alignmt top|b|l|r */ -#define WC_VARY_MSGCOUNT 0x800L /* 12 supports varying message window */ -#define WC_FONT_MAP 0x1000L /* 13 supports specification of map win font */ -#define WC_FONT_MESSAGE 0x2000L /* 14 supports specification of msg win font */ -#define WC_FONT_STATUS 0x4000L /* 15 supports specification of sts win font */ -#define WC_FONT_MENU 0x8000L /* 16 supports specification of mnu win font */ -#define WC_FONT_TEXT 0x10000L /* 17 supports specification of txt win font */ -#define WC_FONTSIZ_MAP 0x20000L /* 18 supports specification of map win font */ -#define WC_FONTSIZ_MESSAGE 0x40000L /* 19 supports specification of msg win font */ -#define WC_FONTSIZ_STATUS 0x80000L /* 20 supports specification of sts win font */ -#define WC_FONTSIZ_MENU 0x100000L /* 21 supports specification of mnu win font */ -#define WC_FONTSIZ_TEXT 0x200000L /* 22 supports specification of txt win font */ -#define WC_SCROLL_MARGIN 0x400000L /* 23 supports setting scroll margin for map */ -#define WC_SPLASH_SCREEN 0x800000L /* 24 supports display of splash screen */ -#define WC_POPUP_DIALOG 0x1000000L /* 25 supports queries in pop dialogs */ -#define WC_SCROLL_AMOUNT 0x2000000L /* 26 scroll this amount at scroll margin */ -#define WC_EIGHT_BIT_IN 0x4000000L /* 27 8-bit character input */ -#define WC_PERM_INVENT 0x8000000L /* 28 8-bit character input */ -#define WC_MAP_MODE 0x10000000L /* 29 map_mode option */ -#define WC_WINDOWCOLORS 0x20000000L /* 30 background color for message window */ -#define WC_PLAYER_SELECTION 0x40000000L /* 31 background color for message window */ +#define WC_COLOR 0x00000001L /* 01 Port can display things in color */ +#define WC_HILITE_PET 0x00000002L /* 02 supports hilite pet */ +#define WC_ASCII_MAP 0x00000004L /* 03 supports an ascii map */ +#define WC_TILED_MAP 0x00000008L /* 04 supports a tiled map */ +#define WC_PRELOAD_TILES 0x00000010L /* 05 supports pre-loading tiles */ +#define WC_TILE_WIDTH 0x00000020L /* 06 prefer this width of tile */ +#define WC_TILE_HEIGHT 0x00000040L /* 07 prefer this height of tile */ +#define WC_TILE_FILE 0x00000080L /* 08 alternative tile file name */ +#define WC_INVERSE 0x00000100L /* 09 Port supports inverse video */ +#define WC_ALIGN_MESSAGE 0x00000200L /* 10 supports mesg alignment top|b|l|r */ +#define WC_ALIGN_STATUS 0x00000400L /* 11 supports status alignmt top|b|l|r */ +#define WC_VARY_MSGCOUNT 0x00000800L /* 12 supports varying message window */ +#define WC_FONT_MAP 0x00001000L /* 13 supports spec of map window font */ +#define WC_FONT_MESSAGE 0x00002000L /* 14 supports spec of message font */ +#define WC_FONT_STATUS 0x00004000L /* 15 supports spec of status font */ +#define WC_FONT_MENU 0x00008000L /* 16 supports spec of menu font */ +#define WC_FONT_TEXT 0x00010000L /* 17 supports spec of text window font */ +#define WC_FONTSIZ_MAP 0x00020000L /* 18 supports spec of map font size */ +#define WC_FONTSIZ_MESSAGE 0x040000L /* 19 supports spec of mesg font size */ +#define WC_FONTSIZ_STATUS 0x0080000L /* 20 supports spec of status font size */ +#define WC_FONTSIZ_MENU 0x00100000L /* 21 supports spec of menu font size */ +#define WC_FONTSIZ_TEXT 0x00200000L /* 22 supports spec of text font size */ +#define WC_SCROLL_MARGIN 0x00400000L /* 23 supports setting map scroll marg */ +#define WC_SPLASH_SCREEN 0x00800000L /* 24 supports display of splash screen */ +#define WC_POPUP_DIALOG 0x01000000L /* 25 supports queries in popup dialogs */ +#define WC_SCROLL_AMOUNT 0x02000000L /* 26 scroll this amount at scroll marg */ +#define WC_EIGHT_BIT_IN 0x04000000L /* 27 8-bit character input */ +#define WC_PERM_INVENT 0x08000000L /* 28 supports persistent inventory win */ +#define WC_MAP_MODE 0x10000000L /* 29 map_mode option */ +#define WC_WINDOWCOLORS 0x20000000L /* 30 background color for mesg window */ +#define WC_PLAYER_SELECTION 0x40000000L /* 31 supports player selection */ #ifdef NHSTDC -#define WC_MOUSE_SUPPORT 0x80000000UL /* 32 mouse support */ +#define WC_MOUSE_SUPPORT 0x80000000UL /* 32 mouse support */ #else -#define WC_MOUSE_SUPPORT 0x80000000L /* 32 mouse support */ +#define WC_MOUSE_SUPPORT 0x80000000L /* 32 mouse support */ #endif - /* no free bits */ + /* no free bits */ -#define WC2_FULLSCREEN 0x01L /* 01 display full screen */ -#define WC2_SOFTKEYBOARD 0x02L /* 02 software keyboard */ -#define WC2_WRAPTEXT 0x04L /* 03 wrap long lines of text */ -#define WC2_HILITE_STATUS 0x08L /* 04 hilite fields in status */ -#define WC2_SELECTSAVED 0x10L /* 05 saved game selection menu */ -#define WC2_DARKGRAY 0x20L /* 06 use bold black for black glyphs */ - /* 26 free bits */ +#define WC2_FULLSCREEN 0x0001L /* 01 display full screen */ +#define WC2_SOFTKEYBOARD 0x0002L /* 02 software keyboard */ +#define WC2_WRAPTEXT 0x0004L /* 03 wrap long lines of text */ +#define WC2_HILITE_STATUS 0x0008L /* 04 hilite fields in status */ +#define WC2_SELECTSAVED 0x0010L /* 05 saved game selection menu */ +#define WC2_DARKGRAY 0x0020L /* 06 use bold black for black glyphs */ + /* 26 free bits */ #define ALIGN_LEFT 1 #define ALIGN_RIGHT 2 @@ -248,13 +247,13 @@ extern #define MAP_MODE_TILES_FIT_TO_SCREEN 11 #if 0 -#define WC_SND_SOUND 0x01L /* 01 Port has some sound capabilities */ -#define WC_SND_SPEAKER 0x02L /* 02 Sound supported via built-in speaker */ -#define WC_SND_STEREO 0x04L /* 03 Stereo sound supported */ -#define WC_SND_RAW 0x08L /* 04 Raw sound supported */ -#define WC_SND_WAVE 0x10L /* 05 Wave support */ -#define WC_SND_MIDI 0x20L /* 06 Midi support */ - /* 26 free bits */ +#define WC_SND_SOUND 0x0001L /* 01 Port has some sound capabilities */ +#define WC_SND_SPEAKER 0x0002L /* 02 Sound supported via built-in speaker */ +#define WC_SND_STEREO 0x0004L /* 03 Stereo sound supported */ +#define WC_SND_RAW 0x0008L /* 04 Raw sound supported */ +#define WC_SND_WAVE 0x0010L /* 05 Wave support */ +#define WC_SND_MIDI 0x0020L /* 06 Midi support */ + /* 26 free bits */ #endif struct wc_Opt { @@ -302,8 +301,7 @@ struct chain_procs { * not start with '-'. Names starting with * '+' are reserved for processors. */ unsigned long wincap; /* window port capability options supported */ - unsigned long - wincap2; /* additional window port capability options supported */ + unsigned long wincap2; /* additional window port capability options */ void FDECL((*win_init_nhwindows), (CARGS, int *, char **)); void FDECL((*win_player_selection), (CARGS)); void FDECL((*win_askname), (CARGS)); From e8723df7788f382a1f2a81f652f0244a11cac01b Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 8 Aug 2017 18:58:25 -0700 Subject: [PATCH 35/39] grappling with water Reported directly to devteam: if applying a grappling hook towards a target past some water ended up pulling the hero toward the target, hero would drown without any chance of crawling out of the water. It used hurtle() to move, and hurtle assumed levitation so didn't check for entering pools of water except on the Plane of Water. --- doc/fixes36.1 | 2 ++ src/dothrow.c | 30 +++++++++++++++++++++++------- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index d2fb9b2ca..72b2d2d81 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -407,6 +407,8 @@ fix the 'A' command to have the 'D' command's fix for C331-1 (quirk for eating 1 tin from stack of N (for N >= 2) on shop's floor forced hero to buy 2 using a cursed whistle in a vault will summon the guard immediately throne room's throne is occupied by a king +using a grappling hook and getting pulled toward the target into water would + drown hero without any chance to crawl out Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/dothrow.c b/src/dothrow.c index 769be9370..a6825fcc9 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 dothrow.c $NHDT-Date: 1455140444 2016/02/10 21:40:44 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.118 $ */ +/* NetHack 3.6 dothrow.c $NHDT-Date: 1502243899 2017/08/09 01:58:19 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.125 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -448,7 +448,22 @@ genericptr_t arg; int x, y, dx, dy, x_change, y_change, err, i, prev_x, prev_y; boolean keep_going = TRUE; - /* Use Bresenham's Line Algorithm to walk from src to dest */ + /* Use Bresenham's Line Algorithm to walk from src to dest. + * + * This should be replaced with a more versatile algorithm + * since it handles slanted moves in a suboptimal way. + * Going from 'x' to 'y' needs to pass through 'z', and will + * fail if there's an obstable there, but it could choose to + * pass through 'Z' instead if that way imposes no obstacle. + * ..y .Zy + * xz. vs x.. + * Perhaps we should check both paths and accept whichever + * one isn't blocked. But then multiple zigs and zags could + * potentially produce a meandering path rather than the best + * attempt at a straight line. And (*check_proc)() would + * need to work more like 'travel', distinguishing between + * testing a possible move and actually attempting that move. + */ dx = dest_cc->x - src_cc->x; dy = dest_cc->y - src_cc->y; prev_x = x = src_cc->x; @@ -640,9 +655,11 @@ int x, y; vision_recalc(1); /* update for new position */ flush_screen(1); - if (levl[x][y].typ == WATER && Is_waterlevel(&u.uz)) { - multi = 0; - drown(); + if (is_pool(x, y) && !u.uinwater + && ((Is_waterlevel(&u.uz) && levl[x][y].typ == WATER) + || !(Levitation || Flying || Wwalking))) { + multi = 0; /* can move, so drown() allows crawling out of water */ + (void) drown(); return FALSE; } @@ -768,8 +785,7 @@ boolean verbose; (void) walk_path(&uc, &cc, hurtle_step, (genericptr_t) &range); } -/* Move a monster through the air for a few squares. - */ +/* Move a monster through the air for a few squares. */ void mhurtle(mon, dx, dy, range) struct monst *mon; From fd74f2e149a3a81c0cd7c4fcaf2d4163b4244434 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 12 Aug 2017 16:44:39 -0700 Subject: [PATCH 36/39] fix files.c part of #H5778 - file descriptor leaks Fix the SELF_RECOVER part of files.c which was actually in slightly worse shape than previously fixed recover.c itself. I think the HOLD_LOCKFILE_OPEN edition of nhclose() is the only remaining file descriptor leak from the original #H5778 report. The revised code compiles when SELF_RECOVER is defined, but I didn't actually force a recovery to fully test it. This patch includes a couple of reformatting and/or reorganization bits in addition to the fd leak fix. --- src/files.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/files.c b/src/files.c index ddba24d09..b25539954 100644 --- a/src/files.c +++ b/src/files.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 files.c $NHDT-Date: 1459987580 2016/04/07 00:06:20 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.205 $ */ +/* NetHack 3.6 files.c $NHDT-Date: 1502581476 2017/08/12 23:44:36 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.211 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -617,6 +617,7 @@ const char *name; int lev, oflag; { int reslt, fd; + if (!lftrack.init) { lftrack.init = 1; lftrack.fd = -1; @@ -671,7 +672,7 @@ int fd; } return close(fd); } -#else +#else /* !HOLD_LOCKFILE_OPEN */ int nhclose(fd) @@ -679,7 +680,7 @@ int fd; { return close(fd); } -#endif +#endif /* ?HOLD_LOCKFILE_OPEN */ /* ---------- END LEVEL FILE HANDLING ----------- */ @@ -3065,8 +3066,8 @@ const char *dir UNUSED_if_not_OS2_CODEVIEW; #ifdef VMS /* must be stream-lf to use UPDATE_RECORD_IN_PLACE */ if (!file_is_stmlf(fd)) { raw_printf( - "Warning: scoreboard file %s is not in stream_lf format", - fq_record); + "Warning: scoreboard file '%s' is not in stream_lf format", + fq_record); wait_synch(); } #endif @@ -3078,7 +3079,7 @@ const char *dir UNUSED_if_not_OS2_CODEVIEW; (void) chmod(fq_record, FCMASK | 007); #endif /* VMS && !SECURE */ } else { - raw_printf("Warning: cannot write scoreboard file %s", fq_record); + raw_printf("Warning: cannot write scoreboard file '%s'", fq_record); wait_synch(); } #endif /* !UNIX && !VMS */ @@ -3101,22 +3102,25 @@ const char *dir UNUSED_if_not_OS2_CODEVIEW; #endif if ((fd = open(fq_record, O_RDWR)) < 0) { -/* try to create empty record */ + /* try to create empty 'record' */ #if defined(AZTEC_C) || defined(_DCC) \ || (defined(__GNUC__) && defined(__AMIGA__)) /* Aztec doesn't use the third argument */ /* DICE doesn't like it */ - if ((fd = open(fq_record, O_CREAT | O_RDWR)) < 0) { + fd = open(fq_record, O_CREAT | O_RDWR); #else - if ((fd = open(fq_record, O_CREAT | O_RDWR, S_IREAD | S_IWRITE)) - < 0) { + fd = open(fq_record, O_CREAT | O_RDWR, S_IREAD | S_IWRITE); #endif - raw_printf("Warning: cannot write record %s", tmp); + if (fd = < 0) { + raw_printf("Warning: cannot write record '%s'", tmp); wait_synch(); - } else + } else { (void) nhclose(fd); - } else /* open succeeded */ + } + } else { + /* open succeeded => 'record' exists */ (void) nhclose(fd); + } #else /* MICRO || WIN32*/ #ifdef MAC @@ -3260,6 +3264,7 @@ recover_savefile() raw_printf("\nError writing %s; recovery failed.", SAVEF); (void) nhclose(gfd); (void) nhclose(sfd); + (void) nhclose(lfd); delete_savefile(); return FALSE; } @@ -3269,6 +3274,7 @@ recover_savefile() SAVEF); (void) nhclose(gfd); (void) nhclose(sfd); + (void) nhclose(lfd); delete_savefile(); return FALSE; } @@ -3279,6 +3285,7 @@ recover_savefile() SAVEF); (void) nhclose(gfd); (void) nhclose(sfd); + (void) nhclose(lfd); delete_savefile(); return FALSE; } @@ -3288,13 +3295,15 @@ recover_savefile() SAVEF); (void) nhclose(gfd); (void) nhclose(sfd); + (void) nhclose(lfd); delete_savefile(); return FALSE; } if (!copy_bytes(lfd, sfd)) { - (void) nhclose(lfd); + (void) nhclose(gfd); (void) nhclose(sfd); + (void) nhclose(lfd); delete_savefile(); return FALSE; } @@ -3302,7 +3311,7 @@ recover_savefile() processed[savelev] = 1; if (!copy_bytes(gfd, sfd)) { - (void) nhclose(lfd); + (void) nhclose(gfd); (void) nhclose(sfd); delete_savefile(); return FALSE; @@ -3343,6 +3352,7 @@ recover_savefile() for (lev = 0; lev < 256; lev++) { if (processed[lev]) { const char *fq_lock; + set_levelfile_name(lock, lev); fq_lock = fqname(lock, LEVELPREFIX, 3); (void) unlink(fq_lock); From 5e543976095d0286cc7aabd63b775aa1375f66de Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 14 Aug 2017 16:30:23 -0700 Subject: [PATCH 37/39] fix #H5853 - carrots don't cure blind pets Report was for a blinded horse which ate a carrot but remained blind. This fixes that, and also lets blinded carnivorous pets eat carrots. Gelatinous cubes now handle carrots too, but since they lack eyses there won't be any noticeable effect for them. --- include/extern.h | 3 ++- src/dog.c | 14 +++++++++----- src/dogmove.c | 7 +++++-- src/mon.c | 9 ++++++--- src/muse.c | 47 +++++++++++++++++++++++------------------------ 5 files changed, 45 insertions(+), 35 deletions(-) diff --git a/include/extern.h b/include/extern.h index 7d49b29d1..1d44d0d37 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1501725402 2017/08/03 01:56:42 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.598 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1502753404 2017/08/14 23:30:04 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.600 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1563,6 +1563,7 @@ E int FDECL(rnd_misc_item, (struct monst *)); E boolean FDECL(searches_for_item, (struct monst *, struct obj *)); E boolean FDECL(mon_reflects, (struct monst *, const char *)); E boolean FDECL(ureflects, (const char *, const char *)); +E void FDECL(mcureblindness, (struct monst *, BOOLEAN_P)); E boolean FDECL(munstone, (struct monst *, BOOLEAN_P)); E boolean FDECL(munslime, (struct monst *, BOOLEAN_P)); diff --git a/src/dog.c b/src/dog.c index 0b9d2ae9f..41a5e4f05 100644 --- a/src/dog.c +++ b/src/dog.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 dog.c $NHDT-Date: 1446808440 2015/11/06 11:14:00 $ $NHDT-Branch: master $:$NHDT-Revision: 1.52 $ */ +/* NetHack 3.6 dog.c $NHDT-Date: 1502753406 2017/08/14 23:30:06 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.60 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -736,7 +736,8 @@ struct monst *mon; register struct obj *obj; { struct permonst *mptr = mon->data, *fptr = 0; - boolean carni = carnivorous(mptr), herbi = herbivorous(mptr), starving; + boolean carni = carnivorous(mptr), herbi = herbivorous(mptr), + starving, mblind; if (is_quest_artifact(obj) || obj_resists(obj, 0, 95)) return obj->cursed ? TABU : APPORT; @@ -755,8 +756,10 @@ register struct obj *obj; return obj->cursed ? UNDEF : APPORT; /* a starving pet will eat almost anything */ - starving = - (mon->mtame && !mon->isminion && EDOG(mon)->mhpmax_penalty); + starving = (mon->mtame && !mon->isminion + && EDOG(mon)->mhpmax_penalty); + /* even carnivores will eat carrots if they're temporarily blind */ + mblind = (!mon->mcansee && haseyes(mon->data)); /* ghouls prefer old corpses and unhatchable eggs, yum! they'll eat fresh non-veggy corpses and hatchable eggs @@ -813,8 +816,9 @@ register struct obj *obj; case TIN: return metallivorous(mptr) ? ACCFOOD : MANFOOD; case APPLE: - case CARROT: return herbi ? DOGFOOD : starving ? ACCFOOD : MANFOOD; + case CARROT: + return (herbi || mblind) ? DOGFOOD : starving ? ACCFOOD : MANFOOD; case BANANA: return (mptr->mlet == S_YETI && herbi) ? DOGFOOD /* for monkey and ape (tameable), sasquatch */ diff --git a/src/dogmove.c b/src/dogmove.c index 321deb0af..5a385b6aa 100644 --- a/src/dogmove.c +++ b/src/dogmove.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 dogmove.c $NHDT-Date: 1463704424 2016/05/20 00:33:44 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.60 $ */ +/* NetHack 3.6 dogmove.c $NHDT-Date: 1502753407 2017/08/14 23:30:07 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.63 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -209,7 +209,7 @@ int x, y; /* dog's starting location, might be different from current */ boolean devour; { register struct edog *edog = EDOG(mtmp); - boolean poly, grow, heal, slimer, deadmimic; + boolean poly, grow, heal, eyes, slimer, deadmimic; int nutrit; long oprice; char objnambuf[BUFSZ]; @@ -226,6 +226,7 @@ boolean devour; poly = polyfodder(obj); grow = mlevelgain(obj); heal = mhealup(obj); + eyes = (obj->otyp == CARROT); if (devour) { if (mtmp->meating > 1) @@ -343,6 +344,8 @@ boolean devour; } if (heal) mtmp->mhp = mtmp->mhpmax; + if ((eyes || heal) && !mtmp->mcansee) + mcureblindness(mtmp, canseemon(mtmp)); if (deadmimic) quickmimic(mtmp); return 1; diff --git a/src/mon.c b/src/mon.c index 69d64490b..466de5c31 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mon.c $NHDT-Date: 1496531114 2017/06/03 23:05:14 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.240 $ */ +/* NetHack 3.6 mon.c $NHDT-Date: 1502753408 2017/08/14 23:30:08 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.242 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -886,9 +886,9 @@ int meatobj(mtmp) /* for gelatinous cubes */ struct monst *mtmp; { - register struct obj *otmp, *otmp2; + struct obj *otmp, *otmp2; struct permonst *ptr, *original_ptr = mtmp->data; - int poly, grow, heal, count = 0, ecount = 0; + int poly, grow, heal, eyes, count = 0, ecount = 0; char buf[BUFSZ]; buf[0] = '\0'; @@ -986,6 +986,7 @@ struct monst *mtmp; poly = polyfodder(otmp); grow = mlevelgain(otmp); heal = mhealup(otmp); + eyes = (otmp->otyp == CARROT); delobj(otmp); /* munch */ ptr = mtmp->data; if (poly) { @@ -996,6 +997,8 @@ struct monst *mtmp; } else if (heal) { mtmp->mhp = mtmp->mhpmax; } + if ((eyes || heal) && !mtmp->mcansee) + mcureblindness(mtmp, canseemon(mtmp)); /* in case it polymorphed or died */ if (ptr != original_ptr) return !ptr ? 2 : 1; diff --git a/src/muse.c b/src/muse.c index 420c8564e..f91a88b23 100644 --- a/src/muse.c +++ b/src/muse.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 muse.c $NHDT-Date: 1469840918 2016/07/30 01:08:38 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.78 $ */ +/* NetHack 3.6 muse.c $NHDT-Date: 1502753408 2017/08/14 23:30:08 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.79 $ */ /* Copyright (C) 1990 by Ken Arromdee */ /* NetHack may be freely redistributed. See license for details. */ @@ -603,7 +603,7 @@ struct monst *mtmp; int i, fleetim, how = 0; struct obj *otmp = m.defensive; boolean vis, vismon, oseen; - const char *Mnam, *mcsa = "%s can see again."; + const char *Mnam; if ((i = precheck(mtmp, otmp)) != 0) return i; @@ -628,10 +628,7 @@ struct monst *mtmp; pline_The("tip of %s's horn glows!", mon_nam(mtmp)); } if (!mtmp->mcansee) { - mtmp->mcansee = 1; - mtmp->mblinded = 0; - if (vismon) - pline(mcsa, Monnam(mtmp)); + mcureblindness(mtmp, vismon); } else if (mtmp->mconf || mtmp->mstun) { mtmp->mconf = mtmp->mstun = 0; if (vismon) @@ -939,12 +936,8 @@ struct monst *mtmp; mtmp->mhp += i; if (mtmp->mhp > mtmp->mhpmax) mtmp->mhp = ++mtmp->mhpmax; - if (!otmp->cursed && !mtmp->mcansee) { - mtmp->mcansee = 1; - mtmp->mblinded = 0; - if (vismon) - pline(mcsa, Monnam(mtmp)); - } + if (!otmp->cursed && !mtmp->mcansee) + mcureblindness(mtmp, vismon); if (vismon) pline("%s looks better.", Monnam(mtmp)); if (oseen) @@ -957,12 +950,8 @@ struct monst *mtmp; mtmp->mhp += i; if (mtmp->mhp > mtmp->mhpmax) mtmp->mhp = (mtmp->mhpmax += (otmp->blessed ? 5 : 2)); - if (!mtmp->mcansee) { - mtmp->mcansee = 1; - mtmp->mblinded = 0; - if (vismon) - pline(mcsa, Monnam(mtmp)); - } + if (!mtmp->mcansee) + mcureblindness(mtmp, vismon); if (vismon) pline("%s looks much better.", Monnam(mtmp)); if (oseen) @@ -974,12 +963,8 @@ struct monst *mtmp; if (otmp->otyp == POT_SICKNESS) unbless(otmp); /* Pestilence */ mtmp->mhp = (mtmp->mhpmax += (otmp->blessed ? 8 : 4)); - if (!mtmp->mcansee && otmp->otyp != POT_SICKNESS) { - mtmp->mcansee = 1; - mtmp->mblinded = 0; - if (vismon) - pline(mcsa, Monnam(mtmp)); - } + if (!mtmp->mcansee && otmp->otyp != POT_SICKNESS) + mcureblindness(mtmp, vismon); if (vismon) pline("%s looks completely healed.", Monnam(mtmp)); if (oseen) @@ -2175,6 +2160,20 @@ const char *fmt, *str; return FALSE; } +/* cure mon's blindness (use_defensive, dog_eat, meatobj) */ +void +mcureblindness(mon, verbos) +struct monst *mon; +boolean verbos; +{ + if (!mon->mcansee) { + mon->mcansee = 1; + mon->mblinded = 0; + if (verbos && haseyes(mon->data)) + pline("%s can see again.", Monnam(mon)); + } +} + /* TRUE if the monster ate something */ boolean munstone(mon, by_you) From 5f3e11687c839c74cf65757f47445480196a29ff Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 14 Aug 2017 16:36:37 -0700 Subject: [PATCH 38/39] healing vapor vs blindness Drinking any potion full healing or extra healing or non-cursed potion of healing cures blindness in addition to restoring lost hit points. Now breathing vapor from any potion of full healing or non-cursed potion of extra healing or blessed potion of healing will also do so. --- src/potion.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/potion.c b/src/potion.c index fd1161e3a..1a7e08ecc 100644 --- a/src/potion.c +++ b/src/potion.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 potion.c $NHDT-Date: 1501725406 2017/08/03 01:56:46 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.137 $ */ +/* NetHack 3.6 potion.c $NHDT-Date: 1502753790 2017/08/14 23:36:30 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.138 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1343,15 +1343,22 @@ boolean your_fault; if (useeit && !affected) pline("%s %s wet.", buf, aobjnam(saddle, "get")); } else { - boolean angermon = your_fault; + boolean angermon = your_fault, cureblind = FALSE; switch (obj->otyp) { - case POT_HEALING: - case POT_EXTRA_HEALING: case POT_FULL_HEALING: + cureblind = TRUE; + /*FALLTHRU*/ + case POT_EXTRA_HEALING: + if (!obj->cursed) + cureblind = TRUE; + /*FALLTHRU*/ + case POT_HEALING: + if (obj->blessed) + cureblind = TRUE; if (mon->data == &mons[PM_PESTILENCE]) goto do_illness; - /*FALLTHRU*/ + /*FALLTHRU*/ case POT_RESTORE_ABILITY: case POT_GAIN_ABILITY: do_healing: @@ -1361,6 +1368,8 @@ boolean your_fault; if (canseemon(mon)) pline("%s looks sound and hale again.", Monnam(mon)); } + if (cureblind) + mcureblindness(mon, canseemon(mon)); break; case POT_SICKNESS: if (mon->data == &mons[PM_PESTILENCE]) @@ -1419,7 +1428,7 @@ boolean your_fault; break; case POT_BLINDNESS: if (haseyes(mon->data)) { - register int btmp = 64 + rn2(32) + int btmp = 64 + rn2(32) + rn2(32) * !resist(mon, POTION_CLASS, 0, NOTELL); btmp += mon->mblinded; @@ -1534,7 +1543,8 @@ void potionbreathe(obj) register struct obj *obj; { - register int i, ii, isdone, kn = 0; + int i, ii, isdone, kn = 0; + boolean cureblind = FALSE; switch (obj->otyp) { case POT_RESTORE_ABILITY: @@ -1569,18 +1579,25 @@ register struct obj *obj; u.mh++, context.botl = 1; if (u.uhp < u.uhpmax) u.uhp++, context.botl = 1; + cureblind = TRUE; /*FALLTHRU*/ case POT_EXTRA_HEALING: if (Upolyd && u.mh < u.mhmax) u.mh++, context.botl = 1; if (u.uhp < u.uhpmax) u.uhp++, context.botl = 1; + if (!obj->cursed) + cureblind = TRUE; /*FALLTHRU*/ case POT_HEALING: if (Upolyd && u.mh < u.mhmax) u.mh++, context.botl = 1; if (u.uhp < u.uhpmax) u.uhp++, context.botl = 1; + if (obj->blessed) + cureblind = TRUE; + if (cureblind) + make_blinded(0L, !u.ucreamed); exercise(A_CON, TRUE); break; case POT_SICKNESS: From 0840974b42b117289a2401c97d8254c64d59811d Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 14 Aug 2017 16:42:44 -0700 Subject: [PATCH 39/39] nurse corpse vs blindness The carrot patch made monsters (carnivorous pets, g.cubes) who eat nurse corpses have temporary blindness be cured. Do the same for the hero. --- src/eat.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/eat.c b/src/eat.c index 4e011899d..552ae147b 100644 --- a/src/eat.c +++ b/src/eat.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 eat.c $NHDT-Date: 1498778062 2017/06/29 23:14:22 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.178 $ */ +/* NetHack 3.6 eat.c $NHDT-Date: 1502754159 2017/08/14 23:42:39 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.179 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -979,6 +979,7 @@ register int pm; u.mh = u.mhmax; else u.uhp = u.uhpmax; + make_blinded(0L, !u.ucreamed); context.botl = 1; break; case PM_STALKER: