diff --git a/FreeCell b/FreeCell new file mode 100644 index 0000000..6d2f640 Binary files /dev/null and b/FreeCell differ diff --git a/FreeCell. b/FreeCell. new file mode 100644 index 0000000..2a97ab1 Binary files /dev/null and b/FreeCell. differ diff --git a/FreeCell..rsrc b/FreeCell..rsrc new file mode 100644 index 0000000..6537769 Binary files /dev/null and b/FreeCell..rsrc differ diff --git a/bitset.h b/bitset.h new file mode 100644 index 0000000..fe1f692 --- /dev/null +++ b/bitset.h @@ -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 diff --git a/common.h b/common.h new file mode 100644 index 0000000..009bb51 --- /dev/null +++ b/common.h @@ -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 \ No newline at end of file diff --git a/freecell.c b/freecell.c new file mode 100644 index 0000000..596a5c1 --- /dev/null +++ b/freecell.c @@ -0,0 +1,265 @@ +#include "freecell.h" +#include "common.h" +#include + +#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; +} \ No newline at end of file diff --git a/freecell.h b/freecell.h new file mode 100644 index 0000000..65e4126 --- /dev/null +++ b/freecell.h @@ -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 */ \ No newline at end of file diff --git a/gameintf.c b/gameintf.c new file mode 100644 index 0000000..ba2e0cc --- /dev/null +++ b/gameintf.c @@ -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 + } +} \ No newline at end of file diff --git a/gameintf.h b/gameintf.h new file mode 100644 index 0000000..c64338b --- /dev/null +++ b/gameintf.h @@ -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 */ \ No newline at end of file diff --git a/gamemenu.c b/gamemenu.c new file mode 100644 index 0000000..37f7ce9 --- /dev/null +++ b/gamemenu.c @@ -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; + } +} \ No newline at end of file diff --git a/gamemenu.h b/gamemenu.h new file mode 100644 index 0000000..0615834 --- /dev/null +++ b/gamemenu.h @@ -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 */ \ No newline at end of file diff --git a/gamestate.h b/gamestate.h new file mode 100644 index 0000000..def39a3 --- /dev/null +++ b/gamestate.h @@ -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 */ \ No newline at end of file diff --git a/gametypes.h b/gametypes.h new file mode 100644 index 0000000..4c8f98c --- /dev/null +++ b/gametypes.h @@ -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 */ \ No newline at end of file diff --git a/gamewind.c b/gamewind.c new file mode 100644 index 0000000..f69452c --- /dev/null +++ b/gamewind.c @@ -0,0 +1,344 @@ +#include "gamewind.h" +#include "gamewindlow.h" +#include "common.h" +#include "pstring.h" +#include "strntol.h" +#include +#include + +#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; + } + */ \ No newline at end of file diff --git a/gamewind.h b/gamewind.h new file mode 100644 index 0000000..798b941 --- /dev/null +++ b/gamewind.h @@ -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 */ \ No newline at end of file diff --git a/gamewindlow.c b/gamewindlow.c new file mode 100644 index 0000000..740d1bc --- /dev/null +++ b/gamewindlow.c @@ -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); +} \ No newline at end of file diff --git a/gamewindlow.h b/gamewindlow.h new file mode 100644 index 0000000..47d71e4 --- /dev/null +++ b/gamewindlow.h @@ -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 */ \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..856f33d --- /dev/null +++ b/main.c @@ -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); +} \ No newline at end of file diff --git a/pstring.c b/pstring.c new file mode 100644 index 0000000..fb10361 --- /dev/null +++ b/pstring.c @@ -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 + } +} \ No newline at end of file diff --git a/pstring.h b/pstring.h new file mode 100644 index 0000000..3efcc1c --- /dev/null +++ b/pstring.h @@ -0,0 +1,6 @@ +#ifndef PSTRING_H +#define PSTRING_H + +StringPtr strcat_p(StringPtr a, const StringPtr b); + +#endif /* PSTRING_H */ \ No newline at end of file diff --git a/strntol.c b/strntol.c new file mode 100644 index 0000000..5029c41 --- /dev/null +++ b/strntol.c @@ -0,0 +1,90 @@ +/* + * Adapted from GCC strtol.c + */ + +#include "strntol.h" +#include +#include + +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; +} \ No newline at end of file diff --git a/strntol.h b/strntol.h new file mode 100644 index 0000000..5facf5b --- /dev/null +++ b/strntol.h @@ -0,0 +1,8 @@ +#ifndef STRNTOL_H +#define STRNTOL_H + +#include + +long strntol(const char *nptr, size_t sz, char **endptr, int base); + +#endif /* STRNTOL_H */ \ No newline at end of file