NetHackWiki:Skill table generator

From NetHackWiki
Revision as of 23:25, 13 November 2010 by Paxed (talk | contribs) (use <syntaxhighlight>)
Jump to navigation Jump to search

Here's the program I used to generate Template:Weapon skill table, Template:Combat skill table, and Template:Spell skill table. -- Killian 07:39, 4 November 2006 (UTC)

modified_skills.h should be a copy of skills.h, modified so that P_BARE_HANDS and P_MARTIAL_ARTS have separate numbers.

u_init_snippet.inc should be the struct def_skill definitions in u_init.c, from Skill_A[] to Skill_W[].

This program is designed to work with NetHack 3.4.3. If a new version is released, the program might need changes, and the generated tables might need to be re-generated.

To compile the program, you need a C++ compiler, such as g++. Assuming you've saved the code as skillgen.cpp, you can compile it with

g++ -o skillgen skillgen.c


This page may need to be updated for the current version of NetHack.

It may contain text specific to NetHack 3.4.3. Information on this page may be out of date.

Editors: After reviewing this page and making necessary edits, please change the {{nethack-343}} tag to the current version's tag or {{noversion}} as appropriate.


The code for skillgen.cpp:

#include <string>
#include <iostream>
#include <cctype>
#include <vector>
#include <stdlib.h>

using std::string;
using std::cout;
using std::endl;
using std::toupper;
using std::vector;

#define STEED
#define TOURIST

typedef signed char xchar;

typedef int Skill;
typedef int Level;

#include "modified_skills.h"

struct Role {
    const char *long_name;
    const char *short_name;
    const def_skill *skills;
};

const char *
skill_raw_name(const Skill &s) {
    switch (s) {
        case P_DAGGER:           return "dagger";
        case P_KNIFE:            return "knife";
        case P_AXE:              return "axe";
        case P_PICK_AXE:         return "pick-axe";
        case P_SHORT_SWORD:      return "short sword";
        case P_BROAD_SWORD:      return "broadsword"; // !!!
        case P_LONG_SWORD:       return "long sword";
        case P_TWO_HANDED_SWORD: return "two-handed sword";
        case P_SCIMITAR:         return "scimitar";
        case P_SABER:            return "saber";
        case P_CLUB:             return "club";
        case P_MACE:             return "mace";
        case P_MORNING_STAR:     return "morning star";
        case P_FLAIL:            return "flail";
        case P_HAMMER:           return "hammer";
        case P_QUARTERSTAFF:     return "quarterstaff";
        case P_POLEARMS:         return "polearms";
        case P_SPEAR:            return "spear";
        case P_JAVELIN:          return "javelin";
        case P_TRIDENT:          return "trident";
        case P_LANCE:            return "lance";
        case P_BOW:              return "bow";
        case P_SLING:            return "sling";
        case P_CROSSBOW:         return "crossbow";
        case P_DART:             return "dart";
        case P_SHURIKEN:         return "shuriken";
        case P_BOOMERANG:        return "boomerang";
        case P_WHIP:             return "whip";
        case P_UNICORN_HORN:     return "unicorn horn";

        case P_ATTACK_SPELL:      return "attack";
        case P_HEALING_SPELL:     return "healing";
        case P_DIVINATION_SPELL:  return "divination";
        case P_ENCHANTMENT_SPELL: return "enchantment";
        case P_CLERIC_SPELL:      return "clerical";
        case P_ESCAPE_SPELL:      return "escape";
        case P_MATTER_SPELL:      return "matter";

        case P_BARE_HANDED_COMBAT: return "bare hands";
        case P_MARTIAL_ARTS:       return "martial arts";
        case P_TWO_WEAPON_COMBAT:  return "two weapon combat";
        case P_RIDING:             return "riding";

        default:                 return "<STRANGE SKILL>";
    }
}

const string
init_cap(const string &c) {
    string s(c);

    string::iterator i = s.begin();

    if (i != s.end()) { *i = toupper(*i); }

    return s;
}

const string
skill_full_name(const Skill &s) {
    if (P_FIRST_WEAPON <= s && s <= P_LAST_WEAPON) {
        //return skill_raw_name(s) + string(" skill");
        return skill_raw_name(s);
    }

    if (P_FIRST_SPELL <= s && s <= P_LAST_SPELL) {
        return skill_raw_name(s) + string(" spells");
    }

    return string(skill_raw_name(s));
}

