mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-09 03:10:22 +00:00
MTROPOLIS: Text rendering and Obsidian WordMixer
This commit is contained in:
parent
f9c294fd83
commit
1050da2a12
@ -22,6 +22,7 @@
|
||||
#include "mtropolis/assets.h"
|
||||
#include "mtropolis/asset_factory.h"
|
||||
|
||||
#include "graphics/managed_surface.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
@ -1078,7 +1079,7 @@ bool TextAsset::load(AssetLoaderContext &context, const Data::TextAsset &data) {
|
||||
if (!_bitmapRect.load(data.bitmapRect))
|
||||
return false;
|
||||
|
||||
_bitmapData.reset(new Graphics::Surface());
|
||||
_bitmapData.reset(new Graphics::ManagedSurface());
|
||||
|
||||
uint16 width = _bitmapRect.getWidth();
|
||||
uint16 height = _bitmapRect.getHeight();
|
||||
@ -1132,7 +1133,7 @@ bool TextAsset::isBitmap() const {
|
||||
return _isBitmap;
|
||||
}
|
||||
|
||||
const Common::SharedPtr<Graphics::Surface>& TextAsset::getBitmapSurface() const {
|
||||
const Common::SharedPtr<Graphics::ManagedSurface>& TextAsset::getBitmapSurface() const {
|
||||
return _bitmapData;
|
||||
}
|
||||
|
||||
|
@ -283,7 +283,7 @@ public:
|
||||
AssetType getAssetType() const override;
|
||||
|
||||
bool isBitmap() const;
|
||||
const Common::SharedPtr<Graphics::Surface> &getBitmapSurface() const;
|
||||
const Common::SharedPtr<Graphics::ManagedSurface> &getBitmapSurface() const;
|
||||
const Common::String &getString() const;
|
||||
const Common::Array<MacFormattingSpan> &getMacFormattingSpans() const;
|
||||
|
||||
@ -292,7 +292,7 @@ private:
|
||||
TextAlignment _alignment;
|
||||
bool _isBitmap;
|
||||
|
||||
Common::SharedPtr<Graphics::Surface> _bitmapData;
|
||||
Common::SharedPtr<Graphics::ManagedSurface> _bitmapData;
|
||||
Common::String _stringData;
|
||||
|
||||
Common::Array<MacFormattingSpan> _macFormattingSpans;
|
||||
|
@ -29,6 +29,10 @@
|
||||
#include "video/qt_decoder.h"
|
||||
|
||||
#include "common/substream.h"
|
||||
|
||||
#include "graphics/macgui/macfontmanager.h"
|
||||
#include "graphics/fontman.h"
|
||||
#include "graphics/font.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
|
||||
namespace MTropolis {
|
||||
@ -958,12 +962,16 @@ MiniscriptInstructionOutcome MToonElement::scriptSetRate(MiniscriptThread *threa
|
||||
}
|
||||
|
||||
|
||||
TextLabelElement::TextLabelElement() : _needsRender(false), _isBitmap(false) {
|
||||
TextLabelElement::TextLabelElement() : _needsRender(false), _isBitmap(false), _macFontID(0), _size(12), _alignment(kTextAlignmentLeft) {
|
||||
}
|
||||
|
||||
TextLabelElement::~TextLabelElement() {
|
||||
}
|
||||
|
||||
bool TextLabelElement::isTextLabel() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextLabelElement::load(ElementLoaderContext &context, const Data::TextLabelElement &data) {
|
||||
if (!loadCommon(data.name, data.guid, data.rect1, data.elementFlags, data.layer, 0, data.sectionID))
|
||||
return false;
|
||||
@ -976,13 +984,62 @@ bool TextLabelElement::load(ElementLoaderContext &context, const Data::TextLabel
|
||||
}
|
||||
|
||||
bool TextLabelElement::readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) {
|
||||
if (attrib == "text") {
|
||||
result.setString(_text);
|
||||
return true;
|
||||
}
|
||||
|
||||
return VisualElement::readAttribute(thread, result, attrib);
|
||||
}
|
||||
|
||||
bool TextLabelElement::readAttributeIndexed(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib, const DynamicValue &index) {
|
||||
if (attrib == "line") {
|
||||
int32 asInteger = 0;
|
||||
if (!index.roundToInt(asInteger) || asInteger < 1) {
|
||||
thread->error("Invalid text label line index");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t lineIndex = asInteger - 1;
|
||||
uint32 startPos;
|
||||
uint32 endPos;
|
||||
if (findLineRange(lineIndex, startPos, endPos))
|
||||
result.setString(_text.substr(startPos, endPos - startPos));
|
||||
else
|
||||
result.setString("");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return VisualElement::readAttributeIndexed(thread, result, attrib, index);
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome TextLabelElement::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) {
|
||||
if (attrib == "text") {
|
||||
DynamicValueWriteFuncHelper<TextLabelElement, &TextLabelElement::scriptSetText>::create(this, writeProxy);
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
|
||||
return VisualElement::writeRefAttribute(thread, writeProxy, attrib);
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome TextLabelElement::writeRefAttributeIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib, const DynamicValue &index) {
|
||||
if (attrib == "line") {
|
||||
int32 asInteger = 0;
|
||||
if (!index.roundToInt(asInteger) || asInteger < 1) {
|
||||
thread->error("Invalid text label line set index");
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
|
||||
writeProxy.pod.ifc = &TextLabelLineWriteInterface::_instance;
|
||||
writeProxy.pod.objectRef = this;
|
||||
writeProxy.pod.ptrOrOffset = asInteger - 1;
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
|
||||
return VisualElement::writeRefAttributeIndexed(thread, writeProxy, attrib, index);
|
||||
}
|
||||
|
||||
void TextLabelElement::activate() {
|
||||
Project *project = _runtime->getProject();
|
||||
Common::SharedPtr<Asset> asset = project->getAssetByID(_assetID).lock();
|
||||
@ -1012,10 +1069,251 @@ void TextLabelElement::activate() {
|
||||
void TextLabelElement::deactivate() {
|
||||
}
|
||||
|
||||
|
||||
void TextLabelElement::render(Window *window) {
|
||||
if (!_visible)
|
||||
return;
|
||||
|
||||
int renderWidth = _rect.getWidth();
|
||||
int renderHeight = _rect.getHeight();
|
||||
if (_renderedText) {
|
||||
if (renderWidth != _renderedText->w || renderHeight != _renderedText->h)
|
||||
_needsRender = true;
|
||||
}
|
||||
|
||||
if (_needsRender) {
|
||||
_needsRender = false;
|
||||
|
||||
_renderedText.reset();
|
||||
_renderedText.reset(new Graphics::ManagedSurface());
|
||||
|
||||
_renderedText->create(renderWidth, renderHeight, Graphics::PixelFormat::createFormatCLUT8());
|
||||
_renderedText->fillRect(Common::Rect(0, 0, renderWidth, renderHeight), 0);
|
||||
|
||||
const Graphics::Font *font = nullptr;
|
||||
if (_fontFamilyName.size() > 0) {
|
||||
font = FontMan.getFontByName(_fontFamilyName.c_str());
|
||||
} else if (_macFontID != 0) {
|
||||
// TODO: Formatting spans
|
||||
int slant = 0;
|
||||
// FIXME/HACK: These aren't public...
|
||||
if (_styleFlags.bold)
|
||||
slant |= 1;
|
||||
if (_styleFlags.italic)
|
||||
slant |= 2;
|
||||
if (_styleFlags.underline)
|
||||
slant |= 4;
|
||||
if (_styleFlags.outline)
|
||||
slant |= 8;
|
||||
if (_styleFlags.shadow)
|
||||
slant |= 16;
|
||||
if (_styleFlags.condensed)
|
||||
slant |= 32;
|
||||
if (_styleFlags.expanded)
|
||||
slant |= 64;
|
||||
|
||||
// FIXME/HACK: This is a stupid way to make getFont return null on failure
|
||||
font = _runtime->getMacFontManager()->getFont(Graphics::MacFont(_macFontID, _size, slant, static_cast<Graphics::FontManager::FontUsage>(-1)));
|
||||
}
|
||||
|
||||
if (!font)
|
||||
font = FontMan.getFontByUsage(Graphics::FontManager::kConsoleFont);
|
||||
|
||||
int height = font->getFontHeight();
|
||||
int ascent = font->getFontAscent();
|
||||
|
||||
Graphics::TextAlign textAlign = Graphics::kTextAlignLeft;
|
||||
switch (_alignment) {
|
||||
case kTextAlignmentLeft:
|
||||
textAlign = Graphics::kTextAlignLeft;
|
||||
break;
|
||||
case kTextAlignmentCenter:
|
||||
textAlign = Graphics::kTextAlignCenter;
|
||||
break;
|
||||
case kTextAlignmentRight:
|
||||
textAlign = Graphics::kTextAlignRight;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
int line = 0;
|
||||
uint32 lineStart = 0;
|
||||
while (lineStart < _text.size()) {
|
||||
bool noMoreLines = false;
|
||||
uint32 lineEndPos = _text.find('\r', lineStart);
|
||||
if (lineEndPos == Common::String::npos) {
|
||||
lineEndPos = _text.size();
|
||||
noMoreLines = true;
|
||||
}
|
||||
|
||||
Common::String lineStr;
|
||||
if (lineStart != 0 || lineEndPos != _text.size())
|
||||
lineStr = _text.substr(lineStart, lineEndPos - lineStart);
|
||||
else
|
||||
lineStr = _text;
|
||||
|
||||
// Split the line into sublines
|
||||
while (lineStr.size() > 0) {
|
||||
size_t lineCommitted = 0;
|
||||
bool prevWasWhitespace = true;
|
||||
bool hasWhitespaceAtStart = false;
|
||||
for (size_t i = 0; i <= lineStr.size(); i++) {
|
||||
bool isWhitespace = (i == lineStr.size() || lineStr[i] < ' ');
|
||||
|
||||
if (isWhitespace) {
|
||||
if (!prevWasWhitespace) {
|
||||
int width = font->getStringWidth(lineStr.substr(0, i));
|
||||
if (width > renderWidth)
|
||||
break;
|
||||
}
|
||||
lineCommitted = i + 1;
|
||||
}
|
||||
|
||||
prevWasWhitespace = isWhitespace;
|
||||
}
|
||||
|
||||
if (lineCommitted > lineStr.size())
|
||||
lineCommitted = lineStr.size();
|
||||
|
||||
// Too little space for anything
|
||||
if (lineCommitted == 0) {
|
||||
lineCommitted = 1;
|
||||
for (size_t i = 2; i <= lineStr.size(); i++) {
|
||||
int width = font->getStringWidth(lineStr.substr(0, i));
|
||||
if (width > renderWidth)
|
||||
break;
|
||||
lineCommitted = i;
|
||||
}
|
||||
}
|
||||
|
||||
font->drawString(_renderedText.get(), lineStr.substr(0, lineCommitted), 0, line * height + (height - ascent) / 2, renderWidth, 1, textAlign, 0, false);
|
||||
|
||||
if (lineCommitted == lineStr.size())
|
||||
lineStr.clear();
|
||||
else {
|
||||
lineStr = lineStr.substr(lineCommitted);
|
||||
line++;
|
||||
}
|
||||
}
|
||||
|
||||
if (noMoreLines)
|
||||
break;
|
||||
|
||||
line++;
|
||||
lineStart = lineEndPos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
Graphics::ManagedSurface *target = window->getSurface().get();
|
||||
Common::Rect srcRect(0, 0, renderWidth, renderHeight);
|
||||
Common::Rect destRect(_cachedAbsoluteOrigin.x, _cachedAbsoluteOrigin.y, _cachedAbsoluteOrigin.x + _rect.getWidth(), _cachedAbsoluteOrigin.y + _rect.getHeight());
|
||||
|
||||
const uint32 opaqueColor = 0xff000000;
|
||||
const uint32 drawPalette[2] = {0, opaqueColor};
|
||||
|
||||
if (_renderedText) {
|
||||
_renderedText->setPalette(drawPalette, 0, 2);
|
||||
target->transBlitFrom(*_renderedText.get(), srcRect, destRect, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void TextLabelElement::setTextStyle(uint16 macFontID, const Common::String &fontFamilyName, uint size, TextAlignment alignment, const TextStyleFlags &styleFlags) {
|
||||
_needsRender = true;
|
||||
|
||||
_macFontID = macFontID;
|
||||
_fontFamilyName = fontFamilyName;
|
||||
_size = size;
|
||||
_alignment = alignment;
|
||||
_styleFlags = styleFlags;
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome TextLabelElement::scriptSetText(MiniscriptThread *thread, const DynamicValue &value) {
|
||||
if (value.getType() != DynamicValueTypes::kString) {
|
||||
thread->error("Tried to set a text label element's text to something that wasn't a string");
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
|
||||
_text = value.getString();
|
||||
_needsRender = true;
|
||||
_macFormattingSpans.clear();
|
||||
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
|
||||
|
||||
MiniscriptInstructionOutcome TextLabelElement::scriptSetLine(MiniscriptThread *thread, size_t lineIndex, const DynamicValue &value) {
|
||||
if (value.getType() != DynamicValueTypes::kString) {
|
||||
thread->error("Tried to set a text label element's text to something that wasn't a string");
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
|
||||
uint32 startPos;
|
||||
uint32 endPos;
|
||||
if (findLineRange(lineIndex, startPos, endPos))
|
||||
_text = _text.substr(0, startPos) + value.getString() + _text.substr(endPos, _text.size() - endPos);
|
||||
else {
|
||||
size_t numLines = countLines();
|
||||
while (numLines <= lineIndex) {
|
||||
_text += '\r';
|
||||
numLines++;
|
||||
}
|
||||
_text += value.getString();
|
||||
}
|
||||
|
||||
_needsRender = true;
|
||||
_macFormattingSpans.clear();
|
||||
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
|
||||
bool TextLabelElement::findLineRange(size_t lineIndex, uint32 &outStartPos, uint32 &outEndPos) const {
|
||||
uint32 lineStart = 0;
|
||||
uint32 lineEnd = _text.size();
|
||||
size_t linesToScan = lineIndex + 1;
|
||||
|
||||
while (linesToScan > 0) {
|
||||
linesToScan--;
|
||||
|
||||
lineEnd = _text.find('\r', lineStart);
|
||||
if (lineEnd == Common::String::npos) {
|
||||
lineEnd = _text.size();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (linesToScan > 0)
|
||||
return false;
|
||||
|
||||
outStartPos = lineStart;
|
||||
outEndPos = lineEnd;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t TextLabelElement::countLines() const {
|
||||
size_t numLines = 1;
|
||||
for (char c : _text)
|
||||
if (c == '\r')
|
||||
numLines++;
|
||||
|
||||
return numLines;
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome TextLabelElement::TextLabelLineWriteInterface::write(MiniscriptThread *thread, const DynamicValue &dest, void *objectRef, uintptr ptrOrOffset) const {
|
||||
return static_cast<TextLabelElement *>(objectRef)->scriptSetLine(thread, ptrOrOffset, dest);
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome TextLabelElement::TextLabelLineWriteInterface::refAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib) const {
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome TextLabelElement::TextLabelLineWriteInterface::refAttribIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib, const DynamicValue &index) const {
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
|
||||
TextLabelElement::TextLabelLineWriteInterface TextLabelElement::TextLabelLineWriteInterface::_instance;
|
||||
|
||||
|
||||
SoundElement::SoundElement() : _finishTime(0), _shouldPlayIfNotPaused(true), _needsReset(true) {
|
||||
}
|
||||
|
||||
|
@ -239,22 +239,42 @@ public:
|
||||
TextLabelElement();
|
||||
~TextLabelElement();
|
||||
|
||||
bool isTextLabel() const override;
|
||||
|
||||
bool load(ElementLoaderContext &context, const Data::TextLabelElement &data);
|
||||
|
||||
bool readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib);
|
||||
bool readAttributeIndexed(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib, const DynamicValue &index) override;
|
||||
MiniscriptInstructionOutcome writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib);
|
||||
MiniscriptInstructionOutcome writeRefAttributeIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib, const DynamicValue &index) override;
|
||||
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
|
||||
void render(Window *window) override;
|
||||
|
||||
void setTextStyle(uint16 macFontID, const Common::String &fontFamilyName, uint size, TextAlignment alignment, const TextStyleFlags &styleFlags);
|
||||
|
||||
#ifdef MTROPOLIS_DEBUG_ENABLE
|
||||
const char *debugGetTypeName() const override { return "Text Label Element"; }
|
||||
SupportStatus debugGetSupportStatus() const override { return kSupportStatusPartial; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
struct TextLabelLineWriteInterface : public IDynamicValueWriteInterface {
|
||||
MiniscriptInstructionOutcome write(MiniscriptThread *thread, const DynamicValue &dest, void *objectRef, uintptr ptrOrOffset) const override;
|
||||
MiniscriptInstructionOutcome refAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib) const override;
|
||||
MiniscriptInstructionOutcome refAttribIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib, const DynamicValue &index) const override;
|
||||
|
||||
static TextLabelLineWriteInterface _instance;
|
||||
};
|
||||
|
||||
MiniscriptInstructionOutcome scriptSetText(MiniscriptThread *thread, const DynamicValue &value);
|
||||
MiniscriptInstructionOutcome scriptSetLine(MiniscriptThread *thread, size_t lineIndex, const DynamicValue &value);
|
||||
|
||||
bool findLineRange(size_t lineNum, uint32 &outStartPos, uint32 &outEndPos) const;
|
||||
size_t countLines() const;
|
||||
|
||||
bool _cacheBitmap;
|
||||
bool _needsRender;
|
||||
|
||||
@ -262,8 +282,18 @@ private:
|
||||
uint32 _assetID;
|
||||
|
||||
Common::String _text;
|
||||
uint16 _macFontID;
|
||||
Common::String _fontFamilyName;
|
||||
uint _size;
|
||||
TextAlignment _alignment;
|
||||
TextStyleFlags _styleFlags;
|
||||
|
||||
Common::Array<MacFormattingSpan> _macFormattingSpans;
|
||||
Common::SharedPtr<Graphics::Surface> _renderedText; // NOTE: This may be a pre-rendered instance that is read-only. Rendering must create a new surface!
|
||||
|
||||
// NOTE: This may be a surface loaded from data, so it must not be altered.
|
||||
// If you need to render again, recreate the surface. If you want to change
|
||||
// this behavior, please add a flag indicating that it is from the asset.
|
||||
Common::SharedPtr<Graphics::ManagedSurface> _renderedText;
|
||||
|
||||
Runtime *_runtime;
|
||||
};
|
||||
@ -275,8 +305,8 @@ public:
|
||||
|
||||
bool load(ElementLoaderContext &context, const Data::SoundElement &data);
|
||||
|
||||
bool readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib);
|
||||
MiniscriptInstructionOutcome writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib);
|
||||
bool readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) override;
|
||||
MiniscriptInstructionOutcome writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) override;
|
||||
|
||||
VThreadState consumeCommand(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg);
|
||||
|
||||
|
@ -484,7 +484,7 @@ MiniscriptInstructionOutcome Set::execute(MiniscriptThread *thread) const {
|
||||
}
|
||||
|
||||
// Convert value
|
||||
MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0, false);
|
||||
MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(0, true);
|
||||
if (outcome != kMiniscriptInstructionOutcomeContinue)
|
||||
return outcome;
|
||||
|
||||
@ -1679,18 +1679,26 @@ PushGlobal::PushGlobal(uint32 globalID, bool isLValue) : _globalID(globalID), _i
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome PushGlobal::execute(MiniscriptThread *thread) const {
|
||||
DynamicValue value;
|
||||
thread->pushValue(DynamicValue());
|
||||
|
||||
DynamicValue &value = thread->getStackValueFromTop(0).value;
|
||||
|
||||
switch (_globalID) {
|
||||
case kGlobalRefElement:
|
||||
case kGlobalRefSection:
|
||||
case kGlobalRefScene:
|
||||
case kGlobalRefProject:
|
||||
return executeFindFilteredParent(thread);
|
||||
return executeFindFilteredParent(thread, value);
|
||||
case kGlobalRefModifier:
|
||||
value.setObject(thread->getModifier()->getSelfReference());
|
||||
break;
|
||||
case kGlobalRefIncomingData:
|
||||
value = thread->getMessageProperties()->getValue();
|
||||
if (_isLValue) {
|
||||
DynamicValueWriteProxy proxy;
|
||||
thread->createWriteIncomingDataProxy(proxy);
|
||||
value.setWriteProxy(proxy);
|
||||
} else
|
||||
value = thread->getMessageProperties()->getValue();
|
||||
break;
|
||||
case kGlobalRefSource:
|
||||
value.setObject(ObjectReference(thread->getMessageProperties()->getSource()));
|
||||
@ -1713,12 +1721,10 @@ MiniscriptInstructionOutcome PushGlobal::execute(MiniscriptThread *thread) const
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
|
||||
thread->pushValue(value);
|
||||
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome PushGlobal::executeFindFilteredParent(MiniscriptThread *thread) const {
|
||||
MiniscriptInstructionOutcome PushGlobal::executeFindFilteredParent(MiniscriptThread *thread, DynamicValue &result) const {
|
||||
Common::WeakPtr<RuntimeObject> ref = thread->getModifier()->getSelfReference();
|
||||
for (;;) {
|
||||
Common::SharedPtr<RuntimeObject> obj = ref.lock();
|
||||
@ -1762,10 +1768,7 @@ MiniscriptInstructionOutcome PushGlobal::executeFindFilteredParent(MiniscriptThr
|
||||
}
|
||||
}
|
||||
|
||||
DynamicValue value;
|
||||
value.setObject(ref);
|
||||
|
||||
thread->pushValue(value);
|
||||
result.setObject(ref);
|
||||
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
@ -1927,6 +1930,31 @@ bool MiniscriptThread::evaluateTruthOfResult(bool &isTrue) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void MiniscriptThread::createWriteIncomingDataProxy(DynamicValueWriteProxy &proxy) {
|
||||
proxy.pod.ifc = &IncomingDataWriteInterface::_instance;
|
||||
proxy.pod.objectRef = this;
|
||||
proxy.pod.ptrOrOffset = 0;
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome MiniscriptThread::IncomingDataWriteInterface::write(MiniscriptThread *thread, const DynamicValue &value, void *objectRef, uintptr ptrOrOffset) const {
|
||||
thread->_msgProps->setValue(value);
|
||||
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome MiniscriptThread::IncomingDataWriteInterface::refAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib) const {
|
||||
// TODO: Generic refAttrib for dynamic values
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome MiniscriptThread::IncomingDataWriteInterface::refAttribIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib, const DynamicValue &index) const {
|
||||
// TODO: Generic refAttribIndexed for dynamic values
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
|
||||
MiniscriptThread::IncomingDataWriteInterface MiniscriptThread::IncomingDataWriteInterface::_instance;
|
||||
|
||||
|
||||
VThreadState MiniscriptThread::resumeTask(const ResumeTaskData &data) {
|
||||
return data.thread->resume(data);
|
||||
}
|
||||
|
@ -351,7 +351,7 @@ namespace MiniscriptInstructions {
|
||||
kGlobalRefActiveScene = 11,
|
||||
};
|
||||
|
||||
MiniscriptInstructionOutcome executeFindFilteredParent(MiniscriptThread *thread) const;
|
||||
MiniscriptInstructionOutcome executeFindFilteredParent(MiniscriptThread *thread, DynamicValue &result) const;
|
||||
|
||||
uint32 _globalID;
|
||||
bool _isLValue;
|
||||
@ -409,7 +409,17 @@ public:
|
||||
|
||||
bool evaluateTruthOfResult(bool &isTrue);
|
||||
|
||||
void createWriteIncomingDataProxy(DynamicValueWriteProxy &proxy);
|
||||
|
||||
private:
|
||||
struct IncomingDataWriteInterface : public IDynamicValueWriteInterface {
|
||||
MiniscriptInstructionOutcome write(MiniscriptThread *thread, const DynamicValue &dest, void *objectRef, uintptr ptrOrOffset) const override;
|
||||
MiniscriptInstructionOutcome refAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib) const override;
|
||||
MiniscriptInstructionOutcome refAttribIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib, const DynamicValue &index) const override;
|
||||
|
||||
static IncomingDataWriteInterface _instance;
|
||||
};
|
||||
|
||||
struct ResumeTaskData {
|
||||
Common::SharedPtr<MiniscriptThread> thread;
|
||||
};
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include "mtropolis/modifier_factory.h"
|
||||
#include "mtropolis/saveload.h"
|
||||
|
||||
#include "mtropolis/elements.h"
|
||||
|
||||
#include "common/memstream.h"
|
||||
|
||||
namespace MTropolis {
|
||||
@ -321,7 +323,7 @@ bool MessengerModifier::respondsToEvent(const Event &evt) const {
|
||||
|
||||
VThreadState MessengerModifier::consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) {
|
||||
if (_when.respondsTo(msg->getEvent())) {
|
||||
_sendSpec.sendFromMessenger(runtime, this);
|
||||
_sendSpec.sendFromMessenger(runtime, this, msg->getValue());
|
||||
}
|
||||
|
||||
return kVThreadReturn;
|
||||
@ -645,6 +647,7 @@ VThreadState IfMessengerModifier::consumeMessage(Runtime *runtime, const Common:
|
||||
EvaluateAndSendTaskData *evalAndSendData = runtime->getVThread().pushTask("IfMessengerModifier::evaluateAndSendTask", this, &IfMessengerModifier::evaluateAndSendTask);
|
||||
evalAndSendData->thread = thread;
|
||||
evalAndSendData->runtime = runtime;
|
||||
evalAndSendData->incomingData = msg->getValue();
|
||||
|
||||
MiniscriptThread::runOnVThread(runtime->getVThread(), thread);
|
||||
}
|
||||
@ -680,7 +683,7 @@ VThreadState IfMessengerModifier::evaluateAndSendTask(const EvaluateAndSendTaskD
|
||||
return kVThreadError;
|
||||
|
||||
if (isTrue)
|
||||
_sendSpec.sendFromMessenger(taskData.runtime, this);
|
||||
_sendSpec.sendFromMessenger(taskData.runtime, this, taskData.incomingData);
|
||||
|
||||
return kVThreadReturn;
|
||||
}
|
||||
@ -726,6 +729,9 @@ VThreadState TimerMessengerModifier::consumeMessage(Runtime *runtime, const Comm
|
||||
debug(3, "Timer %x '%s' scheduled to execute in %i milliseconds", getStaticGUID(), getName().c_str(), realMilliseconds);
|
||||
if (!_scheduledEvent) {
|
||||
_scheduledEvent = runtime->getScheduler().scheduleMethod<TimerMessengerModifier, &TimerMessengerModifier::trigger>(runtime->getPlayTime() + realMilliseconds, this);
|
||||
_incomingData = msg->getValue();
|
||||
if (_incomingData.getType() == DynamicValueTypes::kList)
|
||||
_incomingData.setList(_incomingData.getList()->clone());
|
||||
}
|
||||
}
|
||||
|
||||
@ -757,7 +763,7 @@ void TimerMessengerModifier::trigger(Runtime *runtime) {
|
||||
} else
|
||||
_scheduledEvent.reset();
|
||||
|
||||
_sendSpec.sendFromMessenger(runtime, this);
|
||||
_sendSpec.sendFromMessenger(runtime, this, _incomingData);
|
||||
}
|
||||
|
||||
bool BoundaryDetectionMessengerModifier::load(ModifierLoaderContext &context, const Data::BoundaryDetectionMessengerModifier &data) {
|
||||
@ -1014,16 +1020,13 @@ bool KeyboardMessengerModifier::checkKeyEventTrigger(Runtime *runtime, Common::E
|
||||
|
||||
void KeyboardMessengerModifier::dispatchMessage(Runtime *runtime, const Common::String &charStr) {
|
||||
Common::SharedPtr<MessageProperties> msgProps;
|
||||
if (_sendSpec.with.getType() == DynamicValueTypes::kIncomingData) {
|
||||
if (charStr.size() != 1)
|
||||
warning("Keyboard messenger is supposed to send the character code, but they key was a special key and we haven't implemented conversion of those keycodes");
|
||||
|
||||
DynamicValue charStrValue;
|
||||
charStrValue.setString(charStr);
|
||||
_sendSpec.sendFromMessengerWithCustomData(runtime, this, charStrValue);
|
||||
} else {
|
||||
_sendSpec.sendFromMessenger(runtime, this);
|
||||
}
|
||||
if (charStr.size() != 1)
|
||||
warning("Keyboard messenger is supposed to send the character code, but they key was a special key and we haven't implemented conversion of those keycodes");
|
||||
|
||||
DynamicValue charStrValue;
|
||||
charStrValue.setString(charStr);
|
||||
_sendSpec.sendFromMessenger(runtime, this, charStrValue);
|
||||
}
|
||||
|
||||
void KeyboardMessengerModifier::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
|
||||
@ -1034,20 +1037,6 @@ void KeyboardMessengerModifier::linkInternalReferences(ObjectLinkingScope *scope
|
||||
_sendSpec.linkInternalReferences(scope);
|
||||
}
|
||||
|
||||
TextStyleModifier::StyleFlags::StyleFlags() : bold(false), italic(false), underline(false), outline(false), shadow(false), condensed(false), expanded(false) {
|
||||
}
|
||||
|
||||
bool TextStyleModifier::StyleFlags::load(uint8 dataStyleFlags) {
|
||||
bold = ((dataStyleFlags & 0x01) != 0);
|
||||
italic = ((dataStyleFlags & 0x02) != 0);
|
||||
underline = ((dataStyleFlags & 0x03) != 0);
|
||||
outline = ((dataStyleFlags & 0x04) != 0);
|
||||
shadow = ((dataStyleFlags & 0x10) != 0);
|
||||
condensed = ((dataStyleFlags & 0x20) != 0);
|
||||
expanded = ((dataStyleFlags & 0x40) != 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextStyleModifier::load(ModifierLoaderContext &context, const Data::TextStyleModifier &data) {
|
||||
if (!loadTypicalHeader(data.modHeader))
|
||||
return false;
|
||||
@ -1057,12 +1046,59 @@ bool TextStyleModifier::load(ModifierLoaderContext &context, const Data::TextSty
|
||||
|
||||
_macFontID = data.macFontID;
|
||||
_size = data.size;
|
||||
_alignment = static_cast<Alignment>(data.alignment);
|
||||
_fontFamilyName = data.fontFamilyName;
|
||||
|
||||
if (!_styleFlags.load(data.flags))
|
||||
return false;
|
||||
|
||||
switch (data.alignment) {
|
||||
case 0:
|
||||
_alignment = kTextAlignmentLeft;
|
||||
break;
|
||||
case 1:
|
||||
_alignment = kTextAlignmentCenter;
|
||||
break;
|
||||
case 0xffff:
|
||||
_alignment = kTextAlignmentRight;
|
||||
break;
|
||||
default:
|
||||
warning("Unrecognized text alignment");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextStyleModifier::respondsToEvent(const Event &evt) const {
|
||||
if (_applyWhen.respondsTo(evt) || _removeWhen.respondsTo(evt))
|
||||
return true;
|
||||
|
||||
return Modifier::respondsToEvent(evt);
|
||||
}
|
||||
|
||||
VThreadState TextStyleModifier::consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) {
|
||||
if (_applyWhen.respondsTo(msg->getEvent())) {
|
||||
Structural *owner = findStructuralOwner();
|
||||
if (owner && owner->isElement()) {
|
||||
Element *element = static_cast<Element *>(owner);
|
||||
if (element->isVisual()) {
|
||||
VisualElement *visualElement = static_cast<VisualElement *>(element);
|
||||
if (visualElement->isTextLabel()) {
|
||||
static_cast<TextLabelElement *>(visualElement)->setTextStyle(_macFontID, _fontFamilyName, _size, _alignment, _styleFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return kVThreadReturn;
|
||||
} else if (_removeWhen.respondsTo(msg->getEvent())) {
|
||||
// Doesn't actually do anything
|
||||
return kVThreadReturn;
|
||||
}
|
||||
|
||||
return Modifier::consumeMessage(runtime, msg);
|
||||
}
|
||||
|
||||
|
||||
Common::SharedPtr<Modifier> TextStyleModifier::shallowClone() const {
|
||||
return Common::SharedPtr<Modifier>(new TextStyleModifier(*this));
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
#ifndef MTROPOLIS_MODIFIERS_H
|
||||
#define MTROPOLIS_MODIFIERS_H
|
||||
|
||||
#include "mtropolis/render.h"
|
||||
#include "mtropolis/runtime.h"
|
||||
#include "mtropolis/data.h"
|
||||
|
||||
@ -349,6 +350,7 @@ private:
|
||||
struct EvaluateAndSendTaskData {
|
||||
Common::SharedPtr<MiniscriptThread> thread;
|
||||
Runtime *runtime;
|
||||
DynamicValue incomingData;
|
||||
};
|
||||
|
||||
Common::SharedPtr<Modifier> shallowClone() const override;
|
||||
@ -391,6 +393,7 @@ private:
|
||||
MessengerSendSpec _sendSpec;
|
||||
uint32 _milliseconds;
|
||||
bool _looping;
|
||||
DynamicValue _incomingData;
|
||||
|
||||
Common::SharedPtr<ScheduledEvent> _scheduledEvent;
|
||||
};
|
||||
@ -521,24 +524,8 @@ class TextStyleModifier : public Modifier {
|
||||
public:
|
||||
bool load(ModifierLoaderContext &context, const Data::TextStyleModifier &data);
|
||||
|
||||
enum Alignment {
|
||||
kAlignmentLeft = 0,
|
||||
kAlignmentCenter = 1,
|
||||
kAlignmentRight = 0xffff,
|
||||
};
|
||||
|
||||
struct StyleFlags {
|
||||
bool bold : 1;
|
||||
bool italic : 1;
|
||||
bool underline : 1;
|
||||
bool outline : 1;
|
||||
bool shadow : 1;
|
||||
bool condensed : 1;
|
||||
bool expanded : 1;
|
||||
|
||||
StyleFlags();
|
||||
bool load(uint8 dataStyleFlags);
|
||||
};
|
||||
bool respondsToEvent(const Event &evt) const override;
|
||||
VThreadState consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) override;
|
||||
|
||||
#ifdef MTROPOLIS_DEBUG_ENABLE
|
||||
const char *debugGetTypeName() const override { return "Text Style Modifier"; }
|
||||
@ -551,7 +538,8 @@ private:
|
||||
uint16 _size;
|
||||
ColorRGB8 _textColor;
|
||||
ColorRGB8 _backgroundColor;
|
||||
Alignment _alignment;
|
||||
TextAlignment _alignment;
|
||||
TextStyleFlags _styleFlags;
|
||||
Event _applyWhen;
|
||||
Event _removeWhen;
|
||||
Common::String _fontFamilyName;
|
||||
|
@ -85,8 +85,18 @@ bool TextWorkModifier::readAttribute(MiniscriptThread *thread, DynamicValue &res
|
||||
result.setInt(index);
|
||||
return true;
|
||||
} else if (attrib == "numword") {
|
||||
thread->error("Not-yet-implemented TextWork attribute 'numword'");
|
||||
return false;
|
||||
int numWords = 0;
|
||||
bool lastWasWhitespace = true;
|
||||
for (size_t i = 0; i < _string.size(); i++) {
|
||||
char c = _string[i];
|
||||
bool isWhitespace = (c <= ' ');
|
||||
if (lastWasWhitespace && !isWhitespace)
|
||||
numWords++;
|
||||
lastWasWhitespace = isWhitespace;
|
||||
}
|
||||
|
||||
result.setInt(numWords);
|
||||
return true;
|
||||
}
|
||||
|
||||
return Modifier::readAttribute(thread, result, attrib);
|
||||
@ -106,11 +116,11 @@ MiniscriptInstructionOutcome TextWorkModifier::writeRefAttribute(MiniscriptThrea
|
||||
DynamicValueWriteStringHelper::create(&_token, result);
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
} else if (attrib == "firstword") {
|
||||
thread->error("Not-yet-implemented TextWork attrib 'firstword'");
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
DynamicValueWriteFuncHelper<TextWorkModifier, &TextWorkModifier::scriptSetFirstWord>::create(this, result);
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
} else if (attrib == "lastword") {
|
||||
thread->error("Not-yet-implemented TextWork attrib 'lastword'");
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
DynamicValueWriteFuncHelper<TextWorkModifier, &TextWorkModifier::scriptSetLastWord>::create(this, result);
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
|
||||
return Modifier::writeRefAttribute(thread, result, attrib);
|
||||
@ -120,6 +130,75 @@ Common::SharedPtr<Modifier> TextWorkModifier::shallowClone() const {
|
||||
return Common::SharedPtr<Modifier>(new TextWorkModifier(*this));
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome TextWorkModifier::scriptSetFirstWord(MiniscriptThread *thread, const DynamicValue &value) {
|
||||
// This and lastword are only used in tandem with lastword, exact functionality is unclear since it's
|
||||
// also used in tandem with "output" which is normally used with firstchar+lastchar.
|
||||
// We attempt to emulate it by setting firstchar+lastchar to the correct values
|
||||
int32 asInteger = 0;
|
||||
if (!value.roundToInt(asInteger))
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
|
||||
int numWords = 0;
|
||||
bool lastWasWhitespace = true;
|
||||
for (size_t i = 0; i < _string.size(); i++) {
|
||||
char c = _string[i];
|
||||
bool isWhitespace = (c <= ' ');
|
||||
if (lastWasWhitespace && !isWhitespace) {
|
||||
numWords++;
|
||||
|
||||
if (numWords == asInteger) {
|
||||
_firstChar = i + 1;
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
}
|
||||
lastWasWhitespace = isWhitespace;
|
||||
|
||||
}
|
||||
|
||||
thread->error("Invalid index for 'firstword'");
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome TextWorkModifier::scriptSetLastWord(MiniscriptThread* thread, const DynamicValue& value) {
|
||||
int32 asInteger = 0;
|
||||
if (!value.roundToInt(asInteger))
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
|
||||
int numWordEnds = 0;
|
||||
bool lastWasWhitespace = true;
|
||||
for (size_t i = 0; i < _string.size(); i++) {
|
||||
char c = _string[i];
|
||||
bool isWhitespace = (c <= ' ');
|
||||
if (!lastWasWhitespace && isWhitespace) {
|
||||
numWordEnds++;
|
||||
|
||||
if (numWordEnds == asInteger) {
|
||||
_firstChar = i - 1;
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
}
|
||||
lastWasWhitespace = isWhitespace;
|
||||
|
||||
if (numWordEnds == asInteger) {
|
||||
_lastChar = i;
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!lastWasWhitespace) {
|
||||
numWordEnds++;
|
||||
if (numWordEnds == asInteger) {
|
||||
_lastChar = _string.size();
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
}
|
||||
|
||||
thread->error("Invalid index for 'firstword'");
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
DictionaryModifier::DictionaryModifier() : _plugIn(nullptr) {
|
||||
}
|
||||
|
||||
@ -257,13 +336,155 @@ Common::SharedPtr<Modifier> DictionaryModifier::shallowClone() const {
|
||||
return Common::SharedPtr<Modifier>(new DictionaryModifier(*this));
|
||||
}
|
||||
|
||||
WordMixerModifier::WordMixerModifier() {
|
||||
WordMixerModifier::WordMixerModifier() : _matches(0), _result(0) {
|
||||
}
|
||||
|
||||
bool WordMixerModifier::load(const PlugInModifierLoaderContext &context, const Data::Obsidian::WordMixerModifier &data) {
|
||||
_plugIn = static_cast<const ObsidianPlugIn *>(context.plugIn);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WordMixerModifier::readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) {
|
||||
if (attrib == "result") {
|
||||
result.setInt(_result);
|
||||
return true;
|
||||
}
|
||||
if (attrib == "matches") {
|
||||
result.setInt(_matches);
|
||||
return true;
|
||||
}
|
||||
if (attrib == "output") {
|
||||
result.setString(_output);
|
||||
return true;
|
||||
}
|
||||
|
||||
return Modifier::readAttribute(thread, result, attrib);
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome WordMixerModifier::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
|
||||
if (attrib == "input") {
|
||||
DynamicValueWriteFuncHelper<WordMixerModifier, &WordMixerModifier::scriptSetInput>::create(this, result);
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
if (attrib == "search") {
|
||||
DynamicValueWriteFuncHelper<WordMixerModifier, &WordMixerModifier::scriptSetSearch>::create(this, result);
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
|
||||
return Modifier::writeRefAttribute(thread, result, attrib);
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome WordMixerModifier::scriptSetInput(MiniscriptThread *thread, const DynamicValue &value) {
|
||||
if (value.getType() != DynamicValueTypes::kString) {
|
||||
thread->error("Invalid type for WordMixer input attribute");
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
|
||||
_input = value.getString();
|
||||
|
||||
Common::Array<char> sourceChars;
|
||||
for (char c : _input) {
|
||||
if (c > ' ')
|
||||
sourceChars.push_back(invariantToLower(c));
|
||||
}
|
||||
|
||||
Common::Array<bool> charIsUsed;
|
||||
charIsUsed.resize(sourceChars.size());
|
||||
|
||||
const Common::Array<WordGameData::WordBucket> &wordBuckets = _plugIn->getWordGameData()->getWordBuckets();
|
||||
|
||||
_output.clear();
|
||||
_matches = 0;
|
||||
|
||||
size_t numWordBuckets = wordBuckets.size();
|
||||
for (size_t rbucket = 0; rbucket < numWordBuckets; rbucket++) {
|
||||
size_t wordLength = numWordBuckets - 1 - rbucket;
|
||||
|
||||
const WordGameData::WordBucket &bucket = wordBuckets[wordLength];
|
||||
|
||||
size_t numWords = bucket.wordIndexes.size();
|
||||
|
||||
for (size_t wi = 0; wi < numWords; wi++) {
|
||||
const char *wordChars = &bucket.chars[bucket.spacing * wi];
|
||||
|
||||
for (bool &b : charIsUsed)
|
||||
b = false;
|
||||
|
||||
bool isMatch = true;
|
||||
for (size_t ci = 0; ci < wordLength; ci++) {
|
||||
const char wordChar = wordChars[ci];
|
||||
|
||||
bool foundAvailableSource = false;
|
||||
for (size_t srci = 0; srci < sourceChars.size(); srci++) {
|
||||
if (sourceChars[srci] == wordChar && !charIsUsed[srci]) {
|
||||
foundAvailableSource = true;
|
||||
charIsUsed[srci] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundAvailableSource) {
|
||||
isMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isMatch) {
|
||||
if (_matches > 0)
|
||||
_output += ' ';
|
||||
_output += Common::String(wordChars, wordLength);
|
||||
_matches++;
|
||||
}
|
||||
}
|
||||
|
||||
if (_matches > 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (_matches == 0)
|
||||
_output = "xxx";
|
||||
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
|
||||
MiniscriptInstructionOutcome WordMixerModifier::scriptSetSearch(MiniscriptThread *thread, const DynamicValue &value) {
|
||||
if (value.getType() != DynamicValueTypes::kBoolean) {
|
||||
thread->error("Invalid type for WordMixer search attribute");
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
}
|
||||
|
||||
if (!value.getBool())
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
|
||||
size_t searchLength = _input.size();
|
||||
const Common::Array<WordGameData::WordBucket> &buckets = _plugIn->getWordGameData()->getWordBuckets();
|
||||
_result = 0;
|
||||
if (searchLength < buckets.size()) {
|
||||
const WordGameData::WordBucket &bucket = buckets[searchLength];
|
||||
|
||||
bool found = false;
|
||||
for (size_t wi = 0; wi < bucket.wordIndexes.size(); wi++) {
|
||||
const char *wordChars = &bucket.chars[wi * bucket.spacing];
|
||||
|
||||
bool isMatch = true;
|
||||
for (size_t ci = 0; ci < searchLength; ci++) {
|
||||
if (invariantToLower(_input[ci]) != wordChars[ci]) {
|
||||
isMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isMatch) {
|
||||
_result = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return kMiniscriptInstructionOutcomeContinue;
|
||||
}
|
||||
|
||||
Common::SharedPtr<Modifier> WordMixerModifier::shallowClone() const {
|
||||
return Common::SharedPtr<Modifier>(new WordMixerModifier(*this));
|
||||
}
|
||||
|
@ -74,6 +74,9 @@ public:
|
||||
private:
|
||||
Common::SharedPtr<Modifier> shallowClone() const override;
|
||||
|
||||
MiniscriptInstructionOutcome scriptSetFirstWord(MiniscriptThread *thread, const DynamicValue &value);
|
||||
MiniscriptInstructionOutcome scriptSetLastWord(MiniscriptThread *thread, const DynamicValue &value);
|
||||
|
||||
Common::String _string;
|
||||
Common::String _token;
|
||||
|
||||
@ -115,12 +118,25 @@ public:
|
||||
|
||||
bool load(const PlugInModifierLoaderContext &context, const Data::Obsidian::WordMixerModifier &data);
|
||||
|
||||
bool readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib);
|
||||
MiniscriptInstructionOutcome writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib);
|
||||
|
||||
#ifdef MTROPOLIS_DEBUG_ENABLE
|
||||
const char *debugGetTypeName() const override { return "WordMixer Modifier"; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
Common::SharedPtr<Modifier> shallowClone() const override;
|
||||
|
||||
MiniscriptInstructionOutcome scriptSetInput(MiniscriptThread *thread, const DynamicValue &value);
|
||||
MiniscriptInstructionOutcome scriptSetSearch(MiniscriptThread *thread, const DynamicValue &value);
|
||||
|
||||
Common::String _input;
|
||||
Common::String _output;
|
||||
int _matches;
|
||||
int _result;
|
||||
|
||||
const ObsidianPlugIn *_plugIn;
|
||||
};
|
||||
|
||||
class ObsidianPlugIn : public MTropolis::PlugIn {
|
||||
|
@ -66,9 +66,13 @@ DataReadErrorCode TextWorkModifier::load(PlugIn& plugIn, const PlugInModifier& p
|
||||
}
|
||||
|
||||
DataReadErrorCode WordMixerModifier::load(PlugIn &plugIn, const PlugInModifier &prefix, DataReader &reader) {
|
||||
if (prefix.plugInRevision != 1)
|
||||
if (prefix.plugInRevision != 0)
|
||||
return kDataReadErrorUnsupportedRevision;
|
||||
|
||||
// Looks like this contains matches, but don't really need them...
|
||||
if (!reader.skip(prefix.subObjectSize))
|
||||
return kDataReadErrorReadFailed;
|
||||
|
||||
return kDataReadErrorNone;
|
||||
}
|
||||
|
||||
|
@ -963,8 +963,11 @@ Common::SharedPtr<ModifierSaveLoad> ListVariableModifier::getSaveLoad() {
|
||||
bool ListVariableModifier::varSetValue(MiniscriptThread *thread, const DynamicValue &value) {
|
||||
if (value.getType() == DynamicValueTypes::kList)
|
||||
_list = value.getList()->clone();
|
||||
else
|
||||
return false;
|
||||
else {
|
||||
if (!_list)
|
||||
_list.reset(new DynamicList());
|
||||
return _list->setAtIndex(0, value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -78,6 +78,20 @@ inline int expand5To8(int value) {
|
||||
return (value * 33) >> 2;
|
||||
}
|
||||
|
||||
TextStyleFlags::TextStyleFlags() : bold(false), italic(false), underline(false), outline(false), shadow(false), condensed(false), expanded(false) {
|
||||
}
|
||||
|
||||
bool TextStyleFlags::load(uint8 dataStyleFlags) {
|
||||
bold = ((dataStyleFlags & 0x01) != 0);
|
||||
italic = ((dataStyleFlags & 0x02) != 0);
|
||||
underline = ((dataStyleFlags & 0x03) != 0);
|
||||
outline = ((dataStyleFlags & 0x04) != 0);
|
||||
shadow = ((dataStyleFlags & 0x10) != 0);
|
||||
condensed = ((dataStyleFlags & 0x20) != 0);
|
||||
expanded = ((dataStyleFlags & 0x40) != 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
MacFontFormatting::MacFontFormatting() : fontID(0), fontFlags(0), size(12) {
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,19 @@ enum TextAlignment {
|
||||
kTextAlignmentRight,
|
||||
};
|
||||
|
||||
struct TextStyleFlags {
|
||||
bool bold : 1;
|
||||
bool italic : 1;
|
||||
bool underline : 1;
|
||||
bool outline : 1;
|
||||
bool shadow : 1;
|
||||
bool condensed : 1;
|
||||
bool expanded : 1;
|
||||
|
||||
TextStyleFlags();
|
||||
bool load(uint8 dataStyleFlags);
|
||||
};
|
||||
|
||||
struct MacFontFormatting {
|
||||
MacFontFormatting();
|
||||
MacFontFormatting(uint16 fontID, uint8 fontFlags, uint16 size);
|
||||
|
@ -1559,7 +1559,7 @@ void DynamicValue::initFromOther(const DynamicValue &other) {
|
||||
|
||||
switch (other._type) {
|
||||
case DynamicValueTypes::kNull:
|
||||
case DynamicValueTypes::kIncomingData:
|
||||
case DynamicValueTypes::kIncomingData: // FIXME: Get rid of this
|
||||
break;
|
||||
case DynamicValueTypes::kInteger:
|
||||
_value.asInt = other._value.asInt;
|
||||
@ -1860,8 +1860,11 @@ void MessengerSendSpec::resolveVariableObjectType(RuntimeObject *obj, Common::We
|
||||
}
|
||||
}
|
||||
|
||||
void MessengerSendSpec::sendFromMessenger(Runtime *runtime, Modifier *sender) const {
|
||||
sendFromMessengerWithCustomData(runtime, sender, this->with);
|
||||
void MessengerSendSpec::sendFromMessenger(Runtime *runtime, Modifier *sender, const DynamicValue &incomingData) const {
|
||||
if (this->with.getType() == DynamicValueTypes::kIncomingData)
|
||||
sendFromMessengerWithCustomData(runtime, sender, incomingData);
|
||||
else
|
||||
sendFromMessengerWithCustomData(runtime, sender, this->with);
|
||||
}
|
||||
|
||||
void MessengerSendSpec::sendFromMessengerWithCustomData(Runtime *runtime, Modifier *sender, const DynamicValue &data) const {
|
||||
@ -2230,6 +2233,13 @@ const Common::WeakPtr<RuntimeObject>& MessageProperties::getSource() const {
|
||||
return _source;
|
||||
}
|
||||
|
||||
void MessageProperties::setValue(const DynamicValue &value) {
|
||||
if (value.getType() == DynamicValueTypes::kList)
|
||||
_value.setList(value.getList()->clone());
|
||||
else
|
||||
_value = value;
|
||||
}
|
||||
|
||||
WorldManagerInterface::WorldManagerInterface() {
|
||||
}
|
||||
|
||||
@ -5986,6 +5996,10 @@ bool VisualElement::isVisual() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VisualElement::isTextLabel() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VisualElement::isVisible() const {
|
||||
return _visible;
|
||||
}
|
||||
@ -6575,6 +6589,20 @@ bool Modifier::loadTypicalHeader(const Data::TypicalModifierHeader &typicalHeade
|
||||
return true;
|
||||
}
|
||||
|
||||
Structural *Modifier::findStructuralOwner() const {
|
||||
RuntimeObject *scan = _parent.lock().get();
|
||||
while (scan) {
|
||||
if (scan->isModifier())
|
||||
scan = static_cast<Modifier *>(scan)->_parent.lock().get();
|
||||
else if (scan->isStructural())
|
||||
return static_cast<Structural *>(scan);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Modifier::linkInternalReferences(ObjectLinkingScope *scope) {
|
||||
}
|
||||
|
||||
|
@ -1047,7 +1047,7 @@ struct MessengerSendSpec {
|
||||
|
||||
static void resolveVariableObjectType(RuntimeObject *obj, Common::WeakPtr<Structural> &outStructuralDest, Common::WeakPtr<Modifier> &outModifierDest);
|
||||
|
||||
void sendFromMessenger(Runtime *runtime, Modifier *sender) const;
|
||||
void sendFromMessenger(Runtime *runtime, Modifier *sender, const DynamicValue &incomingData) const;
|
||||
void sendFromMessengerWithCustomData(Runtime *runtime, Modifier *sender, const DynamicValue &data) const;
|
||||
|
||||
Event send;
|
||||
@ -1769,6 +1769,8 @@ struct MessageProperties {
|
||||
const DynamicValue &getValue() const;
|
||||
const Common::WeakPtr<RuntimeObject> &getSource() const;
|
||||
|
||||
void setValue(const DynamicValue &value);
|
||||
|
||||
private:
|
||||
Event _evt;
|
||||
DynamicValue _value;
|
||||
@ -2212,6 +2214,7 @@ public:
|
||||
VisualElement();
|
||||
|
||||
bool isVisual() const override;
|
||||
virtual bool isTextLabel() const;
|
||||
|
||||
bool isVisible() const;
|
||||
bool isDirectToScreen() const;
|
||||
|
Loading…
Reference in New Issue
Block a user