-
Posts
867 -
Joined
-
Last visited
-
Days Won
3
Everything posted by JimiIT92
-
I made a custom Sword Item that should be always pre-enchanted with a specific enchant set via code. Right now I am using the Item#inventoryTick method to show the enchantment (kinda how Enchanted Books does). However this means that every tick the Word tries to get enchanted (of course it fails after the first tick where is actually enchanted), so I guess is not ideal. So what would it be an optimal way to have an enchanted Item in the Creative Inventory where the enchant is not applied every tick?
-
I achieved a similiar result overriding the Item#onCraftedBy method inside my Item class. Essentially I have a sword that should always be enchanted with FireAspect X. I made a recipe that if you upgrade a Netherite Sword with a mod material you get this sword, regardless of the existing sword enchantments. By using the onCraftedBy method, as soon as the Player gets the Item, this is enchanted with FireAspect X. Is not ideal, since you won't actually see the applied enchantment until you get out of the smithing table, but is way easier than implementing a custom recipe serializer (which is not hard, but sure is way more work than this)
-
EDIT: After some digging into the Forum I found out what was wrong. Codewise essentially nothing, except for the fact that I'm returning a new instance every time .The issue was in the JSON file. I've now ported the "spear_in_hand.json" content into the "spear.json" file and I now see the Spear in 3D like the Trident. However now I have the issue that is 3D in the inventory as well How can I render a different texture in Inventory? I tried this { "parent": "minecraft:builtin/entity", "gui_light": "front", "textures": { "particle": "blazersmod:item/spear", "layer0": "#particle" }, "display": { "thirdperson_righthand": { "rotation": [ 0, 60, 0 ], "translation": [ 11, 17, -2 ], "scale": [ 1, 1, 1 ] }, "thirdperson_lefthand": { "rotation": [ 0, 60, 0 ], "translation": [ 3, 17, 12 ], "scale": [ 1, 1, 1 ] }, "firstperson_righthand": { "rotation": [ 0, -90, 25 ], "translation": [ -3, 17, 1], "scale": [ 1, 1, 1 ] }, "firstperson_lefthand": { "rotation": [ 0, 90, -25 ], "translation": [ 13, 17, 1], "scale": [ 1, 1, 1 ] }, "gui": { "rotation": [ 15, -25, -5 ], "translation": [ 2, 3, 0 ], "scale": [ 0.65, 0.65, 0.65 ] }, "fixed": { "rotation": [ 0, 180, 0 ], "translation": [ -2, 4, -5], "scale":[ 0.5, 0.5, 0.5] }, "ground": { "rotation": [ 0, 0, 0 ], "translation": [ 4, 4, 2], "scale":[ 0.25, 0.25, 0.25] } }, "overrides": [ { "predicate": { "throwing": 1 }, "model": "blazersmod:item/spear_throwing" } ] } but I can't seem to display the texture in the inventory Sorry I've never used this, so I'm fairly new to it ๐ I've overridden the initializeClient method, and added a custom IItemRenderProperties like this @Override public void initializeClient(Consumer<IItemRenderProperties> consumer) { consumer.accept(new IItemRenderProperties() { @Override public BlockEntityWithoutLevelRenderer getItemStackRenderer() { return new SpearItemRenderer(new EntityModelSet()); } }); } With the ItemRenderer class being this @OnlyIn(Dist.CLIENT) public class SpearItemRenderer extends BlockEntityWithoutLevelRenderer { private ThrownSpearModel spearModel; private ThrownMalachiteSpearModel malachiteSpearModel; private final EntityModelSet entityModelSet; public SpearItemRenderer(EntityModelSet entityModelSet) { super(new BlockEntityRenderDispatcher(Minecraft.getInstance().font, entityModelSet, () -> Minecraft.getInstance().getBlockRenderer()), entityModelSet); this.entityModelSet = entityModelSet; } public void onResourceManagerReload(@NotNull ResourceManager resourceManager) { this.spearModel = new ThrownSpearModel(this.entityModelSet.bakeLayer(ThrownSpearModel.LAYER_LOCATION)); this.malachiteSpearModel = new ThrownMalachiteSpearModel(this.entityModelSet.bakeLayer(ThrownMalachiteSpearModel.LAYER_LOCATION)); } @Override public void renderByItem(ItemStack stack, ItemTransforms.@NotNull TransformType transformType, @NotNull PoseStack pose, @NotNull MultiBufferSource buffer, int packedLight, int packedOverlay) { if(stack.getItem() instanceof SpearItem) { pose.pushPose(); pose.scale(1.0F, -1.0F, -1.0F); boolean isMalachiteSpear = stack.is(BLItems.MALACHITE_SPEAR.get()); EntityModel<ThrownSpear> model = isMalachiteSpear ? this.malachiteSpearModel : this.spearModel; ResourceLocation layerLocation = isMalachiteSpear ? ThrownMalachiteSpearRenderer.SPEAR_LOCATION : ThrownSpearRenderer.SPEAR_LOCATION; VertexConsumer vertexConsumer = ItemRenderer.getFoilBufferDirect(buffer, model.renderType(layerLocation), false, stack.hasFoil()); model.renderToBuffer(pose, vertexConsumer, packedLight, packedOverlay, 1.0F, 1.0F, 1.0F, 1.0F); pose.popPose(); } } } I also tried to look at how the Shield/Trident are rendered, but can't find anything related to the Item itself (something that binds the actual Item to the rendering, I just found the base BlockEntityWithoutLevelRenderer where those items are rendered)
-
[1.18.2] [SOLVED] Custom block renderer, like cable mods do
JimiIT92 replied to Robsutar's topic in Modder Support
In Vanilla, Redstone and Fences/Walls kinda do already this thing, where their shape changes according to the neighbors. You should check how they do, which properties are stored in the blockstate file and do the model files accordingly -
EDIT: Nevermind, I figured out ๐ I just need to explicit the class in the Builder public static final RegistryObject<EntityType<ThrownSpear>> SPEAR = ENTITY_TYPES.register("spear", () -> EntityType.Builder.<ThrownSpear>of(ThrownSpear::new, MobCategory.MISC) .sized(0.5F, 0.5F).clientTrackingRange(4).updateInterval(20) .build(new ResourceLocation(BlazersMod.MOD_ID, "spear").toString())); So I can now instance the Spear with the LivingEntity constructor and at least the spear gets spawned inside the world. But this doesn't solve the model in hand issue, where the Spear appears as a regular Item. It gets shifted in position when charging, however the entity model is not showing How can I bind the entity model to the item so that the actual Spear model gets displayed when holding/throwing the Item? The problem is that if I add the Living Entity constructor, then the EntityType registration complains I'm registering the EntityType like this public static final RegistryObject<EntityType<ThrownSpear>> SPEAR = ENTITY_TYPES.register("spear", () -> EntityType.Builder.of(ThrownSpear::new, MobCategory.MISC) .sized(0.5F, 0.5F).clientTrackingRange(4).updateInterval(20) .build(new ResourceLocation(BlazersMod.MOD_ID, "spear").toString())); And if I add the LivingEntity constructor, I get this error Cannot resolve constructor 'ThrownSpear' Of course the EntityType constructor still remains in the class public ThrownSpear(EntityType<? extends AbstractArrow> type, Level level) { super(type, level); } public ThrownSpear(Level world, LivingEntity entity, ItemStack stack) { super(BLEntityTypes.SPEAR.get(), entity, world); this.spearItem = stack.copy(); }
-
I'm trying to make a Spear that the Player can throw, so what I've done is essentially make a custom Trident Item with a custom entity as well. However when I release the spear the entity related is not spawning If I summon the Entity via command it is spawned and rendered as well... This is the class I'm using, very similar to the Trident Item class public class SpearItem extends SwordItem implements Vanishable { public SpearItem(Tier tier, int attackDamageModifier, float attackSpeedModifier) { super(tier, attackDamageModifier, attackSpeedModifier, new Properties().durability(tier.getUses()).tab(BLTabs.TAB_COMBAT)); } public @NotNull UseAnim getUseAnimation(@NotNull ItemStack itemStack) { return UseAnim.SPEAR; } public int getUseDuration(@NotNull ItemStack itemStack) { return 36000; } public void releaseUsing(@NotNull ItemStack stack, @NotNull Level level, @NotNull LivingEntity livingEntity, int timeLeft) { if (livingEntity instanceof Player player) { int i = this.getUseDuration(stack) - timeLeft; if (i >= 10) { if (!level.isClientSide) { stack.hurtAndBreak(1, player, p -> p.broadcastBreakEvent(livingEntity.getUsedItemHand())); ThrownSpear thrownSpear = ThrownSpear.from(level, player, stack); thrownSpear.shootFromRotation(player, player.getXRot(), player.getYRot(), 0.0F, 2.5F, 1.0F); if (player.getAbilities().instabuild) { thrownSpear.pickup = AbstractArrow.Pickup.CREATIVE_ONLY; } level.addFreshEntity(thrownSpear); level.playSound(null, thrownSpear, SoundEvents.TRIDENT_THROW, SoundSource.PLAYERS, 1.0F, 1.0F); if (!player.getAbilities().instabuild) { player.getInventory().removeItem(stack); } } player.awardStat(Stats.ITEM_USED.get(this)); } } } public @NotNull InteractionResultHolder<ItemStack> use(@NotNull Level level, Player player, @NotNull InteractionHand hand) { player.startUsingItem(hand); return InteractionResultHolder.consume(player.getItemInHand(hand)); } } And this is the ThrownSpear entity class (again, very similar to the ThrownTrident class as well) public class ThrownSpear extends AbstractArrow { private ItemStack spearItem = new ItemStack(BLItems.SPEAR.get()); private boolean dealtDamage; public ThrownSpear(EntityType<? extends ThrownSpear> spear, Level world) { super(spear, world); } public void tick() { if (this.inGroundTime > 4) { this.dealtDamage = true; } super.tick(); } protected ItemStack getPickupItem() { return this.spearItem.copy(); } @Nullable protected EntityHitResult findHitEntity(Vec3 startVec, Vec3 endVec) { return this.dealtDamage ? null : super.findHitEntity(startVec, endVec); } protected void onHitEntity(EntityHitResult pResult) { Entity entity = pResult.getEntity(); float f = 8.0F; if (entity instanceof LivingEntity livingentity) { f += EnchantmentHelper.getDamageBonus(this.spearItem, livingentity.getMobType()); } Entity entity1 = this.getOwner(); DamageSource damagesource = DamageSource.trident(this, (Entity)(entity1 == null ? this : entity1)); this.dealtDamage = true; SoundEvent soundevent = SoundEvents.TRIDENT_HIT; if (entity.hurt(damagesource, f)) { if (entity.getType() == EntityType.ENDERMAN) { return; } if (entity instanceof LivingEntity livingentity1) { if (entity1 instanceof LivingEntity) { EnchantmentHelper.doPostHurtEffects(livingentity1, entity1); EnchantmentHelper.doPostDamageEffects((LivingEntity)entity1, livingentity1); } this.doPostHurtEffects(livingentity1); } } this.setDeltaMovement(this.getDeltaMovement().multiply(-0.01D, -0.1D, -0.01D)); this.playSound(soundevent, 1.0F, 1.0F); } protected boolean tryPickup(Player player) { return super.tryPickup(player) || this.isNoPhysics() && this.ownedBy(player) && player.getInventory().add(this.getPickupItem()); } protected SoundEvent getDefaultHitGroundSoundEvent() { return SoundEvents.TRIDENT_HIT_GROUND; } public void playerTouch(Player player) { if (this.ownedBy(player) || this.getOwner() == null) { super.playerTouch(player); } } public void readAdditionalSaveData(CompoundTag compound) { super.readAdditionalSaveData(compound); if (compound.contains("Spear", 10)) { this.spearItem = ItemStack.of(compound.getCompound("Spear")); } this.dealtDamage = compound.getBoolean("DealtDamage"); } public void addAdditionalSaveData(CompoundTag compound) { super.addAdditionalSaveData(compound); compound.put("Spear", this.spearItem.save(new CompoundTag())); compound.putBoolean("DealtDamage", this.dealtDamage); } public boolean shouldRender(double x, double y, double z) { return true; } public ItemStack getSpearItem() { return this.spearItem; } public static ThrownSpear from(Level world, LivingEntity entity, ItemStack stack) { ThrownSpear thrownSpear = new ThrownSpear(BLEntityTypes.SPEAR.get(), world); thrownSpear.spearItem = stack.copy(); return thrownSpear; } } What am I missing in the Item or the Entity class? Also, how do I make so the Item inside the inventory actually renders the entity? I've already registered the "throwing" property, like vanilla Trident does, but it looks like this is ignored ItemProperties.register(item.get(), new ResourceLocation("throwing"), (stack, level, entity, seed) -> entity != null && entity.isUsingItem() && entity.getUseItem() == stack ? 1.0F : 0.0F); where item is just a Spear item I'm passing (I have multiple spears registered that shares the same class and entity, just different textures). I've already registered other Item properties, like the "pull" and "pulling" for a custom bow and they are working just fine, so I don't understand why is not working for the spears as well
-
I'm facing a strange issue with custom biomes in Forge 1.18. Essentially they do not seem to be generated or be registered at all... This is how I'm registering a biome @Mod.EventBusSubscriber(modid = "test", bus = Mod.EventBusSubscriber.Bus.MOD) public class CommonModEvents { @SubscribeEvent public static void registerOres(FMLCommonSetupEvent event) { event.enqueueWork(BiomeGeneration::registerBiomes); } } public class BiomeGeneration { public static final ResourceKey<Biome> TEST_BIOME = ResourceKey.create(Registry.BIOME_REGISTRY, new ResourceLocation("test", "test_biome")); public static void registerBiomes() { BiomeDictionary.addTypes(TEST_BIOME, BiomeDictionary.Type.FOREST); BiomeManager.addAdditionalOverworldBiomes(TEST_BIOME); } } And this is the Biome json file. Nothing crazy here, just a Birch Forest json file without Birch trees (just to see if it actually spawns) { "effects": { "mood_sound": { "sound": "minecraft:ambient.cave", "tick_delay": 6000, "block_search_extent": 8, "offset": 2.0 }, "sky_color": 8037887, "fog_color": 12638463, "water_color": 4159204, "water_fog_color": 329011 }, "carvers": { "air": [ "minecraft:cave", "minecraft:cave_extra_underground", "minecraft:canyon" ] }, "features": [ [], [ "minecraft:lake_lava_underground", "minecraft:lake_lava_surface" ], [ "minecraft:amethyst_geode" ], [ "minecraft:monster_room", "minecraft:monster_room_deep" ], [], [], [ "minecraft:ore_dirt", "minecraft:ore_gravel", "minecraft:ore_granite_upper", "minecraft:ore_granite_lower", "minecraft:ore_diorite_upper", "minecraft:ore_diorite_lower", "minecraft:ore_andesite_upper", "minecraft:ore_andesite_lower", "minecraft:ore_tuff", "minecraft:ore_coal_upper", "minecraft:ore_coal_lower", "minecraft:ore_iron_upper", "minecraft:ore_iron_middle", "minecraft:ore_iron_small", "minecraft:ore_gold", "minecraft:ore_gold_lower", "minecraft:ore_redstone", "minecraft:ore_redstone_lower", "minecraft:ore_diamond", "minecraft:ore_diamond_large", "minecraft:ore_diamond_buried", "minecraft:ore_lapis", "minecraft:ore_lapis_buried", "minecraft:ore_copper", "minecraft:underwater_magma", "minecraft:disk_sand", "minecraft:disk_clay", "minecraft:disk_gravel" ], [], [ "minecraft:spring_water", "minecraft:spring_lava" ], [ "minecraft:glow_lichen", "minecraft:forest_flowers", "minecraft:flower_default", "minecraft:patch_grass_forest", "minecraft:brown_mushroom_normal", "minecraft:red_mushroom_normal", "minecraft:patch_sugar_cane", "minecraft:patch_pumpkin" ], [ "minecraft:freeze_top_layer" ] ], "spawners": { "monster": [ { "type": "minecraft:spider", "weight": 100, "minCount": 4, "maxCount": 4 }, { "type": "minecraft:zombie", "weight": 95, "minCount": 4, "maxCount": 4 }, { "type": "minecraft:zombie_villager", "weight": 5, "minCount": 1, "maxCount": 1 }, { "type": "minecraft:skeleton", "weight": 100, "minCount": 4, "maxCount": 4 }, { "type": "minecraft:creeper", "weight": 100, "minCount": 4, "maxCount": 4 }, { "type": "minecraft:slime", "weight": 100, "minCount": 4, "maxCount": 4 }, { "type": "minecraft:enderman", "weight": 10, "minCount": 1, "maxCount": 4 }, { "type": "minecraft:witch", "weight": 5, "minCount": 1, "maxCount": 1 } ], "creature": [ { "type": "minecraft:sheep", "weight": 12, "minCount": 4, "maxCount": 4 }, { "type": "minecraft:pig", "weight": 10, "minCount": 4, "maxCount": 4 }, { "type": "minecraft:chicken", "weight": 10, "minCount": 4, "maxCount": 4 }, { "type": "minecraft:cow", "weight": 8, "minCount": 4, "maxCount": 4 } ], "ambient": [ { "type": "minecraft:bat", "weight": 10, "minCount": 8, "maxCount": 8 } ], "axolotls": [], "underground_water_creature": [ { "type": "minecraft:glow_squid", "weight": 10, "minCount": 4, "maxCount": 6 } ], "water_creature": [], "water_ambient": [], "misc": [] }, "spawn_costs": {}, "precipitation": "rain", "temperature": 0.6, "downfall": 0.6, "category": "forest" } If I try to locate the biome using the /locatebiome test:test_biome it says that it can't find the Biome. Also, if I try to generate a single biome world, the custom biome doesn't even show in the biomes list. What I noticed, in comparison to 1.17, is that the BiomeEntry class essentially is ignoring the weight parameter. This is the 1.17 BiomeEntry class public static class BiomeEntry extends WeighedRandom.WeighedRandomItem { private final ResourceKey<Biome> key; public BiomeEntry(ResourceKey<Biome> key, int weight) { super(weight); this.key = key; } public ResourceKey<Biome> getKey() { return this.key; } } And this is the 1.18 one public static class BiomeEntry { private final ResourceKey<Biome> key; public BiomeEntry(ResourceKey<Biome> key, int weight) { this.key = key; } public ResourceKey<Biome> getKey() { return this.key; } } Also, looking at the BiomeManager class, specifically the getAdditionalOverworldBiomes method, which should be used to attach the mod biomes to the vanilla ones, if you try to look for method usages, in 1.18 it appears that this method is unused. This is what IntelliJ shows if I run the "Find usages" command on this method https://imgur.com/a/r0Fvkjp Where in 1.17 this method is called by the OverworldBiomeSource constructor to attach the mod biomes to the already registered ones public OverworldBiomeSource(long p_48590_, boolean p_48591_, boolean p_48592_, Registry<Biome> p_48593_) { super(java.util.stream.Stream.concat(POSSIBLE_BIOMES.stream(), net.minecraftforge.common.BiomeManager.getAdditionalOverworldBiomes().stream()).map((p_48603_) -> { return () -> { return p_48593_.getOrThrow(p_48603_); }; })); //... } So, am I missing something during the creation of a Biome in 1.18 or there's still something missing on this?
-
Allright, so I got the correct event now, and I'm adding the layer like this @Mod.EventBusSubscriber(modid = MOD_ID, value = Dist.CLIENT) public final class LayerRenderSubscriber { @SubscribeEvent(priority = EventPriority.LOW) public static void renderPlayer(final EntityRenderersEvent.AddLayers event) { LivingEntityRenderer<Player, PlayerModel<Player>> renderer = event.getRenderer(EntityType.PLAYER); BlackElytraLayer<Player, PlayerModel<Player>> layer = new BlackElytraLayer<>(renderer, event.getEntityModels()); renderer.addLayer(layer); } } But the Layer doesn't render at all. Even worse, if I start the game in debug mode, a breakpoint into the event doesn't hit, so it seems like the event isn't even fired. Do I need to register something or do something else that I'm missing?
-
I'm trying to add a custom layer to the vanilla player rendering. Specifically, I'm trying to create some custom Elytras. I've already extended the vanilla Elytra Layer, and tried to attach it inside the RenderPlayerEvent.Pre event (which I'm not sure if is the right place, I'm using a 1.16.x code base as reference) @SubscribeEvent public static void renderPlayer(final RenderPlayerEvent.Pre event) { Player player = event.getPlayer(); PlayerRenderer renderer = event.getRenderer(); renderer.addLayer(new BlackElytraLayer<>(renderer, ?)); //? is for the missing EntityModelSet parameter } However, the ElytraLayer requires an EntityModelSet as parameter, to bake the Elytra Model public ElytraLayer(RenderLayerParent<T, M> parent, EntityModelSet modelSet) { super(parent); this.elytraModel = new ElytraModel<>(modelSet.bakeLayer(ModelLayers.ELYTRA)); } But inside the event I see nothing that can get me an instance of this varaiable, leading me to bake the Elytra Model as well. So is there any way to get this or do I just have to recreate the entire Elytra model and use that in the custom layer?
-
As the tile says, is there any way to add or edit requirements for Vanilla Advancements? For instance, let's say I add a new food to the game. I want that food to be counted into the "A balanced diet" advancement, but of course I want to avoid edit the vanilla json file. So is there any way to add this requirement to the advancement without editing it (similar to how you can add or modify Loot to LootTables using GlobalLootModifiers)?
-
So essentially I need the RegistryObject just for overworld biomes? For instance, if I have a custom dimension or want to add new Biomes to Nether/End I can just register the key and then handle everything via the Json? I assume this excludes features, so if I want my custom feature to be generated inside the Biome I still need to code it like I'm doing now, right? Just I won't need to add it through code in the BiomeLoadingEvent?
-
Ok, so adding custom features during the BiomeLoadingEvent (essentialy what I already do for flowers/tree generation) solves the issue. However I'm curious to know more about the json Biomes. Where can I find some details about this system? Will this allow me to add biomes to the game without even writing code (so just the Json file)? I looked on the documentation but found nothing about it
-
The point is that even without adding the CF to a custom Biome the game crashes However, here is how I create the Biome public static Biome volcanicWasteland() { MobSpawnSettings mobSpawnSettings = new MobSpawnSettings.Builder() .addSpawn(MobCategory.MONSTER, new MobSpawnSettings.SpawnerData(EntityType.MAGMA_CUBE, 100, 2, 5)) .build(); BiomeGenerationSettings.Builder biomeBuilder = new BiomeGenerationSettings.Builder(); addDefaultOverworldUnderground(biomeBuilder); biomeBuilder.surfaceBuilder(MwConfiguredSurfaceBuilders.VOLCANIC_WASTELAND) .addFeature(GenerationStep.Decoration.SURFACE_STRUCTURES, Features.DELTA) .addFeature(GenerationStep.Decoration.VEGETAL_DECORATION, Features.SPRING_LAVA_DOUBLE) .addFeature(GenerationStep.Decoration.UNDERGROUND_DECORATION, Features.SPRING_DELTA) .addFeature(GenerationStep.Decoration.UNDERGROUND_DECORATION, Features.ORE_MAGMA) .addFeature(GenerationStep.Decoration.UNDERGROUND_DECORATION, Features.SPRING_CLOSED_DOUBLE); //Add custom CF biomeBuilder.addFeature(GenerationStep.Decoration.SURFACE_STRUCTURES, ModConfiguredFeatures.SMALL_LAVA_ROCK_COLUMNS); var temperature = 10.0F; var ashColor = 0x333333; return new Biome.BiomeBuilder() .precipitation(Biome.Precipitation.NONE) .biomeCategory(Biome.BiomeCategory.DESERT) .depth(0.25F) .scale(0.1F) .temperature(temperature) .downfall(0.0F) .specialEffects( new BiomeSpecialEffects.Builder() .waterColor(ashColor) .waterFogColor(ashColor) .fogColor(ashColor) .foliageColorOverride(ashColor) .grassColorOverride(ashColor) .skyColor(calculateSkyColor(temperature)) .ambientParticle(new AmbientParticleSettings(ParticleTypes.WHITE_ASH, 0.118093334F)) .ambientMoodSound(AmbientMoodSettings.LEGACY_CAVE_SETTINGS) .build()) .mobSpawnSettings(mobSpawnSettings) .generationSettings(biomeBuilder.build()) .build(); } which I register on the Biome Registry from the main mod constructor var eventBus = FMLJavaModLoadingContext.get().getModEventBus(); MOD_FEATURES.register(eventBus); MOD_BIOMES.register(eventBus); and here is were I store the Biomes //Registry public static final DeferredRegister<Biome> MOD_BIOMES = DeferredRegister.create(ForgeRegistries.BIOMES, MOD_ID); //Biomes public static final RegistryObject<Biome> VOLCANIC_WASTELAND = register("volcanic_wasteland", BiomeUtils::volcanicWasteland); //Register Biome public static RegistryObject<Biome> register(String name, Supplier<Biome> biomeSupplier) { return REGISTRY.register(name, biomeSupplier); } Finally, during FMLCommonSetupEvent I add the Biome to the BiomeDictionary @SubscribeEvent public static void onCommonSetup(final FMLCommonSetupEvent event) { ... event.enqueueWork(CommonSetupSubscriber::addVolcanicWastelandBiome); } private static void addVolcanicWastelandBiome() { var key = ResourceKey.create(ForgeRegistries.Keys.BIOMES, Objects.requireNonNull(ForgeRegistries.BIOMES).getKey(ModBiomes.VOLCANIC_WASTELAND.get())); BiomeDictionary.addTypes(key, BiomeDictionary.Type.HOT, BiomeDictionary.Type.DRY, BiomeDictionary.Type.WASTELAND, BiomeDictionary.Type.DEAD); BiomeManager.addBiome(BiomeManager.BiomeType.DESERT, new BiomeManager.BiomeEntry(key, 70)); } This is the full debug log I have. I noticed that error is actually thrown by another method (the appleForest method is used to create another mod biome). I guess is called when the Mod feature hasn't been registered yet, so it crashes?
-
I'm having some troubles creating/attaching a ConfiguredFeature built on top of a modded feature in a modded biome. I had no issues using CF with vanilla features (like Flower placing or Tree placement with mod blocks, for example), however if I want to use my own Feature as well, I get some troubles. What I've done is this: I create the Feature like this // Deferred Register public static final DeferredRegister<Feature<?>> MOD_FEATURES = DeferredRegister.create(ForgeRegistries.FEATURES, MOD_ID); // Feature public static final RegistryObject<Feature<ColumnFeatureConfiguration>> LAVA_ROCK_COLUMNS = register("lava_rock_columns", () -> new LavaRockColumnsFeature(ColumnFeatureConfiguration.CODEC)); //LavaRockColumnsFeature is just a class that extends Feature<ColumnFeatureConfiguration> //to generate basalt columns but with a mod block instead // Register the Feature public static <T extends FeatureConfiguration> RegistryObject<Feature<T>> register(String name, Supplier<Feature<T>> featureSupplier) { return MOD_FEATURES.register(name, featureSupplier); } Then, from the main mod class constructor, I call the register method for the Feature registry var eventBus = FMLJavaModLoadingContext.get().getModEventBus(); MOD_FEATURES.register(eventBus); Next I create my ConfiguredFeatures like this //Generate "Anemone" flowers inside the World public static final ConfiguredFeature<?, ?> ANEMONE = Feature.FLOWER.configured( (new RandomPatchConfiguration.GrassConfigurationBuilder( new SimpleStateProvider(ModBlocks.ANEMONE.get().defaultBlockState()), SimpleBlockPlacer.INSTANCE) ).tries(2).build()) .decorated(Features.Decorators.HEIGHTMAP_SQUARE).count(1); //Generate "Small Lava Rock Columns" in a custom Biome public static final ConfiguredFeature<?, ?> SMALL_LAVA_ROCK_COLUMNS = ModFeatures.LAVA_ROCK_COLUMNS.get() .configured(new ColumnFeatureConfiguration(ConstantInt.of(1), UniformInt.of(1, 4))) .decorated(FeatureDecorator.COUNT_MULTILAYER.configured(new CountConfiguration(4))); Then I register this feature during the FMLCommonSetupEvent by doing this @Mod.EventBusSubscriber(modid = MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD) public final class CommonSetupSubscriber { @SubscribeEvent public static void onCommonSetup(final FMLCommonSetupEvent event) { event.enqueueWork(() -> { registerConfiguredFeatures(); }); } } private static void registerConfiguredFeatures() { Registry.register(BuiltinRegistries.CONFIGURED_FEATURE, new ResourceLocation(MOD_ID, "anemone"), ModConfiguredFeatures.ANEMONE); Registry.register(BuiltinRegistries.CONFIGURED_FEATURE, new ResourceLocation(MOD_ID, "small_lava_rock_columns"), ModConfiguredFeatures.SMALL_LAVA_ROCK_COLUMNS); } Even without adding the "Small lava rock" configured feature to the custom Biome, if I run the game, I get this error java.lang.NullPointerException: Registry Object not present: mod_id:lava_rock_columns So I guess the issue is that the ConfiguredFeature is trying to get the Feature from the Registry, but can't find it? So what should be the correct way to register the Feature and/or the Configured Feature?
-
I've noticed something strange when using commands like /give or /setblock. Essentially the mod blocks and items are kinda ignored by these commands (these two are just an example, but this behaviour happens to all commands in the game). Let me explain: Let's say you have a block in your mod registered as mymod:my_cobblestone_block If you type /give Dev bble, the vanilla Cobblestone block shows in the suggestions list but not the mod blocks. This is because the suggestions look for blocks and items that contains the word you typed in (in this case minecraft:cobblestone contains the word bble) This system only works for mod blocks and items if you type the mod_id first. For example, typing /give Dev mymod:bble will make the my_cobblestone_block appear in the suggestions list. Is there any way to prevent this and make the mod blocks and items appear in the suggestions list even without typing the mod id first?
-
[SOLVED][1.17.1] Using functions inside Global Loot Modifiers
JimiIT92 replied to JimiIT92's topic in Modder Support
Alright, so after a whole morning of tries, I finally got it working! Let's say I have this object inside my GLM json { "item": "minecraft:emerald", "chance": 0.0265, "functions": [ { "function": "minecraft:set_count", "count": { "type": "minecraft:uniform", "min": 2.0, "max": 7.0 }, "add": false } ] } where I am saying that the emerald has a 2.65% chacne of being inside the chest loot. But, I want that the stack, if any, must be between 2 and 7 items. Sure I can add my "min" and "max" parameters, but since we already have a function that does this, why not using it? So I added the vanilla set_count function, using the exact same syntax as any vanilla loot table. In the deserializer (the class that extends GlobalLootModifierSerializer) I can read the functions array by doing so var functionsArray = jsonObject.getAsJsonArray("functions"); if(functionsArray != null) { var functions = Deserializers.createFunctionSerializer().create().fromJson(functionsArray, LootItemFunction[].class); } This will return a LootItemFunction array that is the actual list of functions that are specified inside the JSON, correctly deserialized. So we can store these functions and use them when adding the ItemStack to the generated loot, by doing a simple for loop var stack = new ItemStack(item); if(functions != null && !functions.isEmpty()) { functions.forEach(function -> function.apply(stack, context)); } By doing that the function will be applied to the item of the GLM! Of course we are not limited by the set_count, but we can use any vanilla function as we want (I think even custom functions, but I'm not sure about this since I haven't any of these). To correctly serialize said functions, inside the write method, we can use this function Deserializers.createFunctionSerializer().create().toJsonTree(functions) assuming that functions is a variable containing all of the functions previously deserialized -
Hi everyone! As the title says, I am wondering if there is a way to use vanilla loot table functions inside a Global Loot Modifier. For instance: I want to add a Sword to the Mineshaft chests and I want this Sword to be randomly enchanted, kinda like vanilla does it to Desert Pyarmid chests or for End Cities. In the first case, this function is called minecraft:enchant_randomly while for End Cities is this one { "function": "minecraft:enchant_with_levels", "levels": { "type": "minecraft:uniform", "min": 20.0, "max": 39.0 }, "treasure": true } Either way, using these functions the Item can sometime be enchanted. How can I make so in a Loot Modifier I can use these functions as well, if is ever possible? I read the doc about Global Item Modifier and of course have seen the GitHub examples. This helped me setting up some Loot Modifiers, which has no issues adding custom items to Vanilla Loot Tables, however I can't seem to find any example or indication about the possibility to use functions inside Loot Modifiers (aside from inserting some custom keywords in the json that if parsed will manually trigger the said function)
-
[1.16.3] Creating and rendering a custom tooltip
JimiIT92 replied to JimiIT92's topic in Modder Support
Can confirm, I update to the latest version (1.16.3-34.1.16) and it fires -
I was saying use the ClientChatReceivedEvent event, but I have a doubt: if a Player send the same exact message what would happen? How can you tell if the message has been sent from a player or not?
-
[1.16.3] Creating and rendering a custom tooltip
JimiIT92 replied to JimiIT92's topic in Modder Support
Darn... so how would I catch this event to change what is shown? I tried the other sub events as well and they did not get catched as well