/* * Is (x,y) a good position of mtmp? If mtmp is NULL, then is (x,y) good * for an object? * * This function will only look at mtmp->mdat, so makemon, mplayer, etc can * call it to generate new monster positions with fake monster structures. */ boolean goodpos(struct level *lev, int x, int y, struct monst *mtmp, unsigned gpflags) { const struct permonst *mdat = NULL; boolean ignorewater = ((gpflags & MM_IGNOREWATER) != 0); if (!isok(x, y)) return FALSE; /* in many cases, we're trying to create a new monster, which * can't go on top of the player or any existing monster. * however, occasionally we are relocating engravings or objects, * which could be co-located and thus get restricted a bit too much. * oh well. */ if (mtmp != &youmonst && x == u.ux && y == u.uy && (!u.usteed || mtmp != u.usteed)) return FALSE; if (mtmp) { struct monst *mtmp2 = m_at(lev, x,y); /* Be careful with long worms. A monster may be placed back in * its own location. Normally, if m_at() returns the same monster * that we're trying to place, the monster is being placed in its * own location. However, that is not correct for worm segments, * because all the segments of the worm return the same m_at(). * Actually we overdo the check a little bit--a worm can't be placed * in its own location, period. If we just checked for mtmp->mx * != x || mtmp->my != y, we'd miss the case where we're called * to place the worm segment and the worm's head is at x,y. */ if (mtmp2 && (mtmp2 != mtmp || mtmp->wormno)) return FALSE; mdat = mtmp->data; if (is_pool(lev, x,y) && !ignorewater) { if (mtmp == &youmonst) return !!(HLevitation || Flying || Wwalking || Swimming || Amphibious); else return (is_flyer(mdat) || is_swimmer(mdat) || is_clinger(mdat)); } else if (mdat->mlet == S_EEL && rn2(13) && !ignorewater) { return FALSE; } else if (is_lava(lev, x,y)) { if (mtmp == &youmonst) return !!HLevitation; else return is_flyer(mdat) || likes_lava(mdat); } if (passes_walls(mdat) && may_passwall(lev, x,y)) return TRUE; } if (!ACCESSIBLE(lev->locations[x][y].typ)) { if (!(is_pool(lev, x,y) && ignorewater)) return FALSE; } if (closed_door(lev, x, y) && (!mdat || !amorphous(mdat))) return FALSE; if (sobj_at(BOULDER, lev, x, y) && (!mdat || !throws_rocks(mdat))) return FALSE; return TRUE; }
void nh_describe_pos(int x, int y, struct nh_desc_buf *bufs, int *is_in) { bufs->bgdesc[0] = '\0'; bufs->trapdesc[0] = '\0'; bufs->objdesc[0] = '\0'; bufs->mondesc[0] = '\0'; bufs->invisdesc[0] = '\0'; bufs->effectdesc[0] = '\0'; bufs->feature_described = FALSE; bufs->objcount = -1; if (is_in) *is_in = 0; if (!program_state.game_running || !isok(x, y)) return; API_ENTRY_CHECKPOINT_RETURN_VOID_ON_ERROR(); if (is_in) { if (IS_ROCK(level->locations[x][y].typ) || closed_door(level, x, y)) *is_in = 1; else *is_in = 0; } int monid = dbuf_get_mon(x, y); int mem_bg = level->locations[x][y].mem_bg; describe_bg(x, y, mem_bg, bufs->bgdesc); int tt = level->locations[x][y].mem_trap; if (tt) { strcpy(bufs->trapdesc, trapexplain[level->locations[x][y].mem_trap - 1]); if (tt != BEAR_TRAP && tt != WEB && tt != STATUE_TRAP && mem_bg && is_in) *is_in = 1; } bufs->objcount = describe_object(x, y, level->locations[x][y].mem_obj - 1, bufs->objdesc, mem_bg && is_in, &bufs->feature_described); describe_mon(x, y, monid - 1, bufs->mondesc); if (level->locations[x][y].mem_invis) strcpy(bufs->invisdesc, invisexplain); if (Engulfed && (x != u.ux || y != u.uy)) { /* all locations when swallowed other than the hero are the monster */ snprintf(bufs->effectdesc, SIZE(bufs->effectdesc), "interior of %s", Blind ? "a monster" : a_monnam(u.ustuck)); } API_EXIT(); }
static int describe_object(int x, int y, int votyp, char *buf) { int num_objs = 0; struct obj *otmp; if (votyp == -1) return -1; otmp = vobj_at(x,y); if (!otmp || otmp->otyp != votyp) { if (votyp != STRANGE_OBJECT) { otmp = mksobj(level, votyp, FALSE, FALSE); if (otmp->oclass == COIN_CLASS) otmp->quan = 1L; /* to force pluralization off */ else if (otmp->otyp == SLIME_MOLD) otmp->spe = current_fruit; /* give the fruit a type */ strcpy(buf, distant_name(otmp, xname)); dealloc_obj(otmp); otmp = vobj_at(x,y); /* make sure we don't point to the temp obj any more */ } } else strcpy(buf, distant_name(otmp, xname)); if (level->locations[x][y].typ == STONE || level->locations[x][y].typ == SCORR) strcat(buf, " embedded in stone"); else if (IS_WALL(level->locations[x][y].typ) || level->locations[x][y].typ == SDOOR) strcat(buf, " embedded in a wall"); else if (closed_door(level, x,y)) strcat(buf, " embedded in a door"); else if (is_pool(level, x,y)) strcat(buf, " in water"); else if (is_lava(level, x,y)) strcat(buf, " in molten lava"); /* [can this ever happen?] */ if (!cansee(x, y)) return -1; /* don't disclose the number of objects for location out of LOS */ if (!otmp) /* There is no object here. Since the player sees one it must be a mimic */ return 1; if (otmp->otyp != votyp) /* Hero sees something other than the actual top object. Probably a mimic */ num_objs++; for ( ; otmp; otmp = otmp->nexthere) num_objs++; return num_objs; }
/* * Try to find a dismount point adjacent to the steed's location. * If all else fails, try enexto(). Use enexto() as a last resort because * enexto() chooses its point randomly, possibly even outside the * room's walls, which is not what we want. * Adapted from mail daemon code. */ static boolean landing_spot(coord * spot, /* landing position (we fill it in) */ int reason, int forceit) { int i = 0, x, y, distance, min_distance = -1; boolean found = FALSE; struct trap *t; /* avoid known traps (i == 0) and boulders, but allow them as a backup */ if (reason != DISMOUNT_BYCHOICE || Stunned || Confusion || Fumbling) i = 1; for (; !found && i < 2; ++i) { for (x = u.ux - 1; x <= u.ux + 1; x++) for (y = u.uy - 1; y <= u.uy + 1; y++) { if (!isok(x, y) || (x == u.ux && y == u.uy)) continue; if (ACCESSIBLE(level->locations[x][y].typ) && !MON_AT(level, x, y) && !closed_door(level, x, y)) { distance = distu(x, y); if (min_distance < 0 || distance < min_distance || (distance == min_distance && rn2(2))) { if (i > 0 || (((t = t_at(level, x, y)) == 0 || !t->tseen) && (!sobj_at(BOULDER, level, x, y) || throws_rocks(youmonst.data)))) { spot->x = x; spot->y = y; min_distance = distance; found = TRUE; } } } } } /* If we didn't find a good spot and forceit is on, try enexto(). */ if (forceit && min_distance < 0 && !enexto(spot, level, u.ux, u.uy, youmonst.data)) return FALSE; return found; }
static int describe_object(int x, int y, int votyp, char *buf, int known_embed, boolean *feature_described) { int num_objs = 0; struct obj *otmp; int typ; *feature_described = FALSE; if (votyp == -1) return -1; otmp = vobj_at(x, y); if (!otmp || otmp->otyp != votyp) { /* We have a mimic. */ if (votyp == STRANGE_OBJECT) { strcpy(buf, "strange object"); } else { otmp = mktemp_sobj(level, votyp); otmp->corpsenm = PM_TENGU; /* (basic object only, no random features) */ if (otmp->oclass == COIN_CLASS) otmp->quan = 1L; /* to force pluralization off */ else if (otmp->otyp == SLIME_MOLD) otmp->spe = gamestate.fruits.current;/* give the fruit a type */ strcpy(buf, distant_name(otmp, xname)); dealloc_obj(otmp); otmp = vobj_at(x, y); /* make sure we don't point to the temp obj any more */ } } else strcpy(buf, distant_name(otmp, xname)); typ = level->locations[x][y].typ; if (known_embed && IS_TREE(typ)) strcat(buf, " stuck"); else if (known_embed && (IS_ROCK(typ) || closed_door(level, x, y))) strcat(buf, " embedded"); else if (IS_TREE(typ)) { strcat(buf, " stuck in a tree"); *feature_described = TRUE; } else if (typ == STONE || typ == SCORR) { strcat(buf, " embedded in stone"); *feature_described = TRUE; } else if (IS_WALL(typ) || typ == SDOOR) { strcat(buf, " embedded in a wall"); *feature_described = TRUE; } else if (closed_door(level, x, y)) { strcat(buf, " embedded in a door"); *feature_described = TRUE; } else if (is_pool(level, x, y)) { strcat(buf, " in water"); *feature_described = TRUE; } else if (is_lava(level, x, y)) { strcat(buf, " in molten lava"); /* [can this ever happen?] */ *feature_described = TRUE; } if (!cansee(x, y)) return -1; /* don't disclose the number of objects for location out of LOS */ if (!otmp) /* There is no object here. Since the player sees one it must be a mimic */ return 1; if (otmp->otyp != votyp) /* Hero sees something other than the actual top object. Probably a mimic */ num_objs++; for (; otmp; otmp = otmp->nexthere) num_objs++; return num_objs; }
/* Is a monster willing to fire with a compass beam in a given direction? Returns TRUE and sets *mdef to a monster if there's a monster it wants to target (and no monster it doesn't want to attack). *mdef can be &youmonst. If mdef is non-NULL, also set the tbx/tby globals for backwards compatibility (clobbered on a *mdef == NULL return, appropriately otherwise). TODO: get rid of these, they're really spaghetti. Returns TRUE and sets *mdef to NULL if there's no reason it wants to fire in that direction, but no reason it dislikes firing in that direction either. Returns FALSE if the monster refuses to fire in that direction. In this case, *mdef will be a monster that we don't want to target (possibly &youmonst). helpful determines whether or not a beam gives a positive effect */ boolean m_beam_ok(struct monst *magr, int dx, int dy, struct monst **mdef, boolean helpful) { int x = magr->mx; int y = magr->my; int i; struct monst *mat; if (mdef) *mdef = NULL; for (i = 0; i < BOLT_LIM; i++) { x += dx; y += dy; /* Will the beam stop at this point? As usual, assume that monsters have perfect knowledge of the level layout. */ if (!isok(x, y) || !ZAP_POS(level->locations[x][y].typ) || closed_door(level, x, y)) break; /* Will the monster think that the beam will hit the player? Does it care? Hostiles like to attack the player; peacefuls don't want to. Pets have perfect knowledge of their master's location even if they can't sense their master. Confused monsters can't aim a beam at the player (or to avoid the player); no monster can hit an engulfed player with a beam. */ if ((x == magr->mux && y == magr->muy && msensem(magr, &youmonst)) || (magr->mtame && x == u.ux && y == u.uy)) { if (!Engulfed && !magr->mconf) { if (mdef) { *mdef = &youmonst; tbx = x - magr->mx; tby = y - magr->my; } if (!Conflict) { if ((!helpful && magr->mpeaceful) || (helpful && !magr->mpeaceful)) return FALSE; } } } mat = m_at(level, x, y); /* special case: make sure we don't hit the quest leader with stray beams, as it can make the game unwinnable; do this regardless of LOS or hostility or Conflict or confusion or anything like that */ if (mat && mat->data->msound == MS_LEADER && !helpful) { if (mdef) *mdef = mat; return FALSE; } /* Confused monsters aren't trying to target anything in particular, because they don't have full control of their actions. Monsters won't intentionally aim at or to avoid a monster they can't see (apart from the above MS_LEADER case). */ if (mat && (msensem(magr, mat) & ~MSENSE_ITEMMIMIC) && !magr->mconf) { /* Note: the couldsee() here is an LOE check and has nothing to do with vision; it determines conflict radius */ if (Conflict && !resist(magr, RING_CLASS, 0, 0) && couldsee(magr->mx, magr->my) && distu(magr->mx, magr->my) <= BOLT_LIM * BOLT_LIM) { /* we're conflicted, anything is a valid target */ if (mdef) { *mdef = mat; tbx = x - magr->mx; tby = y - magr->my; } } else if (mm_aggression(magr, mat) & ALLOW_M && !helpful) { /* we want to target this monster */ if (mdef) { *mdef = mat; tbx = x - magr->mx; tby = y - magr->my; } } else if (!helpful && (magr->mpeaceful || mat->mpeaceful)) { /* we don't want to attack this monster; peacefuls (including pets) should avoid collateral damage; also handles the pet_attacks_up_to_difficulty checks; symmetrised so that hostiles won't attack pets who won't attack them */ if (mdef) *mdef = mat; return FALSE; } else if (helpful && ((magr->mpeaceful && mat->mpeaceful) || (!magr->mpeaceful && !mat->mpeaceful))) { /* helpful beam, hostile-hostile or peaceful/tame-peaceful/tame is OK; the peaceful checks aren't redundant with above since pet_attacks_up_to_difficulty is a thing */ if (mdef) { *mdef = mat; tbx = x - magr->mx; tby = y - magr->my; } } } } return TRUE; }
void m_throw(struct monst *mon, int x, int y, int dx, int dy, int range, struct obj *obj, boolean verbose) { struct monst *mtmp; struct obj *singleobj; struct tmp_sym *tsym = 0; int hitu, blindinc = 0; bhitpos.x = x; bhitpos.y = y; if (obj->quan == 1L) { /* * Remove object from minvent. This cannot be done later on; * what if the player dies before then, leaving the monster * with 0 daggers? (This caused the infamous 2^32-1 orcish * dagger bug). * * VENOM is not in minvent - it should already be OBJ_FREE. * The extract below does nothing. */ /* not possibly_unwield, which checks the object's */ /* location, not its existence */ if (MON_WEP(mon) == obj) { setmnotwielded(mon, obj); MON_NOWEP(mon); } obj_extract_self(obj); singleobj = obj; obj = NULL; } else { singleobj = splitobj(obj, 1L); obj_extract_self(singleobj); } singleobj->owornmask = 0; /* threw one of multiple weapons in hand? */ singleobj->olev = level; /* object is on the same level as monster */ if ((singleobj->cursed || singleobj->greased) && (dx || dy) && !rn2(7)) { if (canseemon(mon) && flags.verbose) { if (is_ammo(singleobj)) pline("%s misfires!", Monnam(mon)); else pline("%s as %s throws it!", Tobjnam(singleobj, "slip"), mon_nam(mon)); } dx = rn2(3) - 1; dy = rn2(3) - 1; /* check validity of new direction */ if (!dx && !dy) { drop_throw(singleobj, 0, bhitpos.x, bhitpos.y); return; } } /* pre-check for doors, walls and boundaries. Also need to pre-check for bars regardless of direction; the random chance for small objects hitting bars is skipped when reaching them at point blank range */ if (!isok(bhitpos.x + dx, bhitpos.y + dy) || IS_ROCK(level->locations[bhitpos.x + dx][bhitpos.y + dy].typ) || closed_door(level, bhitpos.x + dx, bhitpos.y + dy) || (level->locations[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS && hits_bars(&singleobj, bhitpos.x, bhitpos.y, 0, 0))) { drop_throw(singleobj, 0, bhitpos.x, bhitpos.y); return; } /* Note: drop_throw may destroy singleobj. Since obj must be destroyed early to avoid the dagger bug, anyone who modifies this code should be careful not to use either one after it's been freed. */ tsym = tmpsym_initobj(singleobj); while (range-- > 0) { /* Actually the loop is always exited by break */ bhitpos.x += dx; bhitpos.y += dy; if ((mtmp = m_at(level, bhitpos.x, bhitpos.y)) != 0) { if (ohitmon(mtmp, singleobj, range, verbose)) break; } else if (bhitpos.x == u.ux && bhitpos.y == u.uy) { action_interrupted(); if (singleobj->oclass == GEM_CLASS && singleobj->otyp <= LAST_GEM + 9 /* 9 glass colors */ && is_unicorn(youmonst.data) && !u_helpless(hm_all)) { if (singleobj->otyp > LAST_GEM) { pline("You catch the %s.", xname(singleobj)); pline("You are not interested in %s junk.", s_suffix(mon_nam(mon))); makeknown(singleobj->otyp); dropy(singleobj); } else { pline("You accept %s gift in the spirit in which it was " "intended.", s_suffix(mon_nam(mon))); hold_another_object(singleobj, "You catch, but drop, %s.", xname(singleobj), "You catch:"); } break; } if (singleobj->oclass == POTION_CLASS) { if (!Blind) singleobj->dknown = 1; potionhit(&youmonst, singleobj, FALSE); break; } switch (singleobj->otyp) { int dam, hitv; case EGG: if (!touch_petrifies(&mons[singleobj->corpsenm])) { impossible("monster throwing egg type %d", singleobj->corpsenm); hitu = 0; break; } /* fall through */ case CREAM_PIE: case BLINDING_VENOM: hitu = thitu(8, 0, singleobj, NULL); break; default: dam = dmgval(singleobj, &youmonst); hitv = 3 - distmin(u.ux, u.uy, mon->mx, mon->my); if (hitv < -4) hitv = -4; if (is_elf(mon->data) && objects[singleobj->otyp].oc_skill == P_BOW) { hitv++; if (MON_WEP(mon) && MON_WEP(mon)->otyp == ELVEN_BOW) hitv++; if (singleobj->otyp == ELVEN_ARROW) dam++; } if (bigmonst(youmonst.data)) hitv++; hitv += 8 + singleobj->spe; if (dam < 1) dam = 1; if (objects[singleobj->otyp].oc_class == WEAPON_CLASS || objects[singleobj->otyp].oc_class == VENOM_CLASS) { hitv += objects[singleobj->otyp].oc_hitbon; } hitu = thitu(hitv, dam, singleobj, NULL); } if (hitu && singleobj->opoisoned && is_poisonable(singleobj)) { poisoned(xname(singleobj), A_STR, killer_msg_obj(POISONING, singleobj), -10); } if (hitu && can_blnd(NULL, &youmonst, (uchar) (singleobj->otyp == BLINDING_VENOM ? AT_SPIT : AT_WEAP), singleobj)) { blindinc = rnd(25); if (singleobj->otyp == CREAM_PIE) { if (!Blind) pline("Yecch! You've been creamed."); else pline("There's something sticky all over your %s.", body_part(FACE)); } else if (singleobj->otyp == BLINDING_VENOM) { int num_eyes = eyecount(youmonst.data); /* venom in the eyes */ if (!Blind) pline("The venom blinds you."); else pline("Your %s sting%s.", (num_eyes == 1) ? body_part(EYE) : makeplural(body_part(EYE)), (num_eyes == 1) ? "s" : ""); } } if (hitu && singleobj->otyp == VAMPIRE_BLOOD) { if (!Drain_resistance) { losexp("vampire blood", FALSE); } } if (hitu && singleobj->otyp == EGG) { if (touched_monster(singleobj->corpsenm)) Stoned = 5; } action_interrupted(); if (hitu || !range) { drop_throw(singleobj, hitu, u.ux, u.uy); break; } } else if (!range /* reached end of path */ /* missile hits edge of screen */ || !isok(bhitpos.x + dx, bhitpos.y + dy) /* missile hits the wall */ || IS_ROCK(level-> locations[bhitpos.x + dx][bhitpos.y + dy].typ) /* missile hit closed door */ || closed_door(level, bhitpos.x + dx, bhitpos.y + dy) /* missile might hit iron bars */ || (level->locations[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS && hits_bars(&singleobj, bhitpos.x, bhitpos.y, !rn2(5), 0)) /* Thrown objects "sink" */ || IS_SINK(level->locations[bhitpos.x][bhitpos.y].typ)) { if (singleobj) /* hits_bars might have destroyed it */ drop_throw(singleobj, 0, bhitpos.x, bhitpos.y); break; } tmpsym_at(tsym, bhitpos.x, bhitpos.y); win_delay_output(); } tmpsym_at(tsym, bhitpos.x, bhitpos.y); win_delay_output(); tmpsym_end(tsym); if (blindinc) { u.ucreamed += blindinc; make_blinded(Blinded + (long)blindinc, FALSE); if (!Blind) pline("Your vision quickly clears."); else if (flags.verbose) pline("Use the command #wipe to clean your %s.", body_part(FACE)); } }
/* returns number of scattered objects */ long scatter(int sx, int sy, /* location of objects to scatter */ int blastforce, /* force behind the scattering */ unsigned int scflags, struct obj *obj) { /* only scatter this obj */ struct obj *otmp; struct level *lev = obj ? obj->olev : level; int tmp; int farthest = 0; uchar typ; long qtmp; boolean used_up; boolean individual_object = obj ? TRUE : FALSE; struct monst *mtmp; struct scatter_chain *stmp, *stmp2 = 0; struct scatter_chain *schain = NULL; long total = 0L; boolean visible = (lev == level && cansee(sx, sy)); while ((otmp = individual_object ? obj : lev->objects[sx][sy]) != 0) { if (otmp->quan > 1L) { qtmp = otmp->quan - 1; if (qtmp > LARGEST_INT) qtmp = LARGEST_INT; qtmp = (long)rnd((int)qtmp); otmp = splitobj(otmp, qtmp); } else { obj = NULL; /* all used */ } obj_extract_self(otmp); used_up = FALSE; /* 9 in 10 chance of fracturing boulders or statues */ if ((scflags & MAY_FRACTURE) && ((otmp->otyp == BOULDER) || (otmp->otyp == STATUE)) && rn2(10)) { if (otmp->otyp == BOULDER) { if (visible) pline("%s apart.", Tobjnam(otmp, "break")); fracture_rock(otmp); place_object(otmp, lev, sx, sy); if ((otmp = sobj_at(BOULDER, lev, sx, sy)) != 0) { /* another boulder here, restack it to the top */ obj_extract_self(otmp); place_object(otmp, lev, sx, sy); } } else { struct trap *trap; if ((trap = t_at(lev, sx, sy)) && trap->ttyp == STATUE_TRAP) deltrap(lev, trap); if (visible) pline("%s.", Tobjnam(otmp, "crumble")); break_statue(otmp); place_object(otmp, lev, sx, sy); /* put fragments on floor */ } used_up = TRUE; /* 1 in 10 chance of destruction of obj; glass, egg destruction */ } else if ((scflags & MAY_DESTROY) && (!rn2(10) || (objects[otmp->otyp].oc_material == GLASS || otmp->otyp == EGG))) { if (breaks(otmp, (xchar) sx, (xchar) sy)) used_up = TRUE; } if (!used_up) { stmp = malloc(sizeof (struct scatter_chain)); stmp->next = NULL; stmp->obj = otmp; stmp->ox = sx; stmp->oy = sy; tmp = rn2(8); /* get the direction */ stmp->dx = xdir[tmp]; stmp->dy = ydir[tmp]; tmp = blastforce - (otmp->owt / 40); if (tmp < 1) tmp = 1; stmp->range = rnd(tmp); /* anywhere up to that determ. by wt */ if (farthest < stmp->range) farthest = stmp->range; stmp->stopped = FALSE; if (!schain) schain = stmp; else stmp2->next = stmp; stmp2 = stmp; } } while (farthest-- > 0) { for (stmp = schain; stmp; stmp = stmp->next) { if ((stmp->range-- > 0) && (!stmp->stopped)) { bhitpos.x = stmp->ox + stmp->dx; bhitpos.y = stmp->oy + stmp->dy; typ = lev->locations[bhitpos.x][bhitpos.y].typ; if (!isok(bhitpos.x, bhitpos.y)) { bhitpos.x -= stmp->dx; bhitpos.y -= stmp->dy; stmp->stopped = TRUE; } else if (!ZAP_POS(typ) || closed_door(lev, bhitpos.x, bhitpos.y)) { bhitpos.x -= stmp->dx; bhitpos.y -= stmp->dy; stmp->stopped = TRUE; } else if ((mtmp = m_at(lev, bhitpos.x, bhitpos.y)) != 0) { if (scflags & MAY_HITMON) { stmp->range--; if (ohitmon(mtmp, stmp->obj, 1, FALSE)) { stmp->obj = NULL; stmp->stopped = TRUE; } } } else if (bhitpos.x == u.ux && bhitpos.y == u.uy) { if (scflags & MAY_HITYOU) { int hitvalu, hitu; action_interrupted(); hitvalu = 8 + stmp->obj->spe; if (bigmonst(youmonst.data)) hitvalu++; hitu = thitu(hitvalu, dmgval(stmp->obj, &youmonst), stmp->obj, NULL); if (hitu) stmp->range -= 3; } } else { if (scflags & VIS_EFFECTS) { /* tmpsym_at(bhitpos.x, bhitpos.y); */ /* delay_output(); */ } } stmp->ox = bhitpos.x; stmp->oy = bhitpos.y; } } } for (stmp = schain; stmp; stmp = stmp2) { int x, y; stmp2 = stmp->next; x = stmp->ox; y = stmp->oy; if (stmp->obj) { if (x != sx || y != sy) total += stmp->obj->quan; place_object(stmp->obj, lev, x, y); stackobj(stmp->obj); } free(stmp); if (lev == level) newsym(x, y); } return total; }