diff --git a/doc/fixes35.0 b/doc/fixes35.0 index ac81d7ac2..b6f6a320d 100644 --- a/doc/fixes35.0 +++ b/doc/fixes35.0 @@ -269,6 +269,7 @@ add oracle and rumor regarding priestly donations anti-magic traps have alternate effect on targets who have magic resistance the Amulet can be offered to Moloch javelins and spears now share the same weapon skill +all stackable weapons are capable of being thrown/shot for multi-shot volleys Platform- and/or Interface-Specific New Features diff --git a/include/extern.h b/include/extern.h index 40820b708..60aa32df4 100644 --- a/include/extern.h +++ b/include/extern.h @@ -474,6 +474,7 @@ E void FDECL(impact_drop, (struct obj *,XCHAR_P,XCHAR_P,XCHAR_P)); E int NDECL(dothrow); E int NDECL(dofire); +E void FDECL(endmultishot, (BOOLEAN_P)); E void FDECL(hitfloor, (struct obj *)); E void FDECL(hurtle, (int,int,int,BOOLEAN_P)); E void FDECL(mhurtle, (struct monst *,int,int,int)); diff --git a/src/dothrow.c b/src/dothrow.c index fd89d24b3..47d8ffc2e 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -37,7 +37,7 @@ struct obj *obj; int shotlimit; { struct obj *otmp; - int multishot = 1; + int multishot; schar skill; long wep_mask; boolean twoweap; @@ -109,18 +109,30 @@ int shotlimit; /* Multishot calculations */ + multishot = 1; skill = objects[obj->otyp].oc_skill; - if ((ammo_and_launcher(obj, uwep) || skill == P_DAGGER || - skill == -P_DART || skill == -P_SHURIKEN) && + if (obj->quan > 1L && /* no point checking if there's only 1 */ + /* ammo requires corresponding launcher be wielded */ + (is_ammo(obj) ? matching_launcher(obj, uwep) : + /* otherwise any stackable (non-ammo) weapon */ + obj->oclass == WEAPON_CLASS) && !(Confusion || Stunned)) { /* Bonus if the player is proficient in this weapon... */ switch (P_SKILL(weapon_type(obj))) { - default: break; /* No bonus */ - case P_SKILLED: multishot++; break; - case P_EXPERT: multishot += 2; break; + case P_EXPERT: + if (!Role_if(PM_WIZARD)) multishot++; + /*FALLTHRU*/ + case P_SKILLED: + multishot++; + break; + default: /* No bonus */ + break; } /* ...or is using a special weapon for their role... */ switch (Role_switch) { + case PM_MONK: + if (skill == -P_SHURIKEN) multishot++; + break; case PM_RANGER: multishot++; break; @@ -146,17 +158,20 @@ int shotlimit; default: break; /* No bonus */ } - } - /* crossbows are slow to load and probably shouldn't allow multiple - shots at all, but that would result in players never using them; - instead, we require high strength to load and shoot quickly */ - if (multishot > 1 && (int)ACURRSTR < (Race_if(PM_GNOME) ? 16 : 18) && - ammo_and_launcher(obj, uwep) && weapon_type(uwep) == P_CROSSBOW) - multishot = rnd(multishot); - if ((long)multishot > obj->quan) multishot = (int)obj->quan; - multishot = rnd(multishot); - if (shotlimit > 0 && multishot > shotlimit) multishot = shotlimit; + /* crossbows are slow to load and probably shouldn't allow multiple + shots at all, but that would result in players never using them; + instead, we require high strength to load and shoot quickly */ + if (multishot > 1 && + (int)ACURRSTR < (Race_if(PM_GNOME) ? 16 : 18) && + ammo_and_launcher(obj, uwep) && + weapon_type(uwep) == P_CROSSBOW) + multishot = rnd(multishot); + + multishot = rnd(multishot); + if ((long)multishot > obj->quan) multishot = (int)obj->quan; + if (shotlimit > 0 && multishot > shotlimit) multishot = shotlimit; + } m_shot.s = ammo_and_launcher(obj,uwep) ? TRUE : FALSE; /* give a message if shooting more than one, or if player @@ -192,7 +207,6 @@ int shotlimit; return 1; } - int dothrow() { @@ -338,6 +352,19 @@ dofire() return throw_obj(uquiver, shotlimit); } +/* if in midst of multishot shooting/throwing, stop early */ +void +endmultishot(verbose) +boolean verbose; +{ + if (m_shot.i < m_shot.n) { + if (verbose && !context.mon_moving) { + You("stop %s after the %d%s %s.", m_shot.s ? "firing" : "throwing", + m_shot.i, ordin(m_shot.i), m_shot.s ? "shot" : "toss"); + } + m_shot.n = m_shot.i; /* make current shot be the last */ + } +} /* * Object hits floor at hero's feet. Called from drop() and throwit(). @@ -646,12 +673,7 @@ hurtle(dx, dy, range, verbose) if (verbose) You("%s in the opposite direction.", range > 1 ? "hurtle" : "float"); /* if we're in the midst of shooting multiple projectiles, stop */ - if (m_shot.i < m_shot.n) { - /* last message before hurtling was "you shoot N arrows" */ - You("stop %sing after the first %s.", - m_shot.s ? "shoot" : "throw", m_shot.s ? "shot" : "toss"); - m_shot.n = m_shot.i; /* make current shot be the last */ - } + endmultishot(TRUE); if (In_sokoban(&u.uz)) change_luck(-1); /* Sokoban guilt */ uc.x = u.ux; diff --git a/src/makemon.c b/src/makemon.c index 047463261..dfdbfd45e 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)makemon.c 3.5 2006/09/06 */ +/* SCCS Id: @(#)makemon.c 3.5 2006/12/15 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -244,6 +244,9 @@ register struct monst *mtmp; if(!rn2(2)) curse(otmp); (void) mpickobj(mtmp, otmp); } + } else if (mm == PM_NINJA) { /* extra quest villains */ + (void)mongets(mtmp, rn2(4) ? SHURIKEN : DART); + (void)mongets(mtmp, rn2(4) ? SHORT_SWORD : AXE); } break; diff --git a/src/mplayer.c b/src/mplayer.c index c96cff13a..e77197846 100644 --- a/src/mplayer.c +++ b/src/mplayer.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)mplayer.c 3.5 2006/04/14 */ +/* SCCS Id: @(#)mplayer.c 3.5 2006/12/15 */ /* Copyright (c) Izchak Miller, 1992. */ /* NetHack may be freely redistributed. See license for details. */ @@ -179,7 +179,7 @@ register boolean special; if (rn2(2)) armor = rnd_class(PLATE_MAIL, CHAIN_MAIL); break; case PM_MONK: - weapon = STRANGE_OBJECT; + weapon = !rn2(3) ? SHURIKEN : STRANGE_OBJECT; armor = STRANGE_OBJECT; cloak = ROBE; if (rn2(2)) shield = STRANGE_OBJECT; @@ -196,7 +196,7 @@ register boolean special; if (rn2(2)) weapon = ELVEN_DAGGER; break; case PM_ROGUE: - if (rn2(2)) weapon = SHORT_SWORD; + if (rn2(2)) weapon = rn2(2) ? SHORT_SWORD : ORCISH_DAGGER; break; case PM_SAMURAI: if (rn2(2)) weapon = KATANA; @@ -232,6 +232,9 @@ register boolean special; else if (!rn2(2)) otmp->greased = 1; if (special && rn2(2)) otmp = mk_artifact(otmp, A_NONE); + /* usually increase stack size if stackable weapon */ + if (objects[otmp->otyp].oc_merge && !otmp->oartifact) + otmp->quan += (long) rn2(is_spear(otmp) ? 4 : 8); /* mplayers knew better than to overenchant Magicbane */ if (otmp->oartifact == ART_MAGICBANE) otmp->spe = rnd(4); diff --git a/src/mthrowu.c b/src/mthrowu.c index d6f37cb73..c33a027fc 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -481,7 +481,6 @@ struct monst *mtmp; { struct obj *otmp, *mwep; xchar x, y; - schar skill; int multishot; const char *onm; @@ -533,18 +532,29 @@ struct monst *mtmp; rn2(BOLT_LIM - distmin(x,y,mtmp->mux,mtmp->muy)))) return; - skill = objects[otmp->otyp].oc_skill; mwep = MON_WEP(mtmp); /* wielded weapon */ /* Multishot calculations */ multishot = 1; - if ((ammo_and_launcher(otmp, mwep) || skill == P_DAGGER || - skill == -P_DART || skill == -P_SHURIKEN) && !mtmp->mconf) { + if (otmp->quan > 1L && /* no point checking if there's only 1 */ + /* ammo requires corresponding launcher be wielded */ + (is_ammo(otmp) ? matching_launcher(otmp, mwep) : + /* otherwise any stackable (non-ammo) weapon */ + otmp->oclass == WEAPON_CLASS) && + !mtmp->mconf) { + int skill = (int)objects[otmp->otyp].oc_skill; + /* Assumes lords are skilled, princes are expert */ if (is_prince(mtmp->data)) multishot += 2; else if (is_lord(mtmp->data)) multishot++; + /* fake players treated as skilled (regardless of role limits) */ + else if (is_mplayer(mtmp->data)) multishot++; + /* class bonus */ switch (monsndx(mtmp->data)) { + case PM_MONK: + if (skill == -P_SHURIKEN) multishot++; + break; case PM_RANGER: multishot++; break; @@ -552,6 +562,8 @@ struct monst *mtmp; if (skill == P_DAGGER) multishot++; break; case PM_NINJA: + if (skill == -P_SHURIKEN || skill == -P_DART) multishot++; + /*FALLTHRU*/ case PM_SAMURAI: if (otmp->otyp == YA && mwep && mwep->otyp == YUMI) multishot++; @@ -568,9 +580,8 @@ struct monst *mtmp; mwep && mwep->otyp == ORCISH_BOW)) multishot++; + multishot = rnd(multishot); if ((long)multishot > otmp->quan) multishot = (int)otmp->quan; - if (multishot < 1) multishot = 1; - else multishot = rnd(multishot); } if (canseemon(mtmp)) { @@ -595,9 +606,14 @@ struct monst *mtmp; } m_shot.n = multishot; - for (m_shot.i = 1; m_shot.i <= m_shot.n; m_shot.i++) + for (m_shot.i = 1; m_shot.i <= m_shot.n; m_shot.i++) { + /* this continues even if mtmp gets killed (shot kills + adjacent gas spore and triggers explosion, perhaps) + because they're supposed to have been shot in a rapid + fire volley and conceptually all be in flight at once */ m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby), distmin(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy), otmp); + } m_shot.n = m_shot.i = 0; m_shot.o = STRANGE_OBJECT; m_shot.s = FALSE;