Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Smelty rod improvements #4661

Draft
wants to merge 6 commits into
base: 1.20.x
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ private void registerCapabilities() {
BotaniaFabricCapabilities.BLOCK_PROVIDER.registerForItems((stack, c) -> new LandsRodItem.BlockProviderImpl(stack), BotaniaItems.dirtRod, BotaniaItems.skyDirtRod);
BotaniaFabricCapabilities.BLOCK_PROVIDER.registerForItems((stack, c) -> new BlackHoleTalismanItem.BlockProviderImpl(stack), BotaniaItems.blackHoleTalisman);
BotaniaFabricCapabilities.BLOCK_PROVIDER.registerForItems((stack, c) -> new DepthsRodItem.BlockProviderImpl(), BotaniaItems.cobbleRod);
BotaniaFabricCapabilities.BLOCK_PROVIDER.registerForItems((stack, c) -> new MoltenCoreRodItem.BlockProviderImpl(), BotaniaItems.smeltRod);
BotaniaFabricCapabilities.BLOCK_PROVIDER.registerForItems((stack, c) -> new EnderHandItem.BlockProviderImpl(stack), BotaniaItems.enderHand);
BotaniaFabricCapabilities.BLOCK_PROVIDER.registerForItems((stack, c) -> new TerraFirmaRodItem.BlockProviderImpl(), BotaniaItems.terraformRod);
BotaniaFabricCapabilities.COORD_BOUND_ITEM.registerForItems((st, c) -> new EyeOfTheFlugelItem.CoordBoundItemImpl(st), BotaniaItems.flugelEye);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,8 @@ private void registerEvents() {
BotaniaItems.blackHoleTalisman, BlackHoleTalismanItem.BlockProviderImpl::new,
BotaniaItems.cobbleRod, s -> new DepthsRodItem.BlockProviderImpl(),
BotaniaItems.enderHand, EnderHandItem.BlockProviderImpl::new,
BotaniaItems.terraformRod, s -> new TerraFirmaRodItem.BlockProviderImpl()
BotaniaItems.terraformRod, s -> new TerraFirmaRodItem.BlockProviderImpl(),
BotaniaItems.smeltRod, s -> new MoltenCoreRodItem.BlockProviderImpl()
));

