LOP/
LOP/area/
LOP/boards/
LOP/channels/
LOP/clans/
LOP/classes/
LOP/color/
LOP/councils/
LOP/deity/
LOP/races/
LOP/src/specials/
/*****************************************************************************
 * DikuMUD (C) 1990, 1991 by:                                                *
 *   Sebastian Hammer, Michael Seifert, Hans Henrik Staefeldt, Tom Madsen,   *
 *   and Katja Nyboe.                                                        *
 *---------------------------------------------------------------------------*
 * MERC 2.1 (C) 1992, 1993 by:                                               *
 *   Michael Chastain, Michael Quan, and Mitchell Tse.                       *
 *---------------------------------------------------------------------------*
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998 by: Derek Snider.                    *
 *   Team: Thoric, Altrag, Blodkai, Narn, Haus, Scryn, Rennard, Swordbearer, *
 *         gorog, Grishnakh, Nivek, Tricops, and Fireblade.                  *
 *---------------------------------------------------------------------------*
 * SMAUG 1.7 FUSS by: Samson and others of the SMAUG community.              *
 *                    Their contributions are greatly appreciated.           *
 *---------------------------------------------------------------------------*
 * LoP (C) 2006, 2007, 2008 by: the LoP team.                                *
 *---------------------------------------------------------------------------*
 *			     Special clan module			     *
 *****************************************************************************/

#include <ctype.h>
#include <stdio.h>
#include "h/mud.h"

static OBJ_DATA *rgObjNest[MAX_NEST];

CLAN_DATA *first_clan, *last_clan;
COUNCIL_DATA *first_council, *last_council;

int get_pc_class( char *Class );
int get_pc_race( char *type );

/* local routines */
void fread_clan( CLAN_DATA *clan, FILE *fp );
void write_clan_list( void );
void fread_council( COUNCIL_DATA *council, FILE *fp );
void write_council_list( void );

void free_member( MEMBER_DATA *member )
{
   if( !member )
      return;
   STRFREE( member->name );
   DISPOSE( member );
}

void free_clan_members( CLAN_DATA *clan )
{
   MEMBER_DATA *member, *member_next;

   if( !clan )
      return;
   for( member = clan->first_member; member; member = member_next )
   {
      member_next = member->next;
      UNLINK( member, clan->first_member, clan->last_member, next, prev );
      free_member( member );
   }
}

bool is_clan_member( CLAN_DATA *clan, char *name )
{
   MEMBER_DATA *member;

   if( !clan || !name || name[0] == '\0' )
      return false;
   for( member = clan->first_member; member; member = member->next )
   {
      if( !str_cmp( member->name, name ) )
         return true;
   }
   return false;
}

bool remove_clan_member( CLAN_DATA *clan, char *name )
{
   MEMBER_DATA *member;

   if( !clan || !name || name[0] == '\0' )
      return false;
   for( member = clan->first_member; member; member = member->next )
   {
      if( !str_cmp( member->name, name ) )
      {
         if( !str_cmp( name, clan->leader ) )
            STRFREE( clan->leader );
         if( !str_cmp( name, clan->number1 ) )
            STRFREE( clan->number1 );
         if( !str_cmp( name, clan->number2 ) )
            STRFREE( clan->number2 );
         UNLINK( member, clan->first_member, clan->last_member, next, prev );
         free_member( member );
         if( --clan->members < 0 )
            clan->members = 0;
         return true;
      }
   }
   return false;
}

void add_clan_member( CLAN_DATA *clan, char *name )
{
   MEMBER_DATA *member;

   if( !clan || !name || name[0] == '\0' )
      return;
   if( is_clan_member( clan, name ) )
      return;
   CREATE( member, MEMBER_DATA, 1 );
   member->name = STRALLOC( name );
   LINK( member, clan->first_member, clan->last_member, next, prev );
   if( ( clan->members + 1 ) > 0 )
      clan->members++;
}

void free_one_clan( CLAN_DATA *clan )
{
   if( !clan )
      return;
   free_clan_members( clan );
   UNLINK( clan, first_clan, last_clan, next, prev );
   STRFREE( clan->filename );
   STRFREE( clan->name );
   STRFREE( clan->motto );
   STRFREE( clan->description );
   STRFREE( clan->leader );
   STRFREE( clan->number1 );
   STRFREE( clan->number2 );
   STRFREE( clan->leadrank );
   STRFREE( clan->onerank );
   STRFREE( clan->tworank );
   STRFREE( clan->badge );
   DISPOSE( clan );
}

void free_clans( void )
{
   CLAN_DATA *clan, *clan_next;

   for( clan = first_clan; clan; clan = clan_next )
   {
      clan_next = clan->next;
      free_one_clan( clan );
   }
}

/* Get pointer to clan structure from clan name. */
CLAN_DATA *get_clan( char *name )
{
   CLAN_DATA *clan;

   for( clan = first_clan; clan; clan = clan->next )
      if( clan->name && name && !str_cmp( name, clan->name ) )
         return clan;
   return NULL;
}

void write_clan_list( void )
{
   CLAN_DATA *tclan;
   FILE *fp;

   if( !( fp = fopen( CLAN_LIST, "w" ) ) )
   {
      bug( "%s: can't open %s for writing!", __FUNCTION__, CLAN_LIST );
      return;
   }
   for( tclan = first_clan; tclan; tclan = tclan->next )
      fprintf( fp, "%s\n", tclan->filename );
   fprintf( fp, "$\n" );
   fclose( fp );
   fp = NULL;
}

/* Save a clan's data to its data file */
void save_clan( CLAN_DATA *clan )
{
   FILE *fp;
   char filename[256];
   MEMBER_DATA *member;

   if( !clan )
   {
      bug( "%s: NULL clan", __FUNCTION__ );
      return;
   }

   if( !clan->name )
   {
      bug( "%s: NULL clan->name", __FUNCTION__ );
      return;
   }

   if( !clan->filename )
   {
      bug( "%s: %s NULL filename", __FUNCTION__, clan->name );
      return;
   }

   snprintf( filename, sizeof( filename ), "%s%s", CLAN_DIR, clan->filename );
   if( !( fp = fopen( filename, "w" ) ) )
   {
      perror( filename );
      return;
   }

   fprintf( fp, "Name           %s~\n", clan->name );
   fprintf( fp, "Filename       %s~\n", clan->filename );
   if( clan->motto )
      fprintf( fp, "Motto          %s~\n", clan->motto );
   if( clan->description )
      fprintf( fp, "Description    %s~\n", clan->description );
   if( clan->leader )
   {
      fprintf( fp, "Leader         %s~\n", clan->leader );
      if( clan->leadrank )
         fprintf( fp, "Leadrank       %s~\n", clan->leadrank );
   }
   if( clan->number1 )
   {
      fprintf( fp, "NumberOne      %s~\n", clan->number1 );
      if( clan->onerank )
         fprintf( fp, "Onerank        %s~\n", clan->onerank );
   }
   if( clan->number2 )
   {
      fprintf( fp, "NumberTwo      %s~\n", clan->number2 );
      if( clan->tworank )
         fprintf( fp, "Tworank        %s~\n", clan->tworank );
   }
   if( clan->badge )
      fprintf( fp, "Badge          %s~\n", clan->badge );
   if( clan->clan_type )
      fprintf( fp, "Type           %d\n", clan->clan_type );
   if( clan->race )
      fprintf( fp, "Race           %d\n", clan->race );
   for( member = clan->first_member; member; member = member->next )
      fprintf( fp, "Member         %s~\n", member->name );
   if( clan->recall )
      fprintf( fp, "Recall         %d\n", clan->recall );
   if( clan->storeroom )
      fprintf( fp, "Storeroom      %d\n", clan->storeroom );
   fprintf( fp, "End\n\n" );
   fclose( fp );
   fp = NULL;
}

