diff --git a/libs/flib-1.21.1-0.1.2-SNAPSHOT.jar b/libs/flib-1.21.1-0.1.2-SNAPSHOT.jar deleted file mode 100644 index ceb1d307f..000000000 Binary files a/libs/flib-1.21.1-0.1.2-SNAPSHOT.jar and /dev/null differ diff --git a/src/main/java/com/lothrazar/cyclic/enchant/AutoSmeltEnchant.java b/src/main/java/com/lothrazar/cyclic/enchant/AutoSmeltEnchant.java index af20cd6c9..53ef18bb6 100644 --- a/src/main/java/com/lothrazar/cyclic/enchant/AutoSmeltEnchant.java +++ b/src/main/java/com/lothrazar/cyclic/enchant/AutoSmeltEnchant.java @@ -3,15 +3,22 @@ import java.util.Optional; import java.util.function.Supplier; import com.google.common.base.Suppliers; +import com.lothrazar.cyclic.registry.EnchantRegistry; +import com.lothrazar.library.util.EnchantUtil; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.core.Holder; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.RecipeHolder; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.item.crafting.SingleRecipeInput; import net.minecraft.world.item.crafting.SmeltingRecipe; +import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; import net.neoforged.neoforge.common.ModConfigSpec.BooleanValue; import net.neoforged.neoforge.common.loot.IGlobalLootModifier; @@ -37,6 +44,20 @@ public EnchantAutoSmeltModifier(LootItemCondition[] conditionsIn) { @Override protected ObjectArrayList doApply(ObjectArrayList originalLoot, LootContext context) { + /// First, know if the enchantment is really applied and enabled + if (!isEnabled()) return originalLoot; + + ItemStack tool = context.getParamOrNull(LootContextParams.TOOL); + if (tool == null || tool.isEmpty()) return originalLoot; + + Entity entity = context.getParamOrNull(LootContextParams.THIS_ENTITY); + if (!(entity instanceof Player player)) return originalLoot; + + Holder holder = EnchantUtil.holder(EnchantRegistry.AUTO_SMELT, player); + int level = EnchantUtil.getCurrentLevelTool(holder, tool); + if (level <= 0) return originalLoot; + + /// The enchantment is applied and enabled, convert the loot ObjectArrayList newLoot = new ObjectArrayList<>(); originalLoot.forEach((stack) -> { Optional> optional = context.getLevel().getRecipeManager() diff --git a/src/main/java/com/lothrazar/cyclic/enchant/MultiJumpEnchant.java b/src/main/java/com/lothrazar/cyclic/enchant/MultiJumpEnchant.java index b4b54d7e2..669b0847a 100644 --- a/src/main/java/com/lothrazar/cyclic/enchant/MultiJumpEnchant.java +++ b/src/main/java/com/lothrazar/cyclic/enchant/MultiJumpEnchant.java @@ -1,18 +1,12 @@ package com.lothrazar.cyclic.enchant; import com.lothrazar.cyclic.registry.EnchantRegistry; -import com.lothrazar.cyclic.registry.PacketRegistry; import com.lothrazar.library.core.Const; import com.lothrazar.library.util.EnchantUtil; import com.lothrazar.library.packet.PacketPlayerFalldamage; import com.lothrazar.library.util.EntityUtil; -import com.lothrazar.library.util.ParticleUtil; import net.minecraft.client.Minecraft; import net.minecraft.core.Holder; -import net.minecraft.core.component.DataComponents; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.core.particles.ParticleTypes; -import net.minecraft.world.item.component.CustomData; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.vehicle.Boat; import net.minecraft.world.item.ItemStack; @@ -22,6 +16,8 @@ import net.neoforged.neoforge.event.tick.EntityTickEvent; import net.neoforged.neoforge.network.PacketDistributor; +import static com.lothrazar.cyclic.registry.AttachmentRegistry.LAUNCH_USES; + public class MultiJumpEnchant { public static final String ID = "launch"; @@ -38,11 +34,13 @@ public static boolean isEnabled() { @SubscribeEvent public void onEntityUpdate(EntityTickEvent.Pre event) { if (!(event.getEntity() instanceof Player p)) { return; } + Holder h = EnchantUtil.holder(EnchantRegistry.LAUNCH, p); ItemStack armorStack = EnchantUtil.getFirstArmorStackWithEnchant(h, p); if (armorStack.isEmpty()) { return; } - if ((p.hasImpulse == false || p.onGround()) && armorStack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag().getInt(NBT_USES) > 0) { - armorStack.update(DataComponents.CUSTOM_DATA, CustomData.EMPTY, d -> { CompoundTag t = d.copyTag(); t.putInt(NBT_USES, 0); return CustomData.of(t); }); + + if (p.onGround()) { + p.setData(LAUNCH_USES, 0); } } @@ -50,28 +48,24 @@ public void onEntityUpdate(EntityTickEvent.Pre event) { // (in 1.21 EnchantRegistry.LAUNCH is a ResourceKey, not a DeferredHolder<…, MultiJumpEnchant>). public static void onKeyInput(Player player) { if (player == null || player.getVehicle() instanceof Boat) { return; } + if (player.onGround() || player.isCrouching() || player.isInWater()) return; + if (Minecraft.getInstance().screen != null) return; + Holder h = EnchantUtil.holder(EnchantRegistry.LAUNCH, player); ItemStack feet = EnchantUtil.getFirstArmorStackWithEnchant(h, player); - if (feet.isEmpty() || player.isCrouching()) { return; } - int level = EnchantUtil.getCurrentLevelTool(h, feet); - if (level <= 0) { return; } - if (player.getCooldowns().isOnCooldown(feet.getItem())) { return; } - if (Minecraft.getInstance().options.keyJump.isDown() - && player.getY() < player.yOld && player.hasImpulse && !player.isInWater()) { - int uses = feet.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag().getInt(NBT_USES); - player.fallDistance = 0; - float angle = (player.getDeltaMovement().x == 0 && player.getDeltaMovement().z == 0) ? 90 : ROTATIONPITCH; - EntityUtil.launch(player, angle, POWER); - ParticleUtil.spawnParticle(player.getCommandSenderWorld(), ParticleTypes.CRIT, player.blockPosition(), 7); - uses++; - if (uses >= level) { - if (!feet.isEmpty()) EntityUtil.setCooldownItem(player, feet.getItem(), COOLDOWN); - uses = 0; - } - final int finalUses = uses; - feet.update(DataComponents.CUSTOM_DATA, CustomData.EMPTY, d -> { CompoundTag t = d.copyTag(); t.putInt(NBT_USES, finalUses); return CustomData.of(t); }); - player.fallDistance = 0; - PacketDistributor.sendToServer(new PacketPlayerFalldamage()); + int level = feet.getEnchantmentLevel(h); + if (level <= 0 || player.getCooldowns().isOnCooldown(feet.getItem())) return; + + int jumpsUsed = player.getData(LAUNCH_USES) + 1; + if (jumpsUsed >= level) { + if (!feet.isEmpty()) EntityUtil.setCooldownItem(player, feet.getItem(), COOLDOWN); } + + float angle = (player.getDeltaMovement().x == 0 && player.getDeltaMovement().z == 0) ? 90 : ROTATIONPITCH; + EntityUtil.launch(player, angle, POWER); + player.setData(LAUNCH_USES, jumpsUsed); + + player.fallDistance = 0; + PacketDistributor.sendToServer(new PacketPlayerFalldamage()); } } diff --git a/src/main/java/com/lothrazar/cyclic/event/ClientInputEventHandler.java b/src/main/java/com/lothrazar/cyclic/event/ClientInputEventHandler.java index e5bba7af7..117d988e4 100644 --- a/src/main/java/com/lothrazar/cyclic/event/ClientInputEventHandler.java +++ b/src/main/java/com/lothrazar/cyclic/event/ClientInputEventHandler.java @@ -27,12 +27,15 @@ import net.neoforged.neoforge.client.event.InputEvent; import net.neoforged.neoforge.client.event.ScreenEvent; import net.neoforged.neoforge.network.PacketDistributor; +import org.lwjgl.glfw.GLFW; public class ClientInputEventHandler { @SubscribeEvent public void onKeyInput(InputEvent.Key event) { - MultiJumpEnchant.onKeyInput(Minecraft.getInstance().player); + if (event.getKey() == Minecraft.getInstance().options.keyJump.getKey().getValue() && event.getAction() == GLFW.GLFW_PRESS) { + MultiJumpEnchant.onKeyInput(Minecraft.getInstance().player); + } if (ClientRegistryCyclic.CAKE.consumeClick()) { ItemCakeInventory.onKeyInput(Minecraft.getInstance().player); } diff --git a/src/main/java/com/lothrazar/cyclic/event/LaserBeamHandler.java b/src/main/java/com/lothrazar/cyclic/event/LaserBeamHandler.java new file mode 100644 index 000000000..0180d50e1 --- /dev/null +++ b/src/main/java/com/lothrazar/cyclic/event/LaserBeamHandler.java @@ -0,0 +1,73 @@ +package com.lothrazar.cyclic.event; + +import com.lothrazar.cyclic.config.ConfigRegistry; +import com.lothrazar.cyclic.item.LaserItem; +import com.lothrazar.library.render.RenderEntityToBlockLaser; +import net.minecraft.client.Minecraft; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.projectile.ProjectileUtil; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.phys.*; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.client.event.RenderLevelStageEvent; +import net.neoforged.neoforge.energy.IEnergyStorage; + +public class LaserBeamHandler { + @SubscribeEvent + public void onRenderBeam(RenderLevelStageEvent event) { + if (event.getStage() != RenderLevelStageEvent.Stage.AFTER_TRANSLUCENT_BLOCKS) { + return; + } + + Minecraft mc = Minecraft.getInstance(); + Player player = mc.player; + if (player == null) { + return; + } + + ItemStack stack = LaserItem.getIfHeld(player); + if (!stack.isEmpty() && player.isUsingItem()) { + // Check energy + IEnergyStorage storage = stack.getCapability(Capabilities.EnergyStorage.ITEM); + if (storage == null || storage.getEnergyStored() < ConfigRegistry.LaserItemEnergy.get()) { + return; + } + + float partialTick = event.getPartialTick().getGameTimeDeltaTicks(); + + // Close-range + if (mc.crosshairPickEntity != null) { + RenderEntityToBlockLaser.renderLaser(event, player, partialTick, stack, InteractionHand.MAIN_HAND, 18, -0.02F); + } + // Long-range + else { + boolean shouldRenderMiss = ConfigRegistry.LaserRenderMisses.get(); + boolean hitLongRange = false; + + // Determine where the laser should hit + double laserRange = ConfigRegistry.LaserItemRange.get(); + Vec3 eyePos = player.getEyePosition(partialTick); + Vec3 view = player.getViewVector(partialTick); + Vec3 end = eyePos.add(view.scale(laserRange)); + AABB aabb = player.getBoundingBox().expandTowards(view.scale(laserRange)).inflate(1.0D); + + EntityHitResult ehr = ProjectileUtil.getEntityHitResult(player, eyePos, end, aabb, ent -> ent.isAttackable() && ent.isAlive(), 0); + + if (ehr != null) { + Vec3 hitLoc = ehr.getLocation(); + BlockHitResult miss = player.level().clip(new ClipContext(eyePos, hitLoc, ClipContext.Block.VISUAL, ClipContext.Fluid.NONE, player)); + if (miss.getType() != HitResult.Type.BLOCK) { + hitLongRange = true; + } + } + + if (hitLongRange || shouldRenderMiss) { + RenderEntityToBlockLaser.renderLaser(event, player, partialTick, stack, InteractionHand.MAIN_HAND); + } + } + } + } +} diff --git a/src/main/java/com/lothrazar/cyclic/event/OutlineRenderHandler.java b/src/main/java/com/lothrazar/cyclic/event/OutlineRenderHandler.java new file mode 100644 index 000000000..0fa42e4da --- /dev/null +++ b/src/main/java/com/lothrazar/cyclic/event/OutlineRenderHandler.java @@ -0,0 +1,158 @@ +package com.lothrazar.cyclic.event; + +import com.lothrazar.cyclic.config.ClientConfigCyclic; +import com.lothrazar.cyclic.item.OreProspector; +import com.lothrazar.cyclic.item.builder.BuildStyle; +import com.lothrazar.cyclic.item.builder.BuilderItem; +import com.lothrazar.cyclic.item.builder.PacketSwapBlock; +import com.lothrazar.cyclic.item.datacard.LocationGpsCard; +import com.lothrazar.cyclic.item.datacard.ShapeCard; +import com.lothrazar.cyclic.item.random.RandomizerItem; +import com.lothrazar.library.data.BlockPosDim; +import com.lothrazar.library.data.RelativeShape; +import com.lothrazar.library.util.LevelWorldUtil; +import com.lothrazar.library.util.RenderBlockUtils; +import com.mojang.blaze3d.vertex.*; +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.neoforge.client.event.RenderLevelStageEvent; + +import java.awt.Color; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@OnlyIn(Dist.CLIENT) +public class OutlineRenderHandler { + @SubscribeEvent + public void onRenderOutline(RenderLevelStageEvent event) { + if (event.getStage() != RenderLevelStageEvent.Stage.AFTER_TRANSLUCENT_BLOCKS) { + return; + } + + Minecraft mc = Minecraft.getInstance(); + Player player = mc.player; + if (player == null) { + return; + } + + PoseStack poseStack = event.getPoseStack(); + Vec3 cameraPosition = event.getCamera().getPosition(); + Level world = player.level(); + ItemStack stack; + + // Normally, each condition should be mutually exclusive, so the overhead of render delay seems minimal + /// Cubes: outlines and shadows + stack = OreProspector.getIfHeld(player); + if (stack.getItem() instanceof OreProspector) { + handleOreProspector(poseStack, cameraPosition, world, stack); + } + + stack = BuilderItem.getIfHeld(player); + if (stack.getItem() instanceof BuilderItem) { + HitResult hitResult = mc.hitResult; + handleBuilderItem(poseStack, cameraPosition, world, hitResult, stack); + } + + stack = RandomizerItem.getIfHeld(player); + if (stack.getItem() instanceof RandomizerItem) { + handleRandomizerItem(poseStack, cameraPosition, world, player); + } + + stack = player.getMainHandItem(); + if (stack.getItem() instanceof LocationGpsCard) { + handleLocationGpsCard(poseStack, cameraPosition, world, stack); + } + + if (stack.getItem() instanceof ShapeCard) { + handleShapeCard(poseStack, cameraPosition, player, stack); + } + } + + private void handleOreProspector(PoseStack poseStack, Vec3 camPos, Level world, ItemStack stack) { + List coords = OreProspector.getPosition(stack); + for (BlockPosDim loc : coords) { + if (loc != null) { + if (loc.getDimension() == null || + loc.getDimension().equalsIgnoreCase(LevelWorldUtil.dimensionToString(world))) { + RenderBlockUtils.createBox(poseStack, loc.getPos(), camPos); + } + } + } + } + + private void handleBuilderItem(PoseStack poseStack, Vec3 camPos, Level world, HitResult hitResult, ItemStack stack) { + if (hitResult != null && hitResult.getType() == HitResult.Type.BLOCK) { + BlockHitResult lookingAt = (BlockHitResult) hitResult; + BlockPos pos = lookingAt.getBlockPos(); + BuildStyle buildStyle = ((BuilderItem) stack.getItem()).style; + if (buildStyle.isOffset()) { + pos = pos.relative(lookingAt.getDirection()); + } + + List coordinates = PacketSwapBlock.getSelectedBlocks(world, pos, BuilderItem.getActionType(stack), lookingAt.getDirection(), buildStyle); + + for (BlockPos outline : coordinates) { + RenderBlockUtils.createBox(poseStack, outline, camPos); + } + } + } + + private void handleRandomizerItem(PoseStack poseStack, Vec3 camPos, Level world, Player player) { + int range = 6; + BlockHitResult lookingAt = RenderBlockUtils.getLookingAt(player, range); + if (world.getBlockState(lookingAt.getBlockPos()).isAir()) { + return; + } + + List coords = RandomizerItem.getPlaces(lookingAt.getBlockPos(), lookingAt.getDirection()); + Map colourMap = new HashMap<>(); + for (BlockPos e : coords) { + BlockState stHere = world.getBlockState(e); + if (!RandomizerItem.canMove(stHere, world, e) && !stHere.isAir()) { + colourMap.put(e, Color.RED); + } + else if (!stHere.isAir()) { + RenderBlockUtils.createBox(poseStack, e, camPos); + } + } + + if (!colourMap.isEmpty()) { + final float scale = 1, alpha = 1; + RenderBlockUtils.renderColourCubes(poseStack, camPos, colourMap, scale, alpha); + } + } + + private void handleLocationGpsCard(PoseStack poseStack, Vec3 camPos, Level world, ItemStack stack) { + BlockPosDim loc = LocationGpsCard.getPosition(stack); + if (loc != null) { + if (loc.getDimension() == null || + loc.getDimension().equalsIgnoreCase(LevelWorldUtil.dimensionToString(world))) { + final float scale = 1, alpha = 1; + Map colourMap = new HashMap<>(); + colourMap.put(loc.getPos(), ClientConfigCyclic.getColor(stack)); + RenderBlockUtils.renderColourCubes(poseStack, camPos, colourMap, scale, alpha); + } + } + } + + private void handleShapeCard(PoseStack poseStack, Vec3 camPos, Player player, ItemStack stack) { + RelativeShape shape = RelativeShape.read(stack); + if (shape != null) { + BlockPos here = player.blockPosition(); + for (BlockPos s : shape.getShape()) { + RenderBlockUtils.createBox(poseStack, here.offset(s), camPos); + } + } + } +} diff --git a/src/main/java/com/lothrazar/cyclic/item/ItemHasEnergy.java b/src/main/java/com/lothrazar/cyclic/item/ItemHasEnergy.java index 13509ef34..da902a6cf 100644 --- a/src/main/java/com/lothrazar/cyclic/item/ItemHasEnergy.java +++ b/src/main/java/com/lothrazar/cyclic/item/ItemHasEnergy.java @@ -14,9 +14,11 @@ import java.util.List; public class ItemHasEnergy extends ItemFlib { + public static final int MAX_ENERGY = 16000; + public static final String NBT_TAG = "energy"; public ItemHasEnergy(Properties properties) { - super(properties); + super(properties, new Settings().tooltip()); } public ItemHasEnergy(Properties properties, Settings settings) { super(properties, settings); diff --git a/src/main/java/com/lothrazar/cyclic/item/LaserItem.java b/src/main/java/com/lothrazar/cyclic/item/LaserItem.java index ef074ae04..1574b7e21 100644 --- a/src/main/java/com/lothrazar/cyclic/item/LaserItem.java +++ b/src/main/java/com/lothrazar/cyclic/item/LaserItem.java @@ -1,19 +1,34 @@ package com.lothrazar.cyclic.item; +import com.lothrazar.cyclic.config.ConfigRegistry; +import com.lothrazar.cyclic.net.PacketEntityLaser; +import com.lothrazar.cyclic.registry.SoundRegistry; +import com.lothrazar.library.util.SoundUtil; +import com.lothrazar.library.util.TagDataUtil; +import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.Component; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; 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.entity.projectile.ProjectileUtil; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.UseAnim; +import net.minecraft.world.level.ClipContext; import net.minecraft.world.level.Level; -import net.minecraft.world.item.component.CustomData; +import net.minecraft.world.phys.*; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.energy.IEnergyStorage; +import net.neoforged.neoforge.network.PacketDistributor; public class LaserItem extends ItemHasEnergy { public static final int DELAYDAMAGETICKS = 5; - // public static final double RANGE_FACTOR = 8; + + private static final String NBT_TAG = "damage_cooldown"; + private static long previousMessageTime = 0; public LaserItem(Properties properties) { super(properties.stacksTo(1)); @@ -31,6 +46,67 @@ public InteractionResultHolder use(Level worldIn, Player playerIn, In return new InteractionResultHolder<>(InteractionResult.SUCCESS, itemstack); } + @Override + public void onUseTick(Level level, LivingEntity entityLiving, ItemStack stack, int count) { + if (level.isClientSide() && entityLiving instanceof Player player) { + IEnergyStorage storage = stack.getCapability(Capabilities.EnergyStorage.ITEM); + if (storage == null || storage.getEnergyStored() < ConfigRegistry.LaserItemEnergy.get()) { + return; + } + + long gameTime = level.getGameTime(); + boolean crosshair = false; + + if (gameTime - getDamageCooldown(stack) >= DELAYDAMAGETICKS) { + Minecraft mc = Minecraft.getInstance(); + Entity target = null; + + // Close-range target (handled by Minecraft) + if (mc.crosshairPickEntity != null && mc.crosshairPickEntity.isAlive()) { + crosshair = true; + target = mc.crosshairPickEntity; + } + // Long-range target + else { + double laserRange = ConfigRegistry.LaserItemRange.get(); + Vec3 eyePos = player.getEyePosition(1.0F); + Vec3 view = player.getViewVector(1.0F); + Vec3 end = eyePos.add(view.scale(laserRange)); + AABB aabb = player.getBoundingBox().expandTowards(view.scale(laserRange)).inflate(1.0D); + + EntityHitResult ehr = ProjectileUtil.getEntityHitResult(player, eyePos, end, aabb, ent -> ent.isAttackable() && ent.isAlive(), 0); + + if (ehr != null) { + Vec3 hitLoc = ehr.getLocation(); + // First vector eyePos = FROM, second vector hitLoc = TO + BlockHitResult miss = level.clip(new ClipContext(eyePos, hitLoc, ClipContext.Block.VISUAL, ClipContext.Fluid.NONE, player)); + + if (miss.getType() != HitResult.Type.BLOCK) { + // Don't shoot through walls + target = ehr.getEntity(); + } + } + } + + // Handling the shot + if (target != null) { + resetStackDamageCool(stack, gameTime); + PacketDistributor.sendToServer(new PacketEntityLaser(target.getId(), crosshair)); // Envoi NeoForge 1.21.1 + SoundUtil.playSound(player, SoundRegistry.LASERBEANPEW.get(), 0.2F); + } else { + if (!ConfigRegistry.LaserRenderMisses.get()) { + if (previousMessageTime == 0) { + previousMessageTime = gameTime; + } else { + previousMessageTime = 0; + } + player.displayClientMessage(Component.translatable("item.cyclic.laser_cannon.notarget"), true); + } + } + } + } + } + @Override public int getUseDuration(ItemStack stack, LivingEntity entity) { return 72000 * 2; @@ -41,19 +117,18 @@ public static ItemStack getIfHeld(Player player) { if (heldItem.getItem() instanceof LaserItem) { return heldItem; } - //MAIN HAND ONLY for this case + //MAIN HAND ONLY for this case return ItemStack.EMPTY; } @Override public void releaseUsing(ItemStack stack, Level world, LivingEntity entity, int chargeTimer) {} - public static void resetStackDamageCool(ItemStack lasercannon, long gametime) { - CustomData.EMPTY.copyTag().putLong("damagecooldown", gametime); + public static void resetStackDamageCool(ItemStack stack, long gameTime) { + TagDataUtil.setItemStackNBTVal(stack, NBT_TAG, gameTime); } - public static int getDamageCooldown(ItemStack lasercannon) { - int thisOne = CustomData.EMPTY.copyTag().getInt("damagecooldown"); - return thisOne; + public static int getDamageCooldown(ItemStack stack) { + return TagDataUtil.getItemStackNBT(stack).getInt(NBT_TAG); } } diff --git a/src/main/java/com/lothrazar/cyclic/item/builder/BuilderActionType.java b/src/main/java/com/lothrazar/cyclic/item/builder/BuilderActionType.java index 9affa1003..bd3e206af 100644 --- a/src/main/java/com/lothrazar/cyclic/item/builder/BuilderActionType.java +++ b/src/main/java/com/lothrazar/cyclic/item/builder/BuilderActionType.java @@ -67,7 +67,7 @@ public static void setBlockState(ItemStack wand, BlockState target) { CompoundTag tag = wand.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag(); tag.put(NBTBLOCKSTATE, encoded); wand.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); } - public static BlockState getBlockState(Level level, ItemStack wand) { + public static BlockState getBlockState(ItemStack wand) { if (!wand.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag().contains(NBTBLOCKSTATE)) { return null; } diff --git a/src/main/java/com/lothrazar/cyclic/item/builder/BuilderItem.java b/src/main/java/com/lothrazar/cyclic/item/builder/BuilderItem.java index 3c96efbcc..54f67ab09 100644 --- a/src/main/java/com/lothrazar/cyclic/item/builder/BuilderItem.java +++ b/src/main/java/com/lothrazar/cyclic/item/builder/BuilderItem.java @@ -32,7 +32,7 @@ public BuilderItem(Properties properties, BuildStyle t) { public void appendHoverText(ItemStack stack, Item.TooltipContext worldIn, List tooltip, TooltipFlag flagIn) { String msg = ChatFormatting.GREEN + ChatUtil.lang(BuilderActionType.getName(stack)); tooltip.add(Component.translatable(msg)); - BlockState target = BuilderActionType.getBlockState(null, stack); + BlockState target = BuilderActionType.getBlockState(stack); String block = "scepter.cyclic.nothing"; if (target != null) { block = target.getBlock().getDescriptionId(); diff --git a/src/main/java/com/lothrazar/cyclic/item/builder/PacketSwapBlock.java b/src/main/java/com/lothrazar/cyclic/item/builder/PacketSwapBlock.java index 2d7b966a5..05b8d218f 100644 --- a/src/main/java/com/lothrazar/cyclic/item/builder/PacketSwapBlock.java +++ b/src/main/java/com/lothrazar/cyclic/item/builder/PacketSwapBlock.java @@ -1,5 +1,6 @@ package com.lothrazar.cyclic.item.builder; +import com.lothrazar.library.util.*; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.FriendlyByteBuf; @@ -8,8 +9,16 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.InteractionHand; import com.lothrazar.cyclic.ModCyclic; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; import net.neoforged.neoforge.network.handling.IPayloadContext; +import java.util.*; + public class PacketSwapBlock implements CustomPacketPayload { public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(ModCyclic.MODID, "packet_swap_block")); @@ -54,7 +63,164 @@ public Type type() { public static void handle(PacketSwapBlock message, IPayloadContext ctx) { ctx.enqueueWork(() -> { - // Stubbed payload handler + Player player = ctx.player(); + ItemStack itemStackHeld = player.getItemInHand(message.hand); + BlockState targetState = BuilderActionType.getBlockState(itemStackHeld); + if (targetState == null || !(itemStackHeld.getItem() instanceof BuilderItem)) { + return; + } + BuildStyle buildStyle = ((BuilderItem) itemStackHeld.getItem()).style; + Level world = player.getCommandSenderWorld(); + BlockState replacedBlockState; + List places = getSelectedBlocks(world, message.pos, message.actionType, message.side, buildStyle); + Map processed = new HashMap<>(); + BlockPos curPos; + boolean atLeastOne = false; + synchronized (places) { + for (BlockPos place : places) { + curPos = place; + if (!processed.containsKey(curPos)) { + processed.put(curPos, 0); + } + if (processed.get(curPos) > 0) { + continue; //don't process the same location more than once per click + } + processed.put(curPos, processed.get(curPos) + 1); + int slot = PlayerUtil.getFirstSlotWithBlock(player, targetState); + if (slot < 0) { + //nothing found. is that ok? + if (!player.isCreative()) { + ChatUtil.sendStatusMessage(player, "scepter.cyclic.empty"); + break; + //you have no materials left + } + } + if (world.getBlockEntity(curPos) != null) { + continue; + //ignore tile entities IE do not break chests / etc + } + replacedBlockState = world.getBlockState(curPos); + Block replacedBlock = replacedBlockState.getBlock(); + boolean isInBlacklist = false; + // for (String s : ItemBuildSwapper.swapBlacklist) {//don't use .contains on the list. must use .equals on string + // if (s != null && s.equals(itemName)) { + // isInBlacklist = true; + // break; + // } + // } + if (isInBlacklist) { + continue; + } + if (replacedBlockState.getDestroySpeed(world, curPos) < 0) { + continue; //since we know -1 is unbreakable + } + //wait, do they match? are they the same? do not replace myself + if (LevelWorldUtil.doBlockStatesMatch(replacedBlockState, targetState)) { + continue; + } + //break it and drop the whatever + //the destroy then set was causing exceptions, changed to setAir // https://github.com/PrinceOfAmber/Cyclic/issues/114 + world.setBlock(curPos, Blocks.AIR.defaultBlockState(), 0); + boolean success = false; + //place item block gets slabs in top instead of bottom. but tries to do facing stairs + // success = UtilPlaceBlocks.placeItemblock(world, curPos, stackBuildWith, player); + if (!success) { + success = BlockUtil.placeStateSafe(world, player, curPos, targetState); + } + if (success) { + atLeastOne = true; + PlayerUtil.decrStackSize(player, slot); + world.levelEvent(2001, curPos, Block.getId(targetState)); + //always break with PLAYER CONTEXT in mind + replacedBlock.playerDestroy(world, player, curPos, replacedBlockState, null, itemStackHeld); + } + } // close off the for loop + } + if (atLeastOne) { + ItemStackUtil.damageItem(player, itemStackHeld); + } }); } + + public static List getSelectedBlocks(Level world, BlockPos pos, BuilderActionType actionType, Direction side, BuildStyle style) { + List places = new ArrayList<>(); + int xMin = pos.getX(); + int yMin = pos.getY(); + int zMin = pos.getZ(); + int xMax = pos.getX(); + int yMax = pos.getY(); + int zMax = pos.getZ(); + boolean isVertical = (side == Direction.UP || side == Direction.DOWN); + int offsetH = 0; + int offsetW = 0; + switch (actionType) { + case SINGLE: + places.add(pos); + offsetW = offsetH = 0; + break; + case X3: + offsetW = offsetH = 1; + break; + case X5: + offsetW = offsetH = 2; + break; + case X7: + offsetW = offsetH = 3; + break; + case X9: + offsetW = offsetH = 4; + break; + case X19: + offsetH = 0; + offsetW = 4; + break; + case X91: + offsetH = 4; + offsetW = 0; + break; + default: + break; + } + if (actionType != BuilderActionType.SINGLE) { + if (isVertical) { + //then we just go in all horizontal directions + xMin -= offsetH; + xMax += offsetH; + zMin -= offsetW; + zMax += offsetW; + } + //we hit a horizontal side + else if (side == Direction.EAST || side == Direction.WEST) { + //now we go in a vertical plane + zMin -= offsetH; + zMax += offsetH; + yMin -= offsetW; + yMax += offsetW; + } + else { + //axis hit was north/south, so we go in YZ + xMin -= offsetH; + xMax += offsetH; + yMin -= offsetW; + yMax += offsetW; + } + places = LevelWorldUtil.getPositionsInRange(pos, xMin, xMax, yMin, yMax, zMin, zMax); + } + List retPlaces = new ArrayList<>(); + for (BlockPos p : places) { + if (!world.getBlockState(p).canBeReplaced()) { + //i am not replaceable. so i am a solid block or something + if (!style.isReplaceable()) { + continue; + } + } + // if (wandType == WandType.MATCH && matched != null && + // !UtilWorld.doBlockStatesMatch(matched, world.getBlockState(p))) { + // //we have saved the one we clicked on so only that gets replaced + // continue; + // } + retPlaces.add(p); + } + return retPlaces; + } } diff --git a/src/main/java/com/lothrazar/cyclic/item/datacard/LocationGpsCard.java b/src/main/java/com/lothrazar/cyclic/item/datacard/LocationGpsCard.java index 4b080c568..d042af8bd 100644 --- a/src/main/java/com/lothrazar/cyclic/item/datacard/LocationGpsCard.java +++ b/src/main/java/com/lothrazar/cyclic/item/datacard/LocationGpsCard.java @@ -1,6 +1,8 @@ package com.lothrazar.cyclic.item.datacard; import java.util.List; +import java.util.function.Consumer; + import com.lothrazar.cyclic.ModCyclic; import com.lothrazar.cyclic.item.ItemBaseCyclic; import com.lothrazar.library.data.BlockPosDim; @@ -70,15 +72,18 @@ public InteractionResult useOn(UseOnContext context) { Direction side = context.getClickedFace(); ItemStack held = player.getMainHandItem(); TagDataUtil.setItemStackBlockPos(held, pos); - held.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag().putString(NBT_DIM, LevelWorldUtil.dimensionToString(player.level())); + + String dimText = LevelWorldUtil.dimensionToString(player.level()); + TagDataUtil.setItemStackNBTVal(held, NBT_DIM, dimText); + TagDataUtil.setItemStackNBTVal(held, NBT_SIDE, side.ordinal()); TagDataUtil.setItemStackNBTVal(held, NBT_SIDE + "facing", player.getDirection().ordinal()); ChatUtil.sendStatusMessage(player, ChatUtil.lang("item.location.saved") + ChatUtil.blockPosToString(pos)); // fl Vec3 vec = context.getClickLocation(); - held.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag().putDouble("hitx", vec.x - pos.getX()); - held.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag().putDouble("hity", vec.y - pos.getY()); - held.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag().putDouble("hitz", vec.z - pos.getZ()); + TagDataUtil.setItemStackNBTVal(held, "hitx", vec.x - pos.getX()); + TagDataUtil.setItemStackNBTVal(held, "hity", vec.y - pos.getY()); + TagDataUtil.setItemStackNBTVal(held, "hitz", vec.z - pos.getZ()); return InteractionResult.SUCCESS; } diff --git a/src/main/java/com/lothrazar/cyclic/item/datacard/ShapeCard.java b/src/main/java/com/lothrazar/cyclic/item/datacard/ShapeCard.java index ee9a58d3f..e62c333b5 100644 --- a/src/main/java/com/lothrazar/cyclic/item/datacard/ShapeCard.java +++ b/src/main/java/com/lothrazar/cyclic/item/datacard/ShapeCard.java @@ -41,7 +41,7 @@ public void appendHoverText(ItemStack stack, Item.TooltipContext worldIn, List< MutableComponent t = Component.translatable(getDescriptionId() + ".count"); t.append(shape.getCount() + ""); tooltip.add(t); - BlockState target = BuilderActionType.getBlockState(null, stack); + BlockState target = BuilderActionType.getBlockState(stack); String block = "scepter.cyclic.nothing"; if (target != null) { block = target.getBlock().getDescriptionId(); @@ -66,7 +66,7 @@ public InteractionResultHolder use(Level world, Player player, Intera ItemStack stack = player.getMainHandItem(); RelativeShape shape = RelativeShape.read(stack); if (shape != null) { - BlockState targetState = BuilderActionType.getBlockState(world, stack); + BlockState targetState = BuilderActionType.getBlockState(stack); if (targetState != null) { final BlockPos centerPos = player.blockPosition(); // Direction side = context.getFace(); diff --git a/src/main/java/com/lothrazar/cyclic/item/elemental/IceWand.java b/src/main/java/com/lothrazar/cyclic/item/elemental/IceWand.java index 4e1ffa553..a357149d3 100644 --- a/src/main/java/com/lothrazar/cyclic/item/elemental/IceWand.java +++ b/src/main/java/com/lothrazar/cyclic/item/elemental/IceWand.java @@ -34,8 +34,11 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; import net.neoforged.neoforge.common.ModConfigSpec; public class IceWand extends ItemBaseCyclic { @@ -51,28 +54,34 @@ public InteractionResult useOn(UseOnContext context) { Player player = context.getPlayer(); BlockPos pos = context.getClickedPos(); Direction side = context.getClickedFace(); - if (side != null) { - pos = pos.relative(side); - } + boolean isLevelClientSide = context.getLevel().isClientSide(); + if (spreadWaterFromCenter(context.getLevel(), pos.relative(side))) { //but the real sound - SoundUtil.playSound(player, Blocks.PACKED_ICE.defaultBlockState().getSoundType().getBreakSound()); - ItemStackUtil.damageItem(player, context.getItemInHand()); + if (player != null) { + SoundUtil.playSound(player, Blocks.PACKED_ICE.defaultBlockState().getSoundType().getBreakSound()); + + if (!isLevelClientSide) { + ItemStackUtil.damageItem(player, context.getItemInHand()); + } + } + + return InteractionResult.sidedSuccess(isLevelClientSide); } return super.useOn(context); } private boolean spreadWaterFromCenter(Level world, BlockPos posCenter) { int count = 0; + final BlockState iceState = Blocks.ICE.defaultBlockState(); List water = LevelWorldUtil.findBlocks(world, posCenter, Blocks.WATER, RADIUS.get()); + for (BlockPos pos : water) { - FluidState fluidState = world.getBlockState(pos).getFluidState(); - if (fluidState != null && - // fluidState.getFluidState() != null && - fluidState.getAmount() >= 8) { // .getFluidState() - world.setBlock(pos, Blocks.ICE.defaultBlockState(), 3); + FluidState fluid = world.getFluidState(pos); + if (fluid.is(Fluids.WATER) && fluid.isSource()) { + world.setBlock(pos, iceState, Block.UPDATE_ALL); + count++; } - count++; } return count > 0; } diff --git a/src/main/java/com/lothrazar/cyclic/item/equipment/MattockItem.java b/src/main/java/com/lothrazar/cyclic/item/equipment/MattockItem.java index f24daba73..94bcc2678 100644 --- a/src/main/java/com/lothrazar/cyclic/item/equipment/MattockItem.java +++ b/src/main/java/com/lothrazar/cyclic/item/equipment/MattockItem.java @@ -32,7 +32,7 @@ public MattockItem(Tiers tr, Properties builder, int radius) { this.radius = radius; } - private List getShape(BlockPos pos, int yoff, Direction sideHit) { + private List getShape(BlockPos pos, int yOff, Direction sideHit) { List shape; if (sideHit == Direction.UP || sideHit == Direction.DOWN) { shape = ShapeUtil.squareHorizontalHollow(pos, radius); @@ -41,13 +41,13 @@ private List getShape(BlockPos pos, int yoff, Direction sideHit) { } } else if (sideHit == Direction.EAST || sideHit == Direction.WEST) { - int y = 1 + radius - yoff; + int y = 1 + radius - yOff; int z = radius; shape = ShapeUtil.squareVerticalZ(pos, y, z); } else { //has to be NORTHSOUTH int x = radius; - int y = 1 + radius - yoff; + int y = 1 + radius - yOff; shape = ShapeUtil.squareVerticalX(pos, x, y); } return shape; diff --git a/src/main/java/com/lothrazar/cyclic/item/random/PacketRandomize.java b/src/main/java/com/lothrazar/cyclic/item/random/PacketRandomize.java index e6122a3a3..7c11a6e49 100644 --- a/src/main/java/com/lothrazar/cyclic/item/random/PacketRandomize.java +++ b/src/main/java/com/lothrazar/cyclic/item/random/PacketRandomize.java @@ -1,23 +1,99 @@ package com.lothrazar.cyclic.item.random; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import com.lothrazar.library.util.BlockUtil; +import com.lothrazar.library.util.ItemStackUtil; +import net.minecraft.core.Direction; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; import net.minecraft.core.BlockPos; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; import net.neoforged.neoforge.network.handling.IPayloadContext; import com.lothrazar.cyclic.ModCyclic; + public class PacketRandomize implements CustomPacketPayload { public static final CustomPacketPayload.Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(ModCyclic.MODID, "packet_randomize")); - public static final StreamCodec STREAM_CODEC = StreamCodec.of(PacketRandomize::encode, PacketRandomize::decode); - @Override public CustomPacketPayload.Type type() { return TYPE; } + + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + PacketRandomize::encode, + PacketRandomize::decode + ); + + private static final Random RND = new Random(); private BlockPos pos; - public PacketRandomize(BlockPos p) { pos = p; } - public static PacketRandomize decode(FriendlyByteBuf buf) { return new PacketRandomize(buf.readBlockPos()); } - public static void encode(FriendlyByteBuf buf, PacketRandomize msg) { buf.writeBlockPos(msg.pos); } + private Direction side; + private InteractionHand hand; + + public PacketRandomize(BlockPos pos, Direction side, InteractionHand hand) { + this.pos = pos; + this.side = side; + this.hand = hand; + } + + public static PacketRandomize decode(FriendlyByteBuf buf) { + return new PacketRandomize(buf.readBlockPos(), + Direction.values()[buf.readInt()], + InteractionHand.values()[buf.readInt()]); + } + + public static void encode(FriendlyByteBuf buf, PacketRandomize msg) { + buf.writeBlockPos(msg.pos); + buf.writeInt(msg.side.ordinal()); + buf.writeInt(msg.hand.ordinal()); + } + + @Override + public CustomPacketPayload.Type type() { + return TYPE; + } + public static void handle(PacketRandomize message, IPayloadContext ctx) { ctx.enqueueWork(() -> { - ServerPlayer sender = (ServerPlayer) ctx.player(); + Player player = ctx.player(); + Level world = player.getCommandSenderWorld(); + List places = RandomizerItem.getPlaces(message.pos, message.side); + List rpos = new ArrayList<>(); + List rstates = new ArrayList<>(); + // + BlockState stateHere; + boolean atLeastOne = false; + for (BlockPos p : places) { + stateHere = world.getBlockState(p); + boolean canMove = RandomizerItem.canMove(stateHere, world, p); + // if (stateHere.getBlock().getBlockHardness(stateHere, world, p) < 0) { + // continue;//skip unbreakable + // } + if (canMove) { + //removed world.isSideSolid(p, message.side) && as it was blocking stairs/slabs from moving + rpos.add(p); + rstates.add(stateHere); + } + } + Collections.shuffle(rpos, RND); + BlockPos swapPos; + BlockState swapState; + synchronized (rpos) { //just in case + for (int i = 0; i < rpos.size(); i++) { + swapPos = rpos.get(i); + swapState = rstates.get(i); + world.destroyBlock(swapPos, false); + //playing sound here in large areas causes ConcurrentModificationException + if (BlockUtil.placeStateSafe(world, player, swapPos, swapState, false)) { + atLeastOne = true; + } + } + } + if (atLeastOne) { + ItemStackUtil.damageItem(player, player.getItemInHand(message.hand)); + } }); } } diff --git a/src/main/java/com/lothrazar/cyclic/item/random/RandomizerItem.java b/src/main/java/com/lothrazar/cyclic/item/random/RandomizerItem.java index b9165f9fb..a3198f22c 100644 --- a/src/main/java/com/lothrazar/cyclic/item/random/RandomizerItem.java +++ b/src/main/java/com/lothrazar/cyclic/item/random/RandomizerItem.java @@ -3,7 +3,6 @@ import java.util.ArrayList; import java.util.List; import com.lothrazar.cyclic.item.ItemBaseCyclic; -import com.lothrazar.cyclic.registry.PacketRegistry; import com.lothrazar.library.util.EntityUtil; import com.lothrazar.library.util.LevelWorldUtil; import net.minecraft.core.BlockPos; @@ -14,6 +13,7 @@ import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.network.PacketDistributor; public class RandomizerItem extends ItemBaseCyclic { @@ -45,14 +45,15 @@ public InteractionResult useOn(UseOnContext context) { BlockPos pos = context.getClickedPos(); Direction side = context.getClickedFace(); if (player.level().isClientSide) { - // PacketRegistry.INSTANCE.sendToServer + PacketRandomize message = new PacketRandomize(pos, side, context.getHand()); + PacketDistributor.sendToServer(message); } EntityUtil.setCooldownItem(player, this, COOLDOWN); return super.useOn(context); } public static List getPlaces(final BlockPos pos, final Direction side) { - List places = new ArrayList(); + List places = new ArrayList<>(); int xMin = pos.getX(); int yMin = pos.getY(); int zMin = pos.getZ(); diff --git a/src/main/java/com/lothrazar/cyclic/net/PacketEntityLaser.java b/src/main/java/com/lothrazar/cyclic/net/PacketEntityLaser.java index 1afea3d5e..d8c1498ad 100644 --- a/src/main/java/com/lothrazar/cyclic/net/PacketEntityLaser.java +++ b/src/main/java/com/lothrazar/cyclic/net/PacketEntityLaser.java @@ -37,11 +37,15 @@ import net.neoforged.neoforge.energy.IEnergyStorage; import net.neoforged.neoforge.network.handling.IPayloadContext; import com.lothrazar.cyclic.ModCyclic; + public class PacketEntityLaser implements CustomPacketPayload { public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(ResourceLocation.fromNamespaceAndPath(ModCyclic.MODID, "packet_entity_laser")); - public static final StreamCodec STREAM_CODEC = StreamCodec.of(PacketEntityLaser::encode, PacketEntityLaser::decode); + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + PacketEntityLaser::encode, + PacketEntityLaser::decode + ); @Override @@ -49,7 +53,6 @@ public CustomPacketPayload.Type type() { return TYPE; } - private int entityId; private boolean crosshair; @@ -58,13 +61,23 @@ public PacketEntityLaser(int eid, boolean cross) { this.crosshair = cross; } + public static PacketEntityLaser decode(RegistryFriendlyByteBuf buf) { + return new PacketEntityLaser(buf.readInt(), buf.readBoolean()); + } + + public static void encode(RegistryFriendlyByteBuf buf, PacketEntityLaser msg) { + buf.writeInt(msg.entityId); + buf.writeBoolean(msg.crosshair); + } + public static void handle(PacketEntityLaser message, IPayloadContext ctx) { ctx.enqueueWork(() -> { ServerPlayer sender = (ServerPlayer) ctx.player(); Level level = sender.level(); Entity target = level.getEntity(message.entityId); - //validate also covers delay + ItemStack stack = LaserItem.getIfHeld(sender); + if (PacketEntityLaser.canShoot(sender, target, stack)) { IEnergyStorage storage = stack.getCapability(Capabilities.EnergyStorage.ITEM); if (storage != null) { @@ -83,23 +96,14 @@ public static void handle(PacketEntityLaser message, IPayloadContext ctx) { private static boolean canShoot(ServerPlayer sender, Entity target, ItemStack stack) { if (!sender.isAlive() || !target.isAlive() || target.isInvulnerable()) { - //somene died or target is invincible + // Someone died or target is invincible return false; } if (stack.isEmpty()) { return false; } + IEnergyStorage storage = stack.getCapability(Capabilities.EnergyStorage.ITEM); return (storage != null && storage.extractEnergy(ConfigRegistry.LaserItemEnergy.get(), true) == ConfigRegistry.LaserItemEnergy.get()); } - - public static PacketEntityLaser decode(RegistryFriendlyByteBuf buf) { - PacketEntityLaser msg = new PacketEntityLaser(buf.readInt(), buf.readBoolean()); - return msg; - } - - public static void encode(RegistryFriendlyByteBuf buf, PacketEntityLaser msg) { - buf.writeInt(msg.entityId); - buf.writeBoolean(msg.crosshair); - } } diff --git a/src/main/java/com/lothrazar/cyclic/potion/effect/FlightMayflyEffect.java b/src/main/java/com/lothrazar/cyclic/potion/effect/FlightMayflyEffect.java index 15a321a26..acc621a42 100644 --- a/src/main/java/com/lothrazar/cyclic/potion/effect/FlightMayflyEffect.java +++ b/src/main/java/com/lothrazar/cyclic/potion/effect/FlightMayflyEffect.java @@ -19,6 +19,8 @@ public FlightMayflyEffect(MobEffectCategory typeIn, int liquidColorIn) { private static void setMayFlyFromServer(LivingEntity entity, boolean mayflyIn) { if (entity instanceof ServerPlayer sp) { + if (sp.isCreative() || sp.isSpectator()) return; + //set server-player sp.getAbilities().mayfly = mayflyIn; if (!mayflyIn) { @@ -51,7 +53,7 @@ public void tick(EntityTickEvent.Pre event) { @Override public void isPotionApplicable(MobEffectEvent.Applicable event) { if (event.getEntity() instanceof Player player) { - if (player.isCreative()) { //no creative players should use this to fly + if (player.isCreative() || player.isSpectator()) { //no creative players should use this to fly event.setResult(MobEffectEvent.Applicable.Result.DO_NOT_APPLY); } } diff --git a/src/main/java/com/lothrazar/cyclic/potion/effect/FrostEffect.java b/src/main/java/com/lothrazar/cyclic/potion/effect/FrostEffect.java index 4699ade3c..8d402b02a 100644 --- a/src/main/java/com/lothrazar/cyclic/potion/effect/FrostEffect.java +++ b/src/main/java/com/lothrazar/cyclic/potion/effect/FrostEffect.java @@ -1,10 +1,14 @@ package com.lothrazar.cyclic.potion.effect; import com.lothrazar.cyclic.potion.CyclicMobEffect; +import net.minecraft.core.BlockPos; +import net.minecraft.util.Mth; import net.minecraft.world.effect.MobEffectCategory; import net.minecraft.world.entity.LivingEntity; -// import net.minecraft.world.item.enchantment.FrostWalkerEnchantment; -import net.neoforged.neoforge.event.tick.EntityTickEvent; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.shapes.CollisionContext; public class FrostEffect extends CyclicMobEffect { @@ -13,10 +17,43 @@ public FrostEffect(MobEffectCategory typeIn, int liquidColorIn) { } @Override - public void tick(EntityTickEvent.Pre event) { - // delete me i guess - // LivingEntity living = event.getEntity(); - // int amp = living.getEffect(this).getAmplifier(); - // FrostWalkerEnchantment.onEntityMoved(living, living.level(), living.blockPosition(), amp); + public boolean applyEffectTick(LivingEntity entity, int amplifier) { + if (!entity.onGround()) { + return true; + } + + Level level = entity.level(); + BlockPos pos = entity.blockPosition(); + BlockState frostedIce = Blocks.FROSTED_ICE.defaultBlockState(); + + int radius = Math.min(16, 2 + amplifier); + BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos(); + + for (BlockPos blockPos : BlockPos.betweenClosed(pos.offset(-radius, -1, -radius), pos.offset(radius, -1, radius))) { + if (blockPos.closerToCenterThan(entity.position(), radius)) { + mutablePos.set(blockPos.getX(), blockPos.getY() + 1, blockPos.getZ()); + BlockState blockStateAbove = level.getBlockState(mutablePos); + + if (blockStateAbove.isAir()) { + BlockState currentBlockState = level.getBlockState(blockPos); + + if (currentBlockState.getBlock() == Blocks.WATER && + level.getFluidState(blockPos).isSource() && + frostedIce.canSurvive(level, blockPos) && + level.isUnobstructed(frostedIce, blockPos, CollisionContext.empty())) { + + level.setBlockAndUpdate(blockPos, frostedIce); + + level.scheduleTick(blockPos, Blocks.FROSTED_ICE, Mth.nextInt(entity.getRandom(), 60, 120)); + } + } + } + } + return true; + } + + @Override + public boolean shouldApplyEffectTickThisTick(int tickCount, int amplifier) { + return true; } } diff --git a/src/main/java/com/lothrazar/cyclic/potion/effect/MagneticEffect.java b/src/main/java/com/lothrazar/cyclic/potion/effect/MagneticEffect.java index 215c0c405..d535ba3d1 100644 --- a/src/main/java/com/lothrazar/cyclic/potion/effect/MagneticEffect.java +++ b/src/main/java/com/lothrazar/cyclic/potion/effect/MagneticEffect.java @@ -21,7 +21,7 @@ public void tick(EntityTickEvent.Pre event) { if (inst == null) { return; } - final int amp = inst.getAmplifier(); + final int amp = inst.getAmplifier() + 1; EntityUtil.moveEntityItemsInRegion(livingEntity.level(), livingEntity.blockPosition(), 8 * amp, 1 + amp); } } diff --git a/src/main/java/com/lothrazar/cyclic/registry/AttachmentRegistry.java b/src/main/java/com/lothrazar/cyclic/registry/AttachmentRegistry.java index 373970b01..b7391dc42 100644 --- a/src/main/java/com/lothrazar/cyclic/registry/AttachmentRegistry.java +++ b/src/main/java/com/lothrazar/cyclic/registry/AttachmentRegistry.java @@ -23,4 +23,7 @@ public class AttachmentRegistry { .serialize(PlayerCapabilityStorage.CODEC) .copyOnDeath() .build()); + + public static final Supplier> LAUNCH_USES = + ATTACHMENT_TYPES.register("launch_uses", () -> AttachmentType.builder(() -> 0).build()); } diff --git a/src/main/java/com/lothrazar/cyclic/registry/CapabilityRegistry.java b/src/main/java/com/lothrazar/cyclic/registry/CapabilityRegistry.java index 16fccf6d6..102fc71ac 100644 --- a/src/main/java/com/lothrazar/cyclic/registry/CapabilityRegistry.java +++ b/src/main/java/com/lothrazar/cyclic/registry/CapabilityRegistry.java @@ -7,15 +7,20 @@ import com.lothrazar.cyclic.fluid.FluidSlimeHolder; import com.lothrazar.cyclic.fluid.FluidWaxHolder; import com.lothrazar.cyclic.fluid.FluidXpJuiceHolder; +import com.lothrazar.cyclic.item.ItemHasEnergy; import com.lothrazar.cyclic.item.crafting.CraftingBagCapability; import com.lothrazar.cyclic.item.datacard.filter.FilterCardCapability; import com.lothrazar.cyclic.item.enderbook.EnderBookCapability; import com.lothrazar.cyclic.item.lunchbox.LunchboxCapability; import com.lothrazar.cyclic.item.storagebag.StorageBagCapability; +import com.lothrazar.library.util.TagDataUtil; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.capabilities.Capabilities; import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; +import net.neoforged.neoforge.energy.EnergyStorage; import net.neoforged.neoforge.fluids.capability.wrappers.FluidBucketWrapper; import com.lothrazar.cyclic.ModCyclic; @@ -44,6 +49,36 @@ public static void registerCapabilities(RegisterCapabilitiesEvent event) { return null; }); } + + for (var entry : ItemRegistry.ITEMS.getEntries()) { + Item item = entry.get(); + if (item instanceof ItemHasEnergy energyItem) { + event.registerItem( + Capabilities.EnergyStorage.ITEM, + (stack, ctx) -> new EnergyStorage(ItemHasEnergy.MAX_ENERGY) { + @Override + public int receiveEnergy(int maxReceive, boolean simulate) { + int received = super.receiveEnergy(maxReceive, simulate); + if (!simulate) saveToStack(stack); + return received; + } + @Override + public int extractEnergy(int maxExtract, boolean simulate) { + int extracted = super.extractEnergy(maxExtract, simulate); + if (!simulate) saveToStack(stack); + return extracted; + } + private void saveToStack(ItemStack stack) { + TagDataUtil.setItemStackNBTVal(stack, ItemHasEnergy.NBT_TAG, getEnergyStored()); + } + // Load from NBT on creation + { this.energy = TagDataUtil.getItemStackNBT(stack).getInt(ItemHasEnergy.NBT_TAG); } + }, + item + ); + } + } + event.registerItem( Capabilities.ItemHandler.ITEM, (stack, ctx) -> new StorageBagCapability(stack), diff --git a/src/main/java/com/lothrazar/cyclic/registry/EventRegistry.java b/src/main/java/com/lothrazar/cyclic/registry/EventRegistry.java index 5be8451ad..eddf1ec91 100644 --- a/src/main/java/com/lothrazar/cyclic/registry/EventRegistry.java +++ b/src/main/java/com/lothrazar/cyclic/registry/EventRegistry.java @@ -36,6 +36,8 @@ public static void registerNetworking(final RegisterPayloadHandlersEvent event) public static void setup(final FMLCommonSetupEvent event) { //now all blocks/items exist + NeoForge.EVENT_BUS.register(new OutlineRenderHandler()); + NeoForge.EVENT_BUS.register(new LaserBeamHandler()); NeoForge.EVENT_BUS.register(new PotionEventHandler()); NeoForge.EVENT_BUS.register(new ItemEventHandler()); NeoForge.EVENT_BUS.register(new EnchantEventHandler()); @@ -62,8 +64,6 @@ public static void setup(final FMLCommonSetupEvent event) { NeoForge.EVENT_BUS.register(new TravellerEnchant()); NeoForge.EVENT_BUS.register(new VenomEnchant()); NeoForge.EVENT_BUS.register(new XpEnchant()); - event.enqueueWork(() -> { - CompostRegistry.setup(); - }); + event.enqueueWork(CompostRegistry::setup); } } diff --git a/src/main/java/com/lothrazar/cyclic/registry/PacketRegistry.java b/src/main/java/com/lothrazar/cyclic/registry/PacketRegistry.java index 8f1a45525..0c2a45065 100644 --- a/src/main/java/com/lothrazar/cyclic/registry/PacketRegistry.java +++ b/src/main/java/com/lothrazar/cyclic/registry/PacketRegistry.java @@ -60,7 +60,7 @@ public static void setup(RegisterPayloadHandlersEvent event) { registrar.playToServer(PacketKeyBind.TYPE, PacketKeyBind.STREAM_CODEC, PacketKeyBind::handle); registrar.playToServer(PacketRecordSound.TYPE, PacketRecordSound.STREAM_CODEC, PacketRecordSound::handle); registrar.playToServer(PacketHarvesting.TYPE, PacketHarvesting.STREAM_CODEC, PacketHarvesting::handle); - registrar.playToClient(PacketEntityLaser.TYPE, PacketEntityLaser.STREAM_CODEC, PacketEntityLaser::handle); + registrar.playToServer(PacketEntityLaser.TYPE, PacketEntityLaser.STREAM_CODEC, PacketEntityLaser::handle); registrar.playToClient(PacketPlayerSyncToClient.TYPE, PacketPlayerSyncToClient.STREAM_CODEC, PacketPlayerSyncToClient::handle); registrar.playToClient(PacketSyncManaToClient.TYPE, PacketSyncManaToClient.STREAM_CODEC, PacketSyncManaToClient::handle); registrar.playToClient(PacketSyncHorseCarrots.TYPE, PacketSyncHorseCarrots.STREAM_CODEC, PacketSyncHorseCarrots::handle); diff --git a/src/main/resources/assets/cyclic/lang/en_us.json b/src/main/resources/assets/cyclic/lang/en_us.json index 97f832ac8..edc9546ef 100644 --- a/src/main/resources/assets/cyclic/lang/en_us.json +++ b/src/main/resources/assets/cyclic/lang/en_us.json @@ -77,7 +77,7 @@ "item.cyclic.shield_obsidian.guide":"Prevents knockback completely while blocking. Takes no durability damage from projectiles or explosions (unless they bypass armor)", "item.cyclic.shield_bone":"Bone Shield", "item.cyclic.shield_bone.tooltip":"Average shield", - "item.cyclic.shield_bone.guide":"Takes no durability tamage from projectiles. Not weaker than normal shields unlike some others", + "item.cyclic.shield_bone.guide":"Takes no durability damage from projectiles. Not weaker than normal shields unlike some others", "item.cyclic.flute_summoning":"Summoning Flute", "item.cyclic.flute_summoning.tooltip":"Tap on a specific entity to summon it later", "item.cyclic.flute_summoning.teleported":"Attempting teleport...", @@ -1049,7 +1049,7 @@ "item.cyclic.spell_water.tooltip": "Replace nearby flowing water with source water", "item.cyclic.spell_water.guide": "Turns nearby flowing water into source water. Fills lakes and rivers out very quickly.", "item.cyclic.teleport_wand": "Ender Scepter", - "item.cyclic.teleport_wand.tooltip": "Teleports where you look, using Ender Pearls as fuel", + "item.cyclic.teleport_wand.tooltip": "Teleports where you look", "item.cyclic.teleport_wand.guide": "The Ender Scepter when held and charged, can be used to teleport large distances to the block that you're looking at. Automatically consumes Ender Pearls in your inventory to repair itself.", "item.cyclic.elevation_wand": "Rod of Elevation", "item.cyclic.elevation_wand.tooltip": "Teleport yourself or the target entity up to the nearest surface above", @@ -1391,7 +1391,7 @@ "potion.butter.oops": "Butterfingers! Dropped something when you moved.", "effect.cyclic.noclip": "Phasing", - "effect.cyclic.noclip.description": "Phasing", + "effect.cyclic.noclip.description": "Players go though blocks with this effect", "item.minecraft.potion.effect.cyclic_noclip": "Potion of Phasing", "item.minecraft.splash_potion.effect.cyclic_noclip": "Splash Potion of Phasing", "item.minecraft.lingering_potion.effect.cyclic_noclip": "Lingering Potion of Phasing", diff --git a/src/main/resources/assets/cyclic/textures/mob_effect/noclip.png b/src/main/resources/assets/cyclic/textures/mob_effect/noclip.png new file mode 100644 index 000000000..83959ae81 Binary files /dev/null and b/src/main/resources/assets/cyclic/textures/mob_effect/noclip.png differ diff --git a/src/main/resources/data/minecraft/tags/item/enchantable/mining.json b/src/main/resources/data/minecraft/tags/item/enchantable/mining.json new file mode 100644 index 000000000..f41782b2b --- /dev/null +++ b/src/main/resources/data/minecraft/tags/item/enchantable/mining.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "values": [ + "cyclic:mattock", + "cyclic:mattock_nether", + "cyclic:mattock_stone" + ] +} \ No newline at end of file