const char *
level_char(const Level &l) {
    switch (l) {
        case P_ISRESTRICTED:   return "-";
        case P_BASIC:        return "b";
        case P_SKILLED:      return "''S''";
        case P_EXPERT:       return "'''E'''";
        case P_MASTER:       return "'''''M'''''";
        case P_GRAND_MASTER: return "'''''GM'''''";
        default:             return "???";
    }
}

const char *
level_full(const Level &l) {
    switch (l) {
        case P_ISRESTRICTED:   return "''(restricted)''";
        case P_BASIC:        return "Basic";
        case P_SKILLED:      return "Skilled";
        case P_EXPERT:       return "Expert";
        case P_MASTER:       return "Master";
        case P_GRAND_MASTER: return "Grand Master";
        default:             return "???";
    }
}

const char *
level_span_class(const Level &l) {
    switch (l) {
        case P_ISRESTRICTED:   return "class=\"restricted\"";
        case P_BASIC:        return "class=\"basic\"";
        case P_SKILLED:      return "class=\"skilled\"";
        case P_EXPERT:       return "class=\"expert\"";
        case P_MASTER:       return "class=\"master\"";
        case P_GRAND_MASTER: return "class=\"grandmaster\"";
        default:             return "class=\"unknown_skill\"";
    }
}

#include "u_init_snippet.inc"

const int NUM_ROLES = 13;
const Role ROLES[NUM_ROLES] = {
    { "Archeologist", "Arc", Skill_A },
    { "Barbarian",    "Bar", Skill_B },
    { "Caveman",      "Cav", Skill_C },
    { "Healer",       "Hea", Skill_H },
    { "Knight",       "Kni", Skill_K },
    { "Monk",         "Mon", Skill_Mon },
    { "Priest",       "Pri", Skill_P },
    { "Rogue",        "Rog", Skill_R },
    { "Ranger",       "Ran", Skill_Ran },
    { "Samurai",      "Sam", Skill_S },
    { "Tourist",      "Tou", Skill_T },
    { "Valkyrie",     "Val", Skill_V },
    { "Wizard",       "Wiz", Skill_W },
};

bool
role_has_skill_level(const Role &r, const Skill &s, const Level &l) {
    for (const def_skill *sp = r.skills; sp->skill != P_NONE; ++sp) {
        if (sp->skill == s) {
            return sp->skmax == l;
        }
    }
    return l == P_ISRESTRICTED;
}

bool
is_restricted(const Role &r, const Skill &s) {
    return role_has_skill_level(r, s, P_ISRESTRICTED);
}

bool
role_has_level(const Role &r, const Level &l) {
    for (const def_skill *sp = r.skills; sp->skill != P_NONE; ++sp) {
        if (sp->skmax == l) {
            return true;
        }
    }
    return l == P_ISRESTRICTED;
}

void
print_header(void) {
    cout << "! Skill \\ Role";

    for (int i = 0; i < NUM_ROLES; i++) {
        const Role &r = ROLES[i];

        cout << " !! [[" << r.long_name << "|" << r.short_name << "]]";
    }

    cout << endl;
}

void
print_role_skill_level(
    const Role &r,
    const Skill &s,
    const char *stringifier(const Skill &)
) {
    for (const def_skill *sp = r.skills; sp->skill != P_NONE; ++sp) {
        if (sp->skill == s) {
            cout << stringifier(sp->skmax);
            return;
        }
    }

    cout << stringifier(P_ISRESTRICTED);
}

void
print_role_skill_level_char(const Role &r, const Skill &s) {
    print_role_skill_level(r, s, level_char);
}

void
print_role_skill_level_full(const Role &r, const Skill &s) {
    print_role_skill_level(r, s, level_full);
}

void
print_role_skill_level_span_class(const Role &r, const Skill &s) {
    print_role_skill_level(r, s, level_span_class);
}


void
print_warning(void) {
    cout << "<!-- GENERATED PAGE; DO NOT EDIT DIRECTLY -->" << endl;
}

void
print_noinclude(const char *category, const char *index) {
    cout << "<noinclude>" << endl;

    cout <<
        "This page was generated using [[NetHackWiki:Skill table generator]]. "
        "Instead of editing this page, edit that program, and re-generate "
        "this page."
        << endl << endl
        << "{{nethack-343}}" << endl
        << "[[Category:" << category << "|" << index << "]]" << endl
        << "</noinclude>" << endl;
}

