Added hippogriffs and seaponies

This commit is contained in:
Sollace 2023-11-08 12:58:31 +00:00
parent d91d358552
commit 0d0ea4558d
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
54 changed files with 792 additions and 168 deletions

View file

@ -0,0 +1,15 @@
package com.minelittlepony.unicopia;
public enum Availability {
DEFAULT,
COMMANDS,
NONE;
public boolean isSelectable() {
return this == DEFAULT;
}
public boolean isGrantable() {
return this != NONE;
}
}

View file

@ -27,6 +27,7 @@ public interface EquinePredicates {
Predicate<Entity> PLAYER_CHANGELING = IS_PLAYER.and(ofRace(Race.CHANGELING)); Predicate<Entity> PLAYER_CHANGELING = IS_PLAYER.and(ofRace(Race.CHANGELING));
Predicate<Entity> PLAYER_KIRIN = IS_PLAYER.and(ofRace(Race.KIRIN)); Predicate<Entity> PLAYER_KIRIN = IS_PLAYER.and(ofRace(Race.KIRIN));
Predicate<Entity> PLAYER_PEGASUS = IS_PLAYER.and(e -> ((PlayerEntity)e).getAbilities().creativeMode || RACE_INTERACT_WITH_CLOUDS.test(e)); Predicate<Entity> PLAYER_PEGASUS = IS_PLAYER.and(e -> ((PlayerEntity)e).getAbilities().creativeMode || RACE_INTERACT_WITH_CLOUDS.test(e));
Predicate<Entity> PLAYER_SEAPONY = IS_PLAYER.and(raceMatches(Race::isFish));
Predicate<Entity> PLAYER_CAN_USE_EARTH = IS_PLAYER.and(raceMatches(Race::canUseEarth)); Predicate<Entity> PLAYER_CAN_USE_EARTH = IS_PLAYER.and(raceMatches(Race::canUseEarth));
Predicate<Entity> IS_CASTER = e -> !e.isRemoved() && (e instanceof Caster || IS_PLAYER.test(e)); Predicate<Entity> IS_CASTER = e -> !e.isRemoved() && (e instanceof Caster || IS_PLAYER.test(e));

View file

@ -24,18 +24,18 @@ import net.minecraft.util.Identifier;
import net.minecraft.registry.Registry; import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryKey;
public record Race (Supplier<Composite> compositeSupplier, boolean canCast, FlightType flightType, boolean canUseEarth, boolean isNocturnal, boolean canHang) implements Affine { public record Race (Supplier<Composite> compositeSupplier, Availability availability, boolean canCast, FlightType flightType, boolean canUseEarth, boolean isNocturnal, boolean canHang) implements Affine {
public static final String DEFAULT_ID = "unicopia:unset"; 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> REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("race"), DEFAULT_ID);
public static final RegistryKey<? extends Registry<Race>> REGISTRY_KEY = REGISTRY.getKey(); 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("race.unknown", id));
public static Race register(String name, boolean magic, FlightType flight, boolean earth, boolean nocturnal, boolean canHang) { public static Race register(String name, Availability availability, boolean magic, FlightType flight, boolean earth, boolean nocturnal, boolean canHang) {
return register(Unicopia.id(name), magic, flight, earth, nocturnal, canHang); return register(Unicopia.id(name), availability, magic, flight, earth, nocturnal, canHang);
} }
public static Race register(Identifier id, boolean magic, FlightType flight, boolean earth, boolean nocturnal, boolean canHang) { public static Race register(Identifier id, Availability availability, boolean magic, FlightType flight, boolean earth, boolean nocturnal, boolean canHang) {
return Registry.register(REGISTRY, id, new Race(Suppliers.memoize(() -> new Composite(REGISTRY.get(id), null)), magic, flight, earth, nocturnal, canHang)); return Registry.register(REGISTRY, id, new Race(Suppliers.memoize(() -> new Composite(REGISTRY.get(id), null)), availability, magic, flight, earth, nocturnal, canHang));
} }
public static RegistryKeyArgumentType<Race> argument() { public static RegistryKeyArgumentType<Race> argument() {
@ -46,15 +46,17 @@ public record Race (Supplier<Composite> compositeSupplier, boolean canCast, Flig
* The default, unset race. * The default, unset race.
* This is used if there are no other races. * This is used if there are no other races.
*/ */
public static final Race UNSET = register("unset", false, FlightType.NONE, false, false, false); public static final Race UNSET = register("unset", Availability.COMMANDS, false, FlightType.NONE, false, false, false);
public static final Race HUMAN = register("human", 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", false, FlightType.NONE, true, false, false); public static final Race EARTH = register("earth", Availability.DEFAULT, false, FlightType.NONE, true, false, false);
public static final Race UNICORN = register("unicorn", true, FlightType.NONE, false, false, false); public static final Race UNICORN = register("unicorn", Availability.DEFAULT, true, FlightType.NONE, false, false, false);
public static final Race PEGASUS = register("pegasus", false, FlightType.AVIAN, 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", false, FlightType.AVIAN, false, true, true); public static final Race BAT = register("bat", Availability.DEFAULT, false, FlightType.AVIAN, false, true, true);
public static final Race ALICORN = register("alicorn", true, FlightType.AVIAN, true, false, false); public static final Race ALICORN = register("alicorn", Availability.COMMANDS, true, FlightType.AVIAN, true, false, false);
public static final Race CHANGELING = register("changeling", false, FlightType.INSECTOID, false, false, true); public static final Race CHANGELING = register("changeling", Availability.DEFAULT, false, FlightType.INSECTOID, false, false, true);
public static final Race KIRIN = register("kirin", true, FlightType.NONE, false, false, false); 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 void bootstrap() {} public static void bootstrap() {}
@ -83,6 +85,10 @@ public record Race (Supplier<Composite> compositeSupplier, boolean canCast, Flig
return !isHuman(); return !isHuman();
} }
public boolean isFish() {
return this == SEAPONY;
}
public boolean isHuman() { public boolean isHuman() {
return this == UNSET || this == HUMAN; return this == UNSET || this == HUMAN;
} }
@ -91,16 +97,12 @@ public record Race (Supplier<Composite> compositeSupplier, boolean canCast, Flig
return !isNocturnal(); return !isNocturnal();
} }
public boolean isOp() {
return this == ALICORN;
}
public boolean canFly() { public boolean canFly() {
return !flightType().isGrounded(); return !flightType().isGrounded();
} }
public boolean canInteractWithClouds() { public boolean canInteractWithClouds() {
return canFly() && this != CHANGELING && this != BAT; return canFly() && this != CHANGELING && this != BAT && this != HIPPOGRIFF;
} }
public Identifier getId() { public Identifier getId() {

View file

@ -14,6 +14,8 @@ public interface USounds {
SoundEvent ENTITY_PLAYER_CORRUPTION = PARTICLE_SOUL_ESCAPE; SoundEvent ENTITY_PLAYER_CORRUPTION = PARTICLE_SOUL_ESCAPE;
SoundEvent ENTITY_PLAYER_BATPONY_SCREECH = register("entity.player.batpony.screech"); SoundEvent ENTITY_PLAYER_BATPONY_SCREECH = register("entity.player.batpony.screech");
SoundEvent ENTITY_PLAYER_HIPPOGRIFF_SCREECH = register("entity.player.hippogriff.screech");
SoundEvent ENTITY_PLAYER_HIPPOGRIFF_PECK = ENTITY_CHICKEN_STEP;
SoundEvent ENTITY_PLAYER_REBOUND = register("entity.player.rebound"); SoundEvent ENTITY_PLAYER_REBOUND = register("entity.player.rebound");
SoundEvent ENTITY_PLAYER_PEGASUS_WINGSFLAP = register("entity.player.pegasus.wingsflap"); SoundEvent ENTITY_PLAYER_PEGASUS_WINGSFLAP = register("entity.player.pegasus.wingsflap");
SoundEvent ENTITY_PLAYER_PEGASUS_FLYING = register("entity.player.pegasus.flying"); SoundEvent ENTITY_PLAYER_PEGASUS_FLYING = register("entity.player.pegasus.flying");

View file

@ -42,9 +42,14 @@ public interface Abilities {
Ability<?> RAINBOOM = register(new PegasusRainboomAbility(), "rainboom", AbilitySlot.PRIMARY); Ability<?> RAINBOOM = register(new PegasusRainboomAbility(), "rainboom", AbilitySlot.PRIMARY);
Ability<?> CAPTURE_CLOUD = register(new PegasusCaptureStormAbility(), "capture_cloud", AbilitySlot.SECONDARY); Ability<?> CAPTURE_CLOUD = register(new PegasusCaptureStormAbility(), "capture_cloud", AbilitySlot.SECONDARY);
// pegasus / bat / alicorn / changeling // hippogriff
Ability<?> DASH = register(new FlyingDashAbility(), "dash", AbilitySlot.PRIMARY);
Ability<?> SCREECH = register(new ScreechAbility(), "screech", AbilitySlot.SECONDARY);
Ability<?> PECK = register(new PeckAbility(), "peck", AbilitySlot.SECONDARY);
// pegasus / bat / alicorn / changeling / hippogriff
Ability<?> CARRY = register(new CarryAbility(), "carry", AbilitySlot.PRIMARY); Ability<?> CARRY = register(new CarryAbility(), "carry", AbilitySlot.PRIMARY);
Ability<?> TOGGLE_FLIGHT = register(new PegasusFlightToggleAbility(), "toggle_flight", AbilitySlot.TERTIARY); Ability<?> TOGGLE_FLIGHT = register(new ToggleFlightAbility(), "toggle_flight", AbilitySlot.TERTIARY);
// changeling // changeling
Ability<?> DISGUISE = register(new ChangelingDisguiseAbility(), "disguise", AbilitySlot.SECONDARY); Ability<?> DISGUISE = register(new ChangelingDisguiseAbility(), "disguise", AbilitySlot.SECONDARY);

View file

@ -1,55 +1,20 @@
package com.minelittlepony.unicopia.ability; package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import com.minelittlepony.unicopia.AwaitTickQueue; import com.minelittlepony.unicopia.AwaitTickQueue;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.ability.data.Numeric;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.advancement.UCriteria; import com.minelittlepony.unicopia.advancement.UCriteria;
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.entity.damage.UDamageTypes; import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.util.RegistryUtils;
import com.minelittlepony.unicopia.util.VecHelper;
import net.minecraft.entity.LivingEntity;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.random.Random; import net.minecraft.util.math.random.Random;
import net.minecraft.world.event.GameEvent;
/** /**
* A magic casting ability for unicorns. * An ability to screeeeeeeeEeEeEeeee!
* (only shields for now)
*/ */
public class BatEeeeAbility implements Ability<Numeric> { public class BatEeeeAbility extends ScreechAbility {
public static final int SELF_SPOOK_PROBABILITY = 20000; public static final int SELF_SPOOK_PROBABILITY = 20000;
public static final int MOB_SPOOK_PROBABILITY = 1000;
@Override
public int getWarmupTime(Pony player) {
return 30;
}
@Override
public int getCooldownTime(Pony player) {
return 5;
}
@Override
public double getCostEstimate(Pony player) {
return 0;
}
@Override
public boolean activateOnEarlyRelease() {
return true;
}
@Override @Override
public boolean canUse(Race race) { public boolean canUse(Race race) {
@ -57,22 +22,7 @@ public class BatEeeeAbility implements Ability<Numeric> {
} }
@Override @Override
public Optional<Numeric> prepare(Pony player) { protected void playSounds(Pony player, Random rng, float strength) {
return player.getAbilities().getActiveStat()
.map(stat -> (int)(stat.getWarmup() * getWarmupTime(player)))
.filter(i -> i >= 0)
.map(Numeric::new);
}
@Override
public Numeric.Serializer<Numeric> getSerializer() {
return Numeric.SERIALIZER;
}
@Override
public boolean apply(Pony player, Numeric data) {
float strength = 1 - MathHelper.clamp(data.type() / (float)getWarmupTime(player), 0, 1);
Random rng = player.asWorld().random;
int count = 1 + rng.nextInt(10) + (int)(strength * 10); int count = 1 + rng.nextInt(10) + (int)(strength * 10);
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
@ -81,6 +31,7 @@ public class BatEeeeAbility implements Ability<Numeric> {
1.6F + (rng.nextFloat() - 0.5F) 1.6F + (rng.nextFloat() - 0.5F)
); );
} }
player.asWorld().emitGameEvent(player.asEntity(), GameEvent.ENTITY_ROAR, player.asEntity().getEyePos());
for (int j = 0; j < (int)(strength * 2); j++) { for (int j = 0; j < (int)(strength * 2); j++) {
for (int k = 0; k < count; k++) { for (int k = 0; k < count; k++) {
AwaitTickQueue.scheduleTask(player.asWorld(), w -> { AwaitTickQueue.scheduleTask(player.asWorld(), w -> {
@ -88,59 +39,14 @@ public class BatEeeeAbility implements Ability<Numeric> {
(0.9F + (rng.nextFloat() - 0.5F) / 2F) * strength, (0.9F + (rng.nextFloat() - 0.5F) / 2F) * strength,
1.6F + (rng.nextFloat() - 0.5F) 1.6F + (rng.nextFloat() - 0.5F)
); );
player.asWorld().emitGameEvent(player.asEntity(), GameEvent.ENTITY_ROAR, player.asEntity().getEyePos());
}, rng.nextInt(3)); }, rng.nextInt(3));
} }
} }
if (!player.getPhysics().isFlying()) {
player.setAnimation(Animation.SPREAD_WINGS, Animation.Recipient.ANYONE);
}
Vec3d origin = player.getOriginVector();
if (strength > 0.5F && rng.nextInt(SELF_SPOOK_PROBABILITY) == 0) { if (strength > 0.5F && rng.nextInt(SELF_SPOOK_PROBABILITY) == 0) {
player.asEntity().damage(player.damageOf(UDamageTypes.BAT_SCREECH, player), 0.1F); player.asEntity().damage(player.damageOf(UDamageTypes.BAT_SCREECH, player), 0.1F);
UCriteria.SCREECH_SELF.trigger(player.asEntity()); UCriteria.SCREECH_SELF.trigger(player.asEntity());
} }
int total = player.findAllEntitiesInRange((int)Math.max(1, 8 * strength)).mapToInt(e -> {
if (e instanceof LivingEntity living && !SpellType.SHIELD.isOn(e)) {
boolean isEarthPony = EquinePredicates.PLAYER_EARTH.test(e);
e.damage(player.damageOf(UDamageTypes.BAT_SCREECH, player), isEarthPony ? 0.1F : 0.3F);
if (e.getWorld().random.nextInt(MOB_SPOOK_PROBABILITY) == 0) {
RegistryUtils.pickRandom(e.getWorld(), UTags.SPOOKED_MOB_DROPS).ifPresent(drop -> {
e.dropStack(drop.getDefaultStack());
e.playSound(USounds.Vanilla.ENTITY_ITEM_PICKUP, 1, 0.1F);
UCriteria.SPOOK_MOB.trigger(player.asEntity());
});
}
Vec3d knockVec = origin.subtract(e.getPos()).multiply(strength);
living.takeKnockback((isEarthPony ? 0.3F : 0.5F) * strength, knockVec.getX(), knockVec.getZ());
if (!isEarthPony) {
e.addVelocity(0, 0.1 * strength, 0);
}
Living.updateVelocity(e);
return 1;
}
return 0;
}).sum();
if (total >= 20) {
UCriteria.SCREECH_TWENTY_MOBS.trigger(player.asEntity());
}
return true;
}
@Override
public void warmUp(Pony player, AbilitySlot slot) {
}
@Override
public void coolDown(Pony player, AbilitySlot slot) {
for (int i = 0; i < 20; i++) {
player.addParticle(ParticleTypes.BUBBLE_POP, player.getPhysics().getHeadPosition().toCenterPos(), VecHelper.supply(() -> player.asWorld().getRandom().nextGaussian() - 0.5));
}
} }
} }

View file

@ -0,0 +1,76 @@
package com.minelittlepony.unicopia.ability;
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;
/**
* Dashing ability for flying creatures.
*/
public class FlyingDashAbility implements Ability<Hit> {
@Override
public int getWarmupTime(Pony player) {
return 19;
}
@Override
public int getCooldownTime(Pony player) {
return 30;
}
@Override
public boolean canUse(Race race) {
return race == Race.HIPPOGRIFF;
}
@Nullable
@Override
public Optional<Hit> prepare(Pony player) {
return Hit.of(player.getPhysics().isFlying());
}
@Override
public Hit.Serializer<Hit> getSerializer() {
return Hit.SERIALIZER;
}
@Override
public double getCostEstimate(Pony player) {
return 0;
}
@Override
public boolean onQuickAction(Pony player, ActivationType type, Optional<Hit> data) {
if (type == ActivationType.TAP && !player.getMotion().isRainbooming() && player.getPhysics().isFlying() && player.getMagicalReserves().getMana().get() > 40) {
player.getPhysics().dashForward((float)player.asWorld().random.nextTriangular(2.5F, 0.3F));
return true;
}
return false;
}
@Override
public boolean apply(Pony player, Hit data) {
player.getPhysics().dashForward((float)player.asWorld().random.nextTriangular(2.5F, 0.3F));
player.subtractEnergyCost(2);
return true;
}
@Override
public void warmUp(Pony player, AbilitySlot slot) {
player.getMagicalReserves().getExertion().addPercent(6);
}
@Override
public void coolDown(Pony player, AbilitySlot slot) {
float velocityScale = player.getAbilities().getStat(slot).getCooldown();
float multiplier = 1 + (0.02F * velocityScale);
player.asEntity().getVelocity().multiply(multiplier, 1, multiplier);
}
}

View file

@ -0,0 +1,152 @@
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;
import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.util.TraceHelper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.World;
import net.minecraft.world.WorldEvents;
import net.minecraft.world.event.GameEvent;
/**
* Hippogriff ability to use their beak as a weapon
*/
public class PeckAbility implements Ability<Hit> {
@Override
public int getWarmupTime(Pony player) {
return 1;
}
@Override
public int getCooldownTime(Pony player) {
return 10;
}
@Override
public double getCostEstimate(Pony player) {
return 0;
}
@Override
public boolean activateOnEarlyRelease() {
return true;
}
@Override
public boolean canUse(Race race) {
return race == Race.HIPPOGRIFF;
}
@Override
public Optional<Hit> prepare(Pony player) {
return Hit.INSTANCE;
}
@Override
public Numeric.Serializer<Hit> getSerializer() {
return Hit.SERIALIZER;
}
protected LivingEntity findTarget(PlayerEntity player, World w) {
return TraceHelper.<LivingEntity>findEntity(player, 5, 1, hit -> {
return EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR.test(hit) && !player.isConnectedThroughVehicle(hit);
}).orElse(null);
}
@Override
public boolean apply(Pony player, Hit hit) {
LivingEntity target = findTarget(player.asEntity(), player.asWorld());
playSounds(player, player.asWorld().getRandom(), 1);
if (target != null) {
spookMob(player, target, 1);
} else {
BlockPos pos = TraceHelper.findBlock(player.asEntity(), 5, 1).orElse(BlockPos.ORIGIN);
if (pos != BlockPos.ORIGIN) {
BlockState state = player.asWorld().getBlockState(pos);
if (state.isReplaceable()) {
player.asWorld().breakBlock(pos, true);
} else if (state.isIn(BlockTags.DIRT) || player.asWorld().random.nextInt(40000) == 0) {
player.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, pos, Block.getRawIdFromState(state));
pos = pos.up();
World world = player.asWorld();
if (world instanceof ServerWorld sw) {
for (ItemStack stack : Block.getDroppedStacks(state, sw, pos, null)) {
if (Block.getBlockFromItem(stack.getItem()) == Blocks.AIR) {
Block.dropStack(world, pos, stack);
}
}
state.onStacksDropped(sw, pos, ItemStack.EMPTY, true);
if (world.random.nextInt(20) == 0) {
world.breakBlock(pos.down(), false);
player.asEntity().sendMessage(Text.translatable("ability.unicopia.peck.block.fled"), true);
}
}
} else {
player.asEntity().sendMessage(Text.translatable("ability.unicopia.peck.block.unfased"), true);
}
}
}
return true;
}
protected void playSounds(Pony player, Random rng, float strength) {
player.getMagicalReserves().getExertion().addPercent(100);
player.getMagicalReserves().getEnergy().addPercent(10);
player.playSound(USounds.Vanilla.ENTITY_CHICKEN_HURT,
1,
0.9F + (rng.nextFloat() - 0.5F)
);
player.asWorld().emitGameEvent(player.asEntity(), GameEvent.STEP, player.asEntity().getEyePos());
}
protected void spookMob(Pony player, LivingEntity living, float strength) {
boolean isEarthPony = EquinePredicates.PLAYER_EARTH.test(living);
boolean isBracing = isEarthPony && player.asEntity().isSneaking();
if (!isBracing) {
living.damage(player.damageOf(UDamageTypes.BAT_SCREECH, player), isEarthPony ? 0.1F : 0.3F);
}
Vec3d knockVec = player.getOriginVector().subtract(living.getPos()).multiply(strength);
living.takeKnockback((isBracing ? 0.2F : isEarthPony ? 0.3F : 0.5F) * strength, knockVec.getX(), knockVec.getZ());
if (!isEarthPony) {
living.addVelocity(0, 0.1 * strength, 0);
}
Living.updateVelocity(living);
}
@Override
public void warmUp(Pony player, AbilitySlot slot) {
}
@Override
public void coolDown(Pony player, AbilitySlot slot) {
}
}

View file

@ -0,0 +1,141 @@
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;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.advancement.UCriteria;
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.util.RegistryUtils;
import com.minelittlepony.unicopia.util.VecHelper;
import net.minecraft.entity.LivingEntity;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.event.GameEvent;
/**
* An ability to scream very loud
*/
public class ScreechAbility implements Ability<Numeric> {
public static final int MOB_SPOOK_PROBABILITY = 1000;
@Override
public int getWarmupTime(Pony player) {
return 30;
}
@Override
public int getCooldownTime(Pony player) {
return 5;
}
@Override
public double getCostEstimate(Pony player) {
return 0;
}
@Override
public boolean activateOnEarlyRelease() {
return true;
}
@Override
public boolean canUse(Race race) {
return race == Race.HIPPOGRIFF;
}
@Override
public Optional<Numeric> prepare(Pony player) {
return player.getAbilities().getActiveStat()
.map(stat -> (int)(stat.getWarmup() * getWarmupTime(player)))
.filter(i -> i >= 0)
.map(Numeric::new);
}
@Override
public Numeric.Serializer<Numeric> getSerializer() {
return Numeric.SERIALIZER;
}
@Override
public boolean apply(Pony player, Numeric data) {
float strength = 1 - MathHelper.clamp(data.type() / (float)getWarmupTime(player), 0, 1);
Random rng = player.asWorld().random;
playSounds(player, rng, strength);
if (!player.getPhysics().isFlying()) {
player.setAnimation(Animation.SPREAD_WINGS, Animation.Recipient.ANYONE);
}
int total = player.findAllEntitiesInRange((int)Math.max(1, 8 * strength)).mapToInt(e -> {
if (e instanceof LivingEntity living && !SpellType.SHIELD.isOn(e)) {
spookMob(player, living, strength);
return 1;
}
return 0;
}).sum();
if (total >= 20) {
UCriteria.SCREECH_TWENTY_MOBS.trigger(player.asEntity());
}
return true;
}
protected void playSounds(Pony player, Random rng, float strength) {
player.playSound(USounds.ENTITY_PLAYER_HIPPOGRIFF_SCREECH,
(1.2F + (rng.nextFloat() - 0.5F) / 2F) * strength,
1.1F + (rng.nextFloat() - 0.5F)
);
player.asWorld().emitGameEvent(player.asEntity(), GameEvent.ENTITY_ROAR, player.asEntity().getEyePos());
}
protected void spookMob(Pony player, LivingEntity living, float strength) {
boolean isEarthPony = EquinePredicates.PLAYER_EARTH.test(living);
boolean isBracing = isEarthPony && player.asEntity().isSneaking();
if (!isBracing) {
living.damage(player.damageOf(UDamageTypes.BAT_SCREECH, player), isEarthPony ? 0.1F : 0.3F);
if (living.getWorld().random.nextInt(MOB_SPOOK_PROBABILITY) == 0) {
RegistryUtils.pickRandom(living.getWorld(), UTags.SPOOKED_MOB_DROPS).ifPresent(drop -> {
living.dropStack(drop.getDefaultStack());
living.playSound(USounds.Vanilla.ENTITY_ITEM_PICKUP, 1, 0.1F);
UCriteria.SPOOK_MOB.trigger(player.asEntity());
});
}
}
Vec3d knockVec = player.getOriginVector().subtract(living.getPos()).multiply(strength);
living.takeKnockback((isBracing ? 0.2F : isEarthPony ? 0.3F : 0.5F) * strength, knockVec.getX(), knockVec.getZ());
if (!isEarthPony) {
living.addVelocity(0, 0.1 * strength, 0);
}
Living.updateVelocity(living);
}
@Override
public void warmUp(Pony player, AbilitySlot slot) {
}
@Override
public void coolDown(Pony player, AbilitySlot slot) {
for (int i = 0; i < 20; i++) {
player.addParticle(ParticleTypes.BUBBLE_POP, player.asEntity().getEyePos(),
VecHelper.supply(() -> (player.asWorld().getRandom().nextGaussian() - 0.5) * 0.3)
);
}
}
}

View file

@ -12,7 +12,7 @@ import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
public class PegasusFlightToggleAbility implements Ability<Hit> { public class ToggleFlightAbility implements Ability<Hit> {
@Override @Override
public int getWarmupTime(Pony player) { public int getWarmupTime(Pony player) {

View file

@ -0,0 +1,61 @@
package com.minelittlepony.unicopia.client.gui;
import java.util.HashSet;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.entity.duck.EntityDuck;
import com.minelittlepony.unicopia.entity.effect.EffectUtils;
import com.minelittlepony.unicopia.entity.effect.UEffects;
import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluid;
import net.minecraft.registry.tag.FluidTags;
import net.minecraft.registry.tag.TagKey;
public class HudEffects {
private static boolean addedHunger;
private static Set<TagKey<Fluid>> originalTags = null;
public static void tryApply(@Nullable PlayerEntity player, float tickDelta, boolean on) {
if (player != null) {
apply(Pony.of(player), tickDelta, on);
}
}
private static void apply(Pony pony, float tickDelta, boolean on) {
if (on) {
if (!pony.asEntity().hasStatusEffect(StatusEffects.HUNGER) && EffectUtils.getAmplifier(pony.asEntity(), UEffects.FOOD_POISONING) > 0) {
addedHunger = true;
pony.asEntity().addStatusEffect(new StatusEffectInstance(StatusEffects.HUNGER, 1, 1, false, false));
}
} else {
if (addedHunger) {
addedHunger = false;
pony.asEntity().removeStatusEffect(StatusEffects.HUNGER);
}
}
if (pony.getCompositeRace().includes(Race.SEAPONY)) {
Set<TagKey<Fluid>> fluidTags = ((EntityDuck)pony.asEntity()).getSubmergedFluidTags();
if (on) {
originalTags = new HashSet<>(fluidTags);
if (fluidTags.contains(FluidTags.WATER)) {
fluidTags.clear();
} else {
fluidTags.add(FluidTags.WATER);
}
} else if (originalTags != null) {
fluidTags.clear();
fluidTags.addAll(originalTags);
originalTags = null;
}
}
}
}

View file

@ -54,7 +54,7 @@ public class TribeSelectionScreen extends GameGui implements HidesHud {
final int itemWidth = 70 + 10; final int itemWidth = 70 + 10;
List<Race> options = Race.REGISTRY.stream().filter(race -> !race.isHuman() && !race.isOp()).toList(); List<Race> options = Race.REGISTRY.stream().filter(race -> race.availability().isSelectable()).toList();
int columns = Math.min(width / itemWidth, options.size()); int columns = Math.min(width / itemWidth, options.size());

View file

@ -15,6 +15,7 @@ import com.minelittlepony.client.model.entity.race.UnicornModel;
import com.minelittlepony.client.model.part.UnicornHorn; import com.minelittlepony.client.model.part.UnicornHorn;
import com.minelittlepony.mson.api.MsonModel; import com.minelittlepony.mson.api.MsonModel;
import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.FlightType;
import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.entity.AmuletSelectors; import com.minelittlepony.unicopia.entity.AmuletSelectors;
@ -43,7 +44,7 @@ class BodyPartGear<M extends ClientPonyModel<LivingEntity> & MsonModel & IModel>
public static final Predicate<LivingEntity> UNICORN_HORN_PREDICATE = MINE_LP_HAS_NO_HORN.and(AmuletSelectors.ALICORN_AMULET.or(EquinePredicates.raceMatches(Race::canCast))); public static final Predicate<LivingEntity> UNICORN_HORN_PREDICATE = MINE_LP_HAS_NO_HORN.and(AmuletSelectors.ALICORN_AMULET.or(EquinePredicates.raceMatches(Race::canCast)));
public static final Identifier UNICORN_HORN = Unicopia.id("textures/models/horn/unicorn.png"); 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::canInteractWithClouds))); 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 Identifier PEGASUS_WINGS = Unicopia.id("textures/models/wings/pegasus_pony.png"); public static final Identifier PEGASUS_WINGS = Unicopia.id("textures/models/wings/pegasus_pony.png");
public static BodyPartGear<WingsGearModel> pegasusWings() { public static BodyPartGear<WingsGearModel> pegasusWings() {

View file

@ -55,9 +55,9 @@ public class Main extends MineLPDelegate implements ClientModInitializer {
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.CHANGEDLING, Race.CHANGELING); registerRaceMapping(com.minelittlepony.api.pony.meta.Race.CHANGEDLING, Race.CHANGELING);
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.ZEBRA, Race.EARTH); registerRaceMapping(com.minelittlepony.api.pony.meta.Race.ZEBRA, Race.EARTH);
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.GRYPHON, Race.PEGASUS); registerRaceMapping(com.minelittlepony.api.pony.meta.Race.GRYPHON, Race.PEGASUS);
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.HIPPOGRIFF, Race.PEGASUS); registerRaceMapping(com.minelittlepony.api.pony.meta.Race.HIPPOGRIFF, Race.HIPPOGRIFF);
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.BATPONY, Race.BAT); registerRaceMapping(com.minelittlepony.api.pony.meta.Race.BATPONY, Race.BAT);
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.SEAPONY, Race.UNICORN); registerRaceMapping(com.minelittlepony.api.pony.meta.Race.SEAPONY, Race.SEAPONY);
} }
private void onPonyModelPrepared(Entity entity, IModel model, ModelAttributes.Mode mode) { private void onPonyModelPrepared(Entity entity, IModel model, ModelAttributes.Mode mode) {

View file

@ -32,7 +32,6 @@ public class ModelPartHooks {
final var bestCandidate = new EnqueudHeadRender(); final var bestCandidate = new EnqueudHeadRender();
matrices.push();
part.forEachCuboid(matrices, (entry, name, index, cube) -> { part.forEachCuboid(matrices, (entry, name, index, cube) -> {
float x = cube.maxX - cube.minX; float x = cube.maxX - cube.minX;
float y = cube.maxY - cube.minY; float y = cube.maxY - cube.minY;
@ -47,7 +46,6 @@ public class ModelPartHooks {
bestCandidate.maxSideLength = Math.max(Math.max(x, z), y); bestCandidate.maxSideLength = Math.max(Math.max(x, z), y);
} }
}); });
matrices.pop();
if (bestCandidate.transformation != null) { if (bestCandidate.transformation != null) {
head.add(bestCandidate); head.add(bestCandidate);
@ -75,8 +73,6 @@ public class ModelPartHooks {
matrices.translate(x * PIXEL_SCALE, y * PIXEL_SCALE, z * PIXEL_SCALE); matrices.translate(x * PIXEL_SCALE, y * PIXEL_SCALE, z * PIXEL_SCALE);
matrices.scale(scale, scale, scale); matrices.scale(scale, scale, scale);
//matrices.peek().getPositionMatrix().scaleAround(scale, x * PIXEL_SCALE, y * PIXEL_SCALE, z * PIXEL_SCALE);
//matrices.translate(cube.minX * PIXEL_SCALE, cube.minY * PIXEL_SCALE, cube.minZ * PIXEL_SCALE);
} }
} }
} }

View file

@ -1,7 +1,6 @@
package com.minelittlepony.unicopia.client.render; package com.minelittlepony.unicopia.client.render;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
import com.minelittlepony.unicopia.entity.Creature; import com.minelittlepony.unicopia.entity.Creature;
import com.minelittlepony.unicopia.item.enchantment.WantItNeedItEnchantment; import com.minelittlepony.unicopia.item.enchantment.WantItNeedItEnchantment;
@ -20,7 +19,6 @@ import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.Box; import net.minecraft.util.math.Box;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RotationAxis;
public class SmittenEyesRenderer { public class SmittenEyesRenderer {
private static final Identifier TEXTURE = Unicopia.id("textures/entity/smitten_eyes.png"); private static final Identifier TEXTURE = Unicopia.id("textures/entity/smitten_eyes.png");
@ -42,9 +40,6 @@ public class SmittenEyesRenderer {
ModelPartHooks.stopCollecting().forEach(head -> { ModelPartHooks.stopCollecting().forEach(head -> {
matrices.push(); matrices.push();
head.transform(matrices, 0.95F); head.transform(matrices, 0.95F);
if (MineLPDelegate.getInstance().getRace(pony.asEntity()).isEquine()) {
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-90), 0, 1.2F, 0);
}
float scale = 1F + (1.3F + MathHelper.sin(pony.asEntity().age / 3F) * 0.06F); float scale = 1F + (1.3F + MathHelper.sin(pony.asEntity().age / 3F) * 0.06F);
matrices.scale(scale, scale, scale); matrices.scale(scale, scale, scale);
matrices.translate(0, 0.05F, 0); matrices.translate(0, 0.05F, 0);

View file

@ -1,5 +1,6 @@
package com.minelittlepony.unicopia.client.render; package com.minelittlepony.unicopia.client.render;
import com.minelittlepony.unicopia.FlightType;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
@ -50,7 +51,7 @@ public class WingsFeatureRenderer<E extends LivingEntity> implements AccessoryFe
} }
protected boolean canRender(E entity) { protected boolean canRender(E entity) {
return entity instanceof PlayerEntity && Pony.of((PlayerEntity)entity).getObservedSpecies().canInteractWithClouds(); return entity instanceof PlayerEntity && Pony.of((PlayerEntity)entity).getObservedSpecies().flightType() == FlightType.AVIAN;
} }
protected Identifier getTexture(E entity) { protected Identifier getTexture(E entity) {

View file

@ -4,7 +4,10 @@ import java.util.Optional;
import com.minelittlepony.client.util.render.RenderLayerUtil; import com.minelittlepony.client.util.render.RenderLayerUtil;
import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
import com.minelittlepony.unicopia.client.render.model.SphereModel;
import com.minelittlepony.unicopia.entity.Creature; import com.minelittlepony.unicopia.entity.Creature;
import com.minelittlepony.unicopia.entity.Equine; import com.minelittlepony.unicopia.entity.Equine;
import com.minelittlepony.unicopia.entity.ItemImpl; import com.minelittlepony.unicopia.entity.ItemImpl;
@ -80,6 +83,27 @@ public class WorldRenderDelegate {
smittenEyesRenderer.render(creature, matrices, immediate, light, 0); smittenEyesRenderer.render(creature, matrices, immediate, light, 0);
} }
if (pony instanceof Pony p) {
if (p.getCompositeRace().includes(Race.SEAPONY)
&& pony.asEntity().isSubmergedInWater()
&& MineLPDelegate.getInstance().getPlayerPonyRace(p.asEntity()) != Race.SEAPONY) {
for (var head : ModelPartHooks.stopCollecting()) {
matrices.push();
head.transform(matrices, 1F);
Immediate immediate = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers();
RenderLayer layer = RenderLayers.getMagicColored();
float scale = 0.9F;
SphereModel.SPHERE.render(matrices, immediate.getBuffer(layer), light, 0, scale, 0.5F, 0.5F, 0.5F, 0.1F);
SphereModel.SPHERE.render(matrices, immediate.getBuffer(layer), light, 0, scale + 0.2F, 0.5F, 0.5F, 0.5F, 0.1F);
matrices.pop();
}
}
}
if (pony instanceof ItemImpl || pony instanceof Living) { if (pony instanceof ItemImpl || pony instanceof Living) {
matrices.pop(); matrices.pop();
@ -162,6 +186,13 @@ public class WorldRenderDelegate {
roll -= 180; roll -= 180;
} }
if (p.getAcrobatics().isFloppy()) {
matrices.translate(0, -0.5, 0);
p.asEntity().setBodyYaw(0);
p.asEntity().setYaw(0);
matrices.multiply(RotationAxis.NEGATIVE_X.rotationDegrees(90));
}
matrices.multiply(RotationAxis.NEGATIVE_Y.rotationDegrees(yaw)); matrices.multiply(RotationAxis.NEGATIVE_Y.rotationDegrees(yaw));
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(roll)); matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(roll));
@ -169,6 +200,12 @@ public class WorldRenderDelegate {
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(diveAngle)); matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(diveAngle));
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw)); matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw));
if (p.getCompositeRace().includes(Race.SEAPONY)
&& pony.asEntity().isSubmergedInWater()
&& MineLPDelegate.getInstance().getPlayerPonyRace(p.asEntity()) != Race.SEAPONY) {
ModelPartHooks.startCollecting();
}
} else if (pony instanceof Creature creature && smittenEyesRenderer.isSmitten(creature)) { } else if (pony instanceof Creature creature && smittenEyesRenderer.isSmitten(creature)) {
ModelPartHooks.startCollecting(); ModelPartHooks.startCollecting();
} }

View file

@ -8,7 +8,6 @@ import com.minelittlepony.unicopia.network.MsgTribeSelect;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.command.argument.EntityArgumentType; import net.minecraft.command.argument.EntityArgumentType;
import net.minecraft.command.argument.RegistryKeyArgumentType;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.CommandManager.RegistrationEnvironment; import net.minecraft.server.command.CommandManager.RegistrationEnvironment;
@ -32,8 +31,6 @@ class SpeciesCommand {
} }
} }
RegistryKeyArgumentType<Race> raceArgument = Race.argument();
return builder return builder
.then(CommandManager.literal("get") .then(CommandManager.literal("get")
.executes(context -> get(context.getSource(), context.getSource().getPlayer(), true)) .executes(context -> get(context.getSource(), context.getSource().getPlayer(), true))
@ -41,13 +38,13 @@ class SpeciesCommand {
.executes(context -> get(context.getSource(), EntityArgumentType.getPlayer(context, "target"), false)) .executes(context -> get(context.getSource(), EntityArgumentType.getPlayer(context, "target"), false))
)) ))
.then(CommandManager.literal("set") .then(CommandManager.literal("set")
.then(CommandManager.argument("race", raceArgument) .then(CommandManager.argument("race", Race.argument())
.executes(context -> set(context.getSource(), context.getSource().getPlayer(), Race.fromArgument(context, "race"), true)) .executes(context -> set(context.getSource(), context.getSource().getPlayer(), Race.fromArgument(context, "race"), true))
.then(CommandManager.argument("target", EntityArgumentType.player()) .then(CommandManager.argument("target", EntityArgumentType.player())
.executes(context -> set(context.getSource(), EntityArgumentType.getPlayer(context, "target"), Race.fromArgument(context, "race"), false))) .executes(context -> set(context.getSource(), EntityArgumentType.getPlayer(context, "target"), Race.fromArgument(context, "race"), false)))
)) ))
.then(CommandManager.literal("describe") .then(CommandManager.literal("describe")
.then(CommandManager.argument("race", raceArgument) .then(CommandManager.argument("race", Race.argument())
.executes(context -> describe(context.getSource().getPlayer(), Race.fromArgument(context, "race"))) .executes(context -> describe(context.getSource().getPlayer(), Race.fromArgument(context, "race")))
)) ))
.then(CommandManager.literal("list") .then(CommandManager.literal("list")
@ -101,7 +98,7 @@ class SpeciesCommand {
boolean first = true; boolean first = true;
for (Race i : Race.REGISTRY) { for (Race i : Race.REGISTRY) {
if (!i.isUnset() && i.isPermitted(player)) { if (i.availability().isGrantable() && !i.isUnset() && i.isPermitted(player)) {
message.append(Text.literal((!first ? "\n" : "") + " - ")); message.append(Text.literal((!first ? "\n" : "") + " - "));
message.append(i.getDisplayName()); message.append(i.getDisplayName());
first = false; first = false;

View file

@ -10,6 +10,7 @@ public interface AmuletSelectors {
Predicate<LivingEntity> ALICORN_AMULET = UItems.ALICORN_AMULET::isApplicable; Predicate<LivingEntity> ALICORN_AMULET = UItems.ALICORN_AMULET::isApplicable;
Predicate<LivingEntity> PEGASUS_AMULET = UItems.PEGASUS_AMULET::isApplicable; Predicate<LivingEntity> PEGASUS_AMULET = UItems.PEGASUS_AMULET::isApplicable;
Predicate<LivingEntity> UNICORN_AMULET = UItems.UNICORN_AMULET::isApplicable; Predicate<LivingEntity> UNICORN_AMULET = UItems.UNICORN_AMULET::isApplicable;
Predicate<LivingEntity> PEARL_NECKLACE = UItems.PEARL_NECKLACE::isApplicable;
Predicate<LivingEntity> ALICORN_AMULET_AFTER_1_DAYS = ALICORN_AMULET.and(ItemTracker.wearing(UItems.ALICORN_AMULET, ItemTracker.after(ItemTracker.DAYS))); Predicate<LivingEntity> ALICORN_AMULET_AFTER_1_DAYS = ALICORN_AMULET.and(ItemTracker.wearing(UItems.ALICORN_AMULET, ItemTracker.after(ItemTracker.DAYS)));
Predicate<LivingEntity> ALICORN_AMULET_AFTER_2_DAYS = ALICORN_AMULET.and(ItemTracker.wearing(UItems.ALICORN_AMULET, ItemTracker.after(2 * ItemTracker.DAYS))); Predicate<LivingEntity> ALICORN_AMULET_AFTER_2_DAYS = ALICORN_AMULET.and(ItemTracker.wearing(UItems.ALICORN_AMULET, ItemTracker.after(2 * ItemTracker.DAYS)));

View file

@ -1,12 +1,18 @@
package com.minelittlepony.unicopia.entity.duck; package com.minelittlepony.unicopia.entity.duck;
import java.util.Set;
import com.minelittlepony.unicopia.compat.pehkui.PehkuiEntityExtensions; import com.minelittlepony.unicopia.compat.pehkui.PehkuiEntityExtensions;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.Entity.RemovalReason; import net.minecraft.entity.Entity.RemovalReason;
import net.minecraft.fluid.Fluid;
import net.minecraft.registry.tag.TagKey;
public interface EntityDuck extends LavaAffine, PehkuiEntityExtensions { public interface EntityDuck extends LavaAffine, PehkuiEntityExtensions {
Set<TagKey<Fluid>> getSubmergedFluidTags();
void setRemovalReason(RemovalReason reason); void setRemovalReason(RemovalReason reason);
void setVehicle(Entity vehicle); void setVehicle(Entity vehicle);

View file

@ -34,6 +34,7 @@ public class RaceChangeStatusEffect extends StatusEffect {
public static final StatusEffect CHANGE_RACE_BAT = register(0x0FFF00, Race.BAT); public static final StatusEffect CHANGE_RACE_BAT = register(0x0FFF00, Race.BAT);
public static final StatusEffect CHANGE_RACE_CHANGELING = register(0xFFFF00, Race.CHANGELING); public static final StatusEffect CHANGE_RACE_CHANGELING = register(0xFFFF00, Race.CHANGELING);
public static final StatusEffect CHANGE_RACE_KIRIN = register(0xFF8800, Race.KIRIN); public static final StatusEffect CHANGE_RACE_KIRIN = register(0xFF8800, Race.KIRIN);
public static final StatusEffect CHANGE_RACE_HIPPOGRIFF = register(0x00FFFF, Race.HIPPOGRIFF);
private final Race race; private final Race race;

View file

@ -18,6 +18,8 @@ public interface UPotions {
Potion TRIBE_SWAP_PEGASUS = register("tribe_swap_pegasus", new Potion("unicopia.tribe_swap_pegasus", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_PEGASUS, RaceChangeStatusEffect.MAX_DURATION))); Potion TRIBE_SWAP_PEGASUS = register("tribe_swap_pegasus", new Potion("unicopia.tribe_swap_pegasus", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_PEGASUS, RaceChangeStatusEffect.MAX_DURATION)));
Potion TRIBE_SWAP_BAT = register("tribe_swap_bat", new Potion("unicopia.tribe_swap_bat", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_BAT, RaceChangeStatusEffect.MAX_DURATION))); Potion TRIBE_SWAP_BAT = register("tribe_swap_bat", new Potion("unicopia.tribe_swap_bat", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_BAT, RaceChangeStatusEffect.MAX_DURATION)));
Potion TRIBE_SWAP_CHANGELING = register("tribe_swap_changeling", new Potion("unicopia.tribe_swap_changeling", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_CHANGELING, RaceChangeStatusEffect.MAX_DURATION))); Potion TRIBE_SWAP_CHANGELING = register("tribe_swap_changeling", new Potion("unicopia.tribe_swap_changeling", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_CHANGELING, RaceChangeStatusEffect.MAX_DURATION)));
Potion TRIBE_SWAP_KIRIN = register("tribe_swap_kirin", new Potion("unicopia.tribe_swap_kirin", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_KIRIN, RaceChangeStatusEffect.MAX_DURATION)));
Potion TRIBE_SWAP_HIPPOGRIFF = register("tribe_swap_hippogriff", new Potion("unicopia.tribe_swap_hippogriff", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_HIPPOGRIFF, RaceChangeStatusEffect.MAX_DURATION)));
static Potion register(String name, Potion potion) { static Potion register(String name, Potion potion) {
REGISTRY.add(potion); REGISTRY.add(potion);

View file

@ -7,6 +7,7 @@ import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation; import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation.Recipient; import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation.Recipient;
import com.minelittlepony.unicopia.entity.duck.LivingEntityDuck; import com.minelittlepony.unicopia.entity.duck.LivingEntityDuck;
import com.minelittlepony.unicopia.entity.mob.StormCloudEntity;
import com.minelittlepony.unicopia.util.NbtSerialisable; import com.minelittlepony.unicopia.util.NbtSerialisable;
import com.minelittlepony.unicopia.util.Tickable; import com.minelittlepony.unicopia.util.Tickable;
@ -17,6 +18,8 @@ import net.minecraft.entity.data.TrackedData;
import net.minecraft.entity.data.TrackedDataHandlerRegistry; import net.minecraft.entity.data.TrackedDataHandlerRegistry;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
@ -42,6 +45,17 @@ public class Acrobatics implements Tickable, NbtSerialisable {
pony.addTicker(this::checkDislodge); pony.addTicker(this::checkDislodge);
} }
public boolean isImmobile() {
return isFloppy() && entity.isOnGround();
}
public boolean isFloppy() {
if (entity.isCreative() && entity.getAbilities().flying) {
return false;
}
return pony.getCompositeRace().any(Race::isFish) && !entity.isTouchingWater() && !entity.getWorld().isWater(StormCloudEntity.findSurfaceBelow(entity.getWorld(), entity.getBlockPos()));
}
@Override @Override
public void tick() { public void tick() {
BlockPos climbingPos = entity.getClimbingPos().orElse(null); BlockPos climbingPos = entity.getClimbingPos().orElse(null);
@ -115,6 +129,11 @@ public class Acrobatics implements Tickable, NbtSerialisable {
} else { } else {
ticksHanging = 0; ticksHanging = 0;
} }
if (pony.getCompositeRace().includes(Race.SEAPONY) && !entity.isSubmergedInWater() && pony.landedChanged()) {
entity.getWorld().playSound(null, entity.getBlockPos(), SoundEvents.ENTITY_GUARDIAN_FLOP, SoundCategory.PLAYERS, 1, 1);
}
} }
boolean isFaceClimbable(World world, BlockPos pos, Direction direction) { boolean isFaceClimbable(World world, BlockPos pos, Direction direction) {

View file

@ -36,12 +36,23 @@ public class PlayerAttributes implements Tickable {
new ToggleableAttribute( new ToggleableAttribute(
new EntityAttributeModifier(UUID.fromString("9e2699fc-3b8d-4f71-9d2d-fb92ee19b4f7"), "Pegasus Speed", 0.2, Operation.MULTIPLY_TOTAL), new EntityAttributeModifier(UUID.fromString("9e2699fc-3b8d-4f71-9d2d-fb92ee19b4f7"), "Pegasus Speed", 0.2, Operation.MULTIPLY_TOTAL),
List.of(EntityAttributes.GENERIC_MOVEMENT_SPEED, EntityAttributes.GENERIC_ATTACK_SPEED), List.of(EntityAttributes.GENERIC_MOVEMENT_SPEED, EntityAttributes.GENERIC_ATTACK_SPEED),
pony -> pony.getCompositeRace().canFly() pony -> pony.getCompositeRace().canFly() && !pony.getCompositeRace().includes(Race.HIPPOGRIFF)
), ),
new ToggleableAttribute( new ToggleableAttribute(
new EntityAttributeModifier(UUID.fromString("707b50a8-03e8-40f4-8553-ecf67025fd6d"), "Pegasus Reach", 1.5, Operation.ADDITION), new EntityAttributeModifier(UUID.fromString("707b50a8-03e8-40f4-8553-ecf67025fd6d"), "Pegasus Reach", 1.5, Operation.ADDITION),
List.of(UEntityAttributes.EXTENDED_REACH_DISTANCE), List.of(UEntityAttributes.EXTENDED_REACH_DISTANCE),
pony -> pony.getCompositeRace().canFly() pony -> pony.getCompositeRace().canFly() && !pony.getCompositeRace().includes(Race.HIPPOGRIFF)
),
new ToggleableAttribute(
new EntityAttributeModifier(UUID.fromString("9e2699fc-3b8d-4f71-92dd-bef19b92e4f7"), "Hippogriff Speed", 0.1, Operation.MULTIPLY_TOTAL),
List.of(EntityAttributes.GENERIC_MOVEMENT_SPEED, EntityAttributes.GENERIC_ATTACK_SPEED),
pony -> pony.getCompositeRace().includes(Race.HIPPOGRIFF)
),
new ToggleableAttribute(
new EntityAttributeModifier(UUID.fromString("707b50a8-03e8-40f4-5853-fc7e0f625d6d"), "Hippogriff Reach", 1.3, Operation.ADDITION),
List.of(UEntityAttributes.EXTENDED_REACH_DISTANCE),
pony -> pony.getCompositeRace().includes(Race.HIPPOGRIFF)
), ),
new ToggleableAttribute( new ToggleableAttribute(

View file

@ -3,9 +3,11 @@ package com.minelittlepony.unicopia.entity.player;
import java.util.Optional; import java.util.Optional;
import com.minelittlepony.common.util.animation.MotionCompositor; import com.minelittlepony.common.util.animation.MotionCompositor;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate; import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.AbstractDisguiseSpell; import com.minelittlepony.unicopia.ability.magic.spell.AbstractDisguiseSpell;
import net.minecraft.client.render.CameraSubmersionType;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
public class PlayerCamera extends MotionCompositor { public class PlayerCamera extends MotionCompositor {
@ -35,6 +37,11 @@ public class PlayerCamera extends MotionCompositor {
roll = player.getInterpolator().interpolate("roll", (float)roll, 15); roll = player.getInterpolator().interpolate("roll", (float)roll, 15);
} }
if (player.getAcrobatics().isFloppy()) {
roll += 90;
}
return (float)roll; return (float)roll;
} }
@ -54,6 +61,15 @@ public class PlayerCamera extends MotionCompositor {
.map(d -> distance * d); .map(d -> distance * d);
} }
public Optional<CameraSubmersionType> getSubmersionType(CameraSubmersionType original) {
if (player.getCompositeRace().includes(Race.SEAPONY)) {
if (original == CameraSubmersionType.WATER) {
return Optional.of(CameraSubmersionType.NONE);
}
}
return Optional.empty();
}
public double calculateFieldOfView(double fov) { public double calculateFieldOfView(double fov) {
fov += player.getMagicalReserves().getExertion().get() / 5F; fov += player.getMagicalReserves().getExertion().get() / 5F;
fov += getEnergyAddition(); fov += getEnergyAddition();

View file

@ -414,7 +414,7 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
if (type.isAvian()) { if (type.isAvian()) {
if (pony.getObservedSpecies() != Race.BAT && entity.getWorld().random.nextInt(9000) == 0) { if (pony.getObservedSpecies() != Race.BAT && entity.getWorld().random.nextInt(9000) == 0) {
entity.dropItem(UItems.PEGASUS_FEATHER); entity.dropItem(pony.getObservedSpecies() == Race.HIPPOGRIFF ? UItems.GRYPHON_FEATHER : UItems.PEGASUS_FEATHER);
playSound(USounds.ENTITY_PLAYER_PEGASUS_MOLT, 0.3F, 1); playSound(USounds.ENTITY_PLAYER_PEGASUS_MOLT, 0.3F, 1);
UCriteria.SHED_FEATHER.trigger(entity); UCriteria.SHED_FEATHER.trigger(entity);
} }
@ -513,7 +513,9 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
if (entity.getWorld().random.nextInt(110) == 1 && !pony.isClient()) { if (entity.getWorld().random.nextInt(110) == 1 && !pony.isClient()) {
pony.getLevel().add(1); pony.getLevel().add(1);
pony.getMagicalReserves().getCharge().addPercent(4); if (Abilities.RAINBOOM.canUse(pony.getCompositeRace())) {
pony.getMagicalReserves().getCharge().addPercent(4);
}
pony.getMagicalReserves().getExertion().set(0); pony.getMagicalReserves().getExertion().set(0);
pony.getMagicalReserves().getExhaustion().set(0); pony.getMagicalReserves().getExhaustion().set(0);
mana.set(mana.getMax() * 100); mana.set(mana.getMax() * 100);

View file

@ -36,7 +36,9 @@ import com.google.common.collect.Streams;
import com.minelittlepony.common.util.animation.Interpolator; import com.minelittlepony.common.util.animation.Interpolator;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.enchantment.Enchantments;
import net.minecraft.entity.*; import net.minecraft.entity.*;
import net.minecraft.entity.attribute.DefaultAttributeContainer; import net.minecraft.entity.attribute.DefaultAttributeContainer;
import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.damage.DamageSource;
@ -359,6 +361,7 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
.orElseGet(this::getSpecies).composite( .orElseGet(this::getSpecies).composite(
AmuletSelectors.UNICORN_AMULET.test(entity) ? Race.UNICORN AmuletSelectors.UNICORN_AMULET.test(entity) ? Race.UNICORN
: AmuletSelectors.ALICORN_AMULET.test(entity) ? Race.ALICORN : AmuletSelectors.ALICORN_AMULET.test(entity) ? Race.ALICORN
: AmuletSelectors.PEARL_NECKLACE.test(entity) ? Race.SEAPONY
: null : null
); );
} }
@ -428,6 +431,23 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
} }
} }
if (getCompositeRace().includes(Race.SEAPONY)) {
if (entity.isSubmergedInWater()) {
if (entity.getVelocity().lengthSquared() > 0.02) {
spawnParticles(ParticleTypes.BUBBLE, 4);
}
} else {
if (entity.getAir() == entity.getMaxAir()) {
entity.setAir(entity.getAir() - 1);
}
if (entity.getAir() == -20) {
entity.setAir(0);
entity.damage(entity.getDamageSources().dryOut(), 2);
}
}
}
return super.beforeUpdate(); return super.beforeUpdate();
} }
@ -568,6 +588,14 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
if (getObservedSpecies() == Race.KIRIN) { if (getObservedSpecies() == Race.KIRIN) {
return Optional.of(speed.multiply(0.5, 1, 0.5)); return Optional.of(speed.multiply(0.5, 1, 0.5));
} }
if (getCompositeRace().includes(Race.SEAPONY)) {
float factor = entity.isSwimming() ? 1.132F : 1.232F;
return Optional.of(new Vec3d(
speed.x * factor,
speed.y * 1.101,
speed.z * factor
));
}
return Optional.empty(); return Optional.empty();
} }
@ -595,6 +623,18 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
return false; return false;
} }
public int getImplicitEnchantmentLevel(Enchantment enchantment) {
if ((enchantment == Enchantments.AQUA_AFFINITY
|| enchantment == Enchantments.DEPTH_STRIDER
|| enchantment == Enchantments.LUCK_OF_THE_SEA
|| enchantment == Enchantments.LURE) && getCompositeRace().includes(Race.SEAPONY)) {
return 3;
}
return 0;
}
public Optional<Float> modifyDamage(DamageSource cause, float amount) { public Optional<Float> modifyDamage(DamageSource cause, float amount) {
if (getObservedSpecies() == Race.KIRIN) { if (getObservedSpecies() == Race.KIRIN) {

View file

@ -197,6 +197,9 @@ public interface UItems {
.maxCount(1) .maxCount(1)
.maxDamage(890) .maxDamage(890)
.rarity(Rarity.UNCOMMON), 0), ItemGroups.TOOLS); .rarity(Rarity.UNCOMMON), 0), ItemGroups.TOOLS);
AmuletItem PEARL_NECKLACE = register("pearl_necklace", new AmuletItem(new FabricItemSettings()
.maxCount(1)
.rarity(Rarity.UNCOMMON), 0), ItemGroups.TOOLS);
GlassesItem SUNGLASSES = register("sunglasses", new GlassesItem(new FabricItemSettings().maxCount(1)), ItemGroups.COMBAT); GlassesItem SUNGLASSES = register("sunglasses", new GlassesItem(new FabricItemSettings().maxCount(1)), ItemGroups.COMBAT);
GlassesItem BROKEN_SUNGLASSES = register("broken_sunglasses", new GlassesItem(new FabricItemSettings().maxCount(1)), ItemGroups.COMBAT); GlassesItem BROKEN_SUNGLASSES = register("broken_sunglasses", new GlassesItem(new FabricItemSettings().maxCount(1)), ItemGroups.COMBAT);
@ -213,6 +216,7 @@ public interface UItems {
Item BAT_BADGE = register(Race.BAT); Item BAT_BADGE = register(Race.BAT);
Item CHANGELING_BADGE = register(Race.CHANGELING); Item CHANGELING_BADGE = register(Race.CHANGELING);
Item KIRIN_BADGE = register(Race.KIRIN); Item KIRIN_BADGE = register(Race.KIRIN);
Item HIPPOGRIFF_BADGE = register(Race.HIPPOGRIFF);
private static <T extends Item> T register(String name, T item, RegistryKey<ItemGroup> group) { private static <T extends Item> T register(String name, T item, RegistryKey<ItemGroup> group) {
return ItemGroupRegistry.register(Unicopia.id(name), item, group); return ItemGroupRegistry.register(Unicopia.id(name), item, group);

View file

@ -22,12 +22,14 @@ public interface Toxics {
Toxic EDIBLE = register("edible", new Toxic.Builder(Ailment.INNERT) Toxic EDIBLE = register("edible", new Toxic.Builder(Ailment.INNERT)
.with(Race.CHANGELING, of(FAIR, LOVE_SICKNESS)) .with(Race.CHANGELING, of(FAIR, LOVE_SICKNESS))
.with(Race.SEAPONY, of(FAIR, FOOD_POISONING))
); );
Toxic FORAGE_EDIBLE = register("forage_edible", new Toxic.Builder(Ailment.INNERT) Toxic FORAGE_EDIBLE = register("forage_edible", new Toxic.Builder(Ailment.INNERT)
.food(UFoodComponents.RANDOM_FOLIAGE) .food(UFoodComponents.RANDOM_FOLIAGE)
.with(Race.HUMAN, of(LETHAL, FOOD_POISONING)) .with(Race.HUMAN, of(LETHAL, FOOD_POISONING))
.with(Race.CHANGELING, of(FAIR, LOVE_SICKNESS)) .with(Race.CHANGELING, of(FAIR, LOVE_SICKNESS))
.with(Race.SEAPONY, of(FAIR, FOOD_POISONING))
); );
Toxic FORAGE_EDIBLE_FILLING = register("forage_edible_filling", new Toxic.Builder(Ailment.INNERT) Toxic FORAGE_EDIBLE_FILLING = register("forage_edible_filling", new Toxic.Builder(Ailment.INNERT)
@ -59,6 +61,7 @@ public interface Toxics {
Toxic FORAGE_PRICKLY = register("forage_prickly", new Toxic.Builder(of(SAFE, INSTANT_DAMAGE.withChance(30))) Toxic FORAGE_PRICKLY = register("forage_prickly", new Toxic.Builder(of(SAFE, INSTANT_DAMAGE.withChance(30)))
.food(UFoodComponents.RANDOM_FOLIAGE) .food(UFoodComponents.RANDOM_FOLIAGE)
.with(Race.HUMAN, of(LETHAL, FOOD_POISONING)) .with(Race.HUMAN, of(LETHAL, FOOD_POISONING))
.with(Race.HIPPOGRIFF, Ailment.INNERT)
.with(Race.KIRIN, Ailment.INNERT) .with(Race.KIRIN, Ailment.INNERT)
); );
Toxic FORAGE_STRENGHTENING = register("forage_strengthening", new Toxic.Builder(of(SEVERE, STRENGTH.and(FOOD_POISONING))) Toxic FORAGE_STRENGHTENING = register("forage_strengthening", new Toxic.Builder(of(SEVERE, STRENGTH.and(FOOD_POISONING)))
@ -94,6 +97,7 @@ public interface Toxics {
Toxic COOKED_MEAT = register("cooked_meat", new Toxic.Builder(of(FAIR, FOOD_POISONING)) Toxic COOKED_MEAT = register("cooked_meat", new Toxic.Builder(of(FAIR, FOOD_POISONING))
.with(Race.HUMAN, Ailment.INNERT) .with(Race.HUMAN, Ailment.INNERT)
.with(Race.CHANGELING, Ailment.INNERT) .with(Race.CHANGELING, Ailment.INNERT)
.with(Race.HIPPOGRIFF, of(MILD, FOOD_POISONING))
.with(Race.BAT, Ailment.INNERT) .with(Race.BAT, Ailment.INNERT)
.with(Race.KIRIN, Ailment.INNERT) .with(Race.KIRIN, Ailment.INNERT)
); );
@ -101,12 +105,16 @@ public interface Toxics {
Toxic RAW_FISH = register("raw_fish", new Toxic.Builder(of(FAIR, FOOD_POISONING.and(CHANCE_OF_POISON))) Toxic RAW_FISH = register("raw_fish", new Toxic.Builder(of(FAIR, FOOD_POISONING.and(CHANCE_OF_POISON)))
.with(Race.HUMAN, Ailment.INNERT) .with(Race.HUMAN, Ailment.INNERT)
.with(Race.PEGASUS, of(MILD, FOOD_POISONING)) .with(Race.PEGASUS, of(MILD, FOOD_POISONING))
.with(Race.HIPPOGRIFF, Ailment.INNERT)
.with(Race.SEAPONY, Ailment.INNERT)
.with(Race.ALICORN, Ailment.INNERT) .with(Race.ALICORN, Ailment.INNERT)
.with(Race.CHANGELING, of(FAIR, LOVE_SICKNESS)) .with(Race.CHANGELING, of(FAIR, LOVE_SICKNESS))
); );
Toxic COOKED_FISH = register("cooked_fish", new Toxic.Builder(of(MILD, FOOD_POISONING)) Toxic COOKED_FISH = register("cooked_fish", new Toxic.Builder(of(MILD, FOOD_POISONING))
.with(Race.HUMAN, Ailment.INNERT) .with(Race.HUMAN, Ailment.INNERT)
.with(Race.PEGASUS, Ailment.INNERT) .with(Race.PEGASUS, Ailment.INNERT)
.with(Race.HIPPOGRIFF, Ailment.INNERT)
.with(Race.SEAPONY, Ailment.INNERT)
.with(Race.ALICORN, Ailment.INNERT) .with(Race.ALICORN, Ailment.INNERT)
.with(Race.CHANGELING, of(FAIR, LOVE_SICKNESS)) .with(Race.CHANGELING, of(FAIR, LOVE_SICKNESS))
); );
@ -129,6 +137,7 @@ public interface Toxics {
Toxic PINECONE = register("pinecone", new Toxic.Builder(of(Toxicity.SAFE, Toxin.healing(1))) Toxic PINECONE = register("pinecone", new Toxic.Builder(of(Toxicity.SAFE, Toxin.healing(1)))
.with(Race.HUMAN, Ailment.INNERT) .with(Race.HUMAN, Ailment.INNERT)
.with(Race.HIPPOGRIFF, of(Toxicity.SAFE, Toxin.healing(3)))
); );
Toxic BAT_PONYS_DELIGHT = register("bat_ponys_delight", new Toxic.Builder(Ailment.INNERT) Toxic BAT_PONYS_DELIGHT = register("bat_ponys_delight", new Toxic.Builder(Ailment.INNERT)

View file

@ -0,0 +1,25 @@
package com.minelittlepony.unicopia.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.LivingEntity;
@Mixin(EnchantmentHelper.class)
abstract class MixinEnchantmentHelper {
@Inject(method = "getEquipmentLevel", at = @At("RETURN"), cancellable = true)
private static void getEquipmentLevel(Enchantment enchantment, LivingEntity entity, CallbackInfoReturnable<Integer> info) {
Pony.of(entity).ifPresent(pony -> {
int implicitLevel = pony.getImplicitEnchantmentLevel(enchantment);
if (implicitLevel > 0) {
info.setReturnValue(implicitLevel + info.getReturnValue());
}
});
}
}

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.mixin; package com.minelittlepony.unicopia.mixin;
import java.util.Set;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@ -17,9 +19,15 @@ import com.minelittlepony.unicopia.entity.duck.EntityDuck;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.Entity.PositionUpdater; import net.minecraft.entity.Entity.PositionUpdater;
import net.minecraft.entity.Entity.RemovalReason; import net.minecraft.entity.Entity.RemovalReason;
import net.minecraft.fluid.Fluid;
import net.minecraft.registry.tag.TagKey;
@Mixin(Entity.class) @Mixin(Entity.class)
abstract class MixinEntity implements EntityDuck { abstract class MixinEntity implements EntityDuck {
@Override
@Accessor("submergedFluidTag")
public abstract Set<TagKey<Fluid>> getSubmergedFluidTags();
@Override @Override
@Accessor @Accessor
public abstract void setRemovalReason(RemovalReason reason); public abstract void setRemovalReason(RemovalReason reason);

View file

@ -11,6 +11,7 @@ import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.entity.duck.PlayerEntityDuck; import com.minelittlepony.unicopia.entity.duck.PlayerEntityDuck;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.entity.Equine; import com.minelittlepony.unicopia.entity.Equine;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.mojang.datafixers.util.Either; import com.mojang.datafixers.util.Either;
@ -114,4 +115,20 @@ abstract class MixinPlayerEntity extends LivingEntity implements Equine.Containe
private void onGetBlockBreakingSpeed(BlockState state, CallbackInfoReturnable<Float> info) { private void onGetBlockBreakingSpeed(BlockState state, CallbackInfoReturnable<Float> info) {
info.setReturnValue(info.getReturnValue() * get().getBlockBreakingSpeed()); info.setReturnValue(info.getReturnValue() * get().getBlockBreakingSpeed());
} }
@Override
protected int getNextAirUnderwater(int air) {
if (EquinePredicates.PLAYER_SEAPONY.test(this)) {
return super.getNextAirOnLand(air);
}
return super.getNextAirUnderwater(air);
}
@Override
protected int getNextAirOnLand(int air) {
if (EquinePredicates.PLAYER_SEAPONY.test(this)) {
return super.getNextAirUnderwater(air);
}
return super.getNextAirOnLand(air);
}
} }

View file

@ -0,0 +1,25 @@
package com.minelittlepony.unicopia.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.passive.FishEntity;
import net.minecraft.entity.passive.PufferfishEntity;
@Mixin(PufferfishEntity.class)
abstract class MixinPufferfishEntity extends FishEntity {
MixinPufferfishEntity() { super(null, null); }
@Inject(method = "method_6591(Lnet/minecraft/class_1309;)Z", at = @At("HEAD"), cancellable = true)
private static void onShouldTarget(LivingEntity entity, CallbackInfoReturnable<Boolean> info) {
Pony.of(entity).filter(pony -> pony.getCompositeRace().includes(Race.SEAPONY)).ifPresent(pony -> {
info.setReturnValue(false);
});
}
}

View file

@ -8,6 +8,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.client.UnicopiaClient; import com.minelittlepony.unicopia.client.UnicopiaClient;
import net.minecraft.client.render.Camera; import net.minecraft.client.render.Camera;
import net.minecraft.client.render.CameraSubmersionType;
@Mixin(Camera.class) @Mixin(Camera.class)
abstract class MixinCamera { abstract class MixinCamera {
@ -33,4 +34,11 @@ abstract class MixinCamera {
private void redirectCameraDistance(double initial, CallbackInfoReturnable<Double> info) { private void redirectCameraDistance(double initial, CallbackInfoReturnable<Double> info) {
UnicopiaClient.getCamera().flatMap(c -> c.calculateDistance(info.getReturnValueD())).ifPresent(info::setReturnValue); UnicopiaClient.getCamera().flatMap(c -> c.calculateDistance(info.getReturnValueD())).ifPresent(info::setReturnValue);
} }
@Inject(method = "getSubmersionType",
at = @At("RETURN"),
cancellable = true)
public void getSubmersionType(CallbackInfoReturnable<CameraSubmersionType> info) {
UnicopiaClient.getCamera().flatMap(c -> c.getSubmersionType(info.getReturnValue())).ifPresent(info::setReturnValue);
}
} }

View file

@ -6,43 +6,27 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.client.gui.HudEffects;
import com.minelittlepony.unicopia.client.gui.UHud; import com.minelittlepony.unicopia.client.gui.UHud;
import com.minelittlepony.unicopia.entity.effect.EffectUtils;
import com.minelittlepony.unicopia.entity.effect.UEffects;
import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.hud.InGameHud; import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
@Mixin(InGameHud.class) @Mixin(InGameHud.class)
abstract class MixinInGameHud { abstract class MixinInGameHud {
private boolean addedHunger;
@Shadow @Shadow
abstract PlayerEntity getCameraPlayer(); abstract PlayerEntity getCameraPlayer();
@Inject(method = "render(Lnet/minecraft/client/gui/DrawContext;F)V", at = @At("HEAD")) @Inject(method = "render(Lnet/minecraft/client/gui/DrawContext;F)V", at = @At("HEAD"))
private void onRender(DrawContext context, float tickDelta, CallbackInfo info) { private void onRender(DrawContext context, float tickDelta, CallbackInfo info) {
PlayerEntity player = getCameraPlayer(); HudEffects.tryApply(getCameraPlayer(), tickDelta, true);
if (player != null && !player.hasStatusEffect(StatusEffects.HUNGER) && EffectUtils.getAmplifier(player, UEffects.FOOD_POISONING) > 0) {
addedHunger = true;
player.addStatusEffect(new StatusEffectInstance(StatusEffects.HUNGER, 1, 1, false, false));
}
UHud.INSTANCE.render((InGameHud)(Object)this, context, tickDelta); UHud.INSTANCE.render((InGameHud)(Object)this, context, tickDelta);
} }
@Inject(method = "render(Lnet/minecraft/client/gui/DrawContext;F)V", at = @At("RETURN")) @Inject(method = "render(Lnet/minecraft/client/gui/DrawContext;F)V", at = @At("RETURN"))
private void afterRender(DrawContext context, float tickDelta, CallbackInfo info) { private void afterRender(DrawContext context, float tickDelta, CallbackInfo info) {
if (addedHunger) { HudEffects.tryApply(getCameraPlayer(), tickDelta, false);
addedHunger = false;
PlayerEntity player = getCameraPlayer();
if (player != null) {
player.removeStatusEffect(StatusEffects.HUNGER);
}
}
} }
} }

View file

@ -40,6 +40,11 @@ abstract class MixinKeyboardInput extends Input {
movementForward = 0; movementForward = 0;
jumping = false; jumping = false;
} }
if (player.getAcrobatics().isImmobile()) {
movementSideways = 0;
movementForward = 0;
}
} }
} }
} }

View file

@ -39,7 +39,7 @@ abstract class MixinModelPart implements Hookable {
isHeadPart = true; isHeadPart = true;
} }
@Inject(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;IIFFFF)V", at = @At("RETURN")) @Inject(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;IIFFFF)V", at = @At("HEAD"))
public void render(MatrixStack matrices, VertexConsumer vertices, int light, int overlay, float red, float green, float blue, float alpha, CallbackInfo info) { public void render(MatrixStack matrices, VertexConsumer vertices, int light, int overlay, float red, float green, float blue, float alpha, CallbackInfo info) {
if (visible && isHeadPart) { if (visible && isHeadPart) {
ModelPartHooks.onHeadRendered((ModelPart)(Object)this, matrices); ModelPartHooks.onHeadRendered((ModelPart)(Object)this, matrices);

View file

@ -52,6 +52,7 @@
"item.unicopia.changeling_badge": "Changeling Emblem", "item.unicopia.changeling_badge": "Changeling Emblem",
"item.unicopia.bat_badge": "Bat Pony Emblem", "item.unicopia.bat_badge": "Bat Pony Emblem",
"item.unicopia.kirin_badge": "Kirin Emblem", "item.unicopia.kirin_badge": "Kirin Emblem",
"item.unicopia.hippogriff_badge": "Hippogriff Emblem",
"item.unicopia.butterfly_spawn_egg": "Butterfly Spawn Egg", "item.unicopia.butterfly_spawn_egg": "Butterfly Spawn Egg",
"item.unicopia.butterfly": "Butterfly", "item.unicopia.butterfly": "Butterfly",
@ -141,13 +142,16 @@
"item.unicopia.alicorn_amulet": "Alicorn Amulet", "item.unicopia.alicorn_amulet": "Alicorn Amulet",
"item.unicopia.alicorn_amulet.lore": "Time worn: %d", "item.unicopia.alicorn_amulet.lore": "Time worn: %d",
"item.unicopia.pearl_necklace": "Pearl Necklace",
"item.unicopia.pearl_necklace.lore": "Gives underwater abilities to the wearer",
"item.unicopia.horse_shoe.accuracy": "Accuracy: %d%%", "item.unicopia.horse_shoe.accuracy": "Accuracy: %d%%",
"item.unicopia.horse_shoe.speed": "Speed: %d", "item.unicopia.horse_shoe.speed": "Speed: %d",
"item.unicopia.iron_horse_shoe": "Iron Horse Shoe", "item.unicopia.iron_horse_shoe": "Iron Horse Shoe",
"item.unicopia.golden_horse_shoe": "Golden Horse Shoe", "item.unicopia.golden_horse_shoe": "Golden Horse Shoe",
"item.unicopia.copper_horse_shoe": "Copper Horse Shoe", "item.unicopia.copper_horse_shoe": "Copper Horse Shoe",
"item.unicopia.netherite_horse_shoe": "Netherite Horse Shoe", "item.unicopia.netherite_horse_shoe": "Netherite Horse Shoe",
"item.unicopia.broken_alicorn_amulet": "Broken Alicorn Amulet", "item.unicopia.broken_alicorn_amulet": "Broken Alicorn Amulet",
"item.unicopia.unicorn_amulet": "Unicorn Amulet", "item.unicopia.unicorn_amulet": "Unicorn Amulet",
"item.unicopia.unicorn_amulet.lore": "Grants magical abilities to whoever wears it", "item.unicopia.unicorn_amulet.lore": "Grants magical abilities to whoever wears it",
@ -344,6 +348,12 @@
"item.minecraft.lingering_potion.effect.unicopia.tribe_swap_kirin": "Lingering Potion of Kirin Metamorphosis", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_kirin": "Lingering Potion of Kirin Metamorphosis",
"item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_kirin": "Arrow of Kirin Metamorphosis", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_kirin": "Arrow of Kirin Metamorphosis",
"effect.unicopia.change_race_hippogriff": "Hippogriff Metamorphosis",
"item.minecraft.potion.effect.unicopia.tribe_swap_hippogriff": "Potion of Hippogriff Metamorphosis",
"item.minecraft.splash_potion.effect.unicopia.tribe_swap_hippogriff": "Splash Potion of Hippogriff Metamorphosis",
"item.minecraft.lingering_potion.effect.unicopia.tribe_swap_hippogriff": "Lingering Potion of Hippogriff Metamorphosis",
"item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_hippogriff": "Arrow of Hippogriff Metamorphosis",
"potion.potency.6": "VII", "potion.potency.6": "VII",
"spell.unicopia.frost": "Frost", "spell.unicopia.frost": "Frost",
@ -480,6 +490,11 @@
"ability.unicopia.rainboom": "Sonic Rainboom", "ability.unicopia.rainboom": "Sonic Rainboom",
"ability.unicopia.rage": "Rage", "ability.unicopia.rage": "Rage",
"ability.unicopia.nirik_blast": "Nirik Blast", "ability.unicopia.nirik_blast": "Nirik Blast",
"ability.unicopia.screech": "Screech",
"ability.unicopia.peck": "Peck/Squawk",
"ability.unicopia.peck.block.fled": "The block has fled",
"ability.unicopia.peck.block.unfased": "The block is unfazed by your threats",
"ability.unicopia.dash": "Flying Dash",
"gui.unicopia.trait.label": "Element of %s", "gui.unicopia.trait.label": "Element of %s",
"gui.unicopia.trait.group": "\n %s", "gui.unicopia.trait.group": "\n %s",
@ -511,6 +526,7 @@
"gui.unicopia.tribe_selection.describe.unicopia.pegasus": "Join the Pegasus Tribe, soar with the Wonderbolts", "gui.unicopia.tribe_selection.describe.unicopia.pegasus": "Join the Pegasus Tribe, soar with the Wonderbolts",
"gui.unicopia.tribe_selection.describe.unicopia.bat": "Join the Bat Tribe, become the darkest night", "gui.unicopia.tribe_selection.describe.unicopia.bat": "Join the Bat Tribe, become the darkest night",
"gui.unicopia.tribe_selection.describe.unicopia.kirin": "Join the Kirin's Village, take the vow of silence", "gui.unicopia.tribe_selection.describe.unicopia.kirin": "Join the Kirin's Village, take the vow of silence",
"gui.unicopia.tribe_selection.describe.unicopia.hippogriff": "Join Hippogriff Tibe, screech at your neighbors",
"gui.unicopia.tribe_selection.describe.unicopia.changeling": "Join the Changeling Hive, your Queen demands it", "gui.unicopia.tribe_selection.describe.unicopia.changeling": "Join the Changeling Hive, your Queen demands it",
"gui.unicopia.tribe_selection.confirm": "You have selected %s", "gui.unicopia.tribe_selection.confirm": "You have selected %s",
@ -551,6 +567,11 @@
"gui.unicopia.tribe_selection.confirm.goods.3.unicopia.kirin": " - Is silent in their default state, and can move freely around the warden", "gui.unicopia.tribe_selection.confirm.goods.3.unicopia.kirin": " - Is silent in their default state, and can move freely around the warden",
"gui.unicopia.tribe_selection.confirm.goods.4.unicopia.kirin": " - Can eat fruits and vegetables, red meats, and some foraged items normally toxic to other races", "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.kirin": " - Can eat fruits and vegetables, red meats, and some foraged items normally toxic to other races",
"gui.unicopia.tribe_selection.confirm.goods.1.unicopia.hippogriff": " - Flight and the ability to train to build endurance",
"gui.unicopia.tribe_selection.confirm.goods.2.unicopia.hippogriff": " - Can dash whilst flying",
"gui.unicopia.tribe_selection.confirm.goods.3.unicopia.hippogriff": " - Has a sharp and noisy beak for screeching and pecking",
"gui.unicopia.tribe_selection.confirm.goods.4.unicopia.hippogriff": " - Loves stairs, fish, and meat, but mostly stairs",
"gui.unicopia.tribe_selection.confirm.bads": "but they...", "gui.unicopia.tribe_selection.confirm.bads": "but they...",
"gui.unicopia.tribe_selection.confirm.bads.1.unicopia.human": " - Have no magical abilities what-so-ever", "gui.unicopia.tribe_selection.confirm.bads.1.unicopia.human": " - Have no magical abilities what-so-ever",
@ -586,6 +607,11 @@
"gui.unicopia.tribe_selection.confirm.bads.3.unicopia.kirin": " - Lighter than other ponies, and might take increased knockback", "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.4.unicopia.kirin": " - Doesn't like water",
"gui.unicopia.tribe_selection.confirm.bads.1.unicopia.hippogriff": " - Weak to brute force attacks",
"gui.unicopia.tribe_selection.confirm.bads.2.unicopia.hippogriff": " - Must rest between flights to regain their strength",
"gui.unicopia.tribe_selection.confirm.bads.3.unicopia.hippogriff": " - Has a bird brain",
"gui.unicopia.tribe_selection.confirm.bads.4.unicopia.hippogriff": "",
"gui.unicopia.tribe_selection.join": "Join Tribe", "gui.unicopia.tribe_selection.join": "Join Tribe",
"gui.unicopia.tribe_selection.cancel": "Go Back", "gui.unicopia.tribe_selection.cancel": "Go Back",
@ -811,6 +837,10 @@
"unicopia.race.bat.alt": "Bat Ponies", "unicopia.race.bat.alt": "Bat Ponies",
"unicopia.race.kirin": "Kirin", "unicopia.race.kirin": "Kirin",
"unicopia.race.kirin.alt": "Kirins", "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",
"death.attack.unicopia.generic.and_also": "%1$s and %2$s", "death.attack.unicopia.generic.and_also": "%1$s and %2$s",
"death.attack.unicopia.generic.whilst_flying": "%1$s whilst flying", "death.attack.unicopia.generic.whilst_flying": "%1$s whilst flying",
@ -932,6 +962,7 @@
"unicopia.subtitle.entity.player.kick": "Player kicks", "unicopia.subtitle.entity.player.kick": "Player kicks",
"unicopia.subtitle.magic_aura": "Magic humming", "unicopia.subtitle.magic_aura": "Magic humming",
"unicopia.subtitle.player.rebound": "Player Bounces Off Wall", "unicopia.subtitle.player.rebound": "Player Bounces Off Wall",
"unicopia.subtitle.screech": "Player Screeches",
"unicopia.subtitle.ears_ringing": "Ears ringing", "unicopia.subtitle.ears_ringing": "Ears ringing",
"unicopia.subtitle.heartbeat": "Heart beats", "unicopia.subtitle.heartbeat": "Heart beats",
"unicopia.subtitle.entity.artefact.ambient": "Magic hums", "unicopia.subtitle.entity.artefact.ambient": "Magic hums",

View file

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

View file

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

View file

@ -29,6 +29,13 @@
} }
] ]
}, },
"entity.player.hippogriff.screech": {
"category": "player",
"subtitle": "unicopia.subtitle.screech",
"sounds": [
"unicopia:screech/screech0"
]
},
"item.magic.aura": { "item.magic.aura": {
"category": "player", "category": "player",
"subtitle": "unicopia.subtitle.magic_aura", "subtitle": "unicopia.subtitle.magic_aura",

Binary file not shown.

After

Width:  |  Height:  |  Size: 7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View file

@ -3,6 +3,7 @@
"values": [ "values": [
"unicopia:alicorn_amulet", "unicopia:alicorn_amulet",
"unicopia:pegasus_amulet", "unicopia:pegasus_amulet",
"unicopia:unicorn_amulet" "unicopia:unicorn_amulet",
"unicopia:pearl_necklace"
] ]
} }

View file

@ -17,6 +17,7 @@
"MixinChunkBlockLightProvider", "MixinChunkBlockLightProvider",
"MutableBlockLightStorage", "MutableBlockLightStorage",
"MixinDamageSource", "MixinDamageSource",
"MixinEnchantmentHelper",
"MixinFallLocation", "MixinFallLocation",
"MixinEntity", "MixinEntity",
"MixinEntityShapeContext", "MixinEntityShapeContext",
@ -34,6 +35,7 @@
"MixinPlayerManager", "MixinPlayerManager",
"MixinPowderSnowBlock", "MixinPowderSnowBlock",
"MixinProjectileEntity", "MixinProjectileEntity",
"MixinPufferfishEntity",
"MixinServerPlayerEntity", "MixinServerPlayerEntity",
"MixinServerPlayNetworkHandler", "MixinServerPlayNetworkHandler",
"MixinServerWorld", "MixinServerWorld",