Difference between revisions of "NetHackWiki:Skill table generator"

From NetHackWiki
Jump to navigation Jump to search
(now supports skill-specific and role-specific tables)
(now with two different role-specific table formats)
Line 1: Line 1:
Here's the program I used to generate [[Template:Weapon skill table]], [[Template:Combat skill table]], and [[Template:Spell skill table]]. <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>. -- [[User:Killian|Killian]] 04:36, 4 November 2006 (UTC)
+
Here's the program I used to generate [[Template:Weapon skill table]], [[Template:Combat skill table]], and [[Template:Spell skill table]]. -- [[User:Killian|Killian]] 07:39, 4 November 2006 (UTC)
 +
 
 +
<code>modified_skills.h</code> should be a copy of [[skills.h]], modified so that <code>P_BARE_HANDS</code> and <code>P_MARTIAL_ARTS</code> have separate numbers.
 +
 
 +
<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>.
  
 
<pre>
 
<pre>
Line 5: Line 9:
 
#include <iostream>
 
#include <iostream>
 
#include <cctype>
 
#include <cctype>
 +
#include <vector>
  
 
using std::string;
 
using std::string;
Line 10: Line 15:
 
using std::endl;
 
using std::endl;
 
using std::toupper;
 
using std::toupper;
 +
using std::vector;
  
typedef int Skill;
+
#define STEED
 +
#define TOURIST
  
const Skill P_DAGGER = 1;
+
typedef signed char xchar;
const Skill P_KNIFE = 2;
 
const Skill P_AXE = 3;
 
const Skill P_PICK_AXE = 4;
 
const Skill P_SHORT_SWORD = 5;
 
const Skill P_BROAD_SWORD = 6;
 
const Skill P_LONG_SWORD = 7;
 
const Skill P_TWO_HANDED_SWORD = 8;
 
const Skill P_SCIMITAR = 9;
 
const Skill P_SABER = 10;
 
const Skill P_CLUB = 11;
 
const Skill P_MACE = 12;
 
const Skill P_MORNING_STAR = 13;
 
const Skill P_FLAIL = 14;
 
const Skill P_HAMMER = 15;
 
const Skill P_QUARTERSTAFF = 16;
 
const Skill P_POLEARMS = 17;
 
const Skill P_SPEAR = 18;
 
const Skill P_JAVELIN = 19;
 
const Skill P_TRIDENT = 20;
 
const Skill P_LANCE = 21;
 
const Skill P_BOW = 22;
 
const Skill P_SLING = 23;
 
const Skill P_CROSSBOW = 24;
 
const Skill P_DART = 25;
 
const Skill P_SHURIKEN = 26;
 
const Skill P_BOOMERANG = 27;
 
const Skill P_WHIP = 28;
 
const Skill P_UNICORN_HORN = 29;
 
  
const Skill P_FIRST_WEAPON = P_DAGGER;
+
typedef int Skill;
const Skill P_LAST_WEAPON = P_UNICORN_HORN;
+
typedef int Level;
  
const Skill P_ATTACK_SPELL = 30;
+
#include "modified_skills.h"
const Skill P_HEALING_SPELL = 31;
 
const Skill P_DIVINATION_SPELL = 32;
 
const Skill P_ENCHANTMENT_SPELL = 33;
 
const Skill P_CLERIC_SPELL = 34;
 
const Skill P_ESCAPE_SPELL = 35;
 
const Skill P_MATTER_SPELL = 36;
 
  
const Skill P_FIRST_SPELL = P_ATTACK_SPELL;
+
struct Role {
const Skill P_LAST_SPELL = P_MATTER_SPELL;
+
    const char *long_name;
 
+
    const char *short_name;
const Skill P_BARE_HANDED_COMBAT = 37;
+
    const def_skill *skills;
const Skill P_MARTIAL_ARTS = 38;
+
};
const Skill P_TWO_WEAPON_COMBAT = 39;
 
const Skill P_RIDING = 40;
 
 
 
