/
/* This code was written by Jaey of Crimson Souls MUD.  It 
   was written specificly for a ROM 2.4 MUD, and I am
   releasing it into the public domain.  Any credit given,
   while appreciated, is not necessary.

   Contact info:

       E-mail:  scarrico@mail.transy.edu
          Web:  http://www.transy.edu/~steven
Crimson Souls:  ns2.ka.net 6969
*/


Add the following to interp.h:

struct pair_type
{
    char * const        first;
    char * const       second;
    bool              one_way;
};

DECLARE_DO_FUN( do_grant        );
DECLARE_DO_FUN( do_gstat        );
DECLARE_DO_FUN( do_revoke       );
--------------------------------------------------------------------------
Add the following to merc.h:

typedef struct  grant_data              GRANT_DATA;

struct grant_data
{
    GRANT_DATA *        next;
    DO_FUN *            do_fun;
    char *              name;
    int                 duration;
    int                 level;
};

Add this to the pc_data structure:

    GRANT_DATA *        granted;

Add these function prototypes:

bool is_granted_name    args( ( CHAR_DATA *ch, char *argument ) );
bool    is_granted      args( ( CHAR_DATA *ch, DO_FUN *do_fun ) );

Only add this prototype if you don't already have it:

bool    is_exact_name   args( ( char *str, char *namelist ) );
--------------------------------------------------------------------------
Add the following to act_comm.c:

In function do_immtalk change the line:

             IS_IMMORTAL(d->character) &&

to:

           (is_granted_name(d->character,"immtalk") ||
            is_granted_name(d->character,":")) &&

If function do_channels change the line for the immtalk channel:

          if (IS_IMMORTAL(ch))

to:

          if (is_granted_name(ch,"immtalk") || is_granted_name(ch,":"))

--------------------------------------------------------------------------
Add the following to act_info.c:

#include "interp.h"

bool is_command( char *arg )
{
int cmd;

for (cmd = 0; cmd_table[cmd].name[0] != '\0'; cmd++ )
  if ( UPPER(arg[0]) == UPPER(cmd_table[cmd].name[0])
  && is_exact_name( cmd_table[cmd].name, arg ) )
    return TRUE;

return FALSE;
}

In function do_help:

/* 
   On some muds, the "pHelp->level" found below might just
   be "level".  If that is the case, change each reference
   to "pHelp->level" to just "level".
*/

change:

        if (pHelp->level > get_trust( ch ) )
            continue;

to:

        if ( pHelp->level > get_trust( ch )
        && ( ( pHelp->level < LEVEL_IMMORTAL
        && !is_granted_name(ch,pHelp->keyword) )
        || (pHelp->level >= LEVEL_IMMORTAL
        && !is_command( pHelp->keyword ) ) ) )
            continue;

      if (pHelp->level >= LEVEL_IMMORTAL && is_command( pHelp->keyword )
      && !is_granted_name(ch,pHelp->keyword)) continue;
--------------------------------------------------------------------------
Add the following to act_wiz.c:

#include "interp.h"

const   struct  pair_type        pair_table       [] =
{
  {"switch", "return",FALSE},
  {"reboo", "reboot",FALSE},
  {"shutdow", "shutdown",FALSE},
  {"sla", "slay",FALSE},
  {"", "",FALSE}
};

bool is_granted( CHAR_DATA *ch, DO_FUN *do_fun)
{
GRANT_DATA *gran;

if (ch->desc == NULL) return FALSE;

if (ch->desc->original != NULL) ch = ch->desc->original;

for (gran=ch->pcdata->granted; gran != NULL; gran=gran->next)
  if (do_fun == gran->do_fun)
    return TRUE;

return FALSE;
}

bool is_granted_name( CHAR_DATA *ch, char *name)
{
GRANT_DATA *gran;

if (ch->desc == NULL) return FALSE;

if (ch->desc->original != NULL) ch = ch->desc->original;

for (gran=ch->pcdata->granted; gran != NULL; gran=gran->next)
  if (is_exact_name(gran->name,name))
    return TRUE;

return FALSE;
}

