MTROPOLIS: Fix enough things for Obsidian forest intro to be completable (sometimes)

This commit is contained in:
elasota 2022-05-13 01:16:53 -04:00 committed by Eugene Sandulenko
parent 9c16acd00e
commit 5ab54de82a
12 changed files with 279 additions and 16 deletions

View File

@ -248,11 +248,11 @@ void CachedMToon::decompressRLEFrameToImage(size_t frameIndex, Graphics::Surface
int32 originY = frameDef.rect.top;
bool decompressedOK = false;
if (_rleOptimizedFormat.bytesPerPixel == 32) {
if (_rleOptimizedFormat.bytesPerPixel == 4) {
decompressedOK = decompressMToonRLE<Rle32Frame, uint32, 0x80000000u, 0x80000000u>(_dataRLE32[frameIndex], surface);
} else if (_rleOptimizedFormat.bytesPerPixel == 16) {
} else if (_rleOptimizedFormat.bytesPerPixel == 2) {
decompressedOK = decompressMToonRLE<Rle16Frame, uint16, 0x8000u, 0x8000u>(_dataRLE16[frameIndex], surface);
} else if (_rleOptimizedFormat.bytesPerPixel == 8) {
} else if (_rleOptimizedFormat.bytesPerPixel == 1) {
decompressedOK = decompressMToonRLE<Rle8Frame, uint8, 0x80u, 0u>(_dataRLE8[frameIndex], surface);
} else
error("Unknown mToon encoding");

View File

@ -0,0 +1,32 @@
/* 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 "mtropolis/hacks.h"
#include "common/system.h"
namespace MTropolis {
Hacks::Hacks() {
memset(this, 0, sizeof(*this));
}
} // End of namespace MTropolis

39
engines/mtropolis/hacks.h Normal file
View File

@ -0,0 +1,39 @@
/* 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/>.
*
*/
namespace MTropolis {
struct Hacks {
Hacks();
// Workaround for bug in Obsidian:
// When opening the journal in the intro, a script checks if cGSt.cfst.binjournal is false and if so,
// sets cGSt.cfst.binjournal to true and then sets including setting cJournalConst.aksjournpath to the
// main journal scene path. That scene path is used to resolve the scene to go to after clicking
// the "Continue" button on the warning that pops up.
//
// The problem is that cJournalConst uses a project name that doesn't match the retail data, and
// cJournalConst is unloaded if the player leaves the journal. This causes a progression blocker if
// the player leaves the journal without clicking Continue.
bool ignoreMismatchedProjectNameInObjectLookups;
};
} // End of namespace MTropolis

View File

@ -1826,7 +1826,7 @@ VThreadState MiniscriptThread::resume(const ResumeTaskData &taskData) {
if (instrsArray.size() == 0)
return kVThreadReturn;
if (_modifier->getStaticGUID() == 0x985d1) {
if (_modifier->getStaticGUID() == 0x48890) {
int n = 0;
}

View File

@ -716,9 +716,14 @@ VThreadState TimerMessengerModifier::consumeMessage(Runtime *runtime, const Comm
if (_scheduledEvent)
_scheduledEvent->cancel();
} else if (_executeWhen.respondsTo(msg->getEvent())) {
debug(3, "Timer %x '%s' scheduled to execute in %i milliseconds", getStaticGUID(), getName().c_str(), _milliseconds);
// 0-time events are not allowed
uint32 realMilliseconds = _milliseconds;
if (realMilliseconds == 0)
realMilliseconds = 1;
debug(3, "Timer %x '%s' scheduled to execute in %i milliseconds", getStaticGUID(), getName().c_str(), realMilliseconds);
if (!_scheduledEvent) {
_scheduledEvent = runtime->getScheduler().scheduleMethod<TimerMessengerModifier, &TimerMessengerModifier::activate>(runtime->getPlayTime() + _milliseconds, this);
_scheduledEvent = runtime->getScheduler().scheduleMethod<TimerMessengerModifier, &TimerMessengerModifier::trigger>(runtime->getPlayTime() + realMilliseconds, this);
}
}
@ -740,11 +745,14 @@ Common::SharedPtr<Modifier> TimerMessengerModifier::shallowClone() const {
return Common::SharedPtr<Modifier>(clone);
}
void TimerMessengerModifier::activate(Runtime *runtime) {
void TimerMessengerModifier::trigger(Runtime *runtime) {
debug(3, "Timer %x '%s' triggered", getStaticGUID(), getName().c_str());
if (_looping)
_scheduledEvent = runtime->getScheduler().scheduleMethod<TimerMessengerModifier, &TimerMessengerModifier::activate>(runtime->getPlayTime() + _milliseconds, this);
else
if (_looping) {
uint32 realMilliseconds = _milliseconds;
if (realMilliseconds == 0)
realMilliseconds = 1;
_scheduledEvent = runtime->getScheduler().scheduleMethod<TimerMessengerModifier, &TimerMessengerModifier::trigger>(runtime->getPlayTime() + realMilliseconds, this);
} else
_scheduledEvent.reset();
_sendSpec.sendFromMessenger(runtime, this);

View File

@ -384,7 +384,7 @@ public:
private:
Common::SharedPtr<Modifier> shallowClone() const override;
void activate(Runtime *runtime);
void trigger(Runtime *runtime);
Event _executeWhen;
Event _terminateWhen;

View File

@ -9,6 +9,7 @@ MODULE_OBJS = \
detection.o \
element_factory.o \
elements.o \
hacks.o \
metaengine.o \
miniscript.o \
modifiers.o \

View File

@ -277,6 +277,8 @@ Common::Error MTropolisEngine::run() {
preferredHeight = 480;
preferredColorDepthMode = kColorDepthMode16Bit;
_runtime->getHacks().ignoreMismatchedProjectNameInObjectLookups = true;
_runtime->addVolume(0, "Installed", true);
_runtime->addVolume(1, "OBSIDIAN1", true);
_runtime->addVolume(2, "OBSIDIAN2", true);
@ -314,11 +316,14 @@ Common::Error MTropolisEngine::run() {
desc->addPlugIn(PlugIns::createObsidian());
_runtime->queueProject(desc);
} else if (_gameDescription->gameID == GID_OBSIDIAN && _gameDescription->desc.platform == Common::kPlatformMacintosh) {
preferredWidth = 640;
preferredHeight = 480;
preferredColorDepthMode = kColorDepthMode16Bit;
_runtime->getHacks().ignoreMismatchedProjectNameInObjectLookups = true;
MacObsidianResources *resources = new MacObsidianResources();
Common::SharedPtr<ProjectResources> resPtr(resources);

View File

@ -884,7 +884,7 @@ MiniscriptInstructionOutcome MidiModifier::scriptSetNoteVelocity(MiniscriptThrea
return kMiniscriptInstructionOutcomeContinue;
}
ListVariableModifier::ListVariableModifier() : _list(new DynamicList()) {
ListVariableModifier::ListVariableModifier() : _list(new DynamicList()), _preferredContentType(DynamicValueTypes::kInteger) {
}
bool ListVariableModifier::load(const PlugInModifierLoaderContext &context, const Data::Standard::ListVariableModifier &data) {
@ -944,6 +944,8 @@ bool ListVariableModifier::load(const PlugInModifierLoaderContext &context, cons
}
}
_preferredContentType = expectedType;
return true;
}
@ -981,6 +983,15 @@ bool ListVariableModifier::readAttributeIndexed(MiniscriptThread *thread, Dynami
return Modifier::readAttributeIndexed(thread, result, attrib, index);
}
MiniscriptInstructionOutcome ListVariableModifier::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) {
if (attrib == "count") {
DynamicValueWriteFuncHelper<ListVariableModifier, &ListVariableModifier::scriptSetCount>::create(this, writeProxy);
return kMiniscriptInstructionOutcomeContinue;
}
return VariableModifier::writeRefAttribute(thread, writeProxy, attrib);
}
MiniscriptInstructionOutcome ListVariableModifier::writeRefAttributeIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib, const DynamicValue &index) {
if (attrib == "value") {
size_t realIndex = 0;
@ -1055,6 +1066,33 @@ ListVariableModifier::ListVariableModifier(const ListVariableModifier &other) {
_list = other._list->clone();
}
MiniscriptInstructionOutcome ListVariableModifier::scriptSetCount(MiniscriptThread *thread, const DynamicValue &value) {
int32 asInteger = 0;
if (!value.roundToInt(asInteger)) {
thread->error("Tried to set a list variable count to something other than an integer");
return kMiniscriptInstructionOutcomeFailed;
}
if (asInteger < 0) {
thread->error("Tried to set a list variable count to a negative value");
return kMiniscriptInstructionOutcomeFailed;
}
size_t newSize = asInteger;
if (newSize > _list->getSize()) {
if (_list->getSize() == 0) {
thread->error("Restoring an empty list by setting its count isn't implemented");
return kMiniscriptInstructionOutcomeFailed;
}
_list->expandToMinimumSize(newSize);
} else if (newSize < _list->getSize()) {
_list->truncateToSize(newSize);
}
return kMiniscriptInstructionOutcomeContinue;
}
Common::SharedPtr<Modifier> ListVariableModifier::shallowClone() const {
return Common::SharedPtr<Modifier>(new ListVariableModifier(*this));
}

View File

@ -246,9 +246,9 @@ public:
bool readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) override;
bool readAttributeIndexed(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib, const DynamicValue &index) override;
MiniscriptInstructionOutcome writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) override;
MiniscriptInstructionOutcome writeRefAttributeIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib, const DynamicValue &index) override;
#ifdef MTROPOLIS_DEBUG_ENABLE
const char *debugGetTypeName() const override { return "List Variable Modifier"; }
SupportStatus debugGetSupportStatus() const override { return kSupportStatusDone; }
@ -272,9 +272,12 @@ private:
ListVariableModifier(const ListVariableModifier &other);
ListVariableModifier &operator=(const ListVariableModifier &other);
MiniscriptInstructionOutcome scriptSetCount(MiniscriptThread *thread, const DynamicValue &value);
Common::SharedPtr<Modifier> shallowClone() const override;
Common::SharedPtr<DynamicList> _list;
DynamicValueTypes::DynamicValueType _preferredContentType;
};
class SysInfoModifier : public Modifier {

View File

@ -527,6 +527,9 @@ bool DynamicListContainer<void>::setAtIndex(size_t index, const DynamicValue &dy
return true;
}
void DynamicListContainer<void>::truncateToSize(size_t sz) {
}
bool DynamicListContainer<void>::expandToMinimumSize(size_t sz) {
return false;
}
@ -589,6 +592,11 @@ bool DynamicListContainer<VarReference>::setAtIndex(size_t index, const DynamicV
return true;
}
void DynamicListContainer<VarReference>::truncateToSize(size_t sz) {
if (_array.size() > sz)
_array.resize(sz);
}
bool DynamicListContainer<VarReference>::expandToMinimumSize(size_t sz) {
if (_array.size() < sz) {
size_t prevSize = _array.size();
@ -800,6 +808,13 @@ bool DynamicList::setAtIndex(size_t index, const DynamicValue &value) {
}
}
void DynamicList::truncateToSize(size_t sz) {
if (sz == 0)
clear();
else if (_container)
_container->truncateToSize(sz);
}
void DynamicList::expandToMinimumSize(size_t sz) {
if (_container)
_container->expandToMinimumSize(sz);
@ -1669,12 +1684,40 @@ void MessengerSendSpec::resolveDestination(Runtime *runtime, Modifier *sender, C
if (!outStructuralDest.expired())
outStructuralDest = outStructuralDest.lock()->getParent()->getSelfReference().staticCast<Structural>();
break;
case kMessageDestNextElement:
case kMessageDestPrevElement: {
Common::WeakPtr<Structural> elementWeak;
Common::WeakPtr<Modifier> modifier;
resolveHierarchyStructuralDestination(runtime, sender, elementWeak, modifier, isElementFilter);
Common::SharedPtr<Structural> sibling;
Common::SharedPtr<Structural> element = elementWeak.lock();
if (element) {
Structural *parent = element->getParent();
if (parent) {
const Common::Array<Common::SharedPtr<Structural> > &siblings = parent->getChildren();
for (size_t i = 0; i < siblings.size(); i++) {
if (siblings[i] == element) {
if (destination == kMessageDestPrevElement) {
if (i != 0)
sibling = siblings[i - 1];
} else if (destination == kMessageDestNextElement) {
if (i != siblings.size() - 1)
sibling = siblings[i + 1];
}
break;
}
}
}
}
if (sibling)
outStructuralDest = sibling;
} break;
case kMessageDestChildren:
case kMessageDestSubsection:
case kMessageDestSourcesParent:
case kMessageDestBehavior:
case kMessageDestNextElement:
case kMessageDestPrevElement:
case kMessageDestBehaviorsParent:
warning("Not-yet-implemented message destination type");
break;
@ -4558,6 +4601,15 @@ Audio::Mixer *Runtime::getAudioMixer() const {
return _mixer;
}
Hacks &Runtime::getHacks() {
return _hacks;
}
const Hacks &Runtime::getHacks() const {
return _hacks;
}
void Runtime::ensureMainWindowExists() {
// Maybe there's a better spot for this
if (_mainWindow.expired() && _project) {
@ -5809,7 +5861,7 @@ MiniscriptInstructionOutcome VisualElement::writeRefAttribute(MiniscriptThread *
DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetDirect>::create(this, writeProxy);
return kMiniscriptInstructionOutcomeContinue;
} else if (attrib == "position") {
DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetPosition>::create(this, writeProxy);
DynamicValueWriteOrRefAttribFuncHelper<VisualElement, &VisualElement::scriptSetPosition, &VisualElement::scriptWriteRefPositionAttribute>::create(this, writeProxy);
return kMiniscriptInstructionOutcomeContinue;
} else if (attrib == "centerposition") {
DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetCenterPosition>::create(this, writeProxy);
@ -5908,6 +5960,32 @@ MiniscriptInstructionOutcome VisualElement::scriptSetPosition(MiniscriptThread *
return kMiniscriptInstructionOutcomeFailed;
}
MiniscriptInstructionOutcome VisualElement::scriptSetPositionX(MiniscriptThread *thread, const DynamicValue &dest) {
int32 asInteger = 0;
if (!dest.roundToInt(asInteger))
return kMiniscriptInstructionOutcomeFailed;
int32 xDelta = asInteger - _rect.left;
if (xDelta != 0)
offsetTranslate(xDelta, 0, false);
return kMiniscriptInstructionOutcomeContinue;
}
MiniscriptInstructionOutcome VisualElement::scriptSetPositionY(MiniscriptThread *thread, const DynamicValue &dest) {
int32 asInteger = 0;
if (!dest.roundToInt(asInteger))
return kMiniscriptInstructionOutcomeFailed;
int32 yDelta = asInteger - _rect.top;
if (yDelta != 0)
offsetTranslate(0, yDelta, false);
return kMiniscriptInstructionOutcomeContinue;
}
MiniscriptInstructionOutcome VisualElement::scriptSetCenterPosition(MiniscriptThread *thread, const DynamicValue &value) {
if (value.getType() == DynamicValueTypes::kPoint) {
const Point16 &destPoint = value.getPoint();
@ -5963,6 +6041,18 @@ MiniscriptInstructionOutcome VisualElement::scriptSetLayer(MiniscriptThread *thr
return kMiniscriptInstructionOutcomeContinue;
}
MiniscriptInstructionOutcome VisualElement::scriptWriteRefPositionAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) {
if (attrib == "x") {
DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetPositionX>::create(this, writeProxy);
return kMiniscriptInstructionOutcomeContinue;
} else if (attrib == "y") {
DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetPositionY>::create(this, writeProxy);
return kMiniscriptInstructionOutcomeContinue;
}
return kMiniscriptInstructionOutcomeFailed;
}
void VisualElement::offsetTranslate(int32 xDelta, int32 yDelta, bool cachedOriginOnly) {
if (!cachedOriginOnly) {
_rect.left += xDelta;

View File

@ -36,6 +36,7 @@
#include "mtropolis/actions.h"
#include "mtropolis/data.h"
#include "mtropolis/debug.h"
#include "mtropolis/hacks.h"
#include "mtropolis/vthread.h"
class OSystem;
@ -511,6 +512,7 @@ public:
virtual ~DynamicListContainerBase();
virtual bool setAtIndex(size_t index, const DynamicValue &dynValue) = 0;
virtual bool getAtIndex(size_t index, DynamicValue &dynValue) const = 0;
virtual void truncateToSize(size_t sz) = 0;
virtual bool expandToMinimumSize(size_t sz) = 0;
virtual void setFrom(const DynamicListContainerBase &other) = 0; // Only supports setting same type!
virtual const void *getConstArrayPtr() const = 0;
@ -567,6 +569,7 @@ class DynamicListContainer : public DynamicListContainerBase {
public:
bool setAtIndex(size_t index, const DynamicValue &dynValue) override;
bool getAtIndex(size_t index, DynamicValue &dynValue) const override;
void truncateToSize(size_t sz) override;
bool expandToMinimumSize(size_t sz) override;
void setFrom(const DynamicListContainerBase &other) override;
const void *getConstArrayPtr() const override;
@ -586,6 +589,7 @@ public:
bool setAtIndex(size_t index, const DynamicValue &dynValue) override;
bool getAtIndex(size_t index, DynamicValue &dynValue) const override;
void truncateToSize(size_t sz) override;
bool expandToMinimumSize(size_t sz) override;
void setFrom(const DynamicListContainerBase &other) override;
const void *getConstArrayPtr() const override;
@ -603,6 +607,7 @@ class DynamicListContainer<VarReference> : public DynamicListContainerBase {
public:
bool setAtIndex(size_t index, const DynamicValue &dynValue) override;
bool getAtIndex(size_t index, DynamicValue &dynValue) const override;
void truncateToSize(size_t sz) override;
bool expandToMinimumSize(size_t sz) override;
void setFrom(const DynamicListContainerBase &other) override;
const void *getConstArrayPtr() const override;
@ -641,6 +646,12 @@ bool DynamicListContainer<T>::setAtIndex(size_t index, const DynamicValue &dynVa
return true;
}
template<class T>
void DynamicListContainer<T>::truncateToSize(size_t sz) {
if (_array.size() > sz)
_array.resize(sz);
}
template<class T>
bool DynamicListContainer<T>::expandToMinimumSize(size_t sz) {
_array.reserve(sz);
@ -730,6 +741,7 @@ struct DynamicList {
bool getAtIndex(size_t index, DynamicValue &value) const;
bool setAtIndex(size_t index, const DynamicValue &value);
void truncateToSize(size_t sz);
void expandToMinimumSize(size_t sz);
size_t getSize() const;
@ -937,6 +949,32 @@ private:
static DynamicValueWriteStringHelper _instance;
};
template<class TClass, MiniscriptInstructionOutcome (TClass::*TWriteMethod)(MiniscriptThread *thread, const DynamicValue &dest), MiniscriptInstructionOutcome (TClass::*TRefAttribMethod)(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, const Common::String &attrib)>
struct DynamicValueWriteOrRefAttribFuncHelper : public IDynamicValueWriteInterface {
MiniscriptInstructionOutcome write(MiniscriptThread *thread, const DynamicValue &dest, void *objectRef, uintptr ptrOrOffset) const override {
return (static_cast<TClass *>(objectRef)->*TWriteMethod)(thread, dest);
}
MiniscriptInstructionOutcome refAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib) const override {
return (static_cast<TClass *>(objectRef)->*TRefAttribMethod)(thread, proxy, attrib);
}
MiniscriptInstructionOutcome refAttribIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib, const DynamicValue &index) const override {
return kMiniscriptInstructionOutcomeFailed;
}
static void create(TClass *obj, DynamicValueWriteProxy &proxy) {
proxy.pod.ptrOrOffset = 0;
proxy.pod.objectRef = obj;
proxy.pod.ifc = &_instance;
}
private:
static DynamicValueWriteOrRefAttribFuncHelper _instance;
};
template<class TClass, MiniscriptInstructionOutcome (TClass::*TWriteMethod)(MiniscriptThread *thread, const DynamicValue &dest), MiniscriptInstructionOutcome (TClass::*TRefAttribMethod)(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, const Common::String &attrib)>
DynamicValueWriteOrRefAttribFuncHelper<TClass, TWriteMethod, TRefAttribMethod> DynamicValueWriteOrRefAttribFuncHelper<TClass, TWriteMethod, TRefAttribMethod>::_instance;
template<class TClass, MiniscriptInstructionOutcome (TClass::*TWriteMethod)(MiniscriptThread *thread, const DynamicValue &dest)>
struct DynamicValueWriteFuncHelper : public IDynamicValueWriteInterface {
MiniscriptInstructionOutcome write(MiniscriptThread *thread, const DynamicValue &dest, void *objectRef, uintptr ptrOrOffset) const override {
@ -1462,6 +1500,9 @@ public:
Audio::Mixer *getAudioMixer() const;
Hacks &getHacks();
const Hacks &getHacks() const;
#ifdef MTROPOLIS_DEBUG_ENABLE
void debugSetEnabled(bool enabled);
void debugBreak();
@ -1633,6 +1674,8 @@ private:
uint32 _modifierOverrideCursorID;
bool _haveModifierOverrideCursor;
Hacks _hacks;
#ifdef MTROPOLIS_DEBUG_ENABLE
Common::SharedPtr<Debugger> _debugger;
#endif
@ -2166,12 +2209,16 @@ protected:
MiniscriptInstructionOutcome scriptSetDirect(MiniscriptThread *thread, const DynamicValue &dest);
MiniscriptInstructionOutcome scriptSetPosition(MiniscriptThread *thread, const DynamicValue &dest);
MiniscriptInstructionOutcome scriptSetPositionX(MiniscriptThread *thread, const DynamicValue &dest);
MiniscriptInstructionOutcome scriptSetPositionY(MiniscriptThread *thread, const DynamicValue &dest);
MiniscriptInstructionOutcome scriptSetCenterPosition(MiniscriptThread *thread, const DynamicValue &dest);
MiniscriptInstructionOutcome scriptSetVisibility(MiniscriptThread *thread, const DynamicValue &result);
MiniscriptInstructionOutcome scriptSetWidth(MiniscriptThread *thread, const DynamicValue &dest);
MiniscriptInstructionOutcome scriptSetHeight(MiniscriptThread *thread, const DynamicValue &dest);
MiniscriptInstructionOutcome scriptSetLayer(MiniscriptThread *thread, const DynamicValue &dest);
MiniscriptInstructionOutcome scriptWriteRefPositionAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib);
void offsetTranslate(int32 xDelta, int32 yDelta, bool cachedOriginOnly);
Point16 getCenterPosition() const;