MTROPOLIS: Implement Miniscript global references and MTI Shanghai minigame

This commit is contained in:
elasota 2023-06-12 22:44:49 -04:00
parent 89c8f002c0
commit 07726745d4
6 changed files with 479 additions and 27 deletions

View File

@ -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;

View File

@ -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;
};

View File

@ -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() {
}

View File

@ -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 {

View File

@ -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;

View File

@ -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;