3716 lines
80 KiB
C++
3716 lines
80 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/afxwin.h"
|
|
#include "bagel/hodjnpodj/hnplibs/dibdoc.h"
|
|
#include "bagel/hodjnpodj/hnplibs/stdinc.h"
|
|
#include "bagel/hodjnpodj/hnplibs/text.h"
|
|
#include "bagel/hodjnpodj/globals.h"
|
|
#include "bagel/hodjnpodj/hnplibs/sprite.h"
|
|
#include "bagel/hodjnpodj/hnplibs/mainmenu.h"
|
|
#include "bagel/hodjnpodj/hnplibs/cmessbox.h"
|
|
#include "bagel/hodjnpodj/hnplibs/button.h"
|
|
#include "bagel/boflib/misc.h"
|
|
#include "bagel/hodjnpodj/hnplibs/rules.h"
|
|
#include "bagel/boflib/error.h"
|
|
#include "bagel/boflib/sound.h"
|
|
#include "bagel/hodjnpodj/hnplibs/gamedll.h"
|
|
#include "bagel/hodjnpodj/battlefish/bfish.h"
|
|
#include "bagel/hodjnpodj/battlefish/usercfg.h"
|
|
#include "bagel/hodjnpodj/hodjnpodj.h"
|
|
|
|
namespace Bagel {
|
|
namespace HodjNPodj {
|
|
namespace Battlefish {
|
|
|
|
#define CSOUND 0
|
|
#define FONT_SIZE 8
|
|
|
|
//int bob[5] = { 27, 56, 7, 63, 31 };
|
|
//int fred;
|
|
|
|
//
|
|
// This mini-game's main screen bitmap
|
|
//
|
|
#define MINI_GAME_MAP ".\\ART\\BFISH.BMP"
|
|
|
|
//
|
|
// Game theme song
|
|
//
|
|
#define MID_SOUNDTRACK ".\\SOUND\\BFISH.MID"
|
|
|
|
//
|
|
// Button ID constants
|
|
//
|
|
#define IDC_MENU 100
|
|
|
|
#define COMPUTERS_TURN 101
|
|
|
|
// Bitmap IDs
|
|
//
|
|
#define IDB_FISH 200
|
|
#define IDB_FISH0 200
|
|
#define IDB_FISH1 201
|
|
#define IDB_FISH2 202
|
|
#define IDB_FISH3 203
|
|
|
|
#define IDB_HARPOON 204
|
|
|
|
#define IDB_HIT 205
|
|
#define IDB_MISS 206
|
|
#define IDB_FISHROT 207
|
|
#define IDB_FISHROT0 207
|
|
#define IDB_FISHROT1 208
|
|
#define IDB_FISHROT2 202 // 2x2 fish has same picture
|
|
#define IDB_FISHROT3 210
|
|
|
|
// animation sprites
|
|
#define IDB_PLUME IDB_MISS
|
|
#define IDB_HARP IDB_HIT
|
|
#define IDB_APLUME 211
|
|
#define IDB_AHARP 212
|
|
|
|
#define IDB_HOOK 213
|
|
#define IDB_HOOK0 213
|
|
#define IDB_HOOK1 214
|
|
#define IDB_HOOK2 215
|
|
#define IDB_HOOK3 216
|
|
|
|
#define IDB_OCTOPUS 217
|
|
|
|
#define N_PLUME_CELS 14
|
|
#define N_HARP_CELS 10
|
|
|
|
// Rules Text File Identifier
|
|
//
|
|
#define RULESFILE "BFISH.TXT"
|
|
|
|
//
|
|
// .WAV sounds for Battle Fish
|
|
//
|
|
#define WAV_PLACESHIP ".\\SOUND\\PUTFISH.WAV"
|
|
#define WAV_ROTATESHIP ".\\SOUND\\TURNFISH.WAV"
|
|
#define WAV_MYTURN1 ".\\SOUND\\IGO1.WAV"
|
|
#define WAV_MYTURN2 ".\\SOUND\\IGO2.WAV"
|
|
#define WAV_YOURTURN1 ".\\SOUND\\YOUGO1.WAV"
|
|
#define WAV_YOURTURN2 ".\\SOUND\\YOUGO2.WAV"
|
|
#define WAV_SHOOT ".\\SOUND\\SHOOT.WAV"
|
|
#define WAV_YOUMISS ".\\SOUND\\MISS.WAV"
|
|
#define WAV_YOUHIT ".\\SOUND\\HIT.WAV"
|
|
#define WAV_BADSINK1 ".\\SOUND\\SANK1.WAV"
|
|
#define WAV_BADSINK2 ".\\SOUND\\SANK2.WAV"
|
|
#define WAV_BADSINK3 ".\\SOUND\\SANK3.WAV"
|
|
#define WAV_BADSINK4 ".\\SOUND\\SANK4.WAV"
|
|
#define WAV_BADSINK5 ".\\SOUND\\SANK5.WAV"
|
|
#define WAV_BADSINK6 ".\\SOUND\\SANK6.WAV"
|
|
#define WAV_BADSINK7 ".\\SOUND\\SANK7.WAV"
|
|
#define WAV_BADSINK8 ".\\SOUND\\SANK8.WAV"
|
|
#define WAV_YOUSINK ".\\SOUND\\SINKFISH.WAV"
|
|
#define WAV_YOUWIN ".\\SOUND\\FANFARE2.WAV"
|
|
#define WAV_GAMEOVER ".\\SOUND\\SOSORRY.WAV"
|
|
#define WAV_INVALID ".\\SOUND\\INVALID.WAV"
|
|
#define WAV_NARRATION ".\\SOUND\\BFISH.WAV" // Rules wav file
|
|
|
|
#define NUM_SINK_WAVS 8 // Number of "You sank my.." sounds
|
|
#define NUM_TURN_WAVS 2
|
|
#define VOICE_CUTOFF 2
|
|
|
|
//
|
|
// Audio easter eggs
|
|
//
|
|
#define WAV_WINDOW ".\\SOUND\\WINDOW.WAV"
|
|
#define WAV_TRAWLER ".\\SOUND\\FOGHORN.WAV"
|
|
#define WAV_ROWBOAT ".\\SOUND\\ROWBOAT.WAV"
|
|
#define WAV_SAILBOAT ".\\SOUND\\ANCHORS.WAV"
|
|
//
|
|
// Audio easter egg locations
|
|
//
|
|
#define WINDOW_X 70
|
|
#define WINDOW_Y 23
|
|
#define WINDOW_DX 41
|
|
#define WINDOW_DY 49
|
|
|
|
#define TRAWLER_X 156
|
|
#define TRAWLER_Y 23
|
|
#define TRAWLER_DX 138
|
|
#define TRAWLER_DY 93
|
|
|
|
#define ROWBOAT_X 537
|
|
#define ROWBOAT_Y 135
|
|
#define ROWBOAT_DX 87
|
|
#define ROWBOAT_DY 40
|
|
|
|
#define SAILBOAT_X 323
|
|
#define SAILBOAT_Y 23
|
|
#define SAILBOAT_DX 74
|
|
#define SAILBOAT_DY 71
|
|
|
|
#define OCTOPUS_X 233
|
|
#define OCTOPUS_Y 95
|
|
|
|
#define EMPTY 0x00
|
|
#define SHOT 0x01
|
|
|
|
#define FISH0 0
|
|
#define FISH1 1
|
|
#define FISH2 2
|
|
#define FISH3 3
|
|
|
|
#define NONE 0x50
|
|
|
|
#define SEARCH_FACTOR 5
|
|
|
|
#define DIFF_WIMPY 0
|
|
#define DIFF_AVERAGE 1
|
|
#define DIFF_HEFTY 2
|
|
|
|
#define FISHBIN_X 92
|
|
#define FISHBIN_Y 166
|
|
|
|
#define RGRID_MIN_X 321
|
|
#define RGRID_MIN_Y 202
|
|
#define RGRID_MAX_X 598
|
|
#define RGRID_MAX_Y 442
|
|
|
|
|
|
STATIC FISH gFishSizes[MAX_FISH] = {
|
|
{{{0, 0}, {0, 1}, {NONE, NONE}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 1 * 2},
|
|
{{{0, 1}, {0, 0}, {0, 2}, {NONE, NONE}, {0, 0}, {0, 0}, {0, 0}}, 1 * 3},
|
|
{{{0, 0}, {0, 1}, {1, 0}, {1, 1}, {NONE, NONE}, {0, 0}, {0, 0}}, 2 * 2},
|
|
{{{0, 1}, {1, 1}, {0, 0}, {1, 0}, {0, 2}, {1, 2}, {NONE, NONE}}, 2 * 3}
|
|
};
|
|
|
|
STATIC POINT ptHarpoons[MAX_TURNS] = {
|
|
{460, 153},
|
|
{450, 153},
|
|
{440, 153},
|
|
{430, 153}
|
|
};
|
|
|
|
|
|
STATIC POINT ptFishBin[MAX_FISH] = {
|
|
{FISHBIN_X, FISHBIN_Y},
|
|
{FISHBIN_X + 48, FISHBIN_Y},
|
|
{FISHBIN_X + 136, FISHBIN_Y - 25},
|
|
{FISHBIN_X + 184, FISHBIN_Y},
|
|
};
|
|
|
|
STATIC POINT ptFishHooks[MAX_FISH] = {
|
|
{430, GAME_TOP_BORDER_WIDTH},
|
|
{470, GAME_TOP_BORDER_WIDTH},
|
|
{505, GAME_TOP_BORDER_WIDTH},
|
|
{550, GAME_TOP_BORDER_WIDTH},
|
|
};
|
|
|
|
STATIC const char *pszFishSound[MAX_FISH] = {
|
|
WAV_BADSINK8,
|
|
WAV_BADSINK4,
|
|
WAV_BADSINK7,
|
|
WAV_BADSINK1
|
|
};
|
|
|
|
|
|
// Local Prototypes
|
|
//
|
|
void CALLBACK GetGameParams(CWnd *);
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
CPalette *pGamePalette;
|
|
const char *INI_SECTION = "BattleFish";
|
|
LPGAMESTRUCT pGameParams;
|
|
|
|
extern HWND ghParentWnd;
|
|
|
|
|
|
// these arrays to be filled with the values of the grid screen coordinates
|
|
//
|
|
POINT gLeftGrid[GRID_ROWS][GRID_COLS] = {
|
|
|
|
{{82, 203}, {110, 203}, {138, 203}, {168, 203}, {196, 203}, {224, 203}, {252, 203}, {281, 203}},
|
|
{{77, 230}, {106, 230}, {135, 230}, {163, 230}, {193, 230}, {222, 230}, {251, 230}, {280, 230}},
|
|
{{72, 257}, {102, 257}, {131, 257}, {160, 257}, {190, 257}, {220, 257}, {249, 257}, {279, 257}},
|
|
{{67, 284}, { 97, 284}, {127, 284}, {156, 284}, {187, 284}, {218, 284}, {248, 284}, {278, 284}},
|
|
{{60, 315}, { 91, 315}, {122, 315}, {153, 315}, {184, 315}, {215, 315}, {246, 315}, {277, 315}},
|
|
{{52, 346}, { 86, 346}, {118, 346}, {149, 346}, {181, 346}, {212, 346}, {244, 346}, {276, 346}},
|
|
{{48, 381}, { 80, 381}, {113, 381}, {145, 381}, {178, 381}, {213, 381}, {245, 381}, {278, 381}},
|
|
{{41, 414}, { 74, 414}, {108, 414}, {141, 414}, {174, 414}, {208, 414}, {241, 414}, {275, 414}},
|
|
|
|
/*{{54, 344}, { 86, 344}, {118, 344}, {149, 344}, {181, 344}, {212, 344}, {244, 344}, {276, 344}},
|
|
{{48, 376}, { 80, 376}, {113, 376}, {145, 376}, {177, 376}, {210, 376}, {242, 376}, {275, 376}},
|
|
{{41, 410}, { 74, 410}, {108, 410}, {141, 410}, {174, 410}, {207, 410}, {240, 410}, {274, 410}},*/
|
|
};
|
|
|
|
POINT gRightGrid[GRID_ROWS][GRID_COLS] = {
|
|
|
|
{{324, 203}, {353, 203}, {381, 203}, {410, 203}, {439, 203}, {467, 203}, {496, 203}, {524, 203}},
|
|
{{325, 230}, {354, 230}, {383, 230}, {412, 230}, {442, 230}, {470, 230}, {499, 230}, {529, 230}},
|
|
{{325, 257}, {355, 257}, {385, 257}, {414, 257}, {444, 257}, {474, 257}, {503, 257}, {533, 257}},
|
|
{{325, 284}, {356, 284}, {386, 284}, {417, 284}, {447, 284}, {478, 284}, {507, 284}, {538, 284}},
|
|
{{325, 314}, {357, 314}, {388, 314}, {419, 314}, {450, 314}, {480, 314}, {512, 314}, {543, 314}},
|
|
{{326, 344}, {358, 344}, {390, 344}, {422, 344}, {453, 344}, {485, 344}, {516, 344}, {548, 344}},
|
|
{{326, 376}, {359, 376}, {392, 376}, {424, 376}, {457, 376}, {489, 376}, {521, 376}, {553, 376}},
|
|
{{327, 410}, {360, 410}, {394, 410}, {427, 410}, {460, 410}, {493, 410}, {525, 410}, {560, 410}},
|
|
};
|
|
|
|
/*****************************************************************
|
|
*
|
|
* CBFishWindow
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Constructor for CBFishWindow
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
CBFishWindow::CBFishWindow() {
|
|
CString WndClass;
|
|
CRect tmpRect;
|
|
CDC *pDC;
|
|
CDibDoc *pDibDoc;
|
|
ERROR_CODE errCode;
|
|
bool bSuccess;
|
|
|
|
// assume no error
|
|
errCode = ERR_NONE;
|
|
|
|
// Initialize members
|
|
//
|
|
m_pGamePalette = nullptr;
|
|
m_bPause = false;
|
|
m_bGameActive = false;
|
|
m_bMovingFish = false;
|
|
m_bUserEditMode = false;
|
|
m_bInMenu = false;
|
|
m_pMasterHit = nullptr;
|
|
m_pMasterMiss = nullptr;
|
|
m_pDragFish = nullptr;
|
|
m_pTxtClickHere = nullptr;
|
|
m_pScrollSprite = nullptr;
|
|
m_pSoundTrack = nullptr; // Game theme song
|
|
|
|
// make sure score is initially zero
|
|
pGameParams->lScore = 0;
|
|
|
|
// Init the fish sprites
|
|
memset(m_pFish, 0, sizeof(CSprite *) * MAX_FISH);
|
|
memset(m_pEnemyFish, 0, sizeof(CSprite *) * MAX_FISH);
|
|
|
|
// Init the player grids
|
|
//
|
|
memset(m_nUserGrid, EMPTY, sizeof(byte) * GRID_ROWS * GRID_COLS);
|
|
memset(m_nEnemyGrid, EMPTY, sizeof(byte) * GRID_ROWS * GRID_COLS);
|
|
|
|
// Set the coordinates for the "Start New Game" button
|
|
//
|
|
m_rNewGameButton.SetRect(15, 4, 233, 20);
|
|
|
|
// Set the coordinates for the "End Placement Button"
|
|
m_rEndPlacement.SetRect(380, 157, 531, 200);
|
|
|
|
// Define a special window class which traps double-clicks, is byte aligned
|
|
// to maximize BITBLT performance, and creates "owned" DCs rather than sharing
|
|
// the five system defined DCs which are not guaranteed to be available;
|
|
// this adds a bit to our app size but avoids hangs/freezes/lockups.
|
|
WndClass = AfxRegisterWndClass(CS_DBLCLKS | CS_BYTEALIGNWINDOW | CS_OWNDC, nullptr, nullptr, nullptr);
|
|
|
|
// can't play this game if the background art is not available
|
|
//
|
|
if (FileExists(MINI_GAME_MAP)) {
|
|
|
|
// Acquire the shared palette for our game from the splash screen art
|
|
//
|
|
if ((pDibDoc = new CDibDoc()) != nullptr) {
|
|
if (pDibDoc->OpenDocument(MINI_GAME_MAP) != false)
|
|
pGamePalette = m_pGamePalette = pDibDoc->DetachPalette();
|
|
else
|
|
errCode = ERR_UNKNOWN;
|
|
delete pDibDoc;
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
} else {
|
|
errCode = ERR_FFIND;
|
|
}
|
|
|
|
// Center our window on the screen
|
|
//
|
|
tmpRect.SetRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
|
|
#ifndef DEBUG
|
|
if ((pDC = GetDC()) != nullptr) {
|
|
tmpRect.left = (pDC->GetDeviceCaps(HORZRES) - GAME_WIDTH) >> 1;
|
|
tmpRect.top = (pDC->GetDeviceCaps(VERTRES) - GAME_HEIGHT) >> 1;
|
|
tmpRect.right = tmpRect.left + GAME_WIDTH;
|
|
tmpRect.bottom = tmpRect.top + GAME_HEIGHT;
|
|
ReleaseDC(pDC);
|
|
} else {
|
|
errCode = ERR_UNKNOWN;
|
|
}
|
|
#endif
|
|
|
|
// 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 -- Battlefish", WS_POPUP, tmpRect, nullptr, 0);
|
|
|
|
BeginWaitCursor();
|
|
ShowWindow(SW_SHOWNORMAL);
|
|
PaintScreen();
|
|
EndWaitCursor();
|
|
|
|
// only continue if there was no error
|
|
//
|
|
if (errCode == ERR_NONE) {
|
|
|
|
if ((m_pScrollSprite = new CSprite) != nullptr) {
|
|
|
|
m_pScrollSprite->SharePalette(m_pGamePalette);
|
|
|
|
if ((pDC = GetDC()) != nullptr) {
|
|
bSuccess = m_pScrollSprite->LoadResourceSprite(pDC, IDB_SCROLBTN);
|
|
|
|
if (!bSuccess)
|
|
errCode = ERR_UNKNOWN;
|
|
ReleaseDC(pDC);
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
m_pScrollSprite->SetMasked(true);
|
|
m_pScrollSprite->SetMobile(true);
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
|
|
// only continue if there was no error
|
|
//
|
|
if (errCode == ERR_NONE) {
|
|
// Start the BFish soundtrack
|
|
if (pGameParams->bMusicEnabled) {
|
|
if ((m_pSoundTrack = new CSound) != nullptr) {
|
|
m_pSoundTrack->initialize(this, MID_SOUNDTRACK, SOUND_MIDI | SOUND_LOOP | SOUND_DONT_LOOP_TO_END);
|
|
m_pSoundTrack->midiLoopPlaySegment(2470, 32160, 0, FMT_MILLISEC);
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
}
|
|
|
|
// seed the random number generator
|
|
//srand((unsigned)time(nullptr));
|
|
if (errCode == ERR_NONE) {
|
|
errCode = LoadMasterSprites();
|
|
|
|
// if we are not playing from the metagame
|
|
if (!pGameParams->bPlayingMetagame) {
|
|
// Automatically bring up the main menu
|
|
PostMessage(WM_COMMAND, IDC_MENU, BN_CLICKED);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
HandleError(errCode);
|
|
}
|
|
|
|
/*****************************************************************
|
|
*
|
|
* HandleError
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Handles Fatal error by show a message box, and posting WM_CLOSE
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* ERROR_CODE = Error return code to indicate type of fatal error
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::HandleError(ERROR_CODE errCode) {
|
|
//
|
|
// Exit this application on fatal errors
|
|
//
|
|
if (errCode != ERR_NONE) {
|
|
|
|
// pause the current game (if any)
|
|
GamePause();
|
|
|
|
// Display Error Message to the user
|
|
MessageBox(errList[errCode], "Fatal Error!", MB_OK | MB_ICONSTOP);
|
|
|
|
// Force this application to terminate
|
|
PostMessage(WM_CLOSE, 0, 0);
|
|
|
|
// Don't allow a repaint (remove all WM_PAINT messages)
|
|
ValidateRect(nullptr);
|
|
}
|
|
}
|
|
|
|
ERROR_CODE CBFishWindow::LoadMasterSprites() {
|
|
CDC *pDC;
|
|
int i;
|
|
ERROR_CODE errCode;
|
|
|
|
// assume no error
|
|
errCode = ERR_NONE;
|
|
|
|
if ((pDC = GetDC()) != nullptr) {
|
|
|
|
if ((m_pMasterHit = new CSprite) != nullptr) {
|
|
|
|
// this BMP uses the same palette as entire game
|
|
//
|
|
if (m_pMasterHit->SharePalette(m_pGamePalette) != false) {
|
|
|
|
if (m_pMasterHit->LoadResourceSprite(pDC, IDB_HIT) != false) {
|
|
|
|
m_pMasterHit->SetMasked(true);
|
|
m_pMasterHit->SetMobile(true);
|
|
m_pMasterHit->SetZOrder(SPRITE_TOPMOST);
|
|
} else {
|
|
errCode = ERR_UNKNOWN;
|
|
}
|
|
} else {
|
|
errCode = ERR_UNKNOWN;
|
|
}
|
|
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
|
|
if (errCode == ERR_NONE) {
|
|
|
|
if ((m_pMasterMiss = new CSprite) != nullptr) {
|
|
|
|
// this BMP uses the same palette as entire game
|
|
//
|
|
if (m_pMasterMiss->SharePalette(m_pGamePalette) != false) {
|
|
|
|
if (m_pMasterMiss->LoadResourceSprite(pDC, IDB_MISS) != false) {
|
|
|
|
m_pMasterMiss->SetMasked(true);
|
|
m_pMasterMiss->SetMobile(true);
|
|
m_pMasterMiss->SetZOrder(SPRITE_TOPMOST);
|
|
} else {
|
|
errCode = ERR_UNKNOWN;
|
|
}
|
|
} else {
|
|
errCode = ERR_UNKNOWN;
|
|
}
|
|
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (errCode == ERR_NONE) {
|
|
|
|
if ((m_pMasterHarpoon = new CSprite) != nullptr) {
|
|
|
|
// this BMP uses the same palette as entire game
|
|
//
|
|
if (m_pMasterHarpoon->SharePalette(m_pGamePalette) != false) {
|
|
|
|
if (m_pMasterHarpoon->LoadResourceSprite(pDC, IDB_HARPOON) != false) {
|
|
|
|
m_pMasterHarpoon->SetMasked(true);
|
|
m_pMasterHarpoon->SetMobile(true);
|
|
} else {
|
|
errCode = ERR_UNKNOWN;
|
|
}
|
|
} else {
|
|
errCode = ERR_UNKNOWN;
|
|
}
|
|
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (errCode == ERR_NONE) {
|
|
|
|
if ((m_pOctopus = new CSprite) != nullptr) {
|
|
|
|
// this BMP uses the same palette as entire game
|
|
//
|
|
if (m_pOctopus->SharePalette(m_pGamePalette) != false) {
|
|
|
|
if (m_pOctopus->LoadResourceSprite(pDC, IDB_OCTOPUS) != false) {
|
|
m_pOctopus->SetZOrder(SPRITE_BACKGROUND);
|
|
m_pOctopus->SetMasked(false);
|
|
m_pOctopus->SetMobile(false);
|
|
m_pOctopus->SetPosition(OCTOPUS_X, OCTOPUS_Y);
|
|
} else {
|
|
errCode = ERR_UNKNOWN;
|
|
}
|
|
} else {
|
|
errCode = ERR_UNKNOWN;
|
|
}
|
|
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (errCode == ERR_NONE) {
|
|
|
|
for (i = 0; i < MAX_FISH; i++) {
|
|
|
|
if ((m_pFish[i] = new CSprite) != nullptr) {
|
|
|
|
// attach sprite to the Game Palette
|
|
//
|
|
if (m_pFish[i]->SharePalette(m_pGamePalette) != false) {
|
|
|
|
if (m_pFish[i]->LoadResourceSprite(pDC, IDB_FISH + i) != false) {
|
|
|
|
m_pFish[i]->SetTypeCode(false);
|
|
m_pFish[i]->SetMasked(true);
|
|
m_pFish[i]->SetMobile(true);
|
|
m_pFish[i]->SetZOrder(SPRITE_BACKGROUND);
|
|
//m_pFish[i]->SetPosition(ptFishBin[i]);
|
|
//m_pFish[i]->LinkSprite();
|
|
} else {
|
|
errCode = ERR_UNKNOWN;
|
|
}
|
|
} else {
|
|
errCode = ERR_UNKNOWN;
|
|
}
|
|
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (errCode == ERR_NONE) {
|
|
|
|
for (i = 0; i < MAX_FISH; i++) {
|
|
|
|
if ((m_pEnemyFish[i] = new CSprite) != nullptr) {
|
|
|
|
// attach good guy to the Game Palette
|
|
//
|
|
if (m_pEnemyFish[i]->SharePalette(m_pGamePalette) != false) {
|
|
|
|
if (m_pEnemyFish[i]->LoadResourceSprite(pDC, IDB_HOOK + i) != false) {
|
|
|
|
// true if linked into chain, false otherwise
|
|
m_pEnemyFish[i]->SetTypeCode(false);
|
|
|
|
m_pEnemyFish[i]->SetMasked(true);
|
|
m_pEnemyFish[i]->SetMobile(true);
|
|
m_pEnemyFish[i]->SetPosition(ptFishHooks[i]);
|
|
} else {
|
|
errCode = ERR_UNKNOWN;
|
|
}
|
|
} else {
|
|
errCode = ERR_UNKNOWN;
|
|
}
|
|
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
ReleaseDC(pDC);
|
|
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
|
|
return errCode;
|
|
}
|
|
|
|
|
|
void CBFishWindow::ReleaseMasterSprites() {
|
|
int i;
|
|
|
|
// free the hooked fish, and the users fish
|
|
//
|
|
for (i = 0; i < MAX_FISH; i++) {
|
|
|
|
if (m_pEnemyFish[i] != nullptr) {
|
|
delete m_pEnemyFish[i];
|
|
m_pEnemyFish[i] = nullptr;
|
|
}
|
|
|
|
if (m_pFish[i] != nullptr) {
|
|
delete m_pFish[i];
|
|
m_pFish[i] = nullptr;
|
|
}
|
|
}
|
|
|
|
// free the octopus sprite
|
|
//
|
|
assert(m_pOctopus != nullptr);
|
|
if (m_pOctopus != nullptr) {
|
|
delete m_pOctopus;
|
|
m_pOctopus = nullptr;
|
|
}
|
|
|
|
// free the master turn-harpoon sprite
|
|
//
|
|
assert(m_pMasterHarpoon != nullptr);
|
|
if (m_pMasterHarpoon != nullptr) {
|
|
delete m_pMasterHarpoon;
|
|
m_pMasterHarpoon = nullptr;
|
|
}
|
|
|
|
// free the master shoot-miss sprite
|
|
//
|
|
assert(m_pMasterMiss != nullptr);
|
|
if (m_pMasterMiss != nullptr) {
|
|
delete m_pMasterMiss;
|
|
m_pMasterMiss = nullptr;
|
|
}
|
|
|
|
// free the master shoot-hit sprite
|
|
//
|
|
assert(m_pMasterHit != nullptr);
|
|
if (m_pMasterHit != nullptr) {
|
|
delete m_pMasterHit;
|
|
m_pMasterHit = nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
*
|
|
* OnPaint
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Handles WM_PAINT messages
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* None
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::OnPaint() {
|
|
PAINTSTRUCT lpPaint;
|
|
|
|
Invalidate(false);
|
|
BeginPaint(&lpPaint);
|
|
PaintScreen();
|
|
EndPaint(&lpPaint);
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
*
|
|
* PaintScreen
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Repaints background art, sprites, and text fields
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* None
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::PaintScreen() {
|
|
CDibDoc myDoc;
|
|
CRect rcDest;
|
|
CRect rcDIB;
|
|
HDIB hDIB;
|
|
CDC *pDC;
|
|
ERROR_CODE errCode;
|
|
|
|
// assume no error
|
|
errCode = ERR_NONE;
|
|
|
|
//
|
|
// Paint the background art and update any sprites
|
|
//
|
|
if (FileExists(MINI_GAME_MAP)) {
|
|
|
|
myDoc.OpenDocument(MINI_GAME_MAP);
|
|
hDIB = myDoc.GetHDIB();
|
|
|
|
pDC = GetDC();
|
|
assert(pDC != nullptr);
|
|
|
|
if (pDC != nullptr) {
|
|
|
|
if (hDIB && (m_pGamePalette != nullptr)) {
|
|
|
|
GetClientRect(rcDest);
|
|
|
|
rcDIB.top = rcDIB.left = 0;
|
|
rcDIB.right = (int) DIBWidth(hDIB);
|
|
rcDIB.bottom = (int) DIBHeight(hDIB);
|
|
|
|
PaintDIB(pDC->m_hDC, &rcDest, hDIB, &rcDIB, m_pGamePalette);
|
|
}
|
|
|
|
if (!m_bInMenu && (m_pScrollSprite != nullptr)) {
|
|
m_pScrollSprite->PaintSprite(pDC, SCROLL_BUTTON_X, SCROLL_BUTTON_Y);
|
|
}
|
|
|
|
// repaint any on-screen sprites
|
|
//
|
|
errCode = RepaintSpriteList(pDC);
|
|
|
|
// redisplay the "click here" text
|
|
if (m_pTxtClickHere != nullptr) {
|
|
m_pTxtClickHere->DisplayString(pDC, "Click here when done", FONT_SIZE, TEXT_NORMAL, RGB(0, 0, 0));
|
|
}
|
|
|
|
ReleaseDC(pDC);
|
|
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
|
|
} else {
|
|
errCode = ERR_FFIND;
|
|
}
|
|
|
|
HandleError(errCode);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* RepaintSpriteList -
|
|
*
|
|
* DESCRIPTION: Repaint all Linked Sprites
|
|
*
|
|
*
|
|
* SAMPLE USAGE:
|
|
* errCode = RepaintSpriteList(pDC);
|
|
* CDC *pDC; pointer to current device context
|
|
*
|
|
* RETURNS: ERROR_CODE = error return code
|
|
*
|
|
*****************************************************************************/
|
|
ERROR_CODE CBFishWindow::RepaintSpriteList(CDC *pDC) {
|
|
CSprite *pSprite;
|
|
ERROR_CODE errCode;
|
|
|
|
// assume no error
|
|
errCode = ERR_NONE;
|
|
|
|
// can't use a null pointer
|
|
assert(pDC != nullptr);
|
|
|
|
//
|
|
// Paint each sprite
|
|
//
|
|
pSprite = CSprite::GetSpriteChain();
|
|
while (pSprite) {
|
|
|
|
pSprite->ClearBackground();
|
|
|
|
pSprite = pSprite->GetNextSprite();
|
|
}
|
|
|
|
pSprite = CSprite::GetSpriteChain();
|
|
while (pSprite) {
|
|
|
|
pSprite->RefreshSprite(pDC);
|
|
|
|
pSprite = pSprite->GetNextSprite();
|
|
}
|
|
|
|
return errCode;
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
*
|
|
* OnCommand
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Handles WM_COMMAND messages
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* WPARAM = uint16 parameter for this message
|
|
* LPARAM = long parameter for this message
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* bool = true if message was handled
|
|
*
|
|
****************************************************************/
|
|
bool CBFishWindow::OnCommand(WPARAM wParam, LPARAM lParam) {
|
|
CMainMenu COptionsWind((CWnd *)this,
|
|
m_pGamePalette,
|
|
(pGameParams->bPlayingMetagame ? (NO_NEWGAME | NO_OPTIONS) : 0) | (m_bGameActive || m_bUserEditMode ? 0 : NO_RETURN),
|
|
GetGameParams, "bfish.txt", (pGameParams->bSoundEffectsEnabled ? WAV_NARRATION : nullptr), pGameParams);
|
|
CDC *pDC;
|
|
//CSound *pSound;
|
|
int nPick = 0;
|
|
|
|
if (HIWORD(lParam) == BN_CLICKED) {
|
|
switch (wParam) {
|
|
|
|
//
|
|
// must bring up our menu of controls
|
|
//
|
|
case IDC_MENU:
|
|
|
|
GamePause();
|
|
|
|
m_bInMenu = true;
|
|
|
|
if ((pDC = GetDC()) != nullptr) {
|
|
m_pScrollSprite->EraseSprite(pDC);
|
|
}
|
|
|
|
CSound::waitWaveSounds();
|
|
|
|
// Get users choice from command menu
|
|
//
|
|
switch (COptionsWind.DoModal()) {
|
|
|
|
// User has chosen to play a new game
|
|
//
|
|
case IDC_OPTIONS_NEWGAME:
|
|
PlayGame();
|
|
break;
|
|
|
|
// User has chosen to quit this mini-game
|
|
//
|
|
case IDC_OPTIONS_QUIT:
|
|
PostMessage(WM_CLOSE, 0, 0);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (pDC != nullptr) {
|
|
m_pScrollSprite->PaintSprite(pDC, SCROLL_BUTTON_X, SCROLL_BUTTON_Y);
|
|
ReleaseDC(pDC);
|
|
}
|
|
|
|
m_bInMenu = false;
|
|
|
|
if (!pGameParams->bMusicEnabled && (m_pSoundTrack != nullptr)) {
|
|
|
|
m_pSoundTrack->stop();
|
|
delete m_pSoundTrack;
|
|
m_pSoundTrack = nullptr;
|
|
|
|
} else if (pGameParams->bMusicEnabled && (m_pSoundTrack == nullptr)) {
|
|
|
|
if ((m_pSoundTrack = new CSound) != nullptr) {
|
|
m_pSoundTrack->initialize(this, MID_SOUNDTRACK, SOUND_MIDI | SOUND_LOOP | SOUND_DONT_LOOP_TO_END);
|
|
m_pSoundTrack->midiLoopPlaySegment(2470, 32160, 0, FMT_MILLISEC);
|
|
}
|
|
}
|
|
|
|
GameResume();
|
|
|
|
return true;
|
|
|
|
case COMPUTERS_TURN:
|
|
|
|
if ((pDC = GetDC()) != nullptr) {
|
|
if (m_pOctopus != nullptr) {
|
|
m_pOctopus->LinkSprite();
|
|
m_pOctopus->PaintSprite(pDC, OCTOPUS_X, OCTOPUS_Y);
|
|
AfxGetApp()->pause();
|
|
}
|
|
ReleaseDC(pDC);
|
|
}
|
|
|
|
if (pGameParams->bSoundEffectsEnabled &&
|
|
((m_nUserFish > VOICE_CUTOFF) && (m_nEnemyFish > VOICE_CUTOFF))) {
|
|
nPick = brand() % NUM_TURN_WAVS;
|
|
if (nPick == 0) {
|
|
#if CSOUND
|
|
pSound = new CSound((CWnd *)this, WAV_MYTURN1, SOUND_WAVE | SOUND_AUTODELETE);
|
|
#else
|
|
sndPlaySound(WAV_MYTURN1, SND_SYNC);
|
|
#endif
|
|
} else {
|
|
#if CSOUND
|
|
pSound = new CSound((CWnd *)this, WAV_MYTURN2, SOUND_WAVE | SOUND_AUTODELETE);
|
|
#else
|
|
sndPlaySound(WAV_MYTURN2, SND_SYNC);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
BeginWaitCursor();
|
|
|
|
m_nTurns = m_nEnemyFish;
|
|
while (m_nTurns-- > 0) {
|
|
//CSound::HandleMessages();
|
|
ComputersTurn();
|
|
}
|
|
|
|
m_nTurns = m_nUserFish;
|
|
m_bUsersTurn = true;
|
|
|
|
FlushInputEvents();
|
|
|
|
EndWaitCursor();
|
|
|
|
if (m_bGameActive) {
|
|
|
|
if (pGameParams->bSoundEffectsEnabled &&
|
|
((m_nUserFish > VOICE_CUTOFF) && (m_nEnemyFish > VOICE_CUTOFF))) {
|
|
nPick = brand() % NUM_TURN_WAVS;
|
|
if (nPick == 0) {
|
|
#if CSOUND
|
|
pSound = new CSound((CWnd *)this, WAV_YOURTURN1, SOUND_WAVE | SOUND_AUTODELETE);
|
|
#else
|
|
sndPlaySound(WAV_YOURTURN1, SND_SYNC);
|
|
#endif
|
|
} else {
|
|
#if CSOUND
|
|
pSound = new CSound((CWnd *)this, WAV_YOURTURN2, SOUND_WAVE | SOUND_AUTODELETE);
|
|
#else
|
|
sndPlaySound(WAV_YOURTURN2, SND_SYNC);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
PlaceTurnHarpoons();
|
|
|
|
if ((pDC = GetDC()) != nullptr) {
|
|
if (m_pOctopus != nullptr) {
|
|
m_pOctopus->EraseSprite(pDC);
|
|
m_pOctopus->UnlinkSprite();
|
|
}
|
|
ReleaseDC(pDC);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CBFishWindow::PlaceTurnHarpoons() {
|
|
CSprite *pSprite;
|
|
CDC *pDC;
|
|
int i;
|
|
ERROR_CODE errCode;
|
|
|
|
// assume no error
|
|
errCode = ERR_NONE;
|
|
|
|
if ((pDC = GetDC()) != nullptr) {
|
|
|
|
for (i = 0; i < m_nTurns; i++) {
|
|
|
|
// create a dup of the master harpoon
|
|
//
|
|
if ((pSprite = m_pMasterHarpoon->DuplicateSprite(pDC)) != nullptr) {
|
|
pSprite->LinkSprite();
|
|
pSprite->PaintSprite(pDC, ptHarpoons[i]);
|
|
m_pHarpoons[i] = pSprite;
|
|
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
break;
|
|
}
|
|
}
|
|
ReleaseDC(pDC);
|
|
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
|
|
HandleError(errCode);
|
|
}
|
|
|
|
void CBFishWindow::RemoveTurnHarpoon() {
|
|
assert(m_nTurns >= 0 && m_nTurns < MAX_TURNS);
|
|
assert(m_pHarpoons[m_nTurns] != nullptr);
|
|
|
|
DeleteSprite(m_pHarpoons[m_nTurns]);
|
|
m_pHarpoons[m_nTurns] = nullptr;
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
*
|
|
* GamePause
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Pauses the current game (if any)
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* None
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::GamePause() {
|
|
m_bPause = true;
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
*
|
|
* GameResume
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Resumes the current game (if any)
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* None
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::GameResume() {
|
|
m_bPause = false;
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
*
|
|
* PlayGame
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Stops any active game, resets all game parameters, and starts new game
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* None
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::PlayGame() {
|
|
CRect rTmpRect;
|
|
CDC *pDC;
|
|
int i;
|
|
ERROR_CODE errCode;
|
|
|
|
// assume no error
|
|
errCode = ERR_NONE;
|
|
|
|
// load the .INI settings
|
|
//
|
|
LoadIniSettings();
|
|
|
|
// reset all game parameters
|
|
//
|
|
GameReset();
|
|
|
|
//
|
|
// Start game
|
|
//
|
|
|
|
// show fish in the bin
|
|
//
|
|
if ((pDC = GetDC()) != nullptr) {
|
|
|
|
//RepaintSpriteList(pDC);
|
|
for (i = 0; i < MAX_FISH; i++) {
|
|
m_pFish[i]->LinkSprite();
|
|
m_pFish[i]->PaintSprite(pDC, ptFishBin[i]);
|
|
}
|
|
|
|
// show the "Click here when you are finished placing your fish" text
|
|
//
|
|
if (m_pTxtClickHere != nullptr) {
|
|
m_pTxtClickHere->RestoreBackground(pDC);
|
|
delete m_pTxtClickHere;
|
|
m_pTxtClickHere = nullptr;
|
|
}
|
|
rTmpRect.SetRect(380, 180, 510, 200);
|
|
if ((m_pTxtClickHere = new CText) != nullptr) {
|
|
m_pTxtClickHere->SetupText(pDC, m_pGamePalette, &rTmpRect, JUSTIFY_LEFT);
|
|
m_pTxtClickHere->DisplayString(pDC, "Click here when done", FONT_SIZE, TEXT_NORMAL, RGB(0, 0, 0));
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
|
|
ReleaseDC(pDC);
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
|
|
// computer places pieces
|
|
PlaceEnemyFish();
|
|
|
|
// User places pieces (fish)
|
|
PlaceUserFish();
|
|
|
|
HandleError(errCode);
|
|
}
|
|
|
|
|
|
void CBFishWindow::PlaceUserFish() {
|
|
//
|
|
// Initiate User-Edit-Mode (Allow user to drag and drop fish to grid)
|
|
//
|
|
m_bUserEditMode = true;
|
|
}
|
|
|
|
|
|
void CBFishWindow::PlaceEnemyFish() {
|
|
int i, j, k;
|
|
int row, col, rowTmp, colTmp;
|
|
bool bFound;
|
|
|
|
// For each fish, randomly select a location in the grid (rotate if neccessary)
|
|
//
|
|
for (i = 0; i < MAX_FISH; i++) {
|
|
|
|
bFound = false;
|
|
do {
|
|
|
|
// select random starting square
|
|
//
|
|
row = brand() % GRID_ROWS;
|
|
col = brand() % GRID_COLS;
|
|
|
|
// make a copy of this fish
|
|
memcpy(&m_aEnemyFishInfo[i], &gFishSizes[i], sizeof(FISH));
|
|
|
|
// rotate some of the fish
|
|
//
|
|
if (brand() & 1) {
|
|
for (k = 0; k < MAX_FISH_SIZE; k++) {
|
|
m_aEnemyFishInfo[i].nLoc[k].x = gFishSizes[i].nLoc[k].y;
|
|
m_aEnemyFishInfo[i].nLoc[k].y = gFishSizes[i].nLoc[k].x;
|
|
}
|
|
}
|
|
|
|
// Try fish at both 0 and 90 degrees
|
|
//
|
|
for (j = 0; j < 2; j++) {
|
|
|
|
// Does the fish fit at this location
|
|
//
|
|
bFound = true;
|
|
for (k = 0; k < MAX_FISH_SIZE; k++) {
|
|
|
|
// if there are no more squares for this fish, then done
|
|
//
|
|
if (m_aEnemyFishInfo[i].nLoc[k].x == NONE)
|
|
break;
|
|
|
|
assert(m_aEnemyFishInfo[i].nLoc[k].y != NONE);
|
|
|
|
rowTmp = (m_aEnemyFishInfo[i].nLoc[k].x += row);
|
|
colTmp = (m_aEnemyFishInfo[i].nLoc[k].y += col);
|
|
|
|
if ((rowTmp >= GRID_ROWS) || (colTmp >= GRID_COLS) || (m_nEnemyGrid[rowTmp][colTmp] != EMPTY)) {
|
|
bFound = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// the fish fit - so set the grid and then go on to next fish
|
|
//
|
|
if (bFound) {
|
|
for (k = 0; k < MAX_FISH_SIZE; k++) {
|
|
|
|
// if there are no more squares for this fish, then done
|
|
//
|
|
if (m_aEnemyFishInfo[i].nLoc[k].x == NONE)
|
|
break;
|
|
|
|
assert(m_aEnemyFishInfo[i].nLoc[k].x < NONE);
|
|
assert(m_aEnemyFishInfo[i].nLoc[k].y < NONE);
|
|
|
|
rowTmp = m_aEnemyFishInfo[i].nLoc[k].x;
|
|
colTmp = m_aEnemyFishInfo[i].nLoc[k].y;
|
|
|
|
m_nEnemyGrid[rowTmp][colTmp] = (byte)IndexToId(i);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Fish didn't fit, so try rotating it by swapping row
|
|
// and column for each square in fish
|
|
//
|
|
for (k = 0; k < MAX_FISH_SIZE; k++) {
|
|
m_aEnemyFishInfo[i].nLoc[k].x = gFishSizes[i].nLoc[k].y;
|
|
m_aEnemyFishInfo[i].nLoc[k].y = gFishSizes[i].nLoc[k].x;
|
|
}
|
|
}
|
|
|
|
} while (!bFound);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************
|
|
*
|
|
* LoadIniSettings
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Loads this game's parameters from .INI file
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* None
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::LoadIniSettings() {
|
|
int nVal;
|
|
|
|
if (pGameParams->bPlayingMetagame) {
|
|
|
|
switch (pGameParams->nSkillLevel) {
|
|
|
|
case SKILLLEVEL_LOW:
|
|
m_nDifficultyLevel = 1;
|
|
m_bUsersTurn = true;
|
|
break;
|
|
|
|
case SKILLLEVEL_MEDIUM:
|
|
m_nDifficultyLevel = 1;
|
|
m_bUsersTurn = false;
|
|
break;
|
|
|
|
case SKILLLEVEL_HIGH:
|
|
m_nDifficultyLevel = 2;
|
|
m_bUsersTurn = false;
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
|
|
// Get the Difficulty level (0..2)
|
|
//
|
|
nVal = GetPrivateProfileInt(INI_SECTION, "DifficultyLevel", DIFF_DEF, INI_FILENAME);
|
|
m_nDifficultyLevel = nVal;
|
|
if (nVal < DIFF_MIN || nVal > DIFF_MAX)
|
|
m_nDifficultyLevel = DIFF_DEF;
|
|
|
|
nVal = GetPrivateProfileInt(INI_SECTION, "UserGoesFirst", 0, INI_FILENAME);
|
|
m_bUsersTurn = false;
|
|
if (nVal != 0)
|
|
m_bUsersTurn = true;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
*
|
|
* SaveIniSettings
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Saves this game's parameters to it's .INI file
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* None
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::SaveIniSettings() {
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
*
|
|
* GameReset
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Resets all game parameters
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* None
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::GameReset() {
|
|
CDC *pDC;
|
|
int i;
|
|
|
|
//fred = 0;
|
|
|
|
if (pGameParams->bSoundEffectsEnabled)
|
|
sndPlaySound(nullptr, SND_ASYNC); // stop all sounds
|
|
|
|
m_bGameActive = false; // there is no active game
|
|
|
|
m_bPause = false; // the game is not paused
|
|
|
|
m_bUserEditMode = false; // user can't edit board
|
|
|
|
m_bMovingFish = false; // user is not moving fish
|
|
|
|
if ((pDC = GetDC()) != nullptr) { // erase all sprites from the screen
|
|
CSprite::EraseSprites(pDC);
|
|
|
|
for (i = 0; i < MAX_FISH; i++) { // remove fish from the sprite chain
|
|
assert(m_pFish[i] != nullptr); // ...so they are not discarded
|
|
m_pFish[i]->UnlinkSprite();
|
|
|
|
assert(m_pEnemyFish[i] != nullptr);
|
|
if (m_pEnemyFish[i]->GetTypeCode()) {
|
|
m_pEnemyFish[i]->UnlinkSprite();
|
|
m_pEnemyFish[i]->SetTypeCode(false);
|
|
}
|
|
|
|
if (m_pFish[i]->GetTypeCode()) { // need to re-rotate this fish
|
|
m_pFish[i]->SetTypeCode(false);
|
|
(void)m_pFish[i]->LoadResourceSprite(pDC, IDB_FISH + i);
|
|
}
|
|
}
|
|
|
|
// make sure we don't delete the octopus yet
|
|
//
|
|
if (m_pOctopus != nullptr) {
|
|
if (m_pOctopus->IsLinked()) {
|
|
m_pOctopus->UnlinkSprite();
|
|
}
|
|
}
|
|
|
|
CSprite::FlushSpriteChain(); // delete all linked sprites
|
|
|
|
for (i = 0; i < MAX_FISH; i++) { // Put the active fish back
|
|
assert(m_pFish[i] != nullptr); // and reset them to bin coordinates
|
|
//m_pFish[i]->SetPosition(ptFishBin[i]);
|
|
//m_pFish[i]->LinkSprite();
|
|
}
|
|
ReleaseDC(pDC);
|
|
}
|
|
|
|
// reset the play grids
|
|
//
|
|
memset(m_nUserGrid, EMPTY, sizeof(byte) * GRID_ROWS * GRID_COLS);
|
|
memset(m_nEnemyGrid, EMPTY, sizeof(byte) * GRID_ROWS * GRID_COLS);
|
|
|
|
// make a copy of the fish sizes
|
|
memcpy(&m_aUserFishInfo, &gFishSizes, sizeof(FISH) * MAX_FISH);
|
|
memcpy(&m_aEnemyFishInfo, &gFishSizes, sizeof(FISH) * MAX_FISH);
|
|
|
|
// reset the turn-harpoons
|
|
memset(m_pHarpoons, 0, sizeof(CSprite *) * MAX_TURNS);
|
|
|
|
m_nUserFish = m_nEnemyFish = MAX_FISH; // set intial number of fish
|
|
|
|
m_nTurns = 1; // User has first turn
|
|
|
|
m_bStillCheck = false; // Computer AI starts fresh
|
|
}
|
|
|
|
|
|
void CBFishWindow::RotateFish(int nFishIndex) {
|
|
CSize size;
|
|
CRect rect, tmpRect;
|
|
POINT point;
|
|
CSprite *pSprite;
|
|
CDC *pDC;
|
|
int nIDB;
|
|
bool bRotated, bPaintFish;
|
|
|
|
// validate the index
|
|
assert((nFishIndex >= 0) && (nFishIndex < MAX_FISH));
|
|
|
|
// must be in User-Edit Mode to rotate a fish
|
|
assert(m_bUserEditMode == true);
|
|
|
|
// game can not yet be active
|
|
assert(m_bGameActive == false);
|
|
|
|
pSprite = m_pFish[nFishIndex];
|
|
|
|
// can't access a null pointer
|
|
assert(pSprite != nullptr);
|
|
|
|
if ((pDC = GetDC()) != nullptr) {
|
|
|
|
// is this fish horizontal or vertical
|
|
//
|
|
bRotated = pSprite->GetTypeCode();
|
|
nIDB = (bRotated ? IDB_FISH : IDB_FISHROT);
|
|
|
|
// get fish size and location
|
|
//
|
|
point = pSprite->GetPosition();
|
|
size = pSprite->GetSize();
|
|
|
|
// Rotate fish during drag'n'drop
|
|
//
|
|
if (m_bMovingFish) {
|
|
|
|
// play the rotate fish sound
|
|
//
|
|
if (pGameParams->bSoundEffectsEnabled)
|
|
sndPlaySound(WAV_ROTATESHIP, SND_ASYNC);
|
|
|
|
// indicate that the fish is rotated
|
|
//
|
|
pSprite->SetTypeCode(!bRotated);
|
|
|
|
// aquire the center of the fish
|
|
point.x += size.cx / 2;
|
|
point.y += size.cy / 2;
|
|
|
|
// erase old fish
|
|
pSprite->EraseSprite(pDC);
|
|
|
|
// load BMP of new rotated fish
|
|
pSprite->LoadResourceSprite(pDC, nIDB + nFishIndex);
|
|
|
|
// re-aquire the upper-left corner of the fish
|
|
//
|
|
size = pSprite->GetSize();
|
|
point.x -= size.cx / 2;
|
|
point.y -= size.cy / 2;
|
|
|
|
// paint the fish
|
|
pSprite->PaintSprite(pDC, point);
|
|
|
|
// must check to make sure we do not rotate this fish onto another
|
|
//
|
|
} else {
|
|
|
|
// get rectangle of fish rotated 90 degrees
|
|
// NOTE: (size.cx and size.cy are switched on purpose)
|
|
//
|
|
rect.SetRect(point.x, point.y, point.x + size.cy, point.y + size.cx);
|
|
|
|
// assume we can rotate
|
|
bPaintFish = true;
|
|
|
|
if (!OkToPlaceFish(nFishIndex, m_pFish[nFishIndex]->GetPosition(), (m_pFish[nFishIndex]->GetTypeCode() ? false : true))) {
|
|
bPaintFish = false;
|
|
}
|
|
|
|
// Ok, it is safe to rotate
|
|
//
|
|
if (bPaintFish) {
|
|
|
|
// play the rotate fish sound
|
|
//
|
|
if (pGameParams->bSoundEffectsEnabled)
|
|
sndPlaySound(WAV_ROTATESHIP, SND_ASYNC);
|
|
|
|
// indicate that the fish is rotated
|
|
//
|
|
pSprite->SetTypeCode(!bRotated);
|
|
|
|
PlaceFish(nFishIndex, pSprite->GetPosition());
|
|
|
|
// erase old image
|
|
pSprite->EraseSprite(pDC);
|
|
|
|
// load new rotated fish
|
|
(void)pSprite->LoadResourceSprite(pDC, nIDB + nFishIndex);
|
|
|
|
// paint him
|
|
pSprite->PaintSprite(pDC, point);
|
|
}
|
|
}
|
|
|
|
ReleaseDC(pDC);
|
|
}
|
|
}
|
|
|
|
void CBFishWindow::AssignFishToGrid(int nFishIndex) {
|
|
CPoint point;
|
|
int i, nRow, nCol, nRowTmp, nColTmp;
|
|
|
|
assert(nFishIndex >= 0 && nFishIndex < MAX_FISH);
|
|
|
|
point = m_pFish[nFishIndex]->GetPosition();
|
|
|
|
i = GetUserGridIndex(point);
|
|
|
|
assert(i != -1);
|
|
|
|
// calc row and column from grid index
|
|
//
|
|
nRow = i / GRID_ROWS;
|
|
nCol = i % GRID_ROWS;
|
|
|
|
for (i = 0; i < MAX_FISH_SIZE; i++) {
|
|
|
|
// if there are no more squares for this fish, then done
|
|
//
|
|
if (m_aUserFishInfo[nFishIndex].nLoc[i].x == NONE)
|
|
break;
|
|
|
|
assert(m_aUserFishInfo[nFishIndex].nLoc[i].x < NONE);
|
|
assert(m_aUserFishInfo[nFishIndex].nLoc[i].y < NONE);
|
|
|
|
// if fish is rotated, then swap x and y
|
|
//
|
|
if (m_pFish[nFishIndex]->GetTypeCode()) {
|
|
|
|
//nRowTmp = nRow + m_aUserFishInfo[nFishIndex].nLoc[i].y;
|
|
//nColTmp = nCol + m_aUserFishInfo[nFishIndex].nLoc[i].x;
|
|
|
|
nRowTmp = (m_aUserFishInfo[nFishIndex].nLoc[i].y += nRow);
|
|
nColTmp = (m_aUserFishInfo[nFishIndex].nLoc[i].x += nCol);
|
|
} else {
|
|
|
|
//nRowTmp = nRow + m_aUserFishInfo[nFishIndex].nLoc[i].x;
|
|
//nColTmp = nCol + m_aUserFishInfo[nFishIndex].nLoc[i].y;
|
|
|
|
nRowTmp = (m_aUserFishInfo[nFishIndex].nLoc[i].x += nRow);
|
|
nColTmp = (m_aUserFishInfo[nFishIndex].nLoc[i].y += nCol);
|
|
}
|
|
|
|
assert(nRowTmp >= 0 && nRowTmp < GRID_ROWS);
|
|
assert(nColTmp >= 0 && nColTmp < GRID_COLS);
|
|
|
|
// this square must be empty (or can contain parts of same fish)
|
|
assert(m_nUserGrid[nRowTmp][nColTmp] == EMPTY || m_nUserGrid[nRowTmp][nColTmp] == (byte)IndexToId(nFishIndex));
|
|
|
|
m_nUserGrid[nRowTmp][nColTmp] = (byte)IndexToId(nFishIndex);
|
|
}
|
|
}
|
|
|
|
|
|
int CBFishWindow::GetUserGridIndex(CPoint point) {
|
|
int i, j, iVal, jVal, nGridIndex;
|
|
bool bEndLoop;
|
|
|
|
iVal = -1;
|
|
jVal = -1;
|
|
bEndLoop = false;
|
|
for (i = 0; i < GRID_ROWS; i++) {
|
|
for (j = 0; j < GRID_COLS; j++) {
|
|
if ((point.x == gLeftGrid[i][j].x) && (point.y == gLeftGrid[i][j].y)) {
|
|
assert(iVal == -1 && jVal == -1);
|
|
bEndLoop = true;
|
|
iVal = i;
|
|
jVal = j;
|
|
break;
|
|
}
|
|
}
|
|
if (bEndLoop)
|
|
break;
|
|
}
|
|
|
|
nGridIndex = (iVal * GRID_ROWS) + jVal;
|
|
|
|
// this point does notif we did not find the correct index
|
|
if ((iVal == -1) && (jVal == -1))
|
|
nGridIndex = -1;
|
|
|
|
// this method will not work if GRID_ROWS or GRID_COLS is greater then 10
|
|
|
|
return nGridIndex;
|
|
}
|
|
|
|
|
|
int CBFishWindow::GetFishIndex(CSprite *pSprite) {
|
|
int i, nIndex;
|
|
|
|
assert(pSprite != nullptr);
|
|
|
|
nIndex = -1;
|
|
for (i = 0; i < MAX_FISH; i++) {
|
|
if (pSprite == m_pFish[i]) {
|
|
nIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
assert(nIndex != -1);
|
|
|
|
return nIndex;
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
*
|
|
* OnRButtonDown
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Handles WM_RBUTTONDOWN messages
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* unsigned int nFlags = Mouse button down flags
|
|
* CPoint point = Point where the mouse was at time of this message
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::OnRButtonDown(unsigned int, CPoint point) {
|
|
CRect tmpRect;
|
|
int i;
|
|
|
|
if (m_bUserEditMode) {
|
|
|
|
assert(m_bGameActive != true);
|
|
|
|
if (m_bMovingFish) {
|
|
|
|
RotateFish(GetFishIndex(m_pDragFish));
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < MAX_FISH; i++) {
|
|
tmpRect = m_pFish[i]->GetRect();
|
|
|
|
if (tmpRect.PtInRect(point)) {
|
|
RotateFish(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
*
|
|
* OnLButtonDown
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Handles WM_LBUTTONDOWN messages
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* unsigned int nFlags = Mouse button down flags
|
|
* CPoint point = Point where the mouse was at time of this message
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::OnLButtonDown(unsigned int, CPoint point) {
|
|
CRect tmpRect,
|
|
winRect,
|
|
trawlerRect,
|
|
sailRect,
|
|
rowRect;
|
|
CPoint ptTmp;
|
|
//CSound *pSound;
|
|
CDC *pDC;
|
|
int i,
|
|
nPick;
|
|
bool bOkToPlay;
|
|
|
|
tmpRect = m_pScrollSprite->GetRect();
|
|
|
|
winRect.SetRect(WINDOW_X, WINDOW_Y, WINDOW_X + WINDOW_DX, WINDOW_Y + WINDOW_DY);
|
|
trawlerRect.SetRect(TRAWLER_X, TRAWLER_Y, TRAWLER_X + TRAWLER_DX, TRAWLER_Y + TRAWLER_DY);
|
|
sailRect.SetRect(SAILBOAT_X, SAILBOAT_Y, SAILBOAT_X + SAILBOAT_DX, SAILBOAT_Y + SAILBOAT_DY);
|
|
rowRect.SetRect(ROWBOAT_X, ROWBOAT_Y, ROWBOAT_X + ROWBOAT_DX, ROWBOAT_Y + ROWBOAT_DY);
|
|
|
|
if (tmpRect.PtInRect(point)) {
|
|
|
|
if (!m_bMovingFish) {
|
|
SendMessage(WM_COMMAND, IDC_MENU, BN_CLICKED);
|
|
}
|
|
|
|
// User clicked on the Title - NewGame button
|
|
//
|
|
} else if (m_rNewGameButton.PtInRect(point)) {
|
|
|
|
// if we are not playing from metagame
|
|
//
|
|
if (!pGameParams->bPlayingMetagame) {
|
|
|
|
// start a new game
|
|
PlayGame();
|
|
}
|
|
|
|
} else if (winRect.PtInRect(point) && pGameParams->bSoundEffectsEnabled) {
|
|
|
|
sndPlaySound(WAV_WINDOW, SND_ASYNC);
|
|
|
|
} else if (trawlerRect.PtInRect(point) && pGameParams->bSoundEffectsEnabled) {
|
|
|
|
sndPlaySound(WAV_TRAWLER, SND_ASYNC);
|
|
|
|
} else if (sailRect.PtInRect(point) && pGameParams->bSoundEffectsEnabled) {
|
|
|
|
sndPlaySound(WAV_SAILBOAT, SND_ASYNC);
|
|
|
|
} else if (rowRect.PtInRect(point) && pGameParams->bSoundEffectsEnabled) {
|
|
|
|
sndPlaySound(WAV_ROWBOAT, SND_ASYNC);
|
|
|
|
// End User-Placement Mode
|
|
//
|
|
} else if (!m_bMovingFish && m_rEndPlacement.PtInRect(point)) {
|
|
|
|
// there can be NO current fish being dragged
|
|
assert(m_pDragFish == nullptr);
|
|
|
|
if (m_bUserEditMode) {
|
|
|
|
// Check to make sure all fish are correctly placed
|
|
//
|
|
bOkToPlay = true;
|
|
for (i = 0; i < MAX_FISH; i++) {
|
|
ptTmp = m_pFish[i]->GetPosition();
|
|
if (ptTmp.x == ptFishBin[i].x && ptTmp.y == ptFishBin[i].y) {
|
|
bOkToPlay = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bOkToPlay) {
|
|
|
|
if ((pDC = GetDC()) != nullptr) {
|
|
if (m_pTxtClickHere != nullptr) {
|
|
m_pTxtClickHere->RestoreBackground(pDC);
|
|
delete m_pTxtClickHere;
|
|
m_pTxtClickHere = nullptr;
|
|
}
|
|
ReleaseDC(pDC);
|
|
}
|
|
|
|
m_bUserEditMode = false;
|
|
m_bGameActive = true;
|
|
m_nTurns = m_nUserFish;
|
|
|
|
|
|
// Convert each fish to grid locations
|
|
//
|
|
for (i = 0; i < MAX_FISH; i++) {
|
|
AssignFishToGrid(i);
|
|
}
|
|
|
|
// select who will go first
|
|
//
|
|
if (!m_bUsersTurn) {
|
|
|
|
// computer goes first
|
|
//
|
|
SendMessage(WM_COMMAND, COMPUTERS_TURN, BN_CLICKED);
|
|
|
|
// player goes first
|
|
//
|
|
} else {
|
|
|
|
if ((pDC = GetDC()) != nullptr) { // put up the octopus
|
|
if (m_pOctopus != nullptr) {
|
|
m_pOctopus->LinkSprite();
|
|
m_pOctopus->PaintSprite(pDC, OCTOPUS_X, OCTOPUS_Y);
|
|
}
|
|
}
|
|
|
|
if (pGameParams->bSoundEffectsEnabled &&
|
|
((m_nUserFish > VOICE_CUTOFF) && (m_nEnemyFish > VOICE_CUTOFF))) { // have her say somethin' clever
|
|
nPick = brand() % NUM_TURN_WAVS;
|
|
if (nPick == 0) {
|
|
#if CSOUND
|
|
pSound = new CSound((CWnd *)this, WAV_YOURTURN1, SOUND_WAVE | SOUND_AUTODELETE);
|
|
#else
|
|
sndPlaySound(WAV_YOURTURN1, SND_SYNC);
|
|
#endif
|
|
} else {
|
|
#if CSOUND
|
|
pSound = new CSound((CWnd *)this, WAV_YOURTURN2, SOUND_WAVE | SOUND_AUTODELETE);
|
|
#else
|
|
sndPlaySound(WAV_YOURTURN2, SND_SYNC);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
PlaceTurnHarpoons();
|
|
|
|
if (pDC != nullptr) { // get her outta there
|
|
if (m_pOctopus != nullptr) {
|
|
m_pOctopus->UnlinkSprite();
|
|
m_pOctopus->EraseSprite(pDC);
|
|
}
|
|
}
|
|
ReleaseDC(pDC);
|
|
} // end if m_bUsersTurn
|
|
}
|
|
|
|
} else {
|
|
|
|
if (pGameParams->bSoundEffectsEnabled)
|
|
sndPlaySound(WAV_INVALID, SND_ASYNC);
|
|
}
|
|
|
|
} else {
|
|
|
|
if (m_bUserEditMode) {
|
|
|
|
if (!m_bMovingFish) {
|
|
|
|
for (i = 0; i < MAX_FISH; i++) {
|
|
tmpRect = m_pFish[i]->GetRect();
|
|
|
|
if (tmpRect.PtInRect(point)) {
|
|
|
|
m_bMovingFish = true;
|
|
m_pDragFish = m_pFish[i];
|
|
m_cLastPoint = m_pDragFish->GetPosition();
|
|
m_bLastRotated = m_pDragFish->GetTypeCode();
|
|
|
|
if ((pDC = GetDC()) != nullptr) {
|
|
|
|
// Dragged fish must be topmost
|
|
//
|
|
m_pDragFish->EraseSprite(pDC);
|
|
m_pDragFish->SetZOrder(SPRITE_TOPMOST);
|
|
m_pDragFish->PaintSprite(pDC, m_cLastPoint);
|
|
ReleaseDC(pDC);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
} else if (m_bGameActive && !m_bPause) {
|
|
|
|
if (m_bUsersTurn) {
|
|
|
|
//
|
|
// Determine if user clicked into enemy grid (to select target)
|
|
// if point is a valid grid location then handle shot
|
|
//
|
|
UsersTurn(GetEnemyGridIndex(point));
|
|
|
|
assert(m_nTurns >= 0);
|
|
|
|
if (m_nTurns == 0) {
|
|
// Computers turn to shoot
|
|
//
|
|
m_bUsersTurn = false;
|
|
SendMessage(WM_COMMAND, COMPUTERS_TURN, BN_CLICKED);
|
|
}
|
|
|
|
} else {
|
|
|
|
// user clicked on an invalid location
|
|
//
|
|
if (pGameParams->bSoundEffectsEnabled)
|
|
sndPlaySound(WAV_INVALID, SND_ASYNC);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
*
|
|
* OnLButtonUp
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Handles WM_LBUTTONUP messages
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* unsigned int nFlags = Mouse button down flags
|
|
* CPoint point = Point where the mouse was at time of this message
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::OnLButtonUp(unsigned int, CPoint point) {
|
|
CSize size;
|
|
CRect rect, tmpRect;
|
|
CDC *pDC;
|
|
int i;
|
|
bool bRevert;
|
|
|
|
if (m_bUserEditMode && m_bMovingFish) {
|
|
|
|
assert(m_bGameActive != true);
|
|
assert(m_pDragFish != nullptr);
|
|
|
|
m_bMovingFish = false;
|
|
|
|
size = m_pDragFish->GetSize();
|
|
point.x -= size.cx / 2;
|
|
point.y -= size.cy / 2;
|
|
|
|
point = SnapToGrid(point);
|
|
bRevert = false;
|
|
|
|
// test for valid fish placement
|
|
//
|
|
rect.SetRect(point.x, point.y, point.x + size.cx, point.y + size.cy);
|
|
|
|
if (!OkToPlaceFish(GetFishIndex(m_pDragFish), point, m_pDragFish->GetTypeCode())) {
|
|
point = m_cLastPoint;
|
|
bRevert = true;
|
|
}
|
|
|
|
// drop fish
|
|
//
|
|
if (pGameParams->bSoundEffectsEnabled)
|
|
sndPlaySound(WAV_PLACESHIP, SND_ASYNC);
|
|
|
|
if ((pDC = GetDC()) != nullptr) {
|
|
|
|
m_pDragFish->EraseSprite(pDC);
|
|
|
|
if (bRevert) {
|
|
|
|
// should we paint normal or rotated?
|
|
i = (m_bLastRotated ? IDB_FISHROT : IDB_FISH);
|
|
|
|
// re-load BMP of fish
|
|
(void)m_pDragFish->LoadResourceSprite(pDC, i + GetFishIndex(m_pDragFish));
|
|
|
|
// FIX
|
|
m_pDragFish->SetTypeCode(m_bLastRotated);
|
|
} else {
|
|
PlaceFish(GetFishIndex(m_pDragFish), point);
|
|
}
|
|
|
|
m_pDragFish->SetZOrder(SPRITE_BACKGROUND);
|
|
m_pDragFish->PaintSprite(pDC, point);
|
|
ReleaseDC(pDC);
|
|
}
|
|
m_pDragFish = nullptr;
|
|
}
|
|
}
|
|
|
|
bool CBFishWindow::OkToPlaceFish(int nFishIndex, CPoint point, bool bRotated) {
|
|
int i, nRow, nCol, nGridIndex, nID;
|
|
bool bOk;
|
|
|
|
nID = IndexToId(nFishIndex);
|
|
nGridIndex = GetUserGridIndex(point);
|
|
|
|
// assume the fish will fit
|
|
bOk = true;
|
|
|
|
// if point is not in the grid, then we can't put the fish here
|
|
//
|
|
if (nGridIndex == -1) {
|
|
bOk = false;
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < MAX_FISH_SIZE; i++) {
|
|
if (m_aUserFishInfo[nFishIndex].nLoc[i].x == NONE)
|
|
break;
|
|
|
|
// is this fish rotated?
|
|
//
|
|
if (bRotated) {
|
|
nRow = (nGridIndex / GRID_ROWS) + m_aUserFishInfo[nFishIndex].nLoc[i].y;
|
|
nCol = (nGridIndex % GRID_COLS) + m_aUserFishInfo[nFishIndex].nLoc[i].x;
|
|
} else {
|
|
nRow = (nGridIndex / GRID_ROWS) + m_aUserFishInfo[nFishIndex].nLoc[i].x;
|
|
nCol = (nGridIndex % GRID_COLS) + m_aUserFishInfo[nFishIndex].nLoc[i].y;
|
|
}
|
|
|
|
// do not exceed grid boundary
|
|
if (nRow < 0 || nRow >= GRID_ROWS || nCol < 0 || nCol >= GRID_COLS) {
|
|
bOk = false;
|
|
break;
|
|
}
|
|
// can't put the new fish into a non-empty square
|
|
else if ((m_nUserGrid[nRow][nCol] != EMPTY) && (m_nUserGrid[nRow][nCol] != (byte)nID)) {
|
|
bOk = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bOk;
|
|
}
|
|
|
|
void CBFishWindow::PlaceFish(int nFishIndex, CPoint point) {
|
|
int i, nRow, nCol, nGridIndex, nID;
|
|
|
|
nID = IndexToId(nFishIndex);
|
|
nGridIndex = GetUserGridIndex(point);
|
|
|
|
assert(nGridIndex != -1);
|
|
|
|
for (i = 0; i < GRID_ROWS * GRID_COLS; i++) {
|
|
|
|
nRow = i / GRID_ROWS;
|
|
nCol = i % GRID_COLS;
|
|
|
|
if (m_nUserGrid[nRow][nCol] & nID) {
|
|
m_nUserGrid[nRow][nCol] = EMPTY;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < MAX_FISH_SIZE; i++) {
|
|
if (m_aUserFishInfo[nFishIndex].nLoc[i].x == NONE)
|
|
break;
|
|
|
|
// is this fish rotated?
|
|
//
|
|
if (m_pFish[nFishIndex]->GetTypeCode()) {
|
|
nRow = (nGridIndex / GRID_ROWS) + m_aUserFishInfo[nFishIndex].nLoc[i].y;
|
|
nCol = (nGridIndex % GRID_COLS) + m_aUserFishInfo[nFishIndex].nLoc[i].x;
|
|
} else {
|
|
nRow = (nGridIndex / GRID_ROWS) + m_aUserFishInfo[nFishIndex].nLoc[i].x;
|
|
nCol = (nGridIndex % GRID_COLS) + m_aUserFishInfo[nFishIndex].nLoc[i].y;
|
|
}
|
|
|
|
m_nUserGrid[nRow][nCol] = (byte)nID;
|
|
}
|
|
}
|
|
|
|
CPoint CBFishWindow::SnapToGrid(CPoint point) {
|
|
int i, j;
|
|
int iMin, jMin;
|
|
int nVal, nMin;
|
|
|
|
// Inits
|
|
nMin = INT_MAX;
|
|
iMin = -1;
|
|
|
|
// find the closest point in the grid to the specified point
|
|
//
|
|
for (i = 0; i < GRID_ROWS; i++) {
|
|
nVal = abs(point.y - gLeftGrid[i][0].y);
|
|
|
|
// if this is the best so far, then store the index
|
|
//
|
|
if (nVal < nMin) {
|
|
iMin = i;
|
|
nMin = nVal;
|
|
}
|
|
}
|
|
|
|
assert(iMin != -1);
|
|
|
|
// Inits
|
|
nMin = INT_MAX;
|
|
jMin = -1;
|
|
|
|
for (j = 0; j < GRID_COLS; j++) {
|
|
nVal = abs(point.x - gLeftGrid[iMin][j].x);
|
|
|
|
// if this is the best so far, then store the index
|
|
//
|
|
if (nVal < nMin) {
|
|
jMin = j;
|
|
nMin = nVal;
|
|
}
|
|
}
|
|
|
|
assert(jMin != -1);
|
|
|
|
point = gLeftGrid[iMin][jMin];
|
|
|
|
return point;
|
|
}
|
|
|
|
/*****************************************************************
|
|
*
|
|
* OnMouseMove
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Handles WM_MOUSEMOVE messages
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* unsigned int nFlags = Mouse button down flags
|
|
* CPoint point = Point where the mouse was at time of message
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::OnMouseMove(unsigned int, CPoint point) {
|
|
CSize size;
|
|
HCURSOR hCursor;
|
|
CDC *pDC;
|
|
|
|
if (m_bUserEditMode && m_bMovingFish) {
|
|
|
|
hCursor = nullptr;
|
|
|
|
assert(m_pDragFish != nullptr);
|
|
|
|
if ((pDC = GetDC()) != nullptr) {
|
|
size = m_pDragFish->GetSize();
|
|
point.x -= size.cx / 2;
|
|
point.y -= size.cy / 2;
|
|
m_pDragFish->PaintSprite(pDC, point);
|
|
ReleaseDC(pDC);
|
|
}
|
|
} else {
|
|
|
|
hCursor = LoadCursor(nullptr, IDC_ARROW);
|
|
}
|
|
|
|
MFC::SetCursor(hCursor);
|
|
}
|
|
|
|
|
|
void CBFishWindow::UsersTurn(int nGridIndex) {
|
|
//CSound *pSound;
|
|
int nRow, nCol, nSquare;
|
|
int nFishIndex;
|
|
//int nPick = 0;
|
|
|
|
// validate the grid index
|
|
|
|
if ((nGridIndex >= 0) && (nGridIndex < GRID_ROWS * GRID_COLS)) {
|
|
|
|
// Shoot at nGridIndex - calc row and column for this grid location
|
|
//
|
|
nRow = nGridIndex / GRID_ROWS;
|
|
nCol = nGridIndex % GRID_ROWS;
|
|
|
|
nSquare = m_nEnemyGrid[nRow][nCol];
|
|
|
|
assert(nSquare <= (IndexToId(FISH3) | SHOT));
|
|
|
|
// can't shoot same square twice
|
|
//
|
|
if (nSquare & SHOT) {
|
|
|
|
if (pGameParams->bSoundEffectsEnabled)
|
|
sndPlaySound(WAV_INVALID, SND_ASYNC);
|
|
|
|
} else {
|
|
|
|
m_nTurns--;
|
|
|
|
// take one harpoon off the game board
|
|
RemoveTurnHarpoon();
|
|
|
|
// shoot harpoon
|
|
if (pGameParams->bSoundEffectsEnabled) {
|
|
#if CSOUND
|
|
pSound = new CSound((CWnd *)this, WAV_SHOOT, SOUND_WAVE | SOUND_AUTODELETE);
|
|
#else
|
|
sndPlaySound(WAV_SHOOT, SND_SYNC);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// check to see if this location contains an enemy fish
|
|
//
|
|
if (nSquare != EMPTY) {
|
|
|
|
// play the you hit sound
|
|
if (pGameParams->bSoundEffectsEnabled) {
|
|
#if CSOUND
|
|
pSound = new CSound((CWnd *)this, WAV_YOUHIT, SOUND_WAVE | SOUND_AUTODELETE);
|
|
#else
|
|
sndPlaySound(WAV_YOUHIT, SND_ASYNC);
|
|
#endif
|
|
}
|
|
|
|
// get index to fish
|
|
nFishIndex = IdToIndex(nSquare);
|
|
|
|
assert(nFishIndex >= FISH0 && nFishIndex <= FISH3);
|
|
|
|
//
|
|
// Put a Harpoon here
|
|
//
|
|
CreateHarpoon(gRightGrid[nRow][nCol]);
|
|
|
|
assert(m_aEnemyFishInfo[nFishIndex].life > 0);
|
|
|
|
//
|
|
// indicate that this fish has one less life
|
|
//
|
|
if (--m_aEnemyFishInfo[nFishIndex].life == 0) {
|
|
|
|
if (pGameParams->bSoundEffectsEnabled) {
|
|
|
|
sndPlaySound(pszFishSound[nFishIndex], SND_SYNC);
|
|
}
|
|
|
|
SinkEnemyFish(nFishIndex);
|
|
|
|
// one less fish
|
|
//
|
|
assert(m_nEnemyFish > 0);
|
|
if (--m_nEnemyFish == 0) {
|
|
|
|
GamePause();
|
|
|
|
// Display any extra animation
|
|
//
|
|
|
|
// User Wins
|
|
//
|
|
if (pGameParams->bSoundEffectsEnabled)
|
|
sndPlaySound(WAV_YOUWIN, SND_ASYNC);
|
|
|
|
CMessageBox(this, m_pGamePalette, "Game over.", "You win!");
|
|
|
|
GameReset();
|
|
|
|
if (pGameParams->bPlayingMetagame) {
|
|
pGameParams->lScore = 1;
|
|
PostMessage(WM_CLOSE, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// play the you missed sound
|
|
if (pGameParams->bSoundEffectsEnabled) {
|
|
sndPlaySound(WAV_YOUMISS, SND_ASYNC);
|
|
}
|
|
|
|
//
|
|
// square was empty, so put a plume of water here
|
|
//
|
|
CreatePlume(gRightGrid[nRow][nCol]);
|
|
}
|
|
|
|
// indicate that this sqaure has been shot
|
|
m_nEnemyGrid[nRow][nCol] |= SHOT;
|
|
}
|
|
} else {
|
|
|
|
// invalid click
|
|
//
|
|
if (pGameParams->bSoundEffectsEnabled)
|
|
sndPlaySound(WAV_INVALID, SND_ASYNC);
|
|
}
|
|
}
|
|
|
|
void CBFishWindow::SinkEnemyFish(int nFishIndex) {
|
|
CSprite *pSprite;
|
|
CDC *pDC;
|
|
|
|
// display fish on hook
|
|
|
|
// validate the input
|
|
assert(nFishIndex >= 0 && nFishIndex < MAX_FISH);
|
|
|
|
pSprite = m_pEnemyFish[nFishIndex];
|
|
|
|
assert(pSprite != nullptr);
|
|
|
|
pSprite->SetTypeCode(true);
|
|
pSprite->LinkSprite();
|
|
|
|
if ((pDC = GetDC()) != nullptr) {
|
|
pSprite->PaintSprite(pDC, pSprite->GetPosition());
|
|
ReleaseDC(pDC);
|
|
}
|
|
}
|
|
|
|
|
|
void CBFishWindow::ComputersTurn() {
|
|
STATIC int nLastRow, nLastCol;
|
|
//CSound *pSound;
|
|
int nRow, nCol, nFishIndex, nGridIndex;
|
|
int nSquare;
|
|
|
|
if (m_bGameActive && !m_bPause) {
|
|
|
|
//
|
|
// Perform some AI to find the next best target
|
|
//
|
|
nGridIndex = FindTarget(nLastRow, nLastCol);
|
|
nRow = nGridIndex / GRID_ROWS;
|
|
nCol = nGridIndex % GRID_COLS;
|
|
|
|
nSquare = m_nUserGrid[nRow][nCol];
|
|
|
|
// this square could not have been shot before
|
|
assert((m_nUserGrid[nRow][nCol] & SHOT) != SHOT);
|
|
|
|
// indicate that this square has now been shot
|
|
m_nUserGrid[nRow][nCol] |= SHOT;
|
|
|
|
// shoot harpoon
|
|
//
|
|
if (pGameParams->bSoundEffectsEnabled) {
|
|
#if CSOUND
|
|
pSound = new CSound((CWnd *)this, WAV_SHOOT, SOUND_WAVE | SOUND_AUTODELETE);
|
|
#else
|
|
sndPlaySound(WAV_SHOOT, SND_SYNC);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// check to see if this location contains an enemy fish
|
|
//
|
|
if (nSquare != EMPTY) {
|
|
|
|
// play the you hit sound (harpoon)
|
|
if (pGameParams->bSoundEffectsEnabled)
|
|
sndPlaySound(WAV_YOUHIT, SND_ASYNC);
|
|
|
|
// get index to fish
|
|
nFishIndex = IdToIndex(nSquare);
|
|
|
|
if ((m_bStillCheck == false) || (m_nDifficultyLevel == DIFF_AVERAGE)) {
|
|
m_bStillCheck = true;
|
|
nLastRow = nRow;
|
|
nLastCol = nCol;
|
|
}
|
|
|
|
//
|
|
// Put a Harpoon here
|
|
//
|
|
CreateHarpoon(gLeftGrid[nRow][nCol]);
|
|
|
|
//
|
|
// indicate that this fish has one less life
|
|
//
|
|
|
|
assert(m_aUserFishInfo[nFishIndex].life > 0);
|
|
|
|
//
|
|
// indicate that this fish has one less life
|
|
//
|
|
if (--m_aUserFishInfo[nFishIndex].life == 0) {
|
|
|
|
m_bStillCheck = false;
|
|
|
|
assert(m_nUserFish > 0);
|
|
|
|
if (pGameParams->bSoundEffectsEnabled) {
|
|
#if CSOUND
|
|
pSound = new CSound((CWnd *)this, WAV_YOUSINK, SOUND_WAVE | SOUND_AUTODELETE);
|
|
#else
|
|
sndPlaySound(WAV_YOUSINK, SND_SYNC);
|
|
#endif
|
|
}
|
|
|
|
// for DiffLevel2 (Computer AI), erase trace of fish from board
|
|
//
|
|
SinkUserFish(nFishIndex);
|
|
|
|
// one less fish
|
|
//
|
|
if (--m_nUserFish == 0) {
|
|
|
|
GamePause();
|
|
|
|
// Display any extra animation
|
|
//
|
|
|
|
// User Lost
|
|
//
|
|
if (pGameParams->bSoundEffectsEnabled)
|
|
sndPlaySound(WAV_GAMEOVER, SND_SYNC);
|
|
|
|
CMessageBox(this, m_pGamePalette, "Game over.", "You have lost!");
|
|
|
|
GameReset();
|
|
|
|
if (pGameParams->bPlayingMetagame) {
|
|
PostMessage(WM_CLOSE, 0, 0);
|
|
PostMessage(WM_CLOSE, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// play the "you missed" sound (splash of water)
|
|
//
|
|
if (pGameParams->bSoundEffectsEnabled)
|
|
sndPlaySound(WAV_YOUMISS, SND_ASYNC);
|
|
|
|
//
|
|
// square was empty, so put a plume of water here
|
|
//
|
|
CreatePlume(gLeftGrid[nRow][nCol]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CBFishWindow::SinkUserFish(int nFishIndex) {
|
|
int i, nRow, nCol;
|
|
|
|
for (i = 0; i < GRID_ROWS * GRID_COLS; i++) {
|
|
|
|
nRow = i / GRID_ROWS;
|
|
nCol = i % GRID_COLS;
|
|
|
|
if (m_nUserGrid[nRow][nCol] & IndexToId(nFishIndex)) {
|
|
m_nUserGrid[nRow][nCol] = (EMPTY | SHOT);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int CBFishWindow::SelectRandomTarget() {
|
|
int n;
|
|
int nRow, nCol;
|
|
|
|
//
|
|
// choose a random target, but favor targets
|
|
// with maximum number of empty neighbors
|
|
//
|
|
n = 0;
|
|
do {
|
|
nRow = brand() % GRID_ROWS;
|
|
nCol = brand() % GRID_COLS;
|
|
|
|
if (m_nUserGrid[nRow][nCol] & SHOT)
|
|
continue;
|
|
|
|
n = GetNeighbors(nRow, nCol);
|
|
|
|
} while (n == 0);
|
|
|
|
assert(nRow >= 0 && nRow < GRID_ROWS);
|
|
assert(nCol >= 0 && nCol < GRID_COLS);
|
|
|
|
return (nRow * GRID_ROWS) + nCol;
|
|
}
|
|
|
|
|
|
int CBFishWindow::SelectBurningTarget() {
|
|
int i, nGridIndex, nRow, nCol;
|
|
bool bFound;
|
|
|
|
nRow = nCol = 0;
|
|
|
|
// try to re-aquire a fish we have already damaged, but has not yet sank
|
|
//
|
|
bFound = false;
|
|
for (i = 0; i < GRID_ROWS * GRID_COLS; i++) {
|
|
nRow = i / GRID_ROWS;
|
|
nCol = i % GRID_COLS;
|
|
|
|
if ((m_nUserGrid[nRow][nCol] & SHOT) && (m_nUserGrid[nRow][nCol] != (EMPTY | SHOT))) {
|
|
bFound = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
nGridIndex = (nRow * GRID_ROWS) + nCol;
|
|
|
|
// if there are no such damaged fish, then just select a random target
|
|
//
|
|
if (!bFound)
|
|
nGridIndex = SelectBestFitTarget();
|
|
|
|
return nGridIndex;
|
|
}
|
|
|
|
int CBFishWindow::SelectBestFitTarget() {
|
|
int nRow, nCol, nFishIndex, nGridIndex;
|
|
int counter, i;
|
|
|
|
nFishIndex = MAX_FISH;
|
|
while (m_aUserFishInfo[--nFishIndex].life == 0) {
|
|
assert(nFishIndex >= 0);
|
|
}
|
|
|
|
i = counter = 0;
|
|
do {
|
|
if (counter < 1000) {
|
|
|
|
nGridIndex = SelectRandomTarget();
|
|
assert((nGridIndex >= 0) && (nGridIndex < (GRID_ROWS * GRID_COLS)));
|
|
nRow = nGridIndex / GRID_ROWS;
|
|
nCol = nGridIndex % GRID_COLS;
|
|
|
|
i = 0;
|
|
|
|
counter++;
|
|
|
|
// we have checked too many random locations
|
|
} else {
|
|
|
|
// now start at (0,0) and check until (7,7)
|
|
|
|
assert((i >= 0) && (i < (GRID_ROWS * GRID_COLS)));
|
|
|
|
nRow = i / GRID_ROWS;
|
|
nCol = i % GRID_COLS;
|
|
nGridIndex = i;
|
|
i++;
|
|
}
|
|
|
|
} while (!FishFits(nFishIndex, nRow, nCol));
|
|
|
|
return nGridIndex;
|
|
}
|
|
|
|
bool CBFishWindow::FishFits(int nFishIndex, int row, int col) {
|
|
FISH cFishInfo;
|
|
int nRow, nCol, colTmp, rowTmp;
|
|
int i, j, k, rotate;
|
|
bool bFound;
|
|
|
|
// Try fish at both 0 and 90 degrees
|
|
//
|
|
bFound = false;
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
rotate = brand() & 1;
|
|
for (j = 0; j < MAX_FISH_SIZE; j++) {
|
|
|
|
// make a fresh copy of this fish
|
|
memcpy(&cFishInfo, &gFishSizes[nFishIndex], sizeof(FISH));
|
|
|
|
//
|
|
// Fish didn't fit the first time thru, so try rotating
|
|
// it by swapping row and column for each square in
|
|
// fish.
|
|
//
|
|
if (i == rotate) {
|
|
for (k = 0; k < MAX_FISH_SIZE; k++) {
|
|
cFishInfo.nLoc[k].x = gFishSizes[nFishIndex].nLoc[k].y;
|
|
cFishInfo.nLoc[k].y = gFishSizes[nFishIndex].nLoc[k].x;
|
|
}
|
|
}
|
|
|
|
nRow = row - cFishInfo.nLoc[j].x;
|
|
nCol = col - cFishInfo.nLoc[j].y;
|
|
|
|
if ((nRow >= 0) && (nCol >= 0)) {
|
|
|
|
// Does the fish fit at this location
|
|
//
|
|
bFound = true;
|
|
for (k = 0; k < MAX_FISH_SIZE; k++) {
|
|
|
|
// if there are no more squares for this fish, then done
|
|
//
|
|
if (cFishInfo.nLoc[k].x == NONE)
|
|
break;
|
|
|
|
assert(cFishInfo.nLoc[k].y != NONE);
|
|
|
|
rowTmp = (cFishInfo.nLoc[k].x += nRow);
|
|
colTmp = (cFishInfo.nLoc[k].y += nCol);
|
|
|
|
if ((rowTmp >= GRID_ROWS) || (colTmp >= GRID_COLS) || (m_nUserGrid[rowTmp][colTmp] == (EMPTY | SHOT))) {
|
|
bFound = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (bFound)
|
|
break;
|
|
}
|
|
|
|
return bFound;
|
|
}
|
|
|
|
|
|
int CBFishWindow::GetNeighbors(int nRow, int nCol) {
|
|
int n;
|
|
|
|
// validate the input
|
|
//
|
|
assert(nRow >= 0 && nRow < GRID_ROWS);
|
|
assert(nCol >= 0 && nCol < GRID_COLS);
|
|
|
|
// start with NO neighbors
|
|
n = 0;
|
|
|
|
// if specified square has an empty square to it's left then we have
|
|
// a neighbor
|
|
//
|
|
if ((nRow + 1 >= GRID_ROWS) || (m_nUserGrid[nRow + 1][nCol] != (EMPTY | SHOT))) {
|
|
n++;
|
|
if ((nRow + 1 < GRID_ROWS) && isodd(m_nUserGrid[nRow + 1][nCol]))
|
|
n++;
|
|
}
|
|
|
|
// if specified square has an empty square to it's right then we have
|
|
// another neighbor
|
|
//
|
|
if ((nRow - 1 < 0) || (m_nUserGrid[nRow - 1][nCol] != (EMPTY | SHOT))) {
|
|
n++;
|
|
if ((nRow - 1 >= 0) && isodd(m_nUserGrid[nRow - 1][nCol]))
|
|
n++;
|
|
}
|
|
|
|
// if specified square has an empty square to it's bottom then we have
|
|
// another neighbor
|
|
//
|
|
if ((nCol + 1 >= GRID_COLS) || (m_nUserGrid[nRow][nCol + 1] != (EMPTY | SHOT))) {
|
|
n++;
|
|
if ((nCol + 1 < GRID_COLS) && isodd(m_nUserGrid[nRow][nCol + 1]))
|
|
n++;
|
|
}
|
|
|
|
// if specified square has an empty square to it's top then we have
|
|
// another neighbor
|
|
//
|
|
if ((nCol - 1 < 0) || (m_nUserGrid[nRow][nCol - 1] != (EMPTY | SHOT))) {
|
|
n++;
|
|
if ((nCol - 1 >= 0) && isodd(m_nUserGrid[nRow][nCol - 1]))
|
|
n++;
|
|
}
|
|
|
|
// can have no more than 4 neighbors, and no less than 0
|
|
assert(n >= 0 && n <= 8);
|
|
|
|
// return number of neighbors found for this square
|
|
return n;
|
|
}
|
|
|
|
|
|
int CBFishWindow::FindTarget(int nLastHitRow, int nLastHitCol) {
|
|
int nGridIndex;
|
|
|
|
assert(nLastHitRow >= 0 && nLastHitRow < GRID_ROWS);
|
|
assert(nLastHitCol >= 0 && nLastHitCol < GRID_COLS);
|
|
|
|
nGridIndex = -1;
|
|
switch (m_nDifficultyLevel) {
|
|
|
|
//
|
|
// Easiest level: select random targets
|
|
//
|
|
case DIFF_WIMPY:
|
|
nGridIndex = SelectRandomTarget();
|
|
break;
|
|
|
|
//
|
|
// Medium level: shoot one of the surrounding squares of our last hit
|
|
//
|
|
case DIFF_AVERAGE:
|
|
if (m_bStillCheck) {
|
|
nGridIndex = FindNeighborTarget(nLastHitRow, nLastHitCol);
|
|
} else {
|
|
nGridIndex = SelectRandomTarget();
|
|
}
|
|
break;
|
|
|
|
//
|
|
// toughest level: uses pattern recognition to determine where
|
|
// fish would best fit
|
|
//
|
|
default:
|
|
assert(m_nDifficultyLevel == DIFF_HEFTY);
|
|
|
|
nGridIndex = FindMatch(nLastHitRow, nLastHitCol);
|
|
break;
|
|
}
|
|
|
|
// Grid Index can only be 0..63
|
|
assert((nGridIndex >= 0) && (nGridIndex < (GRID_ROWS * GRID_COLS)));
|
|
|
|
return nGridIndex;
|
|
}
|
|
|
|
|
|
int CBFishWindow::FindNeighborTarget(int nLastHitRow, int nLastHitCol) {
|
|
int nRow, nCol, nGridIndex;
|
|
int nState, nDisplacement;
|
|
|
|
nState = 0;
|
|
nRow = nCol = 0;
|
|
|
|
assert(nLastHitRow >= 0 && nLastHitRow < GRID_ROWS);
|
|
assert(nLastHitCol >= 0 && nLastHitCol < GRID_COLS);
|
|
|
|
nDisplacement = 1;
|
|
do {
|
|
switch (nState) {
|
|
|
|
case 0:
|
|
nRow = nLastHitRow + nDisplacement;
|
|
nCol = nLastHitCol;
|
|
break;
|
|
|
|
case 1:
|
|
nRow = nLastHitRow - nDisplacement;
|
|
nCol = nLastHitCol;
|
|
break;
|
|
|
|
case 2:
|
|
nRow = nLastHitRow;
|
|
nCol = nLastHitCol + nDisplacement;
|
|
break;
|
|
|
|
case 3:
|
|
nRow = nLastHitRow;
|
|
nCol = nLastHitCol - nDisplacement;
|
|
break;
|
|
|
|
case 4:
|
|
nRow = nLastHitRow + nDisplacement;
|
|
nCol = nLastHitCol + nDisplacement;
|
|
break;
|
|
|
|
case 5:
|
|
nRow = nLastHitRow - nDisplacement;
|
|
nCol = nLastHitCol - nDisplacement;
|
|
break;
|
|
|
|
case 6:
|
|
nRow = nLastHitRow - nDisplacement;
|
|
nCol = nLastHitCol + nDisplacement;
|
|
break;
|
|
|
|
case 7:
|
|
nRow = nLastHitRow + nDisplacement;
|
|
nCol = nLastHitCol - nDisplacement;
|
|
break;
|
|
|
|
default:
|
|
|
|
if (nDisplacement == 1) {
|
|
nDisplacement++;
|
|
nState = -1;
|
|
} else {
|
|
nState--;
|
|
|
|
nGridIndex = SelectRandomTarget();
|
|
|
|
nRow = nGridIndex / GRID_ROWS;
|
|
nCol = nGridIndex % GRID_COLS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (nRow < 0 || nRow >= GRID_ROWS)
|
|
nRow = nLastHitRow;
|
|
|
|
if (nCol < 0 || nCol >= GRID_COLS)
|
|
nCol = nLastHitCol;
|
|
|
|
nState++;
|
|
|
|
} while (m_nUserGrid[nRow][nCol] & SHOT);
|
|
|
|
return (nRow * GRID_ROWS) + nCol;
|
|
}
|
|
|
|
#if 1
|
|
|
|
int CBFishWindow::FindMatch(int nLastHitRow, int nLastHitCol) {
|
|
FISH cFishInfo;
|
|
int i, j, k, l, rotate;
|
|
int nRow, nCol, nBestRow, nBestCol;
|
|
int n, nLast;
|
|
int row, col, rowTmp, colTmp, nGridIndex;
|
|
int nUseRow, nUseCol;
|
|
int nHits, nBestHits;
|
|
bool bFound, bUse;
|
|
|
|
rowTmp = colTmp = 0;
|
|
nBestRow = nBestCol = 0;
|
|
nUseRow = nUseCol = 0;
|
|
nBestHits = -1;
|
|
nHits = 0;
|
|
|
|
// validate the input
|
|
//
|
|
assert(nLastHitRow >= 0 && nLastHitRow < GRID_ROWS);
|
|
assert(nLastHitCol >= 0 && nLastHitCol < GRID_COLS);
|
|
|
|
if (m_bStillCheck) {
|
|
row = nLastHitRow;
|
|
col = nLastHitCol;
|
|
} else {
|
|
|
|
// select random starting square
|
|
//
|
|
nGridIndex = SelectBurningTarget();
|
|
|
|
row = nGridIndex / GRID_ROWS;
|
|
col = nGridIndex % GRID_COLS;
|
|
}
|
|
|
|
// Try to match a fish pattern onto the grid
|
|
//
|
|
bUse = false;
|
|
bFound = false;
|
|
for (i = MAX_FISH - 1; i >= 0; i--) {
|
|
|
|
bFound = false;
|
|
if (m_aUserFishInfo[i].life != 0) {
|
|
|
|
// Try fish at both 0 and 90 degrees
|
|
//
|
|
rotate = brand() & 1;
|
|
for (j = 0; j < 2; j++) {
|
|
|
|
bFound = false;
|
|
for (l = 0; l < MAX_FISH_SIZE; l++) {
|
|
|
|
// make a fresh copy of this fish
|
|
memcpy(&cFishInfo, &gFishSizes[i], sizeof(FISH));
|
|
|
|
//
|
|
// Fish didn't fit the first time thru, so try rotating
|
|
// it by swapping row and column for each square in
|
|
// fish.
|
|
//
|
|
if (j == rotate) {
|
|
for (k = 0; k < MAX_FISH_SIZE; k++) {
|
|
cFishInfo.nLoc[k].x = gFishSizes[i].nLoc[k].y;
|
|
cFishInfo.nLoc[k].y = gFishSizes[i].nLoc[k].x;
|
|
}
|
|
}
|
|
|
|
nRow = row - cFishInfo.nLoc[l].x;
|
|
nCol = col - cFishInfo.nLoc[l].y;
|
|
|
|
if ((nRow >= 0) && (nCol >= 0)) {
|
|
|
|
// Does the fish fit at this location
|
|
//
|
|
bFound = true;
|
|
for (k = 0; k < MAX_FISH_SIZE; k++) {
|
|
|
|
// if there are no more squares for this fish, then done
|
|
//
|
|
if (cFishInfo.nLoc[k].x == NONE)
|
|
break;
|
|
|
|
assert(cFishInfo.nLoc[k].y != NONE);
|
|
|
|
rowTmp = (cFishInfo.nLoc[k].x += nRow);
|
|
colTmp = (cFishInfo.nLoc[k].y += nCol);
|
|
|
|
if ((rowTmp < 0) || (rowTmp >= GRID_ROWS) || (colTmp < 0) || (colTmp >= GRID_COLS) || (m_nUserGrid[rowTmp][colTmp] == (EMPTY | SHOT))) {
|
|
bFound = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check to see if we can shoot one of these spots
|
|
//
|
|
if (bFound) {
|
|
bFound = false;
|
|
nLast = -1;
|
|
nBestRow = row;
|
|
nBestCol = col;
|
|
nHits = 0;
|
|
for (k = 0; k < MAX_FISH_SIZE; k++) {
|
|
|
|
// if there are no more squares for this fish, then done
|
|
//
|
|
if (cFishInfo.nLoc[k].x == NONE)
|
|
break;
|
|
|
|
assert(cFishInfo.nLoc[k].y < NONE);
|
|
|
|
rowTmp = cFishInfo.nLoc[k].x;
|
|
colTmp = cFishInfo.nLoc[k].y;
|
|
|
|
if ((m_nUserGrid[rowTmp][colTmp] & SHOT) == EMPTY) {
|
|
|
|
n = GetNeighbors(rowTmp, colTmp);
|
|
|
|
if (n > nLast) {
|
|
|
|
nBestRow = rowTmp;
|
|
nBestCol = colTmp;
|
|
nLast = n;
|
|
}
|
|
bFound = true;
|
|
} else if (m_nUserGrid[rowTmp][colTmp] & IndexToId(i)) {
|
|
nHits++;
|
|
}
|
|
}
|
|
|
|
//TRACE("Fish %d at (%d, %d) has %d hits\n", i, nBestRow, nBestCol, nHits);
|
|
}
|
|
|
|
|
|
// the fish fit - so set the grid and then go on to next fish
|
|
//
|
|
if (bFound) {
|
|
|
|
if (nHits > nBestHits) {
|
|
|
|
nBestHits = nHits;
|
|
nUseRow = rowTmp = nBestRow;
|
|
nUseCol = colTmp = nBestCol;
|
|
bUse = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//TRACE("Chose (%d, %d) with %d hits\n", nUseRow, nUseCol, nBestHits);
|
|
rowTmp = nUseRow;
|
|
colTmp = nUseCol;
|
|
|
|
assert(bUse);
|
|
|
|
assert(rowTmp >= 0 && rowTmp < GRID_ROWS);
|
|
assert(colTmp >= 0 && colTmp < GRID_COLS);
|
|
|
|
// this square could not have already been shot
|
|
assert((m_nUserGrid[rowTmp][colTmp] & SHOT) == EMPTY);
|
|
|
|
return (rowTmp * GRID_ROWS) + colTmp;
|
|
}
|
|
#else
|
|
|
|
int CBFishWindow::FindMatch(int nLastHitRow, int nLastHitCol) {
|
|
FISH cFishInfo;
|
|
int i, j, k, l, rotate;
|
|
int nRow, nCol, nBestRow, nBestCol;
|
|
int n, nLast;
|
|
int row, col, rowTmp, colTmp, nGridIndex;
|
|
bool bFound;
|
|
|
|
rowTmp = colTmp = 0;
|
|
nBestRow = nBestCol = 0;
|
|
|
|
// validate the input
|
|
//
|
|
assert(nLastHitRow >= 0 && nLastHitRow < GRID_ROWS);
|
|
assert(nLastHitCol >= 0 && nLastHitCol < GRID_COLS);
|
|
|
|
if (m_bStillCheck) {
|
|
row = nLastHitRow;
|
|
col = nLastHitCol;
|
|
} else {
|
|
|
|
// select random starting square
|
|
//
|
|
nGridIndex = SelectBurningTarget();
|
|
|
|
//nGridIndex = bob[fred++];
|
|
|
|
row = nGridIndex / GRID_ROWS;
|
|
col = nGridIndex % GRID_COLS;
|
|
}
|
|
|
|
// Try to match a fish pattern onto the grid
|
|
//
|
|
bFound = false;
|
|
for (i = MAX_FISH - 1; i >= 0; i--) {
|
|
|
|
bFound = false;
|
|
if (m_aUserFishInfo[i].life != 0) {
|
|
|
|
// Try fish at both 0 and 90 degrees
|
|
//
|
|
rotate = brand() & 1;
|
|
for (j = 0; j < 2; j++) {
|
|
|
|
bFound = false;
|
|
for (l = 0; l < MAX_FISH_SIZE; l++) {
|
|
|
|
// make a fresh copy of this fish
|
|
memcpy(&cFishInfo, &gFishSizes[i], sizeof(FISH));
|
|
|
|
//
|
|
// Fish didn't fit the first time thru, so try rotating
|
|
// it by swapping row and column for each square in
|
|
// fish.
|
|
//
|
|
if (j == rotate) {
|
|
for (k = 0; k < MAX_FISH_SIZE; k++) {
|
|
cFishInfo.nLoc[k].x = gFishSizes[i].nLoc[k].y;
|
|
cFishInfo.nLoc[k].y = gFishSizes[i].nLoc[k].x;
|
|
}
|
|
}
|
|
|
|
nRow = row - cFishInfo.nLoc[l].x;
|
|
nCol = col - cFishInfo.nLoc[l].y;
|
|
|
|
if ((nRow >= 0) && (nCol >= 0)) {
|
|
|
|
// Does the fish fit at this location
|
|
//
|
|
bFound = true;
|
|
for (k = 0; k < MAX_FISH_SIZE; k++) {
|
|
|
|
// if there are no more squares for this fish, then done
|
|
//
|
|
if (cFishInfo.nLoc[k].x == NONE)
|
|
break;
|
|
|
|
assert(cFishInfo.nLoc[k].y != NONE);
|
|
|
|
rowTmp = (cFishInfo.nLoc[k].x += nRow);
|
|
colTmp = (cFishInfo.nLoc[k].y += nCol);
|
|
|
|
if ((rowTmp < 0) || (rowTmp >= GRID_ROWS) || (colTmp < 0) || (colTmp >= GRID_COLS) || (m_nUserGrid[rowTmp][colTmp] == (EMPTY | SHOT))) {
|
|
bFound = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check to see if we can shoot one of these spots
|
|
//
|
|
if (bFound) {
|
|
bFound = false;
|
|
nLast = -1;
|
|
nBestRow = row;
|
|
nBestCol = col;
|
|
for (k = 0; k < MAX_FISH_SIZE; k++) {
|
|
|
|
// if there are no more squares for this fish, then done
|
|
//
|
|
if (cFishInfo.nLoc[k].x == NONE)
|
|
break;
|
|
|
|
assert(cFishInfo.nLoc[k].y < NONE);
|
|
|
|
rowTmp = cFishInfo.nLoc[k].x;
|
|
colTmp = cFishInfo.nLoc[k].y;
|
|
|
|
if ((m_nUserGrid[rowTmp][colTmp] & SHOT) == EMPTY) {
|
|
|
|
n = GetNeighbors(rowTmp, colTmp);
|
|
|
|
if (n > nLast) {
|
|
nBestRow = rowTmp;
|
|
nBestCol = colTmp;
|
|
nLast = n;
|
|
}
|
|
bFound = true;
|
|
}
|
|
}
|
|
if (bFound) {
|
|
rowTmp = nBestRow;
|
|
colTmp = nBestCol;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// the fish fit - so set the grid and then go on to next fish
|
|
//
|
|
if (bFound) {
|
|
|
|
rowTmp = nBestRow;
|
|
colTmp = nBestCol;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (bFound)
|
|
break;
|
|
}
|
|
}
|
|
if (bFound)
|
|
break;
|
|
}
|
|
|
|
assert(bFound);
|
|
|
|
if (!bFound) {
|
|
assert(m_bStillCheck == false);
|
|
rowTmp = row;
|
|
colTmp = col;
|
|
}
|
|
|
|
assert(rowTmp >= 0 && rowTmp < GRID_ROWS);
|
|
assert(colTmp >= 0 && colTmp < GRID_COLS);
|
|
|
|
// this square could not have already been shot
|
|
assert((m_nUserGrid[rowTmp][colTmp] & SHOT) == EMPTY);
|
|
|
|
return (rowTmp * GRID_ROWS) + colTmp;
|
|
}
|
|
#endif
|
|
|
|
|
|
void CBFishWindow::CreatePlume(CPoint point) {
|
|
CDC *pDC;
|
|
CSprite *pSprite;
|
|
int i;
|
|
ERROR_CODE errCode;
|
|
|
|
// assume no error
|
|
errCode = ERR_NONE;
|
|
|
|
if ((pDC = GetDC()) != nullptr) {
|
|
|
|
// Play a plume of water animation sequence inline
|
|
//
|
|
if ((pSprite = new CSprite) != nullptr) {
|
|
|
|
// attach good guy to the Game Palette
|
|
//
|
|
if (pSprite->SharePalette(m_pGamePalette) != false) {
|
|
|
|
if (pSprite->LoadResourceSprite(pDC, IDB_PLUME) != false) {
|
|
|
|
pSprite->LoadResourceCels(pDC, IDB_APLUME, N_PLUME_CELS);
|
|
|
|
pSprite->SetMasked(true);
|
|
pSprite->SetMobile(true);
|
|
|
|
for (i = 0; i < N_PLUME_CELS; i++) {
|
|
pSprite->PaintSprite(pDC, point);
|
|
AfxGetApp()->pause();
|
|
Sleep(100);
|
|
}
|
|
} else {
|
|
errCode = ERR_UNKNOWN;
|
|
}
|
|
} else {
|
|
errCode = ERR_UNKNOWN;
|
|
}
|
|
|
|
pSprite->EraseSprite(pDC);
|
|
delete pSprite;
|
|
AfxGetApp()->pause();
|
|
|
|
// create a dup of the master plume of water
|
|
//
|
|
if ((pSprite = m_pMasterMiss->DuplicateSprite(pDC)) != nullptr) {
|
|
pSprite->LinkSprite();
|
|
pSprite->PaintSprite(pDC, point);
|
|
AfxGetApp()->pause();
|
|
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
|
|
ReleaseDC(pDC);
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
|
|
HandleError(errCode);
|
|
}
|
|
|
|
|
|
void CBFishWindow::CreateHarpoon(CPoint point) {
|
|
CDC *pDC;
|
|
CSprite *pSprite;
|
|
int i;
|
|
ERROR_CODE errCode;
|
|
|
|
// assume no error
|
|
errCode = ERR_NONE;
|
|
|
|
if ((pDC = GetDC()) != nullptr) {
|
|
|
|
// Play a harpoon hitting fish animation sequence inline
|
|
//
|
|
if ((pSprite = new CSprite) != nullptr) {
|
|
|
|
// attach good guy to the Game Palette
|
|
//
|
|
if (pSprite->SharePalette(m_pGamePalette) != false) {
|
|
|
|
if (pSprite->LoadResourceSprite(pDC, IDB_HARP) != false) {
|
|
|
|
pSprite->LoadResourceCels(pDC, IDB_AHARP, N_HARP_CELS);
|
|
|
|
pSprite->SetMasked(true);
|
|
pSprite->SetMobile(true);
|
|
|
|
for (i = 0; i < N_HARP_CELS; i++) {
|
|
pSprite->PaintSprite(pDC, point);
|
|
AfxGetApp()->pause();
|
|
Sleep(100);
|
|
}
|
|
} else {
|
|
errCode = ERR_UNKNOWN;
|
|
}
|
|
} else {
|
|
errCode = ERR_UNKNOWN;
|
|
}
|
|
|
|
pSprite->EraseSprite(pDC);
|
|
delete pSprite;
|
|
AfxGetApp()->pause();
|
|
|
|
// create a dup of the master harpoon
|
|
//
|
|
if ((pSprite = m_pMasterHit->DuplicateSprite(pDC)) != nullptr) {
|
|
pSprite->LinkSprite();
|
|
pSprite->PaintSprite(pDC, point);
|
|
AfxGetApp()->pause();
|
|
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
|
|
ReleaseDC(pDC);
|
|
|
|
} else {
|
|
errCode = ERR_MEMORY;
|
|
}
|
|
|
|
HandleError(errCode);
|
|
}
|
|
|
|
|
|
int CBFishWindow::IndexToId(int nFishIndex) {
|
|
return 2 << nFishIndex;
|
|
}
|
|
|
|
|
|
int CBFishWindow::IdToIndex(int nId) {
|
|
int i;
|
|
|
|
assert(iseven(nId));
|
|
|
|
i = 0;
|
|
while (nId > 2) {
|
|
nId = nId >> 1;
|
|
i++;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
int CBFishWindow::GetEnemyGridIndex(CPoint point) {
|
|
CRect rect;
|
|
int nIndex;
|
|
int i, j;
|
|
int iVal, jVal;
|
|
|
|
nIndex = -1;
|
|
rect.SetRect(RGRID_MIN_X, RGRID_MIN_Y, RGRID_MAX_X, RGRID_MAX_Y);
|
|
|
|
if (rect.PtInRect(point)) {
|
|
|
|
// determine if the given point is in one of the grid sqaures
|
|
//
|
|
iVal = -1;
|
|
for (i = GRID_ROWS - 1; i >= 0; i--) {
|
|
|
|
if (point.y >= gRightGrid[i][0].y) {
|
|
iVal = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (iVal != -1) {
|
|
|
|
jVal = -1;
|
|
for (j = GRID_COLS - 1; j >= 0; j--) {
|
|
if (point.x >= gRightGrid[iVal][j].x) {
|
|
jVal = j;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (jVal != -1)
|
|
nIndex = (iVal * GRID_ROWS) + jVal;
|
|
}
|
|
}
|
|
|
|
return nIndex;
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
*
|
|
* DeleteSprite
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Erases, Unlinks, and Deletes specified sprite
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* CSprite *pSprite = Pointer to sprite that is to be deleted
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::DeleteSprite(CSprite *pSprite) {
|
|
CDC *pDC;
|
|
|
|
// can't delete a null pointer
|
|
assert(pSprite != nullptr);
|
|
|
|
if (pSprite != nullptr) {
|
|
|
|
if ((pDC = GetDC()) != nullptr) {
|
|
pSprite->EraseSprite(pDC); // erase it from screen
|
|
ReleaseDC(pDC);
|
|
}
|
|
pSprite->UnlinkSprite(); // unlink it
|
|
|
|
delete pSprite; // delete it
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
*
|
|
* OnSysChar
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Handles WM_SYSCHAR messages
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* unsigned int nChar = key that was pressed
|
|
* unsigned int nRepCnt = nunmber of times key was repeated
|
|
* unsigned int nFlags = ALT, CTRL, and SHFT key flags
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::OnSysChar(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags) {
|
|
// terminate app on ALT_Q
|
|
//
|
|
if ((nChar == 'q') && (nFlags & 0x2000)) {
|
|
|
|
PostMessage(WM_CLOSE, 0, 0);
|
|
|
|
} else {
|
|
|
|
// default action
|
|
CFrameWnd ::OnSysChar(nChar, nRepCnt, nFlags);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************
|
|
*
|
|
* OnSysKeyDown
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Handles WM_SYSKEYDOWN messages
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* unsigned int nChar = key that was pressed
|
|
* unsigned int nRepCnt = nunmber of times key was repeated
|
|
* unsigned int nFlags = ALT, CTRL, and SHFT key flags
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::OnSysKeyDown(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags) {
|
|
switch (nChar) {
|
|
|
|
// User has hit ALT_F4 so close down this App
|
|
//
|
|
case VK_F4:
|
|
PostMessage(WM_CLOSE, 0, 0);
|
|
break;
|
|
|
|
default:
|
|
CFrameWnd::OnSysKeyDown(nChar, nRepCnt, nFlags);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
*
|
|
* OnKeyDown
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Handles WM_KEYDOWN messages
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* unsigned int nChar = key that was pressed
|
|
* unsigned int nRepCnt = nunmber of times key was repeated
|
|
* unsigned int nFlags = ALT, CTRL, and SHFT key flags
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::OnKeyDown(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags) {
|
|
// Handle keyboard input
|
|
//
|
|
switch (nChar) {
|
|
|
|
//
|
|
// Bring up the Rules
|
|
//
|
|
case VK_F1: {
|
|
GamePause();
|
|
CSound::waitWaveSounds();
|
|
CRules RulesDlg(this, "bfish.txt", m_pGamePalette, (pGameParams->bSoundEffectsEnabled ? WAV_NARRATION : nullptr));
|
|
PaintScreen();
|
|
RulesDlg.DoModal();
|
|
GameResume();
|
|
}
|
|
break;
|
|
|
|
case VK_F2:
|
|
// Bring up the options menu
|
|
SendMessage(WM_COMMAND, IDC_MENU, BN_CLICKED);
|
|
break;
|
|
|
|
default:
|
|
CFrameWnd::OnKeyDown(nChar, nRepCnt, nFlags);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
*
|
|
* OnActivate
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Handles WM_ACTIVATE messages
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* unsigned int nState = WA_ACTIVE, WA_CLICKACTIVE or WA_INACTIVE
|
|
* CWnd *pWnd = Pointer to Window that is losing/gaining activation
|
|
* bool bMin = true if this app is minimized
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::OnActivate(unsigned int nState, CWnd *, bool bMinimized) {
|
|
if (!bMinimized) {
|
|
|
|
switch (nState) {
|
|
case WA_ACTIVE:
|
|
case WA_CLICKACTIVE:
|
|
//InvalidateRect(nullptr, false);
|
|
break;
|
|
|
|
case WA_INACTIVE:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CBFishWindow::FlushInputEvents() {
|
|
MSG msg;
|
|
|
|
// find and remove all keyboard events
|
|
//
|
|
while (true) {
|
|
if (!PeekMessage(&msg, nullptr, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))
|
|
break;
|
|
}
|
|
|
|
// find and remove all mouse events
|
|
//
|
|
while (true) {
|
|
if (!PeekMessage(&msg, nullptr, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
*
|
|
* OnClose
|
|
*
|
|
* FUNCTIONAL DESCRIPTION:
|
|
*
|
|
* Handles WM_CLOSE messages
|
|
*
|
|
* FORMAL PARAMETERS:
|
|
*
|
|
* None
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
****************************************************************/
|
|
void CBFishWindow::OnClose() {
|
|
CBrush myBrush;
|
|
CRect rMyRect;
|
|
CDC *pDC;
|
|
|
|
// perform cleanup
|
|
//
|
|
GameReset();
|
|
|
|
// delete the game theme song
|
|
//
|
|
if (m_pSoundTrack != nullptr) {
|
|
delete m_pSoundTrack;
|
|
m_pSoundTrack = nullptr;
|
|
}
|
|
|
|
CSound::clearSounds(); // Make sure to return cleanly to Metagame
|
|
|
|
// de-allocate the master sprites
|
|
ReleaseMasterSprites();
|
|
|
|
if (m_pTxtClickHere != nullptr) {
|
|
delete m_pTxtClickHere;
|
|
m_pTxtClickHere = nullptr;
|
|
}
|
|
|
|
//
|
|
// de-allocate any controls that we used
|
|
//
|
|
assert(m_pScrollSprite != nullptr);
|
|
if (m_pScrollSprite != nullptr) {
|
|
delete m_pScrollSprite;
|
|
m_pScrollSprite = nullptr;
|
|
}
|
|
|
|
//
|
|
// need to de-allocate the game palette
|
|
//
|
|
assert(m_pGamePalette != nullptr);
|
|
if (m_pGamePalette != nullptr) {
|
|
m_pGamePalette->DeleteObject();
|
|
delete m_pGamePalette;
|
|
m_pGamePalette = nullptr;
|
|
}
|
|
|
|
if ((pDC = GetDC()) != nullptr) { // paint black
|
|
|
|
rMyRect.SetRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
|
|
myBrush.CreateStockObject(BLACK_BRUSH);
|
|
pDC->FillRect(&rMyRect, &myBrush);
|
|
ReleaseDC(pDC);
|
|
}
|
|
|
|
CFrameWnd::OnClose();
|
|
|
|
MFC::PostMessage(ghParentWnd, WM_PARENTNOTIFY, WM_DESTROY, 0L);
|
|
}
|
|
|
|
//////////// Additional Sound Notify routines //////////////
|
|
|
|
LRESULT CBFishWindow::OnMCINotify(WPARAM wParam, LPARAM lParam) {
|
|
CSound *pSound;
|
|
|
|
pSound = CSound::OnMCIStopped(wParam, lParam);
|
|
if (pSound != nullptr)
|
|
OnSoundNotify(pSound);
|
|
return 0;
|
|
}
|
|
|
|
|
|
LRESULT CBFishWindow::OnMMIONotify(WPARAM wParam, LPARAM lParam) {
|
|
CSound *pSound;
|
|
|
|
pSound = CSound::OnMMIOStopped(wParam, lParam);
|
|
if (pSound != nullptr)
|
|
OnSoundNotify(pSound);
|
|
return 0;
|
|
}
|
|
|
|
void CBFishWindow::OnSoundNotify(CSound *) {
|
|
//
|
|
// Add your code to process explicit notification of a sound "done" event here.
|
|
// pSound is a pointer to a CSound object for which you requested SOUND_NOTIFY.
|
|
//
|
|
}
|
|
|
|
//
|
|
// CBFishWindow message map:
|
|
// Associate messages with member functions.
|
|
//
|
|
BEGIN_MESSAGE_MAP(CBFishWindow, CFrameWnd)
|
|
ON_WM_PAINT()
|
|
ON_WM_SYSCHAR()
|
|
ON_WM_KEYDOWN()
|
|
ON_WM_SYSKEYDOWN()
|
|
ON_WM_RBUTTONDOWN()
|
|
ON_WM_LBUTTONDOWN()
|
|
ON_WM_LBUTTONUP()
|
|
ON_WM_MOUSEMOVE()
|
|
ON_WM_CLOSE()
|
|
ON_MESSAGE(MM_MCINOTIFY, CBFishWindow::OnMCINotify)
|
|
ON_MESSAGE(MM_WOM_DONE, CBFishWindow::OnMMIONotify)
|
|
END_MESSAGE_MAP()
|
|
|
|
void CALLBACK GetGameParams(CWnd *pParentWnd) {
|
|
//
|
|
// Our user preference dialog box is self contained in this object
|
|
//
|
|
CUserCfgDlg dlgUserCfg(pParentWnd, pGamePalette, IDD_USERCFG);
|
|
}
|
|
|
|
} // namespace Battlefish
|
|
} // namespace HodjNPodj
|
|
} // namespace Bagel
|