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

View File

@@ -0,0 +1 @@
engines/mutationofjb/util.cpp

View File

@@ -0,0 +1,240 @@
/* 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 "mutationofjb/animationdecoder.h"
#include "mutationofjb/encryptedfile.h"
#include "mutationofjb/util.h"
#include "common/debug.h"
namespace MutationOfJB {
AnimationDecoder::AnimationDecoder(const Common::Path &fileName) : _fileName(fileName), _fromFrame(-1), _toFrame(-1), _threshold(0xFF) {
_surface.create(IMAGE_WIDTH, IMAGE_HEIGHT, Graphics::PixelFormat::createFormatCLUT8());
_owningSurface = true;
}
AnimationDecoder::AnimationDecoder(const Common::Path &fileName, const Graphics::Surface &outSurface) : _fileName(fileName), _surface(outSurface), _owningSurface(false), _fromFrame(-1), _toFrame(-1), _threshold(0xFF) {}
bool AnimationDecoder::decode(AnimationDecoderCallback *callback) {
EncryptedFile file;
file.open(_fileName);
if (!file.isOpen()) {
reportFileMissingError(_fileName.toString(Common::Path::kNativeSeparator).c_str());
return false;
}
file.seek(0, SEEK_END);
const int32 endPos = file.pos();
// Skip header - we don't need it anyway.
file.seek(0x80);
int frameNo = 0;
while (file.pos() != endPos) {
// Record.
const uint32 length = file.readUint32LE();
const uint16 recordId = file.readUint16LE();
const uint16 subrecords = file.readUint16LE();
// Skip 8 empty bytes.
file.seek(8, SEEK_CUR);
// Subrecords.
if (recordId == 0xF1FA) {
if ((_fromFrame != -1 && frameNo < _fromFrame) || (_toFrame != -1 && frameNo > _toFrame)) {
file.seek(length - 16, SEEK_CUR);
} else {
if (subrecords == 0) {
if (callback) {
callback->onFrame(frameNo, _surface); // Empty record, frame identical to the previous one.
}
} else {
for (int i = 0; i < subrecords; ++i) {
int32 filePos = file.pos();
const uint32 subLength = file.readUint32LE();
const uint16 type = file.readUint16LE();
if (type == 0x0B) {
loadPalette(file);
if (callback) {
callback->onPaletteUpdated(_palette);
}
} else if (type == 0x0F) {
loadFullFrame(file, subLength - 6);
if (callback) {
callback->onFrame(frameNo, _surface);
}
} else if (type == 0x0C) {
loadDiffFrame(file, subLength - 6);
if (callback) {
callback->onFrame(frameNo, _surface);
}
} else {
debug("Unsupported record type %02X.", type);
file.seek(subLength - 6, SEEK_CUR);
}
// Makes decoding more robust, because for some reason records might have extra data at the end.
file.seek(filePos + subLength, SEEK_SET);
}
}
}
frameNo++;
} else {
file.seek(length - 16, SEEK_CUR);
}
}
file.close();
return true;
}
void AnimationDecoder::setPartialMode(int fromFrame, int toFrame, const Common::Rect &area, uint8 threshold) {
_fromFrame = fromFrame;
_toFrame = toFrame;
_area = area;
_threshold = threshold;
}
void AnimationDecoder::loadPalette(Common::SeekableReadStream &file) {
uint16 packets = file.readUint16LE();
const uint8 skipCount = file.readByte();
int copyCount = file.readByte();
if (copyCount == 0) {
copyCount = PALETTE_COLORS;
}
while (packets--) {
file.read(_palette + skipCount * 3, copyCount * 3);
for (int j = skipCount * 3; j < (skipCount + copyCount) * 3; ++j) {
_palette[j] <<= 2; // Uses 6-bit colors.
}
}
}
void AnimationDecoder::loadFullFrame(EncryptedFile &file, uint32 size) {
uint8 *const pixels = reinterpret_cast<uint8 *>(_surface.getPixels());
uint8 *ptr = pixels;
uint32 readBytes = 0;
uint32 lines = 0;
while (readBytes != size) {
if (lines == 200) {
// Some full frames have an unknown byte at the end,
// so break when we encounter all 200 lines.
break;
}
uint8 no = file.readByte();
readBytes++;
while (no--) {
uint8 n = file.readByte();
readBytes++;
if (n < 0x80) {
// RLE - Copy color n times.
uint8 color = file.readByte();
readBytes++;
while (n--) {
*ptr++ = color;
}
} else {
// Take next 0x100 - n bytes as they are.
const uint32 rawlen = 0x100 - n;
file.read(ptr, rawlen);
readBytes += rawlen;
ptr += rawlen;
}
}
lines++;
}
}
void AnimationDecoder::loadDiffFrame(EncryptedFile &file, uint32) {
const uint16 firstLine = file.readUint16LE();
const uint16 numLines = file.readUint16LE();
for (uint16 line = firstLine; line < firstLine + numLines; ++line) {
uint8 *imageData = reinterpret_cast<uint8 *>(_surface.getBasePtr(0, line));
uint16 lineOffset = 0;
// Optimization for skipping the whole line if outside of confined area.
const bool skipLineOutput = !_area.isEmpty() && (line < _area.top || line >= _area.bottom);
uint8 buf[0x80];
uint8 runs = file.readByte();
while (runs--) {
uint8 localOffset = file.readByte();
uint8 num = file.readByte();
imageData += localOffset;
lineOffset += localOffset;
if (num == 0) {
// Ignore?
debug("Zero RLE number found.");
} else if (num < 0x80) {
if (!skipLineOutput) {
if (_area.isEmpty() && _threshold == 0xFF) {
file.read(imageData, num);
} else {
file.read(buf, num);
for (uint16 i = 0; i < num; i++) {
if ((_area.isEmpty() || _area.contains(lineOffset + i, line)) && imageData[i] <= _threshold)
imageData[i] = buf[i];
}
}
} else {
file.skip(num);
}
imageData += num;
lineOffset += num;
} else {
const uint8 color = file.readByte();
const int no = 0x100 - num;
if (!skipLineOutput) {
if (_area.isEmpty() && _threshold == 0xFF) {
memset(imageData, color, no);
} else {
for (int i = 0; i < no; i++) {
if ((_area.isEmpty() || _area.contains(lineOffset + i, line)) && imageData[i] <= _threshold)
imageData[i] = color;
}
}
}
imageData += no;
lineOffset += no;
}
}
}
}
AnimationDecoder::~AnimationDecoder() {
if (_owningSurface)
_surface.free();
}
}

View File

@@ -0,0 +1,86 @@
/* 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 MUTATIONOFJB_ANIMATIONDECODER_H
#define MUTATIONOFJB_ANIMATIONDECODER_H
#include "common/rect.h"
#include "common/path.h"
#include "common/scummsys.h"
#include "graphics/surface.h"
#include "mutationofjb/encryptedfile.h"
namespace Common {
class SeekableReadStream;
}
namespace MutationOfJB {
enum {
PALETTE_COLORS = 256,
PALETTE_SIZE = PALETTE_COLORS * 3,
IMAGE_WIDTH = 320,
IMAGE_HEIGHT = 200
};
class AnimationDecoderCallback {
public:
virtual void onFrame(int frameNo, Graphics::Surface &surface) = 0;
virtual void onPaletteUpdated(byte palette[PALETTE_SIZE]) = 0;
virtual ~AnimationDecoderCallback() {}
};
class AnimationDecoder {
public:
AnimationDecoder(const Common::Path &fileName);
AnimationDecoder(const Common::Path &fileName, const Graphics::Surface &outSurface);
~AnimationDecoder();
bool decode(AnimationDecoderCallback *callback);
/**
* Enables partial decoding mode.
*
* @param fromFrame Frame to start decoding on (inclusive).
* @param toFrame Frame to end decoding on (inclusive).
* @param area Output surface will be confined to this area.
* @param threshold Source pixels with color index above this threshold will not be replaced.
*/
void setPartialMode(int fromFrame, int toFrame, const Common::Rect &area = Common::Rect(), uint8 threshold = 0xFF);
private:
void loadPalette(Common::SeekableReadStream &stream);
void loadFullFrame(EncryptedFile &file, uint32 size);
void loadDiffFrame(EncryptedFile &file, uint32 size);
Common::Path _fileName;
Graphics::Surface _surface;
bool _owningSurface;
byte _palette[PALETTE_SIZE];
int _fromFrame;
int _toFrame;
Common::Rect _area;
uint8 _threshold;
};
}
#endif

View File

@@ -0,0 +1,52 @@
/* 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 "mutationofjb/assets.h"
namespace MutationOfJB {
Assets::Assets(Game &game) : _toSayList("tosay.ger"), _responseList("response.ger"), _hardcodedStrings(game) {}
Font &Assets::getSystemFont() {
return _systemFont;
}
Font &Assets::getSpeechFont() {
return _speechFont;
}
ConversationLineList &Assets::getToSayList() {
return _toSayList;
}
ConversationLineList &Assets::getResponseList() {
return _responseList;
}
InventoryItemDefinitionList &Assets::getInventoryItemDefList() {
return _invItemDefList;
}
HardcodedStrings &Assets::getHardcodedStrings() {
return _hardcodedStrings;
}
}

View File

@@ -0,0 +1,79 @@
/* 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 MUTATIONOFJB_ASSETS_H
#define MUTATIONOFJB_ASSETS_H
#include "mutationofjb/font.h"
#include "mutationofjb/conversationlinelist.h"
#include "mutationofjb/inventoryitemdefinitionlist.h"
#include "mutationofjb/hardcodedstrings.h"
namespace MutationOfJB {
class Game;
class Assets {
public:
Assets(Game &game);
Font &getSystemFont();
Font &getSpeechFont();
/**
* Access to "to say" list for conversations.
*
* @return Conversation line list.
*/
ConversationLineList &getToSayList();
/**
* Access to "response" list for conversations.
*
* @return Conversation line list.
*/
ConversationLineList &getResponseList();
/**
* Access to inventory definitions.
*
* @return Inventory item definition list.
*/
InventoryItemDefinitionList &getInventoryItemDefList();
/**
* Access to strings hardcoded in game executable.
*
* @return Hardcoded strings.
*/
HardcodedStrings &getHardcodedStrings();
private:
SystemFont _systemFont;
SpeechFont _speechFont;
ConversationLineList _toSayList;
ConversationLineList _responseList;
InventoryItemDefinitionList _invItemDefList;
HardcodedStrings _hardcodedStrings;
};
}
#endif

View File

@@ -0,0 +1,52 @@
/* 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 "mutationofjb/commands/additemcommand.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/script.h"
/** @file
* "ADDITEM " <item>
*
* Adds item to inventory.
*/
namespace MutationOfJB {
bool AddItemCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
if (!line.hasPrefix("ADDITEM") || line.size() < 9) {
return false;
}
command = new AddItemCommand(line.c_str() + 8);
return true;
}
Command::ExecuteResult AddItemCommand::execute(ScriptExecutionContext &scriptExecCtx) {
scriptExecCtx.getGameData()._inventory.addItem(_item);
return Finished;
}
Common::String AddItemCommand::debugString() const {
return Common::String::format("ADDITEM '%s'", _item.c_str());
}
}

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 MUTATIONOFJB_ADDITEMCOMMAND_H
#define MUTATIONOFJB_ADDITEMCOMMAND_H
#include "mutationofjb/commands/seqcommand.h"
#include "common/str.h"
namespace MutationOfJB {
class AddItemCommandParser : public SeqCommandParser {
public:
AddItemCommandParser() {}
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class AddItemCommand : public SeqCommand {
public:
AddItemCommand(const Common::String &item) : _item(item) {}
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
Common::String _item;
};
}
#endif

View File

@@ -0,0 +1,57 @@
/* 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 "mutationofjb/commands/bitmapvisibilitycommand.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/script.h"
/** @file
* "RB " <sceneId> " " <bitmapId> " " <visible>
*
* Changes visibility of a bitmap in the specified scene.
*/
namespace MutationOfJB {
bool BitmapVisibilityCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
if (line.size() < 10 || !line.hasPrefix("RB "))
return false;
const uint8 sceneId = (uint8) atoi(line.c_str() + 3);
const uint8 bitmapId = (uint8) atoi(line.c_str() + 6);
const bool visible = (line[9] == '1');
command = new BitmapVisibilityCommand(sceneId, bitmapId, visible);
return true;
}
Command::ExecuteResult BitmapVisibilityCommand::execute(ScriptExecutionContext &scriptExecCtx) {
scriptExecCtx.getGameData().getScene(_sceneId)->getBitmap(_bitmapId)->_isVisible = _visible;
return Finished;
}
Common::String BitmapVisibilityCommand::debugString() const {
return Common::String::format("SETBITMAPVIS %u %u %s", (unsigned int) _sceneId, (unsigned int) _bitmapId, _visible ? "true" : "false");
}
}

View File

@@ -0,0 +1,52 @@
/* 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 MUTATIONOFJB_BITMAPVISIBILITYCOMMAND_H
#define MUTATIONOFJB_BITMAPVISIBILITYCOMMAND_H
#include "mutationofjb/commands/seqcommand.h"
#include "common/str.h"
namespace MutationOfJB {
class BitmapVisibilityCommandParser : public SeqCommandParser {
public:
BitmapVisibilityCommandParser() {}
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class BitmapVisibilityCommand : public SeqCommand {
public:
BitmapVisibilityCommand(uint8 sceneId, uint8 bitmapId, bool visible) : _sceneId(sceneId), _bitmapId(bitmapId), _visible(visible) {}
const Common::String &getName() const;
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
uint8 _sceneId;
uint8 _bitmapId;
bool _visible;
};
}
#endif

View File

@@ -0,0 +1,82 @@
/* 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 "mutationofjb/commands/callmacrocommand.h"
#include "mutationofjb/script.h"
#include "mutationofjb/game.h"
/** @file
* "_" <name>
*
* Calls macro with the specified name.
*/
namespace MutationOfJB {
bool CallMacroCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
if (line.size() < 2 || line.firstChar() != '_') {
return false;
}
const Common::String macroName = line.c_str() + 1;
command = new CallMacroCommand(macroName);
return true;
}
void CallMacroCommandParser::transition(ScriptParseContext &, Command *oldCommand, Command *newCommand, CommandParser *) {
if (!oldCommand || !newCommand) {
warning("Unexpected empty command in transition");
return;
}
static_cast<CallMacroCommand *>(oldCommand)->setReturnCommand(newCommand);
}
void CallMacroCommand::setReturnCommand(Command *cmd) {
_returnCommand = cmd;
}
Command *CallMacroCommand::getReturnCommand() const {
return _returnCommand;
}
Command::ExecuteResult CallMacroCommand::execute(ScriptExecutionContext &scriptExecCtx) {
_callCommand = scriptExecCtx.getMacro(_macroName);
if (_callCommand) {
scriptExecCtx.pushReturnCommand(_returnCommand);
} else {
warning("Macro '%s' not found.", _macroName.c_str());
}
return Finished;
}
Command *CallMacroCommand::next() const {
return _callCommand;
}
Common::String CallMacroCommand::debugString() const {
return Common::String::format("CALL '%s'", _macroName.c_str());
}
}

View File

@@ -0,0 +1,57 @@
/* 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 MUTATIONOFJB_CALLMACROCOMMAND_H
#define MUTATIONOFJB_CALLMACROCOMMAND_H
#include "mutationofjb/commands/command.h"
#include "common/scummsys.h"
#include "common/str.h"
namespace MutationOfJB {
class CallMacroCommandParser : public CommandParser {
public:
CallMacroCommandParser() {}
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser) override;
};
class CallMacroCommand : public Command {
public:
CallMacroCommand(const Common::String &macroName) : _macroName(macroName), _returnCommand(nullptr), _callCommand(nullptr) {}
void setReturnCommand(Command *);
Command *getReturnCommand() const;
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Command *next() const override;
Common::String debugString() const override;
private:
Common::String _macroName;
Command *_returnCommand;
Command *_callCommand;
};
}
#endif

View File

@@ -0,0 +1,58 @@
/* 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 "mutationofjb/commands/camefromcommand.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/script.h"
#include "common/str.h"
/** @file
* "CAMEFROM " <sceneId>
*
* This command tests whether last scene (the scene player came from) is sceneId.
* If true, the execution continues after this command.
* Otherwise the execution continues after first '#' found.
*/
namespace MutationOfJB {
bool CameFromCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
if (line.size() < 10 || !line.hasPrefix("CAMEFROM")) {
return false;
}
const uint8 sceneId = atoi(line.c_str() + 9);
_tags.push(0);
command = new CameFromCommand(sceneId);
return true;
}
Command::ExecuteResult CameFromCommand::execute(ScriptExecutionContext &scriptExecCtx) {
_cachedResult = (scriptExecCtx.getGameData()._lastScene == _sceneId);
return Finished;
}
Common::String CameFromCommand::debugString() const {
return Common::String::format("CAMEFROM %d", _sceneId);
}
}

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 MUTATIONOFJB_CAMEFROMCOMMAND_H
#define MUTATIONOFJB_CAMEFROMCOMMAND_H
#include "mutationofjb/commands/conditionalcommand.h"
#include "common/scummsys.h"
namespace MutationOfJB {
class CameFromCommandParser : public ConditionalCommandParser {
public:
CameFromCommandParser() : ConditionalCommandParser(true) {}
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class CameFromCommand : public ConditionalCommand {
public:
CameFromCommand(uint8 sceneId) : _sceneId(sceneId) {}
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
uint8 _sceneId;
};
}
#endif

View File

@@ -0,0 +1,558 @@
/* 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 "mutationofjb/commands/changecommand.h"
#include "mutationofjb/script.h"
#include "mutationofjb/gamedata.h"
/** @file
* "CHANGE" <entity> " " <register> " " <sceneId> " " <entityId> " " <value>
*
* Changes entity register value for specified scene.
* <entity> 1B Entity to change register for.
* Possible values:
* 'D' - door
* 'O' - object
* 'S' - static
* '' - scene
* <register> 2B Register name.
* <sceneId> 2B Scene ID.
* <entityid> 2B Entity ID.
* <value> *B Value (variable length).
*/
namespace MutationOfJB {
bool ChangeCommandParser::parseValueString(const Common::String &valueString, bool changeEntity, uint8 &sceneId, uint8 &entityId, ChangeCommand::ChangeRegister &reg, ChangeCommand::ChangeOperation &op, ChangeCommandValue &ccv) {
if (changeEntity) {
if (valueString.size() < 8) {
return false;
}
} else {
if (valueString.size() < 7) {
return false;
}
}
sceneId = atoi(valueString.c_str() + 3);
if (changeEntity) {
entityId = atoi(valueString.c_str() + 6);
}
const char *val = "";
if (changeEntity) {
if (valueString.size() >= 9) {
val = valueString.c_str() + 9;
}
} else {
if (valueString.size() >= 6) {
val = valueString.c_str() + 6;
}
}
if (valueString.hasPrefix("NM")) {
reg = ChangeCommand::NM;
op = ChangeCommand::SetValue;
Common::strlcpy(ccv._strVal, val, MAX_ENTITY_NAME_LENGTH + 1);
} else if (valueString.hasPrefix("LT")) {
reg = ChangeCommand::LT;
ccv._byteVal = parseInteger(val, op);
} else if (valueString.hasPrefix("SX")) {
reg = ChangeCommand::SX;
ccv._wordVal = parseInteger(val, op);
} else if (valueString.hasPrefix("SY")) {
reg = ChangeCommand::SY;
ccv._wordVal = parseInteger(val, op);
} else if (valueString.hasPrefix("XX")) {
reg = ChangeCommand::XX;
ccv._wordVal = parseInteger(val, op);
} else if (valueString.hasPrefix("YY")) {
reg = ChangeCommand::YY;
ccv._byteVal = parseInteger(val, op);
} else if (valueString.hasPrefix("XL")) {
reg = ChangeCommand::XL;
ccv._wordVal = parseInteger(val, op);
} else if (valueString.hasPrefix("YL")) {
reg = ChangeCommand::YL;
ccv._byteVal = parseInteger(val, op);
} else if (valueString.hasPrefix("WX")) {
reg = ChangeCommand::WX;
ccv._wordVal = parseInteger(val, op);
} else if (valueString.hasPrefix("WY")) {
reg = ChangeCommand::WY;
ccv._byteVal = parseInteger(val, op);
} else if (valueString.hasPrefix("AC")) {
reg = ChangeCommand::AC;
ccv._byteVal = parseInteger(val, op);
} else if (valueString.hasPrefix("FA")) {
reg = ChangeCommand::FA;
ccv._byteVal = parseInteger(val, op);
} else if (valueString.hasPrefix("FR")) {
reg = ChangeCommand::FR;
ccv._byteVal = parseInteger(val, op);
} else if (valueString.hasPrefix("NA")) {
reg = ChangeCommand::NA;
ccv._byteVal = parseInteger(val, op);
} else if (valueString.hasPrefix("FS")) {
reg = ChangeCommand::FS;
ccv._byteVal = parseInteger(val, op);
} else if (valueString.hasPrefix("CA")) {
reg = ChangeCommand::CA;
ccv._byteVal = parseInteger(val, op);
} else if (valueString.hasPrefix("DS")) {
reg = ChangeCommand::DS;
ccv._byteVal = parseInteger(val, op);
} else if (valueString.hasPrefix("DL")) {
reg = ChangeCommand::DL;
ccv._byteVal = parseInteger(val, op);
} else if (valueString.hasPrefix("ND")) {
reg = ChangeCommand::ND;
ccv._byteVal = parseInteger(val, op);
} else if (valueString.hasPrefix("NO")) {
reg = ChangeCommand::NO;
ccv._byteVal = parseInteger(val, op);
} else if (valueString.hasPrefix("NS")) {
reg = ChangeCommand::NS;
ccv._byteVal = parseInteger(val, op);
} else if (valueString.hasPrefix("PF")) {
reg = ChangeCommand::PF;
ccv._byteVal = parseInteger(val, op);
} else if (valueString.hasPrefix("PL")) {
reg = ChangeCommand::PL;
ccv._byteVal = parseInteger(val, op);
} else if (valueString.hasPrefix("PD")) {
reg = ChangeCommand::PD;
ccv._byteVal = parseInteger(val, op);
}
return true;
}
bool ChangeDoorCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
if (!line.hasPrefix("CHANGED ")) {
return false;
}
uint8 sceneId = 0;
uint8 objectId = 0;
ChangeCommand::ChangeRegister reg;
ChangeCommand::ChangeOperation op;
ChangeCommandValue val;
if (!parseValueString(line.c_str() + 8, true, sceneId, objectId, reg, op, val)) {
return false;
}
command = new ChangeDoorCommand(sceneId, objectId, reg, op, val);
return true;
}
bool ChangeObjectCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
if (!line.hasPrefix("CHANGEO ")) {
return false;
}
uint8 sceneId = 0;
uint8 objectId = 0;
ChangeCommand::ChangeRegister reg;
ChangeCommand::ChangeOperation op;
ChangeCommandValue val;
if (!parseValueString(line.c_str() + 8, true, sceneId, objectId, reg, op, val)) {
return false;
}
command = new ChangeObjectCommand(sceneId, objectId, reg, op, val);
return true;
}
bool ChangeStaticCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
if (!line.hasPrefix("CHANGES ")) {
return false;
}
uint8 sceneId = 0;
uint8 objectId = 0;
ChangeCommand::ChangeRegister reg;
ChangeCommand::ChangeOperation op;
ChangeCommandValue val;
if (!parseValueString(line.c_str() + 8, true, sceneId, objectId, reg, op, val)) {
return false;
}
command = new ChangeStaticCommand(sceneId, objectId, reg, op, val);
return true;
}
bool ChangeSceneCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
if (!line.hasPrefix("CHANGE ")) {
return false;
}
uint8 sceneId = 0;
uint8 objectId = 0;
ChangeCommand::ChangeRegister reg;
ChangeCommand::ChangeOperation op;
ChangeCommandValue val;
if (!parseValueString(line.c_str() + 7, false, sceneId, objectId, reg, op, val)) {
return false;
}
command = new ChangeSceneCommand(sceneId, objectId, reg, op, val);
return true;
}
int ChangeCommandParser::parseInteger(const char *val, ChangeCommand::ChangeOperation &op) {
op = ChangeCommand::SetValue;
if (!val || !(*val)) {
return 0;
}
if (val[0] == '\\') {
op = ChangeCommand::SetValue;
val++;
} else if (val[0] == '+') {
op = ChangeCommand::AddValue;
val++;
} else if (val[0] == '-') {
op = ChangeCommand::SubtractValue;
val++;
}
return atoi(val);
}
const char *ChangeCommand::getRegisterAsString() const {
switch (_register) {
case NM:
return "NM";
case LT:
return "LT";
case SX:
return "SX";
case SY:
return "SY";
case XX:
return "XX";
case YY:
return "YY";
case XL:
return "XL";
case YL:
return "YL";
case WX:
return "WX";
case WY:
return "WY";
case SP:
return "SP";
case AC:
return "AC";
case FA:
return "FA";
case FR:
return "FR";
case NA:
return "NA";
case FS:
return "FS";
case CA:
return "CA";
case DS:
return "DS";
case DL:
return "DL";
case ND:
return "ND";
case NO:
return "NO";
case NS:
return "NS";
case PF:
return "PF";
case PL:
return "PL";
case PD:
return "PD";
default:
return "(unknown)";
}
}
Common::String ChangeCommand::getValueAsString() const {
switch (_register) {
case NM:
return Common::String::format("\"%s\"", _value._strVal);
case LT:
case YY:
case YL:
case WY:
case SP:
case AC:
case FA:
case FR:
case NA:
case FS:
case CA:
case DS:
case DL:
case ND:
case NO:
case NS:
case PF:
case PL:
case PD:
return Common::String::format("%d", static_cast<int>(_value._byteVal));
case SX:
case SY:
case XX:
case XL:
case WX:
return Common::String::format("%d", static_cast<int>(_value._wordVal));
default:
return "(unknown)";
}
}
const char *ChangeCommand::getOperationAsString() const {
switch (_operation) {
case SetValue:
return "=";
case AddValue:
return "+=";
case SubtractValue:
return "-=";
default:
return "(unknown)";
}
}
Command::ExecuteResult ChangeDoorCommand::execute(ScriptExecutionContext &scriptExecCtx) {
Scene *const scene = scriptExecCtx.getGameData().getScene(_sceneId);
if (!scene) {
return Finished;
}
Door *const door = scene->getDoor(_entityId);
if (!door) {
return Finished;
}
switch (_register) {
case NM:
Common::strlcpy(door->_name, _value._strVal, MAX_ENTITY_NAME_LENGTH + 1);
break;
case LT:
door->_destSceneId = _value._byteVal;
break;
case SX:
door->_destX = _value._wordVal;
break;
case SY:
door->_destY = _value._wordVal;
break;
case XX:
door->_x = _value._wordVal;
break;
case YY:
door->_y = _value._byteVal;
break;
case XL:
door->_width = _value._wordVal;
break;
case YL:
door->_height = _value._byteVal;
break;
case WX:
door->_walkToX = _value._wordVal;
break;
case WY:
door->_walkToY = _value._byteVal;
break;
case SP:
door->_SP = _value._byteVal;
break;
default:
warning("Object does not support changing this register.");
break;
}
return Finished;
}
Common::String ChangeDoorCommand::debugString() const {
return Common::String::format("SCENE%d.DOOR%d.%s %s %s", _sceneId, _entityId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
}
Command::ExecuteResult ChangeObjectCommand::execute(ScriptExecutionContext &scriptExecCtx) {
Scene *const scene = scriptExecCtx.getGameData().getScene(_sceneId);
if (!scene) {
return Finished;
}
Object *const object = scene->getObject(_entityId, true);
if (!object) {
return Finished;
}
switch (_register) {
case AC:
object->_active = _value._byteVal;
break;
case FA:
object->_firstFrame = _value._byteVal;
break;
case FR:
object->_randomFrame = _value._byteVal;
break;
case NA:
object->_numFrames = _value._byteVal;
break;
case FS:
object->_roomFrameLSB = _value._byteVal;
break;
case CA:
object->_currentFrame = _value._byteVal;
break;
case XX:
object->_x = _value._wordVal;
break;
case YY:
object->_y = _value._byteVal;
break;
case XL:
object->_width = _value._wordVal;
break;
case YL:
object->_height = _value._byteVal;
break;
case WX:
object->_WX = _value._wordVal;
break;
case WY:
object->_roomFrameMSB = _value._byteVal;
break;
case SP:
object->_SP = _value._byteVal;
break;
default:
warning("Object does not support changing this register.");
break;
}
return Finished;
}
Common::String ChangeObjectCommand::debugString() const {
return Common::String::format("SCENE%d.OBJECT%d.%s %s %s", _sceneId, _entityId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
}
Command::ExecuteResult ChangeStaticCommand::execute(ScriptExecutionContext &scriptExecCtx) {
Scene *const scene = scriptExecCtx.getGameData().getScene(_sceneId);
if (!scene) {
return Finished;
}
Static *const stat = scene->getStatic(_entityId);
if (!stat) {
return Finished;
}
switch (_register) {
case AC:
stat->_active = _value._byteVal;
break;
case NM:
Common::strlcpy(stat->_name, _value._strVal, MAX_ENTITY_NAME_LENGTH + 1);
break;
case XX:
stat->_x = _value._wordVal;
break;
case YY:
stat->_y = _value._byteVal;
break;
case XL:
stat->_width = _value._wordVal;
break;
case YL:
stat->_height = _value._byteVal;
break;
case WX:
stat->_walkToX = _value._wordVal;
break;
case WY:
stat->_walkToY = _value._byteVal;
break;
case SP:
stat->_walkToFrame = _value._byteVal;
break;
default:
warning("Object does not support changing this register.");
break;
}
return Finished;
}
Common::String ChangeStaticCommand::debugString() const {
return Common::String::format("SCENE%d.STATIC%d.%s %s %s", _sceneId, _entityId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
}
Command::ExecuteResult ChangeSceneCommand::execute(ScriptExecutionContext &scriptExecCtx) {
Scene *const scene = scriptExecCtx.getGameData().getScene(_sceneId);
if (!scene) {
return Finished;
}
switch (_register) {
case DS:
scene->_startup = _value._byteVal;
break;
case DL:
scene->_delay = _value._byteVal;
break;
case ND:
scene->_noDoors = _value._byteVal;
break;
case NO:
scene->_noObjects = _value._byteVal;
break;
case NS:
scene->_noStatics = _value._byteVal;
break;
case PF:
scene->_palRotFirst = _value._byteVal;
break;
case PL:
scene->_palRotLast = _value._byteVal;
break;
case PD:
scene->_palRotDelay = _value._byteVal;
break;
default:
warning("Scene does not support changing this register.");
break;
}
return Finished;
}
Common::String ChangeSceneCommand::debugString() const {
return Common::String::format("SCENE%d.%s %s %s", _sceneId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
}
}

View File

@@ -0,0 +1,146 @@
/* 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 "mutationofjb/commands/seqcommand.h"
#include "mutationofjb/gamedata.h"
namespace MutationOfJB {
union ChangeCommandValue {
uint8 _byteVal;
uint16 _wordVal;
char _strVal[MAX_ENTITY_NAME_LENGTH + 1];
};
class ChangeCommand : public SeqCommand {
public:
enum ChangeRegister {
NM, // Name
LT, // Destination scene ID
SX, // Destination X
SY, // Destination Y
XX, // X
YY, // Y
XL, // Width
YL, // Height
WX, // Walk to X
WY, // Walk to Y
SP, //
AC, // Active
FA, // First animation
FR,
NA,
FS,
CA,
DS, // Startup
DL,
ND, // Number of doors
NO, // Number of objects
NS, // Number of statics
PF, // Palette rotation first
PL, // Palette rotation last
PD // Palette rotation delay
};
enum ChangeOperation {
SetValue,
AddValue,
SubtractValue
};
ChangeCommand(uint8 sceneId, uint8 entityId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue &val) :
_sceneId(sceneId), _entityId(entityId), _register(reg), _operation(op), _value(val)
{}
protected:
const char *getRegisterAsString() const;
Common::String getValueAsString() const;
const char *getOperationAsString() const;
uint8 _sceneId;
uint8 _entityId;
ChangeRegister _register;
ChangeOperation _operation;
ChangeCommandValue _value;
};
class ChangeCommandParser : public SeqCommandParser {
protected:
bool parseValueString(const Common::String &valueString, bool changeEntity, uint8 &sceneId, uint8 &entityId, ChangeCommand::ChangeRegister &reg, ChangeCommand::ChangeOperation &op, ChangeCommandValue &ccv);
int parseInteger(const char *val, ChangeCommand::ChangeOperation &op);
};
class ChangeObjectCommandParser : public ChangeCommandParser {
public:
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class ChangeDoorCommandParser : public ChangeCommandParser {
public:
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class ChangeStaticCommandParser : public ChangeCommandParser {
public:
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class ChangeSceneCommandParser : public ChangeCommandParser {
public:
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class ChangeDoorCommand : public ChangeCommand {
public:
ChangeDoorCommand(uint8 sceneId, uint8 doorId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue &val)
: ChangeCommand(sceneId, doorId, reg, op, val)
{}
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
};
class ChangeObjectCommand : public ChangeCommand {
public:
ChangeObjectCommand(uint8 sceneId, uint8 objectId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue &val)
: ChangeCommand(sceneId, objectId, reg, op, val)
{}
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
};
class ChangeStaticCommand : public ChangeCommand {
public:
ChangeStaticCommand(uint8 sceneId, uint8 staticId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue &val)
: ChangeCommand(sceneId, staticId, reg, op, val)
{}
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
};
class ChangeSceneCommand : public ChangeCommand {
public:
ChangeSceneCommand(uint8 sceneId, uint8 staticId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue &val)
: ChangeCommand(sceneId, staticId, reg, op, val)
{}
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
};
}

View File

@@ -0,0 +1,33 @@
/* 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 "mutationofjb/commands/command.h"
#include "common/scummsys.h"
namespace MutationOfJB {
void CommandParser::transition(ScriptParseContext &, Command *, Command *, CommandParser *) {}
void CommandParser::finish(ScriptParseContext &) {}
CommandParser::~CommandParser() {}
Command::~Command() {}
}

View File

@@ -0,0 +1,103 @@
/* 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 MUTATIONOFJB_COMMAND_H
#define MUTATIONOFJB_COMMAND_H
namespace Common {
class String;
}
namespace MutationOfJB {
class Command;
class ScriptExecutionContext;
class ScriptParseContext;
/**
* Base class for command parsers.
*
* The parser's main job is to create a Command instance from input line.
*/
class CommandParser {
public:
virtual ~CommandParser();
/**
* Parses the specified line and possibly returns a Command instance.
*
* @param line Line to parse.
* @param parseCtx Parse context.
* @param command Output parameter for newly created command.
* @return True if the line has been successfully parsed by this parser, false otherwise.
* @note You may return true and set command to nullptr.
* That means the line has been successfully parsed, but no command is needed.
*/
virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) = 0;
/**
* Called when transitioning parsing between two commands.
*
* For example, cmdParserA->transition(parseCtx, cmdA, cmdB, cmdParserB) is called after command B is done parsing
* to notify command A parser about the transition from command A to command B.
* This is useful for sequential commands, because at the time command A is being parsed,
* we don't have any information about command B, so we cannot set the next pointer.
* Transition method can be used to set the next pointer after command B is available.
*
* @param parseCtx Parse context.
* @param oldCommand Old command (created by this parser).
* @param newCommand New command (created by newCommandParser).
* @param newCommandParser Command parser which created the new command.
*/
virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser);
/**
* Called after the whole script is parsed.
*
* Can be used for cleanup.
*
* @param parseCtx Parse context.
*/
virtual void finish(ScriptParseContext &parseCtx);
};
/**
* Base class for script commands.
*/
class Command {
public:
enum ExecuteResult {
None,
Finished,
InProgress
};
virtual ~Command();
virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) = 0;
virtual Command *next() const = 0;
virtual Common::String debugString() const = 0;
};
}
#endif