const Skill P_FIRST_COMBAT = P_BARE_HANDED_COMBAT;
 
const Skill P_LAST_COMBAT = P_RIDING;
 
 
 
const Skill P_FIRST = P_DAGGER;
 
const Skill P_LAST = P_RIDING;
 
 
 
const Skill P_NONE = 0;
 
 
 
 
 
 
 
typedef int Level;
 
const Level P_RESTRICTED = 0;
 
const Level P_BASIC = 1;
 
const Level P_SKILLED = 2;
 
const Level P_EXPERT = 3;
 
const Level P_MASTER = 4;
 
const Level P_GRAND_MASTER = 5;
 
  
 
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 136: Line 89:
 
     string::iterator i = s.begin();
 
     string::iterator i = s.begin();
 
      
 
      
     if (i) { *i = toupper(*i); }
+
     if (i != s.end()) { *i = toupper(*i); }
 
      
 
      
 
     return s;
 
     return s;
Line 157: Line 110:
 
level_char(const Level &l) {
 
level_char(const Level &l) {
 
     switch (l) {
 
     switch (l) {
         case P_RESTRICTED:  return "-";
+
         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 170: Line 123:
 
level_full(const Level &l) {
 
level_full(const Level &l) {
 
     switch (l) {
 
     switch (l) {
         case P_RESTRICTED:  return "''(restricted)''";
+
         case P_ISRESTRICTED:  return "''(restricted)''";
 
         case P_BASIC:        return "Basic";
 
         case P_BASIC:        return "Basic";
 
         case P_SKILLED:      return "Skilled";
 
         case P_SKILLED:      return "Skilled";
Line 180: Line 133:
 
}
 
}
  
struct def_skill {
 
    Skill skill;
 
    Level level;
 
};
 
 
struct Role {
 
    const char *long_name;
 
    const char *short_name;
 
    const def_skill *skills;
 
};
 
 
#define STEED
 
#define TOURIST
 
 
#include "u_init_snippet.inc"
 
#include "u_init_snippet.inc"
  
Line 211: Line 151:
 
     { "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
 
bool
 
is_restricted(const Role &r, const Skill &s) {
 
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) {
 
     for (const def_skill *sp = r.skills; sp->skill != P_NONE; ++sp) {
         if (sp->skill == s) {
+
         if (sp->skmax == l) {
             return false;
+
             return true;
 
         }
 
         }
 
     }
 
     }
   
+
     return l == P_ISRESTRICTED;
     return true;
 
 
}
 
}
  
Line 237: Line 191:
  
 
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 << level_char(sp->level);
+
             cout << stringifier(sp->skmax);
 
             return;
 
             return;
 
         }
 
         }
 
     }
 
     }
 
      
 
      
     cout << level_char(P_RESTRICTED);
+
     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
 
void
 
print_role_skill_level_full(const Role &r, const Skill &s) {
 
print_role_skill_level_full(const Role &r, const Skill &s) {
     for (const def_skill *sp = r.skills; sp->skill != P_NONE; ++sp) {
+
     print_role_skill_level(r, s, level_full);
        if (sp->skill == s) {
 
            cout << level_full(sp->level);
 
            return;
 
        }
 
    }
 
   
 
    cout << level_full(P_RESTRICTED);
 
 
}   
 
}   
  
Line 274: Line 230:
 
         "this page."
 
         "this page."
 
         << "</noinclude>" << endl;
 
         << "</noinclude>" << endl;
 +
}
 +
 +
string
 +
skill_link(const Skill &s) {
 +
    return string("[[") + skill_full_name(s)
 +
        + "|" + skill_raw_name(s) + "]]";
 
}
 
}
  
 
void
 
void
 
print_skill_link(const Skill &s) {
 
print_skill_link(const Skill &s) {
     cout << "[[" << skill_full_name(s)  
+
     cout << skill_link(s);
            << "|" << skill_raw_name(s) << "]]";
 
 
}
 
}
 +
  
 
void
 