/* Read in actual clan data. */
CLAN_DATA *fread_clan( FILE *fp )
{
   CLAN_DATA *clan = NULL;
   const char *word;
   bool fMatch;

   CREATE( clan, CLAN_DATA, 1 );
   if( !clan )
      return NULL;

   clan->first_member = NULL;
   clan->last_member = NULL;
   clan->members = 0;
   for( ;; )
   {
      word = feof( fp ) ? "End" : fread_word( fp );
      fMatch = false;

      switch( UPPER( word[0] ) )
      {
         case '*':
            fMatch = true;
            fread_to_eol( fp );
            break;

         case 'B':
            KEY( "Badge", clan->badge, fread_string( fp ) );
            break;

         case 'D':
            KEY( "Description", clan->description, fread_string( fp ) );
            break;

         case 'E':
            if( !str_cmp( word, "End" ) )
               return clan;
            break;

         case 'F':
            KEY( "Filename", clan->filename, fread_string( fp ) );
            break;

         case 'L':
            KEY( "Leader", clan->leader, fread_string( fp ) );
            KEY( "Leadrank", clan->leadrank, fread_string( fp ) );
            break;

         case 'M':
            if( !str_cmp( word, "Member" ) )
            {
               char *name = fread_flagstring( fp );

               if( valid_pfile( name ) )
                  add_clan_member( clan, name );
               else
                  bug( "%s: not adding member %s because no pfile found.", __FUNCTION__, name );
               fMatch = true;
               break;
            }
            KEY( "Motto", clan->motto, fread_string( fp ) );
            break;

         case 'N':
            KEY( "Name", clan->name, fread_string( fp ) );
            KEY( "NumberOne", clan->number1, fread_string( fp ) );
            KEY( "NumberTwo", clan->number2, fread_string( fp ) );
            break;

         case 'O':
            KEY( "Onerank", clan->onerank, fread_string( fp ) );
            break;

         case 'R':
            KEY( "Recall", clan->recall, fread_number( fp ) );
            KEY( "Race", clan->race, fread_number( fp ) );
            break;

         case 'S':
            KEY( "Storeroom", clan->storeroom, fread_number( fp ) );
            break;

         case 'T':
            KEY( "Tworank", clan->tworank, fread_string( fp ) );
            KEY( "Type", clan->clan_type, fread_number( fp ) );
            break;
      }

      if( !fMatch )
      {
         bug( "%s: no match: %s", __FUNCTION__, word );
         fread_to_eol( fp );
      }
   }
   /* Made it here? Then lets free the clan and return NULL */
   free_one_clan( clan );
   return NULL;
}

/* Load a clan file */
void load_clan_file( const char *clanfile )
{
   char filename[256];
   CLAN_DATA *clan;
   FILE *fp;
   ROOM_INDEX_DATA *storeroom;
   int iNest;
   OBJ_DATA *tobj, *tobj_next;

   snprintf( filename, sizeof( filename ), "%s%s", CLAN_DIR, clanfile );
   if( !( fp = fopen( filename, "r" ) ) )
   {
      perror( filename );
      return;
   }

   clan = fread_clan( fp );
   fclose( fp );
   fp = NULL;

   if( !clan )
      return;

   LINK( clan, first_clan, last_clan, next, prev );

   if( clan->storeroom == 0 || !( storeroom = get_room_index( clan->storeroom ) ) )
   {
      log_string( "Storeroom not found" );
      return;
   }

   snprintf( filename, sizeof( filename ), "%s%s.vault", CLAN_DIR, clan->filename );
   if( !( fp = fopen( filename, "r" ) ) )
   {
      perror( filename );
      return;
   }

   log_string( "Loading clan storage room" );
   rset_supermob( storeroom );
   for( iNest = 0; iNest < MAX_NEST; iNest++ )
      rgObjNest[iNest] = NULL;
   for( ;; )
   {
      char letter;
      char *word;

      letter = fread_letter( fp );
      if( letter == '*' )
      {
         fread_to_eol( fp );
         continue;
      }
      if( letter != '#' )
      {
         bug( "%s: # not found for %s.", __FUNCTION__, clan->name );
         break;
      }
      word = fread_word( fp );
      if( !str_cmp( word, "OBJECT" ) ) /* Objects  */
         fread_obj( supermob, NULL, fp, OS_CARRY );
      else if( !str_cmp( word, "END" ) )  /* Done     */
         break;
      else
      {
         bug( "%s: bad section (%s) for %s.", __FUNCTION__, word, clan->name );
         break;
      }
   }
   fclose( fp );
   fp = NULL;

   for( tobj = supermob->first_carrying; tobj; tobj = tobj_next )
   {
      tobj_next = tobj->next_content;
      obj_from_char( tobj );
      obj_to_room( tobj, storeroom );
   }
   release_supermob( );
}

/* Load in all the clan files. */
void load_clans( void )
{
   FILE *fp;
   const char *filename;

   first_clan = last_clan = NULL;

   log_string( "Loading clans..." );
   if( !( fp = fopen( CLAN_LIST, "r" ) ) )
   {
      perror( CLAN_LIST );
      return;
   }

   for( ;; )
   {
      filename = feof( fp ) ? "$" : fread_word( fp );
      if( !filename || filename[0] == '\0' || filename[0] == '$' )
         break;
      log_string( filename );
      load_clan_file( filename );
   }
   fclose( fp );
   fp = NULL;
   log_string( " Done loading clans " );
}

CMDF( do_induct )
{
   char arg[MIL], buf[MSL];
   CHAR_DATA *victim;
   CLAN_DATA *clan;

   if( is_npc( ch ) || !ch->pcdata->clan )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   clan = ch->pcdata->clan;

   if( !is_name( "induct", ch->pcdata->bestowments )
   && str_cmp( ch->name, clan->leader ) && str_cmp( ch->name, clan->number1 ) && str_cmp( ch->name, clan->number2 ) )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   argument = one_argument( argument, arg );

   if( !arg || arg[0] == '\0' )
   {
      send_to_char( "Induct whom?\r\n", ch );
      return;
   }

   if( !( victim = get_char_room( ch, arg ) ) )
   {
      send_to_char( "That player is not here.\r\n", ch );
      return;
   }

   if( is_npc( victim ) )
   {
      send_to_char( "Not on NPC's.\r\n", ch );
      return;
   }

   if( is_immortal( victim ) )
   {
      send_to_char( "You can't induct such a godly presence.\r\n", ch );
      return;
   }

   if( clan->clan_type == CLAN_NATION )
   {
      if( victim->race != clan->race )
      {
         send_to_char( "This player's race is not in accordance with your clan.\r\n", ch );
         return;
      }
   }
   else
   {
      if( victim->level < 10 )
      {
         send_to_char( "This player is not worthy of joining yet.\r\n", ch );
         return;
      }

      if( victim->level > ch->level )
      {
         send_to_char( "This player is too powerful for you to induct.\r\n", ch );
         return;
      }
   }

   if( victim->pcdata->clan )
   {
      ch_printf( ch, "This player already belongs to %s clan!\r\n", victim->pcdata->clan == clan ? "your" : "a" );
      return;
   }

   if( xIS_SET( victim->act, PLR_NOINDUCT ) )
   {
      send_to_char( "This player doesn't currently wish to be inducted into a clan.\r\n", ch );
      return;
   }

   if( clan->clan_type == CLAN_PLAIN )
      xSET_BIT( victim->pcdata->flags, PCFLAG_DEADLY );

   victim->pcdata->clan = clan;
   add_clan_member( clan, victim->name );
   act( AT_MAGIC, "You induct $N into $t", ch, clan->name, victim, TO_CHAR );
   act( AT_MAGIC, "$n inducts $N into $t", ch, clan->name, victim, TO_NOTVICT );
   act( AT_MAGIC, "$n inducts you into $t", ch, clan->name, victim, TO_VICT );
   snprintf( buf, sizeof( buf ), "%s has been inducted into %s!", victim->name, clan->name );
   echo_to_all( AT_MAGIC, buf, ECHOTAR_ALL );
   save_char_obj( victim );
   save_clan( clan );
}

CMDF( do_nation_induct )
{
   char buf[MSL];
   CHAR_DATA *victim;
   CLAN_DATA *nation;

   if( is_npc( ch ) || !ch->pcdata->nation )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   nation = ch->pcdata->nation;

   if( !is_name( "ninduct", ch->pcdata->bestowments )
   && str_cmp( ch->name, nation->leader ) && str_cmp( ch->name, nation->number1 ) && str_cmp( ch->name, nation->number2 ) )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   if( !argument || argument[0] == '\0' )
   {
      send_to_char( "Ninduct whom?\r\n", ch );
      return;
   }

   if( !( victim = get_char_room( ch, argument ) ) )
   {
      send_to_char( "That player is not here.\r\n", ch );
      return;
   }

   if( is_npc( victim ) || victim->pcdata->nation || is_immortal( victim ) || victim->race != nation->race )
   {
      send_to_char( "You can't ninduct them.\r\n", ch );
      return;
   }

   if( xIS_SET( victim->act, PLR_NOINDUCT ) )
   {
      send_to_char( "This player doesn't currently wish to be inducted into a nation.\r\n", ch );
      return;
   }

   victim->pcdata->nation = nation;
   add_clan_member( nation, victim->name );
   act( AT_MAGIC, "You induct $N into $t", ch, nation->name, victim, TO_CHAR );
   act( AT_MAGIC, "$n inducts $N into $t", ch, nation->name, victim, TO_NOTVICT );
   act( AT_MAGIC, "$n inducts you into $t", ch, nation->name, victim, TO_VICT );
   snprintf( buf, sizeof( buf ), "%s has been inducted into %s!", victim->name, nation->name );
   echo_to_all( AT_MAGIC, buf, ECHOTAR_ALL );
   save_char_obj( victim );
   save_clan( nation );
}

