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/
/********************************************************************************
 **    ( .-""-.   )+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+(   .-""-. )   **
 **     / _  _ \ ( | | | | | | |D|A|R|K| |R|E|A|L|M|S| | | | | | ) / _  _ \    **
 **     |(_\/_)|  )+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+(  |/_)(_\|    **
 **     (_ /\ _)           MULTIVERSE Distribution v1.0            (_ /\ _)    **
 **      |v==v| (       Multiverse source created by Exodus       ) |mmmm|     **
 **      '-..-'           with Aeone, Kain, Kyros, and Kast         '-..-'     **
 **----------------------------------------------------------------------------**
 ** SMAUG 1.4 written by : Thoric (Derek Snider) with Altrag, Blodkai,         **
 **                        Haus, Narn, Scryn, Swordbearer, Tricops, Gorog,     **
 **                        Rennard, Grishnakh, Fireblade and Nivek.            **
 **----------------------------------------------------------------------------**
 ** Original MERC 2.1 by : Hatchet, Furey, and Kahn.                           **
 ** Original Diku MUD by : Hans Staerfeldt, Katja Nyboe, Tom Madsen,           **
 **                        Michael Seifert & Sebastian Hammer.                 **
 **----------------------------------------------------------------------------**
 *		           Bug/Idea/Typo Tracking Module		        *
 ********************************************************************************/

#include <time.h>
#include "h/files.h"
#include "h/mud.h"
#include "h/key.h"

BUG_DATA               *firstBug;
BUG_DATA               *lastBug;
IDEA_DATA              *firstIdea;
IDEA_DATA              *lastIdea;
TYPO_DATA              *firstTypo;
TYPO_DATA              *lastTypo;

char                   *timeStamp args((void));
void checkBuidty        args((CHAR_DATA *ch));
void nstralloc          args((char **pointer));

struct bug_data
{
  BUG_DATA               *next;
  BUG_DATA               *prev;
  char                   *bugDesc;
  char                   *foundBy;
  char                   *foundWhen;
  char                   *fixedBy;
  char                   *fixedWhen;
  char                   *fixDesc;
  short                   type;
  short                   bonus;
  bool                    reward;
  int                     room;
};

struct idea_data
{
  IDEA_DATA              *next;
  IDEA_DATA              *prev;
  char                   *ideaDesc;
  char                   *madeBy;
  char                   *madeWhen;
  char                   *usedBy;
  char                   *usedWhen;
  char                   *useDesc;
  short                   type;
  short                   bonus;
  bool                    reward;
  int                     room;
};

struct typo_data
{
  TYPO_DATA              *next;
  TYPO_DATA              *prev;
  char                   *typoDesc;
  char                   *foundBy;
  char                   *foundWhen;
  char                   *fixedBy;
  char                   *fixedWhen;
  short                   type;
  short                   bonus;
  bool                    reward;
  int                     room;
};

void freeOneBug(BUG_DATA * Bug)
{
  UNLINK(Bug, firstBug, lastBug, next, prev);
  STRFREE(Bug->bugDesc);
  STRFREE(Bug->foundBy);
  STRFREE(Bug->foundWhen);
  STRFREE(Bug->fixedBy);
  STRFREE(Bug->fixedWhen);
  STRFREE(Bug->fixDesc);
  DISPOSE(Bug);
}

void freeBugs(void)
{
  BUG_DATA               *bug, *bug_next;

  for(bug = firstBug; bug; bug = bug_next)
  {
    bug_next = bug->next;
    freeOneBug(bug);
  }
}

// Quick timestamp function. Feel free to change the format if you wish --Exo
char                   *timeStamp(void)
{
  static char             buf[MSL];
  struct tm              *t = localtime(&current_time);
  bool                    ispm = FALSE;
  short                   htime;

  htime = t->tm_hour;
  if(htime >= 12)
  {
    htime -= 12;
    ispm = TRUE;
  }
  snprintf(buf, MSL, "%02d-%02d-%04d %02d:%02d:%02d%s", (t->tm_mon + 1), t->tm_mday, (t->tm_year + 1900), htime, t->tm_min, t->tm_sec, ispm ? "PM" : "AM");
  return buf;
}

void saveBugs(void)
{
  FILE                   *fp = NULL;
  BUG_DATA               *Bug = NULL;
  char                    fname[MFL];

  snprintf(fname, MFL, "%s%s", SYSTEM_DIR, PBUG_FILE);
  if(!(fp = FileOpen(fname, "w")))
  {
    bug("%s: Couldn't open %s for writing!", __FUNCTION__, fname);
    return;
  }

  for(Bug = firstBug; Bug; Bug = Bug->next)
  {
    fprintf(fp, "#BUG\n");
    fprintf(fp, "FoundBy       %s~\n", Bug->foundBy);
    fprintf(fp, "FoundWhen     %s~\n", Bug->foundWhen);
    fprintf(fp, "BugDesc       %s~\n", strip_cr(Bug->bugDesc));
    fprintf(fp, "Type          %d\n", Bug->type);
    if(Bug->fixedBy)
      fprintf(fp, "FixedBy       %s~\n", Bug->fixedBy);
    if(Bug->fixedWhen)
      fprintf(fp, "FixedWhen     %s~\n", Bug->fixedWhen);
    if(Bug->fixDesc)
      fprintf(fp, "FixDesc       %s~\n", Bug->fixDesc);
    fprintf(fp, "Reward        %d\n", Bug->reward);
    fprintf(fp, "RewardBonus   %d\n", Bug->bonus);
    fprintf(fp, "Room          %d\n", Bug->room);
    fprintf(fp, "End\n\n");
  }
  fprintf(fp, "#END\n");
  FileClose(fp);
}