View File

@@ -0,0 +1,73 @@
/* 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 "mutationofjb/commands/conditionalcommand.h"
#include "mutationofjb/script.h"
#include "common/scummsys.h"
namespace MutationOfJB {
void ConditionalCommandParser::transition(ScriptParseContext &parseContext, Command *oldCommand, Command *newCommand, CommandParser *) {
if (!oldCommand || !newCommand) {
warning("Unexpected empty command in transition");
return;
}
ConditionalCommand *const condCommand = static_cast<ConditionalCommand *>(oldCommand);
parseContext.addConditionalCommand(condCommand, _tags.pop(), _firstHash);
condCommand->setTrueCommand(newCommand);
}
void ConditionalCommandParser::finish(ScriptParseContext &) {
_tags.clear();
}
ConditionalCommand::ConditionalCommand() :
_trueCommand(nullptr),
_falseCommand(nullptr),
_cachedResult(false) {}
Command *ConditionalCommand::getTrueCommand() const {
return _trueCommand;
}
Command *ConditionalCommand::getFalseCommand() const {
return _falseCommand;
}
void ConditionalCommand::setTrueCommand(Command *command) {
_trueCommand = command;
}
void ConditionalCommand::setFalseCommand(Command *command) {
_falseCommand = command;
}
Command *ConditionalCommand::next() const {
if (_cachedResult) {
return _trueCommand;
} else {
return _falseCommand;
}
}
}

View File

@@ -0,0 +1,61 @@
/* 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 MUTATIONOFJB_CONDITIONALCOMMAND_H
#define MUTATIONOFJB_CONDITIONALCOMMAND_H
#include "mutationofjb/commands/command.h"
#include "common/scummsys.h"
#include "common/queue.h"
namespace MutationOfJB {
class ConditionalCommandParser : public CommandParser {
public:
ConditionalCommandParser(bool firstHash = false) : _firstHash(firstHash) {}
void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser) override;
void finish(ScriptParseContext &parseCtx) override;
protected:
Common::Queue<char> _tags;
private:
bool _firstHash;
};
class ConditionalCommand : public Command {
public:
ConditionalCommand();
Command *getTrueCommand() const;
Command *getFalseCommand() const;
void setTrueCommand(Command *command);
void setFalseCommand(Command *command);
Command *next() const override;
protected:
Command *_trueCommand;
Command *_falseCommand;
bool _cachedResult;
};
}
#endif

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/>.
*
*/
#include "mutationofjb/commands/definestructcommand.h"
#include "mutationofjb/script.h"
#include "mutationofjb/game.h"
#include "common/debug.h"
/** @file
* "DEFINE_STRUCT " <numItemGroups> " " <context> " " <objectId> " " <colorString> <CRLF>
* <itemGroup> { <CRLF> <itemGroup> }
*
* item ::= <questionIndex> " " <responseIndex> " " <nextGroup>
* itemGroup ::= <item> " " <item> " " <item> " " <item> " " <item>
*
* Defines the flow of an interactive conversation.
*
* Every item group consists of 5 conversation items.
* "questionIndex" and "responseIndex" specify what the player and the responder say when the conversation item is selected.
* They refer to the line numbers of TOSAY.GER and RESPONSE.GER, respectively.
* "nextGroup" refers to the group that follows when the conversation item is selected. A value of 0 indicates end of
* conversation.
*/
namespace MutationOfJB {
bool DefineStructCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
if (line.size() < 24 || !line.hasPrefix("DEFINE_STRUCT")) {
return false;
}
ConversationInfo convInfo;
const int numLines = atoi(line.c_str() + 14);
convInfo._context = atoi(line.c_str() + 18);
convInfo._objectId = atoi(line.c_str() + 20);
convInfo._color = Game::colorFromString(line.c_str() + 23);
for (int i = 0; i < numLines; ++i) {
Common::String convLineStr;
if (!parseCtx.readLine(convLineStr)) {
break;
}
if (convLineStr.size() != 74) {
debug("Conversation line in DEFINE_STRUCT with wrong length");
continue;
}
const char *linePtr = convLineStr.c_str();
ConversationInfo::ItemGroup convGroup;
for (int j = 0; j < 5; ++j) {
ConversationInfo::Item convItem;
convItem._question = atoi(linePtr);
linePtr += 6;
convItem._response = atoi(linePtr);
linePtr += 6;
convItem._nextGroupIndex = atoi(linePtr);
linePtr += 3;
convGroup.push_back(convItem);
}
convInfo._itemGroups.push_back(convGroup);
}
command = new DefineStructCommand(convInfo);
return true;
}
Command::ExecuteResult DefineStructCommand::execute(ScriptExecutionContext &scriptExecCtx) {
scriptExecCtx.getGameData()._conversationInfo = _conversationInfo;
return Command::Finished;
}
Common::String DefineStructCommand::debugString() const {
return "DEFINE_STRUCT <data omitted>";
}
}

View File

@@ -0,0 +1,41 @@
/* 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 "mutationofjb/commands/seqcommand.h"
#include "mutationofjb/gamedata.h"
namespace MutationOfJB {
class DefineStructCommandParser : public SeqCommandParser {
public:
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class DefineStructCommand : public SeqCommand {
public:
DefineStructCommand(const ConversationInfo& convInfo) : _conversationInfo(convInfo) {}
Command::ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
ConversationInfo _conversationInfo;
};
}

View File

@@ -0,0 +1,259 @@
/* 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 "mutationofjb/commands/endblockcommand.h"
#include "mutationofjb/script.h"
#include "mutationofjb/commands/conditionalcommand.h"
#include "common/str.h"
#include "common/debug.h"
/** @file
* <look> | <walk> | <talk> | <pickup> | <use> | <else> | <macro> | <extra> | <endRandom>
*
* look ::= ("#L " | "-L ") <object>
* walk ::= ("#W " | "-W ") <object>
* talk ::= ("#T " | "-T ") <object>
* pickup ::= ("#P " | "-P ") <object1>
* use ::= ("#U " | "-U ") <object1> [<object2>]
* else ::= ("#ELSE" | "-ELSE") [<tag>]
* macro ::= "#MACRO " <name>
* extra ::= "#EXTRA" <name>
* endRandom ::= "\"
*
* If a line starts with '#', '=', '-', '\' it is treated as the end of a section.
* However, at the same time it can also start a new section depending on what follows.
*
* #L (look), #W (walk), #T (talk), #U (use) sections are executed
* when the user starts corresponding action on the object or in case of "use" up to two objects.
* The difference between '#' and '-' version is whether the player walks towards the object ('#') or not ('-').
*
* #ELSE is used by conditional commands (see comments for IfCommand and others).
*
* #MACRO starts a new macro. Global script can call macros from local script and vice versa.
*
* #EXTRA defines an "extra" section. This is called from dialog responses ("TALK TO HIM" command).
*
* TODO: TIMERPROC.
*/
namespace MutationOfJB {
bool EndBlockCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
if (line.empty()) {
return false;
}
const char firstChar = line.firstChar();
if (firstChar != '#' && firstChar != '=' && firstChar != '-' && firstChar != '\\') {
return false;
}
// This is the start or end of section/block.
command = new EndBlockCommand();
if (line.size() >= 4 && (line.hasPrefix("#L ") || line.hasPrefix("-L "))) {
ActionInfo ai = {ActionInfo::Look, line.c_str() + 3, "", firstChar == '#', nullptr};
parseCtx._actionInfos.push_back(ai);
_pendingActionInfos.push_back(parseCtx._actionInfos.size() - 1);
} else if (line.size() >= 4 && (line.hasPrefix("#W ") || line.hasPrefix("-W "))) {
ActionInfo ai = {ActionInfo::Walk, line.c_str() + 3, "", firstChar == '#', nullptr};
parseCtx._actionInfos.push_back(ai);
_pendingActionInfos.push_back(parseCtx._actionInfos.size() - 1);
} else if (line.size() >= 4 && (line.hasPrefix("#T ") || line.hasPrefix("-T "))) {
ActionInfo ai = {ActionInfo::Talk, line.c_str() + 3, "", firstChar == '#', nullptr};
parseCtx._actionInfos.push_back(ai);
_pendingActionInfos.push_back(parseCtx._actionInfos.size() - 1);
} else if (line.size() >= 4 && (line.hasPrefix("#P ") || line.hasPrefix("-P "))) {
ActionInfo ai = {ActionInfo::PickUp, line.c_str() + 3, "", firstChar == '#', nullptr};
parseCtx._actionInfos.push_back(ai);
_pendingActionInfos.push_back(parseCtx._actionInfos.size() - 1);
} else if (line.size() >= 4 && (line.hasPrefix("#U ") || line.hasPrefix("-U "))) {
int secondObjPos = -1;
for (uint i = 3; i < line.size(); ++i) {
if (line[i] == ' ') {
secondObjPos = i + 1;
break;
}
}
Common::String obj1;
Common::String obj2;
if (secondObjPos == -1) {
obj1 = line.c_str() + 3;
} else {
obj1 = Common::String(line.c_str() + 3, secondObjPos - 4);
obj2 = line.c_str() + secondObjPos;
}
ActionInfo ai = {
ActionInfo::Use,
obj1,
obj2,
firstChar == '#',
nullptr
};
parseCtx._actionInfos.push_back(ai);
_pendingActionInfos.push_back(parseCtx._actionInfos.size() - 1);
} else if ((line.hasPrefix("#ELSE") || line.hasPrefix("=ELSE"))) {
_elseFound = true;
_ifTag = 0;
if (line.size() >= 6) {
_ifTag = line[5];
}
} else if (line.size() >= 8 && line.hasPrefix("#MACRO")) {
NameAndCommand nc = {line.c_str() + 7, command};
_foundMacros.push_back(nc);
} else if (line.size() >= 10 && line.hasPrefix("#STARTUP")) {
const uint8 startupId = atoi(line.c_str() + 9);
IdAndCommand ic = {startupId, command};
_foundStartups.push_back(ic);
} else if (line.size() >= 7 && line.hasPrefix("#EXTRA")) {
NameAndCommand nc = {line.c_str() + 6, command};
_foundExtras.push_back(nc);
}
if (firstChar == '#') {
_hashFound = true;
}
return true;
}
void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser) {
if (_elseFound || _hashFound) {
if (newCommand) {
ScriptParseContext::ConditionalCommandInfos::iterator it = parseCtx._pendingCondCommands.begin();
while (it != parseCtx._pendingCondCommands.end()) {
if ((it->_firstHash && _hashFound) || (!it->_firstHash && it->_tag == _ifTag)) {
it->_command->setFalseCommand(newCommand);
it = parseCtx._pendingCondCommands.erase(it);
} else {
++it;
}
}
}
_elseFound = false;
_hashFound = false;
_ifTag = 0;
}
if (!_foundMacros.empty()) {
if (newCommand) {
for (NameAndCommandArray::iterator it = _foundMacros.begin(); it != _foundMacros.end();) {
if (it->_command != oldCommand) {
it++;
continue;
}
if (!parseCtx._macros.contains(it->_name)) {
parseCtx._macros[it->_name] = newCommand;
} else {
warning("Macro '%s' already exists", it->_name.c_str());
}
it = _foundMacros.erase(it);
}
}
}
if (!_foundStartups.empty()) {
if (newCommand) {
for (IdAndCommandArray::iterator it = _foundStartups.begin(); it != _foundStartups.end();) {
if (it->_command != oldCommand) {
it++;
continue;
}
if (!parseCtx._startups.contains(it->_id)) {
parseCtx._startups[it->_id] = newCommand;
} else {
warning("Startup %u already exists", (unsigned int) it->_id);
}
it = _foundStartups.erase(it);
}
}
}
if (!_foundExtras.empty()) {
if (newCommand) {
for (NameAndCommandArray::iterator it = _foundExtras.begin(); it != _foundExtras.end();) {
if (it->_command != oldCommand) {
it++;
continue;
}
if (!parseCtx._extras.contains(it->_name)) {
parseCtx._extras[it->_name] = newCommand;
} else {
warning("Extra '%s' already exists", it->_name.c_str());
}
it = _foundExtras.erase(it);
}
}
}
if (newCommandParser != this) {
if (!_pendingActionInfos.empty()) {
for (Common::Array<uint>::iterator it = _pendingActionInfos.begin(); it != _pendingActionInfos.end(); ++it) {
parseCtx._actionInfos[*it]._command = newCommand;
}
_pendingActionInfos.clear();
}
}
}
void EndBlockCommandParser::finish(ScriptParseContext &) {
_elseFound = false;
_hashFound = false;
_ifTag = 0;
if (!_pendingActionInfos.empty()) {
debug("Problem: Pending action infos from end block parser is not empty!");
}
if (!_foundMacros.empty()) {
debug("Problem: Found macros from end block parser is not empty!");
}
if (!_foundStartups.empty()) {
debug("Problem: Found startups from end block parser is not empty!");
}
if (!_foundExtras.empty()) {
debug("Problem: Found extras from end block parser is not empty!");
}
_pendingActionInfos.clear();
_foundMacros.clear();
_foundStartups.clear();
_foundExtras.clear();
}
Command::ExecuteResult EndBlockCommand::execute(ScriptExecutionContext &scriptExecCtx) {
_nextCmd = scriptExecCtx.popReturnCommand();
return Finished;
}
Command *EndBlockCommand::next() const {
return _nextCmd;
}
Common::String EndBlockCommand::debugString() const {
return "ENDBLOCK";
}
}

View File

@@ -0,0 +1,77 @@
/* 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 MUTATIONOFJB_ENDBLOCKCOMMAND_H
#define MUTATIONOFJB_ENDBLOCKCOMMAND_H
#include "mutationofjb/commands/command.h"
#include "common/scummsys.h"
#include "common/array.h"
#include "common/str.h"
namespace MutationOfJB {
struct ActionInfo;
class EndBlockCommandParser : public CommandParser {
public:
EndBlockCommandParser() : _elseFound(false), _hashFound(false), _ifTag(0) {}
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser) override;
void finish(ScriptParseContext &parseCtx) override;
private:
bool _elseFound;
bool _hashFound;
char _ifTag;
Common::Array<uint> _pendingActionInfos;
struct NameAndCommand {
Common::String _name;
Command *_command;
};
struct IdAndCommand {
uint8 _id;
Command *_command;
};
typedef Common::Array<NameAndCommand> NameAndCommandArray;
typedef Common::Array<IdAndCommand> IdAndCommandArray;
NameAndCommandArray _foundMacros;
IdAndCommandArray _foundStartups;
NameAndCommandArray _foundExtras;
};
class EndBlockCommand : public Command {
public:
EndBlockCommand() : _nextCmd(nullptr) {}
static bool ParseFunc(const Common::String &line, ScriptParseContext &parseContext, Command *&command);
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Command *next() const override;
Common::String debugString() const override;
private:
Command *_nextCmd;
};
}
#endif

View File

@@ -0,0 +1,79 @@
/* 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 "mutationofjb/commands/gotocommand.h"
#include "mutationofjb/commands/labelcommand.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/script.h"
/** @file
* "GOTO " <label>
*
* Jumps to a label.
*/
namespace MutationOfJB {
bool GotoCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
if (line.size() < 6 || !line.hasPrefix("GOTO")) {
return false;
}
Common::String label = line.c_str() + 5;
GotoCommand *gotoCmd = new GotoCommand();
if (parseCtx._labels.contains(label)) {
// We already have the label, set it.
gotoCmd->setLabelCommand(parseCtx._labels[label]);
} else {
// Label is after goto, add to pending list.
parseCtx._pendingGotos[label].push_back(gotoCmd);
}
command = gotoCmd;
return true;
}
void GotoCommand::setLabelCommand(LabelCommand *labelCmd) {
_labelCommand = labelCmd;
}
Command::ExecuteResult GotoCommand::execute(ScriptExecutionContext &) {
// Intentionally empty.
return Finished;
}
Command *GotoCommand::next() const {
return _labelCommand;
}
Common::String GotoCommand::debugString() const {
if (!_labelCommand) {
return "GOTO (null)";
}
return Common::String::format("GOTO %s", _labelCommand->getName().c_str());
}
}

View File

@@ -0,0 +1,54 @@
/* 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 MUTATIONOFJB_GOTOCOMMAND_H
#define MUTATIONOFJB_GOTOCOMMAND_H
#include "mutationofjb/commands/command.h"
#include "common/str.h"
namespace MutationOfJB {
class LabelCommand;
class GotoCommandParser : public CommandParser {
public:
GotoCommandParser() {}
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class GotoCommand : public Command {
public:
GotoCommand() : _labelCommand(nullptr) {}
void setLabelCommand(LabelCommand *labelCmd);
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Command *next() const override;
Common::String debugString() const override;
private:
LabelCommand *_labelCommand;
};
}
#endif

View File

@@ -0,0 +1,108 @@
/* 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 "mutationofjb/commands/ifcommand.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/script.h"
#include "common/str.h"
/** @file
* "IF" <tag> <sceneId> <objectId> <value> ["!"]
*
* IF command compares the value of the WX pseudo-register of the object in the specified scene.
* If the values match, execution continues to the next line.
* Otherwise execution continues after first "#ELSE" or "=ELSE" with the same <tag>.
* The logic can be reversed with exclamation mark at the end.
*
* <tag> is always 1 character long, <sceneId> and <objectId> 2 characters long.
*
* Please note that this does not work like you are used to from saner languages.
* IF does not have any blocks. It only searches for first #ELSE, so you can have stuff like:
* IF something
* IF something else
* #ELSE
* ...
* This is effectively logical AND.
*/
namespace MutationOfJB {
bool IfCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
// IFtss oo val!
// <t> 1B Tag.
// <ss> 2B Scene.
// <oo> 2B Object ID.
// <val> VL Value.
// ! 1B Negation (optional).
if (line.size() < 10) {
return false;
}
if (!line.hasPrefix("IF")) {
return false;
}
const char *const cstr = line.c_str();
const char tag = cstr[2] == ' ' ? 0 : cstr[2];
const uint8 sceneId = atoi(cstr + 3);
const uint8 objectId = atoi(cstr + 6);
const uint8 value = atoi(cstr + 9);
const bool negative = (line.lastChar() == '!');
_tags.push(tag);
command = new IfCommand(sceneId, objectId, value, negative);
return true;
}
IfCommand::IfCommand(uint8 sceneId, uint8 objectId, uint16 value, bool negative) :
_sceneId(sceneId),
_objectId(objectId),
_value(value),
_negative(negative) {}
Command::ExecuteResult IfCommand::execute(ScriptExecutionContext &scriptExecCtx) {
Scene *const scene = scriptExecCtx.getGameData().getScene(_sceneId);
if (!scene) {
return Finished;
}
Object *const object = scene->getObject(_objectId, true);
if (!object) {
return Finished;
}
_cachedResult = (object->_WX == _value);
if (_negative) {
_cachedResult = !_cachedResult;
}
return Finished;
}
Common::String IfCommand::debugString() const {
return Common::String::format("IF scene%d.object%d.WX %s %d", _sceneId, _objectId, _negative ? "!=" : "==", _value);
}
}

View File

@@ -0,0 +1,54 @@
/* 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 MUTATIONOFJB_IFCOMMAND_H
#define MUTATIONOFJB_IFCOMMAND_H
#include "mutationofjb/commands/conditionalcommand.h"
#include "common/scummsys.h"
namespace MutationOfJB {
class ScriptParseContext;
class IfCommandParser : public ConditionalCommandParser {
public:
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
private:
};
class IfCommand : public ConditionalCommand {
public:
IfCommand(uint8 sceneId, uint8 objectId, uint16 value, bool negative);
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
uint8 _sceneId;
uint8 _objectId;
uint16 _value;
bool _negative;
};
}
#endif

View File

@@ -0,0 +1,86 @@
/* 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 "mutationofjb/commands/ifitemcommand.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/script.h"
#include "mutationofjb/util.h"
#include "common/str.h"
/** @file
* "IFITEM " <item> [ "!" ]
*
* IFITEM command tests whether an item is in the inventory.
* If it is, execution continues to the next line.
* Otherwise execution continues after first "#ELSE" or "=ELSE".
* The logic can be reversed with exclamation mark at the end.
*
* Please note that this does not work like you are used to from saner languages.
* IFITEM does not have any blocks. It only searches for first #ELSE, so you can have stuff like:
* IFITEM item1
* IFITEM item2
* #ELSE
* ...
* This is effectively logical AND.
*/
namespace MutationOfJB {
bool IfItemCommandParser::parse(const Common::String &line, ScriptParseContext &parseContext, Command *&command) {
if (line.size() < 8) {
return false;
}
if (!line.hasPrefix("IFITEM")) {
return false;
}
const bool negative = (line.lastChar() == '!');
Common::String item(line.c_str() + 7);
if (negative) {
item.deleteLastChar(); // Remove '!'.
}
_tags.push(0);
command = new IfItemCommand(item, negative);
return true;
}
IfItemCommand::IfItemCommand(const Common::String &item, bool negative) :
_item(item),
_negative(negative) {}
Command::ExecuteResult IfItemCommand::execute(ScriptExecutionContext &scriptExecCtx) {
_cachedResult = scriptExecCtx.getGameData()._inventory.hasItem(_item);
if (_negative) {
_cachedResult = !_cachedResult;
}
return Finished;
}
Common::String IfItemCommand::debugString() const {
return Common::String::format("IFITEM %s%s", _negative ? "NOT " : "", _item.c_str());
}
}

View File

@@ -0,0 +1,52 @@
/* 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 MUTATIONOFJB_IFITEMCOMMAND_H
#define MUTATIONOFJB_IFITEMCOMMAND_H
#include "mutationofjb/commands/conditionalcommand.h"
#include "common/scummsys.h"
#include "common/str.h"
namespace MutationOfJB {
class ScriptParseContext;
class IfItemCommandParser : public ConditionalCommandParser {
public:
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class IfItemCommand : public ConditionalCommand {
public:
IfItemCommand(const Common::String &item, bool negative);
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
Common::String _item;
bool _negative;
};
}
#endif

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 "mutationofjb/commands/ifpiggycommand.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/script.h"
#include "mutationofjb/util.h"
#include "common/str.h"
/** @file
* "IFPIGGY"
*
* IFPIGGY command tests whether current loaded APK file (character animation) is "piggy.apk".
* If it is, execution continues to the next line.
* Otherwise execution continues after first "#ELSE" or "=ELSE".
*
* Please note that this does not work like you are used to from saner languages.
* IFPIGGY does not have any blocks. It only searches for first #ELSE, so you can have stuff like:
* IFPIGGY
* IFITEM someitem
* #ELSE
* ...
* This is effectively logical AND.
*/
namespace MutationOfJB {
bool IfPiggyCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
if (line != "IFPIGGY") {
return false;
}
_tags.push(0);
command = new IfPiggyCommand();
return true;
}
Command::ExecuteResult IfPiggyCommand::execute(ScriptExecutionContext &scriptExecCtx) {
_cachedResult = scriptExecCtx.getGameData()._currentAPK == "piggy.apk";
return Finished;
}
Common::String IfPiggyCommand::debugString() const {
return "IFPIGGY";
}
}

View File

@@ -0,0 +1,48 @@
/* 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 MUTATIONOFJB_IFPIGGYCOMMAND_H
#define MUTATIONOFJB_IFPIGGYCOMMAND_H
#include "mutationofjb/commands/conditionalcommand.h"
#include "common/scummsys.h"
#include "common/str.h"
namespace MutationOfJB {
class ScriptParseContext;
class IfPiggyCommandParser : public ConditionalCommandParser {
public:
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class IfPiggyCommand : public ConditionalCommand {
public:
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
};
}
#endif

View File

@@ -0,0 +1,76 @@
/* 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 "mutationofjb/commands/labelcommand.h"
#include "mutationofjb/commands/gotocommand.h"
#include "mutationofjb/script.h"
/** @file
* <label> ":"
*
* Creates a label.
*/
namespace MutationOfJB {
bool LabelCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
if (line.lastChar() != ':') {
return false;
}
Common::String label = line;
label.deleteLastChar();
LabelCommand *labelCmd = new LabelCommand(label);
if (!parseCtx._labels.contains(label)) {
parseCtx._labels[label] = labelCmd;
} else {
warning("Label '%s' already exists", label.c_str());
}
if (parseCtx._pendingGotos.contains(label)) {
GotoCommands &gotos = parseCtx._pendingGotos[label];
for (GotoCommands::const_iterator it = gotos.begin(); it != gotos.end(); ++it) {
(*it)->setLabelCommand(labelCmd);
}
gotos.clear();
}
command = labelCmd;
return true;
}
const Common::String &LabelCommand::getName() const {
return _name;
}
Command::ExecuteResult LabelCommand::execute(ScriptExecutionContext &) {
// Intentionally empty.
return Finished;
}
Common::String LabelCommand::debugString() const {
return Common::String::format("LABEL %s", _name.c_str());
}
}

View File

