diff --git a/README.md b/README.md index e54b1da3..aa63374a 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Unicorns, Pegasi, Earth Ponies, and even Changelings get their own special abili ### Manage your diet - Playing as a pony isn't all just kicking and zapping, though! As herbivores your food options open up to include + Playing as a pony isn't all just kicking and zapping, though! As herbivores, your food options open up to include a lot of items normal players don't usually get to eat. Feeling peckish? Try for some flowers from the meadow, or some hay! I hear the hay burgers of good, if you can find some oats. @@ -57,21 +57,24 @@ Unicorns, Pegasi, Earth Ponies, and even Changelings get their own special abili ### Natural Stuff - Airflow is simulated (badly) - Pegasi, beware about flying during storms! It can get dangerous out there! - If you're playing as a flying species, or just like having nice things, try building a weather vane. + + Pegasi, beware of flying during storms! It can get dangerous out there! + If you're playing as a flying species, or just like having nice things, try building a weather vein. + It shows the actual, totally real and not simulated badly, wind direction of your minecraft world. Just beware - that the direction and strength is situational (and bad), and will be different depending where you are and + that the direction and strength are situational (and bad), and will be different depending on where you are and how high up you are. - Hot air Rises + No, it's not a bad Star Wars movie, it's an actual mechanic. Sand and lava will give flying species extra lift. Water does the opposite. Try it! Actually don't, I don't want you to drown. ### Magic Items And Artifacts - - Craft and build s shrine for the Crystal Heart to provide valuable support to your friends - - Or give out bangles of comradery to your non-unicorn buddies so they can share in your powers, - or just so you can laugh when you teleport and they end up coming witht + - Craft and build a shrine for the Crystal Heart to provide valuable support to your friends + - Or give out bangles of comradery to your non-unicorn buddies, so they can share in your powers, + or just so you can laugh when you teleport and they end up coming with - Send and receive items using the Dragon's Breath Scroll - Possibly more I'm forgetting about (or am I? OoOoOooOOoo...Spooky surprise mechanics) @@ -88,7 +91,7 @@ View the HOW_TO_PLAY.md file for more details. ### 1.19.3 Only This project uses reach-entity-attributes, which may not be updated at the time of this writing. -If you building for 1.19.3, you may follow these steps to make sure it's available to git: +If you're building for 1.19.3, you may follow these steps to make sure it's available to git: `git clone https://github.com/Sollace/reach-entity-attributes` `cd reach-entity-attributes` diff --git a/README_RU.md b/README_RU.md index b33b1564..64d5081b 100644 --- a/README_RU.md +++ b/README_RU.md @@ -48,19 +48,22 @@ ### Понифицированные картины - Ведь что это был бы за пони-мод, если бы в нём не было этого? У каждой расы есть хотя бы один рисунок, представляющий её, + Ведь что это был бы за пони-мод, если бы в нём _не_ было этого? У каждой расы есть хотя бы один рисунок, представляющий её, так что покажите свою гордость и поднимите флаг! - Дисклеймер: Радужных флагов нет (пока) + Дисклеймер: Радужных флагов (пока) нет ### Природные явления - - Воздушный поток (плохо) влияет на пегасов, остерегайтесь летать во время грозы! Там может быть опасно! - Если вы играете за летающий вид или просто любите приятные вещи, попробуйте построить метеорологическую жилу. + - Воздушный поток + + Пегасы, остерегайтесь полётов во время грозы! Это может быть опасно! + Если вы играете за летающий вид или просто любите приятные вещи, попробуйте построить погодную жилу. Она показывает фактическое, абсолютно реальное, а не плохо смоделированное направление ветра в вашем мире Minecraft. Только учтите, - что направление и сила ветра ситуативны (и плохи), и будут отличаться в зависимости от того, где вы находитесь и на какой высоте. + что направление и сила ветра ситуативны, и будут отличаться в зависимости от того, где вы находитесь и на какой высоте. - Горячий воздух поднимает + Нет, это не плохой фильм про "Звездные войны", это реальная механика. Песок и лава придают летающим видам дополнительную подъёмную силу. Вода - наоборот. Попробуйте! А вообще, не стоит, я не хочу чтобы вы утонули. diff --git a/src/main/java/com/minelittlepony/unicopia/AllowList.java b/src/main/java/com/minelittlepony/unicopia/AllowList.java new file mode 100644 index 00000000..f1810cd1 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/AllowList.java @@ -0,0 +1,57 @@ +package com.minelittlepony.unicopia; + +import java.util.Set; + +public class AllowList { + public static final AllowList INSTANCE = new AllowList(); + + public AllowList() { + + } + + public boolean disable() { + if (!isEnabled()) { + return false; + } + Unicopia.getConfig().speciesWhiteList.get().clear(); + Unicopia.getConfig().save(); + return true; + } + + public boolean isEnabled() { + return !Unicopia.getConfig().speciesWhiteList.get().isEmpty(); + } + + public boolean add(Race race) { + if (race.isUnset() || race.isHuman()) { + return false; + } + Set values = Unicopia.getConfig().speciesWhiteList.get(); + boolean added = values.add(race.getId().toString()); + Unicopia.getConfig().save(); + return added; + } + + public boolean remove(Race race) { + Set values = Unicopia.getConfig().speciesWhiteList.get(); + if (values.isEmpty()) { + for (Race r : Race.REGISTRY) { + if (!r.isUnset() && r != race) { + values.add(r.getId().toString()); + } + } + Unicopia.getConfig().save(); + return true; + } + boolean removed = values.remove(race.getId().toString()); + Unicopia.getConfig().save(); + return removed; + } + + public boolean permits(Race race) { + return race.isUnset() + || race.isHuman() + || !isEnabled() + || Unicopia.getConfig().speciesWhiteList.get().contains(race.getId().toString()); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/Race.java b/src/main/java/com/minelittlepony/unicopia/Race.java index ac63cfff..a6d73d72 100644 --- a/src/main/java/com/minelittlepony/unicopia/Race.java +++ b/src/main/java/com/minelittlepony/unicopia/Race.java @@ -19,6 +19,7 @@ import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; import net.minecraft.command.argument.RegistryKeyArgumentType; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; import net.minecraft.util.Identifier; import net.minecraft.registry.Registry; @@ -133,20 +134,16 @@ public record Race (Supplier compositeSupplier, Availability availabi } public boolean isPermitted(@Nullable PlayerEntity sender) { - Set whitelist = Unicopia.getConfig().speciesWhiteList.get(); - - return isUnset() - || whitelist.isEmpty() - || whitelist.contains(getId().toString()); + return AllowList.INSTANCE.permits(this); } public Race validate(PlayerEntity sender) { if (!isPermitted(sender)) { - if (this == EARTH) { - return HUMAN; + Race alternative = this == EARTH ? HUMAN : EARTH.validate(sender); + if (alternative != this && sender instanceof ServerPlayerEntity spe) { + spe.sendMessageToClient(Text.translatable("respawn.reason.illegal_race", getDisplayName()), false); } - - return EARTH.validate(sender); + return alternative; } return this; diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyKickAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyKickAbility.java index 9f6bb09c..0affd0cf 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyKickAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyKickAbility.java @@ -61,7 +61,7 @@ public class EarthPonyKickAbility implements Ability { @Override public Identifier getIcon(Pony player) { return getId().withPath(p -> "textures/gui/ability/" + p - + "_" + player.getObservedSpecies().getId().getPath() + + "_" + (player.getObservedSpecies().isHuman() ? Race.EARTH : player.getObservedSpecies()).getId().getPath() + "_" + (getKickDirection(player) > 0 ? "forward" : "backward") + ".png"); } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java index e81f5a0d..b702400f 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java @@ -70,8 +70,9 @@ public class EarthPonyStompAbility implements Ability { @Override public Identifier getIcon(Pony player) { Identifier id = Abilities.REGISTRY.getId(this); + Race race = player.getObservedSpecies(); return new Identifier(id.getNamespace(), "textures/gui/ability/" + id.getPath() - + "_" + player.getObservedSpecies().getId().getPath() + + "_" + (race.isHuman() ? Race.EARTH : race).getId().getPath() + ".png"); } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/TimeChangeAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/TimeChangeAbility.java index 15a00851..198aee2f 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/TimeChangeAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/TimeChangeAbility.java @@ -19,7 +19,7 @@ public class TimeChangeAbility implements Ability { @Override public boolean canUse(Race.Composite race) { - return race.pseudo() == Race.UNICORN; + return Ability.super.canUse(race) || race.pseudo() == Race.UNICORN; } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/ability/ToggleFlightAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/ToggleFlightAbility.java index d9811e74..4f20d77d 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/ToggleFlightAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/ToggleFlightAbility.java @@ -43,9 +43,10 @@ public class ToggleFlightAbility implements Ability { @Override public Identifier getIcon(Pony player) { Identifier id = Abilities.REGISTRY.getId(this); + Race race = player.getObservedSpecies(); return new Identifier(id.getNamespace(), "textures/gui/ability/" + id.getPath() + (player.getPhysics().isFlying() ? "_land" : "_takeoff") - + "_" + player.getObservedSpecies().getId().getPath() + + "_" + (race.isHuman() ? Race.EARTH : race).getId().getPath() + ".png"); } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/SpellReference.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/SpellReference.java index c8b504f8..cca32178 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/SpellReference.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/SpellReference.java @@ -50,6 +50,10 @@ public final class SpellReference implements NbtSerialisable { @Override public void fromNBT(NbtCompound compound) { + fromNBT(compound, true); + } + + public void fromNBT(NbtCompound compound, boolean force) { final int hash = compound.hashCode(); if (nbtHash == hash) { return; @@ -58,7 +62,7 @@ public final class SpellReference implements NbtSerialisable { if (spell == null || !Objects.equals(Spell.getUuid(compound), spell.getUuid())) { spell = Spell.readNbt(compound); - } else { + } else if (force || !spell.isDirty()) { spell.fromNBT(compound); } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/FlowingText.java b/src/main/java/com/minelittlepony/unicopia/client/TextHelper.java similarity index 59% rename from src/main/java/com/minelittlepony/unicopia/client/FlowingText.java rename to src/main/java/com/minelittlepony/unicopia/client/TextHelper.java index 1d820649..52d57c43 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/FlowingText.java +++ b/src/main/java/com/minelittlepony/unicopia/client/TextHelper.java @@ -1,12 +1,14 @@ package com.minelittlepony.unicopia.client; import java.util.Optional; +import java.util.stream.Collectors; import java.util.stream.Stream; +import java.util.stream.StreamSupport; import net.minecraft.client.MinecraftClient; import net.minecraft.text.*; -public interface FlowingText { +public interface TextHelper { static Stream wrap(Text text, int maxWidth) { return MinecraftClient.getInstance().textRenderer.getTextHandler().wrapLines(text, maxWidth, Style.EMPTY).stream().map(line -> { MutableText compiled = Text.literal(""); @@ -17,4 +19,11 @@ public interface FlowingText { return compiled; }); } + + static Text join(Text delimiter, Iterable elements) { + MutableText initial = Text.empty(); + return StreamSupport.stream(elements.spliterator(), false).collect(Collectors.reducing(initial, (a, b) -> { + return a == initial ? b : a.append(delimiter).append(b); + })); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java index e7e15b4e..4f436d79 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java @@ -89,7 +89,7 @@ public interface URenderers { AccessoryFeatureRenderer.register( BraceletFeatureRenderer::new, AmuletFeatureRenderer::new, GlassesFeatureRenderer::new, WingsFeatureRenderer::new, HornFeatureRenderer::new, IcarusWingsFeatureRenderer::new, BatWingsFeatureRenderer::new, - HeldEntityFeatureRenderer::new + HeldEntityFeatureRenderer::new, DisguisedArmsFeatureRenderer::new ); EntityRendererRegistry.register(UEntities.THROWN_ITEM, FlyingItemEntityRenderer::new); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/DismissSpellScreen.java b/src/main/java/com/minelittlepony/unicopia/client/gui/DismissSpellScreen.java index ca759456..57ea2a25 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/DismissSpellScreen.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/DismissSpellScreen.java @@ -9,7 +9,7 @@ import com.minelittlepony.common.client.gui.GameGui; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.magic.SpellPredicate; import com.minelittlepony.unicopia.ability.magic.spell.*; -import com.minelittlepony.unicopia.client.FlowingText; +import com.minelittlepony.unicopia.client.TextHelper; import com.minelittlepony.unicopia.client.render.model.SphereModel; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.item.UItems; @@ -195,7 +195,7 @@ public class DismissSpellScreen extends GameGui { tooltip.add(ScreenTexts.EMPTY); tooltip.add(Text.translatable("gui.unicopia.dispell_screen.affinity", actualSpell.getAffinity().name()).formatted(actualSpell.getAffinity().getColor())); tooltip.add(ScreenTexts.EMPTY); - tooltip.addAll(FlowingText.wrap(Text.translatable(actualSpell.getType().getTranslationKey() + ".lore").formatted(actualSpell.getAffinity().getColor()), 180).toList()); + tooltip.addAll(TextHelper.wrap(Text.translatable(actualSpell.getType().getTranslationKey() + ".lore").formatted(actualSpell.getAffinity().getColor()), 180).toList()); if (spell instanceof TimedSpell timed) { tooltip.add(ScreenTexts.EMPTY); tooltip.add(Text.translatable("gui.unicopia.dispell_screen.time_left", StringHelper.formatTicks(timed.getTimer().getTicksRemaining()))); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/TribeConfirmationScreen.java b/src/main/java/com/minelittlepony/unicopia/client/gui/TribeConfirmationScreen.java index cccc04b8..50967105 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/TribeConfirmationScreen.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/TribeConfirmationScreen.java @@ -28,6 +28,9 @@ public class TribeConfirmationScreen extends GameGui implements HidesHud { @Override protected void init() { + if (parent != null) { + parent.init(client, width, height); + } final int columnHeight = 167; final int columnWidth = 310; final int padding = 15; @@ -101,6 +104,13 @@ public class TribeConfirmationScreen extends GameGui implements HidesHud { @Override public void render(DrawContext context, int mouseX, int mouseY, float delta) { + if (parent != null) { + context.getMatrices().push(); + context.getMatrices().translate(0, 0, -100); + parent.render(context, 0, 0, delta); + context.getMatrices().pop(); + } + final int columnHeight = 180; final int columnWidth = 310; final int segmentWidth = 123; diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/TribeSelectionScreen.java b/src/main/java/com/minelittlepony/unicopia/client/gui/TribeSelectionScreen.java index 1dca0030..a18e17ca 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/TribeSelectionScreen.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/TribeSelectionScreen.java @@ -71,10 +71,7 @@ public class TribeSelectionScreen extends GameGui implements HidesHud { addOption(race, top); } - if (SELECTION == -1) { - SELECTION = options.size() / 2; - } - scroll(SELECTION, false); + scroll(SELECTION == -1 ? options.size() / 2 : SELECTION, false); } private void addOption(Race race, int y) { @@ -159,18 +156,21 @@ public class TribeSelectionScreen extends GameGui implements HidesHud { return true; } if (keyCode == GLFW.GLFW_KEY_ENTER) { - options.get(SELECTION).onPress(); + if (options.get(SELECTION).active) { + options.get(SELECTION).onPress(); + } } return super.keyPressed(keyCode, scanCode, modifiers); } private void scroll(int target, boolean animate) { - if (target == SELECTION) { + SELECTION = target; + target *= 4; + if (targetScroll == target) { return; } - SELECTION = target; - targetScroll = SELECTION * 4; + targetScroll = target; if (!animate) { scrollPosition = targetScroll; prevScrollPosition = scrollPosition; diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookScreen.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookScreen.java index afacbfa2..e60e8393 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookScreen.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookScreen.java @@ -14,7 +14,7 @@ import com.minelittlepony.unicopia.Debug; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; -import com.minelittlepony.unicopia.client.FlowingText; +import com.minelittlepony.unicopia.client.TextHelper; import com.minelittlepony.unicopia.client.gui.*; import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.*; import com.minelittlepony.unicopia.compat.trinkets.TrinketSlotBackSprites; @@ -218,7 +218,7 @@ public class SpellbookScreen extends HandledScreen imple List tooltip = new ArrayList<>(); tooltip.add(spell.type().getName()); - tooltip.addAll(FlowingText.wrap(Text.translatable(spell.type().getTranslationKey() + ".lore").formatted(spell.type().getAffinity().getColor()), 180).toList()); + tooltip.addAll(TextHelper.wrap(Text.translatable(spell.type().getTranslationKey() + ".lore").formatted(spell.type().getAffinity().getColor()), 180).toList()); context.drawTooltip(textRenderer, tooltip, x, y); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookTraitDexPageContent.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookTraitDexPageContent.java index e8f83db6..c5f7f347 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookTraitDexPageContent.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookTraitDexPageContent.java @@ -9,7 +9,7 @@ import com.minelittlepony.common.client.gui.element.Label; import com.minelittlepony.common.client.gui.sprite.TextureSprite; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.magic.spell.trait.*; -import com.minelittlepony.unicopia.client.FlowingText; +import com.minelittlepony.unicopia.client.TextHelper; import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Chapter; import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen.ImageButton; import com.minelittlepony.unicopia.container.SpellbookState; @@ -191,7 +191,7 @@ public class SpellbookTraitDexPageContent implements SpellbookChapterList.Conten .setTextureSize(16, 16) .setSize(16, 16) .setTexture(trait.getSprite())); - getStyle().setTooltip(Tooltip.of(FlowingText.wrap(trait.getTooltip(), 200).toList())); + getStyle().setTooltip(Tooltip.of(TextHelper.wrap(trait.getTooltip(), 200).toList())); onClick(sender -> Pony.of(MinecraftClient.getInstance().player).getDiscoveries().markRead(trait)); } diff --git a/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/Main.java b/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/Main.java index fccaca0a..0f2c8e0f 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/Main.java +++ b/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/Main.java @@ -10,6 +10,7 @@ import com.minelittlepony.api.events.PonyModelPrepareCallback; import com.minelittlepony.api.model.*; import com.minelittlepony.api.model.gear.Gear; import com.minelittlepony.api.pony.PonyData; +import com.minelittlepony.client.render.MobRenderers; import com.minelittlepony.unicopia.*; import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation; import com.minelittlepony.unicopia.compat.trinkets.TrinketsDelegate; @@ -18,6 +19,7 @@ import com.minelittlepony.unicopia.util.AnimationUtil; import net.fabricmc.api.ClientModInitializer; import net.minecraft.entity.Entity; +import net.minecraft.entity.passive.AllayEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.util.Util; import net.minecraft.util.math.MathHelper; @@ -105,6 +107,10 @@ public class Main extends MineLPDelegate implements ClientModInitializer { @Override public Race getRace(Entity entity) { + if (entity instanceof AllayEntity) { + return MobRenderers.ALLAY.get() ? Race.PEGASUS : Race.HUMAN; + } + return com.minelittlepony.api.pony.Pony.getManager().getPony(entity).map(com.minelittlepony.api.pony.Pony::race).map(Main::toUnicopiaRace).orElse(Race.HUMAN); } diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractBillboardParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractBillboardParticle.java index 02d624c6..52420762 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractBillboardParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractBillboardParticle.java @@ -27,6 +27,7 @@ public abstract class AbstractBillboardParticle extends AbstractGeometryBasedPar RenderSystem.disableCull(); RenderSystem.enableBlend(); RenderSystem.enableDepthTest(); + RenderSystem.defaultBlendFunc(); Vec3d cam = camera.getPos(); diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/DustCloudParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/DustCloudParticle.java index e39bb0b0..2e550c7a 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/DustCloudParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/DustCloudParticle.java @@ -1,11 +1,16 @@ package com.minelittlepony.unicopia.client.particle; +import org.joml.Vector4f; + import com.minelittlepony.common.util.Color; -import com.minelittlepony.unicopia.client.render.RenderUtil; +import com.minelittlepony.unicopia.client.render.model.FanModel; +import com.minelittlepony.unicopia.client.render.model.VertexLightSource; import net.minecraft.block.Blocks; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.Tessellator; +import net.minecraft.client.render.VertexFormat; +import net.minecraft.client.render.VertexFormats; import net.minecraft.client.texture.Sprite; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.world.ClientWorld; @@ -16,15 +21,15 @@ import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.RotationAxis; public class DustCloudParticle extends AbstractBillboardParticle { - //private static final Identifier TEXTURE = new Identifier("textures/particle/big_smoke_3.png"); - protected static final int SEGMENTS = 20; protected static final int SEPARATION = 270 / SEGMENTS; private float scaleFactor; protected Sprite sprite; - private final RenderUtil.Vertex[] vertices; + private final FanModel model; + + private final VertexLightSource lightSource; public DustCloudParticle(BlockStateParticleEffect effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) { super(world, x, y, z, velocityX, velocityY, velocityZ); @@ -33,14 +38,15 @@ public class DustCloudParticle extends AbstractBillboardParticle { red = 0.6F; green = 0.6F; blue = 0.6F; - alpha = (float)world.getRandom().nextTriangular(0.6, 0.2); + alpha = (float)world.getRandom().nextTriangular(0.6, 0.2) * 0.3F; scaleFactor = (float)world.getRandom().nextTriangular(2, 1.2); sprite = MinecraftClient.getInstance().getBlockRenderManager().getModels().getModelParticleSprite(effect.getBlockState()); - vertices = new RenderUtil.Vertex[]{ - new RenderUtil.Vertex(-1, -1, 0, sprite.getMinU(), sprite.getMinV()), - new RenderUtil.Vertex(-1, 1, 0, sprite.getMaxU(), sprite.getMinV()), - new RenderUtil.Vertex( 1, 1, 0, sprite.getMaxU(), sprite.getMaxV()), - new RenderUtil.Vertex( 1, -1, 0, sprite.getMinU(), sprite.getMaxV()) + lightSource = new VertexLightSource(world); + model = new FanModel(sprite) { + @Override + protected int getLightAt(Vector4f pos, int light) { + return lightSource.getLight(pos, light); + } }; if (!effect.getBlockState().isOf(Blocks.GRASS_BLOCK)) { int i = MinecraftClient.getInstance().getBlockColors().getColor(effect.getBlockState(), world, BlockPos.ofFloored(x, y, z), 0); @@ -60,26 +66,29 @@ public class DustCloudParticle extends AbstractBillboardParticle { super.tick(); scaleFactor += 0.001F; scale(MathHelper.clamp(age / 5F, 0, 1) * scaleFactor); + lightSource.tick(); } @Override protected void renderQuads(Tessellator te, BufferBuilder buffer, float x, float y, float z, float tickDelta) { - float scale = getScale(tickDelta); + float scale = getScale(tickDelta) * 0.5F; float alpha = this.alpha * (1 - ((float)age / maxAge)); MatrixStack matrices = new MatrixStack(); matrices.translate(x, y, z); - matrices.scale(scale, scale * 0.5F, scale); + matrices.scale(1, 0.5F, 1); - float angle = ((this.age + tickDelta) % 360) / SEGMENTS; + float angle = (MathHelper.sin((this.age + tickDelta) / 100F) * 360) / SEGMENTS; for (int i = 0; i < SEGMENTS; i++) { matrices.push(); - matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees((i * angle) % 360)); - matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees((SEPARATION * i + angle) % 360)); - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees((SEPARATION * i + angle) % 360)); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees((i * angle))); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees((SEPARATION * i - angle))); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees((SEPARATION * i + angle))); float ringScale = 1 + MathHelper.sin(((i * 10) + age + tickDelta) * 0.05F) * 0.1F; - matrices.scale(ringScale, ringScale, ringScale); - renderQuad(matrices, te, buffer, vertices, alpha, tickDelta); + + buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT); + model.render(matrices, buffer, 0, scale * ringScale, 1, 1, 1, alpha); + te.draw(); matrices.pop(); } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/DisguisedArmsFeatureRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/DisguisedArmsFeatureRenderer.java new file mode 100644 index 00000000..c130779c --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/DisguisedArmsFeatureRenderer.java @@ -0,0 +1,203 @@ +package com.minelittlepony.unicopia.client.render; + +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.jetbrains.annotations.Nullable; + +import com.google.common.base.MoreObjects; +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.SpellPredicate; +import com.minelittlepony.unicopia.client.FirstPersonRendererOverrides.ArmRenderer; +import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate; +import com.minelittlepony.unicopia.entity.behaviour.Disguise; +import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.datafixers.util.Pair; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.model.ModelPart; +import net.minecraft.client.render.OverlayTexture; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.entity.LivingEntityRenderer; +import net.minecraft.client.render.entity.feature.FeatureRendererContext; +import net.minecraft.client.render.entity.model.BipedEntityModel; +import net.minecraft.client.render.entity.model.EntityModel; +import net.minecraft.client.render.entity.model.EntityModelLayers; +import net.minecraft.client.render.entity.model.EntityModelPartNames; +import net.minecraft.client.render.entity.model.SinglePartEntityModel; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.ZombieEntity; +import net.minecraft.entity.passive.IronGolemEntity; +import net.minecraft.util.Arm; +import net.minecraft.util.Hand; +import net.minecraft.util.Identifier; +import net.minecraft.util.Util; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RotationAxis; + +public class DisguisedArmsFeatureRenderer implements AccessoryFeatureRenderer.Feature { + + private final MinecraftClient client = MinecraftClient.getInstance(); + + private static final Map, Identifier> OVERLAY_TEXTURES = Map.of( + EntityType.DROWNED, new Identifier("textures/entity/zombie/drowned_outer_layer.png"), + EntityType.STRAY, new Identifier("textures/entity/skeleton/stray_overlay.png") + ); + + private final Function, Set>> overlayModelCache = Util.memoize(type -> { + return EntityModelLayers.getLayers() + .filter(layer -> layer.getId().equals(EntityType.getId(type)) && !"main".equals(layer.getName())) + .map(MinecraftClient.getInstance().getEntityModelLoader()::getModelPart) + .map(model -> { + ModelPart arms = getPart(model, EntityModelPartNames.ARMS).orElse(null); + ModelPart leftArm = getPart(model, EntityModelPartNames.LEFT_ARM) + .or(() -> getPart(model, EntityModelPartNames.LEFT_FRONT_LEG)) + .orElse(arms); + ModelPart rightArm = getPart(model, EntityModelPartNames.RIGHT_ARM) + .or(() -> getPart(model, EntityModelPartNames.RIGHT_FRONT_LEG)) + .orElse(arms); + return leftArm != null && rightArm != null ? Pair.of(leftArm, rightArm) : null; + }) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + }); + + private static Optional getPart(ModelPart part, String childName) { + return part.hasChild(childName) ? Optional.of(part.getChild(childName)) : Optional.empty(); + } + + public DisguisedArmsFeatureRenderer(FeatureRendererContext> context) { + + } + + @Override + public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, E entity, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + } + + @Override + public boolean beforeRenderArms(ArmRenderer sender, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, E entity, int light) { + Entity appearance = getAppearance(entity); + if (appearance instanceof LivingEntity l) { + float swingProgress = entity.getHandSwingProgress(tickDelta); + + Hand hand = MoreObjects.firstNonNull(entity.preferredHand, Hand.MAIN_HAND); + + boolean bothHands = l instanceof ZombieEntity || l instanceof IronGolemEntity; + + if (bothHands || hand == Hand.MAIN_HAND) { + if (entity.getMainHandStack().isEmpty()) { + matrices.push(); + renderArmHoldingItem(l, matrices, vertexConsumers, light, 1 - sender.getEquipProgress(Hand.MAIN_HAND, tickDelta), hand == Hand.MAIN_HAND ? swingProgress : 0, entity.getMainArm()); + matrices.pop(); + } + } + + if (bothHands || hand == Hand.OFF_HAND) { + if (entity.getOffHandStack().isEmpty()) { + matrices.push(); + renderArmHoldingItem(l, matrices, vertexConsumers, light, 1 - sender.getEquipProgress(Hand.OFF_HAND, tickDelta), hand == Hand.OFF_HAND ? swingProgress : 0, entity.getMainArm().getOpposite()); + matrices.pop(); + } + } + } + + return false; + } + + private Entity getAppearance(E entity) { + return Caster.of(entity).flatMap(caster -> caster.getSpellSlot().get(SpellPredicate.IS_DISGUISE, false)).map(Disguise.class::cast) + .flatMap(Disguise::getAppearance) + .map(EntityAppearance::getAppearance) + .orElse(null); + } + + @SuppressWarnings("unchecked") + @Nullable + private ModelPart getArmModel(@Nullable EntityModel model, boolean right) { + + if (model instanceof BipedEntityModel bipedModel) { + return right ? bipedModel.rightArm : bipedModel.leftArm; + } + if (model instanceof SinglePartEntityModel quad) { + ModelPart arms = (ModelPart)quad.getChild(EntityModelPartNames.ARMS).orElse((ModelPart)null); + return (ModelPart)quad.getChild(right ? EntityModelPartNames.RIGHT_ARM : EntityModelPartNames.LEFT_ARM) + .or(() -> quad.getChild(right ? EntityModelPartNames.RIGHT_FRONT_LEG : EntityModelPartNames.LEFT_FRONT_LEG)) + .orElse(arms); + } + + return null; + } + + @SuppressWarnings("unchecked") + private void renderArmHoldingItem(LivingEntity entity, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, float equipProgress, float swingProgress, Arm arm) { + if (!(client.getEntityRenderDispatcher().getRenderer(entity) instanceof LivingEntityRenderer renderer)) { + return; + } + + boolean right = arm != Arm.LEFT; + EntityModel model = renderer.getModel(); + @Nullable + ModelPart part = getArmModel(model, right); + + if (part == null) { + return; + } + + model.animateModel(entity, 0, 0, 0); + model.setAngles(entity, 0, 0, 0, 0, client.getTickDelta()); + + float signum = right ? 1 : -1; + float srtSwingProgress = MathHelper.sqrt(swingProgress); + float xOffset = -0.3F * MathHelper.sin(srtSwingProgress * MathHelper.PI); + float yOffset = 0.4F * MathHelper.sin(srtSwingProgress * (MathHelper.TAU)); + float swingAmount = -0.4F * MathHelper.sin(swingProgress * MathHelper.PI); + matrices.translate(signum * (xOffset + 0.64000005F), yOffset + -0.6F + equipProgress * -0.6F, swingAmount + -0.71999997f); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(signum * 45)); + float zRot = MathHelper.sin(swingProgress * swingProgress * MathHelper.PI); + float yRot = MathHelper.sin(srtSwingProgress * MathHelper.PI); + + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(signum * yRot * 70)); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(signum * zRot * -20)); + matrices.translate(signum * -1, 3.6F, 3.5F); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(signum * 120)); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(200)); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(signum * -135)); + matrices.translate(signum * 5.6F, 0, 0); + + if (entity instanceof IronGolemEntity golem) { + int attackTicks = golem.getAttackTicksLeft(); + if (attackTicks > 0) { + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-signum * part.pitch * MathHelper.DEGREES_PER_RADIAN - 90 * signum)); + } + } + part.pitch = 0; + + if (MineLPDelegate.getInstance().getRace(entity).isEquine()) { + matrices.translate(0, -part.pivotY / 16F, 0); + } + + Identifier texture = renderer.getTexture(entity); + RenderSystem.setShaderTexture(0, texture); + part.render(matrices, vertexConsumers.getBuffer(RenderLayer.getEntityTranslucent(texture)), light, OverlayTexture.DEFAULT_UV); + + Identifier overlayTexture = OVERLAY_TEXTURES.get(entity.getType()); + if (overlayTexture != null) { + overlayModelCache.apply(entity.getType()).forEach(arms -> { + ModelPart armPart = right ? arms.getSecond() : arms.getFirst(); + armPart.copyTransform(part); + + RenderSystem.setShaderTexture(0, overlayTexture); + part.render(matrices, vertexConsumers.getBuffer(RenderLayer.getEntityTranslucent(overlayTexture)), light, OverlayTexture.DEFAULT_UV); + }); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/EntityDisguiseRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/EntityDisguiseRenderer.java index e130258c..1e0aba0c 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/EntityDisguiseRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/EntityDisguiseRenderer.java @@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.client.render; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.client.render.spell.SpellEffectsRenderDispatcher; import com.minelittlepony.unicopia.compat.pehkui.PehkUtil; import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.behaviour.Disguise; @@ -58,6 +59,11 @@ class EntityDisguiseRenderer { PehkUtil.clearScale(ee); }); + matrices.push(); + matrices.translate(x, y, z); + SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertexConsumers, light, pony, 0, 0, tickDelta, pony.asEntity().age + tickDelta, 0, 0); + matrices.pop(); + delegate.afterEntityRender(pony, matrices, light); PehkUtil.clearScale(e); return true; @@ -118,12 +124,11 @@ class EntityDisguiseRenderer { } @Nullable - private BipedEntityModel getBipedModel(Entity entity) { - if (delegate.client.getEntityRenderDispatcher().getRenderer(entity) instanceof LivingEntityRenderer livingRenderer + static BipedEntityModel getBipedModel(Entity entity) { + if (MinecraftClient.getInstance().getEntityRenderDispatcher().getRenderer(entity) instanceof LivingEntityRenderer livingRenderer && livingRenderer.getModel() instanceof BipedEntityModel biped) { return biped; } return null; } - } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java index 3bceca54..58e65456 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java @@ -50,7 +50,7 @@ public class BakedModel { textureMatrix.identity(); } - public final void render(MatrixStack matrices, VertexConsumer buffer, float scale, float r, float g, float b, float a) { + public final void render(MatrixStack matrices, VertexConsumer buffer, int light, float scale, float r, float g, float b, float a) { scale = Math.abs(scale); if (scale < 0.001F) { return; @@ -62,9 +62,13 @@ public class BakedModel { for (RenderUtil.Vertex vertex : vertices) { Vector4f pos = vertex.position(positionmatrix); Vector4f tex = vertex.texture(textureMatrix); - buffer.vertex(pos.x, pos.y, pos.z).texture(tex.x, tex.y).color(r, g, b, a).next(); + buffer.vertex(pos.x, pos.y, pos.z).texture(tex.x, tex.y).color(r, g, b, a).light(getLightAt(pos, light)).next(); } matrices.pop(); textureMatrix.identity(); } + + protected int getLightAt(Vector4f pos, int light) { + return light; + } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/model/FanModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/model/FanModel.java new file mode 100644 index 00000000..29eaf2d2 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/model/FanModel.java @@ -0,0 +1,40 @@ +package com.minelittlepony.unicopia.client.render.model; + +import com.minelittlepony.unicopia.client.render.RenderUtil; + +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.RotationAxis; + +public class FanModel extends BakedModel { + + public FanModel(Sprite sprite) { + RenderUtil.Vertex[] dorito = createDorito(sprite); + MatrixStack matrices = new MatrixStack(); + for (int d = 0; d < 12; d++) { + matrices.push(); + + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(30 * d)); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(15)); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(15 * d)); + matrices.translate(2.9F, 0, 0); + for (RenderUtil.Vertex corner : dorito) { + var position = corner.position(matrices.peek().getPositionMatrix()); + addVertex(position.x, position.y(), position.z(), corner.texture().x, corner.texture().y); + } + matrices.pop(); + } + } + + static RenderUtil.Vertex[] createDorito(Sprite sprite) { + float chunkSize = 1F; + float baseLength = 0.8F; + float uLength = sprite.getMaxU() - sprite.getMinU(); + return new RenderUtil.Vertex[]{ + new RenderUtil.Vertex(-chunkSize, -chunkSize * baseLength, 0, sprite.getMinU() + uLength * baseLength, sprite.getMinV()), + new RenderUtil.Vertex( chunkSize, 0, 0, sprite.getMaxU(), sprite.getMaxV()), + new RenderUtil.Vertex(-chunkSize, chunkSize * baseLength, 0, sprite.getMinU(), sprite.getMinV()), + new RenderUtil.Vertex(-chunkSize * 3, 0, 0, sprite.getMinU(), sprite.getMaxV()) + }; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/model/VertexLightSource.java b/src/main/java/com/minelittlepony/unicopia/client/render/model/VertexLightSource.java new file mode 100644 index 00000000..86184540 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/model/VertexLightSource.java @@ -0,0 +1,44 @@ +package com.minelittlepony.unicopia.client.render.model; + +import org.joml.Vector4f; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.WorldRenderer; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; + +public class VertexLightSource { + private final ClientWorld world; + private final Long2ObjectMap lightCache = new Long2ObjectOpenHashMap<>(); + + public VertexLightSource(ClientWorld world) { + this.world = world; + } + + public void tick() { + lightCache.clear(); + } + + public int getLight(Vector4f vertexPosition, int light) { + return lightCache.computeIfAbsent(getBlockPosition(vertexPosition), this::getLight); + } + + @SuppressWarnings("deprecation") + private int getLight(long p) { + final BlockPos pos = BlockPos.fromLong(p); + return world.isChunkLoaded(pos) ? WorldRenderer.getLightmapCoordinates(world, pos) : 0; + } + + private long getBlockPosition(Vector4f vertexPosition) { + Vec3d cameraPos = MinecraftClient.getInstance().gameRenderer.getCamera().getPos(); + return BlockPos.asLong( + MathHelper.floor(cameraPos.x + vertexPosition.x), + MathHelper.floor(cameraPos.y + vertexPosition.y), + MathHelper.floor(cameraPos.z + vertexPosition.z) + ); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/shader/UShaders.java b/src/main/java/com/minelittlepony/unicopia/client/render/shader/UShaders.java index fd86786d..9a7d3cc7 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/shader/UShaders.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/shader/UShaders.java @@ -3,23 +3,16 @@ package com.minelittlepony.unicopia.client.render.shader; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; -import org.jetbrains.annotations.Nullable; - import com.minelittlepony.unicopia.Unicopia; import net.fabricmc.fabric.api.client.rendering.v1.CoreShaderRegistrationCallback; import net.minecraft.client.gl.ShaderProgram; import net.minecraft.client.render.VertexFormat; import net.minecraft.client.render.VertexFormats; -public final class UShaders { - @Nullable - private static Supplier renderTypePortalSurfaceProgram = register("rendertype_portal_surface", VertexFormats.POSITION_COLOR); +public interface UShaders { + Supplier RENDER_TYPE_PORTAL_SURFACE = register("rendertype_portal_surface", VertexFormats.POSITION_COLOR); - public static ShaderProgram getRenderTypePortalSurfaceProgram() { - return renderTypePortalSurfaceProgram.get(); - } - - public static void bootstrap() { } + static void bootstrap() { } static Supplier register(String name, VertexFormat format) { AtomicReference holder = new AtomicReference<>(); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java index 823220f5..e594b647 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java @@ -94,14 +94,14 @@ class PortalFrameBuffer implements AutoCloseable { BufferBuilder buffer = tessellator.getBuffer(); float uScale = (float)framebuffer.viewportWidth / (float)framebuffer.textureWidth; float vScale = (float)framebuffer.viewportHeight / (float)framebuffer.textureHeight; - RenderSystem.setShader(UShaders::getRenderTypePortalSurfaceProgram); + RenderSystem.setShader(UShaders.RENDER_TYPE_PORTAL_SURFACE); //RenderSystem.setShader(GameRenderer::getPositionTexColorProgram); RenderSystem._setShaderTexture(0, framebuffer.getColorAttachment()); buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR); SphereModel.DISK.scaleUV(uScale, vScale); RenderSystem.setTextureMatrix(SphereModel.DISK.getTextureMatrix()); - SphereModel.DISK.render(matrices, buffer, 2F, 1, 1, 1, 1); + SphereModel.DISK.render(matrices, buffer, 1, 2F, 1, 1, 1, 1); tessellator.draw(); client.getTextureManager().bindTexture(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java index aa96a28f..1fc202ad 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java @@ -51,7 +51,7 @@ public class ShieldSpellRenderer extends SpellRenderer { model.render(matrices, buffer, light, 1, radius, colors[0], colors[1], colors[2], alpha * 0.2F); } else { matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180)); - matrices.scale(1, radius == 0 ? 1 : 2.6F / radius, 1); + matrices.scale(1, radius == 0 ? 1 : MathHelper.clamp(2.6F / radius, 0.7F, 1.8F), 1); SphereModel.SPHERE.render(matrices, buffer, light, 1, radius + thickness, colors[0], colors[1], colors[2], alpha * 0.08F); SphereModel.SPHERE.render(matrices, buffer, light, 1, radius - thickness, colors[0], colors[1], colors[2], alpha * 0.05F); SphereModel.SPHERE.render(matrices, buffer, light, 1, radius + thickness * 2, colors[0], colors[1], colors[2], alpha * 0.05F); diff --git a/src/main/java/com/minelittlepony/unicopia/command/ManaCommand.java b/src/main/java/com/minelittlepony/unicopia/command/ManaCommand.java index c549219b..b75fd8e8 100644 --- a/src/main/java/com/minelittlepony/unicopia/command/ManaCommand.java +++ b/src/main/java/com/minelittlepony/unicopia/command/ManaCommand.java @@ -33,6 +33,11 @@ public class ManaCommand { var bar = type.getBar(pony.getMagicalReserves()); float value = source.getArgument("value", Float.class); + if (type == ManaType.LEVEL) { + pony.getLevel().set((int)value); + value -= (int)value; + type = ManaType.XP; + } if (type == ManaType.XP) { int currentLevel = pony.getLevel().get(); while (type == ManaType.XP && value > 1) { @@ -53,7 +58,8 @@ public class ManaCommand { EXHAUSTION(MagicReserves::getExhaustion), ENERGY(MagicReserves::getEnergy), MANA(MagicReserves::getMana), - XP(MagicReserves::getXp); + XP(MagicReserves::getXp), + LEVEL(MagicReserves::getXp); private final Function getter; diff --git a/src/main/java/com/minelittlepony/unicopia/command/RacelistCommand.java b/src/main/java/com/minelittlepony/unicopia/command/RacelistCommand.java index 333da868..00d776a8 100644 --- a/src/main/java/com/minelittlepony/unicopia/command/RacelistCommand.java +++ b/src/main/java/com/minelittlepony/unicopia/command/RacelistCommand.java @@ -1,13 +1,16 @@ package com.minelittlepony.unicopia.command; +import java.util.HashSet; +import java.util.Set; import java.util.function.Function; - import com.minelittlepony.unicopia.*; +import com.minelittlepony.unicopia.client.TextHelper; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.util.Formatting; @@ -15,38 +18,58 @@ class RacelistCommand { static LiteralArgumentBuilder create() { return CommandManager.literal("racelist").requires(s -> s.hasPermissionLevel(3)) - .then(CommandManager.literal("allow") - .then(CommandManager.argument("race", Race.argument()) - .executes(context -> toggle(context.getSource(), context.getSource().getPlayer(), Race.fromArgument(context, "race"), "allowed", race -> { - - if (race.isUnset()) { - return false; + .then(CommandManager.literal("show") + .executes(context -> { + context.getSource().sendFeedback(() -> { + if (!AllowList.INSTANCE.isEnabled()) { + return Text.translatable("commands.racelist.inactive"); } + Set allowed = new HashSet<>(); + Set unallowed = new HashSet<>(); + Race.REGISTRY.forEach(race -> { + (AllowList.INSTANCE.permits(race) ? allowed : unallowed).add(Text.translatable("commands.racelist.get.list_item", + race.getDisplayName(), + Text.literal(race.getId().toString()).formatted(Formatting.GRAY) + )); + }); - boolean result = Unicopia.getConfig().speciesWhiteList.get().add(race.getId().toString()); - - Unicopia.getConfig().save(); - - return result; - })) + return Text.translatable("commands.racelist.get.allowed", allowed.size()).formatted(Formatting.YELLOW) + .append("\n").append(TextHelper.join(Text.literal("\n"), allowed)) + .append("\n") + .append(Text.translatable("commands.racelist.get.not_allowed", unallowed.size()).formatted(Formatting.YELLOW)) + .append("\n").append(TextHelper.join(Text.literal("\n"), unallowed)); + }, false); + return 0; + }) + ) + .then(CommandManager.literal("reset") + .executes(context -> { + boolean success = AllowList.INSTANCE.disable(); + context.getSource().sendFeedback(() -> Text.translatable("commands.racelist.reset." + (success ? "success" : "fail")).formatted(Formatting.YELLOW), false); + return 0; + }) + ) + .then(CommandManager.literal("allow") + .then(CommandManager.argument("race", Race.argument()).suggests(UCommandSuggestion.ALL_RACE_SUGGESTIONS) + .executes(context -> toggle(context.getSource(), context.getSource().getPlayer(), Race.fromArgument(context, "race"), "allowed", AllowList.INSTANCE::add)) )) .then(CommandManager.literal("disallow") - .then(CommandManager.argument("race", Race.argument()) - .executes(context -> toggle(context.getSource(), context.getSource().getPlayer(), Race.fromArgument(context, "race"), "disallowed", race -> { - boolean result = Unicopia.getConfig().speciesWhiteList.get().remove(race.getId().toString()); - - Unicopia.getConfig().save(); - - return result; - })) + .then(CommandManager.argument("race", Race.argument()).suggests(UCommandSuggestion.ALL_RACE_SUGGESTIONS) + .executes(context -> toggle(context.getSource(), context.getSource().getPlayer(), Race.fromArgument(context, "race"), "disallowed", AllowList.INSTANCE::remove)) )); } static int toggle(ServerCommandSource source, ServerPlayerEntity player, Race race, String action, Function func) { + boolean enabled = AllowList.INSTANCE.isEnabled(); + boolean success = func.apply(race); + + if (enabled != AllowList.INSTANCE.isEnabled()) { + source.sendFeedback(() -> Text.translatable("commands.racelist." + (enabled ? "disabled" : "enabled")).formatted(enabled ? Formatting.RED : Formatting.GREEN), false); + } + source.sendFeedback(() -> { String translationKey = "commands.racelist." + action; - - if (!func.apply(race)) { + if (!success) { if (race.isUnset()) { translationKey = "commands.racelist.illegal"; } else { @@ -54,8 +77,7 @@ class RacelistCommand { } } - Text formattedName = race.getDisplayName().copy().formatted(Formatting.GOLD); - return Text.translatable(translationKey, formattedName).formatted(Formatting.GREEN); + return Text.translatable(translationKey, race.getDisplayName().copy().formatted(Formatting.GOLD)).formatted(success ? Formatting.GREEN : Formatting.RED); }, false); return 0; } diff --git a/src/main/java/com/minelittlepony/unicopia/command/SpeciesCommand.java b/src/main/java/com/minelittlepony/unicopia/command/SpeciesCommand.java index 1363ee96..e1e9e999 100644 --- a/src/main/java/com/minelittlepony/unicopia/command/SpeciesCommand.java +++ b/src/main/java/com/minelittlepony/unicopia/command/SpeciesCommand.java @@ -6,7 +6,6 @@ import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.network.Channel; import com.minelittlepony.unicopia.network.MsgTribeSelect; import com.mojang.brigadier.builder.LiteralArgumentBuilder; - import net.minecraft.command.argument.EntityArgumentType; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.command.CommandManager; @@ -38,13 +37,13 @@ class SpeciesCommand { .executes(context -> get(context.getSource(), EntityArgumentType.getPlayer(context, "target"), false)) )) .then(CommandManager.literal("set") - .then(CommandManager.argument("race", Race.argument()) + .then(CommandManager.argument("race", Race.argument()).suggests(UCommandSuggestion.ALL_RACE_SUGGESTIONS) .executes(context -> set(context.getSource(), context.getSource().getPlayer(), Race.fromArgument(context, "race"), true)) .then(CommandManager.argument("target", EntityArgumentType.player()) .executes(context -> set(context.getSource(), EntityArgumentType.getPlayer(context, "target"), Race.fromArgument(context, "race"), false))) )) .then(CommandManager.literal("describe") - .then(CommandManager.argument("race", Race.argument()) + .then(CommandManager.argument("race", Race.argument()).suggests(UCommandSuggestion.ALL_RACE_SUGGESTIONS) .executes(context -> describe(context.getSource().getPlayer(), Race.fromArgument(context, "race"))) )) .then(CommandManager.literal("list") @@ -60,7 +59,7 @@ class SpeciesCommand { pony.setDirty(); if (race.isUnset()) { - Channel.SERVER_SELECT_TRIBE.sendToPlayer(new MsgTribeSelect(Race.allPermitted(player), "gui.unicopia.tribe_selection.respawn"), (ServerPlayerEntity)player); + Channel.SERVER_SELECT_TRIBE.sendToPlayer(new MsgTribeSelect(Race.allPermitted(player), "gui.unicopia.tribe_selection.welcome"), (ServerPlayerEntity)player); } if (player == source.getPlayer()) { diff --git a/src/main/java/com/minelittlepony/unicopia/command/UCommandSuggestion.java b/src/main/java/com/minelittlepony/unicopia/command/UCommandSuggestion.java new file mode 100644 index 00000000..39d8f858 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/command/UCommandSuggestion.java @@ -0,0 +1,62 @@ +package com.minelittlepony.unicopia.command; + +import java.util.Locale; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.Function; +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.Race; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; + +import net.minecraft.command.CommandSource; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.util.Identifier; + +public class UCommandSuggestion { + public static final SuggestionProvider ALL_RACE_SUGGESTIONS = suggestFromRegistry(Race.REGISTRY_KEY); + public static final SuggestionProvider ALLOWED_RACE_SUGGESTIONS = suggestFromRegistry(Race.REGISTRY_KEY, (context, race) -> race.isPermitted(context.getSource().getPlayer())); + + public static SuggestionProvider suggestFromRegistry(RegistryKey> registryKey, @Nullable BiPredicate, T> filter) { + return (context, builder) -> { + Registry registry = context.getSource().getRegistryManager().get(registryKey); + return suggestIdentifiers( + filter == null ? registry : registry.stream().filter(v -> filter.test(context, v))::iterator, + registry::getId, + builder, registryKey.getValue().getNamespace()); + }; + } + + public static SuggestionProvider suggestFromRegistry(RegistryKey> registryKey) { + return suggestFromRegistry(registryKey, null); + } + + public static CompletableFuture suggestIdentifiers(Iterable candidates, Function idFunc, SuggestionsBuilder builder, String defaultNamespace) { + forEachMatching(candidates, builder.getRemaining().toLowerCase(Locale.ROOT), idFunc, id -> builder.suggest(idFunc.apply(id).toString()), defaultNamespace); + return builder.buildFuture(); + } + + public static void forEachMatching(Iterable candidates, String input, Function idFunc, Consumer consumer, String defaultNamespace) { + final boolean hasNamespaceDelimiter = input.indexOf(58) > -1; + for (T object : candidates) { + final Identifier id = idFunc.apply(object); + if (hasNamespaceDelimiter) { + if (CommandSource.shouldSuggest(input, id.toString())) { + consumer.accept(object); + } + } else { + if (CommandSource.shouldSuggest(input, id.getNamespace()) + || (id.getNamespace().equals(defaultNamespace) && CommandSource.shouldSuggest(input, id.getPath())) + ) { + consumer.accept(object); + } + } + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/command/WorldTribeCommand.java b/src/main/java/com/minelittlepony/unicopia/command/WorldTribeCommand.java index e09b9af1..116068f7 100644 --- a/src/main/java/com/minelittlepony/unicopia/command/WorldTribeCommand.java +++ b/src/main/java/com/minelittlepony/unicopia/command/WorldTribeCommand.java @@ -14,7 +14,7 @@ class WorldTribeCommand { return CommandManager.literal("worldtribe").requires(s -> s.hasPermissionLevel(3)) .then(CommandManager.literal("get").executes(context -> get(context.getSource()))) .then(CommandManager.literal("set") - .then(CommandManager.argument("race", Race.argument()) + .then(CommandManager.argument("race", Race.argument()).suggests(UCommandSuggestion.ALLOWED_RACE_SUGGESTIONS) .executes(context -> set(context.getSource(), Race.fromArgument(context, "race"))))); } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Living.java b/src/main/java/com/minelittlepony/unicopia/entity/Living.java index 96e515c8..661b8b0a 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/Living.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/Living.java @@ -18,6 +18,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.Situation; import com.minelittlepony.unicopia.advancement.UCriteria; import com.minelittlepony.unicopia.compat.trinkets.TrinketsDelegate; import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance; +import com.minelittlepony.unicopia.entity.behaviour.Guest; import com.minelittlepony.unicopia.entity.collision.MultiBoundingBoxEntity; import com.minelittlepony.unicopia.entity.damage.MagicalDamageSource; import com.minelittlepony.unicopia.entity.duck.LivingEntityDuck; @@ -36,6 +37,7 @@ import com.minelittlepony.unicopia.server.world.DragonBreathStore; import com.minelittlepony.unicopia.util.*; import it.unimi.dsi.fastutil.floats.Float2ObjectFunction; +import net.fabricmc.fabric.api.util.TriState; import net.minecraft.block.BlockState; import net.minecraft.enchantment.Enchantment; import net.minecraft.enchantment.EnchantmentHelper; @@ -44,6 +46,7 @@ import net.minecraft.entity.attribute.EntityAttribute; import net.minecraft.entity.attribute.EntityAttributeInstance; import net.minecraft.entity.attribute.EntityAttributeModifier; import net.minecraft.entity.damage.DamageSource; +import net.minecraft.entity.damage.DamageTypes; import net.minecraft.entity.data.*; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.projectile.ProjectileEntity; @@ -92,6 +95,8 @@ public abstract class Living implements Equine, Caste @Nullable private Caster attacker; + @Nullable + private transient Caster host; private Optional> target = Optional.empty(); @@ -476,6 +481,19 @@ public abstract class Living implements Equine, Caste public Optional onDamage(DamageSource source, float amount) { + if (Guest.of(source.getAttacker()).hostIs(this) + || Guest.of(source.getSource()).hostIs(this)) { + var type = source.getTypeRegistryEntry(); + return Optional.of(entity.damage( + type.matchesKey(DamageTypes.FIREBALL) ? entity.getDamageSources().create(DamageTypes.UNATTRIBUTED_FIREBALL) : + type.matchesKey(DamageTypes.PLAYER_EXPLOSION) ? entity.getDamageSources().create(DamageTypes.EXPLOSION) : + new DamageSource(type, entity, entity), amount)); + } + + if (Guest.of(entity).getHost() instanceof Living l) { + l.asEntity().damage(source, amount); + } + if (source.isIn(DamageTypeTags.IS_LIGHTNING) && (invinsibilityTicks > 0 || tryCaptureLightning())) { return Optional.of(false); } @@ -500,6 +518,10 @@ public abstract class Living implements Equine, Caste return Optional.empty(); } + public TriState canBeHurtByWater() { + return TriState.DEFAULT; + } + public Optional chooseClimbingPos() { return getSpellSlot().get(SpellPredicate.IS_DISGUISE, false) .map(AbstractDisguiseSpell::getDisguise) diff --git a/src/main/java/com/minelittlepony/unicopia/entity/behaviour/Disguise.java b/src/main/java/com/minelittlepony/unicopia/entity/behaviour/Disguise.java index 272cde97..7041b6e7 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/behaviour/Disguise.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/behaviour/Disguise.java @@ -9,6 +9,7 @@ import com.minelittlepony.unicopia.Owned; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.duck.LivingEntityDuck; +import com.minelittlepony.unicopia.entity.duck.RotatedView; import com.minelittlepony.unicopia.entity.player.PlayerDimensions; import com.minelittlepony.unicopia.entity.player.Pony; @@ -100,7 +101,14 @@ public interface Disguise extends FlightType.Provider, PlayerDimensions.Provider behaviour.copyBaseAttributes(owner, entity); if (tick && !getDisguise().skipsUpdate()) { - entity.tick(); + ((RotatedView)entity.getWorld()).setMirrorEntityStatuses(entity.getWorld().isClient); + if (entity.getWorld().isClient) { + entity.tick(); + } else { + entity.tick(); + } + + ((RotatedView)entity.getWorld()).setMirrorEntityStatuses(false); } if (!(owner instanceof PlayerEntity) && !((LivingEntityDuck)owner).isJumping()) { diff --git a/src/main/java/com/minelittlepony/unicopia/entity/behaviour/EndermanBehaviour.java b/src/main/java/com/minelittlepony/unicopia/entity/behaviour/EndermanBehaviour.java index 1e1562a6..e18f3761 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/behaviour/EndermanBehaviour.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/behaviour/EndermanBehaviour.java @@ -5,11 +5,13 @@ import com.minelittlepony.unicopia.entity.Living; import net.minecraft.entity.mob.EndermanEntity; import net.minecraft.item.BlockItem; import net.minecraft.item.ItemStack; +import net.minecraft.predicate.entity.EntityPredicates; import net.minecraft.util.Hand; public class EndermanBehaviour extends EntityBehaviour { @Override public void update(Living source, EndermanEntity entity, Disguise spell) { + entity.setInvulnerable(!EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR.test(source.asEntity())); if (source.asEntity().isSneaking() || source.asEntity().isSprinting()) { entity.setTarget(entity); } else { @@ -22,5 +24,13 @@ public class EndermanBehaviour extends EntityBehaviour { } else { entity.setCarriedBlock(null); } + + //if (entity.hurtTime > 0) { + /* Vec3d teleportedPos = entity.getPos(); + + if (!teleportedPos.equals(source.asEntity().getPos())) { + source.asEntity().refreshPositionAfterTeleport(teleportedPos.x, teleportedPos.y, teleportedPos.z); + }*/ + //} } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/behaviour/EntityAppearance.java b/src/main/java/com/minelittlepony/unicopia/entity/behaviour/EntityAppearance.java index b6cb7492..f2a84233 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/behaviour/EntityAppearance.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/behaviour/EntityAppearance.java @@ -204,12 +204,18 @@ public class EntityAppearance implements NbtSerialisable, PlayerDimensions.Provi return; } - if (entity instanceof LivingEntity) { - ((LivingEntity) entity).getAttributeInstance(UEntityAttributes.ENTITY_GRAVITY_MODIFIER).clearModifiers(); + if (entity instanceof LivingEntity l) { + l.getAttributeInstance(UEntityAttributes.ENTITY_GRAVITY_MODIFIER).clearModifiers(); + } + + if (entity instanceof Guest guest) { + guest.setHost(source); } if (source.isClient()) { source.asWorld().spawnEntity(entity); + } else { + entity.setId(source.asEntity().getId()); } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/behaviour/EntityBehaviour.java b/src/main/java/com/minelittlepony/unicopia/entity/behaviour/EntityBehaviour.java index b37e06df..a04113e6 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/behaviour/EntityBehaviour.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/behaviour/EntityBehaviour.java @@ -276,7 +276,8 @@ public class EntityBehaviour { static { register(PlayerBehaviour::new, EntityType.PLAYER); register(FallingBlockBehaviour::new, EntityType.FALLING_BLOCK); - register(MobBehaviour::new, EntityType.RAVAGER, EntityType.IRON_GOLEM); + register(MobBehaviour::new, EntityType.RAVAGER); + register(IronGolemBehaviour::new, EntityType.IRON_GOLEM); register(HoppingBehaviour::new, EntityType.RABBIT, EntityType.SLIME, EntityType.MAGMA_CUBE); register(TraderBehaviour::new, EntityType.VILLAGER, EntityType.WANDERING_TRADER); register(SteedBehaviour::new, EntityType.HORSE, EntityType.DONKEY, EntityType.SKELETON_HORSE, EntityType.ZOMBIE_HORSE); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/behaviour/Guest.java b/src/main/java/com/minelittlepony/unicopia/entity/behaviour/Guest.java new file mode 100644 index 00000000..7d432c2d --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/behaviour/Guest.java @@ -0,0 +1,37 @@ +package com.minelittlepony.unicopia.entity.behaviour; + +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.ability.magic.Caster; + +import net.minecraft.entity.Entity; + +public interface Guest { + Guest NULL = new Guest() { + @Override + public void setHost(@Nullable Caster host) { } + + @Nullable + @Override + public Caster getHost() { + return null; + } + }; + + void setHost(@Nullable Caster host); + + @Nullable + Caster getHost(); + + static Guest of(@Nullable Entity entity) { + return entity == null ? NULL : (Guest)entity; + } + + default boolean hasHost() { + return getHost() != null; + } + + default boolean hostIs(Caster self) { + return getHost() == self; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/behaviour/IronGolemBehaviour.java b/src/main/java/com/minelittlepony/unicopia/entity/behaviour/IronGolemBehaviour.java new file mode 100644 index 00000000..f0ba9640 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/behaviour/IronGolemBehaviour.java @@ -0,0 +1,18 @@ +package com.minelittlepony.unicopia.entity.behaviour; + +import com.minelittlepony.unicopia.entity.player.Pony; + +import net.minecraft.entity.passive.IronGolemEntity; +import net.minecraft.item.Items; +import net.minecraft.util.Hand; + +public class IronGolemBehaviour extends MobBehaviour { + @Override + public void update(Pony player, IronGolemEntity entity, Disguise spell) { + super.update(player, entity, spell); + boolean hasPoppy = player.asEntity().getStackInHand(Hand.MAIN_HAND).isOf(Items.POPPY); + if (hasPoppy != entity.getLookingAtVillagerTicks() > 0) { + entity.setLookingAtVillager(hasPoppy); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/behaviour/MobBehaviour.java b/src/main/java/com/minelittlepony/unicopia/entity/behaviour/MobBehaviour.java index a56ae3d9..450b8a61 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/behaviour/MobBehaviour.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/behaviour/MobBehaviour.java @@ -5,6 +5,9 @@ import com.minelittlepony.unicopia.util.TraceHelper; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.passive.IronGolemEntity; +import net.minecraft.item.Items; +import net.minecraft.util.Hand; public class MobBehaviour extends EntityBehaviour { @@ -23,6 +26,13 @@ public class MobBehaviour extends EntityBehaviour { entity.tryAttack(target); target.setAttacker(player.asEntity()); } + + if (entity instanceof IronGolemEntity i) { + boolean hasPoppy = player.asEntity().getStackInHand(Hand.MAIN_HAND).isOf(Items.POPPY); + if (hasPoppy != i.getLookingAtVillagerTicks() > 0) { + i.setLookingAtVillager(hasPoppy); + } + } } protected LivingEntity findTarget(Pony player, T entity) { diff --git a/src/main/java/com/minelittlepony/unicopia/entity/duck/EntityDuck.java b/src/main/java/com/minelittlepony/unicopia/entity/duck/EntityDuck.java index 659a3a24..fbe95209 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/duck/EntityDuck.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/duck/EntityDuck.java @@ -3,13 +3,14 @@ package com.minelittlepony.unicopia.entity.duck; import java.util.Set; import com.minelittlepony.unicopia.compat.pehkui.PehkuiEntityExtensions; +import com.minelittlepony.unicopia.entity.behaviour.Guest; import net.minecraft.entity.Entity; import net.minecraft.entity.Entity.RemovalReason; import net.minecraft.fluid.Fluid; import net.minecraft.registry.tag.TagKey; -public interface EntityDuck extends LavaAffine, PehkuiEntityExtensions { +public interface EntityDuck extends LavaAffine, PehkuiEntityExtensions, Guest { Set> getSubmergedFluidTags(); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/duck/LivingEntityDuck.java b/src/main/java/com/minelittlepony/unicopia/entity/duck/LivingEntityDuck.java index 850e673e..a65c2a25 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/duck/LivingEntityDuck.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/duck/LivingEntityDuck.java @@ -5,7 +5,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.Hand; import net.minecraft.util.math.Vec3d; -public interface LivingEntityDuck { +public interface LivingEntityDuck extends EntityDuck { void updateItemUsage(Hand hand, ItemStack stack, int time); boolean isJumping(); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/duck/RotatedView.java b/src/main/java/com/minelittlepony/unicopia/entity/duck/RotatedView.java index 33e09463..e5c60d40 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/duck/RotatedView.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/duck/RotatedView.java @@ -10,6 +10,8 @@ public interface RotatedView { boolean hasTransform(); + void setMirrorEntityStatuses(boolean enable); + default void pushRotation(int y) { getRotations().add(y); } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/CrystalShardsEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/CrystalShardsEntity.java index c23327d5..12ff12ee 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/CrystalShardsEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/CrystalShardsEntity.java @@ -8,6 +8,7 @@ import java.util.stream.Collectors; import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.entity.behaviour.Guest; import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.particle.ParticleUtils; @@ -172,9 +173,11 @@ public class CrystalShardsEntity extends StationaryObjectEntity { } } - if (isDead() || isInvalid(getWorld(), getBlockPos(), getAttachmentFace())) { - kill(); - ParticleUtils.spawnParticles(ParticleTypes.CLOUD, this, 10); + if (!Guest.of(this).hasHost()) { + if (isDead() || isInvalid(getWorld(), getBlockPos(), getAttachmentFace())) { + kill(); + ParticleUtils.spawnParticles(ParticleTypes.CLOUD, this, 10); + } } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/FriendlyCreeperEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/FriendlyCreeperEntity.java index 01e128d5..785dad5f 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/FriendlyCreeperEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/FriendlyCreeperEntity.java @@ -10,6 +10,7 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.entity.Creature; import com.minelittlepony.unicopia.entity.Equine; import com.minelittlepony.unicopia.entity.ai.FleeExplosionGoal; +import com.minelittlepony.unicopia.entity.behaviour.Guest; import net.minecraft.block.BlockState; import net.minecraft.client.render.entity.feature.SkinOverlayOwner; @@ -200,7 +201,7 @@ public class FriendlyCreeperEntity extends TameableEntity implements SkinOverlay lastHugTime = hugTime; - if (!isTamed()) { + if (!isTamed() && ((Guest)this).getHost() == null) { if (isConverting()) { if (++hugTime >= 100) { if (!getWorld().isClient) { diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/StationaryObjectEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/StationaryObjectEntity.java index 3e89f204..8b0c61e8 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/StationaryObjectEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/StationaryObjectEntity.java @@ -59,7 +59,6 @@ public abstract class StationaryObjectEntity extends Entity implements UDamageSo } protected void onHurt() { - } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java index e1a6cd75..2b4b93d0 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java @@ -88,6 +88,9 @@ public class PlayerPhysics extends EntityPhysics implements Tickab private final Pony pony; + private Lerp updraft = new Lerp(0); + private Lerp windStrength = new Lerp(0); + public PlayerPhysics(Pony pony) { super(pony.asEntity(), Creature.GRAVITY); this.pony = pony; @@ -364,6 +367,8 @@ public class PlayerPhysics extends EntityPhysics implements Tickab soundPlaying = false; descentRate = 0; ticksDiving = 0; + updraft.update(0, 100); + windStrength.update(0, 100); if (Abilities.RAINBOOM.canUse(pony.getCompositeRace()) && entity.isOnGround()) { pony.getMagicalReserves().getCharge().set(0); @@ -376,6 +381,8 @@ public class PlayerPhysics extends EntityPhysics implements Tickab } else { descentRate = 0; soundPlaying = false; + updraft.update(0, 100); + windStrength.update(0, 100); } if (!entity.isOnGround()) { @@ -567,7 +574,7 @@ public class PlayerPhysics extends EntityPhysics implements Tickab if (entity.isOnGround() || !force) { BlockState steppingState = pony.asEntity().getSteppingBlockState(); if (steppingState.isIn(UTags.KICKS_UP_DUST)) { - pony.addParticle(new BlockStateParticleEffect(UParticles.DUST_CLOUD, steppingState), pony.getOrigin().down().toCenterPos(), Vec3d.ZERO); + pony.addParticle(new BlockStateParticleEffect(UParticles.DUST_CLOUD, steppingState), pony.getOrigin().toCenterPos(), Vec3d.ZERO); } else { Supplier pos = VecHelper.sphere(pony.asWorld().getRandom(), 0.5D); Supplier vel = VecHelper.sphere(pony.asWorld().getRandom(), 0.015D); @@ -623,12 +630,13 @@ public class PlayerPhysics extends EntityPhysics implements Tickab velocity.x += - forward * MathHelper.sin(entity.getYaw() * 0.017453292F); velocity.z += forward * MathHelper.cos(entity.getYaw() * 0.017453292F); - if (entity.getWorld().hasRain(entity.getBlockPos())) { applyTurbulance(velocity); } else { - double updraft = WeatherConditions.getUpdraft(new BlockPos.Mutable().set(entity.getBlockPos()), entity.getWorld()) / 3F; - updraft *= 1 + motion; + float targetUpdraft = (float)WeatherConditions.getUpdraft(new BlockPos.Mutable().set(entity.getBlockPos()), entity.getWorld()) / 3F; + targetUpdraft *= 1 + motion; + this.updraft.update(targetUpdraft, targetUpdraft > this.updraft.getTarget() ? 30_000 : 3000); + double updraft = this.updraft.getValue(); velocity.y += updraft; descentRate -= updraft; } @@ -702,19 +710,25 @@ public class PlayerPhysics extends EntityPhysics implements Tickab .multiply(globalEffectStrength / 100D) .multiply(1 / (1 + Math.floor(pony.getLevel().get() / 10F))); + + + + if (effectStrength * gust.getX() >= 1) { SoundEmitter.playSoundAt(entity, USounds.AMBIENT_WIND_GUST, SoundCategory.AMBIENT, 3, 1); } float weight = 1 + (EnchantmentHelper.getEquipmentLevel(UEnchantments.HEAVY, entity) * 0.8F) + (pony.getCompositeRace().canUseEarth() ? 1 : 0); - velocity.add(WeatherConditions.getAirflow(entity.getBlockPos(), entity.getWorld()), 0.04F * effectStrength); - velocity.add(Vec3d.fromPolar( - (entity.getPitch() + (float)gust.getY()) * MathHelper.RADIANS_PER_DEGREE, - (entity.getYaw() + (float)gust.getZ()) * MathHelper.RADIANS_PER_DEGREE - ), - effectStrength * (float)gust.getX() / weight - ); + Vec3d airflow = WeatherConditions.getAirflow(entity.getBlockPos(), entity.getWorld()) + .multiply(0.04F * effectStrength) + .add(Vec3d.fromPolar( + (entity.getPitch() + (float)gust.getY()) * MathHelper.RADIANS_PER_DEGREE, + (entity.getYaw() + (float)gust.getZ()) * MathHelper.RADIANS_PER_DEGREE + ).multiply(effectStrength * (float)gust.getX() / weight)); + + windStrength.update((float)airflow.length(), airflow.length() > windStrength.getValue() ? 1000 : 500); + velocity.add(airflow.normalize(), windStrength.getValue()); if (!entity.getWorld().isClient && effectStrength > 0.9F && entity.getWorld().isThundering() && entity.getWorld().random.nextInt(9000) == 0) { LightningEntity lightning = EntityType.LIGHTNING_BOLT.create(entity.getWorld()); @@ -794,6 +808,7 @@ public class PlayerPhysics extends EntityPhysics implements Tickab compound.putBoolean("isFlyingEither", isFlyingEither); compound.putInt("ticksInAir", ticksInAir); compound.putFloat("descentRate", descentRate); + compound.putFloat("updraft", updraft.getValue()); } @Override @@ -804,6 +819,7 @@ public class PlayerPhysics extends EntityPhysics implements Tickab isFlyingEither = compound.getBoolean("isFlyingEither"); ticksInAir = compound.getInt("ticksInAir"); descentRate = compound.getFloat("descentRate"); + updraft.update(compound.getFloat("updraft"), 0); entity.calculateDimensions(); } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java index fab87347..072cee98 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -28,6 +28,7 @@ import com.minelittlepony.unicopia.entity.mob.UEntityAttributes; import com.minelittlepony.unicopia.entity.player.MagicReserves.Bar; import com.minelittlepony.unicopia.item.FriendshipBraceletItem; import com.minelittlepony.unicopia.item.UItems; +import com.minelittlepony.unicopia.item.enchantment.EnchantmentUtil; import com.minelittlepony.unicopia.item.enchantment.UEnchantments; import com.minelittlepony.unicopia.util.*; import com.minelittlepony.unicopia.network.*; @@ -39,7 +40,6 @@ import com.minelittlepony.common.util.animation.Interpolator; import com.mojang.authlib.GameProfile; import net.minecraft.enchantment.Enchantment; -import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.enchantment.Enchantments; import net.minecraft.entity.*; import net.minecraft.entity.attribute.DefaultAttributeContainer; @@ -231,7 +231,7 @@ public class Pony extends Living implements Copyable, Update public void setSpecies(Race race) { race = race.validate(entity); Race current = getSpecies(); - entity.getDataTracker().set(RACE, Race.REGISTRY.getId(race.validate(entity)).toString()); + entity.getDataTracker().set(RACE, race.getId().toString()); if (race != current) { clearSuppressedRace(); } @@ -244,7 +244,7 @@ public class Pony extends Living implements Copyable, Update } public void setSuppressedRace(Race race) { - entity.getDataTracker().set(SUPPRESSED_RACE, Race.REGISTRY.getId(race.validate(entity)).toString()); + entity.getDataTracker().set(SUPPRESSED_RACE, race.validate(entity).getId().toString()); } public void clearSuppressedRace() { @@ -919,7 +919,7 @@ public class Pony extends Living implements Copyable, Update PlayerInventory inventory = oldPlayer.asEntity().getInventory(); for (int i = 0; i < inventory.size(); i++) { ItemStack stack = inventory.getStack(i); - if (EnchantmentHelper.getLevel(UEnchantments.HEART_BOUND, stack) > 0) { + if (EnchantmentUtil.consumeEnchantment(UEnchantments.HEART_BOUND, 1, stack, entity.getWorld().random, EnchantmentUtil.getLuck(3, oldPlayer.asEntity()))) { asEntity().getInventory().setStack(i, stack); } } diff --git a/src/main/java/com/minelittlepony/unicopia/item/GemstoneItem.java b/src/main/java/com/minelittlepony/unicopia/item/GemstoneItem.java index 62160c86..dcc35a05 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/GemstoneItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/GemstoneItem.java @@ -10,7 +10,7 @@ import com.minelittlepony.unicopia.Affinity; import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; -import com.minelittlepony.unicopia.client.FlowingText; +import com.minelittlepony.unicopia.client.TextHelper; import com.minelittlepony.unicopia.entity.player.PlayerCharmTracker; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.item.group.MultiItem; @@ -90,7 +90,7 @@ public class GemstoneItem extends Item implements MultiItem, EnchantableItem { line = line.formatted(Formatting.OBFUSCATED); } - lines.addAll(FlowingText.wrap(line, 180).toList()); + lines.addAll(TextHelper.wrap(line, 180).toList()); } } diff --git a/src/main/java/com/minelittlepony/unicopia/item/enchantment/EnchantmentUtil.java b/src/main/java/com/minelittlepony/unicopia/item/enchantment/EnchantmentUtil.java index 5bc6d8f2..f518d5d3 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/enchantment/EnchantmentUtil.java +++ b/src/main/java/com/minelittlepony/unicopia/item/enchantment/EnchantmentUtil.java @@ -16,6 +16,7 @@ import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.random.Random; public interface EnchantmentUtil { + String HEART_BOUND_CONSUMED_FLAG = "unicopia:heart_bound_consumed"; static boolean consumeEnchantment(Enchantment enchantment, int levels, ItemStack stack) { return consumeEnchantment(enchantment, levels, stack, null, 0); @@ -33,7 +34,7 @@ public interface EnchantmentUtil { if (level == 0) { enchantments.remove(enchantment); } else { - enchantments.put(enchantment, level - 1); + enchantments.put(enchantment, level); } EnchantmentHelper.set(enchantments, stack); } diff --git a/src/main/java/com/minelittlepony/unicopia/item/enchantment/HeartboundEnchantmentUtil.java b/src/main/java/com/minelittlepony/unicopia/item/enchantment/HeartboundEnchantmentUtil.java new file mode 100644 index 00000000..b27dd3ad --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/enchantment/HeartboundEnchantmentUtil.java @@ -0,0 +1,51 @@ +package com.minelittlepony.unicopia.item.enchantment; + +import java.util.List; + +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.enchantment.Enchantments; +import net.minecraft.item.ItemStack; +import net.minecraft.util.collection.DefaultedList; + +public interface HeartboundEnchantmentUtil { + static InventorySnapshot createSnapshot(List> combinedInventory) { + List> storedCombinedInventory = combinedInventory.stream().map(l -> DefaultedList.ofSize(l.size(), ItemStack.EMPTY)).toList(); + boolean empty = true; + for (int group = 0; group < combinedInventory.size(); group++) { + var original = combinedInventory.get(group); + for (int i = 0; i < original.size(); i++) { + ItemStack stack = original.get(i); + if (EnchantmentHelper.getLevel(Enchantments.BINDING_CURSE, stack) == 0 + && EnchantmentHelper.getLevel(UEnchantments.HEART_BOUND, stack) > 0) { + original.set(i, ItemStack.EMPTY); + storedCombinedInventory.get(group).set(i, stack); + empty = false; + } + } + } + return empty ? InventorySnapshot.EMPTY : new InventorySnapshot(storedCombinedInventory); + } + + public record InventorySnapshot(List> combinedInventory) { + public static InventorySnapshot EMPTY = new InventorySnapshot(List.of()); + + public boolean empty() { + return combinedInventory.isEmpty(); + } + + public void restoreInto(List> combinedInventory) { + if (empty()) { + return; + } + for (int group = 0; group < combinedInventory.size(); group++) { + var original = combinedInventory.get(group); + for (int i = 0; i < original.size(); i++) { + ItemStack stored = this.combinedInventory.get(group).get(i); + if (!stored.isEmpty()) { + original.set(i, stored); + } + } + } + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/enchantment/UEnchantments.java b/src/main/java/com/minelittlepony/unicopia/item/enchantment/UEnchantments.java index 3414e340..559e9406 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/enchantment/UEnchantments.java +++ b/src/main/java/com/minelittlepony/unicopia/item/enchantment/UEnchantments.java @@ -30,12 +30,12 @@ public interface UEnchantments { /** * Protects against wall collisions and earth pony attacks! */ - Enchantment PADDED = register("padded", new SimpleEnchantment(Options.armor().rarity(Rarity.COMMON).maxLevel(3))); + Enchantment PADDED = register("padded", new SimpleEnchantment(Options.armor().rarity(Rarity.UNCOMMON).maxLevel(3))); /** * Heavy players move more slowly but are less likely to be flung around wildly. */ - Enchantment HEAVY = register("heavy", new AttributedEnchantment(Options.armor().rarity(Rarity.COMMON).maxLevel(4))) + Enchantment HEAVY = register("heavy", new AttributedEnchantment(Options.armor().rarity(Rarity.UNCOMMON).maxLevel(4))) .addModifier(EntityAttributes.GENERIC_MOVEMENT_SPEED, (user, level) -> { return new EntityAttributeModifier(UUID.fromString("a3d5a94f-4c40-48f6-a343-558502a13e10"), "Heavyness", (1 - level/(float)10) - 1, Operation.MULTIPLY_TOTAL); }); @@ -83,7 +83,7 @@ public interface UEnchantments { * Items with loyalty are kept after death. * Only works if they don't also have curse of binding. */ - Enchantment HEART_BOUND = register("heart_bound", new SimpleEnchantment(Options.create(EnchantmentTarget.VANISHABLE, UEnchantmentValidSlots.ANY).rarity(Rarity.COMMON).maxLevel(5))); + Enchantment HEART_BOUND = register("heart_bound", new SimpleEnchantment(Options.create(EnchantmentTarget.VANISHABLE, UEnchantmentValidSlots.ANY).rarity(Rarity.UNCOMMON).maxLevel(5))); /** * Consumes drops whilst mining and produces experience instead diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinEntity.java index 90febf2d..986e9bf5 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinEntity.java @@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.mixin; import java.util.Set; +import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.injection.At; @@ -12,18 +13,35 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import com.minelittlepony.unicopia.entity.duck.LavaAffine; import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.entity.Equine; import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.duck.EntityDuck; import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; import net.minecraft.entity.Entity.PositionUpdater; import net.minecraft.entity.Entity.RemovalReason; import net.minecraft.fluid.Fluid; +import net.minecraft.item.ItemStack; import net.minecraft.registry.tag.TagKey; @Mixin(Entity.class) abstract class MixinEntity implements EntityDuck { + @Nullable + private transient Caster host; + + @Override + @Nullable + public Caster getHost() { + return host; + } + + @Override + public void setHost(Caster host) { + this.host = host; + } + @Override @Accessor("submergedFluidTag") public abstract Set> getSubmergedFluidTags(); @@ -83,4 +101,11 @@ abstract class MixinEntity implements EntityDuck { info.cancel(); } } + + @Inject(method = "dropStack(Lnet/minecraft/item/ItemStack;F)Lnet/minecraft/entity/ItemEntity;", at = @At("HEAD"), cancellable = true) + private void onDropStack(ItemStack stack, float yOffset, CallbackInfoReturnable info) { + if (getHost() != null) { + info.setReturnValue(null); + } + } } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java index 27d286c6..f71ef863 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java @@ -18,6 +18,7 @@ import com.minelittlepony.unicopia.entity.*; import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance; import com.minelittlepony.unicopia.entity.duck.*; +import net.fabricmc.fabric.api.util.TriState; import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.attribute.DefaultAttributeContainer; @@ -157,6 +158,14 @@ abstract class MixinLivingEntity extends Entity implements LivingEntityDuck, Equ get().onDamage(source, amount).ifPresent(info::setReturnValue); } + @Inject(method = "hurtByWater()Z", at = @At("HEAD"), cancellable = true) + private void onCanBeHurtByWater(CallbackInfoReturnable info) { + TriState hurtByWater = get().canBeHurtByWater(); + if (hurtByWater != TriState.DEFAULT) { + info.setReturnValue(hurtByWater.get()); + } + } + @Inject(method = "writeCustomDataToNbt(Lnet/minecraft/nbt/NbtCompound;)V", at = @At("HEAD")) private void onWriteCustomDataToTag(NbtCompound tag, CallbackInfo info) { tag.put("unicopia_caster", get().toNBT()); diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinPlayerInventory.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinPlayerInventory.java index 5debbac8..e2311fef 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinPlayerInventory.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinPlayerInventory.java @@ -11,11 +11,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import com.minelittlepony.unicopia.advancement.UCriteria; -import com.minelittlepony.unicopia.item.enchantment.EnchantmentUtil; -import com.minelittlepony.unicopia.item.enchantment.UEnchantments; - -import net.minecraft.enchantment.EnchantmentHelper; -import net.minecraft.enchantment.Enchantments; +import com.minelittlepony.unicopia.item.enchantment.HeartboundEnchantmentUtil; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.Inventory; @@ -31,37 +27,21 @@ abstract class MixinPlayerInventory implements Inventory, Nameable { private @Final List> combinedInventory; @Nullable - private List> storedCombinedInventory; + private HeartboundEnchantmentUtil.InventorySnapshot inventorySnapshot; @Inject(method = "dropAll()V", at = @At("HEAD")) public void beforeDropAll(CallbackInfo info) { - storedCombinedInventory = combinedInventory.stream().map(l -> DefaultedList.ofSize(l.size(), ItemStack.EMPTY)).toList(); - for (int group = 0; group < combinedInventory.size(); group++) { - var original = combinedInventory.get(group); - for (int i = 0; i < original.size(); i++) { - ItemStack stack = original.get(i); - if (EnchantmentHelper.getLevel(Enchantments.BINDING_CURSE, stack) == 0 - && EnchantmentUtil.consumeEnchantment(UEnchantments.HEART_BOUND, 1, stack, player.getWorld().random, EnchantmentUtil.getLuck(3, player))) { - original.set(i, ItemStack.EMPTY); - UCriteria.USE_SOULMATE.trigger(player); - storedCombinedInventory.get(group).set(i, stack); - } - } + inventorySnapshot = HeartboundEnchantmentUtil.createSnapshot(combinedInventory); + if (!inventorySnapshot.empty()) { + UCriteria.USE_SOULMATE.trigger(player); } } - @Inject(method = "dropAll()V", at = @At("TAIL")) + @Inject(method = "dropAll()V", at = @At("RETURN")) public void afterDropAll(CallbackInfo info) { - if (storedCombinedInventory != null) { - for (int group = 0; group < combinedInventory.size(); group++) { - var original = combinedInventory.get(group); - for (int i = 0; i < original.size(); i++) { - ItemStack stored = storedCombinedInventory.get(group).get(i); - if (!stored.isEmpty()) { - original.set(i, stored); - } - } - } + if (inventorySnapshot != null) { + inventorySnapshot.restoreInto(combinedInventory); + inventorySnapshot = null; } } } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinWorld.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinWorld.java index eda9c5b4..04db071b 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinWorld.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinWorld.java @@ -6,11 +6,13 @@ 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.ModifyVariable; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import com.minelittlepony.unicopia.entity.duck.RotatedView; import com.minelittlepony.unicopia.server.world.BlockDestructionManager; import net.minecraft.block.BlockState; +import net.minecraft.entity.Entity; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraft.world.WorldAccess; @@ -22,6 +24,7 @@ abstract class MixinWorld implements WorldAccess, BlockDestructionManager.Source private int recurseCount = 0; private final Stack rotations = new Stack<>(); + private boolean mirrorEntityStatuses; @Override public Stack getRotations() { @@ -33,11 +36,23 @@ abstract class MixinWorld implements WorldAccess, BlockDestructionManager.Source return recurseCount <= 0; } + @Override + public void setMirrorEntityStatuses(boolean enable) { + mirrorEntityStatuses = enable; + } + @Override public BlockDestructionManager getDestructionManager() { return destructions.get(); } + @Inject(method = "sendEntityStatus(Lnet/minecraft/entity/Entity;B)V", at = @At("HEAD")) + private void onSendEntityStatus(Entity entity, byte status, CallbackInfo info) { + if (mirrorEntityStatuses) { + entity.handleStatus(status); + } + } + @ModifyVariable(method = "setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;II)Z", at = @At("HEAD")) private BlockPos modifyBlockPos(BlockPos pos) { pos = applyRotation(pos); diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinClientPlayNetworkHandler.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinClientPlayNetworkHandler.java new file mode 100644 index 00000000..06821d12 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinClientPlayNetworkHandler.java @@ -0,0 +1,35 @@ +package com.minelittlepony.unicopia.mixin.client; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +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.ability.magic.SpellPredicate; +import com.minelittlepony.unicopia.entity.Living; +import com.minelittlepony.unicopia.entity.behaviour.Disguise; +import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance; + +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.network.packet.s2c.play.EntityStatusS2CPacket; + +@Mixin(ClientPlayNetworkHandler.class) +abstract class MixinClientPlayNetworkHandler { + @Shadow private ClientWorld world; + + @Inject(method = "onEntityStatus", at = @At("TAIL")) + private void onOnEntityStatus(EntityStatusS2CPacket packet, CallbackInfo info) { + Living living = Living.living(packet.getEntity(world)); + if (living != null) { + living.getSpellSlot() + .get(SpellPredicate.IS_DISGUISE, false) + .map(Disguise::getDisguise) + .map(EntityAppearance::getAppearance) + .ifPresent(appearance -> { + appearance.handleStatus(packet.getStatus()); + }); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/network/MsgRequestSpeciesChange.java b/src/main/java/com/minelittlepony/unicopia/network/MsgRequestSpeciesChange.java index a0af9093..0fe99689 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/MsgRequestSpeciesChange.java +++ b/src/main/java/com/minelittlepony/unicopia/network/MsgRequestSpeciesChange.java @@ -36,7 +36,11 @@ public record MsgRequestSpeciesChange ( Pony player = Pony.of(sender); if (force || player.getSpecies().isUnset()) { - player.setSpecies(newRace.isPermitted(sender) ? newRace : UnicopiaWorldProperties.forWorld((ServerWorld)player.asWorld()).getDefaultRace()); + boolean permitted = newRace.isPermitted(sender); + player.setSpecies(permitted ? newRace : UnicopiaWorldProperties.forWorld((ServerWorld)player.asWorld()).getDefaultRace()); + if (!permitted) { + sender.sendMessageToClient(Text.translatable("respawn.reason.illegal_race", newRace.getDisplayName()), false); + } if (force) { if (sender.getWorld().getGameRules().getBoolean(UGameRules.ANNOUNCE_TRIBE_JOINS)) { diff --git a/src/main/java/com/minelittlepony/unicopia/network/datasync/SpellNetworkedReference.java b/src/main/java/com/minelittlepony/unicopia/network/datasync/SpellNetworkedReference.java index 9ae9f29a..fbc8ba3b 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/datasync/SpellNetworkedReference.java +++ b/src/main/java/com/minelittlepony/unicopia/network/datasync/SpellNetworkedReference.java @@ -31,7 +31,7 @@ public class SpellNetworkedReference implements NetworkedRefere @Override public boolean fromNbt(NbtCompound comp) { dirty = false; - currentValue.fromNBT(comp); + currentValue.fromNBT(comp, owner.isClient()); return isDirty(); } diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index c55ccb5b..24d06e29 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -699,6 +699,7 @@ "gui.unicopia.page_num": "%d of %d", "respawn.reason.joined_new_tribe": "%1$s was reborn as a %2$s", + "respawn.reason.illegal_race": "The %s race is not permitted by your server's configuration.", "gui.unicopia.tribe_selection.respawn": "You have died.", "gui.unicopia.tribe_selection.respawn.journey": "But the end is not all, for at the end of every end is another beginning.", @@ -1326,16 +1327,23 @@ "commands.race.tell.other.alt": "%s is an ", "commands.racelist.illegal": "The default race %s cannot be used with this command.", - "commands.racelist.allowed": "Added %1$s to the whitelist.", - "commands.racelist.allowed.failed": "%1$s is already whitelisted.", - - "commands.racelist.disallowed": "Removed %1$s from the whitelist.", - "commands.racelist.disallowed.failed": "%1$s is not on the whitelist.", + + "commands.racelist.get.allowed": "Allowed (%s):", + "commands.racelist.get.not_allowed": "Not Allowed (%s):", + "commands.racelist.get.list_item": "- %s (%s)", + "commands.racelist.reset.success": "Cleared and disabled allow list.", + "commands.racelist.reset.fail": "The allow list is not active. Doing nothing.", + "commands.racelist.inactive": "The allow list is not active. Add races with /unicopia racelist to configure it.", + "commands.racelist.enabled": "Enabled allow list", + "commands.racelist.disabled": "Disabled allow list", + "commands.racelist.allowed": "Added %1$s to the allow list.", + "commands.racelist.allowed.failed": "%1$s is already allowed.", + "commands.racelist.disallowed": "Removed %1$s from the allow list.", + "commands.racelist.disallowed.failed": "%1$s is already not allowed.", "commands.worldtribe.success.get": "Default race for all new players is currently set to: %s", "commands.worldtribe.success.set": "Set default race for new players is now set to: %s", - "commands.disguise.usage": "/disguise [nbt]", "commands.disguise.notfound": "The entity id '%s' does not exist.", "commands.disguise.removed": "Your disguise has been removed.", "commands.disguise.removed.self": "Removed own disguise.", diff --git a/src/main/resources/assets/unicopia/lang/ru_ru.json b/src/main/resources/assets/unicopia/lang/ru_ru.json index c03f94db..96227648 100644 --- a/src/main/resources/assets/unicopia/lang/ru_ru.json +++ b/src/main/resources/assets/unicopia/lang/ru_ru.json @@ -699,6 +699,7 @@ "gui.unicopia.page_num": "%d из %d", "respawn.reason.joined_new_tribe": "%1$s был перерождён как %2$s", + "respawn.reason.illegal_race": "Раса %s не разрешена конфигурацией вашего сервера.", "gui.unicopia.tribe_selection.respawn": "Вы умерли.", "gui.unicopia.tribe_selection.respawn.journey": "Но конец - это еще не всё, потому что в конце каждого конца есть другое начало.", @@ -1326,16 +1327,20 @@ "commands.race.tell.other.alt": "%s - это ", "commands.racelist.illegal": "Раса %s по умолчанию не может быть использована этой командой.", - "commands.racelist.allowed": "Добавлен %1$s в белый список.", - "commands.racelist.allowed.failed": "%1$s уже внесён в белый список.", + "commands.racelist.allowed": "Раса %1$s добавлена в белый список.", + "commands.racelist.get.allowed": "Разрешены (%s):", + "commands.racelist.get.not_allowed": "Не разрешены (%s):", + "commands.racelist.get.list_item": "- %s (%s)", + "commands.racelist.clear.success": "Белый список отключён.", + "commands.racelist.allowed.failed": "Раса %1$s уже разрешена.", + "commands.racelist.inactive": "Список разрешений не активен. Добавьте расы с помощью /unicopia racelist allow , чтобы настроить его.", - "commands.racelist.disallowed": "Удален %1$s из белого списка.", - "commands.racelist.disallowed.failed": "%1$s отсутствует в белом списке.", + "commands.racelist.disallowed": "Раса %1$s удалена из белого списка.", + "commands.racelist.disallowed.failed": "Раса %1$s отсутствует в белом списке.", "commands.worldtribe.success.get": "Раса по умолчанию для всех новых игроков в настоящее время установлена на: %s.", "commands.worldtribe.success.set": "Установка расы по умолчанию для новых игроков теперь имеет значение: %s.", - "commands.disguise.usage": "/disguise [nbt]", "commands.disguise.notfound": "Идентификатор сущности \"%s\" не существует.", "commands.disguise.removed": "Ваша маскировка была удалена.", "commands.disguise.removed.self": "Удалена собственная маскировка.", diff --git a/src/main/resources/assets/unicopia/lang/zh_cn.json b/src/main/resources/assets/unicopia/lang/zh_cn.json index 5b978ee5..bbb43499 100644 --- a/src/main/resources/assets/unicopia/lang/zh_cn.json +++ b/src/main/resources/assets/unicopia/lang/zh_cn.json @@ -49,7 +49,10 @@ "emi.category.unicopia.spellbook": "魔法书", "emi.category.unicopia.cloud_shaping": "塑形", "emi.category.unicopia.growing": "生长", - + "emi.category.unicopia.altar": "黑暗仪式", + "recipe.unicopia.altar.instruction": "将物品掷入火中", + "recipe.unicopia.growing.instruction": "聚焦陆马魔法", + "item.unicopia.alicorn_badge": "天角兽徽章", "item.unicopia.unicorn_badge": "独角兽徽章", "item.unicopia.pegasus_badge": "天马徽章", @@ -142,7 +145,7 @@ "item.unicopia.crispy_hay_fries": "酥脆炸草条", "item.unicopia.horse_shoe_fries": "炸蹄铁", "item.unicopia.wheat_worms": "小麦虫", - "item.unicopia.muffin": "马芬", + "item.unicopia.muffin": "玛芬", "item.unicopia.pegasus_amulet": "伊卡洛斯之翼", "item.unicopia.pegasus_amulet.lore": "让穿戴者享受短暂的飞行乐趣", @@ -222,12 +225,27 @@ "block.unicopia.rocks": "一些石块", "block.unicopia.plunder_vine": "掠夺之藤", "block.unicopia.plunder_vine_bud": "掠夺之藤幼芽", + "block.unicopia.spectral_fire": "节律火", "block.unicopia.bananas": "香蕉", "block.unicopia.zapling": "魔虹苹果树苗", "block.unicopia.zap_log": "魔虹苹果木原木", "block.unicopia.zap_wood": "魔虹苹果木", "block.unicopia.stripped_zap_log": "去皮魔虹苹果木原木", "block.unicopia.stripped_zap_wood": "去皮魔虹苹果木", + "block.unicopia.zap_planks": "魔虹苹果木板", + "block.unicopia.zap_stairs": "魔虹苹果木楼梯", + "block.unicopia.zap_slab": "魔虹苹果木台阶", + "block.unicopia.zap_fence": "魔虹苹果木栅栏", + "block.unicopia.zap_fence_gate": "魔虹苹果木栅栏门", + "block.unicopia.waxed_zap_log": "涂蜡魔虹苹果木原木", + "block.unicopia.waxed_zap_wood": "涂蜡魔虹苹果木", + "block.unicopia.waxed_stripped_zap_log": "涂蜡去皮魔虹苹果木原木", + "block.unicopia.waxed_stripped_zap_wood": "涂蜡去皮魔虹苹果木", + "block.unicopia.waxed_zap_planks": "涂蜡魔虹苹果木板", + "block.unicopia.waxed_zap_stairs": "涂蜡魔虹苹果木楼梯", + "block.unicopia.waxed_zap_slab": "涂蜡魔虹苹果木台阶", + "block.unicopia.waxed_zap_fence": "涂蜡魔虹苹果木栅栏", + "block.unicopia.waxed_zap_fence_gate": "涂蜡魔虹苹果木栅栏门", "block.unicopia.zap_leaves": "魔虹苹果树叶", "block.unicopia.flowering_zap_leaves": "盛开的魔虹苹果树叶", "block.unicopia.zap_apple": "魔虹苹果", @@ -681,6 +699,7 @@ "gui.unicopia.page_num": "第%d页(共%d页)", "respawn.reason.joined_new_tribe": "%1$s 以一只 %2$s 的身份重生了", + "respawn.reason.illegal_race": "%s这个种族目前不被你的服务器配置所允许", "gui.unicopia.tribe_selection.respawn": "你已经死了。", "gui.unicopia.tribe_selection.respawn.journey": "但结束不是全部,因为每个结束的尽头都是另一个开始。", @@ -1309,15 +1328,19 @@ "commands.racelist.illegal": "默认种族 %s 不适用于本指令。", "commands.racelist.allowed": "将 %1$s 加进白名单。", - "commands.racelist.allowed.failed": "%1$s 已在白名单中。", - + "commands.racelist.get.allowed": "允许 (%s):", + + "commands.racelist.get.not_allowed": "不允许 (%s):", + "commands.racelist.get.list_item": "- %s (%s)", + "commands.racelist.clear.success": "停用白名单", + "commands.racelist.allowed.failed": "%1$s 已经允许了", + "commands.racelist.inactive": "许可列表未激活。使用 /unicopia racelist allow 指令来添加种族和配置种族。", "commands.racelist.disallowed": "将 %1$s 从白名单移除。", "commands.racelist.disallowed.failed": "%1$s 并不在白名单中。", "commands.worldtribe.success.get": "目前,新玩家都将以 %s 的身份加入游戏", "commands.worldtribe.success.set": "已更改设置,这将使新玩家以 %s 的身份加入游戏", - "commands.disguise.usage": "/disguise <玩家名> <实体名> [nbt]", "commands.disguise.notfound": "实体id '%s' 并不存在。", "commands.disguise.removed": "你的伪装被移除了。", "commands.disguise.removed.self": "移除了自己的伪装。", @@ -1443,6 +1466,8 @@ "death.attack.unicopia.horseshoe.self": "%1$s 咣了自己", "death.attack.unicopia.horseshoe.item": "%1$s 被 %2$s 用 %3$s 咣了", "death.attack.unicopia.horseshoe.player": "%1$s 被 %2$s 咣了", + "death.attack.unicopia.spikes": "%1$s 被扎死了", + "death.attack.unicopia.spikes.player": "%1$s 在试图逃离 %2$s 时落入了一堆尖刺中", "death.fell.accident.ladder.pegasus": "%1$s 从梯子上掉下来时忘了自己会飞", "death.fell.accident.vines.pegasus": "%1$s 从藤蔓上掉下来时忘了自己会飞", diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/carry.png b/src/main/resources/assets/unicopia/textures/gui/ability/carry.png index 6dc5380d..68271d24 100644 Binary files a/src/main/resources/assets/unicopia/textures/gui/ability/carry.png and b/src/main/resources/assets/unicopia/textures/gui/ability/carry.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/cast.png b/src/main/resources/assets/unicopia/textures/gui/ability/cast.png index 097c07af..4af1518a 100644 Binary files a/src/main/resources/assets/unicopia/textures/gui/ability/cast.png and b/src/main/resources/assets/unicopia/textures/gui/ability/cast.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/dispell.png b/src/main/resources/assets/unicopia/textures/gui/ability/dispell.png index 7afef72e..f79663be 100644 Binary files a/src/main/resources/assets/unicopia/textures/gui/ability/dispell.png and b/src/main/resources/assets/unicopia/textures/gui/ability/dispell.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/kick_bat_backward.png b/src/main/resources/assets/unicopia/textures/gui/ability/kick_bat_backward.png new file mode 100644 index 00000000..e06dfedc Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/kick_bat_backward.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/kick_bat_forward.png b/src/main/resources/assets/unicopia/textures/gui/ability/kick_bat_forward.png new file mode 100644 index 00000000..80bd4a02 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/kick_bat_forward.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/kick_changeling_backward.png b/src/main/resources/assets/unicopia/textures/gui/ability/kick_changeling_backward.png new file mode 100644 index 00000000..21bed4d9 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/kick_changeling_backward.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/kick_changeling_forward.png b/src/main/resources/assets/unicopia/textures/gui/ability/kick_changeling_forward.png new file mode 100644 index 00000000..50500939 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/kick_changeling_forward.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/kick_hippogriff_backward.png b/src/main/resources/assets/unicopia/textures/gui/ability/kick_hippogriff_backward.png new file mode 100644 index 00000000..73b98514 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/kick_hippogriff_backward.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/kick_hippogriff_forward.png b/src/main/resources/assets/unicopia/textures/gui/ability/kick_hippogriff_forward.png new file mode 100644 index 00000000..03601d14 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/kick_hippogriff_forward.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/kick_kirin_backward.png b/src/main/resources/assets/unicopia/textures/gui/ability/kick_kirin_backward.png new file mode 100644 index 00000000..fcef4e75 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/kick_kirin_backward.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/kick_kirin_forward.png b/src/main/resources/assets/unicopia/textures/gui/ability/kick_kirin_forward.png new file mode 100644 index 00000000..54626e5b Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/kick_kirin_forward.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/kick_pegasus_backward.png b/src/main/resources/assets/unicopia/textures/gui/ability/kick_pegasus_backward.png new file mode 100644 index 00000000..e5660f02 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/kick_pegasus_backward.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/kick_pegasus_forward.png b/src/main/resources/assets/unicopia/textures/gui/ability/kick_pegasus_forward.png new file mode 100644 index 00000000..7a377704 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/kick_pegasus_forward.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/kick_unicorn_backward.png b/src/main/resources/assets/unicopia/textures/gui/ability/kick_unicorn_backward.png new file mode 100644 index 00000000..73b98514 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/kick_unicorn_backward.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/kick_unicorn_forward.png b/src/main/resources/assets/unicopia/textures/gui/ability/kick_unicorn_forward.png new file mode 100644 index 00000000..7952d24e Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/kick_unicorn_forward.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/shoot.png b/src/main/resources/assets/unicopia/textures/gui/ability/shoot.png index 72eb1dcc..f293d7ec 100644 Binary files a/src/main/resources/assets/unicopia/textures/gui/ability/shoot.png and b/src/main/resources/assets/unicopia/textures/gui/ability/shoot.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/stomp_bat.png b/src/main/resources/assets/unicopia/textures/gui/ability/stomp_bat.png new file mode 100644 index 00000000..3e2cdece Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/stomp_bat.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/stomp_changeling.png b/src/main/resources/assets/unicopia/textures/gui/ability/stomp_changeling.png new file mode 100644 index 00000000..5423656b Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/stomp_changeling.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/stomp_hippogriff.png b/src/main/resources/assets/unicopia/textures/gui/ability/stomp_hippogriff.png new file mode 100644 index 00000000..d928d59d Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/stomp_hippogriff.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/stomp_kirin.png b/src/main/resources/assets/unicopia/textures/gui/ability/stomp_kirin.png new file mode 100644 index 00000000..73b51d6e Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/stomp_kirin.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/stomp_pegasus.png b/src/main/resources/assets/unicopia/textures/gui/ability/stomp_pegasus.png new file mode 100644 index 00000000..88823358 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/stomp_pegasus.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/stomp_unicorn.png b/src/main/resources/assets/unicopia/textures/gui/ability/stomp_unicorn.png new file mode 100644 index 00000000..29fcde62 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/stomp_unicorn.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_land_earth.png b/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_land_earth.png new file mode 100644 index 00000000..d29305e5 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_land_earth.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_land_kirin.png b/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_land_kirin.png new file mode 100644 index 00000000..d29305e5 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_land_kirin.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_land_unicorn.png b/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_land_unicorn.png new file mode 100644 index 00000000..7b9c8ea5 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_land_unicorn.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_takeoff_earth.png b/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_takeoff_earth.png new file mode 100644 index 00000000..1587dceb Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_takeoff_earth.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_takeoff_kirin.png b/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_takeoff_kirin.png new file mode 100644 index 00000000..1587dceb Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_takeoff_kirin.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_takeoff_unicorn.png b/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_takeoff_unicorn.png new file mode 100644 index 00000000..17c52359 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_takeoff_unicorn.png differ diff --git a/src/main/resources/unicopia.mixin.json b/src/main/resources/unicopia.mixin.json index 549625a7..20a09367 100644 --- a/src/main/resources/unicopia.mixin.json +++ b/src/main/resources/unicopia.mixin.json @@ -67,6 +67,7 @@ "client.MixinBackgroundRenderer", "client.MixinCamera", "client.MixinClientWorld", + "client.MixinClientPlayNetworkHandler", "client.MixinEntityRenderDispatcher", "client.MixinGameRenderer", "client.MixinHeldItemRenderer",