Initial commit
This commit is contained in:
1459
engines/agds/agds.cpp
Normal file
1459
engines/agds/agds.cpp
Normal file
File diff suppressed because it is too large
Load Diff
365
engines/agds/agds.h
Normal file
365
engines/agds/agds.h
Normal file
@@ -0,0 +1,365 @@
|
||||
/* 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 AGDS_H
|
||||
#define AGDS_H
|
||||
|
||||
#include "agds/database.h"
|
||||
#include "agds/dialog.h"
|
||||
#include "agds/inventory.h"
|
||||
#include "agds/mouseMap.h"
|
||||
#include "agds/processExitCode.h"
|
||||
#include "agds/resourceManager.h"
|
||||
#include "agds/screen.h"
|
||||
#include "agds/soundManager.h"
|
||||
#include "agds/textLayout.h"
|
||||
#include "common/array.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/ptr.h"
|
||||
#include "common/random.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "engines/advancedDetector.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "video/flic_decoder.h"
|
||||
|
||||
/**
|
||||
* This is the namespace of the AGDS engine.
|
||||
*
|
||||
* Status of this engine: In Progress
|
||||
*
|
||||
* Games using this engine:
|
||||
* - Black Mirror (Windows)
|
||||
*/
|
||||
|
||||
namespace Graphics {
|
||||
class Font;
|
||||
class ManagedSurface;
|
||||
} // namespace Graphics
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
class Animation;
|
||||
using AnimationPtr = Common::SharedPtr<Animation>;
|
||||
class Character;
|
||||
class Font;
|
||||
class Object;
|
||||
using ObjectPtr = Common::SharedPtr<Object>;
|
||||
struct ObjectPatch;
|
||||
using ObjectPatchPtr = Common::SharedPtr<ObjectPatch>;
|
||||
struct Patch;
|
||||
using PatchPtr = Common::SharedPtr<Patch>;
|
||||
class Process;
|
||||
using ProcessPtr = Common::SharedPtr<Process>;
|
||||
struct Region;
|
||||
using RegionPtr = Common::SharedPtr<Region>;
|
||||
struct MouseRegion;
|
||||
|
||||
class MJPGPlayer;
|
||||
class Screen;
|
||||
class SystemVariable;
|
||||
class Console;
|
||||
|
||||
class AGDSEngine : public Engine {
|
||||
friend class Process;
|
||||
using ProcessListType = Common::Array<ProcessPtr>;
|
||||
static constexpr uint MaxProcesses = 100;
|
||||
|
||||
public:
|
||||
struct Color {
|
||||
uint8 r = 0;
|
||||
uint8 g = 0;
|
||||
uint8 b = 0;
|
||||
|
||||
void FromString(const Common::String &rgb);
|
||||
Common::String ToString() const;
|
||||
|
||||
uint32 map(const Graphics::PixelFormat &format) const;
|
||||
};
|
||||
|
||||
AGDSEngine(OSystem *syst, const ADGameDescription *gameDesc);
|
||||
AGDSEngine(const AGDSEngine &) = delete;
|
||||
~AGDSEngine();
|
||||
|
||||
Common::Error run() override;
|
||||
|
||||
void setGlobal(const Common::String &name, int value);
|
||||
int getGlobal(const Common::String &name) const;
|
||||
bool hasGlobal(const Common::String &name) const {
|
||||
return _globals.contains(name);
|
||||
}
|
||||
|
||||
private:
|
||||
void addSystemVar(const Common::String &name, SystemVariable *var);
|
||||
bool initGraphics(int w, int h);
|
||||
bool load();
|
||||
void runProcesses();
|
||||
void tick();
|
||||
void fadeAndReactivate();
|
||||
|
||||
public:
|
||||
bool hasFeature(EngineFeature f) const override;
|
||||
Common::Error loadGameState(int slot) override;
|
||||
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
|
||||
bool canLoadGameStateCurrently(Common::U32String *msg) override { return true; }
|
||||
bool canSaveGameStateCurrently(Common::U32String *msg) override { return false; }
|
||||
|
||||
ObjectPtr loadObject(const Common::String &name, const Common::String &prototype = Common::String(), bool allowInitialise = true);
|
||||
ObjectPtr runObject(const Common::String &name, const Common::String &prototype = Common::String(), bool allowInitialise = true);
|
||||
void runObject(const ObjectPtr &object);
|
||||
void runProcess(const ObjectPtr &object, uint ip = 0);
|
||||
ProcessPtr findProcess(const Common::String &name) const;
|
||||
void reactivate(const Common::String &name, const Common::String &where, bool runNow = false);
|
||||
bool hasActiveProcesses(const Common::String &name) const;
|
||||
void runPendingReactivatedProcesses();
|
||||
|
||||
void resetCurrentScreen();
|
||||
void loadScreen(const Common::String &name, ScreenLoadingType type, bool savePatch = true);
|
||||
void loadNextScreen();
|
||||
void saveScreenPatch();
|
||||
|
||||
RegionPtr loadRegion(const Common::String &name);
|
||||
Common::String loadText(const Common::String &name);
|
||||
|
||||
int appendToSharedStorage(const Common::String &value);
|
||||
const Common::String &getSharedStorage(int id) const;
|
||||
|
||||
bool active() const { return !_mjpgPlayer; }
|
||||
void playFilm(Process &process, const Common::String &video, const Common::String &audio, const Common::String &subtitles);
|
||||
void skipFilm();
|
||||
|
||||
ResourceManager &resourceManager() {
|
||||
return _resourceManager;
|
||||
}
|
||||
|
||||
SoundManager &soundManager() {
|
||||
return _soundManager;
|
||||
}
|
||||
|
||||
Inventory &inventory() {
|
||||
return _inventory;
|
||||
}
|
||||
|
||||
Dialog &dialog() {
|
||||
return _dialog;
|
||||
}
|
||||
|
||||
const ProcessListType &processes() const {
|
||||
return _processes;
|
||||
}
|
||||
|
||||
TextLayout &textLayout() {
|
||||
return _textLayout;
|
||||
}
|
||||
|
||||
Screen *getCurrentScreen() {
|
||||
return _currentScreen.get();
|
||||
}
|
||||
Console *getConsole();
|
||||
|
||||
Common::String &getCurrentScreenName() {
|
||||
return _currentScreenName;
|
||||
}
|
||||
ObjectPtr getCurrentScreenObject(const Common::String &name);
|
||||
|
||||
const Graphics::PixelFormat &pixelFormat() const {
|
||||
return _pixelFormat;
|
||||
}
|
||||
|
||||
Graphics::ManagedSurface *loadPicture(const Common::String &name);
|
||||
Graphics::Surface *createSurface(int w, int h);
|
||||
Graphics::ManagedSurface *convertToTransparent(Graphics::Surface *surface); // destroys surface!
|
||||
|
||||
int loadFromCache(const Common::String &name) const;
|
||||
Graphics::ManagedSurface *loadFromCache(int id) const;
|
||||
int saveToCache(const Common::String &name, Graphics::ManagedSurface *surface);
|
||||
|
||||
void loadFont(int id, const Common::String &name, int gw, int gh);
|
||||
const Graphics::Font *getFont(int id) const;
|
||||
|
||||
AnimationPtr loadAnimation(const Common::String &name);
|
||||
AnimationPtr loadMouseCursor(const Common::String &name);
|
||||
AnimationPtr findAnimationByPhaseVar(const Common::String &phaseVar);
|
||||
void loadCharacter(const Common::String &id, const Common::String &name, const Common::String &object);
|
||||
Character *getCharacter(const Common::String &name) {
|
||||
return _currentCharacterName == name ? _currentCharacter.get() : nullptr;
|
||||
}
|
||||
Character *currentCharacter() const {
|
||||
return _currentCharacter.get();
|
||||
}
|
||||
Character *jokes() const {
|
||||
return _jokes.get();
|
||||
}
|
||||
|
||||
void loadDefaultMouseCursor(const Common::String &name) {
|
||||
_defaultMouseCursorName = name;
|
||||
_defaultMouseCursor = loadMouseCursor(name);
|
||||
}
|
||||
|
||||
void changeMouseArea(int id, int enabled);
|
||||
|
||||
void enableUser(bool enabled) {
|
||||
_userEnabled = enabled;
|
||||
}
|
||||
void enableSystemUser(bool enabled) {
|
||||
_systemUserEnabled = enabled;
|
||||
}
|
||||
|
||||
bool userEnabled() const {
|
||||
return _userEnabled && _systemUserEnabled && !_mjpgPlayer;
|
||||
}
|
||||
|
||||
void newGame();
|
||||
void initSystemVariables();
|
||||
SystemVariable *getSystemVariable(const Common::String &name);
|
||||
|
||||
void setNextScreenName(const Common::String &nextScreenName, ScreenLoadingType type);
|
||||
void returnToPreviousScreen();
|
||||
|
||||
void tickInventory();
|
||||
|
||||
void playSoundSync(int syncSoundId) {
|
||||
_syncSoundId = syncSoundId;
|
||||
}
|
||||
|
||||
void setAmbientSoundId(int id) {
|
||||
stopAmbientSound();
|
||||
_ambientSoundId = id;
|
||||
}
|
||||
|
||||
void tell(Process &process, const Common::String ®ion, Common::String text, Common::String sound, bool npc);
|
||||
|
||||
bool fastMode() const {
|
||||
return _fastMode;
|
||||
}
|
||||
|
||||
Common::Point mousePosition() const {
|
||||
return _mouse;
|
||||
}
|
||||
|
||||
void currentInventoryObject(const ObjectPtr &object);
|
||||
void resetCurrentInventoryObject();
|
||||
ObjectPtr popCurrentInventoryObject();
|
||||
const ObjectPtr ¤tInventoryObject() const {
|
||||
return _currentInventoryObject;
|
||||
}
|
||||
|
||||
bool showHints() const {
|
||||
return _hintMode;
|
||||
}
|
||||
|
||||
PatchPtr getPatch(const Common::String &screenName) const;
|
||||
PatchPtr createPatch(const Common::String &screenName);
|
||||
ObjectPatchPtr getObjectPatch(const Common::String &screenName) const;
|
||||
ObjectPatchPtr createObjectPatch(const Common::String &screenName);
|
||||
|
||||
void shadowIntensity(int intensity) {
|
||||
_shadowIntensity = intensity;
|
||||
}
|
||||
int getRandomNumber(int max);
|
||||
|
||||
void curtain(const Common::String &process, int screen, int sound, int music, bool updateGlobals);
|
||||
bool activeCurtain() const {
|
||||
return _curtainTimer >= 0;
|
||||
}
|
||||
|
||||
bool v2() const;
|
||||
int version() const;
|
||||
|
||||
private:
|
||||
void stopAmbientSound();
|
||||
void loadPatches(Common::SeekableReadStream &file, Database &db);
|
||||
|
||||
using PictureCacheType = Common::HashMap<int, Common::ScopedPtr<Graphics::ManagedSurface>>;
|
||||
using PictureCacheLookup = Common::HashMap<Common::String, int, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo>;
|
||||
using SystemVariablesListType = Common::Array<Common::String>;
|
||||
using SystemVariablesType = Common::HashMap<Common::String, SystemVariable *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo>;
|
||||
using GlobalsType = Common::HashMap<Common::String, int, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo>;
|
||||
using FontsType = Common::HashMap<int, Common::ScopedPtr<Graphics::Font>>;
|
||||
using PatchesType = Common::HashMap<Common::String, PatchPtr, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo>;
|
||||
using ObjectPatchesType = Common::HashMap<Common::String, ObjectPatchPtr, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo>;
|
||||
using PatchDatabase = Common::HashMap<Common::String, Common::Array<uint8>>;
|
||||
|
||||
const ADGameDescription *_gameDescription;
|
||||
ResourceManager _resourceManager;
|
||||
SoundManager _soundManager;
|
||||
Database _data;
|
||||
PictureCacheType _pictureCache;
|
||||
PictureCacheLookup _pictureCacheLookup;
|
||||
int _pictureCacheId;
|
||||
FontsType _fonts;
|
||||
ProcessListType _processes;
|
||||
ProcessListType _pendingReactivatedProcesses;
|
||||
PatchesType _patches;
|
||||
ObjectPatchesType _objectPatches;
|
||||
int _sharedStorageIndex;
|
||||
Common::String _sharedStorage[10];
|
||||
GlobalsType _globals;
|
||||
SystemVariablesListType _systemVarList;
|
||||
SystemVariablesType _systemVars;
|
||||
Graphics::PixelFormat _pixelFormat;
|
||||
Color _colorKey;
|
||||
Color _minShadowColor;
|
||||
Color _maxShadowColor;
|
||||
int _shadowIntensity;
|
||||
Common::ScopedPtr<MJPGPlayer> _mjpgPlayer;
|
||||
uint32 _filmStarted;
|
||||
Common::String _filmProcess;
|
||||
Common::ScopedPtr<Screen> _currentScreen;
|
||||
Common::String _currentScreenName;
|
||||
Common::ScopedPtr<Character> _currentCharacter;
|
||||
Common::ScopedPtr<Character> _jokes;
|
||||
Common::String _currentCharacterName, _currentCharacterFilename, _currentCharacterObject;
|
||||
Common::String _nextScreenName;
|
||||
ScreenLoadingType _nextScreenType;
|
||||
Common::String _defaultMouseCursorName;
|
||||
AnimationPtr _defaultMouseCursor;
|
||||
Common::Point _mouse;
|
||||
bool _userEnabled;
|
||||
bool _systemUserEnabled;
|
||||
MouseMap _mouseMap;
|
||||
Common::RandomSource _random;
|
||||
|
||||
Inventory _inventory;
|
||||
Common::String _inventoryRegionName;
|
||||
RegionPtr _inventoryRegion;
|
||||
ObjectPtr _currentInventoryObject;
|
||||
|
||||
Dialog _dialog;
|
||||
|
||||
// Original engine use weird names for the vars, I keep them.
|
||||
int _tellTextTimer;
|
||||
TextLayout _textLayout;
|
||||
|
||||
int _syncSoundId;
|
||||
int _ambientSoundId;
|
||||
|
||||
Common::String _curtainProcess;
|
||||
int _curtainTimer;
|
||||
int _curtainScreen;
|
||||
|
||||
bool _fastMode;
|
||||
bool _hintMode;
|
||||
};
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif /* AGDS_AGDS_H */
|
||||
290
engines/agds/animation.cpp
Normal file
290
engines/agds/animation.cpp
Normal file
@@ -0,0 +1,290 @@
|
||||
/* 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 "agds/animation.h"
|
||||
#include "agds/agds.h"
|
||||
#include "agds/object.h"
|
||||
#include "agds/resourceManager.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
#include "video/flic_decoder.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
Animation::Animation(AGDSEngine *engine, const Common::String &name) : _engine(engine), _name(name), _flic(), _frame(), _scaledFrame(),
|
||||
_frames(0), _loop(false), _cycles(1), _phaseVarControlled(false),
|
||||
_phase(0), _paused(false), _speed(100), _z(0), _rotation(0),
|
||||
_delay(0), _random(0), _scale(1), _onScreen(true),
|
||||
_visibleHeight(0), _visibleCenter(0) {
|
||||
}
|
||||
|
||||
Animation::~Animation() {
|
||||
freeFrame();
|
||||
}
|
||||
|
||||
void Animation::freeScaledFrame() {
|
||||
if (_scaledFrame) {
|
||||
_scaledFrame->free();
|
||||
_scaledFrame.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Animation::freeFrame() {
|
||||
freeScaledFrame();
|
||||
if (_frame) {
|
||||
_frame->free();
|
||||
_frame.reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool Animation::load(Common::SeekableReadStream *stream, const Common::String &fname) {
|
||||
if (_phaseVarControlled) {
|
||||
if (_phaseVar.empty()) {
|
||||
warning("phase var controlled animation with no phase var");
|
||||
_phaseVarControlled = false;
|
||||
return false;
|
||||
}
|
||||
if (_loop) {
|
||||
warning("phase var controller animation with loop, resetting");
|
||||
_loop = false;
|
||||
}
|
||||
if (_cycles > 1) {
|
||||
warning("phase var controller animation with cycles, resetting");
|
||||
_cycles = 1;
|
||||
}
|
||||
if (_random) {
|
||||
warning("phase var controller animation with random, resetting");
|
||||
_random = false;
|
||||
}
|
||||
if (_delay > 0) {
|
||||
warning("phase var controller animation with delay, resetting");
|
||||
_delay = 0;
|
||||
}
|
||||
}
|
||||
_flic.reset();
|
||||
auto is_bmp = ResourceManager::IsBMP(*stream);
|
||||
|
||||
if (fname.hasSuffixIgnoreCase(".bmp") || is_bmp) {
|
||||
_frame.reset(_engine->loadPicture(fname));
|
||||
rescaleCurrentFrame();
|
||||
_frames = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
Video::FlicDecoder *flic = new Video::FlicDecoder;
|
||||
if (flic->loadStream(stream)) {
|
||||
_frames = flic->getFrameCount();
|
||||
_flic.reset(flic);
|
||||
decodeNextFrame();
|
||||
return true;
|
||||
} else {
|
||||
_frames = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Animation::scale(float scale) {
|
||||
if (scale != _scale) {
|
||||
debug("changing scale to %g", scale);
|
||||
_scale = scale;
|
||||
rescaleCurrentFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void Animation::rescaleCurrentFrame() {
|
||||
if (!_frame)
|
||||
return;
|
||||
|
||||
freeScaledFrame();
|
||||
if (_rotation != 0) {
|
||||
Graphics::TransformStruct transform(_scale * 100, _scale * 100, 90 * _rotation, _frame->w / 2, _frame->h / 2, Graphics::TSpriteBlendMode::BLEND_NORMAL, Graphics::kDefaultRgbaMod);
|
||||
_scaledFrame.reset(_frame->rotoscale(transform));
|
||||
} else if (_scale != 1) {
|
||||
_scaledFrame.reset(_frame->scale(_frame->w * _scale, _frame->h * _scale, true));
|
||||
}
|
||||
auto *frame = _scaledFrame ? _scaledFrame.get() : _frame.get();
|
||||
if (frame) {
|
||||
uint h = frame->h, w = frame->w;
|
||||
_visibleHeight = 0;
|
||||
uint minX = w, maxX = 0;
|
||||
for (uint i = 0; i != h; ++i) {
|
||||
uint y = h - 1 - i;
|
||||
auto *ptr = static_cast<uint32 *>(frame->getBasePtr(0, y));
|
||||
for (uint x = 0; x != w; ++x) {
|
||||
uint8 a, r, g, b;
|
||||
frame->format.colorToARGB(*ptr++, a, r, g, b);
|
||||
if (a != 0) {
|
||||
if (h - i > _visibleHeight)
|
||||
_visibleHeight = h - i;
|
||||
if (x > maxX)
|
||||
maxX = x;
|
||||
else if (x < minX)
|
||||
minX = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
_visibleCenter = minX < maxX ? (maxX + minX) / 2 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Animation::decodeNextFrame() {
|
||||
if (ended()) {
|
||||
_phase = 0;
|
||||
_flic->rewind();
|
||||
}
|
||||
|
||||
auto frame = _flic->decodeNextFrame();
|
||||
if (!frame) {
|
||||
debug("frame of %s couldn't be decoded, process: %s, phase var: %s, at end: %d", _name.c_str(), _process.c_str(), _phaseVar.c_str(), _flic->endOfVideo());
|
||||
warning("frame of %s couldn't be decoded, process: %s, phase var: %s, at end: %d", _name.c_str(), _process.c_str(), _phaseVar.c_str(), _flic->endOfVideo());
|
||||
return;
|
||||
}
|
||||
|
||||
freeFrame();
|
||||
_delay = _flic->getCurFrameDelay() * _speed / 4000; // 40 == 1000 / 25, 25 fps
|
||||
_frame.reset(_engine->convertToTransparent(frame->convertTo(_engine->pixelFormat(), _flic->getPalette())));
|
||||
rescaleCurrentFrame();
|
||||
++_phase;
|
||||
}
|
||||
|
||||
void Animation::rewind() {
|
||||
_phase = 0;
|
||||
_flic->rewind();
|
||||
}
|
||||
|
||||
void Animation::onScreen(bool onScreen) {
|
||||
_onScreen = onScreen;
|
||||
}
|
||||
|
||||
bool Animation::tick() {
|
||||
if (!_flic && _frame) { // static frame
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_paused || !_frame || (_phaseVarControlled && !_onScreen)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_phaseVarControlled && _engine->getGlobal(_phaseVar) == -2) {
|
||||
debug("phase var %s signalled deleting of animation", _phaseVar.c_str());
|
||||
freeFrame();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_delay > 0) {
|
||||
--_delay;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool eov = _phase >= _frames;
|
||||
if (!_phaseVar.empty() && eov) {
|
||||
_engine->setGlobal(_phaseVar, -1);
|
||||
onScreen(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!eov) {
|
||||
decodeNextFrame();
|
||||
onScreen(true);
|
||||
}
|
||||
|
||||
if (!_process.empty()) {
|
||||
if (!_phaseVar.empty())
|
||||
_engine->setGlobal(_phaseVar, _phase - 1);
|
||||
if (/*_phase || */ _phaseVarControlled) {
|
||||
if (eov && _random) {
|
||||
rewind();
|
||||
_delay = _engine->getRandomNumber(_random);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (eov && !_loop && --_cycles <= 0) {
|
||||
if (!_phaseVar.empty()) {
|
||||
_engine->setGlobal(_phaseVar, _phase - 1);
|
||||
} else {
|
||||
_engine->reactivate(_process, "animation end", true);
|
||||
_engine->runPendingReactivatedProcesses();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (eov && frames() > 1)
|
||||
rewind();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Animation::paint(Graphics::Surface &backbuffer, Common::Point dst, Graphics::ManagedSurface *mask, int maskAlpha) const {
|
||||
dst += _position;
|
||||
auto *frame = _scaledFrame ? _scaledFrame.get() : _frame.get();
|
||||
if (!frame || !_onScreen)
|
||||
return;
|
||||
|
||||
Common::Rect srcRect = frame->getBounds();
|
||||
if (!Common::Rect::getBlitRect(dst, srcRect, backbuffer.getRect()))
|
||||
return;
|
||||
|
||||
if (mask) {
|
||||
int invMaskAlpha = 255 - maskAlpha;
|
||||
auto subFrame = frame->getSubArea(srcRect);
|
||||
auto subMask = mask->getSubArea(srcRect);
|
||||
byte *dstPixels = static_cast<byte *>(backbuffer.getBasePtr(dst.x, dst.y));
|
||||
const byte *srcPixels = static_cast<byte *>(subFrame.getPixels());
|
||||
const byte *maskPixels = static_cast<byte *>(subMask.getPixels());
|
||||
for (int y = 0; y != srcRect.height(); ++y) {
|
||||
byte *dstRow = dstPixels;
|
||||
const byte *srcRow = srcPixels;
|
||||
const byte *maskRow = maskPixels;
|
||||
for (int x = 0; x != srcRect.width(); ++x) {
|
||||
uint8 srcA = *srcRow++;
|
||||
++dstRow;
|
||||
++maskRow;
|
||||
uint8 invSrcA = 255 - srcA;
|
||||
if (srcA != 0) {
|
||||
for (int n = 3; n--; ++dstRow) {
|
||||
*dstRow = ((*dstRow * invSrcA + *srcRow++ * srcA) * invMaskAlpha + *maskRow++ * maskAlpha * srcA) / 65025;
|
||||
}
|
||||
} else {
|
||||
srcRow += 3;
|
||||
dstRow += 3;
|
||||
maskRow += 3;
|
||||
}
|
||||
}
|
||||
dstPixels += backbuffer.pitch;
|
||||
srcPixels += subFrame.pitch;
|
||||
maskPixels += subMask.pitch;
|
||||
}
|
||||
} else
|
||||
frame->blendBlitTo(backbuffer, dst.x, dst.y, Graphics::FLIP_NONE, &srcRect);
|
||||
}
|
||||
|
||||
uint Animation::width() const {
|
||||
return _flic ? _flic->getWidth() * _scale : 0;
|
||||
}
|
||||
uint Animation::height() const {
|
||||
return _flic ? _flic->getHeight() * _scale : 0;
|
||||
}
|
||||
|
||||
} // namespace AGDS
|
||||
201
engines/agds/animation.h
Normal file
201
engines/agds/animation.h
Normal file
@@ -0,0 +1,201 @@
|
||||
/* 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 ANIMATION_H
|
||||
#define ANIMATION_H
|
||||
|
||||
#include "common/ptr.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
class ManagedSurface;
|
||||
} // namespace Graphics
|
||||
namespace Video {
|
||||
class FlicDecoder;
|
||||
}
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
class AGDSEngine;
|
||||
class Object;
|
||||
|
||||
class Animation {
|
||||
using FlicPtr = Common::ScopedPtr<Video::FlicDecoder>;
|
||||
using ManagedSurfacePtr = Common::ScopedPtr<Graphics::ManagedSurface>;
|
||||
|
||||
AGDSEngine *_engine;
|
||||
Common::String _name;
|
||||
FlicPtr _flic;
|
||||
ManagedSurfacePtr _frame;
|
||||
ManagedSurfacePtr _scaledFrame;
|
||||
int _frames;
|
||||
Common::Point _position;
|
||||
Common::String _process;
|
||||
Common::String _phaseVar;
|
||||
bool _loop;
|
||||
int _cycles;
|
||||
bool _phaseVarControlled;
|
||||
int _phase;
|
||||
bool _paused;
|
||||
int _speed;
|
||||
int _z;
|
||||
int _rotation;
|
||||
int _delay;
|
||||
int _random;
|
||||
float _scale;
|
||||
bool _onScreen;
|
||||
uint _visibleHeight;
|
||||
uint _visibleCenter;
|
||||
|
||||
public:
|
||||
Animation(AGDSEngine *engine, const Common::String &name);
|
||||
~Animation();
|
||||
|
||||
bool hasFrame() const {
|
||||
return _frame != nullptr;
|
||||
}
|
||||
|
||||
int frames() const {
|
||||
return _frames;
|
||||
}
|
||||
bool ended() const {
|
||||
return _phase >= _frames;
|
||||
}
|
||||
|
||||
const Common::Point &position() const {
|
||||
return _position;
|
||||
}
|
||||
|
||||
void position(Common::Point position) {
|
||||
_position = position;
|
||||
}
|
||||
|
||||
const Common::String &phaseVar() const {
|
||||
return _phaseVar;
|
||||
}
|
||||
|
||||
void phaseVar(const Common::String &phaseVar) {
|
||||
_phaseVar = phaseVar;
|
||||
}
|
||||
|
||||
const Common::String &process() const {
|
||||
return _process;
|
||||
}
|
||||
|
||||
void process(const Common::String &process) {
|
||||
_process = process;
|
||||
}
|
||||
|
||||
void loop(bool loop) {
|
||||
_loop = loop;
|
||||
}
|
||||
|
||||
void cycles(int cycles) {
|
||||
_cycles = cycles;
|
||||
}
|
||||
|
||||
void delay(int delay) {
|
||||
_delay = delay;
|
||||
}
|
||||
|
||||
void setRandom(int value) { // can't declare random() because of stupid macro
|
||||
_random = value;
|
||||
}
|
||||
|
||||
void phaseVarControlled(bool controlled) {
|
||||
_phaseVarControlled = controlled;
|
||||
_onScreen = !controlled;
|
||||
}
|
||||
|
||||
bool paused() const {
|
||||
return _paused;
|
||||
}
|
||||
|
||||
void pause() {
|
||||
_paused = true;
|
||||
}
|
||||
|
||||
void resume() {
|
||||
_paused = false;
|
||||
}
|
||||
|
||||
void rewind();
|
||||
|
||||
void speed(int speed) {
|
||||
_speed = speed;
|
||||
}
|
||||
|
||||
void z(int z) {
|
||||
_z = z;
|
||||
}
|
||||
|
||||
int z() const {
|
||||
return _z;
|
||||
}
|
||||
|
||||
void rotate(int rotation) {
|
||||
if (_rotation == rotation)
|
||||
return;
|
||||
|
||||
_rotation = rotation;
|
||||
rescaleCurrentFrame();
|
||||
}
|
||||
|
||||
void scale(float scale);
|
||||
float scale() const {
|
||||
return _scale;
|
||||
}
|
||||
|
||||
int phase() const {
|
||||
return _phase;
|
||||
}
|
||||
|
||||
void onScreen(bool onScreen);
|
||||
|
||||
bool load(Common::SeekableReadStream *stream, const Common::String &fname);
|
||||
void paint(Graphics::Surface &backbuffer, Common::Point dst, Graphics::ManagedSurface *mask = nullptr, int maskAlpha = 0) const;
|
||||
uint width() const;
|
||||
uint height() const;
|
||||
uint visibleHeight() const {
|
||||
return _visibleHeight;
|
||||
}
|
||||
uint visibleCenter() const {
|
||||
return _visibleCenter;
|
||||
}
|
||||
bool tick();
|
||||
|
||||
void decodeNextFrame();
|
||||
|
||||
private:
|
||||
void rescaleCurrentFrame();
|
||||
void freeFrame();
|
||||
void freeScaledFrame();
|
||||
};
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif /* AGDS_ANIMATION_H */
|
||||
439
engines/agds/character.cpp
Normal file
439
engines/agds/character.cpp
Normal file
@@ -0,0 +1,439 @@
|
||||
/* 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 "agds/character.h"
|
||||
#include "agds/agds.h"
|
||||
#include "agds/animation.h"
|
||||
#include "agds/object.h"
|
||||
#include "agds/region.h"
|
||||
#include "agds/resourceManager.h"
|
||||
#include "common/array.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
Character::Character(AGDSEngine *engine, const Common::String &name) : _engine(engine), _object(), _animation(nullptr), _jokes(false),
|
||||
_name(name),
|
||||
_enabled(true), _visible(false), _stopped(false), _shown(false),
|
||||
_phase(-1), _frames(0), _direction(-1), _movementDirections(0) {
|
||||
}
|
||||
|
||||
Character::~Character() {
|
||||
}
|
||||
|
||||
void Character::load(Common::SeekableReadStream &stream) {
|
||||
debug("loading character...");
|
||||
stream.readUint32LE(); // unk
|
||||
uint16 magic = stream.readUint16LE();
|
||||
switch (magic) {
|
||||
case 0xdead:
|
||||
_movementDirections = 16;
|
||||
break;
|
||||
case 0x8888:
|
||||
_movementDirections = 8;
|
||||
break;
|
||||
default:
|
||||
error("invalid magic %04x", magic);
|
||||
}
|
||||
|
||||
_animations.clear();
|
||||
while (stream.pos() < stream.size()) {
|
||||
uint size = stream.readUint32LE();
|
||||
uint index = stream.readUint16LE();
|
||||
debug("header size %u, index: %u", size, index);
|
||||
|
||||
uint16 frames = stream.readUint16LE();
|
||||
uint16 format = stream.readUint16LE();
|
||||
Common::String filename = readString(stream);
|
||||
|
||||
AnimationDescription animation;
|
||||
animation.filename = filename;
|
||||
debug("%s:%u: animation %s, frames: %d, format: %d", _name.c_str(), _animations.size(), animation.filename.c_str(), frames, format);
|
||||
while (frames--) {
|
||||
int x = stream.readSint16LE();
|
||||
int y = stream.readSint16LE();
|
||||
uint w = stream.readUint32LE();
|
||||
uint h = stream.readUint32LE();
|
||||
AnimationDescription::Frame frame = {x, y, w, h};
|
||||
animation.frames.push_back(frame);
|
||||
debug("frame %d, %d, %dx%d", x, y, w, h);
|
||||
uint unk1 = stream.readUint32LE();
|
||||
uint unk2 = stream.readUint32LE();
|
||||
uint unk3 = stream.readUint32LE();
|
||||
uint unk4 = stream.readUint32LE(); // GRP file offset?
|
||||
uint unk5 = stream.readUint32LE();
|
||||
uint unk6 = stream.readByte();
|
||||
uint unk7 = stream.readUint32LE();
|
||||
uint unk8 = stream.readUint32LE();
|
||||
stream.readUint32LE(); // CDCDCDCD
|
||||
uint unk9 = stream.readUint32LE();
|
||||
uint unk10 = stream.readUint32LE();
|
||||
stream.readUint32LE(); // CDCDCDCD
|
||||
uint unk11 = stream.readByte();
|
||||
stream.readUint32LE(); // CDCDCDCD
|
||||
debug("unknown: %u %u %u 0x%08x - %u %u %u %u - %u %u %u",
|
||||
unk1, unk2, unk3, unk4,
|
||||
unk5, unk6, unk7, unk8,
|
||||
unk9, unk10, unk11);
|
||||
}
|
||||
_animations[index] = animation;
|
||||
}
|
||||
}
|
||||
|
||||
void Character::associate(const Common::String &name) {
|
||||
_object = _engine->loadObject(name);
|
||||
_engine->runObject(_object);
|
||||
}
|
||||
|
||||
void Character::visible(bool visible) {
|
||||
if (visible) {
|
||||
_shown = true;
|
||||
}
|
||||
_visible = visible;
|
||||
}
|
||||
|
||||
void Character::loadState(Common::ReadStream &stream) {
|
||||
int x = stream.readUint16LE();
|
||||
int y = stream.readUint16LE();
|
||||
int dir = stream.readSint16LE();
|
||||
debug("character at %d, %d, dir: %d", x, y, dir);
|
||||
position(Common::Point(x, y));
|
||||
direction(dir);
|
||||
_visible = stream.readUint16LE();
|
||||
_enabled = stream.readUint16LE();
|
||||
}
|
||||
|
||||
void Character::saveState(Common::WriteStream &stream) const {
|
||||
stream.writeUint16LE(_pos.x);
|
||||
stream.writeUint16LE(_pos.y);
|
||||
stream.writeUint16LE(_direction);
|
||||
stream.writeUint16LE(_visible);
|
||||
stream.writeUint16LE(_enabled);
|
||||
}
|
||||
|
||||
bool Character::direction(int dir) {
|
||||
debug("setDirection %d", dir);
|
||||
_direction = dir;
|
||||
|
||||
if (dir < 0)
|
||||
return false;
|
||||
|
||||
_animationPos = Common::Point();
|
||||
return animate(dir, 100, false);
|
||||
}
|
||||
|
||||
void Character::notifyProcess(const Common::String &name) {
|
||||
debug("%s:notifyProcess %s", _name.c_str(), name.c_str());
|
||||
if (!_processName.empty())
|
||||
_engine->reactivate(name, "Character::notifyProcess", false);
|
||||
|
||||
_processName = name;
|
||||
}
|
||||
|
||||
bool Character::moveTo(const Common::String &processName, Common::Point dst, int dir) {
|
||||
if (!_visible)
|
||||
return false;
|
||||
|
||||
debug("character move %d,%d %d", dst.x, dst.y, dir);
|
||||
notifyProcess(processName);
|
||||
_pos = dst;
|
||||
_shown = true;
|
||||
bool r = direction(dir);
|
||||
|
||||
auto *screen = _engine->getCurrentScreen();
|
||||
if (screen) {
|
||||
auto objects = screen->find(dst);
|
||||
for (auto &object : objects) {
|
||||
auto region = object->getTrapRegion();
|
||||
if (region && region->pointIn(dst)) {
|
||||
debug("starting trap process");
|
||||
_engine->runProcess(object, object->getTrapHandler());
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void Character::pointTo(const Common::String &processName, Common::Point dst) {
|
||||
debug("character point to stub %d,%d, process: %s", dst.x, dst.y, processName.c_str());
|
||||
notifyProcess(processName);
|
||||
if (!_processName.empty() && !_engine->activeCurtain()) {
|
||||
_engine->reactivate(_processName, "Character::pointTo");
|
||||
_processName.clear();
|
||||
}
|
||||
_shown = true;
|
||||
}
|
||||
|
||||
bool Character::animate(int direction, int speed, bool jokes) {
|
||||
if (direction == -1 || !_enabled)
|
||||
return false;
|
||||
|
||||
if (_stopped) {
|
||||
debug("character stopped, skipping");
|
||||
_stopped = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto character = jokes ? _engine->jokes() : this;
|
||||
auto description = character->animationDescription(direction);
|
||||
if (!description) {
|
||||
warning("no %s animation %d", jokes ? "jokes" : "character", direction);
|
||||
return false;
|
||||
}
|
||||
auto animation = _engine->loadAnimation(description->filename);
|
||||
if (!animation) {
|
||||
warning("no %s animation file %s", jokes ? "jokes" : "character", description->filename.c_str());
|
||||
return false;
|
||||
}
|
||||
_description = description;
|
||||
_shown = true;
|
||||
_animation = animation;
|
||||
_animation->speed(speed);
|
||||
_animation->rewind();
|
||||
_phase = 0;
|
||||
_frames = _animation->frames();
|
||||
_jokes = jokes;
|
||||
if (jokes)
|
||||
_jokesDirection = direction;
|
||||
else
|
||||
_direction = direction;
|
||||
debug("character animation frames: %d, enabled: %d, visible: %d", _frames, _enabled, _visible);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Character::animate(Common::Point pos, int direction, int speed) {
|
||||
debug("animate character: %d,%d %d %d", pos.x, pos.y, direction, speed);
|
||||
auto ok = animate(direction, speed, true);
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
_animationPos = pos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Character::stop() {
|
||||
_stopped = true;
|
||||
}
|
||||
|
||||
void Character::leave(const Common::String &processName) {
|
||||
debug("character %s: leave, process: %s", _object->getName().c_str(), processName.c_str());
|
||||
notifyProcess(processName);
|
||||
}
|
||||
|
||||
void Character::tick(bool reactivate) {
|
||||
if (!active())
|
||||
return;
|
||||
if (_animation) {
|
||||
auto screen = _engine->getCurrentScreen();
|
||||
auto scale = screen ? screen->getZScale(_pos.y) : 1;
|
||||
_animation->scale(scale);
|
||||
|
||||
if (!_stopped && _phase >= 0 && _phase < _frames) {
|
||||
_animation->tick();
|
||||
_phase = _animation->phase();
|
||||
if (_phase >= _frames) {
|
||||
bool wasJokes = _jokes;
|
||||
_jokes = false;
|
||||
_phase = -1;
|
||||
_frames = 0;
|
||||
if (wasJokes)
|
||||
direction(_direction);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (reactivate && !_processName.empty() && !_engine->activeCurtain()) {
|
||||
_engine->reactivate(_processName, "Character::tick");
|
||||
_processName.clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool Character::pointIn(Common::Point pos) const {
|
||||
if (!_animation)
|
||||
return false;
|
||||
|
||||
Common::Rect rect(_animation->width(), _animation->height());
|
||||
rect.moveTo(animationPosition());
|
||||
return rect.contains(pos);
|
||||
}
|
||||
|
||||
Common::Point Character::animationPosition() const {
|
||||
Common::Point pos = _pos + _animationPos;
|
||||
|
||||
if (_animation) {
|
||||
pos.y -= _animation->visibleHeight();
|
||||
pos.x -= _animation->visibleCenter();
|
||||
|
||||
if (_description) {
|
||||
auto &frames = _description->frames;
|
||||
if (_phase >= 0 && _phase < static_cast<int>(frames.size())) {
|
||||
auto &frame = frames[_phase];
|
||||
pos.x += frame.x * _animation->scale();
|
||||
pos.y += frame.y * _animation->scale();
|
||||
}
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
void Character::paint(Graphics::Surface &backbuffer, Common::Point pos) const {
|
||||
if (!_enabled || !visible() || !_animation)
|
||||
return;
|
||||
|
||||
pos += animationPosition();
|
||||
|
||||
int fogAlpha = 0;
|
||||
if (_fog) {
|
||||
auto z = this->z();
|
||||
if (z >= _fogMinZ && z < _fogMaxZ) {
|
||||
fogAlpha = 255 * (z - _fogMinZ) / (_fogMaxZ - _fogMinZ);
|
||||
} else if (z >= _fogMaxZ) {
|
||||
fogAlpha = 255;
|
||||
}
|
||||
}
|
||||
_animation->paint(backbuffer, pos, _fog.get(), fogAlpha);
|
||||
}
|
||||
|
||||
int Character::z() const {
|
||||
int y = _pos.y + _animationPos.y;
|
||||
// fixme: add temp var : _movePos?
|
||||
// debug("char z = %d", y);
|
||||
return g_system->getHeight() - y;
|
||||
}
|
||||
|
||||
void Character::reset() {
|
||||
_fog.reset();
|
||||
_shown = false;
|
||||
_animation.reset();
|
||||
_phase = -1;
|
||||
_frames = 0;
|
||||
}
|
||||
|
||||
void Character::setFog(Graphics::ManagedSurface *surface, int minZ, int maxZ) {
|
||||
_fog.reset(surface);
|
||||
_fogMinZ = minZ;
|
||||
_fogMaxZ = maxZ;
|
||||
}
|
||||
|
||||
int Character::getDirectionForMovement(Common::Point delta) {
|
||||
auto angle = atan2(delta.y, delta.x);
|
||||
if (angle < 0)
|
||||
angle += M_PI * 2;
|
||||
|
||||
if (_movementDirections == 16) {
|
||||
if (angle < 6.1850053125 && angle > 0.0981746875) {
|
||||
if (angle > 0.5235983333333333) {
|
||||
if (angle > 0.9490219791666666) {
|
||||
if (angle > 1.374445625) {
|
||||
if (angle > 1.767144375) {
|
||||
if (angle > 2.192568020833333) {
|
||||
if (angle > 2.617991666666666) {
|
||||
if (angle > 3.0434153125) {
|
||||
if (angle > 3.2397646875) {
|
||||
if (angle > 3.665188333333333) {
|
||||
if (angle > 4.090611979166667) {
|
||||
if (angle > 4.516035625) {
|
||||
if (angle > 4.908734375) {
|
||||
if (angle > 5.334158020833333) {
|
||||
if (angle > 5.759581666666667)
|
||||
return 3;
|
||||
else
|
||||
return 2;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 15;
|
||||
}
|
||||
} else {
|
||||
return 14;
|
||||
}
|
||||
} else {
|
||||
return 13;
|
||||
}
|
||||
} else {
|
||||
return 12;
|
||||
}
|
||||
} else {
|
||||
return 11;
|
||||
}
|
||||
} else {
|
||||
return 10;
|
||||
}
|
||||
} else {
|
||||
return 9;
|
||||
}
|
||||
} else {
|
||||
return 8;
|
||||
}
|
||||
} else {
|
||||
return 7;
|
||||
}
|
||||
} else {
|
||||
return 6;
|
||||
}
|
||||
} else {
|
||||
return 5;
|
||||
}
|
||||
} else {
|
||||
return 4;
|
||||
}
|
||||
} else if (angle < 5.89048125 && angle > 0.39269875) {
|
||||
if (angle > 1.17809625) {
|
||||
if (angle > 1.96349375) {
|
||||
if (angle > 2.74889125) {
|
||||
if (angle > 3.53428875) {
|
||||
if (angle > 4.31968625) {
|
||||
if (angle > 5.105083749999999)
|
||||
return 2;
|
||||
else
|
||||
return 0;
|
||||
} else {
|
||||
return 14;
|
||||
}
|
||||
} else {
|
||||
return 12;
|
||||
}
|
||||
} else {
|
||||
return 10;
|
||||
}
|
||||
} else {
|
||||
return 8;
|
||||
}
|
||||
} else {
|
||||
return 6;
|
||||
}
|
||||
} else {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AGDS
|
||||
165
engines/agds/character.h
Normal file
165
engines/agds/character.h
Normal file
@@ -0,0 +1,165 @@
|
||||
/* 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 CHARACTER_H
|
||||
#define CHARACTER_H
|
||||
|
||||
#include "common/hashmap.h"
|
||||
#include "common/ptr.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
class ReadStream;
|
||||
class WriteStream;
|
||||
} // namespace Common
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
class ManagedSurface;
|
||||
} // namespace Graphics
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
class AGDSEngine;
|
||||
class Object;
|
||||
using ObjectPtr = Common::SharedPtr<Object>;
|
||||
class Animation;
|
||||
using AnimationPtr = Common::SharedPtr<Animation>;
|
||||
|
||||
class Character {
|
||||
using FogPtr = Common::ScopedPtr<Graphics::ManagedSurface>;
|
||||
AGDSEngine *_engine;
|
||||
ObjectPtr _object;
|
||||
AnimationPtr _animation;
|
||||
FogPtr _fog;
|
||||
bool _jokes;
|
||||
Common::String _name;
|
||||
Common::String _processName;
|
||||
Common::Point _pos;
|
||||
Common::Point _animationPos;
|
||||
bool _enabled;
|
||||
bool _visible;
|
||||
bool _stopped;
|
||||
bool _shown;
|
||||
int _phase;
|
||||
int _frames;
|
||||
int _direction;
|
||||
int _jokesDirection;
|
||||
int _movementDirections;
|
||||
int _fogMinZ, _fogMaxZ;
|
||||
|
||||
struct AnimationDescription {
|
||||
struct Frame {
|
||||
int x, y;
|
||||
uint w, h;
|
||||
};
|
||||
|
||||
Common::String filename;
|
||||
Common::Array<Frame> frames;
|
||||
};
|
||||
Common::HashMap<uint, AnimationDescription> _animations;
|
||||
const AnimationDescription *_description;
|
||||
|
||||
bool animate(int direction, int speed, bool jokes);
|
||||
Common::Point animationPosition() const;
|
||||
|
||||
public:
|
||||
Character(AGDSEngine *engine, const Common::String &name);
|
||||
~Character();
|
||||
|
||||
void associate(const Common::String &name);
|
||||
|
||||
const AnimationDescription *animationDescription(uint index) const {
|
||||
auto it = _animations.find(index);
|
||||
return it != _animations.end() ? &it->_value : nullptr;
|
||||
}
|
||||
|
||||
const Common::String &name() const {
|
||||
return _name;
|
||||
}
|
||||
|
||||
const ObjectPtr &object() const {
|
||||
return _object;
|
||||
}
|
||||
|
||||
void load(Common::SeekableReadStream &stream);
|
||||
void loadState(Common::ReadStream &stream);
|
||||
void saveState(Common::WriteStream &stream) const;
|
||||
|
||||
void enable(bool enabled = true) {
|
||||
_enabled = enabled;
|
||||
}
|
||||
|
||||
void visible(bool visible);
|
||||
bool visible() const {
|
||||
return _visible && _shown;
|
||||
}
|
||||
|
||||
bool active() const {
|
||||
return _enabled && _visible;
|
||||
}
|
||||
|
||||
bool animate(Common::Point pos, int direction, int speed);
|
||||
|
||||
void stop();
|
||||
void leave(const Common::String &processName);
|
||||
|
||||
int phase() const {
|
||||
return _jokes ? _phase : -1;
|
||||
}
|
||||
void phase(int phase) {
|
||||
_phase = phase;
|
||||
}
|
||||
|
||||
void position(Common::Point pos) {
|
||||
_pos = pos;
|
||||
}
|
||||
|
||||
Common::Point position() const {
|
||||
return _pos;
|
||||
}
|
||||
bool pointIn(Common::Point pos) const;
|
||||
|
||||
void notifyProcess(const Common::String &processName);
|
||||
bool moveTo(const Common::String &processName, Common::Point dst, int direction);
|
||||
void pointTo(const Common::String &processName, Common::Point dst);
|
||||
|
||||
bool direction(int dir);
|
||||
|
||||
int direction() const {
|
||||
return _jokes ? _jokesDirection : _direction;
|
||||
}
|
||||
|
||||
void tick(bool reactivate);
|
||||
void paint(Graphics::Surface &backbuffer, Common::Point pos) const;
|
||||
|
||||
int getDirectionForMovement(Common::Point delta);
|
||||
|
||||
int z() const;
|
||||
|
||||
void reset();
|
||||
void setFog(Graphics::ManagedSurface *surface, int minZ, int maxZ);
|
||||
};
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif /* AGDS_CHARACTER_H */
|
||||
3
engines/agds/configure.engine
Normal file
3
engines/agds/configure.engine
Normal 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 agds "AGDS (Black Mirror and NiBiRu)" no "" "" "highres 16bit jpeg" ""
|
||||
188
engines/agds/console.cpp
Normal file
188
engines/agds/console.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
/* 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 "agds/console.h"
|
||||
#include "agds/agds.h"
|
||||
#include "agds/animation.h"
|
||||
#include "agds/object.h"
|
||||
#include "agds/patch.h"
|
||||
#include "agds/process.h"
|
||||
#include "agds/screen.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
Console::Console(AGDSEngine *engine) : _engine(engine) {
|
||||
registerCmd("activate", WRAP_METHOD(Console, activate));
|
||||
registerCmd("info", WRAP_METHOD(Console, info));
|
||||
registerCmd("load", WRAP_METHOD(Console, load));
|
||||
registerCmd("run", WRAP_METHOD(Console, run));
|
||||
registerCmd("stop", WRAP_METHOD(Console, stop));
|
||||
registerCmd("set", WRAP_METHOD(Console, setGlobal));
|
||||
registerCmd("invadd", WRAP_METHOD(Console, inventoryAdd));
|
||||
registerCmd("patch", WRAP_METHOD(Console, patch));
|
||||
}
|
||||
|
||||
bool Console::load(int argc, const char **argv) {
|
||||
if (argc < 2) {
|
||||
debugPrintf("usage: %s object_id\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
ObjectPtr object = _engine->loadObject(argv[1]);
|
||||
if (!object) {
|
||||
debugPrintf("no object %s\n", argv[1]);
|
||||
return true;
|
||||
}
|
||||
_engine->runObject(object);
|
||||
detach();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Console::run(int argc, const char **argv) {
|
||||
if (argc < 2) {
|
||||
debugPrintf("usage: %s object_id\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
ObjectPtr object = _engine->getCurrentScreenObject(argv[1]);
|
||||
if (!object) {
|
||||
debugPrintf("no object %s\n", argv[1]);
|
||||
return true;
|
||||
}
|
||||
_engine->getCurrentScreen()->remove(object);
|
||||
_engine->runObject(object);
|
||||
detach();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Console::stop(int argc, const char **argv) {
|
||||
if (argc < 2) {
|
||||
debugPrintf("usage: %s object_id\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
ObjectPtr object = _engine->getCurrentScreenObject(argv[1]);
|
||||
if (!object) {
|
||||
debugPrintf("no object %s\n", argv[1]);
|
||||
return true;
|
||||
}
|
||||
_engine->getCurrentScreen()->remove(object);
|
||||
auto process = _engine->findProcess(argv[1]);
|
||||
if (!process) {
|
||||
debugPrintf("no process %s\n", argv[1]);
|
||||
return true;
|
||||
}
|
||||
process->done();
|
||||
detach();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Console::activate(int argc, const char **argv) {
|
||||
if (argc < 2) {
|
||||
debugPrintf("usage: %s object_id\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
_engine->reactivate(argv[1], "console");
|
||||
detach();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Console::info(int argc, const char **argv) {
|
||||
auto screen = _engine->getCurrentScreen();
|
||||
if (screen) {
|
||||
debugPrintf("screen %s:\n", screen->getName().c_str());
|
||||
for (auto &object : screen->children()) {
|
||||
auto pos = object->getPosition();
|
||||
debugPrintf("object %s [alive: %d] at %d,%d\n", object->getName().c_str(), object->alive(), pos.x, pos.y);
|
||||
}
|
||||
for (auto &desc : screen->animations()) {
|
||||
auto &animation = desc.animation;
|
||||
auto pos = animation->position();
|
||||
debugPrintf("animation %s (process: %s, %s) at %d,%d,%d, frame: %d\n",
|
||||
animation->phaseVar().c_str(), animation->process().c_str(), animation->paused() ? "paused" : "running",
|
||||
pos.x, pos.y, animation->z(), animation->phase());
|
||||
}
|
||||
}
|
||||
debugPrintf("processes:\n");
|
||||
auto &processes = _engine->processes();
|
||||
for (auto &process : processes) {
|
||||
if (process)
|
||||
debugPrintf("%s\n", process->getName().c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::setGlobal(int argc, const char **argv) {
|
||||
if (argc < 3) {
|
||||
debugPrintf("usage: %s var value\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
int value;
|
||||
if (sscanf(argv[2], "%d", &value) != 1) {
|
||||
debugPrintf("invalid value\n");
|
||||
return true;
|
||||
}
|
||||
_engine->setGlobal(argv[1], value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::inventoryAdd(int argc, const char **argv) {
|
||||
if (argc < 2) {
|
||||
debugPrintf("usage: %s inv.object\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
int idx = _engine->inventory().add(argv[1]);
|
||||
debugPrintf("add = %d\n", idx);
|
||||
|
||||
detach();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::patch(int argc, const char **argv) {
|
||||
if (argc != 2 && argc != 4) {
|
||||
debugPrintf("usage: %s screen [object flag]\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
if (argc == 2) {
|
||||
auto patch = _engine->getPatch(argv[1]);
|
||||
if (!patch) {
|
||||
debugPrintf("no patch for %s found.", argv[1]);
|
||||
return true;
|
||||
}
|
||||
debugPrintf("screen saved: %d\n", patch->screenSaved);
|
||||
debugPrintf("screen region: %s\n", patch->screenRegionName.c_str());
|
||||
debugPrintf("previous screen: %s\n", patch->prevScreenName.c_str());
|
||||
debugPrintf("screen loading type: %d\n", static_cast<int>(patch->loadingType));
|
||||
debugPrintf("character pos: %d,%d, direction: %d, present: %d\n", patch->characterPosition.x, patch->characterPosition.y, patch->characterDirection, patch->characterPresent);
|
||||
debugPrintf("mouse cursor: %s\n", patch->defaultMouseCursor.c_str());
|
||||
for (auto &object : patch->objects) {
|
||||
debugPrintf(" - object %s: present: %d\n", object.name.c_str(), object.flag);
|
||||
}
|
||||
} else if (argc == 4) {
|
||||
auto patch = _engine->getPatch(argv[1]);
|
||||
if (!patch)
|
||||
debugPrintf("no patch found, creating...\n");
|
||||
patch = _engine->createPatch(argv[1]);
|
||||
patch->setFlag(argv[2], atoi(argv[3]));
|
||||
}
|
||||
|
||||
detach();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AGDS
|
||||
52
engines/agds/console.h
Normal file
52
engines/agds/console.h
Normal 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 CONSOLE_H
|
||||
#define CONSOLE_H
|
||||
|
||||
#include "gui/debugger.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
class AGDSEngine;
|
||||
|
||||
class Console : public GUI::Debugger {
|
||||
public:
|
||||
Console(AGDSEngine *engine);
|
||||
using GUI::Debugger::clearVars;
|
||||
using GUI::Debugger::registerVar;
|
||||
|
||||
private:
|
||||
bool load(int argc, const char **argv);
|
||||
bool run(int argc, const char **argv);
|
||||
bool stop(int argc, const char **argv);
|
||||
bool activate(int argc, const char **argv);
|
||||
bool info(int argc, const char **argv);
|
||||
bool setGlobal(int argc, const char **argv);
|
||||
bool inventoryAdd(int argc, const char **argv);
|
||||
bool patch(int argc, const char **argv);
|
||||
|
||||
AGDSEngine *_engine;
|
||||
};
|
||||
|
||||
} // namespace AGDS
|
||||
|
||||
#endif
|
||||
3
engines/agds/credits.pl
Normal file
3
engines/agds/credits.pl
Normal file
@@ -0,0 +1,3 @@
|
||||
begin_section("AGDS");
|
||||
add_person("Vladimir Menshakov", "whoozle", "");
|
||||
end_section();
|
||||
137
engines/agds/database.cpp
Normal file
137
engines/agds/database.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
/* 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 "agds/database.h"
|
||||
#include "common/algorithm.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
bool Database::open(const Common::String &filename) {
|
||||
Common::File file;
|
||||
if (!file.open(Common::Path{filename}))
|
||||
return false;
|
||||
|
||||
return open(filename, file);
|
||||
}
|
||||
|
||||
namespace {
|
||||
static const uint32 kMagic = 666;
|
||||
static const uint32 kHeaderFieldSize = 0x09;
|
||||
static const uint32 kHeaderSize = 0x14;
|
||||
static const uint32 kDefaultNameSize = 0x1f;
|
||||
} // namespace
|
||||
|
||||
uint32 Database::getDataOffset(uint32 maxNameSize, uint32 totalEntries) {
|
||||
return kHeaderSize + (maxNameSize + kHeaderFieldSize) * totalEntries;
|
||||
}
|
||||
|
||||
bool Database::open(const Common::String &filename, Common::SeekableReadStream &stream) {
|
||||
_filename = filename;
|
||||
uint32 magic = stream.readUint32LE();
|
||||
if (magic != kMagic) {
|
||||
debug("invalid magic for database %s", _filename.c_str());
|
||||
return false;
|
||||
}
|
||||
_writeable = stream.readUint32LE();
|
||||
_totalEntries = stream.readUint32LE();
|
||||
_usedEntries = stream.readUint32LE();
|
||||
_maxNameSize = stream.readUint32LE();
|
||||
if (_maxNameSize == 0) {
|
||||
debug("invalid max name record size");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 dataOffset = getDataOffset(_maxNameSize, _totalEntries);
|
||||
Common::Array<char> nameBuffer(_maxNameSize + 1);
|
||||
for (uint32 i = 0; i < _usedEntries; ++i) {
|
||||
uint32 offset = stream.readUint32LE();
|
||||
stream.read(nameBuffer.data(), nameBuffer.size());
|
||||
char *z = Common::find(nameBuffer.begin(), nameBuffer.end(), 0);
|
||||
Common::String name(nameBuffer.data(), z - nameBuffer.begin());
|
||||
uint32 size = stream.readUint32LE();
|
||||
// debug("adb entry: %s, offset %08x, size: %u", name.c_str(), offset, size);
|
||||
_entries.setVal(name, Entry(dataOffset + offset, size));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Database::write(Common::WriteStream &stream, const Common::HashMap<Common::String, Common::Array<uint8>> &entries) {
|
||||
auto n = entries.size();
|
||||
stream.writeUint32LE(kMagic);
|
||||
stream.writeUint32LE(1);
|
||||
stream.writeUint32LE(n);
|
||||
stream.writeUint32LE(n);
|
||||
stream.writeUint32LE(kDefaultNameSize);
|
||||
auto dataOffset = getDataOffset(kDefaultNameSize, n);
|
||||
debug("database data offset: 0x%06x", dataOffset);
|
||||
uint offset = 0;
|
||||
|
||||
for (auto &entry : entries) {
|
||||
auto &key = entry._key;
|
||||
auto &value = entry._value;
|
||||
|
||||
stream.writeUint32LE(offset);
|
||||
Common::Array<char> text(kDefaultNameSize + 1);
|
||||
strncpy(text.data(), key.c_str(), kDefaultNameSize);
|
||||
stream.write(text.data(), text.size());
|
||||
debug("database entry %s: 0x%06x", key.c_str(), offset);
|
||||
offset += value.size();
|
||||
stream.writeUint32LE(value.size());
|
||||
}
|
||||
|
||||
for (auto &entry : entries) {
|
||||
auto &value = entry._value;
|
||||
stream.write(value.data(), value.size());
|
||||
}
|
||||
}
|
||||
|
||||
Common::Array<Common::String> Database::getEntries() const {
|
||||
Common::Array<Common::String> names;
|
||||
for (EntriesType::const_iterator i = _entries.begin(); i != _entries.end(); ++i) {
|
||||
names.push_back(i->_key);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *Database::getEntry(const Common::String &name) const {
|
||||
Common::File file;
|
||||
if (!file.open(Common::Path{_filename})) {
|
||||
error("could not open database file %s", _filename.c_str()); // previously available, but now disappeared or no fd, error
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return getEntry(file, name);
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *Database::getEntry(Common::SeekableReadStream &parent, const Common::String &name) const {
|
||||
EntriesType::const_iterator i = _entries.find(name);
|
||||
if (i == _entries.end())
|
||||
return NULL;
|
||||
|
||||
const Entry &entry = i->_value;
|
||||
parent.seek(entry.offset);
|
||||
return parent.readStream(entry.size);
|
||||
}
|
||||
|
||||
} // namespace AGDS
|
||||
69
engines/agds/database.h
Normal file
69
engines/agds/database.h
Normal 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 DATABASE_H
|
||||
#define DATABASE_H
|
||||
|
||||
#include "common/hash-str.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
class Database {
|
||||
private:
|
||||
struct Entry {
|
||||
uint32 offset;
|
||||
uint32 size;
|
||||
|
||||
Entry() : offset(), size() {}
|
||||
Entry(uint32 o, uint32 s) : offset(o), size(s) {}
|
||||
};
|
||||
|
||||
using EntriesType = Common::HashMap<Common::String, Entry>;
|
||||
|
||||
Common::String _filename;
|
||||
bool _writeable;
|
||||
uint32 _totalEntries;
|
||||
uint32 _usedEntries;
|
||||
uint32 _maxNameSize;
|
||||
|
||||
EntriesType _entries;
|
||||
|
||||
private:
|
||||
static uint32 getDataOffset(uint32 maxNameSize, uint32 totalEntries);
|
||||
|
||||
public:
|
||||
bool open(const Common::String &filename);
|
||||
bool open(const Common::String &filename, Common::SeekableReadStream &stream);
|
||||
Common::Array<Common::String> getEntries() const;
|
||||
|
||||
Common::SeekableReadStream *getEntry(const Common::String &name) const;
|
||||
Common::SeekableReadStream *getEntry(Common::SeekableReadStream &parent, const Common::String &name) const;
|
||||
|
||||
static void write(Common::WriteStream &stream, const Common::HashMap<Common::String, Common::Array<uint8>> &entries);
|
||||
};
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif /* AGDS_DATABASE_H */
|
||||
50
engines/agds/detection.cpp
Normal file
50
engines/agds/detection.cpp
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
static const PlainGameDescriptor agdsGames[] = {
|
||||
{"nibiru", "NiBiRu: Age of Secrets"},
|
||||
{"black-mirror", "Black Mirror"},
|
||||
{0, 0}};
|
||||
|
||||
#include "agds/detection_tables.h"
|
||||
|
||||
class AGDSMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
|
||||
public:
|
||||
AGDSMetaEngineDetection() : AdvancedMetaEngineDetection(AGDS::gameDescriptions, agdsGames) {
|
||||
_maxScanDepth = 1;
|
||||
}
|
||||
|
||||
const char *getName() const override {
|
||||
return "agds";
|
||||
}
|
||||
|
||||
const char *getEngineName() const override {
|
||||
return "AGDS Engine";
|
||||
}
|
||||
|
||||
const char *getOriginalCopyright() const override {
|
||||
return "AGDS (C) Future Games";
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_PLUGIN_STATIC(AGDS_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, AGDSMetaEngineDetection);
|
||||
33
engines/agds/detection.h
Normal file
33
engines/agds/detection.h
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DETECTION_H
|
||||
#define DETECTION_H
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
enum AGDSGameFlag {
|
||||
AGDS_V2 = (1 << 0),
|
||||
};
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif // DETECTION_H
|
||||
100
engines/agds/detection_tables.h
Normal file
100
engines/agds/detection_tables.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/* 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 "engines/advancedDetector.h"
|
||||
#include "engines/agds/detection.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
static const ADGameDescription gameDescriptions[] = {
|
||||
|
||||
// Black Mirror
|
||||
{
|
||||
"black-mirror",
|
||||
0,
|
||||
AD_ENTRY2s(
|
||||
"gfx1.grp", "6665ce103cf12a362fd55f863d1ec9e6", 907820240,
|
||||
"data.adb", "3b2d85f16e24ac81c4ede7a1da55f786", 2169482),
|
||||
Common::EN_USA,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DROPPLATFORM,
|
||||
GUIO1(GUIO_NONE)},
|
||||
{"black-mirror",
|
||||
"CD version protected with StarForce",
|
||||
AD_ENTRY2s(
|
||||
"gfx1.grp", "6665ce103cf12a362fd55f863d1ec9e6", 907820240,
|
||||
"data.adb", "98d6a1e673e1d3f21be8c89fff25bc4a", 2169482),
|
||||
Common::EN_USA,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DROPPLATFORM | ADGF_CD | ADGF_UNSUPPORTED,
|
||||
GUIO1(GUIO_NONE)},
|
||||
{"black-mirror",
|
||||
0,
|
||||
AD_ENTRY2s(
|
||||
"gfx1.grp", "a5f84365d1e15a8403237fa3ad339f86", 850840170,
|
||||
"data.adb", "5f98eabecf94569be7046d3813edc49a", 2169111),
|
||||
Common::EN_GRB,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DROPPLATFORM | ADGF_CD,
|
||||
GUIO1(GUIO_NONE)},
|
||||
{"black-mirror",
|
||||
"Demo",
|
||||
AD_ENTRY2s(
|
||||
"gfx_en.grp", "7539f35f9242461114c8746b62f884a6", 67516239,
|
||||
"data.adb", "56680d118afddf9477c57e508d118dc8", 114764),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DROPPLATFORM | ADGF_DEMO | ADGF_UNSTABLE,
|
||||
GUIO1(GUIO_NONE)},
|
||||
{"black-mirror",
|
||||
0,
|
||||
AD_ENTRY2s(
|
||||
"gfx1.grp", "652f931f02c5a79fb9bcbe32abafbdf7", 907732355,
|
||||
"data.adb", "d8706b17fb89d58d4dba094a73e5490a", 2152794),
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DROPPLATFORM,
|
||||
GUIO1(GUIO_NONE)},
|
||||
|
||||
// NiBiRu
|
||||
{
|
||||
"nibiru",
|
||||
0,
|
||||
AD_ENTRY2s(
|
||||
"gfx1.grp", "40a7a88f77c35305b6aba0329ed8a9ac", 381768750,
|
||||
"data.adb", "40a7a88f77c35305b6aba0329ed8a9ac", 1391440),
|
||||
Common::EN_USA,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DROPPLATFORM | AGDS_V2 | ADGF_UNSTABLE,
|
||||
GUIO1(GUIO_NONE)},
|
||||
{"nibiru",
|
||||
0,
|
||||
AD_ENTRY2s(
|
||||
"gfx1.grp", "c8e711bc01b16cd82849cbd996d02642", 381768360,
|
||||
"data.adb", "40a7a88f77c35305b6aba0329ed8a9ac", 1391440),
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DROPPLATFORM | AGDS_V2 | ADGF_UNSTABLE,
|
||||
GUIO1(GUIO_NONE)},
|
||||
|
||||
AD_TABLE_END_MARKER};
|
||||
|
||||
} // End of namespace AGDS
|
||||
251
engines/agds/dialog.cpp
Normal file
251
engines/agds/dialog.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
/* 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 "agds/dialog.h"
|
||||
#include "agds/agds.h"
|
||||
#include "agds/object.h"
|
||||
#include "agds/systemVariable.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
void Dialog::parseDialogDefs(const Common::String &defs) {
|
||||
_dialogDefs.clear();
|
||||
Common::String name, value;
|
||||
bool readName = true;
|
||||
for (uint32 p = 0, size = defs.size(); p < size; ++p) {
|
||||
char ch = defs[p];
|
||||
if (ch == ' ') {
|
||||
continue;
|
||||
} else if (ch == '\n' || ch == '\r' || p + 1 == size) {
|
||||
if (p + 1 == size)
|
||||
value += ch;
|
||||
// debug("dialog definition: '%s' = '%s'", name.c_str(), value.c_str());
|
||||
if (!name.empty() && !value.empty()) {
|
||||
_dialogDefs[name] = atoi(value.c_str());
|
||||
}
|
||||
readName = true;
|
||||
name.clear();
|
||||
value.clear();
|
||||
continue;
|
||||
} else if (ch == '=') {
|
||||
if (readName) {
|
||||
readName = false;
|
||||
} else {
|
||||
warning("equal sign in value, skipping");
|
||||
}
|
||||
} else {
|
||||
if (readName)
|
||||
name += ch;
|
||||
else
|
||||
value += ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Dialog::load(const Common::String &processName, const Common::String &dialogScript, const Common::String &defs) {
|
||||
_currentDef.clear();
|
||||
_sounds.clear();
|
||||
parseDialogDefs(defs);
|
||||
_dialogProcessName = processName;
|
||||
_dialogScript = dialogScript;
|
||||
_dialogScriptPos = 0;
|
||||
_engine->getSystemVariable("dialog_var")->setInteger(-1);
|
||||
}
|
||||
|
||||
int Dialog::textDelay(const Common::String &str) {
|
||||
int speed = _engine->getSystemVariable("text_speed")->getInteger();
|
||||
if (speed < 0)
|
||||
speed = 0;
|
||||
else if (speed > 100)
|
||||
speed = 100;
|
||||
speed /= 10;
|
||||
|
||||
int delay = str.size();
|
||||
if (delay < 20)
|
||||
delay = 20;
|
||||
|
||||
// this looks like an error in original code
|
||||
// original first value in this table is 0x0FFFFFEB4 (-332)
|
||||
// 0x0FFFFFEB4 * 20 and then 16-to-32 conversion gives 0xffffe610
|
||||
// 0xe610 / 20 = 2944
|
||||
static const int delays[] = {
|
||||
2944, 631, 398, 251, 158,
|
||||
100, 63, 40, 25, 16, 10};
|
||||
return delays[speed] * delay / 41 + 1;
|
||||
}
|
||||
|
||||
bool Dialog::tick() {
|
||||
if (_dialogProcessName.empty())
|
||||
return false;
|
||||
|
||||
auto dialog_var = _engine->getSystemVariable("dialog_var");
|
||||
int dialog_var_value = dialog_var->getInteger();
|
||||
if (dialog_var_value != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint n = _dialogScript.size();
|
||||
if (_dialogScriptPos >= n) {
|
||||
|
||||
if (!_dialogProcessName.empty()) {
|
||||
debug("end of dialog, running %s", _dialogProcessName.c_str());
|
||||
dialog_var->setInteger(-2);
|
||||
_engine->reactivate(_dialogProcessName, "end of dialog");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::String &line = _dialogLine;
|
||||
line.clear();
|
||||
|
||||
bool command = _dialogScript[_dialogScriptPos] == '@';
|
||||
while (_dialogScriptPos < n) {
|
||||
if (!command && _dialogScript[_dialogScriptPos] == '@')
|
||||
break;
|
||||
|
||||
while (_dialogScriptPos < n && _dialogScript[_dialogScriptPos] != '\n' && _dialogScript[_dialogScriptPos] != '\r') {
|
||||
line += _dialogScript[_dialogScriptPos++];
|
||||
}
|
||||
if (!command)
|
||||
line += '\n';
|
||||
|
||||
while (_dialogScriptPos < n && (_dialogScript[_dialogScriptPos] == '\n' || _dialogScript[_dialogScriptPos] == '\r'))
|
||||
++_dialogScriptPos;
|
||||
|
||||
if (command)
|
||||
break;
|
||||
}
|
||||
|
||||
if (line.empty())
|
||||
return true;
|
||||
|
||||
debug("dialog line: %s", line.c_str());
|
||||
if (line[0] == '@') {
|
||||
processDirective(line);
|
||||
} else {
|
||||
debug("text: %s", line.c_str() + 1);
|
||||
dialog_var->setInteger(-3);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Dialog::processSoundDirective(const Common::String &line) {
|
||||
debug("sound: %s", line.c_str());
|
||||
auto arg1 = line.find('(');
|
||||
if (arg1 == line.npos) {
|
||||
warning("invalid sound directive");
|
||||
return;
|
||||
}
|
||||
++arg1;
|
||||
|
||||
auto comma1 = line.find(',', arg1);
|
||||
if (comma1 == line.npos) {
|
||||
warning("invalid sound directive, missing arg2");
|
||||
return;
|
||||
}
|
||||
++comma1;
|
||||
auto comma2 = line.find(',', comma1);
|
||||
if (comma2 == line.npos) {
|
||||
warning("invalid sound directive, missing arg3");
|
||||
return;
|
||||
}
|
||||
auto end = line.find(')', comma2 + 1);
|
||||
if (end == line.npos) {
|
||||
warning("invalid sound directive");
|
||||
return;
|
||||
}
|
||||
--end;
|
||||
Common::String name = line.substr(arg1, comma1 - arg1 - 1);
|
||||
while (line[comma1] == ' ')
|
||||
++comma1;
|
||||
Common::String sample = line.substr(comma1, comma2 - comma1);
|
||||
while (line[comma2] == ' ')
|
||||
++comma2;
|
||||
Common::String step = line.substr(comma2 + 1, end - comma2);
|
||||
debug("sound args = %s,%s,%s", name.c_str(), sample.c_str(), step.c_str());
|
||||
_sounds.push_back(Sound(name, sample, atoi(step.c_str())));
|
||||
}
|
||||
|
||||
void Dialog::processDirective(Common::String line) {
|
||||
if (line.size() < 2 || line[1] == '@') {
|
||||
debug("comment, bailing out");
|
||||
return;
|
||||
}
|
||||
|
||||
line.erase(0, 1);
|
||||
|
||||
if (line.hasPrefix("sound")) {
|
||||
processSoundDirective(line);
|
||||
} else {
|
||||
DialogDefsType::const_iterator it = _dialogDefs.find(line);
|
||||
if (it != _dialogDefs.end()) {
|
||||
int value = it->_value;
|
||||
_currentDef = line;
|
||||
if (!_currentDef.hasPrefix("vybervarianty") && !_currentDef.hasPrefix("varianta")) {
|
||||
_currentSoundIndex = -1;
|
||||
|
||||
for (uint s = 0; s < _sounds.size(); ++s) {
|
||||
auto &sound = _sounds[s];
|
||||
if (_currentDef.hasPrefixIgnoreCase(sound.Name)) {
|
||||
_currentSoundIndex = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
debug("dialog value %s = %d (0x%04x), sample index: %d", line.c_str(), value, value, _currentSoundIndex);
|
||||
_engine->getSystemVariable("dialog_var")->setInteger(value);
|
||||
} else
|
||||
warning("invalid dialog directive: %s", line.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
Common::String Dialog::getNextDialogSound() {
|
||||
if (_currentDef.empty())
|
||||
return Common::String();
|
||||
|
||||
debug("getNextDialogSound %s %d", _currentDef.c_str(), _currentSoundIndex);
|
||||
if (_currentSoundIndex < 0)
|
||||
return Common::String();
|
||||
|
||||
auto &sound = _sounds[_currentSoundIndex];
|
||||
auto &sample = sound.Sample;
|
||||
auto currentSample = sample;
|
||||
|
||||
int carry = sound.Step;
|
||||
for (auto pos = sample.size() - 1; pos > 0 && sample[pos] >= '0' && sample[pos] <= '9'; --pos) {
|
||||
int d = sample[pos] - '0';
|
||||
d += carry;
|
||||
carry = d / 10;
|
||||
sample.setChar('0' + (d % 10), pos);
|
||||
if (carry == 0)
|
||||
break;
|
||||
}
|
||||
if (carry != 0)
|
||||
warning("sample index overflow, %s", sample.c_str());
|
||||
|
||||
debug("returning sample name %s", currentSample.c_str());
|
||||
return currentSample + ".ogg";
|
||||
}
|
||||
|
||||
} // namespace AGDS
|
||||
81
engines/agds/dialog.h
Normal file
81
engines/agds/dialog.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/* 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 DIALOG_H
|
||||
#define DIALOG_H
|
||||
|
||||
#include "common/hash-str.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace AGDS {
|
||||
class AGDSEngine;
|
||||
|
||||
class Dialog {
|
||||
using DialogDefsType = Common::HashMap<Common::String, int, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo>;
|
||||
|
||||
private:
|
||||
struct Sound {
|
||||
Common::String Name;
|
||||
Common::String Sample;
|
||||
int Step;
|
||||
|
||||
Sound() : Step(1) {
|
||||
}
|
||||
|
||||
Sound(const Common::String &name, const Common::String &sample, int step) : Name(name), Sample(sample), Step(step) {
|
||||
}
|
||||
};
|
||||
using SoundsType = Common::Array<Sound>;
|
||||
|
||||
AGDSEngine *_engine;
|
||||
DialogDefsType _dialogDefs;
|
||||
Common::String _dialogScript;
|
||||
uint32 _dialogScriptPos;
|
||||
Common::String _dialogProcessName;
|
||||
Common::String _dialogLine;
|
||||
|
||||
SoundsType _sounds;
|
||||
Common::String _currentDef;
|
||||
int _currentSoundIndex;
|
||||
|
||||
void parseDialogDefs(const Common::String &defs);
|
||||
|
||||
public:
|
||||
Dialog(AGDSEngine *engine) : _engine(engine), _dialogScriptPos(0), _currentSoundIndex(-1) {}
|
||||
int textDelay(const Common::String &str);
|
||||
const Common::String &getNextDialogLine() const {
|
||||
return _dialogLine;
|
||||
}
|
||||
Common::String getNextDialogSound();
|
||||
|
||||
void load(const Common::String &processName, const Common::String &dialogScript, const Common::String &defs);
|
||||
bool tick();
|
||||
|
||||
private:
|
||||
void processSoundDirective(const Common::String &line);
|
||||
void processDirective(Common::String line);
|
||||
};
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif /* AGDS_DIALOG_H */
|
||||
60
engines/agds/font.cpp
Normal file
60
engines/agds/font.cpp
Normal 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 "agds/font.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
Font::Font(Graphics::ManagedSurface *surface, int gw, int gh) : _surface(surface),
|
||||
_glyphW(gw), _glyphH(gh),
|
||||
_cellW(surface->w / 16), _cellH(surface->h / 16) {
|
||||
|
||||
// debug("surface cell %dx%d", _cellW, _cellH);
|
||||
for (int y = 0; y < 16; ++y) {
|
||||
for (int x = 0; x < 16; ++x) {
|
||||
const uint32 *pixels = static_cast<uint32 *>(_surface->getBasePtr(x * _cellW, y * _cellH));
|
||||
int w;
|
||||
int ch = (y << 4) | x;
|
||||
for (w = 0; w <= _glyphW; ++w, ++pixels) {
|
||||
uint8 r, g, b, a;
|
||||
// debug("%d color #%08x", ch, *pixels);
|
||||
surface->format.colorToARGB(*pixels, r, g, b, a);
|
||||
// debug("%d %d %d %d", r, g, b, a);
|
||||
if (r == 0) // fixme: mapped incorrectly
|
||||
break;
|
||||
}
|
||||
_width[ch] = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Font::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
|
||||
if (chr >= 0x100)
|
||||
return;
|
||||
|
||||
Common::Rect srcRect(getCharWidth(chr), _glyphH);
|
||||
srcRect.moveTo(_cellW * (chr & 0x0f), _cellH * (chr >> 4));
|
||||
if (!srcRect.isEmpty())
|
||||
_surface->blendBlitTo(*dst, x, y, Graphics::FLIP_NONE, &srcRect);
|
||||
}
|
||||
|
||||
} // namespace AGDS
|
||||
60
engines/agds/font.h
Normal file
60
engines/agds/font.h
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FONT_H
|
||||
#define FONT_H
|
||||
|
||||
#include "common/ptr.h"
|
||||
#include "graphics/font.h"
|
||||
|
||||
namespace Graphics {
|
||||
class ManagedSurface;
|
||||
}
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
class Font : public Graphics::Font {
|
||||
Common::ScopedPtr<Graphics::ManagedSurface> _surface;
|
||||
int _glyphW, _glyphH;
|
||||
int _cellW, _cellH;
|
||||
uint8 _width[0x100];
|
||||
|
||||
public:
|
||||
Font(Graphics::ManagedSurface *surface, int gw, int gh);
|
||||
|
||||
virtual int getFontHeight() const {
|
||||
return _glyphH;
|
||||
}
|
||||
|
||||
virtual int getMaxCharWidth() const {
|
||||
return _glyphW;
|
||||
}
|
||||
|
||||
virtual int getCharWidth(uint32 chr) const {
|
||||
return chr < 0x100 ? _width[chr] : 0;
|
||||
}
|
||||
|
||||
virtual void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const;
|
||||
};
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif /* AGDS_FONT_H */
|
||||
210
engines/agds/inventory.cpp
Normal file
210
engines/agds/inventory.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
/* 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 "agds/inventory.h"
|
||||
#include "agds/agds.h"
|
||||
#include "agds/object.h"
|
||||
#include "agds/resourceManager.h"
|
||||
#include "agds/systemVariable.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
Inventory::Inventory(AGDSEngine *engine) : _engine(engine), _entries(kMaxSize), _enabled(false), _visible(false) {}
|
||||
Inventory::~Inventory() {}
|
||||
|
||||
void Inventory::visible(bool visible) {
|
||||
if (_visible == visible)
|
||||
return;
|
||||
|
||||
_visible = visible;
|
||||
|
||||
if (!visible) {
|
||||
debug("closing inventory...");
|
||||
Common::String inv_close = _engine->getSystemVariable("inv_close")->getString();
|
||||
if (!inv_close.empty())
|
||||
_engine->runObject(inv_close);
|
||||
} else {
|
||||
debug("opening inventory...");
|
||||
if (!_engine->currentInventoryObject())
|
||||
removeGaps();
|
||||
Common::String inv_open = _engine->getSystemVariable("inv_open")->getString();
|
||||
if (!inv_open.empty())
|
||||
_engine->runObject(inv_open);
|
||||
}
|
||||
}
|
||||
|
||||
int Inventory::free() const {
|
||||
int free = 0;
|
||||
for (uint i = 0; i < _entries.size(); ++i)
|
||||
if (!_entries[i].hasObject)
|
||||
++free;
|
||||
return free;
|
||||
}
|
||||
|
||||
bool Inventory::has(int index) const {
|
||||
if (index >= 0 && index < kMaxSize) {
|
||||
auto &entry = _entries[index];
|
||||
return entry.hasObject;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ObjectPtr Inventory::get(int index) {
|
||||
if (index >= 0 && index < kMaxSize) {
|
||||
auto &entry = _entries[index];
|
||||
if (entry.hasObject && !entry.object) {
|
||||
entry.object = _engine->runObject(entry.name);
|
||||
entry.object->persistent(false);
|
||||
}
|
||||
return entry.object;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
int Inventory::add(const Common::String &name) {
|
||||
int idx = find(name);
|
||||
if (idx >= 0) {
|
||||
warning("Double adding object %s, skipping...", name.c_str());
|
||||
return idx;
|
||||
}
|
||||
for (uint i = 0; i < _entries.size(); ++i) {
|
||||
auto &entry = _entries[i];
|
||||
if (!entry.hasObject) {
|
||||
entry.name = name;
|
||||
entry.object.reset();
|
||||
entry.hasObject = true;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
int Inventory::add(const ObjectPtr &object) {
|
||||
for (uint i = 0; i < _entries.size(); ++i) {
|
||||
auto &entry = _entries[i];
|
||||
if (entry.hasObject && entry.name == object->getName()) {
|
||||
warning("Double adding object [pointer] %s, skipping...", object->getName().c_str());
|
||||
return i;
|
||||
}
|
||||
}
|
||||
object->persistent(false);
|
||||
for (uint i = 0; i < _entries.size(); ++i) {
|
||||
auto &entry = _entries[i];
|
||||
if (!entry.hasObject) {
|
||||
entry.name = object->getName();
|
||||
entry.object = object;
|
||||
entry.hasObject = true;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
error("inventory overflow");
|
||||
}
|
||||
|
||||
bool Inventory::remove(const Common::String &name) {
|
||||
bool removed = false;
|
||||
for (uint i = 0; i < _entries.size(); ++i) {
|
||||
auto &entry = _entries[i];
|
||||
if (entry.hasObject && entry.name == name) {
|
||||
entry.reset();
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
void Inventory::removeGaps() {
|
||||
auto n = _entries.size();
|
||||
for (uint src = 0, dst = 0; src < n; ++src) {
|
||||
auto &entry = _entries[src];
|
||||
if (entry.hasObject) {
|
||||
if (dst != src) {
|
||||
debug("moving inventory object %u -> %u", src, dst);
|
||||
_entries[dst++] = _entries[src];
|
||||
_entries[src].reset();
|
||||
} else
|
||||
++dst;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Inventory::find(const Common::String &name) const {
|
||||
for (uint i = 0; i < _entries.size(); ++i) {
|
||||
auto &entry = _entries[i];
|
||||
if (entry.hasObject && entry.name == name)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
ObjectPtr Inventory::find(const Common::Point pos) const {
|
||||
if (!_enabled)
|
||||
return {};
|
||||
|
||||
for (uint i = 0; i < _entries.size(); ++i) {
|
||||
auto &entry = _entries[i];
|
||||
if (!entry.object)
|
||||
continue;
|
||||
|
||||
auto &object = entry.object;
|
||||
auto picture = object->getPicture();
|
||||
if (picture) {
|
||||
auto rect = picture->getBounds();
|
||||
rect.moveTo(object->getPosition());
|
||||
if (rect.contains(pos))
|
||||
return object;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void Inventory::clear() {
|
||||
for (uint i = 0; i < _entries.size(); ++i) {
|
||||
_entries[i].reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Inventory::load(Common::ReadStream &stream) {
|
||||
clear();
|
||||
for (int i = 0; i < kMaxSize; ++i) {
|
||||
Common::String name = readString(stream);
|
||||
int refcount = stream.readUint32LE();
|
||||
int objectPtr = stream.readUint32LE();
|
||||
if (!name.empty() && refcount) {
|
||||
debug("load inventory object: %s %d %d", name.c_str(), refcount, objectPtr);
|
||||
auto &entry = _entries[i];
|
||||
entry.name = name;
|
||||
entry.hasObject = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Inventory::save(Common::WriteStream &stream) const {
|
||||
for (const auto &entry : _entries) {
|
||||
writeString(stream, entry.name);
|
||||
stream.writeUint32LE(entry.hasObject ? 1 : 0);
|
||||
stream.writeSint32LE(entry.hasObject ? -1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AGDS
|
||||
105
engines/agds/inventory.h
Normal file
105
engines/agds/inventory.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/* 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 INVENTORY_H
|
||||
#define INVENTORY_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/ptr.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Common {
|
||||
class ReadStream;
|
||||
class WriteStream;
|
||||
} // namespace Common
|
||||
|
||||
namespace AGDS {
|
||||
class Object;
|
||||
using ObjectPtr = Common::SharedPtr<Object>;
|
||||
class AGDSEngine;
|
||||
|
||||
class Inventory {
|
||||
class AGDSEngine *_engine;
|
||||
struct Entry {
|
||||
Common::String name;
|
||||
bool hasObject;
|
||||
ObjectPtr object;
|
||||
|
||||
void reset() {
|
||||
name.clear();
|
||||
hasObject = false;
|
||||
object.reset();
|
||||
}
|
||||
};
|
||||
using EntriesType = Common::Array<Entry>;
|
||||
EntriesType _entries;
|
||||
bool _enabled;
|
||||
bool _visible;
|
||||
|
||||
public:
|
||||
static const int kMaxSize = 35;
|
||||
|
||||
Inventory(AGDSEngine *engine);
|
||||
~Inventory();
|
||||
|
||||
void load(Common::ReadStream &stream);
|
||||
void save(Common::WriteStream &stream) const;
|
||||
|
||||
bool enabled() const {
|
||||
return _enabled;
|
||||
}
|
||||
void enable(bool enabled) {
|
||||
_enabled = enabled;
|
||||
}
|
||||
|
||||
bool visible() const {
|
||||
return _visible;
|
||||
}
|
||||
|
||||
void visible(bool visible);
|
||||
|
||||
int add(const Common::String &name);
|
||||
int add(const ObjectPtr &object);
|
||||
bool remove(const Common::String &name);
|
||||
void removeGaps();
|
||||
|
||||
bool has(int index) const;
|
||||
ObjectPtr get(int index);
|
||||
int find(const Common::String &name) const;
|
||||
ObjectPtr find(const Common::Point pos) const;
|
||||
|
||||
int free() const;
|
||||
void clear();
|
||||
|
||||
const EntriesType &entries() const {
|
||||
return _entries;
|
||||
}
|
||||
|
||||
uint maxSize() const {
|
||||
return _entries.size();
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif /* AGDS_INVENTORY_H */
|
||||
89
engines/agds/metaengine.cpp
Normal file
89
engines/agds/metaengine.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
/* 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 "agds/agds.h"
|
||||
#include "agds/object.h"
|
||||
#include "base/plugins.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/system.h"
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
class AGDSMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
|
||||
public:
|
||||
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
|
||||
|
||||
bool hasFeature(MetaEngineFeature f) const override {
|
||||
switch (f) {
|
||||
case kSimpleSavesNames:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const char *getName() const override {
|
||||
return "agds";
|
||||
}
|
||||
|
||||
int getMaximumSaveSlot() const override {
|
||||
return 24;
|
||||
}
|
||||
|
||||
SaveStateList listSaves(const char *target) const override {
|
||||
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
|
||||
Common::StringArray filenames;
|
||||
Common::String pattern(getSavegameFilePattern(target));
|
||||
|
||||
filenames = saveFileMan->listSavefiles(pattern);
|
||||
|
||||
SaveStateList saveList;
|
||||
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
|
||||
// Obtain the last 2 digits of the filename, since they correspond to the save slot
|
||||
int slotNum = atoi(file->c_str() + file->size() - 2);
|
||||
|
||||
if (slotNum >= 0 && slotNum <= getMaximumSaveSlot()) {
|
||||
SaveStateDescriptor desc;
|
||||
|
||||
desc.setSaveSlot(slotNum);
|
||||
desc.setDescription(*file);
|
||||
if (slotNum == getAutosaveSlot())
|
||||
desc.setWriteProtectedFlag(true);
|
||||
|
||||
saveList.push_back(desc);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort saves based on slot number.
|
||||
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
|
||||
return saveList;
|
||||
}
|
||||
};
|
||||
|
||||
Common::Error AGDSMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
|
||||
*engine = new AGDS::AGDSEngine(syst, desc);
|
||||
return Common::Error(Common::kNoError);
|
||||
}
|
||||
|
||||
#if PLUGIN_ENABLED_DYNAMIC(AGDS)
|
||||
REGISTER_PLUGIN_DYNAMIC(AGDS, PLUGIN_TYPE_ENGINE, AGDSMetaEngine);
|
||||
#else
|
||||
REGISTER_PLUGIN_STATIC(AGDS, PLUGIN_TYPE_ENGINE, AGDSMetaEngine);
|
||||
#endif
|
||||
139
engines/agds/mjpgPlayer.cpp
Normal file
139
engines/agds/mjpgPlayer.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/* 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 "agds/mjpgPlayer.h"
|
||||
#include "agds/agds.h"
|
||||
#include "agds/font.h"
|
||||
#include "agds/object.h"
|
||||
#include "agds/systemVariable.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
MJPGPlayer::MJPGPlayer(Common::SeekableReadStream *stream, const Common::String &subtitles) : _stream(stream), _firstFramePos(_stream->pos()), _framesPlayed(0), _nextSubtitleIndex(0) {
|
||||
uint pos = 0;
|
||||
while (pos < subtitles.size()) {
|
||||
auto next = subtitles.find('\n', pos);
|
||||
if (next == subtitles.npos)
|
||||
next = subtitles.size();
|
||||
Common::String line = subtitles.substr(pos, next - pos);
|
||||
|
||||
uint begin, end;
|
||||
int offset;
|
||||
if (sscanf(line.c_str(), "{%u}{%u}%n", &begin, &end, &offset) != 2) {
|
||||
warning("malformed subtitle line: %s", line.c_str());
|
||||
pos = next + 1;
|
||||
continue;
|
||||
}
|
||||
while (offset < static_cast<int>(line.size()) && line[offset] == ' ')
|
||||
++offset;
|
||||
|
||||
line = line.substr(offset);
|
||||
debug("subtitle line: %d-%d: %s", begin, end, line.c_str());
|
||||
Text::Lines lines;
|
||||
{
|
||||
uint lineBegin = 0;
|
||||
while (lineBegin < line.size()) {
|
||||
uint lineEnd = line.find('|', lineBegin);
|
||||
if (lineEnd == line.npos)
|
||||
lineEnd = line.size();
|
||||
lines.push_back(line.substr(lineBegin, lineEnd - lineBegin));
|
||||
lineBegin = lineEnd + 1;
|
||||
}
|
||||
}
|
||||
_subtitles.push_back({begin, end, lines});
|
||||
pos = next + 1;
|
||||
}
|
||||
}
|
||||
|
||||
MJPGPlayer::~MJPGPlayer() {}
|
||||
|
||||
void MJPGPlayer::rewind() {
|
||||
_framesPlayed = 0;
|
||||
_nextSubtitleIndex = 0;
|
||||
_stream->seek(_firstFramePos);
|
||||
}
|
||||
|
||||
const Graphics::Surface *MJPGPlayer::decodeFrame() {
|
||||
if (_stream->eos())
|
||||
return NULL;
|
||||
|
||||
uint32 size = _stream->readSint32LE();
|
||||
if (size == 0)
|
||||
return NULL;
|
||||
|
||||
if (_stream->eos())
|
||||
return NULL;
|
||||
Common::ScopedPtr<Common::SeekableReadStream> stream(_stream->readStream(size));
|
||||
const Graphics::Surface *surface = _decoder.decodeFrame(*stream);
|
||||
++_framesPlayed;
|
||||
return surface;
|
||||
}
|
||||
|
||||
void MJPGPlayer::paint(AGDSEngine &engine, Graphics::Surface &backbuffer) {
|
||||
auto *surface = decodeFrame();
|
||||
if (surface) {
|
||||
Common::ScopedPtr<Graphics::Surface> scaled;
|
||||
{
|
||||
Common::ScopedPtr<Graphics::Surface> converted(surface->convertTo(engine.pixelFormat()));
|
||||
auto scaleW = 1.0f * backbuffer.w / converted->w;
|
||||
auto scaleH = 1.0f * backbuffer.h / converted->h;
|
||||
auto scale = MIN(scaleW, scaleH);
|
||||
|
||||
scaled.reset(converted->scale(converted->w * scale, converted->h * scale));
|
||||
converted->free();
|
||||
}
|
||||
|
||||
Common::Point dst((backbuffer.w - scaled->w) / 2, (backbuffer.h - scaled->h) / 2);
|
||||
Common::Rect srcRect(scaled->getRect());
|
||||
if (Common::Rect::getBlitRect(dst, srcRect, backbuffer.getRect()))
|
||||
backbuffer.copyRectToSurface(*scaled, dst.x, dst.y, srcRect);
|
||||
scaled->free();
|
||||
}
|
||||
|
||||
if (_subtitles.empty())
|
||||
return;
|
||||
|
||||
while (_nextSubtitleIndex < _subtitles.size() && _framesPlayed > _subtitles[_nextSubtitleIndex].end)
|
||||
++_nextSubtitleIndex;
|
||||
|
||||
if (_nextSubtitleIndex >= _subtitles.size() || _framesPlayed < _subtitles[_nextSubtitleIndex].begin)
|
||||
return;
|
||||
|
||||
auto font = engine.getFont(engine.getSystemVariable("tell_font")->getInteger());
|
||||
int baseX = engine.getSystemVariable("subtitle_x")->getInteger();
|
||||
int baseY = engine.getSystemVariable("subtitle_y")->getInteger();
|
||||
int maxWidth = engine.getSystemVariable("subtitle_width")->getInteger();
|
||||
|
||||
auto &lines = _subtitles[_nextSubtitleIndex].lines;
|
||||
for (auto &line : lines) {
|
||||
Text::Lines sublines;
|
||||
font->wordWrapText(line, maxWidth, sublines);
|
||||
for (auto &subline : sublines) {
|
||||
int w = font->getStringWidth(subline);
|
||||
int x = baseX - w / 2;
|
||||
font->drawString(&backbuffer, subline, x, baseY, backbuffer.w - x, 0);
|
||||
baseY += font->getFontHeight();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AGDS
|
||||
72
engines/agds/mjpgPlayer.h
Normal file
72
engines/agds/mjpgPlayer.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MJPG_PLAYER_H
|
||||
#define MJPG_PLAYER_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
#include "common/stream.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "image/jpeg.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
class AGDSEngine;
|
||||
class MJPGPlayer {
|
||||
using StreamPtr = Common::ScopedPtr<Common::SeekableReadStream>;
|
||||
StreamPtr _stream;
|
||||
int32 _firstFramePos;
|
||||
Image::JPEGDecoder _decoder;
|
||||
uint _framesPlayed;
|
||||
|
||||
struct Text {
|
||||
using Lines = Common::Array<Common::String>;
|
||||
|
||||
uint begin;
|
||||
uint end;
|
||||
Lines lines;
|
||||
};
|
||||
|
||||
Common::Array<Text> _subtitles;
|
||||
uint _nextSubtitleIndex;
|
||||
|
||||
public:
|
||||
MJPGPlayer(Common::SeekableReadStream *stream, const Common::String &subtitles);
|
||||
~MJPGPlayer();
|
||||
|
||||
bool eos() {
|
||||
return _stream->eos();
|
||||
}
|
||||
|
||||
void rewind();
|
||||
const Graphics::Surface *decodeFrame();
|
||||
void paint(AGDSEngine &engine, Graphics::Surface &backbuffer);
|
||||
|
||||
uint32 getNextFrameTimestamp() const {
|
||||
return _framesPlayed * 1000 / 24;
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif /* AGDS_MJPG_PLAYER_H */
|
||||
35
engines/agds/module.mk
Normal file
35
engines/agds/module.mk
Normal file
@@ -0,0 +1,35 @@
|
||||
MODULE := engines/agds
|
||||
|
||||
MODULE_OBJS := \
|
||||
agds.o \
|
||||
animation.o \
|
||||
character.o \
|
||||
console.o \
|
||||
database.o \
|
||||
dialog.o \
|
||||
font.o \
|
||||
inventory.o \
|
||||
metaengine.o \
|
||||
mjpgPlayer.o \
|
||||
mouseMap.o \
|
||||
object.o \
|
||||
patch.o \
|
||||
process.o \
|
||||
process_opcodes.o \
|
||||
region.o \
|
||||
resourceManager.o \
|
||||
screen.o \
|
||||
soundManager.o \
|
||||
systemVariable.o \
|
||||
textLayout.o
|
||||
|
||||
# This module can be built as a plugin
|
||||
ifeq ($(ENABLE_AGDS), DYNAMIC_PLUGIN)
|
||||
PLUGIN := 1
|
||||
endif
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
||||
|
||||
# Detection objects
|
||||
DETECT_OBJS += $(MODULE)/detection.o
|
||||
106
engines/agds/mouseMap.cpp
Normal file
106
engines/agds/mouseMap.cpp
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "agds/mouseMap.h"
|
||||
#include "agds/agds.h"
|
||||
#include "agds/object.h"
|
||||
#include "agds/region.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
int MouseMap::findFree() const {
|
||||
for (int i = 0, n = _mouseRegions.size(); i != n; ++i) {
|
||||
auto ®ion = _mouseRegions[i];
|
||||
if (!region)
|
||||
return i;
|
||||
}
|
||||
error("no mouse region available");
|
||||
}
|
||||
|
||||
int MouseMap::add(MouseRegion area) {
|
||||
auto id = findFree();
|
||||
auto ®ion = _mouseRegions[id];
|
||||
region.reset(new MouseRegion(Common::move(area)));
|
||||
region->id = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
void MouseMap::hideInactive(AGDSEngine *engine, Common::Point pos) {
|
||||
for (auto ®ion : _mouseRegions) {
|
||||
if (!region || !region->enabled)
|
||||
continue;
|
||||
|
||||
if (_disabled) {
|
||||
region->hide(engine);
|
||||
} else if (engine->userEnabled()) {
|
||||
if (!region->region->pointIn(pos)) {
|
||||
region->hide(engine);
|
||||
} else if (!region->visible) {
|
||||
region->show(engine);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseRegion *MouseMap::find(int id) {
|
||||
for (auto ®ion : _mouseRegions) {
|
||||
if (region && region->id == id)
|
||||
return region.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MouseMap::remove(int id) {
|
||||
auto ®ion = _mouseRegions[id];
|
||||
if (!region) {
|
||||
warning("removing non-existent mouse region %d", id);
|
||||
return;
|
||||
}
|
||||
region->disable();
|
||||
region.reset();
|
||||
}
|
||||
|
||||
void MouseRegion::show(AGDSEngine *engine) {
|
||||
if (visible)
|
||||
return;
|
||||
|
||||
visible = true;
|
||||
debug("calling mouseArea[%d].onEnter: %s", id, onEnter.c_str());
|
||||
engine->runObject(onEnter);
|
||||
}
|
||||
|
||||
void MouseRegion::hide(AGDSEngine *engine) {
|
||||
if (!visible)
|
||||
return;
|
||||
|
||||
visible = false;
|
||||
debug("calling mouseArea[%d].onLeave: %s...", id, onLeave.c_str());
|
||||
engine->runObject(onLeave);
|
||||
}
|
||||
|
||||
void MouseMap::hideAll(AGDSEngine *engine) {
|
||||
for (auto ®ion : _mouseRegions)
|
||||
if (region)
|
||||
region->hide(engine);
|
||||
}
|
||||
|
||||
} // End of namespace AGDS
|
||||
90
engines/agds/mouseMap.h
Normal file
90
engines/agds/mouseMap.h
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MOUSE_MAP_H
|
||||
#define MOUSE_MAP_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/ptr.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
class AGDSEngine;
|
||||
struct Region;
|
||||
using RegionPtr = Common::SharedPtr<Region>;
|
||||
|
||||
struct MouseRegion {
|
||||
int id = -1;
|
||||
RegionPtr region = nullptr;
|
||||
bool enabled = true;
|
||||
bool visible = false;
|
||||
|
||||
Common::String onEnter;
|
||||
Common::String onLeave;
|
||||
|
||||
void enable() {
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
void disable() {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
MouseRegion() {}
|
||||
|
||||
MouseRegion(RegionPtr reg, const Common::String &enter, const Common::String &leave) : region(reg), onEnter(enter), onLeave(leave) {
|
||||
}
|
||||
|
||||
void hide(AGDSEngine *engine);
|
||||
void show(AGDSEngine *engine);
|
||||
};
|
||||
using MouseRegionPtr = Common::ScopedPtr<MouseRegion>;
|
||||
|
||||
class MouseMap {
|
||||
Common::Array<MouseRegionPtr> _mouseRegions;
|
||||
bool _disabled;
|
||||
|
||||
public:
|
||||
MouseMap() : _mouseRegions(100), _disabled(false) {}
|
||||
|
||||
void disable(bool disabled) {
|
||||
_disabled = disabled;
|
||||
}
|
||||
|
||||
bool disabled() const {
|
||||
return _disabled;
|
||||
}
|
||||
|
||||
int findFree() const;
|
||||
int add(MouseRegion area);
|
||||
void remove(int id);
|
||||
|
||||
void hideAll(AGDSEngine *engine);
|
||||
|
||||
void hideInactive(AGDSEngine *engine, Common::Point pos);
|
||||
MouseRegion *find(int id);
|
||||
};
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif /* AGDS_SCREEN_H */
|
||||
298
engines/agds/object.cpp
Normal file
298
engines/agds/object.cpp
Normal file
@@ -0,0 +1,298 @@
|
||||
/* 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 "agds/object.h"
|
||||
#include "agds/agds.h"
|
||||
#include "agds/animation.h"
|
||||
#include "agds/font.h"
|
||||
#include "agds/region.h"
|
||||
#include "agds/systemVariable.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/rect.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
Object::Object(const Common::String &name, Common::SeekableReadStream &stream, int version) : _name(name), _stringTableLoaded(false),
|
||||
_picture(), _rotatedPicture(), _region(),
|
||||
_animation(), _mouseCursor(),
|
||||
_pos(), _z(10), _rotation(0),
|
||||
_clickHandler(0), _examineHandler(0), _userUseHandler(0),
|
||||
_throwHandler(0), _useOnHandler(0),
|
||||
_handlerBD(0), _handlerC1(0),
|
||||
_alpha(255), _scale(100), _locked(0), _alive(true),
|
||||
_persistent(true), _allowInitialise(true),
|
||||
_ignoreRegion(false), _version(version) {
|
||||
uint16 id = stream.readUint16LE();
|
||||
debug("id: 0x%02x %u", id, id);
|
||||
|
||||
uint16 dataSize = stream.readUint16LE();
|
||||
if (dataSize != 0)
|
||||
error("implement me: object with data (%u/0x%04x)", dataSize, dataSize);
|
||||
uint16 codeSize = stream.readUint16LE();
|
||||
uint8 flags = stream.readByte();
|
||||
if (flags != 1)
|
||||
error("implement me: no flags handling yet");
|
||||
|
||||
debug("object code size %u", codeSize);
|
||||
_code.resize(codeSize);
|
||||
stream.read(_code.data(), _code.size());
|
||||
}
|
||||
|
||||
Object::~Object() {
|
||||
freeRotated();
|
||||
freePicture();
|
||||
}
|
||||
|
||||
void Object::lock() {
|
||||
++_locked;
|
||||
}
|
||||
|
||||
void Object::unlock() {
|
||||
if (_locked == 0)
|
||||
error("%s: object lock counter underrun", _name.c_str());
|
||||
--_locked;
|
||||
}
|
||||
|
||||
void Object::freeRotated() {
|
||||
if (_rotatedPicture) {
|
||||
_rotatedPicture->free();
|
||||
_rotatedPicture.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Object::freePicture() {
|
||||
if (_picture) {
|
||||
_picture->free();
|
||||
_picture.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Object::readStringTable(unsigned resOffset, uint16 resCount) {
|
||||
if (_stringTableLoaded)
|
||||
return;
|
||||
|
||||
resOffset += 5 /*instruction*/ + (_version >= 2 ? 0x13 : 0x11) /*another header*/;
|
||||
if (resOffset >= _code.size())
|
||||
error("invalid resource table offset %u/%u", resOffset, _code.size());
|
||||
|
||||
// debug("resource table at %04x", resOffset);
|
||||
Common::MemoryReadStream stream(_code.data() + resOffset, _code.size() - resOffset);
|
||||
_stringTable.resize(resCount);
|
||||
for (uint16 i = 0; i < resCount; ++i) {
|
||||
uint16 offset = stream.readUint16LE();
|
||||
uint16 flags = stream.readUint16LE();
|
||||
|
||||
unsigned nameOffset = resOffset + offset;
|
||||
if (nameOffset > _code.size())
|
||||
error("invalid resource name offset %u/%u", nameOffset, _code.size());
|
||||
|
||||
const char *nameBegin = reinterpret_cast<const char *>(_code.data() + nameOffset);
|
||||
const char *codeEnd = reinterpret_cast<const char *>(_code.data() + _code.size());
|
||||
const char *nameEnd = Common::find(nameBegin, codeEnd, 0);
|
||||
|
||||
Common::String name(nameBegin, nameEnd - nameBegin);
|
||||
|
||||
// debug("resource table 1[%04u]: 0x%04x %s", i, flags, name.c_str());
|
||||
_stringTable[i] = StringEntry(name, flags);
|
||||
}
|
||||
debug("loaded %u strings", resCount);
|
||||
_stringTableLoaded = true;
|
||||
}
|
||||
|
||||
const Object::StringEntry &Object::getString(uint16 index) const {
|
||||
if (!_stringTableLoaded)
|
||||
error("no string table loaded");
|
||||
|
||||
if (index >= _stringTable.size()) {
|
||||
static StringEntry empty;
|
||||
warning("no resource name with id %u", index);
|
||||
return empty;
|
||||
}
|
||||
|
||||
return _stringTable[index];
|
||||
}
|
||||
|
||||
void Object::setPicture(Graphics::ManagedSurface *picture) {
|
||||
_pos = Common::Point();
|
||||
freePicture();
|
||||
_picture.reset(picture);
|
||||
freeRotated();
|
||||
_rotation = 0;
|
||||
|
||||
if (!picture) {
|
||||
_offset = Common::Point();
|
||||
return;
|
||||
}
|
||||
|
||||
assert(picture->format.bytesPerPixel == 4);
|
||||
const byte *pixels = static_cast<const byte *>(picture->getPixels());
|
||||
uint pitch = picture->pitch;
|
||||
|
||||
uint16 w = picture->w, h = picture->h;
|
||||
_offset.x = w;
|
||||
_offset.y = h;
|
||||
|
||||
for (uint16 y = 0; y < h; ++y) {
|
||||
for (uint16 x = 0; x < w; ++x) {
|
||||
uint32 color = *reinterpret_cast<const uint32 *>(pixels + (y * pitch + x * picture->format.bytesPerPixel));
|
||||
uint8 r, g, b, a;
|
||||
picture->format.colorToARGB(color, a, r, g, b);
|
||||
if (a != 0) {
|
||||
if (y < _offset.y)
|
||||
_offset.y = y;
|
||||
if (x < _offset.x)
|
||||
_offset.x = x;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
createRotated();
|
||||
}
|
||||
|
||||
void Object::rotate(int rot) {
|
||||
rot %= 4;
|
||||
if (rot == _rotation)
|
||||
return;
|
||||
|
||||
debug("%s: setting rotation to %d", _name.c_str(), rot);
|
||||
_rotation = rot;
|
||||
createRotated();
|
||||
}
|
||||
|
||||
void Object::createRotated() {
|
||||
freeRotated();
|
||||
if (_rotation == 0 || !_picture)
|
||||
return;
|
||||
|
||||
Graphics::TransformStruct transform(100, 100, 90 * _rotation, _picture->w / 2, _picture->h / 2);
|
||||
_rotatedPicture.reset(getPicture()->rotoscale(transform));
|
||||
}
|
||||
|
||||
void Object::alive(bool value) {
|
||||
_alive = value;
|
||||
if (!_alive)
|
||||
_region.reset();
|
||||
}
|
||||
|
||||
void Object::region(RegionPtr region) {
|
||||
_region = region;
|
||||
}
|
||||
|
||||
void Object::moveTo(Common::Point pos) {
|
||||
_pos = pos;
|
||||
}
|
||||
|
||||
void Object::generateRegion(Common::Rect rect) {
|
||||
_region = RegionPtr(new Region(rect));
|
||||
debug("%s: generated region: %s", _name.c_str(), _region->toString().c_str());
|
||||
}
|
||||
|
||||
void Object::generateRegion() {
|
||||
if (!getPicture()) {
|
||||
warning("generateRegion called on null picture");
|
||||
return;
|
||||
}
|
||||
|
||||
generateRegion(getRect());
|
||||
}
|
||||
|
||||
Common::Rect Object::getRect() const {
|
||||
auto picture = getPicture();
|
||||
if (!picture) {
|
||||
warning("getRect called on null picture");
|
||||
return Common::Rect();
|
||||
}
|
||||
Common::Rect rect = picture->getBounds();
|
||||
rect.moveTo(_pos.x, _pos.y);
|
||||
return rect;
|
||||
}
|
||||
|
||||
bool Object::pointIn(Common::Point pos) {
|
||||
if (!_alive)
|
||||
return false;
|
||||
|
||||
auto picture = getPicture();
|
||||
if (picture) {
|
||||
auto rect = getRect();
|
||||
if (rect.contains(pos)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_animation) {
|
||||
Common::Rect rect(0, 0, _animation->width(), _animation->height());
|
||||
rect.moveTo(_pos + _animationPos);
|
||||
if (rect.contains(pos)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// pos -= _pos;
|
||||
pos -= _regionOffset;
|
||||
|
||||
if (_trapRegion && _trapRegion->pointIn(pos))
|
||||
return true;
|
||||
|
||||
if (!_ignoreRegion && _region && _region->pointIn(pos))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Object::paint(AGDSEngine &engine, Graphics::Surface &backbuffer, Common::Point pos) const {
|
||||
auto picture = getPicture();
|
||||
if (picture) {
|
||||
Common::Point dst = pos + getPosition();
|
||||
Common::Rect srcRect = picture->getBounds();
|
||||
if (!_srcRect.isEmpty()) {
|
||||
srcRect = _srcRect;
|
||||
}
|
||||
uint32 color = _picture->format.ARGBToColor(_alpha, 255, 255, 255);
|
||||
if (Common::Rect::getBlitRect(dst, srcRect, backbuffer.getRect())) {
|
||||
picture->blendBlitTo(backbuffer, dst.x, dst.y, Graphics::FLIP_NONE, &srcRect, color);
|
||||
}
|
||||
}
|
||||
|
||||
if (_animation) {
|
||||
pos += _pos + _animationPos;
|
||||
_animation->tick();
|
||||
_animation->paint(backbuffer, pos);
|
||||
}
|
||||
|
||||
if (!_text.empty()) {
|
||||
pos += _region ? _region->center + _regionOffset : _pos;
|
||||
int w = backbuffer.w - pos.x;
|
||||
engine.getFont(engine.getSystemVariable("objtext_font")->getInteger())->drawString(&backbuffer, _text, pos.x, pos.y, w, 0);
|
||||
}
|
||||
|
||||
if (engine.showHints() && !_title.empty()) {
|
||||
pos += _region ? _region->center : _pos;
|
||||
int w = backbuffer.w - pos.x;
|
||||
auto font = engine.getFont(engine.getSystemVariable("tell_font")->getInteger());
|
||||
pos.x -= font->getStringWidth(_title) / 2;
|
||||
font->drawString(&backbuffer, _title, pos.x, pos.y, w, 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AGDS
|
||||
343
engines/agds/object.h
Normal file
343
engines/agds/object.h
Normal file
@@ -0,0 +1,343 @@
|
||||
/* 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 OBJECT_H
|
||||
#define OBJECT_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/hash-str.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/ptr.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
class ManagedSurface;
|
||||
} // namespace Graphics
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
class AGDSEngine;
|
||||
struct Region;
|
||||
using RegionPtr = Common::SharedPtr<Region>;
|
||||
class Animation;
|
||||
using AnimationPtr = Common::SharedPtr<Animation>;
|
||||
|
||||
class Object {
|
||||
public:
|
||||
using CodeType = Common::Array<uint8>;
|
||||
|
||||
struct StringEntry {
|
||||
Common::String string;
|
||||
uint16 flags;
|
||||
|
||||
StringEntry() : string(), flags() {}
|
||||
StringEntry(const Common::String &s, uint16 f) : string(s), flags(f) {}
|
||||
};
|
||||
|
||||
private:
|
||||
using StringTableType = Common::Array<StringEntry>;
|
||||
using KeyHandlersType = Common::HashMap<Common::String, uint, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo>;
|
||||
using UseHandlersType = Common::HashMap<Common::String, uint>;
|
||||
using ManagedSurfacePtr = Common::ScopedPtr<Graphics::ManagedSurface>;
|
||||
|
||||
Common::String _name;
|
||||
CodeType _code;
|
||||
StringTableType _stringTable;
|
||||
bool _stringTableLoaded;
|
||||
KeyHandlersType _keyHandlers;
|
||||
UseHandlersType _useHandlers;
|
||||
ManagedSurfacePtr _picture;
|
||||
ManagedSurfacePtr _rotatedPicture;
|
||||
RegionPtr _region;
|
||||
RegionPtr _trapRegion;
|
||||
AnimationPtr _animation;
|
||||
AnimationPtr _mouseCursor;
|
||||
Common::Point _pos, _animationPos, _offset;
|
||||
Common::Point _regionOffset;
|
||||
Common::Rect _srcRect;
|
||||
int _z;
|
||||
int _rotation;
|
||||
Common::String _text;
|
||||
Common::String _title;
|
||||
uint _clickHandler;
|
||||
uint _examineHandler;
|
||||
uint _userUseHandler;
|
||||
uint _throwHandler;
|
||||
uint _useOnHandler;
|
||||
uint _trapHandler;
|
||||
uint _handlerBD;
|
||||
uint _handlerC1;
|
||||
int _alpha;
|
||||
int _scale;
|
||||
uint _locked;
|
||||
bool _alive;
|
||||
bool _persistent;
|
||||
bool _allowInitialise;
|
||||
bool _ignoreRegion;
|
||||
int _version;
|
||||
|
||||
private:
|
||||
void freeRotated();
|
||||
void freePicture();
|
||||
void createRotated();
|
||||
|
||||
public:
|
||||
Object(const Common::String &name, Common::SeekableReadStream &stream, int version);
|
||||
~Object();
|
||||
|
||||
bool allowInitialise() const {
|
||||
return _allowInitialise;
|
||||
}
|
||||
void allowInitialise(bool allow) {
|
||||
_allowInitialise = allow;
|
||||
}
|
||||
|
||||
void persistent(bool persistent) {
|
||||
_persistent = persistent;
|
||||
}
|
||||
bool persistent() const {
|
||||
return _persistent;
|
||||
}
|
||||
|
||||
void ignoreRegion(bool ignoreRegion) {
|
||||
_ignoreRegion = ignoreRegion;
|
||||
}
|
||||
bool ignoreRegion() const {
|
||||
return _ignoreRegion;
|
||||
}
|
||||
|
||||
void readStringTable(unsigned resOffset, uint16 resCount);
|
||||
const StringEntry &getString(uint16 index) const;
|
||||
uint getStringTableSize() const { return _stringTable.size(); }
|
||||
|
||||
const Common::String &getName() const { return _name; }
|
||||
|
||||
const CodeType &getCode() const {
|
||||
return _code;
|
||||
}
|
||||
|
||||
void setAnimation(const AnimationPtr &animation) {
|
||||
_animation = animation;
|
||||
}
|
||||
|
||||
const AnimationPtr &getAnimation() const {
|
||||
return _animation;
|
||||
}
|
||||
|
||||
void setAnimationPosition(Common::Point animationPos) {
|
||||
_animationPos = animationPos;
|
||||
}
|
||||
|
||||
void setMouseCursor(const AnimationPtr &mouseCursor) {
|
||||
_mouseCursor = mouseCursor;
|
||||
freeRotated();
|
||||
}
|
||||
|
||||
const AnimationPtr &getMouseCursor() const {
|
||||
return _mouseCursor;
|
||||
}
|
||||
|
||||
void setPicture(Graphics::ManagedSurface *);
|
||||
|
||||
Graphics::ManagedSurface *getPicture() const {
|
||||
return _rotatedPicture ? _rotatedPicture.get() : _picture.get();
|
||||
}
|
||||
|
||||
void rotate(int rot);
|
||||
int rotation() const {
|
||||
return _rotation;
|
||||
}
|
||||
|
||||
void generateRegion();
|
||||
void generateRegion(Common::Rect rect);
|
||||
|
||||
void regionOffset(Common::Point offset) {
|
||||
_regionOffset = offset;
|
||||
}
|
||||
|
||||
void setAlpha(int alpha) {
|
||||
if (alpha < 0)
|
||||
alpha = 0;
|
||||
if (alpha > 100)
|
||||
alpha = 100;
|
||||
_alpha = (100 - alpha) * 255 / 100;
|
||||
}
|
||||
|
||||
int alpha() const {
|
||||
return _alpha;
|
||||
}
|
||||
|
||||
void scale(int scale) {
|
||||
_scale = scale;
|
||||
}
|
||||
|
||||
int scale() const {
|
||||
return _scale;
|
||||
}
|
||||
|
||||
void region(RegionPtr region);
|
||||
|
||||
const RegionPtr ®ion() const {
|
||||
return _region;
|
||||
}
|
||||
|
||||
void setClickHandler(uint ip) {
|
||||
_clickHandler = ip;
|
||||
}
|
||||
|
||||
uint getClickHandler() const {
|
||||
return _clickHandler;
|
||||
}
|
||||
|
||||
void setExamineHandler(uint ip) {
|
||||
_examineHandler = ip;
|
||||
}
|
||||
|
||||
uint getExamineHandler() const {
|
||||
return _examineHandler;
|
||||
}
|
||||
|
||||
void setUseHandler(const Common::String &name, uint ip) {
|
||||
_useHandlers[name] = ip;
|
||||
}
|
||||
|
||||
uint getUseHandler(const Common::String &name) const {
|
||||
return _useHandlers.getValOrDefault(name, 0); // use handler can never be 0
|
||||
}
|
||||
|
||||
void setUserUseHandler(uint ip) {
|
||||
_userUseHandler = ip;
|
||||
}
|
||||
|
||||
void setThrowHandler(uint ip) {
|
||||
_throwHandler = ip;
|
||||
}
|
||||
|
||||
void setUseOnHandler(uint ip) {
|
||||
_useOnHandler = ip;
|
||||
}
|
||||
|
||||
uint getUserUseHandler() const {
|
||||
return _userUseHandler;
|
||||
}
|
||||
|
||||
void setHandlerBD(uint ip) {
|
||||
_handlerBD = ip;
|
||||
}
|
||||
|
||||
uint getHandlerBD() const {
|
||||
return _handlerBD;
|
||||
}
|
||||
|
||||
void setHandlerC1(uint ip) {
|
||||
_handlerC1 = ip;
|
||||
}
|
||||
|
||||
uint getHandlerC1() const {
|
||||
return _handlerC1;
|
||||
}
|
||||
|
||||
void setTrapHandler(uint ip, RegionPtr region) {
|
||||
_trapHandler = ip;
|
||||
_trapRegion = region;
|
||||
}
|
||||
|
||||
RegionPtr getTrapRegion() const { return _trapRegion; }
|
||||
|
||||
uint getTrapHandler() const { return _trapHandler; }
|
||||
|
||||
void paint(AGDSEngine &engine, Graphics::Surface &backbuffer, Common::Point pos) const;
|
||||
|
||||
void moveTo(Common::Point pos);
|
||||
|
||||
void z(int z) {
|
||||
_z = z;
|
||||
}
|
||||
|
||||
int z() const {
|
||||
return _z;
|
||||
}
|
||||
|
||||
const Common::String &getText() const {
|
||||
return _text;
|
||||
}
|
||||
|
||||
void setText(const Common::String &text) {
|
||||
_text = text;
|
||||
}
|
||||
|
||||
void title(const Common::String &title) {
|
||||
_title = title;
|
||||
}
|
||||
|
||||
const Common::String &title() const {
|
||||
return _title;
|
||||
}
|
||||
|
||||
Common::Point getPosition() const {
|
||||
return _pos - _offset;
|
||||
}
|
||||
|
||||
Common::Point getOffset() const {
|
||||
return _offset;
|
||||
}
|
||||
|
||||
Common::Rect getRect() const;
|
||||
|
||||
void setKeyHandler(const Common::String &name, uint ip) {
|
||||
_keyHandlers[name] = ip;
|
||||
}
|
||||
|
||||
uint getKeyHandler(const Common::String &name) const {
|
||||
KeyHandlersType::const_iterator i = _keyHandlers.find(name);
|
||||
return i != _keyHandlers.end() ? i->_value : 0;
|
||||
}
|
||||
|
||||
uint throwHandler() const {
|
||||
return _throwHandler;
|
||||
}
|
||||
|
||||
uint useOnHandler() const {
|
||||
return _useOnHandler;
|
||||
}
|
||||
|
||||
bool locked() const {
|
||||
return _locked != 0;
|
||||
}
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
bool alive() const { return _alive; }
|
||||
|
||||
void alive(bool value);
|
||||
|
||||
void srcRect(Common::Rect srcRect) { _srcRect = srcRect; }
|
||||
|
||||
bool pointIn(Common::Point pos);
|
||||
};
|
||||
using ObjectPtr = Common::SharedPtr<Object>;
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif /* AGDS_OBJECT_H */
|
||||
524
engines/agds/opcode.h
Normal file
524
engines/agds/opcode.h
Normal file
@@ -0,0 +1,524 @@
|
||||
/* 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 OPCODE_H
|
||||
#define OPCODE_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
enum Opcode : uint16 {
|
||||
kEnter = 5,
|
||||
kJumpZImm16 = 8,
|
||||
kJumpImm16 = 9,
|
||||
kPop = 10,
|
||||
kDup = 11,
|
||||
kExitProcess = 12,
|
||||
kSuspendProcess = 13,
|
||||
kPushImm32 = 14,
|
||||
kPushImm16 = 15,
|
||||
kPushImm8 = 16,
|
||||
kPushImm16_2 = 17,
|
||||
kPushImm8_2 = 18,
|
||||
kStub19 = 19,
|
||||
kStub20 = 20,
|
||||
kGetGlobalImm8 = 21,
|
||||
kEquals = 22,
|
||||
kNotEquals = 23,
|
||||
kGreater = 24,
|
||||
kLess = 25,
|
||||
kGreaterOrEquals = 26,
|
||||
kLessOrEquals = 27,
|
||||
kAdd = 28,
|
||||
kSub = 29,
|
||||
kMul = 30,
|
||||
kDiv = 31,
|
||||
kMod = 32,
|
||||
kAnd = 33,
|
||||
kOr = 34,
|
||||
kXor = 35,
|
||||
kNot = 36,
|
||||
kShl = 37,
|
||||
kShr = 38,
|
||||
kBoolAnd = 39,
|
||||
kBoolOr = 40,
|
||||
kBoolNot = 41,
|
||||
kNegate = 42,
|
||||
kPreIncrementGlobal = 43,
|
||||
kPreDecrementGlobal = 44,
|
||||
kPostIncrementGlobal = 45,
|
||||
kPostDecrementGlobal = 46,
|
||||
kSetGlobalWithTop = 47,
|
||||
kSetGlobal = 48,
|
||||
kIncrementGlobalByTop = 49,
|
||||
kDecrementGlobalByTop = 50,
|
||||
kMultiplyGlobalByTop = 51,
|
||||
kDivideGlobalByTop = 52,
|
||||
kModGlobalByTop = 53,
|
||||
kShlGlobalByTop = 54,
|
||||
kShrGlobalByTop = 55,
|
||||
kAndGlobalByTop = 56,
|
||||
kOrGlobalByTop = 57,
|
||||
kXorGlobalByTop = 58,
|
||||
kObjectInitialise = 59,
|
||||
kObjectRegisterLookHandler = 60,
|
||||
kObjectRegisterUseHandler = 61,
|
||||
kObjectRegisterHandlerC1 = 62,
|
||||
kObjectRegisterUseObjectHandler = 63,
|
||||
kObjectRegisterHandlerBD = 64,
|
||||
kObjectRegisterCharacterTrap = 65,
|
||||
kLoadMouseCursorFromObject = 66,
|
||||
kLoadRegionFromObject = 68,
|
||||
kLoadPictureFromObject = 69,
|
||||
kLoadAnimationFromObject = 70,
|
||||
kSetObjectZ = 71,
|
||||
kSetScreenBackground = 72,
|
||||
kLoadTextFromObject = 73,
|
||||
kScreenSetZNearFar = 74,
|
||||
kScreenLoadRegion = 75,
|
||||
kScreenLoadObject = 76,
|
||||
kScreenCloneObject = 77,
|
||||
kScreenRemoveObject = 78,
|
||||
kSetNextScreen = 79,
|
||||
kSetNextScreenSaveOrLoad = 80,
|
||||
kObjectPatchSetText = 82,
|
||||
kObjectPatchSetRegionName = 83,
|
||||
kScreenPatchSetRegionName = 84,
|
||||
kLoadCharacter = 85,
|
||||
kUnloadCharacter = 86,
|
||||
kAssociateCharacter = 87,
|
||||
kAnimateCharacter = 88,
|
||||
kHideCharacter = 89,
|
||||
kShowCharacter = 90,
|
||||
kDisableCharacter = 91,
|
||||
kEnableCharacter = 92,
|
||||
kMoveCharacterUserMove = 93,
|
||||
kLeaveCharacter = 94,
|
||||
kSetCharacter = 95,
|
||||
kSetCharacterDirection = 96,
|
||||
kPointCharacter = 97,
|
||||
kDisableUser = 98,
|
||||
kEnableUser = 99,
|
||||
kRestartSample = 100,
|
||||
kStopSample = 101,
|
||||
kStub102 = 102,
|
||||
kStub103 = 103,
|
||||
kStub104 = 104,
|
||||
kClearScreen = 105,
|
||||
kInventoryClear = 106,
|
||||
kStub107 = 107,
|
||||
kLoadMouse = 108,
|
||||
kAttachInventoryObjectToMouse0 = 109,
|
||||
kResetMousePointer = 110,
|
||||
kInventoryAddObject = 111,
|
||||
kInventoryRemoveObject = 112,
|
||||
kStub113 = 113,
|
||||
kStub114 = 114,
|
||||
kStub115 = 115,
|
||||
kStub116 = 116,
|
||||
kLoadAnimation = 117,
|
||||
kLoadSample = 118,
|
||||
kSetPhaseVarControlledFlag = 119,
|
||||
kPlayerSay120 = 120,
|
||||
kStub121 = 121,
|
||||
kPlayerSay122 = 122,
|
||||
kNPCSayNoSound = 123,
|
||||
kNPCSay = 124,
|
||||
kPlayerSay125 = 125,
|
||||
kStub126 = 126,
|
||||
kSetTimer = 127,
|
||||
kProcessResetState = 128,
|
||||
kSetAnimationZ = 129,
|
||||
kSetCycles = 130,
|
||||
kSetRandom = 131,
|
||||
kSetPeriodic = 132,
|
||||
kSetPanAndVolume = 133,
|
||||
kSetAnimationPosition = 134,
|
||||
kSetPhaseVar = 135,
|
||||
kSetAnimationLoop = 136,
|
||||
kSetAnimationSpeed = 137,
|
||||
kScreenObjectPatchIncRef = 138,
|
||||
kScreenObjectPatchDecRef = 139,
|
||||
kScreenCheckScreenPatch = 140,
|
||||
kGetFreeInventorySpace = 141,
|
||||
kSetStringSystemVariable = 142,
|
||||
kSetSystemIntegerVariable = 143,
|
||||
kGetSavedMouseX = 144,
|
||||
kGetSavedMouseY = 145,
|
||||
kGetRegionCenterX = 146,
|
||||
kGetRegionCenterY = 147,
|
||||
kGetCharacterAnimationPhase = 148,
|
||||
kGetCharacterX = 149,
|
||||
kGetCharacterY = 150,
|
||||
kCompareScreenName = 151,
|
||||
kGetPictureBaseX = 152,
|
||||
kGetPictureBaseY = 153,
|
||||
kGetObjectSurfaceX = 154,
|
||||
kGetObjectSurfaceY = 155,
|
||||
kLoadGame = 156,
|
||||
kSaveGame = 157,
|
||||
kQuit = 158,
|
||||
kStartNewGame = 159,
|
||||
kLoadSaveSlotNamePicture = 160,
|
||||
kGetSaveGameName = 161,
|
||||
kDisableInventory = 162,
|
||||
kEnableInventory = 163,
|
||||
kLoadPreviousScreen = 164,
|
||||
kMoveScreenObject = 165,
|
||||
kSetObjectRegionOffset = 166,
|
||||
kReturnCurrentInventoryObject = 167,
|
||||
kStub168 = 168,
|
||||
kGetIntegerSystemVariable = 169,
|
||||
kSetDelay = 170,
|
||||
kGetRandomNumber = 171,
|
||||
kSetSampleVolume = 172,
|
||||
kStub173 = 173,
|
||||
kStub174 = 174,
|
||||
kAppendToSharedStorage = 175,
|
||||
kCloneName = 176,
|
||||
kAppendNameToSharedStorage = 177,
|
||||
kGetCloneVar = 178,
|
||||
kSetCloneVar = 179,
|
||||
kGetObjectId = 180,
|
||||
kStub181 = 181,
|
||||
kSetTileSize = 182,
|
||||
kGenerateRegion = 183,
|
||||
kSetObjectTile = 184,
|
||||
kGetMaxInventorySize = 185,
|
||||
kInventoryHasObject = 186,
|
||||
kAppendInventoryObjectNameToSharedSpace = 187,
|
||||
kSetObjectText = 188,
|
||||
kInventoryFindObjectByName = 189,
|
||||
kSetObjectScale = 190,
|
||||
kDisableMouseAreas = 191,
|
||||
kObjectIgnoreRegion = 192,
|
||||
kRemoveGapsFromInventory = 193,
|
||||
kSampleAmbient = 194,
|
||||
kGetObjectPictureWidth = 195,
|
||||
kGetObjectPictureHeight = 196,
|
||||
kSetRotation = 197,
|
||||
kLoadPicture = 198,
|
||||
kStub199 = 199,
|
||||
kSetTileIndex = 200,
|
||||
kSetThrowHandler = 201,
|
||||
kSetUseOnHandler = 202,
|
||||
kPlayFilm = 203,
|
||||
kStub204 = 204,
|
||||
kAddMouseArea = 205,
|
||||
kModifyMouseArea = 206,
|
||||
kSetRain = 207,
|
||||
kFogOnCharacter = 208,
|
||||
kObjectRegisterUserUseHandler = 209,
|
||||
kStub210 = 210,
|
||||
kStub211 = 211,
|
||||
kSetSampleVolumeAndPan = 212,
|
||||
kStub213 = 213,
|
||||
kAddSampleToSoundGroup = 214,
|
||||
kClearSoundGroup = 215,
|
||||
kStub216 = 216,
|
||||
kStub217 = 217,
|
||||
kSetRainDensity = 218,
|
||||
kLeaveCharacterEx = 219,
|
||||
kStopCharacter = 220,
|
||||
kRestartAnimation = 221,
|
||||
kSignalAnimationEnd = 222,
|
||||
kSetShadowIntensity = 223,
|
||||
kSetNPCTellNotifyVar = 224,
|
||||
kPauseAnimation = 225,
|
||||
kFadeObject = 226,
|
||||
kLoadFont = 227,
|
||||
kMoveCharacterNoUserMove = 228,
|
||||
kOnKey = 229,
|
||||
kStub230 = 230,
|
||||
kStub231 = 231,
|
||||
kStub232 = 232,
|
||||
kObjectFreePictureAndAnimation = 233,
|
||||
kGetSampleVolume = 234,
|
||||
kFadeScreen = 235,
|
||||
kUserEnabled = 236,
|
||||
kAnimationNextFrame = 237,
|
||||
kInventoryHasObjectByName = 238,
|
||||
kStub239 = 239,
|
||||
kLoadDialog = 240,
|
||||
kStub241 = 241,
|
||||
kHasGlobal = 242,
|
||||
kStub243 = 243,
|
||||
kSetCharacterNotifyVars = 244,
|
||||
kAttachInventoryObjectToMouse1 = 245,
|
||||
kStub246 = 246,
|
||||
kSetDialogForNextFilm = 247,
|
||||
kSetCamera = 252,
|
||||
kStub251 = 251,
|
||||
kCharacterStub253 = 253,
|
||||
kStub257 = 257,
|
||||
kStub258 = 258,
|
||||
kSetFog = 263,
|
||||
kStub264 = 264,
|
||||
kStub265 = 265,
|
||||
kStub266 = 266,
|
||||
kStub267 = 267,
|
||||
kStub275 = 275,
|
||||
kStub276 = 276,
|
||||
kStub278 = 278,
|
||||
kStub279 = 279,
|
||||
kStub280 = 280,
|
||||
kStub284 = 284,
|
||||
kStub291 = 291,
|
||||
kStub292 = 292,
|
||||
};
|
||||
|
||||
#define AGDS_OPCODE_LIST(OP, OP_C, OP_B, OP_W, OP_U, OP_UD, OP_UU) \
|
||||
OP_UU(kEnter, enter) \
|
||||
OP_W(kJumpZImm16, jumpz) \
|
||||
OP_W(kJumpImm16, jump) \
|
||||
OP(kPop, popNoResult) \
|
||||
OP(kDup, dup) \
|
||||
OP(kExitProcess, exitProcess) \
|
||||
OP(kSuspendProcess, suspend) \
|
||||
OP_UD(kPushImm32, push) \
|
||||
OP_C(kPushImm8, push) \
|
||||
OP_W(kPushImm16, push) \
|
||||
OP_C(kPushImm8_2, push2) \
|
||||
OP_W(kPushImm16_2, push2) \
|
||||
OP_B(kGetGlobalImm8, getGlobal) \
|
||||
OP(kPostIncrementGlobal, postIncrementGlobal) \
|
||||
OP(kPostDecrementGlobal, postDecrementGlobal) \
|
||||
OP(kIncrementGlobalByTop, incrementGlobalByTop) \
|
||||
OP(kDecrementGlobalByTop, decrementGlobalByTop) \
|
||||
OP(kMultiplyGlobalByTop, multiplyGlobalByTop) \
|
||||
OP(kDivideGlobalByTop, divideGlobalByTop) \
|
||||
OP(kModGlobalByTop, modGlobalByTop) \
|
||||
OP(kShlGlobalByTop, shlGlobalByTop) \
|
||||
OP(kShrGlobalByTop, shrGlobalByTop) \
|
||||
OP(kAndGlobalByTop, andGlobalByTop) \
|
||||
OP(kOrGlobalByTop, orGlobalByTop) \
|
||||
OP(kXorGlobalByTop, xorGlobalByTop) \
|
||||
OP(kEquals, equals) \
|
||||
OP(kNotEquals, notEquals) \
|
||||
OP(kGreater, greater) \
|
||||
OP(kLess, less) \
|
||||
OP(kGreaterOrEquals, greaterOrEquals) \
|
||||
OP(kLessOrEquals, lessOrEquals) \
|
||||
OP(kAdd, add) \
|
||||
OP(kSub, sub) \
|
||||
OP(kMul, mul) \
|
||||
OP(kDiv, div) \
|
||||
OP(kMod, mod) \
|
||||
OP(kSetGlobalWithTop, setGlobalWithTop) \
|
||||
OP(kSetGlobal, setGlobal) \
|
||||
OP(kBoolOr, boolOr) \
|
||||
OP(kBoolAnd, boolAnd) \
|
||||
OP(kAnd, bitAnd) \
|
||||
OP(kOr, bitOr) \
|
||||
OP(kXor, bitXor) \
|
||||
OP(kNot, bitNot) \
|
||||
OP(kShl, shl) \
|
||||
OP(kShr, shr) \
|
||||
OP(kBoolNot, boolNot) \
|
||||
OP(kNegate, negate) \
|
||||
OP_U(kObjectInitialise, initialise) \
|
||||
OP_U(kObjectRegisterLookHandler, onLook) \
|
||||
OP_U(kObjectRegisterUseHandler, onUse) \
|
||||
OP_U(kObjectRegisterHandlerC1, onObjectC1) \
|
||||
OP_U(kObjectRegisterCharacterTrap, onCharacterTrap) \
|
||||
OP_U(kObjectRegisterHandlerBD, onObjectBD) \
|
||||
OP(kLoadMouseCursorFromObject, loadMouseCursorFromObject) \
|
||||
OP(kLoadRegionFromObject, loadRegionFromObject) \
|
||||
OP(kLoadPictureFromObject, loadPictureFromObject) \
|
||||
OP(kLoadAnimationFromObject, loadAnimationFromObject) \
|
||||
OP(kHideCharacter, hideCharacter) \
|
||||
OP(kShowCharacter, showCharacter) \
|
||||
OP(kEnableCharacter, enableCharacter) \
|
||||
OP(kMoveCharacterUserMove, moveCharacterUserMove) \
|
||||
OP(kLeaveCharacter, leaveCharacter) \
|
||||
OP(kSetCharacter, setCharacter) \
|
||||
OP(kSetCharacterDirection, setCharacterDirection) \
|
||||
OP(kPointCharacter, pointCharacter) \
|
||||
OP(kDisableUser, disableUser) \
|
||||
OP(kEnableUser, enableUser) \
|
||||
OP(kRestartSample, restartSample) \
|
||||
OP(kStopSample, stopSample) \
|
||||
OP(kStub102, stub102) \
|
||||
OP(kClearScreen, clearScreen) \
|
||||
OP(kInventoryClear, inventoryClear) \
|
||||
OP(kLoadMouse, loadMouse) \
|
||||
OP(kResetMousePointer, resetMousePointer) \
|
||||
OP(kInventoryAddObject, inventoryAddObject) \
|
||||
OP(kInventoryRemoveObject, inventoryRemoveObject) \
|
||||
OP_U(kObjectRegisterUseObjectHandler, onObjectUse) \
|
||||
OP(kObjectPatchSetText, objectPatchSetText) \
|
||||
OP(kObjectPatchSetRegionName, objectPatchSetRegionName) \
|
||||
OP(kScreenPatchSetRegionName, screenPatchSetRegionName) \
|
||||
OP(kAnimateCharacter, animateCharacter) \
|
||||
OP(kLoadCharacter, loadCharacter) \
|
||||
OP(kAssociateCharacter, associateCharacter) \
|
||||
OP(kSetObjectZ, setObjectZ) \
|
||||
OP(kSetScreenBackground, setScreenBackground) \
|
||||
OP(kLoadTextFromObject, loadTextFromObject) \
|
||||
OP(kCompareScreenName, compareScreenName) \
|
||||
OP(kScreenSetZNearFar, screenSetZNearFar) \
|
||||
OP(kScreenLoadObject, loadScreenObject) \
|
||||
OP(kScreenLoadRegion, loadScreenRegion) \
|
||||
OP(kScreenCloneObject, cloneObject) \
|
||||
OP(kSetNextScreen, setNextScreen) \
|
||||
OP(kSetNextScreenSaveOrLoad, setNextScreenSaveOrLoad) \
|
||||
OP(kScreenRemoveObject, removeScreenObject) \
|
||||
OP(kLoadAnimation, loadAnimation) \
|
||||
OP(kLoadSample, loadSample) \
|
||||
OP(kSetPhaseVarControlledFlag, setPhaseVarControlledFlag) \
|
||||
OP(kPlayerSay120, playerSay120) \
|
||||
OP(kPlayerSay122, playerSay122) \
|
||||
OP(kPlayerSay125, playerSay125) \
|
||||
OP(kNPCSay, npcSay) \
|
||||
OP(kNPCSayNoSound, npcSayNoSound) \
|
||||
OP(kSetTimer, setTimer) \
|
||||
OP(kProcessResetState, resetState) \
|
||||
OP(kSetAnimationZ, setAnimationZ) \
|
||||
OP(kSetCycles, setCycles) \
|
||||
OP(kSetRandom, setRandom) \
|
||||
OP(kSetPeriodic, setPeriodic) \
|
||||
OP(kSetPanAndVolume, setPanAndVolume) \
|
||||
OP(kSetAnimationPosition, setAnimationPosition) \
|
||||
OP(kSetPhaseVar, setPhaseVar) \
|
||||
OP(kSetAnimationLoop, setAnimationLoop) \
|
||||
OP(kSetAnimationSpeed, setAnimationSpeed) \
|
||||
OP(kScreenObjectPatchIncRef, screenObjectPatchIncRef) \
|
||||
OP(kScreenObjectPatchDecRef, screenObjectPatchDecRef) \
|
||||
OP(kGetSavedMouseX, getSavedMouseX) \
|
||||
OP(kGetSavedMouseY, getSavedMouseY) \
|
||||
OP(kScreenCheckScreenPatch, checkScreenPatch) \
|
||||
OP(kGetFreeInventorySpace, getInventoryFreeSpace) \
|
||||
OP(kSetStringSystemVariable, setStringSystemVariable) \
|
||||
OP(kSetSystemIntegerVariable, setIntegerSystemVariable) \
|
||||
OP(kGetRegionCenterX, getRegionCenterX) \
|
||||
OP(kGetRegionCenterY, getRegionCenterY) \
|
||||
OP(kGetCharacterAnimationPhase, getCharacterAnimationPhase) \
|
||||
OP(kGetCharacterX, getCharacterX) \
|
||||
OP(kGetCharacterY, getCharacterY) \
|
||||
OP(kGetIntegerSystemVariable, getIntegerSystemVariable) \
|
||||
OP(kGetRandomNumber, getRandomNumber) \
|
||||
OP(kAppendToSharedStorage, appendToSharedStorage) \
|
||||
OP(kAppendNameToSharedStorage, appendNameToSharedStorage) \
|
||||
OP(kCloneName, cloneName) \
|
||||
OP(kGetCloneVar, getCloneVar) \
|
||||
OP(kSetCloneVar, setCloneVar) \
|
||||
OP(kGetPictureBaseX, getPictureBaseX) \
|
||||
OP(kGetPictureBaseY, getPictureBaseY) \
|
||||
OP(kGetObjectSurfaceX, getObjectSurfaceX) \
|
||||
OP(kGetObjectSurfaceY, getObjectSurfaceY) \
|
||||
OP(kLoadGame, loadGame) \
|
||||
OP(kSaveGame, saveGame) \
|
||||
OP(kLoadSaveSlotNamePicture, loadSaveSlotNamePicture) \
|
||||
OP(kGetSaveGameName, getSaveGameName) \
|
||||
OP(kSetObjectRegionOffset, setObjectRegionOffset) \
|
||||
OP(kSetDelay, setDelay) \
|
||||
OP(kSetSampleVolume, setSampleVolume) \
|
||||
OP(kStub173, stub173) \
|
||||
OP(kStub174, stub174) \
|
||||
OP(kObjectIgnoreRegion, objectIgnoreRegion) \
|
||||
OP(kQuit, quit) \
|
||||
OP(kStartNewGame, startNewGame) \
|
||||
OP(kDisableInventory, disableInventory) \
|
||||
OP(kEnableInventory, enableInventory) \
|
||||
OP(kLoadPreviousScreen, loadPreviousScreen) \
|
||||
OP(kMoveScreenObject, moveScreenObject) \
|
||||
OP(kGetObjectId, getObjectId) \
|
||||
OP(kSetTileSize, setTileSize) \
|
||||
OP(kGenerateRegion, generateRegion) \
|
||||
OP(kGetMaxInventorySize, getMaxInventorySize) \
|
||||
OP(kAppendInventoryObjectNameToSharedSpace, appendInventoryObjectNameToSharedSpace) \
|
||||
OP(kSetObjectTile, setObjectTile) \
|
||||
OP(kInventoryHasObject, inventoryHasObject) \
|
||||
OP(kReturnCurrentInventoryObject, returnCurrentInventoryObject) \
|
||||
OP(kSetObjectText, setObjectText) \
|
||||
OP(kSetObjectScale, setObjectScale) \
|
||||
OP(kDisableMouseAreas, disableMouseAreas) \
|
||||
OP(kRemoveGapsFromInventory, removeGapsFromInventory) \
|
||||
OP(kSampleAmbient, sampleAmbient) \
|
||||
OP(kGetObjectPictureWidth, getObjectPictureWidth) \
|
||||
OP(kGetObjectPictureHeight, getObjectPictureHeight) \
|
||||
OP(kLoadPicture, loadPicture) \
|
||||
OP(kSetRotation, setRotation) \
|
||||
OP(kStub199, stub199) \
|
||||
OP(kSetSampleVolumeAndPan, setSampleVolumeAndPan) \
|
||||
OP(kAddSampleToSoundGroup, addSampleToSoundGroup) \
|
||||
OP(kClearSoundGroup, clearSoundGroup) \
|
||||
OP(kStub216, stub216) \
|
||||
OP(kStub217, stub217) \
|
||||
OP(kStopCharacter, stopCharacter) \
|
||||
OP(kLeaveCharacterEx, leaveCharacterEx) \
|
||||
OP(kRestartAnimation, restartAnimation) \
|
||||
OP(kAnimationNextFrame, animationNextFrame) \
|
||||
OP(kSignalAnimationEnd, signalAnimationEnd) \
|
||||
OP(kSetShadowIntensity, setShadowIntensity) \
|
||||
OP(kSetNPCTellNotifyVar, setNPCTellNotifyVar) \
|
||||
OP(kPauseAnimation, pauseAnimation) \
|
||||
OP(kFadeObject, fadeObject) \
|
||||
OP(kLoadFont, loadFont) \
|
||||
OP_U(kSetThrowHandler, setThrowHandler) \
|
||||
OP_U(kSetUseOnHandler, setUseOnHandler) \
|
||||
OP(kPlayFilm, playFilm) \
|
||||
OP(kAddMouseArea, addMouseArea) \
|
||||
OP(kSetRain, setRain) \
|
||||
OP(kSetRainDensity, setRainDensity) \
|
||||
OP(kFogOnCharacter, fogOnCharacter) \
|
||||
OP(kSetTileIndex, setTileIndex) \
|
||||
OP(kModifyMouseArea, modifyMouseArea) \
|
||||
OP_U(kObjectRegisterUserUseHandler, onObjectUserUse) \
|
||||
OP(kMoveCharacterNoUserMove, moveCharacterNoUserMove) \
|
||||
OP_U(kOnKey, onKey) \
|
||||
OP(kGetSampleVolume, getSampleVolume) \
|
||||
OP(kStub231, stub231) \
|
||||
OP(kObjectFreePictureAndAnimation, objectFreePictureAndAnimation) \
|
||||
OP(kFadeScreen, fadeScreen) \
|
||||
OP(kUserEnabled, userEnabled) \
|
||||
OP(kSetCharacterNotifyVars, setCharacterNotifyVars) \
|
||||
OP(kInventoryFindObjectByName, inventoryFindObjectByName) \
|
||||
OP(kInventoryHasObjectByName, inventoryHasObjectByName) \
|
||||
OP(kLoadDialog, loadDialog) \
|
||||
OP(kStub241, stub241) \
|
||||
OP(kHasGlobal, hasGlobal) \
|
||||
OP(kAttachInventoryObjectToMouse0, attachInventoryObjectToMouse0) \
|
||||
OP(kAttachInventoryObjectToMouse1, attachInventoryObjectToMouse1) \
|
||||
OP(kSetDialogForNextFilm, setDialogForNextFilm) \
|
||||
OP(kCharacterStub253, characterStub253) \
|
||||
OP(kStub251, stub251) \
|
||||
OP(kStub257, stub257) \
|
||||
OP(kStub258, stub258) \
|
||||
OP(kStub264, stub264) \
|
||||
OP(kStub265, stub265) \
|
||||
OP(kStub266, stub266) \
|
||||
OP(kStub267, stub267) \
|
||||
OP(kStub275, stub275) \
|
||||
OP(kStub276, stub276) \
|
||||
OP(kSetCamera, setCamera) \
|
||||
OP(kSetFog, setFog) \
|
||||
OP(kStub278, stub278) \
|
||||
OP(kStub279, stub279) \
|
||||
OP(kStub280, stub280) \
|
||||
OP(kStub284, stub284) \
|
||||
OP(kStub291, stub291) \
|
||||
OP(kStub292, stub292)
|
||||
|
||||
} // namespace AGDS
|
||||
|
||||
#endif
|
||||
134
engines/agds/patch.cpp
Normal file
134
engines/agds/patch.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
/* 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 "agds/patch.h"
|
||||
#include "agds/resourceManager.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/error.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
void ObjectPatch::load(Common::ReadStream &stream) {
|
||||
text = readString(stream);
|
||||
region = readString(stream);
|
||||
z = stream.readUint16LE();
|
||||
}
|
||||
|
||||
void ObjectPatch::save(Common::WriteStream &stream) const {
|
||||
writeString(stream, text);
|
||||
writeString(stream, region);
|
||||
stream.writeUint16LE(z);
|
||||
}
|
||||
|
||||
void Patch::load(Common::ReadStream &stream) {
|
||||
screenSaved = stream.readByte();
|
||||
screenRegionName = readString(stream);
|
||||
prevScreenName = readString(stream);
|
||||
debug("patch screen, valid: %d region: %s, prev: %s", screenSaved, screenRegionName.c_str(), prevScreenName.c_str());
|
||||
|
||||
loadingType = static_cast<ScreenLoadingType>(stream.readUint32LE());
|
||||
characterPosition.x = stream.readSint32LE();
|
||||
characterPosition.y = stream.readSint32LE();
|
||||
characterDirection = stream.readSint32LE();
|
||||
characterPresent = stream.readUint32LE();
|
||||
debug("character %s at %u,%u with dir: %d", characterPresent ? "[present]" : "[absent]", characterPosition.x, characterPosition.y, characterDirection);
|
||||
uint object_count = stream.readUint32LE();
|
||||
debug("objects in this patch: %u", object_count);
|
||||
if (stream.read(palette, sizeof(palette)) != sizeof(palette)) {
|
||||
error("short read, can't read palette");
|
||||
}
|
||||
|
||||
defaultMouseCursor = readString(stream);
|
||||
debug("default pointer name: %s", defaultMouseCursor.c_str());
|
||||
objects.clear();
|
||||
for (uint i = 0; i < object_count; ++i) {
|
||||
int flag = stream.readSint16LE();
|
||||
Common::String name = readString(stream);
|
||||
debug("patch object %s %d", name.c_str(), flag);
|
||||
objects.push_back(Object(name, flag));
|
||||
}
|
||||
}
|
||||
|
||||
void Patch::save(Common::WriteStream &stream) {
|
||||
stream.writeByte(screenSaved);
|
||||
writeString(stream, screenRegionName);
|
||||
writeString(stream, prevScreenName);
|
||||
|
||||
stream.writeUint32LE(static_cast<uint>(loadingType));
|
||||
stream.writeUint32LE(characterPosition.x);
|
||||
stream.writeUint32LE(characterPosition.y);
|
||||
stream.writeSint32LE(characterDirection);
|
||||
stream.writeUint32LE(characterPresent);
|
||||
|
||||
stream.writeUint32LE(objects.size());
|
||||
if (stream.write(palette, sizeof(palette)) != sizeof(palette)) {
|
||||
error("short write, can't write palette");
|
||||
}
|
||||
|
||||
writeString(stream, defaultMouseCursor);
|
||||
for (auto &object : objects) {
|
||||
stream.writeSint16LE(object.flag);
|
||||
writeString(stream, object.name);
|
||||
}
|
||||
}
|
||||
|
||||
void Patch::setFlag(const Common::String &name, int flag) {
|
||||
for (auto &object : objects) {
|
||||
if (object.name == name) {
|
||||
object.flag = flag;
|
||||
return;
|
||||
}
|
||||
}
|
||||
objects.push_back({name, flag});
|
||||
}
|
||||
|
||||
int Patch::getFlag(const Common::String &name) const {
|
||||
for (auto &object : objects) {
|
||||
if (object.name == name)
|
||||
return object.flag;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Patch::incRef(const Common::String &name) {
|
||||
for (auto &object : objects) {
|
||||
if (object.name == name) {
|
||||
return ++object.flag;
|
||||
}
|
||||
}
|
||||
objects.push_back({name, 1});
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Patch::decRef(const Common::String &name) {
|
||||
for (auto &object : objects) {
|
||||
if (object.name == name) {
|
||||
// this is original code lol
|
||||
object.flag = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
objects.push_back({name, 0});
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace AGDS
|
||||
82
engines/agds/patch.h
Normal file
82
engines/agds/patch.h
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PATCH_H
|
||||
#define PATCH_H
|
||||
|
||||
#include "agds/screenLoadingType.h"
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Common {
|
||||
class ReadStream;
|
||||
class WriteStream;
|
||||
} // namespace Common
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
class AGDSEngine;
|
||||
class Object;
|
||||
|
||||
struct ObjectPatch {
|
||||
static constexpr unsigned Size = 66;
|
||||
|
||||
Common::String text;
|
||||
Common::String region;
|
||||
int z;
|
||||
void load(Common::ReadStream &stream);
|
||||
void save(Common::WriteStream &stream) const;
|
||||
};
|
||||
|
||||
struct Patch {
|
||||
struct Object {
|
||||
Common::String name;
|
||||
int16 flag;
|
||||
|
||||
Object(const Common::String &n, int f) : name(n), flag(f) {}
|
||||
};
|
||||
|
||||
bool screenSaved = false;
|
||||
Common::String screenRegionName;
|
||||
Common::String prevScreenName;
|
||||
|
||||
ScreenLoadingType loadingType = ScreenLoadingType::Normal;
|
||||
Common::Point characterPosition;
|
||||
int characterDirection = 0;
|
||||
bool characterPresent = false;
|
||||
|
||||
byte palette[0x300] = {};
|
||||
Common::String defaultMouseCursor;
|
||||
Common::Array<Object> objects;
|
||||
|
||||
void load(Common::ReadStream &stream);
|
||||
void save(Common::WriteStream &stream);
|
||||
void setFlag(const Common::String &name, int flag);
|
||||
int getFlag(const Common::String &name) const;
|
||||
int incRef(const Common::String &name);
|
||||
int decRef(const Common::String &name);
|
||||
};
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif /* AGDS_PATCH_H */
|
||||
534
engines/agds/process.cpp
Normal file
534
engines/agds/process.cpp
Normal file
@@ -0,0 +1,534 @@
|
||||
/* 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 "agds/process.h"
|
||||
#include "agds/agds.h"
|
||||
#include "agds/animation.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/system.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
Process::Process(AGDSEngine *engine, const ObjectPtr &object, unsigned ip, int version) : _engine(engine), _parentScreen(engine->getCurrentScreenName()), _object(object),
|
||||
_entryPoint(ip), _ip(ip), _lastIp(ip),
|
||||
_status(kStatusActive), _exited(false), _exitCode(kExitCodeDestroy),
|
||||
_tileWidth(16), _tileHeight(16), _tileResource(-1), _tileIndex(0),
|
||||
_timer(0),
|
||||
_animationCycles(1), _animationLoop(false), _animationZ(0), _animationDelay(-1), _animationRandom(0),
|
||||
_phaseVarControlled(false), _animationSpeed(100),
|
||||
_samplePeriodic(false), _sampleAmbient(false), _sampleVolume(100),
|
||||
_filmSubtitlesResource(-1), _version(version) {
|
||||
updateWithCurrentMousePosition();
|
||||
}
|
||||
|
||||
void Process::debug(const char *str, ...) {
|
||||
va_list va;
|
||||
va_start(va, str);
|
||||
|
||||
Common::String format = Common::String::format("%s %04x[%u]: %s", _object->getName().c_str(), _lastIp, _stack.size(), str);
|
||||
Common::String buf = Common::String::vformat(format.c_str(), va);
|
||||
|
||||
buf += '\n';
|
||||
|
||||
if (g_system)
|
||||
g_system->logMessage(LogMessageType::kDebug, buf.c_str());
|
||||
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
// fixme: remove copy-paste
|
||||
void Process::error(const char *str, ...) {
|
||||
va_list va;
|
||||
va_start(va, str);
|
||||
|
||||
Common::String format = Common::String::format("WARNING: %s %04x[%u]: %s", _object->getName().c_str(), _lastIp + 7, _stack.size(), str);
|
||||
Common::String buf = Common::String::vformat(format.c_str(), va);
|
||||
|
||||
buf += '\n';
|
||||
|
||||
if (g_system)
|
||||
g_system->logMessage(LogMessageType::kWarning, buf.c_str());
|
||||
|
||||
va_end(va);
|
||||
_status = kStatusError;
|
||||
}
|
||||
|
||||
void Process::suspend(ProcessExitCode exitCode, const Common::String &arg1, const Common::String &arg2) {
|
||||
debug("suspend %d", exitCode);
|
||||
_exited = true;
|
||||
_exitCode = exitCode;
|
||||
_exitIntArg1 = 0;
|
||||
_exitIntArg2 = 0;
|
||||
_exitArg1 = arg1;
|
||||
_exitArg2 = arg2;
|
||||
}
|
||||
|
||||
void Process::suspend(ProcessExitCode exitCode, int arg1, int arg2) {
|
||||
debug("suspend %d", exitCode);
|
||||
_exited = true;
|
||||
_exitCode = exitCode;
|
||||
_exitIntArg1 = arg1;
|
||||
_exitIntArg2 = arg2;
|
||||
_exitArg1.clear();
|
||||
_exitArg2.clear();
|
||||
}
|
||||
|
||||
uint8 Process::next() {
|
||||
const Object::CodeType &code = _object->getCode();
|
||||
if (_ip < code.size()) {
|
||||
return code[_ip++];
|
||||
} else {
|
||||
_status = kStatusError;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Process::jump(int16 delta) {
|
||||
_ip += delta;
|
||||
}
|
||||
|
||||
void Process::jumpz(int16 delta) {
|
||||
int value = pop();
|
||||
if (value == 0) {
|
||||
debug("jumpz %d %+d", value, delta);
|
||||
_ip += delta;
|
||||
} else
|
||||
debug("jumpz ignored %d %+d", value, delta);
|
||||
}
|
||||
|
||||
void Process::suspend() {
|
||||
suspend(kExitCodeSuspend);
|
||||
}
|
||||
|
||||
void Process::suspendIfPassive() {
|
||||
if (passive())
|
||||
suspend();
|
||||
}
|
||||
|
||||
int32 Process::pop() {
|
||||
if (_stack.empty()) {
|
||||
error("stack underflow at %s:%04x", _object->getName().c_str(), _lastIp + 7);
|
||||
return 0;
|
||||
}
|
||||
return _stack.pop();
|
||||
}
|
||||
|
||||
int32 Process::top() {
|
||||
if (_stack.empty()) {
|
||||
error("stack underflow, %s:%04x", _object->getName().c_str(), _lastIp + 7);
|
||||
return 0;
|
||||
}
|
||||
return _stack.top();
|
||||
}
|
||||
|
||||
Common::String Process::getString(int id) {
|
||||
if (id == -1)
|
||||
return Common::String();
|
||||
else if (id <= -2 && id > -12)
|
||||
return _engine->getSharedStorage(id);
|
||||
else
|
||||
return _object->getString(id).string;
|
||||
}
|
||||
|
||||
Common::String Process::popText() {
|
||||
return _engine->loadText(popString());
|
||||
}
|
||||
|
||||
uint32 Process::popColor() {
|
||||
int b = pop();
|
||||
int g = pop();
|
||||
int r = pop();
|
||||
return _engine->pixelFormat().RGBToColor(r, g, b);
|
||||
}
|
||||
|
||||
void Process::updateWithCurrentMousePosition() {
|
||||
setMousePosition(_engine->mousePosition());
|
||||
}
|
||||
|
||||
void Process::setupAnimation(const AnimationPtr &animation) {
|
||||
animation->position(_animationPosition);
|
||||
animation->z(_animationZ);
|
||||
animation->process(getName());
|
||||
animation->phaseVar(_phaseVar);
|
||||
animation->loop(_animationLoop);
|
||||
animation->cycles(_animationCycles);
|
||||
animation->delay(_animationDelay);
|
||||
animation->setRandom(_animationRandom);
|
||||
animation->phaseVarControlled(_phaseVarControlled);
|
||||
if (_phaseVar.empty())
|
||||
suspend();
|
||||
else if (_phaseVarControlled)
|
||||
_engine->setGlobal(_phaseVar, 0);
|
||||
}
|
||||
|
||||
void Process::removeProcessAnimation() {
|
||||
if (_processAnimation) {
|
||||
auto *screen = _engine->getCurrentScreen();
|
||||
if (screen)
|
||||
screen->remove(_processAnimation);
|
||||
_processAnimation = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Process::activate() {
|
||||
switch (status()) {
|
||||
case kStatusPassive:
|
||||
_status = Process::kStatusActive;
|
||||
break;
|
||||
case kStatusDone:
|
||||
case kStatusError:
|
||||
debug("finished");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Process::deactivate() {
|
||||
switch (status()) {
|
||||
case kStatusActive:
|
||||
debug("deactivated");
|
||||
_status = Process::kStatusPassive;
|
||||
break;
|
||||
case kStatusDone:
|
||||
case kStatusError:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Process::done() {
|
||||
if (_status != kStatusDone) {
|
||||
debug("done");
|
||||
_status = kStatusDone;
|
||||
if (!_stack.empty())
|
||||
warning("process %s finished with non-empty stack", getName().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Process::fail() {
|
||||
_status = kStatusError;
|
||||
}
|
||||
|
||||
void Process::run() {
|
||||
bool restart = true;
|
||||
while (_status != kStatusDone && _status != kStatusError && restart) {
|
||||
restart = false;
|
||||
ProcessExitCode code = resume();
|
||||
switch (code) {
|
||||
case kExitCodeDestroy:
|
||||
debug("process %s returned destroy exit code", getName().c_str());
|
||||
done();
|
||||
if (!getObject()->alive() && !_engine->hasActiveProcesses(getName())) {
|
||||
removeScreenObject(getName());
|
||||
}
|
||||
break;
|
||||
case kExitCodeLoadScreenObjectAs:
|
||||
case kExitCodeLoadScreenObject:
|
||||
deactivate();
|
||||
_object->lock();
|
||||
_engine->runObject(getExitArg1(), getExitArg2());
|
||||
_object->unlock();
|
||||
activate();
|
||||
restart = true;
|
||||
break;
|
||||
case kExitCodeRunDialog:
|
||||
deactivate();
|
||||
_object->lock();
|
||||
_engine->runObject(getExitArg1());
|
||||
_object->unlock();
|
||||
break;
|
||||
case kExitCodeSetNextScreen:
|
||||
case kExitCodeSetNextScreenSaveOrLoad:
|
||||
done();
|
||||
debug("process %s launches screen: %s, saveorload: ", getName().c_str(), getExitArg1().c_str(), code == kExitCodeSetNextScreenSaveOrLoad);
|
||||
_engine->setNextScreenName(getExitArg1(), code == kExitCodeSetNextScreenSaveOrLoad ? ScreenLoadingType::SaveOrLoad : ScreenLoadingType::Normal);
|
||||
break;
|
||||
case kExitCodeMouseAreaChange:
|
||||
deactivate();
|
||||
_object->lock();
|
||||
_engine->changeMouseArea(getExitIntArg1(), getExitIntArg2());
|
||||
_object->unlock();
|
||||
activate();
|
||||
restart = true;
|
||||
break;
|
||||
case kExitCodeLoadInventoryObject:
|
||||
deactivate();
|
||||
_object->lock();
|
||||
_engine->inventory().add(getExitArg1());
|
||||
_object->unlock();
|
||||
activate();
|
||||
restart = true;
|
||||
break;
|
||||
case kExitCodeCloseInventory:
|
||||
_object->lock();
|
||||
_engine->inventory().enable(false);
|
||||
updateWithCurrentMousePosition();
|
||||
_object->unlock();
|
||||
break;
|
||||
case kExitCodeSuspend:
|
||||
updateWithCurrentMousePosition();
|
||||
break;
|
||||
case kExitCodeNewGame:
|
||||
deactivate();
|
||||
_object->lock();
|
||||
debug("exitProcessNewGame");
|
||||
_engine->newGame();
|
||||
_object->unlock();
|
||||
activate();
|
||||
restart = true;
|
||||
break;
|
||||
case kExitCodeLoadGame:
|
||||
deactivate();
|
||||
_object->lock();
|
||||
if (_engine->loadGameState(getExitIntArg1()).getCode() == Common::kNoError) {
|
||||
done();
|
||||
} else {
|
||||
debug("save loading failed, resuming execution...");
|
||||
}
|
||||
_object->unlock();
|
||||
activate();
|
||||
restart = true;
|
||||
break;
|
||||
case kExitCodeSaveGame:
|
||||
break;
|
||||
default:
|
||||
error("unknown process exit code %d", code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define UNARY_OP(NAME, OP) \
|
||||
void Process::NAME() { \
|
||||
int32 arg = pop(); \
|
||||
debug(#NAME " %d", arg); \
|
||||
push(OP arg); \
|
||||
}
|
||||
#define BINARY_OP(NAME, OP) \
|
||||
void Process::NAME() { \
|
||||
int32 arg2 = pop(); \
|
||||
int32 arg1 = pop(); \
|
||||
debug(" %d " #NAME " %d", arg1, arg2); \
|
||||
push(arg1 OP arg2); \
|
||||
}
|
||||
|
||||
UNARY_OP(boolNot, !)
|
||||
UNARY_OP(bitNot, ~)
|
||||
UNARY_OP(negate, -)
|
||||
BINARY_OP(shl, <<)
|
||||
BINARY_OP(shr, >>)
|
||||
BINARY_OP(boolOr, ||)
|
||||
BINARY_OP(boolAnd, &&)
|
||||
BINARY_OP(equals, ==)
|
||||
BINARY_OP(notEquals, !=)
|
||||
BINARY_OP(greater, >)
|
||||
BINARY_OP(greaterOrEquals, >=)
|
||||
BINARY_OP(less, <)
|
||||
BINARY_OP(lessOrEquals, <=)
|
||||
BINARY_OP(add, +)
|
||||
BINARY_OP(sub, -)
|
||||
BINARY_OP(mul, *)
|
||||
BINARY_OP(div, /)
|
||||
BINARY_OP(mod, %)
|
||||
BINARY_OP(bitAnd, &)
|
||||
BINARY_OP(bitOr, |)
|
||||
BINARY_OP(bitXor, ^)
|
||||
|
||||
#undef UNARY_OP
|
||||
#undef BINARY_OP
|
||||
|
||||
// fixme: add trace here
|
||||
#define AGDS_OP(NAME, METHOD) \
|
||||
case NAME: \
|
||||
METHOD(); \
|
||||
break;
|
||||
|
||||
#define AGDS_OP_C(NAME, METHOD) \
|
||||
case NAME: { \
|
||||
int8 arg = next(); \
|
||||
METHOD(arg); \
|
||||
} break;
|
||||
|
||||
#define AGDS_OP_B(NAME, METHOD) \
|
||||
case NAME: { \
|
||||
uint8 arg = next(); \
|
||||
METHOD(arg); \
|
||||
} break;
|
||||
|
||||
#define AGDS_OP_W(NAME, METHOD) \
|
||||
case NAME: { \
|
||||
int16 arg = next16(); \
|
||||
METHOD(arg); \
|
||||
} break;
|
||||
|
||||
#define AGDS_OP_U(NAME, METHOD) \
|
||||
case NAME: { \
|
||||
uint16 arg = next16(); \
|
||||
METHOD(arg); \
|
||||
} break;
|
||||
|
||||
#define AGDS_OP_UU(NAME, METHOD) \
|
||||
case NAME: { \
|
||||
uint16 arg1 = next16(); \
|
||||
uint16 arg2 = next16(); \
|
||||
METHOD(arg1, arg2); \
|
||||
} break;
|
||||
|
||||
#define AGDS_OP_UD(NAME, METHOD) \
|
||||
case NAME: { \
|
||||
uint16 arg1 = next16(); \
|
||||
uint32 arg2 = next16(); \
|
||||
METHOD(static_cast<int32>(arg1 | (arg2 << 16))); \
|
||||
} break;
|
||||
|
||||
uint16 Process::nextOpcode() {
|
||||
uint16 op = next();
|
||||
switch (_version) {
|
||||
case 0:
|
||||
return op + 5;
|
||||
|
||||
case 2:
|
||||
if (op & 1) {
|
||||
op |= next() << 8;
|
||||
op >>= 1;
|
||||
}
|
||||
return op - 7995;
|
||||
|
||||
default:
|
||||
return op;
|
||||
}
|
||||
}
|
||||
|
||||
ProcessExitCode Process::resume() {
|
||||
_exitCode = kExitCodeDestroy;
|
||||
if (_timer) {
|
||||
--_timer;
|
||||
return kExitCodeSuspend;
|
||||
}
|
||||
|
||||
const Object::CodeType &code = _object->getCode();
|
||||
while (!_exited && _ip < code.size()) {
|
||||
if (_timer) {
|
||||
return kExitCodeSuspend;
|
||||
}
|
||||
_lastIp = _ip;
|
||||
auto op = nextOpcode();
|
||||
// debug("CODE %04x: %u", _lastIp, (uint)op);
|
||||
switch (op) {
|
||||
AGDS_OPCODE_LIST(
|
||||
AGDS_OP,
|
||||
AGDS_OP_C, AGDS_OP_B,
|
||||
AGDS_OP_W, AGDS_OP_U,
|
||||
AGDS_OP_UD, AGDS_OP_UU)
|
||||
default:
|
||||
error("%s: %08x: unknown opcode 0x%02x (%u)", _object->getName().c_str(), _lastIp, (unsigned)op, (unsigned)op);
|
||||
fail();
|
||||
break;
|
||||
}
|
||||
}
|
||||
_exited = false;
|
||||
|
||||
return _exitCode;
|
||||
}
|
||||
|
||||
#define AGDS_DIS(NAME, METHOD) \
|
||||
case NAME: \
|
||||
source += Common::String::format("%s\n", #NAME); \
|
||||
break;
|
||||
|
||||
#define AGDS_DIS_C(NAME, METHOD) \
|
||||
case NAME: { \
|
||||
int8 arg = code[ip++]; \
|
||||
source += Common::String::format("%s %d\n", #NAME, (int)arg); \
|
||||
} break;
|
||||
|
||||
#define AGDS_DIS_B(NAME, METHOD) \
|
||||
case NAME: { \
|
||||
uint8 arg = code[ip++]; \
|
||||
source += Common::String::format("%s %u\n", #NAME, (uint)arg); \
|
||||
} break;
|
||||
|
||||
#define AGDS_DIS_W(NAME, METHOD) \
|
||||
case NAME: { \
|
||||
int16 arg = code[ip++]; \
|
||||
arg |= ((int16)code[ip++]) << 8; \
|
||||
source += Common::String::format("%s %d\n", #NAME, (int)arg); \
|
||||
} break;
|
||||
|
||||
#define AGDS_DIS_U(NAME, METHOD) \
|
||||
case NAME: { \
|
||||
uint16 arg = code[ip++]; \
|
||||
arg |= ((uint16)code[ip++]) << 8; \
|
||||
source += Common::String::format("%s %u\n", #NAME, (uint)arg); \
|
||||
} break;
|
||||
|
||||
#define AGDS_DIS_UU(NAME, METHOD) \
|
||||
case NAME: { \
|
||||
uint16 arg1 = code[ip++]; \
|
||||
arg1 |= ((uint16)code[ip++]) << 8; \
|
||||
uint16 arg2 = code[ip++]; \
|
||||
arg2 |= ((uint16)code[ip++]) << 8; \
|
||||
source += Common::String::format("%s %u %u\n", #NAME, (uint)arg1, (uint)arg2); \
|
||||
} break;
|
||||
|
||||
#define AGDS_DIS_UD(NAME, METHOD) \
|
||||
case NAME: { \
|
||||
uint16 arg1 = code[ip++]; \
|
||||
arg1 |= ((uint16)code[ip++]) << 8; \
|
||||
uint16 arg2 = code[ip++]; \
|
||||
arg2 |= ((uint16)code[ip++]) << 8; \
|
||||
source += Common::String::format("%s %u\n", #NAME, (uint)(arg1 | (arg2 << 16))); \
|
||||
} break;
|
||||
|
||||
Common::String Process::disassemble(const ObjectPtr &object, int version) {
|
||||
Common::String source = Common::String::format("Object %s disassembly:\n", object->getName().c_str());
|
||||
|
||||
const auto &code = object->getCode();
|
||||
uint ip = 0;
|
||||
while (ip < code.size()) {
|
||||
uint16 op = code[ip++];
|
||||
if (version == 2) {
|
||||
if (op & 1) {
|
||||
op |= code[ip++] << 8;
|
||||
op >>= 1;
|
||||
}
|
||||
} else if (version == 0) {
|
||||
op += 5;
|
||||
}
|
||||
|
||||
source += Common::String::format("%04x: %02x: ", ip - 1, op);
|
||||
switch (op) {
|
||||
AGDS_OPCODE_LIST(
|
||||
AGDS_DIS,
|
||||
AGDS_DIS_C, AGDS_DIS_B,
|
||||
AGDS_DIS_W, AGDS_DIS_U,
|
||||
AGDS_DIS_UD, AGDS_DIS_UU)
|
||||
default:
|
||||
source += Common::String::format("unknown opcode 0x%02x (%u)\n", (unsigned)op, (unsigned)op);
|
||||
break;
|
||||
}
|
||||
}
|
||||
source += Common::String::format("Object %s disassembly end\n", object->getName().c_str());
|
||||
return source;
|
||||
}
|
||||
|
||||
} // namespace AGDS
|
||||
220
engines/agds/process.h
Normal file
220
engines/agds/process.h
Normal file
@@ -0,0 +1,220 @@
|
||||
/* 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 PROCESS_H
|
||||
#define PROCESS_H
|
||||
|
||||
#include "agds/object.h"
|
||||
#include "agds/opcode.h"
|
||||
#include "agds/processExitCode.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stack.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
class AGDSEngine;
|
||||
class Screen;
|
||||
class Animation;
|
||||
using AnimationPtr = Common::SharedPtr<Animation>;
|
||||
|
||||
class Process {
|
||||
public:
|
||||
enum Status { kStatusActive,
|
||||
kStatusPassive,
|
||||
kStatusDone,
|
||||
kStatusError };
|
||||
|
||||
private:
|
||||
using StackType = Common::Stack<int32>;
|
||||
|
||||
AGDSEngine *_engine;
|
||||
Common::String _parentScreen;
|
||||
ObjectPtr _object;
|
||||
StackType _stack;
|
||||
unsigned _entryPoint;
|
||||
unsigned _ip, _lastIp;
|
||||
Status _status;
|
||||
bool _exited;
|
||||
ProcessExitCode _exitCode;
|
||||
Common::String _exitArg1, _exitArg2;
|
||||
int _exitIntArg1, _exitIntArg2;
|
||||
int _tileWidth, _tileHeight;
|
||||
int _tileResource;
|
||||
int _tileIndex;
|
||||
Common::String _phaseVar;
|
||||
int _timer;
|
||||
int _animationCycles;
|
||||
bool _animationLoop;
|
||||
Common::Point _animationPosition;
|
||||
int _animationZ;
|
||||
int _animationDelay;
|
||||
int _animationRandom;
|
||||
bool _phaseVarControlled;
|
||||
int _animationSpeed;
|
||||
bool _samplePeriodic;
|
||||
bool _sampleAmbient;
|
||||
int32 _sampleVolume;
|
||||
Common::Point _mousePosition;
|
||||
int _filmSubtitlesResource;
|
||||
AnimationPtr _processAnimation;
|
||||
int _version;
|
||||
|
||||
private:
|
||||
void debug(const char *str, ...);
|
||||
void error(const char *str, ...);
|
||||
|
||||
void push(bool);
|
||||
|
||||
uint8 next();
|
||||
uint16 next16() {
|
||||
uint8 l = next();
|
||||
uint16 h = next();
|
||||
return (h << 8) | l;
|
||||
}
|
||||
uint16 nextOpcode();
|
||||
|
||||
int32 pop();
|
||||
int32 top();
|
||||
|
||||
Common::String getString(int id);
|
||||
Common::String popString() {
|
||||
return getString(pop());
|
||||
}
|
||||
Common::String popText();
|
||||
uint32 popColor();
|
||||
|
||||
#define AGDS_PROCESS_METHOD(opcode, method) \
|
||||
void method();
|
||||
#define AGDS_PROCESS_METHOD_C(opcode, method) \
|
||||
void method(int8);
|
||||
#define AGDS_PROCESS_METHOD_B(opcode, method) \
|
||||
void method(uint8);
|
||||
#define AGDS_PROCESS_METHOD_W(opcode, method) \
|
||||
void method(int16);
|
||||
#define AGDS_PROCESS_METHOD_U(opcode, method) \
|
||||
void method(uint16);
|
||||
#define AGDS_PROCESS_METHOD_UD(opcode, method) \
|
||||
void method(int32);
|
||||
#define AGDS_PROCESS_METHOD_UU(opcode, method) \
|
||||
void method(uint16, uint16);
|
||||
|
||||
AGDS_OPCODE_LIST(AGDS_PROCESS_METHOD,
|
||||
AGDS_PROCESS_METHOD_C, AGDS_PROCESS_METHOD_B, AGDS_PROCESS_METHOD_W,
|
||||
AGDS_PROCESS_METHOD_U, AGDS_PROCESS_METHOD_UD, AGDS_PROCESS_METHOD_UU)
|
||||
|
||||
void moveCharacter(bool usermove);
|
||||
void tell(bool npc, const Common::String &sound);
|
||||
void tell(bool npc) { tell(npc, Common::String()); }
|
||||
|
||||
Common::String getCloneVarName(const Common::String &arg1, const Common::String &arg2);
|
||||
|
||||
void suspend(ProcessExitCode exitCode, const Common::String &arg1, const Common::String &arg2 = Common::String());
|
||||
void suspend(ProcessExitCode exitCode, int arg1 = 0, int arg2 = 0);
|
||||
|
||||
ProcessExitCode resume();
|
||||
void suspendIfPassive();
|
||||
|
||||
void setupAnimation(const AnimationPtr &animation);
|
||||
void attachInventoryObjectToMouse(bool flag);
|
||||
void leaveCharacter(const Common::String &name, const Common::String ®ionName, int dir);
|
||||
void removeScreenObject(const Common::String &name);
|
||||
|
||||
public:
|
||||
Process(AGDSEngine *engine, const ObjectPtr &object, unsigned ip, int version);
|
||||
unsigned entryPoint() const {
|
||||
return _entryPoint;
|
||||
}
|
||||
|
||||
static Common::String disassemble(const ObjectPtr &object, int version);
|
||||
|
||||
ObjectPtr getObject() const {
|
||||
return _object;
|
||||
}
|
||||
|
||||
const Common::String &getName() const {
|
||||
return _object->getName();
|
||||
}
|
||||
|
||||
const Common::String &parentScreenName() const {
|
||||
return _parentScreen;
|
||||
}
|
||||
|
||||
Status status() const {
|
||||
return _status;
|
||||
}
|
||||
|
||||
bool active() const {
|
||||
return _status == kStatusActive;
|
||||
}
|
||||
bool passive() const {
|
||||
return _status == kStatusPassive;
|
||||
}
|
||||
void activate();
|
||||
void deactivate();
|
||||
void done();
|
||||
void fail();
|
||||
|
||||
bool finished() const {
|
||||
return _status == kStatusDone || _status == kStatusError;
|
||||
}
|
||||
|
||||
void run();
|
||||
|
||||
ProcessExitCode getExitCode() const {
|
||||
return _exitCode;
|
||||
}
|
||||
|
||||
const Common::String &getExitArg1() const {
|
||||
return _exitArg1;
|
||||
}
|
||||
|
||||
int getExitIntArg1() const {
|
||||
return _exitIntArg1;
|
||||
}
|
||||
|
||||
const Common::String &getExitArg2() const {
|
||||
return _exitArg2;
|
||||
}
|
||||
|
||||
int getExitIntArg2() const {
|
||||
return _exitIntArg2;
|
||||
}
|
||||
|
||||
void setMousePosition(Common::Point mousePosition) {
|
||||
_mousePosition = mousePosition;
|
||||
}
|
||||
void updateWithCurrentMousePosition();
|
||||
|
||||
const Common::String &phaseVar() const {
|
||||
return _phaseVar;
|
||||
}
|
||||
|
||||
const AnimationPtr &processAnimation() const {
|
||||
return _processAnimation;
|
||||
}
|
||||
|
||||
void removeProcessAnimation();
|
||||
};
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif /* AGDS_PROCESS_H */
|
||||
46
engines/agds/processExitCode.h
Normal file
46
engines/agds/processExitCode.h
Normal 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 PROCESS_EXIT_CODE_H
|
||||
#define PROCESS_EXIT_CODE_H
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
enum ProcessExitCode {
|
||||
kExitCodeDestroy = 2,
|
||||
kExitCodeSuspend = 5,
|
||||
kExitCodeSetNextScreen = 6,
|
||||
kExitCodeSetNextScreenSaveOrLoad = 7,
|
||||
kExitCodeLoadScreenObject = 8,
|
||||
kExitCodeLoadScreenObjectAs = 9,
|
||||
kExitCodeLoadInventoryObject = 10,
|
||||
kExitCodeMouseAreaChange = 11,
|
||||
kExitCodeRunDialog = 12,
|
||||
kExitCodeNewGame = 13,
|
||||
kExitCodeLoadGame = 14,
|
||||
kExitCodeExitScreen = 15,
|
||||
kExitCodeCloseInventory = 16,
|
||||
kExitCodeSaveGame = 17,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
1925
engines/agds/process_opcodes.cpp
Normal file
1925
engines/agds/process_opcodes.cpp
Normal file
File diff suppressed because it is too large
Load Diff
171
engines/agds/region.cpp
Normal file
171
engines/agds/region.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
/* 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 "agds/region.h"
|
||||
#include "agds/resourceManager.h"
|
||||
#include "common/algorithm.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
Region::Region(const Common::String &resourceName, Common::SeekableReadStream &stream) {
|
||||
auto size = stream.size();
|
||||
name = readString(stream);
|
||||
center.x = stream.readSint16LE();
|
||||
center.y = stream.readSint16LE();
|
||||
flags = stream.readUint16LE();
|
||||
debug("region %s at (%d,%d) %04x", name.c_str(), center.x, center.y, flags);
|
||||
while (stream.pos() + 2 <= size) {
|
||||
uint16 ext = stream.readUint16LE();
|
||||
if (ext)
|
||||
debug("extended entries %u", ext);
|
||||
|
||||
PointsType points;
|
||||
while (ext--) {
|
||||
int16 a = stream.readSint16LE();
|
||||
int16 b = stream.readSint16LE();
|
||||
int16 c = stream.readUint16LE();
|
||||
if (c != -12851) // 0xcdcd
|
||||
debug("extended entry: %d %d %d", a, b, c);
|
||||
else
|
||||
debug("extended entry: %d %d", a, b);
|
||||
points.push_back(Common::Point(a, b));
|
||||
}
|
||||
regions.push_back(points);
|
||||
}
|
||||
}
|
||||
|
||||
Region::Region(const Common::Rect rect) : flags(0) {
|
||||
PointsType points;
|
||||
points.push_back(Common::Point(rect.left, rect.top));
|
||||
points.push_back(Common::Point(rect.right, rect.top));
|
||||
points.push_back(Common::Point(rect.right, rect.bottom));
|
||||
points.push_back(Common::Point(rect.left, rect.bottom));
|
||||
regions.push_back(points);
|
||||
|
||||
center.x = (rect.left + rect.right) / 2;
|
||||
center.y = (rect.top + rect.bottom) / 2;
|
||||
}
|
||||
|
||||
Common::String Region::toString() const {
|
||||
Common::String str = Common::String::format("region(%d, %d, [", center.x, center.y);
|
||||
for (size_t i = 0; i < regions.size(); ++i) {
|
||||
if (i != 0)
|
||||
str += ", ";
|
||||
str += "Region {";
|
||||
const PointsType &points = regions[i];
|
||||
for (size_t j = 0; j < points.size(); ++j) {
|
||||
if (j != 0)
|
||||
str += ", ";
|
||||
str += Common::String::format("(%d, %d)", points[j].x, points[j].y);
|
||||
}
|
||||
str += "}";
|
||||
}
|
||||
str += "]";
|
||||
return str;
|
||||
}
|
||||
|
||||
void Region::move(Common::Point rel) {
|
||||
if (rel.x == 0 && rel.y == 0)
|
||||
return;
|
||||
|
||||
center += rel;
|
||||
for (uint i = 0; i < regions.size(); ++i) {
|
||||
PointsType &points = regions[i];
|
||||
for (uint j = 0; j < points.size(); ++j)
|
||||
points[j] += rel;
|
||||
}
|
||||
}
|
||||
|
||||
Common::Point Region::topLeft() const {
|
||||
if (regions.empty())
|
||||
return Common::Point();
|
||||
|
||||
Common::Point p = regions[0][0];
|
||||
for (uint i = 0; i < regions.size(); ++i) {
|
||||
const PointsType &points = regions[i];
|
||||
|
||||
for (uint j = 0; j < points.size(); ++j) {
|
||||
Common::Point point = points[j];
|
||||
if (point.x < p.x)
|
||||
p.x = point.x;
|
||||
if (point.y < p.y)
|
||||
p.y = point.y;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// FIXME: copied from wintermute/base_region.cpp
|
||||
|
||||
struct dPoint {
|
||||
double x, y;
|
||||
};
|
||||
|
||||
bool Region::pointIn(Common::Point point) const {
|
||||
for (uint r = 0; r < regions.size(); ++r) {
|
||||
const PointsType &points = regions[r];
|
||||
uint32 size = points.size();
|
||||
if (size < 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
double xinters;
|
||||
dPoint p, p1, p2;
|
||||
|
||||
p.x = (double)point.x;
|
||||
p.y = (double)point.y;
|
||||
|
||||
p1.x = (double)points[0].x;
|
||||
p1.y = (double)points[0].y;
|
||||
|
||||
for (uint32 i = 1; i <= size; i++) {
|
||||
p2.x = (double)points[i % size].x;
|
||||
p2.y = (double)points[i % size].y;
|
||||
|
||||
if (p.y > MIN(p1.y, p2.y)) {
|
||||
if (p.y <= MAX(p1.y, p2.y)) {
|
||||
if (p.x <= MAX(p1.x, p2.x)) {
|
||||
if (p1.y != p2.y) {
|
||||
xinters = (p.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x;
|
||||
if (p1.x == p2.x || p.x <= xinters) {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
|
||||
if (counter % 2 == 0) {
|
||||
continue;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace AGDS
|
||||
55
engines/agds/region.h
Normal file
55
engines/agds/region.h
Normal 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 REGION_H
|
||||
#define REGION_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
struct Region {
|
||||
using PointsType = Common::Array<Common::Point>;
|
||||
|
||||
Common::String name;
|
||||
Common::Point center;
|
||||
uint16 flags;
|
||||
Common::Array<PointsType> regions;
|
||||
|
||||
Region(const Common::String &resourceName, Common::SeekableReadStream &stream);
|
||||
Region(const Common::Rect rect);
|
||||
|
||||
bool pointIn(Common::Point point) const;
|
||||
Common::String toString() const;
|
||||
void move(Common::Point rel);
|
||||
Common::Point topLeft() const;
|
||||
|
||||
bool empty() const {
|
||||
return regions.empty();
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif /* AGDS_REGION_H */
|
||||
262
engines/agds/resourceManager.cpp
Normal file
262
engines/agds/resourceManager.cpp
Normal file
@@ -0,0 +1,262 @@
|
||||
/* 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 "agds/resourceManager.h"
|
||||
#include "common/algorithm.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/path.h"
|
||||
#include "common/ptr.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "image/bmp.h"
|
||||
#include "image/pcx.h"
|
||||
#include "video/flic_decoder.h"
|
||||
|
||||
namespace AGDS {
|
||||
ResourceManager::ResourceManager(int version) : _version(version) {}
|
||||
|
||||
ResourceManager::~ResourceManager() {}
|
||||
|
||||
void ResourceManager::decrypt(uint8 *data, unsigned size) {
|
||||
static const char *kKey = "Vyvojovy tym AGDS varuje: Hackerovani skodi obchodu!";
|
||||
const char *ptr = kKey;
|
||||
while (size--) {
|
||||
*data++ ^= 0xff ^ *ptr++;
|
||||
if (*ptr == 0)
|
||||
ptr = kKey;
|
||||
}
|
||||
}
|
||||
|
||||
bool ResourceManager::GrpFile::load(const Common::Path &grpPath) {
|
||||
static const char *kSignature = "AGDS group file\x1a";
|
||||
static const uint32 kMagic = 0x1a03c9e6;
|
||||
static const uint32 kVersion1 = 44;
|
||||
static const uint32 kVersion2 = 2;
|
||||
|
||||
debug("adding path %s", grpPath.toString().c_str());
|
||||
if (!_file.open(grpPath)) {
|
||||
warning("failing opening grp file %s", grpPath.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
uint8 header[0x2c];
|
||||
if (_file.read(header, sizeof(header)) != sizeof(header)) {
|
||||
warning("short read from header");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strncmp(reinterpret_cast<const char *>(header), kSignature, 0x10) != 0) {
|
||||
decrypt(header, 0x10);
|
||||
if (strncmp(reinterpret_cast<const char *>(header), kSignature, 0x10) != 0) {
|
||||
warning("invalid signature");
|
||||
return false;
|
||||
}
|
||||
debug("load grp file %s, encrypted", grpPath.toString().c_str());
|
||||
_encrypted = true;
|
||||
} else {
|
||||
debug("load grp file %s, unencrypted", grpPath.toString().c_str());
|
||||
_encrypted = false;
|
||||
}
|
||||
|
||||
Common::MemoryReadStreamEndian reader(header + 0x10, sizeof(header) - 0x10, false);
|
||||
uint32 version1 = reader.readUint32();
|
||||
if (version1 != kVersion1) {
|
||||
warning("invalid version 1 (%d)", version1);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 magic = reader.readUint32();
|
||||
if (magic != kMagic) {
|
||||
warning("invalid magic (0x%08x)", magic);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 version2 = reader.readUint32();
|
||||
if (version2 != kVersion2) {
|
||||
warning("invalid version 2 (%d)", version2);
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned dirCount = reader.readUint32();
|
||||
if (!reader.skip(3 * 4))
|
||||
return false;
|
||||
|
||||
// debug("+%u files in index", dirCount);
|
||||
while (dirCount--) {
|
||||
uint8 dirData[0x31];
|
||||
uint8 *dirDataEnd = dirData + sizeof(dirData);
|
||||
|
||||
if (_file.read(dirData, sizeof(dirData)) != sizeof(dirData)) {
|
||||
warning("short read, corrupted file");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8 *nameEnd = Common::find(dirData, dirDataEnd, 0);
|
||||
if (nameEnd == dirDataEnd) {
|
||||
warning("corrupted entry at %d", (int)_file.pos() - 0x31);
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned nameLength = nameEnd - dirData;
|
||||
if (_encrypted)
|
||||
decrypt(dirData, nameLength);
|
||||
Common::String name(reinterpret_cast<char *>(dirData), nameLength);
|
||||
|
||||
Common::MemoryReadStreamEndian dirReader(dirData + 0x21, 8, false);
|
||||
|
||||
uint32 offset = dirReader.readSint32();
|
||||
uint32 size = dirReader.readSint32();
|
||||
// debug("\t\tfile %s %u %u", name.c_str(), offset, size);
|
||||
ArchiveMemberPtr resource(new ArchiveMember(this, name, offset, size));
|
||||
_members.setVal(name, resource);
|
||||
}
|
||||
|
||||
debug("%s: %u files in index", grpPath.toString().c_str(), _members.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
int ResourceManager::GrpFile::listMembers(Common::ArchiveMemberList &list) const {
|
||||
int size = 0;
|
||||
for (MembersType::const_iterator i = _members.begin(); i != _members.end(); ++i, ++size)
|
||||
list.push_back(i->_value);
|
||||
return size;
|
||||
}
|
||||
|
||||
bool ResourceManager::GrpFile::hasFile(const Common::Path &name) const {
|
||||
return _members.find(name.toString()) != _members.end();
|
||||
}
|
||||
|
||||
const Common::ArchiveMemberPtr ResourceManager::GrpFile::getMember(const Common::Path &name) const {
|
||||
Common::ArchiveMemberPtr member;
|
||||
MembersType::const_iterator i = _members.find(name.toString());
|
||||
if (i != _members.end())
|
||||
member = i->_value;
|
||||
return member;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *ResourceManager::GrpFile::createReadStreamForMember(const Common::Path &name) const {
|
||||
Common::ArchiveMemberPtr member = getMember(name);
|
||||
return member ? member->createReadStream() : NULL;
|
||||
}
|
||||
|
||||
bool ResourceManager::addPath(const Common::Path &grpFilename) {
|
||||
Common::ScopedPtr<GrpFile> grpFile(new GrpFile());
|
||||
if (!grpFile->load(grpFilename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SearchMan.add(grpFilename.toString(), grpFile.release(), 0, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *ResourceManager::ArchiveMember::createReadStream() const {
|
||||
Common::SeekableReadStream &file = _parent->getArchiveStream();
|
||||
file.seek(_offset);
|
||||
return file.readStream(_size);
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *ResourceManager::getResource(const Common::String &name) const {
|
||||
Common::File file;
|
||||
return (file.open(Common::Path{name})) ? file.readStream(file.size()) : NULL;
|
||||
}
|
||||
|
||||
bool ResourceManager::IsBMP(Common::SeekableReadStream &stream) {
|
||||
auto b0 = stream.readByte();
|
||||
auto b1 = stream.readByte();
|
||||
stream.seek(-2, SEEK_CUR);
|
||||
return b0 == 'B' && b1 == 'M';
|
||||
}
|
||||
|
||||
Graphics::Surface *ResourceManager::loadPicture(const Common::String &name, const Graphics::PixelFormat &format) {
|
||||
Common::SeekableReadStream *stream = getResource(name);
|
||||
if (!stream)
|
||||
return NULL;
|
||||
|
||||
auto is_bmp = IsBMP(*stream);
|
||||
|
||||
Common::String lname = name;
|
||||
lname.toLowercase();
|
||||
|
||||
if (lname.hasSuffix(".bmp") || is_bmp) {
|
||||
Image::BitmapDecoder bmp;
|
||||
return bmp.loadStream(*stream) ? bmp.getSurface()->convertTo(format) : NULL;
|
||||
} else if (lname.hasSuffix(".pcx")) {
|
||||
Image::PCXDecoder pcx;
|
||||
return pcx.loadStream(*stream) ? pcx.getSurface()->convertTo(format) : NULL;
|
||||
} else if (lname.hasSuffix(".flc")) {
|
||||
Video::FlicDecoder flic;
|
||||
if (!flic.loadStream(stream)) {
|
||||
warning("flic decoder failed to load %s", name.c_str());
|
||||
return NULL;
|
||||
}
|
||||
const Graphics::Surface *surface = flic.decodeNextFrame();
|
||||
return surface ? surface->convertTo(format, flic.getPalette()) : NULL;
|
||||
} else
|
||||
warning("unknown extensions for resource %s", name.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Common::String ResourceManager::loadText(Common::SeekableReadStream &stream) const {
|
||||
Common::Array<char> text(stream.size());
|
||||
if (stream.read(text.data(), text.size()) != text.size())
|
||||
error("short read from text resource");
|
||||
|
||||
if (text.empty())
|
||||
return Common::String();
|
||||
|
||||
char *begin = text.data();
|
||||
char *end = begin + text.size();
|
||||
while (begin != end && end[-1] == 0)
|
||||
--end;
|
||||
|
||||
if (_version != 0 || (text[0] < ' ' || text[0] >= 0x7f))
|
||||
decrypt(reinterpret_cast<uint8 *>(text.data()), end - begin);
|
||||
|
||||
while (begin != end && end[-1] == 0)
|
||||
--end;
|
||||
|
||||
return Common::String(begin, end);
|
||||
}
|
||||
|
||||
Common::String ResourceManager::loadText(const Common::String &name) const {
|
||||
Common::ScopedPtr<Common::SeekableReadStream> stream(getResource(name));
|
||||
if (!stream)
|
||||
error("no text resource %s", name.c_str());
|
||||
return loadText(*stream);
|
||||
}
|
||||
|
||||
Common::String readString(Common::ReadStream &stream, uint size) {
|
||||
Common::Array<char> text(size);
|
||||
if (stream.read(text.data(), text.size()) != text.size())
|
||||
error("readString: short read");
|
||||
|
||||
return Common::String(text.data(), strlen(text.data()));
|
||||
}
|
||||
|
||||
void writeString(Common::WriteStream &stream, const Common::String &string, uint size) {
|
||||
Common::Array<char> text(size);
|
||||
memcpy(text.data(), string.c_str(), MIN(string.size(), size));
|
||||
|
||||
if (stream.write(text.data(), text.size()) != text.size())
|
||||
error("writeString: short write");
|
||||
}
|
||||
|
||||
} // End of namespace AGDS
|
||||
112
engines/agds/resourceManager.h
Normal file
112
engines/agds/resourceManager.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef RESOURCE_MANAGER_H
|
||||
#define RESOURCE_MANAGER_H
|
||||
|
||||
#include "common/archive.h"
|
||||
#include "common/file.h"
|
||||
#include "common/hash-str.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/ptr.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
struct PixelFormat;
|
||||
} // namespace Graphics
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
class ResourceManager {
|
||||
private:
|
||||
class GrpFile;
|
||||
class ArchiveMember : public Common::ArchiveMember {
|
||||
GrpFile *_parent;
|
||||
Common::String _name;
|
||||
uint32 _offset;
|
||||
uint32 _size;
|
||||
|
||||
public:
|
||||
ArchiveMember(GrpFile *parent, const Common::String &name, uint32 offset, uint32 size) : _parent(parent), _name(name), _offset(offset), _size(size) {
|
||||
}
|
||||
Common::SeekableReadStream *createReadStream() const override;
|
||||
Common::String getName() const override {
|
||||
return getFileName();
|
||||
}
|
||||
Common::Path getPathInArchive() const override {
|
||||
return Common::Path{_name};
|
||||
}
|
||||
Common::String getFileName() const override {
|
||||
return _name;
|
||||
}
|
||||
Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType) const override {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
using ArchiveMemberPtr = Common::SharedPtr<ArchiveMember>;
|
||||
|
||||
class GrpFile : public Common::Archive {
|
||||
Common::File _file;
|
||||
bool _encrypted;
|
||||
|
||||
using MembersType = Common::HashMap<Common::String, ArchiveMemberPtr, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo>;
|
||||
MembersType _members;
|
||||
|
||||
public:
|
||||
bool load(const Common::Path &path);
|
||||
|
||||
Common::SeekableReadStream &getArchiveStream() {
|
||||
return _file;
|
||||
}
|
||||
|
||||
bool hasFile(const Common::Path &name) const override;
|
||||
int listMembers(Common::ArchiveMemberList &list) const override;
|
||||
const Common::ArchiveMemberPtr getMember(const Common::Path &name) const override;
|
||||
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &name) const override;
|
||||
};
|
||||
|
||||
static void decrypt(uint8 *data, unsigned size);
|
||||
|
||||
int _version;
|
||||
|
||||
public:
|
||||
ResourceManager(int version);
|
||||
~ResourceManager();
|
||||
|
||||
static bool IsBMP(Common::SeekableReadStream &stream);
|
||||
|
||||
bool addPath(const Common::Path &grpFilename);
|
||||
|
||||
Common::SeekableReadStream *getResource(const Common::String &name) const;
|
||||
Graphics::Surface *loadPicture(const Common::String &name, const Graphics::PixelFormat &format);
|
||||
|
||||
Common::String loadText(Common::SeekableReadStream &stream) const;
|
||||
Common::String loadText(const Common::String &name) const;
|
||||
};
|
||||
|
||||
Common::String readString(Common::ReadStream &stream, uint size = 32);
|
||||
void writeString(Common::WriteStream &stream, const Common::String &string, uint size = 32);
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif /* AGDS_RESOURCE_MANAGER_H */
|
||||
348
engines/agds/screen.cpp
Normal file
348
engines/agds/screen.cpp
Normal file
@@ -0,0 +1,348 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "agds/screen.h"
|
||||
#include "agds/agds.h"
|
||||
#include "agds/animation.h"
|
||||
#include "agds/character.h"
|
||||
#include "agds/object.h"
|
||||
#include "agds/patch.h"
|
||||
#include "agds/region.h"
|
||||
#include "common/system.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
int Screen::ObjectZCompare(const ObjectPtr &a, const ObjectPtr &b) {
|
||||
return b->z() - a->z();
|
||||
}
|
||||
|
||||
int Screen::AnimationZCompare(const ScreenAnimationDesc &a, const ScreenAnimationDesc &b) {
|
||||
return b.animation->z() - a.animation->z();
|
||||
}
|
||||
|
||||
Screen::Screen(AGDSEngine *engine, ObjectPtr object, ScreenLoadingType loadingType, const Common::String &prevScreen) : _engine(engine), _object(object), _background(nullptr),
|
||||
_name(object->getName()), _loadingType(loadingType), _previousScreen(prevScreen),
|
||||
_children(&ObjectZCompare), _animations(&AnimationZCompare), _applyingPatch(false),
|
||||
_characterNear(g_system->getHeight()), _characterFar(g_system->getHeight()), _fade(0) {
|
||||
add(object);
|
||||
}
|
||||
|
||||
Screen::~Screen() {
|
||||
_children.clear();
|
||||
}
|
||||
|
||||
void Screen::scrollTo(Common::Point scroll) {
|
||||
int windowW = g_system->getWidth();
|
||||
int windowH = g_system->getHeight();
|
||||
|
||||
if (!_background) {
|
||||
_scroll.x = _scroll.y = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
auto rect = _background->getRect();
|
||||
int w = rect.width(), h = rect.height();
|
||||
debug("picture size %dx%d", w, h);
|
||||
if (scroll.x + windowW > w)
|
||||
scroll.x = w - windowW;
|
||||
if (scroll.y + windowH > h)
|
||||
scroll.y = h - windowH;
|
||||
if (scroll.x < 0)
|
||||
scroll.x = 0;
|
||||
if (scroll.y < 0)
|
||||
scroll.y = 0;
|
||||
|
||||
debug("setting scroll to %d,%d", scroll.x, scroll.y);
|
||||
_scroll = scroll;
|
||||
}
|
||||
|
||||
float Screen::getZScale(int y) const {
|
||||
int h = g_system->getHeight();
|
||||
int dy = h - y;
|
||||
if (dy > _characterNear) {
|
||||
if (dy < _characterFar)
|
||||
return 1.0f * (_characterFar - dy) / (_characterFar - _characterNear);
|
||||
else
|
||||
return 0.0f;
|
||||
} else
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
bool Screen::add(ObjectPtr object) {
|
||||
if (object == NULL) {
|
||||
warning("refusing to add null to scene");
|
||||
return false;
|
||||
}
|
||||
for (auto i = _children.begin(); i != _children.end();) {
|
||||
if (*i == object) {
|
||||
if (object->alive()) {
|
||||
debug("double adding object %s", object->getName().c_str());
|
||||
return false;
|
||||
} else {
|
||||
debug("recovering object %s", object->getName().c_str());
|
||||
object->alive(true);
|
||||
object->allowInitialise(false);
|
||||
return true;
|
||||
}
|
||||
} else
|
||||
++i;
|
||||
}
|
||||
_children.insert(object);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Screen::add(AnimationPtr animation) {
|
||||
if (animation)
|
||||
_animations.insert({animation});
|
||||
else
|
||||
warning("Screen: skipping null animation");
|
||||
}
|
||||
|
||||
bool Screen::remove(const AnimationPtr &animation) {
|
||||
if (!animation)
|
||||
return false;
|
||||
|
||||
bool removed = false;
|
||||
for (auto i = _animations.begin(); i != _animations.end(); ++i) {
|
||||
if (i->animation == animation) {
|
||||
debug("removing animation %s:%s", animation->process().c_str(), animation->phaseVar().c_str());
|
||||
i->removed = true;
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
ObjectPtr Screen::find(const Common::String &name) {
|
||||
for (auto i = _children.begin(); i != _children.end(); ++i) {
|
||||
ObjectPtr &object = *i;
|
||||
if (object->getName() == name)
|
||||
return *i;
|
||||
}
|
||||
return ObjectPtr();
|
||||
}
|
||||
|
||||
bool Screen::remove(const ObjectPtr &object) {
|
||||
for (auto i = _children.begin(); i != _children.end(); ++i) {
|
||||
if (*i == object) {
|
||||
if (object->locked())
|
||||
object->alive(false);
|
||||
else
|
||||
_children.erase(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Screen::remove(const Common::String &name) {
|
||||
for (auto i = _children.begin(); i != _children.end(); ++i) {
|
||||
const ObjectPtr &object = *i;
|
||||
if (object->getName() == name) {
|
||||
if (object->locked())
|
||||
object->alive(false);
|
||||
else
|
||||
_children.erase(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
AnimationPtr Screen::findAnimationByPhaseVar(const Common::String &phaseVar) {
|
||||
for (auto i = _animations.begin(); i != _animations.end(); ++i) {
|
||||
const auto &desc = *i;
|
||||
const auto &animation = desc.animation;
|
||||
if (!desc.removed && animation->phaseVar() == phaseVar)
|
||||
return animation;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Screen::tick() {
|
||||
for (uint i = 0; i < _animations.size();) {
|
||||
const auto &desc = _animations.data()[i];
|
||||
const auto &animation = desc.animation;
|
||||
if (!desc.removed && animation->tick())
|
||||
++i;
|
||||
else {
|
||||
debug("removing animation %s:%s", animation->process().c_str(), animation->phaseVar().c_str());
|
||||
_animations.erase(_animations.begin() + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::paint(Graphics::Surface &backbuffer) const {
|
||||
auto ¤tInventoryObject = _engine->currentInventoryObject();
|
||||
Character *character = _engine->currentCharacter();
|
||||
|
||||
auto child = _children.begin();
|
||||
auto desc = _animations.begin();
|
||||
while (child != _children.end() || desc != _animations.end() || character) {
|
||||
bool child_valid = child != _children.end();
|
||||
bool animation_valid = desc != _animations.end();
|
||||
if (animation_valid && desc->removed) {
|
||||
++desc;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool z_valid = false;
|
||||
int z = 0;
|
||||
int render_type = -1;
|
||||
if (animation_valid) {
|
||||
const auto &animation = desc->animation;
|
||||
if (!z_valid || animation->z() > z) {
|
||||
z = animation->z();
|
||||
z_valid = true;
|
||||
render_type = 1;
|
||||
}
|
||||
}
|
||||
if (child_valid) {
|
||||
if (!z_valid || (*child)->z() > z) {
|
||||
z = (*child)->z();
|
||||
z_valid = true;
|
||||
render_type = 0;
|
||||
}
|
||||
}
|
||||
if (character) {
|
||||
if (!z_valid || character->z() > z) {
|
||||
z = character->z();
|
||||
z_valid = true;
|
||||
render_type = 2;
|
||||
}
|
||||
}
|
||||
|
||||
auto basePos = Common::Point() - _scroll;
|
||||
switch (render_type) {
|
||||
case 0:
|
||||
// debug("object z: %d", (*child)->z());
|
||||
if ((*child) != currentInventoryObject && (*child)->alive()) {
|
||||
if ((*child)->scale() < 0)
|
||||
basePos = Common::Point();
|
||||
(*child)->paint(*_engine, backbuffer, basePos);
|
||||
}
|
||||
++child;
|
||||
break;
|
||||
case 1:
|
||||
// debug("animation z: %d", (*animation)->z());
|
||||
if ((*desc).animation->scale() < 0)
|
||||
basePos = Common::Point();
|
||||
(*desc).animation->paint(backbuffer, basePos);
|
||||
++desc;
|
||||
break;
|
||||
case 2:
|
||||
// debug("character z: %d", character->z());
|
||||
character->paint(backbuffer, basePos);
|
||||
character = nullptr;
|
||||
break;
|
||||
default:
|
||||
error("invalid logic in z-sort");
|
||||
}
|
||||
}
|
||||
if (_fade != 0) {
|
||||
auto alpha = (100 - _fade) * 256 / 100;
|
||||
auto &format = backbuffer.format;
|
||||
for (int y = 0; y < backbuffer.h; ++y) {
|
||||
uint32 *line = (uint32 *)backbuffer.getBasePtr(0, y);
|
||||
int w = backbuffer.w;
|
||||
while (w--) {
|
||||
uint8 r, g, b;
|
||||
format.colorToRGB(*line, r, g, b);
|
||||
r = (r * alpha) >> 8;
|
||||
g = (g * alpha) >> 8;
|
||||
b = (b * alpha) >> 8;
|
||||
*line++ = format.RGBToColor(r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Common::Array<ObjectPtr> Screen::find(Common::Point pos) const {
|
||||
Common::Array<ObjectPtr> objects;
|
||||
objects.reserve(_children.size());
|
||||
for (auto i = _children.begin(); i != _children.end(); ++i) {
|
||||
const auto &object = *i;
|
||||
auto visiblePos = (object->scale() >= 0) ? pos + _scroll : pos;
|
||||
if (object->pointIn(visiblePos) && object->alive()) {
|
||||
objects.insert_at(0, object);
|
||||
}
|
||||
}
|
||||
Character *character = _engine->currentCharacter();
|
||||
if (character) {
|
||||
if (character->pointIn(pos - _scroll)) {
|
||||
objects.insert_at(0, character->object());
|
||||
}
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
|
||||
Screen::KeyHandler Screen::findKeyHandler(const Common::String &keyName) {
|
||||
KeyHandler keyHandler;
|
||||
for (auto i = _children.begin(); i != _children.end(); ++i) {
|
||||
const auto &object = *i;
|
||||
if (!object->alive())
|
||||
continue;
|
||||
|
||||
uint ip = object->getKeyHandler(keyName);
|
||||
if (ip) {
|
||||
keyHandler.ip = ip;
|
||||
keyHandler.object = object;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return keyHandler;
|
||||
}
|
||||
|
||||
void Screen::load(const PatchPtr &patch) {
|
||||
debug("applying patch with %u objects", patch->objects.size());
|
||||
_applyingPatch = true;
|
||||
for (uint i = 0; i < patch->objects.size(); ++i) {
|
||||
const Patch::Object &object = patch->objects[i];
|
||||
debug("patch object %s %d", object.name.c_str(), object.flag);
|
||||
if (object.name == _name)
|
||||
continue;
|
||||
else if (object.flag <= 0)
|
||||
remove(object.name);
|
||||
else
|
||||
_engine->runObject(object.name, Common::String(), false);
|
||||
}
|
||||
_loadingType = patch->loadingType;
|
||||
if (!patch->prevScreenName.empty())
|
||||
_previousScreen = patch->prevScreenName;
|
||||
_applyingPatch = false;
|
||||
}
|
||||
|
||||
void Screen::save(const PatchPtr &patch) {
|
||||
patch->screenSaved = 1;
|
||||
patch->prevScreenName = _previousScreen;
|
||||
patch->loadingType = _loadingType;
|
||||
patch->objects.clear();
|
||||
for (auto i = _children.begin(); i != _children.end(); ++i) {
|
||||
const auto &object = *i;
|
||||
if (!object->persistent() || !object->alive() || object->getName() == _name)
|
||||
continue;
|
||||
debug("saving patch object %s %d", object->getName().c_str(), object->alive());
|
||||
patch->objects.push_back(Patch::Object(object->getName(), 1));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AGDS
|
||||
173
engines/agds/screen.h
Normal file
173
engines/agds/screen.h
Normal file
@@ -0,0 +1,173 @@
|
||||
/* 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 SCREEN_H
|
||||
#define SCREEN_H
|
||||
|
||||
#include "agds/screenLoadingType.h"
|
||||
#include "common/array.h"
|
||||
#include "common/ptr.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
class AGDSEngine;
|
||||
class Animation;
|
||||
using AnimationPtr = Common::SharedPtr<Animation>;
|
||||
class Object;
|
||||
using ObjectPtr = Common::SharedPtr<Object>;
|
||||
struct Region;
|
||||
using RegionPtr = Common::SharedPtr<Region>;
|
||||
struct Patch;
|
||||
using PatchPtr = Common::SharedPtr<Patch>;
|
||||
|
||||
struct ScreenAnimationDesc {
|
||||
AnimationPtr animation;
|
||||
bool removed = false;
|
||||
ScreenAnimationDesc(const AnimationPtr &a) : animation(a) {
|
||||
}
|
||||
};
|
||||
|
||||
class Screen {
|
||||
static int ObjectZCompare(const ObjectPtr &a, const ObjectPtr &b);
|
||||
static int AnimationZCompare(const ScreenAnimationDesc &a, const ScreenAnimationDesc &b);
|
||||
|
||||
using Animations = Common::SortedArray<ScreenAnimationDesc, const ScreenAnimationDesc &>;
|
||||
using Children = Common::SortedArray<ObjectPtr, const ObjectPtr &>;
|
||||
|
||||
AGDSEngine *_engine;
|
||||
ObjectPtr _object;
|
||||
Object *_background;
|
||||
Common::Point _scroll;
|
||||
Common::String _name;
|
||||
ScreenLoadingType _loadingType;
|
||||
Common::String _previousScreen;
|
||||
Children _children;
|
||||
Animations _animations;
|
||||
RegionPtr _region;
|
||||
bool _applyingPatch;
|
||||
int _characterNear, _characterFar;
|
||||
int _fade;
|
||||
|
||||
public:
|
||||
struct KeyHandler {
|
||||
ObjectPtr object;
|
||||
uint ip;
|
||||
|
||||
KeyHandler() : object(), ip() {}
|
||||
KeyHandler(Object *o, uint i) : object(o), ip(i) {}
|
||||
};
|
||||
|
||||
Screen(AGDSEngine *engine, ObjectPtr object, ScreenLoadingType loadingType, const Common::String &prevScreen);
|
||||
~Screen();
|
||||
|
||||
int fade() const {
|
||||
return _fade;
|
||||
}
|
||||
|
||||
void fade(int fade) {
|
||||
_fade = fade;
|
||||
}
|
||||
|
||||
void setCharacterNearFar(int near, int far) {
|
||||
_characterNear = near;
|
||||
_characterFar = far;
|
||||
}
|
||||
|
||||
float getZScale(int y) const;
|
||||
|
||||
bool applyingPatch() const {
|
||||
return _applyingPatch;
|
||||
}
|
||||
|
||||
ObjectPtr getObject() {
|
||||
return _object;
|
||||
}
|
||||
|
||||
const Common::String &getName() const {
|
||||
return _name;
|
||||
}
|
||||
|
||||
const Common::String &getPreviousScreenName() const {
|
||||
return _previousScreen;
|
||||
}
|
||||
|
||||
ScreenLoadingType loadingType() const {
|
||||
return _loadingType;
|
||||
}
|
||||
|
||||
const RegionPtr ®ion() const {
|
||||
return _region;
|
||||
}
|
||||
|
||||
void region(RegionPtr region) {
|
||||
_region = region;
|
||||
}
|
||||
|
||||
const Children &children() const {
|
||||
return _children;
|
||||
}
|
||||
|
||||
const Animations &animations() const {
|
||||
return _animations;
|
||||
}
|
||||
void scrollTo(Common::Point scroll);
|
||||
Common::Point scrollPosition() const {
|
||||
return _scroll;
|
||||
}
|
||||
|
||||
bool add(ObjectPtr object);
|
||||
void add(AnimationPtr animation);
|
||||
bool remove(const AnimationPtr &animation);
|
||||
|
||||
void update(const ObjectPtr &object) {
|
||||
bool found = remove(object);
|
||||
if (found)
|
||||
add(object);
|
||||
}
|
||||
|
||||
void setBackground(Object *object) {
|
||||
_background = object;
|
||||
}
|
||||
|
||||
bool remove(const Common::String &name);
|
||||
bool remove(const ObjectPtr &object);
|
||||
|
||||
void tick();
|
||||
void paint(Graphics::Surface &backbuffer) const;
|
||||
Common::Array<ObjectPtr> find(Common::Point pos) const;
|
||||
ObjectPtr find(const Common::String &name);
|
||||
KeyHandler findKeyHandler(const Common::String &keyName);
|
||||
AnimationPtr findAnimationByPhaseVar(const Common::String &phaseVar);
|
||||
|
||||
void load(const PatchPtr &patch);
|
||||
void save(const PatchPtr &patch);
|
||||
};
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif /* AGDS_SCREEN_H */
|
||||
35
engines/agds/screenLoadingType.h
Normal file
35
engines/agds/screenLoadingType.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/* 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 SCREEN_LOADING_TYPE_H
|
||||
#define SCREEN_LOADING_TYPE_H
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
enum struct ScreenLoadingType {
|
||||
Normal = 0,
|
||||
SaveOrLoad = 1,
|
||||
Previous = 2
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
175
engines/agds/soundManager.cpp
Normal file
175
engines/agds/soundManager.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
/* 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 "agds/soundManager.h"
|
||||
#include "agds/agds.h"
|
||||
#include "agds/object.h"
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/vorbis.h"
|
||||
#include "audio/decoders/wave.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
void SoundManager::tick() {
|
||||
for (auto it = _sounds.begin(); it != _sounds.end();) {
|
||||
Sound &sound = *it;
|
||||
auto &phaseVar = sound.phaseVar;
|
||||
|
||||
bool active = playing(sound.id);
|
||||
if (!sound.phaseVar.empty()) {
|
||||
int value = _engine->getGlobal(sound.phaseVar);
|
||||
if (value <= 1) {
|
||||
if (value == 1 && !active) {
|
||||
debug("sample %s:%s resets phase var to 0", sound.resource.c_str(), sound.filename.c_str());
|
||||
_engine->setGlobal(phaseVar, 0);
|
||||
}
|
||||
} else if (value & 2) {
|
||||
debug("sample %s:%s restarts (via phase var)", sound.resource.c_str(), sound.filename.c_str());
|
||||
_engine->setGlobal(phaseVar, 1);
|
||||
_mixer->stopID(sound.id);
|
||||
play(sound.process, sound.resource, sound.filename, sound.phaseVar, true, sound.volume, sound.pan, sound.id);
|
||||
} else if (value & 4) {
|
||||
debug("sample %s:%s stops (via phase var)", sound.resource.c_str(), sound.filename.c_str());
|
||||
_mixer->stopID(sound.id);
|
||||
_engine->setGlobal(phaseVar, 0);
|
||||
}
|
||||
++it;
|
||||
} else if (!active) {
|
||||
_engine->reactivate(sound.process, "sound " + sound.resource + " inactive", true);
|
||||
it = _sounds.erase(it);
|
||||
} else
|
||||
++it;
|
||||
}
|
||||
_engine->runPendingReactivatedProcesses();
|
||||
}
|
||||
|
||||
const Sound *SoundManager::find(int id) const {
|
||||
for (auto i = _sounds.begin(); i != _sounds.end(); ++i) {
|
||||
auto &sound = *i;
|
||||
if (sound.id == id)
|
||||
return &sound;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Sound *SoundManager::findSampleByPhaseVar(const Common::String &phaseVar) {
|
||||
if (phaseVar.empty())
|
||||
return nullptr;
|
||||
|
||||
for (auto i = _sounds.begin(); i != _sounds.end(); ++i) {
|
||||
auto &sound = *i;
|
||||
if (sound.phaseVar == phaseVar) {
|
||||
return &sound;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SoundManager::stopAll() {
|
||||
_mixer->stopAll();
|
||||
for (auto i = _sounds.begin(); i != _sounds.end(); ++i) {
|
||||
auto &sound = *i;
|
||||
if (!sound.phaseVar.empty())
|
||||
_engine->setGlobal(sound.phaseVar, 0);
|
||||
}
|
||||
_sounds.clear();
|
||||
}
|
||||
|
||||
void SoundManager::stopAllFrom(const Common::String &process) {
|
||||
for (auto i = _sounds.begin(); i != _sounds.end();) {
|
||||
auto &sound = *i;
|
||||
if (sound.process == process) {
|
||||
_mixer->stopID(sound.id);
|
||||
if (!sound.phaseVar.empty())
|
||||
_engine->setGlobal(sound.phaseVar, 0);
|
||||
i = _sounds.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int SoundManager::play(Common::String process, const Common::String &resource, const Common::String &filename, const Common::String &phaseVar, bool startPlaying, int volume, int pan, int id, bool ambient) {
|
||||
debug("SoundMan::play(process: '%s', resource: '%s', filename: '%s', phaseVar: '%s', start: %d, volume: %d, pan: %d, id: %d, ambient: %d", process.c_str(), resource.c_str(), filename.c_str(), phaseVar.c_str(), startPlaying, volume, pan, id, ambient);
|
||||
if (filename.empty())
|
||||
return -1;
|
||||
|
||||
{
|
||||
auto sample = findSampleByPhaseVar(phaseVar);
|
||||
if (sample && playing(sample->id)) {
|
||||
debug("already playing");
|
||||
return sample->id;
|
||||
}
|
||||
}
|
||||
Common::ScopedPtr<Common::File> file(new Common::File());
|
||||
if (!file->open(Common::Path{filename})) {
|
||||
if (!phaseVar.empty())
|
||||
_engine->setGlobal(phaseVar, 1);
|
||||
warning("no sound %s", filename.c_str());
|
||||
return -1;
|
||||
}
|
||||
if (ambient)
|
||||
process.clear();
|
||||
|
||||
Common::String lname(filename);
|
||||
lname.toLowercase();
|
||||
|
||||
Audio::SeekableAudioStream *stream = NULL;
|
||||
if (lname.hasSuffix(".ogg")) {
|
||||
#ifdef USE_VORBIS
|
||||
stream = Audio::makeVorbisStream(file.release(), DisposeAfterUse::YES);
|
||||
#endif
|
||||
} else if (lname.hasSuffix(".wav")) {
|
||||
stream = Audio::makeWAVStream(file.release(), DisposeAfterUse::YES);
|
||||
}
|
||||
if (!stream) {
|
||||
warning("could not play sound %s", filename.c_str());
|
||||
if (!phaseVar.empty())
|
||||
_engine->setGlobal(phaseVar, _engine->getGlobal(phaseVar) ? 1 : 0);
|
||||
else
|
||||
_engine->reactivate(process, "no sound");
|
||||
return -1;
|
||||
}
|
||||
if (id == -1)
|
||||
id = _nextId++;
|
||||
|
||||
_sounds.push_back(Sound(id, process, resource, filename, phaseVar, volume, pan));
|
||||
auto handle = &_sounds.back().handle;
|
||||
if (startPlaying)
|
||||
_mixer->playStream(
|
||||
ambient ? Audio::Mixer::kMusicSoundType : Audio::Mixer::kPlainSoundType,
|
||||
&_sounds.back().handle, stream, id,
|
||||
volume * Audio::Mixer::kMaxChannelVolume / 100, pan * 127 / 100);
|
||||
if (ambient)
|
||||
_mixer->loopChannel(*handle);
|
||||
// if (sound_off)
|
||||
// setPhaseVar(_sounds.back(), 1);
|
||||
return id;
|
||||
}
|
||||
|
||||
bool SoundManager::playing(int id) const {
|
||||
return _mixer->isSoundIDActive(id);
|
||||
}
|
||||
|
||||
} // namespace AGDS
|
||||
84
engines/agds/soundManager.h
Normal file
84
engines/agds/soundManager.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/* 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 SOUND_MANAGER_H
|
||||
#define SOUND_MANAGER_H
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/list.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
namespace Audio {
|
||||
class Mixer;
|
||||
}
|
||||
|
||||
namespace AGDS {
|
||||
class AGDSEngine;
|
||||
|
||||
struct Sound {
|
||||
int id;
|
||||
Common::String process;
|
||||
Common::String resource;
|
||||
Common::String filename;
|
||||
Common::String phaseVar;
|
||||
Audio::SoundHandle handle;
|
||||
int volume;
|
||||
int pan;
|
||||
int group;
|
||||
bool paused;
|
||||
Sound(int id_, const Common::String &process_, const Common::String &res, const Common::String &filename_, const Common::String &var, int volume_, int pan_, int group_ = 0) : id(id_), process(process_), resource(res), filename(filename_), phaseVar(var), handle(), volume(volume_), pan(pan_), group(group_), paused(false) {
|
||||
}
|
||||
|
||||
int leftVolume() const {
|
||||
return pan < 0 ? volume : volume * (100 - pan) / 100;
|
||||
}
|
||||
|
||||
int rightVolume() const {
|
||||
return pan < 0 ? volume * (100 + pan) / 100 : volume;
|
||||
}
|
||||
};
|
||||
|
||||
class SoundManager {
|
||||
using SoundList = Common::List<Sound>;
|
||||
int _nextId;
|
||||
AGDSEngine *_engine;
|
||||
Audio::Mixer *_mixer;
|
||||
SoundList _sounds;
|
||||
|
||||
public:
|
||||
SoundManager(AGDSEngine *engine, Audio::Mixer *mixer) : _nextId(1), _engine(engine), _mixer(mixer) {}
|
||||
void tick();
|
||||
int play(Common::String process, const Common::String &resource, const Common::String &filename, const Common::String &phaseVar, bool startPlaying, int volume, int pan, int id = -1, bool ambient = false);
|
||||
bool playing(int id) const;
|
||||
void stopAllFrom(const Common::String &process);
|
||||
void stopAll();
|
||||
const Sound *find(int id) const;
|
||||
Sound *findSampleByPhaseVar(const Common::String &phaseVar);
|
||||
};
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif /* AGDS_SOUND_MANAGER_H */
|
||||
86
engines/agds/systemVariable.cpp
Normal file
86
engines/agds/systemVariable.cpp
Normal 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 "agds/systemVariable.h"
|
||||
#include "common/array.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
const Common::String &IntegerSystemVariable::getString() const {
|
||||
error("invalid type");
|
||||
}
|
||||
|
||||
int IntegerSystemVariable::getInteger() const {
|
||||
return _value;
|
||||
}
|
||||
|
||||
void IntegerSystemVariable::setString(const Common::String &value) {
|
||||
error("invalid type");
|
||||
}
|
||||
|
||||
void IntegerSystemVariable::setInteger(int value) {
|
||||
_value = value;
|
||||
}
|
||||
|
||||
void IntegerSystemVariable::read(Common::ReadStream &stream) {
|
||||
_value = stream.readSint32LE();
|
||||
}
|
||||
|
||||
void IntegerSystemVariable::write(Common::WriteStream &stream) const {
|
||||
stream.writeSint32LE(_value);
|
||||
}
|
||||
|
||||
const Common::String &StringSystemVariable::getString() const {
|
||||
return _value;
|
||||
}
|
||||
|
||||
int StringSystemVariable::getInteger() const {
|
||||
error("invalid type");
|
||||
}
|
||||
|
||||
void StringSystemVariable::setString(const Common::String &value) {
|
||||
_value = value;
|
||||
}
|
||||
|
||||
void StringSystemVariable::setInteger(int value) {
|
||||
error("invalid type");
|
||||
}
|
||||
|
||||
void StringSystemVariable::read(Common::ReadStream &stream) {
|
||||
byte len = stream.readByte();
|
||||
if (len == 0)
|
||||
error("invalid string var length");
|
||||
Common::Array<char> str(len);
|
||||
stream.read(str.data(), str.size());
|
||||
_value = Common::String(str.data(), len - 1);
|
||||
}
|
||||
|
||||
void StringSystemVariable::write(Common::WriteStream &stream) const {
|
||||
uint len = _value.size() + 1;
|
||||
if (len > 255)
|
||||
error("variable too long, %u", len);
|
||||
stream.writeByte(len);
|
||||
stream.write(_value.c_str(), len);
|
||||
}
|
||||
|
||||
} // namespace AGDS
|
||||
87
engines/agds/systemVariable.h
Normal file
87
engines/agds/systemVariable.h
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM_VARIABLE_H
|
||||
#define SYSTEM_VARIABLE_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Common {
|
||||
class ReadStream;
|
||||
class WriteStream;
|
||||
} // namespace Common
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
class SystemVariable {
|
||||
public:
|
||||
virtual ~SystemVariable() {}
|
||||
|
||||
virtual const Common::String &getString() const = 0;
|
||||
virtual int getInteger() const = 0;
|
||||
virtual void setString(const Common::String &value) = 0;
|
||||
virtual void setInteger(int value) = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual void read(Common::ReadStream &stream) = 0;
|
||||
virtual void write(Common::WriteStream &stream) const = 0;
|
||||
};
|
||||
|
||||
class IntegerSystemVariable : public SystemVariable {
|
||||
int _value;
|
||||
int _defaultValue;
|
||||
|
||||
public:
|
||||
IntegerSystemVariable(int defaultValue = 0) : _value(defaultValue), _defaultValue(defaultValue) {
|
||||
}
|
||||
|
||||
virtual const Common::String &getString() const;
|
||||
virtual int getInteger() const;
|
||||
virtual void setString(const Common::String &value);
|
||||
virtual void setInteger(int value);
|
||||
virtual void reset() {
|
||||
_value = _defaultValue;
|
||||
}
|
||||
virtual void read(Common::ReadStream &stream);
|
||||
virtual void write(Common::WriteStream &stream) const;
|
||||
};
|
||||
|
||||
class StringSystemVariable : public SystemVariable {
|
||||
Common::String _value;
|
||||
Common::String _defaultValue;
|
||||
|
||||
public:
|
||||
StringSystemVariable(const Common::String &defaultValue = Common::String()) : _value(defaultValue), _defaultValue(defaultValue) {
|
||||
}
|
||||
virtual const Common::String &getString() const;
|
||||
virtual int getInteger() const;
|
||||
virtual void setString(const Common::String &value);
|
||||
virtual void setInteger(int value);
|
||||
virtual void reset() {
|
||||
_value = _defaultValue;
|
||||
}
|
||||
virtual void read(Common::ReadStream &stream);
|
||||
virtual void write(Common::WriteStream &stream) const;
|
||||
};
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif /* AGDS_SYSTEM_VARIABLE_H */
|
||||
141
engines/agds/textLayout.cpp
Normal file
141
engines/agds/textLayout.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
/* 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 "agds/textLayout.h"
|
||||
#include "agds/agds.h"
|
||||
#include "agds/character.h"
|
||||
#include "agds/font.h"
|
||||
#include "agds/object.h"
|
||||
#include "agds/process.h"
|
||||
#include "agds/systemVariable.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
void TextLayout::paint(AGDSEngine &engine, Graphics::Surface &backbuffer) {
|
||||
if (!_valid)
|
||||
return;
|
||||
auto *font = engine.getFont(_fontId);
|
||||
for (uint i = 0; i < _lines.size(); ++i) {
|
||||
Line &line = _lines[i];
|
||||
font->drawString(&backbuffer, line.text, line.pos.x, line.pos.y, line.size.x, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void TextLayout::reset(AGDSEngine &engine) {
|
||||
bool valid = _valid;
|
||||
_valid = false;
|
||||
_lines.clear();
|
||||
|
||||
if (valid) {
|
||||
Common::String &var = _npc ? _npcNotifyVar : _charNotifyVar;
|
||||
if (!var.empty()) {
|
||||
engine.setGlobal(var, 0);
|
||||
}
|
||||
engine.reactivate(_process, "text layout reset");
|
||||
if (engine.getSystemVariable("tell_close_inv")->getInteger()) {
|
||||
engine.inventory().enable(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextLayout::layout(AGDSEngine &engine, Process &process, const Common::String &text, Common::Point pos, int fontId, bool npc) {
|
||||
if (text.empty()) {
|
||||
_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_process = process.getName();
|
||||
process.deactivate();
|
||||
_fontId = fontId;
|
||||
_npc = npc;
|
||||
auto *font = engine.getFont(fontId);
|
||||
|
||||
_lines.clear();
|
||||
int w = 0;
|
||||
|
||||
Common::Point basePos;
|
||||
size_t begin = 0;
|
||||
while (begin < text.size()) {
|
||||
while (begin < text.size() && (text[begin] == '\r' || text[begin] == ' '))
|
||||
++begin;
|
||||
size_t end = text.find('\n', begin);
|
||||
if (end == text.npos)
|
||||
end = text.size();
|
||||
|
||||
Common::String line = text.substr(begin, end - begin);
|
||||
debug("parsed line: %s", line.c_str());
|
||||
begin = end + 1;
|
||||
Common::Point size;
|
||||
size.x = font->getStringWidth(line);
|
||||
size.y = font->getFontHeight();
|
||||
_lines.push_back(Line());
|
||||
|
||||
Line &l = _lines.back();
|
||||
l.pos = basePos;
|
||||
l.text = line;
|
||||
l.size = size;
|
||||
|
||||
basePos.y += size.y;
|
||||
if (size.x > w)
|
||||
w = size.x;
|
||||
}
|
||||
|
||||
int dy = -basePos.y / 2;
|
||||
for (uint i = 0; i < _lines.size(); ++i) {
|
||||
Line &line = _lines[i];
|
||||
line.pos.x += pos.x - line.size.x / 2;
|
||||
line.pos.y += pos.y + dy;
|
||||
}
|
||||
|
||||
_valid = true;
|
||||
|
||||
Common::String &var = _npc ? _npcNotifyVar : _charNotifyVar;
|
||||
if (!var.empty()) {
|
||||
if (!engine.getGlobal(var))
|
||||
engine.setGlobal(var, 1);
|
||||
}
|
||||
if (!_npc) {
|
||||
auto character = engine.currentCharacter();
|
||||
if (character) {
|
||||
if (!_charDirectionNotifyVar.empty()) {
|
||||
if (!engine.getGlobal(_charDirectionNotifyVar))
|
||||
engine.setGlobal(_charDirectionNotifyVar, character->direction());
|
||||
} else {
|
||||
switch (character->direction()) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
break;
|
||||
default:
|
||||
character->animate(Common::Point(), character->direction(), 100);
|
||||
}
|
||||
}
|
||||
} else
|
||||
warning("no current character, skipping direction notification");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AGDS
|
||||
85
engines/agds/textLayout.h
Normal file
85
engines/agds/textLayout.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/* 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 TEXT_LAYOUT_H
|
||||
#define TEXT_LAYOUT_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace AGDS {
|
||||
|
||||
class AGDSEngine;
|
||||
class Process;
|
||||
|
||||
class TextLayout {
|
||||
int _fontId;
|
||||
Common::Point _pos;
|
||||
bool _npc;
|
||||
bool _valid;
|
||||
|
||||
struct Line {
|
||||
Common::Point pos;
|
||||
Common::String text;
|
||||
Common::Point size;
|
||||
};
|
||||
|
||||
Common::Array<Line> _lines;
|
||||
|
||||
Common::String _process;
|
||||
Common::String _charNotifyVar;
|
||||
Common::String _charDirectionNotifyVar;
|
||||
Common::String _npcNotifyVar;
|
||||
|
||||
public:
|
||||
TextLayout() : _fontId(-1), _npc(true), _valid(false) {}
|
||||
|
||||
bool valid() const {
|
||||
return _valid;
|
||||
}
|
||||
|
||||
void reset(AGDSEngine &engine);
|
||||
|
||||
void setCharNotifyVar(const Common::String &name) {
|
||||
_charNotifyVar = name;
|
||||
}
|
||||
|
||||
void setCharDirectionNotifyVar(const Common::String &name) {
|
||||
_charDirectionNotifyVar = name;
|
||||
}
|
||||
|
||||
void setNPCNotifyVar(const Common::String &name) {
|
||||
_npcNotifyVar = name;
|
||||
}
|
||||
|
||||
void paint(AGDSEngine &engine, Graphics::Surface &backbuffer);
|
||||
void layout(AGDSEngine &engine, Process &process, const Common::String &text, Common::Point pos, int fontId, bool npc);
|
||||
};
|
||||
|
||||
} // End of namespace AGDS
|
||||
|
||||
#endif /* AGDS_TEXT_LAYOUT_H */
|
||||
Reference in New Issue
Block a user