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/
 /****************************************************************************
 * AFKMud Copyright 1997-2002 Alsherok. Contributors: Samson, Dwip, Whir,   *
 * Cyberfox, Karangi, Rathian, Cam, Raine, and Tarl.                        *
 * Original SMAUG 1.4a written by Thoric (Derek Snider) with Altrag,        *
 * Blodkai, Haus, Narn, Scryn, Swordbearer, Tricops, Gorog, Rennard,        *
 * Grishnakh, Fireblade, and Nivek.                                         *
 * Original MERC 2.1 code by Hatchet, Furey, and Kahn.                      *
 * Original DikuMUD code by: Hans Staerfeldt, Katja Nyboe, Tom Madsen,      *
 * Michael Seifert, and Sebastian Hammer.                                   *
 ****************************************************************************
 *                          Dynamic Channel System                          *
 ****************************************************************************/
#include <stdarg.h>
#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#ifdef REGEX
#include <regex.h>
#endif

#ifdef FREEBSD
#include <unistd.h>
#include <regex.h>
#endif
#include "h/key.h"
#include "h/mud.h"
#include "h/files.h"
#include "h/channels.h"
#include "h/languages.h"
#include "h/clans.h"
#include "h/city.h"
#include "h/polymorph.h"

#ifdef REGEX
extern int re_exec      _RE_ARGS((const char *));
#endif

MUD_CHANNEL            *first_channel;
MUD_CHANNEL            *last_channel;

const char             *const chan_types[] = {
  "Global", "Zone", "Alliance", "Council", "PK", "Log", "Room", "Secret", "Throng",
  "Halcyon", "Paleon", "Dakar", "Forbidden"
};

int get_chantypes(char *name)
{
  unsigned int            x;

  for(x = 0; x < (sizeof(chan_types) / sizeof(chan_types[0])); x++)
    if(!str_cmp(chan_types[x], name))
      return x;
  return -1;
}

/* Provided by Remcon to stop crashes with channel history */
char                   *add_percent(char *str)
{
  static char             newstr[MSL];
  int                     i, j;

  for(i = j = 0; str[i] != '\0'; i++)
  {
    if(str[i] == '%')
      newstr[j++] = '%';
    newstr[j++] = str[i];
  }
  newstr[j] = '\0';
  return newstr;
}

void read_channel(MUD_CHANNEL * channel, 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 'C':
        KEY("ChanName", channel->name, fread_string(fp));
        KEY("ChanLevel", channel->level, fread_number(fp));
        KEY("ChanType", channel->type, fread_number(fp));
        KEY("ChanHistory", channel->keephistory, fread_number(fp));
        break;
      case 'D':
        KEY("DoScramble", channel->doscramble, fread_number(fp));
        break;
      case 'E':
        if(!str_cmp(word, "End"))
          return;
        break;
    }
    if(!fMatch)
    {
      bug("read_channel: no match: %s", word);
      fread_to_eol(fp);
    }
  }
}

void load_mudchannels(void)
{
  FILE                   *fp;
  MUD_CHANNEL            *channel;

  first_channel = NULL;
  last_channel = NULL;

  log_string("Loading mud channels...");

  if((fp = FileOpen(CHANNEL_FILE, "r")) == NULL)
  {
    log_string("No channel file found.");
    return;
  }

  for(;;)
  {
    char                    letter;
    char                   *word;

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

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

    word = fread_word(fp);
    if(!str_cmp(word, "CHANNEL"))
    {
      CREATE(channel, MUD_CHANNEL, 1);
      read_channel(channel, fp);

      LINK(channel, first_channel, last_channel, next, prev);
      continue;
    }
    else if(!str_cmp(word, "END"))
      break;
    else
    {
      bug("load_channels: bad section: %s.", word);
      continue;
    }
  }
  FileClose(fp);
  return;
}

void save_mudchannels(void)
{
  FILE                   *fp;
  MUD_CHANNEL            *channel;

  if((fp = FileOpen(CHANNEL_FILE, "w")) == NULL)
  {
    log_string("Couldn't write to channel file.");
    return;
  }

  for(channel = first_channel; channel; channel = channel->next)
  {
    if(channel->name)
    {
      fprintf(fp, "#CHANNEL\n");
      fprintf(fp, "ChanName    %s~\n", channel->name);
      fprintf(fp, "ChanLevel   %d\n", channel->level);
      fprintf(fp, "ChanType    %d\n", channel->type);
      fprintf(fp, "ChanHistory %d\n", channel->keephistory);
      fprintf(fp, "DoScramble  %d\n", channel->doscramble);
      fprintf(fp, "End\n\n");
    }
  }
  fprintf(fp, "#END\n");
  FileClose(fp);
}

MUD_CHANNEL            *find_channel(const char *name)
{
  MUD_CHANNEL            *channel = NULL;

  for(channel = first_channel; channel; channel = channel->next)
  {

    if(!str_cmp(channel->name, name))
      return channel;
  }
  return NULL;
}

void do_makechannel(CHAR_DATA *ch, char *argument)
{
  MUD_CHANNEL            *channel;

  if(!argument || argument[0] == '\0')
  {
    send_to_char("&GSyntax: makechannel <name>\r\n", ch);
    return;
  }
  if((channel = find_channel(argument)))
  {
    send_to_char("&RA channel with that name already exists.\r\n", ch);
    return;
  }
  CREATE(channel, MUD_CHANNEL, 1);
  channel->name = STRALLOC(argument);
  channel->level = LEVEL_IMMORTAL;
  channel->type = CHAN_GLOBAL;
  channel->keephistory = FALSE;
  channel->doscramble = FALSE;
  LINK(channel, first_channel, last_channel, next, prev);
  ch_printf(ch, "&YNew channel &G%s &Ycreated.\r\n", argument);
  save_mudchannels();
  return;
}

