/* I understand that installing something like this is not a walk in the park, and its
 * also extremely easy after you get the hang of mudcoding, as long as you can think straight.
 * This code isn't the best documented, so if you have trouble understanding how I wrote it,
 * just take some time to think about what is trying to be accomplished. Given the difficulty
 * of the task I've made it easy to place what goes where by suggesting where to put it.
 * Some of these locations are definite, others are just suggestions based on my own
 * expertise. Make a backup before you play with this, if you screw up you'll want to start
 * over. *DISCLAIMER* This code has been tested to a reasonable degree, and if it fucks
 * up your mud its probably YOUR fault. It was also designed to work for SWRs only.
 * Alright, questions and comments, possibly thanks can be directed to ghoulavenger@hotmail.com.
 * -Arcturus
 */
/* In Mud.h */
/* under typedef struct skill_type SKILL_TYPE;
typedef  struct   skill_req      SKILL_REQ;


/* Next section right about struct skill_type */
/* Two different skill requirement types to worry about for now.
 * Skills account for spells, feats, and any other with a skill sn.
 * Attributes account for base attributes, like strength rating.
 * -Arcturus
 */
#define REQ_SKILL        0
#define REQ_ATTRIBUTE    1

struct skill_req
{
   SKILL_REQ  *next_req;
   SKILL_REQ  *prev_req;
   char       *name;
   sh_int     attribute;
   sh_int     prof;
   sh_int     type;
};

/* Put this in struct skill_type */
    /* For Skill Requirements, by Arcturus */
    SKILL_REQ *next_req;
    SKILL_REQ *prev_req;
    SKILL_REQ *first_req;
    SKILL_REQ *last_req;

/* Put this right below ability class defines. */
/* Attribute List for Skill Requirements. */
#define ATTRIBUTE_STR  0
#define ATTRIBUTE_INT  1
#define ATTRIBUTE_WIS  2
#define ATTRIBUTE_DEX  3
#define ATTRIBUTE_CON  4
#define ATTRIBUTE_CHA  5
#define ATTRIBUTE_LCK  6
#define ATTRIBUTE_FRC  7
#define MAX_ATTRIBUTE  8 /* Number of attributes. */

/* Under this
extern   char *  const              ability_name   [MAX_ABILITY];
 * Place this */
extern   char *  const              attribute_name [MAX_ATTRIBUTE];

/* tables.c fwrite_skill after minlevel */
        if ( skill->first_req )
        {
           SKILL_REQ *requirement;
           for( requirement = skill->first_req; requirement; requirement = requirement->next_req )
           {
              if(requirement->type == REQ_SKILL )
                 fprintf(fpout, "REQ          %s~  %d\n", requirement->name,    requirement->prof );
              else if(requirement->type == REQ_ATTRIBUTE )
                 fprintf(fpout, "REQSTAT      %d %d\n", requirement->attribute, requirement->prof );
           }
        }

/* fread_skill under case 'R' */
            if( !str_cmp( word, "REQ" ) )
            {
               SKILL_REQ *requirement;
               CREATE( requirement, SKILL_REQ, 1 );
               requirement->name = STRALLOC( fread_string( fp ) );
               requirement->prof = fread_number( fp );
               requirement->type = REQ_SKILL;
               LINK( requirement , skill->first_req, skill->last_req, next_req, prev_req );
               fMatch = TRUE;
            }
            if( !str_cmp( word, "REQSTAT" ) )
            {
               SKILL_REQ *requirement;
               CREATE( requirement, SKILL_REQ, 1 );
               requirement->attribute = fread_number( fp );
               requirement->prof      = fread_number( fp );
               requirement->name      = str_dup("Attribute");
               requirement->type      = REQ_ATTRIBUTE;
               LINK( requirement , skill->first_req, skill->last_req, next_req, prev_req );
               fMatch = TRUE;
            }

/* const.c under ability_name */
char *  const   attribute_name [MAX_ATTRIBUTE] =
{
   "strength", "intelligence", "wisdom", "dexterity", "constitution",
   "charisma", "luck", "force"
};

