mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
1722 lines
56 KiB
C++
1722 lines
56 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/config-manager.h"
|
|
#include "common/events.h"
|
|
#include "common/file.h"
|
|
#include "common/memstream.h"
|
|
#include "common/random.h"
|
|
|
|
#include "freescape/freescape.h"
|
|
#include "freescape/language/8bitDetokeniser.h"
|
|
|
|
namespace Freescape {
|
|
|
|
enum {
|
|
kDrillerCGAPalettePinkBlue = 0,
|
|
kDrillerCGAPaletteRedGreen = 1,
|
|
};
|
|
|
|
static const struct CGAPalettteEntry {
|
|
int areaId;
|
|
int palette;
|
|
} rawCGAPaletteTable[] {
|
|
{1, kDrillerCGAPaletteRedGreen},
|
|
{2, kDrillerCGAPalettePinkBlue},
|
|
{3, kDrillerCGAPaletteRedGreen},
|
|
{4, kDrillerCGAPalettePinkBlue},
|
|
{5, kDrillerCGAPaletteRedGreen},
|
|
{6, kDrillerCGAPalettePinkBlue},
|
|
{7, kDrillerCGAPaletteRedGreen},
|
|
{8, kDrillerCGAPalettePinkBlue},
|
|
{9, kDrillerCGAPaletteRedGreen},
|
|
{10, kDrillerCGAPalettePinkBlue},
|
|
{11, kDrillerCGAPaletteRedGreen},
|
|
{12, kDrillerCGAPalettePinkBlue},
|
|
|
|
{14, kDrillerCGAPalettePinkBlue},
|
|
|
|
{16, kDrillerCGAPalettePinkBlue},
|
|
|
|
{19, kDrillerCGAPaletteRedGreen},
|
|
{20, kDrillerCGAPalettePinkBlue},
|
|
{21, kDrillerCGAPaletteRedGreen},
|
|
{22, kDrillerCGAPalettePinkBlue},
|
|
{23, kDrillerCGAPaletteRedGreen},
|
|
|
|
{28, kDrillerCGAPalettePinkBlue},
|
|
|
|
{32, kDrillerCGAPalettePinkBlue},
|
|
{127, kDrillerCGAPaletteRedGreen},
|
|
{0, 0} // This marks the end
|
|
};
|
|
|
|
byte kDrillerCGAPalettePinkBlueData[4][3] = {
|
|
{0x00, 0x00, 0x00},
|
|
{0x00, 0xaa, 0xaa},
|
|
{0xaa, 0x00, 0xaa},
|
|
{0xaa, 0xaa, 0xaa},
|
|
};
|
|
|
|
byte kDrillerCGAPaletteRedGreenData[4][3] = {
|
|
{0x00, 0x00, 0x00},
|
|
{0x00, 0xaa, 0x00},
|
|
{0xaa, 0x00, 0x00},
|
|
{0xaa, 0x55, 0x00},
|
|
};
|
|
|
|
enum {
|
|
kDrillerNoRig = 0,
|
|
kDrillerRigInPlace = 1,
|
|
kDrillerRigOutOfPlace = 2,
|
|
kDrillerRigNoGas = 3
|
|
};
|
|
|
|
DrillerEngine::DrillerEngine(OSystem *syst, const ADGameDescription *gd) : FreescapeEngine(syst, gd) {
|
|
|
|
if (!Common::parseBool(ConfMan.get("automatic_drilling"), _useAutomaticDrilling))
|
|
error("Failed to parse bool from automatic_drilling option");
|
|
|
|
if (isDOS()) {
|
|
if (_renderMode == Common::kRenderEGA)
|
|
_viewArea = Common::Rect(40, 16, 280, 117);
|
|
else if (_renderMode == Common::kRenderCGA)
|
|
_viewArea = Common::Rect(36, 16, 284, 117);
|
|
else
|
|
error("Invalid or unknown render mode");
|
|
}
|
|
else if (isAmiga() || isAtariST())
|
|
_viewArea = Common::Rect(36, 16, 284, 118);
|
|
else if (isSpectrum())
|
|
_viewArea = Common::Rect(58, 20, 266, 124);
|
|
else if (isCPC())
|
|
_viewArea = Common::Rect(36, 19, 284, 120);
|
|
else if (isC64())
|
|
_viewArea = Common::Rect(32, 16, 288, 119);
|
|
|
|
_playerHeightNumber = 1;
|
|
_playerHeights.push_back(16);
|
|
_playerHeights.push_back(48);
|
|
_playerHeights.push_back(80);
|
|
_playerHeights.push_back(112);
|
|
|
|
_angleRotations.push_back(5);
|
|
_angleRotations.push_back(10);
|
|
_angleRotations.push_back(15);
|
|
_angleRotations.push_back(30);
|
|
_angleRotations.push_back(45);
|
|
_angleRotations.push_back(90);
|
|
|
|
_playerHeight = _playerHeights[_playerHeightNumber];
|
|
_playerWidth = 12;
|
|
_playerDepth = 32;
|
|
|
|
_initialTankEnergy = 48;
|
|
_initialTankShield = 50;
|
|
_initialJetEnergy = 29;
|
|
_initialJetShield = 34;
|
|
|
|
Math::Vector3d drillBaseOrigin = Math::Vector3d(0, 0, 0);
|
|
Math::Vector3d drillBaseSize = Math::Vector3d(3, 2, 3);
|
|
_drillBase = new GeometricObject(kCubeType, 0, 0, drillBaseOrigin, drillBaseSize, nullptr, nullptr, FCLInstructionVector(), "");
|
|
assert(!_drillBase->isDestroyed() && !_drillBase->isInvisible());
|
|
}
|
|
|
|
DrillerEngine::~DrillerEngine() {
|
|
delete _drillBase;
|
|
}
|
|
|
|
void DrillerEngine::titleScreen() {
|
|
if (isDOS() && isDemo()) // Demo will not show any title screen
|
|
return;
|
|
|
|
if (isAmiga() || isAtariST()) // These releases has their own screens
|
|
return;
|
|
|
|
if (_title) {
|
|
drawTitle();
|
|
_gfx->flipBuffer();
|
|
g_system->updateScreen();
|
|
g_system->delayMillis(3000);
|
|
}
|
|
}
|
|
void DrillerEngine::borderScreen() {
|
|
if (isDOS() && isDemo()) // Demo will not show the border
|
|
return;
|
|
|
|
if (isAmiga() || isAtariST()) // These releases has their own screens
|
|
return;
|
|
|
|
if (_border) {
|
|
drawBorder();
|
|
_gfx->flipBuffer();
|
|
g_system->updateScreen();
|
|
g_system->delayMillis(3000);
|
|
}
|
|
}
|
|
|
|
void DrillerEngine::gotoArea(uint16 areaID, int entranceID) {
|
|
int prevAreaID = _currentArea ? _currentArea->getAreaID(): -1;
|
|
debugC(1, kFreescapeDebugMove, "Jumping to area: %d, entrance: %d", areaID, entranceID);
|
|
if (!_gameStateBits.contains(areaID))
|
|
_gameStateBits[areaID] = 0;
|
|
|
|
if (!_areaMap.contains(areaID)) {
|
|
assert(isDOS() && isDemo());
|
|
// Not included in the demo, abort area change
|
|
return;
|
|
}
|
|
_currentArea = _areaMap[areaID];
|
|
_currentArea->show();
|
|
|
|
if (entranceID > 0 || areaID == 127) {
|
|
traverseEntrance(entranceID);
|
|
} else if (entranceID == 0) {
|
|
int newPos = -1;
|
|
// FIX: The next check will abort changing the current another
|
|
// area if the player is too close to the corners
|
|
if ((_position.z() < 100 && _position.x() > 3900) ||
|
|
(_position.z() > 3900 && _position.x() < 100) ||
|
|
(_position.z() < 100 && _position.x() < 100) ||
|
|
(_position.z() > 3900 && _position.x() > 3900)) {
|
|
assert(prevAreaID > 0);
|
|
_currentArea = _areaMap[prevAreaID];
|
|
return;
|
|
}
|
|
if (_position.z() < 200 || _position.z() >= 3800) {
|
|
if (_position.z() < 200)
|
|
newPos = 4000;
|
|
else
|
|
newPos = 100;
|
|
_position.setValue(2, newPos);
|
|
} else if(_position.x() < 200 || _position.x() >= 3800) {
|
|
if (_position.x() < 200)
|
|
newPos = 4000;
|
|
else
|
|
newPos = 100;
|
|
_position.setValue(0, newPos);
|
|
} else
|
|
error("Invalid movement across areas");
|
|
assert(newPos != -1);
|
|
_sensors = _currentArea->getSensors();
|
|
}
|
|
_lastPosition = _position;
|
|
|
|
if (areaID == _startArea && entranceID == _startEntrance) {
|
|
_yaw = 280;
|
|
} else if (areaID == 127) {
|
|
assert(entranceID == 0);
|
|
_yaw = 90;
|
|
_pitch = 335;
|
|
_flyMode = true; // Avoid falling
|
|
// Show the number of completed areas
|
|
_areaMap[127]->_name.replace(0, 3, Common::String::format("%4d", _gameStateVars[32]));
|
|
}
|
|
|
|
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->setColorRemaps(&_currentArea->_colorRemaps);
|
|
|
|
swapPalette(areaID);
|
|
_currentArea->_skyColor = 0;
|
|
_currentArea->_usualBackgroundColor = 0;
|
|
|
|
if (areaID != _startArea || entranceID != _startEntrance) {
|
|
g_system->warpMouse(_crossairPosition.x, _crossairPosition.y);
|
|
rotate(0, 0);
|
|
}
|
|
}
|
|
|
|
void DrillerEngine::loadGlobalObjects(Common::SeekableReadStream *file, int offset) {
|
|
assert(!_areaMap.contains(255));
|
|
ObjectMap *globalObjectsByID = new ObjectMap;
|
|
file->seek(offset);
|
|
for (int i = 0; i < 8; i++) {
|
|
if (isDOS() && isDemo()) // The DOS demo has a few missing objects
|
|
if (i == 5)
|
|
break;
|
|
|
|
Object *gobj = load8bitObject(file);
|
|
assert(gobj);
|
|
assert(!globalObjectsByID->contains(gobj->getObjectID()));
|
|
debugC(1, kFreescapeDebugParser, "Adding global object: %d", gobj->getObjectID());
|
|
(*globalObjectsByID)[gobj->getObjectID()] = gobj;
|
|
}
|
|
|
|
_areaMap[255] = new Area(255, 0, globalObjectsByID, nullptr);
|
|
}
|
|
|
|
void DrillerEngine::loadAssets() {
|
|
if (isDemo())
|
|
loadAssetsDemo();
|
|
else
|
|
loadAssetsFullGame();
|
|
|
|
// Start playing music, if any, in any supported format
|
|
playMusic("Matt Gray - The Best Of Reformation - 07 Driller Theme");
|
|
}
|
|
|
|
void DrillerEngine::loadAssetsDemo() {
|
|
Common::File file;
|
|
if (isAmiga()) {
|
|
file.open("lift.neo");
|
|
if (!file.isOpen())
|
|
error("Failed to open 'lift.neo' file");
|
|
|
|
_title = loadAndConvertNeoImage(&file, 0);
|
|
|
|
file.close();
|
|
file.open("console.neo");
|
|
if (!file.isOpen())
|
|
error("Failed to open 'console.neo' file");
|
|
|
|
_border = loadAndConvertNeoImage(&file, 0);
|
|
|
|
file.close();
|
|
file.open("demo.cmd");
|
|
if (!file.isOpen())
|
|
error("Failed to open 'demo.cmd' file");
|
|
|
|
loadDemoData(&file, 0, 0x1000);
|
|
|
|
file.close();
|
|
file.open("data");
|
|
if (!file.isOpen())
|
|
error("Failed to open 'data' file");
|
|
|
|
load8bitBinary(&file, 0x442, 16);
|
|
loadPalettes(&file, 0x0);
|
|
|
|
file.close();
|
|
file.open("driller");
|
|
if (!file.isOpen())
|
|
error("Failed to open 'driller' file");
|
|
|
|
loadFonts(&file, 0xa30);
|
|
loadMessagesFixedSize(&file, 0x3960, 14, 20);
|
|
loadGlobalObjects(&file, 0x3716);
|
|
|
|
file.close();
|
|
file.open("soundfx");
|
|
if (!file.isOpen())
|
|
error("Failed to open 'soundfx' executable for Amiga");
|
|
|
|
loadSoundsFx(&file, 0, 25);
|
|
} else if (isAtariST()) {
|
|
file.open("lift.neo");
|
|
if (!file.isOpen())
|
|
error("Failed to open 'lift.neo' file");
|
|
|
|
_title = loadAndConvertNeoImage(&file, 0);
|
|
|
|
file.close();
|
|
file.open("console.neo");
|
|
if (!file.isOpen())
|
|
error("Failed to open 'console.neo' file");
|
|
|
|
_border = loadAndConvertNeoImage(&file, 0);
|
|
|
|
file.close();
|
|
file.open("demo.cmd");
|
|
if (!file.isOpen())
|
|
error("Failed to open 'demo.cmd' file");
|
|
|
|
loadDemoData(&file, 0, 0x1000);
|
|
|
|
file.close();
|
|
file.open("data");
|
|
|
|
if (!file.isOpen())
|
|
error("Failed to open 'data' file");
|
|
|
|
load8bitBinary(&file, 0x442, 16);
|
|
loadPalettes(&file, 0x0);
|
|
|
|
file.close();
|
|
file.open("x.prg");
|
|
if (!file.isOpen())
|
|
error("Failed to open 'x.prg' file");
|
|
|
|
loadFonts(&file, 0x7bc);
|
|
loadMessagesFixedSize(&file, 0x3b90, 14, 20);
|
|
loadGlobalObjects(&file, 0x3946);
|
|
|
|
file.close();
|
|
file.open("soundfx");
|
|
if (!file.isOpen())
|
|
error("Failed to open 'soundfx' executable for AtariST demo");
|
|
|
|
loadSoundsFx(&file, 0, 25);
|
|
} else if (isDOS()) {
|
|
_renderMode = Common::kRenderCGA; // DOS demos is CGA only
|
|
_viewArea = Common::Rect(36, 16, 284, 117); // correct view area
|
|
_gfx->_renderMode = _renderMode;
|
|
loadBundledImages();
|
|
file.open("d2");
|
|
if (!file.isOpen())
|
|
error("Failed to open 'd2' file");
|
|
|
|
loadFonts(&file, 0x4eb0);
|
|
loadMessagesFixedSize(&file, 0x636, 14, 20);
|
|
load8bitBinary(&file, 0x55b0, 4);
|
|
loadGlobalObjects(&file, 0x8c);
|
|
|
|
// Fixed for a corrupted area names in the demo data
|
|
_areaMap[2]->_name = "LAPIS LAZULI";
|
|
_areaMap[3]->_name = "EMERALD";
|
|
_areaMap[8]->_name = "TOPAZ";
|
|
file.close();
|
|
} else
|
|
error("Unsupported demo for Driller");
|
|
|
|
_demoMode = !_disableDemoMode;
|
|
_angleRotationIndex = 0;
|
|
}
|
|
|
|
|
|
byte *parseEDSK(const Common::String filename, int &size) {
|
|
debugC(1, kFreescapeDebugParser, "Trying to parse edsk file: %s", filename.c_str());
|
|
Common::File file;
|
|
file.open(filename);
|
|
if (!file.isOpen())
|
|
error("Failed to open %s", filename.c_str());
|
|
|
|
int totalSize = file.size();
|
|
byte *edskBuffer = (byte *)malloc(totalSize);
|
|
file.read(edskBuffer, totalSize);
|
|
file.close();
|
|
|
|
// We don't know the final size, but we allocate enough
|
|
byte *memBuffer = (byte *)malloc(totalSize);
|
|
|
|
byte nsides = edskBuffer[49];
|
|
assert(nsides == 1);
|
|
int ntracks = 0;
|
|
int i = 256;
|
|
int j = 0;
|
|
while (i + 1 < totalSize) {
|
|
byte ssize = edskBuffer[i + 0x14];
|
|
debug("i: %x ssize: %d, number: %d", i, ssize, edskBuffer[i + 0x10]);
|
|
assert(ssize == 3 || edskBuffer[i + 0x0] == 'T');
|
|
assert(ssize == 3 || edskBuffer[i + 0x1] == 'r');
|
|
assert(ssize == 3 || edskBuffer[i + 0x2] == 'a');
|
|
//assert(ssize == 3 || ntracks == edskBuffer[i + 0x10]);
|
|
int start = i + 0x100;
|
|
debugC(1, kFreescapeDebugParser, "sector size: %d", ssize);
|
|
if (ssize == 2) {
|
|
i = i + 9 * 512 + 256;
|
|
} else if (ssize == 5) {
|
|
i = i + 8 * 512 + 256;
|
|
} else if (ssize == 0) {
|
|
i = totalSize - 1;
|
|
} else if (ssize == 3) {
|
|
break; // Not sure
|
|
} else {
|
|
error("ssize: %d", ssize);
|
|
}
|
|
int osize = i - start;
|
|
debugC(1, kFreescapeDebugParser, "copying track %d start: %x size: %x, dest: %x", ntracks, start, osize, j);
|
|
memcpy(memBuffer + j, edskBuffer + start, osize);
|
|
j = j + osize;
|
|
ntracks++;
|
|
}
|
|
size = j;
|
|
|
|
if (0) { // Useful to debug where exactly each object is located in memory once it is parsed
|
|
i = 0;
|
|
while(i < j) {
|
|
debugN("%05x: ", i);
|
|
for (int k = 0; k <= 16; k++) {
|
|
debugN("%02x ", memBuffer[i]);
|
|
i++;
|
|
}
|
|
debugN("\n");
|
|
}
|
|
}
|
|
free(edskBuffer);
|
|
return memBuffer;
|
|
}
|
|
|
|
|
|
void DrillerEngine::loadAssetsFullGame() {
|
|
Common::File file;
|
|
if (isAmiga()) {
|
|
if (_variant & GF_AMIGA_RETAIL) {
|
|
file.open("driller");
|
|
|
|
if (!file.isOpen())
|
|
error("Failed to open 'driller' executable for Amiga");
|
|
|
|
_border = loadAndConvertNeoImage(&file, 0x137f4);
|
|
byte *palette = (byte *)malloc(16 * 3);
|
|
for (int i = 0; i < 16; i++) { // gray scale palette
|
|
palette[i * 3 + 0] = i * (255 / 16);
|
|
palette[i * 3 + 1] = i * (255 / 16);
|
|
palette[i * 3 + 2] = i * (255 / 16);
|
|
}
|
|
_title = loadAndConvertNeoImage(&file, 0x10, palette);
|
|
|
|
loadFonts(&file, 0x8940);
|
|
loadMessagesFixedSize(&file, 0xc66e, 14, 20);
|
|
loadGlobalObjects(&file, 0xbd62);
|
|
load8bitBinary(&file, 0x29c16, 16);
|
|
loadPalettes(&file, 0x297d4);
|
|
loadSoundsFx(&file, 0x30e80, 25);
|
|
} else if (_variant & GF_AMIGA_BUDGET) {
|
|
file.open("lift.neo");
|
|
if (!file.isOpen())
|
|
error("Failed to open 'lift.neo' file");
|
|
|
|
_title = loadAndConvertNeoImage(&file, 0);
|
|
|
|
file.close();
|
|
file.open("console.neo");
|
|
if (!file.isOpen())
|
|
error("Failed to open 'console.neo' file");
|
|
|
|
_border = loadAndConvertNeoImage(&file, 0);
|
|
|
|
file.close();
|
|
file.open("driller");
|
|
if (!file.isOpen())
|
|
error("Failed to open 'driller' executable for Amiga");
|
|
|
|
loadFonts(&file, 0xa62);
|
|
loadMessagesFixedSize(&file, 0x499a, 14, 20);
|
|
loadGlobalObjects(&file, 0x4098);
|
|
load8bitBinary(&file, 0x21a3e, 16);
|
|
loadPalettes(&file, 0x215fc);
|
|
|
|
file.close();
|
|
file.open("soundfx");
|
|
if (!file.isOpen())
|
|
error("Failed to open 'soundfx' executable for Amiga");
|
|
|
|
loadSoundsFx(&file, 0, 25);
|
|
}
|
|
else
|
|
error("Invalid or unknown Amiga release");
|
|
} else if (isAtariST()) {
|
|
file.open("x.prg");
|
|
|
|
if (!file.isOpen())
|
|
error("Failed to open 'x.prg' executable for AtariST");
|
|
|
|
_border = loadAndConvertNeoImage(&file, 0x1371a);
|
|
byte *palette = (byte *)malloc(16 * 3);
|
|
for (int i = 0; i < 16; i++) { // gray scale palette
|
|
palette[i * 3 + 0] = i * (255 / 16);
|
|
palette[i * 3 + 1] = i * (255 / 16);
|
|
palette[i * 3 + 2] = i * (255 / 16);
|
|
}
|
|
_title = loadAndConvertNeoImage(&file, 0x10, palette);
|
|
|
|
loadFonts(&file, 0x8a32);
|
|
loadMessagesFixedSize(&file, 0xc5d8, 14, 20);
|
|
loadGlobalObjects(&file, 0xbccc);
|
|
load8bitBinary(&file, 0x29b3c, 16);
|
|
loadPalettes(&file, 0x296fa);
|
|
loadSoundsFx(&file, 0x30da6, 25);
|
|
} else if (isSpectrum()) {
|
|
loadBundledImages();
|
|
file.open("driller.zx.extracted");
|
|
|
|
if (!file.isOpen())
|
|
error("Failed to open driller.zx.extracted");
|
|
|
|
loadMessagesFixedSize(&file, 0x20e4, 14, 20);
|
|
|
|
if (_variant & GF_ZX_RETAIL)
|
|
loadFonts(&file, 0x62ca);
|
|
if (_variant & GF_ZX_BUDGET)
|
|
loadFonts(&file, 0x5aa8);
|
|
|
|
loadGlobalObjects(&file, 0x1c93);
|
|
|
|
if (_variant & GF_ZX_RETAIL)
|
|
load8bitBinary(&file, 0x642c, 4);
|
|
else if (_variant & GF_ZX_BUDGET)
|
|
load8bitBinary(&file, 0x5c0a, 4);
|
|
else
|
|
error("Unknown ZX spectrum variant");
|
|
} else if (isCPC()) {
|
|
loadBundledImages();
|
|
byte *memBuffer;
|
|
int memSize = 0;
|
|
if (_variant & GF_CPC_VIRTUALWORLDS) {
|
|
memBuffer = parseEDSK("virtualworlds.A.cpc.edsk", memSize);
|
|
|
|
// Deofuscation / loader code
|
|
for (int j = 0; j < 0x200; j++) {
|
|
memBuffer[0x14000 + j] = memBuffer[0x14200 + j];
|
|
memBuffer[0x14200 + j] = memBuffer[0x13400 + j];
|
|
memBuffer[0x14400 + j] = memBuffer[0x13800 + j];
|
|
memBuffer[0x14600 + j] = memBuffer[0x13c00 + j];
|
|
}
|
|
|
|
for (int j = 0; j < 0x200; j++) {
|
|
memBuffer[0x13c00 + j] = memBuffer[0x13a00 + j];
|
|
memBuffer[0x13a00 + j] = memBuffer[0x13600 + j];
|
|
memBuffer[0x13800 + j] = memBuffer[0x13200 + j];
|
|
memBuffer[0x13600 + j] = memBuffer[0x12e00 + j];
|
|
memBuffer[0x12e00 + j] = memBuffer[0x13000 + j];
|
|
memBuffer[0x13000 + j] = memBuffer[0x12200 + j];
|
|
memBuffer[0x13200 + j] = memBuffer[0x12600 + j];
|
|
memBuffer[0x13400 + j] = memBuffer[0x12a00 + j];
|
|
}
|
|
|
|
for (int i = 6; i >= 0; i--) {
|
|
//debug("copying 0x200 bytes to %x from %x", 0x12000 + 0x200*i, 0x11400 + 0x400*i);
|
|
for (int j = 0; j < 0x200; j++) {
|
|
memBuffer[0x12000 + 0x200*i + j] = memBuffer[0x11400 + 0x400*i + j];
|
|
}
|
|
}
|
|
|
|
for (int j = 0; j < 0x200; j++) {
|
|
memBuffer[0x11c00 + j] = memBuffer[0x11e00 + j];
|
|
memBuffer[0x11e00 + j] = memBuffer[0x11000 + j];
|
|
}
|
|
} else
|
|
memBuffer = parseEDSK("driller.cpc.edsk", memSize);
|
|
assert(memSize > 0);
|
|
Common::SeekableReadStream *stream = new Common::MemoryReadStream((const byte*)memBuffer, memSize);
|
|
|
|
if (_variant & GF_CPC_RETAIL) {
|
|
loadMessagesFixedSize(stream, 0xb0f7, 14, 20);
|
|
loadFonts(stream, 0xeb14);
|
|
load8bitBinary(stream, 0xec76, 4);
|
|
loadGlobalObjects(stream, 0xacb2);
|
|
} else if (_variant & GF_CPC_RETAIL2) {
|
|
loadMessagesFixedSize(stream, 0xb0f7 - 0x3fab, 14, 20);
|
|
loadFonts(stream, 0xeb14 - 0x3fab);
|
|
load8bitBinary(stream, 0xaccb, 4);
|
|
loadGlobalObjects(stream, 0xacb2 - 0x3fab);
|
|
} else if (_variant & _variant & GF_CPC_VIRTUALWORLDS) {
|
|
load8bitBinary(stream, 0x11acb, 4);
|
|
} else if (_variant & GF_CPC_BUDGET) {
|
|
loadMessagesFixedSize(stream, 0x9ef7, 14, 20);
|
|
loadFonts(stream, 0xd914);
|
|
load8bitBinary(stream, 0xda76, 4);
|
|
loadGlobalObjects(stream, 0x9ab2);
|
|
} else
|
|
error("Unknown Amstrad CPC variant");
|
|
} else if (isC64()) {
|
|
if (_targetName.hasPrefix("spacestationoblivion")) {
|
|
loadBundledImages();
|
|
file.open("spacestationoblivion.c64.extracted");
|
|
loadMessagesFixedSize(&file, 0x167a, 14, 20);
|
|
//loadFonts(&file, 0xae54);
|
|
load8bitBinary(&file, 0x8e02, 4);
|
|
loadGlobalObjects(&file, 0x1855);
|
|
} else if (_targetName.hasPrefix("driller")) {
|
|
file.open("driller.c64.extracted");
|
|
loadMessagesFixedSize(&file, 0x167a - 0x400, 14, 20);
|
|
//loadFonts(&file, 0xae54);
|
|
load8bitBinary(&file, 0x8e02 - 0x400, 4);
|
|
loadGlobalObjects(&file, 0x1855 - 0x400);
|
|
}
|
|
} else if (_renderMode == Common::kRenderEGA) {
|
|
loadBundledImages();
|
|
file.open("DRILLE.EXE");
|
|
|
|
if (!file.isOpen())
|
|
error("Failed to open DRILLE.EXE");
|
|
|
|
loadMessagesFixedSize(&file, 0x4135, 14, 20);
|
|
loadFonts(&file, 0x99dd);
|
|
loadGlobalObjects(&file, 0x3b42);
|
|
load8bitBinary(&file, 0x9b40, 16);
|
|
|
|
} else if (_renderMode == Common::kRenderCGA) {
|
|
loadBundledImages();
|
|
file.open("DRILLC.EXE");
|
|
|
|
if (!file.isOpen())
|
|
error("Failed to open DRILLC.EXE");
|
|
|
|
loadFonts(&file, 0x07a4a);
|
|
loadMessagesFixedSize(&file, 0x2585, 14, 20);
|
|
load8bitBinary(&file, 0x7bb0, 4);
|
|
loadGlobalObjects(&file, 0x1fa2);
|
|
} else
|
|
error("Invalid or unsupported render mode %s for Driller", Common::getRenderModeDescription(_renderMode));
|
|
|
|
/*
|
|
We are going to inject a small script in the
|
|
last area to force the game to end:
|
|
IF COLLIDED? THEN
|
|
IF VAR!=? (v32, 18) THEN END ENDIF
|
|
GOTO (127, 0)
|
|
*/
|
|
|
|
FCLInstructionVector instructions;
|
|
Common::Array<uint8> conditionArray;
|
|
|
|
conditionArray.push_back(0xb);
|
|
conditionArray.push_back(0x20);
|
|
conditionArray.push_back(0x12);
|
|
conditionArray.push_back(0x12);
|
|
conditionArray.push_back(0x7f);
|
|
conditionArray.push_back(0x0);
|
|
|
|
Common::String conditionSource = detokenise8bitCondition(conditionArray, instructions);
|
|
debugC(1, kFreescapeDebugParser, "%s", conditionSource.c_str());
|
|
_areaMap[18]->_conditions.push_back(instructions);
|
|
_areaMap[18]->_conditionSources.push_back(conditionSource);
|
|
}
|
|
|
|
void DrillerEngine::processBorder() {
|
|
FreescapeEngine::processBorder();
|
|
if (isDOS() && _renderMode == Common::kRenderCGA) { // Replace some colors for the CGA borders
|
|
uint32 color1 = _border->format.ARGBToColor(0xFF, 0xAA, 0x00, 0xAA);
|
|
uint32 color2 = _border->format.ARGBToColor(0xFF, 0xAA, 0x55, 0x00);
|
|
|
|
uint32 colorA = _border->format.ARGBToColor(0xFF, 0x00, 0xAA, 0xAA);
|
|
uint32 colorB = _border->format.ARGBToColor(0xFF, 0x00, 0xAA, 0x00);
|
|
|
|
uint32 colorX = _border->format.ARGBToColor(0xFF, 0xAA, 0xAA, 0xAA);
|
|
uint32 colorY = _border->format.ARGBToColor(0xFF, 0xAA, 0x00, 0x00);
|
|
|
|
Graphics::Surface *borderRedGreen = new Graphics::Surface();
|
|
borderRedGreen->create(1, 1, _border->format);
|
|
borderRedGreen->copyFrom(*_border);
|
|
|
|
for (int i = 0; i < _border->w; i++) {
|
|
for (int j = 0; j < _border->h; j++) {
|
|
if (borderRedGreen->getPixel(i, j) == color1)
|
|
borderRedGreen->setPixel(i, j, color2);
|
|
else if (borderRedGreen->getPixel(i, j) == colorA)
|
|
borderRedGreen->setPixel(i, j, colorB);
|
|
else if (borderRedGreen->getPixel(i, j) == colorX)
|
|
borderRedGreen->setPixel(i, j, colorY);
|
|
|
|
}
|
|
}
|
|
Texture *borderTextureRedGreen = _gfx->createTexture(borderRedGreen);
|
|
|
|
const CGAPalettteEntry *entry = rawCGAPaletteTable;
|
|
while (entry->areaId) {
|
|
|
|
if (entry->palette == kDrillerCGAPaletteRedGreen) {
|
|
_borderCGAByArea[entry->areaId] = borderTextureRedGreen;
|
|
_paletteCGAByArea[entry->areaId] = (byte *)kDrillerCGAPaletteRedGreenData;
|
|
} else if (entry->palette == kDrillerCGAPalettePinkBlue) {
|
|
_borderCGAByArea[entry->areaId] = _borderTexture;
|
|
_paletteCGAByArea[entry->areaId] = (byte *)kDrillerCGAPalettePinkBlueData;
|
|
} else
|
|
error("Invalid CGA palette to use");
|
|
entry++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DrillerEngine::drawUI() {
|
|
Graphics::Surface *surface = nullptr;
|
|
if (_border) { // This can be removed when all the borders are loaded
|
|
uint32 gray = _gfx->_texturePixelFormat.ARGBToColor(0x00, 0xA0, 0xA0, 0xA0);
|
|
surface = new Graphics::Surface();
|
|
surface->create(_screenW, _screenH, _gfx->_texturePixelFormat);
|
|
surface->fillRect(_fullscreenViewArea, gray);
|
|
drawCrossair(surface);
|
|
} else
|
|
return;
|
|
|
|
if (isDOS())
|
|
drawDOSUI(surface);
|
|
else if (isC64())
|
|
drawC64UI(surface);
|
|
else if (isSpectrum())
|
|
drawZXUI(surface);
|
|
else if (isCPC())
|
|
drawCPCUI(surface);
|
|
else if (isAmiga() || isAtariST())
|
|
drawAmigaAtariSTUI(surface);
|
|
|
|
if (!_uiTexture)
|
|
_uiTexture = _gfx->createTexture(surface);
|
|
else
|
|
_uiTexture->update(surface);
|
|
|
|
_gfx->setViewport(_fullscreenViewArea);
|
|
_gfx->drawTexturedRect2D(_fullscreenViewArea, _fullscreenViewArea, _uiTexture);
|
|
_gfx->setViewport(_viewArea);
|
|
|
|
surface->free();
|
|
delete surface;
|
|
}
|
|
|
|
void DrillerEngine::drawDOSUI(Graphics::Surface *surface) {
|
|
uint32 color = _renderMode == Common::kRenderCGA ? 1 : 14;
|
|
uint8 r, g, b;
|
|
|
|
_gfx->readFromPalette(color, r, g, b);
|
|
uint32 front = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
|
|
|
color = _currentArea->_usualBackgroundColor;
|
|
if (_gfx->_colorRemaps && _gfx->_colorRemaps->contains(color)) {
|
|
color = (*_gfx->_colorRemaps)[color];
|
|
}
|
|
|
|
_gfx->readFromPalette(color, r, g, b);
|
|
uint32 back = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
|
|
|
int score = _gameStateVars[k8bitVariableScore];
|
|
drawStringInSurface(_currentArea->_name, 196, 185, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%04d", int(2 * _position.x())), 150, 145, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%04d", int(2 * _position.z())), 150, 153, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%04d", int(2 * _position.y())), 150, 161, front, back, surface);
|
|
if (_playerHeightNumber >= 0)
|
|
drawStringInSurface(Common::String::format("%d", _playerHeightNumber), 57, 161, front, back, surface);
|
|
else
|
|
drawStringInSurface(Common::String::format("%s", "J"), 57, 161, front, back, surface);
|
|
|
|
drawStringInSurface(Common::String::format("%02d", int(_angleRotations[_angleRotationIndex])), 46, 145, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%3d", _playerSteps[_playerStepIndex]), _renderMode == Common::kRenderCGA ? 44 : 46, 153, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%07d", score), 238, 129, front, back, surface);
|
|
|
|
int hours = _countdown <= 0 ? 0 : _countdown / 3600;
|
|
drawStringInSurface(Common::String::format("%02d", hours), 208, 8, front, back, surface);
|
|
int minutes = _countdown <= 0 ? 0 : (_countdown - hours * 3600) / 60;
|
|
drawStringInSurface(Common::String::format("%02d", minutes), 230, 8, front, back, surface);
|
|
int seconds = _countdown <= 0 ? 0 : _countdown - hours * 3600 - minutes * 60;
|
|
drawStringInSurface(Common::String::format("%02d", seconds), 254, 8, front, back, surface);
|
|
|
|
Common::String message;
|
|
int deadline;
|
|
getLatestMessages(message, deadline);
|
|
if (deadline <= _countdown) {
|
|
drawStringInSurface(message, 190, 177, back, front, surface);
|
|
_temporaryMessages.push_back(message);
|
|
_temporaryMessageDeadlines.push_back(deadline);
|
|
} else {
|
|
if (_currentArea->_gasPocketRadius == 0)
|
|
message = _messagesList[2];
|
|
else if (_drillStatusByArea[_currentArea->getAreaID()])
|
|
message = _messagesList[0];
|
|
else
|
|
message = _messagesList[1];
|
|
|
|
drawStringInSurface(message, 191, 177, front, back, surface);
|
|
}
|
|
|
|
int energy = _gameStateVars[k8bitVariableEnergy];
|
|
int shield = _gameStateVars[k8bitVariableShield];
|
|
|
|
if (energy >= 0) {
|
|
Common::Rect backBar(20, 185, 88 - energy, 191);
|
|
surface->fillRect(backBar, back);
|
|
Common::Rect energyBar(87 - energy, 185, 88, 191);
|
|
surface->fillRect(energyBar, front);
|
|
}
|
|
|
|
if (shield >= 0) {
|
|
Common::Rect backBar(20, 177, 88 - shield, 183);
|
|
surface->fillRect(backBar, back);
|
|
|
|
Common::Rect shieldBar(87 - shield, 177, 88, 183);
|
|
surface->fillRect(shieldBar, front);
|
|
}
|
|
}
|
|
|
|
void DrillerEngine::drawCPCUI(Graphics::Surface *surface) {
|
|
uint32 color = 1;
|
|
uint8 r, g, b;
|
|
|
|
_gfx->selectColorFromFourColorPalette(color, r, g, b);
|
|
uint32 front = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
|
|
|
color = 0;
|
|
if (_gfx->_colorRemaps && _gfx->_colorRemaps->contains(color)) {
|
|
color = (*_gfx->_colorRemaps)[color];
|
|
}
|
|
|
|
_gfx->readFromPalette(color, r, g, b);
|
|
uint32 back = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
|
|
|
int score = _gameStateVars[k8bitVariableScore];
|
|
drawStringInSurface(_currentArea->_name, 200, 188, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%04d", int(2 * _position.x())), 149, 148, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%04d", int(2 * _position.z())), 149, 156, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%04d", int(2 * _position.y())), 149, 164, front, back, surface);
|
|
if (_playerHeightNumber >= 0)
|
|
drawStringInSurface(Common::String::format("%d", _playerHeightNumber), 54, 164, front, back, surface);
|
|
else
|
|
drawStringInSurface(Common::String::format("%s", "J"), 54, 164, front, back, surface);
|
|
|
|
drawStringInSurface(Common::String::format("%02d", int(_angleRotations[_angleRotationIndex])), 46, 148, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%3d", _playerSteps[_playerStepIndex]), 44, 156, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%07d", score), 239, 132, front, back, surface);
|
|
|
|
int hours = _countdown <= 0 ? 0 : _countdown / 3600;
|
|
drawStringInSurface(Common::String::format("%02d", hours), 209, 11, front, back, surface);
|
|
int minutes = _countdown <= 0 ? 0 : (_countdown - hours * 3600) / 60;
|
|
drawStringInSurface(Common::String::format("%02d", minutes), 232, 11, front, back, surface);
|
|
int seconds = _countdown <= 0 ? 0 : _countdown - hours * 3600 - minutes * 60;
|
|
drawStringInSurface(Common::String::format("%02d", seconds), 254, 11, front, back, surface);
|
|
|
|
Common::String message;
|
|
int deadline;
|
|
getLatestMessages(message, deadline);
|
|
if (deadline <= _countdown) {
|
|
drawStringInSurface(message, 191, 180, back, front, surface);
|
|
_temporaryMessages.push_back(message);
|
|
_temporaryMessageDeadlines.push_back(deadline);
|
|
} else if (_messagesList.size() > 0) {
|
|
if (_currentArea->_gasPocketRadius == 0)
|
|
message = _messagesList[2];
|
|
else if (_drillStatusByArea[_currentArea->getAreaID()])
|
|
message = _messagesList[0];
|
|
else
|
|
message = _messagesList[1];
|
|
|
|
drawStringInSurface(message, 191, 180, front, back, surface);
|
|
}
|
|
|
|
int energy = _gameStateVars[k8bitVariableEnergy];
|
|
int shield = _gameStateVars[k8bitVariableShield];
|
|
|
|
if (energy >= 0) {
|
|
Common::Rect backBar(25, 187, 89 - energy, 194);
|
|
surface->fillRect(backBar, back);
|
|
Common::Rect energyBar(88 - energy, 187, 88, 194);
|
|
surface->fillRect(energyBar, front);
|
|
}
|
|
|
|
if (shield >= 0) {
|
|
Common::Rect backBar(25, 180, 89 - shield, 186);
|
|
surface->fillRect(backBar, back);
|
|
|
|
Common::Rect shieldBar(88 - shield, 180, 88, 186);
|
|
surface->fillRect(shieldBar, front);
|
|
}
|
|
}
|
|
|
|
void DrillerEngine::drawC64UI(Graphics::Surface *surface) {
|
|
uint32 color = 1;
|
|
uint8 r, g, b;
|
|
|
|
_gfx->selectColorFromFourColorPalette(color, r, g, b);
|
|
uint32 front = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
|
|
|
color = 0;
|
|
if (_gfx->_colorRemaps && _gfx->_colorRemaps->contains(color)) {
|
|
color = (*_gfx->_colorRemaps)[color];
|
|
}
|
|
|
|
_gfx->readFromPalette(color, r, g, b);
|
|
uint32 back = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
|
|
|
int score = _gameStateVars[k8bitVariableScore];
|
|
drawStringInSurface(_currentArea->_name, 200, 188, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%04d", int(2 * _position.x())), 149, 148, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%04d", int(2 * _position.z())), 149, 156, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%04d", int(2 * _position.y())), 149, 164, front, back, surface);
|
|
if (_playerHeightNumber >= 0)
|
|
drawStringInSurface(Common::String::format("%d", _playerHeightNumber), 54, 164, front, back, surface);
|
|
else
|
|
drawStringInSurface(Common::String::format("%s", "J"), 54, 164, front, back, surface);
|
|
|
|
drawStringInSurface(Common::String::format("%02d", int(_angleRotations[_angleRotationIndex])), 46, 148, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%3d", _playerSteps[_playerStepIndex]), 44, 156, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%07d", score), 240, 128, front, back, surface);
|
|
|
|
int hours = _countdown <= 0 ? 0 : _countdown / 3600;
|
|
drawStringInSurface(Common::String::format("%02d", hours), 209, 11, front, back, surface);
|
|
int minutes = _countdown <= 0 ? 0 : (_countdown - hours * 3600) / 60;
|
|
drawStringInSurface(Common::String::format("%02d", minutes), 232, 11, front, back, surface);
|
|
int seconds = _countdown <= 0 ? 0 : _countdown - hours * 3600 - minutes * 60;
|
|
drawStringInSurface(Common::String::format("%02d", seconds), 254, 11, front, back, surface);
|
|
|
|
Common::String message;
|
|
int deadline;
|
|
getLatestMessages(message, deadline);
|
|
if (deadline <= _countdown) {
|
|
drawStringInSurface(message, 191, 180, back, front, surface);
|
|
_temporaryMessages.push_back(message);
|
|
_temporaryMessageDeadlines.push_back(deadline);
|
|
} else {
|
|
if (_currentArea->_gasPocketRadius == 0)
|
|
message = _messagesList[2];
|
|
else if (_drillStatusByArea[_currentArea->getAreaID()])
|
|
message = _messagesList[0];
|
|
else
|
|
message = _messagesList[1];
|
|
|
|
drawStringInSurface(message, 191, 180, front, back, surface);
|
|
}
|
|
|
|
int energy = _gameStateVars[k8bitVariableEnergy];
|
|
int shield = _gameStateVars[k8bitVariableShield];
|
|
|
|
if (energy >= 0) {
|
|
Common::Rect backBar(25, 187, 89 - energy, 194);
|
|
surface->fillRect(backBar, back);
|
|
Common::Rect energyBar(88 - energy, 187, 88, 194);
|
|
surface->fillRect(energyBar, front);
|
|
}
|
|
|
|
if (shield >= 0) {
|
|
Common::Rect backBar(25, 180, 89 - shield, 186);
|
|
surface->fillRect(backBar, back);
|
|
|
|
Common::Rect shieldBar(88 - shield, 180, 88, 186);
|
|
surface->fillRect(shieldBar, front);
|
|
}
|
|
}
|
|
|
|
void DrillerEngine::drawZXUI(Graphics::Surface *surface) {
|
|
uint32 color = 5;
|
|
uint8 r, g, b;
|
|
|
|
_gfx->readFromPalette(color, r, g, b);
|
|
uint32 front = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
|
|
|
color = _currentArea->_usualBackgroundColor;
|
|
if (_gfx->_colorRemaps && _gfx->_colorRemaps->contains(color)) {
|
|
color = (*_gfx->_colorRemaps)[color];
|
|
}
|
|
|
|
_gfx->readFromPalette(color, r, g, b);
|
|
uint32 back = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
|
uint32 white = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0xFF, 0xFF, 0xFF);
|
|
|
|
int score = _gameStateVars[k8bitVariableScore];
|
|
drawStringInSurface(_currentArea->_name, 176, 188, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%04d", int(2 * _position.x())), 152, 149, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%04d", int(2 * _position.z())), 152, 157, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%04d", int(2 * _position.y())), 152, 165, front, back, surface);
|
|
if (_playerHeightNumber >= 0)
|
|
drawStringInSurface(Common::String::format("%d", _playerHeightNumber), 74, 165, front, back, surface);
|
|
else
|
|
drawStringInSurface(Common::String::format("%s", "J"), 74, 165, front, back, surface);
|
|
|
|
drawStringInSurface(Common::String::format("%02d", int(_angleRotations[_angleRotationIndex])), 64, 149, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%3d", _playerSteps[_playerStepIndex]), 65, 157, front, back, surface);
|
|
drawStringInSurface(Common::String::format("%07d", score), 217, 133, white, back, surface);
|
|
|
|
int hours = _countdown <= 0 ? 0 : _countdown / 3600;
|
|
drawStringInSurface(Common::String::format("%02d", hours), 187, 12, front, back, surface);
|
|
int minutes = _countdown <= 0 ? 0 : (_countdown - hours * 3600) / 60;
|
|
drawStringInSurface(Common::String::format("%02d", minutes), 209, 12, front, back, surface);
|
|
int seconds = _countdown <= 0 ? 0 : _countdown - hours * 3600 - minutes * 60;
|
|
drawStringInSurface(Common::String::format("%02d", seconds), 232, 12, front, back, surface);
|
|
|
|
Common::String message;
|
|
int deadline;
|
|
getLatestMessages(message, deadline);
|
|
if (deadline <= _countdown) {
|
|
drawStringInSurface(message, 169, 181, back, front, surface);
|
|
_temporaryMessages.push_back(message);
|
|
_temporaryMessageDeadlines.push_back(deadline);
|
|
} else {
|
|
if (_currentArea->_gasPocketRadius == 0)
|
|
message = _messagesList[2];
|
|
else if (_drillStatusByArea[_currentArea->getAreaID()])
|
|
message = _messagesList[0];
|
|
else
|
|
message = _messagesList[1];
|
|
|
|
drawStringInSurface(message, 169, 181, front, back, surface);
|
|
}
|
|
|
|
int energy = _gameStateVars[k8bitVariableEnergy];
|
|
int shield = _gameStateVars[k8bitVariableShield];
|
|
|
|
if (energy >= 0) {
|
|
Common::Rect backBar(45, 188, 109 - energy, 194);
|
|
surface->fillRect(backBar, back);
|
|
Common::Rect energyBar(108 - energy, 188, 108, 194);
|
|
surface->fillRect(energyBar, front);
|
|
}
|
|
|
|
if (shield >= 0) {
|
|
Common::Rect backBar(45, 181, 109 - shield, 187);
|
|
surface->fillRect(backBar, back);
|
|
|
|
Common::Rect shieldBar(108 - shield, 181, 108, 187);
|
|
surface->fillRect(shieldBar, front);
|
|
}
|
|
}
|
|
|
|
void DrillerEngine::drawAmigaAtariSTUI(Graphics::Surface *surface) {
|
|
uint32 white = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0xFF, 0xFF, 0xFF);
|
|
uint32 yellow = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0xFF, 0xFF, 0x55);
|
|
uint32 brownish = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0x9E, 0x80, 0x20);
|
|
uint32 brown = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0x7E, 0x60, 0x19);
|
|
uint32 black = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0x00, 0x00, 0x00);
|
|
uint32 transparent = _gfx->_texturePixelFormat.ARGBToColor(0x00, 0x00, 0x00, 0x00);
|
|
|
|
int score = _gameStateVars[k8bitVariableScore];
|
|
Common::String coords;
|
|
|
|
if (!isDemo()) { // It seems demos will not include the complete font?
|
|
drawStringInSurface("x", 37, 18, white, transparent, surface, 82);
|
|
coords = Common::String::format("%04d", 2 * int(_position.x()));
|
|
for (int i = 0; i < 4; i++)
|
|
drawStringInSurface(Common::String(coords[i]), 47 + 6*i, 18, white, transparent, surface, 112);
|
|
|
|
drawStringInSurface("y", 37, 26, white, transparent, surface, 82);
|
|
coords = Common::String::format("%04d", 2 * int(_position.z())); // Coords y and z are swapped!
|
|
for (int i = 0; i < 4; i++)
|
|
drawStringInSurface(Common::String(coords[i]), 47 + 6*i, 26, white, transparent, surface, 112);
|
|
|
|
drawStringInSurface("z", 37, 34, white, transparent, surface, 82);
|
|
coords = Common::String::format("%04d", 2 * int(_position.y())); // Coords y and z are swapped!
|
|
for (int i = 0; i < 4; i++)
|
|
drawStringInSurface(Common::String(coords[i]), 47 + 6*i, 34, white, transparent, surface, 112);
|
|
}
|
|
|
|
drawStringInSurface(_currentArea->_name, 188, 185, yellow, black, surface);
|
|
drawStringInSurface(Common::String::format("%07d", score), 240, 129, yellow, black, surface);
|
|
|
|
int hours = _countdown <= 0 ? 0 : _countdown / 3600;
|
|
drawStringInSurface(Common::String::format("%02d:", hours), 208, 7, yellow, black, surface);
|
|
int minutes = _countdown <= 0 ? 0 : (_countdown - hours * 3600) / 60;
|
|
drawStringInSurface(Common::String::format("%02d:", minutes), 230, 7, yellow, black, surface);
|
|
int seconds = _countdown <= 0 ? 0 : _countdown - hours * 3600 - minutes * 60;
|
|
drawStringInSurface(Common::String::format("%02d", seconds), 254, 7, yellow, black, surface);
|
|
|
|
Common::String message;
|
|
int deadline;
|
|
getLatestMessages(message, deadline);
|
|
if (deadline <= _countdown) {
|
|
drawStringInSurface(message, 188, 177, black, yellow, surface);
|
|
_temporaryMessages.push_back(message);
|
|
_temporaryMessageDeadlines.push_back(deadline);
|
|
} else {
|
|
if (_currentArea->_gasPocketRadius == 0)
|
|
message = _messagesList[2];
|
|
else if (_drillStatusByArea[_currentArea->getAreaID()])
|
|
message = _messagesList[0];
|
|
else
|
|
message = _messagesList[1];
|
|
|
|
drawStringInSurface(message, 188, 177, yellow, black, surface);
|
|
}
|
|
|
|
int energy = _gameStateVars[k8bitVariableEnergy];
|
|
int shield = _gameStateVars[k8bitVariableShield];
|
|
|
|
if (shield >= 0) {
|
|
Common::Rect shieldBar;
|
|
shieldBar = Common::Rect(11, 178, 76 - (k8bitMaxShield - shield), 184);
|
|
surface->fillRect(shieldBar, brown);
|
|
|
|
shieldBar = Common::Rect(11, 179, 76 - (k8bitMaxShield - shield), 183);
|
|
surface->fillRect(shieldBar, brownish);
|
|
|
|
shieldBar = Common::Rect(11, 180, 76 - (k8bitMaxShield - shield), 182);
|
|
surface->fillRect(shieldBar, yellow);
|
|
}
|
|
|
|
if (energy >= 0) {
|
|
Common::Rect energyBar;
|
|
energyBar = Common::Rect(11, 186, 75 - (k8bitMaxEnergy - energy), 192);
|
|
surface->fillRect(energyBar, brown);
|
|
|
|
energyBar = Common::Rect(11, 187, 75 - (k8bitMaxEnergy - energy), 191);
|
|
surface->fillRect(energyBar, brownish);
|
|
|
|
energyBar = Common::Rect(11, 188, 75 - (k8bitMaxEnergy - energy), 190);
|
|
surface->fillRect(energyBar, yellow);
|
|
}
|
|
}
|
|
|
|
void DrillerEngine::drawInfoMenu() {
|
|
_savedScreen = _gfx->getScreenshot();
|
|
|
|
uint32 color = _gfx->_texturePixelFormat.ARGBToColor(0x00, 0x00, 0x00, 0x00);
|
|
Graphics::Surface *surface = new Graphics::Surface();
|
|
surface->create(_screenW, _screenH, _gfx->_texturePixelFormat);
|
|
surface->fillRect(_fullscreenViewArea, color);
|
|
|
|
uint32 black = _gfx->_texturePixelFormat.ARGBToColor(0xFF, 0x00, 0x00, 0x00);
|
|
surface->fillRect(_viewArea, black);
|
|
|
|
switch (_renderMode) {
|
|
case Common::kRenderCGA:
|
|
color = 1;
|
|
break;
|
|
case Common::kRenderZX:
|
|
color = 6;
|
|
break;
|
|
default:
|
|
color = 14;
|
|
}
|
|
uint8 r, g, b;
|
|
|
|
_gfx->readFromPalette(color, r, g, b);
|
|
uint32 front = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
|
|
|
|
drawStringInSurface(Common::String::format("%10s : %s", "sector", _currentArea->_name.c_str()), 69, 25, front, black, surface);
|
|
Common::String rigStatus;
|
|
Common::String gasFound;
|
|
Common::String perTapped;
|
|
Common::String gasTapped;
|
|
|
|
switch (_drillStatusByArea[_currentArea->getAreaID()]) {
|
|
case kDrillerNoRig:
|
|
rigStatus = "Unpositioned";
|
|
gasFound = "-";
|
|
perTapped = "-";
|
|
gasTapped = "-";
|
|
break;
|
|
case kDrillerRigInPlace:
|
|
case kDrillerRigOutOfPlace:
|
|
rigStatus = "Positioned";
|
|
gasFound = Common::String::format("%d CFT", _drillMaxScoreByArea[_currentArea->getAreaID()]);
|
|
perTapped = Common::String::format("%d %%", _drillSuccessByArea[_currentArea->getAreaID()]);
|
|
gasTapped = Common::String::format("%d", uint32(_drillSuccessByArea[_currentArea->getAreaID()] * _drillMaxScoreByArea[_currentArea->getAreaID()]) / 100);
|
|
break;
|
|
case kDrillerRigNoGas:
|
|
rigStatus = "Positioned";
|
|
gasFound = "none";
|
|
perTapped = "none";
|
|
gasTapped = "zero";
|
|
break;
|
|
default:
|
|
error("Invalid drill status");
|
|
break;
|
|
}
|
|
|
|
drawStringInSurface(Common::String::format("%10s : %s", "rig status", rigStatus.c_str()), 69, 33, front, black, surface);
|
|
drawStringInSurface(Common::String::format("%10s : %s", "gas found", gasFound.c_str()), 69, 41, front, black, surface);
|
|
drawStringInSurface(Common::String::format("%10s : %s", "% tapped", perTapped.c_str()), 69, 49, front, black, surface);
|
|
drawStringInSurface(Common::String::format("%10s : %s", "gas tapped", gasTapped.c_str()), 69, 57, front, black, surface);
|
|
|
|
drawStringInSurface(Common::String::format("%13s : %d", "total sectors", 18), 84, 73, front, black, surface);
|
|
drawStringInSurface(Common::String::format("%13s : %d", "safe sectors", _gameStateVars[32]), 84, 81, front, black, surface);
|
|
|
|
if (isDOS()) {
|
|
drawStringInSurface("l-load s-save esc-terminate", 53, 97, front, black, surface);
|
|
drawStringInSurface("t-toggle sound on/off", 76, 105, front, black, surface);
|
|
} else if (isSpectrum()) {
|
|
drawStringInSurface("l-load s-save 1-abort", 53, 97, front, black, surface);
|
|
drawStringInSurface("any other key-continue", 76, 105, front, black, surface);
|
|
}
|
|
|
|
_uiTexture->update(surface);
|
|
_gfx->setViewport(_fullscreenViewArea);
|
|
_gfx->drawTexturedRect2D(_fullscreenViewArea, _fullscreenViewArea, _uiTexture);
|
|
_gfx->setViewport(_viewArea);
|
|
|
|
_gfx->flipBuffer();
|
|
g_system->updateScreen();
|
|
|
|
Common::Event event;
|
|
bool cont = true;
|
|
while (!shouldQuit() && cont) {
|
|
while (g_system->getEventManager()->pollEvent(event)) {
|
|
|
|
// Events
|
|
switch (event.type) {
|
|
case Common::EVENT_KEYDOWN:
|
|
if (event.kbd.keycode == Common::KEYCODE_l) {
|
|
_gfx->setViewport(_fullscreenViewArea);
|
|
loadGameDialog();
|
|
_gfx->setViewport(_viewArea);
|
|
} else if (event.kbd.keycode == Common::KEYCODE_s) {
|
|
_gfx->setViewport(_fullscreenViewArea);
|
|
saveGameDialog();
|
|
_gfx->setViewport(_viewArea);
|
|
} else if (isDOS() && event.kbd.keycode == Common::KEYCODE_t) {
|
|
// TODO
|
|
} else if (isDOS() && event.kbd.keycode == Common::KEYCODE_ESCAPE) {
|
|
_forceEndGame = true;
|
|
cont = false;
|
|
} else if (isSpectrum() && event.kbd.keycode == Common::KEYCODE_1) {
|
|
_forceEndGame = true;
|
|
cont = false;
|
|
} else
|
|
cont = false;
|
|
break;
|
|
case Common::EVENT_SCREEN_CHANGED:
|
|
_gfx->computeScreenViewport();
|
|
// TODO: properly refresh screen
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
g_system->delayMillis(10);
|
|
}
|
|
|
|
_savedScreen->free();
|
|
delete _savedScreen;
|
|
surface->free();
|
|
delete surface;
|
|
}
|
|
|
|
Math::Vector3d getProjectionToPlane(const Math::Vector3d &vect, const Math::Vector3d normal) {
|
|
assert (normal.length() == 1);
|
|
// Formula: return p - n * (n . p)
|
|
Math::Vector3d result = vect;
|
|
result -= normal * normal.dotProduct(vect);
|
|
return result;
|
|
}
|
|
|
|
void DrillerEngine::pressedKey(const int keycode) {
|
|
if (keycode == Common::KEYCODE_d) {
|
|
if (isDOS() && isDemo()) // No support for drilling here yet
|
|
return;
|
|
clearTemporalMessages();
|
|
Common::Point gasPocket = _currentArea->_gasPocketPosition;
|
|
uint32 gasPocketRadius = _currentArea->_gasPocketRadius;
|
|
if (gasPocketRadius == 0) {
|
|
insertTemporaryMessage(_messagesList[2], _countdown - 2);
|
|
return;
|
|
}
|
|
|
|
if (_flyMode) {
|
|
insertTemporaryMessage(_messagesList[8], _countdown - 2);
|
|
return;
|
|
}
|
|
|
|
if (drillDeployed(_currentArea)) {
|
|
insertTemporaryMessage(_messagesList[12], _countdown - 2);
|
|
return;
|
|
}
|
|
|
|
if (_gameStateVars[k8bitVariableEnergy] < 5) {
|
|
insertTemporaryMessage(_messagesList[7], _countdown - 2);
|
|
return;
|
|
}
|
|
|
|
Math::Vector3d drill = drillPosition();
|
|
debugC(1, kFreescapeDebugMove, "Current position at %f %f %f", _position.x(), _position.y(), _position.z());
|
|
debugC(1, kFreescapeDebugMove, "Trying to adding drill at %f %f %f", drill.x(), drill.y(), drill.z());
|
|
debugC(1, kFreescapeDebugMove, "with pitch: %f and yaw %f", _pitch, _yaw);
|
|
|
|
if (!checkDrill(drill)) {
|
|
insertTemporaryMessage(_messagesList[4], _countdown - 2);
|
|
return;
|
|
}
|
|
|
|
_gameStateVars[k8bitVariableEnergy] = _gameStateVars[k8bitVariableEnergy] - 5;
|
|
const Math::Vector3d gasPocket3D(gasPocket.x, drill.y(), gasPocket.y);
|
|
float distanceToPocket = (gasPocket3D - drill).length();
|
|
float success = _useAutomaticDrilling ? 100.0 : 100.0 * (1.0 - distanceToPocket / _currentArea->_gasPocketRadius);
|
|
insertTemporaryMessage(_messagesList[3], _countdown - 2);
|
|
addDrill(drill, success > 0);
|
|
if (success <= 0) {
|
|
insertTemporaryMessage(_messagesList[9], _countdown - 4);
|
|
_drillStatusByArea[_currentArea->getAreaID()] = kDrillerRigNoGas;
|
|
return;
|
|
}
|
|
Common::String maxScoreMessage = _messagesList[5];
|
|
int maxScore = _drillMaxScoreByArea[_currentArea->getAreaID()];
|
|
maxScoreMessage.replace(2, 6, Common::String::format("%d", maxScore));
|
|
insertTemporaryMessage(maxScoreMessage, _countdown - 4);
|
|
Common::String successMessage = _messagesList[6];
|
|
successMessage.replace(0, 4, Common::String::format("%d", int(success)));
|
|
while (successMessage.size() < 14)
|
|
successMessage += " ";
|
|
insertTemporaryMessage(successMessage, _countdown - 6);
|
|
_drillSuccessByArea[_currentArea->getAreaID()] = uint32(success);
|
|
_gameStateVars[k8bitVariableScore] += uint32(maxScore * uint32(success)) / 100;
|
|
|
|
if (success >= 50.0) {
|
|
_drillStatusByArea[_currentArea->getAreaID()] = kDrillerRigInPlace;
|
|
_gameStateVars[32]++;
|
|
} else
|
|
_drillStatusByArea[_currentArea->getAreaID()] = kDrillerRigOutOfPlace;
|
|
} else if (keycode == Common::KEYCODE_c) {
|
|
if (isDOS() && isDemo()) // No support for drilling here yet
|
|
return;
|
|
uint32 gasPocketRadius = _currentArea->_gasPocketRadius;
|
|
clearTemporalMessages();
|
|
if (gasPocketRadius == 0) {
|
|
insertTemporaryMessage(_messagesList[2], _countdown - 2);
|
|
return;
|
|
}
|
|
|
|
if (_flyMode) {
|
|
insertTemporaryMessage(_messagesList[8], _countdown - 2);
|
|
return;
|
|
}
|
|
|
|
if (!drillDeployed(_currentArea)) {
|
|
insertTemporaryMessage(_messagesList[13], _countdown - 2);
|
|
return;
|
|
}
|
|
|
|
if (_gameStateVars[k8bitVariableEnergy] < 5) {
|
|
insertTemporaryMessage(_messagesList[7], _countdown - 2);
|
|
return;
|
|
}
|
|
|
|
_gameStateVars[k8bitVariableEnergy] = _gameStateVars[k8bitVariableEnergy] - 5;
|
|
|
|
uint16 areaID = _currentArea->getAreaID();
|
|
if (_drillStatusByArea[areaID] > 0) {
|
|
if (_drillStatusByArea[areaID] == kDrillerRigInPlace)
|
|
_gameStateVars[32]--;
|
|
_drillStatusByArea[areaID] = kDrillerNoRig;
|
|
}
|
|
removeDrill(_currentArea);
|
|
insertTemporaryMessage(_messagesList[10], _countdown - 2);
|
|
int maxScore = _drillMaxScoreByArea[_currentArea->getAreaID()];
|
|
uint32 success = _drillSuccessByArea[_currentArea->getAreaID()];
|
|
uint32 scoreToRemove = uint32(maxScore * success) / 100;
|
|
assert(scoreToRemove <= uint32(_gameStateVars[k8bitVariableScore]));
|
|
_gameStateVars[k8bitVariableScore] -= scoreToRemove;
|
|
}
|
|
}
|
|
|
|
Math::Vector3d DrillerEngine::drillPosition() {
|
|
Math::Vector3d position = _position;
|
|
position.setValue(1, position.y() - _playerHeight);
|
|
position = position + 300 * getProjectionToPlane(_cameraFront, Math::Vector3d(0, 1, 0));
|
|
|
|
Object *obj = (GeometricObject *)_areaMap[255]->objectWithID(255); // Drill base
|
|
assert(obj);
|
|
position.setValue(0, position.x() - 128);
|
|
position.setValue(2, position.z() - 128);
|
|
return position;
|
|
}
|
|
|
|
bool DrillerEngine::drillDeployed(Area *area) {
|
|
return (area->objectWithID(255) != nullptr);
|
|
}
|
|
|
|
bool DrillerEngine::checkDrill(const Math::Vector3d position) {
|
|
GeometricObject *obj = nullptr;
|
|
Math::Vector3d origin = position;
|
|
|
|
int16 id;
|
|
int heightLastObject;
|
|
|
|
origin.setValue(0, origin.x() + 128);
|
|
origin.setValue(1, origin.y() - 5);
|
|
origin.setValue(2, origin.z() + 128);
|
|
|
|
_drillBase->setOrigin(origin);
|
|
if (_currentArea->checkCollisions(_drillBase->_boundingBox).empty())
|
|
return false;
|
|
|
|
origin.setValue(0, origin.x() - 128);
|
|
origin.setValue(2, origin.z() - 128);
|
|
|
|
id = 255;
|
|
obj = (GeometricObject *)_areaMap[255]->objectWithID(id);
|
|
assert(obj);
|
|
obj = (GeometricObject *)obj->duplicate();
|
|
origin.setValue(1, origin.y() + 6);
|
|
obj->setOrigin(origin);
|
|
|
|
// This bounding box is too large and can result in the drill to float next to a wall
|
|
if (!_currentArea->checkCollisions(obj->_boundingBox).empty())
|
|
return false;
|
|
|
|
origin.setValue(1, origin.y() + 15);
|
|
obj->setOrigin(origin);
|
|
|
|
if (!_currentArea->checkCollisions(obj->_boundingBox).empty())
|
|
return false;
|
|
|
|
origin.setValue(1, origin.y() - 10);
|
|
heightLastObject = obj->getSize().y();
|
|
delete obj;
|
|
|
|
id = 254;
|
|
debugC(1, kFreescapeDebugParser, "Adding object %d to room structure", id);
|
|
obj = (GeometricObject *)_areaMap[255]->objectWithID(id);
|
|
assert(obj);
|
|
// Set position for object
|
|
origin.setValue(0, origin.x() - obj->getSize().x() / 5);
|
|
origin.setValue(1, origin.y() + heightLastObject);
|
|
origin.setValue(2, origin.z() - obj->getSize().z() / 5);
|
|
|
|
obj = (GeometricObject *)obj->duplicate();
|
|
obj->setOrigin(origin);
|
|
if (!_currentArea->checkCollisions(obj->_boundingBox).empty())
|
|
return false;
|
|
|
|
// Undo offset
|
|
origin.setValue(0, origin.x() + obj->getSize().x() / 5);
|
|
heightLastObject = obj->getSize().y();
|
|
origin.setValue(2, origin.z() + obj->getSize().z() / 5);
|
|
delete obj;
|
|
|
|
id = 253;
|
|
debugC(1, kFreescapeDebugParser, "Adding object %d to room structure", id);
|
|
obj = (GeometricObject *)_areaMap[255]->objectWithID(id);
|
|
assert(obj);
|
|
obj = (GeometricObject *)obj->duplicate();
|
|
|
|
origin.setValue(0, origin.x() + obj->getSize().x() / 5);
|
|
origin.setValue(1, origin.y() + heightLastObject);
|
|
origin.setValue(2, origin.z() + obj->getSize().z() / 5);
|
|
|
|
obj->setOrigin(origin);
|
|
if (!_currentArea->checkCollisions(obj->_boundingBox).empty())
|
|
return false;
|
|
|
|
// Undo offset
|
|
// origin.setValue(0, origin.x() - obj->getSize().x() / 5);
|
|
heightLastObject = obj->getSize().y();
|
|
// origin.setValue(2, origin.z() - obj->getSize().z() / 5);
|
|
delete obj;
|
|
return true;
|
|
}
|
|
|
|
|
|
void DrillerEngine::addDrill(const Math::Vector3d position, bool gasFound) {
|
|
// int drillObjectIDs[8] = {255, 254, 253, 252, 251, 250, 248, 247};
|
|
GeometricObject *obj = nullptr;
|
|
Math::Vector3d origin = position;
|
|
|
|
int16 id;
|
|
int heightLastObject;
|
|
|
|
id = 255;
|
|
debugC(1, kFreescapeDebugParser, "Adding object %d to room structure", id);
|
|
obj = (GeometricObject *)_areaMap[255]->objectWithID(id);
|
|
assert(obj);
|
|
obj = (GeometricObject *)obj->duplicate();
|
|
obj->setOrigin(origin);
|
|
// offset.setValue(1, offset.y() + obj->getSize().y());
|
|
obj->makeVisible();
|
|
_currentArea->addObject(obj);
|
|
|
|
heightLastObject = obj->getSize().y();
|
|
|
|
id = 254;
|
|
debugC(1, kFreescapeDebugParser, "Adding object %d to room structure", id);
|
|
obj = (GeometricObject *)_areaMap[255]->objectWithID(id);
|
|
assert(obj);
|
|
// Set position for object
|
|
origin.setValue(0, origin.x() - obj->getSize().x() / 5);
|
|
origin.setValue(1, origin.y() + heightLastObject);
|
|
origin.setValue(2, origin.z() - obj->getSize().z() / 5);
|
|
|
|
obj = (GeometricObject *)obj->duplicate();
|
|
obj->setOrigin(origin);
|
|
obj->makeVisible();
|
|
_currentArea->addObject(obj);
|
|
|
|
// Undo offset
|
|
origin.setValue(0, origin.x() + obj->getSize().x() / 5);
|
|
heightLastObject = obj->getSize().y();
|
|
origin.setValue(2, origin.z() + obj->getSize().z() / 5);
|
|
|
|
id = 253;
|
|
debugC(1, kFreescapeDebugParser, "Adding object %d to room structure", id);
|
|
obj = (GeometricObject *)_areaMap[255]->objectWithID(id);
|
|
assert(obj);
|
|
obj = (GeometricObject *)obj->duplicate();
|
|
|
|
origin.setValue(0, origin.x() + obj->getSize().x() / 5);
|
|
origin.setValue(1, origin.y() + heightLastObject);
|
|
origin.setValue(2, origin.z() + obj->getSize().z() / 5);
|
|
|
|
obj->setOrigin(origin);
|
|
obj->makeVisible();
|
|
_currentArea->addObject(obj);
|
|
|
|
// Undo offset
|
|
// origin.setValue(0, origin.x() - obj->getSize().x() / 5);
|
|
heightLastObject = obj->getSize().y();
|
|
// origin.setValue(2, origin.z() - obj->getSize().z() / 5);
|
|
|
|
if (gasFound) {
|
|
id = 252;
|
|
debugC(1, kFreescapeDebugParser, "Adding object %d to room structure", id);
|
|
obj = (GeometricObject *)_areaMap[255]->objectWithID(id);
|
|
assert(obj);
|
|
obj = (GeometricObject *)obj->duplicate();
|
|
origin.setValue(0, origin.x() + obj->getSize().x());
|
|
origin.setValue(1, origin.y() + heightLastObject);
|
|
origin.setValue(2, origin.z() + obj->getSize().z());
|
|
obj->setOrigin(origin);
|
|
assert(obj);
|
|
obj->makeVisible();
|
|
_currentArea->addObject(obj);
|
|
heightLastObject = obj->getSize().y();
|
|
|
|
id = 251;
|
|
debugC(1, kFreescapeDebugParser, "Adding object %d to room structure", id);
|
|
obj = (GeometricObject *)_areaMap[255]->objectWithID(id);
|
|
assert(obj);
|
|
obj = (GeometricObject *)obj->duplicate();
|
|
origin.setValue(1, origin.y() + heightLastObject);
|
|
obj->setOrigin(origin);
|
|
assert(obj);
|
|
obj->makeVisible();
|
|
_currentArea->addObject(obj);
|
|
}
|
|
}
|
|
|
|
void DrillerEngine::removeDrill(Area *area) {
|
|
for (int16 id = 251; id < 256; id++) {
|
|
if (id > 252)
|
|
assert(area->objectWithID(id));
|
|
|
|
if (area->objectWithID(id))
|
|
area->removeObject(id);
|
|
}
|
|
}
|
|
|
|
void DrillerEngine::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[it._key] = 0;
|
|
if (_drillStatusByArea[it._key] != kDrillerNoRig)
|
|
removeDrill(it._value);
|
|
_drillStatusByArea[it._key] = kDrillerNoRig;
|
|
if (it._key != 255) {
|
|
_drillMaxScoreByArea[it._key] = (10 + _rnd->getRandomNumber(89)) * 1000;
|
|
}
|
|
_drillSuccessByArea[it._key] = 0;
|
|
}
|
|
|
|
_gameStateVars[k8bitVariableEnergy] = _initialTankEnergy;
|
|
_gameStateVars[k8bitVariableShield] = _initialTankShield;
|
|
|
|
_gameStateVars[k8bitVariableEnergyDrillerTank] = _initialTankEnergy;
|
|
_gameStateVars[k8bitVariableShieldDrillerTank] = _initialTankShield;
|
|
|
|
_gameStateVars[k8bitVariableEnergyDrillerJet] = _initialJetEnergy;
|
|
_gameStateVars[k8bitVariableShieldDrillerJet] = _initialJetShield;
|
|
|
|
_playerHeightNumber = 1;
|
|
_playerHeight = _playerHeights[_playerHeightNumber];
|
|
removeTimers();
|
|
startCountdown(_initialCountdown);
|
|
|
|
_demoIndex = 0;
|
|
_demoEvents.clear();
|
|
}
|
|
|
|
bool DrillerEngine::checkIfGameEnded() {
|
|
if (isDemo())
|
|
return (_demoData[_demoIndex + 1] == 0x5f);
|
|
|
|
if (_countdown <= 0) {
|
|
insertTemporaryMessage(_messagesList[14], _countdown - 2);
|
|
drawFrame();
|
|
_gfx->flipBuffer();
|
|
g_system->updateScreen();
|
|
g_system->delayMillis(2000);
|
|
gotoArea(127, 0);
|
|
}
|
|
|
|
if (_gameStateVars[k8bitVariableShield] == 0) {
|
|
insertTemporaryMessage(_messagesList[15], _countdown - 2);
|
|
drawFrame();
|
|
_gfx->flipBuffer();
|
|
g_system->updateScreen();
|
|
g_system->delayMillis(2000);
|
|
gotoArea(127, 0);
|
|
}
|
|
|
|
if (_gameStateVars[k8bitVariableEnergy] == 0) {
|
|
insertTemporaryMessage(_messagesList[16], _countdown - 2);
|
|
drawFrame();
|
|
_gfx->flipBuffer();
|
|
g_system->updateScreen();
|
|
g_system->delayMillis(2000);
|
|
gotoArea(127, 0);
|
|
}
|
|
|
|
if (_hasFallen) {
|
|
_hasFallen = false;
|
|
playSound(14, false);
|
|
insertTemporaryMessage(_messagesList[17], _countdown - 4);
|
|
drawBackground();
|
|
drawBorder();
|
|
drawUI();
|
|
_gfx->flipBuffer();
|
|
g_system->updateScreen();
|
|
g_system->delayMillis(1000);
|
|
gotoArea(127, 0);
|
|
}
|
|
|
|
if (_forceEndGame) {
|
|
_forceEndGame = false;
|
|
insertTemporaryMessage(_messagesList[18], _countdown - 2);
|
|
drawFrame();
|
|
_gfx->flipBuffer();
|
|
g_system->updateScreen();
|
|
g_system->delayMillis(2000);
|
|
gotoArea(127, 0);
|
|
}
|
|
|
|
if (_currentArea->getAreaID() == 127) {
|
|
if (_gameStateVars[32] == 18) { // All areas are complete
|
|
insertTemporaryMessage(_messagesList[19], _countdown - 2);
|
|
_gameStateVars[32] = 0; // Avoid repeating the message
|
|
}
|
|
drawFrame();
|
|
_gfx->flipBuffer();
|
|
g_system->updateScreen();
|
|
g_system->delayMillis(5000);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Common::Error DrillerEngine::saveGameStreamExtended(Common::WriteStream *stream, bool isAutosave) {
|
|
for (auto &it : _areaMap) { // All but skip area 255
|
|
if (it._key == 255)
|
|
continue;
|
|
stream->writeUint16LE(it._key);
|
|
stream->writeUint32LE(_drillStatusByArea[it._key]);
|
|
stream->writeUint32LE(_drillMaxScoreByArea[it._key]);
|
|
stream->writeUint32LE(_drillSuccessByArea[it._key]);
|
|
}
|
|
|
|
return Common::kNoError;
|
|
}
|
|
|
|
Common::Error DrillerEngine::loadGameStreamExtended(Common::SeekableReadStream *stream) {
|
|
for (uint i = 0; i < _areaMap.size() - 1; i++) { // All except area 255
|
|
uint16 key = stream->readUint16LE();
|
|
assert(key != 255);
|
|
assert(_areaMap.contains(key));
|
|
_drillStatusByArea[key] = stream->readUint32LE();
|
|
if (_drillStatusByArea[key] == kDrillerNoRig)
|
|
if (drillDeployed(_areaMap[key]))
|
|
removeDrill(_areaMap[key]);
|
|
|
|
_drillMaxScoreByArea[key] = stream->readUint32LE();
|
|
_drillSuccessByArea[key] = stream->readUint32LE();
|
|
}
|
|
|
|
return Common::kNoError;
|
|
}
|
|
|
|
} // End of namespace Freescape
|