From b570bd594e534b9883df0aad3b3d546631ad4334 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Mon, 24 Aug 2026 23:10:55 +0200 Subject: [PATCH] feat(core): add piston extend and retract block-pushing mechanics --- core/include/vf/Piston.h | 24 +++++++++ core/src/Piston.cpp | 103 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 core/include/vf/Piston.h create mode 100644 core/src/Piston.cpp diff --git a/core/include/vf/Piston.h b/core/include/vf/Piston.h new file mode 100644 index 0000000..3caaddc --- /dev/null +++ b/core/include/vf/Piston.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include "World.h" +#include "Block.h" + +namespace vf { + +constexpr int pistonMaxPush = 12; + +struct PistonMoveResult { + bool moved = false; + int blocksPushed = 0; + std::vector destroyed; +}; + +bool isPistonImmovable(Block b); +bool isPistonFragile(Block b); +bool isPistonBlock(Block b); + +PistonMoveResult extendPiston(World& world, const Vec3i& pos, Face facing); +PistonMoveResult retractPiston(World& world, const Vec3i& pos, Face facing); + +} // namespace vf diff --git a/core/src/Piston.cpp b/core/src/Piston.cpp new file mode 100644 index 0000000..7afc744 --- /dev/null +++ b/core/src/Piston.cpp @@ -0,0 +1,103 @@ +#include "vf/Piston.h" + +namespace vf { + +namespace { + +Vec3i faceOffset(Face f) { + switch (f) { + case Face::PosX: return {1, 0, 0}; + case Face::NegX: return {-1, 0, 0}; + case Face::PosY: return {0, 1, 0}; + case Face::NegY: return {0, -1, 0}; + case Face::PosZ: return {0, 0, 1}; + case Face::NegZ: return {0, 0, -1}; + } + return {0, 0, 0}; +} + +} // namespace + +bool isPistonImmovable(Block b) { return b == Block::Bedrock || b == Block::Obsidian; } + +bool isPistonFragile(Block b) { + switch (b) { + case Block::Torch: + case Block::RedstoneTorch: + case Block::RedstoneWire: + case Block::Lever: + case Block::Button: + return true; + default: + return false; + } +} + +bool isPistonBlock(Block b) { return b == Block::Piston || b == Block::PistonSticky; } + +PistonMoveResult extendPiston(World& world, const Vec3i& pos, Face facing) { + PistonMoveResult result; + if (!isPistonBlock(world.blockAt(pos.x, pos.y, pos.z))) return result; + + Vec3i offset = faceOffset(facing); + Vec3i headPos = pos + offset; + Vec3i cursor = headPos; + std::vector chain; + Vec3i fragileAt{}; + bool hasFragile = false; + + for (int step = 0; step <= pistonMaxPush; ++step) { + Block b = world.blockAt(cursor.x, cursor.y, cursor.z); + if (b == Block::Air) break; + if (isPistonImmovable(b)) return result; + if (isPistonFragile(b)) { + fragileAt = cursor; + hasFragile = true; + break; + } + if (static_cast(chain.size()) == pistonMaxPush) return result; + chain.push_back(cursor); + cursor = cursor + offset; + } + + for (auto it = chain.rbegin(); it != chain.rend(); ++it) { + Vec3i dest = *it + offset; + Block moving = world.blockAt(it->x, it->y, it->z); + world.setBlock(dest.x, dest.y, dest.z, moving); + } + if (hasFragile) { + world.setBlock(fragileAt.x, fragileAt.y, fragileAt.z, Block::Air); + result.destroyed.push_back(fragileAt); + } + world.setBlock(headPos.x, headPos.y, headPos.z, Block::PistonHead); + + result.moved = true; + result.blocksPushed = static_cast(chain.size()); + return result; +} + +PistonMoveResult retractPiston(World& world, const Vec3i& pos, Face facing) { + PistonMoveResult result; + Block base = world.blockAt(pos.x, pos.y, pos.z); + if (!isPistonBlock(base)) return result; + + Vec3i offset = faceOffset(facing); + Vec3i headPos = pos + offset; + if (world.blockAt(headPos.x, headPos.y, headPos.z) != Block::PistonHead) return result; + + world.setBlock(headPos.x, headPos.y, headPos.z, Block::Air); + result.moved = true; + + if (base == Block::PistonSticky) { + Vec3i pullPos = headPos + offset; + Block pullBlock = world.blockAt(pullPos.x, pullPos.y, pullPos.z); + if (pullBlock != Block::Air && !isPistonImmovable(pullBlock) && !isPistonFragile(pullBlock)) { + world.setBlock(headPos.x, headPos.y, headPos.z, pullBlock); + world.setBlock(pullPos.x, pullPos.y, pullPos.z, Block::Air); + result.blocksPushed = 1; + } + } + return result; +} + +} // namespace vf