feat(core): add piston extend and retract block-pushing mechanics

This commit is contained in:
dualfroz committed 2026-08-24 23:10:55 +02:00
1 parent cff1e80fe8
commit b570bd594e
2 files changed
+127

No files matched your search

+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include <vector>
#include "World.h"
#include "Block.h"
namespace vf {
constexpr int pistonMaxPush = 12;
struct PistonMoveResult {
bool moved = false;
int blocksPushed = 0;
std::vector<Vec3i> 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
+103
View File
@@ -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<Vec3i> 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<int>(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<int>(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