回精華區
作者Iuu.bbs@bupt.cn-bbs.org (麥籽) 看板: plan
標題[功能]殺人遊戲(警察與殺手)
時間真情流露 (Wed, 07 May 2003 13:20:23 +0800 (CST)) Updated: 2004/10/30
    不知道大家是否玩過警察與殺手的遊戲,大概說起來,
是一個多人的鬥智遊戲。可以通過使用bbs的聊天室來完成,
當然,多個人聚在一起現場遊戲也很有氣氛。

    下面是需要改動的代碼,好在改動的文件并不多,并且
所有代碼都是在 #ifdef HAVE_KILL_GAME #endif 中的。后
面會粘貼相關的遊戲規則。需改動的文件有:
config.h  chatd.c xchatd.c
    coded by esp @ 真情流露

: config.h

#define LOG_TALK                /* lkchu.981201: 聊天記錄處理 */

+ #define HAVE_KILL_GAME        /* esp.030414: 是否提供殺手遊戲 */

: chat.c:chat_recv()

      else if (fd == 't')
      {
        str_ncpy(chatopic, str, sizeof(chatopic));
        chat_topic();
      }
#ifdef HAVE_KILL_GAME
      else if (fd == 'k') /* esp: only send /k on entering a kill-game room */
      {
        char temp[10];

        for (fd = 2; fd < stop_line; fd++)
        {
          move(fd,0);
          clrtoeol();
        }
        sscanf(str, "%s", temp);
        chatline = atoi(temp);
        move(chatline, 0);
      }
      else if (fd == 'u')    /* esp: refresh the top user table */
      {
        char temp[10];

        sscanf(str, "%s", temp);
        fd = atoi(temp);
        str = strchr(str, ' ') + 1;
        cursor_save();
        move(fd, 0);
        outs(str);
        cursor_restore();
      }
#endif

: xchatd.c

  在 maple/xchatd.c 中增加的代碼數量比較多,也是全部
定義在 #define HAVE_KILL_GAME #endif 中的。
  對 xchatd.c 中增加的代碼有28處,一一列出來,請搜索
#define HAVE_KILL_GAME,把這部分的代碼加到自己的 xchatd.c 中。
  注意,如果直接下載的話,有些代碼因為超過了80字,所
以bbs自動做了回行,這部分使用的時候,要修正。

/*-------------------------------------------------------*/
/* xchatd.c     ( NTHU CS MapleBBS Ver 3.00 )            */
/*-------------------------------------------------------*/
/* target : super KTV daemon for chat server             */
/* create : 95/03/29                                     */
/* update : 97/10/20                                     */
/*-------------------------------------------------------*/


#include "bbs.h"
#include "xchat.h"


#ifdef HAVE_KILL_GAME
typedef struct
{
  int won;
  int lost;
  int dropped;
  int nreserved;
  int lastgame;   /* win or lose or drop */
  time_t tlastgame;
  time_t treserved;
} KILL_STATE;
#endif



#include <stdlib.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <fcntl.h>

#if defined(AIX) || defined(HIGHGCC)
#include <sys/time.h>
#else
#include <time.h>
#endif          /* hightman.011028: Redhat7.x有點怪 :) */

#include <sys/resource.h>


#ifdef SOLARIS
extern char *sys_errlist[];
#endif


#define SERVER_USAGE
#define WATCH_DOG
#undef  DEBUG           /* 程式除錯之用 */
#undef  MONITOR         /* 監督 chatroom 活動以解決糾紛 */
#undef  STAND_ALONE     /* 不搭配 BBS 獨立執行 */


#ifdef  DEBUG
#define MONITOR
#endif

static int gline;

#ifdef  WATCH_DOG
#define MYDOG  gline = __LINE__
#else
#define MYDOG           /* NOOP */
#endif


#define CHAT_PIDFILE    "run/chat.pid"
#define CHAT_LOGFILE    "run/chat.log"
#define CHAT_INTERVAL   (60 * 30)
#define SOCK_QLEN   3


/* name of the main room (always exists) */


#define MAIN_NAME   "main"
#define MAIN_TOPIC  "萍水相逢自有緣"


#define ROOM_LOCKED 0x01
#define ROOM_SECRET 0x02
#define ROOM_OPENTOPIC  0x04
/* fuse.991210: 沒有動作,沒有系統訊息的聊天室 */
#define ROOM_QUIET  0x08
#define ROOM_INTERVIEW  0x10

/* esp.030414: 殺人遊戲 */
#ifdef HAVE_KILL_GAME
#define ROOM_KILLGAME   0x20
#define ROOM_KILL_STARTED 0x40
#define ROOM_KILL_WAITDROP 0x80
#define ROOM_KILL_DISCUSS 0x100
#define ROOM_KILL_NOGHOST 0x200
#define ROOM_KILL_POLICE 0x400
#define ROOM_KILL_NOPOLICE 0x800
#define ROOM_KILL_STRPOLICE 0x1000
#endif


#define ROOM_ALL    (NULL)


#define LOCKED(room)    (room->rflag & ROOM_LOCKED)
#define SECRET(room)    (room->rflag & ROOM_SECRET)
#define OPENTOPIC(room) (room->rflag & ROOM_OPENTOPIC)
#define QUIET(room) (room->rflag & ROOM_QUIET)
#define INTERVIEW(room) (room->rflag & ROOM_INTERVIEW)


#define RESTRICTED(usr) (usr->uflag == 0)   /* guest */
#define CHATSYSOP(usr)  (usr->uflag & (PERM_SYSOP | PERM_CHATROOM))
#define PERM_ROOMOP PERM_CHAT   /* Thor: 借 PERM_CHAT為 PERM_ROOMOP */
#define PERM_CHATOP PERM_DENYCHAT   /* Thor: 借 PERM_DENYCHAT為 PERM_CHATOP */
/* #define ROOMOP(usr)  (usr->uflag & ( PERM_ROOMOP | PERM_SYSOP | PERM_CHATROO
M)) */
/* Thor.980603: PERM_CHATROOM改為 default 沒有 roomop, 但可以自己取得 chatop*/
#define ROOMOP(usr)  (usr->uflag & (PERM_ROOMOP|PERM_CHATOP))
#define CLOAK(usr)  (usr->uflag & PERM_CLOAK)

#ifdef HAVE_KILL_GAME

#define KILL_ROLE_MASK 0x0F

#define KILL_NONE 0
#define KILL_JUDGE 1
#define KILL_POLICE 2
#define KILL_KILLER 3
#define KILL_NORMAL 4

#define KILL_ROLE(x) (x->killflag & KILL_ROLE_MASK)

#define KILL_KILLED 0x10
#define KILL_KNOCKOUT 0x20
#define KILL_VOTED 0x40
#define KILL_TALKED 0x80 // esp: use it for talk & look around
#define KILL_DROPED 0x100 // esp: for reconnect...
#define KILL_QUERYED 0x200 // esp: police use this for done,killer use this for
 having been queryed

/* kill game status */

#define KILL_NOT_START 0
#define KILL_NIGHT_KILLER 1
#define KILL_NIGHT_POLICE 2
#define KILL_DAY_MURDERED 3
#define KILL_DAY_TALK 4
#define KILL_DAY_VOTE 5
#define KILL_AFTERDAY 6
#define KILL_BEFORENIGHT 7

/* kill user states */
#define KILL_USER_UNKNOWN 0
#define KILL_USER_KILLER 1
#define KILL_USER_KILLED_KILLER 2
#define KILL_USER_POLICE 3
#define KILL_USER_KILLED_POLICE 4
#define KILL_USER_MISKILLED_POLICE 5
#define KILL_USER_NORMAL 6
#define KILL_USER_KILLED_NORMAL 7
#define KILL_USER_MISKILLED_NORMAL 8
#define KILL_USER_JUDGE 9
#define KILL_USER_JUSTKILLED 10

/* states only for the judge */
#define KILL_JUDGE_FRESH 0
#define KILL_JUDGE_KILLED 1 /* esp: prevent judge from kill more at one round o
r no kill at all */

static char userkillstates[11][11] =
{
  "目前未知",
  "殺手",
  "被殺殺手",
  "警察",
  "被殺警察",
  "被冤殺警察",
  "平民",
  "被殺平民",
  "被冤殺平民",
  "法官",
  "被殺"
};

static char userkillstatecolors[11][10] = {
  "\033[37m",
  "\033[1;31m",
  "\033[4;31m",
  "\033[1;32m",
  "\033[4;32m",
  "\033[1;4;32m",
  "\033[1;33m",
  "\033[4;33m",
  "\033[1;4;33m",
  "\033[30;47m",
  "\033[4m"
};
#endif

/* ----------------------------------------------------- */
/* ChatRoom data structure                               */
/* ----------------------------------------------------- */


typedef struct ChatRoom ChatRoom;
typedef struct ChatUser ChatUser;
typedef struct UserList UserList;
typedef struct ChatCmd ChatCmd;
typedef struct ChatAction ChatAction;


struct ChatUser
{
  ChatUser *unext;
  ChatRoom *room;
  UserList *ignore;
  int sock;         /* user socket */
  int userno;
  int uflag;
  int clitype;          /* Xshadow: client type. 1 for common client,
                 * 0 for bbs only client */
#ifdef HAVE_KILL_GAME
  int killflag;
  int killstate;
  int votenum;
  char killid[16];
  time_t tkillbegin;
#endif

  time_t tbegin;
  time_t uptime;
  int sno;
  int xdata;
  int retry;

  int isize;            /* current size of ibuf */
  char ibuf[80];        /* buffer for non-blocking receiving */
  char userid[IDLEN + 1];   /* real userid */
  char chatid[9];       /* chat id */
  char rhost[30];       /* host address */
};


struct ChatRoom
{
  ChatRoom *next, *prev;
  UserList *invite;
  char name[IDLEN];
  char topic[48];       /* Let the room op to define room topic */
  int rflag;            /* ROOM_LOCKED, ROOM_SECRET, ROOM_OPENTOPIC */
#ifdef HAVE_KILL_GAME
  int killstatus;
  /* esp: the judge always only one */
  int policenum;
  int killernum;
  int normalnum;
  ChatUser *currenttalker;
  time_t tvotebegin;
#endif
  int occupants;        /* number of users in room */
};


struct UserList
{
  UserList *next;
  int userno;
  char userid[0];
};


struct ChatCmd
{
  char *cmdstr;
  void (*cmdfunc) ();
  int exact;
};


static ChatRoom mainroom, *roompool;
static ChatUser *mainuser, *userpool;
static fd_set mainfset;
static int totaluser;       /* current number of connections */
static struct timeval zerotv;   /* timeval for selecting */
static int common_client_command;

/* fuse.991211:被采訪人 */
static char interviewer[20];

#ifdef STAND_ALONE
static int userno_inc = 0;  /* userno auto-incrementer */
#endif


static char msg_not_op[] = "◆ 您不是這間聊天室的 Op";
static char msg_no_such_id[] = "◆ 目前沒有人使用 [%s] 這個聊天代號";
static char msg_not_here[] = "◆ [%s] 不在這間聊天室";


#define FUZZY_USER  ((ChatUser *) -1)


#ifdef HAVE_KILL_GAME

/* esp: kill game functions */

static void kill_start(ChatUser* cu, char* msg);
static void kill_end(ChatUser* cu, char* msg);
static void kill_parse_start_options(ChatRoom *room, char* msg);
// static void kill_sunrise(ChatUser *cu, char* msg);
// static void kill_sunset(ChatUser *cu, char* msg);
// static void kill_vote(ChatUser *cu, char* msg);
// static void kill_user_vote(ChatUser *cu, char* msg);
// static void kill_clear_vote(ChatUser *cu, char* msg);
static void kill_kill(ChatUser *cu, char* msg);
static int kill_can_talk_between(ChatRoom *room, ChatUser *cu, ChatUser *xuser)
;
static int kill_can_talk(ChatRoom *room, ChatUser *user );
static int kill_get_user_state(ChatRoom *room, ChatUser *cu, ChatUser *user);
static void kill_refresh_table(ChatRoom* room);
static void kill_over(ChatUser *cu, char *msg);
// static void kill_killer_over(ChatRoom *room, ChatUser *cu);
// static void kill_police_over(ChatRoom *room, ChatUser *cu);
static void kill_talk_over(ChatRoom *room, ChatUser *cu);
static int kill_on_user_over(ChatRoom *room);
static ChatUser *kill_get_next_user(ChatRoom *room, ChatUser *cu);
static void kill_check_vote(ChatRoom* room, ChatUser* cu);
static void kill_clear_user_state(ChatRoom* room);
static void kill_on_reconnect(ChatRoom* room);
static void kill_police_query(ChatUser *cu, char* msg);
static void kill_show_next_killer(ChatRoom *room);
static void kill_talk_time_check(ChatRoom *room);
static void kill_vote_time_check(ChatRoom *room);
static ChatUser* kill_auto_vote(ChatRoom *room);
static void kill_kkk(ChatUser *user,char* msg);
static void kill_log_state(ChatUser *user, int nMode);
static void kill_show_state(ChatUser *cu, char *msg);

#endif

/* ----------------------------------------------------- */
/* operation log and debug information                   */
/* ----------------------------------------------------- */


static FILE *flog;


/* Thor.990211: 統一用 dao library */
#define str_time(t) Btime(t)

#if 0
static char *
str_time(t)
  time_t *t;
{
  static char buf[20];
  struct tm *p;

  p = localtime(t);
  sprintf(buf, "%02d/%02d %02d:%02d:%02d",
    p->tm_mon + 1, p->tm_mday,
    p->tm_hour, p->tm_min, p->tm_sec);
  return buf;
}

#endif

static void
logit(key, msg)
  char *key;
  char *msg;
{
  time_t now;
  struct tm *p;

  time(&now);
  p = localtime(&now);
  fprintf(flog, "%02d/%02d %02d:%02d:%02d %-13s%s\n",
    p->tm_mon + 1, p->tm_mday,
    p->tm_hour, p->tm_min, p->tm_sec, key, msg);
}


static inline void
log_init()
{
  FILE *fp;

  /* --------------------------------------------------- */
  /* log daemon's PID                    */
  /* --------------------------------------------------- */

  if (fp = fopen(CHAT_PIDFILE, "w"))
  {
    fprintf(fp, "%d\n", getpid());
    fclose(fp);
  }

  flog = fopen(CHAT_LOGFILE, "a");
  logit("START", "chat daemon");
}


#ifdef  DEBUG
static char chatbuf[256];   /* general purpose buffer */


static void
debug_list(list)
  UserList *list;
{
  char buf[80];
  int i = 0;

  if (!list)
  {
    logit("DEBUG_L", "NULL");
    return;
  }
  while (list)
  {
    sprintf(buf, "%d) list: %p userno: %d next: %p", i++, list, list->userno, l
ist->next);
    logit("DEBUG_L", buf);

    list = list->next;
  }
  logit("DEBUG_L", "end");
}


static void
debug_user()
{
  ChatUser *user;
  int i;
  char buf[80];

  sprintf(buf, "mainuser: %p userpool: %p", mainuser, userpool);
  logit("DEBUG_U", buf);
  for (i = 0, user = mainuser; user; user = user->unext)
  {
    /* MYDOG; */
    sprintf(buf, "%d) %p %-6d %s %s", ++i, user, user->userno, user->userid, us
er->chatid);
    logit("DEBUG_U", buf);
  }
}


static void
debug_room()
{
  ChatRoom *room;
  int i;
  char buf[80];

  i = 0;
  room = &mainroom;

  sprintf(buf, "mainroom: %p roompool: %p", mainroom, roompool);
  logit("DEBUG_R", buf);
  do
  {
    MYDOG;
    sprintf(buf, "%d) %p %s %d", ++i, room, room->name, room->occupants);
    logit("DEBUG_R", buf);
  } while (room = room->next);
}


static void
log_user(cu)
  ChatUser *cu;
{
  static int log_num;

  if (cu)
  {
    if (log_num > 100 && log_num < 150)
    {
      sprintf(chatbuf, "%d: %p <%d>", log_num, cu, gline);
      logit("travese user ", chatbuf);
    }
    else if (log_num == 100)
    {
      sprintf(chatbuf, "BOOM !! at line %d", gline);
      logit("travese user ", chatbuf);
    }
    log_num++;
  }
  else
    log_num = 0;
}
#endif              /* DEBUG */


/* ----------------------------------------------------- */
/* string routines                                       */
/* ----------------------------------------------------- */


static int
valid_chatid(id)
  char *id;
{
  int ch, len;

  for (len = 0; ch = *id; id++)
  { /* Thor.980921: 空白為不合理chatid, 怕getnext判斷錯誤等等 */
    if (ch == '/' || ch == '*' || ch == ':' || ch ==' ')
      return 0;
    if (++len > 8)
      return 0;
  }
  return len;
}


#if 0
static void
str_lower(dst, src)
  char *dst, *src;
{
  int ch;

  do
  {
    ch = *src++;
    if (ch >= 'A' && ch <= 'Z')
      ch += 0x20;
    *dst++ = ch;
  } while (ch);
}


static void
str_ncpy(dst, src, n)
  char *dst;
  char *src;
  int n;
{
  char *end;

  end = dst + n;

  do
  {
    n = (dst == end) ? 0 : *src++;
    *dst++ = n;
  } while (n);
}
#endif


/* Case Independent strcmp : 1 ==> euqal */


/* Thor.990211: 統一使用dao library */
#define str_equal(s1,s2) (!str_cmp(s1,s2))
#if 0
static int
str_equal(s1, s2)
  uschar *s1;       /* lower-case (sub)string */
  uschar *s2;
{
  int c1, c2;

  for (;;)
  {
    c1 = *s1;

    c2 = *s2;
    if (c2 >= 'A' && c2 <= 'Z')
      c2 += 32;

    if (c1 != c2)
      return 0;

    if (!c1)
      return 1;

    s1++;
    s2++;
  }
}

#endif

/* ----------------------------------------------------- */
/* match strings' similarity case-insensitively          */
/* ----------------------------------------------------- */
/* str_match(keyword, string)                */
/* ----------------------------------------------------- */
/* 0 : equal            ("foo", "foo")                   */
/* -1 : mismatch        ("abc", "xyz")                   */
/* ow : similar         ("goo", "good")                  */
/* ----------------------------------------------------- */


static int
str_match(s1, s2)
  uschar *s1;       /* lower-case (sub)string */
  uschar *s2;
{
  int c1, c2;

  for (;;)
  {
    c1 = *s1;
    c2 = *s2;

    if (!c1)
      return c2;

    if (c2 >= 'A' && c2 <= 'Z')
      c2 += 32;

    if (c1 != c2)
      return -1;

    s1++;
    s2++;
  }
}


/* ----------------------------------------------------- */
/* search user/room by its ID                            */
/* ----------------------------------------------------- */


static ChatUser *
cuser_by_userid(userid)
  char *userid;
{
  ChatUser *cu;
  char buf[80]; /* Thor.980727: 一次最長才80 */

  str_lower(buf, userid);
  for (cu = mainuser; cu; cu = cu->unext)
  {
    if (!cu->userno)
      continue;
    if (str_equal(buf, cu->userid))
      break;
  }
  return cu;
}


static ChatUser *
cuser_by_chatid(chatid)
  char *chatid;
{
  ChatUser *cu;
  char buf[80]; /* Thor.980727: 一次最長才80 */

  str_lower(buf, chatid);

  for (cu = mainuser; cu; cu = cu->unext)
  {
    if (!cu->userno)
      continue;
    if (str_equal(buf, cu->chatid))
      break;
  }
  return cu;
}


static ChatUser *
fuzzy_cuser_by_chatid(chatid)
  char *chatid;
{
  ChatUser *cu, *xuser;
  int mode;
  char buf[80]; /* Thor.980727: 一次最長才80 */

  str_lower(buf, chatid);
  xuser = NULL;

  for (cu = mainuser; cu; cu = cu->unext)
  {
    if (!cu->userno)
      continue;

    mode = str_match(buf, cu->chatid);
    if (mode == 0)
      return cu;

    if (mode > 0)
    {
      if (xuser)
    return FUZZY_USER;  /* 符合者大於 2 人 */

      xuser = cu;
    }
  }
  return xuser;
}


static ChatRoom *
croom_by_roomid(roomid)
  char *roomid;
{
  ChatRoom *room;
  char buf[80]; /* Thor.980727: 一次最長才80 */

  str_lower(buf, roomid);
  room = &mainroom;
  do
  {
    if (str_equal(buf, room->name))
      break;
  } while (room = room->next);
  return room;
}


/* ----------------------------------------------------- */
/* UserList routines                                     */
/* ----------------------------------------------------- */


static void
list_free(list)
  UserList **list;
{
  UserList *user, *next;

  for (user = *list, *list = NULL; user; user = next)
  {
    next = user->next;
    free(user);
  }
}


static void
list_add(list, user)
  UserList **list;
  ChatUser *user;
{
  UserList *node;
  char *userid;
  int len;

  len = strlen(userid = user->userid) + 1;
  if (node = (UserList *) malloc(sizeof(UserList) + len))
  {
    node->next = *list;
    node->userno = user->userno;
    memcpy(node->userid, userid, len);
    *list = node;
  }
}


static int
list_delete(list, userid)
  UserList **list;
  char *userid;
{
  UserList *node;
  char buf[80]; /* Thor.980727: 輸入一次最長才 80 */

  str_lower(buf, userid);

  while (node = *list)
  {
    if (str_equal(buf, node->userid))
    {
      *list = node->next;
      free(node);
      return 1;
    }
    list = &node->next;
  }

  return 0;
}


static int
list_belong(list, userno)
  UserList *list;
  int userno;
{
  while (list)
  {
    if (userno == list->userno)
      return 1;
    list = list->next;
  }
  return 0;
}


/* ------------------------------------------------------ */
/* non-blocking socket routines : send message to users   */
/* ------------------------------------------------------ */


static void
do_send(nfds, wset, msg)
  int nfds;
  fd_set *wset;
  char *msg;
{
  int len, sr;

#if 1
  /* Thor: for future reservation bug */
  zerotv.tv_sec = 0;
  zerotv.tv_usec = 0;
#endif

  sr = select(nfds + 1, NULL, wset, NULL, &zerotv);

  if (sr > 0)
  {
    len = strlen(msg) + 1;
    do
    {
      if (FD_ISSET(nfds, wset))
      {
    send(nfds, msg, len, 0);
    if (--sr <= 0)
      return;
      }
    } while (--nfds > 0);
  }
}


