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 12:37:13 +02:00
parent 27a03ef75b
commit 87694e1a95
12 changed files with 75 additions and 36 deletions

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)
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
remember box is trapped after finding the trap
Fixes to 3.7.0-x General Problems Exposed Via git Repository

View File

@@ -849,6 +849,7 @@ The table parameter accepts the following:
| eroded | int | Object erosion
| locked | boolean | Is the object locked?
| trapped | boolean | Is the object trapped?
| trap_known | boolean | If container is trapped, is it obvious?
| recharged | boolean | Is the object recharged?
| greased | boolean | Is the object greased?
| broken | boolean | Is the object broken?

View File

@@ -141,9 +141,9 @@ struct obj {
* they have no locks */
Bitfield(pickup_prev, 1); /* was picked up previously */
Bitfield(ghostly, 1); /* it just got placed into a bones file */
Bitfield(tknown, 1); /* trap status known for chests */
#if 0
/* not implemented */
Bitfield(tknown, 1); /* trap status known for chests */
Bitfield(eknown, 1); /* effect known for wands zapped or rings worn when
* not seen yet after being picked up while blind
* [maybe for remaining stack of used potion too] */

View File

@@ -17,7 +17,7 @@
* Incrementing EDITLEVEL can be used to force invalidation of old bones
* and save files.
*/
#define EDITLEVEL 112
#define EDITLEVEL 113
/*
* Development status possibilities.

View File

@@ -156,7 +156,7 @@ typedef struct {
int quan;
short buried;
short lit;
short eroded, locked, trapped, recharged, invis, greased, broken,
short eroded, locked, trapped, tknown, recharged, invis, greased, broken,
achievement;
} object;

View File

@@ -930,6 +930,8 @@ detect_obj_traps(
continue;
}
if (Is_box(otmp) && otmp->otrapped) {
otmp->tknown = 1;
otmp->dknown = 1;
result |= u_at(x, y) ? OTRAP_HERE : OTRAP_THERE;
if (ft) {
flash_glyph_at(x, y, trapglyph, FOUND_FLASH_COUNT);

View File

@@ -105,11 +105,12 @@ picklock(void)
: (gx.xlock.door->doormask & D_TRAPPED) != 0)
&& gx.xlock.magic_key) {
gx.xlock.chance += 20; /* less effort needed next time */
/* unfortunately we don't have a 'tknown' flag to record
"known to be trapped" so declining to disarm and then
retrying lock manipulation will find it all over again */
if (y_n("You find a trap! Do you want to try to disarm it?")
== 'y') {
if (!gx.xlock.door) {
if (!gx.xlock.box->tknown)
You("find a trap!");
gx.xlock.box->tknown = 1;
}
if (y_n("Do you want to try to disarm it?") == 'y') {
const char *what;
boolean alreadyunlocked;
@@ -120,6 +121,7 @@ picklock(void)
alreadyunlocked = !(gx.xlock.door->doormask & D_LOCKED);
} else {
gx.xlock.box->otrapped = 0;
gx.xlock.box->tknown = 0;
what = (gx.xlock.box->otyp == CHEST) ? "chest" : "box";
alreadyunlocked = !gx.xlock.box->olocked;
}

View File

@@ -852,6 +852,7 @@ unknow_object(struct obj *obj)
obj->bknown = obj->rknown = 0;
obj->cknown = obj->lknown = 0;
obj->tknown = 0;
/* for an existing object, awareness of charges or enchantment has
gone poof... [object types which don't use the known flag have
it set True for some reason] */
@@ -1000,6 +1001,7 @@ mksobj_init(struct obj *otmp, boolean artif)
case LARGE_BOX:
otmp->olocked = !!(rn2(5));
otmp->otrapped = !(rn2(10));
otmp->tknown = otmp->otrapped && !rn2(100); /* obvious trap */
FALLTHROUGH;
/*FALLTHRU*/
case ICE_BOX:

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, "bknown", obj->bknown);
nhl_add_table_entry_int(L, "rknown", obj->rknown);
nhl_add_table_entry_int(L, "tknown", obj->tknown);
if (obj->oclass == POTION_CLASS)
nhl_add_table_entry_int(L, "odiluted", obj->odiluted);
else

View File

@@ -1334,6 +1334,9 @@ doname_base(
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 (obj->obroken)
/* 3.6.0 used "unlockable" here but that could be misunderstood

View File

@@ -2277,6 +2277,8 @@ create_object(object *o, struct mkroom *croom)
}
if (o->trapped == 0 || o->trapped == 1)
otmp->otrapped = o->trapped;
if (o->trapped && (o->tknown == 0 || o->tknown == 1))
otmp->tknown = o->tknown;
otmp->greased = o->greased ? 1 : 0;
if (o->quan > 0 && objects[otmp->otyp].oc_merge) {
@@ -3517,7 +3519,7 @@ lspo_object(lua_State *L)
0, /* quan */
0, /* buried */
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 */
};
#if 0
@@ -3537,6 +3539,7 @@ lspo_object(lua_State *L)
tmpobj.spe = -127;
tmpobj.quan = -1;
tmpobj.trapped = -1;
tmpobj.tknown = -1;
tmpobj.locked = -1;
tmpobj.corpsenm = NON_PM;
@@ -3590,6 +3593,7 @@ lspo_object(lua_State *L)
tmpobj.eroded = get_table_int_opt(L, "eroded", 0);
tmpobj.locked = get_table_boolean_opt(L, "locked", -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.greased = get_table_boolean_opt(L, "greased", 0);
tmpobj.broken = get_table_boolean_opt(L, "broken", 0);

View File

@@ -65,6 +65,7 @@ staticfn void clear_conjoined_pits(struct trap *);
staticfn boolean adj_nonconjoined_pit(struct trap *);
staticfn int try_lift(struct monst *, struct trap *, int, boolean);
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);
#if 0
staticfn void join_adjacent_pits(struct trap *);
@@ -5687,6 +5688,32 @@ help_monster_out(
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 */
staticfn void
untrap_box(
@@ -5696,32 +5723,19 @@ untrap_box(
{
if ((box->otrapped
&& (force || (!confused && rn2(MAXULEV + 1 - u.ulevel) < 10)))
|| box->tknown
|| (!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)
exercise(A_WIS, TRUE);
if (ynq("Disarm it?") == 'y') {
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;
more_experienced(8, 0);
newexplevel();
}
exercise(A_DEX, TRUE);
} else {
pline("That %s was not trapped.", xname(box));
}
}
if (ynq("Disarm it?") == 'y')
disarm_box(box, force, confused);
} else {
You("find no traps on %s.", the(xname(box)));
}
@@ -5882,20 +5896,28 @@ untrap(
whether any had been found but not attempted to untrap;
now at most one per move may be checked and we only
continue on to door handling if they are all declined */
for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp->nexthere)
if (Is_box(otmp)) {
for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp->nexthere) {
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 ",
" here. Check it for traps?", otmp,
doname, ansimpleoname, "a box");
switch (ynq(qbuf)) {
switch (ynq(qbuf)) {
case 'q':
return 0;
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 */
}
/* 'n' => continue to next box */
}
}
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 */
obj->ox = cc.x, obj->oy = cc.y;
otmp->tknown = 0;
otmp->otrapped = 0; /* trap is one-shot; clear flag first in case
chest kills you and ends up in bones file */
You(disarm ? "set it off!" : "trigger a trap!");