aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/pegasus/elements.cpp121
-rw-r--r--engines/pegasus/elements.h59
-rw-r--r--engines/pegasus/pegasus.cpp8
-rw-r--r--engines/pegasus/pegasus.h7
-rwxr-xr-xengines/pegasus/timers.cpp282
-rwxr-xr-xengines/pegasus/timers.h166
6 files changed, 643 insertions, 0 deletions
diff --git a/engines/pegasus/elements.cpp b/engines/pegasus/elements.cpp
index 132c19a02a..695a6e54b8 100644
--- a/engines/pegasus/elements.cpp
+++ b/engines/pegasus/elements.cpp
@@ -23,6 +23,9 @@
*
*/
+#include "common/macresman.h"
+#include "common/stream.h"
+
#include "pegasus/elements.h"
#include "pegasus/graphics.h"
@@ -238,4 +241,122 @@ void EnergyBar::calcLevelRect(Common::Rect &r) const {
}
}
+IdlerAnimation::IdlerAnimation(const tDisplayElementID id) : Animation(id) {
+ _lastTime = 0xffffffff;
+}
+
+void IdlerAnimation::startDisplaying() {
+ if (!isDisplaying()) {
+ Animation::startDisplaying();
+ startIdling();
+ }
+}
+
+void IdlerAnimation::stopDisplaying() {
+ if (isDisplaying()) {
+ Animation::stopDisplaying();
+ stopIdling();
+ }
+}
+
+void IdlerAnimation::useIdleTime() {
+ uint32 currentTime = getTime();
+
+ if (currentTime != _lastTime) {
+ _lastTime = currentTime;
+ timeChanged(_lastTime);
+ }
+}
+
+void IdlerAnimation::timeChanged(const TimeValue) {
+ triggerRedraw();
+}
+
+FrameSequence::FrameSequence(const tDisplayElementID id) : IdlerAnimation(id) {
+ _duration = 0;
+ _currentFrameNum = 0;
+ _resFork = new Common::MacResManager();
+}
+
+FrameSequence::~FrameSequence() {
+ delete _resFork;
+}
+
+void FrameSequence::useFileName(const Common::String &fileName) {
+ _resFork->open(fileName);
+}
+
+void FrameSequence::openFrameSequence() {
+ if (!_resFork->hasResFork())
+ return;
+
+ Common::SeekableReadStream *res = _resFork->getResource(MKTAG('P', 'F', 'r', 'm'), 0x80);
+
+ if (!res)
+ return;
+
+ uint32 scale = res->readUint32BE();
+ _bounds.top = res->readUint16BE();
+ _bounds.left = res->readUint16BE();
+ _bounds.bottom = res->readUint16BE();
+ _bounds.right = res->readUint16BE();
+ _numFrames = res->readUint16BE();
+ _duration = 0;
+
+ _frameTimes.clear();
+ for (uint32 i = 0; i < _numFrames; i++) {
+ TimeValue time = res->readUint32BE();
+ _duration += time;
+ _frameTimes.push_back(_duration);
+ }
+
+ setScale(scale);
+ setSegment(0, _duration);
+ setTime(0);
+ _currentFrameNum = 0;
+ newFrame(_currentFrameNum);
+ triggerRedraw();
+
+ delete res;
+}
+
+void FrameSequence::closeFrameSequence() {
+ stop();
+ _resFork->close();
+ _duration = 0;
+ _numFrames = 0;
+ _frameTimes.clear();
+}
+
+void FrameSequence::timeChanged(const TimeValue time) {
+ int16 frameNum = 0;
+ for (int16 i = _numFrames - 1; i >= 0; i--) {
+ if (_frameTimes[i] < time) {
+ frameNum = i;
+ break;
+ }
+ }
+
+ if (frameNum != _currentFrameNum) {
+ _currentFrameNum = frameNum;
+ newFrame(_currentFrameNum);
+ triggerRedraw();
+ }
+}
+
+void FrameSequence::setFrameNum(const int16 frameNum) {
+ int16 f = CLIP<int>(frameNum, 0, _numFrames);
+
+ if (_currentFrameNum != f) {
+ _currentFrameNum = f;
+ setTime(_frameTimes[f]);
+ newFrame(f);
+ triggerRedraw();
+ }
+}
+
+bool FrameSequence::isSequenceOpen() const {
+ return _numFrames != 0;
+}
+
} // End of namespace Pegasus
diff --git a/engines/pegasus/elements.h b/engines/pegasus/elements.h
index 2fc7a3abd3..460b9a71d9 100644
--- a/engines/pegasus/elements.h
+++ b/engines/pegasus/elements.h
@@ -33,8 +33,13 @@
#include "graphics/surface.h"
#include "pegasus/pegasus.h"
+#include "pegasus/timers.h"
#include "pegasus/util.h"
+namespace Common {
+ class MacResManager;
+}
+
namespace Pegasus {
class DisplayElement : public IDObject {
@@ -134,6 +139,60 @@ protected:
uint32 _barColor;
};
+class Animation : public DisplayElement, public DynamicElement {
+public:
+ Animation(const tDisplayElementID id) : DisplayElement(id) {}
+};
+
+class IdlerAnimation : public Animation, public Idler {
+public:
+ IdlerAnimation(const tDisplayElementID);
+
+ virtual void startDisplaying();
+ virtual void stopDisplaying();
+
+ TimeValue getLastTime() const { return _lastTime; }
+
+protected:
+ virtual void useIdleTime();
+ virtual void timeChanged(const TimeValue);
+
+ TimeValue _lastTime;
+};
+
+// This class reads PICT resources and plays them like a movie.
+// Assumes there is a resource of type 'PFrm' describing the time values for each
+// PICT frame, as well as the total time in the movie.
+// Assumes that PICT frames begin at PICT 128
+
+class FrameSequence : public IdlerAnimation {
+public:
+ FrameSequence(const tDisplayElementID);
+ virtual ~FrameSequence();
+
+ void useFileName(const Common::String &fileName);
+
+ virtual void openFrameSequence();
+ virtual void closeFrameSequence();
+ bool isSequenceOpen() const;
+
+ uint16 getNumFrames() const { return _numFrames; }
+ virtual uint16 getFrameNum() const { return _currentFrameNum; }
+ virtual void setFrameNum(const int16);
+
+protected:
+ virtual void timeChanged(const TimeValue);
+ virtual void newFrame(const uint16) {}
+
+ Common::MacResManager *_resFork;
+ TimeValue _duration;
+
+ uint16 _numFrames;
+ Common::Array<TimeValue> _frameTimes;
+
+ uint16 _currentFrameNum;
+};
+
} // End of namespace Pegasus
#endif
diff --git a/engines/pegasus/pegasus.cpp b/engines/pegasus/pegasus.cpp
index 39eb7d50c4..dacc2a0a30 100644
--- a/engines/pegasus/pegasus.cpp
+++ b/engines/pegasus/pegasus.cpp
@@ -332,4 +332,12 @@ void PegasusEngine::giveIdleTime() {
(*it)->useIdleTime();
}
+void PegasusEngine::addTimeBase(TimeBase *timeBase) {
+ _timeBases.push_back(timeBase);
+}
+
+void PegasusEngine::removeTimeBase(TimeBase *timeBase) {
+ _timeBases.remove(timeBase);
+}
+
} // End of namespace Pegasus
diff --git a/engines/pegasus/pegasus.h b/engines/pegasus/pegasus.h
index 8ab891df9c..5074aaba09 100644
--- a/engines/pegasus/pegasus.h
+++ b/engines/pegasus/pegasus.h
@@ -50,6 +50,7 @@ class VideoManager;
class GraphicsManager;
class Idler;
class Cursor;
+class TimeBase;
static const int kViewScreenOffset = 64;
@@ -85,6 +86,9 @@ public:
void addIdler(Idler *idler);
void removeIdler(Idler *idler);
+ void addTimeBase(TimeBase *timeBase);
+ void removeTimeBase(TimeBase *timeBase);
+
protected:
Common::Error run();
@@ -141,6 +145,9 @@ private:
// Items
void createItems();
void createItem(tItemID itemID, tNeighborhoodID neighborhoodID, tRoomID roomID, tDirectionConstant direction);
+
+ // TimeBases
+ Common::List<TimeBase *> _timeBases;
};
} // End of namespace Pegasus
diff --git a/engines/pegasus/timers.cpp b/engines/pegasus/timers.cpp
index 5908854d13..fe487e90d2 100755
--- a/engines/pegasus/timers.cpp
+++ b/engines/pegasus/timers.cpp
@@ -24,6 +24,7 @@
*/
#include "pegasus/pegasus.h"
+#include "pegasus/notification.h"
#include "pegasus/timers.h"
namespace Pegasus {
@@ -50,4 +51,285 @@ void Idler::stopIdling() {
}
}
+TimeBase::TimeBase(const TimeScale preferredScale) {
+ _preferredScale = preferredScale;
+ _callBackList = 0;
+ _paused = false;
+ _flags = 0;
+ ((PegasusEngine *)g_engine)->addTimeBase(this);
+}
+
+TimeBase::~TimeBase() {
+ if (_master)
+ _master->_slaves.remove(this);
+
+ ((PegasusEngine *)g_engine)->removeTimeBase(this);
+ disposeAllCallBacks();
+
+ // TODO: Remove slaves? Make them remove themselves?
+}
+
+void TimeBase::setTime(const TimeValue time, const TimeScale scale) {
+ // So, we're saying the *current* time from g_system->getMillis() is
+ // what time/scale is. Which means we need to subtract time/scale from
+ // the offset so that g_system->getMillis() - _currentOffset is
+ // equal to time/scale.
+ _timeOffset = g_system->getMillis() - time * 1000 / ((scale == 0) ? _preferredScale : scale);
+
+ // TODO: Also adjust the slaves' offsets (once we're actually using the
+ // masters for calculating time)
+}
+
+TimeValue TimeBase::getTime(const TimeScale scale) {
+ if (_paused)
+ return _pausedTime;
+
+ Common::Rational normalTime = Common::Rational((g_system->getMillis() - _timeOffset) * ((scale == 0) ? _preferredScale : scale), 1000);
+
+ // Adjust it according to our rate
+ return (normalTime * getEffectiveRate()).toInt();
+}
+
+void TimeBase::setRate(const Common::Rational rate) {
+ if (rate == 0) {
+ _paused = false;
+ _timeOffset = 0;
+ } else if (_timeOffset != 0) {
+ // Convert the time from the old rate to the new rate
+ _timeOffset = g_system->getMillis() - (rate * (g_system->getMillis() - _timeOffset) / _rate).toInt();
+ } else {
+ // TODO: Check this
+ setTime(_startTime, _startScale);
+ }
+
+ _rate = rate;
+}
+
+Common::Rational TimeBase::getEffectiveRate() const {
+ return _rate * ((_master == 0) ? 1 : _master->getEffectiveRate());
+}
+
+void TimeBase::start() {
+ if (_paused)
+ _pausedRate = 1;
+ else
+ setRate(1);
+}
+
+void TimeBase::stop() {
+ setRate(0);
+ _paused = false;
+}
+
+void TimeBase::pause() {
+ if (isRunning() && !_paused) {
+ _pausedRate = getRate();
+ stop();
+ _pausedTime = getTime();
+ _paused = true;
+ }
+}
+
+void TimeBase::resume() {
+ if (_paused) {
+ setRate(_pausedRate);
+ _paused = false;
+ }
+}
+
+bool TimeBase::isRunning() {
+ if (_paused && _pausedRate != 0)
+ return true;
+
+ Common::Rational rate = getRate();
+
+ if (rate == 0)
+ return false;
+
+ if (getFlags() & kLoopTimeBase)
+ return true;
+
+ if (rate > 0)
+ return getTime() < getStop();
+
+ return getTime() > getStart();
+}
+
+void TimeBase::setStart(const TimeValue startTime, const TimeScale scale) {
+ _startTime = startTime;
+ _startScale = (scale == 0) ? _preferredScale : scale;
+}
+
+TimeValue TimeBase::getStart(const TimeScale scale) const {
+ if (scale)
+ return _startTime * scale / _startScale;
+
+ return _startTime * _preferredScale / _startScale;
+}
+
+void TimeBase::setStop(const TimeValue stopTime, const TimeScale scale) {
+ _stopTime = stopTime;
+ _stopScale = (scale == 0) ? _preferredScale : scale;
+}
+
+TimeValue TimeBase::getStop(const TimeScale scale) const {
+ if (scale)
+ return _stopTime * scale / _stopScale;
+
+ return _stopTime * _preferredScale / _stopScale;
+}
+
+void TimeBase::setSegment(const TimeValue startTime, const TimeValue stopTime, const TimeScale scale) {
+ setStart(startTime, scale);
+ setStop(stopTime, scale);
+}
+
+void TimeBase::getSegment(TimeValue &startTime, TimeValue &stopTime, const TimeScale scale) const {
+ startTime = getStart(scale);
+ stopTime = getStop(scale);
+}
+
+TimeValue TimeBase::getDuration(const TimeScale scale) const {
+ TimeValue startTime, stopTime;
+ getSegment(startTime, stopTime, scale);
+ return stopTime - startTime;
+}
+
+void TimeBase::setMasterTimeBase(TimeBase *tb) {
+ // TODO: We're just ignoring the master (except for effective rate)
+ // for now to simplify things
+ if (_master)
+ _master->_slaves.remove(this);
+
+ _master = tb;
+
+ if (_master)
+ _master->_slaves.push_back(this);
+}
+
+void TimeBase::checkCallBacks() {
+ uint32 time = getTime();
+ uint32 startTime = getStart();
+ uint32 stopTime = getStop();
+
+ for (TimeBaseCallBack *runner = _callBackList; runner != 0; runner = runner->_nextCallBack) {
+ if (runner->_type == kCallBackAtTime && runner->_trigger == kTriggerTimeFwd) {
+ if (time >= (runner->_param2 * _preferredScale / runner->_param3) && getRate() > 0)
+ runner->callBack();
+ } else if (runner->_type == kCallBackAtExtremes) {
+ if (runner->_trigger == kTriggerAtStop) {
+ if (time >= stopTime)
+ runner->callBack();
+ } else if (runner->_trigger == kTriggerAtStart) {
+ if (time <= startTime)
+ runner->callBack();
+ }
+ }
+ }
+
+ if (getFlags() & kLoopTimeBase) {
+ // Loop
+ if (time >= startTime)
+ setTime(stopTime);
+ else if (time <= stopTime)
+ setTime(startTime);
+ }
+}
+
+// Protected functions only called by TimeBaseCallBack.
+
+void TimeBase::addCallBack(TimeBaseCallBack *callBack) {
+ callBack->_nextCallBack = _callBackList;
+ _callBackList = callBack;
+}
+
+void TimeBase::removeCallBack(TimeBaseCallBack *callBack) {
+ if (_callBackList == callBack) {
+ _callBackList = callBack->_nextCallBack;
+ } else {
+ TimeBaseCallBack *runner, *prevRunner;
+
+ for (runner = _callBackList->_nextCallBack, prevRunner = _callBackList; runner != callBack; prevRunner = runner, runner = runner->_nextCallBack)
+ ;
+
+ prevRunner->_nextCallBack = runner->_nextCallBack;
+ }
+
+ callBack->_nextCallBack = 0;
+}
+
+void TimeBase::disposeAllCallBacks() {
+ TimeBaseCallBack *nextRunner;
+
+ for (TimeBaseCallBack *runner = _callBackList; runner != 0; runner = nextRunner) {
+ nextRunner = runner->_nextCallBack;
+ runner->disposeCallBack();
+ runner->_nextCallBack = 0;
+ }
+
+ _callBackList = 0;
+}
+
+TimeBaseCallBack::TimeBaseCallBack() {
+ _timeBase = 0;
+ _nextCallBack = 0;
+ _trigger = kTriggerNone;
+ _type = kCallBackNone;
+}
+
+TimeBaseCallBack::~TimeBaseCallBack() {
+ releaseCallBack();
+}
+
+void TimeBaseCallBack::initCallBack(TimeBase *tb, CallBackType type) {
+ releaseCallBack();
+ _timeBase = tb;
+ _timeBase->addCallBack(this);
+ _type = type;
+}
+
+void TimeBaseCallBack::releaseCallBack() {
+ if (_timeBase)
+ _timeBase->removeCallBack(this);
+ disposeCallBack();
+}
+
+void TimeBaseCallBack::disposeCallBack() {
+ _timeBase = 0;
+}
+
+void TimeBaseCallBack::scheduleCallBack(CallBackTrigger trigger, uint32 param2, uint32 param3) {
+ // TODO: Rename param2/param3?
+ _trigger = trigger;
+ _param2 = param2;
+ _param3 = param3;
+}
+
+void TimeBaseCallBack::cancelCallBack() {
+ _trigger = kTriggerNone;
+}
+
+IdlerTimeBase::IdlerTimeBase() {
+ _lastTime = 0xffffffff;
+ startIdling();
+}
+
+void IdlerTimeBase::useIdleTime() {
+ uint32 currentTime = getTime();
+ if (currentTime != _lastTime) {
+ _lastTime = currentTime;
+ timeChanged(_lastTime);
+ }
+}
+
+NotificationCallBack::NotificationCallBack() {
+ _callBackFlag = 0;
+ _notifier = 0;
+}
+
+void NotificationCallBack::callBack() {
+ if (_notifier)
+ _notifier->setNotificationFlags(_callBackFlag, _callBackFlag);
+}
+
} // End of namespace Pegasus
diff --git a/engines/pegasus/timers.h b/engines/pegasus/timers.h
index e4bb41086f..9ce3093820 100755
--- a/engines/pegasus/timers.h
+++ b/engines/pegasus/timers.h
@@ -26,6 +26,10 @@
#ifndef PEGASUS_TIMERS_H
#define PEGASUS_TIMERS_H
+#include "common/rational.h"
+
+#include "pegasus/constants.h"
+
namespace Pegasus {
class Idler {
@@ -45,6 +49,168 @@ protected:
bool _isIdling;
};
+enum {
+ kLoopTimeBase = 1,
+ kPalindromeLoopTimeBase = 2,
+ kMaintainTimeBaseZero = 4
+};
+
+class TimeBaseCallBack;
+
+class TimeBase {
+friend class TimeBaseCallBack;
+public:
+ TimeBase(const TimeScale = kDefaultTimeScale);
+ virtual ~TimeBase();
+
+ virtual void setTime(const TimeValue, const TimeScale = 0);
+ virtual TimeValue getTime(const TimeScale = 0);
+
+ virtual void setScale(const TimeScale scale) { _preferredScale = scale; }
+ virtual TimeScale getScale() const { return _preferredScale; }
+
+ virtual void setRate(const Common::Rational);
+ virtual Common::Rational getRate() const { return _rate; }
+
+ virtual void start();
+ virtual void stop();
+ virtual bool isRunning();
+
+ virtual void pause();
+ virtual void resume();
+ bool isPaused() const { return _paused; }
+
+ virtual void setFlags(const uint32 flags) { _flags = flags; }
+ virtual uint32 getFlags() const { return _flags; }
+
+ virtual void setStart(const TimeValue, const TimeScale = 0);
+ virtual TimeValue getStart(const TimeScale = 0) const;
+
+ virtual void setStop(const TimeValue, const TimeScale = 0);
+ virtual TimeValue getStop(const TimeScale = 0) const;
+
+ virtual void setSegment(const TimeValue, const TimeValue, const TimeScale = 0);
+ virtual void getSegment(TimeValue&, TimeValue&, const TimeScale = 0) const;
+
+ virtual TimeValue getDuration(const TimeScale = 0) const;
+
+ virtual void setMasterTimeBase(TimeBase *timeBase);
+
+ void disposeAllCallBacks();
+
+ // ScummVM's API additions (to replace the need for actual timers)
+ void checkCallBacks();
+
+protected:
+ void addCallBack(TimeBaseCallBack *);
+ void removeCallBack(TimeBaseCallBack *);
+
+ TimeBase *_master;
+ TimeScale _preferredScale;
+ TimeBaseCallBack *_callBackList;
+ Common::Rational _rate, _pausedRate;
+ bool _paused;
+ uint32 _startTime, _startScale;
+ uint32 _stopTime, _stopScale;
+ int32 _timeOffset;
+ uint32 _flags;
+ TimeValue _pausedTime;
+
+private:
+ Common::Rational getEffectiveRate() const;
+
+ Common::List<TimeBase *> _slaves;
+};
+
+// Type passed to initCallBack()
+enum CallBackType {
+ kCallBackNone = 0,
+ kCallBackAtTime = 1,
+ kCallBackAtExtremes = 4
+};
+
+// Trigger passed to scheduleCallBack()
+enum CallBackTrigger {
+ kTriggerNone = 0,
+
+ // AtTime flags
+ kTriggerTimeFwd = 1,
+
+ // AtExtremes flags
+ kTriggerAtStart = 1,
+ kTriggerAtStop = 2
+};
+
+class TimeBaseCallBack {
+friend class TimeBase;
+
+public:
+ TimeBaseCallBack();
+ virtual ~TimeBaseCallBack();
+
+ void initCallBack(TimeBase *, CallBackType type);
+
+ void releaseCallBack();
+
+ void scheduleCallBack(CallBackTrigger trigger, uint32 param2, uint32 param3);
+ void cancelCallBack();
+
+protected:
+ virtual void callBack() = 0;
+
+ TimeBase *_timeBase;
+
+ // Owned and operated by TimeBase;
+ TimeBaseCallBack *_nextCallBack;
+
+ // Our storage of the QuickTime timer crap
+ CallBackType _type;
+ CallBackTrigger _trigger;
+ uint32 _param2, _param3;
+
+private:
+ void disposeCallBack();
+};
+
+class IdlerTimeBase : public Idler, public TimeBase {
+public:
+ IdlerTimeBase();
+ virtual ~IdlerTimeBase() { stopIdling(); }
+
+ TimeValue getLastTime() const { return _lastTime; }
+
+protected:
+ virtual void useIdleTime();
+ virtual void timeChanged(const TimeValue) {}
+
+ TimeValue _lastTime;
+
+};
+
+class Notification;
+
+class NotificationCallBack : public TimeBaseCallBack {
+public:
+ NotificationCallBack();
+ virtual ~NotificationCallBack() {}
+
+ void setNotification(Notification *notifier) { _notifier = notifier; }
+
+ void setCallBackFlag(const tNotificationFlags flag) { _callBackFlag = flag; }
+ tNotificationFlags getCallBackFlag() const { return _callBackFlag; }
+
+protected:
+ void callBack();
+
+ Notification *_notifier;
+ tNotificationFlags _callBackFlag;
+};
+
+class DynamicElement : public TimeBase {
+public:
+ TimeValue percentSeconds(const uint32 percent) { return getScale() * percent / 100; }
+};
+
} // End of namespace Pegasus
#endif