void look(int wakeup) { int x, y; char ch, och; int oldx, oldy; int inpass, horizontal, vertical, do_light = FALSE, do_blank = FALSE; int passcount = 0; struct room *rp; int ey, ex; /* Are we moving vertically or horizontally? */ if (runch == 'h' || runch == 'l') horizontal = TRUE; else horizontal = FALSE; if (runch == 'j' || runch == 'k') vertical = TRUE; else vertical = FALSE; getyx(cw, oldy, oldx); /* Save current position */ /* * Blank out the floor around our last position and check for moving * out of a corridor in a maze. */ if (oldrp != NULL && (oldrp->r_flags & ISDARK) && !(oldrp->r_flags & HASFIRE) && off(player, ISBLIND)) do_blank = TRUE; for (x = player.t_oldpos.x - 1; x <= player.t_oldpos.x + 1; x++) for (y = player.t_oldpos.y - 1; y <= player.t_oldpos.y + 1; y++) { ch = show(y, x); if (do_blank && (y != hero.y || x != hero.x) && ch == FLOOR) mvwaddch(cw, y, x, ' '); /* Moving out of a corridor? */ if (levtype == MAZELEV && (ch != '|' && ch != '-') && /* Not a wall */ ((vertical && x != player.t_oldpos.x && y == player.t_oldpos.y) || (horizontal && y != player.t_oldpos.y && x == player.t_oldpos.x))) do_light = TRUE; /* Just came to a turn */ } inpass = ((rp = roomin(hero)) == NULL); /* Are we in a passage? */ /* Are we coming out of a wall into a corridor in a maze? */ och = show(player.t_oldpos.y, player.t_oldpos.x); ch = show(hero.y, hero.x); if (levtype == MAZELEV && (och == '|' || och == '-' || och == SECRETDOOR) && (ch != '|' && ch != '-' && ch != SECRETDOOR)) { do_light = off(player, ISBLIND); /* Light it up if not blind */ } /* Look around the player */ ey = hero.y + 1; ex = hero.x + 1; for (x = hero.x - 1; x <= ex; x++) if (x >= 0 && x < COLS) for (y = hero.y - 1; y <= ey; y++) { if (y <= 0 || y >= LINES - 2) continue; if (isalpha(mvwinch(mw, y, x))) { struct linked_list *it; struct thing *tp; if (wakeup) it = wake_monster(y, x); else it = find_mons(y, x); if (it == NULL) continue; tp = THINGPTR(it); tp->t_oldch = CCHAR( mvinch(y, x) ); if (isatrap(tp->t_oldch)) { struct trap *trp = trap_at(y, x); tp->t_oldch = (trp->tr_flags & ISFOUND) ? tp->t_oldch : trp->tr_show; } if (tp->t_oldch == FLOOR && (rp->r_flags & ISDARK) && !(rp->r_flags & HASFIRE) && off(player, ISBLIND)) tp->t_oldch = ' '; } /* Secret doors show as walls */ if ((ch = show(y, x)) == SECRETDOOR) ch = secretdoor(y, x); /* * Don't show room walls if he is in a * passage and check for maze turns */ if (off(player, ISBLIND)) { if (y == hero.y && x == hero.x || (inpass && (ch == '-' || ch == '|'))) continue; /* Are we at a crossroads in a maze? */ if (levtype == MAZELEV && (ch != '|' && ch != '-') && /* Not a wall */ ((vertical && x != hero.x && y == hero.y) || (horizontal && y != hero.y && x == hero.x))) do_light = TRUE; /* Just came to a turn */ } else if (y != hero.y || x != hero.x) continue; wmove(cw, y, x); waddch(cw, ch); if (door_stop && !firstmove && running) { switch (runch) { case 'h': if (x == ex) continue; break; case 'j': if (y == hero.y - 1) continue; break; case 'k': if (y == ey) continue; break; case 'l': if (x == hero.x - 1) continue; break; case 'y': if ((x + y) - (hero.x + hero.y) >= 1) continue; break; case 'u': if ((y - x) - (hero.y - hero.x) >= 1) continue; break; case 'n': if ((x + y) - (hero.x + hero.y) <= -1) continue; break; case 'b': if ((y - x) - (hero.y - hero.x) <= -1) continue; break; } switch (ch) { case DOOR: if (x == hero.x || y == hero.y) running = FALSE; break; case PASSAGE: if (x == hero.x || y == hero.y) passcount++; break; case FLOOR: /* * Stop by new passages in a * maze (floor next to us) */ if ((levtype == MAZELEV) && ((horizontal && x == hero.x && y != hero.y) || (vertical && y == hero.y && x != hero.x))) running = FALSE; case '|': case '-': case ' ': break; default: running = FALSE; break; } } } if (door_stop && !firstmove && passcount > 1) running = FALSE; /* * Do we have to light up the area (just stepped into a new * corridor)? */ if (do_light && wakeup && /* wakeup will be true on a normal move */ !(rp->r_flags & ISDARK) && /* We have some light */ !ce(hero, player.t_oldpos)) /* Don't do anything if we didn't move */ light(&hero); mvwaddch(cw, hero.y, hero.x, PLAYER); wmove(cw, oldy, oldx); if (wakeup) { player.t_oldpos = hero; /* Don't change if we didn't move */ oldrp = rp; } }
/* * 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); } }