- 6 Talk
Wikihack: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.
#include <string>
#include <iostream>
#include <cctype>
#include <vector>
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 "???";
}
}
#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_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 [[Wikihack: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\"" << 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 << " || ";
print_role_skill_level_char(r, s);
}
cout << endl;
}
cout << "|}";
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]);
}
}