Skip to content

Commit

Permalink
feat: add Modular Larynx
Browse files Browse the repository at this point in the history
  • Loading branch information
Elenterius committed Apr 2, 2024
1 parent ac0b552 commit 63e55a2
Show file tree
Hide file tree
Showing 19 changed files with 352 additions and 177 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,6 @@ private void addBlockTranslations() {
addBlock(ModBlocks.BIO_FORGE, "Bio-Forge", "Crafting Station");
addBlock(ModBlocks.BIO_LAB, "Bio-Lab", "Bio-alchemical Brewer");

// addBlock(ModBlocks.VOICE_BOX, "[PH] Modular Larynx", EMPTY_STRING);
addBlock(ModBlocks.TONGUE, "Tongue", "Extracts up to 3 items of the same type every 24 ticks from containers it's attached to, and drops them on the ground.");
addBlock(ModBlocks.MAW_HOPPER, "Maw Hopper", "A fleshy sister of the hopper. Transfers up to 16 items at a time.");

Expand Down Expand Up @@ -563,6 +562,7 @@ private void addBlockTranslations() {
add(ModBlocks.BIOMETRIC_MEMBRANE.get().getDescriptionId() + ".inverted.unique", "Inverted Unique Biometric Membrane");
add(ModBlocks.BIOMETRIC_MEMBRANE.get().getDescriptionId() + ".unique", "Unique Biometric Membrane");

addBlock(ModBlocks.MODULAR_LARYNX, "Modular Larynx", "Similar to a Jukebox but made of an adaptive larynx. Capable of reproducing the sounds of a mob via the insertion of essence and redstone power.");
//addBlock(ModBlocks.NEURAL_INTERCEPTOR, "Neural Interceptor", "A psychic node that prevents natural mob spawning in a 48 block radius.");

addBlock(ModBlocks.PRIMAL_FLESH, "Primal Flesh Block", "Primitive and pure, you better not touch this with your dirty mitts.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ protected void generate() {
dropSelf(ModBlocks.UNDEAD_PERMEABLE_MEMBRANE_PANE.get());
add(ModBlocks.BIOMETRIC_MEMBRANE.get(), this::dropMembraneSettings);

// dropSelf(ModBlocks.VOICE_BOX.get());
dropSelf(ModBlocks.MODULAR_LARYNX.get());
//dropSelf(ModBlocks.NEURAL_INTERCEPTOR.get());

dropSelf(ModBlocks.FLESH_IRIS_DOOR.get());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ protected void registerStatesAndModels() {
membranePaneWithItem(ModBlocks.UNDEAD_PERMEABLE_MEMBRANE_PANE, ModBlocks.UNDEAD_PERMEABLE_MEMBRANE);
existingBlockWithItem(ModBlocks.BIOMETRIC_MEMBRANE);

existingBlockWithItem(ModBlocks.MODULAR_LARYNX);
//horizontalBlockWithItem(ModBlocks.NEURAL_INTERCEPTOR);

bioLantern(ModBlocks.YELLOW_BIO_LANTERN);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "biomancy:block/modular_larynx"
}
}
}
2 changes: 2 additions & 0 deletions src/generated/resources/assets/biomancy/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@
"block.biomancy.biometric_membrane.inverted": "Inverted Biometric Membrane",
"block.biomancy.biometric_membrane.inverted.unique": "Inverted Unique Biometric Membrane",
"block.biomancy.biometric_membrane.unique": "Unique Biometric Membrane",
"block.biomancy.modular_larynx": "Modular Larynx",
"block.biomancy.modular_larynx.tooltip": "Similar to a Jukebox but made of an adaptive larynx. Capable of reproducing the sounds of a mob via the insertion of essence and redstone power.",
"block.biomancy.primal_flesh": "Primal Flesh Block",
"block.biomancy.primal_flesh.tooltip": "Primitive and pure, you better not touch this with your dirty mitts.",
"block.biomancy.primal_flesh_slab": "Primal Flesh Slab",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"parent": "biomancy:block/modular_larynx"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"type": "minecraft:block",
"pools": [
{
"bonus_rolls": 0.0,
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
],
"entries": [
{
"type": "minecraft:item",
"name": "biomancy:modular_larynx"
}
],
"rolls": 1.0
}
],
"random_sequence": "biomancy:blocks/modular_larynx"
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"biomancy:primal_bloom",
"biomancy:bloomlight",
"biomancy:primal_orifice",
"biomancy:modular_larynx",
"biomancy:flesh_spike",
"biomancy:vial_holder",
"biomancy:impermeable_membrane",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.github.elenterius.biomancy.block.modularlarynx;

