NetHackWiki:Skill table generator
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]); } }