@@ -0,0 +1,50 @@
/* 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 MUTATIONOFJB_LABELCOMMAND_H
#define MUTATIONOFJB_LABELCOMMAND_H
#include "mutationofjb/commands/seqcommand.h"
#include "common/str.h"
namespace MutationOfJB {
class LabelCommandParser : public SeqCommandParser {
public:
LabelCommandParser() {}
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class LabelCommand : public SeqCommand {
public:
LabelCommand(const Common::String &name) : _name(name) {}
const Common::String &getName() const;
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
Common::String _name;
};
}
#endif

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/>.
*
*/
#include "mutationofjb/commands/loadplayercommand.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/script.h"
/** @file
* "RABLOAD " <apkFrameFirst> " " <apkFrameLast> " " <playerFrameFirst> " " <palIndexFirst> " " <apkFilename>
*
* Load player frames from APK file specified by apkFileName.
* Only frames between apkFrameFirst and apkFrameLast are loaded onto position defined by playerFrameFirst.
* Player's palette is loaded at index defined by palIndexFirst.
*/
namespace MutationOfJB {
bool LoadPlayerCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
if (line.size() < 25 || !line.hasPrefix("RABLOAD ")) {
return false;
}
const uint8 apkFrameFirst = atoi(line.c_str() + 8);
const uint8 apkFrameLast = atoi(line.c_str() + 12);
const uint8 playerFrameFirst = atoi(line.c_str() + 16);
const uint8 palIndexFirst = atoi(line.c_str() + 20);
const Common::String apkFileName = line.c_str() + 24;
command = new LoadPlayerCommand(apkFrameFirst, apkFrameLast, playerFrameFirst, palIndexFirst, apkFileName);
return true;
}
Command::ExecuteResult LoadPlayerCommand::execute(ScriptExecutionContext &scriptExeCtx) {
scriptExeCtx.getGameData()._currentAPK = _apkFileName;
return Command::Finished;
}
Common::String LoadPlayerCommand::debugString() const {
return Common::String::format("LOADPLAYER %u %u %u %u %s", (unsigned int) _apkFrameFirst, (unsigned int) _apkFrameLast, (unsigned int) _playerFrameFirst, (unsigned int) _palIndexFirst, _apkFileName.c_str());
}
}

View File

@@ -0,0 +1,56 @@
/* 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 MUTATIONOFJB_LOADPLAYERCOMMAND_H
#define MUTATIONOFJB_LOADPLAYERCOMMAND_H
#include "mutationofjb/commands/seqcommand.h"
#include "mutationofjb/tasks/task.h"
#include "common/scummsys.h"
#include "common/str.h"
namespace MutationOfJB {
class ConversationTask;
class LoadPlayerCommandParser : public SeqCommandParser {
public:
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class LoadPlayerCommand : public SeqCommand {
public:
LoadPlayerCommand(uint8 apkFrameFirst, uint8 apkFrameLast, uint8 playerFrameFirst, uint8 palIndexFirst, const Common::String &apkFileName) : _apkFrameFirst(apkFrameFirst), _apkFrameLast(apkFrameLast), _playerFrameFirst(playerFrameFirst), _palIndexFirst(palIndexFirst), _apkFileName(apkFileName) {}
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
uint8 _apkFrameFirst;
uint8 _apkFrameLast;
uint8 _playerFrameFirst;
uint8 _palIndexFirst;
Common::String _apkFileName;
};
}
#endif

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/>.
*
*/
#include "mutationofjb/commands/newroomcommand.h"
#include "mutationofjb/script.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "common/str.h"
/** @file
* "NEWROOM " <sceneId> " " <x> " " <y> [ " " <frame> ]
*
* NEWROOM changes the current scene. While doing that, it also executes STARTUP section for the new room.
* However, after that, the execution goes back to the old script to finish commands after NEWROOM.
*
* All parameters are supposed to be 3 characters long.
* SceneId is the scene to load, x and y are the player's new position and frame is the player's new frame (orientation).
*/
namespace MutationOfJB {
bool NewRoomCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
if (line.size() < 19 || !line.hasPrefix("NEWROOM")) {
return false;
}
const uint8 sceneId = atoi(line.c_str() + 8);
const uint16 x = atoi(line.c_str() + 12);
const uint16 y = atoi(line.c_str() + 16);
uint8 frame = 0;
if (line.size() >= 21)
frame = atoi(line.c_str() + 20);
command = new NewRoomCommand(sceneId, x, y, frame);
return true;
}
NewRoomCommand::NewRoomCommand(uint8 sceneId, uint16 x, uint16 y, uint8 frame) : _sceneId(sceneId), _x(x), _y(y), _frame(frame), _innerExecCtx(nullptr) {}
Command::ExecuteResult NewRoomCommand::execute(ScriptExecutionContext &scriptExecCtx) {
Game &game = scriptExecCtx.getGame();
// Execute new startup section.
ExecuteResult res;
if (!_innerExecCtx) {
Script *newScript = game.changeSceneDelayScript(_sceneId, game.getGameData()._partB);
_innerExecCtx = new ScriptExecutionContext(scriptExecCtx.getGame(), newScript);
res = _innerExecCtx->startStartupSection();
} else {
res = _innerExecCtx->runActiveCommand();
}
if (res == Finished) {
delete _innerExecCtx;
_innerExecCtx = nullptr;
}
return res;
}
Common::String NewRoomCommand::debugString() const {
return Common::String::format("NEWROOM %u %u %u %u", (unsigned int) _sceneId, (unsigned int) _x, (unsigned int) _y, (unsigned int) _frame);
}
}

View File

@@ -0,0 +1,55 @@
/* 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 MUTATIONOFJB_NEWROOMCOMMAND_H
#define MUTATIONOFJB_NEWROOMCOMMAND_H
#include "mutationofjb/commands/seqcommand.h"
namespace MutationOfJB {
class ScriptExecutionContext;
class NewRoomCommandParser : public SeqCommandParser {
public:
NewRoomCommandParser() {}
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class NewRoomCommand : public SeqCommand {
public:
NewRoomCommand(uint8 sceneId, uint16 x, uint16 y, uint8 frame);
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
uint8 _sceneId;
uint16 _x;
uint16 _y;
uint8 _frame;
ScriptExecutionContext *_innerExecCtx;
};
}
#endif

View File

@@ -0,0 +1,60 @@
/* 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 "mutationofjb/commands/playanimationcommand.h"
#include "mutationofjb/game.h"
#include "mutationofjb/room.h"
#include "mutationofjb/script.h"
/** @file
* ( "FLB " | "FLX" ) <fromFrame> " " <toFrame>
*
* Plays the specified frames from room animation.
*
* TODO: Parse all arguments of this command.
* TODO: Actually play the animation instead of just showing last frame.
*/
namespace MutationOfJB {
bool PlayAnimationCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
if (line.size() < 11 || (!line.hasPrefix("FLB ") && !line.hasPrefix("FLX ")))
return false;
const int fromFrame = atoi(line.c_str() + 4);
const int toFrame = atoi(line.c_str() + 8);
command = new PlayAnimationCommand(fromFrame, toFrame);
return true;
}
Command::ExecuteResult PlayAnimationCommand::execute(ScriptExecutionContext &scriptExecCtx) {
scriptExecCtx.getGame().getRoom().drawFrames(_fromFrame - 1, _toFrame - 1);
return Finished;
}
Common::String PlayAnimationCommand::debugString() const {
return Common::String::format("PLAYROOMANIM %u %u", (unsigned int) _fromFrame, (unsigned int) _toFrame);
}
}

View File

@@ -0,0 +1,51 @@
/* 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 MUTATIONOFJB_PLAYANIMATIONCOMMAND_H
#define MUTATIONOFJB_PLAYANIMATIONCOMMAND_H
#include "mutationofjb/commands/seqcommand.h"
#include "common/str.h"
namespace MutationOfJB {
class PlayAnimationCommandParser : public SeqCommandParser {
public:
PlayAnimationCommandParser() {}
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class PlayAnimationCommand : public SeqCommand {
public:
PlayAnimationCommand(int fromFrame, int toFrame) : _fromFrame(fromFrame), _toFrame(toFrame) {}
const Common::String &getName() const;
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
int _fromFrame;
int _toFrame;
};
}
#endif

View File

@@ -0,0 +1,114 @@
/* 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 "mutationofjb/commands/randomcommand.h"
#include "mutationofjb/game.h"
#include "mutationofjb/script.h"
#include "common/debug.h"
#include "common/random.h"
/** @file
* "RANDOM " <numChoices>
*
* RANDOM command randomly picks one of the command blocks that
* follow it and jumps to its start.
*
* These blocks start with "/" and end with "\". The end of a random
* block also ends the current section. The number of blocks must
* match numChoices.
*/
namespace MutationOfJB {
bool RandomCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
if (line.size() < 8 || !line.hasPrefix("RANDOM")) {
return false;
}
int numChoices = atoi(line.c_str() + 7);
if (parseCtx._pendingRandomCommand) {
// Nested RANDOM commands are unused and not properly supported by the original game.
warning("Ignoring nested RANDOM command.");
} else if (numChoices >= 1) {
RandomCommand *randomCommand = new RandomCommand(static_cast<uint>(numChoices));
parseCtx._pendingRandomCommand = randomCommand;
command = randomCommand;
} else {
warning("Ignoring malformed RANDOM command with %d choices.", numChoices);
}
return true;
}
bool RandomBlockStartParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&) {
if (line != "/") {
return false;
}
if (!parseCtx._pendingRandomCommand) {
warning("Unexpected start of RANDOM block");
}
return true;
}
void RandomBlockStartParser::transition(ScriptParseContext &parseCtx, Command *, Command *newCommand, CommandParser *) {
RandomCommand *randomCommand = parseCtx._pendingRandomCommand;
if (newCommand && randomCommand) {
randomCommand->_choices.push_back(newCommand);
if (randomCommand->_choices.size() == randomCommand->_numChoices) {
parseCtx._pendingRandomCommand = nullptr;
}
}
}
RandomCommand::RandomCommand(uint numChoices)
: _numChoices(numChoices),
_chosenNext(nullptr) {
_choices.reserve(numChoices);
}
Command::ExecuteResult RandomCommand::execute(ScriptExecutionContext &scriptExecCtx) {
assert(!_choices.empty());
Common::RandomSource &rng = scriptExecCtx.getGame().getRandomSource();
uint choice = rng.getRandomNumber(_choices.size() - 1);
_chosenNext = _choices[choice];
return Finished;
}
Command *RandomCommand::next() const {
return _chosenNext;
}
Common::String RandomCommand::debugString() const {
return Common::String::format("RANDOM %u", _numChoices);
}
const RandomCommand::Choices &RandomCommand::getChoices() const {
return _choices;
}
}

View File

@@ -0,0 +1,69 @@
/* 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 MUTATIONOFJB_RANDOMCOMMAND_H
#define MUTATIONOFJB_RANDOMCOMMAND_H
#include "mutationofjb/commands/command.h"
#include "common/array.h"
#include "common/scummsys.h"
namespace MutationOfJB {
class RandomCommandParser : public CommandParser {
public:
RandomCommandParser() {}
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class RandomBlockStartParser : public CommandParser {
public:
RandomBlockStartParser() {}
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser) override;
};
class RandomCommand : public Command {
friend class RandomBlockStartParser;
public:
typedef Common::Array<Command *> Choices;
RandomCommand(uint numChoices);
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Command *next() const override;
Common::String debugString() const override;
const Choices &getChoices() const;
private:
uint _numChoices;
Choices _choices;
Command *_chosenNext;
};
}
#endif

View File

@@ -0,0 +1,52 @@
/* 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 "mutationofjb/commands/removeallitemscommand.h"
#include "mutationofjb/script.h"
#include "mutationofjb/gamedata.h"
/** @file
* "DELALLITEMS"
*
* Removes all items from inventory.
*/
namespace MutationOfJB {
bool RemoveAllItemsCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
if (line != "DELALLITEMS") {
return false;
}
command = new RemoveAllItemsCommand();
return true;
}
Command::ExecuteResult RemoveAllItemsCommand::execute(ScriptExecutionContext &scriptExecCtx) {
scriptExecCtx.getGameData()._inventory.removeAllItems();
return Finished;
}
Common::String RemoveAllItemsCommand::debugString() const {
return "DELALLITEM";
}
}

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 MUTATIONOFJB_REMOVEALLITEMSCOMMAND_H
#define MUTATIONOFJB_REMOVEALLITEMSCOMMAND_H
#include "mutationofjb/commands/seqcommand.h"
namespace MutationOfJB {
class RemoveAllItemsCommandParser : public SeqCommandParser {
public:
RemoveAllItemsCommandParser() {}
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class RemoveAllItemsCommand : public SeqCommand {
public:
RemoveAllItemsCommand() {}
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
};
}
#endif

View File

@@ -0,0 +1,52 @@
/* 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 "mutationofjb/commands/removeitemcommand.h"
#include "mutationofjb/script.h"
#include "mutationofjb/gamedata.h"
/** @file
* "DELITEM " <item>
*
* Removes item from inventory.
*/
namespace MutationOfJB {
bool RemoveItemCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
if (!line.hasPrefix("DELITEM") || line.size() < 9) {
return false;
}
command = new RemoveItemCommand(line.c_str() + 8);
return true;
}
Command::ExecuteResult RemoveItemCommand::execute(ScriptExecutionContext &scriptExecCtx) {
scriptExecCtx.getGameData()._inventory.removeItem(_item);
return Finished;
}
Common::String RemoveItemCommand::debugString() const {
return Common::String::format("DELITEM '%s'", _item.c_str());
}
}

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 MUTATIONOFJB_REMOVEITEMCOMMAND_H
#define MUTATIONOFJB_REMOVEITEMCOMMAND_H
#include "mutationofjb/commands/seqcommand.h"
#include "common/str.h"
namespace MutationOfJB {
class RemoveItemCommandParser : public SeqCommandParser {
public:
RemoveItemCommandParser() {}
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class RemoveItemCommand : public SeqCommand {
public:
RemoveItemCommand(const Common::String &item) : _item(item) {}
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
Common::String _item;
};
}
#endif

View File

@@ -0,0 +1,78 @@
/* 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 "mutationofjb/commands/renamecommand.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "common/algorithm.h"
/** @file
* "REN " <oldName> " " <newName>
*
* Renames every door, static (in the current scene) and inventory item
* with the name oldName to newName.
*/
namespace MutationOfJB {
bool RenameCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
if (line.size() < 7 || !line.hasPrefix("REN")) {
return false;
}
Common::String::const_iterator sep = Common::find(line.begin() + 4, line.end(), ' ');
if (sep == line.end() || sep + 1 == line.end()) {
return false;
}
const Common::String oldName(line.begin() + 4, sep);
const Common::String newName(sep + 1, line.end());
command = new RenameCommand(oldName, newName);
return true;
}
Command::ExecuteResult RenameCommand::execute(ScriptExecutionContext &scriptExecCtx) {
Scene *const scene = scriptExecCtx.getGameData().getCurrentScene();
for (int i = 1; i <= scene->getNoDoors(); ++i) {
Door *const door = scene->getDoor(i);
if (strcmp(door->_name, _oldName.c_str()) == 0) {
strncpy(door->_name, _newName.c_str(), MAX_ENTITY_NAME_LENGTH);
}
}
for (int i = 1; i <= scene->getNoStatics(); ++i) {
Static *const stat = scene->getStatic(i);
if (strcmp(stat->_name, _oldName.c_str()) == 0) {
strncpy(stat->_name, _newName.c_str(), MAX_ENTITY_NAME_LENGTH);
}
}
scriptExecCtx.getGameData().getInventory().renameItem(_oldName, _newName);
return Finished;
}
Common::String RenameCommand::debugString() const {
return Common::String::format("RENAME '%s' '%s'", _oldName.c_str(), _newName.c_str());
}
}

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 MUTATIONOFJB_RENAMECOMMAND_H
#define MUTATIONOFJB_RENAMECOMMAND_H
#include "mutationofjb/commands/seqcommand.h"
#include "common/str.h"
namespace MutationOfJB {
class RenameCommandParser : public SeqCommandParser {
public:
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class RenameCommand : public SeqCommand {
public:
RenameCommand(const Common::String &oldName, const Common::String &newName) : _oldName(oldName), _newName(newName) {}
Command::ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
Common::String _oldName;
Common::String _newName;
};
}
#endif

View File

@@ -0,0 +1,169 @@
/* 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 "mutationofjb/commands/saycommand.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/script.h"
#include "mutationofjb/tasks/saytask.h"
#include "mutationofjb/tasks/taskmanager.h"
#include "common/str.h"
#include "common/debug.h"
#include "common/debug-channels.h"
/** @file
* <firstLine> { <CRLF> <additionalLine> }
*
* firstLine ::= ("SM" | "SLM" | "NM" | "NLM") " " <lineToSay> [ "<" <voiceFile> | "<!" ]
* additionalLine ::= <skipped> " " <lineToSay> ( "<" <voiceFile> | "<!" )
*
* Say command comes in four variants: SM, SLM, NM and NLM.
* Note: In script files, they are usually written as *SM.
* The asterisk is ignored by the readLine function.
*
* Each of them plays a voice file (if present) and/or shows a message
* (if voice file not present or subtitles are enabled).
*
* The difference between versions starting with "S" and "N" is that
* the "N" version does not show talking animation.
*
* The "L" versions are "blocking", i.e. they wait for the previous say command to finish.
*
* If the line ends with "<!", it means the message continues to the next line.
* Next line usually has "SM" (or other variant) repeated, but it does not have to.
* Then we have the rest of the string to say (which is concatenated with the previous line)
* and possibly the voice file or "<!" again.
*/
namespace MutationOfJB {
bool SayCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
bool waitForPrevious = false;
bool talkingAnimation = false;
if (line.hasPrefix("SM")) {
waitForPrevious = false;
talkingAnimation = true;
} else if (line.hasPrefix("SLM")) {
waitForPrevious = true;
talkingAnimation = true;
} else if (line.hasPrefix("NM")) {
waitForPrevious = false;
talkingAnimation = false;
} else if (line.hasPrefix("NLM")) {
waitForPrevious = true;
talkingAnimation = false;
} else {
return false;
}
Common::String currentLine = line;
Common::String lineToSay;
Common::String voiceFile;
bool cont = false;
bool firstPass = true;
do {
cont = false;
uint startPos;
for (startPos = 0; startPos < currentLine.size(); ++startPos) {
if (currentLine[startPos] == ' ') {
break;
}
}
if (startPos == currentLine.size()) {
if (!firstPass) {
warning("Unable to parse line '%s'", currentLine.c_str());
break;
}
}
if (startPos != currentLine.size()) {
startPos++;
}
uint endPos;
for (endPos = startPos; endPos < currentLine.size(); ++endPos) {
if (currentLine[endPos] == '<') {
break;
}
}
Common::String talkStr(currentLine.c_str() + startPos, endPos - startPos);
if (endPos != currentLine.size()) {
const char *end = currentLine.c_str() + endPos + 1;
if (end[0] == '!') {
cont = true;
} else {
voiceFile = end;
}
}
if (talkStr.lastChar() == '~') {
debug("Found say command ending with '~'. Please take a look.");
}
if (lineToSay.empty()) {
lineToSay = talkStr;
} else {
lineToSay += " " + talkStr;
}
if (cont) {
if (!parseCtx.readLine(currentLine)) {
cont = false;
}
}
firstPass = false;
} while (cont);
command = new SayCommand(lineToSay, voiceFile, waitForPrevious, talkingAnimation);
return true;
}
Command::ExecuteResult SayCommand::execute(ScriptExecutionContext &scriptExecCtx) {
Game &game = scriptExecCtx.getGame();
if (_waitForPrevious) {
if (game.getActiveSayTask()) {
return InProgress;
}
}
TaskPtr task(new SayTask(_lineToSay, game.getGameData()._color));
game.getTaskManager().startTask(task);
return Finished;
}
Common::String SayCommand::debugString() const {
return Common::String::format("SHOWMSG%s%s '%s' '%s'", _waitForPrevious ? "+WAIT" : "", _talkingAnimation ? "+TALKANIM" : "", _lineToSay.c_str(), _voiceFile.c_str());
}
}

View File

@@ -0,0 +1,55 @@
/* 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 MUTATIONOFJB_SAYCOMMAND_H
#define MUTATIONOFJB_SAYCOMMAND_H
#include "mutationofjb/commands/seqcommand.h"
#include "common/str.h"
namespace MutationOfJB {
class SayCommandParser : public SeqCommandParser {
public:
SayCommandParser() {}
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class SayCommand : public SeqCommand {
public:
SayCommand(const Common::String &lineToSay, const Common::String &voiceFile, bool waitForPrevious, bool talkingAnimation) :
_lineToSay(lineToSay),
_voiceFile(voiceFile),
_waitForPrevious(waitForPrevious),
_talkingAnimation(talkingAnimation) {}
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
Common::String _lineToSay;
Common::String _voiceFile;
bool _waitForPrevious;
bool _talkingAnimation;
};
}
#endif

View File

@@ -0,0 +1,45 @@
/* 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 "mutationofjb/commands/seqcommand.h"
#include "common/textconsole.h"
namespace MutationOfJB {
void SeqCommandParser::transition(ScriptParseContext &, Command *oldCommand, Command *newCommand, CommandParser *) {
if (!oldCommand || !newCommand) {
warning("Unexpected empty command in transition");
return;
}
static_cast<SeqCommand *>(oldCommand)->setNextCommand(newCommand);
}
void SeqCommand::setNextCommand(Command *nextCommand) {
_nextCommand = nextCommand;
}
Command *SeqCommand::next() const {
return _nextCommand;
}
}

View File

@@ -0,0 +1,50 @@
/* 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 MUTATIONOFJB_SEQCOMMAND_H
#define MUTATIONOFJB_SEQCOMMAND_H
#include "mutationofjb/commands/command.h"
#include "common/scummsys.h"
namespace MutationOfJB {
class SeqCommandParser : public CommandParser {
public:
void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser) override;
};
/**
* Base class for sequential commands.
*/
class SeqCommand : public Command {
public:
SeqCommand() : _nextCommand(nullptr) {}
void setNextCommand(Command *nextCommand);
Command *next() const override;
private:
Command *_nextCommand;
};
}
#endif

View File

@@ -0,0 +1,59 @@
/* 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 "mutationofjb/commands/setcolorcommand.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/script.h"
#include "common/str.h"
/** @file
* "SETCOL " <colorString>
*
* Sets subtitle color used by SayCommand.
*/
namespace MutationOfJB {
bool SetColorCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
if (line.size() < 8 || !line.hasPrefix("SETCOL")) {
return false;
}
const char *const colorStr = line.c_str() + 7;
const uint8 color = Game::colorFromString(colorStr);
command = new SetColorCommand(color);
return true;
}
Command::ExecuteResult SetColorCommand::execute(ScriptExecutionContext &scriptExecCtx) {
scriptExecCtx.getGameData()._color = _color;
return Finished;
}
Common::String SetColorCommand::debugString() const {
return Common::String::format("SETCOL %u", _color);
}
}

View File

@@ -0,0 +1,50 @@
/* 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 MUTATIONOFJB_SETCOLORCOMMAND_H
#define MUTATIONOFJB_SETCOLORCOMMAND_H
#include "mutationofjb/commands/seqcommand.h"
#include "common/scummsys.h"
namespace MutationOfJB {
class SetColorCommandParser : public SeqCommandParser {
public:
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class SetColorCommand : public SeqCommand {
public:
SetColorCommand(uint8 color) : _color(color) {}
Command::ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
uint8 _color;
};
}
#endif

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/>.
*
*/
#include "mutationofjb/commands/setobjectframecommand.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/room.h"
#include "mutationofjb/script.h"
/** @file
* "SETANIM " <objectId> " " <frame>
*
* Draws the frame for the specified object without changing the object's current frame.
* If the object is active, it is deactivated.
*/
namespace MutationOfJB {
bool SetObjectFrameCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
if (line.size() < 13 || !line.hasPrefix("SETANIM "))
return false;
const uint8 objectId = (uint8) atoi(line.c_str() + 8);
const unsigned int frame = atoi(line.c_str() + 11);
command = new SetObjectFrameCommand(objectId, frame);
return true;
}
Command::ExecuteResult SetObjectFrameCommand::execute(ScriptExecutionContext &scriptExecCtx) {
Object *const object = scriptExecCtx.getGameData().getCurrentScene()->getObject(_objectId);
object->_active = 0;
// The object's current frame is not changed, so use frame override instead.
scriptExecCtx.getGame().getRoom().drawObject(_objectId, _frame);
return Finished;
}
Common::String SetObjectFrameCommand::debugString() const {
return Common::String::format("SETOBJECTFRAME %u %u", (unsigned int) _objectId, (unsigned int) _frame);
}
}

View File

@@ -0,0 +1,50 @@
/* 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 MUTATIONOFJB_SETOBJECTFRAMECOMMAND_H
#define MUTATIONOFJB_SETOBJECTFRAMECOMMAND_H
#include "mutationofjb/commands/seqcommand.h"
#include "common/str.h"
namespace MutationOfJB {
class SetObjectFrameCommandParser : public SeqCommandParser {
public:
SetObjectFrameCommandParser() {}
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class SetObjectFrameCommand : public SeqCommand {
public:
SetObjectFrameCommand(uint8 objectId, uint8 frame) : _objectId(objectId), _frame(frame) {}
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
uint8 _objectId;
uint8 _frame;
};
}
#endif

View File

@@ -0,0 +1,77 @@
/* 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 "mutationofjb/commands/specialshowcommand.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/script.h"
#include "common/str.h"
/** @file
* "SPECIALSHOW " <mode>
*
* Shows special screen.
* The command supports multiple modes:
* 1 - show puzzle hint,
* 2 - show computer puzzle.
*/
namespace MutationOfJB {
bool SpecialShowCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
if (line.size() < 13 || !line.hasPrefix("SPECIALSHOW ")) {
return false;
}
const int modeInt = atoi(line.c_str() + 12);
SpecialShowCommand::Mode mode = SpecialShowCommand::PUZZLE_HINT;
if (modeInt == 1) {
mode = SpecialShowCommand::PUZZLE_HINT;
} else if (modeInt == 2) {
mode = SpecialShowCommand::COMPUTER_PUZZLE;
} else {
warning("Invalid special show mode %d", modeInt);
return false;
}
command = new SpecialShowCommand(mode);
return true;
}
Command::ExecuteResult SpecialShowCommand::execute(ScriptExecutionContext &scriptExeCtx) {
// TODO: Show UI.
if (_mode == COMPUTER_PUZZLE) {
scriptExeCtx.getGameData().getScene(32)->getObject(2, true)->_WX = 255;
scriptExeCtx.getGameData().getScene(32)->getObject(1, true)->_active = 0;
}
return Command::Finished;
}
Common::String SpecialShowCommand::debugString() const {
const char *modes[] = {"PUZZLE_HINT", "COMPUTER_PUZZLE"};
return Common::String::format("SPECIALSHOW %s", modes[static_cast<int>(_mode)]);
}
}

View File

@@ -0,0 +1,52 @@
/* 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 MUTATIONOFJB_SPECIALSHOWCOMMAND_H
#define MUTATIONOFJB_SPECIALSHOWCOMMAND_H
#include "mutationofjb/commands/seqcommand.h"
#include "common/scummsys.h"
namespace MutationOfJB {
class SpecialShowCommandParser : public SeqCommandParser {
public:
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class SpecialShowCommand : public SeqCommand {
public:
enum Mode {
PUZZLE_HINT,
COMPUTER_PUZZLE
};
SpecialShowCommand(Mode mode) : _mode(mode) {}
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
Mode _mode;
};
}
#endif

View File

@@ -0,0 +1,57 @@
/* 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 "mutationofjb/commands/switchpartcommand.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/script.h"
#include "common/str.h"
/** @file
* "SWITCHPART"
*
* Switches to the second part of the game (part B).
*/
namespace MutationOfJB {
bool SwitchPartCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
if (line != "SWITCHPART") {
return false;
}
command = new SwitchPartCommand();
return true;
}
Command::ExecuteResult SwitchPartCommand::execute(ScriptExecutionContext &scriptExeCtx) {
scriptExeCtx.getGame().switchToPartB();
return Command::Finished;
}
Common::String SwitchPartCommand::debugString() const {
return "SWITCHPART";
}
}

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 MUTATIONOFJB_SWITCHPARTCOMMAND_H
#define MUTATIONOFJB_SWITCHPARTCOMMAND_H
#include "mutationofjb/commands/seqcommand.h"
#include "common/scummsys.h"
namespace MutationOfJB {
class ConversationTask;
class SwitchPartCommandParser : public SeqCommandParser {
public:
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class SwitchPartCommand : public SeqCommand {
public:
SwitchPartCommand() {}
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
};
}
#endif

View File

@@ -0,0 +1,87 @@
/* 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 "mutationofjb/commands/talkcommand.h"
#include "mutationofjb/game.h"
#include "mutationofjb/script.h"
#include "mutationofjb/tasks/conversationtask.h"
#include "mutationofjb/tasks/taskmanager.h"
#include "common/str.h"
/** @file
* "TALK TO HIM" [ " " <mode> ]
*
* Begins interactive conversation defined by DefineStructCommand.
* The command supports multiple modes:
* 0 - normal mode,
* 1 - Ray and Buttleg mode (two responders),
* 2 - unknown (unused) mode,
* 3 - carnival ticket seller mode (special animation).
*/
namespace MutationOfJB {
bool TalkCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
if (line.size() < 11 || !line.hasPrefix("TALK TO HIM")) {
return false;
}
int modeInt = 0;
if (line.size() >= 13) {
modeInt = atoi(line.c_str() + 12);
}
TalkCommand::Mode mode = TalkCommand::NORMAL_MODE;
if (modeInt == 1) {
mode = TalkCommand::RAY_AND_BUTTLEG_MODE;
} else if (modeInt == 3) {
mode = TalkCommand::CARNIVAL_TICKET_SELLER_MODE;
}
command = new TalkCommand(mode);
return true;
}
Command::ExecuteResult TalkCommand::execute(ScriptExecutionContext &scriptExeCtx) {
if (!_task) {
_task = TaskPtr(new ConversationTask(scriptExeCtx.getGameData()._currentScene, scriptExeCtx.getGame().getGameData()._conversationInfo, _mode));
scriptExeCtx.getGame().getTaskManager().startTask(_task);
}
if (_task->getState() == Task::FINISHED) {
_task.reset();
return Command::Finished;
}
return Command::InProgress;
}
Common::String TalkCommand::debugString() const {
const char *modes[] = {"NORMAL", "RAY_AND_BUTTLEG", "CARNIVAL_TICKET_SELLER"};
return Common::String::format("TALK %s", modes[static_cast<int>(_mode)]);
}
}

View File

