Skip to content

Commit

Permalink
feat: implement cactus block
Browse files Browse the repository at this point in the history
  • Loading branch information
smartcmd committed Jan 26, 2025
1 parent d2091b9 commit 9a7c3e4
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 2 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Unless otherwise specified, any version comparison below is the comparison of se
- (API) Added `ChunkService#removeUnusedChunksImmediately` method that can remove unused chunks immediately. Also, the `/gc` command
will call this method in all dimensions now.
- (API) Added `ItemBaseComponent#getLockMode` and `ItemBaseComponent#setLockMode` methods to get and set the lock mode of an item.
- Implemented reeds (also called sugar cane).
- Implemented reeds (also called sugar cane) and cactus.

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,17 @@ public static DamageContainer entityExplosion(Entity attacker, float sourceDamag
return new DamageContainer(attacker, ENTITY_EXPLOSION, sourceDamage);
}

/**
* Create a contact damage container.
*
* @param sourceDamage the source damage.
*
* @return the damage container.
*/
public static DamageContainer contact(float sourceDamage) {
return new DamageContainer(null, CONTACT, sourceDamage);
}

/**
* Get the attacker.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package org.allaymc.server.block.component;

import org.allaymc.api.block.BlockBehavior;
import org.allaymc.api.block.data.BlockFace;
import org.allaymc.api.block.dto.BlockStateWithPos;
import org.allaymc.api.block.dto.PlayerInteractInfo;
import org.allaymc.api.block.property.type.BlockPropertyTypes;
import org.allaymc.api.block.tag.BlockTags;
import org.allaymc.api.block.type.BlockState;
import org.allaymc.api.block.type.BlockType;
import org.allaymc.api.block.type.BlockTypes;
import org.allaymc.api.entity.Entity;
import org.allaymc.api.entity.component.EntityDamageComponent;
import org.allaymc.api.entity.damage.DamageContainer;
import org.allaymc.api.world.Dimension;
import org.joml.Vector3ic;

/**
* @author daoge_cmd
*/
public class BlockCactusBaseComponentImpl extends BlockBaseComponentImpl {
public BlockCactusBaseComponentImpl(BlockType<? extends BlockBehavior> blockType) {
super(blockType);
}

@Override
public boolean place(Dimension dimension, BlockState blockState, Vector3ic placeBlockPos, PlayerInteractInfo placementInfo) {
if (!canGrowHere(dimension, placeBlockPos, true)) {
return false;
}

dimension.setBlockState(placeBlockPos.x(), placeBlockPos.y(), placeBlockPos.z(), blockState, placementInfo);
return true;
}

@Override
public boolean canRandomUpdate() {
return true;
}

@Override
public boolean canCollideWithEntity() {
return true;
}

@Override
public void onCollideWithEntity(BlockStateWithPos blockStateWithPos, Entity entity) {
if (entity instanceof EntityDamageComponent damageComponent) {
damageComponent.attack(DamageContainer.contact(0.5f));
}
}

@Override
public void onNeighborUpdate(BlockStateWithPos current, BlockStateWithPos neighbor, BlockFace face) {
if (!canGrowHere(current.dimension(), current.pos(), true)) {
current.dimension().breakBlock(current.pos(), null, null);
}
}

@Override
public void onRandomUpdate(BlockStateWithPos current) {
var dimension = current.dimension();
var pos = current.pos();
var block = current.blockState();
var age = block.getPropertyValue(BlockPropertyTypes.AGE_16);
if (age < 15) {
block = block.setProperty(BlockPropertyTypes.AGE_16, age + 1);
} else if (age == 15) {
block = block.setProperty(BlockPropertyTypes.AGE_16, 0);
if (canGrowHere(dimension, pos, false)) {
for (var y = 1; y < 3; y++) {
var blockType = dimension.getBlockState(pos.x(), pos.y() + y, pos.z()).getBlockType();
if (blockType == BlockTypes.AIR) {
dimension.setBlockState(pos.x(), pos.y() + y, pos.z(), BlockTypes.CACTUS.getDefaultState());
break;
} else if (blockType != BlockTypes.CACTUS) {
break;
}
}
}
}

dimension.setBlockState(pos, block);
}

/**
* Check if cactus can live/grow here.
*
* @param dimension the dimension that the cactus is in.
* @param pos the pos of the cactus.
* @param recursive whether to check the block below recursively.
*
* @return {@code true} if cactus can live/grow here, {@code false} otherwise
*/
protected boolean canGrowHere(Dimension dimension, Vector3ic pos, boolean recursive) {
for (var face : BlockFace.getHorizontalBlockFaces()) {
if (dimension.getBlockState(face.offsetPos(pos)).getBlockType() != BlockTypes.AIR) {
return false;
}
}

var downBlockType = dimension.getBlockState(BlockFace.DOWN.offsetPos(pos)).getBlockType();
if (recursive && downBlockType == BlockTypes.CACTUS) {
return canGrowHere(dimension, BlockFace.DOWN.offsetPos(pos), true);
}

return downBlockType.hasBlockTag(BlockTags.SAND);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public boolean onInteract(ItemStack itemStack, Dimension dimension, PlayerIntera
*/
protected boolean canGrowHere(Dimension dimension, Vector3ic pos, boolean recursive) {
var downBlockType = dimension.getBlockState(BlockFace.DOWN.offsetPos(pos)).getBlockType();
if (downBlockType == BlockTypes.REEDS && recursive) {
if (recursive && downBlockType == BlockTypes.REEDS) {
return canGrowHere(dimension, BlockFace.DOWN.offsetPos(pos), true);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.allaymc.api.item.data.ItemId;
import org.allaymc.api.item.type.ItemType;
import org.allaymc.api.item.type.ItemTypes;
import org.allaymc.api.math.voxelshape.VoxelShape;
import org.allaymc.api.math.voxelshape.VoxelShapes;
import org.allaymc.server.block.component.*;
import org.allaymc.server.block.component.button.BlockButtonBaseComponentImpl;
Expand Down Expand Up @@ -1339,4 +1340,16 @@ public static void initReeds() {
.setBaseComponentSupplier(BlockReedsBaseComponentImpl::new)
.build();
}

public static void initCactus() {
BlockTypes.CACTUS = AllayBlockType
.builder(BlockCactusBehaviorImpl.class)
.vanillaBlock(BlockId.CACTUS)
.setProperties(BlockPropertyTypes.AGE_16)
.setBaseComponentSupplier(BlockCactusBaseComponentImpl::new)
// The height of cactus' collision shape is 0.9375 instead of 1.0,
// and that's different from its shape (height is 1.0).
.addComponent(BlockStateDataComponentImpl.ofRedefinedCollisionShape($ -> VoxelShape.builder().solid(0.0625f, 0.0f, 0.0625f, 0.9375f, 0.9375f, 0.9375f).build()))
.build();
}
}

0 comments on commit 9a7c3e4

Please sign in to comment.