#include "FluidBlock.h" #include "FluidContainer.h" #include "Game.h" FluidBlock::FluidBlock(int typeId, Framework::Vec3 pos, int dimensionId, Vec3 lightWeights) : Block(typeId, pos, dimensionId, 0), lightWeights(lightWeights), neighborChanged(1), nextFlow(0), maxFlowDistance(8) { transparent = 1; passable = 1; hp = 1; maxHP = 1; hardness = -1.f; speedModifier = 0.5f; tickSource = 1; interactable = 0; flowOptions = 0; distanceToSource = 0; } FluidBlock::~FluidBlock() {} bool FluidBlock::onTick(TickQueue* zQueue, int numTicks, bool& blocked) { if (neighborChanged) { nextFlow -= numTicks; if (nextFlow <= 0) { const FluidBlockType* zType = dynamic_cast(zBlockType()); if (zType) { nextFlow = zType->getTicktsToFlow(); } else { nextFlow = 0; } neighborChanged = 0; doFlow(); } return true; } return false; } void FluidBlock::onPostTick() {} void FluidBlock::setNeighbourType(Direction dir, int type) { Block::setNeighbourType(dir, type); neighborChanged = 1; } void FluidBlock::sendModelInfo(NetworkMessage* zMessage) { zMessage->addressBlock(this); char* msg = new char[3]; msg[0] = 2; // fluid amount change *(msg + 1) = flowOptions; *(msg + 2) = distanceToSource; zMessage->setMessage(msg, 3); } void FluidBlock::doFlow() { bool doesFlowSidewards = 1; if (((zNeighbours[getDirectionIndex(Direction::BOTTOM)] && zNeighbours[getDirectionIndex(Direction::BOTTOM)] ->zBlockType() == zBlockType() || neighbourTypes[getDirectionIndex(Direction::BOTTOM)] == BlockTypeEnum::AIR) && distanceToSource) || distanceToSource >= maxFlowDistance) { doesFlowSidewards = 0; } bool changed = false; int minNeighborDistance = maxFlowDistance; for (int i = 0; i < 6; i++) { Direction dir = getDirectionFromIndex(i); if (dir & Direction::TOP) { if (zNeighbours[i] && zNeighbours[i]->zBlockType() == zBlockType()) { FluidBlock* neighbour = dynamic_cast(zNeighbours[i]); minNeighborDistance = 0; distanceToSource = neighbour->distanceToSource + 1; flowOptions = (char)getOppositeDirection(dir); changed = true; } continue; } if (neighbourTypes[i] == BlockTypeEnum::AIR) { if (dir & Direction::BOTTOM || doesFlowSidewards) { Game::INSTANCE->doLater([this, dir, i]() { if (neighbourTypes[i] == BlockTypeEnum::AIR) { Block* belowBlock = zBlockType()->createBlockAt( getPos() + getDirection(dir), getDimensionId(), 0); FluidBlock* fluidBlock = dynamic_cast(belowBlock); if (fluidBlock) { fluidBlock->distanceToSource = dir & Direction::BOTTOM ? 1 : distanceToSource + 1; fluidBlock->flowOptions = (char)dir; Game::INSTANCE->zDimension(getDimensionId()) ->placeBlock( getPos() + getDirection(dir), belowBlock); } else { std::cout << "ERROR: created flow fuild block is not an " "instance of FluidBlock\n"; belowBlock->release(); } } }); } } else if (zNeighbours[i] && zNeighbours[i]->zBlockType() == zBlockType()) { if (dir & Direction::BOTTOM) continue; FluidBlock* neighbour = dynamic_cast(zNeighbours[i]); if (neighbour) { if (distanceToSource > neighbour->distanceToSource + 1) { distanceToSource = neighbour->distanceToSource + 1; flowOptions = (char)getOppositeDirection(dir); changed = true; } else if (distanceToSource == neighbour->distanceToSource + 1) { char tmp = flowOptions; flowOptions |= (char)getOppositeDirection(dir); changed |= tmp != flowOptions; } if (neighbour->distanceToSource < minNeighborDistance) { minNeighborDistance = neighbour->distanceToSource; } } } } if (distanceToSource) { if (minNeighborDistance + 1 > distanceToSource) { distanceToSource = minNeighborDistance + 1; flowOptions = 0; // reavaluated next tick changed = true; } } if (distanceToSource > maxFlowDistance) { distanceToSource = maxFlowDistance; Game::INSTANCE->doLater([this]() { setHP(0.f); }); } if (changed) { broadcastModelInfoChange(); neighborChanged = 1; for (int i = 0; i < 6; i++) { Direction dir = getDirectionFromIndex(i); if (dir & (Direction::TOP | Direction::BOTTOM)) continue; if (zNeighbours[i] && zNeighbours[i]->zBlockType() == zBlockType()) { FluidBlock* neighbour = dynamic_cast(zNeighbours[i]); if (neighbour) { neighbour->neighborChanged = 1; } } } } } bool FluidBlock::isInteractable(const Item* zItem) const { return distanceToSource == 0 && dynamic_cast(zItem); } void FluidBlock::filterPassingLight(unsigned char rgb[3]) const { rgb[0] = (unsigned char)(rgb[0] * lightWeights.x); rgb[1] = (unsigned char)(rgb[1] * lightWeights.y); rgb[2] = (unsigned char)(rgb[2] * lightWeights.z); } char FluidBlock::getDistanceToSource() const { return distanceToSource; } char FluidBlock::getFlowOptions() const { return flowOptions; } FluidBlockType::FluidBlockType(ModelInfo* model, Framework::Text name, int mapColor, Vec3 lightWeights, int ticktsToFlow, unsigned char flowDistance, float hpRecoveryPerL, float thirstRecoveryPerL, float heat, Framework::RCArray groupNames) : BlockType(0, model, 1, 10, 0, name, true, mapColor, groupNames, 0.f), lightWeights(lightWeights), ticktsToFlow(ticktsToFlow), flowDistance(flowDistance), hungerRecoveryPerL(hungerRecoveryPerL), thirstRecoveryPerL(thirstRecoveryPerL), heat(heat) {} void FluidBlockType::loadSuperBlock( Block* zBlock, Framework::StreamReader* zReader, int dimensionId) const { FluidBlock* block = dynamic_cast(zBlock); zReader->lese(&block->flowOptions, 1); zReader->lese(&block->distanceToSource, 1); block->nextFlow = ticktsToFlow; block->maxFlowDistance = flowDistance; BlockType::loadSuperBlock(zBlock, zReader, dimensionId); } void FluidBlockType::saveSuperBlock( Block* zBlock, Framework::StreamWriter* zWriter) const { FluidBlock* block = dynamic_cast(zBlock); zWriter->schreibe(&block->flowOptions, 1); zWriter->schreibe(&block->distanceToSource, 1); BlockType::saveSuperBlock(zBlock, zWriter); } Item* FluidBlockType::createItem() const { return 0; } Block* FluidBlockType::createBlock( Framework::Vec3 position, int dimensionId) const { FluidBlock* result = new FluidBlock(getId(), position, dimensionId, lightWeights); result->nextFlow = ticktsToFlow; result->maxFlowDistance = flowDistance; return result; } bool FluidBlockType::isFluid() const { return true; } int FluidBlockType::getTicktsToFlow() const { return ticktsToFlow; } unsigned char FluidBlockType::getFlowDistance() const { return flowDistance; } Framework::Vec3 FluidBlockType::getLightWeights() const { return lightWeights; } float FluidBlockType::getHungerRecoveryPerL() const { return hungerRecoveryPerL; } float FluidBlockType::getThirstRecoveryPerL() const { return thirstRecoveryPerL; } ItemType* FluidBlockType::createItemType() const { return 0; } float FluidBlockType::getHeat() const { return heat; } FluidBlockTypeFactory::FluidBlockTypeFactory() : SubTypeFactory() {} FluidBlockType* FluidBlockTypeFactory::fromJson( Framework::JSON::JSONObject* zJson) const { Framework::RCArray groupNames; for (Framework::JSON::JSONValue* value : *zJson->zValue("groupNames")->asArray()) { groupNames.add(new Framework::Text(value->asString()->getString())); } return new FluidBlockType( Game::INSTANCE->zTypeRegistry()->fromJson( zJson->zValue("model")), zJson->zValue("name")->asString()->getString(), (int)zJson->zValue("mapColor")->asString()->getString(), Framework::Vec3((float)zJson->zValue("lightWeight") ->asObject() ->zValue("red") ->asNumber() ->getNumber(), (float)zJson->zValue("lightWeight") ->asObject() ->zValue("green") ->asNumber() ->getNumber(), (float)zJson->zValue("lightWeight") ->asObject() ->zValue("blue") ->asNumber() ->getNumber()), (int)zJson->zValue("ticksToFlow")->asNumber()->getNumber(), (char)zJson->zValue("flowDistance")->asNumber()->getNumber(), (float)zJson->zValue("hungerRecoveryPerL")->asNumber()->getNumber(), (float)zJson->zValue("thirstRecoveryPerL")->asNumber()->getNumber(), (float)zJson->zValue("heat")->asNumber()->getNumber(), groupNames); } Framework::JSON::JSONObject* FluidBlockTypeFactory::toJson( FluidBlockType* zObject) const { Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject(); result->addValue( "model", Game::INSTANCE->zTypeRegistry()->toJson(zObject->zModel())); result->addValue( "name", new Framework::JSON::JSONString(zObject->getName())); result->addValue( "mapColor", new Framework::JSON::JSONString(zObject->getMapColor())); Framework::JSON::JSONObject* lightWeight = new Framework::JSON::JSONObject(); lightWeight->addValue( "red", new Framework::JSON::JSONNumber(zObject->getLightWeights().x)); lightWeight->addValue( "green", new Framework::JSON::JSONNumber(zObject->getLightWeights().y)); lightWeight->addValue( "blue", new Framework::JSON::JSONNumber(zObject->getLightWeights().z)); result->addValue("lightWeight", lightWeight); result->addValue("ticksToFlow", new Framework::JSON::JSONNumber((double)zObject->getTicktsToFlow())); result->addValue("flowDistance", new Framework::JSON::JSONNumber((double)zObject->getFlowDistance())); result->addValue("hungerRecoveryPerL", new Framework::JSON::JSONNumber( (double)zObject->getHungerRecoveryPerL())); result->addValue("thirstRecoveryPerL", new Framework::JSON::JSONNumber( (double)zObject->getThirstRecoveryPerL())); result->addValue("heat", new Framework::JSON::JSONNumber( (double)zObject->getHeat())); Framework::JSON::JSONArray* groupNames = new Framework::JSON::JSONArray(); for (Framework::Text* groupName : zObject->getGroupNames()) { groupNames->addValue(new Framework::JSON::JSONString(*groupName)); } result->addValue("groupNames", groupNames); return result; } Framework::JSON::Validator::JSONValidator* FluidBlockTypeFactory::getValidator( Framework::JSON::Validator::ObjectValidationBuilder< Framework::JSON::Validator::JSONValidator>* builder) const { return builder ->withRequiredAttribute( "model", Game::INSTANCE->zTypeRegistry()->getValidator()) ->withRequiredString("name") ->finishString() ->withRequiredString("mapColor") ->finishString() ->withRequiredObject("lightWeight") ->withRequiredNumber("red") ->whichIsGreaterOrEqual(0.0) ->whichIsLessOrEqual(1.0) ->finishNumber() ->withRequiredNumber("green") ->whichIsGreaterOrEqual(0.0) ->whichIsLessOrEqual(1.0) ->finishNumber() ->withRequiredNumber("blue") ->whichIsGreaterOrEqual(0.0) ->whichIsLessOrEqual(1.0) ->finishNumber() ->finishObject() ->withRequiredNumber("ticksToFlow") ->whichIsGreaterOrEqual(1.0) ->finishNumber() ->withRequiredNumber("flowDistance") ->whichIsGreaterOrEqual(0.0) ->finishNumber() ->withRequiredNumber("hungerRecoveryPerL") ->whichIsGreaterOrEqual(0.0) ->withDefault(0.0) ->finishNumber() ->withRequiredNumber("thirstRecoveryPerL") ->whichIsGreaterOrEqual(0.0) ->withDefault(0.0) ->finishNumber() ->withRequiredNumber("heat") ->withDefault(10.0) ->finishNumber() ->withRequiredArray("groupNames") ->withDefault(new Framework::JSON::JSONArray()) ->addAcceptedStringInArray() ->finishString() ->finishArray() ->finishObject(); } Framework::Text FluidBlockTypeFactory::getTypeToken() const { return "fluid"; }