/* Can the character outcast the victim? */
bool can_outcast( CLAN_DATA *clan, CHAR_DATA *ch, CHAR_DATA *victim )
{
   if( !clan || !ch || !victim )
      return false;
   if( !str_cmp( ch->name, clan->leader ) )
      return true;
   if( !str_cmp( victim->name, clan->leader ) )
      return false;
   if( !str_cmp( ch->name, clan->number1 ) )
      return true;
   if( !str_cmp( victim->name, clan->number1 ) )
      return false;
   if( !str_cmp( ch->name, clan->number2 ) )
      return true;
   if( !str_cmp( victim->name, clan->number2 ) )
      return false;
   return true;
}

CMDF( do_outcast )
{
   char buf[MSL];
   CHAR_DATA *victim;
   CLAN_DATA *clan;

   if( is_npc( ch ) || !ch->pcdata->clan )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   clan = ch->pcdata->clan;

   if( !is_name( "outcast", ch->pcdata->bestowments )
   && str_cmp( ch->name, clan->leader ) && str_cmp( ch->name, clan->number1 ) && str_cmp( ch->name, clan->number2 ) )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   if( !argument || argument[0] == '\0' )
   {
      send_to_char( "Outcast whom?\r\n", ch );
      return;
   }

   if( !str_cmp( ch->name, clan->leader ) )
      victim = get_char_world( ch, argument );
   else
      victim = get_char_room( ch, argument );

   if( !victim || is_npc( victim ) )
   {
      if( !victim && !str_cmp( ch->name, clan->leader ) && remove_clan_member( clan, argument ) )
      {
         send_to_char( "That player has been removed from your clan.\r\n", ch );
         snprintf( buf, sizeof( buf ), "%s has been outcast from %s!", capitalize( argument ), clan->name );
         echo_to_all( AT_MAGIC, buf, ECHOTAR_ALL );
         save_clan( clan );
      }
      else
         send_to_char( "That player is not here.\r\n", ch );
      return;
   }

   if( victim == ch )
   {
      ch_printf( ch, "Kick yourself out of your own %s?\r\n", "clan" );
      return;
   }

   if( victim->pcdata->clan != ch->pcdata->clan )
   {
      ch_printf( ch, "This player does not belong to your %s!\r\n", "clan" );
      return;
   }

   if( !can_outcast( clan, ch, victim ) )
   {
      send_to_char( "You aren't able to outcast them.\r\n", ch );
      return;
   }

   remove_clan_member( clan, victim->name );
   victim->pcdata->clan = NULL;
   act( AT_MAGIC, "You outcast $N from $t", ch, clan->name, victim, TO_CHAR );
   act( AT_MAGIC, "$n outcasts $N from $t", ch, clan->name, victim, TO_ROOM );
   act( AT_MAGIC, "$n outcasts you from $t", ch, clan->name, victim, TO_VICT );
   snprintf( buf, sizeof( buf ), "%s has been outcast from %s!", victim->name, clan->name );
   echo_to_all( AT_MAGIC, buf, ECHOTAR_ALL );
   save_char_obj( victim );
   save_clan( clan );
}

CMDF( do_nation_outcast )
{
   char buf[MSL];
   CHAR_DATA *victim;
   CLAN_DATA *nation;

   if( is_npc( ch ) || !ch->pcdata->clan )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   nation = ch->pcdata->nation;

   if( !is_name( "noutcast", ch->pcdata->bestowments )
   && str_cmp( ch->name, nation->leader ) && str_cmp( ch->name, nation->number1 ) && str_cmp( ch->name, nation->number2 ) )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   if( !argument || argument[0] == '\0' )
   {
      send_to_char( "Noutcast whom?\r\n", ch );
      return;
   }

   if( !str_cmp( ch->name, nation->leader ) )
      victim = get_char_world( ch, argument );
   else
      victim = get_char_room( ch, argument );

   if( !victim || is_npc( victim ) )
   {
      if( !victim && !str_cmp( ch->name, nation->leader ) && remove_clan_member( nation, argument ) )
      {
         send_to_char( "That player has been removed from your nation.\r\n", ch );
         snprintf( buf, sizeof( buf ), "%s has been outcast from %s!", capitalize( argument ), nation->name );
         echo_to_all( AT_MAGIC, buf, ECHOTAR_ALL );
         save_clan( nation );
      }
      else
         send_to_char( "That player is not here.\r\n", ch );
      return;
   }

   if( victim == ch || victim->pcdata->nation != nation )
   {
      ch_printf( ch, "You can't kick %s out of your nation!\r\n", victim == ch ? "yourself" : "them" );
      return;
   }

   if( !can_outcast( nation, ch, victim ) )
   {
      send_to_char( "You aren't able to outcast them.\r\n", ch );
      return;
   }

   remove_clan_member( nation, victim->name );
   victim->pcdata->nation = NULL;
   act( AT_MAGIC, "You outcast $N from $t", ch, nation->name, victim, TO_CHAR );
   act( AT_MAGIC, "$n outcasts $N from $t", ch, nation->name, victim, TO_ROOM );
   act( AT_MAGIC, "$n outcasts you from $t", ch, nation->name, victim, TO_VICT );
   snprintf( buf, sizeof( buf ), "%s has been outcast from %s!", victim->name, nation->name );
   echo_to_all( AT_MAGIC, buf, ECHOTAR_ALL );
   save_char_obj( victim );
   save_clan( nation );
}

void show_clan( CHAR_DATA *ch, CLAN_DATA *clan )
{
   MEMBER_DATA *member;
   int cnt = 0;

   set_char_color( AT_PLAIN, ch );

   if( is_npc( ch ) )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   if( !clan )
   {
      send_to_char( "No such clan or nation.\r\n", ch );
      return;
   }

   ch_printf( ch, "\r\n&w%s    : &W%s\r\n&wBadge: &W%s\r\n&wFilename : &W%s\r\n&wMotto    : &W%s\r\n",
      clan->clan_type == CLAN_NATION ? "Nation" : "Clan ",
      clan->name ? clan->name : "(Not Set)",
      clan->badge ? clan->badge : "(Not Set)",
      clan->filename ? clan->filename : "(Not Set)",
      clan->motto ? clan->motto : "(Not Set)" );
   ch_printf( ch, "&wDesc     : &W%s\r\n",
      clan->description ? clan->description : "(Not Set)" );
   ch_printf( ch, "&wLeader   : &W%-19.19s   &wRank: &W%s\r\n",
      clan->leader ? clan->leader : "(Not Set)",
      clan->leadrank ? clan->leadrank : "(Not Set)" );
   ch_printf( ch, "&wNumber1  : &W%-19.19s   &wRank: &W%s\r\n",
      clan->number1 ? clan->number1 : "(Not Set)",
      clan->onerank ? clan->onerank : "(Not Set)" );
   ch_printf( ch, "&wNumber2  : &W%-19.19s   &wRank: &W%s\r\n",
      clan->number2 ? clan->number2 : "(Not Set)",
      clan->tworank ? clan->tworank : "(Not Set)" );
   ch_printf( ch, "&wMembers  : &W%-6d   ", clan->members );
   ch_printf( ch, "&wRace   : &W%d &w(&W%s&w)", clan->race, dis_race_name( clan->race ) );
   send_to_char( "\r\n", ch );
   ch_printf( ch, "&wRecall : &W%-5d    &wStorage: &W%-5d\r\n", clan->recall, clan->storeroom );
   send_to_char( "Members:\r\n", ch );
   if( !clan->first_member )
      send_to_char( "  This clan currently has no members.\r\n", ch );
   for( member = clan->first_member; member; member = member->next )
   {
      ch_printf( ch, "  %15.15s", member->name );
      if( ++cnt == 4 )
      {
         cnt = 0;
         send_to_char( "\r\n", ch );
      }
   }
   if( cnt != 0 )
      send_to_char( "\r\n", ch );
}

