pgplus/bin/
pgplus/help_files/
pgplus/port_redirector/
pgplus/src/configure/makefiles/
/*
 * Playground+ - multi.c
 * Multi (or chains) code written by Segtor
 * ---------------------------------------------------------------------------
 */

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "include/config.h"

#ifdef ALLOW_MULTIS

#define MULTI_VERSION "1.35.1"

#include "include/player.h"
#include "include/proto.h"


multi *all_multis;


void init_multis(void)
{
  all_multis = NULL;
}

void destroy_all_multis(void)
{
  multi *mscan = all_multis, *mscan2 = NULL;
  multiplayer *pscan, *pscan2;

  while (mscan)
  {
    pscan = mscan->players_list;
    pscan2 = NULL;

    while (pscan)
    {
      pscan2 = pscan;
      pscan = pscan->next_player;
      FREE(pscan2);
    }

    mscan2 = mscan;
    mscan = mscan->next_multi;
    FREE(mscan2);
  }

  all_multis = 0;
}

void multi_change_entries(player * p1, player * p2)
{
  multi *mscan = all_multis;
  multiplayer *pscan, *prev;

  while (mscan)
  {
    pscan = mscan->players_list;
    prev = NULL;

    while (pscan && (pscan->the_player != p1))
    {
      prev = pscan;
      pscan = pscan->next_player;
    }
    if (pscan->the_player == p1)
      pscan->the_player = p2;

    mscan = mscan->next_multi;
  }
}

int get_number(int real_number)
{
  return ((real_number - 1) / 32);
}

int get_bit(int real_number)
{
  return ((real_number - 1) % 32);
}

int get_real_number(int number, int bit)
{
  return ((number * 32 + bit) + 1);
}

int multi_get_new_number(void)
{
  multi *mscan = all_multis;
  int inuse[32];		/* 32 * 32 = 1024 possible multis */
  int i, j;

  for (i = 0; i < 32; i++)
    inuse[i] = 0;

  /* create list of multis in use */
  while (mscan)
  {
    inuse[get_number(mscan->number)] |= (2 << get_bit(mscan->number));
    mscan = mscan->next_multi;
  }

  /* find free number */
  for (i = 0; i < 32; i++)
    for (j = 0; j < 32; j++)
      if (!(inuse[i] & 2 << j))
	return (get_real_number(i, j));

  /* none found */
  return 0;
}

void remove_from_multis(player * p)
{
  multi *mscan = all_multis;
  multiplayer *pscan;

  while (mscan)
  {
    pscan = mscan->players_list;

    while (pscan)
    {
      if (pscan->the_player == p)
	pscan->the_player = NULL;

      pscan = pscan->next_player;
    }

    mscan = mscan->next_multi;
  }
}

void remove_from_multi(player * p, multi * m)
{
  multiplayer *pscan;

  if (!(m->players_list))
    return;

  pscan = m->players_list;

  while (pscan)
  {
    if (pscan->the_player == p)
      pscan->the_player = NULL;

    pscan = pscan->next_player;
  }
}

