
Xcard - Card game framework
---------------------------

INTRODUCTION

Xcard is a framework for Xt card games. It revolves around
a special widget called a 'Stack', which holds a stack of cards.
Cards cannot be drawn on their own: anywhere a card needs to
be displayed, a stack widget must be created to contain it.

Stack widgets provide only a handful of methods for adding and
removing cards. Each card is identified by a short integer, which
is composed of bits to indicate suit and face value, and a
bit to indicate whether the card is face up or face down.

Stacks can be configured to display their cards in a spread
fashion (with gravities of 'South', 'East', 'West', 'North') or 
only to show the top card ('Center' gravity).

Stack widgets automatically provide graphical drag and drop. Callback
procedures on the stacks are invoked to determine if
the stack (or a substack) can be picked up ('split') with the 
cursor, or if a stack should accept a 'drop' of new cards on it.

A collection of card artwork pixmaps is supplied.

RESOURCES

Stack widgets have various resources, other than those of Core:

  foreground	- colour used for drawing bitmaps
  dropCallback	- query if a particular drop is allowed
  splitCallback	- query if a stack split or pick-up is allowed
  dropNotify 	- called when a stack drop actually occurs
  hitNotify  	- called on a hit() action (normally a single click)
  compact	- indicates the widget should keep a minimal size
  highlight	- the border highlight colour
  highlightWidth
  highlightHeight - size of the highlight rectangle reserved around the stack
  gravity	- the spread gravity: indicates orientation of 'bottom' card.
  vStepMin
  vStepMax
  hStepMin
  hStepMax	- the card overlap dimensions (only with non-Center gravity)
  lockResize	- stop the stack from stretching/shrinking (used during D&D)

  placeholderPixmap	- image drawn when the stack is empty (or mising pixmap)
  backPixmap		- image drawn for a card when it is 'face down'
  aceDiamondsPixmap   	- 'face up' card artwork for the Ace of Diamonds
  twoDiamondsPixmap   	- 'face up' card artwork for the Two of Diamonds
  ...etc		- ...etc