@@ -0,0 +1,57 @@
/* 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 MUTATIONOFJB_TALKCOMMAND_H
#define MUTATIONOFJB_TALKCOMMAND_H
#include "mutationofjb/commands/seqcommand.h"
#include "common/scummsys.h"
#include "mutationofjb/tasks/task.h"
namespace MutationOfJB {
class ConversationTask;
class TalkCommandParser : public SeqCommandParser {
public:
bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
};
class TalkCommand : public SeqCommand {
public:
enum Mode {
NORMAL_MODE,
RAY_AND_BUTTLEG_MODE,
CARNIVAL_TICKET_SELLER_MODE
};
TalkCommand(Mode mode) : _mode(mode) {}
ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
Common::String debugString() const override;
private:
Mode _mode;
TaskPtr _task;
};
}
#endif

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 mutationofjb "Mutation of JB" no

View File

@@ -0,0 +1,90 @@
/* 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 "mutationofjb/conversationlinelist.h"
#include "mutationofjb/encryptedfile.h"
#include "mutationofjb/util.h"
namespace MutationOfJB {
ConversationLineList::ConversationLineList(const Common::Path &fileName) {
parseFile(fileName);
}
const ConversationLineList::Line *ConversationLineList::getLine(uint index) const {
if (index > _lines.size()) {
return nullptr;
}
return &_lines[index - 1];
}
bool ConversationLineList::parseFile(const Common::Path &fileName) {
EncryptedFile file;
file.open(fileName);
if (!file.isOpen()) {
reportFileMissingError(fileName.toString(Common::Path::kNativeSeparator).c_str());
return false;
}
while (!file.eos()) {
Common::String lineStr = file.readLine();
if (lineStr.empty()) {
continue;
}
Line line;
Common::String::iterator endIt = Common::find(lineStr.begin(), lineStr.end(), '|');
if (endIt != lineStr.end()) {
endIt++;
if (endIt != lineStr.end() && *endIt == 'X') {
line._extra = Common::String(endIt + 1, lineStr.end()); // Skip 'X' char.
}
}
Common::String::iterator startSpeechIt = lineStr.begin();
Common::String::iterator endSpeechIt = startSpeechIt;
while (startSpeechIt < endIt) {
endSpeechIt = Common::find(startSpeechIt, endIt, '\\');
Common::String::iterator voiceFileIt = Common::find(startSpeechIt, endSpeechIt, '<');
Speech speech;
if (voiceFileIt != endSpeechIt) {
if (*voiceFileIt == 'S') {
speech._voiceFile = Common::String(voiceFileIt + 1, endSpeechIt);
}
}
speech._text = Common::String(startSpeechIt, voiceFileIt);
line._speeches.push_back(speech);
startSpeechIt = endSpeechIt + 1;
}
_lines.push_back(line);
}
return true;
}
}

View File

@@ -0,0 +1,65 @@
/* 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 MUTATIONOFJB_CONVERSATIONLINELIST_H
#define MUTATIONOFJB_CONVERSATIONLINELIST_H
#include "common/array.h"
#include "common/path.h"
#include "common/str.h"
namespace MutationOfJB {
class ConversationLineList {
public:
struct Speech {
Common::String _text;
Common::String _voiceFile;
bool isRepeating() const {
return _text.firstChar() == '*';
}
bool isFirstSpeaker() const {
return _text.firstChar() == '~';
}
bool isSecondSpeaker() const {
return _text.firstChar() == '`';
}
};
typedef Common::Array<Speech> Speeches;
struct Line {
Speeches _speeches;
Common::String _extra;
};
ConversationLineList(const Common::Path &fileName);
const Line *getLine(uint index) const;
private:
bool parseFile(const Common::Path &fileName);
Common::Array<Line> _lines;
};
}
#endif

View File

@@ -0,0 +1,4 @@
begin_section("MutationOfJB");
add_person("&Lcaron;ubom&iacute;r Rem&aacute;k", "LubomirR", "");
add_person("Miroslav Rem&aacute;k", "MiroslavR", "");
end_section();

View File

@@ -0,0 +1,491 @@
/* 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 "mutationofjb/debug.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/inventory.h"
#include "mutationofjb/mutationofjb.h"
#include "mutationofjb/script.h"
#include "mutationofjb/commands/command.h"
#include "mutationofjb/commands/seqcommand.h"
#include "mutationofjb/commands/conditionalcommand.h"
#include "mutationofjb/commands/callmacrocommand.h"
#include "mutationofjb/commands/randomcommand.h"
#include "common/debug-channels.h"
#include "common/scummsys.h"
namespace MutationOfJB {
/* Converts CP895 string to 7bit ASCII, so we can show it in the console. */
static Common::String convertToASCII(const Common::String &str) {
static const char conversionTable[] = {
'C', 'u', 'e', 'd', 'a', 'D', 'T', 'c', 'e', 'E', 'L', 'I', 'l', 'l', 'A', 'A', /* 0x80-0x8F */
'E', 'z', 'Z', 'o', 'o', 'O', 'u', 'U', 'y', 'O', 'U', 'S', 'L', 'Y', 'R', 't', /* 0x90-0x9F */
'a', 'i', 'o', 'u', 'n', 'N', 'U', 'O', 's', 'r', 'r', 'R' /* 0xA0-0xAB */
};
Common::String ret = str;
for (Common::String::iterator it = ret.begin(); it != ret.end(); ++it) {
const byte cp895Byte = reinterpret_cast<const byte &>(*it);
if (cp895Byte >= 0x80 && cp895Byte <= 0xAB) {
*it = conversionTable[cp895Byte - 0x80];
} else if (cp895Byte == 0xE1) { // ß
*it = 's';
}
}
return ret;
}
Console::Console(MutationOfJBEngine *vm) : _vm(vm) {
registerCmd("showallcommands", WRAP_METHOD(Console, cmd_showallcommands));
registerCmd("listsections", WRAP_METHOD(Console, cmd_listsections));
registerCmd("showsection", WRAP_METHOD(Console, cmd_showsection));
registerCmd("listmacros", WRAP_METHOD(Console, cmd_listmacros));
registerCmd("showmacro", WRAP_METHOD(Console, cmd_showmacro));
registerCmd("liststartups", WRAP_METHOD(Console, cmd_liststartups));
registerCmd("showstartup", WRAP_METHOD(Console, cmd_showstartup));
registerCmd("changescene", WRAP_METHOD(Console, cmd_changescene));
registerCmd("dumpsceneinfo", WRAP_METHOD(Console, cmd_dumpsceneinfo));
registerCmd("dumpdoorinfo", WRAP_METHOD(Console, cmd_dumpdoorinfo));
registerCmd("dumpobjectinfo", WRAP_METHOD(Console, cmd_dumpobjectinfo));
registerCmd("dumpstaticinfo", WRAP_METHOD(Console, cmd_dumpstaticinfo));
registerCmd("dumpbitmapinfo", WRAP_METHOD(Console, cmd_dumpbitmapinfo));
registerCmd("listinventory", WRAP_METHOD(Console, cmd_listinventory));
}
bool Console::cmd_showallcommands(int argc, const char **argv) {
if (argc == 2) {
Script *const script = getScriptFromArg(argv[1]);
if (script) {
const Commands &commands = script->getAllCommands();
for (Commands::const_iterator it = commands.begin(); it != commands.end(); ++it) {
debugPrintf("%s\n", convertToASCII((*it)->debugString()).c_str());
}
}
} else {
debugPrintf("showallcommands <G|L>\n");
}
return true;
}
bool Console::cmd_listsections(int argc, const char **argv) {
if (argc == 3) {
Script *const script = getScriptFromArg(argv[1]);
if (script) {
ActionInfo::Action action = ActionInfo::Look;
const char *word = nullptr;
if (strcmp(argv[2], "L") == 0) {
action = ActionInfo::Look;
word = "Look";
} else if (strcmp(argv[2], "W") == 0) {
action = ActionInfo::Walk;
word = "Walk";
} else if (strcmp(argv[2], "T") == 0) {
action = ActionInfo::Talk;
word = "Talk";
} else if (strcmp(argv[2], "U") == 0) {
action = ActionInfo::Use;
word = "Use";
} else if (strcmp(argv[2], "P") == 0) {
action = ActionInfo::PickUp;
word = "Pick up";
} else {
debugPrintf("Choose 'L' (look), 'W' (walk), 'T' (talk), 'U' (use) or 'P' (pick up).\n");
}
if (word) {
const ActionInfos &actionInfos = script->getActionInfos(action);
for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
const ActionInfo &actionInfo = *it;
if (action != ActionInfo::Use || actionInfo._entity2Name.empty()) {
debugPrintf("%s %s\n", word, convertToASCII(actionInfo._entity1Name).c_str());
} else {
debugPrintf("%s %s %s\n", word, convertToASCII(actionInfo._entity1Name).c_str(), convertToASCII(actionInfo._entity2Name).c_str());
}
}
}
}
} else {
debugPrintf("listsections <G|L> <L|W|T|U|P>\n");
}
return true;
}
void Console::showIndent(int indentLevel) {
for (int i = 0; i < indentLevel; ++i) {
debugPrintf(" ");
}
}
void Console::showCommands(Command *command, int indentLevel) {
while (command) {
showIndent(indentLevel);
debugPrintf("%s\n", convertToASCII(command->debugString()).c_str());
if (SeqCommand *const seqCmd = dynamic_cast<SeqCommand *>(command)) {
command = seqCmd->next();
} else if (ConditionalCommand *const condCmd = dynamic_cast<ConditionalCommand *>(command)) {
showCommands(condCmd->getTrueCommand(), indentLevel + 1);
showIndent(indentLevel);
debugPrintf("ELSE\n");
showCommands(condCmd->getFalseCommand(), indentLevel + 1);
command = nullptr;
} else if (CallMacroCommand *const callMacroCmd = dynamic_cast<CallMacroCommand *>(command)) {
command = callMacroCmd->getReturnCommand();
} else if (RandomCommand *const randomCmd = dynamic_cast<RandomCommand *>(command)) {
const RandomCommand::Choices &choices = randomCmd->getChoices();
for (RandomCommand::Choices::size_type i = 0; i < choices.size(); ++i) {
showIndent(indentLevel + 1);
debugPrintf("CASE %u\n", i);
showCommands(choices[i], indentLevel + 2);
}
command = nullptr;
} else {
command = nullptr;
}
}
}
bool Console::cmd_showsection(int argc, const char **argv) {
if (argc >= 4) {
Script *const script = getScriptFromArg(argv[1]);
if (script) {
Command *command = nullptr;
ActionInfo::Action action = ActionInfo::Look;
bool correctAction = true;
bool found = false;
if (strcmp(argv[2], "L") == 0) {
action = ActionInfo::Look;
} else if (strcmp(argv[2], "W") == 0) {
action = ActionInfo::Walk;
} else if (strcmp(argv[2], "T") == 0) {
action = ActionInfo::Talk;
} else if (strcmp(argv[2], "U") == 0) {
action = ActionInfo::Use;
} else if (strcmp(argv[2], "P") == 0) {
action = ActionInfo::PickUp;
} else {
debugPrintf("Choose 'L' (look), 'W' (walk), 'T' (talk), 'U' (use) or 'P' (pick up).\n");
correctAction = false;
}
if (correctAction) {
const ActionInfos &actionInfos = script->getActionInfos(action);
for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
const ActionInfo &actionInfo = *it;
if (convertToASCII(actionInfo._entity1Name) == argv[3] && (action != ActionInfo::Use || ((argc == 4 && actionInfo._entity2Name.empty()) || (argc > 4 && convertToASCII(actionInfo._entity2Name) == argv[4])))) {
found = true;
command = actionInfo._command;
break;
}
}
if (found) {
if (command) {
showCommands(command);
}
} else {
debugPrintf("Section not found.\n");
}
}
}
} else {
debugPrintf("showsection <G|L> <L|W|T|U|P> <sectionname>\n");
}
return true;
}
bool Console::cmd_listmacros(int argc, const char **argv) {
if (argc == 2) {
Script *const script = getScriptFromArg(argv[1]);
if (script) {
const Macros &macros = script->getMacros();
for (Macros::const_iterator it = macros.begin(); it != macros.end(); ++it) {
debugPrintf("%s\n", it->_key.c_str());
}
}
} else {
debugPrintf("listmacros <G|L>\n");
}
return true;
}
bool Console::cmd_showmacro(int argc, const char **argv) {
if (argc == 3) {
Script *script = nullptr;
if (strcmp(argv[1], "G") == 0) {
script = _vm->getGame().getGlobalScript();
} else if (strcmp(argv[1], "L") == 0) {
script = _vm->getGame().getLocalScript();
}
if (!script) {
debugPrintf("Choose 'G' (global) or 'L' (local) script.\n");
} else {
const Macros &macros = script->getMacros();
Macros::const_iterator itMacro = macros.find(argv[2]);
if (itMacro != macros.end()) {
if (itMacro->_value) {
showCommands(itMacro->_value);
}
} else {
debugPrintf("Macro not found.\n");
}
}
} else {
debugPrintf("showmacro <G|L> <macroname>\n");
}
return true;
}
bool Console::cmd_liststartups(int argc, const char **argv) {
if (argc == 2) {
Script *const script = getScriptFromArg(argv[1]);
if (script) {
const Startups &startups = script->getStartups();
for (Startups::const_iterator it = startups.begin(); it != startups.end(); ++it) {
debugPrintf("%u\n", (unsigned int) it->_key);
}
}
} else {
debugPrintf("liststartups <G|L>\n");
}
return true;
}
bool Console::cmd_showstartup(int argc, const char **argv) {
if (argc == 3) {
Script *const script = getScriptFromArg(argv[1]);
if (script) {
const Startups &startups = script->getStartups();
Startups::const_iterator itMacro = startups.find(static_cast<uint8>(atoi(argv[2])));
if (itMacro != startups.end()) {
if (itMacro->_value) {
showCommands(itMacro->_value);
}
} else {
debugPrintf("Startup not found.\n");
}
}
} else {
debugPrintf("showstartup <G|L> <startupid>\n");
}
return true;
}
bool Console::cmd_changescene(int argc, const char **argv) {
if (argc == 2) {
const uint8 sceneId = atoi(argv[1]);
const bool partB = argv[1][strlen(argv[1]) - 1] == 'B';
_vm->getGame().changeScene(sceneId, partB);
} else {
debugPrintf("changescene <scenename>\n");
}
return true;
}
bool Console::cmd_dumpsceneinfo(int argc, const char **argv) {
uint8 sceneId = _vm->getGame().getGameData()._currentScene;
if (argc == 2) {
sceneId = atoi(argv[1]);
} else if (argc != 1) {
debugPrintf("dumpsceneinfo [<sceneid>]\n");
}
if (Scene *const scene = _vm->getGame().getGameData().getScene(sceneId)) {
debugPrintf("Scene ID: %u\n", (unsigned int) sceneId);
debugPrintf("Startup: %u\n", (unsigned int) scene->_startup);
debugPrintf("Delay: %u\n", (unsigned int) scene->_delay);
debugPrintf("Doors: %u\n", (unsigned int) scene->_noDoors);
debugPrintf("Objects: %u\n", (unsigned int) scene->_noObjects);
debugPrintf("Statics: %u\n", (unsigned int) scene->_noStatics);
debugPrintf("ObstacleY1: %u\n", (unsigned int) scene->_obstacleY1);
debugPrintf("PalRotFirst: %u\n", (unsigned int) scene->_palRotFirst);
debugPrintf("PalRotLast: %u\n", (unsigned int) scene->_palRotLast);
debugPrintf("PalRotDelay: %u\n", (unsigned int) scene->_palRotDelay);
} else {
debugPrintf("Scene %u not found.\n", (unsigned int) sceneId);
}
return true;
}
bool Console::cmd_dumpdoorinfo(int argc, const char **argv) {
if (argc == 3) {
const uint8 sceneId = atoi(argv[1]);
const uint8 doorId = atoi(argv[2]);
Scene *const scene = _vm->getGame().getGameData().getScene(sceneId);
if (scene) {
Door *const door = scene->getDoor(doorId);
if (door) {
debugPrintf("Name: '%s'\n", convertToASCII(door->_name).c_str());
debugPrintf("DestSceneId: %u\n", (unsigned int) door->_destSceneId);
debugPrintf("DestX: %u\n", (unsigned int) door->_destX);
debugPrintf("DestY: %u\n", (unsigned int) door->_destY);
debugPrintf("X: %u\n", (unsigned int) door->_x);
debugPrintf("Y: %u\n", (unsigned int) door->_y);
debugPrintf("Width: %u\n", (unsigned int) door->_width);
debugPrintf("Height: %u\n", (unsigned int) door->_height);
debugPrintf("WalkToX: %u\n", (unsigned int) door->_walkToX);
debugPrintf("WalkToY: %u\n", (unsigned int) door->_walkToY);
debugPrintf("SP: %u\n", (unsigned int) door->_SP);
} else {
debugPrintf("Door %u not found.\n", (unsigned int) doorId);
}
} else {
debugPrintf("Scene %u not found.\n", (unsigned int) sceneId);
}
} else {
debugPrintf("dumpdoorinfo <sceneid> <doorid>\n");
}
return true;
}
bool Console::cmd_dumpobjectinfo(int argc, const char **argv) {
if (argc == 3) {
const uint8 sceneId = atoi(argv[1]);
const uint8 objectId = atoi(argv[2]);
Scene *const scene = _vm->getGame().getGameData().getScene(sceneId);
if (scene) {
Object *const object = scene->getObject(objectId);
if (object) {
debugPrintf("AC: %u\n", (unsigned int) object->_active);
debugPrintf("FA: %u\n", (unsigned int) object->_firstFrame);
debugPrintf("FR: %u\n", (unsigned int) object->_randomFrame);
debugPrintf("NA: %u\n", (unsigned int) object->_numFrames);
debugPrintf("FS: %u\n", (unsigned int) object->_roomFrameLSB);
debugPrintf("Jump chance: %u\n", (unsigned int) object->_jumpChance);
debugPrintf("CA: %u\n", (unsigned int) object->_currentFrame);
debugPrintf("X: %u\n", (unsigned int) object->_x);
debugPrintf("Y: %u\n", (unsigned int) object->_y);
debugPrintf("XL: %u\n", (unsigned int) object->_width);
debugPrintf("YL: %u\n", (unsigned int) object->_height);
debugPrintf("WX: %u\n", (unsigned int) object->_WX);
debugPrintf("WY: %u\n", (unsigned int) object->_roomFrameMSB);
debugPrintf("SP: %u\n", (unsigned int) object->_SP);
} else {
debugPrintf("Object %u not found.\n", (unsigned int) objectId);
}
} else {
debugPrintf("Scene %u not found.\n", (unsigned int) sceneId);
}
} else {
debugPrintf("dumpobjectinfo <sceneid> <objectid>\n");
}
return true;
}
bool Console::cmd_dumpstaticinfo(int argc, const char **argv) {
if (argc == 3) {
const uint8 sceneId = atoi(argv[1]);
const uint8 staticId = atoi(argv[2]);
Scene *const scene = _vm->getGame().getGameData().getScene(sceneId);
if (scene) {
Static *const stat = scene->getStatic(staticId, true);
if (stat) {
debugPrintf("Active: %u\n", (unsigned int) stat->_active);
debugPrintf("Name: '%s'\n", convertToASCII(stat->_name).c_str());
debugPrintf("X: %u\n", (unsigned int) stat->_x);
debugPrintf("Y: %u\n", (unsigned int) stat->_y);
debugPrintf("Width: %u\n", (unsigned int) stat->_width);
debugPrintf("Height: %u\n", (unsigned int) stat->_height);
debugPrintf("WalkToX: %u\n", (unsigned int) stat->_walkToY);
debugPrintf("WalkToY: %u\n", (unsigned int) stat->_walkToX);
debugPrintf("WalkToFrame: %u\n", (unsigned int) stat->_walkToFrame);
} else {
debugPrintf("Static %u not found.\n", (unsigned int) staticId);
}
} else {
debugPrintf("Scene %u not found.\n", (unsigned int) sceneId);
}
} else {
debugPrintf("dumpstaticinfo <sceneid> <staticid>\n");
}
return true;
}
bool Console::cmd_dumpbitmapinfo(int argc, const char **argv) {
if (argc == 3) {
const uint8 sceneId = atoi(argv[1]);
const uint8 bitmapId = atoi(argv[2]);
Scene *const scene = _vm->getGame().getGameData().getScene(sceneId);
if (scene) {
Bitmap *const bitmap = scene->getBitmap(bitmapId);
if (bitmap) {
debugPrintf("Room Frame: %u\n", (unsigned int) bitmap->_roomFrame);
debugPrintf("Visible: %u\n", (unsigned int) bitmap->_isVisible);
debugPrintf("X1: %u\n", (unsigned int) bitmap->_x1);
debugPrintf("Y1: %u\n", (unsigned int) bitmap->_y1);
debugPrintf("X2: %u\n", (unsigned int) bitmap->_x2);
debugPrintf("Y2: %u\n", (unsigned int) bitmap->_y2);
} else {
debugPrintf("Bitmap %u not found.\n", (unsigned int) bitmapId);
}
} else {
debugPrintf("Scene %u not found.\n", (unsigned int) sceneId);
}
} else {
debugPrintf("dumpbitmapinfo <sceneid> <bitmapid>\n");
}
return true;
}
Script *Console::getScriptFromArg(const char *arg) {
Script *script = nullptr;
if (strcmp(arg, "G") == 0) {
script = _vm->getGame().getGlobalScript();
} else if (strcmp(arg, "L") == 0) {
script = _vm->getGame().getLocalScript();
}
if (!script) {
debugPrintf("Choose 'G' (global) or 'L' (local) script.\n");
}
return script;
}
bool Console::cmd_listinventory(int, const char **) {
Inventory &inventory = _vm->getGame().getGameData().getInventory();
const Inventory::Items &items = inventory.getItems();
for (Inventory::Items::const_iterator it = items.begin(); it != items.end(); ++it) {
debugPrintf("%s\n", convertToASCII(*it).c_str());
}
return true;
}
}

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 MUTATIONOFJB_DEBUG_H
#define MUTATIONOFJB_DEBUG_H
#include "gui/debugger.h"
namespace MutationOfJB {
class MutationOfJBEngine;
class Command;
class Script;
class Console : public GUI::Debugger {
public:
Console(MutationOfJBEngine *vm);
~Console(void) override {}
private:
bool cmd_showallcommands(int argc, const char **argv);
bool cmd_listsections(int argc, const char **argv);
bool cmd_showsection(int argc, const char **argv);
bool cmd_listmacros(int argc, const char **argv);
bool cmd_showmacro(int argc, const char **argv);
bool cmd_liststartups(int argc, const char **argv);
bool cmd_showstartup(int argc, const char **argv);
bool cmd_changescene(int argc, const char **argv);
bool cmd_dumpsceneinfo(int argc, const char **argv);
bool cmd_dumpdoorinfo(int argc, const char **argv);
bool cmd_dumpobjectinfo(int argc, const char **argv);
bool cmd_dumpstaticinfo(int argc, const char **argv);
bool cmd_dumpbitmapinfo(int argc, const char **argv);
bool cmd_listinventory(int argc, const char **argv);
void showIndent(int indentLevel);
void showCommands(Command *command, int indentLevel = 0);
Script *getScriptFromArg(const char *arg);
MutationOfJBEngine *_vm;
};
}
#endif

View File

@@ -0,0 +1,119 @@
/* 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"
static const PlainGameDescriptor mutationofjbGames[] = {
{"mutationofjb", "Mutation of J.B."},
{nullptr, nullptr}
};
static const ADGameDescription mutationofjbDescriptions[] = {
{
"mutationofjb",
"",
{
{"jb.ex_", 0, "934164b09c72fa7167811f448ee0a426", 150048},
{"startup.dat", 0, nullptr, AD_NO_SIZE},
{"startupb.dat", 0, nullptr, AD_NO_SIZE},
{"global.atn", 0, nullptr, AD_NO_SIZE},
{"piggy.apk", 0, nullptr, AD_NO_SIZE},
{"foogl.apk", 0, nullptr, AD_NO_SIZE},
{"tosay.ger", 0, nullptr, AD_NO_SIZE},
{"response.ger", 0, nullptr, AD_NO_SIZE},
{"font1.aft", 0, nullptr, AD_NO_SIZE},
{"sysfnt.aft", 0, nullptr, AD_NO_SIZE},
{nullptr, 0, nullptr, 0}
},
Common::SK_SVK,
Common::kPlatformDOS,
ADGF_CD,
GUIO1(GUIO_NOMIDI)
},
{
"mutationofjb",
"",
{
{"jb.ex_", 0, "8833f22f1763d05eeb909e8626cdec7b", 150800},
{"startup.dat", 0, nullptr, AD_NO_SIZE},
{"startupb.dat", 0, nullptr, AD_NO_SIZE},
{"global.atn", 0, nullptr, AD_NO_SIZE},
{"piggy.apk", 0, nullptr, AD_NO_SIZE},
{"foogl.apk", 0, nullptr, AD_NO_SIZE},
{"tosay.ger", 0, nullptr, AD_NO_SIZE},
{"response.ger", 0, nullptr, AD_NO_SIZE},
{"font1.aft", 0, nullptr, AD_NO_SIZE},
{"sysfnt.aft", 0, nullptr, AD_NO_SIZE},
{nullptr, 0, nullptr, 0}
},
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_CD,
GUIO1(GUIO_NOMIDI)
},
{ // Demo from Riki Multimedia Magazine (Slovakia) #23 - Nov 1996
"mutationofjb",
"Demo",
{
{"jbdemo.exe", 0, "97943a569bacc4131447577436389276", 121696},
{"strt.dat", 0, nullptr, AD_NO_SIZE},
{"startupb.dat", 0, nullptr, AD_NO_SIZE},
{"global.atn", 0, nullptr, AD_NO_SIZE},
{"piggy.apk", 0, nullptr, AD_NO_SIZE},
{"font1.aft", 0, nullptr, AD_NO_SIZE},
{"sysfnt.aft", 0, nullptr, AD_NO_SIZE},
{nullptr, 0, nullptr, 0}
},
Common::SK_SVK,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GUIO_NOMIDI)
},
AD_TABLE_END_MARKER
};
static const char *const mutationofjbDirectoryGlobs[] = {
"data",
nullptr
};
class MutationOfJBMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
public:
MutationOfJBMetaEngineDetection() : AdvancedMetaEngineDetection(mutationofjbDescriptions, mutationofjbGames) {
_maxScanDepth = 2;
_directoryGlobs = mutationofjbDirectoryGlobs;
}
const char *getName() const override {
return "mutationofjb";
}
const char *getEngineName() const override {
return "Mutation of J.B.";
}
const char *getOriginalCopyright() const override {
return "Mutation of J.B. (C) 1996 RIKI Computer Games";
}
};
REGISTER_PLUGIN_STATIC(MUTATIONOFJB_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, MutationOfJBMetaEngineDetection);

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/>.
*
*/
#include "encryptedfile.h"
static const uint8 XOR_TABLE[] = {
0x41, 0x2b, 0x7a, 0x0c, 0xc8, 0xe5, 0x0c, 0xde, 0x45, 0xa8, 0x00, 0xad,
0x70, 0xac, 0x23, 0xe0, 0x0c, 0xde, 0xac, 0x16, 0xa1, 0x1a, 0x70, 0x17,
0x1b, 0x90, 0x34, 0x6e, 0x53, 0xfe, 0xdd, 0x3c, 0xef, 0x74, 0x75, 0x3e,
0x2e, 0xb0, 0x3d, 0x2b, 0x74, 0x6d, 0x59, 0xde, 0xc6, 0x20, 0xb8, 0xf3,
0x7d, 0xfa, 0x4d, 0xbd, 0xdb, 0x3c, 0xc5, 0xcb, 0x57, 0x13, 0x40, 0x6b,
0xb8, 0xad, 0xb9, 0xc1, 0x6a, 0x37, 0x39, 0x80, 0x94, 0xd3, 0xdf, 0x4b,
0xe4, 0xe4, 0x7a, 0x4c, 0x0f, 0x21, 0x27, 0x9a, 0x7e, 0x52, 0x35, 0x58,
0xb4, 0xbc, 0x5a, 0xc9, 0x48, 0x7f, 0xcc, 0xb6, 0x97, 0x7b, 0xf1, 0xd5,
0x88, 0x8c, 0xa9, 0x27, 0xf7, 0x20, 0x68, 0x65, 0xad, 0x6f, 0x9e, 0x07,
0xf8, 0xf6, 0x2c, 0x65, 0x4a, 0xe9, 0xd8, 0x13, 0x6a, 0xb5, 0x14, 0x6f,
0x29, 0xdc, 0x68, 0xf8, 0xa0, 0xb6, 0x73, 0x03, 0x69, 0xe3, 0x4f, 0xb6,
0x59, 0x79, 0xae, 0x93, 0xad, 0x40, 0x27, 0xd0, 0xb5, 0x7a, 0x68, 0x5d,
0x5e, 0x19, 0x53, 0x4f, 0x40, 0x56, 0x3d, 0x10, 0xf8, 0x0a, 0xc6, 0x90,
0x06, 0x45, 0x13, 0x4a, 0x6a, 0xfe, 0x56, 0xeb, 0xbc, 0xd9, 0xee, 0xe0,
0x85, 0x5e, 0x98, 0x23, 0xf9, 0x19, 0x60, 0xf9, 0x7e, 0x8d, 0x61, 0xa0,
0x7c, 0xe1, 0x84, 0xf2, 0x7a, 0xb8, 0xbe, 0x8e, 0x81, 0x9e, 0x87, 0x20,
0x32, 0xf3, 0x8c, 0xb4, 0x2c, 0x4d, 0xc8, 0x50, 0x9b, 0xa5, 0x9c, 0x27,
0x02, 0xd6, 0x7f, 0x2a, 0xaf, 0x46, 0x65, 0xd0, 0x6a, 0xae, 0xfa, 0x53,
0x37, 0x6c, 0x49, 0xb9, 0x4d, 0xcd, 0x6c, 0x6b, 0xa7, 0x2d, 0x66, 0x32,
0xb4, 0xf5, 0x41, 0xd5, 0x18, 0xc4, 0xfd, 0xbe, 0x8a, 0x47, 0x11, 0x50,
0x3d, 0x97, 0x64, 0xda, 0x5a, 0x27, 0x18, 0x60, 0x78, 0x80, 0x86, 0x8a,
0x2a, 0x72, 0x40, 0x89
};
namespace MutationOfJB {
uint32 EncryptedFile::read(void *dataPtr, uint32 dataSize) {
uint8 xorPos = static_cast<uint8>(pos() % ARRAYSIZE(XOR_TABLE));
const uint32 readBytes = Common::File::read(dataPtr, dataSize);
for (uint32 i = 0; i < readBytes; ++i) {
static_cast<uint8 *>(dataPtr)[i] ^= XOR_TABLE[xorPos];
xorPos++;
}
return readBytes;
}
}

View File

