VoxelEngine/src/logic/BlocksController.cpp
2024-06-08 16:58:05 +03:00

203 lines
6.1 KiB
C++

#include "BlocksController.hpp"
#include "../voxels/voxel.hpp"
#include "../voxels/Block.hpp"
#include "../voxels/Chunk.hpp"
#include "../voxels/Chunks.hpp"
#include "../world/Level.hpp"
#include "../world/World.hpp"
#include "../content/Content.hpp"
#include "../lighting/Lighting.hpp"
#include "../util/timeutil.hpp"
#include "../maths/fastmaths.hpp"
#include "../items/Inventory.hpp"
#include "../items/Inventories.hpp"
#include "scripting/scripting.hpp"
Clock::Clock(int tickRate, int tickParts)
: tickRate(tickRate),
tickParts(tickParts) {
}
bool Clock::update(float delta) {
tickTimer += delta;
float delay = 1.0f / float(tickRate);
if (tickTimer > delay || tickPartsUndone) {
if (tickPartsUndone) {
tickPartsUndone--;
} else {
tickTimer = fmod(tickTimer, delay);
tickPartsUndone = tickParts-1;
}
return true;
}
return false;
}
int Clock::getParts() const {
return tickParts;
}
int Clock::getPart() const {
return tickParts-tickPartsUndone-1;
}
int Clock::getTickRate() const {
return tickRate;
}
int Clock::getTickId() const {
return tickId;
}
BlocksController::BlocksController(Level* level, uint padding)
: level(level),
chunks(level->chunks.get()),
lighting(level->lighting.get()),
randTickClock(20, 3),
blocksTickClock(20, 1),
worldTickClock(20, 1),
padding(padding) {
}
void BlocksController::updateSides(int x, int y, int z) {
updateBlock(x-1, y, z);
updateBlock(x+1, y, z);
updateBlock(x, y-1, z);
updateBlock(x, y+1, z);
updateBlock(x, y, z-1);
updateBlock(x, y, z+1);
}
void BlocksController::breakBlock(Player* player, const Block* def, int x, int y, int z) {
chunks->set(x,y,z, 0, {});
lighting->onBlockSet(x,y,z, 0);
if (def->rt.funcsset.onbroken) {
scripting::on_block_broken(player, def, x, y, z);
}
updateSides(x, y, z);
}
void BlocksController::updateBlock(int x, int y, int z) {
voxel* vox = chunks->get(x, y, z);
if (vox == nullptr)
return;
const Block* def = level->content->getIndices()->getBlockDef(vox->id);
if (def->grounded && !chunks->isSolidBlock(x, y-1, z)) {
breakBlock(nullptr, def, x, y, z);
return;
}
if (def->rt.funcsset.update) {
scripting::update_block(def, x, y, z);
}
}
void BlocksController::update(float delta) {
if (randTickClock.update(delta)) {
randomTick(randTickClock.getPart(), randTickClock.getParts());
}
if (blocksTickClock.update(delta)) {
onBlocksTick(blocksTickClock.getPart(), blocksTickClock.getParts());
}
if (worldTickClock.update(delta)) {
scripting::on_world_tick();
}
}
void BlocksController::onBlocksTick(int tickid, int parts) {
auto content = level->content;
auto indices = content->getIndices();
int tickRate = blocksTickClock.getTickRate();
for (size_t id = 0; id < indices->countBlockDefs(); id++) {
if ((id + tickid) % parts != 0)
continue;
auto def = indices->getBlockDef(id);
auto interval = def->tickInterval;
if (def->rt.funcsset.onblockstick && tickid / parts % interval == 0) {
scripting::on_blocks_tick(def, tickRate / interval);
}
}
}
void BlocksController::randomTick(int tickid, int parts) {
const int w = chunks->w;
const int d = chunks->d;
int segments = 4;
int segheight = CHUNK_H / segments;
auto indices = level->content->getIndices();
for (uint z = padding; z < d-padding; z++){
for (uint x = padding; x < w-padding; x++){
int index = z * w + x;
if ((index + tickid) % parts != 0) {
continue;
}
auto& chunk = chunks->chunks[index];
if (chunk == nullptr || !chunk->flags.lighted) {
continue;
}
for (int s = 0; s < segments; s++) {
for (int i = 0; i < 4; i++) {
int bx = random.rand() % CHUNK_W;
int by = random.rand() % segheight + s * segheight;
int bz = random.rand() % CHUNK_D;
const voxel& vox = chunk->voxels[(by * CHUNK_D + bz) * CHUNK_W + bx];
Block* block = indices->getBlockDef(vox.id);
if (block->rt.funcsset.randupdate) {
scripting::random_update_block(
block,
chunk->x * CHUNK_W + bx, by,
chunk->z * CHUNK_D + bz
);
}
}
}
}
}
}
int64_t BlocksController::createBlockInventory(int x, int y, int z) {
auto chunk = chunks->getChunkByVoxel(x, y, z);
if (chunk == nullptr) {
return 0;
}
int lx = x - chunk->x * CHUNK_W;
int lz = z - chunk->z * CHUNK_D;
auto inv = chunk->getBlockInventory(lx, y, lz);
if (inv == nullptr) {
auto indices = level->content->getIndices();
auto def = indices->getBlockDef(chunk->voxels[vox_index(lx, y, lz)].id);
int invsize = def->inventorySize;
if (invsize == 0) {
return 0;
}
inv = level->inventories->create(invsize);
chunk->addBlockInventory(inv, lx, y, lz);
}
return inv->getId();
}
void BlocksController::bindInventory(int64_t invid, int x, int y, int z) {
auto chunk = chunks->getChunkByVoxel(x, y, z);
if (chunk == nullptr) {
throw std::runtime_error("block does not exists");
}
if (invid <= 0) {
throw std::runtime_error("unable to bind virtual inventory");
}
int lx = x - chunk->x * CHUNK_W;
int lz = z - chunk->z * CHUNK_D;
chunk->addBlockInventory(level->inventories->get(invid), lx, y, lz);
}
void BlocksController::unbindInventory(int x, int y, int z) {
auto chunk = chunks->getChunkByVoxel(x, y, z);
if (chunk == nullptr) {
throw std::runtime_error("block does not exists");
}
int lx = x - chunk->x * CHUNK_W;
int lz = z - chunk->z * CHUNK_D;
chunk->removeBlockInventory(lx, y, lz);
}