/* 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/dibdoc.h" #include "bagel/hodjnpodj/hnplibs/stdinc.h" #include "bagel/hodjnpodj/hnplibs/text.h" #include "bagel/hodjnpodj/globals.h" #include "bagel/hodjnpodj/hnplibs/sprite.h" #include "bagel/hodjnpodj/hnplibs/mainmenu.h" #include "bagel/hodjnpodj/hnplibs/cmessbox.h" #include "bagel/boflib/misc.h" #include "bagel/hodjnpodj/hnplibs/rules.h" #include "bagel/boflib/error.h" #include "bagel/hodjnpodj/hnplibs/button.h" #include "bagel/hodjnpodj/hnplibs/gamedll.h" #include "bagel/boflib/sound.h" #include "bagel/hodjnpodj/fuge/fuge.h" #include "bagel/hodjnpodj/fuge/usercfg.h" #include "bagel/hodjnpodj/hodjnpodj.h" namespace Bagel { namespace HodjNPodj { namespace Fuge { extern CWnd *ghParentWnd; #define CSOUND 0 // // This mini-game's main screen bitmap // #define MINI_GAME_MAP ".\\ART\\FUGE6.BMP" // Fuge sprite bitmaps // #define IDB_BALL 103 #define N_PADDLE_CELS 31 #define PADDLE_START_X 285 #define PADDLE_START_Y 205 #define PADDLE_CEL_JUMP 1 #define FACE_ANGLE (double)0.977322 #define PADDLE0_ANGLE (PI/4) // 45 degrees #define PADDLE1_ANGLE (PI/3) // 60 degrees #define PADDLE2_ANGLE (PI/2) // 90 degrees #define MOUSE_SENS 32 #define BLACKHOLE_RADIUS 14 #define PADDLE_RADIUS 34 #define INNER_BRICK_RADIUS 102 #define ROW6_RADIUS 114 #define ROW5_RADIUS 129 #define ROW4_RADIUS 145 #define ROW3_RADIUS 160 #define ROW2_RADIUS 175 #define ROW1_RADIUS 190 #define WHEEL_RADIUS 191 #define BRICK0_ANGLE 0.000000 #define BRICK1_ANGLE 0.392699 #define BRICK2_ANGLE 0.785398 #define BRICK3_ANGLE 1.178097 #define BRICK4_ANGLE 1.570796 #define BRICK5_ANGLE 1.963495 #define BRICK6_ANGLE 2.356194 #define BRICK7_ANGLE 2.748894 #define BRICK8_ANGLE 3.141593 #define BRICK9_ANGLE 3.534291 #define BRICK10_ANGLE 3.926990 #define BRICK11_ANGLE 4.319690 #define BRICK12_ANGLE 4.712389 #define BRICK13_ANGLE 5.105088 #define BRICK14_ANGLE 5.497787 #define BRICK15_ANGLE 5.890486 #define N_BLACKHOLE_CELS 5 #define N_BRICK_POINTS 21 #define N_BALL_MOVES 3 #define BALL_START_X 309 #define BALL_START_Y 180 #define BALL_SIZE_X 20 #define BALL_SIZE_Y 20 #define BALL_RADIUS (BALL_SIZE_X/2) #define CENTER_X 319 #define CENTER_Y 239 #define G_FORCE 0.009000 #define TURBO_MIN 0 #define TURBO_DEF 10 #define TURBO_MAX 10 #define EXTRA_LIFE_SCORE 100 // // Button ID constants // #define IDC_MENU 100 #define TIMER_ID 10 #define TIMER_INTERVAL 50 // Sounds // #define WAV_PADDLE ".\\SOUND\\PADDLE.WAV" #define WAV_WALL ".\\SOUND\\WALL.WAV" #define WAV_WINWAVE ".\\SOUND\\WINWAVE.WAV" #define WAV_GAMEOVER ".\\SOUND\\SOSORRY.WAV" //GAMEOVER.WAV" #define WAV_LOSEBALL ".\\SOUND\\TOILET.WAV" //LOSEBALL.WAV" #define WAV_NARRATION ".\\SOUND\\FUGE.WAV" #define MID_SOUNDTRACK ".\\SOUND\\FUGE.MID" #define WAV_CAR1 ".\\SOUND\\SICK.WAV" #define WAV_CAR2 ".\\SOUND\\FERRIS.WAV" #define WAV_TENT ".\\SOUND\\LAWYER.WAV" #define WAV_BOOTH ".\\SOUND\\STEPUP.WAV" #define WAV_PEOPLE1 ".\\SOUND\\BALLOON.WAV" #define WAV_PEOPLE2 ".\\SOUND\\AUNTEDNA.WAV" #define N_PADDLE_SIZES (PSIZE_MAX+1) STATIC const char *pszPaddles[N_PADDLE_SIZES] = { ".\\ART\\PADCEL45.BMP", ".\\ART\\PADCEL60.BMP", ".\\ART\\PADCEL90.BMP" }; STATIC const double fPaddleAngles[N_PADDLE_SIZES] = { PADDLE0_ANGLE, PADDLE1_ANGLE, PADDLE2_ANGLE }; #define NUM_WAVS 2 //There are two people and two car sounds // Audio Easter Egg areas // #define TENT_X 16 #define TENT_Y 320 #define TENT_DX 75 #define TENT_DY 47 #define BOOTH_X 503 #define BOOTH_Y 377 #define BOOTH_DX 93 #define BOOTH_DY 53 #define PEOPLE_X 27 #define PEOPLE_Y 370 #define PEOPLE_DX 102 #define PEOPLE_DY 60 #define CAR1_X 131 #define CAR1_Y 385 #define CAR_DX 41 #define CAR_DY 49 #define CAR2_X 84 #define CAR2_Y 231 #define CAR3_X 95 #define CAR3_Y 149 #define CAR4_X 133 #define CAR4_Y 73 #define CAR5_X 465 #define CAR5_Y 75 #define CAR6_X 503 #define CAR6_Y 150 #define CAR7_X 514 #define CAR7_Y 230 #define CAR8_X 500 #define CAR8_Y 315 #define CAR9_X 218 #define CAR9_Y 23 #define CAR9_DX 37 #define CAR9_DY 39 #define CAR10_X 384 #define CAR10_Y 23 #define CAR10_DX 39 #define CAR10_DY 39 // // Globals // STATIC const char *pszFugeArt[N_ROWS + 1] = { ".\\ART\\FUGE6.BMP", ".\\ART\\FUGE1.BMP", ".\\ART\\FUGE2.BMP", ".\\ART\\FUGE3.BMP", ".\\ART\\FUGE4.BMP", ".\\ART\\FUGE5.BMP", ".\\ART\\FUGE6.BMP" }; STATIC CPalette *pGamePalette; const char *INI_SECTION = "Fuge"; LPGAMESTRUCT pGameParams; extern HWND ghParentWnd; typedef struct { VECTOR v1, v2; } BRICK_VECTORS; STATIC const BRICK_VECTORS aBrickVectors[BRICKS_PER_ROW] = { {{-1, 1, 0}, { 5, -2, 0}}, {{-5, 2, 0}, { 1, 0, 0}}, {{-1, 0, 0}, { 5, 2, 0}}, {{-5, -2, 0}, { 1, 1, 0}}, {{-1, -1, 0}, { 2, 5, 0}}, {{-2, -5, 0}, { 0, 1, 0}}, {{ 0, -1, 0}, {-2, 5, 0}}, {{ 2, -5, 0}, {-1, 1, 0}}, {{ 1, -1, 0}, {-5, 2, 0}}, {{ 5, -2, 0}, {-1, 0, 0}}, {{ 1, 0, 0}, {-5, -2, 0}}, {{ 5, 2, 0}, {-1, -1, 0}}, {{ 1, 1, 0}, {-2, -5, 0}}, {{ 2, 5, 0}, { 0, -1, 0}}, {{ 0, 1, 0}, { 2, -5, 0}}, {{-2, 5, 0}, { 1, -1, 0}} }; STATIC const VECTOR BRICK_CRIT_POINTS[N_ROWS][N_BRICK_POINTS] = { { { 0, -191, 0}, { 8, -191, 0}, { 15, -190, 0}, { 23, -190, 0}, { 31, -188, 0}, { 39, -187, 0}, { 47, -185, 0}, { 55, -183, 0}, { 62, -181, 0}, { 69, -178, 0}, { 67, -172, 0}, { 64, -165, 0}, { 56, -168, 0}, { 48, -171, 0}, { 40, -173, 0}, { 32, -174, 0}, { 24, -175, 0}, { 16, -176, 0}, { 8, -177, 0}, { 0, -177, 0}, { 0, -184, 0} }, { { 0, -175, 0}, { 7, -175, 0}, { 14, -175, 0}, { 21, -174, 0}, { 28, -173, 0}, { 35, -172, 0}, { 42, -170, 0}, { 49, -168, 0}, { 56, -166, 0}, { 63, -164, 0}, { 61, -158, 0}, { 59, -152, 0}, { 52, -154, 0}, { 44, -157, 0}, { 36, -159, 0}, { 29, -160, 0}, { 21, -162, 0}, { 14, -162, 0}, { 7, -163, 0}, { 0, -163, 0}, { 0, -169, 0} }, { { 0, -161, 0}, { 7, -161, 0}, { 13, -160, 0}, { 19, -160, 0}, { 26, -159, 0}, { 33, -158, 0}, { 39, -156, 0}, { 45, -155, 0}, { 51, -153, 0}, { 58, -150, 0}, { 56, -143, 0}, { 54, -137, 0}, { 48, -139, 0}, { 41, -141, 0}, { 35, -143, 0}, { 28, -144, 0}, { 21, -146, 0}, { 14, -146, 0}, { 7, -147, 0}, { 0, -147, 0}, { 0, -154, 0} }, { { 0, -145, 0}, { 7, -145, 0}, { 13, -145, 0}, { 19, -144, 0}, { 25, -143, 0}, { 31, -142, 0}, { 36, -141, 0}, { 41, -139, 0}, { 47, -138, 0}, { 53, -135, 0}, { 51, -129, 0}, { 48, -122, 0}, { 42, -124, 0}, { 37, -126, 0}, { 31, -127, 0}, { 26, -129, 0}, { 19, -130, 0}, { 13, -130, 0}, { 7, -131, 0}, { 0, -131, 0}, { 0, -138, 0} }, { { 0, -129, 0}, { 6, -129, 0}, { 11, -129, 0}, { 16, -128, 0}, { 21, -127, 0}, { 26, -127, 0}, { 31, -125, 0}, { 36, -124, 0}, { 41, -123, 0}, { 47, -121, 0}, { 45, -115, 0}, { 43, -109, 0}, { 37, -111, 0}, { 32, -112, 0}, { 27, -114, 0}, { 22, -115, 0}, { 17, -116, 0}, { 11, -116, 0}, { 6, -117, 0}, { 0, -117, 0}, { 0, -123, 0} }, { { 0, -115, 0}, { 6, -115, 0}, { 11, -115, 0}, { 16, -114, 0}, { 20, -113, 0}, { 25, -112, 0}, { 29, -111, 0}, { 33, -110, 0}, { 37, -109, 0}, { 42, -107, 0}, { 40, -102, 0}, { 38, -96, 0}, { 33, -98, 0}, { 29, -99, 0}, { 25, -100, 0}, { 21, -101, 0}, { 16, -102, 0}, { 11, -102, 0}, { 6, -103, 0}, { 0, -103, 0}, { 0, -109, 0} } }; STATIC VECTOR vBrickCritPoints[N_ROWS][N_BRICK_POINTS]; STATIC const POINT ptBrickPos[N_BRICKS] = { {184, 62}, {250, 48}, {319, 48}, {385, 62}, // Purple Bricks {445, 104}, {483, 165}, {483, 240}, {445, 309}, {385, 365}, {321, 404}, {250, 404}, {184, 365}, {144, 309}, {128, 240}, {128, 164}, {144, 105}, {195, 76}, {256, 64}, {319, 64}, {380, 76}, // Blue Bricks {435, 115}, {469, 171}, {469, 240}, {435, 304}, {380, 355}, {321, 391}, {256, 391}, {195, 355}, {158, 304}, {143, 240}, {143, 171}, {158, 115}, {206, 90}, {261, 78}, {319, 78}, {375, 90}, // Green Bricks {424, 126}, {455, 177}, {455, 240}, {424, 298}, {375, 344}, {321, 376}, {261, 376}, {206, 344}, {171, 298}, {158, 240}, {158, 177}, {171, 126}, {217, 104}, {266, 94}, {319, 94}, {369, 104}, // Yellow Bricks {413, 137}, {441, 183}, {441, 240}, {413, 292}, {369, 333}, {321, 361}, {266, 361}, {217, 333}, {185, 292}, {173, 240}, {173, 183}, {185, 137}, {228, 119}, {272, 110}, {319, 110}, {364, 119}, // Orange Bricks {402, 148}, {427, 189}, {427, 240}, {402, 286}, {364, 322}, {321, 348}, {272, 348}, {228, 322}, {200, 286}, {189, 240}, {189, 189}, {200, 148}, {238, 133}, {277, 124}, {319, 124}, {359, 133}, // Red Bricks {392, 158}, {414, 195}, {414, 240}, {392, 280}, {359, 312}, {321, 335}, {277, 335}, {238, 312}, {214, 280}, {204, 240}, {204, 195}, {214, 158} }; STATIC const SIZE ptBrickSize[N_BRICKS] = { {70, 52}, {68, 27}, {70, 27}, {70, 52}, {50, 66}, {28, 74}, {28, 75}, {50, 66}, {70, 52}, {68, 27}, {70, 27}, {69, 52}, {50, 66}, {28, 74}, {28, 75}, {50, 65}, {64, 48}, {62, 24}, {64, 24}, {64, 48}, {46, 60}, {27, 68}, {27, 68}, {46, 60}, {64, 48}, {62, 24}, {64, 24}, {64, 48}, {46, 60}, {27, 68}, {27, 68}, {46, 60}, {58, 45}, {57, 25}, {59, 25}, {58, 45}, {44, 55}, {26, 62}, {26, 62}, {44, 55}, {58, 45}, {57, 25}, {59, 25}, {58, 45}, {44, 55}, {26, 62}, {26, 62}, {44, 55}, {53, 42}, {52, 24}, {54, 24}, {53, 42}, {41, 50}, {25, 56}, {25, 56}, {41, 50}, {53, 42}, {52, 24}, {54, 24}, {53, 42}, {41, 50}, {25, 56}, {25, 56}, {41, 50}, {47, 38}, {46, 21}, {48, 21}, {47, 38}, {37, 45}, {23, 50}, {23, 50}, {37, 45}, {47, 38}, {46, 21}, {48, 21}, {47, 38}, {37, 45}, {23, 50}, {23, 50}, {37, 45}, {42, 34}, {41, 20}, {43, 20}, {42, 34}, {33, 41}, {21, 44}, {21, 44}, {33, 41}, {42, 34}, {41, 20}, {43, 20}, {42, 34}, {33, 41}, {21, 44}, {21, 44}, {33, 41} }; // Local Prototypes // void CALLBACK GetGameParams(CWnd *); CFugeWindow::CFugeWindow() : gvCenter(CENTER_X, CENTER_Y) { CString WndClass; CRect tmpRect; CDC *pDC; CBitmap *pBmp; ERROR_CODE errCode; bool bSuccess; // assume no error errCode = ERR_NONE; // Initialize fields initMembers(); initStatics(); // Set the coordinates for the "Start New Game" button // m_rNewGameButton.SetRect(NEWGAME_LOCATION_X, NEWGAME_LOCATION_Y, NEWGAME_LOCATION_X + NEWGAME_WIDTH, NEWGAME_LOCATION_Y + NEWGAME_HEIGHT); // 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_DBLCLKS | CS_BYTEALIGNWINDOW | CS_OWNDC, nullptr, nullptr, nullptr); // can't play this game if the background art is not available // if (FileExists(MINI_GAME_MAP)) { // get this game's palette // if ((pDC = GetDC()) != nullptr) { if ((pBmp = FetchBitmap(pDC, &m_pGamePalette, MINI_GAME_MAP)) != nullptr) { delete pBmp; pBmp = nullptr; pGamePalette = m_pGamePalette; } ReleaseDC(pDC); } } else { errCode = ERR_FFIND; } // Center our window on the screen // tmpRect.SetRect(0, 0, GAME_WIDTH, GAME_HEIGHT); #ifndef DEBUG if ((pDC = GetDC()) != nullptr) { tmpRect.left = (pDC->GetDeviceCaps(HORZRES) - GAME_WIDTH) >> 1; tmpRect.top = (pDC->GetDeviceCaps(VERTRES) - GAME_HEIGHT) >> 1; tmpRect.right = tmpRect.left + GAME_WIDTH; tmpRect.bottom = tmpRect.top + GAME_HEIGHT; ReleaseDC(pDC); } else { errCode = ERR_UNKNOWN; } #endif // set mouse anchor point as center or game window // m_ptOrigin.x = GAME_WIDTH / 2 + tmpRect.left; m_ptOrigin.y = GAME_HEIGHT / 2 + tmpRect.top; // 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 -- Fuge", WS_POPUP, tmpRect, nullptr, 0); BeginWaitCursor(); ShowWindow(SW_SHOWNORMAL); PaintScreen(); EndWaitCursor(); ClipCursor(&tmpRect); // only continue if there was no error // if (errCode == ERR_NONE) { if ((m_pScrollButton = new CBmpButton) != nullptr) { m_bIgnoreScrollClick = false; tmpRect.SetRect(SCROLL_BUTTON_X, SCROLL_BUTTON_Y, SCROLL_BUTTON_X + SCROLL_BUTTON_DX, SCROLL_BUTTON_Y + SCROLL_BUTTON_DY); bSuccess = m_pScrollButton->Create(nullptr, BS_OWNERDRAW | WS_CHILD | WS_VISIBLE, tmpRect, this, IDC_MENU); assert(bSuccess); if (bSuccess) { bSuccess = m_pScrollButton->LoadBitmaps(SCROLLUP, SCROLLDOWN, SCROLLUP, SCROLLUP); assert(bSuccess); } else { errCode = ERR_UNKNOWN; } } else { errCode = ERR_MEMORY; } // only continue if there was no error // if (errCode == ERR_NONE) { BeginWaitCursor(); // Start the Fuge soundtrack // if (pGameParams->bMusicEnabled) { if ((m_pSoundTrack = new CSound) != nullptr) { m_pSoundTrack->initialize(this, MID_SOUNDTRACK, SOUND_MIDI | SOUND_LOOP | SOUND_DONT_LOOP_TO_END); m_pSoundTrack->midiLoopPlaySegment(5390, 32280, 0, FMT_MILLISEC); } else { errCode = ERR_MEMORY; } } // seed the random number generator //srand((unsigned int)time(nullptr)); // // The vector table is rotated by 11 or so degrees, because it // was easier to type in (125, 320) as opposed to (114.452865, 300.526372). // So I do the conversion here. // RealignVectors(); if (!errCode) errCode = LoadMasterSprites(); if (!errCode) errCode = LoadMasterSounds(); InitializeJoystick(); EndWaitCursor(); // as long as there was no error // if (!errCode) { // if we are not playing from the metagame // if (!pGameParams->bPlayingMetagame) { // Automatically bring up the main menu PostMessage(WM_COMMAND, IDC_MENU, BN_CLICKED); } } } } HandleError(errCode); } void CFugeWindow::initMembers() { m_nInitNumBalls = BALLS_DEF; m_nInitStartLevel = LEVEL_DEF; m_nInitBallSpeed = SPEED_DEF; m_nInitPaddleSize = PSIZE_DEF; m_nGForceFactor = GFORCE_DEF; m_pScrollButton = nullptr; m_pGamePalette = nullptr; m_pSoundTrack = nullptr; m_bPause = false; m_bGameActive = false; m_bIgnoreScrollClick = false; m_bBallOnPaddle = false; m_nPaddleCelIndex = 29; m_pPaddle = nullptr; m_pBall = nullptr; m_bMovingPaddle = false; m_lScore = 0; m_pBrickSound = nullptr; m_pWallSound = nullptr; m_pPaddleSound = nullptr; m_pExtraLifeSound = nullptr; m_hBrickRes = nullptr; m_hWallRes = nullptr; m_hPaddleRes = nullptr; m_hExtraLifeRes = nullptr; pGameParams->lScore = 0; m_nNumRows = 0; m_bJoyActive = false; memset(m_bBrickVisible, 0, sizeof(bool) * N_BRICKS); } void CFugeWindow::initStatics() { pGamePalette = nullptr; // Set up the brick critical points from defaults Common::copy(&BRICK_CRIT_POINTS[0][0], &BRICK_CRIT_POINTS[0][0] + N_ROWS * N_BRICK_POINTS, &vBrickCritPoints[0][0]); } void CFugeWindow::InitializeJoystick() { JOYINFO joyInfo; if (joySetCapture(m_hWnd, JOYSTICKID1, 10000, true) == JOYERR_NOERROR) { // // Calibrate the joystick // joySetThreshold(JOYSTICKID1, 5000); joyGetPos(JOYSTICKID1, &joyInfo); m_nJoyOrgX = joyInfo.wXpos; m_nJoyOrgY = joyInfo.wYpos; m_bJoyActive = true; } else { //CMessageBox dlgNoJoystick((CWnd *)this, m_pGamePalette, "Warning! No Joystick", "Driver Installed"); } } void CFugeWindow::RealignVectors() { CVector vTmp; int i, j; for (i = 0; i < N_ROWS; i++) { for (j = 0; j < N_BRICK_POINTS; j++) { vTmp = vBrickCritPoints[i][j]; vTmp.Rotate(2 * PI / BRICKS_PER_ROW * -2); vBrickCritPoints[i][j] = vTmp; } } } ERROR_CODE CFugeWindow::LoadMasterSprites() { CDC *pDC; ERROR_CODE errCode; errCode = LoadNewPaddle(m_nInitPaddleSize); if (errCode == ERR_NONE) { if ((pDC = GetDC()) != nullptr) { if ((m_pBall = new CSprite) != nullptr) { if (m_pBall->SharePalette(m_pGamePalette) != false) { if (m_pBall->LoadResourceSprite(pDC, IDB_BALL) != false) { m_pBall->SetMasked(true); m_pBall->SetMobile(true); // uncomment this if we decide to animate the ball as // it moves //if (m_pBall->LoadResourceCels(pDC, IDB_BALLSTRIP, N_BALLS) == false) // errCode = ERR_UNKNOWN; } else { errCode = ERR_UNKNOWN; } } else { errCode = ERR_UNKNOWN; } } else { errCode = ERR_MEMORY; } ReleaseDC(pDC); } else { errCode = ERR_MEMORY; } } return errCode; } void CFugeWindow::ReleaseMasterSprites() { assert(m_pBall != nullptr); if (m_pBall != nullptr) { delete m_pBall; m_pBall = nullptr; } assert(m_pPaddle != nullptr); if (m_pPaddle != nullptr) { delete m_pPaddle; m_pPaddle = nullptr; } } void CFugeWindow::HandleError(ERROR_CODE errCode) { // // Exit this application on fatal errors // if (errCode != ERR_NONE) { // pause the current game (if any) GamePause(); // Display Error Message to the user MessageBox(errList[errCode], "Fatal Error!", MB_OK | MB_ICONSTOP); // Force this application to terminate PostMessage(WM_CLOSE, 0, 0); // Don't allow a repaint (remove all WM_PAINT messages) ValidateRect(nullptr); } } void CFugeWindow::OnPaint() { PAINTSTRUCT lpPaint; Invalidate(false); BeginPaint(&lpPaint); PaintScreen(); EndPaint(&lpPaint); } void CFugeWindow::PaintScreen() { CDC *pDC; // // Paint the background art and upadate any sprites called by OnPaint // if ((pDC = GetDC()) != nullptr) { // painting the bricks will paint the screen // PaintBricks(pDC); RepaintSpriteList(pDC); ReleaseDC(pDC); } } /***************************************************************************** * * RepaintSpriteList - * * DESCRIPTION: Longer description of this function. Continued onto next * line like this. * * SAMPLE USAGE: * errCode = RepaintSpriteList(pDC); * CDC *pDC; pointer to current device context * * RETURNS: ERROR_CODE = error return code * *****************************************************************************/ void CFugeWindow::RepaintSpriteList(CDC *pDC) { CSprite *pSprite; // can't use a null pointer assert(pDC != nullptr); // // Paint each sprite // pSprite = CSprite::GetSpriteChain(); while (pSprite) { pSprite->ClearBackground(); pSprite = pSprite->GetNextSprite(); } pSprite = CSprite::GetSpriteChain(); while (pSprite) { pSprite->RefreshSprite(pDC); pSprite = pSprite->GetNextSprite(); } } bool CFugeWindow::OnCommand(WPARAM wParam, LPARAM lParam) { CMainMenu COptionsWind((CWnd *)this, m_pGamePalette, (pGameParams->bPlayingMetagame ? (NO_NEWGAME | NO_OPTIONS) : 0) | (m_bGameActive ? 0 : NO_RETURN), GetGameParams, "fuge.txt", (pGameParams->bSoundEffectsEnabled ? WAV_NARRATION : nullptr), pGameParams); if (HIWORD(lParam) == BN_CLICKED) { switch (wParam) { // // must bring up our menu of controls // case IDC_MENU: // hide the command scroll // m_pScrollButton->SendMessage(BM_SETSTATE, true, 0); if (!m_bIgnoreScrollClick) { m_bIgnoreScrollClick = true; GamePause(); CSound::waitWaveSounds(); // Get users choice from command menu // switch (COptionsWind.DoModal()) { // User has chosen to play a new game // case IDC_OPTIONS_NEWGAME: PlayGame(); break; // User has chosen to quit this mini-game // case IDC_OPTIONS_QUIT: PostMessage(WM_CLOSE, 0, 0); break; default: break; } // show the command scroll // m_pScrollButton->SendMessage(BM_SETSTATE, false, 0); m_bIgnoreScrollClick = false; if (!pGameParams->bMusicEnabled && (m_pSoundTrack != nullptr)) { m_pSoundTrack->stop(); delete m_pSoundTrack; m_pSoundTrack = nullptr; } else if (pGameParams->bMusicEnabled && (m_pSoundTrack == nullptr)) { if ((m_pSoundTrack = new CSound) != nullptr) { m_pSoundTrack->initialize(this, MID_SOUNDTRACK, SOUND_MIDI | SOUND_LOOP | SOUND_DONT_LOOP_TO_END); m_pSoundTrack->midiLoopPlaySegment(5390, 32280, 0, FMT_MILLISEC); } } GameResume(); } return true; } } return false; } void CFugeWindow::GamePause() { m_bPause = true; } void CFugeWindow::GameResume() { if (!m_bBallOnPaddle && m_bMovingPaddle) m_bPause = false; } void CFugeWindow::PlayGame() { ERROR_CODE errCode; // load the .INI settings // LoadIniSettings(); // reset all game parameters // GameReset(); errCode = LoadNewPaddle(m_nInitPaddleSize); if (errCode == ERR_NONE) { // // Start game // // link StartBricks(); StartBall(); StartPaddle(); // game starts paused m_bPause = true; m_bGameActive = true; SetTimer(TIMER_ID, TIMER_INTERVAL, nullptr); } HandleError(errCode); } ERROR_CODE CFugeWindow::LoadNewPaddle(int nNewSize) { CDC *pDC; ERROR_CODE errCode; assert(nNewSize >= PSIZE_MIN && nNewSize <= PSIZE_MAX); // assume no error errCode = ERR_NONE; if ((pDC = GetDC()) != nullptr) { // don't try to load the same paddle // if (m_nOldSize != nNewSize) { if (m_pPaddle != nullptr) { m_pPaddle->EraseSprite(pDC); m_pPaddle->UnlinkSprite(); delete m_pPaddle; m_pPaddle = nullptr; } if ((m_pPaddle = new CSprite) != nullptr) { if (m_pPaddle->SharePalette(m_pGamePalette) != false) { if (m_pPaddle->LoadCels(pDC, pszPaddles[nNewSize], N_PADDLE_CELS) != false) { m_nOldSize = nNewSize; m_pPaddle->SetMasked(true); m_pPaddle->SetMobile(true); m_pPaddle->SetAnimated(true); } else { errCode = ERR_UNKNOWN; } } else { errCode = ERR_UNKNOWN; } } else { errCode = ERR_MEMORY; } } ReleaseDC(pDC); } else { errCode = ERR_MEMORY; } return errCode; } ERROR_CODE CFugeWindow::LoadMasterSounds() { HANDLE hResInfo; HINSTANCE hInst; ERROR_CODE errCode; // assume no error errCode = ERR_NONE; hInst = (HINSTANCE)GetWindowWord(m_hWnd, GWW_HINSTANCE); // Load and lock the Brick "ping" into memory // if ((hResInfo = FindResource(hInst, "brickSound", "WAVE")) != nullptr) { if ((m_hBrickRes = LoadResource(hInst, (HRSRC)hResInfo)) != nullptr) { if ((m_pBrickSound = (char *)LockResource((HGLOBAL)m_hBrickRes)) != nullptr) { // we have now loaded at least one of the master sounds } else { errCode = ERR_UNKNOWN; } } else { errCode = ERR_UNKNOWN; } } else { errCode = ERR_UNKNOWN; } if (errCode == ERR_NONE) { // Load and lock the wall "ping" into memory // if ((hResInfo = FindResource(hInst, "wallSound", "WAVE")) != nullptr) { if ((m_hWallRes = LoadResource(hInst, (HRSRC)hResInfo)) != nullptr) { if ((m_pWallSound = (char *)LockResource((HGLOBAL)m_hWallRes)) == nullptr) errCode = ERR_UNKNOWN; } else { errCode = ERR_UNKNOWN; } } else { errCode = ERR_UNKNOWN; } } if (errCode == ERR_NONE) { // Load and lock the paddle "ping" into memory // if ((hResInfo = FindResource(hInst, "paddleSound", "WAVE")) != nullptr) { if ((m_hPaddleRes = LoadResource(hInst, (HRSRC)hResInfo)) != nullptr) { if ((m_pPaddleSound = (char *)LockResource((HGLOBAL)m_hPaddleRes)) == nullptr) errCode = ERR_UNKNOWN; } else { errCode = ERR_UNKNOWN; } } else { errCode = ERR_UNKNOWN; } } if (errCode == ERR_NONE) { // Load and lock the extra life sound into memory // if ((hResInfo = FindResource(hInst, "NewLife", "WAVE")) != nullptr) { if ((m_hExtraLifeRes = LoadResource(hInst, (HRSRC)hResInfo)) != nullptr) { if ((m_pExtraLifeSound = (char *)LockResource((HGLOBAL)m_hExtraLifeRes)) == nullptr) errCode = ERR_UNKNOWN; } else { errCode = ERR_UNKNOWN; } } else { errCode = ERR_UNKNOWN; } } return errCode; } void CFugeWindow::ReleaseMasterSounds() { if (m_hExtraLifeRes != nullptr) { FreeResource(m_hExtraLifeRes); m_hExtraLifeRes = nullptr; } if (m_pPaddleSound != nullptr) { FreeResource(m_hPaddleRes); m_pPaddleSound = nullptr; } if (m_pWallSound != nullptr) { FreeResource(m_hWallRes); m_pWallSound = nullptr; } if (m_pBrickSound != nullptr) { FreeResource(m_hBrickRes); m_pBrickSound = nullptr; } } void CFugeWindow::EndBall() { CDC *pDC; assert(m_pBall != nullptr); if ((pDC = GetDC()) != nullptr) { if (m_pBall->IsLinked()) { m_pBall->EraseSprite(pDC); m_pBall->UnlinkSprite(); } ReleaseDC(pDC); } } void CFugeWindow::StartBall() { CDC *pDC; assert(m_pBall != nullptr); // have the ball start on the paddle like in arkinoids // m_ptBallLocation = BallOnPaddle(); m_pBall->LinkSprite(); if ((pDC = GetDC()) != nullptr) { m_pBall->PaintSprite(pDC, (int)m_ptBallLocation.x, (int)m_ptBallLocation.y); ReleaseDC(pDC); } } CVector CFugeWindow::BallOnPaddle() { CVector vBall(0, -(PADDLE_RADIUS + BALL_RADIUS)); vBall.Rotate(fPaddleAngles[m_nInitPaddleSize] / 2); vBall.Rotate(m_nPaddleCelIndex * ((2 * PI) / N_PADDLE_CELS)); vBall -= CVector(BALL_RADIUS, BALL_RADIUS); // this vector was relative to center so now make it a real point vBall += gvCenter; return vBall; } void CFugeWindow::PaintBall() { CPoint ptLast; CVector vBall, vGravity; double length; CDC *pDC; if (m_bGameActive && !m_bPause && !m_bBallOnPaddle) { assert(m_pBall != nullptr); ptLast.x = (int)m_ptBallLocation.x; ptLast.y = (int)m_ptBallLocation.y; vGravity = gvCenter - (m_ptBallLocation + BALL_RADIUS); vGravity.Unitize(); // calc new ball location // if (m_nGForceFactor != 0) { vGravity *= G_FORCE * m_nGForceFactor; m_vBallVector += vGravity; m_ptBallLocation += m_vBallVector * (m_fTurboBoost + m_nBallSpeed); } else { vGravity *= G_FORCE * 10; m_ptBallLocation += (m_vBallVector + vGravity) * (m_fTurboBoost + m_nBallSpeed); } assert(m_fTurboBoost <= TURBO_MAX); m_fTurboBoost -= 0.05; if (m_fTurboBoost < 0.0) m_fTurboBoost = 0.0; m_vBallVector.Unitize(); // get radius of the ball from the center of the screen // vBall = m_ptBallLocation + BALL_RADIUS - gvCenter; length = vBall.Length() + BALL_RADIUS; // check to see if ball has entered the balck hole // if (length <= BLACKHOLE_RADIUS + BALL_RADIUS * 2) { // Play the ball-gets-sucked-into-black-hole animation // LoseBall(); m_bPaddleHit = false; // or has ball hit the paddle? // } else if (length <= PADDLE_RADIUS + BALL_RADIUS * 2) { BallvsPaddle(); // or has ball hit a brick? // } else if ((length >= INNER_BRICK_RADIUS) && (length < WHEEL_RADIUS + (m_bOutterWall ? BALL_RADIUS * 2 : 0))) { // // determine which row ball is in // BallvsBrick(length); // // determine if a ball actually hit a brick // m_bPaddleHit = false; // or did ball hit edge of ferris wheel // } else if (length >= WHEEL_RADIUS) { if (m_bOutterWall) { // has ball hit right border // if (m_ptBallLocation.x >= GAME_WIDTH - GAME_RIGHT_BORDER_WIDTH - BALL_SIZE_X) { m_ptBallLocation.x = GAME_WIDTH - GAME_RIGHT_BORDER_WIDTH - BALL_SIZE_X; m_vBallVector.x = -m_vBallVector.x; // randomly rotate 1 or -1 degrees m_vBallVector.Rotate(Deg2Rad((brand() & 1) ? 0.125 : 0)); m_fTurboBoost = (double)12 - m_nBallSpeed; if (pGameParams->bSoundEffectsEnabled) { sndPlaySound(m_pWallSound, SND_MEMORY | SND_ASYNC | SND_NODEFAULT); } // has ball hit left border // } else if (m_ptBallLocation.x <= 0 + GAME_LEFT_BORDER_WIDTH) { m_ptBallLocation.x = 0 + GAME_LEFT_BORDER_WIDTH; m_vBallVector.x = -m_vBallVector.x; // randomly rotate 1 or -1 degrees m_vBallVector.Rotate(Deg2Rad((brand() & 1) ? 0.125 : 0)); m_fTurboBoost = (double)12 - m_nBallSpeed; if (pGameParams->bSoundEffectsEnabled) { sndPlaySound(m_pWallSound, SND_MEMORY | SND_ASYNC | SND_NODEFAULT); } // has ball hit bottom of screen // } else if (m_ptBallLocation.y >= GAME_HEIGHT - GAME_BOTTOM_BORDER_WIDTH - BALL_SIZE_Y) { m_ptBallLocation.y = GAME_HEIGHT - GAME_BOTTOM_BORDER_WIDTH - BALL_SIZE_Y; m_vBallVector.y = -m_vBallVector.y; // randomly rotate 1 or -1 degrees m_vBallVector.Rotate(Deg2Rad((brand() & 1) ? 0.125 : 0)); m_fTurboBoost = (double)12 - m_nBallSpeed; if (pGameParams->bSoundEffectsEnabled) { sndPlaySound(m_pWallSound, SND_MEMORY | SND_ASYNC | SND_NODEFAULT); } // has ball hit top of screen // } else if (m_ptBallLocation.y <= 0 + GAME_TOP_BORDER_WIDTH) { m_ptBallLocation.y = 0 + GAME_TOP_BORDER_WIDTH; m_vBallVector.y = -m_vBallVector.y; // randomly rotate 1 or -1 degrees m_vBallVector.Rotate(Deg2Rad((brand() & 1) ? 0.125 : 0)); m_fTurboBoost = (double)12 - m_nBallSpeed; if (pGameParams->bSoundEffectsEnabled) { sndPlaySound(m_pWallSound, SND_MEMORY | SND_ASYNC | SND_NODEFAULT); } } } else { if (pGameParams->bSoundEffectsEnabled) { sndPlaySound(m_pWallSound, SND_MEMORY | SND_ASYNC | SND_NODEFAULT); } // pull ball back to just on the border // m_ptBallLocation -= m_vBallVector * (length - WHEEL_RADIUS + 1); m_vBallVector.x = -m_vBallVector.x; m_vBallVector.y = -m_vBallVector.y; m_vBallVector.Reflect(vBall); } m_bPaddleHit = false; } // only paint the ball if it actually moved // if ((ptLast.x != (int)m_ptBallLocation.x) || (ptLast.y != (int)m_ptBallLocation.y)) { if (m_pBall->IsLinked()) { // paint the ball to it's new location // if ((pDC = GetDC()) != nullptr) { m_pBall->PaintSprite(pDC, (int)m_ptBallLocation.x, (int)m_ptBallLocation.y); ReleaseDC(pDC); } } } } } #define N_CRIT_POINTS 7 void CFugeWindow::BallvsPaddle() { CVector vPoints[N_CRIT_POINTS]; CVector vTmp, vPaddle, vFace, vBallCenter, vBallEdge; double a1, a2; double fLen1, fLen2, fLen3, fLen4, fLen5, fLen6, fMin, length; int i, j, k; int nRollBack = 0; bool bHit; // calculate the 7 critical points for the paddle // vTmp.SetVector(0, -PADDLE_RADIUS); // cel index determines paddle angle // vTmp.Rotate((2 * PI / N_PADDLE_CELS) * m_nPaddleCelIndex); vPoints[1] = gvCenter + vTmp; vPoints[0] = vTmp; vPoints[0].Rotate(fPaddleAngles[m_nInitPaddleSize] / 2); vPoints[0] += gvCenter; vPoints[2] = vTmp; vPoints[2].Rotate(fPaddleAngles[m_nInitPaddleSize]); vPoints[2] += gvCenter; vTmp.Unitize(); vTmp *= 25; vPoints[3] = gvCenter + vTmp; vPoints[4] = vTmp; vPoints[4].Rotate(fPaddleAngles[m_nInitPaddleSize]); vPoints[4] += gvCenter; vPoints[5] = vPoints[1]; vPoints[6] = vPoints[2]; if (m_nInitPaddleSize > PSIZE_MIN) { vTmp = vPoints[0] - vPoints[1]; vTmp.Unitize(); vTmp *= 9; // paddle width vPoints[5] = vPoints[1] + vTmp; vTmp = vPoints[0] - vPoints[2]; vTmp.Unitize(); vTmp *= 9; // paddle width vPoints[6] = vPoints[2] - vTmp; } // get center of the ball vBallCenter = m_ptBallLocation + BALL_RADIUS; // if any of those points are less than the radius distance // away from the center of the ball, then the ball has hit // the paddle // bHit = false; for (i = 0; i < N_CRIT_POINTS - 1; i++) { switch (i) { case 0: j = 0; k = 5; break; case 1: j = 0; k = 6; break; case 2: j = 1; k = 5; break; case 3: j = 2; k = 6; break; case 4: j = 1; k = 3; break; default: j = 2; k = 4; break; } length = distanceBetweenPoints(vPoints[j], vPoints[k]) / 2; length = sqrt(BALL_RADIUS * BALL_RADIUS + length * length) * 2; fLen1 = distanceBetweenPoints(vPoints[j], vBallCenter); fLen2 = distanceBetweenPoints(vPoints[k], vBallCenter); nRollBack = 0; if (fLen1 <= BALL_RADIUS) { nRollBack = BALL_RADIUS - (int)fLen1; bHit = true; break; } else if (fLen2 <= BALL_RADIUS) { nRollBack = (BALL_RADIUS - (int)fLen2); bHit = true; break; } else if (fLen1 + fLen2 <= length) { nRollBack = (int)(length - (fLen1 + fLen2)) + 2; bHit = true; break; } } if (bHit) { nRollBack = MIN(m_nInitBallSpeed, nRollBack); if (pGameParams->bSoundEffectsEnabled) { sndPlaySound(m_pPaddleSound, SND_MEMORY | SND_ASYNC | SND_NODEFAULT); } // if we hit the ball twice in a row // if (m_bPaddleHit) { TRACE("DoubleHit\n"); // then shoot the ball away from the paddle // m_vBallVector = vBallCenter - gvCenter; m_vBallVector.Unitize(); m_vBallVector *= 2; } else { // role the ball back to the exact point that it hit the paddle // vTmp = m_vBallVector; vTmp.Unitize(); vTmp *= nRollBack; m_ptBallLocation -= vTmp; // get center of ball vBallCenter = m_ptBallLocation + BALL_RADIUS; fLen1 = distanceBetweenPoints(vBallCenter, vPoints[0]); fLen1 += distanceBetweenPoints(vBallCenter, vPoints[1]); fMin = fLen1; fLen2 = distanceBetweenPoints(vBallCenter, vPoints[0]); fLen2 += distanceBetweenPoints(vBallCenter, vPoints[2]); fMin = MIN(fMin, fLen2); fLen3 = distanceBetweenPoints(vBallCenter, vPoints[3]); fLen3 += distanceBetweenPoints(vBallCenter, vPoints[1]); fMin = MIN(fMin, fLen3); fLen4 = distanceBetweenPoints(vBallCenter, vPoints[4]); fLen4 += distanceBetweenPoints(vBallCenter, vPoints[2]); fMin = MIN(fMin, fLen4); fLen5 = fLen1; fLen6 = fLen2; if (m_nInitPaddleSize > PSIZE_MIN) { fLen5 = distanceBetweenPoints(vBallCenter, vPoints[1]); fLen5 += distanceBetweenPoints(vBallCenter, vPoints[5]); fMin = MIN(fMin, fLen5); fLen6 = distanceBetweenPoints(vBallCenter, vPoints[2]); fLen6 += distanceBetweenPoints(vBallCenter, vPoints[6]); fMin = MIN(fMin, fLen6); } vTmp = m_vBallVector; m_vBallVector.x = -m_vBallVector.x; m_vBallVector.y = -m_vBallVector.y; vFace = vPoints[0] - gvCenter; if ((fMin == fLen1) || (fMin == fLen5) || (fMin == fLen2) || (fMin == fLen6)) { if (fMin == fLen5) { vFace = vBallCenter - gvCenter; vFace.Rotate(-Deg2Rad(10)); } else if (fMin == fLen6) { vFace = vBallCenter - gvCenter; vFace.Rotate(Deg2Rad(10)); } // get center of ball vBallCenter = m_ptBallLocation + BALL_RADIUS; // roll ball back to edge of paddle // while (distanceBetweenPoints(vBallCenter, gvCenter) < PADDLE_RADIUS + BALL_RADIUS) { m_ptBallLocation -= vTmp; // get center of ball vBallCenter = m_ptBallLocation + BALL_RADIUS; } vPaddle = vFace; } else if (fMin == fLen3) { vPaddle.SetVector(vPoints[3].x - CENTER_X, vPoints[3].y - CENTER_Y); vPaddle.Rotate(-PI / 2); a1 = vPaddle.AngleBetween(m_vBallVector); a2 = vFace.AngleBetween(m_vBallVector); // kludge to compensate for when angle is too big // if (a1 > a2) { vPaddle = vFace; } } else if (fMin == fLen4) { vPaddle.SetVector(vPoints[4].x - CENTER_X, vPoints[4].y - CENTER_Y); vPaddle.Rotate(PI / 2); a1 = vPaddle.AngleBetween(m_vBallVector); a2 = vFace.AngleBetween(m_vBallVector); // kludge to compensate for when angle is too big // if (a1 > a2) { vPaddle = vFace; } } // reflect the ball vector around the final paddle (mirror) vector // m_vBallVector.Reflect(vPaddle); // // one final check to make sure the ball is bouncing in the // correct direction. // if (m_vBallVector.AngleBetween(gvCenter - vBallCenter) <= Deg2Rad(15)) { //TRACE("RealAjusting the Vector\n"); m_vBallVector.Rotate(Deg2Rad(180)); } } m_bPaddleHit = true; } } #define MAX_BRICK_HITS 6 void CFugeWindow::BallvsBrick(double length) { char buf1[32], buf2[32]; CVector vPoints[N_BRICK_POINTS]; CVector vBrick, vBallCenter, vOrigin, vTmp; CDC *pDC; CRect rTmpRect, rBall, cRect; CPoint ptTmp; double fMin, fLast, fLen[N_BRICK_POINTS]; double angle; int i, j, nIndex, nLastIndex, nBrickIndex, nMaxHits; int nBrick0, nBrick1, nRow0, nRow1, nRow2, nUse[MAX_BRICK_HITS]; bool bHit, bStillHit; // get bounding rectangle of the ball // rBall.SetRect((int)m_ptBallLocation.x, (int)m_ptBallLocation.y, (int)m_ptBallLocation.x + BALL_SIZE_X, (int)m_ptBallLocation.y + BALL_SIZE_Y); // get center of the ball vBallCenter.SetVector(m_ptBallLocation.x + BALL_RADIUS, m_ptBallLocation.y + BALL_RADIUS); vOrigin.SetVector(-1, -1); vOrigin.Rotate(PI / BRICKS_PER_ROW); vTmp = vBallCenter - gvCenter; angle = vTmp.RealAngle(vOrigin); //TRACE("RealAngle: %f\n", angle); nMaxHits = MAX_BRICK_HITS; if (length <= ROW6_RADIUS) { nRow0 = 5; nRow1 = 5; nRow2 = 5; nMaxHits = 2; } else if (length <= ROW5_RADIUS) { nRow0 = 5; nRow1 = 4; nRow2 = 4; nMaxHits = 4; } else if (length <= ROW4_RADIUS - BALL_RADIUS) { nRow0 = 5; nRow1 = 4; nRow2 = 3; } else if (length <= ROW3_RADIUS - BALL_RADIUS) { nRow0 = 4; nRow1 = 3; nRow2 = 2; } else if (length <= ROW2_RADIUS - BALL_RADIUS) { nRow0 = 3; nRow1 = 2; nRow2 = 1; } else { nRow0 = 2; nRow1 = 1; nRow2 = 0; } if (angle >= BRICK15_ANGLE) { nBrick0 = 0; nBrick1 = 15; } else if (angle >= BRICK14_ANGLE) { nBrick0 = 15; nBrick1 = 14; } else if (angle >= BRICK13_ANGLE) { nBrick0 = 14; nBrick1 = 13; } else if (angle >= BRICK12_ANGLE) { nBrick0 = 13; nBrick1 = 12; } else if (angle >= BRICK11_ANGLE) { nBrick0 = 12; nBrick1 = 11; } else if (angle >= BRICK10_ANGLE) { nBrick0 = 11; nBrick1 = 10; } else if (angle >= BRICK9_ANGLE) { nBrick0 = 10; nBrick1 = 9; } else if (angle >= BRICK8_ANGLE) { nBrick0 = 9; nBrick1 = 8; } else if (angle >= BRICK7_ANGLE) { nBrick0 = 8; nBrick1 = 7; } else if (angle >= BRICK6_ANGLE) { nBrick0 = 7; nBrick1 = 6; } else if (angle >= BRICK5_ANGLE) { nBrick0 = 6; nBrick1 = 5; } else if (angle >= BRICK4_ANGLE) { nBrick0 = 5; nBrick1 = 4; } else if (angle >= BRICK3_ANGLE) { nBrick0 = 4; nBrick1 = 3; } else if (angle >= BRICK2_ANGLE) { nBrick0 = 3; nBrick1 = 2; } else if (angle >= BRICK1_ANGLE) { nBrick0 = 2; nBrick1 = 1; } else { nBrick0 = 1; nBrick1 = 0; } nUse[0] = (nRow0 * BRICKS_PER_ROW) + nBrick0; nUse[1] = (nRow0 * BRICKS_PER_ROW) + nBrick1; nUse[2] = (nRow1 * BRICKS_PER_ROW) + nBrick0; nUse[3] = (nRow1 * BRICKS_PER_ROW) + nBrick1; nUse[4] = (nRow2 * BRICKS_PER_ROW) + nBrick0; nUse[5] = (nRow2 * BRICKS_PER_ROW) + nBrick1; // which brick did we hit? // bHit = false; for (i = 0; i < nMaxHits; i++) { nBrickIndex = nUse[i]; assert(nBrickIndex >= 0 && nBrickIndex < N_BRICKS); if (m_bBrickVisible[nBrickIndex]) { //TRACE("Checking %d\n", nBrickIndex); // if ball's rectange intersects this brick's rectangle // cRect.SetRect(ptBrickPos[nBrickIndex].x, ptBrickPos[nBrickIndex].y, ptBrickPos[nBrickIndex].x + ptBrickSize[nBrickIndex].cx, ptBrickPos[nBrickIndex].y + ptBrickSize[nBrickIndex].cy); if (rTmpRect.IntersectRect(rBall, cRect)) { // calculate the 21 points for this brick // for (j = 0; j < N_BRICK_POINTS; j++) { vPoints[j] = vBrickCritPoints[nBrickIndex / BRICKS_PER_ROW][j]; vPoints[j].Rotate(((2 * PI) / BRICKS_PER_ROW) * (nBrickIndex % BRICKS_PER_ROW)); vPoints[j] += gvCenter; if (distanceBetweenPoints(vBallCenter, vPoints[j]) < 11.0) { bHit = true; } } if (bHit) { if (pGameParams->bSoundEffectsEnabled) { sndPlaySound(m_pBrickSound, SND_MEMORY | SND_ASYNC | SND_NODEFAULT); } if ((pDC = GetDC()) != nullptr) { m_pBall->EraseSprite(pDC); EraseBrick(pDC, nBrickIndex); m_pBall->PaintSprite(pDC, m_pBall->GetPosition()); ReleaseDC(pDC); } // on less brick --m_nBricks; // Score: 1 point for each brick regardless of color m_lScore += 1; // did user earn an extra ball? // if (m_lScore >= m_lExtraLifeScore) { if (pGameParams->bSoundEffectsEnabled) { sndPlaySound(m_pExtraLifeSound, SND_MEMORY | SND_SYNC | SND_NODEFAULT); } // double the ammount the user needs for their next extra life m_lExtraLifeScore += m_lExtraLifeScore; // extra ball m_nBalls++; } // if no bricks left // if (m_nBricks == 0) { GamePause(); // reset turbo m_fTurboBoost = 0.0; if (pGameParams->bSoundEffectsEnabled) { #if CSOUND pEffect = new CSound((CWnd *)this, WAV_WINWAVE, SOUND_WAVE | SOUND_ASYNCH | SOUND_AUTODELETE); //...Wave file, to delete itself (*pEffect).play(); //...play the narration #else sndPlaySound(WAV_WINWAVE, SND_ASYNC); #endif } // 5 point bonus for clearing all bricks m_lScore += 5; // // User wins this round // Common::sprintf_s(buf1, "Round complete."); Common::sprintf_s(buf2, "Score: %ld", m_lScore); CMessageBox dlgYouWin((CWnd *)this, m_pGamePalette, buf1, buf2); // stop all sounds // if (pGameParams->bSoundEffectsEnabled) { #if CSOUND #else sndPlaySound(nullptr, SND_ASYNC); #endif } m_nNumRows++; if (m_nNumRows > N_ROWS) { m_nNumRows = 1; m_nBallSpeed++; //if (m_nBallSpeed > SPEED_MAX) // m_nBallSpeed = SPEED_MAX; } // get new brick count m_nBricks = m_nNumRows * BRICKS_PER_ROW; // if user is playing the metagame // if (pGameParams->bPlayingMetagame) { // return to the metagame pGameParams->lScore = m_lScore; PostMessage(WM_CLOSE, 0, 0); } else { // reset the ball position // EndPaddle(); EndBall(); StartBricks(); StartBall(); StartPaddle(); GameResume(); } // // there are more bricks left, // so just calc a new vector for the ball // } else { bStillHit = true; while (bStillHit) { // roll ball back to point of contact with brick m_ptBallLocation -= m_vBallVector; // get new center of ball vBallCenter = m_ptBallLocation + BALL_RADIUS; bStillHit = false; for (j = 0; j < N_BRICK_POINTS; j++) { if ((fLen[j] = distanceBetweenPoints(vBallCenter, vPoints[j])) < 11.0) { bStillHit = true; break; } } } // find the 2 closest points to the center of the ball // nIndex = nLastIndex = -1; fMin = fLast = 9999; for (j = 0; j < N_BRICK_POINTS; j++) { if (fLen[j] < fMin) { fLast = fMin; fMin = fLen[j]; nLastIndex = nIndex; nIndex = j; } else if (fLen[j] < fLast) { fLast = fLen[j]; nLastIndex = j; } } // make sure we actually found an intersect point assert((nIndex != -1) && (nLastIndex != -1)); // if ball hit a corner, then use next best point to // determine which side the ball actually hit // switch (nIndex) { case 0: case 9: case 11: case 19: nIndex = nLastIndex; break; default: break; } // if hit back face of brick // if (nIndex >= 1 && nIndex <= 8) { vBrick = vPoints[nIndex] - gvCenter; // if hit inner face of brick // } else if (nIndex >= 12 && nIndex <= 18) { vBrick = gvCenter - vPoints[nIndex]; // hit clockwise side of brick // } else if (nIndex == 10) { vBrick = aBrickVectors[nBrickIndex % BRICKS_PER_ROW].v2; // hit counter-clockwise side of brick // } else if (nIndex == 20) { vBrick = aBrickVectors[nBrickIndex % BRICKS_PER_ROW].v1; // invalid index // } else { warning("Invalid Index (%d)", nIndex); assert(0); } // determine the vector of the brick (using ptHit as the intersect point) // m_vBallVector.x = -m_vBallVector.x; m_vBallVector.y = -m_vBallVector.y; m_vBallVector.Reflect(vBrick); } break; } } } } } void CFugeWindow::EraseBrick(CDC *pDC, int nBrickIndex) { assert(pDC != nullptr); assert((nBrickIndex >= 0) && (nBrickIndex < N_BRICKS)); // remove this brick from the screen // pDC->FloodFill(ptBrickPos[nBrickIndex].x + ptBrickSize[nBrickIndex].cx / 2, ptBrickPos[nBrickIndex].y + ptBrickSize[nBrickIndex].cy / 2, RGB(255, 255, 255)); m_bBrickVisible[nBrickIndex] = false; } void CFugeWindow::LoseBall() { char buf1[32], buf2[32]; CDC *pDC; ERROR_CODE errCode; // assume no error errCode = ERR_NONE; // pause the game GamePause(); // reset turbo m_fTurboBoost = 0.0; if ((pDC = GetDC()) != nullptr) { m_pBall->EraseSprite(pDC); m_pBall->UnlinkSprite(); assert(m_nBalls > 0); // on less ball m_nBalls--; if (pGameParams->bSoundEffectsEnabled) { #if CSOUND pEffect = new CSound((CWnd *)this, WAV_LOSEBALL, SOUND_WAVE | SOUND_ASYNCH | SOUND_AUTODELETE); //...Wave file, to delete itself (*pEffect).play(); //...play the narration #else sndPlaySound(WAV_LOSEBALL, SND_ASYNC); #endif } /* BLACK HOLE ANIMATION REMOVED DUE TO LACK OF ARTWORK CSprite *pSprite; int i; // start the black hole animation // if ((pSprite = new CSprite) != nullptr) { if (pSprite->SharePalette(m_pGamePalette) != false) { if (pSprite->LoadCels(pDC, ".\\ART\\LOSEBALL.BMP", N_BLACKHOLE_CELS) != false) { pSprite->SetMasked(true); pSprite->SetMobile(true); pSprite->LinkSprite(); for (i = 0; i < N_BLACKHOLE_CELS * 2; i++) { pSprite->PaintSprite(pDC, 296, 216); Sleep(300); } pSprite->EraseSprite(pDC); pSprite->UnlinkSprite(); delete pSprite; } else { errCode = ERR_UNKNOWN; } } else { errCode = ERR_UNKNOWN; } } else { errCode = ERR_MEMORY; } */ // if no more balls left - user has lost // if (m_nBalls == 0) { if (pGameParams->bSoundEffectsEnabled) { #if CSOUND pEffect = new CSound((CWnd *)this, WAV_GAMEOVER, SOUND_WAVE | SOUND_ASYNCH | SOUND_AUTODELETE); //...Wave file, to delete itself (*pEffect).play(); //...play the narration #else sndPlaySound(WAV_GAMEOVER, SND_ASYNC); #endif } Common::sprintf_s(buf1, "Score: %ld", m_lScore); CMessageBox dlgLoseBall((CWnd *)this, m_pGamePalette, "Game over.", buf1); if (pGameParams->bPlayingMetagame) { // return the final score // pGameParams->lScore = m_lScore; PostMessage(WM_CLOSE, 0, 0); } GameReset(); } else { // display score, and start a new ball // Common::sprintf_s(buf1, "Score: %ld", m_lScore); Common::sprintf_s(buf2, "Balls Left: %d", m_nBalls); CMessageBox dlgLoseBall((CWnd *)this, m_pGamePalette, buf1, buf2); // // reset the ball position // EndPaddle(); StartPaddle(); StartBall(); } ReleaseDC(pDC); } else { errCode = ERR_MEMORY; } HandleError(errCode); } void CFugeWindow::StartPaddle() { assert(m_pPaddle != nullptr); if (m_pPaddle != nullptr) m_pPaddle->LinkSprite(); m_bBallOnPaddle = true; PaintPaddle(true); } void CFugeWindow::EndPaddle() { CDC *pDC; assert(m_pPaddle != nullptr); if ((m_pPaddle != nullptr) && (m_pPaddle->IsLinked())) { if ((pDC = GetDC()) != nullptr) { m_pPaddle->EraseSprite(pDC); m_pPaddle->UnlinkSprite(); ReleaseDC(pDC); } } } void CFugeWindow::LaunchBall() { assert(m_bGameActive); assert(m_bBallOnPaddle); m_bPause = false; m_bBallOnPaddle = false; // starting ball vector is determined by the location of the paddle m_vBallVector = gvCenter - (m_ptBallLocation + BALL_RADIUS); // add a slight randomness to the balls vector // m_vBallVector.Rotate(Deg2Rad((brand() % 2) * ((brand() & 1) ? -1 : 1))); m_vBallVector.Unitize(); PaintBall(); } void CFugeWindow::PaintPaddle(bool bPaint) { CVector vPaddle; CDC *pDC; int nOldIndex; bool bSuccess; // verify that the input was not tainted assert(m_nPaddleCelIndex < N_PADDLE_CELS * 2); assert(m_nPaddleCelIndex > -N_PADDLE_CELS); // can't access a null pointer assert(m_pPaddle != nullptr); // get old cel index nOldIndex = m_pPaddle->GetCelIndex(); if (m_nPaddleCelIndex >= N_PADDLE_CELS) { m_nPaddleCelIndex = m_nPaddleCelIndex - N_PADDLE_CELS; } else if (m_nPaddleCelIndex < 0) { m_nPaddleCelIndex = m_nPaddleCelIndex + N_PADDLE_CELS; } // verify our calculations assert((m_nPaddleCelIndex >= 0) && (m_nPaddleCelIndex < N_PADDLE_CELS)); // don't re-paint the paddle if we would paint the same cel // if (bPaint || (nOldIndex != m_nPaddleCelIndex)) { m_pPaddle->SetCel(m_nPaddleCelIndex - 1); // paint paddle to new location // if ((pDC = GetDC()) != nullptr) { bSuccess = m_pPaddle->PaintSprite(pDC, PADDLE_START_X, PADDLE_START_Y); assert(bSuccess); // if the ball is resting on the paddle // if (m_bBallOnPaddle) { assert(m_pBall != nullptr); // ball is rotating with paddle // m_ptBallLocation = BallOnPaddle(); // paint the ball to it's new location // m_pBall->PaintSprite(pDC, (int)m_ptBallLocation.x, (int)m_ptBallLocation.y); } else { #if 0 // if the paddle's rectangle hit the ball's rectangle // if (m_pPaddle->InterceptOccurred()) { // did the paddle actually hit the ball? // if (m_pPaddle->TestInterception(pDC, m_pBall)) { double angle; TRACE("Paddle Hit Ball\n"); // // handle collision with ball // vPaddle.SetVector(-13, 31); // cel index determines paddle angle // angle = (2 * PI / N_PADDLE_CELS) * m_nPaddleCelIndex; // if paddle is moving clockwise then rotate -90 instead of 90 degrees // angle += ((nOldIndex < m_nPaddleCelIndex) ? (PI / 2) : (-PI / 2)); vPaddle.Rotate(angle); vPaddle.Unitize(); m_vBallVector.Unitize(); // handle spin // m_vBallVector += vPaddle * 2; } } #endif } ReleaseDC(pDC); } } } void CFugeWindow::StartBricks() { CDC *pDC; int i, nBricks; nBricks = m_nNumRows * BRICKS_PER_ROW; for (i = 0; i < nBricks; i++) { if (!m_bBrickVisible[i]) { m_bBrickVisible[i] = true; } } if ((pDC = GetDC()) != nullptr) { PaintBricks(pDC); ReleaseDC(pDC); } } void CFugeWindow::PaintBricks(CDC *pDC) { CSize size; CDC *pMemDC; CBitmap *pBmp, *pMemBmp; CPalette *pPalOld, *pScreenPalOld; HBITMAP hOldBitmap; int i; assert(pDC != nullptr); assert((m_nNumRows >= 0) && (m_nNumRows <= N_ROWS)); if (FileExists(pszFugeArt[m_nNumRows])) { pBmp = FetchBitmap(pDC, nullptr, pszFugeArt[m_nNumRows]); pMemDC = nullptr; pMemBmp = nullptr; if ((pMemDC = new CDC) != nullptr) { if ((pMemBmp = new CBitmap) != nullptr) { size = GetBitmapSize(pBmp); if ((m_nNumRows != 0) && (pMemBmp->CreateCompatibleBitmap(pDC, size.cx, size.cy) != false)) { pMemDC->CreateCompatibleDC(nullptr); pPalOld = pMemDC->SelectPalette(m_pGamePalette, false); pMemDC->RealizePalette(); hOldBitmap = SelectBitmap(pMemDC->m_hDC, pMemBmp->m_hObject); PaintBitmap(pMemDC, m_pGamePalette, pBmp); // repaint all the visible bricks // for (i = 0; i < N_BRICKS; i++) { if (!m_bBrickVisible[i]) { EraseBrick(pMemDC, i); } } pScreenPalOld = pDC->SelectPalette(m_pGamePalette, false); pDC->BitBlt(0, 0, size.cx, size.cy, pMemDC, 0, 0, SRCCOPY); pDC->SelectPalette(pScreenPalOld, false); SelectBitmap(pMemDC->m_hDC, hOldBitmap); pMemDC->SelectPalette(pPalOld, false); // fall back to yucky method // } else { PaintBitmap(pDC, m_pGamePalette, pBmp); if (m_nNumRows != 0) { // repaint all the visible bricks // for (i = 0; i < N_BRICKS; i++) { if (!m_bBrickVisible[i]) { EraseBrick(pDC, i); } } } } } } if (pMemBmp != nullptr) { delete pMemBmp; pMemBmp = nullptr; } if (pMemDC != nullptr) { delete pMemDC; pMemDC = nullptr; } if (pBmp != nullptr) { delete pBmp; pBmp = nullptr; } } if (m_pScrollButton != nullptr) { m_pScrollButton->Invalidate(false); m_pScrollButton->UpdateWindow(); } } void CFugeWindow::EndBricks() { memset(m_bBrickVisible, 0, sizeof(bool) * N_BRICKS); } void CFugeWindow::LoadIniSettings() { if (pGameParams->bPlayingMetagame) { m_bOutterWall = false; m_nInitNumBalls = 1; m_nInitStartLevel = 3; m_nGForceFactor = GFORCE_DEF; m_nInitPaddleSize = PSIZE_MAX; switch (pGameParams->nSkillLevel) { case SKILLLEVEL_LOW: m_nInitBallSpeed = 4; break; case SKILLLEVEL_MEDIUM: m_nInitBallSpeed = 6; break; default: assert(pGameParams->nSkillLevel == SKILLLEVEL_HIGH); m_nInitBallSpeed = 8; break; } } else { m_nInitNumBalls = GetPrivateProfileInt(INI_SECTION, "NumberOfBalls", BALLS_DEF, INI_FILENAME); if ((m_nInitNumBalls < BALLS_MIN) || (m_nInitNumBalls > BALLS_MAX)) m_nInitNumBalls = BALLS_DEF; m_nInitStartLevel = GetPrivateProfileInt(INI_SECTION, "StartingLevel", LEVEL_DEF, INI_FILENAME); if ((m_nInitStartLevel < LEVEL_MIN) || (m_nInitStartLevel > LEVEL_MAX)) m_nInitStartLevel = LEVEL_DEF; m_nInitBallSpeed = GetPrivateProfileInt(INI_SECTION, "BallSpeed", SPEED_DEF, INI_FILENAME); if ((m_nInitBallSpeed < SPEED_MIN) || (m_nInitBallSpeed > SPEED_MAX)) m_nInitBallSpeed = SPEED_DEF; m_nInitPaddleSize = GetPrivateProfileInt(INI_SECTION, "PaddleSize", PSIZE_DEF, INI_FILENAME); if ((m_nInitPaddleSize < PSIZE_MIN) || (m_nInitPaddleSize > PSIZE_MAX)) m_nInitPaddleSize = PSIZE_DEF; int outerWall = GetPrivateProfileInt(INI_SECTION, "OutterWall", 0, INI_FILENAME); m_bOutterWall = outerWall != 0; m_nGForceFactor = GetPrivateProfileInt(INI_SECTION, "Gravity", GFORCE_DEF, INI_FILENAME); if ((m_nGForceFactor < GFORCE_MIN) || (m_nGForceFactor > GFORCE_MAX)) m_nGForceFactor = GFORCE_DEF; } } void CFugeWindow::GameReset() { KillTimer(TIMER_ID); // stop the timer if (pGameParams->bSoundEffectsEnabled) { #if CSOUND #else sndPlaySound(nullptr, SND_ASYNC); // stop all sounds #endif } EndBricks(); // remove all bricks from sprite chain EndBall(); // remove ball from sprite chain EndPaddle(); // remove paddle from sprite chain m_fTurboBoost = 0.0; // no turbo m_lExtraLifeScore = EXTRA_LIFE_SCORE; // user needs this many points for an extra life m_bGameActive = false; // there is no currently active game m_bPause = false; // the game is not paused m_bBallOnPaddle = false; // Ball is not yet on paddle m_bMovingPaddle = false; // user is not moving the paddle m_nBalls = m_nInitNumBalls; // reset # of balls m_nBallSpeed = m_nInitBallSpeed; // reset ball speed m_nNumRows = m_nInitStartLevel; // reset number of brick rows m_nBricks = m_nNumRows * BRICKS_PER_ROW; // get new brick count m_lScore = 0; // reset the score m_bPaddleHit = false; // paddle starts fresh } void CFugeWindow::OnTimer(uintptr nEvent) { // there should be only one timer assert(nEvent == TIMER_ID); // // continue as long as there is a currently active non-paused game // if (m_bGameActive) { // check for joystick movement // if (m_bJoyActive) { JOYINFO joyInfo; joyGetPos(JOYSTICKID1, &joyInfo); OnJoyStick(joyInfo.wButtons, ((long)joyInfo.wYpos << 16) | joyInfo.wXpos); } if (!m_bPause) { PaintBall(); PaintBall(); } } } long CFugeWindow::OnJoyStick(unsigned int wParam, long lParam) { long nThresholdX, nThresholdY; if (m_bGameActive) { if (wParam & JOY_BUTTON1) { if (m_bBallOnPaddle) { LaunchBall(); } } nThresholdX = m_nJoyOrgX; nThresholdX -= (unsigned int)LOWORD(lParam); nThresholdY = m_nJoyOrgY; nThresholdY -= (unsigned int)HIWORD(lParam); if (nThresholdY > 5000) { // up and left if (nThresholdX > 5000) { m_nPaddleCelIndex = 25; // up and right } else if (nThresholdX < -5000) { m_nPaddleCelIndex = 2; // just up } else { m_nPaddleCelIndex = 29; } } else if (nThresholdY < -5000) { // down and left if (nThresholdX > 5000) { m_nPaddleCelIndex = 17; // down and right } else if (nThresholdX < -5000) { m_nPaddleCelIndex = 9; // just down } else { m_nPaddleCelIndex = 13; } } else { // left if (nThresholdX > 5000) { m_nPaddleCelIndex = 21; // right } else if (nThresholdX < -5000) { m_nPaddleCelIndex = 5; } } PaintPaddle(false); } return 0; } void CFugeWindow::OnMouseMove(unsigned int, CPoint point) { int nMove; if (m_bGameActive && m_bMovingPaddle) { // Need to implement the mouse better for paddle movement // GetCursorPos(&point); if (point.x > m_ptOrigin.x) { nMove = (point.x - m_ptOrigin.x) / MOUSE_SENS + 1; m_nPaddleCelIndex += nMove; PaintPaddle(false); SetCursorPos(m_ptOrigin.x, m_ptOrigin.y); } else if (point.x < m_ptOrigin.x) { nMove = -((m_ptOrigin.x - point.x) / MOUSE_SENS + 1); m_nPaddleCelIndex += nMove; PaintPaddle(false); SetCursorPos(m_ptOrigin.x, m_ptOrigin.y); } // hide the cursor SetCursor(nullptr); } else { SetCursor(LoadCursor(nullptr, IDC_ARROW)); } } void CFugeWindow::OnRButtonUp(unsigned int nFlags, CPoint point) { if (m_bGameActive) { // toggle move paddle mode m_bMovingPaddle = !m_bMovingPaddle; if (m_bMovingPaddle) { SetCursor(nullptr); SetCursorPos(m_ptOrigin.x, m_ptOrigin.y); GameResume(); } else { SetCursor(LoadCursor(nullptr, IDC_ARROW)); GamePause(); } } else { CFrameWnd::OnRButtonDown(nFlags, point); } } void CFugeWindow::OnLButtonDown(unsigned int nFlags, CPoint point) { CRect boothRect, tentRect, peopRect, car1Rect, car2Rect, car3Rect, car4Rect, car5Rect, car6Rect, car7Rect, car8Rect, car9Rect, car10Rect; int nPick = 0; boothRect.SetRect(BOOTH_X, BOOTH_Y, BOOTH_X + BOOTH_DX, BOOTH_Y + BOOTH_DY); tentRect.SetRect(TENT_X, TENT_Y, TENT_X + TENT_DX, TENT_Y + TENT_DY); peopRect.SetRect(PEOPLE_X, PEOPLE_Y, PEOPLE_X + PEOPLE_DX, PEOPLE_Y + PEOPLE_DY); car1Rect.SetRect(CAR1_X, CAR1_Y, CAR1_X + CAR_DX, CAR1_Y + CAR_DY); car2Rect.SetRect(CAR2_X, CAR2_Y, CAR2_X + CAR_DX, CAR2_Y + CAR_DY); car3Rect.SetRect(CAR3_X, CAR3_Y, CAR3_X + CAR_DX, CAR3_Y + CAR_DY); car4Rect.SetRect(CAR4_X, CAR4_Y, CAR4_X + CAR_DX, CAR4_Y + CAR_DY); car5Rect.SetRect(CAR5_X, CAR5_Y, CAR5_X + CAR_DX, CAR5_Y + CAR_DY); car6Rect.SetRect(CAR6_X, CAR6_Y, CAR6_X + CAR_DX, CAR6_Y + CAR_DY); car7Rect.SetRect(CAR7_X, CAR7_Y, CAR7_X + CAR_DX, CAR7_Y + CAR_DY); car8Rect.SetRect(CAR8_X, CAR8_Y, CAR8_X + CAR_DX, CAR8_Y + CAR_DY); car9Rect.SetRect(CAR9_X, CAR9_Y, CAR9_X + CAR9_DX, CAR9_Y + CAR9_DY); car10Rect.SetRect(CAR10_X, CAR10_Y, CAR10_X + CAR10_DX, CAR10_Y + CAR10_DY); // User clicked on the Title - NewGame button // if (m_rNewGameButton.PtInRect(point)) { // if we are not playing from the metagame // if (!pGameParams->bPlayingMetagame) { // start a new game PlayGame(); } } else if (boothRect.PtInRect(point)) { if (pGameParams->bSoundEffectsEnabled) { #if CSOUND pEffect = new CSound((CWnd *)this, WAV_BOOTH, SOUND_WAVE | SOUND_ASYNCH | SOUND_AUTODELETE | SOUND_QUEUE); //...Wave file, to delete itself (*pEffect).play(); //...play the narration #else sndPlaySound(WAV_BOOTH, SND_ASYNC); #endif } } else if (tentRect.PtInRect(point)) { if (pGameParams->bSoundEffectsEnabled) { #if CSOUND pEffect = new CSound((CWnd *)this, WAV_TENT, SOUND_WAVE | SOUND_ASYNCH | SOUND_AUTODELETE | SOUND_QUEUE); //...Wave file, to delete itself (*pEffect).play(); //...play the narration #else sndPlaySound(WAV_TENT, SND_ASYNC); #endif } } else if (peopRect.PtInRect(point)) { if (pGameParams->bSoundEffectsEnabled) { nPick = brand() % NUM_WAVS; if (nPick == 0) { #if CSOUND pEffect = new CSound((CWnd *)this, WAV_PEOPLE1, SOUND_WAVE | SOUND_ASYNCH | SOUND_AUTODELETE | SOUND_QUEUE); //...Wave file, to delete itself (*pEffect).play(); //...play the narration #else sndPlaySound(WAV_PEOPLE1, SND_ASYNC); #endif } else { #if CSOUND pEffect = new CSound((CWnd *)this, WAV_PEOPLE2, SOUND_WAVE | SOUND_ASYNCH | SOUND_AUTODELETE | SOUND_QUEUE); //...Wave file, to delete itself (*pEffect).play(); //...play the narration #else sndPlaySound(WAV_PEOPLE2, SND_ASYNC); #endif } } } else if (((((((((car1Rect.PtInRect(point) || car2Rect.PtInRect(point)) || car3Rect.PtInRect(point)) || car4Rect.PtInRect(point)) || car5Rect.PtInRect(point)) || car6Rect.PtInRect(point)) || car7Rect.PtInRect(point)) || car8Rect.PtInRect(point)) || car9Rect.PtInRect(point)) || car10Rect.PtInRect(point)) { if (pGameParams->bSoundEffectsEnabled) { nPick = brand() % NUM_WAVS; #if CSOUND // Wave file, to delete itself play the narration // pEffect = new CSound((CWnd *)this, ((nPick == 0) ? WAV_CAR1 : WAV_CAR2), SOUND_WAVE | SOUND_ASYNCH | SOUND_AUTODELETE | SOUND_QUEUE); pEffect->play(); #else sndPlaySound(((nPick == 0) ? WAV_CAR1 : WAV_CAR2), SND_ASYNC); #endif } } else { if (m_bGameActive) { if (m_bBallOnPaddle) { LaunchBall(); } } else { // is this needed ? CFrameWnd::OnLButtonDown(nFlags, point); } } } void CFugeWindow::OnSysChar(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags) { // terminate app on ALT_Q // if ((nChar == 'q') && (nFlags & 0x2000)) { PostMessage(WM_CLOSE, 0, 0); } else { // default action CFrameWnd ::OnSysChar(nChar, nRepCnt, nFlags); } } void CFugeWindow::OnSysKeyDown(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags) { switch (nChar) { // User has hit ALT_F4 so close down this App // case VK_F4: PostMessage(WM_CLOSE, 0, 0); break; default: CFrameWnd::OnSysKeyDown(nChar, nRepCnt, nFlags); break; } } void CFugeWindow::OnKeyDown(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags) { // Handle keyboard input // switch (nChar) { // Move paddle clockwise // case VK_UP: case VK_RIGHT: if (m_bGameActive) { m_nPaddleCelIndex += PADDLE_CEL_JUMP; PaintPaddle(false); } break; // Move paddle counter-clockwise // case VK_DOWN: case VK_LEFT: if (m_bGameActive) { m_nPaddleCelIndex -= PADDLE_CEL_JUMP; PaintPaddle(false); } break; case VK_RETURN: case VK_SPACE: if (m_bGameActive) { if (m_bBallOnPaddle) { LaunchBall(); } } break; // // Bring up the Rules // case VK_F1: { GamePause(); CSound::waitWaveSounds(); CRules RulesDlg(this, "fuge.txt", m_pGamePalette, (pGameParams->bSoundEffectsEnabled ? WAV_NARRATION : nullptr)); RulesDlg.DoModal(); GameResume(); } break; // // Bring up the options menu // case VK_F2: SendMessage(WM_COMMAND, IDC_MENU, BN_CLICKED); break; case VK_SCROLL: if (m_bGameActive) { if (!m_bPause) { GamePause(); } else { GameResume(); } } break; default: CFrameWnd::OnKeyDown(nChar, nRepCnt, nFlags); break; } } void CFugeWindow::OnActivate(unsigned int nState, CWnd *, bool bMinimized) { if (!bMinimized) { switch (nState) { case WA_ACTIVE: case WA_CLICKACTIVE: InvalidateRect(nullptr, false); break; default: break; } } } LRESULT CFugeWindow::OnMCINotify(WPARAM wParam, LPARAM lParam) { CSound *pSound; pSound = CSound::OnMCIStopped(wParam, lParam); if (pSound != nullptr) OnSoundNotify(pSound); return 0; } LRESULT CFugeWindow::OnMMIONotify(WPARAM wParam, LPARAM lParam) { CSound *pSound; pSound = CSound::OnMMIOStopped(wParam, lParam); if (pSound != nullptr) OnSoundNotify(pSound); return 0; } void CFugeWindow::OnSoundNotify(CSound *) { // // 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. // } void CFugeWindow::OnClose() { CBrush myBrush; CRect myRect; CDC *pDC; ClipCursor(nullptr); // perform cleanup // GameReset(); ReleaseMasterSounds(); ReleaseMasterSprites(); // release all master sprites // Stop the sountrack // if (m_pSoundTrack != nullptr) { // we should have been playing music assert(pGameParams->bMusicEnabled); delete m_pSoundTrack; m_pSoundTrack = nullptr; } #if CSOUND CSound::clearSounds(); #endif // // de-allocate the main menu scroll button // assert(m_pScrollButton != nullptr); if (m_pScrollButton != nullptr) { delete m_pScrollButton; m_pScrollButton = nullptr; } // // need to de-allocate the game palette // assert(m_pGamePalette != nullptr); if (m_pGamePalette != nullptr) { m_pGamePalette->DeleteObject(); delete m_pGamePalette; m_pGamePalette = nullptr; } if ((pDC = GetDC()) != nullptr) { // paint black myRect.SetRect(0, 0, GAME_WIDTH, GAME_HEIGHT); myBrush.CreateStockObject(BLACK_BRUSH); pDC->FillRect(&myRect, &myBrush); ReleaseDC(pDC); } CFrameWnd::OnClose(); MFC::PostMessage(ghParentWnd, WM_PARENTNOTIFY, WM_DESTROY, 0L); } // // CFugeWindow message map: // Associate messages with member functions. // BEGIN_MESSAGE_MAP(CFugeWindow, CFrameWnd) ON_WM_PAINT() ON_WM_CLOSE() ON_WM_TIMER() ON_WM_MOUSEMOVE() ON_WM_SYSCHAR() ON_WM_KEYDOWN() ON_WM_SYSKEYDOWN() ON_WM_RBUTTONUP() ON_WM_LBUTTONDOWN() ON_WM_ACTIVATE() ON_MESSAGE(MM_MCINOTIFY, CFugeWindow::OnMCINotify) ON_MESSAGE(MM_WOM_DONE, CFugeWindow::OnMMIONotify) END_MESSAGE_MAP() void CALLBACK GetGameParams(CWnd *pParentWnd) { // // Our user preference dialog box is self contained in this object // CUserCfgDlg dlgUserCfg(pParentWnd, pGamePalette, IDD_USERCFG); } } // namespace Fuge } // namespace HodjNPodj } // namespace Bagel