Jump to content

JoieNL

Members
  • Posts

    32
  • Joined

  • Last visited

Posts posted by JoieNL

  1. Heya, thanks for the swift reply. Changing the method return value to

    InteractionResult.sidedSuccess(level.isClientSide)

    actually resolved the issue somehow. Thanks for pointing out the flags in the Block class too, I was looking for those exactly. Not that I need them anymore, but at least I know where to look now.

  2. Hey y'all,

    I've been working on a simple farming mod for a while, and I ran into some trouble trying to implement a scythe tool. When the player right-clicks on a crop using the tool, I want that crop and the two adjacent crops (in the direction perpendicular to the player's horizontal view direction) to break. Currently, when I right-click a crop with a scythe, the clicked crop breaks normally but the adjacent crops stay behind as ghost blocks. All three crops do drop their respective loot, however. Interacting with the ghost blocks makes them vanish. Here's my relevant code in the ScytheItem class:

    Spoiler
    @Override
    public InteractionResult useOn(UseOnContext context) {
        Level level = context.getLevel();
        BlockPos pos = context.getClickedPos();
        if (!level.isClientSide && level.getBlockState(pos).getBlock() instanceof CropBlock && !(level.getBlockState(pos).getBlock() instanceof PlantedTrellisBlock)) {
            Player player = context.getPlayer();
            level.destroyBlock(pos, true, player);
            switch (context.getHorizontalDirection()) {
                case NORTH, SOUTH -> {
                    if (level.getBlockState(pos.east()).getBlock() instanceof CropBlock && !(level.getBlockState(pos.east()).getBlock() instanceof PlantedTrellisBlock)) {
                        level.destroyBlock(pos.east(), true, player);
                    }
                    if (level.getBlockState(pos.west()).getBlock() instanceof CropBlock && !(level.getBlockState(pos.west()).getBlock() instanceof PlantedTrellisBlock)) {
                        level.destroyBlock(pos.west(), true, player);
                    }
                }
                case EAST, WEST -> {
                    if (level.getBlockState(pos.north()).getBlock() instanceof CropBlock && !(level.getBlockState(pos.north()).getBlock() instanceof PlantedTrellisBlock)) {
                        level.destroyBlock(pos.north(), true, player);
                    }
                    if (level.getBlockState(pos.south()).getBlock() instanceof CropBlock && !(level.getBlockState(pos.south()).getBlock() instanceof PlantedTrellisBlock)) {
                        level.destroyBlock(pos.south(), true, player);
                    }
                }
            }
            context.getItemInHand().hurtAndBreak(1, player, (livingEntity) -> livingEntity.broadcastBreakEvent(context.getHand()));
        }
        return super.useOn(context);
    }

     

    My guess is that I should pass some (non-default) integer flag in all the calls to destroyBlock. I can't seem to find what each integer flag does though. Any help would be greatly appreciated!

  3. Hey guys,

    I've been working on my custom world type lately, and I ran into an issue again. My custom world type is based off of the default world type, but I would like to replace the default fluid used below sea level by compressed ice. I noticed that vanilla defines the default fluid using the

    DimensionSettings

    class. I would like to use my custom settings (say "FROZEN_OVERWORLD_SETTINGS") for the NoiseChunkGenerator constructor that I pass to the ForgeWorldType constructor:

    new ForgeWorldType((biomeRegistry, dimensionSettingsRegistry, seed) ->
        new NoiseChunkGenerator(new FrozenOverworldBiomeProvider(seed, false, false, biomeRegistry), seed, () -> FROZEN_OVERWORLD_SETTINGS)
    )

    The problem is: I can't instantiate DimensionSettings without using reflection. The constructor is private, the class is final, and there is no builder method. Using reflection seems to work when first creating a world and entering is as long as the client is left running. But after closing the game and rerunning it, I am unable to re-enter the world, instead getting the following error message:

    [13:07:56] [Render thread/ERROR] [minecraft/SaveFormat]: WorldGenSettings: Unknown registry key: aid:biome_source missed input: {"minecraft:overworld":{generator:{settings:"aid:frozen_overworld",seed:-2783071959927850081L,biome_source:{seed:-2783071959927850081L,large_biomes:0b,type:"aid:biome_source"},type:"minecraft:noise"},type:"minecraft:overworld"}}; Overworld settings missing

    I know this error message is referring to the registry key I use to register my biome provider codec in my FrozenOverworldBiomeProvider class:

    Registry.register(Registry.BIOME_PROVIDER_CODEC, new ResourceLocation(MOD_ID, "biome_source"), CODEC);

    Yet this line seems necessary, because I get this error message when creating a world without the codec being registered:

    [13:27:33] [Render thread/ERROR] [minecraft/Minecraft]: Error reading worldgen settings after loading data packs: Unknown registry element RecordCodec[UnitDecoder[joienl.world.FrozenOverworldBiomeProvider$$Lambda$4386/249483336@a859c5] * Field[seed: Long] * OptionalFieldCodec[legacy_biome_init_layer: Bool][flatXmapped] * Field[large_biomes: Bool][mapResult OrElse[false]] * RegistryLookupCodec[ResourceKey[minecraft:root / minecraft:worldgen/biome]]]
    [13:27:33] [Render thread/ERROR] [minecraft/ServerWorldInfo]: WorldGenSettings: Unknown registry element RecordCodec[UnitDecoder[joienl.world.FrozenOverworldBiomeProvider$$Lambda$4386/249483336@a859c5] * Field[seed: Long] * OptionalFieldCodec[legacy_biome_init_layer: Bool][flatXmapped] * Field[large_biomes: Bool][mapResult OrElse[false]] * RegistryLookupCodec[ResourceKey[minecraft:root / minecraft:worldgen/biome]]]

    I am unable to reload the world at all if I don't register the codec. I just don't know where to look anymore. Any help would be greatly appreciated.

  4. So it took me a few days, but I managed to figure things out myself. First of all, apparently

    RegistryObject<Biome>

    fields used with

    DeferredRegister

     cannot be referenced directly. When you want to reference your custom biomes, you need to get them from the biome registry itself. This solves the issues I had with the

    getNoiseBiome

    method. Secondly, the codec thing. All I had to do to get that working, was to register the codec in a static initialiser in my biome provider class:

    static {
        Registry.register(Registry.BIOME_PROVIDER_CODEC, new ResourceLocation(MOD_ID, "biome_source"), CODEC);
    }

    I hope this helps anyone trying this for themselves.

    • Like 1
  5. Hey y'all,

    I've been trying my hands at making a new world type lately. This works exactly as it should using the recently added class

    ForgeWorldType

    . Now, I want my custom world type to use a set of custom biomes, and this is where I run into trouble. I've copied the code in

    OverworldBiomeProvider

    and swapped the entries of the biomes field out for my custom biomes. However, the vanilla biomes spawn like in the Default world type when I generate a world using my custom world type. And for the record: yes, I am passing it into the constructor of ForgeWorldType. I have already located the issue: the

    getNoiseBiome

    method in my copied biome provider class uses a lookup registry that always contains the vanilla biomes. How would I go about correctly implementing this method, such that my custom biomes generate instead? And on the subject of implementing biome provider methods, how do I implement the

    getBiomeProviderCodec

    method?

    TL;DR: How do I implement BiomeProvider's abstract methods in my custom biome provider class?

  6. 1 hour ago, diesieben07 said:

    DId you configure the runData configuration in your build.gradle correctly? Are you actually running runData?

    Good thing this forum has people with functional brain cells to compensate for my lack thereof. I was running runClient instead of runData.

    I'll look into generating the bow model files once I find the time to. If I run into any problems along the way, I'll post them here.

  7. I can't believe I've never seen this Forge test library before, thanks for sharing! I'm still having trouble though. My model provider class listens to

    GatherDataEvent

    and the listener is registered to the mod event bus like in the Forge test, but it seems the event never fires. The model provider should spit some "Hello World"s into the console but it never does. Here's the relevant part of my model provider class:

    public class ModelGenerators {
    
        static {
            System.out.println("Hello World!");
        }
    
        @SubscribeEvent
        public static void gatherData(GatherDataEvent event) {
            System.out.println("Hello World!");
            DataGenerator generator = event.getGenerator();
            ExistingFileHelper helper = event.getExistingFileHelper();
    
            if (event.includeClient()) {
                generator.addProvider(new BowModelProvider(generator, helper));
            }
        }
    }

    In my main mod class I have added the following line:

    FMLJavaModLoadingContext.get().getModEventBus().addListener(ModelGenerators::gatherData);

    What's going wrong here?

  8. Hiya guys,

    I'm developing a mod that adds, among other things, custom bows and arrows to the game. The different arrows can have different damage output, knockback, velocity, or even special attributes. I would like the player to be able to see which arrow they're using when pulling a bow, as opposed to how vanilla handles things. That is, I want to be able to dynamically add another layer to my bow models depending on the arrow used, without having to write a .json file for every combination of bow, arrow, and pull phase.

    Now I know that

    ItemModelsProperties

    are a thing, but I feel like this is not the right approach. I'm pretty sure I'd have to add overrides for every new arrow type to every new bow type model if I used that. And if I were to add another arrow type, I'd have to change every bow model again. Seems like a lot of unnecessary work.

    I looked at vanilla's

    DynamicBucketModel

    and forge's

    ItemLayerModel

    , and these look promising. I'm just not sure how to go about making these bow models in code, and especially where to register them. Could anyone point me in the right direction or provide some relevant documentation/code? Thanks in advance!

  9. 10 minutes ago, diesieben07 said:

    Not true. Item#addInformation is called whenever the tooltip is displayed, every frame while it is on screen for example.

    You're right, I completely missed that because it's called twice during setup as well and it crashed before I could test stuff in-game. It was stupid of me to assume it wasn't called in-game. I got things working now with a simple nullity check on the world parameter. Thanks for clearing things up!

  10. Hey everyone,

    I've always been a fan of dynamic items that display useful information like the clock and compass, and I want to make my own take on the clock this time. Instead of having the texture update though, I would like it to display the current day time as a tooltip. Is this possible? From what I've gathered, tooltips are generated whenever an ItemStack is created, and then never touched again. Maybe I can do something with NBT? Hopefully someone can help me out, because I'm rather stuck.

  11. Thanks everyone! I got things working with ease thanks to your help. Here's what my ModItemGroup class looks like now for future reference:

    public class ModItemGroup extends ItemGroup {
    
        private Supplier<ItemStack> displayStack;
    
        public static final ModItemGroup ACCESSORIES = new ModItemGroup("accessories", () -> new ItemStack(ModItems.SHACKLE.get()));
    
        private ModItemGroup(String label, Supplier<ItemStack> displayStack) {
            super(label);
            this.displayStack = displayStack;
        }
    
        @Override
        public ItemStack createIcon() { return displayStack.get(); }
    }

     

  12. Hey y'all,

     

    I'm currently working on a mod that adds a bunch of equipable accessories to the game. I've put them all in their own creative tab, which works fine. The only thing I'm stuck on is the icon. I'd like it to be one of the accessories, but this creates a reference loop. I need to supply the custom ItemGroup to the Item when I register it:

    public static final RegistryObject<Item> SHACKLE = ITEMS.register("shackle", () ->
                new Item(new Item.Properties().group(ModItemGroup.ACCESSORIES)));

    But at the same time I need to supply the Item to the custom ItemGroup when I create it:

    public static final ItemGroup ACCESSORIES = new ItemGroup("accessories") {
            @Override
            public ItemStack createIcon() {
                return new ItemStack(ModItems.SHACKLE.get();
            }
        };

    How do I work around this issue? I've tried registering the Shackle without an ItemGroup first, then creating the ItemGroup, and then setting the Shackle's ItemGroup with SHACKLE.get().getCreativeTabs().add(), but this doesn't work either. I'm probably overlooking something really simple, but my brain has just been refusing to work lately. Thanks in advance for any help!

  13. Thanks for the quick reply.

    I had indeed already had a look at this post and read about the offsetting by (8, 8). But I don't think this is the problem. I believe that vanilla methods like MapGenStructure#generateStructure handle that:

    int i = (chunkCoord.x << 4) + 8;
    int j = (chunkCoord.z << 4) + 8;

    I think things are going wrong because pieces of the structure (and with pieces I mean StructureFrozenNetherBridgePieces.Pieces) are being generated in unloaded chunks when they should not.

    I believe that pieces that are awaiting generation are stored in StructureFrozenNetherBridgePieces.Start#pendingChildren and generated in MapGenFrozenNetherBridge.Start like such:

    List<StructureComponent> list = structurefrozennetherbridgepieces$start.pendingChildren;
    
    while (!list.isEmpty()) {
        int i = random.nextInt(list.size());
        StructureComponent structurecomponent = list.remove(i);
        structurecomponent.buildComponent(structurefrozennetherbridgepieces$start, this.components, random);
    }

    My hypothesis that somehow all pieces of the structure end up in this list when the first crossing is initialised and everything tries to generate at once. Does anyone know how this could be?

     

     

    Edit: I should've definitely checked this before I made this post... Printing the list after it is initialised reveals that it is always empty of all things:

    [13:37:16] [Server thread/INFO]: [STDOUT]: []

    Does this mean that all the structure pieces generate even before entering this list? It would explain why it remains empty. I will conduct further research regarding this finding.

     

     

    Edit again: I did some more testing and it turns out that this list was empty because things were passing null all over the place. Bottom line is: IntelliJ was trying to be a smartass, replacing this...

    return isAboveGround(structureboundingbox) && StructureComponent.findIntersecting(p_175882_0_, structureboundingbox) == null ? new StructureFrozenNetherBridgePieces.Straight(p_175882_6_, p_175882_1_, structureboundingbox, p_175882_5_) : null;

    ...with...

    return null;

    ...for every piece.

     

    Now that that's solved, the entire structure spawns, but the cascading worldgen lag remains. I'll continue my investigations.

     

     

    I keep having to edit this post: I've 'turned off' all world generation features for my dimension except fortresses, and the cascading worldgen lag is gone. This means that there has been something besides the fortresses causing lag as well. I will now turn all features on again one by one to see if I can determine which of them causes the lag.

     

    One last edit: All is well now! I figured out this line was the culprit:

    this.lavaTrapGen.generate(this.world, this.rand, blockpos.add(this.rand.nextInt(16), this.rand.nextInt(108) + 4, this.rand.nextInt(16)));

    So I added an (8, 8) offset and things are working fine now! Thanks again for the help jabelar.

  14. Hello fellow modders,

     

    Currently I am working on a mod that, among other things, adds a Frozen Nether dimension. In this dimension, I want to generate Frozen Nether Fortresses. These structures look just like the normal variants, except, well, frozen. To create these structures, I have copied code from vanilla's MapGenNetherBridge and StructureNetherBridgePieces classes, and just changed some blocks. The structure is used in ChunkGeneratorFrozenHell (which also takes after the vanilla version) and registered in my ModStructures class.

     

    Now for the issue. When I move around in the Frozen Nether, I experience extreme amounts of lag, and the structures only spawn one part in one chunk (always the Crossing). I've looked around already, and I know what causes this. The structure is trying to generate in unloaded chunks. This makes Minecraft load a large amount of chunks quickly, resulting in - as the console calls it - cascading worldgen lag. And because things cannot generate in unloaded chunks, only a single piece of the structure is actually generated.

     

    I may know the cause of my problems, but what I cannot find is a descriptive solution. People are saying to generate the structure as the chunks are loaded, but I have no idea how I would go about this. Any help would be greatly appreciated. For reference, the classes involved can be found on my GitHub (some of them are too big to post here directly): https://github.com/JoieNL/Winter-Is-Here.

     

    These classes are potentially important:

    StructureFrozenNetherBridgePieces (in /world/gen/structure/).

    MapGenFrozenNetherBridge (in /world/gen/structure).

    ModStructures (in /world/gen/structure).

    ChunkGeneratorFrozenHell (in /world/gen).

     

    Thanks in advance!

  15. Right, that was just a thing I left there for no reason. The casting is to RendererLivingEntity by the way, not ModelBase. Here's the preferred thing:

    ModelBase defaultModel = ((RendererLivingEntity) Minecraft.getMinecraft().getRenderManager().getEntityRenderObject(entity)).getMainModel();

    The weird cast was there because I was getting confused with both RendererLiving and Render being generic classes.

     

    Anyway, this is kind of besides the point of the thread, and I still haven't made any progress on the topic.

  16. A few days have passed now and I have made little progress. I have found a way to easily apply most important attributes that the model can have:

    @Override
        @SideOnly(Side.CLIENT)
        public ModelBiped getArmorModel(EntityLivingBase entity, ItemStack stack, int armorSlot) {
            [...]
            ModelElementalArmourIce model = ClientProxy.getElementalArmourModel(4 - armorSlot);
            ModelBase defaultModel = RendererLivingEntity.class.cast(Minecraft.getMinecraft().getRenderManager().getEntityRenderObject(entity)).getMainModel();
            model.setModelAttributes(defaultModel);
            return model;
        }

    This way of doing things does not look particularly beautiful and I would not be surprised if there was some better way of retrieving an entity's default model.

     

    Of course, my problem still stands. I have experimented some more with the armour, and have been able to pinpoint several issues that all relate to the entity wearing the armour.

    - On armour stands, the helmet's front always faces south and the arms move (idle animation).

    - For entities that have their arms sticking forward (e.g. zombies, skeletons), the armour's arms simply hang downwards.

    - Zombie villagers also have their heads sticking out the top of the helmet.

     

    I will persist in trying to find a solution. I hope someone may be able to help me out.

  17. Recently I've gotten into making custom models for my armour sets. I have used this tutorial to guide me. Now, the armour works mostly fine. The only thing that goes wrong is that the armour model does not change according to the entity wearing it. That is, it is always rendered small, the arms always move (even on armour stands) and the armour does not follow the player sneaking, holding an item, guarding with a sword, etc. I do realise that some of these things can be circumvented by changing certain fields in ModelBiped and ModelBase (e.g. isChild, isSneaking), but these fields do not cover every movement. How would I go about making the armour render correctly according to the attributes of the entity wearing it? Thanks in advance for any help!

     

    Here are the classes involved for reference:

     

    ModelElementalArmourIce.java:

    Spoiler
    
    public class ModelElementalArmourIce extends ModelBiped {
    
        public ModelElementalArmourIce(int slot, float scale) {
            super(scale, 0, 64, 64);
    
            ModelRenderer icyEyes = new ModelRenderer(this, 0, 32);
            icyEyes.addBox(-11F, -3F, 0F, 22, 8, 0);
            icyEyes.setRotationPoint(0F, -8F, -5.1F);
            icyEyes.setTextureSize(64, 64);
            icyEyes.mirror = true;
            setRotation(icyEyes, 0F, 0F);
            bipedHeadwear.addChild(icyEyes);
    
            ModelRenderer spike1 = new ModelRenderer(this, 48, 32);
            spike1.addBox(-0.5F, -0.5F, 0F, 1, 1, 4);
            spike1.setRotationPoint(1.5F, 2.5F, 2F);
            spike1.setTextureSize(64, 64);
            setRotation(spike1, 0.2617994F, 0.0872665F);
            bipedBody.addChild(spike1);
    
            ModelRenderer spike2 = new ModelRenderer(this, 48, 32);
            spike2.addBox(-0.5F, -0.5F, 0F, 1, 1, 4);
            spike2.setRotationPoint(-1.5F, 2.5F, 2F);
            spike2.setTextureSize(64, 64);
            spike2.mirror = true;
            setRotation(spike2, 0.2617994F, -0.0872665F);
            bipedBody.addChild(spike2);
    
            ModelRenderer spike3 = new ModelRenderer(this, 48, 32);
            spike3.addBox(-0.5F, -0.5F, 0F, 1, 1, 4);
            spike3.setRotationPoint(2F, 6F, 2F);
            spike3.setTextureSize(64, 64);
            setRotation(spike3, 0F, 0.1745329F);
            bipedBody.addChild(spike3);
    
            ModelRenderer spike4 = new ModelRenderer(this, 48, 32);
            spike4.addBox(-0.5F, -0.5F, 0F, 1, 1, 4);
            spike4.setRotationPoint(-2F, 6F, 2F);
            spike4.setTextureSize(64, 64);
            spike4.mirror = true;
            setRotation(spike4, 0F, -0.1745329F);
            bipedBody.addChild(spike4);
    
            ModelRenderer spike5 = new ModelRenderer(this, 48, 32);
            spike5.addBox(-0.5F, -0.5F, 0F, 1, 1, 4);
            spike5.setRotationPoint(1.5F, 9.5F, 2F);
            spike5.setTextureSize(64, 64);
            setRotation(spike5, -0.2617994F, 0.0872665F);
            bipedBody.addChild(spike5);
    
            ModelRenderer spike6 = new ModelRenderer(this, 48, 32);
            spike6.addBox(-0.5F, -0.5F, 0F, 1, 1, 4);
            spike6.setRotationPoint(-1.5F, 9.5F, 2F);
            spike6.setTextureSize(64, 64);
            spike6.mirror = true;
            setRotation(spike6, -0.2617994F, -0.0872665F);
            bipedBody.addChild(spike6);
    
            ModelRenderer clawRightFront = new ModelRenderer(this, 44, 32);
            clawRightFront.addBox(0F, 0F, -0.5F, 1, 5, 1);
            clawRightFront.setRotationPoint(-3F, 10F, -0.7F);
            clawRightFront.setTextureSize(64, 64);
            clawRightFront.mirror = true;
            setRotation(clawRightFront, 0F, 0F);
            bipedRightArm.addChild(clawRightFront);
    
            ModelRenderer clawRightBack = new ModelRenderer(this, 44, 32);
            clawRightBack.addBox(0F, 0F, -0.5F, 1, 5, 1);
            clawRightBack.setRotationPoint(-3F, 10F, 0.67F);
            clawRightBack.setTextureSize(64, 64);
            clawRightBack.mirror = true;
            setRotation(clawRightBack, 0F, 0F);
            bipedRightArm.addChild(clawRightBack);
    
            ModelRenderer clawLeftFront = new ModelRenderer(this, 44, 32);
            clawLeftFront.addBox(-1F, 0F, -0.5F, 1, 5, 1);
            clawLeftFront.setRotationPoint(3F, 10F, -0.7F);
            clawLeftFront.setTextureSize(64, 64);
            setRotation(clawLeftFront, 0F, 0F);
            bipedLeftArm.addChild(clawLeftFront);
    
            ModelRenderer clawLeftBack = new ModelRenderer(this, 44, 32);
            clawLeftBack.addBox(-1F, 0F, -0.5F, 1, 5, 1);
            clawLeftBack.setRotationPoint(3F, 10F, 0.7F);
            clawLeftBack.setTextureSize(64, 64);
            setRotation(clawLeftBack, 0F, 0F);
            bipedLeftArm.addChild(clawLeftBack);
    
            switch (slot) {
                case 0:
                    bipedHeadwear.showModel = true;
                    bipedHead.showModel = true;
                    bipedBody.showModel = false;
                    bipedLeftArm.showModel = false;
                    bipedRightArm.showModel = false;
                    bipedLeftLeg.showModel = false;
                    bipedRightLeg.showModel = false;
                    break;
                case 1:
                    bipedHeadwear.showModel = false;
                    bipedHead.showModel = false;
                    bipedBody.showModel = true;
                    bipedLeftArm.showModel = true;
                    bipedRightArm.showModel = true;
                    bipedLeftLeg.showModel = false;
                    bipedRightLeg.showModel = false;
                    break;
                case 2:
                    bipedHeadwear.showModel = false;
                    bipedHead.showModel = false;
                    bipedBody.showModel = true;
                    bipedLeftArm.showModel = false;
                    bipedRightArm.showModel = false;
                    bipedLeftLeg.showModel = true;
                    bipedRightLeg.showModel = true;
    
                    spike1.showModel = false;
                    spike2.showModel = false;
                    spike3.showModel = false;
                    spike4.showModel = false;
                    spike5.showModel = false;
                    spike6.showModel = false;
                    break;
                case 3:
                    bipedHeadwear.showModel = false;
                    bipedHead.showModel = false;
                    bipedBody.showModel = false;
                    bipedLeftArm.showModel = false;
                    bipedRightArm.showModel = false;
                    bipedLeftLeg.showModel = true;
                    bipedRightLeg.showModel = true;
                    break;
            }
        }
    
        public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5) {
            super.render(entity, f, f1, f2, f3, f4, f5);
            super.setRotationAngles(f, f1, f2, f3, f4, f5, entity);
        }
    
        private void setRotation(ModelRenderer model, float x, float y) {
            model.rotateAngleX = x;
            model.rotateAngleY = y;
            model.rotateAngleZ = 0F;
        }
    }

     

     

    ElementalArmourItem.java:

    Spoiler
    
    [unimportant contructors and the like]
    
    @Override
    @SideOnly(Side.CLIENT)
    public ModelBiped getArmorModel(EntityLivingBase entity, ItemStack stack, int armorSlot) {
      GL11.glPushMatrix();
      GL11.glEnable(GL11.GL_BLEND);
      GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
      GL11.glPopMatrix();
      ModelElementalArmourIce model = ClientProxy.getElementalArmourModel(4 - armorSlot);
      return model;
    }

     

     

    ClientProxy.java:

    Spoiler
    
    public class ClientProxy extends CommonProxy {
    
        private static ModelElementalArmourIce[] elementalArmour;
    
        [preInit]
    
        @Override
        public void init(FMLInitializationEvent e) {
            super.init(e);
            elementalArmour = new ModelElementalArmourIce[4];
            elementalArmour[0] = new ModelElementalArmourIce(0, 0.5F);
            elementalArmour[1] = new ModelElementalArmourIce(1, 1.0F);
            elementalArmour[2] = new ModelElementalArmourIce(2, 0.5F);
            elementalArmour[3] = new ModelElementalArmourIce(3, 0.5F);
            [other stuff]
        }
    
        [postInit]
    
        public static ModelElementalArmourIce getElementalArmourModel(int slot) {
            return elementalArmour[slot];
        }
    }

     

     

  18. Well, after a lot of trial and error, I've figured things out. I am now able to configure the specifics of a generated mob spawner, as well as the specifics of the entitiy it should spawn. All these values are contained within the MobSpawnerBaseLogic class. I created a class, MobSpawnerExtendedLogic, that extends that class in which I created methods for modifying all these values. The methods I created use Java's reflection system. The methods are static because I have to use them in a static context. The argument to pass in as spawnerLogic should be the logic of the spawner you are trying to modify. For a spawner positioned at BlockPos pos in World world, you would pass ((TileEntityMobSpawner) world.getTileEntity(pos))).getSpawnerBaseLogic(). Here are my MobSpawnerExtendedLogic class and an implementation of the methods within it:

     

    MobSpawnerExtendedLogic.class:

    Spoiler
    
    public abstract class MobSpawnerExtendedLogic extends MobSpawnerBaseLogic {
        /**
         *
         * @param mobID The {@code String} that represents the Entity the spawner should spawn.
         * @param initialSpawnDelay The delay (in ticks) between when the player first enters the spawner's range and the first spawn.
         * @param minDelay The minimum delay between two spawns.
         * @param maxDelay The maximum delay between two spawns.
         * @param spawnCount The amount of Entities spawned every spawn.
         * @param spawnRange The range (cuboid with height 3 and remaining sides of range) in which spawned Entities should be placed.
         * @param maxNearbyEntities If this or a greater number of Entities is within the spawner's spawn range, it will stop spawning Entities.
         * @param minPlayerDistance The minimum distance between a player and the spawner at which the spawner should activate.
         * @param spawnerLogic A reference to the spawner's logic.
         */
        public static void setSpawnerSpecifics(String mobID, int initialSpawnDelay, int minDelay, int maxDelay, int spawnCount, int spawnRange, int maxNearbyEntities, int minPlayerDistance, MobSpawnerBaseLogic spawnerLogic) {
            setMobID(mobID, spawnerLogic);
            setInitialSpawnDelay(initialSpawnDelay, spawnerLogic);
            setMinDelay(minDelay, spawnerLogic);
            setMaxDelay(maxDelay, spawnerLogic);
            setSpawnCount(spawnCount, spawnerLogic);
            setSpawnRange(spawnRange, spawnerLogic);
            setMaxNearbyEntities(maxNearbyEntities, spawnerLogic);
            setMinPlayerDistance(minPlayerDistance, spawnerLogic);
        }
    
        /**
         *
         * @param entity The entity to spawn.
         * @param initialSpawnDelay The delay (in ticks) between when the player first enters the spawner's range and the first spawn.
         * @param minDelay The minimum delay between two spawns.
         * @param maxDelay The maximum delay between two spawns.
         * @param spawnCount The amount of Entities spawned every spawn.
         * @param spawnRange The range (cuboid with height 3 and remaining sides of range) in which spawned Entities should be placed.
         * @param maxNearbyEntities If this or a greater number of Entities is within the spawner's spawn range, it will stop spawning Entities.
         * @param minPlayerDistance The minimum distance between a player and the spawner at which the spawner should activate.
         * @param spawnerLogic A reference to the spawner's logic.
         */
        public static void setSpawnerSpecifics(EntityLiving entity, int initialSpawnDelay, int minDelay, int maxDelay, int spawnCount, int spawnRange, int maxNearbyEntities, int minPlayerDistance, MobSpawnerBaseLogic spawnerLogic) {
            NBTTagCompound compound = new NBTTagCompound();
            entity.writeEntityToNBT(compound);
            setNBT(entity.getName(), compound, spawnerLogic);
            setInitialSpawnDelay(initialSpawnDelay, spawnerLogic);
            setMinDelay(minDelay, spawnerLogic);
            setMaxDelay(maxDelay, spawnerLogic);
            setSpawnCount(spawnCount, spawnerLogic);
            setSpawnRange(spawnRange, spawnerLogic);
            setMaxNearbyEntities(maxNearbyEntities, spawnerLogic);
            setMinPlayerDistance(minPlayerDistance, spawnerLogic);
        }
    
        private static void setMobID(String id, MobSpawnerBaseLogic obj) {
            try {
                Field field = MobSpawnerBaseLogic.class.getDeclaredField("mobID");
                field.setAccessible(true);
                field.set(obj, id);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    
        private static void setInitialSpawnDelay(int delay, MobSpawnerBaseLogic obj) {
            try {
                Field field = MobSpawnerBaseLogic.class.getDeclaredField("spawnDelay");
                field.setAccessible(true);
                field.set(obj, delay);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    
        private static void setMinDelay(int delay, MobSpawnerBaseLogic obj) {
            try {
                Field field = MobSpawnerBaseLogic.class.getDeclaredField("minSpawnDelay");
                field.setAccessible(true);
                field.set(obj, delay);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    
        private static void setMaxDelay(int delay, MobSpawnerBaseLogic obj) {
            try {
                Field field = MobSpawnerBaseLogic.class.getDeclaredField("maxSpawnDelay");
                field.setAccessible(true);
                field.set(obj, delay);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    
        private static void setSpawnCount(int count, MobSpawnerBaseLogic obj) {
            try {
                Field field = MobSpawnerBaseLogic.class.getDeclaredField("spawnCount");
                field.setAccessible(true);
                field.set(obj, count);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    
        private static void setSpawnRange(int range, MobSpawnerBaseLogic obj) {
            try {
                Field field = MobSpawnerBaseLogic.class.getDeclaredField("spawnRange");
                field.setAccessible(true);
                field.set(obj, range);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    
        private static void setMaxNearbyEntities(int count, MobSpawnerBaseLogic obj) {
            try {
                Field field = MobSpawnerBaseLogic.class.getDeclaredField("maxNearbyEntities");
                field.setAccessible(true);
                field.set(obj, count);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    
        private static void setMinPlayerDistance(int distance, MobSpawnerBaseLogic obj) {
            try {
                Field field = MobSpawnerBaseLogic.class.getDeclaredField("activatingRangeFromPlayer");
                field.setAccessible(true);
                field.set(obj, distance);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    
        @SuppressWarnings("JavaReflectionMemberAccess")
        private static void setNBT(String mobID, NBTTagCompound compound, MobSpawnerBaseLogic obj) {
            try {
                Constructor constructor = MobSpawnerBaseLogic.WeightedRandomMinecart.class.getDeclaredConstructor(MobSpawnerBaseLogic.class, NBTTagCompound.class, String.class);
                Field randomEntity = MobSpawnerBaseLogic.class.getDeclaredField("randomEntity");
                randomEntity.setAccessible(true);
                randomEntity.set(obj, constructor.newInstance(obj, compound, mobID));
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    }

     

     

    An implementation of the methods:

    Spoiler
    
    EntityZombie zombie1 = new EntityZombie(world);
    applyRandomTierFourArmour(zombie1);
    for (int i = 0; i < 5; i++) {
        zombie1.setEquipmentDropChance(i, 0);
    }
    zombie1.addPotionEffect(new PotionEffect(Potion.moveSpeed.getId(), 1000000, 0));
    world.setBlockState(origin.add(6, 1, 6), Blocks.mob_spawner.getDefaultState());
    setSpawnerSpecifics(zombie1, 0, 40, 100, 1, 16, 8, 16, ((TileEntityMobSpawner) world.getTileEntity(origin.add(6, 1, 6))).getSpawnerBaseLogic());
    
    world.setBlockState(origin.add(11, 18, 11), Blocks.mob_spawner.getDefaultState());
    setSpawnerSpecifics("em.ice_cube", 0, 100, 200, 3, 10, 8, 11, ((TileEntityMobSpawner) world.getTileEntity(origin.add(11, 18, 11))).getSpawnerBaseLogic());

     

     

  19. 1 minute ago, diesieben07 said:

    This does not make sense, the NBT is for an entity, not for ItemStacks.

    Perhaps I should have been a bit more elaborate. The array of ItemStacks would be the equipment the Entity must have.

     

    As for my current code...

    MobSpawnerExtendedLogic.class:

    Spoiler
    
    public static void setNBT(Object object) {
        NBTTagList tagList = new NBTTagList();
        ItemStack[] equipment = new ItemStack[] {new ItemStack(ModItems.osmiumSword), new ItemStack(ModItems.osmiumBoots), new ItemStack(ModItems.osmiumLeggings), new ItemStack(ModItems.osmiumChestplate), new ItemStack(ModItems.osmiumHelmet)};
        for (ItemStack stack : equipment) {
            NBTTagCompound nbttagcompound = new NBTTagCompound();
            stack.writeToNBT(nbttagcompound);
            tagList.appendTag(nbttagcompound);
        }
        NBTTagCompound tag = new NBTTagCompound();
        tag.setTag("Equipment", tagList);
        NBTTagCompound tagCompound = new NBTTagCompound();
        tagCompound.setTag("Properties", tag);
        try {
            Field field = MobSpawnerBaseLogic.WeightedRandomMinecart.class.getDeclaredField("nbtData");
            field.setAccessible(true);
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
            Field randomEntity = MobSpawnerBaseLogic.class.getDeclaredField("randomEntity");
            randomEntity.setAccessible(true);
            Object obj = randomEntity.get(object);
            field.set(obj, tagCompound);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

     

     

    WaterTower.class:

    world.setBlockState(origin.add(11, 1, 11), Blocks.mob_spawner.getDefaultState());
    TileEntityMobSpawner spawner = (TileEntityMobSpawner) world.getTileEntity(origin.add(11, 1, 11));
    setNBT(spawner.getSpawnerBaseLogic());

     

  20. I have figured out how to edit the mob spawner's characteristics through the use of reflection. Now, I have been trying to configure the spawned entity's characteristics by using reflection to edit the NBTTagCompound "nbtData" in the inner class WeightedRandomMinecart in MobSpawnerBaseLogic. After many fruitless attempts, I just don't know what to try anymore. How do I go about setting "nbtData" while using the right arguments in Field::set(Object obj, Object value) and how do I convert (an array of) ItemStacks to NBT that MobSpawnerBaseLogic can read?

×
×
  • Create New...

Important Information

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