Skip to content

Adding Custom Behaviors

GizmoTheMoonPig edited this page Apr 3, 2024 · 1 revision

If you're a mod maker and you want to add your own behaviors, you can register a new behavior via the CUSTOM_BEHAVIORS registry. Custom behaviors must implement the CustomBehavior class and must have a codec that defines how they're formatted in json. You can look at how all my behaviors are registered here.

Creating the base class

Let's say you want to create a behavior that sets a certain statistic to a certain value. You would start by creating a new class that implements CustomBehavior, and would need to override 2 methods: getType() and execute(TrophyBlockEntity, ServerPlayer, ItemStack). Lets go over each method in detail.

  • getType() returns the Registry Object for your behavior. This is so the behavior actually knows what it is and can easily read and write from jsons. The registration will be explained a bit later.
  • execute() returns an integer which acts as the behavior cooldown. This method is where you should do all the logic when the trophy is right clicked and then return the cooldown if necessary. You are given the trophy, the player, and the stack used to click on the trophy to use as you please.

Creating a codec

I'll be assuming you know how to write codecs if youre reading this, but if not theres plenty of resources online that explain how they work. Here's a very good guide by my friend Drullkus about how codecs are structured and how they function. (You can also ask me about them, happy to help anyone who needs it!)

I've written an example codec for a class called ChangeStatisticBehavior that takes in a ResourceLocation, an Integer, and a Boolean:

public static final Codec<ChangeStatisticBehavior> CODEC = RecordCodecBuilder.create(instance -> instance.group(
	ResourceLocation.CODEC.fieldOf("statistic").forGetter(ChangeStatisticBehavior::stat),
	Codec.INT.fieldOf("value").forGetter(ChangeStatisticBehavior::value),
	Codec.BOOL.optionalFieldOf("add", false).forGetter(ChangeStatisticBehavior::add)
).apply(instance, ChangeStatisticBehavior::new));

Ive marked the boolean as optional here to show that you can make not all fields required when parsing this in your json. If a mod/datapack doesn't add the add field to their json it will default to false.

A codec is absolutely REQUIRED for this as the mod needs to know how to format your behavior in json format.

Adding a Registry Object

Adding a registry object for your behavior is relatively easy. You will need to create a DeferredRegister for the CUSTOM_BEHAVIORS registry located in OpenBlocksTrophies, add your entry to it, and call the registry to register in your mod constructor. Your registry class for this may look something like this:

public static final DeferredRegister<CustomBehaviorType> CUSTOM_BEHAVIORS = DeferredRegister.create(OpenBlocksTrophies.CUSTOM_BEHAVIORS_KEY, YOUR_MOD_ID);

public static final DeferredHolder<CustomBehaviorType, CustomBehaviorType> CHANGE_STATISTIC = CUSTOM_BEHAVIORS.register("change_statistic", () -> new CustomBehaviorType(ChangeStatisticBehavior.CODEC));

You would then call YourClassName.CUSTOM_BEHAVIORS.register(bus) in your mod constructor like so.

The finished product

Here's the class I put together for this example, just so you can see how everything looks put together:

public record ChangeStatisticBehavior(ResourceLocation stat, int value, boolean add) implements CustomBehavior {

	public static final Codec<ChangeStatisticBehavior> CODEC = RecordCodecBuilder.create(instance -> instance.group(
			ResourceLocation.CODEC.fieldOf("statistic").forGetter(ChangeStatisticBehavior::stat),
			Codec.INT.fieldOf("value").forGetter(ChangeStatisticBehavior::value),
			Codec.BOOL.optionalFieldOf("add", false).forGetter(ChangeStatisticBehavior::add)
	).apply(instance, ChangeStatisticBehavior::new));

	@Override
	public CustomBehaviorType getType() {
		return CustomTrophyBehaviors.CHANGE_STATISTIC.get();
	}

	@Override
	public int execute(TrophyBlockEntity block, ServerPlayer player, ItemStack usedItem) {
		if (Stats.CUSTOM.contains(this.stat())) {
			if (this.add()) {
				player.awardStat(this.stat(), this.value());
			} else {
				player.getStats().setValue(player, Stats.CUSTOM.get(this.stat()), this.value());
			}
		}
		return 0;
	}
}

Feel free to reach out to me with any questions!