eafooe
Members-
Posts
15 -
Joined
-
Last visited
Recent Profile Visitors
The recent visitors block is disabled and is not being shown to other users.
eafooe's Achievements
Tree Puncher (2/8)
2
Reputation
-
[1.16] What to use in lieu of 'setRotationAngles'?
eafooe replied to eafooe's topic in Modder Support
Awesome, thank you very much! -
I am trying to update my mod from 1.14.x to 1.16.5 and have experienced no difficulties except for the (seemingly?) absence of this method. I have a nose item that can be equipped by players in the head slot. Depending on the player's proximity to emerald ore, the item will start moving up and down on the player's face (i.e., the nose starts to wiggle). Previously, I just overrode the setRotationAngles method to make the nose wiggle, however, it looks like this method is no longer around and I can't find any documentation on the best programming practices to re-implement this feature. Any insight would be helpful. This is my code for 1.14: @OnlyIn(Dist.CLIENT) public class ModelNose extends BipedModel<PlayerEntity> { private final RendererModel bipedNose; public ModelNose() { this(0.0F, 0.0F, 8, 6); } private ModelNose(float scaleFactor, float rotationY, int textureWidth, int textureHeight) { super(scaleFactor, rotationY, 0, 0); bipedNose = new RendererModel(this); bipedNose.setTextureSize(textureWidth, textureHeight); bipedNose.addBox(-1.0F, -1.0F, -6.0F, 2, 4, 2, scaleFactor); bipedNose.setRotationPoint(0.0F, rotationY - 2.0F, 0.0F); bipedHead.addChild(bipedNose); } // If emerald ore is nearby, 'wiggle' the model by allowing its y-offset to bob up and down @Override public void setRotationAngles(PlayerEntity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch, float scale) { super.setRotationAngles(entity, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scale); // Trigonometry boo-yeah if (ItemNose.emeraldsAreNearby(entity.world, (int) entity.posX, (int) entity.posY, (int) entity.posZ)) { bipedNose.offsetY = MathHelper.cos((ageInTicks % 10) * (0.2F) * ((float) Math.PI)) * 0.03125F; } } }
-
-
Aren't they equivalent?
-
Is this what you mean? @Nonnull @Override public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) { if (cap == NOSE_CAP){ return holder.cast(); } return LazyOptional.empty(); }
-
Like this? public class NoseProvider implements ICapabilitySerializable<INBT> { public static final ResourceLocation IDENTIFIER = new ResourceLocation(MODID, "capability_nose"); @CapabilityInject(INose.class) public static Capability<INose> NOSE_CAP = null; private INose instance = NOSE_CAP.getDefaultInstance(); private final LazyOptional<INose> holder = LazyOptional.of(() -> instance); @Override public INBT serializeNBT() { VillagersNose.LOGGER.info("Serializing NBT..."); return NOSE_CAP.getStorage().writeNBT(NOSE_CAP, instance, null); } @Override public void deserializeNBT(INBT nbt) { VillagersNose.LOGGER.info("Deserializing NBT..."); NOSE_CAP.getStorage().readNBT(NOSE_CAP, instance, null, nbt); } @Nonnull @Override public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) { return NOSE_CAP.orEmpty(cap, holder); } }
-
Hi all, I have made a capability (see spoiler) that I attach to vanilla VillagerEntity instances during the AttachCapabilitiesEvent<Entity>: @SubscribeEvent public static void attachEntityCapabilities(AttachCapabilitiesEvent<Entity> event){ if (event.getObject() instanceof VillagerEntity){ event.addCapability(NoseProvider.IDENTIFIER, new NoseProvider()); } } Currently, the capability has one boolean, 'hasNose' which allows me to know whether the VillagerEntity instance has a nose, or whether it was removed by the player. If 'hasNose' is set to false, the VillagerEntity instance should render without a nose. I believe I need to send a packet from the server and to the client with this information during the events specified in the following quote?: I have a packet class set up, but I'm not sure what I should put in the handle method to make sure the client gets the data that it needs. public class ClientPacket { private int entityId; public ClientPacket(int entityId){ this.entityId = entityId; } static void encode(ClientPacket msg, PacketBuffer buffer){ buffer.writeInt(msg.entityId); VillagersNose.LOGGER.info("Wrote entityId: " + buffer.readInt()); } static ClientPacket decode(PacketBuffer buffer){ int entityId = buffer.readInt(); VillagersNose.LOGGER.info("Read entityId: " + entityId); return new ClientPacket(entityId); } // Send from server to client static void handle(ClientPacket msg, Supplier<NetworkEvent.Context> ctx){ ctx.get().enqueueWork(() -> { // ? // ? I've tried all sorts of things here but nothing seems to work // ? }); ctx.get().setPacketHandled(true); } } In addition to the handle method, I want to ensure that I am sending my packet correctly. Is the following code what I should be doing? // Send to all players tracking entity @SubscribeEvent public static void entityJoinWorldEvent(EntityJoinWorldEvent event){ Entity entity = event.getEntity(); if (entity instanceof VillagerEntity && !event.getWorld().isRemote){ PacketDistributor.PacketTarget dest = PacketDistributor.TRACKING_ENTITY.with(() -> entity); PacketHandler.INSTANCE.send(dest, new ClientPacket(entity.getEntityId())); } } I know this is kind of a lengthy question, so thank you for taking your time to help me out.
-
[1.14.3] Check VillagerEntity attributes before opening trade screen
eafooe replied to eafooe's topic in Modder Support
Bump. -
Hello, I am trying to prevent a player from opening the trade screen with a villager if they have previously removed the villager's nose with a pair of shears. The trade screen should never open, and the player should be told "Leave me alone! I miss my nose". I have stored whether the villager has a nose by attaching a capability to all instances of VillagerEntity. But I am having difficulty obtaining the VillagerEntity instance before I open the trade screen. I tried casting the IMerchant from the MerchantContainer to an AbstractVillagerEntity (which implements IMerchant) as well as VillagerEntity, but all I get are ClassCastExceptions. I must be overlooking something. This is what I have so far (throws ClassCastException): @SubscribeEvent public static void guiOpenEvent(GuiScreenEvent event){ if (event.getGui() instanceof MerchantScreen){ IMerchant merchant = ObfuscationReflectionHelper.getPrivateValue(MerchantContainer.class, ((MerchantScreen) event.getGui()).getContainer(), "merchant"); if (merchant instanceof VillagerEntity){ VillagerEntity villager = (VillagerEntity) merchant; INose noseCapability = villager.getCapability(NOSE_CAP).orElseThrow(() -> new RuntimeException("No inventory!")); if (!noseCapability.getHasNose()){ event.setCanceled(true); event.getGui().getMinecraft().player.sendChatMessage(String.valueOf(new TranslationTextComponent("translation.villagersnose.trade_refusal", (Object) null))); } } } } How can I determine which VillagerEntity instance the player is attempting to trade with?
-
Hi, I am creating a custom entity that has two inventory slots, much like a vanilla MC horse. I was told in a previous post: I have attempted using ItemStackHandler, which implements IItemHandler, but am experiencing difficulty accessing the contents of my inventory. Here is a small rundown of the issue: Suppose I enter my world and equip my entity with a saddle and diamond horse armor. The first slot (slot #0) holds the saddle and the second slot (slot #1) holds the armor. When I save and quit my world, this is written to my log: [09:48:16.477] [Server thread/INFO] [minecraft/MinecraftServer]: Stopping server [09:48:16.478] [Server thread/INFO] [minecraft/MinecraftServer]: Saving players [09:48:16.478] [Server thread/INFO] [minecraft/MinecraftServer]: Saving worlds [09:48:16.478] [Server thread/INFO] [minecraft/MinecraftServer]: Saving chunks for level 'New World'/minecraft:overworld [09:48:16.485] [Server thread/INFO] [co.em.wh.WhenPigsFly/]: Wrote inventory: [09:48:16.485] [Server thread/INFO] [co.em.wh.WhenPigsFly/]: [0]: minecraft:saddle [09:48:16.485] [Server thread/INFO] [co.em.wh.WhenPigsFly/]: [1]: minecraft:diamond_horse_armor When I reenter this same world, this is written to my log: [09:49:33.820] [Server thread/INFO] [minecraft/MinecraftServer]: Preparing start region for dimension minecraft:overworld [09:49:34.129] [Client thread/INFO] [minecraft/LoggingChunkStatusListener]: Preparing spawn area: 0% [09:49:34.247] [Server thread/INFO] [co.em.wh.WhenPigsFly/]: Found inventory: [09:49:34.247] [Server thread/INFO] [co.em.wh.WhenPigsFly/]: [0]: minecraft:saddle [09:49:34.247] [Server thread/INFO] [co.em.wh.WhenPigsFly/]: [1]: minecraft:diamond_horse_armor [09:49:34.563] [Client thread/INFO] [minecraft/LoggingChunkStatusListener]: Preparing spawn area: 91% [09:49:34.831] [Client thread/INFO] [minecraft/LoggingChunkStatusListener]: Preparing spawn area: 93% Surely, I thought, my inventory is being written and read correctly, but alas, !chest.getStackInSlot(0).isEmpty(); !getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null).orElseThrow(NullPointerException::new).getStackInSlot(0).isEmpty(); always return false, indicating that my entity is not saddled. When I call WhenPigsFly.LOGGER.info(chest.getStackInSlot(0).getItem().getRegistryName()); I get minecraft:air. Can anyone enlighten me on what I could be doing wrong? I have attached my entity's code below: public abstract class AbstractRideableEntity extends AnimalEntity { private static List<Item> saddleItems = SaddleItems.getSaddleItems(); private static List<Item> armorItems = ArmorItems.getArmorItems(); private ItemStackHandler chest; // Constructor protected AbstractRideableEntity(EntityType<? extends AbstractRideableEntity> type, World world) { super(type, world); chest = new ItemStackHandler(2){ @Override public boolean isItemValid(int slot, @Nonnull ItemStack itemStack) { if (slot == 0) { return saddleItems.contains(itemStack.getItem()); } else if (slot == 1) { return armorItems.contains(itemStack.getItem()); } else { throw new Error("Unsupported slot"); } } @Override public int getSlotLimit(int slot) { return 1; } }; } // Attributes protected void registerAttributes() { super.registerAttributes(); getAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(53.0D); getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue((double) 0.225F); } // Read chest from NBT @Override public void readAdditional(CompoundNBT compound) { super.readAdditional(compound); if (compound.contains("Chest")){ chest.deserializeNBT(compound.getCompound("Chest")); } WhenPigsFly.LOGGER.info("Found inventory: "); WhenPigsFly.LOGGER.info("[0]: " + chest.getStackInSlot(0).getItem().getRegistryName()); WhenPigsFly.LOGGER.info("[1]: " + chest.getStackInSlot(1).getItem().getRegistryName()); } // Write chest to NBT @Override public void writeAdditional(CompoundNBT compound) { super.writeAdditional(compound); compound.put("Chest", chest.serializeNBT()); WhenPigsFly.LOGGER.info("Wrote inventory: "); WhenPigsFly.LOGGER.info("[0]: " + chest.getStackInSlot(0).getItem().getRegistryName()); WhenPigsFly.LOGGER.info("[1]: " + chest.getStackInSlot(1).getItem().getRegistryName()); } // Get capability @Override @Nonnull public <T> LazyOptional<T> getCapability(@Nullable Capability<T> capability, @Nullable Direction facing) { if (isAlive() && capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) return LazyOptional.of(() -> chest).cast(); return super.getCapability(capability, facing); } public boolean isSaddled() { WhenPigsFly.LOGGER.info("Saddled: " + chest.getStackInSlot(0).getItem().getRegistryName()); return !chest.getStackInSlot(0).isEmpty(); } public boolean isArmored() { WhenPigsFly.LOGGER.info("Armored: " + !chest.getStackInSlot(1).isEmpty()); return !chest.getStackInSlot(0).isEmpty(); } private void equipSaddle(ItemStack saddle) { if (saddleItems.contains(saddle.getItem())) { chest.insertItem(0, saddle, false); } else { throw new Error("Attempted to saddle pig with unsupported item."); } } private void equipArmor(ItemStack armor) { if (armorItems.contains(armor.getItem())) { chest.insertItem(1, armor, false); } else { throw new Error("Attempted to armor pig with unsupported item."); } } public Item getArmor() { if (isArmored()) { return getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null).orElseThrow(NullPointerException::new).getStackInSlot(1).getItem(); } else { return null; } } private void openGUI(PlayerEntity playerEntity) { int id = getEntityId(); if (!world.isRemote) { WhenPigsFly.LOGGER.info("Attempting to open container..."); NetworkHooks.openGui((ServerPlayerEntity) playerEntity, new INamedContainerProvider() { @Override @Nonnull public ITextComponent getDisplayName() { return new StringTextComponent(Integer.toString(id)); } @Override public Container createMenu(int i, @Nonnull PlayerInventory playerInventory, @Nonnull PlayerEntity playerEntity) { WhenPigsFly.LOGGER.info("Creating menu..."); return new MyContainer(i, playerInventory, id); } }, buf -> buf.writeInt(id)); } else { WhenPigsFly.LOGGER.info("Cannot open GUI; world is remote."); } } public boolean canBeLeashedTo(PlayerEntity player) { return super.canBeLeashedTo(player) && getCreatureAttribute() != CreatureAttribute.UNDEAD; } public boolean canBePushed() { return !isBeingRidden(); } public boolean processInteract(PlayerEntity player, @Nonnull Hand hand) { // Get held item ItemStack itemstack = player.getHeldItem(hand); // Do nothing if (!itemstack.isEmpty() && itemstack.getItem() instanceof SpawnEggItem || !isChild() && isBeingRidden()) { WhenPigsFly.LOGGER.info("Nothing happened."); return super.processInteract(player, hand); } // Open GUI if (!isChild() && player.isSneaking()) { WhenPigsFly.LOGGER.info("Opened GUI."); openGUI(player); return true; } // Apply entity-item interaction if (!itemstack.isEmpty() && itemstack.interactWithEntity(player, this, hand)) { WhenPigsFly.LOGGER.info("Some other interaction"); return true; } // Equip saddle if (!itemstack.isEmpty() && !isChild() && !isSaddled() && saddleItems.contains(itemstack.getItem())) { WhenPigsFly.LOGGER.info("Equipped saddle."); equipSaddle(itemstack); if (!player.isCreative()) { itemstack.shrink(1); } return true; } // Equip armor if (!itemstack.isEmpty() && !isChild() && !isArmored() && armorItems.contains(itemstack.getItem())) { WhenPigsFly.LOGGER.info("Equipped armor."); equipArmor(itemstack); if (!player.isCreative()) { itemstack.shrink(1); } return true; } // Attempt to age up child entity if (isChild()) { WhenPigsFly.LOGGER.info("Interacted with child entity."); return super.processInteract(player, hand); } // Start riding on the pig else { WhenPigsFly.LOGGER.info("Mounted entity."); mountTo(player); return false; } } private void mountTo(PlayerEntity player) { if (!world.isRemote) { player.rotationYaw = rotationYaw; player.rotationPitch = rotationPitch; player.startRiding(this); } } // No babby public AgeableEntity createChild(@Nullable AgeableEntity entity) { return null; } }
-
Thank you very much; this is just what I needed. My GUI is now opening when I require it to ?
-
I've modified my code (see above) so that I am getting the entity using its integer ID. However, now I am getting java.lang.UnsupportedOperationException: Unable to construct this menu by type whenever I hit this line: super(TYPE, id); in the MyContainer constructor. I've still yet to read up on IInventory and IItemHandler.
-
Here is my thought process: All Containers need a constructor with these parameters: MyContainer(int id, PlayerInventory playerInventory, PacketBuffer extraData){ // stuff goes here } If you want to right-click a block to pull up a GUI, you need to know the block's position. However, you still need a constructor with the parameters defined above. So, you create two constructors, one like above, and one with the parameters you actually need: public class MyContainer extends Container{ public static ContainerType<MyContainer> TYPE; public MyContainer(int id, PlayerInventory playerInventory, PacketBuffer extraData){ this(id, extraData.readBlockPos(), playerInventory); } public MyContainer(int id, BlockPos blockPos, PlayerInventory playerInventory){ super(TYPE, id); // more stuff here } } You can easily access the block's position using the pre-defined readBlockPos() method. But I don't need to know a block's position; I am interacting with an entity, not a block. Thus, I need know the id of the entity the player has interacted with and I need to access its Inventory (which can contain a saddle and armor). Right now, my actual constructor looks like this: public MyContainer(int windowId, PlayerInventory playerInventory, IInventory chest, final AbstractRideableEntity pig){ super(TYPE, windowId); // more stuff here } Somehow I need to be able to get my entity and its chest using data from the PacketBuffer parameter. I suppose my question is how can I store an IInventory and AbstractRideableEntity object then retrieve it using a PacketBuffer?
-
I have read the source code for AbstractHorseEntity and have looked for other 1.13+ examples, but am still experiencing difficulties. Unlike tile entities, AbstractHorseEntity uses null for its ContainerType, which I can not get to work in forge (forge will throw an error if you say your ContainerType is null). According to this post, horse inventory handling is a special case. Right now, these are my classes: Container: public class MyContainer extends Container { // Type of container public static ContainerType<MyContainer> TYPE; // Inventory of flying pig entity private final Inventory chest; // Instance of flying pig entity private final AbstractRideableEntity pig; // Lists of acceptable saddle/armor items private final LinkedList<Item> saddleItems = getSaddleItems(); private final LinkedList<Item> armorItems = getArmorItems(); public MyContainer(int windowId, PlayerInventory playerInventory, PacketBuffer extraData){ this(windowId, playerInventory, extraData.readInt()); } public MyContainer(int windowId, PlayerInventory playerInventory, int entityId){ super(TYPE, windowId); this.pig = (AbstractRideableEntity) playerInventory.player.world.getEntityByID(entityId); this.chest = pig.chest; chest.openInventory(playerInventory.player); this.addSlot(new Slot(chest, 0, 8, 18) { public boolean isItemValid(ItemStack itemStack) { return saddleItems.contains(itemStack.getItem()); } @OnlyIn(Dist.CLIENT) public boolean isEnabled() { return true; } public int getSlotStackLimit() { return 1; } }); this.addSlot(new Slot(chest, 1, 8, 36) { public boolean isItemValid(ItemStack itemStack) { return armorItems.contains(itemStack.getItem()); } @OnlyIn(Dist.CLIENT) public boolean isEnabled() { return true; } public int getSlotStackLimit() { return 1; } }); int i; int j; for(i = 0; i < 3; ++i) { for(j = 0; j < 9; ++j) { this.addSlot(new Slot(playerInventory, j + i * 9 + 9, 8 + j * 18, 102 + i * 18 + -18)); } } for(i = 0; i < 9; ++i) { this.addSlot(new Slot(playerInventory, i, 8 + i * 18, 142)); } } public boolean canInteractWith(PlayerEntity playerEntity){ return this.chest.isUsableByPlayer(playerEntity) && this.pig.isAlive() && this.pig.getDistance(playerEntity) < 8.0F; } public void onContainerClosed(PlayerEntity playerEntity){ super.onContainerClosed(playerEntity); this.chest.closeInventory(playerEntity); } // List of acceptable saddle items private static LinkedList<Item> getSaddleItems(){ return new LinkedList<>(Arrays.asList( Items.SADDLE )); } // List of acceptable armor items private static LinkedList<Item> getArmorItems(){ return new LinkedList<>(Arrays.asList( Items.LEATHER_HORSE_ARMOR, Items.IRON_HORSE_ARMOR, Items.GOLDEN_HORSE_ARMOR, Items.DIAMOND_HORSE_ARMOR )); } } Screen for Container: public class MyContainerScreen extends ContainerScreen<MyContainer> { private static final ResourceLocation GUI_TEXTURES = new ResourceLocation(WhenPigsFly.MODID, "textures/gui/container/flying_pig.png"); private final AbstractRideableEntity myEntity; private float mousePosX; private float mousePosY; // All container-based GUI's must provide a constructor taking(T, PlayerInventory, ITextComponent), where the generic T is the type of your container object. public MyContainerScreen(MyContainer inventory, PlayerInventory playerInventory, ITextComponent text){ super(inventory, playerInventory, playerInventory.player.getRidingEntity().getDisplayName()); this.myEntity = (AbstractRideableEntity)playerInventory.player.getRidingEntity(); this.passEvents = false; } protected void drawGuiContainerForegroundLayer(int a, int b) { this.font.drawString(this.title.getFormattedText(), 8.0F, 6.0F, 4210752); this.font.drawString(this.playerInventory.getDisplayName().getFormattedText(), 8.0F, (float)(this.ySize - 96 + 2), 4210752); } protected void drawGuiContainerBackgroundLayer(float a, int b, int c) { GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F); this.minecraft.getTextureManager().bindTexture(GUI_TEXTURES); int lvt_4_1_ = (this.width - this.xSize) / 2; int lvt_5_1_ = (this.height - this.ySize) / 2; this.blit(lvt_4_1_, lvt_5_1_, 0, 0, this.xSize, this.ySize); this.blit(lvt_4_1_ + 7, lvt_5_1_ + 35 - 18, 18, this.ySize + 54, 18, 18); this.blit(lvt_4_1_ + 7, lvt_5_1_ + 35, 0, this.ySize + 54, 18, 18); InventoryScreen.drawEntityOnScreen(lvt_4_1_ + 51, lvt_5_1_ + 60, 17, (float)(lvt_4_1_ + 51) - this.mousePosX, (float)(lvt_5_1_ + 75 - 50) - this.mousePosY, this.myEntity); } public void render(int posX, int posY, float p_render_3_) { this.renderBackground(); this.mousePosX = (float)posX; this.mousePosY = (float)posY; super.render(posX, posY, p_render_3_); this.renderHoveredToolTip(posX, posY); } } Main Class: public void registerContainers(RegistryEvent.Register<ContainerType<?>> event){ event.getRegistry().register(IForgeContainerType.create(MyContainer::new).setRegistryName(new ResourceLocation(WhenPigsFly.MODID, "my_container"))); } private void clientRegistries(final FMLClientSetupEvent event){ RenderingRegistry.registerEntityRenderingHandler(EntityFlyingPig.class, RenderFlyingPig::new); ScreenManager.registerFactory(MyContainer.TYPE, MyContainerScreen::new); LOGGER.info("Client setup method registered."); } Usage in AbstractRideableEntity: public void openGUI(PlayerEntity playerEntity){ int id = this.getEntityId(); if (!this.world.isRemote){ WhenPigsFly.LOGGER.info("Attempting to open container..."); NetworkHooks.openGui((ServerPlayerEntity) playerEntity, new INamedContainerProvider() { @Override public ITextComponent getDisplayName() { return null; } @Nullable @Override public Container createMenu(int i, PlayerInventory playerInventory, PlayerEntity playerEntity) { WhenPigsFly.LOGGER.info("Creating menu..."); return new MyContainer(i, playerInventory, id); } }, buf -> buf.writeInt(id)); } } Usually, I'd look at open-source mods that have similar functionality, but it looks like there is not much around for the time being.
-
Hi, I have a custom entity with an Inventory that can hold a saddle and armor, much like a vanilla horse. I would like a GUI to open when a player right-clicks on my entity while sneaking. However, I do not want my custom entity to extend the vanilla AbstractHorseEntity class. All of the tutorials I have found for GUIs in 1.13+, however, use a TileEntity. Would anybody be so kind as to explain how this could be accomplished?