Browse Source

add general fluid blocks and water fluid

Kolja Strohm 1 year ago
parent
commit
a530c30b61

+ 3 - 0
FactoryCraft/BiomGenerator.h

@@ -31,6 +31,9 @@ public:
     virtual Framework::Either<Block*, int> generateCaveBlock(
         int x, int y, int z)
         = 0;
+    virtual Framework::Either<Block*, int> generateUnderWaterBlock(
+        int x, int y, int z, int surfaceHeight, Chunk* partialGeneratedChunk)
+        = 0;
     virtual void setSeed(int seed);
     virtual Noise* zHeightMapNoise() = 0;
     const Framework::RCArray<GenerationTemplate>& getTemplates();

+ 2 - 1
FactoryCraft/BlockType.h

@@ -40,8 +40,9 @@ public:
     static const int FARMLAND = 23;
     static const int WHEAT_SEED = 24;
     static const int WHEAT = 25;
+    static const int WATER = 26;
 
-    static const int MAX_VALUE = 25;
+    static const int MAX_VALUE = 26;
 };
 
 class BlockType : public virtual Framework::ReferenceCounter

+ 1 - 0
FactoryCraft/Constants.h

@@ -5,6 +5,7 @@
 #define WORLD_HEIGHT             500
 #define BIOM_GENERATION_Z_OFFSET 100
 #define AIR_LEVEL_Z_OFFSET       99
+#define WATER_LEVEL              150
 #define MIN_AIR_LEVEL            100
 #define MAX_AIR_LEVEL            400
 #define NUM_TICK_WORKERS         4

+ 23 - 3
FactoryCraft/DimensionGenerator.cpp

@@ -219,7 +219,12 @@ Chunk* DimensionGenerator::generateChunk(int centerX, int centerY)
                     else if (z < height)
                         generated = biom->generateBelowSurfaceBlock(
                             x + centerX, y + centerY, z);
