|
@@ -1443,3 +1443,340 @@ const char* JFirstBlockAboveBoolExpressionFactory::getTypeToken() const
|
|
|
{
|
|
{
|
|
|
return "firstBlockAboveMatches";
|
|
return "firstBlockAboveMatches";
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+JNaighborBlockBoolExpression::JNaighborBlockBoolExpression()
|
|
|
|
|
+ : JBoolExpression(),
|
|
|
|
|
+ filter(0),
|
|
|
|
|
+ validDirections(Direction::NO_DIRECTION)
|
|
|
|
|
+{}
|
|
|
|
|
+
|
|
|
|
|
+JNaighborBlockBoolExpression::~JNaighborBlockBoolExpression()
|
|
|
|
|
+{
|
|
|
|
|
+ if (filter)
|
|
|
|
|
+ {
|
|
|
|
|
+ filter->release();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+bool JNaighborBlockBoolExpression::isValidPosition(
|
|
|
|
|
+ int x, int y, Chunk* currentChunk)
|
|
|
|
|
+{
|
|
|
|
|
+ return currentChunk
|
|
|
|
|
+ && Game::getChunkCenter(x, y) == currentChunk->getCenter();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void JNaighborBlockBoolExpression::addEvaluation(
|
|
|
|
|
+ Framework::Assembly::AssemblyBlock& codeBlock, JExpressionMemory* zMemory)
|
|
|
|
|
+{
|
|
|
|
|
+ // call currentChunk->zBlockConstWC(x, y, z)
|
|
|
|
|
+ codeBlock.addLoadValue(
|
|
|
|
|
+ (__int64*)zMemory->zzCurrentChunk(), Framework::Assembly::RCX);
|
|
|
|
|
+ codeBlock.addMemberCall<const Block* (Chunk::*)(int, int, int) const>(
|
|
|
|
|
+ &Chunk::zBlockConstWC,
|
|
|
|
|
+ Framework::Assembly::INT_VALUE,
|
|
|
|
|
+ {Framework::Assembly::RCX,
|
|
|
|
|
+ Framework::Assembly::RDX,
|
|
|
|
|
+ Framework::Assembly::R8,
|
|
|
|
|
+ Framework::Assembly::R9},
|
|
|
|
|
+ {});
|
|
|
|
|
+ // call filter->test(block)
|
|
|
|
|
+ codeBlock.addLoadValue((__int64*)&filter, Framework::Assembly::RCX);
|
|
|
|
|
+ codeBlock.addMoveValue(Framework::Assembly::RDX, Framework::Assembly::RAX);
|
|
|
|
|
+ codeBlock.addMemberCall<bool (BlockFilter::*)(const Block*) const>(
|
|
|
|
|
+ &BlockFilter::test,
|
|
|
|
|
+ Framework::Assembly::INT_VALUE,
|
|
|
|
|
+ {Framework::Assembly::RCX, Framework::Assembly::RDX},
|
|
|
|
|
+ {});
|
|
|
|
|
+ // if filter returns true, jump to end
|
|
|
|
|
+ codeBlock.addTest(Framework::Assembly::RAX,
|
|
|
|
|
+ Framework::Assembly::RAX,
|
|
|
|
|
+ Framework::Assembly::LOWER8);
|
|
|
|
|
+ codeBlock.addJump(Framework::Assembly::JNZ, "end_true");
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+Framework::Assembly::AssemblyBlock& JNaighborBlockBoolExpression::buildAssembly(
|
|
|
|
|
+ JExpressionMemory* zMemory)
|
|
|
|
|
+{
|
|
|
|
|
+ if (validDirections == Direction::NO_DIRECTION)
|
|
|
|
|
+ {
|
|
|
|
|
+ // no directions to check, return false
|
|
|
|
|
+ codeBlock.addMoveValue(Framework::Assembly::RAX, (char)0);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ // load x into R12
|
|
|
|
|
+ codeBlock.addLoadValue(
|
|
|
|
|
+ zMemory->getFloatVariableP("x"), Framework::Assembly::MM0);
|
|
|
|
|
+ codeBlock.addConversion(Framework::Assembly::R12,
|
|
|
|
|
+ Framework::Assembly::MM0,
|
|
|
|
|
+ Framework::Assembly::SINGLE_FLOAT,
|
|
|
|
|
+ Framework::Assembly::LOWER32,
|
|
|
|
|
+ 1);
|
|
|
|
|
+ // load y into R13
|
|
|
|
|
+ codeBlock.addLoadValue(
|
|
|
|
|
+ zMemory->getFloatVariableP("y"), Framework::Assembly::MM0);
|
|
|
|
|
+ codeBlock.addConversion(Framework::Assembly::R13,
|
|
|
|
|
+ Framework::Assembly::MM0,
|
|
|
|
|
+ Framework::Assembly::SINGLE_FLOAT,
|
|
|
|
|
+ Framework::Assembly::LOWER32,
|
|
|
|
|
+ 1);
|
|
|
|
|
+ // load z into R14
|
|
|
|
|
+ codeBlock.addLoadValue(
|
|
|
|
|
+ zMemory->getFloatVariableP("z"), Framework::Assembly::MM0);
|
|
|
|
|
+ codeBlock.addConversion(Framework::Assembly::R14,
|
|
|
|
|
+ Framework::Assembly::MM0,
|
|
|
|
|
+ Framework::Assembly::SINGLE_FLOAT,
|
|
|
|
|
+ Framework::Assembly::LOWER32,
|
|
|
|
|
+ 1);
|
|
|
|
|
+ if (validDirections & Direction::TOP)
|
|
|
|
|
+ {
|
|
|
|
|
+ // add 1 to z and check if it's above world height
|
|
|
|
|
+ codeBlock.addMoveValue(
|
|
|
|
|
+ Framework::Assembly::R9, Framework::Assembly::R14);
|
|
|
|
|
+ codeBlock.addAddition(
|
|
|
|
|
+ Framework::Assembly::R9, (char)1, Framework::Assembly::LOWER16);
|
|
|
|
|
+ codeBlock.addCompare(Framework::Assembly::R9, WORLD_HEIGHT);
|
|
|
|
|
+ codeBlock.addJump(Framework::Assembly::JGE, "skip_top");
|
|
|
|
|
+
|
|
|
|
|
+ // evaluates filter on block at current position
|
|
|
|
|
+ codeBlock.addMoveValue(
|
|
|
|
|
+ Framework::Assembly::RDX, Framework::Assembly::R12);
|
|
|
|
|
+ codeBlock.addMoveValue(
|
|
|
|
|
+ Framework::Assembly::R8, Framework::Assembly::R13);
|
|
|
|
|
+ addEvaluation(codeBlock, zMemory);
|
|
|
|
|
+
|
|
|
|
|
+ codeBlock.defineJumpTarget("skip_top");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (validDirections & Direction::BOTTOM)
|
|
|
|
|
+ {
|
|
|
|
|
+ // subtract 1 from z and check if it's >= 0
|
|
|
|
|
+ codeBlock.addMoveValue(
|
|
|
|
|
+ Framework::Assembly::R9, Framework::Assembly::R14);
|
|
|
|
|
+ codeBlock.addSubtraction(
|
|
|
|
|
+ Framework::Assembly::R9, (char)1, Framework::Assembly::LOWER16);
|
|
|
|
|
+ codeBlock.addTest(Framework::Assembly::R9,
|
|
|
|
|
+ Framework::Assembly::R9,
|
|
|
|
|
+ Framework::Assembly::LOWER32);
|
|
|
|
|
+ codeBlock.addJump(Framework::Assembly::JL, "skip_bottom");
|
|
|
|
|
+
|
|
|
|
|
+ // evaluates filter on block at current position
|
|
|
|
|
+ codeBlock.addMoveValue(
|
|
|
|
|
+ Framework::Assembly::RDX, Framework::Assembly::R12);
|
|
|
|
|
+ codeBlock.addMoveValue(
|
|
|
|
|
+ Framework::Assembly::R8, Framework::Assembly::R13);
|
|
|
|
|
+ addEvaluation(codeBlock, zMemory);
|
|
|
|
|
+
|
|
|
|
|
+ codeBlock.defineJumpTarget("skip_bottom");
|
|
|
|
|
+ }
|
|
|
|
|
+ for (int i = 0; i < 4; i++)
|
|
|
|
|
+ { // check horizontal directions
|
|
|
|
|
+ Direction dir = getDirectionFromIndex(i);
|
|
|
|
|
+ if (validDirections & dir)
|
|
|
|
|
+ {
|
|
|
|
|
+ Framework::Vec3<int> offset = getDirection(dir);
|
|
|
|
|
+ codeBlock.addMoveValue(
|
|
|
|
|
+ Framework::Assembly::RDX, Framework::Assembly::R12);
|
|
|
|
|
+ codeBlock.addMoveValue(
|
|
|
|
|
+ Framework::Assembly::R8, Framework::Assembly::R13);
|
|
|
|
|
+ if (offset.x > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ codeBlock.addAddition(Framework::Assembly::RDX,
|
|
|
|
|
+ (char)1,
|
|
|
|
|
+ Framework::Assembly::LOWER32);
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (offset.x < 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ codeBlock.addSubtraction(Framework::Assembly::RDX,
|
|
|
|
|
+ (char)1,
|
|
|
|
|
+ Framework::Assembly::LOWER32);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (offset.y > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ codeBlock.addAddition(Framework::Assembly::R8,
|
|
|
|
|
+ (char)1,
|
|
|
|
|
+ Framework::Assembly::LOWER32);
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (offset.y < 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ codeBlock.addSubtraction(Framework::Assembly::R8,
|
|
|
|
|
+ (char)1,
|
|
|
|
|
+ Framework::Assembly::LOWER32);
|
|
|
|
|
+ }
|
|
|
|
|
+ // check if new position is in the same chunk
|
|
|
|
|
+ codeBlock.addLoadValue((__int64*)zMemory->zzCurrentChunk(),
|
|
|
|
|
+ Framework::Assembly::R9);
|
|
|
|
|
+ codeBlock.addLoadAddress(this, Framework::Assembly::RCX);
|
|
|
|
|
+ codeBlock.addPush(
|
|
|
|
|
+ Framework::Assembly::RDX, Framework::Assembly::LOWER32);
|
|
|
|
|
+ codeBlock.addPush(
|
|
|
|
|
+ Framework::Assembly::R8, Framework::Assembly::LOWER32);
|
|
|
|
|
+ codeBlock.addMemberCall<bool (JNaighborBlockBoolExpression::*)(
|
|
|
|
|
+ int, int, Chunk*)>(
|
|
|
|
|
+ &JNaighborBlockBoolExpression::isValidPosition,
|
|
|
|
|
+ Framework::Assembly::INT_VALUE,
|
|
|
|
|
+ {Framework::Assembly::RCX,
|
|
|
|
|
+ Framework::Assembly::RDX,
|
|
|
|
|
+ Framework::Assembly::R8,
|
|
|
|
|
+ Framework::Assembly::R9},
|
|
|
|
|
+ {});
|
|
|
|
|
+ // abort if position is not valid
|
|
|
|
|
+ codeBlock.addTest(Framework::Assembly::RAX,
|
|
|
|
|
+ Framework::Assembly::RAX,
|
|
|
|
|
+ Framework::Assembly::LOWER8);
|
|
|
|
|
+ Framework::Text jumpLabel = "skip_dir_";
|
|
|
|
|
+ jumpLabel.append(i);
|
|
|
|
|
+ codeBlock.addJump(Framework::Assembly::JZ, jumpLabel);
|
|
|
|
|
+
|
|
|
|
|
+ // restore parameters for block access
|
|
|
|
|
+ codeBlock.addPop(
|
|
|
|
|
+ Framework::Assembly::R8, Framework::Assembly::LOWER32);
|
|
|
|
|
+ codeBlock.addPop(
|
|
|
|
|
+ Framework::Assembly::RDX, Framework::Assembly::LOWER32);
|
|
|
|
|
+ codeBlock.addMoveValue(
|
|
|
|
|
+ Framework::Assembly::R9, Framework::Assembly::R14);
|
|
|
|
|
+ addEvaluation(codeBlock, zMemory);
|
|
|
|
|
+
|
|
|
|
|
+ codeBlock.defineJumpTarget(jumpLabel);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // set return value to false if no direction matched
|
|
|
|
|
+ codeBlock.addMoveValue(Framework::Assembly::RAX, (char)0);
|
|
|
|
|
+ codeBlock.addJump(Framework::Assembly::JMP, "end");
|
|
|
|
|
+ codeBlock.defineJumpTarget("end_true");
|
|
|
|
|
+ // set return value to true if any direction matched
|
|
|
|
|
+ codeBlock.addMoveValue(Framework::Assembly::RAX, (char)1);
|
|
|
|
|
+ codeBlock.defineJumpTarget("end");
|
|
|
|
|
+ }
|
|
|
|
|
+ return codeBlock;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void JNaighborBlockBoolExpression::setFilter(BlockFilter* filter)
|
|
|
|
|
+{
|
|
|
|
|
+ this->filter = filter;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+BlockFilter* JNaighborBlockBoolExpression::zFilter() const
|
|
|
|
|
+{
|
|
|
|
|
+ return filter;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void JNaighborBlockBoolExpression::setValidDirections(
|
|
|
|
|
+ Directions validDirections)
|
|
|
|
|
+{
|
|
|
|
|
+ this->validDirections = validDirections;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+Directions JNaighborBlockBoolExpression::getValidDirections() const
|
|
|
|
|
+{
|
|
|
|
|
+ return validDirections;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+JNaighborBlockBoolExpressionFactory::JNaighborBlockBoolExpressionFactory()
|
|
|
|
|
+ : SubTypeFactory()
|
|
|
|
|
+{}
|
|
|
|
|
+
|
|
|
|
|
+JNaighborBlockBoolExpression* JNaighborBlockBoolExpressionFactory::fromJson(
|
|
|
|
|
+ Framework::JSON::JSONObject* zJson) const
|
|
|
|
|
+{
|
|
|
|
|
+ JNaighborBlockBoolExpression* result = new JNaighborBlockBoolExpression();
|
|
|
|
|
+ result->setFilter(Game::INSTANCE->zTypeRegistry()->fromJson<BlockFilter>(
|
|
|
|
|
+ zJson->zValue("condition")));
|
|
|
|
|
+ for (Framework::JSON::JSONValue* direction :
|
|
|
|
|
+ *zJson->zValue("directions")->asArray())
|
|
|
|
|
+ {
|
|
|
|
|
+ Framework::Text dirStr = direction->asString()->getString();
|
|
|
|
|
+ if (dirStr.isEqual("TOP"))
|
|
|
|
|
+ {
|
|
|
|
|
+ result->setValidDirections(
|
|
|
|
|
+ result->getValidDirections() | Direction::TOP);
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (dirStr.isEqual("BOTTOM"))
|
|
|
|
|
+ {
|
|
|
|
|
+ result->setValidDirections(
|
|
|
|
|
+ result->getValidDirections() | Direction::BOTTOM);
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (dirStr.isEqual("EAST"))
|
|
|
|
|
+ {
|
|
|
|
|
+ result->setValidDirections(
|
|
|
|
|
+ result->getValidDirections() | Direction::EAST);
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (dirStr.isEqual("NORTH"))
|
|
|
|
|
+ {
|
|
|
|
|
+ result->setValidDirections(
|
|
|
|
|
+ result->getValidDirections() | Direction::NORTH);
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (dirStr.isEqual("WEST"))
|
|
|
|
|
+ {
|
|
|
|
|
+ result->setValidDirections(
|
|
|
|
|
+ result->getValidDirections() | Direction::WEST);
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (dirStr.isEqual("SOUTH"))
|
|
|
|
|
+ {
|
|
|
|
|
+ result->setValidDirections(
|
|
|
|
|
+ result->getValidDirections() | Direction::SOUTH);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+Framework::JSON::JSONObject* JNaighborBlockBoolExpressionFactory::toJsonObject(
|
|
|
|
|
+ JNaighborBlockBoolExpression* zObject) const
|
|
|
|
|
+{
|
|
|
|
|
+ Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
|
|
|
|
|
+ result->addValue("condition",
|
|
|
|
|
+ Game::INSTANCE->zTypeRegistry()->toJson(zObject->zFilter()));
|
|
|
|
|
+ Framework::JSON::JSONArray* directionsArray
|
|
|
|
|
+ = new Framework::JSON::JSONArray();
|
|
|
|
|
+ if (zObject->getValidDirections() & Direction::TOP)
|
|
|
|
|
+ {
|
|
|
|
|
+ directionsArray->addValue(new Framework::JSON::JSONString("TOP"));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (zObject->getValidDirections() & Direction::BOTTOM)
|
|
|
|
|
+ {
|
|
|
|
|
+ directionsArray->addValue(new Framework::JSON::JSONString("BOTTOM"));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (zObject->getValidDirections() & Direction::EAST)
|
|
|
|
|
+ {
|
|
|
|
|
+ directionsArray->addValue(new Framework::JSON::JSONString("EAST"));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (zObject->getValidDirections() & Direction::NORTH)
|
|
|
|
|
+ {
|
|
|
|
|
+ directionsArray->addValue(new Framework::JSON::JSONString("NORTH"));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (zObject->getValidDirections() & Direction::WEST)
|
|
|
|
|
+ {
|
|
|
|
|
+ directionsArray->addValue(new Framework::JSON::JSONString("WEST"));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (zObject->getValidDirections() & Direction::SOUTH)
|
|
|
|
|
+ {
|
|
|
|
|
+ directionsArray->addValue(new Framework::JSON::JSONString("SOUTH"));
|
|
|
|
|
+ }
|
|
|
|
|
+ result->addValue("directions", directionsArray);
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+JSONObjectValidationBuilder*
|
|
|
|
|
+JNaighborBlockBoolExpressionFactory::addToValidator(
|
|
|
|
|
+ JSONObjectValidationBuilder* builder) const
|
|
|
|
|
+{
|
|
|
|
|
+ Framework::JSON::JSONArray* defaultDirectionsArray
|
|
|
|
|
+ = new Framework::JSON::JSONArray();
|
|
|
|
|
+ defaultDirectionsArray->addValue(new Framework::JSON::JSONString("EAST"));
|
|
|
|
|
+ defaultDirectionsArray->addValue(new Framework::JSON::JSONString("NORTH"));
|
|
|
|
|
+ defaultDirectionsArray->addValue(new Framework::JSON::JSONString("WEST"));
|
|
|
|
|
+ defaultDirectionsArray->addValue(new Framework::JSON::JSONString("SOUTH"));
|
|
|
|
|
+ return builder
|
|
|
|
|
+ ->withRequiredAttribute("condition",
|
|
|
|
|
+ Game::INSTANCE->zTypeRegistry()->getValidator<BlockFilter>())
|
|
|
|
|
+ ->withRequiredArray("directions")
|
|
|
|
|
+ ->addAcceptedStringInArray()
|
|
|
|
|
+ ->whichIsOneOf({"TOP", "BOTTOM", "EAST", "NORTH", "WEST", "SOUTH"})
|
|
|
|
|
+ ->finishString()
|
|
|
|
|
+ ->withDefault(defaultDirectionsArray)
|
|
|
|
|
+ ->finishArray();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const char* JNaighborBlockBoolExpressionFactory::getTypeToken() const
|
|
|
|
|
+{
|
|
|
|
|
+ return "anyNeighborBlockMatches";
|
|
|
|
|
+}
|