static void
send_to_room(room, msg, userno, number, fromuser)
  ChatRoom *room;
  char *msg;
  int userno;
  int number;
  ChatUser *fromuser;
{
  ChatUser *cu;
  fd_set wset;
  int sock, max;
  int clitype;          /* 分為 bbs client 及 common client 兩次處理 */
  char *str, buf[256];

  for (clitype = (number == MSG_MESSAGE || !number) ? 0 : 1;
    clitype < 2; clitype++)
  {
    FD_ZERO(&wset);
    max = -1;

    for (cu = mainuser; cu; cu = cu->unext)
    {
      if (cu->userno && (cu->clitype == clitype) &&
    (room == ROOM_ALL || room == cu->room) &&
    (!userno || !list_belong(cu->ignore, userno)))
      {
#ifdef HAVE_KILL_GAME
        if(fromuser != NULL)
        {
          if(!kill_can_talk_between(room, fromuser,cu))
            continue;
        }
#endif
    sock = cu->sock;

    FD_SET(sock, &wset);

    if (max < sock)
      max = sock;
      }
    }

    if (max <= 0)
      continue;

    if (clitype)
    {
      str = buf;

      if (*msg)
    sprintf(str, "%3d %s", number, msg);
      else
    sprintf(str, "%3d", number);
    }
    else
    {
      str = msg;
    }

    do_send(max, &wset, str);
  }
}


static void
send_to_user(user, msg, userno, number)
  ChatUser *user;
  char *msg;
  int userno;
  int number;
{
  int sock;

#if 0
  if (!user->userno || (!user->clitype && number && number != MSG_MESSAGE))
#endif
  /* Thor.980911: 如果查user->userno則在login_user的error message會無法送回 */
  if (!user->clitype && number != MSG_MESSAGE)
    return;


  if ((sock = user->sock) <= 0)
    return;

  if (!userno || !list_belong(user->ignore, userno))
  {
    fd_set wset;
    char buf[256];

    FD_ZERO(&wset);
    FD_SET(sock, &wset);

    if (user->clitype)
    {
      if (*msg)
    sprintf(buf, "%3d %s", number, msg);
      else
    sprintf(buf, "%3d", number);
      msg = buf;
    }

    do_send(sock, &wset, msg);
  }
}


/* ----------------------------------------------------- */


static void
room_changed(room)
  ChatRoom *room;
{
  if (room)
  {
    char buf[256];

    sprintf(buf, "= %s %d %d %s",
      room->name, room->occupants, room->rflag, room->topic);
    send_to_room(ROOM_ALL, buf, 0, MSG_ROOMNOTIFY,NULL);
  }
}


static void
user_changed(cu)
  ChatUser *cu;
{
  if (cu)
  {
    ChatRoom *room;
    char buf[256];

    room = cu->room;
    sprintf(buf, "= %s %s %s %s%s",
      cu->userid, cu->chatid, room->name, cu->rhost,
      ROOMOP(cu) ? " Op" : "");
    send_to_room(room, buf, 0, MSG_USERNOTIFY,NULL);
  }
}


static void
exit_room(user, mode, msg)
  ChatUser *user;
  int mode;
  char *msg;
{
  ChatRoom *room;
  usint uflag;
  char buf[128];

  if (!(room = user->room))
    return;

  uflag = user->uflag;
  user->room = NULL;
  /* user->uflag &= ~(PERM_ROOMOP | PERM_SYSOP | PERM_CHATROOM); */
  user->uflag &= ~PERM_ROOMOP;
  /* Thor.980601: 離開房間時只清 room op, 不清 sysop,chatroom,因天生具有 */

  if (--room->occupants > 0)
  {
    char *chatid;

    chatid = user->chatid;
    switch (mode)
    {
    case EXIT_LOGOUT:

      sprintf(buf, "◆ %s 離開了 ...", chatid);
      if (msg && *msg)
      {
    strcat(buf, ": ");

#if 1
    msg[79] = 0;        /* Thor:防止太長 */
#endif

    strncat(buf, msg, 79);
      }
      break;

    case EXIT_LOSTCONN:

      sprintf(buf, "◆ %s 成了斷線的風箏囉", chatid);
      break;

    case EXIT_KICK:

      sprintf(buf, "◆ 哈哈!%s 被踢出去了", chatid);
      break;
    }

    /* fuse.991210: QUIET模式下不顯示離開訊息 */
    if ((!CLOAK(user)) && (!INTERVIEW(room)))
      send_to_room(room, buf, 0, MSG_MESSAGE,NULL);

    sprintf(buf, "- %s", user->userid);
    send_to_room(room, buf, 0, MSG_USERNOTIFY,NULL);
    room_changed(room);
#ifdef HAVE_KILL_GAME
    if(room->rflag & ROOM_KILL_STARTED)
    {
      if (mode == EXIT_LOSTCONN || mode == EXIT_LOGOUT)
        kill_log_state(user, 3); // 3 for DROP

      send_to_room(room,"\033[1;31m唉,沒辦法,重新開始吧。\033[m", 0, MSG_MESSAGE, NULL);
      send_to_room(room,"\033[1;33m$$         這一輪的遊戲被提前結束了\033[m", 0, MSG_MESSAGE, NULL);

      room->rflag &= ~ROOM_KILL_STARTED;
      room->killstatus = KILL_NOT_START;
      kill_refresh_table(room);

      kill_clear_user_state(room);
    }
#endif
  }
  else if (room != &mainroom)
  {
    ChatRoom *next;

    fprintf(flog, "room-\t[%d] %s\n", user->sno, room->name);
    sprintf(buf, "- %s", room->name);

    room->prev->next = next = room->next;
    if (next)
      next->prev = room->prev;

    list_free(&room->invite);

    /* free(room); */

    /* 回收 */
    room->next = roompool;
    roompool = room;

    send_to_room(ROOM_ALL, buf, 0, MSG_ROOMNOTIFY,NULL);
  }
}


/* ----------------------------------------------------- */
/* chat commands                                         */
/* ----------------------------------------------------- */


#ifndef STAND_ALONE
/* ----------------------------------------------------- */
/* BBS server side routines                              */
/* ----------------------------------------------------- */


/* static */
int
acct_load(acct, userid)
  ACCT *acct;
  char *userid;
{
  int fd;

  usr_fpath((char *) acct, userid, FN_ACCT);
  fd = open((char *) acct, O_RDONLY);
  if (fd >= 0)
  {
    read(fd, acct, sizeof(ACCT));
    close(fd);
  }
  return fd;
}


static void
chat_query(cu, msg)
  ChatUser *cu;
  char *msg;
{
  FILE *fp;
  ACCT acct;
  char buf[256];

  /* Thor.980617: 可先查是否為空字串 */
  if (*msg && acct_load(&acct, msg) >= 0)
  {
    sprintf(buf, "%s(%s) 共上站 %d 次,文章 %d 篇",
      acct.userid, acct.username, acct.numlogins, acct.numposts);
    send_to_user(cu, buf, 0, MSG_MESSAGE);

    sprintf(buf, "最近(%s)從(%s)上站", Btime(&acct.lastlogin),
      (acct.lasthost[0] ? acct.lasthost : "外太空"));
    send_to_user(cu, buf, 0, MSG_MESSAGE);

    usr_fpath(buf, acct.userid, "plans");
    if (fp = fopen(buf, "r"))
    {
      int i;

      i = 0;
      while (fgets(buf, 255, fp) && buf[0])
      {
    buf[strlen(buf) - 1] = 0;
    send_to_user(cu, buf, 0, MSG_MESSAGE);
    if (++i >= MAXQUERYLINES)
      break;
      }
      fclose(fp);
    }
  }
  else
  {
    sprintf(buf, msg_no_such_id, msg);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
  }
}
#endif


static void
chat_clear(cu, msg)
  ChatUser *cu;
  char *msg;
{
  if (cu->clitype)
    send_to_user(cu, "", 0, MSG_CLRSCR);
  else
    send_to_user(cu, "/c", 0, MSG_MESSAGE);
}


static void
chat_date(cu, msg)
  ChatUser *cu;
  char *msg;
{
  time_t thetime;
  char buf[128];

  time(&thetime);
  sprintf(buf, "◆ 標準時間: %s", Btime(&thetime));
  send_to_user(cu, buf, 0, MSG_MESSAGE);
}


static void
chat_topic(cu, msg)
  ChatUser *cu;
  char *msg;
{
  ChatRoom *room;
  char *topic, buf[128];

  room = cu->room;

  if (!ROOMOP(cu) && !OPENTOPIC(room))
  {
    send_to_user(cu, msg_not_op, 0, MSG_MESSAGE);
    return;
  }

  if (*msg == '\0')
  {
    send_to_user(cu, "※ 請指定話題", 0, MSG_MESSAGE);
    return;
  }

  topic = room->topic;
  /* str_ncpy(topic, msg, sizeof(room->topic) - 1); */
  str_ncpy(topic, msg, sizeof(room->topic));
  /* Thor.980921: str_ncpy 已含 0之空間 */
#ifdef HAVE_KILL_GAME
  if (room->rflag & ROOM_KILLGAME)
    strcat(topic, "K");
#endif

  if (cu->clitype)
  {
    send_to_room(room, topic, 0, MSG_TOPIC,NULL);
  }
  else
  {
    sprintf(buf, "/t%s", topic);
    send_to_room(room, buf, 0, MSG_MESSAGE,NULL);
  }

  room_changed(room);

  if (!CLOAK(cu))
  {
    sprintf(buf, "◆ %s 將話題改為 %s", cu->chatid, topic);
    send_to_room(room, buf, 0, MSG_MESSAGE,NULL);
  }
}


static void
chat_version(cu, msg)
  ChatUser *cu;
  char *msg;
{
  char buf[80];

  sprintf(buf, "%d %d", XCHAT_VERSION_MAJOR, XCHAT_VERSION_MINOR);
  send_to_user(cu, buf, 0, MSG_VERSION);
}


static void
chat_nick(cu, msg)
  ChatUser *cu;
  char *msg;
{
  char *chatid, *str, buf[128];
  ChatUser *xuser;

#ifdef HAVE_KILL_GAME
  if(cu->room->rflag & ROOM_KILL_STARTED)
  {
    send_to_user(cu, "※ 殺手遊戲進行中,不能更改ID", 0, MSG_MESSAGE);
    return;
  }
#endif

  chatid = nextword(&msg);
  chatid[8] = '\0';
  if (!valid_chatid(chatid))
  {
    send_to_user(cu, "※ 這個聊天代號是不正確的", 0, MSG_MESSAGE);
    return;
  }

  xuser = cuser_by_chatid(chatid);
  if (xuser != NULL && xuser != cu)
  {
    send_to_user(cu, "※ 已經有人捷足先登囉", 0, MSG_MESSAGE);
    return;
  }

  str = cu->chatid;

  /* fuse.991210: 改代號這種事quiet模式下不要顯示了 */
  if ((!CLOAK(cu)) && (!INTERVIEW(cu->room)))
  {
    sprintf(buf, "※ %s 將聊天代號改為 %s", str, chatid);
    send_to_room(cu->room, buf, cu->userno, MSG_MESSAGE,NULL);
  }

  strcpy(str, chatid);

  user_changed(cu);

  if (cu->clitype)
  {
    send_to_user(cu, chatid, 0, MSG_NICK);
  }
  else
  {
    sprintf(buf, "/n%s", chatid);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
  }
}


static void
chat_list_rooms(cuser, msg)
  ChatUser *cuser;
  char *msg;
{
  ChatRoom *cr, *room;
  char buf[128];
  int mode;

  if (RESTRICTED(cuser))
  {
    send_to_user(cuser, "※ 您沒有權限列出現有的聊天室", 0, MSG_MESSAGE);
    return;
  }

  mode = common_client_command;

  if (mode)
    send_to_user(cuser, "", 0, MSG_ROOMLISTSTART);
  else
    send_to_user(cuser, " 談天室名稱  │人數│話題        ", 0,
      MSG_MESSAGE);

  room = cuser->room;
  cr = &mainroom;

  do
  {
    if ((cr == room) || !SECRET(cr) || CHATSYSOP(cuser))
    {
      if (mode)
      {
    sprintf(buf, "%s %d %d %s",
      cr->name, cr->occupants, cr->rflag, cr->topic);
    send_to_user(cuser, buf, 0, MSG_ROOMLIST);
      }
      else
      {
    sprintf(buf, " %-12s│%4d│%s", cr->name, cr->occupants, cr->topic);
    if (LOCKED(cr))
      strcat(buf, " [鎖住]");
    if (SECRET(cr))
      strcat(buf, " [秘密]");
    if (OPENTOPIC(cr))
      strcat(buf, " [話題]");
    send_to_user(cuser, buf, 0, MSG_MESSAGE);
      }
    }
  } while (cr = cr->next);

  if (mode)
    send_to_user(cuser, "", 0, MSG_ROOMLISTEND);
}


static void
chat_do_user_list(cu, msg, theroom)
  ChatUser *cu;
  char *msg;
  ChatRoom *theroom;
{
  ChatRoom *myroom, *room;
  ChatUser *user;
  int start, stop, curr, mode; /* , uflag; */
  char buf[128];

  curr = 0; /* Thor.980619: initialize curr */
  start = atoi(nextword(&msg));
  stop = atoi(nextword(&msg));

  mode = common_client_command;

  if (mode)
    send_to_user(cu, "", 0, MSG_USERLISTSTART);
  else
    send_to_user(cu, " 聊天代號│使用者代號  │聊天室 ", 0,
      MSG_MESSAGE);

  myroom = cu->room;

  /* Thor.980717: 需要先排除 cu->userno == 0 的狀況嗎? */
  for (user = mainuser; user; user = user->unext)
  {
    /*
    if (!cu->userno)
      continue;
     */ /* Thor.980717: 既然 cu 都空了那還進來幹麼? */
    if (!user->userno)
      continue;

    room = user->room;
    if ((theroom != ROOM_ALL) && (theroom != room))
      continue;

#if 0
    uflag = user->uflag;
    if ((myroom != room) && (!uflag ||
    (room && SECRET(room) && !(uflag & (PERM_SYSOP | PERM_CHATROOM)))))
      continue;

    if ((uflag & PERM_CLOAK) && (user != cu) &&
      !(uflag & (PERM_SYSOP | PERM_CHATROOM)))
      continue;
#endif
    /* Thor.980717:上述logic有誤 */

    /* Thor.980717: viewer check */
    if ((myroom != room) && (RESTRICTED(cu) ||
                             (room && SECRET(room) && !CHATSYSOP(cu))))
      continue;

    /* Thor.980717: viewee check */
    if (CLOAK(user) && (user != cu) && !CHATSYSOP(cu))
      continue;
    /* Thor.980717:重寫*/


    curr++;
    if (start && curr < start)
      continue;
    else if (stop && (curr > stop))
      break;

    if (mode)
    {
      if (!room)
    continue;       /* Xshadow: 還沒進入任何房間的就不列出 */

      sprintf(buf, "%s %s %s %s",
    user->chatid, user->userid, room->name, user->rhost);
/* Thor.980603: PERM_CHATROOM改為 default 沒有 roomop, 但可以自己取得 */
/*       if (uflag & (PERM_ROOMOP | PERM_SYSOP | PERM_CHATROOM)) */
      if (ROOMOP(user))
    strcat(buf, " Op");
    }
    else
    {
      sprintf(buf, " %-8s│%-12s│%s",
    user->chatid, user->userid, room ? room->name : "[在門口徘徊]");
/* Thor.980603: PERM_CHATROOM改為 default 沒有 roomop, 但可以自己取得 */
/*       if (uflag & (PERM_ROOMOP | PERM_SYSOP | PERM_CHATROOM)) */
      /* if (uflag & (PERM_ROOMOP|PERM_CHATOP)) */
      if (ROOMOP(user))  /* Thor.980602: 統一用法 */
    strcat(buf, " [Op]");
    }

    send_to_user(cu, buf, 0, mode ? MSG_USERLIST : MSG_MESSAGE);
  }

  if (mode)
    send_to_user(cu, "", 0, MSG_USERLISTEND);
}


static void
chat_list_by_room(cu, msg)
  ChatUser *cu;
  char *msg;
{
  ChatRoom *whichroom;
  char *roomstr, buf[128];

  roomstr = nextword(&msg);
  if (!*roomstr)
  {
    whichroom = cu->room;
  }
  else
  {
    if (!(whichroom = croom_by_roomid(roomstr)))
    {
      sprintf(buf, "※ 沒有 [%s] 這個聊天室", roomstr);
      send_to_user(cu, buf, 0, MSG_MESSAGE);
      return;
    }

    if (whichroom != cu->room && SECRET(whichroom) && !CHATSYSOP(cu))
    {
      send_to_user(cu, "※ 無法列出在秘密聊天室的使用者", 0, MSG_MESSAGE);
      return;
    }
  }
  chat_do_user_list(cu, msg, whichroom);
}


static void
chat_list_users(cu, msg)
  ChatUser *cu;
  char *msg;
{
  chat_do_user_list(cu, msg, ROOM_ALL);
}


static void
chat_chatroom(cu, msg)
  ChatUser *cu;
  char *msg;
{
  if (common_client_command)
    send_to_user(cu, "真情流露聊天廣場", 0, MSG_CHATROOM);
}


static void
chat_map_chatids(cu, whichroom)
  ChatUser *cu;         /* Thor: 還沒有作不同間的 */
  ChatRoom *whichroom;
{
  int c;
  ChatRoom *myroom, *room;
  ChatUser *user;
  char buf[128];

  myroom = cu->room;

#ifdef HAVE_KILL_GAME
    if (whichroom != ROOM_ALL && whichroom->rflag & (ROOM_KILLGAME | ROOM_KILL_
STARTED) )
      send_to_user(cu,
    " 聊天代號 角色        │ 聊天代號 角色        │ 聊天代號 角色       ", 0,
 MSG_MESSAGE);
    else
#endif
  send_to_user(cu,
    " 聊天代號 使用者代號  │ 聊天代號 使用者代號  │ 聊天代號 使用者代號 ", 0,
 MSG_MESSAGE);

  for (c = 0, user = mainuser; user; user = user->unext)
  {
    if (!cu->userno)
      continue;

    room = user->room;
    if (whichroom != ROOM_ALL && whichroom != room)
      continue;

    if (myroom != room)
    {
      if (RESTRICTED(cu) || /* Thor: 要先check room 是不是空的 */
    (room && SECRET(room) && !CHATSYSOP(cu)))
    continue;
    }

    if (CLOAK(user) && (user != cu) && !CHATSYSOP(cu))  /* Thor:隱身朮 */
      continue;

#ifdef HAVE_KILL_GAME
    if (whichroom != ROOM_ALL && whichroom->rflag & (ROOM_KILLGAME | ROOM_KILL_STARTED))
      sprintf(buf + (c * 24), " %-8s%c%-12s%s",
        user->chatid, ROOMOP(user) ? '*' : ' ',
        userkillstates[kill_get_user_state(whichroom,cu,user)], (c < 2 ? "│" : "  "));
    else
      sprintf(buf + (c * 24), " %-8s%c%-12s%s",
        user->chatid, ROOMOP(user) ? '*' : ' ',
        user->userid, (c < 2 ? "│" : "  "));
#else
    sprintf(buf + (c * 24), " %-8s%c%-12s%s",
      user->chatid, ROOMOP(user) ? '*' : ' ',
      user->userid, (c < 2 ? "│" : "  "));
#endif

    if (++c == 3)
    {
      send_to_user(cu, buf, 0, MSG_MESSAGE);
      c = 0;
    }
  }

  if (c > 0)
    send_to_user(cu, buf, 0, MSG_MESSAGE);
}


static void
chat_map_chatids_thisroom(cu, msg)
  ChatUser *cu;
  char *msg;
{
  chat_map_chatids(cu, cu->room);
}


static void
chat_setroom(cu, msg)
  ChatUser *cu;
  char *msg;
{
  char *modestr;
  ChatRoom *room;
  char *chatid;
  int sign, flag;
  char *fstr, buf[128];

  if (!ROOMOP(cu))
  {
    send_to_user(cu, msg_not_op, 0, MSG_MESSAGE);
    return;
  }

  modestr = nextword(&msg);
  sign = 1;
  if (*modestr == '+')
  {
    modestr++;
  }
  else if (*modestr == '-')
  {
    modestr++;
    sign = 0;
  }

  if (*modestr == '\0')
  {
    send_to_user(cu,
      "※ 請指定狀態: {[+(設定)][-(取消)]}{[L(鎖住)][s(秘密)][t(開放話題)}", 0,
 MSG_MESSAGE);
    return;
  }

  room = cu->room;
  chatid = cu->chatid;

  while (*modestr)
  {
    flag = 0;
    switch (*modestr)
    {
    case 'l':
    case 'L':
      flag = ROOM_LOCKED;
      fstr = "鎖住";
      break;

    case 's':
    case 'S':
      flag = ROOM_SECRET;
      fstr = "秘密";
      break;

    case 't':
    case 'T':
      flag = ROOM_OPENTOPIC;
      fstr = "開放話題";
      break;

    /* fuse.991210: 清靜聊天室 */
    case 'q':
    case 'Q':
      flag = ROOM_QUIET;
      fstr = "清靜聊天室";
      break;

    case 'i':
    case 'I':
      flag = ROOM_INTERVIEW;
      fstr = "貴賓專訪";
      break;

    default:
      sprintf(buf, "※ 狀態錯誤:[%c]", *modestr);
      send_to_user(cu, buf, 0, MSG_MESSAGE);
    }

    /* Thor: check room 是不是空的, 應該不是空的 */

    if (flag && (room->rflag & flag) != sign * flag)
    {
      room->rflag ^= flag;

      if (!CLOAK(cu))
      {
    sprintf(buf, "※ 本聊天室被 %s %s [%s] 狀態",
      chatid, sign ? "設定為" : "取消", fstr);
    send_to_room(room, buf, 0, MSG_MESSAGE,NULL);
      }
    }
    modestr++;
  }

  /* Thor.980602: 不准 Main room 鎖起 or 秘密, 這成何體統?
                  離開的就進不來, 要看也看不到?
                  想要踢人也踢不進 main room, 不會很奇怪嗎? */
  if(str_equal(MAIN_NAME, room->name))
  {
    if(room->rflag & (ROOM_LOCKED | ROOM_SECRET))
    {
        send_to_room(room, "※ 但 真情使者 施了『復原』的魔法", 0, MSG_MESSAGE,
NULL);
        room->rflag &= ~(ROOM_LOCKED|ROOM_SECRET);
    }
  }

  room_changed(room);
}


