Files
2026-02-02 04:50:13 +01:00

1147 lines
34 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "bagel/hodjnpodj/hnplibs/stdafx.h"
#include "bagel/hodjnpodj/hnplibs/mainmenu.h"
#include "bagel/hodjnpodj/hnplibs/gamedll.h"
#include "bagel/boflib/misc.h"
#include "bagel/boflib/sound.h"
#include "bagel/hodjnpodj/mankala/mnk.h"
namespace Bagel {
namespace HodjNPodj {
namespace Mankala {
extern bool gbTurnSoundsOff;
extern LPGAMESTRUCT pGameParams;
static int gCount; //reset to 0 in StartGame() and incremented in AcceptClick to keep track of # of Turns.
//* CMnkWindow::StartGame -- start a new game
bool CMnkWindow::StartGame()
// returns: true if error, false otherwise
{
JXENTER(CMnkWindow::StartGame) ;
int iError = 0 ; // error code
gCount = 0; // this count keeps track of the # of turns of each player so far,
// and is used
FreePitResources() ; // free resources from previous game
// ((CMnk *)this)->InitMankala() ; // initialize mankala game
CMnk::InitMankala() ; // initialize mankala game logic
Invalidate(false) ; // invalidate the entire window, so it
// will be repainted
m_bStartGame = m_bGameOver = false ;
m_bGameOverMusicPlayed = false;
m_bScoresDisplayed = false;
SetCrabSign(false) ;
UpdateWindow();
if (!gbTurnSoundsOff && pGameParams->bSoundEffectsEnabled)
if (!sndPlaySound(YOUGO3, SND_SYNC)) {
MessageBox("Can't Play Wave Sound");
}
JXELEAVE(CMnkWindow::StartGame) ;
RETURN(iError != 0) ;
}
//* CMnkWindow::PaintBitmapObject -- paint bitmap
bool CMnkWindow::PaintBitmapObject(CBmpObject * xpcBmpObject,
int iBmpType, int iBmpArg)
// xpcBmpObject -- pointer to bitmap object
// iBmpType -- BMT_xxxx -- type of bitmap object
// returns: true if error, false otherwise
{
JXENTER(CMnkWindow::PaintBitmapObject) ;
int iError = 0 ; // error code
bool bNew = !xpcBmpObject->m_bInit ; // needs to be initialized
CRect cBmpRect, cDestRect ; // bitmap/destination rectangles
CDC *pDC ;
pDC = GetDC();
MSG msg;
if (MFC::PeekMessage(&msg, nullptr, MM_MCINOTIFY, MM_MCINOTIFY, PM_REMOVE)) {
MFC::TranslateMessage(&msg);
MFC::DispatchMessage(&msg);
}
if (xpcBmpObject->m_iBmpType == 0) // if no type yet inserted
xpcBmpObject->m_iBmpType = iBmpType ; // store type
else if (iBmpType > 0 && xpcBmpObject->m_iBmpType != iBmpType)
// if we're changing types
{
iError = 100 ; // invalid type change
goto cleanup ;
}
if (!xpcBmpObject->m_iBmpType) {
iError = 101 ; // no type specified
goto cleanup ;
}
if (!xpcBmpObject->m_bChained) { // if not yet on chain to be freed
xpcBmpObject->m_xpcNextFree = m_xpcBmpFreeChain ;
m_xpcBmpFreeChain = xpcBmpObject ;
xpcBmpObject->m_bChained = true ;
}
if (iBmpArg)
xpcBmpObject->m_iBmpArg = iBmpArg ;
if (bNew) // if not initialized
InitBitmapObject(xpcBmpObject) ; // load bitmap
if (bNew || xpcBmpObject->m_bSprite)
// if this is new, or if this is a sprite,
SetBitmapCoordinates(xpcBmpObject) ; // set position
if (!xpcBmpObject->m_bDummy) { // if not a dummy bitmap
cBmpRect = CRect(CPoint(0, 0), xpcBmpObject->m_cSize) ;
cDestRect = CRect(xpcBmpObject->m_cPosition, xpcBmpObject->m_cSize) ;
if (xpcBmpObject->m_bSprite) {
if (!m_bJustStarted) {
xpcBmpObject->m_xpcSprite->ClearBackground() ;
// clear the save background for the stone
// sprite, forcing RefreshSprite to fetch/save
// the background it's currently covering
xpcBmpObject->m_xpcSprite->
SetPosition(xpcBmpObject->m_cPosition.x,
xpcBmpObject->m_cPosition.y) ;
xpcBmpObject->m_xpcSprite->RefreshSprite(pDC) ;
// display the stone sprite, saving
// current background image
}
} else if (xpcBmpObject->m_bMasked) {
PaintMaskedDIB(pDC, m_xpGamePalette,
xpcBmpObject->m_xpDibDoc,
xpcBmpObject->m_cPosition.x,
xpcBmpObject->m_cPosition.y,
xpcBmpObject->m_cSize.cx,
xpcBmpObject->m_cSize.cy) ;
}
else // there's a DIB
PaintDIB(pDC->m_hDC, &cDestRect,
xpcBmpObject->m_xpDibDoc->GetHDIB(),
&cBmpRect, m_xpGamePalette) ;
// transfer the bitmap to the screen
}
xpcBmpObject->m_bInit = true ; // object is initialized
cleanup:
if (pDC) {
ReleaseDC(pDC);
pDC = nullptr;
}
JXELEAVE(CMnkWindow::PaintBitmapObject) ;
RETURN(iError != 0) ;
}
//* CMnkWindow::InitBitmapObject -- set up DibDoc in bitmap object
bool CMnkWindow::InitBitmapObject(CBmpObject * xpcBmpObject)
// xpcBmpObject -- pointer to bitmap object
// returns: true if error, false otherwise
{
JXENTER(CMnkWindow::InitBitmapObject) ;
int iError = 0 ; // error code
// CPitWnd * xpcPit = xpcBmpObject->m_xpcPit ;
CSprite * xpcSprite ;
HDIB hDib ;
if (xpcBmpObject->m_bSprite) {
if (!(xpcSprite = xpcBmpObject->m_xpcSprite
= new CSprite)) { // alloc sprite and test
iError = 100 ; // can't allocate sprite
goto cleanup ;
}
xpcSprite->SharePalette(m_xpGamePalette) ;
// set its palette to be the same as the game's
xpcBmpObject->m_xpcSprite = xpcSprite ;
if ((iError = InitBitmapFilename(xpcBmpObject)))
// initialize correct filename and do LoadSprite
goto cleanup ;
xpcSprite->SetMasked(true) ;
// it is a masked image (white/transparent background)
xpcSprite->SetOptimizeSpeed(false) ;
// keep its bitmap inside a device context
xpcSprite->SetMobile(true) ; // it will move around the screen
// xpcSprite->LinkSprite() ;
// // link stone sprite into sprite chain
xpcBmpObject->m_cSize = xpcSprite->GetSize() ;
// now establish its "hotspot" as its center point
xpcSprite->SetHotspot(xpcBmpObject->m_cSize.cx >> 1,
xpcBmpObject->m_cSize.cy >> 1) ;
}
else { // it's a DIB
if (!xpcBmpObject->m_xpDibDoc) { // if no DIB document allocated
if (!(xpcBmpObject->m_xpDibDoc = new CDibDoc()))
// allocate DIB and test
{
iError = 100 ; // can't allocate DIB
goto cleanup ;
}
if ((iError = InitBitmapFilename(xpcBmpObject)))
goto cleanup ;
}
if (!xpcBmpObject->m_bDummy && !xpcBmpObject->m_lpDib) {
if (!(hDib = xpcBmpObject->m_xpDibDoc->GetHDIB()))
// get DIB memory handle and test
{
iError = 103 ; // no DIB memory handle
goto cleanup ;
}
xpcBmpObject->m_lpDib = hDib;
xpcBmpObject->m_cSize = CSize(
(int)DIBWidth(xpcBmpObject->m_lpDib),
(int)DIBHeight(xpcBmpObject->m_lpDib));
}
}
cleanup:
JXELEAVE(CMnkWindow::InitBitmapObject) ;
RETURN(iError != 0) ;
}
//* CMnkWindow::InitBitmapFilename -- set up filename bitmap object
bool CMnkWindow::InitBitmapFilename(CBmpObject * xpcBmpObject)
// xpcBmpObject -- pointer to bitmap object
// returns: true if error, false otherwise
{
JXENTER(CMnkWindow::InitBitmapFilename) ;
CDC *pDC = GetDC();
static class CBmpTable cBmpTable[] = {
{BMT_MAIN, -1, 1, "mankala.bmp", false, false},
{BMT_SCROLL, -1, 1, "scrollup.bmp", false, true}, //scrolbtn.bmp
{BMT_STONE, -1, 4, "mans%d.bmp", false, true},
{BMT_PIT, 0, 1, "manp%d%d.bmp", true, true},
{BMT_PIT, 1, 4, nullptr, true, true},
{BMT_PIT, 2, 6, nullptr, true, true},
{BMT_PIT, 3, 8, nullptr, true, true},
{BMT_PIT, 4, 8, nullptr, true, true},
{BMT_PIT, 5, 5, nullptr, true, true},
{BMT_PIT, 6, 4, nullptr, true, true},
{BMT_PIT, 7, 4, nullptr, true, true},
{BMT_PIT, 8, 4, nullptr, true, true},
{BMT_PIT, -1, 2, "manpm%d.bmp", false, true},
{BMT_LEFTBIN, 22, 1, "manl%02d.bmp", true, true},
{BMT_LEFTBIN, -1, 1, "manlm.bmp", false, true},
{BMT_RIGHTBIN, 22, 1, "manr%02d.bmp", true, true},
{BMT_RIGHTBIN, -1, 1, "manrm.bmp", false, true},
{BMT_HAND, -1, 0, nullptr, false, true},
{BMT_SIGN, SBT_CRAB, 1, "mancrab.bmp", false, false},
{BMT_SIGN, SBT_MYTURN, 1, "mancm.bmp", false, true},
{BMT_SIGN, SBT_YOURTURN, 1, "mancy.bmp", false, true},
{BMT_SIGN, SBT_TIE, 1, "manctie.bmp", false, true},
{BMT_SIGN, SBT_IWIN, 1, "manciwin.bmp", false, true},
{BMT_SIGN, SBT_YOUWIN, 1, "mancuwin.bmp", false, true},
{0, 0, 0, nullptr, false, true}
} ;
int iError = 0 ; // error code
CBmpTable * xpBmpTable ;
bool bFound = false ;
const char *xpszFilenameString = nullptr ;
char szPath[200] = {0} ; // bitmap file path
CPitWnd * xpcPit;
int iNumStones;
xpcPit = xpcBmpObject->m_xpcPit ;
iNumStones = xpcPit ? xpcPit->m_iNumStones
: xpcBmpObject->m_iBmpArg ;
// # stones in pit or bin
Common::strcpy_s(szPath, m_szDataDirectory) ;
for (xpBmpTable = cBmpTable ; !bFound &&
xpBmpTable->m_iBmpType ;
bFound || ++xpBmpTable)
if (xpcBmpObject->m_iBmpType == xpBmpTable->m_iBmpType) {
if (xpBmpTable->m_xpszFilenameString)
xpszFilenameString =
xpBmpTable->m_xpszFilenameString ;
if (xpBmpTable->m_iNumStones < 0 ||
iNumStones <= xpBmpTable->m_iNumStones)
bFound = true ;
}
if (!bFound) {
iError = 101 ; // can't find bmp file
goto cleanup ;
}
xpcBmpObject->m_bDummy = (xpBmpTable->m_iNumBmps < 1) ;
// test if this is a dummy object
xpcBmpObject->m_bMasked = xpBmpTable->m_bMasked ;
if (xpBmpTable->m_iNumBmps >= 1) {
xpcBmpObject->m_iBmpNum = m_cGenUtil.RandomInteger(1,
xpBmpTable->m_iNumBmps) ;
// choose a random file
Common::sprintf_s(szPath + strlen(szPath),
200 - strlen(szPath),
xpszFilenameString,
xpBmpTable->m_bSubNumStones ? iNumStones
: xpcBmpObject->m_iBmpNum,
xpcBmpObject->m_iBmpNum) ;
if (xpcBmpObject->m_bSprite) { // if this is a sprite
if (!xpcBmpObject->m_xpcSprite->LoadSprite(pDC, szPath)) {
iError = 102 ; // can't load bitmap from disk
goto cleanup ;
}
} else { // DIB
// next load in the bitmap segment and test
if (!xpcBmpObject->m_xpDibDoc->OpenDocument(szPath)) {
iError = 103 ;
goto cleanup ;
}
}
}
cleanup:
if (pDC) {
ReleaseDC(pDC);
pDC = nullptr;
}
JXELEAVE(CMnkWindow::InitBitmapFilename) ;
RETURN(iError != 0) ;
}
//* CMnkWindow::SetBitmapCoordinates -- set coordinates of bitmap
bool CMnkWindow::SetBitmapCoordinates(
CBmpObject * xpcBmpObject)
// returns: true if error, false otherwise
{
JXENTER(CMnkWindow::SetBitmapCoordinates) ;
int iError = 0 ; // error code
CPitWnd * xpcPit = xpcBmpObject->m_xpcPit ;
//int iStoneNum = xpcBmpObject->m_iStoneNum ; // stone number
// for stone type
int iBmpType ;
int iX, iY ;
if ((iBmpType = xpcBmpObject->m_iBmpType) == BMT_STONE && xpcPit) {
if (xpcPit->m_iPit == HOMEINDEX)
iBmpType = (xpcPit->m_iPlayer == 0) ? BMT_RIGHTBIN
: BMT_LEFTBIN ;
else if (xpcPit->m_iPit == HANDINDEX)
iBmpType = BMT_HAND ;
else
iBmpType = BMT_PIT ;
}
switch (iBmpType) {
case BMT_MAIN:
iX = iY = 0 ;
break ;
case BMT_SCROLL:
iX = SCROLL_BUTTON_X, iY = SCROLL_BUTTON_Y ;
break ;
case BMT_LEFTBIN:
iX = 38, iY = 253 ;
break ;
case BMT_RIGHTBIN:
iX = 531, iY = 278 ;
break ;
case BMT_PIT:
if (xpcPit->m_iPlayer == 0)
iX = 464 - 72 * xpcPit->m_iPit, iY = 323 ;
else
iX = 114 + 68 * xpcPit->m_iPit, iY = 241 ;
break ;
case BMT_SIGN:
switch (xpcBmpObject->m_iBmpArg) {
case SBT_CRAB:
iX = 255, iY = 148 ;
break ;
case SBT_MYTURN:
case SBT_IWIN:
iX = 307, iY = 152 ;
break ;
case SBT_YOURTURN:
case SBT_YOUWIN:
case SBT_TIE:
iX = 255, iY = 151 ;
break ;
default:
iError = 100 ;
goto cleanup ;
// break ;
}
break ;
case BMT_HAND:
if (xpcPit->m_iPlayer == 0)
iX = 496 - 6 * xpcBmpObject->m_iStoneNum, iY = 430 ;
else
iX = 371 + 6 * xpcBmpObject->m_iStoneNum, iY = 227 ;
break ;
default:
iError = 101 ;
goto cleanup ;
// break ;
}
if (xpcBmpObject->m_iBmpType == BMT_STONE && iBmpType != BMT_HAND)
iX += xpcBmpObject->m_xpcPit->m_cBmpObject.m_cSize.cx / 2,
iY += xpcBmpObject->m_xpcPit->m_cBmpObject.m_cSize.cy / 2 ;
xpcBmpObject->m_cPosition = CPoint(iX, iY) ;
cleanup:
JXELEAVE(CMnkWindow::SetBitmapCoordinates) ;
RETURN(iError != 0) ;
}
//* CMnkWindow::AcceptClick -- process a mouse click by user
bool CMnkWindow::AcceptClick(CPoint cClickPoint)
// cClickPoint -- position of mouse when click (button up) occurred
// returns: true if error, false otherwise
{
JXENTER(CMnkWindow::AcceptClick) ;
int iError = 0 ; // error code
static bool bActive = false ; // prevent recursion
int iPlayer, iPit ;
bool bFound = false ; // found pit clicked on
CBmpObject * xpcBmpObject ; // bitmap object clicked on
CPitWnd * xpcPit = nullptr; // pit clicked on
CMove *xpcMove = &m_cCurrentMove ; // current move/position
//bool bPlayerSwitched;
MSG msg;
if (bActive) // prevent recursive call
goto exit ;
while (MFC::PeekMessage(&msg, m_hWnd, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)); //flush out pending mouse clicks
bActive = true ;
//bPlayerSwitched = false;
if (CRect(m_cBmpScroll.m_cPosition,
m_cBmpScroll.m_cSize).PtInRect(cClickPoint)) {
if (!m_bInMenu)
OptionsDialog() ;
} else {
iPlayer = xpcMove->m_iPlayer ; // only permit user to
// click on current player's pits
for (iPit = 0 ; !m_bGameOver &&
!bFound && iPit < NUMPITS ; bFound || ++iPit) {
xpcPit = m_xpcPits[iPlayer][iPit + 2] ; // point to pit
xpcBmpObject = &xpcPit->m_cBmpObject ;
if (CRect(xpcBmpObject->m_cPosition, xpcBmpObject->m_cSize).PtInRect(cClickPoint))
bFound = true ;
}
if (bFound) {
if (CMnk::Move((CPit *)xpcPit))
// if found, make the move, test for error
{
iError = 100 ;
goto cleanup ;
}
if (xpcMove->m_iPlayer) gCount++;
// increment count only if it's now the computer player, so it's turn based. (Move toggles turns internally)
if (!(gCount < 5 || (gCount % 4 == 0)) && (gCount < 15)) //Play Igo, YouGo,etc. crap only ...
// when for the first 4 turns of each player and
//thereafter every 4th turn until it's 28 turns in all and never thereafter.
gbTurnSoundsOff = true; // "Just Shut up, Crab".
else
gbTurnSoundsOff = false;
}
SetCrabSign() ;
// next loop as long as it's the computer's move
while (!m_bGameOver && m_bComputer[xpcMove->m_iPlayer]
&& !((CMnk *)this)->SearchMove(xpcMove, iPit))
// loop as long as game is not over, it's the computer's
// move, and SearchMove succeeds in finding a move
{
xpcPit = m_xpcPits[xpcMove->m_iPlayer][iPit + 2] ; // pt to pit object
if (((CMnk *)this)->Move((CPit *)xpcPit))
// make the move, test for error
{
iError = 110 ;
goto cleanup ;
}
}
SetCrabSign() ;
}// end else (if CRect(m_cBmpScroll...)
cleanup:
bActive = false ;
exit:
JXELEAVE(CMnkWindow::AcceptClick) ;
RETURN(iError != 0) ;
}
//* CMnkWindow::MoveStoneDisplay -- move a stone from pit to another
bool CMnkWindow::MoveStoneDisplay(CPitWnd * xpcFromPit,
CPitWnd * xpcToPit)
// xpcFromPit -- source pit (where stone comes from)
// xpcToPit -- target pit (where stone goes)
// returns: true if error, false otherwise
{
JXENTER(CMnkWindow::MoveStoneDisplay) ;
int iError = 0 ; // error code
CPoint cOldPosition, cNewPosition ; // old/new positions of stone
CBmpObject * xpcStone ; // stone being moved
// CBmpObject cBmpObject ; // bitmap object for stone
int iXDiff, iYDiff ; // coordinate differences
int iK ; // loop variable
CDC *pDC = GetDC();
if ((xpcStone = xpcFromPit->m_xpcStoneChain)) // get last
// stone from pit stone chain, if any
{
xpcFromPit->m_xpcStoneChain = xpcStone->m_xpcNext ;
// remove stone from "from" pit chain
xpcStone->m_xpcSprite->RefreshBackground(pDC) ;
// erase the stone
// OutputDebugString("Stone from pit chain.\n") ;
} else if ((xpcStone = m_xpFreeStoneChain)) {
m_xpFreeStoneChain = xpcStone->m_xpcNext ;
// OutputDebugString("Stone from free chain.\n") ;
}
else if (!(xpcStone = new CBmpObject)) {
iError = 110 ; // stone allocation failed
goto cleanup ;
} else { // stone allocation succeeded
xpcStone->m_bHeap = true ; // stone is on the heap
xpcStone->m_bSprite = true ; // it's a sprite
// OutputDebugString("New stone allocated.\n") ;
}
xpcStone->m_xpcNext = nullptr ;
xpcStone->m_xpcPit = xpcFromPit ; // set stone's pit ptr
xpcStone->m_iStoneNum = --xpcFromPit->m_iNumStones ;
// store stone number
AdjustPitDisplay(xpcFromPit, true) ;
// adjust the "from" pit
PaintBitmapObject(xpcStone, BMT_STONE) ; // paint stone
cOldPosition = xpcStone->m_cPosition ; // save stone start position
xpcStone->m_xpcPit = xpcToPit ; // now move to the "to" pit
xpcStone->m_iStoneNum = xpcToPit->m_iNumStones++ ;
// increment number of stones,
// and make that new stone number
SetBitmapCoordinates(xpcStone) ; // get coordinates
// of stone at position in new pit
cNewPosition = xpcStone->m_cPosition ; // get new stone position
iXDiff = cNewPosition.x - cOldPosition.x ;
iYDiff = cNewPosition.y - cOldPosition.y ;
//m_cTimeUtil.DelayMs(30) ; // delay 30 ms
//Sleep(30);
for (iK = 1 ; iK <= 5 ; ++iK) {
if (!xpcStone->m_xpcSprite->PaintSprite(pDC,
cOldPosition.x + iXDiff * iK / 5, cOldPosition.y + iYDiff * iK / 5))
// move stone sprite to new location, and test
{
iError = 102 ;
goto cleanup ;
}
MSG msg;
if (MFC::PeekMessage(&msg, nullptr, MM_MCINOTIFY, MM_MCINOTIFY, PM_REMOVE)) {
MFC::TranslateMessage(&msg);
MFC::DispatchMessage(&msg);
}
if (iK != 5) Sleep(10); //delay 10 ms except for the last one,
//so that the shell does not "fly" off the board.
}
if (xpcToPit->m_cBmpObject.m_bDummy) {
xpcStone->m_xpcNext = xpcToPit->m_xpcStoneChain ;
// make it the last stone (front of the chain) for
// the "to" pit
xpcToPit->m_xpcStoneChain = xpcStone ;
} else {
xpcStone->m_xpcSprite->RefreshBackground(pDC) ;
// erase the stone
xpcStone->m_xpcNext = m_xpFreeStoneChain ;
// put stone on the free chain
m_xpFreeStoneChain = xpcStone ;
AdjustPitDisplay(xpcToPit, true) ;
// adjust the "to" pit
}
cleanup:
if (pDC) {
ReleaseDC(pDC);
pDC = nullptr;
}
JXELEAVE(CMnkWindow::MoveStoneDisplay) ;
RETURN(iError != 0) ;
}
//* CMnkWindow::AdjustPitDisplay -- adjust display of pit when
// number of stones changes
bool CMnkWindow::AdjustPitDisplay(CPitWnd * xpcPit,
bool bForcePaint)
// xpcPit -- pit whose display is to be adjusted
// bForcePaint -- if true, then always paint
// returns: true if error, false otherwise
{
JXENTER(CMnkWindow::AdjustPitDisplay) ;
int iError = 0 ; // error code
CBmpObject * xpcBmpObject = &xpcPit->m_cBmpObject ;
int iDispStones ; // number of stones to display
CRect cBitmapRect ;
MSG msg;
if (MFC::PeekMessage(&msg, nullptr, MM_MCINOTIFY, MM_MCINOTIFY, PM_REMOVE)) {
MFC::TranslateMessage(&msg);
MFC::DispatchMessage(&msg);
}
iDispStones = xpcPit->m_iNumStones ;
if (iDispStones > xpcPit->m_iDispMax) // if this is more
// than the max number of stones we can
// display discretely
iDispStones = xpcPit->m_iDispMax + 1 ; // then use "many"
if (iDispStones != xpcPit->m_iDispStones) // if we're changing the
// number of stones
ClearBitmapObject(xpcBmpObject) ; // clear
// bitmap resources, if any
if (bForcePaint || iDispStones != xpcPit->m_iDispStones)
// if we have to repaint
{
PaintBitmapObject(xpcBmpObject) ;
xpcPit->m_iDispStones = iDispStones ;
}
JXELEAVE(CMnkWindow::AdjustPitDisplay) ;
RETURN(iError != 0) ;
}
//* CMnkWindow::PaintScreen -- paint screen for mankala game
void CMnkWindow::PaintScreen()
// returns: void
{
JXENTER(CMnkWindow::PaintScreen) ;
int iPlayer, iPit ; // loop variables
CPitWnd * xpcPit ; // pit variable
CBmpObject * xpcStone ; // stones in chain
PaintBitmapObject(&m_cBmpMain, BMT_MAIN) ;
// paint main screen bitmap
if (!m_bInMenu /*&& !m_bJustStarted*/) { // if not in scroll menu
m_cBmpScroll.m_bSprite = true ; // scroll is a sprite
PaintBitmapObject(&m_cBmpScroll, BMT_SCROLL) ;
}
for (iPlayer = 0 ; iPlayer < NUMPLAYERS ; ++iPlayer)
for (iPit = -2 ; iPit < NUMPITS ; ++iPit) {
xpcPit = m_xpcPits[iPlayer][iPit + 2] ;
// point to pit
if (xpcPit->m_iNumStones > 0)
AdjustPitDisplay(xpcPit, true) ;
// if (xpcPit->m_cBmpObject.m_lpDib)
// PaintBitmap(xpcPit->m_cBmpObject) ;
// ****** unfortunately, this chain must be traversed
// in reverse order ******* bug to be fixed
for (xpcStone = xpcPit->m_xpcStoneChain ; xpcStone ;
xpcStone = xpcStone->m_xpcNext)
// loop through stone chain for this pit
PaintBitmapObject(xpcStone) ;
}
m_iBmpSign = 0 ;
SetCrabSign() ;
JXELEAVE(CMnkWindow::PaintScreen) ;
}
//* CMnkWindow::AllocatePits -- allocate pits (including home bin/hand)
bool CMnkWindow::AllocatePits()
// returns: true if error, false otherwise
{
JXENTER(CMnkWindow::AllocatePits) ;
int iError = 0 ; // error code
int iPlayer, iPit ; // loop variables
CPitWnd * xpcPit ; // pointer to latest pit
for (iPlayer = 0 ; iPlayer < NUMPLAYERS ; ++iPlayer)
for (iPit = -2 ; iPit < NUMPITS ; ++iPit) {
if (!(xpcPit = new CPitWnd))
// allocate a pit and test
{
iError = 100 ; // new CPitWnd failed
goto cleanup ;
}
xpcPit->m_iPlayer = iPlayer ;
xpcPit->m_iPit = iPit ;
m_xpcPits[iPlayer][iPit + 2] = xpcPit ;
CMnk::m_xpcPits[iPlayer][iPit + 2] = (CPit *)xpcPit ;
xpcPit->m_cBmpObject.m_xpcPit = xpcPit ;
xpcPit->m_cBmpObject.m_iBmpType =
(iPit == HANDINDEX) ? BMT_HAND
: (iPit != HOMEINDEX) ? BMT_PIT
: (iPlayer == 0) ? BMT_RIGHTBIN : BMT_LEFTBIN ;
xpcPit->m_cBmpObject.m_bDummy =
(xpcPit->m_cBmpObject.m_iBmpType == BMT_HAND) ;
xpcPit->m_iDispMax = (iPit == HANDINDEX) ? MAXDISPHAND
: (iPit == HOMEINDEX) ? MAXDISPHOME
: MAXDISPPIT ;
}
cleanup:
JXELEAVE(CMnkWindow::AllocatePits) ;
RETURN(iError != 0) ;
}
// CMnkWindow::SetCrabSign -- to my/your turn
bool CMnkWindow::SetCrabSign(bool bPaint)
// bPaint -- paint the new sign
// returns: true if error, false otherwise
{
JXENTER(CMnkWindow::SetCrabSign) ;
int iError = 0; // error code
int iBmpSign; // SBT_xxxx -- sign to display
char *npszHumanScore;
char *npszCrabScore;
HLOCAL hlocHumanScore, hlocCrabScore;
if (m_bStartGame) {
iBmpSign = 0 ;
} else if (m_bGameOver) {
/*
equal stones in home bins; TIE: you HAVE to beat the crab to win
*/
if (m_cCurrentMove.m_iNumStones[0][HOMEINDEX + 2]
== m_cCurrentMove.m_iNumStones[1][HOMEINDEX + 2]) {
iBmpSign = SBT_TIE ;
if (!m_bGameOverMusicPlayed) { // so that FANFARE2/ SOSORRY is played just once.
m_bGameWon = false;
if (pGameParams->bSoundEffectsEnabled)
sndPlaySound(SOSORRY, SND_ASYNC);
m_bGameOverMusicPlayed = true;
}
}
/*
you win if you have more stones
*/
if (m_cCurrentMove.m_iNumStones[0][HOMEINDEX + 2]
> m_cCurrentMove.m_iNumStones[1][HOMEINDEX + 2]) {
iBmpSign = SBT_YOUWIN ;
if (!m_bGameOverMusicPlayed) { // so that FANFARE2/ SOSORRY is played just once.
m_bGameWon = true;
if (pGameParams->bSoundEffectsEnabled)
sndPlaySound(FANFARE2, SND_SYNC);
m_bGameOverMusicPlayed = true;
}
} else {
/*
you lose if you have fewer stones
*/
iBmpSign = SBT_IWIN ;
if (!m_bGameOverMusicPlayed) { // so that FANFARE2/ SOSORRY is played just once.
m_bGameWon = false;
if (pGameParams->bSoundEffectsEnabled)
sndPlaySound(SOSORRY, SND_SYNC);
m_bGameOverMusicPlayed = true;
}
}
}//end if m_hbGameOver // m_bStartGame
else if (m_cCurrentMove.m_iPlayer == 0)
iBmpSign = SBT_YOURTURN ;
else
iBmpSign = SBT_MYTURN ;
if (iBmpSign && m_cBmpSign.m_iBmpArg
&& iBmpSign != m_cBmpSign.m_iBmpArg) {
ClearBitmapObject(&m_cBmpSign) ; // clear old sign bitmap
m_cBmpSign.m_iBmpArg = 0 ;
}
if (m_iBmpSign && m_iBmpSign != iBmpSign) {
PaintBitmapObject(&m_cBmpCrab, BMT_SIGN, SBT_CRAB) ;
m_iBmpSign = 0 ;
}
if (bPaint && iBmpSign && iBmpSign != m_iBmpSign) {
PaintBitmapObject(&m_cBmpSign, BMT_SIGN, iBmpSign) ;
m_iBmpSign = iBmpSign ;
}
if (m_bGameOver) {
if (!m_bScoresDisplayed) {
/*display scores in stand alone mode*/
hlocHumanScore = LocalAlloc(LHND, 32);
hlocCrabScore = LocalAlloc(LHND, 32);
if (hlocHumanScore && hlocCrabScore) {
npszHumanScore = (char *)LocalLock(hlocHumanScore);
npszCrabScore = (char *)LocalLock(hlocCrabScore);
Common::sprintf_s(npszHumanScore, 32, "Your Score: %d shell%c", m_cCurrentMove.m_iNumStones[0][HOMEINDEX + 2], (m_cCurrentMove.m_iNumStones[0][HOMEINDEX + 2] > 1) ? 's' : 0);
Common::sprintf_s(npszCrabScore, 32, "My Score: %d shell%c", m_cCurrentMove.m_iNumStones[1][HOMEINDEX + 2], (m_cCurrentMove.m_iNumStones[1][HOMEINDEX + 2] > 1) ? 's' : 0);
CMessageBox(this, m_xpGamePalette, npszHumanScore, npszCrabScore);
LocalUnlock(hlocHumanScore);
LocalUnlock(hlocCrabScore);
}// end if hlocHumanScore and hlocCrabScore.
if (hlocHumanScore) LocalFree(hlocHumanScore);
if (hlocCrabScore) LocalFree(hlocCrabScore);
m_bScoresDisplayed = true;
} // end if m_bScoredisplayed
if (pGameParams->bPlayingMetagame) {
/*auto exit upon Game Over, in Meta mode.*/
//::Sleep(2000); // prolong the display of win /lose sign.
//UpdateWindow();
pGameParams->lScore = m_bGameWon; //do the same things as in OptionsDialog() with ...
PostMessage(WM_CLOSE, 0, 0L); //IDC_OPTIONS_QUIT
} // end if pGameParams->bPlayingMetagame.
} // end if m_bGameOver.
JXELEAVE(CMnkWindow::SetCrabSign) ;
RETURN(iError != 0) ;
}
//* CMnkWindow::FreePitResources -- free (optionally delete) all pit
// resources -- stone sprites and pit bitmaps
bool CMnkWindow::FreePitResources(bool bDelete)
// bDelete -- if true, then delete all stone sprites
// returns: true if error, false otherwise
{
JXENTER(CMnkWindow::FreePitResources) ;
int iError = 0 ; // error code
int iPlayer, iPit ; // loop variables
CPitWnd * xpcPit ; // pit variable
CBmpObject * xpcLastStone, *xpcNextStone ; // stones in chain
for (iPlayer = 0 ; iPlayer < NUMPLAYERS ; ++iPlayer)
for (iPit = -2 ; iPit < NUMPITS ; ++iPit) {
xpcPit = m_xpcPits[iPlayer][iPit + 2] ;
if ((xpcLastStone = xpcPit->m_xpcStoneChain))
// if pit has stone chain
{
while ((xpcNextStone = xpcLastStone->m_xpcNext))
// loop to end of stone chain
xpcLastStone = xpcNextStone ; // move to next
xpcLastStone->m_xpcNext = m_xpFreeStoneChain ;
// add new chain to free chain
m_xpFreeStoneChain = xpcPit->m_xpcStoneChain ;
xpcPit->m_xpcStoneChain = nullptr ;
// no more chain on pit
}
ClearBitmapObject(&xpcPit->m_cBmpObject) ;
// delete xpcPit;
}
if (bDelete) // if we're deleting
while ((xpcNextStone = m_xpFreeStoneChain))
// get first/next stone sprite in chain
{
m_xpFreeStoneChain = xpcNextStone->m_xpcNext ;
// unlink it from stone sprite chain
(*(CSprite *)xpcNextStone).UnlinkSprite(); // ... unlinking it
// unlink it from sprite chain
delete xpcNextStone ; // delete stone sprite
}
JXELEAVE(CMnkWindow::FreePitResources) ;
RETURN(iError != 0) ;
}
//* CMnkWindow::ClearBitmapObject -- release bitmap object
bool CMnkWindow::ClearBitmapObject(CBmpObject * xpcBmpObject)
// xpcBmpObject -- pointer to bitmap object
// returns: true if error, false otherwise
{
JXENTER(CMnkWindow::ClearBitmapObject) ;
int iError = 0 ; // error code
HDIB hDib ;
if (xpcBmpObject->m_xpcSprite) { // if there's a sprite
delete xpcBmpObject->m_xpcSprite ; // free it
xpcBmpObject->m_xpcSprite = nullptr ; // no more pointer
}
if (xpcBmpObject->m_xpDibDoc) { // if there's a DIB document
if (xpcBmpObject->m_lpDib &&
(hDib = xpcBmpObject->m_xpDibDoc->GetHDIB())) {
GlobalUnlock(hDib) ; // unlock Dib memory
}
xpcBmpObject->m_lpDib = nullptr ; // clear pointers
delete xpcBmpObject->m_xpDibDoc ;
xpcBmpObject->m_xpDibDoc = nullptr ;
}
xpcBmpObject->m_bInit = false ; // object no longer initialized
JXELEAVE(CMnkWindow::ClearBitmapObject) ;
RETURN(iError != 0) ;
}
//* CMnkWindow::ReleaseResources -- release all resources before term
void CMnkWindow::ReleaseResources() {
CBmpObject * xpcBmpObject ; // objects to be freed
CPitWnd* xpcPitWnd;
while ((xpcBmpObject = m_xpcBmpFreeChain)) {
m_xpcBmpFreeChain = xpcBmpObject->m_xpcNextFree ;
xpcBmpObject->m_xpcNextFree = nullptr ; // out of habit
ClearBitmapObject(xpcBmpObject) ; // free resources
if (xpcBmpObject->m_bHeap) // if on the heap
delete xpcBmpObject ;
}
for (int i = 0 ; i < NUMPLAYERS ; ++i)
for (int j = -2 ; j < NUMPITS ; ++j) {
if ((xpcPitWnd = m_xpcPits[i][j + 2]))
delete xpcPitWnd;
xpcPitWnd = nullptr;
}
if (m_pSound) {
m_pSound->stop();
delete m_pSound;
m_pSound = nullptr;
}
}
//* CMnkWindow::DebugDialog -- put up debugging dialog box
bool CMnkWindow::DebugDialog()
// returns: true if error, false otherwise
{
JXENTER(CMnkWindow::DebugDialog) ;
int iError = 0 ; // error code
CMnkOpt cMnkOpt ; // options dialog class object
// initialize the options dialog values
cMnkOpt.m_iStartStones = m_iStartStones ;
cMnkOpt.m_iLevel0 = m_eLevel[0] - 1 ;
cMnkOpt.m_iLevel1 = m_eLevel[1] - 1 ;
cMnkOpt.m_iPlayer0 = m_bComputer[0] ;
cMnkOpt.m_iPlayer1 = m_bComputer[1] ;
cMnkOpt.m_iTableStones = m_iTableStones ;
cMnkOpt.m_bInitData = m_bInitData ;
cMnkOpt.m_iMaxDepth0 = m_iMaxDepth[0] ;
cMnkOpt.m_iMaxDepth1 = m_iMaxDepth[1] ;
cMnkOpt.m_iCapDepth0 = m_iCapDepth[0] ;
cMnkOpt.m_iCapDepth1 = m_iCapDepth[1] ;
cMnkOpt.m_bDumpPopulate = m_bDumpPopulate ;
cMnkOpt.m_bDumpMoves = m_bDumpMoves ;
cMnkOpt.m_bDumpTree = m_bDumpTree ;
if (cMnkOpt.DoModal() == IDOK) {
m_iStartStones = cMnkOpt.m_iStartStones ;
m_eLevel[0] = (enum_Level)(cMnkOpt.m_iLevel0 + 1) ;
m_eLevel[1] = (enum_Level)(cMnkOpt.m_iLevel1 + 1) ;
m_bComputer[0] = cMnkOpt.m_iPlayer0 ;
m_bComputer[1] = cMnkOpt.m_iPlayer1 ;
m_iTableStones = cMnkOpt.m_iTableStones ;
m_bInitData = cMnkOpt.m_bInitData ;
m_iMaxDepth[0] = cMnkOpt.m_iMaxDepth0 ;
m_iMaxDepth[1] = cMnkOpt.m_iMaxDepth1 ;
m_iCapDepth[0] = cMnkOpt.m_iCapDepth0 ;
m_iCapDepth[1] = cMnkOpt.m_iCapDepth1 ;
m_bDumpPopulate = cMnkOpt.m_bDumpPopulate ;
m_bDumpMoves = cMnkOpt.m_bDumpMoves ;
m_bDumpTree = cMnkOpt.m_bDumpTree ;
}
JXELEAVE(CMnkWindow::DebugDialog) ;
RETURN(iError != 0) ;
}
//* CMnkWindow::UserDialog -- put up user dialog box
bool FAR PASCAL CMnkWindow::UserDialog()
// returns: true if error, false otherwise
{
JXENTER(CMnkWindow::UserDialog) ;
int iError = 0 ; // error code
CMnkUsr cMnkUsr(this, m_xpGamePalette) ;
// options dialog class object
// initialize the options dialog values
cMnkUsr.m_iUShells = m_iStartStones ;
cMnkUsr.m_iUStrength = m_eLevel[0] ;
if (cMnkUsr.DoModal() == IDOK) {
m_iStartStones = cMnkUsr.m_iUShells ;
m_eLevel[0] = m_eLevel[1] = (enum_Level)cMnkUsr.m_iUStrength ;
}
JXELEAVE(CMnkWindow::UserDialog) ;
RETURN(iError != 0) ;
}
////* CallUserDialog --
void CALLBACK CallUserDialog(CWnd * xpcWindow) {
// returns: true if error, false otherwise
JXENTER(CallUserDialog) ;
CMnkWindow * xpcMnkWindow = (CMnkWindow *)xpcWindow->GetParent();
xpcMnkWindow->UserDialog() ;
JXELEAVE(CallUserDialog) ;
}
//* CMnkWindow::OptionsDialog -- call options dialog
bool CMnkWindow::OptionsDialog() {
// returns: true if error, false otherwise
JXENTER(CMnkWindow::OptionsDialog) ;
int iError = 0 ; // error code
CDC *pDC = nullptr ;
if (!m_bInMenu) { // prevent recursion
m_bInMenu = true ; // in the options menu now
if ((pDC = GetDC())) {
m_cBmpScroll.m_xpcSprite->EraseSprite(pDC) ;
// hide the command scroll
ReleaseDC(pDC);
pDC = nullptr;
}
/*
set up main menu:
If in MetaGame mode disable newgame and options buttons and on start up
disable Continue button
*/
CMainMenu cOptionsDlg(this, m_xpGamePalette,
(pGameParams->bPlayingMetagame ? NO_NEWGAME | NO_OPTIONS : false) | (m_bStartGame ? NO_RETURN : 0X0),
CallUserDialog, RULES, pGameParams->bSoundEffectsEnabled ? RULES_NARRATION : nullptr, pGameParams) ;
switch (cOptionsDlg.DoModal()) {
case IDC_OPTIONS_NEWGAME:
if (!pGameParams->bPlayingMetagame) StartGame() ;
break ;
case IDC_OPTIONS_QUIT:
pGameParams->lScore = (long)m_bGameWon;
m_bGameOver = true ;
PostMessage(WM_CLOSE, 0, 0);
break ;
default:
break ;
}
PaintBitmapObject(&m_cBmpScroll, BMT_SCROLL) ;
//
// Check to see if the music state was changed and adjust to match it
//
if ((pGameParams->bMusicEnabled == false) && (m_pSound != nullptr)) {
if (m_pSound->playing())
m_pSound->stop();
} else if (pGameParams->bMusicEnabled) {
if (m_pSound == nullptr) {
m_pSound = new CSound(this, MIDI_BCKGND, SOUND_MIDI | SOUND_DONT_LOOP_TO_END);
}
if (m_pSound != nullptr) {
if (!m_pSound->playing())
m_pSound->midiLoopPlaySegment(1004L, 34040L, 1004L, FMT_MILLISEC);
}
}
m_bInMenu = false ;
}
/* cleanup */
JXELEAVE(CMnkWindow::OptionsDialog) ;
RETURN(iError != 0) ;
}
} // namespace Mankala
} // namespace HodjNPodj
} // namespace Bagel