void
Line 322: Line 284:
 
              
 
              
 
             cout << " || ";
 
             cout << " || ";
             print_role_skill_level(r, s);
+
             print_role_skill_level_char(r, s);
 
         }
 
         }
 
          
 
          
Line 354: Line 316:
 
     cout << "| colspan=\"2\" align=\"center\" | ''Combat skills''" << endl;
 
     cout << "| colspan=\"2\" align=\"center\" | ''Combat skills''" << endl;
 
      
 
      
     print_role_skills(r, P_FIRST_COMBAT, P_LAST_COMBAT);
+
     print_role_skills(r, P_FIRST_H_TO_H, P_LAST_H_TO_H);
 
      
 
      
 
     cout << "|-" << endl;
 
     cout << "|-" << endl;
Line 360: Line 322:
  
 
     print_role_skills(r, P_FIRST_SPELL, P_LAST_SPELL);
 
     print_role_skills(r, P_FIRST_SPELL, P_LAST_SPELL);
 +
   
 +
    cout << "|}";
 +
   
 +
    print_noinclude();
 +
}
 +
 +
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 << "|}";
 
     cout << "|}";
Line 397: Line 427:
 
void
 
void
 
print_usage(const char *name) {
 
print_usage(const char *name) {
     cout << "Usage:" << endl;
+
     cout << "Usage:" << endl << endl;
 +
   
 
     cout << "  " << name << " weapon|combat|spell" << endl;
 
     cout << "  " << name << " weapon|combat|spell" << endl;
     cout << "  " << name << " {Role}|{Rol}" << endl;
+
    cout << "        ==> overall weapon/combat/spell matrix" << endl << endl;
     cout << "  " << name << " {Skill}" << 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 411: Line 450:
 
             do_skill_table(P_FIRST_WEAPON, P_LAST_WEAPON);
 
             do_skill_table(P_FIRST_WEAPON, P_LAST_WEAPON);
 
         } else if (arg == "combat") {
 
         } else if (arg == "combat") {
             do_skill_table(P_FIRST_COMBAT, P_LAST_COMBAT);
+
             do_skill_table(P_FIRST_H_TO_H, P_LAST_H_TO_H);
 
         } else if (arg == "spell") {
 
         } else if (arg == "spell") {
 
             do_skill_table(P_FIRST_SPELL, P_LAST_SPELL);
 
             do_skill_table(P_FIRST_SPELL, P_LAST_SPELL);
Line 417: Line 456:
 
             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];
                 if (arg == r.long_name || arg == r.short_name) {
+
                 if (arg == r.short_name) {
 
                     do_role_skill_table(r);
 
                     do_role_skill_table(r);
 +
                    exit(0);
 +
                } else if (arg == r.long_name) {
 +
                    do_role_skill_table_2(r);
 
                     exit(0);
 
                     exit(0);
 
                 }
 
                 }
 
             }
 
             }
             for (Skill s = P_FIRST; s <= P_LAST; ++s) {
+
             for (Skill s = 0; s < P_NUM_SKILLS; ++s) {
 
                 if (arg == skill_raw_name(s)) {
 
                 if (arg == skill_raw_name(s)) {
 
                     do_skill_role_table(s);
 
                     do_skill_role_table(s);
Line 434: Line 476:
 
     }
 
     }
 
}
 
}
<pre>
+
</pre>

Revision as of 07:39, 4 November 2006

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[].

#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 "broad sword";
        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");
    }
    
    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(void) {
    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."
        << "</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 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();
}

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();
}

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();
}

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 << "|}";
    
    print_noinclude();
}
   
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(P_FIRST_WEAPON, P_LAST_WEAPON);
        } else if (arg == "combat") {
            do_skill_table(P_FIRST_H_TO_H, P_LAST_H_TO_H);
        } else if (arg == "spell") {
            do_skill_table(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(s);
                    exit(0);
                }
            }
            print_usage(argv[0]);
        }
    } else {
        print_usage(argv[0]);
    }
}