/* under this
ROOM_INDEX_DATA *generate_exit( ROOM_INDEX_DATA *in_room, EXIT_DATA **pexit );
 * place this */
bool  meets_reqs( CHAR_DATA *ch, int sn );

/* in do_practice after this
        if ( can_prac &&  !IS_NPC(ch)
        &&   ch->skill_level[skill_table[sn]->guild] < skill_table[sn]->min_level  )
        {
            act( AT_TELL, "$n tells you 'You're not ready to learn that yet...'",
                mob, NULL, ch, TO_VICT );
            return;
        }
 * place this */
        if( !meets_reqs(ch, sn) )
        {
            act( AT_TELL, "$n tells you 'You're not ready to learn that yet...'",
                mob, NULL, ch, TO_VICT );
            return;
        }

/* End of act_info.c */
/* This is for a skill tree type deal, where theres requirements to learn skills.
 * This goes through the requirement list and compares stuff.
 * -Arcturus
 */
bool meets_reqs( CHAR_DATA *ch, int sn )
{
   SKILL_REQ *requirement;
   SKILLTYPE *skill;
   int opsn;

   if( IS_NPC(ch) )
       return TRUE;
   if( (skill = get_skilltype(sn)) == NULL )
       return FALSE;
   if( !skill->first_req )
       return TRUE;

   for( requirement = skill->first_req; requirement; requirement = requirement->next_req )
   {
      switch(requirement->type)
      {
      default: bug("Bad %d requirement type.", requirement->type ); break;
      case REQ_SKILL:
         opsn = skill_lookup( requirement->name );
         if(opsn < 0)
            continue;
         if( ch->pcdata->learned[opsn] < requirement->prof )
            return FALSE;
         break;
      case REQ_ATTRIBUTE:
         switch(requirement->attribute)
         {
         default: bug("Bad %d attribute skill requirement.", requirement->attribute ); break;
         case ATTRIBUTE_STR:
             if( ch->perm_str < requirement->prof )
                return FALSE;
             break;
         case ATTRIBUTE_INT:
             if( ch->perm_int < requirement->prof )
                return FALSE;
             break;
         case ATTRIBUTE_WIS:
             if( ch->perm_wis < requirement->prof )
                return FALSE;
             break;
         case ATTRIBUTE_DEX:
             if( ch->perm_dex < requirement->prof )
                return FALSE;
             break;
         case ATTRIBUTE_CON:
             if( ch->perm_con < requirement->prof )
                return FALSE;
             break;
         case ATTRIBUTE_CHA:
             if ( ch->perm_cha < requirement->prof )
                return FALSE;
             break;
         case ATTRIBUTE_LCK:
             if ( ch->perm_lck < requirement->prof )
                return FALSE;
             break;
         case ATTRIBUTE_FRC:
             if ( ch->perm_frc < requirement->prof )
                return FALSE;
             break;
         }
         break;
      }
   }
   return TRUE;
}