string
skill_link(const Skill &s) {
    return string("[[") + skill_full_name(s)
        + "|" + skill_raw_name(s) + "]]";
}

void
print_skill_link(const Skill &s) {
    cout << skill_link(s);
}


void
print_role_skills(const Role &r, const Skill &first, const Skill &last) {
    for (Skill s = first; s <= last; ++s) {
        if (is_restricted(r, s)) { continue; }

        cout << "|-" << endl;
        cout << "| ";

        print_skill_link(s);

        cout << " || ";
        print_role_skill_level_full(r, s);
        cout << endl;
    }
}

void
do_skill_table(const char *index, const Skill &first, const Skill &last) {
    print_warning();

    cout << "{| class=\"prettytable " << index << "-skilltable\"" << endl;

    print_header();

    for (Skill s = first; s <= last; ++s) {

        if (s == (P_LAST_WEAPON/2 + 1)) {
            cout << "|-" << endl;
            print_header();
        }

        cout << "|-" << endl;
        cout << "|";

        print_skill_link(s);

        for (int i = 0; i < NUM_ROLES; i++) {
            const Role &r = ROLES[i];

            cout << "\n|";
	    print_role_skill_level_span_class(r, s);
	    cout << "| ";
            print_role_skill_level_char(r, s);
        }

        cout << endl;

    }

    cout << "|}\n";
    print_noinclude("Skill tables", index);
}

void
do_role_skill_table(const Role &r) {
    print_warning();

    cout << "{| class=\"prettytable\"" << endl;

    cout << "! colspan=\"2\" style=\"font-size:larger\" | "
         << r.long_name << " skills" << endl;

    cout << "|-" << endl;
    cout << "! Skill !! Maximum level" << endl;

    cout << "|-" << endl;
    cout << "| colspan=\"2\" align=\"center\" | ''Weapon skills''" << endl;

    print_role_skills(r, P_FIRST_WEAPON, P_LAST_WEAPON);

    cout << "|-" << endl;
    cout << "| colspan=\"2\" align=\"center\" | ''Combat skills''" << endl;

    print_role_skills(r, P_FIRST_H_TO_H, P_LAST_H_TO_H);

    cout << "|-" << endl;
    cout << "| colspan=\"2\" align=\"center\" | ''Spell skills''" << endl;

    print_role_skills(r, P_FIRST_SPELL, P_LAST_SPELL);

    cout << "|}";

    print_noinclude("Skill tables", r.long_name);
}

void
push_matches(
    const Role &r,
    const Skill &first,
    const Skill &last,
    const Level &l,
    vector<string> &v
) {
        for (Skill s = first; s <= last; ++s) {
            if (role_has_skill_level(r, s, l)) {
                v.push_back(skill_link(s));
            }
        }
}

void
print_matches(const char *heading, vector<string> &v) {
    if (!v.empty()) {
        cout << "* ''" << heading << ":'' ";
        for (vector<string>::iterator i = v.begin(); i != v.end(); ++i) {
            if (i != v.begin()) {
                cout << ", ";
            }

            cout << *i;
        }
        cout << endl;
    }
}

void
do_role_skill_table_2(const Role &r) {
    print_warning();

    cout << "{| class=\"prettytable\"" << endl;

    cout << "! colspan=\"2\" style=\"font-size:larger\" | "
         << r.long_name << " skills" << endl;

    cout << "|-" << endl;
    cout << "! Max !! Skills" << endl;

    for (Level l = P_BASIC; l <= P_GRAND_MASTER; l++) {
        if (!role_has_level(r, l)) { continue; }

        cout << "|-" << endl;
        cout << "| " << level_full(l) << endl;

        cout << "|" << endl;

        vector<string> weapon;
        push_matches(r, P_FIRST_WEAPON, P_LAST_WEAPON, l, weapon);
        print_matches("Weapons", weapon);

        vector<string> combat;
        push_matches(r, P_FIRST_H_TO_H, P_LAST_H_TO_H, l, combat);
        print_matches("Combat", combat);

        vector<string> spell;
        push_matches(r, P_FIRST_SPELL, P_LAST_SPELL, l, spell);
        print_matches("Spells", spell);
    }

    cout << "|}";

    print_noinclude("Skill tables", r.long_name);
}

