/* 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/afxwin.h" #include "bagel/hodjnpodj/hnplibs/stdafx.h" #include "bagel/boflib/misc.h" #include "bagel/hodjnpodj/hnplibs/sprite.h" #include "bagel/hodjnpodj/globals.h" #include "bagel/hodjnpodj/hnplibs/mainmenu.h" #include "bagel/hodjnpodj/hnplibs/cmessbox.h" #include "bagel/hodjnpodj/hnplibs/text.h" #include "bagel/hodjnpodj/hnplibs/gamedll.h" #include "bagel/hodjnpodj/hnplibs/rules.h" #include "bagel/hodjnpodj/hnplibs/button.h" #include "bagel/hodjnpodj/garfunkle/resource.h" #include "bagel/hodjnpodj/garfunkle/garfunkle.h" #include "bagel/hodjnpodj/garfunkle/optndlg.h" #include "bagel/hodjnpodj/garfunkle/note.h" #include "bagel/hodjnpodj/hodjnpodj.h" namespace Bagel { namespace HodjNPodj { namespace Garkfunkle { void CALLBACK GetSubOptions(CWnd* pParentWind); void add_note_to_series(int nNewValue); CBmpButton *m_pScrollButton; // Scroll button CPalette *pGamePalette = nullptr; // Palette of current artwork bool bSuccess = false; bool m_bIgnoreScrollClick; bool bLDown = false; // Make sure we only act on LUp if LDown was on a musician bool bPlayingBackSeries = false; #define FIRST_MUSICIAN 100 #define NOT_PLAYING 0 #define NOT_THERE 1 // // Musician-related stuff: // CSound *pMusic = nullptr; // The sound object for the musician's music CSprite *pAnimSprite[MAX_BUTTONS]; // Pointer for the animating musician CBitmap *pMusicians[(MAX_BUTTONS * 2)]; // Bitmap for Not_playing and Not_there int m_nButID = 0; // button which is animating CText *m_pSignText = nullptr; // The current series length display on the sign CBitmap *pRibbon = nullptr; // The blue ribbon to be put on sign at win condition bool m_bPlaying; bool m_bNewGame = false; bool m_bPlayGame; // Options variables int m_nSpeed; // Speed is in MILLISECONDS int m_nNumButtons; int m_nWinCondition = 0; // Number needed to win the game (in meta mode only) // Temporary variables for new Options bool tempPlayGame; int tempSpeed; // Speed is in MILLISECONDS int tempNumButtons; int nNoteCount = 0; int nCheckCount = 0; unsigned int nSButFlag = MAX_BUTTONS; CNote *pNoteMarker; static const char *cSoundName[MAX_BUTTONS] = { VIOLIN_SOUND, CELLO_SOUND, DRUM_SOUND, SAX_SOUND, HARP_SOUND, CLARINET_SOUND }; static const char *cAnimName[MAX_BUTTONS] = { VIOLIN_ANIM, CELLO_ANIM, DRUM_ANIM, SAX_ANIM, HARP_ANIM, CLARINET_ANIM }; static const int nNumCels[MAX_BUTTONS] = { VIOLIN_CELS, CELLO_CELS, DRUM_CELS, SAX_CELS, HARP_CELS, CLARINET_CELS }; static const POINT Offset[MAX_BUTTONS] = { { VIOLIN_OFFSET_X, VIOLIN_OFFSET_Y }, { CELLO_OFFSET_X, CELLO_OFFSET_Y }, { DRUM_OFFSET_X, DRUM_OFFSET_Y }, { SAX_OFFSET_X, SAX_OFFSET_Y }, { HARP_OFFSET_X, HARP_OFFSET_Y }, { CLARINET_OFFSET_X, CLARINET_OFFSET_Y } }; extern LPGAMESTRUCT pGameInfo; extern HWND ghParentWnd; ///////////////////////////////////////////////////////////////////////////// // 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() { CDC *pDC; CString WndClass; CRect MainRect, StartRect; CBitmap *pBackBitmap = nullptr; CPalette *pOldPal = nullptr; int i, j; BeginWaitCursor(); initStatics(); pGameInfo->lScore = 0L; // 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; ReleaseDC(pDC); // 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 -- Garfunkel", WS_POPUP, MainRect, nullptr, 0); pDC = GetDC(); pBackBitmap = FetchBitmap(pDC, &pGamePalette, MAINSCREEN); pOldPal = pDC->SelectPalette(pGamePalette, false); // select the game palette pDC->RealizePalette(); //...and realize it pBackBitmap->DeleteObject(); delete pBackBitmap; ShowWindow(SW_SHOWNORMAL); // Give 'em something to look at SplashScreen(); // Paint the backdrop with no musicians // Build Scroll Command button m_pScrollButton = new CBmpButton; ASSERT(m_pScrollButton != nullptr); StartRect.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, StartRect, this, IDC_SCROLL); ASSERT(bSuccess); bSuccess = (*m_pScrollButton).LoadBitmaps(SCROLLUP, SCROLLDOWN, 0, 0); ASSERT(bSuccess); m_bIgnoreScrollClick = false; if (pGameInfo->bPlayingMetagame) // only in metamode pRibbon = FetchBitmap(pDC, nullptr, RIBBON); // load bitmap for ribbon // Initialize the musician bitmaps and locations rectMusic[0].SetRect(VIOLIN_LOCATION_X, VIOLIN_LOCATION_Y, VIOLIN_LOCATION_X + VIOLIN_WIDTH, VIOLIN_LOCATION_Y + VIOLIN_HEIGHT); rectMusic[1].SetRect(CELLO_LOCATION_X, CELLO_LOCATION_Y, CELLO_LOCATION_X + CELLO_WIDTH, CELLO_LOCATION_Y + CELLO_HEIGHT); rectMusic[2].SetRect(DRUM_LOCATION_X, DRUM_LOCATION_Y, DRUM_LOCATION_X + DRUM_WIDTH, DRUM_LOCATION_Y + DRUM_HEIGHT); rectMusic[3].SetRect(SAX_LOCATION_X, SAX_LOCATION_Y, SAX_LOCATION_X + SAX_WIDTH, SAX_LOCATION_Y + SAX_HEIGHT); rectMusic[4].SetRect(HARP_LOCATION_X, HARP_LOCATION_Y, HARP_LOCATION_X + HARP_WIDTH, HARP_LOCATION_Y + HARP_HEIGHT); rectMusic[5].SetRect(CLARINET_LOCATION_X, CLARINET_LOCATION_Y, CLARINET_LOCATION_X + CLARINET_WIDTH, CLARINET_LOCATION_Y + CLARINET_HEIGHT); for (i = 0; i < MAX_BUTTONS; i++) { for (j = 0; j < 2; j++) { pMusicians[(i * 2) + j] = FetchResourceBitmap(pDC, nullptr, FIRST_MUSICIAN + (i * 2) + j); } pAnimSprite[i] = new CSprite; (*pAnimSprite[i]).SharePalette(pGamePalette); bSuccess = (*pAnimSprite[i]).LoadCels(pDC, cAnimName[i], nNumCels[i]); ASSERT(bSuccess); (*pAnimSprite[i]).SetMasked(false); (*pAnimSprite[i]).SetMobile(false); } // Set up the text on the sign to display the current series length StartRect.SetRect(SIGN_LOCATION_X, SIGN_LOCATION_Y, SIGN_LOCATION_X + SIGN_WIDTH, SIGN_LOCATION_Y + SIGN_HEIGHT); if ((m_pSignText = new CText()) != nullptr) { (*m_pSignText).SetupText(pDC, pGamePalette, &StartRect, JUSTIFY_CENTER); } (*pDC).SelectPalette(pOldPal, false); // Select back the old palette ReleaseDC(pDC); //srand((unsigned) time(nullptr)); // seed the random number generator m_bPlayGame = true; if (pGameInfo->bPlayingMetagame) { switch (pGameInfo->nSkillLevel) { case SKILLLEVEL_LOW: m_nNumButtons = 4; m_nSpeed = 6; // Allegretto m_nWinCondition = LOW_WIN; break; case SKILLLEVEL_MEDIUM: m_nNumButtons = 5; m_nSpeed = 8; // Vivace m_nWinCondition = MEDIUM_WIN; break; case SKILLLEVEL_HIGH: m_nNumButtons = 6; m_nSpeed = 10; // Prestissimo m_nWinCondition = HIGH_WIN; break; } // end switch } else { m_nNumButtons = MAX_BUTTONS; // 6 m_nSpeed = 8; // Vivace } tempPlayGame = m_bPlayGame; tempNumButtons = m_nNumButtons; tempSpeed = m_nSpeed; m_bPlaying = false; GetNewSequence(MAX_SEQUENCE); // Get a random sequence nNoteCount = 1; // Set the count to the first note nCheckCount = 0; // Haven't checked any input yet MSG lpmsg; // Remove any messages in the queue before starting while (PeekMessage(&lpmsg, m_hWnd, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)) ; // prevent random 'hits' EndWaitCursor(); if (pGameInfo->bPlayingMetagame == false) { ActivateButtons(m_nNumButtons, NOT_PLAYING); // Activate the buttons PostMessage(WM_COMMAND, IDC_SCROLL, BN_CLICKED); // Activate the Options dialog } } //End of CMainWindow void CMainWindow::initStatics() { m_pScrollButton = nullptr; pGamePalette = nullptr; bSuccess = false; m_bIgnoreScrollClick = false; bLDown = false; bPlayingBackSeries = false; pMusic = nullptr; Common::fill(pAnimSprite, pAnimSprite + MAX_BUTTONS, nullptr); Common::fill(pMusicians, pMusicians + (MAX_BUTTONS * 2), nullptr); m_nButID = 0; m_pSignText = nullptr; pRibbon = nullptr; m_bPlaying = false; m_bNewGame = false; m_bPlayGame = false; m_nSpeed = 0; m_nNumButtons = 0; m_nWinCondition = 0; tempPlayGame = false; tempSpeed = 0; tempNumButtons = 0; nNoteCount = 0; nCheckCount = 0; nSButFlag = MAX_BUTTONS; pNoteMarker = nullptr; } // 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() { PAINTSTRUCT lpPaint; InvalidateRect(nullptr, false); // invalidate the entire window BeginPaint(&lpPaint); SplashScreen(); // Paint the backdrop and scroll button ActivateButtons(m_nNumButtons, NOT_PLAYING); // Activate the buttons EndPaint(&lpPaint); } // Paint the background art (splash screen) in the client area; // and repaint the scroll button at the top // called by both OnPaint and InitInstance. void CMainWindow::SplashScreen() { CRect rcDest; CRect rcDIB; CDC *pDC; CDibDoc myDoc; HDIB hDIB; char msg[4]; pDC = GetDC(); myDoc.OpenDocument(MAINSCREEN); 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); } if (m_bPlayGame) { Common::sprintf_s(msg, "%d", nNoteCount - 1); (*m_pSignText).DisplayString(pDC, msg, 32, FW_NORMAL, SIGN_COLOR); if (pGameInfo->bPlayingMetagame && (nNoteCount > m_nWinCondition)) { PaintMaskedBitmap(pDC, pGamePalette, pRibbon, RIBBON_X, RIBBON_Y); } } 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) { CDC *pDC; CNote *pNewNote; CSound *pEffect = nullptr; KillTimer(PLAYER_TIMER); pDC = GetDC(); if (HIWORD(lParam) == BN_CLICKED) { CRules RulesDlg((CWnd *)this, RULES_TEXT, pGamePalette, pGameInfo->bSoundEffectsEnabled ? RULES_SOUND : nullptr); CMainMenu COptionsWind((CWnd *)this, pGamePalette, pGameInfo->bPlayingMetagame ? (NO_NEWGAME | NO_OPTIONS) : 0, GetSubOptions, RULES_TEXT, pGameInfo->bSoundEffectsEnabled ? RULES_SOUND : nullptr, pGameInfo) ; // Construct Option dialog switch (wParam) { case IDC_START: // And we're off! if (pGameInfo->bPlayingMetagame) wait_awhile(PAUSE_TIME); // Give the resources, etc. time to load m_bPlaying = true; m_bNewGame = true; pNewNote = CNote::GetNoteHead(); // Get the first note if (pNewNote) { // if list is not empty PlayBackSeries(nNoteCount); // Play the first note pNoteMarker = CNote::GetNoteHead(); // set checking pointer pNewNote = nullptr; // Stop pointing at the note list SetTimer(PLAYER_TIMER, TIME_LIMIT, nullptr); // Give the player TIME_LIMIT to respond } //...to head of list else // no note chain, m_bPlaying = false; //...so we can't play break; case IDC_A: // If a musician button was hit... case IDC_B: case IDC_C: case IDC_D: case IDC_E: case IDC_F: unsigned int nButID, nHitID; char msg[4]; nHitID = wParam - IDC_A; // The musician buttons are consecutive //...get the index of the one pressed if (m_bPlayGame) { // If they're not just playing music if (pNoteMarker) // If we're playing a game nButID = pNoteMarker->GetValue(); // Get the Index of the correct note else break; if (nButID == nHitID) { // If the one pressed is the correct note pNoteMarker = pNoteMarker->GetNextNote(); // Move to the next nCheckCount++; // Increment the number correct so far } else { // They hit the wrong button :-( char buf[64]; Common::sprintf_s(buf, "Longest series: %d", nNoteCount - 1); if (pGameInfo->bSoundEffectsEnabled) { pEffect = new CSound((CWnd *)this, WRONG_SOUND, SOUND_WAVE | SOUND_ASYNCH | SOUND_AUTODELETE); //...Wave file, to delete itself (*pEffect).play(); //...play the narration } MSG lpmsg; // Clear out any extraneous mouse clicks while (PeekMessage(&lpmsg, m_hWnd, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)) ; CMessageBox GameOverDlg((CWnd *)this, pGamePalette, "Wrong musician!", buf); CSound::waitWaveSounds(); pNoteMarker = nullptr; m_bPlaying = false; m_bNewGame = false; CNote::FlushNoteList(); // Flush the last series of notes if (pGameInfo->bPlayingMetagame) PostMessage(WM_CLOSE, 0, 0); // and post a program exit break; } if (nCheckCount == nNoteCount) { // If they completed the series pNoteMarker = nullptr; if (nNoteCount == MAX_SEQUENCE) { nNoteCount++; // Hitting MAX increments NoteCount to MAX + 1 Common::sprintf_s(msg, "%d", nNoteCount - 1); //...so return is correct (*m_pSignText).DisplayString(pDC, msg, 32, FW_NORMAL, SIGN_COLOR); if (pGameInfo->bPlayingMetagame && (nNoteCount > m_nWinCondition)) { PaintMaskedBitmap(pDC, pGamePalette, pRibbon, RIBBON_X, RIBBON_Y); } if (pGameInfo->bSoundEffectsEnabled) { pEffect = new CSound((CWnd *)this, WIN_SOUND, SOUND_WAVE | SOUND_ASYNCH | SOUND_AUTODELETE); //...Wave file, to delete itself (*pEffect).play(); //...play the narration } MSG lpmsg; while (PeekMessage(&lpmsg, m_hWnd, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)) ; CMessageBox GameOverDlg((CWnd *)this, pGamePalette, "Game over", "You have won!"); CSound::waitWaveSounds(); m_bPlaying = false; m_bNewGame = false; CNote::FlushNoteList(); if (pGameInfo->bPlayingMetagame) PostMessage(WM_CLOSE, 0, 0); // and post a program exit } else { nNoteCount++; Common::sprintf_s(msg, "%d", nNoteCount - 1); (*m_pSignText).DisplayString(pDC, msg, 32, FW_NORMAL, SIGN_COLOR); if (pGameInfo->bPlayingMetagame && (nNoteCount > m_nWinCondition)) { PaintMaskedBitmap(pDC, pGamePalette, pRibbon, RIBBON_X, RIBBON_Y); } wait_awhile(PAUSE_TIME); // Pause before playing sequence nCheckCount = 0; // Reset checking counter if ((nNoteCount % INCREMENT_RATE == 0) && (m_nSpeed != MAX_SPEED)) m_nSpeed++; // on multiples of the increment rate //...increase the speed by one PlayBackSeries(nNoteCount); MSG lpmsg; while (PeekMessage(&lpmsg, m_hWnd, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)) ; SetTimer(PLAYER_TIMER, TIME_LIMIT, nullptr); //Reset time limit } pNoteMarker = CNote::GetNoteHead(); //set checking pointer to head of list } else SetTimer(PLAYER_TIMER, TIME_LIMIT, nullptr); //Reset time limit } break; case IDC_RULES: m_bIgnoreScrollClick = true; (*m_pScrollButton).SendMessage(BM_SETSTATE, true, 0L); CSound::waitWaveSounds(); RulesDlg.DoModal(); m_bIgnoreScrollClick = false; (*m_pScrollButton).SendMessage(BM_SETSTATE, false, 0L); break; case IDC_SCROLL: 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); switch (COptionsWind.DoModal()) { case IDC_OPTIONS_NEWGAME: if (!pGameInfo->bPlayingMetagame) NewGame(); (*m_pScrollButton).SendMessage(BM_SETSTATE, false, 0L); m_bIgnoreScrollClick = false; break; case IDC_OPTIONS_RETURN: (*m_pScrollButton).SendMessage(BM_SETSTATE, false, 0L); m_bIgnoreScrollClick = false; if (m_bPlayGame && m_bNewGame) { // playing repeat game & already wait_awhile(PAUSE_TIME); // started...pause for a second PostMessage(WM_COMMAND, IDC_START, BN_CLICKED); // Activate the Options dialog } break; case IDC_OPTIONS_QUIT: // Quit button was clicked PostMessage(WM_CLOSE, 0, 0); // and post a program exit return false; } //end switch(ComDlg.DoModal()) } //end switch(wParam) } // end if ReleaseDC(pDC); (*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, rectWoodsRight, rectWoodsLeft, rectSign, rectBench; CDC *pDC; CSound *pEffect = nullptr; int i, nPick = 0; char bufName[64]; pDC = GetDC(); rectTitle.SetRect(NEWGAME_LOCATION_X, NEWGAME_LOCATION_Y, NEWGAME_LOCATION_X + NEWGAME_WIDTH, NEWGAME_LOCATION_Y + NEWGAME_HEIGHT); rectWoodsRight.SetRect(WOODRIGHT_LOCATION_X, WOODRIGHT_LOCATION_Y, WOODRIGHT_LOCATION_X + WOODRIGHT_WIDTH, WOODRIGHT_LOCATION_Y + WOODRIGHT_HEIGHT); rectWoodsLeft.SetRect(WOODLEFT_LOCATION_X, WOODLEFT_LOCATION_Y, WOODLEFT_LOCATION_X + WOODLEFT_WIDTH, WOODLEFT_LOCATION_Y + WOODLEFT_HEIGHT); rectSign.SetRect(SIGN_LOCATION_X, SIGN_LOCATION_Y, SIGN_LOCATION_X + SIGN_WIDTH, SIGN_LOCATION_Y + SIGN_HEIGHT); rectBench.SetRect(BENCH_LOCATION_X, BENCH_LOCATION_Y, BENCH_LOCATION_X + BENCH_WIDTH, BENCH_LOCATION_Y + BENCH_HEIGHT); if (rectTitle.PtInRect(point) && (!pGameInfo->bPlayingMetagame)) NewGame(); // Activate New Game else if (m_bNewGame && m_bPlaying) { for (i = 0; i < m_nNumButtons; i++) { if (rectMusic[i].PtInRect(point)) { bLDown = true; KillTimer(PLAYER_TIMER); // don't time out m_nButID = i; StartAnimation(); wait_awhile(10 * ((NUM_SPEEDS - MAX_SPEED) + SLOW_DOWN)); return; //break; } // end if ptinrect } // end for } // end else if m_bPlaying else if (!m_bNewGame) { // if (pGameInfo->bPlayingMetagame) PostMessage(WM_COMMAND, IDC_START, BN_CLICKED); // Activate the imaginary 'start' button return; } if (rectWoodsRight.PtInRect(point) || rectWoodsLeft.PtInRect(point)) { if (pGameInfo->bSoundEffectsEnabled) { KillTimer(PLAYER_TIMER); // so it doesn't run out pEffect = new CSound((CWnd *)this, TREES_SOUND, SOUND_WAVE | SOUND_AUTODELETE); // Wave file, sync, to delete itself (*pEffect).play(); // Play the sound if (m_bPlayGame && m_bNewGame) SetTimer(PLAYER_TIMER, TIME_LIMIT, nullptr); // Reset response time limit } } else if (rectSign.PtInRect(point)) { if (pGameInfo->bSoundEffectsEnabled) { KillTimer(PLAYER_TIMER); // so it doesn't run out nPick = brand() % NUM_SIGN_SOUNDS; switch (nPick) { case 0: Common::sprintf_s(bufName, SIGN_1_SOUND); break; case 1: Common::sprintf_s(bufName, SIGN_2_SOUND); break; case 2: Common::sprintf_s(bufName, SIGN_3_SOUND); break; case 3: Common::sprintf_s(bufName, SIGN_4_SOUND); break; default: Common::sprintf_s(bufName, SIGN_5_SOUND); break; } pEffect = new CSound((CWnd *)this, bufName, SOUND_WAVE | SOUND_AUTODELETE); // Wave file, to delete itself (*pEffect).play(); // play the sound if (m_bPlayGame && m_bNewGame) SetTimer(PLAYER_TIMER, TIME_LIMIT, nullptr); // Reset response time limit } } else if (rectBench.PtInRect(point)) { if (pGameInfo->bSoundEffectsEnabled) { KillTimer(PLAYER_TIMER); // so it doesn't run out pEffect = new CSound((CWnd *)this, BENCH_SOUND, SOUND_WAVE | SOUND_AUTODELETE); // Wave file, to delete itself (*pEffect).play(); // Play the sound if (m_bPlayGame && m_bNewGame) SetTimer(PLAYER_TIMER, TIME_LIMIT, nullptr); // Reset response time limit } } ReleaseDC(pDC); } void CMainWindow::OnLButtonUp(unsigned int nFlags, CPoint point) { if (m_bNewGame) { if (m_bPlaying && bLDown) { if (pAnimSprite[m_nButID] != nullptr) { StopAnimation(); MFC::SendMessage(m_hWnd, WM_COMMAND, m_nButID + IDC_A, BN_CLICKED); // Activate hit logic } bLDown = false; } } } void CMainWindow::OnRButtonDown(unsigned int nFlags, CPoint point) { if (!m_bNewGame) { PostMessage(WM_COMMAND, IDC_START, BN_CLICKED); // Activate the imaginary 'start' button } } /***************************************************************** * * 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) { if (bPlayingBackSeries) { SetCursor(LoadCursor(nullptr, IDC_WAIT)); // Refresh cursor object } else { SetCursor(LoadCursor(nullptr, IDC_ARROW)); // Refresh cursor object } CFrameWnd ::OnMouseMove(nFlags, point); } // 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::OnSysChar(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags) { if ((nChar == 'q') && (nFlags & 0x2000)) // terminate app on ALT-q PostMessage(WM_CLOSE, 0, 0); // *** remove later *** else 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)) // terminate app on ALT-q 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) { if (!bPlayingBackSeries) { if (nChar == VK_F1) { // F1 key is hit SendMessage(WM_COMMAND, IDC_RULES, BN_CLICKED); // Activate the Rules dialog } else if (nChar == VK_F2) { // F2 key is hit SendMessage(WM_COMMAND, IDC_SCROLL, BN_CLICKED); // Activate the Options dialog } } /* one-plus of low priority else if ((nFlags & 0x40) == 0) { // if the key was previously up switch (nChar) { case '1': i = 4; break; case '2': i = 0; break; case '3': i = 1; break; case '4': i = 2; break; case '5': i = 3; break; case '6': i = 5; break; default: break; } // end switch if ((i < MAX_BUTTONS) && (m_bPlaying)) { KillTimer( PLAYER_TIMER ); // don't time out m_nButID = i; StartAnimation(); } } // end else switch */ } /* one-plus of low priority void CMainWindow::OnKeyUp(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags) { bool bMusician = false; switch (nChar) { case '1': case '2': case '3': case '4': case '5': case '6': bMusician = true; break; default: break; } // end switch if (bMusician && m_bPlaying) { if (pAnimSprite[m_nButID] != nullptr) { StopAnimation(); ::SendMessage( m_hWnd, WM_COMMAND, m_nButID + IDC_A, BN_CLICKED ); // Activate hit logic } } } */ /***************************************************************** * * 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 = nullptr; CSound *pEffect = nullptr; pDC = GetDC(); switch (nIDEvent) { case PLAYER_TIMER: { char buf[64]; Common::sprintf_s(buf, "Longest series: %d", nNoteCount - 1); if (pAnimSprite[m_nButID] != nullptr) // If there's an animation StopAnimation(); //...running, stop it KillTimer(nIDEvent); // Stop this timer if (pGameInfo->bSoundEffectsEnabled) { pEffect = new CSound((CWnd *)this, SLOW_SOUND, SOUND_WAVE | SOUND_ASYNCH | SOUND_AUTODELETE); //...Wave file, to delete itself (*pEffect).play(); //...play the narration } MSG lpmsg; while (PeekMessage(&lpmsg, m_hWnd, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)) ; CMessageBox GameOverDlg((CWnd *)this, pGamePalette, "Time ran out!", buf); CSound::waitWaveSounds(); pNoteMarker = nullptr; m_bPlaying = false; CNote::FlushNoteList(); if (pGameInfo->bPlayingMetagame) PostMessage(WM_CLOSE, 0, 0); // and post a program exit } KillTimer(nIDEvent); // Stop the timer break; case ANIM_TIMER: (*pAnimSprite[m_nButID]).PaintSprite(pDC, rectMusic[m_nButID].TopLeft().x - Offset[m_nButID].x, rectMusic[m_nButID].TopLeft().y - Offset[m_nButID].y); break; default: CFrameWnd ::OnTimer(nIDEvent); KillTimer(nIDEvent); //Stop the timer break; } // end switch ReleaseDC(pDC); } void CMainWindow::OnActivate(unsigned int nState, CWnd *pWndOther, bool bMinimized) { if (!bMinimized) { switch (nState) { case WA_ACTIVE: case WA_CLICKACTIVE: //InvalidateRect(nullptr, false); break; default: break; } } } /********************************************************** The following functions handle operations on the list of notes: Getting a new sequence by either: reading in a list from a file, or generating a random list, Adding a new member to the list, and Playing back the entire sequence. ***********************************************************/ void CMainWindow::NewGame() { CDC *pDC; char msg[4]; pDC = GetDC(); (*m_pScrollButton).SendMessage(BM_SETSTATE, false, 0L); m_bIgnoreScrollClick = false; m_bPlayGame = tempPlayGame; m_nNumButtons = tempNumButtons; m_nSpeed = tempSpeed; m_bNewGame = false; CNote::FlushNoteList(); ActivateButtons(m_nNumButtons, NOT_PLAYING); // Bring on the players!! if (m_bPlayGame) { GetNewSequence(MAX_SEQUENCE); nNoteCount = 1; nCheckCount = 0; Common::sprintf_s(msg, "%d", nNoteCount - 1); (*m_pSignText).DisplayString(pDC, msg, 32, FW_NORMAL, SIGN_COLOR); } else { m_bPlaying = true; // Make sure we can play music Common::strcpy_s(msg, ""); (*m_pSignText).DisplayString(pDC, msg, 32, FW_NORMAL, SIGN_COLOR); } ReleaseDC(pDC); } void CMainWindow::StartAnimation() { CDC *pDC; pDC = GetDC(); (*pAnimSprite[m_nButID]).SetCel(nNumCels[m_nButID]); SetTimer(ANIM_TIMER, ANIM_SLEEP, nullptr); if (pGameInfo->bSoundEffectsEnabled) { pMusic = new CSound((CWnd *)this, cSoundName[m_nButID], SOUND_MIDI | SOUND_ASYNCH | SOUND_LOOP | SOUND_NOTIFY); //...Midi file, looping | SOUND_NOTIFY (*pMusic).play(); //...play the sound } (*pAnimSprite[m_nButID]).PaintSprite(pDC, rectMusic[m_nButID].TopLeft().x - Offset[m_nButID].x, rectMusic[m_nButID].TopLeft().y - Offset[m_nButID].y); ReleaseDC(pDC); } void CMainWindow::StopAnimation() { CDC *pDC; pDC = GetDC(); KillTimer(ANIM_TIMER); if (pAnimSprite[m_nButID] != nullptr) { pAnimSprite[m_nButID]->EraseSprite(pDC); } if (pMusic != nullptr) { (*pMusic).stop(); delete pMusic; pMusic = nullptr; } CSound::clearSounds(); PaintBitmap(pDC, pGamePalette, pMusicians[(m_nButID * 2) + NOT_PLAYING], rectMusic[m_nButID].TopLeft().x, rectMusic[m_nButID].TopLeft().y); ReleaseDC(pDC); } bool CMainWindow::GetNewSequence(const char *pszFileName) { int nNote; char note[5]; ifstream IFStream(pszFileName); if (IFStream.fail()) { return false; } while (!IFStream.eof()) { IFStream.getline(note, sizeof(note)); nNote = atoi(note); add_note_to_series(nNote); } return true; }//end GetNewSequence() bool CMainWindow::GetNewSequence(int nLength) { int i; for (i = nLength; i > 0; --i) { add_note_to_series(brand() % m_nNumButtons); } return true; }//end GetNewSequence() void CMainWindow::ActivateButtons(unsigned int nNumActive, bool bState) { CDC *pDC; CPalette *pOldPal = nullptr; unsigned int i; pDC = GetDC(); pOldPal = pDC->SelectPalette(pGamePalette, false); // select the game palette pDC->RealizePalette(); //...and realize it for (i = 0; i < nNumActive; i++) { PaintBitmap(pDC, pGamePalette, pMusicians[(i * 2) + bState], rectMusic[i].TopLeft().x, rectMusic[i].TopLeft().y); } while (i < MAX_BUTTONS) { PaintBitmap(pDC, pGamePalette, pMusicians[(i * 2) + NOT_THERE], rectMusic[i].TopLeft().x, rectMusic[i].TopLeft().y); i++; } (*pDC).SelectPalette(pOldPal, false); // Select back the old palette ReleaseDC(pDC); }//end ActivateButtons() void add_note_to_series(int nNewValue) { CNote *pNewNote; if ((pNewNote = new CNote()) == 0) { MessageBox(nullptr, "Could not create note!!", nullptr, MB_ICONEXCLAMATION); } (*pNewNote).SetValue(nNewValue); (*pNewNote).LinkNote(); //Add the new note to the bottom of the list }//end add_note_to_series void CMainWindow::PlayBackSeries(int nNumNotes) { CDC *pDC; int i; CNote *pNewNote; pDC = GetDC(); bPlayingBackSeries = true; // HCURSOR HOldCursor = MFC::SetCursor( AfxGetApp ()->LoadStandardCursor( nullptr ) ); // Make cursor go away HCURSOR HOldCursor = MFC::SetCursor(LoadCursor(nullptr, IDC_WAIT)); // Refresh cursor object MFC::ShowCursor(true); pNewNote = CNote::GetNoteHead(); for (i = nNumNotes; i > 0; --i) { if (g_engine->shouldQuit()) break; if (pNewNote) { // If this isn't the end m_nButID = pNewNote->GetValue(); // Get the new note StartAnimation(); wait_awhile(10 * ((NUM_SPEEDS - m_nSpeed) + SLOW_DOWN)); StopAnimation(); pNewNote = pNewNote->GetNextNote(); // And move to the next if (i != 1) wait_awhile(5 * (NUM_SPEEDS - m_nSpeed)); // Process events while-you-wait! } } pNewNote = nullptr; MFC::ShowCursor(false); MFC::SetCursor(HOldCursor); bPlayingBackSeries = false; ReleaseDC(pDC); }//end PlayBackSeries bool CMainWindow::wait_awhile(int nHundSecs) { // Given time is in hundreths of sec uint32 goal; MSG msg; goal = (nHundSecs * 10) + GetTickCount(); // time is in millisecs while (goal > GetTickCount()) { pause(); if (PeekMessage(&msg, m_hWnd, 0, WM_MOUSEMOVE, PM_REMOVE)) { // Remove any messages except if (msg.message == WM_CLOSE || msg.message == WM_QUIT) //...quit & close, which get break; //...sent to the queue TranslateMessage(&msg); DispatchMessage(&msg); } if (PeekMessage(&msg, m_hWnd, WM_PARENTNOTIFY, 0xFFFF, PM_REMOVE)) { if (msg.message == WM_CLOSE || msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } } // spin yer wheels 'til nSecs pass return true; }//end wait_awhile() void CMainWindow::OnClose() { CDC *pDC; CBrush myBrush; CRect myRect; int i; if (pGameInfo->bPlayingMetagame) pGameInfo->lScore = (long)(nNoteCount - 1); // Hitting MAX increments NoteCount to MAX + 1 //...so return is correct pDC = GetDC(); myRect.SetRect(0, 0, GAME_WIDTH, GAME_HEIGHT); myBrush.CreateStockObject(BLACK_BRUSH); (*pDC).FillRect(&myRect, &myBrush); ReleaseDC(pDC); CNote::FlushNoteList(); // Delete list from memory CSound::clearSounds(); // Make sure there's no sounds on return if (m_pSignText != nullptr) delete m_pSignText; for (i = 0; i < (MAX_BUTTONS * 2); i++) { if (pMusicians[i] != nullptr) { //pMusicians[i]->DeleteObject; delete pMusicians[i]; // Bitmap for Not_playing and Not_there } } for (i = 0; i < MAX_BUTTONS; i++) { if (pAnimSprite[i] != nullptr) { pAnimSprite[i]->EraseSprite(pDC); delete pAnimSprite[i]; pAnimSprite[i] = nullptr; } } if (m_pScrollButton != nullptr) delete m_pScrollButton; if (pGamePalette != nullptr) { pGamePalette->DeleteObject(); delete pGamePalette; } CFrameWnd::OnClose(); MFC::PostMessage(ghParentWnd, WM_PARENTNOTIFY, WM_DESTROY, 0L); } void CALLBACK GetSubOptions(CWnd* pParentWind) { COptnDlg OptionsDlg(pParentWind, pGamePalette); // Call Specific Game OptionsDlg.m_bPlayGame = m_bPlayGame; OptionsDlg.m_nNumButtons = m_nNumButtons; OptionsDlg.m_nSpeed = tempSpeed; if (OptionsDlg.DoModal() == IDOK) { // save values set in dialog box tempPlayGame = OptionsDlg.m_bPlayGame;; // get new time limit, tempNumButtons = OptionsDlg.m_nNumButtons; //...new rows, and cols tempSpeed = OptionsDlg.m_nSpeed; } } //////////// 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_SYSCHAR() ON_WM_SYSKEYDOWN() ON_WM_KEYDOWN() // ON_WM_KEYUP() ON_WM_TIMER() ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_RBUTTONDOWN() ON_WM_MOUSEMOVE() ON_WM_CLOSE() ON_WM_ACTIVATE() ON_MESSAGE(MM_MCINOTIFY, CMainWindow::OnMCINotify) ON_MESSAGE(MM_WOM_DONE, CMainWindow::OnMMIONotify) //}}AFX_MSG_MAP END_MESSAGE_MAP() } // namespace Garfunkle } // namespace HodjNPodj } // namespace Bagel