/* Skills.c */
/* In do_sset in the field list, add remreq and addreq */
/* in do_sset after this:
        if ( !str_cmp( arg2, "teachers" ) )
        {
            if ( skill->teachers )
              DISPOSE(skill->teachers);
            if ( str_cmp( argument, "clear" ) )
              skill->teachers = str_dup( argument );
            send_to_char( "Ok.\n\r", ch );
            return;
        }
   Place this */
        if ( !str_cmp( arg2, "remreq" ) )
        {
           SKILL_REQ *requirement;
           int num = atoi(argument);

           if( !skill->first_req )
           {
              send_to_char("No requirements to remove.\n\r", ch);
              return;
           }
           if( !is_number(argument) || argument[0] == '\0')
           {
              send_to_char("Remove which requirement?\n\r", ch);
              return;
           }
           if(num == 1)
           {
              requirement = skill->first_req;
              UNLINK(requirement, skill->first_req, skill->last_req, next_req, prev_req );
              DISPOSE(requirement);
              send_to_char( "Ok.\n\r", ch);
              return;
           }
           else
           {
              int counter = 0;
              for( requirement = skill->first_req; requirement; requirement = requirement->next_req )
              {
                 ++counter;
                 if(counter == num)
                 {
                    UNLINK( requirement, skill->first_req, skill->last_req, next_req, prev_req );
                    DISPOSE( requirement );
                    send_to_char( "Ok.\n\r", ch);
                    return;
                 }
              }
              send_to_char("There isn't that many requirements.\n\r", ch);
              return;
           }
        }
        if( !str_cmp( arg2, "addreq" ) )
        {
           SKILL_REQ *requirement;
           SKILLTYPE *newskill;
           int num, typenum = -1;
           int slot, attribute;
           bool aFound = FALSE;
           char location[MAX_INPUT_LENGTH];
           char modifier[MAX_INPUT_LENGTH];
           char typename[MAX_INPUT_LENGTH];

           argument = one_argument( argument, typename );
           argument = one_argument( argument, location );
           argument = one_argument( argument, modifier );

           if( !str_cmp(typename, "attribute") )
              typenum = REQ_ATTRIBUTE;
           else if( !str_cmp(typename, "skill" ) )
              typenum = REQ_SKILL;
           if( typenum == -1 )
           {
              if( is_number(typename) )
                 typenum = atoi(typename);
           }
           switch(typenum)
           {
           default: send_to_char("Invalid type argument.\n\r", ch); return; break;
           case REQ_SKILL:
             if ( (slot = skill_lookup( location ) ) < 0 )
             {
                if( is_number(location) )
                {
                   slot = atoi(location);
                   newskill = get_skilltype( slot );
                   if(newskill == NULL )
                   {
                     send_to_char("Invalid sn.\n\r", ch);
                     return;
                   }
                }
             }
             else
                newskill = get_skilltype( slot );
             if(!newskill)
             {
                if ( slot <= 0 )
                {
                   send_to_char("Invalid skill.\n\r", ch);
                   return;
                }
                newskill = get_skilltype( slot );
                if( newskill == NULL )
                {
                   send_to_char("Invalid skill.\n\r", ch);
                   return;
                }
             }
             if( !(num = atoi(modifier)) )
             {
                send_to_char("Invalid Requirement Level.\n\r", ch);
                return;
             }
             CREATE( requirement, SKILL_REQ, 1 );
             requirement->name = str_dup(newskill->name);
             requirement->prof = num;
             requirement->type = typenum;
             LINK( requirement, skill->first_req, skill->last_req, next_req, prev_req );
             send_to_char("Ok.\n\r", ch);
             return;
             break;
          case REQ_ATTRIBUTE:
             for( attribute = 0; attribute < MAX_ATTRIBUTE; attribute++)
             {
                if(!str_cmp( attribute_name[attribute], location ) )
                {
                   aFound = TRUE;
                   break;
                }
             }
             if(!aFound)
             {
                attribute = atoi( location );
                if(attribute < 0 || attribute >= MAX_ATTRIBUTE )
                {
                   send_to_char("Invalid attribute.\n\r", ch);
                   return;
                }
             }
             if( !(num = atoi(modifier)) )
             {
                send_to_char("Invalid Requirement Level.\n\r", ch);
                return;
             }
             CREATE( requirement, SKILL_REQ, 1 );
             requirement->name      = str_dup("Attribute");
             requirement->prof      = num;
             requirement->type      = typenum;
             requirement->attribute = attribute;
             LINK( requirement, skill->first_req, skill->last_req, next_req, prev_req );
             send_to_char("Ok.\n\r", ch);
             return;
             break;
          }
/* in do_slookup */
/* after this
        if ( skill->participants )
            ch_printf( ch, "Participants: %d\n\r", (int) skill->participants );
 * place this */
        if ( skill->first_req )
        {
           SKILL_REQ *requirement;
           for( requirement = skill->first_req; requirement; requirement = requirement->next_req )
           {
              if(requirement->type == REQ_SKILL)
                 ch_printf(ch, "%s required at %d%%.\n\r", requirement->name, requirement->prof );
              else if(requirement->type == REQ_ATTRIBUTE)
                 ch_printf(ch, "%s required at %d points.\n\r", attribute_name[requirement->attribute], requirement->prof );
              else
                 ch_printf(ch, "Unknown Requirement Type.\n\r");
           }
        }