diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/villager/VillagerProfessionBuilder.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/villager/VillagerProfessionBuilder.java new file mode 100644 index 000000000..38a3111f2 --- /dev/null +++ b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/villager/VillagerProfessionBuilder.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.api.object.builder.v1.villager; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.collect.ImmutableSet; + +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.client.render.entity.feature.VillagerResourceMetadata; +import net.minecraft.item.Item; +import net.minecraft.sound.SoundEvent; +import net.minecraft.util.Identifier; +import net.minecraft.village.TradeOffers; +import net.minecraft.village.VillagerProfession; +import net.minecraft.world.poi.PointOfInterestType; + +import net.fabricmc.fabric.mixin.object.builder.VillagerProfessionAccessor; + +/** + * Allows for the creation of new {@link VillagerProfession}s. + * + *

The texture for the villagers are located at assets/IDENTIFIER_NAMESPACE/textures/entity/villager/profession/IDENTIFIER_PATH.png + * + *

A corresponding IDENTIFIER_PATH.mcmeta file exits in the same directory to define properties such as the {@link VillagerResourceMetadata.HatType HatType} this profession would use. + * + *

Note this does not register any trades to these villagers. To register trades, add a new entry with your profession as the key to {@link TradeOffers#PROFESSION_TO_LEVELED_TRADE}. + */ +public final class VillagerProfessionBuilder { + private final ImmutableSet.Builder gatherableItemsBuilder = ImmutableSet.builder(); + private final ImmutableSet.Builder secondaryJobSiteBlockBuilder = ImmutableSet.builder(); + private Identifier identifier; + private PointOfInterestType pointOfInterestType; + /* @Nullable */ + private SoundEvent workSoundEvent; + + private VillagerProfessionBuilder() { + } + + /** + * Creates a builder instance to allow for creation of a {@link VillagerProfession}. + * @return A new builder. + */ + static VillagerProfessionBuilder create() { + return new VillagerProfessionBuilder(); + } + + /** + * The Identifier used to identify this villager profession. + * + * @param id The identifier to assign to this profession. + * @return this builder + */ + public VillagerProfessionBuilder id(Identifier id) { + this.identifier = id; + return this; + } + + /** + * The {@link PointOfInterestType} the Villager of this profession will search for when finding a workstation. + * + * @param type The {@link PointOfInterestType} the Villager will attempt to find. + * @return this builder. + */ + public VillagerProfessionBuilder workstation(PointOfInterestType type) { + this.pointOfInterestType = type; + return this; + } + + /** + * Items that a Villager may harvest in this profession. + * + *

In Vanilla, this is used by the farmer to define what type of crops the farmer can harvest. + * + * @param items Items harvestable by this profession. + * @return this builder. + */ + public VillagerProfessionBuilder harvestableItems(Item... items) { + this.gatherableItemsBuilder.add(items); + return this; + } + + /** + * Items that a Villager may harvest in this profession. + * + *

In Vanilla, this is used by the farmer to define what type of crops the farmer can harvest. + * + * @param items Items harvestable by this profession. + * @return this builder. + */ + public VillagerProfessionBuilder harvestableItems(Iterable items) { + this.gatherableItemsBuilder.addAll(items); + return this; + } + + /** + * A collection of blocks blocks which may suffice as a secondary job site for a Villager. + * + *

In Vanilla, this is used by the {@link VillagerProfession#FARMER Farmer} to stay near {@link Blocks#FARMLAND Farmland} when at it's job site. + * + * @param blocks Collection of secondary job site blocks. + * @return this builder. + */ + public VillagerProfessionBuilder secondaryJobSites(Block... blocks) { + this.secondaryJobSiteBlockBuilder.add(blocks); + return this; + } + + /** + * A collection of blocks blocks which may suffice as a secondary job site for a Villager. + * + *

In Vanilla, this is used by the {@link VillagerProfession#FARMER Farmer} to stay near {@link Blocks#FARMLAND Farmland} when at it's job site. + * + * @param blocks Collection of secondary job site blocks. + * @return this builder. + */ + public VillagerProfessionBuilder secondaryJobSites(Iterable blocks) { + this.secondaryJobSiteBlockBuilder.addAll(blocks); + return this; + } + + /** + * Provides the sound made when a Villager works. + * + * @param workSoundEvent The {@link SoundEvent} to be played. + * @return this builder. + */ + public VillagerProfessionBuilder workSound(/* @Nullable */ SoundEvent workSoundEvent) { + this.workSoundEvent = workSoundEvent; + return this; + } + + /** + * Creates the {@link VillagerProfession}. + * + * @return a new {@link VillagerProfession}. + * @throws IllegalStateException if the builder is missing an {@link Identifier id} and {@link PointOfInterestType workstation}. + */ + public VillagerProfession build() { + checkState(this.identifier != null, "An Identifier is required to build a new VillagerProfession."); + checkState(this.pointOfInterestType != null, "A PointOfInterestType is required to build a new VillagerProfession."); + return VillagerProfessionAccessor.create(this.identifier.toString(), this.pointOfInterestType, this.gatherableItemsBuilder.build(), this.secondaryJobSiteBlockBuilder.build(), this.workSoundEvent); + } +} diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/world/poi/PointOfInterestHelper.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/world/poi/PointOfInterestHelper.java new file mode 100644 index 000000000..b8cf5516b --- /dev/null +++ b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/world/poi/PointOfInterestHelper.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.api.object.builder.v1.world.poi; + +import java.util.Set; +import java.util.function.Predicate; + +import com.google.common.collect.ImmutableSet; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.poi.PointOfInterest; +import net.minecraft.world.poi.PointOfInterestType; + +import net.fabricmc.fabric.mixin.object.builder.PointOfInterestTypeAccessor; + +/** + * This class provides utilities to create a {@link PointOfInterestType}. + * + *

A point of interest is typically used by villagers to specify their workstation blocks, meeting zones and homes. + * Points of interest are also used by bees to specify where their bee hive is and nether portals to find existing portals. + */ +public final class PointOfInterestHelper { + private PointOfInterestHelper() { + } + + /** + * Creates and registers a {@link PointOfInterestType}. + * + * @param id The id of this {@link PointOfInterestType}. + * @param ticketCount the amount of tickets. + * @param searchDistance the search distance. + * @param blocks all the blocks where a {@link PointOfInterest} of this type will be present. + * @return a new {@link PointOfInterestType}. + */ + public static PointOfInterestType register(Identifier id, int ticketCount, int searchDistance, Block... blocks) { + final ImmutableSet.Builder builder = ImmutableSet.builder(); + + for (Block block : blocks) { + builder.addAll(block.getStateManager().getStates()); + } + + return register(id, ticketCount, searchDistance, builder.build()); + } + + /** + * Creates and registers a {@link PointOfInterestType}. + * + * @param id The id of this {@link PointOfInterestType}. + * @param ticketCount the amount of tickets. + * @param completionCondition a {@link Predicate} which determines if two {@link PointOfInterestType}s are the same. + * @param searchDistance the search distance. + * @param blocks all blocks where a {@link PointOfInterest} of this type will be present + * @return a new {@link PointOfInterestType}. + */ + public static PointOfInterestType register(Identifier id, int ticketCount, Predicate completionCondition, int searchDistance, Block... blocks) { + final ImmutableSet.Builder builder = ImmutableSet.builder(); + + for (Block block : blocks) { + builder.addAll(block.getStateManager().getStates()); + } + + return register(id, ticketCount, completionCondition, searchDistance, builder.build()); + } + + /** + * Creates and registers a {@link PointOfInterestType}. + * + * @param id the id of this {@link PointOfInterestType}. + * @param ticketCount the amount of tickets. + * @param searchDistance the search distance. + * @param blocks all {@link BlockState block states} where a {@link PointOfInterest} of this type will be present + * @return a new {@link PointOfInterestType}. + */ + public static PointOfInterestType register(Identifier id, int ticketCount, int searchDistance, Iterable blocks) { + final ImmutableSet.Builder builder = ImmutableSet.builder(); + + return register(id, ticketCount, searchDistance, builder.addAll(blocks).build()); + } + + /** + * Creates and registers a {@link PointOfInterestType}. + * + * @param id the id of this {@link PointOfInterestType}. + * @param ticketCount the amount of tickets. + * @param typePredicate a {@link Predicate} which determines if two {@link PointOfInterestType}s are the same. + * @param searchDistance the search distance. + * @param states all {@link BlockState block states} where a {@link PointOfInterest} of this type will be present + * @return a new {@link PointOfInterestType}. + */ + public static PointOfInterestType register(Identifier id, int ticketCount, Predicate typePredicate, int searchDistance, Iterable states) { + final ImmutableSet.Builder builder = ImmutableSet.builder(); + + return register(id, ticketCount, typePredicate, searchDistance, builder.addAll(states).build()); + } + + // INTERNAL METHODS + + private static PointOfInterestType register(Identifier id, int ticketCount, int searchDistance, Set states) { + return Registry.register(Registry.POINT_OF_INTEREST_TYPE, id, PointOfInterestTypeAccessor.callSetup( + PointOfInterestTypeAccessor.callCreate(id.toString(), states, ticketCount, searchDistance))); + } + + private static PointOfInterestType register(Identifier id, int ticketCount, Predicate typePredicate, int searchDistance, Set states) { + return Registry.register(Registry.POINT_OF_INTEREST_TYPE, id, PointOfInterestTypeAccessor.callSetup( + PointOfInterestTypeAccessor.callCreate(id.toString(), states, ticketCount, typePredicate, searchDistance))); + } +} diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/PointOfInterestTypeAccessor.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/PointOfInterestTypeAccessor.java new file mode 100644 index 000000000..c3c9df89d --- /dev/null +++ b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/PointOfInterestTypeAccessor.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.object.builder; + +import java.util.Set; +import java.util.function.Predicate; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import net.minecraft.block.BlockState; +import net.minecraft.world.poi.PointOfInterestType; + +@Mixin(PointOfInterestType.class) +public interface PointOfInterestTypeAccessor { + @Invoker("") + static PointOfInterestType callCreate(String id, Set blockStates, int ticketCount, Predicate typePredicate, int searchDistance) { + throw new AssertionError("Untransformed Accessor!"); + } + + @Invoker("") + static PointOfInterestType callCreate(String id, Set blockStates, int ticketCount, int searchDistance) { + throw new AssertionError("Untransformed Accessor!"); + } + + @Invoker("setup") + static PointOfInterestType callSetup(PointOfInterestType pointOfInterestType) { + throw new AssertionError("Untransformed Accessor!"); + } +} diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/VillagerProfessionAccessor.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/VillagerProfessionAccessor.java new file mode 100644 index 000000000..46bfb6c2a --- /dev/null +++ b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/VillagerProfessionAccessor.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.object.builder; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; +import com.google.common.collect.ImmutableSet; + +import net.minecraft.block.Block; +import net.minecraft.item.Item; +import net.minecraft.sound.SoundEvent; +import net.minecraft.village.VillagerProfession; +import net.minecraft.world.poi.PointOfInterestType; + +@Mixin(VillagerProfession.class) +public interface VillagerProfessionAccessor { + @Invoker("") + static VillagerProfession create(String id, PointOfInterestType type, ImmutableSet gatherableItems, ImmutableSet secondaryJobSites, /* @Nullable */ SoundEvent soundEvent) { + throw new AssertionError("Untransformed accessor!"); + } +} diff --git a/fabric-object-builder-api-v1/src/main/resources/fabric-object-builder-v1.mixins.json b/fabric-object-builder-api-v1/src/main/resources/fabric-object-builder-v1.mixins.json index 0378b7bdc..866ca7b76 100644 --- a/fabric-object-builder-api-v1/src/main/resources/fabric-object-builder-v1.mixins.json +++ b/fabric-object-builder-api-v1/src/main/resources/fabric-object-builder-v1.mixins.json @@ -9,7 +9,9 @@ "DefaultAttributeRegistryAccessor", "DefaultAttributeRegistryMixin", "MaterialBuilderAccessor", - "MixinBlock" + "MixinBlock", + "PointOfInterestTypeAccessor", + "VillagerProfessionAccessor" ], "client": [ "ModelPredicateProviderRegistryAccessor",