Added abilities for bat ponies

This commit is contained in:
Sollace 2020-09-25 20:24:48 +02:00
parent babc9e680d
commit c9ee710ab5
22 changed files with 285 additions and 13 deletions

View file

@ -2,7 +2,9 @@ package com.minelittlepony.unicopia;
import java.util.function.Predicate; import java.util.function.Predicate;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.entity.Equine; import com.minelittlepony.unicopia.entity.Equine;
import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
@ -12,8 +14,13 @@ public interface EquinePredicates {
Predicate<Entity> RACE_INTERACT_WITH_CLOUDS = entity -> Equine.of(entity).getSpecies().canInteractWithClouds(); Predicate<Entity> RACE_INTERACT_WITH_CLOUDS = entity -> Equine.of(entity).getSpecies().canInteractWithClouds();
Predicate<Entity> PLAYER_EARTH = IS_PLAYER.and(entity -> Equine.of(entity).getSpecies() == Race.EARTH);
Predicate<Entity> PLAYER_BAT = IS_PLAYER.and(entity -> Equine.of(entity).getSpecies() == Race.BAT);
Predicate<Entity> PLAYER_UNICORN = IS_PLAYER.and(entity -> Equine.of(entity).getSpecies().canCast()); Predicate<Entity> PLAYER_UNICORN = IS_PLAYER.and(entity -> Equine.of(entity).getSpecies().canCast());
Predicate<Entity> PLAYER_CHANGELING = IS_PLAYER.and(entity -> Equine.of(entity).getSpecies() == Race.CHANGELING); Predicate<Entity> PLAYER_CHANGELING = IS_PLAYER.and(entity -> Equine.of(entity).getSpecies() == Race.CHANGELING);
Predicate<Entity> PLAYER_PEGASUS = IS_PLAYER.and(entity -> ((PlayerEntity)entity).abilities.creativeMode || RACE_INTERACT_WITH_CLOUDS.test(entity)); Predicate<Entity> PLAYER_PEGASUS = IS_PLAYER.and(entity -> ((PlayerEntity)entity).abilities.creativeMode || RACE_INTERACT_WITH_CLOUDS.test(entity));
static Predicate<Entity> carryingSpell(Class<? extends Spell> type) {
return IS_PLAYER.and(entity -> Pony.of((PlayerEntity)entity).getSpellOrEmpty(type, false).isPresent());
}
} }

View file

@ -9,6 +9,7 @@ public interface USounds {
SoundEvent WING_FLAP = register("wing_flap"); SoundEvent WING_FLAP = register("wing_flap");
SoundEvent WIND_RUSH = register("wind_rush"); SoundEvent WIND_RUSH = register("wind_rush");
SoundEvent BATPONY_EEEE = register("batpony_eeee");
SoundEvent CHANGELING_BUZZ = register("changeling_buzz"); SoundEvent CHANGELING_BUZZ = register("changeling_buzz");
SoundEvent RECORD_CRUSADE = register("record.crusade"); SoundEvent RECORD_CRUSADE = register("record.crusade");

View file

@ -29,6 +29,10 @@ public interface Abilities {
Ability<?> DISGUISE = register(new ChangelingDisguiseAbility(), "disguise", AbilitySlot.SECONDARY); Ability<?> DISGUISE = register(new ChangelingDisguiseAbility(), "disguise", AbilitySlot.SECONDARY);
Ability<?> FEED = register(new ChangelingFeedAbility(), "feed", AbilitySlot.TERTIARY); Ability<?> FEED = register(new ChangelingFeedAbility(), "feed", AbilitySlot.TERTIARY);
// bat
Ability<?> HANG = register(new BatPonyHangAbility(), "hang", AbilitySlot.SECONDARY);
Ability<?> EEEE = register(new BatEeeeAbility(), "eee", AbilitySlot.TERTIARY);
static <T extends Ability<?>> T register(T power, String name, AbilitySlot slot) { static <T extends Ability<?>> T register(T power, String name, AbilitySlot slot) {
Identifier id = new Identifier("unicopia", name); Identifier id = new Identifier("unicopia", name);
BY_SLOT.computeIfAbsent(slot, s -> new HashSet<>()).add(power); BY_SLOT.computeIfAbsent(slot, s -> new HashSet<>()).add(power);

View file

@ -0,0 +1,100 @@
package com.minelittlepony.unicopia.ability;
import java.util.Random;
import java.util.function.Predicate;
import com.minelittlepony.unicopia.AwaitTickQueue;
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.magic.spell.ShieldSpell;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.util.MagicalDamageSource;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.sound.SoundCategory;
import net.minecraft.util.math.Vec3d;
/**
* A magic casting ability for unicorns.
* (only shields for now)
*/
public class BatEeeeAbility implements Ability<Hit> {
private static final Predicate<Entity> HAS_SHIELD = EquinePredicates.carryingSpell(ShieldSpell.class);
@Override
public int getWarmupTime(Pony player) {
return 1;
}
@Override
public int getCooldownTime(Pony player) {
return 1;
}
@Override
public boolean canUse(Race race) {
return race == Race.BAT;
}
@Override
public Hit tryActivate(Pony player) {
return Hit.INSTANCE;
}
@Override
public Hit.Serializer<Hit> getSerializer() {
return Hit.SERIALIZER;
}
@Override
public void apply(Pony player, Hit data) {
Random rng = player.getWorld().random;
int count = 1 + rng.nextInt(10);
for (int i = 0; i < count; i++) {
player.getWorld().playSound(null, player.getOrigin(), USounds.BATPONY_EEEE, SoundCategory.PLAYERS,
0.9F + (rng.nextFloat() - 0.5F) / 2F,
1.6F + (rng.nextFloat() - 0.5F)
);
}
AwaitTickQueue.scheduleTask(player.getWorld(), w -> {
for (int i = 0; i < count; i++) {
player.getWorld().playSound(null, player.getOrigin(), USounds.BATPONY_EEEE, SoundCategory.PLAYERS,
0.9F + (rng.nextFloat() - 0.5F) / 2F,
1.6F + (rng.nextFloat() - 0.5F)
);
}
}, rng.nextInt(10));
Vec3d origin = player.getOriginVector();
if (rng.nextInt(20000) == 0) {
player.getOwner().damage(MagicalDamageSource.create("eeee", player.getOwner()), 0.1F);
}
player.findAllEntitiesInRange(5).forEach(e -> {
if (e instanceof LivingEntity && !HAS_SHIELD.test(e)) {
boolean isEarthPony = EquinePredicates.PLAYER_EARTH.test(e);
e.damage(MagicalDamageSource.create("eeee", player.getOwner()), isEarthPony ? 0.1F : 0.3F);
Vec3d knockVec = origin.subtract(e.getPos());
((LivingEntity) e).takeKnockback(isEarthPony ? 0.3F : 0.5F, knockVec.getX(), knockVec.getZ());
if (!isEarthPony) {
e.addVelocity(0, 0.1, 0);
}
}
});
}
@Override
public void preApply(Pony player, AbilitySlot slot) {
}
@Override
public void postApply(Pony player, AbilitySlot slot) {
}
}

View file

@ -0,0 +1,71 @@
package com.minelittlepony.unicopia.ability;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Numeric;
import com.minelittlepony.unicopia.entity.player.PlayerAttributes;
import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.entity.attribute.EntityAttributeInstance;
/**
* A magic casting ability for unicorns.
* (only shields for now)
*/
public class BatPonyHangAbility implements Ability<Numeric> {
@Override
public int getWarmupTime(Pony player) {
return 1;
}
@Override
public int getCooldownTime(Pony player) {
return 0;
}
@Override
public boolean canUse(Race race) {
return race == Race.BAT;
}
@Override
public Numeric tryActivate(Pony player) {
EntityAttributeInstance attr = player.getOwner().getAttributeInstance(PlayerAttributes.ENTITY_GRAVTY_MODIFIER);
if (attr.hasModifier(PlayerAttributes.BAT_HANGING)) {
return new Numeric(0);
} else if (player.canHangAt()) {
return new Numeric(1);
}
return null;
}
@Override
public Numeric.Serializer<Numeric> getSerializer() {
return Numeric.SERIALIZER;
}
@Override
public void apply(Pony player, Numeric data) {
EntityAttributeInstance attr = player.getOwner().getAttributeInstance(PlayerAttributes.ENTITY_GRAVTY_MODIFIER);
if (data.type == 0 && attr.hasModifier(PlayerAttributes.BAT_HANGING)) {
attr.removeModifier(PlayerAttributes.BAT_HANGING);
return;
}
if (data.type == 1 && player.canHangAt()) {
attr.addPersistentModifier(PlayerAttributes.BAT_HANGING);
}
}
@Override
public void preApply(Pony player, AbilitySlot slot) {
}
@Override
public void postApply(Pony player, AbilitySlot slot) {
}
}

View file

@ -12,9 +12,10 @@ import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
class PlayerAttributes { public class PlayerAttributes {
static final EntityAttribute EXTENDED_REACH_DISTANCE = register("unicopia.pegasus.reach", new ClampedEntityAttribute("player.reachDistance", 0, 0, 10)); public static final EntityAttribute EXTENDED_REACH_DISTANCE = register("unicopia.pegasus.reach", new ClampedEntityAttribute("player.reachDistance", 0, 0, 10).setTracked(true));
public static final EntityAttribute ENTITY_GRAVTY_MODIFIER = register("unicopia.player.gravity", (new EntityAttribute("player.gravityModifier", 1) {}).setTracked(true));
private static final EntityAttributeModifier EARTH_PONY_STRENGTH = private static final EntityAttributeModifier EARTH_PONY_STRENGTH =
new EntityAttributeModifier(UUID.fromString("777a5505-521e-480b-b9d5-6ea54f259564"), "Earth Pony Strength", 0.6, Operation.MULTIPLY_TOTAL); new EntityAttributeModifier(UUID.fromString("777a5505-521e-480b-b9d5-6ea54f259564"), "Earth Pony Strength", 0.6, Operation.MULTIPLY_TOTAL);
@ -23,6 +24,9 @@ class PlayerAttributes {
private static final EntityAttributeModifier PEGASUS_REACH = private static final EntityAttributeModifier PEGASUS_REACH =
new EntityAttributeModifier(UUID.fromString("707b50a8-03e8-40f4-8553-ecf67025fd6d"), "Pegasus Reach", 1.5, Operation.ADDITION); new EntityAttributeModifier(UUID.fromString("707b50a8-03e8-40f4-8553-ecf67025fd6d"), "Pegasus Reach", 1.5, Operation.ADDITION);
public static final EntityAttributeModifier BAT_HANGING =
new EntityAttributeModifier(UUID.fromString("a54f2595-521e-480b-b9d5-6e750577a564"), "Bat Pony Hanging", -2, Operation.MULTIPLY_TOTAL);
public void applyAttributes(Pony pony) { public void applyAttributes(Pony pony) {
PlayerEntity entity = pony.getOwner(); PlayerEntity entity = pony.getOwner();
Race race = pony.getSpecies(); Race race = pony.getSpecies();
@ -49,5 +53,5 @@ class PlayerAttributes {
private static EntityAttribute register(String id, EntityAttribute attribute) { private static EntityAttribute register(String id, EntityAttribute attribute) {
return Registry.register(Registry.ATTRIBUTE, id, attribute); return Registry.register(Registry.ATTRIBUTE, id, attribute);
} }
} }

View file

@ -30,7 +30,7 @@ public class PlayerCamera extends MotionCompositor {
} }
if (player.getEntity().age > 10) { if (player.getEntity().age > 10) {
roll = player.getInterpolator().interpolate("roll", (float)roll, 50); roll = player.getInterpolator().interpolate("roll", (float)roll, 15);
} }
return (float)roll; return (float)roll;

View file

@ -42,6 +42,16 @@ public class PlayerPhysics extends EntityPhysics<Pony> implements Tickable, Moti
dimensions = new PlayerDimensions(pony, this); dimensions = new PlayerDimensions(pony, this);
} }
@Override
public float getGravityModifier() {
if (pony.getOwner().getAttributes() == null) {
// may be null due to order of execution in the contructor.
// Will have the default (1) here in any case, so it's safe to ignore the attribute a this point.
return super.getGravityModifier();
}
return super.getGravityModifier() * (float)pony.getOwner().getAttributeValue(PlayerAttributes.ENTITY_GRAVTY_MODIFIER);
}
private boolean checkCanFly() { private boolean checkCanFly() {
if (pony.getOwner().abilities.creativeMode || pony.getOwner().isSpectator()) { if (pony.getOwner().abilities.creativeMode || pony.getOwner().isSpectator()) {
return true; return true;

View file

@ -31,8 +31,10 @@ import com.minelittlepony.common.util.animation.LinearInterpolator;
import com.minelittlepony.common.util.animation.Interpolator; import com.minelittlepony.common.util.animation.Interpolator;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.attribute.DefaultAttributeContainer; import net.minecraft.entity.attribute.DefaultAttributeContainer;
import net.minecraft.entity.attribute.EntityAttributeInstance;
import net.minecraft.entity.data.DataTracker; import net.minecraft.entity.data.DataTracker;
import net.minecraft.entity.data.TrackedData; import net.minecraft.entity.data.TrackedData;
import net.minecraft.entity.data.TrackedDataHandlerRegistry; import net.minecraft.entity.data.TrackedDataHandlerRegistry;
@ -46,6 +48,7 @@ import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText; import net.minecraft.text.TranslatableText;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
public class Pony implements Caster<PlayerEntity>, Equine<PlayerEntity>, Transmittable, Copieable<Pony> { public class Pony implements Caster<PlayerEntity>, Equine<PlayerEntity>, Transmittable, Copieable<Pony> {
@ -91,6 +94,7 @@ public class Pony implements Caster<PlayerEntity>, Equine<PlayerEntity>, Transmi
public static void registerAttributes(DefaultAttributeContainer.Builder builder) { public static void registerAttributes(DefaultAttributeContainer.Builder builder) {
builder.add(PlayerAttributes.EXTENDED_REACH_DISTANCE); builder.add(PlayerAttributes.EXTENDED_REACH_DISTANCE);
builder.add(PlayerAttributes.ENTITY_GRAVTY_MODIFIER);
} }
@Override @Override
@ -226,8 +230,28 @@ public class Pony implements Caster<PlayerEntity>, Equine<PlayerEntity>, Transmi
return false; return false;
} }
public boolean canHangAt() {
BlockPos above = getOrigin();
above = new BlockPos(above.getX(), getOwner().getEyeY() + 1, above.getZ());
BlockState state = getWorld().getBlockState(above);
return state.hasSolidTopSurface(getWorld(), above, getEntity(), Direction.DOWN);
}
@Override @Override
public void tick() { public void tick() {
EntityAttributeInstance attr = entity.getAttributes().getCustomInstance(PlayerAttributes.ENTITY_GRAVTY_MODIFIER);
if (attr.hasModifier(PlayerAttributes.BAT_HANGING)) {
gravity.isFlying = false;
entity.abilities.flying = false;
if (Entity.squaredHorizontalLength(entity.getVelocity()) > 0.01 || entity.isSneaking() || !canHangAt()) {
attr.removeModifier(PlayerAttributes.BAT_HANGING);
}
}
gravity.tick(); gravity.tick();
if (hasSpell()) { if (hasSpell()) {

View file

@ -6,6 +6,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.client.render.WorldRenderDelegate; import com.minelittlepony.unicopia.client.render.WorldRenderDelegate;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
@ -13,6 +14,8 @@ import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.Camera; import net.minecraft.client.render.Camera;
import net.minecraft.client.render.GameRenderer; import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.resource.SynchronousResourceReloadListener; import net.minecraft.resource.SynchronousResourceReloadListener;
@Mixin(GameRenderer.class) @Mixin(GameRenderer.class)
@ -28,7 +31,24 @@ abstract class MixinGameRenderer implements AutoCloseable, SynchronousResourceRe
@Inject(method = "renderWorld(FJLnet/minecraft/client/util/math/MatrixStack;)V", @Inject(method = "renderWorld(FJLnet/minecraft/client/util/math/MatrixStack;)V",
at = @At("HEAD")) at = @At("HEAD"))
public void onRenderWorld(float tickDelta, long limitTime, MatrixStack matrices, CallbackInfo info) { private void onRenderWorld(float tickDelta, long limitTime, MatrixStack matrices, CallbackInfo info) {
WorldRenderDelegate.INSTANCE.applyWorldTransform(matrices, tickDelta); WorldRenderDelegate.INSTANCE.applyWorldTransform(matrices, tickDelta);
} }
@Inject(method = "getNightVisionStrength(FJLnet/minecraft/entity/LivingEntity;F)F",
at = @At("HEAD"),
cancellable = true)
private static void onGetNightVisionStrengthHead(LivingEntity entity, float tickDelta, CallbackInfoReturnable<Float> info) {
if (!entity.hasStatusEffect(StatusEffects.NIGHT_VISION)) {
info.setReturnValue(0.6F);
}
}
@Inject(method = "getNightVisionStrength(FJLnet/minecraft/entity/LivingEntity;F)F",
at = @At("RETURN"),
cancellable = true)
private static void onGetNightVisionStrengthReturn(LivingEntity entity, float tickDelta, CallbackInfoReturnable<Float> info) {
if (entity.hasStatusEffect(StatusEffects.NIGHT_VISION) && EquinePredicates.PLAYER_BAT.test(entity)) {
info.setReturnValue(Math.min(1, info.getReturnValueF() + 0.6F));
}
}
} }

View file

@ -0,0 +1,23 @@
package com.minelittlepony.unicopia.mixin.client;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import com.minelittlepony.unicopia.EquinePredicates;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.render.LightmapTextureManager;
import net.minecraft.entity.effect.StatusEffect;
import net.minecraft.entity.effect.StatusEffects;
@Mixin(LightmapTextureManager.class)
abstract class MixinLightmapTextureManager implements AutoCloseable {
@Redirect(method = "update(F)V",
at = @At(value = "INVOKE",
target = "net/minecraft/client/network/ClientPlayerEntity.hasStatusEffect(Lnet/minecraft/entity/effect/StatusEffect;)Z")
)
private boolean redirectHasStatusEffect(ClientPlayerEntity entity, StatusEffect effect) {
return (effect == StatusEffects.NIGHT_VISION && EquinePredicates.PLAYER_BAT.test(entity)) || entity.hasStatusEffect(effect);
}
}

View file

@ -182,17 +182,14 @@
"death.attack.darkness": "%1$s went missing", "death.attack.darkness": "%1$s went missing",
"death.attack.feed": "%1$s was drained of all life", "death.attack.feed": "%1$s was drained of all life",
"death.attack.feed.attacker": "%1$s died to feed %2$s", "death.attack.feed.attacker": "%1$s died to feed %2$s",
"death.attack.cold": "%1$s died of frost bite", "death.attack.eeee": "%1$s was firghtened to death",
"death.attack.cold.attacker": "%1$s was frozen to death by %2$s", "death.attack.eeee.attacker": "%2$s scared %1$s",
"death.attack.cold.attacker.item": "%1$s was frozen to death by %2$s using %3$s", "death.attack.eeee.attacker.item": "%1$s was frightened to death by %2$s using %3$s",
"death.attack.eeee.self": "%1$s scared themselves to death",
"death.attack.smash": "%1$s was crushed under hoof", "death.attack.smash": "%1$s was crushed under hoof",
"death.attack.smash.attacker": "%1$s was crushed by %2$s", "death.attack.smash.attacker": "%1$s was crushed by %2$s",
"death.attack.fire": "%1$s was burnt to a crisp by magic", "death.attack.zap": "%1$s bit into a Zap Apple",
"death.attack.fire.attacker": "%1$s was burnt to a crisp by %2$s",
"death.attack.fire.self": "%1$s was burnt to a crisp by their own spell",
"death.attack.zap": "%1$s ate a Zap Apple",
"death.attack.paradox": "%1$s imploded", "death.attack.paradox": "%1$s imploded",
"death.attack.acid": "%1$s drowned in acid",
"death.attack.food_poisoning": "%1$s died of food poisoning", "death.attack.food_poisoning": "%1$s died of food poisoning",
"death.attack.food_poisoning.attacker": "%2$s poisoned %1$s to death", "death.attack.food_poisoning.attacker": "%2$s poisoned %1$s to death",

View file

@ -9,6 +9,16 @@
"unicopia:wing/wing3" "unicopia:wing/wing3"
] ]
}, },
"batpony_eeee": {
"category": "player",
"subtitle": "unicopia.subtitle.batpony_eeee",
"sounds": [
"unicopia:batpony/reep",
"unicopia:batpony/eeep",
"unicopia:batpony/screep0",
"unicopia:batpony/screep1"
]
},
"changeling_buzz": { "changeling_buzz": {
"category": "player", "category": "player",
"subtitle": "unicopia.subtitle.changeling_buzz", "subtitle": "unicopia.subtitle.changeling_buzz",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 793 B

After

Width:  |  Height:  |  Size: 866 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 735 B

View file

@ -24,6 +24,7 @@
"client.MixinInGameHud", "client.MixinInGameHud",
"client.MixinItemModels", "client.MixinItemModels",
"client.MixinKeyboardInput", "client.MixinKeyboardInput",
"client.MixinLightmapTextureManager",
"client.MixinMouse" "client.MixinMouse"
], ],
"injectors": { "injectors": {