diff --git a/doc/fixes37.0 b/doc/fixes37.0 index f3b1e3754..eb60b59df 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -537,6 +537,8 @@ allow killing your quest leader to open the quest give King Arthur Excalibur when moving the cursor to examine the map, have '^' move to next trap even if that trap is displayed with some other symbol (web, vibrating square) +change valkyrie and warrior (valk quest) monsters from chaotic to lawful +change attendant (healer quest) monster from lawful to neutral Fixes to 3.7.0-x Problems that Were Exposed Via git Repository diff --git a/src/monst.c b/src/monst.c index 4f837d1b3..1755e3072 100644 --- a/src/monst.c +++ b/src/monst.c @@ -15,6 +15,8 @@ #define WT_ELF 800 #define WT_DRAGON 4500 +/* caveat: in other source files, C('X') is used to convert 'X' into '^X' but + here is it used to make color values be conditionally present or absent */ #ifdef C #undef C #endif @@ -38,27 +40,46 @@ * sounds made (MS_* defines), physical size (MZ_* defines), * resistances, resistances conferred (both MR_* defines), * 3 * flag bitmaps (M1_*, M2_*, and M3_* defines respectively), - * difficulty, symbol color (C(x) macro) + * difficulty, symbol color (C(x) macro). * - * For AT_BREA attacks, '# sides' is ignored; 6 is used for most - * damage types, 25 for sleep, not applicable for death or poison. + * The difficulty was generated in separate array monstr[] with + * values calculated by makedefs, but has been moved into mons[] + * since it rarely changes. If a new monster is added or an old + * one undergoes significant change, 'makedefs -m' can be used to + * create a dummy monstr.c containing the calculated difficulty + * (of everything in mons[], not just any new or changed ones), + * then the value(s) can be plugged in here and monstr.c deleted. + * [Note that some monsters might warrant manually calculated + * difficulty, on a case by case basis, instead of blindly using + * the default value produced by makedefs. Or fix the algoritm + * used by makedefs to generate a more appropriate value....] + * + * TODO: difficulty is closely releated to level; its field ought + * to be moved sooner in the permonst struct so that it can become + * part of LVL() instead of remaining an orphan near the end. */ -#define MON(nam, sym, lvl, gen, atk, siz, mr1, mr2, flg1, flg2, flg3, d, col) \ - { \ - {(const char *) 0, (const char *) 0, nam}, \ - sym, lvl, gen, atk, siz, mr1, mr2, flg1, flg2, flg3, d, C(col) \ +#define MON(nam, \ + sym, lvl, gen, atk, siz, mr1, mr2, flg1, flg2, flg3, d, col) \ + { \ + { (const char *) 0, (const char *) 0, nam }, \ + sym, lvl, gen, atk, siz, mr1, mr2, flg1, flg2, flg3, d, C(col) \ } -#define MON3(namm, namf, namn, sym, lvl, gen, atk, siz, mr1, mr2, flg1, flg2, flg3, d, col) \ - { \ - {namm, namf, namn}, \ - sym, lvl, gen, atk, siz, mr1, mr2, flg1, flg2, flg3, d, C(col) \ +#define MON3(namm, namf, namn, \ + sym, lvl, gen, atk, siz, mr1, mr2, flg1, flg2, flg3, d, col) \ + { \ + { namm, namf, namn }, \ + sym, lvl, gen, atk, siz, mr1, mr2, flg1, flg2, flg3, d, C(col) \ } -/* LVL() and SIZ() collect several fields to cut down on number of args - * for MON() +/* LVL() and SIZ() collect several fields to cut down on number of args for + * MON()/MON3(). Some pre-processors limit function-like macros to 15 + * parameters (possibly only pre-ANSI ones these days). */ #define LVL(lvl, mov, ac, mr, aln) lvl, mov, ac, mr, aln #define SIZ(wt, nut, snd, siz) wt, nut, snd, siz -/* ATTK() and A() are to avoid braces and commas within args to MON() */ +/* ATTK() and A() are to avoid braces and commas within args to MON(). + * For AT_BREA attacks, '# sides' is ignored; 6 is used for most + * damage types, 25 for sleep, not applicable for death or poison. + */ #define ATTK(at, ad, n, d) \ { \ at, ad, n, d \ @@ -438,10 +459,11 @@ NEARDATA struct permonst mons_init[] = { M2_NOPOLY | M2_DWARF | M2_STRONG | M2_GREEDY | M2_JEWELS | M2_COLLECT, M3_INFRAVISIBLE | M3_INFRAVISION, 4, CLR_RED), MON("bugbear", S_HUMANOID, LVL(3, 9, 5, 0, -6), (G_GENO | 1), - A(ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), - SIZ(1250, 250, MS_GROWL, MZ_LARGE), 0, 0, M1_HUMANOID | M1_OMNIVORE, - M2_STRONG | M2_COLLECT, M3_INFRAVISIBLE | M3_INFRAVISION, 5, CLR_BROWN), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1250, 250, MS_GROWL, MZ_LARGE), 0, 0, + M1_HUMANOID | M1_OMNIVORE, M2_STRONG | M2_COLLECT, + M3_INFRAVISIBLE | M3_INFRAVISION, 5, CLR_BROWN), MON3("dwarf lord", "dwarf lady", "dwarf leader", S_HUMANOID, LVL(4, 6, 10, 10, 5), (G_GENO | 2), A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, @@ -2837,7 +2859,12 @@ struct permonst _mons2[] = { M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_COLLECT, M3_INFRAVISIBLE, 12, HI_DOMESTIC), - MON("valkyrie", S_HUMAN, LVL(10, 12, 10, 1, -1), G_NOGEN, + /* valk is lawful by default; player valk can be neutral, in which case + role_init() will change this monster and 'warrior' to be neutral too; + if a neutral valk leaves a bones file containing neutral warriors, + the latter will magically turn lawful if encountered by a lawful valk + or any non-valk (for bones on the dungeon side of the portal) */ + MON("valkyrie", S_HUMAN, LVL(10, 12, 10, 1, 1), G_NOGEN, A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), MR_COLD, 0, @@ -2909,8 +2936,8 @@ struct permonst _mons2[] = { | M2_COLLECT | M2_MAGIC, M3_CLOSE | M3_INFRAVISIBLE, 22, HI_LORD), MON("King Arthur", S_HUMAN, LVL(20, 15, 0, 90, 20), (G_NOGEN | G_UNIQ), - A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_WEAP, AD_PHYS, 4, 10), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_WEAP, AD_PHYS, 4, 10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_LEADER, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PNAME | M2_PEACEFUL | M2_STRONG | M2_MALE @@ -2956,8 +2983,8 @@ struct permonst _mons2[] = { | M2_JEWELS | M2_COLLECT | M2_MAGIC, M3_CLOSE | M3_INFRAVISIBLE, 24, HI_LORD), MON("Lord Sato", S_HUMAN, LVL(20, 15, 0, 90, 20), (G_NOGEN | G_UNIQ), - A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_WEAP, AD_PHYS, 4, 10), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_WEAP, AD_PHYS, 4, 10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_LEADER, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PNAME | M2_PEACEFUL | M2_STRONG | M2_MALE @@ -2971,9 +2998,11 @@ struct permonst _mons2[] = { M2_NOPOLY | M2_HUMAN | M2_PNAME | M2_PEACEFUL | M2_STRONG | M2_MALE | M2_COLLECT | M2_MAGIC, M3_CLOSE | M3_INFRAVISIBLE, 22, HI_DOMESTIC), + /* for a valkyrie hero, Norn's alignment will be changed to match hero's + starting alignment */ MON("Norn", S_HUMAN, LVL(20, 15, 0, 90, 0), (G_NOGEN | G_UNIQ), - A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_WEAP, AD_PHYS, 4, 10), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_WEAP, AD_PHYS, 4, 10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1800, 550, MS_LEADER, MZ_HUGE), MR_COLD, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_FEMALE @@ -3165,7 +3194,9 @@ struct permonst _mons2[] = { M2_NOPOLY | M2_ELF | M2_PEACEFUL | M2_COLLECT, M3_INFRAVISION | M3_INFRAVISIBLE, 7, HI_DOMESTIC), #endif - MON("attendant", S_HUMAN, LVL(5, 12, 10, 10, 3), G_NOGEN, + /* attendants used to lawful but have been changed to netural because + grow_up() promotes them to healer and the latter is always neutral */ + MON("attendant", S_HUMAN, LVL(5, 12, 10, 10, 0), G_NOGEN, A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_GUARDIAN, MZ_HUMAN), MR_POISON, 0, @@ -3228,7 +3259,10 @@ struct permonst _mons2[] = { M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_COLLECT | M2_MAGIC, M3_INFRAVISIBLE, 8, HI_DOMESTIC), - MON("warrior", S_HUMAN, LVL(5, 12, 10, 10, -1), G_NOGEN, + /* warriors used to be chaotic but have been changed to lawful because + grow_up() promotes them to valkyrie; for a valkyrie hero, they might + be changed to neutral at game start; see the valkyrie comment above */ + MON("warrior", S_HUMAN, LVL(5, 12, 10, 10, 1), G_NOGEN, A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_GUARDIAN, MZ_HUMAN), 0, 0,