static char *chat_msg[] =
{
  "[//]help", "MUD-like 社交動詞",
  "[/h]elp op", "談天室管理員專用指令",
#ifdef HAVE_KILL_GAME
  "[/h]elp ki","殺手遊戲專用指令",
#endif
  "[/a]ct <msg>", "做一個動作",
  "[/b]ye [msg]", "道別",
  "[/c]lear  [/d]ate", "清除螢幕  目前時間",
  /*"[/d]ate", "目前時間", *//* Thor: 指令太多 */

#if 0
  "[/f]ire <user> <msg>", "發送熱訊",   /* Thor.0727: 和 flag 沖key */
#endif

  "[/ignore] [user]", "忽略使用者",
  "[/i]nvite [user]", "邀請使用者",
  "[/j]oin <room>", "建立或加入談天室",
  "[/l]ist [start [stop]]", "列出談天室使用者",
  "[/m]sg <id|user> <msg>", "跟 <id> 說悄悄話",
  "[/n]ick <id>", "將談天代號換成 <id>",
  "[/p]ager", "切換呼叫器",
  "[/q]uery <user>", "查詢網友",
  "[/r]oom", "列出一般談天室",
  "[/t]ape", "開關錄音機",
  "[/to] <user>", "對使用者說!",
  "[/unignore] <user>", "取消忽略",

#if 0
  "[/u]sers", "列出站上使用者",
#endif

  "[/w]ho", "列出本談天室使用者",
  "[/w]hoin <room>", "列出談天室<room> 的使用者",
  NULL
};


static char *room_msg[] =
{
  "[/f]lag [+-][lst]", "設定鎖定、秘密、開放話題",
  "[/i]nvite <id>", "邀請 <id> 加入談天室",
  "[/kick] <id>", "將 <id> 踢出談天室",
  "[/o]p [<id>]", "將 Op 的權力轉移給 <id>",
  "[/topic] <text>", "換個話題",
  "[/w]all", "廣播 (站長專用)",
  NULL
};

#ifdef HAVE_KILL_GAME
static char* kill_msg[] =
{
  "[/jk]ill <room>", "建立或加入殺手遊戲談天室",

  "[/ks]tart ...","開始殺手遊戲(OP專用),參數如下,以空格分隔",
  "    police","強制出現警察",
  "    nopolice","強制不出現警察",
  "    discuss","允許輪流發言時大家插嘴",
  "    noghost","死人不能隨意發言",
  "    strpolice","警察每回合發現一個殺手(殺手投票后)",
  "[/ke]nd","結束殺手遊戲(OP專用)",
  "[/kill] <user>","殺手遊戲中處決一個人,屬于投票性質",
  "[/kq]uery]","殺手遊戲中警察查詢一個人的真實身份",
  "[/ko]ver]","殺手遊戲中認為自己本輪任務已完成,如查看和發言",
  "[/kkk]","殺手遊戲中觸發時間檢查,任何人都可使用,包括死人",
  NULL

};
#endif

static void
chat_help(cu, msg)
  ChatUser *cu;
  char *msg;
{
  char **table, *str, buf[128];

  if (str_equal("op", nextword(&msg)))
  {
    send_to_user(cu, "談天室管理員專用指令", 0, MSG_MESSAGE);
    table = room_msg;
  }
#ifdef HAVE_KILL_GAME
  else if (!str_cmp("ki", nextword(&msg)))
  {
    send_to_user(cu, "殺手遊戲專用指令",0,MSG_MESSAGE);
    table = kill_msg;
  }
#endif
  else
  {
    table = chat_msg;
  }

  while (str = *table++)
  {
    sprintf(buf, "  %-20s- %s", str, *table++);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
  }
}


static void
chat_private(cu, msg)
  ChatUser *cu;
  char *msg;
{
  ChatUser *xuser;
  int userno;
  char *recipient, buf[128];

  userno = 0;
  recipient = nextword(&msg);

  if (strcmp(recipient, interviewer) == 0)
  {
    /* 被采訪人不接受訊息 */
    if (INTERVIEW(cu->room) && (!(ROOMOP(cu))))
       return;
  }

  xuser = (ChatUser *) fuzzy_cuser_by_chatid(recipient);
  if (xuser == NULL)        /* Thor.0724: 用 userid也可傳悄悄話 */
  {
    xuser = cuser_by_userid(recipient);
  }

  if (xuser == NULL)
  {
    sprintf(buf, msg_no_such_id, recipient);
  }
  else if (xuser == FUZZY_USER)
  {             /* ambiguous */
    strcpy(buf, "※ 請指明聊天代號");
  }
  else if (*msg)
  {
#ifdef HAVE_KILL_GAME
    if(cu->room == xuser->room)
    {
      if(cu->room->rflag & ROOM_KILL_STARTED)
      {
        if(KILL_ROLE(cu) != KILL_KILLER || KILL_ROLE(xuser) != KILL_KILLER)
        {
          send_to_user(cu,"\033[1;33m$$ 遊戲中不能說悄悄話!\033[m", 
            0, MSG_MESSAGE);
          return;
        }
      }
    }
#endif
    userno = cu->userno;
    sprintf(buf, "*%s* ", cu->chatid);
    msg[79] = 0;        /* Thor:防止太長 */
    strncat(buf, msg, 80);
    send_to_user(xuser, buf, userno, MSG_MESSAGE);

    if (xuser->clitype)
    {               /* Xshadow: 如果對方是用 client 上來的 */
      sprintf(buf, "%s %s ", cu->userid, cu->chatid);
      msg[79] = 0;
      strncat(buf, msg, 80);
      send_to_user(xuser, buf, userno, MSG_PRIVMSG);
    }

    if (cu->clitype)
    {
      sprintf(buf, "%s %s ", xuser->userid, xuser->chatid);
      msg[79] = 0;
      strncat(buf, msg, 80);
      send_to_user(cu, buf, 0, MSG_MYPRIVMSG);
    }

    sprintf(buf, "%s> ", xuser->chatid);
    strncat(buf, msg, 80);
  }
  else
  {
    sprintf(buf, "※ 您想對 %s 說什麼話呢?", xuser->chatid);
  }

  send_to_user(cu, buf, userno, MSG_MESSAGE);

  /* Thor: userno 要改成 0 嗎? */
}


static void
chat_cloak(cu, msg)
  ChatUser *cu;
  char *msg;
{
  if (CHATSYSOP(cu))
  {
    char buf[128];

    cu->uflag ^= PERM_CLOAK;
    sprintf(buf, "◆ %s", CLOAK(cu) ? MSG_CLOAKED : MSG_UNCLOAK);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
  }
}


/* ----------------------------------------------------- */


static void
arrive_room(cuser, room)
  ChatUser *cuser;
  ChatRoom *room;
{
  char *rname, buf[1024];


  /* Xshadow: 不必送給自己, 反正換房間就會重新 build user list */

  sprintf(buf, "+ %s %s %s %s",
    cuser->userid, cuser->chatid, room->name, cuser->rhost);
  if (ROOMOP(cuser))
    strcat(buf, " Op");
  send_to_room(room, buf, 0, MSG_USERNOTIFY,NULL);

  room->occupants++;
  room_changed(room);

  cuser->room = room;
  rname = room->name;

  if (cuser->clitype)
  {
    send_to_user(cuser, rname, 0, MSG_ROOM);
    send_to_user(cuser, room->topic, 0, MSG_TOPIC);
  }
  else
  {
    sprintf(buf, "/r%s", rname);
    send_to_user(cuser, buf, 0, MSG_MESSAGE);
    sprintf(buf, "/t%s", room->topic);
    send_to_user(cuser, buf, 0, MSG_MESSAGE);
#ifdef HAVE_KILL_GAME
    if(room->rflag & ROOM_KILLGAME)
    {
      sprintf(buf,"/k6");
      send_to_user(cuser, buf, 0, MSG_MESSAGE);
      sprintf(buf,"/u5 %s",MSG_SEPERATOR);
      send_to_user(cuser, buf, 0, MSG_MESSAGE);

    }
    else
    {
      sprintf(buf,"/k2");
      send_to_user(cuser,buf,0,MSG_MESSAGE);
    }
#endif
  }


  /* fuse.991210: INTERVIEW模式下進入不顯示了 */
  if (!INTERVIEW(cuser->room))
    sprintf(buf, "※ %s 進入 [%s] 包廂",
      cuser->chatid, rname);

  if (!CLOAK(cuser))
    send_to_room(room, buf, cuser->userno, MSG_MESSAGE,NULL);
  else
    send_to_user(cuser, buf, 0, MSG_MESSAGE);
}


static int
enter_room(cuser, rname, msg, crflag)
  ChatUser *cuser;
  char *rname;
  char *msg;
  int crflag;
{
  ChatRoom *room;
  int create;
  char buf[256];

  create = 0;
  room = croom_by_roomid(rname);

  if (room == NULL)
  {
    /* new room */

#ifdef DEBUG
    logit(cuser->userid, "create new room");
    debug_room();
#endif

    if (room = roompool)
    {
      roompool = room->next;
    }
    else
    {
      room = (ChatRoom *) malloc(sizeof(ChatRoom));
    }

    if (room == NULL)
    {
      send_to_user(cuser, "※ 無法再新辟包廂了", 0, MSG_MESSAGE);
      return 0;
    }

    memset(room, 0, sizeof(ChatRoom));
    /* str_ncpy(room->name, rname, IDLEN - 1); */
    str_ncpy(room->name, rname, IDLEN);
    /* Thor.980921: 已包含 0 */
    strcpy(room->topic, "這是一個新天地");
#ifdef HAVE_KILL_GAME
    if(crflag & ROOM_KILLGAME)
    {
      strcat(room->topic,"K");
      room->rflag |= ROOM_KILLGAME;
    }
#endif

    sprintf(buf, "+ %s 1 0 %s", room->name, room->topic);
    send_to_room(ROOM_ALL, buf, 0, MSG_ROOMNOTIFY,NULL);

    if (mainroom.next)
      mainroom.next->prev = room;
    room->next = mainroom.next;

    mainroom.next = room;
    room->prev = &mainroom;

#ifdef DEBUG
    logit(cuser->userid, "create room succeed");
    debug_room();
#endif

    create = 1;
    fprintf(flog, "room+\t[%d] %s\n", cuser->sno, rname);
  }
  else
  {
#ifdef HAVE_KILL_GAME
    if(crflag & ROOM_KILLGAME)
    {
      if(!(room->rflag & ROOM_KILLGAME))
      {
        send_to_user(cuser,"※ 這個聊天室不是殺手遊戲室",0,MSG_MESSAGE);
        return 0;
      }

      if(room->rflag & ROOM_KILL_STARTED)
      {
        send_to_user(cuser,"※ 這個聊天室的遊戲已經開始了:(",0,MSG_MESSAGE);
        return 0;
      }
    }
#endif
    if (cuser->room == room)
    {
      sprintf(buf, "※ 您本來就在 [%s] 聊天室囉 :)", rname);
      send_to_user(cuser, buf, 0, MSG_MESSAGE);
      return 0;
    }

#ifdef HAVE_KILL_GAME
      if(room->rflag & ROOM_KILLGAME && room->rflag & ROOM_KILL_STARTED)
      {
        send_to_user(cuser,"※ 這個聊天室裡正在進行遊戲",0,MSG_MESSAGE);
        return 0;
      }
#endif

    if (!CHATSYSOP(cuser) && LOCKED(room) &&
      !list_belong(room->invite, cuser->userno))
    {
      send_to_user(cuser, "※ 內有惡犬,非請莫入", 0, MSG_MESSAGE);
      return 0;
    }
  }

  exit_room(cuser, EXIT_LOGOUT, msg);
  arrive_room(cuser, room);

  if (create)
    cuser->uflag |= PERM_ROOMOP;

  return 0;
}


static void
cuser_free(cuser)
  ChatUser *cuser;
{
  int sock;

  sock = cuser->sock;
  shutdown(sock, 2);
  close(sock);

  FD_CLR(sock, &mainfset);

  list_free(&cuser->ignore);
  totaluser--;

  if (cuser->room)
  {
    exit_room(cuser, EXIT_LOSTCONN, NULL);
  }

  fprintf(flog, "BYE\t[%d] T%d X%d\n",
    cuser->sno, time(0) - cuser->tbegin, cuser->xdata);
}


static void
print_user_counts(cuser)
  ChatUser *cuser;
{
  ChatRoom *room;
  int num, userc, suserc, roomc, number;
  char buf[256];

  userc = suserc = roomc = 0;

  room = &mainroom;
  do
  {
    num = room->occupants;
    if (SECRET(room))
    {
      suserc += num;
      if (CHATSYSOP(cuser))
    roomc++;
    }
    else
    {
      userc += num;
      roomc++;
    }
  } while (room = room->next);

  number = (cuser->clitype) ? MSG_MOTD : MSG_MESSAGE;

  sprintf(buf,
    "⊙ 歡迎光臨【真情聊天室】,目前開了 %d 間包廂", roomc);
  send_to_user(cuser, buf, 0, number);

  sprintf(buf, "⊙ 共有 %d 人來擺龍門陣", userc);
  if (suserc)
    sprintf(buf + strlen(buf), " [%d 人在秘密聊天室]", suserc);

  send_to_user(cuser, buf, 0, number);
}


static int
login_user(cu, msg)
  ChatUser *cu;
  char *msg;
{
  int utent;

  char *userid;
  char *chatid, *passwd;
  ChatUser *xuser;
  int level;
/*   struct hostent *hp; */

#ifndef STAND_ALONE
  ACCT acct;
#endif

  /* Xshadow.0915: common client support : /-! userid chatid password */
  /* client/server 版本依據 userid 抓 .PASSWDS 判斷 userlevel */

  userid = nextword(&msg);
  chatid = nextword(&msg);

#ifdef  DEBUG
  logit("ENTER", userid);
#endif

#ifndef STAND_ALONE
  /* Thor.0730: parse space before passwd */

  passwd = msg;

  /* Thor.0813: 跳過一空格即可, 因為反正如果chatid有空格, 密碼也不對 */
  /* 就算密碼對, 也不會怎麼樣:p */
  /* 可是如果密碼第一個字是空格, 那跳太多空格會進不來... */
  /* Thor.980910: 由於 nextword修改為後接空格填0, 傳入值則直接後移至0後,
                  所以不需作此動作 */
#if 0
  if (*passwd == ' ')
    passwd++;
#endif

  /* Thor.0729: load acct */

  if (!*userid || (acct_load(&acct, userid) < 0))
  {

#ifdef  DEBUG
    logit("noexist", userid);
#endif

    if (cu->clitype)
      send_to_user(cu, "錯誤的使用者代號", 0, ERR_LOGIN_NOSUCHUSER);
    else
      send_to_user(cu, CHAT_LOGIN_INVALID, 0, MSG_MESSAGE);

    return -1;
  }

  /* Thor.0813: 改用真實 password check, for C/S bbs */

  /* Thor.990214: 注意,daolib中 非0代表失敗 */
  /* if (!chkpasswd(acct.passwd, passwd)) */
  if (chkpasswd(acct.passwd, passwd))
  {

#ifdef  DEBUG
    logit("fake", userid);
#endif

    if (cu->clitype)
      send_to_user(cu, "密碼錯誤", 0, ERR_LOGIN_PASSERROR);
    else
      send_to_user(cu, CHAT_LOGIN_INVALID, 0, MSG_MESSAGE);

    return -1;
  }

  level = acct.userlevel;
  utent = acct.userno;

#else               /* STAND_ALONE */
  level = 1;
  utent = ++userno_inc;
#endif              /* STAND_ALONE */

  /* Thor.0819: for client/server bbs */

#ifdef DEBUG
  log_user(NULL);
#endif

  for (xuser = mainuser; xuser; xuser = xuser->unext)
  {

#ifdef DEBUG
    log_user(xuser);
#endif

    if (xuser->userno == utent)
    {

#ifdef  DEBUG
      logit("enter", "bogus");
#endif

#ifdef HAVE_KILL_GAME
      if(xuser->killflag &= KILL_DROPED)
      {
        char buf[128];
        xuser->sock = cu->sock;
        xuser->clitype = cu->clitype;

        totaluser --;

        cu->unext = userpool;
        userpool = cu;

        xuser->room->occupants ++;

        sprintf(buf,"\033[1;33m$$ $$ 歡迎 %s 回來繼續遊戲 $$ $$",xuser->chatid)
;
        send_to_room(xuser->room,buf,0,MSG_MESSAGE,NULL);

        xuser->killflag &= ~KILL_DROPED;

        kill_on_reconnect(xuser->room);
      }
      else
      {
        if (cu->clitype)
      send_to_user(cu, "請勿派遣分身進入聊天室 !!", 0,
        ERR_LOGIN_USERONLINE);
        else
      send_to_user(cu, CHAT_LOGIN_BOGUS, 0, MSG_MESSAGE);
        return -1;      /* Thor: 或是0等它自己了斷? */
      }
#else
        if (cu->clitype)
      send_to_user(cu, "請勿派遣分身進入聊天室 !!", 0,
        ERR_LOGIN_USERONLINE);
        else
      send_to_user(cu, CHAT_LOGIN_BOGUS, 0, MSG_MESSAGE);
        return -1;      /* Thor: 或是0等它自己了斷? */
#endif
    }
  }


#ifndef STAND_ALONE
  /* Thor.980629: 暫時借用 invalid_chatid 濾除 沒有PERM_CHAT的人 */

  if (!valid_chatid(chatid) || !(level & PERM_CHAT) || (level & PERM_DENYCHAT))
  { /* Thor.981012: 徹底一些, 連 denychat也BAN掉, 免得 client作怪 */

#ifdef  DEBUG
    logit("enter", chatid);
#endif

    if (cu->clitype)
      send_to_user(cu, "不合法的聊天室代號 !!", 0, ERR_LOGIN_NICKERROR);
    else
      send_to_user(cu, CHAT_LOGIN_INVALID, 0, MSG_MESSAGE);
    return 0;
  }
#endif

#ifdef  DEBUG
  debug_user();
#endif

  if (cuser_by_chatid(chatid) != NULL)
  {
    /* chatid in use */

#ifdef  DEBUG
    logit("enter", "duplicate");
#endif

    if (cu->clitype)
      send_to_user(cu, "這個代號已經有人使用", 0, ERR_LOGIN_NICKINUSE);
    else
      send_to_user(cu, CHAT_LOGIN_EXISTS, 0, MSG_MESSAGE);
    return 0;
  }

#ifdef DEBUG            /* CHATSYSOP 一進來就隱身 */
  cu->uflag = level & ~(PERM_ROOMOP | PERM_CHATOP | (CHATSYSOP(cu) ? 0 : PERM_C
LOAK));
#else
  cu->uflag = level & ~(PERM_ROOMOP | PERM_CHATOP | PERM_CLOAK);
#endif

  /* Thor: 進來先清空 ROOMOP (同PERM_CHAT) */

  strcpy(cu->userid, userid);
  str_ncpy(cu->chatid, chatid, sizeof(cu->chatid));
  /* Thor.980921: str_ncpy與一般 strncpy有所不同, 特別注意 */

  /* Thor.980921: 防止太長 */
  /* cu->chatid[8]=0; */

  fprintf(flog, "ENTER\t[%d] %s\n", cu->sno, userid);

  /* Xshadow: 取得 client 的來源 */

  dns_name(cu->rhost, cu->ibuf);
  str_ncpy(cu->rhost, cu->ibuf, sizeof(cu->rhost));
#if 0
  hp = gethostbyaddr(cu->rhost, sizeof(struct in_addr), AF_INET);
  str_ncpy(cu->rhost,
    hp ? hp->h_name : inet_ntoa((struct in_addr *) cu->rhost),
    sizeof(cu->rhost) - 1);
#endif

  cu->userno = utent;

  if (cu->clitype)
    send_to_user(cu, "順利", 0, MSG_LOGINOK);
  else
    send_to_user(cu, CHAT_LOGIN_OK, 0, MSG_MESSAGE);

  arrive_room(cu, &mainroom);

  send_to_user(cu, "", 0, MSG_MOTDSTART);
  print_user_counts(cu);
  send_to_user(cu, "", 0, MSG_MOTDEND);

#ifdef  DEBUG
  logit("enter", "OK");
#endif

  return 0;
}


static void
chat_act(cu, msg)
  ChatUser *cu;
  char *msg;
{
  if (QUIET(cu->room) && (!(ROOMOP(cu))))
    return;

#ifdef HAVE_KILL_GAME
  if(!kill_can_talk(cu->room,cu))
  {
    send_to_user(cu,"\033[1;35m$$ 你現在做的動作別人看不到哦\033[m", 
      0, MSG_MESSAGE);
    return;
  }
#endif

  if (*msg)
  {
    char buf[256];

    sprintf(buf, "%s %s", cu->chatid, msg);
    send_to_room(cu->room, buf, cu->userno, MSG_MESSAGE,cu);
  }
}


static void
chat_ignore(cu, msg)
  ChatUser *cu;
  char *msg;
{
  char *str, buf[256];

  if (RESTRICTED(cu))
  {
    str = "※ 您沒有 ignore 別人的權利";
  }
  else
  {
    char *ignoree;

    str = buf;
    ignoree = nextword(&msg);
    if (*ignoree)
    {
      ChatUser *xuser;

      xuser = cuser_by_userid(ignoree);

      if (xuser == NULL)
      {
    sprintf(str, msg_no_such_id, ignoree);
      }
      else if (xuser == cu || CHATSYSOP(xuser) ||
    (ROOMOP(xuser) && (xuser->room == cu->room)))
      {
    sprintf(str, "◆ 不可以 ignore [%s]", ignoree);
      }
      else
      {
    if (list_belong(cu->ignore, xuser->userno))
    {
      sprintf(str, "※ %s 已經被凍結了", xuser->chatid);
    }
    else
    {
      list_add(&(cu->ignore), xuser);
      sprintf(str, "◆ 將 [%s] 打入冷宮了 :p", xuser->chatid);
    }
      }
    }
    else
    {
      UserList *list;

      if (list = cu->ignore)
      {
    int len;
    char userid[16];

    send_to_user(cu, "◆ 這些人被打入冷宮了:", 0, MSG_MESSAGE);
    len = 0;
    do
    {
      sprintf(userid, "%-13s", list->userid);
      strcpy(str + len, userid);
      len += 13;
      if (len >= 78)
      {
        send_to_user(cu, str, 0, MSG_MESSAGE);
        len = 0;
      }
    } while (list = list->next);

    if (len == 0)
      return;
      }
      else
      {
    str = "◆ 您目前并沒有 ignore 任何人";
      }
    }
  }

  send_to_user(cu, str, 0, MSG_MESSAGE);
}