NOTE: All card pixmaps should be the same dimensions. (They need not be, but
it will look messy if they're not.)

Some built-in callback procedures for dropCallback are supplied:
	StackAlwaysDrop	- always allow cards to drop on this stack
	StackNeverDrop	- always deny a drop

Some built-in callback procedures for splitCallback are supplied:
	StackAlwaysSplit - allow any split and drag of cards on this stack
	StackNeverSplit	 - always deny a stack split/pickup

PROGRAMMING API

Xcard does not actually implement any game logic. Instead, it
just provides a widget class for a normal X application to use for
displaying a stack of cards, and maintaining a stack's state. I feel
this way gives the programmer much more freedom for table and control
layouts. It does mean that the game logic must be written in an 
event/callback fashion though.

The Stack widget class is a subtype of Composite, but you should NOT
manually insert anything into a Stacks (with XtCreateWidget for example).
Use StackPush(), StackPop() and StackMove() instead. These functions
create and release managed CardObject children that you should never need
to know about.

STACK UTILITIES

The public functions used to manipulate and use Stack widgets are:

	Widget StackCreateDragAndDropPopup(Widget toplevel, String name)

		Creates an unrealized popup widget to be used
		when performing drag-and-drop. This must be called
		once by the application at an early stage, and passed
		the toplevel shell widget. During drag-and drop,
		the toplevel widget and its descendents are searched for
		stacks that will accept a drop.

		'name' is the name to be given to the popup shell widget.

		(You don't need to call this if you don't want drag-and-drop.)
		Also, you can discard the return result if you wish, since
		the popup is automatically popped-up when needed.

	Cardinal StackDepth(Widget stack)

		Returns the number of cards on the stack.
		A return value of zero means the stack is empty.

	CardID StackPeek(Widget stack, Cardinal depth)

                Returns the card value at the given depth, where
                the top card's depth is zero.  If depth exceeds the
                number of cards in the stack, then CARD_NONE is
                returned.

	CardID StackTop(Widget stack)

		Equivalent to StackPeek(stack, 0).

	void StackPush(Widget stack, CardID card)

		Places the card on top of the stack.
		Pushing CARD_NONE has no effect.
		(Cards of the same value can exist more than once
		in a stack.)
		Remember to bitwise-or CARD_DOWN with the card ID if
		you want the card to be drawn face-down.

	CardID StackPop(Widget stack)

		Removes the top card from the stack and returns it.
		If the stack was empty, returns CARD_NONE.
		
	void StackClear(Widget stack)

		Removes all the cards from the given stack. 
		Useful for resetting stacks.

	void StackMove(dest, source, count, animate)
	    Widget dest, source;
	    Cardinal count;
	    Boolean animate;

		Moves the top 'count' cards from 'source' over and
		onto the top of 'dest' without reordering them.
		This is the same operation performed by a successful 
		drag-and-drop. The move is optionally animated.
		(Only animate if the src+dst are not explicitly directed
		by the user!)
		It is an error if 'count' is too large for 'source'.


CARD UTILITIES

Cards themselves are identified by short integers.  Card values have their 
own collection of utility macros and constants:

	typedef short CardID;

	CardID CARD_SUIT(CardID card)

		Returns the suit indicator for the card, usually one of
			SUIT_DIAMONDS	- Diamonds suit
			SUIT_HEARTS	- Hearts suit
			SUIT_CLUBS	- Clubs suit
			SUIT_SPADES	- Spades suit
			SUIT_SPECIAL	- Special cards such as the Joker

	CardID CARD_FACE(CardID card)

		If the card is a normal suit card (with the SUIT_NORMAL bit)
		then this macro returns the face value of the card. This
		is a number between 1 and 13 inclusive. Helpful constants are:

			FACE_ACE	-  1
			FACE_TEN	- 10
			FACE_JACK	- 11
			FACE_QUEEN	- 12
			FACE_KING	- 13

	CardID CARD_COLOUR(CardID card)

		Return the colour of the card. This is one of

			COLOUR_RED	- red
			COLOUR_BLACK	- black

	Boolean CARD_IS_DOWN(CardID card)

		Returns True if the card is face down. The bit in the card
		value that indicates if it is up or down is CARD_DOWN.

	CARD_NONE	 - reserved to indicate a missing card
	CARD_BLACK_JOKER - a black joker
	CARD_RED_JOKER	 - a red joker

Some utility functions for shuffling decks (arrays) of cards are also provided:

	void CardFillStandardDeck(deck)
		CardID *deck;

	    This procedure fills the 'deck' array with one of each
	    of the 52 normal playing cards. There must be sufficient space
	    in the array for those cards. The macro CARD_STANDARD_DECK_SIZE
	    is set at 52. (Note that jokers are not added by this procedure.)

	void CardShuffle(deck, deck_length)
		CardID *deck;
		Cardinal deck_length;

	    Shuffles the deck using srand()/rand(). Note that srand is
	    initially seeded by the Unix clock, so the program should not
	    be called more than once per second.

	String CardSuitName(card)
		CardID card;

	    Returns a read-only string representation of the card's suit
	    for debugging purposes.

	String CardFaceName(card)
		CardID card;

	    Returns a read-only string representation of a card's face
	    value for debugging purposes.

CALLBACKS

'Split' callback procedures have the following signature

	Boolean split_proc(source, closure, depth)
		Widget source;
		XtPointer closure;
		Cardinal depth;

	The procedure should return True or False to permit/deny the 
	stack from being split and picked up.. 

	'source' is the stack widget being split by the user.
	'closure' is always NULL.
	'depth' is the number of cards from the top of source that the
	user is attempting to pick up.

	Note that if a split callback returns False, the stack is able
	to be 'hit'.

'Drop' callback procedures have the following signature

	Boolean drop_proc(target, closure, cards, num_cards, source)
		Widget target;
		XtPointer closure;
		CardID *cards;
		Cardinal num_cards;
		Widget source;

	The procedure should return True or False to permit/deny the 
	stack from accepting the impending drop. 

	'target' is the widget on which the drop is to occur.
	'cards' is a read-only array of CardIDs, with the bottom card value
	at index 0. 'num_cards' indicates the length of the array.
	'source' is the widget from which the cards were picked up from.
	Note that the 'source' stack will be in a state where the cards
	have already been removed (and put into the 'dragging' pseudo-stack).

'Drop' notify procedures have the following signature

	void drop_notify_proc(widget, closure, data)
		Widget widget;
		XtPointer closure;
		XtPointer data;

	'widget' is the stack that has been hit
	'closure' is always NULL.
	'data' is a pointer to a StackDropEvent structure.

	The fields of 'data' are
		source - the stack that the cards were taken from
		target - the stack where the cards are now
		count  - the number of cards transferred

'Hit' notify procedures have the following signature

	void hit_notify_proc(widget, closure, data)
		Widget widget;
		XtPointer closure;
		XtPointer data;

	'widget' is the stack that has been hit
	'closure' is always NULL.
	'data' are always NULL.

	Note that if a stack is able to be split, the default
	translations will not allow it to be 'hit' (since the mouse
	button down will initiates a drag).

ADDING DOUBLE-CLICK BEHAVIOUR

	Sometimes it is helpful to have a stack widget respond to
	a double-click. This can be achieved by augmenting the widget's
	translations with a resource like this:

		someStack.translations: #augment \
			<Btn1Down>(2):  some-action()

THINGS TO ADD

This framework is incomplete. I need to add:
	* flashing cards - StackFlash()?
	* ability to show the top few cards at a different skew
	* fractional skews (like 0.2) for neater display of tall stacks

AUTHOR

David Leonard, 2003.

SEE ALSO

Ace of Penguins API <http://www.delorie.com/store/ace/docs/toolkit.html>

$Id: NOTES,v 1.11 2003/06/10 14:09:33 d Exp $