void tell_multi(int number, player * p, char *str)
{
  multi *mscan = find_multi_from_number(number);
  multiplayer *pscan;
  int curr_sys_col;
  int is_emote = 0, br;
  int curr_mode;

  sys_color_atm = TELsc;
  command_type |= MULTI_COM;

  if (number < 0)
  {
    number = -number;
    is_emote = 1;
    mscan = find_multi_from_number(number);
  }

  if (mscan)
  {
    mscan->multi_idle = 0;
    mscan->multi_flags &= ~MULTI_WARNED;

    if (mscan->multi_flags & MULTI_FRIENDSLIST)
      sys_color_atm = FRTsc;

    curr_sys_col = sys_color_atm;

    pscan = mscan->players_list;

    while (pscan)
    {
      sys_color_atm = curr_sys_col;
      curr_mode = command_type;
      if ((pscan->the_player) && (pscan->the_player->tag_flags & BLOCK_MULTIS))
      {
	command_type = 0;
	sys_color_atm = SYSsc;
	if (p)
	  TELLPLAYER(p, " %s is blocking multis\n",
		     pscan->the_player->name);
	command_type = curr_mode;
      }
      else if (pscan->the_player && 
              ((pscan->the_player->tag_flags & BLOCK_FRIENDS) && (mscan->multi_flags & MULTI_FRIENDSLIST)))
      {
	command_type = 0;
	sys_color_atm = SYSsc;
	if (p)
	  TELLPLAYER(p, " %s is blocking friends\n",
		     pscan->the_player->name);
	command_type = curr_mode;
      }
      else if ((pscan->the_player) && (pscan->the_player->tag_flags & BLOCK_TELLS))
      {
	command_type = 0;
	sys_color_atm = SYSsc;
	if (p)
	  TELLPLAYER(p, " %s is blocking tells\n",
		     pscan->the_player->name);
	command_type = curr_mode;
      }
      else
      {
	if (pscan->the_player != p)
	{
	  if (pscan->the_player)
	  {
	    if (p && !(command_type & (ECHO_COM | REMOVE_COM)))
	    {
	      if (is_emote && emote_no_break(*str))
	      {
		TELLPLAYER(pscan->the_player, "(%d) %s%s", number, p->name, str);
#ifdef INTELLIBOTS
		if (pscan->the_player->residency & ROBOT_PRIV)
		  intelligent_robot(p, pscan->the_player, str, IR_PRIVATE);
#endif
	      }
	      else
	      {
		br = 0;
		if (*str == '\007' && pscan->the_player->custom_flags & NOBEEPS)
		{
		  br = 1;
		  str++;
		}
		TELLPLAYER(pscan->the_player, "(%d) %s %s", number, p->name, str);
#ifdef INTELLIBOTS
		if (pscan->the_player->residency & ROBOT_PRIV)
		  intelligent_robot(p, pscan->the_player, str, IR_PRIVATE);
#endif
		if (br)
		  str--;
	      }
	    }
	    else
	    {
	      TELLPLAYER(pscan->the_player, "(%d) %s", number, str);
#ifdef INTELLIBOTS
	      if (p && (pscan->the_player->residency & ROBOT_PRIV))
		intelligent_robot(p, pscan->the_player, str, IR_PRIVATE);
#endif
	    }
	  }
	}
      }
      sys_color_atm = curr_sys_col;

      pscan = pscan->next_player;
    }
  }

  command_type &= ~MULTI_COM;
  sys_color_atm = SYSsc;

  if (mscan)
  {
    pscan = mscan->players_list;

    while (pscan)
    {
      if (pscan->the_player)
	pscan->the_player->flags &= ~TAGGED;
      pscan = pscan->next_player;
    }
  }
}

void vtell_multi(int number, player * p, char *format,...)
{
  va_list argum;
  char *oldstack;

  oldstack = stack;
  va_start(argum, format);
  vsprintf(stack, format, argum);
  va_end(argum);
  stack = end_string(stack);
  tell_multi(number, p, oldstack);
  stack = oldstack;
}

multi *remove_multi_player(player *pc, multi * m1, multi * m2, char *reason)
{
  multi *m3 = m1->next_multi;
  multiplayer *p, *n;

  if (!m1)
    return (multi *) 0;

  command_type |= REMOVE_COM;
  if (config_flags & cfMULTI_INFORM)
    vtell_multi(m1->number, pc, "Multi removed%s\n", reason);
  command_type &= ~REMOVE_COM;

  if (m2)
    m2->next_multi = m3;
  else
    all_multis = m3;

  p = m1->players_list;
  while (p)
  {
    n = p;
    p = p->next_player;
    FREE(n);
  }
  FREE(m1);

  if (m2)
    return (m2->next_multi);
  return m3;
}

multi *remove_multi(multi *m1, multi *m2, char *reason)
{
  return remove_multi_player(NULL, m1, m2, reason);
}

