Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

2
engines/access/POTFILES Normal file
View File

@@ -0,0 +1,2 @@
engines/access/metaengine.cpp
engines/access/resources.cpp

609
engines/access/access.cpp Normal file
View File

@@ -0,0 +1,609 @@
/* 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/scummsys.h"
#include "common/config-manager.h"
#include "common/debug-channels.h"
#include "common/events.h"
#include "engines/util.h"
#include "graphics/scaler.h"
#include "graphics/thumbnail.h"
#include "access/access.h"
#include "access/debugger.h"
namespace Access {
AccessEngine::AccessEngine(OSystem *syst, const AccessGameDescription *gameDesc)
: _gameDescription(gameDesc), Engine(syst), _randomSource("Access"),
_useItem(_flags[99]), _startup(_flags[170]), _manScaleOff(_flags[172]), _pictureTaken(_flags[176]) {
// Set up debug channels
_aboutBox = nullptr;
_animation = nullptr;
_bubbleBox = nullptr;
_char = nullptr;
_events = nullptr;
_files = nullptr;
_invBox = nullptr;
_inventory = nullptr;
_helpBox = nullptr;
_midi = nullptr;
_player = nullptr;
_res = nullptr;
_room = nullptr;
_screen = nullptr;
_scripts = nullptr;
_sound = nullptr;
_travelBox = nullptr;
_video = nullptr;
_destIn = nullptr;
_current = nullptr;
_mouseMode = 0;
_playerDataCount = 0;
_currentMan = 0;
_currentManOld = -1;
_converseMode = 0;
_startup = 0;
_currentCharFlag = false;
_boxSelect = false;
_scale = 0;
_scaleH1 = _scaleH2 = 0;
_scaleN1 = 0;
_scaleT1 = 0;
_scaleMaxY = 0;
_scaleI = 0;
_scrollCol = _scrollRow = 0;
_scrollX = _scrollY = 0;
_imgUnscaled = false;
_canSaveLoad = false;
_establish = nullptr;
_conversation = 0;
_newTime = 0;
_newDate = 0;
Common::fill(&_objectsTable[0], &_objectsTable[100], (SpriteResource *)nullptr);
Common::fill(&_establishTable[0], &_establishTable[100], false);
Common::fill(&_flags[0], &_flags[256], 0);
_establishFlag = false;
_establishMode = 0;
_establishGroup = 0;
_establishCtrlTblOfs = 0;
_lastTime = g_system->getMillis();
_curTime = 0;
_narateFile = 0;
_txtPages = 0;
_sndSubFile = 0;
_loadSaveSlot = -1;
_vidX = _vidY = 0;
_cheatFl = false;
_restartFl = false;
_printEnd = 0;
ARRAYCLEAR(_objectsTable);
_clearSummaryFlag = false;
ARRAYCLEAR(_travel);
_startTravelItem = _startTravelBox = 0;
ARRAYCLEAR(_ask);
_startAboutItem = _startAboutBox = 0;
_byte26CB5 = 0;
_bcnt = 0;
_boxDataStart = 0;
_boxDataEnd = false;
_boxSelectY = 0;
_boxSelectYOld = -1;
_numLines = 0;
_pictureTaken = 0;
_vidEnd = false;
_icons = nullptr;
ARRAYCLEAR(_countTbl);
}
AccessEngine::~AccessEngine() {
delete _animation;
delete _bubbleBox;
delete _helpBox;
delete _travelBox;
delete _invBox;
delete _aboutBox;
delete _char;
delete _events;
delete _files;
delete _inventory;
delete _midi;
delete _player;
delete _res;
delete _room;
delete _screen;
delete _scripts;
delete _sound;
delete _video;
delete _icons;
freeCells();
delete _establish;
}
void AccessEngine::setVGA() {
initGraphics(320, 200);
}
void AccessEngine::initialize() {
if (isCD()) {
const Common::FSNode gameDataDir(ConfMan.getPath("path"));
// The CD version contains two versions of the game.
// - The MCGA version, in the CDROM folder
// - The VESA version, in the TDROM folder
// We use the hires version.
// Use forward slash for the folders separator, as documented for SearchSet::addSubDirectoryMatching()
const Common::String subfolderMatchPrefix = "tdrom/";
for (int idx = 0; idx < 15; ++idx) {
Common::String folder = subfolderMatchPrefix + ((idx == 0) ? "game" : Common::String::format("chap%.2d", idx));
SearchMan.addSubDirectoryMatching(gameDataDir, folder);
}
}
// Create sub-objects of the engine
_animation = new AnimationManager(this);
_bubbleBox = new BubbleBox(this, TYPE_2, 64, 32, 130, 122, 0, 0, 0, 0, "");
if (getGameID() == kGameMartianMemorandum) {
// Note: add 1 more pixel to width and height to get the right box size.
_helpBox = new BubbleBox(this, TYPE_1, 64, 24, 147, 123, 1, 32, 2, 76, "HELP");
_travelBox = new BubbleBox(this, TYPE_1, 64, 32, 195, 123, 1, 24, 2, 74, "TRAVEL");
_invBox = new BubbleBox(this, TYPE_1, 64, 32, 147, 123, 1, 32, 2, 76, "INVENTORY");
_aboutBox = new BubbleBox(this, TYPE_1, 64, 32, 195, 123, 1, 32, 2, 76, "ASK ABOUT");
} else {
_helpBox = nullptr;
_travelBox = nullptr;
_invBox = nullptr;
_aboutBox = nullptr;
}
_char = new CharManager(this);
_events = new EventsManager(this);
_files = new FileManager(this);
_inventory = new InventoryManager(this);
_player = Player::init(this);
_screen = new Screen(this);
_sound = new SoundManager(this, _mixer);
_midi = new MusicManager(this);
_video = new VideoPlayer(this);
syncSoundSettings();
setDebugger(Debugger::init(this));
_buffer1.create(g_system->getWidth() + TILE_WIDTH, g_system->getHeight());
_buffer2.create(g_system->getWidth(), g_system->getHeight());
_vidBuf.create(160, 101);
// If requested, load a savegame instead of showing the intro
if (ConfMan.hasKey("save_slot")) {
int saveSlot = ConfMan.getInt("save_slot");
if (saveSlot >= 0 && saveSlot <= 999)
_loadSaveSlot = saveSlot;
}
}
const SpriteResource *AccessEngine::getIcons() {
if (!_icons) {
Resource *iconData = _files->loadFile("ICONS.LZ");
_icons = new SpriteResource(this, iconData);
delete iconData;
}
return _icons;
}
Common::Error AccessEngine::run() {
_res = Resources::init(this);
Common::U32String errorMessage;
if (!_res->load(errorMessage)) {
GUIErrorMessage(errorMessage);
return Common::kNoError;
}
setVGA();
initialize();
playGame();
return Common::kNoError;
}
int AccessEngine::getRandomNumber(int maxNumber) {
return _randomSource.getRandomNumber(maxNumber);
}
void AccessEngine::loadCells(const Common::Array<CellIdent> &cells) {
for (const auto &cell : cells) {
Resource *spriteData = _files->loadFile(cell);
assert(_objectsTable[cell._cell] == nullptr); // ensure no leaks
_objectsTable[cell._cell] = new SpriteResource(this, spriteData);
delete spriteData;
}
}
void AccessEngine::freeCells() {
for (int i = 0; i < ARRAYSIZE(_objectsTable); ++i) {
delete _objectsTable[i];
_objectsTable[i] = nullptr;
}
}
void AccessEngine::speakText(BaseSurface *s, const Common::String &msg) {
Common::String lines = msg;
Common::String line;
int curPage = 0;
int soundsLeft = 0;
while (!shouldQuit()) {
soundsLeft = _countTbl[curPage];
_events->zeroKeysActions();
int width = 0;
bool lastLine = _fonts._font2->getLine(lines, s->_maxChars * 6, line, width);
// Set font colors
Font::_fontColors[0] = 0;
Font::_fontColors[1] = 28;
Font::_fontColors[2] = 29;
Font::_fontColors[3] = 30;
_fonts._font2->drawString(s, line, s->_printOrg);
s->_printOrg = Common::Point(s->_printStart.x, s->_printOrg.y + 9);
if ((s->_printOrg.y > _printEnd) && (!lastLine)) {
_events->clearEvents();
while (!shouldQuit()) {
_sound->freeSounds();
_sound->loadSoundTable(0, _narateFile + 99, _sndSubFile);
_sound->playSound(0);
while(_sound->isSFXPlaying() && !shouldQuit())
_events->pollEvents();
_scripts->cmdFreeSound();
if (_events->isKeyActionMousePressed()) {
_sndSubFile += soundsLeft;
break;
} else {
++_sndSubFile;
--soundsLeft;
if (soundsLeft == 0)
break;
_events->clearEvents();
}
}
s->copyBuffer(&_buffer2);
s->_printOrg.y = s->_printStart.y;
++curPage;
soundsLeft = _countTbl[curPage];
}
if (lastLine)
break;
}
while (soundsLeft) {
_sound->freeSounds();
Resource *res = _sound->loadSound(_narateFile + 99, _sndSubFile);
_sound->_soundTable.push_back(SoundEntry(res, 1));
_sound->playSound(0);
while(_sound->isSFXPlaying() && !shouldQuit())
_events->pollEvents();
_scripts->cmdFreeSound();
if (_events->_leftButton) {
_events->debounceLeft();
_sndSubFile += soundsLeft;
break;
} else if (_events->isKeyActionPending()) {
_sndSubFile += soundsLeft;
break;
} else {
++_sndSubFile;
--soundsLeft;
}
}
}
void AccessEngine::printText(BaseSurface *s, const Common::String &msg) {
Common::String lines = msg;
Common::String line;
int width = 0;
for (;;) {
bool lastLine = _fonts._font2->getLine(lines, s->_maxChars * 6, line, width);
// Set font colors
_fonts._font2->_fontColors[0] = 0;
_fonts._font2->_fontColors[1] = 28;
_fonts._font2->_fontColors[2] = 29;
_fonts._font2->_fontColors[3] = 30;
_fonts._font2->drawString(s, line, s->_printOrg);
s->_printOrg = Common::Point(s->_printStart.x, s->_printOrg.y + 9);
if (s->_printOrg.y >_printEnd && !lastLine) {
_events->waitKeyActionMouse();
s->copyBuffer(&_buffer2);
s->_printOrg.y = s->_printStart.y;
}
if (lastLine)
break;
}
_events->waitKeyActionMouse();
}
void AccessEngine::plotList() {
_player->calcPlayer();
plotList1();
}
void AccessEngine::plotList1() {
for (uint idx = 0; idx < _images.size(); ++idx) {
ImageEntry &ie = _images[idx];
_imgUnscaled = (ie._flags & IMGFLAG_UNSCALED) != 0;
Common::Point pt = ie._position - _screen->_bufferStart;
const SpriteResource *sprites = ie._spritesPtr;
const SpriteFrame *frame = sprites->getFrame(ie._frameNumber);
Common::Rect bounds(pt.x, pt.y, pt.x + frame->w, pt.y + frame->h);
if (!_imgUnscaled) {
bounds.setWidth(_screen->_scaleTable1[frame->w]);
bounds.setHeight(_screen->_scaleTable1[frame->h]);
}
// Make a copy - some of the drawing methods I've adapted need the full
// scaled dimensions on-screen, and handle clipping themselves
Common::Rect destBounds = bounds;
if (_buffer2.clip(bounds)) {
ie._flags |= IMGFLAG_CROPPED;
} else {
ie._flags &= ~IMGFLAG_CROPPED;
if (_buffer2._leftSkip != 0 || _buffer2._rightSkip != 0
|| _buffer2._topSkip != 0 || _buffer2._bottomSkip != 0)
ie._flags |= IMGFLAG_CROPPED;
_newRects.push_back(bounds);
if (!_imgUnscaled) {
_buffer2._rightSkip /= _scale;
bounds.setWidth(bounds.width() / _scale);
if (ie._flags & IMGFLAG_BACKWARDS) {
_buffer2.sPlotB(frame, destBounds);
} else {
_buffer2.sPlotF(frame, destBounds);
}
} else {
if (ie._flags & IMGFLAG_BACKWARDS) {
_buffer2.plotB(frame, Common::Point(destBounds.left, destBounds.top));
} else {
_buffer2.plotF(frame, Common::Point(destBounds.left, destBounds.top));
}
}
}
ie._flags |= IMGFLAG_DRAWN;
}
}
void AccessEngine::copyBlocks() {
// Copy the block list from the previous frame
for (const auto &rect : _oldRects) {
_screen->copyBlock(&_buffer2, rect);
}
copyRects();
}
void AccessEngine::copyRects() {
_oldRects.clear();
for (const auto &rect : _newRects) {
_screen->copyBlock(&_buffer2, rect);
_oldRects.push_back(rect);
}
}
void AccessEngine::copyBF1BF2() {
_buffer2.copyRectToSurface(_buffer1, 0, 0,
Common::Rect(_scrollX, _scrollY,
_scrollX + _screen->_vWindowBytesWide,
_scrollY + _screen->_vWindowLinesTall));
}
void AccessEngine::copyBF2Vid() {
_screen->blitFrom(_buffer2,
Common::Rect(0, 0, _screen->_vWindowBytesWide, _screen->_vWindowLinesTall),
Common::Point(_screen->_windowXAdd, _screen->_windowYAdd));
}
void AccessEngine::playVideo(int videoNum, const Common::Point &pt) {
_video->setVideo(_screen, pt, FileIdent(96, videoNum), 10);
while (!shouldQuit() && !_video->_videoEnd) {
_video->playVideo();
_events->pollEventsAndWait();
}
}
void AccessEngine::freeChar() {
_scripts->freeScriptData();
_animation->clearTimers();
_animation->freeAnimationData();
_player->freeSprites();
}
void AccessEngine::syncSoundSettings() {
Engine::syncSoundSettings();
_midi->syncVolume();
_sound->syncVolume();
}
Common::Error AccessEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
Common::OutSaveFile *out = g_system->getSavefileManager()->openForSaving(
getSaveStateName(slot));
if (!out)
return Common::kCreatingFileFailed;
AccessSavegameHeader header;
header._saveName = desc;
writeSavegameHeader(out, header);
Common::Serializer s(nullptr, out);
synchronize(s);
out->finalize();
delete out;
return Common::kNoError;
}
Common::Error AccessEngine::loadGameState(int slot) {
Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(
getSaveStateName(slot));
if (!saveFile)
return Common::kReadingFailed;
Common::Serializer s(saveFile, nullptr);
// Load the savaegame header
AccessSavegameHeader header;
if (!readSavegameHeader(saveFile, header))
error("Invalid savegame");
// Load most of the savegame data
synchronize(s);
delete saveFile;
// Set extra post-load state
_room->_function = FN_CLEAR1;
_timers._timersSavedFlag = false;
_events->clearEvents();
return Common::kNoError;
}
bool AccessEngine::canLoadGameStateCurrently(Common::U32String *msg) {
return _canSaveLoad;
}
bool AccessEngine::canSaveGameStateCurrently(Common::U32String *msg) {
return _canSaveLoad;
}
void AccessEngine::synchronize(Common::Serializer &s) {
s.syncAsUint16LE(_conversation);
s.syncAsUint16LE(_currentMan);
s.syncAsUint32LE(_newTime);
s.syncAsUint32LE(_newDate);
for (int i = 0; i < 256; ++i)
s.syncAsUint16LE(_flags[i]);
for (int i = 0; i < 100; ++i)
s.syncAsByte(_establishTable[i]);
// Synchronize sub-objects
_timers.synchronize(s);
_inventory->synchronize(s);
_player->synchronize(s);
}
const char *const SAVEGAME_STR = "ACCESS";
#define SAVEGAME_STR_SIZE 6
WARN_UNUSED_RESULT bool AccessEngine::readSavegameHeader(Common::InSaveFile *in, AccessSavegameHeader &header, bool skipThumbnail) {
char saveIdentBuffer[SAVEGAME_STR_SIZE + 1];
// Validate the header Id
in->read(saveIdentBuffer, SAVEGAME_STR_SIZE + 1);
if (strncmp(saveIdentBuffer, SAVEGAME_STR, SAVEGAME_STR_SIZE))
return false;
header._version = in->readByte();
if (header._version > ACCESS_SAVEGAME_VERSION)
return false;
// Read in the string
header._saveName = in->readString();
// Get the thumbnail
if (!Graphics::loadThumbnail(*in, header._thumbnail, skipThumbnail)) {
return false;
}
// Read in save date/time
header._year = in->readSint16LE();
header._month = in->readSint16LE();
header._day = in->readSint16LE();
header._hour = in->readSint16LE();
header._minute = in->readSint16LE();
header._totalFrames = in->readUint32LE();
return true;
}
void AccessEngine::writeSavegameHeader(Common::OutSaveFile *out, AccessSavegameHeader &header) {
// Write out a savegame header
out->write(SAVEGAME_STR, SAVEGAME_STR_SIZE + 1);
out->writeByte(ACCESS_SAVEGAME_VERSION);
// Write savegame name
out->writeString(header._saveName);
out->writeByte('\0');
// Write a thumbnail of the screen
uint8 thumbPalette[Graphics::PALETTE_SIZE];
_screen->getPalette(thumbPalette);
Graphics::Surface saveThumb;
::createThumbnail(&saveThumb, (const byte *)_screen->getPixels(),
_screen->w, _screen->h, thumbPalette);
Graphics::saveThumbnail(*out, saveThumb);
saveThumb.free();
// Write out the save date/time
TimeDate td;
g_system->getTimeAndDate(td);
out->writeSint16LE(td.tm_year + 1900);
out->writeSint16LE(td.tm_mon + 1);
out->writeSint16LE(td.tm_mday);
out->writeSint16LE(td.tm_hour);
out->writeSint16LE(td.tm_min);
out->writeUint32LE(_events->getFrameCounter());
}
bool AccessEngine::shouldQuitOrRestart() {
return shouldQuit() || _restartFl;
}
} // End of namespace Access

369
engines/access/access.h Normal file
View File

@@ -0,0 +1,369 @@
/* 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/>.
*
*/
#ifndef ACCESS_ACCESS_H
#define ACCESS_ACCESS_H
#include "common/scummsys.h"
#include "common/system.h"
#include "common/error.h"
#include "common/random.h"
#include "common/savefile.h"
#include "common/serializer.h"
#include "common/util.h"
#include "engines/engine.h"
#include "graphics/surface.h"
#include "access/animation.h"
#include "access/bubble_box.h"
#include "access/char.h"
#include "access/data.h"
#include "access/events.h"
#include "access/files.h"
#include "access/font.h"
#include "access/inventory.h"
#include "access/player.h"
#include "access/resources.h"
#include "access/room.h"
#include "access/screen.h"
#include "access/scripts.h"
#include "access/sound.h"
#include "access/video.h"
#include "access/detection.h"
/**
* This is the namespace of the Access engine.
*
* Status of this engine: In Development
*
* Games using this engine:
* - Amazon: Guardians of Eden
*/
namespace Access {
enum AccessDebugChannels {
kDebugPath = 1,
kDebugScripts,
kDebugGraphics,
kDebugSound,
};
/* typed enum to match unsignedness of Common::CustomEventType */
enum ACCESSActions : Common::CustomEventType {
kActionNone,
kActionMoveUp,
kActionMoveDown,
kActionMoveLeft,
kActionMoveRight,
kActionMoveUpLeft,
kActionMoveUpRight,
kActionMoveDownLeft,
kActionMoveDownRight,
kActionLook,
kActionUse,
kActionTake,
kActionInventory,
kActionClimb,
kActionTalk,
kActionWalk,
kActionHelp,
kActionOpen,
kActionMove,
kActionTravel,
kActionSkip,
kActionSaveLoad,
};
struct AccessActionCode {
ACCESSActions _action;
int8 _code;
};
static const AccessActionCode AMAZON_ACTION_CODES[] = {
{ kActionLook, 1 },
{ kActionUse, 2 },
{ kActionTake, 3 },
{ kActionInventory, 4 },
{ kActionClimb, 5 },
{ kActionTalk, 6 },
{ kActionWalk, 7 },
{ kActionHelp, 8 },
{ kActionSaveLoad, -2 },
{ kActionNone, -1 },
};
static const AccessActionCode MARTIAN_ACTION_CODES[] = {
{ kActionLook, 0 },
{ kActionOpen, 1 },
{ kActionMove, 2 },
{ kActionTake, 3 },
{ kActionUse, 4 },
{ kActionWalk, 5 },
{ kActionTalk, 6 },
{ kActionTravel, 7 },
{ kActionHelp, 8 },
{ kActionSaveLoad, -2 },
{ kActionNone, -1 },
};
#define ACCESS_SAVEGAME_VERSION 1
struct AccessSavegameHeader {
uint8 _version;
Common::String _saveName;
Graphics::Surface *_thumbnail;
int _year, _month, _day;
int _hour, _minute;
int _totalFrames;
};
class AccessEngine : public Engine {
private:
uint32 _lastTime, _curTime;
/**
* A cache for the ICONS.LZ sprite data
*/
SpriteResource *_icons;
/**
* Handles basic initialization
*/
void initialize();
/**
* Set VGA mode
*/
void setVGA();
protected:
const AccessGameDescription *_gameDescription;
Common::RandomSource _randomSource;
int _loadSaveSlot;
/**
* Main handler for showing game rooms
*/
void doRoom();
/**
* Play back an entire video
*/
void playVideo(int videoNum, const Common::Point &pt);
// Engine APIs
Common::Error run() override;
bool hasFeature(EngineFeature f) const override;
protected:
/**
* Play the game
*/
virtual void playGame() = 0;
/**
* Synchronize savegame data
*/
virtual void synchronize(Common::Serializer &s);
public:
AnimationManager *_animation;
BubbleBox *_bubbleBox;
BubbleBox *_helpBox;
BubbleBox *_travelBox;
BubbleBox *_invBox;
BubbleBox *_aboutBox;
CharManager *_char;
EventsManager *_events;
FileManager *_files;
InventoryManager *_inventory;
Player *_player;
Resources *_res;
Room *_room;
Screen *_screen;
Scripts *_scripts;
SoundManager *_sound;
MusicManager *_midi;
VideoPlayer *_video;
BaseSurface *_destIn;
BaseSurface *_current;
ASurface _buffer1;
ASurface _buffer2;
ASurface _vidBuf;
int _vidX, _vidY;
SpriteResource *_objectsTable[100];
bool _establishTable[100];
bool _establishFlag;
int _establishMode;
int _establishGroup;
int _establishCtrlTblOfs;
TimerList _timers;
DeathList _deaths;
FontManager _fonts;
Common::Array<Common::Rect> _newRects;
Common::Array<Common::Rect> _oldRects;
Common::Array<ExtraCell> _extraCells;
ImageEntryList _images;
int _mouseMode;
uint8 _playerDataCount;
int _currentManOld;
int _converseMode;
bool _currentCharFlag;
bool _boxSelect;
int _scale;
int _scaleH1, _scaleH2;
int _scaleN1;
int _scaleT1;
int _scaleMaxY;
int _scaleI;
int _scrollX, _scrollY;
int _scrollCol, _scrollRow;
bool _imgUnscaled;
bool _canSaveLoad;
Resource *_establish;
int _printEnd;
int _txtPages;
int _narateFile;
int _sndSubFile;
int _countTbl[6];
// Fields that are included in savegames
int _conversation;
int _currentMan;
uint32 _newTime;
uint32 _newDate;
int _flags[256];
// Fields used by MM
// TODO: Refactor
byte _travel[60];
byte _ask[40];
int _startTravelItem;
int _startTravelBox;
int _startAboutItem;
int _startAboutBox;
int _boxDataStart;
bool _boxDataEnd;
int _boxSelectY;
int _boxSelectYOld;
int _numLines;
byte _byte26CB5;
int _bcnt;
bool _vidEnd;
bool _clearSummaryFlag;
bool _cheatFl;
bool _restartFl;
// Fields mapped into the flags array
int &_useItem;
int &_startup;
int &_manScaleOff;
int &_pictureTaken;
public:
AccessEngine(OSystem *syst, const AccessGameDescription *gameDesc);
~AccessEngine() override;
virtual void dead(int deathId) = 0;
uint32 getFeatures() const;
bool isCD() const;
bool isDemo() const;
Common::Language getLanguage() const;
Common::Platform getPlatform() const;
uint16 getVersion() const;
uint32 getGameID() const;
uint32 getGameFeatures() const;
bool shouldQuitOrRestart();
int getRandomNumber(int maxNumber);
const SpriteResource *getIcons();
void loadCells(const Common::Array<CellIdent> &cells);
/**
* Free the sprites list
*/
void freeCells();
virtual void establish(int esatabIndex, int sub) = 0;
void plotList();
void plotList1();
void copyBlocks();
void copyRects();
void copyBF1BF2();
void copyBF2Vid();
void freeChar();
/**
* Draw a string on a given surface and update text positioning
*/
void printText(BaseSurface *s, const Common::String &msg);
void speakText(BaseSurface *s, const Common::String &msg);
void syncSoundSettings() override;
/**
* Load a savegame
*/
Common::Error loadGameState(int slot) override;
/**
* Save the game
*/
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
/**
* Returns true if a savegame can currently be loaded
*/
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
/**
* Returns true if the game can currently be saved
*/
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
/**
* Read in a savegame header
*/
WARN_UNUSED_RESULT static bool readSavegameHeader(Common::InSaveFile *in, AccessSavegameHeader &header, bool skipThumbnail = true);
/**
* Write out a savegame header
*/
void writeSavegameHeader(Common::OutSaveFile *out, AccessSavegameHeader &header);
bool playMovie(const Common::Path &filename, const Common::Point &pos);
};
} // End of namespace Access
#endif /* ACCESS_ACCESS_H */

View File

@@ -0,0 +1,792 @@
/* 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 "access/resources.h"
#include "access/amazon/amazon_game.h"
#include "access/amazon/amazon_resources.h"
#include "access/amazon/amazon_room.h"
#include "access/amazon/amazon_scripts.h"
namespace Access {
namespace Amazon {
AmazonEngine::AmazonEngine(OSystem *syst, const AccessGameDescription *gameDesc)
: AccessEngine(syst, gameDesc), _guardLocation(_flags[122]), _guardFind(_flags[128]),
_helpLevel(_flags[167]), _jasMayaFlag(_flags[168]), _moreHelp(_flags[169]),
_flashbackFlag(_flags[171]), _riverFlag(_flags[185]), _aniOutFlag(_flags[195]),
_badEnd(_flags[218]), _noHints(_flags[219]), _aniFlag(_flags[229]),
_allenFlag(_flags[237]), _noSound(_flags[239]) {
_ant = nullptr;
_cast = nullptr;
_guard = nullptr;
_jungle = nullptr;
_opening = nullptr;
_plane = nullptr;
_river = nullptr;
_charSegSwitch = false;
_oldTitleChapter = _chapter = 0;
_updateChapter = -1;
_rawInactiveX = 0;
_rawInactiveY = 0;
_inactiveYOff = 0;
_hintLevel = 0;
Common::fill(&_tileData[0], &_tileData[0] + sizeof(_tileData), 0);
Common::fill(&_help1[0], &_help1[0] + sizeof(_help1), 0);
Common::fill(&_help2[0], &_help2[0] + sizeof(_help2), 0);
Common::fill(&_help3[0], &_help3[0] + sizeof(_help3), 0);
_helpTbl[0] = _help1;
_helpTbl[1] = _help2;
_helpTbl[2] = _help3;
_chapter = 0;
_rawInactiveX = _rawInactiveY = 0;
_inactiveYOff = 0;
_hintLevel = 0;
_updateChapter = 0;
_oldTitleChapter = 0;
_iqValue = 0;
_chapterCells.push_back(CellIdent(0, 96, 17));
_inactive._spritesPtr = nullptr;
_inactive._flags = _inactive._frameNumber = _inactive._offsetY = 0;
_inactive._position = Common::Point(0, 0);
}
AmazonEngine::~AmazonEngine() {
delete _inactive._altSpritesPtr;
delete _ant;
delete _cast;
delete _guard;
delete _jungle;
delete _opening;
delete _plane;
delete _river;
}
void AmazonEngine::freeInactivePlayer() {
delete _inactive._altSpritesPtr;
_inactive._altSpritesPtr = nullptr;
}
void AmazonEngine::configSelect() {
// Initialize fields contained in the config file.
_hintLevel = 3;
}
void AmazonEngine::initObjects() {
_room = new AmazonRoom(this);
_scripts = new AmazonScripts(this);
_ant = new Ant(this);
_cast = new Cast(this);
_guard = new Guard(this);
_jungle = new Jungle(this);
_opening = new Opening(this);
_plane = new Plane(this);
_river = new River(this);
}
void AmazonEngine::playGame() {
// Initialize Amazon game-specific objects
initObjects();
// Setup the game
setupGame();
configSelect();
if (_loadSaveSlot == -1) {
// Do introduction
_opening->doIntroduction();
if (shouldQuit())
return;
}
do {
_restartFl = false;
_screen->clearScreen();
_screen->setPanel(0);
_screen->forceFadeOut();
_events->showCursor();
initVariables();
// If there's a pending savegame to load, load it
if (_loadSaveSlot != -1) {
loadGameState(_loadSaveSlot);
_loadSaveSlot = -1;
}
// Execute the room
_room->doRoom();
} while (_restartFl);
}
void AmazonEngine::setupGame() {
Amazon::AmazonResources &res = *((Amazon::AmazonResources *)_res);
// Load death list
_deaths.resize(_res->DEATHS.size());
for (uint idx = 0; idx < _deaths.size(); ++idx) {
_deaths[idx]._screenId = res.DEATHS[idx]._screenId;
_deaths[idx]._msg = res.DEATHS[idx]._msg;
}
// Load the deaths cells
_deaths._cells.resize(13);
for (int i = 0; i < 13; ++i)
_deaths._cells[i] = CellIdent(DEATH_CELLS[i][0], DEATH_CELLS[i][1], DEATH_CELLS[i][2]);
// Miscellaneous
_fonts.load(res._font6x6, res._font3x5, nullptr);
initVariables();
}
void AmazonEngine::initVariables() {
_chapter = 1;
// Set player room and position
if (isDemo())
_player->_roomNumber = 33;
else
_player->_roomNumber = 4;
_converseMode = 0;
_inventory->_startInvItem = 0;
_inventory->_startInvBox = 0;
Common::fill(&_objectsTable[0], &_objectsTable[100], (SpriteResource *)nullptr);
_player->_playerOff = false;
// Setup timers
static const int TIMER_DEFAULTS[] = { 3, 10, 8, 1, 1, 1, 1, 2 };
for (int i = 0; i < 32; ++i) {
TimerEntry te;
te._initTm = te._timer = (i < 8) ? TIMER_DEFAULTS[i] : 1;
te._flag = 1;
_timers.push_back(te);
}
_player->_playerX = _player->_rawPlayer.x = _res->ROOMTBL[_player->_roomNumber]._travelPos.x;
_player->_playerY = _player->_rawPlayer.y = _res->ROOMTBL[_player->_roomNumber]._travelPos.y;
_room->_selectCommand = -1;
_events->setNormalCursor(CURSOR_CROSSHAIRS);
_mouseMode = 0;
_animation->clearTimers();
}
void AmazonEngine::establish(int screenId, int esatabIndex) {
_establishMode = 0;
_establishGroup = 0;
doEstablish(screenId, esatabIndex);
}
void AmazonEngine::establishCenter(int screenId, int esatabIndex) {
_establishMode = 1;
doEstablish(screenId, esatabIndex);
}
static const char *const EST_TABLE[] = { "ETEXT0.DAT", "ETEXT1.DAT", "ETEXT2.DAT", "ETEXT3.DAT" };
void AmazonEngine::loadEstablish(int estabIndex) {
if (!_files->existFile("ETEXT.DAT")) {
int oldGroup = _establishGroup;
_establishGroup = 0;
_establish = _files->loadFile(EST_TABLE[oldGroup]);
_establishCtrlTblOfs = READ_LE_UINT16(_establish->data());
int ofs = _establishCtrlTblOfs + (estabIndex * 2);
int idx = READ_LE_UINT16(_establish->data() + ofs);
_narateFile = READ_LE_UINT16(_establish->data() + idx);
_txtPages = READ_LE_UINT16(_establish->data() + idx + 2);
if (!_txtPages)
return;
_sndSubFile = READ_LE_UINT16(_establish->data() + idx + 4);
for (int i = 0; i < _txtPages; ++i)
_countTbl[i] = READ_LE_UINT16(_establish->data() + idx + 6 + (2 * i));
} else {
_establishGroup = 0;
_narateFile = 0;
_txtPages = 0;
_sndSubFile = 0;
_establish = _files->loadFile("ETEXT.DAT");
}
}
void AmazonEngine::doEstablish(int screenId, int estabIndex) {
_establishMode = 1;
_events->clearEvents();
_screen->forceFadeOut();
_screen->clearScreen();
_screen->setPanel(3);
if (screenId != -1) {
_files->loadScreen(95, screenId);
_buffer2.copyBuffer(_screen);
}
_screen->setIconPalette();
_screen->forceFadeIn();
_fonts._charSet._lo = 1;
_fonts._charSet._hi = 10;
_fonts._charFor._lo = 29;
_fonts._charFor._hi = 32;
_screen->_maxChars = 37;
_screen->_printOrg = _screen->_printStart = Common::Point(48, 35);
loadEstablish(estabIndex);
uint16 msgOffset;
if (!isCD())
msgOffset = READ_LE_UINT16(_establish->data() + (estabIndex * 2));
else
msgOffset = READ_LE_UINT16(_establish->data() + (estabIndex * 2) + 2);
_printEnd = 155;
Common::String msg((const char *)_establish->data() + msgOffset);
if ((_txtPages == 0) || !isCD()) {
printText(_screen, msg);
} else {
speakText(_screen, msg);
}
_screen->forceFadeOut();
_screen->clearScreen();
delete _establish;
_establish = nullptr;
if (_establishMode == 0)
_room->init4Quads();
}
const char *const _tileFiles[] = {
"GRAY.BLK", "RED.BLK", "LTBROWN.BLK", "DKBROWN.BLK", "VIOLET.BLK", "LITEBLUE.BLK",
"DARKBLUE.BLK", "CYAN.BLK", "GREEN.BLK", "OLIVE.BLK", "GRAY.BLK", "RED.BLK",
"LTBROWN.BLK", "DKBROWN.BLK", "VIOLET.BLK", "OLIVE.BLK"
};
void AmazonEngine::tileScreen() {
if (!_screen->_vesaMode)
return;
if (!_clearSummaryFlag && (_oldTitleChapter == _chapter))
return;
_oldTitleChapter = _chapter;
int idx = _chapter - 1;
if (!_files->existFile(_tileFiles[idx]))
return;
Resource *res = _files->loadFile(_tileFiles[idx]);
int x = res->_stream->readSint16LE();
int y = res->_stream->readSint16LE();
int size = ((x + 2) * y) + 10;
for (int i = 0; i < size; ++i)
_tileData[i] = res->_stream->readByte();
// CHECKME: Depending on the Vesa mode during initialization, 400 or 480
Common::Point tilePos;
for (tilePos.y = 0; tilePos.y < 480; tilePos.y += y) {
for (tilePos.x = 0; tilePos.x < 640; tilePos.x += x)
warning("TODO: DRAWOBJECT");
}
delete res;
}
void AmazonEngine::updateSummary(int chap) {
if (!_screen->_vesaMode)
return;
int chapter = chap;
if (chapter > 16)
chapter = 16;
if (!_clearSummaryFlag && (chapter == _updateChapter))
return;
_clearSummaryFlag = false;
int celSubFile = 0;
_updateChapter = chapter;
Common::Array<CellIdent> summaryCells;
loadCells(summaryCells);
for (int i = celSubFile; i < 16; ++i) {
if (i > 7)
warning("TODO: DRAWOBJECT (i > 7)");
else
warning("TODO: DRAWOBJECT (i <= 7)");
}
delete _objectsTable[93];
_objectsTable[93] = nullptr;
for (int i = 1; i <= _updateChapter; ++i) {
celSubFile = i;
loadCells(summaryCells);
if (i > 8)
warning("TODO: DRAWOBJECT (i > 8)");
else
warning("TODO: DRAWOBJECT (i <= 8)");
delete _objectsTable[93];
_objectsTable[93] = nullptr;
}
}
void AmazonEngine::calcIQ() {
int tmpIQ = 170;
for (int i = 0; i < 256; i++) {
if (_help1[i] == 1)
tmpIQ -= 3;
}
for (int i = 0; i < 256; i++) {
if (_help2[i] == 1)
tmpIQ -= 5;
}
for (int i = 0; i < 256; i++) {
if (_help3[i] == 1)
tmpIQ -= 10;
}
if (tmpIQ < 0)
tmpIQ = 0;
_iqValue = tmpIQ;
if (_iqValue <= 100)
_badEnd = 1;
if (_iqValue <= 0)
_noHints = 1;
}
void AmazonEngine::helpTitle() {
AmazonResources &res = *(AmazonResources *)_res;
int width = _fonts._font2->stringWidth(_bubbleBox->_bubbleTitle);
int posX = 160 - (width / 2);
_fonts._font2->_fontColors[0] = 0;
_fonts._font2->_fontColors[1] = 33;
_fonts._font2->_fontColors[2] = 34;
_fonts._font2->_fontColors[3] = 35;
_fonts._font2->drawString(_screen, _bubbleBox->_bubbleTitle, Common::Point(posX, 24));
width = _fonts._font2->stringWidth(res.HELPLVLTXT[_helpLevel]);
posX = 160 - (width / 2);
_fonts._font2->_fontColors[0] = 0;
_fonts._font2->_fontColors[1] = 10;
_fonts._font2->_fontColors[2] = 11;
_fonts._font2->_fontColors[3] = 12;
_fonts._font2->drawString(_screen, res.HELPLVLTXT[_helpLevel], Common::Point(posX, 36));
Common::String iqText = "IQ: ";
calcIQ();
Common::String scoreIQ = Common::String::format("%d", _iqValue);
while (scoreIQ.size() < 4)
scoreIQ = " " + scoreIQ;
iqText += scoreIQ;
int index = _iqValue;
if (index == 170)
index = 169;
index /= 20;
iqText += " ";
iqText += res.IQLABELS[index];
width = _fonts._font2->stringWidth(iqText);
posX = 160 - (width / 2);
_fonts._font2->_fontColors[0] = 0;
_fonts._font2->_fontColors[1] = 10;
_fonts._font2->_fontColors[2] = 11;
_fonts._font2->_fontColors[3] = 12;
_fonts._font2->drawString(_screen, iqText, Common::Point(posX, 44));
}
void AmazonEngine::drawHelpText(const Common::String &msg) {
_screen->_maxChars = 39;
_screen->_printOrg = Common::Point(26, 58);
_screen->_printStart = Common::Point(26, 58);
Common::String lines = msg;
Common::String line;
int width = 0;
bool lastLine = false;
do {
lastLine = _fonts._font2->getLine(lines, _screen->_maxChars * 6, line, width);
// Set font colors
_fonts._font2->_fontColors[0] = 0;
_fonts._font2->_fontColors[1] = 27;
_fonts._font2->_fontColors[2] = 28;
_fonts._font2->_fontColors[3] = 29;
_fonts._font2->drawString(_screen, line, _screen->_printOrg);
_screen->_printOrg = Common::Point(_screen->_printStart.x, _screen->_printOrg.y + 8);
} while (!lastLine);
_events->showCursor();
}
void AmazonEngine::drawHelp(const Common::String &str) {
_events->hideCursor();
if (_useItem == 0) {
_buffer2.copyBuffer(_screen);
if (_screen->_vesaMode) {
_screen->setPanel(2);
_screen->saveScreen();
}
_screen->savePalette();
_screen->fadeOut();
_screen->clearBuffer();
if (_moreHelp == 1) {
// Set cells
Common::Array<CellIdent> cells;
cells.push_back(CellIdent(95, 95, 3));
loadCells(cells);
}
}
_files->loadScreen(95, 2);
if (_moreHelp == 1) {
BaseSurface *oldDest = _destIn;
_destIn = _screen;
int oldClip = _screen->_clipHeight;
_screen->_clipHeight = 200;
_screen->plotImage(_objectsTable[95], 0, Common::Point(76, 168));
_destIn = oldDest;
_screen->_clipHeight = oldClip;
}
if ((_useItem == 0) && (_screen->_vesaMode == 0))
_screen->fadeIn();
helpTitle();
drawHelpText(str);
}
void AmazonEngine::startChapter(int chapter) {
_chapter = chapter;
assert(_chapter <= 14);
if (chapter != 1) {
_room->clearRoom();
freeChar();
_midi->newMusic(32, 0);
playVideo(0, Common::Point());
if (shouldQuit())
return;
_events->debounceLeft();
_events->zeroKeysActions();
playVideo(_chapter, Common::Point(4, 113));
if (shouldQuit())
return;
_timers[20]._timer = 500;
_timers[20]._initTm = 500;
_timers[20]._flag++;
_sound->freeSounds();
if (isCD()) {
_sound->loadSoundTable(0, 115, 0);
_sound->loadSoundTable(1, 115, 1);
_sound->playSound(0);
_sound->playSound(1);
_sound->freeSounds();
}
// Wait loop
while (!shouldQuit() && !_events->isKeyActionMousePressed() && _timers[20]._flag) {
_events->pollEventsAndWait();
}
}
_screen->forceFadeOut();
_events->debounceLeft();
_events->zeroKeysActions();
_screen->clearScreen();
_screen->setPanel(3);
// Set up cells for the chapter display
Common::Array<CellIdent> chapterCells;
chapterCells.push_back(CellIdent(0, 96, 17));
const int *chapCell = &CHAPTER_CELLS[_chapter - 1][0];
chapterCells.push_back(CellIdent(chapCell[0], chapCell[1], chapCell[2]));
loadCells(chapterCells);
// Show chapter screen
_files->loadScreen(96, 15);
_buffer2.blitFrom(*_screen);
const int *chapImg = &CHAPTER_TABLE[_chapter - 1][0];
_screen->plotImage(_objectsTable[0], _chapter - 1,
Common::Point(chapImg[1], chapImg[2]));
_screen->plotImage(_objectsTable[_chapter], 0,
Common::Point(chapImg[3], chapImg[4]));
if (chapter == 14)
_screen->plotImage(_objectsTable[_chapter], 1, Common::Point(169, 76));
_midi->newMusic(chapImg[4], 1);
_midi->newMusic(33, 0);
_screen->forceFadeIn();
_timers[20]._timer = 950;
_timers[20]._initTm = 950;
_timers[20]._flag++;
// Wait loop
while (!shouldQuit() && !_events->isKeyActionMousePressed() && _timers[20]._flag) {
_events->pollEventsAndWait();
}
if (shouldQuit())
return;
_screen->forceFadeOut();
_events->debounceLeft();
_events->zeroKeysActions();
_screen->clearBuffer();
_files->loadScreen(96, 16);
_buffer2.blitFrom(*_screen);
_screen->plotImage(_objectsTable[0], chapImg[0], Common::Point(90, 7));
_midi->newMusic(7, 1);
_midi->newMusic(34, 0);
_screen->forceFadeIn();
_buffer2.blitFrom(*_screen);
_fonts._charSet._lo = 1;
_fonts._charSet._hi = 10;
_fonts._charFor._lo = 55;
_fonts._charFor._hi = 0xFF;
_screen->_maxChars = 43;
_screen->_printOrg = Common::Point(31, 77);
_screen->_printStart = Common::Point(31, 77);
_establishGroup = 1;
loadEstablish(0x40 + _chapter);
const byte *entryOffset = _establish->data() + ((0x40 + _chapter) * 2);
if (isCD())
entryOffset += 2;
uint16 msgOffset = READ_LE_UINT16(entryOffset);
_printEnd = 170;
Common::String msg((const char *)_establish->data() + msgOffset);
if ((_txtPages == 0) || !isCD()) {
printText(_screen, msg);
} else {
speakText(_screen, msg);
}
if (shouldQuit())
return;
_screen->forceFadeOut();
_screen->clearBuffer();
freeCells();
_midi->newMusic(_chapter * 2, 1);
if (chapter != 1 && chapter != 14) {
_room->init4Quads();
}
if (isCD()) {
if (chapter == 14) {
_conversation = 31;
_char->loadChar(_conversation);
_events->setCursor(CURSOR_ARROW);
_images.clear();
_oldRects.clear();
_scripts->_sequence = 0;
_scripts->searchForSequence();
if (_screen->_vesaMode) {
_converseMode = 1;
}
} else if (chapter != 1) {
_player->_roomNumber = CHAPTER_JUMP[_chapter - 1];
_room->_function = FN_CLEAR1;
_converseMode = 0;
_scripts->cmdRetPos();
}
}
delete _establish;
_establish = nullptr;
}
void AmazonEngine::dead(int deathId) {
_events->hideCursor();
_screen->forceFadeOut();
_scripts->cmdFreeSound();
_events->debounceLeft();
_events->zeroKeysActions();
_sound->_soundTable.push_back(SoundEntry(_files->loadFile(98, 44), 1));
_screen->clearScreen();
_screen->setPanel(3);
if ((deathId == 10) && !isDemo()) {
quitGame();
_events->pollEvents();
return;
} else {
if (!isDemo())
_midi->newMusic(62, 0);
_files->_setPaletteFlag = false;
_files->loadScreen(94, 0);
_files->_setPaletteFlag = true;
_buffer2.blitFrom(*_screen);
if (!isDemo() || deathId != 10) {
for (int i = 0; i < 3; ++i) {
_sound->playSound(0);
_screen->forceFadeIn();
_sound->playSound(0);
_screen->forceFadeOut();
_events->pollEvents();
if (shouldQuit())
return;
}
}
if (!isDemo()) {
freeCells();
// Load the cell list for the death screen
DeathEntry &de = _deaths[deathId];
Common::Array<CellIdent> cells;
cells.push_back(_deaths._cells[de._screenId]);
loadCells(cells);
_screen->setDisplayScan();
_files->_setPaletteFlag = false;
_files->loadScreen(&_buffer2, 94, 1);
_screen->setIconPalette();
_buffer2.plotImage(_objectsTable[0], 0, Common::Point(105, 25));
_buffer2.copyTo(_screen);
_screen->forceFadeIn();
_fonts._charSet._hi = 10;
_fonts._charSet._lo = 1;
_fonts._charFor._lo = 55;
_fonts._charFor._hi = 255;
_screen->_maxChars = 46;
_screen->_printOrg = Common::Point(20, 155);
_screen->_printStart = Common::Point(20, 155);
const Common::String &msg = de._msg;
_printEnd = 180;
printText(_screen, msg);
_screen->forceFadeOut();
_midi->newMusic(0, 1);
_events->showCursor();
_room->clearRoom();
freeChar();
_currentManOld = 1;
_player->removeSprite1();
} else {
_files->loadScreen(_screen, 94, _deaths[deathId]._screenId);
_screen->forceFadeIn();
_fonts._charSet._hi = 10;
_fonts._charSet._lo = 1;
_fonts._charFor._lo = 55;
_fonts._charFor._hi = 255;
_screen->_maxChars = 49;
_screen->_printOrg = Common::Point(15, 165);
_screen->_printStart = Common::Point(15, 165);
const Common::String &msg = _deaths[deathId]._msg;
_printEnd = 200;
printText(_screen, msg);
_screen->fadeOut();
_events->showCursor();
_room->clearRoom();
freeChar();
_currentManOld = 1;
_player->removeSprite1();
}
// The original was jumping to the restart label in main
_restartFl = true;
_events->pollEvents();
}
}
void AmazonEngine::synchronize(Common::Serializer &s) {
AccessEngine::synchronize(s);
s.syncAsSint16LE(_chapter);
s.syncAsSint16LE(_rawInactiveX);
s.syncAsSint16LE(_rawInactiveY);
s.syncAsSint16LE(_inactiveYOff);
for (int i = 0; i < 366; ++i) {
s.syncAsByte(_help1[i]);
s.syncAsByte(_help2[i]);
s.syncAsByte(_help3[i]);
}
_river->synchronize(s);
_ant->synchronize(s);
}
} // End of namespace Amazon
} // End of namespace Access

View File

@@ -0,0 +1,138 @@
/* 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/>.
*
*/
#ifndef ACCESS_AMAZON_GAME_H
#define ACCESS_AMAZON_GAME_H
#include "access/access.h"
#include "access/amazon/amazon_logic.h"
namespace Access {
namespace Amazon {
class AmazonEngine;
class AmazonEngine : public AccessEngine {
private:
byte _tileData[1455];
Common::Array<CellIdent> _chapterCells;
/**
* Setup variables for the game
*/
void setupGame();
/**
* Initialize variables found in the config file
*/
void configSelect();
void initVariables();
void initObjects();
void calcIQ();
void helpTitle();
void drawHelpText(const Common::String &msg);
void loadEstablish(int estabIndex);
void doEstablish(int screenId, int estabIndex);
protected:
/**
* Play the game
*/
void playGame() override;
/**
* Synchronize savegame data
*/
void synchronize(Common::Serializer &s) override;
public:
InactivePlayer _inactive;
bool _charSegSwitch;
byte _help1[366];
byte _help2[366];
byte _help3[366];
byte *_helpTbl[3];
// Fields that are mapped to flags
int &_guardLocation;
int &_guardFind;
int &_helpLevel;
int &_jasMayaFlag;
int &_moreHelp;
int &_flashbackFlag;
int &_riverFlag;
int &_aniOutFlag;
int &_badEnd;
int &_noHints;
int &_aniFlag;
int &_allenFlag;
int &_noSound;
// Saved fields
int _chapter;
int _rawInactiveX;
int _rawInactiveY;
int _inactiveYOff;
// Other game specific fields
Ant *_ant;
Cast *_cast;
Guard *_guard;
Jungle *_jungle;
Opening *_opening;
Plane *_plane;
River *_river;
int _hintLevel;
int _updateChapter;
int _oldTitleChapter;
int _iqValue;
public:
AmazonEngine(OSystem *syst, const AccessGameDescription *gameDesc);
~AmazonEngine() override;
void dead(int deathId) override;
/**
* Free the inactive player data
*/
void freeInactivePlayer();
void drawHelp(const Common::String &str);
void establish(int esatabIndex, int sub) override;
void tileScreen();
void updateSummary(int chap);
void establishCenter(int screenId, int esatabIndex);
/**
* Show the start of a chapter
*/
void startChapter(int chapter);
};
} // End of namespace Amazon
} // End of namespace Access
#endif /* ACCESS_ACCESS_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,252 @@
/* 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/>.
*
*/
#ifndef ACCESS_AMAZON_LOGIC_H
#define ACCESS_AMAZON_LOGIC_H
#include "common/scummsys.h"
#include "access/scripts.h"
#include "access/asurface.h"
#include "access/amazon/amazon_resources.h"
namespace Access {
namespace Amazon {
class AmazonEngine;
#define PAN_SIZE 32
class AmazonManager {
protected:
AmazonEngine *_vm;
public:
AmazonManager(AmazonEngine *vm) : _vm(vm) {}
};
class PannedScene : public AmazonManager {
struct PanEntry {
SpriteResource *_pObject;
int _pImgNum;
int _pObjX;
int _pObjY;
int _pObjZ;
int _pObjXl;
int _pObjYl;
};
protected:
int _xCount;
int _xTrack;
int _yTrack;
int _zTrack;
int _xCam;
int _yCam;
int _zCam;
int _pNumObj;
PanEntry _pan[PAN_SIZE];
public:
PannedScene(AmazonEngine *vm);
void pan();
};
class CampScene : public PannedScene {
protected:
bool _skipStart;
public:
CampScene(AmazonEngine *vm);
void mWhileDoOpen();
};
class Opening : public CampScene {
private:
int _pCount;
void doTitle();
void doCredit();
void doCreditDemo();
void scrollTitle();
void doTent();
public:
Opening(AmazonEngine *vm);
void doIntroduction();
};
class Plane : public PannedScene {
public:
int _pCount;
Common::Point _position;
int _planeCount;
int _propCount;
void doFlyCell();
void doFallCell();
void scrollFly();
void scrollFall();
void mWhileFly();
void mWhileFall();
public:
Plane(AmazonEngine *vm);
};
#define JUNGLE_SIZE 3
class Jungle : public CampScene {
private:
void initJWalk2();
void jungleMove();
void scrollJWalk();
int _jCnt[JUNGLE_SIZE];
int _jungleX[JUNGLE_SIZE];
public:
Jungle(AmazonEngine *vm);
void mWhileJWalk();
void mWhileJWalk2();
};
class Guard : public PannedScene {
private:
int _guardCel;
Common::Point _position;
int _gCode1;
int _gCode2;
Common::Point _topLeft;
Common::Point _bottomRight;
int _xMid, _yMid;
void chkVLine();
void chkHLine();
void setVerticalCode();
void setHorizontalCode();
void guardSee();
void setGuardFrame();
public:
Guard(AmazonEngine *vm);
void doGuard();
void setPosition(const Common::Point &pt);
};
class Cast : public PannedScene {
public:
Cast(AmazonEngine *vm);
void doCast(int param1);
};
class River : public PannedScene {
private:
bool _chickenOutFl;
const byte *_mapPtr;
int _canoeVXPos;
int _canoeMoveCount;
int _canoeFrame;
RiverStruct *_topList;
RiverStruct *_botList;
int _canoeDir;
bool _saveRiver;
bool _deathFlag;
int _deathCount;
int _deathType;
int _maxHits;
// Saved fields
int _canoeLane;
int _canoeYPos;
int _hitCount;
int _riverIndex;
int _hitSafe;
int _rScrollRow;
int _rScrollCol;
int _rScrollX;
int _rScrollY;
int _mapOffset;
int _screenVertX;
int _oldScrollCol;
void initRiver();
void resetPositions();
void checkRiverPan();
bool riverJumpTest();
void riverSound();
void moveCanoe();
void moveCanoe2();
void updateObstacles();
void riverSetPhysX();
bool checkRiverCollide();
void plotRiver();
void scrollRiver();
void scrollRiver1();
void setRiverPan();
public:
River(AmazonEngine *vm);
void doRiver();
void mWhileDownRiver();
void synchronize(Common::Serializer &s);
};
enum AntDirection { ANT_RIGHT = 0, ANT_LEFT = 1 };
class Ant : public AmazonManager {
private:
AntDirection _antDirection;
AntDirection _pitDirection;
int _antCel;
int _torchCel;
int _pitCel;
int _stabCel;
Common::Point _antPos;
bool _antDieFl;
bool _antEatFl;
bool _stabFl;
Common::Point _pitPos;
void plotTorchSpear(int indx, const int *&buf);
void plotPit(int indx, const int *&buf);
int antHandleRight(int indx, const int *&buf);
int antHandleLeft(int indx, const int *&buf);
int antHandleStab(int indx, const int *&buf);
public:
Ant(AmazonEngine *vm);
void doAnt();
void synchronize(Common::Serializer &s);
};
class InactivePlayer : public ImageEntry {
public:
SpriteResource *_altSpritesPtr;
InactivePlayer() { _altSpritesPtr = nullptr; }
};
} // End of namespace Amazon
} // End of namespace Access
#endif /* ACCESS_AMAZON_LOGIC_H */

View File

@@ -0,0 +1,101 @@
/* 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/scummsys.h"
#include "access/access.h"
#include "access/room.h"
#include "access/amazon/amazon_game.h"
#include "access/amazon/amazon_player.h"
#include "access/amazon/amazon_resources.h"
namespace Access {
namespace Amazon {
AmazonPlayer::AmazonPlayer(AccessEngine *vm) : Player(vm) {
_game = (AmazonEngine *)vm;
}
void AmazonPlayer::load() {
Player::load();
// Special scene setup for the top-down view when on the Slaver ship
if (_vm->_room->_roomFlag == 3) {
_playerOffset.x = _vm->_screen->_scaleTable1[8];
_playerOffset.y = _vm->_screen->_scaleTable1[11];
_leftDelta = 0;
_rightDelta = 8;
_upDelta = 2;
_downDelta = -2;
_scrollConst = 2;
for (uint8 i = 0; i < _vm->_playerDataCount; ++i) {
_walkOffRight[i] = OVEROFFR[i];
_walkOffLeft[i] = OVEROFFL[i];
_walkOffUp[i] = OVEROFFU[i];
_walkOffDown[i] = OVEROFFD[i];
_walkOffUR[i].x = OVEROFFURX[i];
_walkOffUR[i].y = OVEROFFURY[i];
_walkOffDR[i].x = OVEROFFDRX[i];
_walkOffDR[i].y = OVEROFFDRY[i];
_walkOffUL[i].x = OVEROFFULX[i];
_walkOffUL[i].y = OVEROFFULY[i];
_walkOffDL[i].x = OVEROFFDLX[i];
_walkOffDL[i].y = OVEROFFDLY[i];
}
_vm->_timers[8]._initTm = 7;
_vm->_timers[8]._timer = 7;
++_vm->_timers[8]._flag;
_sideWalkMin = 0;
_sideWalkMax = 5;
_upWalkMin = 12;
_upWalkMax = 17;
_downWalkMin = 6;
_downWalkMax = 11;
_diagUpWalkMin = 0;
_diagUpWalkMax = 5;
_diagDownWalkMin = 0;
_diagDownWalkMax = 5;
_game->_guard->setPosition(Common::Point(56, 190));
} else {
for (uint8 i = 0; i < _vm->_playerDataCount; ++i) {
_walkOffRight[i] = SIDEOFFR[i];
_walkOffLeft[i] = SIDEOFFL[i];
_walkOffUp[i] = SIDEOFFU[i];
_walkOffDown[i] = SIDEOFFD[i];
_walkOffUR[i].x = DIAGOFFURX[i];
_walkOffUR[i].y = DIAGOFFURY[i];
_walkOffDR[i].x = DIAGOFFDRX[i];
_walkOffDR[i].y = DIAGOFFDRY[i];
_walkOffUL[i].x = DIAGOFFULX[i];
_walkOffUL[i].y = DIAGOFFULY[i];
_walkOffDL[i].x = DIAGOFFDLX[i];
_walkOffDL[i].y = DIAGOFFDLY[i];
}
}
}
} // End of namespace Amazon
} // End of namespace Access

View File

@@ -0,0 +1,47 @@
/* 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/>.
*
*/
#ifndef ACCESS_AMAZON_PLAYER_H
#define ACCESS_AMAZON_PLAYER_H
#include "common/scummsys.h"
#include "access/player.h"
namespace Access {
namespace Amazon {
class AmazonEngine;
class AmazonPlayer : public Player {
private:
AmazonEngine *_game;
public:
AmazonPlayer(AccessEngine *vm);
void load() override;
};
} // End of namespace Amazon
} // End of namespace Access
#endif /* ACCESS_AMAZON_PLAYER_H */

View File

@@ -0,0 +1,548 @@
/* 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 "access/amazon/amazon_resources.h"
#include "access/access.h"
namespace Access {
namespace Amazon {
static const int BTN_RANGES[6][2] = {
{ 0, 76 }, { 77, 154 }, { 155, 232 }, { 233, 276 }, { 0, 0 }, { 277, 319 }
};
static const int RMOUSE[10][2] = {
{ 0, 35 }, { 0, 0 }, { 36, 70 }, { 71, 106 }, { 107, 141 },
{ 142, 177 }, { 178, 212 }, { 213, 248 }, { 249, 283 }, { 284, 318 }
};
AmazonResources::~AmazonResources() {
delete _font3x5;
delete _font6x6;
}
void AmazonResources::load(Common::SeekableReadStream &s) {
Resources::load(s);
uint count;
// Load the version specific data
NO_HELP_MESSAGE = s.readString();
NO_HINTS_MESSAGE = s.readString();
RIVER_HIT1 = s.readString();
RIVER_HIT2 = s.readString();
BAR_MESSAGE = s.readString();
for (int idx = 0; idx < 3; ++idx)
HELPLVLTXT[idx] = s.readString();
for (int idx = 0; idx < 9; ++idx)
IQLABELS[idx] = s.readString();
CANT_GET_THERE = s.readString();
// Get the offset of the general shared data for the game
uint entryOffset = findEntry(_vm->getGameID(), 2, 0, (Common::Language)0);
s.seek(entryOffset);
// Read in the cursor list
count = s.readUint16LE();
CURSORS.resize(count);
for (uint idx = 0; idx < count; ++idx) {
uint count2 = s.readUint16LE();
CURSORS[idx].resize(count2);
s.read(&CURSORS[idx][0], count2);
}
// Load font data
count = s.readUint16LE();
Common::Array<int> index;
Common::Array<byte> data;
index.resize(count);
for (uint idx = 0; idx < count; ++idx)
index[idx] = s.readSint16LE();
count = s.readUint16LE();
data.resize(count);
for (uint idx = 0; idx < count; ++idx)
data[idx] = s.readByte();
_font3x5 = new AmazonFont(&index[0], &data[0]);
count = s.readUint16LE();
index.resize(count);
for (uint idx = 0; idx < count; ++idx)
index[idx] = s.readSint16LE();
count = s.readUint16LE();
data.resize(count);
for (uint idx = 0; idx < count; ++idx)
data[idx] = s.readByte();
_font6x6 = new AmazonFont(&index[0], &data[0]);
}
const byte *AmazonResources::getCursor(int num) const {
return CURSORS[num].data();
}
int AmazonResources::getRMouse(int i, int j) const {
return RMOUSE[i][j];
}
int AmazonResources::inButtonXRange(int x) const {
for (int i = 0; i < ARRAYSIZE(BTN_RANGES); i++) {
if ((x >= BTN_RANGES[i][0]) && (x < BTN_RANGES[i][1]))
return i;
}
return -1;
}
/*------------------------------------------------------------------------*/
const int SIDEOFFR[] = { 5, 5, 5, 5, 5, 5, 5, 5, 0 };
const int SIDEOFFL[] = { 5, 5, 5, 5, 5, 5, 5, 5, 0 };
const int SIDEOFFU[] = { 2, 2, 2, 2, 2, 2, 2, 2, 0 };
const int SIDEOFFD[] = { 2, 2, 2, 2, 2, 2, 2, 2, 0 };
const int DIAGOFFURX[] = { 4, 5, 2, 2, 3, 4, 2, 2, 0 };
const int DIAGOFFURY[] = { 2, 3, 2, 2, 2, 3, 1, 1, 0 };
const int DIAGOFFDRX[] = { 4, 5, 4, 3, 5, 4, 5, 1, 0 };
const int DIAGOFFDRY[] = { 3, 2, 1, 2, 2, 1, 2, 1, 0 };
const int DIAGOFFULX[] = { 4, 5, 4, 3, 3, 2, 2, 2, 0 };
const int DIAGOFFULY[] = { 3, 3, 1, 2, 2, 1, 1, 1, 0 };
const int DIAGOFFDLX[] = { 4, 5, 3, 3, 5, 4, 6, 1, 0 };
const int DIAGOFFDLY[] = { 2, 2, 1, 2, 3, 1, 2, 1, 0 };
const int OVEROFFR[] = { 2, 2, 1, 2, 2, 1, 0, 0, 0 };
const int OVEROFFL[] = { 2, 2, 1, 2, 2, 1, 0, 0, 0 };
const int OVEROFFU[] = { 1, 1, 1, 1, 1, 1, 0, 0, 0 };
const int OVEROFFD[] = { 1, 1, 1, 1, 1, 1, 0, 0, 0 };
const int OVEROFFURX[] = { 3, 1, 1, 2, 2, 1, 0, 0, 0 };
const int OVEROFFURY[] = { 1, 0, 0, 1, 1, 0, 0, 0, 0 };
const int OVEROFFDRX[] = { 1, 2, 1, 1, 2, 1, 0, 0, 0 };
const int OVEROFFDRY[] = { 0, 1, 0, 0, 1, 1, 0, 0, 0 };
const int OVEROFFULX[] = { 2, 1, 1, 1, 2, 1, 0, 0, 0 };
const int OVEROFFULY[] = { 1, 0, 0, 2, 1, 0, 0, 0, 0 };
const int OVEROFFDLX[] = { 1, 2, 1, 1, 2, 1, 0, 0, 0 };
const int OVEROFFDLY[] = { 0, 1, 0, 0, 1, 1, 0, 0, 0 };
const int DEATH_CELLS[13][3] = {
{ 0, 94, 2 },
{ 0, 94, 3 },
{ 0, 94, 4 },
{ 0, 94, 5 },
{ 0, 94, 6 },
{ 0, 94, 7 },
{ 0, 94, 8 },
{ 0, 94, 9 },
{ 0, 94, 10 },
{ 0, 94, 11 },
{ 0, 94, 12 },
{ 0, 94, 13 },
{ 0, 94, 14 }
};
const int CHAPTER_CELLS[17][3] = {
{ 1, 96, 18 },
{ 2, 96, 19 },
{ 3, 96, 20 },
{ 4, 96, 21 },
{ 5, 96, 22 },
{ 6, 96, 23 },
{ 7, 96, 24 },
{ 8, 96, 25 },
{ 9, 96, 26 },
{ 10, 96, 27 },
{ 11, 96, 28 },
{ 12, 96, 29 },
{ 13, 96, 30 },
{ 14, 96, 31 }
};
const int CHAPTER_TABLE[14][5] = {
{ 18, 136, 27, 76, 49 },
{ 16, 134, 27, 53, 74 },
{ 16, 136, 27, 52, 56 },
{ 16, 135, 26, 46, 75 },
{ 16, 135, 27, 54, 66 },
{ 16, 137, 27, 67, 79 },
{ 14, 136, 27, 82, 52 },
{ 15, 136, 26, 65, 73 },
{ 15, 137, 26, 48, 75 },
{ 17, 135, 27, 52, 66 },
{ 15, 135, 27, 62, 65 },
{ 16, 135, 28, 45, 66 },
{ 16, 135, 28, 36, 67 },
{ 15, 135, 27, 34, 63 }
};
const int CHAPTER_JUMP[14] = {
0, 12, 10, 15, 19, 25, 31, 36, 45, 46, 29, 55, 61, 0
};
const int ANTWALK[24] = {
0, 3, 0,
1, 5, 0,
2, 4, 0,
3, 2, 0,
4, 4, 0,
5, 3, 0,
6, 4, 0,
-1, -1, -1
};
const int ANTEAT[33] = {
7, 0, -1,
8, 0, -5,
9, 0, -11,
10, 0, 7,
11, 0, -3,
12, 0, 3,
13, 0, -1,
9, 0, -6,
8, 0, 11,
7, 0, 6,
-1, -1, -1
};
const int ANTDIE[21] = {
14, 4, 8,
15, 7, 6,
16, 6, 7,
17, 8, 2,
18, 0, 0,
19, 0, 0,
-1, -1, -1
};
const int PITWALK[27] = {
18, 0, -1,
19, -2, 1,
20, -2, 1,
21, -2, 1,
22, -2, 0,
23, -3, 0,
24, -3, -1,
25, -2, -1,
-1, -1, -1
};
const int PITSTAB[21] = {
14, -2, 0,
15, -4, 0,
16, 3, -13,
16, 0, 0,
15, -3, 13,
14, 4, 0,
-1, -1, -1
};
const int TORCH[12] = {
26, -11, -7,
27, -12, -2,
28, -15, -4,
-1, -1, -1
};
const int SPEAR[3] = {30, -13, 1};
const int OPENING_OBJS[10][4] = {
{8, -80, 120, 30},
{13, 229, 0, 50},
{12, 78, 0, 50},
{11, 10, 0, 50},
{10, 178, 97, 50},
{9, 92, 192, 50},
{14, 38, 0, 100},
{15, 132, 76, 100},
{16, 142, 0, 100},
{4, -280, 40, 120},
};
const byte MAP0[26] = {
0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 4, 0,
0, 0, 1, 0, 2, 0, 0, 1, 1, 3, 0, 0,
0, 0xFF
};
const byte MAP1[27] = {
0, 0, 1, 0, 3, 0, 0, 1, 1, 2, 0, 0,
0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 4, 0,
0, 0, 0xFF
};
const byte MAP2[32] = {
0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 1, 0,
3, 0, 0, 1, 0, 4, 0, 0, 1, 1, 2, 0,
0, 1, 0, 1, 0, 0, 0, 0xFF
};
const byte *const MAPTBL[3] = {MAP0, MAP1, MAP2};
const int DOWNRIVEROBJ[14][4] = {
{ 3, 77, 0, 40 },
{ 2, 30, 0, 30 },
{ 2, 290, 0, 50 },
{ 1, 210, 0, 70 },
{ 2, 350, 0, 30 },
{ 1, 370, 0, 20 },
{ 2, 480, 0, 60 },
{ 3, 395, 0, 10 },
{ 1, 550, 0, 30 },
{ 2, 620, 0, 50 },
{ 1, 690, 0, 10 },
{ 2, 715, 0, 40 },
{ 1, 770, 0, 30 },
{ 3, 700, 0, 20 }
};
RiverStruct RIVER0OBJECTS[46] = {
{16, 31, 6400, 0, 4, 12},
{16, 31, 6200, 0, 2, 12},
{17, 30, 6100, 0, 3, 15},
{16, 31, 5970, 0, 7, 12},
{17, 30, 5910, 0, 5, 15},
{17, 30, 5730, 0, 3, 15},
{16, 31, 5700, 0, 7, 12},
{-1, 314, 5392, 0, 4, 0},
{17, 30, 5155, 0, 1, 15},
{16, 31, 5150, 0, 5, 12},
{16, 31, 5056, 0, 7, 12},
{17, 30, 4900, 0, 2, 15},
{17, 30, 4785, 0, 7, 15},
{16, 31, 4690, 0, 4, 12},
{16, 31, 4660, 0, 1, 12},
{17, 30, 4560, 0, 5, 15},
{16, 31, 4465, 0, 2, 12},
{-1, 314, 4112, 0, 4, 0},
{17, 30, 4005, 0, 3, 15},
{16, 31, 3865, 0, 6, 12},
{17, 30, 3605, 0, 4, 15},
{16, 31, 3360, 0, 1, 12},
{17, 30, 3105, 0, 0, 15},
{16, 31, 3080, 0, 7, 12},
{17, 30, 3014, 0, 4, 15},
{16, 31, 2992, 0, 3, 12},
{16, 31, 2976, 0, 2, 12},
{17, 30, 2880, 0, 7, 15},
{17, 30, 2860, 0, 0, 15},
{-1, 314, 2512, 0, 4, 0},
{17, 30, 2270, 0, 4, 15},
{16, 31, 2195, 0, 6, 12},
{17, 30, 1824, 0, 1, 15},
{16, 31, 1776, 0, 4, 12},
{17, 30, 1650, 0, 3, 15},
{16, 31, 1616, 0, 7, 12},
{17, 30, 1585, 0, 2, 15},
{-1, 314, 1232, 0, 4, 0},
{17, 30, 1190, 0, 2, 15},
{16, 31, 1120, 0, 4, 12},
{17, 30, 970, 0, 7, 15},
{16, 31, 910, 0, 5, 12},
{17, 30, 705, 0, 0, 15},
{16, 31, 550, 0, 4, 12},
{17, 30, 305, 0, 2, 15},
{16, 31, 260, 0, 7, 12}
};
RiverStruct RIVER1OBJECTS[50] = {
{16, 31, 6920, 0, 1, 12},
{16, 31, 6740, 0, 4, 12},
{17, 30, 6699, 0, 1, 15},
{16, 31, 6610, 0, 2, 12},
{17, 30, 6495, 0, 6, 15},
{17, 30, 6385, 0, 4, 15},
{16, 31, 6350, 0, 1, 12},
{17, 30, 6180, 0, 0, 15},
{-1, 314, 6032, 0, 4, 0},
{16, 31, 5800, 0, 3, 12},
{17, 30, 5790, 0, 6, 15},
{16, 31, 5530, 0, 4, 12},
{16, 31, 5500, 0, 7, 12},
{17, 30, 5495, 0, 1, 15},
{17, 30, 5376, 0, 0, 15},
{16, 31, 5328, 0, 7, 12},
{17, 30, 5248, 0, 2, 15},
{16, 31, 5248, 0, 6, 12},
{-1, 314, 4752, 0, 4, 0},
{17, 30, 4432, 0, 2, 15},
{16, 31, 4432, 0, 7, 12},
{16, 31, 4384, 0, 2, 12},
{17, 30, 4368, 0, 5, 15},
{16, 31, 4336, 0, 4, 12},
{17, 30, 4185, 0, 1, 15},
{16, 31, 4125, 0, 3, 12},
{17, 30, 3817, 0, 7, 15},
{16, 31, 3612, 0, 4, 12},
{16, 31, 3360, 0, 5, 12},
{16, 31, 3265, 0, 7, 12},
{17, 30, 3200, 0, 1, 15},
{17, 30, 3056, 0, 6, 15},
{-1, 314, 2832, 0, 4, 0},
{16, 31, 2740, 0, 3, 12},
{17, 30, 2694, 0, 6, 15},
{16, 31, 2455, 0, 0, 12},
{17, 30, 2285, 0, 5, 15},
{16, 31, 2260, 0, 2, 12},
{16, 31, 1904, 0, 5, 12},
{17, 30, 1808, 0, 1, 15},
{16, 31, 1744, 0, 7, 12},
{17, 30, 1696, 0, 4, 15},
{16, 31, 1568, 0, 2, 12},
{-1, 314, 1232, 0, 4, 0},
{17, 30, 970, 0, 4, 15},
{16, 31, 910, 0, 7, 12},
{17, 30, 705, 0, 0, 15},
{16, 31, 550, 0, 6, 12},
{17, 30, 305, 0, 3, 15},
{ 16, 31, 260, 0, 1, 12 }
};
RiverStruct RIVER2OBJECTS[54] = {
{16, 31, 8230, 0, 6, 12},
{16, 31, 8115, 0, 7, 12},
{17, 30, 7955, 0, 4, 15},
{16, 31, 7890, 0, 0, 12},
{16, 31, 7616, 0, 2, 12},
{17, 30, 7472, 0, 5, 15},
{16, 31, 7425, 0, 4, 12},
{17, 30, 7360, 0, 1, 15},
{16, 31, 7328, 0, 6, 12},
{-1, 314, 6992, 0, 4, 0},
{16, 31, 6720, 0, 3, 12},
{17, 30, 6700, 0, 6, 15},
{16, 31, 6518, 0, 2, 12},
{17, 30, 6225, 0, 5, 15},
{16, 31, 6200, 0, 2, 12},
{17, 30, 5990, 0, 1, 15},
{16, 31, 5960, 0, 7, 12},
{16, 31, 5700, 0, 2, 12},
{17, 30, 5650, 0, 4, 15},
{16, 31, 5568, 0, 5, 12},
{17, 30, 5488, 0, 6, 15},
{-1, 314, 5072, 0, 4, 0},
{17, 30, 4825, 0, 4, 15},
{16, 31, 4782, 0, 2, 12},
{17, 30, 4660, 0, 5, 15},
{16, 31, 4510, 0, 7, 12},
{16, 31, 4495, 0, 1, 12},
{17, 30, 4250, 0, 2, 15},
{16, 31, 4195, 0, 4, 12},
{-1, 314, 3792, 0, 4, 0},
{17, 30, 3600, 0, 3, 15},
{16, 31, 3470, 0, 5, 12},
{16, 31, 3422, 0, 2, 12},
{17, 30, 3170, 0, 6, 15},
{16, 31, 2960, 0, 4, 12},
{17, 30, 2955, 0, 7, 15},
{-1, 314, 2512, 0, 4, 0},
{17, 30, 2415, 0, 1, 15},
{16, 31, 2318, 0, 0, 12},
{17, 30, 2275, 0, 2, 15},
{16, 31, 2270, 0, 6, 12},
{17, 30, 2026, 0, 3, 15},
{16, 31, 2000, 0, 0, 12},
{16, 31, 1840, 0, 3, 12},
{17, 30, 1795, 0, 7, 15},
{16, 31, 1634, 0, 5, 12},
{17, 30, 1630, 0, 1, 15},
{-1, 314, 1232, 0, 4, 0},
{17, 30, 970, 0, 2, 15},
{16, 31, 910, 0, 5, 12},
{17, 30, 705, 0, 0, 15},
{16, 31, 550, 0, 4, 12},
{17, 30, 305, 0, 3, 15},
{16, 31, 260, 0, 6, 12}
};
RiverStruct *RIVER_OBJECTS[3][2] = {
{ RIVER0OBJECTS, RIVER0OBJECTS + 46 - 1},
{ RIVER1OBJECTS, RIVER1OBJECTS + 50 - 1 },
{ RIVER2OBJECTS, RIVER2OBJECTS + 54 - 1 }
};
const int HELP1COORDS[2][4] = {
{ 76, 129, 168, 183 }, { 187, 240, 168, 183 }
};
const int RIVER1OBJ[23][4] = {
{ 18, -77, 0, 30 },
{ 18, -325, 0, 20 },
{ 18, -450, 0, 15 },
{ 18, -1250, 0, 25 },
{ 19, -130, 0, 20 },
{ 19, -410, 0, 15 },
{ 19, -710, 0, 25 },
{ 19, -1510, 0, 20 },
{ 20, -350, 0, 30 },
{ 20, -695, 0, 25 },
{ 20, -990, 0, 20 },
{ 20, -1300, 0, 25 },
{ 20, -1600, 0, 30 },
{ 21, -370, 0, 20 },
{ 21, -650, 0, 30 },
{ 21, -1215, 0, 40 },
{ 21, -1815, 0, 35 },
{ 22, -380, 0, 25 },
{ 22, -720, 0, 35 },
{ 22, -1020, 0, 30 },
{ 22, -1170, 0, 25 },
{ 22, -1770, 0, 35 },
{ 23, -500, 63, 20 }
};
const int CAST_END_OBJ[26][4] = {
{ 0, 118, 210, 10 },
{ 1, 38, 250, 10 },
{ 2, 38, 280, 10 },
{ 3, 38, 310, 10 },
{ 4, 38, 340, 10 },
{ 5, 38, 370, 10 },
{ 6, 38, 400, 10 },
{ 7, 38, 430, 10 },
{ 8, 38, 460, 10 },
{ 9, 38, 490, 10 },
{ 10, 38, 520, 10 },
{ 11, 38, 550, 10 },
{ 12, 38, 580, 10 },
{ 13, 38, 610, 10 },
{ 14, 38, 640, 10 },
{ 15, 38, 670, 10 },
{ 16, 38, 700, 10 },
{ 17, 38, 730, 10 },
{ 18, 38, 760, 10 },
{ 19, 38, 790, 10 },
{ 20, 95, 820, 10 },
{ 21, 94, 850, 10 },
{ 22, 96, 880, 10 },
{ 23, 114, 910, 10 },
{ 24, 114, 940, 10 },
{ 25, 110, 970, 10 }
};
const int CAST_END_OBJ1[4][4] = {
{ 0, 40, 1100, 10 },
{ 2, 11, 1180, 10 },
{ 1, 154, 1180, 10 },
{ 3, 103, 1300, 10 }
};
} // End of namespace Amazon
} // End of namespace Access

View File

@@ -0,0 +1,156 @@
/* 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/>.
*
*/
#ifndef ACCESS_AMAZON_RESOURCES_H
#define ACCESS_AMAZON_RESOURCES_H
#include "common/scummsys.h"
#include "common/array.h"
#include "access/resources.h"
#include "access/font.h"
namespace Access {
namespace Amazon {
enum InventoryEnum {
INV_BAITED_POLE = 67, INV_TORCH = 76, INV_KNIFE_SPEAR = 78
};
struct RiverStruct {
int _id;
int _width;
int _riverX;
int _xp;
int _lane;
int _offsetY;
};
extern const int SIDEOFFR[];
extern const int SIDEOFFL[];
extern const int SIDEOFFU[];
extern const int SIDEOFFD[];
extern const int DIAGOFFURX[];
extern const int DIAGOFFURY[];
extern const int DIAGOFFDRX[];
extern const int DIAGOFFDRY[];
extern const int DIAGOFFULX[];
extern const int DIAGOFFULY[];
extern const int DIAGOFFDLX[];
extern const int DIAGOFFDLY[];
extern const int _travelPos[][2];
extern const int OVEROFFR[];
extern const int OVEROFFL[];
extern const int OVEROFFU[];
extern const int OVEROFFD[];
extern const int OVEROFFURX[];
extern const int OVEROFFURY[];
extern const int OVEROFFDRX[];
extern const int OVEROFFDRY[];
extern const int OVEROFFULX[];
extern const int OVEROFFULY[];
extern const int OVEROFFDLX[];
extern const int OVEROFFDLY[];
extern const int DEATH_CELLS[13][3];
extern const int CHAPTER_CELLS[17][3];
extern const int CHAPTER_TABLE[14][5];
extern const int CHAPTER_JUMP[14];
extern const int COMBO_TABLE[85][4];
extern const int ANTWALK[24];
extern const int ANTEAT[33];
extern const int ANTDIE[21];
extern const int PITWALK[27];
extern const int PITSTAB[21];
extern const int TORCH[12];
extern const int SPEAR[3];
extern const int OPENING_OBJS[10][4];
extern const byte MAP0[26];
extern const byte MAP1[27];
extern const byte MAP2[32];
extern const byte *const MAPTBL[3];
extern const int DOWNRIVEROBJ[14][4];
extern RiverStruct RIVER0OBJECTS[46];
extern RiverStruct RIVER1OBJECTS[50];
extern RiverStruct RIVER2OBJECTS[54];
extern RiverStruct *RIVER_OBJECTS[3][2];
enum { RIVER_START = 0, RIVER_END = 1 };
extern const int HELP1COORDS[2][4];
extern const int RIVER1OBJ[23][4];
extern const int CAST_END_OBJ[26][4];
extern const int CAST_END_OBJ1[4][4];
class AmazonResources: public Resources {
protected:
/**
* Load data from the access.dat file
*/
void load(Common::SeekableReadStream &s) override;
public:
AmazonFont *_font3x5, *_font6x6;
Common::String NO_HELP_MESSAGE;
Common::String NO_HINTS_MESSAGE;
Common::String RIVER_HIT1;
Common::String RIVER_HIT2;
Common::String BAR_MESSAGE;
Common::String HELPLVLTXT[3];
Common::String IQLABELS[9];
private:
Common::Array< Common::Array<byte> > CURSORS;
public:
AmazonResources(AccessEngine *vm) : Resources(vm), _font3x5(nullptr), _font6x6(nullptr) {}
~AmazonResources() override;
const byte *getCursor(int num) const override;
const char *getEgoName() const override { return "JASON"; }
int getRMouse(int i, int j) const override;
int inButtonXRange(int x) const override;
};
#define AMRES (*((Amazon::AmazonResources *)_vm->_res))
} // End of namespace Amazon
} // End of namespace Access
#endif /* ACCESS_AMAZON_RESOURCES_H */

View File

@@ -0,0 +1,227 @@
/* 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/scummsys.h"
#include "access/access.h"
#include "access/resources.h"
#include "access/amazon/amazon_game.h"
#include "access/amazon/amazon_resources.h"
#include "access/amazon/amazon_room.h"
namespace Access {
namespace Amazon {
AmazonRoom::AmazonRoom(AccessEngine *vm) : Room(vm) {
_game = (AmazonEngine *)vm;
_antOutFlag = false;
_icon = nullptr;
}
AmazonRoom::~AmazonRoom() {
}
void AmazonRoom::loadRoom(int roomNumber) {
loadRoomData(&AMRES.ROOMTBL[roomNumber]._data[0]);
}
void AmazonRoom::reloadRoom() {
loadRoom(_vm->_player->_roomNumber);
if (_roomFlag != 1) {
_vm->_currentMan = _roomFlag;
_vm->_currentManOld = _roomFlag;
_vm->_manScaleOff = 0;
switch (_vm->_currentMan) {
case 0:
_vm->_player->loadSprites("MAN.LZ");
break;
case 2:
_vm->_player->loadSprites("JMAN.LZ");
break;
case 3:
_vm->_player->loadSprites("OVERHEAD.LZ");
_vm->_manScaleOff = 1;
break;
default:
break;
}
}
reloadRoom1();
}
void AmazonRoom::reloadRoom1() {
if (_vm->_player->_roomNumber == 29 || _vm->_player->_roomNumber == 31
|| _vm->_player->_roomNumber == 42 || _vm->_player->_roomNumber == 44) {
Resource *spriteData = _vm->_files->loadFile("MAYA.LZ");
_game->_inactive._altSpritesPtr = new SpriteResource(_vm, spriteData);
delete spriteData;
_vm->_currentCharFlag = false;
}
_selectCommand = -1;
_vm->_events->setNormalCursor(CURSOR_CROSSHAIRS);
_vm->_mouseMode = 0;
_vm->_boxSelect = true;
_vm->_player->_playerOff = false;
_vm->_screen->fadeOut();
_vm->_screen->clearScreen();
roomInit();
if (_roomFlag != 1 && (_vm->_player->_roomNumber != 61 || !_antOutFlag)) {
_vm->_player->load();
_vm->_player->calcManScale();
}
if (_vm->_player->_roomNumber != 20 && _vm->_player->_roomNumber != 24
&& _vm->_player->_roomNumber != 33 && _vm->_player->_roomNumber != 45) {
roomMenu();
}
_vm->_screen->setBufferScan();
setupRoom();
setWallCodes();
buildScreen();
if (!_vm->_screen->_vesaMode) {
_vm->copyBF2Vid();
} else if (_vm->_player->_roomNumber != 20 && _vm->_player->_roomNumber != 24
&& _vm->_player->_roomNumber != 33) {
_vm->_screen->setPalette();
_vm->copyBF2Vid();
}
// Stop player moving
_vm->_player->_playerMove = false;
_vm->_player->_frame = 0;
// Clear any dirty rects from the old scene
_vm->_oldRects.clear();
_vm->_newRects.clear();
}
void AmazonRoom::setupRoom() {
Room::setupRoom();
// WORKAROUND: The original engine doesn't handle vertical scrolling rooms
Screen &screen = *_vm->_screen;
if (screen._vWindowHeight == (_playFieldHeight - 1)) {
_vm->_scrollRow = 1;
_vm->_scrollY = 0;
}
}
void AmazonRoom::roomMenu() {
const SpriteResource *icons = _vm->getIcons();
Screen &screen = *_vm->_screen;
screen.saveScreen();
screen.setDisplayScan();
_vm->_destIn = &screen; // TODO: Redundant
screen.plotImage(icons, 0, Common::Point(0, 177));
screen.plotImage(icons, 1, Common::Point(143, 177));
screen.restoreScreen();
}
void AmazonRoom::mainAreaClick() {
Common::Point &mousePos = _vm->_events->_mousePos;
Common::Point pt = _vm->_events->calcRawMouse();
Screen &screen = *_vm->_screen;
Player &player = *_vm->_player;
if (_selectCommand == -1) {
if (player._roomNumber == 42 || player._roomNumber == 44 ||
player._roomNumber == 31 || player._roomNumber == 29) {
switch (checkBoxes1(pt)) {
case 0:
// Make Jason the active player
_game->_jasMayaFlag = 0;
return;
case 1:
// Make Maya the active player
_game->_jasMayaFlag = 1;
return;
default:
break;
}
}
// WORKAROUND: In Amazon room 9, you can't leave the screen to the south due
// to not being able to click a Y position that's high enough
if (_vm->_scrollRow == 0 && pt.y > 178)
pt.y = 200;
player._moveTo = pt;
player._playerMove = true;
} else if (mousePos.x >= screen._windowXAdd &&
mousePos.x <= (screen._windowXAdd + screen._vWindowBytesWide) &&
mousePos.y >= screen._windowYAdd &&
mousePos.y <= (screen._windowYAdd + screen._vWindowLinesTall)) {
if (checkBoxes1(pt) >= 0) {
checkBoxes3();
}
}
}
void AmazonRoom::walkCursor() {
// WORKAROUND: For scene 29, which is a normal walkable scene, but yet can be
// 'exited'. This workaround ensures the scene will only be left if you click
// the Exit icon when the cursor is already a walk cursor
EventsManager &events = *_vm->_events;
if (_vm->_events->_middleButton || (_vm->_player->_roomNumber == 29 &&
events._normalMouse != CURSOR_CROSSHAIRS)) {
events.forceSetCursor(CURSOR_CROSSHAIRS);
_selectCommand = -1;
_vm->_boxSelect = true;
} else {
Room::walkCursor();
}
}
void AmazonRoom::init4Quads() {
if (!_vm->_screen->_vesaMode)
return;
// CHECKME: in the original, this call of tileScreen uses an useless parameter, "TILES.BLK"
_game->tileScreen();
_vm->_inventory->refreshInventory();
_game->updateSummary(_game->_chapter);
_vm->_screen->setPanel(0);
_vm->_screen->clearScreen();
}
void AmazonRoom::clearRoom() {
_game->freeInactivePlayer();
Room::clearRoom();
}
} // End of namespace Amazon
} // End of namespace Access

View File

@@ -0,0 +1,70 @@
/* 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/>.
*
*/
#ifndef ACCESS_AMAZON_ROOM_H
#define ACCESS_AMAZON_ROOM_H
#include "common/scummsys.h"
#include "access/room.h"
namespace Access {
class AccessEngine;
namespace Amazon {
class AmazonEngine;
class AmazonRoom : public Room {
private:
AmazonEngine *_game;
bool _antOutFlag;
const byte *_icon;
protected:
void loadRoom(int roomNumber) override;
void reloadRoom() override;
void reloadRoom1() override;
void setupRoom() override;
void mainAreaClick() override;
void clearRoom() override;
void walkCursor() override;
public:
AmazonRoom(AccessEngine *vm);
~AmazonRoom() override;
void init4Quads() override;
void roomMenu() override;
};
} // End of namespace Amazon
} // End of namespace Access
#endif /* ACCESS_AMAZON_ROOM_H */

View File

@@ -0,0 +1,535 @@
/* 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/scummsys.h"
#include "access/access.h"
#include "access/resources.h"
#include "access/amazon/amazon_game.h"
#include "access/amazon/amazon_resources.h"
#include "access/amazon/amazon_scripts.h"
namespace Access {
namespace Amazon {
AmazonScripts::AmazonScripts(AccessEngine *vm) : Scripts(vm) {
_game = (AmazonEngine *)_vm;
setOpcodes_v2();
}
void AmazonScripts::cLoop() {
searchForSequence();
_vm->_images.clear();
_vm->_buffer2.blitFrom(_vm->_buffer1);
_vm->_oldRects.clear();
_vm->_scripts->executeScript();
_vm->plotList1();
_vm->copyBlocks();
}
void AmazonScripts::mWhile1() {
_vm->_screen->setDisplayScan();
_vm->_screen->fadeOut();
_vm->_events->hideCursor();
_vm->_files->loadScreen(14, 0);
_vm->_buffer2.blitFrom(*_vm->_screen);
_vm->_buffer1.blitFrom(*_vm->_screen);
_vm->_events->showCursor();
_vm->_screen->setIconPalette();
_vm->_screen->forceFadeIn();
Resource *spriteData = _vm->_files->loadFile(14, 6);
_vm->_objectsTable[0] = new SpriteResource(_vm, spriteData);
delete spriteData;
_vm->_images.clear();
_vm->_oldRects.clear();
_sequence = 2100;
do {
cLoop();
_sequence = 2100;
} while (_vm->_flags[52] == 1);
_vm->_buffer1.copyTo(_vm->_screen);
_vm->_buffer2.copyTo(&_vm->_buffer1);
_game->establish(-1, 14);
spriteData = _vm->_files->loadFile(14, 7);
_vm->_objectsTable[1] = new SpriteResource(_vm, spriteData);
delete spriteData;
_vm->_sound->playSound(0);
_vm->_screen->setDisplayScan();
_vm->_events->hideCursor();
_vm->_files->loadScreen(14, 1);
_vm->_screen->setPalette();
_vm->_buffer2.blitFrom(*_vm->_screen);
_vm->_buffer1.blitFrom(*_vm->_screen);
_vm->_events->showCursor();
_vm->_screen->setIconPalette();
_vm->_images.clear();
_vm->_oldRects.clear();
_sequence = 2200;
_vm->_sound->loadSoundTable(0, 14, 15);
do {
cLoop();
_sequence = 2200;
} while (_vm->_flags[52] == 2);
_vm->_screen->setDisplayScan();
_vm->_events->hideCursor();
_vm->_files->loadScreen(14, 2);
_vm->_screen->setPalette();
_vm->_buffer2.blitFrom(*_vm->_screen);
_vm->_buffer1.blitFrom(*_vm->_screen);
_vm->_events->showCursor();
_vm->_screen->setIconPalette();
_vm->freeCells();
spriteData = _vm->_files->loadFile(14, 8);
_vm->_objectsTable[2] = new SpriteResource(_vm, spriteData);
delete spriteData;
_vm->_images.clear();
_vm->_oldRects.clear();
_sequence = 2300;
_vm->_sound->playSound(0);
do {
cLoop();
_sequence = 2300;
} while (_vm->_flags[52] == 3);
_vm->freeCells();
spriteData = _vm->_files->loadFile(14, 9);
_vm->_objectsTable[3] = new SpriteResource(_vm, spriteData);
delete spriteData;
_vm->_screen->setDisplayScan();
_vm->_events->hideCursor();
_vm->_files->loadScreen(14, 3);
_vm->_screen->setPalette();
_vm->_buffer2.blitFrom(*_vm->_screen);
_vm->_buffer1.blitFrom(*_vm->_screen);
_vm->_events->showCursor();
_vm->_screen->setIconPalette();
_vm->_images.clear();
_vm->_oldRects.clear();
_sequence = 2400;
do {
cLoop();
_sequence = 2400;
} while (_vm->_flags[52] == 4);
}
void AmazonScripts::mWhile2() {
_vm->_screen->setDisplayScan();
_vm->_screen->fadeOut();
_vm->_events->hideCursor();
_vm->_files->loadScreen(14, 0);
_vm->_buffer2.blitFrom(*_vm->_screen);
_vm->_buffer1.blitFrom(*_vm->_screen);
_vm->_events->showCursor();
_vm->_screen->setIconPalette();
_vm->_screen->forceFadeIn();
Resource *spriteData = _vm->_files->loadFile(14, 6);
_vm->_objectsTable[0] = new SpriteResource(_vm, spriteData);
delete spriteData;
_vm->_images.clear();
_vm->_oldRects.clear();
_sequence = 2100;
do {
cLoop();
_sequence = 2100;
} while (_vm->_flags[52] == 1);
_vm->_screen->fadeOut();
_vm->freeCells();
spriteData = _vm->_files->loadFile(14, 9);
_vm->_objectsTable[3] = new SpriteResource(_vm, spriteData);
delete spriteData;
_vm->_screen->setDisplayScan();
_vm->_events->hideCursor();
_vm->_files->loadScreen(14, 3);
_vm->_screen->setPalette();
_vm->_buffer2.blitFrom(*_vm->_screen);
_vm->_buffer1.blitFrom(*_vm->_screen);
_vm->_events->showCursor();
_vm->_screen->setIconPalette();
_vm->_images.clear();
_vm->_oldRects.clear();
_sequence = 2400;
do {
cLoop();
_sequence = 2400;
} while (_vm->_flags[52] == 4);
}
void AmazonScripts::mWhile(int param1) {
switch(param1) {
case 1:
mWhile1();
break;
case 2:
_game->_plane->mWhileFly();
break;
case 3:
_game->_plane->mWhileFall();
break;
case 4:
_game->_jungle->mWhileJWalk();
break;
case 5:
_game->_jungle->mWhileDoOpen();
break;
case 6:
_game->_river->mWhileDownRiver();
break;
case 7:
mWhile2();
break;
case 8:
_game->_jungle->mWhileJWalk2();
break;
default:
break;
}
}
void AmazonScripts::loadBackground(int param1, int param2) {
_vm->_files->_setPaletteFlag = false;
_vm->_files->loadScreen(param1, param2);
_vm->_buffer2.blitFrom(*_vm->_screen);
_vm->_buffer1.blitFrom(*_vm->_screen);
_vm->_screen->forceFadeIn();
}
void AmazonScripts::loadNSound(int param1, int param2) {
Resource *sound = _vm->_files->loadFile(param1, param2);
_vm->_sound->_soundTable.push_back(SoundEntry(sound, 1));
}
void AmazonScripts::setInactive() {
_game->_rawInactiveX = _vm->_player->_rawPlayer.x;
_game->_rawInactiveY = _vm->_player->_rawPlayer.y;
_game->_charSegSwitch = false;
mWhile(_game->_rawInactiveY);
}
void AmazonScripts::boatWalls(int param1, int param2) {
if (param1 == 1)
_vm->_room->_plotter._walls[42] = Common::Rect(96, 27, 96 + 87, 27 + 42);
else {
_vm->_room->_plotter._walls[39].bottom = _vm->_room->_plotter._walls[41].bottom = 106;
_vm->_room->_plotter._walls[40].left = 94;
}
}
void AmazonScripts::plotInactive() {
Player &player = *_vm->_player;
InactivePlayer &inactive = _game->_inactive;
if (_game->_charSegSwitch) {
_game->_currentCharFlag = true;
SWAP(inactive._altSpritesPtr, player._playerSprites);
_game->_charSegSwitch = false;
} else if (_game->_jasMayaFlag != (_game->_currentCharFlag ? 1 : 0)) {
if (player._playerOff) {
_game->_jasMayaFlag = (_game->_currentCharFlag ? 1 : 0);
} else {
_game->_currentCharFlag = (_game->_jasMayaFlag == 1);
int tmpX = _game->_rawInactiveX;
int tmpY = _game->_rawInactiveY;
_game->_rawInactiveX = player._rawPlayer.x;
_game->_rawInactiveY = player._rawPlayer.y;
player._rawPlayer.x = tmpX;
player._rawPlayer.y = tmpY;
_game->_inactiveYOff = player._playerOffset.y;
player.calcManScale();
SWAP(inactive._altSpritesPtr, player._playerSprites);
_vm->_room->setWallCodes();
}
}
_game->_flags[155] = 0;
if (_game->_rawInactiveX >= 152 && _game->_rawInactiveX <= 167 &&
_game->_rawInactiveY >= 158 && _game->_rawInactiveY <= 173) {
_game->_flags[155] = 1;
} else {
_game->_flags[160] = 0;
if (!_game->_jasMayaFlag && _game->_rawInactiveX >= 266 && _game->_rawInactiveX <= 290
&& _game->_rawInactiveY >= 70 && _game->_rawInactiveY <= 87) {
_game->_flags[160] = 1;
}
}
inactive._flags &= ~IMGFLAG_UNSCALED;
inactive._flags &= ~IMGFLAG_BACKWARDS;
inactive._position.x = _game->_rawInactiveX;
inactive._position.y = _game->_rawInactiveY - _game->_inactiveYOff;
inactive._offsetY = _game->_inactiveYOff;
inactive._spritesPtr = inactive._altSpritesPtr;
_vm->_images.addToList(_game->_inactive);
}
void AmazonScripts::executeSpecial(int commandIndex, int param1, int param2) {
switch (commandIndex) {
case 0:
warning("TODO: DEMO - RESETAN");
break;
case 1:
_vm->establish(param1, param2);
break;
case 2:
loadBackground(param1, param2);
break;
case 3:
if (_vm->isDemo())
warning("TODO: DEMO - LOADCELLSET");
else
_game->_cast->doCast(param1);
break;
case 4:
if (_vm->isDemo())
loadNSound(param1, param2);
else
setInactive();
break;
case 5:
warning("TODO: DEMO - UNLOADCELLSET");
break;
case 6:
mWhile(param1);
break;
case 7:
warning("TODO: DEMO - ADDMONEY");
break;
case 8:
warning("TODO: DEMO - CHKMONEY");
break;
case 9:
_game->_guard->doGuard();
break;
case 10:
_vm->_midi->newMusic(param1, param2);
break;
case 11:
plotInactive();
break;
case 13:
_game->_river->doRiver();
break;
case 14:
_game->_ant->doAnt();
break;
case 15:
boatWalls(param1, param2);
break;
default:
warning("Unexpected Special code %d - Skipped", commandIndex);
}
}
typedef void(AmazonScripts::*AmazonScriptMethodPtr)();
void AmazonScripts::executeCommand(int commandIndex) {
static const AmazonScriptMethodPtr AMAZON_COMMAND_LIST[] = {
&AmazonScripts::cmdHelp_v2, &AmazonScripts::cmdCycleBack,
&AmazonScripts::cmdChapter, &AmazonScripts::cmdSetHelp,
&AmazonScripts::cmdCenterPanel, &AmazonScripts::cmdMainPanel,
&AmazonScripts::CMDRETFLASH
};
if (commandIndex >= 73)
(this->*AMAZON_COMMAND_LIST[commandIndex - 73])();
else
Scripts::executeCommand(commandIndex);
}
void AmazonScripts::cmdHelp_v2() {
Common::String helpMessage = _data->readString();
if (_game->_helpLevel == 0) {
_game->_timers.saveTimers();
_game->_useItem = 0;
if (_game->_noHints) {
printString(AMRES.NO_HELP_MESSAGE);
return;
} else if (_game->_hintLevel == 0) {
printString(AMRES.NO_HINTS_MESSAGE);
return;
}
}
int level = _game->_hintLevel - 1;
if (level < _game->_helpLevel)
_game->_moreHelp = 0;
_game->drawHelp(helpMessage);
const Common::Rect butn1 = Common::Rect(HELP1COORDS[0][0], HELP1COORDS[0][2], HELP1COORDS[0][1], HELP1COORDS[0][3]);
const Common::Rect butn2 = Common::Rect(HELP1COORDS[1][0], HELP1COORDS[1][2], HELP1COORDS[1][1], HELP1COORDS[1][3]);
while (!_vm->shouldQuit()) {
while (!_vm->shouldQuit() && !_vm->_events->_leftButton)
_vm->_events->pollEventsAndWait();
_vm->_events->debounceLeft();
const Common::Point pt = _vm->_events->_mousePos;
int choice = -1;
if (butn1.contains(pt))
choice = 0;
else if (butn2.contains(pt))
choice = 1;
if (choice < 0)
continue;
if (choice == 1) {
// Done button selected
_game->_helpLevel = 0;
_game->_moreHelp = 1;
_game->_useItem = 0;
_vm->_events->hideCursor();
if (_vm->_screen->_vesaMode) {
_vm->_screen->restoreScreen();
_vm->_screen->setPanel(0);
} else {
_vm->_screen->fadeOut();
_vm->_screen->clearBuffer();
}
_vm->_buffer2.copyTo(_vm->_screen);
_vm->_screen->restorePalette();
_vm->_screen->setPalette();
_vm->_events->showCursor();
delete _vm->_objectsTable[45];
_vm->_objectsTable[45] = nullptr;
_vm->_timers.restoreTimers();
break;
} else {
// More button selected
if (_game->_moreHelp == 0)
continue;
++_game->_helpLevel;
_game->_useItem = 1;
break;
}
}
findNull();
}
void AmazonScripts::cmdCycleBack() {
if (_vm->_startup == -1)
_vm->_screen->cyclePaletteBackwards();
}
void AmazonScripts::cmdChapter() {
Resource *activeScript = nullptr;
if (_vm->isDemo()) {
cmdSetHelp();
} else {
int chapter = _data->readByte();
if (!_vm->isCD()) {
// For floppy version, the current script remains active even
// after the end of the chapter start, so we need to save it
activeScript = _resource;
_resource = nullptr;
_data = nullptr;
}
_game->startChapter(chapter);
if (!_vm->isCD()) {
assert(!_resource);
setScript(activeScript, false);
}
}
}
void AmazonScripts::cmdSetHelp() {
int arrayId = (_data->readUint16LE() & 0xFF) - 1;
int helpId = _data->readUint16LE() & 0xFF;
byte *help = _game->_helpTbl[arrayId];
help[helpId] = 1;
if (_vm->_useItem == 0) {
_sequence = 11000;
searchForSequence();
}
}
void AmazonScripts::cmdCenterPanel() {
if (_vm->_screen->_vesaMode) {
_vm->_screen->clearScreen();
_vm->_screen->setPanel(3);
}
}
void AmazonScripts::cmdMainPanel() {
if (_vm->_screen->_vesaMode) {
_vm->_room->init4Quads();
_vm->_screen->setPanel(0);
}
}
void AmazonScripts::CMDRETFLASH() {
error("TODO CMDRETFLASH");
}
} // End of namespace Amazon
} // End of namespace Access

View File

@@ -0,0 +1,66 @@
/* 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/>.
*
*/
#ifndef ACCESS_AMAZON_SCRIPTS_H
#define ACCESS_AMAZON_SCRIPTS_H
#include "common/scummsys.h"
#include "access/scripts.h"
namespace Access {
namespace Amazon {
class AmazonEngine;
class AmazonScripts : public Scripts {
private:
AmazonEngine *_game;
protected:
void executeSpecial(int commandIndex, int param1, int param2) override;
void executeCommand(int commandIndex) override;
void cLoop();
void mWhile1();
void mWhile2();
void mWhile(int param1);
void loadBackground(int param1, int param2);
void plotInactive();
void loadNSound(int param1, int param2);
void setInactive();
void boatWalls(int param1, int param2);
void cmdHelp_v2();
void cmdCycleBack();
void cmdChapter();
void cmdSetHelp();
void cmdCenterPanel();
void cmdMainPanel();
void CMDRETFLASH();
public:
AmazonScripts(AccessEngine *vm);
};
} // End of namespace Amazon
} // End of namespace Access
#endif /* ACCESS_AMAZON_SCRIPTS_H */

View File

@@ -0,0 +1,348 @@
/* 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/endian.h"
#include "common/memstream.h"
#include "access/access.h"
#include "access/animation.h"
namespace Access {
AnimationResource::AnimationResource(AccessEngine *vm, Resource *res) {
int count = res->_stream->readUint16LE();
Common::Array<int> offsets;
for (int i = 0; i < count; ++i)
offsets.push_back(res->_stream->readUint32LE());
_animations.reserve(count);
for (int i = 0; i < count; ++i) {
res->_stream->seek(offsets[i]);
Animation *anim = new Animation(vm, res->_stream);
_animations.push_back(anim);
}
}
AnimationResource::~AnimationResource() {
for (auto *animation : _animations)
delete animation;
}
/*------------------------------------------------------------------------*/
Animation::Animation(AccessEngine *vm, Common::SeekableReadStream *stream) : Manager(vm) {
uint32 startOfs = stream->pos();
_type = stream->readByte();
// WORKAROUND: In Amazon floppy English, there's an animation associated with
// the librarian that isn't used, and has junk data. Luckily, it's animation
// type is also invalid, so if the _type isn't in range, exit immediately
if (_type > 7) {
_scaling = -1;
_frameNumber = -1;
_initialTicks = _countdownTicks = 0;
_loopCount = _currentLoopCount = 0;
return;
}
_scaling = stream->readSByte();
stream->readByte(); // unk
_frameNumber = stream->readByte();
_initialTicks = stream->readUint16LE();
stream->readUint16LE(); // unk
stream->readUint16LE(); // unk
_loopCount = stream->readSint16LE();
_countdownTicks = stream->readUint16LE();
_currentLoopCount = stream->readSint16LE();
stream->readUint16LE(); // unk
Common::Array<uint16> frameOffsets;
uint16 ofs;
while ((ofs = stream->readUint16LE()) != 0)
frameOffsets.push_back(ofs);
for (uint16 frameOffset : frameOffsets) {
stream->seek(startOfs + frameOffset);
AnimationFrame *frame = new AnimationFrame(stream, startOfs);
_frames.push_back(frame);
}
}
Animation::~Animation() {
for (auto *frame : _frames)
delete frame;
}
typedef void(Animation::*AnimationMethodPtr)();
void Animation::animate() {
static const AnimationMethodPtr METHODS[8] = {
&Animation::anim0, &Animation::anim1, &Animation::anim2, &Animation::anim3,
&Animation::anim4, &Animation::animNone, &Animation::animNone, &Animation::anim7
};
(this->*METHODS[_type])();
}
void Animation::anim0() {
if (_currentLoopCount != -1) {
if (_countdownTicks != 0) {
setFrame1(calcFrame());
} else {
_countdownTicks = _initialTicks;
++_frameNumber;
const AnimationFrame *frame = calcFrame();
if (frame == nullptr) {
_frameNumber = 0;
_currentLoopCount = -1;
frame = calcFrame();
}
setFrame(frame);
}
}
}
void Animation::anim1() {
if (_currentLoopCount == -1 || _countdownTicks != 0) {
setFrame1(calcFrame());
} else {
_countdownTicks = _initialTicks;
++_frameNumber;
const AnimationFrame *frame = calcFrame();
if (frame == nullptr) {
--_frameNumber;
_currentLoopCount = -1;
frame = calcFrame();
}
setFrame(frame);
}
}
void Animation::anim2() {
if (_countdownTicks != 0) {
setFrame1(calcFrame());
} else {
_countdownTicks = _initialTicks;
++_frameNumber;
const AnimationFrame *frame = calcFrame();
if (frame == nullptr) {
_frameNumber = 0;
frame = calcFrame();
}
setFrame(frame);
}
}
void Animation::anim3() {
if (_currentLoopCount != -1) {
if (_countdownTicks != 0) {
setFrame1(calcFrame());
} else {
_countdownTicks = _initialTicks;
++_frameNumber;
const AnimationFrame *frame = calcFrame();
if (frame == nullptr) {
--_currentLoopCount;
_frameNumber = 0;
frame = calcFrame();
}
setFrame(frame);
}
}
}
void Animation::anim4() {
if (_currentLoopCount == -1 || _countdownTicks != 0) {
setFrame1(calcFrame());
} else {
_countdownTicks = _initialTicks;
++_frameNumber;
const AnimationFrame *frame = calcFrame();
if (frame == nullptr) {
if (--_currentLoopCount == -1) {
setFrame1(calcFrame());
return;
} else {
_frameNumber = 0;
frame = calcFrame();
}
}
setFrame(frame);
}
}
void Animation::animNone() {
// Empty implementation
}
void Animation::anim7() {
setFrame(calcFrame1());
}
const AnimationFrame *Animation::calcFrame() {
return (_frameNumber < (int)_frames.size()) ? _frames[_frameNumber] : nullptr;
}
const AnimationFrame *Animation::calcFrame1() {
return _frames[0];
}
void Animation::setFrame(const AnimationFrame *frame) {
assert(frame);
_countdownTicks += frame->_frameDelay;
setFrame1(frame);
}
void Animation::setFrame1(const AnimationFrame *frame) {
_vm->_animation->_base.x = frame->_baseX;
_vm->_animation->_base.y = frame->_baseY;
// Loop to add image draw requests for the parts of the frame
for (const AnimationFramePart &part: frame->_parts) {
ImageEntry ie;
// Set the flags
ie._flags = part._flags & ~IMGFLAG_UNSCALED;
if (_vm->_animation->_frameScale == -1)
ie._flags |= IMGFLAG_UNSCALED;
// Set the other fields
ie._spritesPtr = _vm->_objectsTable[part._spritesIndex];
ie._frameNumber = part._frameIndex;
ie._position = part._position + _vm->_animation->_base;
ie._offsetY = part._offsetY - ie._position.y;
_vm->_images.addToList(ie);
}
}
/*------------------------------------------------------------------------*/
AnimationFrame::AnimationFrame(Common::SeekableReadStream *stream, int startOffset) {
uint16 nextOffset;
stream->readByte(); // unk
_baseX = stream->readUint16LE();
_baseY = stream->readUint16LE();
_frameDelay = stream->readUint16LE();
nextOffset = stream->readUint16LE();
while (nextOffset != 0) {
stream->seek(startOffset + nextOffset);
AnimationFramePart framePart(stream);
_parts.push_back(framePart);
nextOffset = stream->readUint16LE();
}
}
AnimationFrame::~AnimationFrame() {
}
/*------------------------------------------------------------------------*/
AnimationFramePart::AnimationFramePart(Common::SeekableReadStream *stream) {
_flags = stream->readByte();
_spritesIndex = stream->readByte();
_frameIndex = stream->readByte();
_position.x = stream->readUint16LE();
_position.y = stream->readUint16LE();
_offsetY = stream->readUint16LE();
}
/*------------------------------------------------------------------------*/
AnimationManager::AnimationManager(AccessEngine *vm) : Manager(vm) {
_animation = nullptr;
_animStart = nullptr;
_frameScale = 0;
}
AnimationManager::~AnimationManager() {
delete _animation;
}
void AnimationManager::freeAnimationData() {
delete _animation;
_animation = nullptr;
_animStart = nullptr;
}
void AnimationManager::clearTimers() {
_animationTimers.clear();
}
void AnimationManager::loadAnimations(Resource *res) {
_animationTimers.clear();
delete _animation;
_animation = new AnimationResource(_vm, res);
}
Animation *AnimationManager::setAnimation(int animId) {
Animation *anim = findAnimation(animId);
if (!anim)
return nullptr;
anim->_countdownTicks = anim->_initialTicks;
anim->_frameNumber = 0;
anim->_currentLoopCount = (anim->_type != 3 && anim->_type != 4) ? 0 : anim->_loopCount;
return anim;
}
void AnimationManager::setAnimTimer(Animation *anim) {
_animationTimers.push_back(anim);
}
Animation *AnimationManager::findAnimation(int animId) {
_animStart = (_animation == nullptr) ? nullptr : _animation->getAnimation(animId);
return _animStart;
}
void AnimationManager::animate(int animId) {
Animation *anim = findAnimation(animId);
_frameScale = anim->_scaling;
anim->animate();
}
void AnimationManager::updateTimers() {
for (auto *timer : _animationTimers) {
if (timer->_countdownTicks > 0)
timer->_countdownTicks--;
}
}
} // End of namespace Access

143
engines/access/animation.h Normal file
View File

@@ -0,0 +1,143 @@
/* 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/>.
*
*/
#ifndef ACCESS_ANIMATION_H
#define ACCESS_ANIMATION_H
#include "common/scummsys.h"
#include "common/array.h"
#include "common/memstream.h"
#include "access/data.h"
#include "access/files.h"
namespace Access {
class AnimationResource;
class Animation;
class AnimationFrame;
class AnimationFramePart;
class AnimationManager : public Manager {
private:
Common::Array<Animation *> _animationTimers;
AnimationResource *_animation;
public:
Animation *_animStart;
Common::Point _base;
int _frameScale;
public:
AnimationManager(AccessEngine *vm);
~AnimationManager();
void freeAnimationData();
void loadAnimations(Resource *res);
Animation *findAnimation(int animId);
Animation *setAnimation(int animId);
void animate(int animId);
/**
* Clear the list of currently active animations
*/
void clearTimers();
/**
* Add an animation to the list of currently animating ones
*/
void setAnimTimer(Animation *anim);
/**
* Update the timing of all currently active animation
*/
void updateTimers();
/**
* Remove the last animation timer
*/
void popBackTimer() { _animationTimers.pop_back(); }
};
class AnimationResource {
private:
Common::Array<Animation *> _animations;
public:
AnimationResource(AccessEngine *vm, Resource *res);
~AnimationResource();
int getCount() const { return _animations.size(); }
Animation *getAnimation(int idx) { return _animations[idx]; }
};
class Animation : public Manager {
private:
Common::Array<AnimationFrame *> _frames;
void anim0();
void anim1();
void anim2();
void anim3();
void anim4();
void animNone();
void anim7();
const AnimationFrame *calcFrame();
const AnimationFrame *calcFrame1();
void setFrame(const AnimationFrame *frame);
void setFrame1(const AnimationFrame *frame);
public:
int _type;
int _scaling;
int _frameNumber;
int _initialTicks;
int _loopCount;
int _countdownTicks;
int _currentLoopCount;
public:
Animation(AccessEngine *vm, Common::SeekableReadStream *stream);
~Animation();
void animate();
};
class AnimationFrame {
public:
int _baseX, _baseY;
int _frameDelay;
Common::Array<AnimationFramePart> _parts;
public:
AnimationFrame(Common::SeekableReadStream *stream, int startOffset);
~AnimationFrame();
};
class AnimationFramePart {
public:
byte _flags;
int _spritesIndex;
int _frameIndex;
Common::Point _position;
int _offsetY;
public:
AnimationFramePart(Common::SeekableReadStream *stream);
};
} // End of namespace Access
#endif /* ACCESS_ANIMATION_H */

295
engines/access/asurface.cpp Normal file
View File

@@ -0,0 +1,295 @@
/* 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/algorithm.h"
#include "common/endian.h"
#include "common/memstream.h"
#include "access/access.h"
#include "access/asurface.h"
namespace Access {
const int TRANSPARENCY = 0;
SpriteResource::SpriteResource(const AccessEngine *vm, Resource *res) {
Common::Array<uint32> offsets;
int count = res->_stream->readUint16LE();
for (int i = 0; i < count; i++)
offsets.push_back(res->_stream->readUint32LE());
offsets.push_back(res->_size); // For easier calculations of Noctropolis sizes
// Build up the frames
for (int i = 0; i < count; ++i) {
res->_stream->seek(offsets[i]);
int frameSize = offsets[i + 1] - offsets[i];
SpriteFrame *frame = new SpriteFrame(vm, res->_stream, frameSize);
_frames.push_back(frame);
}
}
SpriteResource::~SpriteResource() {
for (auto *frame : _frames)
delete frame;
}
SpriteFrame::SpriteFrame(const AccessEngine *vm, Common::SeekableReadStream *stream, int frameSize) {
int xSize = stream->readUint16LE();
int ySize = stream->readUint16LE();
if (vm->getGameID() == kGameMartianMemorandum) {
int size = stream->readUint16LE();
if (size != frameSize)
warning("Unexpected file difference: framesize %d - size %d %d - unknown %d", frameSize, xSize, ySize, size);
}
Graphics::Screen::create(xSize, ySize);
// Empty surface
byte *data = (byte *)getPixels();
Common::fill(data, data + w * h, TRANSPARENCY);
// Decode the data
for (int y = 0; y < h; ++y) {
int offset = stream->readByte();
int len = stream->readByte();
assert((offset + len) <= w);
byte *destP = (byte *)getBasePtr(offset, y);
stream->read(destP, len);
}
}
SpriteFrame::~SpriteFrame() {
Graphics::Screen::free();
}
/*------------------------------------------------------------------------*/
ImageEntry::ImageEntry() : _frameNumber(0), _spritesPtr(nullptr), _offsetY(0), _flags(0) {
}
/*------------------------------------------------------------------------*/
static bool sortImagesY(const ImageEntry &ie1, const ImageEntry &ie2) {
int v = (ie1._position.y + ie1._offsetY) - (ie2._position.y + ie2._offsetY);
return (v < 0) || (v == 0 && ie1._position.y <= ie2._position.y);
}
void ImageEntryList::addToList(ImageEntry &ie) {
assert(size() < 35);
push_back(ie);
Common::sort(begin(), end(), sortImagesY);
}
/*------------------------------------------------------------------------*/
int BaseSurface::_clipWidth;
int BaseSurface::_clipHeight;
int BaseSurface::_lastBoundsX;
int BaseSurface::_lastBoundsY;
int BaseSurface::_lastBoundsW;
int BaseSurface::_lastBoundsH;
BaseSurface::BaseSurface(): Graphics::Screen(0, 0) {
Graphics::Screen::free(); // Free the 0x0 surface allocated by Graphics::Screen
_leftSkip = _rightSkip = 0;
_topSkip = _bottomSkip = 0;
_orgX1 = _orgY1 = 0;
_orgX2 = _orgY2 = 0;
_lColor = 0;
_maxChars = 0;
}
BaseSurface::~BaseSurface() {
_savedBlock.free();
}
void BaseSurface::clearBuffer() {
byte *pSrc = (byte *)getPixels();
Common::fill(pSrc, pSrc + w * h, 0);
}
void BaseSurface::plotImage(const SpriteResource *sprite, int frameNum, const Common::Point &pt) {
const SpriteFrame *frame = sprite->getFrame(frameNum);
Common::Rect r(pt.x, pt.y, pt.x + frame->w, pt.y + frame->h);
if (!clip(r)) {
_lastBoundsX = r.left;
_lastBoundsY = r.top;
_lastBoundsW = r.width();
_lastBoundsH = r.height();
plotF(frame, pt);
}
}
void BaseSurface::copyBuffer(Graphics::ManagedSurface *src) {
blitFrom(*src);
}
void BaseSurface::plotF(const SpriteFrame *frame, const Common::Point &pt) {
sPlotF(frame, Common::Rect(pt.x, pt.y, pt.x + frame->w, pt.y + frame->h));
}
void BaseSurface::plotB(const SpriteFrame *frame, const Common::Point &pt) {
sPlotB(frame, Common::Rect(pt.x, pt.y, pt.x + frame->w, pt.y + frame->h));
}
void BaseSurface::sPlotF(const SpriteFrame *frame, const Common::Rect &bounds) {
transBlitFrom(*frame, Common::Rect(0, 0, frame->w, frame->h), bounds, TRANSPARENCY, false);
}
void BaseSurface::sPlotB(const SpriteFrame *frame, const Common::Rect &bounds) {
transBlitFrom(*frame, Common::Rect(0, 0, frame->w, frame->h), bounds, TRANSPARENCY, true);
}
void BaseSurface::copyBlock(const BaseSurface *src, const Common::Rect &bounds) {
copyRectToSurface(*src, bounds.left, bounds.top, bounds);
}
void BaseSurface::copyTo(BaseSurface *dest) {
if (dest->empty())
dest->create(this->w, this->h);
dest->blitFrom(*this);
}
void BaseSurface::saveBlock(const Common::Rect &bounds) {
_savedBounds = bounds;
_savedBounds.clip(Common::Rect(0, 0, this->w, this->h));
_savedBlock.free();
_savedBlock.create(bounds.width(), bounds.height(),
Graphics::PixelFormat::createFormatCLUT8());
_savedBlock.copyRectToSurface(*this, 0, 0, _savedBounds);
}
void BaseSurface::restoreBlock() {
if (!_savedBounds.isEmpty()) {
copyRectToSurface(_savedBlock, _savedBounds.left, _savedBounds.top,
Common::Rect(0, 0, _savedBlock.w, _savedBlock.h));
_savedBlock.free();
_savedBounds = Common::Rect(0, 0, 0, 0);
}
}
void BaseSurface::drawRect() {
Graphics::ManagedSurface::fillRect(Common::Rect(_orgX1, _orgY1, _orgX2, _orgY2), _lColor);
}
void BaseSurface::drawLine(int x1, int y1, int x2, int y2, int col) {
Graphics::ManagedSurface::drawLine(x1, y1, x2, y2, col);
}
void BaseSurface::drawLine() {
Graphics::ManagedSurface::drawLine(_orgX1, _orgY1, _orgX2, _orgY1, _lColor);
}
void BaseSurface::drawBox() {
Graphics::ManagedSurface::hLine(_orgX1, _orgY1, _orgX2, _lColor);
Graphics::ManagedSurface::hLine(_orgX1, _orgY2, _orgX2, _lColor);
Graphics::ManagedSurface::vLine(_orgX1, _orgY1, _orgY2, _lColor);
Graphics::ManagedSurface::vLine(_orgX2, _orgY1, _orgY2, _lColor);
}
void BaseSurface::flipHorizontal(BaseSurface &dest) {
dest.create(this->w, this->h);
for (int y = 0; y < h; ++y) {
const byte *pSrc = (const byte *)getBasePtr(this->w - 1, y);
byte *pDest = (byte *)dest.getBasePtr(0, y);
for (int x = 0; x < w; ++x, --pSrc, ++pDest)
*pDest = *pSrc;
}
}
void BaseSurface::moveBufferLeft() {
byte *p = (byte *)getPixels();
Common::copy(p + TILE_WIDTH, p + (w * h), p);
}
void BaseSurface::moveBufferRight() {
byte *p = (byte *)getPixels();
Common::copy_backward(p, p + (pitch * h) - TILE_WIDTH, p + (pitch * h));
}
void BaseSurface::moveBufferUp() {
byte *p = (byte *)getPixels();
Common::copy(p + (pitch * TILE_HEIGHT), p + (pitch * h), p);
}
void BaseSurface::moveBufferDown() {
byte *p = (byte *)getPixels();
Common::copy_backward(p, p + (pitch * (h - TILE_HEIGHT)), p + (pitch * h));
}
/* For compatibility with the original logic, true return means the
rect is *outside* the clip region. */
bool BaseSurface::clip(Common::Rect &r) {
int skip;
_leftSkip = _rightSkip = 0;
_topSkip = _bottomSkip = 0;
if (r.left > _clipWidth || r.left < 0) {
if (r.left >= 0)
return true;
skip = -r.left;
r.setWidth(r.width() - skip);
_leftSkip = skip;
r.moveTo(0, r.top);
}
int right = r.right - 1;
if (right < 0)
return true;
else if (right > _clipWidth) {
skip = right - _clipWidth;
r.setWidth(r.width() - skip);
_rightSkip = skip;
}
if (r.top > _clipHeight || r.top < 0) {
if (r.top >= 0)
return true;
skip = -r.top;
r.setHeight(r.height() - skip);
_topSkip = skip;
r.moveTo(r.left, 0);
}
int bottom = r.bottom - 1;
if (bottom < 0)
return true;
else if (bottom > _clipHeight) {
skip = bottom - _clipHeight;
_bottomSkip = skip;
r.setHeight(r.height() - skip);
}
return false;
}
} // End of namespace Access

177
engines/access/asurface.h Normal file
View File

@@ -0,0 +1,177 @@
/* 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/>.
*
*/
#ifndef ACCESS_ASURFACE_H
#define ACCESS_ASURFACE_H
#include "common/scummsys.h"
#include "common/array.h"
#include "common/memstream.h"
#include "common/rect.h"
#include "graphics/screen.h"
#include "access/data.h"
namespace Access {
class SpriteResource;
class SpriteFrame;
/**
* Base Access surface class. This derivces from Graphics::Screen
* because it has logic we'll need for our own Screen class that
* derives from this one
*/
class BaseSurface : virtual public Graphics::Screen {
private:
Graphics::Surface _savedBlock;
void flipHorizontal(BaseSurface &dest);
protected:
Common::Rect _savedBounds;
public:
int _leftSkip, _rightSkip;
int _topSkip, _bottomSkip;
int _orgX1, _orgY1;
int _orgX2, _orgY2;
int _lColor;
Common::Point _printOrg;
Common::Point _printStart;
int _maxChars;
public:
// These values need to be shared between the buffers
static int _lastBoundsX, _lastBoundsY;
static int _lastBoundsW, _lastBoundsH;
static int _clipWidth, _clipHeight;
public:
BaseSurface();
~BaseSurface() override;
void clearBuffer();
void plotImage(const SpriteResource *sprite, int frameNum, const Common::Point &pt);
/**
* Scaled draw frame in forward orientation
*/
void sPlotF(const SpriteFrame *frame, const Common::Rect &bounds);
/**
* Scaled draw frame in backwards orientation
*/
void sPlotB(const SpriteFrame *frame, const Common::Rect &bounds);
/**
* Draw an image full-size in forward orientation
*/
void plotF(const SpriteFrame *frame, const Common::Point &pt);
/**
* Draw an image full-size in backwards orientation
*/
void plotB(const SpriteFrame *frame, const Common::Point &pt);
virtual void copyBlock(const BaseSurface *src, const Common::Rect &bounds);
virtual void restoreBlock();
virtual void drawRect();
virtual void drawLine(int x1, int y1, int x2, int y2, int col);
virtual void drawLine();
virtual void drawBox();
virtual void copyBuffer(Graphics::ManagedSurface *src);
void copyTo(BaseSurface *dest);
void saveBlock(const Common::Rect &bounds);
void moveBufferLeft();
void moveBufferRight();
void moveBufferUp();
void moveBufferDown();
bool clip(Common::Rect &r);
};
class ASurface : public BaseSurface {
protected:
/**
* Override the addDirtyRect from Graphics::Screen, since for standard
* surfaces we don't need dirty rects to be tracked
*/
void addDirtyRect(const Common::Rect &r) override {}
public:
ASurface() : BaseSurface() {}
};
class SpriteFrame : public ASurface {
public:
SpriteFrame(const AccessEngine *vm, Common::SeekableReadStream *stream, int frameSize);
~SpriteFrame() override;
};
class SpriteResource {
public:
Common::Array<SpriteFrame *> _frames;
public:
SpriteResource(const AccessEngine *vm, Resource *res);
~SpriteResource();
int getCount() { return _frames.size(); }
const SpriteFrame *getFrame(int idx) const { return _frames[idx]; }
};
enum ImageFlag {
IMGFLAG_NONE = 0,
IMGFLAG_CROPPED = 1,
IMGFLAG_BACKWARDS = 2,
IMGFLAG_DRAWN = 4,
IMGFLAG_UNSCALED = 8
};
class ImageEntry {
public:
int _frameNumber;
SpriteResource *_spritesPtr;
int _offsetY;
Common::Point _position;
int _flags;
public:
ImageEntry();
};
class ImageEntryList : public Common::Array<ImageEntry> {
public:
void addToList(ImageEntry &ie);
};
} // End of namespace Access
#endif /* ACCESS_ASURFACE_H */

View File

@@ -0,0 +1,800 @@
/* 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/algorithm.h"
#include "access/bubble_box.h"
#include "access/access.h"
namespace Access {
BubbleBox::BubbleBox(AccessEngine *vm, Access::BoxType type, int x, int y, int w, int h, int val1, int val2, int val3, int val4, const char *title) : Manager(vm) {
_type = type;
_bounds = Common::Rect(x, y, x + w, y + h);
_bubbleDisplStr = title;
_btnId1 = val1;
_btnX1 = val2;
_btnId2 = val3;
_btnX2 = val4;
_btnId3 = _btnX3 = 0; // Unused in MM and Amazon?
_boxStartX = _boxStartY = 0;
_bIconStartX = _bIconStartY = 0;
_boxEndX = _boxEndY = 0;
_boxPStartX = _boxPStartY = 0;
// Unused in AGoE
for (int i = 0; i < ARRAYSIZE(_tempList); i++) {
_tempList[i].clear();
_tempListIdx[i] = 0;
}
_btnUpPos = Common::Rect(0, 0, 0, 0);
_btnDownPos = Common::Rect(0, 0, 0, 0);
_startItem = _startBox = 0;
_charCol = 0;
_rowOff = 0;
}
void BubbleBox::load(Common::SeekableReadStream *stream) {
_bubbleTitle = stream->readString();
_bubbleDisplStr = _bubbleTitle;
}
void BubbleBox::clearBubbles() {
// Loop through the bubble list to restore the screen areas
for (Common::Rect r: _bubbles) {
_vm->_screen->_screenYOff = 0;
r.left -= 2;
r.right = MIN(r.right, (int16)_vm->_screen->w);
_vm->_screen->copyBlock(&_vm->_buffer1, r);
}
// Clear the list
_bubbles.clear();
}
void BubbleBox::placeBubble(const Common::String &msg) {
_vm->_screen->_maxChars = (_vm->getGameID() == kGameMartianMemorandum) ? 30 : 27;
placeBubble1(msg);
}
void BubbleBox::placeBubble1(const Common::String &msg) {
_bubbles.clear();
_vm->_fonts._charSet._lo = 1;
_vm->_fonts._charSet._hi = 8;
_vm->_fonts._charFor._lo = (_vm->getGameID() == kGameMartianMemorandum) ? 247 : 29;
_vm->_fonts._charFor._hi = (_vm->getGameID() == kGameMartianMemorandum) ? 255 : 32;
calcBubble(msg);
Common::Rect r = _bubbles[0];
r.translate(-2, 0);
_vm->_screen->saveBlock(r);
printBubble(msg);
}
void BubbleBox::calcBubble(const Common::String &msg) {
// Save points
Screen &screen = *_vm->_screen;
Common::Point printOrg = screen._printOrg;
Common::Point printStart = screen._printStart;
// Figure out maximum width allowed
if (_type == kBoxTypeFileDialog) {
_vm->_fonts._printMaxX = 110;
} else {
_vm->_fonts._printMaxX = _vm->_fonts._font2->stringWidth(_bubbleDisplStr);
}
// Start of with a rect with the given starting x and y
Common::Rect bounds(printOrg.x - 2, printOrg.y - 10, printOrg.x - 2, printOrg.y - 10);
// Loop through getting lines
Common::String s = msg;
Common::String line;
int width = 0;
bool lastLine;
do {
if (_vm->getGameID() == kGameMartianMemorandum) {
lastLine = _vm->_fonts._font1->getLine(s, screen._maxChars, line, width, Font::kWidthInChars);
width = _vm->_fonts._font1->stringWidth(line);
} else {
lastLine = _vm->_fonts._font2->getLine(s, screen._maxChars * 6, line, width);
}
_vm->_fonts._printMaxX = MAX(width, _vm->_fonts._printMaxX);
screen._printOrg.y += 6;
screen._printOrg.x = screen._printStart.x;
} while (!lastLine);
if (_vm->getGameID() == kGameMartianMemorandum) {
bounds.setWidth((_vm->_fonts._printMaxX / 16 + 2) * 16 + 2 + 1);
bounds.bottom = screen._printOrg.y + 4 + 1;
} else {
// TODO: Check this maths if we implement original file boxes.
if (_type == kBoxTypeFileDialog)
screen._printOrg.y += 6;
// Determine the width for the area
width = (((_vm->_fonts._printMaxX >> 4) + 1) << 4) + 5;
if (width >= 24)
width += 20 - ((width - 24) % 20);
bounds.setWidth(width);
// Determine the height for area
int y = screen._printOrg.y + 6;
if (_type == kBoxTypeFileDialog)
y += 6;
int height = y - bounds.top;
bounds.setHeight(height);
height -= (_type == kBoxTypeFileDialog) ? 30 : 24;
if (height >= 0)
bounds.setHeight(bounds.height() + 13 - (height % 13));
}
if (bounds.bottom > screen.h)
bounds.translate(0, screen.h - bounds.bottom);
// Add the new bounds to the bubbles list
_bubbles.push_back(bounds);
// Restore points
_vm->_screen->_printOrg = printOrg;
_vm->_screen->_printStart = printStart;
}
void BubbleBox::printBubble(const Common::String &msg) {
if (_vm->getGameID() == kGameMartianMemorandum)
printBubble_v1(msg);
else
printBubble_v2(msg);
}
void BubbleBox::printBubble_v1(const Common::String &msg) {
Font::_fontColors[1] = 255;
drawBubble(_bubbles.size() - 1);
Font::_fontColors[3] = 247;
// Loop through drawing the lines
Common::String s = msg;
Common::String line;
int width = 0;
bool lastLine;
do {
// Get next line
Font &font1 = *_vm->_fonts._font1;
lastLine = font1.getLine(s, _vm->_screen->_maxChars, line, width, Font::kWidthInChars);
// Draw the text
printString(line);
// Move print position
_vm->_screen->_printOrg.y += 6;
_vm->_screen->_printOrg.x = _vm->_screen->_printStart.x;
} while (!lastLine);
}
void BubbleBox::printBubble_v2(const Common::String &msg) {
drawBubble(_bubbles.size() - 1);
// Loop through drawing the lines
Common::String s = msg;
Common::String line;
int width = 0;
bool lastLine;
do {
// Get next line
Font &font2 = *_vm->_fonts._font2;
lastLine = font2.getLine(s, _vm->_screen->_maxChars * 6, line, width);
// Set font colors
font2._fontColors[0] = 0;
font2._fontColors[1] = 27;
font2._fontColors[2] = 28;
font2._fontColors[3] = 29;
int xp = _vm->_screen->_printOrg.x;
if (_type == kBoxTypeFileDialog)
xp = (_bounds.width() - width) / 2 + _bounds.left - 4;
// Draw the text
font2.drawString(_vm->_screen, line, Common::Point(xp, _vm->_screen->_printOrg.y));
// Move print position
_vm->_screen->_printOrg.y += 6;
_vm->_screen->_printOrg.x = _vm->_screen->_printStart.x;
} while (!lastLine);
}
void BubbleBox::drawBubble(int index) {
_bounds = _bubbles[index];
if (_vm->getGameID() == kGameMartianMemorandum) {
int btnSelected = 0;
doBox_v1(0, 0, btnSelected);
} else
doBox(0, 0);
}
void BubbleBox::doBox(int item, int box) {
FontManager &fonts = _vm->_fonts;
Screen &screen = *_vm->_screen;
_startItem = item;
_startBox = box;
// Save state information
FontVal charSet = fonts._charSet;
FontVal charFor = fonts._charFor;
Common::Point printOrg = screen._printOrg;
Common::Point printStart = screen._printStart;
int charCol = _charCol;
int rowOff = _rowOff;
_vm->_screen->saveScreen();
_vm->_screen->setDisplayScan();
fonts._charFor._hi = 0xff;
fonts._charSet._lo = 1;
fonts._charSet._hi = 0;
if (_type == kBoxTypeFileDialog) {
fonts._charFor._lo = 0xFF;
error("TODO: filename listing");
return;
}
const SpriteResource *icons = _vm->getIcons();
// Set the up boundaries and color to use for the box background
_vm->_screen->_orgX1 = _bounds.left - 2;
_vm->_screen->_orgY1 = _bounds.top;
_vm->_screen->_orgX2 = _bounds.right - 2;
_vm->_screen->_orgY2 = _bounds.bottom;
_vm->_screen->_lColor = 1;
int h = _bounds.height() - (_type == kBoxTypeFileDialog ? 30 : 24);
int ySize = (h < 0) ? 0 : (h + 12) / 13;
int w = _bounds.width() - 24;
int xSize = (w < 0) ? 0 : (w + 19) / 20;
// Draw a background for the entire area
screen.drawRect();
// Draw images to form the top border
int xp, yp;
screen.plotImage(icons, 20, Common::Point(screen._orgX1, screen._orgY1));
xp = screen._orgX1 + 12;
for (int x = 0; x < xSize; ++x, xp += 20)
screen.plotImage(icons, 24 + x, Common::Point(xp, screen._orgY1));
screen.plotImage(icons, 21, Common::Point(xp, screen._orgY1));
// Draw images to form the bottom border
yp = screen._orgY2 - (_type == kBoxTypeFileDialog ? 18 : 12);
screen.plotImage(icons, (_type == kBoxTypeFileDialog) ? 72 : 22,
Common::Point(screen._orgX1, yp));
xp = screen._orgX1 + 12;
yp += (_type == kBoxTypeFileDialog) ? 4 : 8;
for (int x = 0; x < xSize; ++x, xp += 20) {
screen.plotImage(icons, (_type == kBoxTypeFileDialog ? 62 : 34) + x,
Common::Point(xp, yp));
}
yp = screen._orgY2 - (_type == kBoxTypeFileDialog ? 18 : 12);
screen.plotImage(icons, (_type == kBoxTypeFileDialog) ? 73 : 23, Common::Point(xp, yp));
if (_type == kBoxTypeFileDialog) {
// Further stuff for filename dialog
error("TODO: Box type 4");
}
// Draw images to form the sides
yp = screen._orgY1 + 12;
for (int y = 0; y < ySize; ++y, yp += 13) {
screen.plotImage(icons, 44 + y, Common::Point(screen._orgX1, yp));
screen.plotImage(icons, 53 + y, Common::Point(screen._orgX2 - 4, yp));
}
// Handle drawing title
int titleWidth = _vm->_fonts._font2->stringWidth(_bubbleDisplStr);
Font &font2 = *_vm->_fonts._font2;
font2._fontColors[0] = 0;
font2._fontColors[1] = 3;
font2._fontColors[2] = 2;
font2._fontColors[3] = 1;
font2.drawString(_vm->_screen, _bubbleDisplStr, Common::Point(
_bounds.left + (_bounds.width() / 2) - (titleWidth / 2), _bounds.top + 1));
// Restore positional state
fonts._charSet = charSet;
fonts._charFor = charFor;
screen._printOrg = printOrg;
screen._printStart = printStart;
_charCol = charCol;
_rowOff = rowOff;
_vm->_screen->restoreScreen();
}
void BubbleBox::setCursorPos(int posX, int posY) {
Common::Point newPt = Common::Point(posX * 8, posY * 8 + _rowOff);
_vm->_screen->_printStart = _vm->_screen->_printOrg = newPt;
}
void BubbleBox::printString(Common::String msg) {
if (_vm->_fonts._charSet._hi & 2) {
// Draw a shadow for the text
Common::Point shadowPt = _vm->_screen->_printOrg + Common::Point(1, 1);
byte oldcol = Font::_fontColors[3];
Font::_fontColors[3] = 0;
_vm->_fonts._font1->drawString(_vm->_screen, msg, shadowPt);
Font::_fontColors[3] = oldcol;
}
_vm->_fonts._font1->drawString(_vm->_screen, msg, _vm->_screen->_printOrg);
}
void BubbleBox::displayBoxData() {
_vm->_boxDataEnd = false;
_rowOff = 2;
_vm->_fonts._charFor._lo = 0xF7;
_vm->_fonts._charFor._hi = 0xFF;
Font::_fontColors[3] = 247;
if (_tempList[0].size() == 0)
return;
int idx = 0;
if ((_type == TYPE_1) || (_type == TYPE_3)) {
_vm->_bcnt = 0;
if (_tempList[idx].size() == 0) {
_vm->_boxDataEnd = true;
return;
}
_vm->_events->hideCursor();
_vm->_screen->_orgX1 = _boxStartX;
_vm->_screen->_orgX2 = _boxEndX + 1;
_vm->_screen->_orgY1 = _boxStartY;
_vm->_screen->_orgY2 = _boxEndY;
_vm->_screen->_lColor = 0xFA;
_vm->_screen->drawRect();
_vm->_events->showCursor();
}
_vm->_events->hideCursor();
int oldPStartY = _boxPStartY;
++_boxPStartY;
idx += _vm->_boxDataStart;
while (true) {
setCursorPos(_boxPStartX, _boxPStartY);
printString(_tempList[idx]);
++idx;
++_boxPStartY;
++_vm->_bcnt;
if (_tempList[idx].size() == 0) {
_boxPStartY = oldPStartY;
_vm->_events->showCursor();
_vm->_boxDataEnd = true;
return;
}
if (_vm->_bcnt == _vm->_numLines) {
_boxPStartY = oldPStartY;
_vm->_events->showCursor();
return;
}
}
}
void BubbleBox::drawSelectBox() {
if (_tempList[0].size() == 0)
return;
if (((_type != TYPE_1) && (_type != TYPE_3)) || !_vm->_bcnt)
return;
if (_vm->_boxSelectYOld != -1) {
_vm->_events->hideCursor();
_vm->_screen->_lColor = 0xFA;
int val = _vm->_boxSelectYOld + _boxPStartY + 1;
_vm->_screen->_orgY1 = (val << 3) + 2;
_vm->_screen->_orgY2 = _vm->_screen->_orgY1 + 7;
_vm->_screen->_orgX1 = _boxStartX;
_vm->_screen->_orgX2 = _boxEndX;
_vm->_screen->drawBox();
_vm->_events->showCursor();
}
_vm->_events->hideCursor();
_vm->_boxSelectYOld = _vm->_boxSelectY;
int val = _boxPStartY + _vm->_boxSelectY + 1;
_vm->_screen->_orgY1 = (val << 3) + 2;
_vm->_screen->_orgY2 = _vm->_screen->_orgY1 + 7;
_vm->_screen->_orgX1 = _boxStartX;
_vm->_screen->_orgX2 = _boxEndX;
_vm->_screen->_lColor = 0xFE;
_vm->_screen->drawBox();
_vm->_events->showCursor();
if (_type == TYPE_3)
warning("TODO: List filenames");
}
int BubbleBox::doBox_v1(int item, int box, int &btnSelected) {
static const int ICONW[] = { 0, 11, 28, 19, 19, 15 };
FontManager &fonts = _vm->_fonts;
int retval_ = -1;
_startItem = item;
_startBox = box;
Common::Point origPrintStart = _vm->_screen->_printStart;
Common::Point origPrintOrg = _vm->_screen->_printOrg;
_vm->_events->hideCursor();
// Save state information
_vm->_screen->saveScreen();
_vm->_screen->setDisplayScan();
fonts._charFor._hi = 0xff;
fonts._charSet._lo = 1;
fonts._charSet._hi = 0;
_vm->_destIn = _vm->_screen; // TODO: Redundant
if (_type != TYPE_2) {
Common::Rect r = _bounds;
r.left -= 2;
_vm->_screen->saveBlock(r);
}
// Set the up boundaries and color to use for the box background
_vm->_screen->_orgX1 = _bounds.left - 2;
_vm->_screen->_orgY1 = _bounds.top;
_vm->_screen->_orgX2 = _bounds.right - 2;
_vm->_screen->_orgY2 = _bounds.bottom;
_vm->_screen->_lColor = 0xFB;
// Draw a background for the entire area
_vm->_screen->drawRect();
// Draw the inner box;
++_vm->_screen->_orgX1;
++_vm->_screen->_orgY1;
_vm->_screen->_orgX2 -= 2;
_vm->_screen->_orgY2 -= 2;
_vm->_screen->_lColor = 0xF9;
// Draw the inner border
_vm->_screen->drawBox();
const SpriteResource *icons = _vm->getIcons();
// Draw upper border
_vm->_bcnt = (_vm->_screen->_orgX2 - _vm->_screen->_orgX1) >> 4;
int oldX = _vm->_screen->_orgX1;
for ( ;_vm->_bcnt > 0; --_vm->_bcnt) {
_vm->_screen->plotImage(icons, 16, Common::Point(_vm->_screen->_orgX1, _vm->_screen->_orgY1));
_vm->_screen->_orgX1 += 16;
}
_vm->_screen->_orgX1 = oldX;
int oldY = _vm->_screen->_orgY2;
_vm->_screen->_orgY2 = _vm->_screen->_orgY1 + 8;
_vm->_screen->_lColor = 0xF9;
_boxStartY = _vm->_screen->_orgY2 + 1;
_vm->_screen->_orgY2 = oldY;
int tmpX = 0;
int tmpY = 0;
if (_type != TYPE_2) {
// Draw the button background at the bottom
oldY = _vm->_screen->_orgY1;
// Original does this here, but we need bottom/right offset by 1.
//--_vm->_screen->_orgY2;
_vm->_screen->_orgY1 = _vm->_screen->_orgY2 - 8;
if (_type == TYPE_3)
_vm->_screen->_orgY1 -= 8;
_vm->_screen->drawRect();
tmpX = _bIconStartX = _vm->_screen->_orgX1;
_boxStartX = tmpX + 1;
tmpY = _boxEndY = _vm->_screen->_orgY1;
if (_type == TYPE_3)
_bIconStartY = tmpY + 9;
else
_bIconStartY = tmpY + 1;
if (_type == TYPE_3) {
_fileStart = Common::Point((tmpX + 2) >> 3, (tmpY + 2) >> 3);
int rowOff = tmpY - (_fileStart.y << 3) + 1;
if (rowOff == 8) {
rowOff = 0;
++_fileStart.y;
}
_fileOff.y = _rowOff = rowOff;
setCursorPos(_fileStart.x, _fileStart.y);
_vm->_fonts._charFor._lo = 0xF7;
_vm->_fonts._charFor._hi = 0;
printString("FILE: ");
_vm->_fonts._charFor._hi = 0xFF;
}
_vm->_screen->_orgY1 = oldY;
}
if ((_type != TYPE_0) && (_type != TYPE_2)) {
_vm->_screen->_orgY1 += 8;
if (_type == TYPE_3)
_vm->_screen->_orgY2 -= 8;
_vm->_screen->_orgY2 -= 8;
_btnUpPos.right = _btnDownPos.right = _vm->_screen->_orgX2;
_btnUpPos.left = _btnDownPos.left = _vm->_screen->_orgX1 = _vm->_screen->_orgX2 - 8;
_boxEndX = _vm->_screen->_orgX1 - 1;
_vm->_screen->drawBox();
_vm->_screen->_orgY1 += 6;
_vm->_screen->_orgY2 -= 6;
_vm->_screen->drawBox();
_btnUpPos.bottom = _vm->_screen->_orgY1 + 1;
_btnUpPos.top = _btnUpPos.bottom - 5;
_btnDownPos.top = _vm->_screen->_orgY2 + 1;
_btnDownPos.bottom = _btnDownPos.top + 6;
_vm->_screen->_orgX1 += 4;
_vm->_screen->_orgX2 = _vm->_screen->_orgX1;
_vm->_screen->_orgY1 -= 4;
_vm->_screen->_orgY2 += 2;
_vm->_screen->drawLine();
++_vm->_screen->_orgY1;
--_vm->_screen->_orgX1;
++_vm->_screen->_orgX2;
_vm->_screen->drawLine();
++_vm->_screen->_orgY1;
--_vm->_screen->_orgX1;
++_vm->_screen->_orgX2;
_vm->_screen->drawLine();
_vm->_screen->_orgY1 = _vm->_screen->_orgY2;
_vm->_screen->drawLine();
++_vm->_screen->_orgX1;
--_vm->_screen->_orgX2;
++_vm->_screen->_orgY1;
_vm->_screen->drawLine();
++_vm->_screen->_orgX1;
--_vm->_screen->_orgX2;
++_vm->_screen->_orgY1;
_vm->_screen->drawLine();
}
// Draw the box title
int displStrLen = _bubbleDisplStr.size();
_boxPStartX = _bounds.left / 8;
int newX = _boxPStartX + (_bounds.width() / 8 - displStrLen) / 2;
_boxPStartY = _bounds.top / 8;
_rowOff = 1 - (_boxPStartY * 8 - _bounds.top);
if (_rowOff == 8) {
_rowOff = 0;
++_boxPStartY;
}
retval_ = _rowOff;
setCursorPos(newX, _boxPStartY);
Font::_fontColors[1] = _vm->_fonts._charFor._lo = 0xFF;
_vm->_fonts._bitFont->drawString(_vm->_screen, _bubbleDisplStr, _vm->_screen->_printOrg);
if (_type == TYPE_2) {
_vm->_events->showCursor();
_vm->_screen->_printStart = origPrintStart;
_vm->_screen->_printOrg = origPrintOrg;
_vm->_screen->restoreScreen();
return retval_;
}
_vm->_destIn = _vm->_screen;
// Draw buttons
int ICON1T = 0;
int ICON1X = 0;
int ICON1Y = 0;
int ICON2T = 0;
int ICON2X = 0;
int ICON3T = 0;
int ICON3X = 0;
if (_btnId1) {
ICON1T = _btnId1;
ICON1X = _bIconStartX + _btnX1;
ICON1Y = _bIconStartY;
_vm->_screen->plotImage(icons, ICON1T + 10, Common::Point(ICON1X, ICON1Y));
if (_btnId2) {
ICON2T = _btnId2;
ICON2X = _bIconStartX + _btnX2;
_vm->_screen->plotImage(icons, ICON2T + 10, Common::Point(ICON2X, _bIconStartY));
if (_btnId3) {
ICON3T = _btnId3;
ICON3X = _bIconStartX + _btnX3;
_vm->_screen->plotImage(icons, ICON3T + 10, Common::Point(ICON3X, _bIconStartY));
}
}
}
_vm->_screen->restoreScreen();
_vm->_boxDataStart = _startItem;
_vm->_boxSelectYOld = -1;
_vm->_boxSelectY = _startBox;
_vm->_numLines = (_bounds.height() / 8) - 2;
if (_type == TYPE_3)
--_vm->_numLines;
_vm->_events->showCursor();
displayBoxData();
drawSelectBox();
while (!_vm->shouldQuit()) {
_vm->_events->pollEvents();
if (!_vm->_events->_leftButton && !_vm->_events->_wheelDown && !_vm->_events->_wheelUp && !_vm->_events->isKeyActionPending())
continue;
//
// Slight enhancement from original - we also allow mouse wheel and
// keyboard events to scroll up/down.
//
if (((_type == TYPE_1) || (_type == TYPE_3)) && (_vm->_timers[2]._flag == 0)) {
++_vm->_timers[2]._flag;
Common::CustomEventType action = kActionNone;
_vm->_events->getAction(action);
if (_btnUpPos.contains(_vm->_events->_mousePos) || _vm->_events->_wheelUp || action == kActionMoveUp) {
if (_vm->_bcnt) {
if (_vm->_boxSelectY != 0) {
--_vm->_boxSelectY;
drawSelectBox();
} else if (_vm->_boxDataStart != 0) {
--_vm->_boxDataStart;
displayBoxData();
drawSelectBox();
}
}
continue;
} else if (_btnDownPos.contains(_vm->_events->_mousePos) || _vm->_events->_wheelDown || action == kActionMoveDown) {
if (_vm->_bcnt) {
if (_vm->_bcnt == _vm->_numLines) {
if (_vm->_bcnt != _vm->_boxSelectY + 1) {
++_vm->_boxSelectY;
drawSelectBox();
} else if (!_vm->_boxDataEnd) {
++_vm->_boxDataStart;
displayBoxData();
drawSelectBox();
}
} else if (_vm->_bcnt != _vm->_boxSelectY + 1) {
++_vm->_boxSelectY;
drawSelectBox();
}
}
continue;
}
}
if (!_vm->_events->_leftButton)
continue;
if ((_vm->_events->_mousePos.x >= _boxStartX) && (_vm->_events->_mousePos.x <= _boxEndX)
&& (_vm->_events->_mousePos.y >= _boxStartY) && (_vm->_events->_mousePos.y <= _boxEndY)) {
int val = (_vm->_events->_mousePos.y >> 3) - _boxPStartY;
if (val > _vm->_bcnt)
continue;
--val;
if (_type == TYPE_3)
_vm->_boxSelect = val;
else {
btnSelected = 1;
if (_vm->_boxSelectY == val)
break;
_vm->_boxSelectY = val;
_vm->_events->debounceLeft();
drawSelectBox();
continue;
}
}
if ((_vm->_events->_mousePos.y >= ICON1Y) && (_vm->_events->_mousePos.y <= ICON1Y + 8)
&& (_vm->_events->_mousePos.x >= ICON1X)) {
btnSelected = 1;
if (_vm->_events->_mousePos.x < ICON1X + ICONW[ICON1T])
break;
if ((_vm->_events->_mousePos.x >= ICON2X) && (_vm->_events->_mousePos.x < ICON2X + ICONW[ICON2T])) {
btnSelected = 2;
break;
}
if ((_vm->_events->_mousePos.x >= ICON3X) && (_vm->_events->_mousePos.x < ICON3X + ICONW[ICON3T])) {
btnSelected = 3;
break;
}
if (_type != TYPE_3)
continue;
if ((_vm->_events->_mousePos.x < tmpX) || (_vm->_events->_mousePos.x > tmpX + 144))
continue;
if ((_vm->_events->_mousePos.y < tmpY) || (_vm->_events->_mousePos.y > tmpY + 8))
continue;
warning("TODO: sub175B5 - List of files");
}
}
_vm->_events->hideCursor();
_vm->_screen->restoreBlock();
_vm->_events->showCursor();
_vm->_events->debounceLeft();
if (_vm->_bcnt == 0)
retval_ = -1;
else
retval_ = _vm->_boxDataStart + _vm->_boxSelectY;
_vm->_screen->_printStart = origPrintStart;
_vm->_screen->_printOrg = origPrintOrg;
return retval_;
}
void BubbleBox::getList(const char *const data[], const byte *flags) {
int srcIdx = 0;
int destIdx = 0;
while (data[srcIdx]) {
if (flags[srcIdx]) {
_tempList[destIdx] = data[srcIdx];
_tempListIdx[destIdx] = srcIdx;
++destIdx;
}
srcIdx++;
}
_tempList[destIdx] = "";
}
} // End of namespace Access

112
engines/access/bubble_box.h Normal file
View File

@@ -0,0 +1,112 @@
/* 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/>.
*
*/
#ifndef ACCESS_BUBBLE_BOX_H
#define ACCESS_BUBBLE_BOX_H
#include "common/scummsys.h"
#include "common/array.h"
#include "common/rect.h"
#include "common/str-array.h"
#include "common/stream.h"
#include "common/types.h"
#include "graphics/surface.h"
#include "access/data.h"
namespace Access {
class AccessEngine;
enum BoxType { TYPE_0 = 0, TYPE_1 = 1, TYPE_2 = 2, TYPE_3 = 3, kBoxTypeFileDialog = 4 };
class BubbleBox : public Manager {
private:
int _startItem, _startBox;
int _charCol, _rowOff;
Common::Point _fileStart;
Common::Point _fileOff;
int _boxStartX, _boxStartY;
int _boxEndX, _boxEndY;
int _bIconStartX, _bIconStartY;
int _boxPStartX, _boxPStartY;
void displayBoxData();
void drawSelectBox();
/**
* Prints a text bubble and it's contents
*/
void printBubble_v1(const Common::String &msg);
void printBubble_v2(const Common::String &msg);
public:
BoxType _type;
Common::Rect _bounds;
Common::StringArray _nameIndex;
Common::String _bubbleTitle;
Common::String _bubbleDisplStr;
Common::String _tempList[60];
int _tempListIdx[60];
int _btnId1;
int _btnX1;
int _btnId2;
int _btnX2;
int _btnId3;
int _btnX3;
Common::Rect _btnUpPos;
Common::Rect _btnDownPos;
Common::Array<Common::Rect> _bubbles;
public:
BubbleBox(AccessEngine *vm, Access::BoxType type, int x, int y, int w, int h, int val1, int val2, int val3, int val4, const char *title);
void load(Common::SeekableReadStream *stream);
void clearBubbles();
void placeBubble(const Common::String &msg);
void placeBubble1(const Common::String &msg);
/**
* Calculate the size of a bubble needed to hold a given string
*/
void calcBubble(const Common::String &msg);
/**
* Prints a text bubble and it's contents
*/
void printBubble(const Common::String &msg);
/*
* Draws the background for a text bubble
* @param index Index of bounds in _bubbles array
*/
void drawBubble(int index);
void doBox(int item, int box);
int doBox_v1(int item, int box, int &btnSelected);
void getList(const char *const data[], const byte *flags);
void setCursorPos(int posX, int posY);
void printString(Common::String msg);
};
} // End of namespace Access
#endif /* ACCESS_BUBBLE_BOX_H */

180
engines/access/char.cpp Normal file
View File

@@ -0,0 +1,180 @@
/* 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/memstream.h"
#include "access/access.h"
#include "access/char.h"
#include "access/amazon/amazon_resources.h"
namespace Access {
CharEntry::CharEntry(const byte *data, AccessEngine *vm) {
Common::MemoryReadStream s(data, 999);
_charFlag = s.readByte();
if (vm->getGameID() != kGameAmazon || !vm->isCD()) {
_screenFile.load(s);
_estabIndex = s.readSint16LE();
} else {
_estabIndex = s.readSint16LE();
_screenFile.load(s);
}
_paletteFile.load(s);
_startColor = s.readUint16LE();
if (vm->getGameID() == kGameMartianMemorandum) {
int lastColor = s.readUint16LE();
_numColors = lastColor - _startColor;
} else
_numColors = s.readUint16LE();
// Load cells
for (byte cell = s.readByte(); cell != 0xff; cell = s.readByte()) {
CellIdent ci;
ci._cell = cell;
ci.load(s);
_cells.push_back(ci);
}
_animFile.load(s);
_scriptFile.load(s);
for (int16 v = s.readSint16LE(); v != -1; v = s.readSint16LE()) {
ExtraCell ec;
ec._vid._fileNum = v;
ec._vid._subfile = s.readSint16LE();
ec._vidSound.load(s);
_extraCells.push_back(ec);
}
}
CharEntry::CharEntry() : _charFlag(0), _estabIndex(0), _startColor(0), _numColors(0) {
}
/*------------------------------------------------------------------------*/
CharManager::CharManager(AccessEngine *vm) : Manager(vm) {
// Setup character list
for (const auto &charEntry: _vm->_res->CHARTBL) {
if (charEntry.size() == 0)
_charTable.push_back(CharEntry());
else
_charTable.push_back(CharEntry(charEntry.data(), _vm));
}
_charFlag = 0;
}
void CharManager::loadChar(int charId) {
const CharEntry &ce = _charTable[charId];
_charFlag = ce._charFlag;
// Amazon calls "establish" before loading the screen, but MM does it after.
_vm->_establishFlag = false;
if (_vm->getGameID() == kGameAmazon && ce._estabIndex != -1) {
_vm->_establishFlag = true;
if (!_vm->_establishTable[ce._estabIndex]) {
_vm->_establishTable[ce._estabIndex] = true;
_vm->establish(0, ce._estabIndex);
}
}
if (_charFlag != 0 && _charFlag != 3) {
if (!_vm->_establishFlag)
_vm->_screen->fadeOut();
_vm->_files->loadScreen(ce._screenFile._fileNum, ce._screenFile._subfile);
_vm->_screen->setIconPalette();
_vm->_screen->fadeIn();
}
_vm->_buffer1.blitFrom(*_vm->_screen);
_vm->_buffer2.blitFrom(*_vm->_screen);
_vm->_screen->setDisplayScan();
if (_vm->getGameID() == kGameMartianMemorandum && ce._estabIndex != -1) {
_vm->_establishFlag = true;
if (!_vm->_establishTable[ce._estabIndex]) {
_vm->_establishTable[ce._estabIndex] = true;
_vm->establish(0, ce._estabIndex);
_vm->_screen->blitFrom(_vm->_buffer1);
}
}
if (_charFlag != 2 && _charFlag != 3) {
charMenu();
}
_vm->_screen->_startColor = ce._startColor;
_vm->_screen->_numColors = ce._numColors;
if (ce._paletteFile._fileNum != -1) {
int srcOffset = (_vm->getGameID() == kGameMartianMemorandum ? ce._startColor * 3 : 0);
_vm->_screen->loadPalette(ce._paletteFile._fileNum, ce._paletteFile._subfile, srcOffset);
}
_vm->_screen->setIconPalette();
_vm->_screen->setPalette();
_vm->loadCells(ce._cells);
if (ce._animFile._fileNum != -1) {
Resource *data = _vm->_files->loadFile(ce._animFile);
_vm->_animation->loadAnimations(data);
delete data;
}
// Load script data
_vm->_scripts->freeScriptData();
if (ce._scriptFile._fileNum != -1) {
Resource *data = _vm->_files->loadFile(ce._scriptFile);
_vm->_scripts->setScript(data);
}
// Load extra cells
_vm->_extraCells.clear();
for (const auto &extraCell : ce._extraCells)
_vm->_extraCells.push_back(extraCell);
}
void CharManager::charMenu() {
const SpriteResource *icons = _vm->getIcons();
Screen &screen = *_vm->_screen;
screen.saveScreen();
screen.setDisplayScan();
if (_vm->getGameID() == kGameMartianMemorandum) {
screen.plotImage(icons, 17, Common::Point(0, 184));
screen.plotImage(icons, 18, Common::Point(193, 184));
} else if (_vm->getGameID() == kGameAmazon) {
screen.plotImage(icons, 17, Common::Point(0, 176));
screen.plotImage(icons, 18, Common::Point(155, 176));
} else
error("Game not supported");
// Make a backup copy of the screen including the character buttons,
// for restoring when erasing conversation boxes
screen.copyTo(&_vm->_buffer1);
screen.restoreScreen();
}
} // End of namespace Access

63
engines/access/char.h Normal file
View File

@@ -0,0 +1,63 @@
/* 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/>.
*
*/
#ifndef ACCESS_CHAR_H
#define ACCESS_CHAR_H
#include "common/scummsys.h"
#include "common/array.h"
#include "access/data.h"
namespace Access {
class CharEntry {
public:
int _charFlag;
int _estabIndex;
FileIdent _screenFile;
FileIdent _paletteFile;
int _startColor, _numColors;
Common::Array<CellIdent> _cells;
FileIdent _animFile;
FileIdent _scriptFile;
Common::Array<ExtraCell> _extraCells;
public:
CharEntry(const byte *data, AccessEngine *vm);
CharEntry();
};
class CharManager : public Manager {
private:
void charMenu();
public:
Common::Array<CharEntry> _charTable;
int _charFlag;
public:
CharManager(AccessEngine *vm);
void loadChar(int charId);
};
} // End of namespace Access
#endif /* ACCESS_CHAR_H */

View File

@@ -0,0 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
add_engine access "Access" yes "" "" "" "midi"

View File

@@ -0,0 +1,5 @@
begin_section("Access");
add_person("Arnaud Boutonn&eacute;", "Strangerke", "");
add_person("Paul Gilbert", "dreammaster", "");
add_person("Matthew Duggan", "stauff", "");
end_section();

74
engines/access/data.cpp Normal file
View File

@@ -0,0 +1,74 @@
/* 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/algorithm.h"
#include "common/stream.h"
#include "common/system.h"
#include "access/data.h"
namespace Access {
TimerList::TimerList() : Common::Array<TimerEntry>() {
_timersSavedFlag = false;
}
void TimerList::saveTimers() {
if (!_timersSavedFlag /* && !_flashbackFlag */) {
_savedTimers = *this;
_timersSavedFlag = true;
}
}
void TimerList::restoreTimers() {
if (_timersSavedFlag /* && !_flashbackFlag */) {
clear();
*static_cast<Common::Array<TimerEntry> *>(this) = _savedTimers;
_timersSavedFlag = false;
}
}
void TimerList::updateTimers() {
for (uint i = 0; i < size(); ++i) {
TimerEntry &te = (*this)[i];
if (te._flag) {
if (!--te._timer) {
te._timer = te._initTm;
te._flag = 0;
}
}
}
}
void TimerList::synchronize(Common::Serializer &s) {
int count = size();
s.syncAsUint16LE(count);
if (!s.isSaving())
resize(count);
for (int i = 0; i < count; ++i) {
s.syncAsUint32LE((*this)[i]._initTm);
s.syncAsUint32LE((*this)[i]._timer);
s.syncAsByte((*this)[i]._flag);
}
}
} // End of namespace Access

99
engines/access/data.h Normal file
View File

@@ -0,0 +1,99 @@
/* 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/>.
*
*/
#ifndef ACCESS_DATA_H
#define ACCESS_DATA_H
#include "common/scummsys.h"
#include "common/array.h"
#include "common/rect.h"
#include "common/serializer.h"
#include "common/types.h"
#include "graphics/surface.h"
#include "access/files.h"
namespace Access {
class AccessEngine;
class Manager {
protected:
AccessEngine *_vm;
public:
Manager(AccessEngine *vm) : _vm(vm) {}
};
struct TimerEntry {
int _initTm;
int _timer;
byte _flag;
TimerEntry() : _flag(0), _initTm(0), _timer(0) { }
};
class TimerList : public Common::Array<TimerEntry> {
private:
Common::Array<TimerEntry> _savedTimers;
public:
bool _timersSavedFlag;
public:
TimerList();
/**
* Save a copy of all current timers
*/
void saveTimers();
/**
* Resetore the set of previously saved timers
*/
void restoreTimers();
/**
* Update the timer list
*/
void updateTimers();
/**
* Synchronize savegame data
*/
void synchronize(Common::Serializer &s);
};
class ExtraCell {
public:
FileIdent _vid;
FileIdent _vidSound;
};
struct DeathEntry {
int _screenId;
Common::String _msg;
};
class DeathList : public Common::Array<DeathEntry> {
public:
Common::Array<CellIdent> _cells;
};
} // End of namespace Access
#endif /* ACCESS_DATA_H */

410
engines/access/debugger.cpp Normal file
View File

@@ -0,0 +1,410 @@
/* 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 "access/access.h"
#include "access/debugger.h"
#include "access/amazon/amazon_game.h"
namespace Access {
static int strToInt(const char *s) {
if (!*s)
// No string at all
return 0;
else if (toupper(s[strlen(s) - 1]) != 'H')
// Standard decimal string
return atoi(s);
// Hexadecimal string
uint tmp = 0;
int read = sscanf(s, "%xh", &tmp);
if (read < 1)
error("strToInt failed on string \"%s\"", s);
return (int)tmp;
}
Debugger *Debugger::init(AccessEngine *vm) {
switch (vm->getGameID()) {
case kGameAmazon:
return new Amazon::AmazonDebugger(vm);
default:
return new Debugger(vm);
}
}
void Debugger::postEnter() {
if (!_playMovieFile.empty()) {
_vm->playMovie(_playMovieFile, Common::Point(0, 0));
_playMovieFile.clear();
}
GUI::Debugger::postEnter();
}
/*------------------------------------------------------------------------*/
Debugger::Debugger(AccessEngine *vm) : GUI::Debugger(), _vm(vm) {
registerCmd("continue", WRAP_METHOD(Debugger, cmdExit));
registerCmd("scene", WRAP_METHOD(Debugger, Cmd_LoadScene));
registerCmd("cheat", WRAP_METHOD(Debugger, Cmd_Cheat));
registerCmd("playmovie", WRAP_METHOD(Debugger, Cmd_PlayMovie));
registerCmd("dumpscript", WRAP_METHOD(Debugger, Cmd_DumpScript));
registerCmd("timers", WRAP_METHOD(Debugger, Cmd_Timers));
registerCmd("flag", WRAP_METHOD(Debugger, Cmd_Flag));
registerCmd("travel", WRAP_METHOD(Debugger, Cmd_Travel));
registerCmd("ask", WRAP_METHOD(Debugger, Cmd_Ask));
registerCmd("inventory", WRAP_METHOD(Debugger, Cmd_Inventory));
registerCmd("everything", WRAP_METHOD(Debugger, Cmd_Everything));
}
Debugger::~Debugger() {
}
bool Debugger::Cmd_LoadScene(int argc, const char **argv) {
switch (argc) {
case 1:
debugPrintf("Current scene is: %d\n\n", _vm->_player->_roomNumber);
for (uint i = 0; i < _vm->_res->ROOMTBL.size(); i++)
if (!_vm->_res->ROOMTBL[i]._desc.empty())
debugPrintf("%d - %s\n", i, _vm->_res->ROOMTBL[i]._desc.c_str());
return true;
case 2: {
int newRoom = strToInt(argv[1]);
if (newRoom < 0 || newRoom >= (int)_vm->_res->ROOMTBL.size()) {
debugPrintf("Invalid Room Number\n");
return true;
}
if (_vm->_res->ROOMTBL[newRoom]._desc.empty()) {
debugPrintf("Unused Room Number\n");
return true;
}
_vm->_player->_roomNumber = newRoom;
_vm->_room->_function = FN_CLEAR1;
_vm->freeChar();
_vm->_converseMode = 0;
_vm->_scripts->_endFlag = true;
_vm->_scripts->_returnCode = 0;
return false;
}
default:
debugPrintf("Current scene is: %d\n", _vm->_player->_roomNumber);
debugPrintf("Usage: %s <scene number>\n", argv[0]);
return true;
}
}
bool Debugger::Cmd_Cheat(int argc, const char **argv) {
if (argc != 1) {
debugPrintf("Usage: %s\n", argv[0]);
debugPrintf("Switches on/off the cheat mode. Cheat mode:\n");
debugPrintf(" - [Amazon] Skips guard on boat\n");
debugPrintf(" - [MM] Allows travel to \"can't get there from here\" locations\n");
return true;
}
_vm->_cheatFl = !_vm->_cheatFl;
debugPrintf("Cheat is now %s\n", _vm->_cheatFl ? "ON" : "OFF");
return true;
}
bool Debugger::Cmd_PlayMovie(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Format: playmovie <movie-file>\n");
return true;
}
// play gets postponed until debugger is closed
Common::String filename = argv[1];
_playMovieFile = filename;
return cmdExit(0, nullptr);
}
bool Debugger::Cmd_DumpScript(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: %s <path>\n", argv[0]);
debugPrintf("Dumps the currently loaded script data to the given path\n");
return true;
}
Common::SeekableReadStream *data = _vm->_scripts->_data;
if (!data) {
debugPrintf("No script loaded\n");
return true;
}
const Common::Path outpath = Common::Path(argv[1]);
Common::DumpFile dumpFile;
dumpFile.open(outpath);
if (!dumpFile.isOpen()) {
debugPrintf("Couldn't open %s\n", argv[1]);
return true;
}
int64 oldpos = data->pos();
data->seek(0);
dumpFile.writeStream(data);
dumpFile.close();
data->seek(oldpos);
return true;
}
bool Debugger::Cmd_Flag(int argc, const char **argv) {
if (argc != 2 && argc != 3) {
debugPrintf("Usage: %s <flag number> [<flag value>]\n", argv[0]);
debugPrintf("Prints or sets the value of the given flag\n");
return true;
}
if (argc == 2) {
int flagNum = strToInt(argv[1]);
if (flagNum < 0 || flagNum >= 256) {
debugPrintf("Invalid flag number\n");
return true;
}
debugPrintf("Flag %d: %d\n", flagNum, _vm->_flags[flagNum]);
return true;
}
int num = strToInt(argv[1]);
if (num < 0 || num >= ARRAYSIZE(_vm->_flags)) {
debugPrintf("Invalid flag number\n");
return true;
}
int val = strToInt(argv[2]);
if (val < 0 || val >= 256) {
debugPrintf("Invalid flag val, must be byte\n");
return true;
}
_vm->_flags[num] = val;
debugPrintf("Flag %d set to %d\n", num, val);
return true;
}
bool Debugger::Cmd_Timers(int argc, const char **argv) {
if (argc != 1) {
debugPrintf("Usage: %s\n", argv[0]);
debugPrintf("Prints the current timers\n");
return true;
}
debugPrintf("Timers:\n");
for (uint i = 0; i < _vm->_timers.size(); ++i) {
const TimerEntry te = _vm->_timers[i];
debugPrintf("%d: init: %d timer: %d flag: %d\n", i, te._initTm, te._timer, te._flag);
}
return true;
}
bool Debugger::Cmd_Travel(int argc, const char **argv) {
if (argc != 1 && argc != 3) {
debugPrintf("Usage: %s [<travel number> <travel value>]\n", argv[0]);
debugPrintf("Dump the travel table, or set a travel table entry to the given value\n");
return true;
}
if (argc == 1) {
debugPrintf("Travel table:\n");
for (int i = 0; i < ARRAYSIZE(_vm->_travel); ++i) {
if (!Martian::TRAVDATA[i])
break;
debugPrintf("%2d: %d (%s)\n", i, _vm->_travel[i], Martian::TRAVDATA[i]);
}
return true;
}
int num = strToInt(argv[1]);
if (num < 0 || num >= ARRAYSIZE(_vm->_travel)) {
debugPrintf("Invalid travel number\n");
return true;
}
int val = strToInt(argv[2]);
if (val < 0 || val >= 256) {
debugPrintf("Invalid travel val, must be byte\n");
return true;
}
_vm->_flags[num] = val;
debugPrintf("Travel %d set to %d\n", num, val);
return true;
}
bool Debugger::Cmd_Ask(int argc, const char **argv) {
if (argc != 1 && argc != 3) {
debugPrintf("Usage: %s [<ask number> <ask value>]\n", argv[0]);
debugPrintf("Dump the ask table, or set an ask table entry to the given value\n");
return true;
}
if (argc == 1) {
debugPrintf("Ask table:\n");
for (int i = 0; i < ARRAYSIZE(_vm->_ask); ++i) {
if (!Martian::ASK_TBL[i])
break;
debugPrintf("%2d: %d (%s)\n", i, _vm->_ask[i], Martian::ASK_TBL[i]);
}
return true;
}
int num = strToInt(argv[1]);
if (num < 0 || num >= ARRAYSIZE(_vm->_ask)) {
debugPrintf("Invalid ask number\n");
return true;
}
int val = strToInt(argv[2]);
if (val < 0 || val >= 256) {
debugPrintf("Invalid ask val, must be byte\n");
return true;
}
_vm->_flags[num] = val;
debugPrintf("Ask %d set to %d\n", num, val);
return true;
}
bool Debugger::Cmd_Inventory(int argc, const char **argv) {
if (argc != 1 && argc != 3) {
debugPrintf("Usage: %s [<inv number> <state value>]\n", argv[0]);
debugPrintf("Dump the list of inventory items and their state, or set the state of an item\n");
return true;
}
static const char* STATE_NAMES[] = {
"0 - Not Found",
"1 - In Inv ",
"2 - Used ",
};
if (argc == 1) {
debugPrintf("Inventory items:\n");
for (int i = 0; i < (int)_vm->_inventory->_inv.size(); ++i) {
const InventoryEntry &entry = _vm->_inventory->_inv[i];
debugPrintf("%2d: %s %s\n", i, STATE_NAMES[entry._value], entry._name.c_str());
}
return true;
}
int num = strToInt(argv[1]);
if (num < 0 || num >= (int)_vm->_inventory->_inv.size()) {
debugPrintf("Invalid inv number\n");
return true;
}
int val = strToInt(argv[2]);
if (val < 0 || val > 2) {
debugPrintf("Invalid inv state val, must be 0/1/2\n");
return true;
}
_vm->_inventory->_inv[num]._value = val;
debugPrintf("Set item %d to %d\n", num, val);
return true;
}
bool Debugger::Cmd_Everything(int argc, const char **argv) {
if (argc != 2 || strcmp(argv[1], "please") != 0) {
debugPrintf("Usage: %s please\n", argv[0]);
debugPrintf("Gives you all items, travel locations, and ask subjects.\n");
debugPrintf("Cannot be undone and may break your game, so you have to confirm with 'please'.\n");
return true;
}
for (uint i = 0; i < _vm->_res->INVENTORY.size(); ++i)
_vm->_inventory->_inv[i]._value = ITEM_IN_INVENTORY;
for (uint i = 0; i < ARRAYSIZE(_vm->_travel); ++i)
_vm->_travel[i] = 1;
//
// Turn off known-broken/cut locations that exist in the travel table
// but you can't go there or going there directly will cause a crash.
//
const int INVALID_TRAVEL_LOCATIONS[] = {
10, // RESTAURANT
12, // LOVE SCENE
};
for (uint i = 0; i < ARRAYSIZE(INVALID_TRAVEL_LOCATIONS); ++i)
_vm->_travel[INVALID_TRAVEL_LOCATIONS[i]] = 0;
for (uint i = 0; i < ARRAYSIZE(_vm->_ask); ++i)
_vm->_ask[i] = 1;
debugPrintf("You now have everything, can go anywhere, and can ask anything.\n");
return true;
}
/*------------------------------------------------------------------------*/
namespace Amazon {
AmazonDebugger::AmazonDebugger(AccessEngine *vm) : Debugger(vm) {
registerCmd("chapter", WRAP_METHOD(AmazonDebugger, Cmd_StartChapter));
}
bool AmazonDebugger::Cmd_StartChapter(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: %s <chapter number>\n", argv[0]);
return true;
}
// Build up a simple one line script to start the given chapter
byte *chapterScript = (byte *)malloc(5);
if (!chapterScript)
error("malloc failed");
chapterScript[0] = SCRIPT_START_BYTE;
chapterScript[1] = ROOM_SCRIPT % 256;
chapterScript[2] = ROOM_SCRIPT / 256;
chapterScript[3] = 0x80 + 75; // cmdChapter
chapterScript[4] = strToInt(argv[1]); // chapter number
_vm->_scripts->setScript(new Resource(chapterScript, 5), true);
return false;
}
} // End of namespace Amazon
} // End of namespace Access

72
engines/access/debugger.h Normal file
View File

@@ -0,0 +1,72 @@
/* 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/>.
*
*/
#ifndef ACCESS_DEBUGGER_H
#define ACCESS_DEBUGGER_H
#include "common/scummsys.h"
#include "gui/debugger.h"
#include "access/amazon/amazon_resources.h"
#include "access/martian/martian_resources.h"
namespace Access {
class AccessEngine;
class Debugger : public GUI::Debugger {
protected:
AccessEngine *_vm;
Common::Path _playMovieFile;
bool Cmd_LoadScene(int argc, const char **argv);
bool Cmd_Cheat(int argc, const char **argv);
bool Cmd_PlayMovie(int argc, const char **argv);
bool Cmd_DumpScript(int argc, const char **argv);
bool Cmd_Timers(int argc, const char **argv);
bool Cmd_Flag(int argc, const char **argv);
bool Cmd_Travel(int argc, const char **argv);
bool Cmd_Ask(int argc, const char **argv);
bool Cmd_Inventory(int argc, const char **argv);
bool Cmd_Everything(int argc, const char **argv);
public:
static Debugger *init(AccessEngine *vm);
void postEnter() override;
public:
Debugger(AccessEngine *vm);
~Debugger() override;
};
namespace Amazon {
class AmazonDebugger : public Debugger {
protected:
bool Cmd_StartChapter(int argc, const char **argv);
public:
AmazonDebugger(AccessEngine *vm);
~AmazonDebugger() override {}
};
} // End of namespace Amazon
} // End of namespace Access
#endif /* ACCESS_DEBUGGER_H */

View File

@@ -0,0 +1,149 @@
/* 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/debug.h"
#include "common/endian.h"
#include "common/util.h"
#include "access/decompress.h"
namespace Access {
void LzwDecompressor::decompress(const byte *source, byte *dest) {
_source = source;
byte litByte = 0;
uint16 oldCode = 0;
uint16 copyLength, maxCodeValue, code, nextCode, lastCode;
byte *copyBuf = new byte[8192];
struct { uint16 code; byte value; } codeTable[8192];
memset(codeTable, 0, sizeof(codeTable));
_codeLength = 9;
nextCode = 258;
maxCodeValue = 512;
copyLength = 0;
_sourceBitsLeft = 8;
while (1) {
code = getCode();
if (code == 257)
break;
if (code == 256) {
_codeLength = 9;
nextCode = 258;
maxCodeValue = 512;
lastCode = getCode();
oldCode = lastCode;
litByte = lastCode;
*dest++ = litByte;
} else {
lastCode = code;
if (code >= nextCode) {
lastCode = oldCode;
copyBuf[copyLength++] = litByte;
}
while (lastCode > 255) {
copyBuf[copyLength++] = codeTable[lastCode].value;
lastCode = codeTable[lastCode].code;
}
litByte = lastCode;
copyBuf[copyLength++] = lastCode;
while (copyLength > 0)
*dest++ = copyBuf[--copyLength];
codeTable[nextCode].value = lastCode;
codeTable[nextCode].code = oldCode;
nextCode++;
oldCode = code;
if (nextCode >= maxCodeValue && _codeLength <= 12) {
_codeLength++;
maxCodeValue <<= 1;
}
}
}
delete[] copyBuf;
}
uint16 LzwDecompressor::getCode() {
const byte bitMasks[9] = {
0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0x0FF
};
byte resultBitsLeft = _codeLength;
byte resultBitsPos = 0;
uint16 result = 0;
byte currentByte = *_source;
byte currentBits = 0;
// Get bits of current byte
while (resultBitsLeft) {
if (resultBitsLeft < _sourceBitsLeft) {
// we need less than we have left
currentBits = (currentByte >> (8 - _sourceBitsLeft)) & bitMasks[resultBitsLeft];
result |= (currentBits << resultBitsPos);
_sourceBitsLeft -= resultBitsLeft;
resultBitsLeft = 0;
} else {
// we need as much as we have left or more
resultBitsLeft -= _sourceBitsLeft;
currentBits = currentByte >> (8 - _sourceBitsLeft);
result |= (currentBits << resultBitsPos);
resultBitsPos += _sourceBitsLeft;
// Go to next byte
_source++;
_sourceBitsLeft = 8;
if (resultBitsLeft) {
currentByte = *_source;
}
}
}
return result;
}
uint32 decompressDBE(const byte *source, byte **dest) {
uint32 destSize = READ_LE_UINT32(source + 4);
*dest = new byte[destSize];
debug(1, "decompressDBE() destSize = %d", destSize);
LzwDecompressor dec;
dec.decompress(source + 16, *dest);
debug(1, "decompressDBE() ok");
return destSize;
}
} // End of namespace Access

View File

@@ -0,0 +1,43 @@
/* 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/>.
*
*/
#ifndef ACCESS_DECOMPRESS_H
#define ACCESS_DECOMPRESS_H
#include "common/scummsys.h"
namespace Access {
class LzwDecompressor {
public:
void decompress(const byte *source, byte *dest);
private:
const byte *_source;
byte _sourceBitsLeft;
byte _codeLength;
uint16 getCode();
};
uint32 decompressDBE(const byte *source, byte **dest);
} // End of namespace Access
#endif

View File

@@ -0,0 +1,67 @@
/* 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 "base/plugins.h"
#include "engines/advancedDetector.h"
#include "access/detection.h"
#include "access/access.h"
static const PlainGameDescriptor AccessGames[] = {
{"amazon", "Amazon: Guardians of Eden"},
{"martian", "Martian Memorandum"},
{nullptr, nullptr}
};
static const DebugChannelDef debugFlagList[] = {
{Access::kDebugPath, "path", "Pathfinding debug level"},
{Access::kDebugScripts, "scripts", "Game scripts"},
{Access::kDebugGraphics, "graphics", "Graphics handling"},
{Access::kDebugSound, "sound", "Sound and Music handling"},
DEBUG_CHANNEL_END
};
#include "access/detection_tables.h"
class AccessMetaEngineDetection : public AdvancedMetaEngineDetection<Access::AccessGameDescription> {
public:
AccessMetaEngineDetection() : AdvancedMetaEngineDetection(Access::gameDescriptions, AccessGames) {
_maxScanDepth = 3;
}
const char *getName() const override {
return "access";
}
const char *getEngineName() const override {
return "Access";
}
const char *getOriginalCopyright() const override {
return "Access Engine (C) 1989-1994 Access Software";
}
const DebugChannelDef *getDebugChannels() const override {
return debugFlagList;
}
};
REGISTER_PLUGIN_STATIC(ACCESS_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, AccessMetaEngineDetection);

View File

@@ -0,0 +1,46 @@
/* 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/>.
*
*/
#ifndef ACCESS_DETECTION_H
#define ACCESS_DETECTION_H
#include "engines/advancedDetector.h"
namespace Access {
enum AccessGameType {
kGameAmazon = 1,
kGameMartianMemorandum = 2,
kGameNoctropolis = 3
};
struct AccessGameDescription {
AD_GAME_DESCRIPTION_HELPERS(desc);
ADGameDescription desc;
int gameID;
uint32 features;
};
} // End of namespace Access
#endif // ACCESS_DETECTION_H

View File

@@ -0,0 +1,136 @@
/* 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/>.
*
*/
namespace Access {
static const AccessGameDescription gameDescriptions[] = {
{
// Amazon Guardians of Eden - Floppy English
// 3.5" and 5.25" floppies provided by Strangerke had the same md5
// Except the sound file. The executable is also identical
{
"amazon",
0,
AD_ENTRY1s("c00.ap", "dcabf69d5a0d911168cb73511ebaead0", 331481),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NONE)
},
kGameAmazon,
0
},
{
// Amazon Guardians of Eden - Spanish
// Provided by dianiu in bug report #6958
{
"amazon",
0,
AD_ENTRY1s("c00.ap", "aeb429ff015596144c0df06886c84825", 303753),
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NONE)
},
kGameAmazon,
0
},
// Amazon Guardians of Eden - Demo English
{
{
"amazon",
"Demo",
AD_ENTRY1s("c25.ap", "5baba0c052d22157499bfa05cb1ed5b7", 65458),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GUIO_NONE)
},
kGameAmazon,
0
},
{
// Amazon: Guardians of Eden - CD English
{
"amazon",
"CD",
AD_ENTRY1s("checksum.crc", "bef85478132fec74cb5d9067f3a37d24", 8),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_CD,
GUIO1(GUIO_NONE)
},
kGameAmazon,
0
},
{
// Martian Memorandum
{
"martian",
nullptr,
AD_ENTRY1s("r01.ap", "c081daca9b0cfd710157cf946e343df6", 39352),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NONE)
},
kGameMartianMemorandum,
0
},
{
// Martian Memorandum from "Lasersoft Top Tracks Vol 1" CD
{
"martian",
nullptr,
AD_ENTRY1s("r01.ap", "1052fc0e5bd979ae2e18b03ed58fda8e", 39349),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NONE)
},
kGameMartianMemorandum,
0
},
{
// Martian Memorandum
{
"martian",
"Demo",
AD_ENTRY1s("r01.rm", "c2facf9c43047211289044ee39a2322a", 2313),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_DEMO | ADGF_UNSTABLE,
GUIO1(GUIO_NONE)
},
kGameMartianMemorandum,
0
},
{ AD_TABLE_END_MARKER, 0, 0 }
};
} // End of namespace Access

384
engines/access/events.cpp Normal file
View File

@@ -0,0 +1,384 @@
/* 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/scummsys.h"
#include "graphics/cursorman.h"
#include "common/events.h"
#include "common/endian.h"
#include "engines/util.h"
#include "access/access.h"
#include "access/events.h"
#include "access/player.h"
#include "access/amazon/amazon_resources.h"
#define CURSOR_WIDTH 16
#define CURSOR_HEIGHT 16
namespace Access {
EventsManager::EventsManager(AccessEngine *vm) : _vm(vm) {
_cursorId = CURSOR_NONE;
_normalMouse = CURSOR_CROSSHAIRS;
_frameCounter = 10;
_priorFrameTime = 0;
_leftButton = _rightButton = false;
_middleButton = false;
_wheelUp = _wheelDown = false;
_mouseCol = _mouseRow = 0;
_cursorExitFlag = false;
_vbCount = 0;
_keyCode = Common::KEYCODE_INVALID;
_priorTimerTime = 0;
_action = kActionNone;
}
EventsManager::~EventsManager() {
_invCursor.free();
}
void EventsManager::forceSetCursor(CursorType cursorId) {
setNormalCursor(cursorId);
setCursor(cursorId);
}
void EventsManager::setNormalCursor(CursorType cursorId) {
_normalMouse = cursorId;
}
void EventsManager::setCursor(CursorType cursorId) {
if (cursorId == _cursorId)
return;
_cursorId = cursorId;
if (cursorId == CURSOR_INVENTORY) {
// Set the cursor
CursorMan.replaceCursor(_invCursor, _invCursor.w / 2, _invCursor.h / 2, 0);
} else {
// Get a pointer to the mouse data to use, and get the cursor hotspot
const byte *srcP = _vm->_res->getCursor(cursorId);
int hotspotX = (int16)READ_LE_UINT16(srcP);
int hotspotY = (int16)READ_LE_UINT16(srcP + 2);
srcP += 4;
// Create a surface to build up the cursor on
Graphics::Surface cursorSurface;
cursorSurface.create(CURSOR_WIDTH, CURSOR_HEIGHT, Graphics::PixelFormat::createFormatCLUT8());
byte *destP = (byte *)cursorSurface.getPixels();
Common::fill(destP, destP + CURSOR_WIDTH * CURSOR_HEIGHT, 0);
// Loop to build up the cursor
for (int y = 0; y < CURSOR_HEIGHT; ++y) {
destP = (byte *)cursorSurface.getBasePtr(0, y);
int width = CURSOR_WIDTH;
int skip = *srcP++;
int plot = *srcP++;
if (skip >= width)
break;
// Skip over pixels
destP += skip;
width -= skip;
// Write out the pixels to plot
while (plot > 0 && width > 0) {
*destP++ = *srcP++;
--plot;
--width;
}
}
// Set the cursor
CursorMan.replaceCursor(cursorSurface, hotspotX, hotspotY, 0);
// Free the cursor surface
cursorSurface.free();
}
}
void EventsManager::setCursorData(Graphics::ManagedSurface *src, const Common::Rect &r) {
_invCursor.create(r.width(), r.height(), Graphics::PixelFormat::createFormatCLUT8());
_invCursor.copyRectToSurface(*src, 0, 0, r);
}
void EventsManager::showCursor() {
CursorMan.showMouse(true);
}
void EventsManager::hideCursor() {
CursorMan.showMouse(false);
}
bool EventsManager::isCursorVisible() {
return CursorMan.isVisible();
}
void EventsManager::delayUntilNextFrame() {
while (!checkForNextFrameCounter())
delay();
nextFrame();
}
void EventsManager::pollEvents(bool skipTimers) {
if (checkForNextFrameCounter()) {
nextFrame();
}
if (checkForNextTimerUpdate() && !skipTimers)
nextTimer();
_vm->_sound->checkSoundQueue();
_wheelUp = _wheelDown = false;
Common::Event event;
while (g_system->getEventManager()->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_QUIT:
case Common::EVENT_RETURN_TO_LAUNCHER:
return;
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
actionControl(event.customType, true);
return;
case Common::EVENT_CUSTOM_ENGINE_ACTION_END:
actionControl(event.customType, false);
return;
case Common::EVENT_KEYDOWN:
// Check for debugger
keyControl(event.kbd.keycode, true);
return;
case Common::EVENT_KEYUP:
keyControl(event.kbd.keycode, false);
return;
case Common::EVENT_MOUSEMOVE:
_mousePos = event.mouse;
_mouseCol = _mousePos.x / 8;
_mouseRow = _mousePos.y / 8;
break;
case Common::EVENT_LBUTTONDOWN:
_leftButton = true;
return;
case Common::EVENT_LBUTTONUP:
_leftButton = false;
return;
case Common::EVENT_RBUTTONDOWN:
_rightButton = true;
return;
case Common::EVENT_RBUTTONUP:
_rightButton = false;
return;
case Common::EVENT_MBUTTONDOWN:
_middleButton = true;
return;
case Common::EVENT_MBUTTONUP:
_middleButton = false;
return;
case Common::EVENT_WHEELUP:
_wheelUp = true;
return;
case Common::EVENT_WHEELDOWN:
_wheelDown = true;
return;
default:
break;
}
}
}
void EventsManager::keyControl(Common::KeyCode keycode, bool isKeyDown) {
Player &player = *_vm->_player;
if (!isKeyDown) {
if (player._move != NONE) {
_keyCode = Common::KEYCODE_INVALID;
player._move = NONE;
}
return;
}
_keyCode = keycode;
}
void EventsManager::actionControl(Common::CustomEventType action, bool isKeyDown) {
Player &player = *_vm->_player;
if (!isKeyDown) {
if (player._move != NONE) {
_action = kActionNone;
player._move = NONE;
}
return;
}
_action = action;
switch (action) {
case kActionMoveUp:
player._move = UP;
break;
case kActionMoveDown:
player._move = DOWN;
break;
case kActionMoveLeft:
player._move = LEFT;
break;
case kActionMoveRight:
player._move = RIGHT;
break;
case kActionMoveUpLeft:
player._move = UPLEFT;
break;
case kActionMoveUpRight:
player._move = UPRIGHT;
break;
case kActionMoveDownLeft:
player._move = DOWNLEFT;
break;
case kActionMoveDownRight:
player._move = DOWNRIGHT;
break;
default:
break;
}
}
void EventsManager::pollEventsAndWait() {
pollEvents();
delay();
}
bool EventsManager::checkForNextFrameCounter() {
// Check for next game frame
uint32 milli = g_system->getMillis();
if ((milli - _priorFrameTime) >= GAME_FRAME_TIME) {
--_vbCount;
++_frameCounter;
_priorFrameTime = milli;
return true;
}
return false;
}
bool EventsManager::checkForNextTimerUpdate() {
// Check for next timer update
uint32 milli = g_system->getMillis();
if ((milli - _priorTimerTime) >= GAME_TIMER_TIME) {
_priorTimerTime = milli;
return true;
}
return false;
}
void EventsManager::nextFrame() {
_vm->_screen->update();
}
void EventsManager::nextTimer() {
_vm->_animation->updateTimers();
_vm->_timers.updateTimers();
}
void EventsManager::delay(int time) {
g_system->delayMillis(time);
}
void EventsManager::zeroKeysActions() {
_keyCode = Common::KEYCODE_INVALID;
_action = kActionNone;
}
bool EventsManager::getAction(Common::CustomEventType &action) {
if (_action == kActionNone) {
return false;
} else {
action = _action;
_action = kActionNone;
return true;
}
}
bool EventsManager::isKeyActionPending() const {
return (_keyCode != Common::KEYCODE_INVALID || _action != kActionNone);
}
void EventsManager::debounceLeft() {
while (_leftButton && !_vm->shouldQuit()) {
pollEventsAndWait();
}
}
void EventsManager::clearEvents() {
_leftButton = _rightButton = false;
zeroKeysActions();
}
void EventsManager::waitKeyActionMouse() {
while (!_vm->shouldQuit() && !isKeyActionMousePressed()) {
pollEvents(true);
delay();
}
}
Common::Point EventsManager::calcRawMouse() {
Common::Point pt;
Screen &screen = *_vm->_screen;
pt.x = _mousePos.x - screen._windowXAdd +
(_vm->_scrollCol * TILE_WIDTH) + _vm->_scrollX;
pt.y = _mousePos.y - screen._screenYOff - screen._windowYAdd +
(_vm->_scrollRow * TILE_HEIGHT) + _vm->_scrollY;
return pt;
}
int EventsManager::checkMouseBox1(const Common::Array<Common::Rect> &rects) {
for (uint16 i = 0; i < rects.size(); ++i) {
if (rects[i].left == -1)
return -1;
if ((_mousePos.x > rects[i].left) && (_mousePos.x < rects[i].right)
&& (_mousePos.y > rects[i].top) && (_mousePos.y < rects[i].bottom))
return i;
}
return -1;
}
bool EventsManager::isKeyActionMousePressed() {
bool result = _leftButton || _rightButton || isKeyActionPending();
debounceLeft();
zeroKeysActions();
return result;
}
void EventsManager::centerMousePos() {
_mousePos = Common::Point(160, 100);
}
void EventsManager::restrictMouse() {
// No implementation in ScummVM
}
} // End of namespace Access

164
engines/access/events.h Normal file
View File

@@ -0,0 +1,164 @@
/* 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/>.
*
*/
#ifndef ACCESS_EVENTS_H
#define ACCESS_EVENTS_H
#include "common/scummsys.h"
#include "common/events.h"
#include "common/stack.h"
namespace Access {
enum CursorType {
CURSOR_NONE = -1,
CURSOR_ARROW = 0, CURSOR_CROSSHAIRS, CURSOR_2, CURSOR_3, CURSOR_LOOK,
CURSOR_USE, CURSOR_TAKE, CURSOR_CLIMB, CURSOR_TALK, CURSOR_HELP,
CURSOR_INVENTORY = 99
};
#define GAME_FRAME_RATE 100
#define GAME_FRAME_TIME (1000 / GAME_FRAME_RATE)
#define GAME_TIMER_TIME 15
class AccessEngine;
class EventsManager {
private:
AccessEngine *_vm;
uint32 _frameCounter;
uint32 _priorFrameTime;
uint32 _priorTimerTime;
Common::KeyCode _keyCode;
Common::CustomEventType _action;
Graphics::Surface _invCursor;
bool checkForNextFrameCounter();
bool checkForNextTimerUpdate();
void nextFrame();
void nextTimer();
void keyControl(Common::KeyCode keycode, bool isKeyDown);
void actionControl(Common::CustomEventType action, bool isKeyDown);
public:
CursorType _cursorId;
CursorType _normalMouse;
bool _leftButton, _rightButton;
bool _middleButton;
bool _wheelUp, _wheelDown;
Common::Point _mousePos;
int _mouseCol, _mouseRow;
bool _cursorExitFlag;
int _vbCount;
public:
/**
* Constructor
*/
EventsManager(AccessEngine *vm);
/**
* Destructor
*/
~EventsManager();
/**
* Return frame counter
*/
uint32 getFrameCounter() const { return _frameCounter; }
/**
* Sets the cursor and reset the normal cursor
*/
void forceSetCursor(CursorType cursorId);
/**
* Sets the normal cursor
*/
void setNormalCursor(CursorType cursorId);
/**
* Sets the cursor
*/
void setCursor(CursorType cursorId);
/**
* Set the image for the inventory cursor
*/
void setCursorData(Graphics::ManagedSurface *src, const Common::Rect &r);
/**
* Return the current cursor Id
*/
CursorType getCursor() const { return _cursorId; }
/**
* Show the mouse cursor
*/
void showCursor();
/**
* Hide the mouse cursor
*/
void hideCursor();
/**
* Returns if the mouse cursor is visible
*/
bool isCursorVisible();
void pollEvents(bool skipTimers = false);
void pollEventsAndWait();
void zeroKeysActions();
bool getAction(Common::CustomEventType &action);
Common::CustomEventType peekAction() const { return _action; }
Common::KeyCode peekKeyCode() const { return _keyCode; }
bool isKeyActionPending() const;
void delay(int time = 5);
void delayUntilNextFrame();
void debounceLeft();
void clearEvents();
void waitKeyActionMouse();
const Common::Point &getMousePos() const { return _mousePos; }
Common::Point calcRawMouse();
int checkMouseBox1(const Common::Array<Common::Rect> &rects);
bool isKeyActionMousePressed();
void centerMousePos();
void restrictMouse();
};
} // End of namespace Access
#endif /* ACCESS_EVENTS_H */

228
engines/access/files.cpp Normal file
View File

@@ -0,0 +1,228 @@
/* 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/substream.h"
#include "access/files.h"
#include "access/amazon/amazon_resources.h"
#include "access/martian/martian_resources.h"
#include "access/access.h"
namespace Access {
FileIdent::FileIdent() {
_fileNum = -1;
_subfile = 0;
}
void FileIdent::load(Common::SeekableReadStream &s) {
_fileNum = s.readSint16LE();
_subfile = s.readUint16LE();
}
/*------------------------------------------------------------------------*/
CellIdent:: CellIdent() {
_cell = 0;
}
CellIdent::CellIdent(int cell, int fileNum, int subfile) {
_cell = cell;
_fileNum = fileNum;
_subfile = subfile;
}
/*------------------------------------------------------------------------*/
Resource::Resource() {
_stream = nullptr;
_size = 0;
_data = nullptr;
}
Resource::~Resource() {
delete[] _data;
delete _stream;
}
Resource::Resource(byte *p, int size) {
_data = p;
_size = size;
_stream = new Common::MemoryReadStream(p, size);
}
byte *Resource::data() {
if (_data == nullptr) {
_data = new byte[_size];
int pos = _stream->pos();
_stream->seek(0);
_stream->read(_data, _size);
_stream->seek(pos);
}
return _data;
}
const char *Resource::getFileName() const {
return _file.getName();
}
/*------------------------------------------------------------------------*/
FileManager::FileManager(AccessEngine *vm) : _vm(vm) {
_fileNumber = -1;
_setPaletteFlag = true;
}
FileManager::~FileManager() {
}
Resource *FileManager::loadFile(int fileNum, int subfile) {
Resource *res = new Resource();
setAppended(res, fileNum);
gotoAppended(res, subfile);
handleFile(res);
return res;
}
Resource *FileManager::loadFile(const FileIdent &fileIdent) {
return loadFile(fileIdent._fileNum, fileIdent._subfile);
}
Resource *FileManager::loadFile(const Common::Path &filename) {
Resource *res = new Resource();
// Open the file
openFile(res, filename);
// Set up stream for the entire file
res->_size = res->_file.size();
res->_stream = res->_file.readStream(res->_size);
handleFile(res);
return res;
}
bool FileManager::existFile(const Common::Path &filename) {
return Common::File::exists(filename);
}
void FileManager::openFile(Resource *res, const Common::Path &filename) {
// Open up the file
_fileNumber = -1;
if (!res->_file.open(filename))
error("Could not open file - %s", filename.toString().c_str());
}
void FileManager::loadScreen(Graphics::ManagedSurface *dest, int fileNum, int subfile) {
Resource *res = loadFile(fileNum, subfile);
handleScreen(dest, res);
delete res;
}
void FileManager::handleScreen(Graphics::ManagedSurface *dest, Resource *res) {
_vm->_screen->loadRawPalette(res->_stream);
if (_setPaletteFlag)
_vm->_screen->setPalette();
_setPaletteFlag = true;
// The remainder of the file after the palette may be separately compressed,
// so call handleFile to handle it if it is
res->_size -= res->_stream->pos();
handleFile(res);
Graphics::Surface destSurface = dest->getSubArea(Common::Rect(0, 0,
_vm->_screen->w, _vm->_screen->h));
if (destSurface.w == destSurface.pitch) {
res->_stream->read((byte *)destSurface.getPixels(), destSurface.w * destSurface.h);
} else {
for (int y = 0; y < destSurface.h; ++y) {
byte *pDest = (byte *)destSurface.getBasePtr(0, y);
res->_stream->read(pDest, destSurface.w);
}
}
}
void FileManager::loadScreen(int fileNum, int subfile) {
loadScreen(_vm->_screen, fileNum, subfile);
}
void FileManager::loadScreen(const Common::Path &filename) {
Resource *res = loadFile(filename);
handleScreen(_vm->_screen, res);
delete res;
}
void FileManager::handleFile(Resource *res) {
char header[3];
res->_stream->read(&header[0], 3);
res->_stream->seek(-3, SEEK_CUR);
bool isCompressed = !strncmp(header, "DBE", 3);
// If the data is compressed, uncompress it and replace the stream
// in the resource with the decompressed one
if (isCompressed) {
// Read in the entire compressed data
byte *src = new byte[res->_size];
res->_stream->read(src, res->_size);
// Decompress the data
res->_size = decompressDBE(src, &res->_data);
// Replace the default resource stream with a stream for the decompressed data
delete res->_stream;
res->_file.close();
res->_stream = new Common::MemoryReadStream(res->_data, res->_size);
delete[] src;
}
}
void FileManager::setAppended(Resource *res, int fileNum) {
// Open the file for access
if (!res->_file.open(_vm->_res->FILENAMES[fileNum]))
error("Could not open file %s", _vm->_res->FILENAMES[fileNum].toString().c_str());
// If a different file has been opened then previously, load its index
if (_fileNumber != fileNum) {
_fileNumber = fileNum;
// Read in the file index
int count = res->_file.readUint16LE();
assert(count <= 100);
_fileIndex.resize(count);
for (int i = 0; i < count; ++i)
_fileIndex[i] = res->_file.readUint32LE();
}
}
void FileManager::gotoAppended(Resource *res, int subfile) {
uint32 offset = _fileIndex[subfile];
uint32 size = (subfile == (int)_fileIndex.size() - 1) ? res->_file.size() - offset :
_fileIndex[subfile + 1] - offset;
res->_size = size;
res->_stream = new Common::SeekableSubReadStream(&res->_file, offset, offset + size);
}
} // End of namespace Access

142
engines/access/files.h Normal file
View File

@@ -0,0 +1,142 @@
/* 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/>.
*
*/
#ifndef ACCESS_FILES_H
#define ACCESS_FILES_H
#include "common/scummsys.h"
#include "common/array.h"
#include "common/file.h"
#include "graphics/managed_surface.h"
#include "access/decompress.h"
namespace Access {
class AccessEngine;
struct FileIdent {
int _fileNum;
int _subfile;
FileIdent();
FileIdent(int fileNum, int subfile) { _fileNum = fileNum; _subfile = subfile; }
void load(Common::SeekableReadStream &s);
};
struct CellIdent : FileIdent {
byte _cell;
CellIdent();
CellIdent(int cell, int fileNum, int subfile);
};
class FileManager;
class Resource {
friend class FileManager;
private:
Common::File _file;
byte *_data;
public:
Common::SeekableReadStream *_stream;
int _size;
Resource();
Resource(byte *data, int size);
~Resource();
byte *data();
const char *getFileName() const;
};
class FileManager {
private:
AccessEngine *_vm;
void openFile(Resource *res, const Common::Path &filename);
/**
* Handles setting up the resource with a stream for the located resource
*/
void handleFile(Resource *res);
/**
* Handles loading a screen surface and palette with decoded resource
*/
void handleScreen(Graphics::ManagedSurface *dest, Resource *res);
/**
* Open up a sub-file container file
*/
void setAppended(Resource *file, int fileNum);
/**
* Open up a sub-file resource within an alrady opened container file.
*/
void gotoAppended(Resource *file, int subfile);
public:
int _fileNumber;
Common::Array<uint32> _fileIndex;
bool _setPaletteFlag;
public:
FileManager(AccessEngine *vm);
~FileManager();
/**
* Check the existence of a given file
*/
bool existFile(const Common::Path &filename);
/**
* Load a given subfile from a container file
*/
Resource *loadFile(int fileNum, int subfile);
/**
* Loads a resource specified by a file identifier
*/
Resource *loadFile(const FileIdent &fileIdent);
/**
* Load a given file by name
*/
Resource *loadFile(const Common::Path &filename);
/**
* Load a given scren from a container file
*/
void loadScreen(int fileNum, int subfile);
/**
* Load a given screen by name
*/
void loadScreen(const Common::Path &filename);
/**
* Load a screen resource onto a designated surface
*/
void loadScreen(Graphics::ManagedSurface *dest, int fileNum, int subfile);
};
} // End of namespace Access
#endif /* ACCESS_FILES_H */

266
engines/access/font.cpp Normal file
View File

@@ -0,0 +1,266 @@
/* 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 "access/font.h"
namespace Access {
byte Font::_fontColors[4];
Font::Font(byte firstCharIndex) : _firstCharIndex(firstCharIndex), _bitWidth(0), _height(0) {
}
Font::~Font() {
for (auto &fontChar : _chars)
fontChar.free();
}
int Font::charWidth(char c) const {
if (c < _firstCharIndex)
return 0;
return _chars[c - _firstCharIndex].w;
}
int Font::stringWidth(const Common::String &msg) const {
int total = 0;
for (const char *c = msg.c_str(); *c != '\0'; ++c)
total += charWidth(*c);
return total;
}
bool Font::getLine(Common::String &s, int maxWidth, Common::String &line, int &width,
LINE_WIDTH_TYPE widthType) const {
assert(maxWidth > 0);
width = 0;
const char *src = s.c_str();
char c;
while ((c = *src) != '\0') {
if (c == '\r') {
// End of line, so return calculated line
line = Common::String(s.c_str(), src);
s = Common::String(src + 1);
return false;
}
++src;
width += (widthType == kWidthInPixels ? charWidth(c) : 1);
if (width < maxWidth)
continue;
// Reached maximum allowed size
// If this was the last character of the string, let it go
if (*src == '\0') {
line = Common::String(s.c_str(), src);
s.clear();
return true;
}
// Work backwards to find space at the start of the current word
// as a point to split the line on
while (src >= s.c_str() && *src != ' ') {
width -= (widthType == kWidthInPixels ? charWidth(*src) : 1);
--src;
}
if (src < s.c_str())
error("Could not fit line");
// Split the line around the space
line = Common::String(s.c_str(), src);
s = Common::String(src + 1);
return false;
}
// Return entire string
line = s;
s.clear();
return true;
}
void Font::drawString(BaseSurface *s, const Common::String &msg, const Common::Point &pt) const {
Common::Point currPt = pt;
const char *msgP = msg.c_str();
while (*msgP) {
currPt.x += drawChar(s, *msgP, currPt);
++msgP;
}
}
int Font::drawChar(BaseSurface *s, char c, Common::Point &pt) const {
const Graphics::Surface &ch = _chars[c - _firstCharIndex];
Graphics::Surface dest = s->getSubArea(Common::Rect(pt.x, pt.y, pt.x + ch.w, pt.y + ch.h));
// Loop through the lines of the character
for (int y = 0; y < ch.h; ++y) {
const byte *pSrc = (const byte *)ch.getBasePtr(0, y);
byte *pDest = (byte *)dest.getBasePtr(0, y);
// Loop through the horizontal pixels of the line
for (int x = 0; x < ch.w; ++x, ++pSrc, ++pDest) {
if (*pSrc != 0)
*pDest = _fontColors[*pSrc];
}
}
return ch.w;
}
/*------------------------------------------------------------------------*/
void AmazonFont::load(const int *fontIndex, const byte *fontData) {
assert(_chars.size() == 0);
int count = fontIndex[0];
_bitWidth = fontIndex[1];
_height = fontIndex[2];
_chars.resize(count);
for (int i = 0; i < count; ++i) {
const byte *pData = fontData + fontIndex[i + 3];
_chars[i].create(*pData++, _height, Graphics::PixelFormat::createFormatCLUT8());
for (int y = 0; y < _height; ++y) {
int bitsLeft = 0;
byte srcByte = 0;
byte pixel;
byte *pDest = (byte *)_chars[i].getBasePtr(0, y);
for (int x = 0; x < _chars[i].w; ++x, ++pDest) {
// Get the pixel
pixel = 0;
for (int pixelCtr = 0; pixelCtr < _bitWidth; ++pixelCtr, --bitsLeft) {
// No bits in current byte left, so get next byte
if (bitsLeft == 0) {
bitsLeft = 8;
srcByte = *pData++;
}
pixel = (pixel << 1) | (srcByte >> 7);
srcByte <<= 1;
}
// Write out the pixel
*pDest = pixel;
}
}
}
}
/*------------------------------------------------------------------------*/
MartianFont::MartianFont(int height, Common::SeekableReadStream &s) : Font(0) {
_height = height;
loadFromStream(s);
}
MartianFont::MartianFont(int height, size_t count, const byte *widths, const int *offsets, const byte *data) : Font(0) {
_height = height;
loadFromData(count, widths, offsets, data);
}
void MartianFont::loadFromStream(Common::SeekableReadStream &s) {
// Get the number of characters and the size of the raw font data
size_t count = s.readUint16LE();
size_t dataSize = s.readUint16LE();
assert(count < 256);
// Get the character widths
Common::Array<byte> widths;
widths.resize(count);
s.read(&widths[0], count);
// Get the character offsets
Common::Array<int> offsets;
offsets.resize(count);
for (size_t idx = 0; idx < count; ++idx)
offsets[idx] = s.readUint16LE();
// Get the raw character data
Common::Array<byte> data;
data.resize(dataSize);
s.read(&data[0], dataSize);
loadFromData(count, widths.data(), offsets.data(), data.data());
}
void MartianFont::loadFromData(size_t count, const byte *widths, const int *offsets, const byte *data) {
// Iterate through decoding each character
_chars.resize(count);
for (size_t idx = 0; idx < count; ++idx) {
Graphics::Surface &surface = _chars[idx];
surface.create(widths[idx], _height, Graphics::PixelFormat::createFormatCLUT8());
const byte *srcP = &data[offsets[idx]];
int x1, y1, x2, y2;
// Write horizontal lines
while ((x1 = *srcP++) != 0xff) {
x2 = *srcP++;
y1 = *srcP++;
surface.hLine(x1, y1, x2, 3);
}
// Write vertical lines
while ((x1 = *srcP++) != 0xff) {
y1 = *srcP++;
y2 = *srcP++;
surface.vLine(x1, y1, y2, 3);
}
}
}
/*------------------------------------------------------------------------*/
MartianBitFont::MartianBitFont(size_t count, const byte *data) : Font(0x20) {
_height = 8;
_chars.resize(count);
for (size_t i = 0; i < count; i++) {
Graphics::Surface &surface = _chars[i];
surface.create(8, _height, Graphics::PixelFormat::createFormatCLUT8());
for (int y = 0; y < _height; y++) {
byte src = data[i * 8 + y];
byte *dst = static_cast<byte *>(surface.getBasePtr(0, y));
for (int x = 7; x >= 0; x--) {
dst[x] = (src & 1);
src >>= 1;
}
}
}
}
/*------------------------------------------------------------------------*/
FontManager::FontManager() : _font1(nullptr), _font2(nullptr), _bitFont(nullptr) {
_printMaxX = 0;
Common::fill(&Font::_fontColors[0], &Font::_fontColors[4], 0);
}
void FontManager::load(Font *font1, Font *font2, Font *bitFont) {
_font1 = font1;
_font2 = font2;
_bitFont = bitFont;
}
} // End of namespace Access

166
engines/access/font.h Normal file
View File

@@ -0,0 +1,166 @@
/* 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/>.
*
*/
#ifndef ACCESS_FONT_H
#define ACCESS_FONT_H
#include "common/scummsys.h"
#include "common/array.h"
#include "common/rect.h"
#include "access/asurface.h"
#include "access/data.h"
namespace Access {
struct FontVal {
public:
int _lo, _hi;
FontVal() { _lo = _hi = 0; }
};
class Font {
protected:
byte _firstCharIndex;
int _bitWidth;
int _height;
Common::Array<Graphics::Surface> _chars;
protected:
/**
* Constructor
*/
Font(byte firstCharIndex);
public:
static byte _fontColors[4];
public:
/**
* Destructor
*/
virtual ~Font();
/**
* Get the width of a given character
*/
int charWidth(char c) const;
/**
* Get the width of a given string
*/
int stringWidth(const Common::String &msg) const;
/**
* Type of line wrapping - Martian wraps based on chars, Amazon based on px.
*
* Since the fonts are variable width we need to support both types to
* exactly wrap like the originals.
*/
enum LINE_WIDTH_TYPE {
kWidthInPixels,
kWidthInChars
};
/**
* Get a partial string that will fit in a given width
* @param s Source string. Modified to remove line
* @param maxWidth Maximum width allowed in px or chars (see widthType)
* @param line Output line
* @param width Actual width of returned line in selected units
* @param widthType Select the type of width constraint - px or chars
* @returns True if last line
*/
bool getLine(Common::String &s, int maxWidth, Common::String &line, int &width,
LINE_WIDTH_TYPE widthType = kWidthInPixels) const;
/**
* Draw a string on a given surface
*/
void drawString(BaseSurface *s, const Common::String &msg, const Common::Point &pt) const;
/**
* Draw a character on a given surface
*/
int drawChar(BaseSurface *s, char c, Common::Point &pt) const;
};
class AmazonFont : public Font {
private:
/**
* Load the given font data
*/
void load(const int *fontIndex, const byte *fontData);
public:
/**
* Constructor
*/
AmazonFont(const int *fontIndex, const byte *fontData) : Font(32) {
load(fontIndex, fontData);
}
};
class MartianFont : public Font {
private:
/**
* Load the given font data
*/
void loadFromStream(Common::SeekableReadStream &s);
void loadFromData(size_t count, const byte *widths, const int *offsets, const byte *data);
public:
/**
* Constructor
*/
MartianFont(int height, Common::SeekableReadStream &s);
MartianFont(int height, size_t count, const byte *widths, const int *offsets, const byte *data);
};
class MartianBitFont : public Font {
public:
/**
* Constructor
*/
MartianBitFont(size_t count, const byte *data);
};
class FontManager {
public:
FontVal _charSet;
FontVal _charFor;
int _printMaxX;
Font *_font1;
Font *_font2;
Font *_bitFont;
public:
/**
* Constructor
*/
FontManager();
/**
* Set the fonts
*/
void load(Font *font1, Font *font2, Font *bitFont);
};
} // End of namespace Access
#endif /* ACCESS_FONT_H */

View File

@@ -0,0 +1,572 @@
/* 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 "access/inventory.h"
#include "access/access.h"
#include "access/resources.h"
#include "access/amazon/amazon_resources.h"
#include "access/martian/martian_resources.h"
namespace Access {
void InventoryEntry::load(const Common::String &name, const int *data) {
_value = ITEM_NOT_FOUND;
_name = name;
if (data) {
_otherItem1 = *data++;
_newItem1 = *data++;
_otherItem2 = *data++;
_newItem2 = *data;
} else {
_otherItem1 = -1;
_newItem1 = -1;
_otherItem2 = -1;
_newItem2 = -1;
}
}
int InventoryEntry::checkItem(int itemId) const {
if (_otherItem1 == itemId)
return _newItem1;
else if (_otherItem2 == itemId)
return _newItem2;
else
return -1;
}
/*------------------------------------------------------------------------*/
InventoryManager::SavedFields::SavedFields() {
_vWindowHeight = _vWindowLinesTall = _vWindowWidth = _vWindowBytesWide = 0;
_playFieldHeight = _playFieldWidth = 0;
_windowXAdd = _windowYAdd = 0;
_screenYOff = 0;
_scrollX = _scrollY = 0;
_clipWidth = _clipHeight = 0;
_scrollCol = _scrollRow = 0;
}
/*------------------------------------------------------------------------*/
InventoryManager::InventoryManager(AccessEngine *vm) : Manager(vm) {
_startInvItem = 0;
_startInvBox = 0;
_invChangeFlag = true;
_invRefreshFlag = false;
_invModeFlag = false;
_startAboutItem = 0;
_startTravelItem = 0;
_iconDisplayFlag = true;
_boxNum = 0;
_inv.resize(_vm->_res->INVENTORY.size());
for (uint idx = 0; idx < _inv.size(); ++idx)
_inv[idx].load(_vm->_res->INVENTORY[idx]._desc, _vm->_res->INVENTORY[idx]._combo);
for (uint i = 0; i < 26; ++i) {
const int *r = INVCOORDS[i];
_invCoords.push_back(Common::Rect(r[0], r[2], r[1], r[3]));
}
}
int &InventoryManager::operator[](int idx) {
// WORKAROUND: At least in Amazon, some game scripts accidentally do reads
// beyond the length of the inventory array
static int invalid = 0;
return (idx >= (int)_inv.size()) ? invalid : _inv[idx]._value;
}
int InventoryManager::useItem() {
return _vm->_useItem;
}
void InventoryManager::setUseItem(int itemId) {
_vm->_useItem = itemId;
}
void InventoryManager::refreshInventory() {
// The original version was using pre-rendering for the inventory to spare some time.
// This is not needed on modern hardware, and it breaks a couple of things.
// Therefore it was removed in order to keep the same logic than for the CD version
// if (_vm->_screen->_vesaMode) {
// _invRefreshFlag = true;
// newDisplayInv();
// }
}
int InventoryManager::newDisplayInv() {
Screen &screen = *_vm->_screen;
EventsManager &events = *_vm->_events;
Room &room = *_vm->_room;
FileManager &files = *_vm->_files;
_invModeFlag = true;
_vm->_timers.saveTimers();
if (!room._tile && !_invRefreshFlag) {
saveScreens();
}
savedFields();
screen.setPanel(1);
events._cursorExitFlag = false;
getList();
initFields();
files._setPaletteFlag = false;
files.loadScreen(&_vm->_buffer1, 99, 0);
_vm->_buffer1.copyTo(&_vm->_buffer2);
_vm->copyBF2Vid();
// Set cells
Common::Array<CellIdent> cells;
cells.push_back(CellIdent(99, 99, 1));
_vm->loadCells(cells);
showAllItems();
if (!_invRefreshFlag) {
chooseItem();
if (_vm->_useItem != -1) {
int savedScale = _vm->_scale;
_vm->_scale = 153;
_vm->_screen->setScaleTable(_vm->_scale);
_vm->_buffer1.clearBuffer();
SpriteResource *spr = _vm->_objectsTable[99];
const SpriteFrame *frame = spr->getFrame(_vm->_useItem);
int w = screen._scaleTable1[46];
int h = screen._scaleTable1[35];
_vm->_buffer1.sPlotF(frame, Common::Rect(0, 0, w, h));
events.setCursorData(&_vm->_buffer1, Common::Rect(0, 0, w, h));
_vm->_scale = savedScale;
screen.setScaleTable(_vm->_scale);
}
}
freeInvCells();
screen.setPanel(0);
events.debounceLeft();
restoreFields();
screen.restorePalette();
// The original was testing the vesa mode too.
// We removed this check as we don't use pre-rendering
if (!_invRefreshFlag) {
screen.clearScreen();
screen.setPalette();
}
if (!room._tile && !_invRefreshFlag) {
restoreScreens();
} else {
screen.setBufferScan();
room.buildScreen();
// The original was doing a check on the vesa mode at this point.
// We don't need it as we don't do inventory pre-rendering
screen.fadeOut();
_vm->copyBF2Vid();
}
events._cursorExitFlag = false;
screen._screenChangeFlag = false;
_invModeFlag = false;
events.debounceLeft();
_vm->_timers.restoreTimers();
_vm->_startup = 1;
int result = 0;
if (!_invRefreshFlag) {
if (_vm->_useItem == -1) {
result = 2;
events.forceSetCursor(CURSOR_CROSSHAIRS);
} else
events.forceSetCursor(CURSOR_INVENTORY);
}
_invRefreshFlag = false;
_invChangeFlag = false;
return result;
}
int InventoryManager::displayInv() {
size_t invSize = _vm->_res->INVENTORY.size();
Common::Array<byte> invFlags(invSize + 1);
Common::Array<const char *> invNames(invSize + 1);
// Only show items that are in the inventory, skip "used".
for (size_t i = 0; i < invSize; i++) {
byte flag = (_inv[i]._value == ITEM_IN_INVENTORY) ? 1 : 0;
invFlags[i] = flag;
invNames[i] = _inv[i]._name.c_str();
}
_vm->_events->forceSetCursor(CURSOR_CROSSHAIRS);
_vm->_invBox->getList(invNames.data(), invFlags.data());
int btnSelected = 0;
int boxX = _vm->_invBox->doBox_v1(_startInvItem, _startInvBox, btnSelected);
_startInvItem = _vm->_boxDataStart;
_startInvBox = _vm->_boxSelectY;
if (boxX == -1)
btnSelected = 2;
if (btnSelected != 2)
_vm->_useItem = _vm->_invBox->_tempListIdx[boxX];
else
_vm->_useItem = -1;
return 0;
}
void InventoryManager::savedFields() {
Screen &screen = *_vm->_screen;
Room &room = *_vm->_room;
_fields._vWindowHeight = screen._vWindowHeight;
_fields._vWindowLinesTall = screen._vWindowLinesTall;
_fields._vWindowWidth = screen._vWindowWidth;
_fields._vWindowBytesWide = screen._vWindowBytesWide;
_fields._playFieldHeight = room._playFieldHeight;
_fields._playFieldWidth = room._playFieldWidth;
_fields._windowXAdd = screen._windowXAdd;
_fields._windowYAdd = screen._windowYAdd;
_fields._screenYOff = screen._screenYOff;
_fields._scrollX = _vm->_scrollX;
_fields._scrollY = _vm->_scrollY;
_fields._clipWidth = screen._clipWidth;
_fields._clipHeight = screen._clipHeight;
_fields._bufferStart = screen._bufferStart;
_fields._scrollCol = _vm->_scrollCol;
_fields._scrollRow = _vm->_scrollRow;
}
void InventoryManager::restoreFields() {
Screen &screen = *_vm->_screen;
Room &room = *_vm->_room;
screen._vWindowHeight = _fields._vWindowHeight;
screen._vWindowLinesTall = _fields._vWindowLinesTall;
screen._vWindowWidth = _fields._vWindowWidth;
screen._vWindowBytesWide = _fields._vWindowBytesWide;
room._playFieldHeight = _fields._playFieldHeight;
room._playFieldWidth = _fields._playFieldWidth;
screen._windowXAdd = _fields._windowXAdd;
screen._windowYAdd = _fields._windowYAdd;
screen._screenYOff = _fields._screenYOff;
_vm->_scrollX = _fields._scrollX;
_vm->_scrollY = _fields._scrollY;
screen._clipWidth = _fields._clipWidth;
screen._clipHeight = _fields._clipHeight;
screen._bufferStart = _fields._bufferStart;
_vm->_scrollCol = _fields._scrollCol;
_vm->_scrollRow = _fields._scrollRow;
}
void InventoryManager::initFields() {
Screen &screen = *_vm->_screen;
Room &room = *_vm->_room;
screen._vWindowHeight = screen.h;
room._playFieldHeight = screen.h;
screen._vWindowLinesTall = screen.h;
screen._clipHeight = screen.h;
room._playFieldWidth = screen.w;
screen._vWindowWidth = screen.w;
screen._vWindowBytesWide = screen.w;
screen._clipWidth = screen.w;
screen._windowXAdd = 0;
screen._windowYAdd = 0;
screen._screenYOff = 0;
screen._bufferStart.x = 0;
screen._bufferStart.y = 0;
_vm->_scrollX = _vm->_scrollY = 0;
_vm->_buffer1.clearBuffer();
_vm->_buffer2.clearBuffer();
// The original was doing at this point a check on vesa mode
// We don't need it as we don't do inventory pre-rendering
if (!_invRefreshFlag)
screen.clearBuffer();
screen.savePalette();
}
void InventoryManager::getList() {
_items.clear();
_tempLOff.clear();
for (uint i = 0; i < _inv.size(); ++i) {
if (_inv[i]._value == ITEM_IN_INVENTORY) {
_items.push_back(i);
_tempLOff.push_back(_inv[i]._name);
}
}
}
void InventoryManager::showAllItems() {
_iconDisplayFlag = true;
for (uint i = 0; i < _items.size(); ++i)
putInvIcon(i, _items[i]);
}
void InventoryManager::putInvIcon(int itemIndex, int itemId) {
SpriteResource *spr = _vm->_objectsTable[99];
assert(spr);
Common::Point pt((itemIndex % 6) * 46 + 23, (itemIndex / 6) * 35 + 15);
_vm->_buffer2.plotImage(spr, itemId, pt);
if (_iconDisplayFlag) {
_vm->_screen->copyBlock(&_vm->_buffer2, Common::Rect(pt.x, pt.y, pt.x + 46, pt.y + 35));
}
}
void InventoryManager::chooseItem() {
EventsManager &events = *_vm->_events;
_vm->_useItem = -1;
while (!_vm->shouldQuit()) {
// Check for events
events.pollEventsAndWait();
int selIndex;
// Poll events and wait for a click on a known area
if (!events._leftButton || ((selIndex = coordIndexOf()) == -1))
continue;
if (selIndex > 23) {
if (selIndex == 25)
_vm->_useItem = -1;
break;
} else if (selIndex < (int)_items.size() && _items[selIndex] != -1) {
_boxNum = selIndex;
_vm->copyBF2Vid();
combineItems();
_vm->copyBF2Vid();
outlineIcon(_boxNum);
_vm->_useItem = _items[_boxNum];
}
}
}
void InventoryManager::freeInvCells() {
delete _vm->_objectsTable[99];
_vm->_objectsTable[99] = nullptr;
}
int InventoryManager::coordIndexOf() const {
const Common::Point pt = _vm->_events->_mousePos;
for (int i = 0; i < (int)_invCoords.size(); ++i) {
if (_invCoords[i].contains(pt))
return i;
}
return -1;
}
void InventoryManager::saveScreens() {
_vm->_buffer1.copyTo(&_savedBuffer1);
_vm->_screen->copyTo(&_savedScreen);
_vm->_newRects.push_back(Common::Rect(0, 0, _savedScreen.w, _savedScreen.h));
}
void InventoryManager::restoreScreens() {
_vm->_buffer1.w = _vm->_buffer1.pitch;
_savedBuffer1.copyTo(&_vm->_buffer1);
_savedScreen.copyTo(_vm->_screen);
_savedBuffer1.free();
_savedScreen.free();
}
void InventoryManager::outlineIcon(int itemIndex) {
Screen &screen = *_vm->_screen;
screen.frameRect(_invCoords[itemIndex], 7);
Common::String s = _tempLOff[itemIndex];
Font &font = *_vm->_fonts._font2;
int strWidth = font.stringWidth(s);
font._fontColors[0] = 0;
font._fontColors[1] = 10;
font._fontColors[2] = 11;
font._fontColors[3] = 12;
font.drawString(&screen, s, Common::Point((screen.w - strWidth) / 2, 184));
}
void InventoryManager::combineItems() {
Screen &screen = *_vm->_screen;
EventsManager &events = *_vm->_events;
screen._leftSkip = screen._rightSkip = 0;
screen._topSkip = screen._bottomSkip = 0;
screen._screenYOff = 0;
const Common::Rect screenBounds = screen.getBounds();
const int screenW = screenBounds.width();
const int screenH = screenBounds.height();
Common::Point tempMouse = events._mousePos;
Common::Point lastMouse = events._mousePos;
Common::Rect &inv = _invCoords[_boxNum];
Common::Rect r(inv.left, inv.top, inv.left + 46, inv.top + 35);
Common::Point tempBox(inv.left, inv.top);
Common::Point lastBox(inv.left, inv.top);
_vm->_buffer2.copyBlock(&_vm->_buffer1, r);
SpriteResource *sprites = _vm->_objectsTable[99];
int invItem = _items[_boxNum];
events.pollEvents();
// Item drag handling loop if left button is held down
while (!_vm->shouldQuit() && events._leftButton) {
// Poll for events
events.pollEventsAndWait();
// Check positioning
if (lastMouse == events._mousePos)
continue;
lastMouse = events._mousePos;
Common::Rect lastRect(lastBox.x, lastBox.y, MIN(lastBox.x + 46, screenW), MIN(lastBox.y + 35, screenH));
screen.copyBlock(&_vm->_buffer2, lastRect);
Common::Point newPt;
newPt.x = MAX(events._mousePos.x - tempMouse.x + tempBox.x, 0);
newPt.y = MAX(events._mousePos.y - tempMouse.y + tempBox.y, 0);
screen.plotImage(sprites, invItem, newPt);
lastBox = newPt;
}
int destBox = events.checkMouseBox1(_invCoords);
if (destBox >= 0 && destBox != _boxNum && destBox < (int)_items.size()
&& _items[destBox] != -1) {
int itemA = invItem;
int itemB = _items[destBox];
// Check whether the items can be combined
int combinedItem = _inv[itemA].checkItem(itemB);
if (combinedItem != -1) {
_inv[combinedItem]._value = 1;
_inv[itemA]._value = 2;
_inv[itemB]._value = 2;
_items[_boxNum] = -1;
_items[destBox] = combinedItem;
_tempLOff[destBox] = _inv[combinedItem]._name;
events.hideCursor();
// Shrink down the first item on top of the second item
zoomIcon(itemA, itemB, destBox, true);
// Shrink down the second item
Common::Rect destRect(_invCoords[destBox].left, _invCoords[destBox].top,
_invCoords[destBox].left + 46, _invCoords[destBox].top + 35);
_vm->_buffer2.copyBlock(&_vm->_buffer1, destRect);
screen._screenYOff = 0;
zoomIcon(itemB, -1, destBox, true);
// Exand up the new combined item from nothing to full size
zoomIcon(combinedItem, -1, destBox, false);
_boxNum = destBox;
events.showCursor();
return;
}
}
_iconDisplayFlag = true;
putInvIcon(_boxNum, invItem);
}
void InventoryManager::zoomIcon(int zoomItem, int backItem, int zoomBox, bool shrink) {
Screen &screen = *_vm->_screen;
screen._screenYOff = 0;
SpriteResource *sprites = _vm->_objectsTable[99];
int oldScale = _vm->_scale;
int zoomScale = shrink ? 255 : 1;
int zoomInc = shrink ? -1 : 1;
Common::Rect boxRect(_invCoords[zoomBox].left, _invCoords[zoomBox].top,
_invCoords[zoomBox].left + 46, _invCoords[zoomBox].top + 35);
while (!_vm->shouldQuit() && zoomScale != 0 && zoomScale != 256) {
_vm->_events->pollEventsAndWait();
_vm->_buffer2.copyBlock(&_vm->_buffer1, boxRect);
if (backItem != -1) {
_iconDisplayFlag = false;
putInvIcon(zoomBox, backItem);
}
_vm->_scale = zoomScale;
screen.setScaleTable(zoomScale);
int xv = screen._scaleTable1[boxRect.width() + 1];
if (xv) {
int yv = screen._scaleTable1[boxRect.height() + 1];
if (yv) {
// The zoomed size is positive in both directions, so show zoomed item
Common::Rect scaledBox(xv, yv);
scaledBox.moveTo(boxRect.left + (boxRect.width() - xv + 1) / 2,
boxRect.top + (boxRect.height() - yv + 1) / 2);
_vm->_buffer2.sPlotF(sprites->getFrame(zoomItem), scaledBox);
}
}
screen.copyBlock(&_vm->_buffer2, boxRect);
zoomScale += zoomInc;
}
if (!shrink) {
// Handle the final full-size version
_vm->_buffer2.copyBlock(&_vm->_buffer1, boxRect);
_vm->_buffer2.plotImage(sprites, zoomItem,
Common::Point(boxRect.left, boxRect.top));
screen.copyBlock(&_vm->_buffer2, boxRect);
}
_vm->_scale = oldScale;
screen.setScaleTable(oldScale);
}
void InventoryManager::synchronize(Common::Serializer &s) {
int count = _inv.size();
s.syncAsUint16LE(count);
if (!s.isSaving())
_inv.resize(count);
for (int i = 0; i < count; ++i)
s.syncAsUint16LE(_inv[i]._value);
}
} // End of namespace Access

142
engines/access/inventory.h Normal file
View File

@@ -0,0 +1,142 @@
/* 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/>.
*
*/
#ifndef ACCESS_INVENTORY_H
#define ACCESS_INVENTORY_H
#include "common/scummsys.h"
#include "common/array.h"
#include "common/rect.h"
#include "common/str-array.h"
#include "access/data.h"
#include "access/asurface.h"
namespace Access {
enum ItemState {
ITEM_NOT_FOUND = 0, ITEM_IN_INVENTORY = 1, ITEM_USED = 2
};
class InventoryEntry {
public:
Common::String _name;
int _value;
int _otherItem1;
int _newItem1;
int _otherItem2;
int _newItem2;
void load(const Common::String &name, const int *data);
int checkItem(int itemId) const;
};
class InventoryManager : public Manager {
struct SavedFields {
int _vWindowHeight;
int _vWindowLinesTall;
int _vWindowWidth;
int _vWindowBytesWide;
int _playFieldHeight;
int _playFieldWidth;
int _windowXAdd;
int _windowYAdd;
int _screenYOff;
int _scrollX;
int _scrollY;
int _clipWidth;
int _clipHeight;
Common::Point _bufferStart;
int _scrollCol;
int _scrollRow;
SavedFields();
};
private:
Common::Array<int> _items;
Common::Array<Common::Rect> _invCoords;
ASurface _savedBuffer1;
ASurface _savedScreen;
SavedFields _fields;
bool _iconDisplayFlag;
Common::Array<int> _tempLPtr;
Common::StringArray _tempLOff;
int _boxNum;
void savedFields();
void restoreFields();
void initFields();
void getList();
void showAllItems();
void putInvIcon(int itemIndex, int itemId);
void chooseItem();
void freeInvCells();
int coordIndexOf() const;
void saveScreens();
void restoreScreens();
void outlineIcon(int itemIndex);
void combineItems();
void zoomIcon(int zoomItem, int backItem, int zoomBox, bool shrink);
public:
Common::Array<InventoryEntry> _inv;
int _startInvItem;
int _startInvBox;
bool _invChangeFlag;
bool _invRefreshFlag;
bool _invModeFlag;
int _startAboutItem;
int _startTravelItem;
public:
InventoryManager(AccessEngine *vm);
int &operator[](int idx);
int useItem();
void setUseItem(int itemId);
void refreshInventory();
int newDisplayInv();
int displayInv();
/**
* Synchronize savegame data
*/
void synchronize(Common::Serializer &s);
};
} // End of namespace Access
#endif /* ACCESS_INVENTORY_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,172 @@
/* 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/>.
*
*/
#ifndef ACCESS_MARTIAN_MARTIAN_DUCT_H
#define ACCESS_MARTIAN_MARTIAN_DUCT_H
#include "common/scummsys.h"
#include "common/array.h"
#include "common/rect.h"
#include "common/events.h"
#include "access/martian/martian_resources.h" // For Point3 .. move it?
namespace Access {
namespace Martian {
enum MoveIntent {
kMoveIntentNone,
kMoveIntentUp,
kMoveIntentLeft,
kMoveIntentDown,
kMoveIntentRight,
};
// Move angles - in the original these are indexes into sin/cos lookup tables.
// Add Invalid to ensure at least 2-byte length.
enum MoveAngle {
kMoveAngleNorth = 0,
kMoveAngleEast = 0x40,
kMoveAngleSouth = 0x80,
kMoveAngleWest = 0xC0,
kMoveAngleInvalid = 0xffff,
};
enum DuctFlags {
kDuctFlagNone = 0,
kDuctFlagZLessThanX = 1,
kDuctFlagXLessThanNegZ = 2,
kDuctFlagYLessThanNegZ = 4,
kDuctFlagZLessThanY = 8,
kDuctFlagZLessThan2 = 16,
};
struct RenderShape {
byte _col;
Common::Array<uint16> _pointIdxs;
};
class MartianEngine;
class MartianDuct {
public:
MartianDuct(MartianEngine *vm);
~MartianDuct();
void duct2();
void duct4();
private:
void doDuct();
void drawArrowSprites();
void drawArrowSprites2();
void clearWorkScreenArea();
void copyBufBlockToScreen();
void waitForMoveUpdate();
void storeLastValsToPrimArray(const Point3 &pt1, const Point3 &pt2);
void updatePlayerPos();
void updateMatrix();
void applyMatrixToMapData();
void updatePrimsAndDraw();
bool updateMapLocation();
void checkFinished();
void doMatrixMulAndAddPoint(int16 x, int16 y, int16 z);
bool doPrimArrayUpdates(int &tempIdx);
void doDraw(int counter);
void getPointValuesFromArray(int offset, Point3 &pt1, Point3 &pt2) const;
Common::Rect calcFinalLineSegment(const Point3 &pt1, const Point3 &pt2) const;
bool checkAndUpdatePrimArray1(int &offset);
bool checkAndUpdatePrimArray2(int &offset);
bool checkAndUpdatePrimArray3(int &offset);
bool checkAndUpdatePrimArray4(int &offset);
bool checkAndUpdatePrimArray5(int &offset);
bool checkAndUpdatePrimArrayForFlag(int &offset, DuctFlags flag, int divmulNum);
static Point3 divmul1(const Point3 &pt1, const Point3 &pt2);
static Point3 divmul2(const Point3 &pt1, const Point3 &pt2);
static Point3 divmul3(const Point3 &pt1, const Point3 &pt2);
static Point3 divmul4(const Point3 &pt1, const Point3 &pt2);
static Point3 divmul5(const Point3 &pt1, const Point3 &pt2);
bool checkMove0();
bool checkMove1();
bool checkMove2();
bool checkMove3();
bool checkMove4();
bool checkMove5();
bool checkMove6();
bool checkMove7();
bool checkMove8();
bool checkMove9();
bool checkMove10();
bool checkMove11();
bool checkMove12();
bool checkMove13_14();
void getXYandRBFlags(DuctFlags &xyflags, DuctFlags &rbflags, const Point3 &pt1, const Point3 &pt2);
int addPointsToMainPrimArray(int tempCount);
static DuctFlags getComparisonFlags(int16 x, int16 y, int16 z);
MartianEngine *_vm;
int16 _playerX;
int16 _preYOffset;
int16 _playerY;
int16 _nextPlayerX;
int16 _nextPlayerY;
int16 _xOffset;
int16 _yOffset;
int16 _xScale;
int16 _yScale;
uint16 _mapLoc;
MoveAngle _moveAngle;
int16 _crawlFrame;
bool _stopMoveLoop;
int16 _threshold1;
int16 _drawDistX;
int16 _drawDistY;
MoveIntent _moveIntent;
// NOTE: Original uses fixed point sin/cos with a lookup table.
float _matrix[3][3];
int16 _primArrayIdx;
Common::Array<Point3> _renderPoints;
Common::Array<RenderShape> _renderShapes;
Common::Array<int16> _primX1Array;
Common::Array<int16> _primY1Array;
Common::Array<int16> _primZ1Array;
Common::Array<int16> _primX2Array;
Common::Array<int16> _primY2Array;
Common::Array<int16> _primZ2Array;
Point3 _tempPoints[32];
};
}
} // end namespace Access
#endif // ACCESS_MARTIAN_MARTIAN_DUCT_H

View File

@@ -0,0 +1,427 @@
/* 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 "access/resources.h"
#include "access/martian/martian_game.h"
#include "access/martian/martian_resources.h"
#include "access/martian/martian_room.h"
#include "access/martian/martian_scripts.h"
#include "access/amazon/amazon_resources.h"
namespace Access {
namespace Martian {
MartianEngine::MartianEngine(OSystem *syst, const AccessGameDescription *gameDesc) :
AccessEngine(syst, gameDesc), _skipStart(false),
_creditsStream(nullptr)
{
}
MartianEngine::~MartianEngine() {
_skipStart = false;
_creditsStream = nullptr;
}
void MartianEngine::initObjects() {
_room = new MartianRoom(this);
_scripts = new MartianScripts(this);
}
void MartianEngine::configSelect() {
// No implementation required in MM
}
void MartianEngine::initVariables() {
// Set player room and position
_player->_roomNumber = 7;
_inventory->_startInvItem = 0;
_inventory->_startInvBox = 0;
Common::fill(&_objectsTable[0], &_objectsTable[100], (SpriteResource *)nullptr);
_player->_playerOff = false;
setupTimers();
_player->_playerX = _player->_rawPlayer.x = _res->ROOMTBL[_player->_roomNumber]._travelPos.x;
_player->_playerY = _player->_rawPlayer.y = _res->ROOMTBL[_player->_roomNumber]._travelPos.y;
_room->_selectCommand = -1;
_events->setNormalCursor(CURSOR_CROSSHAIRS);
_mouseMode = 0;
_animation->clearTimers();
ARRAYCLEAR(_travel);
_travel[7] = 1;
ARRAYCLEAR(_ask);
_ask[33] = 1;
ARRAYCLEAR(_flags);
}
void MartianEngine::setNoteParams() {
_events->hideCursor();
_screen->_orgX1 = 58;
_screen->_orgY1 = 124;
_screen->_orgX2 = 297;
_screen->_orgY2 = 199;
_screen->_lColor = 51;
_screen->drawRect();
_events->showCursor();
}
void MartianEngine::displayNote(const Common::String &msg) {
_fonts._charSet._lo = 1;
_fonts._charSet._hi = 8;
_fonts._charFor._lo = 0;
_fonts._charFor._hi = 255;
Font::_fontColors[3] = 0;
_screen->_maxChars = 40;
_screen->_printOrg = _screen->_printStart = Common::Point(59, 124);
setNoteParams();
Common::String lines = msg;
Common::String line;
int width = 0;
bool lastLine = false;
do {
lastLine = _fonts._font1->getLine(lines, _screen->_maxChars, line, width, Font::kWidthInChars);
_bubbleBox->printString(line);
_screen->_printOrg = Common::Point(_screen->_printStart.x, _screen->_printOrg.y + 6);
if (_screen->_printOrg.y == 196) {
_events->waitKeyActionMouse();
setNoteParams();
_screen->_printOrg = _screen->_printStart;
}
} while (!lastLine);
_events->waitKeyActionMouse();
}
void MartianEngine::doSpecial5(int param1) {
// Seems redundant to store the song as this is
// only ever called from restart or load?
debug("TODO: Push midi song?");
_midi->stopSong();
_midi->setLoop(false);
_midi->loadMusic(47, 4);
_midi->midiPlay();
_screen->setDisplayScan();
_events->clearEvents();
_screen->forceFadeOut();
_events->hideCursor();
_files->loadScreen("DATA.SC");
_events->showCursor();
_screen->setIconPalette();
_screen->forceFadeIn();
Resource *cellsRes = _files->loadFile("CELLS00.LZ");
_objectsTable[0] = new SpriteResource(this, cellsRes);
delete cellsRes;
_timers[20]._timer = _timers[20]._initTm = 30;
Resource *notesRes = _files->loadFile("NOTES.DAT");
notesRes->_stream->skip(param1 * 2);
int pos = notesRes->_stream->readUint16LE();
notesRes->_stream->seek(pos);
Common::String msg = notesRes->_stream->readString();
delete notesRes;
displayNote(msg);
_midi->stopSong();
_midi->freeMusic();
_midi->setLoop(true);
}
void MartianEngine::playGame() {
// Initialize Martian Memorandum game-specific objects
initObjects();
// Setup the game
setupGame();
configSelect();
if (_loadSaveSlot == -1) {
// Do introduction
doCredits();
if (shouldQuit())
return;
// Display Notes screen
doSpecial5(4);
if (shouldQuit())
return;
_screen->forceFadeOut();
}
do {
_restartFl = false;
_screen->clearScreen();
_screen->setPanel(0);
_screen->forceFadeOut();
_events->showCursor();
initVariables();
// If there's a pending savegame to load, load it
if (_loadSaveSlot != -1) {
loadGameState(_loadSaveSlot);
_loadSaveSlot = -1;
}
// Execute the room
_room->doRoom();
} while (_restartFl);
}
bool MartianEngine::showCredits() {
_events->hideCursor();
_screen->clearScreen();
_destIn = _screen;
int posX = _creditsStream->readSint16LE();
int posY = 0;
while (posX != -1) {
posY = _creditsStream->readSint16LE();
int frameNum = _creditsStream->readSint16LE();
_screen->plotImage(_objectsTable[41], frameNum, Common::Point(posX, posY));
posX = _creditsStream->readSint16LE();
}
posY = _creditsStream->readSint16LE();
if (posY == -1) {
_events->showCursor();
_screen->forceFadeOut();
return true;
}
_screen->forceFadeIn();
_timers[3]._timer = _timers[3]._initTm = posY;
while (!shouldQuit() && !_events->isKeyActionMousePressed() && _timers[3]._timer) {
_events->pollEventsAndWait();
}
_events->showCursor();
_screen->forceFadeOut();
if (_events->_rightButton)
return true;
else
return false;
}
void MartianEngine::doCredits() {
_midi->setLoop(false);
_midi->loadMusic(47, 3);
_midi->midiPlay();
_screen->setDisplayScan();
_events->hideCursor();
_screen->forceFadeOut();
Resource *data = _files->loadFile(41, 1);
_objectsTable[41] = new SpriteResource(this, data);
delete data;
_files->loadScreen(41, 0);
_buffer2.copyFrom(*_screen);
_buffer1.copyFrom(*_screen);
_events->showCursor();
_creditsStream = new Common::MemoryReadStream(CREDIT_DATA, 180);
if (!showCredits()) {
_screen->copyFrom(_buffer2);
_screen->forceFadeIn();
_events->_vbCount = 550;
while (!shouldQuit() && !_events->isKeyActionMousePressed() && _events->_vbCount > 0)
_events->pollEventsAndWait();
_screen->forceFadeOut();
while (!shouldQuit() && !_events->isKeyActionMousePressed()&& !showCredits())
_events->pollEventsAndWait();
delete _objectsTable[41];
_objectsTable[41] = nullptr;
_midi->freeMusic();
}
_midi->setLoop(true);
}
void MartianEngine::setupTimers() {
_timers.clear();
const int TIMER_DEFAULTS[] = { 4, 10, 8, 1, 1, 1, 1, 2 };
for (int i = 0; i < 32; ++i) {
TimerEntry te;
te._initTm = te._timer = (i < 8) ? TIMER_DEFAULTS[i] : 1;
te._flag = 1;
_timers.push_back(te);
}
}
void MartianEngine::setupGame() {
// Load death list
_deaths.resize(_res->DEATHS.size());
for (uint idx = 0; idx < _deaths.size(); ++idx) {
_deaths[idx]._screenId = _res->DEATHS[idx]._screenId;
_deaths[idx]._msg = _res->DEATHS[idx]._msg;
}
setupTimers();
// Miscellaneous
Martian::MartianResources &res = *((Martian::MartianResources *)_res);
_fonts.load(res._font1, res._font2, res._bitFont);
// Set player room and position
_player->_roomNumber = 7;
_player->_playerX = _player->_rawPlayer.x = _res->ROOMTBL[_player->_roomNumber]._travelPos.x;
_player->_playerY = _player->_rawPlayer.y = _res->ROOMTBL[_player->_roomNumber]._travelPos.y;
}
void MartianEngine::showExpositionText(Common::String msg) {
Common::String line = "";
int width = 0;
bool lastLine;
do {
lastLine = _fonts._font2->getLine(msg, _screen->_maxChars, line, width, Font::kWidthInChars);
// Draw the text
_bubbleBox->printString(line);
_screen->_printOrg.y += 6;
_screen->_printOrg.x = _screen->_printStart.x;
if (_screen->_printOrg.y == 180) {
_events->waitKeyActionMouse();
_screen->copyBuffer(&_buffer2);
_screen->_printOrg.y = _screen->_printStart.y;
}
} while (!lastLine);
// Avoid re-using double-click
_events->clearEvents();
_events->waitKeyActionMouse();
}
void MartianEngine::dead(int deathId) {
// Load and display death screen
_events->hideCursor();
_screen->forceFadeOut();
_files->loadScreen(48, _deaths[deathId]._screenId - 1);
_screen->setIconPalette();
_buffer2.copyBuffer(_screen);
_screen->forceFadeIn();
_events->showCursor();
// Setup fonts
_fonts._charSet._hi = 10;
_fonts._charSet._lo = 1;
_fonts._charFor._lo = 247;
_fonts._charFor._hi = 255;
Font::_fontColors[3] = 247;
_screen->_maxChars = 50;
_screen->_printOrg = Common::Point(24, 18);
_screen->_printStart = Common::Point(24, 18);
// Display death message
showExpositionText(_deaths[deathId]._msg);
_screen->forceFadeOut();
_room->clearRoom();
freeChar();
// The original was jumping to the restart label in main
_restartFl = true;
_events->pollEvents();
}
void MartianEngine::establish(int estabIndex, int sub) {
_fonts._charSet._hi = 10;
Font::_fontColors[0] = 0xff;
Font::_fontColors[1] = 0xf7;
Font::_fontColors[2] = 0xff;
Font::_fontColors[3] = 0xf7;
_screen->_maxChars = 50;
_screen->_printOrg = _screen->_printStart = Common::Point(24, 18);
// TODO: Original has a small delay here.
Resource *notesRes = _files->loadFile("ETEXT.DAT");
notesRes->_stream->seek(2 * sub);
uint16 msgOffset = notesRes->_stream->readUint16LE();
if (msgOffset == 0 || msgOffset >= notesRes->_stream->size()) {
error("MartianEngine::establish: Invalid message offset %d for msg %d", msgOffset, sub);
}
notesRes->_stream->seek(msgOffset);
Common::String msg = notesRes->_stream->readString();
showExpositionText(msg);
_events->hideCursor();
if (sub != 0x3f) {
_screen->forceFadeOut();
_screen->clearScreen();
}
_events->showCursor();
}
void MartianEngine::synchronize(Common::Serializer &s) {
AccessEngine::synchronize(s);
for (int i = 0; i < ARRAYSIZE(_travel); i++) {
s.syncAsByte(_travel[i]);
}
for (int i = 0; i < ARRAYSIZE(_ask); i++) {
s.syncAsByte(_ask[i]);
}
/*
TODO: Do any of these need to be synchronized here?
Mostly involved in modal dialogs.
_startTravelItem
_startTravelBox
_startAboutItem
_startAboutBox
_byte26CB5
_bcnt
_boxDataStart
_boxDataEnd
_boxSelectY
_boxSelectYOld
_numLines
*/
}
} // End of namespace Martian
} // End of namespace Access

View File

@@ -0,0 +1,80 @@
/* 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/>.
*
*/
#ifndef ACCESS_MARTIAN_GAME_H
#define ACCESS_MARTIAN_GAME_H
#include "access/access.h"
namespace Access {
namespace Martian {
class MartianEngine : public AccessEngine {
private:
bool _skipStart;
Common::MemoryReadStream *_creditsStream;
/**
* Do the game introduction
*/
void doCredits();
bool showCredits();
/**
* Setup variables for the game
*/
void setupGame();
void initObjects();
void configSelect();
void initVariables();
void setupTimers();
protected:
/**
* Play the game
*/
void playGame() override;
void dead(int deathId) override;
void setNoteParams();
void displayNote(const Common::String &msg);
public:
MartianEngine(OSystem *syst, const AccessGameDescription *gameDesc);
~MartianEngine() override;
void doSpecial5(int param1);
void showExpositionText(Common::String msg);
void establish(int estabIndex, int sub) override;
/**
* Synchronize savegame data
*/
void synchronize(Common::Serializer &s) override;
};
} // End of namespace Martian
} // End of namespace Access
#endif /* ACCESS_MARTIAN_GAME_H */

View File

@@ -0,0 +1,68 @@
/* 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/scummsys.h"
#include "access/access.h"
#include "access/room.h"
#include "access/martian/martian_game.h"
#include "access/martian/martian_player.h"
#include "access/martian/martian_resources.h"
namespace Access {
namespace Martian {
MartianPlayer::MartianPlayer(AccessEngine *vm) : Player(vm) {
_game = (MartianEngine *)vm;
}
void MartianPlayer::load() {
Player::load();
// Overwrite game-specific values
_playerOffset.x = _vm->_screen->_scaleTable1[20];
_playerOffset.y = _vm->_screen->_scaleTable1[62];
_leftDelta = -9;
_rightDelta = 33;
_upDelta = 5;
_downDelta = -5;
_scrollConst = 5;
for (uint8 i = 0; i < _vm->_playerDataCount; ++i) {
_walkOffRight[i] = SIDEOFFR[i];
_walkOffLeft[i] = SIDEOFFL[i];
_walkOffUp[i] = SIDEOFFU[i];
_walkOffDown[i] = SIDEOFFD[i];
}
_sideWalkMin = 0;
_sideWalkMax = 7;
_upWalkMin = 8;
_upWalkMax = 14;
_downWalkMin = 15;
_downWalkMax = 23;
// playerPalette is configured in Player::load.
}
} // End of namespace Martian
} // End of namespace Access

View File

@@ -0,0 +1,46 @@
/* 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/>.
*
*/
#ifndef ACCESS_MARTIAN_PLAYER_H
#define ACCESS_MARTIAN_PLAYER_H
#include "common/scummsys.h"
#include "access/player.h"
namespace Access {
namespace Martian {
class MartianEngine;
class MartianPlayer : public Player {
private:
MartianEngine *_game;
public:
MartianPlayer(AccessEngine *vm);
void load() override;
};
} // End of namespace Martian
} // End of namespace Access
#endif /* ACCESS_MARTIAN_PLAYER_H */

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,106 @@
/* 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/>.
*
*/
#ifndef ACCESS_MARTIAN_RESOURCES_H
#define ACCESS_MARTIAN_RESOURCES_H
#include "common/scummsys.h"
#include "access/resources.h"
#include "access/font.h"
namespace Access {
namespace Martian {
extern const int SIDEOFFR[];
extern const int SIDEOFFL[];
extern const int SIDEOFFU[];
extern const int SIDEOFFD[];
extern const int SIDEOFFR[];
extern const int SIDEOFFL[];
extern const int SIDEOFFU[];
extern const int SIDEOFFD[];
extern const byte CREDIT_DATA[];
extern const byte ICON_PALETTE[];
extern byte HELP[];
extern const char *const ASK_TBL[];
extern const char *const TRAVDATA[];
extern const char *const SPEC7MESSAGE;
extern const byte CAN_TRAVEL_MATRIX[];
extern const int16 PICTURE_RANGE[][2];
extern const int16 DUCT_ARROW_BUTTON_RANGE[][2];
struct DuctMapPoint {
int16 ptType;
int16 shapeType;
int16 x;
int16 y;
};
extern const DuctMapPoint DUCT_MAP_DATA[];
struct Point3 {
int16 x;
int16 y;
int16 z;
};
struct DuctShape {
int16 numPts;
int16 array2Len;
const Point3 *points;
const uint16 *data;
};
extern const DuctShape *DUCT_SHAPE_DATA[];
class MartianResources : public Resources {
protected:
/**
* Load data from the access.dat file
*/
void load(Common::SeekableReadStream &s) override;
public:
MartianFont *_font1;
MartianFont *_font2;
MartianBitFont *_bitFont;
public:
MartianResources(AccessEngine *vm) : Resources(vm), _font1(nullptr), _font2(nullptr), _bitFont(nullptr) {}
~MartianResources() override;
const byte *getCursor(int num) const override;
const char *getEgoName() const override { return "TEX"; }
int getRMouse(int i, int j) const override;
int inButtonXRange(int x) const override;
};
#define MMRES (*((Martian::MartianResources *)_vm->_res))
} // End of namespace Martian
} // End of namespace Access
#endif /* ACCESS_MARTIAN_RESOURCES_H */

View File

@@ -0,0 +1,136 @@
/* 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/scummsys.h"
#include "access/access.h"
#include "access/resources.h"
#include "access/martian/martian_game.h"
#include "access/martian/martian_resources.h"
#include "access/martian/martian_room.h"
namespace Access {
namespace Martian {
MartianRoom::MartianRoom(AccessEngine *vm) : Room(vm) {
_game = (MartianEngine *)vm;
for (int i = 0; i < 30; i++)
_vm->_flags[200 + i] = 0;
for (int i = 0; i < 10; i++)
_vm->_flags[178 + i] = 0;
}
MartianRoom::~MartianRoom() {
}
void MartianRoom::loadRoom(int roomNumber) {
loadRoomData(&MMRES.ROOMTBL[roomNumber]._data[0]);
}
void MartianRoom::reloadRoom() {
// _vm->_currentMan = _roomFlag;
// _vm->_currentManOld = _roomFlag;
// _vm->_manScaleOff = 0;
_vm->_player->loadTexPalette();
_vm->_player->loadSprites("TEX.LZ");
loadRoom(_vm->_player->_roomNumber);
reloadRoom1();
}
void MartianRoom::reloadRoom1() {
_selectCommand = -1;
_vm->_boxSelect = false; //-1
_vm->_player->_playerOff = false;
_vm->_screen->forceFadeOut();
_vm->_events->hideCursor();
_vm->_screen->clearScreen();
_vm->_events->showCursor();
roomInit();
_vm->_player->load();
if (_vm->_player->_roomNumber != 47)
_vm->_player->calcManScale();
_vm->_events->hideCursor();
roomMenu();
_vm->_screen->setBufferScan();
setupRoom();
setWallCodes();
buildScreen();
_vm->copyBF2Vid();
_vm->_screen->setManPalette();
_vm->_events->showCursor();
_vm->_player->_frame = 0;
_vm->_oldRects.clear();
_vm->_newRects.clear();
_vm->_events->clearEvents();
}
void MartianRoom::roomInit() {
Room::roomInit();
for (int i = 0; i < 30; i++)
_vm->_flags[200 + i] = 0;
for (int i = 0; i < 10; i++)
_vm->_flags[178 + i] = 0;
}
void MartianRoom::roomMenu() {
const SpriteResource *icons = _vm->getIcons();
_vm->_screen->saveScreen();
_vm->_screen->setDisplayScan();
_vm->_destIn = _vm->_screen; // TODO: Redundant
_vm->_screen->plotImage(icons, 0, Common::Point(5, 184));
_vm->_screen->plotImage(icons, 1, Common::Point(155, 184));
_vm->_screen->restoreScreen();
}
void MartianRoom::mainAreaClick() {
Common::Point &mousePos = _vm->_events->_mousePos;
Common::Point pt = _vm->_events->calcRawMouse();
Screen &screen = *_vm->_screen;
Player &player = *_vm->_player;
if (_selectCommand == -1) {
player._moveTo = pt;
player._playerMove = true;
} else if (mousePos.x >= screen._windowXAdd &&
mousePos.x <= (screen._windowXAdd + screen._vWindowBytesWide) &&
mousePos.y >= screen._windowYAdd &&
mousePos.y <= (screen._windowYAdd + screen._vWindowLinesTall)) {
if (checkBoxes1(pt) >= 0) {
checkBoxes3();
}
}
}
} // End of namespace Martian
} // End of namespace Access

View File

@@ -0,0 +1,64 @@
/* 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/>.
*
*/
#ifndef ACCESS_MARTIAN_ROOM_H
#define ACCESS_MARTIAN_ROOM_H
#include "common/scummsys.h"
#include "access/room.h"
namespace Access {
class AccessEngine;
namespace Martian {
class MartianEngine;
class MartianRoom : public Room {
private:
MartianEngine *_game;
protected:
void loadRoom(int roomNumber) override;
void roomInit() override;
void reloadRoom() override;
void reloadRoom1() override;
void mainAreaClick() override;
public:
MartianRoom(AccessEngine *vm);
~MartianRoom() override;
void init4Quads() override { }
void roomMenu() override;
};
} // End of namespace Martian
} // End of namespace Access
#endif /* ACCESS_AMAZON_ROOM_H */

View File

@@ -0,0 +1,383 @@
/* 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/scummsys.h"
#include "access/access.h"
#include "access/martian/martian_game.h"
#include "access/martian/martian_resources.h"
#include "access/martian/martian_duct.h"
#include "access/martian/martian_scripts.h"
namespace Access {
namespace Martian {
MartianScripts::MartianScripts(AccessEngine *vm) : Scripts(vm) {
_game = (MartianEngine *)_vm;
_duct = new MartianDuct(_game);
}
MartianScripts::~MartianScripts() {
delete _duct;
}
void MartianScripts::cmdSpecial0() {
// Abduction scene
_vm->_sound->stopSound();
_vm->_midi->stopSong();
_vm->_midi->loadMusic(47, 1);
_vm->_midi->midiPlay();
_vm->_midi->setLoop(true);
_vm->_screen->forceFadeOut();
_vm->_files->loadScreen("HOUSE.SC");
_vm->_video->setVideo(_vm->_screen, Common::Point(46, 30), "HVID.VID", 20);
_vm->_events->hideCursor();
_vm->_events->_vbCount = 300;
while (!_vm->shouldQuit() && _vm->_events->_vbCount > 0)
_vm->_events->pollEventsAndWait();
do {
_vm->_video->playVideo();
_vm->_events->pollEvents();
if (_vm->_video->_videoFrame == 4) {
_vm->_screen->flashPalette(16);
_vm->_sound->playSound(4);
do {
_vm->_events->pollEvents();
} while (!_vm->shouldQuit() && _vm->_sound->isSFXPlaying());
//
// TODO:
//
// The original sets these, probably to hold the video until the sound finishes?
// But, if we do that they never get past frame 4, because in the next iteration
// of the loop the timer is still "counting down", so we stay on frame 4 forever.
//
// Need to double-check the exact flow of the original here.
//
//_vm->_timers[31]._timer = _vm->_timers[31]._initTm = 40;
}
} while (!_vm->_video->_videoEnd && !_vm->shouldQuit());
if (_vm->shouldQuit())
return;
_vm->_screen->flashPalette(12);
_vm->_sound->playSound(4);
do {
_vm->_events->pollEvents();
} while (!_vm->shouldQuit() && _vm->_sound->isSFXPlaying());
_vm->_events->showCursor();
_vm->_midi->stopSong();
_vm->_midi->freeMusic();
}
void MartianScripts::cmdSpecial1(int param1, int param2) {
//
// Special 1 is a scene transition with some explanatory text
//
_vm->_events->hideCursor();
if ((byte)param1 != (byte)-1) {
_vm->_files->loadScreen(49, param1);
_vm->_buffer2.copyBuffer(_vm->_screen);
}
_vm->_screen->setIconPalette();
_vm->_screen->forceFadeIn();
_vm->_events->showCursor();
_vm->establish(0, param2);
}
void MartianScripts::cmdSpecial2() {
_duct->duct2();
}
void MartianScripts::cmdSpecial3() {
_vm->_screen->forceFadeOut();
_vm->_events->hideCursor();
_vm->_files->loadScreen(57, 3);
_vm->_buffer2.copyFrom(*_vm->_screen);
_vm->_screen->setIconPalette();
_vm->_events->showCursor();
_vm->_screen->forceFadeIn();
}
void MartianScripts::cmdSpecial4() {
_duct->duct4();
}
void MartianScripts::doIntro(int param1) {
_game->doSpecial5(param1);
}
void MartianScripts::cmdSpecial6() {
// A special transition screen after the jetpack in the outpost.
debug("cmdSpecial6: TODO: Store current music?");
_vm->_midi->stopSong();
_vm->_screen->setDisplayScan();
_vm->_events->clearEvents();
_vm->_screen->forceFadeOut();
_vm->_events->hideCursor();
_vm->_files->loadScreen(49, 9);
_vm->_events->showCursor();
_vm->_screen->setIconPalette();
_vm->_screen->forceFadeIn();
Resource *cellsRes = _vm->_files->loadFile("CELLS00.LZ");
_vm->_objectsTable[0] = new SpriteResource(_vm, cellsRes);
delete cellsRes;
_vm->_timers[20]._timer = _vm->_timers[20]._initTm = 30;
_vm->_fonts._charSet._lo = 1;
_vm->_fonts._charSet._hi = 10;
_vm->_fonts._charFor._lo = 1;
_vm->_fonts._charFor._hi = 255;
_vm->_screen->_maxChars = 50;
_vm->_screen->_printOrg = _vm->_screen->_printStart = Common::Point(24, 18);
Resource *notesRes = _vm->_files->loadFile("ETEXT.DAT");
notesRes->_stream->seek(72);
uint16 offset = notesRes->_stream->readUint16LE();
notesRes->_stream->seek(offset);
// Read the message
Common::String msg = notesRes->_stream->readString();
//display the message
_game->showExpositionText(msg);
delete notesRes;
delete _vm->_objectsTable[0];
_vm->_objectsTable[0] = nullptr;
_vm->_midi->stopSong();
// WORKAROUND: Reset Tex's scale flag after jetpack.
// (bug also present in original game)
_vm->_player->_flags &= ~IMGFLAG_UNSCALED;
// We always go straight to another scene so this seems
// redundant..
debug("cmdSpecial6: TODO: Restore original music?");
}
void MartianScripts::cmdSpecial7() {
_vm->_room->clearRoom();
_vm->_midi->loadMusic(47, 8);
_vm->_sound->freeSounds();
Resource *sound = _vm->_sound->loadSound(46, 14);
_vm->_sound->_soundTable.push_back(SoundEntry(sound, 1));
_vm->_screen->setDisplayScan();
_vm->_screen->forceFadeOut();
_vm->_events->hideCursor();
_vm->_files->loadScreen(40, 3);
_vm->_buffer1.copyBuffer(_vm->_screen);
_vm->_buffer2.copyBuffer(_vm->_screen);
_vm->_events->showCursor();
_vm->_screen->setIconPalette();
_vm->_screen->forceFadeIn();
// Load objects specific to this special scene
Resource *data = _vm->_files->loadFile(40, 2);
_game->_objectsTable[40] = new SpriteResource(_vm, data);
delete data;
// Load animation data
_vm->_animation->freeAnimationData();
Resource *animResource = _vm->_files->loadFile(40, 1);
_vm->_animation->loadAnimations(animResource);
delete animResource;
// Load script
Resource *newScript = _vm->_files->loadFile(40, 0);
setScript(newScript);
_vm->_images.clear();
_vm->_oldRects.clear();
_sequence = 0;
searchForSequence();
executeScript();
_vm->_sound->playSound(0);
do {
charLoop();
_vm->_events->pollEvents();
} while (_vm->_flags[134] != 1);
do {
_vm->_events->pollEvents();
} while (!_vm->shouldQuit() && _vm->_sound->isSFXPlaying());
_vm->_animation->clearTimers();
_vm->_animation->freeAnimationData();
_vm->_scripts->freeScriptData();
_vm->_sound->freeSounds();
_vm->_screen->forceFadeOut();
_vm->_midi->midiPlay();
_vm->_midi->setLoop(true);
_vm->_events->hideCursor();
_vm->_files->loadScreen(40, 4);
_vm->_buffer1.copyBuffer(_vm->_screen);
_vm->_buffer2.copyBuffer(_vm->_screen);
_vm->_events->showCursor();
_vm->_screen->setIconPalette();
_vm->_screen->forceFadeIn();
// Setup fonts
_vm->_fonts._charSet._hi = 10;
_vm->_fonts._charSet._lo = 1;
_vm->_fonts._charFor._lo = 247;
_vm->_fonts._charFor._hi = 255;
_vm->_screen->_maxChars = 50;
_vm->_screen->_printOrg = Common::Point(24, 18);
_vm->_screen->_printStart = Common::Point(24, 18);
_game->showExpositionText(Common::String(SPEC7MESSAGE));
_vm->_events->showCursor();
_vm->_screen->copyBuffer(&_vm->_buffer1);
_vm->_events->hideCursor();
_vm->_video->setVideo(_vm->_screen, Common::Point(120, 16), FileIdent(40, 5), 10);
while (!_vm->shouldQuit() && !_vm->_video->_videoEnd) {
_vm->_video->playVideo();
_vm->_events->pollEventsAndWait();
}
_vm->_sound->freeSounds();
sound = _vm->_sound->loadSound(40, 8);
_vm->_sound->_soundTable.push_back(SoundEntry(sound, 1));
sound = _vm->_sound->loadSound(40, 9);
_vm->_sound->_soundTable.push_back(SoundEntry(sound, 1));
sound = _vm->_sound->loadSound(40, 10);
_vm->_sound->_soundTable.push_back(SoundEntry(sound, 1));
_vm->_screen->forceFadeOut();
_vm->_files->loadScreen(40, 7);
_vm->_destIn = _vm->_screen;
_vm->_screen->plotImage(_game->_objectsTable[40], 8, Common::Point(176, 104));
_vm->_screen->plotImage(_game->_objectsTable[40], 7, Common::Point(160, 102));
_vm->_events->showCursor();
_vm->_screen->forceFadeIn();
_vm->_events->_vbCount = 100;
while (!_vm->shouldQuit() && _vm->_events->_vbCount > 0)
_vm->_events->pollEventsAndWait();
_vm->_sound->playSound(0);
do {
_vm->_events->pollEvents();
} while (!_vm->shouldQuit() && _vm->_sound->isSFXPlaying());
_vm->_events->_vbCount = 80;
while (!_vm->shouldQuit() && _vm->_events->_vbCount > 0)
_vm->_events->pollEventsAndWait();
_vm->_sound->playSound(1);
do {
_vm->_events->pollEvents();
} while (!_vm->shouldQuit() && _vm->_sound->isSFXPlaying());
_vm->_events->_vbCount = 80;
while (!_vm->shouldQuit() && _vm->_events->_vbCount > 0)
_vm->_events->pollEventsAndWait();
_vm->_sound->playSound(2);
do {
_vm->_events->pollEvents();
} while (!_vm->shouldQuit() && _vm->_sound->isSFXPlaying());
_vm->_sound->freeSounds();
delete _game->_objectsTable[40];
_game->_objectsTable[40] = nullptr;
_vm->_events->hideCursor();
_vm->_screen->forceFadeOut();
_vm->_files->loadScreen(40, 6);
_vm->_events->showCursor();
_vm->_screen->forceFadeIn();
_vm->_events->waitKeyActionMouse();
_vm->_midi->stopSong();
_vm->_midi->freeMusic();
// The original was jumping to the restart label in main
_vm->_restartFl = true;
_vm->_events->pollEvents();
}
void MartianScripts::executeSpecial(int commandIndex, int param1, int param2) {
switch (commandIndex) {
case 0:
cmdSpecial0();
break;
case 1:
cmdSpecial1(param1, param2);
break;
case 2:
cmdSpecial2();
break;
case 3:
cmdSpecial3();
break;
case 4:
cmdSpecial4();
break;
case 5:
doIntro(param1);
break;
case 6:
cmdSpecial6();
break;
case 7:
cmdSpecial7();
break;
default:
warning("Unexpected Special code %d - Skipped", commandIndex);
}
}
typedef void(MartianScripts::*MartianScriptMethodPtr)();
void MartianScripts::executeCommand(int commandIndex) {
Scripts::executeCommand(commandIndex);
}
} // End of namespace Martian
} // End of namespace Access

View File

@@ -0,0 +1,62 @@
/* 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/>.
*
*/
#ifndef ACCESS_MARTIAN_SCRIPTS_H
#define ACCESS_MARTIAN_SCRIPTS_H
#include "common/scummsys.h"
#include "access/scripts.h"
namespace Access {
namespace Martian {
class MartianEngine;
class MartianDuct;
class MartianScripts : public Scripts {
private:
MartianEngine *_game;
MartianDuct *_duct;
void cmdSpecial0();
void cmdSpecial1(int param1, int param2);
void cmdSpecial2();
void cmdSpecial3();
void cmdSpecial4();
void doIntro(int param1);
void cmdSpecial6();
void cmdSpecial7();
protected:
void executeSpecial(int commandIndex, int param1, int param2) override;
void executeCommand(int commandIndex) override;
public:
MartianScripts(AccessEngine *vm);
~MartianScripts();
};
} // End of namespace Martian
} // End of namespace Access
#endif /* ACCESS_MARTIAN_SCRIPTS_H */

View File

@@ -0,0 +1,207 @@
/* 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 "access/martian/midiparser_bemd.h"
#include "audio/mididrv.h"
#include "audio/midiparser.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "common/file.h"
namespace Access {
MidiParser_BEmd::MidiParser_BEmd(): _tickData(nullptr), _tickDataEnd(nullptr), _trackDataEnd(nullptr) {
}
bool MidiParser_BEmd::loadMusic(const byte *data, uint32 size) {
unloadMusic();
const byte *pos = data;
//
// 'BEmd' MIDI format from Martian Memorandum.
//
// A simple single-track format that splits note and timing data.
//
// Header is:
// 'BEmd' (magic number)
// 0xC0 0x00 (unknown, always 0xC0?
// 16-bit offset to timing data block
// 16-bit size of timing data block
// 6 bytes unk (normally 0)
// Header is followed by the track data block, then timing delta data
// block.
//
// Track data mostly follows other MIDI formats with a few differences:
// * Fixed length arguments
// * 0xFF 0x51 (tempo meta event) is followed by a uint16 which is fed
// directly to the PIT as a delay
// * Deltas are assumed to be 0. On 0xF8, a 16 bit int is read from
// the timing block and sent to the PIT as a timing delay.
//
if (!memcmp(pos, "BEmd", 4)) {
pos += 4;
if (size <= 16)
error("Wrong BEmd music resource size");
/*uint16 unk1 = */ READ_LE_UINT16(pos); // Normally 0xC0?
uint16 secondBlockOffset = READ_LE_UINT16(pos + 2);
if (secondBlockOffset < 16 || secondBlockOffset >= size)
error("Bad second block offset in BEmd file");
uint16 secondBlockSize = READ_LE_UINT16(pos + 4);
if (static_cast<uint32>(secondBlockOffset + secondBlockSize) != size)
error("Bad second block offset+size in BEmd file");
_trackDataEnd = data + secondBlockOffset;
_tickData = _trackDataEnd;
_tickDataEnd = data + size;
// Only one track
_numTracks = 1;
_numSubtracks[0] = 1;
_autoLoop = false;
_ppqn = 1;
_tracks[0][0] = data + 16;
resetTracking();
setTempo(16667);
setTrack(0);
return true;
} else {
warning("Expected BEmd header but found '%c%c%c%c' instead", pos[0], pos[1], pos[2], pos[3]);
return false;
}
return false;
}
void MidiParser_BEmd::parseNextEvent(EventInfo &info) {
uint8 subtrack = info.subtrack;
const byte *playPos = _position._subtracks[subtrack]._playPos;
info.start = playPos;
info.delta = 0;
// Process the next info.
if ((playPos[0] & 0xF0) >= 0x80)
info.event = *(playPos++);
else
info.event = _position._subtracks[subtrack]._runningStatus;
if (info.event < 0x80) {
_position._subtracks[subtrack]._playPos = playPos;
return;
}
_position._subtracks[subtrack]._runningStatus = info.event;
switch (info.command()) {
case 0x9: // Note On
info.basic.param1 = *(playPos++);
info.basic.param2 = *(playPos++);
if (info.basic.param2 == 0)
info.event = info.channel() | 0x80;
info.length = 0;
break;
case 0xC:
case 0xD:
info.basic.param1 = *(playPos++);
info.basic.param2 = 0;
break;
case 0x8:
case 0xA:
case 0xB:
case 0xE:
info.basic.param1 = *(playPos++);
info.basic.param2 = *(playPos++);
info.length = 0;
break;
case 0xF:
switch (info.event & 0x0F) {
case 0x2: // Song Position Pointer
info.basic.param1 = *(playPos++);
info.basic.param2 = *(playPos++);
break;
case 0x3: // Song Select
info.basic.param1 = *(playPos++);
info.basic.param2 = 0;
break;
case 0x8: // Timing data
// Tick data is stored separately.
info.delta = READ_LE_UINT16(_tickData);
_tickData += 2;
// FALL THROUGH
case 0x6:
case 0xA:
case 0xB:
case 0xC:
case 0xE:
info.basic.param1 = info.basic.param2 = 0;
break;
case 0x0: // SysEx
error("MidiParser_BEmd::parseNextEvent: Unexpected SysEx event");
break;
case 0xF: // META event
info.ext.type = *(playPos++);
if (info.ext.type == 0x51) {
// Set Tempo - 2 bytes and interpreted as direct input for the PIT
// as ticks to next note (0.8381uS/tick)
setTempo(READ_LE_UINT16(playPos) * 0.8381);
}
info.length = (info.ext.type == 0x2f ? 0 : 2);
info.ext.data = playPos;
playPos += info.length;
break;
default:
error("MidiParser_BEmd::parseNextEvent: Unsupported event code %x", info.event);
break;
}
default:
break;
}
_position._subtracks[subtrack]._playPos = playPos;
assert(playPos < _trackDataEnd);
assert(_tickData < _tickDataEnd);
}
bool MidiParser_BEmd::processEvent(const EventInfo &info, bool fireEvents) {
// Ignore timer events we handled already.
if ((info.event == 0xF8) || (info.event == 0xFF && info.ext.type == 0x51))
return true;
return MidiParser::processEvent(info, fireEvents);
}
void MidiParser_BEmd::resetTracking() {
_tickData = _trackDataEnd;
MidiParser::resetTracking();
}
} // end namespace Access

View File

@@ -0,0 +1,49 @@
/* 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/>.
*
*/
#ifndef ACCESS_MARTIAN_MIDI_PARSER_BEMD_H
#define ACCESS_MARTIAN_MIDI_PARSER_BEMD_H
#include "audio/midiparser.h"
namespace Access {
class MidiParser_BEmd : public MidiParser {
public:
MidiParser_BEmd();
bool loadMusic(const byte *data, uint32 size) override;
protected:
void parseNextEvent(EventInfo &info) override;
bool processEvent(const EventInfo &info, bool fireEvents) override;
void resetTracking() override;
private:
const byte *_trackDataEnd;
const byte *_tickData;
const byte *_tickDataEnd;
};
} // end namespace Access
#endif // ACCESS_MARTIAN_MIDI_PARSER_BEMD_H

View File

@@ -0,0 +1,360 @@
/* 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/savefile.h"
#include "common/system.h"
#include "engines/advancedDetector.h"
#include "graphics/surface.h"
#include "access/access.h"
#include "access/amazon/amazon_game.h"
#include "access/martian/martian_game.h"
#include "access/detection.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/standard-actions.h"
#include "common/translation.h"
#define MAX_SAVES 99
namespace Access {
uint32 AccessEngine::getGameID() const {
return _gameDescription->gameID;
}
uint32 AccessEngine::getGameFeatures() const {
return _gameDescription->features;
}
uint32 AccessEngine::getFeatures() const {
return _gameDescription->desc.flags;
}
bool AccessEngine::isCD() const {
return (bool)(_gameDescription->desc.flags & ADGF_CD);
}
bool AccessEngine::isDemo() const {
return (bool)(_gameDescription->desc.flags & ADGF_DEMO);
}
Common::Language AccessEngine::getLanguage() const {
return _gameDescription->desc.language;
}
Common::Platform AccessEngine::getPlatform() const {
return _gameDescription->desc.platform;
}
} // End of namespace Access
class AccessMetaEngine : public AdvancedMetaEngine<Access::AccessGameDescription> {
public:
const char *getName() const override {
return "access";
}
bool hasFeature(MetaEngineFeature f) const override;
Common::Error createInstance(OSystem *syst, Engine **engine, const Access::AccessGameDescription *desc) const override;
SaveStateList listSaves(const char *target) const override;
int getMaximumSaveSlot() const override;
bool removeSaveState(const char *target, int slot) const override;
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
Common::KeymapArray initKeymaps(const char *target) const override;
};
bool AccessMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsListSaves) ||
(f == kSupportsLoadingDuringStartup) ||
(f == kSupportsDeleteSave) ||
(f == kSavesSupportMetaInfo) ||
(f == kSavesSupportThumbnail) ||
(f == kSimpleSavesNames);
}
bool Access::AccessEngine::hasFeature(EngineFeature f) const {
return
(f == kSupportsReturnToLauncher) ||
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime);
}
Common::Error AccessMetaEngine::createInstance(OSystem *syst, Engine **engine, const Access::AccessGameDescription *gd) const {
switch (gd->gameID) {
case Access::kGameAmazon:
*engine = new Access::Amazon::AmazonEngine(syst, gd);
break;
case Access::kGameMartianMemorandum:
*engine = new Access::Martian::MartianEngine(syst, gd);
break;
default:
return Common::kUnsupportedGameidError;
}
return Common::kNoError;
}
SaveStateList AccessMetaEngine::listSaves(const char *target) const {
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
Common::String saveDesc;
Common::String pattern = Common::String::format("%s.0##", target);
Common::StringArray filenames = saveFileMan->listSavefiles(pattern);
Access::AccessSavegameHeader header;
SaveStateList saveList;
for (const auto &filename : filenames) {
const char *ext = strrchr(filename.c_str(), '.');
int slot = ext ? atoi(ext + 1) : -1;
if (slot >= 0 && slot < MAX_SAVES) {
Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename);
if (in) {
if (Access::AccessEngine::readSavegameHeader(in, header))
saveList.push_back(SaveStateDescriptor(this, slot, header._saveName));
delete in;
}
}
}
// Sort saves based on slot number.
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
int AccessMetaEngine::getMaximumSaveSlot() const {
return MAX_SAVES;
}
bool AccessMetaEngine::removeSaveState(const char *target, int slot) const {
Common::String filename = Common::String::format("%s.%03d", target, slot);
return g_system->getSavefileManager()->removeSavefile(filename);
}
SaveStateDescriptor AccessMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
Common::String filename = Common::String::format("%s.%03d", target, slot);
Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(filename);
if (f) {
Access::AccessSavegameHeader header;
if (!Access::AccessEngine::readSavegameHeader(f, header, false)) {
delete f;
return SaveStateDescriptor();
}
delete f;
// Create the return descriptor
SaveStateDescriptor desc(this, slot, header._saveName);
desc.setThumbnail(header._thumbnail);
desc.setSaveDate(header._year, header._month, header._day);
desc.setSaveTime(header._hour, header._minute);
desc.setPlayTime(header._totalFrames * GAME_FRAME_TIME);
return desc;
}
return SaveStateDescriptor();
}
Common::KeymapArray AccessMetaEngine::initKeymaps(const char *target) const {
using namespace Common;
using namespace Access;
// Get the game ID for the target
const Common::String currDomain = ConfMan.getActiveDomainName();
ConfMan.setActiveDomain(target);
const Common::String gameId = ConfMan.get("gameid");
ConfMan.setActiveDomain(currDomain);
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "access-default", _("Default keymappings"));
Action *act;
act = new Action(kStandardActionLeftClick, _("Move / Interact / Skip"));
act->setLeftClickEvent();
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("JOY_A");
engineKeyMap->addAction(act);
act = new Action(kStandardActionRightClick, _("Skip"));
act->setRightClickEvent();
act->addDefaultInputMapping("MOUSE_RIGHT");
act->addDefaultInputMapping("JOY_B");
engineKeyMap->addAction(act);
act = new Action("SKIP", _("Skip movie"));
act->setCustomEngineActionEvent(kActionSkip);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_X");
engineKeyMap->addAction(act);
act = new Action("UP", _("Move up"));
act->setCustomEngineActionEvent(kActionMoveUp);
act->addDefaultInputMapping("UP");
act->addDefaultInputMapping("KP8");
act->addDefaultInputMapping("JOY_UP");
engineKeyMap->addAction(act);
act = new Action("DOWN", _("Move down"));
act->setCustomEngineActionEvent(kActionMoveDown);
act->addDefaultInputMapping("DOWN");
act->addDefaultInputMapping("KP2");
act->addDefaultInputMapping("JOY_DOWN");
engineKeyMap->addAction(act);
act = new Action("LEFT", _("Move left"));
act->setCustomEngineActionEvent(kActionMoveLeft);
act->addDefaultInputMapping("LEFT");
act->addDefaultInputMapping("KP4");
act->addDefaultInputMapping("JOY_LEFT");
engineKeyMap->addAction(act);
act = new Action("RIGHT", _("Move right"));
act->setCustomEngineActionEvent(kActionMoveRight);
act->addDefaultInputMapping("RIGHT");
act->addDefaultInputMapping("KP6");
act->addDefaultInputMapping("JOY_RIGHT");
engineKeyMap->addAction(act);
act = new Action("UPLEFT", _("Move up-left"));
act->setCustomEngineActionEvent(kActionMoveUpLeft);
act->addDefaultInputMapping("KP7");
engineKeyMap->addAction(act);
act = new Action("UPRIGHT", _("Move up-right"));
act->setCustomEngineActionEvent(kActionMoveUpRight);
act->addDefaultInputMapping("KP9");
engineKeyMap->addAction(act);
act = new Action("DOWNLEFT", _("Move down-left"));
act->setCustomEngineActionEvent(kActionMoveDownLeft);
act->addDefaultInputMapping("KP1");
engineKeyMap->addAction(act);
act = new Action("DOWNRIGHT", _("Move down-right"));
act->setCustomEngineActionEvent(kActionMoveDownRight);
act->addDefaultInputMapping("KP3");
engineKeyMap->addAction(act);
act = new Action("LOOK", _("Look"));
act->setCustomEngineActionEvent(kActionLook);
act->addDefaultInputMapping("F1");
if (strcmp(target, "amazon") == 0)
act->addDefaultInputMapping("F2");
engineKeyMap->addAction(act);
if (gameId.equals("martian")) {
act = new Action("OPEN", _("Open"));
act->setCustomEngineActionEvent(kActionOpen);
act->addDefaultInputMapping("F2");
engineKeyMap->addAction(act);
act = new Action("MOVE", _("Move"));
act->setCustomEngineActionEvent(kActionMove);
act->addDefaultInputMapping("F3");
engineKeyMap->addAction(act);
act = new Action("GET", _("Get"));
act->setCustomEngineActionEvent(kActionTake);
act->addDefaultInputMapping("F4");
engineKeyMap->addAction(act);
act = new Action("USE", _("Use"));
act->setCustomEngineActionEvent(kActionUse);
act->addDefaultInputMapping("F5");
engineKeyMap->addAction(act);
act = new Action("GOTO", _("Goto"));
act->setCustomEngineActionEvent(kActionWalk);
act->addDefaultInputMapping("F6");
engineKeyMap->addAction(act);
act = new Action("TALK", _("Talk"));
act->setCustomEngineActionEvent(kActionTalk);
act->addDefaultInputMapping("F7");
engineKeyMap->addAction(act);
act = new Action("TRAVEL", _("Travel"));
act->setCustomEngineActionEvent(kActionTravel);
act->addDefaultInputMapping("F8");
engineKeyMap->addAction(act);
} else {
// Amazon keymaps
act = new Action("USE", _("Use"));
act->setCustomEngineActionEvent(kActionUse);
act->addDefaultInputMapping("F3");
engineKeyMap->addAction(act);
act = new Action("TAKE", _("Take"));
act->setCustomEngineActionEvent(kActionTake);
act->addDefaultInputMapping("F4");
engineKeyMap->addAction(act);
act = new Action("INVENTORY", _("Inventory"));
act->setCustomEngineActionEvent(kActionInventory);
act->addDefaultInputMapping("F5");
engineKeyMap->addAction(act);
act = new Action("CLIMB", _("Climb"));
act->setCustomEngineActionEvent(kActionClimb);
act->addDefaultInputMapping("F6");
engineKeyMap->addAction(act);
act = new Action("TALK", _("Talk"));
act->setCustomEngineActionEvent(kActionTalk);
act->addDefaultInputMapping("F7");
engineKeyMap->addAction(act);
act = new Action("WALK", _("Walk"));
act->setCustomEngineActionEvent(kActionWalk);
act->addDefaultInputMapping("F8");
engineKeyMap->addAction(act);
}
act = new Action("HELP", _("Help"));
act->setCustomEngineActionEvent(kActionHelp);
act->addDefaultInputMapping("F9");
engineKeyMap->addAction(act);
act = new Action("SAVELOAD", _("Open save/load menu"));
act->setCustomEngineActionEvent(kActionSaveLoad);
act->addDefaultInputMapping("F10");
engineKeyMap->addAction(act);
return Keymap::arrayOf(engineKeyMap);
}
#if PLUGIN_ENABLED_DYNAMIC(ACCESS)
REGISTER_PLUGIN_DYNAMIC(ACCESS, PLUGIN_TYPE_ENGINE, AccessMetaEngine);
#else
REGISTER_PLUGIN_STATIC(ACCESS, PLUGIN_TYPE_ENGINE, AccessMetaEngine);
#endif

48
engines/access/module.mk Normal file
View File

@@ -0,0 +1,48 @@
MODULE := engines/access
MODULE_OBJS := \
access.o \
animation.o \
asurface.o \
bubble_box.o \
char.o \
data.o \
debugger.o \
decompress.o \
events.o \
files.o \
font.o \
inventory.o \
metaengine.o \
player.o \
resources.o \
room.o \
screen.o \
scripts.o \
sound.o \
video.o \
amazon/amazon_game.o \
amazon/amazon_logic.o \
amazon/amazon_player.o \
amazon/amazon_resources.o \
amazon/amazon_room.o \
amazon/amazon_scripts.o \
martian/martian_game.o \
martian/martian_player.o \
martian/martian_resources.o \
martian/martian_duct.o \
martian/martian_room.o \
martian/martian_scripts.o \
martian/midiparser_bemd.o \
video/movie_decoder.o
# This module can be built as a plugin
ifeq ($(ENABLE_ACCESS), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

944
engines/access/player.cpp Normal file
View File

@@ -0,0 +1,944 @@
/* 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/algorithm.h"
#include "common/textconsole.h"
#include "access/player.h"
#include "access/access.h"
#include "access/resources.h"
#include "access/amazon/amazon_player.h"
#include "access/martian/martian_player.h"
namespace Access {
Player *Player::init(AccessEngine *vm) {
switch (vm->getGameID()) {
case kGameAmazon:
vm->_playerDataCount = 8;
return new Amazon::AmazonPlayer(vm);
case kGameMartianMemorandum:
vm->_playerDataCount = 10;
return new Martian::MartianPlayer(vm);
default:
vm->_playerDataCount = 8;
return new Player(vm);
}
}
Player::Player(AccessEngine *vm) : Manager(vm), ImageEntry() {
_playerSprites = nullptr;
_playerSprites1 = nullptr;
_manPal1 = nullptr;
_frameNumber = 0;
_rawTempL = 0;
_rawXTemp = 0;
_rawYTempL = 0;
_rawYTemp = 0;
_playerXLow = 0;
_playerX = 0;
_playerYLow = 0;
_playerY = 0;
_frame = 0;
_playerOff = false;
_playerMove = false;
_leftDelta = _rightDelta = 0;
_upDelta = _downDelta = 0;
_scrollConst = 0;
_scrollFlag = false;
_scrollThreshold = 0;
_scrollAmount = 0;
_scrollEnd = 0;
_roomNumber = 0;
_collideFlag = false;
_move = NONE;
_playerDirection = NONE;
_xFlag = _yFlag = 0;
_inactiveYOff = 0;
_jetpackFlag = 0;
_sideWalkMin = _sideWalkMax = 0;
_upWalkMin = _upWalkMax = 0;
_downWalkMin = _downWalkMax = 0;
_diagUpWalkMin = _diagUpWalkMax = 0;
_diagDownWalkMin = _diagDownWalkMax = 0;
_walkOffRight = _walkOffLeft = nullptr;
_walkOffUp = _walkOffDown = nullptr;
_walkOffUR = _walkOffDR = nullptr;
_walkOffUL = _walkOffDL = nullptr;
}
Player::~Player() {
delete _playerSprites;
delete[] _manPal1;
delete[] _walkOffRight;
delete[] _walkOffLeft;
delete[] _walkOffUp;
delete[] _walkOffDown;
delete[] _walkOffUR;
delete[] _walkOffDR;
delete[] _walkOffUL;
delete[] _walkOffDL;
}
void Player::load() {
uint8 dataCount = _vm->_playerDataCount;
_walkOffRight = new int[dataCount];
_walkOffLeft = new int[dataCount];
_walkOffUp = new int[dataCount];
_walkOffDown = new int[dataCount];
_walkOffUR = new Common::Point[dataCount];
_walkOffDR = new Common::Point[dataCount];
_walkOffUL = new Common::Point[dataCount];
_walkOffDL = new Common::Point[dataCount];
// NOTE: Although the values get set here to Amazon defaults, they are overridden
// in both AmazonPlayer and MartianPlayer load() functions.
_playerOffset.x = _vm->_screen->_scaleTable1[25];
_playerOffset.y = _vm->_screen->_scaleTable1[67];
_leftDelta = -3;
_rightDelta = 33;
_upDelta = 5;
_downDelta = -10;
_scrollConst = 5;
_sideWalkMin = 0;
_sideWalkMax = 7;
_upWalkMin = 16;
_upWalkMax = 23;
_downWalkMin = 8;
_downWalkMax = 15;
_diagUpWalkMin = 0;
_diagUpWalkMax = 7;
_diagDownWalkMin = 0;
_diagDownWalkMax = 7;
_playerSprites = _playerSprites1;
if (_manPal1) {
// Those values are from MM as Amazon doesn't use it
Common::copy(_manPal1 + 0x2A0, _manPal1 + 0x2A0 + 0x42, _vm->_screen->_manPal);
} else {
Common::fill(_vm->_screen->_manPal, _vm->_screen->_manPal + 0x60, 0);
}
}
void Player::loadTexPalette() {
Resource *texPal = _vm->_files->loadFile("TEXPAL.COL");
int size = texPal->_size;
assert(size == 768);
_manPal1 = new byte[size];
memcpy(_manPal1, texPal->data(), size);
}
void Player::loadSprites(const Common::Path &name) {
freeSprites();
Resource *data = _vm->_files->loadFile(name);
#if 0
Common::DumpFile *outFile = new Common::DumpFile();
Common::String outName = name + ".dump";
outFile->open(outName);
outFile->write(data->data(), data->_size);
outFile->finalize();
outFile->close();
#endif
_playerSprites1 = new SpriteResource(_vm, data);
delete data;
}
void Player::freeSprites() {
delete _playerSprites;
_playerSprites1 = nullptr;
_playerSprites = nullptr;
}
void Player::removeSprite1() {
if (_playerSprites1) {
delete _playerSprites1;
_playerSprites1 = nullptr;
}
}
bool Player::isMMHover() const {
// Whether or not this is MM and the player is on the hoverboard.
return (_vm->getGameID() == kGameMartianMemorandum &&
_vm->_flags[174] == 1);
}
void Player::calcManScale() {
if (!_vm->_manScaleOff) {
_vm->_scale = ((((_rawPlayer.y - _vm->_scaleMaxY + _vm->_scaleN1) *
_vm->_scaleT1 + (_vm->_scaleH2 << 8)) & 0xff00) / _vm->_scaleH1 * _vm->_scaleI) >> 8;
_vm->_screen->setScaleTable(_vm->_scale);
_playerOffset.x = _vm->_screen->_scaleTable1[20];
_playerOffset.y = _vm->_screen->_scaleTable1[(_vm->getGameID() == kGameMartianMemorandum) ? 62 : 67];
_inactiveYOff = _playerOffset.y;
}
}
void Player::jetpack() {
_playerDirection = UP;
ImageFlag flags = IMGFLAG_NONE;
if (!_vm->_timers[0]._flag) {
_vm->_timers[0]._flag = 1;
_jetpackFlag ^= 1;
_rawPlayer.y -= 4;
if (_move == RIGHT) {
_frame = 0;
flags = IMGFLAG_CROPPED;
if (_rawPlayer.x < 0x101)
_rawPlayer.x += 2;
else
_rawPlayer.x = 256;
}
if (_move == LEFT) {
_frame = 0;
flags = IMGFLAG_BACKWARDS;
_rawPlayer.x -= 2;
if (_rawPlayer.x < 0)
_rawPlayer.x = 0;
}
// Leave tex facing whichever direction he was previously
// unless moving in some direction
if (_move == LEFT || _move == RIGHT)
_flags = (_flags & 0xfd) | flags;
}
_flags |= IMGFLAG_UNSCALED;
_position.x = _rawPlayer.x;
_position.y = _rawPlayer.y - _playerOffset.y;
_offsetY = _playerOffset.y;
_frameNumber = 23 + _jetpackFlag;
ImageEntry ie = *this;
ie._spritesPtr = _vm->_objectsTable[35];
_vm->_images.addToList(ie);
}
void Player::walk() {
_collideFlag = false;
if (_vm->getGameID() == kGameMartianMemorandum && _vm->_flags[173] == 1) {
// Special case for MM Jetpack
jetpack();
return;
}
_playerDirection = NONE;
if (_playerOff)
return;
else if (_vm->_timers[0]._flag) {
plotCom3();
return;
}
++_vm->_timers[0]._flag;
switch (_move) {
case UP:
_playerMove = false;
walkUp();
break;
case DOWN:
_playerMove = false;
walkDown();
break;
case LEFT:
_playerMove = false;
walkLeft();
break;
case RIGHT:
_playerMove = false;
walkRight();
break;
case UPLEFT:
_playerMove = false;
walkUpLeft();
break;
case DOWNLEFT:
_playerMove = false;
walkDownLeft();
break;
case UPRIGHT:
_playerMove = false;
walkUpRight();
break;
case DOWNRIGHT:
_playerMove = false;
walkDownRight();
break;
default:
checkMove();
break;
}
}
void Player::calcPlayer() {
Screen &scr = *_vm->_screen;
scr._bufferStart.x = (_vm->_scrollCol << 4) + _vm->_scrollX;
scr._bufferStart.y = (_vm->_scrollRow << 4) + _vm->_scrollY;
_playerX = _rawPlayer.x - scr._bufferStart.x;
_playerY = _rawPlayer.y - scr._bufferStart.y;
}
void Player::walkUp() {
if (_frame > _upWalkMax || _frame < _upWalkMin)
_frame = _upWalkMin;
_playerDirection = UP;
int walkOff = _walkOffUp[_frame - _upWalkMin];
int tempL = _rawPlayerLow.y - (isMMHover() ? 2 : _vm->_screen->_scaleTable2[walkOff]);
_rawYTempL = (byte)tempL;
int yTemp = _rawPlayer.y - (isMMHover() ? 2 : _vm->_screen->_scaleTable1[walkOff]) -
(tempL < 0 ? 1 : 0);
_rawYTemp = yTemp;
_rawXTemp = _rawPlayer.x;
if (_vm->_room->codeWalls()) {
plotCom2();
} else {
_rawPlayer.y = _rawYTemp;
_rawPlayerLow.y = _rawYTempL;
calcManScale();
if (_vm->getGameID() == kGameMartianMemorandum && _vm->_flags[174] == 0 && (_frame == 8 || _frame == 12))
_vm->_sound->playSound(0);
else if (_vm->getGameID() == kGameAmazon && _vm->_currentMan != 3 && (_frame == 17 || _frame == 21))
_vm->_sound->playSound(0);
if (++_frame > _upWalkMax)
_frame = _upWalkMin;
plotCom(0);
}
}
void Player::walkDown() {
if (_frame > _downWalkMax || _frame < _downWalkMin)
_frame = _downWalkMin;
_playerDirection = DOWN;
int walkOff = _walkOffDown[_frame - _downWalkMin];
int tempL = (isMMHover() ? 2 : _vm->_screen->_scaleTable2[walkOff]) + _rawPlayerLow.y;
_rawYTempL = (byte)tempL;
_rawYTemp = (isMMHover() ? 2 : _vm->_screen->_scaleTable1[walkOff]) + _rawPlayer.y + (tempL >= 0x100 ? 1 : 0);
_rawXTemp = _rawPlayer.x;
if (_vm->_room->codeWalls()) {
plotCom2();
} else {
_rawPlayer.y = _rawYTemp;
_rawPlayerLow.y = _rawYTempL;
calcManScale();
if (_vm->getGameID() == kGameMartianMemorandum && _vm->_flags[174] == 0 && (_frame == 17 || _frame == 21))
_vm->_sound->playSound(0);
else if (_vm->getGameID() == kGameAmazon && _vm->_currentMan != 3 && (_frame == 10 || _frame == 14))
_vm->_sound->playSound(0);
if (++_frame > _downWalkMax)
_frame = _downWalkMin;
plotCom(0);
}
}
void Player::walkLeft() {
if (_frame > _sideWalkMax || _frame < _sideWalkMin)
_frame = _sideWalkMin;
_playerDirection = LEFT;
bool flag = _scrollEnd == 1;
if (!flag) {
calcPlayer();
flag = (_playerX - _vm->_screen->_scaleTable1[_scrollConst] -
_vm->_player->_scrollThreshold) > 0;
}
if (flag) {
int walkOffset = _walkOffLeft[_frame - _sideWalkMin];
int tempL = _rawPlayerLow.x - (isMMHover() ? 2 : _vm->_screen->_scaleTable2[walkOffset]);
_rawTempL = (byte)tempL;
_rawXTemp = _rawPlayer.x - (isMMHover() ? 2 : _vm->_screen->_scaleTable1[walkOffset]) -
(tempL < 0 ? 1 : 0);
} else {
_rawXTemp = _rawPlayer.x - _vm->_screen->_scaleTable1[_scrollConst];
}
_rawYTemp = _rawPlayer.y;
if (_vm->_room->codeWalls()) {
plotCom2();
} else {
_rawPlayer.x = _rawXTemp;
_rawPlayerLow.x = _rawTempL;
++_frame;
if (_vm->getGameID() == kGameMartianMemorandum && _vm->_flags[174] == 0 && (_frame == 7 || _frame == 3))
_vm->_sound->playSound(0);
else if (_vm->getGameID() == kGameAmazon && _vm->_currentMan != 3 && (_frame == 1 || _frame == 5))
_vm->_sound->playSound(0);
if (_frame > _sideWalkMax)
_frame = _sideWalkMin;
plotCom1();
}
}
void Player::walkRight() {
if (_frame > _sideWalkMax || _frame < _sideWalkMin)
_frame = _sideWalkMin;
_playerDirection = RIGHT;
bool flag = _scrollEnd == 2;
if (!flag) {
calcPlayer();
flag = (_vm->_screen->_clipWidth - _playerX - _vm->_screen->_scaleTable1[_scrollConst] -
_vm->_player->_scrollThreshold) > 0;
}
if (flag) {
int walkOffset = _walkOffRight[_frame - _sideWalkMin];
int tempL = _rawPlayerLow.x + (isMMHover() ? 2 : _vm->_screen->_scaleTable2[walkOffset]);
_rawTempL = (byte)tempL;
_rawXTemp = _rawPlayer.x + (isMMHover() ? 2 : _vm->_screen->_scaleTable1[walkOffset]) +
(tempL >= 0x100 ? 1 : 0);
} else {
_rawXTemp = _rawPlayer.x + _vm->_screen->_scaleTable1[_scrollConst];
}
_rawYTemp = _rawPlayer.y;
if (_vm->_room->codeWalls()) {
plotCom2();
} else {
_rawPlayer.x = _rawXTemp;
_rawPlayerLow.x = _rawTempL;
++_frame;
if (_vm->getGameID() == kGameMartianMemorandum && _vm->_flags[174] == 0 && (_frame == 7 || _frame == 3))
_vm->_sound->playSound(0);
else if (_vm->getGameID() == kGameAmazon && _vm->_currentMan != 3 && (_frame == 1 || _frame == 5))
_vm->_sound->playSound(0);
// Useless check removed
if (_frame > _sideWalkMax)
_frame = _sideWalkMin;
plotCom0();
}
}
void Player::walkUpLeft() {
if (_frame > _diagUpWalkMax || _frame < _diagUpWalkMin)
_frame = _diagUpWalkMin;
_playerDirection = UPLEFT;
int walkOffset, tempL;
bool flag = _scrollEnd == 1;
if (!flag) {
calcPlayer();
flag = (_playerX - _vm->_screen->_scaleTable1[_scrollConst] -
_vm->_player->_scrollThreshold) > 0;
}
if (flag) {
walkOffset = _walkOffUL[_frame - _diagUpWalkMin].x;
tempL = _rawPlayerLow.x - _vm->_screen->_scaleTable2[walkOffset];
_rawTempL = (byte)tempL;
_rawXTemp = _rawPlayer.x - _vm->_screen->_scaleTable1[walkOffset] -
(tempL < 0 ? 1 : 0);
} else {
_rawXTemp = _rawPlayer.x - _vm->_screen->_scaleTable1[_scrollConst];
}
walkOffset = _walkOffUL[_frame - _diagUpWalkMin].y;
tempL = _rawPlayerLow.y - _vm->_screen->_scaleTable2[walkOffset];
_rawYTempL = (byte)tempL;
_rawYTemp = _rawPlayer.y - _vm->_screen->_scaleTable1[walkOffset] -
(tempL < 0 ? 1 : 0);
if (_vm->_room->codeWalls()) {
plotCom2();
} else {
_rawPlayer.x = _rawXTemp;
_rawPlayer.y = _rawYTemp;
_rawPlayerLow.x = _rawTempL;
_rawPlayerLow.y = _rawYTempL;
++_frame;
calcManScale();
if (_vm->_currentMan != 3 && (_frame == 1 || _frame == 5))
_vm->_sound->playSound(0);
if (_frame > _diagUpWalkMax)
_frame = _diagUpWalkMin;
plotCom1();
}
}
void Player::walkDownLeft() {
if (_frame > _diagDownWalkMax || _frame < _diagDownWalkMin)
_frame = _diagDownWalkMin;
_playerDirection = DOWNLEFT;
int walkOffset, tempL;
bool flag = _scrollEnd == 1;
if (!flag) {
calcPlayer();
flag = (_playerX - _vm->_screen->_scaleTable1[_scrollConst] -
_vm->_player->_scrollThreshold) > 0;
}
if (flag) {
walkOffset = _walkOffDL[_frame - _sideWalkMin].x;
tempL = _rawPlayerLow.x - _vm->_screen->_scaleTable2[walkOffset];
_rawTempL = (byte)tempL;
_rawXTemp = _rawPlayer.x - _vm->_screen->_scaleTable1[walkOffset] -
(tempL < 0 ? 1 : 0);
} else {
_rawXTemp = _rawPlayer.x - _vm->_screen->_scaleTable1[_scrollConst];
}
walkOffset = _walkOffDL[_frame - _diagDownWalkMin].y;
tempL = _rawPlayerLow.y + _vm->_screen->_scaleTable2[walkOffset];
_rawYTempL = (byte)tempL;
_rawYTemp = _rawPlayer.y + _vm->_screen->_scaleTable1[walkOffset] +
(tempL >= 0x100 ? 1 : 0);
if (_vm->_room->codeWalls()) {
plotCom2();
} else {
_rawPlayer.x = _rawXTemp;
_rawPlayer.y = _rawYTemp;
_rawPlayerLow.x = _rawTempL;
_rawPlayerLow.y = _rawYTempL;
++_frame;
calcManScale();
if (_vm->_currentMan != 3 && (_frame == 1 || _frame == 5))
_vm->_sound->playSound(0);
if (_frame > _diagDownWalkMax)
_frame = _diagDownWalkMin;
plotCom1();
}
}
void Player::walkUpRight() {
if (_frame > _diagUpWalkMax || _frame < _diagUpWalkMin)
_frame = _diagUpWalkMin;
_playerDirection = UPRIGHT;
int walkOffset, tempL;
bool flag = _scrollEnd == 1;
if (!flag) {
calcPlayer();
flag = (_vm->_screen->_clipWidth - _playerX - _vm->_screen->_scaleTable1[_scrollConst] -
_vm->_player->_scrollThreshold) > 0;
}
if (flag) {
walkOffset = _walkOffUR[_frame - _diagUpWalkMin].x;
tempL = _rawPlayerLow.x + _vm->_screen->_scaleTable2[walkOffset];
_rawTempL = (byte)tempL;
_rawXTemp = _rawPlayer.x + _vm->_screen->_scaleTable1[walkOffset] +
(tempL >= 0x100 ? 1 : 0);
} else {
_rawXTemp = _rawPlayer.x + _vm->_screen->_scaleTable1[_scrollConst];
}
walkOffset = _walkOffUL[_frame - _diagUpWalkMin].y;
tempL = _rawPlayerLow.y - _vm->_screen->_scaleTable2[walkOffset];
_rawYTempL = (byte)tempL;
_rawYTemp = _rawPlayer.y - _vm->_screen->_scaleTable1[walkOffset] -
(tempL < 0 ? 1 : 0);
if (_vm->_room->codeWalls()) {
plotCom2();
} else {
_rawPlayer.x = _rawXTemp;
_rawPlayer.y = _rawYTemp;
_rawPlayerLow.x = _rawTempL;
_rawPlayerLow.y = _rawYTempL;
++_frame;
calcManScale();
if (_vm->_currentMan != 3 && (_frame == 1 || _frame == 5))
_vm->_sound->playSound(0);
if (_frame > _diagUpWalkMax)
_frame = _diagUpWalkMin;
plotCom(0);
}
}
void Player::walkDownRight() {
if (_frame > _diagDownWalkMax || _frame < _diagDownWalkMin)
_frame = _diagDownWalkMin;
_playerDirection = DOWNRIGHT;
int walkOffset, tempL;
bool flag = _scrollEnd == 2;
if (!flag) {
calcPlayer();
flag = (_vm->_screen->_clipWidth - _playerX - _vm->_screen->_scaleTable1[_scrollConst] -
_vm->_player->_scrollThreshold) > 0;
}
if (flag) {
walkOffset = _walkOffUR[_frame - _diagDownWalkMin].x;
tempL = _rawPlayerLow.x + _vm->_screen->_scaleTable2[walkOffset];
_rawTempL = (byte)tempL;
_rawXTemp = _rawPlayer.x + _vm->_screen->_scaleTable1[walkOffset] +
(tempL >= 0x100 ? 1 : 0);
} else {
_rawXTemp = _rawPlayer.x + _vm->_screen->_scaleTable1[_scrollConst];
}
walkOffset = _walkOffDR[_frame - _diagDownWalkMin].y;
tempL = _rawPlayerLow.y + _vm->_screen->_scaleTable2[walkOffset];
_rawYTempL = (byte)tempL;
_rawYTemp = _rawPlayer.y + _vm->_screen->_scaleTable1[walkOffset] +
(tempL >= 0x100 ? 1 : 0);
if (_vm->_room->codeWalls()) {
plotCom2();
} else {
_rawPlayer.x = _rawXTemp;
_rawPlayer.y = _rawYTemp;
_rawPlayerLow.x = _rawTempL;
_rawPlayerLow.y = _rawYTempL;
calcManScale();
if (_vm->_currentMan != 3 && (_frame == 1 || _frame == 5))
_vm->_sound->playSound(0);
++_frame;
if (_frame > _diagDownWalkMax)
_frame = _diagDownWalkMin;
plotCom(0);
}
}
void Player::checkMove() {
if (_playerMove) {
if (_xFlag == 0 && _yFlag == 0) {
int xp = (_playerOffset.x / 2) + _rawPlayer.x - _moveTo.x;
if (xp < 0)
xp = -xp;
int yp = _rawPlayer.y - _moveTo.y;
if (yp < 0)
yp = -yp;
if (xp >= yp)
_xFlag = 1;
else
_yFlag = 1;
}
if (_yFlag == 1) {
int yd = _rawPlayer.y - _moveTo.y;
if ((yd >= 0 && yd <= _upDelta) || (yd < 0 && -yd <= _upDelta)) {
++_yFlag;
if (_xFlag) {
_playerMove = false;
_xFlag = _yFlag = 0;
} else {
++_xFlag;
}
} else {
if (yd >= 0)
walkUp();
else
walkDown();
if (_collideFlag) {
_playerMove = false;
_xFlag = _yFlag = 0;
}
}
} else if (_xFlag == 1) {
int xd = (_playerOffset.x / 2) + _rawPlayer.x - _moveTo.x;
if ((xd >= 0 && xd <= -_leftDelta) || (xd < 0 && -xd <= -_leftDelta)) {
++_xFlag;
if (_yFlag) {
_playerMove = false;
_xFlag = _yFlag = 0;
}
} else {
if (xd >= 0)
walkLeft();
else
walkRight();
if (_collideFlag) {
_playerMove = false;
_xFlag = _yFlag = 0;
}
}
} else if (!_yFlag) {
++_yFlag;
} else {
_playerMove = false;
_xFlag = _yFlag = 0;
}
}
plotCom3();
}
void Player::plotCom(int flags) {
_flags &= ~IMGFLAG_BACKWARDS;
_flags &= ~IMGFLAG_UNSCALED;
_flags |= flags;
plotCom3();
}
void Player::plotCom0() {
plotCom(_vm->getGameID() == kGameAmazon ? 0 : IMGFLAG_BACKWARDS);
}
void Player::plotCom1() {
plotCom(_vm->getGameID() == kGameAmazon ? IMGFLAG_BACKWARDS : 0);
}
void Player::plotCom2() {
// WORKAROUND: Amazon has at least one cutscene with the player not properly turned off
if (!_playerOff && _spritesPtr != nullptr) {
ImageEntry ie = *this;
if (!isMMHover()) {
_vm->_images.addToList(ie);
} else if (_vm->_objectsTable[23]) {
// MM player on hoverboard
ie._spritesPtr = _vm->_objectsTable[23];
ie._frameNumber = 13;
_vm->_images.addToList(ie);
}
}
}
void Player::plotCom3() {
// Update the base ImageEntry fields for the player
_position.x = _rawPlayer.x;
_position.y = _rawPlayer.y - _playerOffset.y;
_offsetY = _playerOffset.y;
_spritesPtr = _playerSprites;
_frameNumber = _frame;
plotCom2();
}
void Player::checkScrollUp() {
if ((_playerDirection == DOWNRIGHT || _playerDirection == DOWNLEFT ||
_playerDirection == DOWN) && (_vm->_screen->_clipHeight -
_playerY - _scrollThreshold) <= 0) {
// Scroll up
if (scrollUp()) {
_scrollEnd = 4;
_vm->_scrollY &= TILE_HEIGHT;
_scrollFlag = true;
}
}
}
void Player::checkScroll() {
_scrollFlag = false;
if (_playerDirection == NONE)
return;
calcPlayer();
if ((_playerDirection == UPLEFT || _playerDirection == DOWNLEFT ||
_playerDirection == LEFT) && _playerX <= _scrollThreshold) {
// Scroll right
if (!scrollRight()) {
if (_playerDirection == DOWNLEFT)
checkScrollUp();
return;
}
} else if ((_playerDirection == UPRIGHT || _playerDirection == DOWNRIGHT ||
_playerDirection == RIGHT) && (_vm->_screen->_clipWidth -
_playerX - _scrollThreshold) <= 0) {
// Scroll left
if (!scrollLeft()) {
if (_playerDirection == DOWNRIGHT)
checkScrollUp();
return;
}
}
if ((_playerDirection == UPRIGHT || _playerDirection == UPLEFT ||
_playerDirection == UP) && _playerY <= _scrollThreshold) {
scrollDown();
} else {
checkScrollUp();
}
}
bool Player::scrollUp(int forcedAmount) {
if (forcedAmount == -1)
_scrollAmount = -(_vm->_screen->_clipHeight - _playerY - _scrollThreshold);
else
_scrollAmount = forcedAmount;
if ((_vm->_scrollRow + _vm->_screen->_vWindowHeight) >=
_vm->_room->_playFieldHeight) {
return true;
}
_scrollFlag = true;
_vm->_scrollY = _vm->_scrollY + _scrollAmount;
while (_vm->_scrollY >= TILE_HEIGHT && !_vm->shouldQuit()) {
_vm->_scrollY -= TILE_HEIGHT;
++_vm->_scrollRow;
_vm->_buffer1.moveBufferUp();
_vm->_room->buildRow(_vm->_scrollRow + _vm->_screen->_vWindowHeight,
_vm->_screen->_vWindowLinesTall);
if ((_vm->_scrollRow + _vm->_screen->_vWindowHeight) >=
_vm->_room->_playFieldHeight)
return true;
if (_vm->_scrollY <= TILE_HEIGHT)
return false;
}
return false;
}
bool Player::scrollDown(int forcedAmount) {
if (forcedAmount == -1)
_scrollAmount = -(_playerY - _scrollThreshold);
else
_scrollAmount = forcedAmount;
_scrollFlag = true;
_vm->_scrollY -= _scrollAmount;
if (_vm->_scrollY >= 0)
return true;
do {
_vm->_scrollY += TILE_HEIGHT;
if (--_vm->_scrollRow < 0)
break;
_vm->_buffer1.moveBufferDown();
_vm->_room->buildRow(_vm->_scrollRow, 0);
if (_vm->_scrollY >= 0)
return false;
} while (!_vm->shouldQuit());
_scrollEnd = 3;
_vm->_scrollY = 0;
_vm->_scrollRow = 0;
return true;
}
bool Player::scrollLeft(int forcedAmount) {
Screen &screen = *_vm->_screen;
if (forcedAmount == -1)
_scrollAmount = -(_vm->_screen->_clipWidth - _playerX - _scrollThreshold);
else
_scrollAmount = forcedAmount;
if ((_vm->_scrollCol + screen._vWindowWidth) == _vm->_room->_playFieldWidth) {
_scrollEnd = 2;
_vm->_scrollX = 0;
_scrollFlag = true;
return true;
} else {
_scrollFlag = true;
_vm->_scrollX += _scrollAmount;
do {
if (_vm->_scrollX < TILE_WIDTH)
return true;
_vm->_scrollX -= TILE_WIDTH;
++_vm->_scrollCol;
_vm->_buffer1.moveBufferLeft();
_vm->_room->buildColumn(_vm->_scrollCol + screen._vWindowWidth,
screen._vWindowBytesWide);
} while (!_vm->shouldQuit() && (_vm->_scrollX >= TILE_WIDTH));
return (_playerDirection == UPRIGHT);
}
}
bool Player::scrollRight(int forcedAmount) {
if (forcedAmount == -1)
_scrollAmount = -(_playerX - _scrollThreshold);
else
_scrollAmount = forcedAmount;
_scrollFlag = true;
_vm->_scrollX -= _scrollAmount;
if (_vm->_scrollX < 0) {
do {
_vm->_scrollX += TILE_WIDTH;
if (--_vm->_scrollCol < 0) {
_scrollEnd = 1;
_vm->_scrollX = 0;
_vm->_scrollCol = 0;
return true;
}
_vm->_buffer1.moveBufferRight();
_vm->_room->buildColumn(_vm->_scrollCol, 0);
} while (!_vm->shouldQuit() && (_vm->_scrollX < 0));
return false;
}
return true;
}
void Player::synchronize(Common::Serializer &s) {
s.syncAsUint16LE(_roomNumber);
s.syncAsSint16LE(_rawPlayerLow.x);
s.syncAsSint16LE(_rawPlayer.x);
s.syncAsSint16LE(_rawPlayerLow.y);
s.syncAsSint16LE(_rawPlayer.y);
}
} // End of namespace Access

159
engines/access/player.h Normal file
View File

@@ -0,0 +1,159 @@
/* 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/>.
*
*/
#ifndef ACCESS_PLAYER_H
#define ACCESS_PLAYER_H
#include "common/scummsys.h"
#include "common/rect.h"
#include "common/serializer.h"
#include "access/asurface.h"
#include "access/data.h"
namespace Access {
enum Direction {
NONE = 0,
UP = 1,
DOWN = 2,
LEFT = 3,
RIGHT = 4,
UPRIGHT = 5,
DOWNRIGHT = 6,
UPLEFT = 7,
DOWNLEFT = 8
};
class AccessEngine;
class Player : public ImageEntry, public Manager {
protected:
int _leftDelta, _rightDelta;
int _upDelta, _downDelta;
int _scrollConst;
int _sideWalkMin, _sideWalkMax;
int _upWalkMin, _upWalkMax;
int _downWalkMin, _downWalkMax;
int _diagUpWalkMin, _diagUpWalkMax;
int _diagDownWalkMin, _diagDownWalkMax;
SpriteResource *_playerSprites1;
int _scrollEnd;
int _inactiveYOff;
int _jetpackFlag;
void plotCom(int v1);
void plotCom0();
void plotCom1();
void plotCom2();
void plotCom3();
void walkUp();
void walkDown();
void walkLeft();
void walkRight();
void walkUpLeft();
void walkDownLeft();
void walkUpRight();
void walkDownRight();
void checkScrollUp();
bool isMMHover() const;
void jetpack();
public:
Direction _playerDirection;
SpriteResource *_playerSprites;
// Fields in original Player structure
byte *_manPal1;
int *_walkOffRight;
int *_walkOffLeft;
int *_walkOffUp;
int *_walkOffDown;
Common::Point *_walkOffUR;
Common::Point *_walkOffDR;
Common::Point *_walkOffUL;
Common::Point *_walkOffDL;
byte _rawTempL;
int _rawXTemp;
byte _rawYTempL;
int _rawYTemp;
Common::Point _playerOffset;
int _playerXLow;
int _playerX;
int _playerYLow;
int _playerY;
int _frame;
int _xFlag, _yFlag;
Direction _move;
// Additional public globals we've added to new Player class
bool _playerOff;
bool _playerMove;
Common::Point _moveTo;
bool _collideFlag;
bool _scrollFlag;
int _scrollThreshold;
int _scrollAmount;
// Additional globals that need to be saved
int _roomNumber;
Common::Point _rawPlayerLow;
Common::Point _rawPlayer;
public:
Player(AccessEngine *vm);
virtual ~Player();
static Player *init(AccessEngine *vm);
virtual void load();
void loadTexPalette();
void loadSprites(const Common::Path &name);
void freeSprites();
void removeSprite1();
void calcManScale();
void extracted();
void walk();
void calcPlayer();
bool scrollUp(int forcedAmount = -1);
bool scrollDown(int forcedAmount = -1);
bool scrollLeft(int forcedAmount = -1);
bool scrollRight(int forcedAmount = -1);
void checkScroll();
void checkMove();
/**
* Synchronize savegame data
*/
void synchronize(Common::Serializer &s);
};
} // End of namespace Access
#endif /* ACCESS_PLAYER_H */

View File

@@ -0,0 +1,241 @@
/* 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 "access/resources.h"
#include "access/access.h"
#include "access/amazon/amazon_resources.h"
#include "access/martian/martian_resources.h"
#include "common/translation.h"
namespace Access {
Resources *Resources::init(AccessEngine *vm) {
if (vm->getGameID() == kGameAmazon)
return new Amazon::AmazonResources(vm);
else if (vm->getGameID() == kGameMartianMemorandum)
return new Martian::MartianResources(vm);
error("Unknown game");
}
bool Resources::load(Common::U32String &errorMessage) {
Common::File f;
Common::String filename = "access.dat";
if (!f.open(filename.c_str())) {
errorMessage = Common::U32String::format(_("Unable to locate the '%s' engine data file."), filename.c_str());
return false;
}
// Check for the magic identifier
char buffer[4];
f.read(buffer, 4);
if (strncmp(buffer, "SVMA", 4)) {
errorMessage = Common::U32String::format(_("The '%s' engine data file is corrupt."), filename.c_str());
return false;
}
// Validate the version number
uint expectedVersion = 2;
uint version = f.readUint16LE();
if (version != expectedVersion) {
errorMessage = Common::U32String::format(
_("Incorrect version of the '%s' engine data file found. Expected %d.%d but got %d.%d."),
filename.c_str(), expectedVersion, 0, version, 0);
return false;
}
// Load in the index
uint count = f.readUint16LE();
_datIndex.resize(count);
for (auto &datEntry : _datIndex) {
datEntry._gameId = f.readByte();
datEntry._discType = f.readByte();
datEntry._demoType = f.readByte();
byte language = f.readByte();
switch (language) {
case 0:
datEntry._language = (Common::Language)0;
break;
case 5:
datEntry._language = Common::EN_ANY;
break;
case 23:
datEntry._language = Common::ES_ESP;
break;
default:
error("Unknown language");
break;
}
datEntry._fileOffset = f.readUint32LE();
}
// Load in the data for the game
load(f);
return true;
}
void Resources::load(Common::SeekableReadStream &s) {
uint count;
// Get the offset of the data for the game
uint entryOffset = findEntry(_vm->getGameID(), _vm->isCD() ? 1 : 0,
_vm->isDemo() ? 1 : 0, _vm->getLanguage());
s.seek(entryOffset);
// Load filename list
count = s.readUint16LE();
FILENAMES.resize(count);
for (uint idx = 0; idx < count; ++idx)
FILENAMES[idx] = s.readString();
// Load the character data
count = s.readUint16LE();
CHARTBL.resize(count);
for (uint idx = 0; idx < count; ++idx) {
uint count2 = s.readUint16LE();
CHARTBL[idx].resize(count2);
if (count2 > 0)
s.read(&CHARTBL[idx][0], count2);
}
// Load the room data
count = s.readUint16LE();
ROOMTBL.resize(count);
for (uint idx = 0; idx < count; ++idx) {
ROOMTBL[idx]._desc = s.readString();
ROOMTBL[idx]._travelPos.x = s.readSint16LE();
ROOMTBL[idx]._travelPos.y = s.readSint16LE();
uint count2 = s.readUint16LE();
ROOMTBL[idx]._data.resize(count2);
if (count2 > 0)
s.read(&ROOMTBL[idx]._data[0], count2);
}
// Load the deaths list
count = s.readUint16LE();
DEATHS.resize(count);
for (uint idx = 0; idx < count; ++idx) {
DEATHS[idx]._screenId = s.readByte();
DEATHS[idx]._msg = s.readString();
}
// Load in the inventory list
count = s.readUint16LE();
INVENTORY.resize(count);
for (uint idx = 0; idx < count; ++idx) {
INVENTORY[idx]._desc = s.readString();
for (uint idx2 = 0; idx2 < 4; ++idx2)
INVENTORY[idx]._combo[idx2] = s.readSint16LE();
}
}
uint Resources::findEntry(byte gameId, byte discType, byte demoType, Common::Language language) {
for (const DATEntry &de : _datIndex) {
if (de._gameId == gameId && de._discType == discType &&
de._demoType == demoType && de._language == language)
return de._fileOffset;
}
error("Could not locate appropriate access.dat entry");
}
/*------------------------------------------------------------------------*/
const byte INITIAL_PALETTE[18 * 3] = {
0x00, 0x00, 0x00,
0xff, 0xff, 0xff,
0xf0, 0xf0, 0xf0,
0xe0, 0xe0, 0xe0,
0xd0, 0xd0, 0xd0,
0xc0, 0xc0, 0xc0,
0xb0, 0xb0, 0xb0,
0xa0, 0xa0, 0xa0,
0x90, 0x90, 0x90,
0x80, 0x80, 0x80,
0x70, 0x70, 0x70,
0x60, 0x60, 0x60,
0x50, 0x50, 0x50,
0x40, 0x40, 0x40,
0x30, 0x30, 0x30,
0x20, 0x20, 0x20,
0x10, 0x10, 0x10,
0x00, 0x00, 0x00
};
const char *const GENERAL_MESSAGES[] = {
"LOOKING THERE REVEALS NOTHING OF INTEREST.", // LOOK_MESSAGE
"THAT DOESN'T OPEN.", // OPEN_MESSAGE
"THAT WON'T MOVE.", // MOVE_MESSAGE
"YOU CAN'T TAKE THAT.", // GET_MESSAGE
"THAT DOESN'T SEEM TO WORK.", // USE_MESSAGE
"YOU CAN'T CLIMB THAT.", // GO_MESSAGE
"THERE SEEMS TO BE NO RESPONSE.", // TALK_MESSAGE
"THIS OBJECT REQUIRES NO HINTS", // HELP_MESSAGE
"THIS OBJECT REQUIRES NO HINTS", // HELP_MESSAGE
"THAT DOESN'T SEEM TO WORK." // USE_MESSAGE
};
const char *const ESP_GENERAL_MESSAGES[] = {
"MIRANDO AHI NO ENCONTRARAS NADA DE INTERES.", // LOOK_MESSAGE
"NO ESTA ABIERTO.", // OPEN_MESSAGE
"NO PUEDES MOVERLO.", // MOVE_MESSAGE
"NO PUEDES COGER ESO.", // GET_MESSAGE
"NO PARECE QUE FUNCIONE.", // USE_MESSAGE
"NO PUEDES SUBIRTE A ESO.", // GO_MESSAGE
"PARECE QUE NO TE RESPONDE.", // TALK_MESSAGE
"NO HAY AYUDA PARA ESE OBJETO.", // HELP_MESSAGE
"NO HAY AYUDA PARA ESE OBJETO.", // HELP_MESSAGE
"NO PARECE QUE FUNCIONE." // USE_MESSAGE
};
const int INVCOORDS[][4] = {
{ 23, 68, 15, 49 },
{ 69, 114, 15, 49 },
{ 115, 160, 15, 49 },
{ 161, 206, 15, 49 },
{ 207, 252, 15, 49 },
{ 253, 298, 15, 49 },
{ 23, 68, 50, 84 },
{ 69, 114, 50, 84 },
{ 115, 160, 50, 84 },
{ 161, 206, 50, 84 },
{ 207, 252, 50, 84 },
{ 253, 298, 50, 84 },
{ 23, 68, 85, 119 },
{ 69, 114, 85, 119 },
{ 115, 160, 85, 119 },
{ 161, 206, 85, 119 },
{ 207, 252, 85, 119 },
{ 253, 298, 85, 119 },
{ 23, 68, 120, 154 },
{ 69, 114, 120, 154 },
{ 115, 160, 120, 154 },
{ 161, 206, 120, 154 },
{ 207, 252, 120, 154 },
{ 253, 298, 120, 154 },
{ 237, 298, 177, 193 },
{ 25, 85, 177, 193 }
};
} // End of namespace Access

120
engines/access/resources.h Normal file
View File

@@ -0,0 +1,120 @@
/* 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/>.
*
*/
#ifndef ACCESS_RESOURCES_H
#define ACCESS_RESOURCES_H
#include "common/scummsys.h"
#include "common/array.h"
#include "common/language.h"
#include "common/path.h"
#include "common/rect.h"
#include "common/str-array.h"
#include "common/stream.h"
namespace Access {
extern const byte INITIAL_PALETTE[18 * 3];
extern const char *const GENERAL_MESSAGES[];
extern const char *const ESP_GENERAL_MESSAGES[];
extern const int INVCOORDS[][4];
class AccessEngine;
class Resources {
struct DATEntry {
byte _gameId;
byte _discType;
byte _demoType;
Common::Language _language;
uint _fileOffset;
};
struct DeathEntry {
byte _screenId;
Common::String _msg;
};
struct InventoryEntry {
Common::String _desc;
int _combo[4];
};
protected:
struct RoomEntry {
Common::String _desc;
Common::Point _travelPos;
Common::Array<byte> _data;
};
AccessEngine *_vm;
Common::Array<DATEntry> _datIndex;
/**
* Locate a specified entry in the index and return it's file offset
*/
uint findEntry(byte gameId, byte discType, byte demoType, Common::Language language);
/**
* Load data from the access.dat file
*/
virtual void load(Common::SeekableReadStream &s);
public:
Common::Array<Common::Path> FILENAMES;
Common::Array< Common::Array<byte> > CHARTBL;
Common::Array<RoomEntry> ROOMTBL;
Common::Array<DeathEntry> DEATHS;
Common::Array<InventoryEntry> INVENTORY;
Common::String CANT_GET_THERE;
public:
Resources(AccessEngine *vm) : _vm(vm) {}
virtual ~Resources() {}
static Resources *init(AccessEngine *vm);
/**
* Load the access.dat file
*/
bool load(Common::U32String &errorMessage);
/**
* Get the raw data for the given cursor number
*/
virtual const byte *getCursor(int num) const = 0;
/**
* Get the name of the lead character
*/
virtual const char *getEgoName() const = 0;
/**
* Get the room mouse values
*/
virtual int getRMouse(int i, int j) const = 0;
/**
* Find if the mouse X is inside the range for a button,
* or -1 if no button range matches.
*/
virtual int inButtonXRange(int x) const = 0;
};
} // End of namespace Access
#endif /* ACCESS_RESOURCES_H */

1014
engines/access/room.cpp Normal file

File diff suppressed because it is too large Load Diff

207
engines/access/room.h Normal file
View File

@@ -0,0 +1,207 @@
/* 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/>.
*
*/
#ifndef ACCESS_ROOM_H
#define ACCESS_ROOM_H
#include "common/scummsys.h"
#include "common/array.h"
#include "common/rect.h"
#include "access/data.h"
#define TILE_WIDTH 16
#define TILE_HEIGHT 16
namespace Access {
class Plotter {
public:
Common::Array<Common::Rect> _walls;
Common::Array<Common::Rect> _blocks;
int _blockIn;
int _delta;
public:
Plotter();
void load(Common::SeekableReadStream *stream, int wallCount, int blockCount);
};
class JetFrame {
public:
int _wallCode;
int _wallCodeOld;
int _wallCode1;
int _wallCode1Old;
JetFrame() {
_wallCode = _wallCodeOld = 0;
_wallCode1 = _wallCode1Old = 0;
}
};
enum Function { FN_NONE = 0, FN_CLEAR1 = 1, FN_CLEAR2 = 2, FN_RELOAD = 3, FN_BREAK = 4 };
class Room : public Manager {
private:
void roomLoop();
void loadPlayField(int fileNum, int subfile);
void commandOff();
void swapOrg();
int calcLR(int yp);
int calcUD(int xp);
void takePicture();
/**
* Cycles forwards or backwards through the list of commands
*/
void cycleCommand(int incr);
bool checkCode(int v1, int v2);
protected:
void loadRoomData(const byte *roomData);
/**
* Free the playfield data
*/
void freePlayField();
/**
* Free tile data
*/
void freeTileData();
int checkBoxes();
int checkBoxes1(const Common::Point &pt);
int checkBoxes2(const Common::Point &pt, int start, int count);
void checkBoxes3();
int validateBox(int boxId);
/**
* Inner handler for switching to a given command mode
*/
void executeCommand(int commandId);
void clearCamera();
virtual void roomInit();
virtual void reloadRoom() = 0;
virtual void reloadRoom1() = 0;
virtual void setupRoom();
virtual void doCommands();
virtual void mainAreaClick() = 0;
virtual void walkCursor();
public:
Plotter _plotter;
Common::Array<JetFrame> _jetFrame;
Function _function;
int _roomFlag;
byte *_playField;
int _matrixSize;
int _playFieldWidth;
int _playFieldHeight;
byte *_tile;
int _selectCommand;
bool _conFlag;
int _rMouse[10][2];
public:
Room(AccessEngine *vm);
virtual ~Room();
void doRoom();
virtual void loadRoom(int roomNumber) = 0;
virtual void roomMenu() = 0;
/**
* Clear all the data used by the room
*/
virtual void clearRoom();
/**
* Builds up a game screen
*/
void buildScreen();
/**
* Draw a column of a game scene
*/
void buildColumn(int playX, int screenX);
/**
* Draw a row of a game scene
*/
void buildRow(int playY, int screenY);
virtual void init4Quads() = 0;
void setWallCodes();
bool codeWalls();
/**
* Switch to a given command mode
*/
void handleCommand(int commandId);
};
class RoomInfo {
public:
struct SoundIdent : FileIdent {
int _priority;
};
public:
int _roomFlag;
int _estIndex;
FileIdent _musicFile;
int _scaleH1;
int _scaleH2;
int _scaleN1;
FileIdent _playFieldFile;
Common::Array<CellIdent> _cells;
FileIdent _scriptFile;
FileIdent _animFile;
int _scaleI;
int _scrollThreshold;
FileIdent _paletteFile;
int _startColor;
int _numColors;
Common::Array<ExtraCell> _extraCells;
Common::Array<SoundIdent> _sounds;
public:
RoomInfo(const byte *data, int gameType, bool isCD, bool isDemo);
};
} // End of namespace Access
#endif /* ACCESS_ROOM_H */

378
engines/access/screen.cpp Normal file
View File

@@ -0,0 +1,378 @@
/* 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/algorithm.h"
#include "common/endian.h"
#include "common/rect.h"
#include "common/textconsole.h"
#include "common/system.h"
#include "graphics/paletteman.h"
#include "graphics/palette.h"
#include "access/access.h"
#include "access/screen.h"
#include "access/resources.h"
#include "access/martian/martian_resources.h"
// for frame contents debugging
//#define DEBUG_FRAME_DUMP 1
#ifdef DEBUG_FRAME_DUMP
#include "graphics/paletteman.h"
#include "image/png.h"
#include "common/path.h"
#include "common/file.h"
#endif
namespace Access {
ScreenSave::ScreenSave() : _clipWidth(0), _clipHeight(0), _windowXAdd(0), _windowYAdd(0), _scrollCol(0), _scrollRow(0), _screenYOff(0) {
}
Screen::Screen(AccessEngine *vm) : _vm(vm) {
Graphics::Screen::create(320, 200);
Common::fill(&_tempPalette[0], &_tempPalette[Graphics::PALETTE_SIZE], 0);
Common::fill(&_manPal[0], &_manPal[0x60], 0);
Common::fill(&_scaleTable1[0], &_scaleTable1[256], 0);
Common::fill(&_scaleTable2[0], &_scaleTable2[256], 0);
_savedPaletteCount = 0;
if (_vm->isCD())
_vesaMode = 0;
else
_vesaMode = 1;
_vesaCurrentWin = 0;
_currentPanel = 0;
_hideFlag = true;
_startColor = _numColors = 0;
_windowXAdd = _windowYAdd = 0;
_screenYOff = 0;
_screenChangeFlag = false;
_bufferBytesWide = _vWindowBytesWide = this->w;
_vWindowLinesTall = this->h;
_vWindowWidth = _vWindowHeight = 0;
_clipWidth = _vWindowBytesWide - 1;
_clipHeight = _vWindowLinesTall - 1;
_startCycle = 0;
_cycleStart = 0;
_endCycle = 0;
_fadeIn = false;
for (int i = 0; i < Graphics::PALETTE_SIZE; ++i) {
_rawPalette[i] = 0;
_savedPalettes[0][i] = 0;
_savedPalettes[1][i] = 0;
_tempPalette[i] = 0;
}
}
void Screen::clearScreen() {
clearBuffer();
if (_vesaMode)
_vm->_clearSummaryFlag = true;
}
void Screen::setDisplayScan() {
_clipWidth = this->w - 1;
_clipHeight = this->h - 1;
_windowXAdd = _windowYAdd = 0;
_vm->_scrollX = _vm->_scrollY = 0;
_vm->_scrollCol = _vm->_scrollRow = 0;
_bufferStart.x = _bufferStart.y = 0;
_screenYOff = 0;
}
void Screen::setPanel(int num) {
assert(num < 4);
_currentPanel = num;
_msVirtualOffset = _virtualOffsetsTable[num];
}
void Screen::update() {
if (_vm->_startup >= 0) {
if (--_vm->_startup == -1)
_fadeIn = true;
return;
}
markAllDirty();//****DEBUG****
Graphics::Screen::update();
}
void Screen::setInitialPalettte() {
Common::copy(&INITIAL_PALETTE[0], &INITIAL_PALETTE[18 * 3], _rawPalette);
Common::fill(&_rawPalette[18 * 3], &_rawPalette[Graphics::PALETTE_SIZE], 0);
g_system->getPaletteManager()->setPalette(INITIAL_PALETTE, 0, 18);
}
void Screen::setManPalette() {
// Player palette is colors 224~246
for (int i = 0; i < 0x42; i++) {
_rawPalette[672 + i] = PALETTE_6BIT_TO_8BIT(_manPal[i]);
}
}
void Screen::setIconPalette() {
// Icon palette is colors 247~255
if (_vm->getGameID() == kGameMartianMemorandum) {
for (int i = 0; i < 0x1B; i++) {
_rawPalette[741 + i] = PALETTE_6BIT_TO_8BIT(Martian::ICON_PALETTE[i]);
}
}
}
void Screen::loadPalette(int fileNum, int subfile, int srcOffset) {
Resource *res = _vm->_files->loadFile(fileNum, subfile);
const byte *palette = res->data() + srcOffset;
if (_vm->getGameID() == kGameMartianMemorandum) {
for (int i = 0; i < _numColors * 3; i++)
_rawPalette[_startColor * 3 + i] = PALETTE_6BIT_TO_8BIT(palette[i]);
} else {
// TODO: is this right for Amazon? Surely it should be converted? Maybe never used..
Common::copy(palette, palette + (_numColors * 3), &_rawPalette[_startColor * 3]);
}
delete res;
}
void Screen::setPalette() {
g_system->getPaletteManager()->setPalette(&_rawPalette[0], 0, Graphics::PALETTE_COUNT);
}
void Screen::loadRawPalette(Common::SeekableReadStream *stream) {
stream->read(&_rawPalette[0], Graphics::PALETTE_SIZE);
for (byte *p = &_rawPalette[0]; p < &_rawPalette[Graphics::PALETTE_SIZE]; ++p)
*p = PALETTE_6BIT_TO_8BIT(*p);
}
void Screen::updatePalette() {
g_system->getPaletteManager()->setPalette(&_tempPalette[0], 0, Graphics::PALETTE_COUNT);
update();
}
void Screen::savePalette() {
Common::copy(&_rawPalette[0], &_rawPalette[Graphics::PALETTE_SIZE],
&_savedPalettes[_savedPaletteCount][0]);
if (++_savedPaletteCount == 2)
_savedPaletteCount = 1;
}
void Screen::restorePalette() {
if (--_savedPaletteCount < 0)
_savedPaletteCount = 0;
Common::copy(&_savedPalettes[_savedPaletteCount][0],
&_savedPalettes[_savedPaletteCount][Graphics::PALETTE_SIZE], &_rawPalette[0]);
}
void Screen::getPalette(byte *pal) {
g_system->getPaletteManager()->grabPalette(pal, 0, 256);
}
void Screen::forceFadeOut() {
const int FADE_AMOUNT = 2;
bool repeatFlag;
byte *srcP;
int count;
do {
repeatFlag = false;
for (srcP = &_tempPalette[0], count = 0; count < Graphics::PALETTE_SIZE; ++count, ++srcP) {
int v = *srcP;
if (v) {
repeatFlag = true;
*srcP = MAX((int)*srcP - FADE_AMOUNT, 0);
}
}
updatePalette();
_vm->_events->pollEventsAndWait();
} while (repeatFlag && !_vm->shouldQuit());
}
void Screen::forceFadeIn() {
Common::fill(&_tempPalette[0], &_tempPalette[Graphics::PALETTE_SIZE], 0);
const int FADE_AMOUNT = 2;
bool repeatFlag;
do {
repeatFlag = false;
const byte *srcP = &_rawPalette[0];
byte *destP = &_tempPalette[0];
for (int idx = 0; idx < Graphics::PALETTE_SIZE; ++idx, ++srcP, ++destP) {
if (*destP != *srcP) {
repeatFlag = true;
*destP = MIN((int)*destP + FADE_AMOUNT, (int)*srcP);
}
}
updatePalette();
_vm->_events->pollEventsAndWait();
} while (repeatFlag);
}
void Screen::copyBuffer(const byte *data) {
byte *destP = (byte *)getPixels();
Common::copy(data, data + (h * w), destP);
g_system->copyRectToScreen(destP, w, 0, 0, w, h);
}
void Screen::setBufferScan() {
_clipWidth = _vWindowBytesWide - 1;
_windowXAdd = (320 - _clipWidth) >> 1;
_clipHeight = _vWindowLinesTall - 1;
_windowYAdd = (176 - _clipHeight) >> 1;
}
void Screen::setScaleTable(int scale) {
int total = 0;
for (int idx = 0; idx < ARRAYSIZE(_scaleTable1); ++idx) {
_scaleTable1[idx] = total >> 8;
_scaleTable2[idx] = total & 0xff;
total += scale;
}
}
void Screen::saveScreen() {
_screenSave._clipWidth = _clipWidth;
_screenSave._clipHeight = _clipHeight;
_screenSave._windowXAdd = _windowXAdd;
_screenSave._windowYAdd = _windowYAdd;
_screenSave._scroll.x = _vm->_scrollX;
_screenSave._scroll.y = _vm->_scrollY;
_screenSave._scrollCol = _vm->_scrollCol;
_screenSave._scrollRow = _vm->_scrollRow;
_screenSave._bufferStart.x = _bufferStart.x;
_screenSave._bufferStart.y = _bufferStart.y;
_screenSave._screenYOff = _screenYOff;
}
void Screen::restoreScreen() {
_clipWidth = _screenSave._clipWidth;
_clipHeight = _screenSave._clipHeight;
_windowXAdd = _screenSave._windowXAdd;
_windowYAdd = _screenSave._windowYAdd;
_vm->_scrollX = _screenSave._scroll.x;
_vm->_scrollY = _screenSave._scroll.y;
_vm->_scrollCol = _screenSave._scrollCol;
_vm->_scrollRow = _screenSave._scrollRow;
_bufferStart.x = _screenSave._bufferStart.x;
_bufferStart.y = _screenSave._bufferStart.y;
_screenYOff = _screenSave._screenYOff;
}
void Screen::copyBlock(const BaseSurface *src, const Common::Rect &bounds) {
Common::Rect destBounds = bounds;
destBounds.translate(_windowXAdd, _windowYAdd + _screenYOff);
copyRectToSurface(*src, destBounds.left, destBounds.top, bounds);
addDirtyRect(destBounds);
}
void Screen::restoreBlock() {
if (!_savedBounds.isEmpty())
addDirtyRect(_savedBounds);
BaseSurface::restoreBlock();
}
void Screen::drawRect() {
addDirtyRect(Common::Rect(_orgX1, _orgY1, _orgX2, _orgY2));
BaseSurface::drawRect();
}
void Screen::drawBox() {
addDirtyRect(Common::Rect(_orgX1, _orgY1, _orgX2, _orgY2));
BaseSurface::drawBox();
}
void Screen::copyBuffer(Graphics::ManagedSurface *src) {
addDirtyRect(Common::Rect(0, 0, src->w, src->h));
BaseSurface::copyBuffer(src);
}
void Screen::setPaletteCycle(int startCycle, int endCycle, int timer) {
_startCycle = _cycleStart = startCycle;
_endCycle = endCycle;
TimerEntry &te = _vm->_timers[6];
te._timer = te._initTm = timer;
te._flag++;
}
void Screen::cyclePaletteForward() {
cyclePaletteBackwards();
}
void Screen::cyclePaletteBackwards() {
if (!_vm->_timers[6]._flag) {
_vm->_timers[6]._flag++;
byte *pStart = &_rawPalette[_cycleStart * 3];
byte *pEnd = &_rawPalette[_endCycle * 3];
for (int idx = _startCycle; idx < _endCycle; ++idx) {
g_system->getPaletteManager()->setPalette(pStart, idx, 1);
pStart += 3;
if (pStart == pEnd)
pStart = &_rawPalette[_cycleStart * 3];
}
if (--_cycleStart <= _startCycle)
_cycleStart = _endCycle - 1;
g_system->updateScreen();
g_system->delayMillis(10);
}
}
void Screen::flashPalette(int step) {
// Note: Original parameter is for 64-level palette
step *= 4;
for (int i = 0; i < Graphics::PALETTE_SIZE; ++i) {
_tempPalette[i] = (byte)MIN(_rawPalette[i] + step, 255);
}
updatePalette();
_vm->_events->pollEventsAndWait();
// Ensure at least 1 frame delay at 30FPS before resetting palette
// to ensure the effect is perceptible and matches original.
_vm->_events->delay(30);
setPalette();
_vm->_events->pollEventsAndWait();
}
void Screen::dump(const char *fname) const {
#ifdef DEBUG_FRAME_DUMP
// For debugging, dump the frame contents.
::Common::DumpFile outf;
uint32 now = g_system->getMillis();
outf.open(::Common::Path(::Common::String::format("/tmp/%07d-%s.png", now, fname)));
::Image::writePNG(outf, *this, _rawPalette);
outf.close();
#endif
}
} // End of namespace Access

180
engines/access/screen.h Normal file
View File

@@ -0,0 +1,180 @@
/* 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/>.
*
*/
#ifndef ACCESS_SCREEN_H
#define ACCESS_SCREEN_H
#include "common/scummsys.h"
#include "common/rect.h"
#include "common/stream.h"
#include "graphics/screen.h"
#include "access/asurface.h"
namespace Access {
class AccessEngine;
struct ScreenSave {
int _clipWidth;
int _clipHeight;
int _windowXAdd;
int _windowYAdd;
Common::Point _scroll;
int _scrollCol;
int _scrollRow;
Common::Point _bufferStart;
int _screenYOff;
ScreenSave();
};
class Screen : public BaseSurface {
private:
AccessEngine *_vm;
byte _tempPalette[Graphics::PALETTE_SIZE];
byte _rawPalette[Graphics::PALETTE_SIZE];
byte _savedPalettes[2][Graphics::PALETTE_SIZE];
int _savedPaletteCount;
int _vesaCurrentWin;
int _currentPanel;
Common::Point _msVirtualOffset;
Common::Point _virtualOffsetsTable[4];
bool _hideFlag;
ScreenSave _screenSave;
int _startCycle;
int _cycleStart;
int _endCycle;
void updatePalette();
public:
int _vesaMode;
int _startColor, _numColors;
Common::Point _bufferStart;
int _windowXAdd, _windowYAdd;
int _screenYOff;
byte _manPal[0x60];
byte _scaleTable1[256];
byte _scaleTable2[256];
int _vWindowWidth;
int _vWindowHeight;
int _vWindowBytesWide;
int _bufferBytesWide;
int _vWindowLinesTall;
bool _screenChangeFlag;
bool _fadeIn;
public:
/**
* Updates the screen
*/
void update() override;
void copyBlock(const BaseSurface *src, const Common::Rect &bounds) override;
void restoreBlock() override;
void drawRect() override;
void drawBox() override;
void copyBuffer(Graphics::ManagedSurface *src) override;
public:
Screen(AccessEngine *vm);
~Screen() override {}
void setDisplayScan();
void setPanel(int num);
/**
* Fade out screen
*/
void forceFadeOut();
/**
* Fade in screen
*/
void forceFadeIn();
void fadeOut() { forceFadeOut(); }
void fadeIn() { forceFadeIn(); }
void clearScreen();
/**
* Set the initial palette
*/
void setInitialPalettte();
/**
* Set icon palette
*/
void setIconPalette();
/**
* Set Tex palette (Martian Memorandum)
*/
void setManPalette();
void loadPalette(int fileNum, int subfile, int srcOffset = 0);
void setPalette();
void loadRawPalette(Common::SeekableReadStream *stream);
void savePalette();
void restorePalette();
void getPalette(byte *pal);
void flashPalette(int step);
/**
* Copy a buffer to the screen
*/
void copyBuffer(const byte *data);
void setBufferScan();
void setScaleTable(int scale);
/**
* Save all the screen display state variables
*/
void saveScreen();
/**
* Restores previously saved screen display state variables
*/
void restoreScreen();
void setPaletteCycle(int startCycle, int endCycle, int timer);
void cyclePaletteForward();
void cyclePaletteBackwards();
void dump(const char *fname) const;
};
} // End of namespace Access
#endif /* ACCESS_SCREEN_H */

1261
engines/access/scripts.cpp Normal file

File diff suppressed because it is too large Load Diff

183
engines/access/scripts.h Normal file
View File

@@ -0,0 +1,183 @@
/* 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/>.
*
*/
#ifndef ACCESS_SCRIPTS_H
#define ACCESS_SCRIPTS_H
#include "common/scummsys.h"
#include "common/memstream.h"
#include "access/data.h"
namespace Access {
class AccessEngine;
class Scripts;
#define SCRIPT_START_BYTE 0xE0
#define ROOM_SCRIPT 2000
#define INIT_ROOM_SCRIPT 1000
typedef void(Scripts::*ScriptMethodPtr)();
class Scripts : public Manager {
friend class Debugger;
private:
int _specialFunction;
void clearWatch();
void printWatch();
protected:
Resource *_resource;
Common::SeekableReadStream *_data;
ScriptMethodPtr COMMAND_LIST[100];
virtual void executeSpecial(int commandIndex, int param1, int param2) = 0;
virtual void executeCommand(int commandIndex);
void charLoop();
void cmdObject();
void cmdEndObject();
void cmdJumpLook();
void cmdJumpOpen();
void cmdJumpHelp();
void cmdJumpGet();
void cmdJumpMove();
void cmdJumpUse();
void cmdJumpTalk();
void cmdNull();
void cmdPrint_v1();
void cmdPrint_v2();
void cmdAnim();
void cmdSetFlag();
void cmdCheckFlag();
/**
* Jump to another script
*/
void cmdGoto();
void cmdAddScore();
void cmdSetInventory();
void cmdCheckInventory();
void cmdSetTex();
void cmdNewRoom();
void cmdConverse();
void cmdCheckFrame();
void cmdCheckAnim();
void cmdSnd();
void cmdRetNeg();
void cmdCheckLoc();
void cmdSetAnim();
void cmdDispInv_v1();
void cmdDispInv_v2();
void cmdSetAbout();
void cmdSetTimer();
void cmdCheckTimer();
void cmdJumpGoto();
void cmdSetTravel();
void cmdSetVideo();
void cmdPlayVideo();
void cmdPlotImage();
void cmdSetDisplay();
void cmdSetBuffer();
void cmdSetScroll();
void cmdSaveRect();
void cmdVideoEnded();
void cmdSetBufVid();
void cmdPlayBufVid();
void cmdRemoveLast();
void cmdDoTravel();
void cmdCheckAbout();
void cmdSpecial();
void cmdSetCycle();
void cmdCycle();
void cmdCharSpeak();
void cmdTexSpeak();
void cmdTexChoice();
void cmdWait();
void cmdSetConPos();
void cmdCheckVFrame();
void cmdJumpChoice();
void cmdReturnChoice();
void cmdClearBlock();
void cmdLoadSound();
void cmdSetVideoSound();
void cmdPlayVideoSound();
void cmdPrintWatch();
void cmdDispAbout();
void cmdPushLocation();
void cmdPushLocation_v1();
void cmdCheckTravel();
void cmdBlock();
void cmdPlayerOff();
void cmdPlayerOn();
void cmdDead();
void cmdFadeOut();
void cmdEndVideo();
void cmdHelp_v1();
void cmdHelp_v2();
void cmdCycleBack();
void cmdSetHelp();
public:
int _sequence;
bool _endFlag;
int _returnCode;
int _scriptCommand;
int _choice;
int32 _choiceStart;
Common::Point _charsOrg, _texsOrg;
public:
Scripts(AccessEngine *vm);
virtual ~Scripts();
void setOpcodes();
void setOpcodes_v2();
void setScript(Resource *data, bool restartFlag = false);
void freeScriptData();
void searchForSequence();
int executeScript();
void findNull();
void doCmdPrint_v1(const Common::String &msg);
/**
* Print a given message to the screen in a bubble box
*/
void printString(const Common::String &msg);
// Script commands that need to be public
void cmdFreeSound();
void cmdRetPos();
void converse1(int val);
};
} // End of namespace Access
#endif /* ACCESS_SCRIPTS_H */

425
engines/access/sound.cpp Normal file
View File

@@ -0,0 +1,425 @@
/* 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 "audio/mixer.h"
#include "audio/audiostream.h"
#include "audio/mididrv.h"
#include "audio/midiparser.h"
#include "audio/decoders/raw.h"
#include "audio/decoders/wave.h"
// Miles Audio
#include "audio/miles.h"
#include "access/access.h"
#include "access/sound.h"
#include "access/martian/midiparser_bemd.h"
namespace Access {
SoundManager::SoundManager(AccessEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
_effectsHandle = new Audio::SoundHandle();
}
SoundManager::~SoundManager() {
clearSounds();
delete _effectsHandle;
}
void SoundManager::clearSounds() {
debugC(1, kDebugSound, "clearSounds()");
for (auto &sound : _soundTable)
delete sound._res;
_soundTable.clear();
if (_mixer->isSoundHandleActive(*_effectsHandle))
_mixer->stopHandle(*_effectsHandle);
while (_queue.size()) {
delete _queue[0]._stream;
_queue.remove_at(0);
}
}
bool SoundManager::isSoundQueued(int soundId) const {
for (uint idx = 0; idx < _queue.size(); ++idx) {
if (_queue[idx]._soundId == soundId)
return true;
}
return false;
}
void SoundManager::loadSoundTable(int idx, int fileNum, int subfile, int priority) {
debugC(1, kDebugSound, "loadSoundTable(%d, %d, %d)", idx, fileNum, subfile);
Resource *soundResource;
if (idx >= (int)_soundTable.size())
_soundTable.resize(idx + 1);
delete _soundTable[idx]._res;
soundResource = _vm->_files->loadFile(fileNum, subfile);
_soundTable[idx]._res = soundResource;
_soundTable[idx]._priority = priority;
}
Resource *SoundManager::loadSound(int fileNum, int subfile) {
debugC(1, kDebugSound, "loadSound(%d, %d)", fileNum, subfile);
return _vm->_files->loadFile(fileNum, subfile);
}
void SoundManager::playSound(int soundIndex, bool loop) {
debugC(1, kDebugSound, "playSound(%d, %d)", soundIndex, loop);
if (isSoundQueued(soundIndex))
// Prevent duplicate copies of a sound from being queued
return;
int priority = _soundTable[soundIndex]._priority;
playSound(_soundTable[soundIndex]._res, priority, loop, soundIndex);
}
void SoundManager::playSound(Resource *res, int priority, bool loop, int soundIndex) {
debugC(1, kDebugSound, "playSound");
const byte *resourceData = res->data();
assert(res->_size >= 32);
Audio::RewindableAudioStream *audioStream;
if (READ_BE_UINT32(resourceData) == MKTAG('R','I','F','F')) {
// CD version uses WAVE-files
Common::SeekableReadStream *waveStream = new Common::MemoryReadStream(resourceData, res->_size, DisposeAfterUse::NO);
audioStream = Audio::makeWAVStream(waveStream, DisposeAfterUse::YES);
} else if (READ_BE_UINT32(resourceData) == MKTAG('S', 'T', 'E', 'V')) {
// sound files have a fixed header of 32 bytes in total
// header content:
// "STEVE" - fixed header
// byte - sample rate
// 01h mapped internally to 3Ch
// 02h mapped internally to 78h
// 03h mapped internally to B5h
// 04h mapped internally to F1h
// byte - unknown
// word - actual sample size (should be resource-size - 32)
byte internalSampleRate = resourceData[5];
int sampleSize = READ_LE_UINT16(resourceData + 7);
assert( (sampleSize + 32) <= res->_size);
int sampleRate = 0;
switch (internalSampleRate) {
case 1: // NEG(3Ch) -> C4h time constant
sampleRate = 16666;
break;
case 2: // NEG(78h) -> 88h time constant
sampleRate = 8334;
break;
case 3: // NEG(B5h) -> 4Bh time constant
sampleRate = 5525;
break;
case 4: // NEG(F1h) -> 0Fh time constant
sampleRate = 4150;
break;
default:
error("Unexpected internal Sample Rate %d", internalSampleRate);
return;
}
audioStream = Audio::makeRawStream(resourceData + 32, sampleSize, sampleRate, 0, DisposeAfterUse::NO);
} else
error("Unknown format");
if (loop) {
_queue.push_back(QueuedSound(new Audio::LoopingAudioStream(audioStream, 0,
DisposeAfterUse::NO), soundIndex));
} else {
_queue.push_back(QueuedSound(audioStream, soundIndex));
}
if (!_mixer->isSoundHandleActive(*_effectsHandle))
_mixer->playStream(Audio::Mixer::kSFXSoundType, _effectsHandle,
_queue[0]._stream, -1, _mixer->kMaxChannelVolume, 0,
DisposeAfterUse::NO);
}
void SoundManager::checkSoundQueue() {
debugC(5, kDebugSound, "checkSoundQueue");
if (_queue.empty() || _mixer->isSoundHandleActive(*_effectsHandle))
return;
delete _queue[0]._stream;
_queue.remove_at(0);
if (_queue.size() && _queue[0]._stream)
_mixer->playStream(Audio::Mixer::kSFXSoundType, _effectsHandle,
_queue[0]._stream, -1, _mixer->kMaxChannelVolume, 0,
DisposeAfterUse::NO);
}
bool SoundManager::isSFXPlaying() {
return _mixer->isSoundHandleActive(*_effectsHandle);
}
void SoundManager::syncVolume() {
int sfxVol = CLIP(ConfMan.getInt("sfx_volume"), 0, 255);
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, sfxVol);
}
void SoundManager::loadSounds(const Common::Array<RoomInfo::SoundIdent> &sounds) {
debugC(1, kDebugSound, "loadSounds");
clearSounds();
for (const auto &sound : sounds) {
Resource *soundRes = loadSound(sound._fileNum, sound._subfile);
_soundTable.push_back(SoundEntry(soundRes, sound._priority));
}
}
void SoundManager::stopSound() {
debugC(3, kDebugSound, "stopSound");
_mixer->stopHandle(*_effectsHandle);
}
void SoundManager::freeSounds() {
debugC(3, kDebugSound, "freeSounds");
stopSound();
clearSounds();
}
/******************************************************************************************/
MusicManager::MusicManager(AccessEngine *vm) : _vm(vm) {
_music = nullptr;
_tempMusic = nullptr;
_isLooping = false;
_driver = nullptr;
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MT32);
MusicType musicType = MidiDriver::getMusicType(dev);
// Amazon Guardians of Eden uses MIDPAK inside MIDIDRV.AP
// AdLib patches are inside MIDIDRV.AP too, 2nd resource file
//
// Amazon Guardians of Eden (demo) seems to use another type of driver, possibly written by Access themselves
// Martian Memorandum uses this other type of driver as well, which means it makes sense to reverse engineer it.
//
switch (musicType) {
case MT_ADLIB: {
if (_vm->getGameID() == kGameAmazon && !_vm->isDemo()) {
Resource *midiDrvResource = _vm->_files->loadFile(92, 1);
Common::MemoryReadStream *adLibInstrumentStream = new Common::MemoryReadStream(midiDrvResource->data(), midiDrvResource->_size);
_driver = Audio::MidiDriver_Miles_AdLib_create("", "", adLibInstrumentStream);
delete midiDrvResource;
delete adLibInstrumentStream;
} else {
MidiPlayer::createDriver();
}
break;
}
case MT_MT32:
_driver = Audio::MidiDriver_Miles_MT32_create("");
_nativeMT32 = true;
break;
case MT_GM:
if (ConfMan.getBool("native_mt32")) {
_driver = Audio::MidiDriver_Miles_MT32_create("");
_nativeMT32 = true;
}
break;
default:
break;
}
#if 0
MidiPlayer::createDriver();
MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
#endif
if (_driver) {
int retValue = _driver->open();
if (retValue == 0) {
if (_nativeMT32)
_driver->sendMT32Reset();
else
_driver->sendGMReset();
_driver->setTimerCallback(this, &timerCallback);
}
}
}
MusicManager::~MusicManager() {
delete _music;
delete _tempMusic;
}
void MusicManager::send(uint32 b) {
// Pass data directly to driver
_driver->send(b);
#if 0
if ((b & 0xF0) == 0xC0 && !_nativeMT32) {
b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
}
Audio::MidiPlayer::send(b);
#endif
}
void MusicManager::midiPlay() {
debugC(1, kDebugSound, "midiPlay");
if (!_driver)
return;
if (_music->_size < 4) {
error("midiPlay() wrong music resource size");
}
stop();
uint32 magic = READ_BE_UINT32(_music->data());
if (magic == MKTAG('B', 'E', 'm', 'd')) {
_parser = new MidiParser_BEmd();
if (!_parser->loadMusic(_music->data(), _music->_size))
error("midiPlay() couldn't load music resource");
_parser->setTrack(0);
_parser->setMidiDriver(this);
_parser->setTimerRate(_driver->getBaseTempo());
_parser->property(MidiParser::mpAutoLoop, _isLooping);
syncVolume();
_isPlaying = true;
} else if (magic == MKTAG('F', 'O', 'R', 'M')) {
_parser = MidiParser::createParser_XMIDI();
if (!_parser->loadMusic(_music->data(), _music->_size))
error("midiPlay() wrong music resource");
_parser->setTrack(0);
_parser->setMidiDriver(this);
_parser->setTimerRate(_driver->getBaseTempo());
_parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
_parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1);
// Handle music looping
_parser->property(MidiParser::mpAutoLoop, _isLooping);
syncVolume();
_isPlaying = true;
} else {
warning("midiPlay() Unexpected signature 0x%08x, expected 'FORM'", magic);
_isPlaying = false;
}
}
bool MusicManager::checkMidiDone() {
debugC(1, kDebugSound, "checkMidiDone");
return (!_isPlaying);
}
void MusicManager::midiRepeat() {
debugC(1, kDebugSound, "midiRepeat");
if (!_driver)
return;
if (!_parser)
return;
_isLooping = true;
_parser->property(MidiParser::mpAutoLoop, _isLooping);
if (!_isPlaying)
_parser->setTrack(0);
}
void MusicManager::stopSong() {
debugC(1, kDebugSound, "stopSong");
if (!_driver)
return;
stop();
}
void MusicManager::loadMusic(int fileNum, int subfile) {
debugC(1, kDebugSound, "loadMusic(%d, %d)", fileNum, subfile);
_music = _vm->_files->loadFile(fileNum, subfile);
}
void MusicManager::loadMusic(FileIdent file) {
debugC(1, kDebugSound, "loadMusic(%d, %d)", file._fileNum, file._subfile);
_music = _vm->_files->loadFile(file);
}
void MusicManager::newMusic(int musicId, int mode) {
debugC(1, kDebugSound, "newMusic(%d, %d)", musicId, mode);
if (!_driver)
return;
if (mode == 1) {
stopSong();
freeMusic();
_music = _tempMusic;
_tempMusic = nullptr;
_isLooping = true;
} else {
_isLooping = (mode == 2);
_tempMusic = _music;
stopSong();
loadMusic(97, musicId);
}
if (_music)
midiPlay();
}
void MusicManager::freeMusic() {
debugC(3, kDebugSound, "freeMusic");
delete _music;
_music = nullptr;
}
void MusicManager::setLoop(bool loop) {
debugC(3, kDebugSound, "setLoop");
_isLooping = loop;
if (_parser)
_parser->property(MidiParser::mpAutoLoop, _isLooping);
}
} // End of namespace Access

122
engines/access/sound.h Normal file
View File

@@ -0,0 +1,122 @@
/* 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/>.
*
*/
#ifndef ACCESS_SOUND_H
#define ACCESS_SOUND_H
#include "common/scummsys.h"
#include "access/files.h"
#include "audio/midiplayer.h"
#define MAX_SOUNDS 20
namespace Audio {
class AudioStream;
class SoundHandle;
}
namespace Access {
class AccessEngine;
struct SoundEntry {
Resource *_res;
int _priority;
SoundEntry() { _res = nullptr; _priority = 0; }
SoundEntry(Resource *res, int priority) { _res = res; _priority = priority; }
};
class SoundManager {
struct QueuedSound {
Audio::AudioStream *_stream;
int _soundId;
QueuedSound() : _stream(nullptr), _soundId(-1) {}
QueuedSound(Audio::AudioStream *stream, int soundId) : _stream(stream), _soundId(soundId) {}
};
private:
AccessEngine *_vm;
Audio::Mixer *_mixer;
Audio::SoundHandle *_effectsHandle;
Common::Array<QueuedSound> _queue;
void clearSounds();
void playSound(Resource *res, int priority, bool loop, int soundIndex = -1);
bool isSoundQueued(int soundId) const;
public:
Common::Array<SoundEntry> _soundTable;
public:
SoundManager(AccessEngine *vm, Audio::Mixer *mixer);
~SoundManager();
void loadSoundTable(int idx, int fileNum, int subfile, int priority = 1);
void playSound(int soundIndex, bool loop = false);
void checkSoundQueue();
bool isSFXPlaying();
Resource *loadSound(int fileNum, int subfile);
void loadSounds(const Common::Array<RoomInfo::SoundIdent> &sounds);
void syncVolume();
void stopSound();
void freeSounds();
};
class MusicManager : public Audio::MidiPlayer {
private:
AccessEngine *_vm;
Resource *_tempMusic;
// MidiDriver_BASE interface implementation
void send(uint32 b) override;
public:
Resource *_music;
public:
MusicManager(AccessEngine *vm);
~MusicManager() override;
void midiPlay();
bool checkMidiDone();
void midiRepeat();
void stopSong();
void newMusic(int musicId, int mode);
void freeMusic();
void loadMusic(int fileNum, int subfile);
void loadMusic(FileIdent file);
void setLoop(bool loop);
};
} // End of namespace Access
#endif /* ACCESS_SOUND_H*/

209
engines/access/video.cpp Normal file
View File

@@ -0,0 +1,209 @@
/* 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 "access/video.h"
#include "access/access.h"
namespace Access {
VideoPlayer::VideoPlayer(AccessEngine *vm) : Manager(vm) {
_vidSurface = nullptr;
_videoData = nullptr;
_startCoord = nullptr;
_frameCount = 0;
_xCount = 0;
_scanCount = 0;
_frameSize = 0;
_videoFrame = 0;
_soundFlag = false;
_soundFrame = 0;
_videoEnd = false;
_header._frameCount = 0;
_header._width = _header._height = 0;
_header._flags = VIDEOFLAG_NONE;
}
VideoPlayer::~VideoPlayer() {
closeVideo();
}
void VideoPlayer::setVideo(BaseSurface *vidSurface, const Common::Point &pt, int rate) {
_vidSurface = vidSurface;
vidSurface->_orgX1 = pt.x;
vidSurface->_orgY1 = pt.y;
_vm->_timers[31]._timer = rate;
_vm->_timers[31]._initTm = rate;
// Load in header
_header._frameCount = _videoData->_stream->readUint16LE();
_header._width = _videoData->_stream->readUint16LE();
_header._height = _videoData->_stream->readUint16LE();
_videoData->_stream->skip(1);
_header._flags = (VideoFlags)_videoData->_stream->readByte();
_startCoord = (byte *)vidSurface->getBasePtr(pt.x, pt.y);
_frameCount = _header._frameCount - 2;
_xCount = _header._width;
_scanCount = _header._height;
_videoFrame = 0;
_videoBounds = Common::Rect(pt.x, pt.y, pt.x + _header._width, pt.y + _header._height);
getFrame();
if (_header._flags == VIDEOFLAG_BG) {
// Draw the background
for (int y = 0; y < _scanCount; ++y) {
byte *pDest = (byte *)vidSurface->getBasePtr(pt.x, pt.y + y);
_videoData->_stream->read(pDest, _xCount);
}
if (vidSurface == _vm->_screen) {
assert(pt.x >= 0 && pt.y >= 0 && pt.x < 320 && pt.y < 200 &&
_xCount > 0 && _xCount <= 320 && _scanCount > 0 && _scanCount <= 200);
_vm->_newRects.push_back(Common::Rect(pt.x, pt.y, pt.x + _xCount, pt.y + _scanCount));
}
getFrame();
}
_videoEnd = false;
}
void VideoPlayer::setVideo(BaseSurface *vidSurface, const Common::Point &pt, const Common::Path &filename, int rate) {
// Open up video stream
_videoData = _vm->_files->loadFile(filename);
setVideo(vidSurface, pt, rate);
}
void VideoPlayer::setVideo(BaseSurface *vidSurface, const Common::Point &pt, const FileIdent &videoFile, int rate) {
// Open up video stream
_videoData = _vm->_files->loadFile(videoFile);
setVideo(vidSurface, pt, rate);
}
void VideoPlayer::closeVideo() {
delete _videoData;
_videoData = nullptr;
}
void VideoPlayer::getFrame() {
_frameSize = _videoData->_stream->readUint16LE();
}
void VideoPlayer::playVideo() {
if (_vm->_timers[31]._flag)
return;
_vm->_timers[31]._flag = 1;
byte *pDest = _startCoord;
byte *pLine = _startCoord;
uint32 frameEnd = _videoData->_stream->pos() + _frameSize;
if (frameEnd > _videoData->_stream->size())
error("VideoPlayer::playVideo: Frame end %d > stream size %d", frameEnd, (int)_videoData->_stream->size());
while ((uint32)_videoData->_stream->pos() < frameEnd) {
int count = _videoData->_stream->readByte();
if (count & 0x80) {
count &= 0x7f;
// Skip count number of pixels
// Loop across lines if necessary
while (count >= (pLine + _xCount - pDest)) {
count -= (pLine + _xCount - pDest);
pLine += _vidSurface->pitch;
pDest = pLine;
}
// Skip any remaining pixels in the new line
pDest += count;
} else {
// Read count number of pixels
// Load across lines if necessary
while (count >= (pLine + _xCount - pDest)) {
int lineCount = (pLine + _xCount - pDest);
_videoData->_stream->read(pDest, lineCount);
count -= lineCount;
pLine += _vidSurface->pitch;
pDest = pLine;
}
// Load remainder of pixels on line
if (count > 0) {
_videoData->_stream->read(pDest, count);
pDest += count;
}
}
}
// If the video is playing on the screen surface, add a dirty rect
if (_vidSurface == _vm->_screen)
_vm->_screen->markAllDirty();
_vm->_screen->dump("vidframe");
getFrame();
if (++_videoFrame == _frameCount) {
closeVideo();
_videoEnd = true;
}
}
void VideoPlayer::copyVideo() {
// aka drawTalkVideoFrame
_vm->_player->calcPlayer();
// Figure out the dirty rect area for the video frame
Common::Rect r = Common::Rect(_vm->_vidX - _vm->_screen->_bufferStart.x,
_vm->_vidY - _vm->_screen->_bufferStart.y,
_vm->_vidX - _vm->_screen->_bufferStart.x + _header._width,
_vm->_vidY - _vm->_screen->_bufferStart.y + _header._height);
if (_vm->_screen->clip(r))
return;
assert(r.left >= 0 && r.left < 320 && r.top >= 0 && r.top < 200 &&
r.right > 0 && r.right <= 320 && r.bottom > 0 && r.bottom <= 200);
_vm->_newRects.push_back(r);
// Draw the clipped video to the buffer
int vh = r.height();
int vw = r.width();
int destIdx = r.left + r.top * _vm->_buffer2.pitch;
int srcIdx = _vm->_screen->_leftSkip + _vm->_screen->_topSkip * _vm->_vidBuf.pitch;
assert(srcIdx >= 0);
assert(destIdx >= 0);
const byte *srcP = (const byte *)_vm->_vidBuf.getPixels() + srcIdx;
byte *destP = (byte *)_vm->_buffer2.getPixels() + destIdx;
for (int i = 0; i < vh; i++) {
Common::copy(srcP, srcP + vw, destP);
srcP += _vm->_vidBuf.pitch;
destP += _vm->_buffer2.pitch;
}
}
} // End of namespace Access

83
engines/access/video.h Normal file
View File

@@ -0,0 +1,83 @@
/* 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/>.
*
*/
#ifndef ACCESS_VIDEO_H
#define ACCESS_VIDEO_H
#include "common/scummsys.h"
#include "common/memstream.h"
#include "access/data.h"
#include "access/asurface.h"
#include "access/files.h"
namespace Access {
enum VideoFlags { VIDEOFLAG_NONE = 0, VIDEOFLAG_BG = 1 };
class VideoPlayer : public Manager {
struct VideoHeader {
int _frameCount;
int _width, _height;
VideoFlags _flags;
};
private:
BaseSurface *_vidSurface;
Resource *_videoData;
VideoHeader _header;
byte *_startCoord;
int _frameCount;
int _xCount;
int _scanCount;
int _frameSize;
Common::Rect _videoBounds;
void getFrame();
void setVideo(BaseSurface *vidSurface, const Common::Point &pt, int rate);
public:
int _videoFrame;
bool _soundFlag;
int _soundFrame;
bool _videoEnd;
public:
VideoPlayer(AccessEngine *vm);
~VideoPlayer();
/**
* Start up a video
*/
void setVideo(BaseSurface *vidSurface, const Common::Point &pt, const FileIdent &videoFile, int rate);
void setVideo(BaseSurface *vidSurface, const Common::Point &pt, const Common::Path &filename, int rate);
/**
* Decodes a frame of the video
*/
void playVideo();
void copyVideo();
/**
* Frees the data for a previously loaded video
*/
void closeVideo();
};
} // End of namespace Access
#endif /* ACCESS_VIDEO_H */

View File

@@ -0,0 +1,738 @@
/* 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/scummsys.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "audio/audiostream.h"
#include "audio/decoders/raw.h"
#include "access/access.h"
#include "access/video/movie_decoder.h"
// for Test-Code
#include "common/system.h"
#include "common/events.h"
#include "common/keyboard.h"
#include "engines/engine.h"
#include "engines/util.h"
#include "graphics/paletteman.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
namespace Access {
AccessVIDMovieDecoder::AccessVIDMovieDecoder()
: _stream(nullptr), _videoTrack(nullptr), _audioTrack(nullptr) {
_streamSeekOffset = 0;
_streamVideoIndex = 0;
_streamAudioIndex = 0;
}
AccessVIDMovieDecoder::~AccessVIDMovieDecoder() {
AccessVIDMovieDecoder::close();
}
bool AccessVIDMovieDecoder::loadStream(Common::SeekableReadStream *stream) {
uint32 videoCodecTag = 0;
uint32 videoHeight = 0;
uint32 videoWidth = 0;
uint16 regularDelay = 0;
uint32 audioSampleRate = 0;
close();
_stream = stream;
_streamSeekOffset = 15; // offset of first chunk
_streamVideoIndex = 0;
_streamAudioIndex = 0;
// read header
// ID [dword] "VID"
// ?? [byte]
// ?? [word]
// width [word]
// height [word]
// regular delay between frames (60 per second) [word]
// ?? [word]
videoCodecTag = _stream->readUint32BE();
if (videoCodecTag != MKTAG('V','I','D',0x00)) {
warning("AccessVIDMoviePlay: bad codec tag, not a video file?");
close();
return false;
}
_stream->skip(3);
videoWidth = _stream->readUint16LE();
videoHeight = _stream->readUint16LE();
regularDelay = _stream->readUint16LE();
_stream->skip(2);
if (!regularDelay) {
warning("AccessVIDMoviePlay: delay between frames is zero?");
close();
return false;
}
// create video track
_videoTrack = new StreamVideoTrack(videoWidth, videoHeight, regularDelay);
addTrack(_videoTrack);
//warning("width %d, height %d", videoWidth, videoHeight);
// Look through the first few packets
static const int maxPacketCheckCount = 10;
for (int i = 0; i < maxPacketCheckCount; i++) {
byte chunkId = _stream->readByte();
// Bail out if done
if (_stream->eos())
break;
// Bail also in case end of file chunk was found
if (chunkId == kVIDMovieChunkId_EndOfFile)
break;
uint32 chunkStartOffset = _stream->pos();
//warning("data chunk at %x", chunkStartOffset);
switch (chunkId) {
case kVIDMovieChunkId_FullFrame:
case kVIDMovieChunkId_FullFrameCompressed:
case kVIDMovieChunkId_PartialFrameCompressed:
case kVIDMovieChunkId_FullFrameCompressedFill: {
if (!_videoTrack->skipOverFrame(_stream, chunkId)) {
close();
return false;
}
break;
}
case kVIDMovieChunkId_Palette: {
if (!_videoTrack->skipOverPalette(_stream)) {
close();
return false;
}
break;
}
case kVIDMovieChunkId_AudioFirstChunk:
case kVIDMovieChunkId_Audio: {
// sync [word]
// sampling rate [byte]
// size of audio data [word]
// sample data [] (mono, 8-bit, unsigned)
//
// Only first chunk has sync + sampling rate
if (chunkId == kVIDMovieChunkId_AudioFirstChunk) {
byte soundblasterRate;
_stream->skip(2); // skip over sync
soundblasterRate = _stream->readByte();
audioSampleRate = 1000000 / (256 - soundblasterRate);
_audioTrack = new StreamAudioTrack(audioSampleRate, getSoundType());
addTrack(_audioTrack);
_stream->seek(chunkStartOffset); // seek back
}
if (!_audioTrack) {
warning("AccessVIDMoviePlay: regular audio chunk, before audio chunk w/ header");
close();
return false;
}
if (!_audioTrack->skipOverAudio(_stream, chunkId)) {
close();
return false;
}
break;
}
default:
warning("AccessVIDMoviePlay: Unknown chunk-id '%x' inside VID movie", chunkId);
close();
return false;
}
// Remember this chunk inside our cache
IndexCacheEntry indexCacheEntry;
indexCacheEntry.chunkId = chunkId;
indexCacheEntry.offset = chunkStartOffset;
_indexCacheTable.push_back(indexCacheEntry);
// Got an audio chunk now? -> exit b/c we are done
if (audioSampleRate)
break;
}
// Remember offset of latest not-indexed-yet chunk
_streamSeekOffset = _stream->pos();
// If sample rate was found, create an audio track
if (audioSampleRate) {
_audioTrack = new StreamAudioTrack(audioSampleRate, getSoundType());
addTrack(_audioTrack);
}
// Rewind back to the beginning right to the first chunk
_stream->seek(15);
return true;
}
void AccessVIDMovieDecoder::close() {
Video::VideoDecoder::close();
delete _stream; _stream = nullptr;
_videoTrack = nullptr;
_indexCacheTable.clear();
}
// We try to at least decode 1 frame
// and also try to get at least 0.5 seconds of audio queued up
void AccessVIDMovieDecoder::readNextPacket() {
uint32 currentMovieTime = getTime();
uint32 wantedAudioQueued = currentMovieTime + 500; // always try to be 0.500 seconds in front of movie time
uint32 streamIndex = 0;
IndexCacheEntry indexEntry;
bool currentlySeeking = false;
bool videoDone = false;
bool audioDone = false;
// Seek to smallest stream offset
if ((_streamVideoIndex <= _streamAudioIndex) || (!_audioTrack)) {
streamIndex = _streamVideoIndex;
} else {
streamIndex = _streamAudioIndex;
}
if (_audioTrack) {
if (wantedAudioQueued <= _audioTrack->getTotalAudioQueued()) {
// already got enough audio queued up
audioDone = true;
}
} else {
// no audio track, audio is always done
audioDone = true;
}
while (1) {
// Check, if stream-index is already cached
if (streamIndex < _indexCacheTable.size()) {
indexEntry.chunkId = _indexCacheTable[streamIndex].chunkId;
indexEntry.offset = _indexCacheTable[streamIndex].offset;
currentlySeeking = false;
} else {
// read from file
_stream->seek(_streamSeekOffset);
indexEntry.chunkId = _stream->readByte();
indexEntry.offset = _stream->pos();
currentlySeeking = true;
// and store that as well
_indexCacheTable.push_back(indexEntry);
}
// end of stream -> exit
if (_stream->eos())
break;
// end of file chunk -> exit
if (indexEntry.chunkId == kVIDMovieChunkId_EndOfFile)
break;
// warning("chunk %x", indexEntry.chunkId);
switch (indexEntry.chunkId) {
case kVIDMovieChunkId_FullFrame:
case kVIDMovieChunkId_FullFrameCompressed:
case kVIDMovieChunkId_PartialFrameCompressed:
case kVIDMovieChunkId_FullFrameCompressedFill: {
if ((_streamVideoIndex <= streamIndex) && (!videoDone)) {
// We are at an index, that is still relevant for video decoding
// and we are not done with video yet
if (!currentlySeeking) {
// seek to stream position in case we used the cache
_stream->seek(indexEntry.offset);
}
//warning("video decode chunk %x at %lx", indexEntry.chunkId, _stream->pos());
_videoTrack->decodeFrame(_stream, indexEntry.chunkId);
videoDone = true;
_streamVideoIndex = streamIndex + 1;
} else {
if (currentlySeeking) {
// currently seeking, so we have to skip the frame bytes manually
_videoTrack->skipOverFrame(_stream, indexEntry.chunkId);
}
}
break;
}
case kVIDMovieChunkId_Palette: {
if ((_streamVideoIndex <= streamIndex) && (!videoDone)) {
// We are at an index, that is still relevant for video decoding
// and we are not done with video yet
if (!currentlySeeking) {
// seek to stream position in case we used the cache
_stream->seek(indexEntry.offset);
}
_videoTrack->decodePalette(_stream);
_streamVideoIndex = streamIndex + 1;
} else {
if (currentlySeeking) {
// currently seeking, so we have to skip the frame bytes manually
_videoTrack->skipOverPalette(_stream);
}
}
break;
}
case kVIDMovieChunkId_AudioFirstChunk:
case kVIDMovieChunkId_Audio: {
if ((_streamAudioIndex <= streamIndex) && (!audioDone)) {
// We are at an index that is still relevant for audio decoding
if (!currentlySeeking) {
// seek to stream position in case we used the cache
_stream->seek(indexEntry.offset);
}
_audioTrack->queueAudio(_stream, indexEntry.chunkId);
_streamAudioIndex = streamIndex + 1;
if (wantedAudioQueued <= _audioTrack->getTotalAudioQueued()) {
// Got enough audio
audioDone = true;
}
} else {
if (!_audioTrack) {
error("AccessVIDMoviePlay: audio chunks found without audio track active");
}
if (currentlySeeking) {
// currently seeking, so we have to skip the audio bytes manually
_audioTrack->skipOverAudio(_stream, indexEntry.chunkId);
}
}
break;
}
default:
error("AccessVIDMoviePlay: Unknown chunk-id '%x' inside VID movie", indexEntry.chunkId);
}
if (currentlySeeking) {
// remember currently stream offset in case we are seeking
_streamSeekOffset = _stream->pos();
}
// go to next index
streamIndex++;
if ((videoDone) && (audioDone)) {
return;
}
}
if (!videoDone) {
// no more video frames? set end of video track
_videoTrack->setEndOfTrack();
}
}
AccessVIDMovieDecoder::StreamVideoTrack::StreamVideoTrack(uint32 width, uint32 height, uint16 regularFrameDelay) {
_width = width;
_height = height;
_regularFrameDelay = regularFrameDelay;
_curFrame = -1;
_nextFrameStartTime = 0;
_endOfTrack = false;
_dirtyPalette = false;
memset(&_palette, 0, sizeof(_palette));
_surface = new Graphics::Surface();
_surface->create(_width, _height, Graphics::PixelFormat::createFormatCLUT8());
}
AccessVIDMovieDecoder::StreamVideoTrack::~StreamVideoTrack() {
delete _surface;
}
bool AccessVIDMovieDecoder::StreamVideoTrack::endOfTrack() const {
return _endOfTrack;
}
Graphics::PixelFormat AccessVIDMovieDecoder::StreamVideoTrack::getPixelFormat() const {
return _surface->format;
}
void AccessVIDMovieDecoder::StreamVideoTrack::decodeFrame(Common::SeekableReadStream *stream, byte chunkId) {
byte *framePixelsPtr = (byte *)_surface->getPixels();
byte *pixelsPtr = framePixelsPtr;
byte rleByte = 0;
uint16 additionalDelay = 0;
int32 expectedPixels = 0;
switch (chunkId) {
case kVIDMovieChunkId_FullFrame: {
// Full frame is:
// data [width * height]
additionalDelay = stream->readUint16LE();
stream->read(framePixelsPtr, _width * _height);
break;
}
case kVIDMovieChunkId_FullFrameCompressed:
case kVIDMovieChunkId_PartialFrameCompressed: {
// Skip manually over compressed data
// Full frame compressed is:
// additional delay [word]
// REPEAT:
// RLE [byte]
// RLE upper bit set: skip over RLE & 0x7F pixels
// RLE upper bit not set: draw RLE amount of pixels (those pixels follow right after RLE byte)
//
// Partial frame compressed is:
// sync [word]
// horizontal start position [word]
// REPEAT:
// see full frame compressed
uint16 horizontalStartPosition = 0;
additionalDelay = stream->readUint16LE();
if (chunkId == kVIDMovieChunkId_PartialFrameCompressed) {
horizontalStartPosition = stream->readUint16LE();
if (horizontalStartPosition >= _height) {
error("AccessVIDMoviePlay: starting position larger than height during partial frame compressed, data corrupt?");
return;
}
}
expectedPixels = _width * (_height - horizontalStartPosition);
// adjust frame destination pointer
pixelsPtr += (horizontalStartPosition * _width);
while (expectedPixels >= 0) {
rleByte = stream->readByte();
if (!rleByte) // NUL means end of stream
break;
if (rleByte & 0x80) {
rleByte = rleByte & 0x7F;
expectedPixels -= rleByte;
} else {
// skip over pixels
expectedPixels -= rleByte;
stream->read(pixelsPtr, rleByte); // read pixel data into frame
}
pixelsPtr += rleByte;
}
// expectedPixels may be positive here in case stream got terminated with a NUL
if (expectedPixels < 0) {
error("AccessVIDMoviePlay: pixel count mismatch during full/partial frame compressed, data corrupt?");
}
break;
}
case kVIDMovieChunkId_FullFrameCompressedFill: {
// Full frame compressed fill is:
// additional delay [word]
// REPEAT:
// RLE [byte]
// RLE upper bit set: draw RLE amount (& 0x7F) of pixels with specified color (color byte follows after RLE byte)
// RLE upper bit not set: draw RLE amount of pixels (those pixels follow right after RLE byte)
additionalDelay = stream->readUint16LE();
expectedPixels = _width * _height;
while (expectedPixels > 0) {
rleByte = stream->readByte();
if (rleByte & 0x80) {
rleByte = rleByte & 0x7F;
expectedPixels -= rleByte;
byte fillColor = stream->readByte();
memset(pixelsPtr, fillColor, rleByte);
} else {
// skip over pixels
expectedPixels -= rleByte;
stream->read(pixelsPtr, rleByte); // read pixel data into frame
}
pixelsPtr += rleByte;
}
if (expectedPixels < 0) {
error("AccessVIDMoviePlay: pixel count mismatch during full frame compressed fill, data corrupt?");
}
break;
}
default:
assert(0);
break;
}
_curFrame++;
// TODO: not sure, if additionalDelay is supposed to affect the follow-up frame or the current frame
// the videos, that I found, don't have it set
uint32 currentFrameStartTime = getNextFrameStartTime();
uint32 nextFrameStartTime = (_regularFrameDelay * _curFrame) * 1000 / 60;
if (additionalDelay) {
nextFrameStartTime += additionalDelay * 1000 / 60;
}
assert(currentFrameStartTime <= nextFrameStartTime);
setNextFrameStartTime(nextFrameStartTime);
}
bool AccessVIDMovieDecoder::StreamVideoTrack::skipOverFrame(Common::SeekableReadStream *stream, byte chunkId) {
byte rleByte = 0;
int32 expectedPixels = 0;
switch (chunkId) {
case kVIDMovieChunkId_FullFrame: {
// Full frame is:
// additional delay [word]
// data [width * height]
stream->skip(2);
stream->skip(_width * _height);
break;
}
case kVIDMovieChunkId_FullFrameCompressed:
case kVIDMovieChunkId_PartialFrameCompressed: {
// Skip manually over compressed data
// Full frame compressed is:
// additional delay [word]
// REPEAT:
// RLE [byte]
// RLE upper bit set: skip over RLE & 0x7F pixels
// RLE upper bit not set: draw RLE amount of pixels (those pixels follow right after RLE byte)
//
// Partial frame compressed is:
// sync [word]
// horizontal start position [word]
// REPEAT:
// see full frame compressed
uint16 horizontalStartPosition = 0;
stream->skip(2);
if (chunkId == kVIDMovieChunkId_PartialFrameCompressed) {
horizontalStartPosition = stream->readUint16LE();
if (horizontalStartPosition >= _height) {
warning("AccessVIDMoviePlay: starting position larger than height during partial frame compressed, data corrupt?");
return false;
}
}
expectedPixels = _width * (_height - horizontalStartPosition);
while (expectedPixels >= 0) {
rleByte = stream->readByte();
if (!rleByte) // NUL means end of stream
break;
if (rleByte & 0x80) {
expectedPixels -= rleByte & 0x7F;
} else {
// skip over pixels
expectedPixels -= rleByte;
stream->skip(rleByte); // skip over pixel data
}
}
// expectedPixels may be positive here in case stream got terminated with a NUL
if (expectedPixels < 0) {
warning("AccessVIDMoviePlay: pixel count mismatch during full/partial frame compressed, data corrupt?");
return false;
}
break;
}
case kVIDMovieChunkId_FullFrameCompressedFill: {
// Full frame compressed fill is:
// additional delay [word]
// REPEAT:
// RLE [byte]
// RLE upper bit set: draw RLE amount (& 0x7F) of pixels with specified color (color byte follows after RLE byte)
// RLE upper bit not set: draw RLE amount of pixels (those pixels follow right after RLE byte)
stream->skip(2);
expectedPixels = _width * _height;
while (expectedPixels > 0) {
rleByte = stream->readByte();
if (rleByte & 0x80) {
expectedPixels -= rleByte & 0x7F;
stream->skip(1);
} else {
// skip over pixels
expectedPixels -= rleByte;
stream->skip(rleByte); // skip over pixel data
}
}
if (expectedPixels < 0) {
warning("AccessVIDMoviePlay: pixel count mismatch during full frame compressed fill, data corrupt?");
return false;
}
break;
}
default:
assert(0);
break;
}
return true;
}
bool AccessVIDMovieDecoder::StreamVideoTrack::skipOverPalette(Common::SeekableReadStream *stream) {
stream->skip(0x300); // 3 bytes per color, 256 colors
return true;
}
void AccessVIDMovieDecoder::StreamVideoTrack::decodePalette(Common::SeekableReadStream *stream) {
byte red, green, blue;
assert(stream);
// VID files use a 6-bit palette and not a 8-bit one, we change it to 8-bit
for (uint16 curColor = 0; curColor < 256; curColor++) {
red = stream->readByte() & 0x3F;
green = stream->readByte() & 0x3F;
blue = stream->readByte() & 0x3F;
_palette[curColor * 3] = (red << 2) | (red >> 4);
_palette[curColor * 3 + 1] = (green << 2) | (green >> 4);
_palette[curColor * 3 + 2] = (blue << 2) | (blue >> 4);
}
_dirtyPalette = true;
}
const byte *AccessVIDMovieDecoder::StreamVideoTrack::getPalette() const {
_dirtyPalette = false;
return _palette;
}
bool AccessVIDMovieDecoder::StreamVideoTrack::hasDirtyPalette() const {
return _dirtyPalette;
}
AccessVIDMovieDecoder::StreamAudioTrack::StreamAudioTrack(uint32 sampleRate, Audio::Mixer::SoundType soundType) :
AudioTrack(soundType) {
_totalAudioQueued = 0; // currently 0 milliseconds queued
_sampleRate = sampleRate;
_stereo = false; // always mono
_audioStream = Audio::makeQueuingAudioStream(sampleRate, _stereo);
}
AccessVIDMovieDecoder::StreamAudioTrack::~StreamAudioTrack() {
delete _audioStream;
}
void AccessVIDMovieDecoder::StreamAudioTrack::queueAudio(Common::SeekableReadStream *stream, byte chunkId) {
Common::SeekableReadStream *rawAudioStream = nullptr;
Audio::RewindableAudioStream *audioStream = nullptr;
uint32 audioLengthMSecs = 0;
if (chunkId == kVIDMovieChunkId_AudioFirstChunk) {
stream->skip(3); // skip over additional delay + sample rate
}
uint32 audioSize = stream->readUint16LE();
// Read the specified chunk into memory
rawAudioStream = stream->readStream(audioSize);
audioLengthMSecs = audioSize * 1000 / _sampleRate; // 1 byte == 1 8-bit sample
audioStream = Audio::makeRawStream(rawAudioStream, _sampleRate, Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN, DisposeAfterUse::YES);
if (audioStream) {
_totalAudioQueued += audioLengthMSecs;
_audioStream->queueAudioStream(audioStream, DisposeAfterUse::YES);
} else {
// in case there was an error
delete rawAudioStream;
}
}
bool AccessVIDMovieDecoder::StreamAudioTrack::skipOverAudio(Common::SeekableReadStream *stream, byte chunkId) {
if (chunkId == kVIDMovieChunkId_AudioFirstChunk) {
stream->skip(3); // skip over additional delay + sample rate
}
uint32 audioSize = stream->readUint16LE();
stream->skip(audioSize);
return true;
}
Audio::AudioStream *AccessVIDMovieDecoder::StreamAudioTrack::getAudioStream() const {
return _audioStream;
}
bool AccessEngine::playMovie(const Common::Path &filename, const Common::Point &pos) {
AccessVIDMovieDecoder videoDecoder;
Common::Point framePos(pos.x, pos.y);
if (!videoDecoder.loadFile(filename)) {
warning("AccessVIDMoviePlay: could not open '%s'", filename.toString().c_str());
return false;
}
bool skipVideo = false;
_events->clearEvents();
videoDecoder.start();
while (!shouldQuit() && !videoDecoder.endOfVideo() && !skipVideo) {
if (videoDecoder.needsUpdate()) {
const Graphics::Surface *frame = videoDecoder.decodeNextFrame();
if (frame) {
_screen->blitFrom(*frame);
if (videoDecoder.hasDirtyPalette()) {
const byte *palette = videoDecoder.getPalette();
g_system->getPaletteManager()->setPalette(palette, 0, 256);
}
_screen->update();
}
}
_events->pollEventsAndWait();
Common::CustomEventType action;
if (_events->getAction(action)) {
if (action == kActionSkip)
skipVideo = true;
}
}
return !skipVideo;
}
} // End of namespace Access

View File

@@ -0,0 +1,151 @@
/* 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/>.
*
*/
#ifndef ACCESS_VIDEO_MOVIE_DECODER_H
#define ACCESS_VIDEO_MOVIE_DECODER_H
#include "video/video_decoder.h"
#include "audio/audiostream.h"
namespace Common {
class SeekableReadStream;
}
namespace Image {
class Codec;
}
namespace Access {
enum kDebugLevels {
kVIDMovieChunkId_FullFrame = 0x00,
kVIDMovieChunkId_FullFrameCompressed = 0x01,
kVIDMovieChunkId_Palette = 0x02,
kVIDMovieChunkId_FullFrameCompressedFill = 0x03,
kVIDMovieChunkId_PartialFrameCompressed = 0x04,
kVIDMovieChunkId_EndOfFile = 0x14,
kVIDMovieChunkId_AudioFirstChunk = 0x7C,
kVIDMovieChunkId_Audio = 0x7D
};
// This video format is used in at least the following Access engine games:
// - Noctropolis
// - Synnergist
class AccessVIDMovieDecoder : public Video::VideoDecoder {
public:
AccessVIDMovieDecoder();
~AccessVIDMovieDecoder() override;
bool loadStream(Common::SeekableReadStream *stream) override;
void close() override;
protected:
void readNextPacket() override;
private:
bool streamSkipFullFrameCompressedFill();
private:
int32 _streamSeekOffset; /* current stream offset, pointing to not-yet-indexed stream position */
uint32 _streamVideoIndex; /* current stream index for video decoding */
uint32 _streamAudioIndex; /* current stream index for audio decoding */
struct IndexCacheEntry {
byte chunkId;
int32 offset;
};
Common::Array<IndexCacheEntry> _indexCacheTable;
private:
class StreamVideoTrack : public VideoTrack {
public:
StreamVideoTrack(uint32 width, uint32 height, uint16 regularFrameDelay);
~StreamVideoTrack() override;
bool endOfTrack() const override;
uint16 getWidth() const override { return _width; }
uint16 getHeight() const override { return _height; }
Graphics::PixelFormat getPixelFormat() const override;
int getCurFrame() const override { return _curFrame; }
void setNextFrameStartTime(uint32 nextFrameStartTime) { _nextFrameStartTime = nextFrameStartTime; }
uint32 getNextFrameStartTime() const override { return _nextFrameStartTime; }
const Graphics::Surface *decodeNextFrame() override { return _surface; }
const byte *getPalette() const override;
bool hasDirtyPalette() const override;
void decodePalette(Common::SeekableReadStream *stream);
void decodeFrame(Common::SeekableReadStream *stream, byte chunkId);
bool skipOverFrame(Common::SeekableReadStream *stream, byte chunkId);
bool skipOverPalette(Common::SeekableReadStream *stream);
void setEndOfTrack() { _endOfTrack = true; }
private:
Graphics::Surface *_surface;
int _curFrame;
uint32 _nextFrameStartTime;
byte _palette[3 * 256];
mutable bool _dirtyPalette;
uint16 _width, _height;
uint16 _regularFrameDelay; // delay between frames (1 = 1/60 of a second)
bool _endOfTrack;
};
class StreamAudioTrack : public AudioTrack {
public:
StreamAudioTrack(uint32 sampleRate, Audio::Mixer::SoundType soundType);
~StreamAudioTrack() override;
void queueAudio(Common::SeekableReadStream *stream, byte chunkId);
bool skipOverAudio(Common::SeekableReadStream *stream, byte chunkId);
protected:
Audio::AudioStream *getAudioStream() const override;
private:
Audio::QueuingAudioStream *_audioStream;
uint32 _totalAudioQueued; /* total amount of milliseconds of audio, that we queued up already */
public:
uint32 getTotalAudioQueued() const { return _totalAudioQueued; }
private:
int16 decodeSample(uint8 dataNibble);
uint16 _sampleRate;
bool _stereo;
};
Common::SeekableReadStream *_stream;
StreamVideoTrack *_videoTrack;
StreamAudioTrack *_audioTrack;
};
} // End of namespace Access
#endif