/* 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 .
*
*/
#include "common/random.h"
#include "common/config-manager.h"
#include "common/file.h"
#include "common/macresman.h"
#include "common/translation.h"
#include "gui/dialog.h"
#include "gui/imagealbum-dialog.h"
#include "graphics/palette.h"
#include "image/bmp.h"
#include "image/pict.h"
#include "mtropolis/miniscript.h"
#include "mtropolis/plugin/standard.h"
#include "mtropolis/plugins.h"
#include "mtropolis/miniscript.h"
namespace MTropolis {
namespace Standard {
CursorModifier::CursorModifier() : _cursorID(0) {
}
bool CursorModifier::respondsToEvent(const Event &evt) const {
return _applyWhen.respondsTo(evt) || _removeWhen.respondsTo(evt);
}
VThreadState CursorModifier::consumeMessage(Runtime *runtime, const Common::SharedPtr &msg) {
// As with mTropolis, this doesn't support stacking cursors
if (_applyWhen.respondsTo(msg->getEvent())) {
runtime->setModifierCursorOverride(_cursorID);
}
if (_removeWhen.respondsTo(msg->getEvent())) {
// This doesn't call "disable" because the behavior is actually different.
// Disabling a cursor modifier doesn't seem to remove it.
runtime->clearModifierCursorOverride();
}
return kVThreadReturn;
}
void CursorModifier::disable(Runtime *runtime) {
}
bool CursorModifier::load(const PlugInModifierLoaderContext &context, const Data::Standard::CursorModifier &data) {
if (data.applyWhen.type != Data::PlugInTypeTaggedValue::kEvent || data.cursorIDAsLabel.type != Data::PlugInTypeTaggedValue::kLabel)
return false;
if (!_applyWhen.load(data.applyWhen.value.asEvent))
return false;
if (data.haveRemoveWhen) {
if (!_removeWhen.load(data.removeWhen.value.asEvent))
return false;
}
if (data.cursorIDAsLabel.type != Data::PlugInTypeTaggedValue::kLabel)
return false;
_cursorID = data.cursorIDAsLabel.value.asLabel.labelID;
return true;
}
Common::SharedPtr CursorModifier::shallowClone() const {
Common::SharedPtr clone(new CursorModifier(*this));
return clone;
}
const char *CursorModifier::getDefaultName() const {
return "Cursor Modifier";
}
STransCtModifier::STransCtModifier() : _transitionType(0), _transitionDirection(0), _steps(0), _duration(0), _fullScreen(false) {
}
bool STransCtModifier::load(const PlugInModifierLoaderContext &context, const Data::Standard::STransCtModifier &data) {
if (data.enableWhen.type != Data::PlugInTypeTaggedValue::kEvent ||
data.disableWhen.type != Data::PlugInTypeTaggedValue::kEvent ||
data.transitionType.type != Data::PlugInTypeTaggedValue::kInteger ||
data.transitionDirection.type != Data::PlugInTypeTaggedValue::kInteger ||
data.steps.type != Data::PlugInTypeTaggedValue::kInteger ||
data.duration.type != Data::PlugInTypeTaggedValue::kInteger ||
data.fullScreen.type != Data::PlugInTypeTaggedValue::kBoolean)
return false;
if (!_enableWhen.load(data.enableWhen.value.asEvent) || !_disableWhen.load(data.disableWhen.value.asEvent))
return false;
_transitionType = data.transitionType.value.asInt;
_transitionDirection = data.transitionDirection.value.asInt;
_steps = data.steps.value.asInt;
_duration = data.duration.value.asInt;
_fullScreen = data.fullScreen.value.asBoolean;
return true;
}
bool STransCtModifier::respondsToEvent(const Event &evt) const {
return _enableWhen.respondsTo(evt) || _disableWhen.respondsTo(evt);
}
VThreadState STransCtModifier::consumeMessage(Runtime *runtime, const Common::SharedPtr &msg) {
if (_enableWhen.respondsTo(msg->getEvent())) {
SceneTransitionEffect effect;
effect._duration = _duration / 10;
effect._steps = _steps;
if (SceneTransitionTypes::loadFromData(effect._transitionType, _transitionType) && SceneTransitionDirections::loadFromData(effect._transitionDirection, _transitionDirection)) {
// Duration doesn't seem to affect wipe transitions for some reason.
// In Obsidian, this mostly effects 180-degree turns.
// Good place to test this is in the corners of the Bureau library, where it's 0,
// but some cases where it is set (e.g. the Spider control room) have the same duration anyway.
if (effect._transitionType == SceneTransitionTypes::kWipe)
effect._duration = 500;
runtime->setSceneTransitionEffect(false, &effect);
} else {
warning("Source-scene transition had invalid data");
}
}
if (_disableWhen.respondsTo(msg->getEvent()))
disable(runtime);
return kVThreadReturn;
}
void STransCtModifier::disable(Runtime *runtime) {
runtime->setSceneTransitionEffect(false, nullptr);
}
bool STransCtModifier::readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) {
if (attrib == "rate") {
if (_duration <= (kMaxDuration / 100))
result.setInt(100);
else if (_duration >= kMaxDuration)
result.setInt(1);
else
result.setInt((kMaxDuration + (_duration / 2)) / _duration);
return true;
} else if (attrib == "steps") {
result.setInt(_steps);
return true;
}
return Modifier::readAttribute(thread, result, attrib);
}
MiniscriptInstructionOutcome STransCtModifier::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
if (attrib == "rate") {
DynamicValueWriteFuncHelper::create(this, result);
return kMiniscriptInstructionOutcomeContinue;
} else if (attrib == "steps") {
DynamicValueWriteFuncHelper::create(this, result);
return kMiniscriptInstructionOutcomeContinue;
}
return Modifier::writeRefAttribute(thread, result, attrib);
}
Common::SharedPtr STransCtModifier::shallowClone() const {
return Common::SharedPtr(new STransCtModifier(*this));
}
const char *STransCtModifier::getDefaultName() const {
return "STransCt"; // Probably wrong
}
MiniscriptInstructionOutcome STransCtModifier::scriptSetRate(MiniscriptThread *thread, const DynamicValue &value) {
int32 asInteger = 0;
if (!value.roundToInt(asInteger))
return kMiniscriptInstructionOutcomeFailed;
if (asInteger < 1)
asInteger = 1;
else if (asInteger > 100)
asInteger = 100;
if (asInteger == 100)
_duration = 0;
else
_duration = kMaxDuration / asInteger;
return kMiniscriptInstructionOutcomeContinue;
}
MiniscriptInstructionOutcome STransCtModifier::scriptSetSteps(MiniscriptThread *thread, const DynamicValue &value) {
int32 asInteger = 0;
if (!value.roundToInt(asInteger))
return kMiniscriptInstructionOutcomeFailed;
if (asInteger < 4)
asInteger = 4;
else if (asInteger > 256)
asInteger = 100;
_steps = asInteger;
return kMiniscriptInstructionOutcomeContinue;
}
MediaCueMessengerModifier::CueSourceUnion::CueSourceUnion() : asUnset(0) {
}
MediaCueMessengerModifier::CueSourceUnion::~CueSourceUnion() {
}
template
void MediaCueMessengerModifier::CueSourceUnion::construct(const T &value) {
T *field = &(this->*TMember);
new (field) T(value);
}
template
void MediaCueMessengerModifier::CueSourceUnion::destruct() {
T *field = &(this->*TMember);
field->~T();
}
MediaCueMessengerModifier::MediaCueMessengerModifier() : _isActive(false), _cueSourceType(kCueSourceInvalid) {
_mediaCue.sourceModifier = this;
}
MediaCueMessengerModifier::MediaCueMessengerModifier(const MediaCueMessengerModifier &other)
: Modifier(other), _cueSourceType(other._cueSourceType), _cueSourceModifier(other._cueSourceModifier), _enableWhen(other._enableWhen), _disableWhen(other._disableWhen), _mediaCue(other._mediaCue), _isActive(other._isActive) {
_cueSource.destruct();
switch (_cueSourceType) {
case kCueSourceInteger:
_cueSource.construct(other._cueSource.asInt);
break;
case kCueSourceIntegerRange:
_cueSource.construct(other._cueSource.asIntRange);
break;
case kCueSourceVariableReference:
_cueSource.construct(other._cueSource.asVarRefGUID);
break;
case kCueSourceLabel:
_cueSource.construct