HyCodeYourTale
classpublicPriority 3

DirectionalGrowthBehaviour

com.hypixel.hytale.builtin.adventure.farming.config.stages.spread.DirectionalGrowthBehaviour

extends SpreadGrowthBehaviour

8

Methods

8

Public Methods

4

Fields

1

Constructors

Constants

BuilderCodec<DirectionalGrowthBehaviour>CODEC= BuilderCodec.builder( DirectionalGrowthBehaviour.class, DirectionalGrowthBehaviour::new,...
intPLACE_BLOCK_TRIES= 100

Constructors

public
DirectionalGrowthBehaviour()

Methods

Public Methods (8)

public
String getBlockTypeKey()
public
IWeightedMap<DirectionalGrowthBehaviour.BlockTypeWeight> getBlockTypes()
public
IntRange getHorizontalRange()
public
int getValue()
public
DirectionalGrowthBehaviour.VerticalDirection getVerticalDirection()
public
IntRange getVerticalRange()
public
double getWeight()
@Override
public
String toString()
@Nonnull@Override

Fields

Protected Fields (4)

protectedIWeightedMap<DirectionalGrowthBehaviour.BlockTypeWeight> blockTypes
protectedIntRange horizontalRange
protectedDirectionalGrowthBehaviour.VerticalDirection verticalDirection
protectedIntRange verticalRange

Inheritance

Parent
Current
Interface
Child

Use mouse wheel to zoom, drag to pan. Click nodes to navigate.

Related Classes

Source Code

package com.hypixel.hytale.builtin.adventure.farming.config.stages.spread;

import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlock;
import com.hypixel.hytale.builtin.blockphysics.BlockPhysicsSystems;
import com.hypixel.hytale.builtin.blockphysics.BlockPhysicsUtil;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.codec.codecs.EnumCodec;
import com.hypixel.hytale.codec.validation.Validators;
import com.hypixel.hytale.common.map.IWeightedElement;
import com.hypixel.hytale.common.map.IWeightedMap;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.math.range.IntRange;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.math.util.FastRandom;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.math.util.TrigMathUtil;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation;
import com.hypixel.hytale.server.core.blocktype.component.BlockPhysics;
import com.hypixel.hytale.server.core.codec.WeightedMapCodec;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.accessor.LocalCachedChunkAccessor;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import javax.annotation.Nonnull;

public class DirectionalGrowthBehaviour extends SpreadGrowthBehaviour {
   public static final BuilderCodec<DirectionalGrowthBehaviour> CODEC = BuilderCodec.builder(
         DirectionalGrowthBehaviour.class, DirectionalGrowthBehaviour::new, BASE_CODEC
      )
      .append(
         new KeyedCodec<>(
            "GrowthBlockTypes", new WeightedMapCodec<>(DirectionalGrowthBehaviour.BlockTypeWeight.CODEC, new DirectionalGrowthBehaviour.BlockTypeWeight[0])
         ),
         (directionalGrowthBehaviour, blockTypeWeightIWeightedMap) -> directionalGrowthBehaviour.blockTypes = blockTypeWeightIWeightedMap,
         directionalGrowthBehaviour -> directionalGrowthBehaviour.blockTypes
      )
      .documentation("Defines a map of the possible BlockType to spread.")
      .addValidator(Validators.nonNull())
      .add()
      .<IntRange>append(
         new KeyedCodec<>("Horizontal", IntRange.CODEC),
         (directionalGrowthBehaviour, intRange) -> directionalGrowthBehaviour.horizontalRange = intRange,
         directionalGrowthBehaviour -> directionalGrowthBehaviour.horizontalRange
      )
      .documentation("Defines if the spread can happen horizontally. The range must be set with positive integers.")
      .add()
      .<IntRange>append(
         new KeyedCodec<>("Vertical", IntRange.CODEC),
         (directionalGrowthBehaviour, intRange) -> directionalGrowthBehaviour.verticalRange = intRange,
         directionalGrowthBehaviour -> directionalGrowthBehaviour.verticalRange
      )
      .documentation("Defines if the spread can happen vertically. The range must be set with positive integers.")
      .add()
      .<DirectionalGrowthBehaviour.VerticalDirection>append(
         new KeyedCodec<>("VerticalDirection", new EnumCodec<>(DirectionalGrowthBehaviour.VerticalDirection.class)),
         (directionalGrowthBehaviour, verticalDirection) -> directionalGrowthBehaviour.verticalDirection = verticalDirection,
         directionalGrowthBehaviour -> directionalGrowthBehaviour.verticalDirection
      )
      .documentation("Defines in which direction the vertical spread should happen. Possible values are: 'Upwards' and 'Downwards', default value: 'Upwards'.")
      .addValidator(Validators.nonNull())
      .add()
      .build();
   private static final int PLACE_BLOCK_TRIES = 100;
   protected IWeightedMap<DirectionalGrowthBehaviour.BlockTypeWeight> blockTypes;
   protected IntRange horizontalRange;
   protected IntRange verticalRange;
   protected DirectionalGrowthBehaviour.VerticalDirection verticalDirection = DirectionalGrowthBehaviour.VerticalDirection.BOTH;

   public DirectionalGrowthBehaviour() {
   }

   public IWeightedMap<DirectionalGrowthBehaviour.BlockTypeWeight> getBlockTypes() {
      return this.blockTypes;
   }

   public IntRange getHorizontalRange() {
      return this.horizontalRange;
   }

   public IntRange getVerticalRange() {
      return this.verticalRange;
   }

   public DirectionalGrowthBehaviour.VerticalDirection getVerticalDirection() {
      return this.verticalDirection;
   }

   @Override
   public void execute(
      ComponentAccessor<ChunkStore> commandBuffer,
      Ref<ChunkStore> sectionRef,
      Ref<ChunkStore> blockRef,
      int worldX,
      int worldY,
      int worldZ,
      float newSpreadRate
   ) {
      int x = 0;
      int z = 0;
      FastRandom random = new FastRandom();
      String blockTypeKey = this.blockTypes.get(random).getBlockTypeKey();
      World world = commandBuffer.getExternalData().getWorld();
      LocalCachedChunkAccessor chunkAccessor = LocalCachedChunkAccessor.atWorldCoords(world, worldX, worldZ, 1);

      for (int i = 0; i < 100; i++) {
         if (this.horizontalRange != null) {
            double angle = (double)(6.2831855F * random.nextFloat());
            int radius = this.horizontalRange.getInt(random.nextFloat());
            x = MathUtil.fastRound((float)radius * TrigMathUtil.cos(angle));
            z = MathUtil.fastRound((float)radius * TrigMathUtil.sin(angle));
         }

         int targetX = worldX + x;
         int targetZ = worldZ + z;
         int chunkX = ChunkUtil.chunkCoordinate(targetX);
         int chunkZ = ChunkUtil.chunkCoordinate(targetZ);
         WorldChunk chunk = chunkAccessor.getChunkIfInMemory(ChunkUtil.indexChunk(chunkX, chunkZ));
         if (chunk != null) {
            int targetY;
            if (this.verticalRange != null) {
               int directionValue = switch (this.verticalDirection) {
                  case DOWNWARDS, UPWARDS -> this.verticalDirection.getValue();
                  case BOTH -> random.nextBoolean() ? 1 : -1;
               };
               targetY = worldY + this.verticalRange.getInt(random.nextFloat()) * directionValue;
            } else {
               targetY = chunk.getHeight(targetX, targetZ) + 1;
            }

            if (this.tryPlaceBlock(world, chunk, targetX, targetY, targetZ, blockTypeKey, 0)) {
               int finalTargetY = targetY;
               world.execute(() -> {
                  WorldChunk loadedChunk = chunkAccessor.getChunk(ChunkUtil.indexChunk(chunkX, chunkZ));
                  if (loadedChunk != null) {
                     loadedChunk.placeBlock(targetX, finalTargetY, targetZ, blockTypeKey, Rotation.None, Rotation.None, Rotation.None);
                     decaySpread(commandBuffer, loadedChunk.getBlockComponentChunk(), targetX, finalTargetY, targetZ, newSpreadRate);
                  }
               });
               return;
            }
         }
      }
   }

   private static void decaySpread(
      ComponentAccessor<ChunkStore> commandBuffer, BlockComponentChunk blockComponentChunk, int worldX, int worldY, int worldZ, float newSpreadRate
   ) {
      Ref<ChunkStore> blockRefPlaced = blockComponentChunk.getEntityReference(ChunkUtil.indexBlockInColumn(worldX, worldY, worldZ));
      if (blockRefPlaced != null) {
         FarmingBlock farmingPlaced = commandBuffer.getComponent(blockRefPlaced, FarmingBlock.getComponentType());
         if (farmingPlaced != null) {
            farmingPlaced.setSpreadRate(newSpreadRate);
         }
      }
   }

   private boolean tryPlaceBlock(@Nonnull World world, @Nonnull WorldChunk chunk, int worldX, int worldY, int worldZ, String blockTypeKey, int rotation) {
      if (chunk.getBlock(worldX, worldY, worldZ) != 0) {
         return false;
      } else if (!this.validatePosition(world, worldX, worldY, worldZ)) {
         return false;
      } else {
         BlockType blockType = BlockType.getAssetMap().getAsset(blockTypeKey);
         if (blockType == null) {
            return false;
         } else if (!chunk.testPlaceBlock(worldX, worldY, worldZ, blockType, rotation)) {
            return false;
         } else {
            int cx = chunk.getX();
            int cz = chunk.getZ();
            int cy = ChunkUtil.indexSection(worldY);
            Ref<ChunkStore> sectionRef = world.getChunkStore().getChunkSectionReference(cx, cy, cz);
            if (sectionRef == null) {
               return false;
            } else {
               Store<ChunkStore> store = world.getChunkStore().getStore();
               BlockPhysics blockPhysics = store.getComponent(sectionRef, BlockPhysics.getComponentType());
               FluidSection fluidSection = store.getComponent(sectionRef, FluidSection.getComponentType());
               BlockSection blockSection = store.getComponent(sectionRef, BlockSection.getComponentType());
               int filler = blockSection.getFiller(worldX, worldY, worldZ);
               BlockPhysicsSystems.CachedAccessor cachedAccessor = BlockPhysicsSystems.CachedAccessor.of(
                  store, blockSection, blockPhysics, fluidSection, cx, cy, cz, 14
               );
               return BlockPhysicsUtil.testBlockPhysics(
                     cachedAccessor, blockSection, blockPhysics, fluidSection, worldX, worldY, worldZ, blockType, rotation, filler
                  )
                  != 0;
            }
         }
      }
   }

   @Nonnull
   @Override
   public String toString() {
      return "DirectionalGrowthBehaviour{blockTypes="
         + this.blockTypes
         + ", horizontalRange="
         + this.horizontalRange
         + ", verticalRange="
         + this.verticalRange
         + ", verticalDirection="
         + this.verticalDirection
         + "} "
         + super.toString();
   }

   public static class BlockTypeWeight implements IWeightedElement {
      @Nonnull
      public static BuilderCodec<DirectionalGrowthBehaviour.BlockTypeWeight> CODEC = BuilderCodec.builder(
            DirectionalGrowthBehaviour.BlockTypeWeight.class, DirectionalGrowthBehaviour.BlockTypeWeight::new
         )
         .append(
            new KeyedCodec<>("Weight", Codec.DOUBLE), (blockTypeWeight, integer) -> blockTypeWeight.weight = integer, blockTypeWeight -> blockTypeWeight.weight
         )
         .documentation("Defines the probability to have this entry.")
         .addValidator(Validators.greaterThan(0.0))
         .add()
         .<String>append(
            new KeyedCodec<>("BlockType", Codec.STRING),
            (blockTypeWeight, blockTypeKey) -> blockTypeWeight.blockTypeKey = blockTypeKey,
            blockTypeWeight -> blockTypeWeight.blockTypeKey
         )
         .documentation("Defines the BlockType that'll be spread")
         .addValidator(Validators.nonNull())
         .add()
         .build();
      protected double weight = 1.0;
      protected String blockTypeKey;

      public BlockTypeWeight() {
      }

      @Override
      public double getWeight() {
         return this.weight;
      }

      public String getBlockTypeKey() {
         return this.blockTypeKey;
      }

      @Nonnull
      @Override
      public String toString() {
         return "BlockTypeWeight{weight=" + this.weight + ", blockTypeKey=" + this.blockTypeKey + "}";
      }
   }

   private static enum VerticalDirection {
      DOWNWARDS(-1),
      BOTH(0),
      UPWARDS(1);

      private final int value;

      private VerticalDirection(int value) {
         this.value = value;
      }

      public int getValue() {
         return this.value;
      }
   }
}