/* 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 . * */ #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