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
Fixes to 3.7.0-x Problems that Were Exposed Via git Repository
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);
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;
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 **);
#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 *);
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
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 {
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;
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
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
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;
/* 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)
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;