6D/
6D/area/
6D/boards/
6D/city/
6D/color/
6D/corpses/
6D/councils/
6D/htowns/
6D/news/
6D/specials/
6D/src/specials/
6D/src/trades/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |   \\._.//   *
 * -----------------------------------------------------------|   (0...0)   *
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998  by Derek Snider      |    ).:.(    *
 * -----------------------------------------------------------|    {o o}    *
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |   / ' ' \   *
 * Scryn, Rennard, Swordbearer, Gorog, Grishnakh, Nivek,      |~'~.VxvxV.~'~*
 * Tricops, Fireblade, Edmond, Conran                         |             *
 * ------------------------------------------------------------------------ *
 * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
 * Chastain, Michael Quan, and Mitchell Tse.                                *
 * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.     *
 * ------------------------------------------------------------------------ *
 * 			Variable Handling Module (Thoric)                         *
 ****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "h/mud.h"
#include "h/key.h"

VARIABLE_DATA          *make_variable(char type, int vnum, char *tag)
{
  VARIABLE_DATA          *var;

  CREATE(var, VARIABLE_DATA, 1);
  var->next = NULL;
  var->type = type;
  var->flags = 0;
  var->vnum = vnum;
  var->tag = STRALLOC(tag);
  var->c_time = current_time;
  var->m_time = current_time;
  var->r_time = 0;
  var->timer = 0;
  switch (type)
  {
    case vtINT:
    case vtSTR:
      var->data = NULL;
      break;

    case vtXBIT:
      CREATE(var->data, EXT_BV, 1);
      break;
  }
  return var;
}

void delete_vdata(VARIABLE_DATA * var)
{
  char                   *strdata;

  switch (var->type)
  {
    case vtXBIT:
      if(var->data)
        DISPOSE(var->data);
      break;
    case vtSTR:
      if(var->data)
      {
        strdata = (char *)var->data;
        STRFREE(strdata);
        var->data = NULL;
      }
      break;
  }
}

void delete_variable(VARIABLE_DATA * var)
{
  delete_vdata(var);
  STRFREE(var->tag);
  DISPOSE(var);
}

/*
 * Return the specified tag from a character
 */
VARIABLE_DATA          *get_tag(CHAR_DATA *ch, char *tag, int vnum)
{
  VARIABLE_DATA          *vd;

  for(vd = ch->variables; vd; vd = vd->next)
    if(!str_cmp(tag, vd->tag))
//      if( ( !vnum || vnum == vd->vnum ) && !str_cmp( tag, vd->tag ) )
      return vd;
  return NULL;
}

/*
 * Remove the specified tag from a character
 */
bool remove_tag(CHAR_DATA *ch, char *tag, int vnum)
{
  VARIABLE_DATA          *vd_next, *vd = ch->variables;

  if(!vd)
    return FALSE;

  if((!vnum || vnum == vd->vnum) && !str_cmp(tag, vd->tag))
  {
    ch->variables = vd->next;
    delete_variable(vd);
    return TRUE;
  }

  for(; vd && vd->next; vd = vd_next)
  {
    vd_next = vd->next;
    if((!vnum || vnum == vd_next->vnum) && !str_cmp(tag, vd_next->tag))
    {
      vd->next = vd_next->next;
      delete_variable(vd_next);
      return TRUE;
    }
  }
  return FALSE;
}

/*
 * Tag a variable onto a character  Will replace if specified to do so,
 * otherwise if already exists, fail
 */
int tag_char(CHAR_DATA *ch, VARIABLE_DATA * var, int replace)
{
  VARIABLE_DATA          *vd, *pvd;
  bool                    found = FALSE;

  pvd = vd = ch->variables;
  for(; vd; vd = vd->next)
  {
    if(vd == var)
    { /* same variable -- leave it be */
      var->m_time = current_time;
      return 0;
    }
    if(vd->vnum == var->vnum && !str_cmp(vd->tag, var->tag))
    {
      if(!replace)
        return -1;
      found = TRUE;
      break;
    }
    pvd = vd;
  }

  if(found)
  {
    var->m_time = current_time;
    var->c_time = vd->c_time;
    var->r_time = vd->r_time;
    var->next = vd->next;
    if(vd == ch->variables)
      ch->variables = var;
    else
      pvd->next = var;
    delete_variable(vd);
    return 0;
  }
  var->next = ch->variables;
  ch->variables = var;

  return 0;
}

bool is_valid_tag(const char *tagname)
{
  if(!isalpha(*tagname))
    return FALSE;
  for(++tagname; *tagname; ++tagname)
    if(!isalnum(*tagname) && *tagname != '_')
      return FALSE;
  return TRUE;
}

/*
 *  "tag" is a text identifier to refer to the variable and can
 *  be suffixed with a colon and a mob vnum  ie:  questobj:1101
 *  vnum 0 is used to denote a global tag (local to the victim)
 *  otherwise tags are separated by vnum
 *
 *  mptag	<victim> <tag> [value]
 *  mprmtag	<victim> <tag>
 *  mpflag	<victim> <tag> <flag>
 *  mprmflag    <victim> <tag> <flag>
 *
 *  if istagged($n,tag) [== value]
 *  if isflagged($n,tag[,bit])
 */

/*
 * mptag <victim> <tag> [value]
 */
void do_mptag(CHAR_DATA *ch, char *argument)
{
  CHAR_DATA              *victim;
  VARIABLE_DATA          *vd;
  char                   *p;
  char                    arg1[MAX_INPUT_LENGTH];
  char                    arg2[MAX_INPUT_LENGTH];
  int                     vnum = 0, exp = 0;
  bool                    error = FALSE;

  if((!IS_NPC(ch) && get_trust(ch) < LEVEL_AJ_SGT) || IS_AFFECTED(ch, AFF_CHARM))
  {
    send_to_char("Huh?\r\n", ch);
    return;
  }

  argument = one_argument(argument, arg1);
  if(!str_cmp(arg1, "noexpire"))
  {
    exp = 0;
    argument = one_argument(argument, arg1);
  }
  else if(!str_cmp(arg1, "timer"))
  {
    argument = one_argument(argument, arg1);
    exp = atoi(arg1);
    argument = one_argument(argument, arg1);
  }
  else
    exp = ch->level * get_curr_int(ch);
  argument = one_argument(argument, arg2);

  if(arg1[0] == '\0' || arg2[0] == '\0')
  {
    send_to_char("MPtag whom with what?\r\n", ch);
    return;
  }

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

  if((p = strchr(arg2, ':')) != NULL)
  {
    *p++ = '\0';
    vnum = atoi(p);
  }
  else
  {
    vnum = ch->pIndexData ? ch->pIndexData->vnum : 0;
  }

  if(!is_valid_tag(arg2))
  {
    progbug(ch, "Mptag:  invalid characters in tag");
    return;
  }
  error = FALSE;

  const char             *p2;

  for(p2 = argument; *p2; p2++)
  {
    if(!isdigit(*p2) && !isspace(*p2))
    {
      error = TRUE;
      break;
    }
  }
  if(error)
  {
    vd = make_variable(vtSTR, vnum, arg2);
    vd->data = STRALLOC(argument);
  }
  else
  {
    vd = make_variable(vtINT, vnum, arg2);
    vd->data = (void *)(atol(argument));
  }
  vd->timer = exp;
  tag_char(victim, vd, 1);
}

/*
 * mprmtag <victim> <tag>
 */
void do_mprmtag(CHAR_DATA *ch, char *argument)
{
  CHAR_DATA              *victim;
  char                   *p;
  char                    arg1[MAX_INPUT_LENGTH];
  char                    arg2[MAX_INPUT_LENGTH];
  int                     vnum = 0;

  if((!IS_NPC(ch) && get_trust(ch) < LEVEL_AJ_SGT) || IS_AFFECTED(ch, AFF_CHARM))
  {
    send_to_char("Huh?\r\n", ch);
    return;
  }

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

  if(arg1[0] == '\0' || arg2[0] == '\0')
  {
    send_to_char("MPtag whom with what?\r\n", ch);
    return;
  }

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

  if((p = strchr(arg2, ':')) != NULL)
  {
    *p++ = '\0';
    vnum = atoi(p);
  }
  else
  {
    vnum = ch->pIndexData ? ch->pIndexData->vnum : 0;
  }

  if(!is_valid_tag(arg2))
  {
    progbug(ch, "Mptag:  invalid characters in tag");
    return;
  }

  if(!remove_tag(victim, arg2, vnum))
  {
    progbug(ch, "Mptag:  could not find tag");
  }
}

/*
 * mpflag <victim> <tag> <flag>
 */
void do_mpflag(CHAR_DATA *ch, char *argument)
{
  CHAR_DATA              *victim;
  VARIABLE_DATA          *vd;
  char                   *p;
  char                    arg1[MAX_INPUT_LENGTH];
  char                    arg2[MAX_INPUT_LENGTH];
  char                    arg3[MAX_INPUT_LENGTH];
  int                     vnum = 0, exp = 0, def = 0, flag = 0;
  bool                    error = FALSE;

  if((!IS_NPC(ch) && get_trust(ch) < LEVEL_AJ_SGT) || IS_AFFECTED(ch, AFF_CHARM))
  {
    send_to_char("Huh?\r\n", ch);
    return;
  }

  argument = one_argument(argument, arg1);
  if(!str_cmp(arg1, "noexpire"))
  {
    exp = 0;
    argument = one_argument(argument, arg1);
  }
  else if(!str_cmp(arg1, "timer"))
  {
    argument = one_argument(argument, arg1);
    exp = atoi(arg1);
    argument = one_argument(argument, arg1);
  }
  else
  {
    exp = ch->level * get_curr_int(ch);
    def = 1;
  }
  argument = one_argument(argument, arg2);
  argument = one_argument(argument, arg3);

  if(arg1[0] == '\0' || arg2[0] == '\0' || arg3[0] == '\0')
  {
    send_to_char("MPflag whom with what?\r\n", ch);
    return;
  }

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

  if((p = strchr(arg2, ':')) != NULL)
  {
    *p++ = '\0';
    vnum = atoi(p);
  }
  else
  {
    vnum = ch->pIndexData ? ch->pIndexData->vnum : 0;
  }

  if(!is_valid_tag(arg2))
  {
    progbug(ch, "Mpflag:  invalid characters in tag");
    return;
  }
  error = FALSE;
  for(p = arg3; *p; p++)
  {
    if(!isdigit(*p) && !isspace(*p))
    {
      error = TRUE;
      break;
    }
  }
  flag = atoi(arg3);
  if(error || flag < 0 || flag >= MAX_BITS)
  {
    progbug(ch, "Mpflag:  invalid flag value");
    return;
  }
  if((vd = get_tag(victim, arg2, vnum)) != NULL)
  {
    if(vd->type != vtXBIT)
    {
      progbug(ch, "Mpflag:  type mismatch");
      return;
    }
    if(!def)
      vd->timer = exp;
  }
  else
  {
    vd = make_variable(vtXBIT, vnum, arg2);
    vd->timer = exp;
  }
  xSET_BIT(*(EXT_BV *) vd->data, flag);
  tag_char(victim, vd, 1);
}

/*
 * mprmflag <victim> <tag> <flag>
 */
void do_mprmflag(CHAR_DATA *ch, char *argument)
{
  CHAR_DATA              *victim;
  VARIABLE_DATA          *vd;
  char                   *p;
  char                    arg1[MAX_INPUT_LENGTH];
  char                    arg2[MAX_INPUT_LENGTH];
  char                    arg3[MAX_INPUT_LENGTH];
  int                     vnum = 0;
  bool                    error = FALSE;

  if((!IS_NPC(ch) && get_trust(ch) < LEVEL_AJ_SGT) || IS_AFFECTED(ch, AFF_CHARM))
  {
    send_to_char("Huh?\r\n", ch);
    return;
  }

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

  if(arg1[0] == '\0' || arg2[0] == '\0' || arg3[0] == '\0')
  {
    send_to_char("MPrmflag whom with what?\r\n", ch);
    return;
  }

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

  if((p = strchr(arg2, ':')) != NULL)
  {
    *p++ = '\0';
    vnum = atoi(p);
  }
  else
  {
    vnum = ch->pIndexData ? ch->pIndexData->vnum : 0;
  }

  if(!is_valid_tag(arg2))
  {
    progbug(ch, "Mprmflag:  invalid characters in tag");
    return;
  }
  error = FALSE;
  for(p = arg3; *p; p++)
  {
    if(!isdigit(*p) && !isspace(*p))
    {
      error = TRUE;
      break;
    }
  }
  if(error)
  {
    progbug(ch, "Mprmflag:  invalid flag value");
    return;
  }
  /*
   * Only bother doing anything if the tag exists
   */
  if((vd = get_tag(victim, arg2, vnum)) != NULL)
  {
    if(vd->type != vtXBIT)
    {
      progbug(ch, "Mprmflag:  type mismatch");
      return;
    }
    if(!vd->data)
    {
      progbug(ch, "Mprmflag:  missing data???");
      return;
    }
    xREMOVE_BIT(*(EXT_BV *) vd->data, atoi(arg3));
    tag_char(victim, vd, 1);
  }
}

void fwrite_variables(CHAR_DATA *ch, FILE * fp)
{
  VARIABLE_DATA          *vd;

  for(vd = ch->variables; vd; vd = vd->next)
  {
    fprintf(fp, "#VARIABLE\n");
    fprintf(fp, "Type    %d\n", vd->type);
    fprintf(fp, "Flags   %d\n", vd->flags);
    fprintf(fp, "Vnum    %d\n", vd->vnum);
    fprintf(fp, "Ctime   %ld\n", vd->c_time);
    fprintf(fp, "Mtime   %ld\n", vd->m_time);
    fprintf(fp, "Rtime   %ld\n", vd->r_time);
    fprintf(fp, "Timer   %d\n", vd->timer);
    fprintf(fp, "Tag     %s~\n", vd->tag);
    switch (vd->type)
    {
      case vtSTR:
        fprintf(fp, "Str     %s~\n", (char *)vd->data);
        break;
      case vtXBIT:
        fprintf(fp, "Xbit    %s\n", print_bitvector((EXT_BV *) vd->data));
        break;
      case vtINT:
        fprintf(fp, "Int     %ld\n", (long)vd->data);
        break;
    }
    fprintf(fp, "End\n\n");
  }
}

void fread_variable(CHAR_DATA *ch, FILE * fp)
{
  VARIABLE_DATA          *pvd;
  const char             *word;
  bool                    fMatch;

  CREATE(pvd, VARIABLE_DATA, 1);
  for(;;)
  {
    word = feof(fp) ? "End" : fread_word(fp);
    fMatch = FALSE;

    switch (UPPER(word[0]))
    {
      case '*':
        fMatch = TRUE;
        fread_to_eol(fp);
        break;

      case 'C':
        KEY("Ctime", pvd->c_time, fread_number(fp));
        break;

      case 'E':
        if(!str_cmp(word, "End"))
        {
          switch (pvd->type)
          {
            default:
            {
              bug("%s: invalid/incomplete variable: %s", __FUNCTION__, pvd->tag);
              STRFREE(pvd->tag);
              DISPOSE(pvd);
              break;
            }
            case vtSTR:
            case vtXBIT:
              if(!pvd->data)
              {
                bug("%s: invalid/incomplete variable: %s", __FUNCTION__, pvd->tag);
                STRFREE(pvd->tag);
                DISPOSE(pvd);
                break;
              }
            case vtINT:
              tag_char(ch, pvd, 1);
              break;
          }
          return;
        }
        break;

      case 'F':
        KEY("Flags", pvd->flags, fread_number(fp));
        break;

      case 'I':
        if(!str_cmp(word, "Int"))
        {
          if(pvd->type != vtINT)
            bug("%s: Type mismatch -- type(%d) != vtInt", __FUNCTION__, pvd->type);
          else
          {
            pvd->data = (void *)((long)fread_number(fp));
            fMatch = TRUE;
          }
          break;
        }
        break;

      case 'M':
        KEY("Mtime", pvd->m_time, fread_number(fp));
        break;

      case 'R':
        KEY("Rtime", pvd->r_time, fread_number(fp));
        break;

      case 'S':
        if(!str_cmp(word, "Str"))
        {
          if(pvd->type != vtSTR)
            bug("%s: Type mismatch -- type(%d) != vtSTR", __FUNCTION__, pvd->type);
          else
          {
            pvd->data = fread_string(fp);
            fMatch = TRUE;
          }
          break;
        }
        break;

      case 'T':
        KEY("Tag", pvd->tag, fread_string(fp));
        KEY("Timer", pvd->timer, fread_number(fp));
        KEY("Type", pvd->type, fread_number(fp));
        break;

      case 'V':
        KEY("Vnum", pvd->vnum, fread_number(fp));
        break;

      case 'X':
        if(!str_cmp(word, "Xbit"))
        {
          if(pvd->type != vtXBIT)
            bug("%s: Type mismatch -- type(%d) != vtXBIT", __FUNCTION__, pvd->type);
          else
          {
            CREATE(pvd->data, EXT_BV, 1);
            *(EXT_BV *) pvd->data = fread_bitvector(fp);
            fMatch = TRUE;
          }
          break;
        }
        break;
    }

    if(!fMatch)
      bug("%s: no match: %s", __FUNCTION__, word);
  }
}