/* 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/hodjnpodj/hnplibs/stdafx.h" #include "bagel/boflib/sound.h" #include "bagel/hodjnpodj/hnplibs/sprite.h" #include "bagel/hodjnpodj/hnplibs/button.h" #include "bagel/hodjnpodj/hnplibs/mainmenu.h" #include "bagel/hodjnpodj/hnplibs/cmessbox.h" #include "bagel/hodjnpodj/hnplibs/rules.h" #include "bagel/hodjnpodj/hnplibs/gamedll.h" #include "bagel/hodjnpodj/hnplibs/text.h" #include "bagel/hodjnpodj/mazedoom/resource.h" #include "bagel/hodjnpodj/mazedoom/globals.h" #include "bagel/hodjnpodj/mazedoom/mod.h" #include "bagel/hodjnpodj/mazedoom/mazegen.h" #include "bagel/hodjnpodj/mazedoom/optndlg.h" #include "bagel/hodjnpodj/hodjnpodj.h" namespace Bagel { namespace HodjNPodj { namespace MazeDoom { // // bitmap locations in modparts.bmp // #define WALL_X 0 #define WALL_Y 22 #define PATH_WIDTH 24 #define PATH_HEIGHT 24 #define PATH_X 48 #define START_X 24 #define EDGE_Y 46 #define EDGE_WIDTH 5 #define EDGE_HEIGHT 24 #define TRAP_WIDTH 22 #define TRAP_HEIGHT 22 void CALLBACK GetSubOptions(CWnd* pParentWind); void initialize_maze(); // draw the surrounding wall and start/end squares void create_maze(); // create a maze layout given the intiialized maze int choose_door(); // pick a new path int backup(); // back up a move void SetUpMaze(); void SetInvisibleWalls(); void SetTraps(); void AddEdges(CDC *pDC, int x, int y, int offset_x, int offset_y); void PaintMaze(CDC *pDC); CPoint GetRandomPoint(bool bRight); CPoint ScreenToTile(CPoint pointScreen); bool InArtRegion(CPoint point); CBmpButton *m_pScrollButton; CSprite *_playerSprite = nullptr; CPalette *pGamePalette = nullptr, // Palette of current artwork *pOldPal = nullptr; CBitmap *pMazeBitmap = nullptr, *pOldBmp = nullptr, *_wallBitmap = nullptr, *_pathBitmap = nullptr, *_startBitmap = nullptr, *_topEdgeBmp = nullptr, *_rightEdgeBmp = nullptr, *_bottomEdgeBmp = nullptr, *_leftEdgeBmp = nullptr, *_trapBitmap[NUM_TRAP_MAPS]; CDC *pMazeDC = nullptr; // DC for the MazeBitmap CText *m_pTimeText = nullptr; // Time to be posted in Locale box of screen CBitmap *_localeBitmap = nullptr, // Locale of game bitmap for title bar *_blankBitmap = nullptr; // Blank area of locale for time display bool _success; bool m_bIgnoreScrollClick; bool _playing; bool _gameOver = false; POINT _playerPos; unsigned int m_nPlayerID = PODJ; // Hodj = 0, Podj = 4 to Offset the Bitmap ID for player int _difficulty; int _time, _seconds, _minutes; int tempDifficulty; int tempTime; struct TILE { // Data type for each square of the underlying Grid of the Maze POINT m_nStart; // Upper-left-hand corner where the bmp is to be drawn (24 X 24) unsigned int m_nWall; // 0 = Path, 1 = Wall, 2 = Trap, etc... unsigned int m_nTrap; // Index of trap bitmap to use for drawing POINT m_nDest; // x,y Tile location of Trap exit point bool m_bHidden; // 0 = Visible, 1 = Invisible } _mazeTile[NUM_COLUMNS][NUM_ROWS]; static CSound *_gameSound = nullptr; // Game theme song 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() { CString WndClass; CRect MainRect, tmpRect; CBitmap *pPartsBitmap = nullptr; CDC *pDC = nullptr; int i; // Counter for trap assignment BeginWaitCursor(); m_pScrollButton = nullptr; _playerSprite = nullptr; pGamePalette = nullptr, // Palette of current artwork pOldPal = nullptr; pMazeBitmap = nullptr; pOldBmp = nullptr; _wallBitmap = nullptr; _pathBitmap = nullptr; _startBitmap = nullptr; _topEdgeBmp = nullptr; _rightEdgeBmp = nullptr; _bottomEdgeBmp = nullptr; _leftEdgeBmp = nullptr; pMazeDC = nullptr; m_pTimeText = nullptr; _localeBitmap = nullptr; _blankBitmap = nullptr; _success = false; m_bIgnoreScrollClick = false; _playing = false; _gameOver = false; _playerPos.x = _playerPos.y = 0; m_nPlayerID = PODJ; _difficulty = 0; _time = _seconds = _minutes = 0; tempDifficulty = 0; tempTime = 0; Common::fill(_trapBitmap, _trapBitmap + NUM_TRAP_MAPS, nullptr); if (pGameInfo->bPlayingHodj) m_nPlayerID = HODJ; else m_nPlayerID = PODJ; // Define a special window class which traps double-clicks, is byte aligned // to maximize BITBLT performance, and creates "owned" DCs rather than sharing // the five system defined DCs which are not guaranteed to be available; // this adds a bit to our app size but avoids hangs/freezes/lockups. WndClass = AfxRegisterWndClass(CS_BYTEALIGNWINDOW | CS_OWNDC, nullptr, nullptr, nullptr); // Center our window on the screen pDC = GetDC(); MainRect.left = (pDC->GetDeviceCaps(HORZRES) - GAME_WIDTH) >> 1; MainRect.top = (pDC->GetDeviceCaps(VERTRES) - GAME_HEIGHT) >> 1; MainRect.right = MainRect.left + GAME_WIDTH; MainRect.bottom = MainRect.top + GAME_HEIGHT; // Create the window as a POPUP so no boarders, title, or menu are present; // this is because the game's background art will fill the entire 640x480 area. Create(WndClass, "Boffo Games -- Maze o' Doom", WS_POPUP, MainRect, nullptr, 0); CDibDoc *pSourceDoc; // Get the game palette pSourceDoc = new CDibDoc(); ASSERT(pSourceDoc != nullptr); (*pSourceDoc).OpenDocument(MAINSCREEN); pGamePalette = (*pSourceDoc).DetachPalette(); // Acquire the shared palette for our game from the art delete pSourceDoc; pDC->SelectPalette(pGamePalette, false); // select the game palette pDC->RealizePalette(); //...and realize it ShowWindow(SW_SHOWNORMAL); SplashScreen(); // Build Scroll Command button m_pScrollButton = new CBmpButton; ASSERT(m_pScrollButton != nullptr); tmpRect.SetRect(SCROLL_BUTTON_X, SCROLL_BUTTON_Y, SCROLL_BUTTON_X + SCROLL_BUTTON_DX - 1, SCROLL_BUTTON_Y + SCROLL_BUTTON_DY - 1); _success = (*m_pScrollButton).Create(nullptr, BS_OWNERDRAW | WS_CHILD | WS_VISIBLE, tmpRect, this, IDC_SCROLL); ASSERT(_success); _success = (*m_pScrollButton).LoadBitmaps(SCROLLUP, SCROLLDOWN, 0, 0); ASSERT(_success); m_bIgnoreScrollClick = false; pMazeBitmap = new CBitmap(); pMazeDC = new CDC(); pMazeBitmap->CreateCompatibleBitmap(pDC, NUM_COLUMNS * SQ_SIZE_X, NUM_ROWS * SQ_SIZE_Y); // Set up MazeBitmap pMazeDC->CreateCompatibleDC(pDC); //...and DC pOldBmp = pMazeDC->SelectObject(pMazeBitmap); // select the bitmap in pOldPal = pMazeDC->SelectPalette(pGamePalette, false); // select the game palette pMazeDC->RealizePalette(); //...and realize it // // Load up the various bitmaps for wall, edge, booby traps, etc. // pPartsBitmap = FetchResourceBitmap(pDC, nullptr, IDB_PARTS); _wallBitmap = ExtractBitmap(pDC, pPartsBitmap, pGamePalette, WALL_X, WALL_Y, PATH_WIDTH, PATH_HEIGHT); _pathBitmap = ExtractBitmap(pDC, pPartsBitmap, pGamePalette, PATH_X, WALL_Y, PATH_WIDTH, PATH_HEIGHT); _startBitmap = ExtractBitmap(pDC, pPartsBitmap, pGamePalette, START_X, WALL_Y, PATH_WIDTH, PATH_HEIGHT); _leftEdgeBmp = ExtractBitmap(pDC, pPartsBitmap, pGamePalette, 0, EDGE_Y, EDGE_WIDTH, EDGE_HEIGHT); _rightEdgeBmp = ExtractBitmap(pDC, pPartsBitmap, pGamePalette, EDGE_WIDTH, EDGE_Y, EDGE_WIDTH, EDGE_HEIGHT); _topEdgeBmp = ExtractBitmap(pDC, pPartsBitmap, pGamePalette, EDGE_WIDTH * 2, EDGE_Y, EDGE_HEIGHT, EDGE_WIDTH); _bottomEdgeBmp = ExtractBitmap(pDC, pPartsBitmap, pGamePalette, (EDGE_WIDTH * 2) + EDGE_HEIGHT, EDGE_Y, EDGE_HEIGHT, EDGE_WIDTH); for (i = 0; i < NUM_TRAP_MAPS; i++) { _trapBitmap[i] = ExtractBitmap(pDC, pPartsBitmap, pGamePalette, TRAP_WIDTH * i, 0, TRAP_WIDTH, TRAP_HEIGHT); } if (pPartsBitmap != nullptr) { pPartsBitmap->DeleteObject(); delete pPartsBitmap; } _playerSprite = new CSprite; (*_playerSprite).SharePalette(pGamePalette); _success = (*_playerSprite).LoadResourceCels(pDC, IDB_HODJ_LEFT + m_nPlayerID, NUM_CELS); ASSERT(_success); (*_playerSprite).SetMasked(true); (*_playerSprite).SetMobile(true); _localeBitmap = FetchResourceBitmap(pDC, nullptr, "IDB_LOCALE_BMP"); ASSERT(_localeBitmap != nullptr); _blankBitmap = FetchResourceBitmap(pDC, nullptr, "IDB_BLANK_BMP"); ASSERT(_blankBitmap != nullptr); tmpRect.SetRect(TIME_LOCATION_X, TIME_LOCATION_Y, TIME_LOCATION_X + TIME_WIDTH, TIME_LOCATION_Y + TIME_HEIGHT); if ((m_pTimeText = new CText()) != nullptr) { (*m_pTimeText).SetupText(pDC, pGamePalette, &tmpRect, JUSTIFY_CENTER); } ReleaseDC(pDC); //srand((unsigned) time( nullptr )); // seed the random number generator if (pGameInfo->bPlayingMetagame) { if (pGameInfo->nSkillLevel == SKILLLEVEL_LOW) { _difficulty = MIN_DIFFICULTY; // Total Wussy _time = 60; } else if (pGameInfo->nSkillLevel == SKILLLEVEL_MEDIUM) { _difficulty = 2; // Big Sissy _time = 60; } else { //if (pGameInfo->nSkillLevel == SKILLLEVEL_HIGH) _difficulty = 4; // Minor Whimp _time = 60; } } // end if else { // Use Defaults _difficulty = 6; // Miner _time = 180; } // end else tempDifficulty = _difficulty; tempTime = _time; _seconds = _time % 60; _minutes = _time / 60; initialize_maze(); // draw the surrounding wall and start/end squares create_maze(); // create a maze layout given the intiialized maze SetUpMaze(); // "translate" from the created maze into uniform grid of doom PaintMaze(pMazeDC); // draw it in the MazeBitmap _playing = true; SetTimer(GAME_TIMER, CLICK_TIME, nullptr); // Reset ticker if (pGameInfo->bMusicEnabled) { _gameSound = new CSound(this, GAME_THEME, SOUND_MIDI | SOUND_LOOP | SOUND_DONT_LOOP_TO_END); if (_gameSound != nullptr) { (*_gameSound).midiLoopPlaySegment(3000, 32980, 0, FMT_MILLISEC); } // end if pGameSound } EndWaitCursor(); if (!pGameInfo->bPlayingMetagame) PostMessage(WM_COMMAND, IDC_SCROLL, BN_CLICKED); // Activate the Options dialog } //End of CMainWindow // 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(); EndPaint(&lpPaint); } // Paint the background art (splash screen) in the client area; // called by both OnPaint and InitInstance. void CMainWindow::SplashScreen() { CRect rcDest; CRect rcDIB; CDC *pDC; CPalette *pPalOld = nullptr; // Old palette holder CDibDoc myDoc; HDIB hDIB; char msg[64]; pDC = GetDC(); myDoc.OpenDocument(MAINSCREEN); pPalOld = (*pDC).SelectPalette(pGamePalette, false); // Select Game Palette pDC->RealizePalette(); // Realize the palette to prevent palette shifting hDIB = myDoc.GetHDIB(); if (pMazeDC && 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); pDC->BitBlt(SIDE_BORDER, TOP_BORDER, ART_WIDTH, ART_HEIGHT, pMazeDC, 0, SQ_SIZE_Y / 2, SRCCOPY); // Draw Maze if ((_playerSprite != nullptr) && _playing) (*_playerSprite).PaintSprite(pDC, (_playerPos.x * SQ_SIZE_X) + SIDE_BORDER, (_playerPos.y * SQ_SIZE_Y) + TOP_BORDER - SQ_SIZE_Y / 2); // Update PLAYER } if (_playing) { // only false when the options are displayed PaintBitmap(pDC, pGamePalette, _blankBitmap, TIME_LOCATION_X, TIME_LOCATION_Y); if (_time == 0) Common::sprintf_s(msg, "Time Used: %02d:%02d", _minutes, _seconds); else { Common::sprintf_s(msg, "Time Left: %02d:%02d", _minutes, _seconds); } (*m_pTimeText).DisplayString(pDC, msg, 16, FW_SEMIBOLD, OPTIONS_COLOR); } else { if (_localeBitmap != nullptr) PaintBitmap(pDC, pGamePalette, _localeBitmap, TIME_LOCATION_X, TIME_LOCATION_Y); } (*pDC).SelectPalette(pPalOld, false); // Select back old palette 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) { if (HIWORD(lParam) == BN_CLICKED) { CDC *pDC; CRules RulesDlg((CWnd *)this, RULES_TEXT, pGamePalette, pGameInfo->bSoundEffectsEnabled ? RULES_WAV : nullptr); CMainMenu COptionsWind((CWnd *)this, pGamePalette, pGameInfo->bPlayingMetagame ? (NO_NEWGAME | NO_OPTIONS) : 0, GetSubOptions, RULES_TEXT, pGameInfo->bSoundEffectsEnabled ? RULES_WAV : nullptr, pGameInfo) ; // Construct option dialog pDC = GetDC(); PaintBitmap(pDC, pGamePalette, _localeBitmap, TIME_LOCATION_X, TIME_LOCATION_Y); switch (wParam) { case IDC_RULES: KillTimer(GAME_TIMER); CSound::waitWaveSounds(); m_bIgnoreScrollClick = true; (*m_pScrollButton).SendMessage(BM_SETSTATE, true, 0L); RulesDlg.DoModal(); m_bIgnoreScrollClick = false; (*m_pScrollButton).SendMessage(BM_SETSTATE, false, 0L); if (!_gameOver) SetTimer(GAME_TIMER, CLICK_TIME, nullptr); // Reset ticker break; case IDC_SCROLL: KillTimer(GAME_TIMER); if (m_bIgnoreScrollClick) { (*m_pScrollButton).SendMessage(BM_SETSTATE, true, 0L); break; } m_bIgnoreScrollClick = true; (*m_pScrollButton).SendMessage(BM_SETSTATE, true, 0L); SendDlgItemMessage(IDC_SCROLL, BM_SETSTATE, true, 0L); CSound::waitWaveSounds(); switch (COptionsWind.DoModal()) { case IDC_OPTIONS_NEWGAME: // Selected New Game (*m_pScrollButton).SendMessage(BM_SETSTATE, false, 0L); m_bIgnoreScrollClick = false; if (!pGameInfo->bPlayingMetagame) NewGame(); break; case IDC_OPTIONS_RETURN: (*m_pScrollButton).SendMessage(BM_SETSTATE, false, 0L); m_bIgnoreScrollClick = false; if (!_gameOver) SetTimer(GAME_TIMER, CLICK_TIME, nullptr); // Reset ticker break; case IDC_OPTIONS_QUIT: // Quit button was clicked if (pGameInfo->bPlayingMetagame) pGameInfo->lScore = 0; PostMessage(WM_CLOSE, 0, 0); // and post a program exit ReleaseDC(pDC); return false; } //end switch(ComDlg.DoModal()) if (!pGameInfo->bMusicEnabled && (_gameSound != nullptr)) { _gameSound->stop(); delete _gameSound; _gameSound = nullptr; } else if (pGameInfo->bMusicEnabled && (_gameSound == nullptr)) { if ((_gameSound = new CSound) != nullptr) { _gameSound->initialize(this, GAME_THEME, SOUND_MIDI | SOUND_LOOP | SOUND_DONT_LOOP_TO_END); _gameSound->midiLoopPlaySegment(3000, 32980, 0, FMT_MILLISEC); } } m_bIgnoreScrollClick = false; (*m_pScrollButton).SendMessage(BM_SETSTATE, false, 0L); break; } //end switch(wParam) ReleaseDC(pDC); } // end if (*this).SetFocus(); // Reset focus back to the main window return true; } /***************************************************************** * * OnLButtonDown * * FUNCTIONAL DESCRIPTION: * * Left mouse button processing function * * FORMAL PARAMETERS: * * unsigned int nFlags Virtual key info * CPoint point Location of cursor * * IMPLICIT INPUT PARAMETERS: * * [External data read] * * IMPLICIT OUTPUT PARAMETERS: * * [External data modified] * * RETURN VALUE: * * void * ****************************************************************/ void CMainWindow::OnLButtonDown(unsigned int nFlags, CPoint point) { CRect rectTitle; rectTitle.SetRect(NEWGAME_LOCATION_X, NEWGAME_LOCATION_Y, NEWGAME_LOCATION_X + NEWGAME_WIDTH, NEWGAME_LOCATION_Y + NEWGAME_HEIGHT); if (rectTitle.PtInRect(point) && (pGameInfo->bPlayingMetagame == false)) NewGame(); // Activate New Game if (_playing && InArtRegion(point)) { MovePlayer(point); } CFrameWnd ::OnLButtonDown(nFlags, point); } /***************************************************************** * * OnLButtonUp * * FUNCTIONAL DESCRIPTION: * * Standard Left mouse button processing function * * FORMAL PARAMETERS: * * unsigned int nFlags Virtual key info * CPoint point Location of cursor * * IMPLICIT INPUT PARAMETERS: * * [External data read] * * IMPLICIT OUTPUT PARAMETERS: * * [External data modified] * * RETURN VALUE: * * void * ****************************************************************/ void CMainWindow::OnLButtonUp(unsigned int nFlags, CPoint point) { CFrameWnd ::OnLButtonUp(nFlags, point); } /***************************************************************** * * OnMouseMove * * FUNCTIONAL DESCRIPTION: * * Mouse movement processing function * * FORMAL PARAMETERS: * * unsigned int nFlags Virtual key info * CPoint point Location of cursor * * IMPLICIT INPUT PARAMETERS: * * [External data read] * * IMPLICIT OUTPUT PARAMETERS: * * [External data modified] * * RETURN VALUE: * * void * ****************************************************************/ void CMainWindow::OnMouseMove(unsigned int nFlags, CPoint point) { if (InArtRegion(point) && _playing && !_gameOver) { // If the cursor is within the border GetNewCursor(); //...and we're playing, update the cursor if (nFlags & MK_LBUTTON) { // If the Left mouse button is down, MovePlayer(point); //...have the player follow the mouse } } else SetCursor(LoadCursor(nullptr, IDC_ARROW)); // Refresh cursor object to arrow //...when outside the maze area CFrameWnd ::OnMouseMove(nFlags, point); } /***************************************************************** * * OnRButtonDown * * FUNCTIONAL DESCRIPTION: * * Right mouse button processing function: Undo last move * * FORMAL PARAMETERS: * * unsigned int nFlags Virtual key info * CPoint point Location of cursor * * IMPLICIT INPUT PARAMETERS: * * [External data read] * * IMPLICIT OUTPUT PARAMETERS: * * [External data modified] * * RETURN VALUE: * * void * ****************************************************************/ void CMainWindow::OnRButtonDown(unsigned int nFlags, CPoint point) { CFrameWnd ::OnRButtonDown(nFlags, point); } // End OnRButtonDown // 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 if (pGameInfo->bPlayingMetagame) pGameInfo->lScore = 0; 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) { switch (nChar) { // User has hit ALT_F4 so close down this App // case VK_F4: if (pGameInfo->bPlayingMetagame) pGameInfo->lScore = 0; PostMessage(WM_CLOSE, 0, 0); break; default: CFrameWnd::OnSysKeyDown(nChar, nRepCnt, nFlags); break; } } void CMainWindow::OnKeyDown(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags) { CPoint NewPosition; NewPosition = (*_playerSprite).GetPosition(); switch (nChar) { case VK_F1: // F1 key is hit SendMessage(WM_COMMAND, IDC_RULES, BN_CLICKED); // Activate the Rules dialog break; case VK_F2: // F2 key is hit SendMessage(WM_COMMAND, IDC_SCROLL, BN_CLICKED); // Activate the Options dialog break; case 'h': case 'H': case VK_LEFT: case VK_NUMPAD4: NewPosition.x -= SQ_SIZE_X; MovePlayer(NewPosition); break; case 'k': case 'K': case VK_UP: // case VK_NUMPAD8: NewPosition.y -= SQ_SIZE_Y; MovePlayer(NewPosition); break; case 'l': case 'L': case VK_RIGHT: case VK_NUMPAD6: NewPosition.x += SQ_SIZE_X; MovePlayer(NewPosition); break; case 'j': case 'J': case VK_DOWN: case VK_NUMPAD2: NewPosition.y += SQ_SIZE_Y; MovePlayer(NewPosition); break; } } /***************************************************************** * * OnTimer * * FUNCTIONAL DESCRIPTION: * * Processes Timer events * * FORMAL PARAMETERS: * * unsigned int nIDEvent The ID of the timer event activated * * IMPLICIT INPUT PARAMETERS: * * [External data read] * * IMPLICIT OUTPUT PARAMETERS: * * [External data modified] * * RETURN VALUE: * * void * ****************************************************************/ void CMainWindow::OnTimer(uintptr nIDEvent) { CDC *pDC; CSound *pEffect = nullptr; char msg[64]; pDC = GetDC(); switch (nIDEvent) { case GAME_TIMER: if (_time == 0) { // No time limit, increment _seconds++; if (_seconds == 60) { _minutes++; _seconds = 0; } } else { // Count down time left if (_seconds == 0 && _minutes != 0) { _minutes--; _seconds = 60; } _seconds--; } if (_time == 0) Common::sprintf_s(msg, "Time Used: %02d:%02d", _minutes, _seconds); else { Common::sprintf_s(msg, "Time Left: %02d:%02d", _minutes, _seconds); } (*m_pTimeText).DisplayString(pDC, msg, 16, FW_SEMIBOLD, OPTIONS_COLOR); if (_minutes == 0 && _seconds == 0) { // No time left on the clock!! KillTimer(nIDEvent); // Stop the Display timer _playing = false; _gameOver = true; if (pGameInfo->bSoundEffectsEnabled) { pEffect = new CSound((CWnd *)this, LOSE_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.", "Time ran out!"); CSound::waitWaveSounds(); if (pGameInfo->bPlayingMetagame) { pGameInfo->lScore = 0; PostMessage(WM_CLOSE, 0, 0); // and post a program exit } } break; default: CFrameWnd ::OnTimer(nIDEvent); break; } ReleaseDC(pDC); } /********************************************************** Other functions: ***********************************************************/ /***************************************************************** * * NewGame * * FUNCTIONAL DESCRIPTION: * * Set up and start a new game * * FORMAL PARAMETERS: * * none * * IMPLICIT INPUT PARAMETERS: * * [External data read] * * IMPLICIT OUTPUT PARAMETERS: * * [External data modified] * * RETURN VALUE: * * void * ****************************************************************/ void CMainWindow::NewGame() { CDC *pDC; char msg[64]; pDC = GetDC(); _time = tempTime;; // get new time limit, _difficulty = tempDifficulty; //...new Difficulty if (_playerSprite != nullptr) // Refresh PLAYER (*_playerSprite).EraseSprite(pDC); // Erase PlayerSprite if (_time != 0) { // If we've got a time limit _minutes = _time / 60; //...get the minutes and seconds _seconds = _time % 60; } else { _minutes = 0; _seconds = 0; } initialize_maze(); // draw the surrounding wall and start/end squares create_maze(); // create a maze layout given the intiialized maze SetUpMaze(); // translate maze data to grid layout for display PaintMaze(pMazeDC); // paint that sucker to the offscreen bitmap pDC->BitBlt(SIDE_BORDER, TOP_BORDER, ART_WIDTH, ART_HEIGHT, pMazeDC, 0, SQ_SIZE_Y / 2, SRCCOPY); // Draw Maze if (_playerSprite != nullptr) (*_playerSprite).PaintSprite(pDC, (_playerPos.x * SQ_SIZE_X) + SIDE_BORDER, (_playerPos.y * SQ_SIZE_Y) + TOP_BORDER - SQ_SIZE_Y / 2); // Display PLAYER _playing = true; // Game is started _gameOver = false; PaintBitmap(pDC, pGamePalette, _blankBitmap, TIME_LOCATION_X, TIME_LOCATION_Y); if (_time == 0) Common::sprintf_s(msg, "Time Used: %02d:%02d", _minutes, _seconds); else { Common::sprintf_s(msg, "Time Left: %02d:%02d", _minutes, _seconds); } (*m_pTimeText).DisplayString(pDC, msg, 16, FW_SEMIBOLD, OPTIONS_COLOR); SetTimer(GAME_TIMER, CLICK_TIME, nullptr); // Reset ticker ReleaseDC(pDC); } // end NewGame /***************************************************************** * * MovePlayer * * 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::MovePlayer(CPoint point) { CDC *pDC; CSound *pEffect = nullptr; CPoint NewPosition; CPoint TileLocation; CPoint Hit; bool bCollision = false; POINT Delta; POINT Step; unsigned int nBmpID = IDB_HODJ_RIGHT; auto *app = AfxGetApp(); if (_gameOver) // Don't allow movement after game finishes return; pDC = GetDC(); Hit = ScreenToTile(point); Step.x = 0; Step.y = 0; NewPosition.x = _playerPos.x; NewPosition.y = _playerPos.y; Delta.x = _playerPos.x - Hit.x; // Get x distance from mouse click to player in Tile spaces Delta.y = _playerPos.y - Hit.y; // Get y distance from mouse click to player in Tile spaces if (ABS(Delta.x) > ABS(Delta.y)) { // Moving horizontally: if (Delta.x < 0) { // To the RIGHT Step.x = 1; // move one tile at a time nBmpID = IDB_HODJ_RIGHT + m_nPlayerID; // use the Bitmap of the player moving Right } else if (Delta.x > 0) { // To the LEFT Step.x = -1; // move one tile at a time nBmpID = IDB_HODJ_LEFT + m_nPlayerID; // use Bitmap of player moving Left } } else if (ABS(Delta.y) > ABS(Delta.x)) { if (Delta.y > 0) { // Going UPward Step.y = -1; // move one tile at a time nBmpID = IDB_HODJ_UP + m_nPlayerID; // use Bitmap of player moving Up } else if (Delta.y < 0) { // Going DOWNward Step.y = 1; // move one tile at a time nBmpID = IDB_HODJ_DOWN + m_nPlayerID; // use Bitmap of player moving Down } } if ((Step.x != 0) || (Step.y != 0)) { // If the click is not in the Player's Tile _success = (*_playerSprite).LoadResourceCels(pDC, nBmpID, NUM_CELS); ASSERT(_success); if (_playerSprite != nullptr) // Refresh PLAYER (*_playerSprite).PaintSprite(pDC, (_playerPos.x * SQ_SIZE_X) + SIDE_BORDER, (_playerPos.y * SQ_SIZE_Y) + TOP_BORDER - SQ_SIZE_Y / 2); //...in new direction while (!bCollision) { NewPosition.Offset(Step); if (_mazeTile[NewPosition.x][NewPosition.y].m_nWall == PATH || // Either a pathway ((_mazeTile[NewPosition.x][NewPosition.y].m_nWall == TRAP && //...or a _mazeTile[NewPosition.x][NewPosition.y].m_bHidden == false) || //...revealed trap _mazeTile[NewPosition.x][NewPosition.y].m_nWall == EXIT)) { //...or exit is a GO int i, x, y; x = (_playerPos.x * SQ_SIZE_X) + SIDE_BORDER; // Get player's position y = (_playerPos.y * SQ_SIZE_Y) + TOP_BORDER - SQ_SIZE_Y / 2; //...in screen coords for (i = 0; i < 4; i++) { // Go through three cels app->pause(); x += Step.x * i * (SQ_SIZE_X / 4); //...per tile moved y += Step.y * i * (SQ_SIZE_Y / 4); if (_playerSprite != nullptr) (*_playerSprite).PaintSprite(pDC, x, y); // Update PLAYER } // end for _playerPos.x = NewPosition.x; _playerPos.y = NewPosition.y; if (_playerSprite != nullptr) // Refresh PLAYER (*_playerSprite).PaintSprite(pDC, (_playerPos.x * SQ_SIZE_X) + SIDE_BORDER, (_playerPos.y * SQ_SIZE_Y) + TOP_BORDER - SQ_SIZE_Y / 2); //...in new direction app->pause(); } // end if if ((_mazeTile[NewPosition.x][NewPosition.y].m_bHidden) && (_mazeTile[NewPosition.x][NewPosition.y].m_nWall == WALL)) { if (pGameInfo->bSoundEffectsEnabled) { pEffect = new CSound((CWnd *)this, HIT_SOUND, SOUND_WAVE | SOUND_AUTODELETE); //| SOUND_ASYNCH ...Wave file, to delete itself (*pEffect).play(); //...play the narration } _mazeTile[NewPosition.x][NewPosition.y].m_bHidden = false; if (_playerSprite != nullptr) // Refresh PLAYER (*_playerSprite).EraseSprite(pDC); // Erase PlayerSprite PaintBitmap(pDC, pGamePalette, _wallBitmap, // Paint wall on screen _mazeTile[NewPosition.x][NewPosition.y].m_nStart.x + SIDE_BORDER, _mazeTile[NewPosition.x][NewPosition.y].m_nStart.y + TOP_BORDER - SQ_SIZE_Y / 2); AddEdges(pDC, NewPosition.x, NewPosition.y, SIDE_BORDER, TOP_BORDER - SQ_SIZE_Y / 2); PaintBitmap(pMazeDC, pGamePalette, _wallBitmap, // Paint wall in Maze Bitmap _mazeTile[NewPosition.x][NewPosition.y].m_nStart.x, _mazeTile[NewPosition.x][NewPosition.y].m_nStart.y); AddEdges(pMazeDC, NewPosition.x, NewPosition.y, 0, 0); if (_playerSprite != nullptr) // Refresh PLAYER (*_playerSprite).PaintSprite(pDC, (_playerPos.x * SQ_SIZE_X) + SIDE_BORDER, (_playerPos.y * SQ_SIZE_Y) + TOP_BORDER - SQ_SIZE_Y / 2); //...in new direction bCollision = true; } if (_mazeTile[NewPosition.x][NewPosition.y].m_nWall == TRAP && (_mazeTile[NewPosition.x][NewPosition.y].m_bHidden)) { // Traps are only good once _mazeTile[NewPosition.x][NewPosition.y].m_bHidden = false; _playerPos.x = _mazeTile[NewPosition.x][NewPosition.y].m_nDest.x; _playerPos.y = _mazeTile[NewPosition.x][NewPosition.y].m_nDest.y; if (_playerSprite != nullptr) (*_playerSprite).EraseSprite(pDC); // Erase PlayerSprite PaintBitmap(pDC, pGamePalette, // Paint trap on screen _trapBitmap[_mazeTile[NewPosition.x][NewPosition.y].m_nTrap], _mazeTile[NewPosition.x][NewPosition.y].m_nStart.x + SIDE_BORDER, _mazeTile[NewPosition.x][NewPosition.y].m_nStart.y + TOP_BORDER - SQ_SIZE_Y / 2); if (pGameInfo->bSoundEffectsEnabled) { pEffect = new CSound((CWnd *)this, TRAP_SOUND, SOUND_WAVE | SOUND_AUTODELETE); //| SOUND_ASYNCH ...Wave file, to delete itself (*pEffect).play(); //...play the narration } PaintBitmap(pMazeDC, pGamePalette, // Paint trap in Maze Bitmap _trapBitmap[_mazeTile[NewPosition.x][NewPosition.y].m_nTrap], _mazeTile[NewPosition.x][NewPosition.y].m_nStart.x, _mazeTile[NewPosition.x][NewPosition.y].m_nStart.y); if (_playerSprite != nullptr) (*_playerSprite).PaintSprite(pDC, (_playerPos.x * SQ_SIZE_X) + SIDE_BORDER, (_playerPos.y * SQ_SIZE_Y) + TOP_BORDER - SQ_SIZE_Y / 2); // Update PLAYER bCollision = true; } if ((_mazeTile[NewPosition.x][NewPosition.y].m_nWall == WALL) || (_mazeTile[NewPosition.x][NewPosition.y].m_nWall == START)) { bCollision = true; } if (_mazeTile[NewPosition.x][NewPosition.y].m_nWall == EXIT) { _playing = false; _gameOver = true; KillTimer(GAME_TIMER); 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.", "He's free!"); CSound::waitWaveSounds(); if (pGameInfo->bPlayingMetagame) { pGameInfo->lScore = 1; // A victorious maze solving PostMessage(WM_CLOSE, 0, 0); // and post a program exit } bCollision = true; } if ((NewPosition.x == Hit.x) && (NewPosition.y == Hit.y)) bCollision = true; } // end while GetNewCursor(); } // end if ReleaseDC(pDC); } /***************************************************************** * * GetNewCursor * * FUNCTIONAL DESCRIPTION: * * Loads up a new cursor with regard to the current cursor position, and the player position * * FORMAL PARAMETERS: * * none * * IMPLICIT INPUT PARAMETERS: * * CPoint m_PlayerPos The player's current location in Grid coordinates * * IMPLICIT OUTPUT PARAMETERS: * * [External data modified] * * RETURN VALUE: * * void * ****************************************************************/ void CMainWindow::GetNewCursor() { CPoint Hit, Delta; POINT pCursorLoc; HCURSOR hNewCursor = nullptr; CWinApp *pMyApp; pMyApp = AfxGetApp(); MFC::GetCursorPos(&pCursorLoc); MFC::ScreenToClient(m_hWnd, &pCursorLoc); Delta.x = pCursorLoc.x; Delta.y = pCursorLoc.y; Hit = ScreenToTile(Delta); Delta.x = _playerPos.x - Hit.x; Delta.y = _playerPos.y - Hit.y; if ((_playerPos.x == Hit.x) && (_playerPos.y == Hit.y)) { // Directly over player hNewCursor = (*pMyApp).LoadCursor(IDC_MOD_NOARROW); } else if (ABS(Delta.x) >= ABS(Delta.y)) { // Moving horizontally: if (Delta.x <= 0) // To the RIGHT hNewCursor = (*pMyApp).LoadCursor(IDC_MOD_RTARROW); else if (Delta.x > 0) // To the LEFT hNewCursor = (*pMyApp).LoadCursor(IDC_MOD_LFARROW); } else if (ABS(Delta.y) > ABS(Delta.x)) { if (Delta.y >= 0) // Going UPward hNewCursor = (*pMyApp).LoadCursor(IDC_MOD_UPARROW); else if (Delta.y < 0) // Going DOWNward hNewCursor = (*pMyApp).LoadCursor(IDC_MOD_DNARROW); } // if (hNewCursor != nullptr); MFC::SetCursor(hNewCursor); } /***************************************************************** * * PaintMaze * * FUNCTIONAL DESCRIPTION: * * Paints the maze in the pDC * * FORMAL PARAMETERS: * * CDC pDC Device context to which we're drawing the maze * * IMPLICIT INPUT PARAMETERS: * * struct TILE mazeTile grid * * IMPLICIT OUTPUT PARAMETERS: * * The bitmap associated with pDC * * RETURN VALUE: * * void * ****************************************************************/ void PaintMaze(CDC *pDC) { int x, y; for (x = 0; x < NUM_COLUMNS; x++) { for (y = 0; y < NUM_ROWS; y++) { _mazeTile[x][y].m_nStart.x = x * SQ_SIZE_X; // Put in location info _mazeTile[x][y].m_nStart.y = y * SQ_SIZE_Y; if ((_mazeTile[x][y].m_nWall == PATH) || (_mazeTile[x][y].m_nWall == EXIT) || _mazeTile[x][y].m_bHidden) // Path or hidden obj. PaintBitmap(pDC, pGamePalette, _pathBitmap, //...draw path bitmap _mazeTile[x][y].m_nStart.x, _mazeTile[x][y].m_nStart.y); else if (_mazeTile[x][y].m_nWall == START) // Start of maze PaintBitmap(pDC, pGamePalette, _startBitmap, //...draw start bitmap _mazeTile[x][y].m_nStart.x, _mazeTile[x][y].m_nStart.y); else // Otherwise, it's a PaintBitmap(pDC, pGamePalette, _wallBitmap, //...wall _mazeTile[x][y].m_nStart.x, _mazeTile[x][y].m_nStart.y); } // end for y } // end for x for (x = 0; x < NUM_COLUMNS; x++) { // Go through the grid for (y = 0; y < NUM_ROWS; y++) { //...and for every square AddEdges(pDC, x, y, 0, 0); //...add trim if needed } // end for y } // end for x } // End PaintMaze /***************************************************************** * * SetUpMaze * * FUNCTIONAL DESCRIPTION: * * Translates the random maze generated into the mazeTile grid for the game * * FORMAL PARAMETERS: * * none * * IMPLICIT INPUT PARAMETERS: * * maze[][] The randomly generated maze * struct TILE mazeTile[][] grid * start_y * exit_y * * IMPLICIT OUTPUT PARAMETERS: * * struct TILE mazeTile[][] grid * * RETURN VALUE: * * void * ****************************************************************/ void SetUpMaze() { int x, y; CPoint m_pExit; for (y = 0; y < NUM_ROWS; y++) // Set the right wall solid _mazeTile[NUM_COLUMNS - 1][y].m_nWall = WALL; for (x = 0; x < MAX_MAZE_SIZE_X; x++) { for (y = 0; y < MAX_MAZE_SIZE_Y; y++) { _mazeTile[x * 2 + 1][y * 2 + 1].m_nWall = PATH; // Always is PATH _mazeTile[x * 2][y * 2].m_nWall = PATH; // Will be changed to WALL if if (_maze[x][y] & WALL_TOP) { //...it is found below _mazeTile[x * 2][y * 2].m_nWall = WALL; _mazeTile[x * 2 + 1][y * 2].m_nWall = WALL; } else _mazeTile[x * 2 + 1][y * 2].m_nWall = PATH; if (_maze[x][y] & WALL_LEFT) { _mazeTile[x * 2][y * 2].m_nWall = WALL; _mazeTile[x * 2][y * 2 + 1].m_nWall = WALL; } else _mazeTile[x * 2][y * 2 + 1].m_nWall = PATH; } } for (x = 0; x < NUM_COLUMNS; x++) { // Now go through mazeTile and fix up loose ends, as it were for (y = 0; y < NUM_ROWS; y++) { _mazeTile[x][y].m_bHidden = false; if (x > 0 && y > 0 && x < (NUM_COLUMNS - 1) && y < (NUM_ROWS - 1) && _mazeTile[x][y].m_nWall == PATH) { if (_mazeTile[x + 1][y + 1].m_nWall == PATH && (_mazeTile[x + 1][y].m_nWall == PATH && (_mazeTile[x][y + 1].m_nWall == PATH && (_mazeTile[x - 1][y].m_nWall == WALL && _mazeTile[x][y - 1].m_nWall == WALL)))) _mazeTile[x][y].m_nWall = WALL; // If it's a right-hand corner if (_mazeTile[x][y + 1].m_nWall == PATH && (_mazeTile[x + 1][y - 1].m_nWall == PATH && (_mazeTile[x - 1][y - 1].m_nWall == PATH && (_mazeTile[x - 1][y + 1].m_nWall == PATH && (_mazeTile[x + 1][y + 1].m_nWall == PATH && (_mazeTile[x - 1][y].m_nWall == PATH && _mazeTile[x + 1][y].m_nWall == PATH)))))) _mazeTile[x][y].m_nWall = WALL; // If it's two wide vertically from the top if (_mazeTile[x][y - 1].m_nWall == PATH && (_mazeTile[x - 1][y - 1].m_nWall == PATH && (_mazeTile[x - 1][y + 1].m_nWall == PATH && (_mazeTile[x][y + 1].m_nWall == PATH && (_mazeTile[x + 1][y - 1].m_nWall == PATH && (_mazeTile[x + 1][y].m_nWall == PATH && _mazeTile[x + 1][y + 1].m_nWall == PATH)))))) _mazeTile[x][y].m_nWall = WALL; // If it's two wide horizontally from the left } if (y == NUM_ROWS - 1) _mazeTile[x][y].m_nWall = WALL; // Make bottom wall } } x = NUM_COLUMNS - 1; // Get the Entry point y = (_startY * 2) + 1; _playerPos.x = x - 1; // Start player in one space from the entrance if (_mazeTile[x - 1][y].m_nWall == WALL) { // If a wall runs into the entry space _mazeTile[x][y].m_nWall = WALL; //...make it a wall and put the entry _mazeTile[x][y + 1].m_nWall = START; //...space under that _playerPos.y = y; // Put the player there } else { _mazeTile[x][y].m_nWall = START; // Put in the entry way where it was _mazeTile[x][y + 1].m_nWall = WALL; //...and make sure the one below is a wall _playerPos.y = y; // Put the player there } x = _endX * 2; // This should be 0 y = _endY * 2; m_pExit.x = x; if (_mazeTile[x + 1][y].m_nWall == WALL) { // If a wall runs into the top exit space _mazeTile[x][y].m_nWall = WALL; //...make it a wall and put the exit m_pExit.y = y + 1; //...one space above that } else { _mazeTile[x][y + 1].m_nWall = WALL; // Put the exit in the top space m_pExit.y = y; //...and store the y position in m_pExit } _mazeTile[m_pExit.x][m_pExit.y].m_nWall = EXIT; // Make exit grid space a Pathway SetInvisibleWalls(); // Hide some walls SetTraps(); // Put in some traps }//end SetUpMaze /***************************************************************** * * AddEdges * * FUNCTIONAL DESCRIPTION: * * Draws fancy edgework around a piece of wall on the sides where possible * * FORMAL PARAMETERS: * * CDC *pDC The Device context to which it will draw * int x Column of piece to check * int y Row of piece to check * int offset_x Width offset for drawing the bitmap ( Main window has a border) * int offset_y Hieght offset as above * * IMPLICIT INPUT PARAMETERS: * * struct TILE mazeTile[][] * * IMPLICIT OUTPUT PARAMETERS: * * [External data modified] * * RETURN VALUE: * * void * ****************************************************************/ void AddEdges(CDC *pDC, int x, int y, int offset_x, int offset_y) { if ((_mazeTile[x][y].m_bHidden == false) && (_mazeTile[x][y].m_nWall == WALL)) { if ((y > 0) && ((((_mazeTile[x][y - 1].m_nWall == PATH) || (_mazeTile[x][y - 1].m_nWall == EXIT)) || (_mazeTile[x][y - 1].m_nWall == START)) || _mazeTile[x][y - 1].m_bHidden)) // TOP PaintBitmap(pDC, pGamePalette, _bottomEdgeBmp, _mazeTile[x][y - 1].m_nStart.x + offset_x, _mazeTile[x][y - 1].m_nStart.y + offset_y + SQ_SIZE_Y - 1 - EDGE_SIZE); if ((x < (NUM_COLUMNS - 1)) && ((_mazeTile[x + 1][y].m_nWall == PATH) || _mazeTile[x + 1][y].m_bHidden)) // RIGHT PaintBitmap(pDC, pGamePalette, _leftEdgeBmp, _mazeTile[x + 1][y].m_nStart.x + offset_x, _mazeTile[x + 1][y].m_nStart.y + offset_y); if ((y < (NUM_ROWS - 1)) && ((((_mazeTile[x][y + 1].m_nWall == EXIT) || (_mazeTile[x][y + 1].m_nWall == PATH)) || (_mazeTile[x][y + 1].m_nWall == START)) || _mazeTile[x][y + 1].m_bHidden)) // BOTTOM PaintBitmap(pDC, pGamePalette, _topEdgeBmp, _mazeTile[x][y + 1].m_nStart.x + offset_x, _mazeTile[x][y + 1].m_nStart.y + offset_y); if ((x > 0) && ((_mazeTile[x - 1][y].m_nWall == PATH) || _mazeTile[x - 1][y].m_bHidden)) // LEFT PaintBitmap(pDC, pGamePalette, _rightEdgeBmp, _mazeTile[x - 1][y].m_nStart.x + offset_x + SQ_SIZE_X - 1 - EDGE_SIZE, _mazeTile[x - 1][y].m_nStart.y + offset_y); } // end if WALL } // End AddEdges /***************************************************************** * * SetTraps * * FUNCTIONAL DESCRIPTION: * * Sets traps in the maze * * FORMAL PARAMETERS: * * none * * IMPLICIT INPUT PARAMETERS: * * struct TILE mazeTile[][] array * m_nDifficulty The the number of traps = difficulty setting * * IMPLICIT OUTPUT PARAMETERS: * * struct TILE mazeTile[][] array * * RETURN VALUE: * * void * ****************************************************************/ void SetTraps() { int nTrapCount; int nNumTraps; CPoint In; // nNumTraps = m_nDifficulty + MIN_TRAPS; nNumTraps = MIN_TRAPS + (_difficulty / 2); // 4 + ([1...10]/2) = 4 to 9 for (nTrapCount = 0; nTrapCount < nNumTraps; nTrapCount++) { In = GetRandomPoint(false); // Pick a random PATH square _mazeTile[In.x][In.y].m_nWall = TRAP; // Make it a TRAP _mazeTile[In.x][In.y].m_bHidden = true; // Hide it _mazeTile[In.x][In.y].m_nTrap = nTrapCount % NUM_TRAP_MAPS; // Assign unique trap bitmap ID _mazeTile[In.x][In.y].m_nDest = GetRandomPoint(true); // Pick a random Trap destination } } /***************************************************************** * * GetRandomPoint * * FUNCTIONAL DESCRIPTION: * * Gets a random Grid Point in the maze, which is a PATH (not START or EXIT) * * FORMAL PARAMETERS: * * none * * IMPLICIT INPUT PARAMETERS: * * struct TILE mazeTile[][] grid * * IMPLICIT OUTPUT PARAMETERS: * * none * * RETURN VALUE: * * CPoint the random X and Y of a path space in the mazeTile grid * ****************************************************************/ CPoint GetRandomPoint(bool bRight) { CPoint point; bool bLocated = false; if (bRight) // Get random column point.x = (brand() % (2 * (NUM_COLUMNS / 3))) + (NUM_COLUMNS / 3); //...in the right half else point.x = brand() % (2 * (NUM_COLUMNS / 3)); //...or the left half point.y = brand() % NUM_ROWS; // Get random row while (!bLocated) { if (_mazeTile[point.x][point.y].m_nWall == PATH) bLocated = true; // OK if it's a pathway else { // Otherwise, keep lookin' point.x++; // Increment Column point.y++; // Increment Row if (point.x == NUM_COLUMNS) point.x = 1; // If we're at the end, if (point.y == NUM_ROWS) point.y = 1; //...reset the counter } } // end while return point; } // end GetRandomPoint /***************************************************************** * * SetInvisibleWalls * * FUNCTIONAL DESCRIPTION: * * Randomly sets a number of walls invisible * * FORMAL PARAMETERS: * * none * * IMPLICIT INPUT PARAMETERS: * * struct TILE mazeTile[][] grid * m_nDifficulty If it's MIN_DIFFICULTY, no walls are invisible * If it's MAX_DIFFICULTY, all walls are invisible * Otherwise, every m_nDifficulty-th wall is visible * * IMPLICIT OUTPUT PARAMETERS: * * struct TILE mazeTile[][] grid * * RETURN VALUE: * * void * ****************************************************************/ void SetInvisibleWalls() { int x, y, i, j; int nWallCount = 0; int nMaxWalls = 0; int nTotalWalls = 0; for (x = 1; x < (NUM_COLUMNS - 1); x++) { // Don't make edge walls invisible !! for (y = 1; y < (NUM_ROWS - 1); y++) { if (_mazeTile[x][y].m_nWall == WALL) { if (_difficulty > MIN_DIFFICULTY) // Most difficult has all walls hidden _mazeTile[x][y].m_bHidden = true; // Start with all walls hidden else _mazeTile[x][y].m_bHidden = false; // Least difficult has no walls hidden nTotalWalls++; } // end if } // end for y } // end for x if (_difficulty > MIN_DIFFICULTY && _difficulty < MAX_DIFFICULTY) { x = (brand() % (NUM_COLUMNS - 4)) + 2; // Avoid the edge walls y = (brand() % (NUM_ROWS - 4)) + 2; nMaxWalls = nTotalWalls - (int)(_difficulty * (nTotalWalls / 10)); while (nWallCount < nMaxWalls) { if (_mazeTile[x][y].m_nWall == WALL && _mazeTile[x][y].m_bHidden) { for (i = x - 1; i <= x + 1; i++) { for (j = y - 1; j <= y + 1; j++) { if (_mazeTile[i][j].m_nWall == WALL && _mazeTile[i][j].m_bHidden) { _mazeTile[i][j].m_bHidden = false; // so it's not hidden nWallCount++; // increment the count } // end if } // end j } // end i } // end if x += (brand() % NUM_NEIGHBORS);// + 1; // Increment Column y += (brand() % NUM_NEIGHBORS);// + 1; // Increment Row if (x >= (NUM_COLUMNS - 2)) x = (brand() % (NUM_COLUMNS - 4)) + 2; // If we're at the end, if (y >= (NUM_ROWS - 2)) y = (brand() % (NUM_COLUMNS - 4)) + 2; //...reset the counter } // end while } // end if } void initialize_maze() { /* draw the surrounding wall and start/end squares */ int i, j, wall; _mazeSizeX = MAX_MAZE_SIZE_X; _mazeSizeY = MAX_MAZE_SIZE_Y; /* initialize all squares */ for (i = 0; i < _mazeSizeX; i++) { for (j = 0; j < _mazeSizeY; j++) { _maze[i][j] = 0; } } /* top wall */ for (i = 0; i < _mazeSizeX; i++) { _maze[i][0] |= WALL_TOP; } /* right wall */ for (j = 0; j < _mazeSizeY; j++) { _maze[_mazeSizeX - 1][j] |= WALL_RIGHT; } /* bottom wall */ for (i = 0; i < _mazeSizeX; i++) { _maze[i][_mazeSizeY - 1] |= WALL_BOTTOM; } /* left wall */ for (j = 0; j < _mazeSizeY; j++) { _maze[0][j] |= WALL_LEFT; } /* set start square */ wall = 1; // Start on right side i = _mazeSizeX - 1; // Set maze x location j = brand() % _mazeSizeY; // Set a random y location not on the top row _maze[i][j] |= START_SQUARE; _maze[i][j] |= (DOOR_IN_TOP >> wall); _maze[i][j] &= ~(WALL_TOP >> wall); _startX = i; _startY = j; _curSqX = i; _curSqY = j; _sqNum = 0; /* set end square */ wall = (wall + 2) % 4; switch (wall) { case 0: i = brand() % (_mazeSizeX); j = 0; break; case 1: i = _mazeSizeX - 1; j = brand() % (_mazeSizeY); break; case 2: i = brand() % (_mazeSizeX); j = _mazeSizeY - 1; break; case 3: i = 0; j = brand() % (_mazeSizeY); break; } _maze[i][j] |= END_SQUARE; _maze[i][j] |= (DOOR_OUT_TOP >> wall); _maze[i][j] &= ~(WALL_TOP >> wall); _endX = i; _endY = j; } void create_maze() { /* create a maze layout given the intiialized maze */ int newdoor = 0; // i, do { _moveList[_sqNum].x = _curSqX; _moveList[_sqNum].y = _curSqY; _moveList[_sqNum].dir = newdoor; while ((newdoor = choose_door()) == -1) { /* pick a door */ if (backup() == -1) { /* no more doors ... backup */ return; /* done ... return */ } } /* mark the out door */ _maze[_curSqX][_curSqY] |= (DOOR_OUT_TOP >> newdoor); switch (newdoor) { case 0: _curSqY--; break; case 1: _curSqX++; break; case 2: _curSqY++; break; case 3: _curSqX--; break; } _sqNum++; /* mark the in door */ _maze[_curSqX][_curSqY] |= (DOOR_IN_TOP >> ((newdoor + 2) % 4)); /* if end square set path length and save path */ } while (1); } int choose_door() { /* pick a new path */ int candidates[4]; int num_candidates; num_candidates = 0; //topwall: /* top wall */ if (_maze[_curSqX][_curSqY] & DOOR_IN_TOP) goto rightwall; if (_maze[_curSqX][_curSqY] & DOOR_OUT_TOP) goto rightwall; if (_maze[_curSqX][_curSqY] & WALL_TOP) goto rightwall; if (_maze[_curSqX][_curSqY - 1] & DOOR_IN_ANY) { _maze[_curSqX][_curSqY] |= WALL_TOP; _maze[_curSqX][_curSqY - 1] |= WALL_BOTTOM; goto rightwall; } candidates[num_candidates++] = 0; rightwall: /* right wall */ if (_maze[_curSqX][_curSqY] & DOOR_IN_RIGHT) goto bottomwall; if (_maze[_curSqX][_curSqY] & DOOR_OUT_RIGHT) goto bottomwall; if (_maze[_curSqX][_curSqY] & WALL_RIGHT) goto bottomwall; if (_maze[_curSqX + 1][_curSqY] & DOOR_IN_ANY) { _maze[_curSqX][_curSqY] |= WALL_RIGHT; _maze[_curSqX + 1][_curSqY] |= WALL_LEFT; goto bottomwall; } candidates[num_candidates++] = 1; bottomwall: /* bottom wall */ if (_maze[_curSqX][_curSqY] & DOOR_IN_BOTTOM) goto leftwall; if (_maze[_curSqX][_curSqY] & DOOR_OUT_BOTTOM) goto leftwall; if (_maze[_curSqX][_curSqY] & WALL_BOTTOM) goto leftwall; if (_maze[_curSqX][_curSqY + 1] & DOOR_IN_ANY) { _maze[_curSqX][_curSqY] |= WALL_BOTTOM; _maze[_curSqX][_curSqY + 1] |= WALL_TOP; goto leftwall; } candidates[num_candidates++] = 2; leftwall: /* left wall */ if (_maze[_curSqX][_curSqY] & DOOR_IN_LEFT) goto donewall; if (_maze[_curSqX][_curSqY] & DOOR_OUT_LEFT) goto donewall; if (_maze[_curSqX][_curSqY] & WALL_LEFT) goto donewall; if (_maze[_curSqX - 1][_curSqY] & DOOR_IN_ANY) { _maze[_curSqX][_curSqY] |= WALL_LEFT; _maze[_curSqX - 1][_curSqY] |= WALL_RIGHT; goto donewall; } candidates[num_candidates++] = 3; donewall: if (num_candidates == 0) return -1; if (num_candidates == 1) return candidates[0]; return candidates[brand() % (num_candidates)]; } int backup() { /* back up a move */ _sqNum--; if (_sqNum > 0) { _curSqX = _moveList[_sqNum].x; _curSqY = _moveList[_sqNum].y; } return _sqNum; } /***************************************************************** * * ScreenToTile * * FUNCTIONAL DESCRIPTION: * * Converts a point in screen coordinates to x & y location on maze grid * * FORMAL PARAMETERS: * * CPoint pointScreen a point in screen coordinates * * IMPLICIT INPUT PARAMETERS: * * none * * IMPLICIT OUTPUT PARAMETERS: * * none * * RETURN VALUE: * * CPoint point the x & y grid coordinates where pointScreen fell * ****************************************************************/ CPoint ScreenToTile(CPoint pointScreen) { CPoint point; point.x = (pointScreen.x - SIDE_BORDER) / SQ_SIZE_X; point.y = (pointScreen.y - TOP_BORDER + SQ_SIZE_Y / 2) / SQ_SIZE_Y; return point; } /***************************************************************** * * InArtRegion * * FUNCTIONAL DESCRIPTION: * * Checks to see if a point is within the Artwork region of the window * * FORMAL PARAMETERS: * * CPoint point The point to check * * IMPLICIT INPUT PARAMETERS: * * Extents of the main game window, and the extents of the artwork * * IMPLICIT OUTPUT PARAMETERS: * * none * * RETURN VALUE: * * bool: true if point is within the Art Region, * false if point is outside the Art Region * ****************************************************************/ bool InArtRegion(CPoint point) { if ((point.x > SIDE_BORDER && point.x < GAME_WIDTH - SIDE_BORDER) && // See if point lies within (point.y > TOP_BORDER && point.y < GAME_HEIGHT - BOTTOM_BORDER)) //...ArtWork area return true; // Return true if it's inside else return false; //...and false if not } void CMainWindow::OnClose() { CDC *pDC; CBrush myBrush; CRect myRect; pDC = GetDC(); myRect.SetRect(0, 0, GAME_WIDTH, GAME_HEIGHT); myBrush.CreateStockObject(BLACK_BRUSH); (*pDC).FillRect(&myRect, &myBrush); ReleaseDC(pDC); SetCursor(LoadCursor(nullptr, IDC_ARROW)); // Refresh cursor object to arrow // delete the game theme song // if (_gameSound != nullptr) { delete _gameSound; _gameSound = nullptr; } CSound::clearSounds(); // Clean up sounds before returning if (m_pScrollButton != nullptr) delete m_pScrollButton; if (m_pTimeText != nullptr) delete m_pTimeText; if (_blankBitmap != nullptr) { _blankBitmap->DeleteObject(); delete _blankBitmap; } if (_localeBitmap != nullptr) { _localeBitmap->DeleteObject(); delete _localeBitmap; } if (_playerSprite != nullptr) delete _playerSprite; if (pOldBmp != nullptr) // Get rid of Scratch1 pMazeDC->SelectObject(pOldBmp); if (pOldPal != nullptr) pMazeDC->SelectPalette(pOldPal, false); if (pMazeDC->m_hDC != nullptr) { pMazeDC->DeleteDC(); delete pMazeDC; } if (pMazeBitmap != nullptr) { pMazeBitmap->DeleteObject(); delete pMazeBitmap; } if (_pathBitmap != nullptr) { _pathBitmap->DeleteObject(); delete _pathBitmap; } if (_topEdgeBmp != nullptr) { _topEdgeBmp->DeleteObject(); delete _topEdgeBmp; } if (_rightEdgeBmp != nullptr) { _rightEdgeBmp->DeleteObject(); delete _rightEdgeBmp; } if (_bottomEdgeBmp != nullptr) { _bottomEdgeBmp->DeleteObject(); delete _bottomEdgeBmp; } if (_leftEdgeBmp != nullptr) { _leftEdgeBmp->DeleteObject(); delete _leftEdgeBmp; } if (_wallBitmap != nullptr) { _wallBitmap->DeleteObject(); delete _wallBitmap; } if (_startBitmap != nullptr) { _startBitmap->DeleteObject(); delete _startBitmap; } for (int i = 0; i < NUM_TRAP_MAPS; i++) { if (_trapBitmap[i] != nullptr) { _trapBitmap[i]->DeleteObject(); delete _trapBitmap[i]; } } 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 //...Options dialog box OptionsDlg._time = _time; OptionsDlg._seconds = _seconds; // Send clock info OptionsDlg._minutes = _minutes; OptionsDlg._difficulty = _difficulty; if (OptionsDlg.DoModal() == IDOK) { // save values set in dialog box tempTime = OptionsDlg._time;; // get new time limit, tempDifficulty = OptionsDlg._difficulty; //...new Difficulty } } //////////// 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_TIMER() ON_WM_LBUTTONDOWN() ON_WM_RBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_MOUSEMOVE() ON_WM_CLOSE() ON_WM_DESTROY() ON_MESSAGE(MM_MCINOTIFY, CMainWindow::OnMCINotify) ON_MESSAGE(MM_WOM_DONE, CMainWindow::OnMMIONotify) //}}AFX_MSG_MAP END_MESSAGE_MAP() } // namespace MazeDoom } // namespace HodjNPodj } // namespace Bagel