CMDF( do_setclan )
{
   char arg1[MIL], arg2[MIL];
   CLAN_DATA *clan;

   set_char_color( AT_PLAIN, ch );
   if( is_npc( ch ) )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   argument = one_argument( argument, arg1 );
   argument = one_argument( argument, arg2 );
   if( !arg1 || arg1[0] == '\0' )
   {
      send_to_char( "Usage: setclan <clan> [create]\r\n", ch );
      send_to_char( "Usage: setclan <clan> <field> <leader|number1|number2> <player>\r\n", ch );
      send_to_char( "\r\nField being one of:\r\n", ch );
      send_to_char( "  leader  leadrank   number1   onerank   tworank\r\n", ch );
      send_to_char( "  recall   storage   number2\r\n", ch );
      if( get_trust( ch ) >= PERM_LEADER )
      {
         send_to_char( "    name  filename     motto      desc      race\r\n", ch );
         send_to_char( "    type     class\r\n", ch );
      }
      return;
   }

   clan = get_clan( arg1 );
   if( !str_cmp( arg2, "create" ) )
   {
      if( clan )
      {
         send_to_char( "There is already a clan with that name.\r\n", ch );
         return;
      }
      CREATE( clan, CLAN_DATA, 1 );
      if( !clan )
      {
         bug( "%s: failed to CREATE clan.", __FUNCTION__ );
         return;
      }
      LINK( clan, first_clan, last_clan, next, prev );
      clan->name = STRALLOC( arg1 );
      clan->filename = NULL;
      clan->motto = NULL;
      clan->first_member = clan->last_member = NULL;
      clan->description = NULL;
      clan->leader = NULL;
      clan->number1 = NULL;
      clan->number2 = NULL;
      clan->leadrank = NULL;
      clan->onerank = NULL;
      clan->tworank = NULL;
      clan->badge = NULL;
      ch_printf( ch, "%s clan created.\r\n", clan->name );
      return;
   }

   if( !clan )
   {
      send_to_char( "No such clan.\r\n", ch );
      return;
   }

   if( !arg2 || arg2[0] == '\0' )
   {
      show_clan( ch, clan );
      return;
   }

   if( !str_cmp( arg2, "leader" ) )
   {
      STRSET( clan->leader, argument );
      send_to_char( "Leader Set.\r\n", ch );
      save_clan( clan );
      return;
   }
   if( !str_cmp( arg2, "number1" ) )
   {
      STRSET( clan->number1, argument );
      send_to_char( "Number1 Set.\r\n", ch );
      save_clan( clan );
      return;
   }
   if( !str_cmp( arg2, "number2" ) )
   {
      STRSET( clan->number2, argument );
      send_to_char( "Number2 Set.\r\n", ch );
      save_clan( clan );
      return;
   }
   if( !str_cmp( arg2, "leadrank" ) )
   {
      STRSET( clan->leadrank, argument );
      send_to_char( "Leadrank Set.\r\n", ch );
      save_clan( clan );
      return;
   }
   if( !str_cmp( arg2, "onerank" ) )
   {
      STRSET( clan->onerank, argument );
      send_to_char( "Onerank Set.\r\n", ch );
      save_clan( clan );
      return;
   }
   if( !str_cmp( arg2, "tworank" ) )
   {
      STRSET( clan->tworank, argument );
      send_to_char( "Tworank Set.\r\n", ch );
      save_clan( clan );
      return;
   }
   if( !str_cmp( arg2, "badge" ) )
   {
      STRSET( clan->badge, argument );
      send_to_char( "Done.\r\n", ch );
      save_clan( clan );
      return;
   }
   if( !str_cmp( arg2, "recall" ) )
   {
      clan->recall = UMAX( 0, atoi( argument ) );
      ch_printf( ch, "Recall set to %d.\r\n", clan->recall );
      save_clan( clan );
      return;
   }
   if( !str_cmp( arg2, "storage" ) )
   {
      clan->storeroom = UMAX( 0, atoi( argument ) );
      ch_printf( ch, "Storage set to %d.\r\n", clan->storeroom );
      save_clan( clan );
      return;
   }
   if( get_trust( ch ) < PERM_LEADER )
   {
      do_setclan( ch, "" );
      return;
   }
   if( !str_cmp( arg2, "type" ) )
   {
      if( !str_cmp( argument, "nation" ) )
         clan->clan_type = CLAN_NATION;
      else if( !str_cmp( argument, "clan" ) )
         clan->clan_type = CLAN_PLAIN;
      else if( is_number( argument ) )
      {
         int value = atoi( argument );

         if( value < CLAN_PLAIN || value > CLAN_NATION )
         {
            send_to_char( "Usage: setclan <clan> type <nation/clan>\r\n", ch );
            return;
         }
         clan->clan_type = value;
      }
      else
      {
         send_to_char( "Usage: setclan <clan> type <nation/clan>\r\n", ch );
         return;
      }
      send_to_char( "Done.\r\n", ch );
      save_clan( clan );
      return;
   }
   if( !str_cmp( arg2, "race" ) )
   {
      int value;

      if( is_number( argument ) )
         value = atoi( argument );
      else
         value = get_pc_race( argument );
      if( value < -1 || value >= MAX_PC_RACE )
      {
         send_to_char( "Invalid race.\r\n", ch );
         return;
      }
      clan->race = value;
      ch_printf( ch, "Race set to %d[%s].\r\n", clan->race, dis_race_name( clan->race ) );
      save_clan( clan );
      return;
   }

   if( !str_cmp( arg2, "name" ) )
   {
      CLAN_DATA *uclan = NULL;

      if( !argument || argument[0] == '\0' )
      {
         send_to_char( "You can't name a clan nothing.\r\n", ch );
         return;
      }
      if( ( uclan = get_clan( argument ) ) )
      {
         send_to_char( "There is already another clan with that name.\r\n", ch );
         return;
      }
      STRSET( clan->name, argument );
      send_to_char( "Name Set.\r\n", ch );
      save_clan( clan );
      return;
   }
   if( !str_cmp( arg2, "filename" ) )
   {
      char filename[256];

      if( !argument || argument[0] == '\0' )
      {
         send_to_char( "You can't set a clans's filename to nothing.\r\n", ch );
         return;
      }
      if( !can_use_path( ch, CLAN_DIR, argument ) )
         return;
      if( clan->filename )
      {
         snprintf( filename, sizeof( filename ), "%s%s", CLAN_DIR, clan->filename );
         if( !remove( filename ) )
            send_to_char( "Old clan file deleted.\r\n", ch );
      }
      STRSET( clan->filename, argument );
      send_to_char( "Filename Set.\r\n", ch );
      save_clan( clan );
      write_clan_list( );
      return;
   }
   if( !str_cmp( arg2, "motto" ) )
   {
      STRSET( clan->motto, argument );
      send_to_char( "Motto Set.\r\n", ch );
      save_clan( clan );
      return;
   }
   if( !str_cmp( arg2, "desc" ) )
   {
      STRSET( clan->description, argument );
      send_to_char( "Desc Set.\r\n", ch );
      save_clan( clan );
      return;
   }
   do_setclan( ch, "" );
}

void free_council_members( COUNCIL_DATA *council )
{
   MEMBER_DATA *member, *member_next;

   if( !council )
      return;
   for( member = council->first_member; member; member = member_next )
   {
      member_next = member->next;
      UNLINK( member, council->first_member, council->last_member, next, prev );
      free_member( member );
   }
}

void remove_council_member( COUNCIL_DATA *council, char *name )
{
   MEMBER_DATA *member;

   if( !council || !name || name[0] == '\0' )
      return;
   for( member = council->first_member; member; member = member->next )
   {
      if( !str_cmp( member->name, name ) )
      {
         if( !str_cmp( name, council->head ) )
            STRFREE( council->head );
         if( !str_cmp( name, council->head2 ) )
            STRFREE( council->head2 );
         UNLINK( member, council->first_member, council->last_member, next, prev );
         free_member( member );
         if( --council->members < 0 )
            council->members = 0;
         return;
      }
   }
}

void add_council_member( COUNCIL_DATA *council, char *name )
{
   MEMBER_DATA *member;

   if( !council || !name || name[0] == '\0' )
      return;
   CREATE( member, MEMBER_DATA, 1 );
   member->name = STRALLOC( name );
   LINK( member, council->first_member, council->last_member, next, prev );
   if( ( council->members + 1 ) > 0 )
      council->members++;
}