void
do_skill_role_table(const Skill &s) {
    print_warning();

    cout << "{| class=\"prettytable\"" << endl;

    cout << "! colspan=\"2\" style=\"font-size:larger\" | "
         << init_cap(skill_full_name(s)) << endl;

    cout << "|-" << endl;
    cout << "! Role !! Maximum level" << endl;

    for (int i = 0; i < NUM_ROLES; ++i) {
        const Role &r = ROLES[i];

        if (is_restricted(r, s)) { continue; }

        cout << "|-" << endl;
        cout << "| [[" << r.long_name << "]]";
        cout << " || ";
        print_role_skill_level_full(r, s);
        cout << endl;
    }

    cout << "|}";

    string index(init_cap(skill_full_name(s)));
    print_noinclude("Skill-specific skill tables", index.c_str());
}

bool
some_role_has_level(const Skill &s, const Level &l) {
    for (int i = 0; i < NUM_ROLES; ++i) {
        const Role &r = ROLES[i];

        for (const def_skill *sp = r.skills; sp->skill != P_NONE; ++sp) {
            if (sp->skill == s && sp->skmax == l) {
                return true;
            }
        }
    }

    return l == P_ISRESTRICTED;
}

void
push_matching_roles(const Skill &s, const Level &l, vector<string> &v) {
    for (int i = 0; i < NUM_ROLES; ++i) {
        const Role &r = ROLES[i];

        if (role_has_skill_level(r, s, l)) {
            v.push_back(string("[[") + r.long_name + "]]");
        }
    }
}

void
print_matching_roles(vector<string> &v) {
    if (!v.empty()) {
        cout << "* ";

        for (vector<string>::iterator i = v.begin(); i != v.end(); ++i) {
            if (i != v.begin()) {
                cout << ", ";
            }

            cout << *i;
        }
        cout << endl;
    }
}

void
do_skill_role_table_2(const Skill &s) {
    print_warning();

    cout << "{| class=\"prettytable\"" << endl;

    cout << "! colspan=\"2\" style=\"font-size:larger\" | "
         << init_cap(skill_full_name(s)) << endl;

    cout << "|-" << endl;
    cout << "! Max !! Role" << endl;

    for (Level l = P_BASIC; l <= P_GRAND_MASTER; l++) {
        if (!some_role_has_level(s, l)) { continue; }

        cout << "|-" << endl;
        cout << "| " << level_full(l) << endl;

        cout << "|" << endl;

        vector<string> roles;
        push_matching_roles(s, l, roles);
        print_matching_roles(roles);
    }

    cout << "|}";

    string index(init_cap(skill_full_name(s)));
    print_noinclude("Skill-specific skill tables", index.c_str());
}

void
print_usage(const char *name) {
    cout << "Usage:" << endl << endl;

    cout << "  " << name << " weapon|combat|spell" << endl;
    cout << "        ==> overall weapon/combat/spell matrix" << endl << endl;

    cout << "  " << name << " Role" << endl;
    cout << "        ==> role-specific table (type A)" << endl << endl;

    cout << "  " << name << " Rol" << endl;
    cout << "        ==> role-specific table (type B)" << endl << endl;

    cout << "  " << name << " \"skill\"" << endl;
    cout << "        ==> skill-specific table" << endl;
}

int
main(int argc, const char **argv) {
    if (argc == 2) {
        const string arg(argv[1]);

        if (arg == "weapon") {
            do_skill_table("Weapon", P_FIRST_WEAPON, P_LAST_WEAPON);
        } else if (arg == "combat") {
            do_skill_table("Combat", P_FIRST_H_TO_H, P_LAST_H_TO_H);
        } else if (arg == "spell") {
            do_skill_table("Spell", P_FIRST_SPELL, P_LAST_SPELL);
        } else {
            for (int i = 0; i < NUM_ROLES; ++i) {
                const Role &r = ROLES[i];
                if (arg == r.short_name) {
                    do_role_skill_table(r);
                    exit(0);
                } else if (arg == r.long_name) {
                    do_role_skill_table_2(r);
                    exit(0);
                }
            }
            for (Skill s = 0; s < P_NUM_SKILLS; ++s) {
                if (arg == skill_raw_name(s)) {
                    do_skill_role_table_2(s);
                    exit(0);
                }
            }
            print_usage(argv[0]);
        }
    } else {
        print_usage(argv[0]);
    }
}