Add object builders to simplify the process of creating Villager professions (#493)

* Add Object builders for simplifying the process of creating villager professions and point of interest types.

* Move everything to new object-builders-api

* flatten a bit down

* a few javadoc changes

* More JD tweaks
This commit is contained in:
i509VCB 2020-05-29 12:00:12 -07:00 committed by modmuss50
parent da175ad6ad
commit 2521f6a319
5 changed files with 365 additions and 1 deletions

View file

@ -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.
*
* <p>The texture for the villagers are located at <code>assets/IDENTIFIER_NAMESPACE/textures/entity/villager/profession/IDENTIFIER_PATH.png</code>
*
* <p>A corresponding <code>IDENTIFIER_PATH.mcmeta</code> file exits in the same directory to define properties such as the {@link VillagerResourceMetadata.HatType HatType} this profession would use.
*
* <p>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<Item> gatherableItemsBuilder = ImmutableSet.builder();
private final ImmutableSet.Builder<Block> 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.
*
* <p>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.
*
* <p>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<Item> items) {
this.gatherableItemsBuilder.addAll(items);
return this;
}
/**
* A collection of blocks blocks which may suffice as a secondary job site for a Villager.
*
* <p>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.
*
* <p>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<Block> 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);
}
}

View file

@ -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}.
*
* <p>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<BlockState> 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<PointOfInterestType> completionCondition, int searchDistance, Block... blocks) {
final ImmutableSet.Builder<BlockState> 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<BlockState> blocks) {
final ImmutableSet.Builder<BlockState> 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<PointOfInterestType> typePredicate, int searchDistance, Iterable<BlockState> states) {
final ImmutableSet.Builder<BlockState> 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<BlockState> 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<PointOfInterestType> typePredicate, int searchDistance, Set<BlockState> states) {
return Registry.register(Registry.POINT_OF_INTEREST_TYPE, id, PointOfInterestTypeAccessor.callSetup(
PointOfInterestTypeAccessor.callCreate(id.toString(), states, ticketCount, typePredicate, searchDistance)));
}
}

View file

@ -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("<init>")
static PointOfInterestType callCreate(String id, Set<BlockState> blockStates, int ticketCount, Predicate<PointOfInterestType> typePredicate, int searchDistance) {
throw new AssertionError("Untransformed Accessor!");
}
@Invoker("<init>")
static PointOfInterestType callCreate(String id, Set<BlockState> blockStates, int ticketCount, int searchDistance) {
throw new AssertionError("Untransformed Accessor!");
}
@Invoker("setup")
static PointOfInterestType callSetup(PointOfInterestType pointOfInterestType) {
throw new AssertionError("Untransformed Accessor!");
}
}

View file

@ -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("<init>")
static VillagerProfession create(String id, PointOfInterestType type, ImmutableSet<Item> gatherableItems, ImmutableSet<Block> secondaryJobSites, /* @Nullable */ SoundEvent soundEvent) {
throw new AssertionError("Untransformed accessor!");
}
}

View file

@ -9,7 +9,9 @@
"DefaultAttributeRegistryAccessor", "DefaultAttributeRegistryAccessor",
"DefaultAttributeRegistryMixin", "DefaultAttributeRegistryMixin",
"MaterialBuilderAccessor", "MaterialBuilderAccessor",
"MixinBlock" "MixinBlock",
"PointOfInterestTypeAccessor",
"VillagerProfessionAccessor"
], ],
"client": [ "client": [
"ModelPredicateProviderRegistryAccessor", "ModelPredicateProviderRegistryAccessor",