#include "Block.h" #include "Dimension.h" #include "Game.h" #include "Inventory.h" #include "ItemEntity.h" #include "MultiblockStructure.h" #include "NoBlock.h" #include "PlaceableProof.h" #include "TickQueue.h" #include "WorldGenerator.h" Block::Block( int typeId, Framework::Vec3 pos, int dimensionId, bool hasInventory) : Inventory(pos, dimensionId, hasInventory) { transparent = false; passable = false; hp = 1; maxHP = 1; hardness = 1; this->typeId = typeId; speedModifier = 1; ticksLeftCounter = 0; wasTicked = 0; onTickCalled = 0; minTickTimeout = -1; maxTickTimeout = -1; currentTickTimeout = 0; interactable = 0; deadAndRemoved = 0; memset(zNeighbours, 0, sizeof(Block*) * 6); memset(lightEmisionColor, 0, 3); mapColor = 0; } Block::~Block() {} void Block::onDestroy() { if (!deadAndRemoved) { for (int i = 0; i < 6; i++) { Framework::Vec3 pos = getPos() + getDirection(getDirectionFromIndex(i)); if (neighbourTypes[i] == BlockTypeEnum::NO_BLOCK) { Game::INSTANCE->zDimension(dimensionId) ->placeBlock(pos, Game::INSTANCE->zGenerator()->generateSingleBlock( pos, dimensionId)); } else { Game::INSTANCE->zDimension(dimensionId)->sendBlockInfo(pos); } } Item* blockItem = zBlockType()->getItemFromBlock(this); if (blockItem) { Game::INSTANCE->spawnItem( location + Framework::Vec3(0.5f, 0.5f, 0.5f), dimensionId, blockItem); } deadAndRemoved = 1; for (MultiblockStructure* structure : structures) structure->onBlockRemoved(this); Game::INSTANCE->zDimension(dimensionId) ->placeBlock( getPos(), BlockTypeEnum::AIR); // this will be deleted here } } void Block::onDialogClosed(Framework::Text dialogId) {} void Block::broadcastModelInfoChange() { NetworkMessage* message = new NetworkMessage(); sendModelInfo(message); broadcastMessage(message); } void Block::broadcastMessage(NetworkMessage* message) { if (message->isEmpty()) { message->release(); } else { Dimension* dim = Game::INSTANCE->zDimension(getDimensionId()); if (dim) { Chunk* zChunk = dim->zChunk(Game::getChunkCenter(getPos().x, getPos().y)); if (zChunk) { zChunk->notifyObservers(message); } else { message->release(); } } else { message->release(); } } } void Block::broadcastPassableSpeedModifierChange() { NetworkMessage* message = new NetworkMessage(); message->addressBlock(this); char* msg = new char[6]; msg[0] = 3; msg[1] = passable; *(float*)(msg + 2) = speedModifier; message->setMessage(msg, 6); broadcastMessage(message); } 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::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 Game::INSTANCE->zBlockType(typeId)->getTargetUIML(); } void Block::sendModelInfo(NetworkMessage* zMessage) { // overwritten by some blocks } bool Block::interact(Item* zItem, Entity* zActor) { return false; } void Block::api(Framework::StreamReader* zRequest, NetworkMessage* zResponse) { char id = 0; zRequest->lese(&id, 1); switch (id) { case 0: // request model state sendModelInfo(zResponse); break; case 1: // dialog closed short nameLen; zRequest->lese((char*)&nameLen, 2); char* name = new char[nameLen + 1]; zRequest->lese(name, nameLen); name[nameLen] = 0; onDialogClosed(name); delete[] name; break; } } TickSourceType Block::isTickSource() const { return NONE; } bool Block::needsTick() const { return 1; } const BlockType* Block::zBlockType() const { return Game::INSTANCE->zBlockType(typeId); } bool Block::isTransparent() const { return transparent; } bool Block::isPassable() const { return passable; } bool Block::isInteractable(const Item* zItem) const { return interactable; } float Block::getHP() const { return hp; } float Block::getMaxHP() const { return maxHP; } float Block::getHardness() const { return hardness; } float Block::getSpeedModifier() const { return speedModifier; } const Framework::Vec3 Block::getPos() const { return (Framework::Vec3)location; } 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* zInfo) const { Dimension* dim = Game::INSTANCE->zDimension(getDimensionId()); if (dim) { Chunk* zChunk = dim->zChunk(Game::getChunkCenter(getPos().x, getPos().y)); if (zChunk) { NetworkMessage* changeMsg = new NetworkMessage(); changeMsg->addressBlock(this); Framework::InMemoryBuffer buffer; zInfo->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); zChunk->notifyObservers(changeMsg); } } } int Block::getMapColor() const { return mapColor; } BasicBlockItem::BasicBlockItem(int itemTypeId, int blockTypeId, Framework::Text name, PlaceableProof* placeableProof) : Item(itemTypeId, name), transparent(0), passable(0), hardness(1.f), speedModifier(1.f), interactable(1), placeableProof(placeableProof) { this->blockTypeId = blockTypeId; placeable = 1; } BasicBlockItem::~BasicBlockItem() { if (placeableProof) placeableProof->release(); } 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 && speedModifier == item->speedModifier && interactable == item->interactable; } return 0; } bool BasicBlockItem::canBePlacedAt( int dimensionId, Framework::Vec3 worldPos) const { return Item::canBePlacedAt(dimensionId, worldPos) && (!placeableProof || placeableProof->isPlacable(this, worldPos, dimensionId)); } BasicBlockItemType::BasicBlockItemType() : ItemType(), transparent(0), passable(0), hardness(1.f), speedModifier(1.f), blockTypeName(""), placeableProof(0) {} BasicBlockItemType::BasicBlockItemType(Framework::Text name, ModelInfo* model, bool transparent, bool passable, float hardness, float speedModifier, Framework::Text blockTypeName, PlaceableProof* placeableProof, int maxStackSize, Framework::RCArray groups) : ItemType(), transparent(transparent), passable(passable), hardness(hardness), speedModifier(speedModifier), blockTypeName(blockTypeName), placeableProof(placeableProof) { setName(name); setModel(model); setMaxStackSize(maxStackSize); for (Framework::Text* group : groups) addGroup(*group); } BasicBlockItemType::~BasicBlockItemType() { if (placeableProof) placeableProof->release(); } bool BasicBlockItemType::initialize(Game* zGame) { blockTypeId = zGame->getBlockTypeId(blockTypeName); return blockTypeId >= 0 && ItemType::initialize(zGame); } int BasicBlockItemType::getBlockTypeId() const { return blockTypeId; } void BasicBlockItemType::setTransparent(bool transparent) { this->transparent = transparent; } bool BasicBlockItemType::isTransparent() const { return transparent; } void BasicBlockItemType::setPassable(bool passable) { this->passable = passable; } bool BasicBlockItemType::isPassable() const { return passable; } void BasicBlockItemType::setHardness(float hardness) { this->hardness = hardness; } float BasicBlockItemType::getHardness() const { return hardness; } void BasicBlockItemType::setSpeedModifier(float speedModifier) { this->speedModifier = speedModifier; } float BasicBlockItemType::getSpeedModifier() const { return speedModifier; } void BasicBlockItemType::setBlockTypeName(Framework::Text blockTypeName) { this->blockTypeName = blockTypeName; } Framework::Text BasicBlockItemType::getBlockTypeName() const { return blockTypeName; } void BasicBlockItemType::setPlaceableProof(PlaceableProof* placeableProof) { if (this->placeableProof) this->placeableProof->release(); this->placeableProof = placeableProof; } PlaceableProof* BasicBlockItemType::zPlaceableProof() const { return placeableProof; } 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->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->speedModifier, 4); zWriter->schreibe((char*)&item->interactable, 1); } Item* BasicBlockItemType::createItem() const { BasicBlockItem* item = new BasicBlockItem(id, blockTypeId, name, placeableProof ? dynamic_cast(placeableProof->getThis()) : 0); item->transparent = transparent; item->passable = passable; item->hardness = hardness; item->speedModifier = speedModifier; item->interactable = 1; return item; } BasicBlockItemTypeFactory::BasicBlockItemTypeFactory() : ItemTypeFactoryBase() {} BasicBlockItemType* BasicBlockItemTypeFactory::createValue( Framework::JSON::JSONObject* zJson) const { return new BasicBlockItemType(); } void BasicBlockItemTypeFactory::fromJson( BasicBlockItemType* zResult, Framework::JSON::JSONObject* zJson) const { zResult->setTransparent(zJson->zValue("transparent")->asBool()->getBool()); zResult->setPassable(zJson->zValue("passable")->asBool()->getBool()); zResult->setHardness( (float)zJson->zValue("hardness")->asNumber()->getNumber()); zResult->setSpeedModifier( (float)zJson->zValue("speedModifier")->asNumber()->getNumber()); zResult->setBlockTypeName( zJson->zValue("blockType")->asString()->getString()); zResult->setPlaceableProof( zJson->zValue("placeableProof")->getType() == Framework::JSON::JSONType::OBJECT ? Game::INSTANCE->zTypeRegistry()->fromJson( zJson->zValue("placeableProof")) : 0); ItemTypeFactoryBase::fromJson(zResult, zJson); } void BasicBlockItemTypeFactory::toJson( BasicBlockItemType* zObject, Framework::JSON::JSONObject* zResult) const { zResult->addValue( "transparent", new Framework::JSON::JSONBool(zObject->isTransparent())); zResult->addValue( "passable", new Framework::JSON::JSONBool(zObject->isPassable())); zResult->addValue( "hardness", new Framework::JSON::JSONNumber(zObject->getHardness())); zResult->addValue("speedModifier", new Framework::JSON::JSONNumber(zObject->getSpeedModifier())); zResult->addValue("blockType", new Framework::JSON::JSONString(zObject->getBlockTypeName())); zResult->addValue("placeableProof", zObject->zPlaceableProof() ? Game::INSTANCE->zTypeRegistry()->toJson( zObject->zPlaceableProof()) : new Framework::JSON::JSONValue()); ItemTypeFactoryBase::toJson(zObject, zResult); } JSONObjectValidationBuilder* BasicBlockItemTypeFactory::addToValidator( JSONObjectValidationBuilder* builder) const { return ItemTypeFactoryBase::addToValidator( builder->withRequiredBool("transparent") ->withDefault(false) ->finishBool() ->withRequiredBool("passable") ->withDefault(false) ->finishBool() ->withRequiredNumber("hardness") ->withDefault(1.f) ->finishNumber() ->withRequiredNumber("speedModifier") ->withDefault(1.f) ->finishNumber() ->withRequiredString("blockType") ->finishString() ->withRequiredAttribute("placeableProof", Game::INSTANCE->zTypeRegistry()->getValidator()) ->withRequiredObject("placeableProof") ->withDefaultNull() ->whichCanBeNull() ->finishObject()); } Framework::Text BasicBlockItemTypeFactory::getTypeToken() const { return "placeable"; }