Added hippogriffs and seaponies
15
src/main/java/com/minelittlepony/unicopia/Availability.java
Normal 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;
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ public interface EquinePredicates {
|
|||
Predicate<Entity> PLAYER_CHANGELING = IS_PLAYER.and(ofRace(Race.CHANGELING));
|
||||
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_SEAPONY = IS_PLAYER.and(raceMatches(Race::isFish));
|
||||
|
||||
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));
|
||||
|
|
|
@ -24,18 +24,18 @@ import net.minecraft.util.Identifier;
|
|||
import net.minecraft.registry.Registry;
|
||||
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 Registry<Race> REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("race"), DEFAULT_ID);
|
||||
public static final RegistryKey<? extends Registry<Race>> REGISTRY_KEY = REGISTRY.getKey();
|
||||
private static final DynamicCommandExceptionType UNKNOWN_RACE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("race.unknown", id));
|
||||
|
||||
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<Race> argument() {
|
||||
|
@ -46,15 +46,17 @@ public record Race (Supplier<Composite> 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<Composite> 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<Composite> 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() {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<Numeric> {
|
||||
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<Numeric> {
|
|||
}
|
||||
|
||||
@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;
|
||||
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<Numeric> {
|
|||
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<Numeric> {
|
|||
(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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ import com.minelittlepony.unicopia.entity.player.Pony;
|
|||
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class PegasusFlightToggleAbility implements Ability<Hit> {
|
||||
public class ToggleFlightAbility implements Ability<Hit> {
|
||||
|
||||
@Override
|
||||
public int getWarmupTime(Pony player) {
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -54,7 +54,7 @@ public class TribeSelectionScreen extends GameGui implements HidesHud {
|
|||
|
||||
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());
|
||||
|
||||
|
|
|
@ -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<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 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 BodyPartGear<WingsGearModel> pegasusWings() {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<E extends LivingEntity> 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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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<Race> 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;
|
||||
|
|
|
@ -10,6 +10,7 @@ public interface AmuletSelectors {
|
|||
Predicate<LivingEntity> ALICORN_AMULET = UItems.ALICORN_AMULET::isApplicable;
|
||||
Predicate<LivingEntity> PEGASUS_AMULET = UItems.PEGASUS_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_2_DAYS = ALICORN_AMULET.and(ItemTracker.wearing(UItems.ALICORN_AMULET, ItemTracker.after(2 * ItemTracker.DAYS)));
|
||||
|
|
|
@ -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<TagKey<Fluid>> getSubmergedFluidTags();
|
||||
|
||||
void setRemovalReason(RemovalReason reason);
|
||||
|
||||
void setVehicle(Entity vehicle);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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<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) {
|
||||
fov += player.getMagicalReserves().getExertion().get() / 5F;
|
||||
fov += getEnergyAddition();
|
||||
|
|
|
@ -414,7 +414,7 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> 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<PlayerEntity> 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);
|
||||
|
|
|
@ -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<PlayerEntity> implements Copyable<Pony>, 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<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();
|
||||
}
|
||||
|
||||
|
@ -568,6 +588,14 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, 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<PlayerEntity> implements Copyable<Pony>, 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<Float> modifyDamage(DamageSource cause, float amount) {
|
||||
|
||||
if (getObservedSpecies() == Race.KIRIN) {
|
||||
|
|
|
@ -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 extends Item> T register(String name, T item, RegistryKey<ItemGroup> group) {
|
||||
return ItemGroupRegistry.register(Unicopia.id(name), item, group);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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<TagKey<Fluid>> getSubmergedFluidTags();
|
||||
|
||||
@Override
|
||||
@Accessor
|
||||
public abstract void setRemovalReason(RemovalReason reason);
|
||||
|
|
|
@ -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<Float> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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<Double> info) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,11 @@ abstract class MixinKeyboardInput extends Input {
|
|||
movementForward = 0;
|
||||
jumping = false;
|
||||
}
|
||||
|
||||
if (player.getAcrobatics().isImmobile()) {
|
||||
movementSideways = 0;
|
||||
movementForward = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,6 +142,9 @@
|
|||
"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",
|
||||
|
@ -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",
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "item/generated",
|
||||
"textures": {
|
||||
"layer0": "unicopia:item/hippogriff_badge"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "unicopia:item/amulet",
|
||||
"textures": {
|
||||
"layer0": "unicopia:item/pearl_necklace"
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
|
|
BIN
src/main/resources/assets/unicopia/sounds/screech/screech0.ogg
Normal file
BIN
src/main/resources/assets/unicopia/textures/gui/ability/dash.png
Normal file
After Width: | Height: | Size: 7 KiB |
After Width: | Height: | Size: 8.6 KiB |
BIN
src/main/resources/assets/unicopia/textures/gui/ability/peck.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 7.1 KiB |
After Width: | Height: | Size: 7.1 KiB |
After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 6.4 KiB |
|
@ -3,6 +3,7 @@
|
|||
"values": [
|
||||
"unicopia:alicorn_amulet",
|
||||
"unicopia:pegasus_amulet",
|
||||
"unicopia:unicorn_amulet"
|
||||
"unicopia:unicorn_amulet",
|
||||
"unicopia:pearl_necklace"
|
||||
]
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
"MixinChunkBlockLightProvider",
|
||||
"MutableBlockLightStorage",
|
||||
"MixinDamageSource",
|
||||
"MixinEnchantmentHelper",
|
||||
"MixinFallLocation",
|
||||
"MixinEntity",
|
||||
"MixinEntityShapeContext",
|
||||
|
@ -34,6 +35,7 @@
|
|||
"MixinPlayerManager",
|
||||
"MixinPowderSnowBlock",
|
||||
"MixinProjectileEntity",
|
||||
"MixinPufferfishEntity",
|
||||
"MixinServerPlayerEntity",
|
||||
"MixinServerPlayNetworkHandler",
|
||||
"MixinServerWorld",
|
||||
|
|