int grant_duration(CHAR_DATA *ch, DO_FUN *do_fun)
{
GRANT_DATA *gran;

if (ch->desc->original != NULL) ch=ch->desc->original;

/*  Replace the x's in the line below with the name of
    a character that is allowed to grant commands to
    anyone, even if they don't have the command
    themselves.  This is useful when you add new
    imm commands, and need to give them to yourself.
    Additional names can be added as needed and
    should be seperated by spaces.  */

if (is_exact_name(ch->name,"xxxxxx"))
  return -1;

for (gran=ch->pcdata->granted; gran != NULL; gran=gran->next)
  if (gran->do_fun == do_fun)
    return gran->duration;

return 0;
}

void grant_add(CHAR_DATA *ch, char *name, DO_FUN *do_fun, int duration,
               int level)
{
GRANT_DATA *gran;

if (ch->desc->original != NULL) ch=ch->desc->original;

gran = alloc_mem(sizeof(*gran));
gran->name = str_dup(name);
gran->do_fun = do_fun;
gran->duration = duration;
gran->level = level;

gran->next = ch->pcdata->granted;
ch->pcdata->granted = gran;

return;
}

void grant_remove(CHAR_DATA *ch, DO_FUN *do_fun, bool mshow)
{
GRANT_DATA *p,*gran;
char buf[MAX_STRING_LENGTH];
CHAR_DATA *rch;

rch = ch->desc->original?ch->desc->original:ch;

p=NULL;
gran=rch->pcdata->granted;
if (gran->do_fun == do_fun)
  rch->pcdata->granted=gran->next;
else
  for (gran=rch->pcdata->granted; gran != NULL; gran=gran->next)
  {
    if (gran->do_fun == do_fun) break;
    p=gran;
  }

if (p != NULL) p->next=gran->next;
sprintf(buf,"You have lost access to the %s command.\n\r",gran->name);
if (mshow) send_to_char(buf,ch);
free_string(gran->name);
free_mem(gran,sizeof(*gran));
return;
}

void grant_level( CHAR_DATA *ch, CHAR_DATA *victim, int level, int duration )
{
int cmd;

for (cmd = 0; cmd_table[cmd].name[0] != '\0'; cmd++ )
  if ( cmd_table[cmd].level == level
  && !is_granted(victim,cmd_table[cmd].do_fun)
  && grant_duration(ch,cmd_table[cmd].do_fun) == -1)
    grant_add(victim,cmd_table[cmd].name,cmd_table[cmd].do_fun,
              duration, cmd_table[cmd].level);
return;
}

void revoke_level( CHAR_DATA *ch, CHAR_DATA *victim, int level )
{
int cmd;

for (cmd = 0; cmd_table[cmd].name[0] != '\0'; cmd++ )
  if ( cmd_table[cmd].level == level
  && is_granted(victim,cmd_table[cmd].do_fun)
  && grant_duration(ch,cmd_table[cmd].do_fun) == -1)
    grant_remove(victim,cmd_table[cmd].do_fun,FALSE);
return;
}

