Re-enabled chat system equivalent with features of PennMUSH 1.8.3p12..
authorRick L Bird <nveid@yahoo.com>
Fri, 6 May 2011 04:40:52 +0000 (00:40 -0400)
committerRick L Bird <nveid@yahoo.com>
Fri, 6 May 2011 04:40:52 +0000 (00:40 -0400)
The standard CobraMUSH channel object features are not back yet, but at
least we have a chat system while we're in the testing phase.

hdrs/extchat.h
options.h.dist
src/extchat.c
src/function.c

index a011309314c0704fb1faad06d778715f1a68fc83..73c2d4cecf3f42346b5d2e631e03e10bb8f8388b 100644 (file)
@@ -65,9 +65,15 @@ struct chanuser {
 #define CU_DEFAULT_FLAGS 0x0
 
 /* channel_broadcast flags */
-#define CB_CHECKQUIET 0x1       /* Check for quiet flag on recipients */
-#define CB_NOSPOOF    0x2       /* Use nospoof emits */
-#define CB_PRESENCE   0x4       /* This is a presence message, not sound */
+#define CB_CHECKQUIET 0x01      /* Check for quiet flag on recipients */
+#define CB_NOSPOOF    0x02      /* Use nospoof emits */
+#define CB_PRESENCE   0x04      /* This is a presence message, not sound */
+#define CB_QUIET      0x08      /* Do not prepend the <Channel> name */
+#define CB_SPEECH     0x10      /* This is player speech */
+#define CB_POSE       0x20      /* This is a pose */
+#define CB_SEMIPOSE   0x40      /* This is a semipose */
+#define CB_EMIT       0x80      /* This is an emit */
+#define CB_TYPE       0xF0      /* Type of a message. */
 
 #define CUdbref(u) ((u)->who)
 #define CUtype(u) ((u)->type)
@@ -90,6 +96,7 @@ struct channel {
   privbits type;                /**< Channel flags */
   int cost;             /**< What it cost to make this channel */
   dbref creator;                /**< This is who paid the cost for the channel */
+  dbref mogrifier;              /**< This is the object that mogrifies the channel text. */
   int num_users;                /**< Number of connected users */
   int max_users;                /**< Maximum allocated users */
   struct chanuser *users;       /**< Linked list of current users */
@@ -182,6 +189,7 @@ struct na_cpass {
 /* Who can change channel privileges to type t */
 #define Chan_Can_Priv(p,t) (Chan_Can(p,t))
 #define Chan_Can_Access(c,p) (Chan_Can(p,ChanType(c)))
+#define ChanMogrifier(c) ((c)->mogrifier)
 #define Chan_Can_Join(c,p) \
      (Chan_Can_Access(c,p) && \
      (eval_chan_lock(c,p, CLOCK_JOIN)))
@@ -194,15 +202,12 @@ struct na_cpass {
      (Director(p) || (ChanCreator(c) == (p)) || \
      (!Guest(p) && Chan_Can_Access(c,p) && \
      (eval_chan_lock(c,p, CLOCK_MOD))))
-#define Chan_Can_See(c,p) \
-       ((Admin(p) || See_All(p)) || (Chan_Can_Access(c,p) && \
-                                         (eval_chan_lock(c,p,CLOCK_SEE))))
+#define Chan_Can_See(c,p) ((Admin(p)) || CanSee(p,ChanCreator(c)) || (Chan_Can_Access(c,p) && (eval_chan_lock(c,p,CLOCK_SEE))))
 #define Chan_Can_Hide(c,p) \
      (Can_Hide(p) || (Channel_CanHide(c) && Chan_Can_Access(c,p) && \
      (eval_chan_lock(c,p, CLOCK_HIDE))))
 #define Chan_Can_Nuke(c,p) (ChanCreator(c) == (p) || div_powover(p, ChanCreator(c), "Chat"))
-#define Chan_Can_Decomp(c,p) (Director(p) || (ChanCreator(c) == (p)))
-
+#define Chan_Can_Decomp(c,p) ((div_powover(p,ChanCreator(c), "Chat") && CanSee(p,ChanCreator(c))) || (ChanCreator(c) == (p)))
 
 
      /* For use in channel matching */
@@ -217,8 +222,8 @@ extern void WIN32_CDECL channel_chat
   __attribute__ ((__format__(__printf__, 6, 7)));
 extern CHANUSER *onchannel(dbref who, CHAN *c);
 extern void init_chatdb(void);
-extern int load_chatdb(FILE * fp);
-extern int save_chatdb(FILE * fp);
+extern int load_chatdb(PENNFILE * fp);
+extern int save_chatdb(PENNFILE * fp);
 extern void do_cemit
   (dbref player, const char *name, const char *msg, int flags);
 extern void do_chan_user_flags
@@ -258,5 +263,7 @@ enum chan_match_type {
   PMATCH_ON    /**< Match channels user is on */
 };
 
+/* Chat db flags */
+#define CDB_SPIFFY 0x01         /* Has mogrifier and buffer */
 
 #endif                          /* __EXTCHAT_H */
index a713edb50ed0d141b594b31238638b0c719c8028..920b6c1f94f414b09a7a3561de6a7d16b53c197a 100644 (file)
 /* Standard PennMUSH chat system.  Allows user to talk cross-MUSH
  * to each other, without needing to be in the same room.  Wether or
  * not you want this depends on what type of MUSH you want.
- * **RECOMMENDED NOT TO ENABLE THIS DURING 0.80 DEVELOPMENT!**
  */
-/* #define CHAT_SYSTEM /* */
+#define CHAT_SYSTEM /* */
 
 /* Many MUSHes want to change the +channels to =channels. That's
  * annoying. So we've got this CHAT_TOKEN_ALIAS, which allows + as well
index df738c77f3cb37ef11d811cd6cf0ab014ff04f01..e0bff524b96730da4b751879d31c13c0f941821f 100644 (file)
@@ -44,11 +44,11 @@ static CHANUSER *new_user(dbref who, const void *hint);
 static void free_channel(CHAN *c);
 static void free_chanlist(CHANLIST *cl);
 static void free_user(CHANUSER *u);
-static int load_chatdb_oldstyle(FILE * fp);
-static int load_channel(FILE * fp, CHAN *ch);
-static int load_chanusers(FILE * fp, CHAN *ch);
-static int load_labeled_channel(FILE * fp, CHAN *ch);
-static int load_labeled_chanusers(FILE * fp, CHAN *ch);
+static int load_chatdb_oldstyle(PENNFILE *fp);
+static int load_channel(PENNFILE *fp, CHAN *ch);
+static int load_chanusers(PENNFILE *fp, CHAN *ch);
+static int load_labeled_channel(PENNFILE *fp, CHAN *ch, int dbflags);
+static int load_labeled_chanusers(PENNFILE *fp, CHAN *ch);
 static void insert_channel(CHAN **ch);
 static void remove_channel(CHAN *ch);
 static void insert_obj_chan(dbref who, CHAN **ch);
@@ -58,8 +58,8 @@ static void chan_chown(CHAN *c, dbref victim);
 void chan_chownall(dbref old, dbref new);
 static int insert_user(CHANUSER *user, CHAN *ch);
 static int remove_user(CHANUSER *u, CHAN *ch);
-static int save_channel(FILE * fp, CHAN *ch);
-static int save_chanuser(FILE * fp, CHANUSER *user);
+static int save_channel(PENNFILE *fp, CHAN *ch);
+static int save_chanuser(PENNFILE *fp, CHANUSER *user);
 static void channel_wipe(dbref player, CHAN *chan);
 static int yesno(const char *str);
 static int canstilladd(dbref player);
@@ -73,8 +73,12 @@ static void channel_leave_self(dbref player, const char *name);
 static void do_channel_who(dbref player, CHAN *chan);
 void chat_player_announce(dbref player, char *msg, int ungag);
 static int ok_channel_name(const char *n);
+/*
 static void format_channel_chat(CHAN *chan, CHANUSER *u, dbref victim,
                                 int flags, const char *msg, const char *extra);
+                               */
+static void channel_send(CHAN *channel, dbref player, int flags,
+                          const char *origmessage);
 static void list_partial_matches(dbref player, const char *name,
                                  enum chan_match_type type);
 
@@ -88,9 +92,9 @@ slab *channel_slab; /**< slab for 'struct channel' allocations */
 slab *chanlist_slab; /**< slab for 'struct chanlist' allocations */
 slab *chanuser_slab; /**< slab for 'struct chanuser' allocations */
 
-#define YES 1     /**< An affirmative. */
-#define NO 0      /**< A negative. */
-#define ERR -1    /**< An error. Clever, eh? */
+#define CHAN_YES 1     /**< An affirmative. */
+#define CHAN_NO 0      /**< A negative. */
+#define CHAN_ERR -1    /**< An error. Clever, eh? */
 
 /** Wrapper for insert_user() that generates a new CHANUSER and inserts it */
 #define insert_user_by_dbref(who,chan) \
@@ -110,6 +114,7 @@ static PRIV priv_table[] = {
   {"Admin", 'A', CHANNEL_ADMIN | CHANNEL_PLAYER, CHANNEL_ADMIN},
   {"Director", 'W', CHANNEL_DIRECTOR | CHANNEL_PLAYER, CHANNEL_DIRECTOR},
   {"Player", 'P', CHANNEL_PLAYER, CHANNEL_PLAYER},
+  {"Thing", 'T', CHANNEL_OBJECT, CHANNEL_OBJECT},
   {"Object", 'O', CHANNEL_OBJECT, CHANNEL_OBJECT},
   {"Quiet", 'Q', CHANNEL_QUIET, CHANNEL_QUIET},
   {"Open", 'o', CHANNEL_OPEN, CHANNEL_OPEN},
@@ -217,7 +222,7 @@ init_chatdb(void)
  * \retval 0 failure
  */
 static int
-load_chatdb_oldstyle(FILE * fp)
+load_chatdb_oldstyle(PENNFILE *fp)
 {
   int i;
   CHAN *ch;
@@ -230,7 +235,7 @@ load_chatdb_oldstyle(FILE * fp)
 
   /* Load all channels */
   for (i = 0; i < num_channels; i++) {
-    if (feof(fp))
+    if (penn_feof(fp))
       break;
     ch = new_channel();
     if (!ch)
@@ -245,7 +250,7 @@ load_chatdb_oldstyle(FILE * fp)
   num_channels = i;
 
   /* Check for **END OF DUMP*** */
-  if (!fgets(buff, sizeof buff, fp))
+  if (!penn_fgets(buff, sizeof buff, fp))
     do_rawlog(LT_ERR, T("CHAT: No end-of-dump marker in the chat database."));
   else if (strcmp(buff, EOD) != 0)
     do_rawlog(LT_ERR, T("CHAT: Trailing garbage in the chat database."));
@@ -261,23 +266,23 @@ extern char db_timestamp[];
  * \retval 0 failure
  */
 int
-load_chatdb(FILE * fp)
+load_chatdb(PENNFILE *fp)
 {
   int i, flags;
   CHAN *ch;
   char buff[20];
   char *chat_timestamp;
 
-  i = fgetc(fp);
+  i = penn_fgetc(fp);
   if (i == EOF) {
     do_rawlog(LT_ERR, T("CHAT: Invalid database format!"));
     longjmp(db_err, 1);
   } else if (i != '+') {
-    ungetc(i, fp);
+    penn_ungetc(i, fp);
     return load_chatdb_oldstyle(fp);
   }
 
-  i = fgetc(fp);
+  i = penn_fgetc(fp);
 
   if (i != 'V') {
     do_rawlog(LT_ERR, T("CHAT: Invalid database format!"));
@@ -303,7 +308,7 @@ load_chatdb(FILE * fp)
     ch = new_channel();
     if (!ch)
       return 0;
-    if (!load_labeled_channel(fp, ch)) {
+    if (!load_labeled_channel(fp, ch, flags)) {
       do_rawlog(LT_ERR, T("Unable to load channel %d."), i);
       free_channel(ch);
       return 0;
@@ -313,7 +318,7 @@ load_chatdb(FILE * fp)
   num_channels = i;
 
   /* Check for **END OF DUMP*** */
-  if (!fgets(buff, sizeof buff, fp))
+  if (!penn_fgets(buff, sizeof buff, fp))
     do_rawlog(LT_ERR, T("CHAT: No end-of-dump marker in the chat database."));
   else if (strcmp(buff, EOD) != 0)
     do_rawlog(LT_ERR, T("CHAT: Trailing garbage in the chat database."));
@@ -335,6 +340,7 @@ new_channel(void)
   ch->title[0] = '\0';
   ChanType(ch) = CHANNEL_DEFAULT_FLAGS;
   ChanCreator(ch) = NOTHING;
+  ChanMogrifier(ch) = NOTHING;
   ChanCost(ch) = CHANNEL_COST;
   ChanNext(ch) = NULL;
   ChanNumMsgs(ch) = 0;
@@ -401,14 +407,15 @@ free_user(CHANUSER *u)
  * successful, 0 otherwise.
  */
 static int
-load_channel(FILE * fp, CHAN *ch)
+load_channel(PENNFILE *fp, CHAN *ch)
 {
   mush_strncpy(ChanName(ch), getstring_noalloc(fp), CHAN_NAME_LEN);
-  if (feof(fp))
+  if (penn_feof(fp))
     return 0;
   mush_strncpy(ChanTitle(ch), getstring_noalloc(fp), CHAN_TITLE_LEN);
   ChanType(ch) = (privbits) getref(fp);
   ChanCreator(ch) = getref(fp);
+  ChanMogrifier(ch) = NOTHING;
   ChanCost(ch) = getref(fp);
   ChanNumMsgs(ch) = 0;
   ChanJoinLock(ch) = getboolexp(fp, chan_join_lock);
@@ -428,7 +435,7 @@ load_channel(FILE * fp, CHAN *ch)
  * successful, 0 otherwise.
  */
 static int
-load_labeled_channel(FILE * fp, CHAN *ch)
+load_labeled_channel(PENNFILE *fp, CHAN *ch, int dbflags)
 {
   char *tmp;
   int i;
@@ -445,6 +452,13 @@ load_labeled_channel(FILE * fp, CHAN *ch)
   ChanCreator(ch) = d;
   db_read_this_labeled_int(fp, "cost", &i);
   ChanCost(ch) = i;
+  if (dbflags & CDB_SPIFFY) {
+    db_read_this_labeled_int(fp, "buffer", &i);
+    if (i)
+      ChanBufferQ(ch) = allocate_bufferq(i);
+    db_read_this_labeled_dbref(fp, "mogrifier", &d);
+    ChanMogrifier(ch) = d;
+  }
   ChanNumMsgs(ch) = 0;
   while (1) {
     db_read_labeled_string(fp, &label, &value);
@@ -472,7 +486,7 @@ load_labeled_channel(FILE * fp, CHAN *ch)
 
 /* Load the *channel's user list. Return number of users on success, or 0 */
 static int
-load_chanusers(FILE * fp, CHAN *ch)
+load_chanusers(PENNFILE *fp, CHAN *ch)
 {
   int i, num = 0;
   CHANUSER *user;
@@ -500,7 +514,7 @@ load_chanusers(FILE * fp, CHAN *ch)
 
 /* Load the *channel's user list. Return number of users on success, or 0 */
 static int
-load_labeled_chanusers(FILE * fp, CHAN *ch)
+load_labeled_chanusers(PENNFILE *fp, CHAN *ch)
 {
   int i, num = 0, n;
   char *tmp;
@@ -682,6 +696,9 @@ remove_all_obj_chan(dbref thing)
   CHANLIST *p, *nextp;
   for (p = Chanlist(thing); p; p = nextp) {
     nextp = p->next;
+    if (ChanMogrifier(p->chan) == thing) {
+      ChanMogrifier(p->chan) = NOTHING;
+    }
     remove_user_by_dbref(thing, p->chan);
   }
   return;
@@ -782,26 +799,26 @@ remove_user(CHANUSER *u, CHAN *ch)
  * \retval 0 failure
  */
 int
-save_chatdb(FILE * fp)
+save_chatdb(PENNFILE *fp)
 {
   CHAN *ch;
   int default_flags = 0;
 
   /* How many channels? */
-  OUTPUT(fprintf(fp, "+V%d\n", default_flags));
+  penn_fprintf(fp, "+V%d\n", default_flags);
   db_write_labeled_string(fp, "savedtime", show_time(mudtime, 1));
   db_write_labeled_int(fp, "channels", num_channels);
   for (ch = channels; ch; ch = ch->next) {
     save_channel(fp, ch);
   }
-  OUTPUT(fputs(EOD, fp));
+  penn_fputs(EOD, fp);
   return 1;
 }
 
 /* Save a single channel. Return 1 if  successful, 0 otherwise.
  */
 static int
-save_channel(FILE * fp, CHAN *ch)
+save_channel(PENNFILE *fp, CHAN *ch)
 {
   CHANUSER *cu;
 
@@ -810,6 +827,8 @@ save_channel(FILE * fp, CHAN *ch)
   db_write_labeled_int(fp, "  flags", ChanType(ch));
   db_write_labeled_dbref(fp, "  creator", ChanCreator(ch));
   db_write_labeled_int(fp, "  cost", ChanCost(ch));
+  db_write_labeled_int(fp, "  buffer", bufferq_blocks(ChanBufferQ(ch)));
+  db_write_labeled_dbref(fp, "  mogrifier", ChanMogrifier(ch));
   db_write_labeled_string(fp, "  lock", "join");
   putboolexp(fp, ChanJoinLock(ch));
   db_write_labeled_string(fp, "  lock", "speak");
@@ -828,7 +847,7 @@ save_channel(FILE * fp, CHAN *ch)
 
 /* Save the channel's user list. Return 1 on success, 0 on failure */
 static int
-save_chanuser(FILE * fp, CHANUSER *user)
+save_chanuser(PENNFILE *fp, CHANUSER *user)
 {
   db_write_labeled_dbref(fp, "   dbref", CUdbref(user));
   db_write_labeled_int(fp, "    flags", CUtype(user));
@@ -1240,11 +1259,11 @@ do_channel(dbref player, const char *name, const char *target, const char *com)
                     T("CHAT: You join %s to channel <%s>."), Name(victim),
                     ChanName(chan));
       u = onchannel(victim, chan);
+      ChanNumUsers(chan)++;
       if (!Channel_Quiet(chan) && !DarkLegal(victim)) {
-        format_channel_chat(chan, u, victim, CB_CHECKQUIET | CB_PRESENCE,
-                            T("%s has joined this channel."), NULL);
+        channel_send(chan, victim, CB_CHECKQUIET | CB_PRESENCE | CB_POSE,
+                            T("%s has joined this channel."));
       }
-      ChanNumUsers(chan)++;
     } else {
       notify_format(player,
                     T("%s is already on channel <%s>."), Name(victim),
@@ -1266,9 +1285,9 @@ do_channel(dbref player, const char *name, const char *target, const char *com)
     strcpy(title, (u && CUtitle(u)) ? CUtitle(u) : "");
     if (remove_user(u, chan)) {
       if (!Channel_Quiet(chan) && !DarkLegal(victim)) {
-        format_channel_chat(chan, NULL, victim,
-                            CB_CHECKQUIET | CB_PRESENCE,
-                            T("%s has left this channel."), title);
+        channel_send(chan, victim,
+                     CB_CHECKQUIET | CB_PRESENCE | CB_POSE,
+                     T("has left this channel."));     
       }
       notify_format(victim,
                     T("CHAT: %s removes you from channel <%s>."),
@@ -1326,10 +1345,10 @@ channel_join_self(dbref player, const char *name)
   /* Does victim pass the joinlock? */
   if (!Chan_Can_Join(chan, player)) {
     if (Director(player)) {
-      /* Directors can override join locks */
+      /* Wizards can override join locks */
       notify(player,
              T
-             ("CHAT: Warning: You don't meet channel join permissions (joining anyway)"));
+             ("CHAT: Warning: You don't meet channel join permissions! (joining anyway)"));
     } else {
       notify(player, T("Permission to join denied."));
       return;
@@ -1338,10 +1357,11 @@ channel_join_self(dbref player, const char *name)
   if (insert_user_by_dbref(player, chan)) {
     notify_format(player, T("CHAT: You join channel <%s>."), ChanName(chan));
     u = onchannel(player, chan);
-    if (!Channel_Quiet(chan) && !DarkLegal(player))
-      format_channel_chat(chan, u, player, CB_CHECKQUIET | CB_PRESENCE,
-                          T("%s has joined this channel."), NULL);
     ChanNumUsers(chan)++;
+    if (!Channel_Quiet(chan) && !DarkLegal(player))
+      channel_send(chan, player,
+                   CB_CHECKQUIET | CB_PRESENCE | CB_POSE,
+                   T("has joined this channel."));
   } else {
     /* Should never happen */
     notify_format(player,
@@ -1381,8 +1401,9 @@ channel_leave_self(dbref player, const char *name)
   strcpy(title, (u && CUtitle(u)) ? CUtitle(u) : "");
   if (remove_user(u, chan)) {
     if (!Channel_Quiet(chan) && !DarkLegal(player))
-      format_channel_chat(chan, NULL, player, CB_CHECKQUIET | CB_PRESENCE,
-                          T("%s has left this channel."), title);
+     channel_send(chan, player,
+                   CB_CHECKQUIET | CB_PRESENCE | CB_POSE,
+                   T("has left this channel."));
     notify_format(player, T("CHAT: You leave channel <%s>."), ChanName(chan));
   } else {
     /* Should never happen */
@@ -1503,9 +1524,6 @@ do_chat(dbref player, CHAN *chan, const char *arg1)
   CHANUSER *u;
   const char *gap;
   char type;
-  const char *someone = "Someone";
-  char *title;
-  const char *name;
   bool canhear;
 
   if (!Chan_Ok_Type(chan, player)) {
@@ -1542,17 +1560,6 @@ do_chat(dbref player, CHAN *chan, const char *arg1)
     return;
   }
 
-  if (!Channel_NoTitles(chan) && u && CUtitle(u) && *CUtitle(u))
-    title = CUtitle(u);
-  else
-    title = NULL;
-  if (Channel_NoNames(chan))
-    name = NULL;
-  else
-    name = accented_name(player);
-  if (!title && !name)
-    name = someone;
-
   /* figure out what kind of message we have */
   gap = " ";
   type = ':';
@@ -1563,27 +1570,12 @@ do_chat(dbref player, CHAN *chan, const char *arg1)
     /* FALLTHRU */
   case POSE_TOKEN:
     arg1 = arg1 + 1;
-    channel_chat(chan, player, 0, arg1, type, "<%s> %s%s%s%s%s%s",
-                 ChanName(chan), title ? title : "", title ? ANSI_ENDALL : "",
-                 (title && name) ? " " : "", name ? name : "", gap, arg1);
-    if (!canhear)
-      notify_format(player, T("To channel %s: %s%s%s%s%s%s"), ChanName(chan),
-                    title ? title : "", title ? ANSI_ENDALL : "",
-                    (title && name) ? " " : "", name ? name : "", gap, arg1);
+    channel_send(chan, player, type == ';' ? CB_SEMIPOSE : CB_POSE, arg1);
     break;
   default:
     if (CHAT_STRIP_QUOTE && (*arg1 == SAY_TOKEN))
       arg1 = arg1 + 1;
-    channel_chat(chan, player, 0, arg1, '"', T("<%s> %s%s%s%s says, \"%s\""),
-                 ChanName(chan), title ? title : "",
-                 title ? ANSI_ENDALL : "", (title && name) ? " " : "",
-                 name ? name : "", arg1);
-    if (!canhear)
-      notify_format(player,
-                    T("To channel %s: %s%s%s%s says, \"%s\""),
-                    ChanName(chan), title ? title : "",
-                    title ? ANSI_ENDALL : "", (title && name) ? " " : "",
-                    name ? name : "", arg1);
+    channel_send(chan, player, CB_SPEECH, arg1);
     break;
   }
 
@@ -1605,7 +1597,6 @@ do_cemit(dbref player, const char *name, const char *msg, int flags)
   CHAN *chan = NULL;
   CHANUSER *u;
   int canhear;
-  int override_checks = 0;
 
   if (!name || !*name) {
     notify(player, T("That is not a valid channel."));
@@ -1626,18 +1617,15 @@ do_cemit(dbref player, const char *name, const char *msg, int flags)
     notify(player, T("CHAT: I don't recognize that channel."));
     return;
   }
-  /* If the cemitter is both See_All and Pemit_All, always allow them
-   * to @cemit, as they could iterate over connected players, examine
-   * their channels, and pemit to them anyway.
-   */
-  if (!override_checks && !Chan_Ok_Type(chan, player)) {
+
+  if (!Chan_Ok_Type(chan, player)) {
     notify_format(player,
                   T
                   ("Sorry, you're not the right type to be on channel <%s>."),
                   ChanName(chan));
     return;
   }
-  if (!override_checks && !Chan_Can_Cemit(chan, player)) {
+  if (!Chan_Can_Cemit(chan, player)) {
     notify_format(player,
                   T("Sorry, you're not allowed to @cemit on channel <%s>."),
                   ChanName(chan));
@@ -1646,7 +1634,7 @@ do_cemit(dbref player, const char *name, const char *msg, int flags)
   u = onchannel(player, chan);
   canhear = u ? !Chanuser_Gag(u) : 0;
   /* If the channel isn't open, you must hear it in order to speak */
-  if (!override_checks && !Channel_Open(chan)) {
+  if (!Channel_Open(chan)) {
     if (!u) {
       notify(player, T("You must be on that channel to speak on it."));
       return;
@@ -1661,11 +1649,9 @@ do_cemit(dbref player, const char *name, const char *msg, int flags)
     return;
   }
   if (!(flags & PEMIT_SILENT))
-    channel_chat(chan, player, (flags & PEMIT_SPOOF) ? 0 : CB_NOSPOOF,
-                 msg, '|', "<%s> %s", ChanName(chan), msg);
+    channel_send(chan, player, CB_EMIT, msg);
   else
-    channel_chat(chan, player, (flags & PEMIT_SPOOF) ? 0 : CB_NOSPOOF,
-                 msg, '|', "%s", msg);
+    channel_send(chan, player, CB_EMIT | CB_QUIET, msg);
   if (!canhear)
     notify_format(player, T("Cemit to channel %s: %s"), ChanName(chan), msg);
   ChanNumMsgs(chan)++;
@@ -1768,6 +1754,7 @@ do_chan_admin(dbref player, char *name, const char *perms, int flag)
     if (type)
       ChanType(chan) = type;
     ChanCreator(chan) = Owner(player);
+    ChanMogrifier(chan) = NOTHING;
     mush_strncpy(ChanName(chan), name, CHAN_NAME_LEN);
     insert_channel(&chan);
     notify_format(player, T("CHAT: Channel <%s> created."), ChanName(chan));
@@ -1817,9 +1804,8 @@ do_chan_admin(dbref player, char *name, const char *perms, int flag)
     insert_channel(&chan);
     snprintf(announcebuff, BUFFER_LEN, "has renamed %s to %s",
              old, ChanName(chan));
-    channel_chat(chan, player, 0, announcebuff, '@',
-                 "<%s> %s has renamed channel %s to %s.",
-                 ChanName(chan), Name(player), old, ChanName(chan));
+    channel_send(chan, player,
+                 CB_CHECKQUIET | CB_PRESENCE | CB_POSE, announcebuff);
     notify(player, T("Channel renamed."));
     break;
   case 3:
@@ -2502,6 +2488,61 @@ do_chan_wipe(dbref player, const char *name)
   return;
 }
 
+/** Change the mogrifier of a channel.
+ * \verbatim
+ * This is the top-level function for @channel/mogrifier
+ * \endverbatim
+ * \param player the enactor.
+ * \param name name of the channel.
+ * \param newobj name of the new mogrifier object.
+ */
+void
+do_chan_set_mogrifier(dbref player, const char *name, const char *newobj)
+{
+  CHAN *c;
+  dbref it = NOTHING;
+  /* Find the channel */
+  test_channel(player, name, c);
+
+  /* Only a channel modifier can do this. */
+  if (!Chan_Can_Modify(c, player)) {
+    notify(player, T("CHAT: Only a channel modifier can do that."));
+    return;
+  }
+
+  /* Find the mogrifying object */
+  if (newobj && *newobj) {
+    if ((it = match_result(player, newobj, NOTYPE, MAT_EVERYTHING)) < 0) {
+      if (it == NOTHING)
+        notify(player, T("I can't see that here."));
+      else if (it == AMBIGUOUS)
+        notify(player, T("I don't know which thing you mean."));
+      return;
+    }
+  } else if (ChanMogrifier(c) != NOTHING) {
+    notify_format(player,
+                  T("CHAT: Channel <%s> no longer mogrified by %s."),
+                  ChanName(c), Name(ChanMogrifier(c)));
+    ChanMogrifier(c) = NOTHING;
+    return;
+  } else {
+    notify_format(player,
+                  T("CHAT: Channel <%s> isn't being mogrified."), ChanName(c));
+    return;
+  }
+
+  /* The player must be able to *control* the mogrifier. */
+  if (!controls(player, it)) {
+    notify(player, T("CHAT: You must control the mogrifier."));
+    return;
+  }
+  ChanMogrifier(c) = it;
+  notify_format(player,
+                T("CHAT: Channel <%s> now mogrified by %s."), ChanName(c),
+                Name(it));
+  return;
+}
+
 /** Change the owner of a channel.
  * \verbatim
  * This is the top-level function for @channel/chown, which changes
@@ -2684,6 +2725,10 @@ do_chan_what(dbref player, const char *partname)
       notify(player, ChanName(c));
       notify_format(player, T("Description: %s"), ChanTitle(c));
       notify_format(player, T("Owner: %s"), Name(ChanCreator(c)));
+      if (ChanMogrifier(c) != NOTHING) {
+        notify_format(player, T("Mogrifier: %s (#%d)"),
+                      Name(ChanMogrifier(c)), ChanMogrifier(c));
+      }
       notify_format(player, T("Flags: %s"),
                     privs_to_string(priv_table, ChanType(c)));
       if (ChanBufferQ(c))
@@ -2723,7 +2768,7 @@ do_chan_decompile(dbref player, const char *name, int brief)
     strcpy(cleanp, remove_markup(ChanName(c), NULL));
     if (string_prefix(cleanp, cleanname)) {
       found++;
-      if (!(See_All(player) || Chan_Can_Modify(c, player)
+      if (!(CanSee(player, ChanCreator(c)) || Chan_Can_Modify(c, player)
             || (ChanCreator(c) == player))) {
         if (Chan_Can_See(c, player))
           notify_format(player, T("CHAT: No permission to decompile <%s>"),
@@ -2734,6 +2779,10 @@ do_chan_decompile(dbref player, const char *name, int brief)
                     privs_to_string(priv_table, ChanType(c)));
       notify_format(player, "@channel/chown %s = %s", ChanName(c),
                     Name(ChanCreator(c)));
+      if (ChanMogrifier(c) != NOTHING) {
+        notify_format(player, "@channel/mogrifier %s = #%d", ChanName(c),
+                      ChanMogrifier(c));
+      }
       if (ChanModLock(c) != TRUE_BOOLEXP)
         notify_format(player, "@clock/mod %s = %s", ChanName(c),
                       unparse_boolexp(player, ChanModLock(c), UB_MEREF));
@@ -2808,6 +2857,9 @@ do_channel_who(dbref player, CHAN *chan)
 FUNCTION(fun_cwho)
 {
   int first = 1;
+  int matchcond = 0;
+  int priv = 0;
+  int show;
   CHAN *chan = NULL;
   CHANUSER *u;
   dbref who;
@@ -2823,6 +2875,19 @@ FUNCTION(fun_cwho)
     break;
   }
 
+  if (nargs == 2) {
+    if (!strcasecmp(args[1], "on"))
+      matchcond = 0;
+    else if (!strcasecmp(args[1], "off"))
+      matchcond = 1;
+    else if (!strcasecmp(args[1], "all"))
+      matchcond = 2;
+    else {
+      safe_str(T("#-1 INVALID ARGUMENT"), buff, bp);
+      return;
+    }
+  }
+
   /* Feh. We need to do some sort of privilege checking, so that
    * if mortals can't do '@channel/who wizard', they can't do
    * 'think cwho(wizard)' either. The first approach that comes to
@@ -2840,16 +2905,25 @@ FUNCTION(fun_cwho)
     safe_str(T("#-1 NO PERMISSIONS FOR CHANNEL"), buff, bp);
     return;
   }
+
+  priv = Priv_Who(executor);
+
   for (u = ChanUsers(chan); u; u = u->next) {
     who = CUdbref(u);
-    if ((IsThing(who) || Connected(who)) &&
-        (!Chanuser_Hide(u) || Priv_Who(executor))) {
-      if (first)
-        first = 0;
+    show = 1;
+    if (!IsThing(who) && matchcond != 2) {
+      if (matchcond)
+        show = !Connected(who) || (Chanuser_Hide(u) && !priv);
       else
-        safe_chr(' ', buff, bp);
-      safe_dbref(who, buff, bp);
+        show = Connected(who) && (!Chanuser_Hide(u) || priv);
     }
+    if (!show)
+      continue;
+    if (first)
+      first = 0;
+    else
+      safe_chr(' ', buff, bp);
+    safe_dbref(who, buff, bp);
   }
 }
 
@@ -2897,28 +2971,28 @@ static int
 yesno(const char *str)
 {
   if (!str || !*str)
-    return ERR;
+    return CHAN_ERR;
   switch (str[0]) {
   case 'y':
   case 'Y':
-    return YES;
+    return CHAN_YES;
   case 'n':
   case 'N':
-    return NO;
+    return CHAN_NO;
   case 'o':
   case 'O':
     switch (str[1]) {
     case 'n':
     case 'N':
-      return YES;
+      return CHAN_YES;
     case 'f':
     case 'F':
-      return NO;
+      return CHAN_NO;
     default:
-      return ERR;
+      return CHAN_ERR;
     }
   default:
-    return ERR;
+    return CHAN_ERR;
   }
 }
 
@@ -2955,8 +3029,7 @@ chat_player_announce(dbref player, char *msg, int ungag)
     if (u) {
       if (!Channel_Quiet(c) && (Channel_Admin(c) || Channel_Wizard(c)
                                 || (!Chanuser_Hide(u) && !Dark(player)))) {
-        format_channel_chat(c, u, player, CB_CHECKQUIET | CB_PRESENCE,
-                            msg, NULL);
+        channel_send(c, player, CB_CHECKQUIET | CB_PRESENCE | CB_POSE, msg);
       }
       if (ungag)
         CUtype(u) &= ~CU_GAG;
@@ -3262,7 +3335,8 @@ FUNCTION(fun_crecall)
 
 COMMAND(cmd_cemit)
 {
-  int spflags = !strcmp(cmd->name, "@NSCEMIT") ? PEMIT_SPOOF : 0;
+  int spflags = (!strcmp(cmd->name, "@NSCEMIT")
+                 && Can_Nspemit(player) ? PEMIT_SPOOF : 0);
   SPOOF(player, cause, sw);
   if (SW_ISSET(sw, SWITCH_SILENT))
     spflags |= PEMIT_SILENT;
@@ -3295,6 +3369,8 @@ COMMAND(cmd_channel)
     do_chan_desc(player, arg_left, args_right[1]);
   else if (SW_ISSET(sw, SWITCH_TITLE))
     do_chan_title(player, arg_left, args_right[1]);
+  else if (SW_ISSET(sw, SWITCH_MOGRIFIER))
+    do_chan_set_mogrifier(player, arg_left, args_right[1]);
   else if (SW_ISSET(sw, SWITCH_CHOWN))
     do_chan_chown(player, arg_left, args_right[1]);
   else if (SW_ISSET(sw, SWITCH_WIPE))
@@ -3369,88 +3445,253 @@ na_channel(dbref current, void *data)
   return current;
 }
 
+/**
+ * Mogrify a value using u(<mogrifier>/<attrname>,<value>)
+ *
+ * \param mogrifier The object doing the mogrification
+ * \param attrname The attribute on mogrifier to call.
+ * \param channel The active channel. (%1)
+ * \param value The value to mogrify (%0)
+ * \retval Mogrified text.
+ */
+
+char *
+mogrify(dbref mogrifier, char *attrname,
+        dbref player, int numargs, char *argv[], char *orig)
+{
+  static char buff[BUFFER_LEN];
+  const char *wenv[10] = { 0 };
+  int i;
+  buff[0] = '\0';
+  for (i = 0; i < numargs; i++) {
+    wenv[i] = argv[i];
+  }
+
+  if (call_attrib(mogrifier, attrname, wenv, numargs, buff, player, NULL)) {
+    if (buff[0]) {
+      return buff;
+    }
+  }
+
+  snprintf(buff, BUFFER_LEN, "%s", orig);
+
+  return buff;
+}
+
 /** Broadcast a message to a channel, using @chatformat if it's
- *  available.
+ *  available, and mogrifying.
  * \param channel pointer to channel to broadcast to.
  * \param player message speaker.
  * \param flags broadcast flag mask (see CB_* constants in extchat.h)
  * \param fmt message format string.
  */
-void WIN32_CDECL
-channel_chat(CHAN *channel, dbref player, int flags,
-             const char *message, char type, const char *fmt, ...)
-/* flags: 0x1 = checkquiet, 0x2 = nospoof */
+void
+channel_send(CHAN *channel, dbref player, int flags, const char *origmessage)
 {
-  va_list args;
-#ifdef HAS_VSNPRINTF
-  char tbuf1[BUFFER_LEN];
-#else
-  char tbuf1[BUFFER_LEN * 2];   /* Safety margin as per tprintf */
-#endif
+/* flags:
+ *     CB_CHECKQUIET CB_NOSPOOF CB_PRESENCE CB_POSE CB_EMIT CB_SPEECH
+ */
+
+  /* These are static only to prevent them from chewing up
+   * time to malloc/free, and chewing up space.
+   */
+  static char channame[BUFFER_LEN];
+  static char title[BUFFER_LEN];
+  static char playername[BUFFER_LEN];
+  static char message[BUFFER_LEN];
+  static char buff[BUFFER_LEN];
+  static char speechtext[BUFFER_LEN];
+
   CHANUSER *u;
+  CHANUSER *speaker;
   dbref current;
-  const char *cname;
-  const char *name, *title;
-  char ctype[2];
-  char ctitle[CU_TITLE_LEN];
+  char *bp;
+  char *blockstr = "";
   int na_flags = NA_INTER_LOCK;
   const char *someone = "Someone";
+  dbref mogrifier = NOTHING;
+  char *ctype = NULL;
+  char *argv[10];
+  int override_chatformat = 0;
+  memset(argv, 0, sizeof(argv));
 
-  ctype[0] = type;
-  ctype[1] = '\0';
-
-  /* Make sure we can write to the channel before doing so */
+  /* Make sure we can write to the channel before doing anything */
   if (Channel_Disabled(channel))
     return;
 
-  va_start(args, fmt);
+  speaker = onchannel(player, channel);
 
-#ifdef HAS_VSNPRINTF
-  (void) vsnprintf(tbuf1, sizeof tbuf1, fmt, args);
-#else
-  (void) vsprintf(tbuf1, fmt, args);
-#endif
-  va_end(args);
-  tbuf1[BUFFER_LEN - 1] = '\0';
+  snprintf(channame, BUFFER_LEN, "<%s>", ChanName(channel));
 
-  cname = ChanName(channel);
-  u = onchannel(player, channel);
-  strcpy(ctitle, (u && CUtitle(u)) ? CUtitle(u) : "");
+  if (!Channel_NoTitles(channel) && speaker &&
+      CUtitle(speaker) && *CUtitle(speaker)) {
+    snprintf(title, BUFFER_LEN, "%s", CUtitle(speaker));
+  } else {
+    title[0] = '\0';
+  }
 
-  if (!Channel_NoTitles(channel) || !*ctitle)
-    title = ctitle;
-  else
-    title = NULL;
-  if (Channel_NoNames(channel))
-    name = NULL;
-  else
-    name = accented_name(player);
-  if (!title && !name)
-    name = someone;
+  if (Channel_NoNames(channel)) {
+    playername[0] = '\0';
+  } else {
+    snprintf(playername, BUFFER_LEN, "%s", accented_name(player));
+  }
+  if (!title[0] && !playername[0]) {
+    snprintf(playername, BUFFER_LEN, "%s", someone);
+  }
+
+  if (flags & CB_PRESENCE) {
+    ctype = "@";
+  } else if (flags & CB_POSE) {
+    ctype = ":";
+  } else if (flags & CB_SEMIPOSE) {
+    ctype = ";";
+  } else if (flags & CB_EMIT) {
+    ctype = "|";
+  } else {
+    ctype = "\"";
+  }
+
+  snprintf(speechtext, BUFFER_LEN, "says");
+
+  snprintf(message, BUFFER_LEN, "%s", origmessage);
+
+  if (GoodObject(ChanMogrifier(channel))) {
+    if (eval_lock(player, ChanMogrifier(channel), Use_Lock)) {
+      mogrifier = ChanMogrifier(channel);
+
+      argv[0] = ctype;
+      argv[1] = ChanName(channel);
+      argv[2] = message;
+      argv[3] = playername;
+      argv[4] = title;
+
+      blockstr = mogrify(mogrifier, "MOGRIFY`BLOCK", player, 6, argv, "");
+      if (blockstr && *blockstr) {
+        notify(player, blockstr);
+        return;
+      }
+      // Do we override chatformats?
+      if (parse_boolean
+          (mogrify(mogrifier, "MOGRIFY`OVERRIDE", player, 6, argv, ""))) {
+        override_chatformat = 1;
+      }
+
+      argv[1] = ChanName(channel);
+      argv[2] = ctype;
+      argv[3] = message;
+      argv[4] = title;
+      argv[5] = playername;
+
+      argv[0] = channame;
+      snprintf(channame, BUFFER_LEN, "%s",
+               mogrify(mogrifier, "MOGRIFY`CHANNAME", player, 6, argv,
+                       channame));
+
+      argv[0] = title;
+      snprintf(title, BUFFER_LEN, "%s",
+               mogrify(mogrifier, "MOGRIFY`TITLE", player, 6, argv, title));
+
+      argv[0] = playername;
+      snprintf(playername, BUFFER_LEN, "%s",
+               mogrify(mogrifier, "MOGRIFY`PLAYERNAME", player, 6, argv,
+                       playername));
+
+      if (flags & CB_SPEECH) {
+        argv[0] = speechtext;
+        snprintf(speechtext, BUFFER_LEN, "%s",
+                 mogrify(mogrifier, "MOGRIFY`SPEECHTEXT", player, 6, argv,
+                         speechtext));
+      }
+
+      argv[0] = message;
+      snprintf(message, BUFFER_LEN, "%s",
+               mogrify(mogrifier, "MOGRIFY`MESSAGE", player, 6, argv, message));
+    }
+  }
+
+  bp = buff;
+
+  *bp = '\0';
+
+  if (!(flags & CB_QUIET)) {
+    safe_str(channame, buff, &bp);
+    safe_chr(' ', buff, &bp);
+  }
+
+  if (flags & CB_EMIT) {
+    safe_str(message, buff, &bp);
+  } else {
+    if (!(flags & CB_PRESENCE)) {
+      if (title[0]) {
+        safe_str(title, buff, &bp);
+        safe_chr(' ', buff, &bp);
+      }
+    }
+    safe_str(playername, buff, &bp);
+    switch (flags & CB_TYPE) {
+    case CB_POSE:
+      safe_chr(' ', buff, &bp);
+    case CB_SEMIPOSE:
+      safe_str(message, buff, &bp);
+      break;
+    case CB_SPEECH:
+      safe_format(buff, &bp, " %s, \"%s\"", T(speechtext), message);
+      break;
+    }
+  }
+  *bp = '\0';
 
-  if (Channel_Interact(channel))
+  // @chatformat
+  if (flags & CB_PRESENCE) {
+    snprintf(title, BUFFER_LEN, "%s", message);
+    snprintf(message, BUFFER_LEN, "%s %s", Name(player), title);
+    title[0] = '\0';
+  }
+
+  if (GoodObject(mogrifier)) {
+    argv[0] = ctype;
+    argv[1] = ChanName(channel);
+    argv[2] = message;
+    argv[3] = playername;
+    argv[4] = title;
+    argv[5] = buff;
+    snprintf(buff, BUFFER_LEN, "%s",
+             mogrify(mogrifier, "MOGRIFY`FORMAT", player, 6, argv, buff));
+  }
+
+  if (Channel_Interact(channel)) {
     na_flags |= (flags & CB_PRESENCE) ? NA_INTER_PRESENCE : NA_INTER_HEAR;
+  }
+
+  if (!(flags & CB_NOSPOOF)) {
+    na_flags |= NA_SPOOF;
+  }
 
   for (u = ChanUsers(channel); u; u = u->next) {
     current = CUdbref(u);
 
-    if (!(((flags & 1) && Chanuser_Quiet(u)) ||
+    if (!(((flags & CB_CHECKQUIET) && Chanuser_Quiet(u)) ||
           Chanuser_Gag(u) || (IsPlayer(current) && !Connected(current)))) {
-      if (!vmessageformat(current, "CHATFORMAT", player,
-                          na_flags | ((flags & CB_NOSPOOF) ? 0 : NA_SPOOF),
-                          6, ctype, cname, message, name, title, tbuf1)) {
-        notify_anything(player, na_one, &current, ns_esnotify,
-                        na_flags | ((flags & CB_NOSPOOF) ? 0 : NA_SPOOF),
-                        tbuf1);
+      if (override_chatformat
+          || !vmessageformat(current, "CHATFORMAT", player,
+                             na_flags,
+                             6,
+                             ctype,
+                             ChanName(channel),
+                             message, playername, title, buff)) {
+        notify_anything(player, na_one, &current, ns_esnotify, na_flags, buff);
       }
     }
   }
 
   if (ChanBufferQ(channel))
     add_to_bufferq(ChanBufferQ(channel), 0,
-                   (flags & CB_NOSPOOF) ? player : NOTHING, tbuf1);
-}
+                   (flags & CB_NOSPOOF) ? player : NOTHING, buff);
 
+  if (!(flags & CB_PRESENCE) && !speaker) {
+    notify_format(player, T("To channel %s: %s"), ChanName(channel), buff);
+  }
+}
 
 /** Recall past lines from the channel's buffer.
  * We try to recall no more lines that are requested by the player,
@@ -3648,33 +3889,6 @@ do_chan_buffer(dbref player, const char *name, const char *lines)
   }
 }
 
-/* msg is a printf-style format that has exactly and only 2 %s specifiers
-   in it. */
-static void
-format_channel_chat(CHAN *chan, CHANUSER *u,
-                    dbref victim, int flags, const char *msg, const char *extra)
-{
-  const char *title = NULL;
-  char fmtbuff[BUFFER_LEN];
-  if (extra && *extra)
-    title = extra;
-  else if (u && CUtitle(u))
-    title = CUtitle(u);
-  if (Channel_NoNames(chan)) {
-    if (Channel_NoTitles(chan) || !title) {
-      channel_chat(chan, victim, flags, msg, '@', ChanName(chan), "Someone");
-      snprintf(fmtbuff, BUFFER_LEN, msg, "Someone");
-    } else {
-      snprintf(fmtbuff, BUFFER_LEN, msg, title);
-    }
-  } else {
-    snprintf(fmtbuff, BUFFER_LEN, msg, Name(victim));
-  }
-  channel_chat(chan, victim, flags, fmtbuff, '@', "<%s> %s",
-               ChanName(chan), fmtbuff);
-}
-
-
 /** Evaluate a channel lock with %0 set to the channel name.
  * \param c the channel to test.
  * \param p the object trying to pass the lock.
@@ -3710,7 +3924,7 @@ eval_chan_lock(CHAN *c, dbref p, enum clock_type type)
   global_eval_context.wenv[0] = ChanName(c);
   for (n = 1; n < 10; n++)
     global_eval_context.wenv[n] = NULL;
-  retval = eval_boolexp(p, b, p);
+  retval = eval_boolexp(p, b, p, NULL);
   restore_global_env("eval_chan_lock", oldenv);
   return retval;
 }
index 29aebb942e051a79303c64b629d5eae3fc68c4fe..6823ed1cbc6ebc70b0e6c66f9ec846abfd628958 100644 (file)
@@ -372,7 +372,7 @@ FUNTAB flist[] = {
   {"CHECKPASS", fun_checkpass, 2, 2, FN_REG | FN_DIRECTOR},
   {"CLONE", fun_clone, 1, 1, FN_REG},
   {"CMDS", fun_cmds, 1, 1, FN_REG},
-#ifdef CHAT_SYSTEM
+#ifdef NVCHAT_SYSTEM
   {"COBJ", fun_cobj, 1, 1, FN_REG},
 #endif /* CHAT_SYSTEM */
   {"COMP", fun_comp, 2, 3, FN_REG},