-                    else if (z >= height)
+                    else if (z >= height && z < WATER_LEVEL)
+                    {
+                        generated = biom->generateUnderWaterBlock(
+                            x + centerX, y + centerY, z, height, chunk);
+                    }
+                    else
                     {
                         generated = biom->generateAboveSurfaceBlock(
                             x + centerX, y + centerY, z, height, chunk);
@@ -288,10 +293,25 @@ Framework::Either<Block*, int> DimensionGenerator::generateBlock(
     else if (location.z < height)
         return biom->generateBelowSurfaceBlock(
             location.x, location.y, location.z);
+    else if (location.z >= height && location.z < WATER_LEVEL)
+    {
+        return biom->generateUnderWaterBlock(location.x,
+            location.y,
+            location.z,
+            height,
+            Game::INSTANCE->zDimension(getDimensionId())
+                ->zChunk(
+                    Game::INSTANCE->getChunkCenter(location.x, location.y)));
+    }
     if (location.z >= height)
     {
-        return biom->generateAboveSurfaceBlock(
-            location.x, location.x, location.z, height, Game::INSTANCE->zDimension(getDimensionId())->zChunk(Game::INSTANCE->getChunkCenter(location.x, location.y)));
+        return biom->generateAboveSurfaceBlock(location.x,
+            location.x,
+            location.z,
+            height,
+            Game::INSTANCE->zDimension(getDimensionId())
+                ->zChunk(
+                    Game::INSTANCE->getChunkCenter(location.x, location.y)));
     }
     return BlockTypeEnum::AIR;
 }

+ 3 - 1
FactoryCraft/FactoryCraft.vcxproj

@@ -81,7 +81,7 @@
     <OutDir>/home/kolja/projects/Server/$(ProjectName)/debug/</OutDir>
     <IntDir>/home/kolja/projects/Server/$(ProjectName)/debug/</IntDir>
     <RemoteProjectDir>$(RemoteRootDir)/Server/$(ProjectName)/debug</RemoteProjectDir>
-    <IncludePath>..\..\..\..\..\Allgemein\Framework;../../../Framework/debug;..\..\..\..\..\Allgemein\Network\Network;../../../Network/debug/Network;../../../KsgScript/debug/KsgScript;..\..\..\..\..\Allgemein\KSGScript\KSGScript\Include;$(IncludePath)</IncludePath>
+    <IncludePath>..\..\..\..\..\Allgemein\Framework;../../../Framework/debug;..\..\..\..\..\Allgemein\Network\Network;../../../Network/debug/Network;../../../KsgScript/debug/KsgScript;..\..\..\..\..\Allgemein\KSGScript\KSGScript\Include;D:\visual studio\Common7\IDE\VC\Linux\include\usr\include\x86_64-linux-gnu\bits;$(IncludePath)</IncludePath>
     <RemoteTargetPath>$(RemoteProjectDir)/$(TargetName)$(TargetExt)</RemoteTargetPath>
     <RemoteLinkLocalCopyOutput>true</RemoteLinkLocalCopyOutput>
     <RemoteIntRelDir>/home/kolja/projects/Server/$(ProjectName)/debug/</RemoteIntRelDir>
@@ -117,6 +117,7 @@
     <ClInclude Include="EntityType.h" />
     <ClInclude Include="FastNoiseLite.h" />
     <ClInclude Include="FastNoiseWrapper.h" />
+    <ClInclude Include="FluidBlock.h" />
     <ClInclude Include="Game.h" />
     <ClInclude Include="GeneratedStructure.h" />
     <ClInclude Include="GenerationTemplate.h" />
@@ -183,6 +184,7 @@
     <ClCompile Include="EntityRemovedUpdate.cpp" />
     <ClCompile Include="EntityType.cpp" />
     <ClCompile Include="FastNoiseWrapper.cpp" />
+    <ClCompile Include="FluidBlock.cpp" />
     <ClCompile Include="Game.cpp" />
     <ClCompile Include="GeneratedStructure.cpp" />
     <ClCompile Include="GenerationTemplate.cpp" />

+ 9 - 0
FactoryCraft/FactoryCraft.vcxproj.filters

@@ -73,6 +73,9 @@
     <Filter Include="world\blocks\plants">
       <UniqueIdentifier>{4d1a59d1-2b8c-447f-b1fb-ae6e07b60983}</UniqueIdentifier>
     </Filter>
+    <Filter Include="world\blocks\fluids">
+      <UniqueIdentifier>{677a56aa-6e9d-465c-bdcf-83118b2fc3d2}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Chunk.h">
@@ -276,6 +279,9 @@
     <ClInclude Include="GrowingPlant.h">
       <Filter>world\blocks\plants</Filter>
     </ClInclude>
+    <ClInclude Include="FluidBlock.h">
+      <Filter>world\blocks\fluids</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Server.cpp">
@@ -464,5 +470,8 @@
     <ClCompile Include="GrowingPlant.cpp">
       <Filter>world\blocks\plants</Filter>
     </ClCompile>
+    <ClCompile Include="FluidBlock.cpp">
+      <Filter>world\blocks\fluids</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 249 - 0
FactoryCraft/FluidBlock.cpp

@@ -0,0 +1,249 @@
+#include "FluidBlock.h"
+
+#include "Game.h"
+
+FluidBlock::FluidBlock(int typeId, Framework::Vec3<int> pos)
+    : Block(typeId, 0, pos, 0)
+{
+    transparent = 1;
+    passable = 1;
+    hp = 1;
+    maxHP = 1;
+    hardness = -1.f;
+    speedModifier = 0.5f;
+    tickSource = 1;
+    interactable = 0;
+    fluidAmount = 0;
+}
+
+FluidBlock::~FluidBlock() {}
+
+bool FluidBlock::onTick(TickQueue* zQueue, int numTicks, bool& blocked)
+{
+    nextFlow = 0;
+    int bottom = neighbourTypes[getDirectionIndex(Direction::BOTTOM)];
+    if (bottom != zBlockType()->getId())
+    {
+        if (bottom == BlockTypeEnum::AIR)
+        {
+            nextFlow |= 1 << getDirectionIndex(Direction::BOTTOM);
+        }
+        else
+        {
+            if (zNeighbours[getDirectionIndex(Direction::BOTTOM)]
+                && zNeighbours[getDirectionIndex(Direction::BOTTOM)]
+                           ->zBlockType()
+                           ->getId()
+                       == typeId)
+            {
+                Inventory* array[2]
+                    = {zNeighbours[getDirectionIndex(Direction::BOTTOM)], this};
+                MultipleInventoryLock lock(array, 2);
+                FluidBlock* below = dynamic_cast<FluidBlock*>(
+                    zNeighbours[getDirectionIndex(Direction::BOTTOM)]);
+                if (below->fluidAmount < 1000)
+                {
+                    int transferAmount
+                        = MIN(this->fluidAmount, 1000 - below->fluidAmount);
+                    below->fluidAmount += transferAmount;
+                    this->fluidAmount -= transferAmount;
+                }
+            }
+            int neighborCount = 0;
+            Inventory* others[4];
+            for (int i = 1; i < 5; i++)
+            {
+                if (neighbourTypes[i] == BlockTypeEnum::FLUID && zNeighbours[i]
+                    && zNeighbours[i]->zBlockType()->getId() == typeId)
+                {
+                    others[neighborCount] = zNeighbours[i];
+                    neighborCount++;
+                }
+            }
+            if (neighborCount > 0)
+            {
+                Inventory* array[5]
+                    = {this, others[0], others[1], others[2], others[3]};
+                MultipleInventoryLock lock(array, neighborCount + 1);
+                // all neighbot fluid blocks are locked now
+                FluidBlock* otherFluids[4];
+                for (int i = 0; i < neighborCount; i++)
+                {
+                    otherFluids[i] = dynamic_cast<FluidBlock*>(others[i]);
+                }
+                // order other fluids increasing by fluid amount
+                sortByAmound(otherFluids, neighborCount);
+                int distCount = 0;
+                int targetAmount = 0;
+                for (int i = 1; i <= neighborCount; i++)
+                {
+                    int fluidAmount = this->fluidAmount;
+                    for (int j = 0; j < i; j++)
+                    {
+                        fluidAmount += otherFluids[j]->fluidAmount;
+                    }
+                    if (fluidAmount / (i + 1) > this->fluidAmount)
+                    {
+                        targetAmount = fluidAmount / (i + 1);
+                        distCount = i;
+                    }
+                    else
+                    {
+                        break;
+                    }
+                }
+                for (int i = 0; i < distCount; i++)
+                {
+                    int transferAmount
+                        = targetAmount - otherFluids[i]->fluidAmount;
+                    otherFluids[i]->fluidAmount += transferAmount;
+                    this->fluidAmount -= transferAmount;
+                }
+                // TODO: distribute fluids
+            } // unlock
+            neighborCount = 0;
+            for (int i = 1; i < 5; i++)
+            {
+                int neighbor = neighbourTypes[i];
+                if (neighbor == BlockTypeEnum::AIR
+                    && fluidAmount > neighborCount + 1)
+                {
+                    nextFlow |= 1 << i;
+                    neighborCount++;
+                }
+            }
+        }
+    }
+}
+
+void FluidBlock::onPostTick()
+{
+    if (nextFlow != 0)
+    {
+        Game::INSTANCE->doLater([this]() {
+            for (int i = 0; i < 6; i++)
+            {
+                if ((nextFlow | (1 << i)) == nextFlow)
+                {
+                    Vec3<int> pos
+                        = getPos() + getDirection(getDirectionFromIndex(i));
+                    if (neighbourTypes[i] == BlockTypeEnum::AIR)
+                    {
+                        FluidBlock* spawn = new FluidBlock(typeId, pos);
+                        spawn->fluidAmount = 1;
+                        Game::INSTANCE->zDimension(getDimensionId())
+                            ->placeBlock(pos, spawn);
+                        fluidAmount--;
+                    }
+                }
+            }
+            if (fluidAmount == 0)
+            {
+                Game::INSTANCE->zDimension(getDimensionId())
+                    ->placeBlock(getPos(),
+                        StaticRegistry<BlockType>::INSTANCE
+                            .zElement(BlockTypeEnum::AIR)
+                            ->createBlockAt(getPos(), 0));
+            }
+        });
+    }
+}
+
+void FluidBlock::sortByAmound(FluidBlock** array, int count)
+{
+    if (count == 2)
+    {
+        if (array[0]->fluidAmount > array[1]->fluidAmount)
+        {
+            FluidBlock* tmp = array[0];
+            array[0] = array[1];
+            array[1] = tmp;
+        }
+    }
+    else if (count == 3)
+    {
+        if (array[0]->fluidAmount > array[1]->fluidAmount)
+        {
+            FluidBlock* tmp = array[0];
+            array[0] = array[1];
+            array[1] = tmp;
+        }
+        if (array[1]->fluidAmount > array[2]->fluidAmount)
+        {
+            FluidBlock* tmp = array[1];
+            array[1] = array[2];
+            array[2] = tmp;
+        }
+        if (array[0]->fluidAmount > array[1]->fluidAmount)
+        {
+            FluidBlock* tmp = array[0];
+            array[0] = array[1];
+            array[1] = tmp;
+        }
+    }
+    else if (count == 4)
+    {
+        if (array[0]->fluidAmount > array[1]->fluidAmount)
+        {
+            FluidBlock* tmp = array[0];
+            array[0] = array[1];
+            array[1] = tmp;
+        }
+        if (array[2]->fluidAmount > array[3]->fluidAmount)
+        {
+            FluidBlock* tmp = array[2];
+            array[2] = array[3];
+            array[3] = tmp;
+        }
+        if (array[0]->fluidAmount > array[2]->fluidAmount)
+        {
+            FluidBlock* tmp = array[0];
+            array[0] = array[2];
+            array[2] = tmp;
+        }
+        if (array[1]->fluidAmount > array[3]->fluidAmount)
+        {
+            FluidBlock* tmp = array[1];
+            array[1] = array[3];
+            array[3] = tmp;
+        }
+        if (array[1]->fluidAmount > array[2]->fluidAmount)
+        {
+            FluidBlock* tmp = array[1];
+            array[1] = array[2];
+            array[2] = tmp;
+        }
+    }
+}
+
+FluidBlockType::FluidBlockType(int id, ModelInfo model, const char* name)
+    : BlockType(id, 0, model, 1, 10, 0, name)
+{}
+
+void FluidBlockType::loadSuperBlock(
+    Block* zBlock, Framework::StreamReader* zReader, int dimensionId) const
+{
+    FluidBlock* block = dynamic_cast<FluidBlock*>(zBlock);
+    zReader->lese((char*)&block->fluidAmount, 2);
+    BlockType::loadSuperBlock(zBlock, zReader, dimensionId);
+}
+
+void FluidBlockType::saveSuperBlock(
+    Block* zBlock, Framework::StreamWriter* zWriter) const
+{
+    FluidBlock* block = dynamic_cast<FluidBlock*>(zBlock);
+    zWriter->schreibe((char*)&block->fluidAmount, 2);
+    BlockType::saveSuperBlock(zBlock, zWriter);
+}
+
+Item* FluidBlockType::createItem() const
+{
+    return 0;
+}
+
+Block* FluidBlockType::createBlock(Framework::Vec3<int> position) const
+{
+    FluidBlock* result = new FluidBlock(getId(), position);
+    result->fluidAmount = 1000;
+    return result;
+}

+ 42 - 0
FactoryCraft/FluidBlock.h

@@ -0,0 +1,42 @@
+#pragma once
+
+#include "Block.h"
+#include "TickQueue.h"
+
+class FluidBlockType;
+
+class FluidBlock : public Block
+{
+private:
+    Directions flowDir;
+    short fluidAmount;
+    int nextFlow;
+
+protected:
+    virtual bool onTick(
+        TickQueue* zQueue, int numTicks, bool& blocked) override;
+    virtual void onPostTick() override;
+
+public:
+    FluidBlock(int typeId, Framework::Vec3<int> pos);
+    virtual ~FluidBlock();
+
+    static void sortByAmound(FluidBlock** array, int count);
+
+    friend FluidBlockType;
+};
+
+class FluidBlockType : public BlockType
+{
+protected:
+    virtual void loadSuperBlock(Block* zBlock,
+        Framework::StreamReader* zReader,
+        int dimensionId) const override;
+    virtual void saveSuperBlock(
+        Block* zBlock, Framework::StreamWriter* zWriter) const override;
+    virtual Item* createItem() const override;
+    virtual Block* createBlock(Framework::Vec3<int> position) const override;
+
+public:
+    FluidBlockType(int id, ModelInfo model, const char* name);
+};

+ 14 - 7
FactoryCraft/GrasslandBiom.cpp

@@ -2,12 +2,12 @@
 
 #include "BasicBlocks.h"
 #include "BlockType.h"
+#include "Chunk.h"
+#include "Constants.h"
 #include "FastNoiseLite.h"
 #include "FastNoiseWrapper.h"
 #include "NoBlock.h"
 #include "TreeTemplate.h"
-#include "Chunk.h"
-#include "Constants.h"
 
 GrasslandBiom::GrasslandBiom()
     : BiomGenerator()
@@ -50,9 +50,10 @@ Framework::Either<Block*, int> GrasslandBiom::generateAboveSurfaceBlock(
     int cy = y % CHUNK_SIZE;
     if (cx < 0) cx += CHUNK_SIZE;
     if (cy < 0) cy += CHUNK_SIZE;
-    auto below = partialGeneratedChunk->zBlockAt(
-        Framework::Vec3<int>(cx, cy, z - 1));
-    if ((below.isA() && below.getA()->zBlockType()->getId() == BlockTypeEnum::DIRT)
+    auto below
+        = partialGeneratedChunk->zBlockAt(Framework::Vec3<int>(cx, cy, z - 1));
+    if ((below.isA()
+            && below.getA()->zBlockType()->getId() == BlockTypeEnum::DIRT)
         || (below.isB() && below.getB() == BlockTypeEnum::DIRT))
         return BlockTypeEnum::GRASS;
     return BlockTypeEnum::AIR;
@@ -76,8 +77,7 @@ Framework::Either<Block*, int> GrasslandBiom::generateSurfaceBlock(
 Framework::Either<Block*, int> GrasslandBiom::generateBelowSurfaceBlock(
     int x, int y, int z)
 {
-    if (undergroundDirdNoise->getNoise((double)x, (double)y, (double)z)
-        < 0.35)
+    if (undergroundDirdNoise->getNoise((double)x, (double)y, (double)z) < 0.35)
     {
         return BlockTypeEnum::DIRT;
     }
@@ -95,6 +95,13 @@ Framework::Either<Block*, int> GrasslandBiom::generateCaveBlock(
     return BlockTypeEnum::AIR;
 }
 
+Framework::Either<Block*, int> GrasslandBiom::generateUnderWaterBlock(
+    int x, int y, int z, int surfaceHeight, Chunk* partialGeneratedChunk)
+{
+    return StaticRegistry<BlockType>::INSTANCE.zElement(BlockTypeEnum::WATER)
+        ->createBlockAt(Framework::Vec3<int>(x, y, z), 0);
+}
+
 void GrasslandBiom::setSeed(int seed)
 {
     BiomGenerator::setSeed(seed);

+ 11 - 3
FactoryCraft/GrasslandBiom.h

@@ -10,19 +10,27 @@ class GrasslandBiom : public BiomGenerator
     Noise* surfaceSandNoise;
     Noise* undergroundGravelNoise;
     // TODO: Noise* anySmallOreNoise;
-	// TODO: add water rivers
+    // TODO: add water rivers
 
 public:
     GrasslandBiom();
     ~GrasslandBiom();
-    Framework::Either<Block*, int> generateAboveSurfaceBlock(
-        int x, int y, int z, int surfaceHeight, Chunk *partialGeneratedChunk) override;
+    Framework::Either<Block*, int> generateAboveSurfaceBlock(int x,
+        int y,
+        int z,
+        int surfaceHeight,
+        Chunk* partialGeneratedChunk) override;
     Framework::Either<Block*, int> generateSurfaceBlock(
         int x, int y, int z) override;
     Framework::Either<Block*, int> generateBelowSurfaceBlock(
         int x, int y, int z) override;
     Framework::Either<Block*, int> generateCaveBlock(
         int x, int y, int z) override;
+    Framework::Either<Block*, int> generateUnderWaterBlock(int x,
+        int y,
+        int z,
+        int surfaceHeight,
+        Chunk* partialGeneratedChunk) override;
     Noise* zHeightMapNoise() override;
     void setSeed(int seed) override;
 };

+ 85 - 0
FactoryCraft/Inventory.cpp

@@ -162,6 +162,91 @@ void InventoryInteraction::pushItems(int count, ItemFilter* zFilter)
     transaction(current, other, zFilter, dir, getOppositeDirection(dir), count);
 }
 
+MultipleInventoryLock::MultipleInventoryLock(Inventory** inventories, int count)
+    : locked(0),
+      count(count),
+      inventories(new Inventory*[count])
+{
+    // sort given inventories in locking order
+    bool used[count];
+    memset(&used, 0, count);
+    // TODO: use a performant sorting algorithm
+    for (int i = 0; i < count; i++)
+    {
+        Inventory* min = 0;
+        int minJ = 0;
+        for (int j = 0; j < count; j++)
+        {
+            if (!used[j])
+            {
+                if (!min)
+                {
+                    min = inventories[j];
+                    minJ = j;
+                    continue;
+                }
+                if (inventories[j]->location.x < min->location.x)
+                {
+                    min = inventories[j];
+                    minJ = j;
+                    continue;
+                }
+                if (inventories[j]->location.x == min->location.x)
+                {
+                    if (inventories[j]->location.y < min->location.y)
+                    {
+                        min = inventories[j];
+                        minJ = j;
+                        continue;
+                    }
+                    if (inventories[j]->location.y == min->location.y)
+                    {
+                        if (inventories[j]->location.z < min->location.z)
+                        {
+                            min = inventories[j];
+                            minJ = j;
+                            continue;
+                        }
+                    }
+                }
+            }
+        }
+        inventories[i] = min;
+        used[minJ] = 1;
+    }
+    lock();
+}
+
+MultipleInventoryLock::~MultipleInventoryLock()
+{
+    unlock();
+    delete[] inventories;
+}
+
+void MultipleInventoryLock::unlock()
+{
+    if (locked)
+    {
+        locked = 0;
+        for (int i = count - 1; i >= 0; i--)
+        {
+            inventories[i]->cs.unlock();
+        }
+    }
+}
+
+void MultipleInventoryLock::lock()
+{
+    if (!locked)
+    {
+        locked = 1;
+        for (int i = 0; i < count; i++)
+        {
+            inventories[i]->cs.lock();
+        }
+    }
+}
+
 Inventory::Inventory(const Framework::Vec3<float> location, bool hasInventory)
     : ReferenceCounter(),
       nextSlotId(1),

+ 16 - 0
FactoryCraft/Inventory.h

@@ -39,6 +39,21 @@ public:
     void pushItems(int count, ItemFilter* zFilter);
 };
 
+class MultipleInventoryLock
+{
+private:
+    Inventory** inventories;
+    int count;
+    bool locked;
+
+public:
+    MultipleInventoryLock(Inventory** inventories, int count);
+    ~MultipleInventoryLock();
+
+    void unlock();
+    void lock();
+};
+
 class Inventory : public virtual Framework::ReferenceCounter
 {
 private:
@@ -107,6 +122,7 @@ public:
         std::function<void(Entity* zSource, Framework::Text id)> call);
 
     friend InventoryInteraction;
+    friend MultipleInventoryLock;
 
 private:
     static bool unsafeMove(Inventory* zSource,

+ 3 - 0
FactoryCraft/StaticInitializerOrder.cpp

@@ -7,6 +7,8 @@
 #include "GrowingPlant.h"
 #include "NoBlock.h"
 #include "TreeSeblingBlock.h"
+// fluid blocks
+#include "FluidBlock.h"
 // dimensions
 #include "OverworldDimension.h"
 // entities
@@ -253,6 +255,7 @@ void initializeBlockTypes()
          },
          "Wheat"))
         ->initializeDefault();
+    new FluidBlockType(BlockTypeEnum::WATER, ModelInfo("fluid", "fluids.ltdb/water.png", 6), "Water");
 }
 
 void initializeItemTypes()