Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reworked the Badge system, added badge for RecipePower #537

Merged
merged 7 commits into from
May 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -3,6 +3,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 @@ -13,7 +14,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 me.shedaniel.autoconfig.AutoConfig;
Expand All @@ -37,8 +37,6 @@ public class Origins implements ModInitializer {

public static ServerConfig config;

public static BadgeManager badgeManager;

@Override
public void onInitialize() {
FabricLoader.getInstance().getModContainer(MODID).ifPresent(modContainer -> {
Expand Down Expand Up @@ -83,7 +81,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 Identifier identifier(String path) {
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