void update_multis(void)
{
  multi *mscan = all_multis, *mscan2 = NULL;
  multiplayer *temppl = NULL, *pscan = NULL;
  char ti1[250], ti2[250];
  int ci;

  while (mscan)
  {
    action = "multi: list empty";
    mscan->multi_idle++;

    /* remove multi if player list empty */
    if (!(mscan->players_list))
    {
      mscan = remove_multi(mscan, mscan2, "");
      continue;
    }

    action = "multi: check idle";
    /* remove multi for being idle too long */
    ci = atoi(get_config_msg("multi_idle_out"));
    ci *= ONE_MINUTE;
    if (ci > 0 && mscan->multi_idle >= ci)
    {
      if (!(mscan->multi_flags & MULTI_NOIDLEOUT))
      {
	mscan = remove_multi(mscan, mscan2, " due to being idle");
	continue;
      }
    }

    action = "multi: owner lost";
    /* remove friends multi that lost its owner */
    if (mscan->multi_flags & MULTI_FRIENDSLIST)
    {
      if (!(mscan->players_list->the_player))
      {
	mscan = remove_multi(mscan, mscan2, " due to owner leaving");
	continue;
      }
    }

    action = "multi: enough users";
    /* remove multi if too few people on */
    if (players_on_multi(mscan) < 2)
    {
      mscan = remove_multi(mscan, mscan2, " due to too few users");
      continue;
    }

    if (config_flags & cfMULTI_INFORM)
    {
      action = "multi: warn idle";
      /* warn idle multis */
      ci = atoi(get_config_msg("multi_idle_out"));
      ci *= ONE_MINUTE;
      if (ci > 0)
      {
	if ((mscan->multi_idle >= (ci - 60)) &&
	    (!(mscan->multi_flags & MULTI_WARNED)))
	{
	  if (!(mscan->multi_flags & MULTI_NOIDLEOUT))
	  {
	    strcpy(ti1, word_time(ci - 60));
	    strcpy(ti2, word_time(ci));
	    vtell_multi(mscan->number, NULL, "Multi has been idle for %s, "
			"will timeout in %s\n", ti1, ti2);

	    mscan->multi_flags |= MULTI_WARNED;
	    mscan->multi_idle = ci - 60;
	  }
	}
      }
    }

    action = "multi: catenate first";
    /* catenate players list */
    while (mscan->players_list)
    {
      if (mscan->players_list->the_player)
	break;
      temppl = mscan->players_list;
      mscan->players_list = mscan->players_list->next_player;
      FREE(temppl);
    }

    action = "multi: catenate next";
    pscan = mscan->players_list;
    temppl = NULL;
    while (pscan)
    {
      if (!(pscan->the_player))
      {
	temppl->next_player = pscan->next_player;
	FREE(pscan);
	pscan = temppl;
      }
      temppl = pscan;
      pscan = pscan->next_player;
    }

    mscan2 = mscan;
    mscan = mscan->next_multi;
  }
  action = "";
}

int player_on_multi(player * p, multi * m)
{
  multiplayer *pscan = m->players_list;

  while (pscan)
  {
    if (pscan->the_player == p)
      return 1;
    pscan = pscan->next_player;
  }

  return 0;
}

multi *find_multi_from_number(int number)
{
  multi *mscan = all_multis;

  while (mscan)
  {
    if (mscan->number == number)
      return mscan;
    mscan = mscan->next_multi;
  }

  return NULL;
}

int find_friend_multi_number_name(char *pl_name)
{
  multi *mscan = all_multis;

  while (mscan)
  {
    if (mscan->multi_flags & MULTI_FRIENDSLIST)
      if (mscan->players_list)
	if (mscan->players_list->the_player)
	  if (!(strcasecmp(mscan->players_list->the_player->lower_name, pl_name)))
	    return mscan->number;

    mscan = mscan->next_multi;
  }

  return 0;
}

int find_friend_multi_number(player * p)
{
  return find_friend_multi_number_name(p->lower_name);
}

int players_on_multi(multi * m)
{
  int count = 0;
  multiplayer *pscan;

  if (m)
  {
    pscan = m->players_list;

    while (pscan)
    {
      if (pscan->the_player)
	count++;

      pscan = pscan->next_player;
    }
  }

  return count;
}

int multi_count(void)
{
  multi *mscan = all_multis;
  int count = 0;

  while (mscan)
  {
    count++;
    mscan = mscan->next_multi;
  }

  return count;
}

int assign_friends_multi(player * p)
{
  int mn;

  mn = find_friend_multi_number(p);
  if (mn)
    return mn;

  mn = multi_get_new_number();

  create_friends_multi(p, mn);
  return mn;
}

void add_to_friends_multis(player * p)
{
  player *scan;
  list_ent *l;
  int mn;

  for (scan = flatlist_start; scan; scan = scan->flat_next)
  {
    l = find_list_entry(scan, p->name);
    if (l)
      if (l->flags & FRIEND)
      {
	mn = find_friend_multi_number(scan);
	if (mn)
	  add_to_multi(p, find_multi_from_number(mn));
      }
  }
}

void add_to_multi(player * p, multi * m)
{
  multiplayer *pscan = NULL, *t = NULL;

  pscan = m->players_list;

  while (pscan)
  {
    t = pscan;
    pscan = pscan->next_player;
  }

  pscan = (multiplayer *) MALLOC(sizeof(multiplayer));
  if (!pscan)
  {
    LOGF("multi", "Could not allocate memory for player %s to multi %d!",
         p->name, m->number); 
    return;
  }
  pscan->next_player = NULL;
  pscan->the_player = p;
  if (t)
    t->next_player = pscan;
  else
    m->players_list = pscan;
}

