Jump to content

Recommended Posts

Posted (edited)

Hi, I'm currently trying to learn how to use capabilities. My requirement is as follows:

I have a "relic" item (max stack size 1) in my player's inventory. Whenever a certain event happens (for now I'm using breaking a stone block as prototype), the "experience" of the item is increased.

The experience must only be increased if the item is actually in the player's inventory, not just when it exists somewhere in the world e.g. a chest or on the ground.

 

My problem is that if I drop the item, it continues to gain experience when I break more blocks. When I try to debug and look at the player's inventory, I can see that after dropping the item the ItemStack in that slot is an Air item - but its delegate is my "relic" item, and it also apparently has inherited my "experience" capability from the dropped item??

 

Could anybody please help me to understand:

  • what this delegate property is, how it is used and/or how it is affecting this situation
  • why the Air itemstack has also been given my item's capabilities? (shouldn't my instanceof check stop that?)

 

I'm going to go ahead and assume I've set up the boilerplate capability bits&bobs wrong in some way, too (all the tutorials I could find were either out of date, or only dealt with simple cases for blocks/entities/tileentities and not items) - so if anybody could point out where that'd be great too :)

 

Thanks in advance!

 

(Note that because I have the following, this issue only occurs if I've got more than one of the item in the inventory and then dropped one of them before breaking the stone block; both the dropped and still-held relics continue to gain experience)

if (player.inventory.hasAny(Set.of(MyMod.MINING_RELIC_ITEM.get())))

 

Relevant classes are as follows:

 

public class MiningRelicItem extends Item {
	...
	    @Override
    public ICapabilityProvider initCapabilities(ItemStack itemStack, @Nullable CompoundNBT nbt)
    {
        // To try to stop passing the capability to an Air item if our item is no longer in the inventory? Bah, didn't seem to help
        if (!itemStack.isEmpty()) {
            MyMod.LOGGER.info("MagicRelicItem just provided some kind of capability");
            return new RelicExperienceCapabilityProvider();
        }
        return null;
    }
}

public class CapabilityHandler
{
    public static final ResourceLocation RELIC_EXP_CAP = new ResourceLocation(MyMod.MODID, "relic_exp");

    @SubscribeEvent
    public void attachCapability(AttachCapabilitiesEvent<Item> event)
    {
        Item item = event.getObject().asItem();
        if (item instanceof MiningRelicItem)
        {
            event.addCapability(RELIC_EXP_CAP, new RelicExperienceCapabilityProvider());
        }
    }
}
      
public class RelicExperienceCapabilityProvider implements ICapabilitySerializable<INBT> {

    @CapabilityInject(IRelicExperienceCapability.class)
    public static Capability<IRelicExperienceCapability> RELIC_EXP_CAPABILITY = null;

    private final RelicExperienceCapability relicExperienceCapability = new RelicExperienceCapability();
    private final RelicExperienceStorage relicExperienceStorage = new RelicExperienceStorage();

    @Nonnull
    @Override
    public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {
        if (cap == RELIC_EXP_CAPABILITY)
        {
            return (LazyOptional<T>) LazyOptional.of(() -> relicExperienceCapability);
        }
        return LazyOptional.empty();
    }

    @Override
    public INBT serializeNBT() {
        return relicExperienceStorage.writeNBT(RELIC_EXP_CAPABILITY, relicExperienceCapability, null);
    }

    @Override
    public void deserializeNBT(INBT nbt) {
        relicExperienceStorage.readNBT(RELIC_EXP_CAPABILITY, relicExperienceCapability, null, nbt);
    }
}

@Mod.EventBusSubscriber(modid = MyMod.MODID, bus = Mod.EventBusSubscriber.Bus.FORGE, value = Dist.CLIENT)
public class ClientEvents {

    @SubscribeEvent
    public static void onBlockDestroyed(BlockEvent.BreakEvent breakEvent)
    {
        if (!breakEvent.isCanceled())
        {
            if (breakEvent.getState().getBlock() == Blocks.STONE)
            {
                MyMod.LOGGER.info("Stone block broken.");
                PlayerEntity player = breakEvent.getPlayer();

                if (player.inventory.hasAny(Set.of(MyMod.MINING_RELIC_ITEM.get())))
                {
                    MyMod.LOGGER.info("The dude is holding a relic");
                    final List<ItemStack> playerItems = Stream
                            .concat(player.inventory.mainInventory.stream(), player.inventory.offHandInventory.stream())
                            .collect(Collectors.toList());

                    for (ItemStack i : playerItems)
                    {
                       IRelicExperienceCapability relicExperienceCapability = i.getCapability(RelicExperienceCapabilityProvider.RELIC_EXP_CAPABILITY).orElse(null);

                       if (relicExperienceCapability != null)
                       {
                           relicExperienceCapability.addExp(5);
                           MyMod.LOGGER.info("I just added 5 exp to a relic...");
                               MyMod.LOGGER.info(String.format("Current exp for this relic is: %d", relicExperienceCapability.getExp()));
                       }
                    }
                }
            }
        }
    }
}

 

Edited by GreenGrassIsGreen
Adding mc version
Posted

Alright so I had to dig into the mc/forge code a bit...

 

Pretty sure what's happening is that at some point in the process of dropping the item, ItemStack.split gets called which decreases the preexisting ItemStack's count by 1 (so in my case becomes 0, and shows as "Air" which is just the display name or something anyway...) - but the underlying Item property is still set to be my custom Relic item, and the stack retains it's preexisting capabilities.

 

So my title is wrong I think - stack isn't "given" my capability, it just never gets taken away as it's still the original stack. (To me the uneducated 2-day old modder, seems a bit janky that none of the code cleans things up when the stack becomes empty e.g. replacing it with a new blank ItemStack, but I'm sure there's a good reason for it :) )

 

For the time being to fix my immediate problem I've just tweaked my ClientEvents.onBlockDestroyed method to the following; can't do anything about the empty stack having the capability, so forced to just avoid utilizing it...

...
if (relicExperienceCapability != null && !i.isEmpty())
                       {
                           relicExperienceCapability.addExp(5);
                           MyMod.LOGGER.info("I just added 5 exp to a relic...");
                               MyMod.LOGGER.info(String.format("Current exp for this relic is: %d", relicExperienceCapability.getExp()));
                       }
...

 

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.