static void
chat_unignore(cu, msg)
  ChatUser *cu;
  char *msg;
{
  char *ignoree, *str, buf[80];

  ignoree = nextword(&msg);

  if (*ignoree)
  {
    sprintf(str = buf, (list_delete(&(cu->ignore), ignoree)) ?
      "◆ [%s] 不再被你冷落了" :
      "◆ 您并未 ignore [%s] 這號人物", ignoree);
  }
  else
  {
    str = "◆ 請指明 user ID";
  }
  send_to_user(cu, str, 0, MSG_MESSAGE);
}


static void
chat_join(cu, msg)
  ChatUser *cu;
  char *msg;
{
  if (RESTRICTED(cu))
  {
    send_to_user(cu, "※ 您沒有加入其他聊天室的權限", 0, MSG_MESSAGE);
  }
  else
  {
    char *roomid = nextword(&msg);

    if (*roomid)
      enter_room(cu, roomid, msg, 0);
    else
      send_to_user(cu, "※ 請指定聊天室", 0, MSG_MESSAGE);
  }
}

#ifdef HAVE_KILL_GAME
static void
kill_join(cu, msg)
  ChatUser *cu;
  char *msg;
{
  if (RESTRICTED(cu))
  {
    send_to_user(cu, "※ 您沒有加入其他聊天室的權限", 0, MSG_MESSAGE);
  }
  else
  {
    char *roomid = nextword(&msg);

    if (*roomid)
      enter_room(cu, roomid, msg, ROOM_KILLGAME);
    else
      send_to_user(cu, "※ 請指定聊天室", 0, MSG_MESSAGE);
  }
}

#endif

static void
chat_interview(cu, msg)
  ChatUser *cu;
  char *msg;
{
  char *twit, buf[80];
  ChatUser *xuser;

  if (!ROOMOP(cu))
  {
    send_to_user(cu, msg_not_op, 0, MSG_MESSAGE);
    return;
  }

  twit = nextword(&msg);
  xuser = cuser_by_chatid(twit);

  if (xuser == NULL)
  {                       /* Thor.980604: 用 userid也嘛通 */
    xuser = cuser_by_userid(twit);
  }

  if (xuser == NULL)
  {
    sprintf(buf, msg_no_such_id, twit);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
    return;
  }

  strcpy(interviewer, twit);
}

static void
chat_kick(cu, msg)
  ChatUser *cu;
  char *msg;
{
  char *twit, buf[80];
  ChatUser *xuser;
  ChatRoom *room;

  if (!ROOMOP(cu))
  {
    send_to_user(cu, msg_not_op, 0, MSG_MESSAGE);
    return;
  }

  twit = nextword(&msg);
  xuser = cuser_by_chatid(twit);

  if (xuser == NULL)
  {                       /* Thor.980604: 用 userid也嘛通 */
    xuser = cuser_by_userid(twit);
  }

  if (xuser == NULL)
  {
    sprintf(buf, msg_no_such_id, twit);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
    return;
  }

  room = cu->room;
  if (room != xuser->room || CLOAK(xuser))
  {
    sprintf(buf, msg_not_here, twit);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
    return;
  }

  if (CHATSYSOP(xuser))
  {
    sprintf(buf, "◆ 不可以 kick [%s]", twit);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
    return;
  }

  exit_room(xuser, EXIT_KICK, (char *) NULL);

  if (room == &mainroom)
    xuser->uptime = 0;      /* logout_user(xuser); */
  else
    enter_room(xuser, MAIN_NAME, (char *) NULL, 0);
    /* Thor.980602: 其實踢就踢,不要show出xxx離開了的訊息比較好 */
}

static void
chat_makeop(cu, msg)
  ChatUser *cu;
  char *msg;
{
  char *newop, buf[80];
  ChatUser *xuser;
  ChatRoom *room;

  /* Thor.980603: PERM_CHATROOM改為 default 沒有 roomop, 但可以自己取得 */

  newop = nextword(&msg);

  room = cu->room;

  if(!*newop && CHATSYSOP(cu))
  {
    /* Thor.980603: PERM_CHATROOM改為 default 沒有 roomop, 但可以自己取得 */
    cu->uflag ^= PERM_CHATOP;

    user_changed(cu);
    if (!CLOAK(cu))
    {
      sprintf(buf,ROOMOP(cu) ? "※ 真情使者 將 Op 權力授予 %s"
                             : "※ 真情使者 將 %s 的 Op 權力收回", cu->chatid);
      send_to_room(room, buf, 0, MSG_MESSAGE,NULL);
    }

    return;
  }

  /* if (!ROOMOP(cu)) */
  /* Thor.980603: chat room總管不能轉移 Op 權力 */
  /* fuse:我試試說
  if (!(cu->uflag & PERM_ROOMOP))
  */
  if (!ROOMOP(cu))
  {
    send_to_user(cu, "◆ 您不能轉移 Op 的權力", 0, MSG_MESSAGE);
    return;
  }

  xuser = cuser_by_chatid(newop);

  if (xuser == NULL)
  {
    sprintf(buf, msg_no_such_id, newop);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
    return;
  }

  if (cu == xuser)
  {
    send_to_user(cu, "※ 您早就已經是 Op 了啊", 0, MSG_MESSAGE);
    return;
  }

  /* room = cu->room; */

  if (room != xuser->room || CLOAK(xuser))
  {
    sprintf(buf, msg_not_here, xuser->chatid);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
    return;
  }

  /* 兩次授予Op取消 */
  if (CHATSYSOP(cu) && (xuser->uflag & PERM_ROOMOP))
  {
     xuser->uflag  &= ~PERM_ROOMOP;
     user_changed(xuser);
     return;
  }

  /* fuse: 如果是chat sysop,那就不把自己權力去掉了 */
  if (!CHATSYSOP(cu))
     cu->uflag &= ~PERM_ROOMOP;
  xuser->uflag |= PERM_ROOMOP;

  user_changed(cu);
  user_changed(xuser);

  if (!CLOAK(cu))
  {
    sprintf(buf, "※ %s 將 Op 權力轉移給 %s",
      cu->chatid, xuser->chatid);
    send_to_room(room, buf, 0, MSG_MESSAGE,NULL);
  }
}

#if 0
static void
chat_makeop(cu, msg)
  ChatUser *cu;
  char *msg;
{
  char *newop, buf[80];
  ChatUser *xuser;
  ChatRoom *room;

  /* Thor.980603: PERM_CHATROOM改為 default 沒有 roomop, 但可以自己取得 */
  if (!ROOMOP(cu))
  /* if (!ROOMOP(cu) && !CHATSYSOP(cu)) */
  {
    send_to_user(cu, msg_not_op, 0, MSG_MESSAGE);
    return;
  }

  newop = nextword(&msg);
  xuser = cuser_by_chatid(newop);

#if 0
  if (xuser == NULL)
  {                       /* Thor.980604: 用 userid也嘛通 */
    xuser = cuser_by_userid(newop);
  }
#endif

  if (xuser == NULL)
  {
    sprintf(buf, msg_no_such_id, newop);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
    return;
  }

  if (cu == xuser)
  {
    send_to_user(cu, "※ 您早就已經是 Op 了啊", 0, MSG_MESSAGE);
    return;
  }

  room = cu->room;

  if (room != xuser->room || CLOAK(xuser))
  {
    sprintf(buf, msg_not_here, xuser->chatid);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
    return;
  }

  cu->uflag &= ~PERM_ROOMOP;
  xuser->uflag |= PERM_ROOMOP;

  user_changed(cu);
  user_changed(xuser);

  if (!CLOAK(cu))
  {
    sprintf(buf, "※ %s 將 Op 權力轉移給 %s",
      cu->chatid, xuser->chatid);
    send_to_room(room, buf, 0, MSG_MESSAGE,NULL);
  }
}

#endif

static void
chat_invite(cu, msg)
  ChatUser *cu;
  char *msg;
{
  char *invitee, buf[80];
  ChatUser *xuser;
  ChatRoom *room;
  UserList **list;

  if (!ROOMOP(cu))
  {
    send_to_user(cu, msg_not_op, 0, MSG_MESSAGE);
    return;
  }

  invitee = nextword(&msg);
  xuser = cuser_by_chatid(invitee);

#if 0
  if (xuser == NULL)
  {                       /* Thor.980604: 用 userid也嘛通 */
    xuser = cuser_by_userid(invitee);
  }
#endif

  if (xuser == NULL)
  {
    sprintf(buf, msg_no_such_id, invitee);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
    return;
  }

  room = cu->room;      /* Thor: 是否要 check room 是否 NULL ? */
  list = &(room->invite);

  if (list_belong(*list, xuser->userno))
  {
    sprintf(buf, "※ %s 已經接受過邀請了", xuser->chatid);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
    return;
  }
  list_add(list, xuser);

  sprintf(buf, "※ %s 邀請您到 [%s] 聊天室",
    cu->chatid, room->name);
  send_to_user(xuser, buf, 0, MSG_MESSAGE);
  sprintf(buf, "※ %s 收到您的邀請了", xuser->chatid);
  send_to_user(cu, buf, 0, MSG_MESSAGE);
}


static void
chat_broadcast(cu, msg)
  ChatUser *cu;
  char *msg;
{
  char buf[80];

  if (!CHATSYSOP(cu))
  {
    send_to_user(cu, "※ 您沒有在聊天室廣播的權力!", 0, MSG_MESSAGE);
    return;
  }

  if (*msg == '\0')
  {
    send_to_user(cu, "※ 請指定廣播內容", 0, MSG_MESSAGE);
    return;
  }

  sprintf(buf, "※ " BOARDNAME "談天室廣播中 [%s].....",
    cu->chatid);
  send_to_room(ROOM_ALL, buf, 0, MSG_MESSAGE,NULL);
  sprintf(buf, "◆ %s", msg);
  send_to_room(ROOM_ALL, buf, 0, MSG_MESSAGE,NULL);
}


static void
chat_bye(cu, msg)
  ChatUser *cu;
  char *msg;
{
  exit_room(cu, EXIT_LOGOUT, msg);
  cu->uptime = 0;
  /* logout_user(cu); */
}


/* --------------------------------------------- */
/* MUD-like social commands : action             */
/* --------------------------------------------- */

struct ChatAction
{
  char *verb;           /* 動詞 */
  char *chinese;        /* 中文翻譯 */
  char *part1_msg;      /* 介詞 */
  char *part2_msg;      /* 動作 */
};


static ChatAction party_data[] =
{
  {
    "aluba", "阿魯巴", "把", "架上柱子阿魯巴!!"
  },
  {
    "aodre", "景仰", "對", "的景仰有如滔滔江水,連綿不絕……"
  },
  {
    "bearhug", "熱擁", "熱情的擁抱", ""
  },
  {
    "blade", "一刀", "一刀啟程把", "送上西天"
  },                /* Thor.0729:觀眾要求 */
  {
    "bless", "祝福", "祝福", "心想事成"
  },
  {
    "board", "主機板", "把", "抓去跪主機板"
  },                /* Thor.0730: 觀眾要求 */
  {
    "bokan", "氣功", "雙掌微合,蓄勢待發……突然間,電光乍現,對", "使出了Bo--
Kan!"
  },
  {
    "bow", "鞠躬", "畢躬畢敬的向", "鞠躬"
  },
  {
    "box", "幕之內", "開始輪擺式移位,對", "作肝臟攻擊"
  },
  {
    "boy", "平底鍋", "從背後拿出了平底鍋,把", "敲昏了"
  },
  {
    "bye", "掰掰", "向", "說掰掰!!"
  },
  {
    "call", "呼喚", "大聲的呼喚,啊~~", "啊~~~你在哪裡啊啊啊啊~~~~"
  },
  {
    "caress", "輕撫", "輕輕的撫摸著", ""
  },
  {
    "clap", "鼓掌", "向", "熱烈鼓掌"
  },
  {
    "claw", "抓抓", "從貓咪樂園借了只貓爪,把", "抓得死去活來"
  },
  {
    "comfort", "安慰", "溫言安慰", ""
  },
  {
    "cong", "恭喜", "從背後拿出了拉炮,□!□!恭喜", ""
  },
  {
    "cpr", "口對口", "對著", "做口對口人工呼吸"
  },
  {
    "cringe", "乞憐", "向", "卑躬屈膝,搖尾乞憐"
  },
  {
    "cry", "大哭", "向", "嚎啕大哭"
  },
  {
    "dance", "跳舞", "拉了", "的手翩翩起舞"
  },
  {
    "destroy", "毀滅", "祭起了『極大毀滅咒文』,轟向", ""
  },
  {
    "dogleg", "狗腿", "對", "狗腿"
  },
  {
    "drivel", "流口水", "對著", "流口水"
  },

#if 0
  /* Thor.0729: 與 //love 重覆 */
  {
    "emotion", "告白", "對", "純情的告白"
  },
#endif

  {
    "envy", "羨慕", "向", "流露出羨慕的眼光"
  },
  {
    "eye", "送秋波", "對", "頻送秋波"
  },
  {
    "fire", "銬問", "拿著火紅的鐵棒走向", ""
  },
  {
    "forgive", "原諒", "接受道歉,原諒了", ""
  },
  {
    "french", "法式吻", "把舌頭伸到", "喉嚨裡~~~哇!一個浪漫的法國氏深吻"
  },
  {
    "giggle", "傻笑", "對著", "傻傻的呆笑"
  },
  {
    "glue", "補心", "用三秒膠,把", "的心黏了起來"
  },                /* Thor.0731:應觀眾要求 */
  {
    "goodbye", "告別", "淚眼汪汪的向", "告別"
  },
  {
    "grin", "奸笑", "對", "露出邪惡的笑容"
  },
  {
    "growl", "咆哮", "對", "咆哮不已"
  },
  {
    "hand", "握手", "跟", "握手"
  },
  /* {"hide","躲","用力地躲著",""}, */
  {
    "hide", "躲", "躲在", "背後"
  },                /* Thor.0729: 觀眾修改版 */
  {
    "hospitl", "送醫院", "把", "送進醫院"
  },
  {
    "hug", "擁抱", "輕輕地擁抱", ""
  },
  {
    "hrk", "□龍拳", "沉穩了身形,匯聚了內勁,對", "使出了一記Ho--Ryu--Kan!
!!"
  },
  {
    "hypnoze", "催眠", "拿著挂表晃呀晃的,對", "展開催眠……"
  },    /* Thor.980619: 新詞 */
  {
    "jab", "戳人", "溫柔的戳著", ""
  },
  {
    "judo", "過肩摔", "抓住了", "的衣襟,轉身……啊,是一記過肩摔!"
  },
  {
    "kickout", "踢", "用大腳把", "踢到山下去了"
  },
  {
    "kick", "踢人", "把", "踢的死去活來"
  },
  {
    "kiss", "輕吻", "輕吻", "的臉頰"
  },
  {
    "laugh", "嘲笑", "大聲嘲笑", ""
  },
  {
    "levis", "給我", "說:給我", "!其余免談!"
  },
  {
    "lick", "舔", "狂舔", ""
  },
  {
    "lobster", "壓制", "施展逆蝦形固定,把", "壓制在地板上"
  },
  {
    "love", "表白", "對", "深情的表白"
  },
  {
    "marry", "求婚", "捧著九百九十九朵玫瑰向", "求婚"
  },
  {
    "no", "不要啊", "拼命對著", "搖頭~~~~不要啊~~~~"
  },
  {
    "nod", "點頭", "向", "點頭稱是"
  },
  {
    "nudge", "頂肚子", "用手肘頂", "的肥肚子"
  },
  {
    "pad", "拍肩膀", "輕拍", "的肩膀"
  },
  {
    "pettish", "撒嬌", "跟", "嗲聲嗲氣地撒嬌"
  },
  {
    "pili", "霹靂", "使出 君子風 天地根 般若懺 三式合一打向", "~~~~~~"
  },
  /* Thor.0729: 應觀眾要求 */
  {
    "pinch", "擰人", "用力的把", "擰的黑青"
  },
  {
    "roll", "打滾", "放出多爾袞的音樂,", "在地上滾來滾去"
  },
  {
    "protect", "保護", "保護著", ""
  },
  {
    "pull", "拉", "死命地拉住", "不放"
  },
  {
    "punch", "揍人", "狠狠揍了", "一頓"
  },
  {
    "rascal", "耍賴", "跟", "耍賴"
  },
  {
    "recline", "入懷", "鑽到", "的懷裡睡著了……"
  },
  {
    "respond", "負責", "安慰", "說:『不要哭,我會負責的……』"
  },
  {
    "shrug", "聳肩", "無奈地向", "聳了聳肩膀"
  },
  {
    "sigh", "嘆氣", "對", "嘆了一口氣"
  },
  {
    "slap", "打耳光", "啪啪的巴了", "一頓耳光"
  },
  {
    "smooch", "擁吻", "擁吻著", ""
  },
  {
    "snicker", "竊笑", "嘿嘿嘿..的對", "竊笑"
  },
  {
    "sniff", "不屑", "對", "嗤之以鼻"
  },

  {
    "spank", "打屁屁", "用巴掌打", "的臀部"
  },
  /* {"spank", "用貓爪抓", "的臀部","抓臀部"}, */
  {
    "squeeze", "緊擁", "緊緊地擁抱著", ""
  },
  {
    "sysop", "召喚", "叫出了踢米熊,把", "踩扁了!"
  },
  {
    "thank", "感謝", "向", "感謝得五體投地"
  },
  {
    "tickle", "搔痒", "咕嘰!咕嘰!搔", "的痒"
  },
  {
    "wake", "搖醒", "輕輕地把", "搖醒"
  },
  {
    "wave", "揮手", "對著", "拼命的搖手"
  },
  {
    "welcome", "歡迎", "歡迎", "進來八卦一下"
  },
  {
    "what", "什麼", "說:『", "哩公瞎密哇隴聽某???□?』"
  },
  {
    "whip", "鞭子", "手上拿著蠟燭,用鞭子痛打", ""
  },
  {
    "wink", "眨眼", "對", "神秘的眨眨眼睛"
  },
  {
    "zap", "猛攻", "對", "瘋狂的攻擊"
  },
/* lkchu.981201: add by mat */
  {
    "1", "1號", "拿出電鋸,朝", "鋸了下去..."
  },
  {
    "2", "2號", "對", "開槍!"
  },
  {
    "3", "3號", "碰了一聲,", "啊~~了一聲!"
  },
  {
    "4", "4號", "挺起機槍,把", "打成蜂窩"
  },
  {
    "5", "5號", "駕著卡車,然後突然對", "右轉..."
  },
  {
    "6", "6號", "bon quey 了!拿起六號沖向", ""
  },
  {
    "7", "7號", "對", "拋了一個媚眼,把他看死了..."
  },
  {
    "8", "8號", "按了 pause,然後開始對", "破口大罵"
  },
  {
    "bite", "咬", "把", "咬得死去活來"
  },
  {
    "bfydown", "防御降", "施展咒語", "防御力下降了..."
  },
  {
    "bfyup", "防御升","施展咒語", "防御力上升了..."
  },
  {
    "clock", "切鬧鐘", "切掉", "的鬧鐘..."
  },
  {
    "cola", "灌可樂", "對", "灌了一桶可樂"
  },
  {
    "cowhide", "鞭打","拿鞭子對", "狠狠地抽打"
  },
  {
    "crime", "道德", "說:", "的道德指數不夠"
  },
  {
    "evening", "晚安", "對", "說『晚安』"
  },
  {
    "fireball", "發火球", "對", "發了一顆火球"
  },
  {
    "hpup", "hp上升", "施展咒語", "生命力上升至極限"
  },
  {
    "kissbye", "吻別", "跟", "吻別..."
  },
  {
    "mail", "打包", "把", "打包遞送到百慕大"
  },
  {
    "morning", "早安", "對", "說『早安』"
  },
  {
    "noon", "午安", "對", "說『午安』"
  },
  {
    "puding", "灌布丁", "對", "灌了一卡車布丁"
  },
  {
    "recycle", "回收桶", "把", "丟到資源回收桶"
  },
  {
    "sex", "騷擾", "對", "騷擾"
  },
  {
    "shit", "雪特", "對", "罵了一聲 雪特"
  },
  {
    "wait", "等一下", "叫", "等一下哦!"
  },
  {
    "wall", "他撞牆", "抓著", "的頭往牆用力撞了几下"
  },
  {
    "blink", "委曲淚", "對著", "眨眨眼,掉下了委曲的眼淚。"
  },
  {
    "book", "丟書", "拿了一本書朝", "那丟了過去"
  },
  {
    "crash", "塊石頭", "使勁地舉起大石頭往", "身上丟去"
  },
  {
    "curtsey", "中古禮", "優雅地對著","行中古世紀的屈膝禮。"
  },
  {
    "fuzzy","飛鳥","派出飛鳥一號向","沖過去"
  },
  {
    "gag","縫嘴巴","把","的嘴巴用針縫起來"
  },
  {
    "glare", "瞪人", "冷冷地瞪著",""
  },
  {
    "kill", "砍人", "把", "亂刀砍死~~~~"
  },
  {
    "listen", "聽", "叫", "閉嘴仔細聽"
  },
  {
    "mglint", "調皮", "眼睛對著", "閃動著一陣調皮的光芒"
  },
  {
    "pat", "拍頭", "拍拍", "的頭"
  },
  {
    "pettish", "撒嬌", "跟", "嗲聲嗲氣地撒嬌"
  },
  {
    "scratch", "磨爪", "撿起", "身邊的石子磨磨自己的利爪"
  },
  {
    "sorry", "對不起", "向", "說對不起! 我錯了!"
  },
  {
    "wiggle", "扭屁股", "對著", "扭屁股"
  },

  {
    NULL, NULL, NULL, NULL
  }
};


static int
party_action(cu, cmd, party)
  ChatUser *cu;
  char *cmd;
  char *party;
{
  ChatAction *cap;
  char *verb, buf[256];

  for (cap = party_data; verb = cap->verb; cap++)
  {
    if (str_equal(verb, cmd))
    {
      if (*party == '\0')
      {
    party = "大家";
      }
      else
      {
    ChatUser *xuser;

    xuser = fuzzy_cuser_by_chatid(party);
    if (xuser == NULL)
    {           /* Thor.0724: 用 userid也嘛通 */
      xuser = cuser_by_userid(party);
    }

    if (xuser == NULL)
    {
      sprintf(buf, msg_no_such_id, party);
      send_to_user(cu, buf, 0, MSG_MESSAGE);
      return 0;
    }
    else if (xuser == FUZZY_USER)
    {
      send_to_user(cu, "※ 請指明聊天代號", 0, MSG_MESSAGE);
      return 0;
    }
    else if (cu->room != xuser->room || CLOAK(xuser))
    {
      sprintf(buf, msg_not_here, party);
      send_to_user(cu, buf, 0, MSG_MESSAGE);
      return 0;
    }
    else
    {
      party = xuser->chatid;
    }
      }
      sprintf(buf, "%s %s %s %s",
    cu->chatid, cap->part1_msg, party, cap->part2_msg);
      send_to_room(cu->room, buf, cu->userno, MSG_MESSAGE,cu);
      return 0;         /* Thor: cu->room 是否為 NULL? */
    }
  }
  return 1;
}


