contest/
contest/Merc21/
contest/Merc21/log/
contest/Merc21/player/
contest/doc/
contest/example/Merc21/
contest/example/Merc21/log/
contest/example/Merc21/player/
contest/example/doc/
contest/example/standalone/
contest/standalone/
******************************************************************************
File Name : installation.txt
Date      : 27th October 2005
Author    : Richard Woolcock
******************************************************************************

The following changes are required in order to install the snippet into a 
stock copy of Merc2.1.  As many other codebases are also derived from Merc, 
it shouldn't take much effort to apply the same instructions to them - ROM, 
Smaug, GodWars, Envy, etc.

******************************************************************************
Changes to be made to Merc.h
******************************************************************************

At the top of the file, right under the copyright notice, add:

    #include "deck.h"
    #include "hand.h"

In the pc_data struct, after this:

    char *              bamfin;
    char *              bamfout;
    char *              title;

Add:

    deck_t *            deck;
    hand_t *            hand;

If you later add support for mobs playing card games, you'll probably want to 
move the above into char_data, but for now it's assumed that only players can 
play card games.

In the "Command functions" section, add the following:

    DECLARE_DO_FUN( do_discard      );
    DECLARE_DO_FUN( do_draw         );
    DECLARE_DO_FUN( do_fold         );
    DECLARE_DO_FUN( do_hand         );
    DECLARE_DO_FUN( do_play         );
    DECLARE_DO_FUN( do_shuffle      );

You'll probably want to insert them in alphabetical order, for readability.

******************************************************************************
Changes to be made to interp.c
******************************************************************************

Add the following to the end of the command table (cmd_table):

    /*
     * Card game commands.
     */
    { "discard",        do_discard,     POS_RESTING,     0,  LOG_NORMAL },
    { "draw",           do_draw,        POS_RESTING,     0,  LOG_NORMAL },
    { "fold",           do_fold,        POS_RESTING,     0,  LOG_NORMAL },
    { "hand",           do_hand,        POS_RESTING,     0,  LOG_NORMAL },
    { "play",           do_play,        POS_RESTING,     0,  LOG_NORMAL },
    { "shuffle",        do_shuffle,     POS_RESTING,     0,  LOG_NORMAL },

******************************************************************************
Changes to be made to save.c
******************************************************************************

In the load_char_obj() function, after this:

    ch->pcdata->bamfin                  = str_dup( "" );
    ch->pcdata->bamfout                 = str_dup( "" );
    ch->pcdata->title                   = str_dup( "" );

Add:

    ch->pcdata->deck                    = NULL;
    ch->pcdata->hand                    = NULL;

This ensures that the deck and hand are cleared upon initialisation.

******************************************************************************
Changes to be made to handler.c
******************************************************************************

At the top, after the following headers:

    #include <ctype.h>
    #include <stdio.h>

Add one more header file:

    #include <stdlib.h>

It's needed for the call to free(), which comes next.

In the extract_char() function, after this:

    if ( IS_NPC(ch) )
        --ch->pIndexData->count;

Add this:

    else // They're a PC.
    {
        if ( ch->pcdata->deck != NULL )
        {
            /* Destroy the deck if nobody else is using it */
            if ( --ch->pcdata->deck->Instances <= 0 )
                free( ch->pcdata->deck );
        }

        /* Free and clear their hand */
        free( ch->pcdata->hand );
    }

This will destroy their deck if nobody else is using it, and will also 
destroy their hand.

******************************************************************************
Help files to add to help.are
******************************************************************************

0 DISCARD~
Syntax: discard <card>

This command allows you to discard a card from your hand.  You must specify the
card by using its value - for example 'discard C2' to discard the Two of Clubs.

See also: draw fold hand play shuffle
~

0 DRAW~
Syntax: draw

This command allows you to draw the top card from the deck.  The card you have
drawn will also be revealed to you, although nobody else will see it.

See also: discard fold hand play shuffle
~

0 FOLD~
Syntax: fold

This command causes you to fold, quitting from whichever card game you are 
currently playing.  This will also reveal your hand.

See also: discard draw hand play shuffle
~

0 HAND~
Syntax: hand
Syntax: hand reveal

This command allows you to look at your hand.  If you type 'hand reveal', it 
will also reveal your hand to everyone else in the room.

See also: discard draw fold play shuffle
~

0 PLAY~
Syntax: play <person>

This command allows you to begin a game of cards.  You may choose to join an 
existing game, or start a new one - either with yourself or with someone else.

See also: discard draw fold hand shuffle
~

0 SHUFFLE~
Syntax: shuffle

This command allows you to shuffle the deck you're currently playing with.

See also: discard draw fold hand play
~

******************************************************************************
Create a new file called card_commands.c
******************************************************************************

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "deck.h"
#include "hand.h"
#include "merc.h"