/* creating multis */
void create_friends_multi(player * p, int number)
{
  multi *new_multi = (multi *) MALLOC(sizeof(multi));
  list_ent *l;
  player *p2;

  if (!new_multi)
  {
    LOGF("multi", "Could not create friends' multi for %s!", p->name);
    return;
  }

  new_multi->multi_flags = MULTI_FRIENDSLIST;

  new_multi->number = number;
  new_multi->multi_idle = 0;

  new_multi->next_multi = all_multis;
  all_multis = new_multi;

  new_multi->players_list = NULL;

  add_to_multi(p, new_multi);

  /* loop through players list */
  if (p->saved)
  {
    l = p->saved->list_top;

    if (l)
    {
      do
      {
	if (l->flags & FRIEND && strcasecmp(l->name, "everyone"))
	{
	  p2 = find_player_absolute_quiet(l->name);
	  if (p2)
	    add_to_multi(p2, new_multi);
	}
	l = l->next;
      }
      while (l);
    }
  }
}

/* multi talking stuff */
void multi_number_to_names(player * p, int number, char *str)
{
  char *oldstr = str;
  multi *m = find_multi_from_number(number);
  multiplayer *mp;
  int pls, listed;

  if (!m)
    return;

  pls = players_on_multi(m);
  listed = 0;
  if (p == NULL)
    pls++;
  mp = m->players_list;
  while (mp)
  {
    if (!(mp->the_player == p))
    {
      if (listed < (pls - 3))
	sprintf(str, "%s, ", mp->the_player->name);
      else if (listed == (pls - 3))
	sprintf(str, "%s and ", mp->the_player->name);
      else
	sprintf(str, "%s", mp->the_player->name);
      str = strchr(str, 0);
      listed++;
    }
    mp = mp->next_player;
  }
  str = oldstr;
}

int multi_exists(multi *m)
{
  multi *scan = all_multis;
  multiplayer *pscan = NULL;
  int exists = 0, count = players_on_multi(m), count2;

  while (scan)
  {
    if (scan->multi_flags & MULTI_FRIENDSLIST)
    {
      scan = scan->next_multi;
      continue;
    }

    if (players_on_multi(scan) == count)
    {
      count2 = 0;
      pscan = scan->players_list;
      while (pscan)
      {
        if (player_on_multi(pscan->the_player, m))
          count2++;
        else
          break;
        pscan = pscan->next_player;
      }
      if (count == count2)
      {
        exists = scan->number;
        break;
      }
    }
    scan = scan->next_multi;
  }
  return exists;
}

int solve_multi(player * p, char *str)
{
  char *endp, *start, *oldstack = stack;
  int mn;
  player *p2;
  multi *mnew;

  if (isdigit(*str))
  {
    if (strchr(str, ','))
    {
      start = stack;
      while (*str && *str != ',')
	*stack++ = *str++;
      *stack++ = 0;
      if (*str)
	str++;
      mn = (int) strtol(start, &endp, 10);
      if (*endp)
      {
	tell_player(p, "Please enter a correct multi number\n");
        stack = oldstack;
	return -1;
      }
      mnew = find_multi_from_number(mn);
      if (!mnew)
      {
	tell_player(p, "That multi does not exist\n");
        stack = oldstack;
	return -1;
      }
      if (!(player_on_multi(p, mnew)))
      {
	tell_player(p, "You're not on that multi\n");
        stack = oldstack;
	return -1;
      }
      if (mnew->multi_flags & MULTI_FRIENDSLIST)
      {
	tell_player(p, "You can't add people to friends multis\n");
        stack = oldstack;
	return -1;
      }
      stack = start;
      while (*str)
      {
	start = stack;
	while (*str && *str != ',')
	  *stack++ = *str++;
	*stack++ = 0;
	if (*str)
	  str++;
	if (!strcasecmp(start, "me"))
	  p2 = NULL;
	else
	{
	  command_type |= NO_P_MATCH;
	  p2 = find_player_global(start);
	  command_type &= ~NO_P_MATCH;
	}
	if (p2 && !player_on_multi(p2, mnew))
	  add_to_multi(p2, mnew);
	stack = start;
      }
      return mn;
    }
    mn = (int) strtol(str, &endp, 10);
    if (*endp)
    {
      tell_player(p, "Please enter a correct multi number\n");
      return -1;
    }
    mnew = find_multi_from_number(mn);
    if (!mnew)
    {
      tell_player(p, "No such multi exists\n");
      return -1;
    }
    if (!(player_on_multi(p, mnew)))
    {
      tell_player(p, "You're not on that multi!\n");
      return -1;
    }
  }
  else if (!(strcasecmp(str, "friends")))
  {
    mn = find_friend_multi_number(p);
  }
  else if (strchr(str, ','))
  {
    mnew = (multi *) MALLOC(sizeof(multi));
    if (!mnew)
    {
      LOGF("multi", "Could not create multi for %s!", p->name);
      stack = oldstack;
      return -1;
    }
    mnew->next_multi = all_multis;
    mn = multi_get_new_number();
    mnew->number = mn;
    mnew->multi_idle = 0;
    mnew->multi_flags = MULTI_NOIDLEOUT;
    mnew->players_list = NULL;
    add_to_multi(p, mnew);
    while (*str)
    {
      start = stack;
      while (*str && *str != ',')
	*stack++ = *str++;
      *stack++ = 0;
      if (*str)
	str++;
      if (!strcasecmp(start, "me"))
	p2 = NULL;
      else
      {
	command_type |= NO_P_MATCH;
	p2 = find_player_global(start);
	command_type &= ~NO_P_MATCH;
      }
      if (p2 && !player_on_multi(p2, mnew))
	add_to_multi(p2, mnew);
      stack = start;
    }
    mnew->multi_flags &= ~MULTI_NOIDLEOUT;
    if (!multi_exists(mnew))
      all_multis = mnew;
    else
    {
      mn = multi_exists(mnew);
      FREE(mnew);
    }
  }
  else
  {
    p2 = find_player_global(str);
    mn = find_friend_multi_number(p2);
  }
  stack = oldstack;
  return mn;
}

