1968 lines
61 KiB
C++
1968 lines
61 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "bagel/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
|