void object_notice_attack_plusses(object_type *o_ptr) { if (!o_ptr->k_idx) return; if (object_attack_plusses_are_visible(o_ptr)) return; if (object_add_ident_flags(o_ptr, IDENT_ATTACK)) object_check_for_ident(o_ptr); if (wield_slot(o_ptr) == INVEN_WIELD) { char o_name[80]; object_desc(o_name, sizeof(o_name), o_ptr, ODESC_BASE); message_format(MSG_PSEUDOID, 0, "You know more about the %s you are using.", o_name); } else if ((o_ptr->to_d || o_ptr->to_h) && !((o_ptr->tval == TV_HARD_ARMOR || o_ptr->tval == TV_SOFT_ARMOR) && (o_ptr->to_h < 0))) { char o_name[80]; object_desc(o_name, sizeof(o_name), o_ptr, ODESC_BASE); message_format(MSG_PSEUDOID, 0, "Your %s glows.", o_name); } p_ptr->update |= (PU_BONUS); event_signal(EVENT_INVENTORY); event_signal(EVENT_EQUIPMENT); }
/* Destroy an item */ void do_cmd_destroy(cmd_code code, cmd_arg args[]) { object_type *o_ptr; int item = args[0].item; if (!item_is_available(item, NULL, USE_INVEN | USE_EQUIP | USE_FLOOR)) { msg_print("You do not have that item to ignore it."); return; } o_ptr = object_from_item_idx(item); if ((item >= INVEN_WIELD) && cursed_p(o_ptr)) { msg_print("You cannot ignore cursed items."); } else { char o_name[80]; object_desc(o_name, sizeof o_name, o_ptr, ODESC_PREFIX | ODESC_FULL); message_format(MSG_DESTROY, 0, "Ignoring %s.", o_name); o_ptr->ignore = TRUE; p_ptr->notice |= PN_SQUELCH; } }
int main_draw_call(int argc, char* argv[]) { std::vector<entry> Entries; for(glm::uint TileSizeIndex = 3; TileSizeIndex < 4; ++TileSizeIndex) { for(std::size_t DrawPerTile = 1; DrawPerTile <= 512; DrawPerTile <<= 1) Entries.push_back(entry( message_format("window(%d), tile(%d), triangle-per-draw(%d)", 64 * (TileSizeIndex + 1), 8 * (TileSizeIndex + 1), 1024 / DrawPerTile), glm::uvec2(64) * (TileSizeIndex + 1), glm::vec2(glm::uvec2(8, 8) * (TileSizeIndex + 1)), 512, DrawPerTile, LAYOUT_LINEAR, DRAW_PER_TILE)); } csv CSV; int Error(0); for(std::size_t EntryIndex(0); EntryIndex < Entries.size(); ++EntryIndex) { test_draw_call Test( argc, argv, 1000, Entries[EntryIndex].WindowSize, Entries[EntryIndex].TileSize, Entries[EntryIndex].TrianglePairPerTile, Entries[EntryIndex].DrawPerTile, Entries[EntryIndex].Layout, Entries[EntryIndex].DrawMode); Error += Test(); Test.log(CSV, Entries[EntryIndex].String.c_str()); } CSV.save("../main_draw_call.csv"); return Error; }
/* * Determine if a given grid may be "walked" */ static bool do_cmd_walk_test(int y, int x) { /* Allow attack on visible monsters if unafraid */ if ((cave_m_idx[y][x] > 0) && (mon_list[cave_m_idx[y][x]].ml)) { /* Handle player fear */ if(p_ptr->state.afraid) { /* Extract monster name (or "it") */ char m_name[80]; monster_type *m_ptr; m_ptr = &mon_list[cave_m_idx[y][x]]; monster_desc(m_name, sizeof(m_name), m_ptr, 0); /* Message */ message_format(MSG_AFRAID, 0, "You are too afraid to attack %s!", m_name); /* Nope */ return (FALSE); } return (TRUE); } /* If we don't know the grid, allow attempts to walk into it */ if (!(cave_info[y][x] & CAVE_MARK)) return TRUE; /* Require open space or PASS_WALL/KILL_WALL flag -Simon */ if (!cave_floor_bold(y, x) && !(player_has(PF_PASS_WALL) || player_has(PF_KILL_WALL))) { /* Rubble */ if (cave_feat[y][x] == FEAT_RUBBLE) message(MSG_HITWALL, 0, "There is a pile of rubble in the way!"); /* Door */ else if (cave_feat[y][x] < FEAT_SECRET) return TRUE; /* Wall */ else message(MSG_HITWALL, 0, "There is a wall in the way!"); /* Cancel repeat */ disturb(0, 0); /* Nope */ return (FALSE); } /* Okay */ return (TRUE); }
static void object_notice_defence_plusses(object_type *o_ptr) { if (!o_ptr->k_idx) return; if (object_defence_plusses_are_visible(o_ptr)) return; if (object_add_ident_flags(o_ptr, IDENT_DEFENCE)) object_check_for_ident(o_ptr); if (o_ptr->ac || o_ptr->to_a) { char o_name[80]; object_desc(o_name, sizeof(o_name), o_ptr, ODESC_BASE); message_format(MSG_PSEUDOID, 0, "You know more about the %s you are wearing.", o_name); } p_ptr->update |= (PU_BONUS); event_signal(EVENT_INVENTORY); event_signal(EVENT_EQUIPMENT); }
/* * Perform the basic "disarm" command * * Assume there is no monster blocking the destination * * Returns TRUE if repeated commands may continue */ static bool do_cmd_disarm_aux(int y, int x) { int i, j, power; cptr name; bool more = FALSE; /* Verify legality */ if (!do_cmd_disarm_test(y, x)) return (FALSE); /* Get the trap name */ name = f_info[cave_feat[y][x]].name; /* Get the "disarm" factor */ i = p_ptr->state.skills[SKILL_DISARM]; /* Penalize some conditions */ if (p_ptr->timed[TMD_BLIND] || no_light()) i = i / 10; if (p_ptr->timed[TMD_CONFUSED] || p_ptr->timed[TMD_IMAGE]) i = i / 10; /* XXX XXX XXX Variable power? */ /* Extract trap "power" */ power = 5; /* Extract the difficulty */ j = i - power; /* Always have a small chance of success */ if (j < 2) j = 2; /* Success */ if (randint0(100) < j) { /* Message */ message_format(MSG_DISARM, 0, "You have disarmed the %s.", name); /* Reward */ gain_exp(power); /* Forget the trap */ cave_info[y][x] &= ~(CAVE_MARK); /* Remove the trap */ cave_set_feat(y, x, FEAT_FLOOR); } /* Failure -- Keep trying */ else if ((i > 5) && (randint1(i) > 5)) { flush(); /* Message */ msg_format("You failed to disarm the %s.", name); /* We may keep trying */ more = TRUE; } /* Failure -- Set off the trap */ else { /* Message */ msg_format("You set off the %s!", name); /* Hit the trap */ hit_trap(y, x); } /* Result */ return (more); }
/** * Advance experience levels and print experience */ void check_experience(void) { int i; /* Note current level */ i = p_ptr->lev; /* Hack -- lower limit */ if (p_ptr->exp < 0) p_ptr->exp = 0; /* Hack -- lower limit */ if (p_ptr->max_exp < 0) p_ptr->max_exp = 0; /* Hack -- upper limit */ if (p_ptr->exp > PY_MAX_EXP) p_ptr->exp = PY_MAX_EXP; /* Hack -- upper limit */ if (p_ptr->max_exp > PY_MAX_EXP) p_ptr->max_exp = PY_MAX_EXP; /* Hack -- maintain "max" experience */ if (p_ptr->exp > p_ptr->max_exp) p_ptr->max_exp = p_ptr->exp; /* Redraw experience */ p_ptr->redraw |= (PR_EXP); /* Handle stuff */ handle_stuff(); /* Lose levels while possible */ while ((p_ptr->lev > 1) && (p_ptr->exp < (player_exp[p_ptr->lev - 2]))) { /* Lose a level */ p_ptr->lev--; /* Update some stuff */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_SPECIALTY); /* Redraw some stuff */ p_ptr->redraw |= (PR_EXP | PR_LEV | PR_TITLE); /* Handle stuff */ handle_stuff(); } /* Gain levels while possible */ while ((p_ptr->lev < PY_MAX_LEVEL) && (p_ptr->exp >= (player_exp[p_ptr->lev - 1]))) { bool first_time = FALSE; /* Gain a level */ p_ptr->lev++; /* Save the highest level */ if (p_ptr->lev > p_ptr->max_lev) { p_ptr->max_lev = p_ptr->lev; first_time = TRUE; } /* Sound */ sound(MSG_LEVEL); /* Message */ message_format(MSG_LEVEL, p_ptr->lev, "Welcome to level %d.", p_ptr->lev); /* Write a note to the file every 5th level. */ if (((p_ptr->lev % 5) == 0) && first_time) { char buf[120]; /* Log level updates */ strnfmt(buf, sizeof(buf), "Reached level %d", p_ptr->lev); history_add(buf, HISTORY_GAIN_LEVEL, 0); } /* Update some stuff */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_SPECIALTY); /* Redraw some stuff */ p_ptr->redraw |= (PR_EXP | PR_LEV | PR_TITLE); /* Handle stuff */ handle_stuff(); } /* Gain max levels while possible Called rarely - only when leveling while * experience is drained. */ while ((p_ptr->max_lev < PY_MAX_LEVEL) && (p_ptr->max_exp >= (player_exp[p_ptr->max_lev - 1]))) { /* Gain max level */ p_ptr->max_lev++; /* Update some stuff */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_SPECIALTY); /* Redraw some stuff */ p_ptr->redraw |= (PR_LEV | PR_TITLE); /* Handle stuff */ handle_stuff(); } }
/* * Wield or wear a single item from the pack or floor */ void wield_item(object_type *o_ptr, int item, int slot) { object_type object_type_body; object_type *i_ptr = &object_type_body; cptr fmt; char o_name[80]; bool combined_ammo = FALSE; int num = 1; /* If we are stacking ammo in the quiver */ if (obj_is_ammo(o_ptr)) { num = o_ptr->number; combined_ammo = object_similar(o_ptr, &p_ptr->inventory[slot], OSTACK_QUIVER); } /* Take a turn */ p_ptr->energy_use = 100; /* Obtain local object */ object_copy(i_ptr, o_ptr); /* Modify quantity */ i_ptr->number = num; /* Decrease the item (from the pack) */ if (item >= 0) { inven_item_increase(item, -num); inven_item_optimize(item); } /* Decrease the item (from the floor) */ else { floor_item_increase(0 - item, -num); floor_item_optimize(0 - item); } /* Get the wield slot */ o_ptr = &p_ptr->inventory[slot]; if (combined_ammo) { /* Add the new ammo to the already-quiver-ed ammo */ object_absorb(o_ptr, i_ptr); } else { /* Take off existing item */ if (o_ptr->k_idx) (void)inven_takeoff(slot, 255); /* If we are wielding ammo we may need to "open" the slot by shifting * later ammo up the quiver; this is because we already called the * inven_item_optimize() function. */ if (slot >= QUIVER_START) open_quiver_slot(slot); /* Wear the new stuff */ object_copy(o_ptr, i_ptr); /* Increment the equip counter by hand */ p_ptr->equip_cnt++; } /* Increase the weight */ p_ptr->total_weight += i_ptr->weight * num; /* Do any ID-on-wield */ object_notice_on_wield(o_ptr); /* Where is the item now */ if (slot == INVEN_WIELD) fmt = "You are wielding %s (%c)."; else if (slot == INVEN_BOW) fmt = "You are shooting with %s (%c)."; else if (slot == INVEN_LIGHT) fmt = "Your light source is %s (%c)."; else if (combined_ammo) fmt = "You combine %s in your quiver (%c)."; else if (slot >= QUIVER_START && slot < QUIVER_END) fmt = "You add %s to your quiver (%c)."; else fmt = "You are wearing %s (%c)."; /* Describe the result */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); /* Message */ message_format(MSG_WIELD, 0, fmt, o_name, index_to_label(slot)); /* Cursed! */ if (cursed_p(o_ptr)) { /* Warn the player */ message_format(MSG_CURSED, 0, "Oops! It feels deathly cold!"); /* Sense the object */ object_notice_curses(o_ptr); } /* Save quiver size */ save_quiver_size(p_ptr); /* See if we have to overflow the pack */ pack_overflow(); /* Recalculate bonuses, torch, mana */ p_ptr->notice |= PN_SORT_QUIVER; p_ptr->update |= (PU_BONUS | PU_TORCH | PU_MANA); p_ptr->redraw |= (PR_INVEN | PR_EQUIP); }
/* * Set "p_ptr->food", notice observable changes * * The "p_ptr->food" variable can get as large as 20000, allowing the * addition of the most "filling" item, Elvish Waybread, which adds * 7500 food units, without overflowing the 32767 maximum limit. * * Perhaps we should disturb the player with various messages, * especially messages about hunger status changes. XXX XXX XXX * * Digestion of food is handled in "dungeon.c", in which, normally, * the player digests about 20 food units per 100 game turns, more * when "fast", more when "regenerating", less with "slow digestion", * but when the player is "gorged", he digests 100 food units per 10 * game turns, or a full 1000 food units per 100 game turns. * * Note that the player's speed is reduced by 10 units while gorged, * so if the player eats a single food ration (5000 food units) when * full (15000 food units), he will be gorged for (5000/100)*10 = 500 * game turns, or 500/(100/5) = 25 player turns (if nothing else is * affecting the player speed). */ bool set_food(int v) { int old_aux, new_aux; bool notice = FALSE; /* Hack -- Force good values */ v = MIN(v, PY_FOOD_UPPER); v = MAX(v, 0); /* Fainting / Starving */ if (p_ptr->food < PY_FOOD_FAINT) { old_aux = 0; } /* Weak */ else if (p_ptr->food < PY_FOOD_WEAK) { old_aux = 1; } /* Hungry */ else if (p_ptr->food < PY_FOOD_ALERT) { old_aux = 2; } /* Normal */ else if (p_ptr->food < PY_FOOD_FULL) { old_aux = 3; } /* Full */ else if (p_ptr->food < PY_FOOD_MAX) { old_aux = 4; } /* Gorged */ else { old_aux = 5; } /* Fainting / Starving */ if (v < PY_FOOD_FAINT) { new_aux = 0; } /* Weak */ else if (v < PY_FOOD_WEAK) { new_aux = 1; } /* Hungry */ else if (v < PY_FOOD_ALERT) { new_aux = 2; } /* Normal */ else if (v < PY_FOOD_FULL) { new_aux = 3; } /* Full */ else if (v < PY_FOOD_MAX) { new_aux = 4; } /* Gorged */ else { new_aux = 5; } /* Food increase */ if (new_aux > old_aux) { /* Describe the state */ switch (new_aux) { /* Weak */ case 1: { msg_print("You are still weak."); break; } /* Hungry */ case 2: { msg_print("You are still hungry."); break; } /* Normal */ case 3: { msg_print("You are no longer hungry."); break; } /* Full */ case 4: { msg_print("You are full!"); break; } /* Bloated */ case 5: { msg_print("You have gorged yourself!"); break; } } /* Change */ notice = TRUE; } /* Food decrease */ else if (new_aux < old_aux) { /* Describe the state */ switch (new_aux) { /* Fainting / Starving */ case 0: { message_format(MSG_NOTICE, 0, "You are getting faint from hunger!"); break; } /* Weak */ case 1: { message_format(MSG_NOTICE, 0, "You are getting weak from hunger!"); break; } /* Hungry */ case 2: { message_format(MSG_HUNGRY, 0, "You are getting hungry."); break; } /* Normal */ case 3: { message_format(MSG_NOTICE, 0, "You are no longer full."); break; } /* Full */ case 4: { message_format(MSG_NOTICE, 0, "You are no longer gorged."); break; } } /* Change */ notice = TRUE; } /* Use the value */ p_ptr->food = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (OPT(disturb_state)) disturb(0, 0); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Redraw hunger */ p_ptr->redraw |= (PR_STATUS); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); }
/* * Fire an object from the pack or floor. * * You may only fire items that "match" your missile launcher. * * See "calc_bonuses()" for more calculations and such. * * Note that "firing" a missile is MUCH better than "throwing" it. * * Note: "unseen" monsters are very hard to hit. * * Objects are more likely to break if they "attempt" to hit a monster. * * Rangers (with Bows) and Anyone (with "Extra Shots") get extra shots. * The "extra shot" code works by decreasing the amount of energy * required to make each shot, spreading the shots out over time. * * Note that when firing missiles, the launcher multiplier is applied * after all the bonuses are added in, making multipliers very useful. * * Note that Bows of "Extra Might" get extra range and an extra bonus * for the damage multiplier. */ void do_cmd_fire(cmd_code code, cmd_arg args[]) { int dir, item; int i, j, y, x; s16b ty, tx; int tdam, tdis, thits; int bonus, chance; object_type *o_ptr; object_type *j_ptr; object_type *i_ptr; object_type object_type_body; bool hit_body = FALSE; byte missile_attr; char missile_char; char o_name[80]; u32b msg_type = 0; int path_n; u16b path_g[256]; int msec = op_ptr->delay_factor * op_ptr->delay_factor; /* Get the "bow" */ j_ptr = &p_ptr->inventory[INVEN_BOW]; /* Require a usable launcher */ if (!j_ptr->tval || !p_ptr->state.ammo_tval) { msg_print("You have nothing to fire with."); return; } /* Get item to fire and direction to fire in. */ item = args[0].item; dir = args[1].direction; /* Check the item being fired is usable by the player. */ if (!item_is_available(item, NULL, (USE_EQUIP | USE_INVEN | USE_FLOOR))) { msg_format("That item is not within your reach."); return; } /* Get the object for the ammo */ o_ptr = object_from_item_idx(item); /* Check the ammo can be used with the launcher */ if (o_ptr->tval != p_ptr->state.ammo_tval) { msg_format("That ammo cannot be fired by your current weapon."); return; } /* Base range XXX XXX */ tdis = 6 + 2 * p_ptr->state.ammo_mult; /* Start at the player */ x = p_ptr->px; y = p_ptr->py; /* Predict the "target" location */ ty = y + 99 * ddy[dir]; tx = x + 99 * ddx[dir]; /* Check for target validity */ if ((dir == 5) && target_okay()) { target_get(&tx, &ty); if (distance(y, x, ty, tx) > tdis) { if (!get_check("Target out of range. Fire anyway? ")) return; } } /* Sound */ sound(MSG_SHOOT); object_notice_on_firing(o_ptr); /* Describe the object */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_FULL | ODESC_SINGULAR); /* Find the color and symbol for the object for throwing */ missile_attr = object_attr(o_ptr); missile_char = object_char(o_ptr); /* Use the proper number of shots */ thits = p_ptr->state.num_fire; /* Actually "fire" the object */ bonus = (p_ptr->state.to_h + o_ptr->to_h + j_ptr->to_h); chance = p_ptr->state.skills[SKILL_TO_HIT_BOW] + (bonus * BTH_PLUS_ADJ); /* Take a (partial) turn */ p_ptr->energy_use = (100 / thits); /* Calculate the path */ path_n = project_path(path_g, tdis, y, x, ty, tx, 0); /* Hack -- Handle stuff */ handle_stuff(); /* Project along the path */ for (i = 0; i < path_n; ++i) { int ny = GRID_Y(path_g[i]); int nx = GRID_X(path_g[i]); /* Hack -- Stop before hitting walls */ if (!cave_floor_bold(ny, nx)) break; /* Advance */ x = nx; y = ny; /* Only do visuals if the player can "see" the missile */ if (player_can_see_bold(y, x)) { /* Visual effects */ print_rel(missile_char, missile_attr, y, x); move_cursor_relative(y, x); Term_fresh(); if (p_ptr->redraw) redraw_stuff(); Term_xtra(TERM_XTRA_DELAY, msec); light_spot(y, x); Term_fresh(); if (p_ptr->redraw) redraw_stuff(); } /* Delay anyway for consistency */ else { /* Pause anyway, for consistancy */ Term_xtra(TERM_XTRA_DELAY, msec); } /* Handle monster */ if (cave_m_idx[y][x] > 0) { monster_type *m_ptr = &mon_list[cave_m_idx[y][x]]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; int chance2 = chance - distance(p_ptr->py, p_ptr->px, y, x); int visible = m_ptr->ml; int multiplier = 1; const char *hit_verb = "hits"; const slay_t *best_s_ptr = NULL; /* Note the collision */ hit_body = TRUE; /* Did we hit it (penalize distance travelled) */ if (test_hit(chance2, r_ptr->ac, m_ptr->ml)) { bool fear = FALSE; /* Assume a default death */ cptr note_dies = " dies."; improve_attack_modifier(o_ptr, m_ptr, &best_s_ptr); improve_attack_modifier(j_ptr, m_ptr, &best_s_ptr); if (best_s_ptr != NULL) hit_verb = best_s_ptr->range_verb; /* Some monsters get "destroyed" */ if (monster_is_unusual(r_ptr)) { /* Special note at death */ note_dies = " is destroyed."; } /* Calculate multiplier */ multiplier = p_ptr->state.ammo_mult; if (best_s_ptr != NULL) multiplier += best_s_ptr->mult; /* Apply damage: multiplier, slays, criticals, bonuses */ tdam = damroll(o_ptr->dd, o_ptr->ds); tdam += o_ptr->to_d + j_ptr->to_d; tdam *= multiplier; tdam = critical_shot(o_ptr->weight, o_ptr->to_h, tdam, &msg_type); object_notice_attack_plusses(o_ptr); object_notice_attack_plusses(&p_ptr->inventory[INVEN_BOW]); /* No negative damage; change verb if no damage done */ if (tdam <= 0) { tdam = 0; hit_verb = "fail to harm"; } /* Handle unseen monster */ if (!visible) { /* Invisible monster */ message_format(MSG_SHOOT_HIT, 0, "The %s finds a mark.", o_name); } /* Handle visible monster */ else { char m_name[80]; /* Get "the monster" or "it" */ monster_desc(m_name, sizeof(m_name), m_ptr, 0); /* Tell the player what happened */ if (msg_type == MSG_SHOOT_HIT) message_format(MSG_SHOOT_HIT, 0, "The %s %s %s.", o_name, hit_verb, m_name); else { if (msg_type == MSG_HIT_GOOD) { message_format(MSG_HIT_GOOD, 0, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a good hit!"); } else if (msg_type == MSG_HIT_GREAT) { message_format(MSG_HIT_GREAT, 0, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a great hit!"); } else if (msg_type == MSG_HIT_SUPERB) { message_format(MSG_HIT_SUPERB, 0, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a superb hit!"); } } /* Hack -- Track this monster race */ if (m_ptr->ml) monster_race_track(m_ptr->r_idx); /* Hack -- Track this monster */ if (m_ptr->ml) health_track(cave_m_idx[y][x]); } /* Complex message */ if (p_ptr->wizard) { msg_format("You do %d (out of %d) damage.", tdam, m_ptr->hp); } /* Hit the monster, check for death */ if (mon_take_hit(cave_m_idx[y][x], tdam, &fear, note_dies)) { /* Dead monster */ } /* No death */ else { /* Message */ message_pain(cave_m_idx[y][x], tdam); /* Take note */ if (fear && m_ptr->ml) { char m_name[80]; /* Get the monster name (or "it") */ monster_desc(m_name, sizeof(m_name), m_ptr, 0); /* Message */ message_format(MSG_FLEE, m_ptr->r_idx, "%^s flees in terror!", m_name); } } } /* Stop looking */ break; } } /* Get local object */ i_ptr = &object_type_body; /* Obtain a local object */ object_copy(i_ptr, o_ptr); /* Single object */ i_ptr->number = 1; if (item >= 0) { inven_item_increase(item, -1); inven_item_describe(item); inven_item_optimize(item); } /* Reduce and describe floor item */ else { floor_item_increase(0 - item, -1); floor_item_optimize(0 - item); } /* Chance of breakage (during attacks) */ j = (hit_body ? breakage_chance(i_ptr) : 0); /* Drop (or break) near that location */ drop_near(i_ptr, j, y, x, TRUE); }
/* * Throw an object from the pack or floor. * * Note: "unseen" monsters are very hard to hit. * * Should throwing a weapon do full damage? Should it allow the magic * to hit bonus of the weapon to have an effect? Should it ever cause * the item to be destroyed? Should it do any damage at all? */ void do_cmd_throw(cmd_code code, cmd_arg args[]) { int dir, item; int i, j, y, x; s16b ty, tx; int chance, tdam, tdis; int weight; object_type *o_ptr; object_type *i_ptr; object_type object_type_body; bool hit_body = FALSE; byte missile_attr; char missile_char; char o_name[80]; u32b msg_type = 0; int path_n; u16b path_g[256]; int msec = op_ptr->delay_factor * op_ptr->delay_factor; /* Get item to throw and direction in which to throw it. */ item = args[0].item; dir = args[1].direction; /* Make sure the player isn't throwing wielded items */ if (item >= INVEN_WIELD && item < QUIVER_START) { msg_print("You have cannot throw wielded items."); return; } /* Check the item being thrown is usable by the player. */ if (!item_is_available(item, NULL, (USE_EQUIP | USE_INVEN | USE_FLOOR))) { msg_format("That item is not within your reach."); return; } /* Get the object */ o_ptr = object_from_item_idx(item); object_notice_on_firing(o_ptr); /* Get local object */ i_ptr = &object_type_body; /* Obtain a local object */ object_copy(i_ptr, o_ptr); /* Distribute the charges of rods/wands/staves between the stacks */ distribute_charges(o_ptr, i_ptr, 1); /* Single object */ i_ptr->number = 1; /* Reduce and describe inventory */ if (item >= 0) { inven_item_increase(item, -1); inven_item_describe(item); inven_item_optimize(item); } /* Reduce and describe floor item */ else { floor_item_increase(0 - item, -1); floor_item_optimize(0 - item); } /* Description */ object_desc(o_name, sizeof(o_name), i_ptr, ODESC_FULL); /* Find the color and symbol for the object for throwing */ missile_attr = object_attr(i_ptr); missile_char = object_char(i_ptr); /* Enforce a minimum "weight" of one pound */ weight = ((i_ptr->weight > 10) ? i_ptr->weight : 10); /* Hack -- Distance -- Reward strength, penalize weight */ tdis = (adj_str_blow[p_ptr->state.stat_ind[A_STR]] + 20) * 10 / weight; /* Max distance of 10 */ if (tdis > 10) tdis = 10; /* Hack -- Base damage from thrown object */ tdam = damroll(i_ptr->dd, i_ptr->ds); if (!tdam) tdam = 1; tdam += i_ptr->to_d; /* Chance of hitting */ chance = (p_ptr->state.skills[SKILL_TO_HIT_THROW] + (p_ptr->state.to_h * BTH_PLUS_ADJ)); /* Take a turn */ p_ptr->energy_use = 100; /* Start at the player */ y = p_ptr->py; x = p_ptr->px; /* Predict the "target" location */ ty = p_ptr->py + 99 * ddy[dir]; tx = p_ptr->px + 99 * ddx[dir]; /* Check for "target request" */ if ((dir == 5) && target_okay()) { target_get(&tx, &ty); } /* Calculate the path */ path_n = project_path(path_g, tdis, p_ptr->py, p_ptr->px, ty, tx, 0); /* Hack -- Handle stuff */ handle_stuff(); /* Project along the path */ for (i = 0; i < path_n; ++i) { int ny = GRID_Y(path_g[i]); int nx = GRID_X(path_g[i]); /* Hack -- Stop before hitting walls */ if (!cave_floor_bold(ny, nx)) break; /* Advance */ x = nx; y = ny; /* Only do visuals if the player can "see" the missile */ if (player_can_see_bold(y, x)) { /* Visual effects */ print_rel(missile_char, missile_attr, y, x); move_cursor_relative(y, x); Term_fresh(); if (p_ptr->redraw) redraw_stuff(); Term_xtra(TERM_XTRA_DELAY, msec); light_spot(y, x); Term_fresh(); if (p_ptr->redraw) redraw_stuff(); } /* Delay anyway for consistency */ else { /* Pause anyway, for consistancy */ Term_xtra(TERM_XTRA_DELAY, msec); } /* Handle monster */ if (cave_m_idx[y][x] > 0) { monster_type *m_ptr = &mon_list[cave_m_idx[y][x]]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; int chance2 = chance - distance(p_ptr->py, p_ptr->px, y, x); int visible = m_ptr->ml; /* Note the collision */ hit_body = TRUE; /* Did we hit it (penalize range) */ if (test_hit(chance2, r_ptr->ac, m_ptr->ml)) { const char *hit_verb = "hits"; bool fear = FALSE; const slay_t *best_s_ptr = NULL; /* Assume a default death */ cptr note_dies = " dies."; /* Some monsters get "destroyed" */ if (monster_is_unusual(r_ptr)) { /* Special note at death */ note_dies = " is destroyed."; } /* Apply special damage - brought forward to fill in hit_verb XXX XXX XXX */ improve_attack_modifier(i_ptr, m_ptr, &best_s_ptr); if (best_s_ptr != NULL) { tdam *= best_s_ptr->mult; hit_verb = best_s_ptr->range_verb; } /* Apply special damage XXX XXX XXX */ tdam = critical_shot(i_ptr->weight, i_ptr->to_h, tdam, &msg_type); /* No negative damage; change verb if no damage done */ if (tdam <= 0) { tdam = 0; hit_verb = "fail to harm"; } /* Handle unseen monster */ if (!visible) { /* Invisible monster */ msg_format("The %s finds a mark.", o_name); } /* Handle visible monster */ else { char m_name[80]; /* Get "the monster" or "it" */ monster_desc(m_name, sizeof(m_name), m_ptr, 0); /* Tell the player what happened */ if (msg_type == MSG_SHOOT_HIT) message_format(MSG_SHOOT_HIT, 0, "The %s %s %s.", o_name, hit_verb, m_name); else { if (msg_type == MSG_HIT_GOOD) { message_format(MSG_HIT_GOOD, 0, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a good hit!"); } else if (msg_type == MSG_HIT_GREAT) { message_format(MSG_HIT_GREAT, 0, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a great hit!"); } else if (msg_type == MSG_HIT_SUPERB) { message_format(MSG_HIT_SUPERB, 0, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a superb hit!"); } } /* Hack -- Track this monster race */ if (m_ptr->ml) monster_race_track(m_ptr->r_idx); /* Hack -- Track this monster */ if (m_ptr->ml) health_track(cave_m_idx[y][x]); } /* Learn the bonuses */ /* XXX Eddie This is messed up, better done for firing, */ /* should use that method [split last] instead */ /* check if inven_optimize removed what o_ptr referenced */ if (object_similar(i_ptr, o_ptr, OSTACK_PACK)) object_notice_attack_plusses(o_ptr); object_notice_attack_plusses(i_ptr); /* Complex message */ if (p_ptr->wizard) msg_format("You do %d (out of %d) damage.", tdam, m_ptr->hp); /* Hit the monster, check for death */ if (mon_take_hit(cave_m_idx[y][x], tdam, &fear, note_dies)) { /* Dead monster */ } /* No death */ else { /* Message */ message_pain(cave_m_idx[y][x], tdam); /* Take note */ if (fear && m_ptr->ml) { char m_name[80]; /* Get the monster name (or "it") */ monster_desc(m_name, sizeof(m_name), m_ptr, 0); /* Message */ message_format(MSG_FLEE, m_ptr->r_idx, "%^s flees in terror!", m_name); } } } /* Stop looking */ break; } } /* Chance of breakage (during attacks) */ j = (hit_body ? breakage_chance(i_ptr) : 0); /* Drop (or break) near that location */ drop_near(i_ptr, j, y, x, TRUE); }
/* * 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; if (effect < 1 || effect > EF_MAX) { msg_print("Bad effect passed to do_effect(). Please report this bug."); return FALSE; } switch (effect) { case EF_POISON: { if (!p_ptr->state.resist_pois) { if (!p_ptr->timed[TMD_OPP_POIS] && inc_timed(TMD_POISONED, damroll(2, 7) + 10, TRUE)) *ident = TRUE; } *ident = TRUE; wieldeds_notice_flag(OF_RES_POIS); return TRUE; } case EF_BLIND: { if (!p_ptr->state.resist_blind) { if (inc_timed(TMD_BLIND, damroll(4, 25) + 75, TRUE)) *ident = TRUE; } *ident = TRUE; wieldeds_notice_flag(OF_RES_BLIND); return TRUE; } case EF_SCARE: { if (!p_ptr->state.resist_fear) { if (inc_timed(TMD_AFRAID, randint0(10) + 10, TRUE)) *ident = TRUE; } *ident = TRUE; wieldeds_notice_flag(OF_RES_FEAR); return TRUE; } case EF_CONFUSE: { if (!p_ptr->state.resist_confu) { if (inc_timed(TMD_CONFUSED, damroll(4, 5) + 10, TRUE)) *ident = TRUE; } *ident = TRUE; wieldeds_notice_flag(OF_RES_CONFU); return TRUE; } case EF_HALLUC: { if (!p_ptr->state.resist_chaos) { if (inc_timed(TMD_IMAGE, randint0(250) + 250, TRUE)) *ident = TRUE; } *ident = TRUE; wieldeds_notice_flag(OF_RES_CHAOS); return TRUE; } case EF_PARALYZE: { if (!p_ptr->state.free_act) { if (inc_timed(TMD_PARALYZED, randint0(5) + 5, TRUE)) *ident = TRUE; } *ident = TRUE; wieldeds_notice_flag(OF_FREE_ACT); return TRUE; } case EF_SLOW: { if (inc_timed(TMD_SLOW, randint1(25) + 15, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_POISON: { if (clear_timed(TMD_POISONED, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_BLINDNESS: { if (clear_timed(TMD_BLIND, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_PARANOIA: { if (clear_timed(TMD_AFRAID, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_CONFUSION: { if (clear_timed(TMD_CONFUSED, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_MIND: { if (clear_timed(TMD_CONFUSED, TRUE)) *ident = TRUE; if (clear_timed(TMD_AFRAID, TRUE)) *ident = TRUE; if (clear_timed(TMD_IMAGE, TRUE)) *ident = TRUE; if (!p_ptr->state.resist_confu && inc_timed(TMD_OPP_CONF, damroll(4, 10), TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_BODY: { if (clear_timed(TMD_STUN, TRUE)) *ident = TRUE; if (clear_timed(TMD_CUT, TRUE)) *ident = TRUE; if (clear_timed(TMD_POISONED, TRUE)) *ident = TRUE; if (clear_timed(TMD_BLIND, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_LIGHT: { if (heal_player(15, 15)) *ident = TRUE; if (clear_timed(TMD_BLIND, TRUE)) *ident = TRUE; if (dec_timed(TMD_CUT, 20, TRUE)) *ident = TRUE; if (dec_timed(TMD_CONFUSED, 20, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_SERIOUS: { if (heal_player(20, 25)) *ident = TRUE; if (clear_timed(TMD_CUT, TRUE)) *ident = TRUE; if (clear_timed(TMD_BLIND, TRUE)) *ident = TRUE; if (clear_timed(TMD_CONFUSED, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_CRITICAL: { if (heal_player(25, 30)) *ident = TRUE; if (clear_timed(TMD_BLIND, TRUE)) *ident = TRUE; if (clear_timed(TMD_CONFUSED, TRUE)) *ident = TRUE; if (clear_timed(TMD_POISONED, TRUE)) *ident = TRUE; if (clear_timed(TMD_STUN, TRUE)) *ident = TRUE; if (clear_timed(TMD_CUT, TRUE)) *ident = TRUE; if (clear_timed(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 (clear_timed(TMD_BLIND, TRUE)) *ident = TRUE; if (clear_timed(TMD_CONFUSED, TRUE)) *ident = TRUE; if (clear_timed(TMD_POISONED, TRUE)) *ident = TRUE; if (clear_timed(TMD_STUN, TRUE)) *ident = TRUE; if (clear_timed(TMD_CUT, TRUE)) *ident = TRUE; if (clear_timed(TMD_AMNESIA, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_FULL2: { if (hp_player(1200)) *ident = TRUE; if (clear_timed(TMD_BLIND, TRUE)) *ident = TRUE; if (clear_timed(TMD_CONFUSED, TRUE)) *ident = TRUE; if (clear_timed(TMD_POISONED, TRUE)) *ident = TRUE; if (clear_timed(TMD_STUN, TRUE)) *ident = TRUE; if (clear_timed(TMD_CUT, TRUE)) *ident = TRUE; if (clear_timed(TMD_AMNESIA, TRUE)) *ident = TRUE; return TRUE; } case EF_CURE_TEMP: { if (clear_timed(TMD_BLIND, TRUE)) *ident = TRUE; if (clear_timed(TMD_POISONED, TRUE)) *ident = TRUE; if (clear_timed(TMD_CONFUSED, TRUE)) *ident = TRUE; if (clear_timed(TMD_STUN, TRUE)) *ident = TRUE; if (clear_timed(TMD_CUT, TRUE)) *ident = TRUE; return TRUE; } case EF_HEAL1: { if (hp_player(500)) *ident = TRUE; if (clear_timed(TMD_CUT, TRUE)) *ident = TRUE; return TRUE; } case EF_HEAL2: { if (hp_player(1000)) *ident = TRUE; if (clear_timed(TMD_CUT, TRUE)) *ident = TRUE; return TRUE; } case EF_HEAL3: { if (hp_player(500)) *ident = TRUE; if (clear_timed(TMD_STUN, TRUE)) *ident = TRUE; if (clear_timed(TMD_CUT, TRUE)) *ident = TRUE; return TRUE; } case EF_GAIN_EXP: { if (p_ptr->exp < PY_MAX_EXP) { msg_print("You feel more experienced."); gain_exp(100000L); *ident = TRUE; } return TRUE; } case EF_LOSE_EXP: { if (!p_ptr->state.hold_life && (p_ptr->exp > 0)) { msg_print("You feel your memories fade."); lose_exp(p_ptr->exp / 4); *ident = TRUE; } *ident = TRUE; wieldeds_notice_flag(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_print("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(damroll(5, 5), "stat drain"); (void)do_dec_stat(stat, FALSE); *ident = TRUE; return TRUE; } case EF_LOSE_CON2: { take_hit(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_print("You feel life flow through your body!"); restore_level(); (void)clear_timed(TMD_POISONED, TRUE); (void)clear_timed(TMD_BLIND, TRUE); (void)clear_timed(TMD_CONFUSED, TRUE); (void)clear_timed(TMD_IMAGE, TRUE); (void)clear_timed(TMD_STUN, TRUE); (void)clear_timed(TMD_CUT, TRUE); (void)clear_timed(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(); 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 (inc_timed(TMD_SINFRA, 100 + damroll(4, 25), TRUE)) *ident = TRUE; return TRUE; } case EF_TMD_SINVIS: { if (clear_timed(TMD_BLIND, TRUE)) *ident = TRUE; if (inc_timed(TMD_SINVIS, 12 + damroll(2, 6), TRUE)) *ident = TRUE; return TRUE; } case EF_TMD_ESP: { if (clear_timed(TMD_BLIND, TRUE)) *ident = TRUE; if (inc_timed(TMD_TELEPATHY, 12 + damroll(6, 6), TRUE)) *ident = TRUE; return TRUE; } case EF_ENLIGHTENMENT: { msg_print("An image of your surroundings forms in your mind..."); wiz_light(); *ident = TRUE; return TRUE; } case EF_ENLIGHTENMENT2: { msg_print("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); identify_pack(); *ident = TRUE; return TRUE; } case EF_HERO: { if (hp_player(10)) *ident = TRUE; if (clear_timed(TMD_AFRAID, TRUE)) *ident = TRUE; if (inc_timed(TMD_HERO, randint1(25) + 25, TRUE)) *ident = TRUE; return TRUE; } case EF_SHERO: { if (hp_player(30)) *ident = TRUE; if (clear_timed(TMD_AFRAID, TRUE)) *ident = TRUE; if (inc_timed(TMD_SHERO, randint1(25) + 25, TRUE)) *ident = TRUE; return TRUE; } case EF_RESIST_ACID: { if (inc_timed(TMD_OPP_ACID, randint1(10) + 10, TRUE)) *ident = TRUE; return TRUE; } case EF_RESIST_ELEC: { if (inc_timed(TMD_OPP_ELEC, randint1(10) + 10, TRUE)) *ident = TRUE; return TRUE; } case EF_RESIST_FIRE: { if (inc_timed(TMD_OPP_FIRE, randint1(10) + 10, TRUE)) *ident = TRUE; return TRUE; } case EF_RESIST_COLD: { if (inc_timed(TMD_OPP_COLD, randint1(10) + 10, TRUE)) *ident = TRUE; return TRUE; } case EF_RESIST_POIS: { if (inc_timed(TMD_OPP_POIS, randint1(10) + 10, TRUE)) *ident = TRUE; return TRUE; } case EF_RESIST_ALL: { if (inc_timed(TMD_OPP_ACID, randint1(20) + 20, TRUE)) *ident = TRUE; if (inc_timed(TMD_OPP_ELEC, randint1(20) + 20, TRUE)) *ident = TRUE; if (inc_timed(TMD_OPP_FIRE, randint1(20) + 20, TRUE)) *ident = TRUE; if (inc_timed(TMD_OPP_COLD, randint1(20) + 20, TRUE)) *ident = TRUE; if (inc_timed(TMD_OPP_POIS, randint1(20) + 20, TRUE)) *ident = TRUE; return TRUE; } case EF_DETECT_TREASURE: { if (detect_treasure(aware)) *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; if (!enchant_spell(1, 0, 0)) return FALSE; return TRUE; } case EF_ENCHANT_TODAM: { *ident = TRUE; if (!enchant_spell(0, 1, 0)) return FALSE; return TRUE; } case EF_ENCHANT_WEAPON: { *ident = TRUE; if (!enchant_spell(randint1(3), randint1(3), 0)) return FALSE; return TRUE; } case EF_ENCHANT_ARMOR: { *ident = TRUE; if (!enchant_spell(0, 0, 1)) return FALSE; return TRUE; } case EF_ENCHANT_ARMOR2: { *ident = TRUE; if (!enchant_spell(0, 0, randint1(3) + 2)) return FALSE; return TRUE; } 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_print("The air around your body glows blue for a moment..."); else msg_print("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, SUMMON_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_print("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_print("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_print("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 ((!p_ptr->state.resist_blind) && (!p_ptr->state.resist_dark)) (void)inc_timed(TMD_BLIND, 3 + randint1(5), TRUE); unlight_area(10, 3); wieldeds_notice_flag(OF_RES_BLIND); wieldeds_notice_flag(OF_RES_DARK); *ident = TRUE; return TRUE; } case EF_PROTEVIL: { if (inc_timed(TMD_PROTEVIL, randint1(25) + 3 * p_ptr->lev, TRUE)) *ident = TRUE; return TRUE; } case EF_SATISFY: { if (set_food(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 (inc_timed(TMD_BLESSED, randint1(12) + 6, TRUE)) *ident = TRUE; return TRUE; } case EF_BLESSING2: { if (inc_timed(TMD_BLESSED, randint1(24) + 12, TRUE)) *ident = TRUE; return TRUE; } case EF_BLESSING3: { if (inc_timed(TMD_BLESSED, randint1(48) + 24, 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 = 2; i > 0; i--) { if (is_quest(target_depth)) break; if (target_depth >= MAX_DEPTH - 1) break; target_depth++; } if (target_depth > p_ptr->depth) { message(MSG_TPLEVEL, 0, "You sink through the floor..."); dungeon_change_level(target_depth); *ident = TRUE; } return TRUE; } 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: { *ident = TRUE; (void)hp_player(30); (void)clear_timed(TMD_AFRAID, TRUE); (void)inc_timed(TMD_SHERO, randint1(50) + 50, TRUE); (void)inc_timed(TMD_BLESSED, randint1(50) + 50, TRUE); (void)inc_timed(TMD_OPP_ACID, randint1(50) + 50, TRUE); (void)inc_timed(TMD_OPP_ELEC, randint1(50) + 50, TRUE); (void)inc_timed(TMD_OPP_FIRE, randint1(50) + 50, TRUE); (void)inc_timed(TMD_OPP_COLD, randint1(50) + 50, TRUE); (void)inc_timed(TMD_OPP_POIS, randint1(50) + 50, 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 (set_timed(TMD_FAST, damroll(2, 10) + 20, TRUE)) *ident = TRUE; } else { (void)inc_timed(TMD_FAST, 5, TRUE); } return TRUE; } case EF_HASTE1: { if (!p_ptr->timed[TMD_FAST]) { if (set_timed(TMD_FAST, randint1(20) + 20, TRUE)) *ident = TRUE; } else { (void)inc_timed(TMD_FAST, 5, TRUE); } return TRUE; } case EF_HASTE2: { if (!p_ptr->timed[TMD_FAST]) { if (set_timed(TMD_FAST, randint1(75) + 75, TRUE)) *ident = TRUE; } else { (void)inc_timed(TMD_FAST, 5, 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)clear_timed(TMD_AFRAID, TRUE); (void)clear_timed(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_print("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_print("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: { if (inc_timed(TMD_SHERO, randint1(50) + 50, 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))); if (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_print("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 (inc_timed(TMD_PROTEVIL, randint1(25) + 3 * p_ptr->lev, TRUE)) *ident = TRUE; if (clear_timed(TMD_POISONED, TRUE)) *ident = TRUE; if (clear_timed(TMD_AFRAID, TRUE)) *ident = TRUE; if (hp_player(50)) *ident = TRUE; if (clear_timed(TMD_STUN, TRUE)) *ident = TRUE; if (clear_timed(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_print("You feel less thirsty."); *ident = TRUE; return TRUE; } case EF_DRINK_DEATH: { msg_print("A feeling of Death flows through your body."); take_hit(5000, "a potion of Death"); *ident = TRUE; return TRUE; } case EF_DRINK_RUIN: { msg_print("Your nerves and muscles feel weak and lifeless!"); take_hit(damroll(10, 10), "a potion of Ruination"); (void)dec_stat(A_DEX, TRUE); (void)dec_stat(A_WIS, TRUE); (void)dec_stat(A_CON, TRUE); (void)dec_stat(A_STR, TRUE); (void)dec_stat(A_CHR, TRUE); (void)dec_stat(A_INT, TRUE); *ident = TRUE; return TRUE; } case EF_DRINK_DETONATE: { msg_print("Massive explosions rupture your body!"); take_hit(damroll(50, 20), "a potion of Detonation"); (void)inc_timed(TMD_STUN, 75, TRUE); (void)inc_timed(TMD_CUT, 5000, TRUE); *ident = TRUE; return TRUE; } case EF_DRINK_SALT: { msg_print("The potion makes you vomit!"); (void)set_food(PY_FOOD_STARVE - 1); (void)clear_timed(TMD_POISONED, TRUE); (void)inc_timed(TMD_PARALYZED, 4, TRUE); *ident = TRUE; return TRUE; } case EF_FOOD_GOOD: { msg_print("That tastes good."); *ident = TRUE; return TRUE; } case EF_FOOD_WAYBREAD: { msg_print("That tastes good."); (void)clear_timed(TMD_POISONED, TRUE); (void)hp_player(damroll(4, 8)); *ident = TRUE; return TRUE; } case EF_SHROOM_EMERGENCY: { (void)set_timed(TMD_IMAGE, rand_spread(250, 50), TRUE); (void)set_timed(TMD_OPP_FIRE, rand_spread(30, 10), TRUE); (void)set_timed(TMD_OPP_COLD, rand_spread(30, 10), TRUE); (void)hp_player(200); *ident = TRUE; return TRUE; } case EF_SHROOM_TERROR: { if (set_timed(TMD_TERROR, rand_spread(100, 20), TRUE)) *ident = TRUE; return TRUE; } case EF_SHROOM_STONE: { if (set_timed(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_print("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 (inc_timed(TMD_SPRINT, 100, TRUE)) *ident = TRUE; return TRUE; } case EF_SHROOM_PURGING: { (void)set_food(PY_FOOD_FAINT - 1); if (do_res_stat(A_STR)) *ident = TRUE; if (do_res_stat(A_CON)) *ident = TRUE; if (clear_timed(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); inc_timed(TMD_OPP_ACID, randint1(20) + 20, TRUE); return TRUE; } case EF_RING_FLAMES: { dam = 80 * (100 + boost) / 100; *ident = TRUE; fire_ball(GF_FIRE, dir, dam, 2); inc_timed(TMD_OPP_FIRE, randint1(20) + 20, TRUE); return TRUE; } case EF_RING_ICE: { dam = 75 * (100 + boost) / 100; *ident = TRUE; fire_ball(GF_COLD, dir, dam, 2); inc_timed(TMD_OPP_COLD, randint1(20) + 20, TRUE); return TRUE; } case EF_RING_LIGHTNING: { dam = 85 * (100 + boost) / 100; *ident = TRUE; fire_ball(GF_ELEC, dir, dam, 2); inc_timed(TMD_OPP_ELEC, randint1(20) + 20, TRUE); return TRUE; } case EF_DRAGON_BLUE: { dam = 100 * (100 + boost) / 100; message_format(MSG_BR_ELEC, 0, "You breathe lightning."); fire_ball(GF_ELEC, dir, dam, 2); return TRUE; } case EF_DRAGON_GREEN: { dam = 150 * (100 + boost) / 100; message_format(MSG_BR_GAS, 0, "You breathe poison gas."); fire_ball(GF_POIS, dir, dam, 2); return TRUE; } case EF_DRAGON_RED: { dam = 200 * (100 + boost) / 100; message_format(MSG_BR_FIRE, 0, "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; message_format(mh[chance].msg_sound, 0, "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; message_format(MSG_BR_CONF, 0, "You breathe confusion."); fire_ball(GF_CONFUSION, dir, dam, 2); return TRUE; } case EF_DRAGON_GOLD: { dam = 130 * (100 + boost) / 100; message_format(MSG_BR_SOUND, 0, "You breathe sound."); fire_ball(GF_SOUND, dir, dam, 2); return TRUE; } case EF_DRAGON_CHAOS: { dam = 220 * (100 + boost) / 100; chance = randint0(2); message_format((chance == 1 ? MSG_BR_CHAOS : MSG_BR_DISENCHANT), 0, "You breathe %s.", ((chance == 1 ? "chaos" : "disenchantment"))); fire_ball((chance == 1 ? GF_CHAOS : GF_DISENCHANT), dir, dam, 2); return TRUE; } case EF_DRAGON_LAW: { dam = 230 * (100 + boost) / 100; chance = randint0(2); message_format((chance == 1 ? MSG_BR_SOUND : MSG_BR_SHARDS), 0, "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_format("You breathe %s.", ((chance == 1) ? "chaos" : ((chance == 2) ? "disenchantment" : ((chance == 3) ? "sound" : "shards")))); fire_ball(((chance == 1) ? GF_CHAOS : ((chance == 2) ? GF_DISENCHANT : ((chance == 3) ? GF_SOUND : GF_SHARD))), dir, dam, 2); return TRUE; } case EF_DRAGON_SHINING: { dam = 200 * (100 + boost) / 100; chance = randint0(2); message_format((chance == 0 ? MSG_BR_LIGHT : MSG_BR_DARK), 0, "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; message_format(MSG_BR_ELEMENTS, 0, "You breathe the elements."); fire_ball(GF_MISSILE, dir, dam, 2); return TRUE; } case EF_TRAP_DOOR: case EF_TRAP_PIT: case EF_TRAP_PIT_SPIKES: case EF_TRAP_PIT_POISON: case EF_TRAP_RUNE_SUMMON: case EF_TRAP_RUNE_TELEPORT: case EF_TRAP_SPOT_FIRE: case EF_TRAP_SPOT_ACID: case EF_TRAP_DART_SLOW: case EF_TRAP_DART_LOSE_STR: case EF_TRAP_DART_LOSE_DEX: case EF_TRAP_DART_LOSE_CON: case EF_TRAP_GAS_BLIND: case EF_TRAP_GAS_CONFUSE: case EF_TRAP_GAS_POISON: case EF_TRAP_GAS_SLEEP: { break; } case EF_XXX: case EF_MAX: break; } /* Not used */ msg_print("Effect not handled."); return FALSE; }
/* * Sense the inventory */ void sense_inventory(void) { int i; char o_name[80]; unsigned int rate; /* No ID when confused in a bad state */ if (p_ptr->timed[TMD_CONFUSED]) return; /* Notice some things after a while */ if (turn >= (object_last_wield + 3000)) { object_notice_after_time(); object_last_wield = 0; } /* Get improvement rate */ if (player_has(PF_PSEUDO_ID_IMPROV)) rate = cp_ptr->sense_base / (p_ptr->lev * p_ptr->lev + cp_ptr->sense_div); else rate = cp_ptr->sense_base / (p_ptr->lev + cp_ptr->sense_div); if (!one_in_(rate)) return; /* Check everything */ for (i = 0; i < ALL_INVEN_TOTAL; i++) { const char *text = NULL; object_type *o_ptr = &p_ptr->inventory[i]; obj_pseudo_t feel; bool cursed; bool okay = FALSE; /* Skip empty slots */ if (!o_ptr->k_idx) continue; /* Valid "tval" codes */ switch (o_ptr->tval) { case TV_SHOT: case TV_ARROW: case TV_BOLT: case TV_BOW: case TV_DIGGING: case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_BOOTS: case TV_GLOVES: case TV_HELM: case TV_CROWN: case TV_SHIELD: case TV_CLOAK: case TV_SOFT_ARMOR: case TV_HARD_ARMOR: case TV_DRAG_ARMOR: { okay = TRUE; break; } } /* Skip non-sense machines */ if (!okay) continue; /* It is known, no information needed */ if (object_is_known(o_ptr)) continue; /* It has already been sensed, do not sense it again */ if (object_was_sensed(o_ptr)) { /* Small chance of wielded, sensed items getting complete ID */ if (!o_ptr->name1 && (i >= INVEN_WIELD) && one_in_(1000)) do_ident_item(i, o_ptr); continue; } /* Occasional failure on inventory items */ if ((i < INVEN_WIELD) && one_in_(5)) continue; /* Sense the object */ object_notice_sensing(o_ptr); cursed = object_notice_curses(o_ptr); /* Get the feeling */ feel = object_pseudo(o_ptr); /* Stop everything */ disturb(0, 0); if (cursed) text = "cursed"; else text = inscrip_text[feel]; object_desc(o_name, sizeof(o_name), o_ptr, ODESC_BASE); /* Average pseudo-ID means full ID */ if (feel == INSCRIP_AVERAGE) { object_notice_everything(o_ptr); message_format(MSG_PSEUDOID, 0, "You feel the %s (%c) %s %s average...", o_name, index_to_label(i),((i >= INVEN_WIELD) ? "you are using" : "in your pack"), ((o_ptr->number == 1) ? "is" : "are")); } else { if (i >= INVEN_WIELD) { message_format(MSG_PSEUDOID, 0, "You feel the %s (%c) you are %s %s %s...", o_name, index_to_label(i), describe_use(i), ((o_ptr->number == 1) ? "is" : "are"), text); } else { message_format(MSG_PSEUDOID, 0, "You feel the %s (%c) in your pack %s %s...", o_name, index_to_label(i), ((o_ptr->number == 1) ? "is" : "are"), text); } } /* Set squelch flag as appropriate */ if (i < INVEN_WIELD) p_ptr->notice |= PN_SQUELCH; /* Combine / Reorder the pack (later) */ p_ptr->notice |= (PN_COMBINE | PN_REORDER | PN_SORT_QUIVER); /* Redraw stuff */ p_ptr->redraw |= (PR_INVEN | PR_EQUIP); } }
/* * Destroy all {squelch}able items. * * Imported, with thanks, from Ey... much cleaner than the original. */ void squelch_items(void) { int floor_list[MAX_FLOOR_STACK]; int floor_num, n; int count = 0; object_type *o_ptr; /* Set the hook and scan the floor */ item_tester_hook = squelch_item_ok; floor_num = scan_floor(floor_list, N_ELEMENTS(floor_list), p_ptr->py, p_ptr->px, 0x01); if (floor_num) { for (n = 0; n < floor_num; n++) { o_ptr = &o_list[floor_list[n]]; /* Avoid artifacts */ if (artifact_p(o_ptr)) continue; if (item_tester_okay(o_ptr)) { /* Destroy item */ floor_item_increase(floor_list[n], -o_ptr->number); floor_item_optimize(floor_list[n]); count++; } } } /* Scan through the slots backwards */ for (n = INVEN_PACK - 1; n >= 0; n--) { o_ptr = &inventory[n]; /* Skip non-objects and artifacts */ if (!o_ptr->k_idx) continue; if (artifact_p(o_ptr)) continue; if (item_tester_okay(o_ptr)) { /* Destroy item */ inven_item_increase(n, -o_ptr->number); inven_item_optimize(n); count++; } } item_tester_hook = NULL; /* Mention casualties */ if (count > 0) { message_format(MSG_DESTROY, 0, "%d item%s squelched.", count, ((count > 1) ? "s" : "")); /* Combine/reorder the pack */ p_ptr->notice |= (PN_COMBINE | PN_REORDER | PN_SORT_QUIVER); } }
/* * Attack the monster at the given location * * If no "weapon" is available, then "punch" the monster one time. * * We get blows until energy drops below that required for another blow, or * until the target monster dies. We use a wrapper to work out the number of * blows. We don't allow @ to spend more than 100 energy in one go, to avoid * slower monsters getting double moves. */ bool py_attack_real(int y, int x) { int bonus, chance; monster_type *m_ptr = &mon_list[cave_m_idx[y][x]]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; monster_lore *l_ptr = &l_list[m_ptr->r_idx]; object_type *o_ptr; char m_name[80]; bool fear = FALSE; bool do_quake = FALSE; bool dead = FALSE; u32b msg_type = 0; bool success = FALSE; const char *hit_verb = "ERROR"; int dmg = 1; /* Default to punching for one damage */ /* Maybe handle monster melee here -Simon */ if(!rp_ptr->p_monster_index) { hit_verb = "punch"; msg_type = MSG_HIT; } /* * Hack -- if advanced innate attacks are disabled for this race, fall back * on defaults <player>'s 1d1. */ /* TODO: this -Simon */ /* if (p_ptr->flags[NO_INNATE]) { p_ptr = &r_info[0]; } */ /* Extract monster name (or "it") */ monster_desc(m_name, sizeof(m_name), m_ptr, 0); /* Auto-Recall if possible and visible */ if (m_ptr->ml) monster_race_track(m_ptr->r_idx); /* Track a new monster */ if (m_ptr->ml) health_track(cave_m_idx[y][x]); /* Handle player fear (only for invisible monsters) */ if (p_ptr->state.afraid) { message_format(MSG_AFRAID, 0, "You are too afraid to attack %s!", m_name); return (FALSE); } /* Disturb the monster */ wake_monster(m_ptr); /* Get the weapon */ o_ptr = &p_ptr->inventory[INVEN_WIELD]; /* Calculate the "attack quality" */ bonus = p_ptr->state.to_h + o_ptr->to_h; chance = (p_ptr->state.skills[SKILL_TO_HIT_MELEE] + (bonus * BTH_PLUS_ADJ)); /* Handle innate melee -Simon */ if(!o_ptr->k_idx && rp_ptr->p_monster_index) { for (int num = 0; (num < MONSTER_BLOW_MAX) && !dead; num++) { const slay_t *best_s_ptr = NULL; int type = GF_ARROW, type2 = 0, i; int flg = PROJECT_KILL | PROJECT_STOP | PROJECT_HIDE; // | PROJECT_PASS; TODO: figure out what this is and implement it -Simon char *p = ""; if (!r_info[rp_ptr->p_monster_index].blow[num].method && !dead) continue; /* Test for hit */ if (test_hit(chance, r_info[m_ptr->r_idx].ac, m_ptr->ml)) { int mul = 1; int k = 0; /* Get the method */ switch (r_info[rp_ptr->p_monster_index].blow[num].method) { case RBM_HIT: p = "hit"; break; case RBM_TOUCH: p = "touch"; break; case RBM_PUNCH: p = "punch"; break; case RBM_KICK: p = "kick"; break; case RBM_CLAW: p = "claw"; break; case RBM_BITE: p = "bite"; break; case RBM_STING: p = "sting"; break; case RBM_BUTT: p = "butt"; break; case RBM_CRUSH: p = "crush"; break; case RBM_ENGULF:p = "engulf"; break; case RBM_PECK: p = "peck"; break; case RBM_CRAWL: p = "crawl on"; break; case RBM_DROOL: p = "drool on"; break; case RBM_SPIT: p = "spit on"; break; case RBM_SLIME: p = "slime"; break; case RBM_GAZE: p = "gaze at"; break; case RBM_WAIL: p = "wail at"; break; case RBM_SPORE: p = "release spores at"; break; case RBM_BEG: p = "beg for money"; break; case RBM_INSULT: p = "insult"; break; default: p = "attack"; } /* Get the effect */ switch (r_info[rp_ptr->p_monster_index].blow[num].effect) { case RBE_HURT: type = GF_ARROW; break; case RBE_DISEASE: type = GF_POIS; mul = 2; break; case RBE_POISON: type = GF_POIS; mul = 2; break; case RBE_LOSE_MANA: type = GF_DISENCHANT; break; case RBE_UN_BONUS: type = GF_DISENCHANT; mul = 2; break; case RBE_UN_POWER: type = GF_DISENCHANT; mul = 2; break; /* ? */ case RBE_EAT_LIGHT: type = GF_DARK; mul = 2; break; case RBE_ACID: type = GF_ACID; mul = 2; break; case RBE_ELEC: type = GF_ELEC; mul = 2; break; case RBE_FIRE: type = GF_FIRE; mul = 2; break; case RBE_COLD: type = GF_COLD; mul = 2; break; case RBE_BLIND: type2 = GF_OLD_CONF; break; /* ? */ case RBE_CONFUSE: type2 = GF_OLD_CONF; break; case RBE_TERRIFY: type2 = GF_TURN_ALL; break; /* ? */ case RBE_PARALYZE: type2 = GF_OLD_SLEEP; break; /* ? */ /* Earthquake would be natural, but all monsters * with RBE_SHATTER are already humanoids */ case RBE_SHATTER: type = GF_ARROW; break; case RBE_EXP_10: type = GF_NETHER; mul = 2; break; case RBE_EXP_20: type = GF_NETHER; mul = 2; break; case RBE_EXP_40: type = GF_NETHER; mul = 2; break; case RBE_EXP_80: type = GF_NETHER; mul = 2; break; /* GF_CHAOS will polymorph, so it is bad */ case RBE_HALLU: type = GF_DISENCHANT; mul = 2; break; default: type = GF_ARROW; } k = damroll(r_info[rp_ptr->p_monster_index].blow[num].d_dice, r_info[rp_ptr->p_monster_index].blow[num].d_side); /* Get the best attack from all slays or * brands on all non-launcher equipment */ for (i = INVEN_FINGER; i < INVEN_TOTAL; i++) improve_attack_modifier(&p_ptr->inventory[i], m_ptr, &best_s_ptr); if (best_s_ptr != NULL) { if (best_s_ptr->mult > mul) { p = best_s_ptr->melee_verb; mul = best_s_ptr->mult; if (best_s_ptr->resist_flag == RF_IM_ACID) type = GF_ACID; else if (best_s_ptr->resist_flag == RF_IM_ELEC) type = GF_ELEC; else if (best_s_ptr->resist_flag == RF_IM_FIRE) type = GF_FIRE; else if (best_s_ptr->resist_flag == RF_IM_COLD) type = GF_COLD; else if (best_s_ptr->resist_flag == RF_IM_POIS) type = GF_POIS; } } k *= mul; message_format(MSG_HIT, m_ptr->r_idx, "You %s %s.", p, m_name); /* Add to-dam bonus only if did some damage */ if (k) k += p_ptr->state.to_d; if (k < 0) k = 0; /* Learn by use for other equipped items */ wieldeds_notice_on_attack(); /* If there is an extra effect, project it also, using 4*level as power */ if (type2) { project(-1, 0, y, x, 4 * p_ptr->lev, type2, flg); } /* Confusion attack */ if (p_ptr->confusing) { /* Message */ if (p_ptr->confusing) msg_print("Your limbs stop glowing."); /* Cancel glowing hands */ p_ptr->confusing = FALSE; /* Confuse the monster */ if (rf_has(r_ptr->flags, RF_NO_CONF)) { if (m_ptr->ml) { rf_on(l_ptr->flags, RF_NO_CONF); } msg_format("%^s is unaffected.", m_name); } else if (randint0(100) < r_ptr->level) { msg_format("%^s appears slightly perplexed.", m_name); } else { msg_format("%^s appears confused.", m_name); m_ptr->confused += 10 + randint0(p_ptr->lev) / 5; } } /* Damage, check for fear and death */ /* Makes elemental attacks do half their damage physically -Simon */ if (type != GF_ARROW) { project(-1, 0, y, x, (k + 1)/2, type, flg); /* Make the physical portion TOP SECRET -Simon */ flg &= ~(PROJECT_AWARE); project(-1, 0, y, x, k/2, GF_ARROW, flg); } else project(-1, 0, y, x, k, type, flg); /* Hack: check if the square is empty after we project the attack into it -Simon */ dead = (cave_m_idx[y][x] == 0); /* Hack -- delay fear messages */ if (fear && m_ptr->ml && !dead) message_format(MSG_FLEE, m_ptr->r_idx, "%^s flees in terror!", m_name); } /* Player misses */ else { /* Message */ message_format(MSG_MISS, m_ptr->r_idx, "You miss %s.", m_name); } } return (TRUE); } else { /* See if the player hit */ success = test_hit(chance, r_ptr->ac, m_ptr->ml); /* If a miss, skip this hit */ if (!success) { message_format(MSG_MISS, m_ptr->r_idx, "You miss %s.", m_name); return (FALSE); } /* Handle normal weapon */ if (o_ptr->k_idx) { int i; const slay_t *best_s_ptr = NULL; hit_verb = "hit"; /* Get the best attack from all slays or * brands on all non-launcher equipment */ for (i = INVEN_FINGER; i < INVEN_TOTAL; i++) improve_attack_modifier(&p_ptr->inventory[i], m_ptr, &best_s_ptr); improve_attack_modifier(o_ptr, m_ptr, &best_s_ptr); if (best_s_ptr != NULL) hit_verb = best_s_ptr->melee_verb; dmg = damroll(o_ptr->dd, o_ptr->ds); dmg *= (best_s_ptr == NULL) ? 1 : best_s_ptr->mult; if (p_ptr->state.impact && (dmg > 50)) do_quake = TRUE; dmg += o_ptr->to_d; dmg = critical_norm(o_ptr->weight, o_ptr->to_h, dmg, &msg_type); /* Learn by use for the weapon */ object_notice_attack_plusses(o_ptr); if (do_quake) wieldeds_notice_flag(OF_IMPACT); } /* Learn by use for other equipped items */ wieldeds_notice_on_attack(); /* Apply the player damage bonuses */ dmg += p_ptr->state.to_d; /* No negative damage */ if (dmg <= 0) dmg = 0; /* Tell the player what happened */ if (dmg <= 0) message_format(MSG_MISS, m_ptr->r_idx, "You fail to harm %s.", m_name); else if (msg_type == MSG_HIT) message_format(MSG_HIT, m_ptr->r_idx, "You %s %s.", hit_verb, m_name); else if (msg_type == MSG_HIT_GOOD) message_format(MSG_HIT_GOOD, m_ptr->r_idx, "You %s %s. %s", hit_verb, m_name, "It was a good hit!"); else if (msg_type == MSG_HIT_GREAT) message_format(MSG_HIT_GREAT, m_ptr->r_idx, "You %s %s. %s", hit_verb, m_name, "It was a great hit!"); else if (msg_type == MSG_HIT_SUPERB) message_format(MSG_HIT_SUPERB, m_ptr->r_idx, "You %s %s. %s", hit_verb, m_name, "It was a superb hit!"); else if (msg_type == MSG_HIT_HI_GREAT) message_format(MSG_HIT_HI_GREAT, m_ptr->r_idx, "You %s %s. %s", hit_verb, m_name, "It was a *GREAT* hit!"); else if (msg_type == MSG_HIT_HI_SUPERB) message_format(MSG_HIT_HI_SUPERB, m_ptr->r_idx, "You %s %s. %s", hit_verb, m_name, "It was a *SUPERB* hit!"); /* Complex message */ if (p_ptr->wizard) msg_format("You do %d (out of %d) damage.", dmg, m_ptr->hp); /* Confusion attack */ if (p_ptr->confusing) { /* Cancel glowing hands */ p_ptr->confusing = FALSE; /* Message */ msg_print("Your limbs stop glowing."); /* Update the lore */ if (m_ptr->ml) rf_on(l_ptr->flags, RF_NO_CONF); /* Confuse the monster */ if (rf_has(r_ptr->flags, RF_NO_CONF)) msg_format("%^s is unaffected.", m_name); else if (randint0(100) < r_ptr->level) msg_format("%^s is unaffected.", m_name); else { msg_format("%^s appears confused.", m_name); m_ptr->confused += 10 + randint0(p_ptr->lev) / 5; } } /* Damage, check for fear and death */ dead = mon_take_hit(cave_m_idx[y][x], dmg, &fear, NULL); /* Hack -- delay fear messages */ if (fear && m_ptr->ml) message_format(MSG_FLEE, m_ptr->r_idx, "%^s flees in terror!", m_name); /* Mega-Hack -- apply earthquake brand */ if (do_quake) earthquake(p_ptr->py, p_ptr->px, 10); return (dead); } }
/* * Advance experience levels and print experience */ void check_experience(void) { /* Hack -- lower limit */ if (p_ptr->exp < 0) p_ptr->exp = 0; /* Hack -- lower limit */ if (p_ptr->max_exp < 0) p_ptr->max_exp = 0; /* Hack -- upper limit */ if (p_ptr->exp > PY_MAX_EXP) p_ptr->exp = PY_MAX_EXP; /* Hack -- upper limit */ if (p_ptr->max_exp > PY_MAX_EXP) p_ptr->max_exp = PY_MAX_EXP; /* Hack -- maintain "max" experience */ if (p_ptr->exp > p_ptr->max_exp) p_ptr->max_exp = p_ptr->exp; /* Redraw experience */ p_ptr->redraw |= (PR_EXP); /* Handle stuff */ handle_stuff(); /* Lose levels while possible */ while ((p_ptr->lev > 1) && (p_ptr->exp < (get_experience_by_level(p_ptr->lev-2) * p_ptr->expfact / 100L))) { /* Lose a level */ p_ptr->lev--; /* Update some stuff */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS); /* Redraw some stuff */ p_ptr->redraw |= (PR_LEV | PR_TITLE | PR_MONSTER); /* Handle stuff */ handle_stuff(); } /* Gain levels while possible */ while ((p_ptr->lev < z_info->max_level) && (p_ptr->exp >= (get_experience_by_level(p_ptr->lev-1) * p_ptr->expfact / 100L))) { /* Gain a level */ p_ptr->lev++; /* Message */ message_format(MSG_LEVEL, p_ptr->lev, "Welcome to level %d.", p_ptr->lev); /* Save the highest level*/ if (p_ptr->lev > p_ptr->max_lev) { /* update the highest level*/ p_ptr->max_lev = p_ptr->lev; /* If auto-note taking enabled, write a note to the file every 5th level. */ if ((adult_take_notes) && ((p_ptr->lev % 5) == 0)) { char buf[120]; /* Build the message */ sprintf(buf, "Reached level %d", p_ptr->lev); /* Write message */ do_cmd_note(buf, p_ptr->depth); } } /* Update some stuff */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS); /* Redraw some stuff */ p_ptr->redraw |= (PR_LEV | PR_TITLE | PR_EXP | PR_MONSTER); /* Handle stuff */ handle_stuff(); } /* Gain max levels while possible */ while ((p_ptr->max_lev < z_info->max_level) && (p_ptr->max_exp >= (get_experience_by_level(p_ptr->max_lev-1) * p_ptr->expfact / 100L))) { /* Gain max level */ p_ptr->max_lev++; /* Update some stuff */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS); /* Redraw some stuff */ p_ptr->redraw |= (PR_LEV | PR_TITLE); /* Handle stuff */ handle_stuff(); } }
/* * Advance experience levels and print experience */ void check_experience(void) { /* Hack -- lower limit */ if (p_ptr->exp < 0) p_ptr->exp = 0; /* Hack -- lower limit */ if (p_ptr->max_exp < 0) p_ptr->max_exp = 0; /* Hack -- upper limit */ if (p_ptr->exp > PY_MAX_EXP) p_ptr->exp = PY_MAX_EXP; /* Hack -- upper limit */ if (p_ptr->max_exp > PY_MAX_EXP) p_ptr->max_exp = PY_MAX_EXP; /* Hack -- maintain "max" experience */ if (p_ptr->exp > p_ptr->max_exp) p_ptr->max_exp = p_ptr->exp; /* Redraw experience */ p_ptr->redraw |= (PR_EXP); /* Handle stuff */ handle_stuff(); /* Lose levels while possible */ while ((p_ptr->lev > 1) && (p_ptr->exp < (player_exp[p_ptr->lev-2] * p_ptr->expfact / 100L))) { /* Lose a level */ p_ptr->lev--; /* Update some stuff */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS); /* Redraw some stuff */ p_ptr->redraw |= (PR_LEV | PR_TITLE | PR_EXP); /* Handle stuff */ handle_stuff(); } /* Gain levels while possible */ while ((p_ptr->lev < PY_MAX_LEVEL) && (p_ptr->exp >= (player_exp[p_ptr->lev-1] * p_ptr->expfact / 100L))) { char buf[80]; /* Gain a level */ p_ptr->lev++; /* Save the highest level */ if (p_ptr->lev > p_ptr->max_lev) p_ptr->max_lev = p_ptr->lev; /* Log level updates */ strnfmt(buf, sizeof(buf), "Reached level %d", p_ptr->lev); history_add(buf, HISTORY_GAIN_LEVEL, 0); /* Message */ message_format(MSG_LEVEL, p_ptr->lev, "Welcome to level %d.", p_ptr->lev); /* Add to social class */ p_ptr->sc += randint1(2); if (p_ptr->sc > 150) p_ptr->sc = 150; /* Update some stuff */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS); /* Redraw some stuff */ p_ptr->redraw |= (PR_LEV | PR_TITLE | PR_EXP); /* Handle stuff */ handle_stuff(); } /* Gain max levels while possible */ while ((p_ptr->max_lev < PY_MAX_LEVEL) && (p_ptr->max_exp >= (player_exp[p_ptr->max_lev-1] * p_ptr->expfact / 100L))) { /* Gain max level */ p_ptr->max_lev++; /* Update some stuff */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS); /* Redraw some stuff */ p_ptr->redraw |= (PR_LEV | PR_TITLE | PR_EXP); /* Handle stuff */ handle_stuff(); } }