diff --git a/src/main/java/com/voxelutopia/ultramarine/client/integration/jei/CustomWanderingTraderCategory.java b/src/main/java/com/voxelutopia/ultramarine/client/integration/jei/CustomWanderingTraderCategory.java new file mode 100644 index 00000000..b0054ebb --- /dev/null +++ b/src/main/java/com/voxelutopia/ultramarine/client/integration/jei/CustomWanderingTraderCategory.java @@ -0,0 +1,76 @@ +package com.voxelutopia.ultramarine.client.integration.jei; + +import com.voxelutopia.ultramarine.Ultramarine; +import com.voxelutopia.ultramarine.data.recipe.WoodworkingRecipe; +import com.voxelutopia.ultramarine.data.registry.BlockRegistry; +import mezz.jei.api.constants.VanillaTypes; +import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; +import mezz.jei.api.gui.drawable.IDrawable; +import mezz.jei.api.helpers.IGuiHelper; +import mezz.jei.api.recipe.IFocusGroup; +import mezz.jei.api.recipe.RecipeIngredientRole; +import mezz.jei.api.recipe.RecipeType; +import mezz.jei.api.recipe.category.IRecipeCategory; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; + +public class CustomWanderingTraderCategory implements IRecipeCategory { + + public static final ResourceLocation UID = new ResourceLocation(Ultramarine.MOD_ID, "custom_wandering_trader"); + + public static final RecipeType CUSTOM_WANDERING_TRADER_WRAPPER_RECIPE_TYPE = + new RecipeType<>(UID, CustomWanderingTraderWrapper.class); + + public static final int WIDTH = 82; + public static final int HEIGHT = 34; + + private final IDrawable background; + private final IDrawable icon; + private final Component localizedName; + + public CustomWanderingTraderCategory(IGuiHelper guiHelper) { + background = guiHelper.createDrawable(UltramarinePlugin.JEI_GUI_VANILLA, 0, 220, WIDTH, HEIGHT); + icon = guiHelper.createDrawableIngredient(VanillaTypes.ITEM_STACK, new ItemStack(BlockRegistry.TEAHOUSE_FLAG.get())); + localizedName = new TranslatableComponent("gui.jei.category.custom_wandering_trader"); + } + + @Override + public Component getTitle() { + return localizedName; + } + + @Override + public IDrawable getBackground() { + return background; + } + + @Override + public IDrawable getIcon() { + return icon; + } + + @SuppressWarnings("removal") + @Override + public ResourceLocation getUid() { + return UID; + } + + @SuppressWarnings("removal") + @Override + public Class getRecipeClass() { + return CustomWanderingTraderWrapper.class; + } + + @Override + public RecipeType getRecipeType() { + return CUSTOM_WANDERING_TRADER_WRAPPER_RECIPE_TYPE; + } + + @Override + public void setRecipe(IRecipeLayoutBuilder builder, CustomWanderingTraderWrapper recipe, IFocusGroup focuses) { + builder.addSlot(RecipeIngredientRole.INPUT, 1, 9).addItemStack(recipe.getInput()); + builder.addSlot(RecipeIngredientRole.OUTPUT, 61, 9).addItemStack(recipe.getOutput()); + } +} diff --git a/src/main/java/com/voxelutopia/ultramarine/client/integration/jei/CustomWanderingTraderWrapper.java b/src/main/java/com/voxelutopia/ultramarine/client/integration/jei/CustomWanderingTraderWrapper.java new file mode 100644 index 00000000..5d30aebf --- /dev/null +++ b/src/main/java/com/voxelutopia/ultramarine/client/integration/jei/CustomWanderingTraderWrapper.java @@ -0,0 +1,25 @@ +package com.voxelutopia.ultramarine.client.integration.jei; + +import mezz.jei.api.recipe.category.extensions.IRecipeCategoryExtension; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.trading.MerchantOffer; + +public class CustomWanderingTraderWrapper implements IRecipeCategoryExtension { + + private ItemStack input; + private ItemStack output; + + public CustomWanderingTraderWrapper(MerchantOffer offer){ + this.input = offer.getBaseCostA(); + this.output = offer.getResult(); + } + + public ItemStack getInput() { + return input; + } + + public ItemStack getOutput() { + return output; + } +} diff --git a/src/main/java/com/voxelutopia/ultramarine/client/integration/jei/UltramarinePlugin.java b/src/main/java/com/voxelutopia/ultramarine/client/integration/jei/UltramarinePlugin.java index c4ac432d..24ddde14 100644 --- a/src/main/java/com/voxelutopia/ultramarine/client/integration/jei/UltramarinePlugin.java +++ b/src/main/java/com/voxelutopia/ultramarine/client/integration/jei/UltramarinePlugin.java @@ -6,6 +6,7 @@ import com.voxelutopia.ultramarine.data.recipe.WoodworkingRecipe; import com.voxelutopia.ultramarine.data.registry.BlockRegistry; import com.voxelutopia.ultramarine.data.registry.RecipeTypeRegistry; +import com.voxelutopia.ultramarine.world.entity.CustomWanderingTrader; import mezz.jei.api.IModPlugin; import mezz.jei.api.JeiPlugin; import mezz.jei.api.helpers.IGuiHelper; @@ -43,6 +44,7 @@ public void registerCategories(IRecipeCategoryRegistration registration) { registration.addRecipeCategories(new WoodworkingRecipeCategory(guiHelper)); registration.addRecipeCategories(new CompositeSmeltingRecipeCategory(guiHelper)); registration.addRecipeCategories(new ChiselTableRecipeCategory(guiHelper)); + registration.addRecipeCategories(new CustomWanderingTraderCategory(guiHelper)); } @Override @@ -55,6 +57,8 @@ public void registerRecipes(IRecipeRegistration registration) { registration.addRecipes(CompositeSmeltingRecipeCategory.COMPOSITE_SMELTING_RECIPE_TYPE, compositeSmeltingRecipes); List chiselTableRecipes = recipeManager.getAllRecipesFor(RecipeTypeRegistry.CHISEL_TABLE.get()); registration.addRecipes(ChiselTableRecipeCategory.CHISEL_TABLE_RECIPE_TYPE, chiselTableRecipes); + List customWanderingTraderTrades = CustomWanderingTrader.getTradeOptions().stream().map(CustomWanderingTraderWrapper::new).toList(); + registration.addRecipes(CustomWanderingTraderCategory.CUSTOM_WANDERING_TRADER_WRAPPER_RECIPE_TYPE, customWanderingTraderTrades); } @Override @@ -62,6 +66,7 @@ public void registerRecipeCatalysts(IRecipeCatalystRegistration registration) { registration.addRecipeCatalyst(new ItemStack(BlockRegistry.WOODWORKING_WORKBENCH.get()), WoodworkingRecipeCategory.WOODWORKING_RECIPE_TYPE); registration.addRecipeCatalyst(new ItemStack(BlockRegistry.BRICK_KILN.get()), CompositeSmeltingRecipeCategory.COMPOSITE_SMELTING_RECIPE_TYPE); registration.addRecipeCatalyst(new ItemStack(BlockRegistry.CHISEL_TABLE.get()), ChiselTableRecipeCategory.CHISEL_TABLE_RECIPE_TYPE); + registration.addRecipeCatalyst(new ItemStack(BlockRegistry.TEAHOUSE_FLAG.get()), CustomWanderingTraderCategory.CUSTOM_WANDERING_TRADER_WRAPPER_RECIPE_TYPE); } } diff --git a/src/main/java/com/voxelutopia/ultramarine/data/registry/PoiTypeRegistry.java b/src/main/java/com/voxelutopia/ultramarine/data/registry/PoiTypeRegistry.java index eda31d17..4d32f2e4 100644 --- a/src/main/java/com/voxelutopia/ultramarine/data/registry/PoiTypeRegistry.java +++ b/src/main/java/com/voxelutopia/ultramarine/data/registry/PoiTypeRegistry.java @@ -15,10 +15,13 @@ public class PoiTypeRegistry { public static final RegistryObject COOKING_POI = POI_TYPES.register("cooking_poi", () -> new PoiType("cooking_poi", PoiType.getBlockStates(BlockRegistry.FOOD_HAMPER.get()), 1, 1)); + public static final RegistryObject TRADE_POI = POI_TYPES.register("trade_poi", + () -> new PoiType("trade_poi", PoiType.getBlockStates(BlockRegistry.TEAHOUSE_FLAG.get()), 1, 5)); public static void registerPOI (){ try { ObfuscationReflectionHelper.findMethod(PoiType.class, "registerBlockStates", PoiType.class).invoke(null, COOKING_POI.get()); + ObfuscationReflectionHelper.findMethod(PoiType.class, "registerBlockStates", PoiType.class).invoke(null, TRADE_POI.get()); } catch (InvocationTargetException | IllegalAccessException e) { e.printStackTrace(); } diff --git a/src/main/java/com/voxelutopia/ultramarine/event/CommonEventHandler.java b/src/main/java/com/voxelutopia/ultramarine/event/CommonEventHandler.java index 4353d05a..98244100 100644 --- a/src/main/java/com/voxelutopia/ultramarine/event/CommonEventHandler.java +++ b/src/main/java/com/voxelutopia/ultramarine/event/CommonEventHandler.java @@ -2,17 +2,22 @@ import com.voxelutopia.ultramarine.Ultramarine; import com.voxelutopia.ultramarine.data.ModBlockTags; -import com.voxelutopia.ultramarine.data.registry.BlockRegistry; -import com.voxelutopia.ultramarine.data.registry.ItemRegistry; -import com.voxelutopia.ultramarine.data.registry.VillagerProfessionRegistry; -import com.voxelutopia.ultramarine.world.block.ChiselTableMedium; +import com.voxelutopia.ultramarine.data.registry.*; import com.voxelutopia.ultramarine.world.block.DecorativeBlock; import com.voxelutopia.ultramarine.world.block.SnowRoofRidge; import com.voxelutopia.ultramarine.world.feature.ModPlacedFeatures; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.MobSpawnType; +import net.minecraft.world.entity.SpawnPlacements; +import net.minecraft.world.entity.ai.village.poi.PoiManager; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.npc.VillagerProfession; import net.minecraft.world.entity.npc.VillagerTrades; @@ -20,12 +25,18 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.level.*; +import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.placement.PlacedFeature; +import net.minecraft.world.level.storage.ServerLevelData; import net.minecraftforge.common.Tags; +import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.entity.item.ItemExpireEvent; +import net.minecraftforge.event.entity.living.LivingSpawnEvent; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.event.village.VillagerTradesEvent; import net.minecraftforge.event.village.WandererTradesEvent; @@ -35,7 +46,10 @@ import net.minecraftforge.fml.common.Mod; import org.slf4j.Logger; +import javax.annotation.Nullable; import java.util.List; +import java.util.Optional; +import java.util.Random; @Mod.EventBusSubscriber public class CommonEventHandler { @@ -98,6 +112,66 @@ public static void roofSnowFall(BlockEvent.NeighborNotifyEvent event){ } } + //@SubscribeEvent + public static void accelerateTraderTick(TickEvent.WorldTickEvent event){ + if (!event.world.isClientSide() && event.phase == TickEvent.Phase.START){ + ServerLevel world = (ServerLevel) event.world; + ServerLevelData levelData = (ServerLevelData) world.getLevelData(); + if (world.getGameTime() % 600 == 0){ + ServerPlayer player = world.getRandomPlayer(); + if (player != null){ + EntityType.WANDERING_TRADER.spawn(world, (CompoundTag)null, (Component)null, (Player)null, player.blockPosition(), MobSpawnType.EVENT, false, false); + } + } + } + } + + @SubscribeEvent + public static void travellingMerchantSpawn(LivingSpawnEvent.SpecialSpawn event){ + if (!event.getWorld().isClientSide()){ + ServerLevel world = ((ServerLevelAccessor) event.getWorld()).getLevel(); + if (event.getEntityLiving().getType().equals(EntityType.WANDERING_TRADER) && event.getSpawnReason() == MobSpawnType.EVENT){ + ServerPlayer player = world.getRandomPlayer(); + if (player != null) { + BlockPos blockpos = player.blockPosition(); + PoiManager poiManager = world.getPoiManager(); + Optional poiPos = poiManager.find(PoiTypeRegistry.TRADE_POI.get().getPredicate(), (pos) -> true, blockpos, 16, PoiManager.Occupancy.ANY); + poiPos.ifPresent(pos -> { + + BlockPos potentialSpawn = null; + + for(int i = 0; i < 10; ++i) { + int j = pos.getX() + world.random.nextInt(4 * 2) - 4; + int k = pos.getZ() + world.random.nextInt(4 * 2) - 4; + int l = ((LevelReader) world).getHeight(Heightmap.Types.WORLD_SURFACE, j, k); + BlockPos rolledPos = new BlockPos(j, l, k); + if (NaturalSpawner.isSpawnPositionOk(SpawnPlacements.Type.ON_GROUND, world, rolledPos, EntityType.WANDERING_TRADER)) { + potentialSpawn = rolledPos; + break; + } + } + + if (potentialSpawn != null) { + + boolean hasSpaceAtSpawn = true; + for (BlockPos blockPos : BlockPos.betweenClosed(potentialSpawn, potentialSpawn.offset(1, 2, 1))) { + if (!((BlockGetter) world).getBlockState(blockPos).getCollisionShape(world, blockPos).isEmpty()) { + hasSpaceAtSpawn = false; + break; + } + } + + if (hasSpaceAtSpawn && !world.getBiome(pos).is(Biomes.THE_VOID)) { + EntityTypeRegistry.CUSTOM_WANDERING_TRADER.get().spawn( + world, null, null, null, potentialSpawn.above().above(), MobSpawnType.EVENT, false, false); + } + } + }); + } + } + } + } + @SubscribeEvent public static void villagerTradesHandler(VillagerTradesEvent event){ VillagerProfession profession = event.getType(); diff --git a/src/main/java/com/voxelutopia/ultramarine/world/entity/CustomWanderingTrader.java b/src/main/java/com/voxelutopia/ultramarine/world/entity/CustomWanderingTrader.java index 9385c9d2..d7b41fe1 100644 --- a/src/main/java/com/voxelutopia/ultramarine/world/entity/CustomWanderingTrader.java +++ b/src/main/java/com/voxelutopia/ultramarine/world/entity/CustomWanderingTrader.java @@ -1,5 +1,6 @@ package com.voxelutopia.ultramarine.world.entity; +import com.google.common.collect.ImmutableList; import com.voxelutopia.ultramarine.data.registry.ItemRegistry; import net.minecraft.network.protocol.Packet; import net.minecraft.world.entity.EntityType; @@ -44,6 +45,10 @@ public static AttributeSupplier.Builder setCustomAttributes(){ .add(Attributes.FOLLOW_RANGE, 32d); } + public static List getTradeOptions() { + return ImmutableList.copyOf(TRADE_OPTIONS); + } + @Override public Packet getAddEntityPacket() { return NetworkHooks.getEntitySpawningPacket(this); diff --git a/src/main/resources/assets/ultramarine/lang/en_us.json b/src/main/resources/assets/ultramarine/lang/en_us.json index 68fe9658..30658186 100644 --- a/src/main/resources/assets/ultramarine/lang/en_us.json +++ b/src/main/resources/assets/ultramarine/lang/en_us.json @@ -619,6 +619,7 @@ "gui.jei.category.woodworking": "Woodworking", "gui.jei.category.composite_smelting": "Composite Smelting", "gui.jei.category.chisel_table": "Chisel Table", + "gui.jei.category.custom_wandering_trader": "Custom Wandering Trader", "__EOF__": "" } \ No newline at end of file diff --git a/src/main/resources/assets/ultramarine/lang/zh_cn.json b/src/main/resources/assets/ultramarine/lang/zh_cn.json index 3222ec3a..6cd7174b 100644 --- a/src/main/resources/assets/ultramarine/lang/zh_cn.json +++ b/src/main/resources/assets/ultramarine/lang/zh_cn.json @@ -618,6 +618,7 @@ "gui.jei.category.woodworking": "木工桌", "gui.jei.category.composite_smelting": "复合烧炼", "gui.jei.category.chisel_table": "雕刻桌", + "gui.jei.category.custom_wandering_trader": "行商", "__EOF__": "" } \ No newline at end of file