@@ -0,0 +1,36 @@
/* 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 MUTATIONOFJB_ENCRYPTEDFILE_H
#define MUTATIONOFJB_ENCRYPTEDFILE_H
#include "common/file.h"
namespace MutationOfJB {
class EncryptedFile : public Common::File {
public:
uint32 read(void *dataPtr, uint32 dataSize) override;
};
}
#endif

View File

@@ -0,0 +1,163 @@
/* 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 "mutationofjb/font.h"
#include "mutationofjb/encryptedfile.h"
#include "mutationofjb/util.h"
#include "common/debug.h"
namespace MutationOfJB {
Font::Font(const Common::Path &fileName, int horizSpacing, int lineHeight) :
_horizSpacing(horizSpacing),
_lineHeight(lineHeight),
_maxCharWidth(0) {
load(fileName);
}
bool Font::load(const Common::Path &fileName) {
EncryptedFile file;
file.open(fileName);
if (!file.isOpen()) {
reportFileMissingError(fileName.toString(Common::Path::kNativeSeparator).c_str());
return false;
}
file.seek(0x02D6, SEEK_SET); // Skip header + unknown data (unused palette?).
uint16 noGlyphs = 0;
noGlyphs = file.readUint16LE();
file.seek(7, SEEK_CUR); // Skip unknown data (0s).
uint8 maxHeight = 0;
while (noGlyphs--) {
const uint8 character = file.readByte();
const uint8 width = file.readByte();
const uint8 height = file.readByte();
Graphics::ManagedSurface &surf = _glyphs[character];
surf.create(width, height);
for (int h = 0; h < height; ++h) {
file.read(surf.getBasePtr(0, h), width);
}
if (width > _maxCharWidth) {
_maxCharWidth = width;
}
if (height > maxHeight) {
maxHeight = height;
}
}
if (_lineHeight == -1) {
_lineHeight = maxHeight;
}
return true;
}
int Font::getFontHeight() const {
return _lineHeight;
}
int Font::getMaxCharWidth() const {
return _maxCharWidth;
}
int Font::getCharWidth(uint32 chr) const {
GlyphMap::iterator it = _glyphs.find(chr);
if (it == _glyphs.end()) {
return 0;
}
return it->_value.w;
}
int Font::getKerningOffset(uint32 left, uint32 right) const {
if (left == 0) {
// Do not displace the first character.
return 0;
}
if (_glyphs.find(left) == _glyphs.end()) {
// Missing glyphs must not create extra displacement.
// FIXME: This way is not completely correct, as if the last character is
// missing a glyph, it will still create extra displacement. This should
// not affect the visuals but it might affect getStringWidth() / getBoundingBox().
return 0;
}
return _horizSpacing;
}
class FontBlitOperation {
public:
FontBlitOperation(const Font &font, const byte baseColor)
: _font(font),
_baseColor(baseColor) {}
byte operator()(const byte srcColor, const byte destColor) {
if (srcColor == 0) {
// Transparency - keep destination color.
return destColor;
}
// Replace destination with transformed source color.
return _font.transformColor(_baseColor, srcColor);
}
private:
const Font &_font;
const byte _baseColor;
};
void Font::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
GlyphMap::iterator it = _glyphs.find(chr);
if (it == _glyphs.end()) {
// Missing glyph is a common situation in the game and it's okay to ignore it.
return;
}
Graphics::ManagedSurface &glyphSurface = it->_value;
blit_if(glyphSurface, *dst, Common::Point(x, y), FontBlitOperation(*this, color));
}
uint8 Font::transformColor(uint8 baseColor, uint8 glyphColor) const {
return baseColor + glyphColor - 0x10;
}
SystemFont::SystemFont() : Font("sysfnt.aft", 1, 7) {}
SpeechFont::SpeechFont() : Font("font1.aft", -1, -1) {}
uint8 SpeechFont::transformColor(uint8 baseColor, uint8 glyphColor) const {
// Hack in original game.
if (glyphColor == 0x11) {
return 0xC0;
}
return Font::transformColor(baseColor, glyphColor);
}
}

View File

@@ -0,0 +1,77 @@
/* 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 MUTATIONOFJB_FONT_H
#define MUTATIONOFJB_FONT_H
#include "common/scummsys.h"
#include "common/hashmap.h"
#include "common/path.h"
#include "graphics/font.h"
#include "graphics/managed_surface.h"
#include "graphics/surface.h"
namespace Common {
class String;
}
namespace MutationOfJB {
class Font : public Graphics::Font {
friend class FontBlitOperation;
public:
Font(const Common::Path &fileName, int horizSpacing, int lineHeight);
int getFontHeight() const override;
int getMaxCharWidth() const override;
int getCharWidth(uint32 chr) const override;
int getKerningOffset(uint32 left, uint32 right) const override;
void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
protected:
virtual uint8 transformColor(uint8 baseColor, uint8 glyphColor) const;
private:
bool load(const Common::Path &fileName);
int _horizSpacing;
int _lineHeight;
int _maxCharWidth;
typedef Common::HashMap<uint8, Graphics::ManagedSurface> GlyphMap;
GlyphMap _glyphs;
};
class SystemFont : public Font {
public:
SystemFont();
};
class SpeechFont : public Font {
public:
SpeechFont();
protected:
uint8 transformColor(uint8 baseColor, uint8 glyphColor) const override;
};
}
#endif

View File

@@ -0,0 +1,278 @@
/* 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 "mutationofjb/game.h"
#include "mutationofjb/commands/command.h"
#include "mutationofjb/encryptedfile.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/mutationofjb.h"
#include "mutationofjb/room.h"
#include "mutationofjb/script.h"
#include "mutationofjb/tasks/objectanimationtask.h"
#include "mutationofjb/util.h"
#include "common/str.h"
#include "common/util.h"
#include "engines/advancedDetector.h"
namespace MutationOfJB {
Game::Game(MutationOfJBEngine *vm)
: _vm(vm),
_randomSource("mutationofjb"),
_delayedLocalScript(nullptr),
_runDelayedScriptStartup(false),
_gui(*this, _vm->getScreen()),
_scriptExecCtx(*this),
_taskManager(*this),
_assets(*this) {
_gameData = new GameData;
loadGameData(false);
EncryptedFile globalScriptFile;
globalScriptFile.open("global.atn");
_globalScript = new Script;
_globalScript->loadFromStream(globalScriptFile);
globalScriptFile.close();
_localScript = nullptr;
_room = new Room(this, _vm->getScreen());
_gui.init();
_taskManager.startTask(TaskPtr(new ObjectAnimationTask));
}
MutationOfJBEngine &Game::getEngine() {
return *_vm;
}
Common::RandomSource &Game::getRandomSource() {
return _randomSource;
}
GameData &Game::getGameData() {
return *_gameData;
}
Room &Game::getRoom() {
return *_room;
}
Script *Game::getGlobalScript() const {
return _globalScript;
}
Script *Game::getLocalScript() const {
return _localScript;
}
bool Game::loadGameData(bool partB) {
EncryptedFile file;
const char *fileName = !partB ? "startup.dat" : "startupb.dat";
file.open(fileName);
if (!file.isOpen()) {
reportFileMissingError(fileName);
return false;
}
_gameData->loadInitialState(file);
file.close();
return true;
}
Script *Game::changeSceneLoadScript(uint8 sceneId, bool partB) {
if (isCurrentSceneMap()) {
_gui.markDirty();
}
_gameData->_lastScene = _gameData->_currentScene;
_gameData->_currentScene = sceneId;
_gameData->_partB = partB;
_room->load(_gameData->_currentScene, partB);
_gui.refreshAfterSceneChanged();
EncryptedFile scriptFile;
Common::Path fileName(Common::String::format("scrn%d%s.atn", sceneId, partB ? "b" : ""));
scriptFile.open(fileName);
if (!scriptFile.isOpen()) {
reportFileMissingError(fileName.toString(Common::Path::kNativeSeparator).c_str());
return nullptr;
}
// TODO Actually parse this.
Common::String dummy;
dummy = scriptFile.readLine(); // Skip first line.
scriptFile.seek(126, SEEK_CUR); // Skip 126 bytes.
Script *localScript = new Script;
localScript->loadFromStream(scriptFile);
scriptFile.close();
return localScript;
}
void Game::changeScene(uint8 sceneId, bool partB) {
if (_localScript) {
delete _localScript;
_localScript = nullptr;
}
_localScript = changeSceneLoadScript(sceneId, partB);
if (_localScript) {
_scriptExecCtx.startStartupSection();
}
}
Script *Game::changeSceneDelayScript(uint8 sceneId, bool partB, bool runDelayedScriptStartup) {
_delayedLocalScript = changeSceneLoadScript(sceneId, partB);
_runDelayedScriptStartup = runDelayedScriptStartup;
return _delayedLocalScript;
}
static Command *findActionInfoCommand(const ActionInfos &infos, const Common::String &entity1Name, const Common::String &entity2Name = Common::String()) {
for (ActionInfos::const_iterator it = infos.begin(); it != infos.end(); ++it) {
if (it->_entity1Name == entity1Name && it->_entity2Name == entity2Name) {
return it->_command;
}
}
return nullptr;
}
bool Game::startActionSection(ActionInfo::Action action, const Common::String &entity1Name, const Common::String &entity2Name) {
Script *const localScript = getLocalScript();
Script *const globalScript = getGlobalScript();
Command *command = nullptr;
if (localScript) {
command = findActionInfoCommand(localScript->getActionInfos(action), entity1Name, entity2Name);
}
if (!command && globalScript) {
command = findActionInfoCommand(globalScript->getActionInfos(action), entity1Name, entity2Name);
}
if (command) {
_scriptExecCtx.startCommand(command);
return true;
}
return false;
}
bool Game::isCurrentSceneMap() const {
return _gameData->_currentScene == 12;
}
void Game::update() {
Command::ExecuteResult res = _scriptExecCtx.runActiveCommand();
if (res == Command::Finished && _delayedLocalScript) {
delete _localScript;
_localScript = _delayedLocalScript;
if (_localScript && _runDelayedScriptStartup)
_scriptExecCtx.startStartupSection();
_delayedLocalScript = nullptr;
_runDelayedScriptStartup = false;
}
_taskManager.update();
}
GameScreen &Game::getGameScreen() {
return _gui;
}
uint8 Game::colorFromString(const char *colorStr) {
struct {
const char *str;
uint8 color;
} colors[] = {
{"white", WHITE},
{"darkgray", DARKGRAY},
{"lightgray", LIGHTGRAY},
{"green", GREEN},
{"orange", ORANGE},
{"darkblue", DARKBLUE},
{"lightblue", LIGHTBLUE},
{"brown", BROWN}
};
for (int i = 0; i < ARRAYSIZE(colors); ++i) {
if (strcmp(colors[i].str, colorStr) == 0) {
return colors[i].color;
}
}
if (*colorStr == 'n') {
return static_cast<uint8>(atoi(colorStr + 1));
}
warning("Color not found");
return 0x00;
}
TaskManager &Game::getTaskManager() {
return _taskManager;
}
Assets &Game::getAssets() {
return _assets;
}
Graphics::Screen &Game::getScreen() {
return *_vm->getScreen();
}
TaskPtr Game::getActiveSayTask() const {
return _activeSayTask;
}
void Game::setActiveSayTask(const TaskPtr &sayTask) {
_activeSayTask = sayTask;
}
bool Game::loadSaveAllowed() const {
if (_scriptExecCtx.isCommandRunning())
return false;
if (isCurrentSceneMap())
return false;
return true;
}
Common::Language Game::getLanguage() const {
return _vm->getGameDescription()->language;
}
void Game::switchToPartB() {
getGameData().getInventory().removeAllItems();
loadGameData(true);
changeSceneDelayScript(3, true, true);
}
}

114
engines/mutationofjb/game.h Normal file
View File

@@ -0,0 +1,114 @@
/* 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 MUTATIONOFJB_GAME_H
#define MUTATIONOFJB_GAME_H
#include "mutationofjb/assets.h"
#include "mutationofjb/gamescreen.h"
#include "mutationofjb/tasks/taskmanager.h"
#include "common/language.h"
#include "common/ptr.h"
#include "common/random.h"
#include "common/scummsys.h"
namespace Common {
class String;
}
namespace MutationOfJB {
class Command;
class MutationOfJBEngine;
class Script;
class Room;
class SayTask;
struct GameData;
struct Door;
struct Static;
struct Bitmap;
class Game {
public:
Game(MutationOfJBEngine *vm);
MutationOfJBEngine &getEngine();
Common::RandomSource &getRandomSource();
GameData &getGameData();
Room &getRoom();
Script *getGlobalScript() const;
Script *getLocalScript() const;
void changeScene(uint8 sceneId, bool partB);
Script *changeSceneDelayScript(uint8 sceneId, bool partB, bool runDelayedScriptStartup = false);
bool startActionSection(ActionInfo::Action action, const Common::String &entity1Name, const Common::String &entity2Name = Common::String());
bool isCurrentSceneMap() const;
void update();
GameScreen &getGameScreen();
static uint8 colorFromString(const char *colorStr);
TaskManager &getTaskManager();
Assets &getAssets();
Graphics::Screen &getScreen();
TaskPtr getActiveSayTask() const;
void setActiveSayTask(const TaskPtr &sayTask);
bool loadSaveAllowed() const;
Common::Language getLanguage() const;
void switchToPartB();
private:
bool loadGameData(bool partB);
void runActiveCommand();
void startCommand(Command *cmd);
Script *changeSceneLoadScript(uint8 sceneId, bool partB);
MutationOfJBEngine *_vm;
Common::RandomSource _randomSource;
GameData *_gameData;
Script *_globalScript;
Script *_localScript;
Script *_delayedLocalScript;
bool _runDelayedScriptStartup;
Room *_room;
GameScreen _gui;
ScriptExecutionContext _scriptExecCtx;
TaskManager _taskManager;
Assets _assets;
TaskPtr _activeSayTask;
};
}
#endif

View File

@@ -0,0 +1,428 @@
/* 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 "mutationofjb/gamedata.h"
#include "common/serializer.h"
#include "common/stream.h"
#include "common/util.h"
namespace MutationOfJB {
static bool readEntityNameString(Common::ReadStream &stream, char *str) {
char buf[MAX_ENTITY_NAME_LENGTH];
memset(str, 0, MAX_ENTITY_NAME_LENGTH + 1);
uint8 len = stream.readByte();
stream.read(buf, MAX_ENTITY_NAME_LENGTH);
len = MIN(len, static_cast<uint8>(MAX_ENTITY_NAME_LENGTH));
memcpy(str, buf, len);
return true;
}
static void syncEntityNameString(char *cstr, Common::Serializer &sz) {
if (sz.isLoading()) {
Common::String str;
sz.syncString(str);
strncpy(cstr, str.c_str(), MAX_ENTITY_NAME_LENGTH);
cstr[MAX_ENTITY_NAME_LENGTH] = 0;
} else {
Common::String str(cstr);
sz.syncString(str);
}
}
bool Door::isActive() {
return *_name != '\0';
}
bool Door::loadInitialState(Common::ReadStream &stream) {
readEntityNameString(stream, _name);
_destSceneId = stream.readByte();
_destX = stream.readUint16LE();
_destY = stream.readUint16LE();
_x = stream.readUint16LE();
_y = stream.readByte();
_width = stream.readUint16LE();
_height = stream.readByte();
_walkToX = stream.readUint16LE();
_walkToY = stream.readByte();
_SP = stream.readByte();
return true;
}
void Door::saveLoadWithSerializer(Common::Serializer &sz) {
syncEntityNameString(_name, sz);
sz.syncAsByte(_destSceneId);
sz.syncAsUint16LE(_destX);
sz.syncAsUint16LE(_destY);
sz.syncAsUint16LE(_x);
sz.syncAsByte(_y);
sz.syncAsUint16LE(_width);
sz.syncAsByte(_height);
sz.syncAsUint16LE(_walkToX);
sz.syncAsByte(_walkToY);
sz.syncAsByte(_SP);
}
bool Door::allowsImplicitSceneChange() const {
const size_t length = strlen(_name);
if (length == 0)
return false;
return _name[length - 1] != '+';
}
bool Object::loadInitialState(Common::ReadStream &stream) {
_active = stream.readByte();
_firstFrame = stream.readByte();
_randomFrame = stream.readByte();
_numFrames = stream.readByte();
_roomFrameLSB = stream.readByte();
_jumpChance = stream.readByte();
_currentFrame = stream.readByte();
_x = stream.readUint16LE();
_y = stream.readByte();
_width = stream.readUint16LE();
_height = stream.readByte();
_WX = stream.readUint16LE();
_roomFrameMSB = stream.readByte();
_SP = stream.readByte();
return true;
}
void Object::saveLoadWithSerializer(Common::Serializer &sz) {
sz.syncAsByte(_active);
sz.syncAsByte(_firstFrame);
sz.syncAsByte(_randomFrame);
sz.syncAsByte(_numFrames);
sz.syncAsByte(_roomFrameLSB);
sz.syncAsByte(_jumpChance);
sz.syncAsByte(_currentFrame);
sz.syncAsUint16LE(_x);
sz.syncAsByte(_y);
sz.syncAsUint16LE(_width);
sz.syncAsByte(_height);
sz.syncAsUint16LE(_WX);
sz.syncAsByte(_roomFrameMSB);
sz.syncAsByte(_SP);
}
bool Static::loadInitialState(Common::ReadStream &stream) {
_active = stream.readByte();
readEntityNameString(stream, _name);
_x = stream.readUint16LE();
_y = stream.readByte();
_width = stream.readUint16LE();
_height = stream.readByte();
_walkToX = stream.readUint16LE();
_walkToY = stream.readByte();
_walkToFrame = stream.readByte();
return true;
}
void Static::saveLoadWithSerializer(Common::Serializer &sz) {
sz.syncAsByte(_active);
syncEntityNameString(_name, sz);
sz.syncAsUint16LE(_x);
sz.syncAsByte(_y);
sz.syncAsUint16LE(_width);
sz.syncAsByte(_height);
sz.syncAsUint16LE(_walkToX);
sz.syncAsByte(_walkToY);
sz.syncAsByte(_walkToFrame);
}
bool Static::isCombinable() const {
const size_t length = strlen(_name);
if (length == 0)
return false;
return _name[length - 1] == '[';
}
bool Static::allowsImplicitPickup() const {
return _name[0] == '~';
}
bool Bitmap::loadInitialState(Common::ReadStream &stream) {
_roomFrame = stream.readByte();
_isVisible = stream.readByte();
_x1 = stream.readUint16LE();
_y1 = stream.readByte();
_x2 = stream.readUint16LE();
_y2 = stream.readByte();
return true;
}
void Bitmap::saveLoadWithSerializer(Common::Serializer &sz) {
sz.syncAsByte(_roomFrame);
sz.syncAsByte(_isVisible);
sz.syncAsUint16LE(_x1);
sz.syncAsByte(_y1);
sz.syncAsUint16LE(_x2);
sz.syncAsByte(_y2);
}
bool Scene::loadInitialState(Common::ReadStream &stream) {
int i;
_startup = stream.readByte();
_unknown001 = stream.readByte();
_unknown002 = stream.readByte();
_unknown003 = stream.readByte();
_delay = stream.readByte();
_noDoors = stream.readByte();
_noDoors = MIN(_noDoors, static_cast<uint8>(ARRAYSIZE(_doors)));
for (i = 0; i < ARRAYSIZE(_doors); ++i) {
_doors[i].loadInitialState(stream);
}
_noObjects = stream.readByte();
_noObjects = MIN(_noObjects, static_cast<uint8>(ARRAYSIZE(_objects)));
for (i = 0; i < ARRAYSIZE(_objects); ++i) {
_objects[i].loadInitialState(stream);
}
_noStatics = stream.readByte();
_noStatics = MIN(_noStatics, static_cast<uint8>(ARRAYSIZE(_statics)));
for (i = 0; i < ARRAYSIZE(_statics); ++i) {
_statics[i].loadInitialState(stream);
}
for (i = 0; i < ARRAYSIZE(_bitmaps); ++i) {
_bitmaps[i].loadInitialState(stream);
}
_obstacleY1 = stream.readUint16LE();
_palRotFirst = stream.readByte();
_palRotLast = stream.readByte();
_palRotDelay = stream.readByte();
_exhaustedConvItemNext = stream.readByte();
for (i = 0; i < ARRAYSIZE(_exhaustedConvItems); ++i) {
_exhaustedConvItems[i]._encodedData = stream.readByte();
}
return true;
}
void Scene::saveLoadWithSerializer(Common::Serializer &sz) {
sz.syncAsByte(_startup);
sz.syncAsByte(_unknown001);
sz.syncAsByte(_unknown002);
sz.syncAsByte(_unknown003);
sz.syncAsByte(_delay);
sz.syncAsByte(_noDoors);
for (int i = 0; i < ARRAYSIZE(_doors); ++i) {
_doors[i].saveLoadWithSerializer(sz);
}
sz.syncAsByte(_noObjects);
for (int i = 0; i < ARRAYSIZE(_objects); ++i) {
_objects[i].saveLoadWithSerializer(sz);
}
sz.syncAsByte(_noStatics);
for (int i = 0; i < ARRAYSIZE(_statics); ++i) {
_statics[i].saveLoadWithSerializer(sz);
}
for (int i = 0; i < ARRAYSIZE(_bitmaps); ++i) {
_bitmaps[i].saveLoadWithSerializer(sz);
}
sz.syncAsUint16LE(_obstacleY1);
sz.syncAsByte(_palRotFirst);
sz.syncAsByte(_palRotLast);
sz.syncAsByte(_palRotDelay);
sz.syncAsByte(_exhaustedConvItemNext);
for (int i = 0; i < ARRAYSIZE(_exhaustedConvItems); ++i) {
sz.syncAsByte(_exhaustedConvItems[i]._encodedData);
}
}
Door *Scene::getDoor(uint8 doorId) {
if (doorId == 0 || doorId > _noDoors) {
warning("Door %d does not exist", doorId);
return nullptr;
}
return &_doors[doorId - 1];
}
Object *Scene::getObject(uint8 objectId, bool ignoreNo) {
if (objectId == 0 || objectId > getNoObjects(ignoreNo)) {
warning("Object %d does not exist", objectId);
return nullptr;
}
return &_objects[objectId - 1];
}
Static *Scene::getStatic(uint8 staticId, bool ignoreNo) {
if (staticId == 0 || staticId > (!ignoreNo ? MIN(_noStatics, static_cast<uint8>(ARRAYSIZE(_statics))) : ARRAYSIZE(_statics))) {
warning("Static %d does not exist", staticId);
return nullptr;
}
return &_statics[staticId - 1];
}
Bitmap *Scene::getBitmap(uint8 bitmapId) {
if (bitmapId == 0 || bitmapId > ARRAYSIZE(_bitmaps)) {
warning("Bitmap %d does not exist", bitmapId);
return nullptr;
}
return &_bitmaps[bitmapId - 1];
}
uint8 Scene::getNoDoors(bool ignoreNo) const {
return (!ignoreNo ? MIN(_noDoors, static_cast<uint8>(ARRAYSIZE(_doors))) : ARRAYSIZE(_doors));
}
uint8 Scene::getNoObjects(bool ignoreNo) const {
return (!ignoreNo ? MIN(_noObjects, static_cast<uint8>(ARRAYSIZE(_objects))) : ARRAYSIZE(_objects));
}
uint8 Scene::getNoStatics(bool ignoreNo) const {
return (!ignoreNo ? MIN(_noStatics, static_cast<uint8>(ARRAYSIZE(_statics))) : ARRAYSIZE(_statics));
}
uint8 Scene::getNoBitmaps() const {
return ARRAYSIZE(_bitmaps);
}
Door *Scene::findDoor(int16 x, int16 y, bool activeOnly, int *index) {
for (int i = 0; i < getNoDoors(); ++i) {
Door &door = _doors[i];
if ((!activeOnly || door.isActive()) && (x >= door._x) && (x < door._x + door._width) && (y >= door._y) && (y < door._y + door._height)) {
if (index) {
*index = i + 1;
}
return &door;
}
}
return nullptr;
}
Static *Scene::findStatic(int16 x, int16 y, bool activeOnly, int *index) {
for (int i = 0; i < getNoStatics(); ++i) {
Static &stat = _statics[i];
if ((!activeOnly || stat._active) && (x >= stat._x) && (x < stat._x + stat._width) && (y >= stat._y) && (y < stat._y + stat._height)) {
if (index) {
*index = i + 1;
}
return &stat;
}
}
return nullptr;
}
Bitmap *Scene::findBitmap(int16 x, int16 y, int *index) {
for (int i = 0; i < ARRAYSIZE(_bitmaps); ++i) {
Bitmap &bitmap = _bitmaps[i];
if ((x >= bitmap._x1) && (x <= bitmap._x2) && (y >= bitmap._y1) && (y <= bitmap._y2)) {
if (index) {
*index = i + 1;
}
return &bitmap;
}
}
return nullptr;
}
void Scene::addExhaustedConvItem(uint8 context, uint8 convItemIndex, uint8 convGroupIndex) {
_exhaustedConvItems[_exhaustedConvItemNext - 1] = ExhaustedConvItem(context, convItemIndex, convGroupIndex);
_exhaustedConvItemNext++;
}
bool Scene::isConvItemExhausted(uint8 context, uint8 convItemIndex, uint8 convGroupIndex) const {
for (uint8 i = 0; i < _exhaustedConvItemNext - 1; ++i) {
const ExhaustedConvItem &convItem = _exhaustedConvItems[i];
if (convItem.getContext() == context && convItem.getConvItemIndex() == convItemIndex && convItem.getConvGroupIndex() == convGroupIndex) {
return true;
}
}
return false;
}
GameData::GameData()
: _currentScene(0),
_lastScene(0),
_partB(false),
_inventory(),
_currentAPK("piggy.apk"),
_color(WHITE) {}
Scene *GameData::getScene(uint8 sceneId) {
if (sceneId == 0 || sceneId > ARRAYSIZE(_scenes)) {
warning("Scene %d does not exist", sceneId);
return nullptr;
}
return &_scenes[sceneId - 1];
}
Scene *GameData::getCurrentScene() {
return getScene(_currentScene);
}
Inventory &GameData::getInventory() {
return _inventory;
}
bool GameData::loadInitialState(Common::ReadStream &stream) {
for (int i = 0; i < ARRAYSIZE(_scenes); ++i) {
_scenes[i].loadInitialState(stream);
}
return true;
}
void GameData::saveLoadWithSerializer(Common::Serializer &sz) {
for (int i = 0; i < ARRAYSIZE(_scenes); ++i) {
_scenes[i].saveLoadWithSerializer(sz);
}
sz.syncAsByte(_currentScene);
sz.syncAsByte(_partB);
_inventory.saveLoadWithSerializer(sz);
sz.syncString(_currentAPK);
}
}

View File

@@ -0,0 +1,490 @@
/* 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 MUTATIONOFJB_GAMEDATA_H
#define MUTATIONOFJB_GAMEDATA_H
#include "mutationofjb/inventory.h"
#include "common/serializer.h"
#include "common/scummsys.h"
namespace Common {
class ReadStream;
}
namespace MutationOfJB {
enum {
MAX_ENTITY_NAME_LENGTH = 0x14
};
/** @file
* There are 4 types of entities present in the game data:
* - Door
* - Object
* - Static
* - Bitmap
*/
/**
* An interactable scene changer with no visual representation.
*/
struct Door : public Common::Serializable {
/**
* Door name (NM register).
*
* Can be empty - deactivates door completely (you can't mouse over or interact with it at all).
*
* If it ends with '+', using the "go" verb on the door will not implicitly change the scene,
* but the player will still walk towards the door.
*/
char _name[MAX_ENTITY_NAME_LENGTH + 1];
/**
* Scene ID where the door leads (LT register).
* Can be 0 - you can hover your mouse over it, but clicking it doesn't do anything (unless scripted).
*/
uint8 _destSceneId;
/** X coordinate for player's position after going through the door (SX register). */
uint16 _destX;
/** Y coordinate for player's position after going through the door (SY register). */
uint16 _destY;
/** X coordinate of the door rectangle (XX register). */
uint16 _x;
/** Y coordinate of the door rectangle (YY register). */
uint8 _y;
/** Width of the door rectangle (XL register). */
uint16 _width;
/** Height of the door rectangle (YL register). */
uint8 _height;
/** X coordinate for position player will walk towards after clicking the door (WX register). */
uint16 _walkToX;
/** Y coordinate for position player will walk towards after clicking the door (WY register). */
uint8 _walkToY;
/**
* Encoded player frames.
* 4 bits - destFrame
* 4 bits - walkToFrame
*/
uint8 _SP;
/**
* Check if this door can be interacted with.
* @return True if this door can be interacted with, false otherwise.
*/
bool isActive();
/**
* Load initial state from game data file.
*
* @param stream Stream for reading.
* @return True if success, false otherwise.
*/
bool loadInitialState(Common::ReadStream &stream);
/**
* (De)serialization for save/load.
*
* @param sz Serializer.
*/
void saveLoadWithSerializer(Common::Serializer &sz) override;
/**
* Check whether walk action used on this door causes implicit scene change.
*
* @return True if door implicitly changes current scene, false otherwise.
*/
bool allowsImplicitSceneChange() const;
};
/**
* An animated image in the scene.
*
* Object frames consist of surfaces carved out of room frames (starting from _roomFrame
* up until _roomFrame + _numFrames - 1) based on the object's rectangle. They are stored
* in the shared object frame space that each object occupies a continuous part of from
* the beginning.
*
* By using the term "frame" alone we will be referring to an object frame, not a room
* frame.
*
* For details regarding animation playback, see objectanimationtask.cpp.
*/
struct Object : public Common::Serializable {
/** Controls whether the animation is playing. */
uint8 _active;
/**
* Number of the first frame this object has in the shared object frame space (FA register).
*
* For the first object, it is equal to 1.
* For any subsequent object, it is equal to (_firstFrame + _numFrames) of the previous object.
*
* @note The numbering starts from 1.
* @note Technically this field is useless because it can be calculated.
*/
uint8 _firstFrame;
/**
* The frame that is jumped to randomly based on _jumpChance (FR register).
*
* @note Numbered from 1 and relative to _firstFrame.
* @note A value of 0 disables randomness completely.
* @see objectanimationtask.cpp
* @see _jumpChance
*/
uint8 _randomFrame;
/** Number of animation frames (NA register). */
uint8 _numFrames;
/**
* Low 8 bits of the 16-bit starting room frame (FS register).
* This is in the room frame space.
*
* @see _roomFrameMSB
*/
uint8 _roomFrameLSB;
/**
* Chance (1 in x) of the animation jumping to _randomFrame.
*
* @see objectanimationtask.cpp
*/
uint8 _jumpChance;
/**
* Current animation frame (CA register).
*
* @note Index in the shared object frame space. Numbered from 1.
*/
uint8 _currentFrame;
/** X coordinate of the object rectangle (XX register). */
uint16 _x;
/** Y coordinate of the object rectangle (YY register). */
uint8 _y;
/** Width of the object rectangle (XL register). */
uint16 _width;
/** Height of the object rectangle (YL register). */
uint8 _height;
/** A general-purpose register for use in scripts. Nothing to do with animation. */
uint16 _WX;
/**
* High 8 bits of the 16-bit starting room frame (WY register).
* This is in the room frame space.
*
* @see _roomFrameLSB
*/
uint8 _roomFrameMSB;
/** Unknown. TODO: Figure out what this does. */
uint8 _SP;
/**
* Load initial state from game data file.
*
* @param stream Stream for reading.
* @return True if success, false otherwise.
*/
bool loadInitialState(Common::ReadStream &stream);
/**
* (De)serialization for save/load.
*
* @param sz Serializer.
*/
void saveLoadWithSerializer(Common::Serializer &sz) override;
};
/**
* An interactable area, usually without a visual representation.
*/
struct Static : public Common::Serializable {
/** Whether you can mouse over and interact with the static (AC register). */
uint8 _active;
/**
* Static name (NM register).
*
* If it starts with '~', the static has an implicit "pickup" action that adds
* an item with the same name (except '`' replaces '~') to your inventory and
* disables the static. If there is a matching scripted "pickup" action, it
* overrides the implicit action. This kind of static also has graphics in the
* form of its rectangle extracted from room frame 2 (and 3 after pickup).
*
* If it ends with '[', the "use" action allows combining the static with another
* entity.
*
* TODO: Support '~' statics.
*/
char _name[MAX_ENTITY_NAME_LENGTH + 1];
/** X coordinate of the static rectangle (XX register). */
uint16 _x;
/** Y coordinate of the static rectangle (YY register). */
uint8 _y;
/** Width of the static rectangle (XL register). */
uint16 _width;
/** Height of the static rectangle (YL register). */
uint8 _height;
/** X coordinate of the position the player will walk towards after clicking the static (WX register). */
uint16 _walkToX;
/** Y coordinate of the position the player will walk towards after clicking the static (WY register). */
uint8 _walkToY;
/** Player frame (rotation) set after the player finishes walking towards the walk to position (SP register). */
uint8 _walkToFrame;
/**
* Load initial state from game data file.
*
* @param stream Stream for reading.
* @return True if success, false otherwise.
*/
bool loadInitialState(Common::ReadStream &stream);
/**
* (De)serialization for save/load.
*
* @param sz Serializer.
*/
void saveLoadWithSerializer(Common::Serializer &sz) override;
/**
* Check whether this static is combinable.
* Statics with names ending with '[' are allowed to be combined with other items.
*
* @return True if combinable, false otherwise.
*/
bool isCombinable() const;
/**
* Check whether this static is implicitly picked up.
* Statics with names starting with '~' are implicitly picked up.
*
* @return Returns true if this static is implicitly picked up by pick up action, false otherwise.
*/
bool allowsImplicitPickup() const;
};
/**
* A static image that is carved out of a room frame based on its rectangle.
* The bitmap rectangle also specifies where to blit it on the screen.
*/
struct Bitmap : public Common::Serializable {
/** Room frame that this bitmap carves out of. */
uint8 _roomFrame;
/** Whether to draw the bitmap. */
uint8 _isVisible;
/** X coordinate of the top left corner of the bitmap rectangle. */
uint16 _x1;
/** Y coordinate of the top left corner of the bitmap rectangle. */
uint8 _y1;
/** X coordinate of the bottom right corner of the bitmap rectangle. */
uint16 _x2;
/** Y coordinate of the bottom right corner of the bitmap rectangle. */
uint8 _y2;
/**
* Load initial state from game data file.
*
* @param stream Stream for reading.
* @return True if success, false otherwise.
*/
bool loadInitialState(Common::ReadStream &stream);
/**
* (De)serialization for save/load.
*
* @param sz Serializer.
*/
void saveLoadWithSerializer(Common::Serializer &sz) override;
};
/**
* Encoded exhausted convesation item.
*/
struct ExhaustedConvItem {
/**
* 1 bit - context.
* 3 bits - conversation item index.
* 4 bits - conversation group index.
*/
uint8 _encodedData;
uint8 getContext() const {
return (_encodedData >> 7) & 0x1;
}
uint8 getConvItemIndex() const {
return (_encodedData >> 4) & 0x7;
}
uint8 getConvGroupIndex() const {
return _encodedData & 0xF;
}
ExhaustedConvItem() : _encodedData(0) {}
ExhaustedConvItem(uint8 context, uint8 convItemIndex, uint8 convGroupIndex) :
_encodedData(((context & 0x1) << 7) | ((convItemIndex & 0x7) << 4) | (convGroupIndex & 0xF)) {}
};
struct Scene : Common::Serializable {
Door *getDoor(uint8 objectId);
Object *getObject(uint8 objectId, bool ignoreNo = false);
Static *getStatic(uint8 staticId, bool ignoreNo = false);
Bitmap *getBitmap(uint8 bitmapId);
uint8 getNoDoors(bool ignoreNo = false) const;
uint8 getNoObjects(bool ignoreNo = false) const;
uint8 getNoStatics(bool ignoreNo = false) const;
uint8 getNoBitmaps() const;
/**
* Finds the door at the given position. By default, only active doors are considered.
*
* @param x X coordinate.
* @param y Y coordinate.
* @param activeOnly If true, consider only active doors; otherwise consider any.
* @param index Output parameter for the found door's ID.
* @return A door if found, nullptr otherwise.
*/
Door *findDoor(int16 x, int16 y, bool activeOnly = true, int *index = nullptr);
/**
* Finds the static at the given position. By default, only active statics are considered.
*
* @param x X coordinate.
* @param y Y coordinate.
* @param activeOnly If true, consider only active statics; otherwise consider any.
* @param index Output parameter for the found static's ID.
* @return A static if found, nullptr otherwise.
*/
Static *findStatic(int16 x, int16 y, bool activeOnly = true, int *index = nullptr);
Bitmap *findBitmap(int16 x, int16 y, int *index = nullptr);
void addExhaustedConvItem(uint8 context, uint8 convItemIndex, uint8 convGroupIndex);
bool isConvItemExhausted(uint8 context, uint8 convItemIndex, uint8 convGroupIndex) const;
/** Refers to the script block that will be executed when you enter this scene (DS register). */
uint8 _startup;
/**
* These three variables control downscaling of the player character depending on his Y.
* TODO: Find out more.
*/
uint8 _unknown001;
uint8 _unknown002;
uint8 _unknown003;
uint8 _delay; /**< Delay between object animation advancements (DL register). */
uint8 _noDoors; /**< Number of doors in the scene (ND register). */
Door _doors[5]; /**< Door definitions. */
uint8 _noObjects; /**< Number of animated objects in the scene (NO register). */
Object _objects[9]; /**< Object definitions. */
uint8 _noStatics; /**< Number of statics in the scene (NS register). */
Static _statics[15]; /**< Static definitions. */
Bitmap _bitmaps[10]; /**< Bitmap definitions. There is no corresponding _noBitmaps field. */
uint16 _obstacleY1; /**< Fixed Y coordinate for all static obstacles in the scene. Always 0 in data files. */
/** First index (inclusive and 0-indexed) of the rotating portion of the palette (PF register). */
uint8 _palRotFirst;
/** Last index (inclusive and 0-indexed) of the rotating portion of the palette (PL register). */
uint8 _palRotLast;
/** Delay between each right rotation of the palette portion (PD register). */
uint8 _palRotDelay;
/**
* Points to the first free item in exhausted conversation item array.
* @note Indexed from 1.
*/
uint8 _exhaustedConvItemNext;
ExhaustedConvItem _exhaustedConvItems[79];
/**
* Load initial state from game data file.
*
* @param stream Stream for reading.
* @return True if success, false otherwise.
*/
bool loadInitialState(Common::ReadStream &stream);
/**
* (De)serialization for save/load.
*
* @param sz Serializer.
*/
void saveLoadWithSerializer(Common::Serializer &sz) override;
};
struct ConversationInfo {
struct Item {
uint8 _question;
uint8 _response;
uint8 _nextGroupIndex;
};
typedef Common::Array<Item> ItemGroup;
Common::Array<ItemGroup> _itemGroups;
uint8 _context;
uint8 _objectId;
uint8 _color;
};
struct GameData : public Common::Serializable {
public:
GameData();
Scene *getScene(uint8 sceneId);
Scene *getCurrentScene();
Inventory &getInventory();
/**
* Load initial state from game data file.
*
* @param stream Stream for reading.
* @return True if success, false otherwise.
*/
bool loadInitialState(Common::ReadStream &stream);
/**
* (De)serialization for save/load.
*
* @param sz Serializer.
*/
void saveLoadWithSerializer(Common::Serializer &sz) override;
uint8 _currentScene; // Persistent.
uint8 _lastScene;
bool _partB; // Persistent.
Inventory _inventory; // Persistent.
Common::String _currentAPK; // Persistent.
ConversationInfo _conversationInfo;
/** Current SayCommand color. */
uint8 _color;
private:
Scene _scenes[45]; // Persistent.
};
enum Colors {
WHITE = 0xC6,
DARKGRAY = 0xC2,
LIGHTGRAY = 0xC4,
GREEN = 0xC8,
ORANGE = 0xCA,
DARKBLUE = 0xD6,
LIGHTBLUE = 0xDA,
BROWN = 0xDC
};
}
#endif

