Added friendship bracelets!

This commit is contained in:
Sollace 2021-02-03 22:25:42 +02:00
parent 6ffff32718
commit 120fd6eb38
17 changed files with 420 additions and 16 deletions

View file

@ -12,6 +12,7 @@ import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleHandle;
import com.minelittlepony.unicopia.particle.SphereParticleEffect;
@ -114,7 +115,8 @@ public class ShieldSpell extends AbstractRangedAreaSpell implements Attached {
return source.findAllEntitiesInRange(radius)
.filter(entity -> {
return
(entity instanceof LivingEntity
!FriendshipBraceletItem.isComrade(source, entity)
&& (entity instanceof LivingEntity
|| entity instanceof TntEntity
|| entity instanceof FallingBlockEntity
|| entity instanceof EyeOfEnderEntity

View file

@ -9,15 +9,18 @@ import com.minelittlepony.unicopia.client.particle.RainboomParticle;
import com.minelittlepony.unicopia.client.particle.RainbowTrailParticle;
import com.minelittlepony.unicopia.client.particle.RaindropsParticle;
import com.minelittlepony.unicopia.client.particle.SphereParticle;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.particle.UParticles;
import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry;
import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry.PendingParticleFactory;
import net.fabricmc.fabric.api.client.rendereregistry.v1.EntityRendererRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.particle.SpriteProvider;
import net.minecraft.client.render.entity.FlyingItemEntityRenderer;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.item.DyeableItem;
import net.minecraft.particle.ParticleEffect;
public interface URenderers {
@ -32,6 +35,8 @@ public interface URenderers {
ParticleFactoryRegistry.getInstance().register(UParticles.DISK, DiskParticle::new);
EntityRendererRegistry.INSTANCE.register(UEntities.THROWN_ITEM, (manager, context) -> new FlyingItemEntityRenderer<>(manager, context.getItemRenderer()));
ColorProviderRegistry.ITEM.register((stack, i) -> i > 0 ? -1 : ((DyeableItem)stack.getItem()).getColor(stack), UItems.FRIENDSHIP_BRACELET);
}
static <T extends ParticleEffect> PendingParticleFactory<T> createFactory(ParticleSupplier<T> supplier) {

View file

@ -0,0 +1,96 @@
package com.minelittlepony.unicopia.client.render;
import com.minelittlepony.common.util.Color;
import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
import com.minelittlepony.unicopia.item.GlowableItem;
import net.minecraft.client.model.Model;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.render.OverlayTexture;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.feature.FeatureRenderer;
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.DyeableItem;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Arm;
import net.minecraft.util.Identifier;
public class BraceletFeatureRenderer<
E extends LivingEntity,
M extends BipedEntityModel<E>> extends FeatureRenderer<E, M> {
private static final Identifier TEXTURE = new Identifier("unicopia", "textures/models/armor/bracelet.png");
private final BraceletModel steveModel = new BraceletModel(0.3F, false);
private final BraceletModel alexModel = new BraceletModel(0.3F, true);
public BraceletFeatureRenderer(FeatureRendererContext<E, M> context) {
super(context);
}
@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);
if (item.getItem() instanceof FriendshipBraceletItem) {
VertexConsumer consumer = ItemRenderer.getArmorGlintConsumer(renderContext, RenderLayer.getArmorCutoutNoCull(TEXTURE), false, false);
int j = ((DyeableItem)item.getItem()).getColor(item);
boolean alex = entity instanceof ClientPlayerEntity && ((ClientPlayerEntity)entity).getModel().startsWith("slim");
BraceletModel model = alex ? alexModel : steveModel;
model.setAngles(getContextModel());
model.setVisible(entity.getMainArm());
model.render(stack, consumer, ((GlowableItem)item.getItem()).isGlowing(item) ? 0x0F00F0 : lightUv, OverlayTexture.DEFAULT_UV, Color.r(j), Color.g(j), Color.b(j), 1);
}
}
static class BraceletModel extends Model {
private final ModelPart leftArm;
private final ModelPart rightArm;
private final boolean alex;
public BraceletModel(float dilate, boolean alex) {
super(RenderLayer::getEntityTranslucent);
this.alex = alex;
rightArm = new ModelPart(this, 0, alex ? 6 : 0);
rightArm.addCuboid(-3, 7, -2, alex ? 3 : 4, 2, 4, dilate);
leftArm = new ModelPart(this, 0, alex ? 6 : 0);
leftArm.mirror = true;
leftArm.addCuboid(-1, 7, -2, alex ? 3 : 4, 2, 4, dilate);
}
public void setAngles(BipedEntityModel<?> biped) {
leftArm.copyPositionAndRotation(biped.leftArm);
rightArm.copyPositionAndRotation(biped.rightArm);
if (alex) {
rightArm.pivotX++;
}
}
public void setVisible(Arm arm) {
leftArm.visible = arm == Arm.LEFT;
rightArm.visible = arm == Arm.RIGHT;
}
@Override
public void render(MatrixStack matrixStack, VertexConsumer vertexConsumer, int i, int j, float f, float g, float h, float k) {
leftArm.render(matrixStack, vertexConsumer, i, j, f, g, h, k);
rightArm.render(matrixStack, vertexConsumer, i, j, f, g, h, k);
}
}
}

View file

@ -0,0 +1,126 @@
package com.minelittlepony.unicopia.item;
import java.util.List;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.ability.magic.Caster;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.DispenserBlock;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ArmorItem;
import net.minecraft.item.DyeableItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Wearable;
import net.minecraft.sound.SoundEvents;
import net.minecraft.stat.Stats;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Formatting;
import net.minecraft.util.Hand;
import net.minecraft.util.TypedActionResult;
import net.minecraft.world.World;
public class FriendshipBraceletItem extends Item implements DyeableItem, Wearable, GlowableItem {
public FriendshipBraceletItem(Settings settings) {
super(settings);
DispenserBlock.registerBehavior(this, ArmorItem.DISPENSER_BEHAVIOR);
}
@Override
public TypedActionResult<ItemStack> use(World world, PlayerEntity player, Hand hand) {
ItemStack stack = player.getStackInHand(hand);
if (!isSigned(stack) && EquinePredicates.PLAYER_UNICORN.test(player)) {
player.setCurrentHand(hand);
ItemStack result = stack.copy();
result.setCount(1);
result.getOrCreateTag().putString("issuer", player.getName().asString());
if (!player.abilities.creativeMode) {
stack.decrement(1);
}
player.incrementStat(Stats.USED.getOrCreateStat(this));
player.playSound(SoundEvents.ITEM_BOOK_PUT, 1, 1);
if (stack.isEmpty()) {
return TypedActionResult.consume(result);
}
if (!player.giveItemStack(result)) {
player.dropStack(result);
}
return TypedActionResult.consume(stack);
}
EquipmentSlot slot = MobEntity.getPreferredEquipmentSlot(stack);
ItemStack currentArmor = player.getEquippedStack(slot);
if (currentArmor.isEmpty()) {
ItemStack result = stack.copy();
result.setCount(1);
if (!player.abilities.creativeMode) {
stack.decrement(1);
}
player.equipStack(slot, result);
return TypedActionResult.success(stack, world.isClient());
}
return TypedActionResult.fail(stack);
}
@Override
@Environment(EnvType.CLIENT)
public void appendTooltip(ItemStack stack, @Nullable World world, List<Text> list, TooltipContext tooltipContext) {
if (isSigned(stack)) {
list.add(new TranslatableText("item.unicopia.friendship_bracelet.issuer", getSignature(stack)));
}
if (isGlowing(stack)) {
list.add(new TranslatableText("item.unicopia.friendship_bracelet.glowing").formatted(Formatting.ITALIC, Formatting.GRAY));
}
}
private boolean checkSignature(ItemStack stack, PlayerEntity player) {
return player.getName().asString().contentEquals(getSignature(stack));
}
@Nullable
public static String getSignature(ItemStack stack) {
return isSigned(stack) ? stack.getTag().getString("issuer") : null;
}
public static boolean isSigned(ItemStack stack) {
return stack.hasTag() && stack.getTag().contains("issuer");
}
public static boolean isSignedBy(ItemStack stack, PlayerEntity player) {
return stack.getItem() instanceof FriendshipBraceletItem
&& ((FriendshipBraceletItem)stack.getItem()).checkSignature(stack, player);
}
public static boolean isComrade(Caster<?> caster, Entity entity) {
Entity master = caster.getMaster();
if (master instanceof PlayerEntity && entity instanceof LivingEntity) {
return isSignedBy(((LivingEntity)entity).getOffHandStack(), (PlayerEntity)master)
|| isSignedBy(((LivingEntity)entity).getEquippedStack(EquipmentSlot.CHEST), (PlayerEntity)master);
}
return false;
}
public static EquipmentSlot getPreferredEquipmentSlot(ItemStack stack) {
return isSigned(stack) ? EquipmentSlot.CHEST : EquipmentSlot.OFFHAND;
}
}

View file

@ -0,0 +1,15 @@
package com.minelittlepony.unicopia.item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
public interface GlowableItem {
default boolean isGlowing(ItemStack stack) {
CompoundTag tag = stack.getSubTag("display");
return tag != null && tag.getBoolean("glowing");
}
default void setGlowing(ItemStack stack, boolean glowing) {
stack.getOrCreateSubTag("display").putBoolean("glowing", glowing);
}
}

View file

@ -0,0 +1,79 @@
package com.minelittlepony.unicopia.item;
import net.minecraft.inventory.CraftingInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.recipe.RecipeSerializer;
import net.minecraft.recipe.SpecialCraftingRecipe;
import net.minecraft.util.Identifier;
import net.minecraft.util.Pair;
import net.minecraft.world.World;
public class GlowingRecipe extends SpecialCraftingRecipe {
public GlowingRecipe(Identifier id) {
super(id);
}
@Override
public boolean matches(CraftingInventory inventory, World world) {
Pair<ItemStack, ItemStack> result = runMatch(inventory);
return !result.getLeft().isEmpty() && !result.getRight().isEmpty();
}
@Override
public ItemStack craft(CraftingInventory inventory) {
Pair<ItemStack, ItemStack> pair = runMatch(inventory);
ItemStack result = pair.getLeft().copy();
((GlowableItem)result.getItem()).setGlowing(result, pair.getRight().getItem() == Items.GLOWSTONE_DUST);
return result;
}
private Pair<ItemStack, ItemStack> runMatch(CraftingInventory inventory) {
ItemStack bangle = ItemStack.EMPTY;
ItemStack dust = ItemStack.EMPTY;
for(int i = 0; i < inventory.size(); i++) {
ItemStack stack = inventory.getStack(i);
if (!stack.isEmpty()) {
if (stack.getItem() instanceof GlowableItem) {
if (!bangle.isEmpty()) {
return new Pair<>(bangle, dust);
}
bangle = stack;
} else {
if (!(stack.getItem() == Items.GLOWSTONE_DUST || stack.getItem() == Items.INK_SAC)) {
return new Pair<>(bangle, dust);
}
dust = stack;
}
}
}
if (!bangle.isEmpty()) {
if ((dust.getItem() == Items.GLOWSTONE_DUST) == ((GlowableItem)bangle.getItem()).isGlowing(bangle)) {
return new Pair<>(ItemStack.EMPTY, ItemStack.EMPTY);
}
}
return new Pair<>(bangle, dust);
}
@Override
public boolean fits(int i, int j) {
return i * j >= 2;
}
@Override
public RecipeSerializer<?> getSerializer() {
return URecipes.GLOWING_SERIALIZER;
}
}

View file

@ -13,6 +13,7 @@ import net.minecraft.item.Item;
import net.minecraft.item.Item.Settings;
import net.minecraft.item.ItemGroup;
import net.fabricmc.fabric.api.client.itemgroup.FabricItemGroupBuilder;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.minecraft.item.BlockItem;
import net.minecraft.item.FoodComponents;
import net.minecraft.item.MusicDiscItem;
@ -39,6 +40,13 @@ public interface UItems {
Item MUSIC_DISC_POPULAR = register("music_disc_popular", USounds.RECORD_POPULAR);
Item MUSIC_DISC_FUNK = register("music_disc_funk", USounds.RECORD_FUNK);
FriendshipBraceletItem FRIENDSHIP_BRACELET = register("friendship_bracelet", new FriendshipBraceletItem(
new FabricItemSettings()
.rarity(Rarity.UNCOMMON)
.group(ItemGroup.TOOLS)
.equipmentSlot(FriendshipBraceletItem::getPreferredEquipmentSlot)
));
static <T extends Item> T register(String name, T item) {
ITEMS.add(item);
if (item instanceof BlockItem) {

View file

@ -1,15 +1,21 @@
package com.minelittlepony.unicopia.item;
import com.google.gson.JsonArray;
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.RecipeSerializer;
import net.minecraft.recipe.RecipeType;
import net.minecraft.recipe.ShapelessRecipe;
import net.minecraft.recipe.SpecialRecipeSerializer;
import net.minecraft.util.Identifier;
import net.minecraft.util.collection.DefaultedList;
import net.minecraft.util.registry.Registry;
public interface URecipes {
RecipeSerializer<ShapelessRecipe> ZAP_APPLE_SERIALIZER = register("crafting_zap_apple", new ZapAppleRecipe.Serializer());
RecipeSerializer<GlowingRecipe> GLOWING_SERIALIZER = register("crafting_glowing", new SpecialRecipeSerializer<>(GlowingRecipe::new));
static <T extends Recipe<?>> RecipeType<T> register(final String id) {
return Registry.register(Registry.RECIPE_TYPE, new Identifier("unicopia", id), new RecipeType<T>() {
@ -24,5 +30,19 @@ public interface URecipes {
return Registry.register(Registry.RECIPE_SERIALIZER, new Identifier("unicopia", id), serializer);
}
static DefaultedList<Ingredient> getIngredients(JsonArray json) {
DefaultedList<Ingredient> defaultedList = DefaultedList.of();
for (int i = 0; i < json.size(); ++i) {
Ingredient ingredient = Ingredient.fromJson(json.get(i));
if (!ingredient.isEmpty()) {
defaultedList.add(ingredient);
}
}
return defaultedList;
}
static void bootstrap() {}
}

View file

@ -1,6 +1,5 @@
package com.minelittlepony.unicopia.item;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
@ -22,7 +21,7 @@ public class ZapAppleRecipe extends ShapelessRecipe {
@Override
public ShapelessRecipe read(Identifier identifier, JsonObject json) {
String group = JsonHelper.getString(json, "group", "");
DefaultedList<Ingredient> ingredients = getIngredients(JsonHelper.getArray(json, "ingredients"));
DefaultedList<Ingredient> ingredients = URecipes.getIngredients(JsonHelper.getArray(json, "ingredients"));
if (ingredients.isEmpty()) {
throw new JsonParseException("No ingredients for shapeless recipe");
@ -48,18 +47,5 @@ public class ZapAppleRecipe extends ShapelessRecipe {
return new ZapAppleRecipe(identifier, group, input.readItemStack(), ingredients);
}
private static DefaultedList<Ingredient> getIngredients(JsonArray json) {
DefaultedList<Ingredient> defaultedList = DefaultedList.of();
for (int i = 0; i < json.size(); ++i) {
Ingredient ingredient = Ingredient.fromJson(json.get(i));
if (!ingredient.isEmpty()) {
defaultedList.add(ingredient);
}
}
return defaultedList;
}
}
}

View file

@ -0,0 +1,34 @@
package com.minelittlepony.unicopia.mixin.client;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.minelittlepony.unicopia.client.render.BraceletFeatureRenderer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer;
import net.minecraft.client.render.entity.feature.FeatureRenderer;
import net.minecraft.client.render.entity.feature.FeatureRendererContext;
import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity;
@Mixin(ArmorFeatureRenderer.class)
abstract class MixinArmorFeatureRenderer<T extends LivingEntity, M extends BipedEntityModel<T>, A extends BipedEntityModel<T>> extends FeatureRenderer<T, M> {
private BraceletFeatureRenderer<T, M> bracelet;
MixinArmorFeatureRenderer() { super(null); }
@Inject(method = "<init>", at = @At("RETURN"))
private void onInit(FeatureRendererContext<T, M> context, A inner, A outer, CallbackInfo info) {
bracelet = new BraceletFeatureRenderer<>(context);
}
@Inject(method = "render", at = @At("RETURN"))
private void onRender(MatrixStack stack, VertexConsumerProvider renderContext, int lightUv, T entity, float limbDistance, float limbAngle, float tickDelta, float age, float headYaw, float headPitch, CallbackInfo info) {
bracelet.render(stack, renderContext, lightUv, entity, limbDistance, limbAngle, tickDelta, age, headYaw, headPitch);
}
}

View file

@ -5,6 +5,10 @@
"itemGroup.unicopia.items": "Unicopia - Misc.",
"itemGroup.unicopia.horsefeed": "Unicopia - Horse Feed",
"item.unicopia.friendship_bracelet": "Bangle of Comradery",
"item.unicopia.friendship_bracelet.issuer": "Signed by %s",
"item.unicopia.friendship_bracelet.glowing": "Glowing",
"item.unicopia.green_apple": "Granny Smith Apple",
"item.unicopia.sweet_apple": "Sweet Apple Acres Apple",
"item.unicopia.sour_apple": "Sour Apple",

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "unicopia:item/friendship_bracelet"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,19 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"*#*",
"# #",
"*#*"
],
"key": {
"*": {
"item": "minecraft:string"
},
"#": {
"item": "minecraft:leather"
}
},
"result": {
"item": "unicopia:friendship_bracelet"
}
}

View file

@ -0,0 +1,3 @@
{
"type": "unicopia:crafting_glowing"
}

View file

@ -24,6 +24,7 @@
"MixinWorld"
],
"client": [
"client.MixinArmorFeatureRenderer",
"client.MixinCamera",
"client.MixinClientPlayerInteractionManager",
"client.MixinEntityRenderDispatcher",