import com.github.elenterius.biomancy.block.property.BlockPropertyUtil;
import com.github.elenterius.biomancy.init.ModBlockEntities;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BaseEntityBlock;
import net.minecraft.world.level.block.Block;
Expand All @@ -20,16 +20,19 @@
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;

import javax.annotation.Nullable;

@Deprecated
public class VoiceBoxBlock extends BaseEntityBlock {
public class ModularLarynxBlock extends BaseEntityBlock {

public static final BooleanProperty POWERED = BlockStateProperties.POWERED;
public static final IntegerProperty NOTE = BlockStateProperties.NOTE;

public VoiceBoxBlock(Properties properties) {
protected static final VoxelShape SHAPE = Block.box(0, 0, 0, 16, 16, 16);

public ModularLarynxBlock(Properties properties) {
super(properties);
registerDefaultState(getStateDefinition().any().setValue(NOTE, 0).setValue(POWERED, Boolean.FALSE));
}
Expand All @@ -39,11 +42,9 @@ protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockSt
builder.add(POWERED, NOTE);
}

@Nullable
@Override
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
return null;
// return ModBlockEntities.VOICE_BOX.get().create(pos, state);
public @Nullable BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
return ModBlockEntities.MODULAR_LARYNX.get().create(pos, state);
}

@Override
Expand All @@ -52,20 +53,19 @@ public InteractionResult use(BlockState state, Level level, BlockPos pos, Player

ItemStack heldStack = player.getItemInHand(hand);
if (!heldStack.isEmpty()) {
if (VoiceBoxBlockEntity.VALID_ITEM.test(heldStack) && level.getBlockEntity(pos) instanceof VoiceBoxBlockEntity voiceBox) {
ItemStack storedStack = voiceBox.getStoredItemStack();
if (storedStack.isEmpty()) {
voiceBox.setStoredItemStack(heldStack.copy());
heldStack.shrink(1);
if (level.getBlockEntity(pos) instanceof ModularLarynxBlockEntity larynx) {
ItemStack remainder = larynx.insertItemStack(heldStack.copy());
if (remainder.getCount() != heldStack.getCount()) {
if (!player.isCreative()) {
player.setItemInHand(hand, remainder);
}
return InteractionResult.CONSUME;
}
}
}
else if (player.isShiftKeyDown() && level.getBlockEntity(pos) instanceof VoiceBoxBlockEntity voiceBox) {
ItemStack storedStack = voiceBox.getStoredItemStack();
if (!storedStack.isEmpty()) {
voiceBox.dropAllInvContents(level, pos);
voiceBox.setStoredItemStack(ItemStack.EMPTY);
else if (player.isShiftKeyDown() && level.getBlockEntity(pos) instanceof ModularLarynxBlockEntity larynx) {
if (!larynx.isInventoryEmpty()) {
larynx.dropInventoryContents(level, pos);
return InteractionResult.CONSUME;
}
}
Expand All @@ -92,12 +92,13 @@ public void neighborChanged(BlockState state, Level level, BlockPos pos, Block b
}

private void playSound(BlockState state, Level level, BlockPos pos) {
if (!level.isClientSide && level.isEmptyBlock(pos.above()) && level.getBlockEntity(pos) instanceof VoiceBoxBlockEntity voiceBox) {
if (level.isClientSide) return;
if (!level.isEmptyBlock(pos.above())) return;

if (level.getBlockEntity(pos) instanceof ModularLarynxBlockEntity larynx) {
int note = state.getValue(NOTE);
float pitch = (float) Math.pow(2d, (note - 12) / 12d);
if (!voiceBox.playVoice(3f, pitch)) {
level.playSound(null, pos, SoundEvents.PLAYER_BREATH, SoundSource.RECORDS, 3f, pitch);
}
larynx.playSound(3f, pitch);
level.blockEvent(pos, this, 0, note);
}
}
Expand All @@ -114,13 +115,18 @@ public boolean triggerEvent(BlockState state, Level level, BlockPos pos, int id,
public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
if (!state.is(newState.getBlock())) {
BlockEntity tileEntity = level.getBlockEntity(pos);
if (tileEntity instanceof VoiceBoxBlockEntity voiceBox) {
voiceBox.dropAllInvContents(level, pos);
if (tileEntity instanceof ModularLarynxBlockEntity voiceBox) {
voiceBox.dropInventoryContents(level, pos);
}
}
super.onRemove(state, level, pos, newState, isMoving);
}

@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return SHAPE;
}

@Override
public RenderShape getRenderShape(BlockState state) {
return RenderShape.MODEL;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package com.github.elenterius.biomancy.block.modularlarynx;

import com.github.elenterius.biomancy.init.ModBlockEntities;
import com.github.elenterius.biomancy.init.ModCapabilities;
import com.github.elenterius.biomancy.inventory.itemhandler.SingleItemStackHandler;
import com.github.elenterius.biomancy.item.EssenceItem;
import com.github.elenterius.biomancy.util.MobSoundUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.Containers;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.registries.ForgeRegistries;
import org.jetbrains.annotations.NotNull;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Optional;
import java.util.function.Predicate;

public class ModularLarynxBlockEntity extends BlockEntity {

public static final String INVENTORY_TAG = "inventory";
public static final String SOUND_EVENT_TAG = "sound_event";

public static final Predicate<ItemStack> VALID_ITEM = stack -> stack.getItem() instanceof EssenceItem;

private final SingleItemStackHandler inventory;
private LazyOptional<IItemHandler> optionalItemHandler;

private SoundEvent soundEvent = SoundEvents.PLAYER_BREATH;

public ModularLarynxBlockEntity(BlockPos pos, BlockState state) {
super(ModBlockEntities.MODULAR_LARYNX.get(), pos, state);
inventory = new SingleItemStackHandler() {
@Override
public int getSlotLimit(int slot) {
return 1;
}

@Override
public boolean isItemValid(@NotNull ItemStack stack) {
return VALID_ITEM.test(stack);
}

@Override
protected void onContentsChanged() {
updateSounds();
setChanged();
}
};
optionalItemHandler = LazyOptional.of(() -> inventory);
}

public boolean isInventoryEmpty() {
return inventory.isEmpty();
}

public ItemStack insertItemStack(ItemStack stack) {
return inventory.insertItem(stack, false);
}

protected void updateSounds() {
ItemStack stack = inventory.getStack();

if (stack.getItem() instanceof EssenceItem essenceItem) {
soundEvent = essenceItem
.getEntityType(stack)
.map(entityType -> MobSoundUtil.findSoundFor(entityType, MobSoundUtil.SoundType.AMBIENT))
.orElse(SoundEvents.PLAYER_BREATH);
}
else {
soundEvent = SoundEvents.PLAYER_BREATH;
}
}

public boolean playSound(float volume, float pitch) {
if (level == null || level.isClientSide) return false;

BlockPos pos = getBlockPos();
double x = pos.getX() + 0.5d;
double y = pos.getY() + 0.5d;
double z = pos.getZ() + 0.5d;
level.playSound(null, x, y, z, soundEvent, SoundSource.RECORDS, volume, pitch);

return true;
}

@Override
protected void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
tag.put(INVENTORY_TAG, inventory.serializeNBT());
tag.putString(SOUND_EVENT_TAG, soundEvent.getLocation().toString());
}

@Override
public void load(CompoundTag tag) {
super.load(tag);
inventory.deserializeNBT(tag.getCompound(INVENTORY_TAG));
soundEvent = deserializeSoundEvent(tag.getString(SOUND_EVENT_TAG)).orElse(SoundEvents.PLAYER_BREATH);
}

public Optional<SoundEvent> deserializeSoundEvent(String stringKey) {
ResourceLocation key = ResourceLocation.tryParse(stringKey);
if (key != null) {
return Optional.ofNullable(ForgeRegistries.SOUND_EVENTS.getValue(key));
}
return Optional.empty();
}

public void dropInventoryContents(Level level, BlockPos pos) {
ItemStack stack = inventory.extractItem(inventory.getMaxAmount(), false);
Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), stack);
}

@Override
public void invalidateCaps() {
super.invalidateCaps();
optionalItemHandler.invalidate();
}

@Override
public void reviveCaps() {
super.reviveCaps();
optionalItemHandler = LazyOptional.of(() -> inventory);
}

@Nonnull
@Override
public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {
if (!remove) {
return ModCapabilities.ITEM_HANDLER.orEmpty(cap, optionalItemHandler);
}
return super.getCapability(cap, side);
}

}
Loading

0 comments on commit 63e55a2

Please sign in to comment.