mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-23 19:16:21 +00:00
MTROPOLIS: mToon fixes (get Obsidian file cabinets working)
This commit is contained in:
parent
4d8f8bc43b
commit
ab07cd3e56
@ -327,6 +327,9 @@ void CachedMToon::loadRLEFrames(const Common::Array<uint8> &data) {
|
||||
_dataRLE8[i].data.resize(frameDataSize);
|
||||
memcpy(&_dataRLE8[i].data[0], &data[baseOffset + 20], frameDataSize);
|
||||
} else if (bpp == 16) {
|
||||
// RLE16 data size excludes the header
|
||||
frameDataSize -= 20;
|
||||
|
||||
uint32 numDWords = frameDataSize / 2;
|
||||
_dataRLE16[i].data.resize(numDWords);
|
||||
memcpy(&_dataRLE16[i].data[0], &data[baseOffset + 20], numDWords * 2);
|
||||
@ -448,7 +451,8 @@ void CachedMToon::rleReformat(const TSrcFrame &srcFrame, const Graphics::PixelFo
|
||||
destFrame.version = srcFrame.version;
|
||||
|
||||
while (offset < srcFrame.data.size()) {
|
||||
const uint32 rleCode = srcFrame.data[offset];
|
||||
uint32 rleCodeOffset = offset;
|
||||
const uint32 rleCode = srcFrame.data[rleCodeOffset];
|
||||
if (rleCode == 0) {
|
||||
destFrame.data[offset] = 0;
|
||||
offset++;
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "mtropolis/elements.h"
|
||||
#include "mtropolis/assets.h"
|
||||
#include "mtropolis/element_factory.h"
|
||||
#include "mtropolis/miniscript.h"
|
||||
#include "mtropolis/render.h"
|
||||
|
||||
#include "video/video_decoder.h"
|
||||
@ -450,7 +451,7 @@ void ImageElement::render(Window *window) {
|
||||
}
|
||||
}
|
||||
|
||||
MToonElement::MToonElement() : _cel1Based(1), _renderedFrame(0), _flushPriority(0) {
|
||||
MToonElement::MToonElement() : _frame(0), _renderedFrame(0), _flushPriority(0), _celStartTimeMSec(0), _isPlaying(false), _playRange(IntRange::create(1, 1)) {
|
||||
}
|
||||
|
||||
MToonElement::~MToonElement() {
|
||||
@ -475,11 +476,17 @@ bool MToonElement::load(ElementLoaderContext &context, const Data::MToonElement
|
||||
|
||||
bool MToonElement::readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) {
|
||||
if (attrib == "cel") {
|
||||
result.setInt(_cel1Based);
|
||||
result.setInt(_frame + 1);
|
||||
return true;
|
||||
} else if (attrib == "flushpriority") {
|
||||
result.setInt(_flushPriority);
|
||||
return true;
|
||||
} else if (attrib == "rate") {
|
||||
result.setFloat(_rateTimes10000 / 10000.0);
|
||||
return true;
|
||||
} else if (attrib == "range") {
|
||||
result.setIntRange(_playRange);
|
||||
return true;
|
||||
}
|
||||
|
||||
return VisualElement::readAttribute(thread, result, attrib);
|
||||
@ -488,7 +495,7 @@ bool MToonElement::readAttribute(MiniscriptThread *thread, DynamicValue &result,
|
||||
MiniscriptInstructionOutcome MToonElement::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
|
||||
if (attrib == "cel") {
|
||||
// TODO proper support
|
||||
DynamicValueWriteIntegerHelper<uint32>::create(&_cel1Based, result);
|
||||
DynamicValueWriteFuncHelper<MToonElement, &MToonElement::scriptSetCel>::create(this, result);
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
} else if (attrib == "flushpriority") {
|
||||
DynamicValueWriteIntegerHelper<int32>::create(&_flushPriority, result);
|
||||
@ -496,11 +503,37 @@ MiniscriptInstructionOutcome MToonElement::writeRefAttribute(MiniscriptThread *t
|
||||
} else if (attrib == "maintainrate") {
|
||||
DynamicValueWriteBoolHelper::create(&_maintainRate, result);
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
} else if (attrib == "rate") {
|
||||
DynamicValueWriteFuncHelper<MToonElement, &MToonElement::scriptSetRate>::create(this, result);
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
} else if (attrib == "range") {
|
||||
DynamicValueWriteFuncHelper<MToonElement, &MToonElement::scriptSetRange>::create(this, result);
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
|
||||
return VisualElement::writeRefAttribute(thread, result, attrib);
|
||||
}
|
||||
|
||||
VThreadState MToonElement::consumeCommand(Runtime *runtime, const Common::SharedPtr<MessageProperties>& msg) {
|
||||
if (Event::create(EventIDs::kPlay, 0).respondsTo(msg->getEvent())) {
|
||||
StartPlayingTaskData *startPlayingTaskData = runtime->getVThread().pushTask("MToonElement::startPlayingTask", this, &MToonElement::startPlayingTask);
|
||||
startPlayingTaskData->runtime = runtime;
|
||||
|
||||
ChangeFlagTaskData *becomeVisibleTaskData = runtime->getVThread().pushTask("MToonElement::changeVisibilityTask", static_cast<VisualElement *>(this), &MToonElement::changeVisibilityTask);
|
||||
becomeVisibleTaskData->desiredFlag = true;
|
||||
becomeVisibleTaskData->runtime = runtime;
|
||||
|
||||
return kVThreadReturn;
|
||||
}
|
||||
if (Event::create(EventIDs::kStop, 0).respondsTo(msg->getEvent())) {
|
||||
// Works differently from movies: Needs to hide the element and pause
|
||||
warning("mToon element stops are not implemented");
|
||||
return kVThreadReturn;
|
||||
}
|
||||
|
||||
return kVThreadReturn;
|
||||
}
|
||||
|
||||
void MToonElement::activate() {
|
||||
Project *project = _runtime->getProject();
|
||||
Common::SharedPtr<Asset> asset = project->getAssetByID(_assetID).lock();
|
||||
@ -519,6 +552,7 @@ void MToonElement::activate() {
|
||||
_metadata = _cachedMToon->getMetadata();
|
||||
|
||||
_playMediaSignaller = project->notifyOnPlayMedia(this);
|
||||
_playRange = IntRange::create(1, _metadata->frames.size());
|
||||
}
|
||||
|
||||
void MToonElement::deactivate() {
|
||||
@ -533,17 +567,9 @@ void MToonElement::deactivate() {
|
||||
void MToonElement::render(Window *window) {
|
||||
if (_cachedMToon) {
|
||||
_cachedMToon->optimize(_runtime);
|
||||
uint32 frame = 0;
|
||||
|
||||
if (_cel1Based < 1)
|
||||
frame = 0;
|
||||
else if (_cel1Based > _metadata->frames.size())
|
||||
frame = _metadata->frames.size() - 1;
|
||||
else
|
||||
frame = _cel1Based - 1;
|
||||
|
||||
_cachedMToon->getOrRenderFrame(_renderedFrame, frame, _renderSurface);
|
||||
_renderedFrame = frame;
|
||||
_cachedMToon->getOrRenderFrame(_renderedFrame, _frame, _renderSurface);
|
||||
_renderedFrame = _frame;
|
||||
}
|
||||
|
||||
if (_renderSurface) {
|
||||
@ -553,9 +579,202 @@ void MToonElement::render(Window *window) {
|
||||
}
|
||||
}
|
||||
|
||||
void MToonElement::playMedia(Runtime *runtime, Project *project) {
|
||||
VThreadState MToonElement::startPlayingTask(const StartPlayingTaskData &taskData) {
|
||||
_frame = _playRange.min;
|
||||
_paused = false;
|
||||
_isPlaying = false; // Reset play state, it starts for real in playMedia
|
||||
|
||||
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(EventIDs::kPlay, 0), DynamicValue(), getSelfReference()));
|
||||
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, true, false));
|
||||
taskData.runtime->sendMessageOnVThread(dispatch);
|
||||
|
||||
return kVThreadReturn;
|
||||
}
|
||||
|
||||
VThreadState MToonElement::changeFrameTask(const ChangeFrameTaskData &taskData) {
|
||||
if (taskData.frame == _frame)
|
||||
return kVThreadReturn;
|
||||
|
||||
uint32 minFrame = _playRange.min;
|
||||
uint32 maxFrame = _playRange.max;
|
||||
_frame = taskData.frame;
|
||||
|
||||
if (_frame == minFrame) {
|
||||
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(EventIDs::kAtFirstCel, 0), DynamicValue(), getSelfReference()));
|
||||
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, true, false));
|
||||
taskData.runtime->sendMessageOnVThread(dispatch);
|
||||
}
|
||||
|
||||
if (_frame == maxFrame) {
|
||||
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(EventIDs::kAtLastCel, 0), DynamicValue(), getSelfReference()));
|
||||
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, true, false));
|
||||
taskData.runtime->sendMessageOnVThread(dispatch);
|
||||
}
|
||||
|
||||
return kVThreadReturn;
|
||||
}
|
||||
|
||||
void MToonElement::playMedia(Runtime *runtime, Project *project) {
|
||||
uint32 targetFrame = _frame;
|
||||
|
||||
if (_paused)
|
||||
return;
|
||||
|
||||
uint32 minFrame = _playRange.min - 1;
|
||||
uint32 maxFrame = _playRange.max - 1;
|
||||
|
||||
uint64 playTime = runtime->getPlayTime();
|
||||
if (!_isPlaying) {
|
||||
_isPlaying = true;
|
||||
_celStartTimeMSec = runtime->getPlayTime();
|
||||
}
|
||||
|
||||
if (_rateTimes10000 < 0) {
|
||||
warning("Playing mToons backwards is not implemented yet");
|
||||
_rateTimes10000 = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Might be possible due to drift?
|
||||
if (playTime < _celStartTimeMSec)
|
||||
return;
|
||||
|
||||
uint64 timeSinceCelStart = playTime - _celStartTimeMSec;
|
||||
uint64 framesAdvanced = timeSinceCelStart * static_cast<uint64>(_rateTimes10000) / static_cast<uint64>(10000000);
|
||||
|
||||
if (framesAdvanced > 0) {
|
||||
// This needs to be handled correctly: Reaching the last frame triggers At Last Cel,
|
||||
// but going PAST the last frame triggers automatic stop and pause.
|
||||
// The Obsidian bureau filing cabinets depend on this, since they reset the cel when
|
||||
// reaching the last cel but do not unpause.
|
||||
bool ranPastEnd = false;
|
||||
|
||||
size_t framesRemainingToOnePastEnd = (maxFrame + 1) -_frame;
|
||||
if (framesRemainingToOnePastEnd <= framesAdvanced) {
|
||||
ranPastEnd = true;
|
||||
if (_loop)
|
||||
targetFrame = minFrame;
|
||||
else
|
||||
targetFrame = maxFrame;
|
||||
} else
|
||||
targetFrame = _frame + framesAdvanced;
|
||||
|
||||
if (_frame != targetFrame) {
|
||||
_frame = targetFrame;
|
||||
|
||||
if (_frame == maxFrame) {
|
||||
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(EventIDs::kAtLastCel, 0), DynamicValue(), getSelfReference()));
|
||||
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, true, false));
|
||||
runtime->queueMessage(dispatch);
|
||||
}
|
||||
|
||||
if (_frame == minFrame) {
|
||||
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(EventIDs::kAtFirstCel, 0), DynamicValue(), getSelfReference()));
|
||||
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, true, false));
|
||||
runtime->queueMessage(dispatch);
|
||||
}
|
||||
}
|
||||
|
||||
if (ranPastEnd && !_loop) {
|
||||
_paused = true;
|
||||
|
||||
// Unlike movies, reaching the end of an mToon fires "Paused" not "Stopped"
|
||||
Common::SharedPtr<MessageProperties> msgProps(new MessageProperties(Event::create(EventIDs::kPause, 0), DynamicValue(), getSelfReference()));
|
||||
Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(msgProps, this, false, true, false));
|
||||
runtime->queueMessage(dispatch);
|
||||
}
|
||||
|
||||
if (_maintainRate)
|
||||
_celStartTimeMSec = playTime;
|
||||
else
|
||||
_celStartTimeMSec += (static_cast<uint64>(10000000) * framesAdvanced) / _rateTimes10000;
|
||||
}
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome MToonElement::scriptSetCel(MiniscriptThread *thread, const DynamicValue &value) {
|
||||
int32 asInteger = 0;
|
||||
if (!value.roundToInt(asInteger)) {
|
||||
thread->error("Attempted to set mToon cel to an invalid value");
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
|
||||
if (asInteger < _playRange.min)
|
||||
asInteger = _playRange.min;
|
||||
else if (asInteger > _playRange.max)
|
||||
asInteger = _playRange.max;
|
||||
|
||||
uint32 frame = asInteger - 1;
|
||||
_celStartTimeMSec = thread->getRuntime()->getPlayTime();
|
||||
|
||||
if (frame != _frame) {
|
||||
ChangeFrameTaskData *taskData = thread->getRuntime()->getVThread().pushTask("MToonElement::changeFrameTask", this, &MToonElement::changeFrameTask);
|
||||
taskData->runtime = _runtime;
|
||||
taskData->frame = frame;
|
||||
|
||||
return kMiniscriptInstructionOutcomeYieldToVThreadNoRetry;
|
||||
}
|
||||
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome MToonElement::scriptSetRange(MiniscriptThread *thread, const DynamicValue &value) {
|
||||
if (value.getType() != DynamicValueTypes::kIntegerRange) {
|
||||
thread->error("Invalid type for mToon range");
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
|
||||
IntRange intRange = value.getIntRange();
|
||||
size_t numFrames = _metadata->frames.size();
|
||||
if (intRange.min < 1)
|
||||
intRange.min = 1;
|
||||
if (intRange.max > numFrames)
|
||||
intRange.max = numFrames;
|
||||
|
||||
if (intRange.max < intRange.min)
|
||||
intRange.min = intRange.max;
|
||||
|
||||
_playRange = intRange;
|
||||
|
||||
uint32 targetFrame = _frame;
|
||||
uint32 minFrame = intRange.min - 1;
|
||||
uint32 maxFrame = intRange.max - 1;
|
||||
if (targetFrame < minFrame)
|
||||
targetFrame = minFrame;
|
||||
else if (targetFrame > maxFrame)
|
||||
targetFrame = maxFrame;
|
||||
|
||||
if (targetFrame != _frame) {
|
||||
ChangeFrameTaskData *taskData = thread->getRuntime()->getVThread().pushTask("MToonElement::changeFrameTask", this, &MToonElement::changeFrameTask);
|
||||
taskData->frame = targetFrame;
|
||||
taskData->runtime = _runtime;
|
||||
return kMiniscriptInstructionOutcomeYieldToVThreadNoRetry;
|
||||
}
|
||||
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
|
||||
void MToonElement::onPauseStateChanged() {
|
||||
_celStartTimeMSec = _runtime->getPlayTime();
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome MToonElement::scriptSetRate(MiniscriptThread *thread, const DynamicValue &value) {
|
||||
switch (value.getType()) {
|
||||
case DynamicValueTypes::kFloat:
|
||||
_rateTimes10000 = static_cast<int32>(round(value.getFloat()) * 10000.0);
|
||||
break;
|
||||
case DynamicValueTypes::kInteger:
|
||||
_rateTimes10000 = value.getInt() * 10000;
|
||||
break;
|
||||
default:
|
||||
thread->error("Invalid type for Miniscript rate");
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
_celStartTimeMSec = thread->getRuntime()->getPlayTime();
|
||||
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
|
||||
|
||||
TextLabelElement::TextLabelElement() : _needsRender(false), _isBitmap(false) {
|
||||
}
|
||||
|
||||
|
@ -158,6 +158,8 @@ public:
|
||||
bool readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) override;
|
||||
MiniscriptInstructionOutcome writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) override;
|
||||
|
||||
VThreadState consumeCommand(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) override;
|
||||
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
|
||||
@ -169,7 +171,24 @@ public:
|
||||
#endif
|
||||
|
||||
private:
|
||||
struct StartPlayingTaskData {
|
||||
Runtime *runtime;
|
||||
};
|
||||
|
||||
struct ChangeFrameTaskData {
|
||||
Runtime *runtime;
|
||||
uint32 frame;
|
||||
};
|
||||
|
||||
VThreadState startPlayingTask(const StartPlayingTaskData &taskData);
|
||||
VThreadState changeFrameTask(const ChangeFrameTaskData &taskData);
|
||||
|
||||
void playMedia(Runtime *runtime, Project *project) override;
|
||||
MiniscriptInstructionOutcome scriptSetRate(MiniscriptThread *thread, const DynamicValue &value);
|
||||
MiniscriptInstructionOutcome scriptSetCel(MiniscriptThread *thread, const DynamicValue &value);
|
||||
MiniscriptInstructionOutcome scriptSetRange(MiniscriptThread *thread, const DynamicValue &value);
|
||||
|
||||
void onPauseStateChanged();
|
||||
|
||||
bool _cacheBitmap;
|
||||
|
||||
@ -177,9 +196,11 @@ private:
|
||||
bool _maintainRate;
|
||||
|
||||
uint32 _assetID;
|
||||
uint32 _rateTimes10000;
|
||||
uint32 _cel1Based;
|
||||
int32 _rateTimes10000;
|
||||
uint32 _frame;
|
||||
int32 _flushPriority;
|
||||
uint32 _celStartTimeMSec;
|
||||
bool _isPlaying; // Is actually rolling media, this is only set by playMedia because it needs to start after scene transition
|
||||
|
||||
Runtime *_runtime;
|
||||
Common::SharedPtr<Graphics::Surface> _renderSurface;
|
||||
@ -188,6 +209,8 @@ private:
|
||||
Common::SharedPtr<MToonMetadata> _metadata;
|
||||
Common::SharedPtr<CachedMToon> _cachedMToon;
|
||||
Common::SharedPtr<PlayMediaSignaller> _playMediaSignaller;
|
||||
|
||||
IntRange _playRange;
|
||||
};
|
||||
|
||||
class TextLabelElement : public VisualElement {
|
||||
|
@ -1280,6 +1280,64 @@ MiniscriptInstructionOutcome PointCreate::execute(MiniscriptThread *thread) cons
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome RangeCreate::execute(MiniscriptThread *thread) const {
|
||||
if (thread->getStackSize() < 2) {
|
||||
thread->error("Stack underflow");
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0, false);
|
||||
if (outcome != kMiniscriptInstructionOutcomeContinue)
|
||||
return outcome;
|
||||
|
||||
outcome = thread->dereferenceRValue(1, false);
|
||||
if (outcome != kMiniscriptInstructionOutcomeContinue)
|
||||
return outcome;
|
||||
|
||||
DynamicValue &yVal = thread->getStackValueFromTop(0).value;
|
||||
DynamicValue &xValDest = thread->getStackValueFromTop(1).value;
|
||||
|
||||
int32 coords[2];
|
||||
DynamicValue *coordInputs[2] = {&xValDest, &yVal};
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
DynamicValue *v = coordInputs[i];
|
||||
DynamicValue listContents;
|
||||
|
||||
if (v->getType() == DynamicValueTypes::kList) {
|
||||
// Yes this is actually allowed
|
||||
const Common::SharedPtr<DynamicList> &list = v->getList();
|
||||
if (list->getSize() != 1 || !list->getAtIndex(0, listContents)) {
|
||||
thread->error("Can't convert list to integer");
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
|
||||
v = &listContents;
|
||||
}
|
||||
|
||||
switch (v->getType()) {
|
||||
case DynamicValueTypes::kFloat:
|
||||
coords[i] = static_cast<int32>(floor(v->getFloat() + 0.5)) & 0xffff;
|
||||
break;
|
||||
case DynamicValueTypes::kInteger:
|
||||
coords[i] = v->getInt();
|
||||
break;
|
||||
case DynamicValueTypes::kBoolean:
|
||||
coords[i] = (v->getBool()) ? 1 : 0;
|
||||
break;
|
||||
default:
|
||||
thread->error("Invalid input for point creation");
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
}
|
||||
|
||||
xValDest.setIntRange(IntRange::create(coords[0], coords[1]));
|
||||
|
||||
thread->popValues(1);
|
||||
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
|
||||
GetChild::GetChild(uint32 attribute, bool isLValue, bool isIndexed)
|
||||
: _attribute(attribute), _isLValue(isLValue), _isIndexed(isIndexed) {
|
||||
}
|
||||
@ -1873,10 +1931,6 @@ VThreadState MiniscriptThread::resume(const ResumeTaskData &taskData) {
|
||||
if (instrsArray.size() == 0)
|
||||
return kVThreadReturn;
|
||||
|
||||
if (_modifier->getStaticGUID() == 0x31ae0e) {
|
||||
int n = 0;
|
||||
}
|
||||
|
||||
MiniscriptInstruction *const *instrs = &instrsArray[0];
|
||||
size_t numInstrs = instrsArray.size();
|
||||
|
||||
|
@ -265,7 +265,9 @@ namespace MiniscriptInstructions {
|
||||
MiniscriptInstructionOutcome execute(MiniscriptThread *thread) const override;
|
||||
};
|
||||
|
||||
class RangeCreate : public UnimplementedInstruction {
|
||||
class RangeCreate : public MiniscriptInstruction {
|
||||
private:
|
||||
MiniscriptInstructionOutcome execute(MiniscriptThread *thread) const override;
|
||||
};
|
||||
|
||||
class VectorCreate : public UnimplementedInstruction {
|
||||
|
@ -746,9 +746,6 @@ Common::SharedPtr<Modifier> TimerMessengerModifier::shallowClone() const {
|
||||
}
|
||||
|
||||
void TimerMessengerModifier::trigger(Runtime *runtime) {
|
||||
if (getStaticGUID() == 0xd9550) {
|
||||
int n = 0;
|
||||
}
|
||||
debug(3, "Timer %x '%s' triggered", getStaticGUID(), getName().c_str());
|
||||
if (_looping) {
|
||||
uint32 realMilliseconds = _milliseconds;
|
||||
|
@ -2411,45 +2411,17 @@ bool Structural::readAttribute(MiniscriptThread *thread, DynamicValue &result, c
|
||||
result.clear();
|
||||
return true;
|
||||
} else if (attrib == "previous") {
|
||||
Structural *parent = getParent();
|
||||
if (parent) {
|
||||
const Common::Array<Common::SharedPtr<Structural> > &neighborhood = parent->getChildren();
|
||||
bool found = false;
|
||||
size_t foundIndex = 0;
|
||||
for (size_t i = 0; i < neighborhood.size(); i++) {
|
||||
if (neighborhood[i].get() == this) {
|
||||
foundIndex = i;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found && foundIndex > 0)
|
||||
result.setObject(neighborhood[foundIndex - 1]->getSelfReference());
|
||||
else
|
||||
result.clear();
|
||||
} else
|
||||
Structural *sibling = findPrevSibling();
|
||||
if (sibling)
|
||||
result.setObject(sibling->getSelfReference());
|
||||
else
|
||||
result.clear();
|
||||
return true;
|
||||
} else if (attrib == "next") {
|
||||
Structural *parent = getParent();
|
||||
if (parent) {
|
||||
const Common::Array<Common::SharedPtr<Structural> > &neighborhood = parent->getChildren();
|
||||
bool found = false;
|
||||
size_t foundIndex = 0;
|
||||
for (size_t i = 0; i < neighborhood.size(); i++) {
|
||||
if (neighborhood[i].get() == this) {
|
||||
foundIndex = i;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found && foundIndex < neighborhood.size() - 1)
|
||||
result.setObject(neighborhood[foundIndex + 1]->getSelfReference());
|
||||
else
|
||||
result.clear();
|
||||
} else
|
||||
Structural *sibling = findNextSibling();
|
||||
if (sibling)
|
||||
result.setObject(sibling->getSelfReference());
|
||||
else
|
||||
result.clear();
|
||||
return true;
|
||||
} else if (attrib == "scene") {
|
||||
@ -2511,6 +2483,22 @@ MiniscriptInstructionOutcome Structural::writeRefAttribute(MiniscriptThread *thr
|
||||
} else {
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
} else if (attrib == "next") {
|
||||
Structural *sibling = findNextSibling();
|
||||
if (sibling) {
|
||||
DynamicValueWriteObjectHelper::create(sibling, result);
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
} else {
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
} else if (attrib == "previous") {
|
||||
Structural *sibling = findPrevSibling();
|
||||
if (sibling) {
|
||||
DynamicValueWriteObjectHelper::create(sibling, result);
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
} else {
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
} else if (attrib == "loop") {
|
||||
DynamicValueWriteFuncHelper<Structural, &Structural::scriptSetLoop>::create(this, result);
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
@ -2557,6 +2545,48 @@ Structural *Structural::getParent() const {
|
||||
return _parent;
|
||||
}
|
||||
|
||||
Structural *Structural::findNextSibling() const {
|
||||
Structural *parent = getParent();
|
||||
if (parent) {
|
||||
const Common::Array<Common::SharedPtr<Structural> > &neighborhood = parent->getChildren();
|
||||
bool found = false;
|
||||
size_t foundIndex = 0;
|
||||
for (size_t i = 0; i < neighborhood.size(); i++) {
|
||||
if (neighborhood[i].get() == this) {
|
||||
foundIndex = i;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found && foundIndex < neighborhood.size() - 1)
|
||||
return neighborhood[foundIndex + 1].get();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Structural *Structural::findPrevSibling() const {
|
||||
Structural *parent = getParent();
|
||||
if (parent) {
|
||||
const Common::Array<Common::SharedPtr<Structural> > &neighborhood = parent->getChildren();
|
||||
bool found = false;
|
||||
size_t foundIndex = 0;
|
||||
for (size_t i = 0; i < neighborhood.size(); i++) {
|
||||
if (neighborhood[i].get() == this) {
|
||||
foundIndex = i;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found && foundIndex > 0)
|
||||
return neighborhood[foundIndex - 1].get();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Structural::setParent(Structural *parent) {
|
||||
_parent = parent;
|
||||
}
|
||||
|
@ -340,6 +340,13 @@ struct IntRange {
|
||||
return !((*this) == other);
|
||||
}
|
||||
|
||||
inline static IntRange create(int32 min, int32 max) {
|
||||
IntRange result;
|
||||
result.min = min;
|
||||
result.max = max;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool refAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, const Common::String &attrib);
|
||||
Common::String toString() const;
|
||||
};
|
||||
@ -1831,6 +1838,8 @@ public:
|
||||
void holdAssets(const Common::Array<Common::SharedPtr<Asset> > &assets);
|
||||
|
||||
Structural *getParent() const;
|
||||
Structural *findNextSibling() const;
|
||||
Structural *findPrevSibling() const;
|
||||
void setParent(Structural *parent);
|
||||
|
||||
// Helper that finds the scene containing the structural object, or itself if it is the scene
|
||||
|
Loading…
x
Reference in New Issue
Block a user