diff --git a/include/display.h b/include/display.h index 721b7f10be..f3292a6f09 100644 --- a/include/display.h +++ b/include/display.h @@ -239,6 +239,10 @@ enum explosion_types { /* else U_AP_TYPE == M_AP_MONSTER */ \ : monnum_to_glyph(g.youmonst.mappearance))) +/* Get a string describing the terrain at (x, y). */ +#define explain_terrain(x, y) \ + (defsyms[back_to_defsym((x), (y))].explanation) + /* * A glyph is an abstraction that represents a _unique_ monster, object, * dungeon part, or effect. The uniqueness is important. For example, diff --git a/include/extern.h b/include/extern.h index 7e4af0781d..035a10fb52 100644 --- a/include/extern.h +++ b/include/extern.h @@ -388,6 +388,7 @@ E void FDECL(row_refresh, (int, int, int)); E void NDECL(cls); E void FDECL(flush_screen, (int)); E int FDECL(back_to_glyph, (XCHAR_P, XCHAR_P)); +E int FDECL(back_to_defsym, (XCHAR_P, XCHAR_P)); E int FDECL(zapdir_to_glyph, (int, int, int)); E int FDECL(glyph_at, (XCHAR_P, XCHAR_P)); E void NDECL(reglyph_darkroom); @@ -479,6 +480,7 @@ E struct monst *FDECL(christen_orc, (struct monst *, const char *, const char *)); E const char *FDECL(noveltitle, (int *)); E const char *FDECL(lookup_novel, (const char *, int *)); +E void FDECL(mintroduce, (struct monst *)); /* ### do_wear.c ### */ @@ -862,6 +864,7 @@ E void NDECL(drinkfountain); E void FDECL(dipfountain, (struct obj *)); E void FDECL(breaksink, (int, int)); E void NDECL(drinksink); +E struct obj* FDECL(ring_from_sink, (XCHAR_P, XCHAR_P)); /* ### hack.c ### */ @@ -1462,7 +1465,7 @@ E int FDECL(meatmetal, (struct monst *)); E int FDECL(meatobj, (struct monst *)); E int FDECL(meatcorpse, (struct monst *)); E void FDECL(mpickgold, (struct monst *)); -E boolean FDECL(mpickstuff, (struct monst *, const char *)); +E boolean FDECL(mpickstuff, (struct monst *, boolean (*)(OBJ_P))); E int FDECL(curr_mon_load, (struct monst *)); E int FDECL(max_mon_load, (struct monst *)); E int FDECL(can_carry, (struct monst *, struct obj *)); @@ -1581,6 +1584,7 @@ E void FDECL(mon_yells, (struct monst *, const char *)); E int FDECL(dochug, (struct monst *)); E boolean FDECL(m_digweapon_check, (struct monst *, XCHAR_P, XCHAR_P)); E int FDECL(m_move, (struct monst *, int)); +E int FDECL(concealed_spot, (int, int)); E void FDECL(dissolve_bars, (int, int)); E boolean FDECL(closed_door, (int, int)); E boolean FDECL(accessible, (int, int)); @@ -3174,6 +3178,7 @@ E void FDECL(destroy_item, (int, int)); E int FDECL(destroy_mitem, (struct monst *, int, int)); E int FDECL(resist, (struct monst *, CHAR_P, int, int)); E void NDECL(makewish); +E const char* FDECL(flash_str, (int, BOOLEAN_P)); #endif /* !MAKEDEFS_C && !MDLIB_C */ diff --git a/include/rm.h b/include/rm.h index 761e8b30cb..8e651ac284 100644 --- a/include/rm.h +++ b/include/rm.h @@ -375,7 +375,6 @@ extern const struct symdef def_warnsyms[WARNCOUNT]; */ #define S_LPUDDING 1 #define S_LDWASHER 2 -#define S_LRING 4 /* * The four directions for a DrawBridge. diff --git a/src/apply.c b/src/apply.c index faef080bf3..0030906840 100644 --- a/src/apply.c +++ b/src/apply.c @@ -2127,9 +2127,12 @@ long timeout; suppress_see = TRUE; if (mtmp->mundetected) { - if (hides_under(mtmp->data) && mshelter) { - Sprintf(and_vanish, " and %s under %s", - locomotion(mtmp->data, "crawl"), doname(mshelter)); + if (hides_under(mtmp->data) && concealed_spot(mtmp->mx, mtmp->my)) { + Sprintf(and_vanish, " and %s under %s%s", + locomotion(mtmp->data, "crawl"), + (mshelter ? "" : "the "), + (mshelter ? doname(mshelter) : + explain_terrain(mtmp->mx, mtmp->my))); } else if (mtmp->data->mlet == S_MIMIC || mtmp->data->mlet == S_EEL) { suppress_see = TRUE; diff --git a/src/artifact.c b/src/artifact.c index c71c0e87c1..864f5bd423 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -1451,9 +1451,11 @@ struct obj *obj; /* the artifact is tired :-) */ You_feel("that %s %s ignoring you.", the(xname(obj)), otense(obj, "are")); - /* and just got more so; patience is essential... */ - obj->age += (long) d(3, 10); - return 1; + if (!(wizard && yn("Override?") == 'y')) { + /* and just got more so; patience is essential... */ + obj->age += (long) d(3, 10); + return 1; + } } obj->age = g.monstermoves + rnz(100); @@ -1627,9 +1629,11 @@ struct obj *obj; u.uprops[oart->inv_prop].extrinsic ^= W_ARTI; You_feel("that %s %s ignoring you.", the(xname(obj)), otense(obj, "are")); - /* can't just keep repeatedly trying */ - obj->age += (long) d(3, 10); - return 1; + if (!(wizard && yn("Override?") == 'y')) { + /* can't just keep repeatedly trying */ + obj->age += (long) d(3, 10); + return 1; + } } else if (!on) { /* when turning off property, determine downtime */ /* arbitrary for now until we can tune this -dlc */ diff --git a/src/dig.c b/src/dig.c index 05369498f2..19a52778b1 100644 --- a/src/dig.c +++ b/src/dig.c @@ -2041,7 +2041,7 @@ long timeout; struct monst *mtmp = m_at(x, y); /* a hiding monster may be exposed */ - if (mtmp && !OBJ_AT(x, y) && mtmp->mundetected + if (mtmp && !concealed_spot(x, y) && mtmp->mundetected && hides_under(mtmp->data)) { mtmp->mundetected = 0; } else if (x == u.ux && y == u.uy && u.uundetected && hides_under(g.youmonst.data)) diff --git a/src/display.c b/src/display.c index 0efe1582c9..637c9e2e4e 100644 --- a/src/display.c +++ b/src/display.c @@ -1744,6 +1744,15 @@ int cursor_on_u; int back_to_glyph(x, y) xchar x, y; +{ + return cmap_to_glyph(back_to_defsym(x, y)); +} + +/* extracted from back_to_glyph so that we can get the string description of a + * terrain spot on the map */ +int +back_to_defsym(x, y) +xchar x, y; { int idx; struct rm *ptr = &(levl[x][y]); @@ -1858,12 +1867,12 @@ xchar x, y; idx = (ptr->horizontal) ? S_hodbridge : S_vodbridge; break; default: - impossible("back_to_glyph: unknown level type [ = %d ]", ptr->typ); + impossible("back_to_defsym: unknown level type [ = %d ]", ptr->typ); idx = S_room; break; } - return cmap_to_glyph(idx); + return idx; } /* diff --git a/src/do.c b/src/do.c index 580c701c09..ea282a88dd 100644 --- a/src/do.c +++ b/src/do.c @@ -294,6 +294,50 @@ register struct obj *obj; if (obj->oclass != COIN_CLASS) obj->bknown = 1; /* ok to bypass set_bknown() */ } + + /* From NetHack4: colored flashes one level deep inside containers. */ + if (Has_contents(obj) && !obj->olocked && obj->cknown) { + int blessed = 0; + int cursed = 0; + struct obj * otmp; + for (otmp = obj->cobj; otmp; otmp = otmp->nobj) { + if (otmp->blessed) + blessed++; + if (otmp->cursed) + cursed++; + if (!Hallucination) { + otmp->bknown = 1; + } + } + /* even when hallucinating, if you get no flashes at all, you know + * everything's uncursed, so save the player the trouble of manually + * naming them all */ + if (Hallucination && blessed + cursed == 0) { + for (otmp = obj->cobj; otmp; otmp = otmp->nobj) { + otmp->bknown = 1; + } + } + if (blessed + cursed > 0) { + const char* color; + if (Hallucination) { + color = "pretty multichromatic"; + } + else if (blessed == 0) { + color = hcolor(NH_BLACK); + } + else if (cursed == 0) { + color = hcolor(NH_AMBER); + } + else { + color = "colored"; + } + + pline("From inside %s, you see %s flash%s.", + the(xname(obj)), + (blessed + cursed == 1 ? an(color) : color), + (blessed + cursed == 1 ? "" : "es")); + } + } } static void @@ -549,7 +593,7 @@ register struct obj *obj; pline_The("sink backs up, leaving %s.", doname(obj)); obj->in_use = FALSE; dropx(obj); - } else if (!rn2(5)) { + } else if (rn2(5)) { freeinv(obj); obj->in_use = FALSE; obj->ox = u.ux; @@ -1231,14 +1275,8 @@ struct monst *mtmp; /* There's a monster at your target destination; it might be one which accompanied you--see mon_arrive(dogmove.c)--or perhaps - it was already here. Randomly move you to an adjacent spot - or else the monster to any nearby location. Prior to 3.3.0 - the latter was done unconditionally. */ - if (!rn2(2) && enexto(&cc, u.ux, u.uy, g.youmonst.data) - && distu(cc.x, cc.y) <= 2) - u_on_newpos(cc.x, cc.y); /*[maybe give message here?]*/ - else - mnexto(mtmp); + it was already here. Displace the monster to any nearby location. */ + mnexto(mtmp); if ((mtmp = m_at(u.ux, u.uy)) != 0) { /* there was an unconditional impossible("mnexto failed") diff --git a/src/do_name.c b/src/do_name.c index 2725f4955a..096dc1ffc9 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -2333,4 +2333,62 @@ int *idx; return (const char *) 0; } +/* Most of these are actual names of nymphs from mythology. */ +const char* nymphnames[] = { + "Erythea", "Hesperia", "Arethusa", "Pasithea", "Thaleia", "Halimede", + "Actaea", "Electra", "Maia", "Nesaea", "Alcyone", "Asterope", + "Callianeira", "Nausithoe", "Dione", "Thetis", "Ephyra", "Eulimene", + "Nerea", "Laomedeia", "Echo", "Maera", "Eurydice", "Lysianassa", "Phoebe", + "Daphnis", "Daphnae", "Melinoe", "Othreis", "Polychrome" +}; + +const char* maldemonnames[] = { + "Agiel", "Kali", "Amon", "Foras", "Armaros", "Orias", "Malthus", "Asag", + "Raum", "Iblis", "Vanth", "Bael", "Leonard", "Barbas", "Charun", "Ishmael", + "Balthamel", "Rahvin" +}; + +const char* femdemonnames[] = { + "Mara", "Lamia", "Meraxes", "Daeva", "Amy", "Lilith", "Aliss", "Berith", + "Euryale", "Zorya", "Rhaenyra", "Bellatrix", "Rusalka", "Messaana", + "Jadis", "Anzu", "Eve", "Bilquis", "Cyndane", "Vanessa", "Daenera" +}; +#define rnd_name(list) list[rn2(SIZE(list))] + +/* Monster introduces themselves. If they're not currently named, give them a + * random name from the specified list. */ +void +mintroduce(mtmp) +struct monst *mtmp; +{ + if (!has_mname(mtmp)) { + const char* name; + if (mtmp->data->mlet == S_NYMPH) { + name = rnd_name(nymphnames); + } + else if (is_demon(mtmp->data)) { + if (mtmp->female) + name = rnd_name(femdemonnames); + else + name = rnd_name(maldemonnames); + } + else { + impossible("mintroduce: monster type %s has no defined names!", + mtmp->data->mname); + return; + } + const char* pronoun = + genders[is_neuter(mtmp->data) ? 2 : mtmp->female].him; + if (!Deaf) { + pline("%s introduces %sself to you as %s.", Monnam(mtmp), pronoun, + name); + christen_monst(mtmp, name); + } else { + pline("%s seems to be introducing %sself, but you can't hear %s.", + Monnam(mtmp), pronoun, pronoun); + } + } + return; +} + /*do_name.c*/ diff --git a/src/do_wear.c b/src/do_wear.c index 3d20fe61f5..cdd9352ab8 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -947,7 +947,6 @@ register struct obj *obj; switch (obj->otyp) { case RIN_TELEPORTATION: - case RIN_REGENERATION: case RIN_SEARCHING: case RIN_HUNGER: case RIN_AGGRAVATE_MONSTER: @@ -964,6 +963,10 @@ register struct obj *obj; case RIN_SUSTAIN_ABILITY: case MEAT_RING: break; + case RIN_REGENERATION: + You_feel("invigorated!"); + learnring(obj, TRUE); + break; case RIN_STEALTH: toggle_stealth(obj, oldprop, TRUE); break; @@ -1059,7 +1062,6 @@ boolean gone; switch (obj->otyp) { case RIN_TELEPORTATION: - case RIN_REGENERATION: case RIN_SEARCHING: case RIN_HUNGER: case RIN_AGGRAVATE_MONSTER: @@ -1076,6 +1078,10 @@ boolean gone; case RIN_SUSTAIN_ABILITY: case MEAT_RING: break; + case RIN_REGENERATION: + You_feel("enervated."); + learnring(obj, TRUE); + break; case RIN_STEALTH: toggle_stealth(obj, (EStealth & ~mask), FALSE); break; diff --git a/src/dokick.c b/src/dokick.c index 5564c84c5d..8e3b50d995 100644 --- a/src/dokick.c +++ b/src/dokick.c @@ -1155,7 +1155,7 @@ dokick() if (Levitation) goto dumb; - if (rn2(5)) { + if (rn2(3)) { if (!Deaf) pline("Klunk! The pipes vibrate noisily."); else @@ -1194,14 +1194,12 @@ dokick() : !Deaf ? "You hear a sloshing sound" /* Deaf-aware */ : "Something splashes you in the", buf); - if (!(g.maploc->looted & S_LRING)) { /* once per sink */ + struct obj * otmp = ring_from_sink(x, y); + if (otmp) { if (!Blind) You_see("a ring shining in its midst."); - (void) mkobj_at(RING_CLASS, x, y, TRUE); - newsym(x, y); exercise(A_DEX, TRUE); exercise(A_WIS, TRUE); /* a discovery! */ - g.maploc->looted |= S_LRING; } return 1; } diff --git a/src/eat.c b/src/eat.c index 17040fc9e2..860fb196e7 100644 --- a/src/eat.c +++ b/src/eat.c @@ -952,23 +952,6 @@ int pm; (void) eatmdone(); switch (pm) { - case PM_NEWT: - /* MRKR: "eye of newt" may give small magical energy boost */ - if (rn2(3) || 3 * u.uen <= 2 * u.uenmax) { - int old_uen = u.uen; - - u.uen += rnd(3); - if (u.uen > u.uenmax) { - if (!rn2(3)) - u.uenmax++; - u.uen = u.uenmax; - } - if (old_uen != u.uen) { - You_feel("a mild buzz."); - g.context.botl = 1; - } - } - break; case PM_WRAITH: pluslvl(FALSE); break; @@ -1112,6 +1095,10 @@ int pm; if (check_intrinsics) { struct permonst *ptr = &mons[pm]; boolean conveys_STR = is_giant(ptr); + /* MRKR: "eye of newt" may give small magical energy boost + * as well as other magical monsters */ + boolean conveys_energy = (attacktype(&mons[pm], AT_MAGC) + || pm == PM_NEWT); int i, count; if (dmgtype(ptr, AD_STUN) || dmgtype(ptr, AD_HALU) @@ -1135,6 +1122,11 @@ int pm; tmp = -1; /* use -1 as fake prop index for STR */ debugpline1("\"Intrinsic\" strength, %d", tmp); } + else if (conveys_energy) { + count = 1; + tmp = -2; + debugpline1("\"Intrinsic\" energy gain, %d", tmp); + } for (i = 1; i <= LAST_PROP; i++) { if (!intrinsic_possible(i, ptr)) continue; @@ -1154,6 +1146,23 @@ int pm; /* if something was chosen, give it now (givit() might fail) */ if (tmp == -1) gainstr((struct obj *) 0, 0, TRUE); + else if (tmp == -2) { + if (rn2(3) || 3 * u.uen <= 2 * u.uenmax) { + int old_uen = u.uen, old_uenmax = u.uenmax; + u.uen += (pm == PM_NEWT) ? rnd(3) : rnd(mons[pm].mlevel); + if (u.uen > u.uenmax) { + if (!rn2(3)) + u.uenmax++; + u.uen = u.uenmax; + } + if (old_uen != u.uen) { + You_feel("a %s buzz.", + old_uenmax != u.uenmax ? "moderate" : "mild"); + g.context.botl = 1; + } + } + + } else if (tmp > 0) givit(tmp, ptr); } /* check_intrinsics */ @@ -1393,6 +1402,13 @@ const char *mesg; tintxts[r].txt, fingers_or_gloves(TRUE)); } + if (!strcmp(tintxts[r].txt, "szechuan") && rn2(2)) { + struct obj* cookie = mksobj(FORTUNE_COOKIE, FALSE, FALSE); + cookie->blessed = tin->blessed; + cookie->cursed = tin->cursed; + pline("There is a free fortune cookie inside!"); + hold_another_object(cookie, "It falls to the floor.", NULL, NULL); + } } else { /* spinach... */ if (tin->cursed) { pline("It contains some decaying%s%s substance.", diff --git a/src/engrave.c b/src/engrave.c index 09ddfcc48a..59fde4acfe 100644 --- a/src/engrave.c +++ b/src/engrave.c @@ -6,6 +6,7 @@ #include "hack.h" static const char *NDECL(blengr); +static void FDECL(engraving_learn_wand, (struct obj*)); char * random_engraving(outbuf) @@ -468,20 +469,21 @@ static NEARDATA const char styluses[] = { ALL_CLASSES, ALLOW_NONE, int doengrave() { - boolean dengr = FALSE; /* TRUE if we wipe out the current engraving */ - boolean doblind = FALSE; /* TRUE if engraving blinds the player */ - boolean doknown = FALSE; /* TRUE if we identify the stylus */ - boolean eow = FALSE; /* TRUE if we are overwriting oep */ - boolean jello = FALSE; /* TRUE if we are engraving in slime */ - boolean ptext = TRUE; /* TRUE if we must prompt for engrave text */ - boolean teleengr = FALSE; /* TRUE if we move the old engraving */ - boolean zapwand = FALSE; /* TRUE if we remove a wand charge */ - xchar type = DUST; /* Type of engraving made */ + boolean dengr = FALSE; /* TRUE if we wipe out the current engraving */ + boolean doblind = FALSE; /* TRUE if engraving blinds the player */ + boolean preknown = FALSE; /* TRUE if we identify the stylus before */ + boolean postknown = FALSE; /* TRUE if we identify the stylus after */ + boolean eow = FALSE; /* TRUE if we are overwriting oep */ + boolean jello = FALSE; /* TRUE if we are engraving in slime */ + boolean ptext = TRUE; /* TRUE if we must prompt for engrave text */ + boolean teleengr = FALSE; /* TRUE if we move the old engraving */ + boolean zapwand = FALSE; /* TRUE if we remove a wand charge */ + xchar type = DUST; /* Type of engraving made */ xchar oetype = 0; /* will be set to type of current engraving */ - char buf[BUFSZ]; /* Buffer for final/poly engraving text */ - char ebuf[BUFSZ]; /* Buffer for initial engraving text */ - char fbuf[BUFSZ]; /* Buffer for "your fingers" */ - char qbuf[QBUFSZ]; /* Buffer for query text */ + char buf[BUFSZ]; /* Buffer for final/poly engraving text */ + char ebuf[BUFSZ]; /* Buffer for initial engraving text */ + char fbuf[BUFSZ]; /* Buffer for "your fingers" */ + char qbuf[QBUFSZ]; /* Buffer for query text */ char post_engr_text[BUFSZ]; /* Text displayed after engraving prompt */ const char *everb; /* Present tense of engraving type */ const char *eloc; /* Where the engraving is (ie dust/floor/...) */ @@ -658,6 +660,8 @@ doengrave() case WAN_WISHING: case WAN_ENLIGHTENMENT: zapnodir(otmp); + /* pre/postknown not needed; these will make it known if + * applicable */ break; /* IMMEDIATE wands */ /* If wand is "IMMEDIATE", remember to affect the @@ -666,17 +670,20 @@ doengrave() case WAN_STRIKING: Strcpy(post_engr_text, "The wand unsuccessfully fights your attempt to write!"); + postknown = TRUE; break; case WAN_SLOW_MONSTER: if (!Blind) { Sprintf(post_engr_text, "The bugs on the %s slow down!", surface(u.ux, u.uy)); + postknown = TRUE; } break; case WAN_SPEED_MONSTER: if (!Blind) { Sprintf(post_engr_text, "The bugs on the %s speed up!", surface(u.ux, u.uy)); + postknown = TRUE; } break; case WAN_POLYMORPH: @@ -684,6 +691,7 @@ doengrave() if (!Blind) { type = (xchar) 0; /* random */ (void) random_engraving(buf); + postknown = TRUE; } else { /* keep the same type so that feels don't change and only the text is altered, @@ -709,6 +717,7 @@ doengrave() Sprintf(post_engr_text, "The %s is riddled by bullet holes!", surface(u.ux, u.uy)); + postknown = TRUE; } break; /* can't tell sleep from death - Eric Backus */ @@ -720,9 +729,11 @@ doengrave() } break; case WAN_COLD: - if (!Blind) + if (!Blind) { Strcpy(post_engr_text, "A few ice cubes drop from the wand."); + postknown = TRUE; + } if (!oep || (oep->engr_type != BURN)) break; /*FALLTHRU*/ @@ -750,7 +761,7 @@ doengrave() if (!objects[otmp->otyp].oc_name_known) { if (flags.verbose) pline("This %s is a wand of digging!", xname(otmp)); - doknown = TRUE; + preknown = TRUE; } Strcpy(post_engr_text, (Blind && !Deaf) @@ -773,7 +784,7 @@ doengrave() if (!objects[otmp->otyp].oc_name_known) { if (flags.verbose) pline("This %s is a wand of fire!", xname(otmp)); - doknown = TRUE; + preknown = TRUE; } Strcpy(post_engr_text, Blind ? "You feel the wand heat up." : "Flames fly from the wand."); @@ -784,7 +795,7 @@ doengrave() if (!objects[otmp->otyp].oc_name_known) { if (flags.verbose) pline("This %s is a wand of lightning!", xname(otmp)); - doknown = TRUE; + preknown = TRUE; } if (!Blind) { Strcpy(post_engr_text, "Lightning arcs from the wand."); @@ -890,10 +901,8 @@ doengrave() */ /* Identify stylus */ - if (doknown) { - learnwand(otmp); - if (objects[otmp->otyp].oc_name_known) - more_experienced(0, 10); + if (preknown) { + engraving_learn_wand(otmp); } if (teleengr) { rloc_engr(oep); @@ -1153,8 +1162,12 @@ doengrave() /* Put the engraving onto the map */ make_engr_at(u.ux, u.uy, buf, g.moves - g.multi, type); - if (post_engr_text[0]) + if (post_engr_text[0]) { pline("%s", post_engr_text); + if (postknown) { + engraving_learn_wand(otmp); + } + } if (doblind && !resists_blnd(&g.youmonst)) { You("are blinded by the flash!"); make_blinded((long) rnd(50), FALSE); @@ -1164,6 +1177,18 @@ doengrave() return 1; } +/* Learn what a wand is by engraving with it. */ +static void +engraving_learn_wand(obj) +struct obj* obj; +{ + learnwand(obj); + /* For some reason, this gives 10 points even if you already knew the + * wand... */ + if (objects[obj->otyp].oc_name_known) + more_experienced(0, 10); +} + /* while loading bones, clean up text which might accidentally or maliciously disrupt player's terminal when displayed */ void diff --git a/src/fountain.c b/src/fountain.c index f5be392dd0..bc99247a55 100644 --- a/src/fountain.c +++ b/src/fountain.c @@ -585,12 +585,11 @@ drinksink() obfree(otmp, (struct obj *) 0); break; case 5: - if (!(levl[u.ux][u.uy].looted & S_LRING)) { + /* look for a ring buried beneath the sink ("in the pipe") */ + otmp = ring_from_sink(u.ux, u.uy); + if (otmp) { You("find a ring in the sink!"); - (void) mkobj_at(RING_CLASS, u.ux, u.uy, TRUE); - levl[u.ux][u.uy].looted |= S_LRING; exercise(A_WIS, TRUE); - newsym(u.ux, u.uy); } else pline("Some dirty %s backs up in the drain.", hliquid("water")); break; @@ -640,4 +639,26 @@ drinksink() } } +/* If there is a ring buried under a sink, pop it out and place it on the sink. + * Return the ring that appeared or NULL if there was no ring there. + * The caller should handle messages. */ +struct obj * +ring_from_sink(sink_x, sink_y) +xchar sink_x, sink_y; +{ + struct obj* otmp; + for (otmp = g.level.buriedobjlist; otmp; otmp = otmp->nobj) { + if (otmp->ox == sink_x && otmp->oy == sink_y + && otmp->oclass == RING_CLASS) { + break; + } + } + if (otmp) { + obj_extract_self(otmp); + place_object(otmp, sink_x, sink_y); + newsym(sink_x, sink_y); + } + return otmp; +} + /*fountain.c*/ diff --git a/src/insight.c b/src/insight.c index 519a64be47..ed99514327 100644 --- a/src/insight.c +++ b/src/insight.c @@ -1757,6 +1757,8 @@ int msgflag; /* for variant message phrasing */ if (o) Sprintf(bp, " underneath %s", ansimpleoname(o)); + else if (concealed_spot(u.ux, u.uy)) + Sprintf(bp, " under %s", explain_terrain(u.ux, u.uy)); } else if (is_clinger(g.youmonst.data) || Flying) { /* Flying: 'lurker above' hides on ceiling but doesn't cling */ Sprintf(bp, " on the %s", ceiling(u.ux, u.uy)); diff --git a/src/makemon.c b/src/makemon.c index 3af9524ba2..7bd66a285d 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -79,41 +79,17 @@ int x, y, n, mmflags; { coord mm; register int cnt = rnd(n); + int diff = level_difficulty(); struct monst *mon; -#if defined(__GNUC__) && (defined(HPUX) || defined(DGUX)) - /* There is an unresolved problem with several people finding that - * the game hangs eating CPU; if interrupted and restored, the level - * will be filled with monsters. Of those reports giving system type, - * there were two DG/UX and two HP-UX, all using gcc as the compiler. - * hcroft@hpopb1.cern.ch, using gcc 2.6.3 on HP-UX, says that the - * problem went away for him and another reporter-to-newsgroup - * after adding this debugging code. This has almost got to be a - * compiler bug, but until somebody tracks it down and gets it fixed, - * might as well go with the "but it went away when I tried to find - * it" code. - */ - int cnttmp, cntdiv; - - cnttmp = cnt; - debugpline4("init group call <%d,%d>, n=%d, cnt=%d.", x, y, n, cnt); - cntdiv = ((u.ulevel < 3) ? 4 : (u.ulevel < 5) ? 2 : 1); -#endif - /* Tuning: cut down on swarming at low character levels [mrs] */ - cnt /= (u.ulevel < 3) ? 4 : (u.ulevel < 5) ? 2 : 1; -#if defined(__GNUC__) && (defined(HPUX) || defined(DGUX)) - if (cnt != (cnttmp / cntdiv)) { - pline("cnt=%d using %d, cnttmp=%d, cntdiv=%d", cnt, - (u.ulevel < 3) ? 4 : (u.ulevel < 5) ? 2 : 1, cnttmp, cntdiv); - } -#endif - if (!cnt) + /* Tuning: cut down on swarming at low dungeon levels */ + if (diff < 3) + cnt /= 4; + else if (diff < 5) + cnt /= 2; + else if (diff < 7) + cnt = (cnt * 3) / 4; + if (cnt <= 0) cnt++; -#if defined(__GNUC__) && (defined(HPUX) || defined(DGUX)) - if (cnt < 0) - cnt = 1; - if (cnt > 10) - cnt = 10; -#endif mm.x = x; mm.y = y; @@ -257,8 +233,7 @@ register struct monst *mtmp; || quest_mon_represents_role(ptr, PM_PRIEST)) { otmp = mksobj(MACE, FALSE, FALSE); otmp->spe = rnd(3); - if (!rn2(2)) - curse(otmp); + otmp->cursed = 0; (void) mpickobj(mtmp, otmp); } else if (mm == PM_NINJA) { /* extra quest villains */ (void) mongets(mtmp, rn2(4) ? SHURIKEN : DART); diff --git a/src/mcastu.c b/src/mcastu.c index f6b23986e3..b2cd11834e 100644 --- a/src/mcastu.c +++ b/src/mcastu.c @@ -45,8 +45,6 @@ static boolean FDECL(is_undirected_spell, (unsigned int, int)); static boolean FDECL(spell_would_be_useless, (struct monst *, unsigned int, int)); -extern const char *const flash_types[]; /* from zap.c */ - /* feedback when frustrated monster couldn't cast a spell */ static void @@ -859,9 +857,10 @@ register struct attack *mattk; if (lined_up(mtmp) && rn2(3)) { nomul(0); if (mattk->adtyp && (mattk->adtyp < 11)) { /* no cf unsigned >0 */ - if (canseemon(mtmp)) + if (canseemon(mtmp)) { pline("%s zaps you with a %s!", Monnam(mtmp), - flash_types[ad_to_typ(mattk->adtyp)]); + flash_str(ad_to_typ(mattk->adtyp), FALSE)); + } buzz(-ad_to_typ(mattk->adtyp), (int) mattk->damn, mtmp->mx, mtmp->my, sgn(g.tbx), sgn(g.tby)); } else diff --git a/src/mhitu.c b/src/mhitu.c index f28f0c6b73..a156b28653 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -1380,9 +1380,11 @@ register struct attack *mattk; case AD_SSEX: if (SYSOPT_SEDUCE) { - if (could_seduce(mtmp, &g.youmonst, mattk) == 1 && !mtmp->mcan) + if (could_seduce(mtmp, &g.youmonst, mattk) == 1 && !mtmp->mcan) { + mintroduce(mtmp); if (doseduce(mtmp)) return 3; + } break; } /*FALLTHRU*/ @@ -1406,18 +1408,21 @@ register struct attack *mattk; if (!tele_restrict(mtmp)) (void) rloc(mtmp, TRUE); return 3; - } else if (mtmp->mcan) { - if (!Blind) - pline("%s tries to %s you, but you seem %s.", - Adjmonnam(mtmp, "plain"), - flags.female ? "charm" : "seduce", - flags.female ? "unaffected" : "uninterested"); - if (rn2(3)) { - if (!tele_restrict(mtmp)) - (void) rloc(mtmp, TRUE); - return 3; + } else { + mintroduce(mtmp); + if (mtmp->mcan) { + if (!Blind) + pline("%s tries to %s you, but you seem %s.", + Adjmonnam(mtmp, "plain"), + flags.female ? "charm" : "seduce", + flags.female ? "unaffected" : "uninterested"); + if (rn2(3)) { + if (!tele_restrict(mtmp)) + (void) rloc(mtmp, TRUE); + return 3; + } + break; } - break; } buf[0] = '\0'; switch (steal(mtmp, buf)) { @@ -1846,6 +1851,7 @@ struct attack *mattk; struct obj *otmp2; int i; boolean physical_damage = FALSE; + boolean mon_killed = FALSE; if (!u.uswallow) { /* swallows you */ int omx = mtmp->mx, omy = mtmp->my; @@ -2059,6 +2065,29 @@ struct attack *mattk; drain_en(tmp); tmp = 0; break; + case AD_SLIM: + /* engulf attack always hits regardless of magic cancellation */ + if (mtmp->mcan) { + /* still engulfed, but don't get slimed */ + break; + } + if (flaming(g.youmonst.data)) { + pline("Your body burns through %s!", mon_nam(mtmp)); + /* kills the slime from the inside out - give experience but don't + * break pacifist because this wasn't an active kill */ + xkilled(mtmp, XKILL_NOMSG | XKILL_NOCORPSE | XKILL_NOCONDUCT); + mon_killed = TRUE; + } else if (Unchanging || noncorporeal(g.youmonst.data) + || g.youmonst.data == &mons[PM_GREEN_SLIME]) { + You("are covered in slime, but seem unaffected."); + } else if (!Slimed) { + You("don't feel very well."); + make_slimed(10L, (char *) 0); + delayed_killer(SLIMED, KILLED_BY_AN, mtmp->data->mname); + } else { + pline("Yuck!"); + } + break; default: physical_damage = TRUE; tmp = 0; @@ -2078,7 +2107,8 @@ struct attack *mattk; pline("%s very hurriedly %s you!", Monnam(mtmp), is_animal(mtmp->data) ? "regurgitates" : "expels"); expels(mtmp, mtmp->data, FALSE); - } else if (!u.uswldtim || g.youmonst.data->msize >= MZ_HUGE) { + } else if (!mon_killed + && (!u.uswldtim || g.youmonst.data->msize >= MZ_HUGE)) { /* As of 3.6.2: u.uswldtim used to be set to 0 by life-saving but it expels now so the !u.uswldtim case is no longer possible; however, polymorphing into a huge form while already @@ -2087,7 +2117,8 @@ struct attack *mattk; if (flags.verbose && (is_animal(mtmp->data) || (dmgtype(mtmp->data, AD_DGST) && Slow_digestion))) - pline("Obviously %s doesn't like your taste.", mon_nam(mtmp)); + pline("Obviously %s thinks you would give it indigestion.", + mon_nam(mtmp)); expels(mtmp, mtmp->data, FALSE); } return 1; diff --git a/src/mklev.c b/src/mklev.c index 07feca909c..b43067785c 100644 --- a/src/mklev.c +++ b/src/mklev.c @@ -1777,6 +1777,12 @@ struct mkroom *croom; /* Put a sink at m.x, m.y */ levl[m.x][m.y].typ = SINK; + /* All sinks have a ring stuck in the pipes below */ + struct obj* ring = mkobj(RING_CLASS, TRUE); + ring->ox = m.x; + ring->oy = m.y; + add_to_buried(ring); + g.level.flags.nsinks++; } @@ -2037,8 +2043,8 @@ xchar x, y; source = &br->end1; } - /* Already set or 2/3 chance of deferring until a later level. */ - if (source->dnum < g.n_dgns || (rn2(3) && !wizard)) + /* Already set. */ + if (source->dnum < g.n_dgns) return; if (!(u.uz.dnum == oracle_level.dnum /* in main dungeon */ diff --git a/src/mon.c b/src/mon.c index c90ddcb287..ca53c59907 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1281,10 +1281,14 @@ register struct monst *mtmp; } } +/* Monster tries to pick up an item. Return TRUE if something was picked up. + * mitem_wanted is a callback that takes an object and returns TRUE if the item + * should be picked up or FALSE if not. + */ boolean -mpickstuff(mtmp, str) +mpickstuff(mtmp, mitem_wanted) register struct monst *mtmp; -register const char *str; +boolean FDECL ((*mitem_wanted), (OBJ_P)); { register struct obj *otmp, *otmp2, *otmp3; int carryamt = 0; @@ -1296,8 +1300,8 @@ register const char *str; for (otmp = g.level.objects[mtmp->mx][mtmp->my]; otmp; otmp = otmp2) { otmp2 = otmp->nexthere; /* Nymphs take everything. Most monsters don't pick up corpses. */ - if (!str ? searches_for_item(mtmp, otmp) - : !!(index(str, otmp->oclass))) { + if ((mitem_wanted != NULL && (*mitem_wanted)(otmp)) + || (mitem_wanted == NULL && searches_for_item(mtmp, otmp))) { if (otmp->otyp == CORPSE && mtmp->data->mlet != S_NYMPH /* let a handful of corpse types thru to can_carry() */ && !touch_petrifies(&mons[otmp->corpsenm]) @@ -3422,7 +3426,8 @@ struct monst *mon; } } -/* unwatched hiders may hide again; if so, returns True */ +/* unwatched hiders may hide again; if so, returns True. + * Only applies to is_hider monsters, *not* hides_under monsters. */ static boolean restrap(mtmp) register struct monst *mtmp; @@ -3448,7 +3453,8 @@ register struct monst *mtmp; return FALSE; } -/* monster/hero tries to hide under something at the current location */ +/* monster/hero tries to hide under something at the current location. + * Only applies to hides_under monsters, *not* is_hider monsters. */ boolean hideunder(mtmp) struct monst *mtmp; @@ -3465,14 +3471,21 @@ struct monst *mtmp; ; /* can't hide while stuck in a non-pit trap */ } else if (mtmp->data->mlet == S_EEL) { undetected = (is_pool(x, y) && !Is_waterlevel(&u.uz)); - } else if (hides_under(mtmp->data) && OBJ_AT(x, y)) { - struct obj *otmp = g.level.objects[x][y]; + } else if (hides_under(mtmp->data)) { + int concealment = concealed_spot(x, y); + + if (concealment == 2) { /* object cover */ + struct obj *otmp = g.level.objects[x][y]; - /* most monsters won't hide under cockatrice corpse */ - if (otmp->nexthere || otmp->otyp != CORPSE - || (mtmp == &g.youmonst ? Stone_resistance : resists_ston(mtmp)) - || !touch_petrifies(&mons[otmp->corpsenm])) + /* most monsters won't hide under cockatrice corpse */ + if (otmp->nexthere || otmp->otyp != CORPSE + || (mtmp == &g.youmonst ? Stone_resistance : resists_ston(mtmp)) + || !touch_petrifies(&mons[otmp->corpsenm])) + undetected = TRUE; + } + else if (concealment == 1) { /* terrain cover, no objects */ undetected = TRUE; + } } if (is_u) diff --git a/src/monmove.c b/src/monmove.c index 8842f1b66d..9930c0d4b5 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -751,14 +751,55 @@ register struct monst *mtmp; return (tmp == 2); } -static NEARDATA const char practical[] = { WEAPON_CLASS, ARMOR_CLASS, - GEM_CLASS, FOOD_CLASS, 0 }; -static NEARDATA const char magical[] = { AMULET_CLASS, POTION_CLASS, - SCROLL_CLASS, WAND_CLASS, - RING_CLASS, SPBOOK_CLASS, 0 }; -static NEARDATA const char indigestion[] = { BALL_CLASS, ROCK_CLASS, 0 }; -static NEARDATA const char boulder_class[] = { ROCK_CLASS, 0 }; -static NEARDATA const char gem_class[] = { GEM_CLASS, 0 }; +/* Return true if a "practical" monster should be interested in this obj. */ +boolean +mitem_practical(obj) +struct obj * obj; +{ + switch(obj->oclass) { + case WEAPON_CLASS: + case ARMOR_CLASS: + case GEM_CLASS: + case FOOD_CLASS: + return TRUE; + default: + return FALSE; + } +} + +/* Return true if a monster that likes magical items should be interested in + * this obj. */ +boolean +mitem_magical(obj) +struct obj * obj; +{ + return objects[obj->otyp].oc_magic; +} + +/* Return true if a monster (gelatinous cube) shouldn't try to eat this obj. */ +boolean +mitem_indigestion(obj) +struct obj * obj; +{ + return (obj->oclass == BALL_CLASS || obj->oclass == ROCK_CLASS); +} + +/* Return true if a giant should be interested in this obj. (They like all + * rocks.)*/ +boolean +mitem_rock(obj) +struct obj * obj; +{ + return (obj->oclass == ROCK_CLASS); +} + +/* Return true if a gem-liking monster should be interested in this obj. */ +boolean +mitem_gem(obj) +struct obj * obj; +{ + return (obj->oclass == GEM_CLASS); +} boolean itsstuck(mtmp) @@ -894,8 +935,6 @@ register int after; finish_meating(mtmp); return 3; /* still eating */ } - if (hides_under(ptr) && OBJ_AT(mtmp->mx, mtmp->my) && rn2(10)) - return 0; /* do not leave hiding place */ /* Where does 'mtmp' think you are? Not necessary if m_move() called from this file, but needed for other calls of m_move(). */ @@ -1112,18 +1151,18 @@ register int after; continue; if (((likegold && otmp->oclass == COIN_CLASS) - || (likeobjs && index(practical, otmp->oclass) + || (likeobjs && mitem_practical(otmp) && (otmp->otyp != CORPSE || (ptr->mlet == S_NYMPH && !is_rider(&mons[otmp->corpsenm])))) - || (likemagic && index(magical, otmp->oclass)) + || (likemagic && mitem_magical(otmp)) || (uses_items && searches_for_item(mtmp, otmp)) || (likerock && otmp->otyp == BOULDER) || (likegems && otmp->oclass == GEM_CLASS && objects[otmp->otyp].oc_material != MINERAL) || (conceals && !cansee(otmp->ox, otmp->oy)) || (ptr == &mons[PM_GELATINOUS_CUBE] - && !index(indigestion, otmp->oclass) + && !mitem_indigestion(otmp) && !(otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm])))) && touch_artifact(otmp, mtmp)) { @@ -1316,6 +1355,13 @@ register int after; return 3; } + /* hiding-under monsters will attack things from their hiding spot but + * are less likely to venture out */ + if (hides_under(ptr) && concealed_spot(mtmp->mx, mtmp->my) + && !concealed_spot(nix, niy) && rn2(10)) { + return 0; + } + if ((info[chi] & ALLOW_MDISP)) { struct monst *mtmp2; int mstatus; @@ -1570,15 +1616,15 @@ register int after; boolean picked = FALSE; if (likeobjs) - picked |= mpickstuff(mtmp, practical); + picked |= mpickstuff(mtmp, mitem_practical); if (likemagic) - picked |= mpickstuff(mtmp, magical); + picked |= mpickstuff(mtmp, mitem_magical); if (likerock) - picked |= mpickstuff(mtmp, boulder_class); + picked |= mpickstuff(mtmp, mitem_rock); if (likegems) - picked |= mpickstuff(mtmp, gem_class); + picked |= mpickstuff(mtmp, mitem_gem); if (uses_items) - picked |= mpickstuff(mtmp, (char *) 0); + picked |= mpickstuff(mtmp, NULL); if (picked) mmoved = 3; } @@ -1606,6 +1652,30 @@ register int after; return mmoved; } +/* Return 1 or 2 if a hides_under monster can conceal itself at this spot. + * If the monster can hide under an object, return 2. + * Otherwise, monsters can hide in grass and under some types of dungeon + * furniture. If no object is available but the terrain is suitable, return 1. + * Return 0 if the monster can't conceal itself. + */ +int +concealed_spot(x, y) +register int x, y; +{ + if (OBJ_AT(x, y)) + return 2; + switch (levl[x][y].typ) { + case SINK: + case ALTAR: + case THRONE: + case LADDER: + case GRAVE: + return 1; + default: + return 0; + } +} + void dissolve_bars(x, y) register int x, y; diff --git a/src/monst.c b/src/monst.c index 9bbce0a277..6cb6afff71 100644 --- a/src/monst.c +++ b/src/monst.c @@ -310,7 +310,7 @@ NEARDATA struct permonst mons_init[] = { NO_ATTK), SIZ(10, 10, MS_SILENT, MZ_SMALL), 0, 0, M1_FLY | M1_AMPHIBIOUS | M1_NOLIMBS | M1_NOHEAD | M1_NOTAKE, - M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, 3, CLR_BLUE), + M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, 3, CLR_CYAN), MON("freezing sphere", S_EYE, LVL(6, 13, 4, 0, 0), (G_NOCORPSE | G_NOHELL | G_GENO | 2), A(ATTK(AT_EXPL, AD_COLD, 4, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, @@ -434,8 +434,9 @@ NEARDATA struct permonst mons_init[] = { MON("bugbear", S_HUMANOID, LVL(3, 9, 5, 0, -6), (G_GENO | 1), A(ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(1250, 250, MS_GROWL, MZ_LARGE), 0, 0, M1_HUMANOID | M1_OMNIVORE, - M2_STRONG | M2_COLLECT, M3_INFRAVISIBLE | M3_INFRAVISION, 5, CLR_BROWN), + SIZ(1250, 250, MS_GROWL, MZ_LARGE), MR_POISON, 0, + M1_HUMANOID | M1_OMNIVORE, M2_STRONG | M2_COLLECT, + M3_INFRAVISIBLE | M3_INFRAVISION, 5, CLR_BROWN), MON("dwarf lord", S_HUMANOID, LVL(4, 6, 10, 10, 5), (G_GENO | 2), A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), @@ -619,51 +620,58 @@ NEARDATA struct permonst mons_init[] = { MON("goblin", S_ORC, LVL(0, 6, 10, 0, -3), (G_GENO | 2), A(ATTK(AT_WEAP, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(400, 100, MS_ORC, MZ_SMALL), 0, 0, M1_HUMANOID | M1_OMNIVORE, - M2_ORC | M2_COLLECT, M3_INFRAVISIBLE | M3_INFRAVISION, 1, CLR_GRAY), + SIZ(400, 100, MS_ORC, MZ_SMALL), MR_POISON, 0, + M1_HUMANOID | M1_OMNIVORE, M2_ORC | M2_COLLECT, + M3_INFRAVISIBLE | M3_INFRAVISION, 1, CLR_GRAY), MON("hobgoblin", S_ORC, LVL(1, 9, 10, 0, -4), (G_GENO | 2), A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(1000, 200, MS_ORC, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, - M2_ORC | M2_STRONG | M2_COLLECT, M3_INFRAVISIBLE | M3_INFRAVISION, - 3, CLR_BROWN), + SIZ(1000, 200, MS_ORC, MZ_HUMAN), MR_POISON, 0, + M1_HUMANOID | M1_OMNIVORE, M2_ORC | M2_STRONG | M2_COLLECT, + M3_INFRAVISIBLE | M3_INFRAVISION, 3, CLR_BROWN), /* plain "orc" for zombie corpses only; not created at random */ MON("orc", S_ORC, LVL(1, 9, 10, 0, -3), (G_GENO | G_NOGEN | G_LGROUP), A(ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(850, 150, MS_ORC, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, + SIZ(850, 150, MS_ORC, MZ_HUMAN), MR_POISON, 0, + M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_ORC | M2_STRONG | M2_GREEDY | M2_JEWELS | M2_COLLECT, M3_INFRAVISIBLE | M3_INFRAVISION, 3, CLR_RED), MON("hill orc", S_ORC, LVL(2, 9, 10, 0, -4), (G_GENO | G_LGROUP | 2), A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(1000, 200, MS_ORC, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, + SIZ(1000, 200, MS_ORC, MZ_HUMAN), MR_POISON, 0, + M1_HUMANOID | M1_OMNIVORE, M2_ORC | M2_STRONG | M2_GREEDY | M2_JEWELS | M2_COLLECT, M3_INFRAVISIBLE | M3_INFRAVISION, 4, CLR_YELLOW), MON("Mordor orc", S_ORC, LVL(3, 5, 10, 0, -5), (G_GENO | G_LGROUP | 1), A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(1200, 200, MS_ORC, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, + SIZ(1200, 200, MS_ORC, MZ_HUMAN), MR_POISON, 0, + M1_HUMANOID | M1_OMNIVORE, M2_ORC | M2_STRONG | M2_GREEDY | M2_JEWELS | M2_COLLECT, M3_INFRAVISIBLE | M3_INFRAVISION, 5, CLR_BLUE), MON("Uruk-hai", S_ORC, LVL(3, 7, 10, 0, -4), (G_GENO | G_LGROUP | 1), A(ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(1300, 300, MS_ORC, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, + SIZ(1300, 300, MS_ORC, MZ_HUMAN), MR_POISON, 0, + M1_HUMANOID | M1_OMNIVORE, M2_ORC | M2_STRONG | M2_GREEDY | M2_JEWELS | M2_COLLECT, M3_INFRAVISIBLE | M3_INFRAVISION, 5, CLR_BLACK), MON("orc shaman", S_ORC, LVL(3, 9, 5, 10, -5), (G_GENO | 1), A(ATTK(AT_MAGC, AD_SPEL, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(1000, 300, MS_ORC, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, + SIZ(1000, 300, MS_ORC, MZ_HUMAN), MR_POISON, 0, + M1_HUMANOID | M1_OMNIVORE, M2_ORC | M2_STRONG | M2_GREEDY | M2_JEWELS | M2_MAGIC, M3_INFRAVISIBLE | M3_INFRAVISION, 5, HI_ZAP), - MON("orc-captain", S_ORC, LVL(5, 5, 10, 0, -5), (G_GENO | 1), + MON("orc-captain", S_ORC, LVL(5, 9, 10, 0, -5), (G_GENO | 1), A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(1350, 350, MS_ORC, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, - M2_ORC | M2_STRONG | M2_GREEDY | M2_JEWELS | M2_COLLECT, + SIZ(1350, 350, MS_ORC, MZ_HUMAN), MR_POISON, 0, + M1_HUMANOID | M1_OMNIVORE, + M2_ORC | M2_LORD | M2_STRONG | M2_GREEDY | M2_JEWELS | M2_COLLECT, M3_INFRAVISIBLE | M3_INFRAVISION, 7, HI_LORD), /* * piercers @@ -741,8 +749,8 @@ NEARDATA struct permonst mons_init[] = { A(ATTK(AT_BITE, AD_PHYS, 1, 3), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(20, 12, MS_SQEEK, MZ_TINY), 0, 0, - M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, - 1, CLR_BROWN), + M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE | M1_CONCEAL, M2_HOSTILE, + M3_INFRAVISIBLE, 1, CLR_BROWN), MON("giant rat", S_RODENT, LVL(1, 10, 7, 0, 0), (G_GENO | G_SGROUP | 2), A(ATTK(AT_BITE, AD_PHYS, 1, 3), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), @@ -753,8 +761,8 @@ NEARDATA struct permonst mons_init[] = { A(ATTK(AT_BITE, AD_DRCO, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(30, 5, MS_SQEEK, MZ_TINY), MR_POISON, 0, - M1_ANIMAL | M1_NOHANDS | M1_POIS | M1_CARNIVORE, M2_HOSTILE, - M3_INFRAVISIBLE, 4, CLR_BROWN), + M1_ANIMAL | M1_NOHANDS | M1_POIS | M1_CARNIVORE | M1_CONCEAL, + M2_HOSTILE, M3_INFRAVISIBLE, 4, CLR_BROWN), MON("wererat", S_RODENT, LVL(2, 12, 6, 10, -7), (G_NOGEN | G_NOCORPSE), A(ATTK(AT_BITE, AD_WERE, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), @@ -1716,8 +1724,8 @@ struct permonst _mons2[] = { M2_HOSTILE | M2_NEUTER, 0, 6, CLR_BROWN), MON("green slime", S_PUDDING, LVL(6, 6, 6, 0, 0), (G_HELL | G_GENO | G_NOCORPSE | 1), - A(ATTK(AT_TUCH, AD_SLIM, 1, 4), ATTK(AT_NONE, AD_SLIM, 0, 0), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + A(ATTK(AT_TUCH, AD_SLIM, 1, 4), ATTK(AT_ENGL, AD_SLIM, 1, 1), + ATTK(AT_NONE, AD_SLIM, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(400, 150, MS_SILENT, MZ_LARGE), MR_COLD | MR_ELEC | MR_POISON | MR_ACID | MR_STONE, 0, M1_BREATHLESS | M1_AMORPHOUS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD diff --git a/src/mthrowu.c b/src/mthrowu.c index 4bae7d7885..bba58dc706 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -26,6 +26,19 @@ static NEARDATA const char *breathwep[] = { "strange breath #9" }; +/* also used extern in zap.c + * need exact number because both files need to do rn2(SIZE(hallublasts)) */ +const char* const hallublasts[49] = { + "bubbles", "butterflies", "champagne", "chaos", "coins", "cotton candy", + "crumbs", "dark matter", "darkness", "emotions", "flowers", "fog", + "gelatin", "gemstones", "ghosts", "glass shards", "glitter", "gravel", + "gravity", "gravy", "holy light", "hornets", "hyphens", "laser beams", + "magma", "mathematics", "meteors", "music", "needles", "noise", "nostalgia", + "oil", "plasma", "prismatic light", "purple", "rope", "salt", "sand", + "scrolls", "sludge", "snowflakes", "sparkles", "spores", "steam", + "tetrahedrons", "text", "toxic waste", "water", "wind" +}; + boolean m_has_launcher_and_ammo(mtmp) struct monst *mtmp; @@ -790,6 +803,19 @@ struct attack *mattk; return 0; } +/* Return the name of a breath weapon. If the player is hallucinating, return + * a silly name instead. + * typ is AD_MAGM, AD_FIRE, etc */ +static const char* +breathwep_name(typ) +int typ; +{ + if (Hallucination) + return hallublasts[rn2(SIZE(hallublasts))]; + + return breathwep[typ - 1]; +} + /* monster breathes at monster (ranged) */ int breamm(mtmp, mattk, mtarg) @@ -813,7 +839,7 @@ struct attack *mattk; if ((typ >= AD_MAGM) && (typ <= AD_ACID)) { boolean utarget = (mtarg == &g.youmonst); if (canseemon(mtmp)) - pline("%s breathes %s!", Monnam(mtmp), breathwep[typ - 1]); + pline("%s breathes %s!", Monnam(mtmp), breathwep_name(typ)); dobuzz((int) (-20 - (typ - 1)), (int) mattk->damn, mtmp->mx, mtmp->my, sgn(g.tbx), sgn(g.tby), utarget); nomul(0); diff --git a/src/nhlua.c b/src/nhlua.c index b3906592bf..3df8d56edd 100644 --- a/src/nhlua.c +++ b/src/nhlua.c @@ -386,8 +386,6 @@ lua_State *L; (levl[x][y].flags & S_LPUDDING)); nhl_add_table_entry_bool(L, "dishwasher", (levl[x][y].flags & S_LDWASHER)); - nhl_add_table_entry_bool(L, "ring", - (levl[x][y].flags & S_LRING)); } /* TODO: drawbridges, walls, ladders, room=>ICED_xxx */ diff --git a/src/objects.c b/src/objects.c index 782e8019f3..59269cd139 100644 --- a/src/objects.c +++ b/src/objects.c @@ -480,13 +480,13 @@ SHIELD("small shield", None, SHIELD("elven shield", "blue and green shield", 0, 0, 0, 0, 2, 0, 40, 7, 8, 0, WOOD, CLR_GREEN), SHIELD("Uruk-hai shield", "white-handed shield", - 0, 0, 0, 0, 2, 0, 50, 7, 9, 0, IRON, HI_METAL), + 0, 0, 1, 0, 2, 0, 50, 7, 9, 0, IRON, HI_METAL), SHIELD("orcish shield", "red-eyed shield", 0, 0, 0, 0, 2, 0, 50, 7, 9, 0, IRON, CLR_RED), SHIELD("large shield", None, 1, 0, 1, 0, 7, 0, 100, 10, 8, 0, IRON, HI_METAL), SHIELD("dwarvish roundshield", "large round shield", - 0, 0, 0, 0, 4, 0, 100, 10, 8, 0, IRON, HI_METAL), + 0, 0, 1, 0, 4, 0, 100, 10, 8, 0, IRON, HI_METAL), SHIELD("shield of reflection", "polished silver shield", 0, 1, 0, REFLECTING, 3, 0, 50, 50, 8, 0, SILVER, HI_SILVER), diff --git a/src/objnam.c b/src/objnam.c index cbc539f20d..32520e3c37 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -4075,9 +4075,6 @@ struct obj *no_wish; case SPE_BOOK_OF_THE_DEAD: typ = SPE_BLANK_PAPER; break; - case MAGIC_LAMP: - typ = OIL_LAMP; - break; default: /* catch any other non-wishable objects (venom) */ if (objects[typ].oc_nowish) diff --git a/src/pager.c b/src/pager.c index 7a6acebd36..db355ef0fd 100644 --- a/src/pager.c +++ b/src/pager.c @@ -147,10 +147,16 @@ char *outbuf; Strcpy(outbuf, ", hiding"); if (hides_under(mon->data)) { Strcat(outbuf, " under "); - /* remembered glyph, not glyph_at() which is 'mon' */ - if (glyph_is_object(glyph)) - goto objfrommap; - Strcat(outbuf, something); + int hidetyp = concealed_spot(x, y); + if (hidetyp == 1) { /* hiding with terrain */ + Strcat(outbuf, explain_terrain(x, y)); + } + else { + /* remembered glyph, not glyph_at() which is 'mon' */ + if (glyph_is_object(glyph)) + goto objfrommap; + Strcat(outbuf, something); + } } else if (is_hider(mon->data)) { Sprintf(eos(outbuf), " on the %s", ceiling_hider(mon->data) ? "ceiling" diff --git a/src/polyself.c b/src/polyself.c index 5beef979f6..08310b744f 100644 --- a/src/polyself.c +++ b/src/polyself.c @@ -1531,7 +1531,7 @@ dohide() u.uundetected = 0; return 0; } - if (hides_under(g.youmonst.data) && !g.level.objects[u.ux][u.uy]) { + if (hides_under(g.youmonst.data) && !concealed_spot(u.ux, u.uy)) { There("is nothing to hide under here."); u.uundetected = 0; return 0; diff --git a/src/read.c b/src/read.c index 42b1040a2e..1234a611c8 100644 --- a/src/read.c +++ b/src/read.c @@ -1570,7 +1570,16 @@ struct obj *sobj; /* scroll, or fake spellbook object for scroll-like spell */ } case SCR_EARTH: /* TODO: handle steeds */ - if (!Is_rogue_level(&u.uz) && has_ceiling(&u.uz) + if (confused) { + /* create earth elementals and dust vortices */ + int i; + for (i = 0; i < (2 * (2 + sblessed - scursed)) - 1; ++i) { + makemon(&mons[(rn2(3) ? PM_EARTH_ELEMENTAL : PM_DUST_VORTEX)], + u.ux, u.uy, MM_NOWAIT); + } + pline("The earth moves around you!"); + } + else if (!Is_rogue_level(&u.uz) && has_ceiling(&u.uz) && (!In_endgame(&u.uz) || Is_earthlevel(&u.uz))) { register int x, y; int nboulders = 0; @@ -1594,13 +1603,13 @@ struct obj *sobj; /* scroll, or fake spellbook object for scroll-like spell */ && !IS_AIR(levl[x][y].typ) && (x != u.ux || y != u.uy)) { nboulders += - drop_boulder_on_monster(x, y, confused, TRUE); + drop_boulder_on_monster(x, y, FALSE, TRUE); } } } /* Attack the player */ if (!sblessed) { - drop_boulder_on_player(confused, !scursed, TRUE, FALSE); + drop_boulder_on_player(FALSE, !scursed, TRUE, FALSE); } else if (!nboulders) pline("But nothing else happens."); } @@ -1914,11 +1923,34 @@ struct obj *obj; g.rooms[rnum].rlit = on; } /* hallways remain dark on the rogue level */ - } else - do_clear_area(u.ux, u.uy, - (obj && obj->oclass == SCROLL_CLASS && obj->blessed) - ? 9 : 5, - set_lit, (genericptr_t) (on ? &is_lit : (char *) 0)); + } else { + int radius; + if(obj && obj->oclass == SCROLL_CLASS) { + /* blessed scroll lights up entire level */ + if(obj->blessed) { + int x, y; + radius = -1; + for (x = 1; x < COLNO; x++) { + for (y = 1; y < ROWNO; y++) { + set_lit(x, y, (on ? &is_lit : NULL)); + } + } + } + else { + /* uncursed gets a much larger area than the + * 5 it had previously */ + radius = 11; + } + } + else { + radius = 5; + } + + if(radius > 0) { + do_clear_area(u.ux, u.uy, radius, set_lit, + (genericptr_t) (on ? &is_lit : (char *) 0)); + } + } /* * If we are not blind, then force a redraw on all positions in sight diff --git a/src/role.c b/src/role.c index 98854f3dc8..47b74b215c 100644 --- a/src/role.c +++ b/src/role.c @@ -362,19 +362,6 @@ const struct Role roles[NUM_ROLES+1] = { -4 }, { { "Ranger", 0 }, { -#if 0 /* OBSOLETE */ - {"Edhel", "Elleth"}, - {"Edhel", "Elleth"}, /* elf-maid */ - {"Ohtar", "Ohtie"}, /* warrior */ - {"Kano", "Kanie"}, /* commander (Q.) ['a] educated guess, - until further research- SAC */ - {"Arandur"," Aranduriel"}, /* king's servant, minister (Q.) - guess */ - {"Hir", "Hiril"}, /* lord, lady (S.) ['ir] */ - {"Aredhel", "Arwen"}, /* noble elf, maiden (S.) */ - {"Ernil", "Elentariel"}, /* prince (S.), elf-maiden (Q.) */ - {"Elentar", "Elentari"}, /* Star-king, -queen (Q.) */ - "Solonor Thelandira", "Aerdrie Faenya", "Lolth", /* Elven */ -#endif { "Tenderfoot", 0 }, { "Lookout", 0 }, { "Trailblazer", 0 }, @@ -384,7 +371,7 @@ const struct Role roles[NUM_ROLES+1] = { { "Archer", 0 }, { "Sharpshooter", 0 }, { "Marksman", "Markswoman" } }, - "Mercury", "_Venus", "Mars", /* Roman/planets */ + "Apollo", "_Diana", "Mars", /* Roman/planets */ "Ran", "Orion's camp", "the cave of the wumpus", diff --git a/src/spell.c b/src/spell.c index 312ca44752..b971e4bdaa 100644 --- a/src/spell.c +++ b/src/spell.c @@ -42,6 +42,7 @@ static char *FDECL(spellretention, (int, char *)); static int NDECL(throwspell); static void NDECL(cast_protection); static void FDECL(spell_backfire, (int)); +static int FDECL(spell_hunger, (int)); static const char *FDECL(spelltypemnemonic, (int)); static boolean FDECL(spell_aim_step, (genericptr_t, int, int)); @@ -894,12 +895,47 @@ int spell; return; } +/* Given an expected amount of hunger for a spell, return the amount it should + * be reduced to. High-intelligence Wizards get to cast spells with less or no + * hunger penalty. */ +static int +spell_hunger(hungr) +int hungr; +{ + /* If hero is a wizard, their current intelligence + * (bonuses + temporary + current) + * affects hunger reduction in casting a spell. + * 1. int = 17-18 no reduction + * 2. int = 16 1/4 hungr + * 3. int = 15 1/2 hungr + * 4. int = 1-14 normal reduction + * The reason for this is: + * a) Intelligence affects the amount of exertion + * in thinking. + * b) Wizards have spent their life at magic and + * understand quite well how to cast spells. + */ + if (Role_if(PM_WIZARD)) { + int intel = acurr(A_INT); + if (intel >= 17) + return 0; + else if (intel == 16) + return hungr / 4; + else if (intel == 15) + return hungr / 2; + } + /* no adjustment */ + return hungr; +} +/* for using this function to test whether hunger would be eliminated */ +#define spell_would_hunger() (spell_hunger(100) > 0) + int spelleffects(spell, atme) int spell; boolean atme; { - int energy, damage, chance, n, intell; + int energy, damage, chance, n; int otyp, skill, role_skill, res = 0; boolean confused = (Confusion != 0); boolean physical_damage = FALSE; @@ -943,7 +979,8 @@ boolean atme; */ energy = (spellev(spell) * 5); /* 5 <= energy <= 35 */ - if (u.uhunger <= 10 && spellid(spell) != SPE_DETECT_FOOD) { + if (u.uhunger <= 10 && spellid(spell) != SPE_DETECT_FOOD + && spell_would_hunger() > 0) { You("are too hungry to cast that spell."); return 0; } else if (ACURR(A_STR) < 4 && spellid(spell) != SPE_RESTORE_ABILITY) { @@ -979,43 +1016,7 @@ boolean atme; return res; } else { if (spellid(spell) != SPE_DETECT_FOOD) { - int hungr = energy * 2; - - /* If hero is a wizard, their current intelligence - * (bonuses + temporary + current) - * affects hunger reduction in casting a spell. - * 1. int = 17-18 no reduction - * 2. int = 16 1/4 hungr - * 3. int = 15 1/2 hungr - * 4. int = 1-14 normal reduction - * The reason for this is: - * a) Intelligence affects the amount of exertion - * in thinking. - * b) Wizards have spent their life at magic and - * understand quite well how to cast spells. - */ - intell = acurr(A_INT); - if (!Role_if(PM_WIZARD)) - intell = 10; - switch (intell) { - case 25: - case 24: - case 23: - case 22: - case 21: - case 20: - case 19: - case 18: - case 17: - hungr = 0; - break; - case 16: - hungr /= 4; - break; - case 15: - hungr /= 2; - break; - } + int hungr = spell_hunger(energy * 2); /* don't put player (quite) into fainting from * casting a spell, particularly since they might * not even be hungry at the beginning; however, diff --git a/src/teleport.c b/src/teleport.c index f51001a7e8..63310ebc0f 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -996,6 +996,10 @@ level_tele() /* calls done(ESCAPED) if newlevel==0 */ if (escape_by_flying) { You("%s.", escape_by_flying); + /* Why can't you just walk back into the dungeon? + * This doesn't give an answer to that, but at least it states that + * there's some reason that you can't. */ + pline("But mortals cannot enter the Mazes of Menace more than once..."); /* [dlevel used to be set to 1, but it doesn't make sense to teleport out of the dungeon and float or fly down to the surface but then actually arrive back inside the dungeon] */ diff --git a/src/trap.c b/src/trap.c index 7aa3f3e71e..8cb0e79414 100644 --- a/src/trap.c +++ b/src/trap.c @@ -732,7 +732,7 @@ int *fail_reason; /* avoid hiding under nothing */ if (x == u.ux && y == u.uy && Upolyd && hides_under(g.youmonst.data) - && !OBJ_AT(x, y)) + && !concealed_spot(x, y)) u.uundetected = 0; if (fail_reason) @@ -1650,6 +1650,8 @@ struct obj *otmp; steedhit = TRUE; break; case POLY_TRAP: + deltrap(trap); + newsym(steed->mx, steed->my); if (!resists_magm(steed) && !resist(steed, WAND_CLASS, 0, NOTELL)) { struct permonst *mdat = steed->data; @@ -2683,11 +2685,14 @@ register struct monst *mtmp; if (resists_magm(mtmp)) { shieldeff(mtmp->mx, mtmp->my); } else if (!resist(mtmp, WAND_CLASS, 0, NOTELL)) { - if (newcham(mtmp, (struct permonst *) 0, FALSE, FALSE)) + if (newcham(mtmp, (struct permonst *) 0, FALSE, TRUE)) { /* we're done with mptr but keep it up to date */ mptr = mtmp->data; - if (in_sight) + deltrap(trap); + } + if (in_sight) { seetrap(trap); + } } break; case ROLLING_BOULDER_TRAP: diff --git a/src/uhitm.c b/src/uhitm.c index 53a82d7d3d..2d8240b90b 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -181,16 +181,18 @@ struct obj *wep; /* uwep for attack(), null for kick_monster() */ return FALSE; } if (!((Blind ? Blind_telepat : Unblind_telepat) || Detect_monsters)) { - struct obj *obj; if (!Blind && Hallucination) pline("A %s %s appeared!", mtmp->mtame ? "tame" : "wild", l_monnam(mtmp)); else if (Blind || (is_pool(mtmp->mx, mtmp->my) && !Underwater)) pline("Wait! There's a hidden monster there!"); - else if ((obj = g.level.objects[mtmp->mx][mtmp->my]) != 0) - pline("Wait! There's %s hiding under %s!", - an(l_monnam(mtmp)), doname(obj)); + else if (concealed_spot(mtmp->mx, mtmp->my)) { + struct obj *obj = g.level.objects[mtmp->mx][mtmp->my]; + pline("Wait! There's %s hiding under %s%s!", an(l_monnam(mtmp)), + obj ? "" : "the ", + obj ? doname(obj) : explain_terrain(mtmp->mx, mtmp->my)); + } return TRUE; } } @@ -2969,7 +2971,7 @@ boolean wep_was_destroyed; if (ureflects("%s gaze is reflected by your %s.", s_suffix(Monnam(mon)))) { ; - } else if (Hallucination && rn2(4)) { + } else if (Hallucination) { /* [it's the hero who should be getting paralyzed and isn't; this message describes the monster's reaction rather than the hero's escape] */ @@ -3252,8 +3254,10 @@ light_hits_gremlin(mon, dmg) struct monst *mon; int dmg; { - pline("%s %s!", Monnam(mon), - (dmg > mon->mhp / 2) ? "wails in agony" : "cries out in pain"); + if (canspotmon(mon)) { + pline("%s %s!", Monnam(mon), + (dmg > mon->mhp / 2) ? "wails in agony" : "cries out in pain"); + } mon->mhp -= dmg; wake_nearto(mon->mx, mon->my, 30); if (DEADMONSTER(mon)) { @@ -3261,8 +3265,13 @@ int dmg; monkilled(mon, (char *) 0, AD_BLND); else killed(mon); - } else if (cansee(mon->mx, mon->my) && !canspotmon(mon)) { - map_invisible(mon->mx, mon->my); + } else { + if (cansee(mon->mx, mon->my) && !canspotmon(mon)) { + map_invisible(mon->mx, mon->my); + } + if (!g.context.mon_moving) { + setmangry(mon, FALSE); + } } } diff --git a/src/zap.c b/src/zap.c index 3910b909a9..43b0b2b043 100644 --- a/src/zap.c +++ b/src/zap.c @@ -26,7 +26,7 @@ static void FDECL(backfire, (struct obj *)); static void FDECL(boxlock_invent, (struct obj *)); static int FDECL(spell_hit_bonus, (int)); static void FDECL(destroy_one_item, (struct obj *, int, int)); -static void FDECL(wishcmdassist, (int)); +static void NDECL(wishcmdassist); #define ZT_MAGIC_MISSILE (AD_MAGM - 1) #define ZT_FIRE (AD_FIRE - 1) @@ -67,6 +67,8 @@ const char *const flash_types[] = /* also used in buzzmu(mcastu.c) */ "blast of poison gas", "blast of acid", "", "" }; +extern const char* const hallublasts[49]; /* hallucinatory blasts [mthrowu.c] */ + /* * Recognizing unseen wands by zapping: in 3.4.3 and earlier, zapping * most wand types while blind would add that type to the discoveries @@ -1976,6 +1978,10 @@ struct obj *obj, *otmp; (void) boxlock(obj, otmp); if (obj_shudders(obj)) { + /* Do we need to possibly refresh our cover state? + * If we are currently hiding and the shuddering object might + * have been the only thing on our square, call hideunder + * again. */ boolean cover = ((obj == g.level.objects[u.ux][u.uy]) && u.uundetected && hides_under(g.youmonst.data)); @@ -2210,7 +2216,8 @@ int zappable(wand) register struct obj *wand; { - if (wand->spe < 0 || (wand->spe == 0 && rn2(121))) + int wrestchance = (wand->blessed ? 7 : (wand->cursed ? 121 : 23)); + if (wand->spe < 0 || (wand->spe == 0 && rn2(wrestchance))) return 0; if (wand->spe == 0) You("wrest one last charge from the worn-out wand."); @@ -2677,9 +2684,10 @@ ubreatheu(mattk) struct attack *mattk; { int dtyp = 20 + mattk->adtyp - 1; /* breath by hero */ - const char *fltxt = flash_types[dtyp]; /* blast of */ - zhitu(dtyp, mattk->damn, fltxt, u.ux, u.uy); + /* zhitu doesn't print the flash string; it only needs it for losehp and + * killer. Suppress hallucinatory ray names. */ + zhitu(dtyp, mattk->damn, flash_str(dtyp, TRUE), u.ux, u.uy); } /* light damages hero in gremlin form */ @@ -3353,6 +3361,21 @@ struct obj **pobj; /* object tossed/used, set to NULL typ = levl[g.bhitpos.x][g.bhitpos.y].typ; + if (typ == IRONBARS && weapon == ZAPPED_WAND + && ((levl[g.bhitpos.x][g.bhitpos.y].wall_info & W_NONDIGGABLE) == 0) + && (obj->otyp == SPE_FORCE_BOLT || obj->otyp == WAN_STRIKING)) { + levl[g.bhitpos.x][g.bhitpos.y].typ = ROOM; + if (cansee(g.bhitpos.x, g.bhitpos.y)) + pline_The("iron bars are blown apart!"); + else + You_hear("a lot of loud clanging sounds!"); + wake_nearto(g.bhitpos.x, g.bhitpos.y, 20 * 20); + newsym(g.bhitpos.x, g.bhitpos.y); + /* stop the bolt here; it takes a lot of energy to destroy bars */ + range = 0; + break; + } + /* iron bars will block anything big enough and break some things */ if (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON) { if (obj->lamplit && !Blind) @@ -4140,14 +4163,13 @@ boolean say; /* Announce out of sight hit/miss events if true */ struct monst *mon; coord save_bhitpos; boolean shopdamage = FALSE; - const char *fltxt; struct obj *otmp; int spell_type; + int typ = (type <= -30) ? abstype : abs(type); /* if its a Hero Spell then get its SPE_TYPE */ spell_type = is_hero_spell(type) ? SPE_MAGIC_MISSILE + abstype : 0; - fltxt = flash_types[(type <= -30) ? abstype : abs(type)]; if (u.uswallow) { register int tmp; @@ -4157,8 +4179,8 @@ boolean say; /* Announce out of sight hit/miss events if true */ if (!u.ustuck) u.uswallow = 0; else - pline("%s rips into %s%s", The(fltxt), mon_nam(u.ustuck), - exclam(tmp)); + pline("%s rips into %s%s", The(flash_str(typ, FALSE)), + mon_nam(u.ustuck), exclam(tmp)); /* Using disintegration from the inside only makes a hole... */ if (tmp == MAGIC_COOKIE) u.ustuck->mhp = 0; @@ -4215,7 +4237,7 @@ boolean say; /* Announce out of sight hit/miss events if true */ if (zap_hit(find_mac(mon), spell_type)) { if (mon_reflects(mon, (char *) 0)) { if (cansee(mon->mx, mon->my)) { - hit(fltxt, mon, exclam(0)); + hit(flash_str(typ, FALSE), mon, exclam(0)); shieldeff(mon->mx, mon->my); (void) mon_reflects(mon, "But it reflects from %s %s!"); @@ -4229,7 +4251,7 @@ boolean say; /* Announce out of sight hit/miss events if true */ if (is_rider(mon->data) && abs(type) == ZT_BREATH(ZT_DEATH)) { if (canseemon(mon)) { - hit(fltxt, mon, "."); + hit(flash_str(typ, FALSE), mon, "."); pline("%s disintegrates.", Monnam(mon)); pline("%s body reintegrates before your %s!", s_suffix(Monnam(mon)), @@ -4243,7 +4265,7 @@ boolean say; /* Announce out of sight hit/miss events if true */ } if (mon->data == &mons[PM_DEATH] && abstype == ZT_DEATH) { if (canseemon(mon)) { - hit(fltxt, mon, "."); + hit(flash_str(typ, FALSE), mon, "."); pline("%s absorbs the deadly %s!", Monnam(mon), type == ZT_BREATH(ZT_DEATH) ? "blast" : "ray"); @@ -4253,11 +4275,11 @@ boolean say; /* Announce out of sight hit/miss events if true */ } if (tmp == MAGIC_COOKIE) { /* disintegration */ - disintegrate_mon(mon, type, fltxt); + disintegrate_mon(mon, type, flash_str(typ, FALSE)); } else if (DEADMONSTER(mon)) { if (type < 0) { /* mon has just been killed by another monster */ - monkilled(mon, fltxt, AD_RBRE); + monkilled(mon, flash_str(typ, FALSE), AD_RBRE); } else { int xkflags = XKILL_GIVEMSG; /* killed(mon); */ @@ -4275,7 +4297,7 @@ boolean say; /* Announce out of sight hit/miss events if true */ if (!otmp) { /* normal non-fatal hit */ if (say || canseemon(mon)) - hit(fltxt, mon, exclam(tmp)); + hit(flash_str(typ, FALSE), mon, exclam(tmp)); } else { /* some armor was destroyed; no damage done */ if (canseemon(mon)) @@ -4291,7 +4313,7 @@ boolean say; /* Announce out of sight hit/miss events if true */ range -= 2; } else { if (say || canseemon(mon)) - miss(fltxt, mon); + miss(flash_str(typ, FALSE), mon); } } else if (sx == u.ux && sy == u.uy && range >= 0) { nomul(0); @@ -4300,7 +4322,7 @@ boolean say; /* Announce out of sight hit/miss events if true */ goto buzzmonst; } else if (zap_hit((int) u.uac, 0)) { range -= 2; - pline("%s hits you!", The(fltxt)); + pline("%s hits you!", The(flash_str(typ, FALSE))); if (Reflecting) { if (!Blind) { (void) ureflects("But %s reflects from your %s!", @@ -4311,10 +4333,12 @@ boolean say; /* Announce out of sight hit/miss events if true */ dy = -dy; shieldeff(sx, sy); } else { - zhitu(type, nd, fltxt, sx, sy); + /* flash_str here only used for killer; suppress + * hallucination */ + zhitu(type, nd, flash_str(typ, TRUE), sx, sy); } } else if (!Blind) { - pline("%s whizzes by you!", The(fltxt)); + pline("%s whizzes by you!", The(flash_str(typ, FALSE))); } else if (abstype == ZT_LIGHTNING) { Your("%s tingles.", body_part(ARM)); } @@ -4339,7 +4363,8 @@ boolean say; /* Announce out of sight hit/miss events if true */ if ((--range > 0 && isok(lsx, lsy) && cansee(lsx, lsy)) || fireball) { if (Is_airlevel(&u.uz)) { /* nothing to bounce off of */ - pline_The("%s vanishes into the aether!", fltxt); + pline_The("%s vanishes into the aether!", + flash_str(typ, FALSE)); if (fireball) type = ZT_WAND(ZT_FIRE); /* skip pending fireball */ break; @@ -4348,7 +4373,7 @@ boolean say; /* Announce out of sight hit/miss events if true */ sy = lsy; break; /* fireballs explode before the obstacle */ } else - pline_The("%s bounces!", fltxt); + pline_The("%s bounces!", flash_str(typ, FALSE)); } if (!dx || !dy || !rn2(bchance)) { dx = -dx; @@ -5323,11 +5348,8 @@ int damage, tell; return resisted; } -#define MAXWISHTRY 5 - static void -wishcmdassist(triesleft) -int triesleft; +wishcmdassist() { static NEARDATA const char * wishinfo[] = { @@ -5349,16 +5371,12 @@ int triesleft; 0, }, preserve_wishless[] = "Doing so will preserve 'wishless' conduct.", - retry_info[] = - "If you specify an unrecognized object name %s%s time%s,", - retry_too[] = "a randomly chosen item will be granted.", suppress_cmdassist[] = "(Suppress this assistance with !cmdassist in your config file.)", *cardinals[] = { "zero", "one", "two", "three", "four", "five" }, too_many[] = "too many"; int i; winid win; - char buf[BUFSZ]; win = create_nhwindow(NHW_TEXT); if (!win) @@ -5368,14 +5386,6 @@ int triesleft; if (!u.uconduct.wishes) putstr(win, 0, preserve_wishless); putstr(win, 0, ""); - Sprintf(buf, retry_info, - (triesleft >= 0 && triesleft < SIZE(cardinals)) - ? cardinals[triesleft] - : too_many, - (triesleft < MAXWISHTRY) ? " more" : "", - plur(triesleft)); - putstr(win, 0, buf); - putstr(win, 0, retry_too); putstr(win, 0, ""); if (iflags.cmdassist) putstr(win, 0, suppress_cmdassist); @@ -5405,10 +5415,17 @@ makewish() if (buf[0] == '\033') { buf[0] = '\0'; } else if (!strcmpi(buf, "help")) { - wishcmdassist(MAXWISHTRY - tries); + wishcmdassist(); buf[0] = '\0'; /* for EDIT_GETLIN */ goto retry; } + if (buf[0] == '\0') { + if (yn("Really forfeit this wish?") == 'y') { + Strcpy(buf, "nothing"); + } + else + goto retry; + } /* * Note: if they wished for and got a non-object successfully, * otmp == &zeroobj. That includes an artifact which has been denied. @@ -5417,12 +5434,8 @@ makewish() otmp = readobjnam(buf, ¬hing); if (!otmp) { pline("Nothing fitting that description exists in the game."); - if (++tries < MAXWISHTRY) - goto retry; - pline1(thats_enough_tries); - otmp = readobjnam((char *) 0, (struct obj *) 0); - if (!otmp) - return; /* for safety; should never happen */ + tries++; + goto retry; } else if (otmp == ¬hing) { /* explicitly wished for "nothing", presumably attempting to retain wishless conduct */ @@ -5451,4 +5464,26 @@ makewish() } } +/* Fills buf with the appropriate string for this ray. + * In the hallucination case, insert "blast of ". + * Assumes that the caller will specify typ in the appropriate range for + * wand/spell/breath weapon. */ +const char* +flash_str(typ, nohallu) +int typ; +boolean nohallu; /* suppress hallucination (for death reasons) */ +{ + static char fltxt[BUFSZ]; + if (Hallucination && !nohallu) { + /* always return "blast of foo" for simplicity. + * This could be extended with hallucinatory rays, but probably not worth + * it at this time. */ + Sprintf(fltxt, "blast of %s", hallublasts[rn2(SIZE(hallublasts))]); + } + else { + Strcpy(fltxt, flash_types[typ]); + } + return fltxt; +} + /*zap.c*/