nakedmudv3.3/
nakedmudv3.3/lib/
nakedmudv3.3/lib/logs/
nakedmudv3.3/lib/misc/
nakedmudv3.3/lib/players/
nakedmudv3.3/lib/txt/
nakedmudv3.3/lib/world/
nakedmudv3.3/lib/world/examples/
nakedmudv3.3/lib/world/examples/mproto/
nakedmudv3.3/lib/world/examples/oproto/
nakedmudv3.3/lib/world/examples/reset/
nakedmudv3.3/lib/world/examples/rproto/
nakedmudv3.3/lib/world/examples/trigger/
nakedmudv3.3/lib/world/limbo/
nakedmudv3.3/lib/world/limbo/room/
nakedmudv3.3/lib/world/limbo/rproto/
nakedmudv3.3/src/alias/
nakedmudv3.3/src/char_vars/
nakedmudv3.3/src/editor/
nakedmudv3.3/src/example_module/
nakedmudv3.3/src/help/
nakedmudv3.3/src/set_val/
nakedmudv3.3/src/socials/
nakedmudv3.3/src/time/
//*****************************************************************************
//
// socials.c
//
// socials are commonly used emotes (e.g. smiling, grinning, laughing). Instead
// making people have to write out an entire emote every time they would like
// to express such an emote, they can simply use one of these simple commands
// to perform a pre-made emote.
//
//*****************************************************************************

#include "../mud.h"
#include "../utils.h"
#include "../storage.h"
#include "../handler.h"     
#include "../inform.h"
#include "../character.h"

#include "socedit.h"
#include "socials.h"



//*****************************************************************************
//
// local datastructures, defines, and functions
//
//*****************************************************************************

// the file where all of the socials are stored
#define SOCIALS_FILE "../lib/misc/socials"

// the table we store all of the socials in
HASHTABLE *social_table = NULL;


//
// save all of the socials to disk
//
void save_socials() {
  STORAGE_SET       *set = new_storage_set();
  LIST         *soc_list = newList();

  // iterate across the social table and save all of the unique socials
  HASH_ITERATOR  *hash_i = newHashIterator(social_table);
  const char        *cmd = NULL;
  SOCIAL_DATA      *data = NULL;

  ITERATE_HASH(cmd, data, hash_i)
    listPut(soc_list, data);
  deleteHashIterator(hash_i);

  store_list(set, "socials", gen_store_list(soc_list, socialStore));
  deleteList(soc_list);

  // write the set
  storage_write(set, SOCIALS_FILE);
  
  // close the set
  storage_close(set);
}



//*****************************************************************************
//
// Implementation of the social datastructure
//
//*****************************************************************************
struct social_data {
  char *cmds;          // the list of commands we fire off of
  char *to_char_notgt; // the message to us when there's no target
  char *to_room_notgt; // the message to the room when there's no target
  char *to_char_self;  // the message to ourself when tgt == us
  char *to_room_self;  // the message to the room when tgt == us
  char *to_char_tgt;   // the message to us when there's a target
  char *to_vict_tgt;   // the message to the target
  char *to_room_tgt;   // the message to the room when there's a target

  int min_pos;         // the minimum position we can perform this from
  int max_pos;         // and the maximum
};


SOCIAL_DATA *newSocial(const char *cmds,
		       const char *to_char_notgt,
		       const char *to_room_notgt,
		       const char *to_char_self,
		       const char *to_room_self,
		       const char *to_char_tgt,
		       const char *to_vict_tgt,
		       const char *to_room_tgt,
		       int min_pos, int max_pos) {
  SOCIAL_DATA *data = malloc(sizeof(SOCIAL_DATA));
  data->cmds          = strdupsafe(cmds);
  data->to_char_notgt = strdupsafe(to_char_notgt);
  data->to_room_notgt = strdupsafe(to_room_notgt);
  data->to_char_self  = strdupsafe(to_char_self);
  data->to_room_self  = strdupsafe(to_room_self);
  data->to_char_tgt   = strdupsafe(to_char_tgt);
  data->to_vict_tgt   = strdupsafe(to_vict_tgt);
  data->to_room_tgt   = strdupsafe(to_room_tgt);
  data->min_pos       = min_pos;
  data->max_pos       = max_pos;
  return data;
}


void deleteSocial(SOCIAL_DATA *data) {
  if(data->cmds)          free(data->cmds);
  if(data->to_char_notgt) free(data->to_char_notgt);
  if(data->to_room_notgt) free(data->to_room_notgt);
  if(data->to_char_self)  free(data->to_char_self);
  if(data->to_room_self)  free(data->to_room_self);
  if(data->to_char_tgt)   free(data->to_char_tgt);
  if(data->to_vict_tgt)   free(data->to_vict_tgt);
  if(data->to_room_tgt)   free(data->to_room_tgt);
  free(data);
}