void do_setchannel(CHAR_DATA *ch, char *argument)
{
  MUD_CHANNEL            *channel;
  char                    arg[MIL], arg2[MIL], arg3[MIL];

  if(!argument || argument[0] == '\0')
  {
    send_to_char("&GSyntax: setchannel <channel> <field> <value>\r\n\r\n", ch);
    send_to_char("&YField may be one of the following:\r\n", ch);
    send_to_char("name level type history invite\r\n", ch);
    return;
  }
  argument = one_argument(argument, arg);
  if(!(channel = find_channel(arg)))
  {
    send_to_char("&RNo channel by that name exists.\r\n", ch);
    return;
  }
  argument = one_argument(argument, arg2);
  if(!arg || arg2[0] == '\0')
  {
    do_setchannel(ch, (char *)"");
    return;
  }
  if(!str_cmp(arg2, "name"))
  {
    ch_printf(ch, "&YChannel &G%s &Yrenamed to &G%s\r\n", channel->name, argument);
    STRFREE(channel->name);
    channel->name = STRALLOC(argument);
    save_mudchannels();
    return;
  }
  if(!str_cmp(arg2, "level"))
  {
    int                     level;

    if(!is_number(argument))
    {
      send_to_char("&RLevel must be numerical.\r\n", ch);
      return;
    }
    level = atoi(argument);
    if(level < 1 || level > MAX_LEVEL)
    {
      ch_printf(ch, "&RInvalid level. Acceptable range is 1 to %d.\r\n", MAX_LEVEL);
      return;
    }
    channel->level = level;
    ch_printf(ch, "&YChannel &G%s &Ylevel changed to &G%d\r\n", channel->name, level);
    save_mudchannels();
    return;
  }

  // working in the 6d channel for selected private users. -Taon
  argument = one_argument(argument, arg3);

  if(!str_cmp(arg2, "invite"))
  {
    CHAR_DATA              *victim;

    if(!is_number(arg3) && (victim = get_char_world(ch, arg3)))
    {
      if(str_cmp(ch->name, "Vladaar"))
      {
        send_to_char("Only the game admin can invite others to this channel.\r\n", ch);
        return;
      }
    }

    if(arg3[0] == '\0')
    {
      send_to_char("You must select a target.\r\n", ch);
      return;
    }
    if((victim = get_char_world(ch, arg3)) == NULL)
    {
      send_to_char("That player doesn't exist.\r\n", ch);
      return;
    }

    if(victim->chan_invite != 1)
      victim->chan_invite = 1;
    else  // toggle off. -Taon
    {
      victim->chan_invite = 0;
      ch_printf(victim, "&wYou've been removed from &Y%s&w channel.&d\r\n", channel->name);
      ch_printf(ch, "&wYou removed %s from the &Y%s&w channel.&d\r\n", victim->name, channel->name);
      return;
    }
    ch_printf(victim, "&wYou've been invited to join the &Y%s&w channel.  Usually, only the Game Admin, and Heads of Councils may use this channel.&d\r\n", channel->name);
    ch_printf(ch, "&wYou invited %s to join the &Y%s&w channel.&d\r\n", victim->name, channel->name);
    interpret(victim, (char *)"listen 6d");
    return;
  }
  if(!str_cmp(arg2, "type"))
  {
    int                     type = get_chantypes(arg3);

    if(type == -1)
    {
      send_to_char("&RInvalid channel type.\r\n", ch);
      return;
    }
    channel->type = type;
    send_to_char("New type set.\r\n", ch);
    save_mudchannels();
    return;
  }
  if(!str_cmp(arg2, "history"))
  {
    channel->keephistory = !channel->keephistory;

    if(channel->keephistory)
      ch_printf(ch, "&YChannel &G%s &Ywill now keep a history.\r\n", channel->name);
    else
      ch_printf(ch, "&YChannel &G%s &Ywill no longer keep a history.\r\n", channel->name);
    save_mudchannels();
    return;
  }
  if(!str_cmp(arg2, "scramble"))
  {
    channel->doscramble = !channel->doscramble;

    if(channel->doscramble)
      ch_printf(ch, "&YChannel &G%s &Ywill now scramble the text by languages.\r\n", channel->name);
    else
      ch_printf(ch, "&YChannel &G%s &Ywill no longer scramble the text.\r\n", channel->name);
    save_mudchannels();
    return;
  }
  do_setchannel(ch, (char *)"");
}

void do_destroychannel(CHAR_DATA *ch, char *argument)
{
  MUD_CHANNEL            *channel;

  if(!argument || argument[0] == '\0')
  {
    send_to_char("&GSyntax: destroychannel <name>\r\n", ch);
    return;
  }
  if(!(channel = find_channel(argument)))
  {
    send_to_char("&RNo channel with that name exists.\r\n", ch);
    return;
  }
  STRFREE(channel->name);
  UNLINK(channel, first_channel, last_channel, next, prev);
  DISPOSE(channel);
  ch_printf(ch, "&YChannel &G%s &Ydestroyed.\r\n", argument);
  save_mudchannels();
  return;
}

void do_showchannels(CHAR_DATA *ch, char *argument)
{
  MUD_CHANNEL            *channel;

  send_to_char("&WName               &YLevel &BType       &GHistory?   &GScramble?\r\n", ch);
  send_to_char("&W--------------------------------------------------------\r\n", ch);
  for(channel = first_channel; channel; channel = channel->next)
    ch_printf(ch, "&W%-18s &Y%-4d  &B%-10s &G%8s   &G%9s\r\n", capitalize(channel->name),
              channel->level, chan_types[channel->type], channel->keephistory ? "Yes" : "No", channel->doscramble ? "Yes" : "No");
}

/* Stuff borrowed from I3/MUD-Net code to handle channel listening */

/*  changetarg: extract a single argument (with given max length) from
 *  argument to arg; if arg==NULL, just skip an arg, don't copy it out
 */
const char             *getarg(const char *argument, char *arg, int length)
{
  int                     len = 0;

  while(*argument && isspace(*argument))
    argument++;
  if(arg)
    while(*argument && !isspace(*argument) && len < length - 1)
      *arg++ = *argument++, len++;
  else
    while(*argument && !isspace(*argument))
      argument++;
  while(*argument && !isspace(*argument))
    argument++;
  while(*argument && isspace(*argument))
    argument++;
  if(arg)
    *arg = 0;
  return argument;
}

/* Check for a name in a list */
int hasname(const char *list, const char *name)
{
  const char             *p;
  char                    arg[MIL];

  if(!list)
    return (0);
  p = getarg(list, arg, MIL);
  while(arg[0])
  {
    if(!strcasecmp(name, arg))
      return 1;
    p = getarg(p, arg, MIL);
  }
  return 0;
}

/* Add a name to a list */
void addname(char **list, const char *name)
{
  char                    buf[MSL];

  if(hasname(*list, name))
    return;
  if(*list && *list[0] != '\0')
    snprintf(buf, MSL, "%s %s", *list, name);
  else
    mudstrlcpy(buf, name, MSL);
  if(*list)
    STRFREE(*list);
  *list = STRALLOC(buf);
}

