/* the below copyright probably still applies, but it is heavily changed * copied, adapted & re-engineered by JK. * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "traps.hpp" #include "artifact_type.hpp" #include "cave.hpp" #include "cave_type.hpp" #include "cmd1.hpp" #include "cmd2.hpp" #include "dungeon_flag.hpp" #include "dungeon_info_type.hpp" #include "feature_flag.hpp" #include "feature_type.hpp" #include "files.hpp" #include "gods.hpp" #include "monster2.hpp" #include "monster_race.hpp" #include "monster_race_flag.hpp" #include "monster_type.hpp" #include "object1.hpp" #include "object2.hpp" #include "object_flag.hpp" #include "object_kind.hpp" #include "player_race.hpp" #include "player_race_mod.hpp" #include "player_spec.hpp" #include "player_type.hpp" #include "spells1.hpp" #include "spells2.hpp" #include "stats.hpp" #include "tables.hpp" #include "trap_type.hpp" #include "util.hpp" #include "variable.hpp" #include "xtra1.hpp" #include "xtra2.hpp" #include "z-rand.hpp" bool_ do_player_trap_call_out(void) { s16b i, sn, cx, cy; s16b h_index = 0; s16b h_level = 0; monster_type *m_ptr; char m_name[80]; bool_ ident = FALSE; for (i = 1; i < m_max; i++) { m_ptr = &m_list[i]; /* Paranoia -- Skip dead monsters */ if (!m_ptr->r_idx) continue; if (m_ptr->level >= h_level) { h_level = m_ptr->level; h_index = i; } } /* if the level is empty of monsters, h_index will be 0 */ if (!h_index) return (FALSE); m_ptr = &m_list[h_index]; sn = 0; for (i = 0; i < 8; i++) { cx = p_ptr->px + ddx[i]; cy = p_ptr->py + ddy[i]; /* Skip non-empty grids */ if (!cave_valid_bold(cy, cx)) continue; if (cave[cy][cx].feat == FEAT_GLYPH) continue; if ((cx == p_ptr->px) && (cy == p_ptr->py)) continue; sn++; /* Randomize choice */ if (rand_int(sn) > 0) continue; cave[cy][cx].m_idx = h_index; cave[m_ptr->fy][m_ptr->fx].m_idx = 0; m_ptr->fx = cx; m_ptr->fy = cy; /* we do not change the sublevel! */ ident = TRUE; update_mon(h_index, TRUE); monster_desc(m_name, m_ptr, 0x08); msg_format("You hear a rapid-shifting wail, and %s appears!", m_name); break; } return (ident); } static bool_ do_trap_teleport_away(object_type *i_ptr, s16b y, s16b x) { bool_ ident = FALSE; char o_name[80]; s16b o_idx = 0; object_type *o_ptr; cave_type *c_ptr; s16b x1; s16b y1; if (i_ptr == NULL) return (FALSE); if (i_ptr->name1 == ART_POWER) return (FALSE); while (o_idx == 0) { x1 = rand_int(cur_wid); y1 = rand_int(cur_hgt); /* Obtain grid */ c_ptr = &cave[y1][x1]; /* Require floor space (or shallow terrain) -KMW- */ if (!(f_info[c_ptr->feat].flags & FF_FLOOR)) continue; o_idx = drop_near(i_ptr, 0, y1, x1); } o_ptr = &o_list[o_idx]; x1 = o_ptr->ix; y1 = o_ptr->iy; if (!p_ptr->blind) { note_spot(y, x); lite_spot(y, x); ident = TRUE; object_desc(o_name, i_ptr, FALSE, 0); if (player_has_los_bold(y1, x1)) { lite_spot(y1, x1); msg_format("The %s suddenly stands elsewhere.", o_name); } else { msg_format("You suddenly don't see the %s any more!", o_name); } } else { msg_print("You hear something move."); } return (ident); } /* * this handles a trap that places walls around the player */ static bool_ player_handle_trap_of_walls(void) { bool_ ident; s16b dx, dy, cx, cy; s16b sx = 0, sy = 0, sn, i; cave_type *cv_ptr; bool_ map[5][5] = { {FALSE, FALSE, FALSE, FALSE, FALSE}, {FALSE, FALSE, FALSE, FALSE, FALSE}, {FALSE, FALSE, FALSE, FALSE, FALSE}, {FALSE, FALSE, FALSE, FALSE, FALSE}, {FALSE, FALSE, FALSE, FALSE, FALSE} }; for (dy = -2; dy <= 2; dy++) for (dx = -2; dx <= 2; dx++) { /* Extract the location */ cx = p_ptr->px + dx; cy = p_ptr->py + dy; if (!in_bounds(cy, cx)) continue; cv_ptr = &cave[cy][cx]; if (cv_ptr->m_idx) continue; /* Lose room and vault */ cv_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY); /* Lose light and knowledge */ cv_ptr->info &= ~(CAVE_GLOW | CAVE_MARK); /* Skip the center */ if (!dx && !dy) continue; /* test for dungeon level */ if (randint(100) > 10 + max_dlv[dungeon_type]) continue; /* Damage this grid */ map[2 + dx][2 + dy] = TRUE; } for (dy = -2; dy <= 2; dy++) for (dx = -2; dx <= 2; dx++) { /* Extract the location */ cx = p_ptr->px + dx; cy = p_ptr->py + dy; /* Skip unaffected grids */ if (!map[2 + dx][2 + dy]) continue; cv_ptr = &cave[cy][cx]; if (cv_ptr->m_idx) { monster_type *m_ptr = &m_list[cv_ptr->m_idx]; auto const r_ptr = m_ptr->race(); /* Most monsters cannot co-exist with rock */ if ((!(r_ptr->flags & RF_KILL_WALL)) && (!(r_ptr->flags & RF_PASS_WALL))) { char m_name[80]; /* Assume not safe */ sn = 0; /* Monster can move to escape the wall */ if (!(r_ptr->flags & RF_NEVER_MOVE)) { /* Look for safety */ for (i = 0; i < 8; i++) { /* Access the grid */ cy = p_ptr->py + ddy[i]; cx = p_ptr->px + ddx[i]; /* Skip non-empty grids */ if (!cave_clean_bold(cy, cx)) continue; /* Hack -- no safety on glyph of warding */ if (cave[cy][cx].feat == FEAT_GLYPH) continue; /* Important -- Skip "quake" grids */ if (map[2 + (cx - p_ptr->px)][2 + (cy - p_ptr->py)]) continue; /* Count "safe" grids */ sn++; /* Randomize choice */ if (rand_int(sn) > 0) continue; /* Save the safe grid */ sx = cx; sy = cy; ident = TRUE; break; /* discontinue for loop - safe grid found */ } } /* Describe the monster */ monster_desc(m_name, m_ptr, 0); /* Scream in pain */ msg_format("%^s wails out in pain!", m_name); /* Monster is certainly awake */ m_ptr->csleep = 0; /* Apply damage directly */ m_ptr->hp -= (sn ? damroll(4, 8) : 200); /* Delete (not kill) "dead" monsters */ if (m_ptr->hp < 0) { /* Message */ msg_format("%^s is entombed in the rock!", m_name); /* Delete the monster */ delete_monster_idx(cave[cy][cx].m_idx); /* No longer safe */ sn = 0; } /* Hack -- Escape from the rock */ if (sn) { s16b m_idx = cave[cy][cx].m_idx; /* Update the new location */ cave[sy][sx].m_idx = m_idx; /* Update the old location */ cave[cy][cx].m_idx = 0; /* Move the monster */ m_ptr->fy = sy; m_ptr->fx = sx; /* do not change fz */ /* don't make rock on that square! */ if ((sx >= (p_ptr->px - 2)) && (sx <= (p_ptr->px + 2)) && (sy >= (p_ptr->py - 2)) && (sy <= (p_ptr->py + 2))) { map[2 + (sx - p_ptr->px)][2 + (sy - p_ptr->py)] = FALSE; } /* Update the monster (new location) */ update_mon(m_idx, TRUE); /* Redraw the old grid */ lite_spot(cy, cx); /* Redraw the new grid */ lite_spot(sy, sx); } /* if sn */ } /* if monster can co-exist with rock */ } /* if monster on square */ } /* Examine the quaked region */ for (dy = -2; dy <= 2; dy++) for (dx = -2; dx <= 2; dx++) { /* Extract the location */ cx = p_ptr->px + dx; cy = p_ptr->py + dy; /* Skip unaffected grids */ if (!map[2 + dx][2 + dy]) continue; /* Access the cave grid */ cv_ptr = &cave[cy][cx]; /* Paranoia -- never affect player */ if (!dy && !dx) continue; /* Destroy location (if valid) */ if ((cx < cur_wid) && (cy < cur_hgt) && cave_valid_bold(cy, cx)) { bool floor = bool(f_info[cave[cy][cx].feat].flags & FF_FLOOR); /* Delete any object that is still there */ delete_object(cy, cx); if (floor) { cave_set_feat(cy, cx, FEAT_WALL_OUTER); } else { /* Clear previous contents, add floor */ cave_set_feat(cy, cx, FEAT_FLOOR); } } } /* Mega-Hack -- Forget the view and lite */ p_ptr->update |= PU_UN_VIEW; /* Update stuff */ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE); /* Update the monsters */ p_ptr->update |= (PU_DISTANCE); /* Update the health bar */ p_ptr->redraw |= (PR_FRAME); /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD); handle_stuff(); msg_print("Suddenly the cave shifts around you. The air is getting stale!"); ident = TRUE; return (ident); } /* * this function handles arrow & dagger traps, in various types. * num = number of missiles * tval, sval = kind of missiles * dd,ds = damage roll for missiles * poison_dam = additional poison damage * name = name given if you should die from it... * * return value = ident (always TRUE) */ static bool_ player_handle_missile_trap(s16b num, s16b tval, s16b sval, s16b dd, s16b ds, s16b pdam, cptr name) { object_type *o_ptr, forge; s16b i, k_idx = lookup_kind(tval, sval); char i_name[80]; o_ptr = &forge; object_prep(o_ptr, k_idx); o_ptr->number = num; apply_magic(o_ptr, max_dlv[dungeon_type], FALSE, FALSE, FALSE); object_desc(i_name, o_ptr, TRUE, 0); msg_format("Suddenly %s hit%s you!", i_name, ((num == 1) ? "" : "s")); for (i = 0; i < num; i++) { take_hit(damroll(dd, ds), name); redraw_stuff(); if (pdam > 0) { if (!(p_ptr->resist_pois || p_ptr->oppose_pois)) { (void)set_poisoned(p_ptr->poisoned + pdam); } } } drop_near(o_ptr, -1, p_ptr->py, p_ptr->px); return TRUE; } /* * this function handles a "breath" type trap - acid bolt, lightning balls etc. */ static bool_ player_handle_breath_trap(s16b rad, s16b type, u16b trap) { trap_type *t_ptr = &t_info[trap]; bool_ ident; s16b my_dd, my_ds, dam; my_dd = t_ptr->dd; my_ds = t_ptr->ds; /* these traps gets nastier as levels progress */ if (max_dlv[dungeon_type] > (2 * t_ptr->minlevel)) { my_dd += (max_dlv[dungeon_type] / 15); my_ds += (max_dlv[dungeon_type] / 15); } dam = damroll(my_dd, my_ds); ident = project( -2, rad, p_ptr->py, p_ptr->px, dam, type, PROJECT_KILL | PROJECT_JUMP); return (ident); } /* * this function activates one trap type, and returns * a bool_ indicating if this trap is now identified */ bool_ player_activate_trap_type(s16b y, s16b x, object_type *i_ptr, s16b item) { bool_ ident = FALSE; s16b trap; s16b k, l; trap = cave[y][x].t_idx; if (i_ptr != NULL) { trap = i_ptr->pval; } switch (trap) { /* stat traps */ case TRAP_OF_WEAKNESS_I: ident = do_dec_stat(A_STR, STAT_DEC_TEMPORARY); break; case TRAP_OF_WEAKNESS_II: ident = do_dec_stat(A_STR, STAT_DEC_NORMAL); break; case TRAP_OF_WEAKNESS_III: ident = do_dec_stat(A_STR, STAT_DEC_PERMANENT); break; case TRAP_OF_INTELLIGENCE_I: ident = do_dec_stat(A_INT, STAT_DEC_TEMPORARY); break; case TRAP_OF_INTELLIGENCE_II: ident = do_dec_stat(A_INT, STAT_DEC_NORMAL); break; case TRAP_OF_INTELLIGENCE_III: ident = do_dec_stat(A_INT, STAT_DEC_PERMANENT); break; case TRAP_OF_WISDOM_I: ident = do_dec_stat(A_WIS, STAT_DEC_TEMPORARY); break; case TRAP_OF_WISDOM_II: ident = do_dec_stat(A_WIS, STAT_DEC_NORMAL); break; case TRAP_OF_WISDOM_III: ident = do_dec_stat(A_WIS, STAT_DEC_PERMANENT); break; case TRAP_OF_FUMBLING_I: ident = do_dec_stat(A_DEX, STAT_DEC_TEMPORARY); break; case TRAP_OF_FUMBLING_II: ident = do_dec_stat(A_DEX, STAT_DEC_NORMAL); break; case TRAP_OF_FUMBLING_III: ident = do_dec_stat(A_DEX, STAT_DEC_PERMANENT); break; case TRAP_OF_WASTING_I: ident = do_dec_stat(A_CON, STAT_DEC_TEMPORARY); break; case TRAP_OF_WASTING_II: ident = do_dec_stat(A_CON, STAT_DEC_NORMAL); break; case TRAP_OF_WASTING_III: ident = do_dec_stat(A_CON, STAT_DEC_PERMANENT); break; case TRAP_OF_BEAUTY_I: ident = do_dec_stat(A_CHR, STAT_DEC_TEMPORARY); break; case TRAP_OF_BEAUTY_II: ident = do_dec_stat(A_CHR, STAT_DEC_NORMAL); break; case TRAP_OF_BEAUTY_III: ident = do_dec_stat(A_CHR, STAT_DEC_PERMANENT); break; /* Trap of Curse Weapon */ case TRAP_OF_CURSE_WEAPON: { ident = curse_weapon(); break; } /* Trap of Curse Armor */ case TRAP_OF_CURSE_ARMOR: { ident = curse_armor(); break; } /* Earthquake Trap */ case TRAP_OF_EARTHQUAKE: { msg_print("As you touch the trap, the ground starts to shake."); earthquake(y, x, 10); ident = TRUE; break; } /* Poison Needle Trap */ case TRAP_OF_POISON_NEEDLE: { if (!(p_ptr->resist_pois || p_ptr->oppose_pois)) { msg_print("You prick yourself on a poisoned needle."); (void)set_poisoned(p_ptr->poisoned + rand_int(15) + 10); ident = TRUE; } else { msg_print("You prick yourself on a needle."); } break; } /* Summon Monster Trap */ case TRAP_OF_SUMMON_MONSTER: { msg_print("A spell hangs in the air."); for (k = 0; k < randint(3); k++) { ident |= summon_specific(y, x, max_dlv[dungeon_type], 0); } break; } /* Summon Undead Trap */ case TRAP_OF_SUMMON_UNDEAD: { msg_print("A mighty spell hangs in the air."); for (k = 0; k < randint(3); k++) { ident |= summon_specific(y, x, max_dlv[dungeon_type], SUMMON_UNDEAD); } break; } /* Summon Greater Undead Trap */ case TRAP_OF_SUMMON_GREATER_UNDEAD: { msg_print("An old and evil spell hangs in the air."); for (k = 0; k < randint(3); k++) { ident |= summon_specific(y, x, max_dlv[dungeon_type], SUMMON_HI_UNDEAD); } break; } /* Teleport Trap */ case TRAP_OF_TELEPORT: { msg_print("The world whirls around you."); teleport_player(RATIO * 67); ident = TRUE; break; } /* Paralyzing Trap */ case TRAP_OF_PARALYZING: { if (!p_ptr->free_act) { msg_print("You touch a poisoned part and can't move."); (void)set_paralyzed(rand_int(10) + 10); ident = TRUE; } else { msg_print("You prick yourself on a needle."); } break; } /* Explosive Device */ case TRAP_OF_EXPLOSIVE_DEVICE: { msg_print("A hidden explosive device explodes in your face."); take_hit(damroll(5, 8), "an explosion"); ident = TRUE; break; } /* Teleport Away Trap */ case TRAP_OF_TELEPORT_AWAY: { /* teleport away all items */ while (!cave[y][x].o_idxs.empty()) { auto item = cave[y][x].o_idxs.front(); object_type *o_ptr = &o_list[item]; int amt = o_ptr->number; ident = do_trap_teleport_away(o_ptr, y, x); floor_item_increase(item, -amt); floor_item_optimize(item); } break; } /* Lose Memory Trap */ case TRAP_OF_LOSE_MEMORY: { lose_exp(p_ptr->exp / 4); ident |= dec_stat(A_WIS, rand_int(20) + 10, STAT_DEC_NORMAL); ident |= dec_stat(A_INT, rand_int(20) + 10, STAT_DEC_NORMAL); if (!p_ptr->resist_conf) { ident |= set_confused(p_ptr->confused + rand_int(100) + 50); } if (ident) { msg_print("You suddenly don't remember what you were doing."); } else { msg_print("You feel an alien force probing your mind."); } break; } /* Bitter Regret Trap */ case TRAP_OF_BITTER_REGRET: { msg_print("An age-old and hideous-sounding spell reverberates off the walls."); ident |= dec_stat(A_DEX, 25, TRUE); ident |= dec_stat(A_WIS, 25, TRUE); ident |= dec_stat(A_CON, 25, TRUE); ident |= dec_stat(A_STR, 25, TRUE); ident |= dec_stat(A_CHR, 25, TRUE); ident |= dec_stat(A_INT, 25, TRUE); break; } /* Bowel Cramps Trap */ case TRAP_OF_BOWEL_CRAMPS: { msg_print("A wretched-smelling gas cloud upsets your stomach."); (void)set_food(PY_FOOD_STARVE - 1); (void)set_poisoned(0); if (!p_ptr->free_act) { (void)set_paralyzed(rand_int(dun_level) + 6); } ident = TRUE; break; } /* Blindness/Confusion Trap */ case TRAP_OF_BLINDNESS_CONFUSION: { msg_print("A powerful magic protected this."); if (!p_ptr->resist_blind) { ident |= set_blind(p_ptr->blind + rand_int(100) + 100); } if (!p_ptr->resist_conf) { ident |= set_confused(p_ptr->confused + rand_int(20) + 15); } break; } /* Aggravation Trap */ case TRAP_OF_AGGRAVATION: { msg_print("You hear a hollow noise echoing through the dungeons."); aggravate_monsters(1); break; } /* Multiplication Trap */ case TRAP_OF_MULTIPLICATION: { msg_print("You hear a loud click."); for (k = -1; k <= 1; k++) for (l = -1; l <= 1; l++) { if ((in_bounds(p_ptr->py + l, p_ptr->px + k)) && (!cave[p_ptr->py + l][p_ptr->px + k].t_idx)) { place_trap(p_ptr->py + l, p_ptr->px + k); } } ident = TRUE; break; } /* Steal Item Trap */ case TRAP_OF_STEAL_ITEM: { /* * please note that magical stealing is not so * easily circumvented */ if (!p_ptr->paralyzed && (rand_int(160) < (adj_dex_safe[p_ptr->stat_ind[A_DEX]] + p_ptr->lev))) { /* Saving throw message */ msg_print("Your backpack seems to vibrate strangely!"); break; } /* Find an item */ for (k = 0; k < rand_int(10); k++) { char i_name[80]; object_type *j_ptr, *q_ptr, forge; /* Pick an item */ s16b i = rand_int(INVEN_PACK); /* Obtain the item */ j_ptr = &p_ptr->inventory[i]; /* Accept real items */ if (!j_ptr->k_idx) continue; /* Don't steal artifacts -CFT */ if (artifact_p(j_ptr)) continue; /* Get a description */ object_desc(i_name, j_ptr, FALSE, 3); /* Message */ msg_format("%sour %s (%c) was stolen!", ((j_ptr->number > 1) ? "One of y" : "Y"), i_name, index_to_label(i)); /* Create the item */ q_ptr = &forge; object_copy(q_ptr, j_ptr); q_ptr->number = 1; /* Drop it somewhere */ do_trap_teleport_away(q_ptr, y, x); inc_stack_size_ex(i, -1, OPTIMIZE, NO_DESCRIBE); ident = TRUE; } break; } /* Summon Fast Quylthulgs Trap */ case TRAP_OF_SUMMON_FAST_QUYLTHULGS: { for (k = 0; k < randint(3); k++) { ident |= summon_specific(y, x, max_dlv[dungeon_type], SUMMON_QUYLTHULG); } if (ident) { msg_print("You suddenly have company."); (void)set_slow(p_ptr->slow + randint(25) + 15); } break; } /* Trap of Sinking */ case TRAP_OF_SINKING: { msg_print("You fell through a trap door!"); if (p_ptr->ffall) { if (dungeon_flags & DF_TOWER) { msg_print("You float gently down to the previous level."); } else { msg_print("You float gently down to the next level."); } } else { take_hit(damroll(2, 8), "a trap door"); } /* Still alive and autosave enabled */ if (p_ptr->chp >= 0) { autosave_checkpoint(); } if (dungeon_flags & DF_TOWER) dun_level--; else dun_level++; /* Leaving */ p_ptr->leaving = TRUE; break; } /* Trap of Mana Drain */ case TRAP_OF_MANA_DRAIN: { if (p_ptr->csp > 0) { p_ptr->csp = 0; p_ptr->csp_frac = 0; p_ptr->redraw |= (PR_FRAME); msg_print("You sense a great loss."); ident = TRUE; } else if (p_ptr->msp == 0) { /* no sense saying this unless you never have mana */ msg_format("Suddenly you feel glad you're a mere %s", spp_ptr->title); } else { msg_print("Your head feels dizzy for a moment."); } break; } /* Trap of Missing Money */ case TRAP_OF_MISSING_MONEY: { s32b gold = (p_ptr->au / 10) + randint(25); if (gold < 2) gold = 2; if (gold > 5000) gold = (p_ptr->au / 20) + randint(3000); if (gold > p_ptr->au) gold = p_ptr->au; p_ptr->au -= gold; if (gold <= 0) { msg_print("You feel something touching you."); } else if (p_ptr->au) { msg_print("Your purse feels lighter."); msg_format("%ld coins were stolen!", (long)gold); ident = TRUE; } else { msg_print("Your purse feels empty."); msg_print("All of your coins were stolen!"); ident = TRUE; } p_ptr->redraw |= (PR_FRAME); break; } /* Trap of No Return */ case TRAP_OF_NO_RETURN: { object_type *j_ptr; s16b j; for (j = 0; j < INVEN_WIELD; j++) { if (!p_ptr->inventory[j].k_idx) continue; j_ptr = &p_ptr->inventory[j]; if ((j_ptr->tval == TV_SCROLL) && (j_ptr->sval == SV_SCROLL_WORD_OF_RECALL)) { inc_stack_size_ex(j, -j_ptr->number, OPTIMIZE, NO_DESCRIBE); combine_pack(); reorder_pack(); if (!ident) { msg_print("A small fire works its way through your backpack. " "Some scrolls are burnt."); } else { msg_print("The fire hasn't finished."); } ident = TRUE; } else if ((j_ptr->tval == TV_ROD_MAIN) && (j_ptr->pval == SV_ROD_RECALL)) { j_ptr->timeout = 0; /* a long time */ if (!ident) msg_print("You feel the air stabilise around you."); ident = TRUE; } } if ((!ident) && (p_ptr->word_recall)) { msg_print("You feel like staying around."); p_ptr->word_recall = 0; ident = TRUE; } break; } /* Trap of Silent Switching */ case TRAP_OF_SILENT_SWITCHING: { s16b i, j, slot1, slot2; object_type *j_ptr, *k_ptr; for (i = INVEN_WIELD; i < INVEN_TOTAL; i++) { j_ptr = &p_ptr->inventory[i]; if (!j_ptr->k_idx) continue; /* Do not allow this trap to touch the One Ring */ auto const j_flags = object_flags(j_ptr); if(j_flags & TR_PERMA_CURSE) continue; slot1 = wield_slot(j_ptr); for (j = 0; j < INVEN_WIELD; j++) { k_ptr = &p_ptr->inventory[j]; if (!k_ptr->k_idx) continue; /* Do not allow this trap to touch the One Ring */ auto const k_flags = object_flags(k_ptr); if(k_flags & TR_PERMA_CURSE) continue; /* this is a crude hack, but it prevent wielding 6 torches... */ if (k_ptr->number > 1) continue; slot2 = wield_slot(k_ptr); /* a chance of 4 in 5 of switching something, then 2 in 5 to do it again */ if ((slot1 == slot2) && (rand_int(100) < (80 - (ident * 40)))) { object_type tmp_obj; if (p_ptr->inventory[j].name1) wield_set(p_ptr->inventory[j].name1, a_info[p_ptr->inventory[j].name1].set, FALSE); if (p_ptr->inventory[i].name1) takeoff_set(p_ptr->inventory[i].name1, a_info[p_ptr->inventory[i].name1].set); tmp_obj = p_ptr->inventory[j]; p_ptr->inventory[j] = p_ptr->inventory[i]; p_ptr->inventory[i] = tmp_obj; ident = TRUE; } } } if (ident) { p_ptr->update |= (PU_BONUS); p_ptr->update |= (PU_TORCH); p_ptr->update |= (PU_MANA); msg_print("You somehow feel like another person."); } else { msg_print("You feel a lack of useful items."); } break; } /* Trap of Walls */ case TRAP_OF_WALLS: { ident = player_handle_trap_of_walls(); break; } /* Trap of Calling Out */ case TRAP_OF_CALLING_OUT: { ident = do_player_trap_call_out(); if (!ident) { /* Increase "afraid" */ if (p_ptr->resist_fear) { msg_print("You feel as if you had a nightmare!"); } else if (rand_int(100) < p_ptr->skill_sav) { msg_print("You remember having a nightmare!"); } else { if (set_afraid(p_ptr->afraid + 3 + randint(40))) { msg_print("You have a vision of a powerful enemy."); } } } break; } /* Trap of Sliding */ case TRAP_OF_SLIDING: break; /* Trap of Charges Drain */ case TRAP_OF_CHARGES_DRAIN: { /* Find an item */ for (k = 0; k < 10; k++) { s16b i = rand_int(INVEN_PACK); object_type *j_ptr = &p_ptr->inventory[i]; /* Drain charged wands/staffs Hack -- don't let artifacts get drained */ if (((j_ptr->tval == TV_STAFF) || (j_ptr->tval == TV_WAND)) && (j_ptr->pval) && !artifact_p(j_ptr)) { ident = TRUE; j_ptr->pval = j_ptr->pval / (randint(4) + 1); /* 60% chance of only 1 */ if (randint(10) > 3) break; } } if (ident) { /* Window stuff */ p_ptr->window |= PW_INVEN; /* Combine / Reorder the pack */ p_ptr->notice |= (PN_COMBINE | PN_REORDER); msg_print("Your backpack seems to be turned upside down."); } else { msg_print("You hear a wail of great disappointment."); } break; } /* Trap of Stair Movement */ case TRAP_OF_STAIR_MOVEMENT: { s16b cx, cy, i, j; s16b cnt = 0; s16b cnt_seen = 0; s16b tmps, tmpx; s16b tmpspecial, tmpspecial2; u32b tmpf; bool_ seen = FALSE; s16b index_x[20], index_y[20]; /* 20 stairs per level is enough? */ cave_type *cv_ptr; if (max_dlv[dungeon_type] == 99) { /* no sense in relocating that stair! */ msg_print("You have a feeling that this trap could be dangerous."); break; } for (cx = 0; cx < cur_wid; cx++) for (cy = 0; cy < cur_hgt; cy++) { cv_ptr = &cave[cy][cx]; if ((cv_ptr->feat != FEAT_LESS) && (cv_ptr->feat != FEAT_MORE) && (cv_ptr->feat != FEAT_SHAFT_UP) && (cv_ptr->feat != FEAT_SHAFT_DOWN)) continue; index_x[cnt] = cx; index_y[cnt] = cy; cnt++; } if (cnt == 0) { if (wizard) msg_print("Executing moving stairs trap on level with no stairs!"); break; } for (i = 0; i < cnt; i++) { seen = FALSE; for (j = 0; j < 10; j++) /* try 10 times to relocate */ { cave_type *cv_ptr = &cave[index_y[i]][index_x[i]]; cave_type *cv_ptr2; cx = rand_int(cur_wid); cy = rand_int(cur_hgt); if ((cx == index_x[i]) || (cy == index_y[i])) continue; cv_ptr2 = &cave[cy][cx]; if (!cave_valid_bold(cy, cx) || (!cv_ptr2->o_idxs.empty())) continue; /* don't put anything in vaults */ if (cv_ptr2->info & CAVE_ICKY) continue; tmpx = cv_ptr2->mimic; tmps = cv_ptr2->info; tmpf = cv_ptr2->feat; tmpspecial = cv_ptr2->special; tmpspecial2 = cv_ptr2->special2; cave[cy][cx].mimic = cv_ptr->mimic; cave[cy][cx].info = cv_ptr->info; cave[cy][cx].special = cv_ptr->special; cave[cy][cx].special2 = cv_ptr->special2; cave_set_feat(cy, cx, cv_ptr->feat); cv_ptr->mimic = tmpx; cv_ptr->info = tmps; cv_ptr->special = tmpspecial; cv_ptr->special2 = tmpspecial2; cave_set_feat(index_y[i], index_x[i], tmpf); /* if we are placing walls in rooms, make them rubble instead */ if ((cv_ptr->info & CAVE_ROOM) && (cv_ptr->feat >= FEAT_WALL_EXTRA) && (cv_ptr->feat <= FEAT_PERM_SOLID)) { cave_set_feat(index_y[i], index_x[i], FEAT_RUBBLE); } if (player_has_los_bold(cy, cx)) { note_spot(cy, cx); lite_spot(cy, cx); seen = TRUE; } else { cv_ptr2->info &= ~CAVE_MARK; } if (player_has_los_bold(index_y[i], index_x[i])) { note_spot(index_y[i], index_x[i]); lite_spot(index_y[i], index_x[i]); seen = TRUE; } else { cv_ptr->info &= ~CAVE_MARK; } break; } if (seen) cnt_seen++; } ident = (cnt_seen > 0); if ((ident) && (cnt_seen > 1)) { msg_print("You see some stairs move."); } else if (ident) { msg_print("You see a stair move."); } else { msg_print("You hear distant scraping noises."); } p_ptr->redraw |= PR_MAP; break; } /* Trap of New Trap */ case TRAP_OF_NEW: { /* if we're on a floor or on a door, place a new trap */ if ((item == -1) || (item == -2)) { place_trap(y, x); if (player_has_los_bold(y, x)) { note_spot(y, x); lite_spot(y, x); } } else { /* re-trap the chest */ place_trap(y, x); } msg_print("You hear a noise, and then its echo."); ident = FALSE; break; } /* Trap of Acquirement */ case TRAP_OF_ACQUIREMENT: { /* Get a nice thing */ msg_print("You notice something falling off the trap."); acquirement(y, x, 1, TRUE, FALSE); /* If we're on a floor or on a door, place a new trap */ if ((item == -1) || (item == -2)) { place_trap(y, x); if (player_has_los_bold(y, x)) { note_spot(y, x); lite_spot(y, x); } } else { /* Re-trap the chest */ place_trap(y, x); } msg_print("You hear a noise, and then its echo."); /* Never known */ ident = FALSE; } break; /* Trap of Scatter Items */ case TRAP_OF_SCATTER_ITEMS: { s16b i, j; bool_ message = FALSE; for (i = 0; i < INVEN_PACK; i++) { if (!p_ptr->inventory[i].k_idx) continue; if (rand_int(10) < 3) continue; for (j = 0; j < 10; j++) { object_type tmp_obj, *j_ptr = &tmp_obj; s16b cx = x + 15 - rand_int(30); s16b cy = y + 15 - rand_int(30); if (!in_bounds(cy, cx)) continue; if (!cave_floor_bold(cy, cx)) continue; object_copy(j_ptr, &p_ptr->inventory[i]); inc_stack_size_ex(i, -999, OPTIMIZE, NO_DESCRIBE); p_ptr->notice |= (PN_COMBINE | PN_REORDER); (void)floor_carry(cy, cx, j_ptr); if (!message) { msg_print("You feel light-footed."); message = TRUE; } if (player_has_los_bold(cy, cx)) { char i_name[80]; object_desc(i_name, &tmp_obj, TRUE, 3); note_spot(cy, cx); lite_spot(cy, cx); ident = TRUE; msg_format("Suddenly %s appear%s!", i_name, (j_ptr->number > 1) ? "" : "s"); } break; } } ident = message; break; } /* Trap of Decay */ case TRAP_OF_DECAY: break; /* Trap of Wasting Wands */ case TRAP_OF_WASTING_WANDS: { s16b i; object_type *j_ptr; for (i = 0; i < INVEN_PACK; i++) { if (!p_ptr->inventory[i].k_idx) continue; j_ptr = &p_ptr->inventory[i]; if ((j_ptr->tval == TV_WAND) && (rand_int(5) == 1)) { if (object_known_p(j_ptr)) ident = TRUE; /* Create a Wand of Nothing */ object_prep(j_ptr, lookup_kind(TV_WAND, SV_WAND_NOTHING)); apply_magic(j_ptr, 0, FALSE, FALSE, FALSE, boost::make_optional(0)); j_ptr->ident &= ~IDENT_KNOWN; p_ptr->notice |= (PN_COMBINE | PN_REORDER); } else if ((j_ptr->tval == TV_STAFF) && (rand_int(5) == 1)) { if (object_known_p(j_ptr)) ident = TRUE; /* Create a Staff of Nothing */ object_prep(j_ptr, lookup_kind(TV_STAFF, SV_STAFF_NOTHING)); apply_magic(j_ptr, 0, FALSE, FALSE, FALSE, boost::make_optional(0)); j_ptr->ident &= ~IDENT_KNOWN; p_ptr->notice |= (PN_COMBINE | PN_REORDER); } } if (ident) { msg_print("You have lost trust in your backpack!"); } else { msg_print("You hear an echoing cry of rage."); } break; } /* Trap of Filling */ case TRAP_OF_FILLING: { s16b nx, ny; for (nx = x - 8; nx <= x + 8; nx++) for (ny = y - 8; ny <= y + 8; ny++) { if (!in_bounds (ny, nx)) continue; if (rand_int(distance(ny, nx, y, x)) > 3) { place_trap(ny, nx); } } msg_print("The floor vibrates in a strange way."); ident = FALSE; break; } case TRAP_OF_DRAIN_SPEED: { s16b j, chance = 75; for (j = 0; j < INVEN_TOTAL; j++) { /* don't bother the overflow slot */ if (j == INVEN_PACK) continue; if (!p_ptr->inventory[j].k_idx) continue; auto j_ptr = &p_ptr->inventory[j]; auto const flags = object_flags(j_ptr); /* is it a non-artifact speed item? */ if ((!j_ptr->name1) && (flags & TR_SPEED)) { if (randint(100) < chance) { j_ptr->pval = j_ptr->pval / 2; if (j_ptr->pval == 0) { j_ptr->pval--; } chance /= 2; ident = TRUE; } inven_item_optimize(j); } } if (!ident) { msg_print("You feel some things in your pack vibrating."); } else { combine_pack(); reorder_pack(); msg_print("You suddenly feel you have time for self-reflection."); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Recalculate mana */ p_ptr->update |= (PU_MANA); /* Window stuff */ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); } break; } /* * single missile traps */ case TRAP_OF_ARROW_I: ident = player_handle_missile_trap(1, TV_ARROW, SV_AMMO_NORMAL, 4, 8, 0, "Arrow Trap"); break; case TRAP_OF_ARROW_II: ident = player_handle_missile_trap(1, TV_BOLT, SV_AMMO_NORMAL, 5, 8, 0, "Bolt Trap"); break; case TRAP_OF_ARROW_III: ident = player_handle_missile_trap(1, TV_ARROW, SV_AMMO_HEAVY, 6, 8, 0, "Seeker Arrow Trap"); break; case TRAP_OF_ARROW_IV: ident = player_handle_missile_trap(1, TV_BOLT, SV_AMMO_HEAVY, 8, 10, 0, "Seeker Bolt Trap"); break; case TRAP_OF_POISON_ARROW_I: ident = player_handle_missile_trap(1, TV_ARROW, SV_AMMO_NORMAL, 4, 8, 10 + randint(20), "Poison Arrow Trap"); break; case TRAP_OF_POISON_ARROW_II: ident = player_handle_missile_trap(1, TV_BOLT, SV_AMMO_NORMAL, 5, 8, 15 + randint(30), "Poison Bolt Trap"); break; case TRAP_OF_POISON_ARROW_III: ident = player_handle_missile_trap(1, TV_ARROW, SV_AMMO_HEAVY, 6, 8, 30 + randint(50), "Poison Seeker Arrow Trap"); break; case TRAP_OF_POISON_ARROW_IV: ident = player_handle_missile_trap(1, TV_BOLT, SV_AMMO_HEAVY, 8, 10, 40 + randint(70), "Poison Seeker Bolt Trap"); break; case TRAP_OF_DAGGER_I: ident = player_handle_missile_trap(1, TV_SWORD, SV_BROKEN_DAGGER, 2, 8, 0, "Dagger Trap"); break; case TRAP_OF_DAGGER_II: ident = player_handle_missile_trap(1, TV_SWORD, SV_DAGGER, 3, 8, 0, "Dagger Trap"); break; case TRAP_OF_POISON_DAGGER_I: ident = player_handle_missile_trap(1, TV_SWORD, SV_BROKEN_DAGGER, 2, 8, 15 + randint(20), "Poison Dagger Trap"); break; case TRAP_OF_POISON_DAGGER_II: ident = player_handle_missile_trap(1, TV_SWORD, SV_DAGGER, 3, 8, 20 + randint(30), "Poison Dagger Trap"); break; /* * multiple missile traps * numbers range from 2 (level 0 to 14) to 10 (level 120 and up) */ case TRAP_OF_ARROWS_I: ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_ARROW, SV_AMMO_NORMAL, 4, 8, 0, "Arrow Trap"); break; case TRAP_OF_ARROWS_II: ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_BOLT, SV_AMMO_NORMAL, 5, 8, 0, "Bolt Trap"); break; case TRAP_OF_ARROWS_III: ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_ARROW, SV_AMMO_HEAVY, 6, 8, 0, "Seeker Arrow Trap"); break; case TRAP_OF_ARROWS_IV: ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_BOLT, SV_AMMO_HEAVY, 8, 10, 0, "Seeker Bolt Trap"); break; case TRAP_OF_POISON_ARROWS_I: ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_ARROW, SV_AMMO_NORMAL, 4, 8, 10 + randint(20), "Poison Arrow Trap"); break; case TRAP_OF_POISON_ARROWS_II: ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_BOLT, SV_AMMO_NORMAL, 5, 8, 15 + randint(30), "Poison Bolt Trap"); break; case TRAP_OF_POISON_ARROWS_III: ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_ARROW, SV_AMMO_HEAVY, 6, 8, 30 + randint(50), "Poison Seeker Arrow Trap"); break; case TRAP_OF_POISON_ARROWS_IV: ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_BOLT, SV_AMMO_HEAVY, 8, 10, 40 + randint(70), "Poison Seeker Bolt Trap"); break; case TRAP_OF_DAGGERS_I: ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_SWORD, SV_BROKEN_DAGGER, 2, 8, 0, "Dagger Trap"); break; case TRAP_OF_DAGGERS_II: ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_SWORD, SV_DAGGER, 3, 8, 0, "Dagger Trap"); break; case TRAP_OF_POISON_DAGGERS_I: ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_SWORD, SV_BROKEN_DAGGER, 2, 8, 15 + randint(20), "Poison Dagger Trap"); break; case TRAP_OF_POISON_DAGGERS_II: ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_SWORD, SV_DAGGER, 3, 8, 20 + randint(30), "Poison Dagger Trap"); break; case TRAP_OF_DROP_ITEMS: { s16b i; bool_ message = FALSE; for (i = 0; i < INVEN_PACK; i++) { object_type tmp_obj; if (!p_ptr->inventory[i].k_idx) continue; if (randint(100) < 80) continue; if (p_ptr->inventory[i].name1 == ART_POWER) continue; tmp_obj = p_ptr->inventory[i]; /* drop carefully */ drop_near(&tmp_obj, 0, y, x); inc_stack_size_ex(i, -999, OPTIMIZE, NO_DESCRIBE); p_ptr->notice |= (PN_COMBINE | PN_REORDER); if (!message) { msg_print("You are startled by a sudden sound."); message = TRUE; } ident = TRUE; } if (!ident) { msg_print("You hear a sudden, strange sound."); } break; } case TRAP_OF_DROP_ALL_ITEMS: { s16b i; bool_ message = FALSE; for (i = 0; i < INVEN_PACK; i++) { object_type tmp_obj; if (!p_ptr->inventory[i].k_idx) continue; if (randint(100) < 10) continue; if (p_ptr->inventory[i].name1 == ART_POWER) continue; tmp_obj = p_ptr->inventory[i]; /* drop carefully */ drop_near(&tmp_obj, 0, y, x); inc_stack_size_ex(i, -999, OPTIMIZE, NO_DESCRIBE); p_ptr->notice |= (PN_COMBINE | PN_REORDER); if (!message) { msg_print("You are greatly startled by a sudden sound."); message = TRUE; } ident = TRUE; } if (!ident) { msg_print("You hear a sudden, strange sound."); } break; } case TRAP_OF_DROP_EVERYTHING: { s16b i; bool_ message = FALSE; for (i = 0; i < INVEN_TOTAL; i++) { object_type tmp_obj; if (!p_ptr->inventory[i].k_idx) continue; if (randint(100) < 30) continue; if (p_ptr->inventory[i].name1 == ART_POWER) continue; tmp_obj = p_ptr->inventory[i]; /* drop carefully */ drop_near(&tmp_obj, 0, y, x); inc_stack_size_ex(i, -999, OPTIMIZE, NO_DESCRIBE); p_ptr->notice |= (PN_COMBINE | PN_REORDER); if (!message) { msg_print("You are completely startled by a sudden sound."); message = TRUE; } ident = TRUE; } if (!ident) { msg_print("You hear a sudden, strange sound."); } break; } /* Bolt Trap */ case TRAP_G_ELEC_BOLT: ident = player_handle_breath_trap(1, GF_ELEC, TRAP_G_ELEC_BOLT); break; case TRAP_G_POIS_BOLT: ident = player_handle_breath_trap(1, GF_POIS, TRAP_G_POIS_BOLT); break; case TRAP_G_ACID_BOLT: ident = player_handle_breath_trap(1, GF_ACID, TRAP_G_ACID_BOLT); break; case TRAP_G_COLD_BOLT: ident = player_handle_breath_trap(1, GF_COLD, TRAP_G_COLD_BOLT); break; case TRAP_G_FIRE_BOLT: ident = player_handle_breath_trap(1, GF_FIRE, TRAP_G_FIRE_BOLT); break; case TRAP_OF_ELEC_BOLT: ident = player_handle_breath_trap(1, GF_ELEC, TRAP_OF_ELEC_BOLT); break; case TRAP_OF_POIS_BOLT: ident = player_handle_breath_trap(1, GF_POIS, TRAP_OF_POIS_BOLT); break; case TRAP_OF_ACID_BOLT: ident = player_handle_breath_trap(1, GF_ACID, TRAP_OF_ACID_BOLT); break; case TRAP_OF_COLD_BOLT: ident = player_handle_breath_trap(1, GF_COLD, TRAP_OF_COLD_BOLT); break; case TRAP_OF_FIRE_BOLT: ident = player_handle_breath_trap(1, GF_FIRE, TRAP_OF_FIRE_BOLT); break; case TRAP_OF_PLASMA_BOLT: ident = player_handle_breath_trap(1, GF_PLASMA, TRAP_OF_PLASMA_BOLT); break; case TRAP_OF_WATER_BOLT: ident = player_handle_breath_trap(1, GF_WATER, TRAP_OF_WATER_BOLT); break; case TRAP_OF_LITE_BOLT: ident = player_handle_breath_trap(1, GF_LITE, TRAP_OF_LITE_BOLT); break; case TRAP_OF_DARK_BOLT: ident = player_handle_breath_trap(1, GF_DARK, TRAP_OF_DARK_BOLT); break; case TRAP_OF_SHARDS_BOLT: ident = player_handle_breath_trap(1, GF_SHARDS, TRAP_OF_SHARDS_BOLT); break; case TRAP_OF_SOUND_BOLT: ident = player_handle_breath_trap(1, GF_SOUND, TRAP_OF_SOUND_BOLT); break; case TRAP_OF_CONFUSION_BOLT: ident = player_handle_breath_trap(1, GF_CONFUSION, TRAP_OF_CONFUSION_BOLT); break; case TRAP_OF_FORCE_BOLT: ident = player_handle_breath_trap(1, GF_FORCE, TRAP_OF_FORCE_BOLT); break; case TRAP_OF_INERTIA_BOLT: ident = player_handle_breath_trap(1, GF_INERTIA, TRAP_OF_INERTIA_BOLT); break; case TRAP_OF_MANA_BOLT: ident = player_handle_breath_trap(1, GF_MANA, TRAP_OF_MANA_BOLT); break; case TRAP_OF_ICE_BOLT: ident = player_handle_breath_trap(1, GF_ICE, TRAP_OF_ICE_BOLT); break; case TRAP_OF_CHAOS_BOLT: ident = player_handle_breath_trap(1, GF_CHAOS, TRAP_OF_CHAOS_BOLT); break; case TRAP_OF_NETHER_BOLT: ident = player_handle_breath_trap(1, GF_NETHER, TRAP_OF_NETHER_BOLT); break; case TRAP_OF_DISENCHANT_BOLT: ident = player_handle_breath_trap(1, GF_DISENCHANT, TRAP_OF_DISENCHANT_BOLT); break; case TRAP_OF_NEXUS_BOLT: ident = player_handle_breath_trap(1, GF_NEXUS, TRAP_OF_NEXUS_BOLT); break; case TRAP_OF_TIME_BOLT: ident = player_handle_breath_trap(1, GF_TIME, TRAP_OF_TIME_BOLT); break; case TRAP_OF_GRAVITY_BOLT: ident = player_handle_breath_trap(1, GF_GRAVITY, TRAP_OF_GRAVITY_BOLT); break; /* Ball Trap */ case TRAP_OF_ELEC_BALL: ident = player_handle_breath_trap(3, GF_ELEC, TRAP_OF_ELEC_BALL); break; case TRAP_OF_POIS_BALL: ident = player_handle_breath_trap(3, GF_POIS, TRAP_OF_POIS_BALL); break; case TRAP_OF_ACID_BALL: ident = player_handle_breath_trap(3, GF_ACID, TRAP_OF_ACID_BALL); break; case TRAP_OF_COLD_BALL: ident = player_handle_breath_trap(3, GF_COLD, TRAP_OF_COLD_BALL); break; case TRAP_OF_FIRE_BALL: ident = player_handle_breath_trap(3, GF_FIRE, TRAP_OF_FIRE_BALL); break; case TRAP_OF_PLASMA_BALL: ident = player_handle_breath_trap(3, GF_PLASMA, TRAP_OF_PLASMA_BALL); break; case TRAP_OF_WATER_BALL: ident = player_handle_breath_trap(3, GF_WATER, TRAP_OF_WATER_BALL); break; case TRAP_OF_LITE_BALL: ident = player_handle_breath_trap(3, GF_LITE, TRAP_OF_LITE_BALL); break; case TRAP_OF_DARK_BALL: ident = player_handle_breath_trap(3, GF_DARK, TRAP_OF_DARK_BALL); break; case TRAP_OF_SHARDS_BALL: ident = player_handle_breath_trap(3, GF_SHARDS, TRAP_OF_SHARDS_BALL); break; case TRAP_OF_SOUND_BALL: ident = player_handle_breath_trap(3, GF_SOUND, TRAP_OF_SOUND_BALL); break; case TRAP_OF_CONFUSION_BALL: ident = player_handle_breath_trap(3, GF_CONFUSION, TRAP_OF_CONFUSION_BALL); break; case TRAP_OF_FORCE_BALL: ident = player_handle_breath_trap(3, GF_FORCE, TRAP_OF_FORCE_BALL); break; case TRAP_OF_INERTIA_BALL: ident = player_handle_breath_trap(3, GF_INERTIA, TRAP_OF_INERTIA_BALL); break; case TRAP_OF_MANA_BALL: ident = player_handle_breath_trap(3, GF_MANA, TRAP_OF_MANA_BALL); break; case TRAP_OF_ICE_BALL: ident = player_handle_breath_trap(3, GF_ICE, TRAP_OF_ICE_BALL); break; case TRAP_OF_CHAOS_BALL: ident = player_handle_breath_trap(3, GF_CHAOS, TRAP_OF_CHAOS_BALL); break; case TRAP_OF_NETHER_BALL: ident = player_handle_breath_trap(3, GF_NETHER, TRAP_OF_NETHER_BALL); break; case TRAP_OF_DISENCHANT_BALL: ident = player_handle_breath_trap(3, GF_DISENCHANT, TRAP_OF_DISENCHANT_BALL); break; case TRAP_OF_NEXUS_BALL: ident = player_handle_breath_trap(3, GF_NEXUS, TRAP_OF_NEXUS_BALL); break; case TRAP_OF_TIME_BALL: ident = player_handle_breath_trap(3, GF_TIME, TRAP_OF_TIME_BALL); break; case TRAP_OF_GRAVITY_BALL: ident = player_handle_breath_trap(3, GF_GRAVITY, TRAP_OF_GRAVITY_BALL); break; /* Trap of Divine Anger */ case TRAP_OF_DIVINE_ANGER: { if (p_ptr->pgod == 0) { msg_format("Suddenly you feel glad you're a mere %s", spp_ptr->title); } else { cptr name; name = deity_info[p_ptr->pgod].name; msg_format("You feel you have angered %s.", name); inc_piety(p_ptr->pgod, -3000); } break; } /* Trap of Divine Wrath */ case TRAP_OF_DIVINE_WRATH: { if (p_ptr->pgod == 0) { msg_format("Suddenly you feel glad you're a mere %s", spp_ptr->title); } else { cptr name; name = deity_info[p_ptr->pgod].name; msg_format("%s quakes in rage: ``Thou art supremely insolent, mortal!!''", name); inc_piety(p_ptr->pgod, -500 * p_ptr->lev); } break; } /* Trap of hallucination */ case TRAP_OF_HALLUCINATION: { msg_print("Scintillating colors hypnotise you for a moment."); set_image(80); } break; /* Bolt Trap */ case TRAP_OF_ROCKET: ident = player_handle_breath_trap(1, GF_ROCKET, trap); break; case TRAP_OF_NUKE_BOLT: ident = player_handle_breath_trap(1, GF_NUKE, trap); break; case TRAP_OF_HOLY_FIRE: ident = player_handle_breath_trap(1, GF_HOLY_FIRE, trap); break; case TRAP_OF_HELL_FIRE: ident = player_handle_breath_trap(1, GF_HELL_FIRE, trap); break; case TRAP_OF_PSI_BOLT: ident = player_handle_breath_trap(1, GF_PSI, trap); break; case TRAP_OF_PSI_DRAIN: ident = player_handle_breath_trap(1, GF_PSI_DRAIN, trap); break; /* Ball Trap */ case TRAP_OF_NUKE_BALL: ident = player_handle_breath_trap(3, GF_NUKE, TRAP_OF_NUKE_BALL); break; case TRAP_OF_PSI_BALL: ident = player_handle_breath_trap(3, GF_PSI, TRAP_OF_NUKE_BALL); break; default: { msg_print(format("Executing unknown trap %d", trap)); } } return ident; } void player_activate_door_trap(s16b y, s16b x) { cave_type *c_ptr; bool_ ident = FALSE; c_ptr = &cave[y][x]; /* Return if trap or door not found */ if ((c_ptr->t_idx == 0) || !(f_info[c_ptr->feat].flags & FF_DOOR)) return; /* Disturb */ disturb(0); /* Message */ msg_print("You found a trap!"); /* Pick a trap */ pick_trap(y, x); /* Hit the trap */ ident = player_activate_trap_type(y, x, NULL, -1); if (ident) { t_info[c_ptr->t_idx].ident = TRUE; msg_format("You identified that trap as %s.", t_info[c_ptr->t_idx].name); } } /* * Places a random trap at the given location. * * The location must be a valid, empty, clean, floor grid. */ void place_trap(int y, int x) { s16b trap; trap_type *t_ptr; int cnt; u32b flags; cave_type *c_ptr = &cave[y][x]; dungeon_info_type *d_ptr = &d_info[dungeon_type]; /* No traps in town or on first level */ if (dun_level <= 1) return; /* * Avoid open doors -- because DOOR flag is added to make much more * important processing faster */ if (c_ptr->feat == FEAT_OPEN) return; if (c_ptr->feat == FEAT_BROKEN) return; /* Traps only appears on empty floor */ if (!cave_floor_grid(c_ptr) && !(f_info[c_ptr->feat].flags & FF_DOOR)) return; /* Set flags */ if (f_info[c_ptr->feat].flags & FF_DOOR) flags = FTRAP_DOOR; else flags = FTRAP_FLOOR; /* Try 100 times */ cnt = 100; while (cnt--) { trap = randint(max_t_idx - 1); t_ptr = &t_info[trap]; /* No traps below their minlevel */ if (t_ptr->minlevel > dun_level) continue; /* is this a correct trap now? */ if (!(t_ptr->flags & flags)) continue; /* * Hack -- No trap door at the bottom of dungeon or in flat * (non dungeon) places or on quest levels */ if ((trap == TRAP_OF_SINKING) && ((d_ptr->maxdepth == dun_level) || (dungeon_flags & DF_FLAT) || is_quest(dun_level)) ) { continue; } /* How probable is this trap */ if (rand_int(100) < t_ptr->probability) { c_ptr->t_idx = trap; break; } } return; } /* * Place a leveled trap at given position */ void place_trap_leveled(int y, int x, int lev) { int prev_dun_level = dun_level; dun_level = lev; place_trap(y,x); dun_level = prev_dun_level; } /* * Places a random trap on the given chest. * * The object must be a valid chest. */ void place_trap_object(object_type *o_ptr) { s16b trap; trap_type *t_ptr; int cnt; /* No traps in town or on first level */ if (dun_level <= 1) { /* empty chest were already looted, therefore known */ o_ptr->ident |= IDENT_KNOWN; return; } /* Try 100 times */ cnt = 100; while (cnt--) { trap = randint(max_t_idx - 1); t_ptr = &t_info[trap]; /* no traps below their minlevel */ if (t_ptr->minlevel > dun_level) continue; /* Is this a correct trap now? */ if (!(t_ptr->flags & FTRAP_CHEST)) continue; /* How probable is this trap */ if (rand_int(100) < t_ptr->probability) { o_ptr->pval = trap; break; } } return; } /* Dangerous trap placing function */ void wiz_place_trap(int y, int x, int idx) { cave_type *c_ptr = &cave[y][x]; /* Dangerous enough as it is... */ if (!cave_floor_grid(c_ptr) && (!(f_info[c_ptr->feat].flags & FF_DOOR))) return; c_ptr->t_idx = idx; } /* * Here begin monster traps code */ /* * Hook to determine if an object is a device */ static bool item_tester_hook_device(object_type const *o_ptr) { return (((o_ptr->tval == TV_ROD_MAIN) && (o_ptr->pval != 0)) || (o_ptr->tval == TV_STAFF) || (o_ptr->tval == TV_WAND)); } /* * The trap setting code for rogues -MWK- * * Also, it will fail or give weird results if the tvals are resorted! */ void do_cmd_set_trap(void) { int item_kit, item_load, i; int num; object_type *o_ptr, *j_ptr, *i_ptr; cptr q, s, c; object_type object_type_body; /* Check some conditions */ if (p_ptr->blind) { msg_print("You can't see anything."); return; } if (no_lite()) { msg_print("You don't dare to set a trap in the darkness."); return; } if (p_ptr->confused) { msg_print("You are too confused!"); return; } /* Only set traps on clean floor grids */ if (!cave_clean_bold(p_ptr->py, p_ptr->px)) { msg_print("You cannot set a trap on this."); return; } /* Get an item */ q = "Use which trapping kit? "; s = "You have no trapping kits."; if (!get_item(&item_kit, q, s, USE_INVEN, object_filter::TVal(TV_TRAPKIT))) { return; } o_ptr = &p_ptr->inventory[item_kit]; /* Trap kits need a second object */ object_filter_t object_filter = object_filter::Or( object_filter::And( object_filter::SVal(SV_TRAPKIT_BOW), object_filter::TVal(TV_ARROW)), object_filter::And( object_filter::SVal(SV_TRAPKIT_XBOW), object_filter::TVal(TV_BOLT)), object_filter::And( object_filter::SVal(SV_TRAPKIT_SLING), object_filter::TVal(TV_SHOT)), object_filter::And( object_filter::SVal(SV_TRAPKIT_POTION), object_filter::Or( object_filter::TVal(TV_POTION), object_filter::TVal(TV_POTION2))), object_filter::And( object_filter::SVal(SV_TRAPKIT_SCROLL), object_filter::TVal(TV_SCROLL)), object_filter::And( object_filter::SVal(SV_TRAPKIT_DEVICE), &item_tester_hook_device)); /* Get the second item */ q = "Load with what? "; s = "You have nothing to load that trap with."; if (!get_item(&item_load, q, s, USE_INVEN, object_filter)) return; /* Get the second object */ j_ptr = &p_ptr->inventory[item_load]; /* Assume a single object */ num = 1; /* In some cases, take multiple objects to load */ if (o_ptr->sval != SV_TRAPKIT_DEVICE) { auto const flags = object_flags(o_ptr); if ((flags & TR_XTRA_SHOTS) && (o_ptr->pval > 0)) num += o_ptr->pval; if (flags & (TR_AUTOMATIC_5 | TR_AUTOMATIC_99)) num = 99; if (num > j_ptr->number) num = j_ptr->number; c = format("How many (1-%d)? ", num); /* Ask for number of items to use */ num = get_quantity(c, num); } /* Canceled */ if (!num) return; /* Take a turn */ energy_use = 100; /* Get local object */ i_ptr = &object_type_body; /* Obtain local object for trap content */ object_copy(i_ptr, j_ptr); /* Set number */ i_ptr->number = num; /* Drop it here */ cave[p_ptr->py][p_ptr->px].special = floor_carry(p_ptr->py, p_ptr->px, i_ptr); /* Obtain local object for trap trigger kit */ object_copy(i_ptr, o_ptr); /* Set number */ i_ptr->number = 1; /* Drop it here */ cave[p_ptr->py][p_ptr->px].special2 = floor_carry(p_ptr->py, p_ptr->px, i_ptr); /* Modify, Describe, Optimize */ inc_stack_size_ex(item_kit, -1, NO_OPTIMIZE, DESCRIBE); inc_stack_size_ex(item_load, -num, NO_OPTIMIZE, DESCRIBE); for (i = 0; i < INVEN_WIELD; i++) { if (inven_item_optimize(i)) break; } for (i = 0; i < INVEN_WIELD; i++) { inven_item_optimize(i); } /* Actually set the trap */ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_MON_TRAP); } /* * Monster hitting a rod trap -MWK- * * Return TRUE if the monster died */ bool_ mon_hit_trap_aux_rod(int m_idx, object_type *o_ptr) { int dam = 0, typ = 0; int rad = 0; monster_type *m_ptr = &m_list[m_idx]; int y = m_ptr->fy; int x = m_ptr->fx; /* Depend on rod type */ switch (o_ptr->pval) { case SV_ROD_DETECT_TRAP: m_ptr->smart |= SM_NOTE_TRAP; break; case SV_ROD_DETECTION: m_ptr->smart |= SM_NOTE_TRAP; break; case SV_ROD_ILLUMINATION: typ = GF_LITE_WEAK; dam = damroll(2, 15); rad = 3; lite_room(y, x); break; case SV_ROD_CURING: typ = GF_OLD_HEAL; dam = damroll(3, 4); /* and heal conf? */ break; case SV_ROD_HEALING: typ = GF_OLD_HEAL; dam = 300; break; case SV_ROD_SPEED: typ = GF_OLD_SPEED; dam = 50; break; case SV_ROD_TELEPORT_AWAY: typ = GF_AWAY_ALL; dam = MAX_SIGHT * 5; break; case SV_ROD_DISARMING: break; case SV_ROD_LITE: typ = GF_LITE_WEAK; dam = damroll(6, 8); break; case SV_ROD_SLEEP_MONSTER: typ = GF_OLD_SLEEP; dam = 50; break; case SV_ROD_SLOW_MONSTER: typ = GF_OLD_SLOW; dam = 50; break; case SV_ROD_DRAIN_LIFE: typ = GF_OLD_DRAIN; dam = 75; break; case SV_ROD_POLYMORPH: typ = GF_OLD_POLY; dam = 50; break; case SV_ROD_ACID_BOLT: typ = GF_ACID; dam = damroll(6, 8); break; case SV_ROD_ELEC_BOLT: typ = GF_ELEC; dam = damroll(3, 8); break; case SV_ROD_FIRE_BOLT: typ = GF_FIRE; dam = damroll(8, 8); break; case SV_ROD_COLD_BOLT: typ = GF_COLD; dam = damroll(5, 8); break; case SV_ROD_ACID_BALL: typ = GF_ACID; dam = 60; rad = 2; break; case SV_ROD_ELEC_BALL: typ = GF_ELEC; dam = 32; rad = 2; break; case SV_ROD_FIRE_BALL: typ = GF_FIRE; dam = 72; rad = 2; break; case SV_ROD_COLD_BALL: typ = GF_COLD; dam = 48; rad = 2; break; default: return (FALSE); } /* Actually hit the monster */ if (typ) (void) project( -2, rad, y, x, dam, typ, PROJECT_KILL | PROJECT_ITEM | PROJECT_JUMP); return (cave[y][x].m_idx == 0 ? TRUE : FALSE); } /* * Monster hitting a staff trap -MWK- * * Return TRUE if the monster died */ bool_ mon_hit_trap_aux_staff(int m_idx, object_type *o_ptr) { return (FALSE); } /* * Monster hitting a scroll trap -MWK- * * Return TRUE if the monster died */ bool_ mon_hit_trap_aux_scroll(int m_idx, int sval) { monster_type *m_ptr = &m_list[m_idx]; int dam = 0, typ = 0; int rad = 0; int y = m_ptr->fy; int x = m_ptr->fx; int k; /* Depend on scroll type */ switch (sval) { case SV_SCROLL_CURSE_ARMOR: case SV_SCROLL_CURSE_WEAPON: case SV_SCROLL_TRAP_CREATION: /* these don't work :-( */ case SV_SCROLL_WORD_OF_RECALL: /* should these? */ case SV_SCROLL_IDENTIFY: case SV_SCROLL_STAR_IDENTIFY: case SV_SCROLL_MAPPING: case SV_SCROLL_DETECT_GOLD: case SV_SCROLL_DETECT_ITEM: case SV_SCROLL_REMOVE_CURSE: case SV_SCROLL_STAR_REMOVE_CURSE: case SV_SCROLL_ENCHANT_ARMOR: case SV_SCROLL_ENCHANT_WEAPON_TO_HIT: case SV_SCROLL_ENCHANT_WEAPON_TO_DAM: case SV_SCROLL_STAR_ENCHANT_ARMOR: case SV_SCROLL_STAR_ENCHANT_WEAPON: case SV_SCROLL_RECHARGING: case SV_SCROLL_DETECT_DOOR: case SV_SCROLL_DETECT_INVIS: case SV_SCROLL_SATISFY_HUNGER: case SV_SCROLL_RUNE_OF_PROTECTION: case SV_SCROLL_TRAP_DOOR_DESTRUCTION: case SV_SCROLL_PROTECTION_FROM_EVIL: return (FALSE); case SV_SCROLL_DARKNESS: unlite_room(y, x); typ = GF_DARK_WEAK; dam = 10; rad = 3; break; case SV_SCROLL_AGGRAVATE_MONSTER: aggravate_monsters(m_idx); return (FALSE); case SV_SCROLL_SUMMON_MONSTER: for (k = 0; k < randint(3) ; k++) summon_specific(y, x, dun_level, 0); return (FALSE); case SV_SCROLL_SUMMON_UNDEAD: for (k = 0; k < randint(3) ; k++) summon_specific(y, x, dun_level, SUMMON_UNDEAD); return (FALSE); case SV_SCROLL_PHASE_DOOR: typ = GF_AWAY_ALL; dam = 10; break; case SV_SCROLL_TELEPORT: typ = GF_AWAY_ALL; dam = 100; break; case SV_SCROLL_TELEPORT_LEVEL: delete_monster(y, x); return (TRUE); case SV_SCROLL_LIGHT: lite_room(y, x); typ = GF_LITE_WEAK; dam = damroll(2, 8); rad = 2; break; case SV_SCROLL_DETECT_TRAP: m_ptr->smart |= SM_NOTE_TRAP; return (FALSE); case SV_SCROLL_BLESSING: typ = GF_HOLY_FIRE; dam = damroll(1, 4); break; case SV_SCROLL_HOLY_CHANT: typ = GF_HOLY_FIRE; dam = damroll(2, 4); break; case SV_SCROLL_HOLY_PRAYER: typ = GF_HOLY_FIRE; dam = damroll(4, 4); break; case SV_SCROLL_MONSTER_CONFUSION: typ = GF_OLD_CONF; dam = damroll(5, 10); break; case SV_SCROLL_STAR_DESTRUCTION: destroy_area(y, x, 15); return (FALSE); case SV_SCROLL_DISPEL_UNDEAD: typ = GF_DISP_UNDEAD; rad = 5; dam = 60; break; case SV_SCROLL_GENOCIDE: { monster_race *r_ptr = &r_info[m_ptr->r_idx]; genocide_aux(FALSE, r_ptr->d_char); /* although there's no point in a multiple genocide trap... */ return (!(r_ptr->flags & RF_UNIQUE)); } case SV_SCROLL_MASS_GENOCIDE: for (k = 0; k < 8; k++) delete_monster(y + ddy[k], x + ddx[k]); delete_monster(y, x); return (TRUE); case SV_SCROLL_ACQUIREMENT: acquirement(y, x, 1, TRUE, FALSE); return (FALSE); case SV_SCROLL_STAR_ACQUIREMENT: acquirement(y, x, randint(2) + 1, TRUE, FALSE); return (FALSE); default: return (FALSE); } /* Actually hit the monster */ (void) project( -2, rad, y, x, dam, typ, PROJECT_KILL | PROJECT_ITEM | PROJECT_JUMP); return (cave[y][x].m_idx == 0 ? TRUE : FALSE); } /* * Monster hitting a wand trap -MWK- * * Return TRUE if the monster died */ bool_ mon_hit_trap_aux_wand(int m_idx, object_type *o_ptr) { return (FALSE); } /* * Monster hitting a potions trap -MWK- * * Return TRUE if the monster died */ bool_ mon_hit_trap_aux_potion(int m_idx, object_type *o_ptr) { monster_type *m_ptr = &m_list[m_idx]; int dam = 0, typ = 0; int y = m_ptr->fy; int x = m_ptr->fx; int sval = o_ptr->sval; /* Depend on potion type */ if (o_ptr->tval == TV_POTION) { switch (sval) { /* Nothing happens */ case SV_POTION_WATER: case SV_POTION_APPLE_JUICE: case SV_POTION_SLIME_MOLD: case SV_POTION_SALT_WATER: case SV_POTION_DEC_STR: case SV_POTION_DEC_INT: case SV_POTION_DEC_WIS: case SV_POTION_DEC_DEX: case SV_POTION_DEC_CON: case SV_POTION_DEC_CHR: case SV_POTION_INFRAVISION: case SV_POTION_DETECT_INVIS: case SV_POTION_SLOW_POISON: case SV_POTION_CURE_POISON: case SV_POTION_RESIST_HEAT: case SV_POTION_RESIST_COLD: case SV_POTION_RESTORE_MANA: case SV_POTION_RESTORE_EXP: case SV_POTION_RES_STR: case SV_POTION_RES_INT: case SV_POTION_RES_WIS: case SV_POTION_RES_DEX: case SV_POTION_RES_CON: case SV_POTION_RES_CHR: case SV_POTION_INC_STR: case SV_POTION_INC_INT: case SV_POTION_INC_WIS: case SV_POTION_INC_DEX: case SV_POTION_INC_CON: case SV_POTION_INC_CHR: case SV_POTION_AUGMENTATION: case SV_POTION_RUINATION: /* ??? */ case SV_POTION_ENLIGHTENMENT: case SV_POTION_STAR_ENLIGHTENMENT: case SV_POTION_SELF_KNOWLEDGE: return (FALSE); case SV_POTION_EXPERIENCE: if (m_ptr->level < MONSTER_LEVEL_MAX) { m_ptr->exp = monster_exp(m_ptr->level + 1); monster_check_experience(m_idx, FALSE); } return (FALSE); case SV_POTION_SLOWNESS: typ = GF_OLD_SLOW; dam = damroll(4, 6); break; case SV_POTION_POISON: typ = GF_POIS; dam = damroll(8, 6); break; case SV_POTION_CONFUSION: typ = GF_CONFUSION; dam = damroll(4, 6); break; case SV_POTION_BLINDNESS: typ = GF_DARK; dam = 10; break; case SV_POTION_SLEEP: typ = GF_OLD_SLEEP; dam = damroll (4, 6); break; case SV_POTION_LOSE_MEMORIES: typ = GF_OLD_CONF; dam = damroll(10, 10); break; case SV_POTION_DETONATIONS: typ = GF_DISINTEGRATE; dam = damroll(20, 20); break; case SV_POTION_DEATH: typ = GF_NETHER; dam = damroll(100, 20); break; case SV_POTION_BOLDNESS: m_ptr->monfear = 0; return (FALSE); case SV_POTION_SPEED: typ = GF_OLD_SPEED; dam = damroll(5, 10); break; case SV_POTION_HEROISM: case SV_POTION_BESERK_STRENGTH: m_ptr->monfear = 0; typ = GF_OLD_HEAL; dam = damroll(2, 10); break; case SV_POTION_CURE_LIGHT: typ = GF_OLD_HEAL; dam = damroll(3, 4); break; case SV_POTION_CURE_SERIOUS: typ = GF_OLD_HEAL; dam = damroll(4, 6); break; case SV_POTION_CURE_CRITICAL: typ = GF_OLD_HEAL; dam = damroll(6, 8); break; case SV_POTION_HEALING: typ = GF_OLD_HEAL; dam = 300; break; case SV_POTION_STAR_HEALING: typ = GF_OLD_HEAL; dam = 1000; break; case SV_POTION_LIFE: { monster_race *r_ptr = &r_info[m_ptr->r_idx]; if (r_ptr->flags & RF_UNDEAD) { typ = GF_HOLY_FIRE; dam = damroll(20, 20); } else { typ = GF_OLD_HEAL; dam = 5000; } break; } default: return (FALSE); } } else {} /* Actually hit the monster */ (void) project_m( -2, 0, y, x, dam, typ); return (cave[y][x].m_idx == 0 ? TRUE : FALSE); } /* * Monster hitting a monster trap -MWK- * Returns True if the monster died, false otherwise */ bool_ mon_hit_trap(int m_idx) { monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; object_type object_type_body; int mx = m_ptr->fx; int my = m_ptr->fy; int difficulty; int smartness; char m_name[80]; bool_ notice = FALSE; bool_ disarm = FALSE; bool_ remove = FALSE; bool_ dead = FALSE; bool_ fear = FALSE; s32b special = 0; int dam, chance, shots; int mul = 0; int breakage = -1; int cost = 0; /* Get the trap objects */ auto kit_o_idx = cave[my][mx].special2; auto kit_o_ptr = &o_list[kit_o_idx]; auto load_o_ptr = &o_list[cave[my][mx].special]; auto j_ptr = &object_type_body; /* Get trap properties */ auto const flags = object_flags(kit_o_ptr); /* Can set off check */ /* Ghosts only set off Ghost traps */ if ((r_ptr->flags & RF_PASS_WALL) && !(flags & TR_KILL_GHOST)) return (FALSE); /* Some traps are specialized to some creatures */ if (flags & (TR_ONLY_DRAGON | TR_ONLY_DEMON | TR_ONLY_ANIMAL | TR_ONLY_UNDEAD | TR_ONLY_EVIL)) { bool_ affect = FALSE; if ((flags & TR_ONLY_DRAGON) && (r_ptr->flags & RF_DRAGON)) affect = TRUE; if ((flags & TR_ONLY_DEMON) && (r_ptr->flags & RF_DEMON)) affect = TRUE; if ((flags & TR_ONLY_UNDEAD) && (r_ptr->flags & RF_UNDEAD)) affect = TRUE; if ((flags & TR_ONLY_EVIL) && (r_ptr->flags & RF_EVIL)) affect = TRUE; if ((flags & TR_ONLY_ANIMAL) && (r_ptr->flags & RF_ANIMAL)) affect = TRUE; /* Don't set it off if forbidden */ if (!affect) return (FALSE); } /* Get detection difficulty */ difficulty = 25; /* Some traps are well-hidden */ if (flags & TR_STEALTH) { difficulty += 10 * (kit_o_ptr->pval); } /* Get monster smartness for trap detection */ /* Higher level monsters are smarter */ smartness = r_ptr->level; /* Smart monsters are better at detecting traps */ if (r_ptr->flags & RF_SMART) smartness += 10; /* Some monsters have already noticed one of out traps */ if (m_ptr->smart & SM_NOTE_TRAP) smartness += 20; /* Stupid monsters are no good at detecting traps */ if (r_ptr->flags & (RF_STUPID | RF_EMPTY_MIND)) smartness = -150; /* Check if the monster notices the trap */ if (randint(300) > (difficulty - smartness + 150)) notice = TRUE; /* Disarm check */ if (notice) { /* The next traps will be easier to spot! */ m_ptr->smart |= SM_NOTE_TRAP; /* Get trap disarming difficulty */ difficulty = (kit_o_ptr->ac + kit_o_ptr->to_a); /* Get monster disarming ability */ /* Higher level monsters are better */ smartness = r_ptr->level / 5; /* Smart monsters are better at disarming */ if (r_ptr->flags & RF_SMART) smartness *= 2; /* Stupid monsters never disarm traps */ if (r_ptr->flags & RF_STUPID) smartness = -150; /* Nonsmart animals never disarm traps */ if ((r_ptr->flags & RF_ANIMAL) && !(r_ptr->flags & RF_SMART)) smartness = -150; /* Check if the monster disarms the trap */ if (randint(120) > (difficulty - smartness + 80)) disarm = TRUE; } /* If disarmed, remove the trap and print a message */ if (disarm) { remove = TRUE; if (m_ptr->ml) { /* Get the name */ monster_desc(m_name, m_ptr, 0); /* Print a message */ msg_format("%^s disarms a trap!", m_name); } } /* Otherwise, activate the trap! */ else { /* Message for visible monster */ if (m_ptr->ml) { /* Get the name */ monster_desc(m_name, m_ptr, 0); /* Print a message */ msg_format("%^s sets off a trap!", m_name); } else { /* No message if monster isn't visible ? */ } /* Next time be more careful */ if (randint(100) < 80) m_ptr->smart |= SM_NOTE_TRAP; /* Actually activate the trap */ switch (kit_o_ptr->sval) { case SV_TRAPKIT_BOW: case SV_TRAPKIT_XBOW: case SV_TRAPKIT_SLING: { /* Get number of shots */ shots = 1; if (flags & TR_XTRA_SHOTS) shots += kit_o_ptr->pval; if (shots <= 0) shots = 1; if (shots > load_o_ptr->number) shots = load_o_ptr->number; while (shots-- && !dead) { /* Total base damage */ dam = damroll(load_o_ptr->dd, load_o_ptr->ds) + load_o_ptr->to_d + kit_o_ptr->to_d; /* Total hit probability */ chance = (kit_o_ptr->to_h + load_o_ptr->to_h + 20) * BTH_PLUS_ADJ; /* Damage multiplier */ if (kit_o_ptr->sval == SV_TRAPKIT_BOW) mul = 3; if (kit_o_ptr->sval == SV_TRAPKIT_XBOW) mul = 4; if (kit_o_ptr->sval == SV_TRAPKIT_SLING) mul = 2; if (flags & TR_XTRA_MIGHT) mul += kit_o_ptr->pval; if (mul < 0) mul = 0; /* Multiply damage */ dam *= mul; /* Check if we hit the monster */ if (test_hit_fire(chance, r_ptr->ac, TRUE)) { /* Assume a default death */ cptr note_dies = " dies."; /* Some monsters get "destroyed" */ if ((r_ptr->flags & RF_DEMON) || (r_ptr->flags & RF_UNDEAD) || (r_ptr->flags & RF_STUPID) || (strchr("Evg", r_ptr->d_char))) { /* Special note at death */ note_dies = " is destroyed."; } /* Message if visible */ if (m_ptr->ml) { /* describe the monster (again, just in case :-) */ monster_desc(m_name, m_ptr, 0); /* Print a message */ msg_format("%^s is hit by a missile.", m_name); } /* Apply slays, brand, critical hits */ dam = tot_dam_aux(load_o_ptr, dam, m_ptr, &special); dam = critical_shot(load_o_ptr->weight, load_o_ptr->to_h, dam, SKILL_ARCHERY); /* No negative damage */ if (dam < 0) dam = 0; /* Hit the monster, check for death */ if (mon_take_hit(m_idx, dam, &fear, note_dies)) { /* Dead monster */ dead = TRUE; } /* No death */ else { /* Message */ message_pain(m_idx, dam); if (special) attack_special(m_ptr, special, dam); /* Take note */ if (fear && m_ptr->ml) { /* Message */ msg_format("%^s flees in terror!", m_name); } } } /* Exploding ammo */ if (load_o_ptr->pval2 != 0) { int rad = 0; int dam = (damroll(load_o_ptr->dd, load_o_ptr->ds) + load_o_ptr->to_d)*2; int flag = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_JUMP; switch (load_o_ptr->sval) { case SV_AMMO_LIGHT: rad = 2; dam /= 2; break; case SV_AMMO_NORMAL: rad = 3; break; case SV_AMMO_HEAVY: rad = 4; dam *= 2; break; } project(0, rad, my, mx, dam, load_o_ptr->pval2, flag); breakage = 100; } else { breakage = breakage_chance(load_o_ptr); } /* Copy and decrease ammo */ object_copy(j_ptr, load_o_ptr); j_ptr->number = 1; load_o_ptr->number--; if (load_o_ptr->number <= 0) { remove = TRUE; delete_object_idx(kit_o_idx); } /* Drop (or break) near that location */ drop_near(j_ptr, breakage, my, mx); } break; } case SV_TRAPKIT_POTION: { /* Get number of shots */ shots = 1; if (flags & TR_XTRA_SHOTS) shots += kit_o_ptr->pval; if (shots <= 0) shots = 1; if (shots > load_o_ptr->number) shots = load_o_ptr->number; while (shots-- && !dead) { /* Message if visible */ if (m_ptr->ml) { /* describe the monster (again, just in case :-) */ monster_desc(m_name, m_ptr, 0); /* Print a message */ msg_format("%^s is hit by fumes.", m_name); } /* Get the potion effect */ dead = mon_hit_trap_aux_potion(m_idx, load_o_ptr); /* Copy and decrease ammo */ object_copy(j_ptr, load_o_ptr); j_ptr->number = 1; load_o_ptr->number--; if (load_o_ptr->number <= 0) { remove = TRUE; delete_object_idx(kit_o_idx); } } break; } case SV_TRAPKIT_SCROLL: { /* Get number of shots */ shots = 1; if (flags & TR_XTRA_SHOTS) shots += kit_o_ptr->pval; if (shots <= 0) shots = 1; if (shots > load_o_ptr->number) shots = load_o_ptr->number; while (shots-- && !dead) { /* Message if visible */ if (m_ptr->ml) { /* describe the monster (again, just in case :-) */ monster_desc(m_name, m_ptr, 0); /* Print a message */ msg_format("%^s activates a spell!", m_name); } /* Get the potion effect */ dead = mon_hit_trap_aux_scroll(m_idx, load_o_ptr->sval); /* Copy and decrease ammo */ object_copy(j_ptr, load_o_ptr); j_ptr->number = 1; load_o_ptr->number--; if (load_o_ptr->number <= 0) { remove = TRUE; delete_object_idx(kit_o_idx); } } break; } case SV_TRAPKIT_DEVICE: { if (load_o_ptr->tval == TV_ROD_MAIN) { /* Extract mana cost of the rod tip */ object_kind *tip_o_ptr = &k_info[lookup_kind(TV_ROD, load_o_ptr->pval)]; auto const tflags = object_flags(load_o_ptr); cost = (tflags & TR_CHEAPNESS) ? tip_o_ptr->pval / 2 : tip_o_ptr->pval; if (cost <= 0) cost = 1; } /* Get number of shots */ shots = 1; if (flags & TR_XTRA_SHOTS) shots += kit_o_ptr->pval; if (shots <= 0) shots = 1; if (load_o_ptr->tval == TV_ROD_MAIN) { if (shots > load_o_ptr->timeout / cost) shots = load_o_ptr->timeout / cost; } else { if (shots > load_o_ptr->pval) shots = load_o_ptr->pval; } while (shots-- && !dead) { /* Get the effect effect */ switch (load_o_ptr->tval) { case TV_ROD_MAIN: dead = mon_hit_trap_aux_rod(m_idx, load_o_ptr); break; case TV_WAND: dead = mon_hit_trap_aux_wand(m_idx, load_o_ptr); break; case TV_STAFF: dead = mon_hit_trap_aux_staff(m_idx, load_o_ptr); break; } if (load_o_ptr->tval == TV_ROD_MAIN) { /* decrease stored mana (timeout) for rods */ load_o_ptr->timeout -= cost; } else { /* decrease charges for wands and staves */ load_o_ptr->pval--; } } break; } default: msg_print("oops! nonexistant trap!"); } /* Non-automatic traps are removed */ if (!(flags & (TR_AUTOMATIC_5 | TR_AUTOMATIC_99))) { remove = TRUE; } else if (flags & TR_AUTOMATIC_5) remove = (randint(5) == 1); } /* Special trap effect -- teleport to */ if ((flags & TR_TELEPORT_TO) && (!disarm) && (!dead)) { teleport_monster_to(m_idx, p_ptr->py, p_ptr->px); } /* Remove the trap if inactive now */ if (remove) place_floor_convert_glass(my, mx); /* did it die? */ return (dead); }