void multi_tell(player * p, char *str, char *msg)
{
  char *scan, *mid, pnames[1000];
  int mn;
  multi *m;

  mn = solve_multi(p, str);
  if (mn < 1)
    return;

  m = find_multi_from_number(mn);

  for (scan = msg; *scan; scan++);

  switch (*(--scan))
  {
    case '?':
      mid = "asks of";
      break;
    case '!':
      mid = "exclaims to";
      break;
    default:
      mid = "tells";
      break;
  }

  if (m->multi_flags & MULTI_FRIENDSLIST)
  {
    if (m->players_list->the_player == p)
      vtell_multi(mn, p, "%s %s friends '%s'\n", mid, their_player(p),
		  msg);
    else
      vtell_multi(mn, p, "%s %s's friends '%s'\n", mid,
		  m->players_list->the_player->name, msg);
  }
  else
  {
    pnames[0] = 0;
    multi_number_to_names(p, mn, pnames);
    if (config_flags & cfMULTIVERBOSE)
      vtell_multi(mn, p, "%s %s '%s'\n", mid, pnames, msg);
    else
      vtell_multi(mn, p, "%s you all '%s'\n", mid, msg);
  }

  switch (*scan)
  {
    case '?':
      mid = "ask of";
      break;
    case '!':
      mid = "exclaim to";
      break;
    default:
      mid = "tell";
      break;
  }

  sys_color_atm = SYSsc;

  if (m->multi_flags & MULTI_FRIENDSLIST)
  {
    if (m->players_list->the_player == p)
      TELLPLAYER(p, "(%d) You %s your friends '%s'\n", mn, mid, msg);
    else
      TELLPLAYER(p, "(%d) You %s %s's friends '%s'\n", mn, mid,
		 m->players_list->the_player->name, msg);
  }
  else
  {
    if (config_flags & cfMULTIVERBOSE)
      TELLPLAYER(p, "(%d) You %s %s '%s'\n", mn, mid, pnames, msg);
    else
      TELLPLAYER(p, "(%d) You %s them '%s'\n", mn, mid, msg);
  }
}