View File

@@ -0,0 +1,422 @@
/* 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 "mutationofjb/gamescreen.h"
#include "mutationofjb/animationdecoder.h"
#include "mutationofjb/encryptedfile.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/mutationofjb.h"
#include "mutationofjb/inventory.h"
#include "mutationofjb/room.h"
#include "mutationofjb/util.h"
#include "mutationofjb/widgets/conversationwidget.h"
#include "mutationofjb/widgets/gamewidget.h"
#include "mutationofjb/widgets/imagewidget.h"
#include "mutationofjb/widgets/inventorywidget.h"
#include "mutationofjb/widgets/labelwidget.h"
#include "common/events.h"
#include "common/rect.h"
#include "graphics/screen.h"
namespace MutationOfJB {
enum ButtonType {
BUTTON_WALK = 0,
BUTTON_TALK,
BUTTON_LOOK,
BUTTON_USE,
BUTTON_PICKUP,
BUTTON_SCROLL_LEFT,
BUTTON_SCROLL_RIGHT,
BUTTON_SETTINGS,
NUM_BUTTONS
};
enum {
INVENTORY_START_X = 88,
INVENTORY_START_Y = 149,
INVENTORY_ITEM_WIDTH = 34,
INVENTORY_ITEM_HEIGHT = 33,
INVENTORY_ITEMS_PER_LINE = 8,
INVENTORY_ITEMS_LINES = 5,
CONVERSATION_X = 0,
CONVERSATION_Y = 139,
CONVERSATION_WIDTH = 320,
CONVERSATION_HEIGHT = 61,
STATUS_BAR_X = 0,
STATUS_BAR_Y = 140,
STATUS_BAR_WIDTH = 320,
STATUS_BAR_HEIGHT = 8
};
GameScreen::GameScreen(Game &game, Graphics::Screen *screen)
: GuiScreen(game, screen),
_inventoryWidget(nullptr),
_conversationWidget(nullptr),
_statusBarWidget(nullptr),
_currentAction(ActionInfo::Walk) {}
GameScreen::~GameScreen() {}
bool GameScreen::init() {
if (!loadInventoryGfx()) {
return false;
}
if (!loadHudGfx()) {
return false;
}
_game.getGameData().getInventory().setObserver(this);
// Init widgets.
const Common::Rect backgroundRect(CONVERSATION_X, CONVERSATION_Y, CONVERSATION_X + CONVERSATION_WIDTH, CONVERSATION_Y + CONVERSATION_HEIGHT);
const Graphics::Surface backgroundSurface = _hudSurfaces[0].getSubArea(backgroundRect);
ImageWidget *image = new ImageWidget(*this, backgroundRect, backgroundSurface);
addWidget(image);
_inventoryWidget = new InventoryWidget(*this, _inventorySurfaces);
_inventoryWidget->setCallback(this);
addWidget(_inventoryWidget);
const Common::Rect ButtonRects[] = {
Common::Rect(0, 148, 67, 158), // Walk
Common::Rect(0, 158, 67, 168), // Talk
Common::Rect(0, 168, 67, 178), // Look
Common::Rect(0, 178, 67, 188), // Use
Common::Rect(0, 188, 67, 198), // PickUp
Common::Rect(67, 149, 88, 174), // ScrollLeft
Common::Rect(67, 174, 88, 199), // ScrollRight
Common::Rect(301, 148, 320, 200) // Settings
};
for (int i = 0; i < NUM_BUTTONS; ++i) {
const Graphics::Surface normalSurface = _hudSurfaces[0].getSubArea(ButtonRects[i]);
const Graphics::Surface pressedSurface = _hudSurfaces[1].getSubArea(ButtonRects[i]);
ButtonWidget *button = new ButtonWidget(*this, ButtonRects[i], normalSurface, pressedSurface);
button->setId(i);
button->setCallback(this);
_buttons.push_back(button);
addWidget(button);
}
const Common::Rect statusBarRect(STATUS_BAR_X, STATUS_BAR_Y, STATUS_BAR_X + STATUS_BAR_WIDTH, STATUS_BAR_Y + STATUS_BAR_HEIGHT);
_statusBarWidget = new LabelWidget(*this, statusBarRect);
addWidget(_statusBarWidget);
const Common::Rect conversationRect(CONVERSATION_X, CONVERSATION_Y, CONVERSATION_X + CONVERSATION_WIDTH, CONVERSATION_Y + CONVERSATION_HEIGHT);
const Graphics::Surface conversationSurface = _hudSurfaces[2].getSubArea(conversationRect);
_conversationWidget = new ConversationWidget(*this, conversationRect, conversationSurface);
_conversationWidget->setVisible(false);
addWidget(_conversationWidget);
_gameWidget = new GameWidget(*this);
_gameWidget->setCallback(this);
addWidget(_gameWidget);
return true;
}
void GameScreen::handleEvent(const Common::Event &event) {
switch (event.type) {
case Common::EVENT_KEYUP: {
switch (event.kbd.ascii) {
case 'g':
_currentAction = ActionInfo::Walk;
_currentPickedItem.clear();
break;
case 'r':
_currentAction = ActionInfo::Talk;
_currentPickedItem.clear();
break;
case 's':
_currentAction = ActionInfo::Look;
_currentPickedItem.clear();
break;
case 'b':
_currentAction = ActionInfo::Use;
_currentPickedItem.clear();
break;
case 'n':
_currentAction = ActionInfo::PickUp;
_currentPickedItem.clear();
break;
default:
break;
}
break;
}
default:
break;
}
GuiScreen::handleEvent(event);
}
ConversationWidget &GameScreen::getConversationWidget() {
return *_conversationWidget;
}
void GameScreen::showConversationWidget(bool show) {
_gameWidget->setEnabled(!show);
_conversationWidget->setVisible(show);
_statusBarWidget->setText(Common::String());
for (Common::Array<ButtonWidget *>::const_iterator it = _buttons.begin(); it != _buttons.end(); ++it) {
(*it)->setVisible(!show);
}
_inventoryWidget->setVisible(!show);
}
void GameScreen::refreshAfterSceneChanged() {
const Widgets &widgets = getWidgets();
if (!getGame().isCurrentSceneMap()) {
_gameWidget->setArea(Common::Rect(GameWidget::GAME_NORMAL_AREA_WIDTH, GameWidget::GAME_NORMAL_AREA_HEIGHT));
for (Widgets::const_iterator it = widgets.begin(); it != widgets.end(); ++it) {
if (*it == _gameWidget || *it == _conversationWidget)
continue;
(*it)->setVisible(true);
}
} else {
_gameWidget->setArea(Common::Rect(GameWidget::GAME_FULL_AREA_WIDTH, GameWidget::GAME_FULL_AREA_HEIGHT));
for (Widgets::const_iterator it = widgets.begin(); it != widgets.end(); ++it) {
if (*it == _gameWidget || *it == _conversationWidget)
continue;
(*it)->setVisible(false);
}
}
_gameWidget->clearState();
// Fake mouse move event to update the cursor.
Common::Event event;
event.type = Common::EVENT_MOUSEMOVE;
event.mouse = _game.getEngine().getEventManager()->getMousePos();
_gameWidget->handleEvent(event);
_gameWidget->markDirty(GameWidget::DIRTY_AFTER_SCENE_CHANGE);
_gameWidget->update(*_screen); // Force immediate update.
}
class InventoryAnimationDecoderCallback : public AnimationDecoderCallback {
public:
InventoryAnimationDecoderCallback(GameScreen &gui) : _gui(gui) {}
void onFrame(int frameNo, Graphics::Surface &surface) override;
void onPaletteUpdated(byte palette[PALETTE_SIZE]) override;
private:
GameScreen &_gui;
};
void InventoryAnimationDecoderCallback::onPaletteUpdated(byte palette[PALETTE_SIZE]) {
_gui._screen->setPalette(palette + 0xC0 * 3, 0xC0, 0x20); // Load only 0x20 colors.
}
void InventoryAnimationDecoderCallback::onFrame(int frameNo, Graphics::Surface &surface) {
if (frameNo < 3) {
Graphics::Surface outSurface;
outSurface.copyFrom(surface);
_gui._inventorySurfaces.push_back(outSurface);
}
}
bool GameScreen::loadInventoryGfx() {
AnimationDecoder decoder("icons.dat");
InventoryAnimationDecoderCallback callback(*this);
return decoder.decode(&callback);
}
class HudAnimationDecoderCallback : public AnimationDecoderCallback {
public:
HudAnimationDecoderCallback(GameScreen &gui) : _gui(gui) {}
void onFrame(int frameNo, Graphics::Surface &surface) override;
void onPaletteUpdated(byte palette[PALETTE_SIZE]) override;
private:
GameScreen &_gui;
};
void HudAnimationDecoderCallback::onPaletteUpdated(byte [PALETTE_SIZE]) {
}
void HudAnimationDecoderCallback::onFrame(int frameNo, Graphics::Surface &surface) {
if (frameNo == 0 || frameNo == 1 || frameNo == 4) {
Graphics::Surface outSurface;
outSurface.copyFrom(surface);
_gui._hudSurfaces.push_back(outSurface);
}
}
bool GameScreen::loadHudGfx() {
AnimationDecoder decoder("room0.dat");
HudAnimationDecoderCallback callback(*this);
return decoder.decode(&callback);
}
void GameScreen::updateStatusBarText(const Common::String &entity, bool inventory) {
const bool hasPrevPickedItem = !_currentPickedItem.empty();
const bool hasCurrentItem = !entity.empty();
if (!hasPrevPickedItem && !hasCurrentItem) {
_statusBarWidget->setText(Common::String());
return;
}
HardcodedStrings::StringType actionStringType = HardcodedStrings::LOOK;
if (inventory) {
switch (_currentAction) {
case ActionInfo::Use:
actionStringType = HardcodedStrings::USE;
break;
case ActionInfo::Look:
default:
actionStringType = HardcodedStrings::LOOK;
break;
}
} else {
switch (_currentAction) {
case ActionInfo::Look:
default:
actionStringType = HardcodedStrings::LOOK;
break;
case ActionInfo::Walk:
actionStringType = HardcodedStrings::WALK;
break;
case ActionInfo::Talk:
actionStringType = HardcodedStrings::TALK;
break;
case ActionInfo::Use:
actionStringType = HardcodedStrings::USE;
break;
case ActionInfo::PickUp:
actionStringType = HardcodedStrings::PICKUP;
break;
}
}
Common::String text = _game.getAssets().getHardcodedStrings().getString(actionStringType);
if (hasPrevPickedItem)
text += " " + _currentPickedItem;
if (hasCurrentItem)
text += " " + entity;
_statusBarWidget->setText(text);
}
void GameScreen::onInventoryChanged() {
_inventoryWidget->markDirty();
}
void GameScreen::onButtonClicked(ButtonWidget *button) {
const int buttonId = button->getId();
if (buttonId <= BUTTON_PICKUP) {
const ActionInfo::Action actions[] = {ActionInfo::Walk, ActionInfo::Talk, ActionInfo::Look, ActionInfo::Use, ActionInfo::PickUp};
_currentAction = actions[buttonId];
_currentPickedItem.clear();
} else if (buttonId == BUTTON_SCROLL_LEFT) {
_game.getGameData().getInventory().scrollLeft();
} else if (buttonId == BUTTON_SCROLL_RIGHT) {
_game.getGameData().getInventory().scrollRight();
}
}
void GameScreen::onInventoryItemHovered(InventoryWidget *, int posInWidget) {
if (posInWidget == -1) {
updateStatusBarText(Common::String(), true);
} else {
const Common::String &item = _game.getGameData().getInventory().getItems()[posInWidget];
updateStatusBarText(item, true);
}
}
void GameScreen::onInventoryItemClicked(InventoryWidget *, int posInWidget) {
// Position in widget should match the position in inventory.
const Common::String &item = _game.getGameData().getInventory().getItems()[posInWidget];
if (_currentAction == ActionInfo::Use) {
if (_currentPickedItem.empty()) {
// Inventory items ending with '[' aren't supposed to be combined (e.g. Fisher's mask).
if (item.lastChar() == '[')
_game.startActionSection(ActionInfo::Use, item);
else
_currentPickedItem = item;
} else {
_game.startActionSection(ActionInfo::Use, _currentPickedItem, item);
_currentPickedItem.clear();
}
} else {
_game.startActionSection(ActionInfo::Look, item);
}
}
void GameScreen::onGameDoorClicked(GameWidget *, Door *door) {
if (!_currentPickedItem.empty()) {
_game.startActionSection(_currentAction, _currentPickedItem, door->_name);
_currentPickedItem.clear();
return;
}
if (!_game.startActionSection(_currentAction, door->_name) && _currentAction == ActionInfo::Walk && door->_destSceneId != 0) {
if (door->allowsImplicitSceneChange())
_game.changeScene(door->_destSceneId, _game.getGameData()._partB);
}
}
void GameScreen::onGameStaticClicked(GameWidget *, Static *stat) {
if (_currentAction == ActionInfo::Use) {
if (_currentPickedItem.empty()) {
if (stat->isCombinable())
_currentPickedItem = stat->_name;
else
_game.startActionSection(ActionInfo::Use, stat->_name);
} else {
_game.startActionSection(_currentAction, _currentPickedItem, stat->_name);
_currentPickedItem.clear();
}
} else {
if (!_game.startActionSection(_currentAction, stat->_name)) {
if (_currentAction == ActionInfo::PickUp && stat->allowsImplicitPickup()) {
Common::String inventoryName(stat->_name);
inventoryName.setChar('`', 0);
_game.getGameData().getInventory().addItem(inventoryName);
stat->_active = 0;
_game.getRoom().drawStatic(stat);
}
}
}
}
void GameScreen::onGameEntityHovered(GameWidget *, const Common::String &entity) {
updateStatusBarText(entity, false);
}
}

View File

@@ -0,0 +1,103 @@
/* 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 MUTATIONOFJB_GUI_H
#define MUTATIONOFJB_GUI_H
#include "mutationofjb/inventory.h"
#include "mutationofjb/script.h"
#include "mutationofjb/guiscreen.h"
#include "mutationofjb/widgets/buttonwidget.h"
#include "mutationofjb/widgets/inventorywidget.h"
#include "mutationofjb/widgets/gamewidget.h"
#include "common/array.h"
#include "common/hashmap.h"
#include "common/hash-str.h"
#include "graphics/surface.h"
namespace Common {
struct Event;
}
namespace Graphics {
class Screen;
}
namespace MutationOfJB {
class Game;
class Widget;
class InventoryWidget;
class ConversationWidget;
class LabelWidget;
class GameWidget;
class GameScreen : public GuiScreen, public InventoryObserver, public ButtonWidgetCallback, public InventoryWidgetCallback, public GameWidgetCallback {
public:
friend class InventoryAnimationDecoderCallback;
friend class HudAnimationDecoderCallback;
GameScreen(Game &game, Graphics::Screen *screen);
~GameScreen() override;
bool init();
void handleEvent(const Common::Event &event) override;
void onInventoryChanged() override;
void onButtonClicked(ButtonWidget *) override;
void onInventoryItemHovered(InventoryWidget *widget, int posInWidget) override;
void onInventoryItemClicked(InventoryWidget *widget, int posInWidget) override;
void onGameDoorClicked(GameWidget *, Door *door) override;
void onGameStaticClicked(GameWidget *, Static *stat) override;
void onGameEntityHovered(GameWidget *, const Common::String &entity) override;
ConversationWidget &getConversationWidget();
void showConversationWidget(bool show);
void refreshAfterSceneChanged();
private:
bool loadInventoryGfx();
bool loadHudGfx();
void drawInventoryItem(const Common::String &item, int pos);
void drawInventory();
void updateStatusBarText(const Common::String &entity, bool inventory);
Common::Array<Graphics::Surface> _inventorySurfaces;
Common::Array<Graphics::Surface> _hudSurfaces;
Common::Array<ButtonWidget *> _buttons;
InventoryWidget *_inventoryWidget;
ConversationWidget *_conversationWidget;
LabelWidget *_statusBarWidget;
GameWidget *_gameWidget;
ActionInfo::Action _currentAction;
Common::String _currentPickedItem;
};
}
#endif

View File

@@ -0,0 +1,77 @@
/* 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 "mutationofjb/guiscreen.h"
#include "mutationofjb/widgets/widget.h"
#include "graphics/screen.h"
namespace MutationOfJB {
GuiScreen::GuiScreen(Game &game, Graphics::Screen *screen)
: _game(game),
_screen(screen) {}
GuiScreen::~GuiScreen() {
for (Common::Array<Widget *>::iterator it = _widgets.begin(); it != _widgets.end(); ++it) {
delete *it;
}
}
Game &GuiScreen::getGame() {
return _game;
}
void GuiScreen::markDirty() {
for (Common::Array<Widget *>::iterator it = _widgets.begin(); it != _widgets.end(); ++it) {
if ((*it)->isVisible()) {
(*it)->markDirty();
}
}
}
void GuiScreen::handleEvent(const Common::Event &event) {
for (Common::Array<Widget *>::iterator it = _widgets.begin(); it != _widgets.end(); ++it) {
if ((*it)->isVisible()) {
(*it)->handleEvent(event);
}
}
}
void GuiScreen::update() {
for (Common::Array<Widget *>::iterator it = _widgets.begin(); it != _widgets.end(); ++it) {
if ((*it)->isVisible()) {
(*it)->update(*_screen);
}
}
}
void GuiScreen::addWidget(Widget *widget) {
_widgets.push_back(widget);
widget->markDirty();
}
const GuiScreen::Widgets &GuiScreen::getWidgets() const {
return _widgets;
}
}

View File

@@ -0,0 +1,91 @@
/* 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 MUTATIONOFJB_GUISCREEN_H
#define MUTATIONOFJB_GUISCREEN_H
#include "common/array.h"
namespace Common {
struct Event;
}
namespace Graphics {
class Screen;
}
namespace MutationOfJB {
class Game;
class Widget;
/**
* Base class for GUI screens.
*
* GUI screen is a collection of widgets.
*/
class GuiScreen {
public:
GuiScreen(Game &game, Graphics::Screen *screen);
virtual ~GuiScreen();
Game &getGame();
/**
* Marks all visible widgets as dirty (needs redraw).
*/
void markDirty();
/**
* Lets all visible widgets handle core events.
*
* @param event ScummVM event.
*/
virtual void handleEvent(const Common::Event &event);
/**
* Updates all visible widgets.
*/
void update();
/**
* Adds a widget to the GUI screen.
* The GUI screen will own the widget.
*
* @param widget Widget to add.
*/
void addWidget(Widget *widget);
protected:
typedef Common::Array<Widget *> Widgets;
Game &_game;
Graphics::Screen *_screen;
const Widgets &getWidgets() const;
private:
Widgets _widgets;
};
}
#endif

View File

