Merge branch '1.20.2' into 1.20.4

# Conflicts:
#	gradle.properties
This commit is contained in:
Sollace 2024-04-11 13:27:41 +01:00
commit 7a42a2d35a
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
85 changed files with 1051 additions and 656 deletions

View file

@ -124,6 +124,6 @@
这些状态是按周期循环的,如果您发现的果树不是您想要的状态,可以在它旁边等待几天直到结果,但请不要在魔虹苹果成熟前收获它们,否则它们会把您电得酥脆!
如果您设法获取到了魔虹苹果的木头和叶子,那它们也可以充当威慑滋事者的完美屏障。
###
###
香软可口,猪猪最爱。

3
crowdin.yml Normal file
View file

@ -0,0 +1,3 @@
files:
- source: src/main/resources/assets/unicopia/lang/en_us.json
translation: /%original_path%/%locale_with_underscore%.%file_extension%

View file

@ -23,7 +23,7 @@ org.gradle.daemon=false
fabwork_version=1.3.0+1.20.2
modmenu_version=9.0.0-pre.1
minelp_version=4.11.6+1.20.2
kirin_version=1.17.0+1.20.4
kirin_version=1.17.1+1.20.4
reach_attributes_version=2.4.2
trinkets_version=3.8.0
terraformer_api_version=8.0.0-beta.1

View file