STORAGE_SET *socialStore(SOCIAL_DATA *data) {
  STORAGE_SET *set = new_storage_set();
  store_string(set, "cmds",          data->cmds);
  store_string(set, "to_char_notgt", data->to_char_notgt);
  store_string(set, "to_room_notgt", data->to_room_notgt);
  store_string(set, "to_char_self",  data->to_char_self);
  store_string(set, "to_room_self",  data->to_room_self);
  store_string(set, "to_char_tgt",   data->to_char_tgt);
  store_string(set, "to_vict_tgt",   data->to_vict_tgt);
  store_string(set, "to_room_tgt",   data->to_room_tgt);
  store_string(set, "min_pos",       posGetName(data->min_pos));
  store_string(set, "max_pos",       posGetName(data->max_pos));
  return set;
}


SOCIAL_DATA *socialRead(STORAGE_SET *set) {
  return newSocial(read_string(set, "cmds"),
		   read_string(set, "to_char_notgt"),
		   read_string(set, "to_room_notgt"),
		   read_string(set, "to_char_self"),
		   read_string(set, "to_room_self"),
		   read_string(set, "to_char_tgt"),
		   read_string(set, "to_vict_tgt"),
		   read_string(set, "to_room_tgt"),
		   posGetNum(read_string(set, "min_pos")),
		   posGetNum(read_string(set, "max_pos")));
}


void socialCopyTo(SOCIAL_DATA *from, SOCIAL_DATA *to) {
  // free all of the strings
  if(to->cmds)          free(to->cmds);
  if(to->to_char_notgt) free(to->to_char_notgt);
  if(to->to_room_notgt) free(to->to_room_notgt);
  if(to->to_char_self)  free(to->to_char_self);
  if(to->to_room_self)  free(to->to_room_self);
  if(to->to_char_tgt)   free(to->to_char_tgt);
  if(to->to_vict_tgt)   free(to->to_vict_tgt);
  if(to->to_room_tgt)   free(to->to_room_tgt);

  // copy over all of the new descs and commands
  to->cmds          = strdupsafe(from->cmds);
  to->to_char_notgt = strdupsafe(from->to_char_notgt);
  to->to_room_notgt = strdupsafe(from->to_room_notgt);
  to->to_char_self  = strdupsafe(from->to_char_self);
  to->to_room_self  = strdupsafe(from->to_room_self);
  to->to_char_tgt   = strdupsafe(from->to_char_tgt);
  to->to_vict_tgt   = strdupsafe(from->to_vict_tgt);
  to->to_room_tgt   = strdupsafe(from->to_room_tgt);
  to->min_pos       = from->min_pos;
  to->max_pos       = from->max_pos;
}


SOCIAL_DATA *socialCopy(SOCIAL_DATA *social) {
  SOCIAL_DATA *new_soc = newSocial("", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
				   POS_STANDING, POS_STANDING);
  socialCopyTo(social, new_soc);
  return new_soc;
}

const char *socialGetCmds(SOCIAL_DATA *social) {
  return social->cmds;
}

const char *socialGetCharNotgt(SOCIAL_DATA *social) {
  return social->to_char_notgt;
}

const char *socialGetRoomNotgt(SOCIAL_DATA *social) {
  return social->to_room_notgt;
}

const char *socialGetCharSelf (SOCIAL_DATA *social) {
  return social->to_char_self;
}

const char *socialGetRoomSelf (SOCIAL_DATA *social) {
  return social->to_room_self;
}

const char *socialGetCharTgt(SOCIAL_DATA *social) {
  return social->to_char_tgt;
}

const char *socialGetVictTgt(SOCIAL_DATA *social) {
  return social->to_vict_tgt;
}

const char *socialGetRoomTgt(SOCIAL_DATA *social) {
  return social->to_room_tgt;
}

int socialGetMinPos(SOCIAL_DATA *social) {
  return social->min_pos;
}

int socialGetMaxPos(SOCIAL_DATA *social) {
  return social->max_pos;
}

void socialSetCharNotgt(SOCIAL_DATA *social, const char *mssg) {
  if(social->to_char_notgt) free(social->to_char_notgt);
  social->to_char_notgt   = strdupsafe(mssg);
}

void socialSetRoomNotgt(SOCIAL_DATA *social, const char *mssg) {
  if(social->to_room_notgt) free(social->to_room_notgt);
  social->to_room_notgt   = strdupsafe(mssg);
}

void socialSetCharSelf(SOCIAL_DATA *social, const char *mssg) {
  if(social->to_char_self) free(social->to_char_self);
  social->to_char_self   = strdupsafe(mssg);
}

void socialSetRoomSelf(SOCIAL_DATA *social, const char *mssg) {
  if(social->to_room_self) free(social->to_room_self);
  social->to_room_self   = strdupsafe(mssg);
}