/* --------------------------------------------- */
/* MUD-like social commands : speak              */
/* --------------------------------------------- */


static ChatAction speak_data[] =
{
  {
    "ask", "詢問", "問", NULL
  },
  {
    "chant", "歌頌", "高聲歌頌", NULL
  },
  {
    "cheer", "喝采", "喝采", NULL
  },
  {
    "chuckle", "輕笑", "輕笑", NULL
  },
  {
    "curse", "暗幹", "暗幹", NULL
  },
  /* {"curse", "咒罵", NULL}, */
  {
    "demand", "要求", "要求", NULL
  },
  {
    "frown", "皺眉頭", "蹙眉", NULL
  },
  {
    "groan", "呻吟", "呻吟", NULL
  },
  {
    "grumble", "發牢騷", "發牢騷", NULL
  },
  {
    "guitar", "彈唱", "邊彈著吉他,邊唱著", NULL
  },
  /* {"helpme", "呼救","大聲呼救",NULL}, */
  {
    "hum", "喃喃", "喃喃自語", NULL
  },
  {
    "moan", "怨嘆", "怨嘆", NULL
  },
  {
    "notice", "強調", "強調", NULL
  },
  {
    "order", "命令", "命令", NULL
  },
  {
    "ponder", "沈思", "沈思", NULL
  },
  {
    "pout", "噘嘴", "噘著嘴說", NULL
  },
  {
    "pray", "祈禱", "祈禱", NULL
  },
  {
    "request", "懇求", "懇求", NULL
  },
  {
    "shout", "大罵", "大罵", NULL
  },
  {
    "sing", "唱歌", "唱歌", NULL
  },
  {
    "smile", "微笑", "微笑", NULL
  },
  {
    "smirk", "假笑", "假笑", NULL
  },
  {
    "swear", "發誓", "發誓", NULL
  },
  {
    "tease", "嘲笑", "嘲笑", NULL
  },
#if 0
  {
    "titter", "偷笑", "偷笑", NULL
  }, /* Thor.980619: 新詞 */
#endif
  {
    "whimper", "嗚咽", "嗚咽的說", NULL
  },
  {
    "yawn", "哈欠", "邊打哈欠邊說", NULL
  },
  {
    "yell", "大喊", "大喊", NULL
  },
  {
    NULL, NULL, NULL, NULL
  }
};


static int
speak_action(cu, cmd, msg)
  ChatUser *cu;
  char *cmd;
  char *msg;
{
  ChatAction *cap;
  char *verb, buf[256];

  for (cap = speak_data; verb = cap->verb; cap++)
  {
    if (str_equal(verb, cmd))
    {
      sprintf(buf, "%s %s: %s",
    cu->chatid, cap->part1_msg, msg);
      send_to_room(cu->room, buf, cu->userno, MSG_MESSAGE,cu);
      return 0;
    }
  }
  return 1;
}


/* ----------------------------------------------------- */
/* MUD-like social commands : condition          */
/* ----------------------------------------------------- */


static ChatAction condition_data[] =
{
  {
    "applaud", "拍手", "啪啪啪啪啪啪啪....", NULL
  },
  {
    "ayo", "唉呦喂", "唉呦喂~~~", NULL
  },
  {
    "back", "坐回來", "回來坐正繼續奮戰", NULL
  },
  {
    "blood", "在血中", "倒在血泊之中", NULL
  },
  {
    "blush", "臉紅", "臉都紅了", NULL
  },
  {
    "broke", "心碎", "的心破碎成一片一片的", NULL
  },                /* Thor.0731:應觀眾要求 */
  /* {"bokan", "Bo Kan! Bo Kan!", NULL}, */
  {
    "careles", "沒人理", "嗚~~都沒有人理我 :~~~~", NULL
  },
  {
    "chew", "嗑瓜子", "很悠閑的嗑起瓜子來了", NULL
  },
  {
    "climb", "爬山", "自己慢慢爬上山來……", NULL
  },
  {
    "cold", "感冒了", "感冒了,媽媽不讓我出去玩 :~~~(", NULL
  },
  {
    "cough", "咳嗽", "咳了几聲", NULL
  },
  {
    "die", "暴斃", "當場暴斃", NULL
  },
  {
    "faint", "昏倒", "當場昏倒", NULL
  },
  {
    "flop", "香蕉皮", "踩到香蕉皮... 滑倒!", NULL
  },
  {
    "fly", "飄飄然", "飄飄然", NULL
  },
  {
    "frown", "蹙眉", "蹙眉", NULL
  },
  {
    "gold", "拿金牌", "唱著:『金ㄍㄠ□金ㄍㄠ□  出國比賽! 得冠軍,拿金牌!』",
 NULL
  },
  {
    "gulu", "肚子餓", "的肚子發出咕嚕~~~咕嚕~~~的聲音", NULL
  },
  {
    "haha", "哇哈哈", "哇哈哈哈.....^o^", NULL
  },
  /* {"haha", "大笑","哇哈哈哈哈哈哈哈哈~~~~!!!!!", NULL}, */
  {
    "helpme", "求救", "大喊~~~救命啊~~~~", NULL
  },
  {
    "hoho", "呵呵笑", "呵呵呵笑個不停", NULL
  },
  {
    "happy", "高興", "高興得在地上打滾", NULL
  },
  /* {"happy", "高興", "YA! *^_^*", NULL}, */
  /* {"happy", "", "r-o-O-m....聽了真爽!", NULL}, */
  /* {"hurricane", "Ho---Ryu--Kan!!!", NULL}, */
  {
    "hypnzed", "被催眠", "眼神呆滯,被催眠了……zZzzzz", NULL
  },  /* Thor.980619: 新詞 */
  {
    "idle", "呆住了", "呆住了", NULL
  },
  {
    "jacky", "晃晃", "痞子般的晃來晃去", NULL
  },

#if 0
  /* Thor.0729: 不知其意 */
  {
    "lag", "網路慢", "lllllllaaaaaaaaaaaagggggggggggggg.................", NULL
  },
#endif

  {
    "luck", "幸運", "哇!福氣啦!", NULL
  },
  {
    "macarn", "一種舞", "開始跳起了MaCaReNa~~~~", NULL
  },
  {
    "miou", "喵喵", "喵喵口苗口苗~~~~~", NULL
  },
  {
    "mouth", "扁嘴", "扁嘴中!!", NULL
  },
  {
    "nani", "怎麼會", ":奈ㄝ啊捏??", NULL
  },
  {
    "nose", "流鼻血", "流鼻血", NULL
  },
  {
    "puke", "嘔吐", "嘔吐中", NULL
  },
  /* {"puke", "真惡心,我聽了都想吐", NULL}, */
  {
    "rest", "休息", "休息中,請勿打擾", NULL
  },
  {
    "reverse", "翻肚", "翻肚", NULL
  },
  {
    "room", "開房間", "r-o-O-m-r-O-O-Mmm-rRR........", NULL
  },
  {
    "shake", "搖頭", "搖了搖頭", NULL
  },
  {
    "sleep", "睡著", "趴在鍵盤上睡著了,口水流進鍵盤,造成當機!", NULL
  },
  {
    "so", "就醬子", "就醬子!!", NULL
  },
  {
    "sorry", "道歉", "嗚啊!!我對不起大家,我對不起國家社會~~~~~~嗚啊~~~~~", NULL
  },
  {
    "story", "講古", "開始講古了", NULL
  },
  {
    "strut", "搖擺走", "大搖大擺地走", NULL
  },
  {
    "suicide", "自殺", "自殺", NULL
  },
  {
    "tea", "泡茶", "泡了壺好茶", NULL
  },
  {
    "think", "思考", "歪著頭想了一下", NULL
  },
  {
    "tongue", "吐舌", "吐了吐舌頭", NULL
  },
  {
    "wall", "撞牆", "跑去撞牆", NULL
  },
  {
    "wawa", "哇哇", "哇哇哇~~~~~!!!!!  ~~~>_<~~~", NULL
  },
  {
    "www", "汪汪", "汪汪汪!!!", NULL
  },
  {
    "zzz", "打呼", "呼嚕~~~~ZZzZzzZZZzzZzzzZZ...", NULL
  },
  /* lkchu.981201: add by mat */
  {
    "agree", "同意", "深表同意", NULL
  },
  {
    "ah", "靈光", "苦思良久,忽然靈光一現,不禁呀的一聲。", NULL
  },
  {
    "ak", "插腰", "又氣又無奈的兩手插腰", NULL
  },
  {
    "avert", "害羞", "害羞地轉開視線", NULL
  },
  {
    "dive", "賺錢", "埋首研究怎樣賺大錢。", NULL
  },
  {
    "hiccup", "打嗝", "打嗝中", NULL
  },
  {
    "jealous", "吃醋", "氣鼓鼓地喝了一缸醋", NULL
  },
  {
    "jump", "跳樓", "跳樓自殺", NULL
  },
  {
    "mutter", "低咕", "低聲咕噥著某些事。", NULL
  },
  {
    "scream", "尖叫", "大聲尖叫!! 啊~~~~~~~", NULL
  },
  {
    "snore", "打鼾中", "打鼾中...", NULL
  },
  {
    "stare", "凝視", "靜靜地凝視著天空", NULL
  },
  {
    "stretch", "疲倦", "伸伸懶腰又打了個呵欠很疲倦似的。", NULL
  },
  {
    "sweat", "流汗", "揮汗如雨!!", NULL
  },
  {
    "tear", "流淚", "痛哭流涕中.....", NULL
  },
  {
    "wc", "洗手間", "企洗手間一下 :>", NULL
  },
  {
    "whine", "肚子餓", "肚子餓! :(", NULL
  },
  {
    "whistle", "吹口哨", "吹口哨", NULL
  },
  {
    "ya", "ㄛ耶", "噢~YA! *^_^*", NULL
  },

  {
    NULL, NULL, NULL, NULL
  }
};


static int
condition_action(cu, cmd)
  ChatUser *cu;
  char *cmd;
{
  ChatAction *cap;
  char *verb, buf[256];

  for (cap = condition_data; verb = cap->verb; cap++)
  {
    if (str_equal(verb, cmd))
    {
      sprintf(buf, "%s %s",
    cu->chatid, cap->part1_msg);
      send_to_room(cu->room, buf, cu->userno, MSG_MESSAGE,cu);
      return 1;
    }
  }
  return 0;
}


/* --------------------------------------------- */
/* MUD-like social commands : help               */
/* --------------------------------------------- */


static char *dscrb[] =
{
  "【 Verb + Nick:   動詞 + 對方名字 】  例://kick piggy",
  "【 Verb + Message:動詞 + 要說的話 】  例://sing 天天天藍",
  "【 Verb:動詞 】   ↑↓:舊話重提", NULL
};



static ChatAction *catbl[] =
{
  party_data, speak_data, condition_data, NULL
};


static void
chat_partyinfo(cu, msg)
  ChatUser *cu;
  char *msg;
{
  if (common_client_command)
  {
    send_to_user(cu, "3 動作  交談  狀態", 0, MSG_PARTYINFO);
  }
}


static void
chat_party(cu, msg)
  ChatUser *cu;
  char *msg;
{
  int kind, i;
  ChatAction *cap;
  char buf[80];

  if (!common_client_command)
    return;

  kind = atoi(nextword(&msg));
  if (kind < 0 || kind > 2)
    return;

  sprintf(buf, "%d\t%s", kind, kind == 2 ? "I" : "");

  /* Xshadow: 只有 condition 才是 immediate mode */
  send_to_user(cu, buf, 0, MSG_PARTYLISTSTART);

  cap = catbl[kind];
  for (i = 0; cap[i].verb; i++)
  {
    sprintf(buf, "%-10s %-20s", cap[i].verb, cap[i].chinese);
    send_to_user(cu, buf, 0, MSG_PARTYLIST);
  }

  sprintf(buf, "%d", kind);
  send_to_user(cu, buf, 0, MSG_PARTYLISTEND);
}


#define SCREEN_WIDTH    80
#define MAX_VERB_LEN    8
#define VERB_NO     10


static void
view_action_verb(cu, cmd)   /* Thor.0726: 新加動詞分類顯示 */
  ChatUser *cu;
  int cmd;
{
  int i;
  char *p, *q, *data, *expn, buf[256];
  ChatAction *cap;

  send_to_user(cu, "/c", 0, MSG_CLRSCR);

  data = buf;

  if (cmd < '1' || cmd > '3')
  {             /* Thor.0726: 寫得不好, 想辦法改進... */
    for (i = 0; p = dscrb[i]; i++)
    {
      sprintf(data, "  [//]help %d          - MUD-like 社交動詞   第 %d 類", i
+ 1, i + 1);
      send_to_user(cu, data, 0, MSG_MESSAGE);
      send_to_user(cu, p, 0, MSG_MESSAGE);
      send_to_user(cu, " ", 0, MSG_MESSAGE);    /* Thor.0726: 換行 */
    }
  }
  else
  {
    i = cmd - '1';

    send_to_user(cu, dscrb[i], 0, MSG_MESSAGE);

    expn = buf + 100;       /* Thor.0726: 應該不會overlap吧? */

    *data = '\0';
    *expn = '\0';

    cap = catbl[i];

    for (i = 0; p = cap[i].verb; i++)
    {
      q = cap[i].chinese;

      strcat(data, p);
      strcat(expn, q);

      if (((i + 1) % VERB_NO) == 0)
      {
    send_to_user(cu, data, 0, MSG_MESSAGE);
    send_to_user(cu, expn, 0, MSG_MESSAGE); /* Thor.0726: 顯示中文注解 */
    *data = '\0';
    *expn = '\0';
      }
      else
      {
    strncat(data, "        ", MAX_VERB_LEN - strlen(p));
    strncat(expn, "        ", MAX_VERB_LEN - strlen(q));
      }
    }

    if (i % VERB_NO)
    {
      send_to_user(cu, data, 0, MSG_MESSAGE);
      send_to_user(cu, expn, 0, MSG_MESSAGE);   /* Thor.0726: 顯示中文注解 */
    }
  }
  /* send_to_user(cu, " ",0); *//* Thor.0726: 換行, 需要 " " 嗎? */
}


/* ----------------------------------------------------- */
/* chat user service routines                            */
/* ----------------------------------------------------- */


static ChatCmd chatcmdlist[] =
{
  "act", chat_act, 0,
  "bye", chat_bye, 0,
  "chatroom", chat_chatroom, 1, /* Xshadow: for common client */
  "clear", chat_clear, 0,
  "cloak", chat_cloak, 2,
  "date", chat_date, 0,
  "flags", chat_setroom, 0,
  "help", chat_help, 0,
  "ignore", chat_ignore, 1,
  "invite", chat_invite, 0,
  "join", chat_join, 0,
#ifdef HAVE_KILL_GAME // esp: must follow the 'join' or this will be matched to
 '/j'
  "jkill",kill_join, 0,
  "kstart",kill_start,0,
  "kend",kill_end,0,
  "kill",kill_kill,1,
  "kover",kill_over,0,
  "kquery",kill_police_query,0,
  "kkk",kill_kkk,1,
  "kstate",kill_show_state,1,
  // "kvote",kill_vote,1,
  // "kkile",kill_user_vote,0,
  // "kclearvote",kill_clear_vote,0,
  // "kday",kill_sunrise,1,
  // "knight",kill_sunset,1,
#endif
  "kick", chat_kick, 1,
  "msg", chat_private, 0,
  "nick", chat_nick, 0,
  "operator", chat_makeop, 0,
  "party", chat_party, 1,   /* Xshadow: party data for common client */
  "partyinfo", chat_partyinfo, 1,   /* Xshadow: party info for common
                     * client */

#ifndef STAND_ALONE
  "query", chat_query, 0,
#endif

  "room", chat_list_rooms, 0,
  "unignore", chat_unignore, 1,
  "whoin", chat_list_by_room, 1,
  "wall", chat_broadcast, 2,
  /* fuse.991211 */
  "interview", chat_interview, 2,

  "who", chat_map_chatids_thisroom, 0,
  "list", chat_list_users, 0,
  "topic", chat_topic, 1,
  "to", chat_towards, 1,
  "version", chat_version, 1,

  NULL, NULL, 0
};


/* Thor: 0 不用 exact, 1 要 exactly equal, 2 秘密指令 */


static int
command_execute(cu)
  ChatUser *cu;
{
  char *cmd, *msg, buf[128];
  /* Thor.981108: lkchu patch: chatid + msg 只用 80 bytes 不夠, 改為 128 */
  ChatCmd *cmdrec;
  int match, ch;

  msg = cu->ibuf;
  match = *msg;

  /* Validation routine */

  if (cu->room == NULL)
  {
    /* MUST give special /! or /-! command if not in the room yet */

    if (match == '/' && ((ch = msg[1]) == '!' || (ch == '-' && msg[2] == '!')))
    {
      if (ch == '-')
    fprintf(flog, "cli\t[%d] S%d\n", cu->sno, cu->sock);

      cu->clitype = (ch == '-') ? 1 : 0;
      return (login_user(cu, msg + 2 + cu->clitype));
    }
    else
    {
      return -1;
    }
  }

  /* If not a /-command, it goes to the room. */

  if (match != '/')
  {
    if (match)
    {
      /* fuse.991210: INTERVIEW模式只有op可以說話 */
      if (INTERVIEW(cu->room) && (!(ROOMOP(cu))))
         return 0;

      if (cu->room && !CLOAK(cu))   /* 隱身的人也不能說話哦 */
      {
        char chatid[16];

        sprintf(chatid, "%s:", cu->chatid);
#ifdef HAVE_KILL_GAME
        if (cu->room->rflag & ROOM_KILL_STARTED && 
          cu->room->currenttalker == cu)
          sprintf(buf, "%-10s\033[1;32m%s\033[0m", chatid, msg);
        else
          sprintf(buf, "%-10s%s", chatid, msg);
#else
        sprintf(buf, "%-10s%s", chatid, msg);
#endif
        send_to_room(cu->room, buf, cu->userno, MSG_MESSAGE,cu);
      }
    }
    return 0;
  }

  msg++;
  cmd = nextword(&msg);
  match = 0;

  if (*cmd == '/')
  {
    cmd++;
    if (!*cmd || str_equal("help", cmd))
    {
      cmd = nextword(&msg); /* Thor.0726: 動詞分類 */
      view_action_verb(cu, *cmd);
      match = 1;
    }
    else
    {
     /* fuse.991210: QUIET模式下不作動作了 */
     if (!QUIET(cu->room)) {
      if (party_action(cu, cmd, msg) == 0)
        match = 1;
      else if (speak_action(cu, cmd, msg) == 0)
        match = 1;
      else
        match = condition_action(cu, cmd);
     }
    }
  }
  else
  {
    char *str;

    common_client_command = 0;
    if (*cmd == '-')
    {
      if (cu->clitype)
      {
    cmd++;          /* Xshadow: 指令從下一個字元才開始 */
    common_client_command = 1;
      }
      else
      {
    /* 不是 common client 但送出 common client 指令 -> 假裝沒看到 */
      }
    }

    str_lower(buf, cmd);

    for (cmdrec = chatcmdlist; str = cmdrec->cmdstr; cmdrec++)
    {
      switch (cmdrec->exact)
      {
      case 1:           /* exactly equal */
    match = str_equal(str, buf);
    break;

      case 2:           /* Thor: secret command */
    if (CHATSYSOP(cu))
      match = str_equal(str, buf);
    break;

      default:          /* not necessary equal */
    match = str_match(buf, str) >= 0;
    break;
      }

      if (match)
      {
    cmdrec->cmdfunc(cu, msg);
    break;
      }
    }
  }

  if (!match)
  {
    sprintf(buf, "◆ 指令錯誤:/%s", cmd);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
  }

  return 0;
}


/* ----------------------------------------------------- */
/* serve chat_user's connection                          */
/* ----------------------------------------------------- */


static int
cuser_serve(cu)
  ChatUser *cu;
{
  int ch, len, isize;
  char *str, *cmd, buf[256];

  str = buf;
  len = recv(cu->sock, str, sizeof(buf) - 1, 0);
  if (len < 0)
  {
#ifdef HAVE_KILL_GAME
    if(0 /* cu->room->rflag & ROOM_KILL_STARTED */)
    {
      if(cu->killflag & KILL_DROPED)
      {
        if(time(0) - cu->uptime > 5 * 60) // esp: 5 minutes
        {
          ch = errno;

          exit_room(cu, EXIT_KICK, NULL);
          logit("recv", sys_errlist[ch]);
          return -1;
        }
        else
          return 1;
      }
      else
      {
         // esp: hold this user
         exit_room(cu,EXIT_LOSTCONN,NULL);
         return 1;

      }
    }
    else
    {
      ch = errno;

      exit_room(cu, EXIT_LOSTCONN, NULL);
      logit("recv", sys_errlist[ch]);
      return -1;
    }
#else
    ch = errno;

    exit_room(cu, EXIT_LOSTCONN, NULL);
    logit("recv", sys_errlist[ch]);
    return -1;
#endif

#if 0
    if (ch != EWOULDBLOCK)
    {
      exit_room(cu, EXIT_LOSTCONN, NULL);
      logit("recv", sys_errlist[ch]);
      return -1;
    }

    return 0;           /* would block, so leave it to do later */
#endif
  }

  if (len == 0)
  {
    if (++cu->retry > 100)
      return -1;
    return 0;
  }

#if 0
  /* Xshadow: 將送達的資料忠實紀錄下來 */
  memcpy(logbuf, buf, sizeof(buf));
  for (ch = 0; ch < sizeof(buf); ch++)
    if (!logbuf[ch])
      logbuf[ch] = '$';

  logbuf[len + 1] = '\0';
  logit("recv: ", logbuf);
#endif

#if 0
  logit(cu->userid, str);
#endif

  cu->xdata += len;

  isize = cu->isize;
  cmd = cu->ibuf + isize;
  while (len--)
  {
    ch = *str++;

    if (ch == '\r' || !ch)
      continue;

    if (ch == '\n')
    {
      *cmd = '\0';

      if (command_execute(cu) < 0)
    return -1;

      isize = 0;
      cmd = cu->ibuf;

      continue;
    }

    if (isize < 79)
    {
      *cmd++ = ch;
      isize++;
    }
  }
  cu->isize = isize;
  return 1;
}