void do_grant( CHAR_DATA *ch, char *argument )
{
char buf[MAX_STRING_LENGTH];
char arg1[MAX_INPUT_LENGTH];
char arg2[MAX_INPUT_LENGTH];
char arg3[MAX_INPUT_LENGTH];
CHAR_DATA *victim=NULL,*rch,*rvictim=NULL;
int  dur,cmd,x;
bool found=FALSE;
DESCRIPTOR_DATA *d;

argument = one_argument(argument,arg1);
argument = one_argument(argument,arg2);
one_argument(argument,arg3);

rch = ch->desc->original?ch->desc->original:ch;

if (arg1[0] == '\0')
  {
    send_to_char("Grant who, what?\n\r",ch);
    return;
  }

for (d = descriptor_list; d != NULL; d = d->next)
{
  rvictim = d->original?d->original:d->character;

  if (rvictim == NULL) continue;

  if (!str_cmp(rvictim->name,arg1))
  {
     victim = d->character;
     break;
  }
}

if (victim == NULL && !str_cmp("self",arg1))
{
   rvictim = rch;
   victim = ch;
}

if (victim==NULL)
  {
    send_to_char("Victim not found.\n\r",ch);
    return;
  }

if (arg2[0] == '\0')
  {
    int col=0;
    int lvl;

    sprintf(buf,"%s has not been granted the following commands:\n\r",
            rvictim->name);
    send_to_char(buf,ch);

    for ( lvl = IM; lvl <= ( L1 + 1 ) ; lvl++ )
    for (cmd = 0; cmd_table[cmd].name[0] != '\0'; cmd++ )
      if (cmd_table[cmd].level >= LEVEL_IMMORTAL
      && !is_granted(victim,cmd_table[cmd].do_fun)
      && cmd_table[cmd].level == lvl)
      {
        sprintf( buf,"[L%3d] %-12s", cmd_table[cmd].level, cmd_table[cmd].name );
        send_to_char(buf,ch);
        if ( ++col % 4 == 0 )
          send_to_char( "\n\r", ch );
      }
    if ( col % 4 != 0 )
      send_to_char( "\n\r", ch);
    return;
  }

dur = arg3[0]=='\0' ? -1 : is_number(arg3) ? atoi(arg3) : 0;

if (dur<1 && dur != -1)
  {
    send_to_char("Invalid duration!\n\r",ch);
    return;
  }

if (is_number(arg2))
  {
    if (atoi(arg2)<LEVEL_IMMORTAL || atoi(arg2)>MAX_LEVEL)
    {
      send_to_char("Invalid grant level.\n\r",ch);
      return;
    }
    grant_level(ch, victim, atoi(arg2), dur );
    sprintf(buf, "You have been granted level %d commands.\n\r", atoi(arg2));
    send_to_char("Ok.\n\r",ch);
    send_to_char(buf,victim);
    return;
  }

for (cmd = 0; cmd_table[cmd].name[0] != '\0'; cmd++ )
  if ( arg2[0] == cmd_table[cmd].name[0]
  &&   is_exact_name( arg2, cmd_table[cmd].name ) )
  {
      found = TRUE;
      break;
  }

if (found)
  {
    if (cmd_table[cmd].level < LEVEL_IMMORTAL)
    {
      send_to_char("You can only grant immortal commands.\n\r",ch);
      return;
    }

    if (grant_duration(ch,cmd_table[cmd].do_fun) != -1)
    {
      send_to_char("You can't grant that!\n\r",ch);
      return;
    }

    if (is_granted(victim,cmd_table[cmd].do_fun))
    {
      send_to_char("They already have that command!\n\r",ch);
      return;
    }

    grant_add(victim,cmd_table[cmd].name,cmd_table[cmd].do_fun,
              dur, cmd_table[cmd].level);

    sprintf(buf,"%s has been granted the %s command.\n\r",rvictim->name,
            cmd_table[cmd].name);
    send_to_char(buf,ch);
    sprintf(buf,"%s has granted you the %s command.\n\r",rch->name,
            cmd_table[cmd].name);
    send_to_char(buf,victim);

    for (x=0; pair_table[x].first[0] != '\0'; x++)
      if (!str_cmp(arg2,pair_table[x].first)
      && !is_granted_name(victim,pair_table[x].second))
      {
        sprintf(buf,"%s %s %s",rvictim->name, pair_table[x].second, arg3);
        do_grant(ch,buf);
      }
      else if (!str_cmp(arg2,pair_table[x].second)
           && pair_table[x].one_way != TRUE
           && !is_granted_name(victim,pair_table[x].first))
      {
        sprintf(buf,"%s %s %s",rvictim->name,pair_table[x].first, arg3);
        do_grant(ch,buf);
      }

    return;
  }
  send_to_char("Command not found!\n\r",ch);
  return;
}