/* Remove a name from a list */
void removename(char **list, const char *name)
{
  char                    buf[MSL];
  char                    arg[MIL];
  const char             *p;

  buf[0] = 0;
  p = getarg(*list, arg, MIL);
  while(arg[0])
  {
    if(strcasecmp(arg, name))
    {
      if(buf[0])
        mudstrlcat(buf, " ", MSL);
      mudstrlcat(buf, arg, MSL);
    }
    p = getarg(p, arg, MIL);
  }
  STRFREE(*list);
  *list = STRALLOC(buf);
}

void do_listen(CHAR_DATA *ch, char *argument)
{
  MUD_CHANNEL            *channel;

  if(IS_NPC(ch))
  {
    send_to_char("Mobs can't use this command.\r\n", ch);
    return;
  }
  if(!argument || argument[0] == '\0')
  {
    send_to_char("&GSyntax: listen \r\n", ch);
    send_to_char("&GSyntax: listen all\r\n", ch);
    send_to_char("&GSyntax: listen none\r\n", ch);
    send_to_char("&GFor a list of channels, type &Wchannels\r\n", ch);
    send_to_char("&YYou are listening to the following local mud channels:\r\n\r\n", ch);
    ch_printf(ch, "&W%s\r\n", ch->pcdata->chan_listen);
    return;
  }
  if(!str_cmp(argument, "all"))
  {
    for(channel = first_channel; channel; channel = channel->next)
    {
      if(((ch->level >= channel->level) || (ch->trust >= channel->level)) && !hasname(ch->pcdata->chan_listen, channel->name))
        addname(&ch->pcdata->chan_listen, channel->name);
    }
    send_to_char("&YYou are now listening to all available channels.\r\n", ch);
    return;
  }

  if(!str_cmp(argument, "none"))
  {
    for(channel = first_channel; channel; channel = channel->next)
    {
      if(((ch->level >= channel->level) || (ch->trust >= channel->level)) && hasname(ch->pcdata->chan_listen, channel->name))
        removename(&ch->pcdata->chan_listen, channel->name);
    }
    channel = find_channel("say");
    addname(&ch->pcdata->chan_listen, channel->name);
    /*
     * Volk - still should listen to the SAY channel 
     */
    send_to_char("&YYou no longer listen to any available channel.\r\n", ch);
    return;
  }
  if(hasname(ch->pcdata->chan_listen, argument))
  {
    if(!str_cmp(argument, "say") && !IS_IMMORTAL(ch))
    {
      send_to_char("Mortals can not turn off the SAY channel.\r\n", ch);
      return;
    }
    removename(&ch->pcdata->chan_listen, argument);
    ch_printf(ch, "&YYou no longer listen to &W%s\r\n", argument);
  }
  else
  {
    if(!(channel = find_channel(argument)))
    {
      send_to_char("No such channel.\r\n", ch);
      return;
    }
    if((channel->level > ch->level) && (channel->level > ch->trust))
    {
      send_to_char("That channel is above your level.\r\n", ch);
      return;
    }
    addname(&ch->pcdata->chan_listen, argument);
    ch_printf(ch, "&YYou now listen to &W%s\r\n", channel->name);
  }
  return;
}

/* Revised channel display by Zarius */
void do_channels(CHAR_DATA *ch, char *argument)
{
  MUD_CHANNEL            *channel;

  if(IS_NPC(ch))
  {
    send_to_char("Mobs can't use this command.\r\n", ch);
    return;
  }
  send_to_char("&YThe following channels are available:\r\n", ch);
  send_to_char("To toggle a channel, use the &Wlisten &Ycommand.\r\n\r\n", ch);
  send_to_char("&WChannel        On/Off&D\r\n", ch);
  send_to_char("&B-----------------------&D\r\n", ch);
  for(channel = first_channel; channel; channel = channel->next)
  {
    if((ch->level >= channel->level) || (ch->trust >= channel->level))
    {
      ch_printf(ch, "&w%-17s%s&D\r\n", capitalize(channel->name), (hasname(ch->pcdata->chan_listen, channel->name)) ? "&GOn" : "&ROff");
    }
  }
  send_to_char("\r\n", ch);
}

void invert(char *arg1, char *arg2)
{
  int                     i = 0;
  int                     len = strlen(arg1) - 1;

  while(i <= len)
  {
    *(arg2 + i) = *(arg1 + (len - i));
    i++;
  }
  *(arg2 + i) = '\0';
}

