diff --git a/doc/fixes35.0 b/doc/fixes35.0 index 6fe0f6d42..7b885cf8f 100644 --- a/doc/fixes35.0 +++ b/doc/fixes35.0 @@ -236,6 +236,9 @@ obsolete config file keywords: GRAPHICS, OBJECTS, TRAPS, EFFECTS deprecated options: IBMGraphics, DECGraphics, boulder new options: symset, roguesymset for choosing a symbol set from the symbols file new config file keyword: SYMBOLS for overriding character symbol values by name +opening magic frees from bear traps and webs, activates trap doors +closing magic activates bear traps and webs +locking converts a hole into a trap door; striking does the opposite Platform- and/or Interface-Specific New Features diff --git a/include/extern.h b/include/extern.h index 118ef4ba2..f18970367 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2204,6 +2204,9 @@ E void FDECL(drain_en, (int)); E int NDECL(dountrap); E void FDECL(cnv_trap_obj, (int,int,struct trap *,BOOLEAN_P)); E int FDECL(untrap, (BOOLEAN_P)); +E boolean FDECL(openholdingtrap, (struct monst *,boolean *)); +E boolean FDECL(closeholdingtrap, (struct monst *,boolean *)); +E boolean FDECL(openfallingtrap, (struct monst *,BOOLEAN_P,boolean *)); E boolean FDECL(chest_trap, (struct obj *,int,BOOLEAN_P)); E void FDECL(deltrap, (struct trap *)); E boolean FDECL(delfloortrap, (struct trap *)); diff --git a/src/detect.c b/src/detect.c index 5320610a6..d24344f95 100644 --- a/src/detect.c +++ b/src/detect.c @@ -1112,13 +1112,14 @@ genericptr_t num; { register struct trap *ttmp; register struct obj *otmp; + int *num_p = (int *)num; if(OBJ_AT(zx, zy)) { for(otmp = level.objects[zx][zy]; otmp; otmp = otmp->nexthere) { if(Is_box(otmp) && otmp->olocked) { otmp->olocked = 0; - (*(int*)num)++; + (*num_p)++; } } /* let it fall to the next cases. could be on trap. */ @@ -1139,22 +1140,29 @@ genericptr_t num; levl[zx][zy].doormask = D_ISOPEN; unblock_point(zx, zy); newsym(zx, zy); - (*(int*)num)++; + (*num_p)++; } else if(levl[zx][zy].typ == SCORR) { levl[zx][zy].typ = CORR; unblock_point(zx, zy); newsym(zx, zy); - (*(int*)num)++; + (*num_p)++; } else if ((ttmp = t_at(zx, zy)) != 0) { + struct monst *mon; + boolean dummy; /* unneeded "you notice it arg" */ + if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) { ttmp->tseen = 1; newsym(zx,zy); - (*(int*)num)++; + (*num_p)++; } + mon = (zx == u.ux && zy == u.uy) ? &youmonst : m_at(zx, zy); + if (openholdingtrap(mon, &dummy) || + openfallingtrap(mon, TRUE, &dummy)) + (*num_p)++; } else if (find_drawbridge(&zx, &zy)) { /* make sure it isn't an open drawbridge */ open_drawbridge(zx, zy); - (*(int*)num)++; + (*num_p)++; } } diff --git a/src/trap.c b/src/trap.c index 6f60278ca..06a9c9d2f 100644 --- a/src/trap.c +++ b/src/trap.c @@ -34,6 +34,9 @@ STATIC_OVL boolean FDECL(keep_saddle_with_steedcorpse, (unsigned, struct obj *, struct obj *)); #endif +/* mintrap() should take a flags argument, but for time being we use this */ +STATIC_VAR int force_mintrap = 0; + STATIC_VAR const char * const a_your[2] = { "a", "your" }; STATIC_VAR const char * const A_Your[2] = { "A", "Your" }; STATIC_VAR const char tower_of_flame[] = "tower of flame"; @@ -1066,18 +1069,20 @@ glovecheck: (void) rust_dmg(uarmg, "gauntlets", 1, TRUE, &youmonst); if (webmsgok) { char verbbuf[BUFSZ]; + if (forcetrap) { + Strcpy(verbbuf, "are caught by"); #ifdef STEED - if (u.usteed) - Sprintf(verbbuf, "lead %s", + } else if (u.usteed) { + Sprintf(verbbuf, "lead %s into", x_monnam(u.usteed, steed_article, "poor", SUPPRESS_SADDLE, FALSE)); - else #endif - - Sprintf(verbbuf, "%s", Levitation ? (const char *)"float" : - locomotion(youmonst.data, "stumble")); - You("%s into %s spider web!", - verbbuf, a_your[trap->madeby_u]); + } else { + Sprintf(verbbuf, "%s into", + Levitation ? (const char *)"float" : + locomotion(youmonst.data, "stumble")); + } + You("%s %s spider web!", verbbuf, a_your[trap->madeby_u]); } u.utraptype = TT_WEB; @@ -1833,7 +1838,8 @@ register struct monst *mtmp; } else { register int tt = trap->ttyp; boolean in_sight, tear_web, see_it, - inescapable = ((tt == HOLE || tt == PIT) && + inescapable = force_mintrap || + ((tt == HOLE || tt == PIT) && In_sokoban(&u.uz) && !trap->madeby_u); const char *fallverb; @@ -1940,6 +1946,12 @@ register struct monst *mtmp; && !Deaf) You_hear("the roaring of an angry bear!"); } + } else if (force_mintrap) { + if (in_sight) { + pline("%s evades %s bear trap!", + Monnam(mtmp), a_your[trap->madeby_u]); + seetrap(trap); + } } break; @@ -2079,6 +2091,15 @@ glovecheck: target = which_armor(mtmp, W_ARMG); if (is_flyer(mptr) || is_floater(mptr) || (mtmp->wormno && count_wsegs(mtmp) > 5) || is_clinger(mptr)) { + if (force_mintrap && !In_sokoban(&u.uz)) { + /* openfallingtrap; not inescapable here */ + if (in_sight) { + seetrap(trap); + pline("%s doesn't fall into the pit.", + Monnam(mtmp)); + } + break; /* inescapable = FALSE; */ + } if (!inescapable) break; /* avoids trap */ fallverb = "is dragged"; /* sokoban pit */ } @@ -2109,6 +2130,21 @@ glovecheck: target = which_armor(mtmp, W_ARMG); mptr == &mons[PM_WUMPUS] || (mtmp->wormno && count_wsegs(mtmp) > 5) || mptr->msize >= MZ_HUGE) { + if (force_mintrap && !In_sokoban(&u.uz)) { + /* openfallingtrap; not inescapable here */ + if (in_sight) { + seetrap(trap); + if (tt == TRAPDOOR) + pline( + "A trap door opens, but %s doesn't fall through.", + mon_nam(mtmp)); + else /* (tt == HOLE) */ + pline( + "%s doesn't fall through the hole.", + Monnam(mtmp)); + } + break; /* inescapable = FALSE; */ + } if (inescapable) { /* sokoban hole */ if (in_sight) { pline("%s seems to be yanked down!", @@ -2207,6 +2243,12 @@ glovecheck: target = which_armor(mtmp, W_ARMG); Monnam(mtmp), a_your[trap->madeby_u]); deltrap(trap); newsym(mtmp->mx, mtmp->my); + } else if (force_mintrap && !mtmp->mtrapped) { + if (in_sight) { + pline("%s avoids %s spider web!", + Monnam(mtmp), a_your[trap->madeby_u]); + seetrap(trap); + } } break; @@ -3817,6 +3859,148 @@ boolean force; } } +/* for magic unlocking; returns true if targetted monster (which might + be hero) gets untrapped; the trap remains intact */ +boolean +openholdingtrap(mon, noticed) +struct monst *mon; +boolean *noticed; /* set to true iff hero notices the effect; */ +{ /* otherwise left with its previous value intact */ + struct trap *t; + char buf[BUFSZ]; + const char *trapdescr, *which; + boolean ishero = (mon == &youmonst); + +#ifdef STEED + if (mon == u.usteed) ishero = TRUE; +#endif + t = t_at(ishero ? u.ux : mon->mx, ishero ? u.uy : mon->my); + /* if no trap here or it's not a holding trap, we're done */ + if (!t || (t->ttyp != BEAR_TRAP && t->ttyp != WEB)) return FALSE; + + trapdescr = defsyms[trap_to_defsym(t->ttyp)].explanation; + which = t->tseen ? the_your[t->madeby_u] : + index(vowels, *trapdescr) ? "an" : "a"; + + if (ishero) { + if (!u.utrap) return FALSE; + u.utrap = 0; /* released regardless of type */ + *noticed = TRUE; + /* give message only if trap was the expected type */ + if (u.utraptype == TT_BEARTRAP || u.utraptype == TT_WEB) { +#ifdef STEED + if (u.usteed) + Sprintf(buf, "%s is", noit_Monnam(u.usteed)); + else +#endif + Strcpy(buf, "You are"); + pline("%s released from %s %s.", buf, which, trapdescr); + } + } else { + if (!mon->mtrapped) return FALSE; + mon->mtrapped = 0; + if (canspotmon(mon)) { + *noticed = TRUE; + pline("%s is released from %s %s.", + Monnam(mon), which, trapdescr); + } else if (cansee(t->tx, t->ty) && t->tseen) { + *noticed = TRUE; + if (t->ttyp == WEB) + pline("%s is released from %s %s.", + Something, which, trapdescr); + else /* BEAR_TRAP */ + pline("%s %s opens.", upstart(strcpy(buf, which)), trapdescr); + } + /* might pacify monster if adjacent */ + if (rn2(2) && distu(mon->mx, mon->my) <= 2) reward_untrap(t, mon); + } + return TRUE; +} + +/* for magic locking; returns true if targetted monster (which might + be hero) gets hit by a trap (might avoid actually becoming trapped) */ +boolean +closeholdingtrap(mon, noticed) +struct monst *mon; +boolean *noticed; /* set to true iff hero notices the effect; */ +{ /* otherwise left with its previous value intact */ + struct trap *t; + unsigned dotrapflags; + boolean ishero = (mon == &youmonst), result; + +#ifdef STEED + if (mon == u.usteed) ishero = TRUE; +#endif + t = t_at(ishero ? u.ux : mon->mx, ishero ? u.uy : mon->my); + /* if no trap here or it's not a holding trap, we're done */ + if (!t || (t->ttyp != BEAR_TRAP && t->ttyp != WEB)) return FALSE; + + if (ishero) { + if (u.utrap) return FALSE; /* already trapped */ + *noticed = TRUE; + dotrapflags = FORCETRAP; +#ifdef STEED + /* dotrap calls mintrap when mounted hero encounters a web */ + if (u.usteed) dotrapflags |= NOWEBMSG; +#endif + ++force_mintrap; + dotrap(t, dotrapflags); + --force_mintrap; + result = (u.utrap != 0); + } else { + if (mon->mtrapped) return FALSE; /* already trapped */ + /* you notice it if you see the trap close/tremble/whatever + or if you sense the monster who becomes trapped */ + *noticed = cansee(t->tx, t->ty) || canspotmon(mon); + ++force_mintrap; + result = (mintrap(mon) != 0); + --force_mintrap; + } + return result; +} + +/* for magic unlocking; returns true if targetted monster (which might + be hero) gets hit by a trap (target might avoid its effect) */ +boolean +openfallingtrap(mon, trapdoor_only, noticed) +struct monst *mon; +boolean trapdoor_only; +boolean *noticed; /* set to true iff hero notices the effect; */ +{ /* otherwise left with its previous value intact */ + struct trap *t; + boolean ishero = (mon == &youmonst), result; + +#ifdef STEED + if (mon == u.usteed) ishero = TRUE; +#endif + t = t_at(ishero ? u.ux : mon->mx, ishero ? u.uy : mon->my); + /* if no trap here or it's not a falling trap, we're done + (note: falling rock traps have a trapdoor in the ceiling) */ + if (!t || ((t->ttyp != TRAPDOOR && t->ttyp != ROCKTRAP) && + (trapdoor_only || + (t->ttyp != HOLE && t->ttyp != PIT && t->ttyp != SPIKED_PIT)))) + return FALSE; + + if (ishero) { + if (u.utrap) return FALSE; /* already trapped */ + *noticed = TRUE; + dotrap(t, FORCETRAP); + result = (u.utrap != 0); + } else { + if (mon->mtrapped) return FALSE; /* already trapped */ + /* you notice it if you see the trap close/tremble/whatever + or if you sense the monster who becomes trapped */ + *noticed = cansee(t->tx, t->ty) || canspotmon(mon); + /* monster will be angered; mintrap doesn't handle that */ + wakeup(mon); + ++force_mintrap; + result = (mintrap(mon) != 0); + --force_mintrap; + /* mon might now be on the migrating monsters list */ + } + return TRUE; +} + /* only called when the player is doing something to the chest directly */ boolean chest_trap(obj, bodypart, disarm) diff --git a/src/zap.c b/src/zap.c index 76a400316..db7cc3f43 100644 --- a/src/zap.c +++ b/src/zap.c @@ -270,10 +270,9 @@ struct obj *otmp; } break; } - case WAN_NOTHING: case WAN_LOCKING: case SPE_WIZARD_LOCK: - wake = FALSE; + wake = closeholdingtrap(mtmp, &learn_it); break; case WAN_PROBING: wake = FALSE; @@ -290,8 +289,16 @@ struct obj *otmp; else pline("%s opens its mouth!", Monnam(mtmp)); } expels(mtmp, mtmp->data, TRUE); + /* zap which hits steed will only release saddle if it + doesn't hit a holding or falling trap; playability + here overrides the more logical target ordering */ + } else if (openholdingtrap(mtmp, &learn_it)) { + break; + } else if (openfallingtrap(mtmp, TRUE, &learn_it)) { + /* mtmp might now be on the migrating monsters list */ + break; #ifdef STEED - } else if (!!(obj = which_armor(mtmp, W_SADDLE))) { + } else if ((obj = which_armor(mtmp, W_SADDLE)) != 0) { mtmp->misc_worn_check &= ~obj->owornmask; update_mon_intrinsics(mtmp, obj, FALSE, FALSE); obj->owornmask = 0L; @@ -384,6 +391,9 @@ struct obj *otmp; } } break; + case WAN_NOTHING: + wake = FALSE; + break; default: impossible("What an interesting effect (%d)", otyp); break; @@ -2198,13 +2208,22 @@ boolean ordinary; learn_it = TRUE; unpunish(); } + if (u.utrap) { /* escape web or bear trap */ + (void) openholdingtrap(&youmonst, &learn_it); + } else { /* trigger previously escaped trapdoor */ + (void) openfallingtrap(&youmonst, TRUE, &learn_it); + } + break; + case WAN_LOCKING: + case SPE_WIZARD_LOCK: + if (!u.utrap) { + (void) closeholdingtrap(&youmonst, &learn_it); + } break; case WAN_DIGGING: case SPE_DIG: case SPE_DETECT_UNSEEN: case WAN_NOTHING: - case WAN_LOCKING: - case SPE_WIZARD_LOCK: break; case WAN_PROBING: { @@ -2445,6 +2464,13 @@ struct obj *obj; /* wand or spell */ pline_The("stairs seem to ripple momentarily."); disclose = TRUE; } + /* down will release you from bear trap or web */ + if (u.dz > 0 && u.utrap) { + (void) openholdingtrap(&youmonst, &disclose); + /* down will trigger trapdoor, hole, or [spiked-] pit */ + } else if (u.dz > 0 && !u.utrap) { + (void) openfallingtrap(&youmonst, FALSE, &disclose); + } break; case WAN_STRIKING: case SPE_FORCE_BOLT: @@ -2453,8 +2479,8 @@ struct obj *obj; /* wand or spell */ case WAN_LOCKING: case SPE_WIZARD_LOCK: /* down at open bridge or up or down at open portcullis */ - if ((levl[x][y].typ == DRAWBRIDGE_DOWN) ? (u.dz > 0) : - (is_drawbridge_wall(x,y) && !is_db_wall(x,y)) && + if (((levl[x][y].typ == DRAWBRIDGE_DOWN) ? (u.dz > 0) : + (is_drawbridge_wall(x,y) && !is_db_wall(x,y))) && find_drawbridge(&xx, &yy)) { if (!striking) close_drawbridge(xx, yy); @@ -2476,21 +2502,38 @@ struct obj *obj; /* wand or spell */ stackobj(otmp); } newsym(x, y); - } else if (!striking && ttmp && ttmp->ttyp == TRAPDOOR && u.dz > 0) { - if (!Blind) { - if (ttmp->tseen) { - pline("A trap door beneath you closes up then vanishes."); - disclose = TRUE; - } else { - You_see("a swirl of %s beneath you.", - is_ice(x,y) ? "frost" : "dust"); - } - } else { - You_hear("a twang followed by a thud."); + } else if (u.dz > 0 && ttmp) { + if (!striking && closeholdingtrap(&youmonst, &disclose)) { + ; /* now stuck in web or bear trap */ + } else if (striking && ttmp->ttyp == TRAPDOOR) { + /* striking transforms trapdoor into hole */ + if (Blind && !ttmp->tseen) { + pline("%s beneath you shatters.", Something); + } else if (!ttmp->tseen) { /* => !Blind */ + pline("There's a trapdoor beneath you; it shatters."); + } else { + pline("The trapdoor beneath you shatters."); + disclose = TRUE; + } + ttmp->ttyp = HOLE; + ttmp->tseen = 1; + newsym(x, y); + /* might fall down hole */ + dotrap(ttmp, 0); + } else if (!striking && ttmp->ttyp == HOLE) { + /* locking transforms hole into trapdoor */ + ttmp->ttyp = TRAPDOOR; + if (Blind || !ttmp->tseen) { + pline("Some %s swirls beneath you.", + is_ice(x,y) ? "frost" : "dust"); + } else { + ttmp->tseen = 1; + newsym(x, y); + pline("A trapdoor appears beneath you."); + disclose = TRUE; + } + /* hadn't fallen down hole; won't fall now */ } - deltrap(ttmp); - ttmp = (struct trap *)0; - newsym(x, y); } break; case SPE_STONE_TO_FLESH: