diff --git a/src/main/java/com/minelittlepony/unicopia/Availability.java b/src/main/java/com/minelittlepony/unicopia/Availability.java new file mode 100644 index 00000000..f24497a3 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/Availability.java @@ -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; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/EquinePredicates.java b/src/main/java/com/minelittlepony/unicopia/EquinePredicates.java index 0771db9d..be559b38 100644 --- a/src/main/java/com/minelittlepony/unicopia/EquinePredicates.java +++ b/src/main/java/com/minelittlepony/unicopia/EquinePredicates.java @@ -27,6 +27,7 @@ public interface EquinePredicates { Predicate PLAYER_CHANGELING = IS_PLAYER.and(ofRace(Race.CHANGELING)); Predicate PLAYER_KIRIN = IS_PLAYER.and(ofRace(Race.KIRIN)); Predicate PLAYER_PEGASUS = IS_PLAYER.and(e -> ((PlayerEntity)e).getAbilities().creativeMode || RACE_INTERACT_WITH_CLOUDS.test(e)); + Predicate PLAYER_SEAPONY = IS_PLAYER.and(raceMatches(Race::isFish)); Predicate PLAYER_CAN_USE_EARTH = IS_PLAYER.and(raceMatches(Race::canUseEarth)); Predicate IS_CASTER = e -> !e.isRemoved() && (e instanceof Caster || IS_PLAYER.test(e)); diff --git a/src/main/java/com/minelittlepony/unicopia/Race.java b/src/main/java/com/minelittlepony/unicopia/Race.java index e45fec10..1a214161 100644 --- a/src/main/java/com/minelittlepony/unicopia/Race.java +++ b/src/main/java/com/minelittlepony/unicopia/Race.java @@ -24,18 +24,18 @@ import net.minecraft.util.Identifier; import net.minecraft.registry.Registry; import net.minecraft.registry.RegistryKey; -public record Race (Supplier compositeSupplier, boolean canCast, FlightType flightType, boolean canUseEarth, boolean isNocturnal, boolean canHang) implements Affine { +public record Race (Supplier 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 Registry REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("race"), DEFAULT_ID); public static final RegistryKey> REGISTRY_KEY = REGISTRY.getKey(); 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) { - return register(Unicopia.id(name), magic, flight, earth, nocturnal, canHang); + 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, 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)); + 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)), availability, magic, flight, earth, nocturnal, canHang)); } public static RegistryKeyArgumentType argument() { @@ -46,15 +46,17 @@ public record Race (Supplier compositeSupplier, boolean canCast, Flig * The default, unset race. * 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 HUMAN = register("human", false, FlightType.NONE, false, false, false); - public static final Race EARTH = register("earth", false, FlightType.NONE, true, false, false); - public static final Race UNICORN = register("unicorn", true, FlightType.NONE, false, false, false); - public static final Race PEGASUS = register("pegasus", false, FlightType.AVIAN, false, false, false); - public static final Race BAT = register("bat", false, FlightType.AVIAN, false, true, true); - public static final Race ALICORN = register("alicorn", true, FlightType.AVIAN, true, false, false); - public static final Race CHANGELING = register("changeling", false, FlightType.INSECTOID, false, false, true); - public static final Race KIRIN = register("kirin", true, 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", 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 void bootstrap() {} @@ -83,6 +85,10 @@ public record Race (Supplier compositeSupplier, boolean canCast, Flig return !isHuman(); } + public boolean isFish() { + return this == SEAPONY; + } + public boolean isHuman() { return this == UNSET || this == HUMAN; } @@ -91,16 +97,12 @@ public record Race (Supplier compositeSupplier, boolean canCast, Flig return !isNocturnal(); } - public boolean isOp() { - return this == ALICORN; - } - public boolean canFly() { return !flightType().isGrounded(); } public boolean canInteractWithClouds() { - return canFly() && this != CHANGELING && this != BAT; + return canFly() && this != CHANGELING && this != BAT && this != HIPPOGRIFF; } public Identifier getId() { diff --git a/src/main/java/com/minelittlepony/unicopia/USounds.java b/src/main/java/com/minelittlepony/unicopia/USounds.java index 34d9beff..ec580e36 100644 --- a/src/main/java/com/minelittlepony/unicopia/USounds.java +++ b/src/main/java/com/minelittlepony/unicopia/USounds.java @@ -14,6 +14,8 @@ public interface USounds { SoundEvent ENTITY_PLAYER_CORRUPTION = PARTICLE_SOUL_ESCAPE; 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_PEGASUS_WINGSFLAP = register("entity.player.pegasus.wingsflap"); SoundEvent ENTITY_PLAYER_PEGASUS_FLYING = register("entity.player.pegasus.flying"); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/Abilities.java b/src/main/java/com/minelittlepony/unicopia/ability/Abilities.java index 9dd0d724..f0964765 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/Abilities.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/Abilities.java @@ -42,9 +42,14 @@ public interface Abilities { Ability RAINBOOM = register(new PegasusRainboomAbility(), "rainboom", AbilitySlot.PRIMARY); 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 TOGGLE_FLIGHT = register(new PegasusFlightToggleAbility(), "toggle_flight", AbilitySlot.TERTIARY); + Ability TOGGLE_FLIGHT = register(new ToggleFlightAbility(), "toggle_flight", AbilitySlot.TERTIARY); // changeling Ability DISGUISE = register(new ChangelingDisguiseAbility(), "disguise", AbilitySlot.SECONDARY); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/BatEeeeAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/BatEeeeAbility.java index f2ad4e28..867f67ae 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/BatEeeeAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/BatEeeeAbility.java @@ -1,55 +1,20 @@ package com.minelittlepony.unicopia.ability; -import java.util.Optional; - import com.minelittlepony.unicopia.AwaitTickQueue; -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; /** - * A magic casting ability for unicorns. - * (only shields for now) + * An ability to screeeeeeeeEeEeEeeee! */ -public class BatEeeeAbility implements Ability { +public class BatEeeeAbility extends ScreechAbility { 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 public boolean canUse(Race race) { @@ -57,22 +22,7 @@ public class BatEeeeAbility implements Ability { } @Override - public Optional 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 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; + protected void playSounds(Pony player, Random rng, float strength) { int count = 1 + rng.nextInt(10) + (int)(strength * 10); for (int i = 0; i < count; i++) { @@ -81,6 +31,7 @@ public class BatEeeeAbility implements Ability { 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 k = 0; k < count; k++) { AwaitTickQueue.scheduleTask(player.asWorld(), w -> { @@ -88,59 +39,14 @@ public class BatEeeeAbility implements Ability { (0.9F + (rng.nextFloat() - 0.5F) / 2F) * strength, 1.6F + (rng.nextFloat() - 0.5F) ); + player.asWorld().emitGameEvent(player.asEntity(), GameEvent.ENTITY_ROAR, player.asEntity().getEyePos()); }, 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) { player.asEntity().damage(player.damageOf(UDamageTypes.BAT_SCREECH, player), 0.1F); 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)); - } } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/FlyingDashAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/FlyingDashAbility.java new file mode 100644 index 00000000..0a9f5f6b --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/ability/FlyingDashAbility.java @@ -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 { + + @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 prepare(Pony player) { + return Hit.of(player.getPhysics().isFlying()); + } + + @Override + public Hit.Serializer getSerializer() { + return Hit.SERIALIZER; + } + + @Override + public double getCostEstimate(Pony player) { + return 0; + } + + @Override + public boolean onQuickAction(Pony player, ActivationType type, Optional 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); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/ability/PeckAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/PeckAbility.java new file mode 100644 index 00000000..849dbb4c --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/ability/PeckAbility.java @@ -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 { + + @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 prepare(Pony player) { + return Hit.INSTANCE; + } + + @Override + public Numeric.Serializer getSerializer() { + return Hit.SERIALIZER; + } + + protected LivingEntity findTarget(PlayerEntity player, World w) { + return TraceHelper.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) { + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/ability/ScreechAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/ScreechAbility.java new file mode 100644 index 00000000..ac244ea0 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/ability/ScreechAbility.java @@ -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 { + 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 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 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) + ); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/ability/PegasusFlightToggleAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/ToggleFlightAbility.java similarity index 97% rename from src/main/java/com/minelittlepony/unicopia/ability/PegasusFlightToggleAbility.java rename to src/main/java/com/minelittlepony/unicopia/ability/ToggleFlightAbility.java index c0736de0..d9811e74 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/PegasusFlightToggleAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/ToggleFlightAbility.java @@ -12,7 +12,7 @@ import com.minelittlepony.unicopia.entity.player.Pony; import net.minecraft.util.Identifier; -public class PegasusFlightToggleAbility implements Ability { +public class ToggleFlightAbility implements Ability { @Override public int getWarmupTime(Pony player) { diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/HudEffects.java b/src/main/java/com/minelittlepony/unicopia/client/gui/HudEffects.java new file mode 100644 index 00000000..2188245d --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/HudEffects.java @@ -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> 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> 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; + } + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/TribeSelectionScreen.java b/src/main/java/com/minelittlepony/unicopia/client/gui/TribeSelectionScreen.java index 302c546d..a999236f 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/TribeSelectionScreen.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/TribeSelectionScreen.java @@ -54,7 +54,7 @@ public class TribeSelectionScreen extends GameGui implements HidesHud { final int itemWidth = 70 + 10; - List options = Race.REGISTRY.stream().filter(race -> !race.isHuman() && !race.isOp()).toList(); + List options = Race.REGISTRY.stream().filter(race -> race.availability().isSelectable()).toList(); int columns = Math.min(width / itemWidth, options.size()); diff --git a/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/BodyPartGear.java b/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/BodyPartGear.java index e99a9860..3ec5d015 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/BodyPartGear.java +++ b/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/BodyPartGear.java @@ -15,6 +15,7 @@ import com.minelittlepony.client.model.entity.race.UnicornModel; import com.minelittlepony.client.model.part.UnicornHorn; import com.minelittlepony.mson.api.MsonModel; import com.minelittlepony.unicopia.EquinePredicates; +import com.minelittlepony.unicopia.FlightType; import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.entity.AmuletSelectors; @@ -43,7 +44,7 @@ class BodyPartGear & MsonModel & IModel> public static final Predicate 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 Predicate PEGA_WINGS_PREDICATE = MINE_LP_HAS_NO_WINGS.and(AmuletSelectors.PEGASUS_AMULET.or(EquinePredicates.raceMatches(Race::canInteractWithClouds))); + public static final Predicate 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 BodyPartGear pegasusWings() { diff --git a/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/Main.java b/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/Main.java index 1639d96b..4757d7a3 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/Main.java +++ b/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/Main.java @@ -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.ZEBRA, Race.EARTH); 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.SEAPONY, Race.UNICORN); + registerRaceMapping(com.minelittlepony.api.pony.meta.Race.SEAPONY, Race.SEAPONY); } private void onPonyModelPrepared(Entity entity, IModel model, ModelAttributes.Mode mode) { diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/ModelPartHooks.java b/src/main/java/com/minelittlepony/unicopia/client/render/ModelPartHooks.java index b892836b..21401622 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/ModelPartHooks.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/ModelPartHooks.java @@ -32,7 +32,6 @@ public class ModelPartHooks { final var bestCandidate = new EnqueudHeadRender(); - matrices.push(); part.forEachCuboid(matrices, (entry, name, index, cube) -> { float x = cube.maxX - cube.minX; float y = cube.maxY - cube.minY; @@ -47,7 +46,6 @@ public class ModelPartHooks { bestCandidate.maxSideLength = Math.max(Math.max(x, z), y); } }); - matrices.pop(); if (bestCandidate.transformation != null) { head.add(bestCandidate); @@ -75,8 +73,6 @@ public class ModelPartHooks { matrices.translate(x * PIXEL_SCALE, y * PIXEL_SCALE, z * PIXEL_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); } } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/SmittenEyesRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/SmittenEyesRenderer.java index 2459266a..9fa33446 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/SmittenEyesRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/SmittenEyesRenderer.java @@ -1,7 +1,6 @@ package com.minelittlepony.unicopia.client.render; import com.minelittlepony.unicopia.Unicopia; -import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate; import com.minelittlepony.unicopia.entity.Creature; 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.math.Box; import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.RotationAxis; public class SmittenEyesRenderer { private static final Identifier TEXTURE = Unicopia.id("textures/entity/smitten_eyes.png"); @@ -42,9 +40,6 @@ public class SmittenEyesRenderer { ModelPartHooks.stopCollecting().forEach(head -> { matrices.push(); 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); matrices.scale(scale, scale, scale); matrices.translate(0, 0.05F, 0); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/WingsFeatureRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/WingsFeatureRenderer.java index b9d33535..10c8ecc3 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/WingsFeatureRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/WingsFeatureRenderer.java @@ -1,5 +1,6 @@ package com.minelittlepony.unicopia.client.render; +import com.minelittlepony.unicopia.FlightType; import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.entity.player.Pony; @@ -50,7 +51,7 @@ public class WingsFeatureRenderer implements AccessoryFe } 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) { diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/WorldRenderDelegate.java b/src/main/java/com/minelittlepony/unicopia/client/render/WorldRenderDelegate.java index 30472c2c..78ca8e93 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/WorldRenderDelegate.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/WorldRenderDelegate.java @@ -4,7 +4,10 @@ import java.util.Optional; import com.minelittlepony.client.util.render.RenderLayerUtil; import com.minelittlepony.unicopia.EquinePredicates; +import com.minelittlepony.unicopia.Race; 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.Equine; import com.minelittlepony.unicopia.entity.ItemImpl; @@ -80,6 +83,27 @@ public class WorldRenderDelegate { 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) { matrices.pop(); @@ -162,6 +186,13 @@ public class WorldRenderDelegate { 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.POSITIVE_Z.rotationDegrees(roll)); @@ -169,6 +200,12 @@ public class WorldRenderDelegate { matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(diveAngle)); 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)) { ModelPartHooks.startCollecting(); } diff --git a/src/main/java/com/minelittlepony/unicopia/command/SpeciesCommand.java b/src/main/java/com/minelittlepony/unicopia/command/SpeciesCommand.java index 6f93995b..1363ee96 100644 --- a/src/main/java/com/minelittlepony/unicopia/command/SpeciesCommand.java +++ b/src/main/java/com/minelittlepony/unicopia/command/SpeciesCommand.java @@ -8,7 +8,6 @@ import com.minelittlepony.unicopia.network.MsgTribeSelect; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import net.minecraft.command.argument.EntityArgumentType; -import net.minecraft.command.argument.RegistryKeyArgumentType; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.CommandManager.RegistrationEnvironment; @@ -32,8 +31,6 @@ class SpeciesCommand { } } - RegistryKeyArgumentType raceArgument = Race.argument(); - return builder .then(CommandManager.literal("get") .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)) )) .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)) .then(CommandManager.argument("target", EntityArgumentType.player()) .executes(context -> set(context.getSource(), EntityArgumentType.getPlayer(context, "target"), Race.fromArgument(context, "race"), false))) )) .then(CommandManager.literal("describe") - .then(CommandManager.argument("race", raceArgument) + .then(CommandManager.argument("race", Race.argument()) .executes(context -> describe(context.getSource().getPlayer(), Race.fromArgument(context, "race"))) )) .then(CommandManager.literal("list") @@ -101,7 +98,7 @@ class SpeciesCommand { boolean first = true; 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(i.getDisplayName()); first = false; diff --git a/src/main/java/com/minelittlepony/unicopia/entity/AmuletSelectors.java b/src/main/java/com/minelittlepony/unicopia/entity/AmuletSelectors.java index 96fc930b..81211527 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/AmuletSelectors.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/AmuletSelectors.java @@ -10,6 +10,7 @@ public interface AmuletSelectors { Predicate ALICORN_AMULET = UItems.ALICORN_AMULET::isApplicable; Predicate PEGASUS_AMULET = UItems.PEGASUS_AMULET::isApplicable; Predicate UNICORN_AMULET = UItems.UNICORN_AMULET::isApplicable; + Predicate PEARL_NECKLACE = UItems.PEARL_NECKLACE::isApplicable; Predicate ALICORN_AMULET_AFTER_1_DAYS = ALICORN_AMULET.and(ItemTracker.wearing(UItems.ALICORN_AMULET, ItemTracker.after(ItemTracker.DAYS))); Predicate ALICORN_AMULET_AFTER_2_DAYS = ALICORN_AMULET.and(ItemTracker.wearing(UItems.ALICORN_AMULET, ItemTracker.after(2 * ItemTracker.DAYS))); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/duck/EntityDuck.java b/src/main/java/com/minelittlepony/unicopia/entity/duck/EntityDuck.java index 0a61652d..659a3a24 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/duck/EntityDuck.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/duck/EntityDuck.java @@ -1,12 +1,18 @@ package com.minelittlepony.unicopia.entity.duck; +import java.util.Set; + import com.minelittlepony.unicopia.compat.pehkui.PehkuiEntityExtensions; import net.minecraft.entity.Entity; import net.minecraft.entity.Entity.RemovalReason; +import net.minecraft.fluid.Fluid; +import net.minecraft.registry.tag.TagKey; public interface EntityDuck extends LavaAffine, PehkuiEntityExtensions { + Set> getSubmergedFluidTags(); + void setRemovalReason(RemovalReason reason); void setVehicle(Entity vehicle); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/effect/RaceChangeStatusEffect.java b/src/main/java/com/minelittlepony/unicopia/entity/effect/RaceChangeStatusEffect.java index f7168730..462936ad 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/effect/RaceChangeStatusEffect.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/effect/RaceChangeStatusEffect.java @@ -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_CHANGELING = register(0xFFFF00, Race.CHANGELING); 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; diff --git a/src/main/java/com/minelittlepony/unicopia/entity/effect/UPotions.java b/src/main/java/com/minelittlepony/unicopia/entity/effect/UPotions.java index 2ff93a59..82f5a0b3 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/effect/UPotions.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/effect/UPotions.java @@ -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_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_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) { REGISTRY.add(potion); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Acrobatics.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Acrobatics.java index 032d4f89..1607c64a 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Acrobatics.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Acrobatics.java @@ -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.Recipient; 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.Tickable; @@ -17,6 +18,8 @@ import net.minecraft.entity.data.TrackedData; import net.minecraft.entity.data.TrackedDataHandlerRegistry; import net.minecraft.entity.player.PlayerEntity; 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.Direction; import net.minecraft.util.math.Vec3d; @@ -42,6 +45,17 @@ public class Acrobatics implements Tickable, NbtSerialisable { 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 public void tick() { BlockPos climbingPos = entity.getClimbingPos().orElse(null); @@ -115,6 +129,11 @@ public class Acrobatics implements Tickable, NbtSerialisable { } else { 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) { diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerAttributes.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerAttributes.java index 1b396f98..8bbccdef 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerAttributes.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerAttributes.java @@ -36,12 +36,23 @@ public class PlayerAttributes implements Tickable { new ToggleableAttribute( 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), - pony -> pony.getCompositeRace().canFly() + pony -> pony.getCompositeRace().canFly() && !pony.getCompositeRace().includes(Race.HIPPOGRIFF) ), new ToggleableAttribute( new EntityAttributeModifier(UUID.fromString("707b50a8-03e8-40f4-8553-ecf67025fd6d"), "Pegasus Reach", 1.5, Operation.ADDITION), 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( diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java index bd070bd7..04b5bf22 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java @@ -3,9 +3,11 @@ package com.minelittlepony.unicopia.entity.player; import java.util.Optional; 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.spell.AbstractDisguiseSpell; +import net.minecraft.client.render.CameraSubmersionType; import net.minecraft.util.math.Vec3d; public class PlayerCamera extends MotionCompositor { @@ -35,6 +37,11 @@ public class PlayerCamera extends MotionCompositor { roll = player.getInterpolator().interpolate("roll", (float)roll, 15); } + + if (player.getAcrobatics().isFloppy()) { + roll += 90; + } + return (float)roll; } @@ -54,6 +61,15 @@ public class PlayerCamera extends MotionCompositor { .map(d -> distance * d); } + public Optional 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) { fov += player.getMagicalReserves().getExertion().get() / 5F; fov += getEnergyAddition(); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java index ca497829..bb9b68e9 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java @@ -414,7 +414,7 @@ public class PlayerPhysics extends EntityPhysics implements Tickab if (type.isAvian()) { 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); UCriteria.SHED_FEATHER.trigger(entity); } @@ -513,7 +513,9 @@ public class PlayerPhysics extends EntityPhysics implements Tickab if (entity.getWorld().random.nextInt(110) == 1 && !pony.isClient()) { 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().getExhaustion().set(0); mana.set(mana.getMax() * 100); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java index 2487a67c..74dfa609 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -36,7 +36,9 @@ import com.google.common.collect.Streams; import com.minelittlepony.common.util.animation.Interpolator; import com.mojang.authlib.GameProfile; +import net.minecraft.enchantment.Enchantment; import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.enchantment.Enchantments; import net.minecraft.entity.*; import net.minecraft.entity.attribute.DefaultAttributeContainer; import net.minecraft.entity.damage.DamageSource; @@ -359,6 +361,7 @@ public class Pony extends Living implements Copyable, Update .orElseGet(this::getSpecies).composite( AmuletSelectors.UNICORN_AMULET.test(entity) ? Race.UNICORN : AmuletSelectors.ALICORN_AMULET.test(entity) ? Race.ALICORN + : AmuletSelectors.PEARL_NECKLACE.test(entity) ? Race.SEAPONY : null ); } @@ -428,6 +431,23 @@ public class Pony extends Living implements Copyable, 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(); } @@ -568,6 +588,14 @@ public class Pony extends Living implements Copyable, Update if (getObservedSpecies() == Race.KIRIN) { 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(); } @@ -595,6 +623,18 @@ public class Pony extends Living implements Copyable, Update 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 modifyDamage(DamageSource cause, float amount) { if (getObservedSpecies() == Race.KIRIN) { diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java index 011bff35..282f9e3e 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java +++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java @@ -197,6 +197,9 @@ public interface UItems { .maxCount(1) .maxDamage(890) .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 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 CHANGELING_BADGE = register(Race.CHANGELING); Item KIRIN_BADGE = register(Race.KIRIN); + Item HIPPOGRIFF_BADGE = register(Race.HIPPOGRIFF); private static T register(String name, T item, RegistryKey group) { return ItemGroupRegistry.register(Unicopia.id(name), item, group); diff --git a/src/main/java/com/minelittlepony/unicopia/item/toxin/Toxics.java b/src/main/java/com/minelittlepony/unicopia/item/toxin/Toxics.java index 051b9f73..82157234 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/toxin/Toxics.java +++ b/src/main/java/com/minelittlepony/unicopia/item/toxin/Toxics.java @@ -22,12 +22,14 @@ public interface Toxics { Toxic EDIBLE = register("edible", new Toxic.Builder(Ailment.INNERT) .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) .food(UFoodComponents.RANDOM_FOLIAGE) .with(Race.HUMAN, of(LETHAL, FOOD_POISONING)) .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) @@ -59,6 +61,7 @@ public interface Toxics { Toxic FORAGE_PRICKLY = register("forage_prickly", new Toxic.Builder(of(SAFE, INSTANT_DAMAGE.withChance(30))) .food(UFoodComponents.RANDOM_FOLIAGE) .with(Race.HUMAN, of(LETHAL, FOOD_POISONING)) + .with(Race.HIPPOGRIFF, Ailment.INNERT) .with(Race.KIRIN, Ailment.INNERT) ); 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)) .with(Race.HUMAN, Ailment.INNERT) .with(Race.CHANGELING, Ailment.INNERT) + .with(Race.HIPPOGRIFF, of(MILD, FOOD_POISONING)) .with(Race.BAT, 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))) .with(Race.HUMAN, Ailment.INNERT) .with(Race.PEGASUS, of(MILD, FOOD_POISONING)) + .with(Race.HIPPOGRIFF, Ailment.INNERT) + .with(Race.SEAPONY, Ailment.INNERT) .with(Race.ALICORN, Ailment.INNERT) .with(Race.CHANGELING, of(FAIR, LOVE_SICKNESS)) ); Toxic COOKED_FISH = register("cooked_fish", new Toxic.Builder(of(MILD, FOOD_POISONING)) .with(Race.HUMAN, Ailment.INNERT) .with(Race.PEGASUS, Ailment.INNERT) + .with(Race.HIPPOGRIFF, Ailment.INNERT) + .with(Race.SEAPONY, Ailment.INNERT) .with(Race.ALICORN, Ailment.INNERT) .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))) .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) diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinEnchantmentHelper.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinEnchantmentHelper.java new file mode 100644 index 00000000..71ecc8f9 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinEnchantmentHelper.java @@ -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 info) { + Pony.of(entity).ifPresent(pony -> { + int implicitLevel = pony.getImplicitEnchantmentLevel(enchantment); + if (implicitLevel > 0) { + info.setReturnValue(implicitLevel + info.getReturnValue()); + } + }); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinEntity.java index a522b101..90febf2d 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinEntity.java @@ -1,5 +1,7 @@ package com.minelittlepony.unicopia.mixin; +import java.util.Set; + import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; 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.PositionUpdater; import net.minecraft.entity.Entity.RemovalReason; +import net.minecraft.fluid.Fluid; +import net.minecraft.registry.tag.TagKey; @Mixin(Entity.class) abstract class MixinEntity implements EntityDuck { + @Override + @Accessor("submergedFluidTag") + public abstract Set> getSubmergedFluidTags(); + @Override @Accessor public abstract void setRemovalReason(RemovalReason reason); diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinPlayerEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinPlayerEntity.java index 9d96e9c1..71d0907c 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinPlayerEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinPlayerEntity.java @@ -11,6 +11,7 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import com.minelittlepony.unicopia.entity.duck.PlayerEntityDuck; +import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.entity.Equine; import com.minelittlepony.unicopia.entity.player.Pony; import com.mojang.datafixers.util.Either; @@ -114,4 +115,20 @@ abstract class MixinPlayerEntity extends LivingEntity implements Equine.Containe private void onGetBlockBreakingSpeed(BlockState state, CallbackInfoReturnable info) { 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); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinPufferfishEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinPufferfishEntity.java new file mode 100644 index 00000000..ecadb831 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinPufferfishEntity.java @@ -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 info) { + Pony.of(entity).filter(pony -> pony.getCompositeRace().includes(Race.SEAPONY)).ifPresent(pony -> { + info.setReturnValue(false); + }); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinCamera.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinCamera.java index 81f9f5da..6444e002 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinCamera.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinCamera.java @@ -8,6 +8,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import com.minelittlepony.unicopia.client.UnicopiaClient; import net.minecraft.client.render.Camera; +import net.minecraft.client.render.CameraSubmersionType; @Mixin(Camera.class) abstract class MixinCamera { @@ -33,4 +34,11 @@ abstract class MixinCamera { private void redirectCameraDistance(double initial, CallbackInfoReturnable info) { UnicopiaClient.getCamera().flatMap(c -> c.calculateDistance(info.getReturnValueD())).ifPresent(info::setReturnValue); } + + @Inject(method = "getSubmersionType", + at = @At("RETURN"), + cancellable = true) + public void getSubmersionType(CallbackInfoReturnable info) { + UnicopiaClient.getCamera().flatMap(c -> c.getSubmersionType(info.getReturnValue())).ifPresent(info::setReturnValue); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinInGameHud.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinInGameHud.java index fa02c81d..e4306cbd 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinInGameHud.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinInGameHud.java @@ -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.CallbackInfoReturnable; +import com.minelittlepony.unicopia.client.gui.HudEffects; 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.hud.InGameHud; -import net.minecraft.entity.effect.StatusEffectInstance; -import net.minecraft.entity.effect.StatusEffects; import net.minecraft.entity.player.PlayerEntity; @Mixin(InGameHud.class) abstract class MixinInGameHud { - private boolean addedHunger; - @Shadow abstract PlayerEntity getCameraPlayer(); @Inject(method = "render(Lnet/minecraft/client/gui/DrawContext;F)V", at = @At("HEAD")) private void onRender(DrawContext context, float tickDelta, CallbackInfo info) { - PlayerEntity player = getCameraPlayer(); - 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)); - } + HudEffects.tryApply(getCameraPlayer(), tickDelta, true); UHud.INSTANCE.render((InGameHud)(Object)this, context, tickDelta); } @Inject(method = "render(Lnet/minecraft/client/gui/DrawContext;F)V", at = @At("RETURN")) private void afterRender(DrawContext context, float tickDelta, CallbackInfo info) { - if (addedHunger) { - addedHunger = false; - PlayerEntity player = getCameraPlayer(); - if (player != null) { - player.removeStatusEffect(StatusEffects.HUNGER); - } - } + HudEffects.tryApply(getCameraPlayer(), tickDelta, false); } } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinKeyboardInput.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinKeyboardInput.java index c0fd45da..7bc83f17 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinKeyboardInput.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinKeyboardInput.java @@ -40,6 +40,11 @@ abstract class MixinKeyboardInput extends Input { movementForward = 0; jumping = false; } + + if (player.getAcrobatics().isImmobile()) { + movementSideways = 0; + movementForward = 0; + } } } } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinModelPart.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinModelPart.java index 4fb5cd9a..aef4d87d 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinModelPart.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinModelPart.java @@ -39,7 +39,7 @@ abstract class MixinModelPart implements Hookable { 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) { if (visible && isHeadPart) { ModelPartHooks.onHeadRendered((ModelPart)(Object)this, matrices); diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index 8017b7e3..febcc802 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -52,6 +52,7 @@ "item.unicopia.changeling_badge": "Changeling Emblem", "item.unicopia.bat_badge": "Bat Pony Emblem", "item.unicopia.kirin_badge": "Kirin Emblem", + "item.unicopia.hippogriff_badge": "Hippogriff Emblem", "item.unicopia.butterfly_spawn_egg": "Butterfly Spawn Egg", "item.unicopia.butterfly": "Butterfly", @@ -141,13 +142,16 @@ "item.unicopia.alicorn_amulet": "Alicorn Amulet", "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.speed": "Speed: %d", "item.unicopia.iron_horse_shoe": "Iron Horse Shoe", "item.unicopia.golden_horse_shoe": "Golden Horse Shoe", "item.unicopia.copper_horse_shoe": "Copper Horse Shoe", "item.unicopia.netherite_horse_shoe": "Netherite Horse Shoe", - + "item.unicopia.broken_alicorn_amulet": "Broken Alicorn Amulet", "item.unicopia.unicorn_amulet": "Unicorn Amulet", "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.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", "spell.unicopia.frost": "Frost", @@ -480,6 +490,11 @@ "ability.unicopia.rainboom": "Sonic Rainboom", "ability.unicopia.rage": "Rage", "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.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.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.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.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.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.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.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.cancel": "Go Back", @@ -811,6 +837,10 @@ "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", "death.attack.unicopia.generic.and_also": "%1$s and %2$s", "death.attack.unicopia.generic.whilst_flying": "%1$s whilst flying", @@ -932,6 +962,7 @@ "unicopia.subtitle.entity.player.kick": "Player kicks", "unicopia.subtitle.magic_aura": "Magic humming", "unicopia.subtitle.player.rebound": "Player Bounces Off Wall", + "unicopia.subtitle.screech": "Player Screeches", "unicopia.subtitle.ears_ringing": "Ears ringing", "unicopia.subtitle.heartbeat": "Heart beats", "unicopia.subtitle.entity.artefact.ambient": "Magic hums", diff --git a/src/main/resources/assets/unicopia/models/item/hippogriff_badge.json b/src/main/resources/assets/unicopia/models/item/hippogriff_badge.json new file mode 100644 index 00000000..c37d99ce --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/hippogriff_badge.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/hippogriff_badge" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/pearl_necklace.json b/src/main/resources/assets/unicopia/models/item/pearl_necklace.json new file mode 100644 index 00000000..8716891e --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/pearl_necklace.json @@ -0,0 +1,6 @@ +{ + "parent": "unicopia:item/amulet", + "textures": { + "layer0": "unicopia:item/pearl_necklace" + } +} diff --git a/src/main/resources/assets/unicopia/sounds.json b/src/main/resources/assets/unicopia/sounds.json index 86d150a5..67885c5c 100644 --- a/src/main/resources/assets/unicopia/sounds.json +++ b/src/main/resources/assets/unicopia/sounds.json @@ -29,6 +29,13 @@ } ] }, + "entity.player.hippogriff.screech": { + "category": "player", + "subtitle": "unicopia.subtitle.screech", + "sounds": [ + "unicopia:screech/screech0" + ] + }, "item.magic.aura": { "category": "player", "subtitle": "unicopia.subtitle.magic_aura", diff --git a/src/main/resources/assets/unicopia/sounds/screech/screech0.ogg b/src/main/resources/assets/unicopia/sounds/screech/screech0.ogg new file mode 100644 index 00000000..1343c5e5 Binary files /dev/null and b/src/main/resources/assets/unicopia/sounds/screech/screech0.ogg differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/dash.png b/src/main/resources/assets/unicopia/textures/gui/ability/dash.png new file mode 100644 index 00000000..a2f3938a Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/dash.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/dash_old.png b/src/main/resources/assets/unicopia/textures/gui/ability/dash_old.png new file mode 100644 index 00000000..029293fe Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/dash_old.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/peck.png b/src/main/resources/assets/unicopia/textures/gui/ability/peck.png new file mode 100644 index 00000000..a89a7cb3 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/peck.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/screech.png b/src/main/resources/assets/unicopia/textures/gui/ability/screech.png new file mode 100644 index 00000000..acf51ad5 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/screech.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_land_hippogriff.png b/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_land_hippogriff.png new file mode 100644 index 00000000..4d981f1e Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_land_hippogriff.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_takeoff_hippogriff.png b/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_takeoff_hippogriff.png new file mode 100644 index 00000000..46068f0d Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/toggle_flight_takeoff_hippogriff.png differ diff --git a/src/main/resources/assets/unicopia/textures/gui/race/hippogriff.png b/src/main/resources/assets/unicopia/textures/gui/race/hippogriff.png new file mode 100644 index 00000000..057c2240 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/race/hippogriff.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/hippogriff_badge.png b/src/main/resources/assets/unicopia/textures/item/hippogriff_badge.png new file mode 100644 index 00000000..057c2240 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/hippogriff_badge.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/pearl_necklace.png b/src/main/resources/assets/unicopia/textures/item/pearl_necklace.png new file mode 100644 index 00000000..057c2240 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/pearl_necklace.png differ diff --git a/src/main/resources/data/trinkets/tags/items/chest/necklace.json b/src/main/resources/data/trinkets/tags/items/chest/necklace.json index 675082d4..6b3f0e07 100644 --- a/src/main/resources/data/trinkets/tags/items/chest/necklace.json +++ b/src/main/resources/data/trinkets/tags/items/chest/necklace.json @@ -3,6 +3,7 @@ "values": [ "unicopia:alicorn_amulet", "unicopia:pegasus_amulet", - "unicopia:unicorn_amulet" + "unicopia:unicorn_amulet", + "unicopia:pearl_necklace" ] } \ No newline at end of file diff --git a/src/main/resources/unicopia.mixin.json b/src/main/resources/unicopia.mixin.json index 95738783..045db477 100644 --- a/src/main/resources/unicopia.mixin.json +++ b/src/main/resources/unicopia.mixin.json @@ -17,6 +17,7 @@ "MixinChunkBlockLightProvider", "MutableBlockLightStorage", "MixinDamageSource", + "MixinEnchantmentHelper", "MixinFallLocation", "MixinEntity", "MixinEntityShapeContext", @@ -34,6 +35,7 @@ "MixinPlayerManager", "MixinPowderSnowBlock", "MixinProjectileEntity", + "MixinPufferfishEntity", "MixinServerPlayerEntity", "MixinServerPlayNetworkHandler", "MixinServerWorld",