diff --git a/core/include/vf/Farming.h b/core/include/vf/Farming.h new file mode 100644 index 0000000..8999986 --- /dev/null +++ b/core/include/vf/Farming.h @@ -0,0 +1,106 @@ +#pragma once + +#include +#include +#include +#include "World.h" +#include "Math.h" + +namespace vf { + +enum class CropType : uint8_t { + None = 0, + Wheat = 1, + Carrot = 2, + Potato = 3, + PumpkinStem = 4, + MelonStem = 5, + SugarCane = 6, + Cactus = 7, + Sapling = 8, + Vine = 9, +}; + +const char* cropTypeName(CropType type); +int cropMaxStage(CropType type); + +struct CropState { + CropType type = CropType::None; + int stage = 0; + int maxStage = 0; + + bool mature() const { return stage >= maxStage; } +}; + +struct HarvestDrop { + Block item; + int count; +}; + +struct FarmingConfig { + int hydrationRadius = 4; + int minGrowthLight = 9; + // Growth attempt succeeds when rng % denominator < points; dry farmland + // scores 1 point, moist farmland 3, so hydration roughly triples the rate. + uint32_t growthDenominator = 6; + uint32_t saplingDenominator = 8; + uint32_t verticalDenominator = 6; + int caneMaxHeight = 3; + int cactusMaxHeight = 3; + int vineMaxLength = 4; + int treeTrunkMin = 4; + int treeTrunkMax = 6; + // Bonemeal shoves a crop this many stages forward per application. + int bonemealMinStages = 2; + int bonemealMaxStages = 5; + // Odds (in 100ths) a bonemealed sapling grows into a tree at once. + uint32_t bonemealSaplingChance = 45; +}; + +// Deterministic crop simulation with its own position->state registry, kept +// separate from the block-tick scheduler. Ground crops (wheat, carrot, potato) +// advance on random ticks gated by sky light and accelerated by farmland +// hydration; stems mature and set fruit on a free neighbour; sugar cane grows +// beside water and cactus only where it has no solid neighbours; a mature +// sapling stamps a tree. Harvesting removes the crop and yields drops. A given +// seed and tick order always produce the same growth history. +class Farming { +public: + explicit Farming(uint32_t seed = 1u, const FarmingConfig& config = {}); + + void plant(World& world, const Vec3i& pos, CropType type); + bool randomTick(World& world, const Vec3i& pos, int skyLight); + int growStep(World& world, int skyLight); + + bool applyBonemeal(World& world, const Vec3i& pos); + bool trample(World& world, const Vec3i& farmland); + + std::vector harvest(World& world, const Vec3i& pos); + + bool isHydrated(const World& world, const Vec3i& farmland) const; + + const CropState* stateAt(const Vec3i& pos) const; + std::size_t trackedCount() const { return crops.size(); } + + const FarmingConfig& settings() const { return config; } + +private: + enum class GrowthResult { None, Advanced, Removed }; + + uint32_t nextRandom(); + bool rollGrowth(uint32_t denominator, int points); + + GrowthResult growGroundCrop(World& world, const Vec3i& pos, CropState& state, int skyLight); + GrowthResult growStem(World& world, const Vec3i& pos, CropState& state, int skyLight); + GrowthResult growColumn(World& world, const Vec3i& pos, CropState& state, bool cane); + GrowthResult growVine(World& world, const Vec3i& pos, CropState& state); + GrowthResult growSapling(World& world, const Vec3i& pos, CropState& state, int skyLight); + + void stampTree(World& world, const Vec3i& base); + + FarmingConfig config; + uint32_t rngState; + std::unordered_map crops; +}; + +} // namespace vf