void free_one_council( COUNCIL_DATA *council )
{
   if( !council )
      return;
   free_council_members( council );
   UNLINK( council, first_council, last_council, next, prev );
   STRFREE( council->description );
   STRFREE( council->filename );
   STRFREE( council->head );
   STRFREE( council->head2 );
   STRFREE( council->name );
   STRFREE( council->powers );
   DISPOSE( council );
}

void free_councils( void )
{
   COUNCIL_DATA *council, *council_next;

   for( council = first_council; council; council = council_next )
   {
      council_next = council->next;
      free_one_council( council );
   }
}

COUNCIL_DATA *get_council( char *name )
{
   COUNCIL_DATA *council;

   for( council = first_council; council; council = council->next )
      if( council->name && name && !str_cmp( name, council->name ) )
         return council;
   return NULL;
}

void write_council_list( void )
{
   COUNCIL_DATA *tcouncil;
   FILE *fp;

   if( !( fp = fopen( COUNCIL_LIST, "w" ) ) )
   {
      bug( "%s: can't open %s for writing!", __FUNCTION__, COUNCIL_LIST );
      return;
   }
   for( tcouncil = first_council; tcouncil; tcouncil = tcouncil->next )
      fprintf( fp, "%s\n", tcouncil->filename );
   fprintf( fp, "$\n" );
   fclose( fp );
   fp = NULL;
}

/* Save a council's data to its data file */
void save_council( COUNCIL_DATA *council )
{
   FILE *fp;
   MEMBER_DATA *member;
   char filename[256];

   if( !council )
   {
      bug( "%s: NULL council!", __FUNCTION__ );
      return;
   }

   if( !council->name )
   {
      bug( "%s: NULL council->name", __FUNCTION__ );
      return;
   }

   if( !council->filename || council->filename[0] == '\0' )
   {
      bug( "%s: %s has no filename", __FUNCTION__, council->name );
      return;
   }

   snprintf( filename, sizeof( filename ), "%s%s", COUNCIL_DIR, council->filename );
   if( !( fp = fopen( filename, "w" ) ) )
   {
      perror( filename );
      return;
   }
   fprintf( fp, "Name         %s~\n", council->name );
   fprintf( fp, "Filename     %s~\n", council->filename );
   if( council->description )
      fprintf( fp, "Description  %s~\n", council->description );
   if( council->head )
      fprintf( fp, "Head         %s~\n", council->head );
   if( council->head2 )
      fprintf( fp, "Head2        %s~\n", council->head2 );
   if( council->powers )
      fprintf( fp, "Powers       %s~\n", council->powers );
   for( member = council->first_member; member; member = member->next )
      fprintf( fp, "Member         %s~\n", member->name );
   fprintf( fp, "End\n\n" );
   fclose( fp );
   fp = NULL;
}

/* Read in actual council data. */
COUNCIL_DATA *fread_council( FILE *fp )
{
   COUNCIL_DATA *council = NULL;
   const char *word;
   bool fMatch;

   CREATE( council, COUNCIL_DATA, 1 );
   if( !council )
      return NULL;
   council->first_member = NULL;
   council->last_member = NULL;
   for( ;; )
   {
      word = feof( fp ) ? "End" : fread_word( fp );
      fMatch = false;

      switch( UPPER( word[0] ) )
      {
         case '*':
            fMatch = true;
            fread_to_eol( fp );
            break;

         case 'D':
            KEY( "Description", council->description, fread_string( fp ) );
            break;

         case 'E':
            if( !str_cmp( word, "End" ) )
               return council;
            break;

         case 'F':
            KEY( "Filename", council->filename, fread_string( fp ) );
            break;

         case 'H':
            KEY( "Head", council->head, fread_string( fp ) );
            KEY( "Head2", council->head2, fread_string( fp ) );
            break;

         case 'M':
            if( !str_cmp( word, "Member" ) )
            {
               char *name = fread_flagstring( fp );

               if( valid_pfile( name ) )
                  add_council_member( council, name );
               else
                  bug( "%s: not adding member %s because no pfile found.", __FUNCTION__, name );
               fMatch = true;
               break;
            }
            break;

         case 'N':
            KEY( "Name", council->name, fread_string( fp ) );
            break;

         case 'P':
            KEY( "Powers", council->powers, fread_string( fp ) );
            break;
      }

      if( !fMatch )
      {
         bug( "%s: no match: %s", __FUNCTION__, word );
         fread_to_eol( fp );
      }
   }
   free_one_council( council );
   return NULL;
}

/* Load a council file */
void load_council_file( const char *councilfile )
{
   char filename[256];
   COUNCIL_DATA *council;
   FILE *fp;

   snprintf( filename, sizeof( filename ), "%s%s", COUNCIL_DIR, councilfile );
   if( !( fp = fopen( filename, "r" ) ) )
   {
      perror( filename );
      return;
   }
   council = fread_council( fp );
   fclose( fp );
   if( !council )
      return;
   LINK( council, first_council, last_council, next, prev );
}

/* Load in all the council files. */
void load_councils( void )
{
   FILE *fpList;
   const char *filename;

   first_council = last_council = NULL;

   log_string( "Loading councils..." );
   if( !( fpList = fopen( COUNCIL_LIST, "r" ) ) )
   {
      bug( "%s: Can't read file: %s", __FUNCTION__, COUNCIL_LIST );
      perror( COUNCIL_LIST );
      return;
   }

   for( ;; )
   {
      filename = feof( fpList ) ? "$" : fread_word( fpList );
      if( filename[0] == '$' )
         break;
      log_string( filename );
      load_council_file( filename );
   }
   fclose( fpList );
   fpList = NULL;
   log_string( " Done loading councils " );
}

CMDF( do_council_induct )
{
   char arg[MIL], buf[MSL];
   CHAR_DATA *victim;
   COUNCIL_DATA *council;

   if( is_npc( ch ) || !ch->pcdata->council )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   council = ch->pcdata->council;

   if( str_cmp( ch->name, council->head )
   && str_cmp( ch->name, council->head2 )
   && str_cmp( council->name, "mortal council" ) )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   argument = one_argument( argument, arg );
   if( !arg || arg[0] == '\0' )
   {
      send_to_char( "Induct whom into your council?\r\n", ch );
      return;
   }

   if( !( victim = get_char_room( ch, arg ) ) )
   {
      send_to_char( "That player is not here.\r\n", ch );
      return;
   }

   if( is_npc( victim ) )
   {
      send_to_char( "Not on NPC's.\r\n", ch );
      return;
   }

   if( victim->pcdata->council )
   {
      send_to_char( "This player already belongs to a council!\r\n", ch );
      return;
   }

   if( xIS_SET( victim->act, PLR_NOINDUCT ) )
   {
      send_to_char( "This player doesn't currently wish to be inducted into a council.\r\n", ch );
      return;
   }

   victim->pcdata->council = council;
   add_council_member( council, victim->name );
   act( AT_MAGIC, "You induct $N into $t", ch, council->name, victim, TO_CHAR );
   act( AT_MAGIC, "$n inducts $N into $t", ch, council->name, victim, TO_ROOM );
   act( AT_MAGIC, "$n inducts you into $t", ch, council->name, victim, TO_VICT );
   snprintf( buf, sizeof( buf ), "%s has been inducted into %s!", victim->name, council->name );
   echo_to_all( AT_MAGIC, buf, ECHOTAR_ALL );
   save_char_obj( victim );
   save_council( council );
}

CMDF( do_council_outcast )
{
   char arg[MIL], buf[MSL];
   CHAR_DATA *victim;
   COUNCIL_DATA *council;

   if( is_npc( ch ) || !ch->pcdata->council )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   council = ch->pcdata->council;

   if( str_cmp( ch->name, council->head ) && str_cmp( ch->name, council->head2 )
   && str_cmp( council->name, "mortal council" ) )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   argument = one_argument( argument, arg );
   if( !arg || arg[0] == '\0' )
   {
      send_to_char( "Outcast whom from your council?\r\n", ch );
      return;
   }

   if( !( victim = get_char_room( ch, arg ) ) )
   {
      send_to_char( "That player is not here.\r\n", ch );
      return;
   }

   if( is_npc( victim ) )
   {
      send_to_char( "Not on NPC's.\r\n", ch );
      return;
   }

   if( victim == ch )
   {
      send_to_char( "Kick yourself out of your own council?\r\n", ch );
      return;
   }

   if( victim->pcdata->council != ch->pcdata->council )
   {
      send_to_char( "This player does not belong to your council!\r\n", ch );
      return;
   }

   if( !str_cmp( victim->name, ch->pcdata->council->head2 ) )
   {
      STRFREE( ch->pcdata->council->head2 );
      ch->pcdata->council->head2 = NULL;
   }

   remove_council_member( council, victim->name );
   victim->pcdata->council = NULL;
   act( AT_MAGIC, "You outcast $N from $t", ch, council->name, victim, TO_CHAR );
   act( AT_MAGIC, "$n outcasts $N from $t", ch, council->name, victim, TO_ROOM );
   act( AT_MAGIC, "$n outcasts you from $t", ch, council->name, victim, TO_VICT );
   snprintf( buf, sizeof( buf ), "%s has been outcast from %s!", victim->name, council->name );
   echo_to_all( AT_MAGIC, buf, ECHOTAR_ALL );
   save_char_obj( victim );
   save_council( council );
}

