scummvm/engines/hopkins/computer.cpp
D G Turner 2126bef17a HOPKINS: Further cleanup in ComputerManager class.
Have simplified the parsing of the COMPUTAN.TXT file prior to looking at
supporting the Polish file format variant.

These change should have no functional difference, but improve the code
by removing a set-but-unused bool in the MenuItem structure, fixing a
number of repeated "magic" values to be explicit as various buffer sizes
and replacing usage of strcpy with the safer version from our Common
code etc.
2014-05-17 15:09:46 +01:00

1255 lines
34 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "hopkins/computer.h"
#include "hopkins/font.h"
#include "hopkins/files.h"
#include "hopkins/globals.h"
#include "hopkins/graphics.h"
#include "hopkins/hopkins.h"
#include "hopkins/objects.h"
#include "common/system.h"
#include "common/file.h"
#include "common/textconsole.h"
namespace Hopkins {
ComputerManager::ComputerManager(HopkinsEngine *vm) {
_vm = vm;
for (int i = 0; i < ARRAYSIZE(_menuText); i++) {
_menuText[i]._lineSize = 0;
memset(_menuText[i]._line, 0, ARRAYSIZE(_menuText[0]._line));
}
Common::fill(&_inputBuf[0], &_inputBuf[200], '\0');
_breakoutSpr = NULL;
_textColor = 0;
_breakoutLevel = (int16 *)NULL;
_breakoutBrickNbr = 0;
_breakoutScore = 0;
_breakoutLives = 0;
_breakoutSpeed = 0;
_ballRightFl = false;
_ballUpFl = false;
_breakoutLevelNbr = 0;
_padPositionX = 0;
_minBreakoutMoveSpeed = 0;
_maxBreakoutMoveSpeed = 0;
_lastBreakoutMoveSpeed = 0;
_lowestHiScore = 0;
}
/**
* Sets up textual entry mode. Used by the code for Hopkins computer.
*/
void ComputerManager::setVideoMode() {
setTextMode();
}
/**
* Sets up Textual entry mode
*/
void ComputerManager::setTextMode() {
_vm->_graphicsMan->clearPalette();
_vm->_graphicsMan->clearScreen();
_vm->_graphicsMan->_lineNbr = SCREEN_WIDTH;
_vm->_fontMan->_font = _vm->_globals->freeMemory(_vm->_fontMan->_font);
Common::String filename = "STFONT.SPR";
Common::File f;
if (!f.exists(filename))
filename = "FONTE.SPR"; // Used by the BeOS and OS/2 versions as an alternative
_vm->_fontMan->_font = _vm->_fileIO->loadFile(filename);
_vm->_fontMan->_fontFixedWidth = 8;
_vm->_fontMan->_fontFixedHeight = 8;
_vm->_graphicsMan->loadImage("WINTEXT");
_vm->_graphicsMan->fadeInLong();
loadMenu();
_vm->_events->_mouseFl = false;
}
/**
* Clear the screen
*/
void ComputerManager::clearScreen() {
_vm->_graphicsMan->loadImage("WINTEXT");
_vm->_graphicsMan->fadeInLong();
}
/**
* Sets the text mode color
*/
void ComputerManager::setTextColor(int col) {
_textColor = col;
}
/**
* Sets the text position.
* @param yp Y position
* @param xp X position
* @remarks Yes, the reverse co-ordinate pair is really like that in the original game.
*/
void ComputerManager::setTextPosition(int yp, int xp) {
_textPosition.x = xp << 3;
_textPosition.y = yp << 4;
}
/**
* Show a computer in the FBI office
* @param mode Which computer to display
*/
void ComputerManager::showComputer(ComputerEnum mode) {
_vm->_events->_escKeyFl = false;
_vm->_graphicsMan->resetDirtyRects();
setVideoMode();
setTextColor(4);
setTextPosition(2, 4);
if (mode == COMPUTER_HOPKINS)
outText(Common::String(_menuText[0]._line));
else if (mode == COMPUTER_SAMANTHA)
outText(Common::String(_menuText[1]._line));
else // COMPUTER_PUBLIC
outText(Common::String(_menuText[2]._line));
setTextColor(1);
if (mode == COMPUTER_PUBLIC) {
setTextPosition(10, 8);
outText(Common::String(_menuText[3]._line));
}
setTextPosition(12, 28);
outText(Common::String(_menuText[4]._line));
setTextPosition(14, 35);
displayMessage(280, 224, 8);
bool passwordMatch = false;
if ((mode == COMPUTER_HOPKINS) && !strcmp(_inputBuf, "HOPKINS"))
passwordMatch = true;
else if ((mode == COMPUTER_SAMANTHA) && !strcmp(_inputBuf, "328MHZA"))
passwordMatch = true;
else if ((mode == COMPUTER_PUBLIC) && !strcmp(_inputBuf, "ALLFREE"))
passwordMatch = true;
if (passwordMatch) {
while (!_vm->shouldQuit()) {
_vm->_events->_escKeyFl = false;
clearScreen();
setTextColor(4);
setTextPosition(2, 4);
if (mode == COMPUTER_HOPKINS)
outText(Common::String(_menuText[0]._line));
else if (mode == COMPUTER_SAMANTHA)
outText(Common::String(_menuText[1]._line));
else if (mode == COMPUTER_PUBLIC)
outText(Common::String(_menuText[2]._line));
setTextColor(15);
setTextPosition(8, 25);
setTextColor(15);
outText2(Common::String(_menuText[6]._line));
setTextPosition(20, 25);
outText2(Common::String(_menuText[7]._line));
if (mode == COMPUTER_HOPKINS) {
setTextPosition(10, 25);
outText2(Common::String(_menuText[8]._line));
setTextPosition(12, 25);
outText2(Common::String(_menuText[9]._line));
setTextPosition(14, 25);
outText2(Common::String(_menuText[10]._line));
setTextPosition(16, 25);
outText2(Common::String(_menuText[11]._line));
} else if (mode == COMPUTER_SAMANTHA) {
setTextPosition(10, 25);
// outText2(Common::String(_menuText[0x95A])); <=== CHECKME: Unexpected value! replaced by the following line, for consistancy
outText2(Common::String(_menuText[12]._line));
setTextPosition(12, 25);
outText2(Common::String(_menuText[13]._line));
setTextPosition(14, 25);
outText2(Common::String(_menuText[14]._line));
setTextPosition(16, 25);
outText2(Common::String(_menuText[15]._line));
setTextPosition(18, 25);
outText2(Common::String(_menuText[16]._line));
}
bool numericFlag = false;
char keyPressed;
do {
keyPressed = _vm->_events->waitKeyPress();
if (_vm->shouldQuit())
return;
if ((keyPressed >= '0') && (keyPressed <= '9'))
numericFlag = true;
} while (!numericFlag);
// 0 - Quit
if (keyPressed == '0')
break;
// 1 - Games
if (keyPressed == '1') {
displayGamesSubMenu();
} else if (mode == COMPUTER_HOPKINS) {
clearScreen();
setTextColor(4);
setTextPosition(2, 4);
outText(Common::String(_menuText[0]._line));
setTextColor(15);
switch (keyPressed) {
case '2':
readText(1);
break;
case '3':
readText(2);
break;
case '4':
readText(3);
break;
case '5':
readText(4);
break;
}
} else if (mode == COMPUTER_SAMANTHA) {
clearScreen();
setTextColor(4);
setTextPosition(2, 4);
outText(Common::String(_menuText[1]._line));
setTextColor(15);
switch (keyPressed) {
case '2':
readText(6);
break;
case '3':
readText(7);
break;
case '4':
readText(8);
break;
case '5':
readText(9);
break;
case '6':
readText(10);
_vm->_globals->_saveData->_data[svField270] = 4;
break;
}
}
}
_vm->_graphicsMan->clearScreen();
_vm->_graphicsMan->updateScreen();
restoreFBIRoom();
} else {
// Password doesn't match - Access Denied
setTextColor(4);
setTextPosition(16, 25);
outText(Common::String(_menuText[5]._line));
_vm->_events->refreshScreenAndEvents();
_vm->_events->delay(1000);
memset(_vm->_graphicsMan->_frontBuffer, 0, 307199);
_vm->_graphicsMan->clearScreen();
_vm->_graphicsMan->updateScreen();
restoreFBIRoom();
_vm->_events->mouseOff();
}
if (mode == COMPUTER_HOPKINS)
_vm->_globals->_exitId = 13;
else // Free access or Samantha
_vm->_globals->_exitId = 14;
_vm->_graphicsMan->resetDirtyRects();
}
static const char _englishText[] =
"% ****** FBI COMPUTER NUMBER 4985 ****** J.HOPKINS COMPUTER ******\n"
"% ****** FBI COMPUTER NUMBER 4998 ****** S.COLLINS COMPUTER ******\n"
"% ****** FBI COMPUTER NUMBER 4997 ****** ACCES FREE COMPUTER ******\n"
"% PASSWORD IS: ALLFREE\n% ENTER CURRENT PASSWORD\n"
"% ****** ACCES DENIED ******\n"
"% 1) *** GAME ***\n"
"% 0) QUIT COMPUTER\n"
"% 2) STRANGE CADAVER\n"
"% 3) STRANGE CADAVER\n"
"% 4) SENATOR FERGUSSON\n"
"% 5) DOG KILLER\n"
"% 2) SCIENTIST KIDNAPPED.\n"
"% 3) SCIENTIST KIDNAPPED (next).\n"
"% 4) SCIENTIST KIDNAPPED (next).\n"
"% 5) SCIENTIST KIDNAPPED (next).\n"
"% 6) SCIENTIST KIDNAPPED (next).\n"
"%% fin\n";
static const char _frenchText[] =
"% ****** FBI COMPUTER NUMBER 4985 ****** J.HOPKINS COMPUTER ******\n"
"% ****** FBI COMPUTER NUMBER 4998 ****** S.COLLINS COMPUTER ******\n"
"% ****** FBI COMPUTER NUMBER 4997 ****** ACCES FREE COMPUTER ******\n"
"% PASSWORD IS: ALLFREE\n"
"% ENTER CURRENT PASSWORD\n"
"% ****** ACCES DENIED ******\n"
"% 1) *** CASSE BRIQUE ***\n"
"% 0) QUITTER L'ORDINATEUR\n"
"% 2) CADAVRE SANS TETE\n"
"% 3) CADAVRE SANS TETE\n"
"% 4) AGRESSION DU SENATEUR\n"
"% 5) LES CHIENS TUEURS\n"
"% 2) DISPARITIONS DE CHERCHEURS.\n"
"% 3) DISPARITIONS (suite).\n"
"% 4) DISPARITIONS (suite).\n"
"% 5) DISPARITIONS (suite).\n"
"% 6) DISPARITIONS (suite).\n"
"%% fin\n";
static const char _spanishText[] =
"% **** ORDENADOR DEL FBI NUMERO 4985 **** ORDENADOR J.HOPKINS *****\n"
"% **** ORDENADOR DEL FBI NUMERO 4998 **** ORDENADOR S.COLLINS *****\n"
"% *** ORDENADOR DEL FBI NUMERO 4997 *** ORDENADOR DE ACCESO LIBRE ***\n"
"% LA CONTRASE\0245A ES: ALLFREE\n"
"% ESCRIBE CONTRASE\0245A ACTUAL\n"
"% **** ACCESO DENEGADO ****\n"
"% 1) *** JUEGO ***\n"
"% 0) SALIR DEL ORDENADOR\n"
"% 2) CADAVER EXTRA\0245O\n"
"% 3) CADAVER EXTRA\0245O\n"
"% 4) SENADOR FERGUSSON\n"
"% 5) MATAPERROS\n"
"% 2) CIENTIFICO SECUESTRADO.\n"
"% 3) CIENTIFICO SECUESTRADO (siguiente).\n"
"% 4) CIENTIFICO SECUESTRADO (siguiente).\n"
"% 5) CIENTIFICO SECUESTRADO (siguiente).\n"
"% 6) CIENTIFICO SECUESTRADO (siguiente).\n"
"%% fin\n";
/**
* Load Menu data
*/
void ComputerManager::loadMenu() {
char *ptr;
if (_vm->_fileIO->fileExists("COMPUTAN.TXT")) {
ptr = (char *)_vm->_fileIO->loadFile("COMPUTAN.TXT");
} else {
switch (_vm->_globals->_language) {
case LANG_FR:
ptr = (char *)_vm->_globals->allocMemory(sizeof(_frenchText));
Common::strlcpy(ptr, _frenchText, sizeof(_frenchText));
break;
case LANG_SP:
ptr = (char *)_vm->_globals->allocMemory(sizeof(_spanishText));
Common::strlcpy(ptr, _spanishText, sizeof(_spanishText));
break;
default:
ptr = (char *)_vm->_globals->allocMemory(sizeof(_englishText));
Common::strlcpy(ptr, _englishText, sizeof(_englishText));
break;
}
}
char *tmpPtr = ptr;
int lineNum = 0;
while (tmpPtr[0] != '\0' && lineNum < ARRAYSIZE(_menuText)) {
if (tmpPtr[0] == '%' && tmpPtr[1] == '%') {
// End of file marker found - Break out of parse loop
break;
}
if (tmpPtr[0] == '%') {
int strPos = 0;
while (strPos < ARRAYSIZE(_menuText[0]._line)) {
char curChar = tmpPtr[strPos + 2];
if (curChar == '\0' || curChar == '%' || curChar == 0x0a) // Line Feed
break;
_menuText[lineNum]._line[strPos++] = curChar;
}
if (strPos < ARRAYSIZE(_menuText[0]._line)) {
_menuText[lineNum]._line[strPos] = 0;
_menuText[lineNum]._lineSize = strPos - 1;
}
++lineNum;
}
++tmpPtr;
}
_vm->_globals->freeMemory((byte *)ptr);
}
void ComputerManager::displayMessage(int xp, int yp, int textIdx) {
char curChar;
int x1 = xp;
int x2 = 0;
int textIndex = 0;
bool oldMouseFlag = _vm->_events->_mouseFl;
_vm->_events->_mouseFl = false;
_vm->_fontMan->displayTextVesa(xp, yp, "_", 252);
do {
curChar = _vm->_events->waitKeyPress();
if (_vm->shouldQuit())
return;
char mappedChar = '*';
if ((curChar == '-') || ((curChar >= '0') && (curChar <= '9')) || ((curChar >= 'A') && (curChar <= 'Z')))
mappedChar = curChar;
else if ((curChar >= 'a') && (curChar <= 'z'))
mappedChar = curChar - 32;
// BackSpace
if (curChar == 8 && textIndex > 0) {
_inputBuf[textIndex--] = 0;
x1 -= _vm->_fontMan->_fontFixedWidth;
x2 = x1 + 2 * _vm->_fontMan->_fontFixedWidth;
_vm->_graphicsMan->copyRect(_vm->_graphicsMan->_backBuffer, x1, yp, 3 * _vm->_fontMan->_fontFixedWidth, 12, _vm->_graphicsMan->_frontBuffer, x1, yp);
_vm->_graphicsMan->addDirtyRect(x1, yp, x2, yp + 12);
_vm->_fontMan->displayTextVesa(x1, yp, "_", 252);
}
if (mappedChar != '*') {
char newChar = mappedChar;
_vm->_graphicsMan->copyRect(_vm->_graphicsMan->_backBuffer, x1, yp, _vm->_fontMan->_fontFixedWidth, 12, _vm->_graphicsMan->_frontBuffer, x1, yp);
_vm->_graphicsMan->addDirtyRect(x1, yp, _vm->_fontMan->_fontFixedWidth + x1, yp + 12);
_inputBuf[textIndex] = newChar;
Common::String charString = Common::String::format("%c_", newChar);
_vm->_fontMan->displayTextVesa(x1, yp, charString, 252);
++textIndex;
x1 += _vm->_fontMan->_fontFixedWidth;
}
_vm->_events->refreshScreenAndEvents();
} while (textIndex != textIdx && curChar != 13);
_vm->_graphicsMan->copyRect(_vm->_graphicsMan->_backBuffer, x1, yp, _vm->_fontMan->_fontFixedWidth, 12, _vm->_graphicsMan->_frontBuffer, x1, yp);
_vm->_graphicsMan->addDirtyRect(x1, yp, _vm->_fontMan->_fontFixedWidth + x1, yp + 12);
_vm->_events->refreshScreenAndEvents();
_inputBuf[textIndex] = 0;
_vm->_events->_mouseFl = oldMouseFlag;
}
/**
* Outputs a text string
*/
void ComputerManager::outText(const Common::String &msg) {
_vm->_fontMan->renderTextDisplay(_textPosition.x, _textPosition.y, msg, _textColor);
}
/**
* Outputs a text string
*/
void ComputerManager::outText2(const Common::String &msg) {
_vm->_fontMan->displayTextVesa(_textPosition.x, _textPosition.y, msg, _textColor);
}
/**
* Restores the scene for the FBI headquarters room
*/
void ComputerManager::restoreFBIRoom() {
_vm->_graphicsMan->fadeOutShort();
_vm->_globals->freeMemory(_vm->_fontMan->_font);
_vm->_fontMan->_font = _vm->_fileIO->loadFile("FONTE3.SPR");
_vm->_fontMan->_fontFixedWidth = 12;
_vm->_fontMan->_fontFixedHeight = 21;
_vm->_events->_mouseFl = true;
}
/**
* Display texts for the given menu entry
*/
void ComputerManager::readText(int idx) {
_vm->_events->_escKeyFl = false;
Common::String filename;
switch (_vm->_globals->_language) {
case LANG_EN:
filename = "THOPKAN.TXT";
break;
case LANG_FR:
filename = "THOPK.TXT";
break;
case LANG_SP:
filename = "THOPKES.TXT";
break;
}
byte *ptr = _vm->_fileIO->loadFile(filename);
uint16 fileSize = _vm->_fileIO->fileSize(filename);
int pos;
for (pos = 0; pos < fileSize; pos++) {
if (ptr[pos] == '%') {
Common::String numStr = Common::String::format("%c%c", ptr[pos + 1], ptr[pos + 2]);
if (idx == atol(numStr.c_str()))
break;
}
}
if (pos > fileSize - 3)
error("Error with Hopkins computer file");
pos += 3;
int lineNum = 5;
Common::String curStr = "";
byte curChar;
do {
curChar = ptr[pos];
if (curChar == 13) {
setTextPosition(lineNum, 1);
outText(curStr);
++lineNum;
_vm->_events->refreshScreenAndEvents();
curStr = "";
} else if (curChar != '%') {
curStr += curChar;
}
++pos;
assert(pos <= fileSize);
} while (curChar != '%');
_vm->_events->waitKeyPress();
ptr = _vm->_globals->freeMemory(ptr);
}
/**
* Display breakout when Games sub-menu is selected
*/
void ComputerManager::displayGamesSubMenu() {
const byte *oldSpriteData = _vm->_objectsMan->_sprite[0]._spriteData;
uint oldSpeed = _vm->_globals->_speed;
_vm->_globals->_speed = 1;
_vm->_events->changeMouseCursor(0);
_breakoutSpr = NULL;
_vm->_events->_breakoutFl = true;
_breakoutLevel = (int16 *)NULL;
_breakoutBrickNbr = 0;
_breakoutScore = 0;
_breakoutLives = 5;
_breakoutSpeed = 1;
_ballRightFl = false;
_ballUpFl = false;
_breakoutLevelNbr = 0;
_vm->_graphicsMan->_minY = 0;
_vm->_graphicsMan->_maxX = 320;
_vm->_graphicsMan->_maxY = 200;
_vm->_soundMan->loadSample(1, "SOUND37.WAV");
_vm->_soundMan->loadSample(2, "SOUND38.WAV");
_vm->_soundMan->loadSample(3, "SOUND39.WAV");
_breakoutSpr = _vm->_fileIO->loadFile("CASSE.SPR");
loadHiscore();
setModeVGA256();
newLevel();
_vm->_graphicsMan->updateScreen();
playBreakout();
_vm->_graphicsMan->resetDirtyRects();
_breakoutSpr = _vm->_globals->freeMemory(_breakoutSpr);
_breakoutLevel = (int16 *)_vm->_globals->freeMemory((byte *)_breakoutLevel);
_vm->_objectsMan->_sprite[0]._spriteData = oldSpriteData;
_vm->_soundMan->removeSample(1);
_vm->_soundMan->removeSample(2);
_vm->_soundMan->removeSample(3);
_vm->_globals->_speed = oldSpeed;
_vm->_events->_breakoutFl = false;
setVideoMode();
setTextColor(15);
clearScreen();
_vm->_graphicsMan->_maxX = 680;
_vm->_graphicsMan->_minY = 0;
_vm->_graphicsMan->_maxY = 460;
}
/**
* Load Highscore from file
*/
void ComputerManager::loadHiscore() {
byte *ptr = _vm->_globals->allocMemory(100);
memset(ptr, 0, 100);
if (_vm->_saveLoad->saveExists(_vm->getTargetName() + "-highscore.dat"))
_vm->_saveLoad->load(_vm->getTargetName() + "-highscore.dat", ptr);
for (int scoreIndex = 0; scoreIndex < 6; ++scoreIndex) {
_score[scoreIndex]._name = " ";
_score[scoreIndex]._score = " ";
for (int i = 0; i < 6; ++i) {
char nextChar = ptr[(16 * scoreIndex) + i];
if (!nextChar)
nextChar = ' ';
_score[scoreIndex]._name.setChar(nextChar, i);
}
for (int i = 0; i < 9; ++i) {
char nextChar = ptr[(scoreIndex * 16) + 6 + i];
if (!nextChar)
nextChar = '0';
_score[scoreIndex]._score.setChar(nextChar, i);
}
}
_lowestHiScore = atol(_score[5]._score.c_str());
_vm->_globals->freeMemory(ptr);
}
/**
* VGA 256 col
*/
void ComputerManager::setModeVGA256() {
_vm->_graphicsMan->clearScreen();
_vm->_graphicsMan->clearPalette();
_vm->_graphicsMan->setScreenWidth(320);
}
/**
* Load new level
*/
void ComputerManager::newLevel() {
_vm->_objectsMan->removeSprite(0);
_vm->_objectsMan->removeSprite(1);
++_breakoutLives;
if (_breakoutLives > 11)
_breakoutLives = 11;
_vm->_graphicsMan->loadVgaImage("CASSEF.PCX");
displayLives();
_breakoutLevel = (int16 *)_vm->_globals->freeMemory((byte *)_breakoutLevel);
++_breakoutLevelNbr;
Common::String file;
Common::File f;
while (!_vm->shouldQuit()) {
file = Common::String::format("TAB%d.TAB", _breakoutLevelNbr);
if (f.open(file))
break;
_breakoutLevelNbr = 1;
}
f.close();
_breakoutLevel = (int16 *)_vm->_fileIO->loadFile(file);
displayBricks();
_vm->_objectsMan->addStaticSprite(_breakoutSpr, Common::Point(150, 192), 0, 13, 0, false, 0, 0);
_vm->_objectsMan->addStaticSprite(_breakoutSpr, Common::Point(164, 187), 1, 14, 0, false, 0, 0);
_ballPosition = Common::Point(164, 187);
_padPositionX = 150;
_vm->_objectsMan->animateSprite(0);
_vm->_objectsMan->animateSprite(1);
_vm->_events->mouseOn();
_vm->_soundMan->playSample(3, 5);
}
/**
* Display bricks in breakout game
*/
void ComputerManager::displayBricks() {
_breakoutBrickNbr = 0;
_breakoutSpeed = 1;
int16 *level = _breakoutLevel;
for (int levelIdx = 0; ; levelIdx += 6) {
int cellLeft = (int16)FROM_LE_16(level[levelIdx]);
if (cellLeft == -1)
break;
int cellTop = FROM_LE_16(level[levelIdx + 1]);
int cellType = FROM_LE_16(level[levelIdx + 4]);
if (cellType <= 6)
++_breakoutBrickNbr;
switch (cellType) {
case 1:
_vm->_graphicsMan->fastDisplay2(_breakoutSpr, cellLeft, cellTop, 21);
break;
case 2:
_vm->_graphicsMan->fastDisplay2(_breakoutSpr, cellLeft, cellTop, 22);
break;
case 3:
_vm->_graphicsMan->fastDisplay2(_breakoutSpr, cellLeft, cellTop, 17);
break;
case 4:
_vm->_graphicsMan->fastDisplay2(_breakoutSpr, cellLeft, cellTop, 20);
break;
case 5:
_vm->_graphicsMan->fastDisplay2(_breakoutSpr, cellLeft, cellTop, 19);
break;
case 6:
_vm->_graphicsMan->fastDisplay2(_breakoutSpr, cellLeft, cellTop, 18);
break;
case 31:
_vm->_graphicsMan->fastDisplay2(_breakoutSpr, cellLeft, cellTop, 23);
break;
}
}
displayScore();
// Refresh the entire screen
_vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
_vm->_graphicsMan->updateScreen();
}
/**
* Display Lives in breakout game
*/
void ComputerManager::displayLives() {
for (int i = 0, xp = 10; i <= 11; i++, xp += 7)
_vm->_graphicsMan->fastDisplay2(_breakoutSpr, xp, 10, 15);
for (int i = 0, xp = 10; i < _breakoutLives - 1; i++, xp += 7)
_vm->_graphicsMan->fastDisplay2(_breakoutSpr, xp, 10, 14);
_vm->_graphicsMan->updateScreen();
}
/**
* Main function for breakout game
*/
void ComputerManager::playBreakout() {
int lastBreakoutEvent = 0;
while (!_vm->shouldQuit()) {
while (!_vm->shouldQuit()) {
// Set up the racket and ball
_vm->_events->mouseOff();
_ballPosition = Common::Point(_padPositionX + 14, 187);
_vm->_objectsMan->setSpriteY(1, 187);
_vm->_objectsMan->setSpriteX(1, _ballPosition.x);
_vm->_graphicsMan->resetDirtyRects();
_vm->_events->refreshScreenAndEvents();
_vm->_graphicsMan->fadeInBreakout();
// Wait for mouse press to start playing
do {
_padPositionX = _vm->_events->getMouseX();
if (_vm->_events->_mousePos.x <= 4)
_padPositionX = 5;
if (_padPositionX > 282)
_padPositionX = 282;
_vm->_objectsMan->setSpriteX(0, _padPositionX);
_vm->_objectsMan->setSpriteX(1, _padPositionX + 14);
_vm->_objectsMan->setSpriteY(1, 187);
_vm->_events->refreshScreenAndEvents();
} while (!_vm->shouldQuit() && _vm->_events->getMouseButton() != 1);
_breakoutSpeed = 1;
_ballPosition = Common::Point(_padPositionX + 14, 187);
_ballRightFl = (_padPositionX > 135);
_ballUpFl = false;
// Play loop
do {
_vm->_soundMan->checkSounds();
_padPositionX = _vm->_events->getMouseX();
if (_vm->_events->_mousePos.x <= 4)
_padPositionX = 5;
if (_padPositionX > 282)
_padPositionX = 282;
_vm->_objectsMan->setSpriteX(0, _padPositionX);
lastBreakoutEvent = moveBall();
_vm->_events->refreshScreenAndEvents();
} while (!_vm->shouldQuit() && !lastBreakoutEvent);
if (lastBreakoutEvent != 1)
break;
--_breakoutLives;
if (_breakoutLives) {
displayLives();
if (_breakoutLives)
continue;
}
_vm->_graphicsMan->fadeOutBreakout();
_vm->_events->mouseOn();
_vm->_objectsMan->removeSprite(0);
_vm->_objectsMan->removeSprite(1);
if (_breakoutScore > _lowestHiScore)
getScoreName();
if (displayHiscores() != 1)
break;
_breakoutBrickNbr = 0;
_breakoutScore = 0;
_breakoutLives = 4;
_breakoutSpeed = 1;
_ballRightFl = false;
_ballUpFl = false;
_breakoutLevelNbr = 0;
loadHiscore();
newLevel();
}
if (lastBreakoutEvent != 2)
return;
_vm->_graphicsMan->fadeOutBreakout();
newLevel();
}
}
/**
* Show the high scores for the Breakout game
* @return The selected button index: 1 = Game, 2 = Quit
*/
int ComputerManager::displayHiscores() {
_vm->_graphicsMan->resetDirtyRects();
loadHiscore();
_vm->_graphicsMan->loadVgaImage("HISCORE.PCX");
byte *ptr = _vm->_fileIO->loadFile("ALPHA.SPR");
_vm->_graphicsMan->setColorPercentage(252, 100, 100, 100);
_vm->_graphicsMan->setColorPercentage(253, 100, 100, 100);
_vm->_graphicsMan->setColorPercentage(251, 100, 100, 100);
_vm->_graphicsMan->setColorPercentage(254, 0, 0, 0);
int yp;
// Loop for displaying the scores
for (int scoreIndex = 0; scoreIndex <= 5; scoreIndex++) {
yp = 19 * scoreIndex;
yp += 46;
// Display the characters of the name
for (int i = 0; i < 6; i++)
displayHiscoreLine(ptr, 9 * i + 69, yp, _score[scoreIndex]._name[i]);
// Display the digits of the score
for (int i = 0; i < 9; i++)
displayHiscoreLine(ptr, 9 * i + 199, yp, _score[scoreIndex]._score[i]);
}
_vm->_graphicsMan->fadeInBreakout();
_vm->_graphicsMan->resetDirtyRects();
int buttonIndex = 0;
do {
_vm->_events->refreshEvents();
int xp = _vm->_events->getMouseX();
yp = _vm->_events->getMouseY();
if (_vm->_events->getMouseButton() == 1 && ABS(xp - 79) <= 33 && ABS(yp - 396) <= 13)
buttonIndex = 1;
else if (_vm->_events->getMouseButton() == 1 && ABS(xp - 583) <= 32 && ABS(yp - 396) <= 13)
buttonIndex = 2;
_vm->_events->refreshScreenAndEvents();
} while (!buttonIndex && !_vm->shouldQuit());
_vm->_events->mouseOff();
_vm->_graphicsMan->fadeOutBreakout();
_vm->_globals->freeMemory(ptr);
return buttonIndex;
}
/**
* Display a screen to enter player name in the case of a new hiscore
*/
void ComputerManager::getScoreName() {
_vm->_graphicsMan->loadVgaImage("NAME.PCX");
_vm->_graphicsMan->setColorPercentage(252, 100, 100, 100);
_vm->_graphicsMan->setColorPercentage(253, 100, 100, 100);
_vm->_graphicsMan->setColorPercentage(251, 100, 100, 100);
_vm->_graphicsMan->setColorPercentage(254, 0, 0, 0);
byte *ptr = _vm->_fileIO->loadFile("ALPHA.SPR");
_vm->_graphicsMan->fadeInBreakout();
// Figure out the line to put the new high score on
int scoreLine = 0;
while (scoreLine < 5 && _breakoutScore < atol(_score[scoreLine]._score.c_str()))
++scoreLine;
// If it's not the lasat line, move the lines down
for (int line = 5; line > scoreLine; --line) {
_score[line]._name = _score[line - 1]._name;
_score[line]._score = _score[line - 1]._score;
}
// Get the name for the new high score
for (int strPos = 0; strPos <= 4; strPos++) {
displayHiscoreLine(ptr, 9 * strPos + 140, 78, 1);
char curChar = toupper(_vm->_events->waitKeyPress());
if ((curChar < '0') || (curChar > 'Z'))
curChar = ' ';
if ((curChar > '9') && (curChar < 'A'))
curChar = ' ';
_score[scoreLine]._name.setChar(curChar, strPos);
displayHiscoreLine(ptr, 9 * strPos + 140, 78, curChar);
for (int idx = 0; idx < 12; ++idx)
_vm->_events->refreshScreenAndEvents();
}
// Set up the new score
_score[scoreLine]._score = " ";
char score[16];
sprintf(score, "%d", _breakoutScore);
int scoreLen = 0;
do
++scoreLen;
while (score[scoreLen]);
for (int i = scoreLen - 1, scorePos = 8; i >= 0; i--) {
_score[scoreLine]._score.setChar(score[i], scorePos--);
}
_vm->_graphicsMan->fadeOutBreakout();
_vm->_globals->freeMemory(ptr);
saveScore();
}
/**
* Display current score
*/
void ComputerManager::displayScore() {
Common::String scoreStr = Common::String::format("%d", _breakoutScore);
int strSize = scoreStr.size();
for (int i = strSize - 1, idx = 0; i >= 0; i--) {
displayScoreChar(idx++, scoreStr[i]);
}
}
/**
* Display a character of the score
*/
void ComputerManager::displayScoreChar(int charPos, int charDisp) {
int xp;
switch (charPos) {
case 1:
xp = 190;
break;
case 2:
xp = 180;
break;
case 3:
xp = 167;
break;
case 4:
xp = 157;
break;
case 5:
xp = 147;
break;
case 9:
xp = 134;
break;
default:
xp = 200;
break;
}
int idx = 3;
if (charDisp >= '0' && charDisp <= '9')
idx = charDisp - 45;
_vm->_graphicsMan->fastDisplay2(_breakoutSpr, xp, 11, idx);
}
/**
* Save Hiscore in file
*/
void ComputerManager::saveScore() {
int scores[6];
// Load high scores in an array
for (int i = 0; i <= 5; i++) {
scores[i] = atol(_score[i]._score.c_str());
if (!scores[i])
scores[i] = 5;
}
int scorePlace[6];
// order high scores
for (int scorePlaceIdx = 0; scorePlaceIdx <= 5; scorePlaceIdx++) {
for(int i = 0;;i++) {
int curScore = scores[i];
if (curScore && scores[0] <= curScore && scores[1] <= curScore && scores[2] <= curScore && scores[3] <= curScore
&& scores[4] <= curScore && scores[5] <= curScore) {
scorePlace[scorePlaceIdx] = i;
scores[i] = 0;
break;
}
}
}
byte *ptr = _vm->_globals->allocMemory(100);
memset(ptr, 0, 100);
for (int scorePlaceIdx = 0; scorePlaceIdx <= 5; scorePlaceIdx++) {
int curBufPtr = 16 * scorePlaceIdx;
for (int namePos = 0; namePos < 6; namePos++) {
char curChar = _score[scorePlace[scorePlaceIdx]]._name[namePos];
if (!curChar)
curChar = ' ';
ptr[curBufPtr + namePos] = curChar;
};
ptr[curBufPtr + 5] = 0;
for (int scorePos = 0; scorePos <= 8; scorePos++) {
char curChar = _score[scorePlace[scorePlaceIdx]]._score[scorePos];
if (!curChar)
curChar = '0';
ptr[curBufPtr + 6 + scorePos] = curChar;
};
ptr[curBufPtr + 15] = 0;
}
_vm->_saveLoad->saveFile(_vm->getTargetName() + "-highscore.dat", ptr, 100);
_vm->_globals->freeMemory(ptr);
}
/**
* Display parts of the hiscore line
*/
void ComputerManager::displayHiscoreLine(const byte *objectData, int x, int y, int curChar) {
int idx = 36;
if (curChar == 100)
idx = 0;
else if (curChar >= '0' && curChar <= '9')
idx = curChar - '0';
else if (curChar >= 'A' && curChar <= 'Z')
idx = curChar - 'A' + 10;
else if (curChar == 1)
idx = 37;
_vm->_graphicsMan->fastDisplay2(objectData, x, y, idx);
}
/**
* Handle ball moves
*/
int ComputerManager::moveBall() {
//(signed int)(6.0 * (long double)_vm->getRandomNumber( rand() / 2147483648.0) + 1;
// TODO: Figure out random number
int randVal = _vm->getRandomNumber(6);
switch (_breakoutSpeed) {
case 1:
_minBreakoutMoveSpeed = 1;
_maxBreakoutMoveSpeed = 1;
break;
case 2:
_minBreakoutMoveSpeed = 1;
_maxBreakoutMoveSpeed = 2;
break;
case 3:
_minBreakoutMoveSpeed = 2;
_maxBreakoutMoveSpeed = 2;
break;
case 4:
_minBreakoutMoveSpeed = 3;
_maxBreakoutMoveSpeed = 2;
break;
}
int moveSpeed = _minBreakoutMoveSpeed;
if (_lastBreakoutMoveSpeed == _minBreakoutMoveSpeed)
moveSpeed = _maxBreakoutMoveSpeed;
if (_ballUpFl)
_ballPosition.y += moveSpeed;
else
_ballPosition.y -= moveSpeed;
if (_ballRightFl)
_ballPosition.x += moveSpeed;
else
_ballPosition.x -= moveSpeed;
_lastBreakoutMoveSpeed = moveSpeed;
if (_ballPosition.x <= 6) {
_vm->_soundMan->playSample(2, 6);
_ballPosition.x = randVal + 6;
_ballRightFl = !_ballRightFl;
} else if (_ballPosition.x > 307) {
_vm->_soundMan->playSample(2, 6);
_ballPosition.x = 307 - randVal;
_ballRightFl = !_ballRightFl;
}
if (_ballPosition.y <= 6) {
_vm->_soundMan->playSample(2, 6);
_ballPosition.y = randVal + 7;
_ballUpFl = !_ballUpFl;
} else if (_ballPosition.y >= 186 && _ballPosition.y <= 194) {
_vm->_soundMan->playSample(2, 6);
int ballPosXRight = _ballPosition.x + 6;
if ((_ballPosition.x > _padPositionX - 2) && (ballPosXRight < _padPositionX + 36)) {
_ballUpFl = false;
if (ballPosXRight <= _padPositionX + 15) {
_ballRightFl = false;
if (_ballPosition.x >= _padPositionX && ballPosXRight <= _padPositionX + 5)
_ballPosition.x -= 4;
if (_ballPosition.x >= _padPositionX + 5 && _ballPosition.x + 6 <= _padPositionX + 10)
_ballPosition.x -= 2;
}
if (_ballPosition.x >= _padPositionX + 19 && _ballPosition.x + 6 <= _padPositionX + 36) {
_ballRightFl = true;
if (_ballPosition.x >= _padPositionX + 29)
_ballPosition.x += 4;
if (_ballPosition.x >= _padPositionX + 24 && _ballPosition.x + 6 <= _padPositionX + 29)
_ballPosition.x += 2;
}
}
}
int retVal = 0;
if (_ballPosition.y > 194)
retVal = 1;
checkBallCollisions();
_vm->_objectsMan->setSpriteX(1, _ballPosition.x);
_vm->_objectsMan->setSpriteY(1, _ballPosition.y);
if (!_breakoutBrickNbr)
retVal = 2;
return retVal;
}
/**
* Check ball collision with bricks
*/
void ComputerManager::checkBallCollisions() {
int cellLeft;
bool brickDestroyedFl = false;
// TODO: Check if correct
int randVal = _vm->getRandomNumber(6) + 1;
int ballLeft = _ballPosition.x;
int ballTop = _ballPosition.y;
int ballRight = _ballPosition.x + 6;
int ballBottom = _ballPosition.y + 6;
int16 *level = _breakoutLevel;
uint16 levelIdx = 0;
do {
cellLeft = level[levelIdx];
int cellUp = level[levelIdx + 1];
int cellRight = level[levelIdx + 2];
int cellBottom = level[levelIdx + 3];
int cellType = level[levelIdx + 4];
if (level[levelIdx + 5] == 1 && cellLeft != -1) {
bool collisionFl = false;
if (ballTop <= cellBottom && ballBottom >= cellBottom) {
if (ballLeft >= cellLeft && ballRight <= cellRight) {
collisionFl = true;
_ballUpFl = true;
}
if ((ballRight >= cellLeft) && (ballLeft <= cellLeft)) {
collisionFl = true;
_ballUpFl = true;
_ballRightFl = false;
if (cellType == 31)
_ballPosition.x -= randVal;
}
if ((ballLeft <= cellRight) && (ballRight >= cellRight)) {
collisionFl = true;
_ballUpFl = true;
_ballRightFl = true;
if (cellType == 31)
_ballPosition.x += randVal;
}
}
if (ballBottom >= cellUp && ballTop <= cellUp) {
if (ballLeft >= cellLeft && ballRight <= cellRight) {
collisionFl = true;
_ballUpFl = false;
}
if ((ballRight >= cellLeft) && (ballLeft <= cellLeft)) {
collisionFl = true;
_ballUpFl = false;
_ballRightFl = false;
if (cellType == 31)
_ballPosition.x -= 2;
}
if ((ballLeft <= cellRight) && (ballRight >= cellRight)) {
collisionFl = true;
_ballUpFl = false;
_ballRightFl = true;
if (cellType == 31)
_ballPosition.x += randVal;
}
}
if ((ballTop >= cellUp) && (ballBottom <= cellBottom)) {
if ((ballRight >= cellLeft) && (ballLeft <= cellLeft)) {
collisionFl = true;
_ballRightFl = false;
if (cellType == 31)
_ballPosition.x -= randVal;
}
if ((ballLeft <= cellRight) && (ballRight >= cellRight)) {
collisionFl = true;
_ballRightFl = true;
if (cellType == 31)
_ballPosition.x += randVal;
}
}
if (collisionFl) {
if (cellType == 31) {
_vm->_soundMan->playSample(2, 6);
} else {
_vm->_soundMan->playSample(1, 5);
_vm->_graphicsMan->fastDisplay2(_breakoutSpr, cellLeft, cellUp, 16);
switch (cellType) {
case 1:
_breakoutScore += 10;
break;
case 2:
_breakoutScore += 5;
break;
case 3:
_breakoutScore += 50;
if (_breakoutSpeed <= 1)
_breakoutSpeed = 2;
if (_breakoutBrickNbr <= 19)
_breakoutSpeed = 3;
break;
case 4:
_breakoutScore += 20;
break;
case 5:
_breakoutScore += 30;
if (_breakoutSpeed <= 1)
_breakoutSpeed = 2;
break;
case 6:
_breakoutScore += 40;
break;
}
displayScore();
--_breakoutBrickNbr;
level[levelIdx + 5] = 0;
brickDestroyedFl = true;
}
}
}
if (brickDestroyedFl)
cellLeft = -1;
levelIdx += 6;
} while (cellLeft != -1);
}
} // End of namespace Hopkins