Merge branch 'NetHack-3.7' into expanded-glyphs
This commit is contained in:
@@ -617,6 +617,8 @@ similar "The ogre lord yanks Cleaver from your corpses!" due to caching the
|
||||
update clobber that; fixed by having inventory display release the
|
||||
obuf used for each item so that the same one will be reused for the
|
||||
next item, to avoid churning through the whole pool of obufs
|
||||
gas clouds are a little random in how they spread out from a point
|
||||
Izchak occasionally stocks wands/scrolls/spellbooks of light
|
||||
|
||||
|
||||
Fixes to 3.7.0-x Problems that Were Exposed Via git Repository
|
||||
@@ -1159,6 +1161,7 @@ vomiting on an altar provokes the deities wrath
|
||||
branch stairs have a different glyph, show up in yellow color in tty
|
||||
duration of confusion when drinking booze depends upon hunger state
|
||||
using 'D' allows dropping items picked up previously
|
||||
bones piles can be ransacked by adjacent monsters
|
||||
|
||||
|
||||
Platform- and/or Interface-Specific New Features
|
||||
|
||||
@@ -2142,6 +2142,7 @@ extern void assign_candy_wrapper(struct obj *);
|
||||
extern int doread(void);
|
||||
extern int charge_ok(struct obj *);
|
||||
extern void recharge(struct obj *, int);
|
||||
extern boolean valid_cloud_pos(int, int);
|
||||
extern int seffects(struct obj *);
|
||||
extern void drop_boulder_on_player(boolean, boolean, boolean, boolean);
|
||||
extern boolean drop_boulder_on_monster(int, int, boolean, boolean);
|
||||
|
||||
@@ -35,7 +35,7 @@ struct shclass {
|
||||
struct itp {
|
||||
int iprob; /* probability of an item type */
|
||||
int itype; /* item type: if >=0 a class, if < 0 a specific item */
|
||||
} iprobs[6];
|
||||
} iprobs[9];
|
||||
const char *const *shknms; /* list of shopkeeper names for this type */
|
||||
};
|
||||
|
||||
|
||||
38
src/bones.c
38
src/bones.c
@@ -8,6 +8,7 @@
|
||||
static boolean no_bones_level(d_level *);
|
||||
static void goodfruit(int);
|
||||
static void resetobjs(struct obj *, boolean);
|
||||
static void give_to_nearby_mon(struct obj *, int, int);
|
||||
static boolean fixuporacle(struct monst *);
|
||||
|
||||
static boolean
|
||||
@@ -213,6 +214,41 @@ sanitize_name(char *namebuf)
|
||||
}
|
||||
}
|
||||
|
||||
/* Give object to a random object-liking monster on or adjacent to x,y
|
||||
but skipping hero's location.
|
||||
If no such monster, place object on floor at x,y. */
|
||||
static void
|
||||
give_to_nearby_mon(struct obj *otmp, int x, int y)
|
||||
{
|
||||
struct monst *mtmp;
|
||||
struct monst *selected = (struct monst *) 0;
|
||||
int nmon = 0, xx, yy;
|
||||
|
||||
for (xx = x - 1; xx <= x + 1; ++xx) {
|
||||
for (yy = y - 1; yy <= y + 1; ++yy) {
|
||||
if (!isok(xx, yy))
|
||||
continue;
|
||||
if (xx == u.ux && yy == u.uy)
|
||||
continue;
|
||||
if (!(mtmp = m_at(xx, yy)))
|
||||
continue;
|
||||
/* This doesn't do any checks on otmp to see that it matches the
|
||||
* likes_* property, intentionally. Assume that the monster is
|
||||
* rifling through and taking things that look interesting. */
|
||||
if (!(likes_gold(mtmp->data) || likes_gems(mtmp->data)
|
||||
|| likes_objs(mtmp->data) || likes_magic(mtmp->data)))
|
||||
continue;
|
||||
nmon++;
|
||||
if (!rn2(nmon))
|
||||
selected = mtmp;
|
||||
}
|
||||
}
|
||||
if (selected && can_carry(selected, otmp))
|
||||
add_to_minv(selected, otmp);
|
||||
else
|
||||
place_object(otmp, x, y);
|
||||
}
|
||||
|
||||
/* called by savebones(); also by finish_paybill(shk.c) */
|
||||
void
|
||||
drop_upon_death(struct monst *mtmp, /* monster if hero turned into one (other than ghost) */
|
||||
@@ -249,6 +285,8 @@ drop_upon_death(struct monst *mtmp, /* monster if hero turned into one (other th
|
||||
(void) add_to_minv(mtmp, otmp);
|
||||
else if (cont)
|
||||
(void) add_to_container(cont, otmp);
|
||||
else if (!rn2(8))
|
||||
give_to_nearby_mon(otmp, x, y);
|
||||
else
|
||||
place_object(otmp, x, y);
|
||||
}
|
||||
|
||||
@@ -1315,7 +1315,7 @@ fumaroles(void)
|
||||
xchar y = rn1(ROWNO - 4, 3);
|
||||
|
||||
if (levl[x][y].typ == LAVAPOOL) {
|
||||
NhRegion *r = create_gas_cloud(x, y, 4 + rn2(5), rn1(10, 5));
|
||||
NhRegion *r = create_gas_cloud(x, y, rn1(30, 20), rn1(10, 5));
|
||||
|
||||
clear_heros_fault(r);
|
||||
snd = TRUE;
|
||||
|
||||
81
src/read.c
81
src/read.c
@@ -22,8 +22,7 @@ static void p_glow1(struct obj *);
|
||||
static void p_glow2(struct obj *, const char *);
|
||||
static void forget(int);
|
||||
static int maybe_tame(struct monst *, struct obj *);
|
||||
static boolean get_valid_stinking_cloud_pos(int, int);
|
||||
static boolean is_valid_stinking_cloud_pos(int, int, boolean);
|
||||
static boolean can_center_cloud(int, int);
|
||||
static void display_stinking_cloud_positions(int);
|
||||
static void seffect_enchant_armor(struct obj **);
|
||||
static void seffect_destroy_armor(struct obj **);
|
||||
@@ -52,6 +51,7 @@ static void seffect_mail(struct obj **);
|
||||
#endif /* MAIL_STRUCTURES */
|
||||
static void set_lit(int, int, genericptr);
|
||||
static void do_class_genocide(void);
|
||||
static void do_stinking_cloud(struct obj *, boolean);
|
||||
static boolean create_particular_parse(char *,
|
||||
struct _create_particular_data *);
|
||||
static boolean create_particular_creation(struct _create_particular_data *);
|
||||
@@ -989,23 +989,26 @@ maybe_tame(struct monst* mtmp, struct obj* sobj)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static boolean
|
||||
get_valid_stinking_cloud_pos(int x,int y)
|
||||
/* Can a stinking cloud physically exist at a certain position?
|
||||
* NOT the same thing as can_center_cloud.
|
||||
*/
|
||||
boolean
|
||||
valid_cloud_pos(int x, int y)
|
||||
{
|
||||
return (!(!isok(x,y) || !cansee(x, y)
|
||||
|| !ACCESSIBLE(levl[x][y].typ)
|
||||
|| distu(x, y) >= 32));
|
||||
if (!isok(x,y))
|
||||
return FALSE;
|
||||
return ACCESSIBLE(levl[x][y].typ) || is_pool(x, y) || is_lava(x, y);
|
||||
}
|
||||
|
||||
static boolean
|
||||
is_valid_stinking_cloud_pos(int x, int y, boolean showmsg)
|
||||
/* Callback for getpos_sethilite, also used in determining whether a scroll
|
||||
* should have its regular effects, or not because it was out of range.
|
||||
*/
|
||||
boolean
|
||||
can_center_cloud(int x, int y)
|
||||
{
|
||||
if (!get_valid_stinking_cloud_pos(x,y)) {
|
||||
if (showmsg)
|
||||
You("smell rotten eggs.");
|
||||
if (!valid_cloud_pos(x, y))
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
return (cansee(x, y) && distu(x, y) < 32);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1021,7 +1024,7 @@ display_stinking_cloud_positions(int state)
|
||||
for (dy = -dist; dy <= dist; dy++) {
|
||||
x = u.ux + dx;
|
||||
y = u.uy + dy;
|
||||
if (get_valid_stinking_cloud_pos(x,y))
|
||||
if (can_center_cloud(x,y))
|
||||
tmp_at(x, y);
|
||||
}
|
||||
} else {
|
||||
@@ -1702,9 +1705,9 @@ seffect_fire(struct obj **sobjp)
|
||||
dam *= 5;
|
||||
pline("Where do you want to center the explosion?");
|
||||
getpos_sethilite(display_stinking_cloud_positions,
|
||||
get_valid_stinking_cloud_pos);
|
||||
can_center_cloud);
|
||||
(void) getpos(&cc, TRUE, "the desired position");
|
||||
if (!is_valid_stinking_cloud_pos(cc.x, cc.y, FALSE)) {
|
||||
if (!can_center_cloud(cc.x, cc.y)) {
|
||||
/* try to reach too far, get burned */
|
||||
cc.x = u.ux;
|
||||
cc.y = u.uy;
|
||||
@@ -1786,25 +1789,11 @@ seffect_stinking_cloud(struct obj **sobjp)
|
||||
int otyp = sobj->otyp;
|
||||
boolean already_known = (sobj->oclass == SPBOOK_CLASS /* spell */
|
||||
|| objects[otyp].oc_name_known);
|
||||
coord cc;
|
||||
|
||||
if (!already_known)
|
||||
You("have found a scroll of stinking cloud!");
|
||||
g.known = TRUE;
|
||||
pline("Where do you want to center the %scloud?",
|
||||
already_known ? "stinking " : "");
|
||||
cc.x = u.ux;
|
||||
cc.y = u.uy;
|
||||
getpos_sethilite(display_stinking_cloud_positions,
|
||||
get_valid_stinking_cloud_pos);
|
||||
if (getpos(&cc, TRUE, "the desired position") < 0) {
|
||||
pline1(Never_mind);
|
||||
return;
|
||||
}
|
||||
if (!is_valid_stinking_cloud_pos(cc.x, cc.y, TRUE))
|
||||
return;
|
||||
(void) create_gas_cloud(cc.x, cc.y, 3 + bcsign(sobj),
|
||||
8 + 4 * bcsign(sobj));
|
||||
do_stinking_cloud(sobj, already_known);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2759,6 +2748,34 @@ unpunish(void)
|
||||
setworn((struct obj *) 0, W_BALL); /* sets 'uball' to Null */
|
||||
}
|
||||
|
||||
/* Prompt the player to create a stinking cloud and then create it if they give
|
||||
* a location. */
|
||||
static void
|
||||
do_stinking_cloud(struct obj *sobj, boolean mention_stinking)
|
||||
{
|
||||
coord cc;
|
||||
|
||||
pline("Where do you want to center the %scloud?",
|
||||
mention_stinking ? "stinking " : "");
|
||||
cc.x = u.ux;
|
||||
cc.y = u.uy;
|
||||
getpos_sethilite(display_stinking_cloud_positions, can_center_cloud);
|
||||
if (getpos(&cc, TRUE, "the desired position") < 0) {
|
||||
pline(Never_mind);
|
||||
return;
|
||||
} else if (!can_center_cloud(cc.x, cc.y)) {
|
||||
if (Hallucination)
|
||||
pline("Ugh... someone cut the cheese.");
|
||||
else
|
||||
pline("%s a whiff of rotten eggs.",
|
||||
sobj->oclass == SCROLL_CLASS ? "The scroll crumbles with"
|
||||
: "You smell");
|
||||
return;
|
||||
}
|
||||
(void) create_gas_cloud(cc.x, cc.y, 15 + 10 * bcsign(sobj),
|
||||
8 + 4 * bcsign(sobj));
|
||||
}
|
||||
|
||||
/* some creatures have special data structures that only make sense in their
|
||||
* normal locations -- if the player tries to create one elsewhere, or to
|
||||
* revive one, the disoriented creature becomes a zombie
|
||||
|
||||
93
src/region.c
93
src/region.c
@@ -1034,27 +1034,96 @@ inside_gas_cloud(genericptr_t p1, genericptr_t p2)
|
||||
return FALSE; /* Monster is still alive */
|
||||
}
|
||||
|
||||
/* Create a gas cloud which starts at (x,y) and grows outward from it via
|
||||
* breadth-first search.
|
||||
* cloudsize is the number of squares the cloud will attempt to fill.
|
||||
* damage is how much it deals to afflicted creatures. */
|
||||
#define MAX_CLOUD_SIZE 150
|
||||
NhRegion *
|
||||
create_gas_cloud(xchar x, xchar y, int radius, int damage)
|
||||
create_gas_cloud(xchar x, xchar y, int cloudsize, int damage)
|
||||
{
|
||||
NhRegion *cloud;
|
||||
int i, nrect;
|
||||
int i, j;
|
||||
NhRect tmprect;
|
||||
|
||||
/* store visited coords */
|
||||
xchar xcoords[MAX_CLOUD_SIZE];
|
||||
xchar ycoords[MAX_CLOUD_SIZE];
|
||||
xcoords[0] = x;
|
||||
ycoords[0] = y;
|
||||
int curridx;
|
||||
int newidx = 1; /* initial spot is already taken */
|
||||
|
||||
if (cloudsize > MAX_CLOUD_SIZE) {
|
||||
impossible("create_gas_cloud: cloud too large (%d)!", cloudsize);
|
||||
cloudsize = MAX_CLOUD_SIZE;
|
||||
}
|
||||
|
||||
for (curridx = 0; curridx < newidx; curridx++) {
|
||||
if (newidx >= cloudsize)
|
||||
break;
|
||||
int xx = xcoords[curridx];
|
||||
int yy = ycoords[curridx];
|
||||
/* Do NOT check for if there is already a gas cloud created at some
|
||||
* other time at this position. They can overlap. */
|
||||
|
||||
/* Primitive Fisher-Yates-Knuth shuffle to randomize the order of
|
||||
* directions chosen. */
|
||||
coord dirs[4] = { {0, -1}, {0, 1}, {-1, 0}, {1, 0} };
|
||||
for (i = 4; i > 0; --i) {
|
||||
xchar swapidx = rn2(i);
|
||||
coord tmp = dirs[swapidx];
|
||||
dirs[swapidx] = dirs[i-1];
|
||||
dirs[i-1] = tmp;
|
||||
}
|
||||
int nvalid = 0; /* # of valid adjacent spots */
|
||||
for (i = 0; i < 4; ++i) {
|
||||
/* try all 4 directions */
|
||||
|
||||
int dx = dirs[i].x, dy = dirs[i].y;
|
||||
boolean isunpicked = TRUE;
|
||||
|
||||
if (valid_cloud_pos(xx + dx, yy + dy)) {
|
||||
nvalid++;
|
||||
/* don't pick a location we've already picked */
|
||||
for (j = 0; j < newidx; ++j) {
|
||||
if (xcoords[j] == xx + dx && ycoords[j] == yy + dy) {
|
||||
isunpicked = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* randomly disrupt the natural breadth-first search, so that
|
||||
* clouds released in open spaces don't always tend towards a
|
||||
* rhombus shape */
|
||||
if (nvalid == 4 && !rn2(2))
|
||||
continue;
|
||||
|
||||
if (isunpicked) {
|
||||
xcoords[newidx] = xx + dx;
|
||||
ycoords[newidx] = yy + dy;
|
||||
newidx++;
|
||||
}
|
||||
}
|
||||
if (newidx >= cloudsize) {
|
||||
/* don't try further directions */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* we have now either filled up xcoord and ycoord entirely or run out of
|
||||
* space. In either case, newidx is the correct total number of coordinates
|
||||
* inserted. */
|
||||
cloud = create_region((NhRect *) 0, 0);
|
||||
nrect = radius;
|
||||
tmprect.lx = x;
|
||||
tmprect.hx = x;
|
||||
tmprect.ly = y - (radius - 1);
|
||||
tmprect.hy = y + (radius - 1);
|
||||
for (i = 0; i < nrect; i++) {
|
||||
for (i = 0; i < newidx; ++i) {
|
||||
tmprect.lx = tmprect.hx = xcoords[i];
|
||||
tmprect.ly = tmprect.hy = ycoords[i];
|
||||
add_rect_to_reg(cloud, &tmprect);
|
||||
tmprect.lx--;
|
||||
tmprect.hx++;
|
||||
tmprect.ly++;
|
||||
tmprect.hy--;
|
||||
}
|
||||
cloud->ttl = rn1(3, 4);
|
||||
/* If the cloud was constrained in a small space, give it more time to
|
||||
* live. */
|
||||
cloud->ttl = (cloud->ttl * cloudsize) / newidx;
|
||||
|
||||
if (!g.in_mklev && !g.context.mon_moving)
|
||||
set_heros_fault(cloud); /* assume player has created it */
|
||||
cloud->inside_f = INSIDE_GAS_CLOUD;
|
||||
|
||||
@@ -330,11 +330,14 @@ const struct shclass shtypes[] = {
|
||||
0,
|
||||
D_SHOP,
|
||||
{ { 30, -WAX_CANDLE },
|
||||
{ 48, -TALLOW_CANDLE },
|
||||
{ 44, -TALLOW_CANDLE },
|
||||
{ 5, -BRASS_LANTERN },
|
||||
{ 9, -OIL_LAMP },
|
||||
{ 3, -MAGIC_LAMP },
|
||||
{ 5, -POT_OIL } },
|
||||
{ 5, -POT_OIL },
|
||||
{ 2, -WAN_LIGHT },
|
||||
{ 1, -SCR_LIGHT },
|
||||
{ 1, -SPE_LIGHT } },
|
||||
shklight },
|
||||
/* sentinel */
|
||||
{ (char *) 0,
|
||||
|
||||
@@ -4599,7 +4599,7 @@ zap_over_floor(xchar x, xchar y, int type, boolean *shopdamage,
|
||||
/* don't create steam clouds on Plane of Water; air bubble
|
||||
movement and gas regions don't understand each other */
|
||||
if (!on_water_level)
|
||||
create_gas_cloud(x, y, rnd(3), 0); /* radius 1..3, no damg */
|
||||
create_gas_cloud(x, y, rnd(5), 0); /* 1..5, no damg */
|
||||
|
||||
if (lev->typ != POOL) { /* MOAT or DRAWBRIDGE_UP or WATER */
|
||||
if (on_water_level)
|
||||
@@ -4629,7 +4629,7 @@ zap_over_floor(xchar x, xchar y, int type, boolean *shopdamage,
|
||||
newsym(x, y);
|
||||
}
|
||||
} else if (IS_FOUNTAIN(lev->typ)) {
|
||||
create_gas_cloud(x, y, rnd(2), 0); /* radius 1..2, no damage */
|
||||
create_gas_cloud(x, y, rnd(3), 0); /* 1..3, no damage */
|
||||
if (see_it)
|
||||
pline("Steam billows from the fountain.");
|
||||
rangemod -= 1;
|
||||
|
||||
Reference in New Issue
Block a user