void show_council( CHAR_DATA *ch, COUNCIL_DATA *council )
{
   MEMBER_DATA *member;
   int cnt = 0;

   set_char_color( AT_PLAIN, ch );

   if( is_npc( ch ) )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   if( !council )
   {
      send_to_char( "No such council.\r\n", ch );
      return;
   }

   ch_printf( ch, "\r\n&wCouncil :  &W%s\r\n&wFilename:  &W%s\r\n",
      council->name ? council->name : "(Not Set)", council->filename ? council->filename : "(Not Set)" );
   ch_printf( ch, "&wHead:      &W%s\r\n", council->head ? council->head : "(Not Set)" );
   ch_printf( ch, "&wHead2:     &W%s\r\n", council->head2 ? council->head2 : "(Not Set)" );
   ch_printf( ch, "&wPowers:    &W%s\r\n", council->powers ? council->powers : "(Not Set)" );
   ch_printf( ch, "&wDescription:\r\n&W%s\r\n", council->description ? council->description : "(Not Set)" );
   send_to_char( "Members:\r\n", ch );
   if( !council->first_member )
      send_to_char( "  This council has no members.\r\n", ch );
   for( member = council->first_member; member; member = member->next )
   {
      ch_printf( ch, "  %15.15s", member->name );
      if( ++cnt == 4 )
      {
         cnt = 0;
         send_to_char( "\r\n", ch );
      }
   }
   if( cnt != 0 )
      send_to_char( "\r\n", ch );
}

CMDF( do_setcouncil )
{
   char arg1[MIL], arg2[MIL];
   COUNCIL_DATA *council;

   set_char_color( AT_PLAIN, ch );

   if( is_npc( ch ) )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   argument = one_argument( argument, arg1 );
   argument = one_argument( argument, arg2 );
   if( !arg1 || arg1[0] == '\0' )
   {
      send_to_char( "Usage: setcouncil <council> [create]\r\n", ch );
      send_to_char( "Usage: setcouncil <council> <field> <value>\r\n", ch );
      send_to_char( "\r\nField being one of:\r\n", ch );
      send_to_char( "  head  head2  members", ch );
      if( get_trust( ch ) >= PERM_LEADER )
         send_to_char( "  name  filename  desc", ch );
      if( get_trust( ch ) >= PERM_HEAD )
         send_to_char( "  powers", ch );
      send_to_char( "\r\n", ch );
      return;
   }

   council = get_council( arg1 );

   if( !str_cmp( arg2, "create" ) )
   {
      if( council )
      {
         send_to_char( "A council is already using that name.\r\n", ch );
         return;
      }
      CREATE( council, COUNCIL_DATA, 1 );
      if( !council )
      {
         bug( "%s: failed to CREATE council.", __FUNCTION__ );
         return;
      }
      LINK( council, first_council, last_council, next, prev );
      council->first_member = council->last_member = NULL;
      council->name = STRALLOC( arg1 );
      council->filename = NULL;
      council->head = NULL;
      council->head2 = NULL;
      council->powers = NULL;
      ch_printf( ch, "%s council created.\r\n", council->name );
      return;
   }

   if( !council )
   {
      send_to_char( "No such council.\r\n", ch );
      return;
   }

   if( !arg2 || arg2[0] == '\0' )
   {
      show_council( ch, council );
      return;
   }

   if( !str_cmp( arg2, "head" ) )
   {
      STRSET( council->head, argument );
      send_to_char( "Head Set.\r\n", ch );
      save_council( council );
      return;
   }

   if( !str_cmp( arg2, "head2" ) )
   {
      STRSET( council->head2, argument );
      send_to_char( "Head2 Set.\r\n", ch );
      save_council( council );
      return;
   }
   if( !str_cmp( arg2, "members" ) )
   {
      council->members = UMAX( 0, atoi( argument ) );
      send_to_char( "Done.\r\n", ch );
      save_council( council );
      return;
   }
   if( get_trust( ch ) < PERM_LEADER )
   {
      do_setcouncil( ch, "" );
      return;
   }
   if( !str_cmp( arg2, "name" ) )
   {
      COUNCIL_DATA *ucouncil;

      if( !argument || argument[0] == '\0' )
      {
         send_to_char( "Can't set a council name to nothing.\r\n", ch );
         return;
      }
      if( ( ucouncil = get_council( argument ) ) )
      {
         send_to_char( "A council is already using that name.\r\n", ch );
         return;
      }
      STRSET( council->name, argument );
      send_to_char( "Name Set.\r\n", ch );
      save_council( council );
      return;
   }
   if( !str_cmp( arg2, "filename" ) )
   {
      char filename[256];

      if( !argument || argument[0] == '\0' )
      {
         send_to_char( "You can't set a council's filename to nothing.\r\n", ch );
         return;
      }
      if( !can_use_path( ch, COUNCIL_DIR, argument ) )
         return;
      if( council->filename )
      {
         snprintf( filename, sizeof( filename ), "%s%s", COUNCIL_DIR, council->filename );
         if( !remove( filename ) )
            send_to_char( "Old council file deleted.\r\n", ch );
      }
      STRSET( council->filename, argument );
      send_to_char( "Filename Set.\r\n", ch );
      save_council( council );
      write_council_list( );
      return;
   }
   if( !str_cmp( arg2, "desc" ) )
   {
      STRSET( council->description, argument );
      send_to_char( "Desc Set.\r\n", ch );
      save_council( council );
      return;
   }
   if( get_trust( ch ) < PERM_HEAD )
   {
      do_setcouncil( ch, "" );
      return;
   }
   if( !str_cmp( arg2, "powers" ) )
   {
      STRSET( council->powers, argument );
      send_to_char( "Powers Set.\r\n", ch );
      save_council( council );
      return;
   }

   do_setcouncil( ch, "" );
}

void quit_clan( CHAR_DATA *ch, CLAN_DATA *clan )
{
   char buf[MSL];

   if( !ch || is_npc( ch ) || !clan )
      return;
   if( ch->pcdata->clan == clan )
      ch->pcdata->clan = NULL;
   else if( ch->pcdata->nation == clan )
      ch->pcdata->nation = NULL;
   else
   {
      send_to_char( "You don't belong to that clan or nation.\r\n", ch );
      return;
   }
   remove_clan_member( clan, ch->name );
   act( AT_MAGIC, "You quit $t", ch, clan->name, NULL, TO_CHAR );
   act( AT_MAGIC, "$n quit $t", ch, clan->name, NULL, TO_ROOM );
   snprintf( buf, sizeof( buf ), "%s has quit %s!", ch->name, clan->name );
   echo_to_all( AT_MAGIC, buf, ECHOTAR_ALL );
   save_char_obj( ch );
   save_clan( clan );
}

CMDF( do_clan_quit )
{
   if( !ch || is_npc( ch ) )
      return;
   if( !ch->pcdata->clan || ch->pcdata->clan->clan_type != CLAN_PLAIN )
   {
      send_to_char( "You aren't in a clan.\r\n", ch );
      return;
   }   
   if( !str_cmp( argument, "now" ) )
      quit_clan( ch, ch->pcdata->clan );
   else
      send_to_char( "Usage: cquit now\r\n", ch );
}

CMDF( do_nation_quit )
{
   if( !ch || is_npc( ch ) )
      return;
   if( !ch->pcdata->nation || ch->pcdata->nation->clan_type != CLAN_NATION )
   {
      send_to_char( "You aren't in a nation.\r\n", ch );
      return;
   }   
   if( !str_cmp( argument, "now" ) )
      quit_clan( ch, ch->pcdata->nation );
   else
      send_to_char( "Usage: nquit now\r\n", ch );
}