private static final Supplier<Map<Item, Function<ItemStack, CoordBoundItem>>> COORD_BOUND_ITEM = Suppliers.memoize(() -> Map.of(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,144 +8,194 @@
*/
package vazkii.botania.common.item.rod;

import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.Container;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.*;
import net.minecraft.world.entity.SlotAccess;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.ClickAction;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.ItemUtils;
import net.minecraft.world.item.UseAnim;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.item.crafting.RecipeType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;

import org.jetbrains.annotations.NotNull;

import vazkii.botania.api.BotaniaAPI;
import vazkii.botania.api.item.BlockProvider;
import vazkii.botania.api.mana.ManaItemHandler;
import vazkii.botania.client.fx.WispParticleData;
import vazkii.botania.common.handler.BotaniaSounds;
import vazkii.botania.common.item.equipment.tool.ToolCommons;
import vazkii.botania.common.helper.BlockProviderHelper;
import vazkii.botania.xplat.XplatAbstractions;

import java.util.Map;
import java.util.WeakHashMap;
import java.util.*;

public class MoltenCoreRodItem extends Item {
private static Map<Block, List<Block>> reverseSmeltMap = new HashMap<>();

private static final int TIME = 10;
private static final int COST = 300;
private static final int COST_PER_TICK = COST / TIME;

public static final Map<Player, SmeltData> playerData = new WeakHashMap<>();
private static final long COOLDOWN = 4;
private long lastUsedAt = -1;

public MoltenCoreRodItem(Properties props) {
super(props);
}

@NotNull
@Override
public UseAnim getUseAnimation(ItemStack stack) {
return UseAnim.BOW;
}
public InteractionResult useOn(UseOnContext ctx) {
Player p = ctx.getPlayer();
ItemStack stack = ctx.getItemInHand();
Level world = ctx.getLevel();
Direction side = ctx.getClickedFace();
BlockPos pos = ctx.getClickedPos();

if (p.isSpectator() || stack.isEmpty() || !stack.is(this)) {
return InteractionResult.PASS;
}
if (!ManaItemHandler.instance().requestManaExactForTool(stack, p, COST, false) || lastUsedAt + COOLDOWN > world.getGameTime()) {
return InteractionResult.SUCCESS;
}
if (ManaItemHandler.instance().hasProficiency(p, stack)) {
Direction.Axis axis = side.getAxis();
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
for (int k = -1; k <= 1; k++) {
BlockPos offset = pos.offset(axis == Direction.Axis.X ? 0 : i, axis == Direction.Axis.Y ? 0 : j, axis == Direction.Axis.Z ? 0 : k);
smelt(world, offset, p, stack);
}
}
}
} else {
smelt(world, pos, p, stack);
}

@Override
public int getUseDuration(ItemStack stack) {
return 72000;
}

@NotNull
@Override
public InteractionResultHolder<ItemStack> use(Level world, Player player, @NotNull InteractionHand hand) {
return ItemUtils.startUsingInstantly(world, player, hand);
return InteractionResult.SUCCESS;
}

@Override
public void onUseTick(Level world, LivingEntity living, ItemStack stack, int time) {
if (!(living instanceof Player p)) {
return;
}
private void smelt(Level world, BlockPos pos, Player p, ItemStack stack) {
BlockState state = world.getBlockState(pos);
Container dummyInv = new SimpleContainer(1);
dummyInv.setItem(0, new ItemStack(state.getBlock()));
world.getRecipeManager().getRecipeFor(RecipeType.SMELTING, dummyInv, p.level())
.map(r -> r.assemble(dummyInv, world.registryAccess()))
.filter(r -> !r.isEmpty() && r.getItem() instanceof BlockItem)
.ifPresent(result -> {
if (!world.isClientSide) {
world.setBlockAndUpdate(pos, Block.byItem(result.getItem()).defaultBlockState());
world.playSound(null, p.getX(), p.getY(), p.getZ(), BotaniaSounds.smeltRod, SoundSource.PLAYERS, 1F, 1F);
world.playSound(null, p.getX(), p.getY(), p.getZ(), BotaniaSounds.smeltRod2, SoundSource.PLAYERS, 1F, 1F);

ManaItemHandler.instance().requestManaExactForTool(stack, p, COST, true);
lastUsedAt = world.getGameTime();
}
WispParticleData data1 = WispParticleData.wisp(0.5F, 1F, 0.2F, 0.2F, 1);
for (int i = 0; i < 25; i++) {
double x = pos.getX() + Math.random();
double y = pos.getY() + Math.random();
double z = pos.getZ() + Math.random();
world.addParticle(data1, x, y, z, 0, (float) -Math.random() / 10F, 0);
}
});
}

if (!ManaItemHandler.instance().requestManaExactForTool(stack, p, COST_PER_TICK, false)) {
return;
}

BlockHitResult pos = ToolCommons.raytraceFromEntity(p, 32, false);

if (pos.getType() == HitResult.Type.BLOCK) {
BlockState state = world.getBlockState(pos.getBlockPos());

dummyInv.setItem(0, new ItemStack(state.getBlock()));
world.getRecipeManager().getRecipeFor(RecipeType.SMELTING, dummyInv, p.level())
@Override
public boolean overrideStackedOnOther(
@NotNull ItemStack rod, @NotNull Slot slot,
@NotNull ClickAction clickAction, @NotNull Player player) {
if (clickAction == ClickAction.SECONDARY && ManaItemHandler.instance().requestManaExactForTool(rod, player, COST * slot.getItem().getCount(), false)) {
Container dummyInv = new SimpleContainer(1);
dummyInv.setItem(0, slot.getItem());
Level world = player.level();
world.getRecipeManager().getRecipeFor(RecipeType.SMELTING, dummyInv, world)
.map(r -> r.assemble(dummyInv, world.registryAccess()))
.filter(r -> !r.isEmpty() && r.getItem() instanceof BlockItem)
.filter(r -> !r.isEmpty())
.ifPresent(result -> {
boolean decremented = false;

if (playerData.containsKey(p)) {
SmeltData data = playerData.get(p);

if (data.equalPos(pos)) {
data.progress--;
decremented = true;
if (data.progress <= 0) {
if (!world.isClientSide) {
world.setBlockAndUpdate(pos.getBlockPos(), Block.byItem(result.getItem()).defaultBlockState());
world.playSound(null, p.getX(), p.getY(), p.getZ(), BotaniaSounds.smeltRod, SoundSource.PLAYERS, 1F, 1F);
world.playSound(null, p.getX(), p.getY(), p.getZ(), BotaniaSounds.smeltRod2, SoundSource.PLAYERS, 1F, 1F);

ManaItemHandler.instance().requestManaExactForTool(stack, p, COST_PER_TICK, true);
playerData.remove(p);
decremented = false;
}

WispParticleData data1 = WispParticleData.wisp(0.5F, 1F, 0.2F, 0.2F, 1);
for (int i = 0; i < 25; i++) {
double x = pos.getBlockPos().getX() + Math.random();
double y = pos.getBlockPos().getY() + Math.random();
double z = pos.getBlockPos().getZ() + Math.random();
world.addParticle(data1, x, y, z, 0, (float) -Math.random() / 10F, 0);
}
}
}
}

if (!decremented) {
playerData.put(p, new SmeltData(pos, ManaItemHandler.instance().hasProficiency(p, stack) ? (int) (TIME * 0.6) : TIME));
} else {
for (int i = 0; i < 2; i++) {
double x = pos.getBlockPos().getX() + Math.random();
double y = pos.getBlockPos().getY() + Math.random();
double z = pos.getBlockPos().getZ() + Math.random();
WispParticleData data = WispParticleData.wisp(0.5F, 1F, 0.2F, 0.2F, 1);
world.addParticle(data, x, y, z, 0, (float) Math.random() / 10F, 0);
}
if (time % 10 == 0) {
world.playSound(null, p.getX(), p.getY(), p.getZ(), BotaniaSounds.smeltRodSimmer, SoundSource.PLAYERS, (float) Math.random() / 2F + 0.5F, 1F);
}
}
slot.set(result.copyWithCount(slot.getItem().getCount()));
ManaItemHandler.instance().requestManaExactForTool(rod, player, COST * slot.getItem().getCount(), true);
});
return true;
}
return false;
}

static class SmeltData {
public final BlockHitResult pos;
public int progress;
public static class BlockProviderImpl implements BlockProvider {

public BlockProviderImpl() {}

@Override
public boolean provideBlock(Player player, ItemStack requestor, Block block, boolean doit) {
Level world = player.level();
List<Block> sources = getCachedIngredients(block, world);
for (int i = 0; i < player.getInventory().getContainerSize(); i++) {
ItemStack stack = player.getInventory().getItem(i);
if (stack.isEmpty() || stack.getItem() instanceof MoltenCoreRodItem) {
continue;
}
if (stack.getItem() instanceof BlockItem blockStack && sources.contains(blockStack.getBlock())) {
BotaniaAPI.LOGGER.info("found {} that smelts into {}, doit:{}", stack, block, doit);
if (!doit) {
BotaniaAPI.LOGGER.info("provdie{}, mana{}", BlockProviderHelper.asBlockProvider(stack).provideBlock(player, requestor, block, doit), ManaItemHandler.instance().requestManaExactForTool(requestor, player, COST, doit));
}
return BlockProviderHelper.asBlockProvider(stack).provideBlock(player, requestor, ((BlockItem) stack.getItem()).getBlock(), doit) && ManaItemHandler.instance().requestManaExactForTool(requestor, player, COST, doit);
} else {
var provider = XplatAbstractions.INSTANCE.findBlockProvider(stack);
if (provider != null) {
for (Block b : sources) {
if (provider.provideBlock(player, requestor, b, doit)) {
return ManaItemHandler.instance().requestManaExactForTool(requestor, player, COST, doit);
}
}
}
}
}
return false;
}

public SmeltData(BlockHitResult pos, int progress) {
this.pos = pos;
this.progress = progress;
@Override
public int getBlockCount(Player player, ItemStack requestor, Block block) {
Level world = player.level();
List<Block> sources = getCachedIngredients(block, world);
int count = 0;
for (int i = 0; i < player.getInventory().getContainerSize(); i++) {
ItemStack stack = player.getInventory().getItem(i);
if (stack.isEmpty() || stack.getItem() instanceof MoltenCoreRodItem) {
continue;
}
if (stack.getItem() instanceof BlockItem blockStack && sources.contains(blockStack.getBlock())) {
count += stack.getCount();
} else {
var provider = XplatAbstractions.INSTANCE.findBlockProvider(stack);
if (provider != null) {
for (Block b : sources) {
count += provider.getBlockCount(player, requestor, b);
}
}
}
}
return Math.min(count, ManaItemHandler.instance().getInvocationCountForTool(requestor, player, COST));
}

public boolean equalPos(BlockHitResult pos) {
return pos.getBlockPos().equals(this.pos.getBlockPos());
private List<Block> getCachedIngredients(Block block, Level world) {
if (reverseSmeltMap.containsKey(block)) {
return reverseSmeltMap.get(block);
}
ArrayList<Block> validIngredients = new ArrayList<>();
world.getRecipeManager().getAllRecipesFor(RecipeType.SMELTING).stream()
.filter(r -> r.getResultItem(world.registryAccess()).getItem() instanceof BlockItem b && b.getBlock().equals(block))
.map(r -> r.getIngredients().get(0).getItems())
.forEach(a -> Arrays.asList(a).stream().filter(i -> i.getItem() instanceof BlockItem)
.forEach(i -> validIngredients.add(((BlockItem) i.getItem()).getBlock())));
reverseSmeltMap.put(block, validIngredients);
BotaniaAPI.LOGGER.info("cached recipes for {}: {}", block, validIngredients);
return validIngredients;
}
}
}
2 changes: 1 addition & 1 deletion Xplat/src/main/resources/assets/botania/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -2962,7 +2962,7 @@

"botania.entry.smeltRod": "Rod of the Molten Core",
"botania.tagline.smeltRod": "A rod for smelting blocks",
"botania.page.smeltRod0": "The $(item)Rod of the Molten Core$(0) has the ability to focus heat drawn from the world's core. Focusing it (by holding right-click) at a block in the world that can be smelted into a different block will smelt it into that block.$(p)For example, $(item)Cobblestone$(0) will smelt into $(item)Stone$(0) and then into $(item)Smooth Stone$(0), $(item)Sand$(0) melts into $(item)Glass$(0), and so on.",
"botania.page.smeltRod0": "The $(item)Rod of the Molten Core$(0) has the ability to focus heat drawn from the world's core. Punching a block in the world that can be smelted into a different block will smelt it into that block.$(p)For example, $(item)Cobblestone$(0) will smelt into $(item)Stone$(0) and then into $(item)Smooth Stone$(0), $(item)Sand$(0) melts into $(item)Glass$(0), and so on. Furthermore, the rod can be used as a portable furnace by right clicking items onto it in inventory, for the same mana cost",
"botania.page.smeltRod1": "No raid party required",

"botania.entry.worldSeed": "World Seed",
Expand Down
2 changes: 2 additions & 0 deletions web/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ and start a new "Upcoming" section.
{% include changelog_header.html version="Upcoming" %}

* Add: Horn and Drum of the Wild can break moss carpet, with the option to add more blocks through a block tag
* Add: Rod of the Molten Core can now act as a portable furnace, providing in-inventory smelting
* Change: Cellular blocks around the boundary of a Dandelifeon's simulation area are ignored, unless they belong to another active Dandelifeon (NEstoll)
* Change: Charm of the Diva also supports charming or targeting neutral mobs that are angry at the player or attacking one of the player's tamed animals, and properly prevents the player's tamed animals from being affected or targeted by the charm
* Change: Rod of the Molten Core now smelts blocks punched while holding it (similar to how the Rod of the Shifting Crust works)
* Fix: Flight bar for Flügel Tiara no longer overlaps with the refilling air bubbles indicator or the mount health bar, if that uses more than one row
* Fix: The Manaseer Monocle's flower radius indicator no longer jumps around if you are very far from the world origin, and should also not Z-fight with the binding radius indicator of luminizers anymore
* Fix: The air bubble created by Bubbells no longer flickers with inflowing water
Expand Down