Files
scummvm-cursorfix/engines/bagel/hodjnpodj/artparts/artparts.cpp
2026-02-02 04:50:13 +01:00

1995 lines
64 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/boflib/sound.h"
#include "bagel/boflib/misc.h"
#include "bagel/hodjnpodj/hnplibs/gamedll.h"
#include "bagel/hodjnpodj/artparts/resource.h"
#include "bagel/hodjnpodj/artparts/globals.h"
#include "bagel/hodjnpodj/hnplibs/mainmenu.h"
#include "bagel/hodjnpodj/hnplibs/cmessbox.h"
#include "bagel/hodjnpodj/artparts/artparts.h"
#include "bagel/hodjnpodj/hnplibs/rules.h"
#include "bagel/hodjnpodj/hnplibs/bitmaps.h"
#include "bagel/hodjnpodj/artparts/optndlg.h"
#include "bagel/hodjnpodj/hodjnpodj.h"
namespace Bagel {
namespace HodjNPodj {
namespace ArtParts {
#define FONT_SIZE 14
bool InArtRegion(CPoint point);
CPoint WinToArt(CPoint point);
extern HWND ghParentWnd;
extern LPGAMESTRUCT pGameInfo;
CPalette *CMainWindow::pGamePalette;
int CMainWindow::nSeconds;
int CMainWindow::nMinutes;
int CMainWindow::nLastPick;
int CMainWindow::m_nTime;
int CMainWindow::m_nRows;
int CMainWindow::m_nColumns;
int CMainWindow::m_nWidth;
int CMainWindow::m_nHeight;
float CMainWindow::m_nScore;
bool CMainWindow::bFramed;
int CMainWindow::tempTime;
int CMainWindow::tempRows;
int CMainWindow::tempColumns;
bool CMainWindow::tempFramed;
/////////////////////////////////////////////////////////////////////////////
/**
* Dummy class for the Art Parts demo to map art/ and sound/ subfolders to current folder
*/
class ArtSoundArchive : public Common::Archive {
public:
bool hasFile(const Common::Path &path) const override {
Common::String pathStr = path.toString();
if (pathStr.hasPrefixIgnoreCase("art/")) {
pathStr = pathStr.c_str() + 4;
} else if (pathStr.hasPrefixIgnoreCase("sound/")) {
pathStr = pathStr.c_str() + 6;
} else {
return false;
}
return Common::File::exists(pathStr.c_str());
}
int listMembers(Common::ArchiveMemberList &list) const override {
return 0;
}
const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override {
return Common::ArchiveMemberPtr();
}
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override {
Common::String pathStr = path.toString();
if (pathStr.hasPrefixIgnoreCase("art/")) {
pathStr = pathStr.c_str() + 4;
} else if (pathStr.hasPrefixIgnoreCase("sound/")) {
pathStr = pathStr.c_str() + 6;
} else {
return nullptr;
}
Common::File f;
if (f.open(pathStr.c_str()))
return f.readStream(f.size());
return nullptr;
}
};
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// CMainWindow constructor:
// Create the window with the appropriate style, size, menu, etc.;
// it will be later revealed by CTheApp::InitInstance(). Then
// create our splash screen object by opening and loading its DIB.
//
CMainWindow::CMainWindow() {
CString WndClass;
CRect MainRect, tmpRect;
CDC *pDC = nullptr;
initStatics();
BeginWaitCursor();
if (g_engine->isDemo())
SearchMan.add("artparts_demo", new ArtSoundArchive());
// Define a special window class which traps double-clicks, is byte aligned
// to maximize BITBLT performance, and creates "owned" DCs rather than sharing
// the five system defined DCs which are not guaranteed to be available;
// this adds a bit to our app size but avoids hangs/freezes/lockups.
WndClass = AfxRegisterWndClass(CS_BYTEALIGNWINDOW | CS_OWNDC,
nullptr,
nullptr,
nullptr);
// Center our window on the screen
pDC = GetDC();
MainRect.left = (pDC->GetDeviceCaps(HORZRES) - GAME_WIDTH) >> 1;
MainRect.top = (pDC->GetDeviceCaps(VERTRES) - GAME_HEIGHT) >> 1;
MainRect.right = MainRect.left + GAME_WIDTH;
MainRect.bottom = MainRect.top + GAME_HEIGHT;
// Create the window as a POPUP so no boarders, title, or menu are present;
// this is because the game's background art will fill the entire 640x480 area.
Create(WndClass, "Boffo Games -- Art Parts", WS_POPUP, MainRect, nullptr, 0);
pScratch1 = new CBitmap(); // Scratch1 is the source for copying
pScratch1DC = new CDC();
pScratch2 = new CBitmap(); // Scratch2 is the destination for copying
pScratch2DC = new CDC();
pScratch1->CreateCompatibleBitmap(pDC, ART_WIDTH, ART_HEIGHT); // Set up Scratch1
pScratch1DC->CreateCompatibleDC(pDC); //...bitmap and DC
pOldBmp1 = pScratch1DC->SelectObject(pScratch1);
pScratch2->CreateCompatibleBitmap(pDC, ART_WIDTH, ART_HEIGHT); // Set up Scratch2
pScratch2DC->CreateCompatibleDC(pDC); //...bitmap and DC
pOldBmp2 = pScratch2DC->SelectObject(pScratch2);
//srand((unsigned) time(nullptr)); // seed the random number generator
InitValues(); // Set the default values of global variables
m_bPlaying = false;
if (LoadArtWork() == false) { // Load first artwork & display
PostMessage(WM_CLOSE, 0, 0);
EndWaitCursor();
return;
}
// Build Scroll Command button
m_pScrollButton = new CBmpButton;
ASSERT(m_pScrollButton != nullptr);
tmpRect.SetRect(SCROLL_BUTTON_X, SCROLL_BUTTON_Y,
SCROLL_BUTTON_X + SCROLL_BUTTON_DX - 1,
SCROLL_BUTTON_Y + SCROLL_BUTTON_DY - 1);
bSuccess = (*m_pScrollButton).Create(nullptr, BS_OWNERDRAW | WS_CHILD | WS_VISIBLE, tmpRect, this, IDC_SCROLL);
ASSERT(bSuccess);
bSuccess = (*m_pScrollButton).LoadBitmaps(SCROLLUP, SCROLLDOWN, SCROLLUP, SCROLLUP);
ASSERT(bSuccess);
m_bIgnoreScrollClick = false;
// Put up something in the meantime
//
ShowWindow(SW_SHOWNORMAL);
SplashScreen();
pLocaleBitmap = FetchResourceBitmap(pDC, nullptr, "IDB_LOCALE_BMP");
ASSERT(pLocaleBitmap != nullptr);
pBlankBitmap = FetchResourceBitmap(pDC, nullptr, "IDB_BLANK_BMP");
ASSERT(pBlankBitmap != nullptr);
tmpRect.SetRect(TIME_LOCATION_X, TIME_LOCATION_Y,
TIME_LOCATION_X + TIME_WIDTH, TIME_LOCATION_Y + TIME_HEIGHT);
if ((m_pTimeText = new CText()) != nullptr) {
(*m_pTimeText).SetupText(pDC, pGamePalette, &tmpRect, JUSTIFY_CENTER);
}
ReleaseDC(pDC);
MSG lpmsg;
while (PeekMessage(&lpmsg, nullptr, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE | PM_NOYIELD)) ;
EndWaitCursor();
if (pGameInfo->bPlayingMetagame) {
m_bPlaying = true;
if (pGameInfo->bMusicEnabled) {
pGameSound = new CSound(this, GAME_THEME, SOUND_MIDI | SOUND_LOOP | SOUND_DONT_LOOP_TO_END);
if (pGameSound != nullptr) {
(*pGameSound).midiLoopPlaySegment(2300, 32000, 0, FMT_MILLISEC);
} // end if pGameSound
}
} else {
if (pGameInfo->bMusicEnabled) {
pGameSound = new CSound(this, GAME_THEME, SOUND_MIDI | SOUND_LOOP | SOUND_DONT_LOOP_TO_END);
if (pGameSound != nullptr) {
(*pGameSound).midiLoopPlaySegment(2300, 32000, 0, FMT_MILLISEC);
} // end if pGameSound
}
PostMessage(WM_COMMAND, IDC_SCROLL, BN_CLICKED); // Activate the Options dialog
}
bStartOkay = true;
} //End of CMainWindow
CMainWindow::~CMainWindow() {
SearchMan.remove("artparts_demo");
}
void CMainWindow::initStatics() {
pGamePalette = nullptr;
nSeconds = MIN_TIME;
nMinutes = 0;
nLastPick = 0;
m_nTime = MIN_TIME;
m_nRows = MIN_ROWS;
m_nColumns = MIN_COLUMNS;
m_nWidth = ART_WIDTH / MIN_COLUMNS;
m_nHeight = ART_HEIGHT / MIN_ROWS;
m_nScore = 0.0;
bFramed = false;
tempTime = MIN_TIME;
tempRows = MIN_ROWS;
tempColumns = MIN_COLUMNS;
tempFramed = true;
}
// OnPaint:
// This is called whenever Windows sends a WM_PAINT message.
// Note that creating a CPaintDC automatically does a BeginPaint and
// an EndPaint call is done when it is destroyed at the end of this
// function. CPaintDC's constructor needs the window (this).
//
void CMainWindow::OnPaint() {
CDC *pDC; // Screen device cont.
CPalette *pPalOld = nullptr; // Old palette holder
PAINTSTRUCT lpPaint;
char msg[64];
pDC = GetDC(); // Get screen DC
pPalOld = (*pDC).SelectPalette(pGamePalette, false); // Select Game Palette
(*pDC).RealizePalette(); // Use it
InvalidateRect(nullptr, false); // invalidate the entire window
BeginPaint(&lpPaint);
SplashScreen();
if (m_bPlaying) { // only false when the options are displayed
SplashScratchPaint();
PaintBitmap(pDC, pGamePalette, pBlankBitmap, TIME_LOCATION_X, TIME_LOCATION_Y);
if (m_nTime == 0)
Common::sprintf_s(msg, "Time Used: %02d:%02d", nMinutes, nSeconds);
else {
Common::sprintf_s(msg, "Time Left: %02d:%02d", nMinutes, nSeconds);
}
(*m_pTimeText).DisplayString(pDC, msg, FONT_SIZE, FW_SEMIBOLD, OPTIONS_COLOR);
} else {
PaintBitmap(pDC, pGamePalette, pLocaleBitmap, TIME_LOCATION_X, TIME_LOCATION_Y);
}
EndPaint(&lpPaint);
(*pDC).SelectPalette(pPalOld, false); // Select back old palette
ReleaseDC(pDC); // Release the DC
if (bStartOkay && (bGameStarted == false)) {
MSG lpmsg;
while (PeekMessage(&lpmsg, nullptr, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE | PM_NOYIELD)) ;
bGameStarted = true;
bStartOkay = false;
}
}
/*****************************************************************
*
* SplashScratch()
*
* FUNCTIONAL DESCRIPTION:
*
* Paints the scratch bitmap on the screen
*
* FORMAL PARAMETERS:
*
* none
*
* IMPLICIT INPUT PARAMETERS:
*
* gets Screen DC, uses pScratchDC
*
* IMPLICIT OUTPUT PARAMETERS:
*
* Screen DC
*
* RETURN VALUE:
*
* void
*
****************************************************************/
void CMainWindow::SplashScratch() {
CDC *pDC; // Screen device cont.
CPalette *pPalOld = nullptr; // Old palette holder
pDC = GetDC(); // Get screen DC
pPalOld = (*pDC).SelectPalette(pGamePalette, false); // Select Game Palette
(*pDC).RealizePalette(); // Use it
pDC->BitBlt(SIDE_BORDER, TOP_BORDER, ART_WIDTH, ART_HEIGHT, pScratch2DC, 0, 0, SRCCOPY); // Draw Scratch2
(*pDC).SelectPalette(pPalOld, false); // Select back old palette
ReleaseDC(pDC); // Release the DC
pScratch1DC->BitBlt(0, 0, ART_WIDTH, ART_HEIGHT, pScratch2DC, 0, 0, SRCCOPY); // Copy New parts locations
//...to the Scratch1 bitmap
}
void CMainWindow::SplashScratchPaint() {
CRect rcDest;
CRect rcDIB;
CDC *pDC;
CDibDoc *pSourceDoc;
HDIB hDIB;
CPalette *pOldPalSource = nullptr,
*pOldPalScreen = nullptr;
CDC *pSourceDC;
CBitmap *pSource = nullptr,
*pOldBmp = nullptr;
pDC = GetDC();
pOldPalScreen = (*pDC).SelectPalette(pGamePalette, false); // Select Game Palette
(*pDC).RealizePalette(); // Use it
if (bFramed) {
pSourceDC = new CDC();
pSource = new CBitmap(); // Source is the original, in-tact artwork for framed mode
pSource->CreateCompatibleBitmap(pDC, ART_WIDTH + (2 * FRAME_WIDTH),
ART_HEIGHT + (2 * FRAME_HEIGHT)); // Set up Source
pSourceDC->CreateCompatibleDC(pDC); //...bitmap and DC
pOldBmp = pSourceDC->SelectObject(pSource);
pSourceDoc = new CDibDoc();
ASSERT(pSourceDoc != nullptr);
if (pSourceDoc == nullptr) {
return;
}
(*pSourceDoc).OpenDocument(szCurrentArt);
pOldPalSource = pSourceDC->SelectPalette(pGamePalette, false);
pSourceDC->RealizePalette();
hDIB = (*pSourceDoc).GetHDIB();
if (hDIB) {
rcDest.SetRect(0, 0, ART_WIDTH + (2 * FRAME_WIDTH), ART_HEIGHT + (2 * FRAME_HEIGHT));
int cxDIB = (int) DIBWidth(hDIB);
int cyDIB = (int) DIBHeight(hDIB);
if (cxDIB > ART_WIDTH) { // Center and crop
rcDIB.left = (cxDIB - ART_WIDTH) / 2; //...too wide art
rcDIB.right = rcDIB.left + ART_WIDTH;
} else { // Stretch too thin art
rcDIB.left = 0;
rcDIB.right = cxDIB;
}
if (cyDIB > ART_HEIGHT) { // Center and crop
rcDIB.top = (cyDIB - ART_HEIGHT) / 2; //...too long art
rcDIB.bottom = rcDIB.top + ART_HEIGHT;
} else { // Stretch too short art
rcDIB.top = 0;
rcDIB.bottom = cyDIB;
}
PaintDIB((*pSourceDC).m_hDC, &rcDest, hDIB, &rcDIB, pGamePalette);
pSourceDC->BitBlt(FRAME_WIDTH, FRAME_HEIGHT, ART_WIDTH, ART_HEIGHT, // Copy the Scrambled art to
pScratch2DC, 0, 0, SRCCOPY); //... the Frame + Art bitmap
pDC->BitBlt(SIDE_BORDER - FRAME_WIDTH, TOP_BORDER - FRAME_HEIGHT, // Copy the Frame + Scrambled
ART_WIDTH + (2 * FRAME_WIDTH), ART_HEIGHT + (2 * FRAME_HEIGHT), //...art to the screen
pSourceDC, 0, 0, SRCCOPY);
}
delete pSourceDoc;
pSourceDoc = nullptr;
if (pOldBmp != nullptr) // Get rid of Source
pSourceDC->SelectObject(pOldBmp);
if (pOldPalSource != nullptr)
pSourceDC->SelectPalette(pOldPalSource, false);
if (pSourceDC->m_hDC != nullptr) {
pSourceDC->DeleteDC();
delete pSourceDC;
}
pSource->DeleteObject();
delete pSource;
} else {
pDC->BitBlt(SIDE_BORDER, TOP_BORDER, ART_WIDTH, ART_HEIGHT, pScratch2DC, 0, 0, SRCCOPY); // Draw Scratch2
} //...on screen
(*pDC).SelectPalette(pOldPalScreen, false); // Select back old palette
ReleaseDC(pDC);
pScratch1DC->BitBlt(0, 0, ART_WIDTH, ART_HEIGHT, pScratch2DC, 0, 0, SRCCOPY); // Copy New parts locations
}
// Paint the background art (splash screen) in the client area;
// called by both OnPaint and InitInstance.
void CMainWindow::SplashScreen() {
CRect rcDest;
CRect rcDIB;
CDC *pDC;
CDibDoc myDoc;
HDIB hDIB;
pDC = GetDC();
myDoc.OpenDocument(TEXTSCREEN);
hDIB = myDoc.GetHDIB();
if (pDC && hDIB) {
GetClientRect(rcDest);
int cxDIB = (int)DIBWidth(hDIB);
int cyDIB = (int)DIBHeight(hDIB);
rcDIB.top = rcDIB.left = 0;
rcDIB.right = cxDIB;
rcDIB.bottom = cyDIB;
PaintDIB((*pDC).m_hDC, &rcDest, hDIB, &rcDIB, pGamePalette);
}
ReleaseDC(pDC);
}
/////////////////////////////////////////////////////////////////////////////
//
// Process messages and controls
//
/////////////////////////////////////////////////////////////////////////////
// OnCommand
// This function is called when a WM_COMMAND message is issued,
// typically in order to process control related activities.
//
bool CMainWindow::OnCommand(WPARAM wParam, LPARAM lParam) {
CPalette *pPalOld = nullptr;
CBrush *pBrushOld = nullptr,
*pBrushNew = nullptr;
CDC *pDC;
if ((HIWORD(lParam) == BN_CLICKED) && !m_bShowOutOfPlace) {
pDC = GetDC();
CRules RulesDlg((CWnd *)this, "artparts.txt", pGamePalette, pGameInfo->bSoundEffectsEnabled ? RULES_WAV : nullptr); // Construct Rules dialog
CMainMenu COptionsWind((CWnd *)this, pGamePalette,
pGameInfo->bPlayingMetagame ? (NO_NEWGAME | NO_OPTIONS) : 0,
GetSubOptions, "artparts.txt", pGameInfo->bSoundEffectsEnabled ? RULES_WAV : nullptr, pGameInfo) ; // Construct Option dialog
PaintBitmap(pDC, pGamePalette, pLocaleBitmap, TIME_LOCATION_X, TIME_LOCATION_Y);
switch (wParam) {
case IDC_RULES:
CSound::waitWaveSounds();
m_bIgnoreScrollClick = true;
(*m_pScrollButton).SendMessage(BM_SETSTATE, true, 0L);
RulesDlg.DoModal();
m_bIgnoreScrollClick = false;
(*m_pScrollButton).SendMessage(BM_SETSTATE, false, 0L);
break;
case IDC_SCROLL:
KillTimer(DISPLAY_TIMER); // Stop the Displayed Time timer
if (m_bIgnoreScrollClick) {
(*m_pScrollButton).SendMessage(BM_SETSTATE, true, 0L);
break;
}
m_bIgnoreScrollClick = true;
(*m_pScrollButton).SendMessage(BM_SETSTATE, true, 0L);
SendDlgItemMessage(IDC_SCROLL, BM_SETSTATE, true, 0L);
m_bPlaying = false; // Not playing the game
bSwitched = false; // Prevent ability to Undo after Command is done
CheckForWin(); // Get current score
if (bGameStarted) {
pPalOld = (*pDC).SelectPalette(pGamePalette, false); // Select in the artwork's palette
(*pDC).RealizePalette(); // Use it
pBrushNew = new CBrush(); // Construct a new brush object
if (pBrushNew != nullptr) { // If the constructor was successful:
pBrushNew->CreateSolidBrush(PALETTERGB(128, 0, 0)); // Create my backdrop color brush
pBrushOld = (*pDC).SelectObject(pBrushNew); // Select into the DC my new brush
pDC->SetROP2(R2_COPYPEN); // Set Draw mode to use the pen color
if (bFramed)
(*pDC).Rectangle(SIDE_BORDER - FRAME_WIDTH, TOP_BORDER - FRAME_HEIGHT,
GAME_WIDTH - (SIDE_BORDER - FRAME_WIDTH),
GAME_HEIGHT - (BOTTOM_BORDER - FRAME_HEIGHT));
else
(*pDC).Rectangle(SIDE_BORDER, TOP_BORDER, GAME_WIDTH - SIDE_BORDER, GAME_HEIGHT - BOTTOM_BORDER);
(*pDC).SelectObject(pBrushOld); // Select in the old brush
(*pDC).SelectPalette(pPalOld, false); // Select in the old palette
delete pBrushNew; // Delete the new brush
}
} // end if bGameStarted
CSound::clearWaveSounds();
switch (COptionsWind.DoModal()) {
case IDC_OPTIONS_NEWGAME: // Selected New Game
(*m_pScrollButton).SendMessage(BM_SETSTATE, false, 0L);
m_bIgnoreScrollClick = false;
if (!pGameInfo->bPlayingMetagame)
NewGame();
//if m_nScore == 100, randomly put it in order, so start over
// m_bNewGame = false; PostMessage( IDC_OPTIONS_NEWGAME ...);break;
break;
case IDC_OPTIONS_RETURN:
(*m_pScrollButton).SendMessage(BM_SETSTATE, false, 0L);
m_bIgnoreScrollClick = false;
m_bPlaying = true;
if (bGameStarted && (m_nTime != (nSeconds + (nMinutes * 60)))) { // have started
SetTimer(DISPLAY_TIMER, CLICK_TIME, nullptr); // Set timer
}
break;
case IDC_OPTIONS_QUIT: // Quit button was clicked
PostMessage(WM_CLOSE, 0, 0); // and post a program exit
ReleaseDC(pDC);
return false;
} //end switch(ComDlg.DoModal())
//
// Check to see if the music state was changed and adjust to match it
//
if ((pGameInfo->bMusicEnabled == false) && (pGameSound != nullptr)) {
if (pGameSound->playing())
pGameSound->stop();
} else if (pGameInfo->bMusicEnabled) {
if (pGameSound == nullptr) {
pGameSound = new CSound(this, GAME_THEME, SOUND_MIDI | SOUND_LOOP | SOUND_DONT_LOOP_TO_END);
}
if (pGameSound != nullptr) {
if (!pGameSound->playing())
(*pGameSound).midiLoopPlaySegment(2300, 32000, 0, FMT_MILLISEC);
} // end if pGameSound
}
(*pDC).SelectPalette(pPalOld, false);
InvalidateRect(nullptr, false); // force a redraw of the entire window
//...and stop any other WM_PAINT messages
} //end switch(wParam)
ReleaseDC(pDC);
} // end if
(*this).SetFocus(); // Reset focus back to the main window
return true;
}
/*****************************************************************
*
* OnLButtonDown
*
* FUNCTIONAL DESCRIPTION:
*
* Left mouse button processing function
*
* FORMAL PARAMETERS:
*
* unsigned int nFlags Virtual key info
* CPoint point Location of cursor
*
* IMPLICIT INPUT PARAMETERS:
*
* [External data read]
*
* IMPLICIT OUTPUT PARAMETERS:
*
* [External data modified]
*
* RETURN VALUE:
*
* void
*
****************************************************************/
void CMainWindow::OnLButtonDown(unsigned int nFlags, CPoint point) {
CRect rectTitle;
CSound *pEffect = nullptr;
rectTitle.SetRect(NEWGAME_LOCATION_X, NEWGAME_LOCATION_Y,
NEWGAME_LOCATION_X + NEWGAME_WIDTH,
NEWGAME_LOCATION_Y + NEWGAME_HEIGHT);
if (rectTitle.PtInRect(point) && ((pGameInfo->bPlayingMetagame == false) && !m_bShowOutOfPlace)) {
NewGame();
} else if (bGameStarted && (m_bNewGame && !m_bShowOutOfPlace)) {
CDC *pDC;
CPoint Hit;
pDC = GetDC();
if (!m_bFirst && InArtRegion(point)) { // Selecting First area
if (m_nTime == (nSeconds + (nMinutes * 60))) { // Very first click of the game...
SetTimer(DISPLAY_TIMER, CLICK_TIME, nullptr); // If it is timed. Set timer
}
Hit = WinToArt(point);
First.x = Hit.x - (Hit.x % m_nWidth);
First.y = Hit.y - (Hit.y % m_nHeight);
Second.x = First.x + m_nWidth;
Second.y = First.y + m_nHeight;
BaseRect.SetRect(First.x, First.y, Second.x, Second.y); // Save the starting part to
//...check sizing changes
OldRect.SetRect(First.x + SIDE_BORDER, First.y + TOP_BORDER,
Second.x + SIDE_BORDER, Second.y + TOP_BORDER);
MyFocusRect(pDC, OldRect, R2_NOT);
m_bFirst = true;
bSwitched = false;
SetCapture(); // Hog all the mouse events
} // end if
else if (m_bFirst) { // Selecting Second area
ReleaseCapture(); // Let other windows get mouse events
DstRect.SetRect(OldRect.TopLeft().x - SIDE_BORDER, // in case Point is outside
OldRect.TopLeft().y - TOP_BORDER, //...of art area
OldRect.BottomRight().x - SIDE_BORDER, // Use OldRect for
OldRect.BottomRight().y - TOP_BORDER); //...copy-to area
MyFocusRect(pDC, OldRect, R2_NOT); // erase old rect
SwitchAreas(SrcRect, DstRect); // Switch the two areas
m_bFirst = false;
bSwitched = true;
if (m_nScore == 100) { // If the Picture is all correct:
KillTimer(DISPLAY_TIMER); // Stop the Display timer
if (pGameInfo->bSoundEffectsEnabled) {
pEffect = new CSound((CWnd *)this, WIN_SOUND,
SOUND_WAVE | SOUND_ASYNCH |
SOUND_QUEUE | SOUND_AUTODELETE); //...Wave file, to delete itself
(*pEffect).play(); //...play the narration
}
MSG lpmsg;
while (PeekMessage(&lpmsg, nullptr, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE | PM_NOYIELD)) ;
CMessageBox GameOverDlg((CWnd *)this, pGamePalette, "Game over.", "It's complete!");
m_bNewGame = false; // Game over, but can still look
//..at the art (Playing is true
bGameStarted = false;
if (pGameInfo->bPlayingMetagame) {
pGameInfo->lScore = 1;
PostMessage(WM_CLOSE, 0, 0); // and post a program exit
}
} // end if m_nScore
else {
if (pGameInfo->bSoundEffectsEnabled) {
pEffect = new CSound((CWnd *)this, SWITCH_SOUND,
SOUND_WAVE | SOUND_ASYNCH |
SOUND_QUEUE | SOUND_AUTODELETE); //...Wave file, to delete itself
(*pEffect).play(); //...play the narration
} // end if pGameInfo->bSoundEffectsEnabled
} // end else
}// end else if (m_bFirst)
ReleaseDC(pDC);
} // end else if ( m_bNewGame )
CFrameWnd ::OnLButtonDown(nFlags, point);
}
/*****************************************************************
*
* OnLButtonUp
*
* FUNCTIONAL DESCRIPTION:
*
* Standard Left mouse button processing function
*
* FORMAL PARAMETERS:
*
* unsigned int nFlags Virtual key info
* CPoint point Location of cursor
*
* IMPLICIT INPUT PARAMETERS:
*
* [External data read]
*
* IMPLICIT OUTPUT PARAMETERS:
*
* [External data modified]
*
* RETURN VALUE:
*
* void
*
****************************************************************/
void CMainWindow::OnLButtonUp(unsigned int nFlags, CPoint point) {
if (bGameStarted)
if (m_bNewGame && !m_bShowOutOfPlace) {
CDC *pDC;
CSound *pEffect = nullptr;
ReleaseCapture();
pDC = GetDC();
if (m_bFirst) {
CRect WndRect; // Selecting Source Area
SrcRect.SetRect(OldRect.TopLeft().x - SIDE_BORDER, // SrcRect is in Art coords
OldRect.TopLeft().y - TOP_BORDER,
OldRect.BottomRight().x - SIDE_BORDER,
OldRect.BottomRight().y - TOP_BORDER);
HiLiteRect = OldRect; // Highlight selected area
MyFocusRect(pDC, HiLiteRect, R2_COPYPEN);
if (pGameInfo->bSoundEffectsEnabled) {
// sndPlaySound( nullptr, SND_SYNC ); // Kill any current sound
// sndPlaySound( PICK_SOUND, SND_ASYNC ); // Make pick noise
pEffect = new CSound((CWnd *)this, PICK_SOUND,
SOUND_WAVE | SOUND_ASYNCH |
SOUND_QUEUE | SOUND_AUTODELETE); //...Wave file, to delete itself
(*pEffect).play(); //...play the narration
}
Center.x = First.x + SIDE_BORDER + (OldRect.Width() / 2); // Center is in Screen coords
Center.y = First.y + TOP_BORDER + (OldRect.Height() / 2);
UpLeft.x = First.x + SIDE_BORDER; // Upper left corner of area
UpLeft.y = First.y + TOP_BORDER; //...in Screen coords
//new:
GetWindowRect(WndRect); // Get the screen loc of wnd
SetCursorPos(WndRect.TopLeft().x + Center.x, WndRect.TopLeft().y + Center.y); //...use it to set cursor pos
SetCapture(); // Hog all the mouse events
}
ReleaseDC(pDC);
}
CFrameWnd ::OnLButtonUp(nFlags, point);
}
/*****************************************************************
*
* OnMouseMove
*
* FUNCTIONAL DESCRIPTION:
*
* Mouse movement processing function
*
* FORMAL PARAMETERS:
*
* unsigned int nFlags Virtual key info
* CPoint point Location of cursor
*
* IMPLICIT INPUT PARAMETERS:
*
* [External data read]
*
* IMPLICIT OUTPUT PARAMETERS:
*
* [External data modified]
*
* RETURN VALUE:
*
* void
*
****************************************************************/
void CMainWindow::OnMouseMove(unsigned int nFlags, CPoint point) {
SetCursor(LoadCursor(nullptr, IDC_ARROW)); // Refresh cursor object
if (bGameStarted && (m_bNewGame && !m_bShowOutOfPlace)) {
CDC *pDC;
pDC = GetDC();
CRect NewRect;
if ((nFlags & MK_LBUTTON) && m_bFirst) { // Selecting a region
CPoint Hit;
Hit = WinToArt(point);
if (Hit.x < BaseRect.TopLeft().x) { // Dealing with left side
if (point.x < (OldRect.TopLeft().x)) // Expanding to the left
First.x -= m_nWidth;
else if (point.x > (OldRect.TopLeft().x + m_nWidth))
First.x += m_nWidth; // Shrinking from the left
}
else if (point.x > BaseRect.BottomRight().x) { // Dealing with right side
if (point.x > OldRect.BottomRight().x) // Expanding to the right
Second.x += m_nWidth;
else if (point.x < (OldRect.BottomRight().x - m_nWidth))
Second.x -= m_nWidth; // Shrinking from the right
}
else { // This may be extraneous, no?
First.x = BaseRect.TopLeft().x;
Second.x = BaseRect.BottomRight().x;
}
if (Hit.y < BaseRect.TopLeft().y) { // Dealing with Top
if (point.y < OldRect.TopLeft().y) // Expanding Up
First.y -= m_nHeight;
else if (point.y > (OldRect.TopLeft().y + m_nHeight))
First.y += m_nHeight; // Shrinking from Top
}
else if (point.y > BaseRect.BottomRight().y) { // Dealing with Bottom
if (point.y > OldRect.BottomRight().y) // Expanding Down
Second.y += m_nHeight;
else if (point.y < (OldRect.BottomRight().y - m_nHeight))
Second.y -= m_nHeight;
}
else { // This may be extraneous, no?
First.y = BaseRect.TopLeft().y;
Second.y = BaseRect.BottomRight().y;
}
NewRect.SetRect(First.x + SIDE_BORDER, First.y + TOP_BORDER,
Second.x + SIDE_BORDER, Second.y + TOP_BORDER);
if (NewRect.BottomRight().x > (SIDE_BORDER + ART_WIDTH)) {
NewRect.BottomRight().x = SIDE_BORDER + ART_WIDTH;
} else if (NewRect.TopLeft().x < SIDE_BORDER) {
NewRect.TopLeft().x = SIDE_BORDER;
}
if (NewRect.BottomRight().y > (TOP_BORDER + ART_HEIGHT)) {
NewRect.BottomRight().y = TOP_BORDER + ART_HEIGHT;
} else if (NewRect.TopLeft().y < TOP_BORDER) {
NewRect.TopLeft().y = TOP_BORDER;
}
MyFocusRect(pDC, OldRect, R2_NOT);
MyFocusRect(pDC, NewRect, R2_NOT);
OldRect = NewRect;
} else if (m_bFirst && !m_bShowOutOfPlace) { // Moving selected area
if (point.x > Center.x + m_nWidth / 2) {
Center.x += m_nWidth; // Step by Part Widths
UpLeft.x += m_nWidth;
} else if (point.x < Center.x - m_nWidth / 2) {
Center.x -= m_nWidth;
UpLeft.x -= m_nWidth;
}
if (point.y > Center.y + m_nHeight / 2) {
Center.y += m_nHeight; // Step by Part Heights
UpLeft.y += m_nHeight;
} else if (point.y < Center.y - m_nHeight / 2) {
Center.y -= m_nHeight;
UpLeft.y -= m_nHeight;
}
/*
NewRect.SetRect( Center.x - (OldRect.Width()/2), Center.y - (OldRect.Height()/2),
Center.x + (OldRect.Width()/2), Center.y + (OldRect.Height()/2) );
*/
NewRect.SetRect(UpLeft.x, UpLeft.y,
UpLeft.x + OldRect.Width(), UpLeft.y + OldRect.Height());
if (NewRect.BottomRight().x > (SIDE_BORDER + ART_WIDTH)) { // Keep within Art limits
NewRect.BottomRight().x = SIDE_BORDER + ART_WIDTH;
NewRect.TopLeft().x = NewRect.BottomRight().x - OldRect.Width();
} else if (NewRect.TopLeft().x < SIDE_BORDER) {
NewRect.TopLeft().x = SIDE_BORDER;
NewRect.BottomRight().x = NewRect.TopLeft().x + OldRect.Width();
}
if (NewRect.BottomRight().y > (TOP_BORDER + ART_HEIGHT)) {
NewRect.BottomRight().y = TOP_BORDER + ART_HEIGHT;
NewRect.TopLeft().y = NewRect.BottomRight().y - OldRect.Height();
} else if (NewRect.TopLeft().y < TOP_BORDER) {
NewRect.TopLeft().y = TOP_BORDER;
NewRect.BottomRight().y = NewRect.TopLeft().y + OldRect.Height();
}
if (NewRect.EqualRect(OldRect) == false) { // if the Rectangle has changed
MyFocusRect(pDC, OldRect, R2_NOT); // Erase the old position
MyFocusRect(pDC, NewRect, R2_NOT); // Draw Focus rect in new position
OldRect = NewRect; // Store the new focus rect
MyFocusRect(pDC, HiLiteRect, R2_COPYPEN); // Refresh the highlighted area
}
}
ReleaseDC(pDC);
}
CFrameWnd ::OnMouseMove(nFlags, point);
}
/*****************************************************************
*
* OnRButtonDown
*
* FUNCTIONAL DESCRIPTION:
*
* Right mouse button processing function: Undo last move
*
* FORMAL PARAMETERS:
*
* unsigned int nFlags Virtual key info
* CPoint point Location of cursor
*
* IMPLICIT INPUT PARAMETERS:
*
* [External data read]
*
* IMPLICIT OUTPUT PARAMETERS:
*
* [External data modified]
*
* RETURN VALUE:
*
* void
*
****************************************************************/
void CMainWindow::OnRButtonDown(unsigned int nFlags, CPoint point) {
if (bGameStarted) {
if (InArtRegion(point) && (m_bNewGame && !m_bShowOutOfPlace)) {
CDC *pDC;
CSound *pEffect = nullptr;
pDC = GetDC();
if (pGameInfo->bSoundEffectsEnabled) {
pEffect = new CSound((CWnd *)this, UNDO_SOUND,
SOUND_WAVE | SOUND_ASYNCH |
SOUND_QUEUE | SOUND_AUTODELETE); //...Wave file, to delete itself
(*pEffect).play(); //...play the narration
}
if (m_bFirst) { // Deselect area
MyFocusRect(pDC, OldRect, R2_NOT); // Erase the Focus rect
SplashScratch(); // Repaint the artwork
}
else if (bSwitched) { // Switch back the two areas
SwitchAreas(SrcRect, DstRect);
bSwitched = false; // Don't allow second Undo
}
m_bFirst = false; // Return to beginning state
CFrameWnd ::OnRButtonDown(nFlags, point);
ReleaseDC(pDC);
}
} // end if bGameStarted
} // End OnRButtonDown
/*****************************************************************
*
* OnMButtonDown
*
* FUNCTIONAL DESCRIPTION:
*
* Middle mouse button processing function
*
* FORMAL PARAMETERS:
*
* unsigned int nFlags Virtual key info
* CPoint point Location of cursor
*
* IMPLICIT INPUT PARAMETERS:
*
* [External data read]
*
* IMPLICIT OUTPUT PARAMETERS:
*
* [External data modified]
*
* RETURN VALUE:
*
* void
*
****************************************************************/
void CMainWindow::OnMButtonDown(unsigned int nFlags, CPoint point) {
if (bGameStarted)
if (!m_bShowOutOfPlace && (pGameInfo->bPlayingMetagame == false)) { // Not available in meta-game
m_bShowOutOfPlace = true;
ShowOutOfPlace(); // Momentarily highlight out-of-place pieces
}
} // end OnMButtonDown
// OnChar and OnSysChar
// These functions are called when keyboard input generates a character.
//
void CMainWindow::OnChar(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags) {
CFrameWnd ::OnChar(nChar, nRepCnt, nFlags); // default action
}
void CMainWindow::OnSysKeyDown(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags) {
if ((nChar == VK_F4) && (nFlags & 0x2000)) {
PostMessage(WM_CLOSE, 0, 0); // *** remove later ***
} else
CFrameWnd::OnSysKeyDown(nChar, nRepCnt, nFlags); // default action
}
void CMainWindow::OnKeyDown(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags) {
switch (nChar) {
case VK_F1: // F1 key is hit
SendMessage(WM_COMMAND, IDC_RULES, BN_CLICKED); // Activate the Rules dialog
break;
case VK_F2: // F2 key is hit
SendMessage(WM_COMMAND, IDC_SCROLL, BN_CLICKED); // Activate the Options dialog
break;
case VK_RETURN: // Return key is hit
case VK_SPACE: // Space bar is hit
if (bGameStarted) {
if (!m_bShowOutOfPlace && (pGameInfo->bPlayingMetagame == false)) {
m_bShowOutOfPlace = true;
ShowOutOfPlace(); // Momentarily highlight out-of-place pieces
}
}
break;
} // end switch
} // end OnKeyDown
/*****************************************************************
*
* OnTimer
*
* FUNCTIONAL DESCRIPTION:
*
* Processes Timer events
*
* FORMAL PARAMETERS:
*
* unsigned int nIDEvent The ID of the timer event activated
*
* IMPLICIT INPUT PARAMETERS:
*
* [External data read]
*
* IMPLICIT OUTPUT PARAMETERS:
*
* [External data modified]
*
* RETURN VALUE:
*
* void
*
****************************************************************/
void CMainWindow::OnTimer(uintptr nIDEvent) {
CDC *pDC;
CSound *pEffect = nullptr;
char msg[64];
pDC = GetDC();
switch (nIDEvent) {
case DISPLAY_TIMER:
if (m_nTime == 0) {
nSeconds++; // No time limit, increment
if (nSeconds == 60) {
nMinutes++;
nSeconds = 0;
}
}
else { // Count down time left
if (nSeconds == 0 && nMinutes != 0) {
nMinutes--;
nSeconds = 60;
}
nSeconds--;
}
if (m_nTime == 0)
Common::sprintf_s(msg, "Time Used: %02d:%02d", nMinutes, nSeconds);
else {
Common::sprintf_s(msg, "Time Left: %02d:%02d", nMinutes, nSeconds);
}
(*m_pTimeText).DisplayString(pDC, msg, FONT_SIZE, FW_SEMIBOLD, OPTIONS_COLOR);
if (nMinutes == 0 && nSeconds == 0) {
char buf[64];
bGameStarted = false;
Common::sprintf_s(buf, "Score: %.0f%% correct", m_nScore);
KillTimer(nIDEvent); // Stop the Display timer
m_bNewGame = false;
m_bFirst = false;
if (pGameInfo->bSoundEffectsEnabled) {
pEffect = new CSound((CWnd *)this, LOSE_SOUND,
SOUND_WAVE | SOUND_ASYNCH |
SOUND_QUEUE | SOUND_AUTODELETE); //...Wave file, to delete itself
(*pEffect).play(); //...play the narration
}
CheckForWin(); // Update the score
MSG lpmsg;
while (PeekMessage(&lpmsg, nullptr, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE | PM_NOYIELD)) ;
CMessageBox GameOverDlg((CWnd *)this, pGamePalette, "Game over.", buf);
ReleaseCapture(); // Let other windows get mouse events
if (pGameInfo->bPlayingMetagame) {
pGameInfo->lScore = 0;
PostMessage(WM_CLOSE, 0, 0); // and post a program exit
}
}
break;
case SHOW_TIMER:
if (m_bShowOutOfPlace) {
ShowOutOfPlace(); // Un-highlight out-of-place pieces
m_bShowOutOfPlace = false;
}
KillTimer(nIDEvent);
break;
default:
CFrameWnd ::OnTimer(nIDEvent);
break;
} // end Switch
ReleaseDC(pDC);
}
/**********************************************************
Other functions:
***********************************************************/
/*****************************************************************
*
* SwitchAreas
*
* FUNCTIONAL DESCRIPTION:
*
* Draws a section of the source artwork to the scratch bitmap
*
* FORMAL PARAMETERS:
*
* CRect Src Location of source area
* CRect Dst Location of destination area
*
* IMPLICIT INPUT PARAMETERS:
*
* [External data read]
*
* IMPLICIT OUTPUT PARAMETERS:
*
* [External data modified]
*
* RETURN VALUE:
*
* void
*
****************************************************************/
void CMainWindow::SwitchAreas(const CRect &Src, const CRect &Dst) {
POINT SrcCR, DstCR, SizeCR, Temp;
int c, r;
SizeCR.x = Src.Width() / m_nWidth;
SizeCR.y = Src.Height() / m_nHeight;
SrcCR.x = Src.TopLeft().x / m_nWidth;
SrcCR.y = Src.TopLeft().y / m_nHeight;
DstCR.x = Dst.TopLeft().x / m_nWidth;
DstCR.y = Dst.TopLeft().y / m_nHeight;
DrawPart(Src.TopLeft(), Dst.TopLeft(), Src.Width(), Src.Height());
CRect Overlap;
if (Overlap.IntersectRect(Src, Dst) == 0) {
// They don't intersect
DrawPart(Dst.TopLeft(), Src.TopLeft(), Src.Width(), Src.Height());
for (c = 0; c < SizeCR.x; c++) { // Update the Grid data
for (r = 0; r < SizeCR.y; r++) {
Temp.x = Grid[SrcCR.x + c][SrcCR.y + r].x;
Temp.y = Grid[SrcCR.x + c][SrcCR.y + r].y;
Grid[SrcCR.x + c][SrcCR.y + r].x = Grid[DstCR.x + c][DstCR.y + r].x;
Grid[SrcCR.x + c][SrcCR.y + r].y = Grid[DstCR.x + c][DstCR.y + r].y;
Grid[DstCR.x + c][DstCR.y + r].x = Temp.x;
Grid[DstCR.x + c][DstCR.y + r].y = Temp.y;
}
}
} else { // Deal with intersection
POINT *Movers, *MGrids, *SGrids, *p, *g, *s, Part, NewPart;
Movers = (POINT *)malloc((SizeCR.x * SizeCR.y) * sizeof(POINT));
MGrids = (POINT *)malloc((SizeCR.x * SizeCR.y) * sizeof(POINT));
SGrids = (POINT *)malloc((SizeCR.x * SizeCR.y) * sizeof(POINT));
s = SGrids;
for (c = 0; c < SizeCR.x; c++) { // Put the locations of the source
for (r = 0; r < SizeCR.y; r++) { //...parts into a safe spot: SGrids
s->x = Grid[SrcCR.x + c][SrcCR.y + r].x;
s->y = Grid[SrcCR.x + c][SrcCR.y + r].y;
s++;
}
}
p = Movers;
g = MGrids;
for (c = 0; c < SizeCR.x; c++) {
for (r = 0; r < SizeCR.y; r++) {
Part.x = (DstCR.x + c) * m_nWidth;
Part.y = (DstCR.y + r) * m_nHeight;
if (Overlap.PtInRect(Part) == 0) { //The parts which are not shared
p->x = DstCR.x + c; //...by the source and destination
p->y = DstCR.y + r; //...have their coord's stored in
p++; //...Movers, to which p points
g->x = Grid[DstCR.x + c][DstCR.y + r].x; //Put the locations of the
g->y = Grid[DstCR.x + c][DstCR.y + r].y; //...destinations in MGrids
g++; //...to which g points.
}
}
}
p = Movers;
g = MGrids;
for (c = 0; c < SizeCR.x; c++) {
for (r = 0; r < SizeCR.y; r++) {
Part.x = (SrcCR.x + c) * m_nWidth;
Part.y = (SrcCR.y + r) * m_nHeight;
if (Overlap.PtInRect(Part) == 0) { //Switch the parts that
NewPart.x = p->x * m_nWidth; //...aren't in the overlap
NewPart.y = p->y * m_nHeight; //...area
DrawPart(NewPart, Part, m_nWidth, m_nHeight);
p++;
Grid[SrcCR.x + c][SrcCR.y + r].x = g->x; //Then trade the Grid data
Grid[SrcCR.x + c][SrcCR.y + r].y = g->y; //...for the dst moved data
g++;
}
}
}
s = SGrids;
for (c = 0; c < SizeCR.x; c++) {
for (r = 0; r < SizeCR.y; r++) {
Grid[DstCR.x + c][DstCR.y + r].x = s->x; //Trade Grid data for the
Grid[DstCR.x + c][DstCR.y + r].y = s->y; //...Source area
s++;
}
}
free(Movers);
free(MGrids);
free(SGrids);
} // End else
SplashScratch(); // Repaint the Art
CheckForWin(); // See if the new arrangement is a win
} // End SwitchAreas()
/*****************************************************************
*
* DrawPart
*
* FUNCTIONAL DESCRIPTION:
*
* Draws a section of the source artwork to the scratch bitmap
*
* FORMAL PARAMETERS:
*
* CPoint Src Location of source area
* CPoint Dst Location of destination area
*
* IMPLICIT INPUT PARAMETERS:
*
* [External data read]
*
* IMPLICIT OUTPUT PARAMETERS:
*
* [External data modified]
*
* RETURN VALUE:
*
* void
*
****************************************************************/
void CMainWindow::DrawPart(const CPoint &Src, const CPoint &Dst, int nWidth, int nHeight) {
pScratch2DC->BitBlt(Dst.x, Dst.y, nWidth, nHeight, pScratch1DC, Src.x, Src.y, SRCCOPY);
} //End DrawPart();
bool CMainWindow::CopyPaletteContents(CPalette *pSource, CPalette *pDest) {
ASSERT(pSource && pDest);
// Get the number of entries in the source palette
unsigned int nEntries = pSource->GetEntryCount();
if (nEntries == 0)
return false;
// Use C++-isms to copy palette contents from src to dest
Graphics::Palette *src = pSource->palette();
Graphics::Palette *dest = pDest->palette();
*dest = *src;
return true;
}
/*****************************************************************
*
* LoadArtWork
*
* FUNCTIONAL DESCRIPTION:
*
* Loads the Artwork to the Scratch1 bitmap
*
* FORMAL PARAMETERS:
*
* none
*
* IMPLICIT INPUT PARAMETERS:
*
* none
*
* IMPLICIT OUTPUT PARAMETERS:
*
* none
*
* RETURN VALUE:
*
* none
*
****************************************************************/
bool CMainWindow::LoadArtWork() {
CRect rcDest; // Art work - frame bitmap dims
CRect rcDIB; // Source area for Art Work bmp
HDIB hDIB;
CDC *pDC;
CDC *pSourceDC;
CBitmap *pSource = nullptr,
*pOldBmp = nullptr;
CPalette *pOldPal = nullptr;
char ArtName[MAX_FILE_LENGTH];
char bufName[MAX_FILE_LENGTH + 10];
int nNumEntries;
char chNumEntries[5];
int i, pick;
ifstream inFile;
inFile.open(DATA_FILE); // open the data store
if (inFile.fail()) {
return false;
}
inFile.getline(chNumEntries, sizeof(chNumEntries)); // read number of names in file
nNumEntries = atoi(chNumEntries);
assert(nNumEntries > 0);
pick = nLastPick;
while (pick == nLastPick) {
pick = (brand() % nNumEntries) + 1;
}
for (i = 0; i < pick; i++) {
if (!inFile.eof())
inFile.getline(ArtName, sizeof(ArtName)); // load a name
}
inFile.close(); // close the data store
nLastPick = pick;
pDC = GetDC();
pSourceDC = new CDC();
pSource = new CBitmap(); // Source is the original, in-tact artwork for framed mode
pSource->CreateCompatibleBitmap(pDC, ART_WIDTH + (2 * FRAME_WIDTH),
ART_HEIGHT + (2 * FRAME_HEIGHT)); // Set up Source
pSourceDC->CreateCompatibleDC(pDC); //...bitmap and DC
pOldBmp = pSourceDC->SelectObject(pSource);
CDibDoc *pSourceDoc;
pSourceDoc = new CDibDoc();
ASSERT(pSourceDoc != nullptr);
if (pSourceDoc == nullptr) {
return false;
}
Common::sprintf_s(bufName, ".\\art\\%s", ArtName);
Common::sprintf_s(szCurrentArt, "%s", bufName); // copy to a global for use in OnPaint
(*pSourceDoc).OpenDocument(bufName);
// Acquire the shared palette for our game from the art
if (!pGamePalette) {
pGamePalette = (*pSourceDoc).DetachPalette();
} else {
// WORKAROUND: Keep a single pGamePalette, since there
// are UI elements that have pointers to it
CPalette *src = pSourceDoc->DetachPalette();
CopyPaletteContents(src, pGamePalette);
delete src;
}
// setup new palette in scratch areas
if (!pOldPal1) {
pOldPal1 = pScratch1DC->SelectPalette(pGamePalette, false);
pScratch1DC->RealizePalette();
pOldPal2 = pScratch2DC->SelectPalette(pGamePalette, false);
pScratch2DC->RealizePalette();
pOldPal = pSourceDC->SelectPalette(pGamePalette, false);
pSourceDC->RealizePalette();
}
hDIB = (*pSourceDoc).GetHDIB();
if (hDIB) {
if (bFramed)
rcDest.SetRect(0, 0, ART_WIDTH + (2 * FRAME_WIDTH), ART_HEIGHT + (2 * FRAME_HEIGHT));
else
rcDest.SetRect(0, 0, ART_WIDTH, ART_HEIGHT);
int cxDIB = (int) DIBWidth(hDIB);
int cyDIB = (int) DIBHeight(hDIB);
if (cxDIB > ART_WIDTH) { // Center and crop
rcDIB.left = (cxDIB - ART_WIDTH) / 2; //...too wide art
rcDIB.right = rcDIB.left + ART_WIDTH;
} else { // Stretch too thin art
rcDIB.left = 0;
rcDIB.right = cxDIB;
}
if (cyDIB > ART_HEIGHT) { // Center and crop
rcDIB.top = (cyDIB - ART_HEIGHT) / 2; //...too long art
rcDIB.bottom = rcDIB.top + ART_HEIGHT;
} else { // Stretch too short art
rcDIB.top = 0;
rcDIB.bottom = cyDIB;
}
if (bFramed) {
PaintDIB((*pSourceDC).m_hDC, &rcDest, hDIB, &rcDIB, pGamePalette);
pScratch1DC->BitBlt(0, 0, ART_WIDTH, ART_HEIGHT, pSourceDC,
FRAME_WIDTH, FRAME_HEIGHT, SRCCOPY);
} else {
PaintDIB((*pScratch1DC).m_hDC, &rcDest, hDIB, &rcDIB, pGamePalette);
}
}
delete pSourceDoc;
pSourceDoc = nullptr;
if (pOldBmp != nullptr) // Get rid of Source
pSourceDC->SelectObject(pOldBmp);
if (pOldPal != nullptr)
pSourceDC->SelectPalette(pOldPal, false);
if (pSourceDC->m_hDC != nullptr) {
pSourceDC->DeleteDC();
delete pSourceDC;
}
pSource->DeleteObject();
delete pSource;
ReleaseDC(pDC);
POINT ScrOne, ScrTwo;
int r, c;
int x = 0, y = 0;
bool bCheckGrid[MAX_COLUMNS][MAX_ROWS] = {{0}}; // Initialize all to zero
bool bAssigning; // Flag for random assignment
for (c = 0; c < m_nColumns; c++) { // Each step in X
for (r = 0; r < m_nRows; r++) { // Each step in Y
ScrTwo.x = c * m_nWidth;
ScrTwo.y = r * m_nHeight;
bAssigning = true;
while (bAssigning) {
x = brand() % m_nColumns; // 0 thru m_nColumns - 1
y = brand() % m_nRows; // 0 thru m_nRows - 1
bAssigning = bCheckGrid[x][y];
}
bCheckGrid[x][y] = true;
Grid[c][r].x = x; // The r & c home of the art in
// this spot.
Grid[c][r].y = y;
ScrOne.x = x * m_nWidth; // Store Artwork X and Y
ScrOne.y = y * m_nHeight; //...placed at Grid[c][r]
DrawPart(ScrOne, ScrTwo, m_nWidth, m_nHeight);
} //end for r
} //end for r
return true;
} // end LoadArtWork()
/*****************************************************************
*
* InitValues
*
* FUNCTIONAL DESCRIPTION:
*
* Sets several global variables to their default or initial states
*
* FORMAL PARAMETERS:
*
* none
*
* IMPLICIT INPUT PARAMETERS:
*
* m_bNewGame, m_nTime, m_bFirst, m_nRows, m_nColumns
*
* IMPLICIT OUTPUT PARAMETERS:
*
* none
*
* RETURN VALUE:
*
* void
*
****************************************************************/
void CMainWindow::InitValues() {
m_bPlaying = true; // Start out playing
m_bNewGame = true; //...a new puzzle with
m_bFirst = false; //...minimum everything:
bSwitched = false;
bFramed = true;
if (pGameInfo->bPlayingMetagame) {
pGameInfo->lScore = 0L;
if (pGameInfo->nSkillLevel == SKILLLEVEL_LOW) {
m_nColumns = 3;
m_nRows = 3;
m_nTime = 60;
} else if (pGameInfo->nSkillLevel == SKILLLEVEL_MEDIUM) {
m_nColumns = START_COLUMNS;
m_nRows = 3;
m_nTime = 45;
} else { // SKILLLEVEL_HIGH
m_nColumns = START_COLUMNS; //4;
m_nRows = START_ROWS; //4;
m_nTime = 45;
}
} else {
m_nColumns = START_COLUMNS;
m_nRows = START_ROWS;
m_nTime = 60;
}
m_nWidth = ART_WIDTH / m_nColumns;
m_nHeight = ART_HEIGHT / m_nRows;
nSeconds = m_nTime % 60; // Always starts at one minute or less
nMinutes = m_nTime / 60;
tempTime = m_nTime; // Set the temp values to start values
tempRows = m_nRows;
tempColumns = m_nColumns;
tempFramed = bFramed;
} // End InitValues()
void CMainWindow::NewGame() {
CDC *pDC;
CPalette *pPalOld = nullptr;
CBrush *pBrushOld = nullptr,
*pBrushNew = nullptr;
char msg[64];
KillTimer(DISPLAY_TIMER); // Stop the Display timer
CSound::clearWaveSounds();
BeginWaitCursor();
m_nTime = tempTime; // get new time limit,
m_nRows = tempRows; //...new rows, and cols
m_nColumns = tempColumns;
bFramed = tempFramed;
bSwitched = false; // Don't allow second Undo
m_nWidth = ART_WIDTH / m_nColumns;
m_nHeight = ART_HEIGHT / m_nRows;
nMinutes = m_nTime / 60;
nSeconds = m_nTime % 60;
pDC = GetDC();
pPalOld = (*pDC).SelectPalette(pGamePalette, false); // Select in the artwork's palette
(*pDC).RealizePalette(); // Use it
PaintBitmap(pDC, pGamePalette, pLocaleBitmap, TIME_LOCATION_X, TIME_LOCATION_Y);
pBrushNew = new CBrush(); // Construct a new brush object
if (pBrushNew != nullptr) { // If the constructor was successful:
pBrushNew->CreateSolidBrush(PALETTERGB(128, 0, 0)); // Create my backdrop color brush
pBrushOld = (*pDC).SelectObject(pBrushNew); // Select into the DC my new brush
pDC->SetROP2(R2_COPYPEN); // Set Draw mode to use the pen color
if (bFramed)
(*pDC).Rectangle(SIDE_BORDER - FRAME_WIDTH, TOP_BORDER - FRAME_HEIGHT,
GAME_WIDTH - (SIDE_BORDER - FRAME_WIDTH),
GAME_HEIGHT - (BOTTOM_BORDER - FRAME_HEIGHT));
else
(*pDC).Rectangle(SIDE_BORDER, TOP_BORDER, GAME_WIDTH - SIDE_BORDER, GAME_HEIGHT - BOTTOM_BORDER);
(*pDC).SelectObject(pBrushOld); // Select in the old brush
(*pDC).SelectPalette(pPalOld, false); // Select in the old palette
delete pBrushNew; // Delete the new brush
}
m_bNewGame = true;
m_bPlaying = true;
if (LoadArtWork() == false) { // Load artwork to Scratch1
EndWaitCursor();
PostMessage(WM_CLOSE, 0, 0);
return;
}
CheckForWin();
PaintBitmap(pDC, pGamePalette, pBlankBitmap, TIME_LOCATION_X, TIME_LOCATION_Y);
if (m_nTime == 0)
Common::sprintf_s(msg, "Time Used: %02d:%02d", nMinutes, nSeconds);
else {
Common::sprintf_s(msg, "Time Left: %02d:%02d", nMinutes, nSeconds);
}
(*m_pTimeText).DisplayString(pDC, msg, FONT_SIZE, FW_SEMIBOLD, OPTIONS_COLOR);
EndWaitCursor();
(*pDC).SelectPalette(pPalOld, false);
ReleaseDC(pDC);
InvalidateRect(nullptr, false); // force a redraw of the entire window
MSG lpmsg;
while (PeekMessage(&lpmsg, nullptr, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE | PM_NOYIELD)) ;
bStartOkay = true;
}
/*****************************************************************
*
* InArtRegion
*
* FUNCTIONAL DESCRIPTION:
*
* Checks to see if a point is within the Artwork region of the window
*
* FORMAL PARAMETERS:
*
* CPoint point The point to check
*
* IMPLICIT INPUT PARAMETERS:
*
* Extents of the main game window, and the extents of the artwork
*
* IMPLICIT OUTPUT PARAMETERS:
*
* none
*
* RETURN VALUE:
*
* bool: true if point is within the Art Region,
* false if point is outside the Art Region
*
****************************************************************/
bool InArtRegion(CPoint point) {
if ((point.x > SIDE_BORDER && point.x < GAME_WIDTH - SIDE_BORDER) && // See if point lies within
(point.y > TOP_BORDER && point.y < GAME_HEIGHT - BOTTOM_BORDER)) //...ArtWork area
return true; // Return true if it's inside
else return false; //...and false if not
}
/******************************************************************************
Convert a point from the MainWindow coordinates to the ArtRegion coordinates,
******************************************************************************/
CPoint WinToArt(CPoint point) {
point.x -= SIDE_BORDER;
point.y -= TOP_BORDER;
return point;
}
/*****************************************************************
*
* ShowOutOfPlace();
*
* FUNCTIONAL DESCRIPTION:
*
* Highlights pieces which are not correctly placed
*
* FORMAL PARAMETERS:
*
* none
*
* IMPLICIT INPUT PARAMETERS:
*
* Grid
*
* IMPLICIT OUTPUT PARAMETERS:
*
* none
*
* RETURN VALUE:
*
* void
*
****************************************************************/
void CMainWindow::ShowOutOfPlace() {
CDC *pDC;
CRect rect;
int r, c; // Row counter, column counter
pDC = GetDC();
for (c = 0; c < m_nColumns; c++) { // For each column
for (r = 0; r < m_nRows; r++) { // For each row
if ((Grid[c][r].x != c) || (Grid[c][r].y != r)) { // If the piece is in the wrong location
rect.SetRect(c * m_nWidth + SIDE_BORDER, r * m_nHeight + TOP_BORDER,
(c + 1) * m_nWidth + SIDE_BORDER, (r + 1) * m_nHeight + TOP_BORDER);
MyFocusRect(pDC, rect, R2_NOT); // Increment the number correct
}
}
}
ReleaseDC(pDC);
SetTimer(SHOW_TIMER, PAUSE_TIME, nullptr);
}
/*****************************************************************
*
* MyFocusRect( CDC *pDC, CRect rect, int nDrawMode )
*
* FUNCTIONAL DESCRIPTION:
*
* Draws a rectangle which inverts the current pixels,
* thereby delineating the current area of focus.
*
* FORMAL PARAMETERS:
*
* CDC *pDC The Device context in which the FocusRect is to be drawn
* CRect rect The CRect object holding the location of the FocusRect
*
* IMPLICIT INPUT PARAMETERS:
*
* pMyPen and pMyBrush, global pointers to the Pen and Brush used
*
* IMPLICIT OUTPUT PARAMETERS:
*
* none
*
* RETURN VALUE:
*
* void
*
****************************************************************/
void CMainWindow::MyFocusRect(CDC *pDC, CRect rect, int nDrawMode) {
CBrush *pMyBrush = nullptr; // New Brush
CBrush *pOldBrush = nullptr; // Pointer to old brush
CPen *pMyPen = nullptr; // New Pen
CPen *pOldPen = nullptr; // Pointer to old pen
CPalette *pPalOld = nullptr; // Pointer to old palette
int OldDrawMode; // Holder for old draw mode
pMyBrush = new CBrush(); // Construct new brush
pMyPen = new CPen(); // Construct new pen
LOGBRUSH lb; // log brush type
lb.lbStyle = BS_HOLLOW; // Don't fill in area
pMyBrush->CreateBrushIndirect(&lb); // Create a new brush
pMyPen->CreatePen(PS_INSIDEFRAME, HILITE_BORDER, RGB(255, 255, 255)); // Create a new pen
pPalOld = (*pDC).SelectPalette(pGamePalette, false); // Select in game palette
(*pDC).RealizePalette(); // Use it
pOldPen = pDC->SelectObject(pMyPen); // Select the new pen & save old
pOldBrush = pDC->SelectObject(pMyBrush); // Select the new brush & save old
OldDrawMode = pDC->SetROP2(nDrawMode); // Set pen mode, saving old state
pDC->Rectangle(rect); // Draw the Rectangle to the DC
pDC->SelectObject(pOldPen); // Select the old pen
pDC->SelectObject(pOldBrush); // Select the old brush
pDC->SetROP2(OldDrawMode); // Set pen mode back to old state
(*pDC).SelectPalette(pPalOld, false); // Select back the old palette
pMyBrush->DeleteObject();
delete pMyBrush;
pMyPen->DeleteObject();
delete pMyPen;
}
/*****************************************************************
*
* CheckForWin
*
* FUNCTIONAL DESCRIPTION:
*
* Checks to see how many parts are in their correct spaces
*
* FORMAL PARAMETERS:
*
* none
*
* IMPLICIT INPUT PARAMETERS:
*
* Grid, CurrentLocation, m_nRows, m_nColumns
*
* IMPLICIT OUTPUT PARAMETERS:
*
* none
*
* RETURN VALUE:
*
* float Percent of parts correctly placed
*
****************************************************************/
void CMainWindow::CheckForWin() {
int r, c; // Row counter, column counter
float nCorrect; // Number correct counter
float Percent, TotalParts; // Percent correct, Total number of parts
nCorrect = 0; // Start with none correct
for (c = 0; c < m_nColumns; c++) { // For each column
for (r = 0; r < m_nRows; r++) { // For each row
if ((Grid[c][r].x == c) && (Grid[c][r].y == r)) { // If the piece is in the right location
nCorrect++; // Increment the number correct
}
}
}
TotalParts = m_nColumns * m_nRows; // Calculate the number of parts
Percent = (nCorrect / TotalParts) * 100; // Determine the percentage that are correctly located
m_nScore = Percent; // Set the score equal to this percentage
} // End CheckForWin()
void CMainWindow::OnClose() {
CDC *pDC;
CBrush myBrush;
CRect myRect;
pDC = GetDC();
myRect.SetRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
myBrush.CreateStockObject(BLACK_BRUSH);
(*pDC).FillRect(&myRect, &myBrush);
ReleaseDC(pDC);
// delete the game theme song
//
if (pGameSound != nullptr) { // IF we're playing the game theme song,
delete pGameSound; //...get rid of it
pGameSound = nullptr;
}
CSound::clearSounds(); // Make sure it's cleared before returning to Metagame
if (m_pScrollButton != nullptr)
delete m_pScrollButton;
if (m_pTimeText != nullptr)
delete m_pTimeText;
if (pBlankBitmap != nullptr) {
pBlankBitmap->DeleteObject();
delete pBlankBitmap;
}
if (pLocaleBitmap != nullptr) {
pLocaleBitmap->DeleteObject();
delete pLocaleBitmap;
}
if (pOldBmp1 != nullptr) // Get rid of Scratch1
pScratch1DC->SelectObject(pOldBmp1);
if (pOldPal1 != nullptr)
pScratch1DC->SelectPalette(pOldPal1, false);
if (pScratch1DC->m_hDC != nullptr) {
pScratch1DC->DeleteDC();
delete pScratch1DC;
}
pScratch1->DeleteObject();
delete pScratch1;
if (pOldBmp2 != nullptr) // Get rid of Scratch2
pScratch2DC->SelectObject(pOldBmp2);
if (pOldPal2 != nullptr)
pScratch2DC->SelectPalette(pOldPal2, false);
if (pScratch2DC->m_hDC != nullptr) {
pScratch2DC->DeleteDC();
delete pScratch2DC;
}
if (pGamePalette != nullptr) {
pGamePalette->DeleteObject();
delete pGamePalette;
}
CFrameWnd::OnClose();
MFC::PostMessage(ghParentWnd, WM_PARENTNOTIFY, WM_DESTROY, 0L);
}
void CMainWindow::GetSubOptions(CWnd *pParentWind) {
COptnDlg OptionsDlg(pParentWind, pGamePalette); // Call Specific Game
//...Options dialog box
OptionsDlg.m_nColumns = m_nColumns;
OptionsDlg.m_nRows = m_nRows;
OptionsDlg.m_nTime = m_nTime;
OptionsDlg.m_nMins = nMinutes;
OptionsDlg.m_nSecs = nSeconds;
OptionsDlg.m_nScore = m_nScore;
OptionsDlg.m_bFramed = bFramed;
if (OptionsDlg.DoModal() == IDOK) { // save values set in dialog box
tempTime = OptionsDlg.m_nTime; // get new time limit,
tempRows = OptionsDlg.m_nRows; //...new rows, and cols
tempColumns = OptionsDlg.m_nColumns; //...and store in temps
tempFramed = OptionsDlg.m_bFramed;
}
}
//////////// Additional Sound Notify routines //////////////
LRESULT CMainWindow::OnMCINotify(WPARAM wParam, LPARAM lParam) {
CSound *pSound;
pSound = CSound::OnMCIStopped(wParam, lParam);
if (pSound != nullptr)
OnSoundNotify(pSound);
return 0;
}
LRESULT CMainWindow::OnMMIONotify(WPARAM wParam, LPARAM lParam) {
CSound *pSound;
pSound = CSound::OnMMIOStopped(wParam, lParam);
if (pSound != nullptr)
OnSoundNotify(pSound);
return 0;
}
void CMainWindow::OnSoundNotify(CSound *pSound) {
//
// Add your code to process explicit notification of a sound "done" event here.
// pSound is a pointer to a CSound object for which you requested SOUND_NOTIFY.
//
}
// CMainWindow message map:
// Associate messages with member functions.
//
BEGIN_MESSAGE_MAP(CMainWindow, CFrameWnd)
//{{AFX_MSG_MAP( CMainWindow )
ON_WM_PAINT()
ON_WM_CHAR()
ON_WM_SYSKEYDOWN()
ON_WM_KEYDOWN()
ON_WM_TIMER()
ON_WM_MBUTTONDOWN()
ON_WM_LBUTTONDOWN()
ON_WM_RBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_DESTROY()
ON_WM_CLOSE()
ON_MESSAGE(MM_MCINOTIFY, CMainWindow::OnMCINotify)
ON_MESSAGE(MM_WOM_DONE, CMainWindow::OnMMIONotify)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
} // namespace ArtParts
} // namespace HodjNPodj
} // namespace Bagel