Merge branch 'NetHack-3.7' into expanded-glyphs

This commit is contained in:
nhmall
2021-09-19 14:45:02 -04:00
9 changed files with 181 additions and 50 deletions

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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,

View File

@@ -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;