mirror of
https://github.com/StevenMcLeod/Freecell68k.git
synced 2024-11-27 02:49:54 +00:00
Initial commit
This commit is contained in:
parent
8af048cee7
commit
4b383664bb
BIN
FreeCell..rsrc
Normal file
BIN
FreeCell..rsrc
Normal file
Binary file not shown.
26
bitset.h
Normal file
26
bitset.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// Created by Steven on 10-Dec-17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BITSET_H
|
||||||
|
#define BITSET_H
|
||||||
|
|
||||||
|
//Reference: https://graphics.stanford.edu/~seander/bithacks.html#ConditionalSetOrClearBitsWithoutBranching
|
||||||
|
|
||||||
|
#define EVAL_BOOL(n) ((n)!=0)
|
||||||
|
|
||||||
|
#define BITS_SET(val, bits) (val) |= (bits)
|
||||||
|
#define BITS_RESET(val, bits) (val) &= ~(bits)
|
||||||
|
#define BITS_FLIP(val, bits) (val) ^= (bits)
|
||||||
|
#define BITS_TEST(val, bits) ((val) & (bits))
|
||||||
|
#define BITS_TESTNOT(val, bits) (~(val) & (bits))
|
||||||
|
|
||||||
|
#define BITS_SET_BIT(val, bitpos) BITS_SET(val, 1 << (bitpos))
|
||||||
|
#define BITS_RESET_BIT(val, bitpos) BITS_RESET(val, 1 << (bitpos))
|
||||||
|
#define BITS_FLIP_BIT(val, bitpos) BITS_FLIP(val, 1 << (bitpos))
|
||||||
|
#define BITS_TEST_BIT(val, bitpos) EVAL_BOOL(BITS_TEST(val, 1 << (bitpos)))
|
||||||
|
#define BITS_TESTNOT_BIT(val, bitpos) EVAL_BOOL(!BITS_TESTNOT(val, 1 << (bitpos)))
|
||||||
|
#define BITS_COND_BIT(val, bitpos, bit) do { if(bit) BITS_SET(val, 1 << (bitpos)); else BITS_RESET(val, 1 << (bitpos)); } while(0)
|
||||||
|
|
||||||
|
|
||||||
|
#endif //BITSET_H
|
35
common.h
Normal file
35
common.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#ifndef COMMON_H
|
||||||
|
#define COMMON_H
|
||||||
|
|
||||||
|
typedef unsigned short ushort;
|
||||||
|
typedef unsigned long ulong;
|
||||||
|
|
||||||
|
struct PSTRUCT {
|
||||||
|
Byte len;
|
||||||
|
Byte str[];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ABS(x) ((x)>0?(x):-(x))
|
||||||
|
|
||||||
|
#define HIWORD(x) (((x) & 0xFFFF0000) >> 16)
|
||||||
|
#define LOWORD(x) ((x) & 0x0000FFFF)
|
||||||
|
|
||||||
|
#define TO_PSTRUCT(x) (* (struct PSTRUCT *) (x))
|
||||||
|
|
||||||
|
#define PLEN(x) (TO_PSTRUCT(x).len)
|
||||||
|
#define PSTR(x) (TO_PSTRUCT(x).str)
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.1415926535897932
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ITOC(n) ((n) + '0')
|
||||||
|
#define CTOI(n) ((n) - '0')
|
||||||
|
|
||||||
|
#define ARRLEN(a) (sizeof(a)/sizeof(*(a)))
|
||||||
|
|
||||||
|
#define L2PT(p,n) do { (p).v = HIWORD(n); (p).h = LOWORD(n); } while(0)
|
||||||
|
#define PT2L(p) (((long)(p).v << 16) | (long) (p).h)
|
||||||
|
#define TO_PTL(v,h) (((long)LOWORD(v) << 16) | (long) LOWORD(h))
|
||||||
|
|
||||||
|
#endif
|
265
freecell.c
Normal file
265
freecell.c
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
#include "freecell.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define FC_CEXPAND 2
|
||||||
|
|
||||||
|
static void SequenceDeck(Card *deck);
|
||||||
|
static void AddColumnCard(FCColumn **col, Card card);
|
||||||
|
static Card GetColumnCard(FCColumn *col);
|
||||||
|
static Card RemoveColumnCard(FCColumn *col);
|
||||||
|
static FCColumn *ExpandColumnSize(FCColumn *old, Size ptrSize);
|
||||||
|
|
||||||
|
static Boolean AlternatingColours(Card a, Card b);
|
||||||
|
|
||||||
|
void FreecellInit(FCState *state) {
|
||||||
|
memset(state, 0, sizeof(*state));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreecellStartGame(FCState *state, ushort seed) {
|
||||||
|
state->moves = 0;
|
||||||
|
state->seedno = seed;
|
||||||
|
state->cols = FreecellShuffle(seed, state->cols);
|
||||||
|
memset(state->store, 0, sizeof(state->store));
|
||||||
|
memset(state->foundation, 0, sizeof(state->foundation));
|
||||||
|
}
|
||||||
|
|
||||||
|
FCColumn **FreecellShuffle(ushort seed, FCColumn **deal) {
|
||||||
|
ulong state = seed;
|
||||||
|
ushort i, j;
|
||||||
|
Card deck[CARD_QTY];
|
||||||
|
|
||||||
|
/* Allocate deal memory */
|
||||||
|
if(!deal) {
|
||||||
|
deal = (FCColumn **) NewPtr(FC_COLS * sizeof(FCColumn *));
|
||||||
|
if(!deal) return NULL;
|
||||||
|
for(i = 0; i < FC_COLS; ++i) {
|
||||||
|
deal[i] = (FCColumn *) NewPtr(SIZEOF_FCCOLUMN(FC_DCSIZE));
|
||||||
|
if(!deal[i]) goto err_inner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SequenceDeck(deck);
|
||||||
|
|
||||||
|
/* Freecell deal algorithm */
|
||||||
|
for(i = 0; i < CARD_QTY - 1; ++i) {
|
||||||
|
ushort cidx;
|
||||||
|
Card toswap;
|
||||||
|
|
||||||
|
state = (state * 214013L + 2531011L) & 0x7FFFFFFFL;
|
||||||
|
cidx = CARD_QTY - 1 - ((state >> 16) % (CARD_QTY - i));
|
||||||
|
|
||||||
|
toswap = deck[cidx];
|
||||||
|
deck[cidx] = deck[i];
|
||||||
|
deck[i] = toswap;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < FC_COLS; ++i) {
|
||||||
|
for(j = 0; 8*j + i < CARD_QTY; ++j) {
|
||||||
|
deal[i]->cards[j] = deck[8*j + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
deal[i]->qty = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
return deal;
|
||||||
|
|
||||||
|
err_inner:
|
||||||
|
/* Inner deal array error */
|
||||||
|
while(i--) {
|
||||||
|
DisposPtr(deal[i]);
|
||||||
|
}
|
||||||
|
DisposPtr(deal);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreecellDisposeDeal(FCColumn **deal) {
|
||||||
|
ushort i;
|
||||||
|
|
||||||
|
if(deal) {
|
||||||
|
for(i = 0; i < FC_COLS; ++i) {
|
||||||
|
DisposPtr(deal[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
DisposPtr(deal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Legal move requirements:
|
||||||
|
1: If to is a LastCard, then val(to) == NULL OR
|
||||||
|
val(to) - 1 == val(from) and to, from have alternating colours.
|
||||||
|
2: If to is a storage, then val(to) == CARD_NULL
|
||||||
|
3: If to is a foundation, then val(to) + 1 == val(from) OR
|
||||||
|
val(to) == NULL and val(from) == 1 and have the same suit.
|
||||||
|
*/
|
||||||
|
Boolean FreecellLegalMove(FCState *state, FCZone from, FCZone to) {
|
||||||
|
Card cardFrom = GetCardAt(state, from);
|
||||||
|
Card cardTo = GetCardAt(state, to);
|
||||||
|
|
||||||
|
if(CARD_EMPTY(cardFrom))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch(to.zone) {
|
||||||
|
case FCZ_LASTCARD:
|
||||||
|
return CARD_EMPTY(cardTo)
|
||||||
|
|| (CARD_GETNUM(cardTo) - 1 == CARD_GETNUM(cardFrom)
|
||||||
|
&& AlternatingColours(cardTo, cardFrom));
|
||||||
|
|
||||||
|
case FCZ_COLUMN:
|
||||||
|
/* Can refer to empty columns as either */
|
||||||
|
return CARD_EMPTY(cardTo) && CARD_GETNUM(cardFrom) == 13;
|
||||||
|
|
||||||
|
case FCZ_STORAGE:
|
||||||
|
return CARD_EMPTY(cardTo);
|
||||||
|
|
||||||
|
case FCZ_FOUNDATION:
|
||||||
|
return (CARD_EMPTY(cardTo) && CARD_GETNUM(cardFrom) == 1)
|
||||||
|
|| (CARD_GETNUM(cardTo) + 1 == CARD_GETNUM(cardFrom)
|
||||||
|
&& CARD_GETSUIT(cardTo) == CARD_GETSUIT(cardFrom));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Boolean FreecellPlayMove(FCState *state, FCZone from, FCZone to) {
|
||||||
|
if(!FreecellLegalMove(state, from, to))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
FreecellForceMove(state, from, to);
|
||||||
|
++state->moves;
|
||||||
|
state->lastMove[0] = from;
|
||||||
|
state->lastMove[1] = to;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreecellForceMove(FCState *state, FCZone from, FCZone to) {
|
||||||
|
Card moveCard;
|
||||||
|
|
||||||
|
switch(from.zone) {
|
||||||
|
case FCZ_LASTCARD:
|
||||||
|
moveCard = RemoveColumnCard(state->cols[from.num]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FCZ_STORAGE:
|
||||||
|
moveCard = state->store[from.num];
|
||||||
|
state->store[from.num] = CARD_NULL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FCZ_FOUNDATION:
|
||||||
|
moveCard = state->foundation[from.num];
|
||||||
|
if(CARD_GETNUM(moveCard) == 1) {
|
||||||
|
state->foundation[from.num] = CARD_NULL;
|
||||||
|
} else {
|
||||||
|
ushort num = CARD_GETNUM(moveCard) - 1;
|
||||||
|
CARD_SETNUM(state->foundation[from.num], num);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(to.zone) {
|
||||||
|
case FCZ_LASTCARD:
|
||||||
|
AddColumnCard(&state->cols[to.num], moveCard);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FCZ_STORAGE:
|
||||||
|
state->store[to.num] = moveCard;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FCZ_FOUNDATION:
|
||||||
|
state->foundation[to.num] = moveCard;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreecellUndoMove(FCState *state) {
|
||||||
|
FCZone tmp;
|
||||||
|
|
||||||
|
if(state->lastMove[0].zone == FCZ_NONE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FreecellForceMove(state, state->lastMove[1], state->lastMove[0]);
|
||||||
|
--state->moves;
|
||||||
|
tmp = state->lastMove[0];
|
||||||
|
state->lastMove[0] = state->lastMove[1];
|
||||||
|
state->lastMove[1] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SequenceDeck(Card *deck) {
|
||||||
|
ushort i;
|
||||||
|
deck[0] = TO_CARD(0, 3, 13);
|
||||||
|
for(i = 1; i < CARD_QTY; ++i) {
|
||||||
|
if(CARD_GETSUIT(deck[i-1]) == 0)
|
||||||
|
deck[i] = TO_CARD(0, 3, CARD_GETNUM(deck[i-1]) - 1);
|
||||||
|
else
|
||||||
|
deck[i] = deck[i-1] - TO_CARD(0, 1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddColumnCard(FCColumn **col, Card card) {
|
||||||
|
FCColumn *workCol = *col;
|
||||||
|
Size ptrSize, reqSize;
|
||||||
|
|
||||||
|
if(workCol->qty >= FC_DCSIZE) {
|
||||||
|
ptrSize = GetPtrSize(workCol);
|
||||||
|
reqSize = SIZEOF_FCCOLUMN(workCol->qty+1);
|
||||||
|
if(reqSize > ptrSize) {
|
||||||
|
*col = ExpandColumnSize(workCol, ptrSize);
|
||||||
|
workCol = *col;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
workCol->cards[workCol->qty] = card;
|
||||||
|
++workCol->qty;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Card GetColumnCard(FCColumn *col) {
|
||||||
|
if(col->qty == 0)
|
||||||
|
return CARD_NULL;
|
||||||
|
|
||||||
|
return col->cards[col->qty-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Card RemoveColumnCard(FCColumn *col) {
|
||||||
|
Card card;
|
||||||
|
|
||||||
|
if(col->qty == 0)
|
||||||
|
return CARD_NULL;
|
||||||
|
|
||||||
|
--col->qty;
|
||||||
|
card = col->cards[col->qty];
|
||||||
|
col->cards[col->qty] = CARD_NULL;
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
FCColumn *ExpandColumnSize(FCColumn *old, Size ptrSize) {
|
||||||
|
FCColumn *new = (FCColumn *) NewPtr(ptrSize + FC_CEXPAND * sizeof(Card));
|
||||||
|
memcpy(new, old, ptrSize);
|
||||||
|
return new;
|
||||||
|
}
|
||||||
|
|
||||||
|
Card GetCardAt(FCState *state, FCZone elem) {
|
||||||
|
switch(elem.zone) {
|
||||||
|
case FCZ_LASTCARD:
|
||||||
|
case FCZ_COLUMN:
|
||||||
|
return GetColumnCard(state->cols[elem.num]);
|
||||||
|
|
||||||
|
case FCZ_STORAGE:
|
||||||
|
return state->store[elem.num];
|
||||||
|
|
||||||
|
case FCZ_FOUNDATION:
|
||||||
|
return state->foundation[elem.num];
|
||||||
|
}
|
||||||
|
|
||||||
|
return CARD_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Boolean AlternatingColours(Card a, Card b) {
|
||||||
|
if(CARD_EMPTY(a) || CARD_EMPTY(b))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(CARD_GETSUIT(a) == C_DIAMOND || CARD_GETSUIT(a) == C_HEART) {
|
||||||
|
return CARD_GETSUIT(b) == C_CLUB || CARD_GETSUIT(b) == C_SPADE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CARD_GETSUIT(b) == C_DIAMOND || CARD_GETSUIT(b) == C_HEART;
|
||||||
|
}
|
63
freecell.h
Normal file
63
freecell.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#ifndef FREECELL_H
|
||||||
|
#define FREECELL_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "gametypes.h"
|
||||||
|
|
||||||
|
#define FC_STORES 4
|
||||||
|
#define FC_COLS 8
|
||||||
|
#define FC_DCSIZE 13
|
||||||
|
|
||||||
|
/* Seed values [1, 32001) */
|
||||||
|
#define FC_SEEDLO 1
|
||||||
|
#define FC_SEEDHI 32001
|
||||||
|
|
||||||
|
/* GameElem Structs */
|
||||||
|
enum {
|
||||||
|
FCZ_NONE=0,
|
||||||
|
FCZ_LASTCARD,
|
||||||
|
FCZ_COLUMN,
|
||||||
|
FCZ_STORAGE,
|
||||||
|
FCZ_FOUNDATION
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Byte zone;
|
||||||
|
Byte num;
|
||||||
|
} FCZone;
|
||||||
|
|
||||||
|
#define FCZONE_EQ(a, b) (\
|
||||||
|
((a).zone == (b).zone) \
|
||||||
|
&& ((a).num == (b).num) \
|
||||||
|
)
|
||||||
|
|
||||||
|
#define SIZEOF_FCCOLUMN(qty) (sizeof(FCColumn) + (qty) * sizeof(Card))
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ushort qty;
|
||||||
|
Card cards[];
|
||||||
|
} FCColumn;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ushort moves;
|
||||||
|
ushort seedno;
|
||||||
|
FCColumn **cols; /* Size FC_COLS */
|
||||||
|
Card store[FC_STORES];
|
||||||
|
Card foundation[4];
|
||||||
|
FCZone lastMove[2];
|
||||||
|
} FCState;
|
||||||
|
|
||||||
|
/* Game Setup */
|
||||||
|
void FreecellInit(FCState *state);
|
||||||
|
void FreecellStartGame(FCState *state, ushort seed);
|
||||||
|
FCColumn **FreecellShuffle(ushort seed, FCColumn **deal);
|
||||||
|
void FreecellDisposeDeal(FCColumn **deal);
|
||||||
|
|
||||||
|
/* Game Play */
|
||||||
|
Card GetCardAt(FCState *state, FCZone elem);
|
||||||
|
Boolean FreecellLegalMove(FCState *state, FCZone from, FCZone to);
|
||||||
|
Boolean FreecellPlayMove(FCState *state, FCZone from, FCZone to);
|
||||||
|
void FreecellForceMove(FCState *state, FCZone from, FCZone to);
|
||||||
|
void FreecellUndoMove(FCState *state);
|
||||||
|
|
||||||
|
#endif /* FREECELL_H */
|
245
gameintf.c
Normal file
245
gameintf.c
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
#include "gameintf.h"
|
||||||
|
#include "gamewind.h"
|
||||||
|
#include "gamewindlow.h"
|
||||||
|
#include "gamemenu.h"
|
||||||
|
#include "freecell.h"
|
||||||
|
|
||||||
|
/* HandleGameClick, DragActionProc */
|
||||||
|
static FCZone startHoverElem;
|
||||||
|
static FCZone lastHoverElem;
|
||||||
|
static Boolean lastHoverLegal;
|
||||||
|
|
||||||
|
static pascal void DragActionProc(void);
|
||||||
|
|
||||||
|
static short RndRange(short lower, short upper);
|
||||||
|
|
||||||
|
FCClickErr HandleGameClick(FCState *state, Point hitPt) {
|
||||||
|
FCZone hitElem, dragElem;
|
||||||
|
Point dragPt;
|
||||||
|
long theLPoint;
|
||||||
|
Rect boundsRect = {-3, -3, 3, 3};
|
||||||
|
Rect cardRect;
|
||||||
|
RgnHandle cardRgn;
|
||||||
|
FCClickErr theErr;
|
||||||
|
|
||||||
|
hitElem = GetGamePtLoc(state, hitPt, &cardRect);
|
||||||
|
if(hitElem.zone == FCZ_NONE
|
||||||
|
|| CARD_EMPTY(GetCardAt(state, hitElem))) {
|
||||||
|
theErr = FCCE_BADHIT;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Drag has slack to differentiate from double-click */
|
||||||
|
dragPt = hitPt;
|
||||||
|
OffsetRect(&boundsRect, dragPt.h, dragPt.v);
|
||||||
|
while(PtInRect(dragPt, &boundsRect) && StillDown()) {
|
||||||
|
GetMouse(&dragPt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!StillDown()) {
|
||||||
|
theErr = FCCE_NODRAG;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rect now starts in a different location */
|
||||||
|
OffsetRect(&cardRect, dragPt.h - hitPt.h, dragPt.v - hitPt.v);
|
||||||
|
cardRgn = NewRgn();
|
||||||
|
OpenRgn();
|
||||||
|
FrameRoundRect(&cardRect, CARD_XRATIO, CARD_YRATIO);
|
||||||
|
CloseRgn(cardRgn);
|
||||||
|
|
||||||
|
/* Create drag outline */
|
||||||
|
boundsRect = FrontWindow()->portRect;
|
||||||
|
startHoverElem = hitElem;
|
||||||
|
lastHoverElem.zone = FCZ_NONE;
|
||||||
|
lastHoverLegal = false;
|
||||||
|
theLPoint = DragGrayRgn(cardRgn, dragPt, &boundsRect,
|
||||||
|
&boundsRect, noConstraint, &DragActionProc);
|
||||||
|
if(BAD_PTL(theLPoint)) {
|
||||||
|
theErr = FCCE_OOBDRAG;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
dragPt.v += HIWORD(theLPoint);
|
||||||
|
dragPt.h += LOWORD(theLPoint);
|
||||||
|
dragElem = GetGamePtLoc(state, dragPt, &cardRect);
|
||||||
|
if(dragElem.zone == FCZ_NONE) {
|
||||||
|
theErr = FCCE_BADDRAG;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!lastHoverLegal) {
|
||||||
|
theErr = FCCE_BADMOVE;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
InvertRoundRect(&cardRect, CARD_XRATIO, CARD_YRATIO);
|
||||||
|
/* Play move */
|
||||||
|
if(!FreecellPlayMove(state, hitElem, dragElem)) {
|
||||||
|
theErr = FCCE_BADMOVE;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameDrawMove(state, hitElem, dragElem);
|
||||||
|
return FCCE_OK;
|
||||||
|
|
||||||
|
err:
|
||||||
|
/* In case other deinit needs to occur */
|
||||||
|
return theErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
pascal void DragActionProc(void) {
|
||||||
|
Point mousePoint;
|
||||||
|
FCZone hoverElem;
|
||||||
|
Rect hoverRect, unhoverRect;
|
||||||
|
FCState *state;
|
||||||
|
|
||||||
|
/* Invert spaces that are able to be placed */
|
||||||
|
GetMouse(&mousePoint);
|
||||||
|
state = (FCState *) GetWRefCon(FrontWindow());
|
||||||
|
hoverElem = GetGamePtLoc(state, mousePoint, &hoverRect);
|
||||||
|
if(FCZONE_EQ(hoverElem, lastHoverElem))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(lastHoverLegal) {
|
||||||
|
GetFCZoneRect(state, lastHoverElem, &unhoverRect);
|
||||||
|
InvertRoundRect(&unhoverRect, CARD_XRATIO, CARD_YRATIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastHoverElem = hoverElem;
|
||||||
|
lastHoverLegal = FreecellLegalMove(state, startHoverElem, hoverElem);
|
||||||
|
if(lastHoverLegal) {
|
||||||
|
InvertRoundRect(&hoverRect, CARD_XRATIO, CARD_YRATIO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameDrawMove(FCState *state, FCZone from, FCZone to) {
|
||||||
|
Card replaceCard = GetCardAt(state, from);
|
||||||
|
Card moveCard = GetCardAt(state, to);
|
||||||
|
|
||||||
|
switch(from.zone) {
|
||||||
|
case FCZ_LASTCARD:
|
||||||
|
DrawRemovedColCard(state->cols[from.num], from.num);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FCZ_STORAGE:
|
||||||
|
DrawStorageCard(replaceCard, from.num);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FCZ_FOUNDATION:
|
||||||
|
DrawFoundationCard(replaceCard, from.num);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(to.zone) {
|
||||||
|
case FCZ_LASTCARD:
|
||||||
|
DrawLastColumnCard(state->cols[to.num], to.num);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FCZ_STORAGE:
|
||||||
|
DrawStorageCard(moveCard, to.num);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FCZ_FOUNDATION:
|
||||||
|
DrawFoundationCard(moveCard, to.num);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(state->moves <= 1) {
|
||||||
|
MenuUndoState(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameNewGame(FCState *state, ushort seed) {
|
||||||
|
Boolean needTitleUpdate;
|
||||||
|
if(!seed) {
|
||||||
|
seed = RndRange(FC_SEEDLO, FC_SEEDHI);
|
||||||
|
}
|
||||||
|
|
||||||
|
needTitleUpdate = seed != state->seedno;
|
||||||
|
FreecellStartGame(state, seed);
|
||||||
|
if(needTitleUpdate) {
|
||||||
|
WindUpdateTitle(FrontWindow());
|
||||||
|
}
|
||||||
|
MenuUndoState(false);
|
||||||
|
ForceRedraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* To optimize */
|
||||||
|
/* elemRect can be NULL if not required */
|
||||||
|
FCZone GetGamePtLoc(FCState *state, Point pt, Rect *elemRect) {
|
||||||
|
FCZone res;
|
||||||
|
Rect boundsRect;
|
||||||
|
ushort i;
|
||||||
|
|
||||||
|
for(i = 0; i < FC_STORES + 4; ++i) {
|
||||||
|
GetStoreRect(i, &boundsRect);
|
||||||
|
if(PtInRect(pt, &boundsRect)) {
|
||||||
|
res.zone = (i >= FC_STORES) ? FCZ_FOUNDATION : FCZ_STORAGE;
|
||||||
|
res.num = (i >= FC_STORES) ? i - FC_STORES : i;
|
||||||
|
if(elemRect) *elemRect = boundsRect;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < FC_COLS; ++i) {
|
||||||
|
GetColumnRect(i, state->cols[i]->qty, &boundsRect);
|
||||||
|
if(PtInRect(pt, &boundsRect)) {
|
||||||
|
if(elemRect) *elemRect = boundsRect;
|
||||||
|
GetLastStackedRect(i, state->cols[i]->qty, &boundsRect);
|
||||||
|
if(PtInRect(pt, &boundsRect)) {
|
||||||
|
res.zone = FCZ_LASTCARD;
|
||||||
|
if(elemRect) *elemRect = boundsRect;
|
||||||
|
} else {
|
||||||
|
res.zone = FCZ_COLUMN;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.num = i;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.zone = FCZ_NONE;
|
||||||
|
if(elemRect)
|
||||||
|
SetRect(elemRect, 0, 0, 0, 0);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetFCZoneRect(FCState *state, FCZone elem, Rect *elemRect) {
|
||||||
|
switch(elem.zone) {
|
||||||
|
case FCZ_LASTCARD: {
|
||||||
|
FCColumn *theColumn = state->cols[elem.num];
|
||||||
|
GetLastStackedRect(elem.num, theColumn->qty, elemRect);
|
||||||
|
} break;
|
||||||
|
case FCZ_COLUMN: {
|
||||||
|
FCColumn *theColumn = state->cols[elem.num];
|
||||||
|
GetColumnRect(elem.num, theColumn->qty, elemRect);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case FCZ_STORAGE:
|
||||||
|
GetStoreRect(elem.num, elemRect);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FCZ_FOUNDATION:
|
||||||
|
GetStoreRect(elem.num + FC_STORES, elemRect);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
SetRect(elemRect, 0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If upper <= lower then result undefined. */
|
||||||
|
short RndRange(short lower, short upper) {
|
||||||
|
asm {
|
||||||
|
CLR.W -(sp) ; d0 = Random()
|
||||||
|
_Random
|
||||||
|
CLR.L d0
|
||||||
|
MOVE.W (sp)+, d0
|
||||||
|
MOVE.W upper, d1 ; d1 = upper - lower
|
||||||
|
SUB.W lower, d1
|
||||||
|
DIVU.W d1, d0 ; d0 = d0 % d1
|
||||||
|
SWAP d0
|
||||||
|
ADD.W lower, d0
|
||||||
|
}
|
||||||
|
}
|
26
gameintf.h
Normal file
26
gameintf.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef GAMEINTF_H
|
||||||
|
#define GAMEINTF_H
|
||||||
|
|
||||||
|
#include "freecell.h"
|
||||||
|
|
||||||
|
#define GAME_RANDOMSEED 0
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FCCE_OK=0,
|
||||||
|
FCCE_BADHIT,
|
||||||
|
FCCE_NODRAG,
|
||||||
|
FCCE_OOBDRAG,
|
||||||
|
FCCE_BADDRAG,
|
||||||
|
FCCE_BADMOVE
|
||||||
|
} FCClickErr;
|
||||||
|
|
||||||
|
/* Mouse and Gameplay Handlers */
|
||||||
|
FCClickErr HandleGameClick(FCState *state, Point hitPt);
|
||||||
|
void GameDrawMove(FCState *state, FCZone from, FCZone to);
|
||||||
|
|
||||||
|
void GameNewGame(FCState *state, ushort seed);
|
||||||
|
|
||||||
|
FCZone GetGamePtLoc(FCState *state, Point pt, Rect *elemRect);
|
||||||
|
void GetFCZoneRect(FCState *state, FCZone elem, Rect *elemRect);
|
||||||
|
|
||||||
|
#endif /* GAMEINTF_H */
|
131
gamemenu.c
Normal file
131
gamemenu.c
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
#include "gamemenu.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include "gamewind.h"
|
||||||
|
#include "gamestate.h"
|
||||||
|
#include "gameintf.h"
|
||||||
|
|
||||||
|
#define MBAR_ID 128
|
||||||
|
|
||||||
|
enum {
|
||||||
|
appleID=128,
|
||||||
|
fileID,
|
||||||
|
editID
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
apple_aboutID=1
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
file_newID=1,
|
||||||
|
file_openID,
|
||||||
|
file_restartID,
|
||||||
|
file_quitID=5
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
edit_undoID=1,
|
||||||
|
edit_cutID=3,
|
||||||
|
edit_copyID,
|
||||||
|
edit_pasteID,
|
||||||
|
edit_clearID
|
||||||
|
};
|
||||||
|
|
||||||
|
static void DoAppleMenu(short item);
|
||||||
|
static void DoFileMenu(short item);
|
||||||
|
static void DoEditMenu(short item);
|
||||||
|
|
||||||
|
void MenuCreate(void) {
|
||||||
|
Handle mHandle;
|
||||||
|
mHandle = GetNewMBar(MBAR_ID);
|
||||||
|
SetMenuBar(mHandle);
|
||||||
|
DrawMenuBar();
|
||||||
|
|
||||||
|
mHandle = (Handle) GetMHandle(appleID);
|
||||||
|
AddResMenu((MenuHandle) mHandle, 'DRVR');
|
||||||
|
}
|
||||||
|
|
||||||
|
void MenuEvent(long menuitem) {
|
||||||
|
short menuID = HIWORD(menuitem);
|
||||||
|
short itemID = LOWORD(menuitem);
|
||||||
|
|
||||||
|
switch(menuID) {
|
||||||
|
case appleID: DoAppleMenu(itemID); break;
|
||||||
|
case fileID: DoFileMenu(itemID); break;
|
||||||
|
case editID: DoEditMenu(itemID); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MenuEditState(Boolean active) {
|
||||||
|
MenuHandle theMenu = GetMHandle(editID);
|
||||||
|
if(active) {
|
||||||
|
EnableItem(theMenu, edit_cutID);
|
||||||
|
EnableItem(theMenu, edit_copyID);
|
||||||
|
EnableItem(theMenu, edit_pasteID);
|
||||||
|
EnableItem(theMenu, edit_clearID);
|
||||||
|
} else {
|
||||||
|
DisableItem(theMenu, edit_cutID);
|
||||||
|
DisableItem(theMenu, edit_copyID);
|
||||||
|
DisableItem(theMenu, edit_pasteID);
|
||||||
|
DisableItem(theMenu, edit_clearID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MenuUndoState(Boolean active) {
|
||||||
|
MenuHandle theMenu = GetMHandle(editID);
|
||||||
|
if(active) {
|
||||||
|
EnableItem(theMenu, edit_undoID);
|
||||||
|
} else {
|
||||||
|
DisableItem(theMenu, edit_undoID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoAppleMenu(short item) {
|
||||||
|
GrafPtr *oldPort;
|
||||||
|
MenuHandle theMenu = GetMHandle(appleID);
|
||||||
|
|
||||||
|
if(item > apple_aboutID) {
|
||||||
|
StringPtr name = (StringPtr) NewPtr(sizeof(Str255));
|
||||||
|
GetPort(&oldPort);
|
||||||
|
GetItem(theMenu, item, name);
|
||||||
|
OpenDeskAcc(name);
|
||||||
|
SetPort(oldPort);
|
||||||
|
DisposPtr(name);
|
||||||
|
} else {
|
||||||
|
DlogAbout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoFileMenu(short item) {
|
||||||
|
MenuHandle theMenu = GetMHandle(fileID);
|
||||||
|
switch(item) {
|
||||||
|
case file_newID:
|
||||||
|
GameNewGame(&gstate.fcGame, GAME_RANDOMSEED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case file_openID: {
|
||||||
|
ushort openSeed = DlogOpenGame();
|
||||||
|
if(openSeed != -1) {
|
||||||
|
GameNewGame(&gstate.fcGame, openSeed);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case file_restartID:
|
||||||
|
GameNewGame(&gstate.fcGame, GAME_RANDOMSEED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case file_quitID:
|
||||||
|
gstate.running = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoEditMenu(short item) {
|
||||||
|
switch(item) {
|
||||||
|
case edit_undoID:
|
||||||
|
FreecellUndoMove(&gstate.fcGame);
|
||||||
|
GameDrawMove(&gstate.fcGame, gstate.fcGame.lastMove[0],
|
||||||
|
gstate.fcGame.lastMove[1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
9
gamemenu.h
Normal file
9
gamemenu.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#ifndef GAMEMENU_H
|
||||||
|
#define GAMEMENU_H
|
||||||
|
|
||||||
|
void MenuCreate(void);
|
||||||
|
void MenuEvent(long menuitem);
|
||||||
|
void MenuEditState(Boolean active);
|
||||||
|
void MenuUndoState(Boolean active);
|
||||||
|
|
||||||
|
#endif /* GAMEMENU_H */
|
13
gamestate.h
Normal file
13
gamestate.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#ifndef GAMESTATE_H
|
||||||
|
#define GAMESTATE_H
|
||||||
|
|
||||||
|
#include "freecell.h"
|
||||||
|
|
||||||
|
struct GlobalState {
|
||||||
|
FCState fcGame;
|
||||||
|
Boolean running;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct GlobalState gstate;
|
||||||
|
|
||||||
|
#endif /* GAMESTATE_H */
|
31
gametypes.h
Normal file
31
gametypes.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef GAMETYPES_H
|
||||||
|
#define GAMETYPES_H
|
||||||
|
|
||||||
|
/* Card Type: ffssnnnn. High order can be used for flags. */
|
||||||
|
#define CARD_FMASK 0xC0
|
||||||
|
#define CARD_SMASK 0x30
|
||||||
|
#define CARD_NMASK 0x0F
|
||||||
|
|
||||||
|
#define CARD_GETFLAGS(c) ((Byte) ((c & CARD_FMASK) >> 6))
|
||||||
|
#define CARD_GETSUIT(c) ((Suit) ((c & CARD_SMASK) >> 4))
|
||||||
|
#define CARD_GETNUM(c) ((short) (c & CARD_NMASK))
|
||||||
|
#define CARD_SETFLAGS(c, f) ((c) = ((c & ~CARD_FMASK) | ((f) << 6)))
|
||||||
|
#define CARD_SETSUIT(c, s) ((c) = ((c & ~CARD_SMASK) | ((s) << 4)))
|
||||||
|
#define CARD_SETNUM(c, n) ((c) = ((c & ~CARD_NMASK) | (n)))
|
||||||
|
#define TO_CARD(f, s, n) ((Card) (((f) << 6) | ((s) << 4) | (n)))
|
||||||
|
|
||||||
|
#define CARD_NULL ((Card) 0)
|
||||||
|
#define CARD_EMPTY(c) (CARD_GETNUM(c) == 0)
|
||||||
|
#define CARD_QTY 52
|
||||||
|
|
||||||
|
typedef Byte Card;
|
||||||
|
|
||||||
|
typedef enum Suit {
|
||||||
|
C_CLUB=0,
|
||||||
|
C_DIAMOND,
|
||||||
|
C_HEART,
|
||||||
|
C_SPADE
|
||||||
|
} Suit;
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* GAMETYPES_H */
|
344
gamewind.c
Normal file
344
gamewind.c
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
#include "gamewind.h"
|
||||||
|
#include "gamewindlow.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include "pstring.h"
|
||||||
|
#include "strntol.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#define TITLE_PREF "\pFreeCell - #"
|
||||||
|
#define DLOG_OPEN 128
|
||||||
|
#define DLOG_OPEN_INPUT 3
|
||||||
|
#define DLOG_ABOUT 129
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
short dlgMaxIndex;
|
||||||
|
Handle itmHndl;
|
||||||
|
Rect itmRect;
|
||||||
|
short itmType;
|
||||||
|
Byte itmData[];
|
||||||
|
} DialogItemList;
|
||||||
|
|
||||||
|
static pascal Boolean DigitInputFilter(DialogPtr theDialog,
|
||||||
|
EventRecord *theEvent, short *itemHit);
|
||||||
|
static pascal Boolean AboutFilter(DialogPtr theDialog,
|
||||||
|
EventRecord *theEvent, int *itemHit);
|
||||||
|
|
||||||
|
WindowPtr WindCreateTestEnv(Rect *bounds, StringPtr title) {
|
||||||
|
WindowPtr theWind;
|
||||||
|
Rect windRect;
|
||||||
|
|
||||||
|
title = title ? title : "\pTest Area";
|
||||||
|
theWind = NewWindow(0L, bounds, title, true,
|
||||||
|
noGrowDocProc, (WindowPtr) -1L, true, 0);
|
||||||
|
|
||||||
|
return theWind;
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowPtr WindCreateGame(FCState *state) {
|
||||||
|
WindowPtr theWind;
|
||||||
|
Rect boundsRect;
|
||||||
|
Rect windRect;
|
||||||
|
StringPtr titleString;
|
||||||
|
unsigned char gameNumBuf[6];
|
||||||
|
Pattern bkpat;
|
||||||
|
/*
|
||||||
|
titleString = (StringPtr) NewPtr(sizeof(Str255));
|
||||||
|
if(!titleString) return NULL;
|
||||||
|
memcpy(titleString, TITLE_PREF, sizeof(TITLE_PREF));
|
||||||
|
NumToString(state->seedno, gameNumBuf);
|
||||||
|
strcat_p(titleString, gameNumBuf);*/
|
||||||
|
|
||||||
|
SetRect(&windRect, 0, 0, WIND_XLENGTH, WIND_YLENGTH);
|
||||||
|
boundsRect = screenBits.bounds;
|
||||||
|
boundsRect.top += 20;
|
||||||
|
CentreRect(&windRect, &boundsRect);
|
||||||
|
theWind = NewWindow(0L, &windRect, "\p", true, noGrowDocProc,
|
||||||
|
(WindowPtr) -1L, true, 0);
|
||||||
|
/*DisposPtr(titleString);*/
|
||||||
|
if(theWind) {
|
||||||
|
SetPort(theWind);
|
||||||
|
GetIndPattern(&bkpat, sysPatListID, 21);
|
||||||
|
BackPat(bkpat);
|
||||||
|
SetWRefCon(theWind, (long) state);
|
||||||
|
}
|
||||||
|
return theWind;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindUpdateTitle(WindowPtr theWind) {
|
||||||
|
StringPtr titleString;
|
||||||
|
unsigned char gameNumBuf[6];
|
||||||
|
FCState *state = (FCState *) GetWRefCon(theWind);
|
||||||
|
|
||||||
|
titleString = (StringPtr) NewPtr(sizeof(Str255));
|
||||||
|
if(!titleString) return;
|
||||||
|
memcpy(titleString, TITLE_PREF, sizeof(TITLE_PREF));
|
||||||
|
NumToString(state->seedno, gameNumBuf);
|
||||||
|
strcat_p(titleString, gameNumBuf);
|
||||||
|
SetWTitle(theWind, titleString);
|
||||||
|
DisposPtr(titleString);
|
||||||
|
}
|
||||||
|
|
||||||
|
ushort DlogOpenGame(void) {
|
||||||
|
DialogPtr theDialog;
|
||||||
|
Rect dlogRect, boundsRect;
|
||||||
|
short itemNo;
|
||||||
|
Handle textboxHandle;
|
||||||
|
volatile Size textboxHndlSize;
|
||||||
|
long inputText;
|
||||||
|
|
||||||
|
theDialog = GetNewDialog(DLOG_OPEN, NULL, (WindowPtr) -1);
|
||||||
|
if(!theDialog) return -1;
|
||||||
|
dlogRect = theDialog->portRect;
|
||||||
|
boundsRect = screenBits.bounds;
|
||||||
|
boundsRect.top += 20;
|
||||||
|
CentreRect(&dlogRect, &boundsRect);
|
||||||
|
MoveWindow(theDialog, dlogRect.left, dlogRect.top, true);
|
||||||
|
ShowWindow(theDialog);
|
||||||
|
|
||||||
|
do {
|
||||||
|
do {
|
||||||
|
ModalDialog(&DigitInputFilter, &itemNo);
|
||||||
|
if(itemNo == cancel) {
|
||||||
|
inputText = -1;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
} while(itemNo != ok);
|
||||||
|
|
||||||
|
GetDItem(theDialog, DLOG_OPEN_INPUT, &itemNo,
|
||||||
|
&textboxHandle, &boundsRect);
|
||||||
|
textboxHndlSize = GetHandleSize(textboxHandle);
|
||||||
|
/* Ensure whitespace before / after string */
|
||||||
|
HLock(textboxHandle);
|
||||||
|
inputText = strntol(*textboxHandle, textboxHndlSize, NULL, 10);
|
||||||
|
HUnlock(textboxHandle);
|
||||||
|
if(inputText < 1 || inputText > 32000) {
|
||||||
|
SysBeep(1);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while(true);
|
||||||
|
|
||||||
|
err:
|
||||||
|
DisposDialog(theDialog);
|
||||||
|
return (ushort) inputText;
|
||||||
|
}
|
||||||
|
|
||||||
|
pascal Boolean DigitInputFilter(DialogPtr theDialog, EventRecord *theEvent,
|
||||||
|
short *itemHit) {
|
||||||
|
char theChar;
|
||||||
|
|
||||||
|
if(theEvent->what == keyDown || theEvent->what == autoKey) {
|
||||||
|
theChar = theEvent->message & 0xFF;
|
||||||
|
if(theChar == '\r' || theChar == '\x03') {
|
||||||
|
*itemHit = ok;
|
||||||
|
return true;
|
||||||
|
} else if(!isdigit(theChar) && theChar != '\b') {
|
||||||
|
SysBeep(1);
|
||||||
|
theEvent->what = nullEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DlogAbout(void) {
|
||||||
|
DialogPtr theDialog;
|
||||||
|
Rect dlogRect;
|
||||||
|
Rect boundsRect;
|
||||||
|
short itemNo;
|
||||||
|
|
||||||
|
theDialog = GetNewDialog(DLOG_ABOUT, NULL, (WindowPtr) -1);
|
||||||
|
if(!theDialog) return;
|
||||||
|
dlogRect = theDialog->portRect;
|
||||||
|
boundsRect = screenBits.bounds;
|
||||||
|
boundsRect.top += 20;
|
||||||
|
boundsRect.bottom /= 2;
|
||||||
|
CentreRect(&dlogRect, &boundsRect);
|
||||||
|
MoveWindow(theDialog, dlogRect.left, dlogRect.top, true);
|
||||||
|
ShowWindow(theDialog);
|
||||||
|
ModalDialog(&AboutFilter, &itemNo);
|
||||||
|
DisposDialog(theDialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
pascal Boolean AboutFilter(DialogPtr theDialog, EventRecord *theEvent,
|
||||||
|
int *itemHit) {
|
||||||
|
int windowCode;
|
||||||
|
WindowPtr theWindow;
|
||||||
|
|
||||||
|
if(theEvent->what != mouseDown)
|
||||||
|
return false;
|
||||||
|
windowCode = FindWindow(theEvent->where, &theWindow);
|
||||||
|
if(windowCode != inContent || theWindow != theDialog)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawGameInit(void) {
|
||||||
|
Point drawPoint;
|
||||||
|
ushort i;
|
||||||
|
|
||||||
|
drawPoint.v = CARD_BD_Y;
|
||||||
|
drawPoint.h = CARD_BD_X;
|
||||||
|
for(i = 0; i < FC_STORES; ++i) {
|
||||||
|
DrawEmptyFrame(drawPoint);
|
||||||
|
drawPoint.h += CARD_ST_X + CARD_XLENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawPoint.h = WIND_XLENGTH - CARD_BD_X - CARD_XLENGTH;
|
||||||
|
for(i = 0; i < 4; ++i) {
|
||||||
|
DrawEmptyFrame(drawPoint);
|
||||||
|
drawPoint.h -= CARD_ST_X + CARD_XLENGTH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ForceRedraw(void) {
|
||||||
|
GrafPtr thePort;
|
||||||
|
GetPort(&thePort);
|
||||||
|
InvalRect(&thePort->portRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawAll(FCState *state) {
|
||||||
|
DrawClear();
|
||||||
|
DrawStorage(state->store);
|
||||||
|
DrawFoundation(state->foundation);
|
||||||
|
DrawPlayfield(state->cols);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawClear(void) {
|
||||||
|
GrafPtr thePort;
|
||||||
|
Rect *clearRect;
|
||||||
|
GetPort(&thePort);
|
||||||
|
clearRect = &thePort->portRect;
|
||||||
|
EraseRect(clearRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawPlayfield(FCColumn **cols) {
|
||||||
|
Point drawPoint;
|
||||||
|
ushort i;
|
||||||
|
|
||||||
|
drawPoint.v = CARD_BD_Y + CARD_YLENGTH + CARD_ST_SEP;
|
||||||
|
drawPoint.h = CARD_BD_X;
|
||||||
|
for(i = 0; i < FC_COLS; ++i) {
|
||||||
|
DrawStack(cols[i]->cards, drawPoint, cols[i]->qty);
|
||||||
|
drawPoint.h += CARD_XLENGTH + CARD_PF_X;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawStorage(Card *cards) {
|
||||||
|
Point drawPoint;
|
||||||
|
ushort i;
|
||||||
|
|
||||||
|
drawPoint.v = CARD_BD_Y;
|
||||||
|
drawPoint.h = CARD_BD_X;
|
||||||
|
for(i = 0; i < FC_STORES; ++i) {
|
||||||
|
if(!CARD_EMPTY(cards[i])) {
|
||||||
|
DrawCard(cards[i], drawPoint);
|
||||||
|
} else {
|
||||||
|
DrawEmptyFrame(drawPoint);
|
||||||
|
}
|
||||||
|
drawPoint.h += CARD_ST_X + CARD_XLENGTH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawFoundation(Card *cards) {
|
||||||
|
Point drawPoint;
|
||||||
|
ushort i;
|
||||||
|
|
||||||
|
drawPoint.v = CARD_BD_Y;
|
||||||
|
drawPoint.h = WIND_XLENGTH - CARD_BD_X - CARD_XLENGTH;
|
||||||
|
for(i = 0; i < 4; ++i) {
|
||||||
|
if(!CARD_EMPTY(cards[3-i])) {
|
||||||
|
DrawCard(cards[3-i], drawPoint);
|
||||||
|
} else {
|
||||||
|
DrawEmptyFrame(drawPoint);
|
||||||
|
}
|
||||||
|
drawPoint.h -= CARD_ST_X + CARD_XLENGTH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawStorageCard(Card card, ushort pos) {
|
||||||
|
Point drawPoint;
|
||||||
|
|
||||||
|
drawPoint = GetStorePt(pos);
|
||||||
|
if(!CARD_EMPTY(card)) {
|
||||||
|
DrawCard(card, drawPoint);
|
||||||
|
} else {
|
||||||
|
DrawEmptyFrame(drawPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawFoundationCard(Card card, ushort pos) {
|
||||||
|
Point drawPoint;
|
||||||
|
|
||||||
|
drawPoint = GetStorePt(FC_STORES + pos);
|
||||||
|
if(!CARD_EMPTY(card)) {
|
||||||
|
DrawCard(card, drawPoint);
|
||||||
|
} else {
|
||||||
|
DrawEmptyFrame(drawPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawLastColumnCard(FCColumn *col, ushort colno) {
|
||||||
|
Point drawPoint;
|
||||||
|
|
||||||
|
drawPoint = GetColumnPt(colno);
|
||||||
|
DrawStackedCard(col->cards[col->qty-1], drawPoint, col->qty-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawRemovedColCard(FCColumn *col, ushort colno) {
|
||||||
|
Point drawPoint;
|
||||||
|
|
||||||
|
drawPoint = GetColumnPt(colno);
|
||||||
|
DrawStackedCard(CARD_NULL, drawPoint, col->qty);
|
||||||
|
|
||||||
|
if(col->qty != 0) {
|
||||||
|
DrawStackedCard(col->cards[col->qty-1], drawPoint, col->qty-1);
|
||||||
|
} else {
|
||||||
|
DrawEmptyFrame(drawPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CentreRect(Rect *toCentre, const Rect *bounds) {
|
||||||
|
short bdx, bdy, cdx, cdy;
|
||||||
|
|
||||||
|
bdx = bounds->right - bounds->left;
|
||||||
|
bdy = bounds->bottom - bounds->top;
|
||||||
|
cdx = toCentre->right - toCentre->left;
|
||||||
|
cdy = toCentre->bottom - toCentre->top;
|
||||||
|
if(cdx > bdx || cdy > bdy) return;
|
||||||
|
|
||||||
|
toCentre->top = bounds->top + bdy/2 - cdy/2;
|
||||||
|
toCentre->left = bounds->left + bdx/2 - cdx/2;
|
||||||
|
toCentre->bottom = toCentre->top + cdy;
|
||||||
|
toCentre->right = toCentre->left + cdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
switch(elem.zone) {
|
||||||
|
case FCZ_LASTCARD: {
|
||||||
|
FCColumn *theColumn = state->cols[elem.num];
|
||||||
|
if(CARD_EMPTY(theColumn->cards[theColumn->qty-1]))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
GetLastStackedRect(elem.num, theColumn->qty, &drawRect);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case FCZ_STORAGE:
|
||||||
|
if(CARD_EMPTY(state->store[elem.num]))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
GetStoreRect(elem.num, &drawRect);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FCZ_FOUNDATION:
|
||||||
|
if(CARD_EMPTY(state->foundation[elem.num]))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
GetStoreRect(elem.num + FC_STORES, &drawRect);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
*/
|
33
gamewind.h
Normal file
33
gamewind.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef GAMEWIND_H
|
||||||
|
#define GAMEWIND_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "gametypes.h"
|
||||||
|
#include "freecell.h"
|
||||||
|
|
||||||
|
/* Window / Dialog creation */
|
||||||
|
WindowPtr WindCreateTestEnv(Rect *bounds, StringPtr title);
|
||||||
|
WindowPtr WindCreateGame(FCState *state);
|
||||||
|
void WindUpdateTitle(WindowPtr theWind);
|
||||||
|
|
||||||
|
ushort DlogOpenGame(void);
|
||||||
|
void DlogAbout(void);
|
||||||
|
|
||||||
|
/* High-Level Drawing */
|
||||||
|
void DrawGameInit(void);
|
||||||
|
void ForceRedraw(void);
|
||||||
|
void DrawAll(FCState *state);
|
||||||
|
void DrawClear(void);
|
||||||
|
void DrawPlayfield(FCColumn **cols);
|
||||||
|
void DrawStorage(Card *cards);
|
||||||
|
void DrawFoundation(Card *cards);
|
||||||
|
|
||||||
|
void DrawStorageCard(Card card, ushort pos);
|
||||||
|
void DrawFoundationCard(Card card, ushort pos);
|
||||||
|
void DrawLastColumnCard(FCColumn *col, ushort colno);
|
||||||
|
void DrawRemovedColCard(FCColumn *col, ushort colno);
|
||||||
|
|
||||||
|
/* Misc */
|
||||||
|
void CentreRect(Rect *toCentre, const Rect *bounds);
|
||||||
|
|
||||||
|
#endif /* GAMEWIND_H */
|
156
gamewindlow.c
Normal file
156
gamewindlow.c
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
#include "gamewindlow.h"
|
||||||
|
|
||||||
|
void DrawCard(Card card, Point loc) {
|
||||||
|
Rect cardRect;
|
||||||
|
static short lineAscent = -1;
|
||||||
|
static short lineSpacing = -1;
|
||||||
|
|
||||||
|
SetRect(&cardRect, 0, 0, CARD_XLENGTH, CARD_YLENGTH);
|
||||||
|
OffsetRect(&cardRect, loc.h, loc.v);
|
||||||
|
EraseRoundRect(&cardRect, CARD_XRATIO, CARD_YRATIO);
|
||||||
|
if(CARD_EMPTY(card)) return;
|
||||||
|
|
||||||
|
TextFont(CARDFONT_ID);
|
||||||
|
TextSize(9);
|
||||||
|
if(lineSpacing == -1) {
|
||||||
|
FontInfo cardFontInfo;
|
||||||
|
GetFontInfo(&cardFontInfo);
|
||||||
|
lineAscent = cardFontInfo.ascent;
|
||||||
|
lineSpacing = cardFontInfo.ascent +
|
||||||
|
cardFontInfo.descent +
|
||||||
|
cardFontInfo.leading;
|
||||||
|
}
|
||||||
|
|
||||||
|
FillRoundRect(&cardRect, CARD_XRATIO, CARD_YRATIO, white);
|
||||||
|
FrameRoundRect(&cardRect, CARD_XRATIO, CARD_YRATIO);
|
||||||
|
InsetRect(&cardRect, (CARD_XLENGTH * 9) / 40,
|
||||||
|
(CARD_YLENGTH * 9) / 40);
|
||||||
|
FillRoundRect(&cardRect, CARD_XRATIO, CARD_YRATIO, gray);
|
||||||
|
|
||||||
|
loc.h += (CARD_XLENGTH * 2) / 40;
|
||||||
|
loc.v += (CARD_YLENGTH * 2) / 40 + lineAscent;
|
||||||
|
MoveTo(loc.h, loc.v);
|
||||||
|
DrawChar(ITOC(CARD_GETNUM(card)));
|
||||||
|
/*MoveTo(loc.h, loc.v + lineSpacing);*/
|
||||||
|
DrawChar(SUIT2CHAR(CARD_GETSUIT(card)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawStack(Card cards[], Point loc, ushort qty) {
|
||||||
|
ushort i;
|
||||||
|
|
||||||
|
if(qty == 0) {
|
||||||
|
DrawEmptyFrame(loc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < qty; ++i) {
|
||||||
|
DrawCard(cards[i], loc);
|
||||||
|
loc.v += CARD_PF_Y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawStackedCard(Card card, Point loc, ushort pos) {
|
||||||
|
loc.v += CARD_PF_Y * pos;
|
||||||
|
DrawCard(card, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawEmptyFrame(Point loc) {
|
||||||
|
PenState ps;
|
||||||
|
Rect frameRect = {0, 0, CARD_YLENGTH, CARD_XLENGTH};
|
||||||
|
|
||||||
|
GetPenState(&ps);
|
||||||
|
PenPat(black);
|
||||||
|
PenSize(1,1);
|
||||||
|
OffsetRect(&frameRect, loc.h, loc.v);
|
||||||
|
FillRoundRect(&frameRect, CARD_XRATIO, CARD_YRATIO, white);
|
||||||
|
FrameRoundRect(&frameRect, CARD_XRATIO, CARD_YRATIO);
|
||||||
|
SetPenState(&ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns (INVAL_LOC, INVAL_LOC) on error */
|
||||||
|
Point GetStorePt(ushort store) {
|
||||||
|
Point res;
|
||||||
|
if(store >= FC_STORES + 4) {
|
||||||
|
L2PT(res, INVAL_PTL);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(store >= FC_STORES) {
|
||||||
|
/* Set count from 4-7 to 1-4 backwards */
|
||||||
|
/*store = (4 - 1) - (store - FC_STORES) + 1;*/
|
||||||
|
store = (FC_STORES + 4) - store;
|
||||||
|
res.h = WIND_XLENGTH - CARD_BD_X + CARD_ST_X /* Correction Term */
|
||||||
|
- store * (CARD_XLENGTH + CARD_ST_X);
|
||||||
|
} else {
|
||||||
|
res.h = CARD_BD_X + store * (CARD_XLENGTH + CARD_ST_X);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.v = CARD_BD_Y;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point GetColumnPt(ushort col) {
|
||||||
|
Point res;
|
||||||
|
if(col >= FC_COLS) {
|
||||||
|
L2PT(res, INVAL_PTL);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.v = CARD_BD_Y + CARD_YLENGTH + CARD_ST_SEP;
|
||||||
|
res.h = CARD_BD_X + col * (CARD_XLENGTH + CARD_PF_X);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetStoreRect(ushort store, Rect *res) {
|
||||||
|
topLeft(*res) = GetStorePt(store);
|
||||||
|
if(BAD_PT(topLeft(*res))) {
|
||||||
|
SetRect(res, 0, 0, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res->bottom = res->top + CARD_YLENGTH;
|
||||||
|
res->right = res->left + CARD_XLENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetColumnRect(ushort col, ushort csize, Rect *res) {
|
||||||
|
topLeft(*res) = GetColumnPt(col);
|
||||||
|
if(BAD_PT(topLeft(*res))) {
|
||||||
|
SetRect(res, 0, 0, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(csize == 0) csize = 1;
|
||||||
|
res->bottom = res->top + (csize - 1) * CARD_PF_Y + CARD_YLENGTH;
|
||||||
|
res->right = res->left + CARD_XLENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 0 treated as 1 so that cards can be played on empty columns */
|
||||||
|
void GetStackedCardRect(ushort col, ushort csize, ushort pos, Rect *res) {
|
||||||
|
if(csize == 0) csize = 1;
|
||||||
|
|
||||||
|
if(pos >= csize)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
topLeft(*res) = GetColumnPt(col);
|
||||||
|
if(BAD_PT(topLeft(*res)))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
res->top += pos * CARD_PF_Y;
|
||||||
|
res->right = res->left + CARD_XLENGTH;
|
||||||
|
|
||||||
|
/* If card is last in column, use full area. Otherwise whats seen. */
|
||||||
|
if(pos == csize - 1) {
|
||||||
|
res->bottom = res->top + CARD_YLENGTH;
|
||||||
|
} else {
|
||||||
|
res->bottom = res->top + CARD_PF_Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
err:
|
||||||
|
SetRect(res, 0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetLastStackedRect(ushort col, ushort csize, Rect *res) {
|
||||||
|
if(csize < 1) csize = 1;
|
||||||
|
GetStackedCardRect(col, csize, csize-1, res);
|
||||||
|
}
|
77
gamewindlow.h
Normal file
77
gamewindlow.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#ifndef GAMEWINDLOW_H
|
||||||
|
#define GAMEWINDLOW_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "freecell.h"
|
||||||
|
|
||||||
|
/* Game Dimensions */
|
||||||
|
|
||||||
|
#define CARDFONT_ID 25
|
||||||
|
#define CARDFONT_PT 12
|
||||||
|
|
||||||
|
#define CARD_XRATIO 5
|
||||||
|
#define CARD_YRATIO 7
|
||||||
|
#define CARD_SCALE 9
|
||||||
|
|
||||||
|
#define CARD_XLENGTH (CARD_XRATIO * CARD_SCALE)
|
||||||
|
#define CARD_YLENGTH (CARD_YRATIO * CARD_SCALE)
|
||||||
|
|
||||||
|
/*
|
||||||
|
^
|
||||||
|
|BD_Y
|
||||||
|
|
|
||||||
|
BD_Xv ST_X ST_GAP
|
||||||
|
<-->+--+<---->+--+ ... <------>
|
||||||
|
| | | |
|
||||||
|
| | | |
|
||||||
|
+--+ +--+
|
||||||
|
^
|
||||||
|
ST|
|
||||||
|
SEP| XLEN
|
||||||
|
v PF_X <-->
|
||||||
|
+--+<----->+--+^
|
||||||
|
PF_Y| | | ||YLEN
|
||||||
|
+--+ | ||
|
||||||
|
| | +--+v
|
||||||
|
...
|
||||||
|
*/
|
||||||
|
/* Set to a ratio ? */
|
||||||
|
#define CARD_PF_X 10
|
||||||
|
#define CARD_PF_Y 12
|
||||||
|
|
||||||
|
#define CARD_ST_X ((WIND_XLENGTH - (2*CARD_BD_X) - (8*CARD_XLENGTH) \
|
||||||
|
- CARD_ST_GAP) / 6)
|
||||||
|
#define CARD_ST_GAP 40
|
||||||
|
#define CARD_ST_SEP (2*CARD_BD_Y)
|
||||||
|
|
||||||
|
#define CARD_BD_X 10
|
||||||
|
#define CARD_BD_Y 10
|
||||||
|
|
||||||
|
#define WIND_XLENGTH ((2*CARD_BD_X)+(8*CARD_XLENGTH)+(7*CARD_PF_X))
|
||||||
|
#define WIND_YLENGTH \
|
||||||
|
(((long) (screenBits.bounds.bottom-20) * WIND_XLENGTH) / screenBits.bounds.right)
|
||||||
|
|
||||||
|
#define SUIT2CHAR(s) ('0' - 1 - (s))
|
||||||
|
|
||||||
|
#define INVAL_LOC (0x8000)
|
||||||
|
#define INVAL_PTL (0x80008000L)
|
||||||
|
#define BAD_PT(p) (((p).h == INVAL_LOC) && ((p).v == INVAL_LOC))
|
||||||
|
/*#define BAD_PT(p) BAD_PTL(PT2L(p))*/
|
||||||
|
#define BAD_PTL(p) (p == INVAL_PTL)
|
||||||
|
|
||||||
|
/* Low-Level Drawing */
|
||||||
|
void DrawCard(Card card, Point loc);
|
||||||
|
void DrawStackedCard(Card card, Point loc, ushort pos);
|
||||||
|
void DrawStack(Card cards[], Point loc, ushort qty);
|
||||||
|
void DrawEmptyFrame(Point loc);
|
||||||
|
|
||||||
|
/* Low-Level Point Access */
|
||||||
|
Point GetStorePt(ushort store);
|
||||||
|
Point GetColumnPt(ushort col);
|
||||||
|
void GetStoreRect(ushort store, Rect *res);
|
||||||
|
void GetColumnRect(ushort col, ushort csize, Rect *res);
|
||||||
|
void GetStackedCardRect(ushort col, ushort csize, ushort pos, Rect *res);
|
||||||
|
void GetLastStackedRect(ushort col, ushort csize, Rect *res);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* GAMEWINDLOW_H */
|
144
main.c
Normal file
144
main.c
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
/* TODO
|
||||||
|
- Bug where starting a new game causes some cards to not be drawn properly
|
||||||
|
and may leave residual cards in foundation / storage
|
||||||
|
- Optimize DlogOpenGame string to int
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gamewind.h"
|
||||||
|
#include "gameintf.h"
|
||||||
|
#include "gamemenu.h"
|
||||||
|
#include "gamestate.h"
|
||||||
|
#include "freecell.h"
|
||||||
|
|
||||||
|
struct GlobalState gstate = {0};
|
||||||
|
static Rect dragRect;
|
||||||
|
|
||||||
|
static void InitMacintosh(void);
|
||||||
|
static void InitGameState(void);
|
||||||
|
static void HandleEvent(short eventMask);
|
||||||
|
static void HandleMouseDown(EventRecord *theEvent);
|
||||||
|
static void HandleContentClick(Point mousePt);
|
||||||
|
short RndRange(short lower, short upper);
|
||||||
|
|
||||||
|
void InitMacintosh(void) {
|
||||||
|
MaxApplZone();
|
||||||
|
|
||||||
|
InitGraf(&thePort);
|
||||||
|
InitFonts();
|
||||||
|
InitWindows();
|
||||||
|
InitMenus();
|
||||||
|
TEInit();
|
||||||
|
InitDialogs(0L);
|
||||||
|
randSeed = RndSeed;
|
||||||
|
|
||||||
|
FlushEvents(everyEvent, 0);
|
||||||
|
InitCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitGameState(void) {
|
||||||
|
FreecellInit(&gstate.fcGame);
|
||||||
|
gstate.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleEvent(short eventMask) {
|
||||||
|
int res;
|
||||||
|
EventRecord theEvent;
|
||||||
|
|
||||||
|
HiliteMenu(0);
|
||||||
|
SystemTask(); /* Handle desk accessories */
|
||||||
|
|
||||||
|
if (!GetNextEvent(eventMask, &theEvent)) return;
|
||||||
|
switch (theEvent.what) {
|
||||||
|
case mouseDown:
|
||||||
|
HandleMouseDown(&theEvent);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case keyDown:
|
||||||
|
if(theEvent.modifiers & cmdKey) {
|
||||||
|
MenuEvent(MenuKey(theEvent.message & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
case autoKey:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case activateEvt:
|
||||||
|
if(theEvent.modifiers & activeFlag) {
|
||||||
|
MenuEditState(false);
|
||||||
|
} else {
|
||||||
|
MenuEditState(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case updateEvt:
|
||||||
|
BeginUpdate((WindowPtr) theEvent.message);
|
||||||
|
/*EraseRect(&((WindowPtr) theEvent.message)->portRect);*/
|
||||||
|
DrawAll(&gstate.fcGame);
|
||||||
|
EndUpdate((WindowPtr) theEvent.message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleMouseDown(EventRecord *theEvent) {
|
||||||
|
WindowPtr theWindow;
|
||||||
|
short windowCode = FindWindow(theEvent->where, &theWindow);
|
||||||
|
|
||||||
|
switch(windowCode) {
|
||||||
|
case inSysWindow:
|
||||||
|
SystemClick (theEvent, theWindow);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case inDrag:
|
||||||
|
DragWindow(theWindow, theEvent->where, &dragRect);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case inMenuBar:
|
||||||
|
MenuEvent(MenuSelect(theEvent->where));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case inContent:
|
||||||
|
if(theWindow != FrontWindow()) {
|
||||||
|
SelectWindow(theWindow);
|
||||||
|
} else {
|
||||||
|
GlobalToLocal(&theEvent->where);
|
||||||
|
switch(HandleGameClick(&gstate.fcGame, theEvent->where)) {
|
||||||
|
/*case FCCE_BADDRAG:*/
|
||||||
|
case FCCE_BADMOVE:
|
||||||
|
SysBeep(1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case inGoAway:
|
||||||
|
if(TrackGoAway(theWindow, theEvent->where)) {
|
||||||
|
HideWindow(theWindow);
|
||||||
|
gstate.running = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
Point cardPoint = {10, 10};
|
||||||
|
WindowPtr testWind;
|
||||||
|
|
||||||
|
InitMacintosh();
|
||||||
|
InitGameState();
|
||||||
|
MenuCreate();
|
||||||
|
|
||||||
|
SetRect(&dragRect, 4, 24, screenBits.bounds.right-4,
|
||||||
|
screenBits.bounds.bottom-4);
|
||||||
|
|
||||||
|
testWind = WindCreateGame(&gstate.fcGame);
|
||||||
|
GameNewGame(&gstate.fcGame, GAME_RANDOMSEED);
|
||||||
|
|
||||||
|
while(gstate.running) {
|
||||||
|
HandleEvent(everyEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
FreecellDisposeDeal(gstate.fcGame.cols);
|
||||||
|
}
|
22
pstring.c
Normal file
22
pstring.c
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/* Parameter List and Prototypes disabled so that stack frame not created */
|
||||||
|
|
||||||
|
#pragma options(!require_protos)
|
||||||
|
StringPtr strcat_p(/*StringPtr s1, const StringPtr s2*/) {
|
||||||
|
asm {
|
||||||
|
MOVEA.L 4(sp), a0 ; A0 = s1
|
||||||
|
MOVEA.L 8(sp), a1 ; A1 = s2
|
||||||
|
CLR.L d0
|
||||||
|
CLR.L d1
|
||||||
|
MOVE.B (a0), d0 ; D0 = n(s1)
|
||||||
|
MOVE.B (a1)+, d1 ; D1 = n(s2)
|
||||||
|
ADD.B d1, (a0)+ ; Update n(s1)
|
||||||
|
ADDA.L d0, a0 ; Offset s1
|
||||||
|
TST.B d1
|
||||||
|
BRA.S @2
|
||||||
|
@1 MOVE.B (a1)+, (a0)+
|
||||||
|
SUBQ.B #1, d1
|
||||||
|
@2 BNE.S @1
|
||||||
|
|
||||||
|
MOVE.L 4(sp), d0
|
||||||
|
}
|
||||||
|
}
|
6
pstring.h
Normal file
6
pstring.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef PSTRING_H
|
||||||
|
#define PSTRING_H
|
||||||
|
|
||||||
|
StringPtr strcat_p(StringPtr a, const StringPtr b);
|
||||||
|
|
||||||
|
#endif /* PSTRING_H */
|
90
strntol.c
Normal file
90
strntol.c
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Adapted from GCC strtol.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "strntol.h"
|
||||||
|
#include <limits.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
long strntol(const char *nptr, size_t sz, char **endptr, int base) {
|
||||||
|
const char *s = nptr;
|
||||||
|
unsigned long acc = 0;
|
||||||
|
int c;
|
||||||
|
unsigned long cutoff;
|
||||||
|
int neg = 0, any = 0, cutlim;
|
||||||
|
|
||||||
|
++sz; /* Bounds fix */
|
||||||
|
|
||||||
|
/* Skip whitespace, pick up +/- sign, detect prefix. */
|
||||||
|
do {
|
||||||
|
c = *s++;
|
||||||
|
--sz;
|
||||||
|
} while(isspace(c) && sz > 0);
|
||||||
|
if(sz == 0) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(c == '-') {
|
||||||
|
neg = 1;
|
||||||
|
c = *s++;
|
||||||
|
sz--;
|
||||||
|
} else if(c == '+') {
|
||||||
|
c = *s++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if((base == 0 || base == 16) &&
|
||||||
|
sz >= 2 && c == '0' && (*s == 'x' || *s == 'X')) {
|
||||||
|
c = s[1];
|
||||||
|
s += 2;
|
||||||
|
sz -= 2;
|
||||||
|
base = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(base == 0) {
|
||||||
|
base = c == '0' ? 8 : 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compute cutoff between legal / illegal numbers */
|
||||||
|
cutoff = neg ? -(unsigned long) LONG_MIN : LONG_MAX;
|
||||||
|
cutlim = cutoff % (unsigned long) base;
|
||||||
|
cutoff /= (unsigned long) base;
|
||||||
|
for(;; --sz, c = *s++) {
|
||||||
|
if(sz == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isdigit(c)) {
|
||||||
|
c -= '0';
|
||||||
|
} else if(isalpha(c)) {
|
||||||
|
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(c >= base) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) {
|
||||||
|
any = -1;
|
||||||
|
} else {
|
||||||
|
any = 1;
|
||||||
|
acc *= base;
|
||||||
|
acc += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(any < 0) {
|
||||||
|
acc = neg ? LONG_MIN : LONG_MAX;
|
||||||
|
} else if(neg) {
|
||||||
|
acc = -acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
err:
|
||||||
|
if(endptr != 0) {
|
||||||
|
*endptr = (char *) (any ? s - 1 : nptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user