void do_revoke( CHAR_DATA *ch, char *argument )
{
char arg1[MAX_INPUT_LENGTH];
char arg2[MAX_INPUT_LENGTH];
CHAR_DATA *victim=NULL,*rvictim=NULL;
DESCRIPTOR_DATA *d;
int cmd,x;
bool had_return, found=FALSE;

argument = one_argument(argument,arg1);
one_argument(argument,arg2);

if (arg1[0] == '\0' )
  {
    send_to_char("Revoke who, what?\n\r",ch);
    return;
  }

for (d = descriptor_list; d != NULL; d = d->next)
{
  rvictim = d->original?d->original:d->character;

  if (rvictim == NULL) continue;

  if (!str_cmp(rvictim->name,arg1))
  {
     victim = d->character;
     break;
  }
}

if (victim == NULL && !str_cmp("self",arg1))
{
  rvictim = ch->desc->original ? ch->desc->original : ch;
  victim = ch;
}

if (victim==NULL)
  {
    send_to_char("Victim not found.\n\r",ch);
    return;
  }

had_return = is_granted_name(victim,"return");

if (arg2[0] == '\0')
  {
    int col=0,lvl;
    char buf[MAX_STRING_LENGTH];

    sprintf(buf,"%s has been granted the following commands:\n\r",
            rvictim->name);
    send_to_char(buf,ch);

    for ( lvl = IM; lvl <= ( L1 + 1 ) ; lvl++ )
    for (cmd = 0; cmd_table[cmd].name[0] != '\0'; cmd++ )
      if (cmd_table[cmd].level >= LEVEL_IMMORTAL
      && is_granted(victim,cmd_table[cmd].do_fun)
      && cmd_table[cmd].level == lvl )
      {
        sprintf( buf,"[L%3d] %-12s", cmd_table[cmd].level, cmd_table[cmd].name );
        send_to_char(buf,ch);
        if ( ++col % 4 == 0 )
          send_to_char( "\n\r", ch );
      }
    if ( col % 4 != 0 )
      send_to_char( "\n\r", ch);
    return;
  }

if (is_number(arg2))
  {
    char buf[MAX_STRING_LENGTH];

    if (atoi(arg2)<LEVEL_IMMORTAL || atoi(arg2)>MAX_LEVEL)
    {
      send_to_char("Invalid revoke level.\n\r",ch);
      return;
    }
    revoke_level(ch, victim, atoi(arg2));
    sprintf(buf, "You have lost acess to level %d commands.\n\r", atoi(arg2));
    send_to_char("Ok.\n\r",ch);
    send_to_char(buf,victim);

    if (had_return && !is_granted_name(victim,"return") &&
        rvictim != victim) do_return(victim,"");

    return;
  }

for (cmd = 0; cmd_table[cmd].name[0] != '\0'; cmd++ )
  if ( arg2[0] == cmd_table[cmd].name[0]
  &&   is_exact_name( arg2, cmd_table[cmd].name ) )
  {
      found = TRUE;
      break;
  }

if (found)
  {
    char buf[MAX_STRING_LENGTH];

    if (grant_duration(ch,cmd_table[cmd].do_fun) != -1)
    {
      send_to_char("You can't revoke that!\n\r",ch);
      return;
    }

    if (!is_granted(victim,cmd_table[cmd].do_fun))
    {
      send_to_char("They don't have that command!\n\r",ch);
      return;
    }

    grant_remove(victim,cmd_table[cmd].do_fun,TRUE);

    sprintf(buf,"%s has lost access to the %s command.\n\r",
             rvictim->name,cmd_table[cmd].name);
    send_to_char(buf,ch);

    for (x=0; pair_table[x].first[0] != '\0'; x++)
      if (!str_cmp(arg2,pair_table[x].first)
      && is_granted_name(victim,pair_table[x].second))
      {
        sprintf(buf,"%s %s",rvictim->name,pair_table[x].second);
        do_revoke(ch,buf);
      }
      else if (!str_cmp(arg2,pair_table[x].second)
           && pair_table[x].one_way != TRUE
           && is_granted_name(victim,pair_table[x].first))
      {
        sprintf(buf,"%s %s",rvictim->name,pair_table[x].first);
        do_revoke(ch,buf);
      }

    if (had_return && !is_granted_name(victim,"return") &&
        rvictim != victim) do_return(victim,"");

    return;
  }
  send_to_char("Command not found!\n\r",ch);
  return;
}


