Skip to content

Instantly share code, notes, and snippets.

@lava
Last active June 10, 2018 04:58
Show Gist options
  • Save lava/7239f672b2cb05cba2445976ab7ce494 to your computer and use it in GitHub Desktop.
Save lava/7239f672b2cb05cba2445976ab7ce494 to your computer and use it in GitHub Desktop.
Nethack armor comparison

Nethack armor comparison

Imagine this totally hypothetical situation: You're in a boring meeting without internet, play a round of nethack as a rogue, and come across a dwarvish mithril-coat. Should you replace your current leather armor? On the one hand, mithril sounds pretty nifty. On the other hand, it seems a bit out-of-character for a rogue to wear full plate armor, so surely there are some good reasons to keep the leather?

Luckily, you are a professional developer, so you have a local copy of the games' source code.

On to the actual definitions:

struct objclass objects[] = { 
    // ...

    ARMOR("dwarvish mithril-coat", (char *)0,
    1, 0, 0, 0,	10, 1, 150, 240,  4, 3, ARM_SUIT, MITHRIL, HI_METAL),	

    // ...

    ARMOR("leather armor", (char *)0,
    1, 0, 0, 0,	82, 3, 150,   5,  8, 0, ARM_SUIT, LEATHER, HI_LEATHER),

    // ...
};

Yeah, fun with macros. In src/objects.c:

# define BITS(nmkn,mrg,uskn,ctnr,mgc,chrg,uniq,nwsh,big,tuf,dir,sub,mtrl) \
nmkn,mrg,uskn,0,mgc,chrg,uniq,nwsh,big,tuf,dir,mtrl,sub /* SCO ODT 1.1 cpp fodder */

# define OBJECT(obj,bits,prp,sym,prob,dly,wt,cost,sdam,ldam,oc1,oc2,nut,color) \
{0, 0, (char *)0, bits, prp, sym, dly, COLOR_FIELD(color) \
 prob, wt, cost, sdam, ldam, oc1, oc2, nut}

#define ARMOR(name,desc,kn,mgc,blk,power,prob,delay,wt,cost,ac,can,sub,metal,c) \
    OBJECT( \
	    OBJ(name,desc), BITS(kn,0,1,0,mgc,1,0,0,blk,0,0,sub,metal), power, \
	    ARMOR_CLASS, prob, delay, wt, cost, \
	    0, 0, 10 - ac, can, wt, c )

Untangling the arguments of ARMOR() gives the following map to see in which fields of struct objclass they are stored. Note that some fields have several names:

// name           ->  /* actual name */
// desc           ->  /* description if name is unknown */
// kn             -> oc_name_known
// mgc            -> oc_magic /* inherently magical object */
// blk            -> oc_big, oc_bulky
// power          -> oc_oprop /* property conveyed (invis, etc.) */
// prob           -> oc_prob  /* probability, used in mkobj() */
// delay          -> oc_delay /* delay when using such an object */
// wt             -> oc_weight /* encumbrance (1cn = 0.1lb) */
// cost           -> oc_cost   /* base cost in shops */
// 10 - ac        -> oc_oc1, a_ac /* armor class, used in ARM_BONUS in do.c */
// can            -> oc_oc2, a_can /* magic cancellation */
// sub            -> oc_subtyp, oc_armcat /* armor category */
// metal          -> oc_material
// COLOR_FIELD(c) -> oc_color /* color of the object */

Ok, we can almost understand the object definitions now. First, let's point out the obvious:

  • Both are body armor, their names are known, and they have the same weight
  • Neither is bulky, magic, or conveys any properties
  • Mithril coat has a probability of 10, leather has a probability of 82
  • Mithril is much more valuable (240 base cost vs. 5 for leather)
  • The mithril coat is made of mithril, the leather armor is made of leather (duh)

The fact that the weight is the same is somewhat suprising - mithril is said to be a light material, but it's still a metal after all.

Mithril! All folk desired it. It could be beaten like copper, and polished like glass; and the Dwarves could make of it a metal, light and yet harder than tempered steel. Its beauty was like to that of common silver, but the beauty of mithril did not tarnish or grow dim.

For these, it's not obvious to me what they imply, so let's investigate further:

  • Leather armor has a higher delay (3 vs. 1 for mithril)
  • Their armor class is different
  • Mithril has higher cancellation (ac_can) value (3 vs. 0 for leather)

The only place where ac_can is used is mhitu.c, where your characters (or a monsters) magic cancellation is computed similar to this:

int magic_negation(struct monst *mon) {
   if (mon == &youmonst)
       return max(uarm->a_can, uarmc->a_can, uarmh->a_can, ...);
   else
       return max(which_armor(mon, W_ARM), which_armor(mon, W_ARMC), ...);
}

where uarm, uarmc, uarmh, etc. are the armor slots for your body armor, cloak, helmet, etc. The actual effects of this are somewhat complicated, but roughly higher values are better, with the mithril coat giving a 98% chance to block magic special attacks, as opposed to 0% for the leather armor. A detailed explanation can be found at https://nethackwiki.com/wiki/Magic_cancellation_(3.4.3)

Next, armor class. The only place where a_ac is used in the source is

#define ARM_BONUS(obj)  (objects[(obj)->otyp].a_ac + (obj)->spe \
                - min((int)greatest_erosion(obj),objects[(obj)->otyp].a_ac))

which in turn is only used in do_wear.c like this

void find_ac() {
    int uac = mons[u.umonnum].ac;
    if (uarm) uac -= ARM_BONUS(uarm);
    if (uarmc) uac -= ARM_BONUS(uarmc);
    if (uarmh) uac -= ARM_BONUS(uarmh);
    [...]
}

The handling of armor class gets a bit confusing, because of the triple negation going on. In the old D&D rules that nethack uses, a lower armor class is better, which means higher ARM_BONUS() is better. But the value of ARM_BONUS() is just a_ac reduced by erosion, so a higher value of a_ac is better. What we store in the object is 10-ac, so ultimately lower values of ac mean better protection.

In particular, wearing the dwarwish mithril-coats value of 4 will reduce our characters armor class by 6 points, whereas wearing the leather armor will only reduce it by 2 points. Mithril wins.

Finally, delay. This is simply the time it takes to wear or take off that piece of armor. Note that it's also the time that someone needs to steal that armor from you when it is worn. So the leather armor has one advantage: is actually a bit more more theft-resistant.

Finally, lets see if the material makes any difference. Grepping through the repository for oc_material, LEATHER, and MITHRIL, we find that leather is_flimsy() and is_organic(), whereas mithril is_metallic(). The only important difference it seems to make is during spellcasting, where metallic objects hinder your spellcasting ability. One other is that if you polymorph a leather armor you will create a leather golem, whereas polymorphing a mithril coat will create an iron golem. Also, if you could somehow manage to eat a leather armor, it would break your vegan and vegetarian conducts.

Conclusion: Mithril armor is, in fact, OP and for a non-spellcaster superior to leather armour in every aspect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment