1411 lines
34 KiB
C++
1411 lines
34 KiB
C++
/* -*- Mode: C; tab-width: 4 -*- */
|
|
/* solitare --- Shows a pointless game, used in the Mandarin Candidate */
|
|
|
|
#if !defined( lint ) && !defined( SABER )
|
|
static const char sccsid[] = "@(#)solitare.cc 5.00 2000/11/01 xlockmore";
|
|
|
|
#endif
|
|
|
|
/* Copyright (c) D. Bagley, 1999. */
|
|
|
|
/*
|
|
* Permission to use, copy, modify, and distribute this software and its
|
|
* documentation for any purpose and without fee is hereby granted,
|
|
* provided that the above copyright notice appear in all copies and that
|
|
* both that copyright notice and this permission notice appear in
|
|
* supporting documentation.
|
|
*
|
|
* This file is provided AS IS with no warranties of any kind. The author
|
|
* shall have no liability with respect to the infringement of copyrights,
|
|
* trade secrets or any patents by this file or any part thereof. In no
|
|
* event will the author be liable for any lost revenue or profits or
|
|
* other special, indirect and consequential damages.
|
|
*
|
|
* This module is based on Eric Lassauge's text3d and Timothy Budd's
|
|
* C++ program from 1990 contained in the book "An Introduction to
|
|
* Object Oriented Programming" Addison-Wesley Publishing 1991.
|
|
*
|
|
* Albert H. Morehead and Geoffrey Mott-Smith, The Complete Book of
|
|
* Solitaire and Patience Games, Grosset & Dunlap, New York, 1949.
|
|
*
|
|
* After I started this I found out about some nice related sites:
|
|
* http://www.delorite.com/store/ace
|
|
* http://wildsav.idv.uni-linz.ac.at/mfx/psol-cardsets/index.html
|
|
*
|
|
* Revision History:
|
|
* 01-Nov-2000: Allocation checks
|
|
* 17-Jan-2000: autoplay, trackmouse, resizing, release
|
|
* 10-Dec-1999: wanted to do some C++, may end up using GL but not for now.
|
|
*
|
|
*/
|
|
|
|
#ifdef STANDALONE
|
|
#define MODE_solitare
|
|
#define PROGCLASS "Solitare"
|
|
#define HACK_INIT init_solitare
|
|
#define HACK_DRAW draw_solitare
|
|
#define solitare_opts xlockmore_opts
|
|
#define DEFAULTS "*delay: 2000000 \n" \
|
|
"*ncolors: 64 \n" \
|
|
"*mouse: False \n"
|
|
|
|
extern "C"
|
|
{
|
|
#include "xlockmore.h" /* from the xscreensaver distribution */
|
|
}
|
|
#else /* !STANDALONE */
|
|
#include "xlock.h" /* in xlockmore distribution */
|
|
#endif /* !STANDALONE */
|
|
|
|
#ifdef MODE_solitare
|
|
|
|
/* Methods overridden in subclasses of class cardPile
|
|
Suit Alternate Deck Discard Deal
|
|
initialize X X
|
|
cleanup X
|
|
addCard X X X
|
|
display X
|
|
select X X X
|
|
canTake X X
|
|
*/
|
|
|
|
#define DEF_TRACKMOUSE "False"
|
|
|
|
static Bool trackmouse;
|
|
|
|
#define MAXCOLORS 2
|
|
#define MAXSUITS 4
|
|
#define MAXRANK 13
|
|
#define MAXALTERNATEPILES 7
|
|
#define MAXPILES (MAXALTERNATEPILES+MAXSUITS+2)
|
|
#define OVERLAP (0.25)
|
|
|
|
enum Colors {black, red};
|
|
enum Suits {spade, diamond, club, heart};
|
|
|
|
#ifdef FR
|
|
// Ace => As, Jack => Valet,
|
|
// Queen = Reine (Dame), King = Roi, Joker = Joker
|
|
static const char *RankNames[] = {"", "A",
|
|
"2", "3", "4", "5", "6", "7", "8", "9", "10",
|
|
"V", "D", "R"};
|
|
#else
|
|
#ifdef NL
|
|
// Ace => Aas, Jack => Boer (Farmer),
|
|
// Queen = Vrouw (Lady), King = Heer (Gentleman), Joker = Joker
|
|
static const char *RankNames[] = {"", "A",
|
|
"2", "3", "4", "5", "6", "7", "8", "9", "10",
|
|
"B", "V", "H"};
|
|
#else
|
|
#ifdef RU
|
|
// Some may be approximations to Cyrillic
|
|
static const char *RankNames[] = {"", "T",
|
|
"2", "3", "4", "5", "6", "7", "8", "9", "10",
|
|
"B", "Q", "K"};
|
|
#else
|
|
static const char *RankNames[] = {"", "A",
|
|
"2", "3", "4", "5", "6", "7", "8", "9", "10",
|
|
"J", "Q", "K"};
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
static inline int int_round(double x) { return int(x + 0.5); }
|
|
|
|
#ifdef DOFONT
|
|
// Does this really add anything?
|
|
extern XFontStruct *getFont(Display * display);
|
|
static XFontStruct *mode_font = None;
|
|
static int char_width[256];
|
|
|
|
static int
|
|
font_width(XFontStruct * font, char ch)
|
|
{
|
|
int dummy;
|
|
XCharStruct xcs;
|
|
|
|
(void) XTextExtents(font, &ch, 1, &dummy, &dummy, &dummy, &xcs);
|
|
return xcs.width;
|
|
}
|
|
#endif
|
|
|
|
// I am probably not thinking object oriented here...
|
|
void
|
|
drawSuit(ModeInfo * mi, Suits suit, int x, int y, int width, int height)
|
|
{
|
|
Display *display = MI_DISPLAY(mi);
|
|
Window window = MI_WINDOW(mi);
|
|
GC gc = MI_GC(mi);
|
|
XPoint polygon[4];
|
|
|
|
switch (suit) {
|
|
case spade:
|
|
polygon[0].x = x + width / 2;
|
|
polygon[0].y = y;
|
|
polygon[1].x = short(float(-width) / 3.0);
|
|
polygon[1].y = height / 3;
|
|
polygon[2].x = short(float(width) / 3.0);
|
|
polygon[2].y = height / 3;
|
|
polygon[3].x = short(float(width) / 3.0) + 1;
|
|
polygon[3].y = -height / 3;
|
|
XFillPolygon(display, window, gc,
|
|
polygon, 4, Convex, CoordModePrevious);
|
|
XFillArc(display, window, gc,
|
|
x, y + height / 3, width / 2, height / 2, 0, 23040);
|
|
XFillArc(display, window, gc,
|
|
x + width / 2, y + height / 3, width / 2, height / 2, 0, 23040);
|
|
polygon[0].x = x + width / 2;
|
|
polygon[0].y = y + height / 2;
|
|
polygon[1].x = -width / 8;
|
|
polygon[1].y = height / 2;
|
|
polygon[2].x = width / 4;
|
|
polygon[2].y = 0;
|
|
XFillPolygon(display, window, gc,
|
|
polygon, 3, Convex, CoordModePrevious);
|
|
break;
|
|
case diamond:
|
|
polygon[0].x = x + width / 2;
|
|
polygon[0].y = y;
|
|
polygon[1].x = -width / 2;
|
|
polygon[1].y = height / 2;
|
|
polygon[2].x = width / 2;
|
|
polygon[2].y = height / 2;
|
|
polygon[3].x = width / 2;
|
|
polygon[3].y = -height / 2;
|
|
XFillPolygon(display, window, gc,
|
|
polygon, 4, Convex, CoordModePrevious);
|
|
break;
|
|
case club:
|
|
XFillArc(display, window, gc,
|
|
x, y + short(float(height) / 3.6), width / 2, height / 2, 0, 23040);
|
|
XFillArc(display, window, gc,
|
|
x + width / 2, y + short(float(height) / 3.6), width / 2, height / 2, 0, 23040);
|
|
XFillArc(display, window, gc,
|
|
x + width / 4, y, width / 2, height / 2, 0, 23040);
|
|
polygon[0].x = x + width / 2;
|
|
polygon[0].y = y + height / 2 - 1;
|
|
polygon[1].x = -width / 8;
|
|
polygon[1].y = height / 2 + 1;
|
|
polygon[2].x = width / 4;
|
|
polygon[2].y = 0;
|
|
XFillPolygon(display, window, gc,
|
|
polygon, 3, Convex, CoordModePrevious);
|
|
break;
|
|
case heart:
|
|
XFillArc(display, window, gc,
|
|
x + 1, y, width / 2, height / 2, 0, 23040);
|
|
XFillArc(display, window, gc,
|
|
x + width / 2 - 1, y, width / 2, height / 2, 0, 23040);
|
|
polygon[0].x = x + width / 2;
|
|
polygon[0].y = y + short(float(height) / 4.1);
|
|
polygon[1].x = short(float(-width) / 2.7);
|
|
polygon[1].y = short(float(height) / 4.1);
|
|
polygon[2].x = short(float(width) / 2.7);
|
|
polygon[2].y = short(float(height) / 2.05);
|
|
polygon[3].x = short(float(width) / 2.7);
|
|
polygon[3].y = short(float(-height) / 2.05);
|
|
XFillPolygon(display, window, gc,
|
|
polygon, 4, Convex, CoordModePrevious);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//card.h
|
|
class Card
|
|
{
|
|
private:
|
|
Suits suit;
|
|
int rank;
|
|
public:
|
|
Card(Suits suit, int rank);
|
|
Suits whichSuit();
|
|
int whichRank();
|
|
Colors whichColor();
|
|
};
|
|
inline Card::Card(Suits a_suit, int a_rank) { this->suit = a_suit; this->rank = a_rank;}
|
|
inline Suits Card::whichSuit() { return this->suit;}
|
|
inline int Card::whichRank() { return this->rank;}
|
|
inline Colors Card::whichColor() { return ((whichSuit() % MAXCOLORS) ? red : black);}
|
|
|
|
|
|
//cardview.h
|
|
class CardView
|
|
{
|
|
private:
|
|
ModeInfo *mi;
|
|
Card * card;
|
|
Bool faceUp;
|
|
int locationX, locationY;
|
|
public:
|
|
CardView(ModeInfo * mi, Card * card);
|
|
CardView(ModeInfo *mi, Suits suit, int rank);
|
|
Card * thisCard();
|
|
void draw();
|
|
void erase();
|
|
Bool isFaceUp();
|
|
void flip();
|
|
Bool includes(int, int); // mouse interaction
|
|
int x();
|
|
int y();
|
|
int width();
|
|
int height();
|
|
void moveTo(int, int);
|
|
};
|
|
|
|
inline Card* CardView::thisCard() { return card; }
|
|
inline Bool CardView::isFaceUp() { return faceUp; }
|
|
inline void CardView::flip() { faceUp = !faceUp; }
|
|
inline int CardView::x() { return locationX; }
|
|
inline int CardView::y() { return locationY; }
|
|
inline void CardView::moveTo(int lx, int ly) { locationX = lx; locationY = ly; }
|
|
|
|
//cardview.cc
|
|
CardView::CardView(ModeInfo * a_mi, Card * a_card)
|
|
{
|
|
this->mi = a_mi;
|
|
this->card = a_card;
|
|
faceUp = False;
|
|
locationX = locationY = 0;
|
|
}
|
|
|
|
CardView::CardView(ModeInfo * a_mi, Suits suit, int rank)
|
|
{
|
|
this->mi = a_mi;
|
|
if ((this->card = new Card(suit, rank)) == NULL) {
|
|
return;
|
|
}
|
|
faceUp = False;
|
|
locationX = locationY = 0;
|
|
}
|
|
|
|
void CardView::draw()
|
|
{
|
|
Display * display = MI_DISPLAY(mi);
|
|
Window window = MI_WINDOW(mi);
|
|
GC gc = MI_GC(mi);
|
|
unsigned long red_pixel = (MI_NPIXELS(mi) > 2) ? MI_PIXEL(mi,0) : MI_BLACK_PIXEL(mi);
|
|
unsigned long cyan_pixel = (MI_NPIXELS(mi) > 2) ? MI_PIXEL(mi,MI_NPIXELS(mi) / 2) : MI_WHITE_PIXEL(mi);
|
|
|
|
if (isFaceUp()) {
|
|
XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
|
|
XFillRectangle(display, window, gc, x(), y(), width(), height());
|
|
if (card->whichColor() == red) {
|
|
XSetForeground(display, gc, red_pixel);
|
|
} else {
|
|
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
|
|
}
|
|
if (width() > 16 && height() > 34) {
|
|
#ifdef CENTERLABEL
|
|
XDrawString(display, window, gc,
|
|
x() + width() / 2 - strlen(RankNames[card->whichRank()]) * 8,
|
|
y() + int_round(height() * 0.40),
|
|
RankNames[card->whichRank()], strlen(RankNames[card->whichRank()]));
|
|
#else
|
|
XDrawString(display, window, gc,
|
|
x() + 8 - strlen(RankNames[card->whichRank()]) * 8 + width() / 8,
|
|
y() + 20,
|
|
RankNames[card->whichRank()], strlen(RankNames[card->whichRank()]));
|
|
#endif
|
|
}
|
|
#ifdef SUITNAMES
|
|
char *SuitNames[] = {"Spade", "Diamond", "Club", "Heart"};
|
|
XDrawString(display, window, gc, x() + 0.2, y() + int_round(height() * 0.8),
|
|
SuitNames[card->whichSuit()], strlen(SuitNames[card->whichSuit()]));
|
|
#endif
|
|
#ifdef CENTERLABEL
|
|
drawSuit(mi, card->whichSuit(),
|
|
x() + int_round(width() * 0.35), y() + int_round(height() * 0.55),
|
|
int_round(0.3 * width()), int_round(0.3 * height()));
|
|
#else
|
|
if (width() > 16 && height() > 34) {
|
|
drawSuit(mi, card->whichSuit(),
|
|
x() + 5, y() + 25,
|
|
int_round(0.3 * width()), int_round(0.3 * height()));
|
|
} else {
|
|
drawSuit(mi, card->whichSuit(),
|
|
x() + 2, y() + 2,
|
|
int_round(0.3 * width()), int_round(0.3 * height()));
|
|
}
|
|
#endif
|
|
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
|
|
XDrawLine(display, window, gc,
|
|
x() + 1, y() + 1, x() + width() - 3, y() + 1);
|
|
XDrawLine(display, window, gc,
|
|
x() + 1, y() + height() - 2, x() + width() - 3, y() + height() - 2);
|
|
XDrawLine(display, window, gc,
|
|
x() + 1, y() + 1, x() + 1, y() + height() - 3);
|
|
XDrawLine(display, window, gc,
|
|
x() + width() - 2, y() + 1, x() + width() - 2, y() + height() - 3);
|
|
} else {
|
|
int n;
|
|
|
|
XSetForeground(display, gc, cyan_pixel);
|
|
XFillRectangle(display, window, gc,
|
|
x(), y(), width(), height());
|
|
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
|
|
n = x() + int_round(int_round(width() * 0.3));
|
|
XDrawLine(display, window, gc, n, y() + int_round(height() * 0.1),
|
|
n, y() + int_round(height() * 0.9));
|
|
n = x() + int_round(width() * 0.7);
|
|
XDrawLine(display, window, gc, n, y() + int_round(height() * 0.1),
|
|
n, y() + int_round(height() * 0.9));
|
|
n = y() + int_round(height() * 0.3);
|
|
XDrawLine(display, window, gc, x() + int_round(height() * 0.1), n,
|
|
x() + int_round(width() * 0.9), n);
|
|
n = y() + int_round(height() * 0.7);
|
|
XDrawLine(display, window, gc, x() + int_round(width() * 0.1), n,
|
|
x() + int(width() * 0.9), n);
|
|
}
|
|
// Shadow
|
|
#if 1
|
|
XDrawLine(display, window, gc,
|
|
x() - 1, y() - 1 , x() + width(), y() - 1);
|
|
XDrawLine(display, window, gc,
|
|
x() - 1, y() - 1, x() - 1, y() + height());
|
|
#else
|
|
XDrawLine(display, window, gc,
|
|
x() + 1, y() + height(), x() + width(), y() + height());
|
|
XDrawLine(display, window, gc,
|
|
x() + width(), y() + 1, x() + width(), y() + height());
|
|
#endif
|
|
}
|
|
|
|
void CardView::erase()
|
|
{
|
|
Display * display = MI_DISPLAY(mi);
|
|
Window window = MI_WINDOW(mi);
|
|
GC gc = MI_GC(mi);
|
|
|
|
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
|
|
XFillRectangle(display, window, gc,
|
|
x(), y(), width(), height());
|
|
}
|
|
|
|
//pile.h
|
|
Bool CardView::includes(int a, int b)
|
|
{
|
|
return (a >= x() && (a <= x() + width()) &&
|
|
b >= y() && (b <= y() + height()));
|
|
}
|
|
|
|
class CardLink : public CardView
|
|
{
|
|
public:
|
|
CardLink(ModeInfo * mi, Suits suit, int rank);
|
|
CardLink(ModeInfo * mi, Card * card);
|
|
CardLink * nextCard();
|
|
void setLink(CardLink * card);
|
|
|
|
private:
|
|
CardLink * link;
|
|
};
|
|
|
|
inline CardLink::CardLink(ModeInfo *a_mi, Suits suit, int rank) : CardView(a_mi, suit, rank) { ; }
|
|
inline CardLink::CardLink(ModeInfo *a_mi, Card * a_card) : CardView(a_mi, a_card) { ; }
|
|
inline CardLink * CardLink::nextCard() { return link; }
|
|
inline void CardLink::setLink(CardLink * a_card) { link = a_card; }
|
|
|
|
class CardPile
|
|
{
|
|
public:
|
|
CardPile(ModeInfo *);
|
|
|
|
virtual void updateLocation(ModeInfo *);
|
|
virtual void addCard(CardLink *);
|
|
virtual Bool canTake(Card *);
|
|
Bool contains(int, int); // mouse interaction
|
|
virtual void displayPile();
|
|
virtual Bool initialize();
|
|
virtual void cleanup();
|
|
CardLink * removeCard();
|
|
virtual Bool select(int, int); // mouse interaction
|
|
virtual Bool select(Bool); // generator
|
|
|
|
protected:
|
|
int x, y;
|
|
ModeInfo * mi;
|
|
CardLink *top;
|
|
};
|
|
|
|
#define nilLink (CardLink *) 0
|
|
|
|
inline CardPile::CardPile(ModeInfo * a_mi) { this->mi = a_mi; top = nilLink; }
|
|
|
|
class DealPile : public CardPile
|
|
{
|
|
public:
|
|
DealPile(ModeInfo *);
|
|
virtual void cleanup();
|
|
Bool shuffle(CardPile *);
|
|
};
|
|
inline DealPile::DealPile(ModeInfo * a_mi) : CardPile(a_mi) { ; }
|
|
|
|
class SuitPile : public CardPile
|
|
{
|
|
public:
|
|
SuitPile(ModeInfo * a_mi, int s) : CardPile(a_mi) { suit = s; }
|
|
virtual void updateLocation(ModeInfo *);
|
|
virtual Bool canTake(Card *);
|
|
private:
|
|
int suit;
|
|
};
|
|
|
|
class AlternatePile : public CardPile
|
|
{
|
|
public:
|
|
AlternatePile(ModeInfo * a_mi, int p) : CardPile(a_mi) { pile = p; }
|
|
virtual void updateLocation(ModeInfo *);
|
|
virtual void addCard(CardLink *);
|
|
virtual Bool canTake(Card *);
|
|
void copyBuild(CardLink *, CardPile *);
|
|
virtual void displayPile();
|
|
virtual Bool select(int, int);
|
|
virtual Bool select(Bool);
|
|
virtual Bool initialize();
|
|
private:
|
|
int pile;
|
|
};
|
|
|
|
class DeckPile : public CardPile
|
|
{
|
|
public:
|
|
DeckPile(ModeInfo * a_mi) : CardPile(a_mi) { ; }
|
|
virtual void updateLocation(ModeInfo *);
|
|
virtual void addCard(CardLink *);
|
|
virtual Bool initialize();
|
|
virtual Bool select(int, int);
|
|
virtual Bool select(Bool);
|
|
};
|
|
|
|
class DiscardPile : public CardPile
|
|
{
|
|
public:
|
|
DiscardPile(ModeInfo * a_mi) : CardPile(a_mi) { ; }
|
|
virtual void updateLocation(ModeInfo *);
|
|
virtual void addCard(CardLink *);
|
|
virtual Bool select(int, int);
|
|
virtual Bool select(Bool);
|
|
};
|
|
|
|
//game.h
|
|
class GameTable
|
|
{
|
|
public:
|
|
GameTable(ModeInfo *);
|
|
~GameTable();
|
|
Bool newGame(ModeInfo *);
|
|
Bool suitCanAdd(Card *);
|
|
CardPile * suitAddPile(Card *);
|
|
Bool alternateCanAdd(Card *);
|
|
CardPile * alternateAddPile(Card *);
|
|
void Resize(ModeInfo *);
|
|
void Redraw(ModeInfo *);
|
|
Bool HandleMouse(ModeInfo *);
|
|
Bool HandleGenerate();
|
|
|
|
DealPile *dealPile;
|
|
|
|
CardPile *deckPile;
|
|
CardPile *discardPile;
|
|
protected:
|
|
CardPile * allPiles[MAXALTERNATEPILES + MAXSUITS + 2];
|
|
CardPile * suitPiles[MAXSUITS];
|
|
CardPile * alternatePiles[MAXALTERNATEPILES];
|
|
};
|
|
|
|
// pile.cc
|
|
|
|
typedef struct {
|
|
Bool painted;
|
|
GameTable * game;
|
|
int width, height, cardwidth, cardheight;
|
|
int showend;
|
|
#ifdef DOFONT
|
|
int fontascent, fontheight;
|
|
#endif
|
|
} solitarestruct;
|
|
|
|
static solitarestruct *solitare = (solitarestruct *) NULL;
|
|
|
|
int CardView::width()
|
|
{
|
|
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
|
|
|
|
return bp->cardwidth;
|
|
}
|
|
|
|
int CardView::height()
|
|
{
|
|
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
|
|
|
|
return bp->cardheight;
|
|
}
|
|
|
|
void CardPile::addCard(CardLink * card)
|
|
{
|
|
if (card != nilLink) {
|
|
card->setLink(top);
|
|
top = card;
|
|
top->moveTo(x, y);
|
|
}
|
|
}
|
|
|
|
void CardPile::updateLocation(ModeInfo * a_mi)
|
|
{
|
|
;
|
|
}
|
|
|
|
Bool CardPile::canTake(Card * card)
|
|
{
|
|
return False; // from virtual, card unused
|
|
}
|
|
|
|
Bool CardPile::contains(int a, int b)
|
|
{
|
|
for (CardLink *p = top; p!= nilLink; p = p->nextCard())
|
|
if (p->includes(a, b))
|
|
return True;
|
|
return False;
|
|
}
|
|
|
|
void CardPile::displayPile()
|
|
{
|
|
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
|
|
Display * display = MI_DISPLAY(mi);
|
|
Window window = MI_WINDOW(mi);
|
|
GC gc = MI_GC(mi);
|
|
|
|
if (top == nilLink) {
|
|
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
|
|
XFillRectangle(display, window, gc,
|
|
x, y, bp->cardwidth, bp->cardheight);
|
|
} else {
|
|
top->draw();
|
|
}
|
|
}
|
|
|
|
Bool CardPile::initialize()
|
|
{
|
|
top = nilLink;
|
|
return True;
|
|
}
|
|
|
|
void CardPile::cleanup()
|
|
{
|
|
CardLink *p;
|
|
|
|
while (top != nilLink) {
|
|
p = top;
|
|
top = top->nextCard();
|
|
delete p;
|
|
}
|
|
}
|
|
|
|
CardLink * CardPile::removeCard()
|
|
{
|
|
CardLink *p;
|
|
|
|
if (top == nilLink)
|
|
return nilLink;
|
|
p = top;
|
|
top = top->nextCard();
|
|
return p;
|
|
}
|
|
|
|
Bool CardPile::select(int a_x, int a_y)
|
|
{
|
|
// from virtual, a_x & a_y unused
|
|
return select(True);
|
|
}
|
|
|
|
Bool CardPile::select(Bool first)
|
|
{
|
|
// from virtual, first unused
|
|
return False;
|
|
}
|
|
|
|
void DealPile::cleanup()
|
|
{
|
|
CardLink *p;
|
|
|
|
while (top != nilLink) {
|
|
p = top;
|
|
top = top->nextCard();
|
|
delete p->thisCard();
|
|
delete p;
|
|
}
|
|
}
|
|
|
|
Bool DealPile::shuffle(CardPile * pile)
|
|
{
|
|
CardLink *p, *q;
|
|
int max, limit, i;
|
|
|
|
// first see how many cards we have
|
|
for (max = 0, p = top; p != nilLink; p = p->nextCard()) {
|
|
max++;
|
|
if (p->isFaceUp())
|
|
p->flip();
|
|
}
|
|
// then pull them out, randomly, one at a time
|
|
for (; max > 0; max--) {
|
|
limit = (int) ((LRAND() >> 3) % max) + 1;
|
|
for ( i= 1, p = top; i <= limit; ) {
|
|
while (p->isFaceUp())
|
|
p = p->nextCard();
|
|
i++;
|
|
if (i <= limit)
|
|
p = p->nextCard();
|
|
}
|
|
if ((q = new CardLink(mi, p->thisCard())) == NULL) {
|
|
return False;
|
|
}
|
|
pile->addCard(q);
|
|
p->flip();
|
|
}
|
|
return True;
|
|
}
|
|
|
|
void DeckPile::updateLocation(ModeInfo * a_mi)
|
|
{
|
|
solitarestruct *bp = &solitare[MI_SCREEN(a_mi)];
|
|
|
|
//set pile
|
|
x = int_round(((bp->cardwidth + 2.0 * MAXSUITS * bp->cardwidth *
|
|
(MAXALTERNATEPILES + 1.0)) / (2.0 * MAXALTERNATEPILES)) +
|
|
3 * bp->cardwidth / 4.0);
|
|
y = int_round(0.25 * bp->cardheight);
|
|
|
|
// set card
|
|
for (CardLink *p = top; p!= nilLink; p = p->nextCard()) {
|
|
p->moveTo(x, y);
|
|
}
|
|
}
|
|
|
|
Bool DeckPile::initialize()
|
|
{
|
|
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
|
|
|
|
CardPile::initialize();
|
|
if (!bp->game->dealPile->shuffle(this))
|
|
return False;
|
|
return True;
|
|
}
|
|
|
|
void DeckPile::addCard(CardLink *c)
|
|
{
|
|
if (c->isFaceUp())
|
|
c->flip();
|
|
CardPile::addCard(c);
|
|
}
|
|
|
|
Bool DeckPile::select(int a_x, int a_y)
|
|
{
|
|
// from virtual, a_x & a_y unused
|
|
return select(True);
|
|
}
|
|
|
|
// turn over a new card
|
|
Bool DeckPile::select(Bool first)
|
|
{
|
|
// from virtual, first unused
|
|
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
|
|
CardLink *c;
|
|
Bool didSomething = False;
|
|
|
|
if (top != nilLink) {
|
|
c = removeCard(); // should not this be 3 at a time
|
|
if (c != nilLink)
|
|
(bp->game->discardPile)->addCard(c);
|
|
didSomething = True;
|
|
}
|
|
displayPile();
|
|
(bp->game->discardPile)->displayPile();
|
|
return didSomething;
|
|
}
|
|
|
|
void DiscardPile::updateLocation(ModeInfo * a_mi)
|
|
{
|
|
solitarestruct *bp = &solitare[MI_SCREEN(a_mi)];
|
|
|
|
//set pile
|
|
x = int_round(((bp->cardwidth + 2.0 * MAXSUITS * bp->cardwidth *
|
|
(MAXALTERNATEPILES + 1.0)) / (2.0 * MAXALTERNATEPILES)) +
|
|
2 * bp->cardwidth);
|
|
y = int_round(0.25 * bp->cardheight);
|
|
|
|
// set card
|
|
for (CardLink *p = top; p!= nilLink; p = p->nextCard()) {
|
|
p->moveTo(x, y);
|
|
}
|
|
}
|
|
|
|
void DiscardPile::addCard(CardLink *c)
|
|
{
|
|
if (!(c->isFaceUp()))
|
|
c->flip();
|
|
CardPile::addCard(c);
|
|
}
|
|
|
|
// play the current face card
|
|
Bool DiscardPile::select(int a_x, int a_y)
|
|
{
|
|
// from virtual, a_x & a_y unused
|
|
return select(True);
|
|
}
|
|
|
|
// play the current face card
|
|
Bool DiscardPile::select(Bool first)
|
|
{
|
|
// from virtual, first unused
|
|
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
|
|
CardPile * pile;
|
|
|
|
if (top == nilLink)
|
|
return False;
|
|
// see if we can move it to a suit pile
|
|
if (bp->game->suitCanAdd(top->thisCard())) {
|
|
pile = bp->game->suitAddPile(top->thisCard());
|
|
pile->addCard(removeCard());
|
|
displayPile();
|
|
pile->displayPile();
|
|
return True;
|
|
}
|
|
if (bp->game->alternateCanAdd(top->thisCard())) {
|
|
pile = bp->game->alternateAddPile(top->thisCard());
|
|
pile->addCard(removeCard());
|
|
displayPile();
|
|
pile->displayPile();
|
|
return True;
|
|
}
|
|
return False;
|
|
}
|
|
|
|
void SuitPile::updateLocation(ModeInfo * a_mi)
|
|
{
|
|
solitarestruct *bp = &solitare[MI_SCREEN(a_mi)];
|
|
|
|
//set pile
|
|
x = int_round(((bp->cardwidth + 2.0 * suit * bp->cardwidth *
|
|
(MAXALTERNATEPILES + 1.0)) / (2.0 * MAXALTERNATEPILES)) +
|
|
bp->cardwidth / 2.0);
|
|
y = int_round(0.25 * bp->cardheight);
|
|
|
|
// set card
|
|
for (CardLink *p = top; p!= nilLink; p = p->nextCard()) {
|
|
p->moveTo(x, y);
|
|
}
|
|
}
|
|
|
|
Bool SuitPile::canTake(Card * card)
|
|
{
|
|
if (top == nilLink) { // empty so can take ace
|
|
if (card->whichRank() == 1)
|
|
return True;
|
|
return False;
|
|
}
|
|
if ((top->thisCard())->whichSuit() != card->whichSuit())
|
|
return False;
|
|
if (((top->thisCard())->whichRank() + 1) == card->whichRank())
|
|
return True;
|
|
return False;
|
|
}
|
|
|
|
void AlternatePile::updateLocation(ModeInfo * a_mi)
|
|
{
|
|
solitarestruct *bp = &solitare[MI_SCREEN(a_mi)];
|
|
int ty;
|
|
|
|
//set pile
|
|
x = (int_round((bp->cardwidth + 2.0 * pile * bp->cardwidth *
|
|
(MAXALTERNATEPILES + 1.0)) / (2.0 * MAXALTERNATEPILES)));
|
|
y = int_round(1.5 * bp->cardheight);
|
|
|
|
CardLink *bf = nilLink;
|
|
// set card
|
|
for (CardLink *p = top; p!= nilLink; p = p->nextCard()) {
|
|
p->moveTo(x, y);
|
|
// find bottom faceup card
|
|
// not setup to work with STRANGE_LAYOUT
|
|
if (p->isFaceUp())
|
|
bf = p;
|
|
}
|
|
if (bf != nilLink) {
|
|
// ok but this is wrong. Since we only have a singly link
|
|
// we will do this slightly inefficiently...
|
|
ty = y;
|
|
while (bf != top) {
|
|
CardLink *sp;
|
|
for (sp = top; sp->nextCard() != bf; sp = sp->nextCard());
|
|
bf = sp;
|
|
ty += int_round(bp->cardheight * OVERLAP);
|
|
sp->moveTo(x, ty);
|
|
}
|
|
}
|
|
}
|
|
|
|
Bool AlternatePile::initialize()
|
|
{
|
|
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
|
|
int a_pile;
|
|
|
|
//put the right number of cards on the alternate
|
|
(void) CardPile::initialize();
|
|
for (a_pile = 0; a_pile <= this->pile; a_pile++)
|
|
addCard((bp->game->deckPile)->removeCard());
|
|
// flip the last one
|
|
if (top != nilLink) {
|
|
top->flip();
|
|
}
|
|
return True;
|
|
}
|
|
|
|
void AlternatePile::addCard(CardLink * card)
|
|
{
|
|
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
|
|
int tx, ty;
|
|
|
|
if (top == nilLink)
|
|
CardPile::addCard(card);
|
|
else {
|
|
tx = top->x();
|
|
ty = top->y();
|
|
|
|
// figure out where to place the card
|
|
#ifdef STRANGE_LAYOUT
|
|
// This was the original logic... does not look right to me.
|
|
if (!(top->isFaceUp() && top->nextCard() != nilLink &&
|
|
(top->nextCard())->isFaceUp()))
|
|
#else
|
|
if (top->isFaceUp())
|
|
#endif
|
|
ty += int_round(bp->cardheight * OVERLAP);
|
|
CardPile::addCard(card);
|
|
top->moveTo(tx, ty);
|
|
}
|
|
}
|
|
|
|
Bool AlternatePile::canTake(Card *card)
|
|
{
|
|
if (top == nilLink) { // can take only kings on an empty pile
|
|
if (card->whichRank() == MAXRANK) {
|
|
return True;
|
|
}
|
|
return False;
|
|
}
|
|
// see if it is face up, colors are different, and the number is legal
|
|
if ((top->thisCard())->whichColor() != card->whichColor() &&
|
|
((top->thisCard())->whichRank() - 1) == card->whichRank()) {
|
|
return True;
|
|
}
|
|
return False;
|
|
}
|
|
|
|
void AlternatePile::copyBuild(CardLink *card, CardPile * a_pile)
|
|
{
|
|
CardLink *tempCard;
|
|
|
|
top->erase();
|
|
tempCard = removeCard();
|
|
displayPile();
|
|
if (card != tempCard)
|
|
copyBuild(card, a_pile);
|
|
a_pile->addCard(tempCard);
|
|
a_pile->displayPile();
|
|
}
|
|
|
|
static void stackDisplay(CardLink *p)
|
|
{
|
|
if (p->nextCard())
|
|
stackDisplay(p->nextCard());
|
|
p->draw();
|
|
}
|
|
|
|
void AlternatePile::displayPile()
|
|
{
|
|
// Zero or one cards, can not do any better
|
|
if (top == nilLink)
|
|
CardPile::displayPile();
|
|
else { // otherwise half display all the covered cards
|
|
stackDisplay(top);
|
|
}
|
|
}
|
|
|
|
Bool AlternatePile::select(int a_x, int a_y)
|
|
{
|
|
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
|
|
CardLink *c;
|
|
|
|
// no cards, do nothing
|
|
if (top == nilLink)
|
|
return False;
|
|
|
|
// if top card is not flipped, flip it now
|
|
if (!top->isFaceUp()) {
|
|
top->flip();
|
|
top->draw();
|
|
return True;
|
|
}
|
|
|
|
// if it was to top card, see if we can move it
|
|
if (top->includes(a_x, a_y)) {
|
|
// see if we ca move it to a suit pile
|
|
if (bp->game->suitCanAdd(top->thisCard())) {
|
|
copyBuild(top, bp->game->suitAddPile(top->thisCard()));
|
|
return True;
|
|
}
|
|
// else see if we can move a alternate pile but only if it is not part of pile
|
|
if (((top->nextCard() == nilLink) || !((top->nextCard())->isFaceUp())) &&
|
|
bp->game->alternateCanAdd(top->thisCard())) {
|
|
copyBuild(top, bp->game->alternateAddPile(top->thisCard()));
|
|
return True;
|
|
}
|
|
}
|
|
|
|
// else see if we can move a pile
|
|
for (c = top->nextCard(); c!= nilLink; c = c->nextCard())
|
|
if (c->isFaceUp() && c->includes(a_x, a_y)) {
|
|
if (bp->game->alternateCanAdd(c->thisCard())) {
|
|
copyBuild(c, bp->game->alternateAddPile(c->thisCard()));
|
|
}
|
|
return True;
|
|
}
|
|
return False;
|
|
}
|
|
|
|
Bool AlternatePile::select(Bool first)
|
|
{
|
|
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
|
|
CardLink *c;
|
|
|
|
// no cards, do nothing
|
|
if (top == nilLink)
|
|
return False;
|
|
|
|
// if top card is not flipped, flip it now
|
|
if (!top->isFaceUp()) {
|
|
top->flip();
|
|
top->draw();
|
|
return True;
|
|
}
|
|
// see if we ca move it to a suit pile
|
|
if (bp->game->suitCanAdd(top->thisCard())) {
|
|
copyBuild(top, bp->game->suitAddPile(top->thisCard()));
|
|
return True;
|
|
}
|
|
|
|
// If all cards are not flipped it could lead to problems...
|
|
if (first)
|
|
return False;
|
|
|
|
// Other moves may be better but this is stupid
|
|
// Bouncing may result if one allows to take away moves
|
|
// find highest faceup card
|
|
c = top;
|
|
while (c->nextCard() != nilLink && (c->nextCard())->isFaceUp())
|
|
c = c->nextCard();
|
|
|
|
// if not king see if it can be moved to leave a space (else it will bounce)
|
|
if (((c->nextCard() != nilLink) ||
|
|
((c->thisCard())->whichRank() != MAXRANK)) &&
|
|
bp->game->alternateCanAdd(c->thisCard())) {
|
|
copyBuild(c, bp->game->alternateAddPile(c->thisCard()));
|
|
return True;
|
|
}
|
|
return False;
|
|
}
|
|
|
|
GameTable::GameTable(ModeInfo *mi)
|
|
{
|
|
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
|
|
int suit, rank, pile;
|
|
|
|
bp->width = MI_WIDTH(mi);
|
|
bp->height = MI_HEIGHT(mi);
|
|
bp->showend = 0;
|
|
bp->cardwidth = bp->width / (MAXALTERNATEPILES + 1);
|
|
bp->cardheight = bp->height / 5;
|
|
// Create the original (unshuffled) deck (with no cards) ... dah daaah!
|
|
if ((dealPile = new DealPile(mi)) == NULL) {
|
|
return;
|
|
}
|
|
for (suit = 0; suit < MAXSUITS; suit++)
|
|
for (rank = 1; rank <= MAXRANK; rank++) {
|
|
CardLink * deal;
|
|
// 52 pickup, creating new cards and creating links
|
|
if ((deal = new CardLink(mi, Suits(suit), rank)) == NULL) {
|
|
dealPile->cleanup();
|
|
delete dealPile;
|
|
return;
|
|
}
|
|
dealPile->addCard(deal);
|
|
}
|
|
// create 2 piles the deck and discard piles
|
|
if ((allPiles[0] = deckPile = new DeckPile(mi)) == NULL) {
|
|
dealPile->cleanup();
|
|
delete dealPile;
|
|
return;
|
|
}
|
|
deckPile->updateLocation(mi);
|
|
if ((allPiles[1] = discardPile = new DiscardPile(mi)) == NULL) {
|
|
dealPile->cleanup();
|
|
delete dealPile;
|
|
delete deckPile;
|
|
return;
|
|
}
|
|
discardPile->updateLocation(mi);
|
|
|
|
// create suit piles
|
|
for (suit = 0; suit < MAXSUITS; suit++) {
|
|
if ((allPiles[suit + 2] = suitPiles[Suits(suit)] = new SuitPile(mi, suit)) == NULL) {
|
|
delete this;
|
|
return;
|
|
}
|
|
(suitPiles[Suits(suit)])->updateLocation(mi);
|
|
}
|
|
|
|
// create alternate piles
|
|
for (pile = 0; pile < MAXALTERNATEPILES; pile++) {
|
|
if ((allPiles[pile + 2 + MAXSUITS] = alternatePiles[pile] =
|
|
new AlternatePile(mi, pile)) == NULL) {
|
|
delete this;
|
|
return;
|
|
}
|
|
(alternatePiles[pile])->updateLocation(mi);
|
|
}
|
|
}
|
|
|
|
GameTable::~GameTable()
|
|
{
|
|
for (int pile = 0; pile < MAXPILES; pile++)
|
|
if (allPiles[pile]) {
|
|
allPiles[pile]->cleanup();
|
|
delete allPiles[pile];
|
|
}
|
|
if (dealPile) {
|
|
dealPile->cleanup();
|
|
delete dealPile;
|
|
}
|
|
}
|
|
|
|
void GameTable::Resize(ModeInfo *mi)
|
|
{
|
|
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
|
|
int suit, pile;
|
|
|
|
bp->width = MI_WIDTH(mi);
|
|
bp->height = MI_HEIGHT(mi);
|
|
bp->cardwidth = bp->width / (MAXALTERNATEPILES + 1);
|
|
bp->cardheight = bp->height / 5;
|
|
deckPile->updateLocation(mi);
|
|
discardPile->updateLocation(mi);
|
|
|
|
// move suit piles
|
|
for (suit = 0; suit < MAXSUITS; suit++) {
|
|
(suitPiles[Suits(suit)])->updateLocation(mi);
|
|
}
|
|
|
|
// move alternate piles
|
|
for (pile = 0; pile < MAXALTERNATEPILES; pile++) {
|
|
(alternatePiles[pile])->updateLocation(mi);
|
|
}
|
|
}
|
|
|
|
Bool GameTable::newGame(ModeInfo * mi)
|
|
{
|
|
int pile;
|
|
|
|
// initialize all the piles
|
|
for (pile = 0; pile < MAXPILES; pile++)
|
|
if (!allPiles[pile]->initialize())
|
|
return False;
|
|
// redraw the game window
|
|
Redraw(mi);
|
|
return True;
|
|
}
|
|
|
|
void GameTable::Redraw(ModeInfo * mi)
|
|
{
|
|
int pile;
|
|
|
|
//first clear the entire playing area
|
|
MI_CLEARWINDOW(mi);
|
|
|
|
// then display the piles
|
|
for (pile = 0; pile < MAXPILES; pile++)
|
|
allPiles[pile]->displayPile();
|
|
}
|
|
|
|
// this is where the smarts is/is not
|
|
Bool GameTable::HandleMouse(ModeInfo * mi)
|
|
{
|
|
Window r, c;
|
|
int cx, cy, rx, ry;
|
|
unsigned int m;
|
|
int pile;
|
|
|
|
(void) XQueryPointer(MI_DISPLAY(mi), MI_WINDOW(mi),
|
|
&r, &c, &rx, &ry, &cx, &cy, &m);
|
|
|
|
if (cx <= 0 || cy <= 0 ||
|
|
cx >= MI_WIDTH(mi) - 1 || cy >= MI_HEIGHT(mi) -1) {
|
|
return HandleGenerate();
|
|
}
|
|
|
|
for (pile = 0; pile < MAXPILES; pile++)
|
|
if (allPiles[pile]->contains(cx, cy)) {
|
|
(void) allPiles[pile]->select(cx, cy);
|
|
return True;
|
|
}
|
|
return True;
|
|
}
|
|
|
|
// if done right it looks smart...
|
|
Bool GameTable::HandleGenerate()
|
|
{
|
|
int pile;
|
|
|
|
// Start with the biggest stacks... usually the last ones
|
|
for (pile = MAXALTERNATEPILES - 1; pile >= 0; pile--)
|
|
if (alternatePiles[pile]->select(True))
|
|
return True;
|
|
for (pile = MAXALTERNATEPILES - 1; pile >= 0; pile--)
|
|
if (alternatePiles[pile]->select(False))
|
|
return True;
|
|
// Look for something to do in discard pile
|
|
if (discardPile->select(True))
|
|
return True;
|
|
|
|
// Get a new card or end it
|
|
return (deckPile->select(True));
|
|
}
|
|
|
|
// see if any of the suit piles can add a specific card
|
|
Bool GameTable::suitCanAdd(Card * card)
|
|
{
|
|
int suit;
|
|
|
|
for (suit = 0; suit < MAXSUITS; suit++)
|
|
if (suitPiles[Suits(suit)]->canTake(card))
|
|
return True;
|
|
return False;
|
|
}
|
|
|
|
// see if any of the alternate piles can add a specific card
|
|
Bool GameTable::alternateCanAdd(Card * card)
|
|
{
|
|
int pile;
|
|
|
|
for (pile = 0; pile < MAXALTERNATEPILES; pile++)
|
|
if (alternatePiles[pile]->canTake(card))
|
|
return True;
|
|
return False;
|
|
}
|
|
|
|
// return which of the suit piles can add a card
|
|
CardPile * GameTable::suitAddPile(Card * card)
|
|
{
|
|
int suit;
|
|
|
|
for (suit = 0; suit < MAXSUITS; suit++)
|
|
if (suitPiles[Suits(suit)]->canTake(card))
|
|
return suitPiles[Suits(suit)];
|
|
(void) printf("suitAddPile\n");
|
|
return (CardPile *) NULL; // Hopefully we can not get here
|
|
}
|
|
|
|
// return which of the alternate piles can add a card
|
|
CardPile * GameTable::alternateAddPile(Card * card)
|
|
{
|
|
int pile;
|
|
|
|
for (pile = 0; pile < MAXPILES; pile++)
|
|
if (alternatePiles[pile]->canTake(card))
|
|
return alternatePiles[pile];
|
|
(void) printf("alternateAddPile\n");
|
|
return (CardPile *) NULL; // Hopefully we can not get here
|
|
}
|
|
|
|
/* Yes, it's an ugly mix of 'C' and 'C++' functions */
|
|
extern "C" { void init_solitare(ModeInfo * mi); }
|
|
extern "C" { void draw_solitare(ModeInfo * mi); }
|
|
extern "C" { void change_solitare(ModeInfo * mi); }
|
|
extern "C" { void release_solitare(ModeInfo * mi); }
|
|
extern "C" { void refresh_solitare(ModeInfo * mi); }
|
|
|
|
#ifndef DISABLE_INTERACTIVE
|
|
static XrmOptionDescRec opts[] =
|
|
{
|
|
{(char *) "-trackmouse", (char *) ".solitare.trackmouse", XrmoptionNoArg, (caddr_t) "on"},
|
|
{(char *) "+trackmouse", (char *) ".solitare.trackmouse", XrmoptionNoArg, (caddr_t) "off"}
|
|
};
|
|
|
|
static argtype vars[] =
|
|
{
|
|
{(void *) & trackmouse, (char *) "trackmouse", (char *) "TrackMouse", (char *) DEF_TRACKMOUSE, t_Bool}
|
|
};
|
|
|
|
static OptionStruct desc[] =
|
|
{
|
|
{(char *) "-/+trackmouse", (char *) "turn on/off the tracking of the mouse"}
|
|
};
|
|
|
|
ModeSpecOpt solitare_opts =
|
|
{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
|
|
#else
|
|
ModeSpecOpt solitare_opts =
|
|
{0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
|
|
#endif
|
|
|
|
#ifdef USE_MODULES
|
|
ModStruct solitare_description =
|
|
{(char *) "solitare", (char *) "init_solitare",
|
|
(char *) "draw_solitare", (char *) "release_solitare",
|
|
(char *) "refresh_solitare", (char *) "init_solitare",
|
|
(char *) NULL, &solitare_opts,
|
|
2000000, 1, 1, 1, 64, 1.0, (char *) "",
|
|
(char *) "Shows Klondike's game of solitare", 0, NULL};
|
|
#endif
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*-----------------------------------------------------------------------------
|
|
* Xlock hooks.
|
|
*-----------------------------------------------------------------------------
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* Initialize solitare. Called each time the window changes.
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
init_solitare(ModeInfo * mi)
|
|
{
|
|
solitarestruct *bp;
|
|
|
|
if (solitare == NULL) {
|
|
if ((solitare = (solitarestruct *) calloc(MI_NUM_SCREENS(mi),
|
|
sizeof(solitarestruct))) == NULL)
|
|
return;
|
|
}
|
|
bp = &solitare[MI_SCREEN(mi)];
|
|
|
|
MI_CLEARWINDOW(mi);
|
|
#ifdef DOFONT
|
|
Display *display = MI_DISPLAY(mi);
|
|
XGCValues gcv;
|
|
|
|
if (mode_font == None)
|
|
mode_font = getFont(display);
|
|
if (mode_font != None) {
|
|
gcv.font = mode_font->fid;
|
|
XSetFont(display, MI_GC(mi), mode_font->fid);
|
|
gcv.graphics_exposures = False;
|
|
gcv.foreground = MI_WHITE_PIXEL(mi);
|
|
gcv.background = MI_BLACK_PIXEL(mi);
|
|
if ((mp->gc = XCreateGC(display, MI_WINDOW(mi),
|
|
GCForeground | GCBackground | GCGraphicsExposures | GCFont,
|
|
&gcv)) == None) {
|
|
return;
|
|
}
|
|
mp->ascent = mode_font->ascent;
|
|
mp->height = font_height(mode_font);
|
|
for (i = 0; i < 256; i++)
|
|
char_width[i] = 8;
|
|
}
|
|
#endif
|
|
if (bp->game) {
|
|
if (bp->width != MI_WIDTH(mi) || bp->height != MI_HEIGHT(mi)) {
|
|
/* Let us not be creative then... */
|
|
bp->game->Resize(mi);
|
|
bp->painted = True;
|
|
refresh_solitare(mi);
|
|
return;
|
|
}
|
|
delete bp->game;
|
|
}
|
|
bp->painted = False;
|
|
if ((bp->game = new GameTable(mi)) == NULL) {
|
|
return;
|
|
}
|
|
if (!bp->game->newGame(mi)) {
|
|
delete bp->game;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* Called by the mainline code periodically to update the display.
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
void
|
|
draw_solitare(ModeInfo * mi)
|
|
{
|
|
solitarestruct *bp;
|
|
|
|
if (solitare == NULL)
|
|
return;
|
|
bp = &solitare[MI_SCREEN(mi)];
|
|
if (!bp->game)
|
|
return;
|
|
|
|
MI_IS_DRAWN(mi) = True;
|
|
bp->painted = True;
|
|
if (bp->showend) {
|
|
bp->showend++;
|
|
if (bp->showend >= 10)
|
|
init_solitare(mi);
|
|
} else if (trackmouse) {
|
|
if (!bp->game->HandleMouse(mi)) {
|
|
bp->showend++;
|
|
}
|
|
} else if (!bp->game->HandleGenerate()) {
|
|
bp->showend++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* The display is being taken away from us. Free up malloc'ed
|
|
* memory and X resources that we've alloc'ed. Only called
|
|
* once, we must zap everything for every screen.
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
release_solitare(ModeInfo * mi)
|
|
{
|
|
if (solitare != NULL) {
|
|
for (int screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
|
|
solitarestruct *bp = &solitare[screen];
|
|
|
|
if (bp->game)
|
|
delete bp->game;
|
|
}
|
|
free(solitare);
|
|
solitare = (solitarestruct *) NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
refresh_solitare(ModeInfo * mi)
|
|
{
|
|
solitarestruct *bp;
|
|
|
|
if (solitare == NULL)
|
|
return;
|
|
bp = &solitare[MI_SCREEN(mi)];
|
|
if (!bp->game)
|
|
return;
|
|
|
|
if (bp->painted) {
|
|
bp->game->Redraw(mi);
|
|
bp->painted = False;
|
|
}
|
|
}
|
|
|
|
#endif /* MODE_solitare */
|