Initial commit
This commit is contained in:
4
engines/zvision/POTFILES
Normal file
4
engines/zvision/POTFILES
Normal file
@@ -0,0 +1,4 @@
|
||||
engines/zvision/detection_tables.h
|
||||
engines/zvision/file/save_manager.cpp
|
||||
engines/zvision/metaengine.cpp
|
||||
engines/zvision/zvision.cpp
|
||||
92
engines/zvision/common/focus_list.h
Normal file
92
engines/zvision/common/focus_list.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/* 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 ZVISION_FOCUS_LIST_H
|
||||
#define ZVISION_FOCUS_LIST_H
|
||||
|
||||
#include "common/array.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
/**
|
||||
* FILO array of unique members
|
||||
*
|
||||
* Tracks redraw order of layered graphical elements.
|
||||
* When an element has current focus, it is reshuffled to the top of the pile.
|
||||
* When redrawing, start with last (bottom) element of list and finish with first (top)
|
||||
* Used to:
|
||||
* - ensure scrolling menus are drawn in the order in which they last had mouse focus.
|
||||
* - ensure most recently updated subtitle is drawn atop all others.
|
||||
*/
|
||||
|
||||
template<class T>
|
||||
class FocusList : public Common::Array<T> {
|
||||
private:
|
||||
typedef uint size_type;
|
||||
public:
|
||||
/**
|
||||
* Move unique entry to front of list; add to list if not already present.
|
||||
* Sequence of all remaining members remains unchanged.
|
||||
*/
|
||||
void set(const T currentFocus) {
|
||||
if (!this->size())
|
||||
this->push_back(currentFocus);
|
||||
else {
|
||||
if (this->front() != currentFocus) {
|
||||
Common::Array<T> buffer;
|
||||
while (this->size() > 0) {
|
||||
if (this->back() != currentFocus)
|
||||
buffer.push_back(this->back());
|
||||
this->pop_back();
|
||||
}
|
||||
this->push_back(currentFocus);
|
||||
while (buffer.size() > 0) {
|
||||
this->push_back(buffer.back());
|
||||
buffer.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unique entry, if present.
|
||||
* Sequence of all remaining members remains unchanged.
|
||||
*/
|
||||
void remove(const T value) {
|
||||
if (this->size()) {
|
||||
Common::Array<T> buffer;
|
||||
while (this->size() > 0) {
|
||||
if (this->back() != value)
|
||||
buffer.push_back(this->back());
|
||||
this->pop_back();
|
||||
}
|
||||
while (buffer.size() > 0) {
|
||||
this->push_back(buffer.back());
|
||||
buffer.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
122
engines/zvision/common/scroller.cpp
Normal file
122
engines/zvision/common/scroller.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "zvision/common/scroller.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
LinearScroller::LinearScroller(const int16 activePos, const int16 idlePos, const int16 period) :
|
||||
_pos(idlePos),
|
||||
_prevPos(idlePos),
|
||||
_activePos(activePos),
|
||||
_idlePos(idlePos),
|
||||
_deltaPos((int16)(activePos - idlePos)),
|
||||
_period(period) {
|
||||
}
|
||||
|
||||
LinearScroller::~LinearScroller() {
|
||||
}
|
||||
|
||||
bool LinearScroller::update(uint32 deltatime) {
|
||||
_prevPos = _pos;
|
||||
if (_period != 0) {
|
||||
int16 targetPos;
|
||||
float dPos = 0;
|
||||
if (_active)
|
||||
targetPos = _activePos;
|
||||
else
|
||||
targetPos = _idlePos;
|
||||
if (_pos != targetPos) {
|
||||
dPos = (float)((int32)deltatime * (int32)_deltaPos) / _period;
|
||||
if ((int16)dPos == 0) {
|
||||
if (_deltaPos > 0)
|
||||
dPos = 1;
|
||||
else
|
||||
dPos = -1;
|
||||
}
|
||||
}
|
||||
if (!_active)
|
||||
dPos = -dPos;
|
||||
_pos += (int16)dPos;
|
||||
if ((dPos == 0) || ((dPos > 0) && (_pos > targetPos)) || ((dPos < 0) && (_pos < targetPos)))
|
||||
_pos = targetPos;
|
||||
_moving = (_pos != targetPos);
|
||||
} else {
|
||||
if (_active)
|
||||
_pos = _activePos;
|
||||
else
|
||||
_pos = _idlePos;
|
||||
_moving = false;
|
||||
}
|
||||
return (_pos != _prevPos); // True if redraw necessary
|
||||
}
|
||||
|
||||
void LinearScroller::reset() {
|
||||
setActive(false);
|
||||
_pos = _idlePos;
|
||||
}
|
||||
|
||||
void LinearScroller::setActive(bool active) {
|
||||
_active = active;
|
||||
}
|
||||
|
||||
bool LinearScroller::isMoving() {
|
||||
return _moving;
|
||||
}
|
||||
|
||||
|
||||
Scroller::Scroller(const Common::Point &activePos, const Common::Point &idlePos, int16 period) :
|
||||
_xScroller(activePos.x, idlePos.x, period),
|
||||
_yScroller(activePos.y, idlePos.y, period) {
|
||||
_pos.x = _xScroller._pos;
|
||||
_pos.y = _yScroller._pos;
|
||||
}
|
||||
|
||||
Scroller::~Scroller() {
|
||||
}
|
||||
|
||||
void Scroller::reset() {
|
||||
_xScroller.reset();
|
||||
_yScroller.reset();
|
||||
}
|
||||
|
||||
|
||||
void Scroller::setActive(bool active) {
|
||||
_xScroller.setActive(active);
|
||||
_yScroller.setActive(active);
|
||||
}
|
||||
|
||||
bool Scroller::isMoving() {
|
||||
return _xScroller.isMoving() || _yScroller.isMoving();
|
||||
}
|
||||
|
||||
bool Scroller::update(uint32 deltatime) {
|
||||
bool redraw = false;
|
||||
if (_xScroller.update(deltatime))
|
||||
redraw = true;
|
||||
if (_yScroller.update(deltatime))
|
||||
redraw = true;
|
||||
_pos.x = _xScroller._pos;
|
||||
_pos.y = _yScroller._pos;
|
||||
return (redraw);
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
84
engines/zvision/common/scroller.h
Normal file
84
engines/zvision/common/scroller.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 ZVISION_SCROLLER_H
|
||||
#define ZVISION_SCROLLER_H
|
||||
|
||||
#include "common/rect.h"
|
||||
#include "zvision/zvision.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
/**
|
||||
* Automatically scroll a GUI menu or similar graphical element between an active and an idle position
|
||||
* Movement in one dimension; idle & active positions specified as int16.
|
||||
* Movement is at constant speed determined by period, specified in ms.
|
||||
* If active/idle status is changed mid-transition, will scroll from current position to the appropriate position.
|
||||
* LinearScroller also be used to reversibly scroll animation frames.
|
||||
*/
|
||||
|
||||
class LinearScroller {
|
||||
public:
|
||||
LinearScroller(const int16 activePos, const int16 idlePos, const int16 period = 500);
|
||||
~LinearScroller();
|
||||
void reset(); ///< Set idle and immediately jump to idle position
|
||||
bool update(uint32 deltatime); ///< Calculate updated position of scrolled graphics; return true if redraw is necessary.
|
||||
void setActive(bool active); ///< Set active or idle & scroll at set speed from current position to that position.
|
||||
bool isMoving();
|
||||
int16 getPos();
|
||||
|
||||
int16 _pos;
|
||||
int16 _prevPos;
|
||||
|
||||
private:
|
||||
bool _active = false;
|
||||
bool _moving = false;
|
||||
const int16 _activePos;
|
||||
const int16 _idlePos;
|
||||
const int16 _deltaPos;
|
||||
const int16 _period;
|
||||
};
|
||||
|
||||
/**
|
||||
* Automatically scroll a GUI menu or similar graphical element between an active and an idle position
|
||||
* Movement in two dimensions; idle & active positions specified as Common::Point
|
||||
* Movement is at constant speed determined by period, specified in ms.
|
||||
* If active/idle status is changed mid-transition, will scroll from current position to the appropriate position.
|
||||
*/
|
||||
|
||||
class Scroller {
|
||||
public:
|
||||
Scroller(const Common::Point &activePos, const Common::Point &idlePos, const int16 period = 500);
|
||||
~Scroller();
|
||||
void reset(); ///< Set idle and immediately jump to idle position
|
||||
bool update(uint32 deltatime); ///< Calculate updated position of scrolled graphics; return true if redraw is necessary.
|
||||
void setActive(bool active); ///< Set active or idle & scroll at set speed from current position to that position.
|
||||
bool isMoving();
|
||||
|
||||
Common::Point _pos;
|
||||
|
||||
private:
|
||||
LinearScroller _xScroller, _yScroller;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
3
engines/zvision/configure.engine
Normal file
3
engines/zvision/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 zvision "Z-Vision" yes "" "" "freetype2 16bit highres" "midi truemotion1 mpeg2"
|
||||
65
engines/zvision/core/clock.cpp
Normal file
65
engines/zvision/core/clock.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/system.h"
|
||||
#include "zvision/core/clock.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
Clock::Clock(OSystem *system)
|
||||
: _system(system),
|
||||
_lastTime(system->getMillis()),
|
||||
_deltaTime(0),
|
||||
_pausedTime(0),
|
||||
_paused(false) {
|
||||
}
|
||||
|
||||
void Clock::update() {
|
||||
uint32 currentTime = _system->getMillis();
|
||||
|
||||
_deltaTime = (currentTime - _lastTime);
|
||||
if (_paused) {
|
||||
_deltaTime -= (currentTime - _pausedTime);
|
||||
}
|
||||
|
||||
if (_deltaTime < 0) {
|
||||
_deltaTime = 0;
|
||||
}
|
||||
|
||||
_lastTime = currentTime;
|
||||
}
|
||||
|
||||
void Clock::start() {
|
||||
if (_paused) {
|
||||
_lastTime = _system->getMillis();
|
||||
_paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Clock::stop() {
|
||||
if (!_paused) {
|
||||
_pausedTime = _system->getMillis();
|
||||
_paused = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
83
engines/zvision/core/clock.h
Normal file
83
engines/zvision/core/clock.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_CLOCK_H
|
||||
#define ZVISION_CLOCK_H
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
class OSystem;
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
/* Class for handling frame to frame deltaTime while keeping track of time pauses/un-pauses */
|
||||
class Clock {
|
||||
public:
|
||||
Clock(OSystem *system);
|
||||
|
||||
private:
|
||||
OSystem *_system;
|
||||
uint32 _lastTime;
|
||||
int32 _deltaTime;
|
||||
uint32 _pausedTime;
|
||||
bool _paused;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Updates _deltaTime with the difference between the current time and
|
||||
* when the last update() was called.
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* Get the delta time since the last frame. (The time between update() calls)
|
||||
*
|
||||
* @return Delta time since the last frame (in milliseconds)
|
||||
*/
|
||||
uint32 getDeltaTime() const {
|
||||
return _deltaTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time from the program starting to the last update() call
|
||||
*
|
||||
* @return Time from program start to last update() call (in milliseconds)
|
||||
*/
|
||||
uint32 getLastMeasuredTime() {
|
||||
return _lastTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Un-pause the clock.
|
||||
* Has no effect if the clock is already un-paused.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* Pause the clock. Any future delta times will take this pause into account.
|
||||
* Has no effect if the clock is already paused.
|
||||
*/
|
||||
void stop();
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
370
engines/zvision/core/console.cpp
Normal file
370
engines/zvision/core/console.cpp
Normal file
@@ -0,0 +1,370 @@
|
||||
/* 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 "audio/mixer.h"
|
||||
#include "common/bufferedstream.h"
|
||||
#include "common/file.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/system.h"
|
||||
#include "gui/debugger.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/core/console.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/graphics/cursors/cursor.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/sound/zork_raw.h"
|
||||
#include "zvision/text/string_manager.h"
|
||||
#include "zvision/video/zork_avi_decoder.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
Console::Console(ZVision *engine) : GUI::Debugger(), _engine(engine) {
|
||||
registerCmd("loadvideo", WRAP_METHOD(Console, cmdLoadVideo));
|
||||
registerCmd("loadsound", WRAP_METHOD(Console, cmdLoadSound));
|
||||
registerCmd("raw2wav", WRAP_METHOD(Console, cmdRawToWav));
|
||||
registerCmd("setrenderstate", WRAP_METHOD(Console, cmdSetRenderState));
|
||||
registerCmd("generaterendertable", WRAP_METHOD(Console, cmdGenerateRenderTable));
|
||||
registerCmd("setpanoramafov", WRAP_METHOD(Console, cmdSetPanoramaFoV));
|
||||
registerCmd("setpanoramascale", WRAP_METHOD(Console, cmdSetPanoramaScale));
|
||||
registerCmd("location", WRAP_METHOD(Console, cmdLocation));
|
||||
registerCmd("dumpfile", WRAP_METHOD(Console, cmdDumpFile));
|
||||
registerCmd("dumpfiles", WRAP_METHOD(Console, cmdDumpFiles));
|
||||
registerCmd("dumpimage", WRAP_METHOD(Console, cmdDumpImage));
|
||||
registerCmd("statevalue", WRAP_METHOD(Console, cmdStateValue));
|
||||
registerCmd("stateflag", WRAP_METHOD(Console, cmdStateFlag));
|
||||
}
|
||||
|
||||
bool Console::cmdLoadVideo(int argc, const char **argv) {
|
||||
if (argc != 2) {
|
||||
debugPrintf("Use %s <fileName> to load a video to the screen\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
ZorkAVIDecoder videoDecoder;
|
||||
if (videoDecoder.loadFile(argv[1])) {
|
||||
_engine->playVideo(videoDecoder);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::cmdLoadSound(int argc, const char **argv) {
|
||||
if (!Common::File::exists(argv[1])) {
|
||||
debugPrintf("File does not exist\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argc == 2) {
|
||||
Audio::AudioStream *soundStream = makeRawZorkStream(argv[1], _engine);
|
||||
Audio::SoundHandle handle;
|
||||
_engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, soundStream, -1, 100, 0, DisposeAfterUse::YES, false, false);
|
||||
} else if (argc == 4) {
|
||||
int isStereo = atoi(argv[3]);
|
||||
|
||||
Common::File *file = new Common::File();
|
||||
if (!file->open(argv[1])) {
|
||||
warning("File not found: %s", argv[1]);
|
||||
delete file;
|
||||
return true;
|
||||
}
|
||||
|
||||
Audio::AudioStream *soundStream = makeRawZorkStream(file, atoi(argv[2]), isStereo == 0 ? false : true);
|
||||
Audio::SoundHandle handle;
|
||||
_engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, soundStream, -1, 100, 0, DisposeAfterUse::YES, false, false);
|
||||
} else {
|
||||
debugPrintf("Use %s <fileName> [<rate> <isStereo: 1 or 0>] to load a sound\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::cmdRawToWav(int argc, const char **argv) {
|
||||
if (argc != 3) {
|
||||
debugPrintf("Use %s <rawFilePath> <wavFileName> to dump a .RAW file to .WAV\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::File file;
|
||||
if (!file.open(argv[1])) {
|
||||
warning("File not found: %s", argv[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
Audio::AudioStream *audioStream = makeRawZorkStream(argv[1], _engine);
|
||||
|
||||
Common::DumpFile output;
|
||||
output.open(argv[2]);
|
||||
|
||||
output.writeUint32BE(MKTAG('R', 'I', 'F', 'F'));
|
||||
output.writeUint32LE(file.size() * 2 + 36);
|
||||
output.writeUint32BE(MKTAG('W', 'A', 'V', 'E'));
|
||||
output.writeUint32BE(MKTAG('f', 'm', 't', ' '));
|
||||
output.writeUint32LE(16);
|
||||
output.writeUint16LE(1);
|
||||
uint16 numChannels;
|
||||
if (audioStream->isStereo()) {
|
||||
numChannels = 2;
|
||||
output.writeUint16LE(2);
|
||||
} else {
|
||||
numChannels = 1;
|
||||
output.writeUint16LE(1);
|
||||
}
|
||||
output.writeUint32LE(audioStream->getRate());
|
||||
output.writeUint32LE(audioStream->getRate() * numChannels * 2);
|
||||
output.writeUint16LE(numChannels * 2);
|
||||
output.writeUint16LE(16);
|
||||
output.writeUint32BE(MKTAG('d', 'a', 't', 'a'));
|
||||
output.writeUint32LE(file.size() * 2);
|
||||
int16 *buffer = new int16[file.size()];
|
||||
audioStream->readBuffer(buffer, file.size());
|
||||
#ifndef SCUMM_LITTLE_ENDIAN
|
||||
for (int i = 0; i < file.size(); ++i)
|
||||
buffer[i] = TO_LE_16(buffer[i]);
|
||||
#endif
|
||||
output.write(buffer, file.size() * 2);
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::cmdSetRenderState(int argc, const char **argv) {
|
||||
if (argc != 2) {
|
||||
debugPrintf("Use %s <RenderState: panorama, tilt, flat> to change the current render state\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::String renderState(argv[1]);
|
||||
|
||||
if (renderState.matchString("panorama", true))
|
||||
_engine->getRenderManager()->getRenderTable()->setRenderState(RenderTable::PANORAMA);
|
||||
else if (renderState.matchString("tilt", true))
|
||||
_engine->getRenderManager()->getRenderTable()->setRenderState(RenderTable::TILT);
|
||||
else if (renderState.matchString("flat", true))
|
||||
_engine->getRenderManager()->getRenderTable()->setRenderState(RenderTable::FLAT);
|
||||
else
|
||||
debugPrintf("Use %s <RenderState: panorama, tilt, flat> to change the current render state\n", argv[0]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::cmdGenerateRenderTable(int argc, const char **argv) {
|
||||
_engine->getRenderManager()->getRenderTable()->generateRenderTable();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::cmdSetPanoramaFoV(int argc, const char **argv) {
|
||||
if (argc != 2) {
|
||||
debugPrintf("Use %s <fieldOfView> to change the current panorama field of view\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
_engine->getRenderManager()->getRenderTable()->setPanoramaFoV(atof(argv[1]));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::cmdSetPanoramaScale(int argc, const char **argv) {
|
||||
if (argc != 2) {
|
||||
debugPrintf("Use %s <scale> to change the current panorama scale\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
_engine->getRenderManager()->getRenderTable()->setPanoramaScale(atof(argv[1]));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::cmdLocation(int argc, const char **argv) {
|
||||
Location curLocation = _engine->getScriptManager()->getCurrentLocation();
|
||||
Common::String scrFile = Common::String::format("%c%c%c%c.scr", curLocation.world, curLocation.room, curLocation.node, curLocation.view);
|
||||
debugPrintf("Current location: world '%c', room '%c', node '%c', view '%c', offset %d, script %s\n",
|
||||
curLocation.world, curLocation.room, curLocation.node, curLocation.view, curLocation.offset, scrFile.c_str());
|
||||
|
||||
if (argc != 6) {
|
||||
debugPrintf("Use %s <char: world> <char: room> <char:node> <char:view> <int: x offset> to change your location\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
_engine->getScriptManager()->changeLocation(*(argv[1]), *(argv[2]), *(argv[3]), *(argv[4]), atoi(argv[5]));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dumpFile(Common::SeekableReadStream *s, const Common::Path &outName) {
|
||||
byte *buffer = new byte[s->size()];
|
||||
s->read(buffer, s->size());
|
||||
|
||||
Common::DumpFile dumpFile;
|
||||
dumpFile.open(outName);
|
||||
|
||||
dumpFile.write(buffer, s->size());
|
||||
dumpFile.flush();
|
||||
dumpFile.close();
|
||||
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
bool Console::cmdDumpFile(int argc, const char **argv) {
|
||||
if (argc != 2) {
|
||||
debugPrintf("Use %s <fileName> to dump a file\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::File f;
|
||||
if (!f.open(argv[1])) {
|
||||
warning("File not found: %s", argv[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
dumpFile(&f, argv[1]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::cmdDumpFiles(int argc, const char **argv) {
|
||||
Common::Path fileName;
|
||||
Common::SeekableReadStream *in;
|
||||
|
||||
if (argc != 2) {
|
||||
debugPrintf("Use %s <file extension> to dump all files with a specific extension\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::ArchiveMemberList fileList;
|
||||
Common::Path pattern;
|
||||
pattern = Common::Path(Common::String::format("*.%s", argv[1]));
|
||||
SearchMan.listMatchingMembers(fileList, pattern);
|
||||
|
||||
for (auto &file : fileList) {
|
||||
fileName = file.get()->getFileName();
|
||||
debugPrintf("Dumping %s\n", fileName.toString().c_str());
|
||||
|
||||
in = file.get()->createReadStream();
|
||||
if (in)
|
||||
dumpFile(in, fileName);
|
||||
else
|
||||
debugPrintf("Failed to dump!");
|
||||
delete in;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::cmdDumpImage(int argc, const char **argv) {
|
||||
if (argc != 2) {
|
||||
debugPrintf("Use %s <TGA/TGZ name> to dump a Z-Vision TGA/TGZ image into a regular BMP image\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::Path fileName = Common::Path(argv[1], Common::Path::kNativeSeparator);
|
||||
Common::String baseName(fileName.baseName());
|
||||
if (!baseName.hasSuffix(".tga")) {
|
||||
debugPrintf("%s is not an image file", argv[1]);
|
||||
}
|
||||
|
||||
Common::File f;
|
||||
if (!f.open(fileName)) {
|
||||
warning("File not found: %s", argv[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
Graphics::Surface surface;
|
||||
_engine->getRenderManager()->readImageToSurface(fileName, surface, false);
|
||||
|
||||
// Open file
|
||||
Common::DumpFile out;
|
||||
|
||||
baseName.setChar('b', baseName.size() - 3);
|
||||
baseName.setChar('m', baseName.size() - 2);
|
||||
baseName.setChar('p', baseName.size() - 1);
|
||||
|
||||
out.open(fileName.getParent().appendComponent(baseName));
|
||||
|
||||
// Write BMP header
|
||||
out.writeByte('B');
|
||||
out.writeByte('M');
|
||||
out.writeUint32LE(surface.h * surface.pitch + 54);
|
||||
out.writeUint32LE(0);
|
||||
out.writeUint32LE(54);
|
||||
out.writeUint32LE(40);
|
||||
out.writeUint32LE(surface.w);
|
||||
out.writeUint32LE(surface.h);
|
||||
out.writeUint16LE(1);
|
||||
out.writeUint16LE(16);
|
||||
out.writeUint32LE(0);
|
||||
out.writeUint32LE(0);
|
||||
out.writeUint32LE(0);
|
||||
out.writeUint32LE(0);
|
||||
out.writeUint32LE(0);
|
||||
out.writeUint32LE(0);
|
||||
|
||||
// Write pixel data to BMP
|
||||
out.write(surface.getPixels(), surface.pitch * surface.h);
|
||||
|
||||
out.flush();
|
||||
out.close();
|
||||
|
||||
surface.free();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::cmdStateValue(int argc, const char **argv) {
|
||||
if (argc < 2) {
|
||||
debugPrintf("Use %s <valuenum> to show the value of a state variable\n", argv[0]);
|
||||
debugPrintf("Use %s <valuenum> <newvalue> to set the value of a state variable\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
int valueNum = atoi(argv[1]);
|
||||
int newValue = (argc > 2) ? atoi(argv[2]) : -1;
|
||||
|
||||
if (argc == 2)
|
||||
debugPrintf("[%d] = %d\n", valueNum, _engine->getScriptManager()->getStateValue(valueNum));
|
||||
else if (argc == 3)
|
||||
_engine->getScriptManager()->setStateValue(valueNum, newValue);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::cmdStateFlag(int argc, const char **argv) {
|
||||
if (argc < 2) {
|
||||
debugPrintf("Use %s <flagnum> to show the value of a state flag\n", argv[0]);
|
||||
debugPrintf("Use %s <flagnum> <newvalue> to set the value of a state flag\n", argv[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
int valueNum = atoi(argv[1]);
|
||||
int newValue = (argc > 2) ? atoi(argv[2]) : -1;
|
||||
|
||||
if (argc == 2)
|
||||
debugPrintf("[%d] = %d\n", valueNum, _engine->getScriptManager()->getStateFlag(valueNum));
|
||||
else if (argc == 3)
|
||||
_engine->getScriptManager()->setStateFlag(valueNum, newValue);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
55
engines/zvision/core/console.h
Normal file
55
engines/zvision/core/console.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 ZVISION_CONSOLE_H
|
||||
#define ZVISION_CONSOLE_H
|
||||
|
||||
#include "gui/debugger.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
class Console : public GUI::Debugger {
|
||||
public:
|
||||
Console(ZVision *engine);
|
||||
~Console() override {}
|
||||
|
||||
private:
|
||||
ZVision *_engine;
|
||||
|
||||
bool cmdLoadVideo(int argc, const char **argv);
|
||||
bool cmdLoadSound(int argc, const char **argv);
|
||||
bool cmdRawToWav(int argc, const char **argv);
|
||||
bool cmdSetRenderState(int argc, const char **argv);
|
||||
bool cmdGenerateRenderTable(int argc, const char **argv);
|
||||
bool cmdSetPanoramaFoV(int argc, const char **argv);
|
||||
bool cmdSetPanoramaScale(int argc, const char **argv);
|
||||
bool cmdLocation(int argc, const char **argv);
|
||||
bool cmdDumpFile(int argc, const char **argv);
|
||||
bool cmdDumpFiles(int argc, const char **argv);
|
||||
bool cmdDumpImage(int argc, const char **argv);
|
||||
bool cmdStateValue(int argc, const char **argv);
|
||||
bool cmdStateFlag(int argc, const char **argv);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
#endif
|
||||
493
engines/zvision/core/events.cpp
Normal file
493
engines/zvision/core/events.cpp
Normal file
@@ -0,0 +1,493 @@
|
||||
/* 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 "audio/mixer.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/events.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/rational.h"
|
||||
#include "engines/util.h"
|
||||
#include "zvision/detection.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/core/console.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/menu.h"
|
||||
#include "zvision/sound/zork_raw.h"
|
||||
#include "zvision/text/string_manager.h"
|
||||
#include "zvision/text/subtitle_manager.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
void ZVision::pushKeyToCheatBuf(uint8 key) {
|
||||
for (int i = 0; i < KEYBUF_SIZE - 1; i++)
|
||||
_cheatBuffer[i] = _cheatBuffer[i + 1];
|
||||
_cheatBuffer[KEYBUF_SIZE - 1] = key;
|
||||
}
|
||||
|
||||
bool ZVision::checkCode(const char *code) {
|
||||
int codeLen = strlen(code);
|
||||
if (codeLen > KEYBUF_SIZE)
|
||||
return false;
|
||||
for (int i = 0; i < codeLen; i++)
|
||||
if (code[i] != _cheatBuffer[KEYBUF_SIZE - codeLen + i] && code[i] != '?')
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8 ZVision::getBufferedKey(uint8 pos) {
|
||||
if (pos >= KEYBUF_SIZE)
|
||||
return 0;
|
||||
else
|
||||
return _cheatBuffer[KEYBUF_SIZE - pos - 1];
|
||||
}
|
||||
|
||||
void ZVision::cheatCodes(uint8 key) {
|
||||
Location loc = _scriptManager->getCurrentLocation();
|
||||
// Do not process cheat codes while in the game menus
|
||||
if (loc.world == 'g' && loc.room == 'j')
|
||||
return;
|
||||
|
||||
pushKeyToCheatBuf(key);
|
||||
|
||||
if (getGameId() == GID_GRANDINQUISITOR) {
|
||||
if (checkCode("IMNOTDEAF")) {
|
||||
// Unknown cheat
|
||||
_subtitleManager->showDebugMsg(Common::String::format("IMNOTDEAF cheat or debug, not implemented"));
|
||||
}
|
||||
|
||||
if (checkCode("3100OPB")) {
|
||||
_subtitleManager->showDebugMsg(Common::String::format("Current location: %c%c%c%c",
|
||||
_scriptManager->getStateValue(StateKey_World),
|
||||
_scriptManager->getStateValue(StateKey_Room),
|
||||
_scriptManager->getStateValue(StateKey_Node),
|
||||
_scriptManager->getStateValue(StateKey_View)));
|
||||
}
|
||||
|
||||
if (checkCode("KILLMENOW")) {
|
||||
_scriptManager->changeLocation('g', 'j', 'd', 'e', 0);
|
||||
_scriptManager->setStateValue(2201, 35);
|
||||
}
|
||||
|
||||
if (checkCode("MIKESPANTS")) {
|
||||
_scriptManager->changeLocation('g', 'j', 't', 'm', 0);
|
||||
}
|
||||
|
||||
// There are 3 more cheats in script files:
|
||||
// - "WHOAMI": gjcr.scr
|
||||
// - "HUISOK": hp1e.scr
|
||||
// - "EAT ME": uh1f.scr
|
||||
} else if (getGameId() == GID_NEMESIS) {
|
||||
if (checkCode("CHLOE")) {
|
||||
_scriptManager->changeLocation('t', 'm', '2', 'g', 0);
|
||||
_scriptManager->setStateValue(224, 1);
|
||||
}
|
||||
|
||||
if (checkCode("77MASSAVE")) {
|
||||
_subtitleManager->showDebugMsg(Common::String::format("Current location: %c%c%c%c",
|
||||
_scriptManager->getStateValue(StateKey_World),
|
||||
_scriptManager->getStateValue(StateKey_Room),
|
||||
_scriptManager->getStateValue(StateKey_Node),
|
||||
_scriptManager->getStateValue(StateKey_View)));
|
||||
}
|
||||
|
||||
if (checkCode("IDKFA")) {
|
||||
_scriptManager->changeLocation('t', 'w', '3', 'f', 0);
|
||||
_scriptManager->setStateValue(249, 1);
|
||||
}
|
||||
|
||||
if (checkCode("309NEWDORMA")) {
|
||||
_scriptManager->changeLocation('g', 'j', 'g', 'j', 0);
|
||||
}
|
||||
|
||||
if (checkCode("HELLOSAILOR")) {
|
||||
Audio::AudioStream *soundStream;
|
||||
if (loc == "vb10") {
|
||||
soundStream = makeRawZorkStream("v000hpta.raw", this);
|
||||
} else {
|
||||
soundStream = makeRawZorkStream("v000hnta.raw", this);
|
||||
}
|
||||
Audio::SoundHandle handle;
|
||||
_mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, soundStream);
|
||||
}
|
||||
}
|
||||
|
||||
if (checkCode("FRAME")) {
|
||||
Common::String fpsStr = Common::String::format("FPS: %d", getFPS());
|
||||
_subtitleManager->showDebugMsg(fpsStr);
|
||||
}
|
||||
|
||||
if (checkCode("COMPUTERARCH"))
|
||||
_subtitleManager->showDebugMsg("COMPUTERARCH: var-viewer not implemented");
|
||||
|
||||
// This cheat essentially toggles the GOxxxx cheat below
|
||||
if (checkCode("XYZZY"))
|
||||
_scriptManager->setStateValue(StateKey_DebugCheats, 1 - _scriptManager->getStateValue(StateKey_DebugCheats));
|
||||
|
||||
if (_scriptManager->getStateValue(StateKey_DebugCheats) == 1)
|
||||
if (checkCode("GO????"))
|
||||
_scriptManager->changeLocation(getBufferedKey(3),
|
||||
getBufferedKey(2),
|
||||
getBufferedKey(1),
|
||||
getBufferedKey(0), 0);
|
||||
|
||||
// Show the Venus screen when "?" or "/" is pressed while inside the temple world
|
||||
if (_scriptManager->getStateValue(StateKey_VenusEnable) == 1)
|
||||
if (getBufferedKey(0) == 0xBF && _scriptManager->getStateValue(StateKey_World) == 't')
|
||||
_scriptManager->changeLocation('g', 'j', 'h', 'e', 0);
|
||||
}
|
||||
|
||||
void ZVision::processEvents() {
|
||||
while (_eventMan->pollEvent(_event)) {
|
||||
switch (_event.type) {
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
_cursorManager->cursorDown(true);
|
||||
_menu->onMouseDown(_event.mouse);
|
||||
if (!_menu->inMenu() || !_widescreen) {
|
||||
_scriptManager->setStateValue(StateKey_LMouse, 1);
|
||||
_scriptManager->addEvent(_event);
|
||||
}
|
||||
break;
|
||||
|
||||
case Common::EVENT_LBUTTONUP:
|
||||
_cursorManager->cursorDown(false);
|
||||
_menu->onMouseUp(_event.mouse);
|
||||
if (!_menu->inMenu() || !_widescreen) {
|
||||
_scriptManager->setStateValue(StateKey_LMouse, 0);
|
||||
_scriptManager->addEvent(_event);
|
||||
}
|
||||
break;
|
||||
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
_cursorManager->cursorDown(true);
|
||||
if (!_menu->inMenu() || !_widescreen) {
|
||||
_scriptManager->setStateValue(StateKey_RMouse, 1);
|
||||
if (getGameId() == GID_NEMESIS)
|
||||
_scriptManager->inventoryCycle();
|
||||
}
|
||||
break;
|
||||
|
||||
case Common::EVENT_RBUTTONUP:
|
||||
_cursorManager->cursorDown(false);
|
||||
if (!_menu->inMenu() || !_widescreen)
|
||||
_scriptManager->setStateValue(StateKey_RMouse, 0);
|
||||
break;
|
||||
|
||||
case Common::EVENT_MOUSEMOVE:
|
||||
onMouseMove(_event.mouse);
|
||||
break;
|
||||
|
||||
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
|
||||
switch ((ZVisionAction)_event.customType) {
|
||||
case kZVisionActionLeft:
|
||||
case kZVisionActionRight:
|
||||
if (_renderManager->getRenderTable()->getRenderState() == RenderTable::PANORAMA)
|
||||
_keyboardVelocity = (_event.customType == kZVisionActionLeft ?
|
||||
-_scriptManager->getStateValue(StateKey_KbdRotateSpeed) :
|
||||
_scriptManager->getStateValue(StateKey_KbdRotateSpeed)) * 2;
|
||||
break;
|
||||
|
||||
case kZVisionActionUp:
|
||||
case kZVisionActionDown:
|
||||
if (_renderManager->getRenderTable()->getRenderState() == RenderTable::TILT)
|
||||
_keyboardVelocity = (_event.customType == kZVisionActionUp ?
|
||||
-_scriptManager->getStateValue(StateKey_KbdRotateSpeed) :
|
||||
_scriptManager->getStateValue(StateKey_KbdRotateSpeed)) * 2;
|
||||
break;
|
||||
|
||||
case kZVisionActionSave:
|
||||
if (_menu->getEnable(kMainMenuSave))
|
||||
_scriptManager->changeLocation('g', 'j', 's', 'e', 0);
|
||||
break;
|
||||
|
||||
case kZVisionActionRestore:
|
||||
if (_menu->getEnable(kMainMenuLoad))
|
||||
_scriptManager->changeLocation('g', 'j', 'r', 'e', 0);
|
||||
break;
|
||||
|
||||
case kZVisionActionPreferences:
|
||||
if (_menu->getEnable(kMainMenuPrefs))
|
||||
_scriptManager->changeLocation('g', 'j', 'p', 'e', 0);
|
||||
break;
|
||||
|
||||
case kZVisionActionQuit:
|
||||
if (_menu->getEnable(kMainMenuExit)) {
|
||||
if (ConfMan.hasKey("confirm_exit") && ConfMan.getBool("confirm_exit"))
|
||||
quit(true);
|
||||
else
|
||||
quit(false);
|
||||
}
|
||||
break;
|
||||
|
||||
case kZVisionActionShowFPS: {
|
||||
Common::String fpsStr = Common::String::format("FPS: %d", getFPS());
|
||||
_subtitleManager->showDebugMsg(fpsStr);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case Common::EVENT_CUSTOM_ENGINE_ACTION_END:
|
||||
switch ((ZVisionAction)_event.customType) {
|
||||
case kZVisionActionLeft:
|
||||
case kZVisionActionRight:
|
||||
if (_renderManager->getRenderTable()->getRenderState() == RenderTable::PANORAMA)
|
||||
_keyboardVelocity = 0;
|
||||
break;
|
||||
case kZVisionActionUp:
|
||||
case kZVisionActionDown:
|
||||
if (_renderManager->getRenderTable()->getRenderState() == RenderTable::TILT)
|
||||
_keyboardVelocity = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case Common::EVENT_KEYDOWN: {
|
||||
uint8 vkKey = getZvisionKey(_event.kbd.keycode);
|
||||
_scriptManager->setStateValue(StateKey_KeyPress, vkKey);
|
||||
_scriptManager->addEvent(_event);
|
||||
cheatCodes(vkKey);
|
||||
}
|
||||
break;
|
||||
case Common::EVENT_KEYUP:
|
||||
_scriptManager->addEvent(_event);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ZVision::onMouseMove(const Common::Point &pos) {
|
||||
debugC(6, kDebugEvent, "ZVision::onMouseMove()");
|
||||
_menu->onMouseMove(pos);
|
||||
Common::Point imageCoord(_renderManager->screenSpaceToImageSpace(pos));
|
||||
Common::Rect workingArea = _renderManager->getWorkingArea();
|
||||
bool cursorWasChanged = false;
|
||||
|
||||
// Graph of the function governing rotation velocity:
|
||||
//
|
||||
// |---------------- working window ------------------|
|
||||
// ^ |---------|
|
||||
// | |
|
||||
// +Max velocity | rotation screen edge offset
|
||||
// | /|
|
||||
// | / |
|
||||
// | / |
|
||||
// | / |
|
||||
// | / |
|
||||
// | / |
|
||||
// | / |
|
||||
// | / |
|
||||
// | / |
|
||||
// Zero velocity |______________________________ ______________________________/_________|__________________________>
|
||||
// | Position -> | /
|
||||
// | | /
|
||||
// | | /
|
||||
// | | /
|
||||
// | | /
|
||||
// | | /
|
||||
// | | /
|
||||
// | | /
|
||||
// | | /
|
||||
// -Max velocity | |/
|
||||
// |
|
||||
// |
|
||||
// ^
|
||||
|
||||
// Clip the horizontal mouse position to the working window
|
||||
debugC(6, kDebugEvent, "Mouse pos.x, %d, clipping with %d+1, %d+1", pos.x, workingArea.left, workingArea.right);
|
||||
Common::Point clippedPos = pos;
|
||||
clippedPos.x = CLIP<int16>(pos.x, workingArea.left + 1, workingArea.right - 1);
|
||||
if (workingArea.contains(clippedPos) && !_menu->inMenu()) {
|
||||
cursorWasChanged = _scriptManager->onMouseMove(clippedPos, imageCoord);
|
||||
RenderTable::RenderState renderState = _renderManager->getRenderTable()->getRenderState();
|
||||
switch (renderState) {
|
||||
case RenderTable::PANORAMA:
|
||||
if (clippedPos.x >= workingArea.left && clippedPos.x < workingArea.left + ROTATION_SCREEN_EDGE_OFFSET) {
|
||||
int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4;
|
||||
if (mspeed <= 0)
|
||||
mspeed = 25;
|
||||
_mouseVelocity = MIN(((Common::Rational(mspeed, ROTATION_SCREEN_EDGE_OFFSET) * (clippedPos.x - workingArea.left)) - mspeed).toInt(), -1);
|
||||
_cursorManager->changeCursor(CursorIndex_Left);
|
||||
cursorWasChanged = true;
|
||||
} else if (clippedPos.x <= workingArea.right && clippedPos.x > workingArea.right - ROTATION_SCREEN_EDGE_OFFSET) {
|
||||
int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4;
|
||||
if (mspeed <= 0)
|
||||
mspeed = 25;
|
||||
_mouseVelocity = MAX((Common::Rational(mspeed, ROTATION_SCREEN_EDGE_OFFSET) * (clippedPos.x - workingArea.right + ROTATION_SCREEN_EDGE_OFFSET)).toInt(), 1);
|
||||
_cursorManager->changeCursor(CursorIndex_Right);
|
||||
cursorWasChanged = true;
|
||||
} else
|
||||
_mouseVelocity = 0;
|
||||
break;
|
||||
case RenderTable::TILT:
|
||||
if (clippedPos.y >= workingArea.top && clippedPos.y < workingArea.top + ROTATION_SCREEN_EDGE_OFFSET) {
|
||||
int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4;
|
||||
if (mspeed <= 0)
|
||||
mspeed = 25;
|
||||
_mouseVelocity = MIN(((Common::Rational(mspeed, ROTATION_SCREEN_EDGE_OFFSET) * (pos.y - workingArea.top)) - mspeed).toInt(), -1);
|
||||
_cursorManager->changeCursor(CursorIndex_UpArr);
|
||||
cursorWasChanged = true;
|
||||
} else if (clippedPos.y <= workingArea.bottom && clippedPos.y > workingArea.bottom - ROTATION_SCREEN_EDGE_OFFSET) {
|
||||
int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4;
|
||||
if (mspeed <= 0)
|
||||
mspeed = 25;
|
||||
_mouseVelocity = MAX((Common::Rational(MAX_ROTATION_SPEED, ROTATION_SCREEN_EDGE_OFFSET) * (pos.y - workingArea.bottom + ROTATION_SCREEN_EDGE_OFFSET)).toInt(), 1);
|
||||
_cursorManager->changeCursor(CursorIndex_DownArr);
|
||||
cursorWasChanged = true;
|
||||
} else
|
||||
_mouseVelocity = 0;
|
||||
break;
|
||||
case RenderTable::FLAT:
|
||||
default:
|
||||
_mouseVelocity = 0;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
_mouseVelocity = 0;
|
||||
if (!cursorWasChanged)
|
||||
_cursorManager->changeCursor(CursorIndex_Idle);
|
||||
}
|
||||
|
||||
uint8 ZVision::getZvisionKey(Common::KeyCode scummKeyCode) {
|
||||
if (scummKeyCode >= Common::KEYCODE_a && scummKeyCode <= Common::KEYCODE_z)
|
||||
return 0x41 + scummKeyCode - Common::KEYCODE_a;
|
||||
if (scummKeyCode >= Common::KEYCODE_0 && scummKeyCode <= Common::KEYCODE_9)
|
||||
return 0x30 + scummKeyCode - Common::KEYCODE_0;
|
||||
if (scummKeyCode >= Common::KEYCODE_F1 && scummKeyCode <= Common::KEYCODE_F15)
|
||||
return 0x70 + scummKeyCode - Common::KEYCODE_F1;
|
||||
if (scummKeyCode >= Common::KEYCODE_KP0 && scummKeyCode <= Common::KEYCODE_KP9)
|
||||
return 0x60 + scummKeyCode - Common::KEYCODE_KP0;
|
||||
|
||||
switch (scummKeyCode) {
|
||||
case Common::KEYCODE_BACKSPACE:
|
||||
return 0x8;
|
||||
case Common::KEYCODE_TAB:
|
||||
return 0x9;
|
||||
case Common::KEYCODE_CLEAR:
|
||||
return 0xC;
|
||||
case Common::KEYCODE_RETURN:
|
||||
return 0xD;
|
||||
case Common::KEYCODE_CAPSLOCK:
|
||||
return 0x14;
|
||||
case Common::KEYCODE_ESCAPE:
|
||||
return 0x1B;
|
||||
case Common::KEYCODE_SPACE:
|
||||
return 0x20;
|
||||
case Common::KEYCODE_PAGEUP:
|
||||
return 0x21;
|
||||
case Common::KEYCODE_PAGEDOWN:
|
||||
return 0x22;
|
||||
case Common::KEYCODE_END:
|
||||
return 0x23;
|
||||
case Common::KEYCODE_HOME:
|
||||
return 0x24;
|
||||
case Common::KEYCODE_LEFT:
|
||||
return 0x25;
|
||||
case Common::KEYCODE_UP:
|
||||
return 0x26;
|
||||
case Common::KEYCODE_RIGHT:
|
||||
return 0x27;
|
||||
case Common::KEYCODE_DOWN:
|
||||
return 0x28;
|
||||
case Common::KEYCODE_PRINT:
|
||||
return 0x2A;
|
||||
case Common::KEYCODE_INSERT:
|
||||
return 0x2D;
|
||||
case Common::KEYCODE_DELETE:
|
||||
return 0x2E;
|
||||
case Common::KEYCODE_HELP:
|
||||
return 0x2F;
|
||||
case Common::KEYCODE_KP_MULTIPLY:
|
||||
return 0x6A;
|
||||
case Common::KEYCODE_KP_PLUS:
|
||||
return 0x6B;
|
||||
case Common::KEYCODE_KP_MINUS:
|
||||
return 0x6D;
|
||||
case Common::KEYCODE_KP_PERIOD:
|
||||
return 0x6E;
|
||||
case Common::KEYCODE_KP_DIVIDE:
|
||||
return 0x6F;
|
||||
case Common::KEYCODE_NUMLOCK:
|
||||
return 0x90;
|
||||
case Common::KEYCODE_SCROLLOCK:
|
||||
return 0x91;
|
||||
case Common::KEYCODE_LSHIFT:
|
||||
return 0xA0;
|
||||
case Common::KEYCODE_RSHIFT:
|
||||
return 0xA1;
|
||||
case Common::KEYCODE_LCTRL:
|
||||
return 0xA2;
|
||||
case Common::KEYCODE_RCTRL:
|
||||
return 0xA3;
|
||||
case Common::KEYCODE_MENU:
|
||||
return 0xA5;
|
||||
case Common::KEYCODE_LEFTBRACKET:
|
||||
return 0xDB;
|
||||
case Common::KEYCODE_RIGHTBRACKET:
|
||||
return 0xDD;
|
||||
case Common::KEYCODE_SEMICOLON:
|
||||
return 0xBA;
|
||||
case Common::KEYCODE_BACKSLASH:
|
||||
return 0xDC;
|
||||
case Common::KEYCODE_QUOTE:
|
||||
return 0xDE;
|
||||
case Common::KEYCODE_SLASH:
|
||||
return 0xBF;
|
||||
case Common::KEYCODE_TILDE:
|
||||
return 0xC0;
|
||||
case Common::KEYCODE_COMMA:
|
||||
return 0xBC;
|
||||
case Common::KEYCODE_PERIOD:
|
||||
return 0xBE;
|
||||
case Common::KEYCODE_MINUS:
|
||||
return 0xBD;
|
||||
case Common::KEYCODE_PLUS:
|
||||
return 0xBB;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ZVision::quit(bool askFirst, bool streaming) {
|
||||
debugC(1, kDebugEvent, "ZVision::quit()");
|
||||
if (askFirst)
|
||||
if (!_subtitleManager->askQuestion(_stringManager->getTextLine(StringManager::ZVISION_STR_EXITPROMT), streaming, true)) {
|
||||
debugC(1, kDebugEvent, "~ZVision::quit()");
|
||||
return false;
|
||||
}
|
||||
//quitGame();
|
||||
_breakMainLoop = true;
|
||||
debugC(1, kDebugEvent, "~ZVision::quit()");
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
6
engines/zvision/credits.pl
Normal file
6
engines/zvision/credits.pl
Normal file
@@ -0,0 +1,6 @@
|
||||
begin_section("Z-Vision");
|
||||
add_person("Adrian Astley", "RichieSams", "");
|
||||
add_person("Filippos Karapetis", "bluegr", "");
|
||||
add_person("Anton Yarcev", "Zidane", "");
|
||||
add_person("Thomas N McEwan", "tnm23", "Widescreen mod, HQ panoramas, 3D audio enhancement");
|
||||
end_section();
|
||||
76
engines/zvision/detection.cpp
Normal file
76
engines/zvision/detection.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "base/plugins.h"
|
||||
#include "common/debug-channels.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str-array.h"
|
||||
#include "engines/advancedDetector.h"
|
||||
#include "zvision/detection.h"
|
||||
#include "zvision/detection_tables.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
static const DebugChannelDef debugFlagList[] = {
|
||||
{ ZVision::kDebugScript, "Script", "Show debug messages for script file parsing" },
|
||||
{ ZVision::kDebugLoop, "Loop", "Show debug messages for main game logic cycle" },
|
||||
{ ZVision::kDebugPuzzle, "Puzzle", "Show debug messages for puzzle processing" },
|
||||
{ ZVision::kDebugAction, "Action", "Show debug messages for action processing" },
|
||||
{ ZVision::kDebugControl, "Control", "Show debug messages for control processing" },
|
||||
{ ZVision::kDebugEffect, "Effect", "Show debug messages for effect processing" },
|
||||
{ ZVision::kDebugGraphics, "Graphics", "Show debug messages for graphics compositing & rendering" },
|
||||
{ ZVision::kDebugVideo, "Video", "Show debug messages for video decoding & playback" },
|
||||
{ ZVision::kDebugSound, "Sound", "Show debug messages for sound & music" },
|
||||
{ ZVision::kDebugSubtitle, "Subtitle", "Show debug messages for subtitles" },
|
||||
{ ZVision::kDebugFile, "File", "Show debug messages for file search & load operations" },
|
||||
{ ZVision::kDebugMouse, "Mouse", "Print coordinates of mouse clicks"},
|
||||
{ ZVision::kDebugAssign, "Assign", "Print new slotkey values when changed by an assignment action"},
|
||||
{ ZVision::kDebugEvent, "Event", "Show debug messages for event processing"},
|
||||
DEBUG_CHANNEL_END
|
||||
};
|
||||
|
||||
class ZVisionMetaEngineDetection : public AdvancedMetaEngineDetection<ZVision::ZVisionGameDescription> {
|
||||
public:
|
||||
ZVisionMetaEngineDetection() : AdvancedMetaEngineDetection(ZVision::gameDescriptions, ZVision::zVisionGames) {
|
||||
_maxScanDepth = 2;
|
||||
_directoryGlobs = ZVision::directoryGlobs;
|
||||
}
|
||||
|
||||
const char *getName() const override {
|
||||
return "zvision";
|
||||
}
|
||||
|
||||
const char *getEngineName() const override {
|
||||
return "Z-Vision";
|
||||
}
|
||||
|
||||
const char *getOriginalCopyright() const override {
|
||||
return "Z-Vision (C) 1996 Activision";
|
||||
}
|
||||
|
||||
const DebugChannelDef *getDebugChannels() const override {
|
||||
return debugFlagList;
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
REGISTER_PLUGIN_STATIC(ZVISION_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, ZVision::ZVisionMetaEngineDetection);
|
||||
69
engines/zvision/detection.h
Normal file
69
engines/zvision/detection.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 ZVISION_DETECTION_H
|
||||
#define ZVISION_DETECTION_H
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
enum ZVisionDebugChannels {
|
||||
kDebugScript = 1,
|
||||
kDebugLoop,
|
||||
kDebugPuzzle,
|
||||
kDebugAction,
|
||||
kDebugControl,
|
||||
kDebugEffect,
|
||||
kDebugGraphics,
|
||||
kDebugVideo,
|
||||
kDebugSound,
|
||||
kDebugSubtitle,
|
||||
kDebugFile,
|
||||
kDebugMouse,
|
||||
kDebugAssign,
|
||||
kDebugEvent
|
||||
};
|
||||
|
||||
enum ZVisionGameId {
|
||||
GID_NONE = 0,
|
||||
GID_NEMESIS = 1,
|
||||
GID_GRANDINQUISITOR = 2
|
||||
};
|
||||
|
||||
struct ZVisionGameDescription {
|
||||
AD_GAME_DESCRIPTION_HELPERS(desc);
|
||||
|
||||
ADGameDescription desc;
|
||||
ZVisionGameId gameId;
|
||||
};
|
||||
|
||||
#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
|
||||
#define GAMEOPTION_DOUBLE_FPS GUIO_GAMEOPTIONS2
|
||||
#define GAMEOPTION_ENABLE_VENUS GUIO_GAMEOPTIONS3
|
||||
#define GAMEOPTION_DISABLE_ANIM_WHILE_TURNING GUIO_GAMEOPTIONS4
|
||||
#define GAMEOPTION_USE_HIRES_MPEG_MOVIES GUIO_GAMEOPTIONS5
|
||||
#define GAMEOPTION_ENABLE_WIDESCREEN GUIO_GAMEOPTIONS6
|
||||
#define GAMEOPTION_HQ_PANORAMA GUIO_GAMEOPTIONS7
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif // ZVISION_DETECTION_H
|
||||
268
engines/zvision/detection_tables.h
Normal file
268
engines/zvision/detection_tables.h
Normal file
@@ -0,0 +1,268 @@
|
||||
/* 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 ZVISION_DETECTION_TABLES_H
|
||||
#define ZVISION_DETECTION_TABLES_H
|
||||
|
||||
#include "common/translation.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
static const PlainGameDescriptor zVisionGames[] = {
|
||||
{ "znemesis", "Zork Nemesis: The Forbidden Lands" },
|
||||
{ "zgi", "Zork: Grand Inquisitor" },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static const char *const directoryGlobs[] = {
|
||||
"znemscr",
|
||||
"taunts", // zgi mac
|
||||
0
|
||||
};
|
||||
|
||||
static const ZVisionGameDescription gameDescriptions[] = {
|
||||
|
||||
{
|
||||
// Zork Nemesis English version
|
||||
{
|
||||
"znemesis",
|
||||
0,
|
||||
{
|
||||
{ "CSCR.ZFS", 0, "88226e51a205d2e50c67a5237f3bd5f2", 2397741 },
|
||||
{ "ASCR.ZFS", 0, "9a1e1a48a56cf12a22bad2d2e47f6c92", 917452 },
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO6(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, GAMEOPTION_ENABLE_WIDESCREEN, GAMEOPTION_HQ_PANORAMA)
|
||||
},
|
||||
GID_NEMESIS
|
||||
},
|
||||
|
||||
{
|
||||
// Zork Nemesis French version
|
||||
{
|
||||
"znemesis",
|
||||
0,
|
||||
AD_ENTRY2s("CSCR.ZFS", "f04113357b4748c13efcb58b4629887c", 2577873,
|
||||
"NEMESIS.STR", "333bcb17bbb7f57cae742fbbe44f56f3", 9219),
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO6(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, GAMEOPTION_ENABLE_WIDESCREEN, GAMEOPTION_HQ_PANORAMA)
|
||||
},
|
||||
GID_NEMESIS
|
||||
},
|
||||
|
||||
{
|
||||
// Zork Nemesis German version
|
||||
{
|
||||
"znemesis",
|
||||
0,
|
||||
AD_ENTRY2s("CSCR.ZFS", "f04113357b4748c13efcb58b4629887c", 2577873,
|
||||
"NEMESIS.STR", "3d1a12b907751653866cffc6d4dfb331", 9505),
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO6(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, GAMEOPTION_ENABLE_WIDESCREEN, GAMEOPTION_HQ_PANORAMA)
|
||||
},
|
||||
GID_NEMESIS
|
||||
},
|
||||
|
||||
{
|
||||
// Zork Nemesis Italian version
|
||||
{
|
||||
"znemesis",
|
||||
0,
|
||||
AD_ENTRY2s("CSCR.ZFS", "f04113357b4748c13efcb58b4629887c", 2577873,
|
||||
"NEMESIS.STR", "7c568feca8d9f9ae855c47183612c305", 9061),
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO6(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, GAMEOPTION_ENABLE_WIDESCREEN, GAMEOPTION_HQ_PANORAMA)
|
||||
},
|
||||
GID_NEMESIS
|
||||
},
|
||||
|
||||
{
|
||||
// Zork Nemesis Korean version
|
||||
{
|
||||
"znemesis",
|
||||
0,
|
||||
{
|
||||
{ "CSCR.ZFS", 0, "88226e51a205d2e50c67a5237f3bd5f2", 2397741 },
|
||||
{ "ASCR.ZFS", 0, "127f59f96be3d13eafac665eeede080d", 765413 },
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::KO_KOR,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO6(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, GAMEOPTION_ENABLE_WIDESCREEN, GAMEOPTION_HQ_PANORAMA)
|
||||
},
|
||||
GID_NEMESIS
|
||||
},
|
||||
|
||||
{
|
||||
// Zork Nemesis English Mac version
|
||||
// Bugreport #11755
|
||||
{
|
||||
"znemesis",
|
||||
MetaEngineDetection::GAME_NOT_IMPLEMENTED, // Reason for being unsupported
|
||||
{
|
||||
{ "CSCR.ZFS", 0, "d:ce26cbb17bfbaa774742b3187262a7c0", 2597635 },
|
||||
{ "ASCR.ZFS", 0, "d:5ee98db1bf73983eb8148da231342085", 929931 },
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_UNSUPPORTED,
|
||||
GUIO6(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, GAMEOPTION_ENABLE_WIDESCREEN, GAMEOPTION_HQ_PANORAMA)
|
||||
},
|
||||
GID_NEMESIS
|
||||
},
|
||||
|
||||
{
|
||||
// Zork Nemesis English demo version
|
||||
{
|
||||
"znemesis",
|
||||
"Demo",
|
||||
AD_ENTRY1s("SCRIPTS.ZFS", "64f1e881394e9462305104f99513c833", 380539),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DEMO,
|
||||
GUIO6(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, GAMEOPTION_ENABLE_WIDESCREEN, GAMEOPTION_HQ_PANORAMA)
|
||||
},
|
||||
GID_NEMESIS
|
||||
},
|
||||
|
||||
{
|
||||
// Zork Grand Inquisitor English CD version
|
||||
{
|
||||
"zgi",
|
||||
"CD",
|
||||
AD_ENTRY1s("SCRIPTS.ZFS", "81efd40ecc3d22531e211368b779f17f", 8336944),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO5(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, GAMEOPTION_ENABLE_WIDESCREEN, GAMEOPTION_HQ_PANORAMA)
|
||||
},
|
||||
GID_GRANDINQUISITOR
|
||||
},
|
||||
|
||||
{
|
||||
// Zork Grand Inquisitor French CD version, reported by ulrichh on IRC
|
||||
{
|
||||
"zgi",
|
||||
"CD",
|
||||
AD_ENTRY1s("SCRIPTS.ZFS", "4d1ec4ade7ecc9ee9ec591d43ca3d213", 8338133),
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO5(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, GAMEOPTION_ENABLE_WIDESCREEN, GAMEOPTION_HQ_PANORAMA)
|
||||
},
|
||||
GID_GRANDINQUISITOR
|
||||
},
|
||||
|
||||
{
|
||||
// Zork Grand Inquisitor German CD version, reported by breit in bug #6760
|
||||
{
|
||||
"zgi",
|
||||
"CD",
|
||||
AD_ENTRY1s("SCRIPTS.ZFS", "b7ac7e331b9b7f884590b0b325b560c8", 8338133),
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO5(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, GAMEOPTION_ENABLE_WIDESCREEN, GAMEOPTION_HQ_PANORAMA)
|
||||
},
|
||||
GID_GRANDINQUISITOR
|
||||
},
|
||||
|
||||
{
|
||||
// Zork Grand Inquisitor Spanish CD version, reported by dianiu in bug #6764
|
||||
{
|
||||
"zgi",
|
||||
"CD",
|
||||
AD_ENTRY1s("SCRIPTS.ZFS", "5cdc4b99c1134053af135aae71326fd1", 8338141),
|
||||
Common::ES_ESP,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO5(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, GAMEOPTION_ENABLE_WIDESCREEN, GAMEOPTION_HQ_PANORAMA)
|
||||
},
|
||||
GID_GRANDINQUISITOR
|
||||
},
|
||||
|
||||
{
|
||||
// Zork Grand Inquisitor Mac CD version, reported by macca8 in bug #11756
|
||||
{
|
||||
"zgi",
|
||||
"CD",
|
||||
AD_ENTRY2s("SCRIPTS.ZFS", "81efd40ecc3d22531e211368b779f17f", 8336944,
|
||||
"G0LPH10P.RAW", "c0b1f28b1cd1aaeb83c1a3985401bb14", 24462),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO5(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, GAMEOPTION_ENABLE_WIDESCREEN, GAMEOPTION_HQ_PANORAMA)
|
||||
},
|
||||
GID_GRANDINQUISITOR
|
||||
},
|
||||
|
||||
{
|
||||
// Zork Grand Inquisitor English DVD version
|
||||
{
|
||||
"zgi",
|
||||
"DVD",
|
||||
AD_ENTRY1s("SCRIPTS.ZFS", "03157a3399513bfaaf8dc6d5ab798b36", 8433326),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DVD,
|
||||
#if defined(USE_MPEG2) && defined(USE_A52)
|
||||
GUIO6(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, GAMEOPTION_USE_HIRES_MPEG_MOVIES, GAMEOPTION_ENABLE_WIDESCREEN, GAMEOPTION_HQ_PANORAMA)
|
||||
#else
|
||||
GUIO5(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, GAMEOPTION_ENABLE_WIDESCREEN, GAMEOPTION_HQ_PANORAMA)
|
||||
#endif
|
||||
},
|
||||
GID_GRANDINQUISITOR
|
||||
},
|
||||
|
||||
{
|
||||
// Zork Grand Inquisitor English demo version
|
||||
{
|
||||
"zgi",
|
||||
"Demo",
|
||||
AD_ENTRY1s("SCRIPTS.ZFS", "71a2494fd2fb999347deb13401e9b998", 304239),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DEMO,
|
||||
GUIO5(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, GAMEOPTION_ENABLE_WIDESCREEN, GAMEOPTION_HQ_PANORAMA)
|
||||
},
|
||||
GID_GRANDINQUISITOR
|
||||
},
|
||||
|
||||
{
|
||||
AD_TABLE_END_MARKER,
|
||||
GID_NONE
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
266
engines/zvision/file/file_manager.cpp
Normal file
266
engines/zvision/file/file_manager.cpp
Normal file
@@ -0,0 +1,266 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "zvision/file/file_manager.h"
|
||||
#include "zvision/file/zfs_archive.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
const char* genExcluded[] {"*.dll", "*.ini", "*.exe", "*.isu", "*.inf", "*path*.txt", "r.svr", "*.zix", "*.hlp", "*.gid"};
|
||||
|
||||
const char* zgiAlternates[] {
|
||||
"c000h01q.raw", "cm00h01q.raw", "dm00h01q.raw", "e000h01q.raw", "em00h11p.raw", "em00h50q.raw", "gjnph65p.raw",
|
||||
"gjnph72p.raw", "h000h01q.raw", "m000h01q.raw", "p000h01q.raw", "q000h01q.raw", "sw00h01q.raw", "t000h01q.raw",
|
||||
"u000h01q.raw"
|
||||
};
|
||||
|
||||
FileManager::FileManager(ZVision *engine) {
|
||||
}
|
||||
|
||||
Common::File *FileManager::open(const Common::Path &filePath, bool allowSrc) {
|
||||
debugC(5, kDebugFile, "FileManager::open()");
|
||||
Common::File *file = new Common::File();
|
||||
Common::File *out = nullptr;
|
||||
|
||||
Common::String fileName = filePath.baseName();
|
||||
bool open = false;
|
||||
bool altFound = false;
|
||||
bool altOpen = false;
|
||||
|
||||
bool found = SearchMan.hasFile(filePath);
|
||||
if(found) {
|
||||
debugC(5, kDebugFile,"File %s found", fileName.c_str());
|
||||
open = file->open(filePath);
|
||||
if(open)
|
||||
debugC(5, kDebugFile,"File %s opened", fileName.c_str());
|
||||
}
|
||||
|
||||
if (allowSrc) {
|
||||
Common::File *altFile = new Common::File();
|
||||
Common::String altName = fileName;
|
||||
altName.setChar('s', altName.size() - 3);
|
||||
altName.setChar('r', altName.size() - 2);
|
||||
altName.setChar('c', altName.size() - 1);
|
||||
Common::Path altPath = filePath.getParent().appendComponent(altName);
|
||||
altFound = SearchMan.hasFile(altPath);
|
||||
if (altFound) {
|
||||
debugC(5, kDebugFile,"Alternate file %s found", altName.c_str());
|
||||
altOpen = altFile->open(altPath);
|
||||
if (altOpen)
|
||||
debugC(5, kDebugFile,"Alternate file %s opened", altName.c_str());
|
||||
}
|
||||
|
||||
if(altOpen) {
|
||||
if(open)
|
||||
out = file->size() < altFile->size() ? altFile : file;
|
||||
else
|
||||
out = altFile;
|
||||
}
|
||||
else if(open)
|
||||
out = file;
|
||||
else {
|
||||
if (found && altFound)
|
||||
warning("Found file %s and alternate file %s but unable to open either", fileName.c_str(), altName.c_str());
|
||||
else if (found)
|
||||
warning("Found file %s but unable to open; alternate file %s not found", fileName.c_str(), altName.c_str());
|
||||
else if (altFound)
|
||||
warning("File %s not found; alternate file %s found but but unable to open", fileName.c_str(), altName.c_str());
|
||||
else
|
||||
warning("Unable to find file %s or alternate file %s", fileName.c_str(), altName.c_str());
|
||||
}
|
||||
|
||||
if (out == altFile)
|
||||
debugC(5, kDebugFile,"Returning alternate file %s", altName.c_str());
|
||||
else {
|
||||
if(altOpen)
|
||||
altFile->close();
|
||||
delete altFile;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(open)
|
||||
out = file;
|
||||
else if (found)
|
||||
warning("File %s found, but unable to open", fileName.c_str());
|
||||
else
|
||||
warning("File %s not found", fileName.c_str());
|
||||
}
|
||||
|
||||
if (out == file)
|
||||
debugC(5, kDebugFile,"Returning file %s", fileName.c_str());
|
||||
else {
|
||||
if(open)
|
||||
file->close();
|
||||
delete file;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
bool FileManager::exists(Common::Path filePath, bool allowSrc) {
|
||||
Common::File file;
|
||||
if (file.exists(filePath))
|
||||
return true;
|
||||
else if (allowSrc) {
|
||||
if (file.exists(srcPath(filePath)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::Path FileManager::srcPath(Common::Path filePath) {
|
||||
Common::String name = filePath.baseName();
|
||||
name.setChar('s', name.size() - 3);
|
||||
name.setChar('r', name.size() - 2);
|
||||
name.setChar('c', name.size() - 1);
|
||||
return filePath.getParent().appendComponent(name);
|
||||
}
|
||||
|
||||
bool FileManager::loadZix(const Common::Path &zixPath, const Common::FSNode &gameDataDir) {
|
||||
Common::File zixFile;
|
||||
if (!zixFile.open(zixPath))
|
||||
return false;
|
||||
|
||||
Common::String line;
|
||||
|
||||
// Skip first block
|
||||
while (!zixFile.eos()) {
|
||||
line = zixFile.readLine();
|
||||
if (line.matchString("----------*", true))
|
||||
break;
|
||||
}
|
||||
|
||||
if (zixFile.eos())
|
||||
error("Corrupt ZIX file: %s", zixPath.toString(Common::Path::kNativeSeparator).c_str());
|
||||
|
||||
uint8 archives = 0;
|
||||
|
||||
// Parse subdirectories & archives
|
||||
debugC(1, kDebugFile, "Parsing list of subdirectories & archives in %s", zixPath.toString(Common::Path::kNativeSeparator).c_str());
|
||||
while (!zixFile.eos()) {
|
||||
line = zixFile.readLine();
|
||||
line.trim();
|
||||
if (line.matchString("----------*", true))
|
||||
break;
|
||||
else if (line.matchString("DIR:*", true) || line.matchString("CD0:*", true) || line.matchString("CD1:*", true) || line.matchString("CD2:*", true)) {
|
||||
line = Common::String(line.c_str() + 5);
|
||||
for (uint i = 0; i < line.size(); i++)
|
||||
if (line[i] == '\\')
|
||||
line.setChar('/', i);
|
||||
|
||||
// Check if NEMESIS.ZIX/MEDIUM.ZIX refers to the znemesis folder, and
|
||||
// check the game root folder instead
|
||||
if (line.hasPrefix("znemesis/"))
|
||||
line = Common::String(line.c_str() + 9);
|
||||
|
||||
// Check if INQUIS.ZIX refers to the ZGI folder, and check the game
|
||||
// root folder instead
|
||||
if (line.hasPrefix("zgi/"))
|
||||
line = Common::String(line.c_str() + 4);
|
||||
if (line.hasPrefix("zgi_e/"))
|
||||
line = Common::String(line.c_str() + 6);
|
||||
|
||||
if (line.size() && line[0] == '.')
|
||||
line.deleteChar(0);
|
||||
if (line.size() && line[0] == '/')
|
||||
line.deleteChar(0);
|
||||
if (line.size() && line.hasSuffix("/"))
|
||||
line.deleteLastChar();
|
||||
|
||||
Common::Path path(line, '/');
|
||||
|
||||
if (line.matchString("*.zfs", true)) {
|
||||
if (!SearchMan.hasArchive(line)) {
|
||||
path = path.getLastComponent(); //We are using the search manager in "flat" mode, so only filenames are needed
|
||||
debugC(1, kDebugFile, "Adding archive %s to search manager.", path.toString().c_str());
|
||||
Common::Archive *arc;
|
||||
arc = new ZfsArchive(path);
|
||||
SearchMan.add(line, arc);
|
||||
}
|
||||
}
|
||||
else {
|
||||
debugC(1, kDebugFile, "Adding directory %s to search manager.", path.toString().c_str());
|
||||
SearchMan.addSubDirectoryMatching(gameDataDir,path.toString());
|
||||
}
|
||||
archives++;
|
||||
}
|
||||
}
|
||||
|
||||
if (zixFile.eos())
|
||||
error("Corrupt ZIX file: %s", zixPath.toString(Common::Path::kNativeSeparator).c_str());
|
||||
|
||||
//Parse files
|
||||
debugC(1, kDebugFile, "Parsing list of individual resource files in %s", zixPath.toString(Common::Path::kNativeSeparator).c_str());
|
||||
while (!zixFile.eos()) {
|
||||
line = zixFile.readLine();
|
||||
line.trim();
|
||||
uint dr = 0;
|
||||
char buf[32];
|
||||
if (sscanf(line.c_str(), "%u %s", &dr, buf) == 2) {
|
||||
if (dr <= archives && dr > 0) {
|
||||
Common::String name(buf);
|
||||
bool exclude = false;
|
||||
bool allowSrc = false;
|
||||
for (auto excName : genExcluded)
|
||||
if(name.matchString(excName, true)) {
|
||||
exclude = true;
|
||||
break;
|
||||
}
|
||||
for (auto altName : zgiAlternates)
|
||||
if(name.matchString(altName, true)) {
|
||||
allowSrc = true;
|
||||
break;
|
||||
}
|
||||
if (!exclude) {
|
||||
Common::Path path(name);
|
||||
// No need to add file, just verify that it exists
|
||||
if (allowSrc) {
|
||||
Common::Path altPath = srcPath(path);
|
||||
if (!SearchMan.hasFile(path) && !SearchMan.hasFile(altPath))
|
||||
warning("Missing files %s and/or %s", path.toString().c_str(), altPath.toString().c_str());
|
||||
else if (SearchMan.hasFile(path))
|
||||
debugC(5, kDebugFile, "File found: %s", path.toString().c_str());
|
||||
else
|
||||
debugC(5, kDebugFile, "Alternate file found: %s", altPath.toString().c_str());
|
||||
}
|
||||
else {
|
||||
if (!SearchMan.hasFile(path))
|
||||
warning("Missing file %s", path.toString().c_str());
|
||||
else
|
||||
debugC(5, kDebugFile, "File found: %s", path.toString().c_str());
|
||||
}
|
||||
if (name.matchString("*.zfs", true))
|
||||
if (!SearchMan.hasArchive(name)) {
|
||||
Common::Path path_(path);
|
||||
debugC(kDebugFile, "Adding archive %s to search manager.", path.toString().c_str());
|
||||
Common::Archive *arc;
|
||||
arc = new ZfsArchive(path_);
|
||||
SearchMan.add(name, arc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Zvision
|
||||
46
engines/zvision/file/file_manager.h
Normal file
46
engines/zvision/file/file_manager.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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/path.h"
|
||||
#include "zvision/zvision.h"
|
||||
|
||||
#ifndef ZVISION_FILE_MANAGER
|
||||
#define ZVISION_FILE_MANAGER
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class FileManager {
|
||||
public:
|
||||
FileManager(ZVision *engine);
|
||||
~FileManager() {};
|
||||
|
||||
bool loadZix(const Common::Path &zixPath, const Common::FSNode &gameDataDir);
|
||||
Common::File *open(const Common::Path &fileName, bool allowSrc=true); // Wrapper to automatically handle loading of files which may be empty & have an alternate .src file
|
||||
bool exists(Common::Path filePath, bool allowSrc=true); // Wrapper to automatically handle checking existence of files which may be empty & have an alternate .src file
|
||||
|
||||
private:
|
||||
Common::Path srcPath(Common::Path filePath);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
101
engines/zvision/file/lzss_read_stream.cpp
Normal file
101
engines/zvision/file/lzss_read_stream.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "zvision/file/lzss_read_stream.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
LzssReadStream::LzssReadStream(Common::SeekableReadStream *source)
|
||||
: _source(source),
|
||||
// It's convention to set the starting cursor position to blockSize - 16
|
||||
_windowCursor(0x0FEE),
|
||||
_eosFlag(false) {
|
||||
// All values up to _windowCursor inits by 0x20
|
||||
memset(_window, 0x20, _windowCursor);
|
||||
memset(_window + _windowCursor, 0, BLOCK_SIZE - _windowCursor);
|
||||
}
|
||||
|
||||
uint32 LzssReadStream::decompressBytes(byte *destination, uint32 numberOfBytes) {
|
||||
uint32 destinationCursor = 0;
|
||||
|
||||
while (destinationCursor < numberOfBytes) {
|
||||
byte flagbyte = _source->readByte();
|
||||
if (_source->eos())
|
||||
break;
|
||||
uint mask = 1;
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
if ((flagbyte & mask) == mask) {
|
||||
byte data = _source->readByte();
|
||||
if (_source->eos()) {
|
||||
return destinationCursor;
|
||||
}
|
||||
|
||||
_window[_windowCursor] = data;
|
||||
destination[destinationCursor++] = data;
|
||||
|
||||
// Increment and wrap the window cursor
|
||||
_windowCursor = (_windowCursor + 1) & 0xFFF;
|
||||
} else {
|
||||
byte low = _source->readByte();
|
||||
if (_source->eos()) {
|
||||
return destinationCursor;
|
||||
}
|
||||
|
||||
byte high = _source->readByte();
|
||||
if (_source->eos()) {
|
||||
return destinationCursor;
|
||||
}
|
||||
|
||||
uint16 length = (high & 0xF) + 2;
|
||||
uint16 offset = low | ((high & 0xF0) << 4);
|
||||
|
||||
for (int j = 0; j <= length; ++j) {
|
||||
byte temp = _window[(offset + j) & 0xFFF];
|
||||
_window[_windowCursor] = temp;
|
||||
destination[destinationCursor++] = temp;
|
||||
_windowCursor = (_windowCursor + 1) & 0xFFF;
|
||||
}
|
||||
}
|
||||
|
||||
mask = mask << 1;
|
||||
}
|
||||
}
|
||||
|
||||
return destinationCursor;
|
||||
}
|
||||
|
||||
bool LzssReadStream::eos() const {
|
||||
return _eosFlag;
|
||||
}
|
||||
|
||||
uint32 LzssReadStream::read(void *dataPtr, uint32 dataSize) {
|
||||
uint32 bytesRead = decompressBytes(static_cast<byte *>(dataPtr), dataSize);
|
||||
if (bytesRead < dataSize) {
|
||||
// Flag that we're at EOS
|
||||
_eosFlag = true;
|
||||
}
|
||||
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
70
engines/zvision/file/lzss_read_stream.h
Normal file
70
engines/zvision/file/lzss_read_stream.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_LZSS_STREAM_H
|
||||
#define ZVISION_LZSS_STREAM_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class LzssReadStream : public Common::ReadStream {
|
||||
public:
|
||||
/**
|
||||
* A class that decompresses LZSS data and implements ReadStream for easy access
|
||||
* to the decompiled data.
|
||||
*
|
||||
* @param source The source data
|
||||
*/
|
||||
LzssReadStream(Common::SeekableReadStream *source);
|
||||
|
||||
private:
|
||||
enum {
|
||||
BLOCK_SIZE = 0x1000
|
||||
};
|
||||
|
||||
private:
|
||||
Common::SeekableReadStream *_source;
|
||||
byte _window[BLOCK_SIZE];
|
||||
uint _windowCursor;
|
||||
bool _eosFlag;
|
||||
|
||||
public:
|
||||
bool eos() const override;
|
||||
uint32 read(void *dataPtr, uint32 dataSize) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Decompress the next <numberOfBytes> from the source stream. Or until EOS
|
||||
*
|
||||
* @param numberOfBytes How many bytes to decompress. This is a count of source bytes, not destination bytes
|
||||
*/
|
||||
uint32 decompressBytes(byte *destination, uint32 numberOfBytes);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
275
engines/zvision/file/save_manager.cpp
Normal file
275
engines/zvision/file/save_manager.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/file.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/system.h"
|
||||
#include "common/translation.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "graphics/thumbnail.h"
|
||||
#include "gui/message.h"
|
||||
#include "gui/saveload.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/file/save_manager.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
const uint32 SaveManager::SAVEGAME_ID = MKTAG('Z', 'E', 'N', 'G');
|
||||
|
||||
bool SaveManager::scummVMSaveLoadDialog(bool isSave) {
|
||||
GUI::SaveLoadChooser *dialog;
|
||||
Common::String desc;
|
||||
int slot;
|
||||
|
||||
if (isSave) {
|
||||
dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
|
||||
|
||||
slot = dialog->runModalWithCurrentTarget();
|
||||
desc = dialog->getResultString();
|
||||
|
||||
if (desc.empty()) {
|
||||
// create our own description for the saved game, the user didn't enter it
|
||||
desc = dialog->createDefaultSaveDescription(slot);
|
||||
}
|
||||
|
||||
if (desc.size() > 28)
|
||||
desc = Common::String(desc.c_str(), 28);
|
||||
} else {
|
||||
dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
|
||||
slot = dialog->runModalWithCurrentTarget();
|
||||
}
|
||||
|
||||
delete dialog;
|
||||
|
||||
if (slot < 0)
|
||||
return false;
|
||||
if (!isSave) {
|
||||
Common::ErrorCode result = loadGame(slot).getCode();
|
||||
return (result == Common::kNoError);
|
||||
}
|
||||
saveGame(slot, desc, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SaveManager::saveGame(uint slot, const Common::String &saveName, bool useSaveBuffer) {
|
||||
if (!_tempSave && useSaveBuffer)
|
||||
return;
|
||||
|
||||
Common::SaveFileManager *saveFileManager = g_system->getSavefileManager();
|
||||
Common::OutSaveFile *file = saveFileManager->openForSaving(_engine->getSaveStateName(slot));
|
||||
|
||||
writeSaveGameHeader(file, saveName, useSaveBuffer);
|
||||
|
||||
if (useSaveBuffer)
|
||||
file->write(_tempSave->getData(), _tempSave->size());
|
||||
else
|
||||
_engine->getScriptManager()->serialize(file);
|
||||
|
||||
file->finalize();
|
||||
delete file;
|
||||
|
||||
if (useSaveBuffer)
|
||||
flushSaveBuffer();
|
||||
|
||||
_lastSaveTime = g_system->getMillis();
|
||||
}
|
||||
|
||||
void SaveManager::writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName, bool useSaveBuffer) {
|
||||
file->writeUint32BE(SAVEGAME_ID);
|
||||
|
||||
// Write version
|
||||
file->writeByte(SAVE_VERSION);
|
||||
|
||||
// Write savegame name
|
||||
file->writeString(saveName);
|
||||
file->writeByte(0);
|
||||
|
||||
// Save the game thumbnail
|
||||
if (useSaveBuffer)
|
||||
file->write(_tempThumbnail->getData(), _tempThumbnail->size());
|
||||
else
|
||||
Graphics::saveThumbnail(*file);
|
||||
|
||||
// Write out the save date/time
|
||||
TimeDate td;
|
||||
g_system->getTimeAndDate(td);
|
||||
file->writeSint16LE(td.tm_year + 1900);
|
||||
file->writeSint16LE(td.tm_mon + 1);
|
||||
file->writeSint16LE(td.tm_mday);
|
||||
file->writeSint16LE(td.tm_hour);
|
||||
file->writeSint16LE(td.tm_min);
|
||||
|
||||
file->writeUint32LE(g_engine->getTotalPlayTime() / 1000);
|
||||
}
|
||||
|
||||
Common::Error SaveManager::loadGame(int slot) {
|
||||
Common::SeekableReadStream *saveFile = NULL;
|
||||
if (slot < 0) {
|
||||
// Restart game, used by ZGI death screen only
|
||||
_engine->getScriptManager()->initialize(true);
|
||||
return Common::kNoError;
|
||||
}
|
||||
saveFile = getSlotFile(slot);
|
||||
if (!saveFile)
|
||||
return Common::kPathDoesNotExist;
|
||||
// Read the header
|
||||
SaveGameHeader header;
|
||||
if (!readSaveGameHeader(saveFile, header))
|
||||
return Common::kUnknownError;
|
||||
ScriptManager *scriptManager = _engine->getScriptManager();
|
||||
// Update the state table values
|
||||
scriptManager->deserialize(saveFile);
|
||||
delete saveFile;
|
||||
if (_engine->getGameId() == GID_NEMESIS) {
|
||||
// Zork Nemesis has no in-game option to select panorama quality or animation options
|
||||
// We set them here to ensure loaded games don't override current game configuration
|
||||
scriptManager->setStateValue(StateKey_HighQuality, ConfMan.getBool("highquality"));
|
||||
scriptManager->setStateValue(StateKey_NoTurnAnim, ConfMan.getBool("noanimwhileturning"));
|
||||
if (scriptManager->getCurrentLocation() == "tv2f") {
|
||||
// WORKAROUND for script bug #6793: location tv2f (stairs) has two states:
|
||||
// one at the top of the stairs, and one at the bottom. When the player
|
||||
// goes to the bottom of the stairs, the screen changes, and hotspot
|
||||
// 4652 (exit opposite the stairs) is enabled. However, the variable that
|
||||
// controls the state (2408) is reset when the player goes down the stairs.
|
||||
// Furthermore, the room's initialization script disables the stair exit
|
||||
// control (4652). This leads to an impossible situation, where all the
|
||||
// exit controls are disabled, and the player can't more anywhere. Thus,
|
||||
// when loading a game in that room, we check for that impossible
|
||||
// situation, which only occurs after the player has moved down the stairs,
|
||||
// and fix it here by setting the correct background, and enabling the
|
||||
// stair exit hotspot.
|
||||
if ((scriptManager->getStateFlag(2411) & Puzzle::DISABLED) &&
|
||||
(scriptManager->getStateFlag(2408) & Puzzle::DISABLED) &&
|
||||
(scriptManager->getStateFlag(4652) & Puzzle::DISABLED)) {
|
||||
_engine->getRenderManager()->setBackgroundImage("tv2fb21c.tga");
|
||||
scriptManager->unsetStateFlag(4652, Puzzle::DISABLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
g_engine->setTotalPlayTime(header.playTime * 1000);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
bool SaveManager::readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header, bool skipThumbnail) {
|
||||
header.saveYear = 0;
|
||||
header.saveMonth = 0;
|
||||
header.saveDay = 0;
|
||||
header.saveHour = 0;
|
||||
header.saveMinutes = 0;
|
||||
header.playTime = 0;
|
||||
header.saveName.clear();
|
||||
header.thumbnail = nullptr;
|
||||
header.version = 0;
|
||||
|
||||
uint32 tag = in->readUint32BE();
|
||||
// Check if it's original savegame than fill header structure
|
||||
if (tag == MKTAG('Z', 'N', 'S', 'G')) {
|
||||
header.saveName = "Original Save";
|
||||
header.version = SAVE_ORIGINAL;
|
||||
in->seek(-4, SEEK_CUR);
|
||||
return true;
|
||||
}
|
||||
if (tag != SAVEGAME_ID) {
|
||||
warning("File is not a Z-Vision saved game. Aborting load");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read in the version
|
||||
header.version = in->readByte();
|
||||
|
||||
// Check that the save version isn't newer than this binary
|
||||
if (header.version > SAVE_VERSION) {
|
||||
uint tempVersion = header.version;
|
||||
GUI::MessageDialog dialog(
|
||||
Common::U32String::format(
|
||||
_("This saved game uses version %u, but this engine only "
|
||||
"supports up to version %d. You will need an updated version "
|
||||
"of the engine to use this saved game."), tempVersion, SAVE_VERSION
|
||||
));
|
||||
dialog.runModal();
|
||||
}
|
||||
|
||||
// Read in the save name
|
||||
char ch;
|
||||
while ((ch = (char)in->readByte()) != '\0')
|
||||
header.saveName += ch;
|
||||
|
||||
// Get the thumbnail
|
||||
if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read in save date/time
|
||||
header.saveYear = in->readSint16LE();
|
||||
header.saveMonth = in->readSint16LE();
|
||||
header.saveDay = in->readSint16LE();
|
||||
header.saveHour = in->readSint16LE();
|
||||
header.saveMinutes = in->readSint16LE();
|
||||
|
||||
if (header.version >= 2) {
|
||||
header.playTime = in->readUint32LE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *SaveManager::getSlotFile(uint slot) {
|
||||
Common::SeekableReadStream *saveFile = g_system->getSavefileManager()->openForLoading(_engine->getSaveStateName(slot));
|
||||
if (saveFile == NULL) {
|
||||
// Try to load standard save file
|
||||
Common::Path filename;
|
||||
if (_engine->getGameId() == GID_GRANDINQUISITOR)
|
||||
filename = Common::Path(Common::String::format("inqsav%u.sav", slot));
|
||||
else if (_engine->getGameId() == GID_NEMESIS)
|
||||
filename = Common::Path(Common::String::format("nemsav%u.sav", slot));
|
||||
|
||||
Common::File *tmpFile = new Common::File;
|
||||
if (!tmpFile->open(filename))
|
||||
delete tmpFile;
|
||||
else
|
||||
saveFile = tmpFile;
|
||||
}
|
||||
return saveFile;
|
||||
}
|
||||
|
||||
void SaveManager::prepareSaveBuffer() {
|
||||
delete _tempThumbnail;
|
||||
_tempThumbnail = new Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES);
|
||||
Graphics::saveThumbnail(*_tempThumbnail);
|
||||
|
||||
delete _tempSave;
|
||||
_tempSave = new Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES);
|
||||
_engine->getScriptManager()->serialize(_tempSave);
|
||||
}
|
||||
|
||||
void SaveManager::flushSaveBuffer() {
|
||||
delete _tempThumbnail;
|
||||
_tempThumbnail = NULL;
|
||||
|
||||
delete _tempSave;
|
||||
_tempSave = NULL;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
103
engines/zvision/file/save_manager.h
Normal file
103
engines/zvision/file/save_manager.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_SAVE_MANAGER_H
|
||||
#define ZVISION_SAVE_MANAGER_H
|
||||
|
||||
#include "common/memstream.h"
|
||||
#include "common/savefile.h"
|
||||
|
||||
namespace Common {
|
||||
class String;
|
||||
}
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
struct SaveGameHeader {
|
||||
byte version;
|
||||
Common::String saveName;
|
||||
Graphics::Surface *thumbnail;
|
||||
int16 saveYear, saveMonth, saveDay;
|
||||
int16 saveHour, saveMinutes;
|
||||
uint32 playTime;
|
||||
};
|
||||
|
||||
class SaveManager {
|
||||
public:
|
||||
SaveManager(ZVision *engine) : _engine(engine), _tempSave(NULL), _tempThumbnail(NULL), _lastSaveTime(0) {}
|
||||
~SaveManager() {
|
||||
flushSaveBuffer();
|
||||
}
|
||||
|
||||
uint32 getLastSaveTime() const {
|
||||
return _lastSaveTime;
|
||||
}
|
||||
|
||||
private:
|
||||
ZVision *_engine;
|
||||
uint32 _lastSaveTime;
|
||||
static const uint32 SAVEGAME_ID;
|
||||
|
||||
enum {
|
||||
SAVE_ORIGINAL = 0,
|
||||
SAVE_VERSION = 2
|
||||
};
|
||||
|
||||
Common::MemoryWriteStreamDynamic *_tempThumbnail;
|
||||
Common::MemoryWriteStreamDynamic *_tempSave;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Copies the data from the last auto-save into a new save file. We
|
||||
* can't use the current state data because the save menu *IS* a room.
|
||||
* The file is named using ZVision::generateSaveFileName(slot)
|
||||
*
|
||||
* @param slot The save slot this save pertains to. Must be [1, 20]
|
||||
* @param saveName The internal name for this save. This is NOT the name of the actual save file.
|
||||
*/
|
||||
void saveGame(uint slot, const Common::String &saveName, bool useSaveBuffer);
|
||||
/**
|
||||
* Loads the state data from the save file that slot references. Uses
|
||||
* ZVision::generateSaveFileName(slot) to get the save file name.
|
||||
*
|
||||
* @param slot The save slot to load. Must be [1, 20]
|
||||
*/
|
||||
Common::Error loadGame(int slot);
|
||||
|
||||
Common::SeekableReadStream *getSlotFile(uint slot);
|
||||
bool readSaveGameHeader(Common::SeekableReadStream *in, SaveGameHeader &header, bool skipThumbnail = true);
|
||||
|
||||
void prepareSaveBuffer();
|
||||
void flushSaveBuffer();
|
||||
bool scummVMSaveLoadDialog(bool isSave);
|
||||
private:
|
||||
void writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName, bool useSaveBuffer);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
156
engines/zvision/file/zfs_archive.cpp
Normal file
156
engines/zvision/file/zfs_archive.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "zvision/detection.h"
|
||||
#include "zvision/file/zfs_archive.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
ZfsArchive::ZfsArchive(const Common::Path &fileName) : _fileName(fileName) {
|
||||
Common::File zfsFile;
|
||||
memset(&_header, 0, sizeof(_header));
|
||||
|
||||
if (!zfsFile.open(_fileName)) {
|
||||
warning("ZFSArchive::ZFSArchive(): Could not find the archive file");
|
||||
return;
|
||||
}
|
||||
|
||||
readHeaders(&zfsFile);
|
||||
|
||||
debugC(1, kDebugFile, "ZfsArchive::ZfsArchive(%s): Located %d files", _fileName.toString(Common::Path::kNativeSeparator).c_str(), _entryHeaders.size());
|
||||
}
|
||||
|
||||
ZfsArchive::ZfsArchive(const Common::Path &fileName, Common::SeekableReadStream *stream) : _fileName(fileName) {
|
||||
readHeaders(stream);
|
||||
|
||||
debugC(1, kDebugFile, "ZfsArchive::ZfsArchive(%s): Located %d files", _fileName.toString(Common::Path::kNativeSeparator).c_str(), _entryHeaders.size());
|
||||
}
|
||||
|
||||
ZfsArchive::~ZfsArchive() {
|
||||
debugC(1, kDebugFile, "ZfsArchive Destructor Called");
|
||||
ZfsEntryHeaderMap::iterator it = _entryHeaders.begin();
|
||||
for (; it != _entryHeaders.end(); ++it) {
|
||||
delete it->_value;
|
||||
}
|
||||
}
|
||||
|
||||
void ZfsArchive::readHeaders(Common::SeekableReadStream *stream) {
|
||||
// Don't do a straight struct cast since we can't guarantee endianness
|
||||
_header.magic = stream->readUint32LE();
|
||||
_header.unknown1 = stream->readUint32LE();
|
||||
_header.maxNameLength = stream->readUint32LE();
|
||||
_header.filesPerBlock = stream->readUint32LE();
|
||||
_header.fileCount = stream->readUint32LE();
|
||||
_header.xorKey[0] = stream->readByte();
|
||||
_header.xorKey[1] = stream->readByte();
|
||||
_header.xorKey[2] = stream->readByte();
|
||||
_header.xorKey[3] = stream->readByte();
|
||||
_header.fileSectionOffset = stream->readUint32LE();
|
||||
|
||||
uint32 nextOffset;
|
||||
|
||||
do {
|
||||
// Read the offset to the next block
|
||||
nextOffset = stream->readUint32LE();
|
||||
|
||||
// Read in each entry header
|
||||
for (uint32 i = 0; i < _header.filesPerBlock; ++i) {
|
||||
ZfsEntryHeader entryHeader;
|
||||
|
||||
entryHeader.name = readEntryName(stream);
|
||||
entryHeader.offset = stream->readUint32LE();
|
||||
entryHeader.id = stream->readUint32LE();
|
||||
entryHeader.size = stream->readUint32LE();
|
||||
entryHeader.time = stream->readUint32LE();
|
||||
entryHeader.unknown = stream->readUint32LE();
|
||||
|
||||
if (entryHeader.size != 0)
|
||||
_entryHeaders[entryHeader.name] = new ZfsEntryHeader(entryHeader);
|
||||
}
|
||||
|
||||
// Seek to the next block of headers
|
||||
stream->seek(nextOffset);
|
||||
} while (nextOffset != 0);
|
||||
}
|
||||
|
||||
Common::String ZfsArchive::readEntryName(Common::SeekableReadStream *stream) const {
|
||||
// Entry Names are at most 16 bytes and are null padded
|
||||
char buffer[16];
|
||||
stream->read(buffer, 16);
|
||||
|
||||
return Common::String(buffer);
|
||||
}
|
||||
|
||||
bool ZfsArchive::hasFile(const Common::Path &path) const {
|
||||
Common::String name = path.toString();
|
||||
return _entryHeaders.contains(name);
|
||||
}
|
||||
|
||||
int ZfsArchive::listMembers(Common::ArchiveMemberList &list) const {
|
||||
int matches = 0;
|
||||
|
||||
for (ZfsEntryHeaderMap::const_iterator it = _entryHeaders.begin(); it != _entryHeaders.end(); ++it) {
|
||||
list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(it->_value->name, *this)));
|
||||
matches++;
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
const Common::ArchiveMemberPtr ZfsArchive::getMember(const Common::Path &path) const {
|
||||
if (!hasFile(path))
|
||||
return Common::ArchiveMemberPtr();
|
||||
|
||||
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(path, *this));
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *ZfsArchive::createReadStreamForMember(const Common::Path &path) const {
|
||||
Common::String name = path.toString();
|
||||
if (!_entryHeaders.contains(name)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ZfsEntryHeader *entryHeader = _entryHeaders[name];
|
||||
|
||||
Common::File zfsArchive;
|
||||
zfsArchive.open(_fileName);
|
||||
zfsArchive.seek(entryHeader->offset);
|
||||
|
||||
// This *HAS* to be malloc (not new[]) because MemoryReadStream uses free() to free the memory
|
||||
byte *buffer = (byte *)malloc(entryHeader->size);
|
||||
zfsArchive.read(buffer, entryHeader->size);
|
||||
// Decrypt the data in place
|
||||
if (_header.xorKey[0] + _header.xorKey[1] + _header.xorKey[2] + _header.xorKey[3] != 0)
|
||||
unXor(buffer, entryHeader->size, _header.xorKey);
|
||||
|
||||
return new Common::MemoryReadStream(buffer, entryHeader->size, DisposeAfterUse::YES);
|
||||
}
|
||||
|
||||
void ZfsArchive::unXor(byte *buffer, uint32 length, const byte *xorKey) const {
|
||||
for (uint32 i = 0; i < length; ++i)
|
||||
buffer[i] ^= xorKey[i % 4];
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
124
engines/zvision/file/zfs_archive.h
Normal file
124
engines/zvision/file/zfs_archive.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/* 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 ZVISION_ZFS_ARCHIVE_H
|
||||
#define ZVISION_ZFS_ARCHIVE_H
|
||||
|
||||
#include "common/archive.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/hash-str.h"
|
||||
|
||||
namespace Common {
|
||||
class String;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
struct ZfsHeader {
|
||||
uint32 magic;
|
||||
uint32 unknown1;
|
||||
uint32 maxNameLength;
|
||||
uint32 filesPerBlock;
|
||||
uint32 fileCount;
|
||||
uint8 xorKey[4];
|
||||
uint32 fileSectionOffset;
|
||||
};
|
||||
|
||||
struct ZfsEntryHeader {
|
||||
Common::String name;
|
||||
uint32 offset;
|
||||
uint32 id;
|
||||
uint32 size;
|
||||
uint32 time;
|
||||
uint32 unknown;
|
||||
};
|
||||
|
||||
typedef Common::HashMap<Common::String, ZfsEntryHeader *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> ZfsEntryHeaderMap;
|
||||
|
||||
class ZfsArchive : public Common::Archive {
|
||||
public:
|
||||
ZfsArchive(const Common::Path &fileName);
|
||||
ZfsArchive(const Common::Path &fileName, Common::SeekableReadStream *stream);
|
||||
~ZfsArchive() override;
|
||||
|
||||
/**
|
||||
* Check if a member with the given name is present in the Archive.
|
||||
* Patterns are not allowed, as this is meant to be a quick File::exists()
|
||||
* replacement.
|
||||
*/
|
||||
bool hasFile(const Common::Path &path) const override;
|
||||
|
||||
/**
|
||||
* Add all members of the Archive to list.
|
||||
* Must only append to list, and not remove elements from it.
|
||||
*
|
||||
* @return The number of names added to list
|
||||
*/
|
||||
int listMembers(Common::ArchiveMemberList &list) const override;
|
||||
|
||||
/**
|
||||
* Returns a ArchiveMember representation of the given file.
|
||||
*/
|
||||
const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
|
||||
|
||||
/**
|
||||
* Create a stream bound to a member with the specified name in the
|
||||
* archive. If no member with this name exists, 0 is returned.
|
||||
*
|
||||
* @return The newly created input stream
|
||||
*/
|
||||
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
|
||||
|
||||
private:
|
||||
const Common::Path _fileName;
|
||||
ZfsHeader _header;
|
||||
ZfsEntryHeaderMap _entryHeaders;
|
||||
|
||||
/**
|
||||
* Parses the zfs file into file entry headers that can be used later
|
||||
* to get the entry data.
|
||||
*
|
||||
* @param stream The contents of the zfs file
|
||||
*/
|
||||
void readHeaders(Common::SeekableReadStream *stream);
|
||||
|
||||
/**
|
||||
* Entry names are contained within a 16 byte block. This reads the block
|
||||
* and converts it the name to a Common::String
|
||||
*
|
||||
* @param stream The zfs file stream
|
||||
* @return The entry file name
|
||||
*/
|
||||
Common::String readEntryName(Common::SeekableReadStream *stream) const;
|
||||
|
||||
/**
|
||||
* ZFS file entries can be encrypted using XOR encoding. This method
|
||||
* decodes the buffer in place using the supplied xorKey.
|
||||
*
|
||||
* @param buffer The data to decode
|
||||
* @param length Length of buffer
|
||||
*/
|
||||
void unXor(byte *buffer, uint32 length, const byte *xorKey) const;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
93
engines/zvision/graphics/cursors/cursor.cpp
Normal file
93
engines/zvision/graphics/cursors/cursor.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
#include "zvision/graphics/cursors/cursor.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
ZorkCursor::ZorkCursor()
|
||||
: _width(0),
|
||||
_height(0),
|
||||
_hotspotX(0),
|
||||
_hotspotY(0) {
|
||||
}
|
||||
|
||||
ZorkCursor::ZorkCursor(ZVision *engine, const Common::Path &fileName)
|
||||
: _width(0),
|
||||
_height(0),
|
||||
_hotspotX(0),
|
||||
_hotspotY(0) {
|
||||
Common::File file;
|
||||
if (!file.open(fileName))
|
||||
error("Cursor file %s does not exist", fileName.toString().c_str());
|
||||
|
||||
uint32 magic = file.readUint32BE();
|
||||
if (magic != MKTAG('Z', 'C', 'R', '1')) {
|
||||
warning("%s is not a Zork Cursor file", fileName.toString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
_hotspotX = file.readUint16LE();
|
||||
_hotspotY = file.readUint16LE();
|
||||
_width = file.readUint16LE();
|
||||
_height = file.readUint16LE();
|
||||
|
||||
uint dataSize = _width * _height * sizeof(uint16);
|
||||
_surface.create(_width, _height, engine->_resourcePixelFormat);
|
||||
uint32 bytesRead = file.read(_surface.getPixels(), dataSize);
|
||||
assert(bytesRead == dataSize);
|
||||
|
||||
#ifndef SCUMM_LITTLE_ENDIAN
|
||||
int16 *buffer = (int16 *)_surface.getPixels();
|
||||
for (uint32 i = 0; i < dataSize / 2; ++i)
|
||||
buffer[i] = FROM_LE_16(buffer[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
ZorkCursor::ZorkCursor(const ZorkCursor &other) {
|
||||
_width = other._width;
|
||||
_height = other._height;
|
||||
_hotspotX = other._hotspotX;
|
||||
_hotspotY = other._hotspotY;
|
||||
|
||||
_surface.copyFrom(other._surface);
|
||||
}
|
||||
|
||||
ZorkCursor &ZorkCursor::operator=(const ZorkCursor &other) {
|
||||
_width = other._width;
|
||||
_height = other._height;
|
||||
_hotspotX = other._hotspotX;
|
||||
_hotspotY = other._hotspotY;
|
||||
|
||||
_surface.free();
|
||||
_surface.copyFrom(other._surface);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ZorkCursor::~ZorkCursor() {
|
||||
_surface.free();
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
77
engines/zvision/graphics/cursors/cursor.h
Normal file
77
engines/zvision/graphics/cursors/cursor.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_CURSOR_H
|
||||
#define ZVISION_CURSOR_H
|
||||
|
||||
#include "graphics/surface.h"
|
||||
#include "zvision/zvision.h"
|
||||
|
||||
namespace Common {
|
||||
class String;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
/**
|
||||
* Utility class to parse and hold cursor data
|
||||
* Modeled off Graphics::Cursor
|
||||
*/
|
||||
class ZorkCursor {
|
||||
public:
|
||||
ZorkCursor();
|
||||
ZorkCursor(ZVision *engine, const Common::Path &fileName);
|
||||
ZorkCursor(const ZorkCursor &other);
|
||||
~ZorkCursor();
|
||||
|
||||
private:
|
||||
uint16 _width;
|
||||
uint16 _height;
|
||||
uint16 _hotspotX;
|
||||
uint16 _hotspotY;
|
||||
Graphics::Surface _surface;
|
||||
|
||||
public:
|
||||
ZorkCursor &operator=(const ZorkCursor &other);
|
||||
|
||||
uint16 getWidth() const {
|
||||
return _width;
|
||||
}
|
||||
uint16 getHeight() const {
|
||||
return _height;
|
||||
}
|
||||
uint16 getHotspotX() const {
|
||||
return _hotspotX;
|
||||
}
|
||||
uint16 getHotspotY() const {
|
||||
return _hotspotY;
|
||||
}
|
||||
byte getKeyColor() const {
|
||||
return 0;
|
||||
}
|
||||
const byte *getSurface() const {
|
||||
return (const byte *)_surface.getPixels();
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
153
engines/zvision/graphics/cursors/cursor_manager.cpp
Normal file
153
engines/zvision/graphics/cursors/cursor_manager.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/system.h"
|
||||
#include "graphics/cursorman.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
const char *CursorManager::_cursorNames[NUM_CURSORS] = { "active", "arrow", "backward", "downarrow", "forward", "handpt", "handpu", "hdown", "hleft",
|
||||
"hright", "hup", "idle", "leftarrow", "rightarrow", "suggest_surround", "suggest_tilt", "turnaround", "zuparrow"
|
||||
};
|
||||
|
||||
const char *CursorManager::_zgiCursorFileNames[NUM_CURSORS] = { "g0gbc011.zcr", "g0gac011.zcr", "g0gac021.zcr", "g0gac031.zcr", "g0gac041.zcr", "g0gac051.zcr", "g0gac061.zcr", "g0gac071.zcr", "g0gac081.zcr",
|
||||
"g0gac091.zcr", "g0gac101.zcr", "g0gac011.zcr", "g0gac111.zcr", "g0gac121.zcr", "g0gac131.zcr", "g0gac141.zcr", "g0gac151.zcr", "g0gac161.zcr"
|
||||
};
|
||||
|
||||
const char *CursorManager::_zNemCursorFileNames[NUM_CURSORS] = { "00act", "arrow", "back", "down", "forw", "handpt", "handpu", "hdown", "hleft",
|
||||
"hright", "hup", "00idle", "left", "right", "ssurr", "stilt", "turn", "up"
|
||||
};
|
||||
|
||||
CursorManager::CursorManager(ZVision *engine, const Graphics::PixelFormat &pixelFormat)
|
||||
: _engine(engine),
|
||||
_pixelFormat(pixelFormat),
|
||||
_cursorIsPushed(false),
|
||||
_item(0),
|
||||
_lastitem(0),
|
||||
_currentCursor(CursorIndex_Idle) {
|
||||
for (int i = 0; i < NUM_CURSORS; i++) {
|
||||
if (_engine->getGameId() == GID_NEMESIS) {
|
||||
Common::Path name;
|
||||
if (i == 1) {
|
||||
// Cursors "arrowa.zcr" and "arrowb.zcr" are missing
|
||||
_cursors[i][0] = _cursors[i][1] = ZorkCursor();
|
||||
continue;
|
||||
}
|
||||
name = Common::Path(Common::String::format("%sa.zcr", _zNemCursorFileNames[i]));
|
||||
_cursors[i][0] = ZorkCursor(_engine, name); // Up cursor
|
||||
name = Common::Path(Common::String::format("%sb.zcr", _zNemCursorFileNames[i]));
|
||||
_cursors[i][1] = ZorkCursor(_engine, name); // Down cursor
|
||||
} else if (_engine->getGameId() == GID_GRANDINQUISITOR) {
|
||||
_cursors[i][0] = ZorkCursor(_engine, _zgiCursorFileNames[i]); // Up cursor
|
||||
char buffer[25];
|
||||
memset(buffer, 0, 25);
|
||||
strncpy(buffer, _zgiCursorFileNames[i], 24);
|
||||
buffer[3] += 2;
|
||||
_cursors[i][1] = ZorkCursor(_engine, buffer); // Down cursor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CursorManager::setItemID(int id) {
|
||||
if (id != _item) {
|
||||
if (id) {
|
||||
Common::Path file;
|
||||
if (_engine->getGameId() == GID_NEMESIS) {
|
||||
file = Common::Path(Common::String::format("%2.2d%s%c.zcr", id, "idle", 'a'));
|
||||
_cursors[NUM_CURSORS][0] = ZorkCursor(_engine, file);
|
||||
file = Common::Path(Common::String::format("%2.2d%s%c.zcr", id, "idle", 'b'));
|
||||
_cursors[NUM_CURSORS][1] = ZorkCursor(_engine, file);
|
||||
file = Common::Path(Common::String::format("%2.2d%s%c.zcr", id, "act", 'a'));
|
||||
_cursors[NUM_CURSORS + 1][0] = ZorkCursor(_engine, file);
|
||||
file = Common::Path(Common::String::format("%2.2d%s%c.zcr", id, "act", 'b'));
|
||||
_cursors[NUM_CURSORS + 1][0] = ZorkCursor(_engine, file);
|
||||
} else if (_engine->getGameId() == GID_GRANDINQUISITOR) {
|
||||
file = Common::Path(Common::String::format("g0b%cc%2.2x1.zcr", 'a' , id));
|
||||
_cursors[NUM_CURSORS][0] = ZorkCursor(_engine, file);
|
||||
file = Common::Path(Common::String::format("g0b%cc%2.2x1.zcr", 'c' , id));
|
||||
_cursors[NUM_CURSORS][1] = ZorkCursor(_engine, file);
|
||||
file = Common::Path(Common::String::format("g0b%cc%2.2x1.zcr", 'b' , id));
|
||||
_cursors[NUM_CURSORS + 1][0] = ZorkCursor(_engine, file);
|
||||
file = Common::Path(Common::String::format("g0b%cc%2.2x1.zcr", 'd' , id));
|
||||
_cursors[NUM_CURSORS + 1][1] = ZorkCursor(_engine, file);
|
||||
} else
|
||||
return;
|
||||
}
|
||||
_item = id;
|
||||
changeCursor(CursorIndex_Idle);
|
||||
}
|
||||
}
|
||||
|
||||
void CursorManager::initialize() {
|
||||
changeCursor(_cursors[CursorIndex_Idle][_cursorIsPushed]);
|
||||
showMouse(true);
|
||||
}
|
||||
|
||||
void CursorManager::changeCursor(const ZorkCursor &cursor) {
|
||||
CursorMan.replaceCursor(cursor.getSurface(), cursor.getWidth(), cursor.getHeight(), cursor.getHotspotX(), cursor.getHotspotY(), cursor.getKeyColor(), false, &_pixelFormat);
|
||||
}
|
||||
|
||||
void CursorManager::cursorDown(bool pushed) {
|
||||
if (_cursorIsPushed == pushed)
|
||||
return;
|
||||
|
||||
_cursorIsPushed = pushed;
|
||||
|
||||
changeCursor(_cursors[_currentCursor][_cursorIsPushed]);
|
||||
}
|
||||
|
||||
void CursorManager::changeCursor(int id) {
|
||||
if (_item && (id == CursorIndex_Active ||
|
||||
id == CursorIndex_Idle ||
|
||||
id == CursorIndex_HandPu)) {
|
||||
if (id == CursorIndex_Idle) {
|
||||
id = CursorIndex_ItemIdle;
|
||||
} else {
|
||||
id = CursorIndex_ItemAct;
|
||||
}
|
||||
}
|
||||
|
||||
if (_currentCursor != id || ((id == CursorIndex_ItemAct || id == CursorIndex_ItemIdle) && _lastitem != _item)) {
|
||||
_currentCursor = id;
|
||||
_lastitem = _item;
|
||||
changeCursor(_cursors[_currentCursor][_cursorIsPushed]);
|
||||
}
|
||||
}
|
||||
|
||||
int CursorManager::getCursorId(const Common::String &name) {
|
||||
for (int i = 0; i < NUM_CURSORS; i++) {
|
||||
if (name.equals(_cursorNames[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return CursorIndex_Idle;
|
||||
}
|
||||
|
||||
void CursorManager::showMouse(bool vis) {
|
||||
CursorMan.showMouse(vis);
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
132
engines/zvision/graphics/cursors/cursor_manager.h
Normal file
132
engines/zvision/graphics/cursors/cursor_manager.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/* 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 ZVISION_CURSOR_MANAGER_H
|
||||
#define ZVISION_CURSOR_MANAGER_H
|
||||
|
||||
#include "common/str.h"
|
||||
#include "zvision/graphics/cursors/cursor.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct PixelFormat;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
/**
|
||||
* Mostly usable cursors
|
||||
*/
|
||||
enum CursorIndex {
|
||||
CursorIndex_Active = 0,
|
||||
CursorIndex_DownArr = 3,
|
||||
CursorIndex_HandPu = 6,
|
||||
CursorIndex_Idle = 11,
|
||||
CursorIndex_Left = 12,
|
||||
CursorIndex_Right = 13,
|
||||
CursorIndex_UpArr = 17,
|
||||
CursorIndex_ItemIdle = 18,
|
||||
CursorIndex_ItemAct = 19
|
||||
};
|
||||
|
||||
/**
|
||||
* Class to manage cursor changes. The actual changes have to be done
|
||||
* through CursorMan. Otherwise the cursor will disappear after GMM
|
||||
* or debug console.
|
||||
* TODO: Figure out a way to get rid of the extraneous data copying due to having to use CursorMan
|
||||
*/
|
||||
class CursorManager {
|
||||
public:
|
||||
CursorManager(ZVision *engine, const Graphics::PixelFormat &pixelFormat);
|
||||
|
||||
private:
|
||||
static const int NUM_CURSORS = 18;
|
||||
|
||||
// 18 default cursors in up/down states, +2 for items idle/act cursors
|
||||
ZorkCursor _cursors[NUM_CURSORS + 2][2];
|
||||
|
||||
ZVision *_engine;
|
||||
const Graphics::PixelFormat _pixelFormat;
|
||||
bool _cursorIsPushed;
|
||||
int _item;
|
||||
int _lastitem;
|
||||
int _currentCursor;
|
||||
|
||||
static const char *_cursorNames[];
|
||||
static const char *_zgiCursorFileNames[];
|
||||
static const char *_zNemCursorFileNames[];
|
||||
|
||||
public:
|
||||
/** Creates the idle cursor and shows it */
|
||||
void initialize();
|
||||
|
||||
/**
|
||||
* Change cursor to specified cursor ID. If item setted to not 0 and cursor id idle/acrive/handpu change cursor to item.
|
||||
*
|
||||
* @param id Wanted cursor id.
|
||||
*/
|
||||
|
||||
void changeCursor(int id);
|
||||
|
||||
/**
|
||||
* Return founded id for string contains cursor name
|
||||
*
|
||||
* @param name Cursor name
|
||||
* @return Id of cursor or idle cursor id if not found
|
||||
*/
|
||||
|
||||
int getCursorId(const Common::String &name);
|
||||
|
||||
/**
|
||||
* Load cursor for item by id, and try to change cursor to item cursor if it's not 0
|
||||
*
|
||||
* @param id Item id or 0 for no item cursor
|
||||
*/
|
||||
|
||||
void setItemID(int id);
|
||||
|
||||
/**
|
||||
* Change the cursor to a certain push state. If the cursor is already in the specified push state, nothing will happen.
|
||||
*
|
||||
* @param pushed Should the cursor be pushed (true) or not pushed (false) (Another way to say it: down or up)
|
||||
*/
|
||||
void cursorDown(bool pushed);
|
||||
|
||||
/**
|
||||
* Show or hide mouse cursor.
|
||||
*
|
||||
* @param vis Should the cursor be showed (true) or hide (false)
|
||||
*/
|
||||
void showMouse(bool vis);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Calls CursorMan.replaceCursor() using the data in cursor
|
||||
*
|
||||
* @param cursor The cursor to show
|
||||
*/
|
||||
void changeCursor(const ZorkCursor &cursor);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
170
engines/zvision/graphics/effects/fog.cpp
Normal file
170
engines/zvision/graphics/effects/fog.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/graphics/effects/fog.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
FogFx::FogFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, const Common::Path &clouds):
|
||||
GraphicsEffect(engine, key, region, ported) {
|
||||
|
||||
_map = Map;
|
||||
|
||||
_r = 0;
|
||||
_g = 0;
|
||||
_b = 0;
|
||||
|
||||
_pos = 0;
|
||||
|
||||
if (SearchMan.hasFile(clouds))
|
||||
_engine->getRenderManager()->readImageToSurface(clouds, _fog);
|
||||
else
|
||||
_engine->getRenderManager()->readImageToSurface("cloud.tga", _fog);
|
||||
|
||||
_mp.resize(_fog.h);
|
||||
for (int16 i = 0; i < _fog.h; i++) {
|
||||
_mp[i].resize(_fog.w);
|
||||
for (int16 j = 0; j < _fog.w; j++)
|
||||
_mp[i][j] = true;
|
||||
}
|
||||
|
||||
for (uint8 i = 0; i < 32; i++)
|
||||
_colorMap[i] = 0;
|
||||
}
|
||||
|
||||
FogFx::~FogFx() {
|
||||
if (_map)
|
||||
delete _map;
|
||||
|
||||
for (uint16 i = 0; i < _mp.size(); i++)
|
||||
_mp[i].clear();
|
||||
_mp.clear();
|
||||
}
|
||||
|
||||
const Graphics::Surface *FogFx::draw(const Graphics::Surface &srcSubRect) {
|
||||
_surface.copyFrom(srcSubRect);
|
||||
EffectMap::iterator it = _map->begin();
|
||||
|
||||
uint32 cnt = 0;
|
||||
|
||||
for (uint16 j = 0; j < _surface.h; j++) {
|
||||
uint16 *lineBuf = (uint16 *)_surface.getBasePtr(0, j);
|
||||
|
||||
for (uint16 i = 0; i < _surface.w; i++) {
|
||||
if (it->inEffect) {
|
||||
// Not 100% equivalent, but looks nice and not buggy
|
||||
uint8 sr, sg, sb;
|
||||
_engine->_resourcePixelFormat.colorToRGB(lineBuf[i], sr, sg, sb);
|
||||
uint16 fogColor = *(uint16 *)_fog.getBasePtr((i + _pos) % _fog.w, j);
|
||||
uint8 dr, dg, db;
|
||||
_engine->_resourcePixelFormat.colorToRGB(_colorMap[fogColor & 0x1F], dr, dg, db);
|
||||
uint16 fr = dr + sr;
|
||||
if (fr > 255)
|
||||
fr = 255;
|
||||
uint16 fg = dg + sg;
|
||||
if (fg > 255)
|
||||
fg = 255;
|
||||
uint16 fb = db + sb;
|
||||
if (fb > 255)
|
||||
fb = 255;
|
||||
lineBuf[i] = _engine->_resourcePixelFormat.RGBToColor(fr, fg, fb);
|
||||
}
|
||||
cnt++;
|
||||
if (cnt >= it->count) {
|
||||
it++;
|
||||
cnt = 0;
|
||||
}
|
||||
if (it == _map->end())
|
||||
break;
|
||||
}
|
||||
if (it == _map->end())
|
||||
break;
|
||||
}
|
||||
|
||||
return &_surface;
|
||||
}
|
||||
|
||||
void FogFx::update() {
|
||||
_pos += _engine->getScriptManager()->getStateValue(StateKey_EF9_Speed);
|
||||
_pos %= _fog.w;
|
||||
debugC(2, kDebugEffect, "Updating fog effect");
|
||||
uint8 dr = _engine->getScriptManager()->getStateValue(StateKey_EF9_R);
|
||||
uint8 dg = _engine->getScriptManager()->getStateValue(StateKey_EF9_G);
|
||||
uint8 db = _engine->getScriptManager()->getStateValue(StateKey_EF9_B);
|
||||
dr = CLIP((int)dr, 0, 31);
|
||||
dg = CLIP((int)dg, 0, 31);
|
||||
db = CLIP((int)db, 0, 31);
|
||||
|
||||
if (dr != _r || dg != _g || db != _b) {
|
||||
if (_r > dr)
|
||||
_r--;
|
||||
else if (_r < dr)
|
||||
_r++;
|
||||
|
||||
if (_g > dg)
|
||||
_g--;
|
||||
else if (_g < dg)
|
||||
_g++;
|
||||
|
||||
if (_b > db)
|
||||
_b--;
|
||||
else if (_b < db)
|
||||
_b++;
|
||||
|
||||
// Not 100% equivalent, but looks nice and not buggy
|
||||
|
||||
_colorMap[31] = _engine->_resourcePixelFormat.RGBToColor(_r << 3, _g << 3, _b << 3);
|
||||
|
||||
for (uint8 i = 0; i < 31; i++) {
|
||||
float perc = (float)i / 31.0;
|
||||
uint8 cr = (uint8)((float)_r * perc);
|
||||
uint8 cg = (uint8)((float)_g * perc);
|
||||
uint8 cb = (uint8)((float)_b * perc);
|
||||
_colorMap[i] = _engine->_resourcePixelFormat.RGBToColor(cr << 3, cg << 3, cb << 3);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint16 j = 0; j < _fog.h; j++) {
|
||||
uint16 *pix = (uint16 *)_fog.getBasePtr(0, j);
|
||||
|
||||
for (uint16 i = 0; i < _fog.w; i++) {
|
||||
if (_mp[j][i]) {
|
||||
if ((pix[i] & 0x1F) == 0x1F) {
|
||||
pix[i]--;
|
||||
_mp[j][i] = false;
|
||||
} else
|
||||
pix[i]++;
|
||||
} else {
|
||||
if ((pix[i] & 0x1F) == 0) {
|
||||
pix[i]++;
|
||||
_mp[j][i] = true;
|
||||
} else
|
||||
pix[i]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
52
engines/zvision/graphics/effects/fog.h
Normal file
52
engines/zvision/graphics/effects/fog.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 ZVISION_FOG_H
|
||||
#define ZVISION_FOG_H
|
||||
|
||||
#include "zvision/graphics/graphics_effect.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
// Used by Zork: Nemesis for the mixing chamber gas effect in the gas puzzle (location tt5e, when the blinds are down)
|
||||
class FogFx : public GraphicsEffect {
|
||||
public:
|
||||
|
||||
FogFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, const Common::Path &clouds);
|
||||
~FogFx() override;
|
||||
|
||||
const Graphics::Surface *draw(const Graphics::Surface &srcSubRect) override;
|
||||
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
EffectMap *_map;
|
||||
Graphics::Surface _fog;
|
||||
uint8 _r, _g, _b;
|
||||
int32 _pos;
|
||||
Common::Array< Common::Array< bool > > _mp;
|
||||
uint16 _colorMap[32];
|
||||
};
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif // ZVISION_FOG_H
|
||||
106
engines/zvision/graphics/effects/light.cpp
Normal file
106
engines/zvision/graphics/effects/light.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 "common/scummsys.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/graphics/effects/light.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
LightFx::LightFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, int8 delta, int8 minD, int8 maxD):
|
||||
GraphicsEffect(engine, key, region, ported) {
|
||||
_map = Map;
|
||||
_delta = delta;
|
||||
_up = true;
|
||||
_pos = 0;
|
||||
|
||||
_minD = minD;
|
||||
if (_minD < -delta)
|
||||
_minD = -delta;
|
||||
|
||||
_maxD = maxD;
|
||||
if (_maxD > delta)
|
||||
_maxD = delta;
|
||||
}
|
||||
|
||||
LightFx::~LightFx() {
|
||||
if (_map)
|
||||
delete _map;
|
||||
}
|
||||
|
||||
const Graphics::Surface *LightFx::draw(const Graphics::Surface &srcSubRect) {
|
||||
_surface.copyFrom(srcSubRect);
|
||||
EffectMap::iterator it = _map->begin();
|
||||
uint32 cnt = 0;
|
||||
|
||||
uint32 dcolor = 0;
|
||||
|
||||
if (_pos < 0) {
|
||||
uint8 cc = ((-_pos) & 0x1F) << 3;
|
||||
dcolor = _engine->_resourcePixelFormat.RGBToColor(cc, cc, cc);
|
||||
} else {
|
||||
uint8 cc = (_pos & 0x1F) << 3;
|
||||
dcolor = _engine->_resourcePixelFormat.RGBToColor(cc, cc, cc);
|
||||
}
|
||||
|
||||
for (uint16 j = 0; j < _surface.h; j++) {
|
||||
uint16 *lineBuf = (uint16 *)_surface.getBasePtr(0, j);
|
||||
|
||||
for (uint16 i = 0; i < _surface.w; i++) {
|
||||
if (it->inEffect) {
|
||||
if (_pos < 0) {
|
||||
lineBuf[i] -= dcolor;
|
||||
} else {
|
||||
lineBuf[i] += dcolor;
|
||||
}
|
||||
}
|
||||
cnt++;
|
||||
if (cnt >= it->count) {
|
||||
it++;
|
||||
cnt = 0;
|
||||
}
|
||||
if (it == _map->end())
|
||||
break;
|
||||
}
|
||||
if (it == _map->end())
|
||||
break;
|
||||
}
|
||||
|
||||
return &_surface;
|
||||
}
|
||||
|
||||
void LightFx::update() {
|
||||
if (_up)
|
||||
_pos++;
|
||||
else
|
||||
_pos--;
|
||||
|
||||
if (_pos <= _minD) {
|
||||
_up = !_up;
|
||||
_pos = _minD;
|
||||
} else if (_pos >= _maxD) {
|
||||
_up = !_up;
|
||||
_pos = _maxD;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
52
engines/zvision/graphics/effects/light.h
Normal file
52
engines/zvision/graphics/effects/light.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 LIGHTFX_H_INCLUDED
|
||||
#define LIGHTFX_H_INCLUDED
|
||||
|
||||
#include "zvision/graphics/graphics_effect.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
class LightFx : public GraphicsEffect {
|
||||
public:
|
||||
|
||||
LightFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, int8 delta, int8 minD = -127, int8 maxD = 127);
|
||||
~LightFx() override;
|
||||
|
||||
const Graphics::Surface *draw(const Graphics::Surface &srcSubRect) override;
|
||||
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
EffectMap *_map;
|
||||
int32 _delta;
|
||||
bool _up;
|
||||
int32 _pos;
|
||||
|
||||
int8 _minD;
|
||||
int8 _maxD;
|
||||
};
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif // LIGHTFX_H_INCLUDED
|
||||
142
engines/zvision/graphics/effects/wave.cpp
Normal file
142
engines/zvision/graphics/effects/wave.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/graphics/effects/wave.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
WaveFx::WaveFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, int16 frames, int16 centerX, int16 centerY, float ampl, float waveln, float spd):
|
||||
GraphicsEffect(engine, key, region, ported) {
|
||||
|
||||
_frame = 0;
|
||||
_frameCount = frames;
|
||||
|
||||
_ampls.resize(_frameCount);
|
||||
_halfWidth = _region.width() / 2;
|
||||
_halfHeight = _region.height() / 2;
|
||||
|
||||
int32 frmsize = _halfWidth * _halfHeight;
|
||||
|
||||
float phase = 0;
|
||||
|
||||
int16 quarterWidth = _halfWidth / 2;
|
||||
int16 quarterHeight = _halfHeight / 2;
|
||||
|
||||
for (int16 i = 0; i < _frameCount; i++) {
|
||||
_ampls[i].resize(frmsize);
|
||||
|
||||
for (int16 y = 0; y < _halfHeight; y++)
|
||||
for (int16 x = 0; x < _halfWidth; x++) {
|
||||
int16 dx = (x - quarterWidth);
|
||||
int16 dy = (y - quarterHeight);
|
||||
|
||||
_ampls[i][x + y * _halfWidth] = (int8)(ampl * sin(sqrt(dx * dx / (float)centerX + dy * dy / (float)centerY) / (-waveln * 3.1415926) + phase));
|
||||
}
|
||||
phase += spd;
|
||||
}
|
||||
}
|
||||
|
||||
WaveFx::~WaveFx() {
|
||||
for (uint16 i = 0; i < _ampls.size(); i++)
|
||||
_ampls[i].clear();
|
||||
_ampls.clear();
|
||||
}
|
||||
|
||||
const Graphics::Surface *WaveFx::draw(const Graphics::Surface &srcSubRect) {
|
||||
for (int16 y = 0; y < _halfHeight; y++) {
|
||||
uint16 *abc = (uint16 *)_surface.getBasePtr(0, y);
|
||||
uint16 *abc2 = (uint16 *)_surface.getBasePtr(0, _halfHeight + y);
|
||||
uint16 *abc3 = (uint16 *)_surface.getBasePtr(_halfWidth, y);
|
||||
uint16 *abc4 = (uint16 *)_surface.getBasePtr(_halfWidth, _halfHeight + y);
|
||||
|
||||
for (int16 x = 0; x < _halfWidth; x++) {
|
||||
int8 amnt = _ampls[_frame][x + _halfWidth * y];
|
||||
|
||||
int16 nX = x + amnt;
|
||||
int16 nY = y + amnt;
|
||||
|
||||
if (nX < 0)
|
||||
nX = 0;
|
||||
if (nX >= _region.width())
|
||||
nX = _region.width() - 1;
|
||||
if (nY < 0)
|
||||
nY = 0;
|
||||
if (nY >= _region.height())
|
||||
nY = _region.height() - 1;
|
||||
*abc = *(const uint16 *)srcSubRect.getBasePtr(nX, nY);
|
||||
|
||||
nX = x + amnt + _halfWidth;
|
||||
nY = y + amnt;
|
||||
|
||||
if (nX < 0)
|
||||
nX = 0;
|
||||
if (nX >= _region.width())
|
||||
nX = _region.width() - 1;
|
||||
if (nY < 0)
|
||||
nY = 0;
|
||||
if (nY >= _region.height())
|
||||
nY = _region.height() - 1;
|
||||
*abc3 = *(const uint16 *)srcSubRect.getBasePtr(nX, nY);
|
||||
|
||||
nX = x + amnt;
|
||||
nY = y + amnt + _halfHeight;
|
||||
|
||||
if (nX < 0)
|
||||
nX = 0;
|
||||
if (nX >= _region.width())
|
||||
nX = _region.width() - 1;
|
||||
if (nY < 0)
|
||||
nY = 0;
|
||||
if (nY >= _region.height())
|
||||
nY = _region.height() - 1;
|
||||
*abc2 = *(const uint16 *)srcSubRect.getBasePtr(nX, nY);
|
||||
|
||||
nX = x + amnt + _halfWidth;
|
||||
nY = y + amnt + _halfHeight;
|
||||
|
||||
if (nX < 0)
|
||||
nX = 0;
|
||||
if (nX >= _region.width())
|
||||
nX = _region.width() - 1;
|
||||
if (nY < 0)
|
||||
nY = 0;
|
||||
if (nY >= _region.height())
|
||||
nY = _region.height() - 1;
|
||||
*abc4 = *(const uint16 *)srcSubRect.getBasePtr(nX, nY);
|
||||
|
||||
abc++;
|
||||
abc2++;
|
||||
abc3++;
|
||||
abc4++;
|
||||
}
|
||||
}
|
||||
|
||||
return &_surface;
|
||||
}
|
||||
|
||||
void WaveFx::update() {
|
||||
_frame = (_frame + 1) % _frameCount;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
50
engines/zvision/graphics/effects/wave.h
Normal file
50
engines/zvision/graphics/effects/wave.h
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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WAVEFX_H_INCLUDED
|
||||
#define WAVEFX_H_INCLUDED
|
||||
|
||||
#include "common/array.h"
|
||||
#include "zvision/graphics/graphics_effect.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
class WaveFx : public GraphicsEffect {
|
||||
public:
|
||||
|
||||
WaveFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, int16 frames, int16 centerX, int16 centerY, float ampl, float waveln, float spd);
|
||||
~WaveFx() override;
|
||||
|
||||
const Graphics::Surface *draw(const Graphics::Surface &srcSubRect) override;
|
||||
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
int16 _frame;
|
||||
int16 _frameCount;
|
||||
int16 _halfWidth, _halfHeight;
|
||||
Common::Array< Common::Array< int8 > > _ampls;
|
||||
};
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif // WAVEFX_H_INCLUDED
|
||||
84
engines/zvision/graphics/graphics_effect.h
Normal file
84
engines/zvision/graphics/graphics_effect.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 GRAPHICS_EFFECT_H_INCLUDED
|
||||
#define GRAPHICS_EFFECT_H_INCLUDED
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/rect.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "zvision/zvision.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
class GraphicsEffect {
|
||||
public:
|
||||
|
||||
GraphicsEffect(ZVision *engine, uint32 key, Common::Rect region, bool ported) : _engine(engine), _key(key), _region(region), _ported(ported) {
|
||||
_surface.create(_region.width(), _region.height(), _engine->_resourcePixelFormat);
|
||||
}
|
||||
virtual ~GraphicsEffect() {}
|
||||
|
||||
uint32 getKey() {
|
||||
return _key;
|
||||
}
|
||||
|
||||
Common::Rect getRegion() {
|
||||
return _region;
|
||||
}
|
||||
|
||||
// If true, effect is applied to the current background image prior to panoramic warping
|
||||
// If false, effect is applied to the effects buffer, which corresponds directly to the working window
|
||||
bool isPort() {
|
||||
return _ported;
|
||||
}
|
||||
|
||||
// Make a copy of supplied surface, draw effect on it, then return that altered surface
|
||||
virtual const Graphics::Surface *draw(const Graphics::Surface &srcSubRect) {
|
||||
return &_surface;
|
||||
}
|
||||
|
||||
virtual void update() {}
|
||||
|
||||
protected:
|
||||
ZVision *_engine;
|
||||
uint32 _key;
|
||||
Common::Rect _region;
|
||||
bool _ported;
|
||||
Graphics::Surface _surface;
|
||||
|
||||
// Static member functions
|
||||
public:
|
||||
|
||||
};
|
||||
|
||||
struct EffectMapUnit {
|
||||
uint32 count;
|
||||
bool inEffect;
|
||||
};
|
||||
|
||||
typedef Common::List<EffectMapUnit> EffectMap;
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif // GRAPHICS_EFFECT_H_INCLUDED
|
||||
1123
engines/zvision/graphics/render_manager.cpp
Normal file
1123
engines/zvision/graphics/render_manager.cpp
Normal file
File diff suppressed because it is too large
Load Diff
376
engines/zvision/graphics/render_manager.h
Normal file
376
engines/zvision/graphics/render_manager.h
Normal file
@@ -0,0 +1,376 @@
|
||||
/* 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 ZVISION_RENDER_MANAGER_H
|
||||
#define ZVISION_RENDER_MANAGER_H
|
||||
|
||||
#include "common/hashmap.h"
|
||||
#include "common/rect.h"
|
||||
#include "graphics/framelimiter.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
#include "graphics/screen.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "zvision/graphics/graphics_effect.h"
|
||||
#include "zvision/graphics/render_table.h"
|
||||
#include "zvision/text/truetype_font.h"
|
||||
|
||||
class OSystem;
|
||||
|
||||
namespace Common {
|
||||
class String;
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Video {
|
||||
class VideoDecoder;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class RenderManager {
|
||||
public:
|
||||
RenderManager(ZVision *engine, const ScreenLayout layout, const Graphics::PixelFormat pixelFormat, bool doubleFPS, bool widescreen = false);
|
||||
~RenderManager();
|
||||
|
||||
|
||||
typedef Common::List<GraphicsEffect *> EffectsList;
|
||||
|
||||
private:
|
||||
ZVision *_engine;
|
||||
OSystem *_system;
|
||||
const Graphics::PixelFormat _pixelFormat;
|
||||
const ScreenLayout _layout;
|
||||
bool _hiRes = false;
|
||||
Graphics::FrameLimiter _frameLimiter;
|
||||
|
||||
/**
|
||||
* A Rectangle representing the screen/window resolution.
|
||||
*/
|
||||
Common::Rect _screenArea;
|
||||
|
||||
Common::Rect _HDscreenArea = Common::Rect(800, 600);
|
||||
Common::Rect _HDscreenAreaWide = Common::Rect(720, 377);
|
||||
|
||||
Common::Point _textOffset; //Position vector of text area origin relative to working window origin
|
||||
|
||||
/**
|
||||
* A Rectangle placed inside _screenArea All in-game coordinates
|
||||
* are given in this coordinate space. Also, all images are clipped to the
|
||||
* edges of this Rectangle
|
||||
*/
|
||||
Common::Rect _workingArea;
|
||||
|
||||
Common::Point _workingAreaCenter; //Center of the working area in working area coordinates
|
||||
|
||||
/**
|
||||
Managed surface representing physical screen; dirty rectangles will be handled automatically by this from now on
|
||||
*/
|
||||
Graphics::Screen _screen;
|
||||
|
||||
/** A buffer for background image that's being used to create the background */
|
||||
Graphics::Surface _currentBackgroundImage;
|
||||
Common::Rect _backgroundDirtyRect;
|
||||
|
||||
/**
|
||||
* The x1 or y1 offset of the subRectangle of the background that is currently displayed on the screen
|
||||
* It will be x1 if PANORAMA, or y1 if TILT
|
||||
*/
|
||||
int16 _backgroundOffset;
|
||||
/** The width of the current background image */
|
||||
uint16 _backgroundWidth;
|
||||
/** The height of the current background image */
|
||||
uint16 _backgroundHeight;
|
||||
|
||||
// A buffer that holds the portion of the background that is used to render the final image
|
||||
// If it's a normal scene, the pixels will be blitted directly to the screen
|
||||
// If it's a panorma / tilt scene, the pixels will be first warped to _warpedSceneSurface
|
||||
Graphics::Surface _backgroundSurface;
|
||||
Graphics::ManagedSurface _workingManagedSurface;
|
||||
Common::Rect _backgroundSurfaceDirtyRect;
|
||||
|
||||
//TODO: Migrate this functionality to SubtitleManager to improve encapsulation
|
||||
//*
|
||||
// Buffer for drawing subtitles & other messages
|
||||
Graphics::Surface _textSurface;
|
||||
Graphics::ManagedSurface _textManagedSurface;
|
||||
Common::Rect _textSurfaceDirtyRect;
|
||||
//*/
|
||||
|
||||
// Rectangle for subtitles & other messages
|
||||
Common::Rect _textArea; //NB Screen coordinates
|
||||
Common::Rect _textLetterbox; //Section of text area outside working window, to be filled with black when blanked
|
||||
Common::Rect _textOverlay; //Section of text area to be filled with colorkey when blanked (may potentially intersect text letterbox area if screen/window is wider than working area!)
|
||||
|
||||
// Buffer for drawing menu
|
||||
Graphics::Surface _menuSurface;
|
||||
Graphics::ManagedSurface _menuManagedSurface;
|
||||
Common::Rect _menuSurfaceDirtyRect; //subrectangle of menu area outside working area
|
||||
|
||||
// Rectangle for menu area
|
||||
Common::Rect _menuArea; //Screen coordinates
|
||||
Common::Rect _menuLetterbox; //Section of menu area to be filled with black when blanked
|
||||
Common::Rect _menuOverlay; //Section of menu area to be filled with colorkey when blanked (may potentially intersect menu letterbox area if screen/window is wider than working area!)
|
||||
|
||||
//Buffer for streamed video playback
|
||||
//*
|
||||
Graphics::ManagedSurface _vidManagedSurface;
|
||||
/*/
|
||||
Graphics::Surface _vidSurface;
|
||||
//*/
|
||||
|
||||
//Area of streamed video playback
|
||||
Common::Rect _vidArea;
|
||||
|
||||
// A buffer used for apply graphics effects
|
||||
Graphics::Surface _effectSurface;
|
||||
|
||||
// A buffer to store the result of the panorama / tilt warps
|
||||
Graphics::Surface _warpedSceneSurface;
|
||||
|
||||
|
||||
/** Used to warp the background image */
|
||||
RenderTable _renderTable;
|
||||
|
||||
|
||||
|
||||
// Visual effects list
|
||||
EffectsList _effects;
|
||||
|
||||
//Pointer to currently active backbuffer output surface
|
||||
Graphics::Surface *_outputSurface;
|
||||
bool _doubleFPS;
|
||||
bool _widescreen;
|
||||
|
||||
public:
|
||||
void initialize(bool hiRes = false);
|
||||
|
||||
/**
|
||||
* Renders the scene to the screen
|
||||
* Returns true if screen was updated
|
||||
* If streamMode is set true, all background processing is skipped and the previous framebuffer is used
|
||||
*/
|
||||
bool renderSceneToScreen(bool immediate = false, bool overlayOnly = false, bool preStream = false);
|
||||
|
||||
Graphics::ManagedSurface &getVidSurface(Common::Rect dstRect); //dstRect is defined relative to working area origin
|
||||
|
||||
const Common::Rect &getMenuArea() const {
|
||||
return _menuArea;
|
||||
}
|
||||
const Common::Rect &getWorkingArea() const {
|
||||
return _workingArea;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blits the image or a portion of the image to the background.
|
||||
*
|
||||
* @param fileName Name of the image file
|
||||
* @param destinationX X position where the image should be put. Coords are in working window space, not screen space!
|
||||
* @param destinationY Y position where the image should be put. Coords are in working window space, not screen space!
|
||||
*/
|
||||
void renderImageToBackground(const Common::Path &fileName, int16 destinationX, int16 destinationY);
|
||||
|
||||
/**
|
||||
* Blits the image or a portion of the image to the background.
|
||||
*
|
||||
* @param fileName Name of the image file
|
||||
* @param destX X position where the image should be put. Coords are in working window space, not screen space!
|
||||
* @param destY Y position where the image should be put. Coords are in working window space, not screen space!
|
||||
* @param colorkey Transparent color
|
||||
*/
|
||||
void renderImageToBackground(const Common::Path &fileName, int16 destX, int16 destY, uint32 colorkey);
|
||||
|
||||
/**
|
||||
* Blits the image or a portion of the image to the background.
|
||||
*
|
||||
* @param fileName Name of the image file
|
||||
* @param destX X position where the image should be put. Coords are in working window space, not screen space!
|
||||
* @param destY Y position where the image should be put. Coords are in working window space, not screen space!
|
||||
* @param keyX X position of transparent color
|
||||
* @param keyY Y position of transparent color
|
||||
*/
|
||||
void renderImageToBackground(const Common::Path &fileName, int16 destX, int16 destY, int16 keyX, int16 keyY);
|
||||
|
||||
/**
|
||||
* Sets the current background image to be used by the RenderManager and immediately
|
||||
* blits it to the screen. (It won't show up until the end of the frame)
|
||||
*
|
||||
* @param fileName The name of the image file
|
||||
*/
|
||||
void setBackgroundImage(const Common::Path &fileName);
|
||||
|
||||
/**
|
||||
* Set the background position (_backgroundOffset). If the current RenderState is PANORAMA, the offset
|
||||
* will be in the horizontal direction. If the current RenderState is TILT, the offset will be in the
|
||||
* vertical direction.
|
||||
*
|
||||
* This method will not render anything on the screen. So if nothing else is called that renders the
|
||||
* background, the change won't be seen until next frame.
|
||||
*
|
||||
* @param offset The amount to offset the background
|
||||
*/
|
||||
void setBackgroundPosition(int offset);
|
||||
|
||||
/**
|
||||
* Converts a point in screen coordinate space to image coordinate space
|
||||
*
|
||||
* @param point Point in screen coordinate space
|
||||
* @return Point in image coordinate space
|
||||
*/
|
||||
const Common::Point screenSpaceToImageSpace(const Common::Point &point);
|
||||
|
||||
// Return pointer of RenderTable object
|
||||
RenderTable *getRenderTable();
|
||||
|
||||
// Return current background offset
|
||||
uint32 getCurrentBackgroundOffset();
|
||||
|
||||
/**
|
||||
* Creates a copy of surface and transposes the data.
|
||||
*
|
||||
* Note: The user is responsible for calling free() on the returned surface
|
||||
* and then deleting it
|
||||
*
|
||||
* @param surface The data to be transposed
|
||||
* @return A copy of the surface with the data transposed
|
||||
*/
|
||||
static Graphics::Surface *tranposeSurface(const Graphics::Surface *surface);
|
||||
|
||||
// Scale buffer (nearest)
|
||||
void scaleBuffer(const void *src, void *dst, uint32 srcWidth, uint32 srcHeight, byte bytesPerPixel, uint32 dstWidth, uint32 dstHeight);
|
||||
|
||||
/**
|
||||
* Blit from one surface to another surface
|
||||
*
|
||||
* @param src Source surface
|
||||
* @param srcRect Rectangle defining area of source surface to blit; if this rectangle is empty or not supplied, entire source surface is blitted
|
||||
* @param dst Destination surface
|
||||
* @param x Destination surface x coordinate
|
||||
* @param y Destination surface y coordinate
|
||||
*/
|
||||
|
||||
void blitSurfaceToSurface(const Graphics::Surface &src, Common::Rect srcRect, Graphics::Surface &dst, int _x, int _y);
|
||||
void blitSurfaceToSurface(const Graphics::Surface &src, Common::Rect srcRect, Graphics::Surface &dst, int _x, int _y, uint32 colorkey);
|
||||
void blitSurfaceToSurface(const Graphics::Surface &src, Graphics::Surface &dst, int _x, int _y) {blitSurfaceToSurface(src, Common::Rect(src.w, src.h), dst, _x, _y);}
|
||||
void blitSurfaceToSurface(const Graphics::Surface &src, Graphics::Surface &dst, int _x, int _y, uint32 colorkey) {blitSurfaceToSurface(src, Common::Rect(src.w, src.h), dst, _x, _y, colorkey);}
|
||||
|
||||
// Blitting surface-to-background methods
|
||||
void blitSurfaceToBkg(const Graphics::Surface &src, int x, int y, int32 colorkey = -1);
|
||||
|
||||
// Blitting surface-to-background methods with scale
|
||||
void blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &dstRect, int32 colorkey = -1);
|
||||
|
||||
/**
|
||||
* Blit from source surface to menu area
|
||||
*
|
||||
* @param src Source surface
|
||||
* @param x x coordinate relative to menu area origin
|
||||
* @param y y coordinate relative to menu area origin
|
||||
*/
|
||||
void blitSurfaceToMenu(const Graphics::Surface &src, int16 x, int16 y, int32 colorkey = 0);
|
||||
|
||||
/**
|
||||
* Blit from source surface to text area
|
||||
*
|
||||
* @param src Source surface
|
||||
* @param x x coordinate relative to text area origin
|
||||
* @param y y coordinate relative to text area origin
|
||||
*/
|
||||
void blitSurfaceToText(const Graphics::Surface &src, int16 x, int16 y, int32 colorkey = 0);
|
||||
|
||||
// Return background size
|
||||
Common::Point getBkgSize();
|
||||
|
||||
// Return portion of background as new surface
|
||||
Graphics::Surface *getBkgRect(Common::Rect &rect);
|
||||
|
||||
// Load image into new surface
|
||||
Graphics::Surface *loadImage(const Common::Path &file);
|
||||
Graphics::Surface *loadImage(const Common::Path &file, bool transposed);
|
||||
|
||||
// Clear whole/area of menu backbuffer
|
||||
void clearMenuSurface(bool force = false, int32 colorkey = -1);
|
||||
|
||||
// Clear whole/area of subtitle backbuffer
|
||||
void clearTextSurface(bool force = false, int32 colorkey = -1);
|
||||
|
||||
// Copy needed portion of background surface to workingArea surface
|
||||
void prepareBackground();
|
||||
|
||||
/**
|
||||
* Reads an image file pixel data into a Surface buffer. Also, if the image
|
||||
* is transposed, it will un-transpose the pixel data. The function will
|
||||
* call destination::create() if the dimensions of destination do not match
|
||||
* up with the dimensions of the image.
|
||||
*
|
||||
* @param fileName The name of a .tga file
|
||||
* @param destination A reference to the Surface to store the pixel data in
|
||||
*/
|
||||
void readImageToSurface(const Common::Path &fileName, Graphics::Surface &destination);
|
||||
|
||||
/**
|
||||
* Reads an image file pixel data into a Surface buffer. Also, if the image
|
||||
* is transposed, it will un-transpose the pixel data. The function will
|
||||
* call destination::create() if the dimensions of destination do not match
|
||||
* up with the dimensions of the image.
|
||||
*
|
||||
* @param fileName The name of a .tga file
|
||||
* @param destination A reference to the Surface to store the pixel data in
|
||||
* @param transposed Transpose flag
|
||||
*/
|
||||
void readImageToSurface(const Common::Path &fileName, Graphics::Surface &destination, bool transposed);
|
||||
|
||||
// Add visual effect to effects list
|
||||
void addEffect(GraphicsEffect *_effect);
|
||||
|
||||
// Delete effect(s) by ID (ID equal to slot of action:region that create this effect)
|
||||
void deleteEffect(uint32 ID);
|
||||
|
||||
// Create "mask" for effects - (color +/- depth) will be selected as not transparent. Like color selection
|
||||
// xy - base color
|
||||
// depth - +/- of base color
|
||||
// rect - rectangle where select pixels
|
||||
// minD - if not NULL will receive real bottom border of depth
|
||||
// maxD - if not NULL will receive real top border of depth
|
||||
EffectMap *makeEffectMap(const Common::Point &xy, int16 depth, const Common::Rect &rect, int8 *minD, int8 *maxD);
|
||||
|
||||
// Create "mask" for effects by simple transparent color
|
||||
EffectMap *makeEffectMap(const Graphics::Surface &surf, uint16 transp);
|
||||
|
||||
// Return background rectangle in screen coordinates
|
||||
Common::Rect transformBackgroundSpaceRectToScreenSpace(const Common::Rect &src);
|
||||
|
||||
// Mark whole background surface as dirty
|
||||
void markDirty();
|
||||
|
||||
/*
|
||||
// Fill background surface by color
|
||||
void bkgFill(uint8 r, uint8 g, uint8 b);
|
||||
*/
|
||||
|
||||
void checkBorders();
|
||||
void rotateTo(int16 to, int16 time);
|
||||
void updateRotation();
|
||||
void upscaleRect(Common::Rect &rect);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
345
engines/zvision/graphics/render_table.cpp
Normal file
345
engines/zvision/graphics/render_table.cpp
Normal file
@@ -0,0 +1,345 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/rect.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/system.h"
|
||||
#include "math/utils.h"
|
||||
#include "zvision/graphics/render_table.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
RenderTable::RenderTable(ZVision *engine, uint16 numColumns, uint16 numRows, const Graphics::PixelFormat &pixelFormat)
|
||||
: _engine(engine),
|
||||
_system(engine->_system),
|
||||
_numRows(numRows),
|
||||
_numColumns(numColumns),
|
||||
_renderState(FLAT),
|
||||
_pixelFormat(pixelFormat) {
|
||||
assert(numRows != 0 && numColumns != 0);
|
||||
|
||||
_internalBuffer = new FilterPixel[numRows * numColumns];
|
||||
|
||||
memset(&_panoramaOptions, 0, sizeof(_panoramaOptions));
|
||||
memset(&_tiltOptions, 0, sizeof(_tiltOptions));
|
||||
_halfRows = floor((_numRows - 1) / 2);
|
||||
_halfColumns = floor((_numColumns - 1) / 2);
|
||||
_halfWidth = (float)_numColumns / 2.0f - 0.5f;
|
||||
_halfHeight = (float)_numRows / 2.0f - 0.5f;
|
||||
}
|
||||
|
||||
RenderTable::~RenderTable() {
|
||||
delete[] _internalBuffer;
|
||||
}
|
||||
|
||||
void RenderTable::setRenderState(RenderState newState) {
|
||||
_renderState = newState;
|
||||
|
||||
switch (newState) {
|
||||
case PANORAMA:
|
||||
_panoramaOptions.verticalFOV = Math::deg2rad<float>(27.0f);
|
||||
_panoramaOptions.linearScale = 0.55f;
|
||||
_panoramaOptions.reverse = false;
|
||||
_panoramaOptions.zeroPoint = 0;
|
||||
break;
|
||||
case TILT:
|
||||
_tiltOptions.verticalFOV = Math::deg2rad<float>(27.0f);
|
||||
_tiltOptions.linearScale = 0.65f;
|
||||
_tiltOptions.reverse = false;
|
||||
break;
|
||||
case FLAT:
|
||||
// Intentionally left empty
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const Common::Point RenderTable::convertWarpedCoordToFlatCoord(const Common::Point &point) {
|
||||
// If we're outside the range of the RenderTable, no warping is happening. Return the maximum image coords
|
||||
if (point.x >= (int16)_numColumns || point.y >= (int16)_numRows || point.x < 0 || point.y < 0) {
|
||||
int16 x = CLIP<int16>(point.x, 0, (int16)_numColumns);
|
||||
int16 y = CLIP<int16>(point.y, 0, (int16)_numRows);
|
||||
return Common::Point(x, y);
|
||||
}
|
||||
|
||||
uint32 index = point.y * _numColumns + point.x;
|
||||
|
||||
Common::Point newPoint(point);
|
||||
newPoint.x += (_internalBuffer[index]._xDir ? _internalBuffer[index]._src.right : _internalBuffer[index]._src.left);
|
||||
newPoint.y += (_internalBuffer[index]._yDir ? _internalBuffer[index]._src.bottom : _internalBuffer[index]._src.top);
|
||||
|
||||
return newPoint;
|
||||
}
|
||||
|
||||
// Disused at present; potentially useful for future rendering efficient improvements.
|
||||
/*/
|
||||
void RenderTable::mutateImage(uint16 *sourceBuffer, uint16 *destBuffer, uint32 destWidth, const Common::Rect &subRect) {
|
||||
uint32 destOffset = 0;
|
||||
uint32 sourceXIndex = 0;
|
||||
uint32 sourceYIndex = 0;
|
||||
if(highQuality) {
|
||||
// TODO - convert to high quality pixel filtering
|
||||
for (int16 y = subRect.top; y < subRect.bottom; ++y) {
|
||||
uint32 sourceOffset = y * _numColumns;
|
||||
for (int16 x = subRect.left; x < subRect.right; ++x) {
|
||||
uint32 normalizedX = x - subRect.left;
|
||||
uint32 index = sourceOffset + x;
|
||||
// RenderTable only stores offsets from the original coordinates
|
||||
sourceYIndex = y + _internalBuffer[index]._src.top;
|
||||
sourceXIndex = x + _internalBuffer[index]._src.left;
|
||||
destBuffer[destOffset + normalizedX] = sourceBuffer[sourceYIndex * _numColumns + sourceXIndex];
|
||||
}
|
||||
destOffset += destWidth;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int16 y = subRect.top; y < subRect.bottom; ++y) {
|
||||
uint32 sourceOffset = y * _numColumns;
|
||||
for (int16 x = subRect.left; x < subRect.right; ++x) {
|
||||
uint32 normalizedX = x - subRect.left;
|
||||
uint32 index = sourceOffset + x;
|
||||
// RenderTable only stores offsets from the original coordinates
|
||||
sourceYIndex = y + _internalBuffer[index]._src.top;
|
||||
sourceXIndex = x + _internalBuffer[index]._src.left;
|
||||
destBuffer[destOffset + normalizedX] = sourceBuffer[sourceYIndex * _numColumns + sourceXIndex];
|
||||
}
|
||||
destOffset += destWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
// */
|
||||
|
||||
void RenderTable::mutateImage(Graphics::Surface *dstBuf, Graphics::Surface *srcBuf, bool highQuality) {
|
||||
uint32 destOffset = 0;
|
||||
uint32 sourceOffset = 0;
|
||||
uint16 *sourceBuffer = (uint16 *)srcBuf->getPixels();
|
||||
uint16 *destBuffer = (uint16 *)dstBuf->getPixels();
|
||||
if (highQuality != _highQuality) {
|
||||
_highQuality = highQuality;
|
||||
generateRenderTable();
|
||||
}
|
||||
uint32 mutationTime = _system->getMillis();
|
||||
if (_highQuality) {
|
||||
// Apply bilinear interpolation
|
||||
for (int16 y = 0; y < srcBuf->h; ++y) {
|
||||
sourceOffset = y * _numColumns;
|
||||
for (int16 x = 0; x < srcBuf->w; ++x) {
|
||||
const FilterPixel &curP = _internalBuffer[sourceOffset + x];
|
||||
const uint32 srcIndexYT = y + curP._src.top;
|
||||
const uint32 srcIndexYB = y + curP._src.bottom;
|
||||
const uint32 srcIndexXL = x + curP._src.left;
|
||||
const uint32 srcIndexXR = x + curP._src.right;
|
||||
uint32 rTL, rTR, rBL, rBR;
|
||||
uint32 gTL, gTR, gBL, gBR;
|
||||
uint32 bTL, bTR, bBL, bBR;
|
||||
splitColor(sourceBuffer[srcIndexYT * _numColumns + srcIndexXL], rTL, gTL, bTL);
|
||||
splitColor(sourceBuffer[srcIndexYT * _numColumns + srcIndexXR], rTR, gTR, bTR);
|
||||
splitColor(sourceBuffer[srcIndexYB * _numColumns + srcIndexXL], rBL, gBL, bBL);
|
||||
splitColor(sourceBuffer[srcIndexYB * _numColumns + srcIndexXR], rBR, gBR, bBR);
|
||||
const uint32 rF = curP._fTL * rTL + curP._fTR * rTR + curP._fBL * rBL + curP._fBR * rBR;
|
||||
const uint32 gF = curP._fTL * gTL + curP._fTR * gTR + curP._fBL * gBL + curP._fBR * gBR;
|
||||
const uint32 bF = curP._fTL * bTL + curP._fTR * bTR + curP._fBL * bBL + curP._fBR * bBR;
|
||||
destBuffer[destOffset] = mergeColor(rF, gF, bF);
|
||||
destOffset++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Apply nearest-neighbour interpolation
|
||||
for (int16 y = 0; y < srcBuf->h; ++y) {
|
||||
sourceOffset = y * _numColumns;
|
||||
for (int16 x = 0; x < srcBuf->w; ++x) {
|
||||
const uint32 index = sourceOffset + x;
|
||||
// RenderTable only stores offsets from the original coordinates
|
||||
const uint32 srcIndexX = x + (_internalBuffer[index]._xDir ? _internalBuffer[index]._src.right : _internalBuffer[index]._src.left);
|
||||
const uint32 srcIndexY = y + (_internalBuffer[index]._yDir ? _internalBuffer[index]._src.bottom : _internalBuffer[index]._src.top);
|
||||
destBuffer[destOffset] = sourceBuffer[srcIndexY * _numColumns + srcIndexX];
|
||||
destOffset++;
|
||||
}
|
||||
}
|
||||
}
|
||||
mutationTime = _system->getMillis() - mutationTime;
|
||||
debugC(5, kDebugGraphics, "\tPanorama mutation time %dms, %s quality", mutationTime, _highQuality ? "high" : "low");
|
||||
}
|
||||
|
||||
void RenderTable::generateRenderTable() {
|
||||
switch (_renderState) {
|
||||
case RenderTable::PANORAMA: {
|
||||
generateLookupTable(false);
|
||||
break;
|
||||
}
|
||||
case RenderTable::TILT:
|
||||
generateLookupTable(true);
|
||||
break;
|
||||
case RenderTable::FLAT:
|
||||
// Intentionally left empty
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderTable::generateLookupTable(bool tilt) {
|
||||
debugC(1, kDebugGraphics, "Generating %s lookup table.", tilt ? "tilt" : "panorama");
|
||||
debugC(5, kDebugGraphics, "_halfWidth %f, _halfHeight %f", _halfWidth, _halfHeight);
|
||||
debugC(5, kDebugGraphics, "_halfRows %d, _halfColumns %d", _halfRows, _halfColumns);
|
||||
uint32 generationTime = _system->getMillis();
|
||||
float cosAlpha, polarCoordInCylinderCoords, cylinderRadius, xOffset, yOffset;
|
||||
uint32 indexTL, indexBL, indexTR, indexBR;
|
||||
auto outerLoop = [&](uint & polarCoord, float & halfPolarSize, float & scale) {
|
||||
// polarCoord is the coordinate of the working window pixel parallel to the direction of camera rotation
|
||||
// halfPolarSize is the distance from the central axis to the outermost working window pixel in the direction of camera rotation
|
||||
// alpha represents the angle in the direction of camera rotation between the view axis and the centre of a pixel at the given polar coordinate
|
||||
const float alpha = atan(((float)polarCoord - halfPolarSize) / cylinderRadius);
|
||||
// To map the polar coordinate to the cylinder surface coordinates, we just need to calculate the arc length
|
||||
// We also scale it by linearScale
|
||||
polarCoordInCylinderCoords = (cylinderRadius * scale * alpha) + halfPolarSize;
|
||||
cosAlpha = cos(alpha);
|
||||
};
|
||||
auto innerLoop = [&](uint & polarCoord, uint & linearCoord, float & halfLinearSize, float & polarOffset, float & linearOffset) {
|
||||
// To calculate linear coordinate in cylinder coordinates, we can do similar triangles comparison,
|
||||
// comparing the triangle from the center to the screen and from the center to the edge of the cylinder
|
||||
const float linearCoordInCylinderCoords = halfLinearSize + ((float)linearCoord - halfLinearSize) * cosAlpha;
|
||||
linearOffset = linearCoordInCylinderCoords - linearCoord;
|
||||
polarOffset = polarCoordInCylinderCoords - polarCoord;
|
||||
_internalBuffer[indexTL] = FilterPixel(xOffset, yOffset, _highQuality);
|
||||
// Transformation is both horizontally and vertically symmetrical about the camera axis,
|
||||
// We can thus save on trigonometric calculations by computing one quarter of the transformation matrix and then mirroring it in both X & Y:
|
||||
_internalBuffer[indexBL] = _internalBuffer[indexTL];
|
||||
_internalBuffer[indexBL].flipV();
|
||||
_internalBuffer[indexTR] = _internalBuffer[indexTL];
|
||||
_internalBuffer[indexTR].flipH();
|
||||
_internalBuffer[indexBR] = _internalBuffer[indexBL];
|
||||
_internalBuffer[indexBR].flipH();
|
||||
};
|
||||
if (tilt) {
|
||||
cylinderRadius = (_halfWidth + 0.5f) / tan(_tiltOptions.verticalFOV);
|
||||
_tiltOptions.gap = cylinderRadius * atan2((float)(_halfHeight / cylinderRadius), 1.0f) * _tiltOptions.linearScale;
|
||||
for (uint y = 0; y <= _halfRows; ++y) {
|
||||
outerLoop(y, _halfHeight, _tiltOptions.linearScale);
|
||||
const uint32 columnIndexTL = y * _numColumns;
|
||||
const uint32 columnIndexBL = (_numRows - (y + 1)) * _numColumns;
|
||||
const uint32 columnIndexTR = columnIndexTL + (_numColumns - 1);
|
||||
const uint32 columnIndexBR = columnIndexBL + (_numColumns - 1);
|
||||
for (uint x = 0; x <= _halfColumns; ++x) {
|
||||
indexTL = columnIndexTL + x;
|
||||
indexBL = columnIndexBL + x;
|
||||
indexTR = columnIndexTR - x;
|
||||
indexBR = columnIndexBR - x;
|
||||
innerLoop(y, x, _halfWidth, yOffset, xOffset);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cylinderRadius = (_halfHeight + 0.5f) / tan(_panoramaOptions.verticalFOV);
|
||||
for (uint x = 0; x <= _halfColumns; ++x) {
|
||||
const uint32 columnIndexL = x;
|
||||
const uint32 columnIndexR = (_numColumns - 1) - x;
|
||||
uint32 rowIndexT = 0;
|
||||
uint32 rowIndexB = _numColumns * (_numRows - 1);
|
||||
outerLoop(x, _halfWidth, _panoramaOptions.linearScale);
|
||||
for (uint y = 0; y <= _halfRows; ++y) {
|
||||
indexTL = rowIndexT + columnIndexL;
|
||||
indexBL = rowIndexB + columnIndexL;
|
||||
indexTR = rowIndexT + columnIndexR;
|
||||
indexBR = rowIndexB + columnIndexR;
|
||||
innerLoop(x, y, _halfHeight, xOffset, yOffset);
|
||||
rowIndexT += _numColumns;
|
||||
rowIndexB -= _numColumns;
|
||||
}
|
||||
}
|
||||
}
|
||||
generationTime = _system->getMillis() - generationTime;
|
||||
debugC(1, kDebugGraphics, "Render table generated, %s quality", _highQuality ? "high" : "low");
|
||||
debugC(1, kDebugGraphics, "\tRender table generation time %dms", generationTime);
|
||||
}
|
||||
|
||||
void RenderTable::setPanoramaFoV(float fov) {
|
||||
assert(fov > 0.0f);
|
||||
|
||||
_panoramaOptions.verticalFOV = Math::deg2rad<float>(fov);
|
||||
}
|
||||
|
||||
void RenderTable::setPanoramaScale(float scale) {
|
||||
assert(scale > 0.0f);
|
||||
|
||||
_panoramaOptions.linearScale = scale;
|
||||
}
|
||||
|
||||
void RenderTable::setPanoramaReverse(bool reverse) {
|
||||
_panoramaOptions.reverse = reverse;
|
||||
}
|
||||
|
||||
bool RenderTable::getPanoramaReverse() {
|
||||
return _panoramaOptions.reverse;
|
||||
}
|
||||
|
||||
void RenderTable::setPanoramaZeroPoint(uint16 point) {
|
||||
_panoramaOptions.zeroPoint = point;
|
||||
}
|
||||
|
||||
uint16 RenderTable::getPanoramaZeroPoint() {
|
||||
return _panoramaOptions.zeroPoint;
|
||||
}
|
||||
|
||||
void RenderTable::setTiltFoV(float fov) {
|
||||
assert(fov > 0.0f);
|
||||
|
||||
_tiltOptions.verticalFOV = Math::deg2rad<float>(fov);
|
||||
}
|
||||
|
||||
void RenderTable::setTiltScale(float scale) {
|
||||
assert(scale > 0.0f);
|
||||
|
||||
_tiltOptions.linearScale = scale;
|
||||
}
|
||||
|
||||
void RenderTable::setTiltReverse(bool reverse) {
|
||||
_tiltOptions.reverse = reverse;
|
||||
}
|
||||
|
||||
float RenderTable::getTiltGap() {
|
||||
return _tiltOptions.gap;
|
||||
}
|
||||
|
||||
float RenderTable::getAngle() {
|
||||
switch (_renderState) {
|
||||
case TILT:
|
||||
return Math::rad2deg<float>(_tiltOptions.verticalFOV);
|
||||
case PANORAMA:
|
||||
return Math::rad2deg<float>(_panoramaOptions.verticalFOV);
|
||||
default:
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
float RenderTable::getLinscale() {
|
||||
switch (_renderState) {
|
||||
case TILT:
|
||||
return _tiltOptions.linearScale;
|
||||
case PANORAMA:
|
||||
return _panoramaOptions.linearScale;
|
||||
default:
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
169
engines/zvision/graphics/render_table.h
Normal file
169
engines/zvision/graphics/render_table.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_RENDER_TABLE_H
|
||||
#define ZVISION_RENDER_TABLE_H
|
||||
|
||||
#include "common/rect.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "zvision/zvision.h"
|
||||
|
||||
class OSystem;
|
||||
namespace ZVision {
|
||||
|
||||
class FilterPixel {
|
||||
public:
|
||||
// Bitfields representing sequential direction of contraction
|
||||
bool _xDir = false; // false left, true right
|
||||
bool _yDir = false; // false up, true down
|
||||
Common::Rect _src = Common::Rect(0, 0); // Coordinates of four panorama image pixels around actual working window pixel
|
||||
|
||||
float _fX, _fY, _fTL, _fTR, _fBL, _fBR;
|
||||
|
||||
FilterPixel() {}
|
||||
FilterPixel(float x, float y, bool highQuality = false) {
|
||||
_src.left = int16(floor(x));
|
||||
_src.right = int16(ceil(x));
|
||||
_src.top = int16(floor(y));
|
||||
_src.bottom = int16(ceil(y));
|
||||
if (highQuality) {
|
||||
_fX = x - (float)_src.left;
|
||||
_fY = y - (float)_src.top;
|
||||
_fTL = (1 - _fX) * (1 - _fY);
|
||||
_fTR = _fX * (1 - _fY);
|
||||
_fBL = (1 - _fX) * _fY;
|
||||
_fBR = _fX * _fY;
|
||||
} else {
|
||||
// Nearest neighbour
|
||||
_xDir = (x - _src.left) > 0.5f;
|
||||
_yDir = (y - _src.top) > 0.5f;
|
||||
}
|
||||
}
|
||||
~FilterPixel() {}
|
||||
inline void flipH() {
|
||||
_src.left = -_src.left;
|
||||
_src.right = -_src.right;
|
||||
}
|
||||
inline void flipV() {
|
||||
_src.top = -_src.top;
|
||||
_src.bottom = -_src.bottom;
|
||||
}
|
||||
};
|
||||
|
||||
class RenderTable {
|
||||
public:
|
||||
RenderTable(ZVision *engine, uint16 numRows, uint16 numColumns, const Graphics::PixelFormat &pixelFormat);
|
||||
~RenderTable();
|
||||
|
||||
// Common::Point testPixel = Common::Point(255,0);
|
||||
public:
|
||||
enum RenderState {
|
||||
PANORAMA,
|
||||
TILT,
|
||||
FLAT
|
||||
};
|
||||
|
||||
private:
|
||||
ZVision *_engine;
|
||||
OSystem *_system;
|
||||
uint16 _numRows, _numColumns, _halfRows, _halfColumns; // Working area width, height; half width, half height, in whole pixels
|
||||
float _halfWidth, _halfHeight; // Centre axis to midpoint of outermost pixel
|
||||
FilterPixel *_internalBuffer;
|
||||
RenderState _renderState;
|
||||
bool _highQuality = false;
|
||||
const Graphics::PixelFormat _pixelFormat;
|
||||
|
||||
inline void splitColor(uint16 &color, uint32 &r, uint32 &g, uint32 &b) const {
|
||||
// NB Left & right shifting unnecessary for interpolating & recombining, so not bothering in order to save cycles
|
||||
r = color & 0x001f;
|
||||
g = color & 0x03e0;
|
||||
b = color & 0x7c00;
|
||||
}
|
||||
inline uint16 mergeColor(const uint32 &r, const uint32 &g, const uint32 &b) const {
|
||||
// NB Red uses the lowest bits in RGB555 and so doesn't need its fractional bits masked away after averaging
|
||||
return r | (g & 0x03e0) | (b & 0x7c00);
|
||||
}
|
||||
|
||||
|
||||
struct {
|
||||
float verticalFOV; // Radians
|
||||
float linearScale;
|
||||
bool reverse;
|
||||
uint16 zeroPoint;
|
||||
} _panoramaOptions;
|
||||
|
||||
struct {
|
||||
float verticalFOV; // Radians
|
||||
float linearScale;
|
||||
bool reverse;
|
||||
float gap;
|
||||
} _tiltOptions;
|
||||
|
||||
public:
|
||||
RenderState getRenderState() {
|
||||
return _renderState;
|
||||
}
|
||||
void setRenderState(RenderState newState);
|
||||
|
||||
const Common::Point convertWarpedCoordToFlatCoord(const Common::Point &point); // input point in working area coordinates
|
||||
|
||||
// void mutateImage(uint16 *sourceBuffer, uint16 *destBuffer, uint32 destWidth, const Common::Rect &subRect);
|
||||
void mutateImage(Graphics::Surface *dstBuf, Graphics::Surface *srcBuf, bool filter = false);
|
||||
template <typename I>
|
||||
Common::String pixelToBinary(const I &pixel, bool splitColors = true) const {
|
||||
uint8 bits = sizeof(pixel) << 3;
|
||||
Common::String str("0b");
|
||||
I spaceMask = 0;
|
||||
for (uint8 i = 0; i < 3; i++)
|
||||
spaceMask = (spaceMask << 5) + 0x10;
|
||||
for (I mask = 0x01 << (bits - 1); mask; mask >>= 1) {
|
||||
if (splitColors && (spaceMask & mask))
|
||||
str += " ";
|
||||
str += mask & pixel ? "1" : "0";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
void generateRenderTable();
|
||||
|
||||
void setPanoramaFoV(float fov); // Degrees
|
||||
void setPanoramaScale(float scale);
|
||||
void setPanoramaReverse(bool reverse);
|
||||
void setPanoramaZeroPoint(uint16 point);
|
||||
uint16 getPanoramaZeroPoint();
|
||||
bool getPanoramaReverse();
|
||||
|
||||
void setTiltFoV(float fov); // Degrees
|
||||
void setTiltScale(float scale);
|
||||
void setTiltReverse(bool reverse);
|
||||
|
||||
float getTiltGap();
|
||||
float getAngle();
|
||||
float getLinscale();
|
||||
|
||||
private:
|
||||
void generateLookupTable(bool tilt = false);
|
||||
void generatePanoramaLookupTable();
|
||||
void generateTiltLookupTable();
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
418
engines/zvision/metaengine.cpp
Normal file
418
engines/zvision/metaengine.cpp
Normal file
@@ -0,0 +1,418 @@
|
||||
/* 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 "backends/keymapper/action.h"
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
#include "backends/keymapper/standard-actions.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/system.h"
|
||||
#include "common/translation.h"
|
||||
#include "engines/advancedDetector.h"
|
||||
#include "zvision/detection.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/file/save_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
static const ADExtraGuiOptionsMap optionsList[] = {
|
||||
|
||||
{
|
||||
GAMEOPTION_ORIGINAL_SAVELOAD,
|
||||
{
|
||||
_s("Use original save/load screens"),
|
||||
_s("Use the original save/load screens instead of the ScummVM ones"),
|
||||
"originalsaveload",
|
||||
false,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
GAMEOPTION_DOUBLE_FPS,
|
||||
{
|
||||
_s("Double FPS"),
|
||||
_s("Increase framerate from 30 to 60 FPS"),
|
||||
"doublefps",
|
||||
false,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
GAMEOPTION_ENABLE_VENUS,
|
||||
{
|
||||
_s("Enable Venus"),
|
||||
_s("Enable the Venus help system"),
|
||||
"venusenabled",
|
||||
true,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
GAMEOPTION_DISABLE_ANIM_WHILE_TURNING,
|
||||
{
|
||||
_s("Disable animation while turning"),
|
||||
_s("Disable animation while turning in panorama mode"),
|
||||
"noanimwhileturning",
|
||||
false,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
GAMEOPTION_USE_HIRES_MPEG_MOVIES,
|
||||
{
|
||||
_s("Use high resolution MPEG video"),
|
||||
_s("Use MPEG video from the DVD version instead of lower resolution AVI"),
|
||||
"mpegmovies",
|
||||
true,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
GAMEOPTION_ENABLE_WIDESCREEN,
|
||||
{
|
||||
_s("Enable widescreen support"),
|
||||
_s("Rearrange placement of menus & subtitles so as to make better use of modern wide aspect ratio displays"),
|
||||
"widescreen",
|
||||
true,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
GAMEOPTION_HQ_PANORAMA,
|
||||
{
|
||||
_s("Enable high quality panoramas"),
|
||||
_s("Apply bilinear filtering to panoramic backgrounds"),
|
||||
"highquality",
|
||||
true,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
AD_EXTRA_GUI_OPTIONS_TERMINATOR
|
||||
};
|
||||
|
||||
ZVisionGameId ZVision::getGameId() const {
|
||||
return _gameDescription->gameId;
|
||||
}
|
||||
Common::Language ZVision::getLanguage() const {
|
||||
return _gameDescription->desc.language;
|
||||
}
|
||||
uint32 ZVision::getFeatures() const {
|
||||
return _gameDescription->desc.flags;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
class ZVisionMetaEngine : public AdvancedMetaEngine<ZVision::ZVisionGameDescription> {
|
||||
public:
|
||||
const char *getName() const override {
|
||||
return "zvision";
|
||||
}
|
||||
|
||||
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
|
||||
return ZVision::optionsList;
|
||||
}
|
||||
|
||||
bool hasFeature(MetaEngineFeature f) const override;
|
||||
Common::KeymapArray initKeymaps(const char *target) const override;
|
||||
Common::Error createInstance(OSystem *syst, Engine **engine, const ZVision::ZVisionGameDescription *desc) const override;
|
||||
|
||||
SaveStateList listSaves(const char *target) const override;
|
||||
int getMaximumSaveSlot() const override;
|
||||
bool removeSaveState(const char *target, int slot) const override;
|
||||
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
|
||||
};
|
||||
|
||||
bool ZVisionMetaEngine::hasFeature(MetaEngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsListSaves) ||
|
||||
(f == kSupportsLoadingDuringStartup) ||
|
||||
(f == kSupportsDeleteSave) ||
|
||||
(f == kSavesSupportMetaInfo) ||
|
||||
(f == kSavesSupportThumbnail) ||
|
||||
(f == kSavesSupportCreationDate) ||
|
||||
(f == kSavesSupportPlayTime) ||
|
||||
(f == kSimpleSavesNames);
|
||||
}
|
||||
|
||||
bool ZVision::ZVision::hasFeature(EngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsReturnToLauncher) ||
|
||||
(f == kSupportsLoadingDuringRuntime) ||
|
||||
(f == kSupportsSavingDuringRuntime);
|
||||
}
|
||||
|
||||
Common::Error ZVision::ZVision::loadGameState(int slot) {
|
||||
return _saveManager->loadGame(slot);
|
||||
}
|
||||
|
||||
Common::Error ZVision::ZVision::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
|
||||
_saveManager->saveGame(slot, desc, false);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
bool ZVision::ZVision::canLoadGameStateCurrently(Common::U32String *msg) {
|
||||
return !_videoIsPlaying;
|
||||
}
|
||||
|
||||
bool ZVision::ZVision::canSaveGameStateCurrently(Common::U32String *msg) {
|
||||
Location currentLocation = _scriptManager->getCurrentLocation();
|
||||
return !_videoIsPlaying && currentLocation.world != 'g' && !(currentLocation.room == 'j' || currentLocation.room == 'a');
|
||||
}
|
||||
|
||||
Common::KeymapArray ZVisionMetaEngine::initKeymaps(const char *target) const {
|
||||
using namespace Common;
|
||||
using namespace ZVision;
|
||||
|
||||
Keymap *mainKeymap = new Keymap(Keymap::kKeymapTypeGame, mainKeymapId, "Z-Vision");
|
||||
|
||||
Action *act;
|
||||
|
||||
act = new Action(kStandardActionLeftClick, _("Left click"));
|
||||
act->setLeftClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_LEFT");
|
||||
act->addDefaultInputMapping("JOY_A");
|
||||
mainKeymap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionRightClick, _("Right click"));
|
||||
act->setRightClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_RIGHT");
|
||||
act->addDefaultInputMapping("JOY_B");
|
||||
mainKeymap->addAction(act);
|
||||
|
||||
Keymap *gameKeymap = new Keymap(Keymap::kKeymapTypeGame, gameKeymapId, "Z-Vision - Game");
|
||||
|
||||
act = new Action(kStandardActionMoveUp, _("Look up"));
|
||||
act->setCustomEngineActionEvent(kZVisionActionUp);
|
||||
act->addDefaultInputMapping("UP");
|
||||
act->addDefaultInputMapping("JOY_UP");
|
||||
gameKeymap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionMoveDown, _("Look down"));
|
||||
act->setCustomEngineActionEvent(kZVisionActionDown);
|
||||
act->addDefaultInputMapping("DOWN");
|
||||
act->addDefaultInputMapping("JOY_DOWN");
|
||||
gameKeymap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionMoveLeft, _("Turn left"));
|
||||
act->setCustomEngineActionEvent(kZVisionActionLeft);
|
||||
act->addDefaultInputMapping("LEFT");
|
||||
act->addDefaultInputMapping("JOY_LEFT");
|
||||
gameKeymap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionMoveRight, _("Turn right"));
|
||||
act->setCustomEngineActionEvent(kZVisionActionRight);
|
||||
act->addDefaultInputMapping("RIGHT");
|
||||
act->addDefaultInputMapping("JOY_RIGHT");
|
||||
gameKeymap->addAction(act);
|
||||
|
||||
act = new Action("FPS", _("Show FPS"));
|
||||
act->setCustomEngineActionEvent(kZVisionActionShowFPS);
|
||||
act->addDefaultInputMapping("F10");
|
||||
gameKeymap->addAction(act);
|
||||
|
||||
act = new Action("HELP", _("Help"));
|
||||
act->setKeyEvent(KEYCODE_F1);
|
||||
act->addDefaultInputMapping("F1");
|
||||
act->addDefaultInputMapping("JOY_LEFT_TRIGGER");
|
||||
gameKeymap->addAction(act);
|
||||
|
||||
act = new Action("INV", _("Inventory"));
|
||||
act->setKeyEvent(KEYCODE_F5);
|
||||
act->addDefaultInputMapping("F5");
|
||||
act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
|
||||
gameKeymap->addAction(act);
|
||||
|
||||
act = new Action("SPELL", _("Spellbook"));
|
||||
act->setKeyEvent(KEYCODE_F6);
|
||||
act->addDefaultInputMapping("F6");
|
||||
act->addDefaultInputMapping("JOY_RIGHT_SHOULDER");
|
||||
gameKeymap->addAction(act);
|
||||
|
||||
act = new Action("SCORE", _("Score"));
|
||||
act->setKeyEvent(KEYCODE_F7);
|
||||
act->addDefaultInputMapping("F7");
|
||||
act->addDefaultInputMapping("JOY_RIGHT_TRIGGER");
|
||||
gameKeymap->addAction(act);
|
||||
|
||||
act = new Action("AWAY", _("Put away object"));
|
||||
act->setKeyEvent(KEYCODE_F8);
|
||||
act->addDefaultInputMapping("F8");
|
||||
act->addDefaultInputMapping("JOY_X");
|
||||
gameKeymap->addAction(act);
|
||||
|
||||
act = new Action("COIN", _("Extract coin"));
|
||||
act->setKeyEvent(KEYCODE_F9);
|
||||
act->addDefaultInputMapping("F9");
|
||||
act->addDefaultInputMapping("JOY_Y");
|
||||
gameKeymap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionSave, _("Save"));
|
||||
act->setCustomEngineActionEvent(kZVisionActionSave);
|
||||
act->addDefaultInputMapping("C+s");
|
||||
gameKeymap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionLoad, _("Restore"));
|
||||
act->setCustomEngineActionEvent(kZVisionActionRestore);
|
||||
act->addDefaultInputMapping("C+r");
|
||||
gameKeymap->addAction(act);
|
||||
|
||||
act = new Action("QUIT", _("Quit"));
|
||||
act->setCustomEngineActionEvent(kZVisionActionQuit);
|
||||
act->addDefaultInputMapping("C+q");
|
||||
gameKeymap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionOpenSettings, _("Preferences"));
|
||||
act->setCustomEngineActionEvent(kZVisionActionPreferences);
|
||||
act->addDefaultInputMapping("C+p");
|
||||
gameKeymap->addAction(act);
|
||||
|
||||
Keymap *cutscenesKeymap = new Keymap(Keymap::kKeymapTypeGame, cutscenesKeymapId, "Z-Vision - Cutscenes");
|
||||
|
||||
act = new Action(kStandardActionSkip, _("Skip cutscene"));
|
||||
act->setCustomEngineActionEvent(kZVisionActionSkipCutscene);
|
||||
act->addDefaultInputMapping("SPACE");
|
||||
act->addDefaultInputMapping("JOY_Y");
|
||||
cutscenesKeymap->addAction(act);
|
||||
|
||||
act = new Action("QUIT", _("Quit"));
|
||||
act->setCustomEngineActionEvent(kZVisionActionQuit);
|
||||
act->addDefaultInputMapping("C+q");
|
||||
cutscenesKeymap->addAction(act);
|
||||
|
||||
KeymapArray keymaps(3);
|
||||
keymaps[0] = mainKeymap;
|
||||
keymaps[1] = gameKeymap;
|
||||
keymaps[2] = cutscenesKeymap;
|
||||
|
||||
return keymaps;
|
||||
}
|
||||
|
||||
Common::Error ZVisionMetaEngine::createInstance(OSystem *syst, Engine **engine, const ZVision::ZVisionGameDescription *desc) const {
|
||||
*engine = new ZVision::ZVision(syst,desc);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
SaveStateList ZVisionMetaEngine::listSaves(const char *target) const {
|
||||
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
|
||||
ZVision::SaveGameHeader header;
|
||||
Common::String pattern = target;
|
||||
pattern += ".###";
|
||||
|
||||
Common::StringArray filenames;
|
||||
filenames = saveFileMan->listSavefiles(pattern.c_str());
|
||||
|
||||
SaveStateList saveList;
|
||||
// We only use readSaveGameHeader() here, which doesn't need an engine callback
|
||||
ZVision::SaveManager *zvisionSaveMan = new ZVision::SaveManager(NULL);
|
||||
|
||||
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); file++) {
|
||||
// Obtain the last 3 digits of the filename, since they correspond to the save slot
|
||||
int slotNum = atoi(file->c_str() + file->size() - 3);
|
||||
|
||||
if (slotNum >= 0 && slotNum <= 999) {
|
||||
Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str());
|
||||
if (in) {
|
||||
if (zvisionSaveMan->readSaveGameHeader(in, header)) {
|
||||
saveList.push_back(SaveStateDescriptor(this, slotNum, header.saveName));
|
||||
}
|
||||
delete in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete zvisionSaveMan;
|
||||
|
||||
// Sort saves based on slot number.
|
||||
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
|
||||
return saveList;
|
||||
}
|
||||
|
||||
int ZVisionMetaEngine::getMaximumSaveSlot() const {
|
||||
return 999;
|
||||
}
|
||||
|
||||
bool ZVisionMetaEngine::removeSaveState(const char *target, int slot) const {
|
||||
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
|
||||
return saveFileMan->removeSavefile(Common::String::format("%s.%03u", target, slot));
|
||||
}
|
||||
|
||||
SaveStateDescriptor ZVisionMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
|
||||
Common::String filename = Common::String::format("%s.%03u", target, slot);
|
||||
Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename.c_str());
|
||||
|
||||
if (in) {
|
||||
ZVision::SaveGameHeader header;
|
||||
|
||||
// We only use readSaveGameHeader() here, which doesn't need an engine callback
|
||||
ZVision::SaveManager *zvisionSaveMan = new ZVision::SaveManager(NULL);
|
||||
bool successfulRead = zvisionSaveMan->readSaveGameHeader(in, header, false);
|
||||
delete zvisionSaveMan;
|
||||
delete in;
|
||||
|
||||
if (successfulRead) {
|
||||
SaveStateDescriptor desc(this, slot, header.saveName);
|
||||
|
||||
desc.setThumbnail(header.thumbnail);
|
||||
|
||||
if (header.version >= 1) {
|
||||
int day = header.saveDay;
|
||||
int month = header.saveMonth;
|
||||
int year = header.saveYear;
|
||||
|
||||
desc.setSaveDate(year, month, day);
|
||||
|
||||
int hour = header.saveHour;
|
||||
int minutes = header.saveMinutes;
|
||||
|
||||
desc.setSaveTime(hour, minutes);
|
||||
}
|
||||
|
||||
if (header.version >= 2) {
|
||||
desc.setPlayTime(header.playTime * 1000);
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
|
||||
return SaveStateDescriptor();
|
||||
}
|
||||
|
||||
#if PLUGIN_ENABLED_DYNAMIC(ZVISION)
|
||||
REGISTER_PLUGIN_DYNAMIC(ZVISION, PLUGIN_TYPE_ENGINE, ZVisionMetaEngine);
|
||||
#else
|
||||
REGISTER_PLUGIN_STATIC(ZVISION, PLUGIN_TYPE_ENGINE, ZVisionMetaEngine);
|
||||
#endif
|
||||
67
engines/zvision/module.mk
Normal file
67
engines/zvision/module.mk
Normal file
@@ -0,0 +1,67 @@
|
||||
MODULE := engines/zvision
|
||||
|
||||
MODULE_OBJS := \
|
||||
common/scroller.o \
|
||||
core/clock.o \
|
||||
core/console.o \
|
||||
core/events.o \
|
||||
file/lzss_read_stream.o \
|
||||
file/file_manager.o \
|
||||
file/save_manager.o \
|
||||
file/zfs_archive.o \
|
||||
graphics/cursors/cursor.o \
|
||||
graphics/cursors/cursor_manager.o \
|
||||
graphics/effects/fog.o \
|
||||
graphics/effects/light.o \
|
||||
graphics/effects/wave.o \
|
||||
graphics/render_manager.o \
|
||||
graphics/render_table.o \
|
||||
metaengine.o \
|
||||
scripting/actions.o \
|
||||
scripting/control.o \
|
||||
scripting/controls/fist_control.o \
|
||||
scripting/controls/hotmov_control.o \
|
||||
scripting/controls/input_control.o \
|
||||
scripting/controls/lever_control.o \
|
||||
scripting/controls/paint_control.o \
|
||||
scripting/controls/push_toggle_control.o \
|
||||
scripting/controls/safe_control.o \
|
||||
scripting/controls/save_control.o \
|
||||
scripting/controls/slot_control.o \
|
||||
scripting/controls/titler_control.o \
|
||||
scripting/effects/animation_effect.o \
|
||||
scripting/effects/distort_effect.o \
|
||||
scripting/effects/music_effect.o \
|
||||
scripting/effects/region_effect.o \
|
||||
scripting/effects/syncsound_effect.o \
|
||||
scripting/effects/timer_effect.o \
|
||||
scripting/effects/ttytext_effect.o \
|
||||
scripting/inventory.o \
|
||||
scripting/menu.o \
|
||||
scripting/scr_file_handling.o \
|
||||
scripting/script_manager.o \
|
||||
sound/midi.o \
|
||||
sound/volume_manager.o \
|
||||
sound/zork_raw.o \
|
||||
text/string_manager.o \
|
||||
text/subtitle_manager.o \
|
||||
text/text.o \
|
||||
text/truetype_font.o \
|
||||
video/rlf_decoder.o \
|
||||
video/video.o \
|
||||
video/zork_avi_decoder.o \
|
||||
zvision.o
|
||||
|
||||
MODULE_DIRS += \
|
||||
engines/zvision
|
||||
|
||||
# This module can be built as a plugin
|
||||
ifeq ($(ENABLE_ZVISION), DYNAMIC_PLUGIN)
|
||||
PLUGIN := 1
|
||||
endif
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
||||
|
||||
# Detection objects
|
||||
DETECT_OBJS += $(MODULE)/detection.o
|
||||
1271
engines/zvision/scripting/actions.cpp
Normal file
1271
engines/zvision/scripting/actions.cpp
Normal file
File diff suppressed because it is too large
Load Diff
446
engines/zvision/scripting/actions.h
Normal file
446
engines/zvision/scripting/actions.h
Normal file
@@ -0,0 +1,446 @@
|
||||
/* 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 ZVISION_ACTIONS_H
|
||||
#define ZVISION_ACTIONS_H
|
||||
|
||||
#include "common/path.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
// Forward declaration of ZVision. This file is included before ZVision is declared
|
||||
class ZVision;
|
||||
class ScriptManager;
|
||||
class ValueSlot;
|
||||
|
||||
/**
|
||||
* The base class that represents any action that a Puzzle can take.
|
||||
* This class is purely virtual.
|
||||
*/
|
||||
class ResultAction {
|
||||
public:
|
||||
ResultAction(ZVision *engine, int32 slotkey);
|
||||
virtual ~ResultAction() {}
|
||||
/**
|
||||
* This is called by the script system whenever a Puzzle's criteria are found to be true.
|
||||
* It should execute any necessary actions and return a value indicating whether the script
|
||||
* system should continue to test puzzles. In 99% of cases this will be 'true'.
|
||||
*
|
||||
* @param engine A pointer to the base engine so the ResultAction can access all the necessary methods
|
||||
* @return Should the script system continue to test any remaining puzzles (true) or immediately break and go on to the next frame (false)
|
||||
*/
|
||||
virtual bool execute() = 0;
|
||||
protected:
|
||||
ZVision *_engine;
|
||||
ScriptManager *_scriptManager;
|
||||
int32 _slotKey;
|
||||
};
|
||||
|
||||
class ActionAdd : public ResultAction {
|
||||
public:
|
||||
ActionAdd(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionAdd();
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _key;
|
||||
ValueSlot *_value = NULL;
|
||||
};
|
||||
|
||||
class ActionAssign : public ResultAction {
|
||||
public:
|
||||
ActionAssign(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionAssign() override;
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _key;
|
||||
ValueSlot *_value = NULL;
|
||||
};
|
||||
|
||||
class ActionAttenuate : public ResultAction {
|
||||
public:
|
||||
ActionAttenuate(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _key;
|
||||
int32 _attenuation;
|
||||
};
|
||||
|
||||
class ActionChangeLocation : public ResultAction {
|
||||
public:
|
||||
ActionChangeLocation(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
char _world;
|
||||
char _room;
|
||||
char _node;
|
||||
char _view;
|
||||
uint32 _offset;
|
||||
};
|
||||
|
||||
class ActionCrossfade : public ResultAction {
|
||||
public:
|
||||
ActionCrossfade(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _keyOne;
|
||||
uint32 _keyTwo;
|
||||
int32 _oneStartVolume;
|
||||
int32 _twoStartVolume;
|
||||
int32 _oneEndVolume;
|
||||
int32 _twoEndVolume;
|
||||
int32 _timeInMillis;
|
||||
};
|
||||
|
||||
class ActionCursor : public ResultAction {
|
||||
public:
|
||||
ActionCursor(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint8 _action;
|
||||
};
|
||||
|
||||
class ActionDelayRender : public ResultAction {
|
||||
public:
|
||||
ActionDelayRender(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _framesToDelay;
|
||||
};
|
||||
|
||||
class ActionDisableControl : public ResultAction {
|
||||
public:
|
||||
ActionDisableControl(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _key;
|
||||
};
|
||||
|
||||
class ActionDisplayMessage : public ResultAction {
|
||||
public:
|
||||
ActionDisplayMessage(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
int16 _control;
|
||||
int16 _msgid;
|
||||
};
|
||||
|
||||
class ActionDissolve : public ResultAction {
|
||||
public:
|
||||
ActionDissolve(ZVision *engine);
|
||||
bool execute() override;
|
||||
};
|
||||
|
||||
class ActionDistort : public ResultAction {
|
||||
public:
|
||||
ActionDistort(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionDistort() override;
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
int16 _distSlot;
|
||||
int16 _speed;
|
||||
float _startAngle;
|
||||
float _endAngle;
|
||||
float _startLineScale;
|
||||
float _endLineScale;
|
||||
};
|
||||
|
||||
class ActionEnableControl : public ResultAction {
|
||||
public:
|
||||
ActionEnableControl(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _key;
|
||||
};
|
||||
|
||||
class ActionFlushMouseEvents : public ResultAction {
|
||||
public:
|
||||
ActionFlushMouseEvents(ZVision *engine, int32 slotkey);
|
||||
bool execute() override;
|
||||
};
|
||||
|
||||
class ActionInventory : public ResultAction {
|
||||
public:
|
||||
ActionInventory(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
private:
|
||||
int8 _type;
|
||||
int32 _key;
|
||||
};
|
||||
|
||||
class ActionKill : public ResultAction {
|
||||
public:
|
||||
ActionKill(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _key;
|
||||
uint32 _type;
|
||||
};
|
||||
|
||||
class ActionMenuBarEnable : public ResultAction {
|
||||
public:
|
||||
ActionMenuBarEnable(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
private:
|
||||
uint16 _menus;
|
||||
};
|
||||
|
||||
class ActionMusic : public ResultAction {
|
||||
public:
|
||||
ActionMusic(ZVision *engine, int32 slotkey, const Common::String &line, bool global);
|
||||
~ActionMusic() override;
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
Common::Path _fileName;
|
||||
bool _loop;
|
||||
ValueSlot *_volume;
|
||||
bool _universe;
|
||||
bool _midi;
|
||||
int8 _note;
|
||||
int8 _prog;
|
||||
};
|
||||
|
||||
class ActionPanTrack : public ResultAction {
|
||||
public:
|
||||
ActionPanTrack(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionPanTrack() override;
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
int32 _pos; // Sound source position; NB in panoramas (all original game scripts), this is specified as the X background coordinate; otherwise it is specified in azimuth degrees.
|
||||
uint8 _mag; // Magnitude of effect (not used by original game scripts); 255 for fully directional sound, 0 for fully ambient
|
||||
bool _resetMusicNode; // If true (default, original game scripts have no concept of this), associated music slot value is reset to a value of 2 upon creation of this object.
|
||||
// This seems necessary to ensure all original game pan-track effects load correctly, though it is still unclear exactly what the original intent of these values was.
|
||||
// So far, best guess for music slotkey values is: 0 = has never been loaded, 1 = loaded and actively playing now, 2 = has loaded & played & then subsequently been killed.
|
||||
// Since there is literally nothing in the game scripts that sets some of these values to 2, and certain pan_tracks require it to be 2 for the puzzle that creates them to trigger, the original game engine code must have set these values to 2 manually somehow upon conditions being met to allow a pan_track to be created?
|
||||
bool _staticScreen; // Used by auxiliary scripts to apply directionality to audio in static screens; not used in original game scripts.
|
||||
bool _resetMixerOnDelete; // Unnecessary and should be set false for original scripts; useful in some cases in extra scripts to avoid brief volume spikes on location changes
|
||||
uint32 _musicSlot;
|
||||
};
|
||||
|
||||
class ActionPlayAnimation : public ResultAction {
|
||||
public:
|
||||
ActionPlayAnimation(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionPlayAnimation() override;
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
Common::Path _fileName;
|
||||
uint32 _x;
|
||||
uint32 _y;
|
||||
uint32 _x2;
|
||||
uint32 _y2;
|
||||
uint32 _start;
|
||||
uint32 _end;
|
||||
int32 _mask;
|
||||
int32 _framerate;
|
||||
int32 _loopCount;
|
||||
};
|
||||
|
||||
class ActionPlayPreloadAnimation : public ResultAction {
|
||||
public:
|
||||
ActionPlayPreloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _controlKey;
|
||||
uint32 _x1;
|
||||
uint32 _y1;
|
||||
uint32 _x2;
|
||||
uint32 _y2;
|
||||
uint _startFrame;
|
||||
uint _endFrame;
|
||||
uint _loopCount;
|
||||
};
|
||||
|
||||
class ActionPreloadAnimation : public ResultAction {
|
||||
public:
|
||||
ActionPreloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionPreloadAnimation() override;
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
Common::Path _fileName;
|
||||
int32 _mask;
|
||||
int32 _framerate;
|
||||
};
|
||||
|
||||
class ActionPreferences : public ResultAction {
|
||||
public:
|
||||
ActionPreferences(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
bool _save;
|
||||
};
|
||||
|
||||
class ActionQuit : public ResultAction {
|
||||
public:
|
||||
ActionQuit(ZVision *engine, int32 slotkey) : ResultAction(engine, slotkey) {}
|
||||
bool execute() override;
|
||||
};
|
||||
|
||||
class ActionRegion : public ResultAction {
|
||||
public:
|
||||
ActionRegion(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionRegion() override;
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
Common::String _art;
|
||||
Common::String _custom;
|
||||
Common::Rect _rect;
|
||||
uint16 _delay;
|
||||
uint16 _type;
|
||||
uint16 _unk1;
|
||||
uint16 _unk2;
|
||||
};
|
||||
|
||||
// Only used by ZGI (locations cd6e, cd6k, dg2f, dg4e, dv1j)
|
||||
class ActionUnloadAnimation : public ResultAction {
|
||||
public:
|
||||
ActionUnloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
private:
|
||||
uint32 _key;
|
||||
};
|
||||
|
||||
class ActionRandom : public ResultAction {
|
||||
public:
|
||||
ActionRandom(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionRandom() override;
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
ValueSlot *_max;
|
||||
};
|
||||
|
||||
class ActionRestoreGame : public ResultAction {
|
||||
public:
|
||||
ActionRestoreGame(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
Common::Path _fileName;
|
||||
};
|
||||
|
||||
class ActionRotateTo : public ResultAction {
|
||||
public:
|
||||
ActionRotateTo(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
int32 _toPos;
|
||||
int32 _time;
|
||||
};
|
||||
|
||||
class ActionSetPartialScreen : public ResultAction {
|
||||
public:
|
||||
ActionSetPartialScreen(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint _x;
|
||||
uint _y;
|
||||
Common::Path _fileName;
|
||||
int32 _backgroundColor;
|
||||
};
|
||||
|
||||
class ActionSetScreen : public ResultAction {
|
||||
public:
|
||||
ActionSetScreen(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
Common::Path _fileName;
|
||||
};
|
||||
|
||||
class ActionStop : public ResultAction {
|
||||
public:
|
||||
ActionStop(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _key;
|
||||
};
|
||||
|
||||
class ActionStreamVideo : public ResultAction {
|
||||
public:
|
||||
ActionStreamVideo(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
Common::Path _fileName;
|
||||
uint _x1;
|
||||
uint _y1;
|
||||
uint _x2;
|
||||
uint _y2;
|
||||
uint _flags;
|
||||
bool _skippable;
|
||||
};
|
||||
|
||||
class ActionSyncSound : public ResultAction {
|
||||
public:
|
||||
ActionSyncSound(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
int _syncto;
|
||||
Common::Path _fileName;
|
||||
};
|
||||
|
||||
class ActionTimer : public ResultAction {
|
||||
public:
|
||||
ActionTimer(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionTimer() override;
|
||||
bool execute() override;
|
||||
private:
|
||||
ValueSlot *_time;
|
||||
};
|
||||
|
||||
class ActionTtyText : public ResultAction {
|
||||
public:
|
||||
ActionTtyText(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionTtyText() override;
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
Common::Path _filename;
|
||||
uint32 _delay;
|
||||
Common::Rect _r;
|
||||
};
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
140
engines/zvision/scripting/control.cpp
Normal file
140
engines/zvision/scripting/control.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/detection.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
void Control::parseFlatControl(ZVision *engine) {
|
||||
debugC(1, kDebugGraphics, "Setting render state to FLAT");
|
||||
engine->getRenderManager()->getRenderTable()->setRenderState(RenderTable::FLAT);
|
||||
}
|
||||
|
||||
void Control::parsePanoramaControl(ZVision *engine, Common::SeekableReadStream &stream) {
|
||||
debugC(1, kDebugGraphics, "Setting render state to PANORAMA");
|
||||
RenderTable *renderTable = engine->getRenderManager()->getRenderTable();
|
||||
renderTable->setRenderState(RenderTable::PANORAMA);
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (line.matchString("angle*", true)) {
|
||||
float fov;
|
||||
if (sscanf(line.c_str(), "angle(%f)", &fov) == 1)
|
||||
renderTable->setPanoramaFoV(fov);
|
||||
} else if (line.matchString("linscale*", true)) {
|
||||
float scale;
|
||||
if (sscanf(line.c_str(), "linscale(%f)", &scale) == 1)
|
||||
renderTable->setPanoramaScale(scale);
|
||||
} else if (line.matchString("reversepana*", true)) {
|
||||
uint reverse = 0;
|
||||
sscanf(line.c_str(), "reversepana(%u)", &reverse);
|
||||
if (reverse == 1) {
|
||||
renderTable->setPanoramaReverse(true);
|
||||
}
|
||||
} else if (line.matchString("zeropoint*", true)) {
|
||||
uint point;
|
||||
if (sscanf(line.c_str(), "zeropoint(%u)", &point) == 1)
|
||||
renderTable->setPanoramaZeroPoint(point);
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
}
|
||||
|
||||
renderTable->generateRenderTable();
|
||||
}
|
||||
|
||||
// Only used in Zork Nemesis, handles tilt controls (ZGI doesn't have a tilt view)
|
||||
void Control::parseTiltControl(ZVision *engine, Common::SeekableReadStream &stream) {
|
||||
debugC(1, kDebugGraphics, "Setting render state to TILT");
|
||||
RenderTable *renderTable = engine->getRenderManager()->getRenderTable();
|
||||
renderTable->setRenderState(RenderTable::TILT);
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (line.matchString("angle*", true)) {
|
||||
float fov;
|
||||
if (sscanf(line.c_str(), "angle(%f)", &fov) == 1)
|
||||
renderTable->setTiltFoV(fov);
|
||||
} else if (line.matchString("linscale*", true)) {
|
||||
float scale;
|
||||
if (sscanf(line.c_str(), "linscale(%f)", &scale) == 1)
|
||||
renderTable->setTiltScale(scale);
|
||||
} else if (line.matchString("reversepana*", true)) {
|
||||
uint reverse = 0;
|
||||
sscanf(line.c_str(), "reversepana(%u)", &reverse);
|
||||
if (reverse == 1) {
|
||||
renderTable->setTiltReverse(true);
|
||||
}
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
}
|
||||
|
||||
renderTable->generateRenderTable();
|
||||
}
|
||||
|
||||
void Control::getParams(const Common::String &inputStr, Common::String ¶meter, Common::String &values) {
|
||||
const char *chrs = inputStr.c_str();
|
||||
uint lbr;
|
||||
|
||||
for (lbr = 0; lbr < inputStr.size(); lbr++)
|
||||
if (chrs[lbr] == '(')
|
||||
break;
|
||||
|
||||
if (lbr >= inputStr.size())
|
||||
return;
|
||||
|
||||
uint rbr;
|
||||
|
||||
for (rbr = lbr + 1; rbr < inputStr.size(); rbr++)
|
||||
if (chrs[rbr] == ')')
|
||||
break;
|
||||
|
||||
if (rbr >= inputStr.size())
|
||||
return;
|
||||
|
||||
parameter = Common::String(chrs, chrs + lbr);
|
||||
values = Common::String(chrs + lbr + 1, chrs + rbr);
|
||||
}
|
||||
|
||||
void Control::setVenus() {
|
||||
if (_venusId >= 0)
|
||||
if (_engine->getScriptManager()->getStateValue(_venusId) > 0)
|
||||
_engine->getScriptManager()->setStateValue(StateKey_Venus, _venusId);
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
146
engines/zvision/scripting/control.h
Normal file
146
engines/zvision/scripting/control.h
Normal file
@@ -0,0 +1,146 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_CONTROL_H
|
||||
#define ZVISION_CONTROL_H
|
||||
|
||||
#include "common/keyboard.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
struct Point;
|
||||
class WriteStream;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
/**
|
||||
* The base class for all Controls.
|
||||
*
|
||||
* Controls are the things that the user interacts with. Ex: A lever on the door
|
||||
*/
|
||||
class Control {
|
||||
public:
|
||||
|
||||
enum ControlType {
|
||||
CONTROL_UNKNOW,
|
||||
CONTROL_INPUT,
|
||||
CONTROL_PUSHTGL,
|
||||
CONTROL_SLOT,
|
||||
CONTROL_LEVER,
|
||||
CONTROL_SAVE,
|
||||
CONTROL_SAFE,
|
||||
CONTROL_FIST,
|
||||
CONTROL_TITLER,
|
||||
CONTROL_HOTMOV,
|
||||
CONTROL_PAINT
|
||||
};
|
||||
|
||||
Control(ZVision *engine, uint32 key, ControlType type) : _engine(engine), _key(key), _type(type), _venusId(-1) {}
|
||||
virtual ~Control() {}
|
||||
|
||||
uint32 getKey() {
|
||||
return _key;
|
||||
}
|
||||
|
||||
ControlType getType() {
|
||||
return _type;
|
||||
}
|
||||
|
||||
virtual void focus() {}
|
||||
virtual void unfocus() {}
|
||||
/**
|
||||
* Called when LeftMouse is pushed. Default is NOP.
|
||||
*
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
*/
|
||||
virtual bool onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Called when LeftMouse is lifted. Default is NOP.
|
||||
*
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
*/
|
||||
virtual bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Called on every MouseMove. Default is NOP.
|
||||
*
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
* @return Was the cursor changed?
|
||||
*/
|
||||
virtual bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Called when a key is pressed. Default is NOP.
|
||||
*
|
||||
* @param keycode The key that was pressed
|
||||
*/
|
||||
virtual bool onKeyDown(Common::KeyState keyState) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Called when a key is released. Default is NOP.
|
||||
*
|
||||
* @param keycode The key that was pressed
|
||||
*/
|
||||
virtual bool onKeyUp(Common::KeyState keyState) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Processes the node given the deltaTime since last frame. Default is NOP.
|
||||
*
|
||||
* @param deltaTimeInMillis The number of milliseconds that have passed since last frame
|
||||
* @return If true, the node can be deleted after process() finishes
|
||||
*/
|
||||
virtual bool process(uint32 deltaTimeInMillis) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void setVenus();
|
||||
|
||||
protected:
|
||||
ZVision *_engine;
|
||||
uint32 _key;
|
||||
int32 _venusId;
|
||||
|
||||
void getParams(const Common::String &inputStr, Common::String ¶meter, Common::String &values);
|
||||
// Static member functions
|
||||
public:
|
||||
static void parseFlatControl(ZVision *engine);
|
||||
static void parsePanoramaControl(ZVision *engine, Common::SeekableReadStream &stream);
|
||||
static void parseTiltControl(ZVision *engine, Common::SeekableReadStream &stream);
|
||||
private:
|
||||
ControlType _type;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
304
engines/zvision/scripting/controls/fist_control.cpp
Normal file
304
engines/zvision/scripting/controls/fist_control.cpp
Normal file
@@ -0,0 +1,304 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "video/video_decoder.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/fist_control.h"
|
||||
#include "zvision/video/rlf_decoder.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
FistControl::FistControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_FIST) {
|
||||
_cursor = CursorIndex_Idle;
|
||||
_animation = NULL;
|
||||
_soundKey = 0;
|
||||
_fiststatus = 0;
|
||||
_order = 0;
|
||||
_fistnum = 0;
|
||||
|
||||
_animationId = 0;
|
||||
|
||||
clearFistArray(_fistsUp);
|
||||
clearFistArray(_fistsDwn);
|
||||
|
||||
_numEntries = 0;
|
||||
_entries.clear();
|
||||
|
||||
_anmRect = Common::Rect();
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("sound_key", true)) {
|
||||
_soundKey = atoi(values.c_str());
|
||||
} else if (param.matchString("cursor", true)) {
|
||||
_cursor = _engine->getCursorManager()->getCursorId(values);
|
||||
} else if (param.matchString("descfile", true)) {
|
||||
readDescFile(Common::Path(values));
|
||||
} else if (param.matchString("animation_id", true)) {
|
||||
_animationId = atoi(values.c_str());
|
||||
} else if (param.matchString("venus_id", true)) {
|
||||
_venusId = atoi(values.c_str());
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
}
|
||||
|
||||
FistControl::~FistControl() {
|
||||
if (_animation)
|
||||
delete _animation;
|
||||
|
||||
clearFistArray(_fistsUp);
|
||||
clearFistArray(_fistsDwn);
|
||||
_entries.clear();
|
||||
}
|
||||
|
||||
bool FistControl::process(uint32 deltaTimeInMillis) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_animation && _animation->isPlaying()) {
|
||||
if (_animation->endOfVideo()) {
|
||||
_animation->stop();
|
||||
_engine->getScriptManager()->setStateValue(_animationId, 2);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_animation->needsUpdate()) {
|
||||
const Graphics::Surface *frameData = _animation->decodeNextFrame();
|
||||
if (frameData)
|
||||
// WORKAROUND: Ignore the target frame dimensions for the finger animations.
|
||||
// The target dimensions specify an area smaller than expected, thus if we
|
||||
// scale the finger videos to fit these dimensions, they are not aligned
|
||||
// correctly. Not scaling these videos yields a result identical to the
|
||||
// original. Fixes bug #6784.
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(*frameData, _anmRect.left, _anmRect.top);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FistControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (mouseIn(screenSpacePos, backgroundImageSpacePos) >= 0) {
|
||||
_engine->getCursorManager()->changeCursor(_cursor);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FistControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
int fistNumber = mouseIn(screenSpacePos, backgroundImageSpacePos);
|
||||
|
||||
if (fistNumber >= 0) {
|
||||
setVenus();
|
||||
|
||||
uint32 oldStatus = _fiststatus;
|
||||
_fiststatus ^= (1 << fistNumber);
|
||||
|
||||
for (int i = 0; i < _numEntries; i++)
|
||||
if (_entries[i]._bitsStrt == oldStatus && _entries[i]._bitsEnd == _fiststatus) {
|
||||
if (_animation) {
|
||||
_animation->stop();
|
||||
_animation->seekToFrame(_entries[i]._anmStrt);
|
||||
_animation->setEndFrame(_entries[i]._anmEnd);
|
||||
_animation->start();
|
||||
}
|
||||
|
||||
_engine->getScriptManager()->setStateValue(_animationId, 1);
|
||||
_engine->getScriptManager()->setStateValue(_soundKey, _entries[i]._sound);
|
||||
break;
|
||||
}
|
||||
|
||||
_engine->getScriptManager()->setStateValue(_key, _fiststatus);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FistControl::readDescFile(const Common::Path &fileName) {
|
||||
Common::File file;
|
||||
if (!file.open(fileName)) {
|
||||
warning("Desc file %s could could be opened", fileName.toString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Common::String line;
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
|
||||
while (!file.eos()) {
|
||||
line = file.readLine();
|
||||
getFistParams(line, param, values);
|
||||
|
||||
if (param.matchString("animation_id", true)) {
|
||||
// Not used
|
||||
} else if (param.matchString("animation", true)) {
|
||||
_animation = _engine->loadAnimation(Common::Path(values));
|
||||
} else if (param.matchString("anim_rect", true)) {
|
||||
int left, top, right, bottom;
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &left, &top, &right, &bottom) == 4)
|
||||
_anmRect = Common::Rect(left, top, right, bottom);
|
||||
} else if (param.matchString("num_fingers", true)) {
|
||||
if (sscanf(values.c_str(), "%d", &_fistnum) == 1) {
|
||||
_fistsUp.resize(_fistnum);
|
||||
_fistsDwn.resize(_fistnum);
|
||||
}
|
||||
} else if (param.matchString("entries", true)) {
|
||||
if (sscanf(values.c_str(), "%d", &_numEntries) == 1)
|
||||
_entries.resize(_numEntries);
|
||||
} else if (param.matchString("eval_order_ascending", true)) {
|
||||
sscanf(values.c_str(), "%d", &_order);
|
||||
} else if (param.matchString("up_hs_num_*", true)) {
|
||||
int fist, num;
|
||||
num = atoi(values.c_str());
|
||||
|
||||
if (sscanf(param.c_str(), "up_hs_num_%d", &fist) == 1)
|
||||
_fistsUp[fist].resize(num);
|
||||
} else if (param.matchString("up_hs_*", true)) {
|
||||
int16 fist, box, x1, y1, x2, y2;
|
||||
if (sscanf(param.c_str(), "up_hs_%hd_%hd", &fist, &box) == 2) {
|
||||
if (sscanf(values.c_str(), "%hd %hd %hd %hd", &x1, &y1, &x2, &y2) == 4)
|
||||
(_fistsUp[fist])[box] = Common::Rect(x1, y1, x2, y2);
|
||||
}
|
||||
} else if (param.matchString("down_hs_num_*", true)) {
|
||||
int fist, num;
|
||||
num = atoi(values.c_str());
|
||||
|
||||
if (sscanf(param.c_str(), "down_hs_num_%d", &fist) == 1)
|
||||
_fistsDwn[fist].resize(num);
|
||||
} else if (param.matchString("down_hs_*", true)) {
|
||||
int16 fist, box, x1, y1, x2, y2;
|
||||
if (sscanf(param.c_str(), "down_hs_%hd_%hd", &fist, &box) == 2) {
|
||||
if (sscanf(values.c_str(), "%hd %hd %hd %hd", &x1, &y1, &x2, &y2) == 4)
|
||||
(_fistsDwn[fist])[box] = Common::Rect(x1, y1, x2, y2);
|
||||
}
|
||||
} else {
|
||||
int entry, start, end, sound;
|
||||
char bitsStart[33];
|
||||
char bitsEnd[33];
|
||||
entry = atoi(param.c_str());
|
||||
if (sscanf(values.c_str(), "%s %s %d %d (%d)", bitsStart, bitsEnd, &start, &end, &sound) == 5) {
|
||||
_entries[entry]._bitsStrt = readBits(bitsStart);
|
||||
_entries[entry]._bitsEnd = readBits(bitsEnd);
|
||||
_entries[entry]._anmStrt = start;
|
||||
_entries[entry]._anmEnd = end;
|
||||
_entries[entry]._sound = sound;
|
||||
}
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
void FistControl::clearFistArray(Common::Array< Common::Array<Common::Rect> > &arr) {
|
||||
for (uint i = 0; i < arr.size(); i++)
|
||||
arr[i].clear();
|
||||
|
||||
arr.clear();
|
||||
}
|
||||
|
||||
uint32 FistControl::readBits(const char *str) {
|
||||
uint32 bfield = 0;
|
||||
int len = strlen(str);
|
||||
for (int i = 0; i < len; i++)
|
||||
if (str[i] != '0')
|
||||
bfield |= (1 << i);
|
||||
return bfield;
|
||||
}
|
||||
|
||||
int FistControl::mouseIn(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_order) {
|
||||
for (int i = 0; i < _fistnum; i++) {
|
||||
if (((_fiststatus >> i) & 1) == 1) {
|
||||
for (uint j = 0; j < _fistsDwn[i].size(); j++)
|
||||
if ((_fistsDwn[i])[j].contains(backgroundImageSpacePos))
|
||||
return i;
|
||||
} else {
|
||||
for (uint j = 0; j < _fistsUp[i].size(); j++)
|
||||
if ((_fistsUp[i])[j].contains(backgroundImageSpacePos))
|
||||
return i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = _fistnum - 1; i >= 0; i--) {
|
||||
if (((_fiststatus >> i) & 1) == 1) {
|
||||
for (uint j = 0; j < _fistsDwn[i].size(); j++)
|
||||
if ((_fistsDwn[i])[j].contains(backgroundImageSpacePos))
|
||||
return i;
|
||||
} else {
|
||||
for (uint j = 0; j < _fistsUp[i].size(); j++)
|
||||
if ((_fistsUp[i])[j].contains(backgroundImageSpacePos))
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void FistControl::getFistParams(const Common::String &inputStr, Common::String ¶meter, Common::String &values) {
|
||||
const char *chrs = inputStr.c_str();
|
||||
uint lbr;
|
||||
|
||||
for (lbr = 0; lbr < inputStr.size(); lbr++)
|
||||
if (chrs[lbr] == ':')
|
||||
break;
|
||||
|
||||
if (lbr >= inputStr.size())
|
||||
return;
|
||||
|
||||
uint rbr;
|
||||
|
||||
for (rbr = lbr + 1; rbr < inputStr.size(); rbr++)
|
||||
if (chrs[rbr] == '~')
|
||||
break;
|
||||
|
||||
if (rbr >= inputStr.size())
|
||||
return;
|
||||
|
||||
parameter = Common::String(chrs, chrs + lbr);
|
||||
values = Common::String(chrs + lbr + 1, chrs + rbr);
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
82
engines/zvision/scripting/controls/fist_control.h
Normal file
82
engines/zvision/scripting/controls/fist_control.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 ZVISION_FIST_CONTROL_H
|
||||
#define ZVISION_FIST_CONTROL_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
|
||||
namespace Video {
|
||||
class VideoDecoder;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
// Only used in Zork Nemesis, handles the door lock puzzle with the skeletal fingers (td9e)
|
||||
class FistControl : public Control {
|
||||
public:
|
||||
FistControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
~FistControl() override;
|
||||
|
||||
private:
|
||||
uint32 _fiststatus;
|
||||
int _fistnum;
|
||||
int16 _cursor;
|
||||
int _order;
|
||||
|
||||
Common::Array< Common::Array<Common::Rect> > _fistsUp;
|
||||
Common::Array< Common::Array<Common::Rect> > _fistsDwn;
|
||||
|
||||
int32 _numEntries;
|
||||
|
||||
struct entries {
|
||||
uint32 _bitsStrt;
|
||||
uint32 _bitsEnd;
|
||||
int32 _anmStrt;
|
||||
int32 _anmEnd;
|
||||
int32 _sound;
|
||||
};
|
||||
|
||||
Common::Array<entries> _entries;
|
||||
|
||||
Video::VideoDecoder *_animation;
|
||||
Common::Rect _anmRect;
|
||||
int32 _soundKey;
|
||||
int32 _animationId;
|
||||
|
||||
public:
|
||||
bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
private:
|
||||
void readDescFile(const Common::Path &fileName);
|
||||
void clearFistArray(Common::Array< Common::Array<Common::Rect> > &arr);
|
||||
uint32 readBits(const char *str);
|
||||
int mouseIn(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
|
||||
void getFistParams(const Common::String &inputStr, Common::String ¶meter, Common::String &values);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
184
engines/zvision/scripting/controls/hotmov_control.cpp
Normal file
184
engines/zvision/scripting/controls/hotmov_control.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "video/video_decoder.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/hotmov_control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
HotMovControl::HotMovControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_HOTMOV) {
|
||||
_animation = NULL;
|
||||
_cycle = 0;
|
||||
_frames.clear();
|
||||
_cyclesCount = 0;
|
||||
_framesCount = 0;
|
||||
|
||||
_engine->getScriptManager()->setStateValue(_key, 0);
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("hs_frame_list", true)) {
|
||||
readHsFile(Common::Path(values));
|
||||
} else if (param.matchString("rectangle", true)) {
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height) == 4)
|
||||
_rectangle = Common::Rect(x, y, width, height);
|
||||
} else if (param.matchString("num_frames", true)) {
|
||||
_framesCount = atoi(values.c_str());
|
||||
} else if (param.matchString("num_cycles", true)) {
|
||||
_cyclesCount = atoi(values.c_str());
|
||||
} else if (param.matchString("animation", true)) {
|
||||
char filename[64];
|
||||
if (sscanf(values.c_str(), "%s", filename) == 1) {
|
||||
values = Common::String(filename);
|
||||
_animation = _engine->loadAnimation(Common::Path(values));
|
||||
_animation->start();
|
||||
}
|
||||
} else if (param.matchString("venus_id", true)) {
|
||||
_venusId = atoi(values.c_str());
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
}
|
||||
|
||||
HotMovControl::~HotMovControl() {
|
||||
if (_animation)
|
||||
delete _animation;
|
||||
|
||||
_frames.clear();
|
||||
}
|
||||
|
||||
bool HotMovControl::process(uint32 deltaTimeInMillis) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_cycle < _cyclesCount) {
|
||||
if (_animation && _animation->endOfVideo()) {
|
||||
_cycle++;
|
||||
|
||||
if (_cycle == _cyclesCount) {
|
||||
_engine->getScriptManager()->setStateValue(_key, 2);
|
||||
return false;
|
||||
}
|
||||
|
||||
_animation->rewind();
|
||||
}
|
||||
|
||||
if (_animation && _animation->needsUpdate()) {
|
||||
const Graphics::Surface *frameData = _animation->decodeNextFrame();
|
||||
if (frameData)
|
||||
_engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _rectangle);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HotMovControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (!_animation)
|
||||
return false;
|
||||
|
||||
if (_cycle < _cyclesCount) {
|
||||
if (_frames[_animation->getCurFrame()].contains(backgroundImageSpacePos)) {
|
||||
_engine->getCursorManager()->changeCursor(CursorIndex_Active);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HotMovControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (!_animation)
|
||||
return false;
|
||||
|
||||
if (_cycle < _cyclesCount) {
|
||||
if (_frames[_animation->getCurFrame()].contains(backgroundImageSpacePos)) {
|
||||
setVenus();
|
||||
_engine->getScriptManager()->setStateValue(_key, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HotMovControl::readHsFile(const Common::Path &fileName) {
|
||||
if (_framesCount == 0)
|
||||
return;
|
||||
|
||||
Common::File file;
|
||||
if (!file.open(fileName)) {
|
||||
warning("HS file %s could could be opened", fileName.toString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Common::String line;
|
||||
|
||||
_frames.resize(_framesCount);
|
||||
|
||||
while (!file.eos()) {
|
||||
line = file.readLine();
|
||||
|
||||
int frame;
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
if (sscanf(line.c_str(), "%d:%d %d %d %d~", &frame, &x, &y, &width, &height) == 5) {
|
||||
if (frame >= 0 && frame < _framesCount)
|
||||
_frames[frame] = Common::Rect(x, y, width, height);
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
60
engines/zvision/scripting/controls/hotmov_control.h
Normal file
60
engines/zvision/scripting/controls/hotmov_control.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 ZVISION_HOTMOV_CONTROL_H
|
||||
#define ZVISION_HOTMOV_CONTROL_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/path.h"
|
||||
#include "common/rect.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
|
||||
namespace Video {
|
||||
class VideoDecoder;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
// Only used in Zork Nemesis, handles movies where the player needs to click on something (mj7g, vw3g)
|
||||
class HotMovControl : public Control {
|
||||
public:
|
||||
HotMovControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
~HotMovControl() override;
|
||||
|
||||
private:
|
||||
int32 _framesCount;
|
||||
int32 _cycle;
|
||||
int32 _cyclesCount;
|
||||
Video::VideoDecoder *_animation;
|
||||
Common::Rect _rectangle;
|
||||
Common::Array<Common::Rect> _frames;
|
||||
public:
|
||||
bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
private:
|
||||
void readHsFile(const Common::Path &fileName);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
271
engines/zvision/scripting/controls/input_control.cpp
Normal file
271
engines/zvision/scripting/controls/input_control.cpp
Normal file
@@ -0,0 +1,271 @@
|
||||
/* 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 "backends/keymapper/keymap.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "video/video_decoder.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/input_control.h"
|
||||
#include "zvision/text/string_manager.h"
|
||||
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_INPUT),
|
||||
_background(0),
|
||||
_nextTabstop(0),
|
||||
_focused(false),
|
||||
_textChanged(false),
|
||||
_enterPressed(false),
|
||||
_readOnly(false),
|
||||
_txtWidth(0),
|
||||
_animation(NULL) {
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("rectangle", true)) {
|
||||
int x1, y1, x2, y2;
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &x1, &y1, &x2, &y2) == 4)
|
||||
_textRectangle = Common::Rect(x1, y1, x2, y2);
|
||||
} else if (param.matchString("aux_hotspot", true)) {
|
||||
int x1, y1, x2, y2;
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &x1, &y1, &x2, &y2) == 4)
|
||||
_headerRectangle = Common::Rect(x1, y1, x2, y2);
|
||||
} else if (param.matchString("string_init", true)) {
|
||||
uint fontFormatNumber;
|
||||
if (sscanf(values.c_str(), "%u", &fontFormatNumber) == 1)
|
||||
_stringInit.readAllStyles(_engine->getStringManager()->getTextLine(fontFormatNumber));
|
||||
} else if (param.matchString("chooser_init_string", true)) {
|
||||
uint fontFormatNumber;
|
||||
if (sscanf(values.c_str(), "%u", &fontFormatNumber) == 1)
|
||||
_stringChooserInit.readAllStyles(_engine->getStringManager()->getTextLine(fontFormatNumber));
|
||||
} else if (param.matchString("next_tabstop", true)) {
|
||||
sscanf(values.c_str(), "%u", &_nextTabstop);
|
||||
} else if (param.matchString("cursor_dimensions", true)) {
|
||||
// Ignore, use the dimensions in the animation file
|
||||
} else if (param.matchString("cursor_animation_frames", true)) {
|
||||
// Ignore, use the frame count in the animation file
|
||||
} else if (param.matchString("cursor_animation", true)) {
|
||||
char fileName[25];
|
||||
if (sscanf(values.c_str(), "%24s %*u", fileName) == 1) {
|
||||
_animation = _engine->loadAnimation(fileName);
|
||||
_animation->start();
|
||||
}
|
||||
} else if (param.matchString("focus", true)) {
|
||||
_focused = true;
|
||||
_engine->getScriptManager()->setFocusControlKey(_key);
|
||||
} else if (param.matchString("venus_id", true)) {
|
||||
_venusId = atoi(values.c_str());
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
|
||||
_maxTxtWidth = _textRectangle.width();
|
||||
if (_animation)
|
||||
_maxTxtWidth -= _animation->getWidth();
|
||||
}
|
||||
|
||||
InputControl::~InputControl() {
|
||||
_background->free();
|
||||
delete _background;
|
||||
unfocus();
|
||||
}
|
||||
|
||||
void InputControl::focus() {
|
||||
if (!_readOnly) {
|
||||
_engine->getGameKeymap()->setEnabled(false);
|
||||
}
|
||||
_focused = true;
|
||||
_textChanged = true;
|
||||
}
|
||||
|
||||
void InputControl::unfocus() {
|
||||
if (!_readOnly) {
|
||||
_engine->getGameKeymap()->setEnabled(true);
|
||||
}
|
||||
_focused = false;
|
||||
_textChanged = true;
|
||||
}
|
||||
|
||||
bool InputControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_textRectangle.contains(backgroundImageSpacePos)) {
|
||||
if (!_readOnly) {
|
||||
// Save
|
||||
_engine->getScriptManager()->focusControl(_key);
|
||||
setVenus();
|
||||
} else {
|
||||
// Restore
|
||||
if (_currentInputText.size()) {
|
||||
setVenus();
|
||||
_enterPressed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InputControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_textRectangle.contains(backgroundImageSpacePos)) {
|
||||
if (!_readOnly) {
|
||||
// Save
|
||||
_engine->getCursorManager()->changeCursor(CursorIndex_Active);
|
||||
return true;
|
||||
} else {
|
||||
// Restore
|
||||
if (_currentInputText.size()) {
|
||||
_engine->getCursorManager()->changeCursor(CursorIndex_Active);
|
||||
_engine->getScriptManager()->focusControl(_key);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InputControl::onKeyDown(Common::KeyState keyState) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (!_focused) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keyState.keycode == Common::KEYCODE_BACKSPACE) {
|
||||
if (!_readOnly) {
|
||||
_currentInputText.deleteLastChar();
|
||||
_textChanged = true;
|
||||
}
|
||||
} else if (keyState.keycode == Common::KEYCODE_RETURN) {
|
||||
_enterPressed = true;
|
||||
} else if (keyState.keycode == Common::KEYCODE_TAB) {
|
||||
unfocus();
|
||||
// Focus the next input control
|
||||
_engine->getScriptManager()->focusControl(_nextTabstop);
|
||||
// Don't process this event for other controls
|
||||
return true;
|
||||
} else {
|
||||
if (!_readOnly) {
|
||||
// Otherwise, append the new character to the end of the current text
|
||||
uint16 asciiValue = keyState.ascii;
|
||||
// We only care about text values
|
||||
if (asciiValue >= 32 && asciiValue <= 126) {
|
||||
_currentInputText += (char)asciiValue;
|
||||
_textChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InputControl::process(uint32 deltaTimeInMillis) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (!_background) {
|
||||
_background = _engine->getRenderManager()->getBkgRect(_textRectangle);
|
||||
}
|
||||
|
||||
// First see if we need to render the text
|
||||
if (_textChanged) {
|
||||
// Blit the text using the RenderManager
|
||||
|
||||
Graphics::Surface txt;
|
||||
txt.copyFrom(*_background);
|
||||
|
||||
int32 oldTxtWidth = _txtWidth;
|
||||
|
||||
if (!_readOnly || !_focused)
|
||||
_txtWidth = _engine->getTextRenderer()->drawText(_currentInputText, _stringInit, txt);
|
||||
else
|
||||
_txtWidth = _engine->getTextRenderer()->drawText(_currentInputText, _stringChooserInit, txt);
|
||||
|
||||
if (_readOnly || _txtWidth <= _maxTxtWidth)
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(txt, _textRectangle.left, _textRectangle.top);
|
||||
else {
|
||||
// Assume the last character caused the overflow.
|
||||
_currentInputText.deleteLastChar();
|
||||
_txtWidth = oldTxtWidth;
|
||||
}
|
||||
|
||||
txt.free();
|
||||
}
|
||||
|
||||
if (_animation && !_readOnly && _focused) {
|
||||
if (_animation->endOfVideo())
|
||||
_animation->rewind();
|
||||
|
||||
if (_animation->needsUpdate()) {
|
||||
const Graphics::Surface *srf = _animation->decodeNextFrame();
|
||||
int16 xx = _textRectangle.left + _txtWidth;
|
||||
if (xx >= _textRectangle.left + (_textRectangle.width() - (int16)_animation->getWidth()))
|
||||
xx = _textRectangle.left + _textRectangle.width() - (int16)_animation->getWidth();
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(*srf, xx, _textRectangle.top);
|
||||
}
|
||||
}
|
||||
|
||||
_textChanged = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InputControl::enterPress() {
|
||||
if (_enterPressed) {
|
||||
_enterPressed = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void InputControl::setText(const Common::String &_str) {
|
||||
_currentInputText = _str;
|
||||
_textChanged = true;
|
||||
}
|
||||
|
||||
const Common::String InputControl::getText() {
|
||||
return _currentInputText;
|
||||
}
|
||||
|
||||
void InputControl::setReadOnly(bool readonly) {
|
||||
_readOnly = readonly;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
74
engines/zvision/scripting/controls/input_control.h
Normal file
74
engines/zvision/scripting/controls/input_control.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_INPUT_CONTROL_H
|
||||
#define ZVISION_INPUT_CONTROL_H
|
||||
|
||||
#include "common/rect.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
#include "zvision/text/string_manager.h"
|
||||
#include "zvision/text/text.h"
|
||||
|
||||
namespace Video {
|
||||
class VideoDecoder;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class InputControl : public Control {
|
||||
public:
|
||||
InputControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
~InputControl() override;
|
||||
|
||||
private:
|
||||
Graphics::Surface *_background;
|
||||
Common::Rect _textRectangle;
|
||||
Common::Rect _headerRectangle;
|
||||
TextStyleState _stringInit;
|
||||
TextStyleState _stringChooserInit;
|
||||
uint32 _nextTabstop;
|
||||
bool _focused;
|
||||
|
||||
Common::String _currentInputText;
|
||||
bool _textChanged;
|
||||
bool _enterPressed;
|
||||
bool _readOnly;
|
||||
|
||||
int16 _txtWidth;
|
||||
int16 _maxTxtWidth;
|
||||
Video::VideoDecoder *_animation;
|
||||
|
||||
public:
|
||||
void focus() override;
|
||||
void unfocus() override;
|
||||
bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool onKeyDown(Common::KeyState keyState) override;
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
void setText(const Common::String &_str);
|
||||
const Common::String getText();
|
||||
bool enterPress();
|
||||
void setReadOnly(bool);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
352
engines/zvision/scripting/controls/lever_control.cpp
Normal file
352
engines/zvision/scripting/controls/lever_control.cpp
Normal file
@@ -0,0 +1,352 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/tokenizer.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "video/video_decoder.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/lever_control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
LeverControl::LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_LEVER),
|
||||
_frameInfo(0),
|
||||
_frameCount(0),
|
||||
_startFrame(0),
|
||||
_currentFrame(0),
|
||||
_lastRenderedFrame(0),
|
||||
_mouseIsCaptured(false),
|
||||
_isReturning(false),
|
||||
_accumulatedTime(0),
|
||||
_returnRoutesCurrentFrame(0),
|
||||
_animation(NULL),
|
||||
_cursor(CursorIndex_Active),
|
||||
_mirrored(false) {
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("descfile", true)) {
|
||||
char levFileName[25];
|
||||
if (sscanf(values.c_str(), "%24s", levFileName) == 1)
|
||||
parseLevFile(levFileName);
|
||||
} else if (param.matchString("cursor", true)) {
|
||||
char cursorName[25];
|
||||
if (sscanf(values.c_str(), "%24s", cursorName) == 1)
|
||||
_cursor = _engine->getCursorManager()->getCursorId(Common::String(cursorName));
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
|
||||
renderFrame(_currentFrame);
|
||||
}
|
||||
|
||||
LeverControl::~LeverControl() {
|
||||
if (_animation)
|
||||
delete _animation;
|
||||
|
||||
delete[] _frameInfo;
|
||||
}
|
||||
|
||||
void LeverControl::parseLevFile(const Common::Path &fileName) {
|
||||
debugC(2, kDebugControl, "LeverControl::parseLevFile(%s)", fileName.toString().c_str());
|
||||
Common::File file;
|
||||
if (!file.open(fileName)) {
|
||||
warning("LEV file %s could could be opened", fileName.toString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Common::String line;
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
|
||||
int id = 0;
|
||||
|
||||
while (!file.eos()) {
|
||||
line = file.readLine();
|
||||
getLevParams(line, param, values);
|
||||
|
||||
if (param.matchString("animation_id", true)) {
|
||||
sscanf(values.c_str(), "%d", &id);
|
||||
debugC(2, kDebugControl, "Lever animation ID: %d", id);
|
||||
} else if (param.matchString("filename", true)) {
|
||||
_animation = _engine->loadAnimation(Common::Path(values));
|
||||
} else if (param.matchString("skipcolor", true)) {
|
||||
// Not used
|
||||
} else if (param.matchString("anim_coords", true)) {
|
||||
int left, top, right, bottom;
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &left, &top, &right, &bottom) == 4) {
|
||||
_animationCoords.left = left;
|
||||
_animationCoords.top = top;
|
||||
_animationCoords.right = right;
|
||||
_animationCoords.bottom = bottom;
|
||||
}
|
||||
} else if (param.matchString("mirrored", true)) {
|
||||
uint mirrored;
|
||||
if (sscanf(values.c_str(), "%u", &mirrored) == 1)
|
||||
_mirrored = mirrored == 0 ? false : true;
|
||||
} else if (param.matchString("frames", true)) {
|
||||
if (sscanf(values.c_str(), "%u", &_frameCount) == 1)
|
||||
_frameInfo = new FrameInfo[_frameCount];
|
||||
} else if (param.matchString("elsewhere", true)) {
|
||||
// Not used
|
||||
} else if (param.matchString("out_of_control", true)) {
|
||||
// Not used
|
||||
} else if (param.matchString("start_pos", true)) {
|
||||
if (sscanf(values.c_str(), "%u", &_startFrame) == 1)
|
||||
_currentFrame = _startFrame;
|
||||
} else if (param.matchString("hotspot_deltas", true)) {
|
||||
uint x;
|
||||
uint y;
|
||||
if (sscanf(values.c_str(), "%u %u", &x, &y) == 2) {
|
||||
_hotspotDelta.x = x;
|
||||
_hotspotDelta.y = y;
|
||||
}
|
||||
} else if (param.matchString("venus_id", true)) {
|
||||
_venusId = atoi(values.c_str());
|
||||
} else {
|
||||
uint frameNumber;
|
||||
uint x, y;
|
||||
|
||||
line.toLowercase();
|
||||
|
||||
if (sscanf(line.c_str(), "%u:%u %u", &frameNumber, &x, &y) == 3) {
|
||||
_frameInfo[frameNumber].hotspot.left = x;
|
||||
_frameInfo[frameNumber].hotspot.top = y;
|
||||
_frameInfo[frameNumber].hotspot.right = x + _hotspotDelta.x;
|
||||
_frameInfo[frameNumber].hotspot.bottom = y + _hotspotDelta.y;
|
||||
}
|
||||
|
||||
Common::StringTokenizer tokenizer(line, " ^=()~");
|
||||
tokenizer.nextToken();
|
||||
tokenizer.nextToken();
|
||||
|
||||
Common::String token = tokenizer.nextToken();
|
||||
while (!tokenizer.empty()) {
|
||||
if (token == "d") {
|
||||
token = tokenizer.nextToken();
|
||||
|
||||
uint angle;
|
||||
uint toFrame;
|
||||
if (sscanf(token.c_str(), "%u,%u", &toFrame, &angle) == 2)
|
||||
_frameInfo[frameNumber].paths.push_back(PathSegment(angle, toFrame));
|
||||
} else if (token.hasPrefix("p")) {
|
||||
// Format: P(<from> to <to>)
|
||||
tokenizer.nextToken();
|
||||
tokenizer.nextToken();
|
||||
token = tokenizer.nextToken();
|
||||
uint to = atoi(token.c_str());
|
||||
|
||||
_frameInfo[frameNumber].returnRoute.push_back(to);
|
||||
}
|
||||
|
||||
token = tokenizer.nextToken();
|
||||
}
|
||||
}
|
||||
|
||||
// Don't read lines in this place because last will not be parsed.
|
||||
}
|
||||
// WORKAROUND for a script bug in Zork: Nemesis, room tz2e (orrery)
|
||||
// Animation coordinates for left hand lever do not properly align with background image
|
||||
switch (id) {
|
||||
case 2926:
|
||||
_animationCoords.bottom -= 4;
|
||||
_animationCoords.right += 1;
|
||||
_animationCoords.left -= 1;
|
||||
_animationCoords.top += 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Cycle through all unit direction vectors in path segments & determine step distance
|
||||
debugC(3, kDebugControl, "Setting step distances");
|
||||
for (uint frame=0; frame < _frameCount; frame++) {
|
||||
debugC(3, kDebugControl, "Frame %d", frame);
|
||||
for (auto &iter : _frameInfo[frame].paths) {
|
||||
uint destFrame = iter.toFrame;
|
||||
Common::Point deltaPos = _frameInfo[destFrame].hotspot.origin() - _frameInfo[frame].hotspot.origin();
|
||||
Math::Vector2d deltaPosVector((float)deltaPos.x, (float)deltaPos.y);
|
||||
iter.distance *= deltaPosVector.getMagnitude();
|
||||
debugC(3, kDebugControl, "\tdeltaPos = %d,%d, Distance %f", deltaPos.x, deltaPos.y, iter.distance);
|
||||
}
|
||||
}
|
||||
debugC(2, kDebugControl, "LeverControl::~parseLevFile()");
|
||||
}
|
||||
|
||||
bool LeverControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) {
|
||||
setVenus();
|
||||
_mouseIsCaptured = true;
|
||||
_gripOffset = backgroundImageSpacePos - _frameInfo[_currentFrame].hotspot.origin();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LeverControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_mouseIsCaptured) {
|
||||
_mouseIsCaptured = false;
|
||||
_engine->getScriptManager()->setStateValue(_key, _currentFrame);
|
||||
|
||||
_isReturning = true;
|
||||
_returnRoutesCurrentProgress = _frameInfo[_currentFrame].returnRoute.begin();
|
||||
_returnRoutesCurrentFrame = _currentFrame;
|
||||
}
|
||||
_gripOffset = Common::Point(0,0);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LeverControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
bool cursorWasChanged = false;
|
||||
|
||||
if (_mouseIsCaptured) {
|
||||
uint nextFrame = _currentFrame;
|
||||
do {
|
||||
Common::Point gripOrigin = _frameInfo[_currentFrame].hotspot.origin() + _gripOffset;
|
||||
debugC(1, kDebugControl, "LeverControl::onMouseMove() screenPos = %d,%d, imagePos = %d,%d, gripOrigin = %d,%d", screenSpacePos.x, screenSpacePos.y, backgroundImageSpacePos.x, backgroundImageSpacePos.y, gripOrigin.x, gripOrigin.y);
|
||||
Common::Point deltaPos = backgroundImageSpacePos - gripOrigin;
|
||||
nextFrame = getNextFrame(deltaPos);
|
||||
if (nextFrame != _currentFrame) {
|
||||
_currentFrame = nextFrame;
|
||||
_engine->getScriptManager()->setStateValue(_key, _currentFrame);
|
||||
}
|
||||
} while (nextFrame != _currentFrame);
|
||||
|
||||
if (_lastRenderedFrame != _currentFrame)
|
||||
renderFrame(_currentFrame);
|
||||
|
||||
_engine->getCursorManager()->changeCursor(_cursor);
|
||||
cursorWasChanged = true;
|
||||
} else if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) {
|
||||
_engine->getCursorManager()->changeCursor(_cursor);
|
||||
cursorWasChanged = true;
|
||||
}
|
||||
return cursorWasChanged;
|
||||
}
|
||||
|
||||
uint LeverControl::getNextFrame(Common::Point &deltaPos) {
|
||||
Math::Vector2d movement((float)deltaPos.x, (float)deltaPos.y);
|
||||
for (auto &iter : _frameInfo[_currentFrame].paths) {
|
||||
debugC(1, kDebugControl, "\tPossible step = %f,%f, angle = %d, distance %f", iter.direction.getX(), iter.direction.getY(), (uint)Math::rad2deg(iter.angle), iter.distance);
|
||||
if (movement.dotProduct(iter.direction) >= iter.distance/2) {
|
||||
return iter.toFrame;
|
||||
}
|
||||
}
|
||||
return _currentFrame;
|
||||
}
|
||||
|
||||
bool LeverControl::process(uint32 deltaTimeInMillis) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_isReturning) {
|
||||
_accumulatedTime += deltaTimeInMillis;
|
||||
while (_accumulatedTime >= _returnFramePeriod) {
|
||||
_accumulatedTime -= _returnFramePeriod;
|
||||
if (_returnRoutesCurrentFrame == *_returnRoutesCurrentProgress) {
|
||||
_returnRoutesCurrentProgress++;
|
||||
}
|
||||
if (_returnRoutesCurrentProgress == _frameInfo[_currentFrame].returnRoute.end()) {
|
||||
_isReturning = false;
|
||||
_currentFrame = _returnRoutesCurrentFrame;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint toFrame = *_returnRoutesCurrentProgress;
|
||||
if (_returnRoutesCurrentFrame < toFrame) {
|
||||
_returnRoutesCurrentFrame++;
|
||||
} else if (_returnRoutesCurrentFrame > toFrame) {
|
||||
_returnRoutesCurrentFrame--;
|
||||
}
|
||||
|
||||
_engine->getScriptManager()->setStateValue(_key, _returnRoutesCurrentFrame);
|
||||
renderFrame(_returnRoutesCurrentFrame);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void LeverControl::renderFrame(uint frameNumber) {
|
||||
_lastRenderedFrame = frameNumber;
|
||||
if (frameNumber != 0 && frameNumber < _lastRenderedFrame && _mirrored)
|
||||
frameNumber = (_frameCount * 2) - frameNumber - 1;
|
||||
|
||||
const Graphics::Surface *frameData;
|
||||
|
||||
_animation->seekToFrame(frameNumber);
|
||||
frameData = _animation->decodeNextFrame();
|
||||
if (frameData)
|
||||
_engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _animationCoords);
|
||||
}
|
||||
|
||||
void LeverControl::getLevParams(const Common::String &inputStr, Common::String ¶meter, Common::String &values) {
|
||||
const char *chrs = inputStr.c_str();
|
||||
uint lbr;
|
||||
|
||||
for (lbr = 0; lbr < inputStr.size(); lbr++)
|
||||
if (chrs[lbr] == ':')
|
||||
break;
|
||||
|
||||
if (lbr >= inputStr.size())
|
||||
return;
|
||||
|
||||
uint rbr;
|
||||
|
||||
for (rbr = lbr + 1; rbr < inputStr.size(); rbr++)
|
||||
if (chrs[rbr] == '~')
|
||||
break;
|
||||
|
||||
if (rbr >= inputStr.size())
|
||||
return;
|
||||
|
||||
parameter = Common::String(chrs, chrs + lbr);
|
||||
values = Common::String(chrs + lbr + 1, chrs + rbr);
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
96
engines/zvision/scripting/controls/lever_control.h
Normal file
96
engines/zvision/scripting/controls/lever_control.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/* 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 ZVISION_LEVER_CONTROL_H
|
||||
#define ZVISION_LEVER_CONTROL_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/path.h"
|
||||
#include "common/rect.h"
|
||||
#include "math/vector2d.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
|
||||
namespace Video {
|
||||
class VideoDecoder;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
// Only used in Zork Nemesis, handles draggable levers (te2e, tm7e, tp2e, tt2e, tz2e)
|
||||
class LeverControl : public Control {
|
||||
public:
|
||||
LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
~LeverControl() override;
|
||||
|
||||
private:
|
||||
|
||||
struct PathSegment {
|
||||
PathSegment(uint a, uint t) : angle(Math::deg2rad<float>(a)), toFrame(t), direction(cos(angle), -sin(angle)) {}
|
||||
|
||||
float angle; // Radians
|
||||
uint toFrame;
|
||||
Math::Vector2d direction; // NB unit vector upon initialisation
|
||||
float distance = 1.0f;
|
||||
};
|
||||
|
||||
struct FrameInfo {
|
||||
Common::Rect hotspot;
|
||||
Common::List<PathSegment> paths;
|
||||
Common::List<uint> returnRoute;
|
||||
};
|
||||
|
||||
private:
|
||||
Video::VideoDecoder *_animation;
|
||||
|
||||
int _cursor;
|
||||
Common::Rect _animationCoords;
|
||||
bool _mirrored;
|
||||
uint _frameCount;
|
||||
uint _startFrame;
|
||||
Common::Point _hotspotDelta;
|
||||
FrameInfo *_frameInfo;
|
||||
|
||||
uint _currentFrame;
|
||||
uint _lastRenderedFrame;
|
||||
bool _mouseIsCaptured;
|
||||
bool _isReturning;
|
||||
Common::Point _gripOffset;
|
||||
Common::List<uint>::iterator _returnRoutesCurrentProgress;
|
||||
uint _returnRoutesCurrentFrame;
|
||||
const uint8 _returnFramePeriod = 60; // milliseconds
|
||||
uint32 _accumulatedTime;
|
||||
|
||||
public:
|
||||
bool onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
private:
|
||||
void parseLevFile(const Common::Path &fileName);
|
||||
void renderFrame(uint frameNumber);
|
||||
void getLevParams(const Common::String &inputStr, Common::String ¶meter, Common::String &values);
|
||||
uint getNextFrame(Common::Point &deltaPos);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
216
engines/zvision/scripting/controls/paint_control.cpp
Normal file
216
engines/zvision/scripting/controls/paint_control.cpp
Normal file
@@ -0,0 +1,216 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/paint_control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
PaintControl::PaintControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_PAINT) {
|
||||
|
||||
_cursor = CursorIndex_Active;
|
||||
_paint = NULL;
|
||||
_bkg = NULL;
|
||||
_brush = NULL;
|
||||
_colorKey = 0;
|
||||
_mouseDown = false;
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("rectangle", true)) {
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height) == 4)
|
||||
_rectangle = Common::Rect(x, y, width + x, height + y);
|
||||
} else if (param.matchString("cursor", true)) {
|
||||
_cursor = _engine->getCursorManager()->getCursorId(values);
|
||||
} else if (param.matchString("brush_file", true)) {
|
||||
_brush = _engine->getRenderManager()->loadImage(Common::Path(values), false);
|
||||
} else if (param.matchString("venus_id", true)) {
|
||||
_venusId = atoi(values.c_str());
|
||||
} else if (param.matchString("paint_file", true)) {
|
||||
_paint = _engine->getRenderManager()->loadImage(Common::Path(values), false);
|
||||
} else if (param.matchString("eligible_objects", true)) {
|
||||
char buf[256];
|
||||
memset(buf, 0, 256);
|
||||
strncpy(buf, values.c_str(), 255);
|
||||
|
||||
char *curpos = buf;
|
||||
char *strend = buf + strlen(buf);
|
||||
while (true) {
|
||||
char *st = curpos;
|
||||
|
||||
if (st >= strend)
|
||||
break;
|
||||
|
||||
while (*curpos != ' ' && curpos < strend)
|
||||
curpos++;
|
||||
|
||||
*curpos = 0;
|
||||
curpos++;
|
||||
|
||||
int obj = atoi(st);
|
||||
|
||||
_eligibleObjects.push_back(obj);
|
||||
}
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
|
||||
if (_paint) {
|
||||
_colorKey = _paint->format.RGBToColor(255, 0, 255);
|
||||
_bkg = new Graphics::Surface;
|
||||
_bkg->create(_rectangle.width(), _rectangle.height(), _paint->format);
|
||||
_bkg->fillRect(Common::Rect(_rectangle.width(), _rectangle.height()), _colorKey);
|
||||
|
||||
Graphics::Surface *tmp = new Graphics::Surface;
|
||||
tmp->create(_rectangle.width(), _rectangle.height(), _paint->format);
|
||||
_engine->getRenderManager()->blitSurfaceToSurface(*_paint, _rectangle, *tmp, 0, 0);
|
||||
_paint->free();
|
||||
delete _paint;
|
||||
_paint = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
PaintControl::~PaintControl() {
|
||||
// Clear the state value back to 0
|
||||
//_engine->getScriptManager()->setStateValue(_key, 0);
|
||||
if (_paint) {
|
||||
_paint->free();
|
||||
delete _paint;
|
||||
}
|
||||
if (_brush) {
|
||||
_brush->free();
|
||||
delete _brush;
|
||||
}
|
||||
if (_bkg) {
|
||||
_bkg->free();
|
||||
delete _bkg;
|
||||
}
|
||||
}
|
||||
|
||||
bool PaintControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
_mouseDown = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PaintControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_rectangle.contains(backgroundImageSpacePos)) {
|
||||
int mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem);
|
||||
|
||||
if (eligeblity(mouseItem)) {
|
||||
setVenus();
|
||||
_mouseDown = true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PaintControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_rectangle.contains(backgroundImageSpacePos)) {
|
||||
int mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem);
|
||||
|
||||
if (eligeblity(mouseItem)) {
|
||||
_engine->getCursorManager()->changeCursor(_cursor);
|
||||
|
||||
if (_mouseDown) {
|
||||
Common::Rect bkgRect = paint(backgroundImageSpacePos);
|
||||
if (!bkgRect.isEmpty()) {
|
||||
Common::Rect imgRect = bkgRect;
|
||||
imgRect.translate(-_rectangle.left, -_rectangle.top);
|
||||
|
||||
Graphics::Surface imgUpdate = _bkg->getSubArea(imgRect);
|
||||
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(imgUpdate, bkgRect.left, bkgRect.top, _colorKey);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PaintControl::eligeblity(int itemId) {
|
||||
for (Common::List<int>::iterator it = _eligibleObjects.begin(); it != _eligibleObjects.end(); it++)
|
||||
if (*it == itemId)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::Rect PaintControl::paint(const Common::Point &point) {
|
||||
Common::Rect paintRect = Common::Rect(_brush->w, _brush->h);
|
||||
paintRect.moveTo(point);
|
||||
paintRect.clip(_rectangle);
|
||||
|
||||
if (!paintRect.isEmpty()) {
|
||||
Common::Rect brushRect = paintRect;
|
||||
brushRect.translate(-point.x, -point.y);
|
||||
|
||||
Common::Rect bkgRect = paintRect;
|
||||
bkgRect.translate(-_rectangle.left, -_rectangle.top);
|
||||
|
||||
for (int yy = 0; yy < brushRect.height(); yy++) {
|
||||
uint16 *mask = (uint16 *)_brush->getBasePtr(brushRect.left, brushRect.top + yy);
|
||||
uint16 *from = (uint16 *)_paint->getBasePtr(bkgRect.left, bkgRect.top + yy);
|
||||
uint16 *to = (uint16 *)_bkg->getBasePtr(bkgRect.left, bkgRect.top + yy);
|
||||
for (int xx = 0; xx < brushRect.width(); xx++) {
|
||||
if (*mask != 0)
|
||||
*(to + xx) = *(from + xx);
|
||||
|
||||
mask++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return paintRect;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
89
engines/zvision/scripting/controls/paint_control.h
Normal file
89
engines/zvision/scripting/controls/paint_control.h
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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_PAINT_CONTROL_H
|
||||
#define ZVISION_PAINT_CONTROL_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/rect.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
// Only used in Zork Nemesis, handles the painting puzzle screen in Lucien's room in Irondune (ch4g)
|
||||
class PaintControl : public Control {
|
||||
public:
|
||||
PaintControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
~PaintControl() override;
|
||||
|
||||
/**
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
*/
|
||||
bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
|
||||
/**
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
*/
|
||||
bool onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
/**
|
||||
* Called on every MouseMove. Tests if the mouse is inside _hotspot, and if so, sets the cursor.
|
||||
*
|
||||
* @param engine The base engine
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
* @return Was the cursor changed?
|
||||
*/
|
||||
bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
|
||||
bool process(uint32 deltaTimeInMillis) override {
|
||||
return false;
|
||||
};
|
||||
|
||||
private:
|
||||
/**
|
||||
* The area that will trigger the event
|
||||
* This is in image space coordinates, NOT screen space
|
||||
*/
|
||||
|
||||
uint32 _colorKey;
|
||||
|
||||
Graphics::Surface *_paint;
|
||||
Graphics::Surface *_bkg;
|
||||
Graphics::Surface *_brush;
|
||||
|
||||
Common::List<int> _eligibleObjects;
|
||||
|
||||
int _cursor;
|
||||
Common::Rect _rectangle;
|
||||
|
||||
bool _mouseDown;
|
||||
|
||||
bool eligeblity(int itemId);
|
||||
Common::Rect paint(const Common::Point &point);
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
142
engines/zvision/scripting/controls/push_toggle_control.cpp
Normal file
142
engines/zvision/scripting/controls/push_toggle_control.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/push_toggle_control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
PushToggleControl::PushToggleControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_PUSHTGL),
|
||||
_countTo(2),
|
||||
_cursor(CursorIndex_Active),
|
||||
_event(Common::EVENT_LBUTTONUP) {
|
||||
|
||||
_hotspots.clear();
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("*_hotspot", true)) {
|
||||
uint x;
|
||||
uint y;
|
||||
uint width;
|
||||
uint height;
|
||||
|
||||
if (sscanf(values.c_str(), "%u,%u,%u,%u", &x, &y, &width, &height) == 4)
|
||||
_hotspots.push_back(Common::Rect(x, y, x + width + 1, y + height + 1));
|
||||
} else if (param.matchString("cursor", true)) {
|
||||
_cursor = _engine->getCursorManager()->getCursorId(values);
|
||||
} else if (param.matchString("animation", true)) {
|
||||
// Not used
|
||||
} else if (param.matchString("sound", true)) {
|
||||
// Not used
|
||||
} else if (param.matchString("count_to", true)) {
|
||||
sscanf(values.c_str(), "%u", &_countTo);
|
||||
} else if (param.matchString("mouse_event", true)) {
|
||||
if (values.equalsIgnoreCase("up")) {
|
||||
_event = Common::EVENT_LBUTTONUP;
|
||||
} else if (values.equalsIgnoreCase("down")) {
|
||||
_event = Common::EVENT_LBUTTONDOWN;
|
||||
} else if (values.equalsIgnoreCase("double")) {
|
||||
// Not used
|
||||
}
|
||||
} else if (param.matchString("venus_id", true)) {
|
||||
_venusId = atoi(values.c_str());
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
|
||||
if (_hotspots.size() == 0) {
|
||||
warning("Push_toggle %u was parsed incorrectly", key);
|
||||
}
|
||||
}
|
||||
|
||||
PushToggleControl::~PushToggleControl() {
|
||||
_hotspots.clear();
|
||||
}
|
||||
|
||||
bool PushToggleControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_event != Common::EVENT_LBUTTONUP)
|
||||
return false;
|
||||
|
||||
if (contain(backgroundImageSpacePos)) {
|
||||
setVenus();
|
||||
int32 val = _engine->getScriptManager()->getStateValue(_key);
|
||||
val = (val + 1) % _countTo;
|
||||
_engine->getScriptManager()->setStateValue(_key, val);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PushToggleControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_event != Common::EVENT_LBUTTONDOWN)
|
||||
return false;
|
||||
|
||||
if (contain(backgroundImageSpacePos)) {
|
||||
setVenus();
|
||||
int32 val = _engine->getScriptManager()->getStateValue(_key);
|
||||
val = (val + 1) % _countTo;
|
||||
_engine->getScriptManager()->setStateValue(_key, val);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PushToggleControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (contain(backgroundImageSpacePos)) {
|
||||
_engine->getCursorManager()->changeCursor(_cursor);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PushToggleControl::contain(const Common::Point &point) {
|
||||
for (uint i = 0; i < _hotspots.size(); i++)
|
||||
if (_hotspots[i].contains(point))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
79
engines/zvision/scripting/controls/push_toggle_control.h
Normal file
79
engines/zvision/scripting/controls/push_toggle_control.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_PUSH_TOGGLE_CONTROL_H
|
||||
#define ZVISION_PUSH_TOGGLE_CONTROL_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/events.h"
|
||||
#include "common/rect.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class PushToggleControl : public Control {
|
||||
public:
|
||||
PushToggleControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
~PushToggleControl() override;
|
||||
|
||||
/**
|
||||
* Called when LeftMouse is pushed. Default is NOP.
|
||||
*
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
*/
|
||||
bool onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
/**
|
||||
* Called when LeftMouse is lifted. Calls ScriptManager::setStateValue(_key, 1);
|
||||
*
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
*/
|
||||
bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
/**
|
||||
* Called on every MouseMove. Tests if the mouse is inside _hotspot, and if so, sets the cursor.
|
||||
*
|
||||
* @param engine The base engine
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
* @return Was the cursor changed?
|
||||
*/
|
||||
bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* The area that will trigger the event
|
||||
* This is in image space coordinates, NOT screen space
|
||||
*/
|
||||
Common::Array<Common::Rect> _hotspots;
|
||||
/** The cursor to use when hovering over _hotspot */
|
||||
int _cursor;
|
||||
/** Button maximal values count */
|
||||
uint _countTo;
|
||||
|
||||
Common::EventType _event;
|
||||
|
||||
bool contain(const Common::Point &point);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
177
engines/zvision/scripting/controls/safe_control.cpp
Normal file
177
engines/zvision/scripting/controls/safe_control.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/tokenizer.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "video/video_decoder.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/safe_control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
SafeControl::SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_SAFE) {
|
||||
_statesCount = 0;
|
||||
_curState = 0;
|
||||
_animation = NULL;
|
||||
_innerRaduis = 0;
|
||||
_innerRadiusSqr = 0;
|
||||
_outerRadius = 0;
|
||||
_outerRadiusSqr = 0;
|
||||
_zeroPointer = 0;
|
||||
_startPointer = 0;
|
||||
_targetFrame = 0;
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("animation", true)) {
|
||||
_animation = _engine->loadAnimation(Common::Path(values));
|
||||
_animation->start();
|
||||
} else if (param.matchString("rectangle", true)) {
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height) == 4)
|
||||
_rectangle = Common::Rect(x, y, width, height);
|
||||
} else if (param.matchString("num_states", true)) {
|
||||
_statesCount = atoi(values.c_str());
|
||||
} else if (param.matchString("center", true)) {
|
||||
int x;
|
||||
int y;
|
||||
|
||||
if (sscanf(values.c_str(), "%d %d", &x, &y) == 2)
|
||||
_center = Common::Point(x, y);
|
||||
} else if (param.matchString("dial_inner_radius", true)) {
|
||||
_innerRaduis = atoi(values.c_str());
|
||||
_innerRadiusSqr = _innerRaduis * _innerRaduis;
|
||||
} else if (param.matchString("radius", true)) {
|
||||
_outerRadius = atoi(values.c_str());
|
||||
_outerRadiusSqr = _outerRadius * _outerRadius;
|
||||
} else if (param.matchString("zero_radians_offset", true)) {
|
||||
_zeroPointer = atoi(values.c_str());
|
||||
} else if (param.matchString("pointer_offset", true)) {
|
||||
_startPointer = atoi(values.c_str());
|
||||
_curState = _startPointer;
|
||||
} else if (param.matchString("cursor", true)) {
|
||||
// Not used
|
||||
} else if (param.matchString("mirrored", true)) {
|
||||
// Not used
|
||||
} else if (param.matchString("venus_id", true)) {
|
||||
_venusId = atoi(values.c_str());
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
|
||||
if (_animation)
|
||||
_animation->seekToFrame(_curState);
|
||||
}
|
||||
|
||||
SafeControl::~SafeControl() {
|
||||
if (_animation)
|
||||
delete _animation;
|
||||
|
||||
}
|
||||
|
||||
bool SafeControl::process(uint32 deltaTimeInMillis) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_animation && _animation->getCurFrame() != _targetFrame && _animation->needsUpdate()) {
|
||||
// If we're past the target frame, move back one
|
||||
if (_animation->getCurFrame() > _targetFrame)
|
||||
_animation->seekToFrame(_animation->getCurFrame() - 1);
|
||||
|
||||
const Graphics::Surface *frameData = _animation->decodeNextFrame();
|
||||
if (_animation->getCurFrame() == _targetFrame)
|
||||
_engine->getScriptManager()->setStateValue(_key, _curState);
|
||||
if (frameData)
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(*frameData, _rectangle.left, _rectangle.top);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SafeControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_rectangle.contains(backgroundImageSpacePos)) {
|
||||
int32 mR = backgroundImageSpacePos.sqrDist(_center);
|
||||
if (mR <= _outerRadiusSqr && mR >= _innerRadiusSqr) {
|
||||
_engine->getCursorManager()->changeCursor(CursorIndex_Active);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SafeControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_rectangle.contains(backgroundImageSpacePos)) {
|
||||
int32 mR = backgroundImageSpacePos.sqrDist(_center);
|
||||
if (mR <= _outerRadiusSqr && mR >= _innerRadiusSqr) {
|
||||
setVenus();
|
||||
|
||||
Common::Point tmp = backgroundImageSpacePos - _center;
|
||||
|
||||
// Coverity complains about the order of arguments here,
|
||||
// but changing that breaks the Zork Nemesis safe puzzle.
|
||||
float dd = atan2((float)tmp.x, (float)tmp.y) * 57.29578;
|
||||
|
||||
int16 dp_state = 360 / _statesCount;
|
||||
|
||||
int16 m_state = (_statesCount - ((((int16)dd + 540) % 360) / dp_state)) % _statesCount;
|
||||
|
||||
int16 tmp2 = (m_state + _curState - _zeroPointer + _statesCount - 1) % _statesCount;
|
||||
|
||||
if (_animation)
|
||||
_animation->seekToFrame((_curState + _statesCount - _startPointer) % _statesCount);
|
||||
|
||||
_curState = (_statesCount * 2 + tmp2) % _statesCount;
|
||||
|
||||
_targetFrame = (_curState + _statesCount - _startPointer) % _statesCount;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
63
engines/zvision/scripting/controls/safe_control.h
Normal file
63
engines/zvision/scripting/controls/safe_control.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_SAFE_CONTROL_H
|
||||
#define ZVISION_SAFE_CONTROL_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/rect.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
|
||||
namespace Video {
|
||||
class VideoDecoder;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
// Only used in Zork Nemesis, handles the safe in the Asylum (ac4g)
|
||||
class SafeControl : public Control {
|
||||
public:
|
||||
SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
~SafeControl() override;
|
||||
|
||||
private:
|
||||
int16 _statesCount;
|
||||
int16 _curState;
|
||||
Video::VideoDecoder *_animation;
|
||||
Common::Point _center;
|
||||
Common::Rect _rectangle;
|
||||
int16 _innerRaduis;
|
||||
int32 _innerRadiusSqr;
|
||||
int16 _outerRadius;
|
||||
int32 _outerRadiusSqr;
|
||||
int16 _zeroPointer;
|
||||
int16 _startPointer;
|
||||
int16 _targetFrame;
|
||||
|
||||
public:
|
||||
bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
118
engines/zvision/scripting/controls/save_control.cpp
Normal file
118
engines/zvision/scripting/controls/save_control.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/file/save_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/input_control.h"
|
||||
#include "zvision/scripting/controls/save_control.h"
|
||||
#include "zvision/text/string_manager.h"
|
||||
#include "zvision/text/subtitle_manager.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
SaveControl::SaveControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_SAVE),
|
||||
_saveControl(false) {
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("savebox", true)) {
|
||||
int saveId;
|
||||
int inputId;
|
||||
if (sscanf(values.c_str(), "%d %d", &saveId, &inputId) == 2) {
|
||||
saveElement elmnt;
|
||||
elmnt.inputKey = inputId;
|
||||
elmnt.saveId = saveId;
|
||||
elmnt.exist = false;
|
||||
_inputs.push_back(elmnt);
|
||||
}
|
||||
} else if (param.matchString("control_type", true)) {
|
||||
if (values.contains("save"))
|
||||
_saveControl = true;
|
||||
else
|
||||
_saveControl = false;
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
|
||||
for (saveElmntList::iterator iter = _inputs.begin(); iter != _inputs.end(); ++iter) {
|
||||
Control *ctrl = _engine->getScriptManager()->getControl(iter->inputKey);
|
||||
if (ctrl && ctrl->getType() == Control::CONTROL_INPUT) {
|
||||
InputControl *inp = (InputControl *)ctrl;
|
||||
inp->setReadOnly(!_saveControl);
|
||||
Common::SeekableReadStream *save = _engine->getSaveManager()->getSlotFile(iter->saveId);
|
||||
if (save) {
|
||||
SaveGameHeader header;
|
||||
if (_engine->getSaveManager()->readSaveGameHeader(save, header)) {
|
||||
inp->setText(header.saveName);
|
||||
iter->exist = true;
|
||||
}
|
||||
delete save;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SaveControl::process(uint32 deltaTimeInMillis) {
|
||||
for (saveElmntList::iterator iter = _inputs.begin(); iter != _inputs.end(); ++iter) {
|
||||
Control *ctrl = _engine->getScriptManager()->getControl(iter->inputKey);
|
||||
if (ctrl && ctrl->getType() == Control::CONTROL_INPUT) {
|
||||
InputControl *inp = (InputControl *)ctrl;
|
||||
if (inp->enterPress()) {
|
||||
if (_saveControl) {
|
||||
if (inp->getText().size() > 0) {
|
||||
bool toSave = true;
|
||||
if (iter->exist)
|
||||
if (!_engine->getSubtitleManager()->askQuestion(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEXIST)))
|
||||
toSave = false;
|
||||
|
||||
if (toSave) {
|
||||
_engine->getSaveManager()->saveGame(iter->saveId, inp->getText(), true);
|
||||
_engine->getSubtitleManager()->delayedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVED), 2000);
|
||||
_engine->getScriptManager()->changeLocation(_engine->getScriptManager()->getLastMenuLocation());
|
||||
}
|
||||
} else {
|
||||
_engine->getSubtitleManager()->timedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEMPTY), 2000);
|
||||
}
|
||||
} else {
|
||||
_engine->getSaveManager()->loadGame(iter->saveId);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
53
engines/zvision/scripting/controls/save_control.h
Normal file
53
engines/zvision/scripting/controls/save_control.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* 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 ZVISION_SAVE_CONTROL_H
|
||||
#define ZVISION_SAVE_CONTROL_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class SaveControl : public Control {
|
||||
public:
|
||||
SaveControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
|
||||
private:
|
||||
struct saveElement {
|
||||
int saveId;
|
||||
int inputKey;
|
||||
bool exist;
|
||||
};
|
||||
typedef Common::List<saveElement> saveElmntList;
|
||||
saveElmntList _inputs;
|
||||
|
||||
bool _saveControl;
|
||||
|
||||
public:
|
||||
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
213
engines/zvision/scripting/controls/slot_control.cpp
Normal file
213
engines/zvision/scripting/controls/slot_control.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/slot_control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
SlotControl::SlotControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_SLOT),
|
||||
_cursor(CursorIndex_Active),
|
||||
_distanceId('0') {
|
||||
|
||||
_renderedItem = 0;
|
||||
_bkg = NULL;
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("hotspot", true)) {
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height) == 4)
|
||||
_hotspot = Common::Rect(x, y, width, height);
|
||||
} else if (param.matchString("rectangle", true)) {
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height) == 4)
|
||||
_rectangle = Common::Rect(x, y, width, height);
|
||||
} else if (param.matchString("cursor", true)) {
|
||||
_cursor = _engine->getCursorManager()->getCursorId(values);
|
||||
} else if (param.matchString("distance_id", true)) {
|
||||
sscanf(values.c_str(), "%c", &_distanceId);
|
||||
} else if (param.matchString("venus_id", true)) {
|
||||
_venusId = atoi(values.c_str());
|
||||
} else if (param.matchString("eligible_objects", true)) {
|
||||
char buf[256];
|
||||
memset(buf, 0, 256);
|
||||
strncpy(buf, values.c_str(), 255);
|
||||
|
||||
char *curpos = buf;
|
||||
char *strend = buf + strlen(buf);
|
||||
while (true) {
|
||||
char *st = curpos;
|
||||
|
||||
if (st >= strend)
|
||||
break;
|
||||
|
||||
while (*curpos != ' ' && curpos < strend)
|
||||
curpos++;
|
||||
|
||||
*curpos = 0;
|
||||
curpos++;
|
||||
|
||||
int obj = atoi(st);
|
||||
|
||||
_eligibleObjects.push_back(obj);
|
||||
}
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
|
||||
if (_hotspot.isEmpty() || _rectangle.isEmpty()) {
|
||||
warning("Slot %u was parsed incorrectly", key);
|
||||
}
|
||||
}
|
||||
|
||||
SlotControl::~SlotControl() {
|
||||
// Clear the state value back to 0
|
||||
//_engine->getScriptManager()->setStateValue(_key, 0);
|
||||
|
||||
if (_bkg)
|
||||
delete _bkg;
|
||||
}
|
||||
|
||||
bool SlotControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_hotspot.contains(backgroundImageSpacePos)) {
|
||||
setVenus();
|
||||
|
||||
int item = _engine->getScriptManager()->getStateValue(_key);
|
||||
int mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem);
|
||||
if (item != 0) {
|
||||
if (mouseItem != 0) {
|
||||
if (eligeblity(mouseItem)) {
|
||||
_engine->getScriptManager()->inventoryDrop(mouseItem);
|
||||
_engine->getScriptManager()->inventoryAdd(item);
|
||||
_engine->getScriptManager()->setStateValue(_key, mouseItem);
|
||||
}
|
||||
} else {
|
||||
_engine->getScriptManager()->inventoryAdd(item);
|
||||
_engine->getScriptManager()->setStateValue(_key, 0);
|
||||
}
|
||||
} else if (mouseItem == 0) {
|
||||
if (eligeblity(0)) {
|
||||
_engine->getScriptManager()->inventoryDrop(0);
|
||||
_engine->getScriptManager()->setStateValue(_key, 0);
|
||||
}
|
||||
} else if (eligeblity(mouseItem)) {
|
||||
_engine->getScriptManager()->setStateValue(_key, mouseItem);
|
||||
_engine->getScriptManager()->inventoryDrop(mouseItem);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SlotControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_hotspot.contains(backgroundImageSpacePos)) {
|
||||
_engine->getCursorManager()->changeCursor(_cursor);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SlotControl::process(uint32 deltaTimeInMillis) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_engine->canRender()) {
|
||||
int curItem = _engine->getScriptManager()->getStateValue(_key);
|
||||
if (curItem != _renderedItem) {
|
||||
if (_renderedItem != 0 && curItem == 0) {
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(*_bkg, _rectangle.left, _rectangle.top);
|
||||
_renderedItem = curItem;
|
||||
} else {
|
||||
if (_renderedItem == 0) {
|
||||
if (_bkg)
|
||||
delete _bkg;
|
||||
|
||||
_bkg = _engine->getRenderManager()->getBkgRect(_rectangle);
|
||||
} else {
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(*_bkg, _rectangle.left, _rectangle.top);
|
||||
}
|
||||
|
||||
char buf[16];
|
||||
if (_engine->getGameId() == GID_NEMESIS)
|
||||
Common::sprintf_s(buf, "%d%cobj.tga", curItem, _distanceId);
|
||||
else
|
||||
Common::sprintf_s(buf, "g0z%cu%2.2x1.tga", _distanceId, curItem);
|
||||
|
||||
Graphics::Surface *srf = _engine->getRenderManager()->loadImage(buf);
|
||||
|
||||
int16 drawx = _rectangle.left;
|
||||
int16 drawy = _rectangle.top;
|
||||
|
||||
if (_rectangle.width() > srf->w)
|
||||
drawx = _rectangle.left + (_rectangle.width() - srf->w) / 2;
|
||||
|
||||
if (_rectangle.height() > srf->h)
|
||||
drawy = _rectangle.top + (_rectangle.height() - srf->h) / 2;
|
||||
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(*srf, drawx, drawy, 0);
|
||||
|
||||
delete srf;
|
||||
|
||||
_renderedItem = curItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SlotControl::eligeblity(int itemId) {
|
||||
for (Common::List<int>::iterator it = _eligibleObjects.begin(); it != _eligibleObjects.end(); it++)
|
||||
if (*it == itemId)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
81
engines/zvision/scripting/controls/slot_control.h
Normal file
81
engines/zvision/scripting/controls/slot_control.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 ZVISION_SLOT_CONTROL_H
|
||||
#define ZVISION_SLOT_CONTROL_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/rect.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class SlotControl : public Control {
|
||||
public:
|
||||
SlotControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
~SlotControl() override;
|
||||
|
||||
/**
|
||||
* Called when LeftMouse is lifted. Calls ScriptManager::setStateValue(_key, 1);
|
||||
*
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
*/
|
||||
bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
/**
|
||||
* Called on every MouseMove. Tests if the mouse is inside _hotspot, and if so, sets the cursor.
|
||||
*
|
||||
* @param engine The base engine
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
* @return Was the cursor changed?
|
||||
*/
|
||||
bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* The area that will trigger the event
|
||||
* This is in image space coordinates, NOT screen space
|
||||
*/
|
||||
Common::Rect _rectangle;
|
||||
Common::Rect _hotspot;
|
||||
|
||||
int _cursor;
|
||||
char _distanceId;
|
||||
|
||||
int _renderedItem;
|
||||
|
||||
Common::List<int> _eligibleObjects;
|
||||
|
||||
bool eligeblity(int itemId);
|
||||
|
||||
Graphics::Surface *_bkg;
|
||||
|
||||
/** The cursor to use when hovering over _hotspot */
|
||||
Common::String _hoverCursor;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
103
engines/zvision/scripting/controls/titler_control.cpp
Normal file
103
engines/zvision/scripting/controls/titler_control.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/titler_control.h"
|
||||
#include "zvision/text/text.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
TitlerControl::TitlerControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_TITLER) {
|
||||
|
||||
_surface = NULL;
|
||||
_curString = -1;
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("string_resource_file", true)) {
|
||||
readStringsFile(Common::Path(values));
|
||||
} else if (param.matchString("rectangle", true)) {
|
||||
int x;
|
||||
int y;
|
||||
int x2;
|
||||
int y2;
|
||||
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &x, &y, &x2, &y2) == 4)
|
||||
_rectangle = Common::Rect(x, y, x2, y2);
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
|
||||
if (!_rectangle.isEmpty()) {
|
||||
_surface = new Graphics::Surface;
|
||||
_surface->create(_rectangle.width(), _rectangle.height(), _engine->_resourcePixelFormat);
|
||||
_surface->fillRect(Common::Rect(_surface->w, _surface->h), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TitlerControl::~TitlerControl() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
}
|
||||
|
||||
void TitlerControl::setString(int strLine) {
|
||||
if (strLine != _curString && strLine >= 0 && strLine < (int)_strings.size()) {
|
||||
_surface->fillRect(Common::Rect(_surface->w, _surface->h), 0);
|
||||
_engine->getTextRenderer()->drawTextWithWordWrapping(_strings[strLine], *_surface);
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(*_surface, _rectangle.left, _rectangle.top);
|
||||
_curString = strLine;
|
||||
}
|
||||
}
|
||||
|
||||
void TitlerControl::readStringsFile(const Common::Path &fileName) {
|
||||
Common::File file;
|
||||
if (!file.open(fileName)) {
|
||||
warning("String_resource_file %s could could be opened", fileName.toString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
_strings.clear();
|
||||
|
||||
while (!file.eos()) {
|
||||
|
||||
Common::String line = readWideLine(file).encode();
|
||||
_strings.push_back(line);
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
55
engines/zvision/scripting/controls/titler_control.h
Normal file
55
engines/zvision/scripting/controls/titler_control.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 ZVISION_TITLER_CONTROL_H
|
||||
#define ZVISION_TITLER_CONTROL_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/file.h"
|
||||
#include "common/path.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/stream.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
// Only used in Zork Nemesis, handles the death screen with the Restore/Exit buttons
|
||||
class TitlerControl : public Control {
|
||||
public:
|
||||
TitlerControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
~TitlerControl() override;
|
||||
|
||||
void setString(int strLine);
|
||||
|
||||
private:
|
||||
|
||||
Common::Array< Common::String > _strings;
|
||||
Common::Rect _rectangle;
|
||||
int16 _curString;
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
void readStringsFile(const Common::Path &fileName);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
213
engines/zvision/scripting/effects/animation_effect.cpp
Normal file
213
engines/zvision/scripting/effects/animation_effect.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "video/video_decoder.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/effects/animation_effect.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
AnimationEffect::AnimationEffect(ZVision *engine, uint32 controlKey, const Common::Path &fileName, int32 mask, int32 frate, bool disposeAfterUse)
|
||||
: ScriptingEffect(engine, controlKey, SCRIPTING_EFFECT_ANIM),
|
||||
_disposeAfterUse(disposeAfterUse),
|
||||
_mask(mask),
|
||||
_animation(NULL) {
|
||||
|
||||
_animation = engine->loadAnimation(fileName);
|
||||
|
||||
if (frate > 0) {
|
||||
_frmDelayOverride = (int32)(1000.0 / frate);
|
||||
|
||||
// WORKAROUND: We do not allow the engine to delay more than 66 msec
|
||||
// per frame (15fps max)
|
||||
if (_frmDelayOverride > 66)
|
||||
_frmDelayOverride = 66;
|
||||
} else {
|
||||
_frmDelayOverride = 0;
|
||||
}
|
||||
}
|
||||
|
||||
AnimationEffect::~AnimationEffect() {
|
||||
if (_animation)
|
||||
delete _animation;
|
||||
|
||||
_engine->getScriptManager()->setStateValue(_key, 2);
|
||||
|
||||
PlayNodes::iterator it = _playList.begin();
|
||||
if (it != _playList.end()) {
|
||||
_engine->getScriptManager()->setStateValue((*it).slot, 2);
|
||||
|
||||
if ((*it)._scaled) {
|
||||
(*it)._scaled->free();
|
||||
delete(*it)._scaled;
|
||||
}
|
||||
}
|
||||
|
||||
_playList.clear();
|
||||
}
|
||||
|
||||
bool AnimationEffect::process(uint32 deltaTimeInMillis) {
|
||||
ScriptManager *scriptManager = _engine->getScriptManager();
|
||||
RenderManager *renderManager = _engine->getRenderManager();
|
||||
RenderTable::RenderState renderState = renderManager->getRenderTable()->getRenderState();
|
||||
bool isPanorama = (renderState == RenderTable::PANORAMA);
|
||||
int16 velocity = _engine->getMouseVelocity() + _engine->getKeyboardVelocity();
|
||||
|
||||
// Do not update animation nodes in panoramic mode while turning, if the user
|
||||
// has set this option
|
||||
if (scriptManager->getStateValue(StateKey_NoTurnAnim) == 1 && isPanorama && velocity)
|
||||
return false;
|
||||
|
||||
PlayNodes::iterator it = _playList.begin();
|
||||
if (it != _playList.end()) {
|
||||
playnode *nod = &(*it);
|
||||
|
||||
if (nod->_curFrame == -1) {
|
||||
// The node is just beginning playback
|
||||
nod->_curFrame = nod->start;
|
||||
|
||||
_animation->start();
|
||||
_animation->seekToFrame(nod->start);
|
||||
_animation->setEndFrame(nod->stop);
|
||||
|
||||
nod->_delay = deltaTimeInMillis; // Force the frame to draw
|
||||
if (nod->slot)
|
||||
scriptManager->setStateValue(nod->slot, 1);
|
||||
} else if (_animation->endOfVideo()) {
|
||||
// The node has reached the end; check if we need to loop
|
||||
nod->loop--;
|
||||
|
||||
if (nod->loop == 0) {
|
||||
if (nod->slot >= 0)
|
||||
scriptManager->setStateValue(nod->slot, 2);
|
||||
if (nod->_scaled) {
|
||||
nod->_scaled->free();
|
||||
delete nod->_scaled;
|
||||
}
|
||||
_playList.erase(it);
|
||||
return _disposeAfterUse;
|
||||
}
|
||||
|
||||
nod->_curFrame = nod->start;
|
||||
_animation->seekToFrame(nod->start);
|
||||
}
|
||||
|
||||
// Check if we need to draw a frame
|
||||
bool needsUpdate = false;
|
||||
if (_frmDelayOverride == 0) {
|
||||
// If not overridden, use the VideoDecoder's check
|
||||
needsUpdate = _animation->needsUpdate();
|
||||
} else {
|
||||
// Otherwise, implement our own timing
|
||||
nod->_delay -= deltaTimeInMillis;
|
||||
|
||||
if (nod->_delay <= 0) {
|
||||
nod->_delay += _frmDelayOverride;
|
||||
needsUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsUpdate) {
|
||||
const Graphics::Surface *frame = _animation->decodeNextFrame();
|
||||
|
||||
if (frame) {
|
||||
int dstw;
|
||||
int dsth;
|
||||
if (isPanorama) {
|
||||
dstw = nod->pos.height();
|
||||
dsth = nod->pos.width();
|
||||
} else {
|
||||
dstw = nod->pos.width();
|
||||
dsth = nod->pos.height();
|
||||
}
|
||||
|
||||
// We only scale down the animation to fit its frame, not up, otherwise we
|
||||
// end up with distorted animations - e.g. the armor visor in location cz1e
|
||||
// in Nemesis (one of the armors inside Irondune), or the planet in location
|
||||
// aa10 in Nemesis (Juperon, outside the asylum). We do allow scaling up only
|
||||
// when a simple 2x filter is requested (e.g. the alchemists and cup sequence
|
||||
// in Nemesis)
|
||||
if (frame->w > dstw || frame->h > dsth || (frame->w == dstw / 2 && frame->h == dsth / 2)) {
|
||||
if (nod->_scaled)
|
||||
if (nod->_scaled->w != dstw || nod->_scaled->h != dsth) {
|
||||
nod->_scaled->free();
|
||||
delete nod->_scaled;
|
||||
nod->_scaled = NULL;
|
||||
}
|
||||
|
||||
if (!nod->_scaled) {
|
||||
nod->_scaled = new Graphics::Surface;
|
||||
nod->_scaled->create(dstw, dsth, frame->format);
|
||||
}
|
||||
|
||||
renderManager->scaleBuffer(frame->getPixels(), nod->_scaled->getPixels(), frame->w, frame->h, frame->format.bytesPerPixel, dstw, dsth);
|
||||
frame = nod->_scaled;
|
||||
}
|
||||
|
||||
if (isPanorama) {
|
||||
Graphics::Surface *transposed = RenderManager::tranposeSurface(frame);
|
||||
renderManager->blitSurfaceToBkg(*transposed, nod->pos.left, nod->pos.top, _mask);
|
||||
transposed->free();
|
||||
delete transposed;
|
||||
} else {
|
||||
renderManager->blitSurfaceToBkg(*frame, nod->pos.left, nod->pos.top, _mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AnimationEffect::addPlayNode(int32 slot, int x, int y, int x2, int y2, int startFrame, int endFrame, int loops) {
|
||||
playnode nod;
|
||||
nod.loop = loops;
|
||||
nod.pos = Common::Rect(x, y, x2 + 1, y2 + 1);
|
||||
nod.start = startFrame;
|
||||
nod.stop = CLIP<int>(endFrame, 0, _animation->getFrameCount() - 1);
|
||||
nod.slot = slot;
|
||||
nod._curFrame = -1;
|
||||
nod._delay = 0;
|
||||
nod._scaled = NULL;
|
||||
_playList.push_back(nod);
|
||||
}
|
||||
|
||||
bool AnimationEffect::stop() {
|
||||
PlayNodes::iterator it = _playList.begin();
|
||||
if (it != _playList.end()) {
|
||||
_engine->getScriptManager()->setStateValue((*it).slot, 2);
|
||||
if ((*it)._scaled) {
|
||||
(*it)._scaled->free();
|
||||
delete(*it)._scaled;
|
||||
}
|
||||
}
|
||||
|
||||
_playList.clear();
|
||||
|
||||
// We don't need to delete, it's may be reused
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
79
engines/zvision/scripting/effects/animation_effect.h
Normal file
79
engines/zvision/scripting/effects/animation_effect.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_ANIMATION_NODE_H
|
||||
#define ZVISION_ANIMATION_NODE_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/path.h"
|
||||
#include "common/rect.h"
|
||||
#include "zvision/scripting/scripting_effect.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Video {
|
||||
class VideoDecoder;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
class AnimationEffect : public ScriptingEffect {
|
||||
public:
|
||||
AnimationEffect(ZVision *engine, uint32 controlKey, const Common::Path &fileName, int32 mask, int32 frate, bool disposeAfterUse = true);
|
||||
~AnimationEffect() override;
|
||||
|
||||
struct playnode {
|
||||
Common::Rect pos;
|
||||
int32 slot;
|
||||
int32 start;
|
||||
int32 stop;
|
||||
int32 loop;
|
||||
int32 _curFrame;
|
||||
int32 _delay;
|
||||
Graphics::Surface *_scaled;
|
||||
};
|
||||
|
||||
private:
|
||||
typedef Common::List<playnode> PlayNodes;
|
||||
|
||||
PlayNodes _playList;
|
||||
|
||||
int32 _mask;
|
||||
bool _disposeAfterUse;
|
||||
|
||||
Video::VideoDecoder *_animation;
|
||||
int32 _frmDelayOverride;
|
||||
|
||||
public:
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
void addPlayNode(int32 slot, int x, int y, int x2, int y2, int startFrame, int endFrame, int loops = 1);
|
||||
|
||||
bool stop() override;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
108
engines/zvision/scripting/effects/distort_effect.cpp
Normal file
108
engines/zvision/scripting/effects/distort_effect.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/graphics/render_table.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/effects/distort_effect.h"
|
||||
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
DistortNode::DistortNode(ZVision *engine, uint32 key, int16 speed, float startAngle, float endAngle, float startLineScale, float endLineScale)
|
||||
: ScriptingEffect(engine, key, SCRIPTING_EFFECT_DISTORT) {
|
||||
|
||||
_angle = _engine->getRenderManager()->getRenderTable()->getAngle();
|
||||
_linScale = _engine->getRenderManager()->getRenderTable()->getLinscale();
|
||||
|
||||
_speed = speed;
|
||||
_incr = true;
|
||||
_startAngle = startAngle;
|
||||
_endAngle = endAngle;
|
||||
_startLineScale = startLineScale;
|
||||
_endLineScale = endLineScale;
|
||||
|
||||
_curFrame = 1.0;
|
||||
|
||||
_diffAngle = endAngle - startAngle;
|
||||
_diffLinScale = endLineScale - startLineScale;
|
||||
|
||||
_frmSpeed = (float)speed / 15.0;
|
||||
_frames = (int)ceil((5.0 - _frmSpeed * 2.0) / _frmSpeed);
|
||||
if (_frames <= 0)
|
||||
_frames = 1;
|
||||
|
||||
if (_key != StateKey_NotSet)
|
||||
_engine->getScriptManager()->setStateValue(_key, 1);
|
||||
}
|
||||
|
||||
DistortNode::~DistortNode() {
|
||||
setParams(_angle, _linScale);
|
||||
}
|
||||
|
||||
bool DistortNode::process(uint32 deltaTimeInMillis) {
|
||||
float updTime = deltaTimeInMillis / (1000.0 / 60.0);
|
||||
|
||||
if (_incr)
|
||||
_curFrame += updTime;
|
||||
else
|
||||
_curFrame -= updTime;
|
||||
|
||||
if (_curFrame < 1.0) {
|
||||
_curFrame = 1.0;
|
||||
_incr = true;
|
||||
} else if (_curFrame > _frames) {
|
||||
_curFrame = _frames;
|
||||
_incr = false;
|
||||
}
|
||||
|
||||
float diff = (1.0 / (5.0 - (_curFrame * _frmSpeed))) / (5.0 - _frmSpeed);
|
||||
setParams(_startAngle + diff * _diffAngle, _startLineScale + diff * _diffLinScale);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DistortNode::setParams(float angl, float linScale) {
|
||||
RenderTable *table = _engine->getRenderManager()->getRenderTable();
|
||||
switch (table->getRenderState()) {
|
||||
case RenderTable::PANORAMA: {
|
||||
table->setPanoramaFoV(angl);
|
||||
table->setPanoramaScale(linScale);
|
||||
table->generateRenderTable();
|
||||
_engine->getRenderManager()->markDirty();
|
||||
break;
|
||||
}
|
||||
case RenderTable::TILT: {
|
||||
table->setTiltFoV(angl);
|
||||
table->setTiltScale(linScale);
|
||||
table->generateRenderTable();
|
||||
_engine->getRenderManager()->markDirty();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
62
engines/zvision/scripting/effects/distort_effect.h
Normal file
62
engines/zvision/scripting/effects/distort_effect.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_DISTORT_NODE_H
|
||||
#define ZVISION_DISTORT_NODE_H
|
||||
|
||||
#include "zvision/scripting/scripting_effect.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
class DistortNode : public ScriptingEffect {
|
||||
public:
|
||||
DistortNode(ZVision *engine, uint32 key, int16 speed, float startAngle, float endAngle, float startLineScale, float endLineScale);
|
||||
~DistortNode() override;
|
||||
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
private:
|
||||
int16 _speed;
|
||||
float _startAngle;
|
||||
float _endAngle;
|
||||
float _startLineScale;
|
||||
float _endLineScale;
|
||||
|
||||
float _frmSpeed;
|
||||
float _diffAngle;
|
||||
float _diffLinScale;
|
||||
bool _incr;
|
||||
int16 _frames;
|
||||
|
||||
float _curFrame;
|
||||
|
||||
float _angle;
|
||||
float _linScale;
|
||||
|
||||
private:
|
||||
void setParams(float angl, float linScale);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
290
engines/zvision/scripting/effects/music_effect.cpp
Normal file
290
engines/zvision/scripting/effects/music_effect.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 "audio/decoders/wave.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "math/utils.h"
|
||||
#include "math/angle.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/effects/music_effect.h"
|
||||
#include "zvision/sound/midi.h"
|
||||
#include "zvision/sound/volume_manager.h"
|
||||
#include "zvision/sound/zork_raw.h"
|
||||
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
void MusicNodeBASE::setDirection(Math::Angle azimuth, uint8 magnitude) {
|
||||
if (_engine->getScriptManager()->getStateValue(StateKey_Qsound) >= 1) {
|
||||
_azimuth = azimuth;
|
||||
_directionality = magnitude;
|
||||
_balance = ((int)(127 * _azimuth.getSine()) * _directionality) / 255;
|
||||
} else
|
||||
setBalance(0);
|
||||
updateMixer();
|
||||
}
|
||||
|
||||
void MusicNodeBASE::setBalance(int8 balance) {
|
||||
_balance = balance;
|
||||
_azimuth.setDegrees(0);
|
||||
_directionality = 255;
|
||||
updateMixer();
|
||||
}
|
||||
|
||||
void MusicNodeBASE::updateMixer() {
|
||||
if (_engine->getScriptManager()->getStateValue(StateKey_Qsound) >= 1)
|
||||
_volumeOut = _engine->getVolumeManager()->convert(_volume, _azimuth, _directionality); // Apply game-specific volume profile and then attenuate according to azimuth
|
||||
else
|
||||
_volumeOut = _engine->getVolumeManager()->convert(_volume); // Apply game-specific volume profile and ignore azimuth
|
||||
outputMixer();
|
||||
}
|
||||
|
||||
MusicNode::MusicNode(ZVision *engine, uint32 key, Common::Path &filename, bool loop, uint8 volume)
|
||||
: MusicNodeBASE(engine, key, SCRIPTING_EFFECT_AUDIO) {
|
||||
_loop = loop;
|
||||
_volume = volume;
|
||||
_balance = 0;
|
||||
_fade = false;
|
||||
_fadeStartVol = volume;
|
||||
_fadeEndVol = 0;
|
||||
_fadeTime = 0;
|
||||
_fadeElapsed = 0;
|
||||
_sub = 0;
|
||||
_stereo = false;
|
||||
_loaded = false;
|
||||
|
||||
Audio::RewindableAudioStream *audioStream = NULL;
|
||||
|
||||
if (filename.baseName().contains(".wav")) {
|
||||
Common::File *file = new Common::File();
|
||||
if (file->open(filename)) {
|
||||
audioStream = Audio::makeWAVStream(file, DisposeAfterUse::YES);
|
||||
}
|
||||
} else {
|
||||
audioStream = makeRawZorkStream(filename, _engine);
|
||||
}
|
||||
|
||||
if (audioStream) {
|
||||
_stereo = audioStream->isStereo();
|
||||
|
||||
if (_loop) {
|
||||
Audio::LoopingAudioStream *loopingAudioStream = new Audio::LoopingAudioStream(audioStream, 0, DisposeAfterUse::YES);
|
||||
_engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, loopingAudioStream, -1, _volume);
|
||||
} else {
|
||||
_engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, audioStream, -1, _volume);
|
||||
}
|
||||
|
||||
if (_key != StateKey_NotSet) {
|
||||
debugC(3, kDebugSound, "setting musicnode state value to 1");
|
||||
_engine->getScriptManager()->setStateValue(_key, 1);
|
||||
}
|
||||
|
||||
// Change filename.raw into filename.sub
|
||||
Common::String subname = filename.baseName();
|
||||
subname.setChar('s', subname.size() - 3);
|
||||
subname.setChar('u', subname.size() - 2);
|
||||
subname.setChar('b', subname.size() - 1);
|
||||
|
||||
Common::Path subpath(filename.getParent().appendComponent(subname));
|
||||
if (SearchMan.hasFile(subpath))
|
||||
_sub = _engine->getSubtitleManager()->create(subpath, _handle); // NB automatic subtitle!
|
||||
|
||||
_loaded = true;
|
||||
updateMixer();
|
||||
}
|
||||
debugC(3, kDebugSound, "MusicNode: %d created", _key);
|
||||
}
|
||||
|
||||
MusicNode::~MusicNode() {
|
||||
if (_loaded)
|
||||
_engine->_mixer->stopHandle(_handle);
|
||||
if (_key != StateKey_NotSet)
|
||||
_engine->getScriptManager()->setStateValue(_key, 2);
|
||||
if (_sub)
|
||||
_engine->getSubtitleManager()->destroy(_sub);
|
||||
debugC(3, kDebugSound, "MusicNode: %d destroyed", _key);
|
||||
}
|
||||
|
||||
void MusicNode::outputMixer() {
|
||||
_engine->_mixer->setChannelBalance(_handle, _balance);
|
||||
_engine->_mixer->setChannelVolume(_handle, _volumeOut);
|
||||
}
|
||||
|
||||
void MusicNode::setFade(int32 time, uint8 target) {
|
||||
_fadeStartVol = _volume;
|
||||
_fadeEndVol = target;
|
||||
_fadeElapsed = 0;
|
||||
_fadeTime = time <= 0 ? 0 : (uint32)time;
|
||||
_fade = true;
|
||||
}
|
||||
|
||||
bool MusicNode::process(uint32 deltaTimeInMillis) {
|
||||
if (!_loaded || ! _engine->_mixer->isSoundHandleActive(_handle))
|
||||
return stop();
|
||||
else {
|
||||
if (_fade) {
|
||||
debugC(3, kDebugSound, "Fading music, endVol %d, startVol %d, current %d, fade time %d, elapsed time %dms", _fadeEndVol, _fadeStartVol, _volume, _fadeTime, _fadeElapsed);
|
||||
uint8 _newvol = 0;
|
||||
_fadeElapsed += deltaTimeInMillis;
|
||||
if ((_fadeTime <= 0) | (_fadeElapsed >= _fadeTime)) {
|
||||
_newvol = _fadeEndVol;
|
||||
_fade = false;
|
||||
} else {
|
||||
if (_fadeEndVol > _fadeStartVol)
|
||||
_newvol = _fadeStartVol + (_fadeElapsed * (_fadeEndVol - _fadeStartVol)) / _fadeTime;
|
||||
else
|
||||
_newvol = _fadeStartVol - (_fadeElapsed * (_fadeStartVol - _fadeEndVol)) / _fadeTime;
|
||||
}
|
||||
if (_volume != _newvol)
|
||||
setVolume(_newvol);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MusicNode::setVolume(uint8 newVolume) {
|
||||
if (_loaded) {
|
||||
debugC(4, kDebugSound, "Changing volume of music node %d from %d to %d", _key, _volume, newVolume);
|
||||
_volume = newVolume;
|
||||
updateMixer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PanTrackNode::PanTrackNode(ZVision *engine, uint32 key, uint32 slot, int16 pos, uint8 mag, bool resetMixerOnDelete, bool staticScreen)
|
||||
: ScriptingEffect(engine, key, SCRIPTING_EFFECT_PANTRACK),
|
||||
_slot(slot),
|
||||
_sourcePos(0),
|
||||
_viewPos(0),
|
||||
_mag(mag),
|
||||
_width(0),
|
||||
_pos(pos),
|
||||
_staticScreen(staticScreen),
|
||||
_resetMixerOnDelete(resetMixerOnDelete) {
|
||||
debugC(3, kDebugSound, "Created PanTrackNode, key %d, slot %d", _key, _slot);
|
||||
process(0); // Try to set pan value for music node immediately
|
||||
}
|
||||
|
||||
PanTrackNode::~PanTrackNode() {
|
||||
debugC(1, kDebugSound, "Deleting PanTrackNode, key %d, slot %d", _key, _slot);
|
||||
ScriptManager *scriptManager = _engine->getScriptManager();
|
||||
ScriptingEffect *fx = scriptManager->getSideFX(_slot);
|
||||
if (fx && fx->getType() == SCRIPTING_EFFECT_AUDIO && _resetMixerOnDelete) {
|
||||
debugC(1, kDebugSound, "Resetting mixer, slot %d", _slot);
|
||||
MusicNodeBASE *mus = (MusicNodeBASE *)fx;
|
||||
mus->setBalance(0);
|
||||
} else
|
||||
debugC(1, kDebugSound, "NOT resetting mixer, slot %d", _slot);
|
||||
}
|
||||
|
||||
bool PanTrackNode::process(uint32 deltaTimeInMillis) {
|
||||
debugC(3, kDebugSound, "Processing PanTrackNode, key %d", _key);
|
||||
ScriptManager *scriptManager = _engine->getScriptManager();
|
||||
ScriptingEffect *fx = scriptManager->getSideFX(_slot);
|
||||
if (fx && fx->getType() == SCRIPTING_EFFECT_AUDIO) {
|
||||
MusicNodeBASE *mus = (MusicNodeBASE *)fx;
|
||||
if (!_staticScreen)
|
||||
// Original game scripted behaviour
|
||||
switch (_engine->getRenderManager()->getRenderTable()->getRenderState()) {
|
||||
case RenderTable::PANORAMA:
|
||||
debugC(3, kDebugSound, "PanTrackNode in panorama mode");
|
||||
_width = _engine->getRenderManager()->getBkgSize().x;
|
||||
if (_width) {
|
||||
_sourcePos.setDegrees(360 * _pos / _width);
|
||||
_viewPos.setDegrees(360 * scriptManager->getStateValue(StateKey_ViewPos) / _width);
|
||||
} else {
|
||||
warning("Encountered zero background width whilst processing PanTrackNode in panoramic mode!");
|
||||
}
|
||||
break;
|
||||
case RenderTable::FLAT:
|
||||
case RenderTable::TILT:
|
||||
default:
|
||||
debugC(3, kDebugSound, "PanTrackNode in FLAT/TILT mode");
|
||||
_sourcePos.setDegrees(0);
|
||||
_viewPos.setDegrees(0);
|
||||
break;
|
||||
} else {
|
||||
// Used for auxiliary scripts only
|
||||
_sourcePos.setDegrees(_pos);
|
||||
_viewPos.setDegrees(0);
|
||||
}
|
||||
Math::Angle azimuth;
|
||||
azimuth = _sourcePos - _viewPos;
|
||||
debugC(3, kDebugSound, "soundPos: %f, _viewPos: %f, azimuth: %f, width %d", _sourcePos.getDegrees(), _viewPos.getDegrees(), azimuth.getDegrees(), _width);
|
||||
// azimuth is sound source position relative to player, clockwise from centre of camera axis to front when viewed top-down
|
||||
mus->setDirection(azimuth, _mag);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MusicMidiNode::MusicMidiNode(ZVision *engine, uint32 key, uint8 program, uint8 note, uint8 volume)
|
||||
: MusicNodeBASE(engine, key, SCRIPTING_EFFECT_AUDIO) {
|
||||
_volume = volume;
|
||||
_prog = program;
|
||||
_noteNumber = note;
|
||||
_pan = 0;
|
||||
|
||||
_chan = _engine->getMidiManager()->getFreeChannel();
|
||||
|
||||
if (_chan >= 0) {
|
||||
updateMixer();
|
||||
_engine->getMidiManager()->setProgram(_chan, _prog);
|
||||
_engine->getMidiManager()->noteOn(_chan, _noteNumber, _volume);
|
||||
}
|
||||
|
||||
if (_key != StateKey_NotSet)
|
||||
_engine->getScriptManager()->setStateValue(_key, 1);
|
||||
}
|
||||
|
||||
MusicMidiNode::~MusicMidiNode() {
|
||||
if (_chan >= 0) {
|
||||
_engine->getMidiManager()->noteOff(_chan);
|
||||
}
|
||||
if (_key != StateKey_NotSet)
|
||||
_engine->getScriptManager()->setStateValue(_key, 2);
|
||||
}
|
||||
|
||||
void MusicMidiNode::setFade(int32 time, uint8 target) {
|
||||
}
|
||||
|
||||
bool MusicMidiNode::process(uint32 deltaTimeInMillis) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void MusicMidiNode::setVolume(uint8 newVolume) {
|
||||
if (_chan >= 0) {
|
||||
_volume = newVolume;
|
||||
updateMixer();
|
||||
}
|
||||
}
|
||||
|
||||
void MusicMidiNode::outputMixer() {
|
||||
_engine->getMidiManager()->setBalance(_chan, _balance);
|
||||
_engine->getMidiManager()->setPan(_chan, _pan);
|
||||
_engine->getMidiManager()->setVolume(_chan, _volumeOut);
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
146
engines/zvision/scripting/effects/music_effect.h
Normal file
146
engines/zvision/scripting/effects/music_effect.h
Normal file
@@ -0,0 +1,146 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_MUSIC_NODE_H
|
||||
#define ZVISION_MUSIC_NODE_H
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "math/angle.h"
|
||||
#include "zvision/scripting/scripting_effect.h"
|
||||
#include "zvision/sound/volume_manager.h"
|
||||
#include "zvision/text/subtitle_manager.h"
|
||||
|
||||
namespace Common {
|
||||
class String;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class MusicNodeBASE : public ScriptingEffect {
|
||||
public:
|
||||
MusicNodeBASE(ZVision *engine, uint32 key, ScriptingEffectType type) : ScriptingEffect(engine, key, type) {}
|
||||
~MusicNodeBASE() override {}
|
||||
|
||||
/**
|
||||
* Decrement the timer by the delta time. If the timer is finished, set the status
|
||||
* in _globalState and let this node be deleted
|
||||
*
|
||||
* @param deltaTimeInMillis The number of milliseconds that have passed since last frame
|
||||
* @return If true, the node can be deleted after process() finishes
|
||||
*/
|
||||
bool process(uint32 deltaTimeInMillis) override = 0;
|
||||
|
||||
virtual void setVolume(uint8 volume) = 0;
|
||||
uint8 getVolume() {
|
||||
return _volume;
|
||||
}
|
||||
virtual void setFade(int32 time, uint8 target) = 0;
|
||||
virtual void setBalance(int8 balance); // NB Overrides effects of setDirection()
|
||||
void setDirection(Math::Angle azimuth, uint8 magnitude = 255); // NB Overrides effects of setBalance()
|
||||
protected:
|
||||
void updateMixer();
|
||||
virtual void outputMixer() = 0;
|
||||
|
||||
uint8 _volume = 0;
|
||||
int8 _balance = 0;
|
||||
Math::Angle _azimuth;
|
||||
uint8 _directionality; // 0 = fully ambient, 255 = fully directional
|
||||
uint8 _volumeOut = 0;
|
||||
};
|
||||
|
||||
class MusicNode : public MusicNodeBASE {
|
||||
public:
|
||||
MusicNode(ZVision *engine, uint32 key, Common::Path &file, bool loop, uint8 volume);
|
||||
~MusicNode() override;
|
||||
|
||||
/**
|
||||
* Decrement the timer by the delta time. If the timer is finished, set the status
|
||||
* in _globalState and let this node be deleted
|
||||
*
|
||||
* @param deltaTimeInMillis The number of milliseconds that have passed since last frame
|
||||
* @return If true, the node can be deleted after process() finishes
|
||||
*/
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
void setVolume(uint8 volume) override;
|
||||
|
||||
void setFade(int32 time, uint8 target) override;
|
||||
|
||||
private:
|
||||
void outputMixer() override;
|
||||
bool _loop;
|
||||
bool _fade;
|
||||
uint8 _fadeStartVol;
|
||||
uint8 _fadeEndVol;
|
||||
uint32 _fadeTime;
|
||||
uint32 _fadeElapsed; // Cumulative time since fade start
|
||||
bool _stereo;
|
||||
Audio::SoundHandle _handle;
|
||||
uint16 _sub;
|
||||
bool _loaded;
|
||||
};
|
||||
|
||||
// Only used by Zork: Nemesis, for the flute and piano puzzles (tj4e and ve6f, as well as vr)
|
||||
class MusicMidiNode : public MusicNodeBASE {
|
||||
public:
|
||||
MusicMidiNode(ZVision *engine, uint32 key, uint8 program, uint8 note, uint8 volume);
|
||||
~MusicMidiNode() override;
|
||||
|
||||
/**
|
||||
* Decrement the timer by the delta time. If the timer is finished, set the status
|
||||
* in _globalState and let this node be deleted
|
||||
*
|
||||
* @param deltaTimeInMillis The number of milliseconds that have passed since last frame
|
||||
* @return If true, the node can be deleted after process() finishes
|
||||
*/
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
void setVolume(uint8 volume) override;
|
||||
|
||||
void setFade(int32 time, uint8 target) override;
|
||||
|
||||
private:
|
||||
void outputMixer() override;
|
||||
int8 _chan;
|
||||
uint8 _noteNumber;
|
||||
int8 _pan;
|
||||
uint8 _prog;
|
||||
};
|
||||
|
||||
class PanTrackNode : public ScriptingEffect {
|
||||
public:
|
||||
PanTrackNode(ZVision *engine, uint32 key, uint32 slot, int16 pos, uint8 mag = 255, bool resetMixerOnDelete = false, bool staticScreen = false);
|
||||
~PanTrackNode() override;
|
||||
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
private:
|
||||
uint32 _slot;
|
||||
int16 _width, _pos;
|
||||
Math::Angle _sourcePos, _viewPos;
|
||||
uint8 _mag;
|
||||
bool _resetMixerOnDelete;
|
||||
bool _staticScreen;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
53
engines/zvision/scripting/effects/region_effect.cpp
Normal file
53
engines/zvision/scripting/effects/region_effect.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/effects/region_effect.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
RegionNode::RegionNode(ZVision *engine, uint32 key, GraphicsEffect *effect, uint32 delay)
|
||||
: ScriptingEffect(engine, key, SCRIPTING_EFFECT_REGION) {
|
||||
_effect = effect;
|
||||
_delay = delay;
|
||||
_timeLeft = 0;
|
||||
}
|
||||
|
||||
RegionNode::~RegionNode() {
|
||||
_engine->getRenderManager()->deleteEffect(_key);
|
||||
}
|
||||
|
||||
bool RegionNode::process(uint32 deltaTimeInMillis) {
|
||||
_timeLeft -= deltaTimeInMillis;
|
||||
|
||||
if (_timeLeft <= 0) {
|
||||
_timeLeft = _delay;
|
||||
if (_effect)
|
||||
_effect->update();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
55
engines/zvision/scripting/effects/region_effect.h
Normal file
55
engines/zvision/scripting/effects/region_effect.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 ZVISION_REGION_NODE_H
|
||||
#define ZVISION_REGION_NODE_H
|
||||
|
||||
#include "graphics/surface.h"
|
||||
#include "zvision/graphics/graphics_effect.h"
|
||||
#include "zvision/scripting/scripting_effect.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
class RegionNode : public ScriptingEffect {
|
||||
public:
|
||||
RegionNode(ZVision *engine, uint32 key, GraphicsEffect *effect, uint32 delay);
|
||||
~RegionNode() override;
|
||||
|
||||
/**
|
||||
* Decrement the timer by the delta time. If the timer is finished, set the status
|
||||
* in _globalState and let this node be deleted
|
||||
*
|
||||
* @param deltaTimeInMillis The number of milliseconds that have passed since last frame
|
||||
* @return If true, the node can be deleted after process() finishes
|
||||
*/
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
private:
|
||||
int32 _timeLeft;
|
||||
uint32 _delay;
|
||||
GraphicsEffect *_effect;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
80
engines/zvision/scripting/effects/syncsound_effect.cpp
Normal file
80
engines/zvision/scripting/effects/syncsound_effect.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/decoders/wave.h"
|
||||
#include "common/file.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/effects/syncsound_effect.h"
|
||||
#include "zvision/sound/zork_raw.h"
|
||||
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
SyncSoundNode::SyncSoundNode(ZVision *engine, uint32 key, Common::Path &filename, int32 syncto)
|
||||
: ScriptingEffect(engine, key, SCRIPTING_EFFECT_AUDIO) {
|
||||
_syncto = syncto;
|
||||
_sub = 0;
|
||||
|
||||
Audio::RewindableAudioStream *audioStream = NULL;
|
||||
|
||||
if (filename.baseName().contains(".wav")) {
|
||||
Common::File *file = new Common::File();
|
||||
if (file->open(filename)) {
|
||||
audioStream = Audio::makeWAVStream(file, DisposeAfterUse::YES);
|
||||
}
|
||||
} else {
|
||||
audioStream = makeRawZorkStream(filename, _engine);
|
||||
}
|
||||
|
||||
_engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, audioStream);
|
||||
|
||||
Common::String subname = filename.baseName();
|
||||
subname.setChar('s', subname.size() - 3);
|
||||
subname.setChar('u', subname.size() - 2);
|
||||
subname.setChar('b', subname.size() - 1);
|
||||
|
||||
Common::Path subpath(filename.getParent().appendComponent(subname));
|
||||
if (SearchMan.hasFile(subpath))
|
||||
_sub = _engine->getSubtitleManager()->create(subpath, _handle); // NB automatic subtitle!
|
||||
}
|
||||
|
||||
SyncSoundNode::~SyncSoundNode() {
|
||||
_engine->_mixer->stopHandle(_handle);
|
||||
if (_sub)
|
||||
_engine->getSubtitleManager()->destroy(_sub);
|
||||
}
|
||||
|
||||
bool SyncSoundNode::process(uint32 deltaTimeInMillis) {
|
||||
if (! _engine->_mixer->isSoundHandleActive(_handle))
|
||||
return stop();
|
||||
else {
|
||||
|
||||
if (_engine->getScriptManager()->getSideFX(_syncto) == NULL)
|
||||
return stop();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
55
engines/zvision/scripting/effects/syncsound_effect.h
Normal file
55
engines/zvision/scripting/effects/syncsound_effect.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 ZVISION_SYNCSOUND_NODE_H
|
||||
#define ZVISION_SYNCSOUND_NODE_H
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "zvision/scripting/scripting_effect.h"
|
||||
#include "zvision/text/subtitle_manager.h"
|
||||
|
||||
namespace Common {
|
||||
class String;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
class SyncSoundNode : public ScriptingEffect {
|
||||
public:
|
||||
SyncSoundNode(ZVision *engine, uint32 key, Common::Path &file, int32 syncto);
|
||||
~SyncSoundNode() override;
|
||||
|
||||
/**
|
||||
* Decrement the timer by the delta time. If the timer is finished, set the status
|
||||
* in _globalState and let this node be deleted
|
||||
*
|
||||
* @param deltaTimeInMillis The number of milliseconds that have passed since last frame
|
||||
* @return If true, the node can be deleted after process() finishes
|
||||
*/
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
private:
|
||||
int32 _syncto;
|
||||
Audio::SoundHandle _handle;
|
||||
uint16 _sub;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
77
engines/zvision/scripting/effects/timer_effect.cpp
Normal file
77
engines/zvision/scripting/effects/timer_effect.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/effects/timer_effect.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
TimerNode::TimerNode(ZVision *engine, uint32 key, uint timeInSeconds)
|
||||
: ScriptingEffect(engine, key, SCRIPTING_EFFECT_TIMER) {
|
||||
_timeLeft = 0;
|
||||
|
||||
if (_engine->getGameId() == GID_NEMESIS)
|
||||
_timeLeft = timeInSeconds * 1000;
|
||||
else if (_engine->getGameId() == GID_GRANDINQUISITOR)
|
||||
_timeLeft = timeInSeconds * 100;
|
||||
|
||||
if (_key != StateKey_NotSet)
|
||||
_engine->getScriptManager()->setStateValue(_key, 1);
|
||||
}
|
||||
|
||||
TimerNode::~TimerNode() {
|
||||
if (_key != StateKey_NotSet)
|
||||
_engine->getScriptManager()->setStateValue(_key, 2);
|
||||
int32 timeLeft = _timeLeft / (_engine->getGameId() == GID_NEMESIS ? 1000 : 100);
|
||||
if (timeLeft > 0)
|
||||
_engine->getScriptManager()->setStateValue(_key, timeLeft); // If timer was stopped by stop or kill
|
||||
}
|
||||
|
||||
bool TimerNode::process(uint32 deltaTimeInMillis) {
|
||||
_timeLeft -= deltaTimeInMillis;
|
||||
|
||||
if (_timeLeft <= 0)
|
||||
return stop();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TimerNode::stop() {
|
||||
if (_key != StateKey_NotSet)
|
||||
_engine->getScriptManager()->setStateValue(_key, 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TimerNode::serialize(Common::WriteStream *stream) {
|
||||
stream->writeUint32BE(MKTAG('T', 'I', 'M', 'R'));
|
||||
stream->writeUint32LE(8); // size
|
||||
stream->writeUint32LE(_key);
|
||||
stream->writeUint32LE(_timeLeft);
|
||||
}
|
||||
|
||||
void TimerNode::deserialize(Common::SeekableReadStream *stream) {
|
||||
_timeLeft = stream->readUint32LE();
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
58
engines/zvision/scripting/effects/timer_effect.h
Normal file
58
engines/zvision/scripting/effects/timer_effect.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_TIMER_NODE_H
|
||||
#define ZVISION_TIMER_NODE_H
|
||||
|
||||
#include "zvision/scripting/scripting_effect.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
class TimerNode : public ScriptingEffect {
|
||||
public:
|
||||
TimerNode(ZVision *engine, uint32 key, uint timeInSeconds);
|
||||
~TimerNode() override;
|
||||
|
||||
/**
|
||||
* Decrement the timer by the delta time. If the timer is finished, set the status
|
||||
* in _globalState and let this node be deleted
|
||||
*
|
||||
* @param deltaTimeInMillis The number of milliseconds that have passed since last frame
|
||||
* @return If true, the node can be deleted after process() finishes
|
||||
*/
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
void serialize(Common::WriteStream *stream) override;
|
||||
void deserialize(Common::SeekableReadStream *stream) override;
|
||||
inline bool needsSerialization() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool stop() override;
|
||||
|
||||
private:
|
||||
int32 _timeLeft;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
199
engines/zvision/scripting/effects/ttytext_effect.cpp
Normal file
199
engines/zvision/scripting/effects/ttytext_effect.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/unicode-bidi.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/effects/ttytext_effect.h"
|
||||
#include "zvision/text/text.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
ttyTextNode::ttyTextNode(ZVision *engine, uint32 key, const Common::Path &file, const Common::Rect &r, int32 delay) :
|
||||
ScriptingEffect(engine, key, SCRIPTING_EFFECT_TTYTXT),
|
||||
_fnt(engine) {
|
||||
_delay = delay;
|
||||
_r = r;
|
||||
_txtpos = 0;
|
||||
_nexttime = 0;
|
||||
_dx = 0;
|
||||
_dy = 0;
|
||||
_lineStartPos = 0;
|
||||
_startX = 0;
|
||||
|
||||
Common::File *infile = new Common::File;
|
||||
infile->open(file);
|
||||
if (infile) {
|
||||
while (!infile->eos()) {
|
||||
Common::U32String asciiLine = readWideLine(*infile);
|
||||
if (asciiLine.empty()) {
|
||||
continue;
|
||||
}
|
||||
_txtbuf += asciiLine;
|
||||
}
|
||||
|
||||
delete infile;
|
||||
}
|
||||
_isRTL = Common::convertBiDiU32String(_txtbuf).visual != _txtbuf;
|
||||
_img.create(_r.width(), _r.height(), _engine->_resourcePixelFormat);
|
||||
_state._sharp = true;
|
||||
_state.readAllStyles(_txtbuf.encode());
|
||||
_state.updateFontWithTextState(_fnt);
|
||||
_engine->getScriptManager()->setStateValue(_key, 1);
|
||||
}
|
||||
|
||||
ttyTextNode::~ttyTextNode() {
|
||||
_engine->getScriptManager()->setStateValue(_key, 2);
|
||||
_img.free();
|
||||
}
|
||||
|
||||
bool ttyTextNode::process(uint32 deltaTimeInMillis) {
|
||||
_nexttime -= deltaTimeInMillis;
|
||||
|
||||
if (_nexttime < 0) {
|
||||
if (_txtpos < _txtbuf.size()) {
|
||||
if (_txtbuf[_txtpos] == '<') {
|
||||
int32 start = _txtpos;
|
||||
int32 end = 0;
|
||||
int16 ret = 0;
|
||||
while (_txtbuf[_txtpos] != '>' && _txtpos < _txtbuf.size())
|
||||
_txtpos++;
|
||||
end = _txtpos;
|
||||
if (start != -1) {
|
||||
if ((end - start - 1) > 0) {
|
||||
Common::String buf = _txtbuf.substr(start + 1, end - start - 1);
|
||||
ret = _state.parseStyle(buf, buf.size());
|
||||
}
|
||||
}
|
||||
|
||||
if (ret & (TEXT_CHANGE_FONT_TYPE | TEXT_CHANGE_FONT_STYLE)) {
|
||||
_state.updateFontWithTextState(_fnt);
|
||||
} else if (ret & TEXT_CHANGE_NEWLINE) {
|
||||
newline();
|
||||
}
|
||||
|
||||
if (ret & TEXT_CHANGE_HAS_STATE_BOX) {
|
||||
Common::String buf;
|
||||
buf = Common::String::format("%d", _engine->getScriptManager()->getStateValue(_state._statebox));
|
||||
|
||||
if (_isRTL) {
|
||||
int16 currDx = _dx + _fnt.getStringWidth(buf);
|
||||
_dx = _r.width() - currDx;
|
||||
_isRTL = false;
|
||||
for (uint8 j = 0; j < buf.size(); j++)
|
||||
outchar(buf[j]);
|
||||
_isRTL = true;
|
||||
_dx = currDx;
|
||||
} else {
|
||||
for (uint8 j = 0; j < buf.size(); j++)
|
||||
outchar(buf[j]);
|
||||
}
|
||||
}
|
||||
|
||||
_txtpos++;
|
||||
_lineStartPos = _txtpos;
|
||||
_startX = _dx;
|
||||
} else {
|
||||
uint32 pos = _lineStartPos;
|
||||
int16 dx = _startX;
|
||||
|
||||
while (pos < _txtbuf.size() && _txtbuf[pos] != '<') {
|
||||
uint16 chr = _txtbuf[pos];
|
||||
|
||||
if (chr == ' ') {
|
||||
uint32 i = pos + 1;
|
||||
uint16 width = _fnt.getCharWidth(chr);
|
||||
|
||||
while (i < _txtbuf.size() && _txtbuf[i] != ' ' && _txtbuf[i] != '<') {
|
||||
|
||||
uint16 uchr = _txtbuf[i];
|
||||
|
||||
width += _fnt.getCharWidth(uchr);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (dx + width > _r.width())
|
||||
break;
|
||||
}
|
||||
dx += _fnt.getCharWidth(chr);
|
||||
pos++;
|
||||
}
|
||||
|
||||
Common::U32String lineBuffer = Common::convertBiDiU32String(_txtbuf.substr(_lineStartPos, pos - _lineStartPos)).visual;
|
||||
if (pos == _txtpos)
|
||||
newline();
|
||||
else if (_isRTL)
|
||||
outchar(lineBuffer[pos - _txtpos - 1]);
|
||||
else
|
||||
outchar(lineBuffer[_txtpos - _lineStartPos]);
|
||||
|
||||
_txtpos++;
|
||||
}
|
||||
_nexttime = _delay;
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(_img, _r.left, _r.top);
|
||||
} else
|
||||
return stop();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ttyTextNode::scroll() {
|
||||
int32 scrl = 0;
|
||||
while (_dy - scrl > _r.height() - _fnt.getFontHeight())
|
||||
scrl += _fnt.getFontHeight();
|
||||
int8 *pixels = (int8 *)_img.getPixels();
|
||||
for (uint16 h = scrl; h < _img.h; h++)
|
||||
memcpy(pixels + _img.pitch * (h - scrl), pixels + _img.pitch * h, _img.pitch);
|
||||
|
||||
_img.fillRect(Common::Rect(0, _img.h - scrl, _img.w, _img.h), 0);
|
||||
_dy -= scrl;
|
||||
}
|
||||
|
||||
void ttyTextNode::newline() {
|
||||
_dy += _fnt.getFontHeight();
|
||||
_dx = 0;
|
||||
_lineStartPos = _txtpos + 1;
|
||||
_startX = _dx;
|
||||
}
|
||||
|
||||
void ttyTextNode::outchar(uint16 chr) {
|
||||
uint32 clr = _engine->_resourcePixelFormat.RGBToColor(_state._red, _state._green, _state._blue);
|
||||
|
||||
if (_dx + _fnt.getCharWidth(chr) > _r.width())
|
||||
newline();
|
||||
|
||||
if (_dy + _fnt.getFontHeight() >= _r.height())
|
||||
scroll();
|
||||
|
||||
if (_isRTL)
|
||||
_fnt.drawChar(&_img, chr, _r.width() - _dx - _fnt.getCharWidth(chr), _dy, clr);
|
||||
else
|
||||
_fnt.drawChar(&_img, chr, _dx, _dy, clr);
|
||||
_dx += _fnt.getCharWidth(chr);
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
74
engines/zvision/scripting/effects/ttytext_effect.h
Normal file
74
engines/zvision/scripting/effects/ttytext_effect.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_TTYTEXT_NODE_H
|
||||
#define ZVISION_TTYTEXT_NODE_H
|
||||
|
||||
#include "common/rect.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "zvision/scripting/scripting_effect.h"
|
||||
#include "zvision/text/text.h"
|
||||
#include "zvision/text/truetype_font.h"
|
||||
|
||||
namespace Common {
|
||||
class String;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
class ttyTextNode : public ScriptingEffect {
|
||||
public:
|
||||
ttyTextNode(ZVision *engine, uint32 key, const Common::Path &file, const Common::Rect &r, int32 delay);
|
||||
~ttyTextNode() override;
|
||||
|
||||
/**
|
||||
* Decrement the timer by the delta time. If the timer is finished, set the status
|
||||
* in _globalState and let this node be deleted
|
||||
*
|
||||
* @param deltaTimeInMillis The number of milliseconds that have passed since last frame
|
||||
* @return If true, the node can be deleted after process() finishes
|
||||
*/
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
private:
|
||||
Common::Rect _r;
|
||||
|
||||
TextStyleState _state;
|
||||
StyledTTFont _fnt;
|
||||
Common::U32String _txtbuf;
|
||||
uint32 _txtpos;
|
||||
uint32 _lineStartPos;
|
||||
bool _isRTL;
|
||||
|
||||
int32 _delay;
|
||||
int32 _nexttime;
|
||||
Graphics::Surface _img;
|
||||
int16 _dx;
|
||||
int16 _startX;
|
||||
int16 _dy;
|
||||
private:
|
||||
|
||||
void newline();
|
||||
void scroll();
|
||||
void outchar(uint16 chr);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
120
engines/zvision/scripting/inventory.cpp
Normal file
120
engines/zvision/scripting/inventory.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
int8 ScriptManager::inventoryGetCount() {
|
||||
return getStateValue(StateKey_Inv_Cnt_Slot);
|
||||
}
|
||||
|
||||
void ScriptManager::inventorySetCount(int8 cnt) {
|
||||
setStateValue(StateKey_Inv_Cnt_Slot, cnt);
|
||||
}
|
||||
|
||||
int16 ScriptManager::inventoryGetItem(int8 id) {
|
||||
if (id < 49 && id >= 0)
|
||||
return getStateValue(StateKey_Inv_1_Slot + id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ScriptManager::inventorySetItem(int8 id, int16 item) {
|
||||
if (id < 49 && id >= 0)
|
||||
setStateValue(StateKey_Inv_1_Slot + id, item);
|
||||
}
|
||||
|
||||
void ScriptManager::inventoryAdd(int16 item) {
|
||||
int8 cnt = inventoryGetCount();
|
||||
|
||||
if (cnt < 49) {
|
||||
bool notExist = true;
|
||||
|
||||
if (cnt == 0) {
|
||||
inventorySetItem(0, 0);
|
||||
inventorySetCount(1); // we needed empty item for cycle code
|
||||
cnt = 1;
|
||||
}
|
||||
|
||||
for (int8 cur = 0; cur < cnt; cur++)
|
||||
if (inventoryGetItem(cur) == item) {
|
||||
notExist = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (notExist) {
|
||||
for (int8 i = cnt; i > 0; i--)
|
||||
inventorySetItem(i, inventoryGetItem(i - 1));
|
||||
|
||||
inventorySetItem(0, item);
|
||||
|
||||
setStateValue(StateKey_InventoryItem, item);
|
||||
|
||||
inventorySetCount(cnt + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::inventoryDrop(int16 item) {
|
||||
int8 itemCount = inventoryGetCount();
|
||||
|
||||
// if items in inventory > 0
|
||||
if (itemCount != 0) {
|
||||
int8 index = 0;
|
||||
|
||||
// finding needed item
|
||||
while (index < itemCount) {
|
||||
if (inventoryGetItem(index) == item)
|
||||
break;
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
// if item in the inventory
|
||||
if (itemCount != index) {
|
||||
// shift all items left with rewrite founded item
|
||||
for (int8 v = index; v < itemCount - 1 ; v++)
|
||||
inventorySetItem(v, inventoryGetItem(v + 1));
|
||||
|
||||
// del last item
|
||||
inventorySetItem(itemCount - 1, 0);
|
||||
inventorySetCount(inventoryGetCount() - 1);
|
||||
|
||||
setStateValue(StateKey_InventoryItem, inventoryGetItem(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
void ScriptManager::inventoryCycle() {
|
||||
int8 itemCount = inventoryGetCount();
|
||||
int8 curItem = inventoryGetItem(0);
|
||||
if (itemCount > 1) {
|
||||
for (int8 i = 0; i < itemCount - 1; i++)
|
||||
inventorySetItem(i, inventoryGetItem(i + 1));
|
||||
|
||||
inventorySetItem(itemCount - 1, curItem);
|
||||
|
||||
setStateValue(StateKey_InventoryItem, inventoryGetItem(0));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
592
engines/zvision/scripting/menu.cpp
Normal file
592
engines/zvision/scripting/menu.cpp
Normal file
@@ -0,0 +1,592 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "zvision/detection.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/menu.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
enum {
|
||||
kFocusNone = -1,
|
||||
kFocusItems = 0,
|
||||
kFocusMagic = 1,
|
||||
kFocusMain = 2
|
||||
};
|
||||
|
||||
MenuManager::MenuManager(ZVision *engine, const Common::Rect menuArea, const MenuParams params) :
|
||||
_engine(engine),
|
||||
_params(params),
|
||||
_menuBarFlag(0xFFFF),
|
||||
_menuArea(menuArea),
|
||||
_menuOrigin(menuArea.origin()),
|
||||
_menuTriggerArea(_menuOrigin, _menuArea.width(), _params.triggerHeight),
|
||||
_mainScroller(params.activePos, params.idlePos, params.period) {
|
||||
|
||||
_enableFlags.set_size(6);
|
||||
for (int8 i = 0; i < 4; i++) {
|
||||
// Generate button hotspot areas
|
||||
_menuHotspots[i] = Common::Rect(_params.wxButs[i][1], _menuArea.top, _params.wxButs[i][1] + _params.wxButs[i][0], _menuArea.bottom);
|
||||
// Initialise button animation frames
|
||||
_mainFrames[i] = _params.idleFrame;
|
||||
}
|
||||
for (int i = 0; i < 4; i++)
|
||||
_buttonAnim[i] = new LinearScroller(_params.activeFrame, _params.idleFrame, _params.buttonPeriod);
|
||||
setFocus(kFocusNone); // Ensure focus list is initialised
|
||||
_mainArea = Common::Rect(_params.wMain, _hMainMenu);
|
||||
_mainArea.moveTo(_menuOrigin + _mainScroller._pos);
|
||||
}
|
||||
|
||||
MenuManager::~MenuManager() {
|
||||
for (int i = 0; i < 4; i++)
|
||||
delete _buttonAnim[i];
|
||||
_mainBack.free();
|
||||
}
|
||||
|
||||
void MenuManager::setEnable(uint16 flags) {
|
||||
static const uint16 flagMasks[6] = {0x8, 0x4, 0x2, 0x1, 0x100, 0x200}; // Enum order: save,restore,prefs,quit,items,magic
|
||||
_menuBarFlag = flags;
|
||||
for (uint i = 0; i <= 5; i++) {
|
||||
if (_menuBarFlag & flagMasks[i])
|
||||
_enableFlags.set(i);
|
||||
else
|
||||
_enableFlags.unset(i);
|
||||
}
|
||||
}
|
||||
|
||||
void MenuManager::onMouseUp(const Common::Point &pos) {
|
||||
if (_menuFocus.front() == kFocusMain) {
|
||||
_mouseOnItem = mouseOverMain(pos);
|
||||
if (_mouseOnItem == _mainClicked)
|
||||
// Activate clicked action from main menu
|
||||
switch (_mouseOnItem) {
|
||||
case kMainMenuSave:
|
||||
_engine->getScriptManager()->changeLocation('g', 'j', 's', 'e', 0);
|
||||
setFocus(kFocusNone);
|
||||
_mainScroller.reset();
|
||||
_redraw = true;
|
||||
break;
|
||||
case kMainMenuLoad:
|
||||
_engine->getScriptManager()->changeLocation('g', 'j', 'r', 'e', 0);
|
||||
setFocus(kFocusNone);
|
||||
_mainScroller.reset();
|
||||
_redraw = true;
|
||||
break;
|
||||
case kMainMenuPrefs:
|
||||
_engine->getScriptManager()->changeLocation('g', 'j', 'p', 'e', 0);
|
||||
setFocus(kFocusNone);
|
||||
_mainScroller.reset();
|
||||
_redraw = true;
|
||||
break;
|
||||
case kMainMenuExit:
|
||||
_engine->quit(true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
_mainClicked = -1;
|
||||
}
|
||||
|
||||
void MenuManager::onMouseDown(const Common::Point &pos) {
|
||||
if (_menuFocus.front() == kFocusMain) {
|
||||
_mouseOnItem = mouseOverMain(pos);
|
||||
// Show clicked graphic
|
||||
if ((_mouseOnItem >= 0) && (_mouseOnItem < 4))
|
||||
if (_enableFlags.get(_mouseOnItem)) {
|
||||
_mainClicked = _mouseOnItem;
|
||||
_redraw = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MenuManager::onMouseMove(const Common::Point &pos) {
|
||||
bool nowInMenu = inMenu(pos);
|
||||
if (nowInMenu != _prevInMenu)
|
||||
_redraw = true;
|
||||
_prevInMenu = nowInMenu;
|
||||
|
||||
int lastItem = _mouseOnItem;
|
||||
|
||||
switch (_menuFocus.front()) {
|
||||
case kFocusMain:
|
||||
// Inform game scripting engine that mouse is in main menu
|
||||
if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 2)
|
||||
_engine->getScriptManager()->setStateValue(StateKey_MenuState, 2);
|
||||
_mouseOnItem = mouseOverMain(pos);
|
||||
break;
|
||||
case kFocusNone:
|
||||
// Inform game scripting engine that mouse is not in any menu
|
||||
if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 0)
|
||||
_engine->getScriptManager()->setStateValue(StateKey_MenuState, 0);
|
||||
_mouseOnItem = -1;
|
||||
break;
|
||||
}
|
||||
_mainScroller.setActive(_menuFocus.front() == kFocusMain);
|
||||
// Update button animation status
|
||||
for (int i = 0; i < 4; i++)
|
||||
if (_menuFocus[0] == kFocusMain && _mouseOnItem == i)
|
||||
_buttonAnim[i]->setActive(true);
|
||||
else
|
||||
_buttonAnim[i]->setActive(false);
|
||||
if (lastItem != _mouseOnItem)
|
||||
_redraw = true;
|
||||
}
|
||||
|
||||
int MenuManager::mouseOverMain(const Common::Point &pos) {
|
||||
// Common::Rect mainHotspot(28,_hSideMenu);
|
||||
// mainHotspot.moveTo(mainOrigin + _mainScroller._pos);
|
||||
for (int8 i = 0; i < 4; i++) {
|
||||
if (_enableFlags.get(i) && _menuHotspots[i].contains(pos))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void MenuManager::process(uint32 deltatime) {
|
||||
if (_mainScroller.update(deltatime)) {
|
||||
_mainArea.moveTo(_menuOrigin + _mainScroller._pos);
|
||||
for (int i = 0; i < 4; i++)
|
||||
_menuHotspots[i].moveTo(_menuOrigin + Common::Point(_params.wxButs[i][1], _mainScroller._pos.y));
|
||||
_redraw = true;
|
||||
}
|
||||
// Update button highlight animation frame
|
||||
for (int i = 0; i < 4; i++)
|
||||
if (_buttonAnim[i]->update(deltatime)) {
|
||||
_mainFrames[i] = _buttonAnim[i]->_pos;
|
||||
_redraw = true;
|
||||
}
|
||||
if (_redraw) {
|
||||
_engine->getRenderManager()->clearMenuSurface();
|
||||
redrawAll();
|
||||
_redraw = false;
|
||||
}
|
||||
}
|
||||
|
||||
void MenuNemesis::redrawAll() {
|
||||
redrawMain();
|
||||
}
|
||||
|
||||
void MenuManager::redrawMain() {
|
||||
// Draw menu background
|
||||
_engine->getRenderManager()->blitSurfaceToMenu(_mainBack, _mainScroller._pos.x, _mainScroller._pos.y, 0);
|
||||
// Draw buttons
|
||||
if (_menuFocus.front() == kFocusMain)
|
||||
for (int8 i = 0; i < 4; i++) {
|
||||
if (_enableFlags.get(i) && (_mainFrames[i] >= 0)) {
|
||||
if (_mainClicked == i)
|
||||
_engine->getRenderManager()->blitSurfaceToMenu(_mainButtons[i][_params.clickedFrame], _params.wxButs[i][1], _mainScroller._pos.y, 0);
|
||||
else
|
||||
_engine->getRenderManager()->blitSurfaceToMenu(_mainButtons[i][_mainFrames[i]], _params.wxButs[i][1], _mainScroller._pos.y, 0);
|
||||
}
|
||||
}
|
||||
_clean = false;
|
||||
}
|
||||
|
||||
void MenuManager::setFocus(int8 currentFocus) {
|
||||
_menuFocus.set(currentFocus);
|
||||
assert(_menuFocus.size() <= 4);
|
||||
}
|
||||
|
||||
MenuZGI::MenuZGI(ZVision *engine, const Common::Rect menuArea) :
|
||||
MenuManager(engine, menuArea, zgiParams),
|
||||
_itemsScroller(Common::Point(0, 0), Common::Point(_wSideMenuTab - _wSideMenu, 0), _sideMenuPeriod),
|
||||
_magicScroller(Common::Point(-_wSideMenu, 0), Common::Point(-_wSideMenuTab, 0), _sideMenuPeriod),
|
||||
_itemsOrigin(menuArea.left, menuArea.top),
|
||||
_magicOrigin(menuArea.right, menuArea.top) {
|
||||
|
||||
_magicArea = Common::Rect(_magicOrigin + _magicScroller._pos, _wSideMenu, _hSideMenu);
|
||||
_itemsArea = Common::Rect(_itemsOrigin + _itemsScroller._pos, _wSideMenu, _hSideMenu);
|
||||
|
||||
// Buffer main menu background
|
||||
_engine->getRenderManager()->readImageToSurface("gmzau031.tga", _mainBack, false);
|
||||
|
||||
char buf[24];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
// Buffer menu buttons
|
||||
Common::sprintf_s(buf, "gmzmu%2.2x1.tga", i);
|
||||
_engine->getRenderManager()->readImageToSurface(buf, _mainButtons[i][0], false);
|
||||
Common::sprintf_s(buf, "gmznu%2.2x1.tga", i);
|
||||
_engine->getRenderManager()->readImageToSurface(buf, _mainButtons[i][1], false);
|
||||
}
|
||||
for (int i = 1; i < 4; i++) {
|
||||
// Buffer full menu backgrounds
|
||||
Common::sprintf_s(buf, "gmzau%2.2x1.tga", i);
|
||||
_engine->getRenderManager()->readImageToSurface(buf, _menuBack[i - 1], false);
|
||||
}
|
||||
for (int i = 0; i < 50; i++) {
|
||||
_items[i][0] = NULL;
|
||||
_items[i][1] = NULL;
|
||||
_itemId[i] = 0;
|
||||
}
|
||||
for (int i = 0; i < 12; i++) {
|
||||
_magic[i][0] = NULL;
|
||||
_magic[i][1] = NULL;
|
||||
_magicId[i] = 0;
|
||||
}
|
||||
// Initialise focus sequence
|
||||
setFocus(kFocusMain);
|
||||
setFocus(kFocusMagic);
|
||||
setFocus(kFocusItems);
|
||||
setFocus(kFocusNone);
|
||||
}
|
||||
|
||||
MenuZGI::~MenuZGI() {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
_menuBack[i].free();
|
||||
}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 2; j++)
|
||||
_mainButtons[i][j].free();
|
||||
}
|
||||
for (int i = 0; i < 50; i++) {
|
||||
if (_items[i][0]) {
|
||||
_items[i][0]->free();
|
||||
delete _items[i][0];
|
||||
}
|
||||
if (_items[i][1]) {
|
||||
_items[i][1]->free();
|
||||
delete _items[i][1];
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 12; i++) {
|
||||
if (_magic[i][0]) {
|
||||
_magic[i][0]->free();
|
||||
delete _magic[i][0];
|
||||
}
|
||||
if (_magic[i][1]) {
|
||||
_magic[i][1]->free();
|
||||
delete _magic[i][1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MenuZGI::inMenu(const Common::Point &pos) const {
|
||||
return _menuTriggerArea.contains(pos) || (_menuFocus.front() != kFocusNone);
|
||||
}
|
||||
|
||||
void MenuZGI::onMouseUp(const Common::Point &pos) {
|
||||
if (inMenu(pos))
|
||||
// _redraw = true;
|
||||
switch (_menuFocus.front()) {
|
||||
case kFocusItems:
|
||||
if (_enableFlags.get(kItemsMenu)) {
|
||||
int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots);
|
||||
if (itemCount == 0)
|
||||
itemCount = 20;
|
||||
int i = mouseOverItem(pos, itemCount);
|
||||
if (i != -1) {
|
||||
int32 mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem);
|
||||
if (mouseItem >= 0 && mouseItem < 0xE0) {
|
||||
_engine->getScriptManager()->inventoryDrop(mouseItem);
|
||||
_engine->getScriptManager()->inventoryAdd(_engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + i));
|
||||
_engine->getScriptManager()->setStateValue(StateKey_Inv_StartSlot + i, mouseItem);
|
||||
_redraw = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kFocusMagic:
|
||||
if (_enableFlags.get(kMagicMenu)) {
|
||||
int i = mouseOverMagic(pos);
|
||||
if (i != -1) {
|
||||
|
||||
uint itemnum = _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + i);
|
||||
if (itemnum != 0) {
|
||||
if (_engine->getScriptManager()->getStateValue(StateKey_Reversed_Spellbooc) == 1)
|
||||
itemnum = 0xEE + i;
|
||||
else
|
||||
itemnum = 0xE0 + i;
|
||||
}
|
||||
if (itemnum)
|
||||
if (_engine->getScriptManager()->getStateValue(StateKey_InventoryItem) == 0 || _engine->getScriptManager()->getStateValue(StateKey_InventoryItem) >= 0xE0)
|
||||
_engine->getScriptManager()->setStateValue(StateKey_Active_Spell, itemnum);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case kFocusMain:
|
||||
MenuManager::onMouseUp(pos);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MenuZGI::onMouseMove(const Common::Point &pos) {
|
||||
if (!inMenu(pos)) {
|
||||
_mainScroller.reset();
|
||||
_magicScroller.reset();
|
||||
_itemsScroller.reset();
|
||||
}
|
||||
|
||||
// Set focus to topmost layer of menus that mouse is currently over
|
||||
for (uint8 i = 0; i < _menuFocus.size(); i++) {
|
||||
switch (_menuFocus[i]) {
|
||||
case kFocusItems:
|
||||
if (_itemsArea.contains(pos)) {
|
||||
setFocus(kFocusItems);
|
||||
i = _menuFocus.size() + 1;
|
||||
}
|
||||
break;
|
||||
case kFocusMagic:
|
||||
if (_magicArea.contains(pos)) {
|
||||
setFocus(kFocusMagic);
|
||||
i = _menuFocus.size() + 1;
|
||||
}
|
||||
break;
|
||||
case kFocusMain:
|
||||
if (_mainArea.contains(pos)) {
|
||||
setFocus(kFocusMain);
|
||||
i = _menuFocus.size() + 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
setFocus(kFocusNone);
|
||||
break;
|
||||
}
|
||||
}
|
||||
_itemsScroller.setActive(_menuFocus.front() == kFocusItems);
|
||||
_magicScroller.setActive(_menuFocus.front() == kFocusMagic);
|
||||
if (_menuFocus.front() != kFocusNone) {
|
||||
switch (_menuFocus.front()) {
|
||||
case kFocusItems:
|
||||
if (_enableFlags.get(kItemsMenu)) {
|
||||
int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots);
|
||||
if (itemCount == 0)
|
||||
itemCount = 20;
|
||||
else if (itemCount > 50)
|
||||
itemCount = 50;
|
||||
int lastItem = _mouseOnItem;
|
||||
_mouseOnItem = mouseOverItem(pos, itemCount);
|
||||
if (lastItem != _mouseOnItem)
|
||||
if (_engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + _mouseOnItem) || _engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + lastItem))
|
||||
_redraw = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case kFocusMagic:
|
||||
if (_enableFlags.get(kMagicMenu)) {
|
||||
int lastItem = _mouseOnItem;
|
||||
_mouseOnItem = mouseOverMagic(pos);
|
||||
|
||||
if (lastItem != _mouseOnItem)
|
||||
if (_engine->getScriptManager()->getStateValue(StateKey_Spell_1 + _mouseOnItem) || _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + lastItem))
|
||||
_redraw = true;
|
||||
|
||||
}
|
||||
break;
|
||||
case kFocusMain:
|
||||
break;
|
||||
}
|
||||
}
|
||||
MenuManager::onMouseMove(pos);
|
||||
}
|
||||
|
||||
int MenuZGI::mouseOverItem(const Common::Point &pos, int itemCount) {
|
||||
int itemWidth = (_wSideMenu - 28) / itemCount;
|
||||
Common::Rect itemHotspot = Common::Rect(28, _hSideMenu);
|
||||
itemHotspot.moveTo(_itemsOrigin + _itemsScroller._pos);
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
if (itemHotspot.contains(pos))
|
||||
return i;
|
||||
itemHotspot.translate(itemWidth, 0);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int MenuZGI::mouseOverMagic(const Common::Point &pos) {
|
||||
Common::Rect magicHotspot(28, _hSideMenu);
|
||||
magicHotspot.moveTo(_magicOrigin + _magicScroller._pos);
|
||||
magicHotspot.translate(28, 0); // Offset from end of menu
|
||||
for (int i = 0; i < 12; i++) {
|
||||
if (magicHotspot.contains(pos))
|
||||
return i;
|
||||
magicHotspot.translate(_magicWidth, 0);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void MenuZGI::process(uint32 deltatime) {
|
||||
if (_itemsScroller.update(deltatime)) {
|
||||
_itemsArea.moveTo(_itemsOrigin + _itemsScroller._pos);
|
||||
_redraw = true;
|
||||
}
|
||||
if (_magicScroller.update(deltatime)) {
|
||||
_magicArea.moveTo(_magicOrigin + _magicScroller._pos);
|
||||
_redraw = true;
|
||||
}
|
||||
MenuManager::process(deltatime);
|
||||
}
|
||||
|
||||
void MenuZGI::redrawAll() {
|
||||
if (MenuManager::inMenu())
|
||||
for (int8 i = _menuFocus.size() - 1; i >= 0; i--)
|
||||
switch (_menuFocus[i]) {
|
||||
case kFocusItems:
|
||||
if (_enableFlags.get(kItemsMenu)) {
|
||||
redrawItems();
|
||||
}
|
||||
break;
|
||||
case kFocusMagic:
|
||||
if (_enableFlags.get(kMagicMenu)) {
|
||||
redrawMagic();
|
||||
}
|
||||
break;
|
||||
case kFocusMain:
|
||||
redrawMain();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MenuZGI::redrawMagic() {
|
||||
const int16 yOrigin = _menuArea.width();
|
||||
_engine->getRenderManager()->blitSurfaceToMenu(_menuBack[kFocusMagic], yOrigin + _magicScroller._pos.x, 0, 0);
|
||||
for (int i = 0; i < 12; i++) {
|
||||
bool inrect = false;
|
||||
if (_mouseOnItem == i)
|
||||
inrect = true;
|
||||
uint curItemId = _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + i);
|
||||
if (curItemId) {
|
||||
if (_engine->getScriptManager()->getStateValue(StateKey_Reversed_Spellbooc) == 1)
|
||||
curItemId = 0xEE + i;
|
||||
else
|
||||
curItemId = 0xE0 + i;
|
||||
}
|
||||
if (curItemId != 0) {
|
||||
if (_itemId[i] != curItemId) {
|
||||
char buf[16];
|
||||
Common::sprintf_s(buf, "gmzwu%2.2x1.tga", curItemId);
|
||||
_magic[i][0] = _engine->getRenderManager()->loadImage(buf, false);
|
||||
Common::sprintf_s(buf, "gmzxu%2.2x1.tga", curItemId);
|
||||
_magic[i][1] = _engine->getRenderManager()->loadImage(buf, false);
|
||||
_magicId[i] = curItemId;
|
||||
}
|
||||
if (inrect)
|
||||
_engine->getRenderManager()->blitSurfaceToMenu(*_magic[i][1], yOrigin + _magicScroller._pos.x + 28 + _magicWidth * i, 0, 0);
|
||||
else
|
||||
_engine->getRenderManager()->blitSurfaceToMenu(*_magic[i][0], yOrigin + _magicScroller._pos.x + 28 + _magicWidth * i, 0, 0);
|
||||
} else {
|
||||
if (_magic[i][0]) {
|
||||
_magic[i][0]->free();
|
||||
delete _magic[i][0];
|
||||
_magic[i][0] = NULL;
|
||||
}
|
||||
if (_magic[i][1]) {
|
||||
_magic[i][1]->free();
|
||||
delete _magic[i][1];
|
||||
_magic[i][1] = NULL;
|
||||
}
|
||||
_magicId[i] = 0;
|
||||
}
|
||||
}
|
||||
_clean = false;
|
||||
}
|
||||
|
||||
void MenuZGI::redrawItems() {
|
||||
_engine->getRenderManager()->blitSurfaceToMenu(_menuBack[kFocusItems], _itemsScroller._pos.x, 0, 0);
|
||||
int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots);
|
||||
if (itemCount == 0)
|
||||
itemCount = 20;
|
||||
else if (itemCount > 50)
|
||||
itemCount = 50;
|
||||
int itemWidth = (_wSideMenu - 28) / itemCount;
|
||||
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
bool inrect = false;
|
||||
if (_mouseOnItem == i)
|
||||
inrect = true;
|
||||
uint curItemId = _engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + i);
|
||||
|
||||
if (curItemId != 0) {
|
||||
if (_itemId[i] != curItemId) {
|
||||
char buf[16];
|
||||
Common::sprintf_s(buf, "gmzwu%2.2x1.tga", curItemId);
|
||||
_items[i][0] = _engine->getRenderManager()->loadImage(buf, false);
|
||||
Common::sprintf_s(buf, "gmzxu%2.2x1.tga", curItemId);
|
||||
_items[i][1] = _engine->getRenderManager()->loadImage(buf, false);
|
||||
_itemId[i] = curItemId;
|
||||
}
|
||||
if (inrect)
|
||||
_engine->getRenderManager()->blitSurfaceToMenu(*_items[i][1], _itemsScroller._pos.x + itemWidth * i, 0, 0);
|
||||
else
|
||||
_engine->getRenderManager()->blitSurfaceToMenu(*_items[i][0], _itemsScroller._pos.x + itemWidth * i, 0, 0);
|
||||
} else {
|
||||
if (_items[i][0]) {
|
||||
_items[i][0]->free();
|
||||
delete _items[i][0];
|
||||
_items[i][0] = NULL;
|
||||
}
|
||||
if (_items[i][1]) {
|
||||
_items[i][1]->free();
|
||||
delete _items[i][1];
|
||||
_items[i][1] = NULL;
|
||||
}
|
||||
_itemId[i] = 0;
|
||||
}
|
||||
}
|
||||
_clean = false;
|
||||
}
|
||||
|
||||
MenuNemesis::MenuNemesis(ZVision *engine, const Common::Rect menuArea) :
|
||||
MenuManager(engine, menuArea, nemesisParams) {
|
||||
|
||||
// Buffer menu background image
|
||||
_engine->getRenderManager()->readImageToSurface("bar.tga", _mainBack, false);
|
||||
|
||||
char buf[24];
|
||||
for (int i = 0; i < 4; i++)
|
||||
// Buffer menu buttons
|
||||
for (int j = 0; j < 6; j++) {
|
||||
Common::sprintf_s(buf, "butfrm%d%d.tga", i + 1, j);
|
||||
_engine->getRenderManager()->readImageToSurface(buf, _mainButtons[i][j], false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MenuNemesis::~MenuNemesis() {
|
||||
for (int i = 0; i < 4; i++)
|
||||
for (int j = 0; j < 6; j++)
|
||||
_mainButtons[i][j].free();
|
||||
|
||||
}
|
||||
|
||||
bool MenuNemesis::inMenu(const Common::Point &pos) const {
|
||||
return _menuTriggerArea.contains(pos) || (_menuFocus.front() != kFocusNone);
|
||||
}
|
||||
|
||||
void MenuNemesis::onMouseMove(const Common::Point &pos) {
|
||||
// Trigger main menu scrolldown to get mouse over main trigger area
|
||||
// Set focus to topmost layer of menus that mouse is currently over
|
||||
if (_mainArea.contains(pos) || _menuTriggerArea.contains(pos))
|
||||
setFocus(kFocusMain);
|
||||
else
|
||||
setFocus(kFocusNone);
|
||||
MenuManager::onMouseMove(pos);
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
192
engines/zvision/scripting/menu.h
Normal file
192
engines/zvision/scripting/menu.h
Normal file
@@ -0,0 +1,192 @@
|
||||
/* 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 ZVISION_MENU_H
|
||||
#define ZVISION_MENU_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/bitarray.h"
|
||||
#include "common/rect.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/common/focus_list.h"
|
||||
#include "zvision/common/scroller.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
enum {
|
||||
kMainMenuSave = 0,
|
||||
kMainMenuLoad = 1,
|
||||
kMainMenuPrefs = 2,
|
||||
kMainMenuExit = 3,
|
||||
kItemsMenu = 4,
|
||||
kMagicMenu = 5
|
||||
};
|
||||
|
||||
struct MenuParams {
|
||||
int16 wxButs[4][2]; // Widths & X positions of main menu buttons; {Save, Restore, Prefs, Quit}
|
||||
int16 wMain; // Width of main menu background
|
||||
int8 idleFrame; // Frame to display of unselected main menu button
|
||||
int8 activeFrame; // Frame to display of selected main menu button when mouse is down
|
||||
int8 clickedFrame; // Frame to display of selected main menu button when mouse is down
|
||||
Common::Point activePos; // Fully scrolled main menu position, relative to origin of menu area
|
||||
Common::Point idlePos; // Fully retracted main menu position, relative to origin of menu area
|
||||
int16 period; // Duration of main menu scrolldown
|
||||
int16 triggerHeight; // Height of menu trigger area when inactive
|
||||
int16 buttonPeriod; // Duration of main menu button animation
|
||||
};
|
||||
|
||||
// NB - menu area is same width as working window.
|
||||
|
||||
static const MenuParams nemesisParams {
|
||||
{ {120, -1}, {144, 120}, {128, 264}, {120, 392} },
|
||||
512,
|
||||
-1,
|
||||
4,
|
||||
5,
|
||||
Common::Point(0, 0),
|
||||
Common::Point(0, -32),
|
||||
500,
|
||||
2,
|
||||
500
|
||||
};
|
||||
|
||||
static const MenuParams zgiParams {
|
||||
{ {135, 50}, {135, 185}, {135, 320}, {135, 455} },
|
||||
580,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
Common::Point(30, 0),
|
||||
Common::Point(30, -20),
|
||||
250,
|
||||
32,
|
||||
0
|
||||
};
|
||||
|
||||
class MenuManager {
|
||||
public:
|
||||
MenuManager(ZVision *engine, const Common::Rect menuArea, const MenuParams params);
|
||||
virtual ~MenuManager();
|
||||
virtual void onMouseMove(const Common::Point &pos);
|
||||
virtual void onMouseDown(const Common::Point &pos);
|
||||
virtual void onMouseUp(const Common::Point &pos);
|
||||
virtual void process(uint32 deltaTimeInMillis);
|
||||
bool inMenu() const {
|
||||
return _prevInMenu;
|
||||
}
|
||||
virtual bool inMenu(const Common::Point &pos) const {
|
||||
return false;
|
||||
} // For widescreen mod; used to suspend panning, tilting & scripting triggers when the mouse is within the working window but also in the menu.
|
||||
|
||||
void mainMouseDown(const Common::Point &pos); // Show clicked graphic under selected button
|
||||
bool mainMouseMove(const Common::Point &pos); // return true if selected button has changed
|
||||
|
||||
void setEnable(uint16 flags);
|
||||
uint16 getEnable() const {
|
||||
return _menuBarFlag;
|
||||
}
|
||||
bool getEnable(uint8 flag) const {
|
||||
return _enableFlags.get(flag);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redrawAll() {}
|
||||
void redrawMain();
|
||||
int mouseOverMain(const Common::Point &pos);
|
||||
void setFocus(int8 currentFocus);
|
||||
|
||||
bool _prevInMenu = false;
|
||||
bool _redraw = true;
|
||||
int _mouseOnItem = -1;
|
||||
static const uint8 _hMainMenu = 32;
|
||||
int8 _mainClicked = -1;
|
||||
|
||||
ZVision *_engine;
|
||||
const MenuParams _params;
|
||||
uint16 _menuBarFlag;
|
||||
const Common::Rect _menuArea;
|
||||
const Common::Point _menuOrigin;
|
||||
const Common::Rect _menuTriggerArea;
|
||||
Graphics::Surface _mainBack;
|
||||
Graphics::Surface _mainButtons[4][6];
|
||||
Common::BitArray _enableFlags;
|
||||
Common::Rect _mainArea;
|
||||
Common::Rect _menuHotspots[4];
|
||||
int8 _mainFrames[4]; // Frame to display of each main menu button; first row is currently displayed, 2nd row is backbuffer for idle animations
|
||||
Scroller _mainScroller;
|
||||
FocusList<int8> _menuFocus; // Order in which menus have most recently had focus; determines current mouse focus & order in which to redraw them.
|
||||
bool _clean = false; // Whether or not to blank
|
||||
LinearScroller *_buttonAnim[4];
|
||||
};
|
||||
|
||||
class MenuZGI: public MenuManager {
|
||||
public:
|
||||
MenuZGI(ZVision *engine, Common::Rect menuArea);
|
||||
~MenuZGI() override;
|
||||
void onMouseMove(const Common::Point &pos) override; // NB Pos is in screen coordinates
|
||||
void onMouseUp(const Common::Point &pos) override;
|
||||
void process(uint32 deltaTimeInMillis) override;
|
||||
bool inMenu(const Common::Point &pos) const override;
|
||||
private:
|
||||
void redrawAll() override;
|
||||
Graphics::Surface _menuBack[3];
|
||||
Graphics::Surface *_items[50][2];
|
||||
uint _itemId[50];
|
||||
|
||||
Graphics::Surface *_magic[12][2];
|
||||
uint _magicId[12];
|
||||
// void redrawMain(bool hasFocus) override;
|
||||
void redrawItems();
|
||||
void redrawMagic();
|
||||
int mouseOverItem(const Common::Point &pos, int itemCount);
|
||||
int mouseOverMagic(const Common::Point &pos);
|
||||
|
||||
static const uint16 _hSideMenu = 32;
|
||||
static const uint16 _wSideMenu = 600;
|
||||
static const uint16 _wSideMenuTab = 20;
|
||||
static const int16 _magicWidth = 47;
|
||||
|
||||
const int16 _sideMenuPeriod = 300;
|
||||
|
||||
Scroller _itemsScroller, _magicScroller;
|
||||
|
||||
const Common::Point _magicOrigin;
|
||||
const Common::Point _itemsOrigin;
|
||||
|
||||
Common::Rect _magicArea;
|
||||
Common::Rect _itemsArea;
|
||||
};
|
||||
|
||||
class MenuNemesis: public MenuManager {
|
||||
public:
|
||||
MenuNemesis(ZVision *engine, Common::Rect menuArea);
|
||||
~MenuNemesis() override;
|
||||
void onMouseMove(const Common::Point &pos) override;
|
||||
bool inMenu(const Common::Point &pos) const override;
|
||||
private:
|
||||
void redrawAll() override;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
77
engines/zvision/scripting/puzzle.h
Normal file
77
engines/zvision/scripting/puzzle.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_PUZZLE_H
|
||||
#define ZVISION_PUZZLE_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/ptr.h"
|
||||
#include "zvision/scripting/actions.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
struct Puzzle {
|
||||
Puzzle() : key(0), addedBySetState(false) {}
|
||||
|
||||
~Puzzle() {
|
||||
for (auto & action : resultActions)
|
||||
delete action;
|
||||
}
|
||||
|
||||
/** How criteria should be decided */
|
||||
enum CriteriaOperator {
|
||||
EQUAL_TO,
|
||||
NOT_EQUAL_TO,
|
||||
GREATER_THAN,
|
||||
LESS_THAN
|
||||
};
|
||||
|
||||
/** Criteria for a Puzzle result to be fired */
|
||||
struct CriteriaEntry {
|
||||
/** The key of a global state */
|
||||
uint32 key;
|
||||
/**
|
||||
* What we're comparing the value of the global state against
|
||||
* This can either be a pure value or it can be the key of another global state
|
||||
*/
|
||||
uint32 argument;
|
||||
/** How to do the comparison */
|
||||
CriteriaOperator criteriaOperator;
|
||||
/** Whether 'argument' is the key of a global state (true) or a pure value (false) */
|
||||
bool argumentIsAKey;
|
||||
};
|
||||
|
||||
enum StateFlags {
|
||||
ONCE_PER_INST = 0x01,
|
||||
DISABLED = 0x02,
|
||||
DO_ME_NOW = 0x04
|
||||
};
|
||||
|
||||
uint32 key;
|
||||
Common::List<Common::List <CriteriaEntry> > criteriaList;
|
||||
// This has to be list of pointers because ResultAction is abstract
|
||||
Common::List<ResultAction *> resultActions;
|
||||
bool addedBySetState;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
566
engines/zvision/scripting/scr_file_handling.cpp
Normal file
566
engines/zvision/scripting/scr_file_handling.cpp
Normal file
@@ -0,0 +1,566 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/tokenizer.h"
|
||||
#include "zvision/detection.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/scripting/actions.h"
|
||||
#include "zvision/scripting/puzzle.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/fist_control.h"
|
||||
#include "zvision/scripting/controls/hotmov_control.h"
|
||||
#include "zvision/scripting/controls/input_control.h"
|
||||
#include "zvision/scripting/controls/lever_control.h"
|
||||
#include "zvision/scripting/controls/paint_control.h"
|
||||
#include "zvision/scripting/controls/push_toggle_control.h"
|
||||
#include "zvision/scripting/controls/safe_control.h"
|
||||
#include "zvision/scripting/controls/save_control.h"
|
||||
#include "zvision/scripting/controls/slot_control.h"
|
||||
#include "zvision/scripting/controls/titler_control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
void ScriptManager::parseScrFile(const Common::Path &fileName, ScriptScope &scope) {
|
||||
auto parse = [&](Common::File & file) {
|
||||
while (!file.eos()) {
|
||||
Common::String line = file.readLine();
|
||||
if (file.err())
|
||||
error("Error parsing scr file: %s", fileName.toString().c_str());
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
if (line.empty())
|
||||
continue;
|
||||
if (line.matchString("puzzle:*", true)) {
|
||||
Puzzle *puzzle = new Puzzle();
|
||||
if (sscanf(line.c_str(), "puzzle:%u", &(puzzle->key)) != 1) {
|
||||
debugC(kDebugScript, "Malformed puzzle string: %s", line.c_str());
|
||||
continue;
|
||||
}
|
||||
if (getStateFlag(puzzle->key) & Puzzle::ONCE_PER_INST)
|
||||
setStateValue(puzzle->key, 0);
|
||||
parsePuzzle(puzzle, file);
|
||||
scope.puzzles.push_back(puzzle);
|
||||
} else if (line.matchString("control:*", true)) {
|
||||
Control *ctrl = parseControl(line, file);
|
||||
if (ctrl)
|
||||
scope.controls.push_back(ctrl);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Common::File mainFile;
|
||||
Common::File auxFile;
|
||||
Common::String auxFileName = fileName.toString();
|
||||
replace(auxFileName, Common::String(".scr"), Common::String(".aux"));
|
||||
debugC(1, kDebugFile, "Auxiliary filename %s", auxFileName.c_str());
|
||||
Common::Path auxFilePath(auxFileName);
|
||||
debugC(1, kDebugFile, "Auxiliary path %s", auxFilePath.toString().c_str());
|
||||
|
||||
if (!mainFile.open(fileName))
|
||||
error("Script file not found: %s", fileName.toString().c_str());
|
||||
else {
|
||||
debugC(1, kDebugScript, "Parsing primary script file %s", fileName.toString().c_str());
|
||||
parse(mainFile);
|
||||
// TODO - add config option to disable/enable auxiliary scripting
|
||||
if (auxFile.exists(auxFilePath)) {
|
||||
debugC(1, kDebugFile, "Auxiliary script file found");
|
||||
if (auxFile.open(auxFilePath)) {
|
||||
debugC(1, kDebugScript, "Parsing auxiliary script file %s", auxFilePath.toString().c_str());
|
||||
parse(auxFile);
|
||||
} else
|
||||
debugC(1, kDebugFile, "Unable to open auxiliary script file %s", auxFilePath.toString().c_str());
|
||||
}
|
||||
}
|
||||
scope.procCount = 0;
|
||||
}
|
||||
|
||||
void ScriptManager::parsePuzzle(Puzzle *puzzle, Common::SeekableReadStream &stream) {
|
||||
Common::String line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (line.matchString("criteria {", true)) {
|
||||
parseCriteria(stream, puzzle->criteriaList, puzzle->key);
|
||||
} else if (line.matchString("results {", true)) {
|
||||
parseResults(stream, puzzle->resultActions, puzzle->key);
|
||||
// WORKAROUNDS:
|
||||
switch (_engine->getGameId()) {
|
||||
case GID_NEMESIS:
|
||||
// WORKAROUND for a script bug in Zork Nemesis, room ve5e (tuning
|
||||
// fork box closeup). If the player leaves the screen while the
|
||||
// box is open, puzzle 19398 shows the animation where the box
|
||||
// closes, but the box state (state variable 19397) is not updated.
|
||||
// We insert the missing assignment for the box state here.
|
||||
// Fixes bug #6803.
|
||||
if (puzzle->key == 19398)
|
||||
puzzle->resultActions.push_back(new ActionAssign(_engine, 11, "19397, 0"));
|
||||
break;
|
||||
case GID_GRANDINQUISITOR:
|
||||
switch (puzzle->key) {
|
||||
case 10836:
|
||||
// WORKAROUND for bug #10604. If the player is looking at the
|
||||
// cigar box when Antharia Jack returns to examine the lamp,
|
||||
// pp1f_video_flag remains 1. Later, when the player returns
|
||||
// to pick up the lantern, the game will try to play the
|
||||
// cutscene again, but since that script has already been
|
||||
// run the player gets stuck in a dark room instead. We have
|
||||
// to add the assignment action to the front, or it won't be
|
||||
// reached because changing the location terminates the script.
|
||||
//
|
||||
// Fixing it this way only keeps the bug from happening. It
|
||||
// will not repair old savegames.
|
||||
//
|
||||
// Note that the bug only affects the DVD version. The CD
|
||||
// version doesn't have a separate room for the cutscene.
|
||||
if (_engine->getFeatures() & ADGF_DVD)
|
||||
puzzle->resultActions.push_front(new ActionAssign(_engine, 11, "10803, 0"));
|
||||
break;
|
||||
// WORKAROUND for a script bug in Zork: Grand Inquisitor, room dc10.
|
||||
// Background heartbeat sound effect never terminates upon location change.
|
||||
case 2341:
|
||||
case 2344:
|
||||
case 17545:
|
||||
puzzle->resultActions.push_front(new ActionKill(_engine, 11, "02310"));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (line.matchString("flags {", true)) {
|
||||
setStateFlag(puzzle->key, parseFlags(stream));
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
}
|
||||
|
||||
puzzle->addedBySetState = false;
|
||||
}
|
||||
|
||||
bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::List<Common::List<Puzzle::CriteriaEntry> > &criteriaList, uint32 key) const {
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
|
||||
// Skip any commented out criteria. If all the criteria are commented out,
|
||||
// we might end up with an invalid criteria list (bug #6776).
|
||||
while (line.empty()) {
|
||||
line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
}
|
||||
|
||||
// Criteria can be empty
|
||||
if (line.contains('}')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a new List to hold the CriteriaEntries
|
||||
criteriaList.push_back(Common::List<Puzzle::CriteriaEntry>());
|
||||
|
||||
// WORKAROUNDS
|
||||
switch (_engine->getGameId()) {
|
||||
case GID_NEMESIS:
|
||||
// WORKAROUND for a script bug in Zork: Nemesis, room td9e (fist puzzle)
|
||||
// Since we patch the script that triggers when manipulating the left fist
|
||||
// (below), we add an additional check for the left fist sound, so that it
|
||||
// doesn't get killed immediately when the left fist animation starts.
|
||||
// Together with the workaround below, it fixes bug #6783.
|
||||
if (key == 3594) {
|
||||
Puzzle::CriteriaEntry entry;
|
||||
entry.key = 567;
|
||||
entry.criteriaOperator = Puzzle::NOT_EQUAL_TO;
|
||||
entry.argumentIsAKey = false;
|
||||
entry.argument = 1;
|
||||
|
||||
criteriaList.back().push_back(entry);
|
||||
}
|
||||
break;
|
||||
case GID_GRANDINQUISITOR:
|
||||
// WORKAROUND for a script bug in Zork: Grand Inquisitor, room me2j
|
||||
// (Closing the Time Tunnels). When the time tunnel is open the game
|
||||
// shows a close-up of only the tunnel, instead of showing the entire
|
||||
// booth. However, the scripts that draw the lever in its correct
|
||||
// state do not test this flag, causing it to be drawn when it should
|
||||
// not be. This fixes bug #6770.
|
||||
if (key == 9536) {
|
||||
Puzzle::CriteriaEntry entry;
|
||||
entry.key = 9404; // me2j_time_tunnel_open
|
||||
entry.criteriaOperator = Puzzle::EQUAL_TO;
|
||||
entry.argumentIsAKey = false;
|
||||
entry.argument = 0;
|
||||
|
||||
criteriaList.back().push_back(entry);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
Puzzle::CriteriaEntry entry;
|
||||
|
||||
// Split the string into tokens using ' ' as a delimiter
|
||||
Common::StringTokenizer tokenizer(line);
|
||||
Common::String token;
|
||||
|
||||
// Parse the id out of the first token
|
||||
token = tokenizer.nextToken();
|
||||
if (sscanf(token.c_str(), "[%u]", &(entry.key)) != 1) {
|
||||
debugC(kDebugScript, "Malformed criteria ID string %s", token.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// WORKAROUND for a script bug in Zork: Nemesis, room td9e (fist puzzle)
|
||||
// Check for the state of animation 567 (left fist) when manipulating
|
||||
// the fingers of the left fist (puzzle slots 3582, 3583).
|
||||
// Together with the workaround above, it fixes bug #6783.
|
||||
if (_engine->getGameId() == GID_NEMESIS && (key == 3582 || key == 3583) && entry.key == 568)
|
||||
entry.key = 567;
|
||||
|
||||
// Parse the operator out of the second token
|
||||
token = tokenizer.nextToken();
|
||||
if (token.c_str()[0] == '=')
|
||||
entry.criteriaOperator = Puzzle::EQUAL_TO;
|
||||
else if (token.c_str()[0] == '!')
|
||||
entry.criteriaOperator = Puzzle::NOT_EQUAL_TO;
|
||||
else if (token.c_str()[0] == '>')
|
||||
entry.criteriaOperator = Puzzle::GREATER_THAN;
|
||||
else if (token.c_str()[0] == '<')
|
||||
entry.criteriaOperator = Puzzle::LESS_THAN;
|
||||
|
||||
// There are supposed to be three tokens, but there is no
|
||||
// guarantee that there will be a space between the second and
|
||||
// the third one (bug #6774)
|
||||
if (token.size() == 1) {
|
||||
token = tokenizer.nextToken();
|
||||
} else {
|
||||
token.deleteChar(0);
|
||||
}
|
||||
|
||||
// First determine if the last token is an id or a value
|
||||
// Then parse it into 'argument'
|
||||
if (token.contains('[')) {
|
||||
if (sscanf(token.c_str(), "[%u]", &(entry.argument)) != 1) {
|
||||
debugC(kDebugScript, "Malformed token string %s", token.c_str());
|
||||
return false;
|
||||
}
|
||||
entry.argumentIsAKey = true;
|
||||
} else {
|
||||
if (sscanf(token.c_str(), "%u", &(entry.argument)) != 1) {
|
||||
debugC(kDebugScript, "Malformed token string %s", token.c_str());
|
||||
return false;
|
||||
}
|
||||
entry.argumentIsAKey = false;
|
||||
}
|
||||
|
||||
// WORKAROUND for a script bug in Zork: Grand Inquisitor. If the
|
||||
// fire timer is killed (e.g. by the inventory screen) with less
|
||||
// than 10 units left, it will get stuck and never time out. We
|
||||
// work around that by changing the condition from "greater than
|
||||
// 10" to "greater than 0 but not 2 (the magic time-out value)".
|
||||
//
|
||||
// I have a sneaking suspicion that there may be other timer
|
||||
// glitches like this, but this one makes the game unplayable
|
||||
// and is easy to trigger.
|
||||
if (_engine->getGameId() == GID_GRANDINQUISITOR && key == 17162) {
|
||||
Puzzle::CriteriaEntry entry0;
|
||||
entry0.key = 17161; // pe_fire
|
||||
entry0.criteriaOperator = Puzzle::GREATER_THAN;
|
||||
entry0.argumentIsAKey = false;
|
||||
entry0.argument = 0;
|
||||
|
||||
criteriaList.back().push_back(entry0);
|
||||
|
||||
entry.criteriaOperator = Puzzle::NOT_EQUAL_TO;
|
||||
entry.argument = 2;
|
||||
}
|
||||
|
||||
criteriaList.back().push_back(entry);
|
||||
|
||||
line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::List<ResultAction *> &actionList, uint32 key) const {
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
line.toLowercase();
|
||||
|
||||
// TODO: Re-order the if-then statements in order of highest occurrence
|
||||
// While within results block
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
// Skip empty lines
|
||||
if (line.empty()) {
|
||||
line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
line.toLowercase();
|
||||
continue;
|
||||
}
|
||||
debugC(4, kDebugScript, "Result line: %s", line.c_str());
|
||||
const char *chrs = line.c_str();
|
||||
uint pos;
|
||||
if (line.matchString("action:*", true))
|
||||
pos = 6;
|
||||
else if (line.matchString("event:*", true))
|
||||
pos = 5;
|
||||
else if (line.matchString("background:*", true))
|
||||
pos = 10;
|
||||
else
|
||||
continue;
|
||||
if (pos < line.size()) { // Stuff left
|
||||
uint startpos = pos + 1; // first character after colon
|
||||
// Scan for next colon or opening bracket
|
||||
for (pos = startpos; pos < line.size(); pos++)
|
||||
if (chrs[pos] == ':' || chrs[pos] == '(')
|
||||
break;
|
||||
|
||||
debugC(4, kDebugScript, "startpos %d, pos %d, line.size %d", startpos, pos, line.size());
|
||||
int32 slot = 11; // Non-setting default slot
|
||||
Common::String args = "";
|
||||
Common::String act(chrs + startpos, chrs + pos);
|
||||
// Extract arguments, if any
|
||||
if (pos < line.size()) {
|
||||
|
||||
startpos = pos + 1;
|
||||
|
||||
if (chrs[pos] == ':') {
|
||||
for (pos = startpos; pos < line.size(); pos++)
|
||||
if (chrs[pos] == '(')
|
||||
break;
|
||||
// Extract slotkey, if specified
|
||||
Common::String strSlot(chrs + startpos, chrs + pos);
|
||||
slot = atoi(strSlot.c_str());
|
||||
|
||||
startpos = pos + 1;
|
||||
}
|
||||
|
||||
if (pos < line.size()) {
|
||||
for (pos = startpos; pos < line.size(); pos++)
|
||||
if (chrs[pos] == ')')
|
||||
break;
|
||||
|
||||
args = Common::String(chrs + startpos, chrs + pos);
|
||||
}
|
||||
}
|
||||
debugC(4, kDebugScript, "Action string: '%s', slot %d, arguments string '%s'", act.c_str(), slot, args.c_str());
|
||||
|
||||
// Parse for the action type
|
||||
if (act.matchString("add", true)) {
|
||||
actionList.push_back(new ActionAdd(_engine, slot, args));
|
||||
} else if (act.matchString("animplay", true)) {
|
||||
actionList.push_back(new ActionPlayAnimation(_engine, slot, args));
|
||||
} else if (act.matchString("animpreload", true)) {
|
||||
actionList.push_back(new ActionPreloadAnimation(_engine, slot, args));
|
||||
} else if (act.matchString("animunload", true)) {
|
||||
// Only used by ZGI (locations cd6e, cd6k, dg2f, dg4e, dv1j)
|
||||
actionList.push_back(new ActionUnloadAnimation(_engine, slot, args));
|
||||
} else if (act.matchString("attenuate", true)) {
|
||||
actionList.push_back(new ActionAttenuate(_engine, slot, args));
|
||||
} else if (act.matchString("assign", true)) {
|
||||
if (_engine->getGameId() == GID_GRANDINQUISITOR && key == 17761) {
|
||||
// WORKAROUND for a script bug in Zork: Grand Inquisitor, room tp1e.
|
||||
// Background looping sound effect continuously restarts on every game cycle.
|
||||
// This is caused by resetting itself to zero as a result of itself.
|
||||
// Simple fix is to simply not generate this result assignment action at all.
|
||||
} else
|
||||
actionList.push_back(new ActionAssign(_engine, slot, args));
|
||||
} else if (act.matchString("change_location", true)) {
|
||||
actionList.push_back(new ActionChangeLocation(_engine, slot, args));
|
||||
} else if (act.matchString("crossfade", true)) {
|
||||
actionList.push_back(new ActionCrossfade(_engine, slot, args));
|
||||
} else if (act.matchString("cursor", true)) {
|
||||
actionList.push_back(new ActionCursor(_engine, slot, args));
|
||||
} else if (act.matchString("debug", true)) {
|
||||
// Not used. Purposely left empty
|
||||
} else if (act.matchString("delay_render", true)) {
|
||||
actionList.push_back(new ActionDelayRender(_engine, slot, args));
|
||||
} else if (act.matchString("disable_control", true)) {
|
||||
actionList.push_back(new ActionDisableControl(_engine, slot, args));
|
||||
} else if (act.matchString("disable_venus", true)) {
|
||||
// Not used. Purposely left empty
|
||||
} else if (act.matchString("display_message", true)) {
|
||||
actionList.push_back(new ActionDisplayMessage(_engine, slot, args));
|
||||
} else if (act.matchString("dissolve", true)) {
|
||||
actionList.push_back(new ActionDissolve(_engine));
|
||||
} else if (act.matchString("distort", true)) {
|
||||
// Only used by Zork: Nemesis for the "treatment" puzzle in the Sanitarium (aj30)
|
||||
actionList.push_back(new ActionDistort(_engine, slot, args));
|
||||
} else if (act.matchString("enable_control", true)) {
|
||||
actionList.push_back(new ActionEnableControl(_engine, slot, args));
|
||||
} else if (act.matchString("flush_mouse_events", true)) {
|
||||
actionList.push_back(new ActionFlushMouseEvents(_engine, slot));
|
||||
} else if (act.matchString("inventory", true)) {
|
||||
actionList.push_back(new ActionInventory(_engine, slot, args));
|
||||
} else if (act.matchString("kill", true)) {
|
||||
// Only used by ZGI
|
||||
actionList.push_back(new ActionKill(_engine, slot, args));
|
||||
} else if (act.matchString("menu_bar_enable", true)) {
|
||||
actionList.push_back(new ActionMenuBarEnable(_engine, slot, args));
|
||||
} else if (act.matchString("music", true)) {
|
||||
actionList.push_back(new ActionMusic(_engine, slot, args, false));
|
||||
} else if (act.matchString("pan_track", true)) {
|
||||
actionList.push_back(new ActionPanTrack(_engine, slot, args));
|
||||
} else if (act.matchString("playpreload", true)) {
|
||||
actionList.push_back(new ActionPlayPreloadAnimation(_engine, slot, args));
|
||||
} else if (act.matchString("preferences", true)) {
|
||||
actionList.push_back(new ActionPreferences(_engine, slot, args));
|
||||
} else if (act.matchString("quit", true)) {
|
||||
actionList.push_back(new ActionQuit(_engine, slot));
|
||||
} else if (act.matchString("random", true)) {
|
||||
actionList.push_back(new ActionRandom(_engine, slot, args));
|
||||
} else if (act.matchString("region", true)) {
|
||||
// Only used by Zork: Nemesis
|
||||
actionList.push_back(new ActionRegion(_engine, slot, args));
|
||||
} else if (act.matchString("restore_game", true)) {
|
||||
// Only used by ZGI to load the restart game slot, r.svr.
|
||||
// Used by the credits screen.
|
||||
actionList.push_back(new ActionRestoreGame(_engine, slot, args));
|
||||
} else if (act.matchString("rotate_to", true)) {
|
||||
actionList.push_back(new ActionRotateTo(_engine, slot, args));
|
||||
} else if (act.matchString("save_game", true)) {
|
||||
// Not used. Purposely left empty
|
||||
} else if (act.matchString("set_partial_screen", true)) {
|
||||
actionList.push_back(new ActionSetPartialScreen(_engine, slot, args));
|
||||
} else if (act.matchString("set_screen", true)) {
|
||||
actionList.push_back(new ActionSetScreen(_engine, slot, args));
|
||||
} else if (act.matchString("set_venus", true)) {
|
||||
// Not used. Purposely left empty
|
||||
} else if (act.matchString("stop", true)) {
|
||||
actionList.push_back(new ActionStop(_engine, slot, args));
|
||||
} else if (act.matchString("streamvideo", true)) {
|
||||
actionList.push_back(new ActionStreamVideo(_engine, slot, args));
|
||||
} else if (act.matchString("syncsound", true)) {
|
||||
actionList.push_back(new ActionSyncSound(_engine, slot, args));
|
||||
} else if (act.matchString("timer", true)) {
|
||||
actionList.push_back(new ActionTimer(_engine, slot, args));
|
||||
} else if (act.matchString("ttytext", true)) {
|
||||
actionList.push_back(new ActionTtyText(_engine, slot, args));
|
||||
} else if (act.matchString("universe_music", true)) {
|
||||
actionList.push_back(new ActionMusic(_engine, slot, args, true));
|
||||
} else if (act.matchString("copy_file", true)) {
|
||||
// Not used. Purposely left empty
|
||||
} else {
|
||||
warning("Unhandled result action type: %s", line.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
line.toLowercase();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
uint ScriptManager::parseFlags(Common::SeekableReadStream &stream) const {
|
||||
uint flags = 0;
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (line.matchString("ONCE_PER_INST", true)) {
|
||||
flags |= Puzzle::ONCE_PER_INST;
|
||||
} else if (line.matchString("DO_ME_NOW", true)) {
|
||||
flags |= Puzzle::DO_ME_NOW;
|
||||
} else if (line.matchString("DISABLED", true)) {
|
||||
flags |= Puzzle::DISABLED;
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
Control *ScriptManager::parseControl(Common::String &line, Common::SeekableReadStream &stream) {
|
||||
uint32 key;
|
||||
char controlTypeBuffer[20];
|
||||
|
||||
if (sscanf(line.c_str(), "control:%u %s {", &key, controlTypeBuffer) != 2) {
|
||||
debugC(kDebugScript, "Malformed control string: %s", line.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Common::String controlType(controlTypeBuffer);
|
||||
if (controlType.equalsIgnoreCase("push_toggle")) {
|
||||
// WORKAROUND for a script bug in ZGI: There is an invalid hotspot
|
||||
// at scene em1h (bottom of tower), which points to a missing
|
||||
// script em1n. This is a hotspot at the right of the screen.
|
||||
// In the original, this hotspot doesn't lead anywhere anyway,
|
||||
// so instead of moving to a missing scene, we just remove the
|
||||
// hotspot altogether. The alternative would be to just process
|
||||
// and ignore invalid scenes, but I don't think it's worth the
|
||||
// effort. Fixes bug #6780.
|
||||
if (_engine->getGameId() == GID_GRANDINQUISITOR && key == 5653)
|
||||
return NULL;
|
||||
return new PushToggleControl(_engine, key, stream);
|
||||
} else if (controlType.equalsIgnoreCase("flat")) {
|
||||
Control::parseFlatControl(_engine);
|
||||
return NULL;
|
||||
} else if (controlType.equalsIgnoreCase("pana")) {
|
||||
Control::parsePanoramaControl(_engine, stream);
|
||||
return NULL;
|
||||
} else if (controlType.equalsIgnoreCase("tilt")) {
|
||||
// Only used in Zork Nemesis, handles tilt controls (ZGI doesn't have a tilt view)
|
||||
Control::parseTiltControl(_engine, stream);
|
||||
return NULL;
|
||||
} else if (controlType.equalsIgnoreCase("slot")) {
|
||||
return new SlotControl(_engine, key, stream);
|
||||
} else if (controlType.equalsIgnoreCase("input")) {
|
||||
return new InputControl(_engine, key, stream);
|
||||
} else if (controlType.equalsIgnoreCase("save")) {
|
||||
return new SaveControl(_engine, key, stream);
|
||||
} else if (controlType.equalsIgnoreCase("lever")) {
|
||||
// Only used in Zork Nemesis, handles draggable levers (te2e, tm7e, tp2e, tt2e, tz2e)
|
||||
return new LeverControl(_engine, key, stream);
|
||||
} else if (controlType.equalsIgnoreCase("safe")) {
|
||||
// Only used in Zork Nemesis, handles the safe in the Asylum (ac4g)
|
||||
return new SafeControl(_engine, key, stream);
|
||||
} else if (controlType.equalsIgnoreCase("hotmovie")) {
|
||||
// Only used in Zork Nemesis, handles movies where the player needs to click on something (mj7g, vw3g)
|
||||
return new HotMovControl(_engine, key, stream);
|
||||
} else if (controlType.equalsIgnoreCase("fist")) {
|
||||
// Only used in Zork Nemesis, handles the door lock puzzle with the skeletal fingers (td9e)
|
||||
return new FistControl(_engine, key, stream);
|
||||
} else if (controlType.equalsIgnoreCase("paint")) {
|
||||
// Only used in Zork Nemesis, handles the painting puzzle screen in Lucien's room in Irondune (ch4g)
|
||||
return new PaintControl(_engine, key, stream);
|
||||
} else if (controlType.equalsIgnoreCase("titler")) {
|
||||
// Only used in Zork Nemesis, handles the death screen with the Restore/Exit buttons (cjde)
|
||||
return new TitlerControl(_engine, key, stream);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
912
engines/zvision/scripting/script_manager.cpp
Normal file
912
engines/zvision/scripting/script_manager.cpp
Normal file
@@ -0,0 +1,912 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/algorithm.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/file/save_manager.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/scripting/actions.h"
|
||||
#include "zvision/scripting/menu.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/effects/timer_effect.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
ScriptManager::ScriptManager(ZVision *engine)
|
||||
: _engine(engine),
|
||||
_currentlyFocusedControl(0),
|
||||
_activeControls(NULL) {
|
||||
}
|
||||
|
||||
ScriptManager::~ScriptManager() {
|
||||
cleanScriptScope(_universe);
|
||||
cleanScriptScope(_world);
|
||||
cleanScriptScope(_room);
|
||||
cleanScriptScope(_nodeview);
|
||||
_controlEvents.clear();
|
||||
}
|
||||
|
||||
void ScriptManager::initialize(bool restarted) {
|
||||
if (restarted) {
|
||||
_globalState.clear();
|
||||
_globalStateFlags.clear();
|
||||
}
|
||||
cleanScriptScope(_universe);
|
||||
cleanScriptScope(_world);
|
||||
cleanScriptScope(_room);
|
||||
cleanScriptScope(_nodeview);
|
||||
_currentLocation.node = 0;
|
||||
_currentLocation.world = 0;
|
||||
_currentLocation.room = 0;
|
||||
_currentLocation.view = 0;
|
||||
if (restarted) {
|
||||
for (auto &fx : _activeSideFx)
|
||||
delete fx;
|
||||
_activeSideFx.clear();
|
||||
_referenceTable.clear();
|
||||
switch (_engine->getGameId()) {
|
||||
case GID_GRANDINQUISITOR:
|
||||
// Bypass logo video
|
||||
setStateValue(16966, 1);
|
||||
// Ensure post-logo screen redraw is not inhibited in CD version
|
||||
setStateValue(5813, 1);
|
||||
// Bypass additional logo videos in DVD version
|
||||
setStateValue(19810, 1);
|
||||
setStateValue(19848, 1);
|
||||
break;
|
||||
case GID_NEMESIS:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
parseScrFile("universe.scr", _universe);
|
||||
changeLocation('g', 'a', 'r', 'y', 0);
|
||||
|
||||
_controlEvents.clear();
|
||||
if (restarted)
|
||||
_engine->loadSettings();
|
||||
}
|
||||
|
||||
bool ScriptManager::changingLocation() const {
|
||||
return _currentLocation != _nextLocation;
|
||||
}
|
||||
|
||||
void ScriptManager::process(uint deltaTimeMillis) {
|
||||
// If the location is changing, the script that did that may have
|
||||
// triggered other scripts, so we give them all a few extra cycles to
|
||||
// run. This fixes some missing scoring in ZGI, and quite
|
||||
// possibly other minor glitches as well.
|
||||
//
|
||||
// Another idea would be to change if there are pending scripts
|
||||
// in the exec queues, but that could cause this to hang
|
||||
// indefinitely.
|
||||
for (uint8 pass = 0; pass <= (changingLocation() ? _changeLocationExtraCycles : 0); pass++) {
|
||||
updateNodes(pass == 0 ? deltaTimeMillis : 0);
|
||||
debugC(5, kDebugLoop, "Script nodes updated");
|
||||
if (!execScope(_nodeview))
|
||||
break;
|
||||
if (!execScope(_room))
|
||||
break;
|
||||
if (!execScope(_world))
|
||||
break;
|
||||
if (!execScope(_universe))
|
||||
break;
|
||||
}
|
||||
updateControls(deltaTimeMillis);
|
||||
if (changingLocation())
|
||||
ChangeLocationReal(false);
|
||||
}
|
||||
|
||||
bool ScriptManager::execScope(ScriptScope &scope) {
|
||||
// Swap queues
|
||||
PuzzleList *tmp = scope.execQueue;
|
||||
scope.execQueue = scope.scopeQueue;
|
||||
scope.scopeQueue = tmp;
|
||||
scope.scopeQueue->clear();
|
||||
|
||||
for (auto &puzzle : scope.puzzles)
|
||||
puzzle->addedBySetState = false;
|
||||
|
||||
switch (getStateValue(StateKey_ExecScopeStyle)) {
|
||||
case 0: // ZGI
|
||||
if (scope.procCount < 2)
|
||||
for (auto &puzzle : scope.puzzles) {
|
||||
if (!checkPuzzleCriteria(puzzle, scope.procCount))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
for (auto &puzzle : (*scope.execQueue)) {
|
||||
if (!checkPuzzleCriteria(puzzle, scope.procCount))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 1: // Nemesis
|
||||
default:
|
||||
for (auto &puzzle : scope.puzzles) {
|
||||
if (!checkPuzzleCriteria(puzzle, scope.procCount))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (scope.procCount < 2)
|
||||
scope.procCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptManager::referenceTableAddPuzzle(uint32 key, PuzzleRef ref) {
|
||||
if (_referenceTable.contains(key)) {
|
||||
Common::Array<PuzzleRef> *arr = &_referenceTable[key];
|
||||
for (uint32 i = 0; i < arr->size(); i++) {
|
||||
if ((*arr)[i].puz == ref.puz)
|
||||
return;
|
||||
}
|
||||
}
|
||||
_referenceTable[key].push_back(ref);
|
||||
}
|
||||
|
||||
void ScriptManager::addPuzzlesToReferenceTable(ScriptScope &scope) {
|
||||
// Iterate through each local Puzzle
|
||||
for (auto &puzzle : scope.puzzles) {
|
||||
Puzzle *puzzlePtr = puzzle;
|
||||
|
||||
PuzzleRef ref;
|
||||
ref.scope = &scope;
|
||||
ref.puz = puzzlePtr;
|
||||
|
||||
referenceTableAddPuzzle(puzzlePtr->key, ref);
|
||||
|
||||
// Iterate through each CriteriaEntry and add a reference from the criteria key to the Puzzle
|
||||
for (auto &criteria : puzzle->criteriaList) {
|
||||
for (auto &entry : criteria)
|
||||
referenceTableAddPuzzle(entry.key, ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::updateNodes(uint deltaTimeMillis) {
|
||||
// If process() returns true, it means the node can be deleted
|
||||
for (auto fx = _activeSideFx.begin(); fx != _activeSideFx.end();) {
|
||||
if ((*fx)->process(deltaTimeMillis)) {
|
||||
delete(*fx);
|
||||
// Remove the node
|
||||
fx = _activeSideFx.erase(fx);
|
||||
}
|
||||
else
|
||||
++fx;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::updateControls(uint deltaTimeMillis) {
|
||||
if (!_activeControls)
|
||||
return;
|
||||
|
||||
// Process only one event
|
||||
if (!_controlEvents.empty()) {
|
||||
Common::Event _event = _controlEvents.front();
|
||||
Common::Point imageCoord;
|
||||
switch (_event.type) {
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
imageCoord = _engine->getRenderManager()->screenSpaceToImageSpace(_event.mouse);
|
||||
onMouseDown(_event.mouse, imageCoord);
|
||||
break;
|
||||
case Common::EVENT_LBUTTONUP:
|
||||
imageCoord = _engine->getRenderManager()->screenSpaceToImageSpace(_event.mouse);
|
||||
onMouseUp(_event.mouse, imageCoord);
|
||||
break;
|
||||
case Common::EVENT_KEYDOWN:
|
||||
onKeyDown(_event.kbd);
|
||||
break;
|
||||
case Common::EVENT_KEYUP:
|
||||
onKeyUp(_event.kbd);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_controlEvents.pop_front();
|
||||
}
|
||||
|
||||
for (auto &control : (*_activeControls)) {
|
||||
if (control->process(deltaTimeMillis))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptManager::checkPuzzleCriteria(Puzzle *puzzle, uint counter) {
|
||||
// Check if the puzzle is already finished
|
||||
// Also check that the puzzle isn't disabled
|
||||
if (getStateValue(puzzle->key) == 1 || (getStateFlag(puzzle->key) & Puzzle::DISABLED))
|
||||
return true;
|
||||
|
||||
// Check each Criteria
|
||||
if (counter == 0 && (getStateFlag(puzzle->key) & Puzzle::DO_ME_NOW) == 0)
|
||||
return true;
|
||||
|
||||
// WORKAROUNDS:
|
||||
switch (_engine->getGameId()) {
|
||||
case GID_NEMESIS:
|
||||
switch (puzzle->key) {
|
||||
case 16418:
|
||||
// WORKAROUND for script bug in Zork Nemesis, room mc30 (Monastery Entry)
|
||||
// Rumble sound effect should cease upon changing location to me10 (Hall of Masks),
|
||||
// but this puzzle erroneously restarted it immediately after.
|
||||
if(changingLocation())
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
bool criteriaMet = false;
|
||||
for (auto &criteria : puzzle->criteriaList) {
|
||||
criteriaMet = false;
|
||||
|
||||
for (auto &entry : criteria) {
|
||||
// Get the value to compare against
|
||||
int argumentValue;
|
||||
if (entry.argumentIsAKey)
|
||||
argumentValue = getStateValue(entry.argument);
|
||||
else
|
||||
argumentValue = entry.argument;
|
||||
|
||||
// Do the comparison
|
||||
switch (entry.criteriaOperator) {
|
||||
case Puzzle::EQUAL_TO:
|
||||
criteriaMet = getStateValue(entry.key) == argumentValue;
|
||||
break;
|
||||
case Puzzle::NOT_EQUAL_TO:
|
||||
criteriaMet = getStateValue(entry.key) != argumentValue;
|
||||
break;
|
||||
case Puzzle::GREATER_THAN:
|
||||
criteriaMet = getStateValue(entry.key) > argumentValue;
|
||||
break;
|
||||
case Puzzle::LESS_THAN:
|
||||
criteriaMet = getStateValue(entry.key) < argumentValue;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// If one check returns false, don't keep checking
|
||||
if (!criteriaMet)
|
||||
break;
|
||||
}
|
||||
|
||||
// If any of the Criteria are *fully* met, then execute the results
|
||||
if (criteriaMet)
|
||||
break;
|
||||
}
|
||||
|
||||
// criteriaList can be empty. Aka, the puzzle should be executed immediately
|
||||
if (puzzle->criteriaList.empty() || criteriaMet) {
|
||||
debugC(1, kDebugPuzzle, "Puzzle %u criteria passed. Executing its ResultActions", puzzle->key);
|
||||
|
||||
// Set the puzzle as completed
|
||||
setStateValue(puzzle->key, 1);
|
||||
|
||||
for (auto &result : puzzle->resultActions) {
|
||||
if (!result->execute())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptManager::cleanStateTable() {
|
||||
for (auto entry = _globalState.begin(); entry != _globalState.end(); ++entry) {
|
||||
// If the value is equal to zero, we can purge it since getStateValue()
|
||||
// will return zero if _globalState doesn't contain a key
|
||||
if (entry->_value == 0) {
|
||||
// Remove the node
|
||||
_globalState.erase(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::cleanScriptScope(ScriptScope &scope) {
|
||||
scope.privQueueOne.clear();
|
||||
scope.privQueueTwo.clear();
|
||||
scope.scopeQueue = &scope.privQueueOne;
|
||||
scope.execQueue = &scope.privQueueTwo;
|
||||
for (auto &puzzle : scope.puzzles)
|
||||
delete(puzzle);
|
||||
|
||||
scope.puzzles.clear();
|
||||
|
||||
for (auto &control : scope.controls)
|
||||
delete(control);
|
||||
|
||||
scope.controls.clear();
|
||||
|
||||
scope.procCount = 0;
|
||||
}
|
||||
|
||||
int ScriptManager::getStateValue(uint32 key) {
|
||||
if (_globalState.contains(key))
|
||||
return _globalState[key];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ScriptManager::queuePuzzles(uint32 key) {
|
||||
if (_referenceTable.contains(key)) {
|
||||
Common::Array<PuzzleRef> *arr = &_referenceTable[key];
|
||||
for (int32 i = arr->size() - 1; i >= 0; i--) {
|
||||
if (!(*arr)[i].puz->addedBySetState) {
|
||||
(*arr)[i].scope->scopeQueue->push_back((*arr)[i].puz);
|
||||
(*arr)[i].puz->addedBySetState = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::setStateValue(uint32 key, int value) {
|
||||
if (value == 0)
|
||||
_globalState.erase(key);
|
||||
else
|
||||
_globalState[key] = value;
|
||||
|
||||
queuePuzzles(key);
|
||||
}
|
||||
|
||||
void ScriptManager::setStateValueSilent(uint32 key, int value) {
|
||||
if (value == 0)
|
||||
_globalState.erase(key);
|
||||
else
|
||||
_globalState[key] = value;
|
||||
}
|
||||
|
||||
uint ScriptManager::getStateFlag(uint32 key) {
|
||||
if (_globalStateFlags.contains(key))
|
||||
return _globalStateFlags[key];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ScriptManager::setStateFlag(uint32 key, uint value) {
|
||||
queuePuzzles(key);
|
||||
|
||||
_globalStateFlags[key] |= value;
|
||||
}
|
||||
|
||||
void ScriptManager::setStateFlagSilent(uint32 key, uint value) {
|
||||
if (value == 0)
|
||||
_globalStateFlags.erase(key);
|
||||
else
|
||||
_globalStateFlags[key] = value;
|
||||
}
|
||||
|
||||
void ScriptManager::unsetStateFlag(uint32 key, uint value) {
|
||||
queuePuzzles(key);
|
||||
|
||||
if (_globalStateFlags.contains(key)) {
|
||||
_globalStateFlags[key] &= ~value;
|
||||
if (_globalStateFlags[key] == 0)
|
||||
_globalStateFlags.erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
Control *ScriptManager::getControl(uint32 key) {
|
||||
for (auto &control : (*_activeControls)) {
|
||||
if (control->getKey() == key)
|
||||
return control;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ScriptManager::focusControl(uint32 key) {
|
||||
if (!_activeControls)
|
||||
return;
|
||||
|
||||
if (_currentlyFocusedControl == key)
|
||||
return;
|
||||
|
||||
for (auto &control : (*_activeControls)) {
|
||||
uint32 controlKey = control->getKey();
|
||||
if (controlKey == key)
|
||||
control->focus();
|
||||
else if (controlKey == _currentlyFocusedControl)
|
||||
control->unfocus();
|
||||
}
|
||||
_currentlyFocusedControl = key;
|
||||
}
|
||||
|
||||
void ScriptManager::setFocusControlKey(uint32 key) {
|
||||
_currentlyFocusedControl = key;
|
||||
}
|
||||
|
||||
void ScriptManager::addSideFX(ScriptingEffect *fx) {
|
||||
_activeSideFx.push_back(fx);
|
||||
}
|
||||
|
||||
ScriptingEffect *ScriptManager::getSideFX(uint32 key) {
|
||||
for (auto &fx : _activeSideFx) {
|
||||
if (fx->getKey() == key)
|
||||
return fx;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ScriptManager::deleteSideFx(uint32 key) {
|
||||
for (auto fx = _activeSideFx.begin(); fx != _activeSideFx.end(); ++fx) {
|
||||
if ((*fx)->getKey() == key) {
|
||||
delete(*fx);
|
||||
_activeSideFx.erase(fx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::stopSideFx(uint32 key) {
|
||||
for (auto fx = _activeSideFx.begin(); fx != _activeSideFx.end(); ++fx) {
|
||||
if ((*fx)->getKey() == key) {
|
||||
bool ret = (*fx)->stop();
|
||||
if (ret) {
|
||||
delete(*fx);
|
||||
_activeSideFx.erase(fx);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::killSideFx(uint32 key) {
|
||||
for (auto fx = _activeSideFx.begin(); fx != _activeSideFx.end(); ++fx) {
|
||||
if ((*fx)->getKey() == key) {
|
||||
(*fx)->kill();
|
||||
delete(*fx);
|
||||
_activeSideFx.erase(fx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::killSideFxType(ScriptingEffect::ScriptingEffectType type) {
|
||||
for (auto fx = _activeSideFx.begin(); fx != _activeSideFx.end();) {
|
||||
if ((*fx)->getType() & type) {
|
||||
(*fx)->kill();
|
||||
delete(*fx);
|
||||
fx = _activeSideFx.erase(fx);
|
||||
} else {
|
||||
++fx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
debugC(1, kDebugMouse, "Mouse screen coordinates: %d, %d, background/script coordinates: %d, %d", screenSpacePos.x, screenSpacePos.y, backgroundImageSpacePos.x, backgroundImageSpacePos.y);
|
||||
if (!_activeControls)
|
||||
return;
|
||||
|
||||
for (auto control = _activeControls->reverse_begin(); control != _activeControls->end(); control--) {
|
||||
if ((*control)->onMouseDown(screenSpacePos, backgroundImageSpacePos))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (!_activeControls)
|
||||
return;
|
||||
|
||||
for (auto control = _activeControls->reverse_begin(); control != _activeControls->end(); control--) {
|
||||
if ((*control)->onMouseUp(screenSpacePos, backgroundImageSpacePos))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptManager::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (!_activeControls)
|
||||
return false;
|
||||
|
||||
for (auto control = _activeControls->reverse_begin(); control != _activeControls->end(); control--) {
|
||||
if ((*control)->onMouseMove(screenSpacePos, backgroundImageSpacePos))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptManager::onKeyDown(Common::KeyState keyState) {
|
||||
if (!_activeControls)
|
||||
return;
|
||||
|
||||
for (auto &control : (*_activeControls)) {
|
||||
if (control->onKeyDown(keyState))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::onKeyUp(Common::KeyState keyState) {
|
||||
if (!_activeControls)
|
||||
return;
|
||||
|
||||
for (auto &control : (*_activeControls)) {
|
||||
if (control->onKeyUp(keyState))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::changeLocation(const Location &_newLocation) {
|
||||
changeLocation(_newLocation.world, _newLocation.room, _newLocation.node, _newLocation.view, _newLocation.offset);
|
||||
}
|
||||
|
||||
void ScriptManager::changeLocation(char world, char room, char node, char view, uint32 offset) {
|
||||
debugC(1, kDebugScript, "\tPreparing to change location");
|
||||
_nextLocation.world = world;
|
||||
_nextLocation.room = room;
|
||||
_nextLocation.node = node;
|
||||
_nextLocation.view = view;
|
||||
_nextLocation.offset = offset;
|
||||
// If next location is 0000, return to the previous location.
|
||||
if (_nextLocation == "0000") {
|
||||
if (getStateValue(StateKey_World) != 'g' || getStateValue(StateKey_Room) != 'j') {
|
||||
_nextLocation.world = getStateValue(StateKey_LastWorld);
|
||||
_nextLocation.room = getStateValue(StateKey_LastRoom);
|
||||
_nextLocation.node = getStateValue(StateKey_LastNode);
|
||||
_nextLocation.view = getStateValue(StateKey_LastView);
|
||||
_nextLocation.offset = getStateValue(StateKey_LastViewPos);
|
||||
} else {
|
||||
_nextLocation.world = getStateValue(StateKey_Menu_LastWorld);
|
||||
_nextLocation.room = getStateValue(StateKey_Menu_LastRoom);
|
||||
_nextLocation.node = getStateValue(StateKey_Menu_LastNode);
|
||||
_nextLocation.view = getStateValue(StateKey_Menu_LastView);
|
||||
_nextLocation.offset = getStateValue(StateKey_Menu_LastViewPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::ChangeLocationReal(bool isLoading) {
|
||||
assert(_nextLocation.world != 0);
|
||||
debugC(1, kDebugScript, "\tChanging location to: World %c, Room %c, Node %c, View %c, Offset %u", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view, _nextLocation.offset);
|
||||
|
||||
const bool enteringMenu = (_nextLocation.world == 'g' && _nextLocation.room == 'j');
|
||||
const bool leavingMenu = (_currentLocation.world == 'g' && _currentLocation.room == 'j');
|
||||
const bool isSaveScreen = (enteringMenu && _nextLocation.node == 's' && _nextLocation.view == 'e');
|
||||
const bool isRestoreScreen = (enteringMenu && _nextLocation.node == 'r' && _nextLocation.view == 'e');
|
||||
|
||||
if (enteringMenu && !ConfMan.getBool("originalsaveload")) {
|
||||
if (isSaveScreen || isRestoreScreen) {
|
||||
// Hook up the ScummVM save/restore dialog
|
||||
bool gameSavedOrLoaded = _engine->getSaveManager()->scummVMSaveLoadDialog(isSaveScreen);
|
||||
if (!gameSavedOrLoaded || isSaveScreen) {
|
||||
// Reload the current room
|
||||
_nextLocation.world = _currentLocation.world;
|
||||
_nextLocation.room = _currentLocation.room;
|
||||
_nextLocation.node = _currentLocation.node;
|
||||
_nextLocation.view = _currentLocation.view;
|
||||
_nextLocation.offset = _currentLocation.offset;
|
||||
|
||||
return;
|
||||
} else {
|
||||
_currentLocation.world = 'g';
|
||||
_currentLocation.room = '0';
|
||||
_currentLocation.node = '0';
|
||||
_currentLocation.view = '0';
|
||||
_currentLocation.offset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_engine->setRenderDelay(2); // Necessary to ensure proper redraw in certain locations, in particular the infinite corridor in Zork Grand Inquisitor (room th20)
|
||||
|
||||
if (!leavingMenu) {
|
||||
if (!isLoading && !enteringMenu) {
|
||||
setStateValue(StateKey_LastWorld, getStateValue(StateKey_World));
|
||||
setStateValue(StateKey_LastRoom, getStateValue(StateKey_Room));
|
||||
setStateValue(StateKey_LastNode, getStateValue(StateKey_Node));
|
||||
setStateValue(StateKey_LastView, getStateValue(StateKey_View));
|
||||
setStateValue(StateKey_LastViewPos, getStateValue(StateKey_ViewPos));
|
||||
} else {
|
||||
setStateValue(StateKey_Menu_LastWorld, getStateValue(StateKey_World));
|
||||
setStateValue(StateKey_Menu_LastRoom, getStateValue(StateKey_Room));
|
||||
setStateValue(StateKey_Menu_LastNode, getStateValue(StateKey_Node));
|
||||
setStateValue(StateKey_Menu_LastView, getStateValue(StateKey_View));
|
||||
setStateValue(StateKey_Menu_LastViewPos, getStateValue(StateKey_ViewPos));
|
||||
}
|
||||
}
|
||||
|
||||
if (enteringMenu) {
|
||||
if (isSaveScreen && !leavingMenu)
|
||||
_engine->getSaveManager()->prepareSaveBuffer();
|
||||
}
|
||||
else if (leavingMenu)
|
||||
_engine->getSaveManager()->flushSaveBuffer();
|
||||
|
||||
setStateValue(StateKey_World, _nextLocation.world);
|
||||
setStateValue(StateKey_Room, _nextLocation.room);
|
||||
setStateValue(StateKey_Node, _nextLocation.node);
|
||||
setStateValue(StateKey_View, _nextLocation.view);
|
||||
setStateValue(StateKey_ViewPos, _nextLocation.offset);
|
||||
|
||||
_referenceTable.clear();
|
||||
addPuzzlesToReferenceTable(_universe);
|
||||
|
||||
_engine->getMenuManager()->setEnable(0xFFFF);
|
||||
|
||||
TransitionLevel level = NONE;
|
||||
Common::Path filePath;
|
||||
if (_nextLocation.world != _currentLocation.world)
|
||||
level = WORLD;
|
||||
else if (_nextLocation.room != _currentLocation.room)
|
||||
level = ROOM;
|
||||
else if (_nextLocation.node != _currentLocation.node)
|
||||
level = NODE;
|
||||
else if (_nextLocation.view != _currentLocation.view)
|
||||
level = VIEW;
|
||||
|
||||
switch (level) {
|
||||
case WORLD:
|
||||
cleanScriptScope(_world);
|
||||
filePath = Common::Path(Common::String::format("%c.scr", _nextLocation.world));
|
||||
parseScrFile(filePath, _world);
|
||||
// fall through
|
||||
case ROOM:
|
||||
cleanScriptScope(_room);
|
||||
filePath = Common::Path(Common::String::format("%c%c.scr", _nextLocation.world, _nextLocation.room));
|
||||
parseScrFile(filePath, _room);
|
||||
// fall through
|
||||
case NODE:
|
||||
case VIEW:
|
||||
cleanScriptScope(_nodeview);
|
||||
filePath = Common::Path(Common::String::format("%c%c%c%c.scr", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view));
|
||||
parseScrFile(filePath, _nodeview);
|
||||
addPuzzlesToReferenceTable(_world);
|
||||
addPuzzlesToReferenceTable(_room);
|
||||
addPuzzlesToReferenceTable(_nodeview);
|
||||
break;
|
||||
case NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_activeControls = &_nodeview.controls;
|
||||
|
||||
// Revert to the idle cursor
|
||||
_engine->getCursorManager()->changeCursor(CursorIndex_Idle);
|
||||
|
||||
// Change the background position
|
||||
_engine->getRenderManager()->setBackgroundPosition(_nextLocation.offset);
|
||||
|
||||
if (_currentLocation == "0000")
|
||||
level = WORLD;
|
||||
if (level != NONE)
|
||||
_currentLocation = _nextLocation;
|
||||
switch (level) {
|
||||
case WORLD:
|
||||
execScope(_world);
|
||||
// fall through
|
||||
case ROOM:
|
||||
execScope(_room);
|
||||
// fall through
|
||||
case NODE:
|
||||
case VIEW:
|
||||
execScope(_nodeview);
|
||||
break;
|
||||
case NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_engine->getRenderManager()->checkBorders();
|
||||
|
||||
_engine->onMouseMove(); // Trigger a pseudo mouse movement to change cursor if we enter the new location with it already over a hotspot
|
||||
|
||||
debugC(1, kDebugScript, "\tLocation change complete");
|
||||
}
|
||||
|
||||
void ScriptManager::serialize(Common::WriteStream *stream) {
|
||||
stream->writeUint32BE(MKTAG('Z', 'N', 'S', 'G'));
|
||||
stream->writeUint32LE(4);
|
||||
stream->writeUint32LE(0);
|
||||
stream->writeUint32BE(MKTAG('L', 'O', 'C', ' '));
|
||||
stream->writeUint32LE(8);
|
||||
stream->writeByte(getStateValue(StateKey_World));
|
||||
stream->writeByte(getStateValue(StateKey_Room));
|
||||
stream->writeByte(getStateValue(StateKey_Node));
|
||||
stream->writeByte(getStateValue(StateKey_View));
|
||||
stream->writeUint32LE(getStateValue(StateKey_ViewPos));
|
||||
|
||||
for (auto &fx : _activeSideFx)
|
||||
fx->serialize(stream);
|
||||
|
||||
stream->writeUint32BE(MKTAG('F', 'L', 'A', 'G'));
|
||||
|
||||
int32 slots = _engine->getGameId() == GID_NEMESIS ? 31000 : 21000;
|
||||
// Original games use key values up to 29500 and 19737, respectively
|
||||
// Values 30001~31000 and 20001~21000 are now set aside for auxiliary scripting to add extra directional audio effects.
|
||||
|
||||
stream->writeUint32LE(slots * 2);
|
||||
|
||||
for (int32 i = 0; i < slots; i++)
|
||||
stream->writeUint16LE(getStateFlag(i));
|
||||
|
||||
stream->writeUint32BE(MKTAG('P', 'U', 'Z', 'Z'));
|
||||
stream->writeUint32LE(slots * 2);
|
||||
|
||||
for (int32 i = 0; i < slots; i++)
|
||||
stream->writeSint16LE(getStateValue(i));
|
||||
}
|
||||
|
||||
void ScriptManager::deserialize(Common::SeekableReadStream *stream) {
|
||||
// Clear out the current table values
|
||||
_globalState.clear();
|
||||
_globalStateFlags.clear();
|
||||
|
||||
cleanScriptScope(_nodeview);
|
||||
cleanScriptScope(_room);
|
||||
cleanScriptScope(_world);
|
||||
|
||||
_currentLocation.node = 0;
|
||||
_currentLocation.world = 0;
|
||||
_currentLocation.room = 0;
|
||||
_currentLocation.view = 0;
|
||||
|
||||
for (auto &fx : _activeSideFx)
|
||||
delete fx;
|
||||
|
||||
_activeSideFx.clear();
|
||||
_referenceTable.clear();
|
||||
|
||||
if (stream->readUint32BE() != MKTAG('Z', 'N', 'S', 'G') || stream->readUint32LE() != 4) {
|
||||
changeLocation('g', 'a', 'r', 'y', 0);
|
||||
return;
|
||||
}
|
||||
|
||||
stream->seek(4, SEEK_CUR);
|
||||
|
||||
if (stream->readUint32BE() != MKTAG('L', 'O', 'C', ' ') || stream->readUint32LE() != 8) {
|
||||
changeLocation('g', 'a', 'r', 'y', 0);
|
||||
return;
|
||||
}
|
||||
|
||||
Location nextLocation;
|
||||
|
||||
nextLocation.world = stream->readByte();
|
||||
nextLocation.room = stream->readByte();
|
||||
nextLocation.node = stream->readByte();
|
||||
nextLocation.view = stream->readByte();
|
||||
nextLocation.offset = stream->readUint32LE() & 0x0000FFFF;
|
||||
|
||||
while (stream->pos() < stream->size()) {
|
||||
uint32 tag = stream->readUint32BE();
|
||||
uint32 tagSize = stream->readUint32LE();
|
||||
switch (tag) {
|
||||
case MKTAG('T', 'I', 'M', 'R'): {
|
||||
uint32 key = stream->readUint32LE();
|
||||
uint32 time = stream->readUint32LE();
|
||||
if (_engine->getGameId() == GID_GRANDINQUISITOR)
|
||||
time /= 100;
|
||||
else if (_engine->getGameId() == GID_NEMESIS)
|
||||
time /= 1000;
|
||||
addSideFX(new TimerNode(_engine, key, time));
|
||||
}
|
||||
break;
|
||||
case MKTAG('F', 'L', 'A', 'G'):
|
||||
for (uint32 i = 0; i < tagSize / 2; i++)
|
||||
setStateFlagSilent(i, stream->readUint16LE());
|
||||
break;
|
||||
case MKTAG('P', 'U', 'Z', 'Z'):
|
||||
for (uint32 i = 0; i < tagSize / 2; i++)
|
||||
setStateValueSilent(i, stream->readUint16LE());
|
||||
break;
|
||||
default:
|
||||
stream->seek(tagSize, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
_nextLocation = nextLocation;
|
||||
|
||||
ChangeLocationReal(true);
|
||||
|
||||
_engine->setRenderDelay(10);
|
||||
setStateValue(StateKey_RestoreFlag, 1);
|
||||
|
||||
_engine->loadSettings();
|
||||
}
|
||||
|
||||
Location ScriptManager::getCurrentLocation() const {
|
||||
Location location = _currentLocation;
|
||||
location.offset = _engine->getRenderManager()->getCurrentBackgroundOffset();
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
Location ScriptManager::getLastLocation() {
|
||||
Location location;
|
||||
location.world = getStateValue(StateKey_LastWorld);
|
||||
location.room = getStateValue(StateKey_LastRoom);
|
||||
location.node = getStateValue(StateKey_LastNode);
|
||||
location.view = getStateValue(StateKey_LastView);
|
||||
location.offset = getStateValue(StateKey_LastViewPos);
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
Location ScriptManager::getLastMenuLocation() {
|
||||
Location location;
|
||||
location.world = getStateValue(StateKey_Menu_LastWorld);
|
||||
location.room = getStateValue(StateKey_Menu_LastRoom);
|
||||
location.node = getStateValue(StateKey_Menu_LastNode);
|
||||
location.view = getStateValue(StateKey_Menu_LastView);
|
||||
location.offset = getStateValue(StateKey_Menu_LastViewPos);
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
void ScriptManager::addEvent(Common::Event event) {
|
||||
_controlEvents.push_back(event);
|
||||
}
|
||||
|
||||
void ScriptManager::flushEvent(Common::EventType type) {
|
||||
auto event = _controlEvents.begin();
|
||||
while (event != _controlEvents.end()) {
|
||||
if ((*event).type == type)
|
||||
event = _controlEvents.erase(event);
|
||||
else
|
||||
event++;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::trimCommentsAndWhiteSpace(Common::String *string) const {
|
||||
for (int i = string->size() - 1; i >= 0; i--) {
|
||||
if ((*string)[i] == '#')
|
||||
string->erase(i);
|
||||
}
|
||||
string->trim();
|
||||
}
|
||||
|
||||
ValueSlot::ValueSlot(ScriptManager *scriptManager, const char *slotValue):
|
||||
_scriptManager(scriptManager) {
|
||||
_value = 0;
|
||||
_slot = false;
|
||||
const char *isSlot = strstr(slotValue, "[");
|
||||
if (isSlot) {
|
||||
_slot = true;
|
||||
_value = atoi(isSlot + 1);
|
||||
} else {
|
||||
_slot = false;
|
||||
_value = atoi(slotValue);
|
||||
}
|
||||
}
|
||||
|
||||
int16 ValueSlot::getValue() {
|
||||
if (_slot) {
|
||||
if (_value >= 0)
|
||||
return _scriptManager->getStateValue(_value);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return _value;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
412
engines/zvision/scripting/script_manager.h
Normal file
412
engines/zvision/scripting/script_manager.h
Normal file
@@ -0,0 +1,412 @@
|
||||
/* 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 ZVISION_SCRIPT_MANAGER_H
|
||||
#define ZVISION_SCRIPT_MANAGER_H
|
||||
|
||||
#include "common/events.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/queue.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
#include "zvision/scripting/puzzle.h"
|
||||
#include "zvision/scripting/scripting_effect.h"
|
||||
|
||||
namespace Common {
|
||||
class String;
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
enum StateKey {
|
||||
StateKey_World = 3,
|
||||
StateKey_Room = 4,
|
||||
StateKey_Node = 5,
|
||||
StateKey_View = 6,
|
||||
StateKey_ViewPos = 7,
|
||||
StateKey_KeyPress = 8,
|
||||
StateKey_InventoryItem = 9,
|
||||
StateKey_LMouse = 10,
|
||||
StateKey_NotSet = 11, // This key doesn't set
|
||||
StateKey_Rounds = 12,
|
||||
StateKey_Venus = 13,
|
||||
StateKey_RMouse = 18,
|
||||
StateKey_MenuState = 19,
|
||||
StateKey_RestoreFlag = 20,
|
||||
StateKey_Quitting = 39,
|
||||
StateKey_LastWorld = 40,
|
||||
StateKey_LastRoom = 41,
|
||||
StateKey_LastNode = 42,
|
||||
StateKey_LastView = 43,
|
||||
StateKey_LastViewPos = 44,
|
||||
StateKey_Menu_LastWorld = 45,
|
||||
StateKey_Menu_LastRoom = 46,
|
||||
StateKey_Menu_LastNode = 47,
|
||||
StateKey_Menu_LastView = 48,
|
||||
StateKey_Menu_LastViewPos = 49,
|
||||
StateKey_KbdRotateSpeed = 50,
|
||||
StateKey_Subtitles = 51,
|
||||
StateKey_StreamSkipKey = 52,
|
||||
StateKey_RotateSpeed = 53,
|
||||
StateKey_Volume = 56,
|
||||
StateKey_Qsound = 57,
|
||||
StateKey_VenusEnable = 58,
|
||||
StateKey_HighQuality = 59,
|
||||
StateKey_VideoLineSkip = 65,
|
||||
StateKey_Platform = 66, // 0 = Windows, !0 = DOS
|
||||
StateKey_InstallLevel = 67,
|
||||
StateKey_CountryCode = 68,
|
||||
StateKey_CPU = 69, // !1 = 486, 1 = i586/Pentium
|
||||
StateKey_MovieCursor = 70,
|
||||
StateKey_NoTurnAnim = 71,
|
||||
StateKey_WIN958 = 72, // 0 = high system RAM, !0 = low system RAM (<8MB)
|
||||
StateKey_ShowErrorDlg = 73,
|
||||
StateKey_DebugCheats = 74,
|
||||
StateKey_JapanFonts = 75,
|
||||
StateKey_ExecScopeStyle = 76, // 0 = ZGI, 1 = Nemesis
|
||||
StateKey_Brightness = 77,
|
||||
StateKey_MPEGMovies = 78,
|
||||
StateKey_EF9_R = 91,
|
||||
StateKey_EF9_G = 92,
|
||||
StateKey_EF9_B = 93,
|
||||
StateKey_EF9_Speed = 94,
|
||||
StateKey_Inv_Cnt_Slot = 100,
|
||||
StateKey_Inv_1_Slot = 101,
|
||||
StateKey_Inv_49_Slot = 149,
|
||||
// ZGI only
|
||||
StateKey_Inv_TotalSlots = 150,
|
||||
StateKey_Inv_StartSlot = 151,
|
||||
StateKey_Spell_1 = 191,
|
||||
StateKey_Active_Spell = 205,
|
||||
StateKey_Reversed_Spellbooc = 206
|
||||
};
|
||||
|
||||
struct Location {
|
||||
Location() : world('g'), room('a'), node('r'), view('y'), offset(0) {}
|
||||
|
||||
char world;
|
||||
char room;
|
||||
char node;
|
||||
char view;
|
||||
uint32 offset;
|
||||
};
|
||||
|
||||
inline bool operator==(const Location& lhs, const Location& rhs) {
|
||||
return (
|
||||
lhs.world == rhs.world &&
|
||||
lhs.room == rhs.room &&
|
||||
lhs.node == rhs.node &&
|
||||
lhs.view == rhs.view
|
||||
);
|
||||
}
|
||||
|
||||
inline bool operator==(const Location& lhs, const char* rhs) {
|
||||
Common::String lhsStr = Common::String::format("%c%c%c%c", lhs.world, lhs.room, lhs.node, lhs.view);
|
||||
return lhsStr == rhs;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Location& lhs, const Location& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
inline bool operator!=(const Location& lhs, const char* rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
typedef Common::List<Puzzle *> PuzzleList;
|
||||
typedef Common::Queue<Puzzle *> PuzzleQueue;
|
||||
typedef Common::List<Control *> ControlList;
|
||||
typedef Common::HashMap<uint32, int32> StateMap;
|
||||
typedef Common::List<ScriptingEffect *> SideFXList;
|
||||
typedef Common::List<Common::Event> EventList;
|
||||
|
||||
class ScriptManager {
|
||||
public:
|
||||
ScriptManager(ZVision *engine);
|
||||
~ScriptManager();
|
||||
|
||||
private:
|
||||
ZVision *_engine;
|
||||
|
||||
struct ScriptScope {
|
||||
uint32 procCount;
|
||||
|
||||
PuzzleList *scopeQueue; // For adding puzzles to queue
|
||||
PuzzleList *execQueue; // Switch to it when execute
|
||||
PuzzleList privQueueOne;
|
||||
PuzzleList privQueueTwo;
|
||||
|
||||
PuzzleList puzzles;
|
||||
ControlList controls;
|
||||
};
|
||||
|
||||
struct PuzzleRef {
|
||||
Puzzle *puz;
|
||||
ScriptScope *scope;
|
||||
};
|
||||
|
||||
typedef Common::HashMap<uint32, Common::Array<PuzzleRef> > PuzzleMap;
|
||||
|
||||
/**
|
||||
* Holds the global state variable. Do NOT directly modify this. Use the accessors and
|
||||
* mutators getStateValue() and setStateValue(). This ensures that Puzzles that reference a
|
||||
* particular state key are checked after the key is modified.
|
||||
*/
|
||||
StateMap _globalState;
|
||||
/** Holds execute flags */
|
||||
StateMap _globalStateFlags;
|
||||
/** References _globalState keys to Puzzles */
|
||||
PuzzleMap _referenceTable;
|
||||
/** Holds the currently active controls */
|
||||
ControlList *_activeControls;
|
||||
|
||||
EventList _controlEvents;
|
||||
|
||||
ScriptScope _universe;
|
||||
ScriptScope _world;
|
||||
ScriptScope _room;
|
||||
ScriptScope _nodeview;
|
||||
|
||||
/** Holds the currently active timers, musics, other */
|
||||
SideFXList _activeSideFx;
|
||||
|
||||
Location _currentLocation;
|
||||
Location _nextLocation;
|
||||
const uint8 _changeLocationExtraCycles = 16;
|
||||
|
||||
uint32 _currentlyFocusedControl;
|
||||
|
||||
public:
|
||||
enum TransitionLevel {
|
||||
NONE,
|
||||
VIEW,
|
||||
NODE,
|
||||
ROOM,
|
||||
WORLD
|
||||
};
|
||||
|
||||
void initialize(bool restarted = false);
|
||||
void process(uint deltaTimeMillis);
|
||||
void queuePuzzles(uint32 key);
|
||||
|
||||
int getStateValue(uint32 key);
|
||||
void setStateValue(uint32 key, int value);
|
||||
|
||||
uint getStateFlag(uint32 key);
|
||||
void setStateFlag(uint32 key, uint value);
|
||||
void unsetStateFlag(uint32 key, uint value);
|
||||
|
||||
void addControl(Control *control);
|
||||
Control *getControl(uint32 key);
|
||||
|
||||
void enableControl(uint32 key);
|
||||
void disableControl(uint32 key);
|
||||
|
||||
void focusControl(uint32 key);
|
||||
// Only change focus control without call focus/unfocus.
|
||||
void setFocusControlKey(uint32 key);
|
||||
|
||||
void addSideFX(ScriptingEffect *fx);
|
||||
ScriptingEffect *getSideFX(uint32 key);
|
||||
void deleteSideFx(uint32 key);
|
||||
void stopSideFx(uint32 key);
|
||||
void killSideFx(uint32 key);
|
||||
void killSideFxType(ScriptingEffect::ScriptingEffectType type);
|
||||
|
||||
void addEvent(Common::Event);
|
||||
void flushEvent(Common::EventType type);
|
||||
|
||||
/**
|
||||
* Called when LeftMouse is pushed.
|
||||
*
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
*/
|
||||
void onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
|
||||
/**
|
||||
* Called when LeftMouse is lifted.
|
||||
*
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
*/
|
||||
void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
|
||||
/**
|
||||
* Called on every MouseMove.
|
||||
*
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
* @return Was the cursor changed?
|
||||
*/
|
||||
bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
|
||||
/**
|
||||
* Called when a key is pressed.
|
||||
*
|
||||
* @param keycode The key that was pressed
|
||||
*/
|
||||
void onKeyDown(Common::KeyState keyState);
|
||||
/**
|
||||
* Called when a key is released.
|
||||
*
|
||||
* @param keycode The key that was pressed
|
||||
*/
|
||||
void onKeyUp(Common::KeyState keyState);
|
||||
|
||||
/** Mark next location */
|
||||
void changeLocation(char world, char room, char node, char view, uint32 offset);
|
||||
void changeLocation(const Location &_newLocation);
|
||||
|
||||
bool changingLocation() const;
|
||||
|
||||
void serialize(Common::WriteStream *stream);
|
||||
void deserialize(Common::SeekableReadStream *stream);
|
||||
|
||||
Location getCurrentLocation() const;
|
||||
Location getLastLocation();
|
||||
Location getLastMenuLocation();
|
||||
|
||||
/**
|
||||
* Removes any line comments using '#' as a sequence start.
|
||||
* Then removes any trailing and leading 'whitespace' using String::trim()
|
||||
* Note: String::trim uses isspace() to determine what is whitespace and what is not.
|
||||
*
|
||||
* @param string The string to modify. It is modified in place
|
||||
*/
|
||||
void trimCommentsAndWhiteSpace(Common::String *string) const;
|
||||
|
||||
private:
|
||||
void referenceTableAddPuzzle(uint32 key, PuzzleRef ref);
|
||||
void addPuzzlesToReferenceTable(ScriptScope &scope);
|
||||
void updateNodes(uint deltaTimeMillis);
|
||||
void updateControls(uint deltaTimeMillis);
|
||||
/**
|
||||
* Check a puzzle's criteria; execute its actions and set its state to 1 if these critera are met.
|
||||
* Will not check or execute if:
|
||||
* Puzzle is disabled
|
||||
* Puzzle has already triggered and has a state value of 1
|
||||
* procCount has reached zero AND do_me_now is not set
|
||||
*
|
||||
* @param puzzle puzzle to check
|
||||
* @param counter procCount from this puzzle's scope container
|
||||
* Returns true if OK to keep calling this function this frame; false if we should break and start next frame (only used by RestoreGame action)
|
||||
*/
|
||||
bool checkPuzzleCriteria(Puzzle *puzzle, uint counter);
|
||||
void cleanStateTable(); // Set all global state values to zero
|
||||
void cleanScriptScope(ScriptScope &scope); // Resets everything in this scope, all lists empty, procCount to zero.
|
||||
bool execScope(ScriptScope &scope);
|
||||
|
||||
/** Perform change location */
|
||||
void ChangeLocationReal(bool isLoading);
|
||||
|
||||
int8 inventoryGetCount();
|
||||
void inventorySetCount(int8 cnt);
|
||||
int16 inventoryGetItem(int8 id);
|
||||
void inventorySetItem(int8 id, int16 item);
|
||||
|
||||
void setStateFlagSilent(uint32 key, uint value);
|
||||
void setStateValueSilent(uint32 key, int value);
|
||||
|
||||
public:
|
||||
void inventoryAdd(int16 item);
|
||||
void inventoryDrop(int16 item);
|
||||
void inventoryCycle();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Parses a script file into triggers and events
|
||||
*
|
||||
* @param fileName Name of the .scr file
|
||||
* @param isGlobal Are the puzzles included in the file global (true). AKA, the won't be purged during location changes
|
||||
*/
|
||||
void parseScrFile(const Common::Path &fileName, ScriptScope &scope);
|
||||
|
||||
/**
|
||||
* Parses the stream into a Puzzle object
|
||||
* Helper method for parseScrFile.
|
||||
*
|
||||
* @param puzzle The object to store what is parsed
|
||||
* @param stream Scr file stream
|
||||
*/
|
||||
void parsePuzzle(Puzzle *puzzle, Common::SeekableReadStream &stream);
|
||||
|
||||
/**
|
||||
* Parses the stream into a Criteria object
|
||||
* Helper method for parsePuzzle.
|
||||
*
|
||||
* @param criteria Pointer to the Criteria object to fill
|
||||
* @param stream Scr file stream
|
||||
* @param key Puzzle key (for workarounds)
|
||||
* @return Whether any criteria were read
|
||||
*/
|
||||
bool parseCriteria(Common::SeekableReadStream &stream, Common::List<Common::List<Puzzle::CriteriaEntry> > &criteriaList, uint32 key) const;
|
||||
|
||||
/**
|
||||
* Parses the stream into a ResultAction objects
|
||||
* Helper method for parsePuzzle.
|
||||
*
|
||||
* @param stream Scr file stream
|
||||
* @param actionList The list where the results will be added
|
||||
* @param key Puzzle key (for workarounds)
|
||||
* @return Created Results object
|
||||
*/
|
||||
void parseResults(Common::SeekableReadStream &stream, Common::List<ResultAction *> &actionList, uint32 key) const;
|
||||
|
||||
/**
|
||||
* Helper method for parsePuzzle. Parses the stream into a bitwise or of the StateFlags enum
|
||||
*
|
||||
* @param stream Scr file stream
|
||||
* @return Bitwise OR of all the flags set within the puzzle
|
||||
*/
|
||||
uint parseFlags(Common::SeekableReadStream &stream) const;
|
||||
|
||||
/**
|
||||
* Helper method for parseScrFile. Parses the stream into a Control object
|
||||
*
|
||||
* @param line The line initially read
|
||||
* @param stream Scr file stream
|
||||
*/
|
||||
Control *parseControl(Common::String &line, Common::SeekableReadStream &stream);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instances of this polymorphic class function either as a store of a single value, or as a "slot" that returns a StateValue
|
||||
*
|
||||
* @param line The line initially read
|
||||
* @param slotValue A text string containing a number, which may be enclosed within square braces.
|
||||
* If square braces are not present, getValue() will return slotValue.
|
||||
* If square braces are present, getValue() will return the StateValue to which slotValue is the key.
|
||||
*
|
||||
* Once instantiated, the value and nature of slotValue may not be changed.
|
||||
*/
|
||||
class ValueSlot {
|
||||
public:
|
||||
ValueSlot(ScriptManager *scriptManager, const char *slotValue);
|
||||
int16 getValue();
|
||||
private:
|
||||
int16 _value;
|
||||
bool _slot;
|
||||
ScriptManager *_scriptManager;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
123
engines/zvision/scripting/scripting_effect.h
Normal file
123
engines/zvision/scripting/scripting_effect.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/* 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 SCRIPTING_EFFECT_H_INCLUDED
|
||||
#define SCRIPTING_EFFECT_H_INCLUDED
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
struct Point;
|
||||
class WriteStream;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
/**
|
||||
* The base class that represents effects created from Actions.
|
||||
* This class is virtual.
|
||||
*
|
||||
* Detailed Description:
|
||||
* A scene has Controls. By interacting with the controls, the user
|
||||
* causes Actions to execute. Certain Actions create 'effects', for
|
||||
* example, a sound or an animation. This is the base class for
|
||||
* those effects.
|
||||
*/
|
||||
class ScriptingEffect {
|
||||
public:
|
||||
|
||||
enum ScriptingEffectType {
|
||||
SCRIPTING_EFFECT_ANIM = 1,
|
||||
SCRIPTING_EFFECT_AUDIO = 2,
|
||||
SCRIPTING_EFFECT_DISTORT = 4,
|
||||
SCRIPTING_EFFECT_PANTRACK = 8,
|
||||
SCRIPTING_EFFECT_REGION = 16,
|
||||
SCRIPTING_EFFECT_TIMER = 32,
|
||||
SCRIPTING_EFFECT_TTYTXT = 64,
|
||||
SCRIPTING_EFFECT_UNKNOWN = 128,
|
||||
SCRIPTING_EFFECT_ALL = 255
|
||||
};
|
||||
|
||||
ScriptingEffect() : _engine(0), _key(0), _type(SCRIPTING_EFFECT_UNKNOWN) {}
|
||||
ScriptingEffect(ZVision *engine, uint32 key, ScriptingEffectType type) : _engine(engine), _key(key), _type(type) {}
|
||||
virtual ~ScriptingEffect() {}
|
||||
|
||||
uint32 getKey() {
|
||||
return _key;
|
||||
}
|
||||
ScriptingEffectType getType() {
|
||||
return _type;
|
||||
}
|
||||
|
||||
virtual bool process(uint32 deltaTimeInMillis) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Serialize a SideFX for save game use. This should only be used if a SideFX needs
|
||||
* to save values that would be different from initialization. AKA a TimerNode needs to
|
||||
* store the amount of time left on the timer. Any Controls overriding this *MUST* write
|
||||
* their key as the first data outputted. The default implementation is NOP.
|
||||
*
|
||||
* NOTE: If this method is overridden, you MUST also override deserialize()
|
||||
* and needsSerialization()
|
||||
*
|
||||
* @param stream Stream to write any needed data to
|
||||
*/
|
||||
virtual void serialize(Common::WriteStream *stream) {}
|
||||
/**
|
||||
* De-serialize data from a save game stream. This should only be implemented if the
|
||||
* SideFX also implements serialize(). The calling method assumes the size of the
|
||||
* data read from the stream exactly equals that written in serialize(). The default
|
||||
* implementation is NOP.
|
||||
*
|
||||
* NOTE: If this method is overridden, you MUST also override serialize()
|
||||
* and needsSerialization()
|
||||
*
|
||||
* @param stream Save game file stream
|
||||
*/
|
||||
virtual void deserialize(Common::SeekableReadStream *stream) {}
|
||||
/**
|
||||
* If a SideFX overrides serialize() and deserialize(), this should return true
|
||||
*
|
||||
* @return Does the SideFX need save game serialization?
|
||||
*/
|
||||
virtual inline bool needsSerialization() {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool stop() {
|
||||
return true;
|
||||
}
|
||||
virtual void kill() {}
|
||||
|
||||
protected:
|
||||
ZVision *_engine;
|
||||
uint32 _key;
|
||||
ScriptingEffectType _type;
|
||||
|
||||
// Static member functions
|
||||
public:
|
||||
|
||||
};
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif // SCRIPTING_EFFECT_H_INCLUDED
|
||||
116
engines/zvision/sound/midi.cpp
Normal file
116
engines/zvision/sound/midi.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
/* 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 "audio/mididrv.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "zvision/detection.h"
|
||||
#include "zvision/sound/midi.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
MidiManager::MidiManager() {
|
||||
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB);
|
||||
_driver = MidiDriver::createMidi(dev);
|
||||
if (_driver->open()) {
|
||||
warning("Can't open MIDI, no MIDI output!");
|
||||
_available = false;
|
||||
} else {
|
||||
Common::String driverName = MidiDriver::getDeviceString(dev, MidiDriver::DeviceStringType::kDriverName);
|
||||
Common::String deviceName = MidiDriver::getDeviceString(dev, MidiDriver::DeviceStringType::kDeviceName);
|
||||
_mt32 = MidiDriver::getMusicType(dev) == MT_MT32;
|
||||
debugC(1, kDebugSound, "MIDI opened, driver type: %s, device name: %s", driverName.c_str(), deviceName.c_str());
|
||||
_available = true;
|
||||
_maxChannels = _driver->MIDI_CHANNEL_COUNT;
|
||||
}
|
||||
}
|
||||
|
||||
MidiManager::~MidiManager() {
|
||||
stop();
|
||||
_driver->close();
|
||||
delete _driver;
|
||||
}
|
||||
|
||||
void MidiManager::send(uint8 status, uint8 data1, uint8 data2) {
|
||||
assert(status & 0x80 && "Malformed MIDI status byte");
|
||||
assert(!(data1 & 0x80) && "Malformed MIDI data byte 1");
|
||||
assert(!(data2 & 0x80) && "Malformed MIDI data byte 2");
|
||||
_driver->send(status | (data1 << 8) | (data2 << 16));
|
||||
}
|
||||
|
||||
void MidiManager::stop() {
|
||||
for (uint8 i = 0; i < 16; i++)
|
||||
noteOff(i);
|
||||
}
|
||||
|
||||
void MidiManager::noteOn(uint8 channel, uint8 note, uint8 velocity) {
|
||||
assert(channel <= 15);
|
||||
|
||||
_activeChannels[channel].playing = true;
|
||||
_activeChannels[channel].note = note;
|
||||
send(0x90 | channel, note, velocity);
|
||||
debugC(1, kDebugSound, "MIDI note on, channel %d, note %d, velocity %d", channel, note, velocity);
|
||||
}
|
||||
|
||||
void MidiManager::noteOff(uint8 channel) {
|
||||
assert(channel <= 15);
|
||||
|
||||
if (_activeChannels[channel].playing) {
|
||||
_activeChannels[channel].playing = false;
|
||||
send(0x80 | channel, _activeChannels[channel].note);
|
||||
}
|
||||
}
|
||||
|
||||
int8 MidiManager::getFreeChannel() {
|
||||
uint8 start = _mt32 ? 1 : 0; // MT-32 can be used for MIDI, but does not play anything on MIDI channel 0
|
||||
for (uint8 i = start; i < 16; i++)
|
||||
if (!_activeChannels[i].playing)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void MidiManager::setVolume(uint8 channel, uint8 volume) {
|
||||
assert(channel <= 15);
|
||||
debugC(1, kDebugSound, "MIDI volume out %d", volume >> 1);
|
||||
send(0xB0 | channel, 0x07, volume >> 1);
|
||||
}
|
||||
|
||||
void MidiManager::setBalance(uint8 channel, int8 balance) {
|
||||
assert(channel <= 15);
|
||||
uint8 _balance = (uint8)(balance + 128);
|
||||
debugC(1, kDebugSound, "MIDI balance out %d", _balance >> 1);
|
||||
send(0xB0 | channel, 0x08, _balance >> 1);
|
||||
}
|
||||
|
||||
void MidiManager::setPan(uint8 channel, int8 pan) {
|
||||
assert(channel <= 15);
|
||||
uint8 _pan = (uint8)(pan + 128);
|
||||
debugC(1, kDebugSound, "MIDI pan in %d, out %d", pan, _pan >> 1);
|
||||
send(0xB0 | channel, 0x0A, _pan >> 1);
|
||||
}
|
||||
|
||||
void MidiManager::setProgram(uint8 channel, uint8 prog) {
|
||||
assert(channel <= 15);
|
||||
send(0xC0 | channel, prog);
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
65
engines/zvision/sound/midi.h
Normal file
65
engines/zvision/sound/midi.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_MIDI_H
|
||||
#define ZVISION_MIDI_H
|
||||
|
||||
class MidiDriver;
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class MidiManager {
|
||||
public:
|
||||
MidiManager();
|
||||
~MidiManager();
|
||||
|
||||
void stop();
|
||||
void noteOn(uint8 channel, uint8 noteNumber, uint8 velocity);
|
||||
void noteOff(uint8 channel);
|
||||
void setVolume(uint8 channel, uint8 volume);
|
||||
void setBalance(uint8 channel, int8 balance);
|
||||
void setPan(uint8 channel, int8 pan);
|
||||
void setProgram(uint8 channel, uint8 prog);
|
||||
|
||||
int8 getFreeChannel(); // Negative if none available
|
||||
bool isAvailable() const {
|
||||
return _available;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool _available = false;
|
||||
bool _mt32 = false;
|
||||
struct chan {
|
||||
bool playing;
|
||||
uint8 note;
|
||||
|
||||
chan() : playing(false), note(0) {}
|
||||
};
|
||||
void send(uint8 status, uint8 data1 = 0x00, uint8 data2 = 0x00);
|
||||
uint8 _startChannel = 0;
|
||||
uint8 _maxChannels = 16;
|
||||
MidiDriver *_driver;
|
||||
chan _activeChannels[16];
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
291
engines/zvision/sound/volume_manager.cpp
Normal file
291
engines/zvision/sound/volume_manager.cpp
Normal file
@@ -0,0 +1,291 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "zvision/detection.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/sound/volume_manager.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
// Power law with exponent 1.5.
|
||||
static constexpr uint8 powerLaw[256] = {
|
||||
0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4,
|
||||
4, 4, 5, 5, 6, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11,
|
||||
11, 12, 12, 13, 14, 14, 15, 15, 16, 16, 17, 18, 18, 19, 20, 20,
|
||||
21, 21, 22, 23, 23, 24, 25, 26, 26, 27, 28, 28, 29, 30, 31, 31,
|
||||
32, 33, 34, 34, 35, 36, 37, 37, 38, 39, 40, 41, 41, 42, 43, 44,
|
||||
45, 46, 46, 47, 48, 49, 50, 51, 52, 53, 53, 54, 55, 56, 57, 58,
|
||||
59, 60, 61, 62, 63, 64, 65, 65, 66, 67, 68, 69, 70, 71, 72, 73,
|
||||
74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 88, 89, 90,
|
||||
91, 92, 93, 94, 95, 96, 97, 98, 99,100,102,103,104,105,106,107,
|
||||
108,109,110,112,113,114,115,116,117,119,120,121,122,123,124,126,
|
||||
127,128,129,130,132,133,134,135,136,138,139,140,141,142,144,145,
|
||||
146,147,149,150,151,152,154,155,156,158,159,160,161,163,164,165,
|
||||
167,168,169,171,172,173,174,176,177,178,180,181,182,184,185,187,
|
||||
188,189,191,192,193,195,196,197,199,200,202,203,204,206,207,209,
|
||||
210,211,213,214,216,217,218,220,221,223,224,226,227,228,230,231,
|
||||
233,234,236,237,239,240,242,243,245,246,248,249,251,252,254,255
|
||||
};
|
||||
|
||||
static constexpr uint8 logPower[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
|
||||
3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8,
|
||||
8, 9, 9, 10, 10, 11, 11, 12, 13, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 26, 27, 29, 30, 32, 34, 36, 38, 40, 42, 45,
|
||||
47, 50, 52, 55, 58, 62, 65, 69, 73, 77, 81, 86, 90, 96,101,107,
|
||||
113,119,126,133,140,148,156,165,174,184,194,205,217,229,241,255
|
||||
};
|
||||
|
||||
// */
|
||||
static constexpr uint8 logAmplitude[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8,
|
||||
8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 12, 12,
|
||||
2, 13, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, 18, 18, 19,
|
||||
19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27, 28, 29,
|
||||
30, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 45,
|
||||
46, 47, 48, 50, 51, 52, 54, 55, 57, 58, 60, 62, 63, 65, 67, 69,
|
||||
71, 73, 75, 77, 79, 81, 83, 86, 88, 90, 93, 96, 98,101,104,107,
|
||||
110,113,116,119,122,126,129,133,136,140,144,148,152,156,160,165,
|
||||
169,174,179,184,189,194,200,205,211,217,222,229,235,241,248,255
|
||||
};
|
||||
/*/
|
||||
// Old system; this is wrong, caused bug #7176; cloister fountain (value 50 in-game, 127/255) inaudible
|
||||
// Using linear volume served as a temporary fix, but causes other sounds not to play at correct amplitudes (e.g. beehive, door singing in ZGI)
|
||||
static constexpr uint8 logAmplitude[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4,
|
||||
4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
|
||||
8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 14,
|
||||
14, 15, 15, 16, 16, 17, 18, 18, 19, 20, 21, 21, 22, 23, 24, 25,
|
||||
26, 27, 28, 29, 30, 31, 32, 33, 34, 36, 37, 38, 40, 41, 43, 45,
|
||||
46, 48, 50, 52, 53, 55, 57, 60, 62, 64, 67, 69, 72, 74, 77, 80,
|
||||
83, 86, 89, 92, 96, 99,103,107,111,115,119,123,128,133,137,143,
|
||||
148,153,159,165,171,177,184,191,198,205,212,220,228,237,245,255
|
||||
};
|
||||
// */
|
||||
|
||||
/*
|
||||
Estimated relative amplitude of a point sound source as it circles the listener's head from front to rear, due to ear pinna shape.
|
||||
Maximum attenuation -5dB when fully to rear. Seems to give a reasonably realistic effect when tested on the Nemesis cloister fountain.
|
||||
Should be applied AFTER volume profile is applied to script files.
|
||||
Generating function:
|
||||
for 0 < theta < 90, amp = 255;
|
||||
for 90 < theta < 180, amp = 255*10^(1-(cos(2*(theta-90))/4))
|
||||
where theta is the azimuth, in degrees, of the sound source relative to straight ahead of listener
|
||||
Source: Own work; crude and naive model that is probably not remotely scientifically accurate, but good enough for a 30-year-old game.
|
||||
*/
|
||||
static constexpr uint8 directionalAmplitude[181] = {
|
||||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||||
255,255,255,255,255,255,255,255,255,255,255,255,255,254,254,253,
|
||||
252,251,249,248,246,245,243,241,238,236,234,231,228,226,223,220,
|
||||
217,214,211,208,204,201,198,195,191,188,185,181,178,175,171,168,
|
||||
165,162,158,155,152,149,146,143,141,138,135,132,130,127,125,122,
|
||||
120,118,116,113,111,109,108,106,104,102,101, 99, 98, 96, 95, 93,
|
||||
92, 91, 90, 89, 88, 87, 86, 85, 85, 84, 83, 83, 82, 82, 82, 81,
|
||||
81, 81, 81, 81, 81
|
||||
};
|
||||
|
||||
VolumeManager::VolumeManager(ZVision *engine, volumeScaling mode) :
|
||||
_mode(mode) {
|
||||
}
|
||||
|
||||
uint8 VolumeManager::convert(uint8 inputValue) {
|
||||
return convert(inputValue, _mode);
|
||||
}
|
||||
|
||||
uint8 VolumeManager::convert(uint8 inputValue, Math::Angle azimuth, uint8 directionality) {
|
||||
return convert(inputValue, _mode, azimuth, directionality);
|
||||
}
|
||||
|
||||
uint8 VolumeManager::convert(uint8 inputValue, volumeScaling &mode, Math::Angle azimuth, uint8 directionality) {
|
||||
uint8 index = abs(round(azimuth.getDegrees(-180)));
|
||||
uint32 output = convert(inputValue, mode);
|
||||
uint32 directionalOutput = (output * directionalAmplitude[index]) * directionality;
|
||||
directionalOutput /= 0xFF;
|
||||
output *= (0xFF - directionality);
|
||||
output = (output + directionalOutput) / 0xFF;
|
||||
debugC(4, kDebugSound, "Directionally converted output %d", output);
|
||||
return output;
|
||||
}
|
||||
|
||||
uint8 VolumeManager::convert(uint8 inputValue, volumeScaling &mode) {
|
||||
if (inputValue > _scriptScale)
|
||||
inputValue = _scriptScale;
|
||||
uint32 scaledInput = inputValue * 0xFF;
|
||||
scaledInput /= _scriptScale;
|
||||
uint8 output = 0;
|
||||
switch (mode) {
|
||||
case kVolumeLogPower:
|
||||
output = logPower[scaledInput];
|
||||
break;
|
||||
case kVolumeLogAmplitude:
|
||||
output = logAmplitude[scaledInput];
|
||||
break;
|
||||
case kVolumePowerLaw:
|
||||
output = powerLaw[scaledInput];
|
||||
break;
|
||||
case kVolumeParabolic:
|
||||
scaledInput *= scaledInput;
|
||||
output = scaledInput / 0xFF;
|
||||
break;
|
||||
case kVolumeCubic:
|
||||
scaledInput *= scaledInput * scaledInput;
|
||||
output = scaledInput / 0xFE01;
|
||||
break;
|
||||
case kVolumeQuartic:
|
||||
scaledInput *= scaledInput;
|
||||
scaledInput *= scaledInput;
|
||||
output = scaledInput / 0xFD02FF;
|
||||
break;
|
||||
case kVolumeLinear:
|
||||
default:
|
||||
output = scaledInput;
|
||||
break;
|
||||
}
|
||||
debugC(4, kDebugSound, "Scripted volume %d, scaled volume %d, converted output %d", inputValue, scaledInput, output);
|
||||
return output;
|
||||
}
|
||||
|
||||
#if defined(USE_MPEG2) && defined(USE_A52)
|
||||
double VolumeManager::getVobAmplification(Common::String fileName) const {
|
||||
// For some reason, we get much lower volume in the hi-res videos than
|
||||
// in the low-res ones. So we artificially boost the volume. This is an
|
||||
// approximation, but I've tried to match the old volumes reasonably
|
||||
// well.
|
||||
//
|
||||
// Some of these will cause audio clipping. Hopefully not enough to be
|
||||
// noticeable.
|
||||
double amplification = 0.0;
|
||||
if (fileName == "em00d011.vob") {
|
||||
// The finale.
|
||||
amplification = 10.0;
|
||||
} else if (fileName == "em00d021.vob") {
|
||||
// Jack's escape and arrival at Flathead Mesa.
|
||||
amplification = 9.0;
|
||||
} else if (fileName == "em00d032.vob") {
|
||||
// The Grand Inquisitor's speech.
|
||||
amplification = 11.0;
|
||||
} else if (fileName == "em00d122.vob") {
|
||||
// Jack orders you to the radio tower.
|
||||
amplification = 17.0;
|
||||
} else if (fileName == "em3ed012.vob") {
|
||||
// The Grand Inquisitor gets the Coconut of Quendor.
|
||||
amplification = 12.0;
|
||||
} else if (fileName == "g000d101.vob") {
|
||||
// Griff gets captured.
|
||||
amplification = 11.0;
|
||||
} else if (fileName == "g000d111.vob") {
|
||||
// Brog gets totemized. The music seems to be mixed much softer
|
||||
// in this than in the low-resolution version.
|
||||
amplification = 12.0;
|
||||
} else if (fileName == "g000d122.vob") {
|
||||
// Lucy gets captured.
|
||||
amplification = 14.0;
|
||||
} else if (fileName == "g000d302.vob") {
|
||||
// The Grand Inquisitor visits Jack in his cell.
|
||||
amplification = 13.0;
|
||||
} else if (fileName == "g000d312.vob") {
|
||||
// You get captured.
|
||||
amplification = 14.0;
|
||||
} else if (fileName == "g000d411.vob") {
|
||||
// Propaganda On Parade. No need to make it as loud as the
|
||||
// low-resolution version.
|
||||
amplification = 11.0;
|
||||
} else if (fileName == "pe1ed012.vob") {
|
||||
// Jack lets you in with the lantern.
|
||||
amplification = 14.0;
|
||||
} else if (fileName.hasPrefix("pe1ed")) {
|
||||
// Jack answers the door. Several different ways.
|
||||
amplification = 17.0;
|
||||
} else if (fileName == "pe5ed052.vob") {
|
||||
// You get killed by the guards
|
||||
amplification = 12.0;
|
||||
} else if (fileName == "pe6ed012.vob") {
|
||||
// Jack gets captured by the guards
|
||||
amplification = 17.0;
|
||||
} else if (fileName == "pp1ed022.vob") {
|
||||
// Jack examines the lantern
|
||||
amplification = 10.0;
|
||||
} else if (fileName == "qb1ed012.vob") {
|
||||
// Lucy gets invited to the back room
|
||||
amplification = 17.0;
|
||||
} else if (fileName.hasPrefix("qe1ed")) {
|
||||
// Floyd answers the door. Several different ways.
|
||||
amplification = 17.0;
|
||||
} else if (fileName == "qs1ed011.vob") {
|
||||
// Jack explains the rules of the game.
|
||||
amplification = 16.0;
|
||||
} else if (fileName == "qs1ed021.vob") {
|
||||
// Jack loses the game.
|
||||
amplification = 14.0;
|
||||
} else if (fileName == "uc1gd012.vob") {
|
||||
// Y'Gael appears.
|
||||
amplification = 12.0;
|
||||
} else if (fileName == "ue1ud012.vob") {
|
||||
// Jack gets totemized... or what?
|
||||
amplification = 12.0;
|
||||
} else if (fileName == "ue2qd012.vob") {
|
||||
// Jack agrees to totemization.
|
||||
amplification = 10.0;
|
||||
} else if (fileName == "g000d981.vob") {
|
||||
// The Enterprise logo. Has no low-res version. Its volume is
|
||||
// louder than the other logo animations.
|
||||
amplification = 6.2;
|
||||
} else if (fileName.hasPrefix("g000d")) {
|
||||
// The Dolby Digital and Activision logos. They have no low-res
|
||||
// versions, but I've used the low-resolution Activision logo
|
||||
// (slightly different) as reference.
|
||||
amplification = 8.5;
|
||||
}
|
||||
return amplification;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // End of namespace ZVision
|
||||
66
engines/zvision/sound/volume_manager.h
Normal file
66
engines/zvision/sound/volume_manager.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "math/angle.h"
|
||||
#include "zvision/zvision.h"
|
||||
|
||||
#ifndef ZVISION_VOLUME_MANAGER
|
||||
#define ZVISION_VOLUME_MANAGER
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
enum volumeScaling {
|
||||
kVolumeLinear,
|
||||
kVolumePowerLaw,
|
||||
kVolumeParabolic,
|
||||
kVolumeCubic,
|
||||
kVolumeQuartic,
|
||||
kVolumeLogPower,
|
||||
kVolumeLogAmplitude
|
||||
};
|
||||
|
||||
class VolumeManager {
|
||||
public:
|
||||
VolumeManager(ZVision *engine, volumeScaling mode);
|
||||
~VolumeManager() {};
|
||||
volumeScaling getMode() const {
|
||||
return _mode;
|
||||
}
|
||||
void setMode(volumeScaling mode) {
|
||||
_mode = mode;
|
||||
}
|
||||
uint8 convert(uint8 inputValue);
|
||||
uint8 convert(uint8 inputValue, volumeScaling &mode);
|
||||
uint8 convert(uint8 inputValue, Math::Angle azimuth, uint8 directionality = 255);
|
||||
uint8 convert(uint8 inputValue, volumeScaling &mode, Math::Angle azimuth, uint8 directionality = 255);
|
||||
#if defined(USE_MPEG2) && defined(USE_A52)
|
||||
double getVobAmplification(Common::String fileName) const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
uint _scriptScale = 100; // Z-Vision scripts internally use a volume scale of 0-100; ScummVM uses a scale of 0-255.
|
||||
volumeScaling _mode = kVolumeLinear;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
278
engines/zvision/sound/zork_raw.cpp
Normal file
278
engines/zvision/sound/zork_raw.cpp
Normal file
@@ -0,0 +1,278 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "common/bufferedstream.h"
|
||||
#include "common/file.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/tokenizer.h"
|
||||
#include "common/util.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/file/file_manager.h"
|
||||
#include "zvision/sound/zork_raw.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
const int16 RawChunkStream::_stepAdjustmentTable[8] = { -1, -1, -1, 1, 4, 7, 10, 12};
|
||||
|
||||
const int32 RawChunkStream::_amplitudeLookupTable[89] = {
|
||||
0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E,
|
||||
0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001C, 0x001F,
|
||||
0x0022, 0x0025, 0x0029, 0x002D, 0x0032, 0x0037, 0x003C, 0x0042,
|
||||
0x0049, 0x0050, 0x0058, 0x0061, 0x006B, 0x0076, 0x0082, 0x008F,
|
||||
0x009D, 0x00AD, 0x00BE, 0x00D1, 0x00E6, 0x00FD, 0x0117, 0x0133,
|
||||
0x0151, 0x0173, 0x0198, 0x01C1, 0x01EE, 0x0220, 0x0256, 0x0292,
|
||||
0x02D4, 0x031C, 0x036C, 0x03C3, 0x0424, 0x048E, 0x0502, 0x0583,
|
||||
0x0610, 0x06AB, 0x0756, 0x0812, 0x08E0, 0x09C3, 0x0ABD, 0x0BD0,
|
||||
0x0CFF, 0x0E4C, 0x0FBA, 0x114C, 0x1307, 0x14EE, 0x1706, 0x1954,
|
||||
0x1BDC, 0x1EA5, 0x21B6, 0x2515, 0x28CA, 0x2CDF, 0x315B, 0x364B,
|
||||
0x3BB9, 0x41B2, 0x4844, 0x4F7E, 0x5771, 0x602F, 0x69CE, 0x7462, 0x7FFF
|
||||
};
|
||||
|
||||
RawChunkStream::RawChunkStream(bool stereo) {
|
||||
if (stereo)
|
||||
_stereo = 1;
|
||||
else
|
||||
_stereo = 0;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
void RawChunkStream::init() {
|
||||
_lastSample[0].index = 0;
|
||||
_lastSample[0].sample = 0;
|
||||
_lastSample[1].index = 0;
|
||||
_lastSample[1].sample = 0;
|
||||
}
|
||||
|
||||
RawChunkStream::RawChunk RawChunkStream::readNextChunk(Common::SeekableReadStream *stream) {
|
||||
RawChunk tmp;
|
||||
tmp.size = 0;
|
||||
tmp.data = NULL;
|
||||
|
||||
if (!stream || stream->size() == 0 || stream->eos())
|
||||
return tmp;
|
||||
|
||||
tmp.size = (stream->size() - stream->pos()) * 2;
|
||||
tmp.data = (int16 *)calloc(tmp.size, 1);
|
||||
|
||||
readBuffer(tmp.data, stream, stream->size() - stream->pos());
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
int RawChunkStream::readBuffer(int16 *buffer, Common::SeekableReadStream *stream, const int numSamples) {
|
||||
int32 bytesRead = 0;
|
||||
|
||||
// 0: Left, 1: Right
|
||||
uint channel = 0;
|
||||
|
||||
while (bytesRead < numSamples) {
|
||||
byte encodedSample = stream->readByte();
|
||||
if (stream->eos()) {
|
||||
return bytesRead;
|
||||
}
|
||||
bytesRead++;
|
||||
|
||||
int16 index = _lastSample[channel].index;
|
||||
uint32 lookUpSample = _amplitudeLookupTable[index];
|
||||
|
||||
int32 sample = 0;
|
||||
|
||||
if (encodedSample & 0x40)
|
||||
sample += lookUpSample;
|
||||
if (encodedSample & 0x20)
|
||||
sample += lookUpSample >> 1;
|
||||
if (encodedSample & 0x10)
|
||||
sample += lookUpSample >> 2;
|
||||
if (encodedSample & 8)
|
||||
sample += lookUpSample >> 3;
|
||||
if (encodedSample & 4)
|
||||
sample += lookUpSample >> 4;
|
||||
if (encodedSample & 2)
|
||||
sample += lookUpSample >> 5;
|
||||
if (encodedSample & 1)
|
||||
sample += lookUpSample >> 6;
|
||||
if (encodedSample & 0x80)
|
||||
sample = -sample;
|
||||
|
||||
sample += _lastSample[channel].sample;
|
||||
sample = CLIP<int32>(sample, -32768, 32767);
|
||||
|
||||
buffer[bytesRead - 1] = (int16)sample;
|
||||
|
||||
index += _stepAdjustmentTable[(encodedSample >> 4) & 7];
|
||||
index = CLIP<int16>(index, 0, 88);
|
||||
|
||||
_lastSample[channel].sample = sample;
|
||||
_lastSample[channel].index = index;
|
||||
|
||||
// Increment and wrap the channel
|
||||
channel = (channel + 1) & _stereo;
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
const SoundParams RawZorkStream::_zNemSoundParamLookupTable[32] = {
|
||||
{'0', 0x1F40, false, false, false},
|
||||
{'1', 0x1F40, true, false, false},
|
||||
{'2', 0x1F40, false, false, true},
|
||||
{'3', 0x1F40, true, false, true},
|
||||
{'4', 0x2B11, false, false, false},
|
||||
{'5', 0x2B11, true, false, false},
|
||||
{'6', 0x2B11, false, false, true},
|
||||
{'7', 0x2B11, true, false, true},
|
||||
{'8', 0x5622, false, false, false},
|
||||
{'9', 0x5622, true, false, false},
|
||||
{'a', 0x5622, false, false, true},
|
||||
{'b', 0x5622, true, false, true},
|
||||
{'c', 0xAC44, false, false, false},
|
||||
{'d', 0xAC44, true, false, false},
|
||||
{'e', 0xAC44, false, false, true},
|
||||
{'f', 0xAC44, true, false, true},
|
||||
{'g', 0x1F40, false, true, false},
|
||||
{'h', 0x1F40, true, true, false},
|
||||
{'j', 0x1F40, false, true, true},
|
||||
{'k', 0x1F40, true, true, true},
|
||||
{'l', 0x2B11, false, true, false},
|
||||
{'m', 0x2B11, true, true, false},
|
||||
{'n', 0x2B11, false, true, true},
|
||||
{'p', 0x2B11, true, true, true},
|
||||
{'q', 0x5622, false, true, false},
|
||||
{'r', 0x5622, true, true, false},
|
||||
{'s', 0x5622, false, true, true},
|
||||
{'t', 0x5622, true, true, true},
|
||||
{'u', 0xAC44, false, true, false},
|
||||
{'v', 0xAC44, true, true, false},
|
||||
{'w', 0xAC44, false, true, true},
|
||||
{'x', 0xAC44, true, true, true}
|
||||
};
|
||||
|
||||
const SoundParams RawZorkStream::_zgiSoundParamLookupTable[24] = {
|
||||
{'4', 0x2B11, false, false, false},
|
||||
{'5', 0x2B11, true, false, false},
|
||||
{'6', 0x2B11, false, false, true},
|
||||
{'7', 0x2B11, true, false, true},
|
||||
{'8', 0x5622, false, false, false},
|
||||
{'9', 0x5622, true, false, false},
|
||||
{'a', 0x5622, false, false, true},
|
||||
{'b', 0x5622, true, false, true},
|
||||
{'c', 0xAC44, false, false, false},
|
||||
{'d', 0xAC44, true, false, false},
|
||||
{'e', 0xAC44, false, false, true},
|
||||
{'f', 0xAC44, true, false, true},
|
||||
{'g', 0x2B11, false, true, false},
|
||||
{'h', 0x2B11, true, true, false},
|
||||
{'j', 0x2B11, false, true, true},
|
||||
{'k', 0x2B11, true, true, true},
|
||||
{'m', 0x5622, false, true, false},
|
||||
{'n', 0x5622, true, true, false},
|
||||
{'p', 0x5622, false, true, true},
|
||||
{'q', 0x5622, true, true, true},
|
||||
{'r', 0xAC44, false, true, false},
|
||||
{'s', 0xAC44, true, true, false},
|
||||
{'t', 0xAC44, false, true, true},
|
||||
{'u', 0xAC44, true, true, true}
|
||||
};
|
||||
|
||||
RawZorkStream::RawZorkStream(uint32 rate, bool stereo, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream)
|
||||
: _rate(rate),
|
||||
_stereo(0),
|
||||
_stream(stream, disposeStream),
|
||||
_endOfData(false),
|
||||
_streamReader(stereo) {
|
||||
if (stereo)
|
||||
_stereo = 1;
|
||||
|
||||
// Calculate the total playtime of the stream
|
||||
if (stereo)
|
||||
_playtime = Audio::Timestamp(0, _stream->size() / 2, rate);
|
||||
else
|
||||
_playtime = Audio::Timestamp(0, _stream->size(), rate);
|
||||
}
|
||||
|
||||
int RawZorkStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int32 bytesRead = _streamReader.readBuffer(buffer, _stream.get(), numSamples);
|
||||
|
||||
if (_stream->eos())
|
||||
_endOfData = true;
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
bool RawZorkStream::rewind() {
|
||||
_stream->seek(0, 0);
|
||||
_stream->clearErr();
|
||||
_endOfData = false;
|
||||
_streamReader.init();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Audio::RewindableAudioStream *makeRawZorkStream(Common::SeekableReadStream *stream,
|
||||
int rate,
|
||||
bool stereo,
|
||||
DisposeAfterUse::Flag disposeAfterUse) {
|
||||
if (stereo)
|
||||
assert(stream->size() % 2 == 0);
|
||||
|
||||
return new RawZorkStream(rate, stereo, disposeAfterUse, stream);
|
||||
}
|
||||
|
||||
Audio::RewindableAudioStream *makeRawZorkStream(const Common::Path &filePath, ZVision *engine) {
|
||||
Common::String baseName = filePath.baseName();
|
||||
Common::File *file = engine->getFileManager()->open(filePath);
|
||||
|
||||
const SoundParams *soundParams = NULL;
|
||||
|
||||
if (engine->getGameId() == GID_NEMESIS) {
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
if (RawZorkStream::_zNemSoundParamLookupTable[i].identifier == (baseName[6]))
|
||||
soundParams = &RawZorkStream::_zNemSoundParamLookupTable[i];
|
||||
}
|
||||
} else if (engine->getGameId() == GID_GRANDINQUISITOR) {
|
||||
for (int i = 0; i < 24; ++i) {
|
||||
if (RawZorkStream::_zgiSoundParamLookupTable[i].identifier == (baseName[7]))
|
||||
soundParams = &RawZorkStream::_zgiSoundParamLookupTable[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (soundParams == NULL)
|
||||
return NULL;
|
||||
|
||||
if (soundParams->packed) {
|
||||
return makeRawZorkStream(wrapBufferedSeekableReadStream(file, 2048, DisposeAfterUse::YES), soundParams->rate, soundParams->stereo, DisposeAfterUse::YES);
|
||||
} else {
|
||||
byte flags = 0;
|
||||
if (soundParams->bits16)
|
||||
flags |= Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN;
|
||||
if (soundParams->stereo)
|
||||
flags |= Audio::FLAG_STEREO;
|
||||
|
||||
return Audio::makeRawStream(file, soundParams->rate, flags, DisposeAfterUse::YES);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
141
engines/zvision/sound/zork_raw.h
Normal file
141
engines/zvision/sound/zork_raw.h
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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_ZORK_RAW_H
|
||||
#define ZVISION_ZORK_RAW_H
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
struct SoundParams {
|
||||
char identifier;
|
||||
uint32 rate;
|
||||
bool stereo;
|
||||
bool packed;
|
||||
bool bits16;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a ADPCM stream-reader, this class holds context for multi-chunk reading and no buffers.
|
||||
*/
|
||||
class RawChunkStream {
|
||||
public:
|
||||
RawChunkStream(bool stereo);
|
||||
|
||||
~RawChunkStream() {
|
||||
}
|
||||
private:
|
||||
uint _stereo;
|
||||
|
||||
/**
|
||||
* Holds the frequency and index from the last sample
|
||||
* 0 holds the left channel, 1 holds the right channel
|
||||
*/
|
||||
struct {
|
||||
int32 sample;
|
||||
int16 index;
|
||||
} _lastSample[2];
|
||||
|
||||
static const int16 _stepAdjustmentTable[8];
|
||||
static const int32 _amplitudeLookupTable[89];
|
||||
|
||||
public:
|
||||
|
||||
struct RawChunk {
|
||||
int16 *data;
|
||||
uint32 size;
|
||||
};
|
||||
|
||||
void init();
|
||||
//Read next audio portion in new stream (needed for avi), return structure with buffer
|
||||
RawChunk readNextChunk(Common::SeekableReadStream *stream);
|
||||
//Read numSamples from stream to buffer
|
||||
int readBuffer(int16 *buffer, Common::SeekableReadStream *stream, const int numSamples);
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a stream, which allows for playing raw ADPCM data from a stream.
|
||||
*/
|
||||
class RawZorkStream : public Audio::RewindableAudioStream {
|
||||
public:
|
||||
RawZorkStream(uint32 rate, bool stereo, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream);
|
||||
|
||||
~RawZorkStream() override {
|
||||
}
|
||||
|
||||
public:
|
||||
static const SoundParams _zNemSoundParamLookupTable[32];
|
||||
static const SoundParams _zgiSoundParamLookupTable[24];
|
||||
|
||||
private:
|
||||
const int _rate; // Sample rate of stream
|
||||
Audio::Timestamp _playtime; // Calculated total play time
|
||||
Common::DisposablePtr<Common::SeekableReadStream> _stream; // Stream to read data from
|
||||
bool _endOfData; // Whether the stream end has been reached
|
||||
uint _stereo;
|
||||
|
||||
RawChunkStream _streamReader;
|
||||
|
||||
public:
|
||||
int readBuffer(int16 *buffer, const int numSamples) override;
|
||||
|
||||
bool isStereo() const override {
|
||||
return _stereo;
|
||||
}
|
||||
bool endOfData() const override {
|
||||
return _endOfData;
|
||||
}
|
||||
|
||||
int getRate() const override {
|
||||
return _rate;
|
||||
}
|
||||
Audio::Timestamp getLength() const {
|
||||
return _playtime;
|
||||
}
|
||||
|
||||
bool rewind() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an audio stream, which plays from the given stream.
|
||||
*
|
||||
* @param stream Stream object to play from.
|
||||
* @param rate Rate of the sound data.
|
||||
* @param dispose AfterUse Whether to delete the stream after use.
|
||||
* @return The new SeekableAudioStream (or 0 on failure).
|
||||
*/
|
||||
Audio::RewindableAudioStream *makeRawZorkStream(Common::SeekableReadStream *stream,
|
||||
int rate,
|
||||
bool stereo,
|
||||
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
|
||||
|
||||
Audio::RewindableAudioStream *makeRawZorkStream(const Common::Path &filePath, ZVision *engine);
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
65
engines/zvision/text/string_manager.cpp
Normal file
65
engines/zvision/text/string_manager.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/tokenizer.h"
|
||||
#include "graphics/fontman.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/text/string_manager.h"
|
||||
#include "zvision/text/text.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
StringManager::StringManager(ZVision *engine) {
|
||||
}
|
||||
|
||||
StringManager::~StringManager() {
|
||||
|
||||
}
|
||||
|
||||
void StringManager::initialize(ZVisionGameId gameId) {
|
||||
if (gameId == GID_NEMESIS)
|
||||
loadStrFile("nemesis.str");
|
||||
else if (gameId == GID_GRANDINQUISITOR)
|
||||
loadStrFile("inquis.str");
|
||||
}
|
||||
|
||||
void StringManager::loadStrFile(const Common::Path &fileName) {
|
||||
Common::File file;
|
||||
if (!file.open(fileName))
|
||||
error("%s does not exist. String parsing failed", fileName.toString().c_str());
|
||||
|
||||
uint lineNumber = 0;
|
||||
while (!file.eos()) {
|
||||
_lines[lineNumber] = readWideLine(file).encode();
|
||||
|
||||
lineNumber++;
|
||||
assert(lineNumber <= NUM_TEXT_LINES);
|
||||
}
|
||||
}
|
||||
|
||||
const Common::String StringManager::getTextLine(uint stringNumber) {
|
||||
return _lines[stringNumber];
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
66
engines/zvision/text/string_manager.h
Normal file
66
engines/zvision/text/string_manager.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_STRING_MANAGER_H
|
||||
#define ZVISION_STRING_MANAGER_H
|
||||
|
||||
#include "zvision/text/truetype_font.h"
|
||||
|
||||
namespace Graphics {
|
||||
class FontManager;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
class StringManager {
|
||||
public:
|
||||
StringManager(ZVision *engine);
|
||||
~StringManager();
|
||||
|
||||
public:
|
||||
enum {
|
||||
ZVISION_STR_SAVEEXIST = 23,
|
||||
ZVISION_STR_SAVED = 4,
|
||||
ZVISION_STR_SAVEEMPTY = 21,
|
||||
ZVISION_STR_EXITPROMT = 6
|
||||
};
|
||||
|
||||
private:
|
||||
enum {
|
||||
NUM_TEXT_LINES = 56 // Max number of lines in a .str file. We hardcode this number because we know ZNem uses 42 strings and ZGI uses 56
|
||||
};
|
||||
|
||||
private:
|
||||
Common::String _lines[NUM_TEXT_LINES];
|
||||
|
||||
public:
|
||||
void initialize(ZVisionGameId gameId);
|
||||
const Common::String getTextLine(uint stringNumber);
|
||||
|
||||
private:
|
||||
void loadStrFile(const Common::Path &fileName);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
430
engines/zvision/text/subtitle_manager.cpp
Normal file
430
engines/zvision/text/subtitle_manager.cpp
Normal file
@@ -0,0 +1,430 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/file.h"
|
||||
#include "common/system.h"
|
||||
#include "zvision/detection.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/text/subtitle_manager.h"
|
||||
#include "zvision/text/text.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
SubtitleManager::SubtitleManager(ZVision *engine, const ScreenLayout layout, const Graphics::PixelFormat pixelFormat, bool doubleFPS) :
|
||||
_engine(engine),
|
||||
_system(engine->_system),
|
||||
_renderManager(engine->getRenderManager()),
|
||||
_pixelFormat(pixelFormat),
|
||||
_textOffset(layout.workingArea.origin() - layout.textArea.origin()),
|
||||
_textArea(layout.textArea.width(), layout.textArea.height()),
|
||||
_redraw(false),
|
||||
_doubleFPS(doubleFPS),
|
||||
_subId(0) {
|
||||
}
|
||||
|
||||
SubtitleManager::~SubtitleManager() {
|
||||
// Delete all subtitles referenced in subslist
|
||||
for (auto &it : _subsList)
|
||||
delete it._value;
|
||||
}
|
||||
|
||||
void SubtitleManager::process(int32 deltatime) {
|
||||
for (SubtitleMap::iterator it = _subsList.begin(); it != _subsList.end(); it++) {
|
||||
// Update all automatic subtitles
|
||||
if (it->_value->selfUpdate())
|
||||
_redraw = true;
|
||||
// Update all subtitles' respective deletion timers
|
||||
if (it->_value->process(deltatime)) {
|
||||
debugC(4, kDebugSubtitle, "Deleting subtitle, subId=%d", it->_key);
|
||||
_subsFocus.remove(it->_key);
|
||||
delete it->_value;
|
||||
_subsList.erase(it);
|
||||
_redraw = true;
|
||||
}
|
||||
}
|
||||
if (_subsList.size() == 0)
|
||||
if (_subId != 0) {
|
||||
debugC(4, kDebugSubtitle, "Resetting subId to 0");
|
||||
_subId = 0;
|
||||
_subsFocus.clear();
|
||||
}
|
||||
if (_redraw) {
|
||||
debugC(4, kDebugSubtitle, "Redrawing subtitles");
|
||||
// Blank subtitle buffer
|
||||
_renderManager->clearTextSurface();
|
||||
// Render just the most recent subtitle
|
||||
if (_subsFocus.size()) {
|
||||
uint16 curSub = _subsFocus.front();
|
||||
debugC(4, kDebugSubtitle, "Rendering subtitle %d", curSub);
|
||||
Subtitle *sub = _subsList[curSub];
|
||||
if (sub->_lineId >= 0) {
|
||||
Graphics::Surface textSurface;
|
||||
//TODO - make this surface a persistent member of the manager; only call create() when currently displayed subtitle is changed.
|
||||
textSurface.create(sub->_textArea.width(), sub->_textArea.height(), _engine->_resourcePixelFormat);
|
||||
textSurface.fillRect(Common::Rect(sub->_textArea.width(), sub->_textArea.height()), (uint32)-1); // TODO Unnecessary operation? Check later.
|
||||
_engine->getTextRenderer()->drawTextWithWordWrapping(sub->_lines[sub->_lineId].subStr, textSurface, _engine->isWidescreen());
|
||||
_renderManager->blitSurfaceToText(textSurface, sub->_textArea.left, sub->_textArea.top, -1);
|
||||
textSurface.free();
|
||||
sub->_redraw = false;
|
||||
}
|
||||
}
|
||||
_redraw = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SubtitleManager::update(int32 count, uint16 subid) {
|
||||
if (_subsList.contains(subid))
|
||||
if (_subsList[subid]->update(count)) {
|
||||
// _subsFocus.set(subid);
|
||||
_redraw = true;
|
||||
}
|
||||
}
|
||||
|
||||
uint16 SubtitleManager::create(const Common::Path &subname, bool vob) {
|
||||
_subId++;
|
||||
debugC(2, kDebugSubtitle, "Creating scripted subtitle, subId=%d", _subId);
|
||||
_subsList[_subId] = new Subtitle(_engine, subname, vob);
|
||||
_subsFocus.set(_subId);
|
||||
return _subId;
|
||||
}
|
||||
|
||||
uint16 SubtitleManager::create(const Common::Path &subname, Audio::SoundHandle handle) {
|
||||
_subId++;
|
||||
debugC(2, kDebugSubtitle, "Creating scripted subtitle, subId=%d", _subId);
|
||||
_subsList[_subId] = new AutomaticSubtitle(_engine, subname, handle);
|
||||
_subsFocus.set(_subId);
|
||||
return _subId;
|
||||
}
|
||||
|
||||
uint16 SubtitleManager::create(const Common::String &str) {
|
||||
_subId++;
|
||||
debugC(2, kDebugSubtitle, "Creating simple subtitle, subId=%d, message %s", _subId, str.c_str());
|
||||
_subsList[_subId] = new Subtitle(_engine, str, _textArea);
|
||||
_subsFocus.set(_subId);
|
||||
return _subId;
|
||||
}
|
||||
|
||||
void SubtitleManager::destroy(uint16 id) {
|
||||
if (_subsList.contains(id)) {
|
||||
debugC(2, kDebugSubtitle, "Marking subtitle %d for immediate deletion", id);
|
||||
_subsList[id]->_toDelete = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SubtitleManager::destroy(uint16 id, int16 delay) {
|
||||
if (_subsList.contains(id)) {
|
||||
debugC(2, kDebugSubtitle, "Marking subtitle %d for deletion in %dms", id, delay);
|
||||
_subsList[id]->_timer = delay;
|
||||
}
|
||||
}
|
||||
|
||||
void SubtitleManager::timedMessage(const Common::String &str, uint16 milsecs) {
|
||||
uint16 msgid = create(str);
|
||||
debugC(1, kDebugSubtitle, "initiating timed message: %s to subtitle id %d, time %d", str.c_str(), msgid, milsecs);
|
||||
update(0, msgid);
|
||||
process(0);
|
||||
destroy(msgid, milsecs);
|
||||
}
|
||||
|
||||
bool SubtitleManager::askQuestion(const Common::String &str, bool streaming, bool safeDefault) {
|
||||
uint16 msgid = create(str);
|
||||
debugC(1, kDebugSubtitle, "initiating user question: %s to subtitle id %d", str.c_str(), msgid);
|
||||
update(0, msgid);
|
||||
process(0);
|
||||
if(streaming)
|
||||
_renderManager->renderSceneToScreen(true,true,true);
|
||||
else
|
||||
_renderManager->renderSceneToScreen(true);
|
||||
_engine->stopClock();
|
||||
int result = 0;
|
||||
while (result == 0) {
|
||||
Common::Event evnt;
|
||||
while (_engine->getEventManager()->pollEvent(evnt)) {
|
||||
switch (evnt.type) {
|
||||
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
|
||||
if ((ZVisionAction)evnt.customType != kZVisionActionQuit)
|
||||
break;
|
||||
// fall through
|
||||
case Common::EVENT_QUIT:
|
||||
debugC(1, kDebugEvent, "Attempting to quit within quit dialog!");
|
||||
_engine->quit(false);
|
||||
return safeDefault;
|
||||
break;
|
||||
case Common::EVENT_KEYDOWN:
|
||||
// English: yes/no
|
||||
// German: ja/nein
|
||||
// Spanish: si/no
|
||||
// French Nemesis: F4/any other key _engine(engine),
|
||||
// French ZGI: oui/non
|
||||
// TODO: Handle this using the keymapper
|
||||
switch (evnt.kbd.keycode) {
|
||||
case Common::KEYCODE_y:
|
||||
if (_engine->getLanguage() == Common::EN_ANY)
|
||||
result = 2;
|
||||
break;
|
||||
case Common::KEYCODE_j:
|
||||
if (_engine->getLanguage() == Common::DE_DEU)
|
||||
result = 2;
|
||||
break;
|
||||
case Common::KEYCODE_s:
|
||||
if (_engine->getLanguage() == Common::ES_ESP)
|
||||
result = 2;
|
||||
break;
|
||||
case Common::KEYCODE_o:
|
||||
if (_engine->getLanguage() == Common::FR_FRA && _engine->getGameId() == GID_GRANDINQUISITOR)
|
||||
result = 2;
|
||||
break;
|
||||
case Common::KEYCODE_F4:
|
||||
if (_engine->getLanguage() == Common::FR_FRA && _engine->getGameId() == GID_NEMESIS)
|
||||
result = 2;
|
||||
break;
|
||||
case Common::KEYCODE_n:
|
||||
result = 1;
|
||||
break;
|
||||
default:
|
||||
if (_engine->getLanguage() == Common::FR_FRA && _engine->getGameId() == GID_NEMESIS)
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(streaming)
|
||||
_renderManager->renderSceneToScreen(true,true,false);
|
||||
else
|
||||
_renderManager->renderSceneToScreen(true);
|
||||
if (_doubleFPS)
|
||||
_system->delayMillis(33);
|
||||
else
|
||||
_system->delayMillis(66);
|
||||
}
|
||||
destroy(msgid);
|
||||
_engine->startClock();
|
||||
return result == 2;
|
||||
}
|
||||
|
||||
void SubtitleManager::delayedMessage(const Common::String &str, uint16 milsecs) {
|
||||
uint16 msgid = create(str);
|
||||
debugC(1, kDebugSubtitle, "initiating delayed message: %s to subtitle id %d, delay %dms", str.c_str(), msgid, milsecs);
|
||||
update(0, msgid);
|
||||
process(0);
|
||||
_renderManager->renderSceneToScreen(true);
|
||||
_engine->stopClock();
|
||||
|
||||
uint32 stopTime = _system->getMillis() + milsecs;
|
||||
while (_system->getMillis() < stopTime) {
|
||||
Common::Event evnt;
|
||||
while (_engine->getEventManager()->pollEvent(evnt)) {
|
||||
switch (evnt.type) {
|
||||
case Common::EVENT_KEYDOWN:
|
||||
switch (evnt.kbd.keycode) {
|
||||
case Common::KEYCODE_SPACE:
|
||||
case Common::KEYCODE_RETURN:
|
||||
case Common::KEYCODE_ESCAPE:
|
||||
goto skip_delayed_message;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
|
||||
if ((ZVisionAction)evnt.customType != kZVisionActionQuit)
|
||||
break;
|
||||
// fall through
|
||||
case Common::EVENT_QUIT:
|
||||
if (ConfMan.hasKey("confirm_exit") && ConfMan.getBool("confirm_exit"))
|
||||
_engine->quit(true);
|
||||
else
|
||||
_engine->quit(false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
skip_delayed_message:
|
||||
|
||||
_renderManager->renderSceneToScreen(true);
|
||||
if (_doubleFPS)
|
||||
_system->delayMillis(17);
|
||||
else
|
||||
_system->delayMillis(33);
|
||||
}
|
||||
destroy(msgid);
|
||||
_engine->startClock();
|
||||
}
|
||||
|
||||
void SubtitleManager::showDebugMsg(const Common::String &msg, int16 delay) {
|
||||
uint16 msgid = create(msg);
|
||||
debugC(1, kDebugSubtitle, "initiating in-game debug message: %s to subtitle id %d, delay %dms", msg.c_str(), msgid, delay);
|
||||
update(0, msgid);
|
||||
process(0);
|
||||
destroy(msgid, delay);
|
||||
}
|
||||
|
||||
Subtitle::Subtitle(ZVision *engine, const Common::Path &subname, bool vob) :
|
||||
_engine(engine),
|
||||
_lineId(-1),
|
||||
_timer(-1),
|
||||
_toDelete(false),
|
||||
_redraw(false) {
|
||||
Common::File subFile;
|
||||
Common::Point _textOffset = _engine->getSubtitleManager()->getTextOffset();
|
||||
if (!subFile.open(subname)) {
|
||||
warning("Failed to open subtitle %s", subname.toString().c_str());
|
||||
_toDelete = true;
|
||||
return;
|
||||
}
|
||||
// Parse subtitle parameters from script
|
||||
while (!subFile.eos()) {
|
||||
Common::String str = subFile.readLine();
|
||||
if (str.lastChar() == '~')
|
||||
str.deleteLastChar();
|
||||
if (str.matchString("*Initialization*", true)) {
|
||||
// Not used
|
||||
} else if (str.matchString("*Rectangle*", true)) {
|
||||
int32 x1, y1, x2, y2;
|
||||
if (sscanf(str.c_str(), "%*[^:]:%d %d %d %d", &x1, &y1, &x2, &y2) == 4) {
|
||||
_textArea = Common::Rect(x1, y1, x2, y2);
|
||||
debugC(1, kDebugSubtitle, "Original subtitle script rectangle coordinates: l%d, t%d, r%d, b%d", x1, y1, x2, y2);
|
||||
// Original game subtitle scripts appear to define subtitle rectangles relative to origin of working area.
|
||||
// To allow arbitrary aspect ratios, we need to instead place these relative to origin of text area.
|
||||
// This will allow the managed text area to then be arbitrarily placed on the screen to suit different aspect ratios.
|
||||
_textArea.translate(_textOffset.x, _textOffset.y); // Convert working area coordinates to text area coordinates
|
||||
debugC(1, kDebugSubtitle, "Text area coordinates: l%d, t%d, r%d, b%d", _textArea.left, _textArea.top, _textArea.right, _textArea.bottom);
|
||||
}
|
||||
} else if (str.matchString("*TextFile*", true)) {
|
||||
char filename[64];
|
||||
if (sscanf(str.c_str(), "%*[^:]:%s", filename) == 1) {
|
||||
Common::File txtFile;
|
||||
if (txtFile.open(Common::Path(filename))) {
|
||||
while (!txtFile.eos()) {
|
||||
Common::String txtline = readWideLine(txtFile).encode();
|
||||
Line curLine;
|
||||
curLine.start = -1;
|
||||
curLine.stop = -1;
|
||||
curLine.subStr = txtline;
|
||||
_lines.push_back(curLine);
|
||||
}
|
||||
txtFile.close();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int32 st; // Line start time
|
||||
int32 en; // Line end time
|
||||
int32 sb; // Line number
|
||||
if (sscanf(str.c_str(), "%*[^:]:(%d,%d)=%d", &st, &en, &sb) == 3) {
|
||||
if (sb <= (int32)_lines.size()) {
|
||||
if (vob) {
|
||||
// Convert frame number from 15FPS (AVI) to 29.97FPS (VOB) to synchronise with video
|
||||
// st = st * 2997 / 1500;
|
||||
// en = en * 2997 / 1500;
|
||||
st = st * 2900 / 1500; // TODO: Subtitles only synchronise correctly at 29fps, but vob files should be 29.97fps; check if video codec is rounding this value down!
|
||||
en = en * 2900 / 1500;
|
||||
}
|
||||
_lines[sb].start = st;
|
||||
_lines[sb].stop = en;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
subFile.close();
|
||||
}
|
||||
|
||||
Subtitle::Subtitle(ZVision *engine, const Common::String &str, const Common::Rect &textArea) :
|
||||
_engine(engine),
|
||||
_lineId(-1),
|
||||
_timer(-1),
|
||||
_toDelete(false),
|
||||
_redraw(false) {
|
||||
_textArea = textArea;
|
||||
debugC(1, kDebugSubtitle, "Text area coordinates: l%d, t%d, r%d, b%d", _textArea.left, _textArea.top, _textArea.right, _textArea.bottom);
|
||||
Line curLine;
|
||||
curLine.start = -1;
|
||||
curLine.stop = 0;
|
||||
curLine.subStr = str;
|
||||
_lines.push_back(curLine);
|
||||
}
|
||||
|
||||
Subtitle::~Subtitle() {
|
||||
_lines.clear();
|
||||
}
|
||||
|
||||
bool Subtitle::process(int32 deltatime) {
|
||||
if (_timer != -1) {
|
||||
_timer -= deltatime;
|
||||
if (_timer <= 0)
|
||||
_toDelete = true;
|
||||
}
|
||||
return _toDelete;
|
||||
}
|
||||
|
||||
bool Subtitle::update(int32 count) {
|
||||
int16 j = -1;
|
||||
// Search all lines to find first line that encompasses current time/framecount, set j to this
|
||||
for (uint16 i = (_lineId >= 0 ? _lineId : 0); i < _lines.size(); i++)
|
||||
if (count >= _lines[i].start && count <= _lines[i].stop) {
|
||||
j = i;
|
||||
break;
|
||||
}
|
||||
if (j == -1) {
|
||||
// No line exists for current time/framecount
|
||||
if (_lineId != -1) {
|
||||
// Line is set
|
||||
_lineId = -1; // Unset line
|
||||
_redraw = true;
|
||||
}
|
||||
} else {
|
||||
// Line exists for current time/framecount
|
||||
if (j != _lineId && _lines[j].subStr.size()) {
|
||||
// Set line is not equal to current line & current line is not blank
|
||||
_lineId = j; // Set line to current
|
||||
_redraw = true;
|
||||
}
|
||||
}
|
||||
return _redraw;
|
||||
}
|
||||
|
||||
AutomaticSubtitle::AutomaticSubtitle(ZVision *engine, const Common::Path &subname, Audio::SoundHandle handle) :
|
||||
Subtitle(engine, subname, false),
|
||||
_handle(handle) {
|
||||
}
|
||||
|
||||
bool AutomaticSubtitle::selfUpdate() {
|
||||
if (_engine->_mixer->isSoundHandleActive(_handle) && _engine->getScriptManager()->getStateValue(StateKey_Subtitles) == 1)
|
||||
return update(_engine->_mixer->getSoundElapsedTime(_handle) / 100);
|
||||
else {
|
||||
_toDelete = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AutomaticSubtitle::process(int32 deltatime) {
|
||||
Subtitle::process(deltatime);
|
||||
if (!_engine->_mixer->isSoundHandleActive(_handle))
|
||||
_toDelete = true;
|
||||
return _toDelete;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
128
engines/zvision/text/subtitle_manager.h
Normal file
128
engines/zvision/text/subtitle_manager.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/* 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 ZVISION_SUBTITLES_H
|
||||
#define ZVISION_SUBTITLES_H
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/common/focus_list.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
class Subtitle {
|
||||
friend class SubtitleManager;
|
||||
public:
|
||||
Subtitle(ZVision *engine, const Common::Path &subname, bool vob = false); // For scripted subtitles
|
||||
Subtitle(ZVision *engine, const Common::String &str, const Common::Rect &textArea); // For other text messages
|
||||
virtual ~Subtitle();
|
||||
bool update(int32 count); // Return true if necessary to redraw
|
||||
virtual bool selfUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool process(int32 deltatime); // Return true if to be deleted
|
||||
ZVision *_engine;
|
||||
Common::Rect _textArea;
|
||||
int16 _timer; // Always in milliseconds; countdown to deletion
|
||||
bool _toDelete;
|
||||
bool _redraw;
|
||||
|
||||
int16 _lineId;
|
||||
struct Line {
|
||||
int start;
|
||||
int stop;
|
||||
Common::String subStr;
|
||||
};
|
||||
// NB: start & stop do not always use the same units between different instances of this struct!
|
||||
// Sound effect & music subtitles use milliseconds
|
||||
// Video subtitle timings are specified in video frames at 15fps, i.e. in multiples of 66.6' milliseconds!
|
||||
// AVI videos run at 15fps and can have frames counted directly
|
||||
// DVD videos in VOB format run at 29.97 fps and must be converted to work with the subtitle files, which were made for AVI.
|
||||
|
||||
Common::Array<Line> _lines;
|
||||
};
|
||||
|
||||
class AutomaticSubtitle : public Subtitle {
|
||||
public:
|
||||
AutomaticSubtitle(ZVision *engine, const Common::Path &subname, Audio::SoundHandle handle); // For scripted audio subtitles
|
||||
~AutomaticSubtitle() {}
|
||||
|
||||
private:
|
||||
bool process(int32 deltatime); // Return true if to be deleted
|
||||
bool selfUpdate(); // Return true if necessary to redraw
|
||||
Audio::SoundHandle _handle;
|
||||
};
|
||||
|
||||
class SubtitleManager {
|
||||
public:
|
||||
SubtitleManager(ZVision *engine, const ScreenLayout layout, const Graphics::PixelFormat pixelFormat, bool doubleFPS);
|
||||
~SubtitleManager();
|
||||
private:
|
||||
ZVision *_engine;
|
||||
OSystem *_system;
|
||||
RenderManager *_renderManager;
|
||||
const Graphics::PixelFormat _pixelFormat;
|
||||
const Common::Point _textOffset; // Position vector of text area origin relative to working window origin
|
||||
const Common::Rect _textArea;
|
||||
bool _redraw;
|
||||
bool _doubleFPS;
|
||||
// Internal subtitle ID counter
|
||||
uint16 _subId;
|
||||
|
||||
typedef Common::HashMap<uint16, Subtitle *> SubtitleMap;
|
||||
|
||||
// Subtitle list
|
||||
SubtitleMap _subsList;
|
||||
// Subtitle focus history
|
||||
FocusList<uint16> _subsFocus;
|
||||
|
||||
public:
|
||||
// Update all subtitle objects' deletion timers, delete expired subtitles, & redraw most recent. Does NOT update any subtitle's count value or displayed string!
|
||||
void process(int32 deltatime); // deltatime is always milliseconds
|
||||
// Update counter value of referenced subtitle id & set current line to display, if any.
|
||||
void update(int32 count, uint16 subid); // Count is milliseconds for sound & music; frames for video playback.
|
||||
|
||||
const Common::Point &getTextOffset() const {
|
||||
return _textOffset;
|
||||
}
|
||||
|
||||
// Create subtitle object and return ID
|
||||
uint16 create(const Common::Path &subname, bool vob = false);
|
||||
uint16 create(const Common::Path &subname, Audio::SoundHandle handle); // NB this creates an automatic subtitle
|
||||
uint16 create(const Common::String &str);
|
||||
|
||||
// Delete subtitle object by ID
|
||||
void destroy(uint16 id);
|
||||
void destroy(uint16 id, int16 delay);
|
||||
|
||||
bool askQuestion(const Common::String &str, bool streaming = false, bool safeDefault = false);
|
||||
void delayedMessage(const Common::String &str, uint16 milsecs);
|
||||
void timedMessage(const Common::String &str, uint16 milsecs);
|
||||
void showDebugMsg(const Common::String &msg, int16 delay = 3000);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
539
engines/zvision/text/text.cpp
Normal file
539
engines/zvision/text/text.cpp
Normal file
@@ -0,0 +1,539 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/tokenizer.h"
|
||||
#include "graphics/font.h"
|
||||
#include "graphics/fontman.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "graphics/fonts/ttf.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/text/text.h"
|
||||
#include "zvision/text/truetype_font.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
TextStyleState::TextStyleState() {
|
||||
_fontname = "Arial";
|
||||
_blue = 255;
|
||||
_green = 255;
|
||||
_red = 255;
|
||||
_bold = false;
|
||||
#if 0
|
||||
_newline = false;
|
||||
_escapement = 0;
|
||||
#endif
|
||||
_italic = false;
|
||||
_justification = TEXT_JUSTIFY_LEFT;
|
||||
_size = 12;
|
||||
#if 0
|
||||
_skipcolor = false;
|
||||
#endif
|
||||
_strikeout = false;
|
||||
_underline = false;
|
||||
_statebox = 0;
|
||||
_sharp = false;
|
||||
}
|
||||
|
||||
TextChange TextStyleState::parseStyle(const Common::String &str, int16 len) {
|
||||
Common::String buf = Common::String(str.c_str(), len);
|
||||
|
||||
uint retval = TEXT_CHANGE_NONE;
|
||||
|
||||
Common::StringTokenizer tokenizer(buf, " ");
|
||||
Common::String token;
|
||||
|
||||
while (!tokenizer.empty()) {
|
||||
token = tokenizer.nextToken();
|
||||
|
||||
if (token.matchString("font", true)) {
|
||||
token = tokenizer.nextToken();
|
||||
if (token[0] == '"') {
|
||||
Common::String _tmp = Common::String(token.c_str() + 1);
|
||||
|
||||
while (token.lastChar() != '"' && !tokenizer.empty()) {
|
||||
token = tokenizer.nextToken();
|
||||
_tmp += " " + token;
|
||||
}
|
||||
|
||||
if (_tmp.lastChar() == '"')
|
||||
_tmp.deleteLastChar();
|
||||
|
||||
_fontname = _tmp;
|
||||
} else {
|
||||
if (!tokenizer.empty())
|
||||
_fontname = token;
|
||||
}
|
||||
retval |= TEXT_CHANGE_FONT_TYPE;
|
||||
|
||||
} else if (token.matchString("blue", true)) {
|
||||
if (!tokenizer.empty()) {
|
||||
token = tokenizer.nextToken();
|
||||
int32 tmp = atoi(token.c_str());
|
||||
if (_blue != tmp) {
|
||||
_blue = tmp;
|
||||
retval |= TEXT_CHANGE_FONT_STYLE;
|
||||
}
|
||||
}
|
||||
} else if (token.matchString("red", true)) {
|
||||
if (!tokenizer.empty()) {
|
||||
token = tokenizer.nextToken();
|
||||
int32 tmp = atoi(token.c_str());
|
||||
if (_red != tmp) {
|
||||
_red = tmp;
|
||||
retval |= TEXT_CHANGE_FONT_STYLE;
|
||||
}
|
||||
}
|
||||
} else if (token.matchString("green", true)) {
|
||||
if (!tokenizer.empty()) {
|
||||
token = tokenizer.nextToken();
|
||||
int32 tmp = atoi(token.c_str());
|
||||
if (_green != tmp) {
|
||||
_green = tmp;
|
||||
retval |= TEXT_CHANGE_FONT_STYLE;
|
||||
}
|
||||
}
|
||||
} else if (token.matchString("newline", true)) {
|
||||
#if 0
|
||||
if ((retval & TXT_RET_NEWLN) == 0)
|
||||
_newline = 0;
|
||||
|
||||
_newline++;
|
||||
#endif
|
||||
retval |= TEXT_CHANGE_NEWLINE;
|
||||
} else if (token.matchString("point", true)) {
|
||||
if (!tokenizer.empty()) {
|
||||
token = tokenizer.nextToken();
|
||||
int32 tmp = atoi(token.c_str());
|
||||
if (_size != tmp) {
|
||||
_size = tmp;
|
||||
retval |= TEXT_CHANGE_FONT_TYPE;
|
||||
}
|
||||
}
|
||||
} else if (token.matchString("escapement", true)) {
|
||||
if (!tokenizer.empty()) {
|
||||
token = tokenizer.nextToken();
|
||||
#if 0
|
||||
int32 tmp = atoi(token.c_str());
|
||||
_escapement = tmp;
|
||||
#endif
|
||||
}
|
||||
} else if (token.matchString("italic", true)) {
|
||||
if (!tokenizer.empty()) {
|
||||
token = tokenizer.nextToken();
|
||||
if (token.matchString("on", true)) {
|
||||
if (_italic != true) {
|
||||
_italic = true;
|
||||
retval |= TEXT_CHANGE_FONT_STYLE;
|
||||
}
|
||||
} else if (token.matchString("off", true)) {
|
||||
if (_italic != false) {
|
||||
_italic = false;
|
||||
retval |= TEXT_CHANGE_FONT_STYLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (token.matchString("underline", true)) {
|
||||
if (!tokenizer.empty()) {
|
||||
token = tokenizer.nextToken();
|
||||
if (token.matchString("on", true)) {
|
||||
if (_underline != true) {
|
||||
_underline = true;
|
||||
retval |= TEXT_CHANGE_FONT_STYLE;
|
||||
}
|
||||
} else if (token.matchString("off", true)) {
|
||||
if (_underline != false) {
|
||||
_underline = false;
|
||||
retval |= TEXT_CHANGE_FONT_STYLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (token.matchString("strikeout", true)) {
|
||||
if (!tokenizer.empty()) {
|
||||
token = tokenizer.nextToken();
|
||||
if (token.matchString("on", true)) {
|
||||
if (_strikeout != true) {
|
||||
_strikeout = true;
|
||||
retval |= TEXT_CHANGE_FONT_STYLE;
|
||||
}
|
||||
} else if (token.matchString("off", true)) {
|
||||
if (_strikeout != false) {
|
||||
_strikeout = false;
|
||||
retval |= TEXT_CHANGE_FONT_STYLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (token.matchString("bold", true)) {
|
||||
if (!tokenizer.empty()) {
|
||||
token = tokenizer.nextToken();
|
||||
if (token.matchString("on", true)) {
|
||||
if (_bold != true) {
|
||||
_bold = true;
|
||||
retval |= TEXT_CHANGE_FONT_STYLE;
|
||||
}
|
||||
} else if (token.matchString("off", true)) {
|
||||
if (_bold != false) {
|
||||
_bold = false;
|
||||
retval |= TEXT_CHANGE_FONT_STYLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (token.matchString("skipcolor", true)) {
|
||||
if (!tokenizer.empty()) {
|
||||
token = tokenizer.nextToken();
|
||||
#if 0
|
||||
if (token.matchString("on", true)) {
|
||||
_skipcolor = true;
|
||||
} else if (token.matchString("off", true)) {
|
||||
_skipcolor = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else if (token.matchString("image", true)) {
|
||||
// Not used
|
||||
} else if (token.matchString("statebox", true)) {
|
||||
if (!tokenizer.empty()) {
|
||||
token = tokenizer.nextToken();
|
||||
_statebox = atoi(token.c_str());
|
||||
retval |= TEXT_CHANGE_HAS_STATE_BOX;
|
||||
}
|
||||
} else if (token.matchString("justify", true)) {
|
||||
if (!tokenizer.empty()) {
|
||||
token = tokenizer.nextToken();
|
||||
if (token.matchString("center", true))
|
||||
_justification = TEXT_JUSTIFY_CENTER;
|
||||
else if (token.matchString("left", true))
|
||||
_justification = TEXT_JUSTIFY_LEFT;
|
||||
else if (token.matchString("right", true))
|
||||
_justification = TEXT_JUSTIFY_RIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (TextChange)retval;
|
||||
}
|
||||
|
||||
void TextStyleState::readAllStyles(const Common::String &txt) {
|
||||
int16 startTextPosition = -1;
|
||||
int16 endTextPosition = -1;
|
||||
|
||||
for (uint16 i = 0; i < txt.size(); i++) {
|
||||
if (txt[i] == '<')
|
||||
startTextPosition = i;
|
||||
else if (txt[i] == '>') {
|
||||
endTextPosition = i;
|
||||
if (startTextPosition != -1) {
|
||||
if ((endTextPosition - startTextPosition - 1) > 0) {
|
||||
parseStyle(Common::String(txt.c_str() + startTextPosition + 1), endTextPosition - startTextPosition - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void TextStyleState::updateFontWithTextState(StyledTTFont &font) {
|
||||
uint tempStyle = 0;
|
||||
|
||||
if (_bold) {
|
||||
tempStyle |= StyledTTFont::TTF_STYLE_BOLD;
|
||||
}
|
||||
if (_italic) {
|
||||
tempStyle |= StyledTTFont::TTF_STYLE_ITALIC;
|
||||
}
|
||||
if (_underline) {
|
||||
tempStyle |= StyledTTFont::TTF_STYLE_UNDERLINE;
|
||||
}
|
||||
if (_strikeout) {
|
||||
tempStyle |= StyledTTFont::TTF_STYLE_STRIKETHROUGH;
|
||||
}
|
||||
if (_sharp) {
|
||||
tempStyle |= StyledTTFont::TTF_STYLE_SHARP;
|
||||
}
|
||||
|
||||
font.loadFont(_fontname, _size, tempStyle);
|
||||
}
|
||||
|
||||
void TextRenderer::drawTextWithJustification(const Common::String &text, StyledTTFont &font, uint32 color, Graphics::Surface &dest, int lineY, TextJustification justify) {
|
||||
switch (justify) {
|
||||
case TEXT_JUSTIFY_LEFT :
|
||||
font.drawString(&dest, text, 0, lineY, dest.w, color, Graphics::kTextAlignLeft);
|
||||
break;
|
||||
case TEXT_JUSTIFY_CENTER :
|
||||
font.drawString(&dest, text, 0, lineY, dest.w, color, Graphics::kTextAlignCenter);
|
||||
break;
|
||||
case TEXT_JUSTIFY_RIGHT :
|
||||
font.drawString(&dest, text, 0, lineY, dest.w, color, Graphics::kTextAlignRight);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int32 TextRenderer::drawText(const Common::String &text, TextStyleState &state, Graphics::Surface &dest) {
|
||||
StyledTTFont font(_engine);
|
||||
state.updateFontWithTextState(font);
|
||||
|
||||
uint32 color = _engine->_resourcePixelFormat.RGBToColor(state._red, state._green, state._blue);
|
||||
drawTextWithJustification(text, font, color, dest, 0, state._justification);
|
||||
|
||||
return font.getStringWidth(text);
|
||||
}
|
||||
|
||||
struct TextSurface {
|
||||
TextSurface(Graphics::Surface *surface, Common::Point surfaceOffset, uint lineNumber)
|
||||
: _surface(surface),
|
||||
_surfaceOffset(surfaceOffset),
|
||||
_lineNumber(lineNumber) {
|
||||
}
|
||||
|
||||
Graphics::Surface *_surface;
|
||||
Common::Point _surfaceOffset;
|
||||
uint _lineNumber;
|
||||
};
|
||||
|
||||
void TextRenderer::drawTextWithWordWrapping(const Common::String &text, Graphics::Surface &dest, bool blackFrame) {
|
||||
Common::Array<TextSurface> textSurfaces;
|
||||
Common::Array<uint> lineWidths;
|
||||
Common::Array<TextJustification> lineJustifications;
|
||||
|
||||
// Create the initial text state
|
||||
TextStyleState currentState;
|
||||
|
||||
// Create an empty font and bind it to the state
|
||||
StyledTTFont font(_engine);
|
||||
currentState.updateFontWithTextState(font);
|
||||
|
||||
Common::String currentSentence; // Not a true 'grammatical' sentence. Rather, it's just a collection of words
|
||||
Common::String currentWord;
|
||||
int sentenceWidth = 0;
|
||||
int wordWidth = 0;
|
||||
int lineWidth = 0;
|
||||
int lineHeight = font.getFontHeight();
|
||||
|
||||
uint currentLineNumber = 0u;
|
||||
|
||||
uint numSpaces = 0u;
|
||||
int spaceWidth = 0;
|
||||
|
||||
// The pixel offset to the currentSentence
|
||||
Common::Point sentencePixelOffset;
|
||||
|
||||
uint i = 0u;
|
||||
uint stringlen = text.size();
|
||||
|
||||
// Parse entirety of supplied text
|
||||
while (i < stringlen) {
|
||||
// Style tag encountered?
|
||||
if (text[i] == '<') {
|
||||
// Flush the currentWord to the currentSentence
|
||||
currentSentence += currentWord;
|
||||
sentenceWidth += wordWidth;
|
||||
|
||||
// Reset the word variables
|
||||
currentWord.clear();
|
||||
wordWidth = 0;
|
||||
|
||||
// Parse the style tag
|
||||
uint startTextPosition = i;
|
||||
while (i < stringlen && text[i] != '>') {
|
||||
++i;
|
||||
}
|
||||
uint endTextPosition = i;
|
||||
|
||||
uint32 textColor = currentState.getTextColor(_engine);
|
||||
|
||||
uint stateChanges = 0u;
|
||||
if ((endTextPosition - startTextPosition - 1) > 0) {
|
||||
stateChanges = currentState.parseStyle(Common::String(text.c_str() + startTextPosition + 1), endTextPosition - startTextPosition - 1);
|
||||
}
|
||||
|
||||
if (stateChanges & (TEXT_CHANGE_FONT_TYPE | TEXT_CHANGE_FONT_STYLE)) {
|
||||
// Use the last state to render out the current sentence
|
||||
// Styles apply to the text 'after' them
|
||||
if (!currentSentence.empty()) {
|
||||
textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, textColor), sentencePixelOffset, currentLineNumber));
|
||||
|
||||
lineWidth += sentenceWidth;
|
||||
sentencePixelOffset.x += sentenceWidth;
|
||||
|
||||
// Reset the sentence variables
|
||||
currentSentence.clear();
|
||||
sentenceWidth = 0;
|
||||
}
|
||||
|
||||
// Update the current state with the style information
|
||||
currentState.updateFontWithTextState(font);
|
||||
|
||||
lineHeight = MAX(lineHeight, font.getFontHeight());
|
||||
spaceWidth = font.getCharWidth(' ');
|
||||
}
|
||||
if (stateChanges & TEXT_CHANGE_NEWLINE) {
|
||||
// If the current sentence has content, render it out
|
||||
if (!currentSentence.empty()) {
|
||||
textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, textColor), sentencePixelOffset, currentLineNumber));
|
||||
}
|
||||
|
||||
// Set line width
|
||||
lineWidths.push_back(lineWidth + sentenceWidth - (numSpaces * spaceWidth));
|
||||
|
||||
currentSentence.clear();
|
||||
sentenceWidth = 0;
|
||||
|
||||
// Update the offsets
|
||||
sentencePixelOffset.x = 0u;
|
||||
sentencePixelOffset.y += lineHeight;
|
||||
|
||||
// Reset the line variables
|
||||
lineHeight = font.getFontHeight();
|
||||
lineWidth = 0;
|
||||
++currentLineNumber;
|
||||
lineJustifications.push_back(currentState._justification);
|
||||
}
|
||||
if (stateChanges & TEXT_CHANGE_HAS_STATE_BOX) {
|
||||
Common::String temp = Common::String::format("%d", _engine->getScriptManager()->getStateValue(currentState._statebox));
|
||||
wordWidth += font.getStringWidth(temp);
|
||||
|
||||
// If the word causes the line to overflow, render the sentence and start a new line
|
||||
if (lineWidth + sentenceWidth + wordWidth > dest.w) {
|
||||
textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, textColor), sentencePixelOffset, currentLineNumber));
|
||||
|
||||
// Set line width
|
||||
lineWidths.push_back(lineWidth + sentenceWidth - (numSpaces * spaceWidth));
|
||||
|
||||
currentSentence.clear();
|
||||
sentenceWidth = 0;
|
||||
|
||||
// Update the offsets
|
||||
sentencePixelOffset.x = 0u;
|
||||
sentencePixelOffset.y += lineHeight;
|
||||
|
||||
// Reset the line variables
|
||||
lineHeight = font.getFontHeight();
|
||||
lineWidth = 0;
|
||||
++currentLineNumber;
|
||||
lineJustifications.push_back(currentState._justification);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
currentWord += text[i];
|
||||
wordWidth += font.getCharWidth(text[i]);
|
||||
|
||||
if (text[i] == ' ') {
|
||||
// When we hit the first space, flush the current word to the sentence
|
||||
if (!currentWord.empty()) {
|
||||
currentSentence += currentWord;
|
||||
sentenceWidth += wordWidth;
|
||||
|
||||
currentWord.clear();
|
||||
wordWidth = 0;
|
||||
}
|
||||
|
||||
// We track the number of spaces so we can disregard their width in lineWidth calculations
|
||||
++numSpaces;
|
||||
} else {
|
||||
// If the word causes the line to overflow, render the sentence and start a new line
|
||||
if (lineWidth + sentenceWidth + wordWidth > dest.w) {
|
||||
// Only render out content
|
||||
if (!currentSentence.empty()) {
|
||||
textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, currentState.getTextColor(_engine)), sentencePixelOffset, currentLineNumber));
|
||||
}
|
||||
|
||||
// Set line width
|
||||
lineWidths.push_back(lineWidth + sentenceWidth - (numSpaces * spaceWidth));
|
||||
|
||||
currentSentence.clear();
|
||||
sentenceWidth = 0;
|
||||
|
||||
// Update the offsets
|
||||
sentencePixelOffset.x = 0u;
|
||||
sentencePixelOffset.y += lineHeight;
|
||||
|
||||
// Reset the line variables
|
||||
lineHeight = font.getFontHeight();
|
||||
lineWidth = 0;
|
||||
++currentLineNumber;
|
||||
lineJustifications.push_back(currentState._justification);
|
||||
}
|
||||
|
||||
numSpaces = 0u;
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// Render out any remaining words/sentences
|
||||
if (!currentWord.empty() || !currentSentence.empty()) {
|
||||
currentSentence += currentWord;
|
||||
sentenceWidth += wordWidth;
|
||||
|
||||
textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, currentState.getTextColor(_engine)), sentencePixelOffset, currentLineNumber));
|
||||
}
|
||||
|
||||
lineWidths.push_back(lineWidth + sentenceWidth);
|
||||
lineJustifications.push_back(currentState._justification);
|
||||
|
||||
for (Common::Array<TextSurface>::iterator iter = textSurfaces.begin(); iter != textSurfaces.end(); ++iter) {
|
||||
Common::Rect empty;
|
||||
int16 Xpos = iter->_surfaceOffset.x;
|
||||
switch (lineJustifications[iter->_lineNumber]) {
|
||||
case TEXT_JUSTIFY_LEFT :
|
||||
break;
|
||||
case TEXT_JUSTIFY_CENTER :
|
||||
Xpos += ((dest.w - lineWidths[iter->_lineNumber]) / 2);
|
||||
break;
|
||||
case TEXT_JUSTIFY_RIGHT :
|
||||
Xpos += dest.w - lineWidths[iter->_lineNumber];
|
||||
break;
|
||||
}
|
||||
if (blackFrame)
|
||||
_engine->getRenderManager()->blitSurfaceToSurface(*iter->_surface, empty, dest, Xpos, iter->_surfaceOffset.y);
|
||||
else
|
||||
_engine->getRenderManager()->blitSurfaceToSurface(*iter->_surface, empty, dest, Xpos, iter->_surfaceOffset.y, 0);
|
||||
// Release memory
|
||||
iter->_surface->free();
|
||||
delete iter->_surface;
|
||||
}
|
||||
}
|
||||
|
||||
Common::U32String readWideLine(Common::SeekableReadStream &stream) {
|
||||
Common::U32String asciiString;
|
||||
|
||||
while (true) {
|
||||
uint32 value = stream.readUint16LE();
|
||||
if (stream.eos())
|
||||
break;
|
||||
// Check for CRLF
|
||||
if (value == 0x0A0D) {
|
||||
// Read in the extra NULL char
|
||||
stream.readByte(); // \0
|
||||
// End of the line. Break
|
||||
break;
|
||||
}
|
||||
|
||||
asciiString += value;
|
||||
}
|
||||
return asciiString;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
89
engines/zvision/text/text.h
Normal file
89
engines/zvision/text/text.h
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/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_TEXT_H
|
||||
#define ZVISION_TEXT_H
|
||||
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/text/truetype_font.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
enum TextJustification {
|
||||
TEXT_JUSTIFY_CENTER = 0,
|
||||
TEXT_JUSTIFY_LEFT = 1,
|
||||
TEXT_JUSTIFY_RIGHT = 2
|
||||
};
|
||||
|
||||
enum TextChange {
|
||||
TEXT_CHANGE_NONE = 0x0,
|
||||
TEXT_CHANGE_FONT_TYPE = 0x1,
|
||||
TEXT_CHANGE_FONT_STYLE = 0x2,
|
||||
TEXT_CHANGE_NEWLINE = 0x4,
|
||||
TEXT_CHANGE_HAS_STATE_BOX = 0x8
|
||||
};
|
||||
|
||||
class TextStyleState {
|
||||
public:
|
||||
TextStyleState();
|
||||
TextChange parseStyle(const Common::String &str, int16 len);
|
||||
void readAllStyles(const Common::String &txt);
|
||||
void updateFontWithTextState(StyledTTFont &font);
|
||||
|
||||
uint32 getTextColor(ZVision *engine) {
|
||||
return engine->_resourcePixelFormat.RGBToColor(_red, _green, _blue);
|
||||
}
|
||||
|
||||
public:
|
||||
Common::String _fontname;
|
||||
TextJustification _justification;
|
||||
int16 _size;
|
||||
uint8 _red; // 0-255
|
||||
uint8 _green; // 0-255
|
||||
uint8 _blue; // 0-255
|
||||
bool _italic;
|
||||
bool _bold;
|
||||
bool _underline;
|
||||
bool _strikeout;
|
||||
int32 _statebox;
|
||||
bool _sharp;
|
||||
};
|
||||
|
||||
class TextRenderer {
|
||||
public:
|
||||
TextRenderer(ZVision *engine): _engine(engine) {};
|
||||
|
||||
void drawTextWithJustification(const Common::String &text, StyledTTFont &font, uint32 color, Graphics::Surface &dest, int lineY, TextJustification jusification);
|
||||
int32 drawText(const Common::String &text, TextStyleState &state, Graphics::Surface &dest);
|
||||
void drawTextWithWordWrapping(const Common::String &text, Graphics::Surface &dest, bool blackFrame = false);
|
||||
|
||||
private:
|
||||
ZVision *_engine;
|
||||
};
|
||||
|
||||
Common::U32String readWideLine(Common::SeekableReadStream &stream);
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
229
engines/zvision/text/truetype_font.cpp
Normal file
229
engines/zvision/text/truetype_font.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/system.h"
|
||||
#include "common/unicode-bidi.h"
|
||||
#include "common/ustr.h"
|
||||
#include "common/compression/unzip.h"
|
||||
#include "graphics/font.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "graphics/fonts/ttf.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/text/truetype_font.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
const FontStyle systemFonts[] = {
|
||||
{ "*times new roman*", "times", "LiberationSerif" },
|
||||
{ "*times*", "times", "LiberationSerif" },
|
||||
{ "*century schoolbook*", "censcbk", "LiberationSerif" },
|
||||
{ "*garamond*", "gara", "LiberationSerif" },
|
||||
{ "*courier new*", "cour", "LiberationMono" },
|
||||
{ "*courier*", "cour", "LiberationMono" },
|
||||
{ "*ZorkDeath*", "cour", "LiberationMono" },
|
||||
{ "*arial*", "arial", "LiberationSans" },
|
||||
{ "*ZorkNormal*", "arial", "LiberationSans" }
|
||||
};
|
||||
|
||||
const FontStyle getSystemFont(int fontIndex) {
|
||||
return systemFonts[fontIndex];
|
||||
}
|
||||
|
||||
StyledTTFont::StyledTTFont(ZVision *engine) {
|
||||
_engine = engine;
|
||||
_style = 0;
|
||||
_font = nullptr;
|
||||
_lineHeight = 0;
|
||||
}
|
||||
|
||||
StyledTTFont::~StyledTTFont() {
|
||||
delete _font;
|
||||
}
|
||||
|
||||
bool StyledTTFont::loadFont(const Common::String &fontName, int32 point, uint style) {
|
||||
// Don't re-load the font if we've already loaded it
|
||||
// We have to check for empty so we can default to Arial
|
||||
if (!fontName.empty() && _fontName.equalsIgnoreCase(fontName) && _lineHeight == point && _style == style) {
|
||||
return true;
|
||||
}
|
||||
|
||||
_style = style;
|
||||
|
||||
Common::String newFontName;
|
||||
Common::String liberationFontName;
|
||||
|
||||
for (int i = 0; i < FONT_COUNT; i++) {
|
||||
FontStyle curFont = getSystemFont(i);
|
||||
if (fontName.matchString(curFont.zorkFont, true)) {
|
||||
newFontName = curFont.fontBase;
|
||||
liberationFontName = curFont.liberationFontBase;
|
||||
|
||||
if ((_style & TTF_STYLE_BOLD) && (_style & TTF_STYLE_ITALIC)) {
|
||||
newFontName += "bi";
|
||||
liberationFontName += "-BoldItalic";
|
||||
} else if (_style & TTF_STYLE_BOLD) {
|
||||
newFontName += "bd";
|
||||
liberationFontName += "-Bold";
|
||||
} else if (_style & TTF_STYLE_ITALIC) {
|
||||
newFontName += "i";
|
||||
liberationFontName += "-Italic";
|
||||
} else {
|
||||
liberationFontName += "-Regular";
|
||||
}
|
||||
|
||||
newFontName += ".ttf";
|
||||
liberationFontName += ".ttf";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newFontName.empty()) {
|
||||
warning("Could not identify font: %s. Reverting to Arial", fontName.c_str());
|
||||
newFontName = "arial.ttf";
|
||||
liberationFontName = "LiberationSans-Regular.ttf";
|
||||
}
|
||||
|
||||
bool sharp = (_style & TTF_STYLE_SHARP) == TTF_STYLE_SHARP;
|
||||
|
||||
Common::File *file = new Common::File();
|
||||
Graphics::Font *newFont;
|
||||
if (!file->open(Common::Path(newFontName)) &&
|
||||
!file->open(Common::Path(liberationFontName))) {
|
||||
newFont = Graphics::loadTTFFontFromArchive(liberationFontName, point, Graphics::kTTFSizeModeCell, 0, 0, (sharp ? Graphics::kTTFRenderModeMonochrome : Graphics::kTTFRenderModeNormal));
|
||||
delete file;
|
||||
} else {
|
||||
newFont = Graphics::loadTTFFont(file, DisposeAfterUse::YES, point, Graphics::kTTFSizeModeCell, 0, 0, (sharp ? Graphics::kTTFRenderModeMonochrome : Graphics::kTTFRenderModeNormal));
|
||||
}
|
||||
|
||||
if (newFont == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
delete _font;
|
||||
_font = newFont;
|
||||
|
||||
_fontName = fontName;
|
||||
_lineHeight = point;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int StyledTTFont::getFontHeight() {
|
||||
if (_font)
|
||||
return _font->getFontHeight();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int StyledTTFont::getMaxCharWidth() {
|
||||
if (_font)
|
||||
return _font->getMaxCharWidth();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int StyledTTFont::getCharWidth(uint16 chr) {
|
||||
if (_font)
|
||||
return _font->getCharWidth(chr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int StyledTTFont::getKerningOffset(byte left, byte right) {
|
||||
if (_font)
|
||||
return _font->getKerningOffset(left, right);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void StyledTTFont::drawChar(Graphics::Surface *dst, uint16 chr, int x, int y, uint32 color) {
|
||||
if (_font) {
|
||||
_font->drawChar(dst, chr, x, y, color);
|
||||
if (_style & TTF_STYLE_UNDERLINE) {
|
||||
int16 pos = (int16)floor(_font->getFontHeight() * 0.87);
|
||||
int thk = MAX((int)(_font->getFontHeight() * 0.05), 1);
|
||||
dst->fillRect(Common::Rect(x, y + pos, x + _font->getCharWidth(chr), y + pos + thk), color);
|
||||
}
|
||||
if (_style & TTF_STYLE_STRIKETHROUGH) {
|
||||
int16 pos = (int16)floor(_font->getFontHeight() * 0.60);
|
||||
int thk = MAX((int)(_font->getFontHeight() * 0.05), 1);
|
||||
dst->fillRect(Common::Rect(x, y + pos, x + _font->getCharWidth(chr), y + pos + thk), color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StyledTTFont::drawString(Graphics::Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, Graphics::TextAlign align) {
|
||||
if (_font) {
|
||||
Common::U32String u32str = Common::convertUtf8ToUtf32(str);
|
||||
_font->drawString(dst, Common::convertBiDiU32String(u32str).visual, x, y, w, color, align);
|
||||
if (_style & TTF_STYLE_UNDERLINE) {
|
||||
int16 pos = (int16)floor(_font->getFontHeight() * 0.87);
|
||||
int16 wd = MIN(_font->getStringWidth(u32str), w);
|
||||
int16 stX = x;
|
||||
if (align == Graphics::kTextAlignCenter)
|
||||
stX += (w - wd) / 2;
|
||||
else if (align == Graphics::kTextAlignRight)
|
||||
stX += (w - wd);
|
||||
|
||||
int thk = MAX((int)(_font->getFontHeight() * 0.05), 1);
|
||||
|
||||
dst->fillRect(Common::Rect(stX, y + pos, stX + wd, y + pos + thk), color);
|
||||
}
|
||||
if (_style & TTF_STYLE_STRIKETHROUGH) {
|
||||
int16 pos = (int16)floor(_font->getFontHeight() * 0.60);
|
||||
int16 wd = MIN(_font->getStringWidth(u32str), w);
|
||||
int16 stX = x;
|
||||
if (align == Graphics::kTextAlignCenter)
|
||||
stX += (w - wd) / 2;
|
||||
else if (align == Graphics::kTextAlignRight)
|
||||
stX += (w - wd);
|
||||
|
||||
int thk = MAX((int)(_font->getFontHeight() * 0.05), 1);
|
||||
|
||||
dst->fillRect(Common::Rect(stX, y + pos, stX + wd, y + pos + thk), color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int StyledTTFont::getStringWidth(const Common::String &str) {
|
||||
if (_font)
|
||||
return _font->getStringWidth(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Graphics::Surface *StyledTTFont::renderSolidText(const Common::String &str, uint32 color) {
|
||||
Graphics::Surface *tmp = new Graphics::Surface;
|
||||
if (_font) {
|
||||
int16 w = _font->getStringWidth(str);
|
||||
if (w && w < 1024) {
|
||||
tmp->create(w, _font->getFontHeight(), _engine->_resourcePixelFormat);
|
||||
drawString(tmp, str, 0, 0, w, color);
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
89
engines/zvision/text/truetype_font.h
Normal file
89
engines/zvision/text/truetype_font.h
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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// This file is based on engines/wintermute/base/fonts/base_font_truetype.h/.cpp
|
||||
|
||||
#ifndef ZVISION_TRUETYPE_FONT_H
|
||||
#define ZVISION_TRUETYPE_FONT_H
|
||||
|
||||
#include "graphics/font.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
struct FontStyle {
|
||||
const char *zorkFont;
|
||||
const char *fontBase;
|
||||
const char *liberationFontBase;
|
||||
};
|
||||
|
||||
#define FONT_COUNT 9
|
||||
|
||||
class ZVision;
|
||||
|
||||
// Styled TTF
|
||||
class StyledTTFont {
|
||||
public:
|
||||
StyledTTFont(ZVision *engine);
|
||||
~StyledTTFont();
|
||||
|
||||
enum {
|
||||
TTF_STYLE_BOLD = 0x01,
|
||||
TTF_STYLE_ITALIC = 0x02,
|
||||
TTF_STYLE_UNDERLINE = 0x04,
|
||||
TTF_STYLE_STRIKETHROUGH = 0x08,
|
||||
TTF_STYLE_SHARP = 0x10
|
||||
};
|
||||
|
||||
private:
|
||||
ZVision *_engine;
|
||||
Graphics::Font *_font;
|
||||
int _lineHeight;
|
||||
uint _style;
|
||||
Common::String _fontName;
|
||||
|
||||
public:
|
||||
bool loadFont(const Common::String &fontName, int32 point, uint style);
|
||||
|
||||
int getFontHeight();
|
||||
int getMaxCharWidth();
|
||||
int getCharWidth(uint16 chr);
|
||||
int getKerningOffset(byte left, byte right);
|
||||
|
||||
void drawChar(Graphics::Surface *dst, uint16 chr, int x, int y, uint32 color);
|
||||
|
||||
void drawString(Graphics::Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
|
||||
int getStringWidth(const Common::String &str);
|
||||
|
||||
Graphics::Surface *renderSolidText(const Common::String &str, uint32 color);
|
||||
|
||||
bool isLoaded() {
|
||||
return _font != NULL;
|
||||
};
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
314
engines/zvision/video/rlf_decoder.cpp
Normal file
314
engines/zvision/video/rlf_decoder.cpp
Normal file
@@ -0,0 +1,314 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/file.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "zvision/detection.h"
|
||||
#include "zvision/video/rlf_decoder.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
RLFDecoder::~RLFDecoder() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool RLFDecoder::loadStream(Common::SeekableReadStream *stream) {
|
||||
debugC(5, kDebugVideo, "loadStream()");
|
||||
close();
|
||||
bool isValid = false;
|
||||
// Check if the stream is valid
|
||||
if (stream && !stream->err() && stream->readUint32BE() == MKTAG('F', 'E', 'L', 'R')) {
|
||||
addTrack(new RLFVideoTrack(stream));
|
||||
isValid = true;
|
||||
} else {
|
||||
warning("Invalid rlf stream");
|
||||
}
|
||||
debugC(5, kDebugVideo, "~loadStream()");
|
||||
return isValid;
|
||||
}
|
||||
|
||||
RLFDecoder::RLFVideoTrack::RLFVideoTrack(Common::SeekableReadStream *stream)
|
||||
: _readStream(stream),
|
||||
_lastFrameRead(0),
|
||||
_frameCount(0),
|
||||
_width(0),
|
||||
_height(0),
|
||||
_frameTime(0),
|
||||
_frames(0),
|
||||
_displayedFrame(-1),
|
||||
_frameBufferByteSize(0) {
|
||||
|
||||
if (!readHeader()) {
|
||||
warning("Not a RLF animation file. Wrong magic number");
|
||||
return;
|
||||
}
|
||||
|
||||
_currentFrameBuffer.create(_width, _height, getPixelFormat());
|
||||
_frameBufferByteSize = _width * _height * sizeof(uint16);
|
||||
|
||||
_frames = new Frame[_frameCount];
|
||||
|
||||
// Read in each frame
|
||||
for (uint i = 0; i < _frameCount; ++i) {
|
||||
_frames[i] = readNextFrame();
|
||||
}
|
||||
}
|
||||
|
||||
RLFDecoder::RLFVideoTrack::~RLFVideoTrack() {
|
||||
for (uint i = 0; i < _frameCount; ++i) {
|
||||
delete[] _frames[i].encodedData;
|
||||
}
|
||||
delete[] _frames;
|
||||
delete _readStream;
|
||||
_currentFrameBuffer.free();
|
||||
}
|
||||
|
||||
bool RLFDecoder::RLFVideoTrack::readHeader() {
|
||||
// Read the header
|
||||
_readStream->readUint32LE(); // Size1
|
||||
_readStream->readUint32LE(); // Unknown1
|
||||
_readStream->readUint32LE(); // Unknown2
|
||||
_frameCount = _readStream->readUint32LE(); // Frame count
|
||||
|
||||
// Since we don't need any of the data, we can just seek right to the
|
||||
// entries we need rather than read in all the individual entries.
|
||||
_readStream->seek(136, SEEK_CUR);
|
||||
|
||||
//// Read CIN header
|
||||
//_readStream->readUint32BE(); // Magic number FNIC
|
||||
//_readStream->readUint32LE(); // Size2
|
||||
//_readStream->readUint32LE(); // Unknown3
|
||||
//_readStream->readUint32LE(); // Unknown4
|
||||
//_readStream->readUint32LE(); // Unknown5
|
||||
//_readStream->seek(0x18, SEEK_CUR); // VRLE
|
||||
//_readStream->readUint32LE(); // LRVD
|
||||
//_readStream->readUint32LE(); // Unknown6
|
||||
//_readStream->seek(0x18, SEEK_CUR); // HRLE
|
||||
//_readStream->readUint32LE(); // ELHD
|
||||
//_readStream->readUint32LE(); // Unknown7
|
||||
//_readStream->seek(0x18, SEEK_CUR); // HKEY
|
||||
//_readStream->readUint32LE(); // ELRH
|
||||
|
||||
//// Read MIN info header
|
||||
//_readStream->readUint32BE(); // Magic number FNIM
|
||||
//_readStream->readUint32LE(); // Size3
|
||||
//_readStream->readUint32LE(); // OEDV
|
||||
//_readStream->readUint32LE(); // Unknown8
|
||||
//_readStream->readUint32LE(); // Unknown9
|
||||
//_readStream->readUint32LE(); // Unknown10
|
||||
_width = _readStream->readUint32LE(); // Width
|
||||
_height = _readStream->readUint32LE(); // Height
|
||||
|
||||
// Read time header
|
||||
_readStream->readUint32BE(); // Magic number EMIT
|
||||
_readStream->readUint32LE(); // Size4
|
||||
_readStream->readUint32LE(); // Unknown11
|
||||
_frameTime = _readStream->readUint32LE() / 10; // Frame time in microseconds
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RLFDecoder::RLFVideoTrack::Frame RLFDecoder::RLFVideoTrack::readNextFrame() {
|
||||
RLFDecoder::RLFVideoTrack::Frame frame;
|
||||
|
||||
_readStream->readUint32BE(); // Magic number MARF
|
||||
uint32 size = _readStream->readUint32LE(); // Size
|
||||
_readStream->readUint32LE(); // Unknown1
|
||||
_readStream->readUint32LE(); // Unknown2
|
||||
uint32 type = _readStream->readUint32BE(); // Either ELHD or ELRH
|
||||
uint32 headerSize = _readStream->readUint32LE(); // Offset from the beginning of this frame to the frame data. Should always be 28
|
||||
_readStream->readUint32LE(); // Unknown3
|
||||
|
||||
frame.encodedSize = size - headerSize;
|
||||
frame.encodedData = new int8[frame.encodedSize];
|
||||
_readStream->read(frame.encodedData, frame.encodedSize);
|
||||
|
||||
if (type == MKTAG('E', 'L', 'H', 'D')) {
|
||||
frame.type = Masked;
|
||||
} else if (type == MKTAG('E', 'L', 'R', 'H')) {
|
||||
frame.type = Simple;
|
||||
_completeFrames.push_back(_lastFrameRead);
|
||||
} else {
|
||||
warning("Frame %u doesn't have type that can be decoded", _lastFrameRead);
|
||||
}
|
||||
|
||||
_lastFrameRead++;
|
||||
return frame;
|
||||
}
|
||||
|
||||
bool RLFDecoder::RLFVideoTrack::seek(const Audio::Timestamp &time) {
|
||||
uint frame = getFrameAtTime(time);
|
||||
assert(frame < _frameCount);
|
||||
|
||||
if ((uint)_displayedFrame == frame)
|
||||
return true;
|
||||
|
||||
int closestFrame = _displayedFrame;
|
||||
int distance = (int)frame - closestFrame;
|
||||
|
||||
if (distance < 0) {
|
||||
for (uint i = 0; i < _completeFrames.size(); ++i) {
|
||||
if (_completeFrames[i] > frame)
|
||||
break;
|
||||
closestFrame = _completeFrames[i];
|
||||
}
|
||||
} else {
|
||||
for (uint i = 0; i < _completeFrames.size(); ++i) {
|
||||
int newDistance = (int)frame - (int)(_completeFrames[i]);
|
||||
if (newDistance < 0)
|
||||
break;
|
||||
if (newDistance < distance) {
|
||||
closestFrame = _completeFrames[i];
|
||||
distance = newDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint i = closestFrame; i < frame; ++i) {
|
||||
applyFrameToCurrent(i);
|
||||
}
|
||||
|
||||
_displayedFrame = frame - 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const Graphics::Surface *RLFDecoder::RLFVideoTrack::decodeNextFrame() {
|
||||
if (_displayedFrame >= (int)_frameCount)
|
||||
return NULL;
|
||||
|
||||
_displayedFrame++;
|
||||
applyFrameToCurrent(_displayedFrame);
|
||||
|
||||
return &_currentFrameBuffer;
|
||||
}
|
||||
|
||||
void RLFDecoder::RLFVideoTrack::applyFrameToCurrent(uint frameNumber) {
|
||||
if (_frames[frameNumber].type == Masked) {
|
||||
decodeMaskedRunLengthEncoding(_frames[frameNumber].encodedData, (int8 *)_currentFrameBuffer.getPixels(), _frames[frameNumber].encodedSize, _frameBufferByteSize);
|
||||
} else if (_frames[frameNumber].type == Simple) {
|
||||
decodeSimpleRunLengthEncoding(_frames[frameNumber].encodedData, (int8 *)_currentFrameBuffer.getPixels(), _frames[frameNumber].encodedSize, _frameBufferByteSize);
|
||||
}
|
||||
}
|
||||
|
||||
void RLFDecoder::RLFVideoTrack::decodeMaskedRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const {
|
||||
uint32 sourceOffset = 0;
|
||||
uint32 destOffset = 0;
|
||||
int16 numberOfCopy = 0;
|
||||
|
||||
while (sourceOffset < sourceSize) {
|
||||
int8 numberOfSamples = source[sourceOffset];
|
||||
sourceOffset++;
|
||||
|
||||
// If numberOfSamples is negative, the next abs(numberOfSamples) samples should
|
||||
// be copied directly from source to dest
|
||||
if (numberOfSamples < 0) {
|
||||
numberOfCopy = -numberOfSamples;
|
||||
|
||||
while (numberOfCopy > 0) {
|
||||
if (sourceOffset + 1 >= sourceSize) {
|
||||
return;
|
||||
} else if (destOffset + 1 >= destSize) {
|
||||
debugC(3, kDebugVideo, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize);
|
||||
return;
|
||||
}
|
||||
|
||||
WRITE_UINT16(dest + destOffset, READ_LE_UINT16(source + sourceOffset));
|
||||
|
||||
sourceOffset += 2;
|
||||
destOffset += 2;
|
||||
numberOfCopy--;
|
||||
}
|
||||
|
||||
// If numberOfSamples is >= 0, move destOffset forward ((numberOfSamples * 2) + 2)
|
||||
// This function assumes the dest buffer has been memset with 0's.
|
||||
} else {
|
||||
if (sourceOffset + 1 >= sourceSize) {
|
||||
return;
|
||||
} else if (destOffset + 1 >= destSize) {
|
||||
debugC(3, kDebugVideo, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize);
|
||||
return;
|
||||
}
|
||||
|
||||
destOffset += (numberOfSamples * 2) + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RLFDecoder::RLFVideoTrack::decodeSimpleRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const {
|
||||
uint32 sourceOffset = 0;
|
||||
uint32 destOffset = 0;
|
||||
int16 numberOfCopy = 0;
|
||||
|
||||
while (sourceOffset < sourceSize) {
|
||||
int8 numberOfSamples = source[sourceOffset];
|
||||
sourceOffset++;
|
||||
|
||||
// If numberOfSamples is negative, the next abs(numberOfSamples) samples should
|
||||
// be copied directly from source to dest
|
||||
if (numberOfSamples < 0) {
|
||||
numberOfCopy = -numberOfSamples;
|
||||
|
||||
while (numberOfCopy > 0) {
|
||||
if (sourceOffset + 1 >= sourceSize) {
|
||||
return;
|
||||
} else if (destOffset + 1 >= destSize) {
|
||||
debugC(3, kDebugVideo, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize);
|
||||
return;
|
||||
}
|
||||
|
||||
WRITE_UINT16(dest + destOffset, READ_LE_UINT16(source + sourceOffset));
|
||||
|
||||
sourceOffset += 2;
|
||||
destOffset += 2;
|
||||
numberOfCopy--;
|
||||
}
|
||||
|
||||
// If numberOfSamples is >= 0, copy one sample from source to the
|
||||
// next (numberOfSamples + 2) dest spots
|
||||
} else {
|
||||
if (sourceOffset + 1 >= sourceSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16 sampleColor = READ_LE_UINT16(source + sourceOffset);
|
||||
sourceOffset += 2;
|
||||
|
||||
numberOfCopy = numberOfSamples + 2;
|
||||
while (numberOfCopy > 0) {
|
||||
if (destOffset + 1 >= destSize) {
|
||||
debugC(3, kDebugVideo, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize);
|
||||
return;
|
||||
}
|
||||
|
||||
WRITE_UINT16(dest + destOffset, sampleColor);
|
||||
destOffset += 2;
|
||||
numberOfCopy--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user