/* ----------------------------------------------------- */
/* chatroom server core routines                         */
/* ----------------------------------------------------- */


static int
/* start_daemon(mode)
  int mode; */
servo_daemon(inetd)
  int inetd;
{
  int fd, value;
  char buf[80];
  struct sockaddr_in sin;
  struct linger ld;
  struct rlimit limit;

  /*
   * More idiot speed-hacking --- the first time conversion makes the C
   * library open the files containing the locale definition and time zone.
   * If this hasn't happened in the parent process, it happens in the
   * children, once per connection --- and it does add up.
   */

  time((time_t *) &value);
  gmtime((time_t *) &value);
  strftime(buf, 80, "%d/%b/%Y:%H:%M:%S", localtime((time_t *) &value));

  /* --------------------------------------------------- */
  /* speed-hacking DNS resolve                           */
  /* --------------------------------------------------- */

  dns_init();
#if 0
  gethostname(buf, sizeof(buf));
  gethostbyname(buf);
#endif

  /* --------------------------------------------------- */
  /* adjust the resource limit                           */
  /* --------------------------------------------------- */

  getrlimit(RLIMIT_NOFILE, &limit);
  limit.rlim_cur = limit.rlim_max;
  setrlimit(RLIMIT_NOFILE, &limit);

  limit.rlim_cur = limit.rlim_max = 4 * 1024 * 1024;
  setrlimit(RLIMIT_DATA, &limit);

#ifdef SOLARIS
#define RLIMIT_RSS RLIMIT_AS
  /* Thor.981206: port for solaris 2.6 */
#endif

  setrlimit(RLIMIT_RSS, &limit);

  limit.rlim_cur = limit.rlim_max = 0;
  setrlimit(RLIMIT_CORE, &limit);

#if 0
  limit.rlim_cur = limit.rlim_max = 60 * 20;
  setrlimit(RLIMIT_CPU, &limit);
#endif

  /* --------------------------------------------------- */
  /* detach daemon process                               */
  /* --------------------------------------------------- */

  close(2);
  close(1);

  /* if (mode > 1) */
  if(inetd)
    return 0;

  close(0);

  if (fork())
    exit(0);

  setsid();

  if (fork())
    exit(0);

  /* --------------------------------------------------- */
  /* bind the service port               */
  /* --------------------------------------------------- */

  fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

  /*
   * timeout 方面, 將 socket 改成 O_NDELAY (no delay, non-blocking),
   * 如果能順利送出資料就送出, 不能送出就算了, 不再等待 TCP_TIMEOUT 時間。
   * (default 是 120 秒, 并且有 3-way handshaking 機制, 有可能一等再等)。
   */

#if 1
  fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NDELAY);
#endif

  value = 1;
  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &value, sizeof(value));

  value = 1;
  setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));

  ld.l_onoff = ld.l_linger = 0;
  setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld));

  sin.sin_family = AF_INET;
  sin.sin_port = htons(CHAT_PORT);
  sin.sin_addr.s_addr = htonl(INADDR_ANY);
  memset(sin.sin_zero, 0, sizeof(sin.sin_zero));

  if ((bind(fd, (struct sockaddr *) & sin, sizeof(sin)) < 0) ||
    (listen(fd, SOCK_QLEN) < 0))
    exit(1);

  return fd;
}


#ifdef  SERVER_USAGE
static void
server_usage()
{
  struct rusage ru;

  if (getrusage(RUSAGE_SELF, &ru))
    return;

  fprintf(flog, "\n[Server Usage]\n\n"
    "user time: %.6f\n"
    "system time: %.6f\n"
    "maximum resident set size: %lu P\n"
    "integral resident set size: %lu\n"
    "page faults not requiring physical I/O: %d\n"
    "page faults requiring physical I/O: %d\n"
    "swaps: %d\n"
    "block input operations: %d\n"
    "block output operations: %d\n"
    "messages sent: %d\n"
    "messages received: %d\n"
    "signals received: %d\n"
    "voluntary context switches: %d\n"
    "involuntary context switches: %d\n"
    "gline: %d\n\n",

    (double) ru.ru_utime.tv_sec + (double) ru.ru_utime.tv_usec / 1000000.0,
    (double) ru.ru_stime.tv_sec + (double) ru.ru_stime.tv_usec / 1000000.0,
    ru.ru_maxrss,
    ru.ru_idrss,
    ru.ru_minflt,
    ru.ru_majflt,
    ru.ru_nswap,
    ru.ru_inblock,
    ru.ru_oublock,
    ru.ru_msgsnd,
    ru.ru_msgrcv,
    ru.ru_nsignals,
    ru.ru_nvcsw,
    ru.ru_nivcsw,
    gline);

  fflush(flog);
}
#endif


static void
reaper()
{
  while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0)
    ;
}


static void
sig_trap(sig)
  int sig;
{
  char buf[80];

  sprintf(buf, "signal [%d] at line %d (errno: %d)", sig, gline, errno);
  logit("EXIT", buf);
  fclose(flog);
  exit(1);
}


static void
sig_over()
{
  int fd;

  server_usage();
  logit("OVER", "");
  fclose(flog);
  for (fd = 0; fd < 64; fd++)
    close(fd);
  execl("bin/xchatd", NULL);
}


static void
main_signals()
{
  struct sigaction act;

  /* sigblock(sigmask(SIGPIPE)); */
  /* Thor.981206: 統一 POSIX 標准用法  */

  /* act.sa_mask = 0; */ /* Thor.981105: 標准用法 */
  sigemptyset(&act.sa_mask);
  act.sa_flags = 0;

  act.sa_handler = sig_trap;
  sigaction(SIGBUS, &act, NULL);
  sigaction(SIGSEGV, &act, NULL);
  sigaction(SIGTERM, &act, NULL);

  act.sa_handler = sig_over;
  sigaction(SIGXCPU, &act, NULL);

  act.sa_handler = reaper;
  sigaction(SIGCHLD, &act, NULL);

#ifdef  SERVER_USAGE
  act.sa_handler = server_usage;
  sigaction(SIGPROF, &act, NULL);
#endif

  /* Thor.981206: lkchu patch: 統一 POSIX 標准用法  */
  /* 在此借用 sigset_t act.sa_mask */
  sigaddset(&act.sa_mask, SIGPIPE);
  sigprocmask(SIG_BLOCK, &act.sa_mask, NULL);

}


int
main(argc, argv)
  int argc;
  char *argv[];
{
  int sock, nfds, maxfds, servo_sno;
  ChatUser *cu,/* *userpool,*/ **FBI;
  time_t uptime, tcheck;
  fd_set rset, xset;
  static struct timeval tv = {CHAT_INTERVAL, 0};
  struct timeval tv_tmp; /* Thor.981206: for future reservation bug */

  sock = 0;

  while ((nfds = getopt(argc, argv, "hid")) != -1)
  {
    switch (nfds)
    {
    case 'i':
      sock = 1;
      break;

    case 'd':
      break;

    case 'h':
    default:

      fprintf(stderr, "Usage: %s [options]\n"
        "\t-i  start from inetd with wait option\n"
        "\t-d  debug mode\n"
        "\t-h  help\n",
        argv[0]);
      exit(0);
    }
  }

  servo_daemon(sock);
  /* start_daemon(argc); */

  setgid(BBSGID);
  setuid(BBSUID);
  chdir(BBSHOME);
  umask(077);

  log_init();

  main_signals();

  /* --------------------------------------------------- */
  /* init variable : rooms & users           */
  /* --------------------------------------------------- */

  userpool = NULL;
  strcpy(mainroom.name, MAIN_NAME);
  strcpy(mainroom.topic, MAIN_TOPIC);

  /* --------------------------------------------------- */
  /* main loop                       */
  /* --------------------------------------------------- */

  tcheck = 0;
  servo_sno = 0;

  for (;;)
  {
    uptime = time(0);
    if (tcheck < uptime)
    {
      nfds = maxfds = 0;
      FD_ZERO(&mainfset);
      FD_SET(0, &mainfset);

      tcheck = uptime - CHAT_INTERVAL;

      for (FBI = &mainuser; cu = *FBI;)
      {
    if (cu->uptime < tcheck)
    {
      cuser_free(cu);

      *FBI = cu->unext;

      cu->unext = userpool;
      userpool = cu;
    }
    else
    {
      nfds++;
      sock = cu->sock;
      FD_SET(sock, &mainfset);
      if (maxfds < sock)
        maxfds = sock;

      FBI = &(cu->unext);
    }
      }

      totaluser = nfds;
      fprintf(flog, "MAINTAIN %d user (%d)\n", nfds, maxfds++);
      fflush(flog);

      tcheck = uptime + CHAT_INTERVAL;
    }

    /* ------------------------------------------------- */
    /* Set up the fdsets                 */
    /* ------------------------------------------------- */

    rset = mainfset;
    xset = mainfset;

    /* Thor.981206: for future reservation bug */
    tv_tmp = tv;
    nfds = select(maxfds, &rset, NULL, &xset, &tv_tmp);

#if 0
    {
      char buf[32];
      static int xxx;

      if ((++xxx & 8191) == 0)
      {
    sprintf(buf, "%d/%d", nfds, maxfds);
    logit("MAIN", buf);
      }
    }
#endif

    if (nfds == 0)
    {
      continue;
    }

    if (nfds < 0)
    {
      sock = errno;
      if (sock != EINTR)
      {
    logit("select", sys_errlist[sock]);
      }
      continue;
    }

    /* ------------------------------------------------- */
    /* serve active agents               */
    /* ------------------------------------------------- */

    uptime = time(0);

    for (FBI = &mainuser; cu = *FBI;)
    {
      sock = cu->sock;

      if (FD_ISSET(sock, &rset))
      {
    static int xxx, xno;

    nfds = cuser_serve(cu);

    if ((++xxx & 511) == 0)
    {
      int sno;

      sno = cu->sno;
      fprintf(flog, "rset\t[%d] S%d R%d %d\n", sno, sock, nfds, xxx);
      if (sno == xno)
        nfds = -1;
      else
        xno = sno;
    }
      }
      else if (FD_ISSET(sock, &xset))
      {
    nfds = -1;
      }
      else
      {
    nfds = 0;
      }

      if (nfds < 0 || cu->uptime <= 0)  /* free this client */
      {
    cuser_free(cu);

    *FBI = cu->unext;

    cu->unext = userpool;
    userpool = cu;

    continue;
      }

      if (nfds > 0)
      {
    cu->uptime = uptime;
      }

      FBI = &(cu->unext);
    }

    /* ------------------------------------------------- */
    /* accept new connection                 */
    /* ------------------------------------------------- */

    if (FD_ISSET(0, &rset))
    {

      {
    static int yyy;

    if ((++yyy & 2047) == 0)
      fprintf(flog, "conn\t%d\n", yyy);
      }

      for (;;)
      {
    int value;
    struct sockaddr_in sin;

    value = sizeof(sin);
    sock = accept(0, (struct sockaddr *) &sin, &value);
    if (sock > 0)
    {
      if (cu = userpool)
      {
        userpool = cu->unext;
      }
      else
      {
        cu = (ChatUser *) malloc(sizeof(ChatUser));
      }

      *FBI = cu;

      /* variable initialization */

      memset(cu, 0, sizeof(ChatUser));
      cu->sock = sock;
      cu->tbegin = uptime;
      cu->uptime = uptime;
      cu->sno = ++servo_sno;
      cu->xdata = 0;
      cu->retry = 0;
      memcpy(cu->rhost, &sin.sin_addr, sizeof(struct in_addr));

      totaluser++;

      FD_SET(sock, &mainfset);
      if (sock >= maxfds)
        maxfds = sock + 1;

      {
        int value;

        value = 1;
        setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
          (char *) &value, sizeof(value));
      }

#if 1
      fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NDELAY);
#endif

      fprintf(flog, "CONN\t[%d] %d %s\n",
        servo_sno, sock, str_time(&cu->tbegin));
      break;
    }

    nfds = errno;
    if (nfds != EINTR)
    {
      logit("accept", sys_errlist[nfds]);
      break;
    }

#if 0
    while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0);
#endif
      }
    }

    /* ------------------------------------------------- */
    /* tail of main loop                 */
    /* ------------------------------------------------- */

  }
}

#ifdef HAVE_KILL_GAME

static void kill_clear_user_vote(room)
  ChatRoom *room;
{
  ChatUser *user;
  if(!room)
    return ;

  for(user = mainuser;user;user = user->unext)
  {
    if(!user->userno)
      continue;
    if(user->room != room)
      continue;

    user->votenum = 0;

  }
}

static void kill_enable_police_query(room)
  ChatRoom *room;
{
  ChatUser *user;
  if(!room)
    return ;

  for(user = mainuser;user;user = user->unext)
  {
    if(!user->userno)
      continue;
    if(user->room != room)
      continue;

    if(KILL_ROLE(user) == KILL_POLICE)
      user->killflag &= ~KILL_QUERYED;
  }
}
static void kill_enable_user_talk(room)
  ChatRoom *room;
{
  ChatUser *user;
  if(!room)
    return ;

  for(user = mainuser;user;user = user->unext)
  {
    if(!user->userno)
      continue;
    if(user->room != room)
      continue;

    user->killflag &= ~KILL_TALKED;
  }
}

static void kill_enable_user_vote(room)
  ChatRoom *room;
{
  ChatUser *user;
  if(!room)
    return ;

  for(user = mainuser;user;user = user->unext)
  {
    if(!user->userno)
      continue;
    if(user->room != room)
      continue;

    user->killflag &= ~KILL_VOTED;

  }
}

#if 0

static void kill_clear_vote(cu,msg)
  ChatUser* cu;
  char* msg;
{
  ChatRoom *room;

  room = cu->room;
  if(room == ROOM_ALL || !(room->rflag & ROOM_KILLGAME))
  {
    send_to_user(cu,"$$ 這個房間不是遊戲房間",0,MSG_MESSAGE);
    return;
  }

  if(!(room->rflag & ROOM_KILL_STARTED))
  {
    send_to_user(cu,"$$ 這個房間的遊戲還沒有開始",0,MSG_MESSAGE);
    return;
  }

  if(KILL_ROLE(cu) != KILL_JUDGE)
  {
    send_to_user(cu,"$$ 你不是法官,不能清除投票記錄",0,MSG_MESSAGE);
    return;
  }

  if(room->killstatus != KILL_AFTERDAY)
  {
    send_to_user(cu,"$$ 現在沒有進行投票",0,MSG_MESSAGE);
    return;
  }

  kill_clear_user_vote(room);
  kill_enable_user_vote(room);

  kill_refresh_table(room);

  send_to_room(room,"$$ 現在大家重新投票吧",0,MSG_MESSAGE,cu);

}

#endif

static void kill_clear_user_state(room)
  ChatRoom *room;
{
  ChatUser *user;
  if(!room)
    return ;

  room->currenttalker = NULL;

  for(user = mainuser;user;user = user->unext)
  {
    if(!user->userno)
      continue;
    if(user->room != room)
      continue;

    user->killflag = 0;
    user->killstate = 0;
    user->votenum = 0;

  }

  return ;
}

static ChatUser* kill_find_just_killed(room)
  ChatRoom *room;
{
  ChatUser *user;
  if(!room)
    return NULL;

  for(user = mainuser;user;user = user->unext)
  {
    if(user->room != room)
      continue;

    if(user->killflag & KILL_KILLED && !(user->killflag & KILL_KNOCKOUT))
      return user;

  }

  return NULL;
}

static int chat_get_user_number(room)
  ChatRoom *room;
{
  ChatUser *user;
  int count = 0;
  if(!room)
    return 0;

  /* esp: no cloak , to prevent cheating */
  for(user = mainuser;user;user = user->unext)
  {
    if(!user->userno)
      continue;
    if(user->room != room)
      continue;

    count ++;

  }

  return count;
}

static ChatUser *chat_get_nth_user(room, nth)
  ChatRoom *room;
  int nth;
{
  ChatUser *user;
  int count = 0;
  if(!room)
    return NULL;

  for(user = mainuser;user;user = user->unext)
  {
    if(!user->userno)
      continue;
    if(user->room != room)
      continue;

    count ++;
    if(count == nth)
      break;
  }

  return user;
}

static int kill_get_user_state(whichroom, cu, user)
  ChatRoom *whichroom;
  ChatUser *cu;
  ChatUser *user;
{
  ChatRoom *room;
  room = cu->room;
  if(room != whichroom)
    return KILL_USER_UNKNOWN;


  if(!(room->rflag & ROOM_KILL_STARTED))
    return KILL_USER_UNKNOWN;

  if(cu == user)
    return user->killstate;

  if(KILL_ROLE(cu) == KILL_KILLER)
  {
    if(KILL_ROLE(user) == KILL_KILLER)
      return user->killstate;
  }
/* esp: 大家覺得警察太強大了 */

  else if(KILL_ROLE(cu) == KILL_POLICE
    && room->rflag & ROOM_KILL_STRPOLICE
    && user->killflag & KILL_QUERYED
    && !(cu->killflag & KILL_KILLED))
  {
      return user->killstate;
  }

/* esp: zk 又說了,殺手隱藏得很深…… */
/*
  else if(KILL_ROLE(cu) == KILL_NORMAL)
  {
    if(user->killstate == KILL_USER_KILLED_KILLER
      && user->killflag & KILL_KNOCKOUT)
      return user->killstate;
  }
*/

  if(room->killstatus == KILL_DAY_MURDERED || room->killstatus == KILL_DAY_TALK
    || room->killstatus == KILL_DAY_VOTE || room->killstatus == KILL_AFTERDAY)
    if(user->killflag & KILL_KILLED && !(user->killflag & KILL_KNOCKOUT))
      return KILL_USER_JUSTKILLED;



  if(user->killflag & KILL_KNOCKOUT)
    return KILL_USER_JUSTKILLED;

  return KILL_USER_UNKNOWN;
}

static int kill_can_talk(room,user)
  ChatRoom *room;
  ChatUser *user;
{
  /* esp: return 1 means ok, 0 means can't */

  if(!(room->rflag & ROOM_KILLGAME))
    return 1;

  if(!(room->rflag & ROOM_KILL_STARTED))
    return 1;

  if(room->rflag & ROOM_KILL_WAITDROP)
    return 1;

  if(user->killflag & KILL_KNOCKOUT
    && !(room->rflag & ROOM_KILL_NOGHOST))
    return 1;

  if(room->killstatus == KILL_DAY_TALK
    && room->rflag & ROOM_KILL_DISCUSS)
    return 1; // esp : for argue..;)

  if(room->currenttalker == NULL)
    return 1;

  if(room->killstatus == KILL_DAY_TALK
     || room->killstatus == KILL_BEFORENIGHT)
  {
    if(room->currenttalker == user)
      return 1;
    else
      return 0;
  }
  return 1;
}

static int kill_can_talk_between(room, user1, user2)
  ChatRoom *room;
  ChatUser *user1;
  ChatUser *user2;
{
  /* esp: return 1 means ok, 0 means can't */

  /* What's more. this function only detect if user1 can send
     message to user2, doesn't care about user2 to user1 */

  int rc = 0;
  kill_talk_time_check(room);
  kill_vote_time_check(room);

  if(!(room->rflag & ROOM_KILLGAME))
    return 1;

  if(!(room->rflag & ROOM_KILL_STARTED))
    return 1;

  if(room->rflag & ROOM_KILL_WAITDROP)
    return 1;

  if(user1->killflag & KILL_KNOCKOUT
    && !(room->rflag & ROOM_KILL_NOGHOST))
    return 1;

  switch(room->killstatus)
  {
  case KILL_NIGHT_KILLER:
    if((KILL_ROLE(user1) == KILL_KILLER)
      && KILL_ROLE(user2) == KILL_KILLER)
      rc = 1;
    break;
  case KILL_NIGHT_POLICE:
    if((KILL_ROLE(user1) == KILL_POLICE)
      && KILL_ROLE(user2) == KILL_POLICE)
      rc = 1;
    break;
  case KILL_DAY_MURDERED:
    if(user1->killflag & KILL_KILLED && !(user1->killflag & KILL_KNOCKOUT)
      && room->currenttalker == user1)
      rc = 1;
    break;
  case KILL_DAY_TALK:
    if(
      (!(user1->killflag & KILL_KILLED) && room->currenttalker == user1)
      || room->rflag & ROOM_KILL_DISCUSS)
      rc = 1;
    else
      rc = 0;
    break;
  case KILL_DAY_VOTE:
    rc = 0;
    break;
  case KILL_AFTERDAY:
    if(room->currenttalker == user1)
      rc = 1;
    else
      rc = 0;
    break;
  case KILL_BEFORENIGHT:
    if(user1->killflag & KILL_KILLED && !(user1->killflag & KILL_KNOCKOUT))
      rc = 1;

    break;
  }

  return rc;


}