void socialSetCharTgt(SOCIAL_DATA *social, const char *mssg) {
  if(social->to_char_tgt) free(social->to_char_tgt);
  social->to_char_tgt   = strdupsafe(mssg);
}

void socialSetVictTgt(SOCIAL_DATA *social, const char *mssg) {
  if(social->to_vict_tgt) free(social->to_vict_tgt);
  social->to_vict_tgt   = strdupsafe(mssg);
}

void socialSetRoomTgt(SOCIAL_DATA *social, const char *mssg) {
  if(social->to_room_tgt) free(social->to_room_tgt);
  social->to_room_tgt   = strdupsafe(mssg);
}

void socialSetMinPos(SOCIAL_DATA *social, int pos) {
  social->min_pos = pos;
}

void socialSetMaxPos(SOCIAL_DATA *social, int pos) {
  social->max_pos = pos;
}


//
// Link one social to another
//   usage: soclink [new command] [old command]
//
COMMAND(cmd_soclink) {
  char new_soc[SMALL_BUFFER];

  if(!arg || !*arg) {
    send_to_char(ch, "Link which social to which?\r\n");
    return;
  }

  arg = one_arg(arg, new_soc);

  if(!*new_soc || !*arg) {
    send_to_char(ch, "You must provide a new command and an old social "
		 "to link it to.\r\n");
    return;
  }

  // find the social we're trying to link to
  SOCIAL_DATA *social = get_social(arg);

  if(social == NULL) {
    send_to_char(ch, "No social exists for %s.\r\n", arg);
    return;
  }

  // perform the link
  link_social(new_soc, arg);
  send_to_char(ch, "%s linked to %s.\r\n", new_soc, arg);
}


//
// Unlink a social
//   usage: socunlink [command]
//
COMMAND(cmd_socunlink) {
  if(!arg || !*arg) {
    send_to_char(ch, "Unlink which social?\r\n");
    return;
  }

  // find the social we're trying to link to
  SOCIAL_DATA *social = get_social(arg);

  if(social == NULL) {
    send_to_char(ch, "No social exists for %s.\r\n", arg);
    return;
  }

  // perform the unlinking
  unlink_social(arg);
  send_to_char(ch, "The %s social was unlinked.\r\n", arg);
}


//
// List all of the socials to the character
//
COMMAND(cmd_socials) {
  HASH_ITERATOR  *soc_i = newHashIterator(social_table);
  const char       *soc = NULL;
  SOCIAL_DATA     *data = NULL;
  LIST        *soc_list = newList();
  int count             = 0;

  // go through the hashtable and pull out each key
  ITERATE_HASH(soc, data, soc_i)
    listPut(soc_list, strdup(soc));
  deleteHashIterator(soc_i);

  // now sort them, and pop them out one at a time
  listSortWith(soc_list, strcmp);

  // and pop them out one at a time to print
  while( (soc = listPop(soc_list)) != NULL) {
    count++;
    send_to_char(ch, "%-20s%s", soc, (count % 4 == 0 ? "\r\n" : ""));    
  }
  deleteListWith(soc_list, free);

  if(count == 0 || count % 4 != 0)
    send_to_char(ch, "\r\n");
}



//
// One generic command for handling socials. Does table lookup on all of
// the existing socials and executes the proper one.
//
COMMAND(cmd_social) {
  // look up the social
  SOCIAL_DATA *data = hashGet(social_table, cmd);

  // does the social exist? Do we have a problem? DO WE?
  if(data != NULL) {
    // find the target
    CHAR_DATA *tgt = NULL;
    if(*arg) {
      int tgt_type = FOUND_NONE;
      tgt = generic_find(ch, arg, FIND_TYPE_CHAR, FIND_SCOPE_IMMEDIATE,
			 FALSE, &tgt_type);
    }

    // see if we were trying to find a target but couldn't
    if(*arg && !tgt)
      send_to_char(ch, "Who were you looking for?\r\n");
    // no target was supplied
    else if(!tgt) {
      if(*data->to_char_notgt)
	message(ch, NULL, NULL, NULL, TRUE, TO_CHAR, data->to_char_notgt);
      if(*data->to_room_notgt)
	message(ch, NULL, NULL, NULL, TRUE, TO_ROOM, data->to_room_notgt);
    }

    // a target was supplied, and it was us
    else if(ch == tgt) {
      if(*data->to_char_self)
	message(ch, NULL, NULL, NULL, TRUE, TO_CHAR, data->to_char_self);
      else if(*data->to_char_notgt)
	message(ch, NULL, NULL, NULL, TRUE, TO_CHAR, data->to_char_notgt);
      if(*data->to_room_self)
	message(ch, NULL, NULL, NULL, TRUE, TO_ROOM, data->to_room_self);
      else if(*data->to_room_notgt)
	message(ch, NULL, NULL, NULL, TRUE, TO_ROOM, data->to_room_notgt);
    }

    // a target was supplied and it was not us
    else {
      if(*data->to_char_tgt)
	message(ch, tgt, NULL, NULL, TRUE, TO_CHAR, data->to_char_tgt);
      if(*data->to_vict_tgt)
	message(ch, tgt, NULL, NULL, TRUE, TO_VICT, data->to_vict_tgt);
      if(*data->to_room_tgt)
	message(ch, tgt, NULL, NULL, TRUE, TO_ROOM, data->to_room_tgt);
    }
  }
  else
    log_string("ERROR: %s tried social, %s, but no such social exists!",
	charGetName(ch), cmd);
}



