#include "Block.h" #include "AddEntityUpdate.h" #include "Game.h" #include "Inventory.h" #include "ItemEntity.h" #include "MultiblockStructure.h" #include "NoBlock.h" Block::Block(int typeId, const ItemType* zTool, Framework::Vec3 pos, bool hasInventory) : Inventory(pos, hasInventory) { transparent = false; passable = false; hp = 1; maxHP = 1; hardness = 1; this->typeId = typeId; this->zTool = zTool; speedModifier = 1; ticksLeftCounter = 0; wasTicked = 0; onTickCalled = 0; minTickTimeout = -1; maxTickTimeout = -1; tickSource = 0; currentTickTimeout = 0; dimensionId = 0; interactable = 0; deadAndRemoved = 0; memset(zNeighbours, 0, sizeof(Block*) * 6); memset(lightEmisionColor, 0, 3); } Block::~Block() {} void Block::onDestroy() { if (!deadAndRemoved) { for (int i = 0; i < 6; i++) { if (neighbourTypes[i] == BlockTypeEnum::NO_BLOCK) { Framework::Vec3 pos = getPos() + getDirection(getDirectionFromIndex(i)); Game::INSTANCE->zDimension(dimensionId) ->placeBlock(pos, Game::INSTANCE->zGenerator()->generateSingleBlock( pos, dimensionId)); } } Item* blockItem = zBlockType()->getItemFromBlock(this); if (blockItem) { ItemEntity* itemEntity = (ItemEntity*)StaticRegistry::INSTANCE .zElement(EntityTypeEnum::ITEM) ->createEntity( location + Framework::Vec3(0.5f, 0.5f, 0.5f), dimensionId, Game::INSTANCE->getNextEntityId()); ItemStack* stack = new ItemStack(blockItem, 1, blockItem->getMaxStackSize()); itemEntity->unsaveAddItem(stack, NO_DIRECTION); stack->release(); Game::INSTANCE->requestWorldUpdate( new AddEntityUpdate(itemEntity, dimensionId)); deadAndRemoved = 1; } for (MultiblockStructure* structure : structures) structure->onBlockRemoved(this); Game::INSTANCE->zDimension(dimensionId) ->placeBlock( getPos(), BlockTypeEnum::AIR); // this will be deleted here } } void Block::tick(TickQueue* zQueue) { if (wasTicked) return; wasTicked = 1; ticksLeftCounter++; if (minTickTimeout >= 0) { if (currentTickTimeout < ticksLeftCounter) { onTickCalled = 1; bool blocked = 0; bool result = onTick(zQueue, ticksLeftCounter, blocked); if (blocked) { wasTicked = 0; ticksLeftCounter--; onTickCalled = 0; zQueue->addToQueue(this); return; } if (result) currentTickTimeout = MAX(MIN(currentTickTimeout - 1, maxTickTimeout), MAX(minTickTimeout, 0)); else currentTickTimeout = MAX(MIN(currentTickTimeout + 1, maxTickTimeout), MAX(minTickTimeout, 0)); ticksLeftCounter = 0; } } else { onTickCalled = 1; bool blocked = 0; onTick(zQueue, 1, blocked); if (blocked) { wasTicked = 0; onTickCalled = 0; zQueue->addToQueue(this); return; } } } void Block::postTick() { wasTicked = 0; if (onTickCalled) { onPostTick(); onTickCalled = 0; } } void Block::setNeighbour( Direction dir, Framework::Either neighbour) { if (neighbour.isA()) setNeighbourBlock(dir, neighbour); else { setNeighbourBlock(dir, 0); setNeighbourType(dir, neighbour); } } void Block::setNeighbourBlock(Direction dir, Block* zN) { if (zN) setNeighbourType(dir, zN->zBlockType()->getId()); zNeighbours[getDirectionIndex(dir)] = zN; } void Block::setNeighbourType(Direction dir, int type) { neighbourTypes[getDirectionIndex(dir)] = type; } void Block::setDimensionId(int id) { dimensionId = id; } void Block::addToStructure(MultiblockStructure* structure) { if (structure->isBlockMember(this)) structures.add(structure); else structure->release(); } void Block::onLoaded() { for (MultiblockStructure* structure : structures) structure->onBlockLoaded(dynamic_cast(getThis())); } void Block::onUnloaded() { for (MultiblockStructure* structure : structures) structure->onBlockUnloaded(this); } Framework::Text Block::getTargetUIML() { return StaticRegistry::INSTANCE.zElement(typeId) ->getTargetUIML(); } void Block::sendModelInfo(NetworkMessage* zMessage) { // overwritten by some blocks } void Block::api(Framework::StreamReader* zRequest, NetworkMessage* zResponse) { // TODO: answer api requests char id = 0; zRequest->lese(&id, 1); switch (id) { case 0: // request model state sendModelInfo(zResponse); } } bool Block::isTickSource() const { return tickSource; } const BlockType* Block::zBlockType() const { return StaticRegistry::INSTANCE.zElement(typeId); } bool Block::isTransparent() const { return transparent; } bool Block::isPassable() const { return passable; } bool Block::isInteractable() const { return interactable; } float Block::getHP() const { return hp; } float Block::getMaxHP() const { return maxHP; } float Block::getHardness() const { return hardness; } const ItemType* Block::zEffectiveTool() const { return zTool; } float Block::getSpeedModifier() const { return speedModifier; } const Framework::Vec3 Block::getPos() const { return (Framework::Vec3)location; } int Block::getDimensionId() const { return dimensionId; } bool Block::isVisible() const { if (passable || transparent) return 1; for (int i = 0; i < 6; i++) { const Block* neighbour = CONST_BLOCK(zNeighbours[i], neighbourTypes[i]); if (neighbour->isPassable() || neighbour->isTransparent()) return 1; } return 0; } void Block::setHP(float hp) { bool isDead = this->hp == 0.f; this->hp = MAX(0.f, hp); if (!isDead && this->hp == 0.f) { onDestroy(); // this will be deleted } else { NetworkMessage* changeMsg = new NetworkMessage(); changeMsg->addressBlock(this); char* msg = new char[5]; msg[0] = 0; // hp changed *(float*)(msg + 1) = this->hp; changeMsg->setMessage(msg, 5); Game::INSTANCE->broadcastMessage(changeMsg); } } bool Block::isDeadAndRemoved() const { return deadAndRemoved; } const unsigned char* Block::getLightEmisionColor() const { return lightEmisionColor; } void Block::filterPassingLight(unsigned char rgb[3]) const { if (!transparent) // let no light pass intransparent blocks memset(rgb, 0, 3); } Block* Block::zNeighbor(Direction dir) const { return zNeighbours[getDirectionIndex(dir)]; } void Block::updateModel(ModelInfo info) const { NetworkMessage* changeMsg = new NetworkMessage(); changeMsg->addressBlock(this); InMemoryBuffer buffer; info.writeTo(&buffer); char* msg = new char[(int)buffer.getSize() + 1]; msg[0] = 1; // hmodel change buffer.lese(msg + 1, (int)buffer.getSize()); changeMsg->setMessage(msg, (int)buffer.getSize() + 1); Game::INSTANCE->broadcastMessage(changeMsg); } BasicBlockItem::BasicBlockItem( int itemTypeId, int blockTypeId, const char* name) : Item(itemTypeId, name), transparent(0), passable(0), hardness(1.f), toolId(0), speedModifier(1.f), interactable(1) { this->blockTypeId = blockTypeId; placeable = 1; placableProof = [this](const Item* self, int dimensionId, Framework::Vec3 worldPos) { return Item::canBePlacedAt(dimensionId, worldPos); }; } void BasicBlockItem::setPlacableProof( std::function)> condition, bool andDefault) { if (andDefault) { placableProof = [this, condition](const Item* self, int dimensionId, Framework::Vec3 worldPos) { return Item::canBePlacedAt(dimensionId, worldPos) && condition(self, dimensionId, worldPos); }; } else { placableProof = condition; } } bool BasicBlockItem::canBeStackedWith(const Item* zItem) const { const BasicBlockItem* item = dynamic_cast(zItem); if (item) { return Item::canBeStackedWith(zItem) && transparent == item->transparent && passable == item->passable && hardness == item->hardness && toolId == item->toolId && speedModifier == item->speedModifier && interactable == item->interactable; } return 0; } bool BasicBlockItem::canBePlacedAt( int dimensionId, Framework::Vec3 worldPos) const { return placableProof(this, dimensionId, worldPos); } BasicBlockItemType::BasicBlockItemType(int id, const char* name, ItemSkillLevelUpRule* levelUpRule, int brokenTypeId, ModelInfo model, int blockTypeId) : ItemType(id, name, levelUpRule, brokenTypeId, model), transparent(0), passable(0), hardness(1.f), toolId(0), speedModifier(1.f), blockTypeId(blockTypeId) {} void BasicBlockItemType::loadSuperItem( Item* zItem, Framework::StreamReader* zReader) const { ItemType::loadSuperItem(zItem, zReader); BasicBlockItem* item = dynamic_cast(zItem); if (!item) throw "BasicBlockItemType::loadSuperItem was called with an invalid " "item"; zReader->lese((char*)&item->transparent, 1); zReader->lese((char*)&item->passable, 1); zReader->lese((char*)&item->hardness, 4); zReader->lese((char*)&item->toolId, 4); zReader->lese((char*)&item->speedModifier, 4); zReader->lese((char*)&item->interactable, 1); } void BasicBlockItemType::saveSuperItem( const Item* zItem, Framework::StreamWriter* zWriter) const { ItemType::saveSuperItem(zItem, zWriter); const BasicBlockItem* item = dynamic_cast(zItem); if (!item) throw "BasicBlockItemType::saveSuperItem was called with an invalid " "item"; zWriter->schreibe((char*)&item->transparent, 1); zWriter->schreibe((char*)&item->passable, 1); zWriter->schreibe((char*)&item->hardness, 4); zWriter->schreibe((char*)&item->toolId, 4); zWriter->schreibe((char*)&item->speedModifier, 4); zWriter->schreibe((char*)&item->interactable, 1); } Item* BasicBlockItemType::createItem() const { BasicBlockItem* item = new BasicBlockItem(id, blockTypeId, name); item->transparent = transparent; item->passable = passable; item->hardness = hardness; item->toolId = toolId; item->speedModifier = speedModifier; item->interactable = 1; if (placableProofState) { item->setPlacableProof(placableProof, placableProofState > 1); } return item; } BasicBlockItemType* BasicBlockItemType::setHardness(float hardness) { this->hardness = hardness; return this; } BasicBlockItemType* BasicBlockItemType::setPlacableProof( std::function)> condition, bool andDefault) { placableProofState = 1 + (andDefault ? 1 : 0); placableProof = condition; return this; }