void do_gstat( CHAR_DATA *ch, char *argument )
{
char arg[MAX_INPUT_LENGTH];
char buf[MAX_STRING_LENGTH];
BUFFER *buffer;
GRANT_DATA *grant;
CHAR_DATA *victim;
int col=0;

one_argument(argument,arg);

if (arg[0] == '\0')
  {
    send_to_char("Gstat who?\n\r",ch);
    return;
  }

if ( ( victim = get_char_world( ch, arg ) ) == NULL )
  {
    send_to_char( "They aren't here.\n\r", ch );
    return;
  }

if (IS_NPC(victim))
  {
    send_to_char("Not on mobs.\n\r",ch);
    return;
  }

if (get_trust(ch)<get_trust(victim))
  {
    send_to_char("You can't do that.\n\r",ch);
    return;
  }

buffer=new_buf();

sprintf(buf,"Grant status for %s:\n\r\n\r",victim->name );

add_buf(buffer,buf);

for (grant=victim->pcdata->granted; grant!=NULL; grant=grant->next)
  {
    char ds[50],ss[25],s2[25];
    int x,sl;

    sprintf(ds,"%d",grant->duration);
    ss[0]='\0';
    sl = (int)((6-strlen(ds)) / 2);

    for (x=0; x<sl ;x++)
      strcat(ss," ");

    strcpy(s2,ss);

    if ((strlen(ss)+strlen(ds)) % 2 == 1) strcat(s2," ");

    if (grant->duration==-1)
      sprintf(buf,"[ perm ] %-11s",grant->name);
    else
      sprintf(buf,"[%s%d%s] %-11s",ss, grant->duration, s2, grant->name);

    add_buf(buffer,buf);

    col++;
    col %= 4;

    if (col==0) add_buf(buffer,"\n\r");
  }

if (col != 0) add_buf(buffer,"\n\r");

page_to_char(buf_string(buffer),ch);

free_buf(buffer);
return;
}

In do_force change:

    if (!str_cmp(arg2,"delete"))

to:

    if (!str_cmp(arg2,"delete") || is_name(arg2,"gran")
    || is_name(arg2,"rev"))

/*
    Here, "gran" and "rev" should be replaced, if necessary, with
    the mininum number of letters required to execute the "grant"
    and "revoke" commands on the mud.  If desired, you can change
    them to "grant" and "revoke" and add a "gran" and "revok"
    command in the same manner as the "delet" command.  If you
    take this course of action, you should add these new commands
    to the pair_table with:

	{"grant","gran",FALSE},
	{"revoke","revok",FALSE}
*/
--------------------------------------------------------------------------
Add the following to comm.c:

In function nanny change:

        if ( IS_IMMORTAL(ch) )
        {
            do_help( ch, "imotd" );
            d->connected = CON_READ_IMOTD;
        }

to:

        if (is_granted_name(ch,"imotd"))
        {
            do_help( ch, "imotd" );
            d->connected = CON_READ_IMOTD;
        }
--------------------------------------------------------------------------
Add the following function to handler.c, if you don't already have it:

bool is_exact_name( char *str, char *namelist )
{
    char name[MAX_INPUT_LENGTH];

    if (namelist == NULL)
        return FALSE;

    for ( ; ; )
    {
        namelist = one_argument( namelist, name );
        if ( name[0] == '\0' )
            return FALSE;
        if ( !str_cmp( str, name ) )
            return TRUE;
    }
}
--------------------------------------------------------------------------
Add the following to interp.c:

Add these commands to the command table:

    { "grant",          do_grant,       POS_DEAD,       ML,  LOG_ALWAYS, 1 },
    { "gstat",          do_gstat,       POS_DEAD,       L1,  LOG_NORMAL, 1 },
    { "revoke",         do_revoke,      POS_DEAD,       ML,  LOG_ALWAYS, 1 },

In the function interpret, look for where it searches through
the command table to see if the character has the command.

Remove the text:  

	&&  cmd_table[cmd].level <= trust

Then, down inside the braces of that same if statement, change:

            found = TRUE;
            break;

to:

            if (cmd_table[cmd].level<LEVEL_IMMORTAL)
            {
              if (cmd_table[cmd].level >= trust) continue;
            }
            else
              if (ch->desc == NULL
              || !is_granted(ch,cmd_table[cmd].do_fun)) continue;

            found = TRUE;
            break;

In function do_wizhelp change the line:

        &&   cmd_table[cmd].level <= get_trust( ch )

to:

        &&   is_granted(ch,cmd_table[cmd].do_fun)
--------------------------------------------------------------------------
Add the following to recycle.c