void multi_remote(player * p, char *str, char *msg)
{
  int mn;
  multi *m;
  char pnames[1000];
  char tostr[512];

  mn = solve_multi(p, str);
  if (mn < 1)
    return;

  m = find_multi_from_number(mn);

  if (m->multi_flags & MULTI_FRIENDSLIST)
  {
    if (m->players_list->the_player == p)
    {
      if (config_flags & cfMULTIVERBOSE)
	vtell_multi(-mn, p, "%s (to %s friends)\n", msg, their_player(p));
      else
	vtell_multi(-mn, p, "%s\n", msg);
      sys_color_atm = SYSsc;
      if (emote_no_break(*msg))
      {
	if (config_flags & cfMULTIVERBOSE)
	  TELLPLAYER(p, "(%d) You emote: %s%s (to your friends)\n", mn,
		     p->name, msg);
	else
	  TELLPLAYER(p, "(%d) You emote: %s%s\n", mn, p->name, msg);
      }
      else
      {
	if (config_flags & cfMULTIVERBOSE)
	  TELLPLAYER(p, "(%d) You emote: %s %s (to your friends)\n", mn,
		     p->name, msg);
	else
	  TELLPLAYER(p, "(%d) You emote: %s %s\n", mn, p->name, msg);
      }
    }
    else
    {
      if (config_flags & cfMULTIVERBOSE)
	vtell_multi(-mn, p, "%s (to %s's friends)\n", msg,
		    m->players_list->the_player->name);
      else
	vtell_multi(-mn, p, "%s\n", msg);

      sys_color_atm = SYSsc;
      if (emote_no_break(*msg))
      {
	if (config_flags & cfMULTIVERBOSE)
	  TELLPLAYER(p, "(%d) You emote: %s%s (to %s's friends)\n", mn,
		     p->name, msg, m->players_list->the_player->name);
	else
	  TELLPLAYER(p, "(%d) You emote: %s%s\n", mn, p->name, msg);
      }
      else
      {
	if (config_flags & cfMULTIVERBOSE)
	  TELLPLAYER(p, "(%d) You emote: %s %s (to %s's friends)\n", mn,
		     p->name, msg, m->players_list->the_player->name);
	else
	  TELLPLAYER(p, "(%d) You emote: %s %s\n", mn, p->name, msg);
      }
    }
  }
  else
  {
    pnames[0] = 0;
    multi_number_to_names(p, mn, pnames);
    if (config_flags & cfMULTIVERBOSE)
      sprintf(tostr, " (to %s)", pnames);
    else
      memset(tostr, 0, 512);

    vtell_multi(-mn, p, "%s%s\n", msg, tostr);
    sys_color_atm = SYSsc;
    if (emote_no_break(*msg))
      TELLPLAYER(p, "(%d) You emote: %s%s%s\n", mn, p->name,
		 msg, tostr);
    else
      TELLPLAYER(p, "(%d) You emote: %s %s%s\n", mn, p->name,
		 msg, tostr);
  }
}

void multi_rsing(player * p, char *str, char *msg)
{
  char *oldstack = stack;

  sprintf(stack, "sings o/~ %s o/~", msg);
  stack = end_string(stack);
  multi_remote(p, str, oldstack);
  stack = oldstack;
}

void multi_yell(player * p, char *str, char *msg)
{
  char *scan, *mid, pnames[1000];
  int mn;
  multi *m;

  mn = solve_multi(p, str);
  if (mn < 1)
    return;

  m = find_multi_from_number(mn);

  for (scan = msg; *scan; scan++);

  switch (*(--scan))
  {
    case '?':
      mid = "boggles at";
      break;
    case '!':
      mid = "hollers at";
      break;
    default:
      mid = "yells at";
      break;
  }

  if (m->multi_flags & MULTI_FRIENDSLIST)
  {
    if (m->players_list->the_player == p)
      vtell_multi(mn, p, "\007%s %s friends '%s'\n", mid, their_player(p),
		  msg);
    else
      vtell_multi(mn, p, "\007%s %s's friends '%s'\n", mid,
		  m->players_list->the_player->name, msg);
  }
  else
  {
    pnames[0] = 0;
    multi_number_to_names(p, mn, pnames);
    vtell_multi(mn, p, "\007%s %s '%s'\n", mid, pnames, msg);
  }

  switch (*scan)
  {
    case '?':
      mid = "boggle at";
      break;
    case '!':
      mid = "holler at";
      break;
    default:
      mid = "yell at";
      break;
  }

  sys_color_atm = SYSsc;

  if (m->multi_flags & MULTI_FRIENDSLIST)
  {
    if (m->players_list->the_player == p)
      TELLPLAYER(p, "(%d) You %s your friends '%s'\n", mn, mid, msg);
    else
      TELLPLAYER(p, "(%d) You %s %s's friends '%s'\n", mn, mid,
		 m->players_list->the_player->name, msg);
  }
  else
    TELLPLAYER(p, "(%d) You %s %s '%s'\n", mn, mid, pnames, msg);
}

