Skip to content

Commit

Permalink
Reworked the Badge system, added badge for RecipePower (apace100#537)
Browse files Browse the repository at this point in the history
* Reworked the Badge system, added badge for RecipePower

* Added AutoBadgeCallback

* Reworked the BadgeManager using DataObjectRegistry

* Optimized subpower badge loading logic

* Optimized subpower badge loading logic

* Optimized imports

* Fixed subpower badge loading logic
  • Loading branch information
RaymondBlaze authored May 19, 2022
1 parent 7c77c59 commit 36d44b3
Show file tree
Hide file tree
Showing 45 changed files with 704 additions and 257 deletions.
6 changes: 2 additions & 4 deletions src/main/java/io/github/apace100/origins/Origins.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import io.github.apace100.apoli.util.NamespaceAlias;
import io.github.apace100.calio.mixin.CriteriaRegistryInvoker;
import io.github.apace100.calio.util.OrderedResourceListeners;
import io.github.apace100.origins.badge.BadgeManager;
import io.github.apace100.origins.command.LayerArgumentType;
import io.github.apace100.origins.command.OriginArgumentType;
import io.github.apace100.origins.command.OriginCommand;
Expand All @@ -17,7 +18,6 @@
import io.github.apace100.origins.power.OriginsEntityConditions;
import io.github.apace100.origins.power.OriginsPowerTypes;
import io.github.apace100.origins.registry.*;
import io.github.apace100.origins.screen.BadgeManager;
import io.github.apace100.origins.util.ChoseOriginCriterion;
import io.github.apace100.origins.util.OriginsConfigSerializer;
import io.github.apace100.origins.util.OriginsJsonConfigSerializer;
Expand Down Expand Up @@ -45,8 +45,6 @@ public class Origins implements ModInitializer {
public static ServerConfig config;
private static ConfigSerializer<ServerConfig> configSerializer;

public static BadgeManager badgeManager;

@Override
public void onInitialize() {
FabricLoader.getInstance().getModContainer(MODID).ifPresent(modContainer -> {
Expand Down Expand Up @@ -97,7 +95,7 @@ public void onInitialize() {
OrderedResourceListeners.register(new OriginManager()).after(new Identifier("apoli", "powers")).complete();
OrderedResourceListeners.register(new OriginLayers()).after(new Identifier(Origins.MODID, "origins")).complete();

badgeManager = new BadgeManager();
BadgeManager.init();
}

public static void serializeConfig() {
Expand Down
39 changes: 39 additions & 0 deletions src/main/java/io/github/apace100/origins/badge/Badge.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package io.github.apace100.origins.badge;

import io.github.apace100.calio.data.SerializableData;
import io.github.apace100.calio.registry.DataObject;
import io.github.apace100.calio.registry.DataObjectFactory;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.tooltip.TooltipComponent;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.util.Identifier;

import java.util.List;

public interface Badge extends DataObject<Badge> {

Identifier spriteId();

boolean hasTooltip();

@Environment(EnvType.CLIENT)
List<TooltipComponent> getTooltipComponents(TextRenderer textRenderer, int widthLimit, float time);

SerializableData.Instance toData(SerializableData.Instance instance);

BadgeFactory getBadgeFactory();

@Override
default DataObjectFactory<Badge> getFactory() {
return this.getBadgeFactory();
}

default void writeBuf(PacketByteBuf buf) {
DataObjectFactory<Badge> factory = this.getFactory();
buf.writeIdentifier(this.getBadgeFactory().id());
factory.getData().write(buf, factory.toData(this));
}

}
28 changes: 28 additions & 0 deletions src/main/java/io/github/apace100/origins/badge/BadgeFactories.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.github.apace100.origins.badge;

import io.github.apace100.calio.data.SerializableData;
import io.github.apace100.calio.data.SerializableDataTypes;
import io.github.apace100.origins.Origins;

public final class BadgeFactories {

public static final BadgeFactory SPRITE = new BadgeFactory(Origins.identifier("sprite"),
new SerializableData()
.add("sprite", SerializableDataTypes.IDENTIFIER),
SpriteBadge::new);

public static final BadgeFactory TOOLTIP = new BadgeFactory(Origins.identifier("tooltip"),
new SerializableData()
.add("sprite", SerializableDataTypes.IDENTIFIER)
.add("text", SerializableDataTypes.TEXT),
TooltipBadge::new);

public static final BadgeFactory CRAFTING_RECIPE = new BadgeFactory(Origins.identifier("crafting_recipe"),
new SerializableData()
.add("sprite", SerializableDataTypes.IDENTIFIER)
.add("recipe", SerializableDataTypes.RECIPE)
.add("prefix", SerializableDataTypes.TEXT, null)
.add("suffix", SerializableDataTypes.TEXT, null),
CraftingRecipeBadge::new);

}
26 changes: 26 additions & 0 deletions src/main/java/io/github/apace100/origins/badge/BadgeFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.github.apace100.origins.badge;

import io.github.apace100.calio.data.SerializableData;
import io.github.apace100.calio.registry.DataObjectFactory;
import net.minecraft.util.Identifier;

import java.util.function.Function;

public record BadgeFactory(Identifier id, SerializableData data, Function<SerializableData.Instance, Badge> factory) implements DataObjectFactory<Badge> {

@Override
public SerializableData getData() {
return data;
}

@Override
public Badge fromData(SerializableData.Instance instance) {
return factory.apply(instance);
}

@Override
public SerializableData.Instance toData(Badge badge) {
return badge.toData(data.new Instance());
}

}
156 changes: 156 additions & 0 deletions src/main/java/io/github/apace100/origins/badge/BadgeManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package io.github.apace100.origins.badge;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.github.apace100.apoli.integration.PostPowerLoadCallback;
import io.github.apace100.apoli.integration.PostPowerReloadCallback;
import io.github.apace100.apoli.integration.PrePowerReloadCallback;
import io.github.apace100.apoli.power.*;
import io.github.apace100.calio.registry.DataObjectRegistry;
import io.github.apace100.origins.Origins;
import io.github.apace100.origins.integration.AutoBadgeCallback;
import io.github.apace100.origins.networking.ModPackets;
import io.github.apace100.origins.util.PowerKeyManager;
import io.netty.buffer.Unpooled;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.inventory.CraftingInventory;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.ShapedRecipe;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Identifier;

import java.util.*;

public final class BadgeManager {
public static final DataObjectRegistry<Badge> REGISTRY = new DataObjectRegistry.Builder<>(Origins.identifier("badge"), Badge.class)
.readFromData("badges", true)
.dataErrorHandler((id, exception) -> Origins.LOGGER.error("Failed to read badge " + ", caused by", exception))
.defaultFactory(BadgeFactories.TOOLTIP)
.buildAndRegister();
private static final Map<Identifier, List<Badge>> BADGES = new HashMap<>();
private static final Map<Identifier, List<Identifier>> MULTIPLE_POWERS = new HashMap<>();

private static final Identifier TOGGLE_BADGE_SPRITE = Origins.identifier("textures/gui/badge/toggle.png");
private static final Identifier ACTIVE_BADGE_SPRITE = Origins.identifier("textures/gui/badge/active.png");
private static final Identifier RECIPE_BADGE_SPRITE = Origins.identifier("textures/gui/badge/recipe.png");

public static void init() {
//register builtin badge types
register(BadgeFactories.SPRITE);
register(BadgeFactories.TOOLTIP);
register(BadgeFactories.CRAFTING_RECIPE);
//register callbacks
PrePowerReloadCallback.EVENT.register(BadgeManager::clear);
PowerTypes.registerAdditionalData("badges", BadgeManager::readCustomBadges);
PostPowerLoadCallback.EVENT.register(BadgeManager::readAutoBadges);
AutoBadgeCallback.EVENT.register(BadgeManager::createAutoBadges);
PostPowerReloadCallback.EVENT.register(BadgeManager::mergeMultiplePowerBadges);
}

public static void register(BadgeFactory factory) {
REGISTRY.registerFactory(factory.id(), factory);
}

public static void putPowerBadge(Identifier powerId, Badge badge) {
List<Badge> badgeList = BADGES.computeIfAbsent(powerId, id -> new LinkedList<>());
badgeList.add(badge);
}

public static List<Badge> getPowerBadges(Identifier powerId) {
return BADGES.computeIfAbsent(powerId, id -> new LinkedList<>());
}

public static void clear() {
BADGES.clear();
}

public static void sync(ServerPlayerEntity player) {
REGISTRY.sync(player);
PacketByteBuf badgeData = new PacketByteBuf(Unpooled.buffer());
badgeData.writeInt(BADGES.size());
BADGES.forEach((id, list) -> {
badgeData.writeIdentifier(id);
badgeData.writeInt(list.size());
list.forEach(badge -> badge.writeBuf(badgeData));
});
ServerPlayNetworking.send(player, ModPackets.BADGE_LIST, badgeData);
}

public static void readCustomBadges(Identifier powerId, Identifier factoryId, boolean isSubPower, JsonElement data, PowerType<?> powerType) {
if(!powerType.isHidden() || isSubPower) {
if(data.isJsonArray()) {
for(JsonElement badgeJson : data.getAsJsonArray()) {
if(badgeJson.isJsonPrimitive()) {
Identifier badgeId = Identifier.tryParse(badgeJson.getAsString());
if(badgeId != null) {
Badge badge = REGISTRY.get(badgeId);
if(badge != null) {
putPowerBadge(powerId, badge);
} else {
Origins.LOGGER.error("\"badges\" field in power \"{}\" is referring a undefined badge \"{}\" !", powerId, badgeId);
}
} else {
Origins.LOGGER.error("\"badges\" field in power \"{}\" is not a valid identifier!", powerId);
}
} else if(badgeJson.isJsonObject()) {
try {
putPowerBadge(powerId, REGISTRY.readDataObject(badgeJson));
} catch(Exception exception) {
Origins.LOGGER.error("\"badges\" field in power \"" + powerId
+ "\" contained an JSON object entry that cannot be resolved!", exception);
}
} else {
Origins.LOGGER.error("\"badges\" field in power \"" + powerId
+ "\" contained an entry that was a JSON array, which is not allowed!");
}
}
} else {
Origins.LOGGER.error("\"badges\" field in power \"" + powerId + "\" should be an array.");
}
}
}

public static void readAutoBadges(Identifier powerId, Identifier factoryId, boolean isSubPower, JsonObject json, PowerType<?> powerType) {
if(powerType instanceof MultiplePowerType<?> mp) {
MULTIPLE_POWERS.put(powerId, mp.getSubPowers());
} else if((!BADGES.containsKey(powerId) || BADGES.get(powerId).size() == 0) && (!powerType.isHidden() || isSubPower)) {
AutoBadgeCallback.EVENT.invoker().createAutoBadge(powerId, powerType);
}
}

public static void createAutoBadges(Identifier powerId, PowerType<?> powerType) {
Power power = powerType.create(null);
if(power instanceof Active active) {
boolean toggle = active instanceof TogglePower || active instanceof ToggleNightVisionPower;
Text keyText = new LiteralText("[")
.append(KeyBinding.getLocalizedName(PowerKeyManager.getKeyIdentifier(powerId)).get())
.append(new LiteralText("]"));
putPowerBadge(powerId, new TooltipBadge(toggle ? TOGGLE_BADGE_SPRITE : ACTIVE_BADGE_SPRITE,
toggle ? new TranslatableText("origins.gui.badge.toggle", keyText)
: new TranslatableText("origins.gui.badge.active", keyText)
));
} else if(power instanceof RecipePower recipePower) {
Recipe<CraftingInventory> recipe = recipePower.getRecipe();
String type = recipe instanceof ShapedRecipe ? "shaped" : "shapeless";
putPowerBadge(powerId, new CraftingRecipeBadge(RECIPE_BADGE_SPRITE, recipe,
new TranslatableText("origins.gui.badge.recipe.crafting." + type), null
));
}
}

public static void mergeMultiplePowerBadges() {
MULTIPLE_POWERS.forEach((powerId, subPowerIds) ->
subPowerIds.forEach(subPowerId ->
BADGES.computeIfAbsent(powerId, id -> new LinkedList<>())
.addAll(BADGES.computeIfAbsent(subPowerId, id -> new LinkedList<>()))
)
);
MULTIPLE_POWERS.clear();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package io.github.apace100.origins.badge;

import io.github.apace100.calio.data.SerializableData;
import io.github.apace100.origins.screen.tooltip.CraftingRecipeTooltipComponent;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.tooltip.TooltipComponent;
import net.minecraft.inventory.CraftingInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.Recipe;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
import net.minecraft.util.collection.DefaultedList;
import net.minecraft.util.math.MathHelper;

import javax.annotation.Nullable;
import java.util.LinkedList;
import java.util.List;

public record CraftingRecipeBadge(Identifier spriteId,
Recipe<CraftingInventory> recipe,
@Nullable Text prefix,
@Nullable Text suffix) implements Badge {

public CraftingRecipeBadge(SerializableData.Instance instance) {
this(instance.getId("sprite"),
instance.get("recipe"),
instance.get("prefix"),
instance.get("suffix"));
}

@Override
public boolean hasTooltip() {
return true;
}

public DefaultedList<ItemStack> peekInputs(float time) {
int seed = MathHelper.floor(time / 30);
DefaultedList<ItemStack> inputs = DefaultedList.ofSize(9, ItemStack.EMPTY);
List<Ingredient> ingredients = this.recipe.getIngredients();
for(int index = 0; index < ingredients.size(); ++index) {
ItemStack[] stacks = ingredients.get(index).getMatchingStacks();
if(stacks.length > 0) inputs.set(index, stacks[seed % stacks.length]);
}
return inputs;
}

@Override
public List<TooltipComponent> getTooltipComponents(TextRenderer textRenderer, int widthLimit, float time) {
List<TooltipComponent> tooltips = new LinkedList<>();
if(MinecraftClient.getInstance().options.advancedItemTooltips) {
Text recipeIdText = new LiteralText(recipe.getId().toString()).formatted(Formatting.DARK_GRAY);
widthLimit = Math.max(130, textRenderer.getWidth(recipeIdText));
if(prefix != null) TooltipBadge.addLines(tooltips, prefix, textRenderer, widthLimit);
tooltips.add(new CraftingRecipeTooltipComponent(this.peekInputs(time), this.recipe.getOutput()));
if(suffix != null) TooltipBadge.addLines(tooltips, suffix, textRenderer, widthLimit);
TooltipBadge.addLines(tooltips, recipeIdText, textRenderer, widthLimit);
} else {
widthLimit = 130;
if(prefix != null) TooltipBadge.addLines(tooltips, prefix, textRenderer, widthLimit);
tooltips.add(new CraftingRecipeTooltipComponent(this.peekInputs(time), this.recipe.getOutput()));
if(suffix != null) TooltipBadge.addLines(tooltips, suffix, textRenderer, widthLimit);
}
return tooltips;
}

@Override
public SerializableData.Instance toData(SerializableData.Instance instance) {
instance.set("sprite", spriteId);
instance.set("recipe", recipe);
instance.set("prefix", prefix);
instance.set("suffix", suffix);
return instance;
}

@Override
public BadgeFactory getBadgeFactory() {
return BadgeFactories.CRAFTING_RECIPE;
}

}
Loading

0 comments on commit 36d44b3

Please sign in to comment.