In function free_pcdata:

GRANT_DATA *gran,*gran_next;

    for (gran = pcdata->granted; gran != NULL; gran = gran_next)
    {
        gran_next = gran->next;
        free_string(gran->name);
        free_mem(gran,sizeof(*gran));
    }
------------------------------------------------------------------------
Add the following to save.c:

#include "interp.h"

In function fwrite_char:

    GRANT_DATA *gran;

    for (gran=ch->pcdata->granted; gran != NULL; gran=gran->next)
      fprintf(fp,"Grant '%s' %d\n",gran->name, gran->duration);

/*  
    You should change the tag "Grant" used in the line above
    so that it is something that can't be easily guessed by
    someone attempting to exploit certain bugs for disruptive
    purposes.
*/

In function load_char_obj:

    ch->pcdata->granted                 = NULL;

In function fread_char:

/*
    The word "Grant" in the line below should be changed to the
    same tag that you chose to write out to the player file in
    the fwrite_char() function. *WARNING* Make sure that you
    add this code under the case that matches the first letter
    of the tag you chose.  Also, make sure that it is added
    before the "break" that causes the code to bail out of
    each case.
*/

            if ( !str_cmp( word, "Grant" ) )
            {
              GRANT_DATA *gran;
              int cmd;

              gran = alloc_mem(sizeof(*gran));
              gran->name = str_dup(fread_word(fp));
              gran->duration = fread_number(fp);
              gran->next = NULL;
              gran->do_fun = NULL;

              for (cmd = 0; cmd_table[cmd].name[0] != '\0'; cmd++ )
                if ( gran->name[0] == cmd_table[cmd].name[0]
                   &&  is_exact_name( gran->name, cmd_table[cmd].name ) )
                {
                  gran->do_fun = cmd_table[cmd].do_fun;
                  gran->level = cmd_table[cmd].level;
                  break;
                }

              gran->next = ch->pcdata->granted;
              ch->pcdata->granted = gran;

              if (gran->do_fun == NULL)
              {
                sprintf(buf,"Grant: Command %s not found in pfile for %s",
                        gran->name,ch->name);
                log_string(buf);
              }  

              fMatch = TRUE;
            }
--------------------------------------------------------------------------
Add the following to update.c:

In function char_update:

Right before these comments:

        /*
         * Careful with the damages here,
         *   MUST NOT refer to ch after damage taken,
         *   as it may be lethal damage (on NPC).
         */

Add:

        if (!IS_NPC(ch) && ch->pcdata->granted != NULL)
        {
          GRANT_DATA *gran,*gran_next, *gran_prev;
          char buf[MAX_STRING_LENGTH];

          gran_prev=ch->pcdata->granted;

          for (gran=ch->pcdata->granted; gran != NULL; gran=gran_next)
          {
            gran_next = gran->next;
            if (gran->duration > 0) gran->duration--;
            if (gran->duration==0)
            {
              if (gran==ch->pcdata->granted)
                ch->pcdata->granted=gran_next;
              else
                gran_prev->next = gran->next;
              sprintf(buf,
                "Your time runs out on the %s command.\n\r",gran->name);
              send_to_char(buf,ch);
              free_string(gran->name);
              free_mem(gran,sizeof(*gran));
              gran = NULL;
            }
            if (gran != NULL) gran_prev=gran;
          }
        }
--------------------------------------------------------------------------
If desired, you can make the following optional changes to your code.
All this code deals with is limiting a players access to those little
perks given to immortals automaticly, such as being able to read
notes sent to immortal.

In merc.h change:

#define IS_IMMORTAL(ch)              (get_trust(ch) >= LEVEL_IMMORTAL)

to:

#define IS_IMMORTAL(ch) (ch->desc ? is_granted_name(ch,"immflag") : get_trust(ch) >= LEVEL_IMMORTAL)

In act_wiz.c add:

void do_immflag( CHAR_DATA *ch, char *argument )
{
send_to_char( "Huh?\n\r", ch );
return;
}

In interp.h add:

DECLARE_DO_FUN( do_immflag	);

In interp.c add to the command table:

 { "immflag",       do_immflag,     POS_SLEEPING,   IM,  LOG_NORMAL, 0 },