/* Duplicate of to_channel from act_comm.c modified for dynamic channels */
void send_tochannel(CHAR_DATA *ch, MUD_CHANNEL * channel, char *argument)
{
  char                    buf[MSL], buf2[MSL], col[MIL], word[MIL];
  char                    logbuf[MSL];
  char                   *arg, *socbuf_char = NULL, *socbuf_vict = NULL, *socbuf_other = NULL;
  CHAR_DATA              *victim = NULL;
  SOCIALTYPE             *social = NULL;
  CHAR_DATA              *vch = NULL;
  int                     position, x;
  short                   color;
  struct tm              *local;
  time_t                  t;
  bool                    emote = FALSE;

  if(!ch)
    return;
#ifndef SCRAMBLE
  int                     speaking = -1, lang;

  for(lang = 0; lang_array[lang] != LANG_UNKNOWN; lang++)
    if(ch->speaking & lang_array[lang])
    {
      speaking = lang;
      break;
    }
#endif

  if(ch->chan_invite != 1 && channel->type == CHAN_SECRET)
  {
    send_to_char("You're not invited to talk on this channel.\r\n", ch);
    return;
  }

  if(channel->type == CHAN_ROOM && ch->position == POS_SLEEPING && !IS_IMMORTAL(ch))
  {
    send_to_char("Your sleeping, and cannot do that right now.\r\n", ch);
    return;
  }

  if(!str_cmp(channel->name, "shout") && ch->position == POS_SLEEPING && !IS_IMMORTAL(ch))
  {
    send_to_char("Your sleeping, and cannot do that right now.\r\n", ch);
    return;
  }


  if(!IS_CLANNED(ch) && !IS_IMMORTAL(ch))
  {
    if(!str_cmp(channel->name, "alliance") || !str_cmp(channel->name, "throng") || !str_cmp(channel->name, "halcyon"))
    {
      send_to_char("Huh?\r\n", ch);
      return;
    }
  }

  if(!IS_CITY(ch) && !IS_IMMORTAL(ch))
  {
    if(!str_cmp(channel->name, "halcyon") || !str_cmp(channel->name, "paleon") || !str_cmp(channel->name, "forbidden"))
    {
      send_to_char("Huh?\r\n", ch);
      return;
    }
  }

  if(IS_CLANNED(ch) && !IS_IMMORTAL(ch))
  {
    if(str_cmp(ch->pcdata->clan_name, "throng") && !str_cmp(channel->name, "throng"))
    {
      send_to_char("Huh?\r\n", ch);
      return;
    }
    if(str_cmp(ch->pcdata->clan_name, "halcyon") && !str_cmp(channel->name, "halcyon"))
    {
      send_to_char("Huh?\r\n", ch);
      return;
    }
    if(str_cmp(ch->pcdata->clan_name, "alliance") && !str_cmp(channel->name, "alliance"))
    {
      send_to_char("Huh?\r\n", ch);
      return;
    }
  }

  if(IS_CITY(ch) && !IS_IMMORTAL(ch))
  {
    if(str_cmp(ch->pcdata->city_name, "paleon city") && !str_cmp(channel->name, "paleon"))
    {
      send_to_char("Huh?\r\n", ch);
      return;
    }
    if(str_cmp(ch->pcdata->city_name, "dakar city") && !str_cmp(channel->name, "dakar"))
    {
      send_to_char("Huh?\r\n", ch);
      return;
    }
    if(str_cmp(ch->pcdata->city_name, "forbidden city") && !str_cmp(channel->name, "forbidden"))
    {
      send_to_char("Huh?\r\n", ch);
      return;
    }
  }

  if(ch->in_room && IS_SET(ch->in_room->room_flags, ROOM_SILENCE) && !IS_IMMORTAL(ch))
  {
    if(!IS_NPC(ch))
      send_to_char("The room absorbs your words!\r\n", ch);
    return;
  }

  if(ch->position == POS_MEDITATING && strcmp(channel->name, "ooc"))
  {
    send_to_char("You are concentrating too much for that!\r\n", ch);
    return;
  }

  if(IS_NPC(ch) && IS_AFFECTED(ch, AFF_CHARM))
  {
    if(ch->master)
      send_to_char("I don't think so...\r\n", ch->master);
    return;
  }

  if(!str_cmp(argument, "8ball") && !strcmp(channel->name, "ooc"))
  {
    CHAR_DATA              *mob;
    short                   chance;

    chance = number_range(1, 6);

    if((mob = get_char_world(ch, (char *)"magic 8 ball")) == NULL)
      return;
    interpret(ch, (char *)"ooc Oh magic 8 ball we seek an answer.");
    if(chance == 1)
      snprintf(buf, MSL, "ooc %s It is doubtful.", ch->name);
    else if(chance == 2)
      snprintf(buf, MSL, "ooc %s Concentrate and ask again.", ch->name);
    else if(chance == 3)
      snprintf(buf, MSL, "ooc %s Outlook not good.", ch->name);
    else if(chance == 4)
      snprintf(buf, MSL, "ooc %s Reply hazy, try again.", ch->name);
    else if(chance == 5)
      snprintf(buf, MSL, "ooc %s Outlook good.", ch->name);
    else
      snprintf(buf, MSL, "ooc %s Signs point to yes.", ch->name);
    interpret(mob, buf);
    return;
  }

  if(!VLD_STR(argument))
  {
    const char             *name;

  if (IS_PUPPET(ch)) {
  return;    //  stop crashing from channel history search
  }


    if(ch->desc->original)
    {
      send_to_char("You cannot be switched to check the history.\r\n", ch);
      return;
    }

    if(!channel->keephistory)
    {
      ch_printf(ch, "%s what?\r\n", capitalize(channel->name));
      return;
    }
    ch_printf(ch, "&cThe last 20 %s messages:\r\n", channel->name);
    if(!str_cmp(channel->name, "say"))
    {
      for(x = 0; x < 20; x++)
      {
        if(ch->pcdata->say_history[x] == NULL)
          break;
        ch_printf(ch, "%s &D\r\n", ch->pcdata->say_history[x]);
      }
      return;
    }
    else
    {
      for(x = 0; x < 20; x++)
      {
        if(channel->history[x][0] != NULL)
        {
          switch (channel->hlevel[x])
          {
            case 0:
              name = channel->history[x][0];
              break;
            case 1:
              if(IS_AFFECTED(ch, AFF_DETECT_INVIS) || IS_IMMORTAL(ch))
                name = channel->history[x][0];
              else
                name = "Someone";
              break;
            case 2:
              if(ch->level >= channel->hinvis[x] || ch->trust >= channel->hinvis[x])
                name = channel->history[x][0];
              else
                name = "Someone";
              break;
            default:
              name = "Someone";
          }
          ch_printf(ch, channel->history[x][1], name);
        }
        else
          break;
      }
      return;
    }
  }
  if(xIS_SET(ch->act, PLR_SILENCE))
  {
    ch_printf(ch, "You can't %s.\r\n", channel->name);
    return;
  }

  if(IS_AFFECTED(ch, AFF_BURROW) && strcmp(channel->name, "ooc"))
  {
    send_to_char("You cannot speak while burrowed, but you hear things telepathically.\r\n", ch);
    return;
  }

  /*
   * OK, this is hackish - until I can figure out a better method 
   */
  color = -1;
  if(xIS_SET(ch->act, PLR_COMMUNICATION) && (!str_cmp(channel->name, "ooc") || !str_cmp(channel->name, "chat") || !str_cmp(channel->name, "icc")))
    ch_printf(ch, "!!SOUND(sound/ooc.wav)\r\n");

  if(xIS_SET(ch->act, PLR_COMMUNICATION) && IS_IMMORTAL(ch) && !str_cmp(channel->name, "sooc"))
    ch_printf(ch, "!!SOUND(sound/sooc.wav)\r\n");

  if(!str_cmp(channel->name, "chat"))
    color = AT_LBLUE;
  if(!str_cmp(channel->name, "ooc"))
    color = AT_LBLUE;
  if(!str_cmp(channel->name, "alliance"))
    color = AT_LBLUE;
  if(!str_cmp(channel->name, "throng"))
    color = AT_RED;
  if(!str_cmp(channel->name, "halcyon"))
    color = AT_LBLUE;
  if(!str_cmp(channel->name, "dakar"))
    color = AT_LBLUE;
  if(!str_cmp(channel->name, "paleon"))
    color = AT_LBLUE;
  if(!str_cmp(channel->name, "forbidden"))
    color = AT_LBLUE;
  if(!str_cmp(channel->name, "osay"))
    color = AT_CYAN;
  if(!str_cmp(channel->name, "say"))
    color = AT_LBLUE;
  if(!str_cmp(channel->name, "icc"))
    color = AT_LBLUE;
  if(!str_cmp(channel->name, "avtalk"))
    color = AT_YELLOW;
  if(!str_cmp(channel->name, "sooc"))
    color = AT_PINK;
  if(!str_cmp(channel->name, "6d"))
    color = AT_YELLOW;
  if(!str_cmp(channel->name, "yell"))
    color = AT_ORANGE;
  if(!str_cmp(channel->name, "shout"))
    color = AT_DGREEN;
  if(!str_cmp(channel->name, "muse"))
    color = AT_MUSE;
  if(!str_cmp(channel->name, "think"))
    color = AT_THINK;
  if(!str_cmp(channel->name, "auction"))
    color = AT_AUCTION;
  if(!str_cmp(channel->name, "wartalk"))
    color = AT_WARTALK;
  if(!str_cmp(channel->name, "hint"))
    color = AT_LBLUE;
  if(!str_cmp(channel->name, "quest"))
    color = AT_CYAN;
  if(color == -1)
    color = AT_GOSSIP;

  snprintf(col, MIL, "%s", color_str(color, ch));

  arg = argument;
  arg = one_argument(arg, word);

  if(word[0] == '@' && (social = find_social(word + 1)) != NULL)
  {
    if(arg && *arg)
    {
      char                    name[MIL];

      one_argument(arg, name);

      if((victim = get_char_world(ch, name)))
        arg = one_argument(arg, name);

      if(!victim)
      {
        socbuf_char = social->char_no_arg;
        socbuf_vict = social->others_no_arg;
        socbuf_other = social->others_no_arg;
        if(!socbuf_char && !socbuf_other)
          social = NULL;
      }
      else if(victim == ch)
      {
        socbuf_char = social->char_auto;
        socbuf_vict = social->others_auto;
        socbuf_other = social->others_auto;
        if(!socbuf_char && !socbuf_other)
          social = NULL;
      }
      else if(victim != ch)
      {
        socbuf_char = social->char_found;
        socbuf_vict = social->vict_found;
        socbuf_other = social->others_found;
        if(!socbuf_char && !socbuf_other && !socbuf_vict)
          social = NULL;
      }
      else
        social = NULL;
    }
    else
    {
      socbuf_char = social->char_no_arg;
      socbuf_vict = social->others_no_arg;
      socbuf_other = social->others_no_arg;
      if(!socbuf_char && !socbuf_other)
        social = NULL;
    }
  }

  if(word[0] == ',')
    emote = TRUE;

  if((!str_cmp(channel->name, "say")) || (!str_cmp(channel->name, "osay")))
  {
    ch_printf(ch, "%sYou %s '%s'\r\n", col, channel->name, argument);
  }
  else
  {
    if(social)
    {
      act_printf(AT_PLAIN, ch, argument, victim, TO_CHAR, " &W[%s%s&W] %s%s", col, capitalize(channel->name), col, socbuf_char);
    }
    else if(emote)
    {
      argument = argument + 1;
      ch_printf(ch, " &W[%s%s&W] %s%s %s\r\n", col, capitalize(channel->name), col, ch->name, argument);
    }
    else if(!str_cmp(channel->name, "hint"))
    {
      ch_printf(ch, "%s %s&c '%s'\r\n", col, channel->name, argument);
    }
    else
      ch_printf(ch, "%s [%s] '%s'\r\n", col, capitalize(channel->name), argument);
  }

  if(!str_cmp(channel->name, "sooc"))
  {
    snprintf(buf2, MSL, "&P%s: %s (%s)&D", IS_NPC(ch) ? ch->short_descr : ch->name, argument, channel->name);
    append_to_file(CSAVE_FILE, buf2);
  }
  if(!str_cmp(channel->name, "muse"))
  {
    snprintf(buf2, MSL, "&g%s: %s (%s)&D", IS_NPC(ch) ? ch->short_descr : ch->name, argument, channel->name);
    append_to_file(CSAVE_FILE, buf2);
  }
  if(!str_cmp(channel->name, "ooc"))
  {
    snprintf(buf2, MSL, "&C%s: %s (%s)&D", IS_NPC(ch) ? ch->short_descr : ch->name, argument, channel->name);
    append_to_file(CSAVE_FILE, buf2);
  }
  if(!str_cmp(channel->name, "chat"))
  {
    snprintf(buf2, MSL, "&C%s: %s (%s)&D", IS_NPC(ch) ? ch->short_descr : ch->name, argument, channel->name);
    append_to_file(CSAVE_FILE, buf2);
  }
  if(!str_cmp(channel->name, "think"))
  {
    snprintf(buf2, MSL, "&R%s: %s (%s)&D", IS_NPC(ch) ? ch->short_descr : ch->name, argument, channel->name);
    append_to_file(CSAVE_FILE, buf2);
  }
  if(!str_cmp(channel->name, "6d"))
  {
    snprintf(buf2, MSL, "&Y%s: %s (%s)&D", IS_NPC(ch) ? ch->short_descr : ch->name, argument, channel->name);
    append_to_file(CSAVE_FILE, buf2);
  }

  if(IS_SET(ch->in_room->room_flags, ROOM_LOGSPEECH))
  {
    snprintf(buf2, MSL, "%s: %s (%s)", IS_NPC(ch) ? ch->short_descr : ch->name, argument, channel->name);
    append_to_file(LOG_FILE, buf2);
  }

  /*
   * Channel history. Records the last 20 messages to channels which keep historys 
   */
  if(channel->keephistory)
  {

    for(x = 0; x < 20; x++)
    {
      int                     type;
      MORPH_DATA             *morph;

      type = 0;
      if(IS_AFFECTED(ch, AFF_INVISIBLE))
        type = 1;
      if(ch->pcdata && xIS_SET(ch->act, PLR_WIZINVIS))
        type = 2;
      if(!ch->pcdata && xIS_SET(ch->act, ACT_MOBINVIS))
        type = 3;

      if(channel->history[x][0] == NULL)
      {
        if(VLD_STR(channel->history[x][0]))
          STRFREE(channel->history[x][0]);
        if(VLD_STR(channel->history[x][1]))
          STRFREE(channel->history[x][1]);

/* Volk Jan-16: bug fix, hopefully this will prevent unknown history[x][0] */
        if(IS_AFFECTED(ch, AFF_DISGUISE) || IS_AFFECTED(ch, AFF_SHAPESHIFT))
          channel->history[x][0] = STRALLOC(ch->morph->morph->name);
        else if(IS_NPC(ch) && !IS_AFFECTED(ch, AFF_DISGUISE) && !IS_AFFECTED(ch, AFF_SHAPESHIFT))
          channel->history[x][0] = STRALLOC(ch->short_descr);
        else if(!IS_NPC(ch) && !IS_AFFECTED(ch, AFF_DISGUISE) && !IS_AFFECTED(ch, AFF_SHAPESHIFT))
          channel->history[x][0] = STRALLOC(ch->name);
        else
          channel->history[x][0] = STRALLOC("somebody");

        t = time(NULL);
        local = localtime(&t);
        argument = add_percent(argument);
        snprintf(logbuf, MSL, "   &R[%-2.2d:%-2.2d] &G%%s%s %s\r\n", local->tm_hour, local->tm_min, emote ? "" : ":", argument);
        channel->history[x][1] = STRALLOC(logbuf);
        channel->hlevel[x] = type;
        channel->hinvis[x] = 0;
        if(type == 3)
          channel->hinvis[x] = ch->mobinvis;
        else if(type == 2)
          channel->hinvis[x] = ch->pcdata->wizinvis;
        break;
      }

      if(x == 19)
      {
        int                     y;

        for(y = 1; y < 20; y++)
        {
          int                     z = y - 1;

          if(channel->history[z][0] && channel->history[z][1] && channel->history[z][0] != NULL && channel->history[z][1] != NULL)
          {
            STRFREE(channel->history[z][0]);
            STRFREE(channel->history[z][1]);
            channel->history[z][0] = STRALLOC(channel->history[y][0]);
            channel->history[z][1] = STRALLOC(channel->history[y][1]);
            channel->hlevel[z] = channel->hlevel[y];
            channel->hinvis[z] = channel->hinvis[y];
          }
        }

        if(VLD_STR(channel->history[x][0]))
          STRFREE(channel->history[x][0]);
        if(VLD_STR(channel->history[x][1]))
          STRFREE(channel->history[x][1]);
        if(IS_NPC(ch))
          channel->history[x][0] = STRALLOC(ch->short_descr);
        else
          channel->history[x][0] = STRALLOC(ch->name);

        t = time(NULL);
        local = localtime(&t);
        argument = add_percent(argument);
        snprintf(logbuf, MSL, "   &R[%-2.2d:%-2.2d] &G%%s%s %s\r\n", local->tm_hour, local->tm_min, emote ? "" : ":", argument);
        channel->history[x][1] = STRALLOC(logbuf);
        channel->hlevel[x] = type;
        channel->hinvis[x] = 0;
        if(type == 3)
          channel->hinvis[x] = ch->mobinvis;
        else if(type == 2)
          channel->hinvis[x] = ch->pcdata->wizinvis;
      }
    }
  }

  for(vch = first_char; vch; vch = vch->next)
  {
    if(vch == ch)
      continue;

    if(!IS_NPC(ch))
    {
      if(xIS_SET(ch->act, PLR_COMMUNICATION) && !str_cmp(channel->name, "say") && vch->in_room->vnum == ch->in_room->vnum)
        ch_printf(ch, "!!SOUND(sound/say.wav)\r\n");
    }

    /*
     * So puppets can redirect says and that 
     */
    if(IS_PUPPET(vch))
    {
      char                   *sbuf = argument;
      char                    lbuf[MIL + 4];

      position = vch->position;
      set_position(vch, POS_STANDING);

      if(vch->level < channel->level && vch->trust < channel->level)
        continue;

      /*
       * Make it skip the ooc and chat for puppets 
       */
      if(!str_cmp(channel->name, "ooc") || !str_cmp(channel->name, "chat"))
        continue;

      if(IS_SET(vch->in_room->room_flags, ROOM_SILENCE))
        continue;

      if(channel->type == CHAN_ZONE && vch->in_room->area != ch->in_room->area)
        continue;

      if(channel->type == CHAN_ROOM && vch->in_room != ch->in_room)
        continue;

      if(channel->type == CHAN_PK && !IS_PKILL(vch) && !IS_IMMORTAL(vch))
        continue;

      if((vch->position == POS_SLEEPING) && channel->type == CHAN_ROOM && !IS_IMMORTAL(vch))
        continue;

      if(channel->type == CHAN_SECRET)
      {
        if(IS_NPC(vch))
          continue;
        if(vch->chan_invite != 1)
          continue;
      }

      if(channel->type == CHAN_ALLIANCE)
      {
        if(IS_NPC(vch))
          continue;
        if(!vch->pcdata->clan_name || str_cmp(vch->pcdata->clan_name, "alliance"))
          continue;
      }

      if(channel->type == CHAN_THRONG)
      {
        if(IS_NPC(vch))
          continue;
        if(!vch->pcdata->clan_name || str_cmp(vch->pcdata->clan_name, "throng"))
          continue;
      }

      if(channel->type == CHAN_HALCYON)
      {
        if(IS_NPC(vch))
          continue;
        if(!vch->pcdata->clan_name || str_cmp(vch->pcdata->clan_name, "halcyon"))
          continue;
      }

      if(channel->type == CHAN_PALEON)
      {
        if(IS_NPC(vch))
          continue;
        if(!vch->pcdata->city_name || str_cmp(vch->pcdata->city_name, "paleon city"))
          continue;
      }

      if(channel->type == CHAN_DAKAR)
      {
        if(IS_NPC(vch))
          continue;
        if(!vch->pcdata->city_name || str_cmp(vch->pcdata->city_name, "dakar city"))
          continue;
      }

      if(channel->type == CHAN_FORBIDDEN)
      {
        if(IS_NPC(vch))
          continue;
        if(!vch->pcdata->city_name || str_cmp(vch->pcdata->city_name, "forbidden city"))
          continue;
      }

      snprintf(col, MIL, "%s", color_str(color, vch));
      if(((IS_NPC(ch) && xIS_SET(ch->act, ACT_MOBINVIS)) || (!IS_NPC(ch) && xIS_SET(ch->act, PLR_WIZINVIS))) && can_see(vch, ch) && IS_IMMORTAL(vch))
        snprintf(lbuf, sizeof(lbuf), "%s(%d) ", col, (!IS_NPC(ch)) ? ch->pcdata->wizinvis : ch->mobinvis);
      else
        lbuf[0] = '\0';

      if((!str_cmp(channel->name, "say")) || (!str_cmp(channel->name, "osay")))
      {
        if(!social && !emote)
        {
          snprintf(buf, MSL, "$n %ss '$t'", channel->name);
          act(AT_SAY, strcat(lbuf, buf), ch, sbuf, vch, TO_VICT);
        }
      }
      else if(!social && !emote)
      {
        if(!str_cmp(channel->name, "hint"))
        {
          snprintf(buf, MSL, "%s %s $t%s", col, channel->name, col);
          act(AT_PLAIN, strcat(lbuf, buf), ch, sbuf, vch, TO_VICT);
        }
        else
          snprintf(buf, MSL, "%s [%s] $n '$t%s'", col, capitalize(channel->name), col);
        act(AT_PLAIN, strcat(lbuf, buf), ch, sbuf, vch, TO_VICT);
      }
      if(emote)
      {
        snprintf(buf, MSL, " &W[%s%s&W] %s$n $t", col, capitalize(channel->name), col);
        act(AT_PLAIN, strcat(lbuf, buf), ch, sbuf, vch, TO_VICT);
      }
      if(social)
      {
        if(vch == victim)
          act_printf(AT_PLAIN, ch, NULL, vch, TO_VICT, " &W[%s%s&W] %s%s", col, capitalize(channel->name), col, socbuf_vict);
        else
          act_printf(AT_PLAIN, ch, vch, victim, TO_THIRD, " &W[%s%s&W] %s%s", col, capitalize(channel->name), col, socbuf_other);
      }
      set_position(vch, position);
    }

    if(!vch->desc)
      continue;

    if(!IS_NPC(ch) && (!str_cmp(channel->name, "ooc") || !str_cmp(channel->name, "chat") || !str_cmp(channel->name, "icc")))
    {
      if(xIS_SET(vch->act, PLR_COMMUNICATION))
        ch_printf(vch, "!!SOUND(sound/ooc.wav)\r\n");
    }
    if(!IS_NPC(vch) && IS_IMMORTAL(vch) && !str_cmp(channel->name, "sooc"))
    {
      if(xIS_SET(vch->act, PLR_COMMUNICATION))
        ch_printf(vch, "!!SOUND(sound/sooc.wav)\r\n");
    }

    if(!IS_NPC(ch))
    {
      if(xIS_SET(vch->act, PLR_COMMUNICATION) && !str_cmp(channel->name, "say") && vch->in_room->vnum == ch->in_room->vnum)
        ch_printf(vch, "!!SOUND(sound/say.wav)\r\n");
    }
    if(vch->desc->connected == CON_PLAYING && (IS_NPC(vch) || hasname(vch->pcdata->chan_listen, channel->name)))
    {
      char                   *sbuf = argument;
      char                    lbuf[MIL + 4];  /* invis level string + buf */

      if((vch->level < channel->level) && (vch->trust < channel->level))
        continue;

      if(IS_SET(vch->in_room->room_flags, ROOM_SILENCE))
        continue;

      if(channel->type == CHAN_ZONE && vch->in_room->area != ch->in_room->area)
        continue;

      if(channel->type == CHAN_ROOM && vch->in_room != ch->in_room)
        continue;

      if(channel->type == CHAN_PK && !IS_PKILL(vch) && !IS_IMMORTAL(vch))
        continue;

      if((vch->position == POS_SLEEPING) && channel->type == CHAN_ROOM && !IS_IMMORTAL(vch))
      {
        continue;
      }

      if(channel->type == CHAN_SECRET)
      {
        if(IS_NPC(vch))
          continue;
        if(vch->chan_invite != 1)
          continue;
      }

      if(channel->type == CHAN_ALLIANCE)
      {
        if(IS_NPC(vch))
          continue;
        if(vch->pcdata->clan != ch->pcdata->clan)
          continue;
      }

      if(channel->type == CHAN_HALCYON)
      {
        if(IS_NPC(vch))
          continue;
        if(vch->pcdata->clan != ch->pcdata->clan)
          continue;
      }

      if(channel->type == CHAN_THRONG)
      {
        if(IS_NPC(vch))
          continue;
        if(vch->pcdata->clan != ch->pcdata->clan)
          continue;
      }

      if(channel->type == CHAN_COUNCIL)
      {
        if(IS_NPC(vch))
          continue;
        if(vch->pcdata->council != ch->pcdata->council)
          continue;
      }

      if(channel->type == CHAN_PALEON)
      {
        if(IS_NPC(vch))
          continue;
        if(!vch->pcdata->city_name || str_cmp(vch->pcdata->city_name, "paleon city"))
          continue;
      }

      if(channel->type == CHAN_DAKAR)
      {
        if(IS_NPC(vch))
          continue;
        if(!vch->pcdata->city_name || str_cmp(vch->pcdata->city_name, "dakar city"))
          continue;
      }

      if(channel->type == CHAN_FORBIDDEN)
      {
        if(IS_NPC(vch))
          continue;
        if(!vch->pcdata->city_name || str_cmp(vch->pcdata->city_name, "forbidden city"))
          continue;
      }

      position = vch->position;
      set_position(vch, POS_STANDING);

      snprintf(col, MIL, "%s", color_str(color, vch));
      if(((IS_NPC(ch) && xIS_SET(ch->act, ACT_MOBINVIS)) || (!IS_NPC(ch) && xIS_SET(ch->act, PLR_WIZINVIS))) && can_see(vch, ch) && IS_IMMORTAL(vch))
        snprintf(lbuf, sizeof(lbuf), "%s(%d) ", col, (!IS_NPC(ch)) ? ch->pcdata->wizinvis : ch->mobinvis);
      else
        lbuf[0] = '\0';

      /*
       * Should we scramble the channel? 
       */
      if(channel->doscramble)
      {
#ifndef SCRAMBLE
        if(speaking != -1 && (!IS_NPC(ch) || ch->speaking))
        {
          int                     speakswell = UMIN(knows_language(vch, ch->speaking, ch),
                                                    knows_language(ch, ch->speaking, vch));

          if(speakswell < 85)
            sbuf = translate(speakswell, argument, lang_names[speaking]);
        }
#else
        if(!knows_language(vch, ch->speaking, ch) && (!IS_NPC(ch) || ch->speaking != 0))
          sbuf = scramble(argument, ch->speaking);
#endif
      }

      /*
       * Check to see if target is ignoring the sender 
       */
      if(is_ignoring(vch, ch))
      {
        /*
         * If the sender is an imm then they cannot be ignored 
         */
        if(!IS_IMMORTAL(ch) || vch->level > ch->level || vch->trust > ch->level)
        {
          /*
           * Off to oblivion! 
           */
          continue;
        }
        else
          set_char_color(AT_IGNORE, vch);
      }

      MOBtrigger = FALSE;

      /*
       * Hackish solution to stop that damned "someone chat" bug - Matarael 17.3.2002
       * Volk looking into this crashing the mud over and over.. 
       */

      if((!str_cmp(channel->name, "say")) /* || (!str_cmp(channel->name, * * "osay")) */ )
      {
        if(!social && !emote)
        {
          snprintf(buf, MSL, "$n %ss '$t'", channel->name);
          act(AT_SAY, strcat(lbuf, buf), ch, sbuf, vch, TO_VICT);
        }
        else
        {
          act_printf(AT_PLAIN, ch, vch, victim, TO_THIRD, "%s%ss, %s%s", col, socbuf_other, col, channel->name);
        }
        if(!IS_NPC(vch))
        {
          snprintf(logbuf, MSL, "&R[%-2.2d/%-2.2d %-2.2d:%-2.2d] &B%s said '&W %s &D&B'&D", local->tm_mon + 1, local->tm_mday, local->tm_hour, local->tm_min, PERS(ch, vch), sbuf);
          for(x = 0; x < 20; x++)
          {
            if(vch->pcdata->say_history[x] == NULL)
            {
              vch->pcdata->say_history[x] = STRALLOC(logbuf);
              break;
            }
            if(x == 19)
            {
              int                     i;

              for(i = 1; i < 20; i++)
              {
                STRFREE(vch->pcdata->say_history[i - 1]);
                vch->pcdata->say_history[i - 1] = STRALLOC(vch->pcdata->say_history[i]);
              }
              STRFREE(vch->pcdata->say_history[x]);
              vch->pcdata->say_history[x] = STRALLOC(logbuf);
            }
          }
        }
      }
      else if(!social && !emote)
      {
        snprintf(buf, MSL, "%s [%s] $n '$t%s'", col, capitalize(channel->name), col);
        act(AT_PLAIN, strcat(lbuf, buf), ch, sbuf, vch, TO_VICT);
      }
      if(emote)
      {
        snprintf(buf, MSL, " &W[%s%s&W] %s$n $t", col, capitalize(channel->name), col);
        act(AT_PLAIN, strcat(lbuf, buf), ch, sbuf, vch, TO_VICT);
      }
      if(social)
      {
        if(vch == victim)
          act_printf(AT_PLAIN, ch, NULL, vch, TO_VICT, " &W[%s%s&W] %s%s", col, capitalize(channel->name), col, socbuf_vict);
        else
          act_printf(AT_PLAIN, ch, vch, victim, TO_THIRD, " &W[%s%s&W] %s%s", col, capitalize(channel->name), col, socbuf_other);
      }
      set_position(vch, position);
      /*
       * Hackish solution to stop that damned "someone chat" bug - Matarael 17.3.2002 
       */
    }
  }

  /*
   * Do programs 
   */
  if(!str_cmp(channel->name, "osay") || !str_cmp(channel->name, "say"))
  {
    mprog_speech_trigger(argument, ch);
    oprog_speech_trigger(argument, ch);
    rprog_speech_trigger(argument, ch);
  }
}