void multi_recho(player * p, char *str, char *msg)
{
  int mn;
  multi *m;
  char pnames[1000];
  char tostr[512];

  mn = solve_multi(p, str);
  if (mn < 1)
    return;

  m = find_multi_from_number(mn);
  command_type |= ECHO_COM;

  if (m->multi_flags & MULTI_FRIENDSLIST)
  {
    if (m->players_list->the_player == p)
    {
      if (config_flags & cfMULTIVERBOSE)
	sprintf(tostr, " (to %s friends)", their_player(p));
      else
	memset(tostr, 0, 512);

      vtell_multi(mn, p, "%s%s\n", msg, tostr);
      sys_color_atm = SYSsc;
      TELLPLAYER(p, "(%d) You echo: %s (to your friends)\n", mn, msg);
    }
    else
    {
      if (config_flags & cfMULTIVERBOSE)
	sprintf(tostr, " (to %s's friends)",
		m->players_list->the_player->name);
      else
	memset(tostr, 0, 512);

      vtell_multi(mn, p, "%s%s\n", msg, tostr);
      sys_color_atm = SYSsc;
      TELLPLAYER(p, "(%d) You echo: %s (to %s's friends)\n", mn, msg,
		 m->players_list->the_player->name);
    }
  }
  else
  {
    pnames[0] = 0;
    multi_number_to_names(p, mn, pnames);
    if (config_flags & cfMULTIVERBOSE)
      sprintf(tostr, " (to %s)", pnames);
    else
      memset(tostr, 0, 512);
    vtell_multi(mn, p, "%s%s\n", msg, tostr);
    sys_color_atm = SYSsc;

    TELLPLAYER(p, "(%d) You echo: %s%s\n", mn, msg, tostr);
  }
  command_type &= ~ECHO_COM;
}

/* misc commands */
void multi_block(player * p, char *str)
{
  if (*str)
  {
    if (strcasecmp(str, "on"))
    {
      if (strcasecmp(str, "off"))
      {
	tell_player(p, " Format: blockmulti [on/off]\n");
	return;
      }
      p->tag_flags |= BLOCK_MULTIS;
    }
    else
      p->tag_flags &= ~BLOCK_MULTIS;
  }
  else
    p->tag_flags ^= BLOCK_MULTIS;

  if (!(p->tag_flags & BLOCK_MULTIS))
    tell_player(p, " You decide to get multis again\n");
  else
    tell_player(p, " You block multis\n");
}

/* list multis player is on */
void list_multis_me_on(player * p)
{
  multi *mscan = all_multis;
  char *oldstack = stack;
  int inuse[32];
  int i, j, any = 0;

  for (i = 0; i < 32; i++)
    inuse[i] = 0;

  while (mscan)
  {
    if (player_on_multi(p, mscan))
      any++;
    inuse[get_number(mscan->number)] |= (2 << get_bit(mscan->number));
    mscan = mscan->next_multi;
  }

  if (!any)
    tell_player(p, " You aren't on any multis\n");
  else
  {
    sprintf(stack, " You are on %d multis: ", any);
    stack = strchr(stack, 0);

    for (i = 0; i < 32; i++)
      for (j = 0; j < 32; j++)
	if (inuse[i] & 2 << j)
	  if (player_on_multi(p, find_multi_from_number(get_real_number(i, j))))
	  {
	    sprintf(stack, "%d, ", get_real_number(i, j));
	    stack = strchr(stack, 0);
	  }

    stack--;
    stack--;
    *stack++ = '.';
    *stack++ = '\n';
    *stack++ = 0;

    tell_player(p, oldstack);
  }

  stack = oldstack;
}

/* staff list multis, only lists those multis that are in use */
void list_all_in_use(player * p)
{
  multi *mscan = all_multis;
  char *oldstack = stack;
  int inuse[32];
  int i, j, any = 0;

  for (i = 0; i < 32; i++)
    inuse[i] = 0;

  while (mscan)
  {
    any++;
    inuse[get_number(mscan->number)] |= (2 << get_bit(mscan->number));
    mscan = mscan->next_multi;
  }

  if (!any)
    tell_player(p, " There are no multis currently in use\n");
  else
  {
    sprintf(stack, " There are currently %d multis in use: ", any);
    stack = strchr(stack, 0);

    for (i = 0; i < 32; i++)
      for (j = 0; j < 32; j++)
	if (inuse[i] & 2 << j)
	{
	  sprintf(stack, "%d, ", get_real_number(i, j));
	  stack = strchr(stack, 0);
	}

    stack--;
    stack--;
    *stack++ = '.';
    *stack++ = '\n';
    *stack++ = 0;

    tell_player(p, oldstack);
  }

  stack = oldstack;
}

/*
 * list multis / multi
 * -without parameters list all multis user is on
 * -with parameter list members of the multi
 * -for LOWER_ADMIN and higher, list also all multis in use, if used without
 * parameters
 */