void show_clans( CHAR_DATA *ch, CLAN_DATA *clan, int type )
{
   char *disname = "Clan", *argname = "clans", *oargname = "clan";
   int count = 0, color1 = AT_BLOOD, color2 = AT_GREY;
   MEMBER_DATA *member;

   if( !ch )
      return;
   if( type == CLAN_NATION )
   {
      color1 = AT_YELLOW;
      color2 = AT_GREEN;
      disname = "Nation";
      oargname = "nation";
      argname = "nations";
   }
   if( !clan )
   {
      set_char_color( color1, ch );
      ch_printf( ch, "\r\n%-13s %-13s %-7s\r\n", disname, "Leader", "Members" );
      send_to_char( "________________________________________________\r\n\r\n", ch );
      set_char_color( color2, ch );
      for( clan = first_clan; clan; clan = clan->next )
      {
         if( clan->clan_type != type )
            continue;
         ch_printf( ch, "%-13s %-13s %7d\r\n",
            clan->name ? clan->name : "(Not Set)",
            clan->leader ? clan->leader : "(Not Set)", clan->members );
         count++;
      }
      if( !count )
         ch_printf( ch, "There are no %s currently formed.\r\n", argname );
      set_char_color( color1, ch );
      send_to_char( "________________________________________________\r\n\r\n", ch );
      ch_printf( ch, "Use '%s <%s>' for detailed information.\r\n", argname, oargname );
      return;
   }
   set_char_color( color1, ch );
   ch_printf( ch, "\r\n%s, '%s'\r\n\r\n", clan->name ? clan->name : "(Not Set)", clan->motto ? clan->motto : "(Not Set)" );
   set_char_color( color2, ch );
   ch_printf( ch, "Leader:  %s\r\nNumber One :  %s\r\nNumber Two :  %s\r\n",
      clan->leader ? clan->leader : "(Not Set)",
      clan->number1 ? clan->number1 : "(Not Set)",
      clan->number2 ? clan->number2 : "(Not Set)" );
   ch_printf( ch, "Members    :  %d\r\n", clan->members );
   set_char_color( color1, ch );
   ch_printf( ch, "\r\nDescription:  %s\r\n", clan->description ? clan->description : "(Not Set)" );
   send_to_char( "Members:\r\n", ch );
   set_char_color( color2, ch );
   for( member = clan->first_member; member; member = member->next )
   {
      ch_printf( ch, "  %15.15s", member->name );
      if( ++count == 4 )
      {
         count = 0;
         send_to_char( "\r\n", ch );
      }
   }
   if( count != 0 )
      send_to_char( "\r\n", ch );
}

/* Added multiple level pkill and pdeath support. --Shaddai */
CMDF( do_clans )
{
   CLAN_DATA *clan;

   if( !argument || argument[0] == '\0' )
   {
      show_clans( ch, NULL, CLAN_PLAIN );
      return;
   }

   if( !( clan = get_clan( argument ) ) || clan->clan_type != CLAN_PLAIN )
   {
      set_char_color( AT_BLOOD, ch );
      ch_printf( ch, "No clan called %s.\r\n", argument );
      return;
   }
   show_clans( ch, clan, CLAN_PLAIN );
}

CMDF( do_nations )
{
   CLAN_DATA *nation;

   if( !argument || argument[0] == '\0' )
   {
      show_clans( ch, NULL, CLAN_NATION );
      return;
   }

   if( !( nation = get_clan( argument ) ) || nation->clan_type != CLAN_NATION )
   {
      set_char_color( AT_YELLOW, ch );
      ch_printf( ch, "No nation called %s.\r\n", argument );
      return;
   }
   show_clans( ch, nation, CLAN_NATION );
}

CMDF( do_councils )
{
   COUNCIL_DATA *council;
   int count = 0;

   set_char_color( AT_CYAN, ch );
   if( !first_council )
   {
      send_to_char( "There are no councils currently formed.\r\n", ch );
      return;
   }
   if( !argument || argument[0] == '\0' )
   {
      set_char_color( AT_CYAN, ch );
      send_to_char( "\r\nTitle                    Head\r\n", ch );
      send_to_char( "__________________________________________________\r\n\r\n", ch );
      set_char_color( AT_GREY, ch );
      for( council = first_council; council; council = council->next )
      {
         ch_printf( ch, "&w%-24s", council->name ? council->name : "(Not Set)" );
         ch_printf( ch, " %s", council->head ? council->head : "(Not Set)" );
         if( council->head2 )
            ch_printf( ch, " and %s", council->head2 );
         send_to_char( "\r\n", ch );
         count++;
      }
      if( !count )
         send_to_char( "There are no councils currently formed.\r\n", ch );
      set_char_color( AT_CYAN, ch );
      send_to_char( "__________________________________________________\r\n\r\n", ch );
      send_to_char( "Use 'councils <council>' for detailed information.\r\n", ch );
      return;
   }
   if( !( council = get_council( argument ) ) )
   {
      ch_printf( ch, "&cNo council called %s exists...\r\n", argument );
      return;
   }
   ch_printf( ch, "&c\r\n%s\r\n", council->name ? council->name : "(Not Set)" );
   ch_printf( ch, "&cHead:    &w%s\r\n", council->head ? council->head : "(Not Set)" );
   if( council->head2 )
      ch_printf( ch, "&cCo-Head: &w%s\r\n", council->head2 );

   ch_printf( ch, "&cMembers:  &w%d\r\n", council->members );
   ch_printf( ch, "&cDescription:\r\n&w%s\r\n", council->description ? council->description : "(Not Set)" );
}

CMDF( do_victories )
{
   char filename[256], arg[MSL];
   CLAN_DATA *clan;
   bool clear = false;

   if( !ch || is_npc( ch ) )
      return;

   argument = one_argument( argument, arg );
   if( arg && arg[0] != '\0' )
   {
      if( ( clan = get_clan( arg ) ) )
         argument = one_argument( argument, arg );
   }
   else
      clan = ch->pcdata->clan;

   if( is_immortal( ch ) && arg && arg[0] != '\0' && !str_cmp( arg, "clear" ) )
      clear = true;

   if( !clan )
   {
      send_to_char( "No clan to display victories for.\r\n", ch );
      return;
   }

   if( clan->clan_type != CLAN_PLAIN )
   {
      send_to_char( "The specified clan isn't able to legaly pkill others.\r\n", ch );
      return;
   }

   snprintf( filename, sizeof( filename ), "%s%s.record", CLAN_DIR, clan->name );
   set_pager_color( AT_PURPLE, ch );
   if( clear )
   {
      remove_file( filename );
      ch_printf( ch, "%s victories ledger has been cleared.\r\n", clan->name );
   }
   else
   {
      send_to_pager( "\r\nLVL  Character       LVL  Character\r\n", ch );
      show_file( ch, filename );
   }
}