static void kill_random_role(room)
  ChatRoom *room;
{
  /* esp: the number of roles should be indicated now,
     we random them to guys step by step now.
     judge, police, killer, the left are normal guys.
  */

  int count,countnow;
  int i,r;
  ChatUser *user;
  char buf[200];

  countnow = count = chat_get_user_number(room);
  if(count == 0)
    return;

  srand(time(NULL));

  /* policemen */
  for(i = 0;i<room->policenum;i++)
  {
    r = (float) rand() / RAND_MAX * countnow;
    do
    {
      user = chat_get_nth_user(room,r+1);
      r = (r + 1) % count;
    }while(KILL_ROLE(user) != KILL_NONE);
    // esp: don't allow police query at the beginning
    user->killflag = KILL_POLICE | KILL_QUERYED;
    user->killstate = KILL_USER_POLICE;
    sprintf(buf,"\033[1;33m$$                   您的角色是\033[0m %s%s%s ",
      userkillstatecolors[user->killstate],userkillstates[user->killstate],
      "\033[0m");
    send_to_user(user,"\033[1;33m$$\033[0m",0,MSG_MESSAGE,NULL);
    send_to_user(user,buf,0,MSG_MESSAGE,NULL);
    send_to_user(user,"\033[1;33m$$\033[0m",0,MSG_MESSAGE,NULL);
    countnow --;
  }

  /* killers */
  for(i = 0;i<room->killernum;i++)
  {
    r = (float) rand() / RAND_MAX * countnow;
    do
    {
      user = chat_get_nth_user(room,r+1);
      r = (r + 1) % count;
    }while(KILL_ROLE(user) != KILL_NONE);
    user->killflag = KILL_KILLER;
    user->killstate = KILL_USER_KILLER;
    sprintf(buf,"\033[1;33m$$                   您的角色是\033[0m %s%s%s ",
      userkillstatecolors[user->killstate],userkillstates[user->killstate],
      "\033[0m");
    send_to_user(user,"\033[1;33m$$\033[0m",0,MSG_MESSAGE,NULL);
    send_to_user(user,buf,0,MSG_MESSAGE,NULL);
    send_to_user(user,"\033[1;33m$$\033[0m",0,MSG_MESSAGE,NULL);
    countnow --;
  }

  /* normal guys */
  for(i = 0;i<countnow;i++)
  {
    r = i;
    do
    {
      user = chat_get_nth_user(room,r+1);
      r = (r + 1) % count;
    }while(KILL_ROLE(user) != KILL_NONE);
    user->killflag = KILL_NORMAL;
    user->killstate = KILL_USER_NORMAL;
    sprintf(buf,"\033[1;33m$$                   您的角色是\033[0m %s%s%s ",
      userkillstatecolors[user->killstate],userkillstates[user->killstate],
      "\033[0m");
    send_to_user(user,"\033[1;33m$$\033[0m",0,MSG_MESSAGE,NULL);
    send_to_user(user,buf,0,MSG_MESSAGE,NULL);
    send_to_user(user,"\033[1;33m$$\033[0m",0,MSG_MESSAGE,NULL);

  }

  return;
}

