mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
373 lines
11 KiB
C++
373 lines
11 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 "common/file.h"
|
|
#include "common/memstream.h"
|
|
|
|
#include "freescape/freescape.h"
|
|
#include "freescape/language/8bitDetokeniser.h"
|
|
|
|
namespace Freescape {
|
|
|
|
CastleEngine::CastleEngine(OSystem *syst, const ADGameDescription *gd) : FreescapeEngine(syst, gd) {
|
|
_playerHeightNumber = 1;
|
|
_playerHeights.push_back(16);
|
|
_playerHeights.push_back(48);
|
|
_playerHeight = _playerHeights[_playerHeightNumber];
|
|
|
|
_playerWidth = 8;
|
|
_playerDepth = 8;
|
|
_stepUpDistance = 32;
|
|
_option = nullptr;
|
|
}
|
|
|
|
CastleEngine::~CastleEngine() {
|
|
if (_option) {
|
|
_option->free();
|
|
delete _option;
|
|
}
|
|
}
|
|
|
|
byte kFreescapeCastleFont[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x1c, 0x1c, 0x1c, 0x18, 0x18, 0x00, 0x18, 0x18,
|
|
0x66, 0x66, 0x44, 0x22, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x00,
|
|
0x10, 0x54, 0x38, 0xfe, 0x38, 0x54, 0x10, 0x00,
|
|
0x3c, 0x42, 0x9d, 0xb1, 0xb1, 0x9d, 0x42, 0x3c,
|
|
0x78, 0xcc, 0xcc, 0x78, 0xdb, 0xcf, 0xce, 0x7b,
|
|
0x30, 0x30, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00,
|
|
0x10, 0x20, 0x40, 0x40, 0x40, 0x40, 0x20, 0x10,
|
|
0x10, 0x08, 0x04, 0x04, 0x04, 0x04, 0x08, 0x10,
|
|
0x10, 0x54, 0x38, 0xfe, 0x38, 0x54, 0x10, 0x00,
|
|
0x00, 0x00, 0x10, 0x10, 0x7c, 0x10, 0x10, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x08, 0x10,
|
|
0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
|
|
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
|
|
0x18, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x18,
|
|
0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
|
0x9e, 0x61, 0x01, 0x7e, 0xe0, 0xc6, 0xe3, 0xfe,
|
|
0xee, 0x73, 0x03, 0x3e, 0x03, 0x01, 0x7f, 0xe6,
|
|
0x0e, 0x1c, 0x38, 0x71, 0xfd, 0xe6, 0x0c, 0x0c,
|
|
0xfd, 0x86, 0x80, 0x7e, 0x07, 0x63, 0xc7, 0x7c,
|
|
0x3d, 0x66, 0xc0, 0xf0, 0xfc, 0xc6, 0x66, 0x3c,
|
|
0xb3, 0x4e, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x3c,
|
|
0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0xc2, 0xfe, 0x4c,
|
|
0x3c, 0x4e, 0xc6, 0xc6, 0x4e, 0x36, 0x46, 0x3c,
|
|
0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00,
|
|
0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x08, 0x10,
|
|
0x03, 0x0c, 0x30, 0xc0, 0x30, 0x0c, 0x03, 0x00,
|
|
0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00,
|
|
0xc0, 0x30, 0x0c, 0x03, 0x0c, 0x30, 0xc0, 0x00,
|
|
0x7c, 0xc6, 0x06, 0x0c, 0x30, 0x30, 0x00, 0x30,
|
|
0x00, 0x08, 0x0c, 0xfe, 0xff, 0xfe, 0x0c, 0x08,
|
|
0x1e, 0x1c, 0x1e, 0x66, 0xbe, 0x26, 0x43, 0xe3,
|
|
0xee, 0x73, 0x23, 0x3e, 0x23, 0x21, 0x7f, 0xe6,
|
|
0x39, 0x6e, 0xc6, 0xc0, 0xc0, 0xc2, 0x63, 0x3e,
|
|
0xec, 0x72, 0x23, 0x23, 0x23, 0x23, 0x72, 0xec,
|
|
0xce, 0x7f, 0x61, 0x6c, 0x78, 0x61, 0x7f, 0xce,
|
|
0xce, 0x7f, 0x61, 0x6c, 0x78, 0x60, 0x60, 0xf0,
|
|
0x3d, 0x66, 0xc0, 0xc1, 0xce, 0xc6, 0x66, 0x3c,
|
|
0xe7, 0x66, 0x66, 0x6e, 0x76, 0x66, 0x66, 0xe7,
|
|
0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x66,
|
|
0x33, 0x1e, 0x0c, 0x8c, 0x4c, 0xcc, 0xdc, 0x78,
|
|
0xf2, 0x67, 0x64, 0x68, 0x7e, 0x66, 0x66, 0xf3,
|
|
0xd8, 0x70, 0x60, 0x60, 0x66, 0x61, 0xf3, 0x7e,
|
|
0xc3, 0x66, 0x6e, 0x76, 0x56, 0x46, 0x46, 0xef,
|
|
0x87, 0x62, 0x72, 0x7a, 0x5e, 0x4e, 0x46, 0xe1,
|
|
0x18, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x18,
|
|
0xec, 0x72, 0x63, 0x63, 0x72, 0x6c, 0x60, 0xf0,
|
|
0x3c, 0x66, 0xc3, 0xc3, 0x66, 0x3c, 0x31, 0x1e,
|
|
0xec, 0x72, 0x63, 0x63, 0x76, 0x6c, 0x66, 0xf1,
|
|
0x79, 0x86, 0x80, 0x7e, 0x07, 0x63, 0xc7, 0x7c,
|
|
0x01, 0x7f, 0xfe, 0x98, 0x58, 0x18, 0x18, 0x3c,
|
|
0xf7, 0x62, 0x62, 0x62, 0x62, 0x62, 0xf2, 0x3c,
|
|
0xf3, 0x61, 0x72, 0x72, 0x32, 0x32, 0x1c, 0x3e,
|
|
0xc3, 0x62, 0x62, 0x6a, 0x6e, 0x76, 0x66, 0xc3,
|
|
0xf3, 0x72, 0x3c, 0x38, 0x1c, 0x3c, 0x4e, 0xcf,
|
|
0xe3, 0x72, 0x34, 0x38, 0x18, 0x18, 0x18, 0x3c,
|
|
0x7f, 0x87, 0x0e, 0x1c, 0x38, 0x71, 0xfd, 0xe6,
|
|
};
|
|
|
|
Common::SeekableReadStream *CastleEngine::decryptFile(const Common::String filename) {
|
|
Common::File file;
|
|
file.open(filename);
|
|
if (!file.isOpen())
|
|
error("Failed to open %s", filename.c_str());
|
|
|
|
int size = file.size();
|
|
byte *encryptedBuffer = (byte *)malloc(size);
|
|
file.read(encryptedBuffer, size);
|
|
file.close();
|
|
|
|
int seed = 24;
|
|
for (int i = 0; i < size; i++) {
|
|
encryptedBuffer[i] ^= seed;
|
|
seed = (seed + 1) & 0xff;
|
|
}
|
|
|
|
return (new Common::MemoryReadStream(encryptedBuffer, size));
|
|
}
|
|
|
|
extern byte kEGADefaultPalette[16][3];
|
|
|
|
void CastleEngine::loadAssetsDOSFullGame() {
|
|
Common::File file;
|
|
Common::SeekableReadStream *stream = nullptr;
|
|
|
|
if (_renderMode == Common::kRenderEGA) {
|
|
_viewArea = Common::Rect(40, 33, 280, 152);
|
|
|
|
file.open("CMLE.DAT");
|
|
_title = load8bitBinImage(&file, 0x0);
|
|
_title->setPalette((byte *)&kEGADefaultPalette, 0, 16);
|
|
file.close();
|
|
|
|
file.open("CMOE.DAT");
|
|
_option = load8bitBinImage(&file, 0x0);
|
|
_option->setPalette((byte *)&kEGADefaultPalette, 0, 16);
|
|
file.close();
|
|
|
|
file.open("CME.DAT");
|
|
_border = load8bitBinImage(&file, 0x0);
|
|
_border->setPalette((byte *)&kEGADefaultPalette, 0, 16);
|
|
file.close();
|
|
|
|
switch (_language) {
|
|
case Common::ES_ESP:
|
|
stream = decryptFile("CMLS");
|
|
break;
|
|
case Common::FR_FRA:
|
|
stream = decryptFile("CMLF");
|
|
break;
|
|
case Common::DE_DEU:
|
|
stream = decryptFile("CMLG");
|
|
break;
|
|
case Common::EN_ANY:
|
|
stream = decryptFile("CMLE");
|
|
break;
|
|
default:
|
|
error("Invalid or unsupported language: %x", _language);
|
|
}
|
|
|
|
loadFonts(kFreescapeCastleFont, 59);
|
|
loadMessagesVariableSize(stream, 0x11, 164);
|
|
delete stream;
|
|
|
|
stream = decryptFile("CMEDF");
|
|
load8bitBinary(stream, 0, 16);
|
|
for (auto &it : _areaMap)
|
|
it._value->addStructure(_areaMap[255]);
|
|
|
|
_areaMap[2]->addFloor();
|
|
delete stream;
|
|
} else
|
|
error("Not implemented yet");
|
|
// CPC
|
|
// file = gameDir.createReadStreamForMember("cm.bin");
|
|
// if (file == nullptr)
|
|
// error("Failed to open cm.bin");
|
|
// load8bitBinary(file, 0x791a, 16);
|
|
}
|
|
|
|
void CastleEngine::loadAssetsDOSDemo() {
|
|
Common::File file;
|
|
Common::SeekableReadStream *stream = nullptr;
|
|
|
|
if (_renderMode == Common::kRenderEGA) {
|
|
_viewArea = Common::Rect(40, 33, 280, 152);
|
|
|
|
file.open("CMLE.DAT");
|
|
_title = load8bitBinImage(&file, 0x0);
|
|
_title->setPalette((byte *)&kEGADefaultPalette, 0, 16);
|
|
file.close();
|
|
|
|
file.open("CMOE.DAT");
|
|
_option = load8bitBinImage(&file, 0x0);
|
|
_option->setPalette((byte *)&kEGADefaultPalette, 0, 16);
|
|
file.close();
|
|
|
|
file.open("CME.DAT");
|
|
_border = load8bitBinImage(&file, 0x0);
|
|
_border->setPalette((byte *)&kEGADefaultPalette, 0, 16);
|
|
file.close();
|
|
|
|
stream = decryptFile("CMLD"); // Only english
|
|
|
|
loadFonts(kFreescapeCastleFont, 59);
|
|
loadMessagesVariableSize(stream, 0x11, 164);
|
|
delete stream;
|
|
|
|
stream = decryptFile("CDEDF");
|
|
load8bitBinary(stream, 0, 16);
|
|
for (auto &it : _areaMap)
|
|
it._value->addStructure(_areaMap[255]);
|
|
|
|
_areaMap[2]->addFloor();
|
|
delete stream;
|
|
} else
|
|
error("Not implemented yet");
|
|
}
|
|
|
|
void CastleEngine::loadAssetsAmigaDemo() {
|
|
Common::File file;
|
|
file.open("x");
|
|
if (!file.isOpen())
|
|
error("Failed to open 'x' file");
|
|
|
|
loadMessagesVariableSize(&file, 0x8bb2, 164);
|
|
load8bitBinary(&file, 0x162a6, 16);
|
|
loadPalettes(&file, 0x151a6);
|
|
|
|
file.seek(0x2be96); // Area 255
|
|
_areaMap[255] = load8bitArea(&file, 16);
|
|
file.close();
|
|
|
|
|
|
_areaMap[2]->_groundColor = 1;
|
|
for (auto &it : _areaMap)
|
|
it._value->addStructure(_areaMap[255]);
|
|
|
|
_areaMap[2]->addFloor();
|
|
}
|
|
|
|
|
|
void CastleEngine::gotoArea(uint16 areaID, int entranceID) {
|
|
debugC(1, kFreescapeDebugMove, "Jumping to area: %d, entrance: %d", areaID, entranceID);
|
|
|
|
assert(_areaMap.contains(areaID));
|
|
_currentArea = _areaMap[areaID];
|
|
_currentArea->show();
|
|
|
|
if (entranceID > 0)
|
|
traverseEntrance(entranceID);
|
|
|
|
playSound(5, false);
|
|
_lastPosition = _position;
|
|
|
|
if (_currentArea->_skyColor > 0 && _currentArea->_skyColor != 255) {
|
|
_gfx->_keyColor = 0;
|
|
} else
|
|
_gfx->_keyColor = 255;
|
|
|
|
_lastPosition = _position;
|
|
_gameStateVars[0x1f] = 0;
|
|
|
|
if (areaID == _startArea && entranceID == _startEntrance) {
|
|
_yaw = 310;
|
|
_pitch = 0;
|
|
}
|
|
|
|
debugC(1, kFreescapeDebugMove, "starting player position: %f, %f, %f", _position.x(), _position.y(), _position.z());
|
|
clearTemporalMessages();
|
|
playSound(5, false);
|
|
// Ignore sky/ground fields
|
|
_gfx->_keyColor = 0;
|
|
_gfx->clearColorPairArray();
|
|
|
|
_gfx->_colorPair[_currentArea->_underFireBackgroundColor] = _currentArea->_extraColor[0];
|
|
_gfx->_colorPair[_currentArea->_usualBackgroundColor] = _currentArea->_extraColor[1];
|
|
_gfx->_colorPair[_currentArea->_paperColor] = _currentArea->_extraColor[2];
|
|
_gfx->_colorPair[_currentArea->_inkColor] = _currentArea->_extraColor[3];
|
|
|
|
swapPalette(areaID);
|
|
resetInput();
|
|
}
|
|
|
|
void CastleEngine::drawDOSUI(Graphics::Surface *surface) {
|
|
uint32 color = 10;
|
|
uint8 r, g, b;
|
|
|
|
_gfx->readFromPalette(color, r, g, b);
|
|
uint32 front = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
|
|
|
color = 0;
|
|
|
|
_gfx->readFromPalette(color, r, g, b);
|
|
uint32 back = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
|
|
|
Common::Rect backRect(97, 181, 232, 190);
|
|
surface->fillRect(backRect, back);
|
|
|
|
Common::String message;
|
|
int deadline;
|
|
getLatestMessages(message, deadline);
|
|
if (deadline <= _countdown) {
|
|
drawStringInSurface(message, 97, 182, front, back, surface);
|
|
_temporaryMessages.push_back(message);
|
|
_temporaryMessageDeadlines.push_back(deadline);
|
|
} else
|
|
drawStringInSurface(_currentArea->_name, 97, 182, front, back, surface);
|
|
}
|
|
|
|
void CastleEngine::initGameState() {
|
|
_flyMode = false;
|
|
_noClipMode = false;
|
|
_shootingFrames = 0;
|
|
_underFireFrames = 0;
|
|
_yaw = 0;
|
|
_pitch = 0;
|
|
|
|
for (int i = 0; i < k8bitMaxVariable; i++) // TODO: check maximum variable
|
|
_gameStateVars[i] = 0;
|
|
|
|
for (auto &it : _areaMap)
|
|
it._value->resetArea();
|
|
|
|
_gameStateBits = 0;
|
|
|
|
//_gameStateVars[k8bitVariableEnergy] = _initialFuel;
|
|
//_gameStateVars[k8bitVariableShield] = _initialShield;
|
|
|
|
_playerHeightNumber = 1;
|
|
_playerHeight = _playerHeights[_playerHeightNumber];
|
|
removeTimers();
|
|
startCountdown(_initialCountdown);
|
|
_lastMinute = 0;
|
|
_demoIndex = 0;
|
|
_demoEvents.clear();
|
|
}
|
|
|
|
void CastleEngine::executePrint(FCLInstruction &instruction) {
|
|
uint16 index = instruction._source;
|
|
_currentAreaMessages.clear();
|
|
if (index > 127) {
|
|
index = _messagesList.size() - (index - 254) - 2;
|
|
// TODO
|
|
//drawFullscreenMessage(_messagesList[index]);
|
|
return;
|
|
}
|
|
debugC(1, kFreescapeDebugCode, "Printing message %d: \"%s\"", index, _messagesList[index].c_str());
|
|
insertTemporaryMessage(_messagesList[index], _countdown - 3);
|
|
}
|
|
|
|
|
|
Common::Error CastleEngine::saveGameStreamExtended(Common::WriteStream *stream, bool isAutosave) {
|
|
return Common::kNoError;
|
|
}
|
|
|
|
Common::Error CastleEngine::loadGameStreamExtended(Common::SeekableReadStream *stream) {
|
|
return Common::kNoError;
|
|
}
|
|
|
|
} // End of namespace Freescape
|