mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-11 19:54:03 +00:00
413 lines
10 KiB
C++
413 lines
10 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 "glk/comprehend/game_oo.h"
|
|
#include "glk/comprehend/comprehend.h"
|
|
#include "glk/comprehend/draw_surface.h"
|
|
#include "glk/comprehend/pics.h"
|
|
#include "common/md5.h"
|
|
|
|
namespace Glk {
|
|
namespace Comprehend {
|
|
|
|
enum OOToposRoomFlag {
|
|
OO_ROOM_IN_SHIP = 1,
|
|
OO_ROOM_FLAG_DARK = 2
|
|
};
|
|
|
|
enum OOToposFlag {
|
|
OO_FLAG_9 = 9,
|
|
OO_FLAG_13 = 13,
|
|
OO_FLAG_22 = 22,
|
|
OO_BRIGHT_ROOM = 25,
|
|
OO_FLAG_WEARING_GOGGLES = 27,
|
|
OO_FLAG_FLASHLIGHT_ON = 39,
|
|
OO_FLAG_43 = 43,
|
|
OO_FLAG_44 = 44,
|
|
OO_FLAG_SUFFICIENT_FUEL = 51,
|
|
OO_FLAG_REVERSE_VIDEO = 53, // Effect of wearing goggles
|
|
OO_FLAG_TOO_DARK = 55,
|
|
OO_FLAG_TOO_BRIGHT = 56,
|
|
OO_FLAG_58 = 58,
|
|
OO_FLAG_59 = 59,
|
|
OO_FLAG_READY_TO_DEPART = 60,
|
|
OO_TRACTOR_BEAM = 71
|
|
};
|
|
|
|
enum OOToposItem {
|
|
ITEM_SERUM_VIAL = 39
|
|
};
|
|
|
|
static const GameStrings OO_STRINGS = {
|
|
EXTRA_STRING_TABLE(154)
|
|
};
|
|
|
|
OOToposGame::OOToposGame() : ComprehendGameV2(), _restartMode(RESTART_IMMEDIATE),
|
|
_noFloodfill(UNSET), _stringVal1(0), _stringVal2(0),
|
|
_printComputerMsg(true), _shipNotWorking(false) {
|
|
_gameDataFile = "g0";
|
|
|
|
// Extra strings are (annoyingly) stored in the game binary
|
|
Common::File f;
|
|
if (!f.open("novel.exe"))
|
|
error("novel.exe is a required file");
|
|
|
|
Common::String md5 = Common::computeStreamMD5AsString(f, 1024);
|
|
f.close();
|
|
|
|
if (md5 == "3fc2072f6996b17d2f21f0a92e53cdcc") {
|
|
// DOS version from if-archive
|
|
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x16564, 0x17640));
|
|
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x17702, 0x18600));
|
|
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x186b2, 0x19b80));
|
|
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x19c62, 0x1a590));
|
|
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x1a634, 0x1b080));
|
|
} else if (md5 == "e26858f2aaa9dcc28f468b07902813c5") {
|
|
// DOS version from graphicsmagician.com
|
|
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x164c4, 0x175a0));
|
|
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x17662, 0x18560));
|
|
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x18612, 0x19ae0));
|
|
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x19bc2, 0x1a4f0));
|
|
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x1a594, 0x1afe0));
|
|
} else {
|
|
error("Unrecognised novel.exe encountered");
|
|
}
|
|
|
|
_locationGraphicFiles.push_back("RA");
|
|
_locationGraphicFiles.push_back("RB");
|
|
_locationGraphicFiles.push_back("RC");
|
|
_locationGraphicFiles.push_back("RD");
|
|
_locationGraphicFiles.push_back("RE");
|
|
_itemGraphicFiles.push_back("OA");
|
|
_itemGraphicFiles.push_back("OB");
|
|
_itemGraphicFiles.push_back("OC");
|
|
_itemGraphicFiles.push_back("OD");
|
|
|
|
_colorTable = 1;
|
|
_gameStrings = &OO_STRINGS;
|
|
_titleGraphicFile = "t0";
|
|
}
|
|
|
|
void OOToposGame::beforeGame() {
|
|
// Draw the title
|
|
g_comprehend->drawPicture(TITLE_IMAGE);
|
|
|
|
// Print game information
|
|
console_println("Story by Michael and Muffy Berlyn, graphics by Raim und Redlich and Brian Poff");
|
|
console_println("IBM version by Jeffrey A. Jay. Copyright 1987 POLARWARE, Inc.");
|
|
g_comprehend->readChar();
|
|
|
|
g_comprehend->glk_window_clear(g_comprehend->_bottomWindow);
|
|
}
|
|
|
|
int OOToposGame::roomIsSpecial(uint room_index, uint *roomDescString) {
|
|
Room *room = &_rooms[room_index];
|
|
|
|
// Is the room dark
|
|
if ((room->_flags & OO_ROOM_FLAG_DARK) &&
|
|
!(_flags[OO_FLAG_FLASHLIGHT_ON])) {
|
|
if (roomDescString)
|
|
*roomDescString = 0xb3;
|
|
return ROOM_IS_DARK;
|
|
}
|
|
|
|
// Is the room too bright
|
|
if (room_index == OO_BRIGHT_ROOM &&
|
|
!_flags[OO_FLAG_WEARING_GOGGLES]) {
|
|
if (roomDescString)
|
|
*roomDescString = 0x1c;
|
|
return ROOM_IS_TOO_BRIGHT;
|
|
}
|
|
|
|
return ROOM_IS_NORMAL;
|
|
}
|
|
|
|
void OOToposGame::beforeTurn() {
|
|
ComprehendGameV2::beforeTurn();
|
|
|
|
if (_flags[OO_FLAG_TOO_DARK]) {
|
|
// Show placeholder room if room is too dark
|
|
_currentRoom = 55;
|
|
_updateFlags |= UPDATE_GRAPHICS;
|
|
} else if (_flags[OO_FLAG_TOO_BRIGHT]) {
|
|
// Show placeholder room if room is too bright
|
|
_currentRoom = 54;
|
|
_updateFlags |= UPDATE_GRAPHICS;
|
|
} else {
|
|
YesNo nff = _flags[OO_FLAG_REVERSE_VIDEO] ? YES : NO;
|
|
|
|
if (_noFloodfill != nff) {
|
|
_noFloodfill = nff;
|
|
_updateFlags |= UPDATE_GRAPHICS | UPDATE_ROOM_DESC;
|
|
|
|
if (_noFloodfill == YES)
|
|
g_comprehend->_drawFlags |= IMAGEF_REVERSE;
|
|
else
|
|
g_comprehend->_drawFlags &= ~IMAGEF_REVERSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void OOToposGame::beforePrompt() {
|
|
// Handle the computer console if in front of it
|
|
computerConsole();
|
|
}
|
|
|
|
void OOToposGame::afterPrompt() {
|
|
ComprehendGameV2::afterPrompt();
|
|
|
|
// WORKAROUND: Allow for the Apple 2 password in the DOS version
|
|
if (!scumm_stricmp(_inputLine, "vug957a"))
|
|
Common::strcpy_s(_inputLine, "tse957x");
|
|
|
|
if (_currentRoom != _currentRoomCopy)
|
|
_updateFlags |= UPDATE_GRAPHICS;
|
|
_currentRoom = _currentRoomCopy;
|
|
}
|
|
|
|
void OOToposGame::handleSpecialOpcode() {
|
|
switch (_specialOpcode) {
|
|
case 1:
|
|
// Update guard location
|
|
randomizeGuardLocation();
|
|
break;
|
|
|
|
case 2:
|
|
_restartMode = RESTART_IMMEDIATE;
|
|
game_restart();
|
|
break;
|
|
|
|
case 3:
|
|
_restartMode = RESTART_WITH_MSG;
|
|
game_restart();
|
|
break;
|
|
|
|
case 4:
|
|
_restartMode = RESTART_WITHOUT_MSG;
|
|
game_restart();
|
|
break;
|
|
|
|
case 5:
|
|
// Won the game
|
|
g_comprehend->quitGame();
|
|
break;
|
|
|
|
case 6:
|
|
// Save game
|
|
game_save();
|
|
break;
|
|
|
|
case 7:
|
|
// Restore game
|
|
game_restore();
|
|
break;
|
|
|
|
case 8:
|
|
// Computer response
|
|
computerResponse();
|
|
randomizeGuardLocation();
|
|
break;
|
|
|
|
case 9:
|
|
// Checks the ship fuel
|
|
checkShipFuel();
|
|
randomizeGuardLocation();
|
|
break;
|
|
|
|
case 10:
|
|
// Checks whether the ship is working
|
|
checkShipWorking();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool OOToposGame::handle_restart() {
|
|
_ended = false;
|
|
|
|
if (_restartMode != RESTART_IMMEDIATE) {
|
|
if (_restartMode == RESTART_WITH_MSG)
|
|
console_println(stringLookup(_gameStrings->game_restart).c_str());
|
|
|
|
if (tolower(console_get_key()) != 'r') {
|
|
g_comprehend->quitGame();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
loadGame();
|
|
_updateFlags = UPDATE_ALL;
|
|
return true;
|
|
}
|
|
|
|
void OOToposGame::synchronizeSave(Common::Serializer &s) {
|
|
if (s.isSaving())
|
|
_currentRoom = _currentRoomCopy;
|
|
|
|
ComprehendGameV2::synchronizeSave(s);
|
|
|
|
if (s.isLoading()) {
|
|
_noFloodfill = UNSET;
|
|
_currentRoomCopy = _currentRoom;
|
|
|
|
beforeTurn();
|
|
}
|
|
}
|
|
|
|
void OOToposGame::randomizeGuardLocation() {
|
|
Item *item = get_item(22);
|
|
if (_flags[OO_FLAG_13] && item->_room != _currentRoom) {
|
|
if (getRandomNumber(255) > 128 && (_currentRoom == 3 || _currentRoom == 6))
|
|
item->_room = _currentRoom;
|
|
}
|
|
}
|
|
|
|
void OOToposGame::computerConsole() {
|
|
if (_currentRoom == 57) {
|
|
if (!_flags[OO_FLAG_9]) {
|
|
// Mission Code:
|
|
console_println(_strings2[129].c_str());
|
|
} else if (!_flags[OO_FLAG_58]) {
|
|
// Welcome back! I was wondering if you would be returning
|
|
console_println(_strings2[131].c_str());
|
|
_flags[OO_FLAG_58] = true;
|
|
_printComputerMsg = true;
|
|
checkShipWorking();
|
|
} else if (_flags[OO_FLAG_59]) {
|
|
checkShipDepart();
|
|
} else if (_flags[OO_FLAG_43]) {
|
|
// We can reach Mealy Sukas with the fuel we have left
|
|
console_println(_strings2[142].c_str());
|
|
_flags[OO_FLAG_59] = true;
|
|
|
|
if (_flags[OO_FLAG_44])
|
|
// The currency on Mealy Sukas is the 'frod'
|
|
console_println(_strings2[144].c_str());
|
|
else
|
|
// Without evaluation data as to the current fuel prices
|
|
console_println(_strings2[143].c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void OOToposGame::computerResponse() {
|
|
console_println(_strings2[145].c_str());
|
|
if (_flags[OO_FLAG_43])
|
|
console_println(_strings2[144].c_str());
|
|
else
|
|
console_println(_strings2[152].c_str());
|
|
}
|
|
|
|
void OOToposGame::checkShipWorking() {
|
|
_stringVal1 = 164;
|
|
_stringVal2 = 0;
|
|
|
|
// Iterate through the ship's flags
|
|
for (int idx = 42; idx < 51; ++idx, ++_stringVal1) {
|
|
if (!_flags[idx]) {
|
|
if (!_stringVal2) {
|
|
// The following components are not installed
|
|
printComputerMsg(_strings2[132].c_str());
|
|
_stringVal2 = 1;
|
|
}
|
|
|
|
// Power Cylinder
|
|
printComputerMsg(_strings[_stringVal1].c_str());
|
|
}
|
|
}
|
|
|
|
_shipNotWorking = _stringVal2 != 0;
|
|
if (!_shipNotWorking)
|
|
// The ship is in working order
|
|
printComputerMsg(_strings2[153].c_str());
|
|
}
|
|
|
|
void OOToposGame::checkShipFuel() {
|
|
const byte ITEMS[7] = { 24, 27, 28, 29, 30, 31, 32 };
|
|
_variables[0x4b] = 0;
|
|
_stringVal1 = 68;
|
|
_stringVal2 = 0;
|
|
|
|
for (int idx = 168; idx < 175; ++idx, ++_stringVal1, ++_stringVal2) {
|
|
if (_flags[idx]) {
|
|
Item *item = get_item(ITEMS[_stringVal2] - 1);
|
|
if (item->_room == ROOM_INVENTORY || (get_room(item->_room)->_flags & OO_ROOM_IN_SHIP) != 0) {
|
|
Instruction varAdd(0x86, 0x4B, _stringVal1);
|
|
execute_opcode(&varAdd, nullptr, nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Computer: "Our current evaluation...
|
|
Instruction strReplace(0xC9, 0x4B);
|
|
execute_opcode(&strReplace, nullptr, nullptr);
|
|
printComputerMsg(_strings2[146].c_str());
|
|
|
|
FunctionState funcState;
|
|
Instruction test(2, 75, 76);
|
|
execute_opcode(&test, nullptr, nullptr);
|
|
|
|
if (funcState._testResult) {
|
|
// Computer: "We should now have enough
|
|
_flags[OO_FLAG_SUFFICIENT_FUEL] = true;
|
|
printComputerMsg(_strings2[151].c_str());
|
|
} else {
|
|
_flags[OO_FLAG_SUFFICIENT_FUEL] = false;
|
|
}
|
|
}
|
|
|
|
void OOToposGame::checkShipDepart() {
|
|
_printComputerMsg = false;
|
|
checkShipWorking();
|
|
checkShipFuel();
|
|
_printComputerMsg = true;
|
|
|
|
if (!_shipNotWorking && _flags[OO_FLAG_SUFFICIENT_FUEL]) {
|
|
Item *item = get_item(ITEM_SERUM_VIAL - 1);
|
|
if (item->_room == ROOM_INVENTORY || (get_room(item->_room)->_flags & OO_ROOM_IN_SHIP) != 0) {
|
|
if (!_flags[OO_TRACTOR_BEAM]) {
|
|
// I detect a tractor beam
|
|
console_println(_strings2[77].c_str());
|
|
} else if (!_flags[OO_FLAG_READY_TO_DEPART]) {
|
|
// All systems check. Ready to depart
|
|
_flags[OO_FLAG_22] = true;
|
|
console_println(_strings2[79].c_str());
|
|
} else {
|
|
// Please close the airlock
|
|
console_println(_strings2[76].c_str());
|
|
}
|
|
} else {
|
|
// The serum vial is not aboard the ship
|
|
console_println(_strings2[78].c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void OOToposGame::printComputerMsg(const char *str) {
|
|
if (_printComputerMsg)
|
|
console_println(str);
|
|
}
|
|
|
|
} // namespace Comprehend
|
|
} // namespace Glk
|