void to_channel(const char *argument, const char *xchannel, int level)
{
  MUD_CHANNEL            *channel;
  char                    buf[MSL];
  DESCRIPTOR_DATA        *d;

  if(!first_descriptor || argument[0] == '\0')
    return;

  if(!(channel = find_channel(xchannel)))
    return;

  if(channel->type != CHAN_LOG)
    return;

  snprintf(buf, MSL, "%s: %s\r\n", capitalize(channel->name), argument);
  for(d = first_descriptor; d; d = d->next)
  {
    CHAR_DATA              *vch;

    vch = d->original ? d->original : d->character;

    if(!vch)
      continue;

    if(d->original)
      continue;

    /*
     * This could be coming in higher than the normal level, so check first 
     */
    if((vch->level < level) && (vch->trust < level))
      continue;

    if(d->connected == CON_PLAYING && hasname(vch->pcdata->chan_listen, channel->name))
    {
      set_char_color(AT_LOG, vch);
      send_to_char_color(buf, vch);
    }
  }
  return;
}

bool local_channel_hook(CHAR_DATA *ch, const char *command, char *argument)
{
  MUD_CHANNEL            *channel;

  if(!(channel = find_channel(command)))
    return FALSE;

  if((ch->level < channel->level) && (ch->trust < channel->level))
    return FALSE;

  /*
   * Logs are meant to be seen, not talked on 
   */
  if(channel->type == CHAN_LOG)
    return FALSE;

  if(!IS_NPC(ch) && !hasname(ch->pcdata->chan_listen, command))
  {
    ch_printf(ch, "&RYou are not listening to the &G%s &Rchannel.\r\n", channel->name);
    return TRUE;
  }

  send_tochannel(ch, channel, argument);
  return TRUE;
}

/*  Volk 10-8-06  -  Pretty colours, used for global announcements like births and deaths, clan stuff.  */
void announce(const char *argument)
{
  char                    buf[MSL];

  snprintf(buf, MSL, "\r\n&W[&RAnnouncement&W]&C %s&D", argument);
  announce_all(AT_RED, buf, ECHOTAR_ALL);
  return;
}

void to_channel_printf(const char *channel, int level, const char *fmt, ...)
{
  char                    buf[MSL * 2];
  va_list                 args;

  va_start(args, fmt);
  vsnprintf(buf, MSL * 2, fmt, args);
  va_end(args);

  to_channel(buf, channel, level);
}