@ -1,12 +1,18 @@
package com.minelittlepony.unicopia;
import net.minecraft.util.Formatting;
import java.util.Locale;
public enum Affinity {
import net.minecraft.util.Formatting;
import net.minecraft.util.StringIdentifiable;
public enum Affinity implements StringIdentifiable {
GOOD(Formatting.BLUE, -1, 0),
NEUTRAL(Formatting.LIGHT_PURPLE, 0, 0.5F),
BAD(Formatting.RED, 1, 1);
@SuppressWarnings("deprecation")
public static final EnumCodec<Affinity> CODEC = StringIdentifiable.createCodec(Affinity::values);
private final Formatting color;
private final int corruption;
@ -20,6 +26,11 @@ public enum Affinity {
this.alignment = alignment;
}
@Override
public String asString() {
return name().toLowerCase(Locale.ROOT);
}
public Formatting getColor() {
return color;
}

View file

@ -1,10 +1,24 @@
package com.minelittlepony.unicopia;
public enum Availability {
import java.util.Locale;
import net.minecraft.util.StringIdentifiable;
public enum Availability implements StringIdentifiable {
DEFAULT,
COMMANDS,
NONE;
@SuppressWarnings("deprecation")
public static final EnumCodec<Availability> CODEC = StringIdentifiable.createCodec(Availability::values);
private final String name = name().toLowerCase(Locale.ROOT);
@Override
public String asString() {
return name;
}
public boolean isSelectable() {
return this == DEFAULT;
}

View file

@ -34,6 +34,9 @@ public class Config extends com.minelittlepony.common.util.settings.Config {
.addComment("If true Mine Little Pony will not be considered when determining the race to use")
.addComment("The result will always be what is set by this config file.");
public final Setting<Boolean> toggleAbilityKeys = value("client", "toggleAbilityKeys", false)
.addComment("If true the ability keybinds will function as toggle keys rather than hold keys");
public final Setting<Integer> hudPage = value("client", "hudActivePage", 0)
.addComment("The page of abilities currently visible in the HUD. You can change this in-game using the PG_UP and PG_DWN keys (configurable)");

View file

@ -1,15 +1,28 @@
package com.minelittlepony.unicopia;
import java.util.Locale;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.sound.SoundEvent;
import net.minecraft.util.StringIdentifiable;
public enum FlightType {
public enum FlightType implements StringIdentifiable {
UNSET,
NONE,
AVIAN,
INSECTOID,
ARTIFICIAL;
@SuppressWarnings("deprecation")
public static final EnumCodec<FlightType> CODEC = StringIdentifiable.createCodec(FlightType::values);
private final String name = name().toLowerCase(Locale.ROOT);
@Override
public String asString() {
return name;
}
public boolean isGrounded() {
return this == NONE;
}

View file

@ -53,7 +53,7 @@ public interface Owned<E extends Entity> {
}
default boolean hasCommonOwner(Owned<?> sibling) {
return getMasterId().isPresent() && getMasterId().equals(sibling.getMasterId());
return getMasterId().equals(sibling.getMasterId());
}
interface Mutable<E extends Entity> {

View file

@ -1,20 +1,21 @@
package com.minelittlepony.unicopia;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.include.com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.common.base.Suppliers;
import com.minelittlepony.unicopia.ability.Abilities;
import com.minelittlepony.unicopia.ability.Ability;
import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.util.RegistryUtils;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.command.argument.RegistryKeyArgumentType;
import net.minecraft.entity.player.PlayerEntity;
@ -22,52 +23,89 @@ 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.util.Util;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
public record Race (Supplier<Composite> compositeSupplier, Availability availability, boolean canCast, FlightType flightType, boolean canUseEarth, boolean isNocturnal, boolean canHang) implements Affine {
public record Race (
List<Ability<?>> abilities,
Affinity affinity,
Availability availability,
FlightType flightType,
boolean canCast,
boolean hasIronGut,
boolean canUseEarth,
boolean isNocturnal,
boolean canHang,
boolean isFish,
boolean canInfluenceWeather,
boolean canInteractWithClouds
) implements Affine {
public static final String DEFAULT_ID = "unicopia:unset";
public static final Registry<Race> REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("race"), DEFAULT_ID);
public static final Registry<Race> COMMAND_REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("race/grantable"), DEFAULT_ID);
public static final RegistryKey<? extends Registry<Race>> REGISTRY_KEY = REGISTRY.getKey();
private static final DynamicCommandExceptionType UNKNOWN_RACE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("race.unknown", id));
private static final DynamicCommandExceptionType UNKNOWN_RACE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("commands.race.fail", id));
private static final Function<Race, Composite> COMPOSITES = Util.memoize(race -> new Composite(race, null, null));
public static Race register(String name, Availability availability, boolean magic, FlightType flight, boolean earth, boolean nocturnal, boolean canHang) {
return register(Unicopia.id(name), availability, magic, flight, earth, nocturnal, canHang);
}
public static Race register(Identifier id, Availability availability, boolean magic, FlightType flight, boolean earth, boolean nocturnal, boolean canHang) {
Race race = Registry.register(REGISTRY, id, new Race(Suppliers.memoize(() -> new Composite(REGISTRY.get(id), null, null)), availability, magic, flight, earth, nocturnal, canHang));
if (availability.isGrantable()) {
Registry.register(COMMAND_REGISTRY, id, race);
}
return race;
}
public static RegistryKeyArgumentType<Race> argument() {
return RegistryKeyArgumentType.registryKey(COMMAND_REGISTRY.getKey());
}
public static final Codec<Race> CODEC = RecordCodecBuilder.create(i -> i.group(
Abilities.REGISTRY.getCodec().listOf().fieldOf("abilities").forGetter(Race::abilities),
Affinity.CODEC.fieldOf("affinity").forGetter(Race::affinity),
Availability.CODEC.fieldOf("availability").forGetter(Race::availability),
FlightType.CODEC.fieldOf("flight").forGetter(Race::flightType),
Codec.BOOL.fieldOf("magic").forGetter(Race::canCast),
Codec.BOOL.fieldOf("can_forage").forGetter(Race::hasIronGut),
Codec.BOOL.fieldOf("earth_pony_strength").forGetter(Race::canUseEarth),
Codec.BOOL.fieldOf("nocturnal").forGetter(Race::isNocturnal),
Codec.BOOL.fieldOf("hanging").forGetter(Race::canHang),
Codec.BOOL.fieldOf("aquatic").forGetter(Race::isFish),
Codec.BOOL.fieldOf("weather_magic").forGetter(Race::canInfluenceWeather),
Codec.BOOL.fieldOf("cloud_magic").forGetter(Race::canInteractWithClouds)
).apply(i, Race::new));
/**
* The default, unset race.
* This is used if there are no other races.
*/
public static final Race UNSET = register("unset", Availability.COMMANDS, false, FlightType.NONE, false, false, false);
public static final Race HUMAN = register("human", Availability.COMMANDS, false, FlightType.NONE, false, false, false);
public static final Race EARTH = register("earth", Availability.DEFAULT, false, FlightType.NONE, true, false, false);
public static final Race UNICORN = register("unicorn", Availability.DEFAULT, true, FlightType.NONE, false, false, false);
public static final Race PEGASUS = register("pegasus", Availability.DEFAULT, false, FlightType.AVIAN, false, false, false);
public static final Race BAT = register("bat", Availability.DEFAULT, false, FlightType.AVIAN, false, true, true);
public static final Race ALICORN = register("alicorn", Availability.COMMANDS, true, FlightType.AVIAN, true, false, false);
public static final Race CHANGELING = register("changeling", Availability.DEFAULT, false, FlightType.INSECTOID, false, false, true);
public static final Race KIRIN = register("kirin", Availability.DEFAULT, true, FlightType.NONE, false, false, false);
public static final Race HIPPOGRIFF = register("hippogriff", Availability.DEFAULT, false, FlightType.AVIAN, false, false, false);
public static final Race SEAPONY = register("seapony", Availability.NONE, false, FlightType.NONE, false, false, false);
public static final Race UNSET = register("unset", new Builder().availability(Availability.COMMANDS));
public static final Race HUMAN = register("human", new Builder().availability(Availability.COMMANDS));
public static final Race EARTH = register("earth", new Builder().foraging().earth()
.abilities(Abilities.HUG, Abilities.STOMP, Abilities.KICK, Abilities.GROW)
);
public static final Race UNICORN = register("unicorn", new Builder().foraging().magic()
.abilities(Abilities.TELEPORT, Abilities.CAST, Abilities.GROUP_TELEPORT, Abilities.SHOOT, Abilities.DISPELL)
);
public static final Race PEGASUS = register("pegasus", new Builder().foraging().flight(FlightType.AVIAN).weatherMagic().cloudMagic()
.abilities(Abilities.TOGGLE_FLIGHT, Abilities.RAINBOOM, Abilities.CAPTURE_CLOUD, Abilities.CARRY)
);
public static final Race BAT = register("bat", new Builder().foraging().flight(FlightType.AVIAN).canHang().cloudMagic()
.abilities(Abilities.TOGGLE_FLIGHT, Abilities.CARRY, Abilities.HANG, Abilities.EEEE)
);
public static final Race ALICORN = register("alicorn", new Builder().foraging().availability(Availability.COMMANDS).flight(FlightType.AVIAN).earth().magic().weatherMagic().cloudMagic()
.abilities(
Abilities.TELEPORT, Abilities.GROUP_TELEPORT, Abilities.CAST, Abilities.SHOOT, Abilities.DISPELL,
Abilities.TOGGLE_FLIGHT, Abilities.RAINBOOM, Abilities.CAPTURE_CLOUD, Abilities.CARRY,
Abilities.HUG, Abilities.STOMP, Abilities.KICK, Abilities.GROW,
Abilities.TIME
)
);
public static final Race CHANGELING = register("changeling", new Builder().foraging().affinity(Affinity.BAD).flight(FlightType.INSECTOID).canHang()
.abilities(Abilities.DISPELL, Abilities.TOGGLE_FLIGHT, Abilities.FEED, Abilities.DISGUISE, Abilities.CARRY)
);
public static final Race KIRIN = register("kirin", new Builder().foraging().magic()
.abilities(Abilities.DISPELL, Abilities.RAGE, Abilities.NIRIK_BLAST, Abilities.KIRIN_CAST)
);
public static final Race HIPPOGRIFF = register("hippogriff", new Builder().foraging().flight(FlightType.AVIAN).cloudMagic()
.abilities(Abilities.TOGGLE_FLIGHT, Abilities.SCREECH, Abilities.PECK, Abilities.DASH, Abilities.CARRY)
);
public static final Race SEAPONY = register("seapony", new Builder().availability(Availability.COMMANDS).foraging().fish()
.abilities(Abilities.SONAR_PULSE)
);
public static void bootstrap() {}
public Composite composite() {
return compositeSupplier.get();
return COMPOSITES.apply(this);
}
public Composite composite(@Nullable Race pseudo, @Nullable Race potential) {
@ -76,11 +114,7 @@ public record Race (Supplier<Composite> compositeSupplier, Availability availabi
@Override
public Affinity getAffinity() {
return this == CHANGELING ? Affinity.BAD : Affinity.NEUTRAL;
}
public boolean hasIronGut() {
return !isHuman();
return affinity;
}
public boolean isUnset() {
@ -91,12 +125,8 @@ public record Race (Supplier<Composite> compositeSupplier, Availability availabi
return !isHuman();
}
public boolean isFish() {
return this == SEAPONY;
}
public boolean isHuman() {
return this == UNSET || this == HUMAN;
return isUnset() || this == HUMAN;
}
public boolean isDayurnal() {
@ -107,18 +137,14 @@ public record Race (Supplier<Composite> compositeSupplier, Availability availabi
return !flightType().isGrounded();
}
public boolean canInteractWithClouds() {
return canFly() && this != CHANGELING;
}
public boolean canInfluenceWeather() {
return canFly() && this != CHANGELING && this != BAT && this != HIPPOGRIFF;
}
public boolean hasPersistentWeatherMagic() {
return canInfluenceWeather();
}
public boolean canUse(Ability<?> ability) {
return abilities.contains(ability);
}
public Identifier getId() {
return REGISTRY.getId(this);
}
@ -132,13 +158,11 @@ public record Race (Supplier<Composite> compositeSupplier, Availability availabi
}
public String getTranslationKey() {
Identifier id = getId();
return String.format("%s.race.%s", id.getNamespace(), id.getPath().toLowerCase());
return Util.createTranslationKey("race", getId());
}
public Identifier getIcon() {
Identifier id = getId();
return new Identifier(id.getNamespace(), "textures/gui/race/" + id.getPath() + ".png");
return getId().withPath(p -> "textures/gui/race/" + p + ".png");
}
public boolean isPermitted(@Nullable PlayerEntity sender) {
@ -161,16 +185,6 @@ public record Race (Supplier<Composite> compositeSupplier, Availability availabi
return isEquine() ? this : other;
}
@Override
public int hashCode() {
return getId().hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof Race race && Objects.equal(race.getId(), getId());
}
@Override
public String toString() {
return "Race{ " + getId().toString() + " }";
@ -195,13 +209,29 @@ public record Race (Supplier<Composite> compositeSupplier, Availability availabi
return def;
}
public static Race fromName(String name) {
return fromName(name, EARTH);
public static Race register(String name, Builder builder) {
return register(Unicopia.id(name), builder);
}
public static Race register(Identifier id, Builder builder) {
Race race = Registry.register(REGISTRY, id, builder.build());
if (race.availability().isGrantable()) {
Registry.register(COMMAND_REGISTRY, id, race);
}
return race;
}
public static RegistryKeyArgumentType<Race> argument() {
return RegistryKeyArgumentType.registryKey(COMMAND_REGISTRY.getKey());
}
public static Race fromArgument(CommandContext<ServerCommandSource> context, String name) throws CommandSyntaxException {
Identifier id = context.getArgument(name, RegistryKey.class).getValue();
return REGISTRY.getOrEmpty(id).orElseThrow(() -> UNKNOWN_RACE_EXCEPTION.create(id));
final Identifier idf = id;
if (id.getNamespace() == Identifier.DEFAULT_NAMESPACE && !REGISTRY.containsId(id)) {
id = new Identifier(REGISTRY_KEY.getValue().getNamespace(), id.getPath());
}
return REGISTRY.getOrEmpty(id).orElseThrow(() -> UNKNOWN_RACE_EXCEPTION.create(idf));
}
public static Set<Race> allPermitted(PlayerEntity player) {
@ -233,6 +263,10 @@ public record Race (Supplier<Composite> compositeSupplier, Availability availabi
return any(Race::canCast);
}
public boolean canUse(Ability<?> ability) {
return any(r -> r.canUse(ability));
}
public boolean canInteractWithClouds() {
return any(Race::canInteractWithClouds);
}
@ -252,6 +286,85 @@ public record Race (Supplier<Composite> compositeSupplier, Availability availabi
return physical().flightType().or(pseudo().flightType());
}
}
public static final class Builder {
private final List<Ability<?>> abilities = new ArrayList<>();
private Affinity affinity = Affinity.NEUTRAL;
private Availability availability = Availability.DEFAULT;
private boolean canCast;
private boolean hasIronGut;
private FlightType flightType = FlightType.NONE;
private boolean canUseEarth;
private boolean isNocturnal;
private boolean canHang;
private boolean isFish;
private boolean canInfluenceWeather;
private boolean canInteractWithClouds;
public Builder abilities(Ability<?>...abilities) {
this.abilities.addAll(List.of(abilities));
return this;
}
public Builder foraging() {
hasIronGut = true;
return this;
}
public Builder affinity(Affinity affinity) {
this.affinity = affinity;
return this;
}
public Builder availability(Availability availability) {
this.availability = availability;
return this;
}
public Builder flight(FlightType flight) {
flightType = flight;
return this;
}
public Builder magic() {
canCast = true;
return this;
}
public Builder earth() {
canUseEarth = true;
return this;
}
public Builder nocturnal() {
isNocturnal = true;
return this;
}
public Builder canHang() {
canHang = true;
return this;
}
public Builder fish() {
isFish = true;
return this;
}
public Builder weatherMagic() {
canInfluenceWeather = true;
return this;
}
public Builder cloudMagic() {
canInteractWithClouds = true;
return this;
}
public Race build() {
return new Race(List.copyOf(abilities), affinity, availability, flightType, canCast, hasIronGut, canUseEarth, isNocturnal, canHang, isFish, canInfluenceWeather, canInteractWithClouds);
}
}
}

View file

@ -42,8 +42,8 @@ public interface WeaklyOwned<E extends Entity> extends Owned<E>, WorldConvertabl
@Override
@SuppressWarnings("unchecked")
default void setMaster(Owned<? extends E> sibling) {
if (sibling instanceof WeaklyOwned) {
getMasterReference().copyFrom(((WeaklyOwned<E>)sibling).getMasterReference());
if (sibling instanceof WeaklyOwned w) {
getMasterReference().copyFrom(w.getMasterReference());
} else {
setMaster(sibling.getMaster());
}

View file

@ -68,7 +68,9 @@ public interface Ability<T extends Hit> {
* Checks if the given race is permitted to use this ability
* @param playerSpecies The player's species
*/
boolean canUse(Race playerSpecies);
default boolean canUse(Race race) {
return race.canUse(this);
}
/**
* Called when an ability is about to be triggered. This event occurs on both the client and server so check {@code Pony#isClient} if you need to know which one you're on.

View file

@ -1,6 +1,5 @@
package com.minelittlepony.unicopia.ability;
import com.minelittlepony.unicopia.*;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
import com.minelittlepony.unicopia.entity.player.Pony;
@ -11,17 +10,11 @@ import net.minecraft.util.ActionResult;
import net.minecraft.util.TypedActionResult;
abstract class AbstractSpellCastingAbility implements Ability<Hit> {
@Override
public int getCooldownTime(Pony player) {
return 0;
}
@Override
public boolean canUse(Race race) {
return race.canCast() && race != Race.KIRIN;
}
@Override
public Text getName(Pony player) {
CustomisedSpellType<?> spell = player.getCharms().getEquippedSpell(player.getCharms().getHand());

View file

@ -1,7 +1,6 @@
package com.minelittlepony.unicopia.ability;
import com.minelittlepony.unicopia.AwaitTickQueue;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.advancement.UCriteria;
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
@ -16,11 +15,6 @@ import net.minecraft.world.event.GameEvent;
public class BatEeeeAbility extends ScreechAbility {
public static final int SELF_SPOOK_PROBABILITY = 20000;
@Override
public boolean canUse(Race race) {
return race == Race.BAT;
}
@Override
protected void playSounds(Pony player, Random rng, float strength) {
int count = 1 + rng.nextInt(10) + (int)(strength * 10);

View file

@ -2,7 +2,6 @@ package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Multi;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.util.TraceHelper;
@ -30,11 +29,6 @@ public class BatPonyHangAbility implements Ability<Multi> {
return 0;
}
@Override
public boolean canUse(Race race) {
return race == Race.BAT;
}
@Override
public Optional<Multi> prepare(Pony player) {

View file

@ -6,7 +6,6 @@ import java.util.UUID;
import java.util.stream.StreamSupport;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.entity.player.Pony;
@ -37,11 +36,6 @@ public class CarryAbility implements Ability<Hit> {
return 0;
}
@Override
public boolean canUse(Race race) {
return race.canFly();
}
@Override
public Optional<Hit> prepare(Pony player) {
return Hit.INSTANCE;

View file

@ -7,7 +7,6 @@ import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.magic.spell.ChangelingFeedingSpell;
@ -48,11 +47,6 @@ public class ChangelingFeedAbility implements Ability<Hit> {
return !SpellType.FEED.isOn(player) && ChangelingFeedingSpell.canFeed(player) ? 15 : 80;
}
@Override
public boolean canUse(Race race) {
return race == Race.CHANGELING;
}
@Nullable
@Override
public Optional<Hit> prepare(Pony player) {

View file

@ -3,7 +3,6 @@ package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import java.util.function.DoubleSupplier;
import java.util.function.Supplier;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.ability.data.Hit;
@ -48,11 +47,6 @@ public class EarthPonyGrowAbility implements Ability<Pos> {
return 50;
}
@Override
public boolean canUse(Race race) {
return race.canUseEarth();
}
@Override
public Optional<Pos> prepare(Pony player) {
return TraceHelper.findBlock(player.asEntity(), 3, 1).map(Pos::new);

View file

@ -53,11 +53,6 @@ public class EarthPonyKickAbility implements Ability<Pos> {
return 50;
}
@Override
public boolean canUse(Race race) {
return race.canUseEarth();
}
@Override
public Identifier getIcon(Pony player) {
return getId().withPath(p -> "textures/gui/ability/" + p

View file

@ -62,11 +62,6 @@ public class EarthPonyStompAbility implements Ability<Hit> {
return 50;
}
@Override
public boolean canUse(Race race) {
return race.canUseEarth();
}
@Override
public Identifier getIcon(Pony player) {
Identifier id = Abilities.REGISTRY.getId(this);

View file

@ -4,7 +4,6 @@ import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.entity.player.Pony;
@ -23,11 +22,6 @@ public class FlyingDashAbility implements Ability<Hit> {
return 30;
}
@Override
public boolean canUse(Race race) {
return race == Race.HIPPOGRIFF;
}
@Nullable
@Override
public Optional<Hit> prepare(Pony player) {

View file

@ -1,6 +1,5 @@
package com.minelittlepony.unicopia.ability;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
import com.minelittlepony.unicopia.entity.Living;
@ -16,12 +15,6 @@ import net.minecraft.particle.ParticleTypes;
* Ability to hug mobs. Not all of them are receptive to your advances though, so be careful!
*/
public class HugAbility extends CarryAbility {
@Override
public boolean canUse(Race race) {
return race.canUseEarth();
}
@Override
public boolean apply(Pony pony, Hit data) {
PlayerEntity player = pony.asEntity();

View file

@ -1,16 +1,10 @@
package com.minelittlepony.unicopia.ability;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.particle.ParticleTypes;
public class KirinCastingAbility extends UnicornCastingAbility {
@Override
public boolean canUse(Race race) {
return race == Race.KIRIN;
}
@Override
public void coolDown(Pony player, AbilitySlot slot) {
player.spawnParticles(ParticleTypes.FLAME, 5);

View file

@ -4,7 +4,6 @@ import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
@ -30,11 +29,6 @@ public class KirinRageAbility implements Ability<Hit> {
return 60;
}
@Override
public boolean canUse(Race race) {
return race == Race.KIRIN;
}
@Nullable
@Override
public Optional<Hit> prepare(Pony player) {

View file

@ -5,7 +5,6 @@ import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation.Recipient;
@ -36,11 +35,6 @@ public class NirikBlastAbility implements Ability<Hit> {
return 3;
}
@Override
public boolean canUse(Race race) {
return race == Race.KIRIN;
}
@Nullable
@Override
public Optional<Hit> prepare(Pony player) {

View file

@ -3,7 +3,6 @@ package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.data.Numeric;
@ -54,11 +53,6 @@ public class PeckAbility implements Ability<Hit> {
return true;
}
@Override
public boolean canUse(Race race) {
return race == Race.HIPPOGRIFF;
}
@Override
public Optional<Hit> prepare(Pony player) {
return Hit.INSTANCE;

View file

@ -4,7 +4,6 @@ import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.UItems;
@ -33,11 +32,6 @@ public class PegasusCaptureStormAbility implements Ability<Hit> {
return 6;
}
@Override
public boolean canUse(Race race) {
return race.canInfluenceWeather();
}
@Nullable
@Override
public Optional<Hit> prepare(Pony player) {

View file

@ -4,7 +4,6 @@ import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
@ -26,11 +25,6 @@ public class PegasusRainboomAbility implements Ability<Hit> {
return 60;
}
@Override
public boolean canUse(Race race) {
return race.canInfluenceWeather();
}
@Nullable
@Override
public Optional<Hit> prepare(Pony player) {

View file

@ -3,7 +3,6 @@ package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.ability.data.Numeric;
@ -49,11 +48,6 @@ public class ScreechAbility implements Ability<Numeric> {
return true;
}
@Override
public boolean canUse(Race race) {
return race == Race.HIPPOGRIFF;
}
@Override
public Optional<Numeric> prepare(Pony player) {
return player.getAbilities().getActiveStat()

View file

@ -5,7 +5,6 @@ import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.AwaitTickQueue;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.UPOIs;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.data.Hit;
@ -38,11 +37,6 @@ public class SeaponySonarPulseAbility implements Ability<Hit> {
return 100;
}
@Override
public boolean canUse(Race race) {
return race == Race.SEAPONY;
}
@Nullable
@Override
public Optional<Hit> prepare(Pony player) {

View file

@ -12,11 +12,6 @@ import com.minelittlepony.unicopia.server.world.UGameRules;
public class TimeChangeAbility implements Ability<Rot> {
@Override
public boolean canUse(Race race) {
return race == Race.ALICORN;
}
@Override
public boolean canUse(Race.Composite race) {
return canUse(race.physical()) || race.pseudo() == Race.UNICORN;

View file

@ -24,11 +24,6 @@ public class ToggleFlightAbility implements Ability<Hit> {
return 0;
}
@Override
public boolean canUse(Race race) {
return race.canFly();
}
@Nullable
@Override
public Optional<Hit> prepare(Pony player) {

View file

@ -33,11 +33,6 @@ public class UnicornDispellAbility implements Ability<Pos> {
return 0;
}
@Override
public boolean canUse(Race race) {
return race.canCast() || race == Race.CHANGELING;
}
@Override
public int getColor(Pony player) {
return SpellType.PORTAL.getColor();

View file

@ -2,7 +2,6 @@ package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.data.Pos;
@ -60,11 +59,6 @@ public class UnicornTeleportAbility implements Ability<Pos> {
return (int)(50 - Math.min(45F, player.getLevel().get() * 0.75F));
}
@Override
public boolean canUse(Race race) {
return race.canCast() && race != Race.KIRIN;
}
@Override
public double getCostEstimate(Pony player) {
return prepare(player).map(pos -> pos.distanceTo(player) / 10).orElse(0D);

View file

@ -98,7 +98,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
@Override
public boolean isDead() {
return dead && deathTicks <= 0;
return dead && deathTicks <= 0 && super.isDead();
}
@Override

View file

@ -1,8 +1,12 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
@ -12,6 +16,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
import com.minelittlepony.unicopia.entity.mob.CastSpellEntity;
import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils;
@ -19,13 +24,17 @@ import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.projectile.MagicBeamEntity;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
import com.minelittlepony.unicopia.server.world.UGameRules;
import com.minelittlepony.unicopia.util.Lerp;
import com.minelittlepony.unicopia.util.shape.Sphere;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.entity.FallingBlockEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.PersistentProjectileEntity;
import net.minecraft.item.Item;
import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.predicate.entity.EntityPredicates;
@ -39,7 +48,7 @@ import net.minecraft.world.World.ExplosionSourceType;
/**
* More powerful version of the vortex spell which creates a black hole.
*/
public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelegate.BlockHitListener {
public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate.BlockHitListener {
public static final SpellTraits DEFAULT_TRAITS = new SpellTraits.Builder()
.with(Trait.CHAOS, 5)
.with(Trait.KNOWLEDGE, 1)
@ -49,18 +58,31 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
private float accumulatedMass = 0;
private final TargetSelecter targetSelecter = new TargetSelecter(this).setFilter(this::isValidTarget).setTargetowner(true).setTargetAllies(true);
private final Lerp radius = new Lerp(0);
protected DarkVortexSpell(CustomisedSpellType<?> type) {
super(type);
targetSelecter.setTargetowner(true).setTargetAllies(true);
}
// 1. force decreases with distance: distance scale 1 -> 0
// 2. max force (at dist 0) is taken from accumulated mass
// 3. force reaches 0 at distance of drawDropOffRange
private double getMass() {
return 0.1F + accumulatedMass / 10F;
}
@Override
public void onImpact(MagicProjectileEntity projectile, BlockHitResult hit) {
if (!projectile.isClient() && projectile instanceof MagicBeamEntity source) {
BlockPos pos = hit.getBlockPos();
projectile.getWorld().createExplosion(projectile, pos.getX(), pos.getY(), pos.getZ(), 3, ExplosionSourceType.NONE);
toPlaceable().tick(source, Situation.BODY);
public double getEventHorizonRadius() {
return radius.getValue();
}
public double getDrawDropOffRange() {
return getEventHorizonRadius() * 20;
}
private double getAttractiveForce(Caster<?> source, Entity target) {
return AttractionUtils.getAttractiveForce(getMass(), getOrigin(source), target);
}
@Override
@ -79,22 +101,102 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
return true;
}
Vec3d origin = getOrigin(source);
double mass = getMass() * 0.1;
double logarithm = 1 - (1D / (1 + (mass * mass)));
radius.update((float)Math.max(0.01, logarithm * source.asWorld().getGameRules().getInt(UGameRules.MAX_DARK_VORTEX_SIZE)), 200L);
if (source.asEntity().age % 20 == 0) {
source.asWorld().playSound(null, source.getOrigin(), USounds.AMBIENT_DARK_VORTEX_ADDITIONS, SoundCategory.AMBIENT, 1, 1);
}
if (!source.isClient() && source.asWorld().random.nextInt(300) == 0) {
ParticleUtils.spawnParticle(source.asWorld(), LightningBoltParticleEffect.DEFAULT, getOrigin(source), Vec3d.ZERO);
double eventHorizon = getEventHorizonRadius();
if (source.isClient()) {
if (eventHorizon > 0.3) {
double range = eventHorizon * 2;
source.spawnParticles(origin, new Sphere(false, range), 50, p -> {
source.addParticle(
new FollowingParticleEffect(UParticles.HEALTH_DRAIN, origin, 0.4F)
.withChild(source.asWorld().isAir(BlockPos.ofFloored(p)) ? ParticleTypes.SMOKE : ParticleTypes.CAMPFIRE_SIGNAL_SMOKE),
p,
Vec3d.ZERO
);
});
}
if (source.asWorld().random.nextInt(300) == 0) {
ParticleUtils.spawnParticle(source.asWorld(), LightningBoltParticleEffect.DEFAULT, origin, Vec3d.ZERO);
}
} else {
if (eventHorizon > 2) {
new Sphere(false, eventHorizon + 3).translate(origin).randomPoints(10, source.asWorld().random).forEach(i -> {
BlockPos pos = BlockPos.ofFloored(i);
if (!source.asWorld().isAir(pos)) {
new Sphere(false, 3).translate(i).getBlockPositions().forEach(p -> {
affectBlock(source, p, origin);
});
ParticleUtils.spawnParticle(source.asWorld(), new LightningBoltParticleEffect(true, 10, 6, 3, Optional.of(i)), getOrigin(source), Vec3d.ZERO);
}
});
}
}
for (Entity insideEntity : source.findAllEntitiesInRange(eventHorizon * 0.5F).toList()) {
insideEntity.setVelocity(Vec3d.ZERO);
Living.updateVelocity(insideEntity);
if (insideEntity instanceof CastSpellEntity s && getType().isOn(insideEntity)) {
setDead();
s.getSpellSlot().clear();
source.asWorld().createExplosion(source.asEntity(), origin.x, origin.y, origin.z, 12, ExplosionSourceType.NONE);
source.asWorld().createExplosion(source.asEntity(), insideEntity.getX(), insideEntity.getY(), insideEntity.getZ(), 12, ExplosionSourceType.NONE);
return false;
}
}
targetSelecter.getEntities(source, getDrawDropOffRange()).forEach(i -> {
try {
affectEntity(source, i, origin);
} catch (Throwable e) {
Unicopia.LOGGER.error("Error updating radial effect", e);
}
});
if (!source.subtractEnergyCost(0.01)) {
setDead();
source.asWorld().createExplosion(source.asEntity(), origin.x, origin.y, origin.z, 3, ExplosionSourceType.NONE);
}
super.tick(source, situation);
return true;
}
@Override
protected void consumeManage(Caster<?> source, long costMultiplier, float knowledge) {
if (!source.subtractEnergyCost(-accumulatedMass)) {
setDead();
public void tickDying(Caster<?> source) {
accumulatedMass -= 0.8F;
double mass = getMass() * 0.1;
double logarithm = 1 - (1D / (1 + (mass * mass)));
radius.update((float)Math.max(0.1, logarithm * source.asWorld().getGameRules().getInt(UGameRules.MAX_DARK_VORTEX_SIZE)), 200L);
if (accumulatedMass < 1) {
super.tickDying(source);
}
Vec3d origin = getOrigin(source);
ParticleUtils.spawnParticle(source.asWorld(), ParticleTypes.SMOKE, origin, new Vec3d(0, 0.2F, 0));
ParticleUtils.spawnParticle(source.asWorld(), ParticleTypes.SMOKE, origin, new Vec3d(0, -0.2F, 0));
if (!source.isClient() && source.asWorld().getRandom().nextInt(10) == 0) {
Block.dropStack(source.asWorld(), BlockPos.ofFloored(origin), (source.asWorld().getRandom().nextInt(75) == 0 ? Items.ANCIENT_DEBRIS : Items.IRON_NUGGET).getDefaultStack());
}
}
@Override
public void onImpact(MagicProjectileEntity projectile, BlockHitResult hit) {
if (!projectile.isClient() && projectile instanceof MagicBeamEntity source) {
Vec3d pos = hit.getPos();
projectile.getWorld().createExplosion(projectile, pos.x, pos.y, pos.z, 12, ExplosionSourceType.NONE);
toPlaceable().tick(source, Situation.BODY);
}
}
@ -103,102 +205,54 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
return accumulatedMass < 4;
}
@Override
protected boolean isValidTarget(Caster<?> source, Entity entity) {
private boolean isValidTarget(Caster<?> source, Entity entity) {
return EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR.test(entity) && getAttractiveForce(source, entity) > 0;
}
@Override
public void generateParticles(Caster<?> source) {
super.generateParticles(source);
if (getEventHorizonRadius() > 0.3) {
double range = getDrawDropOffRange(source);
Vec3d origin = getOrigin(source);
source.spawnParticles(origin, new Sphere(false, range), 1, p -> {
if (!source.asWorld().isAir(BlockPos.ofFloored(p))) {
source.addParticle(
new FollowingParticleEffect(UParticles.HEALTH_DRAIN, origin, 0.4F)
.withChild(ParticleTypes.CAMPFIRE_SIGNAL_SMOKE),
p,
Vec3d.ZERO
);
}
});
}
public Vec3d getOrigin(Caster<?> source) {
return source.asEntity().getPos().add(0, getYOffset(), 0);
}
@Override
public double getDrawDropOffRange(Caster<?> source) {
return getEventHorizonRadius() * 20;
public double getYOffset() {
return 2;
}
@Override
protected Vec3d getOrigin(Caster<?> source) {
return source.getOriginVector().add(0, getEventHorizonRadius() / 2D, 0);
}
@Override
protected long applyEntities(Caster<?> source) {
if (!source.isClient()) {
double radius = getEventHorizonRadius();
if (radius > 2) {
Vec3d origin = getOrigin(source);
new Sphere(false, radius).translate(origin).getBlockPositions().forEach(i -> {
if (!canAffect(source, i)) {
return;
}
if (source.getOrigin().isWithinDistance(i, getEventHorizonRadius() / 2)) {
source.asWorld().breakBlock(i, false);
} else {
CatapultSpell.createBlockEntity(source.asWorld(), i, e -> {
applyRadialEffect(source, e, e.getPos().distanceTo(origin), radius);
});
}
});
}
}
return super.applyEntities(source);
}
protected boolean canAffect(Caster<?> source, BlockPos pos) {
private boolean canAffect(Caster<?> source, BlockPos pos) {
return source.canModifyAt(pos)
&& source.asWorld().getFluidState(pos).isEmpty()
&& source.asWorld().getBlockState(pos).getHardness(source.asWorld(), pos) >= 0;
&& source.asWorld().getBlockState(pos).getHardness(source.asWorld(), pos) >= 0
&& !source.asWorld().getBlockState(pos).isIn(UTags.Blocks.CATAPULT_IMMUNE);
}
// 1. force decreases with distance: distance scale 1 -> 0
// 2. max force (at dist 0) is taken from accumulated mass
// 3. force reaches 0 at distance of drawDropOffRange
public double getEventHorizonRadius() {
return Math.sqrt(Math.max(0.001, getMass() / 3F));
private void affectBlock(Caster<?> source, BlockPos pos, Vec3d origin) {
if (!canAffect(source, pos)) {
if (source.asWorld().getBlockState(pos).isOf(Blocks.BEDROCK)) {
source.asWorld().setBlockState(pos, Blocks.BARRIER.getDefaultState());
}
private double getAttractiveForce(Caster<?> source, Entity target) {
return AttractionUtils.getAttractiveForce(getMass(), getOrigin(source), target);
}
private double getMass() {
return 0.1F + accumulatedMass / 10F;
}
@Override
protected void applyRadialEffect(Caster<?> source, Entity target, double distance, double radius) {
if (target instanceof FallingBlockEntity && source.isClient()) {
return;
}
if (distance <= getEventHorizonRadius() + 0.5) {
target.setVelocity(target.getVelocity().multiply(distance / (2 * radius)));
if (distance < 1) {
target.setVelocity(target.getVelocity().multiply(distance));
if (pos.isWithinDistance(origin, getEventHorizonRadius())) {
source.asWorld().breakBlock(pos, false);
updateStatePostRemoval(source, pos);
} else {
CatapultSpell.createBlockEntity(source.asWorld(), pos, e -> {
updateStatePostRemoval(source, pos);
e.addVelocity(0, 0.1, 0);
});
}
}
private void updateStatePostRemoval(Caster<?> source, BlockPos pos) {
if (!source.asWorld().getFluidState(pos).isEmpty()) {
source.asWorld().setBlockState(pos, Blocks.AIR.getDefaultState());
}
}
private void affectEntity(Caster<?> source, Entity target, Vec3d origin) {
double distance = target.getPos().distanceTo(origin);
double eventHorizonRadius = getEventHorizonRadius();
if (distance <= eventHorizonRadius + 0.5) {
target.setVelocity(target.getVelocity().multiply(distance < 1 ? distance : distance / (2 * eventHorizonRadius)));
Living.updateVelocity(target);
@Nullable
@ -227,20 +281,22 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
target.damage(source.damageOf(UDamageTypes.GAVITY_WELL_RECOIL, source), Integer.MAX_VALUE);
if (!(target instanceof PlayerEntity)) {
target.discard();
source.asWorld().playSound(null, source.getOrigin(), USounds.ENCHANTMENT_CONSUMPTION_CONSUME, SoundCategory.AMBIENT, 2, 0.02F);
source.asWorld().playSound(null, target.getBlockPos(), USounds.AMBIENT_DARK_VORTEX_MOOD, SoundCategory.AMBIENT, 2, 0.002F);
}
if (target.isAlive()) {
target.damage(source.asEntity().getDamageSources().outOfWorld(), Integer.MAX_VALUE);
}
source.subtractEnergyCost(-massOfTarget * 10);
source.asWorld().playSound(null, source.getOrigin(), USounds.AMBIENT_DARK_VORTEX_MOOD, SoundCategory.AMBIENT, 2, 0.02F);
if (target instanceof PlayerEntity && distance < eventHorizonRadius + 5) {
source.asWorld().playSound(null, target.getBlockPos(), USounds.AMBIENT_DARK_VORTEX_MOOD, SoundCategory.AMBIENT, 2, 0.02F);
}
} else {
double force = getAttractiveForce(source, target);
AttractionUtils.applyForce(getOrigin(source), target, -force, 0, true);
source.subtractEnergyCost(-2);
AttractionUtils.applyForce(origin, target, -force, 0, true);
}
}

View file

@ -197,7 +197,7 @@ public class NecromancySpell extends AbstractAreaEffectSpell implements Projecti
}
Equine.of(minion).filter(eq -> eq instanceof Creature).ifPresent(eq -> {
((Creature)eq).setMaster(source.getMaster());
((Creature)eq).setMaster(source);
if (source.asWorld().random.nextFloat() < source.getCorruption().getScaled(1)) {
((Creature)eq).setDiscorded(true);
}

View file

@ -57,7 +57,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
}
public boolean isLinked() {
return teleportationTarget.getTarget().isPresent();
return teleportationTarget.isSet();
}
public Optional<EntityReference.EntityValues<Entity>> getTarget() {
@ -85,7 +85,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
}
@SuppressWarnings("unchecked")
private Optional<Ether.Entry<PortalSpell>> getTarget(Caster<?> source) {
private Optional<Ether.Entry<PortalSpell>> getDestination(Caster<?> source) {
return getTarget().map(target -> Ether.get(source.asWorld()).get((SpellType<PortalSpell>)getType(), target, targetPortalId));
}
@ -98,13 +98,12 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
@Override
public boolean tick(Caster<?> source, Situation situation) {
if (situation == Situation.GROUND) {
if (source.isClient()) {
source.spawnParticles(particleArea, 5, pos -> {
source.addParticle(ParticleTypes.ELECTRIC_SPARK, pos, Vec3d.ZERO);
});
} else {
getTarget().ifPresent(target -> {
teleportationTarget.getTarget().ifPresent(target -> {
if (Ether.get(source.asWorld()).get(getType(), target, targetPortalId) == null) {
Unicopia.LOGGER.debug("Lost sibling, breaking connection to " + target.uuid());
teleportationTarget.set(null);
@ -113,16 +112,17 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
}
});
getTarget(source).ifPresentOrElse(
getDestination(source).ifPresentOrElse(
entry -> tickWithTargetLink(source, entry),
() -> findLink(source)
);
}
var entry = Ether.get(source.asWorld()).getOrCreate(this, source);
Ether ether = Ether.get(source.asWorld());
var entry = ether.getOrCreate(this, source);
entry.pitch = pitch;
entry.yaw = yaw;
Ether.get(source.asWorld()).markDirty();
ether.markDirty();
}
return !isDead();
@ -185,8 +185,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
}
Ether.get(source.asWorld()).anyMatch(getType(), entry -> {
if (entry.isAvailable() && !entry.entity.referenceEquals(source.asEntity()) && entry.entity.isSet()) {
entry.setTaken(true);
if (!entry.entity.referenceEquals(source.asEntity()) && entry.claim()) {
teleportationTarget.copyFrom(entry.entity);
targetPortalId = entry.getSpellId();
setDirty();
@ -216,9 +215,8 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
@Override
protected void onDestroyed(Caster<?> caster) {
Ether ether = Ether.get(caster.asWorld());
ether.remove(getType(), caster);
getTarget(caster).ifPresent(e -> e.setTaken(false));
Ether.get(caster.asWorld()).remove(getType(), caster);
getDestination(caster).ifPresent(Ether.Entry::release);
}
@Override

View file

@ -1,10 +1,5 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.EnumMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Affinity;
@ -20,6 +15,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.ThrowableSpell;
import com.minelittlepony.unicopia.ability.magic.spell.TimeControlAbilitySpell;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.item.GemstoneItem;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.util.RegistryUtils;
import com.mojang.brigadier.context.CommandContext;
@ -38,48 +34,47 @@ import net.minecraft.server.command.ServerCommandSource;
public final class SpellType<T extends Spell> implements Affine, SpellPredicate<T> {
public static final Identifier EMPTY_ID = Unicopia.id("none");
public static final SpellType<?> EMPTY_KEY = new SpellType<>(EMPTY_ID, Affinity.NEUTRAL, 0xFFFFFF, false, SpellTraits.EMPTY, t -> null);
public static final SpellType<?> EMPTY_KEY = new SpellType<>(EMPTY_ID, Affinity.NEUTRAL, 0xFFFFFF, false, GemstoneItem.Shape.ROUND, SpellTraits.EMPTY, t -> null);
public static final Registry<SpellType<?>> REGISTRY = RegistryUtils.createSimple(Unicopia.id("spells"));
public static final RegistryKey<? extends Registry<SpellType<?>>> REGISTRY_KEY = REGISTRY.getKey();
public static final Map<Affinity, Set<SpellType<?>>> BY_AFFINITY = new EnumMap<>(Affinity.class);
private static final DynamicCommandExceptionType UNKNOWN_SPELL_TYPE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("spell_type.unknown", id));
public static final SpellType<PlaceableSpell> PLACED_SPELL = register("placed", Affinity.NEUTRAL, 0, false, SpellTraits.EMPTY, PlaceableSpell::new);
public static final SpellType<ThrowableSpell> THROWN_SPELL = register("thrown", Affinity.NEUTRAL, 0, false, SpellTraits.EMPTY, ThrowableSpell::new);
public static final SpellType<PlaceableSpell> PLACED_SPELL = register("placed", Affinity.NEUTRAL, 0, false, GemstoneItem.Shape.DONUT, SpellTraits.EMPTY, PlaceableSpell::new);
public static final SpellType<ThrowableSpell> THROWN_SPELL = register("thrown", Affinity.NEUTRAL, 0, false, GemstoneItem.Shape.DONUT, SpellTraits.EMPTY, ThrowableSpell::new);
public static final SpellType<DispersableDisguiseSpell> CHANGELING_DISGUISE = register("disguise", Affinity.BAD, 0x19E48E, false, SpellTraits.EMPTY, DispersableDisguiseSpell::new);
public static final SpellType<ChangelingFeedingSpell> FEED = register("feed", Affinity.BAD, 0xBDBDF9, false, SpellTraits.EMPTY, ChangelingFeedingSpell::new);
public static final SpellType<RainboomAbilitySpell> RAINBOOM = register("rainboom", Affinity.GOOD, 0xBDBDF9, false, SpellTraits.EMPTY, RainboomAbilitySpell::new);
public static final SpellType<RageAbilitySpell> RAGE = register("rage", Affinity.GOOD, 0xBDBDF9, false, SpellTraits.EMPTY, RageAbilitySpell::new);
public static final SpellType<TimeControlAbilitySpell> TIME_CONTROL = register("time_control", Affinity.GOOD, 0xBDBDF9, false, SpellTraits.EMPTY, TimeControlAbilitySpell::new);
public static final SpellType<DispersableDisguiseSpell> CHANGELING_DISGUISE = register("disguise", Affinity.BAD, 0x19E48E, false, GemstoneItem.Shape.ARROW, SpellTraits.EMPTY, DispersableDisguiseSpell::new);
public static final SpellType<ChangelingFeedingSpell> FEED = register("feed", Affinity.BAD, 0xBDBDF9, false, GemstoneItem.Shape.ARROW, SpellTraits.EMPTY, ChangelingFeedingSpell::new);
public static final SpellType<RainboomAbilitySpell> RAINBOOM = register("rainboom", Affinity.GOOD, 0xBDBDF9, false, GemstoneItem.Shape.ROCKET, SpellTraits.EMPTY, RainboomAbilitySpell::new);
public static final SpellType<RageAbilitySpell> RAGE = register("rage", Affinity.GOOD, 0xBDBDF9, false, GemstoneItem.Shape.FLAME, SpellTraits.EMPTY, RageAbilitySpell::new);
public static final SpellType<TimeControlAbilitySpell> TIME_CONTROL = register("time_control", Affinity.GOOD, 0xBDBDF9, false, GemstoneItem.Shape.STAR, SpellTraits.EMPTY, TimeControlAbilitySpell::new);
public static final SpellType<IceSpell> FROST = register("frost", Affinity.GOOD, 0xEABBFF, true, IceSpell.DEFAULT_TRAITS, IceSpell::new);
public static final SpellType<ChillingBreathSpell> CHILLING_BREATH = register("chilling_breath", Affinity.NEUTRAL, 0xFFEAFF, true, ChillingBreathSpell.DEFAULT_TRAITS, ChillingBreathSpell::new);
public static final SpellType<ScorchSpell> SCORCH = register("scorch", Affinity.BAD, 0xF8EC1F, true, ScorchSpell.DEFAULT_TRAITS, ScorchSpell::new);
public static final SpellType<FireSpell> FLAME = register("flame", Affinity.GOOD, 0xFFBB99, true, FireSpell.DEFAULT_TRAITS, FireSpell::new);
public static final SpellType<InfernoSpell> INFERNAL = register("infernal", Affinity.BAD, 0xFFAA00, true, InfernoSpell.DEFAULT_TRAITS, InfernoSpell::new);
public static final SpellType<ShieldSpell> SHIELD = register("shield", Affinity.NEUTRAL, 0x66CDAA, true, ShieldSpell.DEFAULT_TRAITS, ShieldSpell::new);
public static final SpellType<AreaProtectionSpell> ARCANE_PROTECTION = register("arcane_protection", Affinity.BAD, 0x99CDAA, true, AreaProtectionSpell.DEFAULT_TRAITS, AreaProtectionSpell::new);
public static final SpellType<AttractiveSpell> VORTEX = register("vortex", Affinity.NEUTRAL, 0xFFEA88, true, AttractiveSpell.DEFAULT_TRAITS, AttractiveSpell::new);
public static final SpellType<DarkVortexSpell> DARK_VORTEX = register("dark_vortex", Affinity.BAD, 0xA33333, true, DarkVortexSpell.DEFAULT_TRAITS, DarkVortexSpell::new);
public static final SpellType<NecromancySpell> NECROMANCY = register("necromancy", Affinity.BAD, 0xFA3A3A, true, SpellTraits.EMPTY, NecromancySpell::new);
public static final SpellType<SiphoningSpell> SIPHONING = register("siphoning", Affinity.NEUTRAL, 0xFFA3AA, true, SpellTraits.EMPTY, SiphoningSpell::new);
public static final SpellType<DisperseIllusionSpell> REVEALING = register("reveal", Affinity.GOOD, 0xFFFFAF, true, SpellTraits.EMPTY, DisperseIllusionSpell::new);
public static final SpellType<AwkwardSpell> AWKWARD = register("awkward", Affinity.GOOD, 0x3A59FF, true, SpellTraits.EMPTY, AwkwardSpell::new);
public static final SpellType<TransformationSpell> TRANSFORMATION = register("transformation", Affinity.GOOD, 0x19E48E, true, SpellTraits.EMPTY, TransformationSpell::new);
public static final SpellType<FeatherFallSpell> FEATHER_FALL = register("feather_fall", Affinity.GOOD, 0x00EEFF, true, FeatherFallSpell.DEFAULT_TRAITS, FeatherFallSpell::new);
public static final SpellType<CatapultSpell> CATAPULT = register("catapult", Affinity.GOOD, 0x22FF00, true, CatapultSpell.DEFAULT_TRAITS, CatapultSpell::new);
public static final SpellType<FireBoltSpell> FIRE_BOLT = register("fire_bolt", Affinity.GOOD, 0xFF8811, true, FireBoltSpell.DEFAULT_TRAITS, FireBoltSpell::new);
public static final SpellType<LightSpell> LIGHT = register("light", Affinity.GOOD, 0xEEFFAA, true, LightSpell.DEFAULT_TRAITS, LightSpell::new);
public static final SpellType<DisplacementSpell> DISPLACEMENT = register("displacement", Affinity.NEUTRAL, 0x9900FF, true, PortalSpell.DEFAULT_TRAITS, DisplacementSpell::new);
public static final SpellType<PortalSpell> PORTAL = register("portal", Affinity.GOOD, 0x99FFFF, true, PortalSpell.DEFAULT_TRAITS, PortalSpell::new);
public static final SpellType<MimicSpell> MIMIC = register("mimic", Affinity.GOOD, 0xFFFF00, true, SpellTraits.EMPTY, MimicSpell::new);
public static final SpellType<MindSwapSpell> MIND_SWAP = register("mind_swap", Affinity.BAD, 0xF9FF99, true, SpellTraits.EMPTY, MindSwapSpell::new);
public static final SpellType<HydrophobicSpell> HYDROPHOBIC = register("hydrophobic", Affinity.NEUTRAL, 0xF999FF, true, SpellTraits.EMPTY, s -> new HydrophobicSpell(s, FluidTags.WATER));
public static final SpellType<BubbleSpell> BUBBLE = register("bubble", Affinity.NEUTRAL, 0xF999FF, true, BubbleSpell.DEFAULT_TRAITS, BubbleSpell::new);
public static final SpellType<DispellEvilSpell> DISPEL_EVIL = register("dispel_evil", Affinity.GOOD, 0x00FF00, true, DispellEvilSpell.DEFAULT_TRAITS, DispellEvilSpell::new);
public static final SpellType<IceSpell> FROST = register("frost", Affinity.GOOD, 0xEABBFF, true, GemstoneItem.Shape.TRIANGLE, IceSpell.DEFAULT_TRAITS, IceSpell::new);
public static final SpellType<ChillingBreathSpell> CHILLING_BREATH = register("chilling_breath", Affinity.NEUTRAL, 0xFFEAFF, true, GemstoneItem.Shape.TRIANGLE, ChillingBreathSpell.DEFAULT_TRAITS, ChillingBreathSpell::new);
public static final SpellType<ScorchSpell> SCORCH = register("scorch", Affinity.BAD, 0xF8EC1F, true, GemstoneItem.Shape.FLAME, ScorchSpell.DEFAULT_TRAITS, ScorchSpell::new);
public static final SpellType<FireSpell> FLAME = register("flame", Affinity.GOOD, 0xFFBB99, true, GemstoneItem.Shape.FLAME, FireSpell.DEFAULT_TRAITS, FireSpell::new);
public static final SpellType<InfernoSpell> INFERNAL = register("infernal", Affinity.BAD, 0xFFAA00, true, GemstoneItem.Shape.FLAME, InfernoSpell.DEFAULT_TRAITS, InfernoSpell::new);
public static final SpellType<ShieldSpell> SHIELD = register("shield", Affinity.NEUTRAL, 0x66CDAA, true, GemstoneItem.Shape.SHIELD, ShieldSpell.DEFAULT_TRAITS, ShieldSpell::new);
public static final SpellType<AreaProtectionSpell> ARCANE_PROTECTION = register("arcane_protection", Affinity.BAD, 0x99CDAA, true, GemstoneItem.Shape.SHIELD, AreaProtectionSpell.DEFAULT_TRAITS, AreaProtectionSpell::new);
public static final SpellType<AttractiveSpell> VORTEX = register("vortex", Affinity.NEUTRAL, 0xFFEA88, true, GemstoneItem.Shape.VORTEX, AttractiveSpell.DEFAULT_TRAITS, AttractiveSpell::new);
public static final SpellType<DarkVortexSpell> DARK_VORTEX = register("dark_vortex", Affinity.BAD, 0xA33333, true, GemstoneItem.Shape.VORTEX, DarkVortexSpell.DEFAULT_TRAITS, DarkVortexSpell::new);
public static final SpellType<NecromancySpell> NECROMANCY = register("necromancy", Affinity.BAD, 0xFA3A3A, true, GemstoneItem.Shape.SKULL, SpellTraits.EMPTY, NecromancySpell::new);
public static final SpellType<SiphoningSpell> SIPHONING = register("siphoning", Affinity.NEUTRAL, 0xFFA3AA, true, GemstoneItem.Shape.LAMBDA, SpellTraits.EMPTY, SiphoningSpell::new);
public static final SpellType<DisperseIllusionSpell> REVEALING = register("reveal", Affinity.GOOD, 0xFFFFAF, true, GemstoneItem.Shape.CROSS, SpellTraits.EMPTY, DisperseIllusionSpell::new);
public static final SpellType<AwkwardSpell> AWKWARD = register("awkward", Affinity.GOOD, 0x3A59FF, true, GemstoneItem.Shape.ICE, SpellTraits.EMPTY, AwkwardSpell::new);
public static final SpellType<TransformationSpell> TRANSFORMATION = register("transformation", Affinity.GOOD, 0x19E48E, true, GemstoneItem.Shape.BRUSH, SpellTraits.EMPTY, TransformationSpell::new);
public static final SpellType<FeatherFallSpell> FEATHER_FALL = register("feather_fall", Affinity.GOOD, 0x00EEFF, true, GemstoneItem.Shape.LAMBDA, FeatherFallSpell.DEFAULT_TRAITS, FeatherFallSpell::new);
public static final SpellType<CatapultSpell> CATAPULT = register("catapult", Affinity.GOOD, 0x22FF00, true, GemstoneItem.Shape.ROCKET, CatapultSpell.DEFAULT_TRAITS, CatapultSpell::new);
public static final SpellType<FireBoltSpell> FIRE_BOLT = register("fire_bolt", Affinity.GOOD, 0xFF8811, true, GemstoneItem.Shape.FLAME, FireBoltSpell.DEFAULT_TRAITS, FireBoltSpell::new);
public static final SpellType<LightSpell> LIGHT = register("light", Affinity.GOOD, 0xEEFFAA, true, GemstoneItem.Shape.STAR, LightSpell.DEFAULT_TRAITS, LightSpell::new);
public static final SpellType<DisplacementSpell> DISPLACEMENT = register("displacement", Affinity.NEUTRAL, 0x9900FF, true, GemstoneItem.Shape.BRUSH, PortalSpell.DEFAULT_TRAITS, DisplacementSpell::new);
public static final SpellType<PortalSpell> PORTAL = register("portal", Affinity.GOOD, 0x99FFFF, true, GemstoneItem.Shape.RING, PortalSpell.DEFAULT_TRAITS, PortalSpell::new);
public static final SpellType<MimicSpell> MIMIC = register("mimic", Affinity.GOOD, 0xFFFF00, true, GemstoneItem.Shape.ARROW, SpellTraits.EMPTY, MimicSpell::new);
public static final SpellType<MindSwapSpell> MIND_SWAP = register("mind_swap", Affinity.BAD, 0xF9FF99, true, GemstoneItem.Shape.WAVE, SpellTraits.EMPTY, MindSwapSpell::new);
public static final SpellType<HydrophobicSpell> HYDROPHOBIC = register("hydrophobic", Affinity.NEUTRAL, 0xF999FF, true, GemstoneItem.Shape.ROCKET, SpellTraits.EMPTY, s -> new HydrophobicSpell(s, FluidTags.WATER));
public static final SpellType<BubbleSpell> BUBBLE = register("bubble", Affinity.NEUTRAL, 0xF999FF, true, GemstoneItem.Shape.DONUT, BubbleSpell.DEFAULT_TRAITS, BubbleSpell::new);
public static final SpellType<DispellEvilSpell> DISPEL_EVIL = register("dispel_evil", Affinity.GOOD, 0x00FF00, true, GemstoneItem.Shape.CROSS, DispellEvilSpell.DEFAULT_TRAITS, DispellEvilSpell::new);
public static void bootstrap() {}
@ -87,6 +82,7 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
private final Affinity affinity;
private final int color;
private final boolean obtainable;
private final GemstoneItem.Shape shape;
private final Factory<T> factory;
@ -98,11 +94,12 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
private final ItemStack defaultStack;
private SpellType(Identifier id, Affinity affinity, int color, boolean obtainable, SpellTraits traits, Factory<T> factory) {
private SpellType(Identifier id, Affinity affinity, int color, boolean obtainable, GemstoneItem.Shape shape, SpellTraits traits, Factory<T> factory) {
this.id = id;
this.affinity = affinity;
this.color = color;
this.obtainable = obtainable;
this.shape = shape;
this.factory = factory;
this.traits = traits;
traited = new CustomisedSpellType<>(this, traits);
@ -133,6 +130,10 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
return affinity;
}
public GemstoneItem.Shape getGemShape() {
return shape;
}
public SpellTraits getTraits() {
return traits;
}
@ -178,15 +179,12 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
return "SpellType[" + getTranslationKey() + "]";
}
public static <T extends Spell> SpellType<T> register(String name, Affinity affinity, int color, boolean obtainable, SpellTraits traits, Factory<T> factory) {
return register(Unicopia.id(name), affinity, color, obtainable, traits, factory);
public static <T extends Spell> SpellType<T> register(String name, Affinity affinity, int color, boolean obtainable, GemstoneItem.Shape shape, SpellTraits traits, Factory<T> factory) {
return register(Unicopia.id(name), affinity, color, obtainable, shape, traits, factory);
}
public static <T extends Spell> SpellType<T> register(Identifier id, Affinity affinity, int color, boolean obtainable, SpellTraits traits, Factory<T> factory) {
SpellType<T> type = new SpellType<>(id, affinity, color, obtainable, traits, factory);
byAffinity(affinity).add(type);
Registry.register(REGISTRY, id, type);
return type;
public static <T extends Spell> SpellType<T> register(Identifier id, Affinity affinity, int color, boolean obtainable, GemstoneItem.Shape shape, SpellTraits traits, Factory<T> factory) {
return Registry.register(REGISTRY, id, new SpellType<>(id, affinity, color, obtainable, shape, traits, factory));
}
@SuppressWarnings("unchecked")
@ -203,10 +201,6 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
return (SpellType<T>)REGISTRY.getOrEmpty(id).orElse(EMPTY_KEY);
}
public static Set<SpellType<?>> byAffinity(Affinity affinity) {
return BY_AFFINITY.computeIfAbsent(affinity, a -> new LinkedHashSet<>());
}
public static SpellType<?> fromArgument(CommandContext<ServerCommandSource> context, String name) throws CommandSyntaxException {
Identifier id = context.getArgument(name, RegistryKey.class).getValue();
return REGISTRY.getOrEmpty(id).orElseThrow(() -> UNKNOWN_SPELL_TYPE_EXCEPTION.create(id));

View file

@ -18,7 +18,9 @@ import com.minelittlepony.unicopia.entity.player.Pony;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.option.StickyKeyBinding;
import net.minecraft.client.sound.PositionedSoundInstance;
import net.minecraft.client.util.InputUtil;
import net.minecraft.text.Text;
import net.minecraft.util.math.MathHelper;
@ -35,6 +37,10 @@ public class KeyBindingsHandler {
private final Binding pageDown = register(GLFW.GLFW_KEY_PAGE_DOWN, "hud_page_dn");
private final Binding pageUp = register(GLFW.GLFW_KEY_PAGE_UP, "hud_page_up");
private final Binding singleTapModifier = register(InputUtil.UNKNOWN_KEY.getCode(), "ability_modifier_tap");
private final Binding doubleTapModifier = register(InputUtil.UNKNOWN_KEY.getCode(), "ability_modifier_double_tap");
private final Binding tripleTapModifier = register(InputUtil.UNKNOWN_KEY.getCode(), "ability_modifier_triple_tap");
private final Set<KeyBinding> pressed = new HashSet<>();
public KeyBindingsHandler() {
@ -47,8 +53,12 @@ public class KeyBindingsHandler {
return reverse.get(slot);
}
public boolean isToggleMode() {
return Unicopia.getConfig().toggleAbilityKeys.get();
}
public void addKeybind(int code, AbilitySlot slot) {
Binding binding = register(code, slot.name().toLowerCase());
Binding binding = new Binding(KeyBindingHelper.registerKeyBinding(new StickyKeyBinding("key.unicopia." + slot.name().toLowerCase(), code, KEY_CATEGORY, this::isToggleMode)));
reverse.put(slot, binding);
keys.put(binding, slot);
}
@ -104,6 +114,7 @@ public class KeyBindingsHandler {
int page = Unicopia.getConfig().hudPage.get();
page += sigma;
Unicopia.getConfig().hudPage.set(page);
Unicopia.getConfig().save();
client.getSoundManager().play(PositionedSoundInstance.master(USounds.Vanilla.UI_BUTTON_CLICK, 1.75F + (0.25F * sigma)));
UHud.INSTANCE.setMessage(Text.translatable("gui.unicopia.page_num", page + 1, max + 1));
}
@ -141,6 +152,27 @@ public class KeyBindingsHandler {
}
public ActivationType getType() {
if (binding.isPressed() && binding instanceof StickyKeyBinding) {
if (singleTapModifier.binding.isPressed()) {
KeyBinding.untoggleStickyKeys();
return ActivationType.TAP;
}
if (doubleTapModifier.binding.isPressed()) {
KeyBinding.untoggleStickyKeys();
return ActivationType.DOUBLE_TAP;
}
if (tripleTapModifier.binding.isPressed()) {
KeyBinding.untoggleStickyKeys();
return ActivationType.TRIPLE_TAP;
}
if (isToggleMode()) {
return ActivationType.NONE;
}
}
long now = System.currentTimeMillis();
if (type != ActivationType.NONE && now > nextPhaseTime - 70) {
ActivationType t = type;

View file

@ -122,7 +122,8 @@ public interface URenderers {
register(URenderers::renderBedItem, UItems.CLOTH_BED, UItems.CLOUD_BED);
register(URenderers::renderChestItem, UBlocks.CLOUD_CHEST.asItem());
PolearmRenderer.register(UItems.WOODEN_POLEARM, UItems.STONE_POLEARM, UItems.IRON_POLEARM, UItems.GOLDEN_POLEARM, UItems.DIAMOND_POLEARM, UItems.NETHERITE_POLEARM);
ModelPredicateProviderRegistry.register(UItems.GEMSTONE, new Identifier("affinity"), (stack, world, entity, seed) -> EnchantableItem.isEnchanted(stack) ? EnchantableItem.getSpellKey(stack).getAffinity().getAlignment() : 0);
ModelPredicateProviderRegistry.register(UItems.GEMSTONE, new Identifier("affinity"), (stack, world, entity, seed) -> EnchantableItem.getSpellKey(stack).getAffinity().getAlignment());
ModelPredicateProviderRegistry.register(UItems.GEMSTONE, new Identifier("shape"), (stack, world, entity, seed) -> EnchantableItem.getSpellKey(stack).getGemShape().getId());
ModelPredicateProviderRegistry.register(UItems.ROCK_CANDY, new Identifier("count"), (stack, world, entity, seed) -> stack.getCount() / (float)stack.getMaxCount());
ModelPredicateProviderRegistry.register(UItems.BUTTERFLY, new Identifier("variant"), (stack, world, entity, seed) -> (float)ButterflyItem.getVariant(stack).ordinal() / ButterflyEntity.Variant.VALUES.length);
ModelPredicateProviderRegistry.register(Unicopia.id("zap_cycle"), new ClampedModelPredicateProvider() {

View file

@ -68,6 +68,10 @@ public class SettingsScreen extends GameGui {
})
.getStyle().setText("unicopia.options.ignore_mine_lp");
content.addButton(new Toggle(LEFT, row += 20, config.toggleAbilityKeys.get()))
.onChange(config.toggleAbilityKeys)
.getStyle().setText("unicopia.options.toggle_ability_keys");
mineLpStatus = content.addButton(new Label(LEFT, row += 10)).getStyle().setText(getMineLPStatus());
RegistryIndexer<Race> races = RegistryIndexer.of(Race.REGISTRY);

View file

@ -1,5 +1,6 @@
package com.minelittlepony.unicopia.client.gui;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.AbilityDispatcher;
import com.minelittlepony.unicopia.ability.AbilitySlot;
import com.minelittlepony.unicopia.client.KeyBindingsHandler;
@ -82,6 +83,12 @@ class Slot {
bSwap &= abilities.isFilled(bSlot);
}
AbilityDispatcher.Stat stat = abilities.getStat(bSwap ? bSlot : aSlot);
if (stat.getAbility(Unicopia.getConfig().hudPage.get()).isEmpty()) {
return;
}
RenderSystem.setShaderColor(1, 1, 1, 1);
RenderSystem.enableBlend();
MatrixStack matrices = context.getMatrices();
@ -91,7 +98,7 @@ class Slot {
// background
context.drawTexture(UHud.HUD_TEXTURE, 0, 0, backgroundU, backgroundV, size, size, 128, 128);
AbilityDispatcher.Stat stat = abilities.getStat(bSwap ? bSlot : aSlot);
int iconPosition = ((size - iconSize + slotPadding + 1) / 2);
int sz = iconSize - slotPadding;
@ -122,6 +129,11 @@ class Slot {
}
void renderLabel(DrawContext context, AbilityDispatcher abilities, float tickDelta) {
if (abilities.getStat(aSlot).getAbility(Unicopia.getConfig().hudPage.get()).isEmpty()) {
return;
}
Text label = KeyBindingsHandler.INSTANCE.getBinding(aSlot).getLabel();
MatrixStack matrices = context.getMatrices();

View file

@ -48,8 +48,8 @@ public class UHud {
private final List<Slot> slots = List.of(
new ManaRingSlot(this, AbilitySlot.PRIMARY, AbilitySlot.PASSIVE, 0, 0),
new Slot(this, AbilitySlot.SECONDARY, AbilitySlot.SECONDARY, 26, -5),
new Slot(this, AbilitySlot.TERTIARY, AbilitySlot.TERTIARY, 36, 19)
new Slot(this, AbilitySlot.SECONDARY, AbilitySlot.SECONDARY, 30, -8),
new Slot(this, AbilitySlot.TERTIARY, AbilitySlot.TERTIARY, 40, 18)
);
@Nullable
@ -68,10 +68,6 @@ public class UHud {
private SpellType<?> focusedType = SpellType.empty();
public void render(InGameHud hud, DrawContext context, float tickDelta) {
// TODO: Check this when backporting!
// InGameHud#renderHotbar line 460
// context.getMatrices().translate(0.0f, 0.0f, -90.0f);
final int hotbarZ = -90;
if (client.player == null) {
@ -96,7 +92,6 @@ public class UHud {
font = client.textRenderer;
xDirection = client.player.getMainArm() == Arm.LEFT ? -1 : 1;
matrices.push();
matrices.translate(scaledWidth / 2, scaledHeight / 2, 0);
@ -109,7 +104,7 @@ public class UHud {
matrices.pop();
matrices.push();
int hudX = ((scaledWidth - 50) / 2) + (104 * xDirection);
int hudX = ((scaledWidth - 50) / 2) + (109 * xDirection);
int hudY = scaledHeight - 50;
int hudZ = hotbarZ;
@ -139,14 +134,13 @@ public class UHud {
slots.forEach(slot -> slot.renderBackground(context, abilities, swap, tickDelta));
boolean canCast = Abilities.CAST.canUse(pony.getCompositeRace()) || Abilities.KIRIN_CAST.canUse(pony.getCompositeRace());
if (canCast) {
Ability<?> ability = pony.getAbilities().getStat(AbilitySlot.PRIMARY)
.getAbility(Unicopia.getConfig().hudPage.get())
.orElse(null);
boolean canCast = ability == Abilities.CAST || ability == Abilities.KIRIN_CAST || ability == Abilities.SHOOT;
if (ability == Abilities.CAST || ability == Abilities.SHOOT) {
if (canCast) {
matrices.push();
matrices.translate(PRIMARY_SLOT_SIZE / 2F, PRIMARY_SLOT_SIZE / 2F, 0);
boolean first = !pony.asEntity().isSneaking();
@ -158,7 +152,9 @@ public class UHud {
prevReplacing = replacing;
setMessage(ability.getName(pony));
}
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(first ? 37 : 63));
int baseAngle = xDirection < 0 ? 100 : 0;
int secondAngleDif = xDirection * 30;
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(baseAngle + 37 + (first ? 0 : secondAngleDif)));
matrices.translate(-23, 0, 0);
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-26));
matrices.scale(0.8F, 0.8F, 1);
@ -166,15 +162,19 @@ public class UHud {
context.drawTexture(HUD_TEXTURE, 0, 0, u, 120, 13, 7, 128, 128);
matrices.pop();
}
}
slots.forEach(slot -> slot.renderLabel(context, abilities, tickDelta));
matrices.pop();
if (canCast) {
matrices.push();
if (xDirection < 0) {
hudX += PRIMARY_SLOT_SIZE / 2F - 8;
}
SpellIconRenderer.renderSpell(context, pony.getCharms().getEquippedSpell(Hand.MAIN_HAND), hudX + 10 - xDirection * 13, hudY + 2, EQUIPPED_GEMSTONE_SCALE);
SpellIconRenderer.renderSpell(context, pony.getCharms().getEquippedSpell(Hand.OFF_HAND), hudX + 8 - xDirection * 2, hudY - 6, EQUIPPED_GEMSTONE_SCALE);
matrices.pop();
}
RenderSystem.disableBlend();
@ -198,7 +198,7 @@ public class UHud {
float vortexDistortion = DarkVortexSpellRenderer.getCameraDistortion();
if (vortexDistortion > 20) {
if (vortexDistortion > 25) {
context.fill(RenderLayers.getEndPortal(), 0, 0, scaledWidth, scaledHeight, 0);
context.getMatrices().push();
context.getMatrices().translate(scaledWidth / 2, scaledHeight / 2, 0);
@ -206,7 +206,7 @@ public class UHud {
context.getMatrices().pop();
return;
} else if (vortexDistortion > 0) {
context.fill(0, 0, scaledWidth, scaledHeight, (int)((vortexDistortion / 20F) * 255) << 24);
context.fill(0, 0, scaledWidth, scaledHeight, (int)((Math.min(20, vortexDistortion) / 20F) * 255) << 24);
}
boolean hasEffect = client.player.hasStatusEffect(UEffects.SUN_BLINDNESS);

View file

@ -50,7 +50,7 @@ class BodyPartGear<M extends ClientPonyModel<LivingEntity>> implements Gear {
public static final Predicate<LivingEntity> UNICORN_HORN_PREDICATE = MINE_LP_HAS_NO_HORN.and(AmuletSelectors.ALICORN_AMULET.or(EquinePredicates.raceMatches(com.minelittlepony.unicopia.Race::canCast)));
public static final Identifier UNICORN_HORN = Unicopia.id("textures/models/horn/unicorn.png");
public static final Predicate<LivingEntity> PEGA_WINGS_PREDICATE = MINE_LP_HAS_NO_WINGS.and(AmuletSelectors.PEGASUS_AMULET.or(EquinePredicates.raceMatches(race -> race.flightType() == FlightType.AVIAN)));
public static final Predicate<LivingEntity> PEGA_WINGS_PREDICATE = MINE_LP_HAS_NO_WINGS.and(AmuletSelectors.PEGASUS_AMULET.or(EquinePredicates.raceMatches(race -> race != com.minelittlepony.unicopia.Race.BAT && race.flightType() == FlightType.AVIAN)));
public static final Identifier PEGASUS_WINGS = Unicopia.id("textures/models/wings/pegasus_pony.png");
public static BodyPartGear<WingsGearModel> pegasusWings() {

View file

@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.client.render;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.entity.AmuletSelectors;
import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.client.model.Dilation;
@ -56,7 +57,9 @@ public class BatWingsFeatureRenderer<E extends LivingEntity> extends WingsFeatur
@Override
protected boolean canRender(E entity) {
return entity instanceof PlayerEntity && Pony.of((PlayerEntity)entity).getObservedSpecies() == Race.BAT;
return entity instanceof PlayerEntity
&& Pony.of((PlayerEntity)entity).getObservedSpecies() == Race.BAT
&& !AmuletSelectors.PEGASUS_AMULET.test(entity);
}
@Override

View file

@ -1,7 +1,9 @@
package com.minelittlepony.unicopia.client.render;
import com.minelittlepony.unicopia.FlightType;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.entity.AmuletSelectors;
import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.client.model.Dilation;
@ -51,7 +53,10 @@ public class WingsFeatureRenderer<E extends LivingEntity> implements AccessoryFe
}
protected boolean canRender(E entity) {
return entity instanceof PlayerEntity && Pony.of((PlayerEntity)entity).getObservedSpecies().flightType() == FlightType.AVIAN;
return entity instanceof PlayerEntity player
&& Pony.of(player).getObservedSpecies().flightType() == FlightType.AVIAN
&& Pony.of(player).getObservedSpecies() != Race.BAT
&& !AmuletSelectors.PEGASUS_AMULET.test(entity);
}
protected Identifier getTexture(E entity) {

View file

@ -7,15 +7,17 @@ import com.minelittlepony.unicopia.ability.magic.spell.effect.DarkVortexSpell;
import com.minelittlepony.unicopia.client.render.RenderLayers;
import com.minelittlepony.unicopia.client.render.model.PlaneModel;
import com.minelittlepony.unicopia.client.render.model.SphereModel;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RotationAxis;
import net.minecraft.util.math.Vec3d;
public class DarkVortexSpellRenderer extends SpellRenderer<DarkVortexSpell> {
@ -36,65 +38,72 @@ public class DarkVortexSpellRenderer extends SpellRenderer<DarkVortexSpell> {
@Override
public void render(MatrixStack matrices, VertexConsumerProvider vertices, DarkVortexSpell spell, Caster<?> caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) {
super.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch);
Camera camera = MinecraftClient.getInstance().gameRenderer.getCamera();
Entity cameraEntity = MinecraftClient.getInstance().getCameraEntity();
Vec3d ray = camera.getPos().subtract(spell.getOrigin(caster));
float radius = (float)spell.getEventHorizonRadius();
float absDistance = (float)cameraEntity.getEyePos().distanceTo(caster.getOriginVector().add(0, 2, 0));
float absDistance = (float)ray.length();
matrices.push();
matrices.translate(0, 2 + radius, 0);
matrices.translate(0, spell.getYOffset(), 0);
matrices.multiply(RotationAxis.NEGATIVE_Y.rotationDegrees(-caster.asEntity().getYaw()));
SphereModel.SPHERE.render(matrices, vertices.getBuffer(RenderLayers.getSolid()), light, 1, Math.min(radius * 0.6F, absDistance * 0.1F), 0, 0, 0, 1);
float visualRadius = Math.min(radius * 0.8F, absDistance - 1F);
SphereModel.SPHERE.render(matrices, vertices.getBuffer(RenderLayers.getSolid()), light, 1, visualRadius, 0, 0, 0, 1);
SphereModel.SPHERE.render(matrices, vertices.getBuffer(RenderLayers.getMagicColored()), light, 1, visualRadius + 0.05F, 0, 0, 0, 0.9F);
SphereModel.SPHERE.render(matrices, vertices.getBuffer(RenderLayers.getMagicColored()), light, 1, visualRadius + 0.1F, 0, 0, 0, 0.9F);
SphereModel.SPHERE.render(matrices, vertices.getBuffer(RenderLayers.getMagicColored()), light, 1, visualRadius + 0.15F, 0, 0, 0, 0.9F);
matrices.push();
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(90));
matrices.multiply(RotationAxis.NEGATIVE_X.rotationDegrees(90 + cameraEntity.getYaw(tickDelta)));
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-cameraEntity.getPitch(tickDelta)));
matrices.scale(0.7F, 1, 1);
float distance = 1F / MathHelper.clamp((absDistance / (radius * 4)), 0.0000001F, 1);
float distance = 1F / MathHelper.clamp(absDistance / (radius + 7), 0.0000001F, 1);
distance *= distance;
if (absDistance < radius * 4) {
cameraDistortion += distance;
}
matrices.scale(distance, distance, distance);
if (absDistance > radius) {
matrices.push();
matrices.translate(0, -0.1F, 0);
for (int i = 0; i < 10; i++) {
matrices.scale(1, 1, 0.796F);
float brightness = i / 10F;
SphereModel.DISK.render(matrices, vertices.getBuffer(RenderLayers.getMagicNoColor()), light, 1, radius * (1 + (0.25F * i)) * 0.7F, brightness, brightness, brightness, 0.2F);
}
matrices.pop();
}
SphereModel.DISK.render(matrices, vertices.getBuffer(RenderLayers.getEndPortal()), light, 1, radius * 0.5F, 1, 0.5F, 0, 1);
SphereModel.DISK.render(matrices, vertices.getBuffer(RenderLayers.getEndPortal()), light, 1, radius * 0.5F, 0, 0, 0, 0);
if (radius > 0.3F && absDistance > radius) {
double g = Math.sqrt(ray.x * ray.x + ray.z * ray.z);
float pitch = MathHelper.wrapDegrees((float)(-(MathHelper.atan2(ray.y, g) * 180.0F / (float)Math.PI)));
float yaw = MathHelper.wrapDegrees((float)(MathHelper.atan2(ray.z, ray.x) * 180.0F / (float)Math.PI) - 90.0F);
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(caster.asEntity().getYaw()));
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-yaw));
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-pitch));
radius *= Math.min(2, 3 + radius);
matrices.scale(radius, radius, radius);
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90));
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(animationProgress * 168));
float processionSpeed = animationProgress * 0.02F;
float maxProcessionAngle = 15;
float cosProcession = MathHelper.cos(processionSpeed);
float sinProcession = MathHelper.sin(processionSpeed);
float range = (float)spell.getDrawDropOffRange() / 8F;
matrices.scale(range, range, range);
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(cosProcession * maxProcessionAngle));
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(sinProcession * maxProcessionAngle));
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(animationProgress * 18));
VertexConsumer buffer = vertices.getBuffer(RenderLayer.getEntityTranslucent(ACCRETION_DISK_TEXTURE));
PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, 1, 1, 1, 1);
matrices.push();
matrices.scale(0.5F, 0.5F, 0.5F);
float secondaryScale = 0.9F + cosProcession * 0.3F;
matrices.translate(0, 0, 0.0001F);
matrices.scale(secondaryScale, secondaryScale, secondaryScale);
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(33));
PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, 1, 1, 1, 1);
matrices.pop();
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(45));
matrices.translate(0, 0, 0.0001F);
matrices.scale(0.9F, 0.9F, 0.9F);
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(33));
PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, 1, 1, 1, 1);
}
matrices.pop();

View file

@ -14,6 +14,9 @@ import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.entity.mob.CastSpellEntity;
import com.minelittlepony.unicopia.entity.player.Pony;
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer.TextLayerType;
@ -91,6 +94,9 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader
if (client.getEntityRenderDispatcher().shouldRenderHitboxes()
&& !client.hasReducedDebugInfo()
&& !(caster.asEntity() == client.cameraEntity && client.options.getPerspective() == Perspective.FIRST_PERSON)) {
if (!(caster instanceof Pony || caster instanceof CastSpellEntity)) {
return;
}
renderHotspot(matrices, vertices, caster, animationProgress);
renderSpellDebugInfo(matrices, vertices, caster, light);
}

View file

@ -21,7 +21,7 @@ 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 final SuggestionProvider<ServerCommandSource> ALLOWED_RACE_SUGGESTIONS = suggestFromRegistry(Race.REGISTRY_KEY, (context, race) -> race.availability().isGrantable() && 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) -> {
@ -43,7 +43,7 @@ public class UCommandSuggestion {
}
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;
final boolean hasNamespaceDelimiter = input.indexOf(':') > -1;
for (T object : candidates) {
final Identifier id = idFunc.apply(object);
if (hasNamespaceDelimiter) {

View file

@ -28,6 +28,13 @@ public final class ModelOverrides {
this.model = model;
}
public <T> ModelOverrides addUniform(String key, Iterable<T> values, Function<T, Float> idFunc, Function<T, Identifier> childModelSupplier) {
for (T t : values) {
addOverride(childModelSupplier.apply(t), key, idFunc.apply(t));
}
return this;
}
public ModelOverrides addUniform(String key, int from, int to, Identifier model) {
float step = 1F / to;
for (int index = from; index <= to; index++) {

View file

@ -1,12 +1,14 @@
package com.minelittlepony.unicopia.datagen.providers;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.block.UBlocks;
import com.minelittlepony.unicopia.datagen.DataCollector;
import com.minelittlepony.unicopia.item.BedsheetsItem;
import com.minelittlepony.unicopia.item.GemstoneItem;
import com.minelittlepony.unicopia.item.UItems;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider;
@ -137,8 +139,7 @@ public class UModelProvider extends FabricModelProvider {
// gemstone
ModelOverrides.of(ItemModels.GENERATED)
.addOverride(ModelIds.getItemSubModelId(UItems.GEMSTONE, "_pure"), "affinity", 0)
.addOverride(ModelIds.getItemSubModelId(UItems.GEMSTONE, "_corrupted"), "affinity", 1)
.addUniform("shape", List.of(GemstoneItem.Shape.values()), GemstoneItem.Shape::getId, shape -> ModelIds.getItemSubModelId(UItems.GEMSTONE, "_" + shape.name().toLowerCase(Locale.ROOT)))
.upload(UItems.GEMSTONE, itemModelGenerator);
// fishing rod

View file

@ -44,7 +44,11 @@ public class UBlockTagProvider extends FabricTagProvider.BlockTagProvider {
UBlocks.GOLDEN_OAK_SPROUT
};
getOrCreateTagBuilder(UTags.Blocks.CATAPULT_IMMUNE).add(Blocks.BEDROCK).forceAddTag(BlockTags.DOORS).forceAddTag(BlockTags.TRAPDOORS);
getOrCreateTagBuilder(UTags.Blocks.CATAPULT_IMMUNE).add(
Blocks.STRUCTURE_VOID, Blocks.STRUCTURE_BLOCK,
Blocks.COMMAND_BLOCK, Blocks.CHAIN_COMMAND_BLOCK, Blocks.REPEATING_COMMAND_BLOCK,
Blocks.LIGHT, Blocks.JIGSAW, Blocks.BARRIER, Blocks.BEDROCK
).forceAddTag(BlockTags.DOORS).forceAddTag(BlockTags.TRAPDOORS);
getOrCreateTagBuilder(UTags.Blocks.BUTTERFLIES_SPAWNABLE_ON).forceAddTag(BlockTags.ANIMALS_SPAWNABLE_ON).forceAddTag(BlockTags.LEAVES).forceAddTag(BlockTags.FLOWERS).forceAddTag(BlockTags.FLOWER_POTS);
getOrCreateTagBuilder(UTags.Blocks.JARS).add(UBlocks.JAR, UBlocks.CLOUD_JAR, UBlocks.STORM_JAR, UBlocks.LIGHTNING_JAR, UBlocks.ZAP_JAR);
getOrCreateTagBuilder(BlockTags.CROPS).add(crops);

View file

@ -22,7 +22,7 @@ import net.minecraft.world.World;
/**
* An indirect reference to an entity by its unique id.
* Used to store the 'owner' reference for certain objects that allows them to\
* Used to store the 'owner' reference for certain objects that allows them to
* remember who they belong to even when the entity has been unloaded.
*
* Will also remember the position and certain attributes of the owner.

View file

@ -11,7 +11,6 @@ import java.util.stream.Collectors;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.item.ButterflyItem;
import com.minelittlepony.unicopia.util.NbtSerialisable;
@ -74,7 +73,7 @@ public class ButterflyEntity extends AmbientEntity {
}
public static boolean canSpawn(EntityType<? extends ButterflyEntity> type, WorldAccess world, SpawnReason spawnReason, BlockPos pos, Random random) {
return world.getBlockState(pos.down()).isIn(UTags.Blocks.BUTTERFLIES_SPAWNABLE_ON);
return true;//world.getBlockState(pos.down()).isIn(UTags.Blocks.BUTTERFLIES_SPAWNABLE_ON);
}
@Override

View file

@ -27,7 +27,8 @@ import net.minecraft.world.Heightmap.Type;
public interface UEntities {
EntityType<ButterflyEntity> BUTTERFLY = register("butterfly", FabricEntityTypeBuilder.createMob().spawnGroup(SpawnGroup.AMBIENT).entityFactory(ButterflyEntity::new)
.spawnRestriction(Location.NO_RESTRICTIONS, Type.WORLD_SURFACE_WG, ButterflyEntity::canSpawn)
.spawnRestriction(Location.NO_RESTRICTIONS, Type.MOTION_BLOCKING_NO_LEAVES, ButterflyEntity::canSpawn)
.spawnableFarFromPlayer()
.dimensions(EntityDimensions.fixed(0.25F, 0.25F)));
EntityType<MagicProjectileEntity> THROWN_ITEM = register("thrown_item", FabricEntityTypeBuilder.<MagicProjectileEntity>create(SpawnGroup.MISC, MagicProjectileEntity::new)
.trackRangeBlocks(100)
@ -84,7 +85,7 @@ public interface UEntities {
.trackRangeChunks(8)
.dimensions(EntityDimensions.fixed(3, 2)));
EntityType<SpecterEntity> SPECTER = register("specter", FabricEntityTypeBuilder.createMob().spawnGroup(SpawnGroup.MONSTER).entityFactory(SpecterEntity::new)
.spawnRestriction(Location.ON_GROUND, Type.WORLD_SURFACE, HostileEntity::canSpawnInDark)
.spawnRestriction(Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnIgnoreLightLevel)
.fireImmune()
.spawnableFarFromPlayer()
.dimensions(EntityDimensions.fixed(1, 2)));

View file

@ -1,7 +1,10 @@
package com.minelittlepony.unicopia.entity.player;
import java.util.Optional;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.*;
import com.minelittlepony.unicopia.ability.Abilities;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
@ -40,7 +43,6 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.BlockStateParticleEffect;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.registry.RegistryKeys;
@ -82,7 +84,9 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
private int wallHitCooldown;
private Vec3d lastPos = Vec3d.ZERO;
@Nullable
private DimensionType lastDimension;
private Optional<Vec3d> lastPos = Optional.empty();
private Vec3d lastVel = Vec3d.ZERO;
private final PlayerDimensions dimensions;
@ -248,8 +252,14 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
ticksToGlide--;
}
lastVel = entity.getPos().subtract(lastPos);
lastPos = entity.getPos();
DimensionType dimension = entity.getWorld().getDimension();
if (dimension != lastDimension) {
lastDimension = dimension;
lastPos = Optional.empty();
}
lastVel = lastPos.map(entity.getPos()::subtract).orElse(Vec3d.ZERO);
lastPos = Optional.of(entity.getPos());
final MutableVector velocity = new MutableVector(entity.getVelocity());
@ -547,7 +557,7 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
private void checkAvianTakeoffConditions(MutableVector velocity) {
double horMotion = getHorizontalMotion();
double motion = entity.getPos().subtract(lastPos).lengthSquared();
double motion = lastVel.lengthSquared();
boolean takeOffCondition =
(horMotion > 0.05 || motion > 0.05)
@ -581,10 +591,10 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
entity.calculateDimensions();
if (entity.isOnGround() || !force) {
BlockState steppingState = pony.asEntity().getSteppingBlockState();
if (steppingState.isIn(UTags.Blocks.KICKS_UP_DUST)) {
//BlockState steppingState = pony.asEntity().getSteppingBlockState();
/*if (steppingState.isIn(UTags.Blocks.KICKS_UP_DUST)) {
pony.addParticle(new BlockStateParticleEffect(UParticles.DUST_CLOUD, steppingState), pony.getOrigin().toCenterPos(), Vec3d.ZERO);
} else {
} else*/ {
Supplier<Vec3d> pos = VecHelper.sphere(pony.asWorld().getRandom(), 0.5D);
Supplier<Vec3d> vel = VecHelper.sphere(pony.asWorld().getRandom(), 0.015D);
pony.spawnParticles(ParticleTypes.CLOUD, pos, vel, 5);
@ -716,7 +726,7 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
}
private void applyTurbulance(MutableVector velocity) {
int globalEffectStrength = MathHelper.clamp(entity.getWorld().getGameRules().getInt(UGameRules.WEATHER_EFFECTS_STRENGTH), 0, 100);
int globalEffectStrength = entity.getWorld().getGameRules().getInt(UGameRules.WEATHER_EFFECTS_STRENGTH);
float effectStrength = Math.min(1, (float)ticksInAir / MAX_TICKS_TO_WEATHER_EFFECTS) * (globalEffectStrength / 100F);
Vec3d gust = WeatherConditions.getGustStrength(entity.getWorld(), entity.getBlockPos())
.multiply(globalEffectStrength / 100D)

View file

@ -535,15 +535,17 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
return;
}
if (animationDuration > 0 && --animationDuration <= 0) {
if (animationDuration <= 0 || --animationDuration <= 0) {
if (animation.renderBothArms() && acrobatics.distanceClimbed > 0) {
return;
}
if (!getAnimation().isOf(Animation.NONE)) {
setAnimation(AnimationInstance.NONE);
}
}
}
private void updateBatPonyAbilities() {
if (ticksSunImmunity > 0) {

View file

@ -4,8 +4,10 @@ import java.util.Optional;
import net.minecraft.advancement.criterion.Criteria;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUsage;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.stat.Stats;
import net.minecraft.util.UseAction;
@ -29,9 +31,18 @@ public class ConsumableItem extends Item {
serverPlayerEntity.incrementStat(Stats.USED.getOrCreateStat(this));
}
if (stack.isEmpty()) {
return stack.isEmpty() ? Optional.ofNullable(getRecipeRemainder()).map(Item::getDefaultStack).orElse(ItemStack.EMPTY) : stack;
}
if (user instanceof PlayerEntity player) {
return Optional.ofNullable(getRecipeRemainder()).map(Item::getDefaultStack).map(remainder -> {
return ItemUsage.exchangeStack(stack, player, remainder);
}).orElse(stack);
}
return stack;
}
@Override
public UseAction getUseAction(ItemStack stack) {
return action;

View file

@ -1,12 +1,10 @@
package com.minelittlepony.unicopia.item;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.function.Predicate;
import org.jetbrains.annotations.Nullable;
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;
@ -96,11 +94,12 @@ public class GemstoneItem extends Item implements MultiItem, EnchantableItem {
@Override
public List<ItemStack> getDefaultStacks() {
return Arrays.stream(Affinity.VALUES)
.flatMap(i -> SpellType.byAffinity(i).stream()
.filter(type -> type.isObtainable())
.map(type -> EnchantableItem.enchant(getDefaultStack(), type, i))
return SpellType.REGISTRY.stream()
.filter(SpellType::isObtainable)
.sorted(
Comparator.<SpellType<?>, GemstoneItem.Shape>comparing(SpellType::getGemShape).thenComparing(Comparator.comparing(SpellType::getAffinity))
)
.map(type -> EnchantableItem.enchant(getDefaultStack(), type))
.toList();
}
@ -121,4 +120,29 @@ public class GemstoneItem extends Item implements MultiItem, EnchantableItem {
return super.getName();
}
public enum Shape {
ARROW,
BRUSH,
CROSS,
DONUT,
FLAME,
ICE,
LAMBDA,
RING,
ROCKET,
ROUND,
SHIELD,
SKULL,
SPLINT,
STAR,
TRIANGLE,
VORTEX,
WAVE;
public static final int LENGTH = values().length;
public float getId() {
return ordinal() / (float)LENGTH;
}
}
}

View file

@ -111,7 +111,7 @@ public interface UItems {
Item TOM = register("tom", new BluntWeaponItem(new Item.Settings(), ImmutableMultimap.of(
EntityAttributes.GENERIC_KNOCKBACK_RESISTANCE, new EntityAttributeModifier(BluntWeaponItem.KNOCKBACK_MODIFIER_ID, "Weapon modifier", 0.9, EntityAttributeModifier.Operation.ADDITION)
)), ItemGroups.NATURAL);
Item ROCK_STEW = register("rock_stew", new StewItem(new Item.Settings().food(FoodComponents.MUSHROOM_STEW).recipeRemainder(Items.BOWL)), ItemGroups.FOOD_AND_DRINK);
Item ROCK_STEW = register("rock_stew", new StewItem(new Item.Settings().food(FoodComponents.MUSHROOM_STEW).maxCount(1).recipeRemainder(Items.BOWL)), ItemGroups.FOOD_AND_DRINK);
Item ROCK_CANDY = register("rock_candy", new Item(new Item.Settings().food(UFoodComponents.CANDY).maxCount(16)), ItemGroups.FOOD_AND_DRINK);
Item SALT_CUBE = register("salt_cube", new Item(new Item.Settings().food(UFoodComponents.SALT_CUBE)), ItemGroups.FOOD_AND_DRINK);
@ -235,7 +235,7 @@ public interface UItems {
Item COOKED_TROPICAL_FISH = register("cooked_tropical_fish", new Item(new Item.Settings().food(FoodComponents.COOKED_COD)), ItemGroups.FOOD_AND_DRINK);
Item COOKED_PUFFERFISH = register("cooked_pufferfish", new Item(new Item.Settings().food(FoodComponents.COOKED_COD)), ItemGroups.FOOD_AND_DRINK);
Item FRIED_AXOLOTL = register("fried_axolotl", new ConsumableItem(new Item.Settings().food(FoodComponents.COOKED_CHICKEN).recipeRemainder(Items.BUCKET), UseAction.EAT), ItemGroups.FOOD_AND_DRINK);
Item FRIED_AXOLOTL = register("fried_axolotl", new ConsumableItem(new Item.Settings().food(FoodComponents.COOKED_CHICKEN).maxCount(1).recipeRemainder(Items.BUCKET), UseAction.EAT), ItemGroups.FOOD_AND_DRINK);
Item GREEN_FRIED_EGG = register("green_fried_egg", new Item(new Item.Settings().food(UFoodComponents.FRIED_EGG)), ItemGroups.FOOD_AND_DRINK);
Item CARAPACE = register("carapace", new Item(new Item.Settings()), ItemGroups.INGREDIENTS);

View file

@ -211,7 +211,7 @@ public class Ether extends PersistentState {
}
public boolean isAvailable() {
return !isDead() && !taken;
return !isDead() && !taken && entity.isSet();
}
public void setTaken(boolean taken) {
@ -219,6 +219,18 @@ public class Ether extends PersistentState {
markDirty();
}
public void release() {
setTaken(false);
}
public boolean claim() {
if (isAvailable()) {
setTaken(true);
return true;
}
return false;
}
@Nullable
public T getSpell() {
if (removed) {

View file

@ -1,15 +1,18 @@
package com.minelittlepony.unicopia.server.world;
import net.fabricmc.fabric.api.gamerule.v1.GameRuleFactory;
import net.fabricmc.fabric.api.gamerule.v1.GameRuleRegistry;
import net.minecraft.world.GameRules;
import net.minecraft.world.GameRules.BooleanRule;
import net.minecraft.world.GameRules.IntRule;
public interface UGameRules {
GameRules.Key<BooleanRule> SWAP_TRIBE_ON_DEATH = GameRules.register("swapTribeOnDeath", GameRules.Category.SPAWNING, BooleanRule.create(false));
GameRules.Key<BooleanRule> ANNOUNCE_TRIBE_JOINS = GameRules.register("announceTribeJoins", GameRules.Category.SPAWNING, BooleanRule.create(false));
GameRules.Key<BooleanRule> DO_NOCTURNAL_BAT_PONIES = GameRules.register("doNocturnalBatPonies", GameRules.Category.PLAYER, BooleanRule.create(true));
GameRules.Key<IntRule> WEATHER_EFFECTS_STRENGTH = GameRules.register("weatherEffectsStrength", GameRules.Category.MISC, IntRule.create(100));
GameRules.Key<BooleanRule> DO_TIME_MAGIC = GameRules.register("doTimeMagic", GameRules.Category.PLAYER, BooleanRule.create(true));
GameRules.Key<BooleanRule> SWAP_TRIBE_ON_DEATH = GameRuleRegistry.register("swapTribeOnDeath", GameRules.Category.SPAWNING, GameRuleFactory.createBooleanRule(false));
GameRules.Key<BooleanRule> ANNOUNCE_TRIBE_JOINS = GameRuleRegistry.register("announceTribeJoins", GameRules.Category.SPAWNING, GameRuleFactory.createBooleanRule(false));
GameRules.Key<BooleanRule> DO_NOCTURNAL_BAT_PONIES = GameRuleRegistry.register("doNocturnalBatPonies", GameRules.Category.PLAYER, GameRuleFactory.createBooleanRule(true));
GameRules.Key<IntRule> WEATHER_EFFECTS_STRENGTH = GameRuleRegistry.register("weatherEffectsStrength", GameRules.Category.MISC, GameRuleFactory.createIntRule(100, 0, 100));
GameRules.Key<BooleanRule> DO_TIME_MAGIC = GameRuleRegistry.register("doTimeMagic", GameRules.Category.PLAYER, GameRuleFactory.createBooleanRule(true));
GameRules.Key<IntRule> MAX_DARK_VORTEX_SIZE = GameRuleRegistry.register("maxDarkVortexSize", GameRules.Category.PLAYER, GameRuleFactory.createIntRule(20, 1, 25));
static void bootstrap() { }
}

View file

@ -544,7 +544,7 @@
"spell.unicopia.shield.lore": "Casts a protective shield around the user",
"spell.unicopia.bubble": "Bubble",
"spell.unicopia.bubble.lore": "Traps any creature it hits in a soap bubble",
"spell.unicopia.arcane_protection": "Arcane Protections",
"spell.unicopia.arcane_protection": "Arcane Protection",
"spell.unicopia.arcane_protection.lore": "Creates a protective shroud over an area in which no other spells can be cast",
"spell.unicopia.vortex": "Arcane Attraction",
"spell.unicopia.vortex.lore": "Creates a magnetic force that pulls in other targets",
@ -1329,6 +1329,9 @@
"key.unicopia.secondary": "Secondary Ability",
"key.unicopia.tertiary": "Tertiary Ability",
"key.unicopia.passive": "Passive Ability",
"key.unicopia.ability_modifier_tap": "VR Ability Modifier (1-TAP)",
"key.unicopia.ability_modifier_double_tap": "VR Ability Modifier (2-TAP)",
"key.unicopia.ability_modifier_triple_tap": "VR Ability Modifier (3-TAP)",
"key.unicopia.hud_page_dn": "Hud Previous Page",
"key.unicopia.hud_page_up": "Hud Next Page",
@ -1408,6 +1411,7 @@
"commands.gravity.set.multiple": "Updated %s entities",
"unicopia.options.title": "Unicopia Options",
"unicopia.options.toggle_ability_keys": "Sticky Ability Keys",
"unicopia.options.ignore_mine_lp": "Ignore Mine Little Pony",
"unicopia.options.ignore_mine_lp.missing": "* Mine Little Pony is not installed",
"unicopia.options.ignore_mine_lp.detected": "* Your detected race is %s",
@ -1427,28 +1431,28 @@
"command.unicopia.config.list": "[Config] Property (%s) contains (%s) entries: ",
"command.unicopia.config.clear": "[Config] Cleared all values from property %s",
"unicopia.race.unset": "Unset",
"unicopia.race.unset.alt": "Unset",
"unicopia.race.human": "Human",
"unicopia.race.human.alt": "Humans",
"unicopia.race.earth": "Earth Pony",
"unicopia.race.earth.alt": "Earth Ponies",
"unicopia.race.unicorn": "Unicorn",
"unicopia.race.unicorn.alt": "Unicorns",
"unicopia.race.pegasus": "Pegasus",
"unicopia.race.pegasus.alt": "Pegasi",
"unicopia.race.alicorn": "Alicorn",
"unicopia.race.alicorn.alt": "Alicorns",
"unicopia.race.changeling": "Changeling",
"unicopia.race.changeling.alt": "Changelings",
"unicopia.race.bat": "Bat Pony",
"unicopia.race.bat.alt": "Bat Ponies",
"unicopia.race.kirin": "Kirin",
"unicopia.race.kirin.alt": "Kirins",
"unicopia.race.hippogriff": "Hippogriff",
"unicopia.race.hippogriff.alt": "Hippogriffs",
"unicopia.race.seapony": "Sea Pony",
"unicopia.race.seapony.alt": "Sea Ponies",
"race.unicopia.unset": "Unset",
"race.unicopia.unset.alt": "Unset",
"race.unicopia.human": "Human",
"race.unicopia.human.alt": "Humans",
"race.unicopia.earth": "Earth Pony",
"race.unicopia.earth.alt": "Earth Ponies",
"race.unicopia.unicorn": "Unicorn",
"race.unicopia.unicorn.alt": "Unicorns",
"race.unicopia.pegasus": "Pegasus",
"race.unicopia.pegasus.alt": "Pegasi",
"race.unicopia.alicorn": "Alicorn",
"race.unicopia.alicorn.alt": "Alicorns",
"race.unicopia.changeling": "Changeling",
"race.unicopia.changeling.alt": "Changelings",
"race.unicopia.bat": "Bat Pony",
"race.unicopia.bat.alt": "Bat Ponies",
"race.unicopia.kirin": "Kirin",
"race.unicopia.kirin.alt": "Kirins",
"race.unicopia.hippogriff": "Hippogriff",
"race.unicopia.hippogriff.alt": "Hippogriffs",
"race.unicopia.seapony": "Sea Pony",
"race.unicopia.seapony.alt": "Sea Ponies",
"death.attack.unicopia.generic.and_also": "%1$s and %2$s",
"death.attack.unicopia.generic.whilst_flying": "%1$s whilst flying",

View file

@ -74,6 +74,14 @@
"item.unicopia.cooked_zap_apple": "Печёное зап-яблоко",
"item.unicopia.zap_apple": "Зап-яблоко",
"item.unicopia.zap_bulb": "Недозрелое зап-яблоко",
"item.unicopia.rotten_cod": "Гнилая треска",
"item.unicopia.rotten_salmon": "Гнилой лосось",
"item.unicopia.rotten_tropical_fish": "Гнилая тропическая рыба",
"item.unicopia.rotten_pufferfish": "Гнилой иглобрюх",
"item.unicopia.cooked_tropical_fish": "Приготовленная тропическая рыба",
"item.unicopia.cooked_pufferfish": "Приготовленный иглобрюх",
"item.unicopia.fried_axolotl": "Жареный аксолотль",
"item.unicopia.green_fried_egg": "Жареное зелёное яйцо",
"item.unicopia.love_bottle": "Бутылочка любви",
"item.unicopia.love_bucket": "Ведро любви",
@ -81,11 +89,16 @@
"item.unicopia.plunder_vine": "Чёрная лоза",
"item.unicopia.empty_jar": "Стеклянная банка",
"block.unicopia.jar": "Стеклянная банка",
"item.unicopia.filled_jar": "%s в банке",
"item.unicopia.rain_cloud_jar": "Дождь в банке",
"item.unicopia.cloud_jar": "Дождь в банке",
"item.unicopia.storm_cloud_jar": "Буря в банке",
"block.unicopia.storm_jar": "Буря в банке",
"item.unicopia.lightning_jar": "Молния в банке",
"block.unicopia.lightning_jar": "Молния в банке",
"item.unicopia.zap_apple_jam_jar": "Джем из зап-яблока",
"block.unicopia.zap_jar": "Банка зап-яблочного джема",
"item.unicopia.toast": "Тост",
"item.unicopia.burned_toast": "Подгоревший тост",
@ -145,7 +158,13 @@
"item.unicopia.crispy_hay_fries": "Хрустящий картофель фри",
"item.unicopia.horse_shoe_fries": "Подкова из картофеля фри",
"item.unicopia.wheat_worms": "Пшеничные черви",
"item.unicopia.baited_fishing_rod": "Удочка с приманкой",
"item.unicopia.muffin": "Маффин",
"item.unicopia.scone": "Булочка",
"item.unicopia.oatmeal_cookie": "Овсяное печенье",
"item.unicopia.chocolate_oatmeal_cookie": "Шоколадное овсяное печенье",
"item.unicopia.pinecone_cookie": "Печенье из шишек",
"item.unicopia.bowl_of_nuts": "Миска с орехами",
"item.unicopia.pegasus_amulet": "Крылья Икара",
"item.unicopia.pegasus_amulet.lore": "Дарует временный полёт тому, кто носит его",
@ -197,6 +216,7 @@
"item.unicopia.music_disc_funk.desc": "Фанк, просто фанк",
"item.unicopia.cloud_lump": "Облачный ком",
"item.unicopia.white_bed_sheets": "Белая простынь",
"item.unicopia.light_gray_bed_sheets": "Светло-серая простынь",
"item.unicopia.gray_bed_sheets": "Серая простынь",
"item.unicopia.black_bed_sheets": "Черная простынь",
@ -279,6 +299,7 @@
"block.unicopia.golden_oak_leaves": "Листья золотого дуба",
"block.unicopia.golden_oak_log": "Бревно золотого дуба",
"block.unicopia.mango": "Манго",
"block.unicopia.worm_block": "Блок червей",
"block.unicopia.mango_leaves": "Листья мангового дерева",
"block.unicopia.mango_sapling": "Саженец мангового дерева",
"block.unicopia.potted_mango_sapling": "Саженец манго в горшке",
@ -353,7 +374,8 @@
"block.unicopia.oats_crown": "Овёс",
"entity.unicopia.butterfly": "Бабочка",
"entity.unicopia.twittermite": "Твиттермиты",
"entity.unicopia.twittermite": "Твиттермит",
"entity.unicopia.specter": "Призрак",
"entity.unicopia.cast_spell": "Заклинание",
"entity.unicopia.cast_spell.by": "Заклинание, наложенное %s",
"entity.unicopia.spellbook": "Книга заклинаний",
@ -605,42 +627,43 @@
"unicopia.diet.hunger": "Коэффициент голода: %s%%",
"unicopia.diet.saturation": "Коэффициент насыщения: %s%%",
"tag.unicopia.food_types.rotten_meat": "Гниющее мясо",
"tag.unicopia.food_types.raw_meat": "Свежее мясо",
"tag.unicopia.food_types.cooked_meat": "Готовое мясо",
"tag.unicopia.food_types.raw_fish": "Свежая рыба",
"tag.unicopia.food_types.cooked_fish": "Готовая рыба",
"tag.unicopia.food_types.raw_insect": "Жуки и насекомые",
"tag.unicopia.food_types.cooked_insect": "Приготовленные жуки и насекомые",
"tag.unicopia.food_types.nuts_and_seeds": "Орехи и семена",
"tag.unicopia.food_types.love": "Любовь",
"tag.unicopia.food_types.rocks": "Камни",
"tag.unicopia.food_types.pinecone": "Орехи и семена",
"tag.unicopia.food_types.bat_ponys_delight": "Лакомства бэтпони",
"tag.unicopia.food_types.cooked_sea_vegitables": "Готовая рыбная еда",
"tag.unicopia.food_types.raw_sea_vegitables": "Свежая рыбная еда",
"tag.unicopia.food_types.shells": "Морские ракушки",
"tag.unicopia.food_types.shelly": "Морские ракушки",
"tag.unicopia.food_types.candy": "Конфеты",
"tag.unicopia.food_types.desserts": "Десерты",
"tag.unicopia.food_types.fruit": "Фрукты",
"tag.unicopia.food_types.baked_goods": "Выпечка",
"tag.unicopia.food_types.misc": "Прочее",
"tag.unicopia.food_types.fruits_and_vegetables": "Фрукты и овощи",
"tag.unicopia.food_types.drinks": "Напитки",
"tag.minecraft.leaves": "Листья",
"tag.unicopia.food_types.forage_edible_filling": "Крупная растительная масса",
"tag.unicopia.food_types.forage_edible": "Растительная масса",
"tag.unicopia.food_types.forage_nauseating": "Тошнотворное",
"tag.unicopia.food_types.forage_prickly": "Колючее",
"tag.unicopia.food_types.forage_risky": "Небезопасное",
"tag.unicopia.food_types.forage_strengthening": "Повышающее силу",
"tag.unicopia.food_types.forage_severely_prickly": "Очень колючее",
"tag.unicopia.food_types.forage_severely_nauseating": "Отвратительное",
"tag.unicopia.food_types.forage_radioactive": "Светящееся",
"tag.unicopia.food_types.forage_dangerous": "Опасное",
"tag.unicopia.food_types.forage_blinding": "Токсичное",
"food_group.unicopia.meat.rotten": "Гниющее мясо",
"food_group.unicopia.meat.raw": "Свежее мясо",
"food_group.unicopia.meat.cooked": "Готовое мясо",
"food_group.unicopia.fish.rotten": "Гнилая рыба",
"food_group.unicopia.fish.raw": "Свежая рыба",
"food_group.unicopia.fish.cooked": "Готовая рыба",
"food_group.unicopia.insect.rotten": "Гнилые жуки и насекомые",
"food_group.unicopia.insect.raw": "Жуки и насекомые",
"food_group.unicopia.insect.cooked": "Приготовленные жуки и насекомые",
"food_group.unicopia.nuts_and_seeds": "Орехи и семена",
"food_group.unicopia.love": "Любовь",
"food_group.unicopia.rocks": "Камни",
"food_group.unicopia.pinecone": "Орехи и семена",
"food_group.unicopia.bat_ponys_delight": "Лакомства бэтпони",
"food_group.unicopia.sea_vegetable.cooked": "Подготовленные ракушки и кораллы",
"food_group.unicopia.sea_vegetable.raw": "Ракушки и кораллы",
"food_group.unicopia.shells": "Морские ракушки",
"food_group.unicopia.special_shells": "Компаньоны",
"food_group.unicopia.candy": "Конфеты",
"food_group.unicopia.desserts": "Десерты",
"food_group.unicopia.fruit": "Фрукты",
"food_group.unicopia.baked_goods": "Выпечка",
"food_group.unicopia.misc": "Прочее",
"food_group.unicopia.fruits_and_vegetables": "Фрукты и овощи",
"food_group.unicopia.drinks": "Напитки",
"food_group.unicopia.foraging.edible_filling": "Крупная растительная масса",
"food_group.unicopia.foraging.edible": "Растительная масса",
"food_group.unicopia.foraging.nauseating": "Тошнотворное",
"food_group.unicopia.foraging.prickly": "Колючее",
"food_group.unicopia.foraging.risky": "Небезопасное",
"food_group.unicopia.foraging.strengthening": "Повышающее силу",
"food_group.unicopia.foraging.severely_prickly": "Очень колючее",
"food_group.unicopia.foraging.severely_nauseating": "Отвратительное",
"food_group.unicopia.foraging.radioactive": "Светящееся",
"food_group.unicopia.foraging.dangerous": "Опасное",
"food_group.unicopia.foraging.blinding": "Токсичное",
"food_group.unicopia.foraging.leafy_greens": "Листовая зелень",
"toxicity.safe.name": "Безопасное",
"toxicity.mild.name": "Слаботоксичное",
@ -1306,6 +1329,9 @@
"key.unicopia.secondary": "Вторичная способность",
"key.unicopia.tertiary": "Третичная способность",
"key.unicopia.passive": "Пассивная способность",
"key.unicopia.ability_modifier_tap": "Модификатор способностей VR (1-TAP)",
"key.unicopia.ability_modifier_double_tap": "Модификатор способностей VR (2-TAP)",
"key.unicopia.ability_modifier_triple_tap": "Модификатор способностей VR (3-TAP)",
"key.unicopia.hud_page_dn": "Предыдущая страница",
"key.unicopia.hud_page_up": "Следующая страница",
@ -1332,6 +1358,8 @@
"enchantment.unicopia.heart_bound.desc": "Заставляет предмет оставаться с вами после смерти",
"enchantment.unicopia.consumption": "Потребление",
"enchantment.unicopia.consumption.desc": "Преобразует предметы, добытые с помощью инструмента, в опыт",
"enchantment.unicopia.feather_touch": "Касание пера",
"enchantment.unicopia.feather_touch.desc": "Позволяет ломать и размещать облачные блоки при держании",
"commands.race.success.self": "Изменена раса на %1$s.",
"commands.race.success": "%1$s изменил расу на %2$s.",
@ -1380,8 +1408,10 @@
"commands.gravity.set": "Ваша гравитация была обновлена до %f",
"commands.gravity.set.self": "Установить собственную гравитацию на %f",
"commands.gravity.set.other": "Установить гравитацию %s на %f",
"commands.gravity.set.multiple": "Обновлено %s сущностей",
"unicopia.options.title": "Опции Unicopia",
"unicopia.options.toggle_ability_keys": "Нажатие кнопки способности вместо зажатия",
"unicopia.options.ignore_mine_lp": "Игнорировать Mine Little Pony",
"unicopia.options.ignore_mine_lp.missing": "* Mine Little Pony не установлен",
"unicopia.options.ignore_mine_lp.detected": "* Ваша обнаруженная раса %s",
@ -1401,28 +1431,28 @@
"command.unicopia.config.list": "[Config] Свойство (%s) содержит (%s) записей:",
"command.unicopia.config.clear": "[Config] Удалены все значения из свойства %s",
"unicopia.race.unset": "Не выбрано",
"unicopia.race.unset.alt": "не выбрано",
"unicopia.race.human": "Человек",
"unicopia.race.human.alt": "Люди",
"unicopia.race.earth": "Земной пони",
"unicopia.race.earth.alt": "Земные пони",
"unicopia.race.unicorn": "Единорог",
"unicopia.race.unicorn.alt": "Единороги",
"unicopia.race.pegasus": "Пегас",
"unicopia.race.pegasus.alt": "Пегасы",
"unicopia.race.alicorn": "Аликорн",
"unicopia.race.alicorn.alt": "Аликорны",
"unicopia.race.changeling": "Чейнджлинг",
"unicopia.race.changeling.alt": "Чейнджлинги",
"unicopia.race.bat": "Бэтпони",
"unicopia.race.bat.alt": "Бэтпони",
"unicopia.race.kirin": "Кирин",
"unicopia.race.kirin.alt": "Кирины",
"unicopia.race.hippogriff": "Гиппогриф",
"unicopia.race.hippogriff.alt": "Гиппогрифы",
"unicopia.race.seapony": "Морской пони",
"unicopia.race.seapony.alt": "Морские пони",
"race.unicopia.unset": "Не выбрано",
"race.unicopia.unset.alt": "не выбрано",
"race.unicopia.human": "Человек",
"race.unicopia.human.alt": "Люди",
"race.unicopia.earth": "Земной пони",
"race.unicopia.earth.alt": "Земные пони",
"race.unicopia.unicorn": "Единорог",
"race.unicopia.unicorn.alt": "Единороги",
"race.unicopia.pegasus": "Пегас",
"race.unicopia.pegasus.alt": "Пегасы",
"race.unicopia.alicorn": "Аликорн",
"race.unicopia.alicorn.alt": "Аликорны",
"race.unicopia.changeling": "Чейнджлинг",
"race.unicopia.changeling.alt": "Чейнджлинги",
"race.unicopia.bat": "Бэтпони",
"race.unicopia.bat.alt": "Бэтпони",
"race.unicopia.kirin": "Кирин",
"race.unicopia.kirin.alt": "Кирины",
"race.unicopia.hippogriff": "Гиппогриф",
"race.unicopia.hippogriff.alt": "Гиппогрифы",
"race.unicopia.seapony": "Морской пони",
"race.unicopia.seapony.alt": "Морские пони",
"death.attack.unicopia.generic.and_also": "%1$s и %2$s",
"death.attack.unicopia.generic.whilst_flying": "%1$s во время полёта",
@ -1592,13 +1622,23 @@
"advancements.unicopia.praise_the_sun.title": "Хвала Солнцу!",
"advancements.unicopia.praise_the_sun.description": "Испытайте безудержную славу Селестии",
"advancements.unicopia.cool_potato.title": "Крутая картошка",
"advancements.unicopia.cool_potato.description": "Защитите глаза от солнца",
"advancements.unicopia.cool_potato.description": "Защитите свои глаза от солнца",
"advancements.unicopia.take_a_note.title": "Записывай, Спайк",
"advancements.unicopia.take_a_note.description": "Получите свиток дыхания дракона",
"advancements.unicopia.dear_princess.title": "Дорогая принцесса...",
"advancements.unicopia.dear_princess.description": "Отправьте письмо с помощью свитка дыхания дракона",
"advancements.unicopia.i_await_your_reply.title": "Я жду твоего ответа",
"advancements.unicopia.i_await_your_reply.description": "Используйте свиток дыхания дракона, чтобы отправить кому-нибудь свиток дыхания дракона",
"advancements.unicopia.baked_bads.title": "Запечённые хлебцы",
"advancements.unicopia.baked_bads.description": "Испеките вкусный маффин",
"advancements.unicopia.mid_flight_interruption.title": "Побеспокоили в середине полёта",
"advancements.unicopia.mid_flight_interruption.description": "Получите удар молнии во время полёта в грозу",
"advancements.unicopia.lightning_bug.title": "Ошибка молнии",
"advancements.unicopia.lightning_bug.description": "Привлеките 10 ударов молний",
"advancements.unicopia.lightning_bug.title": "Баг молнии",
"advancements.unicopia.lightning_bug.description": "Привлеките 10 ударов молнии за чейнджлинга",
"advancements.unicopia.wonder_bolt.title": "Вондерболт",
"advancements.unicopia.wonder_bolt.description": "Привлеките 10 ударов молнии",
"advancements.unicopia.bait.title": "Это приманка?",
"advancements.unicopia.bait.description": "Насадите несколько червей на крючок",
"advancements.unicopia.jar.title": "О, ничего себе. Что это?",
"advancements.unicopia.jar.description": "Найдите пустую банку",
"advancements.unicopia.gotcha.title": "Попался!",
@ -1606,11 +1646,17 @@
"advancements.unicopia.trick_apple.title": "Яблоко раздора",
"advancements.unicopia.trick_apple.description": "Найдите своё первое зап-яблоко",
"advancements.unicopia.feed_trick_apple.title": "Вот, попробуй это",
"advancements.unicopia.feed_trick_apple.description": "Скорми зап-яблоко мобу",
"advancements.unicopia.feed_trick_apple.description": "Скормите зап-яблоко мобу",
"advancements.unicopia.eat_trick_apple.title": "Хрустящий",
"advancements.unicopia.eat_trick_apple.description": "Кусните зап-яблоко",
"advancements.unicopia.eat_pinecone.title": "Отчаяние",
"advancements.unicopia.eat_pinecone.description": "Съешьте шишку",
"advancements.unicopia.tastes_like_chicken.title": "На вкус как курица",
"advancements.unicopia.tastes_like_chicken.description": "Зажарьте и съешьте аксолотля",
"advancements.unicopia.what_the_hay.title": "Какого сена?",
"advancements.unicopia.what_the_hay.description": "Съешьте целый блок сена",
"advancements.unicopia.oats_so_easy.title": "Овёс - это просто",
"advancements.unicopia.oats_so_easy.description": "Вырастите немного овса",
"advancements.unicopia.imported_oats.title": "Столь же вкусные, сколь и дорогие",
"advancements.unicopia.imported_oats.description": "Отправьте или получите шикарный импортный овёс",
@ -1640,11 +1686,15 @@
"advancements.unicopia.sweet_apple_acres.description": "Получите по одному яблоку каждого сорта",
"advancements.unicopia.brew_cider.title": "Лучшее от Эпплджек",
"advancements.unicopia.brew_cider.description": "Сварить сидр",
"advancements.unicopia.basket_case.title": "Дело о корзине",
"advancements.unicopia.basket_case.description": "Сплетите корзину",
"advancements.unicopia.aeronaut.title": "Аэронавт",
"advancements.unicopia.aeronaut.description": "Оснастите свою корзину фонарём и воздушным шаром",
"advancements.unicopia.travelling_in_style.title": "Путешествие со стилем",
"advancements.unicopia.travelling_in_style.description": "Прокатитесь на воздушном шаре",
"advancements.unicopia.night_route.title": "Дети Ночи",
"advancements.unicopia.night_route.description": "Пойдите по пути ночи",
"advancements.unicopia.bat_route.title": "Дети Ночи",
"advancements.unicopia.bat_route.description": "Пойдите по пути ночи",
"advancements.unicopia.screech_twenty_mobs.title": "Ужас с неба",
"advancements.unicopia.screech_twenty_mobs.description": "Обрушьте ужас по меньшей мере на 20 мобов одновременно",
"advancements.unicopia.screech_self.title": "Божечки!",
@ -1657,10 +1707,18 @@
"advancements.unicopia.blasphemy.title": "Кощунство!",
"advancements.unicopia.blasphemy.description": "Ударьте Селестию по башке. Упс!",
"advancements.unicopia.earth_route.title": "Путь Пони",
"advancements.unicopia.earth_route.title": "Очаг Земли",
"advancements.unicopia.earth_route.description": "Вступить в клан Яблока",
"advancements.unicopia.sticks_and_stones.title": "Палки и камни",
"advancements.unicopia.sticks_and_stones.description": "Убейте моба, бросая в него камни",
"advancements.unicopia.blacksmith.title": "Кузнец",
"advancements.unicopia.blacksmith.description": "Сделайте подкову",
"advancements.unicopia.change_of_shoes.title": "Переобулся",
"advancements.unicopia.change_of_shoes.description": "Сделайте железную подкову",
"advancements.unicopia.fashionably_expensive.title": "Модно и дорого",
"advancements.unicopia.fashionably_expensive.description": "Обновитесь до золотых подков",
"advancements.unicopia.overkill.title": "Оверкилл",
"advancements.unicopia.overkill.description": "Создайте незеритовую подкову",
"advancements.unicopia.dead_ringer.title": "Звон смерти",
"advancements.unicopia.dead_ringer.description": "Убейте моба подковой",
"advancements.unicopia.born_on_a_rock_farm.title": "Рождённый на Ферме Камней",
@ -1668,14 +1726,32 @@
"advancements.unicopia.thats_unusual.title": "Это необычно",
"advancements.unicopia.thats_unusual.description": "Но что оно делает?",
"advancements.unicopia.sky_route.title": "Путь Пегаса",
"advancements.unicopia.sky_route.description": "Присоединяйтесь к пегасам Клаудсдейла",
"advancements.unicopia.pegasus_route.title": "Путь Пегаса",
"advancements.unicopia.pegasus_route.description": "Присоединитесь к пегасам Клаудсдейла",
"advancements.unicopia.molting_season_1.title": "Сезон линьки",
"advancements.unicopia.molting_season_1.description": "Сбросьте перо во время полёта",
"advancements.unicopia.molting_season_1.description": "Сбросьте своё первое перо во время полёта",
"advancements.unicopia.molting_season_2.title": "Сезон линьки 2",
"advancements.unicopia.molting_season_2.description": "Сбросьте 5 перьев во время полёта",
"advancements.unicopia.molting_season_2.description": "Сбросьте своё второе перо во время полёта",
"advancements.unicopia.molting_season_3.title": "Сезон линьки 3",
"advancements.unicopia.molting_season_3.description": "Сбросьте 15 перьев во время полёта",
"advancements.unicopia.molting_season_3.description": "Сбросьте своё четвёртое перо во время полёта",
"advancements.unicopia.molting_season_4.title": "Сезон линьки 4",
"advancements.unicopia.molting_season_4.description": "Сбросьте своё восьмое перо во время полёта",
"advancements.unicopia.molting_season_5.title": "Сезон линьки 5",
"advancements.unicopia.molting_season_5.description": "Сбросьте своё шестнадцатое перо во время полёта",
"advancements.unicopia.molting_season_6.title": "Сезон линьки 6",
"advancements.unicopia.molting_season_6.description": "Сбросьте своё тридцать второе перо во время полёта",
"advancements.unicopia.molting_season_7.title": "Сезон линьки 7",
"advancements.unicopia.molting_season_7.description": "Сбросьте своё шестьдесят четвёртое перо во время полёта",
"advancements.unicopia.molting_season_8.title": "Сезон линьки 8",
"advancements.unicopia.molting_season_8.description": "Сбросьте своё сто двадцать восьмое перо во время полёта",
"advancements.unicopia.molting_season_9.title": "Сезон линьки 9",
"advancements.unicopia.molting_season_9.description": "Сбросьте своё двести пятьдесят шестое перо во время полёта",
"advancements.unicopia.molting_season_10.title": "Сезон линьки 10",
"advancements.unicopia.molting_season_10.description": "Сбросьте своё пятьсот двенадцатое перо во время полёта",
"advancements.unicopia.molting_season_11.title": "Сезон линьки 11",
"advancements.unicopia.molting_season_11.description": "Сбросьте своё тысяча двадцать четвёртое перо во время полёта",
"advancements.unicopia.dedicated_flier.title": "Всё в порядке, теперь ты можешь остановиться",
"advancements.unicopia.dedicated_flier.description": "Сбросьте своё две тысячи сорок восьмое перо во время полёта",
"advancements.unicopia.rainbow_crash.title": "Дискорд тебя побери, Рэйнбоу",
"advancements.unicopia.rainbow_crash.description": "Устройте войну против злой нации стеклянных окон",
"advancements.unicopia.second_wind.title": "Второе дыхание",
@ -1683,11 +1759,13 @@
"advancements.unicopia.deter_phantom.title": "Что летает вокруг",
"advancements.unicopia.deter_phantom.description": "Поднимитесь и дайте этим фантомам попробовать их собственное лекарство",
"advancements.unicopia.magical_route.title": "Рог единорога",
"advancements.unicopia.magical_route.description": "Окунитесь в мир блеска и радуги",
"advancements.unicopia.unicorn_route.title": "Рог единорога",
"advancements.unicopia.unicorn_route.description": "Окунитесь в мир блеска и радуги",
"advancements.unicopia.books.title": "Книги!",
"advancements.unicopia.books.description": "Это МОЯ книга заклинаний, и я собираюсь её ПРОЧИТАТЬ!",
"advancements.unicopia.books_books_books.title": "Книги! Книги! Книги!",
"advancements.unicopia.books_books_books.description": "Заполните инвентарь книгами",
"advancements.unicopia.tempted.title": "Заманчиво...",
"advancements.unicopia.tempted.description": "Наденьте амулет аликорна",
"advancements.unicopia.hello_darkness_my_old_friend.title": "Здравствуй, Тьма...",
@ -1710,6 +1788,13 @@
"advancements.unicopia.love_is_power.title": "Любовь - это сила",
"advancements.unicopia.love_is_power.description": "Изгоните короля Сомбра с помощью кристального сердца",
"advancements.unicopia.hippogriff_route.title": "Всплеск Сиквестрии",
"advancements.unicopia.hippogriff_route.description": "Присоединитесь к гнезду гиппогрифов",
"advancements.unicopia.shoo_be_doo.title": "Шу-би-ду!",
"advancements.unicopia.shoo_be_doo.description": "Используйте жемчужное ожерелье, чтобы превратиться в морское существо",
"advancements.unicopia.shoo_be_done.title": "Шу-би-всё!",
"advancements.unicopia.shoo_be_done.description": "Используйте жемчужное ожерелье, чтобы стать снова собой",
"unicopia.toast.discoveries.title": "Новые открытия!",
"unicopia.toast.discoveries.description": "Проверьте свою книгу заклинаний"
}

View file

@ -197,6 +197,7 @@
"item.unicopia.music_disc_funk.desc": "funk, just funk",
"item.unicopia.cloud_lump": "云团",
"item.unicopia.white_bed_sheets": "白色被单",
"item.unicopia.light_gray_bed_sheets": "淡灰色被单",
"item.unicopia.gray_bed_sheets": "灰色被单",
"item.unicopia.black_bed_sheets": "黑色被单",
@ -228,7 +229,7 @@
"block.unicopia.spectral_fire": "节律火",
"block.unicopia.bananas": "香蕉",
"block.unicopia.zapling": "魔虹苹果树苗",
"block.unicopia.potted_zapling": "盆中 魔虹苹果树苗",
"block.unicopia.potted_zapling": "魔虹苹果树苗盆栽",
"block.unicopia.zap_log": "魔虹苹果木原木",
"block.unicopia.zap_wood": "魔虹苹果木",
"block.unicopia.stripped_zap_log": "去皮魔虹苹果木原木",
@ -252,7 +253,7 @@
"block.unicopia.zap_apple": "魔虹苹果",
"block.unicopia.zap_bulb": "没熟的魔虹苹果",
"block.unicopia.palm_sapling": "棕榈树苗",
"block.unicopia.potted_palm_sapling": "盆中 棕榈树苗",
"block.unicopia.potted_palm_sapling": "棕榈树苗盆栽",
"block.unicopia.palm_log": "棕榈木原木",
"block.unicopia.palm_wood": "棕榈木",
"block.unicopia.palm_planks": "棕榈木板",
@ -275,13 +276,13 @@
"block.unicopia.gold_root": "黄金根",
"block.unicopia.golden_oak_sprout": "金橡树嫩芽",
"block.unicopia.golden_oak_sapling": "金橡树树苗",
"block.unicopia.potted_golden_oak_sapling": "盆中 金橡树树苗",
"block.unicopia.potted_golden_oak_sapling": "金橡树树苗盆栽",
"block.unicopia.golden_oak_leaves": "金橡树树叶",
"block.unicopia.golden_oak_log": "金橡树原木",
"block.unicopia.mango": "芒果",
"block.unicopia.mango_leaves": "芒果树叶",
"block.unicopia.mango_sapling": "芒果树苗",
"block.unicopia.potted_mango_sapling": "盆中 芒果树苗",
"block.unicopia.potted_mango_sapling": "芒果树苗盆栽",
"block.unicopia.pineapple": "菠萝树",
"block.unicopia.clam_shell": "蛤蜊壳",
@ -290,15 +291,15 @@
"block.unicopia.green_apple_leaves": "史密斯婆婆苹果树树叶",
"block.unicopia.green_apple_sapling": "史密斯婆婆苹果树树苗",
"block.unicopia.potted_green_apple_sapling": "盆中 史密斯婆婆苹果树树苗",
"block.unicopia.potted_green_apple_sapling": "史密斯婆婆苹果树树苗盆栽",
"block.unicopia.green_apple_sprout": "史密斯婆婆苹果嫩芽",
"block.unicopia.sweet_apple_leaves": "甜苹果树树叶",
"block.unicopia.sweet_apple_sapling": "甜苹果树树苗",
"block.unicopia.potted_sweet_apple_sapling": "盆中 甜苹果树树苗",
"block.unicopia.potted_sweet_apple_sapling": "甜苹果树树苗盆栽",
"block.unicopia.sweet_apple_sprout": "甜苹果树嫩芽",
"block.unicopia.sour_apple_leaves": "酸苹果树树叶",
"block.unicopia.sour_apple_sapling": "酸苹果树树苗",
"block.unicopia.potted_sour_apple_sapling": "盆中 酸苹果树树苗",
"block.unicopia.potted_sour_apple_sapling": "酸苹果树树苗盆栽",
"block.unicopia.sour_apple_sprout": "酸苹果树嫩芽",
"block.unicopia.surface_chitin": "几丁质表面",
@ -612,6 +613,7 @@
"tag.unicopia.food_types.cooked_fish": "烤好的鱼",
"tag.unicopia.food_types.raw_insect": "虫类",
"tag.unicopia.food_types.cooked_insect": "烤虫类",
"tag.unicopia.food_types.nuts_and_seeds": "坚果和种子",
"tag.unicopia.food_types.love": "爱",
"tag.unicopia.food_types.rocks": "石块",
"tag.unicopia.food_types.pinecone": "坚果和种子",
@ -1547,6 +1549,7 @@
"unicopia.subtitle.pegasus.molt": "天马:脱羽",
"unicopia.subtitle.unicorn.teleport": "魔法:啵",
"unicopia.subtitle.player.wololo": "Wololo",
"unicopia.subtitle.corrupt": "魔法:堕落",
"unicopia.subtitle.entity.player.whistle": "玩家吹口哨",
"unicopia.subtitle.entity.player.kick": "玩家:踢腿",
"unicopia.subtitle.magic_aura": "魔法:嗡嗡",
@ -1591,12 +1594,18 @@
"advancements.unicopia.praise_the_sun.description": "感受太阳公主的无上熔光",
"advancements.unicopia.cool_potato.title": "酷毙了",
"advancements.unicopia.cool_potato.description": "保护你的眼睛不受阳光侵袭",
"advancements.unicopia.take_a_note.title": "穗龙,把这个记下来",
"advancements.unicopia.take_a_note.description": "获得龙息卷轴",
"advancements.unicopia.dear_princess.title": "亲爱的公主殿下……",
"advancements.unicopia.dear_princess.description": "用龙息卷轴送出一份信",
"advancements.unicopia.baked_bads.title": "烤砸了",
"advancements.unicopia.baked_bads.description": "烤制美味马芬",
"advancements.unicopia.baked_bads.description": "烤制美味芬",
"advancements.unicopia.mid_flight_interruption.title": "风雷混合双打",
"advancements.unicopia.mid_flight_interruption.description": "在风暴中飞行时被雷劈到",
"advancements.unicopia.lightning_bug.title": "引火虫",
"advancements.unicopia.lightning_bug.description": "引得十次雷劈",
"advancements.unicopia.lightning_bug.description": "作为幻形灵,引得十次雷劈",
"advancements.unicopia.wonder_bolt.title": "闪电天马",
"advancements.unicopia.wonder_bolt.description": "引得十次雷劈",
"advancements.unicopia.jar.title": "哇哦,这是什么?",
"advancements.unicopia.jar.description": "找到一个空罐子",
"advancements.unicopia.gotcha.title": "抓到你了!",
@ -1669,15 +1678,33 @@
"advancements.unicopia.sky_route.title": "飞彳亍",
"advancements.unicopia.sky_route.description": "入驻云中城",
"advancements.unicopia.molting_season_1.title": "换羽季节I",
"advancements.unicopia.molting_season_1.description": "飞行时掉下根羽毛",
"advancements.unicopia.molting_season_1.description": "飞行时掉下第1根羽毛",
"advancements.unicopia.molting_season_2.title": "换羽季节II",
"advancements.unicopia.molting_season_2.description": "飞行时掉下根羽毛",
"advancements.unicopia.molting_season_2.description": "飞行时掉下第2根根羽毛",
"advancements.unicopia.molting_season_3.title": "换羽季节III",
"advancements.unicopia.molting_season_3.description": "飞行时掉下十五根羽毛",
"advancements.unicopia.molting_season_3.description": "飞行时掉下第4根羽毛",
"advancements.unicopia.molting_season_4.title": "换羽季节IV",
"advancements.unicopia.molting_season_4.description": "飞行时掉下第8根羽毛",
"advancements.unicopia.molting_season_5.title": "换羽季节V",
"advancements.unicopia.molting_season_5.description": "飞行时掉下第16根羽毛",
"advancements.unicopia.molting_season_6.title": "换羽季节VI",
"advancements.unicopia.molting_season_6.description": "飞行时掉下第32根羽毛",
"advancements.unicopia.molting_season_7.title": "换羽季节VII",
"advancements.unicopia.molting_season_7.description": "飞行时掉下第64根羽毛",
"advancements.unicopia.molting_season_8.title": "换羽季节VIII",
"advancements.unicopia.molting_season_8.description": "飞行时掉下第128根羽毛",
"advancements.unicopia.molting_season_9.title": "换羽季节IX",
"advancements.unicopia.molting_season_9.description": "飞行时掉下第256根羽毛",
"advancements.unicopia.molting_season_10.title": "换羽季节X",
"advancements.unicopia.molting_season_10.description": "飞行时掉下第512根羽毛",
"advancements.unicopia.molting_season_11.title": "换羽季节XI",
"advancements.unicopia.molting_season_11.description": "飞行时掉下第1024根羽毛",
"advancements.unicopia.dedicated_flier.title": "谢谢,够了",
"advancements.unicopia.dedicated_flier.description": "飞行时掉下第2048根羽毛",
"advancements.unicopia.rainbow_crash.title": "去你的彩虹",
"advancements.unicopia.rainbow_crash.description": "向邪恶的玻璃国宣战",
"advancements.unicopia.second_wind.title": "来劲了",
"advancements.unicopia.second_wind.description": "竭力飞行",
"advancements.unicopia.second_wind.description": "难受啊马飞",
"advancements.unicopia.deter_phantom.title": "神仙斗法",
"advancements.unicopia.deter_phantom.description": "马飞飞,让那些修仙的幻翼永远安眠吧",

View file

@ -6,13 +6,13 @@
"sleep.not_possible.nocturnal": "您無法跳過這一天",
"sleep.skipping_day": "這一天將在睡夢中度過",
"ability.unicopia.empty_hooves": "我需要找到一個罐子",
"ability.unicopia.empty_hooves": "我需要一個罐子",
"ability.unicopia.indoors": "我在這裏看不到天空",
"ability.unicopia.too_low": "我要飛得更高",
"ability.unicopia.clear_skies": "天空看起來已經很晴朗了",
"ability.unicopia.too_calm.1": "我需要更加憤怒……",
"ability.unicopia.too_calm.2": "我並不感到憤怒……",
"ability.unicopia.too_calm.3": "李小龍這看着好輕鬆啊……",
"ability.unicopia.too_low": "我要飛得更高",
"ability.unicopia.clear_skies": "天空已經很晴朗了",
"ability.unicopia.too_calm.1": "我的怒火要更加旺盛一些……",
"ability.unicopia.too_calm.2": "我並不怎麼生氣……",
"ability.unicopia.too_calm.3": "李小龍麼做看着好輕鬆啊……",
"ability.unicopia.too_calm.4": "塞蕾斯蒂亞公主,請賜予我力量……",
"itemGroup.unicopia.items": "Unicopia",
@ -25,7 +25,7 @@
"itemGroup.unicopia.changeling": "Unicopia - 幻形峭壁",
"item.unicopia.friendship_bracelet": "友誼手鐲",
"item.unicopia.friendship_bracelet.issuer": "由 %s 簽名",
"item.unicopia.friendship_bracelet.issuer": "由%s簽名",
"item.unicopia.friendship_bracelet.glowing": "正在發光",
"item.unicopia.oak_basket": "橡木籃",
@ -49,6 +49,9 @@
"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": "獨角徽章",
@ -79,9 +82,9 @@
"item.unicopia.plunder_vine": "掠奪藤蔓",
"item.unicopia.empty_jar": "玻璃罐",
"item.unicopia.filled_jar": "%s罐",
"item.unicopia.rain_cloud_jar": "雨罐",
"item.unicopia.rain_cloud_jar": "雨罐",
"item.unicopia.storm_cloud_jar": "暴雨罐",
"item.unicopia.lightning_jar": "電罐",
"item.unicopia.lightning_jar": "電罐",
"item.unicopia.zap_apple_jam_jar": "彩虹蘋果醬罐",
"item.unicopia.toast": "烤麪包片",
@ -156,7 +159,7 @@
"item.unicopia.clam_shell": "蛤殼",
"item.unicopia.scallop_shell": "貝殼",
"item.unicopia.turret_shell": "塔殼",
"item.unicopia.shelly": "小殼",
"item.unicopia.shelly": "螺螺",
"item.unicopia.horse_shoe.accuracy": "精準度:%d%%",
"item.unicopia.horse_shoe.speed": "速度:%d",
@ -194,6 +197,7 @@
"item.unicopia.music_disc_funk.desc": "Death by Glamour",
"item.unicopia.cloud_lump": "雲塊",
"item.unicopia.white_bed_sheets": "白色被單",
"item.unicopia.light_gray_bed_sheets": "淡灰被單",
"item.unicopia.gray_bed_sheets": "灰色被單",
"item.unicopia.black_bed_sheets": "黑色被單",
@ -222,17 +226,34 @@
"block.unicopia.rocks": "岩石",
"block.unicopia.plunder_vine": "掠奪藤蔓",
"block.unicopia.plunder_vine_bud": "掠奪藤蔓芽",
"block.unicopia.spectral_fire": "光譜火焰",
"block.unicopia.bananas": "香蕉",
"block.unicopia.zapling": "彩虹蘋果苗",
"block.unicopia.potted_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": "彩虹蘋果",
"block.unicopia.zap_bulb": "生彩虹蘋果",
"block.unicopia.palm_sapling": "棕櫚苗",
"block.unicopia.potted_palm_sapling": "棕櫚苗盆栽",
"block.unicopia.palm_log": "棕櫚原木",
"block.unicopia.palm_wood": "棕櫚木塊",
"block.unicopia.palm_planks": "棕櫚木材",
@ -255,11 +276,13 @@
"block.unicopia.gold_root": "金橡樹根",
"block.unicopia.golden_oak_sprout": "金橡樹芽",
"block.unicopia.golden_oak_sapling": "金橡樹苗",
"block.unicopia.potted_golden_oak_sapling": "金橡樹苗盆栽",
"block.unicopia.golden_oak_leaves": "金橡樹葉",
"block.unicopia.golden_oak_log": "金橡樹原木",
"block.unicopia.mango": "芒果",
"block.unicopia.mango_leaves": "芒果葉",
"block.unicopia.mango_sapling": "芒果葉",
"block.unicopia.mango_sapling": "芒果苗",
"block.unicopia.potted_mango_sapling": "芒果苗盆栽",
"block.unicopia.pineapple": "鳳梨株",
"block.unicopia.clam_shell": "蛤殼",
@ -268,12 +291,15 @@
"block.unicopia.green_apple_leaves": "婆婆蘋果樹葉",
"block.unicopia.green_apple_sapling": "婆婆蘋果苗",
"block.unicopia.potted_green_apple_sapling": "婆婆蘋果苗盆栽",
"block.unicopia.green_apple_sprout": "婆婆蘋果芽",
"block.unicopia.sweet_apple_leaves": "香甜蘋果樹葉",
"block.unicopia.sweet_apple_sapling": "香甜蘋果苗",
"block.unicopia.potted_sweet_apple_sapling": "香甜蘋果苗盆栽",
"block.unicopia.sweet_apple_sprout": "香甜蘋果芽",
"block.unicopia.sour_apple_leaves": "酸蘋果樹葉",
"block.unicopia.sour_apple_sapling": "酸蘋果苗",
"block.unicopia.potted_sour_apple_sapling": "酸蘋果苗盆栽",
"block.unicopia.sour_apple_sprout": "酸蘋果芽",
"block.unicopia.surface_chitin": "表殼",
@ -575,8 +601,8 @@
"unicopia.diet.side_effects": "副作用:",
"unicopia.diet.not_edible": "物品不可食用",
"unicopia.diet.base_multiplier": "基礎倍增:%s%%",
"unicopia.diet.hunger.detailed": "獲得飢餓:%s of %s (%s%%)",
"unicopia.diet.saturation.detailed": "獲得飽食: %s (%s%%)",
"unicopia.diet.hunger.detailed": "獲得飢餓:%s分之%s (%s%%)",
"unicopia.diet.saturation.detailed": "獲得飽食: %s分之%s (%s%%)",
"unicopia.diet.hunger": "飢餓比:%s%%",
"unicopia.diet.saturation": "飽食比:%s%%",
@ -587,6 +613,7 @@
"tag.unicopia.food_types.cooked_fish": "熟魚",
"tag.unicopia.food_types.raw_insect": "蟲",
"tag.unicopia.food_types.cooked_insect": "熟蟲",
"tag.unicopia.food_types.nuts_and_seeds": "堅果與種子",
"tag.unicopia.food_types.love": "愛意",
"tag.unicopia.food_types.rocks": "岩石",
"tag.unicopia.food_types.pinecone": "堅果與種子",
@ -594,11 +621,15 @@
"tag.unicopia.food_types.cooked_sea_vegitables": "熟魚食",
"tag.unicopia.food_types.raw_sea_vegitables": "鮮魚食",
"tag.unicopia.food_types.shells": "海螺",
"tag.unicopia.food_types.shelly": "螺",
"tag.unicopia.food_types.shelly": "螺",
"tag.unicopia.food_types.candy": "糖果",
"tag.unicopia.food_types.desserts": "點心",
"tag.unicopia.food_types.fruit": "水果",
"tag.unicopia.food_types.baked_goods": "烘培",
"tag.unicopia.food_types.misc": "雜項",
"tag.unicopia.food_types.fruits_and_vegetables": "水果與蔬菜",
"tag.unicopia.food_types.drinks": "飲料",
"tag.minecraft.leaves": "葉類",
"tag.unicopia.food_types.forage_edible_filling": "大型植質",
"tag.unicopia.food_types.forage_edible": "植質",
@ -773,14 +804,14 @@
"gui.unicopia.tribe_selection.confirm.bads.3.unicopia.bat": " - Is sometimes scared of even themselves",
"gui.unicopia.tribe_selection.confirm.bads.4.unicopia.bat": " - Is carnivorous. Can eat raw and cooked meat, or sustain themselved purely on stolen love.",
"gui.unicopia.tribe_selection.confirm.bads.1.unicopia.changeling": " - Are always starving",
"gui.unicopia.tribe_selection.confirm.bads.2.unicopia.changeling": " - Requires love, collected from ponies or other hostile mobs to subsidise their diet",
"gui.unicopia.tribe_selection.confirm.bads.3.unicopia.changeling": " - Becomes sick from eating most regular food and must harvest love to hasten a cure",
"gui.unicopia.tribe_selection.confirm.bads.1.unicopia.changeling": " - 一直餓肚子",
"gui.unicopia.tribe_selection.confirm.bads.2.unicopia.changeling": " - 需要從其他小馬或敵對生物吸取愛意來填飽肚子",
"gui.unicopia.tribe_selection.confirm.bads.3.unicopia.changeling": " - 吃絕大多數普通食物會生病,必須收穫愛意來治癒",
"gui.unicopia.tribe_selection.confirm.bads.1.unicopia.kirin": " - Are no longer quiet whilst raging",
"gui.unicopia.tribe_selection.confirm.bads.2.unicopia.kirin": " - Has a tendency to burn things, especially when close to raging",
"gui.unicopia.tribe_selection.confirm.bads.3.unicopia.kirin": " - Lighter than other ponies, and might take increased knockback",
"gui.unicopia.tribe_selection.confirm.bads.4.unicopia.kirin": " - Doesn't like water",
"gui.unicopia.tribe_selection.confirm.bads.1.unicopia.kirin": " - 發火時不再安靜",
"gui.unicopia.tribe_selection.confirm.bads.2.unicopia.kirin": " - 傾向于燒東西,尤其是在發火時",
"gui.unicopia.tribe_selection.confirm.bads.3.unicopia.kirin": " - 比其他小馬輕盈,可能更容易被擊退",
"gui.unicopia.tribe_selection.confirm.bads.4.unicopia.kirin": " - 不喜歡水",
"gui.unicopia.tribe_selection.confirm.bads.1.unicopia.hippogriff": " - 易受蠻力攻擊",
"gui.unicopia.tribe_selection.confirm.bads.2.unicopia.hippogriff": " - 無法和雲交互",
@ -911,8 +942,6 @@
"gui.unicopia.spellbook.chapter.ice.p7.title": "Bonfire II",
"gui.unicopia.spellbook.chapter.ice.p7.1.body": "On the way back Luna was telling me of the stories her friend told her. The town has a lot of legends, as to be expected.",
"gui.unicopia.spellbook.chapter.ice.p7.2.body": "One of them was about a scary old warlock who lived in a haunted tower at the edge of town. There's no mystery who that was about.",
"gui.unicopia.spellbook.chapter.ice.p7.3.body": "Ice Spell II",
"gui.unicopia.spellbook.chapter.ice.p7.4.body": "Creates a cooling affect up to a radius of 3 hooves from any surfaces it touches.",
"gui.unicopia.spellbook.chapter.ice.p8.title": "6th Trot '12",
"gui.unicopia.spellbook.chapter.ice.p8.1.body": "There was a strange noise in the village last night. Very strange. I heard a lot ponies shouting and there may have been a fire.",
"gui.unicopia.spellbook.chapter.ice.p8.2.body": "I hope everything is okay.",
@ -1118,6 +1147,10 @@
"gui.unicopia.spellbook.chapter.artefacts.torn_page.2.body": "§kAasa sasa fwefsd q43rgfd wqklmsdfl as, klasn.§r",
"gui.unicopia.spellbook.chapter.artefacts.torn_page.3.body": "Building Materials:",
"gui.unicopia.spellbook.chapter.artefacts.crystal_podium.title": "Crystal Podium",
"gui.unicopia.spellbook.chapter.artefacts.altar.title": "Altar",
"gui.unicopia.spellbook.chapter.artefacts.altar.1.body": "An an§kc§rient altar con§ktr§ructed by an early t§kr§ribe of §kunicorn§rs. It's thought that these were used to perform r§kituals§r sum§kmoni§rng upon §keven o§rlder mag§kics§r.",
"gui.unicopia.spellbook.chapter.artefacts.altar.2.body": "Not much is §kkn§rown about these my§ks§rterious structures, and they hold many secrets even to this day.",
"gui.unicopia.spellbook.chapter.artefacts.altar.3.body": "U§ks§re the §ka§rltar",
"gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.2.body": "It's, um a scroll that you write somepony's name on it and you hold it in one hoof and something in the other hoof and, like, um it goes whooosh and the item is sent to that pony.",
"gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.title": "2nd Hoof '12",
"gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.3.body": "P.S. Uncle Starswirly is a dunderhead.",
@ -1126,6 +1159,9 @@
"gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.title": "13th Mare '12",
"gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.3.body": "Anyone wearing a bangle you have signed will be able to benefit from the positive effects of your spells, or will be allowed through protection and shield spells.",
"gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.4.body": "Mana costs are also shared equally between all nearby members.",
"gui.unicopia.spellbook.chapter.artefacts.spectral_clock.title": "14th Mare '12",
"gui.unicopia.spellbook.chapter.artefacts.spectral_clock.1.body": "Not so much an artefact as a strange trinket. Luna happened to bring this home from the market last week, and though at first glance it may seem to be an ordinary broken clock what I've found is far stranger.",
"gui.unicopia.spellbook.chapter.artefacts.spectral_clock.2.body": "This clock doesn't tell the time. Well, it does, but not directly. Rather it seems to be following the cycles of some of the plants in the surrounding forest.",
"gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.1.body": "Commander Hurricane informed me of this, though I've found little texts to back up his claims.",
"gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.2.body": "The Pegasus Amulet is claimed to grant the wearer temporary flight, like a pegasus.",
"gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.title": "21st Trot '12",
@ -1155,16 +1191,16 @@
"gui.unicopia.action.spells_cleared": "已移除所有魔咒",
"gui.unicopia.action.no_spells_cleared": "您沒有活躍的魔咒",
"chapter.unicopia.crafting": "Spell Crafting",
"chapter.unicopia.profile": "Profile",
"chapter.unicopia.traits": "Research",
"chapter.unicopia.introduction": "Introduction",
"chapter.unicopia.fire_magic": "Fire Magic",
"chapter.unicopia.ice_magic": "Ice Magic",
"chapter.unicopia.air_magic": "Air Magic",
"chapter.unicopia.dark_magic": "Dark Magic",
"chapter.unicopia.the_otherworldly": "Forbidden Magic",
"chapter.unicopia.crystal_heart": "Artifacts",
"chapter.unicopia.crafting": "咒語合成",
"chapter.unicopia.profile": "總概",
"chapter.unicopia.traits": "研究",
"chapter.unicopia.introduction": "介紹",
"chapter.unicopia.fire_magic": "火咒",
"chapter.unicopia.ice_magic": "冰咒",
"chapter.unicopia.air_magic": "氣咒",
"chapter.unicopia.dark_magic": "暗咒",
"chapter.unicopia.the_otherworldly": "禁咒",
"chapter.unicopia.crystal_heart": "神器",
"experience.unicopia.pure.magical_kindergartner": "New Blood",
"experience.unicopia.impure.magical_kindergartner": "Impure",
@ -1250,19 +1286,19 @@
"experience.unicopia.corrupt.alicorn_princess": "Shadowy Alicorn Lord",
"experience.unicopia.monstrous.alicorn_princess": "Dark Alicorn Lord",
"experience.unicopia.pure.polycorn_princess": "Polycorn Princess",
"experience.unicopia.impure.polycorn_princess": "Impure Polycorn Princess",
"experience.unicopia.tainted.polycorn_princess": "Tainted Polycorn Princess",
"experience.unicopia.twisted.polycorn_princess": "Fallen Polycorn Princess",
"experience.unicopia.corrupt.polycorn_princess": "Shadowy Polycorn King",
"experience.unicopia.monstrous.polycorn_princess": "Dark Polycorn King",
"experience.unicopia.pure.polycorn_princess": "多角公主",
"experience.unicopia.impure.polycorn_princess": "不潔多角公主",
"experience.unicopia.tainted.polycorn_princess": "髒污多角公主",
"experience.unicopia.twisted.polycorn_princess": "墮落多角公主",
"experience.unicopia.corrupt.polycorn_princess": "暗影多角王子",
"experience.unicopia.monstrous.polycorn_princess": "暗黑多角王子",
"experience.unicopia.pure.faustian_legend": "Hero of Legend",
"experience.unicopia.impure.faustian_legend": "Legendary",
"experience.unicopia.tainted.faustian_legend": "Storied Figure of Distant Descent",
"experience.unicopia.twisted.faustian_legend": "Dark Figure of Lore",
"experience.unicopia.corrupt.faustian_legend": "The Old God",
"experience.unicopia.monstrous.faustian_legend": "Otherworldly Terror",
"experience.unicopia.pure.faustian_legend": "史詩傳奇",
"experience.unicopia.impure.faustian_legend": "名垂青史",
"experience.unicopia.tainted.faustian_legend": "墮落傳說",
"experience.unicopia.twisted.faustian_legend": "暗黑傳說",
"experience.unicopia.corrupt.faustian_legend": "上古惡神",
"experience.unicopia.monstrous.faustian_legend": "異世魔王",
"unicopia.category.name": "小馬能力",
@ -1275,16 +1311,27 @@
"key.unicopia.hud_page_up": "介面下一頁",
"enchantment.unicopia.gem_finder": "尋礦",
"enchantment.unicopia.gem_finder.desc": "靠近高價值礦物時發出嗡嗡聲",
"enchantment.unicopia.padded": "衝擊保護",
"enchantment.unicopia.padded.desc": "撞牆時從牆上彈開,爲您飛行護航",
"enchantment.unicopia.clingy": "纏身",
"enchantment.unicopia.clingy.desc": "物品掉落後緊隨玩家",
"enchantment.unicopia.repulsion": "輕盈",
"enchantment.unicopia.repulsion.desc": "身輕如燕",
"enchantment.unicopia.heavy": "沉重",
"enchantment.unicopia.heavy.desc": "讓穿戴盔甲的有翼種族更沉重,更難被魔法和風推動",
"enchantment.unicopia.herds": "合擊",
"enchantment.unicopia.herds.desc": "盟友更多,力量更強",
"enchantment.unicopia.want_it_need_it": "爭奪詛咒",
"enchantment.unicopia.want_it_need_it.desc": "生物無法抵抗對帶此魔咒的物品的誘惑",
"enchantment.unicopia.poisoned_joke": "毒笑",
"enchantment.unicopia.poisoned_joke.desc": "使用者會不時幻聽",
"enchantment.unicopia.stressed": "重壓",
"enchantment.unicopia.stressed.desc": "危險時搖晃熒幕",
"enchantment.unicopia.heart_bound": "赤膽忠心",
"enchantment.unicopia.heart_bound.desc": "死後物品仍然陪伴著你",
"enchantment.unicopia.consumption": "經驗轉化",
"enchantment.unicopia.consumption.desc": "使用帶本魔咒的工具挖掘時,掉落物化爲經驗",
"commands.race.success.self": "將自己的種族設爲%1$s",
"commands.race.success": "%1$s將種族變爲%2$s",
@ -1303,16 +1350,23 @@
"commands.race.tell.other.alt": "%s是",
"commands.racelist.illegal": "默認的種族%s不能在此指令中使用。",
"commands.racelist.get.allowed": "已放行(%s",
"commands.racelist.get.not_allowed": "已攔截(%s",
"commands.racelist.get.list_item": "- %s%s",
"commands.racelist.reset.success": "已清除並禁用白名單。",
"commands.racelist.reset.fail": "白名單並未啓用。無更改。",
"commands.racelist.enabled": "已啓用白名單",
"commands.racelist.disabled": "已禁用白名單",
"commands.racelist.allowed": "已將%1$s加入白名單。",
"commands.racelist.allowed.failed": "%1$s已在白名單內。",
"commands.racelist.inactive": "白名單並未啓用。執行/unicopia racelist <allow|disallow> <種族> 指令以對其進行配置。",
"commands.racelist.disallowed": "將%1$s從白名單移除",
"commands.racelist.disallowed.failed": "%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": "移除了自己的僞裝。",
@ -1438,6 +1492,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忘記了自己能飛掉下了藤蔓",
@ -1492,6 +1548,7 @@
"unicopia.subtitle.pegasus.molt": "飛馬:掉落羽毛",
"unicopia.subtitle.unicorn.teleport": "魔法:傳送",
"unicopia.subtitle.player.wololo": "喔囉囉!",
"unicopia.subtitle.corrupt": "魔法:墮落",
"unicopia.subtitle.entity.player.whistle": "玩家:吹口哨",
"unicopia.subtitle.entity.player.kick": "玩家:踢",
"unicopia.subtitle.magic_aura": "魔法:低鳴",
@ -1544,13 +1601,13 @@
"advancements.unicopia.lightning_bug.description": "被雷劈十次",
"advancements.unicopia.jar.title": "哇,這是什麼?",
"advancements.unicopia.jar.description": "找到一個空罐子",
"advancements.unicopia.gotcha.title": "抓到",
"advancements.unicopia.gotcha.title": "抓到你了",
"advancements.unicopia.gotcha.description": "捕獲暴雨",
"advancements.unicopia.trick_apple.title": "無序林檎",
"advancements.unicopia.trick_apple.description": "找到你的第一個彩虹蘋果",
"advancements.unicopia.feed_trick_apple.title": "來,試試這個",
"advancements.unicopia.feed_trick_apple.title": "餐廳珍饈",
"advancements.unicopia.feed_trick_apple.description": "將彩虹蘋果喂給其他生物",
"advancements.unicopia.eat_trick_apple.title": "酥脆",
"advancements.unicopia.eat_trick_apple.title": "烤得酥脆",
"advancements.unicopia.eat_trick_apple.description": "吃下彩虹蘋果",
"advancements.unicopia.eat_pinecone.title": "走投無路",
"advancements.unicopia.eat_pinecone.description": "吃下松果",
@ -1569,7 +1626,7 @@
"advancements.unicopia.burn_juice.title": "不太對勁",
"advancements.unicopia.burn_juice.description": "將蘋果汁烤糊",
"advancements.unicopia.apple_route.title": "蘋果、蘋果、蘋果",
"advancements.unicopia.apple_route.title": "林檎傳說",
"advancements.unicopia.apple_route.description": "開啓你的蘋果傳奇遊記",
"advancements.unicopia.juice.title": "清爽",
"advancements.unicopia.juice.description": "這些蘋果終於有用了",
@ -1639,7 +1696,7 @@
"advancements.unicopia.a_falling_wizard.description": "一個施飛行咒失敗的獨角",
"advancements.unicopia.split_the_sea.title": "你叫什麼,摩西嗎?",
"advancements.unicopia.split_the_sea.description": "用魔法一次分開一百方塊以上的水",
"advancements.unicopia.split_the_sea.description": "用魔法一次分開一百方塊以上的水",
"advancements.unicopia.save_the_day.title": "拯救世界",
"advancements.unicopia.save_the_day.description": "擊敗黑晶王",
"advancements.unicopia.ascension.title": "原地昇天",

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -2,6 +2,7 @@
"replace": false,
"values": [
"unicopia:magical_exhaustion",
"unicopia:gravity_well_recoil",
"unicopia:alicorn_amulet",
"unicopia:zap",
"unicopia:kick",

View file

@ -6,9 +6,6 @@ accessible class net/minecraft/client/render/item/HeldItemRenderer$H
accessible class net/minecraft/client/render/VertexConsumers$Union
accessible class net/minecraft/client/gui/hud/InGameHud$HeartType
accessible method net/minecraft/world/GameRules register (Ljava/lang/String;Lnet/minecraft/world/GameRules$Category;Lnet/minecraft/world/GameRules$Type;)Lnet/minecraft/world/GameRules$Key;
accessible method net/minecraft/world/GameRules$BooleanRule create (Z)Lnet/minecraft/world/GameRules$Type;
accessible method net/minecraft/world/GameRules$IntRule create (I)Lnet/minecraft/world/GameRules$Type;
accessible method net/minecraft/world/gen/foliage/FoliagePlacerType <init> (Lcom/mojang/serialization/Codec;)V
accessible field net/minecraft/entity/mob/CreeperEntity CHARGED Lnet/minecraft/entity/data/TrackedData;