/* esp: return 1 means game done, 0 means not done */
static int kill_is_finished(room)
  ChatRoom* room;
{
  int killernum = 0,policenum = 0,normalnum = 0;
  ChatUser *user;
  int rc = 0;

  if(!room)
    return 0;

  for(user = mainuser;user;user = user->unext)
  {
    if(user->room != room)
      continue;

    if(user->killflag & KILL_KILLED)
    {
    if(KILL_ROLE(user)==KILL_POLICE)
      policenum ++;
    else if(KILL_ROLE(user)==KILL_KILLER)
      killernum ++;
    else if(KILL_ROLE(user)==KILL_NORMAL)
      normalnum ++;
    }
  }


  if((room->policenum - policenum) + (room->normalnum - normalnum) <= room->kil
lernum - killernum)
  {
    send_to_room(room,"\033[1;31m$$ $$ \033[5m殺手們已經占據了多數,殺手們勝利了
。\033[0m \033[1;31m$$ $$\033[0m",0,MSG_MESSAGE,NULL);
    rc = 1;
  }
  else if(killernum == room->killernum)
  {
    send_to_room(room,"\033[1;32m$$ $$ \033[5m 殺手全部抓獲,警察與平民勝利了。
\033[0m \033[1;32m$$ $$\033[0m",0,MSG_MESSAGE,NULL);
    rc = 2;
  }

  if(rc != 0)
  for(user = mainuser;user;user = user->unext)
  {
    if(user->room != room)
      continue;

    if(KILL_ROLE(user) == KILL_KILLER)
    {
       if(rc == 1)
         kill_log_state(user,1);
       else if(rc == 2)
         kill_log_state(user,2);
    }
    else
    {
       if(rc == 1)
         kill_log_state(user,2);
       else if(rc == 2)
         kill_log_state(user,1);
    }

  }

  return  rc;


}

static void kill_next_status(room, cu)
  ChatRoom* room;
  ChatUser* cu;
{

  ChatUser *user;
  char buf[80];

  if(kill_is_finished(room))
  {
    room->killstatus = KILL_NOT_START;
    send_to_room(room,"\033[1;33m$$$$$$ 這一輪遊戲結束了 $$$$$$\033[0m",0,MSG_M
ESSAGE,NULL);
    kill_refresh_table(room);
    room->rflag &= ~ROOM_KILL_STARTED;
    kill_clear_user_state(room);

    return;

  }


  kill_clear_user_vote(room);
  kill_enable_user_talk(room);
  kill_enable_user_vote(room);

  room->currenttalker = NULL;

  switch(room->killstatus)
  {
  case KILL_NOT_START:

    room->killstatus = KILL_NIGHT_KILLER;
    room->tvotebegin = time(0);
    send_to_room(room,"\033[1;33m$$         現在黑夜開始了,殺手請投票殺人吧(/k
ill <user>)\033[0m",0,MSG_MESSAGE,NULL);
    break;
  case KILL_NIGHT_KILLER:
    room->killstatus = KILL_NIGHT_POLICE;
    if(room->rflag & ROOM_KILL_STRPOLICE)
      kill_show_next_killer(room);
    else
      kill_enable_police_query(room);
    if(room->policenum > 0)
      send_to_room(room,"\033[1;33m$$         現在殺手消失了,警察們出來了,他們
發現了殺手\033[0m",0,MSG_MESSAGE,NULL);
    // kill_refresh_table(room);
    // break;  // esp: 警察需要查詢才知道殺手是誰。
  case KILL_NIGHT_POLICE:
    room->killstatus = KILL_DAY_MURDERED;
    send_to_room(room,"\033[1;33m$$         天亮了,大家的眼睛都睜開了\033[0m",
0,MSG_MESSAGE,NULL);
    user = kill_find_just_killed(room);
    if(user)
    {
      sprintf(buf,"\033[1;33m$$         很不幸, %s 被暗殺了\033[0m",user->chat
id);
      send_to_room(room,buf,0,MSG_MESSAGE,NULL);
      room->currenttalker = user;
      user->tkillbegin = time(0);
    }
    send_to_room(room,"\033[1;33m$$         現在請聽被害人發言,結束發言請用/ko
\033[0m",0,MSG_MESSAGE,NULL);
    break;
  case KILL_DAY_MURDERED:
    user = kill_find_just_killed(room);
    if(user)
    {
      user->killflag |= KILL_KNOCKOUT;
    }
    room->killstatus = KILL_DAY_TALK;
    send_to_room(room,"\033[1;33m$$         現在大家輪流發言,發表自己的看法\03
3[0m",0,MSG_MESSAGE,NULL);

    user = kill_get_next_user(room,NULL);

    if(user)
    {
      room->currenttalker = user;
      user->tkillbegin = time(0);
      sprintf(buf,"\033[1;33m$$         現在由 %s 發言,結束請用/ko\033[0m",use
r->chatid);
      send_to_room(room,buf,0,MSG_MESSAGE,NULL);
    }
    break;
  case KILL_DAY_TALK:
    room->killstatus = KILL_DAY_VOTE;
    room->tvotebegin = time(0);
    send_to_room(room,"\033[1;33m$$         現在開始投票,你的決定將決定一個人的
生死(/kill <user>)\033[0m",0,MSG_MESSAGE,NULL);
    break;
  case KILL_DAY_VOTE:
  case KILL_AFTERDAY:
    user = kill_find_just_killed(room);
    if(user)
    {
#if 0 // esp: zk told me that either killer or other should have the chance to
speak
      if(KILL_ROLE(user) == KILL_KILLER)
      {
        sprintf(buf,"\033[1;33m$$         殺手 %s 被抓獲了!\033[0m",user->chat
id);
        send_to_room(room, buf, 0, MSG_MESSAGE, NULL);
        user->killflag |= KILL_KNOCKOUT;
        room->killstatus = KILL_NOT_START;
        kill_next_status(room,cu);
      }
      else
#endif
      {
        sprintf(buf,"\033[1;33m$$         %s 被殺害了!\033[0m",user->chatid);
        send_to_room(room, buf, 0, MSG_MESSAGE, NULL);
        send_to_room(room, "\033[1;33m$$         讓我們來聽聽他有什麼話要說\033
[0m", 0, MSG_MESSAGE, NULL);
        room->killstatus = KILL_BEFORENIGHT;
        room->currenttalker = user;
        user->tkillbegin = time(0);
        kill_refresh_table(room);

      }
    }
    break;
  case KILL_BEFORENIGHT:
    user = kill_find_just_killed(room);
    if(user)
    {
      user->killflag |= KILL_KNOCKOUT;
    }
    room->killstatus = KILL_NIGHT_KILLER;
    send_to_room(room,"\033[1;33m$$         現在黑夜開始了,殺手請投票殺人吧(/k
ill <user>)\033[0m",0,MSG_MESSAGE,NULL);

    break;

  }

  kill_refresh_table(room);
  return;
}

static void kill_start(cu, msg)
  ChatUser* cu;
  char* msg;
{
  int count;
  char buf[128];
  ChatRoom *room = cu->room;

  if(room == ROOM_ALL || !(room->rflag & ROOM_KILLGAME))
  {
    send_to_user(cu,"※ 對不起,這個房間不是遊戲房間",0,MSG_MESSAGE);
    return ;
  }

  if(!ROOMOP(cu))
  {
    send_to_user(cu, msg_not_op, 0, MSG_MESSAGE);
    return ;
  }

  if(room->rflag & ROOM_KILL_STARTED)
  {
    send_to_user(cu, "※ 遊戲已經開始了",0,MSG_MESSAGE);
    return ;
  }

  count = chat_get_user_number(room);

  if(count < 7)
  {
    send_to_user(cu,"※ 對不起,這個房間裡的人太少了,至少要7個人",0,MSG_MESSAG
E);
    return ;
  }

  if(count > 15)
  {
    send_to_user(cu,"※ 對不起,這個房間裡的人太多了,最多要15個人",0,MSG_MESSA
GE);
    return ;
  }

  /* clear the top table */
  sprintf(buf,"/u2%80s"," ");
  send_to_room(room, buf, 0, MSG_MESSAGE,NULL);
  sprintf(buf,"/u3%80s"," ");
  send_to_room(room, buf, 0, MSG_MESSAGE,NULL);
  sprintf(buf,"/u4%80s"," ");
  send_to_room(room, buf, 0, MSG_MESSAGE,NULL);

  room->rflag &= 0x2F;
  kill_clear_user_state(room);
  kill_parse_start_options(room,msg);

  if(count >= 7 && count < 10)
  {
    room->policenum = 0;
    if(room->rflag & ROOM_KILL_POLICE)
      room->policenum = 1;
    if(room->rflag & ROOM_KILL_NOPOLICE)
      room->policenum = 0;
    room->killernum = 2;
    room->normalnum = count - room->policenum - room->killernum ;
  }
  if(count >= 10 && count <= 15)
  {
    room->policenum = 1;
    if(room->rflag & ROOM_KILL_POLICE)
      room->policenum = 1;
    if(room->rflag & ROOM_KILL_NOPOLICE)
      room->policenum = 0;
    room->killernum = 3;
    room->normalnum = count - room->policenum - room->killernum ;
  }
  else if(count >= 15 && count <= 20 )
  {
    room->policenum = 2;
    if(room->rflag & ROOM_KILL_POLICE)
      room->policenum = 1;
    if(room->rflag & ROOM_KILL_NOPOLICE)
      room->policenum = 0;
    room->killernum = 4;
    room->normalnum = count - room->policenum - room->killernum ;
  }
/*
  else if(count >= 15)
  {
    room->policenum = 4;
    room->killernum = 4;
    room->normalnum = count - room->policenum - room->killernum ;
  }
*/

  send_to_room(room,"/c",0,MSG_MESSAGE,NULL);
  send_to_room(room,"\033[1;33m$$         新一輪殺手遊戲開始了!\033[0m",0,MSG_
MESSAGE,NULL);
  if(room->rflag & ROOM_KILL_DISCUSS)
    send_to_room(room,"\033[1;33m$$         可以進行討論\033[0m",0,MSG_MESSAGE,
NULL);
  else
    send_to_room(room,"\033[1;33m$$         不可以進行討論\033[0m",0,MSG_MESSAG
E,NULL);
  if(room->rflag & ROOM_KILL_NOGHOST)
    send_to_room(room,"\033[1;33m$$         死人不可以說話\033[0m",0,MSG_MESSAG
E,NULL);
  else
    send_to_room(room,"\033[1;33m$$         死人可以說話\033[0m",0,MSG_MESSAGE,
NULL);

  if(room->policenum > 0)
  {
    if(room->rflag & ROOM_KILL_STRPOLICE)
      send_to_room(room,"\033[1;33m$$         警察每天開始發現一個殺手\033[0m",
0,MSG_MESSAGE,NULL);
    else
      send_to_room(room,"\033[1;33m$$         警察每天可以查詢一次\033[0m",0,MS
G_MESSAGE,NULL);

    sprintf(buf,"\033[1;33m$$         共有 %d 名警察、%d 名殺手和 %d 名平民\033
[0m",room->policenum,room->killernum,room->normalnum);
  }
  else
    sprintf(buf,"\033[1;33m$$         共有 %d 名殺手和 %d 名平民\033[0m",room->
killernum,room->normalnum);

  send_to_room(room,buf,0,MSG_MESSAGE,NULL);


  kill_random_role(room);

  if(count % 2 == 0)  // esp: 偶數夜間開始
  {
    send_to_room(room,"\033[1;33m$$         因為玩家數為偶數,所以從晚上開始、\
033[0m",0,MSG_MESSAGE,NULL);
    room->killstatus = KILL_NOT_START;
  }
  else  // 奇數白天發言開始
  {
    send_to_room(room,"\033[1;33m$$         因為玩家數為奇數,所以從白天開始、\
033[0m",0,MSG_MESSAGE,NULL);
    room->killstatus = KILL_DAY_MURDERED;
  }

  room->rflag |= ROOM_KILL_STARTED;

  kill_next_status(room,cu);
}

static void kill_end(cu, msg)
  ChatUser* cu;
  char* msg;
{
  ChatRoom *room = cu->room;

  if(room == ROOM_ALL || !(room->rflag & ROOM_KILLGAME))
  {
    send_to_user(cu,"※ 對不起,這個房間不是遊戲房間",0,MSG_MESSAGE);
    return ;
  }

  if(cu != NULL && !ROOMOP(cu))
  {
    send_to_user(cu, msg_not_op, 0, MSG_MESSAGE);
    return ;
  }

  if(!(room->rflag & ROOM_KILL_STARTED))
  {
    send_to_user(cu, "※ 遊戲還沒開始呢",0,MSG_MESSAGE);
    return ;
  }

  send_to_room(room,"\033[1;33m$$         這一輪的遊戲被提前結束了\033[0m",0,MS
G_MESSAGE,NULL);

  room->rflag &= ~ROOM_KILL_STARTED;
  room->killstatus = KILL_NOT_START;

  kill_refresh_table(room);

  kill_clear_user_state(room);

}

#if 0
static void kill_sunset(cu,msg)
  ChatUser* cu;
  char* msg;
{
  ChatRoom *room;


  room = cu->room;
  if(room == ROOM_ALL || !(room->rflag & ROOM_KILLGAME))
  {
    send_to_user(cu,"$$ 這個房間不是遊戲房間",0,MSG_MESSAGE);
    return;
  }

  if(!(room->rflag & ROOM_KILL_STARTED))
  {
    send_to_user(cu,"$$ 這個房間的遊戲還沒有開始",0,MSG_MESSAGE);
    return;
  }

  if(KILL_ROLE(cu) != KILL_JUDGE)
  {
    send_to_user(cu,"$$ 你不是法官,不能使天黑下去",0,MSG_MESSAGE);
    return;
  }

  if(room->killstatus != KILL_BEFORENIGHT)
  {
    send_to_user(cu,"$$ 現在還沒到天黑的時候呢",0,MSG_MESSAGE);
    return;
  }

  kill_next_status(room,cu);

  cu->killstate = KILL_JUDGE_FRESH;
}
static void kill_sunrise(cu,msg)
  ChatUser* cu;
  char* msg;
{
  ChatRoom *room;

  room = cu->room;
  if(room == ROOM_ALL || !(room->rflag & ROOM_KILLGAME))
  {
    send_to_user(cu,"$$ 這個房間不是遊戲房間",0,MSG_MESSAGE);
    return;
  }

  if(!(room->rflag & ROOM_KILL_STARTED))
  {
    send_to_user(cu,"$$ 這個房間的遊戲還沒有開始",0,MSG_MESSAGE);
    return;
  }

  if(KILL_ROLE(cu) != KILL_JUDGE)
  {
    send_to_user(cu,"$$ 你不是法官,不能使天亮起來",0,MSG_MESSAGE);
    return;
  }

  if(room->killstatus != KILL_NIGHT_POLICE)
  {
    send_to_user(cu,"$$ 現在還不是后半夜呢",0,MSG_MESSAGE);
    return;
  }

  kill_next_status(room,cu);

  cu->killstate = KILL_JUDGE_FRESH;
}

static void kill_vote(cu,msg)
  ChatUser* cu;
  char* msg;
{
  ChatRoom *room;


  room = cu->room;
  if(room == ROOM_ALL || !(room->rflag & ROOM_KILLGAME))
  {
    send_to_user(cu,"\033[1;35m$$ 這個房間不是遊戲房間\033[0m",0,MSG_MESSAGE);
    return;
  }

  if(!(room->rflag & ROOM_KILL_STARTED))
  {
    send_to_user(cu,"$$ 這個房間的遊戲還沒有開始",0,MSG_MESSAGE);
    return;
  }

  if(KILL_ROLE(cu) != KILL_JUDGE)
  {
    send_to_user(cu,"$$ 你不是法官,不能執行投票",0,MSG_MESSAGE);
    return;
  }

  if(room->killstatus != KILL_DAY)
  {
    send_to_user(cu,"$$ 現在還不能投票",0,MSG_MESSAGE);
    return;
  }

  kill_next_status(room,cu);

  cu->killstate = KILL_JUDGE_FRESH;
}

#endif

static void kill_kill(cu,msg)
  ChatUser* cu;
  char* msg;
{
  ChatRoom *room;
  char *twit,buf[80];
  ChatUser *xuser;


  room = cu->room;
  if(room == ROOM_ALL || !(room->rflag & ROOM_KILLGAME))
  {
    send_to_user(cu,"\033[1;35m$$ 這個房間不是遊戲房間\033[0m",0,MSG_MESSAGE);
    return;
  }

  if(!(room->rflag & ROOM_KILL_STARTED))
  {
    send_to_user(cu,"\033[1;35m$$ 這個房間的遊戲還沒有開始\033[0m",0,MSG_MESSAG
E);
    return;
  }

  if(room->rflag & ROOM_KILL_WAITDROP)
  {
    send_to_user(cu,"\033[1;35m$$ 還是先等等斷線的同志吧。\033[0m",0,MSG_MESSAG
E);
    return;
  }

  if(cu->killflag & KILL_KILLED)
  {
    send_to_user(cu,"\033[1;35m$$ 死人就不能再殺別人了:(\033[0m",0,MSG_MESSAGE)
;
    return;
  }

  if(room->killstatus != KILL_NIGHT_KILLER  && room->killstatus != KILL_DAY_VOT
E)
  {
    send_to_user(cu,"\033[1;35m$$ 現在還不能殺人\033[0m",0,MSG_MESSAGE);
    return;
  }


  twit = nextword(&msg);
  xuser = cuser_by_chatid(twit);

  if(xuser == NULL)
  {
    xuser = cuser_by_userid(twit);
  }

  if(xuser == NULL || xuser->room != room)
  {
    sprintf(buf, msg_no_such_id, twit);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
    return;
  }


  if(room->killstatus == KILL_NIGHT_KILLER
    && KILL_ROLE(cu) != KILL_KILLER)
  {
    send_to_user(cu,"\033[1;35m$$ 很抱歉,目前只有殺手才能投票\033[0m",0,MSG_ME
SSAGE);
    return;
  }

  if(cu->killflag & KILL_VOTED)
  {
    send_to_user(cu,"\033[1;35m$$ 抱歉啦,這次你已經投了一票了\033[0m",0,MSG_ME
SSAGE);
    return;
  }

  if(xuser->killflag & KILL_KNOCKOUT)
  {
    send_to_user(cu,"\033[1;35m$$ 他已經是個死人了,你就行行好吧。\033[0m",0,MS
G_MESSAGE);
    return;
  }

/* esp: 既然大家都覺得晚上也應該可以殺殺手,那就殺 */
/*
  if(room->killstatus == KILL_NIGHT_KILLER)
  {
    if(KILL_ROLE(xuser) == KILL_KILLER)
    {
      send_to_user(cu,"\033[1;35m$$ 不要殺害同類嗎。\033[0m",0,MSG_MESSAGE);
      return;
    }
  }
*/

  // esp: 殺手在白天可以投殺手的票嗎?我就讓他可以,這樣可能更隱蔽,KAKA

  xuser->votenum ++;
  cu->killflag |= KILL_VOTED;


  if(room->killstatus != KILL_NIGHT_KILLER)
  {
    strcpy(cu->killid,xuser->chatid);
    sprintf(buf,"\033[1;31m$$         你投了 %s 一票!\033[0m",xuser->chatid);
    send_to_user(cu,buf,0,MSG_MESSAGE);
    // sprintf(buf,"\033[1;31m$$         %s 投了 %s 一票!\033[0m",cu->chatid,x
user->chatid);
    // send_to_room(room,buf,0,MSG_MESSAGE,NULL);
  }
  else
  {
    sprintf(buf,"\033[1;31m$$         你投了 %s 一票!\033[0m",xuser->chatid);
    send_to_user(cu,buf,0,MSG_MESSAGE);
  }
 //  kill_refresh_table(room);
  kill_check_vote(room,cu);
}

#if 0

static void kill_user_vote(cu,msg)
  ChatUser *cu;
  char* msg;
{
  char *twit, buf[80];
  ChatUser *xuser;
  ChatRoom *room;

  if(room == ROOM_ALL || !(room->rflag & ROOM_KILLGAME))
  {
    send_to_user(cu,"$$ 這個房間不是遊戲房間",0,MSG_MESSAGE);
    return;
  }

  if(!(room->rflag & ROOM_KILL_STARTED))
  {
    send_to_user(cu,"$$ 這個房間的遊戲還沒有開始",0,MSG_MESSAGE);
    return;
  }

  if(room->killstatus != KILL_NIGHT_KILLER
    && room->killstatus != KILL_DAY_VOTE)
  {
    send_to_user(cu,"$$ 現在沒有舉辦投票",0,MSG_MESSAGE);
    return ;
  }

  if(cu->killflag & KILL_KILLED)
  {
    send_to_user(cu,"$$ 抱歉啦,你現在無權投票",0,MSG_MESSAGE);
    return;
  }

  if(KILL_ROLE(cu) == KILL_JUDGE)
  {
    send_to_user(cu,"$$ 抱歉啦,法官是不能投票的",0,MSG_MESSAGE);
    return;
  }

  if(cu->killflag & KILL_VOTED)
  {
    send_to_user(cu,"$$ 抱歉啦,這次你已經投了一票了",0,MSG_MESSAGE);
    return;
  }

  /* esp: 殺自己怎麼辦?? --回答是,才不管呢 */
  twit = nextword(&msg);
  xuser = cuser_by_chatid(twit);

  if (xuser == NULL)
  {                       /* Thor.980604: 用 userid也嘛通 */
    xuser = cuser_by_userid(twit);
  }

  if (xuser == NULL)
  {
    sprintf(buf, msg_no_such_id, twit);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
    return;
  }

  room = cu->room;
  if (room != xuser->room || CLOAK(xuser))
  {
    sprintf(buf, msg_not_here, twit);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
    return;
  }

  if(room->killstatus == KILL_NIGHT_KILLER
    && KILL_ROLE(cu) != KILL_KILLER)
  {
    send_to_user(cu,"$$ 很抱歉,目前只有殺手才能投票",0,MSG_MESSAGE);
    return;
  }

  xuser->votenum ++;
  kill_refresh_table(room);
}

#endif

static void kill_refresh_table(room)
  ChatRoom* room;
{
  int c,line;
  ChatUser *cu,*user;
  char buf[384],buf2[128];

  if(!room)
    return ;


  for(cu = mainuser;cu;cu = cu->unext)
  {
    if(!cu->userno)
      continue;
    if(cu->room != room)
      continue;

    line = 2;
    sprintf(buf,"/u%d ",line);
    for(c = 0,user = mainuser;user;user = user->unext)
    {
      if(!user->userno)
        continue;
      if(user->room != room)
        continue;

      if(room->killstatus == KILL_NOT_START)
        sprintf(buf2,"%s%-12s%s%s",userkillstatecolors[user->killstate],user->c
hatid,"\033[0m",(c < 4 ? "│" : "  "));
      else
        sprintf(buf2,"%s%s%-12s%s%s",userkillstatecolors[kill_get_user_state(ro
om,cu,user)],room->currenttalker == user?"\033[7m":"",user->chatid,"\033[0m",(c
 < 4 ? "│" : "  "));
      strcat(buf,buf2);
      if(++c == 5) // esp: I put 5 user per line, so i can more space
      {
        send_to_user(cu,buf,0,MSG_MESSAGE);
        line ++;
        c = 0;
        sprintf(buf,"/u%d ",line);
      }
    }
    if (c > 0)    send_to_user(cu, buf, 0, MSG_MESSAGE);
  }
}

static void kill_over(cu, msg)
  ChatUser *cu;
  char *msg;
{
  ChatRoom *room;

  room = cu->room;

  if(room == ROOM_ALL || !(room->rflag & ROOM_KILLGAME))
  {
    send_to_user(cu,"\033[1;35m$$ 這個房間不是遊戲房間\033[0m",0,MSG_MESSAGE);
    return;
  }

  if(!(room->rflag & ROOM_KILL_STARTED))
  {
    send_to_user(cu,"\033[1;35m$$ 這個房間的遊戲還沒有開始\033[0m",0,MSG_MESSAG
E);
    return;
  }

  if(room->rflag & ROOM_KILL_WAITDROP)
  {
    send_to_user(cu,"\033[1;35m$$ 還是等等斷線的同志吧,多說會兒,多說兒會兒。\
033[0m",0,MSG_MESSAGE);
    return;
  }

  if(room->killstatus == KILL_DAY_TALK
    || room->killstatus == KILL_DAY_MURDERED
    || room->killstatus == KILL_BEFORENIGHT
    && room->currenttalker == cu
  )
  {

    kill_talk_over(room,cu);
  }
  else
  {
    send_to_user(cu,"\033[1;35m$$ 您現在根本就什麼都沒幹\033[0m",0,MSG_MESSAGE)
;
  }

  return;
}

#if 0

static void kill_killer_over(room,cu)
  ChatRoom *room;
  ChatUser *cu;
{
  if(cu->killflag & KILL_KNOCKOUT)
  {
    send_to_user(cu,"\033[1;35m$$ 你的魂靈還沒有回到身體裡呢\033[0m",0,MSG_MESS
AGE);
    return;
  }

  if(cu->killflag & KILL_TALKED)
  {
    send_to_user(cu,"\033[1;35m$$ 你已經結束晚上的活動了\033[0m",0,MSG_MESSAGE)
;
    return;
  }

  cu->killflag |= KILL_TALKED;

  kill_on_user_over(room);
}

static void kill_police_over(room,cu)
  ChatRoom *room;
  ChatUser *cu;
{
  if(cu->killflag & KILL_KNOCKOUT)
  {
    send_to_user(cu,"\033[1;35m$$ 你的魂靈還沒有回到身體裡呢\033[0m",0,MSG_MESS
AGE);
    return;
  }

  if(cu->killflag & KILL_TALKED)
  {
    send_to_user(cu,"\033[1;35m$$ 你已經結束晚上的活動了\033[0m",0,MSG_MESSAGE)
;
    return;
  }

  cu->killflag |= KILL_TALKED;
  kill_on_user_over(room);
}

#endif

static void kill_talk_over(room,cu)
  ChatRoom *room;
  ChatUser *cu;
{
  ChatUser *user;
  char buf[128];

  if(cu->killflag & KILL_KNOCKOUT)
  {
    send_to_user(cu,"\033[1;35m$$ 你的魂靈還沒有回到身體裡呢\033[0m",0,MSG_MESS
AGE);
    return;
  }

  if(cu->killflag & KILL_TALKED)
  {
    send_to_user(cu,"\033[1;35m$$ 你已經結束發言了\033[0m",0,MSG_MESSAGE);
    return;
  }

  if(cu != room->currenttalker)
  {
    send_to_user(cu,"\033[1;35m$$ 你根本就沒有在發言\033[0m",0,MSG_MESSAGE);
    return;
  }
  sprintf(buf,"\033[1;33m$$         %s 的發言已經結束了\033[0m",cu->chatid);
  send_to_room(room,buf,0,MSG_MESSAGE,NULL);

  cu->killflag |= KILL_TALKED;
  if(!kill_on_user_over(room))
  {
    user = kill_get_next_user(room,cu);
    room->currenttalker = user;
    user->tkillbegin = time(0);
    sprintf(buf,"\033[1;33m$$         現在由 %s 發言\033[0m",user->chatid);
    send_to_room(room,buf,0,MSG_MESSAGE,NULL);
    kill_refresh_table(room);
  }
}

static int kill_on_user_over(room)
  ChatRoom *room;
{
  ChatUser *user;
  int count = 0;

  for(user = mainuser;user;user = user->unext)
  {
    if(!user->userno)
      continue;
    if(user->room != room)
      continue;

    if(room->killstatus == KILL_DAY_MURDERED)
    {
      kill_next_status(room,NULL);
      return 1;
    }
    if(room->killstatus == KILL_BEFORENIGHT)
    {
      kill_next_status(room,NULL);
      return 1;
    }
    else if(room->killstatus == KILL_DAY_TALK)
    {
        if(user->killflag & KILL_TALKED || user->killflag & KILL_KNOCKOUT)
        {
          count ++;
          if(count == room->policenum + room->normalnum + room->killernum)
          {
            kill_next_status(room,NULL);
            return 1;
          }
        }
    }
  }

  return 0;

}

static ChatUser *kill_get_next_user(room,cu)
  ChatRoom *room;
  ChatUser *cu;
{
  ChatUser *user,*prevuser;
  if(!room)
    return NULL;

  prevuser = NULL;
  for(user = mainuser;user;user = user->unext)
  {
    if(!user->userno)
      continue;
    if(user->room != room)
      continue;

    if(room->killstatus == KILL_DAY_TALK)
    {
      if(!(user->killflag & KILL_KNOCKOUT))
      {
        if(prevuser == cu)
          return user;
        prevuser = user;
      }
    }
  }


  return NULL;
}

static void kill_check_vote(room,cu)
  ChatRoom* room;
  ChatUser* cu;
{
  int max = 0, count = 0;

  ChatUser *user,*maxuser = NULL;
  char buf[128];

  for(user = mainuser;user;user = user->unext)
  {
    if(user->room != room)
      continue;

    if(room->killstatus == KILL_NIGHT_KILLER)
    {
      if(KILL_ROLE(user) == KILL_KILLER && !(user->killflag & KILL_KNOCKOUT)
        && !(user->killflag & KILL_VOTED))
        return ;
    }
    else if(room->killstatus == KILL_DAY_VOTE)
    {
      if(!(user->killflag & KILL_KILLED)
        && !(user->killflag & KILL_VOTED))
      {
        return ;
      }
    }
    else
      return;
  }


  for(user = mainuser;user;user = user->unext)
  {
    if(user->room != room)
      continue;

    if(user->killflag & (KILL_KNOCKOUT | KILL_KILLED))
      continue;

    /* esp: zk told me to show the vote detail together */
    if(room->killstatus == KILL_DAY_VOTE)
    {
      sprintf(buf,"\033[1;31m$$         %s 投了 %s 一票!\033[0m",user->chatid,
user->killid);
      send_to_room(room,buf,0,MSG_MESSAGE,NULL);
    }

    if(user->votenum > max)
    {
      maxuser = user;
      max = user->votenum;
      count = 1;
    }
    else if(user->votenum == max)
    {
      count ++;
    }
  }


  if(count != 1)
  {
    kill_clear_user_vote(room);
    kill_enable_user_vote(room);

    // kill_refresh_table(room);

    if(room->killstatus == KILL_NIGHT_KILLER)
      send_to_room(room,"\033[1;35m$$ 有并列第一的,現在大家重新投票吧\033[0m",
0,MSG_MESSAGE,cu);
    else
      send_to_room(room,"\033[1;35m$$ 有并列第一的,現在大家重新投票吧\033[0m",
0,MSG_MESSAGE,NULL);
    return;
  }

  user = maxuser;
  user->killflag |= KILL_KILLED;
  if(room->killstatus == KILL_NIGHT_KILLER)
  {
    if(KILL_ROLE(user) == KILL_KILLER)
      user->killstate = KILL_USER_KILLED_KILLER;
    else if(KILL_ROLE(user) == KILL_POLICE)
      user->killstate = KILL_USER_KILLED_POLICE;
    else if(KILL_ROLE(user) == KILL_NORMAL)
      user->killstate = KILL_USER_KILLED_NORMAL;
  }
  else
  {
    // esp: should be KILL_DAY_VOTE
    if(KILL_ROLE(user) == KILL_KILLER)
      user->killstate = KILL_USER_KILLED_KILLER;
    else if(KILL_ROLE(user) == KILL_POLICE)
      user->killstate = KILL_USER_MISKILLED_POLICE;
    else if(KILL_ROLE(user) == KILL_NORMAL)
      user->killstate = KILL_USER_MISKILLED_NORMAL;
  }


  kill_next_status(room,NULL);

}

static void kill_on_reconnect(room)
  ChatRoom* room;
{
  ChatUser *user;
  for(user = mainuser;user;user = user->unext)
  {
    if(user->room != room)
      continue;

    if(user->killflag & KILL_DROPED)
      return;
  }

  room->rflag &= ~ROOM_KILL_WAITDROP;
}

static void kill_parse_start_options(room,msg)
  ChatRoom *room;
  char* msg;
{
  int police = 0,nopolice = 0,discuss = 0,noghost = 0;
  int strpolice = 0;
  char *str;

  if(!msg)
    return ;
  str = nextword(&msg);
  while(str && *str)
  {
    if(!strcmp(str,"police"))
      police = 1;
    else if(!strcmp(str,"nopolice"))
      nopolice = 1;
    else if(!strcmp(str,"discuss"))
      discuss = 1;
    else if(!strcmp(str,"noghost"))
      noghost = 1;
    else if(!strcmp(str,"strpolice"))
      strpolice = 1;
    str = nextword(&msg);
  }
  if(police == 1)
    room->rflag |= ROOM_KILL_POLICE;
  if(nopolice == 1)
    room->rflag |= ROOM_KILL_NOPOLICE;
  if(discuss == 1)
    room->rflag |= ROOM_KILL_DISCUSS;
  if(noghost == 1)
    room->rflag |= ROOM_KILL_NOGHOST;
  if(strpolice == 1)
    room->rflag |= ROOM_KILL_STRPOLICE;

}

static void kill_police_query(cu, msg)
  ChatUser *cu;
  char *msg;
{
  ChatRoom *room;
  char *twit,buf[80];
  ChatUser *xuser;


  room = cu->room;
  if(room == ROOM_ALL || !(room->rflag & ROOM_KILLGAME))
  {
    send_to_user(cu,"\033[1;35m$$ 這個房間不是遊戲房間\033[0m",0,MSG_MESSAGE);
    return;
  }

  if(!(room->rflag & ROOM_KILL_STARTED))
  {
    send_to_user(cu,"\033[1;35m$$ 這個房間的遊戲還沒有開始\033[0m",0,MSG_MESSAG
E);
    return;
  }

  if(cu->killflag & KILL_KILLED)
  {
    send_to_user(cu,"\033[1;35m$$ 死人就不能再查詢別人了:(\033[0m",0,MSG_MESSAG
E);
    return;
  }

  twit = nextword(&msg);
  xuser = cuser_by_chatid(twit);

  if(xuser == NULL)
  {
    xuser = cuser_by_userid(twit);
  }

  if(xuser == NULL || xuser->room != room)
  {
    sprintf(buf, msg_no_such_id, twit);
    send_to_user(cu, buf, 0, MSG_MESSAGE);
    return;
  }


  if(KILL_ROLE(cu) != KILL_POLICE)
  {
    send_to_user(cu,"\033[1;35m$$ 只有警察才能查詢別人\033[0m",0,MSG_MESSAGE);
    return;
  }

  if(room->rflag & ROOM_KILL_STRPOLICE)
  {
    send_to_user(cu,"\033[1;35m$$ 不必查詢,你會慢慢知道的\033[0m",0,MSG_MESSAG
E);
    return;
  }

  if(cu->killflag & KILL_QUERYED)
  {
    send_to_user(cu,"\033[1;35m$$ 抱歉啦,今天你已經查詢過了\033[0m",0,MSG_MESS
AGE);
    return;
  }

  xuser->killflag |= KILL_QUERYED ;
  cu->killflag |= KILL_QUERYED;


  sprintf(buf,"\033[1;31m$$ %s 的身份是 %s%s%s!\033[0m",
    xuser->chatid, userkillstatecolors[xuser->killstate],
    userkillstates[xuser->killstate],"\033[0m");
  send_to_user(cu,buf,0,MSG_MESSAGE);

}

static void kill_show_next_killer(room)
  ChatRoom *room;
{
  ChatUser * user;

  for(user = mainuser;user;user = user->unext)
  {
    if(!user->userno)
      continue;
    if(user->room != room)
      continue;

    if(KILL_ROLE(user)==KILL_KILLER && !(user->killflag & KILL_QUERYED))    {
       user->killflag |= KILL_QUERYED;
       return;
    }
  }
}

static void kill_talk_time_check(room)
  ChatRoom *room;
{
  int timelimit; // in seconds
  if(!(room->rflag & ROOM_KILL_STARTED))
    return;

  if(NULL == room->currenttalker)
    return;

  if(room->killstatus == KILL_DAY_MURDERED
    || room->killstatus == KILL_BEFORENIGHT)
    timelimit = 60;
  else if(room->killstatus == KILL_DAY_TALK)
    timelimit = 45;
  else
    return;

  if(time(0) - room->currenttalker->tkillbegin > timelimit)
    kill_talk_over(room,room->currenttalker);

  return;
}

static void kill_vote_time_check(room)
  ChatRoom *room;
{
  int timelimit = 30;
  if(!(room->rflag & ROOM_KILL_STARTED))
    return;
  if(room->killstatus != KILL_NIGHT_KILLER
    && room->killstatus != KILL_DAY_VOTE)
  return;

  if(room->killstatus == KILL_NIGHT_KILLER)
    timelimit = 60;

  if(time(0) - room->tvotebegin > timelimit)
  {
    kill_auto_vote(room);
  }

  return;
}


static ChatUser* 
kill_auto_vote(room)
  ChatRoom *room;
{
  int max = 0, count = 0;
  int r,total = 0;

  ChatUser *user,*maxuser = NULL;
  char buf[128];

  for(user = mainuser;user;user = user->unext)
  {
    if(user->room != room)
      continue;

    if(user->killflag & (KILL_KNOCKOUT | KILL_KILLED))
      continue;


    if(user->votenum > max)
    {
      maxuser = user;
      max = user->votenum;
      count = 1;
    }
    else if(user->votenum == max  && max != 0)
    {
      count ++;
    }
  }

  if(count == 0)
  {
    // nobody voted
    total = room->policenum + room->killernum + room->normalnum;
    srand(time(0));

    r = (float) rand() / RAND_MAX * total;
    do {
      user = chat_get_nth_user(room,r+1);
      r = (r + 1) % total;
    } while( user->killflag & KILL_KILLED);

    // now , the 'user' should be the poor guy.
  }
  else if(count != 1)
  {
     // more than one elected, we random one out
     srand(time(0));
     total = room->policenum + room->killernum + room->normalnum;
     r = (float) rand() / RAND_MAX * count;
     count = 0;
     for(user = mainuser;user;user=user->unext)
     {
        if(user->room != room)
          continue;
        if(user->userno == 0)
          continue;
        if(user->killflag & KILL_KILLED)
          continue;
        if(user->votenum != max)
          continue;
        count ++;
        if(count >= r+1)
          break;
     }
   }
   else
   {
      user = maxuser;
    }

   maxuser = user;

   for(user = mainuser;user;user=user->unext)
   {
      if(user->room != room)
        continue;
      if(user->userno == 0)
        continue;
      if(user->killflag & KILL_KILLED)
        continue;

      if(room->killstatus == KILL_NIGHT_KILLER)
        if(KILL_ROLE(user) != KILL_KILLER)
          continue;

      if(!(user->killflag & KILL_VOTED))
      {
        maxuser->votenum ++;
        user->killflag |= KILL_VOTED;

        if(room->killstatus != KILL_NIGHT_KILLER)
        {
           strcpy(user->killid,maxuser->chatid);
           sprintf(buf,"\033[1;31m$$         你投了 %s 一票!\033[0m",maxuser->
chatid);
           send_to_user(user,buf,0,MSG_MESSAGE);
         }
         else
         {
           sprintf(buf,"\033[1;31m$$         你投了 %s 一票!\033[0m",maxuser->
chatid);
           send_to_user(user,buf,0,MSG_MESSAGE);
          }
            kill_check_vote(room,user);
       }
    }
}

static void kill_kkk(cu,msg)
  ChatUser *cu;
  char* msg;
{
  ChatRoom *room;


  room = cu->room;
  if(room == ROOM_ALL || !(room->rflag & ROOM_KILLGAME))
  {
    send_to_user(cu,"\033[1;35m$$ 這個房間不是遊戲房間\033[0m",0,MSG_MESSAGE);
    return;
  }

  if(!(room->rflag & ROOM_KILL_STARTED))
  {
    send_to_user(cu,"\033[1;35m$$ 這個房間的遊戲還沒有開始\033[0m",0,MSG_MESSAGE);
    return;
  }

  kill_talk_time_check(room);
  kill_vote_time_check(room);

}

static void kill_log_state(user, nmode)
  ChatUser *user;
  int nmode;
{
  char fpath[80];
  FILE *fp;
  KILL_STATE killstate;


  memset(&killstate,0,sizeof(KILL_STATE));
  usr_fpath(fpath,user->userid,".KILLGAME");
  fp = fopen(fpath,"rb");
  if(NULL != fp)
  {
    fread(&killstate,sizeof(KILL_STATE),1,fp);
    fclose(fp);
  }

  switch(nmode)
  {
  case 1:
    killstate.won ++;
    break;
  case 2:
    killstate.lost ++;
    break;
  case 3:
    killstate.dropped ++;
    break;
  default:
    break;
  }

  killstate.lastgame = nmode;
  killstate.tlastgame = time(0);

  fp = fopen(fpath,"wb+");
  if(NULL != fp)
  {
    fwrite(&killstate,sizeof(KILL_STATE),1,fp);
    fclose(fp);
  }

}

static void
kill_show_state(cu, msg)
  ChatUser *cu;
  char *msg;
{
  char fpath[80], *twit;
  FILE *fp;
  KILL_STATE killstate;
  struct stat st;
  static char* gamedesc[4] = {
    "","Won","Lost","Dropped"};


  twit = nextword(&msg);
  if(twit == NULL || *twit == 0)
    twit = cu->userid;

  usr_fpath(fpath,twit,FN_ACCT);
  if(-1 == stat(fpath,&st))
  {
    send_to_user(cu,"\033[1;35m$$ 沒有這位使用者!\033[0m",0,MSG_MESSAGE);
    return;
  }
  memset(&killstate,0,sizeof(KILL_STATE));

  usr_fpath(fpath,twit,".KILLGAME");
  fp = fopen(fpath,"rb");
  if(NULL != fp)
  {
    fread(&killstate,sizeof(KILL_STATE),1,fp);
    fclose(fp);

    sprintf(fpath,"\033[1;33m$$ %s:\033[m",twit);
    send_to_user(cu,fpath,0,MSG_MESSAGE);
    sprintf(fpath,"\033[1;33m$$ Scores: %d-%d-%d\033[m",
      killstate.won,killstate.lost,killstate.dropped);
    send_to_user(cu,fpath,0,MSG_MESSAGE);
    sprintf(fpath,"\033[1;33m$$ Lastgame: %s at %s\033[m",
      gamedesc[killstate.lastgame],Btime(&(killstate.tlastgame)));
    send_to_user(cu,fpath,0,MSG_MESSAGE);

  }
  else
  {
    send_to_user(cu,"\033[1;35m$$ 這位使用者沒有玩過遊戲\033[0m",0,MSG_MESSAGE);
  }
  return;

}

#endif /* HAVE_KILL_GAME */





--
                                
      □□□     □□□   □□□     □□□
                            □
                             □
                             □                    
      □□□     □□□□□   □□□□□     □□□  ~~唉 呦呦~~~~~~ 
--
※ Origin: 【北郵真情流露】 <bupt.org> ◆ From: 202.112.254.105
精華選讀←離開[主題上]主題下(k)上篇(j)下篇S/a搜尋 G串列 TAB精華 ↑↓捲 Pg/Space翻