//*****************************************************************************
//
// implementation of socials.h
//
//*****************************************************************************

// I feel dirty doing this, but it's small and it saves us lots of work. So,
// well, we'll just have to live with it ;)
//
// init_socials calls add_social to add in all of the socials it loads up.
// But add_social calls save_socials each time that a new social is added.
// Thus, when we are booting up, we're making so so so many writes to disk
// that are un-needed. Here, we just have a little variable that tells whether
// we're booting up or if we're adding a new social in-game. If we're just
// booting up, then add_socials won't save any changes.
bool in_social_init = TRUE;

void init_socials() {
  // create the social table
  social_table = newHashtable();

  // open up the storage set
  STORAGE_SET       *set = storage_read(SOCIALS_FILE);
  STORAGE_SET_LIST *list = read_list(set, "socials");
  STORAGE_SET    *social = NULL;

  // parse all of the socials
  while( (social = storage_list_next(list)) != NULL)
    add_social(socialRead(social));
  
  // close the storage set
  storage_close(set);

  // add all of the socials to the command table
  HASH_ITERATOR *hash_i = newHashIterator(social_table);
  const char       *cmd = NULL;
  SOCIAL_DATA     *data = NULL;

  ITERATE_HASH(cmd, data, hash_i)
    add_cmd(cmd, NULL, cmd_social, data->min_pos, data->max_pos, 
	    "player", TRUE, FALSE);
  deleteHashIterator(hash_i);

  // link/unlink commands for the admins
  add_cmd("soclink", NULL, cmd_soclink, POS_UNCONSCIOUS, POS_FLYING,
	  "builder", FALSE, FALSE);
  add_cmd("socunlink", NULL, cmd_socunlink, POS_UNCONSCIOUS, POS_FLYING,
	  "builder", FALSE, FALSE);
  add_cmd("socials",   NULL, cmd_socials,   POS_UNCONSCIOUS, POS_FLYING,
	  "player",  TRUE, FALSE);

  // let add_social know it can start saving again
  in_social_init = FALSE;

  init_socedit();
}


SOCIAL_DATA *get_social(const char *cmd) {
  return hashGet(social_table, cmd);
}


void add_social(SOCIAL_DATA *social) {
  // for each of our keywords, go through and 
  // unlink all of the current socials and link the new one
  LIST       *cmd_list = parse_keywords(social->cmds);
  LIST_ITERATOR *cmd_i = newListIterator(cmd_list);
  char            *cmd = NULL;

  ITERATE_LIST(cmd, cmd_i) {
    unlink_social(cmd);
    hashPut(social_table, cmd, social);
    // add the new command to the game
    add_cmd(cmd, NULL, cmd_social, social->min_pos, social->max_pos,
	    "player", TRUE, FALSE);
  } deleteListIterator(cmd_i);

  // garbage collection
  deleteListWith(cmd_list, free);

  // save changes
  if(!in_social_init)
    save_socials();
}


void link_social(const char *new_cmd, const char *old_cmd) {
  // check for old_cmd
  SOCIAL_DATA *data = hashGet(social_table, old_cmd);

  // link new_cmd to old_cmd
  if(data != NULL) {
    // first, remove the current new_cmd social, if it exists
    unlink_social(new_cmd);

    // add the new keyword to the social
    add_keyword(&(data->cmds), new_cmd);
    
    // add the new command to the social table
    hashPut(social_table, new_cmd, data);
    
    // add the new command to the game
    add_cmd(new_cmd, NULL, cmd_social, data->min_pos, data->max_pos,
	    "player", TRUE, FALSE);
  }

  // save changes
  if(!in_social_init)
    save_socials();
}


void unlink_social(const char *cmd) {
  // remove the command
  SOCIAL_DATA *data = hashRemove(social_table, cmd);

  // unlink the social
  if(data != NULL) {
    remove_keyword(data->cmds, cmd);

    // remove the command from the command table
    remove_cmd(cmd);

    // if no links are left, delete the social
    if(!*data->cmds)
      deleteSocial(data);

    // save changes
    if(!in_social_init)
      save_socials();
  }
}