Hero remembers trapped boxes

After finding a trap on a chest or a large box, remember it
as trapped: "You see here a trapped large box."
Randomly generated chests and boxes can be obviously trapped.
Allow defining obviously trapped containers via lua.

Invalidates saves and bones.
This commit is contained in:
Pasi Kallinen
2024-12-19 13:11:25 +02:00
parent 27a03ef75b
commit 87694e1a95
12 changed files with 75 additions and 36 deletions
+1
View File
@@ -1492,6 +1492,7 @@ artifact gifts are rebalanced (easier to obtain; higher-value sacrifices are
the first gift; artifacts you can't use well are less likely) the first gift; artifacts you can't use well are less likely)
luck gains from sacrificing are limited by the value of the sacrifice luck gains from sacrificing are limited by the value of the sacrifice
failed #untrap could move hero diagonally into or out of an open doorway failed #untrap could move hero diagonally into or out of an open doorway
remember box is trapped after finding the trap
Fixes to 3.7.0-x General Problems Exposed Via git Repository Fixes to 3.7.0-x General Problems Exposed Via git Repository
+1
View File
@@ -849,6 +849,7 @@ The table parameter accepts the following:
| eroded | int | Object erosion | eroded | int | Object erosion
| locked | boolean | Is the object locked? | locked | boolean | Is the object locked?
| trapped | boolean | Is the object trapped? | trapped | boolean | Is the object trapped?
| trap_known | boolean | If container is trapped, is it obvious?
| recharged | boolean | Is the object recharged? | recharged | boolean | Is the object recharged?
| greased | boolean | Is the object greased? | greased | boolean | Is the object greased?
| broken | boolean | Is the object broken? | broken | boolean | Is the object broken?
+1 -1
View File
@@ -141,9 +141,9 @@ struct obj {
* they have no locks */ * they have no locks */
Bitfield(pickup_prev, 1); /* was picked up previously */ Bitfield(pickup_prev, 1); /* was picked up previously */
Bitfield(ghostly, 1); /* it just got placed into a bones file */ Bitfield(ghostly, 1); /* it just got placed into a bones file */
Bitfield(tknown, 1); /* trap status known for chests */
#if 0 #if 0
/* not implemented */ /* not implemented */
Bitfield(tknown, 1); /* trap status known for chests */
Bitfield(eknown, 1); /* effect known for wands zapped or rings worn when Bitfield(eknown, 1); /* effect known for wands zapped or rings worn when
* not seen yet after being picked up while blind * not seen yet after being picked up while blind
* [maybe for remaining stack of used potion too] */ * [maybe for remaining stack of used potion too] */
+1 -1
View File
@@ -17,7 +17,7 @@
* Incrementing EDITLEVEL can be used to force invalidation of old bones * Incrementing EDITLEVEL can be used to force invalidation of old bones
* and save files. * and save files.
*/ */
#define EDITLEVEL 112 #define EDITLEVEL 113
/* /*
* Development status possibilities. * Development status possibilities.
+1 -1
View File
@@ -156,7 +156,7 @@ typedef struct {
int quan; int quan;
short buried; short buried;
short lit; short lit;
short eroded, locked, trapped, recharged, invis, greased, broken, short eroded, locked, trapped, tknown, recharged, invis, greased, broken,
achievement; achievement;
} object; } object;
+2
View File
@@ -930,6 +930,8 @@ detect_obj_traps(
continue; continue;
} }
if (Is_box(otmp) && otmp->otrapped) { if (Is_box(otmp) && otmp->otrapped) {
otmp->tknown = 1;
otmp->dknown = 1;
result |= u_at(x, y) ? OTRAP_HERE : OTRAP_THERE; result |= u_at(x, y) ? OTRAP_HERE : OTRAP_THERE;
if (ft) { if (ft) {
flash_glyph_at(x, y, trapglyph, FOUND_FLASH_COUNT); flash_glyph_at(x, y, trapglyph, FOUND_FLASH_COUNT);
+7 -5
View File
@@ -105,11 +105,12 @@ picklock(void)
: (gx.xlock.door->doormask & D_TRAPPED) != 0) : (gx.xlock.door->doormask & D_TRAPPED) != 0)
&& gx.xlock.magic_key) { && gx.xlock.magic_key) {
gx.xlock.chance += 20; /* less effort needed next time */ gx.xlock.chance += 20; /* less effort needed next time */
/* unfortunately we don't have a 'tknown' flag to record if (!gx.xlock.door) {
"known to be trapped" so declining to disarm and then if (!gx.xlock.box->tknown)
retrying lock manipulation will find it all over again */ You("find a trap!");
if (y_n("You find a trap! Do you want to try to disarm it?") gx.xlock.box->tknown = 1;
== 'y') { }
if (y_n("Do you want to try to disarm it?") == 'y') {
const char *what; const char *what;
boolean alreadyunlocked; boolean alreadyunlocked;
@@ -120,6 +121,7 @@ picklock(void)
alreadyunlocked = !(gx.xlock.door->doormask & D_LOCKED); alreadyunlocked = !(gx.xlock.door->doormask & D_LOCKED);
} else { } else {
gx.xlock.box->otrapped = 0; gx.xlock.box->otrapped = 0;
gx.xlock.box->tknown = 0;
what = (gx.xlock.box->otyp == CHEST) ? "chest" : "box"; what = (gx.xlock.box->otyp == CHEST) ? "chest" : "box";
alreadyunlocked = !gx.xlock.box->olocked; alreadyunlocked = !gx.xlock.box->olocked;
} }
+2
View File
@@ -852,6 +852,7 @@ unknow_object(struct obj *obj)
obj->bknown = obj->rknown = 0; obj->bknown = obj->rknown = 0;
obj->cknown = obj->lknown = 0; obj->cknown = obj->lknown = 0;
obj->tknown = 0;
/* for an existing object, awareness of charges or enchantment has /* for an existing object, awareness of charges or enchantment has
gone poof... [object types which don't use the known flag have gone poof... [object types which don't use the known flag have
it set True for some reason] */ it set True for some reason] */
@@ -1000,6 +1001,7 @@ mksobj_init(struct obj *otmp, boolean artif)
case LARGE_BOX: case LARGE_BOX:
otmp->olocked = !!(rn2(5)); otmp->olocked = !!(rn2(5));
otmp->otrapped = !(rn2(10)); otmp->otrapped = !(rn2(10));
otmp->tknown = otmp->otrapped && !rn2(100); /* obvious trap */
FALLTHROUGH; FALLTHROUGH;
/*FALLTHRU*/ /*FALLTHRU*/
case ICE_BOX: case ICE_BOX:
+1
View File
@@ -295,6 +295,7 @@ l_obj_to_table(lua_State *L)
nhl_add_table_entry_int(L, "dknown", obj->dknown); nhl_add_table_entry_int(L, "dknown", obj->dknown);
nhl_add_table_entry_int(L, "bknown", obj->bknown); nhl_add_table_entry_int(L, "bknown", obj->bknown);
nhl_add_table_entry_int(L, "rknown", obj->rknown); nhl_add_table_entry_int(L, "rknown", obj->rknown);
nhl_add_table_entry_int(L, "tknown", obj->tknown);
if (obj->oclass == POTION_CLASS) if (obj->oclass == POTION_CLASS)
nhl_add_table_entry_int(L, "odiluted", obj->odiluted); nhl_add_table_entry_int(L, "odiluted", obj->odiluted);
else else
+3
View File
@@ -1334,6 +1334,9 @@ doname_base(
Strcat(prefix, "uncursed "); Strcat(prefix, "uncursed ");
} }
/* "a large trapped box" would perhaps be more correct */
if (Is_box(obj) && obj->otrapped && obj->tknown && obj->dknown)
Strcat(prefix,"trapped ");
if (lknown && Is_box(obj)) { if (lknown && Is_box(obj)) {
if (obj->obroken) if (obj->obroken)
/* 3.6.0 used "unlockable" here but that could be misunderstood /* 3.6.0 used "unlockable" here but that could be misunderstood
+5 -1
View File
@@ -2277,6 +2277,8 @@ create_object(object *o, struct mkroom *croom)
} }
if (o->trapped == 0 || o->trapped == 1) if (o->trapped == 0 || o->trapped == 1)
otmp->otrapped = o->trapped; otmp->otrapped = o->trapped;
if (o->trapped && (o->tknown == 0 || o->tknown == 1))
otmp->tknown = o->tknown;
otmp->greased = o->greased ? 1 : 0; otmp->greased = o->greased ? 1 : 0;
if (o->quan > 0 && objects[otmp->otyp].oc_merge) { if (o->quan > 0 && objects[otmp->otyp].oc_merge) {
@@ -3517,7 +3519,7 @@ lspo_object(lua_State *L)
0, /* quan */ 0, /* quan */
0, /* buried */ 0, /* buried */
0, /* lit */ 0, /* lit */
0, 0, 0, 0, /* eroded, locked, trapped, recharged */ 0, 0, 0, 0, 0, /* eroded, locked, trapped, tknown, recharged */
0, 0, 0, 0, /* invis, greased, broken, achievement */ 0, 0, 0, 0, /* invis, greased, broken, achievement */
}; };
#if 0 #if 0
@@ -3537,6 +3539,7 @@ lspo_object(lua_State *L)
tmpobj.spe = -127; tmpobj.spe = -127;
tmpobj.quan = -1; tmpobj.quan = -1;
tmpobj.trapped = -1; tmpobj.trapped = -1;
tmpobj.tknown = -1;
tmpobj.locked = -1; tmpobj.locked = -1;
tmpobj.corpsenm = NON_PM; tmpobj.corpsenm = NON_PM;
@@ -3590,6 +3593,7 @@ lspo_object(lua_State *L)
tmpobj.eroded = get_table_int_opt(L, "eroded", 0); tmpobj.eroded = get_table_int_opt(L, "eroded", 0);
tmpobj.locked = get_table_boolean_opt(L, "locked", -1); tmpobj.locked = get_table_boolean_opt(L, "locked", -1);
tmpobj.trapped = get_table_boolean_opt(L, "trapped", -1); tmpobj.trapped = get_table_boolean_opt(L, "trapped", -1);
tmpobj.tknown = get_table_boolean_opt(L, "trap_known", -1);
tmpobj.recharged = get_table_int_opt(L, "recharged", 0); tmpobj.recharged = get_table_int_opt(L, "recharged", 0);
tmpobj.greased = get_table_boolean_opt(L, "greased", 0); tmpobj.greased = get_table_boolean_opt(L, "greased", 0);
tmpobj.broken = get_table_boolean_opt(L, "broken", 0); tmpobj.broken = get_table_boolean_opt(L, "broken", 0);
+50 -27
View File
@@ -65,6 +65,7 @@ staticfn void clear_conjoined_pits(struct trap *);
staticfn boolean adj_nonconjoined_pit(struct trap *); staticfn boolean adj_nonconjoined_pit(struct trap *);
staticfn int try_lift(struct monst *, struct trap *, int, boolean); staticfn int try_lift(struct monst *, struct trap *, int, boolean);
staticfn int help_monster_out(struct monst *, struct trap *); staticfn int help_monster_out(struct monst *, struct trap *);
staticfn void disarm_box(struct obj *, boolean, boolean);
staticfn void untrap_box(struct obj *, boolean, boolean); staticfn void untrap_box(struct obj *, boolean, boolean);
#if 0 #if 0
staticfn void join_adjacent_pits(struct trap *); staticfn void join_adjacent_pits(struct trap *);
@@ -5687,6 +5688,32 @@ help_monster_out(
return 1; return 1;
} }
staticfn void
disarm_box(struct obj *box, boolean force, boolean confused)
{
if (box->otrapped) {
int ch = ACURR(A_DEX) + u.ulevel;
if (Role_if(PM_ROGUE))
ch *= 2;
if (!force && (confused || Fumbling
|| rnd(75 + level_difficulty() / 2) > ch)) {
(void) chest_trap(box, FINGER, TRUE);
/* 'box' might be gone now */
} else {
You("disarm it!");
box->otrapped = 0;
box->tknown = 0;
more_experienced(8, 0);
newexplevel();
}
exercise(A_DEX, TRUE);
} else {
pline("That %s was not trapped.", xname(box));
box->tknown = 0;
}
}
/* check a particular container for a trap and optionally disarm it */ /* check a particular container for a trap and optionally disarm it */
staticfn void staticfn void
untrap_box( untrap_box(
@@ -5696,32 +5723,19 @@ untrap_box(
{ {
if ((box->otrapped if ((box->otrapped
&& (force || (!confused && rn2(MAXULEV + 1 - u.ulevel) < 10))) && (force || (!confused && rn2(MAXULEV + 1 - u.ulevel) < 10)))
|| box->tknown
|| (!force && confused && !rn2(3))) { || (!force && confused && !rn2(3))) {
You("find a trap on %s!", the(xname(box))); if (!(box->tknown && box->dknown))
You("find a trap on %s!", the(xname(box)));
else
pline("There's a trap on %s.", the(xname(box)));
box->tknown = 1;
box->dknown = 1;
if (!confused) if (!confused)
exercise(A_WIS, TRUE); exercise(A_WIS, TRUE);
if (ynq("Disarm it?") == 'y') { if (ynq("Disarm it?") == 'y')
if (box->otrapped) { disarm_box(box, force, confused);
int ch = ACURR(A_DEX) + u.ulevel;
if (Role_if(PM_ROGUE))
ch *= 2;
if (!force && (confused || Fumbling
|| rnd(75 + level_difficulty() / 2) > ch)) {
(void) chest_trap(box, FINGER, TRUE);
/* 'box' might be gone now */
} else {
You("disarm it!");
box->otrapped = 0;
more_experienced(8, 0);
newexplevel();
}
exercise(A_DEX, TRUE);
} else {
pline("That %s was not trapped.", xname(box));
}
}
} else { } else {
You("find no traps on %s.", the(xname(box))); You("find no traps on %s.", the(xname(box)));
} }
@@ -5882,20 +5896,28 @@ untrap(
whether any had been found but not attempted to untrap; whether any had been found but not attempted to untrap;
now at most one per move may be checked and we only now at most one per move may be checked and we only
continue on to door handling if they are all declined */ continue on to door handling if they are all declined */
for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp->nexthere) for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp->nexthere) {
if (Is_box(otmp)) { if (!Is_box(otmp))
continue;
if (otmp->tknown && otmp->dknown)
(void) safe_qbuf(qbuf, "Disarm this ", NULL,
otmp, xname, ansimpleoname, "a box");
else
(void) safe_qbuf(qbuf, "There is ", (void) safe_qbuf(qbuf, "There is ",
" here. Check it for traps?", otmp, " here. Check it for traps?", otmp,
doname, ansimpleoname, "a box"); doname, ansimpleoname, "a box");
switch (ynq(qbuf)) { switch (ynq(qbuf)) {
case 'q': case 'q':
return 0; return 0;
case 'y': case 'y':
untrap_box(otmp, force, confused); if (otmp->tknown && otmp->dknown)
disarm_box(otmp, force, confused);
else
untrap_box(otmp, force, confused);
return 1; /* even for 'no' at "Disarm it?" prompt */ return 1; /* even for 'no' at "Disarm it?" prompt */
} }
/* 'n' => continue to next box */ /* 'n' => continue to next box */
} }
There("are no other chests or boxes here."); There("are no other chests or boxes here.");
} }
@@ -6170,6 +6192,7 @@ chest_trap(
if (get_obj_location(obj, &cc.x, &cc.y, 0)) /* might be carried */ if (get_obj_location(obj, &cc.x, &cc.y, 0)) /* might be carried */
obj->ox = cc.x, obj->oy = cc.y; obj->ox = cc.x, obj->oy = cc.y;
otmp->tknown = 0;
otmp->otrapped = 0; /* trap is one-shot; clear flag first in case otmp->otrapped = 0; /* trap is one-shot; clear flag first in case
chest kills you and ends up in bones file */ chest kills you and ends up in bones file */
You(disarm ? "set it off!" : "trigger a trap!"); You(disarm ? "set it off!" : "trigger a trap!");