mirror of
https://github.com/StevenMcLeod/Freecell68k.git
synced 2024-06-17 06:29:27 +00:00
245 lines
5.4 KiB
C
245 lines
5.4 KiB
C
|
#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
|
||
|
}
|
||
|
}
|