@@ -0,0 +1,168 @@
/* 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 "mutationofjb/hardcodedstrings.h"
#include "mutationofjb/game.h"
#include "mutationofjb/util.h"
#include "common/file.h"
namespace MutationOfJB {
HardcodedStrings::HardcodedStrings(Game &game) : _strings(STRING_TYPES_TOTAL) {
loadStrings(game.getLanguage());
}
const Common::String &HardcodedStrings::getString(HardcodedStrings::StringType strType) const {
const StringArray::size_type index = static_cast<StringArray::size_type>(strType);
assert(index < _strings.size());
return _strings[index];
}
void HardcodedStrings::loadStrings(Common::Language lang) {
Common::File file;
const char *const fileName = "jb.ex_";
if (!file.open(fileName)) {
reportFileMissingError(fileName);
return;
}
if (lang == Common::SK_SVK)
file.seek(0xBAA8);
else if (lang == Common::DE_DEU)
file.seek(0xBC48);
else
return;
Common::String str;
file.readPascalString(); // WALK TO
file.readPascalString(); // TALK TO
file.readPascalString(); // PICK UP
file.readPascalString(); // LOOK AT
str = file.readPascalString();
if (lang == Common::SK_SVK)
_strings[WALK] = str;
str = file.readPascalString();
if (lang == Common::DE_DEU)
_strings[WALK] = str;
str = file.readPascalString();
if (lang == Common::SK_SVK)
_strings[LOOK] = str;
file.readPascalString();
file.readPascalString();
str = file.readPascalString();
if (lang == Common::DE_DEU)
_strings[LOOK] = str;
str = file.readPascalString();
if (lang == Common::SK_SVK)
_strings[PICKUP] = str;
str = file.readPascalString();
if (lang == Common::DE_DEU)
_strings[PICKUP] = str;
str = file.readPascalString();
if (lang == Common::SK_SVK)
_strings[TALK] = str;
str = file.readPascalString();
if (lang == Common::DE_DEU)
_strings[TALK] = str;
file.readPascalString(); // USE
str = file.readPascalString();
if (lang == Common::SK_SVK)
_strings[USE] = str;
str = file.readPascalString();
if (lang == Common::DE_DEU)
_strings[USE] = str;
if (lang == Common::SK_SVK)
file.seek(0x1982F);
else if (lang == Common::DE_DEU)
file.seek(0x199F0);
else
return;
_strings[JOHNNY_CANNOT_USE_1] = file.readPascalString();
_strings[SKEPTO_CANNOT_USE_1] = file.readPascalString();
file.readPascalString();
_strings[JOHNNY_CANNOT_USE_2] = file.readPascalString();
_strings[SKEPTO_CANNOT_USE_2] = file.readPascalString();
file.readPascalString();
_strings[JOHNNY_CANNOT_USE_3] = file.readPascalString();
_strings[SKEPTO_CANNOT_USE_3] = file.readPascalString();
file.readPascalString();
_strings[JOHNNY_CANNOT_USE_4] = file.readPascalString();
_strings[SKEPTO_CANNOT_USE_4] = file.readPascalString();
file.readPascalString();
_strings[JOHNNY_CANNOT_TALK_1] = file.readPascalString();
_strings[SKEPTO_CANNOT_TALK_1] = file.readPascalString();
file.readPascalString();
_strings[JOHNNY_CANNOT_TALK_2] = file.readPascalString();
_strings[SKEPTO_CANNOT_TALK_2] = file.readPascalString();
file.readPascalString();
_strings[JOHNNY_CANNOT_TALK_3] = file.readPascalString();
_strings[SKEPTO_CANNOT_TALK_3] = file.readPascalString();
file.readPascalString();
_strings[JOHNNY_CANNOT_TALK_4] = file.readPascalString();
_strings[SKEPTO_CANNOT_TALK_4] = file.readPascalString();
file.readPascalString();
_strings[JOHNNY_CANNOT_LOOK_1] = file.readPascalString();
_strings[SKEPTO_CANNOT_LOOK_1] = file.readPascalString();
file.readPascalString();
_strings[JOHNNY_CANNOT_LOOK_2] = file.readPascalString();
_strings[SKEPTO_CANNOT_LOOK_2] = file.readPascalString();
file.readPascalString();
if (lang != Common::SK_SVK) // This sentence seems to be missing from the Slovak executable.
_strings[JOHNNY_CANNOT_LOOK_3] = file.readPascalString();
_strings[SKEPTO_CANNOT_LOOK_3] = file.readPascalString();
file.readPascalString();
_strings[JOHNNY_CANNOT_LOOK_4] = file.readPascalString();
_strings[SKEPTO_CANNOT_LOOK_4] = file.readPascalString();
file.readPascalString();
_strings[JOHNNY_CANNOT_PICKUP_1] = file.readPascalString();
_strings[SKEPTO_CANNOT_PICKUP_1] = file.readPascalString();
file.readPascalString();
if (lang != Common::SK_SVK) // This sentence seems to be missing from the Slovak executable.
_strings[JOHNNY_CANNOT_PICKUP_2] = file.readPascalString();
_strings[SKEPTO_CANNOT_PICKUP_2] = file.readPascalString();
file.readPascalString();
_strings[JOHNNY_CANNOT_PICKUP_3] = file.readPascalString();
_strings[SKEPTO_CANNOT_PICKUP_3] = file.readPascalString();
file.readPascalString();
_strings[JOHNNY_CANNOT_PICKUP_4] = file.readPascalString();
_strings[SKEPTO_CANNOT_PICKUP_4] = file.readPascalString();
}
}

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 MUTATIONOFJB_HARDCODEDSTRINGS_H
#define MUTATIONOFJB_HARDCODEDSTRINGS_H
#include "common/language.h"
#include "common/hashmap.h"
namespace MutationOfJB {
class Game;
/**
* Provides access to hardcoded strings.
*
* Currently, we do not have any agreement with the original author of the game,
* so the strings are loaded from the gmae executable file.
*/
class HardcodedStrings {
public:
enum StringType {
WALK,
TALK,
LOOK,
USE,
PICKUP,
JOHNNY_CANNOT_USE_1,
SKEPTO_CANNOT_USE_1,
JOHNNY_CANNOT_USE_2,
SKEPTO_CANNOT_USE_2,
JOHNNY_CANNOT_USE_3,
SKEPTO_CANNOT_USE_3,
JOHNNY_CANNOT_USE_4,
SKEPTO_CANNOT_USE_4,
JOHNNY_CANNOT_TALK_1,
SKEPTO_CANNOT_TALK_1,
JOHNNY_CANNOT_TALK_2,
SKEPTO_CANNOT_TALK_2,
JOHNNY_CANNOT_TALK_3,
SKEPTO_CANNOT_TALK_3,
JOHNNY_CANNOT_TALK_4,
SKEPTO_CANNOT_TALK_4,
JOHNNY_CANNOT_LOOK_1,
SKEPTO_CANNOT_LOOK_1,
JOHNNY_CANNOT_LOOK_2,
SKEPTO_CANNOT_LOOK_2,
JOHNNY_CANNOT_LOOK_3,
SKEPTO_CANNOT_LOOK_3,
JOHNNY_CANNOT_LOOK_4,
SKEPTO_CANNOT_LOOK_4,
JOHNNY_CANNOT_PICKUP_1,
SKEPTO_CANNOT_PICKUP_1,
JOHNNY_CANNOT_PICKUP_2,
SKEPTO_CANNOT_PICKUP_2,
JOHNNY_CANNOT_PICKUP_3,
SKEPTO_CANNOT_PICKUP_3,
JOHNNY_CANNOT_PICKUP_4,
SKEPTO_CANNOT_PICKUP_4,
STRING_TYPES_TOTAL
};
HardcodedStrings(Game &game);
/**
* Get hardcoded string.
*
* @param strType String type.
* @return Hardcoded string.
*/
const Common::String &getString(StringType strType) const;
private:
typedef Common::Array<Common::String> StringArray;
void loadStrings(Common::Language lang);
StringArray _strings;
};
}
#endif

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/>.
*
*/
#include "mutationofjb/inventory.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamescreen.h"
#include "common/algorithm.h"
#include "common/debug.h"
namespace MutationOfJB {
const Inventory::Items &Inventory::getItems() const {
return _items;
}
bool Inventory::hasItem(const Common::String &item) const {
Items::const_iterator it = Common::find(_items.begin(), _items.end(), item);
return (it != _items.end());
}
void Inventory::addItem(const Common::String &item) {
_items.push_back(item);
if (_items.size() > VISIBLE_ITEMS) {
rotateItemsRight(VISIBLE_ITEMS);
}
if (_observer) {
_observer->onInventoryChanged();
}
}
void Inventory::removeItem(const Common::String &item) {
Items::iterator it = Common::find(_items.begin(), _items.end(), item);
if (it == _items.end()) {
debug("Item '%s' not in inventory.", item.c_str());
return;
}
_items.remove_at(it - _items.begin());
if (_observer) {
_observer->onInventoryChanged();
}
}
void Inventory::removeAllItems() {
_items.clear();
if (_observer) {
_observer->onInventoryChanged();
}
}
void Inventory::renameItem(const Common::String &oldName, const Common::String &newName) {
bool renamed = false;
for (Items::iterator it = _items.begin(); it != _items.end(); ++it) {
if (*it == oldName) {
*it = newName;
renamed = true;
}
}
if (renamed && _observer) {
_observer->onInventoryChanged();
}
}
void Inventory::scrollLeft() {
if (_items.size() > VISIBLE_ITEMS) {
rotateItemsRight(1);
}
}
void Inventory::scrollRight() {
if (_items.size() > VISIBLE_ITEMS) {
rotateItemsLeft(1);
}
}
void Inventory::rotateItemsRight(uint n) {
if (_items.size() < 2) {
return;
}
n %= _items.size();
reverseItems(0, _items.size() - 1);
reverseItems(0, n - 1);
reverseItems(n, _items.size() - 1);
if (_observer) {
_observer->onInventoryChanged();
}
}
void Inventory::rotateItemsLeft(uint n) {
if (_items.size() < 2) {
return;
}
n %= _items.size();
reverseItems(0, _items.size() - 1);
reverseItems(_items.size() - n, _items.size() - 1);
reverseItems(0, _items.size() - n - 1);
if (_observer) {
_observer->onInventoryChanged();
}
}
void Inventory::setObserver(InventoryObserver *observer) {
_observer = observer;
}
void Inventory::reverseItems(uint from, uint to) {
assert(from <= to);
if (from == to) {
return;
}
const uint size = to - from + 1;
for (uint i = 0; i < size / 2; ++i) {
SWAP(_items[from + i], _items[to - i]);
}
}
void Inventory::saveLoadWithSerializer(Common::Serializer &sz) {
if (sz.isLoading()) {
uint32 length = 0;
sz.syncAsUint32LE(length);
_items.resize(length);
} else {
uint32 length = static_cast<uint32>(_items.size());
sz.syncAsUint32LE(length);
}
for (Items::size_type i = 0; i < _items.size(); ++i) {
sz.syncString(_items[i]);
}
}
}

View File

@@ -0,0 +1,75 @@
/* 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 MUTATIONOFJB_INVENTORY_H
#define MUTATIONOFJB_INVENTORY_H
#include "common/array.h"
#include "common/serializer.h"
#include "common/scummsys.h"
#include "common/str.h"
namespace MutationOfJB {
class Game;
class InventoryObserver {
public:
virtual void onInventoryChanged() = 0;
virtual ~InventoryObserver() {}
};
class Inventory : public Common::Serializable {
public:
enum {
VISIBLE_ITEMS = 6
};
typedef Common::Array<Common::String> Items;
Inventory() : _observer(nullptr) {}
const Items &getItems() const;
bool hasItem(const Common::String &item) const;
void addItem(const Common::String &item);
void removeItem(const Common::String &item);
void removeAllItems();
void renameItem(const Common::String &oldName, const Common::String &newName);
void scrollLeft();
void scrollRight();
void setObserver(InventoryObserver *observer);
void saveLoadWithSerializer(Common::Serializer &sz) override;
private:
void rotateItemsRight(uint n);
void rotateItemsLeft(uint n);
void reverseItems(uint from, uint to);
Items _items;
InventoryObserver *_observer;
};
}
#endif

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/>.
*
*/
#include "mutationofjb/inventoryitemdefinitionlist.h"
#include "mutationofjb/encryptedfile.h"
#include "mutationofjb/util.h"
namespace MutationOfJB {
InventoryItemDefinitionList::InventoryItemDefinitionList() {
parseFile();
}
int InventoryItemDefinitionList::findItemIndex(const Common::String &itemName) {
const InventoryItemMap::const_iterator it = _inventoryItemMap.find(itemName);
if (it == _inventoryItemMap.end())
return -1;
return it->_value;
}
bool InventoryItemDefinitionList::parseFile() {
EncryptedFile file;
const char *fileName = "fixitems.dat";
file.open(fileName);
if (!file.isOpen()) {
reportFileMissingError(fileName);
return false;
}
int itemIndex = 0;
while (!file.eos()) {
Common::String line = file.readLine();
if (line.empty() || line.hasPrefix("#")) {
continue;
}
Common::String::const_iterator firstSpace = Common::find(line.begin(), line.end(), ' ');
if (firstSpace == line.end()) {
continue;
}
const int len = firstSpace - line.begin();
if (!len) {
continue;
}
Common::String item(line.c_str(), len);
_inventoryItemMap[item] = itemIndex;
itemIndex++;
}
return true;
}
}

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 MUTATIONOFJB_INVENTORYITEMDEFINITIONLIST_H
#define MUTATIONOFJB_INVENTORYITEMDEFINITIONLIST_H
#include "common/str.h"
#include "common/hash-str.h"
#include "common/hashmap.h"
namespace MutationOfJB {
typedef Common::HashMap<Common::String, int> InventoryMap;
class InventoryItemDefinitionList {
public:
InventoryItemDefinitionList();
const InventoryMap &getInventorMap() const;
int findItemIndex(const Common::String &itemName);
private:
bool parseFile();
typedef Common::HashMap<Common::String, int> InventoryItemMap;
InventoryItemMap _inventoryItemMap;
};
}
#endif

View File

@@ -0,0 +1,86 @@
/* 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 "mutationofjb/mutationofjb.h"
#include "common/config-manager.h"
#include "common/system.h"
#include "common/savefile.h"
#include "common/serializer.h"
#include "engines/advancedDetector.h"
class MutationOfJBMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
public:
const char *getName() const override {
return "mutationofjb";
}
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override {
*engine = new MutationOfJB::MutationOfJBEngine(syst, desc);
return Common::kNoError;
}
bool hasFeature(MetaEngineFeature f) const override {
if (f == kSupportsListSaves || f == kSimpleSavesNames || f == kSupportsLoadingDuringStartup) {
return true;
}
return false;
}
int getMaximumSaveSlot() const override {
return 999;
}
SaveStateList listSaves(const char *target) const override {
Common::SaveFileManager *const saveFileMan = g_system->getSavefileManager();
Common::StringArray filenames;
Common::String pattern = target;
pattern += ".###";
filenames = saveFileMan->listSavefiles(pattern);
SaveStateList saveList;
int slotNo = 0;
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
// Obtain the last 3 digits of the filename, since they correspond to the save slot
slotNo = atoi(file->c_str() + file->size() - 3);
Common::InSaveFile *const in = saveFileMan->openForLoading(*file);
if (in) {
Common::Serializer sz(in, nullptr);
MutationOfJB::SaveHeader saveHdr;
if (saveHdr.sync(sz)) {
saveList.push_back(SaveStateDescriptor(this, slotNo, saveHdr._description));
}
}
}
return saveList;
}
};
#if PLUGIN_ENABLED_DYNAMIC(MUTATIONOFJB)
REGISTER_PLUGIN_DYNAMIC(MUTATIONOFJB, PLUGIN_TYPE_ENGINE, MutationOfJBMetaEngine);
#else
REGISTER_PLUGIN_STATIC(MUTATIONOFJB, PLUGIN_TYPE_ENGINE, MutationOfJBMetaEngine);
#endif

View File

@@ -0,0 +1,73 @@
MODULE := engines/mutationofjb
MODULE_OBJS := \
commands/additemcommand.o \
commands/bitmapvisibilitycommand.o \
commands/callmacrocommand.o \
commands/camefromcommand.o \
commands/changecommand.o \
commands/command.o \
commands/conditionalcommand.o \
commands/definestructcommand.o \
commands/endblockcommand.o \
commands/gotocommand.o \
commands/ifcommand.o \
commands/ifitemcommand.o \
commands/ifpiggycommand.o \
commands/labelcommand.o \
commands/loadplayercommand.o \
commands/newroomcommand.o \
commands/playanimationcommand.o \
commands/removeallitemscommand.o \
commands/removeitemcommand.o \
commands/renamecommand.o \
commands/saycommand.o \
commands/seqcommand.o \
commands/setcolorcommand.o \
commands/setobjectframecommand.o \
commands/specialshowcommand.o \
commands/switchpartcommand.o \
commands/talkcommand.o \
commands/randomcommand.o \
tasks/conversationtask.o \
tasks/objectanimationtask.o \
tasks/saytask.o \
tasks/sequentialtask.o \
tasks/taskmanager.o \
widgets/buttonwidget.o \
widgets/conversationwidget.o \
widgets/gamewidget.o \
widgets/imagewidget.o \
widgets/inventorywidget.o \
widgets/labelwidget.o \
widgets/widget.o \
animationdecoder.o \
assets.o \
conversationlinelist.o \
debug.o \
encryptedfile.o \
font.o \
game.o \
gamedata.o \
gamescreen.o \
guiscreen.o \
hardcodedstrings.o \
inventory.o \
inventoryitemdefinitionlist.o \
metaengine.o \
mutationofjb.o \
room.o \
script.o \
timer.o \
util.o
# This module can be built as a plugin
ifeq ($(ENABLE_MUTATIONOFJB), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

View File

@@ -0,0 +1,240 @@
/* 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.h"
#include "common/error.h"
#include "common/system.h"
#include "common/events.h"
#include "common/fs.h"
#include "common/savefile.h"
#include "graphics/screen.h"
#include "graphics/cursorman.h"
#include "engines/util.h"
#include "mutationofjb/mutationofjb.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/gamescreen.h"
#include "mutationofjb/debug.h"
#include "mutationofjb/room.h"
namespace MutationOfJB {
MutationOfJBEngine::MutationOfJBEngine(OSystem *syst, const ADGameDescription *gameDesc)
: Engine(syst),
_gameDesc(gameDesc),
_screen(nullptr),
_game(nullptr),
_mapObjectId(0),
_cursorState(CURSOR_IDLE),
_currentScreen(nullptr) {
const Common::FSNode gameDataDir(ConfMan.getPath("path"));
SearchMan.addSubDirectoryMatching(gameDataDir, "data");
}
MutationOfJBEngine::~MutationOfJBEngine() {}
void MutationOfJBEngine::setupCursor() {
const uint8 cursor[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
updateCursorPalette();
CursorMan.disableCursorPalette(true);
CursorMan.pushCursor(cursor, 15, 15, 7, 7, 0);
CursorMan.showMouse(true);
}
void MutationOfJBEngine::updateCursorPalette() {
if (_cursorState == CURSOR_OFF) {
return;
}
const uint8 white[] = {0xFF, 0xFF, 0xFF};
const uint8 blue[] = {0x00, 0xFF, 0xC3};
_screen->setPalette(_cursorState == CURSOR_ACTIVE ? blue : white, 0xFF, 1);
}
Graphics::Screen *MutationOfJBEngine::getScreen() const {
return _screen;
}
Game &MutationOfJBEngine::getGame() {
return *_game;
}
void MutationOfJBEngine::setCursorState(CursorState cursorState) {
if (_cursorState == cursorState) {
return;
}
_cursorState = cursorState;
updateCursorPalette();
}
bool MutationOfJBEngine::hasFeature(Engine::EngineFeature f) const {
if (f == kSupportsLoadingDuringRuntime || f == kSupportsSavingDuringRuntime) {
return true;
}
return false;
}
bool MutationOfJBEngine::canLoadGameStateCurrently(Common::U32String *msg) {
return _game->loadSaveAllowed();
}
Common::Error MutationOfJBEngine::loadGameState(int slot) {
const Common::String saveName = getSaveStateName(slot);
Common::InSaveFile *const saveFile = g_system->getSavefileManager()->openForLoading(saveName);
if (!saveFile)
return Common::kReadingFailed;
Common::Serializer sz(saveFile, nullptr);
SaveHeader saveHdr;
saveHdr.sync(sz);
_game->getGameData().saveLoadWithSerializer(sz);
delete saveFile;
_game->changeScene(_game->getGameData()._currentScene, _game->getGameData()._partB);
_game->getGameScreen().markDirty();
return Common::kNoError;
}
bool MutationOfJBEngine::canSaveGameStateCurrently(Common::U32String *msg) {
return _game->loadSaveAllowed();
}
Common::Error MutationOfJBEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
Common::OutSaveFile *const saveFile = g_system->getSavefileManager()->openForSaving(
getSaveStateName(slot));
if (!saveFile)
return Common::kWritingFailed;
Common::Serializer sz(nullptr, saveFile);
SaveHeader saveHdr;
saveHdr._description = desc;
saveHdr.sync(sz);
_game->getGameData().saveLoadWithSerializer(sz);
saveFile->finalize();
delete saveFile;
return Common::kNoError;
}
const ADGameDescription *MutationOfJBEngine::getGameDescription() const {
return _gameDesc;
}
Common::Error MutationOfJBEngine::run() {
initGraphics(320, 200);
setDebugger(new Console(this));
_screen = new Graphics::Screen();
_game = new Game(this);
_currentScreen = &_game->getGameScreen();
setupCursor();
if (ConfMan.hasKey("save_slot")) {
const Common::Error err = loadGameState(ConfMan.getInt("save_slot"));
if (err.getCode() != Common::kNoError)
return err;
} else {
_game->changeScene(13, false); // Initial scene.
}
while (!shouldQuit()) {
Common::Event event;
while (_eventMan->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_KEYDOWN: {
if (event.kbd.keycode == Common::KEYCODE_F5 && event.kbd.hasFlags(0)) {
openMainMenuDialog();
}
break;
}
default:
break;
}
if (_currentScreen)
_currentScreen->handleEvent(event);
}
_game->update();
if (_currentScreen)
_currentScreen->update();
_system->delayMillis(10);
_screen->update();
}
return Common::kNoError;
}
bool SaveHeader::sync(Common::Serializer &sz) {
const uint32 SAVE_MAGIC_NUMBER = MKTAG('M', 'O', 'J', 'B');
const uint32 SAVE_FILE_VERSION = 1;
if (sz.isLoading()) {
uint32 magic = 0;
sz.syncAsUint32BE(magic);
if (magic != SAVE_MAGIC_NUMBER) {
warning("Invalid save");
return false;
}
} else {
uint32 magic = SAVE_MAGIC_NUMBER;
sz.syncAsUint32BE(magic);
}
sz.syncVersion(SAVE_FILE_VERSION);
sz.syncString(_description);
return true;
}
}

View File

@@ -0,0 +1,91 @@
/* 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 MUTATIONOFJB_MUTATIONOFJB_H
#define MUTATIONOFJB_MUTATIONOFJB_H
#include "engines/engine.h"
#include "mutationofjb/script.h"
struct ADGameDescription;
namespace Common {
struct Event;
class Serializer;
}
namespace Graphics {
class Screen;
}
namespace MutationOfJB {
class Console;
class Game;
class GuiScreen;
struct SaveHeader {
bool sync(Common::Serializer &sz);
Common::String _description;
};
class MutationOfJBEngine : public Engine {
public:
enum CursorState {
CURSOR_OFF,
CURSOR_IDLE,
CURSOR_ACTIVE
};
MutationOfJBEngine(OSystem *syst, const ADGameDescription *gameDesc);
~MutationOfJBEngine() override;
Common::Error run() override;
Graphics::Screen *getScreen() const;
Game &getGame();
void setCursorState(CursorState cursorState);
bool hasFeature(EngineFeature f) const override;
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
Common::Error loadGameState(int slot) override;
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
const ADGameDescription *getGameDescription() const;
private:
bool loadGameData(bool partB);
void setupCursor();
void updateCursorPalette();
const ADGameDescription *_gameDesc;
Graphics::Screen *_screen;
Game *_game;
uint8 _mapObjectId;
CursorState _cursorState;
GuiScreen *_currentScreen;
};
}
#endif

View File

@@ -0,0 +1,244 @@
/* 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 "mutationofjb/room.h"
#include "mutationofjb/animationdecoder.h"
#include "mutationofjb/encryptedfile.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/util.h"
#include "common/rect.h"
#include "common/str.h"
#include "graphics/screen.h"
namespace MutationOfJB {
enum {
GAME_AREA_WIDTH = 320,
GAME_AREA_HEIGHT = 139
};
class RoomAnimationDecoderCallback : public AnimationDecoderCallback {
public:
RoomAnimationDecoderCallback(Room &room) : _room(room) {}
void onFrame(int frameNo, Graphics::Surface &surface) override;
void onPaletteUpdated(byte palette[PALETTE_SIZE]) override;
private:
Room &_room;
};
void RoomAnimationDecoderCallback::onPaletteUpdated(byte palette[PALETTE_SIZE]) {
_room._screen->setPalette(palette, 0x00, 0xC0); // Load only 0xC0 colors.
}
void RoomAnimationDecoderCallback::onFrame(int frameNo, Graphics::Surface &surface) {
if (frameNo == 0) {
Common::Rect rect(0, 0, GAME_AREA_WIDTH, GAME_AREA_HEIGHT);
if (_room._game->isCurrentSceneMap()) {
rect = Common::Rect(0, 0, 320, 200);
} else {
_room._background.blitFrom(surface, rect, Common::Point(0, 0));
}
_room._screen->blitFrom(surface, rect, Common::Point(0, 0));
}
const int frameNo1 = frameNo + 1;
Scene *scene = _room._game->getGameData().getCurrentScene();
if (scene) {
const uint8 noObjects = scene->getNoObjects();
for (int i = 0; i < noObjects; ++i) {
Object &object = scene->_objects[i];
const uint16 startFrame = (object._roomFrameMSB << 8) + object._roomFrameLSB;
if (frameNo1 >= startFrame && frameNo1 < startFrame + object._numFrames) {
const int x = object._x;
const int y = object._y;
const int w = (object._width + 3) / 4 * 4; // Original code uses this to round up width to a multiple of 4.
const int h = object._height;
Common::Rect rect(x, y, x + w, y + h);
const Graphics::Surface sharedSurface = surface.getSubArea(rect);
Graphics::Surface outSurface;
outSurface.copyFrom(sharedSurface);
_room._surfaces[_room._objectsStart[i] + frameNo1 - startFrame] = outSurface;
}
}
}
}
Room::Room(Game *game, Graphics::Screen *screen) : _game(game), _screen(screen), _background(GAME_AREA_WIDTH, GAME_AREA_HEIGHT) {}
bool Room::load(uint8 roomNumber, bool roomB) {
_objectsStart.clear();
_surfaces.clear(); // TODO: Fix memory leak.
Scene *const scene = _game->getGameData().getCurrentScene();
if (scene) {
const uint8 noObjects = scene->getNoObjects();
for (int i = 0; i < noObjects; ++i) {
uint8 firstIndex = 0;
if (i != 0) {
firstIndex = _objectsStart[i - 1] + scene->_objects[i - 1]._numFrames;
}
_objectsStart.push_back(firstIndex);
uint8 numAnims = scene->_objects[i]._numFrames;
while (numAnims--) {
_surfaces.push_back(Graphics::Surface());
}
}
}
const Common::Path fileName(Common::String::format("room%d%s.dat", roomNumber, roomB ? "b" : ""));
AnimationDecoder decoder(fileName);
RoomAnimationDecoderCallback callback(*this);
return decoder.decode(&callback);
}
// TODO: Take the threshold value from ATN data.
struct ThresholdBlitOperation {
byte operator()(const byte srcColor, const byte destColor) {
if (destColor <= 0xBF) {
// Within threshold - replace destination with source color.
return srcColor;
}
// Outside of threshold - keep destination color.
return destColor;
}
};
void Room::drawObjectAnimation(uint8 objectId, int animOffset) {
Scene *const scene = _game->getGameData().getCurrentScene();
if (!scene) {
return;
}
Object *const object = scene->getObject(objectId);
if (!object) {
return;
}
const int startFrame = _objectsStart[objectId - 1];
const int animFrame = startFrame + animOffset;
blit_if(_surfaces[animFrame], *_screen, Common::Point(object->_x, object->_y), ThresholdBlitOperation());
if (!_game->isCurrentSceneMap())
blit_if(_surfaces[animFrame], _background, Common::Point(object->_x, object->_y), ThresholdBlitOperation());
}
void Room::drawObject(uint8 objectId, uint8 overrideFrame) {
Scene *const currentScene = _game->getGameData().getCurrentScene();
Object *const object = currentScene->getObject(objectId);
drawObjectAnimation(objectId, (overrideFrame ? overrideFrame : object->_currentFrame) - _objectsStart[objectId - 1] - 1);
}
void Room::drawBitmap(uint8 bitmapId) {
GameData &gameData = _game->getGameData();
Scene *const scene = gameData.getCurrentScene();
if (!scene) {
return;
}
Bitmap *const bitmap = scene->getBitmap(bitmapId);
if (!bitmap) {
return;
}
Common::Rect bitmapArea(bitmap->_x1, bitmap->_y1, bitmap->_x2 + 1, bitmap->_y2 + 1);
drawFrames(bitmap->_roomFrame - 1, bitmap->_roomFrame - 1, bitmapArea, 0xC0);
}
void Room::drawStatic(Static *const stat) {
if (!stat || !stat->allowsImplicitPickup()) {
return;
}
const uint8 frame = stat->_active ? 1 : 2; // Hardcoded values. Active is taken from frame 1 and inactive from frame 2.
const Common::Rect staticArea(stat->_x, stat->_y, stat->_x + stat->_width, stat->_y + stat->_height);
drawFrames(frame, frame, staticArea, 0xC0); // Hardcoded threshold.
}
void Room::drawFrames(int fromFrame, int toFrame, const Common::Rect &area, uint8 threshold) {
GameData &gameData = _game->getGameData();
Scene *const scene = gameData.getCurrentScene();
if (!scene) {
return;
}
const Common::Path fileName(Common::String::format("room%d%s.dat", gameData._currentScene, gameData._partB ? "b" : ""));
{
AnimationDecoder decoder(fileName, *_screen);
decoder.setPartialMode(fromFrame, toFrame, area, threshold);
decoder.decode(nullptr);
if (!area.isEmpty())
_screen->getSubArea(area); // Add dirty rect.
else
_screen->makeAllDirty();
}
if (!_game->isCurrentSceneMap()) {
AnimationDecoder decoder(fileName, _background);
decoder.setPartialMode(fromFrame, toFrame, area, threshold);
decoder.decode(nullptr);
}
}
void Room::initialDraw() {
Scene *const currentScene = _game->getGameData().getCurrentScene();
for (uint8 i = 0; i < currentScene->getNoStatics(); ++i) {
Static *const stat = currentScene->getStatic(i + 1);
if (stat->_active && stat->allowsImplicitPickup()) {
drawStatic(stat);
}
}
for (uint8 i = 0; i < currentScene->getNoObjects(); ++i) {
Object *const obj = currentScene->getObject(i + 1);
if (obj->_active) {
drawObjectAnimation(i + 1, obj->_currentFrame - _objectsStart[i] - 1);
}
}
for (uint8 i = 0; i < currentScene->getNoBitmaps(); ++i) {
Bitmap *const bitmap = currentScene->getBitmap(i + 1);
if (bitmap->_isVisible && bitmap->_roomFrame > 0) {
drawBitmap(i + 1);
}
}
}
void Room::redraw(bool useBackgroundBuffer) {
if (useBackgroundBuffer && !_game->isCurrentSceneMap()) {
Common::Rect rect(0, 0, GAME_AREA_WIDTH, GAME_AREA_HEIGHT);
_screen->blitFrom(_background.rawSurface(), rect, Common::Point(0, 0));
} else {
initialDraw();
}
}
}

View File

@@ -0,0 +1,79 @@
/* 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 MUTATIONOFJB_ROOM_H
#define MUTATIONOFJB_ROOM_H
#include "common/scummsys.h"
#include "common/array.h"
#include "graphics/surface.h"
#include "graphics/managed_surface.h"
namespace Graphics {
class Screen;
}
namespace MutationOfJB {
class EncryptedFile;
class Game;
struct Static;
class Room {
public:
friend class RoomAnimationDecoderCallback;
friend class GuiAnimationDecoderCallback;
Room(Game *game, Graphics::Screen *screen);
bool load(uint8 roomNumber, bool roomB);
void drawObjectAnimation(uint8 objectId, int animOffset);
/**
* Draws an object.
* By default, object's current frame is used, but that can be overridden.
*
* @param objectId ID of object to draw.
* @param overrideFrame Optional frame override.
*/
void drawObject(uint8 objectId, uint8 overrideFrame = 0);
void drawBitmap(uint8 bitmapId);
/**
* Draws a static.
* Only statics that allow implicit pickup are drawable.
*
* @param stat Static.
*/
void drawStatic(Static *stat);
void drawFrames(int fromFrame, int toFrame, const Common::Rect &area = Common::Rect(), uint8 threshold = 0xFF);
void initialDraw();
void redraw(bool useBackgroundBuffer = true);
private:
Game *_game;
Graphics::Screen *_screen;
Graphics::ManagedSurface _background;
Common::Array<Graphics::Surface> _surfaces;
Common::Array<int> _objectsStart;
};
}
#endif