void fReadBug(BUG_DATA * Bug, FILE * fp)
{
  const char             *word;
  bool                    fMatch;

  for(;;)
  {
    word = feof(fp) ? "End" : fread_word(fp);
    fMatch = FALSE;
    switch (UPPER(word[0]))
    {
      case '*':
        fMatch = TRUE;
        fread_to_eol(fp);
        break;

      case 'B':
        KEY("BugDesc", Bug->bugDesc, fread_string(fp));
        break;

      case 'E':
        if(!str_cmp(word, "End"))
        {
          nstralloc(&Bug->bugDesc);
          nstralloc(&Bug->foundBy);
          nstralloc(&Bug->foundWhen);
          nstralloc(&Bug->fixedBy);
          nstralloc(&Bug->fixedWhen);
          nstralloc(&Bug->fixDesc);
          return;
        }
        break;

      case 'F':
        KEY("FixDesc", Bug->fixDesc, fread_string(fp));
        KEY("FixedBy", Bug->fixedBy, fread_string(fp));
        KEY("FixedWhen", Bug->fixedWhen, fread_string(fp));
        KEY("FoundBy", Bug->foundBy, fread_string(fp));
        KEY("FoundWhen", Bug->foundWhen, fread_string(fp));
        break;

      case 'R':
        KEY("Reward", Bug->reward, fread_number(fp));
        KEY("RewardBonus", Bug->bonus, fread_number(fp));
        KEY("Room", Bug->room, fread_number(fp));
        break;

      case 'T':
        KEY("Type", Bug->type, fread_number(fp));
        break;
    }

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

void loadBugs(void)
{
  char                    fname[MFL];
  BUG_DATA               *Bug;
  FILE                   *fp;

  firstBug = NULL;
  lastBug = NULL;
  snprintf(fname, MFL, "%s%s", SYSTEM_DIR, PBUG_FILE);
  if((fp = FileOpen(fname, "r")) != NULL)
  {
    for(;;)
    {
      char                    letter;
      const char             *word;

      letter = fread_letter(fp);
      if(letter == '*')
      {
        fread_to_eol(fp);
        continue;
      }

      if(letter != '#')
      {
        bug("%s: # not found: (%c)", __FUNCTION__, letter);
        break;
      }

      word = fread_word(fp);
      if(!str_cmp(word, "BUG"))
      {
        CREATE(Bug, BUG_DATA, 1);
        fReadBug(Bug, fp);
        LINK(Bug, firstBug, lastBug, next, prev);
        continue;
      }
      else if(!str_cmp(word, "END"))
        break;
      else
      {
        bug("%s: bad section: %s", __FUNCTION__, word);
        continue;
      }
    }
    FileClose(fp);
  }
  return;
}

const char             *bugSeverity(short type)
{
  switch (type)
  {
    case 1:
      return "Lesser";
      break;
    case 2:
      return "Minor";
      break;
    case 3:
      return "Major";
      break;
    case 4:
      return "Severe";
      break;
    case 5:
      return "Critical";
      break;
    default:
      return "Unknown??";
      break;
  }
}

short severityArg(char *arg)
{

  if(!str_cmp(arg, "lesser"))
    return 1;
  else if(!str_cmp(arg, "nothing"))
    return 0;
  else if(!str_cmp(arg, "minor"))
    return 2;
  else if(!str_cmp(arg, "major"))
    return 3;
  else if(!str_cmp(arg, "severe"))
    return 4;
  else if(!str_cmp(arg, "critical"))
    return 5;
  else
    return -1;
}

void bugReward(CHAR_DATA *ch, BUG_DATA * error)
{
  int                     gold = 0;
  int                     glory = 0;
  char                    buf1[MSL], buf2[MSL];

  if(error->reward || IS_NPC(ch))
    return;

  if(!str_cmp(error->foundBy, ch->name))
  {
    switch (error->type)
    {
      case 0:
        gold = 0;
        glory = 0;
        break;

      case 1:
        gold = 10;
        glory = 1;
        break;

      case 2:
        gold = 25;
        glory = 2;
        break;

      case 3:
        gold = 50;
        glory = 3;
        break;

      case 4:
        gold = 100;
        glory = 5;
        break;

      case 5:
        gold = 200;
        glory = 10;
        break;

      default:
        break;
    }

    if(error->bonus == 1)
    {
      gold = gold + (gold / 2);
      glory = glory + (glory / 2);
    }
    else if(error->bonus == 2)
    {
      gold = gold * 2;
      glory = glory * 2;
    }
    else if(error->bonus == 3)
    {
      gold = (gold * 2) + (gold / 2);
      glory = (glory * 2) + (glory / 2);
    }

    snprintf(buf1, MSL, "%s", num_punct(gold));
    snprintf(buf2, MSL, "%s", num_punct(glory));
    if(gold == 0 && glory == 0)
    {
      ch_printf(ch, "&CSomeone says, 'The STAFF did not find it fit to reward you for your reported bug!'\r\n");
      ch_printf(ch, "&CSomeone says, 'The bug was probably submitted prior to your discovery.  Keep looking though!'\r\n");
    }
    else
      ch_printf(ch, "&CSomeone says, 'The STAFF have rewarded you %s gold and %s glory for finding a %s bug!'\r\n", buf1, buf2, bugSeverity(error->type));
    error->reward = TRUE;
    ch->pcdata->num_bugs++;
    ch->pcdata->bugReward1 += gold;
    ch->pcdata->bugReward2 += glory;
    GET_MONEY(ch, CURR_GOLD) += gold;
    ch->quest_curr += glory;
/* Volk - bug fix */
    send_to_pager("&CYour bug was:\r\n&B----------------------------------------------------------------------&W\r\n", ch);
    send_to_pager(error->bugDesc, ch);
    send_to_pager("\r\n", ch);
    send_to_pager("&CSTAFF note:\r\n&B----------------------------------------------------------------------&W\r\n", ch);
    send_to_pager(error->fixDesc, ch);
    send_to_pager("\r\n", ch);
    save_char_obj(ch);
    freeOneBug(error);
    saveBugs();
    return;
  }
  return;
}

void do_bug(CHAR_DATA *ch, char *argument)
{
  char                    arg1[MIL];
  char                    arg2[MIL];
  BUG_DATA               *Bug, *nBug;
  short                   cnt = 0;
  DESCRIPTOR_DATA        *d;

  if(IS_NPC(ch))
  {
    send_to_char("Huh?\r\n", ch);
    return;
  }

  switch (ch->substate)
  {
    default:
      break;

    case SUB_RESTRICTED:
      send_to_char("You cannot do this while in another command.\r\n", ch);
      return;

    case SUB_BUG_DESC:
      nBug = (BUG_DATA *) ch->dest_buf;
      if(nBug->bugDesc)
        STRFREE(nBug->bugDesc);
      nBug->bugDesc = copy_buffer(ch);
      stop_editing(ch);

      if(!VLD_STR(nBug->bugDesc))
      {
        send_to_char("&CSomeone says, 'Nothing was in the bug report so it has been discarded.'\r\n", ch);
        freeOneBug(nBug);
      }
      else
      {
        send_to_char("&CSomeone says, 'Thanks, your bug report has been recorded.'\r\n", ch);
        send_to_char("&CSomeone says, 'You will be notified when the bug is fixed.'\r\n", ch);
        log_printf("%s has reported a bug.", ch->name);
        saveBugs();
      }
      ch->substate = ch->tempnum;
      return;

    case SUB_BUG_FIXDESC:
      Bug = (BUG_DATA *) ch->dest_buf;
      if(Bug->fixDesc)
        STRFREE(Bug->fixDesc);
      Bug->fixDesc = copy_buffer(ch);
      stop_editing(ch);
      saveBugs();
      ch->substate = ch->tempnum;
      send_to_char("Ok, the bug has been fixed and the player who reported it will be notified.\r\n", ch);
      if(Bug->foundBy)
      {
        for(d = first_descriptor; d; d = d->next)
        {
          if(!d->character)
            continue;
          if(!str_cmp(Bug->foundBy, d->character->name) && !NULLSTR(Bug->fixedBy))
          {
            checkBuidty(d->character);
            break;
          }
        }
      }
      return;
  }

  argument = one_argument(argument, arg1);
  argument = one_argument(argument, arg2);
  if(NULLSTR(arg1))
  {
    if(IS_IMMORTAL(ch))
    {
      send_to_char("&WThe following are the known bugs within the &CSIX DRAGONS&W realms&D\r\n", ch);
      send_to_char("&CNum   Room    Date       Time        Name\r\n", ch);
      if(!IS_BLIND(ch))
      {
        send_to_char("&B--------------------------------------------------\r\n", ch);
      }
      for(Bug = firstBug; Bug; Bug = Bug->next)
      {
        cnt++;
        ch_printf(ch, "%s%3d>  %-6d  %-21s  %-10s\r\n", NULLSTR(Bug->fixedBy) ? "&R" : "&G", cnt, Bug->room, Bug->foundWhen, Bug->foundBy);
      }

      if(cnt <= 0)
      {
        send_to_char("No bugs have been reported.\r\n", ch);
        return;
      }
      if(IS_BLIND(ch))
      {
        send_to_char("&B--------------------------------------------------\r\n", ch);
      }
      send_to_char("\r\n&WSyntax: bug <field> <number> <severity> <bonus>\r\n", ch);
      send_to_char("\r\n&cField being   : edit, delete, show, fixed, report\r\n", ch);
      send_to_char("&cNumber being  : (what bug number)\r\n", ch);
      send_to_char("&cSeverity being: nothing, lesser, minor, major, severe, critical\r\n", ch);
      send_to_char("&cBonus being   : none, bonus, bonusx2, both\r\n", ch);
    }
    else
    {
      send_to_char("&CBugs\r\nLv Degree    Gold   Glory\r\n", ch);
      send_to_char("&B--------------------------\r\n", ch);
      send_to_char("&W0  nothing    0        0\r\n", ch);
      send_to_char("&W1  lesser    10        1\r\n", ch);
      send_to_char("&W2  minor     25        2\r\n", ch);
      send_to_char("&W3  major     50        3\r\n", ch);
      send_to_char("&W4  severe    100       5\r\n", ch);
      send_to_char("&W5  critical  200      10\r\n", ch);
      send_to_char("&B--------------------------\r\n", ch);
      send_to_char("&CThe STAFF of 6 Dragons will reward you based on the above criteria for\r\nbugs reported.  The reward is automatically generated by the code once the bug is fixed.\r\n", ch);
      send_to_char("\r\nIf you want to report a bug, type &WBUG REPORT&D\r\n", ch);
    }
    return;
  }

  if(!str_cmp(arg1, "report"))
  {
    CREATE(nBug, BUG_DATA, 1);
    LINK(nBug, firstBug, lastBug, next, prev);
    nBug->foundBy = STRALLOC(ch->name);
    nBug->foundWhen = STRALLOC(timeStamp());
    nBug->type = -1;
    nBug->reward = FALSE;
    nBug->room = ch->in_room ? ch->in_room->vnum : -1;
    nBug->fixedBy = NULL;
    nBug->fixedWhen = NULL;
    nBug->fixDesc = NULL;
    nBug->bugDesc = NULL;

    if(ch->substate == SUB_REPEATCMD)
      ch->tempnum = SUB_REPEATCMD;
    else
      ch->tempnum = SUB_NONE;
    ch->substate = SUB_BUG_DESC;
    ch->dest_buf = nBug;
    start_editing(ch, nBug->bugDesc);
    return;
  }

  if(!IS_IMMORTAL(ch))
  {
    send_to_char("Huh?\r\n", ch);
    return;
  }

  if(!str_cmp(arg1, "show"))
  {
    if(NULLSTR(arg2))
    {
      send_to_char("Show which bug?\r\nSyntax: bug show <number>\r\n", ch);
      return;
    }

    cnt = 0;
    for(Bug = firstBug; Bug; Bug = Bug->next)
    {
      cnt++;
      if(cnt == atoi(arg2))
        break;
    }

    if(!Bug)
    {
      send_to_char("No such bug.\r\n", ch);
      return;
    }
    send_to_char("&CNum   Room    Date       Time        Name\r\n", ch);
    send_to_char("&B----------------------------------------------------------------------\r\n", ch);
    ch_printf(ch, "%s%3d&C>  &W%-6d  %-21s  %-10s\r\n", NULLSTR(Bug->fixedBy) ? "&R" : "&W", cnt, Bug->room, Bug->foundWhen, Bug->foundBy);
    send_to_char("&B----------------------------------------------------------------------\r\n", ch);
    ch_printf(ch, "&CDescription\r\n&W%s\r\n", Bug->bugDesc);
    send_to_char("&B----------------------------------------------------------------------\r\n", ch);

    if(!NULLSTR(Bug->fixedBy))
    {
      ch_printf(ch, "&CFixed by: &W%s\r\n", Bug->fixedBy);
      ch_printf(ch, "&CWhen    : &W%s\r\n", Bug->fixedWhen);
      ch_printf(ch, "&CSeverity: &W%s\r\n", bugSeverity(Bug->type));
      ch_printf(ch, "&CRewarded: &W%s\r\n", Bug->reward ? "Yes" : "No");

      if(Bug->bonus > 0)
        ch_printf(ch, "&CBonus   : &W%s\r\n", Bug->bonus == 1 ? "+50%" : Bug->bonus == 1 ? "x2" : Bug->bonus > 1 ? "+50% x2" : "None");
      send_to_char("&B----------------------------------------------------------------------\r\n", ch);
      ch_printf(ch, "&CNotes\r\n&W%s\r\n", Bug->fixDesc);
      send_to_char("&B----------------------------------------------------------------------\r\n", ch);
    }
    return;
  }
  else if(!str_cmp(arg1, "edit"))
  {
    cnt = 0;
    for(Bug = firstBug; Bug; Bug = Bug->next)
    {
      cnt++;
      if(cnt == atoi(arg2))
        break;
    }

    if(!Bug)
    {
      send_to_char("No such bug.\r\n", ch);
      return;
    }

    if(ch->substate == SUB_REPEATCMD)
      ch->tempnum = SUB_REPEATCMD;
    else
      ch->tempnum = SUB_NONE;
    ch->substate = SUB_BUG_DESC;
    ch->dest_buf = Bug;
    start_editing(ch, Bug->bugDesc);
    return;
  }
  else if(!str_cmp(arg1, "fixed"))
  {
    char                    arg3[MIL];
    char                    arg4[MIL];

    argument = one_argument(argument, arg3);
    argument = one_argument(argument, arg4);
    if(NULLSTR(arg2))
    {
      send_to_char("&WWhich bug is fixed?\r\nSyntax: bug fixed <number> <severity> <bonus>\r\n", ch);
      send_to_char("Severity can be:\r\n  &wnothing lesser minor major severe critical\r\n", ch);
      send_to_char("&WBonus can be either: &wnone bonus bonusx2 &Wor &wboth\r\n", ch);
      send_to_char("\r\n&RDo not set a bug as fixed until you are positive the issue is resolved.\r\n", ch);
      return;
    }

    cnt = 0;
    for(Bug = firstBug; Bug; Bug = Bug->next)
    {
      cnt++;
      if(cnt == atoi(arg2))
        break;
    }

    if(!Bug)
    {
      send_to_char("No such bug.\r\n", ch);
      return;
    }

    if(NULLSTR(arg3) || severityArg(arg3) <= -1)
    {
      send_to_char("How severe is the bug you are fixing?\r\nValid types are:  nothing lesser minor major severe critical\r\n", ch);
      return;
    }

    if(NULLSTR(arg4))
    {
      send_to_char("Are you giving a bonus for this bug?\r\nValid types are: bonus bonusx2 both\r\nIf not, use none.\r\n", ch);
      return;
    }

    if(!str_cmp(arg4, "bonus"))
      Bug->bonus = 1;
    else if(!str_cmp(arg4, "bonusx2"))
      Bug->bonus = 2;
    else if(!str_cmp(arg4, "both"))
      Bug->bonus = 3;
    else if(!str_cmp(arg4, "none"))
      Bug->bonus = -1;
    else
    {
      send_to_char("If you're giving a bonus to the bug reporter, use bonus or bonusx2. If not, use none.\r\n", ch);
      return;
    }

    if(!NULLSTR(Bug->fixedBy))
    {
      send_to_char("That bug has already been fixed.\r\n", ch);
      return;
    }

    Bug->fixedBy = STRALLOC(ch->name);
    Bug->fixedWhen = STRALLOC(timeStamp());
    Bug->type = severityArg(arg3);
    Bug->fixDesc = NULL;
    Bug->reward = FALSE;
    send_to_char("Please enter a description of how the issue was resolved.\r\n", ch);
    if(ch->substate == SUB_REPEATCMD)
      ch->tempnum = SUB_REPEATCMD;
    else
      ch->tempnum = SUB_NONE;

    ch->substate = SUB_BUG_FIXDESC;
    ch->dest_buf = Bug;
    start_editing(ch, Bug->fixDesc);
    return;
  }
  else if(!str_cmp(arg1, "delete"))
  {
    if(NULLSTR(arg2))
    {
      send_to_char("Delete which bug?\r\nSyntax: bug delete <number>\r\n", ch);
      return;
    }
    cnt = 0;

    for(Bug = firstBug; Bug; Bug = Bug->next)
    {
      cnt++;
      if(cnt == atoi(arg2))
        break;
    }

    if(!Bug)
    {
      send_to_char("No such bug.\r\n", ch);
      return;
    }
    ch_printf(ch, "Bug #%d deleted.\r\n", cnt);
    freeOneBug(Bug);
    saveBugs();
    return;
  }
  else
  {
    send_to_char("\r\n&WSyntax: bug\r\nSyntax: bug report\r\n" "Syntax: bug show <number>\r\nSyntax: bug fixed <number> <severity> <bonus>\r\n", ch);
    return;
  }
}

void freeOneIdea(IDEA_DATA * idea)
{
  UNLINK(idea, firstIdea, lastIdea, next, prev);
  STRFREE(idea->ideaDesc);
  STRFREE(idea->madeBy);
  STRFREE(idea->madeWhen);
  STRFREE(idea->usedBy);
  STRFREE(idea->usedWhen);
  STRFREE(idea->useDesc);
  DISPOSE(idea);
}

void freeIdeas(void)
{
  IDEA_DATA              *idea, *idea_next;

  for(idea = firstIdea; idea; idea = idea_next)
  {
    idea_next = idea->next;
    freeOneIdea(idea);
  }
}

void saveIdeas(void)
{
  FILE                   *fp = NULL;
  IDEA_DATA              *idea = NULL;
  char                    fname[MFL];

  snprintf(fname, MFL, "%s%s", SYSTEM_DIR, IDEA_FILE);
  if(!(fp = FileOpen(fname, "w")))
  {
    bug("%s: Couldn't open %s for writing", __FUNCTION__, fname);
    return;
  }

  for(idea = firstIdea; idea; idea = idea->next)
  {
    fprintf(fp, "#IDEA\n");
    fprintf(fp, "MadeBy        %s~\n", idea->madeBy);
    fprintf(fp, "MadeWhen      %s~\n", idea->madeWhen);
    fprintf(fp, "IdeaDesc      %s~\n", strip_cr(idea->ideaDesc));
    fprintf(fp, "Type          %d\n", idea->type);
    if(idea->usedBy)
      fprintf(fp, "UsedBy        %s~\n", idea->usedBy);
    if(idea->usedWhen)
      fprintf(fp, "UsedWhen      %s~\n", idea->usedWhen);
    if(idea->useDesc)
      fprintf(fp, "UseDesc       %s~\n", idea->useDesc);
    fprintf(fp, "Reward        %d\n", idea->reward);
    fprintf(fp, "RewardBonus   %d\n", idea->bonus);
    fprintf(fp, "Room          %d\n", idea->room);
    fprintf(fp, "End\n\n");
  }
  fprintf(fp, "#END\n");
  FileClose(fp);
}

void fReadIdea(IDEA_DATA * idea, FILE * fp)
{
  const char             *word;
  bool                    fMatch;

  for(;;)
  {
    word = feof(fp) ? "End" : fread_word(fp);
    fMatch = FALSE;

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

      case 'E':
        if(!str_cmp(word, "End"))
        {
          nstralloc(&idea->ideaDesc);
          nstralloc(&idea->madeBy);
          nstralloc(&idea->madeWhen);
          nstralloc(&idea->usedBy);
          nstralloc(&idea->usedWhen);
          nstralloc(&idea->useDesc);
          return;
        }
        break;

      case 'I':
        KEY("IdeaDesc", idea->ideaDesc, fread_string(fp));
        break;

      case 'M':
        KEY("MadeBy", idea->madeBy, fread_string(fp));
        KEY("MadeWhen", idea->madeWhen, fread_string(fp));
        break;

      case 'R':
        KEY("Reward", idea->reward, fread_number(fp));
        KEY("RewardBonus", idea->bonus, fread_number(fp));
        KEY("Room", idea->room, fread_number(fp));
        break;

      case 'T':
        KEY("Type", idea->type, fread_number(fp));
        break;

      case 'U':
        KEY("UsedBy", idea->usedBy, fread_string(fp));
        KEY("UsedWhen", idea->usedWhen, fread_string(fp));
        KEY("UseDesc", idea->useDesc, fread_string(fp));
    }

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

void loadIdeas(void)
{
  char                    fname[MFL];
  IDEA_DATA              *idea;
  FILE                   *fp;

  firstIdea = NULL;
  lastIdea = NULL;
  snprintf(fname, MFL, "%s%s", SYSTEM_DIR, IDEA_FILE);
  if((fp = FileOpen(fname, "r")) != NULL)
  {
    for(;;)
    {
      char                    letter;
      const char             *word;

      letter = fread_letter(fp);
      if(letter == '*')
      {
        fread_to_eol(fp);
        continue;
      }

      if(letter != '#')
      {
        bug("%s: # not found: (%c)", __FUNCTION__, letter);
        break;
      }

      word = fread_word(fp);
      if(!str_cmp(word, "IDEA"))
      {
        CREATE(idea, IDEA_DATA, 1);
        fReadIdea(idea, fp);
        LINK(idea, firstIdea, lastIdea, next, prev);
        continue;
      }
      else if(!str_cmp(word, "END"))
        break;
      else
      {
        bug("%s: bad section: %s", __FUNCTION__, word);
        continue;
      }
    }
    FileClose(fp);
  }
  return;
}

const char             *ideaLevel(short type)
{
  switch (type)
  {
    case 0:
      return "nothing";
      break;
    case 1:
      return "good";
      break;
    case 2:
      return "great";
      break;
    case 3:
      return "phenomenal";
      break;
    default:
      return "unknown??";
      break;
  }
}

short ideaLevelArg(char *arg)
{
  if(!str_cmp(arg, "good"))
    return 1;
  else if(!str_cmp(arg, "nothing"))
    return 0;
  else if(!str_cmp(arg, "great"))
    return 2;
  else if(!str_cmp(arg, "phenomenal"))
    return 3;
  else
    return -1;
}

void ideaReward(CHAR_DATA *ch, IDEA_DATA * idea)
{
  int                     gold = 0;
  int                     glory = 0;
  char                    buf1[MSL], buf2[MSL];

  if(idea->reward)
    return;

  if(!str_cmp(idea->madeBy, ch->name))
  {
    switch (idea->type)
    {
      case 0:
        gold = 0;
        glory = 0;
        break;

      case 1:
        gold = 3;
        glory = 1;
        break;

      case 2:
        gold = 5;
        glory = 3;
        break;

      case 3:
        gold = 10;
        glory = 5;
        break;

      default:
        break;
    }

    if(idea->bonus == 1)
    {
      gold = gold + (gold / 2);
      glory = glory + (glory / 2);
    }
    else if(idea->bonus == 2)
    {
      gold = gold * 2;
      glory = glory * 2;
    }
    else if(idea->bonus == 3)
    {
      gold = (gold * 2) + (gold / 2);
      glory = (glory * 2) + (glory / 2);
    }

    snprintf(buf1, MSL, "%s", num_punct(gold));
    snprintf(buf2, MSL, "%s", num_punct(glory));
    if(gold == 0 && glory == 0)
    {
      ch_printf(ch, "&CSomeone says, 'The STAFF did not find it fit to reward you for your submitted idea!'\r\n");
      ch_printf(ch, "&CSomeone says, 'The idea was probably submitted prior to your discovery.  Keep looking though!'\r\n");
    }
    else
      ch_printf(ch, "&CSomeone says, 'The STAFF have rewarded you %s gold and %s glory for your %s idea!'\r\n", buf1, buf2, ideaLevel(idea->type));
    idea->reward = TRUE;
    ch->pcdata->num_ideas++;
    ch->pcdata->ideaReward1 += gold;
    ch->pcdata->ideaReward2 += glory;
    GET_MONEY(ch, CURR_GOLD) += gold;
    ch->quest_curr += glory;

    send_to_pager("&CYour idea was:\r\n&B----------------------------------------------------------------------&W\r\n", ch);
    send_to_pager(idea->ideaDesc, ch);
    send_to_pager("\r\n", ch);
    send_to_pager("&CSTAFF note:\r\n&B----------------------------------------------------------------------&W\r\n", ch);
    send_to_pager(idea->useDesc, ch);
    send_to_pager("\r\n", ch);

    save_char_obj(ch);
    freeOneIdea(idea);
    saveIdeas();
    return;
  }
  return;
}

void do_idea(CHAR_DATA *ch, char *argument)
{
  char                    arg1[MIL];
  char                    arg2[MIL];
  IDEA_DATA              *Idea = NULL, *nIdea;
  short                   cnt = 0;
  DESCRIPTOR_DATA        *d;

  if(IS_NPC(ch))
  {
    send_to_char("Huh?\r\n", ch);
    return;
  }

  switch (ch->substate)
  {
    default:
      break;

    case SUB_RESTRICTED:
      send_to_char("You cannot do this while in another command.\r\n", ch);
      return;

    case SUB_IDEA_DESC:
      nIdea = (IDEA_DATA *) ch->dest_buf;
      STRFREE(nIdea->ideaDesc);
      nIdea->ideaDesc = copy_buffer(ch);
      stop_editing(ch);

      if(!VLD_STR(nIdea->ideaDesc))
      {
        send_to_char("&CSomeone says, 'Nothing was in the idea submission so it has been discarded.'\r\n", ch);
        freeOneIdea(nIdea);
      }
      else
      {
        saveIdeas();
        send_to_char("&CSomeone says, 'Thanks, your idea submission has been recorded.'\r\n", ch);
        send_to_char("&CSomeone says, 'You will be notified if the idea is implemented.'\r\n", ch);
        log_printf("%s has submitted an idea.", ch->name);
      }

      ch->substate = ch->tempnum;
      return;

    case SUB_IDEA_USEDESC:
      Idea = (IDEA_DATA *) ch->dest_buf;
      if(Idea->useDesc)
        STRFREE(Idea->useDesc);
      Idea->useDesc = copy_buffer(ch);
      stop_editing(ch);
      saveIdeas();
      ch->substate = ch->tempnum;
      send_to_char("Ok, the idea has been used and the player who submitted it will be notified.\r\n", ch);
      if(Idea->madeBy)
      {
        for(d = first_descriptor; d; d = d->next)
        {
          if(!d->character)
            continue;
          if(!str_cmp(Idea->madeBy, d->character->name) && !NULLSTR(Idea->usedBy))
          {
            checkBuidty(d->character);
            break;
          }
        }
      }
      return;
  }

  argument = one_argument(argument, arg1);
  argument = one_argument(argument, arg2);
  if(NULLSTR(arg1))
  {
    if(IS_IMMORTAL(ch))
    {
      send_to_char("&WThe following are the known ideas within the &CSIX DRAGONS&W realms&D\r\n", ch);
      send_to_char("&CNum   Room    Date       Time        Name\r\n", ch);
      send_to_char("&B--------------------------------------------------\r\n", ch);
      for(Idea = firstIdea; Idea; Idea = Idea->next)
      {
        cnt++;
        ch_printf(ch, "%s%3d>  %-6d  %-21s  %-10s\r\n", NULLSTR(Idea->usedBy) ? "&R" : "&G", cnt, Idea->room, Idea->madeWhen, Idea->madeBy);
      }

      if(cnt <= 0)
      {
        send_to_char("No ideas have been submitted.\r\n", ch);
        return;
      }
      send_to_char("&B--------------------------------------------------\r\n", ch);

      send_to_char("\r\n&WSyntax: idea <field> <number> <level> <bonus>\r\n", ch);
      send_to_char("\r\n&cField being   : edit, delete, show, use, submit\r\n", ch);
      send_to_char("&cNumber being  : (what idea number)\r\n", ch);
      send_to_char("&cLevel being   : nothing, good, great, phenomenal\r\n", ch);
      send_to_char("&cBonus being   : none, bonus, bonusx2, both\r\n", ch);
    }
    else
    {
      send_to_char("&CIdeas\r\nLv Degree     Gold   Glory\r\n", ch);
      send_to_char("&B--------------------------\r\n", ch);
      send_to_char("&W0  Nothing    0         0\r\n", ch);
      send_to_char("&W1  Good       3         1\r\n", ch);
      send_to_char("&W2  Great      5         3\r\n", ch);
      send_to_char("&W3  Phenomenal 10        5\r\n", ch);
      send_to_char("&B--------------------------\r\n", ch);
      send_to_char
        ("&CThe STAFF of 6 Dragons will reward you based on the above criteria for\r\nideas submitted.  The reward is automatically generated by the code once the idea is utilized.\r\n", ch);
      send_to_char("\r\nIf you want to submit a idea, type &WIDEA SUBMIT&D\r\n", ch);
    }
    return;
  }

  if(!str_cmp(arg1, "submit"))
  {
    CREATE(nIdea, IDEA_DATA, 1);
    LINK(nIdea, firstIdea, lastIdea, next, prev);
    nIdea->madeBy = STRALLOC(ch->name);
    nIdea->madeWhen = STRALLOC(timeStamp());
    nIdea->type = -1;
    nIdea->reward = FALSE;
    nIdea->room = ch->in_room ? ch->in_room->vnum : -1;
    nIdea->usedBy = NULL;
    nIdea->usedWhen = NULL;
    nIdea->useDesc = NULL;
    nIdea->ideaDesc = NULL;

    if(ch->substate == SUB_REPEATCMD)
      ch->tempnum = SUB_REPEATCMD;
    else
      ch->tempnum = SUB_NONE;
    ch->substate = SUB_IDEA_DESC;
    ch->dest_buf = nIdea;
    start_editing(ch, nIdea->ideaDesc);
    return;
  }

  if(!IS_IMMORTAL(ch))
  {
    send_to_char("Huh?\r\n", ch);
    return;
  }

  if(!str_cmp(arg1, "edit"))
  {
    cnt = 0;
    for(nIdea = firstIdea; nIdea; nIdea = nIdea->next)
    {
      cnt++;
      if(cnt == atoi(arg2))
        break;
    }

    if(!nIdea)
    {
      send_to_char("No such idea.\r\n", ch);
      return;
    }

    if(ch->substate == SUB_REPEATCMD)
      ch->tempnum = SUB_REPEATCMD;
    else
      ch->tempnum = SUB_NONE;
    ch->substate = SUB_IDEA_DESC;
    ch->dest_buf = nIdea;
    start_editing(ch, nIdea->ideaDesc);
    return;
  }
  else if(!str_cmp(arg1, "show"))
  {
    if(NULLSTR(arg2))
    {
      send_to_char("Show which idea?\r\nSyntax: idea show <number>\r\n", ch);
      return;
    }

    cnt = 0;
    for(Idea = firstIdea; Idea; Idea = Idea->next)
    {
      cnt++;
      if(cnt == atoi(arg2))
        break;
    }

    if(!Idea)
    {
      send_to_char("No such idea.\r\n", ch);
      return;
    }
    send_to_char("&CNum   Room    Date       Time        Name\r\n", ch);
    send_to_char("&B----------------------------------------------------------------------\r\n", ch);
    ch_printf(ch, "%s%3d&C>  &W%-6d  %-21s  %-10s\r\n", NULLSTR(Idea->usedBy) ? "&R" : "&W", cnt, Idea->room, Idea->madeWhen, Idea->madeBy);
    send_to_char("&B----------------------------------------------------------------------\r\n", ch);
    ch_printf(ch, "&CDescription\r\n&W%s\r\n", Idea->ideaDesc);
    send_to_char("&B----------------------------------------------------------------------\r\n", ch);
    if(!NULLSTR(Idea->usedBy))
    {
      ch_printf(ch, "&CUsed by : &W%s\r\n", Idea->usedBy);
      ch_printf(ch, "&CWhen    : &W%s\r\n", Idea->usedWhen);
      ch_printf(ch, "&CLevel   : &W%s\r\n", ideaLevel(Idea->type));
      ch_printf(ch, "&CRewarded: &W%s\r\n", Idea->reward ? "Yes" : "No");
      if(Idea->bonus > 0)
        ch_printf(ch, "&CBonus   : &W%s\r\n", Idea->bonus == 1 ? "+50%" : Idea->bonus == 2 ? "x2" : Idea->bonus > 2 ? "+50% x2" : "None");
      send_to_char("&B----------------------------------------------------------------------\r\n", ch);
      ch_printf(ch, "&CNotes\r\n&W%s\r\n", Idea->useDesc);
      send_to_char("&B----------------------------------------------------------------------\r\n", ch);
    }
    return;
  }
  else if(!str_cmp(arg1, "use"))
  {
    char                    arg3[MIL];
    char                    arg4[MIL];

    argument = one_argument(argument, arg3);
    argument = one_argument(argument, arg4);
    if(NULLSTR(arg2))
    {
      send_to_char("&WWhich idea has been used?\r\nSyntax: idea used <number> <level> <bonus>\r\n", ch);
      send_to_char("Level can be:\r\n  nothing good great phenomenal\r\n", ch);
      send_to_char("Bonus can be either: &wnone bonus bonusx2 &Wor &wboth\r\n", ch);
      send_to_char("\r\n&RDo not set a idea as used until you are positive it has been fully implemented.\r\n", ch);
      return;
    }

    cnt = 0;
    for(Idea = firstIdea; Idea; Idea = Idea->next)
    {
      cnt++;
      if(cnt == atoi(arg2))
        break;
    }

    if(!Idea)
    {
      send_to_char("No such idea.\r\n", ch);
      return;
    }

    if(NULLSTR(arg3) || ideaLevelArg(arg3) <= -1)
    {
      send_to_char("How good is the idea you are using?\r\nValid types are: nothing good great phenomenal\r\n", ch);
      return;
    }

    if(NULLSTR(arg4))
    {
      send_to_char("Are you giving a bonus for this idea?\r\nValid types are: none bonus bonusx2 both\r\n", ch);
      return;
    }

    if(!str_cmp(arg4, "bonus"))
      Idea->bonus = 1;
    else if(!str_cmp(arg4, "bonusx2"))
      Idea->bonus = 2;
    else if(!str_cmp(arg4, "both"))
      Idea->bonus = 3;
    else if(!str_cmp(arg4, "none"))
      Idea->bonus = -1;
    else
    {
      send_to_char("If you're giving a bonus to the idea submitter, use bonus or bonusx2. If not, use none.\r\n", ch);
      return;
    }

    if(!NULLSTR(Idea->usedBy))
    {
      send_to_char("That idea has already been used.\r\n", ch);
      return;
    }

    Idea->usedBy = STRALLOC(ch->name);
    Idea->usedWhen = STRALLOC(timeStamp());
    Idea->type = ideaLevelArg(arg3);
    Idea->reward = FALSE;
    if(VLD_STR(argument))
    {
      Idea->useDesc = STRALLOC(capitalize(argument));
      send_to_char("Ok, the idea has been used and the player who submitted it will be notified.\r\n", ch);
    }
    else
    {
      send_to_char("Please enter a description of how the idea was implemented.\r\n", ch);
      if(ch->substate == SUB_REPEATCMD)
        ch->tempnum = SUB_REPEATCMD;
      else
        ch->tempnum = SUB_NONE;

      ch->substate = SUB_IDEA_USEDESC;
      ch->dest_buf = Idea;
      start_editing(ch, Idea->useDesc);
    }
    return;
  }
  else if(!str_cmp(arg1, "delete"))
  {
    if(NULLSTR(arg2))
    {
      send_to_char("Delete which idea?\r\nSyntax: idea delete <number>\r\n", ch);
      return;
    }

    cnt = 0;
    for(Idea = firstIdea; Idea; Idea = Idea->next)
    {
      cnt++;
      if(cnt == atoi(arg2))
        break;
    }

    if(!Idea)
    {
      send_to_char("No such idea.\r\n", ch);
      return;
    }
    ch_printf(ch, "Idea #%d deleted.\r\n", cnt);
    freeOneIdea(Idea);
    saveIdeas();
    return;
  }
  else
  {
    send_to_char("\r\n&WSyntax: idea\r\nSyntax: idea submit\r\n" "Syntax: idea show <number>\r\nSyntax: idea use <number> <level> <bonus>\r\n", ch);
    return;
  }
  return;
}

void freeOneTypo(TYPO_DATA * typo)
{
  UNLINK(typo, firstTypo, lastTypo, next, prev);
  STRFREE(typo->typoDesc);
  STRFREE(typo->foundBy);
  STRFREE(typo->foundWhen);
  STRFREE(typo->fixedBy);
  STRFREE(typo->fixedWhen);
  DISPOSE(typo);
}

void freeTypos(void)
{
  TYPO_DATA              *typo, *typo_next;

  for(typo = firstTypo; typo; typo = typo_next)
  {
    typo_next = typo->next;
    freeOneTypo(typo);
  }
}

void saveTypos(void)
{
  FILE                   *fp = NULL;
  TYPO_DATA              *typo = NULL;
  char                    fname[MFL];

  snprintf(fname, MFL, "%s%s", SYSTEM_DIR, TYPO_FILE);
  if(!(fp = FileOpen(fname, "w")))
  {
    bug("%s: Couldn't open %s for writing", __FUNCTION__, fname);
    return;
  }

  for(typo = firstTypo; typo; typo = typo->next)
  {
    fprintf(fp, "#TYPO\n");
    fprintf(fp, "FoundBy       %s~\n", typo->foundBy);
    fprintf(fp, "FoundWhen     %s~\n", typo->foundWhen);
    fprintf(fp, "TypoDesc      %s~\n", strip_cr(typo->typoDesc));
    fprintf(fp, "Type          %d\n", typo->type);
    if(typo->fixedBy)
      fprintf(fp, "FixedBy       %s~\n", typo->fixedBy);
    if(typo->fixedWhen)
      fprintf(fp, "FixedWhen     %s~\n", typo->fixedWhen);
    fprintf(fp, "Reward        %d\n", typo->reward);
    fprintf(fp, "RewardBonus   %d\n", typo->bonus);
    fprintf(fp, "Room          %d\n", typo->room);
    fprintf(fp, "End\n\n");
  }
  fprintf(fp, "#END\n");
  FileClose(fp);
  return;
}

void fReadTypo(TYPO_DATA * typo, FILE * fp)
{
  const char             *word;
  bool                    fMatch;

  for(;;)
  {
    word = feof(fp) ? "End" : fread_word(fp);
    fMatch = FALSE;
    switch (UPPER(word[0]))
    {
      case '*':
        fMatch = TRUE;
        fread_to_eol(fp);
        break;

      case 'E':
        if(!str_cmp(word, "End"))
        {
          nstralloc(&typo->typoDesc);
          nstralloc(&typo->foundBy);
          nstralloc(&typo->foundWhen);
          nstralloc(&typo->fixedBy);
          nstralloc(&typo->fixedWhen);
          return;
        }
        break;

      case 'F':
        KEY("FixedBy", typo->fixedBy, fread_string(fp));
        KEY("FixedWhen", typo->fixedWhen, fread_string(fp));
        KEY("FoundBy", typo->foundBy, fread_string(fp));
        KEY("FoundWhen", typo->foundWhen, fread_string(fp));
        break;

      case 'R':
        KEY("Reward", typo->reward, fread_number(fp));
        KEY("RewardBonus", typo->bonus, fread_number(fp));
        KEY("Room", typo->room, fread_number(fp));
        break;

      case 'T':
        KEY("Type", typo->type, fread_number(fp));
        KEY("TypoDesc", typo->typoDesc, fread_string(fp));
        break;
    }

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

void loadTypos(void)
{
  char                    fname[MFL];
  TYPO_DATA              *typo;
  FILE                   *fp;

  firstTypo = NULL;
  lastTypo = NULL;
  snprintf(fname, MFL, "%s%s", SYSTEM_DIR, TYPO_FILE);
  if((fp = FileOpen(fname, "r")) != NULL)
  {
    for(;;)
    {
      char                    letter;
      const char             *word;

      letter = fread_letter(fp);
      if(letter == '*')
      {
        fread_to_eol(fp);
        continue;
      }

      if(letter != '#')
      {
        bug("%s: # not found.", __FUNCTION__);
        break;
      }

      word = fread_word(fp);
      if(!str_cmp(word, "TYPO"))
      {
        CREATE(typo, TYPO_DATA, 1);
        fReadTypo(typo, fp);
        LINK(typo, firstTypo, lastTypo, next, prev);
        continue;
      }
      else if(!str_cmp(word, "END"))
        break;
      else
      {
        bug("%s: bad section: %s", __FUNCTION__, word);
        continue;
      }
    }
    FileClose(fp);
  }
  return;
}

const char             *typoLevel(short type)
{
  switch (type)
  {
    case 0:
      return "nothing";
      break;
    case 1:
      return "misspelled";
      break;
    case 2:
      return "misused";
      break;
    case 3:
      return "offensive";
      break;
    default:
      return "Unknown??";
      break;
  }
}

short typoLevelArg(char *arg)
{
  if(!str_cmp(arg, "misspelled"))
    return 1;
  else if(!str_cmp(arg, "nothing"))
    return 0;
  else if(!str_cmp(arg, "misused"))
    return 2;
  else if(!str_cmp(arg, "offensive"))
    return 3;
  else
    return -1;
}

void typoReward(CHAR_DATA *ch, TYPO_DATA * typo)
{
  int                     gold = 0;
  int                     glory = 0;
  char                    buf1[MSL], buf2[MSL];

  if(typo->reward)
    return;

  if(!str_cmp(typo->foundBy, ch->name))
  {
    switch (typo->type)
    {
      case 0:
        gold = 0;
        glory = 0;
        break;
      case 1:
        gold = 1;
        glory = 1;
        break;
      case 2:
        gold = 3;
        glory = 3;
        break;
      case 3:
        gold = 5;
        glory = 5;
        break;
      default:
        break;
    }

    if(typo->bonus == 1)
    {
      gold = gold + (gold / 2);
      glory = glory + (glory / 2);
    }
    else if(typo->bonus == 2)
    {
      gold = gold * 2;
      glory = glory * 2;
    }
    else if(typo->bonus == 3)
    {
      gold = (gold * 2) + (gold / 2);
      glory = (glory * 2) + (glory / 2);
    }
    snprintf(buf1, MSL, "%s", num_punct(gold));
    snprintf(buf2, MSL, "%s", num_punct(glory));
    if(gold == 0 && glory == 0)
    {
      ch_printf(ch, "&CSomeone says, 'The STAFF did not find it fit to reward you for your reported typo!'\r\n");
      ch_printf(ch, "&CSomeone says, 'The typo was probably submitted prior to your discovery.  Keep looking though!'\r\n");
    }
    else
      ch_printf(ch, "&CSomeone says, 'The STAFF have rewarded you %s gold and %s glory for finding %s typo!'\r\n", buf1, buf2, aoran(typoLevel(typo->type)));

    typo->reward = TRUE;
    ch->pcdata->num_typos++;
    ch->pcdata->typoReward1 += gold;
    ch->pcdata->typoReward2 += glory;
    GET_MONEY(ch, CURR_GOLD) += gold;
    ch->quest_curr += glory;

    send_to_pager("&CYour typo was:\r\n&B----------------------------------------------------------------------&W\r\n", ch);
    send_to_pager(typo->typoDesc, ch);
    send_to_pager("&C\r\nThanks!\r\n", ch);

    save_char_obj(ch);
    freeOneTypo(typo);
    saveTypos();
    return;
  }
  return;
}

void do_typo(CHAR_DATA *ch, char *argument)
{
  char                    arg1[MIL];
  char                    arg2[MIL];
  TYPO_DATA              *typo = NULL, *nTypo;
  short                   cnt = 0;
  DESCRIPTOR_DATA        *d;

  if(IS_NPC(ch))
  {
    send_to_char("Huh?\r\n", ch);
    return;
  }

  switch (ch->substate)
  {
    default:
      break;

    case SUB_RESTRICTED:
      send_to_char("You cannot do this while in another command.\r\n", ch);
      return;

    case SUB_TYPO_DESC:
      nTypo = (TYPO_DATA *) ch->dest_buf;
      if(nTypo->typoDesc)
        STRFREE(nTypo->typoDesc);
      nTypo->typoDesc = copy_buffer(ch);
      stop_editing(ch);
      if(!VLD_STR(nTypo->typoDesc))
      {
        send_to_char("&CSomeone says, 'Nothing was in the typo report so it has been discarded.'\r\n", ch);
        freeOneTypo(nTypo);
      }
      else
      {
        saveTypos();
        ch->substate = ch->tempnum;
        send_to_char("&CSomeone says, 'Thanks, your typo report has been recorded.'\r\n", ch);
        send_to_char("&CSomeone says, 'You will be notified when the typo is fixed.'\r\n", ch);
        log_printf("%s has reported a typo.", ch->name);
      }

      ch->substate = ch->tempnum;
      return;
  }

  argument = one_argument(argument, arg1);
  argument = one_argument(argument, arg2);
  if(NULLSTR(arg1))
  {
    if(IS_IMMORTAL(ch))
    {
      send_to_char("&WThe following are the known typos within the &CSIX DRAGONS&W realms&D\r\n", ch);
      send_to_char("&CNum   Room    Date       Time        Name\r\n", ch);
      send_to_char("&B--------------------------------------------------\r\n", ch);
      for(typo = firstTypo; typo; typo = typo->next)
      {
        cnt++;
        ch_printf(ch, "%s%3d>  %-6d  %-21s  %-10s\r\n", NULLSTR(typo->fixedBy) ? "&R" : "&G", cnt, typo->room, typo->foundWhen, typo->foundBy);
      }

      if(cnt <= 0)
      {
        send_to_char("No typos have been reported.\r\n", ch);
        return;
      }
      send_to_char("&B--------------------------------------------------\r\n", ch);
      send_to_char("\r\n&WSyntax: typo <field> <number> <level> <bonus>\r\n", ch);
      send_to_char("\r\n&cField being   : edit, delete, report, show, fixed\r\n", ch);
      send_to_char("&cNumber being  : (what typo number)\r\n", ch);
      send_to_char("&cLevel being: nothing, misspelled, misused, offensive\r\n", ch);
      send_to_char("&cBonus being   : none, bonus, bonusx2, both\r\n", ch);
    }
    else
    {
      send_to_char("&CTypos\r\nLv Degree     Gold   Glory\r\n", ch);
      send_to_char("&B--------------------------\r\n", ch);
      send_to_char("&W0  nothing    0         0\r\n", ch);
      send_to_char("&W1  misspelled 1         1\r\n", ch);
      send_to_char("&W2  misused    3         3\r\n", ch);
      send_to_char("&W3  offensive  5         5\r\n", ch);
      send_to_char("&B--------------------------\r\n", ch);
      send_to_char("&CTypos are words in the game that are either misspelled, misused, or offensive.\r\n", ch);
      send_to_char("&CThe STAFF of 6 Dragons will reward you based on the above criteria for\r\ntypos reported. The reward is automatically generated by the code once the typo is fixed.\r\n", ch);
      send_to_char("\r\nIf you want to report a typo, type &WTYPO REPORT&D\r\n", ch);
    }
    return;
  }

  if(!str_cmp(arg1, "report"))
  {
    CREATE(nTypo, TYPO_DATA, 1);
    LINK(nTypo, firstTypo, lastTypo, next, prev);
    nTypo->foundBy = STRALLOC(ch->name);
    nTypo->foundWhen = STRALLOC(timeStamp());
    nTypo->type = -1;
    nTypo->reward = FALSE;
    nTypo->room = ch->in_room ? ch->in_room->vnum : -1;
    nTypo->fixedBy = NULL;
    nTypo->fixedWhen = NULL;
    nTypo->typoDesc = NULL;
    if(ch->substate == SUB_REPEATCMD)
      ch->tempnum = SUB_REPEATCMD;
    else
      ch->tempnum = SUB_NONE;

    ch->substate = SUB_TYPO_DESC;
    ch->dest_buf = nTypo;
    start_editing(ch, nTypo->typoDesc);
    return;
  }

  if(!IS_IMMORTAL(ch))
  {
    send_to_char("Huh?\r\n", ch);
    return;
  }

  if(!str_cmp(arg1, "edit"))
  {
    cnt = 0;
    for(nTypo = firstTypo; nTypo; nTypo = nTypo->next)
    {
      cnt++;
      if(cnt == atoi(arg2))
        break;
    }

    if(!nTypo)
    {
      send_to_char("No such typo.\r\n", ch);
      return;
    }

    if(ch->substate == SUB_REPEATCMD)
      ch->tempnum = SUB_REPEATCMD;
    else
      ch->tempnum = SUB_NONE;
    ch->substate = SUB_TYPO_DESC;
    ch->dest_buf = nTypo;
    start_editing(ch, nTypo->typoDesc);
    return;
  }
  else if(!str_cmp(arg1, "show"))
  {
    if(NULLSTR(arg2))
    {
      send_to_char("Show which typo?\r\nSyntax: typo show <number>\r\n", ch);
      return;
    }

    cnt = 0;
    for(typo = firstTypo; typo; typo = typo->next)
    {
      cnt++;
      if(cnt == atoi(arg2))
        break;
    }

    if(!typo)
    {
      send_to_char("No such typo.\r\n", ch);
      return;
    }

    send_to_char("&CNum   Room    Date       Time        Name\r\n", ch);
    send_to_char("&B----------------------------------------------------------------------\r\n", ch);
    ch_printf(ch, "%s%3d&C>  &W%-6d  %-21s  %-10s\r\n", NULLSTR(typo->fixedBy) ? "&R" : "&W", cnt, typo->room, typo->foundWhen, typo->foundBy);
    send_to_char("&B----------------------------------------------------------------------\r\n", ch);
    ch_printf(ch, "&CDescription\r\n&W%s\r\n", typo->typoDesc);
    send_to_char("&B----------------------------------------------------------------------\r\n", ch);
    if(!NULLSTR(typo->fixedBy))
    {
      ch_printf(ch, "&CUsed by : &W%s\r\n", typo->fixedBy);
      ch_printf(ch, "&CWhen    : &W%s\r\n", typo->fixedWhen);
      ch_printf(ch, "&CLevel   : &W%s\r\n", typoLevel(typo->type));
      ch_printf(ch, "&CRewarded: &W%s\r\n", typo->reward ? "Yes" : "No");
      if(typo->bonus > 0)
        ch_printf(ch, "&CBonus   : &W%s\r\n", typo->bonus == 1 ? "+50%" : typo->bonus == 2 ? "x2" : typo->bonus > 2 ? "+50% x2" : "None");
    }
    return;
  }
  else if(!str_cmp(arg1, "fixed"))
  {
    char                    arg3[MIL];
    char                    arg4[MIL];

    argument = one_argument(argument, arg3);
    argument = one_argument(argument, arg4);
    if(NULLSTR(arg2))
    {
      send_to_char("&WWhich typo is fixed?\r\nSyntax: typo use <number> <level> <bonus>\r\n", ch);
      send_to_char("Level can be:\r\n  &wnothing misspelled misused offensive\r\n", ch);
      send_to_char("&WBonus can be either: &wnone bonus bonusx2 &Wor &wboth\r\n", ch);
      send_to_char("\r\n&RDo not set a typo as fixed until you are positive it has been corrected.\r\n", ch);
      return;
    }

    cnt = 0;
    for(typo = firstTypo; typo; typo = typo->next)
    {
      cnt++;
      if(cnt == atoi(arg2))
        break;
    }

    if(!typo)
    {
      send_to_char("No such typo.\r\n", ch);
      return;
    }

    if(NULLSTR(arg3) || typoLevelArg(arg3) == -1)
    {
      send_to_char("&WHow wrong was the typo you corrected?\r\nValid types are: &wnothing misspelled misused offensive\r\n", ch);
      return;
    }

    if(NULLSTR(arg4))
    {
      send_to_char("&WAre you giving a bonus for this typo?\r\nValid types are: &wnone bonus bonusx2 both\r\n", ch);
      return;
    }

    if(!str_cmp(arg4, "bonus"))
      typo->bonus = 1;
    else if(!str_cmp(arg4, "bonusx2"))
      typo->bonus = 2;
    else if(!str_cmp(arg4, "both"))
      typo->bonus = 3;
    else if(!str_cmp(arg4, "none"))
      typo->bonus = -1;
    else
    {
      send_to_char("If you're giving a bonus to the typo reporter, use bonus, bonusx2 or both. If not, use none.\r\n", ch);
      return;
    }

    if(!NULLSTR(typo->fixedBy))
    {
      send_to_char("That typo has already been used.\r\n", ch);
      return;
    }

    typo->fixedBy = STRALLOC(ch->name);
    typo->fixedWhen = STRALLOC(timeStamp());
    typo->type = typoLevelArg(arg3);
    typo->reward = FALSE;
    send_to_char("Ok, the typo has been corrected and the player who reported it will be notified.\r\n", ch);
    saveTypos();
    if(typo->foundBy)
    {
      for(d = first_descriptor; d; d = d->next)
      {
        if(!d->character)
          continue;
        if(!str_cmp(typo->foundBy, d->character->name) && !NULLSTR(typo->fixedBy))
        {
          checkBuidty(d->character);
          break;
        }
      }
    }
    return;
  }
  else if(!str_cmp(arg1, "delete"))
  {
    if(NULLSTR(arg2))
    {
      send_to_char("Delete which typo?\r\nSyntax: typo delete <number>\r\n", ch);
      return;
    }

    cnt = 0;
    for(typo = firstTypo; typo; typo = typo->next)
    {
      cnt++;
      if(cnt == atoi(arg2))
        break;
    }

    if(!typo)
    {
      send_to_char("No such typo.\r\n", ch);
      return;
    }

    ch_printf(ch, "typo #%d deleted.\r\n", cnt);
    freeOneTypo(typo);
    saveTypos();
    return;
  }
  else
  {
    send_to_char("\r\n&WSyntax: typo\r\nSyntax: typo report <typo>\r\n" "Syntax: typo show <number>\r\nSyntax: typo fixed <number> <level> <bonus>\r\n", ch);
    return;
  }
  return;
}

void checkBuidty(CHAR_DATA *ch)
{
  BUG_DATA               *Bug, *bug_next;
  IDEA_DATA              *idea, *idea_next;
  TYPO_DATA              *typo, *typo_next;
  int                     bcnt = 0, icnt = 0, tcnt = 0;

  if(!ch)
  {
    bug("%s: NULL ch!", __FUNCTION__);
    return;
  }

  for(Bug = firstBug; Bug; Bug = bug_next)
  {
    bug_next = Bug->next;

    if(NULLSTR(Bug->fixedBy))
    {
      bcnt++;
      continue;
    }

    if(Bug->reward)
      continue;

    if(!str_cmp(Bug->foundBy, ch->name))
      bugReward(ch, Bug);
  }

  for(idea = firstIdea; idea; idea = idea_next)
  {
    idea_next = idea->next;

    if(NULLSTR(idea->usedBy))
    {
      icnt++;
      continue;
    }

    if(idea->reward)
      continue;

    if(!str_cmp(idea->madeBy, ch->name))
      ideaReward(ch, idea);
  }

  for(typo = firstTypo; typo; typo = typo_next)
  {
    typo_next = typo->next;

    if(NULLSTR(typo->fixedBy))
    {
      tcnt++;
      continue;
    }

    if(typo->reward)
      continue;

    if(!str_cmp(typo->foundBy, ch->name))
      typoReward(ch, typo);
  }

  if(IS_IMMORTAL(ch))
  {
    if(bcnt > 0)
      ch_printf(ch, "&R+++ &WThere are }R%d &d&Wgame issues that need resolving. Type &GBUG &Wto view. &R+++&D\r\n", bcnt);

    if(icnt > 0)
      ch_printf(ch, "&R+++ &WThere are }R%d &d&Wideas that need reviewing. Type &GIDEA &Wto view. &R+++&D\r\n", icnt);

    if(tcnt > 0)
      ch_printf(ch, "&R+++ &WThere are }R%d &d&Wtypos that need to be fixed. Type &GTYPO &Wto view. &R+++&D\r\n", tcnt);
  }
}

void pruneBuidty(void)
{
  BUG_DATA               *Bug = NULL;
  IDEA_DATA              *Idea = NULL;
  TYPO_DATA              *Typo = NULL;

  for(Bug = firstBug; Bug; Bug = Bug->next)
  {
    if(Bug && Bug->reward == TRUE)
      freeOneBug(Bug);
  }
  saveBugs();

  for(Idea = firstIdea; Idea; Idea = Idea->next)
  {
    if(Idea && Idea->reward == TRUE)
      freeOneIdea(Idea);
  }
  saveIdeas();

  for(Typo = firstTypo; Typo; Typo = Typo->next)
  {
    if(Typo && Typo->reward == TRUE)
      freeOneTypo(Typo);
  }
  saveTypos();
  return;
}