Difference between revisions of "NetHackWiki:Skill table generator"
Jump to navigation
Jump to search
(here's my table-generating program) |
(small code tweaks; use {{diagonal split header}}) |
||
(15 intermediate revisions by 5 users not shown) | |||
Line 1: | Line 1: | ||
− | + | Program used to generate [[Template:Weapon skill table]], [[Template:Combat skill table]], and [[Template:Spell skill table]]. Original by [[User:Killian|Killian]], modified for 3.6.6 by [[User:Doran|Doran]]. | |
− | + | ==Usage== | |
− | |||
− | |||
− | + | <code>u_init_snippet.inc</code> should be the <code>struct def_skill</code> definitions in [[u_init.c]], from <code>Skill_A[]</code> to <code>Skill_W[]</code>. | |
− | |||
− | |||
− | + | This program is designed to work with NetHack 3.6.6. 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 <tt>skillgen.cpp</tt>, you can compile it with | |
− | + | g++ -o skillgen skillgen.cpp | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | The perl script skillgen.pl can be used to upload the new skill tables. | |
− | |||
− | + | ==skillgen.cpp== | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | <syntaxhighlight lang="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 "include/patchlevel.h" | ||
+ | #include "include/skills.h" | ||
+ | #undef P_MARTIAL_ARTS | ||
+ | #define P_MARTIAL_ARTS 39 | ||
+ | #undef P_NUM_SKILLS | ||
+ | #define P_NUM_SKILLS 40 | ||
+ | #undef P_LAST_H_TO_H | ||
+ | #define P_LAST_H_TO_H P_MARTIAL_ARTS | ||
− | + | struct Role { | |
− | + | const char *long_name; | |
− | + | const char *short_name; | |
− | const | + | const def_skill *skills; |
− | const | + | }; |
− | const | ||
− | |||
const char * | const char * | ||
− | skill_raw_name(Skill s) { | + | skill_raw_name(const Skill &s) { |
switch (s) { | switch (s) { | ||
case P_DAGGER: return "dagger"; | case P_DAGGER: return "dagger"; | ||
Line 83: | Line 58: | ||
case P_PICK_AXE: return "pick-axe"; | case P_PICK_AXE: return "pick-axe"; | ||
case P_SHORT_SWORD: return "short sword"; | case P_SHORT_SWORD: return "short sword"; | ||
− | case P_BROAD_SWORD: return " | + | case P_BROAD_SWORD: return "broadsword"; // !!! |
case P_LONG_SWORD: return "long sword"; | case P_LONG_SWORD: return "long sword"; | ||
case P_TWO_HANDED_SWORD: return "two-handed sword"; | case P_TWO_HANDED_SWORD: return "two-handed sword"; | ||
Line 96: | Line 71: | ||
case P_POLEARMS: return "polearms"; | case P_POLEARMS: return "polearms"; | ||
case P_SPEAR: return "spear"; | case P_SPEAR: return "spear"; | ||
+ | #ifdef P_JAVELIN | ||
case P_JAVELIN: return "javelin"; | case P_JAVELIN: return "javelin"; | ||
+ | #endif | ||
case P_TRIDENT: return "trident"; | case P_TRIDENT: return "trident"; | ||
case P_LANCE: return "lance"; | case P_LANCE: return "lance"; | ||
Line 107: | Line 84: | ||
case P_WHIP: return "whip"; | case P_WHIP: return "whip"; | ||
case P_UNICORN_HORN: return "unicorn horn"; | case P_UNICORN_HORN: return "unicorn horn"; | ||
− | + | ||
case P_ATTACK_SPELL: return "attack"; | case P_ATTACK_SPELL: return "attack"; | ||
case P_HEALING_SPELL: return "healing"; | case P_HEALING_SPELL: return "healing"; | ||
Line 115: | Line 92: | ||
case P_ESCAPE_SPELL: return "escape"; | case P_ESCAPE_SPELL: return "escape"; | ||
case P_MATTER_SPELL: return "matter"; | case P_MATTER_SPELL: return "matter"; | ||
− | + | ||
case P_BARE_HANDED_COMBAT: return "bare hands"; | case P_BARE_HANDED_COMBAT: return "bare hands"; | ||
case P_MARTIAL_ARTS: return "martial arts"; | case P_MARTIAL_ARTS: return "martial arts"; | ||
Line 126: | Line 103: | ||
const string | const string | ||
− | skill_full_name(Skill s) { | + | init_cap(const string &c) { |
+ | string s(c); | ||
+ | |||
+ | string::iterator i = s.begin(); | ||
+ | |||
+ | if (i != s.end()) { *i = toupper(*i); } | ||
+ | |||
+ | return s; | ||
+ | } | ||
+ | |||
+ | const string | ||
+ | all_caps(const string &c) { | ||
+ | string s(c); | ||
+ | |||
+ | string::iterator i = s.begin(); | ||
+ | |||
+ | while (i != s.end()) { *i = toupper(*i); ++i; } | ||
+ | |||
+ | return s; | ||
+ | } | ||
+ | |||
+ | const string | ||
+ | skill_full_name(const Skill &s) { | ||
if (P_FIRST_WEAPON <= s && s <= P_LAST_WEAPON) { | if (P_FIRST_WEAPON <= s && s <= P_LAST_WEAPON) { | ||
− | return skill_raw_name(s) + string(" skill"); | + | //return skill_raw_name(s) + string(" skill"); |
+ | return skill_raw_name(s); | ||
} | } | ||
− | + | ||
if (P_FIRST_SPELL <= s && s <= P_LAST_SPELL) { | if (P_FIRST_SPELL <= s && s <= P_LAST_SPELL) { | ||
return skill_raw_name(s) + string(" spells"); | return skill_raw_name(s) + string(" spells"); | ||
} | } | ||
− | + | ||
return string(skill_raw_name(s)); | return string(skill_raw_name(s)); | ||
} | } | ||
const char * | const char * | ||
− | level_char(Level l) { | + | level_char(const Level &l) { |
switch (l) { | switch (l) { | ||
− | case | + | case P_ISRESTRICTED: return "-"; |
case P_BASIC: return "b"; | case P_BASIC: return "b"; | ||
case P_SKILLED: return "''S''"; | case P_SKILLED: return "''S''"; | ||
Line 151: | Line 151: | ||
} | } | ||
− | + | 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" | #include "u_init_snippet.inc" | ||
Line 182: | Line 195: | ||
{ "Wizard", "Wiz", Skill_W }, | { "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 | void | ||
print_header(void) { | print_header(void) { | ||
− | cout << "! Skill | + | cout << "! {{diagonal split header|[[Skill|Skill]]|[[Role|Role]]}}"; |
for (int i = 0; i < NUM_ROLES; i++) { | for (int i = 0; i < NUM_ROLES; i++) { | ||
const Role &r = ROLES[i]; | const Role &r = ROLES[i]; | ||
− | + | ||
cout << " !! [[" << r.long_name << "|" << r.short_name << "]]"; | cout << " !! [[" << r.long_name << "|" << r.short_name << "]]"; | ||
} | } | ||
− | + | ||
cout << endl; | cout << endl; | ||
} | } | ||
void | void | ||
− | print_role_skill_level(const Role &r, const Skill &s) { | + | 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) { | for (const def_skill *sp = r.skills; sp->skill != P_NONE; ++sp) { | ||
if (sp->skill == s) { | if (sp->skill == s) { | ||
− | cout << | + | cout << stringifier(sp->skmax); |
return; | return; | ||
} | } | ||
} | } | ||
− | + | ||
− | cout << | + | cout << stringifier(P_ISRESTRICTED); |
} | } | ||
void | 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; | 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-" | ||
+ | << VERSION_MAJOR << VERSION_MINOR << PATCHLEVEL << "}}" << 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(); | print_header(); | ||
− | + | ||
for (Skill s = first; s <= last; ++s) { | for (Skill s = first; s <= last; ++s) { | ||
− | + | ||
if (s == (P_LAST_WEAPON/2 + 1)) { | if (s == (P_LAST_WEAPON/2 + 1)) { | ||
cout << "|-" << endl; | cout << "|-" << endl; | ||
print_header(); | print_header(); | ||
} | } | ||
− | + | ||
cout << "|-" << endl; | cout << "|-" << endl; | ||
cout << "|"; | cout << "|"; | ||
− | + | ||
− | + | print_skill_link(s); | |
− | + | ||
− | |||
for (int i = 0; i < NUM_ROLES; i++) { | for (int i = 0; i < NUM_ROLES; i++) { | ||
const Role &r = ROLES[i]; | const Role &r = ROLES[i]; | ||
− | + | ||
− | cout << " || "; | + | cout << "\n|"; |
− | + | print_role_skill_level_span_class(r, s); | |
+ | cout << "| "; | ||
+ | print_role_skill_level_char(r, s); | ||
} | } | ||
− | + | ||
cout << endl; | cout << endl; | ||
} | } | ||
− | + | ||
− | 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|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 !! [[Skill|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|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|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 | void | ||
print_usage(const char *name) { | print_usage(const char *name) { | ||
− | cout << "Usage:" << endl; | + | cout << "Usage:" << endl << endl; |
− | cout << " " << name << " weapon" << endl; | + | |
− | cout << " " << name << " | + | cout << " " << name << " weapon|combat|spell" << endl; |
− | cout << " " << name << " | + | 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; | ||
} | } | ||
Line 255: | Line 574: | ||
main(int argc, const char **argv) { | main(int argc, const char **argv) { | ||
if (argc == 2) { | if (argc == 2) { | ||
− | const string arg(argv[1]); | + | const string arg = all_caps(argv[1]); |
− | + | ||
− | if (arg == " | + | if (arg == "WEAPON") { |
− | do_skill_table(P_FIRST_WEAPON, P_LAST_WEAPON); | + | do_skill_table("Weapon", P_FIRST_WEAPON, P_LAST_WEAPON); |
− | } else if (arg == " | + | } else if (arg == "COMBAT") { |
− | do_skill_table( | + | do_skill_table("Combat", P_FIRST_H_TO_H, P_LAST_H_TO_H); |
− | } else if (arg == " | + | } else if (arg == "SPELL") { |
− | do_skill_table(P_FIRST_SPELL, P_LAST_SPELL); | + | do_skill_table("Spell", P_FIRST_SPELL, P_LAST_SPELL); |
} else { | } else { | ||
+ | for (int i = 0; i < NUM_ROLES; ++i) { | ||
+ | const Role &r = ROLES[i]; | ||
+ | if (arg == all_caps(r.short_name)) { | ||
+ | do_role_skill_table(r); | ||
+ | exit(0); | ||
+ | } else if (arg == all_caps(r.long_name)) { | ||
+ | do_role_skill_table_2(r); | ||
+ | exit(0); | ||
+ | } | ||
+ | } | ||
+ | for (Skill s = 0; s < P_NUM_SKILLS; ++s) { | ||
+ | if (arg == all_caps(skill_raw_name(s))) { | ||
+ | do_skill_role_table_2(s); | ||
+ | exit(0); | ||
+ | } | ||
+ | } | ||
print_usage(argv[0]); | print_usage(argv[0]); | ||
} | } | ||
Line 270: | Line 605: | ||
} | } | ||
} | } | ||
− | < | + | </syntaxhighlight> |
+ | |||
+ | ==skillgen.pl== | ||
+ | |||
+ | <syntaxhighlight lang="pl"> | ||
+ | #!/usr/bin/env perl | ||
+ | |||
+ | use MediaWiki::API; | ||
+ | |||
+ | $user = shift @ARGV; | ||
+ | $pass = shift @ARGV; | ||
+ | |||
+ | print "Using $user $pass\n"; | ||
+ | |||
+ | $mw = MediaWiki::API->new(); | ||
+ | $mw->{config}->{api_url} = 'https://nethackwiki.com/w/api.php'; | ||
+ | |||
+ | $mw->login( { lgname => $user, lgpassword => $pass } ) | ||
+ | || die $mw->{error}->{code} . ': ' . $mw->{error}->{details}; | ||
+ | |||
+ | @list = ( | ||
+ | "Template:Archeologist skill table", | ||
+ | "Template:Barbarian skill table", | ||
+ | "Template:Caveman skill table", | ||
+ | "Template:Healer skill table", | ||
+ | "Template:Knight skill table", | ||
+ | "Template:Monk skill table", | ||
+ | "Template:Priest skill table", | ||
+ | "Template:Ranger skill table", | ||
+ | "Template:Rogue skill table", | ||
+ | "Template:Samurai skill table", | ||
+ | "Template:Tourist skill table", | ||
+ | "Template:Valkyrie skill table", | ||
+ | "Template:Wizard skill table", | ||
+ | # roles above here, others below | ||
+ | "Template:Attack spells skill table", | ||
+ | "Template:Axe skill table", | ||
+ | "Template:Bare hands skill table", | ||
+ | "Template:Boomerang skill table", | ||
+ | "Template:Bow skill table", | ||
+ | "Template:Broadsword skill table", | ||
+ | "Template:Clerical spells skill table", | ||
+ | "Template:Club skill table", | ||
+ | "Template:Combat skill table", | ||
+ | "Template:Crossbow skill table", | ||
+ | "Template:Dagger skill table", | ||
+ | "Template:Dart skill table", | ||
+ | "Template:Divination spells skill table", | ||
+ | "Template:Enchantment spells skill table", | ||
+ | "Template:Escape spells skill table", | ||
+ | "Template:Flail skill table", | ||
+ | "Template:Hammer skill table", | ||
+ | "Template:Healing spells skill table", | ||
+ | #"Template:Javelin skill table", # skill no longer exists since 3.6.0 | ||
+ | "Template:Knife skill table", | ||
+ | "Template:Lance skill table", | ||
+ | "Template:Long sword skill table", | ||
+ | "Template:Mace skill table", | ||
+ | "Template:Martial arts skill table", | ||
+ | "Template:Matter spells skill table", | ||
+ | "Template:Morning star skill table", | ||
+ | "Template:Pick-axe skill table", | ||
+ | "Template:Polearms skill table", | ||
+ | "Template:Quarterstaff skill table", | ||
+ | "Template:Riding skill table", | ||
+ | "Template:Saber skill table", | ||
+ | "Template:Scimitar skill table", | ||
+ | "Template:Short sword skill table", | ||
+ | "Template:Shuriken skill table", | ||
+ | "Template:Sling skill table", | ||
+ | "Template:Spear skill table", | ||
+ | "Template:Spell skill table", | ||
+ | "Template:Trident skill table", | ||
+ | "Template:Two weapon combat skill table", | ||
+ | "Template:Two-handed sword skill table", | ||
+ | "Template:Unicorn horn skill table", | ||
+ | "Template:Weapon skill table", | ||
+ | "Template:Whip skill table", | ||
+ | ); | ||
+ | |||
+ | foreach $item (@list) { | ||
+ | $page = $item; | ||
+ | $page =~ s/ /_/g; | ||
+ | |||
+ | $arg = $item; | ||
+ | $arg =~ s/ skill table$//g; | ||
+ | $arg =~ s/ spells$//g; | ||
+ | $arg =~ s/^Template://g; | ||
+ | |||
+ | print "./skillgen \"$arg\" > \"$page\"\n"; | ||
+ | `./skillgen "$arg" > "$page"`; | ||
+ | $text = `cat "$page"`; | ||
+ | |||
+ | print "getting token\n"; | ||
+ | $csrf = | ||
+ | $mw->api( { | ||
+ | action => 'query', | ||
+ | meta => 'tokens' | ||
+ | } ) | ||
+ | || die $mw->{error}->{code} . ': ' . $mw->{error}->{details}; | ||
+ | |||
+ | print "doing edit\n"; | ||
+ | $mw->edit( { | ||
+ | action => "edit", | ||
+ | title => $page, | ||
+ | text => $text, | ||
+ | token => $csrf | ||
+ | } ) | ||
+ | || die $mw->{error}->{code} . ': ' . $mw->{error}->{details}; | ||
+ | |||
+ | sleep 5; | ||
+ | } | ||
+ | |||
+ | print "logout\n"; | ||
+ | $mw->logout() | ||
+ | || die $mw->{error}->{code} . ': ' . $mw->{error}->{details}; | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | {{nethack-366}} | ||
+ | [[Category:Skill tables| ]] |
Latest revision as of 04:05, 17 January 2021
Program used to generate Template:Weapon skill table, Template:Combat skill table, and Template:Spell skill table. Original by Killian, modified for 3.6.6 by Doran.
Usage
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.6.6. 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.cpp
The perl script skillgen.pl can be used to upload the new skill tables.
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 "include/patchlevel.h"
#include "include/skills.h"
#undef P_MARTIAL_ARTS
#define P_MARTIAL_ARTS 39
#undef P_NUM_SKILLS
#define P_NUM_SKILLS 40
#undef P_LAST_H_TO_H
#define P_LAST_H_TO_H P_MARTIAL_ARTS
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";
#ifdef P_JAVELIN
case P_JAVELIN: return "javelin";
#endif
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
all_caps(const string &c) {
string s(c);
string::iterator i = s.begin();
while (i != s.end()) { *i = toupper(*i); ++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 << "! {{diagonal split header|[[Skill|Skill]]|[[Role|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-"
<< VERSION_MAJOR << VERSION_MINOR << PATCHLEVEL << "}}" << 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|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 !! [[Skill|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|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|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 = all_caps(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 == all_caps(r.short_name)) {
do_role_skill_table(r);
exit(0);
} else if (arg == all_caps(r.long_name)) {
do_role_skill_table_2(r);
exit(0);
}
}
for (Skill s = 0; s < P_NUM_SKILLS; ++s) {
if (arg == all_caps(skill_raw_name(s))) {
do_skill_role_table_2(s);
exit(0);
}
}
print_usage(argv[0]);
}
} else {
print_usage(argv[0]);
}
}
skillgen.pl
#!/usr/bin/env perl
use MediaWiki::API;
$user = shift @ARGV;
$pass = shift @ARGV;
print "Using $user $pass\n";
$mw = MediaWiki::API->new();
$mw->{config}->{api_url} = 'https://nethackwiki.com/w/api.php';
$mw->login( { lgname => $user, lgpassword => $pass } )
|| die $mw->{error}->{code} . ': ' . $mw->{error}->{details};
@list = (
"Template:Archeologist skill table",
"Template:Barbarian skill table",
"Template:Caveman skill table",
"Template:Healer skill table",
"Template:Knight skill table",
"Template:Monk skill table",
"Template:Priest skill table",
"Template:Ranger skill table",
"Template:Rogue skill table",
"Template:Samurai skill table",
"Template:Tourist skill table",
"Template:Valkyrie skill table",
"Template:Wizard skill table",
# roles above here, others below
"Template:Attack spells skill table",
"Template:Axe skill table",
"Template:Bare hands skill table",
"Template:Boomerang skill table",
"Template:Bow skill table",
"Template:Broadsword skill table",
"Template:Clerical spells skill table",
"Template:Club skill table",
"Template:Combat skill table",
"Template:Crossbow skill table",
"Template:Dagger skill table",
"Template:Dart skill table",
"Template:Divination spells skill table",
"Template:Enchantment spells skill table",
"Template:Escape spells skill table",
"Template:Flail skill table",
"Template:Hammer skill table",
"Template:Healing spells skill table",
#"Template:Javelin skill table", # skill no longer exists since 3.6.0
"Template:Knife skill table",
"Template:Lance skill table",
"Template:Long sword skill table",
"Template:Mace skill table",
"Template:Martial arts skill table",
"Template:Matter spells skill table",
"Template:Morning star skill table",
"Template:Pick-axe skill table",
"Template:Polearms skill table",
"Template:Quarterstaff skill table",
"Template:Riding skill table",
"Template:Saber skill table",
"Template:Scimitar skill table",
"Template:Short sword skill table",
"Template:Shuriken skill table",
"Template:Sling skill table",
"Template:Spear skill table",
"Template:Spell skill table",
"Template:Trident skill table",
"Template:Two weapon combat skill table",
"Template:Two-handed sword skill table",
"Template:Unicorn horn skill table",
"Template:Weapon skill table",
"Template:Whip skill table",
);
foreach $item (@list) {
$page = $item;
$page =~ s/ /_/g;
$arg = $item;
$arg =~ s/ skill table$//g;
$arg =~ s/ spells$//g;
$arg =~ s/^Template://g;
print "./skillgen \"$arg\" > \"$page\"\n";
`./skillgen "$arg" > "$page"`;
$text = `cat "$page"`;
print "getting token\n";
$csrf =
$mw->api( {
action => 'query',
meta => 'tokens'
} )
|| die $mw->{error}->{code} . ': ' . $mw->{error}->{details};
print "doing edit\n";
$mw->edit( {
action => "edit",
title => $page,
text => $text,
token => $csrf
} )
|| die $mw->{error}->{code} . ': ' . $mw->{error}->{details};
sleep 5;
}
print "logout\n";
$mw->logout()
|| die $mw->{error}->{code} . ': ' . $mw->{error}->{details};