void do_discard( CHAR_DATA *ch, char *argument )
{
    char buf[MAX_STRING_LENGTH];
    card_t card;
    suit_t suit;
    rank_t rank;

    if ( ch->pcdata->deck == NULL || ch->pcdata->hand == NULL )
    {
	send_to_char( "You need to start playing before you can discard a card.\n\r", ch );
	return;
    }

    if ( argument[0] == '\0' || strlen(argument) != 2 )
    {
	send_to_char( "Please specify which card do you wish to discard (eg 'discard H5').\n\r", ch );
	return;
    }

    /* Find the suit and rank of the card they're trying to discard */
    suit = CardSuit( UPPER(argument[0]) );
    rank = CardRank( UPPER(argument[1]) );

    if ( suit == eSuitMax || rank == eRankMax )
    {
	sprintf( buf, "There is no such card as '%s'.\n\r", argument );
	send_to_char( buf, ch );
	return;
    }

    /* Find the card type, based on its suit and rank */
    card = CardType( suit, rank );

    /* Try to remove the card from their hand */
    if ( HandRemoveCard(ch->pcdata->hand, card) == eFalse )
    {
	sprintf( buf, "You're not even holding the %s (%s)!\n\r", 
	   CardName(card), CardValue(card) );
	send_to_char( buf, ch );
	return;
    }

    /* Try to put the card back onto the bottom of the deck */
    if ( DeckReturnCard(ch->pcdata->deck, card) == eFalse )
    {
	sprintf( buf, "do_discard: Tried to return '%s' to deck, but it was already there!", CardValue(card) );
	bug( buf, 0 );
    }

    sprintf( buf, "You discard the %s (%s) from your hand.\n\r", 
	CardName(card), CardValue(card) );
    send_to_char( buf, ch );
    act( "$n discards a card from $s hand.", ch, NULL, NULL, TO_ROOM );
}

void do_draw( CHAR_DATA *ch, char *argument )
{
    if ( ch->pcdata->deck == NULL || ch->pcdata->hand == NULL )
    {
	send_to_char( "You need to start playing before you can draw a card.\n\r", ch );
    }
    else /* They're playing, so draw a card */
    {
	char buf[MAX_STRING_LENGTH];

	/* Draw a card from the deck and put it in their hand */
	card_t card = DeckDrawCard(ch->pcdata->deck);

	if ( card == eCardMax )
        {
	    send_to_char( "The deck is empty!\n\r", ch );
        }
        else if ( HandAddCard(ch->pcdata->hand, card) == eTrue )
        {
	    sprintf( buf, "You draw a card from the deck - it's the %s (%s).\n\r", 
		CardName(card), CardValue(card) );
	    send_to_char( buf, ch );
	    act( "$n draws a card from the deck.", ch, NULL, NULL, TO_ROOM );
	}
	else /* They were unable to add the card to their hand */
	{
	    bug( "do_draw: Duplicate card found in hand.", 0 );
	    send_to_char( "You were unable to draw the card.\n\r", ch );
        }
    }
}

void do_fold( CHAR_DATA *ch, char *argument )
{
    if ( ch->pcdata->deck == NULL || ch->pcdata->hand == NULL )
    {
	send_to_char( "You need to be playing before you can fold.\n\r", ch );
    }
    else /* Show the hand (if any) and then discard it */
    {
	if ( HandCountCards(ch->pcdata->hand) == 0 )
	{
	    send_to_char( "You fold.\n\r", ch );
	    act( "$n folds ($s hand is already empty).", ch, NULL, NULL, TO_ROOM );
	}
	else /* Show their hand, then put the cards back into the deck */
	{
	    char buf[MAX_STRING_LENGTH];
	    int i; /* Loop counter */
            const char *pCards = HandShow(ch->pcdata->hand, eFalse);
	    int Max = strlen(pCards);

	    sprintf( buf, "You fold, throwing down your hand: %s\n\r", pCards );
	    send_to_char( buf, ch );
	    sprintf( buf, "Placing $s cards down, $n folds, revealing: %s", pCards );
	    act( buf, ch, NULL, NULL, TO_ROOM );

	    for ( i = 0; i < Max; i += 3 )
            {
		/* Put all the cards in their hand back into the deck */
		card_t card = CardType(CardSuit(pCards[i]), CardRank(pCards[i+1]));
		if ( card != eCardMax )
                {
		    /* Make sure the card isn't already in the deck */
		    if ( DeckReturnCard(ch->pcdata->deck, card) == eFalse )
		    {
			sprintf( buf, "do_discard: Tried to return '%s' to deck, but it was already there!\n\r", 
			   CardValue(card) );
			bug( buf, 0 );
		    }
		}
            }
	}

	/* Destroy the hand (no need to remove the cards beforehand) */
	free(ch->pcdata->hand);
        ch->pcdata->hand = NULL;

	/* If they're the last person using that deck, destroy it */
        if ( --ch->pcdata->deck->Instances == 0 )
           free(ch->pcdata->deck);
        ch->pcdata->deck = NULL;
    }
}