void multi_list(player * p, char *str)
{
  char *endp, pnames[1000];
  int num;
  multi *m;

  if (*str)
  {
    num = (int) strtol(str, &endp, 10);
    if (*endp)
    {
      tell_player(p, "Multi number not correct\n");
      return;
    }

    m = find_multi_from_number(num);
    if (!m)
    {
      tell_player(p, "No such multi exists\n");
      return;
    }

    if (!(player_on_multi(p, m)))
    {
      tell_player(p, "You're not on that multi\n");
      return;
    }

    if (m->multi_flags & MULTI_FRIENDSLIST)
    {
      if (m->players_list && m->players_list->the_player)
      {
	if (m->players_list->the_player == p)
	{
	  pnames[0] = 0;
	  multi_number_to_names(NULL, num, pnames);
	  TELLPLAYER(p, "Multi %d: Your friends (%s)\n", num, pnames);
	}
	else
	  TELLPLAYER(p, "Multi %d: Friends of %s\n", num,
		     m->players_list->the_player->name);
      }
      else
	TELLPLAYER(p, "Multi %d has lost its owner\n", num);
    }
    else
    {
      pnames[0] = 0;
      multi_number_to_names(NULL, num, pnames);
      TELLPLAYER(p, "Multi %d: %s\n", num, pnames);
    }
  }
  else
  {
    list_multis_me_on(p);
    if (p->residency & LOWER_ADMIN)
      list_all_in_use(p);
  }
}

void multi_idle(player * p, char *str)
{
  char *endp;
  int num;
  multi *m;

  if (!(*str))
  {
    tell_player(p, " Format: idle_multi <number of multi>\n");
    return;
  }

  num = (int) strtol(str, &endp, 10);
  if (*endp)
  {
    tell_player(p, " Multi number not correct\n");
    return;
  }

  m = find_multi_from_number(num);
  if (!m)
  {
    tell_player(p, " No such multi in existence\n");
    return;
  }

  if (!(player_on_multi(p, m)))
  {
    tell_player(p, " You're not on that multi\n");
    return;
  }

  TELLPLAYER(p, " That multi has been idle for %s\n", word_time(m->multi_idle));
}

void multi_kill(player *p, char *str)
{
  multi *the_multi = NULL, *mscan = NULL, *mprev = NULL;
  int i;
  char *c = NULL, reason[80];

  if (!(*str))
    i = find_friend_multi_number_name(p->name);
  else if (!(p->residency & ADMIN))
  {
    tell_player(p, " Format: kill_multi <multi number>\n");
    return;
  }
  else
  {
    i = (int) strtol(str, &c, 10);
    if (i < 1 || i > 1024 || (c && *c))
    {
      tell_player(p, " That is not a legal multi number.\n");
      return;
    }
  }

  the_multi = find_multi_from_number(i);
  if (!the_multi)
  {
    if (*str)
      tell_player(p, " That multi doesn't even exist!\n");
    else
      tell_player(p, " You don't have a friends multi to kill.\n");
    return;
  }
  mscan = all_multis;
  while (mscan != the_multi)
  {
    mprev = mscan;
    mscan = mscan->next_multi;
  }
  strcpy(reason, "");
  if (*str)
    sprintf(reason, " by %s", p->name);
  else
    sprintf(reason, " by owner");
  (void) remove_multi_player(p, mscan, mprev, reason);
  tell_player(p, " You've removed the multi\n");
}

void multi_remove(player *p, char *str)
{
  int i;
  multi *the_multi = NULL;
  char *c;

  if (!str || !*str)
  {
    tell_player(p, " Format: rm_multi <number of multi>\n");
    return;
  }

  i = (int) strtol(str, &c, 10);
  if (i < 1 || i > 1024 || (c && *c))
  {
    tell_player(p, " That is not a legal multi number.\n");
    return;
  }

  the_multi = find_multi_from_number(i);
  if (!the_multi)
  {
    tell_player(p, " You aren't even on that multi!\n");
    return;
  }

  if (!player_on_multi(p, the_multi))
  {
    tell_player(p, " You aren't even on that multi!\n");
    return;
  }

  remove_from_multi(p, the_multi);
  vtell_multi(i, NULL, "%s removes %s from the multi\n", p->name, self_string(p));
  tell_player(p, " You removed yourself from the multi.\n");
}


void multis_version(void)
{
  sprintf(stack, " -=*> PG+ Multis/Chains v%s (by Segtor) enabled.\n", MULTI_VERSION);
  stack = strchr(stack, 0);
}

#endif