View File

@@ -0,0 +1,331 @@
/* 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 "mutationofjb/script.h"
#include "common/hashmap.h"
#include "common/hash-str.h"
#include "common/stream.h"
#include "common/debug.h"
#include "mutationofjb/commands/command.h"
#include "mutationofjb/commands/ifcommand.h"
#include "mutationofjb/commands/ifitemcommand.h"
#include "mutationofjb/commands/ifpiggycommand.h"
#include "mutationofjb/commands/endblockcommand.h"
#include "mutationofjb/commands/changecommand.h"
#include "mutationofjb/commands/saycommand.h"
#include "mutationofjb/commands/additemcommand.h"
#include "mutationofjb/commands/removeitemcommand.h"
#include "mutationofjb/commands/removeallitemscommand.h"
#include "mutationofjb/commands/labelcommand.h"
#include "mutationofjb/commands/gotocommand.h"
#include "mutationofjb/commands/camefromcommand.h"
#include "mutationofjb/commands/callmacrocommand.h"
#include "mutationofjb/commands/newroomcommand.h"
#include "mutationofjb/commands/renamecommand.h"
#include "mutationofjb/commands/definestructcommand.h"
#include "mutationofjb/commands/talkcommand.h"
#include "mutationofjb/commands/randomcommand.h"
#include "mutationofjb/commands/setcolorcommand.h"
#include "mutationofjb/commands/specialshowcommand.h"
#include "mutationofjb/commands/switchpartcommand.h"
#include "mutationofjb/commands/loadplayercommand.h"
#include "mutationofjb/commands/bitmapvisibilitycommand.h"
#include "mutationofjb/commands/playanimationcommand.h"
#include "mutationofjb/commands/setobjectframecommand.h"
#include "mutationofjb/game.h"
namespace MutationOfJB {
static CommandParser **getParsers() {
static CommandParser *parsers[] = {
new IfPiggyCommandParser,
new IfItemCommandParser,
new IfCommandParser,
new CameFromCommandParser,
new CallMacroCommandParser,
new EndBlockCommandParser,
new ChangeDoorCommandParser,
new ChangeObjectCommandParser,
new ChangeStaticCommandParser,
new ChangeSceneCommandParser,
new DefineStructCommandParser,
new SayCommandParser,
new TalkCommandParser,
new AddItemCommandParser,
new RemoveItemCommandParser,
new RemoveAllItemsCommandParser,
new RenameCommandParser,
new NewRoomCommandParser,
new GotoCommandParser,
new LabelCommandParser,
new RandomCommandParser,
new RandomBlockStartParser,
new SetColorCommandParser,
new SpecialShowCommandParser,
new SwitchPartCommandParser,
new LoadPlayerCommandParser,
new BitmapVisibilityCommandParser,
new PlayAnimationCommandParser,
new SetObjectFrameCommandParser,
nullptr
};
return parsers;
}
ScriptParseContext::ScriptParseContext(Common::SeekableReadStream &stream) :
_stream(stream),
_currentCommand(nullptr),
_lastCommand(nullptr),
_pendingRandomCommand(nullptr) {}
bool ScriptParseContext::readLine(Common::String &line) {
do {
Common::String str = _stream.readLine();
if (str.empty())
continue;
if (str[0] != '.') {
line = str;
if (line[0] == '*') {
line.deleteChar(0);
}
return true;
}
} while (!_stream.eos());
return false;
}
void ScriptParseContext::addConditionalCommand(ConditionalCommand *command, char tag, bool firstHash) {
ConditionalCommandInfo cmi = {command, tag, firstHash};
_pendingCondCommands.push_back(cmi);
}
void ScriptExecutionContext::pushReturnCommand(Command *cmd) {
_callStack.push(cmd);
}
Command *ScriptExecutionContext::popReturnCommand() {
if (_callStack.empty()) {
return nullptr;
}
return _callStack.pop();
}
Game &ScriptExecutionContext::getGame() {
return _game;
}
GameData &ScriptExecutionContext::getGameData() {
return _game.getGameData();
}
void ScriptExecutionContext::clear() {
_callStack.clear();
}
Command::ExecuteResult ScriptExecutionContext::runActiveCommand() {
while (_activeCommand) {
const Command::ExecuteResult result = _activeCommand->execute(*this);
if (result == Command::Finished) {
_activeCommand = _activeCommand->next();
} else {
return result;
}
}
return Command::Finished;
}
Command::ExecuteResult ScriptExecutionContext::startCommand(Command *cmd) {
if (_activeCommand) {
warning("Trying to start command while another one is running.");
return Command::Finished;
}
getGameData()._color = WHITE; // The original game resets the color to WHITE beforing running script sections.
clear();
_activeCommand = cmd;
return runActiveCommand();
}
Command::ExecuteResult ScriptExecutionContext::startStartupSection() {
Script *localScript = _localScriptOverride ? _localScriptOverride : _game.getLocalScript();
if (localScript) {
Command *const startupCmd = localScript->getStartup(_game.getGameData().getCurrentScene()->_startup);
if (startupCmd) {
return startCommand(startupCmd);
}
}
return Command::Finished;
}
Command *ScriptExecutionContext::getMacro(const Common::String &name) const {
Command *cmd = nullptr;
Script *const localScript = _localScriptOverride ? _localScriptOverride : _game.getLocalScript();
Script *const globalScript = _game.getGlobalScript();
if (localScript) {
cmd = localScript->getMacro(name);
}
if (!cmd && globalScript) {
cmd = globalScript->getMacro(name);
}
return cmd;
}
Command *ScriptExecutionContext::getExtra(const Common::String &name) const {
Command *cmd = nullptr;
Script *const localScript = _localScriptOverride ? _localScriptOverride : _game.getLocalScript();
Script *const globalScript = _game.getGlobalScript();
if (localScript) {
cmd = localScript->getExtra(name);
}
if (!cmd && globalScript) {
cmd = globalScript->getExtra(name);
}
return cmd;
}
bool ScriptExecutionContext::isCommandRunning() const {
return _activeCommand;
}
bool Script::loadFromStream(Common::SeekableReadStream &stream) {
destroy();
CommandParser **parsers = getParsers();
ScriptParseContext parseCtx(stream);
Common::String line;
Command *lastCmd = nullptr;
CommandParser *lastParser = nullptr;
while (parseCtx.readLine(line)) {
Command *currentCmd = nullptr;
CommandParser *currentParser = nullptr;
for (CommandParser **parser = parsers; *parser; ++parser) {
if ((*parser)->parse(line, parseCtx, currentCmd)) {
currentParser = *parser;
break;
}
}
if (!currentParser) {
continue;
}
if (lastParser) {
lastParser->transition(parseCtx, lastCmd, currentCmd, currentParser);
}
if (currentCmd) {
_allCommands.push_back(currentCmd);
}
lastCmd = currentCmd;
lastParser = currentParser;
}
for (CommandParser **parser = parsers; *parser; ++parser) {
(*parser)->finish(parseCtx);
}
for (ActionInfos::iterator it = parseCtx._actionInfos.begin(); it != parseCtx._actionInfos.end(); ++it) {
_actionInfos[it->_action].push_back(*it);
}
_macros = parseCtx._macros;
_startups = parseCtx._startups;
_extras = parseCtx._extras;
return true;
}
void Script::destroy() {
for (Commands::iterator it = _allCommands.begin(); it != _allCommands.end(); ++it) {
delete *it;
}
_allCommands.clear();
}
Script::~Script() {
destroy();
}
const ActionInfos &Script::getActionInfos(ActionInfo::Action action) {
return _actionInfos[action];
}
const Commands &Script::getAllCommands() const {
return _allCommands;
}
const Macros &Script::getMacros() const {
return _macros;
}
Command *Script::getMacro(const Common::String &name) const {
Macros::const_iterator it = _macros.find(name);
if (it == _macros.end()) {
return nullptr;
}
return it->_value;
}
const Startups &Script::getStartups() const {
return _startups;
}
Command *Script::getStartup(uint8 startupId) const {
Startups::const_iterator it = _startups.find(startupId);
if (it == _startups.end()) {
return nullptr;
}
return it->_value;
}
Command *Script::getExtra(const Common::String &name) const {
Extras::const_iterator it = _extras.find(name);
if (it == _extras.end()) {
return nullptr;
}
return it->_value;
}
}

View File

@@ -0,0 +1,154 @@
/* 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 MUTATIONOFJB_SCRIPT_H
#define MUTATIONOFJB_SCRIPT_H
#include "mutationofjb/commands/command.h"
#include "common/array.h"
#include "common/hashmap.h"
#include "common/hash-str.h"
#include "common/stack.h"
namespace Common {
class SeekableReadStream;
class String;
}
namespace MutationOfJB {
class Command;
class LabelCommand;
class Game;
class GotoCommand;
class ConditionalCommand;
class Script;
class RandomCommand;
struct GameData;
typedef Common::Array<Command *> Commands;
struct ActionInfo {
enum Action {
Look,
Walk,
Talk,
Use,
PickUp
};
Action _action;
Common::String _entity1Name;
Common::String _entity2Name;
bool _walkTo;
Command *_command;
};
typedef Common::Array<ActionInfo> ActionInfos;
typedef Common::Array<GotoCommand *> GotoCommands;
typedef Common::HashMap<Common::String, Command *> Macros;
typedef Common::HashMap<uint8, Command *> Startups;
typedef Common::HashMap<Common::String, Command *> Extras;
class ScriptParseContext {
public:
ScriptParseContext(Common::SeekableReadStream &stream);
bool readLine(Common::String &line);
void addConditionalCommand(ConditionalCommand *command, char tag, bool firstHash);
void addLookSection(const Common::String &item, bool walkTo);
Common::SeekableReadStream &_stream;
Command *_currentCommand;
Command *_lastCommand;
struct ConditionalCommandInfo {
ConditionalCommand *_command;
char _tag;
bool _firstHash;
};
typedef Common::Array<ConditionalCommandInfo> ConditionalCommandInfos;
ConditionalCommandInfos _pendingCondCommands;
typedef Common::HashMap<Common::String, LabelCommand *> LabelMap;
LabelMap _labels;
typedef Common::HashMap<Common::String, GotoCommands> PendingGotoMap;
PendingGotoMap _pendingGotos;
RandomCommand *_pendingRandomCommand;
ActionInfos _actionInfos;
Macros _macros;
Startups _startups;
Extras _extras;
private:
};
class ScriptExecutionContext {
public:
ScriptExecutionContext(Game &game, Script *localScriptOverride = nullptr) : _game(game), _activeCommand(nullptr), _localScriptOverride(localScriptOverride) {}
void clear();
Command::ExecuteResult runActiveCommand();
Command::ExecuteResult startCommand(Command *cmd);
Command::ExecuteResult startStartupSection();
void pushReturnCommand(Command *);
Command *popReturnCommand();
Game &getGame();
GameData &getGameData();
Command *getMacro(const Common::String &name) const;
Command *getExtra(const Common::String &name) const;
bool isCommandRunning() const;
private:
Game &_game;
Command *_activeCommand;
Common::Stack<Command *> _callStack;
Script *_localScriptOverride;
};
class Script {
public:
bool loadFromStream(Common::SeekableReadStream &stream);
~Script();
const ActionInfos &getActionInfos(ActionInfo::Action action);
const Commands &getAllCommands() const;
const Macros &getMacros() const;
const Startups &getStartups() const;
Command *getMacro(const Common::String &name) const;
Command *getStartup(uint8 startupId) const;
Command *getExtra(const Common::String &name) const;
private:
void destroy();
Commands _allCommands;
ActionInfos _actionInfos[5];
Macros _macros;
Startups _startups;
Extras _extras;
};
}
#endif

View File

@@ -0,0 +1,284 @@
/* 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 "mutationofjb/tasks/conversationtask.h"
#include "mutationofjb/assets.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/gamescreen.h"
#include "mutationofjb/script.h"
#include "mutationofjb/tasks/saytask.h"
#include "mutationofjb/tasks/sequentialtask.h"
#include "mutationofjb/tasks/taskmanager.h"
#include "mutationofjb/util.h"
#include "mutationofjb/widgets/conversationwidget.h"
namespace MutationOfJB {
void ConversationTask::start() {
setState(RUNNING);
Game &game = getTaskManager()->getGame();
game.getGameScreen().showConversationWidget(true);
ConversationWidget &widget = game.getGameScreen().getConversationWidget();
widget.setCallback(this);
_currentGroupIndex = 0;
showChoicesOrPick();
}
void ConversationTask::update() {
if (_sayTask) {
if (_sayTask->getState() == Task::FINISHED) {
_sayTask.reset();
switch (_substate) {
case SAYING_NO_QUESTIONS:
finish();
break;
case SAYING_QUESTION: {
const ConversationLineList &responseList = getTaskManager()->getGame().getAssets().getResponseList();
const ConversationLineList::Line *const line = responseList.getLine(_currentItem->_response);
_substate = SAYING_RESPONSE;
createSayTasks(line);
getTaskManager()->startTask(_sayTask);
break;
}
case SAYING_RESPONSE: {
startExtra();
if (_substate != RUNNING_EXTRA) {
gotoNextGroup();
}
break;
}
default:
break;
}
}
}
if (_innerExecCtx) {
Command::ExecuteResult res = _innerExecCtx->runActiveCommand();
if (res == Command::Finished) {
delete _innerExecCtx;
_innerExecCtx = nullptr;
gotoNextGroup();
}
}
}
void ConversationTask::onChoiceClicked(ConversationWidget *convWidget, int, uint32 data) {
const ConversationInfo::Item &item = getCurrentGroup()[data];
convWidget->clearChoices();
const ConversationLineList &toSayList = getTaskManager()->getGame().getAssets().getToSayList();
const ConversationLineList::Line *line = toSayList.getLine(item._question);
_substate = SAYING_QUESTION;
createSayTasks(line);
getTaskManager()->startTask(_sayTask);
_currentItem = &item;
if (!line->_speeches[0].isRepeating()) {
getTaskManager()->getGame().getGameData().getCurrentScene()->addExhaustedConvItem(_convInfo._context, data + 1, _currentGroupIndex + 1);
}
}
void ConversationTask::showChoicesOrPick() {
Game &game = getTaskManager()->getGame();
GameData &gameData = game.getGameData();
Scene *const scene = gameData.getScene(_sceneId);
if (!scene) {
return;
}
Common::Array<uint32> itemsWithValidQuestions;
Common::Array<uint32> itemsWithValidResponses;
Common::Array<uint32> itemsWithValidNext;
/*
Collect valid questions (not exhausted and not empty).
Collect valid responses (not exhausted and not empty).
If there are at least two visible questions, we show them.
If there is just one visible question, pick it automatically ONLY if this is not the first question in this conversation.
Otherwise we don't start the conversation.
If there are no visible questions, automatically pick the first valid response.
If nothing above applies, don't start the conversation.
*/
const ConversationInfo::ItemGroup &currentGroup = getCurrentGroup();
for (ConversationInfo::ItemGroup::size_type i = 0; i < currentGroup.size(); ++i) {
const ConversationInfo::Item &item = currentGroup[i];
if (scene->isConvItemExhausted(_convInfo._context, static_cast<uint8>(i + 1), static_cast<uint8>(_currentGroupIndex + 1))) {
continue;
}
const uint8 toSay = item._question;
const uint8 response = item._response;
const uint8 next = item._nextGroupIndex;
if (toSay != 0) {
itemsWithValidQuestions.push_back(i);
}
if (response != 0) {
itemsWithValidResponses.push_back(i);
}
if (next != 0) {
itemsWithValidNext.push_back(i);
}
}
if (itemsWithValidQuestions.size() > 1) {
ConversationWidget &widget = game.getGameScreen().getConversationWidget();
const ConversationLineList &toSayList = game.getAssets().getToSayList();
for (Common::Array<uint32>::size_type i = 0; i < itemsWithValidQuestions.size() && i < ConversationWidget::CONVERSATION_MAX_CHOICES; ++i) {
const ConversationInfo::Item &item = currentGroup[itemsWithValidQuestions[i]];
const ConversationLineList::Line *const line = toSayList.getLine(item._question);
const Common::String widgetText = toUpperCP895(line->_speeches[0]._text);
widget.setChoice(static_cast<int>(i), widgetText, itemsWithValidQuestions[i]);
}
_substate = IDLE;
_currentItem = nullptr;
_haveChoices = true;
} else if (itemsWithValidQuestions.size() == 1 && _haveChoices) {
const ConversationLineList &toSayList = game.getAssets().getToSayList();
const ConversationInfo::Item &item = currentGroup[itemsWithValidQuestions.front()];
const ConversationLineList::Line *const line = toSayList.getLine(item._question);
_substate = SAYING_QUESTION;
createSayTasks(line);
getTaskManager()->startTask(_sayTask);
_currentItem = &item;
if (!line->_speeches[0].isRepeating()) {
game.getGameData().getCurrentScene()->addExhaustedConvItem(_convInfo._context, itemsWithValidQuestions.front() + 1, _currentGroupIndex + 1);
}
_haveChoices = true;
} else if (!itemsWithValidResponses.empty() && _haveChoices) {
const ConversationLineList &responseList = game.getAssets().getResponseList();
const ConversationInfo::Item &item = currentGroup[itemsWithValidResponses.front()];
const ConversationLineList::Line *const line = responseList.getLine(item._response);
_substate = SAYING_RESPONSE;
createSayTasks(line);
getTaskManager()->startTask(_sayTask);
_currentItem = &item;
_haveChoices = true;
} else if (!itemsWithValidNext.empty() && _haveChoices) {
_currentGroupIndex = currentGroup[itemsWithValidNext.front()]._nextGroupIndex - 1;
showChoicesOrPick();
} else {
if (_haveChoices) {
finish();
} else {
_sayTask = TaskPtr(new SayTask("Nothing to talk about.", _convInfo._color)); // TODO: This is hardcoded in executable. Load it.
getTaskManager()->startTask(_sayTask);
_substate = SAYING_NO_QUESTIONS;
_currentItem = nullptr;
}
}
}
const ConversationInfo::ItemGroup &ConversationTask::getCurrentGroup() const {
assert(_currentGroupIndex < _convInfo._itemGroups.size());
return _convInfo._itemGroups[_currentGroupIndex];
}
void ConversationTask::finish() {
setState(FINISHED);
Game &game = getTaskManager()->getGame();
game.getGameScreen().showConversationWidget(false);
ConversationWidget &widget = game.getGameScreen().getConversationWidget();
widget.setCallback(nullptr);
}
void ConversationTask::startExtra() {
const ConversationLineList &responseList = getTaskManager()->getGame().getAssets().getResponseList();
const ConversationLineList::Line *const line = responseList.getLine(_currentItem->_response);
if (!line->_extra.empty()) {
_innerExecCtx = new ScriptExecutionContext(getTaskManager()->getGame());
Command *const extraCmd = _innerExecCtx->getExtra(line->_extra);
if (extraCmd) {
Command::ExecuteResult res = _innerExecCtx->startCommand(extraCmd);
if (res == Command::InProgress) {
_substate = RUNNING_EXTRA;
} else {
delete _innerExecCtx;
_innerExecCtx = nullptr;
}
} else {
warning("Extra '%s' not found", line->_extra.c_str());
delete _innerExecCtx;
_innerExecCtx = nullptr;
}
}
}
void ConversationTask::gotoNextGroup() {
if (_currentItem->_nextGroupIndex == 0) {
finish();
} else {
_currentGroupIndex = _currentItem->_nextGroupIndex - 1;
showChoicesOrPick();
}
}
void ConversationTask::createSayTasks(const ConversationLineList::Line *line) {
if (line->_speeches.size() == 1) {
const ConversationLineList::Speech &speech = line->_speeches[0];
_sayTask = TaskPtr(new SayTask(speech._text, getSpeechColor(speech)));
} else {
TaskPtrs tasks;
for (ConversationLineList::Speeches::const_iterator it = line->_speeches.begin(); it != line->_speeches.end(); ++it) {
tasks.push_back(TaskPtr(new SayTask(it->_text, getSpeechColor(*it))));
}
_sayTask = TaskPtr(new SequentialTask(tasks));
}
}
uint8 ConversationTask::getSpeechColor(const ConversationLineList::Speech &speech) {
uint8 color = WHITE;
if (_substate == SAYING_RESPONSE) {
color = _convInfo._color;
if (_mode == TalkCommand::RAY_AND_BUTTLEG_MODE) {
if (speech.isFirstSpeaker()) {
color = GREEN;
} else if (speech.isSecondSpeaker()) {
color = LIGHTBLUE;
}
}
}
return color;
}
}

View File

@@ -0,0 +1,71 @@
/* 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 "mutationofjb/commands/talkcommand.h"
#include "mutationofjb/conversationlinelist.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/tasks/task.h"
#include "mutationofjb/widgets/conversationwidget.h"
namespace MutationOfJB {
class SayTask;
class ScriptExecutionContext;
class ConversationTask : public Task, public ConversationWidgetCallback {
public:
ConversationTask(uint8 sceneId, const ConversationInfo &convInfo, TalkCommand::Mode mode) : _sceneId(sceneId), _convInfo(convInfo), _mode(mode), _currentGroupIndex(0), _currentItem(nullptr), _substate(IDLE), _haveChoices(false), _innerExecCtx(nullptr) {}
~ConversationTask() override {}
void start() override;
void update() override;
void onChoiceClicked(ConversationWidget *, int response, uint32 data) override;
private:
void showChoicesOrPick();
const ConversationInfo::ItemGroup &getCurrentGroup() const;
void finish();
void startExtra();
void gotoNextGroup();
void createSayTasks(const ConversationLineList::Line *line);
uint8 getSpeechColor(const ConversationLineList::Speech &speech);
uint8 _sceneId;
const ConversationInfo &_convInfo;
TalkCommand::Mode _mode;
uint _currentGroupIndex;
const ConversationInfo::Item *_currentItem;
TaskPtr _sayTask;
enum Substate {
IDLE,
SAYING_QUESTION,
SAYING_RESPONSE,
SAYING_NO_QUESTIONS,
RUNNING_EXTRA
};
Substate _substate;
bool _haveChoices;
ScriptExecutionContext *_innerExecCtx;
};
}

View File

@@ -0,0 +1,153 @@
/* 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 "mutationofjb/tasks/objectanimationtask.h"
#include "mutationofjb/tasks/taskmanager.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/room.h"
namespace MutationOfJB {
static const int TICK_MILLIS = 100;
// TODO: Respect currentScene._delay.
ObjectAnimationTask::ObjectAnimationTask() : _timer(TICK_MILLIS) {
}
void ObjectAnimationTask::start() {
setState(RUNNING);
_timer.start();
}
void ObjectAnimationTask::update() {
_timer.update();
if (_timer.isFinished()) {
_timer.start();
updateObjects();
}
}
void ObjectAnimationTask::updateObjects() {
Scene *const scene = getTaskManager()->getGame().getGameData().getCurrentScene();
if (!scene) {
return;
}
for (uint8 i = 1; i <= scene->getNoObjects(); ++i) {
Object *const object = scene->getObject(i);
// Skip if object animation not active.
if (!object->_active)
continue;
// Number of frames must be higher than 1.
if (object->_numFrames <= 1)
continue;
const uint8 currentAnimOffset = object->_currentFrame - object->_firstFrame;
const bool randomized = object->_randomFrame != 0;
const bool belowRandomFrame = currentAnimOffset < (object->_randomFrame - 1);
uint8 maxAnimOffset = object->_numFrames - 1;
if (randomized && belowRandomFrame) {
maxAnimOffset = object->_randomFrame - 2;
}
uint8 nextAnimationOffset = currentAnimOffset + 1;
if (currentAnimOffset == maxAnimOffset) {
if (randomized && object->_jumpChance != 0 && getTaskManager()->getGame().getRandomSource().getRandomNumber(object->_jumpChance) == 0)
nextAnimationOffset = object->_randomFrame - 1;
else
nextAnimationOffset = 0;
}
object->_currentFrame = nextAnimationOffset + object->_firstFrame;
const bool drawObject = handleHardcodedAnimation(object);
if (drawObject) {
getTaskManager()->getGame().getRoom().drawObject(i);
}
}
}
bool ObjectAnimationTask::handleHardcodedAnimation(Object *const object) {
GameData &gameData = getTaskManager()->getGame().getGameData();
Scene *const scene = gameData.getCurrentScene();
const bool carnivalScene = gameData._currentScene == 30 && !gameData._partB;
const bool tavernScene = gameData._currentScene == 8 && gameData._partB;
if (carnivalScene) {
// This alternates between the two burglars' talking animations.
// Each burglar gets to talk for a varying amount of time since
// the switch occurs when his random frame is reached.
if (object->_WX == 1 && object->_currentFrame == 79) {
object->_currentFrame = 68;
object->_active = 0;
scene->getObject(6)->_active = 1;
scene->getObject(7)->_active = 0;
scene->getObject(8)->_active = 1;
return false;
} else if (object->_WX == 2 && object->_currentFrame == 91) {
object->_currentFrame = 80;
object->_active = 0;
scene->getObject(5)->_active = 1;
scene->getObject(7)->_active = 1;
scene->getObject(8)->_active = 0;
return false;
}
// The following makes sure you can't interact with the glass
// while the scientist is drinking from it.
if (scene->getObject(4)->_currentFrame > 52 && scene->getObject(4)->_active) {
scene->getStatic(9)->_active = 0; // disable scientist's glass
} else {
scene->getStatic(9)->_active = 1; // enable scientist's glass
}
if (!scene->getObject(4)->_active) {
scene->getStatic(9)->_active = 0; // disable scientist's glass
}
} else if (tavernScene) {
// Similarly to the carnival burglars, this alternates between
// the talking animations of the two soldiers in the tavern.
//
// At some point the script disables their conversation
// by nulling their _WX registers.
if (object->_WX == 3 && object->_currentFrame == 46) {
object->_currentFrame = 30;
object->_active = 0;
scene->getObject(3)->_active = 1;
return false;
} else if (object->_WX == 4 && object->_currentFrame == 63) {
object->_currentFrame = 47;
object->_active = 0;
scene->getObject(2)->_active = 1;
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,69 @@
/* 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 MUTATIONOFJB_OBJECTANIMATIONTASK_H
#define MUTATIONOFJB_OBJECTANIMATIONTASK_H
#include "mutationofjb/tasks/task.h"
#include "mutationofjb/timer.h"
namespace MutationOfJB {
struct Object;
class ObjectAnimationTask : public Task {
public:
ObjectAnimationTask();
void start() override;
void update() override;
/**
* Advances every object animation in the current scene to the next frame.
*
* Normally the animation restarts after the last object frame. However, some animations have random
* elements to them. If _randomFrame is set, the animation restarts when _randomFrame is reached.
* Additionally, there is a chance with each frame until _randomFrame that the animation may jump
* straight to _randomFrame and continue until the last frame, then wrap around to the first frame.
*
* Randomness is used to introduce variety - e.g. in the starting scene a perched bird occasionally
* spreads its wings.
*/
void updateObjects();
/**
* Nasty, hacky stuff the original game does to make some complex animations
* in the Carnival and Tavern Earthquake scenes possible.
*
* @param object Object to process.
* @return Whether to draw the object. It's important to respect this, otherwise
* some of the hardcoded animations would suffer from graphical glitches.
*/
bool handleHardcodedAnimation(Object *const object);
private:
Timer _timer;
};
}
#endif

View File

@@ -0,0 +1,103 @@
/* 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 "mutationofjb/tasks/saytask.h"
#include "mutationofjb/tasks/taskmanager.h"
#include "mutationofjb/assets.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/room.h"
#include "mutationofjb/util.h"
#include "graphics/managed_surface.h"
#include "graphics/screen.h"
namespace MutationOfJB {
SayTask::SayTask(const Common::String &toSay, uint8 color) : _toSay(toSay), _color(color), _timer(50 * toSay.size()) {}
void SayTask::start() {
Game &game = getTaskManager()->getGame();
if (game.getActiveSayTask()) {
getTaskManager()->stopTask(game.getActiveSayTask());
}
game.setActiveSayTask(getTaskManager()->getTask(this));
setState(RUNNING);
drawSubtitle(_toSay, 160, 0, _color); // TODO: Respect PTALK and LTALK commands.
_timer.start();
}
void SayTask::update() {
_timer.update();
if (_timer.isFinished()) {
finish();
}
}
void SayTask::stop() {
if (getState() == RUNNING) {
finish();
}
}
void SayTask::drawSubtitle(const Common::String &text, int16 talkX, int16 talkY, uint8 color) {
const int MAX_LINE_WIDTH = 250;
const Font &font = getTaskManager()->getGame().getAssets().getSpeechFont();
Common::Array<Common::String> lines;
const int16 actualMaxWidth = font.wordWrapText(text, MAX_LINE_WIDTH, lines);
// Get the x, y coordinates of the top center point of the text's bounding box
// from the (rather strange) talk coordinates coming from scripts.
int16 x = talkX;
int16 y = talkY - (lines.size() - 1) * font.getFontHeight() - 15;
// Clamp to screen edges.
x = CLIP<int16>(x, 3 + actualMaxWidth / 2, 317 - actualMaxWidth / 2);
y = MAX<int16>(y, 3);
// Remember the area occupied by the text.
_boundingBox.left = x - actualMaxWidth / 2;
_boundingBox.top = y;
_boundingBox.setWidth(actualMaxWidth);
_boundingBox.setHeight(lines.size() * font.getFontHeight());
// Draw lines.
for (uint i = 0; i < lines.size(); i++) {
font.drawString(&getTaskManager()->getGame().getScreen(), lines[i], _boundingBox.left, _boundingBox.top + i * font.getFontHeight(), _boundingBox.width(), color, Graphics::kTextAlignCenter);
}
}
void SayTask::finish() {
getTaskManager()->getGame().getRoom().redraw(); // TODO: Only redraw the area occupied by the text.
setState(FINISHED);
Game &game = getTaskManager()->getGame();
if (game.getActiveSayTask().get() == this) {
game.setActiveSayTask(Common::SharedPtr<SayTask>());
}
}
}

View File

@@ -0,0 +1,54 @@
/* 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 MUTATIONOFJB_SAYTASK_H
#define MUTATIONOFJB_SAYTASK_H
#include "mutationofjb/tasks/task.h"
#include "mutationofjb/timer.h"
#include "common/rect.h"
#include "common/str.h"
namespace MutationOfJB {
class SayTask : public Task {
public:
SayTask(const Common::String &toSay, uint8 color);
void start() override;
void update() override;
void stop() override;
private:
void drawSubtitle(const Common::String &text, int16 talkX, int16 talkY, uint8 color);
void finish();
Common::String _toSay;
uint8 _color;
Timer _timer;
Common::Rect _boundingBox;
};
}
#endif

Some files were not shown because too many files have changed in this diff Show More