void do_hand( CHAR_DATA *ch, char *argument )
{
    if ( ch->pcdata->deck == NULL || ch->pcdata->hand == NULL )
    {
	send_to_char( "You need to start playing in order to get a hand of cards.\n\r", ch );
    }
    else if ( HandCountCards(ch->pcdata->hand) == 0 )
    {
	send_to_char( "Your hand is currently empty - you need to draw a card.\n\r", ch );
    }
    else if ( !strcmp(argument, "reveal") )
    {
	char buf[MAX_STRING_LENGTH];

	sprintf( buf, "You reveal your hand: %s\n\r", 
	    HandShow(ch->pcdata->hand, eFalse) );
	send_to_char( buf, ch );

	sprintf( buf, "With a flourish, $n reveals $s hand: %s", 
	    HandShow(ch->pcdata->hand, eFalse) );
	act( buf, ch, NULL, NULL, TO_ROOM );
    }
    else /* Just show the hand to its owner */
    {
	char buf[MAX_STRING_LENGTH];

	sprintf( buf, "You glance at your hand: %s\n\r", 
	    HandShow(ch->pcdata->hand, eFalse) );
	send_to_char( buf, ch );
    }
}

void do_play( CHAR_DATA *ch, char *argument )
{
    CHAR_DATA *victim;

    if ( argument[0] == '\0' )
    {
	send_to_char( "Who do you wish to play with?\n\r", ch );
	return;
    }

    /* Make sure the target exists, and is in the same room as ch */
    if ( ( victim = get_char_world( ch, argument ) ) == NULL
    || ( victim->in_room != ch->in_room ) )
    {
	send_to_char( "They aren't here.\n\r", ch );
	return;
    }

    /* Make sure that neither ch or victim are NPCs */
    if ( IS_NPC(ch) || IS_NPC(victim) )
    {
	send_to_char( "Only players can play cards.\n\r", ch );
	return;
    }

    /* Make sure the character is not already playing a game */
    if ( ch->pcdata->deck != NULL )
    {
	send_to_char( "You're already playing a game.\n\r", ch );
	return;
    }

    if ( ch == victim )
    {
	/* Create a new deck of cards for solo play */
	ch->pcdata->deck = DeckCreate();
	ch->pcdata->deck->Instances = 1;

	/* Shuffle the deck */
	DeckShuffle(ch->pcdata->deck);

	/* Create an empty hand for the character */
	ch->pcdata->hand = HandCreate();

	send_to_char( "You begin a game of cards.\n\r", ch );
	act( "$n begins a game of cards.", ch, NULL, NULL, TO_ROOM );
    }
    else if ( victim->pcdata->deck == NULL )
    {
	/* Create a new deck of cards, used by both characters */
	victim->pcdata->deck = DeckCreate();
	ch->pcdata->deck = victim->pcdata->deck;
	ch->pcdata->deck->Instances = 2;

	/* Shuffle the deck */
	DeckShuffle(ch->pcdata->deck);

	/* Create an empty hand for both characters */
	victim->pcdata->hand = HandCreate();
	ch->pcdata->hand = HandCreate();

	act( "You begin a game of cards with $N.", ch, NULL, victim, TO_CHAR );
	act( "$n begins a game of cards with you.", ch, NULL, victim, TO_VICT );
	act( "$n begins a game of cards with $N.", ch, NULL, victim, TO_NOTVICT );
    }
    else /* The other person is already playing, so join their game */
    {
	/* Set ch to use the same deck as victim */
	ch->pcdata->deck = victim->pcdata->deck;
	ch->pcdata->deck->Instances++;

	/* Create an empty hand for ch */
	ch->pcdata->hand = HandCreate();

	act( "You join $N's game of cards.", ch, NULL, victim, TO_CHAR );
	act( "$n joins your game of cards.", ch, NULL, victim, TO_VICT );
	act( "$n joins $N's game of cards.", ch, NULL, victim, TO_NOTVICT );
    }
}

void do_shuffle( CHAR_DATA *ch, char *argument )
{
    if ( ch->pcdata->deck == NULL )
    {
	send_to_char( "You need to start playing before you can shuffle the deck.\n\r", ch );
    }
    else if ( DeckCountCards(ch->pcdata->deck) == 0 )
    {
	send_to_char( "You can't shuffle the deck, there's no cards left in it!.\n\r", ch );
    }
    else /* Shuffle the deck */
    {
	DeckShuffle(ch->pcdata->deck);

	send_to_char( "You shuffle the deck.\n\r", ch );
	act( "$n shuffles the deck.", ch, NULL, NULL, TO_ROOM );
    }
}

******************************************************************************
End of installation.txt
******************************************************************************