mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-13 21:31:53 +00:00
MTROPOLIS: Implement Miniscript global references and MTI Shanghai minigame
This commit is contained in:
parent
89c8f002c0
commit
07726745d4
@ -28,6 +28,35 @@
|
||||
|
||||
namespace MTropolis {
|
||||
|
||||
class MiniscriptInstructionParserFeedback : public IMiniscriptInstructionParserFeedback {
|
||||
public:
|
||||
explicit MiniscriptInstructionParserFeedback(Common::Array<MiniscriptReferences::GlobalRef> *globalRefs);
|
||||
|
||||
uint registerGlobalGUIDIndex(uint32 guid) override;
|
||||
|
||||
private:
|
||||
Common::Array<MiniscriptReferences::GlobalRef> *_globalRefs;
|
||||
};
|
||||
|
||||
MiniscriptInstructionParserFeedback::MiniscriptInstructionParserFeedback(Common::Array<MiniscriptReferences::GlobalRef> *globalRefs) : _globalRefs(globalRefs) {
|
||||
}
|
||||
|
||||
uint MiniscriptInstructionParserFeedback::registerGlobalGUIDIndex(uint32 guid) {
|
||||
for (uint i = 0; i < _globalRefs->size(); i++) {
|
||||
if ((*_globalRefs)[i].guid == guid)
|
||||
return i;
|
||||
}
|
||||
|
||||
uint newIndex = _globalRefs->size();
|
||||
|
||||
MiniscriptReferences::GlobalRef globalRef;
|
||||
globalRef.guid = guid;
|
||||
|
||||
_globalRefs->push_back(globalRef);
|
||||
|
||||
return newIndex;
|
||||
}
|
||||
|
||||
bool miniscriptEvaluateTruth(const DynamicValue &value) {
|
||||
// NOTE: Comparing equal to "true" only passes for 1 exactly, but for conditions,
|
||||
// any non-zero value is true.
|
||||
@ -45,21 +74,29 @@ bool miniscriptEvaluateTruth(const DynamicValue &value) {
|
||||
}
|
||||
}
|
||||
|
||||
IMiniscriptInstructionParserFeedback::~IMiniscriptInstructionParserFeedback() {
|
||||
}
|
||||
|
||||
MiniscriptInstruction::~MiniscriptInstruction() {
|
||||
}
|
||||
|
||||
MiniscriptReferences::LocalRef::LocalRef() : guid(0) {
|
||||
}
|
||||
|
||||
MiniscriptReferences::MiniscriptReferences(const Common::Array<LocalRef> &localRefs) : _localRefs(localRefs) {
|
||||
MiniscriptReferences::GlobalRef::GlobalRef() : guid(0) {
|
||||
}
|
||||
|
||||
MiniscriptReferences::MiniscriptReferences(const Common::Array<LocalRef> &localRefs, const Common::Array<GlobalRef> &globalRefs) : _localRefs(localRefs), _globalRefs(globalRefs) {
|
||||
}
|
||||
|
||||
void MiniscriptReferences::linkInternalReferences(ObjectLinkingScope *scope) {
|
||||
// Resolve using name lookups since there are some known cases where the GUID is broken
|
||||
// e.g. "bArriveFromCutScene" in "Set bArriveFromCutScene on PE" in Obsidian
|
||||
for (Common::Array<LocalRef>::iterator it = _localRefs.begin(), itEnd = _localRefs.end(); it != itEnd; ++it) {
|
||||
for (Common::Array<LocalRef>::iterator it = _localRefs.begin(), itEnd = _localRefs.end(); it != itEnd; ++it)
|
||||
it->resolution = scope->resolve(it->guid, it->name, false);
|
||||
}
|
||||
|
||||
for (Common::Array<GlobalRef>::iterator it = _globalRefs.begin(), itEnd = _globalRefs.end(); it != itEnd; ++it)
|
||||
it->resolution = scope->resolve(it->guid, "", true);
|
||||
}
|
||||
|
||||
void MiniscriptReferences::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
|
||||
@ -77,6 +114,21 @@ void MiniscriptReferences::visitInternalReferences(IStructuralReferenceVisitor *
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (GlobalRef &ref : _globalRefs) {
|
||||
Common::SharedPtr<RuntimeObject> obj = ref.resolution.lock();
|
||||
if (obj) {
|
||||
if (obj->isModifier()) {
|
||||
Common::WeakPtr<Modifier> mod = obj.staticCast<Modifier>();
|
||||
visitor->visitWeakModifierRef(mod);
|
||||
ref.resolution = mod;
|
||||
} else if (obj->isStructural()) {
|
||||
Common::WeakPtr<Structural> struc = obj.staticCast<Structural>();
|
||||
visitor->visitWeakStructuralRef(struc);
|
||||
ref.resolution = struc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Common::WeakPtr<RuntimeObject> MiniscriptReferences::getRefByIndex(uint index) const {
|
||||
@ -85,6 +137,13 @@ Common::WeakPtr<RuntimeObject> MiniscriptReferences::getRefByIndex(uint index) c
|
||||
return _localRefs[index].resolution;
|
||||
}
|
||||
|
||||
Common::WeakPtr<RuntimeObject> MiniscriptReferences::getGlobalRefByIndex(uint index) const {
|
||||
if (index >= _globalRefs.size())
|
||||
return Common::WeakPtr<RuntimeObject>();
|
||||
return _globalRefs[index].resolution;
|
||||
|
||||
}
|
||||
|
||||
MiniscriptProgram::MiniscriptProgram(const Common::SharedPtr<Common::Array<uint8> > &programData, const Common::Array<MiniscriptInstruction *> &instructions, const Common::Array<Attribute> &attributes)
|
||||
: _programData(programData), _instructions(instructions), _attributes(attributes) {
|
||||
}
|
||||
@ -105,18 +164,18 @@ const Common::Array<MiniscriptProgram::Attribute> &MiniscriptProgram::getAttribu
|
||||
|
||||
template<class T>
|
||||
struct MiniscriptInstructionLoader {
|
||||
static bool loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader);
|
||||
static bool loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, IMiniscriptInstructionParserFeedback &feedback);
|
||||
};
|
||||
|
||||
template<class T>
|
||||
bool MiniscriptInstructionLoader<T>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader) {
|
||||
bool MiniscriptInstructionLoader<T>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, IMiniscriptInstructionParserFeedback &feedback) {
|
||||
// Default loader for simple instructions with no private data
|
||||
new (dest) T();
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool MiniscriptInstructionLoader<MiniscriptInstructions::Send>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader) {
|
||||
bool MiniscriptInstructionLoader<MiniscriptInstructions::Send>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, IMiniscriptInstructionParserFeedback &feedback) {
|
||||
Data::Event dataEvent;
|
||||
if (!dataEvent.load(instrDataReader))
|
||||
return false;
|
||||
@ -135,7 +194,7 @@ bool MiniscriptInstructionLoader<MiniscriptInstructions::Send>::loadInstruction(
|
||||
}
|
||||
|
||||
template<>
|
||||
bool MiniscriptInstructionLoader<MiniscriptInstructions::BuiltinFunc>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader) {
|
||||
bool MiniscriptInstructionLoader<MiniscriptInstructions::BuiltinFunc>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, IMiniscriptInstructionParserFeedback &feedback) {
|
||||
uint32 functionID;
|
||||
if (!instrDataReader.readU32(functionID))
|
||||
return false;
|
||||
@ -148,7 +207,7 @@ bool MiniscriptInstructionLoader<MiniscriptInstructions::BuiltinFunc>::loadInstr
|
||||
}
|
||||
|
||||
template<>
|
||||
bool MiniscriptInstructionLoader<MiniscriptInstructions::GetChild>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader) {
|
||||
bool MiniscriptInstructionLoader<MiniscriptInstructions::GetChild>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, IMiniscriptInstructionParserFeedback &feedback) {
|
||||
uint32 childAttribute;
|
||||
if (!instrDataReader.readU32(childAttribute))
|
||||
return false;
|
||||
@ -158,7 +217,7 @@ bool MiniscriptInstructionLoader<MiniscriptInstructions::GetChild>::loadInstruct
|
||||
}
|
||||
|
||||
template<>
|
||||
bool MiniscriptInstructionLoader<MiniscriptInstructions::PushGlobal>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader) {
|
||||
bool MiniscriptInstructionLoader<MiniscriptInstructions::PushGlobal>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, IMiniscriptInstructionParserFeedback &feedback) {
|
||||
uint32 globalID;
|
||||
if (!instrDataReader.readU32(globalID))
|
||||
return false;
|
||||
@ -168,7 +227,7 @@ bool MiniscriptInstructionLoader<MiniscriptInstructions::PushGlobal>::loadInstru
|
||||
}
|
||||
|
||||
template<>
|
||||
bool MiniscriptInstructionLoader<MiniscriptInstructions::Jump>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader) {
|
||||
bool MiniscriptInstructionLoader<MiniscriptInstructions::Jump>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, IMiniscriptInstructionParserFeedback &feedback) {
|
||||
uint32 jumpFlags, unknown, instrOffset;
|
||||
if (!instrDataReader.readU32(jumpFlags) || !instrDataReader.readU32(unknown) || !instrDataReader.readU32(instrOffset))
|
||||
return false;
|
||||
@ -185,7 +244,7 @@ bool MiniscriptInstructionLoader<MiniscriptInstructions::Jump>::loadInstruction(
|
||||
}
|
||||
|
||||
template<>
|
||||
bool MiniscriptInstructionLoader<MiniscriptInstructions::PushValue>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader) {
|
||||
bool MiniscriptInstructionLoader<MiniscriptInstructions::PushValue>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, IMiniscriptInstructionParserFeedback &feedback) {
|
||||
uint16 dataType;
|
||||
if (!instrDataReader.readU16(dataType))
|
||||
return false;
|
||||
@ -217,7 +276,9 @@ bool MiniscriptInstructionLoader<MiniscriptInstructions::PushValue>::loadInstruc
|
||||
if (!instrDataReader.readU32(refValue))
|
||||
return false;
|
||||
|
||||
new (dest) MiniscriptInstructions::PushValue(MiniscriptInstructions::PushValue::kDataTypeGlobalRef, &refValue, (instrFlags & 1) != 0);
|
||||
uint32 indexedRef = feedback.registerGlobalGUIDIndex(refValue);
|
||||
|
||||
new (dest) MiniscriptInstructions::PushValue(MiniscriptInstructions::PushValue::kDataTypeGlobalRef, &indexedRef, (instrFlags & 1) != 0);
|
||||
} else if (dataType == 0x1d) {
|
||||
MiniscriptInstructions::PushValue::Label label;
|
||||
if (!instrDataReader.readU32(label.superGroup) || !instrDataReader.readU32(label.id))
|
||||
@ -231,7 +292,7 @@ bool MiniscriptInstructionLoader<MiniscriptInstructions::PushValue>::loadInstruc
|
||||
}
|
||||
|
||||
template<>
|
||||
bool MiniscriptInstructionLoader<MiniscriptInstructions::PushString>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader) {
|
||||
bool MiniscriptInstructionLoader<MiniscriptInstructions::PushString>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, IMiniscriptInstructionParserFeedback &feedback) {
|
||||
uint16 strLength;
|
||||
if (!instrDataReader.readU16(strLength))
|
||||
return false;
|
||||
@ -247,14 +308,14 @@ bool MiniscriptInstructionLoader<MiniscriptInstructions::PushString>::loadInstru
|
||||
}
|
||||
|
||||
struct SIMiniscriptInstructionFactory {
|
||||
bool (*create)(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, MiniscriptInstruction *&outMiniscriptInstructionPtr);
|
||||
bool (*create)(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, MiniscriptInstruction *&outMiniscriptInstructionPtr, IMiniscriptInstructionParserFeedback &feedback);
|
||||
void (*getSizeAndAlignment)(size_t &outSize, size_t &outAlignment);
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class MiniscriptInstructionFactory {
|
||||
public:
|
||||
static bool create(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, MiniscriptInstruction *&outMiniscriptInstructionPtr);
|
||||
static bool create(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, MiniscriptInstruction *&outMiniscriptInstructionPtr, IMiniscriptInstructionParserFeedback &feedback);
|
||||
static void getSizeAndAlignment(size_t &outSize, size_t &outAlignment);
|
||||
|
||||
static SIMiniscriptInstructionFactory *getInstance();
|
||||
@ -264,8 +325,8 @@ private:
|
||||
};
|
||||
|
||||
template<class T>
|
||||
bool MiniscriptInstructionFactory<T>::create(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, MiniscriptInstruction *&outMiniscriptInstructionPtr) {
|
||||
if (!MiniscriptInstructionLoader<T>::loadInstruction(dest, instrFlags, instrDataReader))
|
||||
bool MiniscriptInstructionFactory<T>::create(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, MiniscriptInstruction *&outMiniscriptInstructionPtr, IMiniscriptInstructionParserFeedback &feedback) {
|
||||
if (!MiniscriptInstructionLoader<T>::loadInstruction(dest, instrFlags, instrDataReader, feedback))
|
||||
return false;
|
||||
|
||||
outMiniscriptInstructionPtr = static_cast<MiniscriptInstruction *>(static_cast<T *>(dest));
|
||||
@ -295,6 +356,7 @@ MiniscriptParser::InstructionData::InstructionData()
|
||||
|
||||
bool MiniscriptParser::parse(const Data::MiniscriptProgram &program, Common::SharedPtr<MiniscriptProgram> &outProgram, Common::SharedPtr<MiniscriptReferences> &outReferences) {
|
||||
Common::Array<MiniscriptReferences::LocalRef> localRefs;
|
||||
Common::Array<MiniscriptReferences::GlobalRef> globalRefs;
|
||||
Common::Array<MiniscriptProgram::Attribute> attributes;
|
||||
Common::SharedPtr<Common::Array<uint8> > programDataPtr;
|
||||
Common::Array<MiniscriptInstruction *> miniscriptInstructions;
|
||||
@ -302,7 +364,7 @@ bool MiniscriptParser::parse(const Data::MiniscriptProgram &program, Common::Sha
|
||||
// If the program is empty then just return an empty program
|
||||
if (program.bytecode.size() == 0 || program.numOfInstructions == 0) {
|
||||
outProgram = Common::SharedPtr<MiniscriptProgram>(new MiniscriptProgram(programDataPtr, miniscriptInstructions, attributes));
|
||||
outReferences = Common::SharedPtr<MiniscriptReferences>(new MiniscriptReferences(localRefs));
|
||||
outReferences = Common::SharedPtr<MiniscriptReferences>(new MiniscriptReferences(localRefs, globalRefs));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -377,6 +439,8 @@ bool MiniscriptParser::parse(const Data::MiniscriptProgram &program, Common::Sha
|
||||
|
||||
miniscriptInstructions.resize(program.numOfInstructions);
|
||||
|
||||
MiniscriptInstructionParserFeedback parserFeedback(&globalRefs);
|
||||
|
||||
// Create instructions
|
||||
for (size_t i = 0; i < program.numOfInstructions; i++) {
|
||||
const InstructionData &rawInstruction = rawInstructions[i];
|
||||
@ -388,7 +452,7 @@ bool MiniscriptParser::parse(const Data::MiniscriptProgram &program, Common::Sha
|
||||
Common::MemoryReadStreamEndian instrContentsStream(static_cast<const byte *>(dataLoc), rawInstruction.contents.size(), reader.isBigEndian());
|
||||
Data::DataReader instrContentsReader(0, instrContentsStream, reader.getProjectFormat());
|
||||
|
||||
if (!rawInstruction.instrFactory->create(&programData[baseOffset + rawInstruction.pdPosition], rawInstruction.flags, instrContentsReader, miniscriptInstructions[i])) {
|
||||
if (!rawInstruction.instrFactory->create(&programData[baseOffset + rawInstruction.pdPosition], rawInstruction.flags, instrContentsReader, miniscriptInstructions[i], parserFeedback)) {
|
||||
// Destroy any already-created instructions
|
||||
for (size_t di = 0; di < i; di++) {
|
||||
miniscriptInstructions[i - 1 - di]->~MiniscriptInstruction();
|
||||
@ -400,7 +464,7 @@ bool MiniscriptParser::parse(const Data::MiniscriptProgram &program, Common::Sha
|
||||
|
||||
// Done
|
||||
outProgram = Common::SharedPtr<MiniscriptProgram>(new MiniscriptProgram(programDataPtr, miniscriptInstructions, attributes));
|
||||
outReferences = Common::SharedPtr<MiniscriptReferences>(new MiniscriptReferences(localRefs));
|
||||
outReferences = Common::SharedPtr<MiniscriptReferences>(new MiniscriptReferences(localRefs, globalRefs));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1662,8 +1726,8 @@ MiniscriptInstructionOutcome PushValue::execute(MiniscriptThread *thread) const
|
||||
value.setObject(ObjectReference(thread->getRefs()->getRefByIndex(_value.ref)));
|
||||
break;
|
||||
case DataType::kDataTypeGlobalRef:
|
||||
thread->error("Global references are not implemented");
|
||||
return kMiniscriptInstructionOutcomeFailed;
|
||||
value.setObject(ObjectReference(thread->getRefs()->getGlobalRefByIndex(_value.ref)));
|
||||
break;
|
||||
case DataType::kDataTypeLabel: {
|
||||
MTropolis::Label label;
|
||||
label.id = _value.lbl.id;
|
||||
|
@ -40,6 +40,13 @@ public:
|
||||
virtual MiniscriptInstructionOutcome execute(MiniscriptThread *thread) const = 0;
|
||||
};
|
||||
|
||||
class IMiniscriptInstructionParserFeedback {
|
||||
public:
|
||||
virtual ~IMiniscriptInstructionParserFeedback();
|
||||
|
||||
virtual uint registerGlobalGUIDIndex(uint32 guid) = 0;
|
||||
};
|
||||
|
||||
class MiniscriptReferences {
|
||||
public:
|
||||
struct LocalRef {
|
||||
@ -50,15 +57,24 @@ public:
|
||||
Common::WeakPtr<RuntimeObject> resolution;
|
||||
};
|
||||
|
||||
explicit MiniscriptReferences(const Common::Array<LocalRef> &localRefs);
|
||||
struct GlobalRef {
|
||||
GlobalRef();
|
||||
|
||||
uint32 guid;
|
||||
Common::WeakPtr<RuntimeObject> resolution;
|
||||
};
|
||||
|
||||
explicit MiniscriptReferences(const Common::Array<LocalRef> &localRefs, const Common::Array<GlobalRef> &globalRefs);
|
||||
|
||||
void linkInternalReferences(ObjectLinkingScope *scope);
|
||||
void visitInternalReferences(IStructuralReferenceVisitor *visitor);
|
||||
|
||||
Common::WeakPtr<RuntimeObject> getRefByIndex(uint index) const;
|
||||
Common::WeakPtr<RuntimeObject> getGlobalRefByIndex(uint index) const;
|
||||
|
||||
private:
|
||||
Common::Array<LocalRef> _localRefs;
|
||||
Common::Array<GlobalRef> _globalRefs;
|
||||
|
||||
};
|
||||
|
||||
|
@ -26,21 +26,147 @@
|
||||
|
||||
#include "mtropolis/miniscript.h"
|
||||
|
||||
#include "common/random.h"
|
||||
|
||||
namespace MTropolis {
|
||||
|
||||
namespace MTI {
|
||||
|
||||
|
||||
/*
|
||||
Board layout:
|
||||
|
||||
Layer 0:
|
||||
0 1 2 3 4 5 6 7 8 9 10 11 12
|
||||
+-----+-----+-----+-----+-----+-----+
|
||||
0 + 0 | 1 | 2 | 3 | 4 | 5 |
|
||||
1 +-----+-----+-----+-----+-----+-----+
|
||||
2 +-----+ 7 | 8 | 9 | 10 +-----+-----+
|
||||
3 | 6 +-----+-----+-----+-----+ 11 | 12 |
|
||||
4 +-----+ 13 | 14 | 15 | 16 +-----+-----+
|
||||
5 +-----+-----+-----+-----+-----+-----+
|
||||
6 + 17 | 18 | 19 | 20 | 21 | 22 |
|
||||
+-----+-----+-----+-----+-----+-----+
|
||||
|
||||
Layer 1:
|
||||
0 1 2 3 4 5 6 7 8 9 10 11 12
|
||||
|
||||
0
|
||||
1 +-----+-----+
|
||||
2 | 23 | 24 |
|
||||
3 +-----+-----+
|
||||
4 | 25 | 26 |
|
||||
5 +-----+-----+
|
||||
6
|
||||
|
||||
Layer 2:
|
||||
0 1 2 3 4 5 6 7 8 9 10 11 12
|
||||
|
||||
0
|
||||
1
|
||||
2 +-----+
|
||||
3 | 27 |
|
||||
4 +-----+
|
||||
5
|
||||
6
|
||||
|
||||
*/
|
||||
|
||||
ShanghaiModifier::TileCoordinate ShanghaiModifier::_tileCoordinates[ShanghaiModifier::kNumTiles] = {
|
||||
{0, 0, 0},
|
||||
{2, 0, 0},
|
||||
{4, 0, 0},
|
||||
{6, 0, 0},
|
||||
{8, 0, 0},
|
||||
{10, 0, 0},
|
||||
|
||||
{0, 3, 0},
|
||||
{2, 2, 0},
|
||||
{4, 2, 0},
|
||||
{6, 2, 0},
|
||||
{8, 2, 0},
|
||||
{10, 3, 0},
|
||||
{12, 3, 0},
|
||||
|
||||
{2, 4, 0},
|
||||
{4, 4, 0},
|
||||
{6, 4, 0},
|
||||
{8, 4, 0},
|
||||
|
||||
{0, 6, 0},
|
||||
{2, 6, 0},
|
||||
{4, 6, 0},
|
||||
{6, 6, 0},
|
||||
{8, 6, 0},
|
||||
{10, 6, 0},
|
||||
|
||||
{4, 2, 0},
|
||||
{6, 2, 0},
|
||||
|
||||
{4, 4, 1},
|
||||
{6, 4, 1},
|
||||
|
||||
{5, 3, 2},
|
||||
};
|
||||
|
||||
ShanghaiModifier::ShanghaiModifier() {
|
||||
for (uint x = 0; x < kBoardSizeX; x++)
|
||||
for (uint y = 0; y < kBoardSizeY; y++)
|
||||
for (uint z = 0; z < kBoardSizeZ; z++)
|
||||
_tileAtCoordinate[x][y][z] = -1;
|
||||
|
||||
for (uint i = 0; i < kNumTiles; i++) {
|
||||
const TileCoordinate &coord = _tileCoordinates[i];
|
||||
assert(coord.x < kBoardSizeX);
|
||||
assert(coord.y < kBoardSizeY);
|
||||
assert(coord.z < kBoardSizeZ);
|
||||
_tileAtCoordinate[coord.x][coord.y][coord.z] = i;
|
||||
}
|
||||
}
|
||||
|
||||
ShanghaiModifier::~ShanghaiModifier() {
|
||||
}
|
||||
|
||||
bool ShanghaiModifier::respondsToEvent(const Event &evt) const {
|
||||
if (_resetTileSetWhen.respondsTo(evt))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
VThreadState ShanghaiModifier::consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) {
|
||||
if (_resetTileSetWhen.respondsTo(msg->getEvent())) {
|
||||
uint tileFaces[kNumTiles];
|
||||
|
||||
resetTiles(*runtime->getRandom(), tileFaces);
|
||||
|
||||
Modifier *varMod = this->_tileSetRef.resolution.lock().get();
|
||||
|
||||
if (varMod == nullptr || !varMod->isVariable()) {
|
||||
warning("Shanghai reset var ref was unavailable");
|
||||
return kVThreadError;
|
||||
}
|
||||
|
||||
VariableModifier *var = static_cast<VariableModifier *>(varMod);
|
||||
|
||||
Common::SharedPtr<DynamicList> list(new DynamicList());
|
||||
|
||||
for (uint i = 0; i < kNumTiles; i++) {
|
||||
DynamicValue tileValue;
|
||||
tileValue.setInt(tileFaces[i]);
|
||||
|
||||
list->setAtIndex(i, tileValue);
|
||||
}
|
||||
|
||||
DynamicValue listValue;
|
||||
listValue.setList(list);
|
||||
|
||||
MiniscriptThread thread(runtime, nullptr, nullptr, nullptr, this);
|
||||
var->varSetValue(&thread, listValue);
|
||||
|
||||
return kVThreadReturn;
|
||||
}
|
||||
|
||||
return kVThreadReturn;
|
||||
}
|
||||
|
||||
@ -48,9 +174,217 @@ void ShanghaiModifier::disable(Runtime *runtime) {
|
||||
}
|
||||
|
||||
bool ShanghaiModifier::load(const PlugInModifierLoaderContext &context, const Data::MTI::ShanghaiModifier &data) {
|
||||
if (data.resetWhen.type != Data::PlugInTypeTaggedValue::kEvent)
|
||||
return false;
|
||||
|
||||
if (!_resetTileSetWhen.load(data.resetWhen.value.asEvent))
|
||||
return false;
|
||||
|
||||
if (data.tileSetVar.type != Data::PlugInTypeTaggedValue::kVariableReference)
|
||||
return false;
|
||||
|
||||
_tileSetRef = VarReference(data.tileSetVar.value.asVarRefGUID, "");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShanghaiModifier::linkInternalReferences(ObjectLinkingScope *scope) {
|
||||
_tileSetRef.linkInternalReferences(scope);
|
||||
}
|
||||
|
||||
void ShanghaiModifier::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
|
||||
_tileSetRef.visitInternalReferences(visitor);
|
||||
}
|
||||
|
||||
void ShanghaiModifier::resetTiles(Common::RandomSource &rng, uint (&tileFaces)[kNumTiles]) const {
|
||||
uint possibleFaces[kNumFaces];
|
||||
uint numPossibleFaces = kNumFaces;
|
||||
|
||||
for (uint i = 0; i < kNumFaces; i++)
|
||||
possibleFaces[i] = i + 1;
|
||||
|
||||
uint facesToInsert[kNumTiles / 2];
|
||||
uint numFacesToInsert = kNumTiles / 2;
|
||||
|
||||
// Pick random faces, each one gets inserted twice
|
||||
for (uint i = 0; i < kNumTiles / 4u; i++) {
|
||||
uint faceToInsert = selectAndRemoveOne(rng, possibleFaces, numPossibleFaces);
|
||||
facesToInsert[i * 2 + 0] = faceToInsert;
|
||||
facesToInsert[i * 2 + 1] = faceToInsert;
|
||||
}
|
||||
|
||||
// We build the board by adding all tiles and then randomly picking 2 exposed tiles and
|
||||
// assigning them a matching pair. A pair is only valid if the resulting board state has
|
||||
// valid moves.
|
||||
BoardState_t boardState = emptyBoardState();
|
||||
for (uint i = 0; i < kNumTiles; i++)
|
||||
boardState = boardState | boardStateBit(i);
|
||||
|
||||
for (uint pair = 0; pair < kNumTiles / 2u; pair++) {
|
||||
uint exposedTiles[kNumTiles];
|
||||
uint numExposedTiles = 0;
|
||||
|
||||
for (uint i = 0; i < kNumTiles; i++) {
|
||||
if (boardState & boardStateBit(i)) {
|
||||
if (tileIsExposed(boardState, i))
|
||||
exposedTiles[numExposedTiles++] = i;
|
||||
}
|
||||
}
|
||||
|
||||
uint firstExposedTile = selectAndRemoveOne(rng, exposedTiles, numExposedTiles);
|
||||
|
||||
BoardState_t withFirstRemoved = boardState ^ boardStateBit(firstExposedTile);
|
||||
|
||||
uint secondExposedTile = selectAndRemoveOne(rng, exposedTiles, numExposedTiles);
|
||||
BoardState_t withBothRemoved = withFirstRemoved ^ boardStateBit(secondExposedTile);
|
||||
|
||||
if (numExposedTiles > 0) {
|
||||
// If this isn't the last move, validate that this won't result in a stuck board state (e.g. only one tile exposed)
|
||||
// If it would result in such a state, pick a different move.
|
||||
for (;;) {
|
||||
if (boardStateHasValidMove(withBothRemoved))
|
||||
break;
|
||||
|
||||
if (numExposedTiles == 0) {
|
||||
error("Shanghai board creation failed, board state was %x, removed %u to produce board state %x", static_cast<uint>(boardState), firstExposedTile, static_cast<uint>(withFirstRemoved));
|
||||
break;
|
||||
}
|
||||
|
||||
secondExposedTile = selectAndRemoveOne(rng, exposedTiles, numExposedTiles);
|
||||
withBothRemoved = withFirstRemoved ^ boardStateBit(secondExposedTile);
|
||||
}
|
||||
}
|
||||
|
||||
boardState = withBothRemoved;
|
||||
|
||||
uint faceToInsert = selectAndRemoveOne(rng, facesToInsert, numFacesToInsert);
|
||||
tileFaces[firstExposedTile] = faceToInsert;
|
||||
tileFaces[secondExposedTile] = faceToInsert;
|
||||
}
|
||||
}
|
||||
|
||||
uint ShanghaiModifier::selectAndRemoveOne(Common::RandomSource &rng, uint *valuesList, uint &listSize) {
|
||||
if (listSize == 0) {
|
||||
error("Internal error: selectAndRemoveOne ran out of values");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (listSize == 1) {
|
||||
listSize = 0;
|
||||
return valuesList[0];
|
||||
}
|
||||
|
||||
uint selectedIndex = rng.getRandomNumber(listSize - 1);
|
||||
uint selectedValue = valuesList[selectedIndex];
|
||||
|
||||
valuesList[selectedIndex] = valuesList[listSize - 1];
|
||||
listSize--;
|
||||
|
||||
return selectedValue;
|
||||
}
|
||||
|
||||
bool ShanghaiModifier::boardStateHasValidMove(BoardState_t boardState) const {
|
||||
uint numExposedTiles = 0;
|
||||
for (uint i = 0; i < kNumTiles; i++) {
|
||||
if (boardState & boardStateBit(i)) {
|
||||
if (tileIsExposed(boardState, i)) {
|
||||
numExposedTiles++;
|
||||
if (numExposedTiles == 2)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShanghaiModifier::tileIsExposed(BoardState_t boardState, uint tile) const {
|
||||
uint tileX = _tileCoordinates[tile].x;
|
||||
uint tileY = _tileCoordinates[tile].y;
|
||||
uint tileZ = _tileCoordinates[tile].z;
|
||||
|
||||
uint blockMinY = tileY;
|
||||
uint blockMaxY = tileY;
|
||||
if (blockMinY > 0)
|
||||
blockMinY--;
|
||||
if (blockMaxY < kBoardSizeY - 1u)
|
||||
blockMaxY++;
|
||||
|
||||
bool blockedOnLeft = false;
|
||||
|
||||
if (tileX >= 2) {
|
||||
// Check for left-side blocks
|
||||
for (uint y = blockMinY; y <= blockMaxY; y++) {
|
||||
if (tileExistsAtCoordinate(boardState, tileX - 2, y, tileZ)) {
|
||||
blockedOnLeft = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (blockedOnLeft) {
|
||||
bool blockedOnRight = false;
|
||||
|
||||
// Check for right-side blocks
|
||||
if (tileX < kBoardSizeX - 2u) {
|
||||
for (uint y = blockMinY; y <= blockMaxY; y++) {
|
||||
if (tileExistsAtCoordinate(boardState, tileX + 2, y, tileZ)) {
|
||||
blockedOnRight = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tile is blocked on left and right
|
||||
if (blockedOnRight)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check upper blocks
|
||||
uint blockMinX = tileX;
|
||||
uint blockMaxX = tileX;
|
||||
if (blockMinX > 0)
|
||||
blockMinX--;
|
||||
if (blockMaxX < kBoardSizeX - 1u)
|
||||
blockMaxX++;
|
||||
|
||||
for (uint z = tileZ + 1; z < kBoardSizeZ; z++) {
|
||||
for (uint x = blockMinX; x <= blockMaxX; x++) {
|
||||
for (uint y = blockMinY; y <= blockMaxY; y++) {
|
||||
if (tileExistsAtCoordinate(boardState, x, y, z))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShanghaiModifier::tileExistsAtCoordinate(BoardState_t boardState, uint x, uint y, uint z) const {
|
||||
assert(x < kBoardSizeX);
|
||||
assert(y < kBoardSizeY);
|
||||
assert(z < kBoardSizeZ);
|
||||
|
||||
int8 tile = _tileAtCoordinate[x][y][z];
|
||||
|
||||
if (tile < 0)
|
||||
return false;
|
||||
|
||||
if (boardState & boardStateBit(static_cast<uint>(tile)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ShanghaiModifier::BoardState_t ShanghaiModifier::boardStateBit(uint bit) {
|
||||
return static_cast<BoardState_t>(1) << bit;
|
||||
}
|
||||
|
||||
ShanghaiModifier::BoardState_t ShanghaiModifier::emptyBoardState() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MTROPOLIS_DEBUG_ENABLE
|
||||
void ShanghaiModifier::debugInspect(IDebugInspectionReport *report) const {
|
||||
}
|
||||
@ -64,7 +398,6 @@ const char *ShanghaiModifier::getDefaultName() const {
|
||||
return "Shanghai Modifier"; // ???
|
||||
}
|
||||
|
||||
|
||||
PrintModifier::PrintModifier() {
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,12 @@
|
||||
#include "mtropolis/plugin/mti_data.h"
|
||||
#include "mtropolis/runtime.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
class RandomSource;
|
||||
|
||||
} // End of namespace Common
|
||||
|
||||
namespace MTropolis {
|
||||
|
||||
namespace MTI {
|
||||
@ -44,14 +50,47 @@ public:
|
||||
|
||||
bool load(const PlugInModifierLoaderContext &context, const Data::MTI::ShanghaiModifier &data);
|
||||
|
||||
void linkInternalReferences(ObjectLinkingScope *scope) override;
|
||||
void visitInternalReferences(IStructuralReferenceVisitor *visitor) override;
|
||||
|
||||
#ifdef MTROPOLIS_DEBUG_ENABLE
|
||||
const char *debugGetTypeName() const override { return "Shanghai Modifier"; }
|
||||
void debugInspect(IDebugInspectionReport *report) const override;
|
||||
#endif
|
||||
|
||||
private:
|
||||
static const uint kNumTiles = 28;
|
||||
static const uint kNumFaces = 26;
|
||||
|
||||
typedef uint32 BoardState_t;
|
||||
|
||||
static const uint kBoardSizeX = 13;
|
||||
static const uint kBoardSizeY = 7;
|
||||
static const uint kBoardSizeZ = 3;
|
||||
|
||||
struct TileCoordinate {
|
||||
uint x;
|
||||
uint y;
|
||||
uint z;
|
||||
};
|
||||
|
||||
Common::SharedPtr<Modifier> shallowClone() const override;
|
||||
const char *getDefaultName() const override;
|
||||
|
||||
void resetTiles(Common::RandomSource &rng, uint (&tileFaces)[kNumTiles]) const;
|
||||
static uint selectAndRemoveOne(Common::RandomSource &rng, uint *valuesList, uint &listSize);
|
||||
bool boardStateHasValidMove(BoardState_t boardState) const;
|
||||
bool tileIsExposed(BoardState_t boardState, uint tile) const;
|
||||
bool tileExistsAtCoordinate(BoardState_t boardState, uint x, uint y, uint z) const;
|
||||
|
||||
static BoardState_t boardStateBit(uint bit);
|
||||
static BoardState_t emptyBoardState();
|
||||
|
||||
Event _resetTileSetWhen;
|
||||
VarReference _tileSetRef;
|
||||
|
||||
static TileCoordinate _tileCoordinates[kNumTiles];
|
||||
int8 _tileAtCoordinate[kBoardSizeX][kBoardSizeY][kBoardSizeZ];
|
||||
};
|
||||
|
||||
class PrintModifier : public Modifier {
|
||||
|
@ -31,7 +31,7 @@ DataReadErrorCode ShanghaiModifier::load(PlugIn &plugIn, const PlugInModifier &p
|
||||
if (prefix.plugInRevision != 0)
|
||||
return kDataReadErrorUnsupportedRevision;
|
||||
|
||||
if (!unknown1Event.load(reader) || !unknown2VarRef.load(reader))
|
||||
if (!resetWhen.load(reader) || !tileSetVar.load(reader))
|
||||
return kDataReadErrorReadFailed;
|
||||
|
||||
return kDataReadErrorNone;
|
||||
|
@ -34,8 +34,8 @@ namespace MTI {
|
||||
// Shanghai - ???
|
||||
|
||||
struct ShanghaiModifier : public PlugInModifierData {
|
||||
PlugInTypeTaggedValue unknown1Event; // Probably "Enable When"
|
||||
PlugInTypeTaggedValue unknown2VarRef; // VarRef (Probably tile set)
|
||||
PlugInTypeTaggedValue resetWhen; // Reset When
|
||||
PlugInTypeTaggedValue tileSetVar; // VarRef
|
||||
|
||||
protected:
|
||||
DataReadErrorCode load(PlugIn &plugIn, const PlugInModifier &prefix, DataReader &reader) override;
|
||||
|
Loading…
Reference in New Issue
Block a user