Initial commit

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

View File

@@ -0,0 +1,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

View File

@@ -0,0 +1,79 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef 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

View File

@@ -0,0 +1,108 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "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

View File

@@ -0,0 +1,62 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef 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

View 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

View File

@@ -0,0 +1,146 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#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

View 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

View File

@@ -0,0 +1,55 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef 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

View File

@@ -0,0 +1,80 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#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

View File

@@ -0,0 +1,55 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef 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

View File

@@ -0,0 +1,77 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "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

View File

@@ -0,0 +1,58 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#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

View 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

View File

@@ -0,0 +1,74 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#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