CMDF( do_shove )
{
   char arg[MIL], arg2[MIL];
   int exit_dir, schance = 0;
   EXIT_DATA *pexit;
   CHAR_DATA *victim;
   ROOM_INDEX_DATA *to_room;
   bool nogo;

   argument = one_argument( argument, arg );
   argument = one_argument( argument, arg2 );

   if( is_npc( ch ) || !xIS_SET( ch->pcdata->flags, PCFLAG_DEADLY ) )
   {
      send_to_char( "Only deadly characters can shove.\r\n", ch );
      return;
   }

   if( get_timer( ch, TIMER_PKILLED ) > 0 )
   {
      send_to_char( "You can't shove a player right now.\r\n", ch );
      return;
   }

   if( !arg || arg[0] == '\0' )
   {
      send_to_char( "Shove whom?\r\n", ch );
      return;
   }

   if( !( victim = get_char_room( ch, arg ) ) )
   {
      send_to_char( "They aren't here.\r\n", ch );
      return;
   }

   if( victim == ch )
   {
      send_to_char( "You shove yourself around, to no avail.\r\n", ch );
      return;
   }
   if( is_npc( victim ) || !xIS_SET( victim->pcdata->flags, PCFLAG_DEADLY ) )
   {
      send_to_char( "You can only shove deadly characters.\r\n", ch );
      return;
   }

   if( ch->level - victim->level > 5 || victim->level - ch->level > 5 )
   {
      send_to_char( "There is too great an experience difference for you to even bother.\r\n", ch );
      return;
   }

   if( get_timer( victim, TIMER_PKILLED ) > 0 )
   {
      send_to_char( "You can't shove that player right now.\r\n", ch );
      return;
   }

   if( victim->position != POS_STANDING )
   {
      act( AT_PLAIN, "$N isn't standing up.", ch, NULL, victim, TO_CHAR );
      return;
   }

   if( !arg2 || arg2[0] == '\0' )
   {
      send_to_char( "Shove them in which direction?\r\n", ch );
      return;
   }

   exit_dir = get_dir( arg2 );
   if( xIS_SET( victim->in_room->room_flags, ROOM_SAFE ) && get_timer( victim, TIMER_SHOVEDRAG ) <= 0 )
   {
      send_to_char( "That character can't be shoved right now.\r\n", ch );
      return;
   }
   victim->position = POS_SHOVE;
   nogo = false;
   if( !( pexit = get_exit( ch->in_room, exit_dir ) ) )
      nogo = true;
   else if( xIS_SET( pexit->exit_info, EX_CLOSED )
   && ( !IS_AFFECTED( victim, AFF_PASS_DOOR )
   || xIS_SET( pexit->exit_info, EX_NOPASSDOOR ) ) )
      nogo = true;
   if( nogo )
   {
      send_to_char( "There's no exit in that direction.\r\n", ch );
      victim->position = POS_STANDING;
      return;
   }
   to_room = pexit->to_room;
   if( xIS_SET( to_room->room_flags, ROOM_DEATH ) )
   {
      send_to_char( "You can't shove someone into a death trap.\r\n", ch );
      victim->position = POS_STANDING;
      return;
   }

   if( ch->in_room->area != to_room->area && !in_hard_range( victim, to_room->area ) )
   {
      send_to_char( "That character can't enter that area.\r\n", ch );
      victim->position = POS_STANDING;
      return;
   }

   /* Add 3 points to chance for every str point above 15, subtract for below 15 */
   schance += ( ( get_curr_str( ch ) - 15 ) * 3 );

   schance += ( ch->level - victim->level );

   if( schance < number_percent( ) )
   {
      send_to_char( "You failed.\r\n", ch );
      victim->position = POS_STANDING;
      return;
   }
   act( AT_ACTION, "You shove $M.", ch, NULL, victim, TO_CHAR );
   act( AT_ACTION, "$n shoves you.", ch, NULL, victim, TO_VICT );
   move_char( victim, get_exit( ch->in_room, exit_dir ), 0 );
   if( !char_died( victim ) )
      victim->position = POS_STANDING;
   wait_state( ch, 12 );
   /* Remove protection from shove/drag if char shoves -- Blodkai */
   if( xIS_SET( ch->in_room->room_flags, ROOM_SAFE ) && get_timer( ch, TIMER_SHOVEDRAG ) <= 0 )
      add_timer( ch, TIMER_SHOVEDRAG, 10, NULL, 0 );
}

CMDF( do_drag )
{
   char arg[MIL], arg2[MIL];
   int exit_dir, schance = 0;
   CHAR_DATA *victim;
   EXIT_DATA *pexit;
   ROOM_INDEX_DATA *to_room;

   argument = one_argument( argument, arg );
   argument = one_argument( argument, arg2 );

   if( is_npc( ch ) )
   {
      send_to_char( "Only characters can drag.\r\n", ch );
      return;
   }

   if( get_timer( ch, TIMER_PKILLED ) > 0 )
   {
      send_to_char( "You can't drag a player right now.\r\n", ch );
      return;
   }

   if( !arg || arg[0] == '\0' )
   {
      send_to_char( "Drag whom?\r\n", ch );
      return;
   }

   if( !( victim = get_char_room( ch, arg ) ) )
   {
      send_to_char( "They aren't here.\r\n", ch );
      return;
   }

   if( victim == ch )
   {
      send_to_char( "You take yourself by the scruff of your neck, but go nowhere.\r\n", ch );
      return;
   }

   if( is_npc( victim ) )
   {
      send_to_char( "You can only drag characters.\r\n", ch );
      return;
   }

   if( !xIS_SET( victim->act, PLR_SHOVEDRAG ) && !xIS_SET( victim->pcdata->flags, PCFLAG_DEADLY ) )
   {
      send_to_char( "That character doesn't seem to appreciate your attentions.\r\n", ch );
      return;
   }

   if( get_timer( victim, TIMER_PKILLED ) > 0 )
   {
      send_to_char( "You can't drag that player right now.\r\n", ch );
      return;
   }

   if( victim->fighting )
   {
      send_to_char( "You try, but can't get close enough.\r\n", ch );
      return;
   }

   if( !xIS_SET( ch->pcdata->flags, PCFLAG_DEADLY ) && xIS_SET( victim->pcdata->flags, PCFLAG_DEADLY ) )
   {
      send_to_char( "You can't drag a deadly character.\r\n", ch );
      return;
   }

   if( !xIS_SET( victim->pcdata->flags, PCFLAG_DEADLY ) && victim->position > 3 )
   {
      send_to_char( "They don't seem to need your assistance.\r\n", ch );
      return;
   }

   if( !arg2 || arg2[0] == '\0' )
   {
      send_to_char( "Drag them in which direction?\r\n", ch );
      return;
   }

   if( ch->level - victim->level > 5 || victim->level - ch->level > 5 )
   {
      if( xIS_SET( victim->pcdata->flags, PCFLAG_DEADLY ) && xIS_SET( ch->pcdata->flags, PCFLAG_DEADLY ) )
      {
         send_to_char( "There is too great an experience difference for you to even bother.\r\n", ch );
         return;
      }
   }

   if( xIS_SET( victim->in_room->room_flags, ROOM_SAFE ) && get_timer( victim, TIMER_SHOVEDRAG ) <= 0 )
   {
      send_to_char( "That character can't be dragged right now.\r\n", ch );
      return;
   }

   exit_dir = get_dir( arg2 );

   if( !( pexit = get_exit( ch->in_room, exit_dir ) ) || xIS_SET( pexit->exit_info, EX_HIDDEN ) )
   {
      send_to_char( "There's no exit in that direction.\r\n", ch );
      return;
   }

   if( xIS_SET( pexit->exit_info, EX_CLOSED )
   && ( !IS_AFFECTED( victim, AFF_PASS_DOOR ) || xIS_SET( pexit->exit_info, EX_NOPASSDOOR ) ) )
   {
      send_to_char( "That exit is closed.\r\n", ch );
      return;
   }

   to_room = pexit->to_room;
   if( xIS_SET( to_room->room_flags, ROOM_DEATH ) )
   {
      send_to_char( "You can't drag someone into a death trap.\r\n", ch );
      return;
   }

   if( ch->in_room->area != to_room->area && !in_hard_range( victim, to_room->area ) )
   {
      send_to_char( "That character can't enter that area.\r\n", ch );
      return;
   }

   /* Add 3 points to chance for every str point above 15, subtract for below 15 */
   schance += ( ( get_curr_str( ch ) - 15 ) * 3 );
   schance += ( ch->level - victim->level );

   if( schance < number_percent( ) )
   {
      send_to_char( "You failed.\r\n", ch );
      victim->position = POS_STANDING;
      return;
   }
   if( victim->position < POS_STANDING )
   {
      short temp;

      temp = victim->position;
      victim->position = POS_DRAG;
      act( AT_ACTION, "You drag $M into the next room.", ch, NULL, victim, TO_CHAR );
      act( AT_ACTION, "$n grabs your hair and drags you.", ch, NULL, victim, TO_VICT );
      move_char( victim, get_exit( ch->in_room, exit_dir ), 0 );
      if( !char_died( victim ) )
         victim->position = temp;
      move_char( ch, get_exit( ch->in_room, exit_dir ), 0 );
      wait_state( ch, 12 );
      return;
   }
   send_to_char( "You can't do that to someone who is standing.\r\n", ch );
}

/* Save items in a clan storage room - Scryn & Thoric */
void save_clan_storeroom( CHAR_DATA *ch, CLAN_DATA *clan )
{
   FILE *fp;
   char filename[256];
   short templvl;

   if( !clan )
   {
      bug( "%s: Null clan pointer!", __FUNCTION__ );
      return;
   }

   if( !ch )
   {
      bug( "%s: Null ch pointer!", __FUNCTION__ );
      return;
   }

   snprintf( filename, sizeof( filename ), "%s%s.vault", CLAN_DIR, clan->filename );
   if( !ch->in_room->last_content )
   {
      remove_file( filename );
      return;
   }
   if( !( fp = fopen( filename, "w" ) ) )
   {
      bug( "%s: cant open %s", __FUNCTION__, filename );
      perror( filename );
      return;
   }
   templvl = ch->level;
   ch->level = MAX_LEVEL;
   fwrite_obj( ch, ch->in_room->last_content, fp, 0, OS_CARRY, false );
   fprintf( fp, "#END\n" );
   ch->level = templvl;
   fclose( fp );
   fp = NULL;
}