diff --git a/src/main/java/com/atsuishio/superbwarfare/ModUtils.java b/src/main/java/com/atsuishio/superbwarfare/ModUtils.java index 4b7beb2db..fbb3b1ea9 100644 --- a/src/main/java/com/atsuishio/superbwarfare/ModUtils.java +++ b/src/main/java/com/atsuishio/superbwarfare/ModUtils.java @@ -1,5 +1,6 @@ package com.atsuishio.superbwarfare; +import com.atsuishio.superbwarfare.capability.CapabilityHandler; import com.atsuishio.superbwarfare.component.ModDataComponents; import com.atsuishio.superbwarfare.config.ClientConfig; import com.atsuishio.superbwarfare.config.CommonConfig; @@ -55,6 +56,8 @@ public class ModUtils { // bus.addListener(this::onCommonSetup); // bus.addListener(this::onClientSetup); + CapabilityHandler.register(bus); + NeoForge.EVENT_BUS.register(this); } diff --git a/src/main/java/com/atsuishio/superbwarfare/block/ChargingStationBlock.java b/src/main/java/com/atsuishio/superbwarfare/block/ChargingStationBlock.java new file mode 100644 index 000000000..dfe677c43 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/block/ChargingStationBlock.java @@ -0,0 +1,108 @@ +package com.atsuishio.superbwarfare.block; + +import com.atsuishio.superbwarfare.block.entity.ChargingStationBlockEntity; +import com.atsuishio.superbwarfare.init.ModBlockEntities; +import com.mojang.serialization.MapCodec; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.Containers; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.*; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.phys.BlockHitResult; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.annotation.ParametersAreNonnullByDefault; + +public class ChargingStationBlock extends BaseEntityBlock { + + public static final DirectionProperty FACING = HorizontalDirectionalBlock.FACING; + public static final BooleanProperty SHOW_RANGE = BooleanProperty.create("show_range"); + + public ChargingStationBlock() { + super(Properties.of().sound(SoundType.METAL).strength(3.0f).requiresCorrectToolForDrops()); + this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(SHOW_RANGE, false)); + } + + @Override + @ParametersAreNonnullByDefault + protected @NotNull InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { + if (level.isClientSide) { + return InteractionResult.SUCCESS; + } else { + this.openContainer(level, pos, player); + return InteractionResult.CONSUME; + } + } + + protected void openContainer(Level pLevel, BlockPos pPos, Player pPlayer) { + BlockEntity blockentity = pLevel.getBlockEntity(pPos); + if (blockentity instanceof ChargingStationBlockEntity blockEntity) { + pPlayer.openMenu(blockEntity); + } + } + + public static final MapCodec CODEC = simpleCodec((prop) -> new ChargingStationBlock()); + + @Override + protected @NotNull MapCodec codec() { + return CODEC; + } + + @Override + public @NotNull RenderShape getRenderShape(@NotNull BlockState pState) { + return RenderShape.MODEL; + } + + @Nullable + @Override + public BlockEntity newBlockEntity(@NotNull BlockPos pPos, @NotNull BlockState pState) { + return new ChargingStationBlockEntity(pPos, pState); + } + + @Nullable + @Override + public BlockEntityTicker getTicker(Level pLevel, @NotNull BlockState pState, @NotNull BlockEntityType pBlockEntityType) { + if (!pLevel.isClientSide) { + return createTickerHelper(pBlockEntityType, ModBlockEntities.CHARGING_STATION.get(), ChargingStationBlockEntity::serverTick); + } + return null; + } + + @Override + public void onRemove(BlockState pState, @NotNull Level pLevel, @NotNull BlockPos pPos, BlockState pNewState, boolean pMovedByPiston) { + if (!pState.is(pNewState.getBlock())) { + BlockEntity blockentity = pLevel.getBlockEntity(pPos); + if (blockentity instanceof ChargingStationBlockEntity blockEntity) { + if (pLevel instanceof ServerLevel serverLevel) { + Containers.dropContents(serverLevel, pPos, blockEntity); + } + pLevel.updateNeighbourForOutputSignal(pPos, this); + } + } + + super.onRemove(pState, pLevel, pPos, pNewState, pMovedByPiston); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder pBuilder) { + pBuilder.add(FACING).add(SHOW_RANGE); + } + + @Nullable + @Override + public BlockState getStateForPlacement(BlockPlaceContext pContext) { + return this.defaultBlockState().setValue(FACING, pContext.getHorizontalDirection().getOpposite()).setValue(SHOW_RANGE, false); + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/block/ContainerBlock.java b/src/main/java/com/atsuishio/superbwarfare/block/ContainerBlock.java new file mode 100644 index 000000000..2e807d8a5 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/block/ContainerBlock.java @@ -0,0 +1,218 @@ +package com.atsuishio.superbwarfare.block; + +import com.atsuishio.superbwarfare.block.entity.ContainerBlockEntity; +import com.atsuishio.superbwarfare.init.ModBlockEntities; +import com.atsuishio.superbwarfare.init.ModItems; +import com.atsuishio.superbwarfare.init.ModSounds; +import com.mojang.serialization.MapCodec; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.Component; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.ItemInteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.*; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.List; + +public class ContainerBlock extends BaseEntityBlock { + + public static final DirectionProperty FACING = HorizontalDirectionalBlock.FACING; + public static final BooleanProperty OPENED = BooleanProperty.create("opened"); + + public ContainerBlock() { + super(Properties.of().sound(SoundType.METAL).strength(3.0f).noOcclusion().requiresCorrectToolForDrops()); + this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(OPENED, false)); + } + + + @Override + @ParametersAreNonnullByDefault + protected @NotNull ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult) { + if (level.isClientSide + || state.getValue(OPENED) + || !(level.getBlockEntity(pos) instanceof ContainerBlockEntity containerBlockEntity) + ) return ItemInteractionResult.FAIL; + + if (!stack.is(ModItems.CROWBAR.get())) { + player.displayClientMessage(Component.translatable("des.superbwarfare.container.fail.crowbar"), true); + return ItemInteractionResult.FAIL; + } + + if (!hasEntity(level, pos)) { + player.displayClientMessage(Component.translatable("des.superbwarfare.container.fail.empty"), true); + return ItemInteractionResult.FAIL; + } + + if (canOpen(level, pos, containerBlockEntity.entityType, containerBlockEntity.entity)) { + level.setBlockAndUpdate(pos, state.setValue(OPENED, true)); + level.playSound(null, BlockPos.containing(pos.getX(), pos.getY(), pos.getZ()), ModSounds.OPEN.get(), SoundSource.BLOCKS, 1, 1); + + return ItemInteractionResult.SUCCESS; + } else { + player.displayClientMessage(Component.translatable("des.superbwarfare.container.fail.open"), true); + return ItemInteractionResult.FAIL; + } + } + + public boolean hasEntity(Level level, BlockPos pos) { + BlockEntity blockEntity = level.getBlockEntity(pos); + if (!(blockEntity instanceof ContainerBlockEntity containerBlockEntity)) return false; + return containerBlockEntity.entity != null || containerBlockEntity.entityType != null; + } + + public static boolean canOpen(Level level, BlockPos pos, EntityType entityType, Entity entity) { + boolean flag = true; + + int w = 0; + int h = 0; + + if (entityType != null) { + w = (int) (entityType.getDimensions().width() / 2 + 1); + h = (int) (entityType.getDimensions().height() + 1); + } + + if (entity != null) { + w = (int) (entity.getType().getDimensions().width() / 2 + 1); + h = (int) (entity.getType().getDimensions().height() + 1); + } + + for (int i = -w; i < w + 1; i++) { + for (int j = 0; j < h; j++) { + for (int k = -w; k < w + 1; k++) { + if (i == 0 && j == 0 && k == 0) { + continue; + } + + if (level.getBlockState(pos.offset(i, j, k)).canOcclude()) { + flag = false; + } + } + } + } + + return flag; + } + + @Nullable + @Override + public BlockEntityTicker getTicker(Level level, @NotNull BlockState state, @NotNull BlockEntityType pBlockEntityType) { + if (!level.isClientSide) { + return createTickerHelper(pBlockEntityType, ModBlockEntities.CONTAINER.get(), ContainerBlockEntity::serverTick); + } + return null; + } + + @Override + @ParametersAreNonnullByDefault + public void appendHoverText(ItemStack stack, Item.TooltipContext context, List tooltipComponents, TooltipFlag tooltipFlag) { + super.appendHoverText(stack, context, tooltipComponents, tooltipFlag); + + // TODO read NBT +// CompoundTag tag = BlockItem.getBlockEntityData(pStack); +// if (tag != null && tag.contains("EntityType")) { +// String s = getEntityTranslationKey(tag.getString("EntityType")); +// tooltipComponents.add(Component.translatable(s == null ? "des.superbwarfare.container.empty" : s).withStyle(ChatFormatting.GRAY)); +// +// var entityType = EntityType.byString(tag.getString("EntityType")).orElse(null); +// if (entityType != null) { +// int w = 0, h = 0; +// if (level instanceof Level level && tag.contains("Entity")) { +// var entity = entityType.create(level); +// if (entity != null) { +// entity.load(tag.getCompound("Entity")); +// w = (int) (entity.getType().getDimensions().width() + 1); +// if (w % 2 == 0) w++; +// h = (int) (entity.getType().getDimensions().height() + 1); +// } +// } else { +// w = (int) (entityType.getDimensions().width() + 1); +// if (w % 2 == 0) w++; +// h = (int) (entityType.getDimensions().height() + 1); +// } +// if (w != 0 && h != 0) { +// tooltipComponents.add(Component.literal(w + " x " + w + " x " + h).withStyle(ChatFormatting.YELLOW)); +// } +// } +// } + } + + @Nullable + public static String getEntityTranslationKey(String path) { + String[] parts = path.split(":"); + if (parts.length > 1) { + return "entity." + parts[0] + "." + parts[1]; + } else { + return null; + } + } + + @Override + @ParametersAreNonnullByDefault + public @NotNull VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { + return state.getValue(OPENED) ? box(1, 0, 1, 15, 14, 15) : box(0, 0, 0, 16, 15, 16); + } + + // TODO codec + @Override + protected @NotNull MapCodec codec() { + return null; + } + + @Override + public @NotNull RenderShape getRenderShape(@NotNull BlockState state) { + return RenderShape.ENTITYBLOCK_ANIMATED; + } + + @Nullable + @Override + public BlockEntity newBlockEntity(@NotNull BlockPos blockPos, @NotNull BlockState blockState) { + return new ContainerBlockEntity(blockPos, blockState); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(FACING).add(OPENED); + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()).setValue(OPENED, false); + } + + + @Override + @ParametersAreNonnullByDefault + public @NotNull ItemStack getCloneItemStack(BlockState state, HitResult target, LevelReader level, BlockPos pos, Player player) { + var itemStack = super.getCloneItemStack(state, target, level, pos, player); + // TODO saveToItem +// level.getBlockEntity(pos, ModBlockEntities.CONTAINER.get()).ifPresent((blockEntity) -> blockEntity.saveToItem(itemStack)); + return itemStack; + } + +} + diff --git a/src/main/java/com/atsuishio/superbwarfare/block/CreativeChargingStationBlock.java b/src/main/java/com/atsuishio/superbwarfare/block/CreativeChargingStationBlock.java new file mode 100644 index 000000000..3100150cb --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/block/CreativeChargingStationBlock.java @@ -0,0 +1,89 @@ +package com.atsuishio.superbwarfare.block; + +import com.atsuishio.superbwarfare.block.entity.CreativeChargingStationBlockEntity; +import com.atsuishio.superbwarfare.init.ModBlockEntities; +import com.mojang.serialization.MapCodec; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.*; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.annotation.ParametersAreNonnullByDefault; + +public class CreativeChargingStationBlock extends BaseEntityBlock { + + public static final DirectionProperty FACING = HorizontalDirectionalBlock.FACING; + + public CreativeChargingStationBlock() { + this(Properties.of().sound(SoundType.METAL).strength(3.0f).requiresCorrectToolForDrops()); + } + + public CreativeChargingStationBlock(BlockBehaviour.Properties properties) { + super(properties); + this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH)); + } + + private static final MapCodec CODEC = BlockBehaviour.simpleCodec(CreativeChargingStationBlock::new); + + @Override + protected @NotNull MapCodec codec() { + return CODEC; + } + + @Override + public @NotNull RenderShape getRenderShape(@NotNull BlockState pState) { + return RenderShape.MODEL; + } + + @Nullable + @Override + @ParametersAreNonnullByDefault + public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) { + return new CreativeChargingStationBlockEntity(pPos, pState); + } + + @Nullable + @Override + public BlockEntityTicker getTicker(Level pLevel, @NotNull BlockState pState, @NotNull BlockEntityType pBlockEntityType) { + if (!pLevel.isClientSide) { + return createTickerHelper( + pBlockEntityType, ModBlockEntities.CREATIVE_CHARGING_STATION.get(), + (pLevel1, pPos, pState1, blockEntity) -> CreativeChargingStationBlockEntity.serverTick(blockEntity) + ); + } + return null; + } + + @Override + public void onRemove(BlockState pState, @NotNull Level pLevel, @NotNull BlockPos pPos, BlockState pNewState, boolean pMovedByPiston) { + if (!pState.is(pNewState.getBlock())) { + BlockEntity blockentity = pLevel.getBlockEntity(pPos); + if (blockentity instanceof CreativeChargingStationBlockEntity) { + pLevel.updateNeighbourForOutputSignal(pPos, this); + } + } + + super.onRemove(pState, pLevel, pPos, pNewState, pMovedByPiston); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder pBuilder) { + pBuilder.add(FACING); + } + + @Nullable + @Override + public BlockState getStateForPlacement(BlockPlaceContext pContext) { + return this.defaultBlockState().setValue(FACING, pContext.getHorizontalDirection().getOpposite()); + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/block/DragonTeethBlock.java b/src/main/java/com/atsuishio/superbwarfare/block/DragonTeethBlock.java index 8baa263d2..e705df43a 100644 --- a/src/main/java/com/atsuishio/superbwarfare/block/DragonTeethBlock.java +++ b/src/main/java/com/atsuishio/superbwarfare/block/DragonTeethBlock.java @@ -25,7 +25,7 @@ import org.jetbrains.annotations.Nullable; import javax.annotation.ParametersAreNonnullByDefault; -@SuppressWarnings("deprecation") +// TODO 解决obj渲染问题 public class DragonTeethBlock extends Block { public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED; diff --git a/src/main/java/com/atsuishio/superbwarfare/block/FuMO25Block.java b/src/main/java/com/atsuishio/superbwarfare/block/FuMO25Block.java new file mode 100644 index 000000000..9279678c8 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/block/FuMO25Block.java @@ -0,0 +1,102 @@ +package com.atsuishio.superbwarfare.block; + +import com.atsuishio.superbwarfare.block.entity.FuMO25BlockEntity; +import com.atsuishio.superbwarfare.init.ModBlockEntities; +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.annotation.ParametersAreNonnullByDefault; + +public class FuMO25Block extends Block implements EntityBlock { + + public static final BooleanProperty POWERED = BooleanProperty.create("powered"); + + public FuMO25Block() { + super(Properties.of().sound(SoundType.METAL).strength(3.0f).requiresCorrectToolForDrops()); + this.registerDefaultState(this.stateDefinition.any().setValue(POWERED, false)); + } + + @Override + @ParametersAreNonnullByDefault + protected @NotNull InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { + if (level.isClientSide) { + return InteractionResult.SUCCESS; + } else { + BlockEntity blockentity = level.getBlockEntity(pos); + if (blockentity instanceof FuMO25BlockEntity blockEntity) { + player.openMenu(blockEntity); + } + return InteractionResult.CONSUME; + } + } + + @Override + @ParametersAreNonnullByDefault + public @NotNull VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, CollisionContext pContext) { + return Shapes.or(box(1, 0, 1, 15, 6, 15), box(6, 6, 6, 10, 58, 10)); + } + + @Override + public @NotNull RenderShape getRenderShape(@NotNull BlockState pState) { + return RenderShape.ENTITYBLOCK_ANIMATED; + } + + @Nullable + @Override + @ParametersAreNonnullByDefault + public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) { + return new FuMO25BlockEntity(pPos, pState); + } + + @Override + public @Nullable BlockEntityTicker getTicker(Level pLevel, @NotNull BlockState pState, @NotNull BlockEntityType pBlockEntityType) { + if (!pLevel.isClientSide && pBlockEntityType == ModBlockEntities.FUMO_25.get()) { + return FuMO25BlockEntity::serverTick; + } + return null; + } + + @Override + @ParametersAreNonnullByDefault + public void onRemove(BlockState pState, Level pLevel, BlockPos pPos, BlockState pNewState, boolean pMovedByPiston) { + if (!pState.is(pNewState.getBlock())) { + BlockEntity blockentity = pLevel.getBlockEntity(pPos); + if (blockentity instanceof FuMO25BlockEntity) { + pLevel.updateNeighbourForOutputSignal(pPos, this); + } + } + + super.onRemove(pState, pLevel, pPos, pNewState, pMovedByPiston); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder pBuilder) { + pBuilder.add(POWERED); + } + + @Nullable + @Override + public BlockState getStateForPlacement(@NotNull BlockPlaceContext pContext) { + return this.defaultBlockState().setValue(POWERED, false); + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/block/entity/ChargingStationBlockEntity.java b/src/main/java/com/atsuishio/superbwarfare/block/entity/ChargingStationBlockEntity.java new file mode 100644 index 000000000..bd4db2e88 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/block/entity/ChargingStationBlockEntity.java @@ -0,0 +1,397 @@ +package com.atsuishio.superbwarfare.block.entity; + +import com.atsuishio.superbwarfare.block.ChargingStationBlock; +import com.atsuishio.superbwarfare.init.ModBlockEntities; +import com.atsuishio.superbwarfare.menu.ChargingStationMenu; +import com.atsuishio.superbwarfare.network.dataslot.ContainerEnergyData; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.NonNullList; +import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.WorldlyContainer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.energy.EnergyStorage; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Energy Data Slot Code based on @GoryMoon's Chargers + */ +public class ChargingStationBlockEntity extends BlockEntity implements WorldlyContainer, MenuProvider { + + protected static final int SLOT_FUEL = 0; + protected static final int SLOT_CHARGE = 1; + + public static final int MAX_ENERGY = 4000000; + public static final int MAX_DATA_COUNT = 4; + public static final int DEFAULT_FUEL_TIME = 1600; + public static final int CHARGE_SPEED = 128; + public static final int CHARGE_OTHER_SPEED = 100000; + public static final int CHARGE_RADIUS = 8; + + protected NonNullList items = NonNullList.withSize(2, ItemStack.EMPTY); + + private EnergyStorage energyHandler; + + // TODO SidedInvWrapper Energy +// private LazyOptional[] itemHandlers = SidedInvWrapper.create(this, Direction.UP, Direction.DOWN, Direction.NORTH); + + public int fuelTick = 0; + public int maxFuelTick = DEFAULT_FUEL_TIME; + public boolean showRange = false; + + protected final ContainerEnergyData dataAccess = new ContainerEnergyData() { + public long get(int pIndex) { + return switch (pIndex) { + case 0 -> ChargingStationBlockEntity.this.fuelTick; + case 1 -> ChargingStationBlockEntity.this.maxFuelTick; + case 2 -> { + AtomicInteger energy = new AtomicInteger(); +// ChargingStationBlockEntity.this.getCapability(Capabilities.ENERGY).ifPresent(consumer -> energy.set(consumer.getEnergyStored())); + yield energy.get(); + } + case 3 -> ChargingStationBlockEntity.this.showRange ? 1 : 0; + default -> 0; + }; + } + + public void set(int pIndex, long pValue) { + switch (pIndex) { + case 0: + ChargingStationBlockEntity.this.fuelTick = (int) pValue; + break; + case 1: + ChargingStationBlockEntity.this.maxFuelTick = (int) pValue; + break; + case 2: +// ChargingStationBlockEntity.this.getCapability(Capabilities.ENERGY).ifPresent(consumer -> consumer.receiveEnergy((int) pValue, false)); + break; + case 3: + ChargingStationBlockEntity.this.showRange = pValue == 1; + break; + } + } + + public int getCount() { + return MAX_DATA_COUNT; + } + }; + + public ChargingStationBlockEntity(BlockPos pos, BlockState state) { + super(ModBlockEntities.CHARGING_STATION.get(), pos, state); + + this.energyHandler = new EnergyStorage(MAX_ENERGY); + } + + public static void serverTick(Level pLevel, BlockPos pPos, BlockState pState, ChargingStationBlockEntity blockEntity) { + if (blockEntity.showRange != pState.getValue(ChargingStationBlock.SHOW_RANGE)) { + pLevel.setBlockAndUpdate(pPos, pState.setValue(ChargingStationBlock.SHOW_RANGE, blockEntity.showRange)); + setChanged(pLevel, pPos, pState); + } + + var handler = blockEntity.energyHandler; + int energy = handler.getEnergyStored(); + if (energy > 0) { + blockEntity.chargeEntity(handler); + } + if (handler.getEnergyStored() > 0) { + blockEntity.chargeItemStack(handler); + } + if (handler.getEnergyStored() > 0) { + blockEntity.chargeBlock(handler); + } + + if (blockEntity.fuelTick > 0) { + blockEntity.fuelTick--; + if (energy < handler.getMaxEnergyStored()) { + handler.receiveEnergy(CHARGE_SPEED, false); + } + } else if (!blockEntity.getItem(SLOT_FUEL).isEmpty()) { + if (handler.getEnergyStored() >= handler.getMaxEnergyStored()) return; + + ItemStack fuel = blockEntity.getItem(SLOT_FUEL); + int burnTime = fuel.getBurnTime(RecipeType.SMELTING); + + var fuelEnergy = fuel.getCapability(Capabilities.EnergyStorage.ITEM); + + if (fuelEnergy != null) { + // 优先当作电池处理 + var energyToExtract = Math.min(CHARGE_OTHER_SPEED, handler.getMaxEnergyStored() - handler.getEnergyStored()); + if (fuelEnergy.canExtract() && handler.canReceive()) { + handler.receiveEnergy(fuelEnergy.extractEnergy(energyToExtract, false), false); + } + + blockEntity.setChanged(); + } else if (burnTime > 0) { + // 其次尝试作为燃料处理 + blockEntity.fuelTick = burnTime; + blockEntity.maxFuelTick = burnTime; + + if (fuel.hasCraftingRemainingItem()) { + if (fuel.getCount() <= 1) { + blockEntity.setItem(SLOT_FUEL, fuel.getCraftingRemainingItem()); + } else { + ItemStack copy = fuel.getCraftingRemainingItem().copy(); + copy.setCount(1); + + ItemEntity itemEntity = new ItemEntity(pLevel, + pPos.getX() + 0.5, + pPos.getY() + 0.2, + pPos.getZ() + 0.5, + copy); + pLevel.addFreshEntity(itemEntity); + + fuel.shrink(1); + } + } else { + fuel.shrink(1); + } + + blockEntity.setChanged(); + } else if (fuel.get(DataComponents.FOOD) != null) { + // 最后作为食物处理 + var foodComponent = fuel.get(DataComponents.FOOD); + if (foodComponent == null) return; + + int nutrition = foodComponent.nutrition(); + float saturation = foodComponent.saturation() * 2.0f * nutrition; + int tick = nutrition * 80 + (int) (saturation * 200); + + if (fuel.hasCraftingRemainingItem()) { + tick += 400; + } + + fuel.shrink(1); + + blockEntity.fuelTick = tick; + blockEntity.maxFuelTick = tick; + blockEntity.setChanged(); + } + } + } + + private void chargeEntity(EnergyStorage handler) { + if (this.level == null) return; + if (this.level.getGameTime() % 20 != 0) return; + + List entities = this.level.getEntitiesOfClass(Entity.class, new AABB(this.getBlockPos()).inflate(CHARGE_RADIUS)); + entities.forEach(entity -> { + var cap = entity.getCapability(Capabilities.EnergyStorage.ENTITY, null); + if (cap == null || !cap.canReceive()) return; + + int charged = cap.receiveEnergy(Math.min(handler.getEnergyStored(), CHARGE_OTHER_SPEED * 20), false); + handler.extractEnergy(charged, false); + }); + this.setChanged(); + } + + private void chargeItemStack(EnergyStorage handler) { + ItemStack stack = this.getItem(SLOT_CHARGE); + if (stack.isEmpty()) return; + + var consumer = stack.getCapability(Capabilities.EnergyStorage.ITEM); + if (consumer != null) { + if (consumer.getEnergyStored() < consumer.getMaxEnergyStored()) { + int charged = consumer.receiveEnergy(Math.min(CHARGE_OTHER_SPEED, handler.getEnergyStored()), false); + handler.extractEnergy(Math.min(charged, handler.getEnergyStored()), false); + } + } + this.setChanged(); + } + + private void chargeBlock(EnergyStorage handler) { + if (this.level == null) return; + + for (Direction direction : Direction.values()) { + var blockEntity = this.level.getBlockEntity(this.getBlockPos().relative(direction)); + if (blockEntity == null) return; + + var energy = level.getCapability(Capabilities.EnergyStorage.BLOCK, blockEntity.getBlockPos(), direction); + if (energy == null || !(blockEntity instanceof ChargingStationBlockEntity)) return; + + if (energy.canReceive() && energy.getEnergyStored() < energy.getMaxEnergyStored()) { + int receiveEnergy = energy.receiveEnergy(Math.min(handler.getEnergyStored(), CHARGE_OTHER_SPEED), false); + handler.extractEnergy(receiveEnergy, false); + + blockEntity.setChanged(); + this.setChanged(); + } + } + } + + public NonNullList getItems() { + return this.items; + } + + @Override + protected void loadAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) { + super.loadAdditional(tag, registries); + + if (tag.contains("Energy")) { +// getCapability(Capabilities.ENERGY).ifPresent(handler -> ((EnergyStorage) handler).deserializeNBT(tag.get("Energy"))); + } + this.fuelTick = tag.getInt("FuelTick"); + this.maxFuelTick = tag.getInt("MaxFuelTick"); + this.showRange = tag.getBoolean("ShowRange"); + this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); + ContainerHelper.loadAllItems(tag, this.items, registries); + } + + @Override + protected void saveAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) { + super.saveAdditional(tag, registries); + +// getCapability(Capabilities.ENERGY).ifPresent(handler -> tag.put("Energy", ((EnergyStorage) handler).serializeNBT())); + tag.putInt("FuelTick", this.fuelTick); + tag.putInt("MaxFuelTick", this.maxFuelTick); + tag.putBoolean("ShowRange", this.showRange); + ContainerHelper.saveAllItems(tag, this.items, registries); + } + + @Override + public int @NotNull [] getSlotsForFace(@NotNull Direction pSide) { + return new int[]{SLOT_FUEL}; + } + + @Override + public boolean canPlaceItemThroughFace(int pIndex, @NotNull ItemStack pItemStack, @Nullable Direction pDirection) { + return pIndex == SLOT_FUEL; + } + + @Override + public boolean canTakeItemThroughFace(int pIndex, @NotNull ItemStack pStack, @NotNull Direction pDirection) { + return false; + } + + @Override + public int getContainerSize() { + return this.items.size(); + } + + @Override + public boolean isEmpty() { + for (ItemStack itemstack : this.items) { + if (!itemstack.isEmpty()) { + return false; + } + } + + return true; + } + + @Override + public @NotNull ItemStack getItem(int pSlot) { + return this.items.get(pSlot); + } + + @Override + public @NotNull ItemStack removeItem(int pSlot, int pAmount) { + return ContainerHelper.removeItem(this.items, pSlot, pAmount); + } + + @Override + public @NotNull ItemStack removeItemNoUpdate(int pSlot) { + return ContainerHelper.takeItem(this.items, pSlot); + } + + @Override + public void setItem(int pSlot, ItemStack pStack) { + ItemStack itemstack = this.items.get(pSlot); + boolean flag = !pStack.isEmpty() && ItemStack.isSameItem(itemstack, pStack); + this.items.set(pSlot, pStack); + if (pStack.getCount() > this.getMaxStackSize()) { + pStack.setCount(this.getMaxStackSize()); + } + + if (pSlot == 0 && !flag) { + this.setChanged(); + } + } + + @Override + public boolean stillValid(@NotNull Player pPlayer) { + return Container.stillValidBlockEntity(this, pPlayer); + } + + @Override + public void clearContent() { + this.items.clear(); + } + + @Override + public @NotNull Component getDisplayName() { + return Component.translatable("container.superbwarfare.charging_station"); + } + + @Nullable + @Override + public AbstractContainerMenu createMenu(int pContainerId, @NotNull Inventory pPlayerInventory, @NotNull Player pPlayer) { + return new ChargingStationMenu(pContainerId, pPlayerInventory, this, this.dataAccess); + } + + @Override + public ClientboundBlockEntityDataPacket getUpdatePacket() { + return ClientboundBlockEntityDataPacket.create(this); + } + + @Override + public @NotNull CompoundTag getUpdateTag(HolderLookup.@NotNull Provider registries) { + CompoundTag compoundtag = new CompoundTag(); + ContainerHelper.saveAllItems(compoundtag, this.items, registries); + compoundtag.putBoolean("ShowRange", this.showRange); + return compoundtag; + } + + +// @Override +// public LazyOptional getCapability(Capability cap, Direction side) { +// if (cap == Capabilities.ENERGY) { +// return energyHandler.cast(); +// } +// if (!this.remove && side != null && cap == Capabilities.ITEM_HANDLER) { +// if (side == Direction.UP) { +// return itemHandlers[0].cast(); +// } else if (side == Direction.DOWN) { +// return itemHandlers[1].cast(); +// } else { +// return itemHandlers[2].cast(); +// } +// } +// return super.getCapability(cap, side); +// } +// +// @Override +// public void invalidateCaps() { +// super.invalidateCaps(); +// for (LazyOptional itemHandler : itemHandlers) itemHandler.invalidate(); +// energyHandler.invalidate(); +// } +// +// @Override +// public void reviveCaps() { +// super.reviveCaps(); +// this.itemHandlers = SidedInvWrapper.create(this, Direction.UP, Direction.DOWN, Direction.NORTH); +// this.energyHandler = LazyOptional.of(() -> new EnergyStorage(MAX_ENERGY)); +// } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/block/entity/ContainerBlockEntity.java b/src/main/java/com/atsuishio/superbwarfare/block/entity/ContainerBlockEntity.java new file mode 100644 index 000000000..8de94002f --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/block/entity/ContainerBlockEntity.java @@ -0,0 +1,139 @@ +package com.atsuishio.superbwarfare.block.entity; + +import com.atsuishio.superbwarfare.block.ContainerBlock; +import com.atsuishio.superbwarfare.init.ModBlockEntities; +import com.atsuishio.superbwarfare.tools.ParticleTool; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.NotNull; +import org.joml.Math; +import software.bernie.geckolib.animatable.GeoBlockEntity; +import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache; +import software.bernie.geckolib.animation.*; +import software.bernie.geckolib.util.GeckoLibUtil; + +public class ContainerBlockEntity extends BlockEntity implements GeoBlockEntity { + + public EntityType entityType; + public Entity entity = null; + public int tick = 0; + + private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this); + + public ContainerBlockEntity(BlockPos pos, BlockState state) { + super(ModBlockEntities.CONTAINER.get(), pos, state); + } + + public static void serverTick(Level pLevel, BlockPos pPos, BlockState pState, ContainerBlockEntity blockEntity) { + if (!pState.getValue(ContainerBlock.OPENED)) { + return; + } + + if (blockEntity.tick < 20) { + blockEntity.tick++; + blockEntity.setChanged(); + + if (blockEntity.tick == 18) { + ParticleTool.sendParticle((ServerLevel) pLevel, ParticleTypes.EXPLOSION, pPos.getX(), pPos.getY() + 1, pPos.getZ(), 40, 1.5, 1.5, 1.5, 1, false); + pLevel.playSound(null, pPos, SoundEvents.GENERIC_EXPLODE.value(), SoundSource.BLOCKS, 4.0F, (1.0F + (pLevel.random.nextFloat() - pLevel.random.nextFloat()) * 0.2F) * 0.7F); + } + } else { + if (blockEntity.entity != null) { + blockEntity.entity.setPos(pPos.getX() + 0.5 + (2 * Math.random() - 1) * 0.1f, pPos.getY() + 0.5 + (2 * Math.random() - 1) * 0.1f, pPos.getZ() + 0.5 + (2 * Math.random() - 1) * 0.1f); + pLevel.addFreshEntity(blockEntity.entity); + } else if (blockEntity.entityType != null) { + var entity = blockEntity.entityType.create(pLevel); + if (entity != null) { + entity.setPos(pPos.getX() + 0.5 + (2 * Math.random() - 1) * 0.1f, pPos.getY() + 0.5 + (2 * Math.random() - 1) * 0.1f, pPos.getZ() + 0.5 + (2 * Math.random() - 1) * 0.1f); + pLevel.addFreshEntity(entity); + } + } + + pLevel.setBlockAndUpdate(pPos, Blocks.AIR.defaultBlockState()); + } + } + + private PlayState predicate(AnimationState event) { + if (this.getBlockState().getValue(ContainerBlock.OPENED)) { + return event.setAndContinue(RawAnimation.begin().thenPlay("animation.container.open")); + } + return PlayState.STOP; + } + + @Override + public void registerControllers(AnimatableManager.ControllerRegistrar controllers) { + controllers.add(new AnimationController<>(this, "controller", 0, this::predicate)); + + } + + @Override + public AnimatableInstanceCache getAnimatableInstanceCache() { + return this.cache; + } + + @Override + protected void loadAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) { + super.loadAdditional(tag, registries); + + if (tag.contains("EntityType")) { + this.entityType = EntityType.byString(tag.getString("EntityType")).orElse(null); + } + if (tag.contains("Entity") && this.entityType != null && this.level != null) { + this.entity = this.entityType.create(this.level); + if (entity != null) { + entity.load(tag.getCompound("Entity")); + } + } + this.tick = tag.getInt("Tick"); + } + + @Override + protected void saveAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) { + super.saveAdditional(tag, registries); + + if (this.entity != null) { + tag.put("Entity", this.entity.getPersistentData()); + } + if (this.entityType != null) { + tag.putString("EntityType", EntityType.getKey(this.entityType).toString()); + } + tag.putInt("Tick", this.tick); + } + + @Override + public ClientboundBlockEntityDataPacket getUpdatePacket() { + return ClientboundBlockEntityDataPacket.create(this); + } + + + @Override + public @NotNull CompoundTag getUpdateTag(HolderLookup.@NotNull Provider registries) { + return this.saveWithFullMetadata(registries); + } + + @Override + public void saveToItem(@NotNull ItemStack stack, HolderLookup.@NotNull Provider registries) { + super.saveToItem(stack, registries); + + CompoundTag tag = new CompoundTag(); + if (this.entityType != null) { + tag.putString("EntityType", EntityType.getKey(this.entityType).toString()); + } + BlockItem.setBlockEntityData(stack, this.getType(), tag); + } + +} diff --git a/src/main/java/com/atsuishio/superbwarfare/block/entity/CreativeChargingStationBlockEntity.java b/src/main/java/com/atsuishio/superbwarfare/block/entity/CreativeChargingStationBlockEntity.java new file mode 100644 index 000000000..72b13455d --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/block/entity/CreativeChargingStationBlockEntity.java @@ -0,0 +1,75 @@ +package com.atsuishio.superbwarfare.block.entity; + +import com.atsuishio.superbwarfare.capability.energy.InfinityEnergyStorage; +import com.atsuishio.superbwarfare.init.ModBlockEntities; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.energy.IEnergyStorage; + +import java.util.List; + +/** + * Energy Data Slot Code based on @GoryMoon's Chargers + */ +public class CreativeChargingStationBlockEntity extends BlockEntity { + + public static final int CHARGE_RADIUS = 8; + + private final IEnergyStorage energyHandler; + + public CreativeChargingStationBlockEntity(BlockPos pos, BlockState state) { + super(ModBlockEntities.CREATIVE_CHARGING_STATION.get(), pos, state); + this.energyHandler = new InfinityEnergyStorage(); + } + + public static void serverTick(CreativeChargingStationBlockEntity blockEntity) { + if (blockEntity.level == null) return; + + if (blockEntity.energyHandler != null) { + blockEntity.chargeEntity(); + blockEntity.chargeBlock(); + } + } + + private void chargeEntity() { + if (this.level == null) return; + if (this.level.getGameTime() % 20 != 0) return; + + List entities = this.level.getEntitiesOfClass(Entity.class, new AABB(this.getBlockPos()).inflate(CHARGE_RADIUS)); + entities.forEach(entity -> { + var cap = entity.getCapability(Capabilities.EnergyStorage.ENTITY, null); + if (cap == null || !cap.canReceive()) return; + + cap.receiveEnergy(Integer.MAX_VALUE, false); + }); + } + + private void chargeBlock() { + if (this.level == null) return; + + for (Direction direction : Direction.values()) { + var blockEntity = this.level.getBlockEntity(this.getBlockPos().relative(direction)); + if (blockEntity == null) return; + + var energy = level.getCapability(Capabilities.EnergyStorage.BLOCK, blockEntity.getBlockPos(), direction); + if (energy == null || blockEntity instanceof CreativeChargingStationBlockEntity) continue; + + if (energy.canReceive() && energy.getEnergyStored() < energy.getMaxEnergyStored()) { + energy.receiveEnergy(Integer.MAX_VALUE, false); + blockEntity.setChanged(); + } + } + } + + @Override + public ClientboundBlockEntityDataPacket getUpdatePacket() { + return ClientboundBlockEntityDataPacket.create(this); + } + +} diff --git a/src/main/java/com/atsuishio/superbwarfare/block/entity/FuMO25BlockEntity.java b/src/main/java/com/atsuishio/superbwarfare/block/entity/FuMO25BlockEntity.java new file mode 100644 index 000000000..d6d757704 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/block/entity/FuMO25BlockEntity.java @@ -0,0 +1,245 @@ +package com.atsuishio.superbwarfare.block.entity; + +import com.atsuishio.superbwarfare.init.ModBlockEntities; +import com.atsuishio.superbwarfare.menu.FuMO25Menu; +import com.atsuishio.superbwarfare.network.dataslot.ContainerEnergyData; +import com.atsuishio.superbwarfare.tools.SeekTool; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.util.Mth; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import software.bernie.geckolib.animatable.GeoBlockEntity; +import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache; +import software.bernie.geckolib.animation.AnimatableManager; +import software.bernie.geckolib.util.GeckoLibUtil; + +import java.util.List; + +public class FuMO25BlockEntity extends BlockEntity implements MenuProvider, GeoBlockEntity { + + private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this); + + public static final int MAX_ENERGY = 1000000; + + // 固定距离,以后有人改动这个需要自行解决GUI渲染问题 + public static final int DEFAULT_RANGE = 96; + public static final int MAX_RANGE = 128; + public static final int GLOW_RANGE = 64; + + public static final int DEFAULT_ENERGY_COST = 256; + public static final int MAX_ENERGY_COST = 1024; + + public static final int DEFAULT_MIN_ENERGY = 64000; + + public static final int MAX_DATA_COUNT = 5; + +// private LazyOptional energyHandler; + + public FuncType type = FuncType.NORMAL; + public int time = 0; + public boolean powered = false; + public int tick = 0; + + protected final ContainerEnergyData dataAccess = new ContainerEnergyData() { + // TODO energy + @Override + public long get(int pIndex) { + return switch (pIndex) { +// case 0 -> FuMO25BlockEntity.this.energyHandler.map(EnergyStorage::getEnergyStored).orElse(0); + case 1 -> FuMO25BlockEntity.this.type.ordinal(); + case 2 -> FuMO25BlockEntity.this.time; + case 3 -> FuMO25BlockEntity.this.powered ? 1 : 0; + case 4 -> FuMO25BlockEntity.this.tick; + default -> 0; + }; + } + + @Override + public void set(int pIndex, long pValue) { + switch (pIndex) { + case 0 -> { + } +// FuMO25BlockEntity.this.energyHandler.ifPresent(handler -> handler.receiveEnergy((int) pValue, false)); + case 1 -> FuMO25BlockEntity.this.type = FuncType.values()[(int) pValue]; + case 2 -> FuMO25BlockEntity.this.time = (int) pValue; + case 3 -> FuMO25BlockEntity.this.powered = pValue == 1; + case 4 -> FuMO25BlockEntity.this.tick = (int) pValue; + } + } + + @Override + public int getCount() { + return MAX_DATA_COUNT; + } + }; + + public FuMO25BlockEntity(BlockPos pPos, BlockState pBlockState) { + super(ModBlockEntities.FUMO_25.get(), pPos, pBlockState); +// this.energyHandler = LazyOptional.of(() -> new EnergyStorage(MAX_ENERGY)); + } + + public static void serverTick(Level pLevel, BlockPos pPos, BlockState pState, T blockEntity) { + var radar = (FuMO25BlockEntity) blockEntity; + +// int energy = radar.energyHandler.map(EnergyStorage::getEnergyStored).orElse(0); + radar.tick++; + + FuncType funcType = radar.type; + int energyCost; + if (funcType == FuncType.WIDER) { + energyCost = MAX_ENERGY_COST; + } else { + energyCost = DEFAULT_ENERGY_COST; + } + +// if (energy < energyCost) { +// if (pState.getValue(FuMO25Block.POWERED)) { +// pLevel.setBlockAndUpdate(pPos, pState.setValue(FuMO25Block.POWERED, false)); +// pLevel.playSound(null, pPos, ModSounds.RADAR_SEARCH_END.get(), SoundSource.BLOCKS, 1.0F, 1.0F); +// radar.powered = false; +// setChanged(pLevel, pPos, pState); +// } +// if (radar.time > 0) { +// radar.time = 0; +// radar.setChanged(); +// } +// } else { +// if (!pState.getValue(FuMO25Block.POWERED)) { +// if (energy >= DEFAULT_MIN_ENERGY) { +// pLevel.setBlockAndUpdate(pPos, pState.setValue(FuMO25Block.POWERED, true)); +// pLevel.playSound(null, pPos, ModSounds.RADAR_SEARCH_START.get(), SoundSource.BLOCKS, 1.0F, 1.0F); +// radar.powered = true; +// setChanged(pLevel, pPos, pState); +// } +// } else { +// radar.energyHandler.ifPresent(handler -> handler.extractEnergy(energyCost, false)); +// if (radar.tick == 200) { +// pLevel.playSound(null, pPos, ModSounds.RADAR_SEARCH_IDLE.get(), SoundSource.BLOCKS, 1.0F, 1.0F); +// } +// +// if (radar.time > 0) { +// if (radar.time % 100 == 0) { +// radar.setGlowEffect(); +// } +// radar.time--; +// radar.setChanged(); +// } +// } +// } + + if (radar.tick >= 200) { + radar.tick = 0; + } + + if (radar.time <= 0 && radar.type != FuncType.NORMAL) { + radar.type = FuncType.NORMAL; + radar.setChanged(); + } + } + + private void setGlowEffect() { + if (this.type != FuncType.GLOW) return; + + Level level = this.level; + if (level == null) return; + BlockPos pos = this.getBlockPos(); + List entities = SeekTool.getEntitiesWithinRange(pos, level, GLOW_RANGE); + entities.forEach(e -> { + if (e instanceof LivingEntity living) { + living.addEffect(new MobEffectInstance(MobEffects.GLOWING, 100, 0, true, false)); + } + }); + } + + @Override + protected void loadAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) { + super.loadAdditional(tag, registries); + + if (tag.contains("Energy")) { +// getCapability(Capabilities.ENERGY).ifPresent(handler -> ((EnergyStorage) handler).deserializeNBT(tag.get("Energy"))); + } + this.type = FuncType.values()[Mth.clamp(tag.getInt("Type"), 0, 3)]; + this.time = tag.getInt("Time"); + this.powered = tag.getBoolean("Powered"); + } + + @Override + protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.saveAdditional(tag, registries); + +// getCapability(Capabilities.ENERGY).ifPresent(handler -> tag.put("Energy", ((EnergyStorage) handler).serializeNBT())); + tag.putInt("Type", this.type.ordinal()); + tag.putInt("Time", this.time); + tag.putBoolean("Powered", this.powered); + } + + @Override + public @NotNull Component getDisplayName() { + return Component.literal(""); + } + + @Nullable + @Override + public AbstractContainerMenu createMenu(int pContainerId, @NotNull Inventory pPlayerInventory, @NotNull Player pPlayer) { + if (this.level == null) return null; + return new FuMO25Menu(pContainerId, pPlayerInventory, ContainerLevelAccess.create(this.level, this.getBlockPos()), this.dataAccess); + } + + @Override + public ClientboundBlockEntityDataPacket getUpdatePacket() { + return ClientboundBlockEntityDataPacket.create(this); + } + +// @Override +// public LazyOptional getCapability(Capability cap, Direction side) { +// if (cap == Capabilities.ENERGY) { +// return energyHandler.cast(); +// } +// return super.getCapability(cap, side); +// } +// +// @Override +// public void invalidateCapabilities() { +// this.energyHandler.invalidate(); +// super.invalidateCapabilities(); +// } +// +// +// @Override +// public void reviveCaps() { +// super.reviveCaps(); +// this.energyHandler = LazyOptional.of(() -> new EnergyStorage(MAX_ENERGY)); +// } + + @Override + public void registerControllers(AnimatableManager.ControllerRegistrar controllers) { + } + + @Override + public AnimatableInstanceCache getAnimatableInstanceCache() { + return this.cache; + } + + public enum FuncType { + NORMAL, + WIDER, + GLOW, + GUIDE + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/capability/CapabilityHandler.java b/src/main/java/com/atsuishio/superbwarfare/capability/CapabilityHandler.java new file mode 100644 index 000000000..452395e1b --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/capability/CapabilityHandler.java @@ -0,0 +1,29 @@ +package com.atsuishio.superbwarfare.capability; + +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.player.Player; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; + +public class CapabilityHandler { + + private static final CapabilityHandler INSTANCE = new CapabilityHandler(); + + public static void register(IEventBus bus) { + bus.addListener(INSTANCE::registerCapabilities); + } + + public void registerCapabilities(RegisterCapabilitiesEvent event) { + for (EntityType entityType : BuiltInRegistries.ENTITY_TYPE) { + event.registerEntity(ModCapabilities.LASER_CAPABILITY, entityType, + (entity, ctx) -> { + if (entity instanceof Player) { + return new LaserCapability.LaserCapabilityImpl(); + } + return null; + }); + } + } + +} diff --git a/src/main/java/com/atsuishio/superbwarfare/capability/LaserCapability.java b/src/main/java/com/atsuishio/superbwarfare/capability/LaserCapability.java new file mode 100644 index 000000000..f228bcd8e --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/capability/LaserCapability.java @@ -0,0 +1,100 @@ +package com.atsuishio.superbwarfare.capability; + +import com.atsuishio.superbwarfare.ModUtils; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Player; +import net.neoforged.neoforge.capabilities.ICapabilityProvider; +import net.neoforged.neoforge.common.util.INBTSerializable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; + +public class LaserCapability { + + public static ResourceLocation ID = ModUtils.loc("laser_capability"); + + public interface ILaserCapability extends INBTSerializable { + + void init(LaserHandler handler); + + void start(); + + void tick(); + + void stop(); + + void end(); + + } + + public static class LaserCapabilityImpl implements ILaserCapability { + + public LaserHandler laserHandler; + + @Override + public void init(LaserHandler handler) { + this.laserHandler = handler; + } + + @Override + public void start() { + this.laserHandler.start(); + } + + @Override + public void tick() { + } + + @Override + public void stop() { + if (this.laserHandler != null) { + this.laserHandler.stop(); + } + } + + @Override + public void end() { + if (this.laserHandler != null) { + this.laserHandler.end(); + } + } + + @Override + public @UnknownNullability CompoundTag serializeNBT(HolderLookup.@NotNull Provider provider) { + CompoundTag tag = new CompoundTag(); + if (this.laserHandler != null) { + tag.put("Laser", this.laserHandler.writeNBT()); + } + return tag; + } + + @Override + public void deserializeNBT(HolderLookup.@NotNull Provider provider, @NotNull CompoundTag compoundTag) { + if (compoundTag.contains("Laser") && this.laserHandler != null) { + this.laserHandler.readNBT(compoundTag.getCompound("Laser")); + } + } + } + + public static class LaserCapabilityProvider implements ICapabilityProvider, INBTSerializable { + + private final LaserCapabilityImpl instance = new LaserCapabilityImpl(); + + @Override + public @Nullable ILaserCapability getCapability(@NotNull Player object, Void context) { + return object.getCapability(ModCapabilities.LASER_CAPABILITY, context); + } + + @Override + public @UnknownNullability CompoundTag serializeNBT(HolderLookup.@NotNull Provider provider) { + return instance.serializeNBT(provider); + } + + @Override + public void deserializeNBT(HolderLookup.@NotNull Provider provider, @NotNull CompoundTag nbt) { + instance.deserializeNBT(provider, nbt); + } + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/capability/LaserHandler.java b/src/main/java/com/atsuishio/superbwarfare/capability/LaserHandler.java new file mode 100644 index 000000000..fdff3308b --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/capability/LaserHandler.java @@ -0,0 +1,90 @@ +package com.atsuishio.superbwarfare.capability; + +import com.atsuishio.superbwarfare.entity.projectile.AbstractLaserEntity; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.LivingEntity; + +public class LaserHandler { + + public boolean isUsing; + public final LivingEntity entity; + public final AbstractLaserEntity laserEntity; + private int tick; + + public LaserHandler(LivingEntity entity, AbstractLaserEntity laserEntity) { + this.entity = entity; + this.laserEntity = laserEntity; + } + + public void start() { + this.tick = 0; + this.isUsing = true; + + if (this.entity.level() instanceof ServerLevel level) { + level.addFreshEntity(this.laserEntity); + } + } + + public void tick() { + if (this.isUsing) { + this.tick++; + + if (this.tick > laserEntity.getDuration()) { + this.stop(); + } + } + } + + /** + * 终止光束,并带有淡出效果 + */ + public void stop() { + if (!this.isUsing) return; + + this.isUsing = false; + this.tick = 0; + if (this.laserEntity != null) { + this.laserEntity.setDuration(3); + } + } + + /** + * 直接终止光束 + */ + public void end() { + if (!this.isUsing) return; + + this.isUsing = false; + this.tick = 0; + if (this.laserEntity != null) { + this.laserEntity.discard(); + } + } + + public int getTick() { + return this.tick; + } + + public CompoundTag writeNBT() { + CompoundTag compoundTag = new CompoundTag(); + if (this.isUsing) { + compoundTag.putInt("Tick", this.tick); + } + return compoundTag; + } + + public void readNBT(Tag nbt) { + CompoundTag compoundTag = (CompoundTag) nbt; + this.isUsing = compoundTag.contains("Tick"); + if (this.isUsing) { + this.tick = compoundTag.getInt("Tick"); + } + } + + public boolean isUsable() { + return !this.isUsing; + } + +} diff --git a/src/main/java/com/atsuishio/superbwarfare/capability/ModCapabilities.java b/src/main/java/com/atsuishio/superbwarfare/capability/ModCapabilities.java new file mode 100644 index 000000000..f7a6cd558 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/capability/ModCapabilities.java @@ -0,0 +1,10 @@ +package com.atsuishio.superbwarfare.capability; + +import com.atsuishio.superbwarfare.ModUtils; +import net.neoforged.neoforge.capabilities.EntityCapability; + +public class ModCapabilities { + + public static final EntityCapability LASER_CAPABILITY = EntityCapability.createVoid(ModUtils.loc("laser_capability"), LaserCapability.ILaserCapability.class); + +} diff --git a/src/main/java/com/atsuishio/superbwarfare/capability/energy/InfinityEnergyStorage.java b/src/main/java/com/atsuishio/superbwarfare/capability/energy/InfinityEnergyStorage.java new file mode 100644 index 000000000..66dbaf85c --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/capability/energy/InfinityEnergyStorage.java @@ -0,0 +1,38 @@ +package com.atsuishio.superbwarfare.capability.energy; + +import net.neoforged.neoforge.energy.IEnergyStorage; + +/** + * 无限供电能力,纯逆天 + */ +public class InfinityEnergyStorage implements IEnergyStorage { + @Override + public int receiveEnergy(int maxReceive, boolean simulate) { + return 0; + } + + @Override + public int extractEnergy(int maxExtract, boolean simulate) { + return maxExtract; + } + + @Override + public int getEnergyStored() { + return Integer.MAX_VALUE; + } + + @Override + public int getMaxEnergyStored() { + return Integer.MAX_VALUE; + } + + @Override + public boolean canExtract() { + return true; + } + + @Override + public boolean canReceive() { + return false; + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/capability/energy/ItemEnergyProvider.java b/src/main/java/com/atsuishio/superbwarfare/capability/energy/ItemEnergyProvider.java new file mode 100644 index 000000000..4ddd907bb --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/capability/energy/ItemEnergyProvider.java @@ -0,0 +1,18 @@ +package com.atsuishio.superbwarfare.capability.energy; + +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.capabilities.ICapabilityProvider; +import net.neoforged.neoforge.energy.IEnergyStorage; +import org.jetbrains.annotations.NotNull; + +public class ItemEnergyProvider implements ICapabilityProvider { + + public ItemEnergyProvider(ItemStack stack, int energyCapacity) { + } + + @Override + public @org.jetbrains.annotations.Nullable IEnergyStorage getCapability(@NotNull ItemStack object, Void context) { + return object.getCapability(Capabilities.EnergyStorage.ITEM); + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/capability/energy/ItemEnergyStorage.java b/src/main/java/com/atsuishio/superbwarfare/capability/energy/ItemEnergyStorage.java new file mode 100644 index 000000000..f5173e1a9 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/capability/energy/ItemEnergyStorage.java @@ -0,0 +1,40 @@ +package com.atsuishio.superbwarfare.capability.energy; + +import com.atsuishio.superbwarfare.component.ModDataComponents; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.energy.EnergyStorage; + +public class ItemEnergyStorage extends EnergyStorage { + + private final ItemStack stack; + + public ItemEnergyStorage(ItemStack stack, int capacity) { + super(capacity, Integer.MAX_VALUE, Integer.MAX_VALUE); + + this.stack = stack; + var component = stack.get(ModDataComponents.ENERGY); + this.energy = component == null ? 0 : component; + } + + @Override + public int receiveEnergy(int maxReceive, boolean simulate) { + int received = super.receiveEnergy(maxReceive, simulate); + + if (received > 0 && !simulate) { + stack.set(ModDataComponents.ENERGY, getEnergyStored()); + } + + return received; + } + + @Override + public int extractEnergy(int maxExtract, boolean simulate) { + int extracted = super.extractEnergy(maxExtract, simulate); + + if (extracted > 0 && !simulate) { + stack.set(ModDataComponents.ENERGY, getEnergyStored()); + } + + return extracted; + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/capability/energy/SyncedEntityEnergyStorage.java b/src/main/java/com/atsuishio/superbwarfare/capability/energy/SyncedEntityEnergyStorage.java new file mode 100644 index 000000000..0d0c1eebb --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/capability/energy/SyncedEntityEnergyStorage.java @@ -0,0 +1,66 @@ +package com.atsuishio.superbwarfare.capability.energy; + +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.Tag; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.SynchedEntityData; +import net.neoforged.neoforge.energy.EnergyStorage; +import org.jetbrains.annotations.NotNull; + +/** + * 自动同步的实体能量存储能力,会和客户端自动同步实体的当前能量值 + */ +public class SyncedEntityEnergyStorage extends EnergyStorage { + + protected SynchedEntityData entityData; + protected EntityDataAccessor energyDataAccessor; + + /** + * 自动同步的实体能量存储能力 + * + * @param capacity 能量上限 + * @param data 实体的entityData + * @param energyDataAccessor 能量的EntityDataAccessor + */ + public SyncedEntityEnergyStorage(int capacity, SynchedEntityData data, EntityDataAccessor energyDataAccessor) { + super(capacity, capacity, capacity, 0); + + this.entityData = data; + this.energyDataAccessor = energyDataAccessor; + } + + @Override + public int receiveEnergy(int maxReceive, boolean simulate) { + var received = super.receiveEnergy(maxReceive, simulate); + + if (!simulate) { + entityData.set(energyDataAccessor, this.energy); + } + + return received; + } + + @Override + public int extractEnergy(int maxExtract, boolean simulate) { + var extracted = super.extractEnergy(maxExtract, simulate); + + if (!simulate) { + entityData.set(energyDataAccessor, energy); + } + + return extracted; + } + + @Override + public int getEnergyStored() { + // 获取同步数据,保证客户端能正确获得能量值 + return entityData.get(energyDataAccessor); + } + + @Override + public void deserializeNBT(HolderLookup.@NotNull Provider provider, @NotNull Tag nbt) { + super.deserializeNBT(provider, nbt); + entityData.set(energyDataAccessor, energy); + } + +} diff --git a/src/main/java/com/atsuishio/superbwarfare/client/AnimationTicker.java b/src/main/java/com/atsuishio/superbwarfare/client/AnimationTicker.java new file mode 100644 index 000000000..54c8311a8 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/client/AnimationTicker.java @@ -0,0 +1,82 @@ +package com.atsuishio.superbwarfare.client; + +/** + * Code based on @EEEAB's EEEABsMobs and @Mercurows's DreamaticVoyage + */ +public class AnimationTicker { + + private int tick; + private int prevTick; + private int duration; + + public AnimationTicker(int duration) { + this.tick = 0; + this.prevTick = 0; + this.duration = duration; + } + + public void setDuration(int duration) { + this.tick = 0; + this.prevTick = 0; + this.duration = duration; + } + + public int getTick() { + return this.tick; + } + + public boolean isStopped() { + return this.tick == 0 && this.prevTick == 0; + } + + public boolean isEnded() { + return this.tick == this.duration || this.prevTick == this.duration; + } + + public int getPrevTick() { + return this.prevTick; + } + + public int getDuration() { + return this.duration; + } + + public void changeTimer(boolean flag) { + changeTimer(flag, 1); + } + + public void changeTimer(boolean add, int time) { + if (add) { + increaseTimer(time); + } else { + decreaseTimer(time); + } + } + + public void increaseTimer(int time) { + int newTime = this.tick + time; + if (newTime <= duration && newTime >= 0) { + this.tick = newTime; + } else { + this.tick = newTime < 0 ? 0 : duration; + } + } + + public void decreaseTimer(int time) { + if (this.tick - time > 0.0D) { + this.tick -= time; + } else { + this.tick = 0; + } + } + + public void updatePrevTimer() { + this.prevTick = this.tick; + } + + public void resetTimer() { + this.tick = 0; + this.prevTick = 0; + } + +} diff --git a/src/main/java/com/atsuishio/superbwarfare/component/ModDataComponents.java b/src/main/java/com/atsuishio/superbwarfare/component/ModDataComponents.java index 650e167f3..9365eaff8 100644 --- a/src/main/java/com/atsuishio/superbwarfare/component/ModDataComponents.java +++ b/src/main/java/com/atsuishio/superbwarfare/component/ModDataComponents.java @@ -1,6 +1,7 @@ package com.atsuishio.superbwarfare.component; import com.atsuishio.superbwarfare.ModUtils; +import com.mojang.serialization.Codec; import net.minecraft.core.BlockPos; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.registries.Registries; @@ -19,6 +20,11 @@ public class ModDataComponents { builder -> builder.persistent(BlockPos.CODEC) ); + public static final DeferredHolder, DataComponentType> ENERGY = register( + "energy", + builder -> builder.persistent(Codec.INT) + ); + private static DeferredHolder, DataComponentType> register(String name, UnaryOperator> builderOperator) { return DATA_COMPONENT_TYPES.register(name, () -> builderOperator.apply(DataComponentType.builder()).build()); } diff --git a/src/main/java/com/atsuishio/superbwarfare/entity/projectile/AbstractLaserEntity.java b/src/main/java/com/atsuishio/superbwarfare/entity/projectile/AbstractLaserEntity.java new file mode 100644 index 000000000..cdf50c70c --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/entity/projectile/AbstractLaserEntity.java @@ -0,0 +1,273 @@ +package com.atsuishio.superbwarfare.entity.projectile; + +import com.atsuishio.superbwarfare.client.AnimationTicker; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializers; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.TraceableEntity; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.level.material.PushReaction; +import net.minecraft.world.phys.*; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * Code based on @BobMowzie's MowziesMobs, @EEEAB's EEEABsMobs and @Mercurows's DreamaticVoyage + */ +public abstract class AbstractLaserEntity extends Entity implements TraceableEntity { + + public LivingEntity caster; + public float yaw, pitch; + public float preYaw, prePitch; + public double endPosX, endPosY, endPosZ; + public double collidePosX, collidePosY, collidePosZ; + public double prevCollidePosX, prevCollidePosY, prevCollidePosZ; + public Direction blockSide = null; + public boolean on = true; + public AnimationTicker ticker = new AnimationTicker(3); + + private static final EntityDataAccessor DATA_CASTER_ID = SynchedEntityData.defineId(AbstractLaserEntity.class, EntityDataSerializers.INT); + private static final EntityDataAccessor DATA_YAW = SynchedEntityData.defineId(AbstractLaserEntity.class, EntityDataSerializers.FLOAT); + private static final EntityDataAccessor DATA_PITCH = SynchedEntityData.defineId(AbstractLaserEntity.class, EntityDataSerializers.FLOAT); + private static final EntityDataAccessor DATA_DURATION = SynchedEntityData.defineId(AbstractLaserEntity.class, EntityDataSerializers.INT); + private static final EntityDataAccessor DATA_COUNT_DOWN = SynchedEntityData.defineId(AbstractLaserEntity.class, EntityDataSerializers.INT); + + public AbstractLaserEntity(EntityType type, Level level, int countDown) { + super(type, level); + this.setCountDown(countDown); + this.noCulling = true; + } + + @Override + public void tick() { + super.tick(); + this.prevCollidePosX = this.collidePosX; + this.prevCollidePosY = this.collidePosY; + this.prevCollidePosZ = this.collidePosZ; + this.preYaw = this.yaw; + this.prePitch = this.pitch; + this.yaw = this.getYaw(); + this.pitch = this.getPitch(); + this.xo = this.getX(); + this.yo = this.getY(); + this.zo = this.getZ(); + if (this.tickCount == 1 && this.level().isClientSide) { + this.caster = (LivingEntity) this.level().getEntity(getCasterId()); + } + + this.beamTick(); + + if ((!this.on && this.ticker.isStopped()) || (this.caster != null && !this.caster.isAlive())) { + this.discard(); + } + this.ticker.changeTimer(this.on && this.isAccumulating()); + + if (this.tickCount - this.getCountDown() > this.getDuration()) { + this.on = false; + } + } + +// @Override +// public Packet getAddEntityPacket() { +// return NetworkHooks.getEntitySpawningPacket(this); +// } + + @Override + protected void readAdditionalSaveData(CompoundTag pCompound) { + + } + + @Override + protected void addAdditionalSaveData(CompoundTag pCompound) { + + } + + protected void beamTick() { + } + + @Nullable + public LivingEntity getOwner() { + return caster; + } + + @Override + public boolean isPickable() { + return super.isPickable(); + } + + @Override + public void push(Entity entityIn) { + } + + @Override + public PushReaction getPistonPushReaction() { + return PushReaction.IGNORE; + } + + + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + builder + .define(DATA_CASTER_ID, -1) + .define(DATA_YAW, 0F) + .define(DATA_PITCH, 0F) + .define(DATA_DURATION, 0) + .define(DATA_COUNT_DOWN, 0); + } + + public void setCasterId(int id) { + this.entityData.set(DATA_CASTER_ID, id); + } + + public int getCasterId() { + return this.entityData.get(DATA_CASTER_ID); + } + + public boolean isAccumulating() { + return this.tickCount > this.getCountDown(); + } + + public float getYaw() { + return getEntityData().get(DATA_YAW); + } + + public void setYaw(float rotAngle) { + getEntityData().set(DATA_YAW, rotAngle); + } + + public float getPitch() { + return getEntityData().get(DATA_PITCH); + } + + public void setPitch(float rotAngle) { + getEntityData().set(DATA_PITCH, rotAngle); + } + + public int getDuration() { + return getEntityData().get(DATA_DURATION); + } + + public void setDuration(int duration) { + getEntityData().set(DATA_DURATION, duration); + } + + public int getCountDown() { + return getEntityData().get(DATA_COUNT_DOWN); + } + + public void setCountDown(int countDown) { + getEntityData().set(DATA_COUNT_DOWN, countDown); + } + + protected void calculateEndPos(double radius) { + if (level().isClientSide()) { + endPosX = getX() + radius * Math.cos(yaw) * Math.cos(pitch); + endPosZ = getZ() + radius * Math.sin(yaw) * Math.cos(pitch); + endPosY = getY() + radius * Math.sin(pitch); + } else { + endPosX = getX() + radius * Math.cos(getYaw()) * Math.cos(getPitch()); + endPosZ = getZ() + radius * Math.sin(getYaw()) * Math.cos(getPitch()); + endPosY = getY() + radius * Math.sin(getPitch()); + } + } + + public CustomHitResult raytraceEntities(Level world, Vec3 from, Vec3 to) { + CustomHitResult result = new CustomHitResult(); + result.setBlockHit(world.clip(new ClipContext(from, to, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this))); + if (result.getBlockHit() != null) { + Vec3 hitVec = result.getBlockHit().getLocation(); + collidePosX = hitVec.x; + collidePosY = hitVec.y; + collidePosZ = hitVec.z; + blockSide = result.getBlockHit().getDirection(); + } else { + collidePosX = endPosX; + collidePosY = endPosY; + collidePosZ = endPosZ; + blockSide = null; + } + List entities = world.getEntitiesOfClass(LivingEntity.class, new AABB(Math.min(getX(), collidePosX), Math.min(getY(), collidePosY), Math.min(getZ(), collidePosZ), Math.max(getX(), collidePosX), Math.max(getY(), collidePosY), Math.max(getZ(), collidePosZ)).inflate(1, 1, 1)); + for (LivingEntity entity : entities) { + if (entity == this.caster) { + continue; + } + float pad = entity.getPickRadius() + getBaseScale(); + AABB aabb = entity.getBoundingBox().inflate(pad, pad, pad); + Optional hit = aabb.clip(from, to); + if (aabb.contains(from)) { + result.addEntityHit(entity); + } else if (hit.isPresent()) { + result.addEntityHit(entity); + } + } + return result; + } + + @Override + public boolean isAttackable() { + return false; + } + + @Override + public boolean displayFireAnimation() { + return false; + } + + protected void onHit(HitResult hitResult) { + HitResult.Type hitresult$type = hitResult.getType(); + if (hitresult$type == HitResult.Type.ENTITY) { + this.onHitEntity((EntityHitResult) hitResult); + this.level().gameEvent(GameEvent.PROJECTILE_LAND, hitResult.getLocation(), GameEvent.Context.of(this, null)); + } else if (hitresult$type == HitResult.Type.BLOCK) { + BlockHitResult blockhitresult = (BlockHitResult) hitResult; + this.onHitBlock(blockhitresult); + BlockPos blockpos = blockhitresult.getBlockPos(); + this.level().gameEvent(GameEvent.PROJECTILE_LAND, blockpos, GameEvent.Context.of(this, this.level().getBlockState(blockpos))); + } + } + + protected void onHitEntity(EntityHitResult result) { + } + + protected void onHitBlock(BlockHitResult result) { + } + + protected float getBaseScale() { + return 0.5F; + } + + public static class CustomHitResult { + + private BlockHitResult blockHit; + private final List entities = new ArrayList<>(); + + public BlockHitResult getBlockHit() { + return blockHit; + } + + public List getEntities() { + return entities; + } + + public void setBlockHit(HitResult rayTraceResult) { + if (rayTraceResult.getType() == HitResult.Type.BLOCK) + this.blockHit = (BlockHitResult) rayTraceResult; + } + + public void addEntityHit(LivingEntity entity) { + entities.add(entity); + } + } + +} diff --git a/src/main/java/com/atsuishio/superbwarfare/entity/projectile/LaserEntity.java b/src/main/java/com/atsuishio/superbwarfare/entity/projectile/LaserEntity.java new file mode 100644 index 000000000..6b0af21c4 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/entity/projectile/LaserEntity.java @@ -0,0 +1,141 @@ +package com.atsuishio.superbwarfare.entity.projectile; + +import com.atsuishio.superbwarfare.init.ModEntities; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +/** + * Code based on @BobMowzie's MowziesMobs, @EEEAB's EEEABsMobs and @Mercurows's DreamaticVoyage + */ +public class LaserEntity extends AbstractLaserEntity { + + public static final double RADIUS = 512D; + + public LaserEntity(EntityType type, Level level) { + super(type, level, 1); + } + + public LaserEntity(Level level, LivingEntity caster, double x, double y, double z, float yaw, float pitch, int duration) { + super(ModEntities.LASER.get(), level, 1); + this.caster = caster; + this.setYaw(yaw); + this.setPitch(pitch); + this.setDuration(duration); + this.setPos(x, y, z); + this.calculateEndPos(RADIUS); + if (!level().isClientSide) { + setCasterId(caster.getId()); + } + } + + @Override + public void beamTick() { + if (!this.level().isClientSide) { + if (this.caster instanceof Player) { + this.updateWithPlayer(); + } else if (this.caster != null) { + this.updateWithEntity(); + } + } + + if (caster != null) { + this.yaw = (float) Math.toRadians(caster.yHeadRot + 90); + this.pitch = (float) -Math.toRadians(caster.getXRot()); + } + + if (this.tickCount >= this.getCountDown()) { + this.calculateEndPos(RADIUS); + + raytraceEntities(this.level(), new Vec3(getX(), getY(), getZ()), new Vec3(endPosX, endPosY, endPosZ)); + + if (this.blockSide != null) { + this.spawnExplosionParticles(); + } + } + } + + @Override + public CustomHitResult raytraceEntities(Level world, Vec3 from, Vec3 to) { + CustomHitResult result = new CustomHitResult(); + result.setBlockHit(this.level().clip(new ClipContext(new Vec3(getX(), getY(), getZ()), new Vec3(endPosX, endPosY, endPosZ), + ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this))); + if (result.getBlockHit() != null) { + Vec3 hitVec = result.getBlockHit().getLocation(); + collidePosX = hitVec.x; + collidePosY = hitVec.y; + collidePosZ = hitVec.z; + blockSide = result.getBlockHit().getDirection(); + } else { + List entities = world.getEntitiesOfClass(LivingEntity.class, new AABB(Math.min(getX(), collidePosX), Math.min(getY(), collidePosY), Math.min(getZ(), collidePosZ), Math.max(getX(), collidePosX), Math.max(getY(), collidePosY), Math.max(getZ(), collidePosZ)).inflate(1, 1, 1)); + for (LivingEntity entity : entities) { + if (entity == this.caster) { + continue; + } + float pad = entity.getPickRadius() + getBaseScale(); + AABB aabb = entity.getBoundingBox().inflate(pad, pad, pad); + Optional hit = aabb.clip(from, to); + if (aabb.contains(from)) { + result.addEntityHit(entity); + } else if (hit.isPresent()) { + result.addEntityHit(entity); + } + } + + var target = result.getEntities().stream().min(Comparator.comparingDouble(e -> e.distanceToSqr(this.caster))); + if (target.isPresent()) { + collidePosX = target.get().getX(); + collidePosY = target.get().getY(); + collidePosZ = target.get().getZ(); + } else { + collidePosX = endPosX; + collidePosY = endPosY; + collidePosZ = endPosZ; + } + blockSide = null; + } + + return result; + } + + public void spawnExplosionParticles() { + } + + @Override + protected void readAdditionalSaveData(CompoundTag compoundTag) { + if (this.caster == null) { + discard(); + } + } + + @Override + public boolean shouldRenderAtSqrDistance(double distance) { + return true; + } + + private void updateWithPlayer() { + this.setYaw((float) Math.toRadians(caster.yHeadRot + 90)); + this.setPitch((float) Math.toRadians(-caster.getXRot())); + Vec3 vecOffset = caster.getLookAngle().normalize().scale(1.25); + this.setPos(caster.getX() + vecOffset.x(), caster.getY() + caster.getBbHeight() * 0.5F + vecOffset.y(), caster.getZ() + vecOffset.z()); + } + + private void updateWithEntity() { + double radians = Math.toRadians(this.caster.yHeadRot + 90); + this.setYaw((float) radians); + this.setPitch((float) ((double) (-this.caster.getXRot()) * Math.PI / 180.0)); + double offsetX = Math.cos(radians) * (float) 0.0; + double offsetZ = Math.sin(radians) * (float) 0.0; + this.setPos(this.caster.getX() + offsetX, this.caster.getY((float) 0.75), this.caster.getZ() + offsetZ); + } + +} diff --git a/src/main/java/com/atsuishio/superbwarfare/init/ModBlockEntities.java b/src/main/java/com/atsuishio/superbwarfare/init/ModBlockEntities.java new file mode 100644 index 000000000..05a2a5f32 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/init/ModBlockEntities.java @@ -0,0 +1,26 @@ +package com.atsuishio.superbwarfare.init; + +import com.atsuishio.superbwarfare.ModUtils; +import com.atsuishio.superbwarfare.block.entity.ChargingStationBlockEntity; +import com.atsuishio.superbwarfare.block.entity.ContainerBlockEntity; +import com.atsuishio.superbwarfare.block.entity.CreativeChargingStationBlockEntity; +import com.atsuishio.superbwarfare.block.entity.FuMO25BlockEntity; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredRegister; + +public class ModBlockEntities { + + public static final DeferredRegister> REGISTRY = DeferredRegister.create(BuiltInRegistries.BLOCK_ENTITY_TYPE, ModUtils.MODID); + + public static final DeferredHolder, BlockEntityType> CONTAINER = REGISTRY.register("container", + () -> BlockEntityType.Builder.of(ContainerBlockEntity::new, ModBlocks.CONTAINER.get()).build(null)); + public static final DeferredHolder, BlockEntityType> CHARGING_STATION = REGISTRY.register("charging_station", + () -> BlockEntityType.Builder.of(ChargingStationBlockEntity::new, ModBlocks.CHARGING_STATION.get()).build(null)); + public static final DeferredHolder, BlockEntityType> CREATIVE_CHARGING_STATION = REGISTRY.register("creative_charging_station", + () -> BlockEntityType.Builder.of(CreativeChargingStationBlockEntity::new, ModBlocks.CREATIVE_CHARGING_STATION.get()).build(null)); + public static final DeferredHolder, BlockEntityType> FUMO_25 = REGISTRY.register("fumo_25", + () -> BlockEntityType.Builder.of(FuMO25BlockEntity::new, ModBlocks.FUMO_25.get()).build(null)); + +} diff --git a/src/main/java/com/atsuishio/superbwarfare/init/ModBlocks.java b/src/main/java/com/atsuishio/superbwarfare/init/ModBlocks.java index 4453d656c..9a0ae9f9b 100644 --- a/src/main/java/com/atsuishio/superbwarfare/init/ModBlocks.java +++ b/src/main/java/com/atsuishio/superbwarfare/init/ModBlocks.java @@ -1,10 +1,7 @@ package com.atsuishio.superbwarfare.init; import com.atsuishio.superbwarfare.ModUtils; -import com.atsuishio.superbwarfare.block.BarbedWireBlock; -import com.atsuishio.superbwarfare.block.DragonTeethBlock; -import com.atsuishio.superbwarfare.block.JumpPadBlock; -import com.atsuishio.superbwarfare.block.ReforgingTableBlock; +import com.atsuishio.superbwarfare.block.*; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.SoundType; @@ -46,9 +43,8 @@ public class ModBlocks { () -> new Block(BlockBehaviour.Properties.of().instrument(NoteBlockInstrument.BASEDRUM).sound(SoundType.METAL).strength(5f, 6f).requiresCorrectToolForDrops())); public static final DeferredHolder CEMENTED_CARBIDE_BLOCK = REGISTRY.register("cemented_carbide_block", () -> new Block(BlockBehaviour.Properties.of().instrument(NoteBlockInstrument.BASEDRUM).sound(SoundType.METAL).strength(5f, 6f).requiresCorrectToolForDrops())); - // TODO blocks -// public static final DeferredHolder CONTAINER = REGISTRY.register("container", ContainerBlock::new); -// public static final DeferredHolder CHARGING_STATION = REGISTRY.register("charging_station", ChargingStationBlock::new); -// public static final DeferredHolder CREATIVE_CHARGING_STATION = REGISTRY.register("creative_charging_station", CreativeChargingStationBlock::new); -// public static final DeferredHolder FUMO_25 = REGISTRY.register("fumo_25", FuMO25Block::new); + public static final DeferredHolder CONTAINER = REGISTRY.register("container", ContainerBlock::new); + public static final DeferredHolder CHARGING_STATION = REGISTRY.register("charging_station", ChargingStationBlock::new); + public static final DeferredHolder CREATIVE_CHARGING_STATION = REGISTRY.register("creative_charging_station", () -> new CreativeChargingStationBlock()); + public static final DeferredHolder FUMO_25 = REGISTRY.register("fumo_25", FuMO25Block::new); } diff --git a/src/main/java/com/atsuishio/superbwarfare/init/ModEntities.java b/src/main/java/com/atsuishio/superbwarfare/init/ModEntities.java new file mode 100644 index 000000000..122f596f9 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/init/ModEntities.java @@ -0,0 +1,115 @@ +package com.atsuishio.superbwarfare.init; + +import com.atsuishio.superbwarfare.ModUtils; +import com.atsuishio.superbwarfare.entity.projectile.LaserEntity; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.MobCategory; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.event.entity.EntityAttributeCreationEvent; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredRegister; + +@EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD) +public class ModEntities { + + public static final DeferredRegister> REGISTRY = DeferredRegister.create(BuiltInRegistries.ENTITY_TYPE, ModUtils.MODID); + + // // Living Entities +// public static final DeferredHolder, EntityType> TARGET = register("target", +// EntityType.Builder.of(TargetEntity::new, MobCategory.CREATURE).setTrackingRange(64).setUpdateInterval(3).setCustomClientFactory(TargetEntity::new).fireImmune().sized(0.875f, 2f)); +// public static final DeferredHolder, EntityType> SENPAI = register("senpai", +// EntityType.Builder.of(SenpaiEntity::new, MobCategory.MONSTER).setTrackingRange(64).setUpdateInterval(3).setCustomClientFactory(SenpaiEntity::new) +// .sized(0.6f, 2f)); +// +// // Misc Entities +// public static final DeferredHolder, EntityType> MORTAR = register("mortar", +// EntityType.Builder.of(MortarEntity::new, MobCategory.MISC).setTrackingRange(64).setUpdateInterval(3).setCustomClientFactory(MortarEntity::new).fireImmune().sized(0.8f, 1.4f)); + public static final DeferredHolder, EntityType> LASER = register("laser", + EntityType.Builder.of(LaserEntity::new, MobCategory.MISC).sized(0.1f, 0.1f).fireImmune().setUpdateInterval(1)); +// public static final DeferredHolder, EntityType> FLARE_DECOY = register("flare_decoy", +// EntityType.Builder.of(FlareDecoyEntity::new, MobCategory.MISC).setTrackingRange(64).setUpdateInterval(1).setCustomClientFactory(FlareDecoyEntity::new).sized(0.5f, 0.5f)); +// public static final DeferredHolder, EntityType> CLAYMORE = register("claymore", +// EntityType.Builder.of(ClaymoreEntity::new, MobCategory.MISC).setTrackingRange(64).setUpdateInterval(1).sized(0.5f, 0.5f)); +// public static final DeferredHolder, EntityType> C_4 = register("c4", +// EntityType.Builder.of(C4Entity::new, MobCategory.MISC).setTrackingRange(64).setUpdateInterval(1).sized(0.5f, 0.5f)); +// +// // Projectiles +// public static final DeferredHolder, EntityType> TASER_BULLET = register("taser_bullet", +// EntityType.Builder.of(TaserBulletEntity::new, MobCategory.MISC).setCustomClientFactory(TaserBulletEntity::new).setTrackingRange(64) +// .setUpdateInterval(1).sized(0.5f, 0.5f)); +// +// // Fast Projectiles +// public static final DeferredHolder, EntityType> SMALL_CANNON_SHELL = register("small_cannon_shell", +// EntityType.Builder.of(SmallCannonShellEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).setTrackingRange(64).setUpdateInterval(1).setCustomClientFactory(SmallCannonShellEntity::new).sized(0.5f, 0.5f)); +// public static final DeferredHolder, EntityType> RPG_ROCKET = register("rpg_rocket", +// EntityType.Builder.of(RpgRocketEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).setTrackingRange(64).setUpdateInterval(1).setCustomClientFactory(RpgRocketEntity::new).sized(0.5f, 0.5f)); +// public static final DeferredHolder, EntityType> MORTAR_SHELL = register("mortar_shell", +// EntityType.Builder.of(MortarShellEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).setTrackingRange(64).setUpdateInterval(1).setCustomClientFactory(MortarShellEntity::new).sized(0.5f, 0.5f)); +// public static final DeferredHolder, EntityType> PROJECTILE = register("projectile", +// EntityType.Builder.of(ProjectileEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).setCustomClientFactory(ProjectileEntity::new).setTrackingRange(64).noSave().noSummon().sized(0.25f, 0.25f)); +// public static final DeferredHolder, EntityType> CANNON_SHELL = register("cannon_shell", +// EntityType.Builder.of(CannonShellEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).setTrackingRange(64).setUpdateInterval(1).setCustomClientFactory(CannonShellEntity::new).sized(0.5f, 0.5f)); +// public static final DeferredHolder, EntityType> GUN_GRENADE = register("gun_grenade", +// EntityType.Builder.of(GunGrenadeEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).setTrackingRange(64).setUpdateInterval(1).setCustomClientFactory(GunGrenadeEntity::new).sized(0.5f, 0.5f)); +// public static final DeferredHolder, EntityType> MELON_BOMB = register("melon_bomb", +// EntityType.Builder.of(MelonBombEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).setTrackingRange(64).setUpdateInterval(1).setCustomClientFactory(MelonBombEntity::new).sized(1f, 1f)); +// public static final DeferredHolder, EntityType> HAND_GRENADE = register("hand_grenade", +// EntityType.Builder.of(HandGrenadeEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).setTrackingRange(64).setUpdateInterval(1).setCustomClientFactory(HandGrenadeEntity::new).sized(0.3f, 0.3f)); +// public static final DeferredHolder, EntityType> RGO_GRENADE = register("rgo_grenade", +// EntityType.Builder.of(RgoGrenadeEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).setTrackingRange(64).setUpdateInterval(1).setCustomClientFactory(RgoGrenadeEntity::new).sized(0.3f, 0.3f)); +// public static final DeferredHolder, EntityType> JAVELIN_MISSILE = register("javelin_missile", +// EntityType.Builder.of(JavelinMissileEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).setTrackingRange(64).setUpdateInterval(1).setCustomClientFactory(JavelinMissileEntity::new).sized(0.5f, 0.5f)); +// public static final DeferredHolder, EntityType> HELI_ROCKET = register("heli_rocket", +// EntityType.Builder.of(HeliRocketEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).setTrackingRange(64).setUpdateInterval(1).setCustomClientFactory(HeliRocketEntity::new).sized(0.5f, 0.5f)); +// public static final DeferredHolder, EntityType> WG_MISSILE = register("wg_missile", +// EntityType.Builder.of(WgMissileEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).setTrackingRange(64).setUpdateInterval(3).setCustomClientFactory(WgMissileEntity::new).fireImmune().sized(0.5f, 0.5f)); +// +// // Vehicles +// public static final DeferredHolder, EntityType> MK_42 = register("mk_42", +// EntityType.Builder.of(Mk42Entity::new, MobCategory.MISC).setTrackingRange(64).setUpdateInterval(3).setCustomClientFactory(Mk42Entity::new).fireImmune().sized(3.4f, 3.5f)); +// public static final DeferredHolder, EntityType> MLE_1934 = register("mle_1934", +// EntityType.Builder.of(Mle1934Entity::new, MobCategory.MISC).setTrackingRange(64).setUpdateInterval(3).setCustomClientFactory(Mle1934Entity::new).fireImmune().sized(4.5f, 2.8f)); +// public static final DeferredHolder, EntityType> ANNIHILATOR = register("annihilator", +// EntityType.Builder.of(AnnihilatorEntity::new, MobCategory.MISC).setTrackingRange(64).setUpdateInterval(3).setCustomClientFactory(AnnihilatorEntity::new).fireImmune().sized(13f, 4.2f)); +// +// public static final DeferredHolder, EntityType> SPEEDBOAT = register("speedboat", +// EntityType.Builder.of(SpeedboatEntity::new, MobCategory.MISC).setTrackingRange(64).setUpdateInterval(3).setCustomClientFactory(SpeedboatEntity::new).fireImmune().sized(3.0f, 2.0f)); +// public static final DeferredHolder, EntityType> WHEEL_CHAIR = register("wheel_chair", +// EntityType.Builder.of(WheelChairEntity::new, MobCategory.MISC).setTrackingRange(64).setUpdateInterval(3).setCustomClientFactory(WheelChairEntity::new).fireImmune().sized(1.0f, 1.0f)); +// public static final DeferredHolder, EntityType> AH_6 = register("ah_6", +// EntityType.Builder.of(Ah6Entity::new, MobCategory.MISC).setTrackingRange(64).setUpdateInterval(3).setCustomClientFactory(Ah6Entity::new).fireImmune().sized(2.8f, 2.9f)); +// public static final DeferredHolder, EntityType> LAV_150 = register("lav_150", +// EntityType.Builder.of(Lav150Entity::new, MobCategory.MISC).setTrackingRange(64).setUpdateInterval(3).setCustomClientFactory(Lav150Entity::new).fireImmune().sized(2.8f, 3.1f)); +// public static final DeferredHolder, EntityType> TOM_6 = register("tom_6", +// EntityType.Builder.of(Tom6Entity::new, MobCategory.MISC).setTrackingRange(64).setUpdateInterval(3).setCustomClientFactory(Tom6Entity::new).fireImmune().sized(1.05f, 1.0f)); +// public static final DeferredHolder, EntityType> BMP_2 = register("bmp_2", +// EntityType.Builder.of(Bmp2Entity::new, MobCategory.MISC).setTrackingRange(64).setUpdateInterval(3).setCustomClientFactory(Bmp2Entity::new).fireImmune().sized(4f, 3f)); +// public static final DeferredHolder, EntityType> YX_100 = register("yx_100", +// EntityType.Builder.of(Yx100Entity::new, MobCategory.MISC).setTrackingRange(64).setUpdateInterval(3).setCustomClientFactory(Yx100Entity::new).fireImmune().sized(5.5f, 3.25f)); +// +// public static final DeferredHolder, EntityType> DRONE = register("drone", +// EntityType.Builder.of(DroneEntity::new, MobCategory.MISC).setTrackingRange(64).setUpdateInterval(3).setCustomClientFactory(DroneEntity::new).sized(0.6f, 0.2f)); +// public static final DeferredHolder, EntityType> LASER_TOWER = register("laser_tower", +// EntityType.Builder.of(LaserTowerEntity::new, MobCategory.MISC).setTrackingRange(64).setUpdateInterval(3).setCustomClientFactory(LaserTowerEntity::new).fireImmune().sized(0.9f, 1.65f)); + + private static DeferredHolder, EntityType> register(String name, EntityType.Builder entityTypeBuilder) { + return REGISTRY.register(name, () -> entityTypeBuilder.build(name)); + } + +// @SubscribeEvent +// public static void onRegisterSpawnPlacement(SpawnPlacementRegisterEvent event) { +// event.register(ModEntities.SENPAI.get(), SpawnPlacements.Type.ON_GROUND, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, +// (entityType, world, reason, pos, random) -> (world.getDifficulty() != Difficulty.PEACEFUL && SpawnConfig.SPAWN_SENPAI.get() +// && Monster.isDarkEnoughToSpawn(world, pos, random) && Mob.checkMobSpawnRules(entityType, world, reason, pos, random)), +// SpawnPlacementRegisterEvent.Operation.OR); +// } + + @SubscribeEvent + public static void registerAttributes(EntityAttributeCreationEvent event) { +// event.put(TARGET.get(), TargetEntity.createAttributes().build()); +// event.put(SENPAI.get(), SenpaiEntity.createAttributes().build()); + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/init/ModItems.java b/src/main/java/com/atsuishio/superbwarfare/init/ModItems.java index 3222a5482..c51aa84b0 100644 --- a/src/main/java/com/atsuishio/superbwarfare/init/ModItems.java +++ b/src/main/java/com/atsuishio/superbwarfare/init/ModItems.java @@ -1,9 +1,7 @@ package com.atsuishio.superbwarfare.init; import com.atsuishio.superbwarfare.ModUtils; -import com.atsuishio.superbwarfare.item.FiringParameters; -import com.atsuishio.superbwarfare.item.PerkItem; -import com.atsuishio.superbwarfare.item.ShortcutPack; +import com.atsuishio.superbwarfare.item.*; import com.atsuishio.superbwarfare.item.common.BlueprintItem; import com.atsuishio.superbwarfare.item.common.CannonShellItem; import com.atsuishio.superbwarfare.item.common.MaterialPack; @@ -93,7 +91,7 @@ public class ModItems { public static final DeferredHolder SMALL_SHELL = AMMO.register("small_shell", () -> new Item(new Item.Properties())); public static final DeferredHolder ROCKET_70 = AMMO.register("rocket_70", () -> new Item(new Item.Properties())); public static final DeferredHolder WIRE_GUIDE_MISSILE = AMMO.register("wire_guide_missile", () -> new Item(new Item.Properties())); -// public static final DeferredHolder BEAM_TEST = AMMO.register("beam_test", BeamTest::new); + public static final DeferredHolder BEAM_TEST = AMMO.register("beam_test", BeamTest::new); /** * items @@ -117,7 +115,7 @@ public class ModItems { // public static final DeferredHolder TARGET_DEPLOYER = ITEMS.register("target_deployer", TargetDeployer::new); // public static final DeferredHolder KNIFE = ITEMS.register("knife", Knife::new); // public static final DeferredHolder HAMMER = ITEMS.register("hammer", Hammer::new); -// public static final DeferredHolder CROWBAR = ITEMS.register("crowbar", Crowbar::new); + public static final DeferredHolder CROWBAR = ITEMS.register("crowbar", Crowbar::new); // public static final DeferredHolder DEFUSER = ITEMS.register("defuser", Defuser::new); // public static final DeferredHolder ARMOR_PLATE = ITEMS.register("armor_plate", ArmorPlate::new); // diff --git a/src/main/java/com/atsuishio/superbwarfare/init/ModMenuTypes.java b/src/main/java/com/atsuishio/superbwarfare/init/ModMenuTypes.java new file mode 100644 index 000000000..b1357b023 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/init/ModMenuTypes.java @@ -0,0 +1,31 @@ +package com.atsuishio.superbwarfare.init; + +import com.atsuishio.superbwarfare.ModUtils; +import com.atsuishio.superbwarfare.menu.ChargingStationMenu; +import com.atsuishio.superbwarfare.menu.FuMO25Menu; +import com.atsuishio.superbwarfare.menu.ReforgingTableMenu; +import com.atsuishio.superbwarfare.menu.VehicleMenu; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.MenuType; +import net.neoforged.neoforge.common.extensions.IMenuTypeExtension; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredRegister; + +public class ModMenuTypes { + + public static final DeferredRegister> REGISTRY = DeferredRegister.create(BuiltInRegistries.MENU, ModUtils.MODID); + + public static final DeferredHolder, MenuType> REFORGING_TABLE_MENU = + REGISTRY.register("reforging_table_menu", + () -> IMenuTypeExtension.create((windowId, inv, data) -> new ReforgingTableMenu(windowId, inv))); + public static final DeferredHolder, MenuType> CHARGING_STATION_MENU = + REGISTRY.register("charging_station_menu", + () -> IMenuTypeExtension.create((windowId, inv, data) -> new ChargingStationMenu(windowId, inv))); + public static final DeferredHolder, MenuType> VEHICLE_MENU = + REGISTRY.register("vehicle_menu", + () -> IMenuTypeExtension.create((windowId, inv, data) -> new VehicleMenu(windowId, inv))); + public static final DeferredHolder, MenuType> FUMO_25_MENU = + REGISTRY.register("fumo_25_menu", + () -> IMenuTypeExtension.create((windowId, inv, data) -> new FuMO25Menu(windowId, inv))); +} diff --git a/src/main/java/com/atsuishio/superbwarfare/item/BeamTest.java b/src/main/java/com/atsuishio/superbwarfare/item/BeamTest.java new file mode 100644 index 000000000..a42fddbba --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/item/BeamTest.java @@ -0,0 +1,153 @@ +package com.atsuishio.superbwarfare.item; + +import com.atsuishio.superbwarfare.capability.LaserHandler; +import com.atsuishio.superbwarfare.capability.ModCapabilities; +import com.atsuishio.superbwarfare.entity.projectile.LaserEntity; +import com.atsuishio.superbwarfare.init.ModSounds; +import com.atsuishio.superbwarfare.tools.TraceTool; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundStopSoundPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +public class BeamTest extends Item { + + public BeamTest() { + super(new Properties()); + } + + @Override + public @NotNull InteractionResultHolder use(@NotNull Level level, Player player, @NotNull InteractionHand hand) { + + if (player.level().isClientSide) { + player.playSound(ModSounds.CHARGE_RIFLE_FIRE_1P.get(), 1, 1); + } else { + player.playSound(ModSounds.CHARGE_RIFLE_FIRE_3P.get(), 2, 1); + } + + var capability = player.getCapability(ModCapabilities.LASER_CAPABILITY); + if (capability != null) { + player.startUsingItem(hand); + if (!level.isClientSide) { + double px = player.getX(); + double py = player.getY() + player.getBbHeight() * 0.6F; + double pz = player.getZ(); + float yHeadRotAngle = (float) Math.toRadians(player.yHeadRot + 90); + float xHeadRotAngle = (float) (float) -Math.toRadians(player.getXRot()); + LaserEntity laserEntity = new LaserEntity(player.level(), player, px, py, pz, yHeadRotAngle, xHeadRotAngle, 6000); + capability.init(new LaserHandler(player, laserEntity)); + capability.start(); + } + } + + return InteractionResultHolder.consume(player.getItemInHand(hand)); + } + + @Override + @ParametersAreNonnullByDefault + public void releaseUsing(ItemStack stack, Level level, LivingEntity livingEntity, int timeCharged) { + if (livingEntity instanceof Player player) { + var cap = player.getCapability(ModCapabilities.LASER_CAPABILITY); + if (cap != null) { + cap.stop(); + } + } + if (livingEntity instanceof ServerPlayer serverPlayer && stack.getItem() instanceof BeamTest beamTest) { + stopGunChargeSound(serverPlayer, beamTest); + } + + + super.releaseUsing(stack, level, livingEntity, timeCharged); + } + + private static void stopGunChargeSound(ServerPlayer player, BeamTest beamTest) { + beamTest.getChargeSound().forEach(sound -> { + var clientboundstopsoundpacket = new ClientboundStopSoundPacket(sound.getLocation(), SoundSource.PLAYERS); + final Vec3 center = new Vec3(player.getX(), player.getY(), player.getZ()); + for (ServerPlayer player1 : player.level().getEntitiesOfClass(ServerPlayer.class, new AABB(center, center).inflate(48), e -> true).stream().sorted(Comparator.comparingDouble(e -> e.distanceToSqr(center))).toList()) { + player1.connection.send(clientboundstopsoundpacket); + } + }); + } + + public Set getChargeSound() { + return Set.of(ModSounds.CHARGE_RIFLE_FIRE_1P.get(), ModSounds.CHARGE_RIFLE_FIRE_3P.get()); + } + + @Override + public ItemStack finishUsingItem(ItemStack pStack, Level pLevel, LivingEntity pLivingEntity) { + if (pLivingEntity instanceof Player player) { + var cap = player.getCapability(ModCapabilities.LASER_CAPABILITY); + if (cap != null) { + cap.stop(); + } + player.getCooldowns().addCooldown(pStack.getItem(), 20); + + if (player.level().isClientSide()) { + beamShoot(player); + player.playSound(ModSounds.CHARGE_RIFLE_FIRE_BOOM_1P.get(), 1, 1); + } + if (!player.level().isClientSide) { + player.playSound(ModSounds.CHARGE_RIFLE_FIRE_BOOM_3P.get(), 4, 1); + } + if (player instanceof ServerPlayer serverPlayer) { + // TODO network +// ModUtils.PACKET_HANDLER.send(PacketDistributor.PLAYER.with(() -> serverPlayer), new ShakeClientMessage(10, 10, 30, serverPlayer.getX(), serverPlayer.getEyeY(), serverPlayer.getZ())); + } + } + return super.finishUsingItem(pStack, pLevel, pLivingEntity); + } + + public static void beamShoot(Player player) { + Entity lookingEntity = TraceTool.laserfindLookingEntity(player, 512); + + if (lookingEntity == null) { + return; + } + + boolean canAttack = lookingEntity != player && !(lookingEntity instanceof Player player_ && (player_.isCreative() || player_.isSpectator())) + && (!player.isAlliedTo(lookingEntity) || lookingEntity.getTeam() == null || lookingEntity.getTeam().getName().equals("TDM")); + + if (canAttack) { +// ModUtils.PACKET_HANDLER.sendToServer(new LaserShootMessage(45, lookingEntity.getUUID(), TraceTool.laserHeadshot)); + } + } + + @Override + @ParametersAreNonnullByDefault + public int getUseDuration(ItemStack stack, LivingEntity entity) { + return 11; + } + + @Override + @ParametersAreNonnullByDefault + public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged) { + return slotChanged; + } + + @Override + @ParametersAreNonnullByDefault + public void appendHoverText(ItemStack stack, TooltipContext context, List tooltipComponents, TooltipFlag tooltipFlag) { + // TODO developing + // TooltipTool.addDevelopingText(pTooltipComponents); + } + +} diff --git a/src/main/java/com/atsuishio/superbwarfare/item/Crowbar.java b/src/main/java/com/atsuishio/superbwarfare/item/Crowbar.java new file mode 100644 index 000000000..56b6b8a9b --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/item/Crowbar.java @@ -0,0 +1,88 @@ +package com.atsuishio.superbwarfare.item; + +import com.atsuishio.superbwarfare.init.ModBlocks; +import com.atsuishio.superbwarfare.init.ModItems; +import net.minecraft.core.BlockPos; +import net.minecraft.tags.BlockTags; +import net.minecraft.tags.TagKey; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.SwordItem; +import net.minecraft.world.item.Tier; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.neoforged.neoforge.items.ItemHandlerHelper; +import org.jetbrains.annotations.NotNull; + +public class Crowbar extends SwordItem { + public Crowbar() { + super(new Tier() { + public int getUses() { + return 400; + } + + public float getSpeed() { + return 4f; + } + + public float getAttackDamageBonus() { + return 3.5f; + } + + @Override + public @NotNull TagKey getIncorrectBlocksForDrops() { + return BlockTags.INCORRECT_FOR_IRON_TOOL; + } + + public int getLevel() { + return 1; + } + + public int getEnchantmentValue() { + return 9; + } + + public @NotNull Ingredient getRepairIngredient() { + return Ingredient.of(new ItemStack(Items.IRON_INGOT)); + } + }, new Properties()); + } + + @Override + public boolean hasCraftingRemainingItem(@NotNull ItemStack stack) { + return true; + } + + @Override + public @NotNull ItemStack getCraftingRemainingItem(ItemStack itemstack) { + ItemStack retval = new ItemStack(this); + retval.setDamageValue(itemstack.getDamageValue() + 1); + if (retval.getDamageValue() >= retval.getMaxDamage()) { + return ItemStack.EMPTY; + } + return retval; + } + + @Override + public boolean isRepairable(@NotNull ItemStack itemstack) { + return true; + } + + @Override + public @NotNull InteractionResult useOn(@NotNull UseOnContext context) { + super.useOn(context); + if ((context.getLevel().getBlockState(BlockPos.containing(context.getClickedPos().getX(), context.getClickedPos().getY(), context.getClickedPos().getZ()))).getBlock() == ModBlocks.JUMP_PAD.get()) { + context.getLevel().setBlock(BlockPos.containing(context.getClickedPos().getX(), context.getClickedPos().getY(), context.getClickedPos().getZ()), Blocks.AIR.defaultBlockState(), 3); + + if (context.getPlayer() != null) { + ItemHandlerHelper.giveItemToPlayer(context.getPlayer(), new ItemStack(ModItems.JUMP_PAD.get())); + } + } + return InteractionResult.SUCCESS; + } + + +} diff --git a/src/main/java/com/atsuishio/superbwarfare/menu/ChargingStationMenu.java b/src/main/java/com/atsuishio/superbwarfare/menu/ChargingStationMenu.java new file mode 100644 index 000000000..e8a4033c4 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/menu/ChargingStationMenu.java @@ -0,0 +1,138 @@ +package com.atsuishio.superbwarfare.menu; + +import com.atsuishio.superbwarfare.block.entity.ChargingStationBlockEntity; +import com.atsuishio.superbwarfare.init.ModMenuTypes; +import com.atsuishio.superbwarfare.network.dataslot.ContainerEnergyData; +import com.atsuishio.superbwarfare.network.dataslot.SimpleEnergyData; +import net.minecraft.world.Container; +import net.minecraft.world.SimpleContainer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.Level; +import net.neoforged.neoforge.capabilities.Capabilities; +import org.jetbrains.annotations.NotNull; + +public class ChargingStationMenu extends EnergyMenu { + + private final Container container; + private final ContainerEnergyData containerData; + protected final Level level; + + public static final int X_OFFSET = 0; + public static final int Y_OFFSET = 0; + + public ChargingStationMenu(int id, Inventory inventory) { + this(id, inventory, new SimpleContainer(2), new SimpleEnergyData(ChargingStationBlockEntity.MAX_DATA_COUNT)); + } + + public ChargingStationMenu(int id, Inventory inventory, Container container, ContainerEnergyData containerData) { + super(ModMenuTypes.CHARGING_STATION_MENU.get(), id, containerData); + + checkContainerSize(container, 2); + + this.container = container; + this.containerData = containerData; + this.level = inventory.player.level(); + + this.addSlot(new Slot(container, 0, 44, 54)); + this.addSlot(new ChargingSlot(container, 1, 116, 54)); + + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + this.addSlot(new Slot(inventory, j + i * 9 + 9, 8 + j * 18 + X_OFFSET, 84 + i * 18 + Y_OFFSET)); + } + } + + for (int k = 0; k < 9; ++k) { + this.addSlot(new Slot(inventory, k, 8 + k * 18 + X_OFFSET, 142 + Y_OFFSET)); + } + } + + @Override + public @NotNull ItemStack quickMoveStack(@NotNull Player pPlayer, int pIndex) { + ItemStack itemstack = ItemStack.EMPTY; + Slot slot = this.slots.get(pIndex); + if (slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + itemstack = itemstack1.copy(); + if (pIndex == 1) { + if (!this.moveItemStackTo(itemstack1, 2, 38, true)) { + return ItemStack.EMPTY; + } + } else if (pIndex != 0) { + var itemCapability = itemstack1.getCapability(Capabilities.EnergyStorage.ITEM); + if (itemCapability != null) { + if (!this.moveItemStackTo(itemstack1, 1, 2, true)) { + return ItemStack.EMPTY; + } + } else if (itemstack1.getBurnTime(RecipeType.SMELTING) > 0 || itemstack1.getFoodProperties(null) != null) { + if (!this.moveItemStackTo(itemstack1, 0, 1, false)) { + return ItemStack.EMPTY; + } + } else if (pIndex >= 2 && pIndex < 29) { + if (!this.moveItemStackTo(itemstack1, 29, 38, false)) { + return ItemStack.EMPTY; + } + } else if (pIndex >= 29 && pIndex < 38 && !this.moveItemStackTo(itemstack1, 2, 29, false)) { + return ItemStack.EMPTY; + } + } else if (!this.moveItemStackTo(itemstack1, 2, 38, false)) { + return ItemStack.EMPTY; + } + + if (itemstack1.isEmpty()) { + slot.setByPlayer(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + if (itemstack1.getCount() == itemstack.getCount()) { + return ItemStack.EMPTY; + } + + slot.onTake(pPlayer, itemstack1); + } + + return itemstack; + } + + @Override + public boolean stillValid(@NotNull Player pPlayer) { + return this.container.stillValid(pPlayer); + } + + public long getFuelTick() { + return this.containerData.get(0); + } + + public long getMaxFuelTick() { + return this.containerData.get(1); + } + + public long getEnergy() { + return this.containerData.get(2); + } + + public boolean showRange() { + return this.containerData.get(3) == 1; + } + + public void setShowRange(boolean showRange) { + this.containerData.set(3, showRange ? 1 : 0); + } + + static class ChargingSlot extends Slot { + + public ChargingSlot(Container pContainer, int pSlot, int pX, int pY) { + super(pContainer, pSlot, pX, pY); + } + + @Override + public boolean mayPlace(@NotNull ItemStack pStack) { + return super.mayPlace(pStack); + } + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/menu/EnergyMenu.java b/src/main/java/com/atsuishio/superbwarfare/menu/EnergyMenu.java new file mode 100644 index 000000000..f1d560745 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/menu/EnergyMenu.java @@ -0,0 +1,77 @@ +package com.atsuishio.superbwarfare.menu; + +import com.atsuishio.superbwarfare.network.dataslot.ContainerEnergyData; +import com.atsuishio.superbwarfare.network.dataslot.ContainerEnergyDataSlot; +import com.google.common.collect.Lists; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.MenuType; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +//@EventBusSubscriber(bus = EventBusSubscriber.Bus.GAME) +public abstract class EnergyMenu extends AbstractContainerMenu { + + private final List containerEnergyDataSlots = Lists.newArrayList(); + private final List usingPlayers = new ArrayList<>(); + + public EnergyMenu(@Nullable MenuType pMenuType, int pContainerId) { + super(pMenuType, pContainerId); + } + + public EnergyMenu(@Nullable MenuType pMenuType, int id, ContainerEnergyData containerData) { + super(pMenuType, id); + + for (int i = 0; i < containerData.getCount(); ++i) { + this.containerEnergyDataSlots.add(ContainerEnergyDataSlot.forContainer(containerData, i)); + } + } + + @Override + public void broadcastChanges() { + // TODO network +// List pairs = new ArrayList<>(); +// for (int i = 0; i < this.containerEnergyDataSlots.size(); ++i) { +// ContainerEnergyDataSlot dataSlot = this.containerEnergyDataSlots.get(i); +// if (dataSlot.checkAndClearUpdateFlag()) +// pairs.add(new ContainerDataMessage.Pair(i, dataSlot.get())); +// } +// +// if (!pairs.isEmpty()) { +// PacketDistributor.PacketTarget target = PacketDistributor.NMLIST.with(this.usingPlayers.stream().map(serverPlayer -> serverPlayer.connection.connection)::toList); +// ModUtils.PACKET_HANDLER.send(target, new ContainerDataMessage(this.containerId, pairs)); +// } + + super.broadcastChanges(); + } + + public void setData(int id, int data) { + this.containerEnergyDataSlots.get(id).set(data); + } + + public void setData(int id, long data) { + this.containerEnergyDataSlots.get(id).set(data); + } + +// @SubscribeEvent +// public static void onContainerOpened(PlayerContainerEvent.Open event) { +// if (event.getContainer() instanceof EnergyMenu menu && event.getEntity() instanceof ServerPlayer serverPlayer) { +// menu.usingPlayers.add(serverPlayer); +// +//// List toSync = new ArrayList<>(); +//// for (int i = 0; i < menu.containerEnergyDataSlots.size(); ++i) { +//// toSync.add(new ContainerDataMessage.Pair(i, menu.containerEnergyDataSlots.get(i).get())); +//// } +//// ModUtils.PACKET_HANDLER.send(PacketDistributor.PLAYER.with(() -> serverPlayer), new ContainerDataMessage(menu.containerId, toSync)); +// } +// } +// +// @SubscribeEvent +// public static void onContainerClosed(PlayerContainerEvent.Close event) { +// if (event.getContainer() instanceof EnergyMenu menu && event.getEntity() instanceof ServerPlayer serverPlayer) { +// menu.usingPlayers.remove(serverPlayer); +// } +// } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/menu/FuMO25Menu.java b/src/main/java/com/atsuishio/superbwarfare/menu/FuMO25Menu.java new file mode 100644 index 000000000..8f8103b7e --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/menu/FuMO25Menu.java @@ -0,0 +1,222 @@ +package com.atsuishio.superbwarfare.menu; + +import com.atsuishio.superbwarfare.block.entity.FuMO25BlockEntity; +import com.atsuishio.superbwarfare.component.ModDataComponents; +import com.atsuishio.superbwarfare.init.ModBlocks; +import com.atsuishio.superbwarfare.init.ModItems; +import com.atsuishio.superbwarfare.init.ModMenuTypes; +import com.atsuishio.superbwarfare.network.dataslot.ContainerEnergyData; +import com.atsuishio.superbwarfare.network.dataslot.SimpleEnergyData; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.SimpleContainer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.event.entity.player.PlayerContainerEvent; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; +import java.util.Optional; + +@EventBusSubscriber(bus = EventBusSubscriber.Bus.GAME) +public class FuMO25Menu extends EnergyMenu { + + protected final Container container; + protected final ContainerLevelAccess access; + protected final ContainerEnergyData containerData; + + private int posX = Integer.MIN_VALUE; + private int posY = Integer.MIN_VALUE; + private int posZ = Integer.MIN_VALUE; + + public static final int X_OFFSET = 164; + public static final int Y_OFFSET = 0; + + public FuMO25Menu(int pContainerId, Inventory pPlayerInventory) { + this(pContainerId, pPlayerInventory, new SimpleContainer(1), ContainerLevelAccess.NULL, new SimpleEnergyData(FuMO25BlockEntity.MAX_DATA_COUNT)); + } + + public FuMO25Menu(int pContainerId, Inventory pPlayerInventory, ContainerLevelAccess access, ContainerEnergyData containerData) { + this(pContainerId, pPlayerInventory, new SimpleContainer(1), access, containerData); + } + + public FuMO25Menu(int pContainerId, Inventory inventory, Container container, ContainerLevelAccess access, ContainerEnergyData containerData) { + super(ModMenuTypes.FUMO_25_MENU.get(), pContainerId, containerData); + + checkContainerSize(container, 1); + + this.container = container; + this.access = access; + this.containerData = containerData; + + this.addSlot(new ParaSlot(container, 0, 278, 60)); + + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + this.addSlot(new Slot(inventory, j + i * 9 + 9, 8 + j * 18 + X_OFFSET, 84 + i * 18 + Y_OFFSET)); + } + } + + for (int k = 0; k < 9; ++k) { + this.addSlot(new Slot(inventory, k, 8 + k * 18 + X_OFFSET, 142 + Y_OFFSET)); + } + } + + public void setPos(int x, int y, int z) { + this.posX = x; + this.posY = y; + this.posZ = z; + } + + public void resetPos() { + this.posX = Integer.MIN_VALUE; + this.posY = Integer.MIN_VALUE; + this.posZ = Integer.MIN_VALUE; + } + + public void setPosToParameters() { + if (this.posX != Integer.MIN_VALUE && this.posY != Integer.MIN_VALUE) { + ItemStack stack = this.container.getItem(0); + if (stack.isEmpty()) return; + + stack.set(ModDataComponents.BLOCK_POS, new BlockPos(this.posX, this.posY, this.posZ)); + + this.resetPos(); + this.container.setChanged(); + } + } + + public void setTargetToLaserTower() { + + } + + @Nullable + public BlockPos getCurrentPos() { + if (this.posX != Integer.MIN_VALUE && this.posY != Integer.MIN_VALUE && this.posZ != Integer.MIN_VALUE) { + return new BlockPos(this.posX, this.posY, this.posZ); + } + return null; + } + + public Optional getSelfPos() { + return this.access.evaluate((level, pos) -> pos); + } + + @Override + public @NotNull ItemStack quickMoveStack(@NotNull Player pPlayer, int pIndex) { + ItemStack itemstack = ItemStack.EMPTY; + Slot slot = this.slots.get(pIndex); + if (slot.hasItem()) { + ItemStack stack = slot.getItem(); + itemstack = stack.copy(); + if (pIndex != 0) { + if (!this.moveItemStackTo(stack, 0, 1, false)) { + return ItemStack.EMPTY; + } else if (pIndex >= 1 && pIndex < 28) { + if (!this.moveItemStackTo(stack, 28, 37, false)) { + return ItemStack.EMPTY; + } + } else if (pIndex >= 28 && pIndex < 37 && !this.moveItemStackTo(stack, 1, 28, false)) { + return ItemStack.EMPTY; + } + } else if (!this.moveItemStackTo(stack, 1, 37, false)) { + return ItemStack.EMPTY; + } + + if (stack.isEmpty()) { + slot.setByPlayer(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + if (stack.getCount() == itemstack.getCount()) { + return ItemStack.EMPTY; + } + + slot.onTake(pPlayer, stack); + } + + return itemstack; + } + + @Override + public boolean stillValid(@NotNull Player pPlayer) { + return this.access.evaluate((level, pos) -> level.getBlockState(pos).is(ModBlocks.FUMO_25.get()) + && pPlayer.distanceToSqr((double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D) <= 64.0D, true); + } + + @Override + public void removed(@NotNull Player pPlayer) { + this.access.execute((level, pos) -> { + ItemStack para = this.container.getItem(0); + if (!para.isEmpty()) { + pPlayer.getInventory().placeItemBackInInventory(para); + } + this.container.removeItemNoUpdate(0); + resetPos(); + }); + } + + public long getEnergy() { + return this.containerData.get(0); + } + + public long getFuncType() { + return this.containerData.get(1); + } + + public void setFuncTypeAndTime(byte type) { + this.containerData.set(1, type); + int tick = switch (type) { + case 1, 2 -> 1200; + case 3 -> 600; + default -> 0; + }; + this.containerData.set(2, tick); + } + + public long getTime() { + return this.containerData.get(2); + } + + public boolean isPowered() { + return this.containerData.get(3) == 1; + } + + static class ParaSlot extends Slot { + + public ParaSlot(Container pContainer, int pSlot, int pX, int pY) { + super(pContainer, pSlot, pX, pY); + } + + @Override + public boolean mayPlace(ItemStack pStack) { + return pStack.is(ModItems.FIRING_PARAMETERS.get()); + } + } + + @SubscribeEvent + public static void onContainerOpened(PlayerContainerEvent.Open event) { + if (event.getContainer() instanceof FuMO25Menu fuMO25Menu && event.getEntity() instanceof ServerPlayer serverPlayer) { + fuMO25Menu.getSelfPos().ifPresent(pos -> { + } + // TODO send packet +// ModUtils.PACKET_HANDLER.send(PacketDistributor.PLAYER.with(() -> serverPlayer), new RadarMenuOpenMessage(pos)) + ); + } + } + + @SubscribeEvent + public static void onContainerClosed(PlayerContainerEvent.Close event) { + if (event.getContainer() instanceof FuMO25Menu && event.getEntity() instanceof ServerPlayer serverPlayer) { + // TODO send packet +// ModUtils.PACKET_HANDLER.send(PacketDistributor.PLAYER.with(() -> serverPlayer), new RadarMenuCloseMessage(0)); + } + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/menu/ReforgingTableMenu.java b/src/main/java/com/atsuishio/superbwarfare/menu/ReforgingTableMenu.java new file mode 100644 index 000000000..cef6eb17b --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/menu/ReforgingTableMenu.java @@ -0,0 +1,509 @@ +package com.atsuishio.superbwarfare.menu; + +import com.atsuishio.superbwarfare.init.ModBlocks; +import com.atsuishio.superbwarfare.init.ModMenuTypes; +import com.atsuishio.superbwarfare.init.ModTags; +import com.atsuishio.superbwarfare.item.PerkItem; +import com.atsuishio.superbwarfare.perk.Perk; +import net.minecraft.util.Mth; +import net.minecraft.world.Container; +import net.minecraft.world.SimpleContainer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.DataSlot; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; + +public class ReforgingTableMenu extends AbstractContainerMenu { + + protected final Container container; + protected final ContainerLevelAccess access; + + public static final int INPUT_SLOT = 0; + public static final int AMMO_PERK_SLOT = 1; + public static final int FUNC_PERK_SLOT = 2; + public static final int DAMAGE_PERK_SLOT = 3; + public static final int RESULT_SLOT = 4; + + public static final int MAX_PERK_LEVEL = 20; + public static final int MAX_UPGRADE_POINT = 100; + + public final DataSlot ammoPerkLevel = DataSlot.standalone(); + public final DataSlot funcPerkLevel = DataSlot.standalone(); + public final DataSlot damagePerkLevel = DataSlot.standalone(); + public final DataSlot upgradePoint = DataSlot.standalone(); + + public static final int X_OFFSET = 0; + public static final int Y_OFFSET = 11; + + public ReforgingTableMenu(int pContainerId, Inventory pPlayerInventory) { + this(pContainerId, pPlayerInventory, new SimpleContainer(5), ContainerLevelAccess.NULL); + } + + public ReforgingTableMenu(int pContainerId, Inventory pPlayerInventory, ContainerLevelAccess access) { + this(pContainerId, pPlayerInventory, new SimpleContainer(5), access); + } + + public ReforgingTableMenu(int pContainerId, Inventory inventory, Container container, ContainerLevelAccess pContainerLevelAccess) { + super(ModMenuTypes.REFORGING_TABLE_MENU.get(), pContainerId); + + checkContainerSize(container, 5); + + this.container = container; + this.access = pContainerLevelAccess; + + this.ammoPerkLevel.set(0); + this.funcPerkLevel.set(0); + this.damagePerkLevel.set(0); + this.upgradePoint.set(0); + + this.addDataSlot(ammoPerkLevel); + this.addDataSlot(funcPerkLevel); + this.addDataSlot(damagePerkLevel); + this.addDataSlot(upgradePoint); + + this.addSlot(new InputSlot(container, INPUT_SLOT, 20, 22)); + this.addSlot(new PerkSlot(container, AMMO_PERK_SLOT, Perk.Type.AMMO, 80, 25)); + this.addSlot(new PerkSlot(container, FUNC_PERK_SLOT, Perk.Type.FUNCTIONAL, 80, 45)); + this.addSlot(new PerkSlot(container, DAMAGE_PERK_SLOT, Perk.Type.DAMAGE, 80, 65)); + this.addSlot(new ResultSlot(container, RESULT_SLOT, 142, 45)); + + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + this.addSlot(new Slot(inventory, j + i * 9 + 9, 8 + j * 18 + X_OFFSET, 84 + i * 18 + Y_OFFSET)); + } + } + + for (int k = 0; k < 9; ++k) { + this.addSlot(new Slot(inventory, k, 8 + k * 18 + X_OFFSET, 142 + Y_OFFSET)); + } + } + + @Override + public @NotNull ItemStack quickMoveStack(@NotNull Player pPlayer, int pIndex) { + ItemStack itemstack = ItemStack.EMPTY; + Slot slot = this.slots.get(pIndex); + if (slot.hasItem()) { + ItemStack stack = slot.getItem(); + itemstack = stack.copy(); + + if (pIndex == INPUT_SLOT) { + onTakeGun(stack); + if (!this.moveItemStackTo(stack, RESULT_SLOT + 1, RESULT_SLOT + 37, false)) { + return ItemStack.EMPTY; + } + } else if (pIndex >= AMMO_PERK_SLOT && pIndex <= DAMAGE_PERK_SLOT) { + onTakePerk(stack); + if (!this.moveItemStackTo(stack, RESULT_SLOT + 1, RESULT_SLOT + 37, false)) { + return ItemStack.EMPTY; + } + } else if (pIndex == RESULT_SLOT) { + if (!this.moveItemStackTo(stack, RESULT_SLOT, RESULT_SLOT + 36, false)) { + return ItemStack.EMPTY; + } + } else { + if (stack.is(ModTags.Items.GUN)) { + if (!this.moveItemStackTo(stack, INPUT_SLOT, INPUT_SLOT + 1, false)) { + return ItemStack.EMPTY; + } + } else if (stack.getItem() instanceof PerkItem perkItem) { + Perk.Type type = perkItem.getPerk().type; + if (type == Perk.Type.AMMO) { + if (!this.moveItemStackTo(stack, AMMO_PERK_SLOT, AMMO_PERK_SLOT + 1, false)) { + return ItemStack.EMPTY; + } + } else if (type == Perk.Type.FUNCTIONAL) { + if (!this.moveItemStackTo(stack, FUNC_PERK_SLOT, FUNC_PERK_SLOT + 1, false)) { + return ItemStack.EMPTY; + } + } else if (type == Perk.Type.DAMAGE) { + if (!this.moveItemStackTo(stack, DAMAGE_PERK_SLOT, DAMAGE_PERK_SLOT + 1, false)) { + return ItemStack.EMPTY; + } + } + } + } + + if (stack.isEmpty()) { + slot.setByPlayer(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + if (stack.getCount() == itemstack.getCount()) { + return ItemStack.EMPTY; + } + + slot.onTake(pPlayer, stack); + } + + return itemstack; + } + + @Override + public boolean stillValid(@NotNull Player pPlayer) { + return this.access.evaluate((level, pos) -> level.getBlockState(pos).is(ModBlocks.REFORGING_TABLE.get()) + && pPlayer.distanceToSqr((double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D) <= 64.0D, true); + } + + @Override + public void removed(@NotNull Player pPlayer) { + this.access.execute((level, pos) -> { + ItemStack gun = this.container.getItem(INPUT_SLOT); + ItemStack copy = gun.copy(); + + for (int i = 0; i < this.container.getContainerSize(); ++i) { + ItemStack itemstack = this.container.getItem(i); + + if (itemstack.getItem() instanceof PerkItem perkItem) { + // TODO PerkHelper +// if (!copy.isEmpty() && PerkHelper.getItemPerkLevel(perkItem.getPerk(), copy) > 0) { +// continue; +// } + } + + if (!itemstack.isEmpty()) { + pPlayer.getInventory().placeItemBackInInventory(itemstack); + } + + this.container.removeItemNoUpdate(i); + } + }); + } + + public void setPerkLevel(Perk.Type type, boolean upgrade, boolean isCreative) { + if (upgrade && this.upgradePoint.get() <= 0 && !isCreative) { + return; + } + + if (!upgrade && this.upgradePoint.get() >= MAX_UPGRADE_POINT && !isCreative) { + return; + } + + switch (type) { + case AMMO -> + this.ammoPerkLevel.set(upgrade ? Math.min(MAX_PERK_LEVEL, this.ammoPerkLevel.get() + 1) : Math.max(1, this.ammoPerkLevel.get() - 1)); + case FUNCTIONAL -> + this.funcPerkLevel.set(upgrade ? Math.min(MAX_PERK_LEVEL, this.funcPerkLevel.get() + 1) : Math.max(1, this.funcPerkLevel.get() - 1)); + case DAMAGE -> + this.damagePerkLevel.set(upgrade ? Math.min(MAX_PERK_LEVEL, this.damagePerkLevel.get() + 1) : Math.max(1, this.damagePerkLevel.get() - 1)); + } + + if (!isCreative) { + this.upgradePoint.set(Mth.clamp(this.upgradePoint.get() + (upgrade ? -1 : 1), 0, MAX_UPGRADE_POINT)); + } + } + + public void handleUpgradePoint(ItemStack stack) { + // TODO GunItem GunsTool +// if (!(stack.getItem() instanceof GunItem)) { +// return; +// } +// +// double oldPoint = GunsTool.getGunDoubleTag(stack, "UpgradePoint", 0); +// int point = (int) oldPoint; +// int newPoint = this.upgradePoint.get(); +// int delta = newPoint - point; +// +// if (delta != 0) { +// GunsTool.setGunDoubleTag(stack, "UpgradePoint", oldPoint + delta); +// } + } + + /** + * 根据输入槽的枪械和Perk槽中的物品与等级,生成重铸后的武器,并放入输出槽中 + */ + public void generateResult() { +// ItemStack gun = this.container.getItem(INPUT_SLOT); +// if (!(gun.getItem() instanceof GunItem gunItem)) { +// return; +// } +// +// ItemStack ammo = this.container.getItem(AMMO_PERK_SLOT); +// ItemStack func = this.container.getItem(FUNC_PERK_SLOT); +// ItemStack damage = this.container.getItem(DAMAGE_PERK_SLOT); +// if (ammo.isEmpty() && func.isEmpty() && damage.isEmpty()) { +// return; +// } +// +// ItemStack result = gun.copy(); +// +// if (!ammo.isEmpty() && ammo.getItem() instanceof PerkItem perkItem) { +// if (gunItem.canApplyPerk(perkItem.getPerk())) { +// PerkHelper.setPerk(result, perkItem.getPerk(), this.ammoPerkLevel.get()); +// this.container.setItem(AMMO_PERK_SLOT, ItemStack.EMPTY); +// } +// } +// +// if (!func.isEmpty() && func.getItem() instanceof PerkItem perkItem) { +// if (gunItem.canApplyPerk(perkItem.getPerk())) { +// PerkHelper.setPerk(result, perkItem.getPerk(), this.funcPerkLevel.get()); +// this.container.setItem(FUNC_PERK_SLOT, ItemStack.EMPTY); +// } +// } +// +// if (!damage.isEmpty() && damage.getItem() instanceof PerkItem perkItem) { +// if (gunItem.canApplyPerk(perkItem.getPerk())) { +// PerkHelper.setPerk(result, perkItem.getPerk(), this.damagePerkLevel.get()); +// this.container.setItem(DAMAGE_PERK_SLOT, ItemStack.EMPTY); +// } +// } + +// handleUpgradePoint(result); +// +// this.ammoPerkLevel.set(0); +// this.funcPerkLevel.set(0); +// this.damagePerkLevel.set(0); +// this.upgradePoint.set(0); +// +// this.container.setItem(INPUT_SLOT, ItemStack.EMPTY); +// this.container.setItem(RESULT_SLOT, result); +// this.container.setChanged(); + } + + /** + * 从Perk槽中取出对应的Perk物品时,根据其类型移除输入槽中枪械的Perk + * + * @param perk Perk物品 + */ + private void onTakePerk(ItemStack perk) { +// ItemStack gun = this.container.getItem(INPUT_SLOT); +// if (!(gun.getItem() instanceof GunItem)) { +// return; +// } +// +// if (perk.getItem() instanceof PerkItem perkItem) { +// switch (perkItem.getPerk().type) { +// case AMMO -> this.ammoPerkLevel.set(0); +// case FUNCTIONAL -> this.funcPerkLevel.set(0); +// case DAMAGE -> this.damagePerkLevel.set(0); +// } +// +// int level = PerkHelper.getItemPerkLevel(perkItem.getPerk(), gun); +// +// if (level <= 0) { +// this.upgradePoint.set((int) GunsTool.getGunDoubleTag(gun, "UpgradePoint", 0)); +// return; +// } +// +// ItemStack output = gun.copy(); +// PerkHelper.removePerkByType(output, perkItem.getPerk().type); +// GunsTool.setGunDoubleTag(output, "UpgradePoint", Math.min(MAX_UPGRADE_POINT, level - 1 + GunsTool.getGunDoubleTag(output, "UpgradePoint", 0))); +// this.upgradePoint.set((int) GunsTool.getGunDoubleTag(output, "UpgradePoint", 0)); +// +// this.container.setItem(INPUT_SLOT, output); +// this.container.setChanged(); +// } + } + + /** + * 放置perk物品时,将对应位置的level设置为1 + * + * @param pStack Perk物品 + */ + private void onPlacePerk(ItemStack pStack) { + if (!(pStack.getItem() instanceof PerkItem perkItem)) { + return; + } + + switch (perkItem.getPerk().type) { + case AMMO -> this.ammoPerkLevel.set(1); + case FUNCTIONAL -> this.funcPerkLevel.set(1); + case DAMAGE -> this.damagePerkLevel.set(1); + } + } + + /** + * 将枪械放入输入槽中时,根据枪械上已有的Perk生成对应的Perk物品,并将等级调整为当前的等级 + * + * @param stack 输入的枪械 + */ + private void onPlaceGun(ItemStack stack) { +// if (!(stack.getItem() instanceof GunItem)) { +// return; +// } +// +// int point = (int) GunsTool.getGunDoubleTag(stack, "UpgradePoint", 0); +// this.upgradePoint.set(Mth.clamp(point, 0, MAX_UPGRADE_POINT)); +// +// var ammoPerk = PerkHelper.getPerkByType(stack, Perk.Type.AMMO); +// if (ammoPerk != null) { +// this.ammoPerkLevel.set(PerkHelper.getItemPerkLevel(ammoPerk, stack)); +// var ammoPerkItem = PerkHelper.getPerkItem(ammoPerk); +// ammoPerkItem.ifPresent(registryObject -> this.container.setItem(AMMO_PERK_SLOT, registryObject.get().getDefaultInstance())); +// } +// +// var funcPerk = PerkHelper.getPerkByType(stack, Perk.Type.FUNCTIONAL); +// if (funcPerk != null) { +// this.funcPerkLevel.set(PerkHelper.getItemPerkLevel(funcPerk, stack)); +// var funcPerkItem = PerkHelper.getPerkItem(funcPerk); +// funcPerkItem.ifPresent(registryObject -> this.container.setItem(FUNC_PERK_SLOT, registryObject.get().getDefaultInstance())); +// } +// +// var damagePerk = PerkHelper.getPerkByType(stack, Perk.Type.DAMAGE); +// if (damagePerk != null) { +// this.damagePerkLevel.set(PerkHelper.getItemPerkLevel(damagePerk, stack)); +// var damagePerkItem = PerkHelper.getPerkItem(damagePerk); +// damagePerkItem.ifPresent(registryObject -> this.container.setItem(DAMAGE_PERK_SLOT, registryObject.get().getDefaultInstance())); +// } + + this.container.setChanged(); + this.broadcastChanges(); + } + + /** + * 拿走输入槽中的枪械时,如果Perk槽中存在放入枪械时生成的Perk物品,则将其移除,如果是没有的Perk则无视 + * + * @param pStack 输入的枪械 + */ + private void onTakeGun(ItemStack pStack) { +// if (!(pStack.getItem() instanceof GunItem)) { +// return; +// } +// +// var ammoPerk = PerkHelper.getPerkByType(pStack, Perk.Type.AMMO); +// if (ammoPerk != null) { +// if (this.container.getItem(AMMO_PERK_SLOT).getItem() instanceof PerkItem perkItem && perkItem.getPerk() == ammoPerk) { +// this.container.setItem(AMMO_PERK_SLOT, ItemStack.EMPTY); +// } +// } +// +// var funcPerk = PerkHelper.getPerkByType(pStack, Perk.Type.FUNCTIONAL); +// if (funcPerk != null) { +// if (this.container.getItem(FUNC_PERK_SLOT).getItem() instanceof PerkItem perkItem && perkItem.getPerk() == funcPerk) { +// this.container.setItem(FUNC_PERK_SLOT, ItemStack.EMPTY); +// } +// } +// +// var damagePerk = PerkHelper.getPerkByType(pStack, Perk.Type.DAMAGE); +// if (damagePerk != null) { +// if (this.container.getItem(DAMAGE_PERK_SLOT).getItem() instanceof PerkItem perkItem && perkItem.getPerk() == damagePerk) { +// this.container.setItem(DAMAGE_PERK_SLOT, ItemStack.EMPTY); +// } +// } + + this.upgradePoint.set(0); + this.ammoPerkLevel.set(0); + this.funcPerkLevel.set(0); + this.damagePerkLevel.set(0); + + var ammo = this.container.getItem(AMMO_PERK_SLOT); + if (ammo != ItemStack.EMPTY) { + this.moveItemStackTo(ammo, RESULT_SLOT + 1, RESULT_SLOT + 37, false); + } + + var func = this.container.getItem(FUNC_PERK_SLOT); + if (func != ItemStack.EMPTY) { + this.moveItemStackTo(func, RESULT_SLOT + 1, RESULT_SLOT + 37, false); + } + + var damage = this.container.getItem(DAMAGE_PERK_SLOT); + if (damage != ItemStack.EMPTY) { + this.moveItemStackTo(damage, RESULT_SLOT + 1, RESULT_SLOT + 37, false); + } + + this.container.setChanged(); + } + + @Nullable + public ItemStack getPerkItemBySlot(Perk.Type type) { + return switch (type) { + case AMMO -> this.container.getItem(AMMO_PERK_SLOT); + case FUNCTIONAL -> this.container.getItem(FUNC_PERK_SLOT); + case DAMAGE -> this.container.getItem(DAMAGE_PERK_SLOT); + }; + } + + class InputSlot extends Slot { + public InputSlot(Container pContainer, int pSlot, int pX, int pY) { + super(pContainer, pSlot, pX, pY); + } + + public boolean mayPlace(@NotNull ItemStack pStack) { +// if (pStack.getItem() instanceof GunItem) { +// ItemStack ammoPerk = this.container.getItem(AMMO_PERK_SLOT); +// ItemStack funcPerk = this.container.getItem(FUNC_PERK_SLOT); +// ItemStack damagePerk = this.container.getItem(DAMAGE_PERK_SLOT); +// +// boolean flag1 = ammoPerk.isEmpty(); +// boolean flag2 = funcPerk.isEmpty(); +// boolean flag3 = damagePerk.isEmpty(); +// +// return flag1 && flag2 && flag3 && this.container.getItem(RESULT_SLOT).isEmpty() && this.container.getItem(INPUT_SLOT).isEmpty(); +// } + return false; + } + + public int getMaxStackSize() { + return 1; + } + + @Override + public void onTake(@NotNull Player pPlayer, @NotNull ItemStack pStack) { + super.onTake(pPlayer, pStack); + onTakeGun(pStack); + } + + @Override + public void setByPlayer(@NotNull ItemStack pStack) { + onPlaceGun(pStack); + super.setByPlayer(pStack); + } + } + + class PerkSlot extends Slot { + public Perk.Type type; + + public PerkSlot(Container pContainer, int pSlot, Perk.Type type, int pX, int pY) { + super(pContainer, pSlot, pX, pY); + this.type = type; + } + + public boolean mayPlace(@NotNull ItemStack pStack) { + var slot = switch (type) { + case AMMO -> AMMO_PERK_SLOT; + case FUNCTIONAL -> FUNC_PERK_SLOT; + case DAMAGE -> DAMAGE_PERK_SLOT; + }; + + return pStack.getItem() instanceof PerkItem perkItem && perkItem.getPerk().type == type; +// && !container.getItem(INPUT_SLOT).isEmpty() && container.getItem(INPUT_SLOT).getItem() instanceof GunItem gunItem +// && gunItem.canApplyPerk(perkItem.getPerk()) && container.getItem(slot).isEmpty(); + } + + public int getMaxStackSize() { + return 1; + } + + @Override + public void onTake(@NotNull Player pPlayer, @NotNull ItemStack pStack) { + onTakePerk(pStack); + super.onTake(pPlayer, pStack); + } + + @Override + public void setByPlayer(@NotNull ItemStack pStack) { + onPlacePerk(pStack); + super.setByPlayer(pStack); + } + } + + static class ResultSlot extends Slot { + public ResultSlot(Container pContainer, int pSlot, int pX, int pY) { + super(pContainer, pSlot, pX, pY); + } + + public boolean mayPlace(@NotNull ItemStack pStack) { + return false; + } + + public int getMaxStackSize() { + return 1; + } + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/menu/VehicleMenu.java b/src/main/java/com/atsuishio/superbwarfare/menu/VehicleMenu.java new file mode 100644 index 000000000..b275c6ef6 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/menu/VehicleMenu.java @@ -0,0 +1,82 @@ +package com.atsuishio.superbwarfare.menu; + +import com.atsuishio.superbwarfare.init.ModMenuTypes; +import net.minecraft.world.Container; +import net.minecraft.world.SimpleContainer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class VehicleMenu extends AbstractContainerMenu { + + private final Container container; + private final int containerRows; + + public static final int DEFAULT_SIZE = 102; + + public static final int X_OFFSET = 97; + public static final int Y_OFFSET = 20; + + public VehicleMenu(int pContainerId, Inventory pPlayerInventory) { + this(pContainerId, pPlayerInventory, new SimpleContainer(DEFAULT_SIZE)); + } + + public VehicleMenu(int pContainerId, Inventory pPlayerInventory, Container pContainer) { + super(ModMenuTypes.VEHICLE_MENU.get(), pContainerId); + + checkContainerSize(pContainer, DEFAULT_SIZE); + this.container = pContainer; + this.containerRows = 6; + pContainer.startOpen(pPlayerInventory.player); + int i = (this.containerRows - 4) * 18; + + for (int j = 0; j < this.containerRows; ++j) { + for (int k = 0; k < 17; ++k) { + this.addSlot(new Slot(pContainer, k + j * 17, 8 + k * 18 + 25, 18 + j * 18)); + } + } + + for (int l = 0; l < 3; ++l) { + for (int j = 0; j < 9; ++j) { + this.addSlot(new Slot(pPlayerInventory, j + l * 9 + 9, 8 + j * 18 + X_OFFSET, 84 + l * 18 + Y_OFFSET + i)); + } + } + + for (int k = 0; k < 9; ++k) { + this.addSlot(new Slot(pPlayerInventory, k, 8 + k * 18 + X_OFFSET, 142 + Y_OFFSET + i)); + } + } + + @Override + public @NotNull ItemStack quickMoveStack(@NotNull Player pPlayer, int pIndex) { + ItemStack itemstack = ItemStack.EMPTY; + Slot slot = this.slots.get(pIndex); + if (slot.hasItem()) { + ItemStack stack = slot.getItem(); + itemstack = stack.copy(); + if (pIndex < this.containerRows * 17 + 3) { + if (!this.moveItemStackTo(stack, this.containerRows * 17 + 3, this.slots.size(), true)) { + return ItemStack.EMPTY; + } + } else if (!this.moveItemStackTo(stack, 0, this.containerRows * 17, false)) { + return ItemStack.EMPTY; + } + + if (stack.isEmpty()) { + slot.setByPlayer(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + } + + return itemstack; + } + + @Override + public boolean stillValid(@NotNull Player pPlayer) { + return this.container.stillValid(pPlayer); + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/network/dataslot/ContainerEnergyData.java b/src/main/java/com/atsuishio/superbwarfare/network/dataslot/ContainerEnergyData.java new file mode 100644 index 000000000..53abc739d --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/network/dataslot/ContainerEnergyData.java @@ -0,0 +1,10 @@ +package com.atsuishio.superbwarfare.network.dataslot; + +public interface ContainerEnergyData { + + long get(int pIndex); + + void set(int pIndex, long pValue); + + int getCount(); +} diff --git a/src/main/java/com/atsuishio/superbwarfare/network/dataslot/ContainerEnergyDataSlot.java b/src/main/java/com/atsuishio/superbwarfare/network/dataslot/ContainerEnergyDataSlot.java new file mode 100644 index 000000000..6c92fe746 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/network/dataslot/ContainerEnergyDataSlot.java @@ -0,0 +1,35 @@ +package com.atsuishio.superbwarfare.network.dataslot; + +/** + * Code based on @GoryMoon's Chargers + */ +public abstract class ContainerEnergyDataSlot { + + private long prevValue; + + public ContainerEnergyDataSlot() { + } + + public static ContainerEnergyDataSlot forContainer(final ContainerEnergyData data, final int index) { + return new ContainerEnergyDataSlot() { + public long get() { + return data.get(index); + } + + public void set(long value) { + data.set(index, value); + } + }; + } + + public abstract long get(); + + public abstract void set(long value); + + public boolean checkAndClearUpdateFlag() { + long tmp = this.get(); + boolean changed = tmp != this.prevValue; + this.prevValue = tmp; + return changed; + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/network/dataslot/SimpleEnergyData.java b/src/main/java/com/atsuishio/superbwarfare/network/dataslot/SimpleEnergyData.java new file mode 100644 index 000000000..b48faf85a --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/network/dataslot/SimpleEnergyData.java @@ -0,0 +1,28 @@ +package com.atsuishio.superbwarfare.network.dataslot; + +/** + * Code based on @GoryMoon's Chargers + */ +public class SimpleEnergyData implements ContainerEnergyData { + + private final long[] data; + + public SimpleEnergyData(int size) { + this.data = new long[size]; + } + + @Override + public long get(int index) { + return this.data[index]; + } + + @Override + public void set(int index, long value) { + this.data[index] = value; + } + + @Override + public int getCount() { + return this.data.length; + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/tools/ParticleTool.java b/src/main/java/com/atsuishio/superbwarfare/tools/ParticleTool.java new file mode 100644 index 000000000..31c612766 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/tools/ParticleTool.java @@ -0,0 +1,174 @@ +package com.atsuishio.superbwarfare.tools; + +import com.atsuishio.superbwarfare.init.ModParticleTypes; +import com.atsuishio.superbwarfare.init.ModSounds; +import net.minecraft.core.BlockPos; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.phys.Vec3; + +public class ParticleTool { + public static void sendParticle(ServerLevel level, T particle, double x, double y, double z, int count, + double xOffset, double yOffset, double zOffset, double speed, boolean force) { + for (ServerPlayer serverPlayer : level.players()) { + sendParticle(level, particle, x, y, z, count, xOffset, yOffset, zOffset, speed, force, serverPlayer); + } + } + + public static void sendParticle(ServerLevel level, T particle, double x, double y, double z, int count, + double xOffset, double yOffset, double zOffset, double speed, boolean force, ServerPlayer viewer) { + level.sendParticles(viewer, particle, force, x, y, z, count, xOffset, yOffset, zOffset, speed); + } + + public static void spawnMiniExplosionParticles(Level level, Vec3 pos) { + double x = pos.x; + double y = pos.y; + double z = pos.z; + + if (!level.isClientSide()) { + if ((level.getBlockState(BlockPos.containing(x, y, z))).getBlock() == Blocks.WATER) { + level.playSound(null, BlockPos.containing(x, y + 1, z), ModSounds.EXPLOSION_WATER.get(), SoundSource.BLOCKS, 2, 1); + } + level.playSound(null, BlockPos.containing(x, y + 1, z), SoundEvents.FIREWORK_ROCKET_BLAST, SoundSource.BLOCKS, 4, 1); + } else { + if ((level.getBlockState(BlockPos.containing(x, y, z))).getBlock() == Blocks.WATER) { + level.playLocalSound(x, (y + 1), z, ModSounds.EXPLOSION_WATER.get(), SoundSource.BLOCKS, 1, 1, false); + } + level.playLocalSound(x, (y + 1), z, SoundEvents.FIREWORK_ROCKET_BLAST, SoundSource.BLOCKS, 2, 1, false); + } + + if (level instanceof ServerLevel serverLevel) { + sendParticle(serverLevel, ParticleTypes.CAMPFIRE_COSY_SMOKE, x, y, z, 3, 0.1, 0.1, 0.1, 0.02, true); + sendParticle(serverLevel, ParticleTypes.LARGE_SMOKE, x, y, z, 4, 0.2, 0.2, 0.2, 0.02, true); + sendParticle(serverLevel, ModParticleTypes.FIRE_STAR.get(), x, y, z, 6, 0, 0, 0, 0.2, true); + } + } + + public static void spawnSmallExplosionParticles(Level level, Vec3 pos) { + double x = pos.x; + double y = pos.y; + double z = pos.z; + + if (!level.isClientSide()) { + if ((level.getBlockState(BlockPos.containing(x, y, z))).getBlock() == Blocks.WATER) { + level.playSound(null, BlockPos.containing(x, y + 1, z), ModSounds.EXPLOSION_WATER.get(), SoundSource.BLOCKS, 2, 1); + } + level.playSound(null, BlockPos.containing(x, y + 1, z), SoundEvents.FIREWORK_ROCKET_BLAST, SoundSource.BLOCKS, 4, 1); + level.playSound(null, BlockPos.containing(x, y + 1, z), ModSounds.EXPLOSION_CLOSE.get(), SoundSource.BLOCKS, 3, 1); + level.playSound(null, BlockPos.containing(x, y + 1, z), ModSounds.EXPLOSION_FAR.get(), SoundSource.BLOCKS, 6, 1); + level.playSound(null, BlockPos.containing(x, y + 1, z), ModSounds.EXPLOSION_VERY_FAR.get(), SoundSource.BLOCKS, 12, 1); + } else { + if ((level.getBlockState(BlockPos.containing(x, y, z))).getBlock() == Blocks.WATER) { + level.playLocalSound(x, (y + 1), z, ModSounds.EXPLOSION_WATER.get(), SoundSource.BLOCKS, 1, 1, false); + } + level.playLocalSound(x, (y + 1), z, SoundEvents.FIREWORK_ROCKET_BLAST, SoundSource.BLOCKS, 2, 1, false); + level.playLocalSound(x, (y + 1), z, ModSounds.EXPLOSION_CLOSE.get(), SoundSource.BLOCKS, 1, 1, false); + level.playLocalSound(x, (y + 1), z, ModSounds.EXPLOSION_FAR.get(), SoundSource.BLOCKS, 1, 1, false); + level.playLocalSound(x, (y + 1), z, ModSounds.EXPLOSION_VERY_FAR.get(), SoundSource.BLOCKS, 1, 1, false); + } + + if (level instanceof ServerLevel serverLevel) { + sendParticle(serverLevel, ParticleTypes.EXPLOSION, x, y, z, 2, 0.05, 0.05, 0.05, 1, true); + sendParticle(serverLevel, ParticleTypes.CAMPFIRE_COSY_SMOKE, x, y, z, 3, 0.1, 0.1, 0.1, 0.02, true); + sendParticle(serverLevel, ParticleTypes.LARGE_SMOKE, x, y, z, 4, 0.2, 0.2, 0.2, 0.02, true); + sendParticle(serverLevel, ModParticleTypes.FIRE_STAR.get(), x, y, z, 20, 0, 0, 0, 0.6, true); + sendParticle(serverLevel, ParticleTypes.FLASH, x, y, z, 5, 0.1, 0.1, 0.1, 20, true); + } + } + + public static void spawnMediumExplosionParticles(Level level, Vec3 pos) { + double x = pos.x; + double y = pos.y; + double z = pos.z; + + if (!level.isClientSide()) { + if ((level.getBlockState(BlockPos.containing(x, y, z))).getBlock() == Blocks.WATER) { + level.playSound(null, BlockPos.containing(x, y + 1, z), ModSounds.EXPLOSION_WATER.get(), SoundSource.BLOCKS, 3, 1); + } + level.playSound(null, BlockPos.containing(x, y + 1, z), ModSounds.EXPLOSION_CLOSE.get(), SoundSource.BLOCKS, 6, 1); + level.playSound(null, BlockPos.containing(x, y + 1, z), ModSounds.EXPLOSION_FAR.get(), SoundSource.BLOCKS, 12, 1); + level.playSound(null, BlockPos.containing(x, y + 1, z), ModSounds.EXPLOSION_VERY_FAR.get(), SoundSource.BLOCKS, 32, 1); + } else { + if ((level.getBlockState(BlockPos.containing(x, y, z))).getBlock() == Blocks.WATER) { + level.playLocalSound(x, (y + 1), z, ModSounds.EXPLOSION_WATER.get(), SoundSource.BLOCKS, 1, 1, false); + } + level.playLocalSound(x, (y + 1), z, ModSounds.EXPLOSION_CLOSE.get(), SoundSource.BLOCKS, 1, 1, false); + level.playLocalSound(x, (y + 1), z, ModSounds.EXPLOSION_FAR.get(), SoundSource.BLOCKS, 1, 1, false); + level.playLocalSound(x, (y + 1), z, ModSounds.EXPLOSION_VERY_FAR.get(), SoundSource.BLOCKS, 1, 1, false); + } + + if (level instanceof ServerLevel serverLevel) { + if ((level.getBlockState(BlockPos.containing(x, y, z))).getBlock() == Blocks.WATER) { + sendParticle(serverLevel, ParticleTypes.CLOUD, x, y + 3, z, 20, 1, 3, 1, 0.01, true); + sendParticle(serverLevel, ParticleTypes.CLOUD, x, y + 3, z, 30, 2, 1, 2, 0.01, true); + sendParticle(serverLevel, ParticleTypes.FALLING_WATER, x, y + 3, z, 50, 1.5, 4, 1.5, 1, true); + sendParticle(serverLevel, ParticleTypes.BUBBLE_COLUMN_UP, x, y, z, 60, 3, 0.5, 3, 0.1, true); + } + sendParticle(serverLevel, ParticleTypes.EXPLOSION, x, y + 1, z, 5, 0.7, 0.7, 0.7, 1, true); + sendParticle(serverLevel, ParticleTypes.CAMPFIRE_COSY_SMOKE, x, y + 1, z, 20, 0.2, 1, 0.2, 0.02, true); + sendParticle(serverLevel, ParticleTypes.LARGE_SMOKE, x, y + 1, z, 10, 0.4, 1, 0.4, 0.02, true); + sendParticle(serverLevel, ParticleTypes.CAMPFIRE_COSY_SMOKE, x, y + 0.25, z, 40, 2, 0.001, 2, 0.01, true); + sendParticle(serverLevel, ModParticleTypes.FIRE_STAR.get(), x, y + 0.2, z, 50, 0, 0, 0, 0.9, true); + sendParticle(serverLevel, ParticleTypes.FLASH, x, y + 0.5, z, 50, 0.2, 0.2, 0.2, 20, true); + } + } + + public static void spawnHugeExplosionParticles(Level level, Vec3 pos) { + double x = pos.x; + double y = pos.y; + double z = pos.z; + + if (!level.isClientSide()) { + if ((level.getBlockState(BlockPos.containing(x, y, z))).getBlock() == Blocks.WATER) { + level.playSound(null, BlockPos.containing(x, y + 1, z), ModSounds.EXPLOSION_WATER.get(), SoundSource.BLOCKS, 3, 1); + } + level.playSound(null, BlockPos.containing(x, y + 1, z), ModSounds.HUGE_EXPLOSION_CLOSE.get(), SoundSource.BLOCKS, 8, 1); + level.playSound(null, BlockPos.containing(x, y + 1, z), ModSounds.HUGE_EXPLOSION_FAR.get(), SoundSource.BLOCKS, 16, 1); + level.playSound(null, BlockPos.containing(x, y + 1, z), ModSounds.HUGE_EXPLOSION_VERY_FAR.get(), SoundSource.BLOCKS, 32, 1); + } else { + if ((level.getBlockState(BlockPos.containing(x, y, z))).getBlock() == Blocks.WATER) { + level.playLocalSound(x, (y + 1), z, ModSounds.EXPLOSION_WATER.get(), SoundSource.BLOCKS, 1, 1, false); + } + level.playLocalSound(x, (y + 1), z, ModSounds.HUGE_EXPLOSION_CLOSE.get(), SoundSource.BLOCKS, 1, 1, false); + level.playLocalSound(x, (y + 1), z, ModSounds.HUGE_EXPLOSION_FAR.get(), SoundSource.BLOCKS, 1, 1, false); + level.playLocalSound(x, (y + 1), z, ModSounds.HUGE_EXPLOSION_VERY_FAR.get(), SoundSource.BLOCKS, 1, 1, false); + } + + if (level instanceof ServerLevel serverLevel) { + if ((level.getBlockState(BlockPos.containing(x, y, z))).getBlock() == Blocks.WATER) { + sendParticle(serverLevel, ParticleTypes.CLOUD, x, y + 3, z, 100, 2, 6, 2, 0.01, true); + sendParticle(serverLevel, ParticleTypes.CLOUD, x, y + 3, z, 200, 4, 2, 4, 0.01, true); + sendParticle(serverLevel, ParticleTypes.FALLING_WATER, x, y + 3, z, 500, 3, 8, 3, 1, true); + sendParticle(serverLevel, ParticleTypes.BUBBLE_COLUMN_UP, x, y, z, 350, 6, 1, 6, 0.1, true); + } + + sendParticle(serverLevel, ParticleTypes.EXPLOSION, x, y + 1, z, 75, 2.5, 2.5, 2.5, 1, true); + sendParticle(serverLevel, ParticleTypes.FLASH, x, y + 1, z, 200, 5, 5, 5, 20, true); + sendParticle(serverLevel, ModParticleTypes.FIRE_STAR.get(), x, y + 1, z, 400, 0, 0, 0, 1.5, true); + sendParticle(serverLevel, ParticleTypes.CAMPFIRE_COSY_SMOKE, x, y + 1, z, 75, 2, 3, 2, 0.005, true); + sendParticle(serverLevel, ParticleTypes.CAMPFIRE_COSY_SMOKE, x, y, z, 150, 7, 0.1, 7, 0.005, true); + sendParticle(serverLevel, ParticleTypes.CLOUD, x, y + 1, z, 200, 3, 4, 3, 0.4, true); + } + + } + + public static void cannonHitParticles(Level level, Vec3 pos, Entity entity) { + double x = pos.x + 0.5 * entity.getDeltaMovement().x; + double y = pos.y + 0.5 * entity.getDeltaMovement().y; + double z = pos.z + 0.5 * entity.getDeltaMovement().z; + + if (level instanceof ServerLevel serverLevel) { + sendParticle(serverLevel, ParticleTypes.EXPLOSION, x, y, z, 2, 0.5, 0.5, 0.5, 1, true); + sendParticle(serverLevel, ParticleTypes.FLASH, x, y, z, 2, 0.2, 0.2, 0.2, 10, true); + sendParticle(serverLevel, ModParticleTypes.FIRE_STAR.get(), x, y, z, 40, 0, 0, 0, 1.5, true); + } + + } +} diff --git a/src/main/resources/assets/superbwarfare/blockstates/dragon_teeth.json b/src/main/resources/assets/superbwarfare/blockstates/dragon_teeth.json new file mode 100644 index 000000000..f08ff6463 --- /dev/null +++ b/src/main/resources/assets/superbwarfare/blockstates/dragon_teeth.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "superbwarfare:block/dragon_teeth" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/superbwarfare/blockstates/sandbag.json b/src/main/resources/assets/superbwarfare/blockstates/sandbag.json new file mode 100644 index 000000000..2da1289c7 --- /dev/null +++ b/src/main/resources/assets/superbwarfare/blockstates/sandbag.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "superbwarfare:block/sandbag" + } + } +} \ No newline at end of file