From 212623962e3e7de39e6436b6e3a1f28f35958386 Mon Sep 17 00:00:00 2001 From: "nethack.rankin" Date: Sun, 17 Dec 2006 05:47:17 +0000 Subject: [PATCH] multi-shot throwing for knives & spears (trunk only) Make all stackable weapons capable of multi-shot volleys when thrown. Affects knives, spears & javelins, and boomerangs; requires advanced skill assignment (skilled: 1-2 missiles per throw, expert: 1-3 missiles) or role-specific bonus (ranger class's general +1 bonus is the only one that applies to any of these weapon types). For monsters, prince-caste get 1-3 missiles and lord-caste get 1-2, as before, with fake player monsters now also getting 1-2; those counts apply to all stackable weapons regardless of whether the species or role ordinarily uses whatever is being thrown. Related changes: monks now get a role-based +1 count for shuriken, throwing 1-2 instead of just one (they're only allowed to achieve basic skill so won't reach any higher volley count). Monster monks and ninjas get that too; ninjas now get the same for darts and they're guaranteed weapons in starting inventory. Also, fake player rogues now sometimes get orcish daggers instead of short sword, providing a decent chance to occasionally have Grimtooth be randomly generated on the Astral level. Potentially controversial: wizards can still become expert in dagger skill and receive the to-hit and damage bonuses for that when throwing as well as when wielding, but the number of missiles for them has now been reduced to 1-2 (in other words, going from skilled to expert no longer improves the max count for the volley amount for wizard role). They're supposed to be spellcasters; being able to throw up to three +7 daggers at a pop was a big temptation for resorting to brute force, particularly since they'll already want highest dagger skill for wielding Magicbane. To do: throwing multiple boomerangs either needs to behave as if they're all in flight before the first returns, or else the volley needs to be cut short if one comes back and isn't successfully caught. The latter is a lot easier to do but the former fits better with what multi- shot volley is supposed to represent. Another alternative is to change them to no longer be stackable, then this sequencing issue goes away. To do too: make worm teeth and crysknives become stackable like the other knife-skill weapons. --- doc/fixes35.0 | 1 + include/extern.h | 1 + src/dothrow.c | 68 ++++++++++++++++++++++++++++++++---------------- src/makemon.c | 5 +++- src/mplayer.c | 9 ++++--- src/mthrowu.c | 30 ++++++++++++++++----- 6 files changed, 80 insertions(+), 34 deletions(-) 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;