Added Trinkets support

This commit is contained in:
Sollace 2022-09-20 23:50:15 +02:00
parent 0e74cc6bbe
commit 32a23a6ca5
24 changed files with 445 additions and 136 deletions

View file

@ -39,6 +39,14 @@ repositories {
name = 'minelp-release'
url = 'https://repo.minelittlepony-mod.com/maven/release'
}
maven {
name = "TerraformersMC"
url = "https://maven.terraformersmc.com/"
}
maven {
name = "Ladysnake Libs"
url = "https://ladysnake.jfrog.io/artifactory/mods"
}
}
dependencies {
@ -55,6 +63,8 @@ dependencies {
modCompileOnly("com.terraformersmc:modmenu:${project.modmenu_version}")
// implementation 'org.jetbrains:intellij-fernflower:1.2.1.16'
modCompileOnly("net.caffienemc.sodium:sodium-fabric-mc1.19.2:0.4.4+build.18")
modCompileOnly "dev.emi:trinkets:3.4.0"
}
processResources {

View file

@ -10,7 +10,7 @@ import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.data.Pos;
import com.minelittlepony.unicopia.ability.data.tree.TreeType;
import com.minelittlepony.unicopia.block.data.BlockDestructionManager;
import com.minelittlepony.unicopia.client.minelittlepony.MineLPConnector;
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.ParticleUtils;
@ -54,7 +54,7 @@ public class EarthPonyKickAbility implements Ability<Pos> {
@Override
public double getCostEstimate(Pony player) {
double distance = MineLPConnector.getPlayerPonyRace(player.getMaster()).isDefault() ? 6 : -6;
double distance = MineLPDelegate.getInstance().getPlayerPonyRace(player.getMaster()).isDefault() ? 6 : -6;
return RayTraceHelper.doTrace(player.getMaster(), distance, 1)
.getBlockPos()
@ -65,7 +65,7 @@ public class EarthPonyKickAbility implements Ability<Pos> {
@Nullable
@Override
public Pos tryActivate(Pony player) {
double distance = MineLPConnector.getPlayerPonyRace(player.getMaster()).isDefault() ? 6 : -6;
double distance = MineLPDelegate.getInstance().getPlayerPonyRace(player.getMaster()).isDefault() ? 6 : -6;
return RayTraceHelper.doTrace(player.getMaster(), distance, 1)
.getBlockPos()
@ -76,7 +76,7 @@ public class EarthPonyKickAbility implements Ability<Pos> {
private Pos getDefaultKickLocation(Pony player) {
Vec3d kickVector = player.getMaster().getRotationVector().multiply(1, 0, 1);
if (!MineLPConnector.getPlayerPonyRace(player.getMaster()).isDefault()) {
if (!MineLPDelegate.getInstance().getPlayerPonyRace(player.getMaster()).isDefault()) {
kickVector = kickVector.rotateY((float)Math.PI);
}
return new Pos(new BlockPos(player.getOriginVector().add(kickVector)));

View file

@ -11,7 +11,7 @@ import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.client.gui.LanSettingsScreen;
import com.minelittlepony.unicopia.client.gui.UHud;
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen;
import com.minelittlepony.unicopia.client.minelittlepony.MineLPConnector;
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
import com.minelittlepony.unicopia.container.*;
import com.minelittlepony.unicopia.entity.player.PlayerCamera;
import com.minelittlepony.unicopia.entity.player.Pony;
@ -41,7 +41,7 @@ public class UnicopiaClient implements ClientModInitializer {
public static Race getPreferredRace() {
if (!Unicopia.getConfig().ignoreMineLP.get()
&& MinecraftClient.getInstance().player != null) {
Race race = MineLPConnector.getPlayerPonyRace();
Race race = MineLPDelegate.getInstance().getPlayerPonyRace();
if (!race.isDefault()) {
return race;

View file

@ -7,7 +7,7 @@ import com.minelittlepony.common.client.gui.ScrollContainer;
import com.minelittlepony.common.client.gui.element.*;
import com.minelittlepony.common.client.gui.style.Style;
import com.minelittlepony.unicopia.*;
import com.minelittlepony.unicopia.client.minelittlepony.MineLPConnector;
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
import com.minelittlepony.unicopia.util.RegistryIndexer;
import net.fabricmc.loader.api.FabricLoader;
@ -97,7 +97,7 @@ public class SettingsScreen extends GameGui {
return Text.translatable("unicopia.options.ignore_mine_lp.undetected").formatted(Formatting.DARK_GREEN);
}
return Text.translatable("unicopia.options.ignore_mine_lp.detected", MineLPConnector.getPlayerPonyRace().getDisplayName()).formatted(Formatting.GREEN);
return Text.translatable("unicopia.options.ignore_mine_lp.detected", MineLPDelegate.getInstance().getPlayerPonyRace().getDisplayName()).formatted(Formatting.GREEN);
}
return Text.translatable("unicopia.options.ignore_mine_lp.missing").formatted(Formatting.RED);

View file

@ -1,26 +1,33 @@
package com.minelittlepony.unicopia.client.minelittlepony;
import java.util.Optional;
import com.minelittlepony.api.model.IModel;
import com.minelittlepony.api.model.ModelAttributes;
import com.minelittlepony.api.model.fabric.PonyModelPrepareCallback;
import com.minelittlepony.api.model.gear.IGear;
import com.minelittlepony.unicopia.Owned;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.client.MineLittlePony;
import com.minelittlepony.client.render.LevitatingItemRenderer;
import com.minelittlepony.unicopia.*;
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.util.AnimationUtil;
import net.fabricmc.api.ClientModInitializer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
public class Main implements ClientModInitializer {
public class Main extends MineLPDelegate implements ClientModInitializer {
private boolean hookErroring;
@Override
public void onInitializeClient() {
INSTANCE = this;
PonyModelPrepareCallback.EVENT.register(this::onPonyModelPrepared);
IGear.register(BangleGear::new);
IGear.register(AmuletGear::new);
@ -52,4 +59,39 @@ public class Main implements ClientModInitializer {
hookErroring = true;
}
}
@Override
public Race getPlayerPonyRace(PlayerEntity player) {
switch (MineLittlePony.getInstance().getManager().getPony(player).getRace(false)) {
case ALICORN:
return Race.ALICORN;
case CHANGELING:
case CHANGEDLING:
return Race.CHANGELING;
case ZEBRA:
case EARTH:
return Race.EARTH;
case GRYPHON:
case HIPPOGRIFF:
case PEGASUS:
return Race.PEGASUS;
case BATPONY:
return Race.BAT;
case SEAPONY:
case UNICORN:
return Race.UNICORN;
default:
return Race.HUMAN;
}
}
@Override
public Optional<VertexConsumer> getItemBuffer(VertexConsumerProvider vertexConsumers, Identifier texture) {
if (LevitatingItemRenderer.isEnabled()) {
return Optional.of(vertexConsumers.getBuffer(LevitatingItemRenderer.getRenderLayer(texture)));
}
return Optional.empty();
}
}

View file

@ -1,60 +0,0 @@
package com.minelittlepony.unicopia.client.minelittlepony;
import java.util.Optional;
import com.minelittlepony.client.MineLittlePony;
import com.minelittlepony.client.render.LevitatingItemRenderer;
import com.minelittlepony.unicopia.Race;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Identifier;
public final class MineLPConnector {
public static Race getPlayerPonyRace() {
return getPlayerPonyRace(MinecraftClient.getInstance().player);
}
public static Race getPlayerPonyRace(PlayerEntity player) {
if (!FabricLoader.getInstance().isModLoaded("minelp") || player == null) {
return Race.HUMAN;
}
switch (MineLittlePony.getInstance().getManager().getPony(player).getRace(false)) {
case ALICORN:
return Race.ALICORN;
case CHANGELING:
case CHANGEDLING:
return Race.CHANGELING;
case ZEBRA:
case EARTH:
return Race.EARTH;
case GRYPHON:
case HIPPOGRIFF:
case PEGASUS:
return Race.PEGASUS;
case BATPONY:
return Race.BAT;
case SEAPONY:
case UNICORN:
return Race.UNICORN;
default:
return Race.HUMAN;
}
}
public static Optional<VertexConsumer> getItemBuffer(VertexConsumerProvider vertexConsumers, Identifier texture) {
if (!FabricLoader.getInstance().isModLoaded("minelp")) {
return Optional.empty();
}
if (LevitatingItemRenderer.isEnabled()) {
return Optional.of(vertexConsumers.getBuffer(LevitatingItemRenderer.getRenderLayer(texture)));
}
return Optional.empty();
}
}

View file

@ -0,0 +1,31 @@
package com.minelittlepony.unicopia.client.minelittlepony;
import java.util.Optional;
import com.minelittlepony.unicopia.Race;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Identifier;
public class MineLPDelegate {
static MineLPDelegate INSTANCE = new MineLPDelegate();
public static MineLPDelegate getInstance() {
return INSTANCE;
}
public final Race getPlayerPonyRace() {
return getPlayerPonyRace(MinecraftClient.getInstance().player);
}
public Race getPlayerPonyRace(PlayerEntity player) {
return Race.HUMAN;
}
public Optional<VertexConsumer> getItemBuffer(VertexConsumerProvider vertexConsumers, Identifier texture) {
return Optional.empty();
}
}

View file

@ -21,7 +21,6 @@ import net.minecraft.client.render.entity.feature.FeatureRendererContext;
import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.client.render.item.ItemRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
@ -43,9 +42,9 @@ public class AmuletFeatureRenderer<E extends LivingEntity> implements AccessoryF
@Override
public void render(MatrixStack matrices, VertexConsumerProvider renderContext, int lightUv, E entity, float limbDistance, float limbAngle, float tickDelta, float age, float headYaw, float headPitch) {
ItemStack stack = entity.getEquippedStack(EquipmentSlot.CHEST);
ItemStack stack = AmuletItem.getForEntity(entity);
if (stack.getItem() instanceof AmuletItem) {
if (!stack.isEmpty()) {
Identifier texture = textures.computeIfAbsent(Registry.ITEM.getId(stack.getItem()), id -> new Identifier(id.getNamespace(), "textures/models/armor/" + id.getPath() + ".png"));
VertexConsumer consumer = ItemRenderer.getArmorGlintConsumer(renderContext, RenderLayer.getArmorCutoutNoCull(texture), false, false);

View file

@ -2,9 +2,9 @@ package com.minelittlepony.unicopia.client.render;
import com.minelittlepony.common.util.Color;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.client.minelittlepony.MineLPConnector;
import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
import com.minelittlepony.unicopia.item.GlowableItem;
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
import com.minelittlepony.unicopia.item.*;
import com.minelittlepony.unicopia.trinkets.TrinketsDelegate;
import net.minecraft.client.model.Dilation;
import net.minecraft.client.model.Model;
@ -24,13 +24,11 @@ import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.client.render.entity.model.EntityModelPartNames;
import net.minecraft.client.render.item.ItemRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.decoration.ArmorStandEntity;
import net.minecraft.item.DyeableItem;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Arm;
import net.minecraft.util.Identifier;
import net.minecraft.util.*;
public class BraceletFeatureRenderer<E extends LivingEntity> implements AccessoryFeatureRenderer.Feature<E> {
@ -50,16 +48,21 @@ public class BraceletFeatureRenderer<E extends LivingEntity> implements Accessor
@Override
public void render(MatrixStack stack, VertexConsumerProvider renderContext, int lightUv, E entity, float limbDistance, float limbAngle, float tickDelta, float age, float headYaw, float headPitch) {
ItemStack item = entity.getEquippedStack(EquipmentSlot.CHEST);
FriendshipBraceletItem.getWornBangles(entity, TrinketsDelegate.MAINHAND).findFirst().ifPresent(bangle -> {
renderBangleThirdPerson(bangle, stack, renderContext, lightUv, entity, limbDistance, limbAngle, tickDelta, age, headYaw, headPitch, entity.getMainArm());
});
FriendshipBraceletItem.getWornBangles(entity, TrinketsDelegate.OFFHAND).findFirst().ifPresent(bangle -> {
renderBangleThirdPerson(bangle, stack, renderContext, lightUv, entity, limbDistance, limbAngle, tickDelta, age, headYaw, headPitch, entity.getMainArm().getOpposite());
});
}
if (item.getItem() instanceof FriendshipBraceletItem) {
private void renderBangleThirdPerson(ItemStack item, MatrixStack stack, VertexConsumerProvider renderContext, int lightUv, E entity, float limbDistance, float limbAngle, float tickDelta, float age, float headYaw, float headPitch, Arm mainArm) {
int j = ((DyeableItem)item.getItem()).getColor(item);
boolean alex = entity instanceof ClientPlayerEntity && ((ClientPlayerEntity)entity).getModel().startsWith("slim");
BraceletModel model = alex ? alexModel : steveModel;
boolean isLeft = entity.getMainArm() == Arm.LEFT;
boolean isLeft = mainArm == Arm.LEFT;
if (entity instanceof ArmorStandEntity) {
ModelPart arm = isLeft ? context.getModel().leftArm : context.getModel().rightArm;
@ -73,16 +76,13 @@ public class BraceletFeatureRenderer<E extends LivingEntity> implements Accessor
VertexConsumer consumer = ItemRenderer.getArmorGlintConsumer(renderContext, RenderLayer.getArmorCutoutNoCull(TEXTURE), false, false);
model.setAngles(context.getModel());
model.setVisible(entity.getMainArm());
model.setVisible(mainArm);
model.render(stack, consumer, glowing ? 0x0F00F0 : lightUv, OverlayTexture.DEFAULT_UV, Color.r(j), Color.g(j), Color.b(j), 1);
}
}
@Override
public void renderArm(MatrixStack stack, VertexConsumerProvider renderContext, int lightUv, E entity, ModelPart armModel, Arm side) {
ItemStack item = entity.getEquippedStack(EquipmentSlot.CHEST);
if (item.getItem() instanceof FriendshipBraceletItem) {
FriendshipBraceletItem.getWornBangles(entity, side == entity.getMainArm() ? TrinketsDelegate.MAINHAND : TrinketsDelegate.OFFHAND).findFirst().ifPresent(item -> {
int j = ((DyeableItem)item.getItem()).getColor(item);
boolean alex = entity instanceof ClientPlayerEntity && ((ClientPlayerEntity)entity).getModel().startsWith("slim");
@ -91,8 +91,7 @@ public class BraceletFeatureRenderer<E extends LivingEntity> implements Accessor
boolean glowing = ((GlowableItem)item.getItem()).isGlowing(item);
if (!MineLPConnector.getPlayerPonyRace((ClientPlayerEntity)entity).isDefault()) {
if (!MineLPDelegate.getInstance().getPlayerPonyRace((ClientPlayerEntity)entity).isDefault()) {
stack.translate(side == Arm.LEFT ? 0.06 : -0.06, 0.3, 0);
} else {
stack.translate(0, -0.1, 0);
@ -103,7 +102,7 @@ public class BraceletFeatureRenderer<E extends LivingEntity> implements Accessor
model.setAngles(context.getModel());
model.setVisible(side);
model.render(stack, consumer, glowing ? 0x0F00F0 : lightUv, OverlayTexture.DEFAULT_UV, Color.r(j), Color.g(j), Color.b(j), 1);
}
});
}
public static class BraceletModel extends Model {

View file

@ -3,7 +3,7 @@ package com.minelittlepony.unicopia.client.render;
import java.util.Optional;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.client.minelittlepony.MineLPConnector;
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.util.AnimationUtil;
@ -24,7 +24,7 @@ public class PlayerPoser {
Pony pony = Pony.of(player);
float progress = pony.getAnimationProgress(MinecraftClient.getInstance().getTickDelta());
Animation animation = pony.getAnimation();
boolean isPony = !MineLPConnector.getPlayerPonyRace(player).isDefault();
boolean isPony = !MineLPDelegate.getInstance().getPlayerPonyRace(player).isDefault();
switch (animation) {
case WOLOLO: {

View file

@ -1,6 +1,6 @@
package com.minelittlepony.unicopia.client.render;
import com.minelittlepony.unicopia.client.minelittlepony.MineLPConnector;
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.BuiltinItemRendererRegistry;
@ -70,7 +70,7 @@ public class PolearmRenderer implements DynamicItemRenderer, UnclampedModelPredi
matrices.scale(1, -1, -1);
Identifier id = Registry.ITEM.getId(stack.getItem());
Identifier texture = new Identifier(id.getNamespace(), "textures/entity/polearm/" + id.getPath() + ".png");
model.render(matrices, MineLPConnector.getItemBuffer(vertexConsumers, texture).orElseGet(() -> {
model.render(matrices, MineLPDelegate.getInstance().getItemBuffer(vertexConsumers, texture).orElseGet(() -> {
return ItemRenderer.getDirectItemGlintConsumer(vertexConsumers, model.getLayer(texture), false, stack.hasGlint());
}), light, overlay, 1, 1, 1, 1);
matrices.pop();

View file

@ -12,6 +12,7 @@ import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.item.GemstoneItem;
import com.minelittlepony.unicopia.trinkets.TrinketsDelegate;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import com.minelittlepony.unicopia.util.Tickable;
@ -43,7 +44,17 @@ public class PlayerCharmTracker implements Tickable, NbtSerialisable {
@Override
public void tick() {
armour.update(pony.getMaster().getInventory().armor.stream());
armour.update(getAll());
}
private Stream<ItemStack> getAll() {
if (!TrinketsDelegate.hasTrinkets()) {
return pony.getMaster().getInventory().armor.stream();
}
return Stream.concat(
TrinketsDelegate.getInstance().getEquipped(pony.getMaster(), TrinketsDelegate.NECKLACE),
pony.getMaster().getInventory().armor.stream()
);
}
public ItemTracker getArmour() {

View file

@ -201,7 +201,7 @@ public class AlicornAmuletItem extends AmuletItem implements PlayerCharmTracker.
}, 50);
}
pony.findAllEntitiesInRange(10, e -> e instanceof MobEntity && !((MobEntity)e).hasStatusEffect(UEffects.CORRUPT_INFLUENCE)).forEach(e -> {
pony.findAllEntitiesInRange(10, e -> e instanceof MobEntity mob && !mob.hasStatusEffect(UEffects.CORRUPT_INFLUENCE)).forEach(e -> {
((MobEntity)e).addStatusEffect(new StatusEffectInstance(UEffects.CORRUPT_INFLUENCE, 1300, 1));
});
}

View file

@ -9,6 +9,7 @@ import org.jetbrains.annotations.Nullable;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.trinkets.TrinketsDelegate;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.minecraft.client.MinecraftClient;
@ -99,7 +100,14 @@ public class AmuletItem extends WearableItem {
}
public boolean isApplicable(LivingEntity entity) {
return isApplicable(entity.getEquippedStack(EquipmentSlot.CHEST));
return isApplicable(getForEntity(entity));
}
public static ItemStack getForEntity(LivingEntity entity) {
return TrinketsDelegate.getInstance().getEquipped(entity, TrinketsDelegate.NECKLACE)
.filter(stack -> stack.getItem() instanceof AmuletItem)
.findFirst()
.orElse(ItemStack.EMPTY);
}
public boolean isChargable() {

View file

@ -10,6 +10,7 @@ import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.trinkets.TrinketsDelegate;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@ -23,9 +24,7 @@ import net.minecraft.item.DyeableItem;
import net.minecraft.item.ItemStack;
import net.minecraft.stat.Stats;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Hand;
import net.minecraft.util.TypedActionResult;
import net.minecraft.util.*;
import net.minecraft.world.World;
public class FriendshipBraceletItem extends WearableItem implements DyeableItem, GlowableItem {
@ -115,10 +114,11 @@ public class FriendshipBraceletItem extends WearableItem implements DyeableItem,
public static boolean isComrade(Caster<?> caster, Entity entity) {
if (entity instanceof LivingEntity) {
return caster.getMasterId().filter(id -> {
return isSignedBy(((LivingEntity)entity).getOffHandStack(), id)
|| isSignedBy(((LivingEntity)entity).getEquippedStack(EquipmentSlot.CHEST), id);
}).isPresent();
return caster.getMasterId()
.filter(id -> getWornBangles((LivingEntity)entity)
.anyMatch(stack -> isSignedBy(stack, id))
)
.isPresent();
}
return false;
}
@ -126,4 +126,16 @@ public class FriendshipBraceletItem extends WearableItem implements DyeableItem,
public static Stream<Pony> getPartyMembers(Caster<?> caster, double radius) {
return Pony.stream(caster.findAllEntitiesInRange(radius, entity -> isComrade(caster, entity)));
}
public static Stream<ItemStack> getWornBangles(LivingEntity entity) {
return Stream.concat(
TrinketsDelegate.getInstance().getEquipped(entity, TrinketsDelegate.MAINHAND),
TrinketsDelegate.getInstance().getEquipped(entity, TrinketsDelegate.OFFHAND)
).filter(stack -> stack.getItem() == UItems.FRIENDSHIP_BRACELET);
}
public static Stream<ItemStack> getWornBangles(LivingEntity entity, Identifier slot) {
return TrinketsDelegate.getInstance().getEquipped(entity, slot)
.filter(stack -> stack.getItem() == UItems.FRIENDSHIP_BRACELET);
}
}

View file

@ -1,9 +1,10 @@
package com.minelittlepony.unicopia.item;
import com.minelittlepony.unicopia.trinkets.TrinketsDelegate;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.minecraft.block.DispenserBlock;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ArmorItem;
import net.minecraft.item.ArmorMaterials;
@ -18,26 +19,26 @@ import net.minecraft.world.World;
public abstract class WearableItem extends Item implements Wearable {
public WearableItem(FabricItemSettings settings) {
super(settings.equipmentSlot(s -> ((WearableItem)s.getItem()).getPreferredSlot(s)));
super(configureEquipmentSlotSupplier(settings));
DispenserBlock.registerBehavior(this, ArmorItem.DISPENSER_BEHAVIOR);
TrinketsDelegate.getInstance().registerTrinket(this);
}
private static FabricItemSettings configureEquipmentSlotSupplier(FabricItemSettings settings) {
if (TrinketsDelegate.hasTrinkets()) {
return settings;
}
return settings.equipmentSlot(s -> ((WearableItem)s.getItem()).getPreferredSlot(s));
}
@Override
public TypedActionResult<ItemStack> use(World world, PlayerEntity player, Hand hand) {
ItemStack stack = player.getStackInHand(hand);
EquipmentSlot slot = MobEntity.getPreferredEquipmentSlot(stack);
ItemStack currentArmor = player.getEquippedStack(slot);
if (currentArmor.isEmpty()) {
ItemStack result = stack.copy();
result.setCount(1);
player.equipStack(slot, result);
stack.decrement(1);
return TypedActionResult.success(stack, world.isClient());
}
return TypedActionResult.fail(stack);
return TrinketsDelegate.getInstance().getAvailableTrinketSlots(player, TrinketsDelegate.ALL).stream()
.findAny()
.filter(slotId -> TrinketsDelegate.getInstance().equipStack(player, slotId, stack))
.map(slotId -> TypedActionResult.success(stack, world.isClient()))
.orElseGet(() -> TypedActionResult.fail(stack));
}
@Override

View file

@ -0,0 +1,68 @@
package com.minelittlepony.unicopia.trinkets;
import java.util.*;
import java.util.stream.Stream;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
public interface TrinketsDelegate {
Identifier MAINHAND = new Identifier("hand:glove");
Identifier OFFHAND = new Identifier("offhand:glove");
Identifier NECKLACE = new Identifier("chest:necklace");
Set<Identifier> ALL = new TreeSet<>(List.of(MAINHAND, OFFHAND, NECKLACE));
TrinketsDelegate EMPTY = new TrinketsDelegate() {};
static TrinketsDelegate getInstance() {
if (!hasTrinkets()) {
return EMPTY;
}
return TrinketsDelegateImpl.INSTANCE;
}
static boolean hasTrinkets() {
return FabricLoader.getInstance().isModLoaded("trinkets");
}
default boolean equipStack(LivingEntity entity, Identifier slot, ItemStack stack) {
EquipmentSlot eq = MobEntity.getPreferredEquipmentSlot(stack);
if (!entity.getEquippedStack(eq).isEmpty()) {
return false;
}
entity.equipStack(eq, stack.split(1));
return true;
}
default Set<Identifier> getAvailableTrinketSlots(LivingEntity entity, Set<Identifier> probedSlots) {
return Set.of();
}
default Stream<ItemStack> getEquipped(LivingEntity entity) {
return Stream.empty();
}
default Stream<ItemStack> getEquipped(LivingEntity entity, Identifier slot) {
if (slot == NECKLACE || slot == MAINHAND) {
return Stream.of(entity.getEquippedStack(EquipmentSlot.CHEST));
}
if (slot == OFFHAND) {
return Stream.of(entity.getOffHandStack());
}
return Stream.empty();
}
default void registerTrinket(Item item) {
}
}

View file

@ -0,0 +1,85 @@
package com.minelittlepony.unicopia.trinkets;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.minelittlepony.unicopia.util.InventoryUtil;
import dev.emi.trinkets.TrinketSlot;
import dev.emi.trinkets.api.*;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.sound.SoundEvent;
import net.minecraft.util.Identifier;
import net.minecraft.world.event.GameEvent;
class TrinketsDelegateImpl implements TrinketsDelegate {
static final TrinketsDelegateImpl INSTANCE = new TrinketsDelegateImpl();
// who tf designed this api?
@Override
public boolean equipStack(LivingEntity entity, Identifier slot, ItemStack stack) {
return getInventory(entity, slot).map(inventory -> {
for (int position = 0; position < inventory.size(); position++) {
if (inventory.getStack(position).isEmpty() && TrinketSlot.canInsert(stack, new SlotReference(inventory, position), entity)) {
SoundEvent soundEvent = stack.getEquipSound();
inventory.setStack(position, stack.split(1));
if (soundEvent != null) {
entity.emitGameEvent(GameEvent.EQUIP);
entity.playSound(soundEvent, 1, 1);
}
return true;
}
}
return false;
}).orElse(false);
}
@Override
public Set<Identifier> getAvailableTrinketSlots(LivingEntity entity, Set<Identifier> probedSlots) {
probedSlots = new HashSet<>(probedSlots);
probedSlots.removeAll(getInventories(entity)
.filter(inventory -> InventoryUtil.getOpenSlot(inventory) == -1)
.map(slot -> slot.getSlotType())
.map(TrinketsDelegateImpl::getSlotId)
.collect(Collectors.toSet()));
return probedSlots;
}
@Override
public Stream<ItemStack> getEquipped(LivingEntity entity) {
return getInventories(entity).flatMap(InventoryUtil::stream).filter(s -> !s.isEmpty());
}
@Override
public Stream<ItemStack> getEquipped(LivingEntity entity, Identifier slot) {
return getInventory(entity, slot).stream().flatMap(InventoryUtil::stream).filter(s -> !s.isEmpty());
}
@Override
public void registerTrinket(Item item) {
TrinketsApi.registerTrinket(item, new UnicopiaTrinket(item));
}
public Optional<TrinketInventory> getInventory(LivingEntity entity, Identifier slot) {
return TrinketsApi.getTrinketComponent(entity)
.map(component -> component.getInventory()
.getOrDefault(slot.getNamespace(), Map.of())
.getOrDefault(slot.getPath(), null)
);
}
private Stream<TrinketInventory> getInventories(LivingEntity entity) {
return TrinketsApi.getTrinketComponent(entity)
.stream()
.map(component -> component.getInventory())
.flatMap(groups -> groups.values().stream())
.flatMap(group -> group.values().stream());
}
private static Identifier getSlotId(SlotType slotType) {
return new Identifier(slotType.getGroup(), slotType.getName());
}
}

View file

@ -0,0 +1,65 @@
package com.minelittlepony.unicopia.trinkets;
import java.util.UUID;
import com.google.common.collect.Multimap;
import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
import com.minelittlepony.unicopia.item.WearableItem;
import dev.emi.trinkets.api.*;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.sound.SoundEvent;
public class UnicopiaTrinket implements Trinket {
private final Item item;
public UnicopiaTrinket(Item item) {
this.item = item;
}
@Override
public void onEquip(ItemStack stack, SlotReference slot, LivingEntity entity) {
if (entity.isSpectator() || stack.isEmpty()) {
return;
}
SoundEvent soundEvent = stack.getEquipSound();
if (soundEvent != null) {
entity.playSound(soundEvent, 1, 1);
}
}
// @Override
public int getMaxCount(ItemStack stack, SlotReference slot, LivingEntity entity) {
// https://github.com/emilyploszaj/trinkets/issues/215
return 1;
}
@Override
public boolean canEquip(ItemStack stack, SlotReference slot, LivingEntity entity) {
if (item instanceof FriendshipBraceletItem && !FriendshipBraceletItem.isSigned(stack)) {
return false;
}
return slot.inventory().getStack(slot.index()).isEmpty();
}
@Override
public void tick(ItemStack stack, SlotReference slot, LivingEntity entity) {
item.inventoryTick(stack, entity.world, entity, slot.index(), false);
}
@Override
public Multimap<EntityAttribute, EntityAttributeModifier> getModifiers(ItemStack stack, SlotReference slot, LivingEntity entity, UUID uuid) {
Multimap<EntityAttribute, EntityAttributeModifier> modifiers = Trinket.super.getModifiers(stack, slot, entity, uuid);
if (item instanceof WearableItem wearable) {
item.getAttributeModifiers(wearable.getPreferredSlot(stack));
}
return modifiers;
}
}

View file

@ -13,4 +13,13 @@ public interface InventoryUtil {
static Stream<Integer> slots(Inventory inventory) {
return Stream.iterate(0, i -> i < inventory.size(), i -> i + 1);
}
static int getOpenSlot(Inventory inventory) {
for (int i = 0; i < inventory.size(); i++) {
if (inventory.getStack(i).isEmpty()) {
return i;
}
}
return -1;
}
}

View file

@ -0,0 +1,10 @@
{
"entities": [
"player"
],
"slots": [
"hand/glove",
"offhand/glove",
"chest/necklace"
]
}

View file

@ -0,0 +1,7 @@
{
"replace": false,
"values": [
"unicopia:alicorn_amulet",
"unicopia:pegasus_amulet"
]
}

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"unicopia:friendship_bracelet"
]
}

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"unicopia:friendship_bracelet"
]
}