CODE: social editor

From: David A. Carver (
Date: 09/19/96

Its a good thing I'm a pack rat.  Haven't had a chance to install 
this code, but here is the social editor that was posted here a while 

----- Forwarded Message follows -------
Date sent:      11-May-1996 23:59:10 -0700
Send reply to: (Michael Scott)
To:    (Circle Mailinglist)
Subject:        WARNING! NO NEWBIE CODERS: Adding AEDIT (social editor)

    My last project was adding an online social editor to SaberMUD 
(mine) .. and it was a little more difficult than i imagined .. so for 
all those who are contemplating adding a social editor to their mud, I 
am going to sort of outline what it took .. that way you know what yer 
getting into :>

    First of all the command_info structure which is used in a 
variable named cmd_info in interpreter.c is a HARD CODED variable.. to 
have an editor which can dynamically add socials on line, you need to 
create another pointer to a command_info structure which you allocate 
enough space for at boot time (after figuring out how many socials you 
have in the social file and how many ACTUAL commands you have in cmd_info)..
I used 'used_cmd_info' for this variable which was put in the place of 
cmd_info throughout the mud where a command like decision was based.: 
struct command_info *used_cmd_info;

    Then I had to add to the social file some more info ... (the 
command stuff which was going to get taken out of interpreter.c) like 
min position the char had to be in .. the minimum level for the 
command/social.. etc.. i decided on a firstline social format of this:
~accuse accuse 0 6 5 0
^ the actual command name
         ^ the name I want to be used to decide WHERE to sort this 
command into the command listing.. (explained more later)
               ^ this number is the 'hide' flag (already in old file)
                 ^ this is the minimum position the character has to be 
in to do the social.
                   ^ this is the minimum position the victim has to be 
in to do the social. (already in old file)
                     ^ this is the min. level that the character has to 
be to do the social.

    Then after working my way through the socials file.. i had to go 
into and add all these extra fields to what the 
social_messg can handle.. and i also MOVED it from to 
structs.h because more than just the socials handler file would need 
it (aedit.c in particular) here's what my structure ended up looking 
like (at the end of structs.h):

/* used in the socials */
struct social_messg {
   int act_nr;
   char *command;               /* holds copy of activating command */
   char *sort_as;       /* holds a copy of a similar command or
                 * abbreviation to sort by for the parser */
   int hide;            /* ? */
   int min_victim_position; /* Position of victim */
   int min_char_position;   /* Position of char */
   int min_level_char;          /* Minimum level of socialing char */

   /* No argument was supplied */
   char *char_no_arg;
   char *others_no_arg;

   /* An argument was there, and a victim was found */
   char *char_found;
   char *others_found;
   char *vict_found;

   /* An argument was there, but no victim was found */
   char *not_found;

   /* The victim turned out to be the character */
   char *char_auto;
   char *others_auto;

    Of course you need to go into and adjust how the 
boot_social_messages() routine loads the data from the file.. (include 
the new info).
    Then I took the sort out of that routine and placed it into a 
new routine after it .. which i named 'create_command_list()' and they 
ended up looking like this:

void boot_social_messages(void) {
   FILE *fl;
   int nr = 0, hide, min_char_pos, min_pos, min_lvl, curr_soc = -1;
   char next_soc[MAX_STRING_LENGTH], sorted[MAX_INPUT_LENGTH];

   /* open social file */
   if (!(fl = fopen(SOCMESS_FILE, "r"))) {
      sprintf(buf, "Can't open socials file '%s'", SOCMESS_FILE);

   /* count socials & allocate space */
   *next_soc = NULL;
   while (!feof(fl)) {
      fgets(next_soc, MAX_STRING_LENGTH, fl);
      if (*next_soc == '~') top_of_socialt++;
   sprintf(buf, "Social table contains %d socials.", top_of_socialt);
   CREATE(soc_mess_list, struct social_messg, top_of_socialt + 1);

   /* now read 'em */
   for (;;) {
      fscanf(fl, " %s ", next_soc);
      if (*next_soc == '$') break;
      if (fscanf(fl, " %s %d %d %d %d \n",
         sorted, &hide, &min_char_pos, &min_pos, &min_lvl) != 5) {
     fprintf(stderr, "Format error in social file near social '%s'\n",
      /* read the stuff */
      soc_mess_list[curr_soc].command = str_dup(next_soc+1);
      soc_mess_list[curr_soc].sort_as = str_dup(sorted);
      soc_mess_list[curr_soc].hide = hide;
      soc_mess_list[curr_soc].min_char_position = min_char_pos;
      soc_mess_list[curr_soc].min_victim_position = min_pos;
      soc_mess_list[curr_soc].min_level_char = min_lvl;

      soc_mess_list[curr_soc].char_no_arg = fread_action(fl, nr);
      soc_mess_list[curr_soc].others_no_arg = fread_action(fl, nr);
      soc_mess_list[curr_soc].char_found = fread_action(fl, nr);
      soc_mess_list[curr_soc].others_found = fread_action(fl, nr);
      soc_mess_list[curr_soc].vict_found = fread_action(fl, nr);
      soc_mess_list[curr_soc].not_found = fread_action(fl, nr);
      soc_mess_list[curr_soc].char_auto = fread_action(fl, nr);
      soc_mess_list[curr_soc].others_auto = fread_action(fl, nr);

   /* close file & set top */
   top_of_socialt = curr_soc;

/* this function adds in the loaded socials and assigns them a command # */
void create_command_list(void)  {
   int i, j, k;
   struct social_messg temp;
   extern struct command_info *used_cmd_info;
   extern struct command_info cmd_info[];

   /* free up old command list */
   if (used_cmd_info) free(used_cmd_info);
   used_cmd_info = NULL;

   /* re check the sort on the socials */
   for (j = 0; j < top_of_socialt; j++) {
      k = j;
      for (i = j + 1; i <= top_of_socialt; i++)
        if (str_cmp(soc_mess_list[i].sort_as, soc_mess_list[k].sort_as) < 0)
        k = i;
      if (j != k) {
     temp = soc_mess_list[j];
     soc_mess_list[j] = soc_mess_list[k];
     soc_mess_list[k] = temp;

   /* count the commands in the command list */
   i = 0;
   while(*cmd_info[i].command != '\n') i++;

   CREATE(used_cmd_info, struct command_info, top_of_socialt + i + 2);

   /* this loop sorts the socials and commands together into one big list */
   i = 0;
   j = 0;
   k = 0;
   while ((*cmd_info[i].command != '\n') || (j <= top_of_socialt))  {
      if ((i < NUM_RESERVED_CMDS) ||
      (j > top_of_socialt) ||
      (str_cmp(cmd_info[i].sort_as, soc_mess_list[j].sort_as) < 1))
        used_cmd_info[k++] = cmd_info[i++];
      else  {
     soc_mess_list[j].act_nr        = k;
     used_cmd_info[k].command       = soc_mess_list[j].command;
     used_cmd_info[k].sort_as       = soc_mess_list[j].sort_as;
     used_cmd_info[k].minimum_position  = soc_mess_list[j].min_char_position;
     used_cmd_info[k].command_pointer   = do_action;
     used_cmd_info[k].minimum_level     = soc_mess_list[j++].min_level_char;
     used_cmd_info[k].subcmd        = 0;
     used_cmd_info[k++].show            = 1;
   used_cmd_info[k].command     = str_dup("\n");
   used_cmd_info[k].sort_as     = str_dup("zzzzzzz");
   used_cmd_info[k].minimum_position    = 0;
   used_cmd_info[k].command_pointer = 0;
   used_cmd_info[k].minimum_level   = 0;
   used_cmd_info[k].subcmd      = 0;
   used_cmd_info[k].show            = 0;
   sprintf(buf, "Command info rebuilt, %d total commands.", k);

    Then.. in db.c i added a prototype at the top:
void create_command_list(void);

    Then where it boots all the info up i added a line:
   log("Loading social messages.");
   create_command_list();    /* newline */

    This effectively read all the info from the socials file, then 
sorted it with what was already in the cmd_info variable (in 
interpreter.c) and ended up in used_cmd_info..
    So this means you have to go back through interpreter.c and 
REMOVE any line with the do_action statement in it.. (its a social) and 
put the appropriate info into the socials file .. (ie. min level able to 
do the command, min position .. etc)..
    BUT there is ONE more slot which i added to retain the basic 
functionality of the cmd_info list.. and thats a "sort_as" variable.. 
which tells the create_command_info WHERE to place the command.. (for 
example many muds, want the tell command to trigger when you type 't 
person blah' .. henceforth they place the tell command at the beginning 
of the 't' commands.  so .. here's an example of what i did to my 
command_info structure (in interpreter.h):

struct command_info {
   char *command;
   char *sort_as;
   byte minimum_position;
   void (*command_pointer) (struct char_data *ch, char * argument, int cmd, int subcmd);
   sh_int minimum_level;
   int  subcmd;
   int  show;

then here's an example of the 't' section of my interpreter.c's 
definition of cmd_info:

  { "tell"  , "t"       , POS_DEAD    , do_tell     , 0, 0, 1 },
  { "take"  , "take"    , POS_RESTING , do_get      , 0, 0, 1 },
  { "taste" , "taste"   , POS_RESTING , do_eat      , 0, SCMD_TASTE, 1 },
  { "teleport"  , "teleport"    , POS_DEAD    , do_teleport , LVL_GOD, 0, 1 },
  { "thaw"  , "thaw"    , POS_DEAD    , do_wizutil  , LVL_FREEZE, SCMD_THAW, 1 },
  { "title" , "ti"      , POS_DEAD    , do_title    , 0, 0, 1 },
  { "time"  , "time"    , POS_DEAD    , do_time     , 0, 0, 1 },
  { "toggle"    , "toggle"  , POS_DEAD    , do_toggle   , 0, 0, 1 },
  { "transfer"  , "transfer"    , POS_SLEEPING, do_trans    , LVL_GOD, 0, 1 },
  { "typo"  , "typo"    , POS_DEAD    , do_gen_write, 0, SCMD_TYPO, 1 },

    notice that the first field (the actual command name) is NOT 
sorted into alphabetical order.. but the second field (the newly added 
sort_as field) IS sorted into alphabetical order.. this means that when the
create_command_list() routine goes to add in the socials it will know 
where to place each social.. (note: the second field now back in the 
example of the 'socials' file .. is the same as the second field here in 
the command_info structure of cmd_info, it tells the social where to go 
in relation to the next ACTUAL command..)  DO NOT make the sort_as field 
a value equivalent to 'LATER' than the original command .. this field is 
ONLY meant to tell the create_command_list() routine if a command was 
meant to be shifted before other commands.. (or else all the commands 
behind the command which you put a 'later than' sort_as field will be 
shifted too.)

    Hmm... then I'd guess you have your own dynamic command list.. 
which enables you to build a aedit..(pretty much copy redit.c then 
replace with the appropriate menu and defines in OLC.H .)..
heres an example of my aedit.c (most important routines):

**  File: aedit.c
**  Comment: OLC for MUDs -- this one edits socials

#include "conf.h"
#include "sysdep.h"

#include "structs.h"
#include "interpreter.h"
#include "handler.h"
#include "comm.h"
#include "utils.h"
#include "db.h"
#include "olc.h"
#include "screen.h"

extern int               top_of_socialt;
extern struct social_messg  *soc_mess_list;
extern char         *edit_errors[];
extern char         *position_types[];

#define RESERVE_CMDS        15  /* reserve these commands to come straight
                     * from the cmd list then start sorting */

/* external functs */
void create_command_list(void);
void free_action(struct social_messg *action);

/* function protos */
void aedit_disp_menu(struct descriptor_data * d);
void aedit_parse(struct descriptor_data * d, char *arg);
void aedit_setup_new(struct descriptor_data *d);
void aedit_setup_existing(struct descriptor_data *d, int real_num);
void aedit_save_to_disk(struct descriptor_data *d);
void aedit_save_internally(struct descriptor_data *d);

** Utils and exported functions.

void aedit_setup_new(struct descriptor_data *d) {
   CREATE(OLC_ACTION(d), struct social_messg, 1);
   OLC_ACTION(d)->command = str_dup("newcmd");
   OLC_ACTION(d)->sort_as = str_dup("newcmd");
   OLC_ACTION(d)->hide    = 0;
   OLC_ACTION(d)->min_victim_position = POS_STANDING;
   OLC_ACTION(d)->min_char_position   = POS_STANDING;
   OLC_ACTION(d)->min_level_char      = 0;
   OLC_ACTION(d)->char_no_arg = str_dup("This action is unfinished.");
   OLC_ACTION(d)->others_no_arg = str_dup("This action is unfinished.");
   OLC_ACTION(d)->char_found = NULL;
   OLC_ACTION(d)->others_found = NULL;
   OLC_ACTION(d)->vict_found = NULL;
   OLC_ACTION(d)->not_found = NULL;
   OLC_ACTION(d)->char_auto = NULL;
   OLC_ACTION(d)->others_auto = NULL;
   OLC_VAL(d) = 0;


void aedit_setup_existing(struct descriptor_data *d, int real_num) {
   CREATE(OLC_ACTION(d), struct social_messg, 1);
   OLC_ACTION(d)->command = str_dup(soc_mess_list[real_num].command);
   OLC_ACTION(d)->sort_as = str_dup(soc_mess_list[real_num].sort_as);
   OLC_ACTION(d)->hide    = soc_mess_list[real_num].hide;
   OLC_ACTION(d)->min_victim_position = soc_mess_list[real_num].min_victim_position;
   OLC_ACTION(d)->min_char_position   = soc_mess_list[real_num].min_char_position;
   OLC_ACTION(d)->min_level_char      = soc_mess_list[real_num].min_level_char;
   OLC_ACTION(d)->char_no_arg   =
   ((soc_mess_list[real_num].char_no_arg != NULL)?str_dup(soc_mess_list[real_num].char_no_arg):NULL);
   OLC_ACTION(d)->others_no_arg =
   ((soc_mess_list[real_num].others_no_arg != NULL)?str_dup(soc_mess_list[real_num].others_no_arg):NULL);
   OLC_ACTION(d)->char_found    =
   ((soc_mess_list[real_num].char_found != NULL)?str_dup(soc_mess_list[real_num].char_found):NULL);
   OLC_ACTION(d)->others_found  =
   ((soc_mess_list[real_num].others_found != NULL)?str_dup(soc_mess_list[real_num].others_found):NULL);
   OLC_ACTION(d)->vict_found    =
   ((soc_mess_list[real_num].vict_found != NULL)?str_dup(soc_mess_list[real_num].vict_found):NULL);
   OLC_ACTION(d)->not_found     =
   ((soc_mess_list[real_num].not_found != NULL)?str_dup(soc_mess_list[real_num].not_found):NULL);
   OLC_ACTION(d)->char_auto     =
   ((soc_mess_list[real_num].char_auto != NULL)?str_dup(soc_mess_list[real_num].char_auto):NULL);
   OLC_ACTION(d)->others_auto   =
   ((soc_mess_list[real_num].others_auto != NULL)?str_dup(soc_mess_list[real_num].others_auto):NULL);
   OLC_VAL(d) = 0;

void aedit_save_internally(struct descriptor_data *d) {
   struct social_messg *new_soc_mess_list = NULL;
   int i;
   /* add a new social into the list */
   if (OLC_ZNUM(d) > top_of_socialt)  {
      CREATE(new_soc_mess_list, struct social_messg, top_of_socialt + 2);
      for (i = 0; i <= top_of_socialt; i++)
        new_soc_mess_list[i] = soc_mess_list[i];
      new_soc_mess_list[++top_of_socialt] = *OLC_ACTION(d);
      soc_mess_list = new_soc_mess_list;
   /* pass the editted action back to the list - no need to add */
   else {
      soc_mess_list[OLC_ZNUM(d)] = *OLC_ACTION(d);
   olc_add_to_save_list(-1, OLC_SAVE_ACTION);


void aedit_save_to_disk(struct descriptor_data *d) {
   FILE *fp;
   int i;

   if (!(fp = fopen(SOCMESS_FILE, "w+")))  {
      sprintf(buf, "Can't open socials file '%s'", SOCMESS_FILE);

   for (i = 0; i <= top_of_socialt; i++)  {
      sprintf(buf, "~%s %s %d %d %d %d\n",
      fputs(buf, fp);
      sprintf(buf, "%s\n%s\n%s\n%s\n",
      fputs(buf, fp);
      sprintf(buf, "%s\n%s\n%s\n%s\n\n",
      fputs(buf, fp);
   fprintf(fp, "$\n");
   olc_remove_from_save_list(-1, OLC_SAVE_ACTION);


/* Menu functions */

    Anyways .. i am SURE that this is FAR from complete, but as for 
a quicky explanation of what it took to impl an aedit function.. i hope 
it covers all the basis.

        I for some reason i forgot a major portion of 
something, and you're still stumped, let me know.

                                  Michael Scott aka Manx!
                   FLAMES ==

Ps. Sorry for the long post.. but the code examples I felt sortof added 
to the easability of understanding.
A Tickism minute:
On justice and on friendship, there is no price,
but there are established credit limits.
         -- The Tick
| David Carver, Programmer Analyst        "Spooooooooonnnnnn!!!"   |      
| Columbus State Community College               -- The Tick       |
| Email:                          | 
Thought for the day:
    Bagpipes (n): an octopus wearing a kilt.

| David Carver, Programmer Analyst        "Spooooooooonnnnnn!!!"   |      
| Columbus State Community College               -- The Tick       |
| Email:                          | 

| Ensure that you have read the CircleMUD Mailing List FAQ: |
|   |

This archive was generated by hypermail 2b30 : 12/18/00 PST