Merge branch '1.20.2' into 1.20.4
19
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`
|
||||
|
|
13
README_RU.md
|
@ -48,19 +48,22 @@
|
|||
|
||||
### Понифицированные картины
|
||||
|
||||
Ведь что это был бы за пони-мод, если бы в нём не было этого? У каждой расы есть хотя бы один рисунок, представляющий её,
|
||||
Ведь что это был бы за пони-мод, если бы в нём _не_ было этого? У каждой расы есть хотя бы один рисунок, представляющий её,
|
||||
так что покажите свою гордость и поднимите флаг!
|
||||
|
||||
Дисклеймер: Радужных флагов нет (пока)
|
||||
Дисклеймер: Радужных флагов (пока) нет
|
||||
|
||||
### Природные явления
|
||||
|
||||
- Воздушный поток (плохо) влияет на пегасов, остерегайтесь летать во время грозы! Там может быть опасно!
|
||||
Если вы играете за летающий вид или просто любите приятные вещи, попробуйте построить метеорологическую жилу.
|
||||
- Воздушный поток
|
||||
|
||||
Пегасы, остерегайтесь полётов во время грозы! Это может быть опасно!
|
||||
Если вы играете за летающий вид или просто любите приятные вещи, попробуйте построить погодную жилу.
|
||||
Она показывает фактическое, абсолютно реальное, а не плохо смоделированное направление ветра в вашем мире Minecraft. Только учтите,
|
||||
что направление и сила ветра ситуативны (и плохи), и будут отличаться в зависимости от того, где вы находитесь и на какой высоте.
|
||||
что направление и сила ветра ситуативны, и будут отличаться в зависимости от того, где вы находитесь и на какой высоте.
|
||||
|
||||
- Горячий воздух поднимает
|
||||
|
||||
Нет, это не плохой фильм про "Звездные войны", это реальная механика. Песок и лава придают летающим видам дополнительную подъёмную силу.
|
||||
Вода - наоборот. Попробуйте! А вообще, не стоит, я не хочу чтобы вы утонули.
|
||||
|
||||
|
|
57
src/main/java/com/minelittlepony/unicopia/AllowList.java
Normal file
|
@ -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<String> values = Unicopia.getConfig().speciesWhiteList.get();
|
||||
boolean added = values.add(race.getId().toString());
|
||||
Unicopia.getConfig().save();
|
||||
return added;
|
||||
}
|
||||
|
||||
public boolean remove(Race race) {
|
||||
Set<String> 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());
|
||||
}
|
||||
}
|
|
@ -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<Composite> compositeSupplier, Availability availabi
|
|||
}
|
||||
|
||||
public boolean isPermitted(@Nullable PlayerEntity sender) {
|
||||
Set<String> 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;
|
||||
|
|
|
@ -61,7 +61,7 @@ public class EarthPonyKickAbility implements Ability<Pos> {
|
|||
@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");
|
||||
}
|
||||
|
|
|
@ -70,8 +70,9 @@ public class EarthPonyStompAbility implements Ability<Hit> {
|
|||
@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");
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ public class TimeChangeAbility implements Ability<Rot> {
|
|||
|
||||
@Override
|
||||
public boolean canUse(Race.Composite race) {
|
||||
return race.pseudo() == Race.UNICORN;
|
||||
return Ability.super.canUse(race) || race.pseudo() == Race.UNICORN;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -43,9 +43,10 @@ public class ToggleFlightAbility implements Ability<Hit> {
|
|||
@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");
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,10 @@ public final class SpellReference<T extends Spell> 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<T extends Spell> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Text> 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<? extends MutableText> 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);
|
||||
}));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
float tickRate = MinecraftClient.getInstance().world.getTickManager().getTickRate();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<SpellbookScreenHandler> imple
|
|||
|
||||
List<Text> 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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ public abstract class AbstractBillboardParticle extends AbstractGeometryBasedPar
|
|||
RenderSystem.disableCull();
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.enableDepthTest();
|
||||
RenderSystem.defaultBlendFunc();
|
||||
|
||||
Vec3d cam = camera.getPos();
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<E extends LivingEntity> implements AccessoryFeatureRenderer.Feature<E> {
|
||||
|
||||
private final MinecraftClient client = MinecraftClient.getInstance();
|
||||
|
||||
private static final Map<EntityType<?>, 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<EntityType<?>, Set<Pair<ModelPart, ModelPart>>> 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<ModelPart> getPart(ModelPart part, String childName) {
|
||||
return part.hasChild(childName) ? Optional.of(part.getChild(childName)) : Optional.empty();
|
||||
}
|
||||
|
||||
public DisguisedArmsFeatureRenderer(FeatureRendererContext<E, ? extends BipedEntityModel<E>> 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<Entity> 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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<Integer> 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)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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<ShaderProgram> renderTypePortalSurfaceProgram = register("rendertype_portal_surface", VertexFormats.POSITION_COLOR);
|
||||
public interface UShaders {
|
||||
Supplier<ShaderProgram> 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<ShaderProgram> register(String name, VertexFormat format) {
|
||||
AtomicReference<ShaderProgram> holder = new AtomicReference<>();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -51,7 +51,7 @@ public class ShieldSpellRenderer extends SpellRenderer<ShieldSpell> {
|
|||
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);
|
||||
|
|
|
@ -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<MagicReserves, MagicReserves.Bar> getter;
|
||||
|
||||
|
|
|
@ -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<ServerCommandSource> 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<MutableText> allowed = new HashSet<>();
|
||||
Set<MutableText> 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<Race, Boolean> 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;
|
||||
}
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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<ServerCommandSource> ALL_RACE_SUGGESTIONS = suggestFromRegistry(Race.REGISTRY_KEY);
|
||||
public static final SuggestionProvider<ServerCommandSource> ALLOWED_RACE_SUGGESTIONS = suggestFromRegistry(Race.REGISTRY_KEY, (context, race) -> race.isPermitted(context.getSource().getPlayer()));
|
||||
|
||||
public static <T> SuggestionProvider<ServerCommandSource> suggestFromRegistry(RegistryKey<? extends Registry<T>> registryKey, @Nullable BiPredicate<CommandContext<ServerCommandSource>, T> filter) {
|
||||
return (context, builder) -> {
|
||||
Registry<T> 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 <T> SuggestionProvider<ServerCommandSource> suggestFromRegistry(RegistryKey<? extends Registry<T>> registryKey) {
|
||||
return suggestFromRegistry(registryKey, null);
|
||||
}
|
||||
|
||||
public static <T> CompletableFuture<Suggestions> suggestIdentifiers(Iterable<T> candidates, Function<T, Identifier> 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 <T> void forEachMatching(Iterable<T> candidates, String input, Function<T, Identifier> idFunc, Consumer<T> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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")))));
|
||||
}
|
||||
|
||||
|
|
|
@ -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<T extends LivingEntity> implements Equine<T>, Caste
|
|||
|
||||
@Nullable
|
||||
private Caster<?> attacker;
|
||||
@Nullable
|
||||
private transient Caster<?> host;
|
||||
|
||||
private Optional<Living<?>> target = Optional.empty();
|
||||
|
||||
|
@ -476,6 +481,19 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
|
|||
|
||||
public Optional<Boolean> 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<T extends LivingEntity> implements Equine<T>, Caste
|
|||
return Optional.empty();
|
||||
}
|
||||
|
||||
public TriState canBeHurtByWater() {
|
||||
return TriState.DEFAULT;
|
||||
}
|
||||
|
||||
public Optional<BlockPos> chooseClimbingPos() {
|
||||
return getSpellSlot().get(SpellPredicate.IS_DISGUISE, false)
|
||||
.map(AbstractDisguiseSpell::getDisguise)
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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<EndermanEntity> {
|
||||
@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<EndermanEntity> {
|
|||
} 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);
|
||||
}*/
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -276,7 +276,8 @@ public class EntityBehaviour<T extends Entity> {
|
|||
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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<IronGolemEntity> {
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<T extends MobEntity> extends EntityBehaviour<T> {
|
||||
|
||||
|
@ -23,6 +26,13 @@ public class MobBehaviour<T extends MobEntity> extends EntityBehaviour<T> {
|
|||
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) {
|
||||
|
|
|
@ -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<TagKey<Fluid>> getSubmergedFluidTags();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -10,6 +10,8 @@ public interface RotatedView {
|
|||
|
||||
boolean hasTransform();
|
||||
|
||||
void setMirrorEntityStatuses(boolean enable);
|
||||
|
||||
default void pushRotation(int y) {
|
||||
getRotations().add(y);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -59,7 +59,6 @@ public abstract class StationaryObjectEntity extends Entity implements UDamageSo
|
|||
}
|
||||
|
||||
protected void onHurt() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -88,6 +88,9 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> 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<PlayerEntity> 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<PlayerEntity> 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<PlayerEntity> 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<Vec3d> pos = VecHelper.sphere(pony.asWorld().getRandom(), 0.5D);
|
||||
Supplier<Vec3d> vel = VecHelper.sphere(pony.asWorld().getRandom(), 0.015D);
|
||||
|
@ -623,12 +630,13 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> 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<PlayerEntity> 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<PlayerEntity> 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<PlayerEntity> implements Tickab
|
|||
isFlyingEither = compound.getBoolean("isFlyingEither");
|
||||
ticksInAir = compound.getInt("ticksInAir");
|
||||
descentRate = compound.getFloat("descentRate");
|
||||
updraft.update(compound.getFloat("updraft"), 0);
|
||||
|
||||
entity.calculateDimensions();
|
||||
}
|
||||
|
|
|
@ -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<PlayerEntity> implements Copyable<Pony>, 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<PlayerEntity> implements Copyable<Pony>, 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<PlayerEntity> implements Copyable<Pony>, 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<DefaultedList<ItemStack>> combinedInventory) {
|
||||
List<DefaultedList<ItemStack>> 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<DefaultedList<ItemStack>> combinedInventory) {
|
||||
public static InventorySnapshot EMPTY = new InventorySnapshot(List.of());
|
||||
|
||||
public boolean empty() {
|
||||
return combinedInventory.isEmpty();
|
||||
}
|
||||
|
||||
public void restoreInto(List<DefaultedList<ItemStack>> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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<TagKey<Fluid>> 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<ItemEntity> info) {
|
||||
if (getHost() != null) {
|
||||
info.setReturnValue(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Boolean> 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());
|
||||
|
|
|
@ -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<DefaultedList<ItemStack>> combinedInventory;
|
||||
|
||||
@Nullable
|
||||
private List<DefaultedList<ItemStack>> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Integer> rotations = new Stack<>();
|
||||
private boolean mirrorEntityStatuses;
|
||||
|
||||
@Override
|
||||
public Stack<Integer> 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);
|
||||
|
|
|
@ -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());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)) {
|
||||
|
|
|
@ -31,7 +31,7 @@ public class SpellNetworkedReference<T extends Spell> implements NetworkedRefere
|
|||
@Override
|
||||
public boolean fromNbt(NbtCompound comp) {
|
||||
dirty = false;
|
||||
currentValue.fromNBT(comp);
|
||||
currentValue.fromNBT(comp, owner.isClient());
|
||||
return isDirty();
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <allow|disallow> <race> 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 <player> <entity> [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.",
|
||||
|
|
|
@ -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 <race>, чтобы настроить его.",
|
||||
|
||||
"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 <player> <entity> [nbt]",
|
||||
"commands.disguise.notfound": "Идентификатор сущности \"%s\" не существует.",
|
||||
"commands.disguise.removed": "Ваша маскировка была удалена.",
|
||||
"commands.disguise.removed.self": "Удалена собственная маскировка.",
|
||||
|
|
|
@ -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 <race> 指令来添加种族和配置种族。",
|
||||
"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 从藤蔓上掉下来时忘了自己会飞",
|
||||
|
|
Before Width: | Height: | Size: 866 B After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 302 B After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 431 B After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 5 KiB |
After Width: | Height: | Size: 5 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 5 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 5 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 7.1 KiB |
After Width: | Height: | Size: 8.1 KiB |
After Width: | Height: | Size: 8.1 KiB |
After Width: | Height: | Size: 8.3 KiB |
|
@ -67,6 +67,7 @@
|
|||
"client.MixinBackgroundRenderer",
|
||||
"client.MixinCamera",
|
||||
"client.MixinClientWorld",
|
||||
"client.MixinClientPlayNetworkHandler",
|
||||
"client.MixinEntityRenderDispatcher",
|
||||
"client.MixinGameRenderer",
|
||||
"client.MixinHeldItemRenderer",
|
||||
|
|