/* Make a real level */ static bool level_gen(cptr *why) { int level_height, level_width; if ((always_small_levels || ironman_small_levels || (one_in_(SMALL_LEVEL) && small_levels) || (d_info[dungeon_type].flags1 & DF1_BEGINNER) || (d_info[dungeon_type].flags1 & DF1_SMALLEST)) && !(d_info[dungeon_type].flags1 & DF1_BIG)) { if (cheat_room) #ifdef JP msg_print("小さなフロア"); #else msg_print("A 'small' dungeon level."); #endif if (d_info[dungeon_type].flags1 & DF1_SMALLEST) { level_height = 1; level_width = 1; } else if (d_info[dungeon_type].flags1 & DF1_BEGINNER) { level_height = 2; level_width = 2; } else { do { level_height = randint1(MAX_HGT/SCREEN_HGT); level_width = randint1(MAX_WID/SCREEN_WID); } while (level_height + level_width > MAX_HGT/SCREEN_HGT + MAX_WID/SCREEN_WID - 2); /* while ((level_height == MAX_HGT/SCREEN_HGT) && (level_width == MAX_WID/SCREEN_WID)); */ } cur_hgt = level_height * SCREEN_HGT; cur_wid = level_width * SCREEN_WID; /* Assume illegal panel */ panel_row_min = cur_hgt; panel_col_min = cur_wid; if (cheat_room) msg_format("X:%d, Y:%d.", cur_wid, cur_hgt); } else { /* Big dungeon */ cur_hgt = MAX_HGT; cur_wid = MAX_WID; /* Assume illegal panel */ panel_row_min = cur_hgt; panel_col_min = cur_wid; } /* Make a dungeon */ if (!cave_gen()) { #ifdef JP *why = "ダンジョン生成に失敗"; #else *why = "could not place player"; #endif return FALSE; } else return TRUE; }
/** * Applying magic to an object, which includes creating ego-items, and applying * random bonuses, * * The `good` argument forces the item to be at least `good`, and the `great` * argument does likewise. Setting `allow_artifacts` to true allows artifacts * to be created here. * * If `good` or `great` are not set, then the `lev` argument controls the * quality of item. * * Returns 0 if a normal object, 1 if a good object, 2 if an ego item, 3 if an * artifact. */ int apply_magic(struct object *obj, int lev, bool allow_artifacts, bool good, bool great, bool extra_roll) { int i; s16b power = 0; /* Chance of being `good` and `great` */ /* This has changed over the years: * 3.0.0: good = MIN(75, lev + 10); great = MIN(20, lev / 2); * 3.3.0: good = (lev + 2) * 3; great = MIN(lev / 4 + lev, 50); * 3.4.0: good = (2 * lev) + 5 * 3.4 was in between 3.0 and 3.3, 3.5 attempts to keep the same * area under the curve as 3.4, but make the generation chances * flatter. This depresses good items overall since more items * are created deeper. * This change is meant to go in conjunction with the changes * to ego item allocation levels. (-fizzix) */ int good_chance = (33 + lev); int great_chance = 30; /* Roll for "good" */ if (good || (randint0(100) < good_chance)) { power = 1; /* Roll for "great" */ if (great || (randint0(100) < great_chance)) power = 2; } /* Roll for artifact creation */ if (allow_artifacts) { int rolls = 0; /* Get one roll if excellent */ if (power >= 2) rolls = 1; /* Get two rolls if forced great */ if (great) rolls = 2; /* Give some extra rolls for uniques and acq scrolls */ if (extra_roll) rolls += 2; /* Roll for artifacts if allowed */ for (i = 0; i < rolls; i++) if (make_artifact(obj)) return 3; } /* Try to make an ego item */ if (power == 2) make_ego_item(obj, lev); /* Apply magic */ if (tval_is_weapon(obj)) { apply_magic_weapon(obj, lev, power); } else if (tval_is_armor(obj)) { apply_magic_armour(obj, lev, power); } else if (tval_is_ring(obj)) { if (obj->sval == lookup_sval(obj->tval, "Speed")) { /* Super-charge the ring */ while (one_in_(2)) obj->modifiers[OBJ_MOD_SPEED]++; } } else if (tval_is_chest(obj)) { /* Hack -- skip ruined chests */ if (obj->kind->level > 0) { /* Hack -- pick a "difficulty" */ obj->pval = randint1(obj->kind->level); /* Never exceed "difficulty" of 55 to 59 */ if (obj->pval > 55) obj->pval = (s16b)(55 + randint0(5)); } } /* Apply minima from ego items if necessary */ ego_apply_minima(obj); return power; }
/** * Applying magic to an object, which includes creating ego-items, and applying * random bonuses, * * The `good` argument forces the item to be at least `good`, and the `great` * argument does likewise. Setting `allow_artifacts` to TRUE allows artifacts * to be created here. * * If `good` or `great` are not set, then the `lev` argument controls the * quality of item. * * Returns 0 if a normal object, 1 if a good object, 2 if an ego item, 3 if an * artifact. */ s16b apply_magic(object_type *o_ptr, int lev, bool allow_artifacts, bool good, bool great) { int i; s16b power = 0; /* Chance of being `good` and `great` */ /* This has changed over the years: * 3.0.0: good = MIN(75, lev + 10); great = MIN(20, lev / 2); * 3.3.0: good = (lev + 2) * 3; great = MIN(lev / 4 + lev, 50); * The calculations below are somewhere between the two. -AS- */ int good_chance = (2 * lev) + 5; int great_chance = MIN(40, (lev * 3) / 4); /* Roll for "good" */ if (good || (randint0(100) < good_chance)) { power = 1; /* Roll for "great" */ if (great || (randint0(100) < great_chance)) power = 2; } /* Roll for artifact creation */ if (allow_artifacts) { int rolls = 0; /* Get one roll if excellent */ if (power >= 2) rolls = 1; /* Get four rolls if forced great */ if (great) rolls = 4; /* Roll for artifacts if allowed */ for (i = 0; i < rolls; i++) if (make_artifact(o_ptr)) return 3; } /* Try to make an ego item */ if (power == 2) make_ego_item(o_ptr, lev); /* Apply magic */ switch (o_ptr->tval) { case TV_DIGGING: case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_BOW: case TV_SHOT: case TV_ARROW: case TV_BOLT: apply_magic_weapon(o_ptr, lev, power); break; case TV_DRAG_ARMOR: case TV_HARD_ARMOR: case TV_SOFT_ARMOR: case TV_SHIELD: case TV_HELM: case TV_CROWN: case TV_CLOAK: case TV_GLOVES: case TV_BOOTS: apply_magic_armour(o_ptr, lev, power); break; case TV_RING: if (o_ptr->sval == SV_RING_SPEED) { /* Super-charge the ring */ while (one_in_(2)) o_ptr->pval[which_pval(o_ptr, OF_SPEED)]++; } break; case TV_CHEST: /* Hack -- skip ruined chests */ if (o_ptr->kind->level <= 0) break; /* Hack -- pick a "difficulty" */ o_ptr->pval[DEFAULT_PVAL] = randint1(o_ptr->kind->level); /* Never exceed "difficulty" of 55 to 59 */ if (o_ptr->pval[DEFAULT_PVAL] > 55) o_ptr->pval[DEFAULT_PVAL] = (s16b)(55 + randint0(5)); break; } /* Apply minima from ego items if necessary */ ego_apply_minima(o_ptr); return power; }
/** * Work out if a monster can move through the grid, if necessary bashing * down doors in the way. * * Returns TRUE if the monster is able to move through the grid. */ static bool process_monster_can_move(struct chunk *c, struct monster *m_ptr, const char *m_name, int nx, int ny, bool *did_something) { monster_lore *l_ptr = get_lore(m_ptr->race); /* Floor is open? */ if (square_ispassable(c, ny, nx)) return TRUE; /* Permanent wall in the way */ if (square_iswall(c, ny, nx) && square_isperm(c, ny, nx)) return FALSE; /* Normal wall, door, or secret door in the way */ /* There's some kind of feature in the way, so learn about * kill-wall and pass-wall now */ if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE)) { rf_on(l_ptr->flags, RF_PASS_WALL); rf_on(l_ptr->flags, RF_KILL_WALL); } /* Monster moves through walls (and doors) */ if (rf_has(m_ptr->race->flags, RF_PASS_WALL)) return TRUE; /* Monster destroys walls (and doors) */ else if (rf_has(m_ptr->race->flags, RF_KILL_WALL)) { /* Forget the wall */ sqinfo_off(c->squares[ny][nx].info, SQUARE_MARK); /* Notice */ square_destroy_wall(c, ny, nx); /* Note changes to viewable region */ if (player_has_los_bold(ny, nx)) player->upkeep->update |= PU_UPDATE_VIEW; /* Update the flow, since walls affect flow */ player->upkeep->update |= PU_UPDATE_FLOW; return TRUE; } /* Handle doors and secret doors */ else if (square_iscloseddoor(c, ny, nx) || square_issecretdoor(c, ny, nx)) { bool may_bash = rf_has(m_ptr->race->flags, RF_BASH_DOOR) && one_in_(2); /* Take a turn */ *did_something = TRUE; /* Learn about door abilities */ if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE)) { rf_on(l_ptr->flags, RF_OPEN_DOOR); rf_on(l_ptr->flags, RF_BASH_DOOR); } /* Creature can open or bash doors */ if (!rf_has(m_ptr->race->flags, RF_OPEN_DOOR) && !rf_has(m_ptr->race->flags, RF_BASH_DOOR)) return FALSE; /* Stuck door -- try to unlock it */ if (square_islockeddoor(c, ny, nx)) { int k = square_door_power(c, ny, nx); if (randint0(m_ptr->hp / 10) > k) { if (may_bash) msg("%s slams against the door.", m_name); else msg("%s fiddles with the lock.", m_name); /* Reduce the power of the door by one */ square_set_door_lock(c, ny, nx, k - 1); } } else { /* Handle viewable doors */ if (player_has_los_bold(ny, nx)) player->upkeep->update |= PU_UPDATE_VIEW; /* Closed or secret door -- open or bash if allowed */ if (may_bash) { square_smash_door(c, ny, nx); msg("You hear a door burst open!"); disturb(player, 0); /* Fall into doorway */ return TRUE; } else if (rf_has(m_ptr->race->flags, RF_OPEN_DOOR)) { square_open_door(c, ny, nx); } } } return FALSE; }
void banish_evil_spell(int cmd, variant *res) { switch (cmd) { case SPELL_NAME: var_set_string(res, "Banish Evil"); break; case SPELL_DESC: var_set_string(res, "Attempts to remove a single evil opponent."); break; case SPELL_GAIN_MUT: msg_print("You feel a holy wrath fill you."); break; case SPELL_LOSE_MUT: msg_print("You no longer feel a holy wrath."); break; case SPELL_MUT_DESC: var_set_string(res, "You can send evil creatures directly to Hell."); break; case SPELL_CAST: { int dir = 0; int x, y; cave_type *c_ptr; monster_type *m_ptr; monster_race *r_ptr; if (!get_rep_dir2(&dir)) { var_set_bool(res, FALSE); break; } var_set_bool(res, TRUE); y = py + ddy[dir]; x = px + ddx[dir]; c_ptr = &cave[y][x]; if (!c_ptr->m_idx) { msg_print("You sense no evil there!"); break; } m_ptr = &m_list[c_ptr->m_idx]; r_ptr = &r_info[m_ptr->r_idx]; if ((r_ptr->flags3 & RF3_EVIL) && !(r_ptr->flags1 & RF1_QUESTOR) && !(r_ptr->flags1 & RF1_UNIQUE) && !p_ptr->inside_arena && !p_ptr->inside_quest && (r_ptr->level < randint1(p_ptr->lev+50)) && !(m_ptr->mflag2 & MFLAG2_NOGENO)) { /* Delete the monster, rather than killing it. */ delete_monster_idx(c_ptr->m_idx); msg_print("The evil creature vanishes in a puff of sulfurous smoke!"); } else { msg_print("Your invocation is ineffectual!"); if (one_in_(13)) m_ptr->mflag2 |= MFLAG2_NOGENO; } break; } default: default_spell(cmd, res); break; } }
/* * Do an effect, given an object. * Boost is the extent to which skill surpasses difficulty, used as % boost. It * ranges from 0 to 138. */ bool effect_do(effect_type effect, bool *ident, bool aware, int dir, int beam, int boost) { int py = p_ptr->py; int px = p_ptr->px; int dam, chance, dur; if (effect < 1 || effect > EF_MAX) { msg("Bad effect passed to do_effect(). Please report this bug."); return FALSE; } switch (effect) { case EF_POISON: { player_inc_timed(p_ptr, TMD_POISONED, damroll(2, 7) + 10, TRUE, TRUE); *ident = TRUE; return TRUE; } case EF_BLIND: { player_inc_timed(p_ptr, TMD_BLIND, damroll(4, 25) + 75, TRUE, TRUE); *ident = TRUE; return TRUE; } case EF_SCARE: { player_inc_timed(p_ptr, TMD_AFRAID, randint0(10) + 10, TRUE, TRUE); *ident = TRUE; return TRUE; } case EF_CONFUSE: { player_inc_timed(p_ptr, TMD_CONFUSED, damroll(4, 5) + 10, TRUE, TRUE); *ident = TRUE; return TRUE; } case EF_HALLUC: { player_inc_timed(p_ptr, TMD_IMAGE, randint0(250) + 250, TRUE, TRUE); *ident = TRUE; return TRUE; } case EF_PARALYZE: { player_inc_timed(p_ptr, TMD_PARALYZED, randint0(5) + 5, TRUE, TRUE); *ident = TRUE; return TRUE; } case EF_SLOW: { if (player_inc_timed(p_ptr, TMD_SLOW, randint1(25) + 15, TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_POISON: { if (player_clear_timed(p_ptr, TMD_POISONED, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_BLINDNESS: { if (player_clear_timed(p_ptr, TMD_BLIND, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_PARANOIA: { if (player_clear_timed(p_ptr, TMD_AFRAID, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_CONFUSION: { if (player_clear_timed(p_ptr, TMD_CONFUSED, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_MIND: { if (player_clear_timed(p_ptr, TMD_CONFUSED, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_AFRAID, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_IMAGE, TRUE)) *ident = TRUE; if (!of_has(p_ptr->state.flags, OF_RES_CONFU) && player_inc_timed(p_ptr, TMD_OPP_CONF, damroll(4, 10), TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_BODY: { if (player_clear_timed(p_ptr, TMD_STUN, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_CUT, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_POISONED, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_BLIND, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_LIGHT: { if (hp_player(20)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_BLIND, TRUE)) *ident = TRUE; if (player_dec_timed(p_ptr, TMD_CUT, 20, TRUE)) *ident = TRUE; if (player_dec_timed(p_ptr, TMD_CONFUSED, 20, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_SERIOUS: { if (hp_player(40)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_CUT, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_BLIND, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_CONFUSED, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_CRITICAL: { if (hp_player(60)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_BLIND, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_CONFUSED, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_POISONED, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_STUN, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_CUT, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_AMNESIA, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_FULL: { int amt = (p_ptr->mhp * 35) / 100; if (amt < 300) amt = 300; if (hp_player(amt)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_BLIND, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_CONFUSED, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_POISONED, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_STUN, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_CUT, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_AMNESIA, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_FULL2: { if (hp_player(1200)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_BLIND, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_CONFUSED, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_POISONED, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_STUN, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_CUT, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_AMNESIA, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_TEMP: { if (player_clear_timed(p_ptr, TMD_BLIND, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_POISONED, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_CONFUSED, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_STUN, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_CUT, TRUE)) *ident = TRUE; return TRUE; } case EF_HEAL1: { if (hp_player(500)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_CUT, TRUE)) *ident = TRUE; return TRUE; } case EF_HEAL2: { if (hp_player(1000)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_CUT, TRUE)) *ident = TRUE; return TRUE; } case EF_HEAL3: { if (hp_player(500)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_STUN, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_CUT, TRUE)) *ident = TRUE; return TRUE; } case EF_GAIN_EXP: { if (p_ptr->exp < PY_MAX_EXP) { msg("You feel more experienced."); player_exp_gain(p_ptr, 100000L); *ident = TRUE; } return TRUE; } case EF_LOSE_EXP: { if (!check_state(p_ptr, OF_HOLD_LIFE, p_ptr->state.flags) && (p_ptr->exp > 0)) { msg("You feel your memories fade."); player_exp_lose(p_ptr, p_ptr->exp / 4, FALSE); *ident = TRUE; } *ident = TRUE; wieldeds_notice_flag(p_ptr, OF_HOLD_LIFE); return TRUE; } case EF_RESTORE_EXP: { if (restore_level()) *ident = TRUE; return TRUE; } case EF_RESTORE_MANA: { if (p_ptr->csp < p_ptr->msp) { p_ptr->csp = p_ptr->msp; p_ptr->csp_frac = 0; msg("Your feel your head clear."); p_ptr->redraw |= (PR_MANA); *ident = TRUE; } return TRUE; } case EF_GAIN_STR: case EF_GAIN_INT: case EF_GAIN_WIS: case EF_GAIN_DEX: case EF_GAIN_CON: case EF_GAIN_CHR: { int stat = effect - EF_GAIN_STR; if (do_inc_stat(stat)) *ident = TRUE; return TRUE; } case EF_GAIN_ALL: { if (do_inc_stat(A_STR)) *ident = TRUE; if (do_inc_stat(A_INT)) *ident = TRUE; if (do_inc_stat(A_WIS)) *ident = TRUE; if (do_inc_stat(A_DEX)) *ident = TRUE; if (do_inc_stat(A_CON)) *ident = TRUE; if (do_inc_stat(A_CHR)) *ident = TRUE; return TRUE; } case EF_BRAWN: { /* Pick a random stat to decrease other than strength */ int stat = randint0(A_MAX-1) + 1; if (do_dec_stat(stat, TRUE)) { do_inc_stat(A_STR); *ident = TRUE; } return TRUE; } case EF_INTELLECT: { /* Pick a random stat to decrease other than intelligence */ int stat = randint0(A_MAX-1); if (stat >= A_INT) stat++; if (do_dec_stat(stat, TRUE)) { do_inc_stat(A_INT); *ident = TRUE; } return TRUE; } case EF_CONTEMPLATION: { /* Pick a random stat to decrease other than wisdom */ int stat = randint0(A_MAX-1); if (stat >= A_WIS) stat++; if (do_dec_stat(stat, TRUE)) { do_inc_stat(A_WIS); *ident = TRUE; } return TRUE; } case EF_TOUGHNESS: { /* Pick a random stat to decrease other than constitution */ int stat = randint0(A_MAX-1); if (stat >= A_CON) stat++; if (do_dec_stat(stat, TRUE)) { do_inc_stat(A_CON); *ident = TRUE; } return TRUE; } case EF_NIMBLENESS: { /* Pick a random stat to decrease other than dexterity */ int stat = randint0(A_MAX-1); if (stat >= A_DEX) stat++; if (do_dec_stat(stat, TRUE)) { do_inc_stat(A_DEX); *ident = TRUE; } return TRUE; } case EF_PLEASING: { /* Pick a random stat to decrease other than charisma */ int stat = randint0(A_MAX-1); if (do_dec_stat(stat, TRUE)) { do_inc_stat(A_CHR); *ident = TRUE; } return TRUE; } case EF_LOSE_STR: case EF_LOSE_INT: case EF_LOSE_WIS: case EF_LOSE_DEX: case EF_LOSE_CON: case EF_LOSE_CHR: { int stat = effect - EF_LOSE_STR; take_hit(p_ptr, damroll(5, 5), "stat drain"); (void)do_dec_stat(stat, FALSE); *ident = TRUE; return TRUE; } case EF_LOSE_CON2: { take_hit(p_ptr, damroll(10, 10), "poisonous food"); (void)do_dec_stat(A_CON, FALSE); *ident = TRUE; return TRUE; } case EF_RESTORE_STR: case EF_RESTORE_INT: case EF_RESTORE_WIS: case EF_RESTORE_DEX: case EF_RESTORE_CON: case EF_RESTORE_CHR: { int stat = effect - EF_RESTORE_STR; if (do_res_stat(stat)) *ident = TRUE; return TRUE; } case EF_CURE_NONORLYBIG: { msg("You feel life flow through your body!"); restore_level(); (void)player_clear_timed(p_ptr, TMD_POISONED, TRUE); (void)player_clear_timed(p_ptr, TMD_BLIND, TRUE); (void)player_clear_timed(p_ptr, TMD_CONFUSED, TRUE); (void)player_clear_timed(p_ptr, TMD_IMAGE, TRUE); (void)player_clear_timed(p_ptr, TMD_STUN, TRUE); (void)player_clear_timed(p_ptr, TMD_CUT, TRUE); (void)player_clear_timed(p_ptr, TMD_AMNESIA, TRUE); if (do_res_stat(A_STR)) *ident = TRUE; if (do_res_stat(A_INT)) *ident = TRUE; if (do_res_stat(A_WIS)) *ident = TRUE; if (do_res_stat(A_DEX)) *ident = TRUE; if (do_res_stat(A_CON)) *ident = TRUE; if (do_res_stat(A_CHR)) *ident = TRUE; /* Recalculate max. hitpoints */ update_stuff(p_ptr); hp_player(5000); *ident = TRUE; return TRUE; } case EF_RESTORE_ALL: { /* Life, above, also gives these effects */ if (do_res_stat(A_STR)) *ident = TRUE; if (do_res_stat(A_INT)) *ident = TRUE; if (do_res_stat(A_WIS)) *ident = TRUE; if (do_res_stat(A_DEX)) *ident = TRUE; if (do_res_stat(A_CON)) *ident = TRUE; if (do_res_stat(A_CHR)) *ident = TRUE; return TRUE; } case EF_RESTORE_ST_LEV: { if (restore_level()) *ident = TRUE; if (do_res_stat(A_STR)) *ident = TRUE; if (do_res_stat(A_INT)) *ident = TRUE; if (do_res_stat(A_WIS)) *ident = TRUE; if (do_res_stat(A_DEX)) *ident = TRUE; if (do_res_stat(A_CON)) *ident = TRUE; if (do_res_stat(A_CHR)) *ident = TRUE; return TRUE; } case EF_TMD_INFRA: { if (player_inc_timed(p_ptr, TMD_SINFRA, 100 + damroll(4, 25), TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_TMD_SINVIS: { if (player_clear_timed(p_ptr, TMD_BLIND, TRUE)) *ident = TRUE; if (player_inc_timed(p_ptr, TMD_SINVIS, 12 + damroll(2, 6), TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_TMD_ESP: { if (player_clear_timed(p_ptr, TMD_BLIND, TRUE)) *ident = TRUE; if (player_inc_timed(p_ptr, TMD_TELEPATHY, 12 + damroll(6, 6), TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_ENLIGHTENMENT: { msg("An image of your surroundings forms in your mind..."); wiz_light(); *ident = TRUE; return TRUE; } case EF_ENLIGHTENMENT2: { msg("You begin to feel more enlightened..."); message_flush(); wiz_light(); (void)do_inc_stat(A_INT); (void)do_inc_stat(A_WIS); (void)detect_traps(TRUE); (void)detect_doorstairs(TRUE); (void)detect_treasure(TRUE, TRUE); identify_pack(); *ident = TRUE; return TRUE; } case EF_HERO: { dur = randint1(25) + 25; if (hp_player(10)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_AFRAID, TRUE)) *ident = TRUE; if (player_inc_timed(p_ptr, TMD_BOLD, dur, TRUE, TRUE)) *ident = TRUE; if (player_inc_timed(p_ptr, TMD_HERO, dur, TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_SHERO: { dur = randint1(25) + 25; if (hp_player(30)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_AFRAID, TRUE)) *ident = TRUE; if (player_inc_timed(p_ptr, TMD_BOLD, dur, TRUE, TRUE)) *ident = TRUE; if (player_inc_timed(p_ptr, TMD_SHERO, dur, TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_RESIST_ACID: { if (player_inc_timed(p_ptr, TMD_OPP_ACID, randint1(10) + 10, TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_RESIST_ELEC: { if (player_inc_timed(p_ptr, TMD_OPP_ELEC, randint1(10) + 10, TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_RESIST_FIRE: { if (player_inc_timed(p_ptr, TMD_OPP_FIRE, randint1(10) + 10, TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_RESIST_COLD: { if (player_inc_timed(p_ptr, TMD_OPP_COLD, randint1(10) + 10, TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_RESIST_POIS: { if (player_inc_timed(p_ptr, TMD_OPP_POIS, randint1(10) + 10, TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_RESIST_ALL: { if (player_inc_timed(p_ptr, TMD_OPP_ACID, randint1(20) + 20, TRUE, TRUE)) *ident = TRUE; if (player_inc_timed(p_ptr, TMD_OPP_ELEC, randint1(20) + 20, TRUE, TRUE)) *ident = TRUE; if (player_inc_timed(p_ptr, TMD_OPP_FIRE, randint1(20) + 20, TRUE, TRUE)) *ident = TRUE; if (player_inc_timed(p_ptr, TMD_OPP_COLD, randint1(20) + 20, TRUE, TRUE)) *ident = TRUE; if (player_inc_timed(p_ptr, TMD_OPP_POIS, randint1(20) + 20, TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_DETECT_TREASURE: { if (detect_treasure(aware, FALSE)) *ident = TRUE; return TRUE; } case EF_DETECT_TRAP: { if (detect_traps(aware)) *ident = TRUE; return TRUE; } case EF_DETECT_DOORSTAIR: { if (detect_doorstairs(aware)) *ident = TRUE; return TRUE; } case EF_DETECT_INVIS: { if (detect_monsters_invis(aware)) *ident = TRUE; return TRUE; } case EF_DETECT_EVIL: { if (detect_monsters_evil(aware)) *ident = TRUE; return TRUE; } case EF_DETECT_ALL: { if (detect_all(aware)) *ident = TRUE; return TRUE; } case EF_ENCHANT_TOHIT: { *ident = TRUE; return enchant_spell(1, 0, 0); } case EF_ENCHANT_TODAM: { *ident = TRUE; return enchant_spell(0, 1, 0); } case EF_ENCHANT_WEAPON: { *ident = TRUE; return enchant_spell(randint1(3), randint1(3), 0); } case EF_ENCHANT_ARMOR: { *ident = TRUE; return enchant_spell(0, 0, 1); } case EF_ENCHANT_ARMOR2: { *ident = TRUE; return enchant_spell(0, 0, randint1(3) + 2); } case EF_RESTORE_ITEM: { *ident = TRUE; return restore_item(); } case EF_IDENTIFY: { *ident = TRUE; if (!ident_spell()) return FALSE; return TRUE; } case EF_REMOVE_CURSE: { if (remove_curse()) { if (!p_ptr->timed[TMD_BLIND]) msg("The air around your body glows blue for a moment..."); else msg("You feel as if someone is watching over you."); *ident = TRUE; } return TRUE; } case EF_REMOVE_CURSE2: { remove_all_curse(); *ident = TRUE; return TRUE; } case EF_LIGHT: { if (light_area(damroll(2, 8), 2)) *ident = TRUE; return TRUE; } case EF_SUMMON_MON: { int i; sound(MSG_SUM_MONSTER); for (i = 0; i < randint1(3); i++) { if (summon_specific(py, px, p_ptr->depth, 0, 1)) *ident = TRUE; } return TRUE; } case EF_SUMMON_UNDEAD: { int i; sound(MSG_SUM_UNDEAD); for (i = 0; i < randint1(3); i++) { if (summon_specific(py, px, p_ptr->depth, S_UNDEAD, 1)) *ident = TRUE; } return TRUE; } case EF_TELE_PHASE: { teleport_player(10); *ident = TRUE; return TRUE; } case EF_TELE_LONG: { teleport_player(100); *ident = TRUE; return TRUE; } case EF_TELE_LEVEL: { (void)teleport_player_level(); *ident = TRUE; return TRUE; } case EF_CONFUSING: { if (p_ptr->confusing == 0) { msg("Your hands begin to glow."); p_ptr->confusing = TRUE; *ident = TRUE; } return TRUE; } case EF_MAPPING: { map_area(); *ident = TRUE; return TRUE; } case EF_RUNE: { warding_glyph(); *ident = TRUE; return TRUE; } case EF_ACQUIRE: { acquirement(py, px, p_ptr->depth, 1, TRUE); *ident = TRUE; return TRUE; } case EF_ACQUIRE2: { acquirement(py, px, p_ptr->depth, randint1(2) + 1, TRUE); *ident = TRUE; return TRUE; } case EF_ANNOY_MON: { msg("There is a high pitched humming noise."); aggravate_monsters(0); *ident = TRUE; return TRUE; } case EF_CREATE_TRAP: { /* Hack -- no traps in the town */ if (p_ptr->depth == 0) return TRUE; trap_creation(); msg("You hear a low-pitched whistling sound."); *ident = TRUE; return TRUE; } case EF_DESTROY_TDOORS: { if (destroy_doors_touch()) *ident = TRUE; return TRUE; } case EF_RECHARGE: { *ident = TRUE; if (!recharge(60)) return FALSE; return TRUE; } case EF_BANISHMENT: { *ident = TRUE; if (!banishment()) return FALSE; return TRUE; } case EF_DARKNESS: { if (!check_state(p_ptr, OF_RES_DARK, p_ptr->state.flags)) (void)player_inc_timed(p_ptr, TMD_BLIND, 3 + randint1(5), TRUE, TRUE); unlight_area(10, 3); wieldeds_notice_flag(p_ptr, OF_RES_DARK); *ident = TRUE; return TRUE; } case EF_PROTEVIL: { if (player_inc_timed(p_ptr, TMD_PROTEVIL, randint1(25) + 3 * p_ptr->lev, TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_SATISFY: { if (player_set_food(p_ptr, PY_FOOD_MAX - 1)) *ident = TRUE; return TRUE; } case EF_CURSE_WEAPON: { if (curse_weapon()) *ident = TRUE; return TRUE; } case EF_CURSE_ARMOR: { if (curse_armor()) *ident = TRUE; return TRUE; } case EF_BLESSING: { if (player_inc_timed(p_ptr, TMD_BLESSED, randint1(12) + 6, TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_BLESSING2: { if (player_inc_timed(p_ptr, TMD_BLESSED, randint1(24) + 12, TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_BLESSING3: { if (player_inc_timed(p_ptr, TMD_BLESSED, randint1(48) + 24, TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_RECALL: { set_recall(); *ident = TRUE; return TRUE; } case EF_DEEP_DESCENT: { int i, target_depth = p_ptr->depth; /* Calculate target depth */ for (i = 5; i > 0; i--) { if (is_quest(target_depth)) break; if (target_depth >= MAX_DEPTH - 1) break; target_depth++; } if (target_depth > p_ptr->depth) { msgt(MSG_TPLEVEL, "The air around you starts to swirl..."); p_ptr->deep_descent = 3 + randint1(4); *ident = TRUE; return TRUE; } else { msgt(MSG_TPLEVEL, "You sense a malevolent presence blocking passage to the levels below."); *ident = TRUE; return FALSE; } } case EF_LOSHASTE: { if (speed_monsters()) *ident = TRUE; return TRUE; } case EF_LOSSLEEP: { if (sleep_monsters(aware)) *ident = TRUE; return TRUE; } case EF_LOSSLOW: { if (slow_monsters()) *ident = TRUE; return TRUE; } case EF_LOSCONF: { if (confuse_monsters(aware)) *ident = TRUE; return TRUE; } case EF_LOSKILL: { (void)mass_banishment(); *ident = TRUE; return TRUE; } case EF_EARTHQUAKES: { earthquake(py, px, 10); *ident = TRUE; return TRUE; } case EF_DESTRUCTION2: { destroy_area(py, px, 15, TRUE); *ident = TRUE; return TRUE; } case EF_ILLUMINATION: { if (light_area(damroll(2, 15), 3)) *ident = TRUE; return TRUE; } case EF_CLAIRVOYANCE: { *ident = TRUE; wiz_light(); (void)detect_traps(TRUE); (void)detect_doorstairs(TRUE); return TRUE; } case EF_PROBING: { *ident = probing(); return TRUE; } case EF_STONE_TO_MUD: { if (wall_to_mud(dir)) *ident = TRUE; return TRUE; } case EF_CONFUSE2: { *ident = TRUE; confuse_monster(dir, 20, aware); return TRUE; } case EF_BIZARRE: { *ident = TRUE; ring_of_power(dir); return TRUE; } case EF_STAR_BALL: { int i; *ident = TRUE; for (i = 0; i < 8; i++) fire_ball(GF_ELEC, ddd[i], (150 * (100 + boost) / 100), 3); return TRUE; } case EF_RAGE_BLESS_RESIST: { dur = randint1(50) + 50; *ident = TRUE; (void)hp_player(30); (void)player_clear_timed(p_ptr, TMD_AFRAID, TRUE); (void)player_inc_timed(p_ptr, TMD_BOLD, dur, TRUE, TRUE); (void)player_inc_timed(p_ptr, TMD_SHERO, dur, TRUE, TRUE); (void)player_inc_timed(p_ptr, TMD_BLESSED, randint1(50) + 50, TRUE, TRUE); (void)player_inc_timed(p_ptr, TMD_OPP_ACID, randint1(50) + 50, TRUE, TRUE); (void)player_inc_timed(p_ptr, TMD_OPP_ELEC, randint1(50) + 50, TRUE, TRUE); (void)player_inc_timed(p_ptr, TMD_OPP_FIRE, randint1(50) + 50, TRUE, TRUE); (void)player_inc_timed(p_ptr, TMD_OPP_COLD, randint1(50) + 50, TRUE, TRUE); (void)player_inc_timed(p_ptr, TMD_OPP_POIS, randint1(50) + 50, TRUE, TRUE); return TRUE; } case EF_SLEEPII: { *ident = TRUE; sleep_monsters_touch(aware); return TRUE; } case EF_RESTORE_LIFE: { *ident = TRUE; restore_level(); return TRUE; } case EF_MISSILE: { *ident = TRUE; dam = damroll(3, 4) * (100 + boost) / 100; fire_bolt_or_beam(beam, GF_MISSILE, dir, dam); return TRUE; } case EF_DISPEL_EVIL: { *ident = TRUE; dam = p_ptr->lev * 5 * (100 + boost) / 100; dispel_evil(dam); return TRUE; } case EF_DISPEL_EVIL60: { dam = 60 * (100 + boost) / 100; if (dispel_evil(dam)) *ident = TRUE; return TRUE; } case EF_DISPEL_UNDEAD: { dam = 60 * (100 + boost) / 100; if (dispel_undead(dam)) *ident = TRUE; return TRUE; } case EF_DISPEL_ALL: { dam = 120 * (100 + boost) / 100; if (dispel_monsters(dam)) *ident = TRUE; return TRUE; } case EF_HASTE: { if (!p_ptr->timed[TMD_FAST]) { if (player_set_timed(p_ptr, TMD_FAST, damroll(2, 10) + 20, TRUE)) *ident = TRUE; } else { (void)player_inc_timed(p_ptr, TMD_FAST, 5, TRUE, TRUE); } return TRUE; } case EF_HASTE1: { if (!p_ptr->timed[TMD_FAST]) { if (player_set_timed(p_ptr, TMD_FAST, randint1(20) + 20, TRUE)) *ident = TRUE; } else { (void)player_inc_timed(p_ptr, TMD_FAST, 5, TRUE, TRUE); } return TRUE; } case EF_HASTE2: { if (!p_ptr->timed[TMD_FAST]) { if (player_set_timed(p_ptr, TMD_FAST, randint1(75) + 75, TRUE)) *ident = TRUE; } else { (void)player_inc_timed(p_ptr, TMD_FAST, 5, TRUE, TRUE); } return TRUE; } case EF_FIRE_BOLT: { *ident = TRUE; dam = damroll(9, 8) * (100 + boost) / 100; fire_bolt(GF_FIRE, dir, dam); return TRUE; } case EF_FIRE_BOLT2: { dam = damroll(12, 8) * (100 + boost) / 100; fire_bolt_or_beam(beam, GF_FIRE, dir, dam); *ident = TRUE; return TRUE; } case EF_FIRE_BOLT3: { dam = damroll(16, 8) * (100 + boost) / 100; fire_bolt_or_beam(beam, GF_FIRE, dir, dam); *ident = TRUE; return TRUE; } case EF_FIRE_BOLT72: { dam = 72 * (100 + boost) / 100; *ident = TRUE; fire_ball(GF_FIRE, dir, dam, 2); return TRUE; } case EF_FIRE_BALL: { dam = 144 * (100 + boost) / 100; fire_ball(GF_FIRE, dir, dam, 2); *ident = TRUE; return TRUE; } case EF_FIRE_BALL2: { dam = 120 * (100 + boost) / 100; *ident = TRUE; fire_ball(GF_FIRE, dir, dam, 3); return TRUE; } case EF_FIRE_BALL200: { dam = 200 * (100 + boost) / 100; *ident = TRUE; fire_ball(GF_FIRE, dir, dam, 3); return TRUE; } case EF_COLD_BOLT: { dam = damroll(6, 8) * (100 + boost) / 100; *ident = TRUE; fire_bolt_or_beam(beam, GF_COLD, dir, dam); return TRUE; } case EF_COLD_BOLT2: { dam = damroll(12, 8) * (100 + boost) / 100; *ident = TRUE; fire_bolt(GF_COLD, dir, dam); return TRUE; } case EF_COLD_BALL2: { dam = 200 * (100 + boost) / 100; *ident = TRUE; fire_ball(GF_COLD, dir, dam, 3); return TRUE; } case EF_COLD_BALL50: { dam = 50 * (100 + boost) / 100; *ident = TRUE; fire_ball(GF_COLD, dir, dam, 2); return TRUE; } case EF_COLD_BALL100: { dam = 100 * (100 + boost) / 100; *ident = TRUE; fire_ball(GF_COLD, dir, dam, 2); return TRUE; } case EF_COLD_BALL160: { dam = 160 * (100 + boost) / 100; *ident = TRUE; fire_ball(GF_COLD, dir, dam, 3); return TRUE; } case EF_ACID_BOLT: { dam = damroll(5, 8) * (100 + boost) / 100; *ident = TRUE; fire_bolt(GF_ACID, dir, dam); return TRUE; } case EF_ACID_BOLT2: { dam = damroll(10, 8) * (100 + boost) / 100; fire_bolt_or_beam(beam, GF_ACID, dir, dam); *ident = TRUE; return TRUE; } case EF_ACID_BOLT3: { dam = damroll(12, 8) * (100 + boost) / 100; fire_bolt_or_beam(beam, GF_ACID, dir, dam); *ident = TRUE; return TRUE; } case EF_ACID_BALL: { dam = 120 * (100 + boost) / 100; fire_ball(GF_ACID, dir, dam, 2); *ident = TRUE; return TRUE; } case EF_ELEC_BOLT: { dam = damroll(6, 6) * (100 + boost) / 100; *ident = TRUE; fire_beam(GF_ELEC, dir, dam); return TRUE; } case EF_ELEC_BALL: { dam = 64 * (100 + boost) / 100; fire_ball(GF_ELEC, dir, dam, 2); *ident = TRUE; return TRUE; } case EF_ELEC_BALL2: { dam = 250 * (100 + boost) / 100; *ident = TRUE; fire_ball(GF_ELEC, dir, dam, 3); return TRUE; } case EF_ARROW: { dam = 150 * (100 + boost) / 100; *ident = TRUE; fire_bolt(GF_ARROW, dir, dam); return TRUE; } case EF_REM_FEAR_POIS: { *ident = TRUE; (void)player_clear_timed(p_ptr, TMD_AFRAID, TRUE); (void)player_clear_timed(p_ptr, TMD_POISONED, TRUE); return TRUE; } case EF_STINKING_CLOUD: { dam = 12 * (100 + boost) / 100; *ident = TRUE; fire_ball(GF_POIS, dir, dam, 3); return TRUE; } case EF_DRAIN_LIFE1: { dam = 90 * (100 + boost) / 100; if (drain_life(dir, dam)) *ident = TRUE; return TRUE; } case EF_DRAIN_LIFE2: { dam = 120 * (100 + boost) / 100; if (drain_life(dir, dam)) *ident = TRUE; return TRUE; } case EF_DRAIN_LIFE3: { dam = 150 * (100 + boost) / 100; if (drain_life(dir, dam)) *ident = TRUE; return TRUE; } case EF_DRAIN_LIFE4: { dam = 250 * (100 + boost) / 100; if (drain_life(dir, dam)) *ident = TRUE; return TRUE; } case EF_FIREBRAND: { *ident = TRUE; if (!brand_bolts()) return FALSE; return TRUE; } case EF_MANA_BOLT: { dam = damroll(12, 8) * (100 + boost) / 100; fire_bolt(GF_MANA, dir, dam); *ident = TRUE; return TRUE; } case EF_MON_HEAL: { if (heal_monster(dir)) *ident = TRUE; return TRUE; } case EF_MON_HASTE: { if (speed_monster(dir)) *ident = TRUE; return TRUE; } case EF_MON_SLOW: { if (slow_monster(dir)) *ident = TRUE; return TRUE; } case EF_MON_CONFUSE: { if (confuse_monster(dir, 10, aware)) *ident = TRUE; return TRUE; } case EF_MON_SLEEP: { if (sleep_monster(dir, aware)) *ident = TRUE; return TRUE; } case EF_MON_CLONE: { if (clone_monster(dir)) *ident = TRUE; return TRUE; } case EF_MON_SCARE: { if (fear_monster(dir, 10, aware)) *ident = TRUE; return TRUE; } case EF_LIGHT_LINE: { msg("A line of shimmering blue light appears."); light_line(dir); *ident = TRUE; return TRUE; } case EF_TELE_OTHER: { if (teleport_monster(dir)) *ident = TRUE; return TRUE; } case EF_DISARMING: { if (disarm_trap(dir)) *ident = TRUE; return TRUE; } case EF_TDOOR_DEST: { if (destroy_door(dir)) *ident = TRUE; return TRUE; } case EF_POLYMORPH: { if (poly_monster(dir)) *ident = TRUE; return TRUE; } case EF_STARLIGHT: { int i; if (!p_ptr->timed[TMD_BLIND]) msg("Light shoots in all directions!"); for (i = 0; i < 8; i++) light_line(ddd[i]); *ident = TRUE; return TRUE; } case EF_STARLIGHT2: { int k; for (k = 0; k < 8; k++) strong_light_line(ddd[k]); *ident = TRUE; return TRUE; } case EF_BERSERKER: { dur = randint1(50) + 50; if (player_inc_timed(p_ptr, TMD_BOLD, dur, TRUE, TRUE)) *ident = TRUE; if (player_inc_timed(p_ptr, TMD_SHERO, dur, TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_SUN_HERO: { (void)light_area(damroll(20, 10), 8); dur = randint1(25) + 25; if (player_inc_timed(p_ptr, TMD_BOLD, dur, TRUE, TRUE)) *ident = TRUE; if (player_inc_timed(p_ptr, TMD_SHERO, dur, TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_WONDER: { if (effect_wonder(dir, randint1(100) + p_ptr->lev / 5, beam)) *ident = TRUE; return TRUE; } case EF_WAND_BREATH: { /* table of random ball effects and their damages */ const int breath_types[] = { GF_ACID, 200, GF_ELEC, 160, GF_FIRE, 200, GF_COLD, 160, GF_POIS, 120 }; /* pick a random (type, damage) tuple in the table */ int which = 2 * randint0(sizeof(breath_types) / (2 * sizeof(int))); fire_ball(breath_types[which], dir, breath_types[which + 1], 3); *ident = TRUE; return TRUE; } case EF_STAFF_MAGI: { if (do_res_stat(A_INT)) *ident = TRUE; if (p_ptr->csp < p_ptr->msp) { p_ptr->csp = p_ptr->msp; p_ptr->csp_frac = 0; *ident = TRUE; msg("Your feel your head clear."); p_ptr->redraw |= (PR_MANA); } return TRUE; } case EF_STAFF_HOLY: { dam = 120 * (100 + boost) / 100; if (dispel_evil(dam)) *ident = TRUE; if (player_inc_timed(p_ptr, TMD_PROTEVIL, randint1(25) + 3 * p_ptr->lev, TRUE, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_POISONED, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_AFRAID, TRUE)) *ident = TRUE; if (hp_player(50)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_STUN, TRUE)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_CUT, TRUE)) *ident = TRUE; return TRUE; } case EF_DRINK_BREATH: { const int breath_types[] = { GF_FIRE, 80, GF_COLD, 80, }; int which = 2 * randint0(N_ELEMENTS(breath_types) / 2); fire_ball(breath_types[which], dir, breath_types[which + 1], 2); *ident = TRUE; return TRUE; } case EF_DRINK_GOOD: { msg("You feel less thirsty."); *ident = TRUE; return TRUE; } case EF_DRINK_DEATH: { msg("A feeling of Death flows through your body."); take_hit(p_ptr, 5000, "a potion of Death"); *ident = TRUE; return TRUE; } case EF_DRINK_RUIN: { msg("Your nerves and muscles feel weak and lifeless!"); take_hit(p_ptr, damroll(10, 10), "a potion of Ruination"); player_stat_dec(p_ptr, A_DEX, TRUE); player_stat_dec(p_ptr, A_WIS, TRUE); player_stat_dec(p_ptr, A_CON, TRUE); player_stat_dec(p_ptr, A_STR, TRUE); player_stat_dec(p_ptr, A_CHR, TRUE); player_stat_dec(p_ptr, A_INT, TRUE); *ident = TRUE; return TRUE; } case EF_DRINK_DETONATE: { msg("Massive explosions rupture your body!"); take_hit(p_ptr, damroll(50, 20), "a potion of Detonation"); (void)player_inc_timed(p_ptr, TMD_STUN, 75, TRUE, TRUE); (void)player_inc_timed(p_ptr, TMD_CUT, 5000, TRUE, TRUE); *ident = TRUE; return TRUE; } case EF_DRINK_SALT: { msg("The potion makes you vomit!"); player_set_food(p_ptr, PY_FOOD_STARVE - 1); (void)player_clear_timed(p_ptr, TMD_POISONED, TRUE); (void)player_inc_timed(p_ptr, TMD_PARALYZED, 4, TRUE, FALSE); *ident = TRUE; return TRUE; } case EF_FOOD_GOOD: { msg("That tastes good."); *ident = TRUE; return TRUE; } case EF_FOOD_WAYBREAD: { msg("That tastes good."); (void)player_clear_timed(p_ptr, TMD_POISONED, TRUE); (void)hp_player(damroll(4, 8)); *ident = TRUE; return TRUE; } case EF_SHROOM_EMERGENCY: { (void)player_set_timed(p_ptr, TMD_IMAGE, rand_spread(250, 50), TRUE); (void)player_set_timed(p_ptr, TMD_OPP_FIRE, rand_spread(30, 10), TRUE); (void)player_set_timed(p_ptr, TMD_OPP_COLD, rand_spread(30, 10), TRUE); (void)hp_player(200); *ident = TRUE; return TRUE; } case EF_SHROOM_TERROR: { if (player_set_timed(p_ptr, TMD_TERROR, rand_spread(100, 20), TRUE)) *ident = TRUE; return TRUE; } case EF_SHROOM_STONE: { if (player_set_timed(p_ptr, TMD_STONESKIN, rand_spread(80, 20), TRUE)) *ident = TRUE; return TRUE; } case EF_SHROOM_DEBILITY: { int stat = one_in_(2) ? A_STR : A_CON; if (p_ptr->csp < p_ptr->msp) { p_ptr->csp = p_ptr->msp; p_ptr->csp_frac = 0; msg("Your feel your head clear."); p_ptr->redraw |= (PR_MANA); *ident = TRUE; } (void)do_dec_stat(stat, FALSE); *ident = TRUE; return TRUE; } case EF_SHROOM_SPRINTING: { if (player_inc_timed(p_ptr, TMD_SPRINT, 100, TRUE, TRUE)) *ident = TRUE; return TRUE; } case EF_SHROOM_PURGING: { player_set_food(p_ptr, PY_FOOD_FAINT - 1); if (do_res_stat(A_STR)) *ident = TRUE; if (do_res_stat(A_CON)) *ident = TRUE; if (player_clear_timed(p_ptr, TMD_POISONED, TRUE)) *ident = TRUE; return TRUE; } case EF_RING_ACID: { dam = 70 * (100 + boost) / 100; *ident = TRUE; fire_ball(GF_ACID, dir, dam, 2); player_inc_timed(p_ptr, TMD_OPP_ACID, randint1(20) + 20, TRUE, TRUE); return TRUE; } case EF_RING_FLAMES: { dam = 80 * (100 + boost) / 100; *ident = TRUE; fire_ball(GF_FIRE, dir, dam, 2); player_inc_timed(p_ptr, TMD_OPP_FIRE, randint1(20) + 20, TRUE, TRUE); return TRUE; } case EF_RING_ICE: { dam = 75 * (100 + boost) / 100; *ident = TRUE; fire_ball(GF_COLD, dir, dam, 2); player_inc_timed(p_ptr, TMD_OPP_COLD, randint1(20) + 20, TRUE, TRUE); return TRUE; } case EF_RING_LIGHTNING: { dam = 85 * (100 + boost) / 100; *ident = TRUE; fire_ball(GF_ELEC, dir, dam, 2); player_inc_timed(p_ptr, TMD_OPP_ELEC, randint1(20) + 20, TRUE, TRUE); return TRUE; } case EF_DRAGON_BLUE: { dam = 100 * (100 + boost) / 100; msgt(MSG_BR_ELEC, "You breathe lightning."); fire_ball(GF_ELEC, dir, dam, 2); return TRUE; } case EF_DRAGON_GREEN: { dam = 150 * (100 + boost) / 100; msgt(MSG_BR_GAS, "You breathe poison gas."); fire_ball(GF_POIS, dir, dam, 2); return TRUE; } case EF_DRAGON_RED: { dam = 200 * (100 + boost) / 100; msgt(MSG_BR_FIRE, "You breathe fire."); fire_ball(GF_FIRE, dir, dam, 2); return TRUE; } case EF_DRAGON_MULTIHUED: { static const struct { int msg_sound; const char *msg; int typ; } mh[] = { { MSG_BR_ELEC, "lightning", GF_ELEC }, { MSG_BR_FROST, "frost", GF_COLD }, { MSG_BR_ACID, "acid", GF_ACID }, { MSG_BR_GAS, "poison gas", GF_POIS }, { MSG_BR_FIRE, "fire", GF_FIRE } }; int chance = randint0(5); dam = 250 * (100 + boost) / 100; msgt(mh[chance].msg_sound, "You breathe %s.", mh[chance].msg); fire_ball(mh[chance].typ, dir, dam, 2); return TRUE; } case EF_DRAGON_BRONZE: { dam = 120 * (100 + boost) / 100; msgt(MSG_BR_CONF, "You breathe confusion."); fire_ball(GF_CONFU, dir, dam, 2); return TRUE; } case EF_DRAGON_GOLD: { dam = 130 * (100 + boost) / 100; msgt(MSG_BR_SOUND, "You breathe sound."); fire_ball(GF_SOUND, dir, dam, 2); return TRUE; } case EF_DRAGON_CHAOS: { dam = 220 * (100 + boost) / 100; chance = randint0(2); msgt((chance == 1 ? MSG_BR_CHAOS : MSG_BR_DISEN), "You breathe %s.", ((chance == 1 ? "chaos" : "disenchantment"))); fire_ball((chance == 1 ? GF_CHAOS : GF_DISEN), dir, dam, 2); return TRUE; } case EF_DRAGON_LAW: { dam = 230 * (100 + boost) / 100; chance = randint0(2); msgt((chance == 1 ? MSG_BR_SOUND : MSG_BR_SHARDS), "You breathe %s.", ((chance == 1 ? "sound" : "shards"))); fire_ball((chance == 1 ? GF_SOUND : GF_SHARD), dir, dam, 2); return TRUE; } case EF_DRAGON_BALANCE: { dam = 250 * (100 + boost) / 100; chance = randint0(4); msg("You breathe %s.", ((chance == 1) ? "chaos" : ((chance == 2) ? "disenchantment" : ((chance == 3) ? "sound" : "shards")))); fire_ball(((chance == 1) ? GF_CHAOS : ((chance == 2) ? GF_DISEN : ((chance == 3) ? GF_SOUND : GF_SHARD))), dir, dam, 2); return TRUE; } case EF_DRAGON_SHINING: { dam = 200 * (100 + boost) / 100; chance = randint0(2); msgt((chance == 0 ? MSG_BR_LIGHT : MSG_BR_DARK), "You breathe %s.", ((chance == 0 ? "light" : "darkness"))); fire_ball((chance == 0 ? GF_LIGHT : GF_DARK), dir, dam, 2); return TRUE; } case EF_DRAGON_POWER: { dam = 300 * (100 + boost) / 100; msgt(MSG_BR_ELEMENTS, "You breathe the elements."); fire_ball(GF_MISSILE, dir, dam, 2); return TRUE; } case EF_TRAP_DOOR: { msg("You fall through a trap door!"); if (check_state(p_ptr, OF_FEATHER, p_ptr->state.flags)) { msg("You float gently down to the next level."); } else { take_hit(p_ptr, damroll(2, 8), "a trap"); } wieldeds_notice_flag(p_ptr, OF_FEATHER); dungeon_change_level(p_ptr->depth + 1); return TRUE; } case EF_TRAP_PIT: { msg("You fall into a pit!"); if (check_state(p_ptr, OF_FEATHER, p_ptr->state.flags)) { msg("You float gently to the bottom of the pit."); } else { take_hit(p_ptr, damroll(2, 6), "a trap"); } wieldeds_notice_flag(p_ptr, OF_FEATHER); return TRUE; } case EF_TRAP_PIT_SPIKES: { msg("You fall into a spiked pit!"); if (check_state(p_ptr, OF_FEATHER, p_ptr->state.flags)) { msg("You float gently to the floor of the pit."); msg("You carefully avoid touching the spikes."); } else { int dam = damroll(2, 6); /* Extra spike damage */ if (one_in_(2)) { msg("You are impaled!"); dam *= 2; (void)player_inc_timed(p_ptr, TMD_CUT, randint1(dam), TRUE, TRUE); } take_hit(p_ptr, dam, "a trap"); } wieldeds_notice_flag(p_ptr, OF_FEATHER); return TRUE; } case EF_TRAP_PIT_POISON: { msg("You fall into a spiked pit!"); if (check_state(p_ptr, OF_FEATHER, p_ptr->state.flags)) { msg("You float gently to the floor of the pit."); msg("You carefully avoid touching the spikes."); } else { int dam = damroll(2, 6); /* Extra spike damage */ if (one_in_(2)) { msg("You are impaled on poisonous spikes!"); (void)player_inc_timed(p_ptr, TMD_CUT, randint1(dam * 2), TRUE, TRUE); (void)player_inc_timed(p_ptr, TMD_POISONED, randint1(dam * 4), TRUE, TRUE); } take_hit(p_ptr, dam, "a trap"); } wieldeds_notice_flag(p_ptr, OF_FEATHER); return TRUE; } case EF_TRAP_RUNE_SUMMON: { int i; int num = 2 + randint1(3); msgt(MSG_SUM_MONSTER, "You are enveloped in a cloud of smoke!"); /* Remove trap */ cave->info[py][px] &= ~(CAVE_MARK); cave_set_feat(cave, py, px, FEAT_FLOOR); for (i = 0; i < num; i++) (void)summon_specific(py, px, p_ptr->depth, 0, 1); return TRUE; } case EF_TRAP_RUNE_TELEPORT: { msg("You hit a teleport trap!"); teleport_player(100); return TRUE; } case EF_TRAP_SPOT_FIRE: { int dam; msg("You are enveloped in flames!"); dam = damroll(4, 6); dam = adjust_dam(p_ptr, GF_FIRE, dam, RANDOMISE, check_for_resist(p_ptr, GF_FIRE, p_ptr->state.flags, TRUE)); if (dam) { take_hit(p_ptr, dam, "a fire trap"); inven_damage(p_ptr, GF_FIRE, MIN(dam * 5, 300)); } return TRUE; } case EF_TRAP_SPOT_ACID: { int dam; msg("You are splashed with acid!"); dam = damroll(4, 6); dam = adjust_dam(p_ptr, GF_ACID, dam, RANDOMISE, check_for_resist(p_ptr, GF_ACID, p_ptr->state.flags, TRUE)); if (dam) { take_hit(p_ptr, dam, "an acid trap"); inven_damage(p_ptr, GF_ACID, MIN(dam * 5, 300)); } return TRUE; } case EF_TRAP_DART_SLOW: { if (trap_check_hit(125)) { msg("A small dart hits you!"); take_hit(p_ptr, damroll(1, 4), "a trap"); (void)player_inc_timed(p_ptr, TMD_SLOW, randint0(20) + 20, TRUE, FALSE); } else { msg("A small dart barely misses you."); } return TRUE; } case EF_TRAP_DART_LOSE_STR: { if (trap_check_hit(125)) { msg("A small dart hits you!"); take_hit(p_ptr, damroll(1, 4), "a trap"); (void)do_dec_stat(A_STR, FALSE); } else { msg("A small dart barely misses you."); } return TRUE; } case EF_TRAP_DART_LOSE_DEX: { if (trap_check_hit(125)) { msg("A small dart hits you!"); take_hit(p_ptr, damroll(1, 4), "a trap"); (void)do_dec_stat(A_DEX, FALSE); } else { msg("A small dart barely misses you."); } return TRUE; } case EF_TRAP_DART_LOSE_CON: { if (trap_check_hit(125)) { msg("A small dart hits you!"); take_hit(p_ptr, damroll(1, 4), "a trap"); (void)do_dec_stat(A_CON, FALSE); } else { msg("A small dart barely misses you."); } return TRUE; } case EF_TRAP_GAS_BLIND: { msg("You are surrounded by a black gas!"); (void)player_inc_timed(p_ptr, TMD_BLIND, randint0(50) + 25, TRUE, TRUE); return TRUE; } case EF_TRAP_GAS_CONFUSE: { msg("You are surrounded by a gas of scintillating colors!"); (void)player_inc_timed(p_ptr, TMD_CONFUSED, randint0(20) + 10, TRUE, TRUE); return TRUE; } case EF_TRAP_GAS_POISON: { msg("You are surrounded by a pungent green gas!"); (void)player_inc_timed(p_ptr, TMD_POISONED, randint0(20) + 10, TRUE, TRUE); return TRUE; } case EF_TRAP_GAS_SLEEP: { msg("You are surrounded by a strange white mist!"); (void)player_inc_timed(p_ptr, TMD_PARALYZED, randint0(10) + 5, TRUE, TRUE); return TRUE; } case EF_XXX: case EF_MAX: break; } /* Not used */ msg("Effect not handled."); return FALSE; }
/* * Attempt to change an object into an ego-item -MWK- * Better only called by apply_magic(). * The return value says if we picked a cursed item (if allowed) and is * passed on to a_m_aux1/2(). * If no legal ego item is found, this routine returns 0, resulting in * an unenchanted item. */ static int make_ego_item(object_type *o_ptr, int level, bool force_uncursed) { int i, j; int e_idx; long value, total; ego_item_type *e_ptr; alloc_entry *table = alloc_ego_table; /* Fail if object already is ego or artifact */ if (o_ptr->name1) return (FALSE); if (o_ptr->name2) return (FALSE); /* Boost level (like with object base types) */ if (level > 0) { /* Occasional "boost" */ if (one_in_(GREAT_EGO)) { /* The bizarre calculation again */ level = 1 + (level * MAX_DEPTH / randint1(MAX_DEPTH)); } } /* Reset total */ total = 0L; /* Process probabilities */ for (i = 0; i < alloc_ego_size; i++) { /* Default */ table[i].prob3 = 0; /* Objects are sorted by depth */ if (table[i].level > level) continue; /* Get the index */ e_idx = table[i].index; /* Get the actual kind */ e_ptr = &e_info[e_idx]; /* Avoid cursed items if specified */ if (force_uncursed && cursed_p(e_ptr)) continue; /* Test if this is a legal ego-item type for this object */ for (j = 0; j < EGO_TVALS_MAX; j++) { /* Require identical base type */ if (o_ptr->tval == e_ptr->tval[j]) { /* Require sval in bounds, lower */ if (o_ptr->sval >= e_ptr->min_sval[j]) { /* Require sval in bounds, upper */ if (o_ptr->sval <= e_ptr->max_sval[j]) { /* Accept */ table[i].prob3 = table[i].prob2; } } } } /* Total */ total += table[i].prob3; } /* No legal ego-items -- create a normal unenchanted one */ if (total == 0) return (0); /* Pick an ego-item */ value = randint0(total); /* Find the object */ for (i = 0; i < alloc_ego_size; i++) { /* Found the entry */ if (value < table[i].prob3) break; /* Decrement */ value = value - table[i].prob3; } /* We have one */ e_idx = (byte)table[i].index; o_ptr->name2 = e_idx; return (flags_test(e_info[e_idx].flags, OF_SIZE, OF_CURSE_MASK, FLAG_END) ? -2 : 2); }
/* * Handle player hitting a real trap */ void hit_trap(int y, int x) { int i, num, dam; const char *name = "a trap"; /* Disturb the player */ disturb(0, 0); /* Analyze XXX XXX XXX */ switch (cave->feat[y][x]) { case FEAT_TRAP_HEAD + 0x00: { msg("You fall through a trap door!"); if (p_ptr->state.flags[OF_FEATHER]) { msg("You float gently down to the next level."); } else { dam = damroll(2, 8); take_hit(dam, name); } wieldeds_notice_flag(OF_FEATHER); /* New depth */ dungeon_change_level(p_ptr->depth + 1); break; } case FEAT_TRAP_HEAD + 0x01: { msg("You fall into a pit!"); if (p_ptr->state.flags[OF_FEATHER]) { msg("You float gently to the bottom of the pit."); } else { dam = damroll(2, 6); take_hit(dam, name); } wieldeds_notice_flag(OF_FEATHER); break; } case FEAT_TRAP_HEAD + 0x02: { msg("You fall into a spiked pit!"); if (p_ptr->state.flags[OF_FEATHER]) { msg("You float gently to the floor of the pit."); msg("You carefully avoid touching the spikes."); } else { /* Base damage */ dam = damroll(2, 6); /* Extra spike damage */ if (one_in_(2)) { msg("You are impaled!"); dam = dam * 2; (void)inc_timed(TMD_CUT, randint1(dam), TRUE); } /* Take the damage */ take_hit(dam, name); } wieldeds_notice_flag(OF_FEATHER); break; } case FEAT_TRAP_HEAD + 0x03: { msg("You fall into a spiked pit!"); if (p_ptr->state.flags[OF_FEATHER]) { msg("You float gently to the floor of the pit."); msg("You carefully avoid touching the spikes."); } else { /* Base damage */ dam = damroll(2, 6); /* Extra spike damage */ if (one_in_(2)) { msg("You are impaled on poisonous spikes!"); dam = dam * 2; (void)inc_timed(TMD_CUT, randint1(dam), TRUE); if (p_ptr->state.flags[OF_RES_POIS] || p_ptr->timed[TMD_OPP_POIS]) { msg("The poison does not affect you!"); } else { dam = dam * 2; (void)inc_timed(TMD_POISONED, randint1(dam), TRUE); } wieldeds_notice_flag(OF_RES_POIS); } /* Take the damage */ take_hit(dam, name); } wieldeds_notice_flag(OF_FEATHER); break; } case FEAT_TRAP_HEAD + 0x04: { sound(MSG_SUM_MONSTER); msg("You are enveloped in a cloud of smoke!"); cave->info[y][x] &= ~(CAVE_MARK); cave_set_feat(cave, y, x, FEAT_FLOOR); num = 2 + randint1(3); for (i = 0; i < num; i++) { (void)summon_specific(y, x, p_ptr->depth, 0, 1); } break; } case FEAT_TRAP_HEAD + 0x05: { msg("You hit a teleport trap!"); teleport_player(100); break; } case FEAT_TRAP_HEAD + 0x06: { msg("You are enveloped in flames!"); dam = damroll(4, 6); fire_dam(dam, "a fire trap"); break; } case FEAT_TRAP_HEAD + 0x07: { msg("You are splashed with acid!"); dam = damroll(4, 6); acid_dam(dam, "an acid trap"); break; } case FEAT_TRAP_HEAD + 0x08: { if (trap_check_hit(125)) { msg("A small dart hits you!"); dam = damroll(1, 4); take_hit(dam, name); (void)inc_timed(TMD_SLOW, randint0(20) + 20, TRUE); } else { msg("A small dart barely misses you."); } break; } case FEAT_TRAP_HEAD + 0x09: { if (trap_check_hit(125)) { msg("A small dart hits you!"); dam = damroll(1, 4); take_hit(dam, name); (void)do_dec_stat(A_STR, FALSE); } else { msg("A small dart barely misses you."); } break; } case FEAT_TRAP_HEAD + 0x0A: { if (trap_check_hit(125)) { msg("A small dart hits you!"); dam = damroll(1, 4); take_hit(dam, name); (void)do_dec_stat(A_DEX, FALSE); } else { msg("A small dart barely misses you."); } break; } case FEAT_TRAP_HEAD + 0x0B: { if (trap_check_hit(125)) { msg("A small dart hits you!"); dam = damroll(1, 4); take_hit(dam, name); (void)do_dec_stat(A_CON, FALSE); } else { msg("A small dart barely misses you."); } break; } case FEAT_TRAP_HEAD + 0x0C: { msg("You are surrounded by a black gas!"); if (!p_ptr->state.flags[OF_RES_BLIND]) (void)inc_timed(TMD_BLIND, randint0(50) + 25, TRUE); wieldeds_notice_flag(OF_RES_BLIND); break; } case FEAT_TRAP_HEAD + 0x0D: { msg("You are surrounded by a gas of scintillating colors!"); if (!p_ptr->state.flags[OF_RES_CONFU]) (void)inc_timed(TMD_CONFUSED, randint0(20) + 10, TRUE); wieldeds_notice_flag(OF_RES_CONFU); break; } case FEAT_TRAP_HEAD + 0x0E: { msg("You are surrounded by a pungent green gas!"); if (!p_ptr->state.flags[OF_RES_POIS] && !p_ptr->timed[TMD_OPP_POIS]) (void)inc_timed(TMD_POISONED, randint0(20) + 10, TRUE); wieldeds_notice_flag(OF_RES_POIS); break; } case FEAT_TRAP_HEAD + 0x0F: { msg("You are surrounded by a strange white mist!"); if (!p_ptr->state.flags[OF_FREE_ACT]) (void)inc_timed(TMD_PARALYZED, randint0(10) + 5, TRUE); wieldeds_notice_flag(OF_FREE_ACT); break; } } }
/** * Move player in the given direction. * * This routine should only be called when energy has been expended. * * Note that this routine handles monsters in the destination grid, * and also handles attempting to move into walls/doors/rubble/etc. */ void move_player(int dir, bool disarm) { int py = player->py; int px = player->px; int y = py + ddy[dir]; int x = px + ddx[dir]; int m_idx = cave->squares[y][x].mon; struct monster *mon = cave_monster(cave, m_idx); bool alterable = (square_isknowntrap(cave, y, x) || square_iscloseddoor(cave, y, x)); /* Attack monsters, alter traps/doors on movement, hit obstacles or move */ if (m_idx > 0) { /* Mimics surprise the player */ if (is_mimicking(mon)) { become_aware(mon); /* Mimic wakes up */ mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, false); } else { py_attack(y, x); } } else if (disarm && square_isknown(cave, y, x) && alterable) { /* Auto-repeat if not already repeating */ if (cmd_get_nrepeats() == 0) cmd_set_repeat(99); do_cmd_alter_aux(dir); } else if (player->upkeep->running && square_isknowntrap(cave, y, x)) { /* Stop running before known traps */ disturb(player, 0); } else if (!square_ispassable(cave, y, x)) { /* Disturb the player */ disturb(player, 0); /* Notice unknown obstacles, mention known obstacles */ if (!square_isknown(cave, y, x)) { if (square_isrubble(cave, y, x)) { msgt(MSG_HITWALL, "You feel a pile of rubble blocking your way."); square_memorize(cave, y, x); square_light_spot(cave, y, x); } else if (square_iscloseddoor(cave, y, x)) { msgt(MSG_HITWALL, "You feel a door blocking your way."); square_memorize(cave, y, x); square_light_spot(cave, y, x); } else { msgt(MSG_HITWALL, "You feel a wall blocking your way."); square_memorize(cave, y, x); square_light_spot(cave, y, x); } } else { if (square_isrubble(cave, y, x)) msgt(MSG_HITWALL, "There is a pile of rubble blocking your way."); else if (square_iscloseddoor(cave, y, x)) msgt(MSG_HITWALL, "There is a door blocking your way."); else msgt(MSG_HITWALL, "There is a wall blocking your way."); } } else { /* See if trap detection status will change */ bool old_dtrap = square_isdtrap(cave, py, px); bool new_dtrap = square_isdtrap(cave, y, x); /* Note the change in the detect status */ if (old_dtrap != new_dtrap) player->upkeep->redraw |= (PR_DTRAP); /* Disturb player if the player is about to leave the area */ if (player->upkeep->running && !player->upkeep->running_firststep && old_dtrap && !new_dtrap) { disturb(player, 0); return; } /* Move player */ monster_swap(py, px, y, x); /* New location */ y = py = player->py; x = px = player->px; /* Searching */ if (player->searching || (player->state.skills[SKILL_SEARCH_FREQUENCY] >= 50) || one_in_(50 - player->state.skills[SKILL_SEARCH_FREQUENCY])) search(false); /* Handle store doors, or notice objects */ if (square_isshop(cave, player->py, player->px)) { /* Disturb */ disturb(player, 0); event_signal(EVENT_ENTER_STORE); event_remove_handler_type(EVENT_ENTER_STORE); event_signal(EVENT_USE_STORE); event_remove_handler_type(EVENT_USE_STORE); event_signal(EVENT_LEAVE_STORE); event_remove_handler_type(EVENT_LEAVE_STORE); } else { /* Know objects, queue autopickup */ floor_pile_know(cave, player->py, player->px); cmdq_push(CMD_AUTOPICKUP); } /* Discover invisible traps, set off visible ones */ if (square_issecrettrap(cave, y, x)) { /* Disturb */ disturb(player, 0); /* Hit the trap. */ hit_trap(y, x); } else if (square_isknowntrap(cave, y, x)) { /* Disturb */ disturb(player, 0); /* Hit the trap */ hit_trap(y, x); } } player->upkeep->running_firststep = false; }
void pit_stats(void) { int tries = 1000; int depth = 0; int hist[z_info->pit_max]; int j, p; int type = 1; char tmp_val[100]; /* Initialize hist */ for (p = 0; p < z_info->pit_max; p++) hist[p] = 0; /* Format default value */ strnfmt(tmp_val, sizeof(tmp_val), "%d", tries); /* Ask for the input - take the first 7 characters*/ if (!get_string("Num of simulations: ", tmp_val, 7)) return; /* Get the new value */ tries = atoi(tmp_val); if (tries < 1) tries = 1; /* Format second default value */ strnfmt(tmp_val, sizeof(tmp_val), "%d", type); /* Ask for the input - take the first 7 characters*/ if (!get_string("Pit type: ", tmp_val, 7)) return; /* get the new value */ type = atoi(tmp_val); if (type < 1) type = 1; /* Format second default value */ strnfmt(tmp_val, sizeof(tmp_val), "%d", player->depth); /* Ask for the input - take the first 7 characters*/ if (!get_string("Depth: ", tmp_val, 7)) return; /* get the new value */ depth = atoi(tmp_val); if (depth < 1) depth = 1; for (j = 0; j < tries; j++) { int i; int pit_idx = 0; int pit_dist = 999; for (i = 0; i < z_info->pit_max; i++) { int offset, dist; struct pit_profile *pit = &pit_info[i]; if (!pit->name || pit->room_type != type) continue; offset = Rand_normal(pit->ave, 10); dist = ABS(offset - depth); if (dist < pit_dist && one_in_(pit->rarity)) { pit_idx = i; pit_dist = dist; } } hist[pit_idx]++; } for (p = 0; p < z_info->pit_max; p++) { struct pit_profile *pit = &pit_info[p]; if (pit->name) msg("Type: %s, Number: %d.", pit->name, hist[p]); } return; }
/** * This function takes a grid location (x, y) and extracts information the * player is allowed to know about it, filling in the grid_data structure * passed in 'g'. * * The information filled in is as follows: * - g->f_idx is filled in with the terrain's feature type, or FEAT_NONE * if the player doesn't know anything about the grid. The function * makes use of the "mimic" field in terrain in order to allow one * feature to look like another (hiding secret doors, invisible traps, * etc). This will return the terrain type the player "Knows" about, * not necessarily the real terrain. * - g->m_idx is set to the monster index, or 0 if there is none (or the * player doesn't know it). * - g->first_kind is set to the object_kind of the first object in a grid * that the player knows about, or NULL for no objects. * - g->muliple_objects is TRUE if there is more than one object in the * grid that the player knows and cares about (to facilitate any special * floor stack symbol that might be used). * - g->in_view is TRUE if the player can currently see the grid - this can * be used to indicate field-of-view, such as through the OPT(view_bright_light) * option. * - g->lighting is set to indicate the lighting level for the grid: * LIGHTING_DARK for unlit grids, LIGHTING_LIT for inherently light * grids (lit rooms, etc), LIGHTING_TORCH for grids lit by the player's * light source, and LIGHTING_LOS for grids in the player's line of sight. * Note that lighting is always LIGHTING_LIT for known "interesting" grids * like walls. * - g->is_player is TRUE if the player is on the given grid. * - g->hallucinate is TRUE if the player is hallucinating something "strange" * for this grid - this should pick a random monster to show if the m_idx * is non-zero, and a random object if first_kind is non-zero. * * NOTES: * This is called pretty frequently, whenever a grid on the map display * needs updating, so don't overcomplicate it. * * Terrain is remembered separately from objects and monsters, so can be * shown even when the player can't "see" it. This leads to things like * doors out of the player's view still change from closed to open and so on. * * TODO: * Hallucination is currently disabled (it was a display-level hack before, * and we need it to be a knowledge-level hack). The idea is that objects * may turn into different objects, monsters into different monsters, and * terrain may be objects, monsters, or stay the same. */ void map_info(unsigned y, unsigned x, grid_data *g) { object_type *obj; assert(x < (unsigned) cave->width); assert(y < (unsigned) cave->height); /* Default "clear" values, others will be set later where appropriate. */ g->first_kind = NULL; g->trap = NULL; g->multiple_objects = FALSE; g->lighting = LIGHTING_DARK; g->unseen_object = FALSE; g->unseen_money = FALSE; /* Use real feature (remove later) */ g->f_idx = cave->squares[y][x].feat; if (f_info[g->f_idx].mimic) g->f_idx = f_info[g->f_idx].mimic; g->in_view = (square_isseen(cave, y, x)) ? TRUE : FALSE; g->is_player = (cave->squares[y][x].mon < 0) ? TRUE : FALSE; g->m_idx = (g->is_player) ? 0 : cave->squares[y][x].mon; g->hallucinate = player->timed[TMD_IMAGE] ? TRUE : FALSE; g->trapborder = (square_isdedge(cave, y, x)) ? TRUE : FALSE; if (g->in_view) { g->lighting = LIGHTING_LOS; if (!square_isglow(cave, y, x) && OPT(view_yellow_light)) g->lighting = LIGHTING_TORCH; /* Remember seen feature */ cave_k->squares[y][x].feat = cave->squares[y][x].feat; } else if (!square_ismark(cave, y, x)) { g->f_idx = FEAT_NONE; //cave_k->squares[y][x].feat = FEAT_NONE; } else if (square_isglow(cave, y, x)) { g->lighting = LIGHTING_LIT; } /* Use known feature */ /* g->f_idx = cave_k->squares[y][x].feat; if (f_info[g->f_idx].mimic) g->f_idx = f_info[g->f_idx].mimic;*/ /* There is a trap in this square */ if (square_istrap(cave, y, x) && square_ismark(cave, y, x)) { struct trap *trap = cave->squares[y][x].trap; /* Scan the square trap list */ while (trap) { if (trf_has(trap->flags, TRF_TRAP)) { /* Accept the trap */ g->trap = trap; break; } trap = trap->next; } } /* Objects */ for (obj = square_object(cave, y, x); obj; obj = obj->next) { if (obj->marked == MARK_AWARE) { /* Distinguish between unseen money and objects */ if (tval_is_money(obj)) { g->unseen_money = TRUE; } else { g->unseen_object = TRUE; } } else if (obj->marked == MARK_SEEN && !ignore_item_ok(obj)) { if (!g->first_kind) { g->first_kind = obj->kind; } else { g->multiple_objects = TRUE; break; } } } /* Monsters */ if (g->m_idx > 0) { /* If the monster isn't "visible", make sure we don't list it.*/ monster_type *m_ptr = cave_monster(cave, g->m_idx); if (!mflag_has(m_ptr->mflag, MFLAG_VISIBLE)) g->m_idx = 0; } /* Rare random hallucination on non-outer walls */ if (g->hallucinate && g->m_idx == 0 && g->first_kind == 0) { if (one_in_(128) && (int) g->f_idx != FEAT_PERM) g->m_idx = 1; else if (one_in_(128) && (int) g->f_idx != FEAT_PERM) /* if hallucinating, we just need first_kind to not be NULL */ g->first_kind = k_info; else g->hallucinate = FALSE; } assert((int) g->f_idx <= FEAT_PASS_RUBBLE); if (!g->hallucinate) assert((int)g->m_idx < cave->mon_max); /* All other g fields are 'flags', mostly booleans. */ }
/* * Generate a new dungeon level * * Note that "dun_body" adds about 4000 bytes of memory to the stack. */ static bool cave_gen(void) { int i, k, y, x; dun_data dun_body; /* Global data */ dun = &dun_body; dun->destroyed = FALSE; dun->empty_level = FALSE; dun->cavern = FALSE; dun->laketype = 0; /* Fill the arrays of floors and walls in the good proportions */ set_floor_and_wall(dungeon_type); /* Prepare allocation table */ get_mon_num_prep(get_monster_hook(), NULL); /* Randomize the dungeon creation values */ dun_tun_rnd = rand_range(DUN_TUN_RND_MIN, DUN_TUN_RND_MAX); dun_tun_chg = rand_range(DUN_TUN_CHG_MIN, DUN_TUN_CHG_MAX); dun_tun_con = rand_range(DUN_TUN_CON_MIN, DUN_TUN_CON_MAX); dun_tun_pen = rand_range(DUN_TUN_PEN_MIN, DUN_TUN_PEN_MAX); dun_tun_jct = rand_range(DUN_TUN_JCT_MIN, DUN_TUN_JCT_MAX); /* Actual maximum number of rooms on this level */ dun->row_rooms = cur_hgt / BLOCK_HGT; dun->col_rooms = cur_wid / BLOCK_WID; /* Initialize the room table */ for (y = 0; y < dun->row_rooms; y++) { for (x = 0; x < dun->col_rooms; x++) { dun->room_map[y][x] = FALSE; } } /* No rooms yet */ dun->cent_n = 0; /* Empty arena levels */ if (ironman_empty_levels || ((d_info[dungeon_type].flags1 & DF1_ARENA) && (empty_levels && one_in_(EMPTY_LEVEL)))) { dun->empty_level = TRUE; if (cheat_room) msg_print("Arena level."); } if (dun->empty_level) { /* Start with floors */ for (y = 0; y < cur_hgt; y++) { for (x = 0; x < cur_wid; x++) { place_floor_bold(y, x); } } /* Special boundary walls -- Top and bottom */ for (x = 0; x < cur_wid; x++) { place_extra_bold(0, x); place_extra_bold(cur_hgt - 1, x); } /* Special boundary walls -- Left and right */ for (y = 1; y < (cur_hgt - 1); y++) { place_extra_bold(y, 0); place_extra_bold(y, cur_wid - 1); } } else { /* Start with walls */ for (y = 0; y < cur_hgt; y++) { for (x = 0; x < cur_wid; x++) { place_extra_bold(y, x); } } } /* Generate various caverns and lakes */ gen_caverns_and_lakes(); /* Build maze */ if (d_info[dungeon_type].flags1 & DF1_MAZE) { build_maze_vault(cur_wid/2-1, cur_hgt/2-1, cur_wid-4, cur_hgt-4, FALSE); /* Place 3 or 4 down stairs near some walls */ if (!alloc_stairs(feat_down_stair, rand_range(4, 5), 3)) return FALSE; /* Place 1 or 2 up stairs near some walls */ if (!alloc_stairs(feat_up_stair, 1, 2)) return FALSE; } /* Build some rooms */ else { int tunnel_fail_count = 0; /* * Build each type of room in turn until we cannot build any more. */ if (!generate_rooms()) return FALSE; /* Make a hole in the dungeon roof sometimes at level 1 But not in Angband. See Issue #3 */ if (dun_level == 1 && dungeon_type != DUNGEON_ANGBAND) { while (one_in_(DUN_MOS_DEN)) { place_trees(randint1(cur_wid - 2), randint1(cur_hgt - 2)); } } /* Destroy the level if necessary */ if (dun->destroyed) destroy_level(); /* Hack -- Add some rivers */ if (one_in_(7) && (randint1(dun_level) > 5)) { int feat1 = 0, feat2 = 0; /* Choose water or lava */ if ( randint1(MAX_DEPTH * 2) - 1 > dun_level && ( (d_info[dungeon_type].flags1 & DF1_WATER_RIVER) || (no_wilderness && one_in_(3)) ) ) { feat1 = feat_deep_water; feat2 = feat_shallow_water; } else if (d_info[dungeon_type].flags1 & DF1_LAVA_RIVER) { feat1 = feat_deep_lava; feat2 = feat_shallow_lava; } else feat1 = 0; if (feat1) { feature_type *f_ptr = &f_info[feat1]; /* Only add river if matches lake type or if have no lake at all */ if (((dun->laketype == LAKE_T_LAVA) && have_flag(f_ptr->flags, FF_LAVA)) || ((dun->laketype == LAKE_T_WATER) && have_flag(f_ptr->flags, FF_WATER)) || !dun->laketype) { add_river(feat1, feat2); } } } /* Hack -- Scramble the room order */ for (i = 0; i < dun->cent_n; i++) { int ty, tx; int pick = rand_range(0, i); ty = dun->cent[i].y; tx = dun->cent[i].x; dun->cent[i].y = dun->cent[pick].y; dun->cent[i].x = dun->cent[pick].x; dun->cent[pick].y = ty; dun->cent[pick].x = tx; } /* Start with no tunnel doors */ dun->door_n = 0; /* Hack -- connect the first room to the last room */ y = dun->cent[dun->cent_n-1].y; x = dun->cent[dun->cent_n-1].x; /* Connect all the rooms together */ for (i = 0; i < dun->cent_n; i++) { int j; /* Reset the arrays */ dun->tunn_n = 0; dun->wall_n = 0; /* Connect the room to the previous room */ if (randint1(dun_level) > d_info[dungeon_type].tunnel_percent) { /* make cave-like tunnel */ (void)build_tunnel2(dun->cent[i].x, dun->cent[i].y, x, y, 2, 2); } else { /* make normal tunnel */ if (!build_tunnel(dun->cent[i].y, dun->cent[i].x, y, x)) tunnel_fail_count++; } if (tunnel_fail_count >= 2) return FALSE; /* Turn the tunnel into corridor */ for (j = 0; j < dun->tunn_n; j++) { cave_type *c_ptr; feature_type *f_ptr; /* Access the grid */ y = dun->tunn[j].y; x = dun->tunn[j].x; /* Access the grid */ c_ptr = &cave[y][x]; f_ptr = &f_info[c_ptr->feat]; /* Clear previous contents (if not a lake), add a floor */ if (!have_flag(f_ptr->flags, FF_MOVE) || (!have_flag(f_ptr->flags, FF_WATER) && !have_flag(f_ptr->flags, FF_LAVA))) { /* Clear mimic type */ c_ptr->mimic = 0; place_floor_grid(c_ptr); } } /* Apply the piercings that we found */ for (j = 0; j < dun->wall_n; j++) { cave_type *c_ptr; /* Access the grid */ y = dun->wall[j].y; x = dun->wall[j].x; /* Access the grid */ c_ptr = &cave[y][x]; /* Clear mimic type */ c_ptr->mimic = 0; /* Clear previous contents, add up floor */ place_floor_grid(c_ptr); /* Occasional doorway */ if ((randint0(100) < dun_tun_pen) && !(d_info[dungeon_type].flags1 & DF1_NO_DOORS)) { /* Place a random door */ place_random_door(y, x, TRUE); } } /* Remember the "previous" room */ y = dun->cent[i].y; x = dun->cent[i].x; } /* Place intersection doors */ for (i = 0; i < dun->door_n; i++) { /* Extract junction location */ y = dun->door[i].y; x = dun->door[i].x; /* Try placing doors */ try_door(y, x - 1); try_door(y, x + 1); try_door(y - 1, x); try_door(y + 1, x); } if (!alloc_stairs(feat_down_stair, rand_range(4, 5), 3)) return FALSE; /* Place 1 or 2 up stairs near some walls */ if (!alloc_stairs(feat_up_stair, rand_range(1, 2), 3)) return FALSE; } if (!dun->laketype) { if (d_info[dungeon_type].stream2) { /* Hack -- Add some quartz streamers */ for (i = 0; i < DUN_STR_QUA; i++) { build_streamer(d_info[dungeon_type].stream2, DUN_STR_QC); } } if (d_info[dungeon_type].stream1) { /* Hack -- Add some magma streamers */ for (i = 0; i < DUN_STR_MAG; i++) { build_streamer(d_info[dungeon_type].stream1, DUN_STR_MC); } } } /* Special boundary walls -- Top and bottom */ for (x = 0; x < cur_wid; x++) { set_bound_perm_wall(&cave[0][x]); set_bound_perm_wall(&cave[cur_hgt - 1][x]); } /* Special boundary walls -- Left and right */ for (y = 1; y < (cur_hgt - 1); y++) { set_bound_perm_wall(&cave[y][0]); set_bound_perm_wall(&cave[y][cur_wid - 1]); } /* Determine the character location */ if (!new_player_spot()) return FALSE; /* Basic "amount" */ k = (dun_level / 3); if (k > 10) k = 10; if (k < 2) k = 2; /* Pick a base number of monsters */ i = d_info[dungeon_type].min_m_alloc_level; /* To make small levels a bit more playable */ if (cur_hgt < MAX_HGT || cur_wid < MAX_WID) { int small_tester = i; i = (i * cur_hgt) / MAX_HGT; i = (i * cur_wid) / MAX_WID; i += 1; if (i > small_tester) i = small_tester; else if (cheat_hear) { msg_format("Reduced monsters base from %d to %d", small_tester, i); } } if (dungeon_type != DUNGEON_ARENA) { i += randint1(8); /* Put some monsters in the dungeon */ for (i = (dun_level < 50 ? (i+k) : (i+k)*6/10); i > 0; i--) { (void)alloc_monster(0, PM_ALLOW_SLEEP); } } /* Place some traps in the dungeon */ alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_TRAP, randint1(k*3/2)); /* Put some rubble in corridors (except NO_CAVE dungeon (Castle)) */ if (!(d_info[dungeon_type].flags1 & DF1_NO_CAVE)) alloc_object(ALLOC_SET_CORR, ALLOC_TYP_RUBBLE, randint1(k)); if (dungeon_type != DUNGEON_ARENA) { /* Put some objects in rooms */ alloc_object(ALLOC_SET_ROOM, ALLOC_TYP_OBJECT, randnor(DUN_AMT_ROOM, 3)); /* Put some objects/gold in the dungeon */ alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_OBJECT, randnor(DUN_AMT_ITEM, 3)); alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_GOLD, randnor(DUN_AMT_GOLD, 3)); /* Experimental: Guarantee certain objects. Give surprise goodies. */ if (one_in_(2)) alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_FOOD, 1); if (dun_level <= 15) alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_LIGHT, 1); if (dun_level >= 10 && one_in_(2)) alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_RECALL, 1); if (dun_level >= 10 && one_in_(20)) alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_SKELETON, damroll(3, 5)); _mon_give_extra_drop(MFLAG2_DROP_BASIC, 1); _mon_give_extra_drop(MFLAG2_DROP_UTILITY, randint0(4)); if (dun_level > max_dlv[dungeon_type]) _mon_give_extra_drop(MFLAG2_DROP_PRIZE, 1); } /* Set back to default */ object_level = base_level; /* Put the Guardian */ if (!alloc_guardian(TRUE)) return FALSE; if (dun->empty_level && (!one_in_(DARK_EMPTY) || (randint1(100) > dun_level)) && !(d_info[dungeon_type].flags1 & DF1_DARKNESS)) { /* Lite the cave */ for (y = 0; y < cur_hgt; y++) { for (x = 0; x < cur_wid; x++) { cave[y][x].info |= (CAVE_GLOW); } } } return TRUE; }
/* * Generate various caverns and lakes * * There were moved from cave_gen(). */ static void gen_caverns_and_lakes(void) { /* Possible "destroyed" level */ if ((dun_level > 30) && one_in_(DUN_DEST*2) && (small_levels) && (d_info[dungeon_type].flags1 & DF1_DESTROY)) { dun->destroyed = TRUE; /* extra rubble around the place looks cool */ build_lake(one_in_(2) ? LAKE_T_CAVE : LAKE_T_EARTH_VAULT); } /* Make a lake some of the time */ if (one_in_(LAKE_LEVEL) && !dun->empty_level && !dun->destroyed && (d_info[dungeon_type].flags1 & DF1_LAKE_MASK)) { int count = 0; if (d_info[dungeon_type].flags1 & DF1_LAKE_WATER) count += 3; if (d_info[dungeon_type].flags1 & DF1_LAKE_LAVA) count += 3; if (d_info[dungeon_type].flags1 & DF1_LAKE_RUBBLE) count += 3; if (d_info[dungeon_type].flags1 & DF1_LAKE_TREE) count += 3; if (d_info[dungeon_type].flags1 & DF1_LAKE_LAVA) { /* Lake of Lava */ if ((dun_level > 80) && (randint0(count) < 2)) dun->laketype = LAKE_T_LAVA; count -= 2; /* Lake of Lava2 */ if (!dun->laketype && (dun_level > 80) && one_in_(count)) dun->laketype = LAKE_T_FIRE_VAULT; count--; } if ((d_info[dungeon_type].flags1 & DF1_LAKE_WATER) && !dun->laketype) { /* Lake of Water */ if ((dun_level > 50) && randint0(count) < 2) dun->laketype = LAKE_T_WATER; count -= 2; /* Lake of Water2 */ if (!dun->laketype && (dun_level > 50) && one_in_(count)) dun->laketype = LAKE_T_WATER_VAULT; count--; } if ((d_info[dungeon_type].flags1 & DF1_LAKE_RUBBLE) && !dun->laketype) { /* Lake of rubble */ if ((dun_level > 35) && (randint0(count) < 2)) dun->laketype = LAKE_T_CAVE; count -= 2; /* Lake of rubble2 */ if (!dun->laketype && (dun_level > 35) && one_in_(count)) dun->laketype = LAKE_T_EARTH_VAULT; count--; } /* Lake of tree */ if ((dun_level > 5) && (d_info[dungeon_type].flags1 & DF1_LAKE_TREE) && !dun->laketype) dun->laketype = LAKE_T_AIR_VAULT; if (dun->laketype) { if (cheat_room) msg_print("Lake on the level."); build_lake(dun->laketype); } } if ((dun_level > DUN_CAVERN) && !dun->empty_level && (d_info[dungeon_type].flags1 & DF1_CAVERN) && !dun->laketype && !dun->destroyed && (randint1(1000) < dun_level)) { dun->cavern = TRUE; /* make a large fractal cave in the middle of the dungeon */ if (cheat_room) msg_print("Cavern on level."); build_cavern(); } /* Hack -- No destroyed "quest" levels */ if (quests_get_current()) dun->destroyed = FALSE; }
/* * Allocates some objects (using "place" and "type") */ static void alloc_object(int set, int typ, int num) { int y = 0, x = 0, k; /* A small level has few objects. */ num = MAX(1, num * cur_hgt * cur_wid / (MAX_HGT*MAX_WID)); /* Diligent players should be encouraged to explore more! */ if (typ == ALLOC_TYP_OBJECT || typ == ALLOC_TYP_GOLD) num = num * (625 + virtue_current(VIRTUE_DILIGENCE)) / 625; for (k = 0; k < num; k++) { object_type forge; int k_idx; if (!_get_loc(set, &x, &y)) { if (cheat_room) msg_print("Warning! Could not place object!"); return; } switch (typ) { case ALLOC_TYP_RUBBLE: place_rubble(y, x); cave[y][x].info &= ~(CAVE_FLOOR); break; case ALLOC_TYP_TRAP: place_trap(y, x); cave[y][x].info &= ~(CAVE_FLOOR); break; case ALLOC_TYP_GOLD: place_gold(y, x); break; case ALLOC_TYP_OBJECT: /* Comment: Monsters drop objects at (ML + DL)/2. In practice, this means that your best drops are just laying on the ground, and this encourages recall scumming for end game resources such as wands of rockets. Note: Vaults are not affected and we want to encourage these! Room templates need some thought ... */ if (base_level > 31) { int n = base_level - 30; object_level = 30 + n/2 + randint1(n/2); } else object_level = base_level; /* paranoia */ place_object(y, x, 0L); object_level = base_level; break; case ALLOC_TYP_FOOD: if (prace_is_(RACE_ENT)) k_idx = lookup_kind(TV_POTION, SV_POTION_WATER); else k_idx = lookup_kind(TV_FOOD, SV_FOOD_RATION); object_prep(&forge, k_idx); obj_make_pile(&forge); drop_near(&forge, -1, y, x); break; case ALLOC_TYP_LIGHT: if (one_in_(3)) k_idx = lookup_kind(TV_FLASK, SV_FLASK_OIL); else k_idx = lookup_kind(TV_LITE, SV_LITE_LANTERN); object_prep(&forge, k_idx); apply_magic(&forge, dun_level, 0); obj_make_pile(&forge); drop_near(&forge, -1, y, x); break; case ALLOC_TYP_RECALL: k_idx = lookup_kind(TV_SCROLL, SV_SCROLL_WORD_OF_RECALL); object_prep(&forge, k_idx); /*obj_make_pile(&forge);*/ drop_near(&forge, -1, y, x); break; case ALLOC_TYP_SKELETON: k_idx = lookup_kind(TV_CORPSE, SV_SKELETON); object_prep(&forge, k_idx); apply_magic(&forge, dun_level, 0); drop_near(&forge, -1, y, x); break; } } }
/** * This function takes a grid location (x, y) and extracts information the * player is allowed to know about it, filling in the grid_data structure * passed in 'g'. * * The information filled in is as follows: * - g->f_idx is filled in with the terrain's feature type, or FEAT_NONE * if the player doesn't know anything about the grid. The function * makes use of the "mimic" field in terrain in order to allow one * feature to look like another (hiding secret doors, invisible traps, * etc). This will return the terrain type the player "Knows" about, * not necessarily the real terrain. * - g->m_idx is set to the monster index, or 0 if there is none (or the * player doesn't know it). * - g->first_kind is set to the object_kind of the first object in a grid * that the player knows about, or NULL for no objects. * - g->muliple_objects is true if there is more than one object in the * grid that the player knows and cares about (to facilitate any special * floor stack symbol that might be used). * - g->in_view is true if the player can currently see the grid - this can * be used to indicate field-of-view, such as through the * OPT(player, view_bright_light) option. * - g->lighting is set to indicate the lighting level for the grid: * LIGHTING_DARK for unlit grids, LIGHTING_LIT for inherently light * grids (lit rooms, etc), LIGHTING_TORCH for grids lit by the player's * light source, and LIGHTING_LOS for grids in the player's line of sight. * Note that lighting is always LIGHTING_LIT for known "interesting" grids * like walls. * - g->is_player is true if the player is on the given grid. * - g->hallucinate is true if the player is hallucinating something "strange" * for this grid - this should pick a random monster to show if the m_idx * is non-zero, and a random object if first_kind is non-zero. * * NOTES: * This is called pretty frequently, whenever a grid on the map display * needs updating, so don't overcomplicate it. * * Terrain is remembered separately from objects and monsters, so can be * shown even when the player can't "see" it. This leads to things like * doors out of the player's view still change from closed to open and so on. * * TODO: * Hallucination is currently disabled (it was a display-level hack before, * and we need it to be a knowledge-level hack). The idea is that objects * may turn into different objects, monsters into different monsters, and * terrain may be objects, monsters, or stay the same. */ void map_info(unsigned y, unsigned x, struct grid_data *g) { struct object *obj; assert(x < (unsigned) cave->width); assert(y < (unsigned) cave->height); /* Default "clear" values, others will be set later where appropriate. */ g->first_kind = NULL; g->trap = NULL; g->multiple_objects = false; g->lighting = LIGHTING_DARK; g->unseen_object = false; g->unseen_money = false; /* Use real feature (remove later) */ g->f_idx = cave->squares[y][x].feat; if (f_info[g->f_idx].mimic) g->f_idx = lookup_feat(f_info[g->f_idx].mimic); g->in_view = (square_isseen(cave, y, x)) ? true : false; g->is_player = (cave->squares[y][x].mon < 0) ? true : false; g->m_idx = (g->is_player) ? 0 : cave->squares[y][x].mon; g->hallucinate = player->timed[TMD_IMAGE] ? true : false; if (g->in_view) { g->lighting = LIGHTING_LOS; if (!square_isglow(cave, y, x) && OPT(player, view_yellow_light)) g->lighting = LIGHTING_TORCH; /* Remember seen feature */ square_memorize(cave, y, x); } else if (!square_isknown(cave, y, x)) { g->f_idx = FEAT_NONE; } else if (square_isglow(cave, y, x)) { g->lighting = LIGHTING_LIT; } /* Use known feature */ g->f_idx = player->cave->squares[y][x].feat; if (f_info[g->f_idx].mimic) g->f_idx = lookup_feat(f_info[g->f_idx].mimic); /* There is a trap in this square */ if (square_istrap(cave, y, x) && square_isknown(cave, y, x)) { struct trap *trap = cave->squares[y][x].trap; /* Scan the square trap list */ while (trap) { if (trf_has(trap->flags, TRF_TRAP) || trf_has(trap->flags, TRF_RUNE)) { /* Accept the trap - only if not disabled, maybe we need * a special graphic for this */ if (!trap->timeout) { g->trap = trap; break; } } trap = trap->next; } } /* Objects */ for (obj = square_object(player->cave, y, x); obj; obj = obj->next) { if (obj->kind == unknown_gold_kind) { g->unseen_money = true; } else if (obj->kind == unknown_item_kind) { g->unseen_object = true; } else if (ignore_known_item_ok(obj)) { /* Item stays hidden */ } else if (!g->first_kind) { g->first_kind = obj->kind; } else { g->multiple_objects = true; break; } } /* Monsters */ if (g->m_idx > 0) { /* If the monster isn't "visible", make sure we don't list it.*/ struct monster *mon = cave_monster(cave, g->m_idx); if (!monster_is_visible(mon)) g->m_idx = 0; } /* Rare random hallucination on non-outer walls */ if (g->hallucinate && g->m_idx == 0 && g->first_kind == 0) { if (one_in_(128) && (int) g->f_idx != FEAT_PERM) g->m_idx = 1; else if (one_in_(128) && (int) g->f_idx != FEAT_PERM) /* if hallucinating, we just need first_kind to not be NULL */ g->first_kind = k_info; else g->hallucinate = false; } assert((int) g->f_idx <= FEAT_PASS_RUBBLE); if (!g->hallucinate) assert((int)g->m_idx < cave->mon_max); /* All other g fields are 'flags', mostly booleans. */ }
static void cmd_racial_power_aux(const mutation_type *mut_ptr) { s16b plev = p_ptr->lev; int dir = 0; if (racial_aux(mut_ptr->level, mut_ptr->cost, mut_ptr->stat, mut_ptr->diff)) { switch (p_ptr->prace) { case RACE_DWARF: { msg_print("You examine your surroundings."); (void)detect_traps(); (void)detect_doors(); (void)detect_stairs(); break; } case RACE_HOBBIT: { object_type *q_ptr; object_type forge; /* Get local object */ q_ptr = &forge; /* Create the food ration */ object_prep(q_ptr, 21); /* Drop the object from heaven */ (void)drop_near(q_ptr, -1, p_ptr->py, p_ptr->px); msg_print("You cook some food."); break; } case RACE_GNOME: { msg_print("Blink!"); teleport_player(10 + plev); break; } case RACE_HALF_ORC: { msg_print("You play tough."); (void)set_afraid(0); break; } case RACE_HALF_TROLL: { msg_print("RAAAGH!"); if (!p_ptr->shero) { (void)hp_player(30); } (void)set_afraid(0); (void)set_shero(p_ptr->shero + 10 + randint1(plev)); break; } case RACE_AMBERITE: { /* Hack - use levels to choose ability */ if (mut_ptr->level == 30) { msg_print("You picture the Pattern in your mind and walk it..."); (void)set_poisoned(0); (void)set_image(0); (void)set_stun(0); (void)set_cut(0); (void)set_blind(0); (void)set_afraid(0); (void)do_res_stat(A_STR, 200); (void)do_res_stat(A_INT, 200); (void)do_res_stat(A_WIS, 200); (void)do_res_stat(A_DEX, 200); (void)do_res_stat(A_CON, 200); (void)do_res_stat(A_CHR, 200); (void)restore_level(); } else if (mut_ptr->level == 40) { /* No effect in arena or quest */ if (p_ptr->inside_quest) { msg_print("There is no effect."); } else { msg_print("You start walking around. Your surroundings change."); if (autosave_l) do_cmd_save_game(TRUE); /* Leaving */ p_ptr->leaving = TRUE; } } break; } case RACE_BARBARIAN: { msg_print("Raaagh!"); if (!p_ptr->shero) { (void)hp_player(30); } (void)set_afraid(0); (void)set_shero(p_ptr->shero + 10 + randint1(plev)); break; } case RACE_HALF_OGRE: { msg_print("You carefully set an explosive rune..."); (void)explosive_rune(); break; } case RACE_HALF_GIANT: { if (!get_aim_dir(&dir)) break; msg_print("You bash at a stone wall."); (void)wall_to_mud(dir); break; } case RACE_HALF_TITAN: { msg_print("You examine your foes..."); (void)probing(); break; } case RACE_CYCLOPS: { if (!get_aim_dir(&dir)) break; msg_print("You throw a huge boulder."); (void)fire_bolt(GF_MISSILE, dir, (3 * plev) / 2); break; } case RACE_YEEK: { if (!get_aim_dir(&dir)) break; msg_print("You make a horrible scream!"); (void)fear_monster(dir, plev); break; } case RACE_KLACKON: { if (!get_aim_dir(&dir)) break; msg_print("You spit acid."); if (plev < 25) (void)fire_bolt(GF_ACID, dir, plev); else (void)fire_ball(GF_ACID, dir, plev, 2); break; } case RACE_KOBOLD: { if (!get_aim_dir(&dir)) break; msg_print("You throw a dart of poison."); (void)fire_bolt(GF_POIS, dir, plev); break; } case RACE_NIBELUNG: { msg_print("You examine your surroundings."); (void)detect_traps(); (void)detect_doors(); (void)detect_stairs(); break; } case RACE_DARK_ELF: { if (!get_aim_dir(&dir)) break; msg_print("You cast a magic missile."); (void)fire_bolt_or_beam(10, GF_MISSILE, dir, damroll(3 + ((plev - 1) / 5), 4)); break; } case RACE_DRACONIAN: { int Type = (one_in_(3) ? GF_COLD : GF_FIRE); cptr Type_desc = ((Type == GF_COLD) ? "cold" : "fire"); if (randint1(100) < plev) { switch (p_ptr->pclass) { case CLASS_WARRIOR: case CLASS_RANGER: if (one_in_(3)) { Type = GF_MISSILE; Type_desc = "the elements"; } else { Type = GF_SHARDS; Type_desc = "shards"; } break; case CLASS_MAGE: case CLASS_WARRIOR_MAGE: case CLASS_HIGH_MAGE: if (one_in_(3)) { Type = GF_MANA; Type_desc = "mana"; } else { Type = GF_DISENCHANT; Type_desc = "disenchantment"; } break; case CLASS_CHAOS_WARRIOR: if (!one_in_(3)) { Type = GF_CONFUSION; Type_desc = "confusion"; } else { Type = GF_CHAOS; Type_desc = "chaos"; } break; case CLASS_MONK: if (!one_in_(3)) { Type = GF_CONFUSION; Type_desc = "confusion"; } else { Type = GF_SOUND; Type_desc = "sound"; } break; case CLASS_MINDCRAFTER: if (!one_in_(3)) { Type = GF_CONFUSION; Type_desc = "confusion"; } else { Type = GF_PSI; Type_desc = "mental energy"; } break; case CLASS_PRIEST: case CLASS_PALADIN: if (one_in_(3)) { Type = GF_HELL_FIRE; Type_desc = "hellfire"; } else { Type = GF_HOLY_FIRE; Type_desc = "holy fire"; } break; case CLASS_ROGUE: if (one_in_(3)) { Type = GF_DARK; Type_desc = "darkness"; } else { Type = GF_POIS; Type_desc = "poison"; } break; } } if (!get_aim_dir(&dir)) break; msg_format("You breathe %s.", Type_desc); (void)fire_ball(Type, dir, plev * 2, (plev / 15) + 1); break; } case RACE_MIND_FLAYER: { if (!get_aim_dir(&dir)) break; else { msg_print("You concentrate and your eyes glow red..."); (void)fire_bolt(GF_PSI, dir, plev); } break; } case RACE_IMP: { if (!get_aim_dir(&dir)) break; if (plev >= 30) { msg_print("You cast a ball of fire."); (void)fire_ball(GF_FIRE, dir, plev, 2); } else { msg_print("You cast a bolt of fire."); (void)fire_bolt(GF_FIRE, dir, plev); } break; } case RACE_GOLEM: { (void)set_shield(p_ptr->shield + rand_range(30, 50)); break; } case RACE_SKELETON: case RACE_ZOMBIE: { msg_print("You attempt to restore your lost energies."); (void)restore_level(); break; } case RACE_VAMPIRE: { int y, x, dummy; cave_type *c_ptr; /* Only works on adjacent monsters */ if (!get_rep_dir(&dir)) break; y = p_ptr->py + ddy[dir]; x = p_ptr->px + ddx[dir]; /* Paranoia */ if (!in_bounds2(y, x)) break; c_ptr = area(y, x); if (!c_ptr->m_idx) { msg_print("You bite into thin air!"); break; } msg_print("You grin and bare your fangs..."); dummy = plev + randint1(plev) * MAX(1, plev / 10); /* Dmg */ if (drain_gain_life(dir, dummy)) { /* Gain nutritional sustenance: 150/hp drained */ /* A Food ration gives 5000 food points (by contrast) */ /* Don't ever get more than "Full" this way */ /* But if we ARE Gorged, it won't cure us */ dummy = p_ptr->food + MIN(5000, 100 * dummy); if (p_ptr->food < PY_FOOD_MAX) /* Not gorged already */ (void)set_food(dummy >= PY_FOOD_MAX ? PY_FOOD_MAX - 1 : dummy); } else msg_print("Yechh. That tastes foul."); break; } case RACE_SPECTRE: { msg_print("You emit an eldritch howl!"); if (!get_aim_dir(&dir)) break; (void)fear_monster(dir, plev); break; } case RACE_SPRITE: { msg_print("You throw some magic dust..."); if (plev < 25) (void)sleep_monsters_touch(); else (void)sleep_monsters(); break; } case RACE_GHOUL: { if (mut_ptr->level == 30) { /* Sense living */ (void)detect_monsters_living(); } else { eat_corpse(); } break; } default: msg_print("This race has no bonus power."); p_ptr->energy_use = 0; } } /* Redraw mana and hp */ p_ptr->redraw |= (PR_HP | PR_MANA); /* Window stuff */ p_ptr->window |= (PW_PLAYER | PW_SPELL); }
/* * Move player in the given direction. * * This routine should only be called when energy has been expended. * * Note that this routine handles monsters in the destination grid, * and also handles attempting to move into walls/doors/rubble/etc. */ void move_player(int dir, bool disarm) { int py = p_ptr->py; int px = p_ptr->px; int y = py + ddy[dir]; int x = px + ddx[dir]; /* Attack monsters */ if (cave->m_idx[y][x] > 0) py_attack(y, x); /* Optionally alter known traps/doors on movement */ else if (disarm && (cave->info[y][x] & CAVE_MARK) && (cave->feat[y][x] >= FEAT_TRAP_HEAD) && (cave->feat[y][x] <= FEAT_DOOR_TAIL)) { /* Auto-repeat if not already repeating */ if (cmd_get_nrepeats() == 0) cmd_set_repeat(99); do_cmd_alter_aux(dir); } /* Cannot walk through walls */ else if (!cave_floor_bold(y, x)) { /* Disturb the player */ disturb(0, 0); /* Notice unknown obstacles */ if (!(cave->info[y][x] & CAVE_MARK)) { /* Rubble */ if (cave->feat[y][x] == FEAT_RUBBLE) { msgt(MSG_HITWALL, "You feel a pile of rubble blocking your way."); cave->info[y][x] |= (CAVE_MARK); cave_light_spot(cave, y, x); } /* Closed door */ else if (cave->feat[y][x] < FEAT_SECRET) { msgt(MSG_HITWALL, "You feel a door blocking your way."); cave->info[y][x] |= (CAVE_MARK); cave_light_spot(cave, y, x); } /* Wall (or secret door) */ else { msgt(MSG_HITWALL, "You feel a wall blocking your way."); cave->info[y][x] |= (CAVE_MARK); cave_light_spot(cave, y, x); } } /* Mention known obstacles */ else { if (cave->feat[y][x] == FEAT_RUBBLE) msgt(MSG_HITWALL, "There is a pile of rubble blocking your way."); else if (cave->feat[y][x] < FEAT_SECRET) msgt(MSG_HITWALL, "There is a door blocking your way."); else msgt(MSG_HITWALL, "There is a wall blocking your way."); } } /* Normal movement */ else { /* See if trap detection status will change */ bool old_dtrap = ((cave->info2[py][px] & (CAVE2_DTRAP)) != 0); bool new_dtrap = ((cave->info2[y][x] & (CAVE2_DTRAP)) != 0); /* Note the change in the detect status */ if (old_dtrap != new_dtrap) p_ptr->redraw |= (PR_DTRAP); /* Disturb player if the player is about to leave the area */ if (OPT(disturb_detect) && p_ptr->running && !p_ptr->running_firststep && old_dtrap && !new_dtrap) { disturb(0, 0); return; } /* Move player */ monster_swap(py, px, y, x); /* New location */ y = py = p_ptr->py; x = px = p_ptr->px; /* Searching */ if (p_ptr->searching || (p_ptr->state.skills[SKILL_SEARCH_FREQUENCY] >= 50) || one_in_(50 - p_ptr->state.skills[SKILL_SEARCH_FREQUENCY])) search(FALSE); /* Handle "store doors" */ if ((cave->feat[p_ptr->py][p_ptr->px] >= FEAT_SHOP_HEAD) && (cave->feat[p_ptr->py][p_ptr->px] <= FEAT_SHOP_TAIL)) { /* Disturb */ disturb(0, 0); cmd_insert(CMD_ENTER_STORE); } /* All other grids (including traps) */ else { /* Handle objects (later) */ p_ptr->notice |= (PN_PICKUP); } /* Discover invisible traps */ if (cave->feat[y][x] == FEAT_INVIS) { /* Disturb */ disturb(0, 0); /* Message */ msg("You found a trap!"); /* Pick a trap */ pick_trap(y, x); /* Hit the trap */ hit_trap(y, x); } /* Set off an visible trap */ else if ((cave->feat[y][x] >= FEAT_TRAP_HEAD) && (cave->feat[y][x] <= FEAT_TRAP_TAIL)) { /* Disturb */ disturb(0, 0); /* Hit the trap */ hit_trap(y, x); } } p_ptr->running_firststep = FALSE; }
void prise_silmaril(void) { object_type *o_ptr; object_type *w_ptr; artefact_type *a_ptr; object_type object_type_body; cptr freed_msg = NULL; // default to soothe compiler warnings bool freed = FALSE; int slot = 0; int dam = 0; int prt = 0; int net_dam = 0; int prt_percent = 0; int hit_result = 0; int crit_bonus_dice = 0; int pd = 0; int noise = 0; u32b dummy_noticed_flag; int mds = p_ptr->mds; int attack_mod = p_ptr->skill_use[S_MEL]; char o_name[80]; // the Crown is on the ground o_ptr = &o_list[cave_o_idx[p_ptr->py][p_ptr->px]]; switch (o_ptr->name1) { case ART_MORGOTH_3: { pd = 15; noise = 5; freed_msg = "You have freed a Silmaril!"; break; } case ART_MORGOTH_2: { pd = 25; noise = 10; if (p_ptr->crown_shatter) freed_msg = "The fates be damned! You free a second Silmaril."; else freed_msg = "You free a second Silmaril."; break; } case ART_MORGOTH_1: { pd = 30; noise = 15; freed_msg = "You free the final Silmaril. You have a very bad feeling about this."; msg_print("Looking into the hallowed light of the final Silmaril, you are filled with a strange dread."); if (!get_check("Are you sure you wish to proceed? ")) return; break; } } /* Get the weapon */ w_ptr = &inventory[INVEN_WIELD]; // undo rapid attack penalties if (p_ptr->active_ability[S_MEL][MEL_RAPID_ATTACK]) { // undo strength adjustment to the attack mds = total_mds(w_ptr, 0); // undo the dexterity adjustment to the attack attack_mod += 3; } /* Test for hit */ hit_result = hit_roll(attack_mod, 0, PLAYER, NULL, TRUE); /* Make some noise */ stealth_score -= noise; // Determine damage if (hit_result > 0) { crit_bonus_dice = crit_bonus(hit_result, w_ptr->weight, &r_info[R_IDX_MORGOTH], S_MEL, FALSE); dam = damroll(p_ptr->mdd + crit_bonus_dice, mds); prt = damroll(pd, 4); prt_percent = prt_after_sharpness(w_ptr, &dummy_noticed_flag); prt = (prt * prt_percent) / 100; net_dam = dam - prt; /* No negative damage */ if (net_dam < 0) net_dam = 0; //update_combat_rolls1b(PLAYER, TRUE); update_combat_rolls2(p_ptr->mdd + crit_bonus_dice, mds, dam, pd, 4, prt, prt_percent, GF_HURT, TRUE); } // if you succeed in prising out a Silmaril... if (net_dam > 0) { freed = TRUE; switch (o_ptr->name1) { case ART_MORGOTH_3: { break; } case ART_MORGOTH_2: { if (!p_ptr->crown_shatter && one_in_(2)) { shatter_weapon(2); freed = FALSE; } break; } case ART_MORGOTH_1: { if (!p_ptr->crown_shatter) { shatter_weapon(3); freed = FALSE; } else { p_ptr->cursed = TRUE; } break; } } if (freed) { // change its type to that of the crown with one less silmaril o_ptr->name1--; // get the details of this new crown a_ptr = &a_info[o_ptr->name1]; // modify the existing crown object_into_artefact(o_ptr, a_ptr); // report success msg_print(freed_msg); // Get new local object o_ptr = &object_type_body; // Make Silmaril object_prep(o_ptr, lookup_kind(TV_LIGHT, SV_LIGHT_SILMARIL)); // Get it slot = inven_carry(o_ptr); /* Get the object again */ o_ptr = &inventory[slot]; /* Describe the object */ object_desc(o_name, sizeof(o_name), o_ptr, TRUE, 3); /* Message */ msg_format("You have %s (%c).", o_name, index_to_label(slot)); // Break the truce (always) break_truce(TRUE); // add a note to the notes file do_cmd_note("Cut a Silmaril from Morgoth's crown", p_ptr->depth); } } // if you fail to prise out a Silmaril... else { msg_print("Try though you might, you were unable to free a Silmaril."); msg_print("Perhaps you should try again or use a different weapon."); if (pd == 15) msg_print("(The combat rolls window shows what is happening.)"); // Break the truce if creatures see break_truce(FALSE); } // check for taking of final Silmaril if ((pd == 30) && freed) { msg_print("Until you escape you must now roll twice for every skill check, taking the worse result each time."); msg_print("You hear a cry of veangance echo through the iron hells."); wake_all_monsters(0); } }
void mimic_race(int new_race) { int old_race = p_ptr->mimic_form; if (p_ptr->prace != RACE_DOPPELGANGER) return; if (p_ptr->tim_mimic) return; if (new_race == old_race) return; if (old_race == RACE_HUMAN || old_race == RACE_DEMIGOD) { int i, idx; for (i = 0; i < MAX_DEMIGOD_POWERS; i++) { idx = p_ptr->demigod_power[i]; if (idx >= 0) { mut_unlock(idx); mut_lose(idx); /* Lose the mutation, but not the choice! p_ptr->demigod_power[i] = -1; */ } } } /* Shifting form causes mutations to vanish! */ mut_lose_all(); if (new_race == MIMIC_NONE) msg_print("You resume your true form."); else { race_t *race_ptr = get_race_t_aux(new_race, 0); if (is_a_vowel(race_ptr->name[0])) msg_format("You turn into an %s!", race_ptr->name); else msg_format("You turn into a %s!", race_ptr->name); } p_ptr->mimic_form = new_race; p_ptr->expfact = calc_exp_factor(); check_experience(); if (new_race == RACE_HUMAN || new_race == RACE_DEMIGOD) { get_race_t()->gain_level(p_ptr->lev); /* This is OK ... Just make sure we get to choose racial powers on mimicry */ } if (new_race == RACE_BEASTMAN) { int i; mut_gain_random(mut_good_pred); for (i = 2; i <= p_ptr->lev; i++) { if (one_in_(5)) mut_gain_random(NULL); } } p_ptr->redraw |= (PR_BASIC | PR_STATUS | PR_MAP); p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA); equip_on_change_race(); reset_visuals(); handle_stuff(); }
/** * Chooses a monster race that seems "appropriate" to the given level * * This function uses the "prob2" field of the "monster allocation table", * and various local information, to calculate the "prob3" field of the * same table, which is then used to choose an "appropriate" monster, in * a relatively efficient manner. * * Note that "town" monsters will *only* be created in the town, and * "normal" monsters will *never* be created in the town, unless the * "level" is "modified", for example, by polymorph or summoning. * * There is a small chance (1/50) of "boosting" the given depth by * a small amount (up to four levels), except in the town. * * It is (slightly) more likely to acquire a monster of the given level * than one of a lower level. This is done by choosing several monsters * appropriate to the given level and keeping the "hardest" one. * * Note that if no monsters are "appropriate", then this function will * fail, and return zero, but this should *almost* never happen. */ monster_race *get_mon_num(int level) { int i, p; long total; monster_race *race; alloc_entry *table = alloc_race_table; /* Occasionally produce a nastier monster in the dungeon */ if (level > 0 && one_in_(NASTY_MON)) level += MIN(level / 4 + 2, MON_OOD_MAX); total = 0L; /* Process probabilities */ for (i = 0; i < alloc_race_size; i++) { /* Monsters are sorted by depth */ if (table[i].level > level) break; /* Default */ table[i].prob3 = 0; /* No town monsters in dungeon */ if ((level > 0) && (table[i].level <= 0)) continue; /* Get the chosen monster */ race = &r_info[table[i].index]; /* Only one copy of a a unique must be around at the same time */ if (rf_has(race->flags, RF_UNIQUE) && race->cur_num >= race->max_num) continue; /* Some monsters never appear out of depth */ if (rf_has(race->flags, RF_FORCE_DEPTH) && race->level > p_ptr->depth) continue; /* Accept */ table[i].prob3 = table[i].prob2; /* Total */ total += table[i].prob3; } /* No legal monsters */ if (total <= 0) return NULL; /* Pick a monster */ race = get_mon_race_aux(total, table); /* Try for a "harder" monster once (50%) or twice (10%) */ p = randint0(100); if (p < 60) { monster_race *old = race; /* Pick a new monster */ race = get_mon_race_aux(total, table); /* Keep the deepest one */ if (race->level < old->level) race = old; } /* Try for a "harder" monster twice (10%) */ if (p < 10) { monster_race *old = race; /* Pick a monster */ race = get_mon_race_aux(total, table); /* Keep the deepest one */ if (race->level < old->level) race = old; } /* Result */ return race; }
/* * Apply magic to an item known to be a "weapon" * * Hack -- note special base damage dice boosting * Hack -- note special processing for weapon/digger * Hack -- note special rating boost for dragon scale mail */ static void a_m_aux_1(object_type *o_ptr, int level, int power) { int tohit1 = randint1(5) + m_bonus(5, level); int tohit2 = m_bonus(10, level); int todam1 = randint1(5) + m_bonus(5, level); int todam2 = m_bonus(10, level); switch (power) { case -2: o_ptr->to_h -= tohit2; o_ptr->to_d -= todam2; case -1: o_ptr->to_h -= tohit1; o_ptr->to_d -= todam1; break; case 2: o_ptr->to_h += tohit2; o_ptr->to_d += todam2; case 1: o_ptr->to_h += tohit1; o_ptr->to_d += todam1; break; } /* Analyze type */ switch (o_ptr->tval) { case TV_DIGGING: { /* Very bad */ if (power < -1) { /* Hack -- Horrible digging bonus */ o_ptr->pval = 0 - (5 + randint1(5)); } /* Bad */ else if (power < 0) { /* Hack -- Reverse digging bonus */ o_ptr->pval = -o_ptr->pval; } break; } case TV_HAFTED: case TV_POLEARM: case TV_SWORD: { /* Very Good */ if (power > 1) { /* Hack -- Super-charge the damage dice */ while ((o_ptr->dd * o_ptr->ds > 0) && one_in_(10L * o_ptr->dd * o_ptr->ds)) { o_ptr->dd++; } /* Hack -- Lower the damage dice */ if (o_ptr->dd > 9) o_ptr->dd = 9; } break; } case TV_BOLT: case TV_ARROW: case TV_SHOT: { /* Very good */ if (power > 1) { /* Hack -- super-charge the damage dice */ while ((o_ptr->dd * o_ptr->ds > 0) && one_in_(10L * o_ptr->dd * o_ptr->ds)) { o_ptr->dd++; } /* Hack -- restrict the damage dice */ if (o_ptr->dd > 9) o_ptr->dd = 9; } break; } } }
/** * Attempts to place a copy of the given monster at the given position in * the dungeon. * * All of the monster placement routines eventually call this function. This * is what actually puts the monster in the dungeon (i.e., it notifies the cave * and sets the monsters position). The dungeon loading code also calls this * function directly. * * `origin` is the item origin to use for any monster drops (e.g. ORIGIN_DROP, * ORIGIN_DROP_PIT, etc.) The dungeon loading code calls this with origin = 0, * which prevents the monster's drops from being generated again. * * Returns the m_idx of the newly copied monster, or 0 if the placement fails. */ s16b place_monster(int y, int x, monster_type *mon, byte origin) { s16b m_idx; monster_type *m_ptr; assert(cave_in_bounds(cave, y, x)); assert(!cave_monster_at(cave, y, x)); /* Get a new record */ m_idx = mon_pop(); if (!m_idx) return 0; /* Copy the monster */ m_ptr = cave_monster(cave, m_idx); COPY(m_ptr, mon, monster_type); /* Set the ID */ m_ptr->midx = m_idx; /* Set the location */ cave->m_idx[y][x] = m_ptr->midx; m_ptr->fy = y; m_ptr->fx = x; assert(cave_monster_at(cave, y, x) == m_ptr); update_mon(m_ptr, TRUE); /* Hack -- Count the number of "reproducers" */ if (rf_has(m_ptr->race->flags, RF_MULTIPLY)) num_repro++; /* Count racial occurrences */ m_ptr->race->cur_num++; /* Create the monster's drop, if any */ if (origin) (void)mon_create_drop(m_ptr, origin); /* Make mimics start mimicking */ if (origin && m_ptr->race->mimic_kinds) { object_type *i_ptr; object_type object_type_body; object_kind *kind = m_ptr->race->mimic_kinds->kind; struct monster_mimic *mimic_kind; int i = 1; /* Pick a random object kind to mimic */ for (mimic_kind = m_ptr->race->mimic_kinds; mimic_kind; mimic_kind = mimic_kind->next, i++) { if (one_in_(i)) kind = mimic_kind->kind; } i_ptr = &object_type_body; if (kind->tval == TV_GOLD) { make_gold(i_ptr, p_ptr->depth, kind->sval); } else { object_prep(i_ptr, kind, m_ptr->race->level, RANDOMISE); apply_magic(i_ptr, m_ptr->race->level, TRUE, FALSE, FALSE, FALSE); i_ptr->number = 1; } i_ptr->origin = origin; i_ptr->mimicking_m_idx = m_idx; m_ptr->mimicked_o_idx = floor_carry(cave, y, x, i_ptr); } /* Result */ return m_idx; }
/** * Acid has hit the player, attempt to affect some armor. * * Note that the "base armor" of an object never changes. * If any armor is damaged (or resists), the player takes less damage. */ int minus_ac(struct player *p) { int i, count = 0; object_type *obj = NULL; char o_name[80]; /* Avoid crash during monster power calculations */ if (!p->gear) return FALSE; /* Count the armor slots */ for (i = 0; i < player->body.count; i++) { /* Ignore non-armor */ if (slot_type_is(i, EQUIP_WEAPON)) continue; if (slot_type_is(i, EQUIP_BOW)) continue; if (slot_type_is(i, EQUIP_RING)) continue; if (slot_type_is(i, EQUIP_AMULET)) continue; if (slot_type_is(i, EQUIP_LIGHT)) continue; /* Add */ count++; } /* Pick one at random */ for (i = player->body.count - 1; i >= 0; i--) { /* Ignore non-armor */ if (slot_type_is(i, EQUIP_WEAPON)) continue; if (slot_type_is(i, EQUIP_BOW)) continue; if (slot_type_is(i, EQUIP_RING)) continue; if (slot_type_is(i, EQUIP_AMULET)) continue; if (slot_type_is(i, EQUIP_LIGHT)) continue; if (one_in_(count--)) break; } /* Get the item */ obj = slot_object(player, i); /* Nothing to damage */ if (!obj) return (FALSE); /* No damage left to be done */ if (obj->ac + obj->to_a <= 0) return (FALSE); /* Describe */ object_desc(o_name, sizeof(o_name), obj, ODESC_BASE); /* Object resists */ if (obj->el_info[ELEM_ACID].flags & EL_INFO_IGNORE) { msg("Your %s is unaffected!", o_name); return (TRUE); } /* Message */ msg("Your %s is damaged!", o_name); /* Damage the item */ obj->to_a--; p->upkeep->update |= (PU_BONUS); p->upkeep->redraw |= (PR_EQUIP); /* Item was damaged */ return (TRUE); }
static void _necro_do_summon(int what, int num, bool fail) { int x = px; int y = py; if (fail) /* Failing spells should not be insta-death ... */ num = MAX(1, num/4); else num = spell_power(num); if (!fail && use_old_target && target_okay() && los(py, px, target_row, target_col) && !one_in_(3)) { y = target_row; x = target_col; } if (trump_summoning(num, !fail, y, x, 0, what, PM_ALLOW_UNIQUE)) { if (fail) { if (num == 1) msg_print("The summoned monster gets angry!"); else msg_print("The summoned monsters get angry!"); } } }
/** * Chooses a monster race that seems "appropriate" to the given level * * This function uses the "prob2" field of the "monster allocation table", * and various local information, to calculate the "prob3" field of the * same table, which is then used to choose an "appropriate" monster, in * a relatively efficient manner. * * Note that "town" monsters will *only* be created in the town, and * "normal" monsters will *never* be created in the town, unless the * "level" is "modified", for example, by polymorph or summoning. * * There is a small chance (1/50) of "boosting" the given depth by * a small amount (up to four levels), except in the town. * * It is (slightly) more likely to acquire a monster of the given level * than one of a lower level. This is done by choosing several monsters * appropriate to the given level and keeping the "hardest" one. * * Note that if no monsters are "appropriate", then this function will * fail, and return zero, but this should *almost* never happen. */ struct monster_race *get_mon_num(int level) { int i, p; long total; struct monster_race *race; alloc_entry *table = alloc_race_table; /* Occasionally produce a nastier monster in the dungeon */ if (level > 0 && one_in_(z_info->ood_monster_chance)) level += MIN(level / 4 + 2, z_info->ood_monster_amount); total = 0L; /* Process probabilities */ for (i = 0; i < alloc_race_size; i++) { time_t cur_time = time(NULL); struct tm *date = localtime(&cur_time); /* Monsters are sorted by depth */ if (table[i].level > level) break; /* Default */ table[i].prob3 = 0; /* No town monsters in dungeon */ if ((level > 0) && (table[i].level <= 0)) continue; /* Get the chosen monster */ race = &r_info[table[i].index]; /* No seasonal monsters outside of Christmas */ if (rf_has(race->flags, RF_SEASONAL) && !(date->tm_mon == 11 && date->tm_mday >= 24 && date->tm_mday <= 26)) continue; /* Only one copy of a a unique must be around at the same time */ if (rf_has(race->flags, RF_UNIQUE) && race->cur_num >= race->max_num) continue; /* Some monsters never appear out of depth */ if (rf_has(race->flags, RF_FORCE_DEPTH) && race->level > player->depth) continue; /* Accept */ table[i].prob3 = table[i].prob2; /* Total */ total += table[i].prob3; } /* No legal monsters */ if (total <= 0) return NULL; /* Pick a monster */ race = get_mon_race_aux(total, table); /* Try for a "harder" monster once (50%) or twice (10%) */ p = randint0(100); if (p < 60) { struct monster_race *old = race; /* Pick a new monster */ race = get_mon_race_aux(total, table); /* Keep the deepest one */ if (race->level < old->level) race = old; } /* Try for a "harder" monster twice (10%) */ if (p < 10) { struct monster_race *old = race; /* Pick a monster */ race = get_mon_race_aux(total, table); /* Keep the deepest one */ if (race->level < old->level) race = old; } /* Result */ return race; }
/** * Handle things that need updating once every 10 game turns */ void process_world(struct chunk *c) { int i, y, x; /* Compact the monster list if we're approaching the limit */ if (cave_monster_count(cave) + 32 > z_info->level_monster_max) compact_monsters(64); /* Too many holes in the monster list - compress */ if (cave_monster_count(cave) + 32 < cave_monster_max(cave)) compact_monsters(0); /*** Check the Time ***/ /* Play an ambient sound at regular intervals. */ if (!(turn % ((10L * z_info->day_length) / 4))) play_ambient_sound(); /*** Handle stores and sunshine ***/ if (!player->depth) { /* Daybreak/Nighfall in town */ if (!(turn % ((10L * z_info->day_length) / 2))) { bool dawn; /* Check for dawn */ dawn = (!(turn % (10L * z_info->day_length))); /* Day breaks */ if (dawn) msg("The sun has risen."); /* Night falls */ else msg("The sun has fallen."); /* Illuminate */ cave_illuminate(c, dawn); } } else { /* Update the stores once a day (while in the dungeon). The changes are not actually made until return to town, to avoid giving details away in the knowledge menu. */ if (!(turn % (10L * z_info->store_turns))) daycount++; } /* Check for creature generation */ if (one_in_(z_info->alloc_monster_chance)) (void)pick_and_place_distant_monster(cave, loc(player->px, player->py), z_info->max_sight + 5, true, player->depth); /*** Damage over Time ***/ /* Take damage from poison */ if (player->timed[TMD_POISONED]) take_hit(player, 1, "poison"); /* Take damage from cuts */ if (player->timed[TMD_CUT]) { /* Mortal wound or Deep Gash */ if (player->timed[TMD_CUT] > TMD_CUT_SEVERE) i = 3; /* Severe cut */ else if (player->timed[TMD_CUT] > TMD_CUT_NASTY) i = 2; /* Other cuts */ else i = 1; /* Take damage */ take_hit(player, i, "a fatal wound"); } /*** Check the Food, and Regenerate ***/ /* Digest normally */ if (!(turn % 100)) { /* Basic digestion rate based on speed */ i = turn_energy(player->state.speed) * 2; /* Regeneration takes more food */ if (player_of_has(player, OF_REGEN)) i += 30; /* Slow digestion takes less food */ if (player_of_has(player, OF_SLOW_DIGEST)) i /= 5; /* Minimal digestion */ if (i < 1) i = 1; /* Digest some food */ player_set_food(player, player->food - i); } /* Getting Faint */ if (player->food < PY_FOOD_FAINT) { /* Faint occasionally */ if (!player->timed[TMD_PARALYZED] && one_in_(10)) { /* Message */ msg("You faint from the lack of food."); disturb(player, 1); /* Faint (bypass free action) */ (void)player_inc_timed(player, TMD_PARALYZED, 1 + randint0(5), true, false); } } /* Starve to death (slowly) */ if (player->food < PY_FOOD_STARVE) { /* Calculate damage */ i = (PY_FOOD_STARVE - player->food) / 10; /* Take damage */ take_hit(player, i, "starvation"); } /* Regenerate Hit Points if needed */ if (player->chp < player->mhp) player_regen_hp(); /* Regenerate mana if needed */ if (player->csp < player->msp) player_regen_mana(); /* Timeout various things */ decrease_timeouts(); /* Process light */ player_update_light(); /*** Process Inventory ***/ /* Handle experience draining */ if (player_of_has(player, OF_DRAIN_EXP)) { if ((player->exp > 0) && one_in_(10)) { s32b d = damroll(10, 6) + (player->exp / 100) * z_info->life_drain_percent; player_exp_lose(player, d / 10, false); } equip_learn_flag(player, OF_DRAIN_EXP); } /* Recharge activatable objects and rods */ recharge_objects(); /* Notice things after time */ if (!(turn % 100)) equip_learn_after_time(player); /* Decrease trap timeouts */ for (y = 0; y < cave->height; y++) { for (x = 0; x < cave->width; x++) { struct trap *trap = cave->squares[y][x].trap; while (trap) { if (trap->timeout) { trap->timeout--; if (!trap->timeout) square_light_spot(cave, y, x); } trap = trap->next; } } } /*** Involuntary Movement ***/ /* Delayed Word-of-Recall */ if (player->word_recall) { /* Count down towards recall */ player->word_recall--; /* Activate the recall */ if (!player->word_recall) { /* Disturbing! */ disturb(player, 0); /* Determine the level */ if (player->depth) { msgt(MSG_TPLEVEL, "You feel yourself yanked upwards!"); dungeon_change_level(0); } else { msgt(MSG_TPLEVEL, "You feel yourself yanked downwards!"); /* Force descent to a lower level if allowed */ if (OPT(birth_force_descend) && player->max_depth < z_info->max_depth - 1 && !is_quest(player->max_depth)) { player->max_depth = dungeon_get_next_level(player->max_depth, 1); } /* New depth - back to max depth or 1, whichever is deeper */ dungeon_change_level(player->max_depth < 1 ? 1: player->max_depth); } } } /* Delayed Deep Descent */ if (player->deep_descent) { /* Count down towards recall */ player->deep_descent--; /* Activate the recall */ if (player->deep_descent == 0) { int target_increment; int target_depth = player->max_depth; /* Calculate target depth */ target_increment = (4 / z_info->stair_skip) + 1; target_depth = dungeon_get_next_level(player->max_depth, target_increment); disturb(player, 0); /* Determine the level */ if (target_depth > player->depth) { msgt(MSG_TPLEVEL, "The floor opens beneath you!"); dungeon_change_level(target_depth); } else { /* Otherwise do something disastrous */ msgt(MSG_TPLEVEL, "You are thrown back in an explosion!"); effect_simple(EF_DESTRUCTION, "0", 0, 5, 0, NULL); } } } }
/** * Funtion for placing appropriate monsters in a room of chambers * * \param c the current chunk being generated * \param y1 * \param x1 * \param y2 * \param x2 the limits of the vault * \param name the name of the monster type for use in mon_select() * \param area the total room area, used for scaling monster quantity */ void get_chamber_monsters(struct chunk *c, int y1, int x1, int y2, int x2, char *name, int area) { int i, y, x; s16b monsters_left, depth; bool random = one_in_(20); /* Get a legal depth. */ depth = c->depth + randint0(11) - 5; /* Choose a pit profile, using that depth. */ if (!random) set_pit_type(depth, 0); /* Allow (slightly) tougher monsters. */ depth = c->depth + (c->depth < 60 ? c->depth / 12 : 5); /* Set monster generation restrictions. Occasionally random. */ if (random) { if (!mon_restrict("random", depth, true)) return; my_strcpy(name, "random", sizeof(name)); } else { if (!mon_restrict(dun->pit_type->name, depth, true)) return; my_strcpy(name, dun->pit_type->name, sizeof(name)); } /* Build the monster probability table. */ if (!get_mon_num(depth)) { (void) mon_restrict(NULL, depth, false); name = NULL; return; } /* No normal monsters. */ generate_mark(c, y1, x1, y2, x2, SQUARE_MON_RESTRICT); /* Allow about a monster every 20-30 grids. */ monsters_left = area / (30 - c->depth / 10); /* Place the monsters. */ for (i = 0; i < 300; i++) { /* Check for early completion. */ if (!monsters_left) break; /* Pick a random in-room square. */ y = y1 + randint0(1 + ABS(y2 - y1)); x = x1 + randint0(1 + ABS(x2 - x1)); /* Require a passable square with no monster in it already. */ if (!square_isempty(c, y, x)) continue; /* Place a single monster. Sleeping 2/3rds of the time. */ pick_and_place_monster(c, y, x, c->depth, (randint0(3) != 0), false, ORIGIN_DROP_SPECIAL); /* One less monster to place. */ monsters_left--; } /* Remove our restrictions. */ (void) mon_restrict(NULL, depth, false); }
/* * Place a random type of door at the given location */ void place_random_door(int y, int x, bool room) { int tmp, type; s16b feat = feat_none; cave_type *c_ptr = &cave[y][x]; /* Initialize mimic info */ c_ptr->mimic = 0; /* if (dungeon_type == DUNGEON_ARENA) { place_rubble_bold(y, x); return; }*/ if (d_info[dungeon_type].flags1 & DF1_NO_DOORS) { place_floor_bold(y, x); return; } type = ((d_info[dungeon_type].flags1 & DF1_CURTAIN) && one_in_((d_info[dungeon_type].flags1 & DF1_NO_CAVE) ? 16 : 256)) ? DOOR_CURTAIN : ((d_info[dungeon_type].flags1 & DF1_GLASS_DOOR) ? DOOR_GLASS_DOOR : DOOR_DOOR); /* Choose an object */ tmp = randint0(1000); /* Open doors (300/1000) */ if (tmp < 300) { /* Create open door */ feat = feat_door[type].open; } /* Broken doors (100/1000) */ else if (tmp < 400) { /* Create broken door */ feat = feat_door[type].broken; } /* Secret doors (200/1000) */ else if (tmp < 600) { /* Create secret door */ place_closed_door(y, x, type); if (type != DOOR_CURTAIN) { /* Hide. If on the edge of room, use outer wall. */ c_ptr->mimic = room ? feat_wall_outer : fill_type[randint0(100)]; /* Floor type terrain cannot hide a door */ if (feat_supports_los(c_ptr->mimic) && !feat_supports_los(c_ptr->feat)) { if (have_flag(f_info[c_ptr->mimic].flags, FF_MOVE) || have_flag(f_info[c_ptr->mimic].flags, FF_CAN_FLY)) { c_ptr->feat = one_in_(2) ? c_ptr->mimic : floor_type[randint0(100)]; } c_ptr->mimic = 0; } } } /* Closed, locked, or stuck doors (400/1000) */ else place_closed_door(y, x, type); if (tmp < 400) { if (feat != feat_none) { set_cave_feat(y, x, feat); } else { place_floor_bold(y, x); } } delete_monster(y, x); }
/** * Find a grid near the given one for an object to fall on * * We check several locations to see if we can find a location at which * the object can combine, stack, or be placed. Artifacts will try very * hard to be placed, including "teleporting" to a useful grid if needed. * * If no appropriate grid is found, the given grid is unchanged */ static void drop_find_grid(struct object *drop, int *y, int *x) { int best_score = -1; int best_y = *y; int best_x = *x; int i, dy, dx; struct object *obj; /* Scan local grids */ for (dy = -3; dy <= 3; dy++) { for (dx = -3; dx <= 3; dx++) { bool combine = false; int dist = (dy * dy) + (dx * dx); int ty = *y + dy; int tx = *x + dx; int num_shown = 0; int num_ignored = 0; int score; /* Lots of reasons to say no */ if ((dist > 10) || !square_in_bounds_fully(cave, ty, tx) || !los(cave, *y, *x, ty, tx) || !square_isfloor(cave, ty, tx) || square_isplayertrap(cave, ty, tx) || square_iswarded(cave, ty, tx)) continue; /* Analyse the grid for carrying the new object */ for (obj = square_object(cave, ty, tx); obj; obj = obj->next) { /* Check for possible combination */ if (object_similar(obj, drop, OSTACK_FLOOR)) combine = true; /* Count objects */ if (!ignore_item_ok(obj)) num_shown++; else num_ignored++; } if (!combine) num_shown++; /* Disallow if the stack size is too big */ if ((OPT(player, birth_stacking) && (num_shown > 1)) || ((num_shown + num_ignored) > z_info->floor_size && !floor_get_oldest_ignored(ty, tx))) continue; /* Score the location based on how close and how full the grid is */ score = 1000 - (dist + num_shown * 5); if ((score < best_score) || ((score == best_score) && one_in_(2))) continue; best_score = score; best_y = ty; best_x = tx; } } /* Return if we have a score, otherwise fail or try harder for artifacts */ if (best_score >= 0) { *y = best_y; *x = best_x; return; } else if (!drop->artifact) { return; } for (i = 0; i < 2000; i++) { /* Start bouncing from grid to grid, stopping if we find an empty one */ if (i < 1000) { best_y = rand_spread(best_y, 1); best_x = rand_spread(best_x, 1); } else { /* Now go to purely random locations */ best_y = randint0(cave->height); best_x = randint0(cave->width); } if (square_canputitem(cave, best_y, best_x)) { *y = best_y; *x = best_x; return; } } }
/** * Work out if a monster can move through the grid, if necessary bashing * down doors in the way. * * Returns true if the monster is able to move through the grid. */ static bool monster_turn_can_move(struct chunk *c, struct monster *mon, const char *m_name, int nx, int ny, bool *did_something) { struct monster_lore *lore = get_lore(mon->race); /* Dangerous terrain in the way */ if (monster_hates_grid(c, mon, ny, nx)) { return false; } /* Floor is open? */ if (square_ispassable(c, ny, nx)) { return true; } /* Permanent wall in the way */ if (square_iswall(c, ny, nx) && square_isperm(c, ny, nx)) { return false; } /* Normal wall, door, or secret door in the way */ /* There's some kind of feature in the way, so learn about * kill-wall and pass-wall now */ if (monster_is_visible(mon)) { rf_on(lore->flags, RF_PASS_WALL); rf_on(lore->flags, RF_KILL_WALL); } /* Monster may be able to deal with walls and doors */ if (rf_has(mon->race->flags, RF_PASS_WALL)) { return true; } else if (rf_has(mon->race->flags, RF_KILL_WALL)) { /* Remove the wall */ square_destroy_wall(c, ny, nx); /* Note changes to viewable region */ if (square_isview(c, ny, nx)) player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS); return true; } else if (square_iscloseddoor(c, ny, nx) || square_issecretdoor(c, ny, nx)) { bool can_open = rf_has(mon->race->flags, RF_OPEN_DOOR); bool can_bash = rf_has(mon->race->flags, RF_BASH_DOOR); bool will_bash = false; /* Take a turn */ *did_something = true; /* Learn about door abilities */ if (monster_is_visible(mon)) { rf_on(lore->flags, RF_OPEN_DOOR); rf_on(lore->flags, RF_BASH_DOOR); } /* If creature can open or bash doors, make a choice */ if (can_open) { /* Sometimes bash anyway (impatient) */ if (can_bash) { will_bash = one_in_(2) ? true : false; } } else if (can_bash) { /* Only choice */ will_bash = true; } else { /* Door is an insurmountable obstacle */ return false; } /* Now outcome depends on type of door */ if (square_islockeddoor(c, ny, nx)) { /* Locked door -- test monster strength against door strength */ int k = square_door_power(c, ny, nx); if (randint0(mon->hp / 10) > k) { if (will_bash) { msg("%s slams against the door.", m_name); } else { msg("%s fiddles with the lock.", m_name); } /* Reduce the power of the door by one */ square_set_door_lock(c, ny, nx, k - 1); } } else { /* Closed or secret door -- always open or bash */ if (square_isview(c, ny, nx)) player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS); if (will_bash) { square_smash_door(c, ny, nx); msg("You hear a door burst open!"); disturb(player, 0); /* Fall into doorway */ return true; } else { square_open_door(c, ny, nx); } } } return false; }