Unicorns now have the option to takes friends with them when they teleport (at an extra cost)

This commit is contained in:
Sollace 2021-02-13 19:59:12 +02:00
parent 209ddbb972
commit fef8de15d8
11 changed files with 89 additions and 57 deletions

View file

@ -17,6 +17,7 @@ public interface Abilities {
// unicorn / alicorn // unicorn / alicorn
Ability<?> CAST = register(new UnicornCastingAbility(), "cast", AbilitySlot.PRIMARY); Ability<?> CAST = register(new UnicornCastingAbility(), "cast", AbilitySlot.PRIMARY);
Ability<?> TELEPORT = register(new UnicornTeleportAbility(), "teleport", AbilitySlot.SECONDARY); Ability<?> TELEPORT = register(new UnicornTeleportAbility(), "teleport", AbilitySlot.SECONDARY);
Ability<?> GROUP_TELEPORT = register(new UnicornGroupTeleportAbility(), "group_teleport", AbilitySlot.SECONDARY);
Ability<?> SHOOT = register(new UnicornProjectileAbility(), "shoot", AbilitySlot.TERTIARY); Ability<?> SHOOT = register(new UnicornProjectileAbility(), "shoot", AbilitySlot.TERTIARY);
// earth / alicorn // earth / alicorn

View file

@ -0,0 +1,34 @@
package com.minelittlepony.unicopia.ability;
import java.util.stream.Stream;
import com.minelittlepony.unicopia.ability.data.Pos;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
/**
* Unicorn teleport ability with friends
*/
public class UnicornGroupTeleportAbility extends UnicornTeleportAbility {
@Override
public double getCostEstimate(Pony player) {
double cost = super.getCostEstimate(player);
if (cost == 0) {
return 0;
}
return cost * (1 + getComrades(player).count());
}
@Override
public void apply(Pony player, Pos data) {
getComrades(player).forEach(teleportee -> teleport(player, teleportee, data));
super.apply(player, data);
}
private Stream<Caster<?>> getComrades(Pony player) {
return Caster.stream(player.findAllEntitiesInRange(3, e -> FriendshipBraceletItem.isComrade(player, e)));
}
}

View file

@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.ability;
import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.data.Pos; import com.minelittlepony.unicopia.ability.data.Pos;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.util.RayTraceHelper; import com.minelittlepony.unicopia.util.RayTraceHelper;
@ -12,6 +13,7 @@ import net.minecraft.block.FenceBlock;
import net.minecraft.block.LeavesBlock; import net.minecraft.block.LeavesBlock;
import net.minecraft.block.WallBlock; import net.minecraft.block.WallBlock;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket; import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket;
import net.minecraft.predicate.entity.EntityPredicates; import net.minecraft.predicate.entity.EntityPredicates;
@ -23,6 +25,7 @@ import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult; import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
/** /**
@ -30,9 +33,6 @@ import net.minecraft.world.World;
*/ */
public class UnicornTeleportAbility implements Ability<Pos> { public class UnicornTeleportAbility implements Ability<Pos> {
/**
* The icon representing this ability on the UI and HUD.
*/
@Override @Override
public Identifier getIcon(Pony player, boolean swap) { public Identifier getIcon(Pony player, boolean swap) {
Identifier id = Abilities.REGISTRY.getId(this); Identifier id = Abilities.REGISTRY.getId(this);
@ -125,10 +125,15 @@ public class UnicornTeleportAbility implements Ability<Pos> {
@Override @Override
public void apply(Pony iplayer, Pos data) { public void apply(Pony iplayer, Pos data) {
iplayer.getWorld().playSound(null, iplayer.getOrigin(), SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.PLAYERS, 1, 1); teleport(iplayer, iplayer, data);
}
PlayerEntity player = iplayer.getMaster(); protected void teleport(Pony teleporter, Caster<?> teleportee, Pos destination) {
double distance = data.distanceTo(iplayer) / 10; teleportee.getWorld().playSound(null, teleportee.getOrigin(), SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.PLAYERS, 1, 1);
double distance = destination.distanceTo(teleportee) / 10;
LivingEntity player = teleportee.getMaster();
if (player.hasVehicle()) { if (player.hasVehicle()) {
Entity mount = player.getVehicle(); Entity mount = player.getVehicle();
@ -140,15 +145,17 @@ public class UnicornTeleportAbility implements Ability<Pos> {
} }
} }
Vec3d offset = teleportee.getOriginVector().subtract(teleporter.getOriginVector());
player.teleport( player.teleport(
data.x + (player.getX() - Math.floor(player.getX())), destination.x + offset.x + (player.getX() - Math.floor(player.getX())),
data.y, destination.y + offset.y,
data.z + (player.getZ() - Math.floor(player.getZ()))); destination.z + offset.z + (player.getZ() - Math.floor(player.getZ())));
iplayer.subtractEnergyCost(distance); teleporter.subtractEnergyCost(distance);
player.fallDistance /= distance; player.fallDistance /= distance;
player.world.playSound(null, data.pos(), SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.PLAYERS, 1, 1); player.world.playSound(null, destination.pos(), SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.PLAYERS, 1, 1);
} }
private boolean enterable(World w, BlockPos pos) { private boolean enterable(World w, BlockPos pos) {

View file

@ -6,8 +6,10 @@ import java.util.stream.Stream;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Owned; import com.minelittlepony.unicopia.Owned;
import com.minelittlepony.unicopia.entity.Physics; import com.minelittlepony.unicopia.entity.Physics;
import com.minelittlepony.unicopia.entity.PonyContainer;
import com.minelittlepony.unicopia.network.EffectSync; import com.minelittlepony.unicopia.network.EffectSync;
import com.minelittlepony.unicopia.particle.ParticleSource; import com.minelittlepony.unicopia.particle.ParticleSource;
import com.minelittlepony.unicopia.util.VecHelper; import com.minelittlepony.unicopia.util.VecHelper;
@ -105,7 +107,11 @@ public interface Caster<E extends LivingEntity> extends Owned<E>, Levelled, Affi
} }
default Stream<Caster<?>> findAllSpellsInRange(double radius) { default Stream<Caster<?>> findAllSpellsInRange(double radius) {
return CasterUtils.findInRange(this, radius); return findAllSpellsInRange(radius, null);
}
default Stream<Caster<?>> findAllSpellsInRange(double radius, @Nullable Predicate<Entity> test) {
return stream(findAllEntitiesInRange(radius, test == null ? EquinePredicates.IS_CASTER : EquinePredicates.IS_CASTER.and(test)));
} }
default Stream<Entity> findAllEntitiesInRange(double radius, @Nullable Predicate<Entity> test) { default Stream<Entity> findAllEntitiesInRange(double radius, @Nullable Predicate<Entity> test) {
@ -115,4 +121,26 @@ public interface Caster<E extends LivingEntity> extends Owned<E>, Levelled, Affi
default Stream<Entity> findAllEntitiesInRange(double radius) { default Stream<Entity> findAllEntitiesInRange(double radius) {
return findAllEntitiesInRange(radius, null); return findAllEntitiesInRange(radius, null);
} }
static Stream<Caster<?>> stream(Stream<Entity> entities) {
return entities
.map(Caster::of)
.filter(Optional::isPresent)
.map(Optional::get);
}
/**
* Attempts to convert the passed entity into a caster using all the known methods.
*/
static Optional<Caster<?>> of(@Nullable Entity entity) {
if (entity instanceof Caster<?>) {
return Optional.of((Caster<?>)entity);
}
if (entity instanceof LivingEntity && !(entity instanceof Magical)) {
return PonyContainer.of(entity).map(PonyContainer::getCaster);
}
return Optional.empty();
}
} }

View file

@ -1,38 +0,0 @@
package com.minelittlepony.unicopia.ability.magic;
import java.util.Optional;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.entity.PonyContainer;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
public class CasterUtils {
/**
* Finds all surrounding spells within range from the given caster.
*/
static Stream<Caster<?>> findInRange(Caster<?> source, double radius) {
return source.findAllEntitiesInRange(radius, EquinePredicates.IS_CASTER)
.map(e -> toCaster(e).filter(o -> o != source))
.filter(Optional::isPresent)
.map(Optional::get);
}
/**
* Attempts to convert the passed entity into a caster using all the known methods.
*/
public static Optional<Caster<?>> toCaster(@Nullable Entity entity) {
if (entity instanceof Caster<?>) {
return Optional.of((Caster<?>)entity);
}
if (entity instanceof LivingEntity && !(entity instanceof Magical)) {
return PonyContainer.of(entity).map(PonyContainer::getCaster);
}
return Optional.empty();
}
}

View file

@ -14,7 +14,6 @@ import com.minelittlepony.unicopia.FlightType;
import com.minelittlepony.unicopia.InteractionManager; import com.minelittlepony.unicopia.InteractionManager;
import com.minelittlepony.unicopia.Owned; import com.minelittlepony.unicopia.Owned;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.CasterUtils;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell; import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.projectile.ProjectileUtil; import com.minelittlepony.unicopia.projectile.ProjectileUtil;
@ -171,7 +170,7 @@ public class Disguise implements NbtSerialisable {
return; return;
} }
CasterUtils.toCaster(entity).ifPresent(c -> c.setSpell(null)); Caster.of(entity).ifPresent(c -> c.setSpell(null));
if (source.isClient()) { if (source.isClient()) {
source.getWorld().spawnEntity(entity); source.getWorld().spawnEntity(entity);
@ -340,7 +339,7 @@ public class Disguise implements NbtSerialisable {
VoxelShape entityShape = VoxelShapes.cuboid(box.expand(1.0E-6D)); VoxelShape entityShape = VoxelShapes.cuboid(box.expand(1.0E-6D));
world.getOtherEntities(entity, box.expand(0.5), predicate.and(e -> { world.getOtherEntities(entity, box.expand(0.5), predicate.and(e -> {
CasterUtils.toCaster(e).flatMap(c -> c.getSpellOrEmpty(DisguiseSpell.class, false)).ifPresent(p -> { Caster.of(e).flatMap(c -> c.getSpellOrEmpty(DisguiseSpell.class, false)).ifPresent(p -> {
p.getDisguise().getCollissionShapes(ctx, shape -> { p.getDisguise().getCollissionShapes(ctx, shape -> {
if (!shape.isEmpty() && VoxelShapes.matchesAnywhere(shape, entityShape, BooleanBiFunction.AND)) { if (!shape.isEmpty() && VoxelShapes.matchesAnywhere(shape, entityShape, BooleanBiFunction.AND)) {
shapes.add(shape); shapes.add(shape);

View file

@ -11,7 +11,7 @@ import org.spongepowered.asm.mixin.injection.ModifyConstant;
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.ability.magic.CasterUtils; import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell; import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.entity.Creature; import com.minelittlepony.unicopia.entity.Creature;
import com.minelittlepony.unicopia.entity.PonyContainer; import com.minelittlepony.unicopia.entity.PonyContainer;
@ -72,7 +72,7 @@ abstract class MixinLivingEntity extends Entity implements PonyContainer<Equine<
@Inject(method = "isPushable()Z", at = @At("HEAD"), cancellable = true) @Inject(method = "isPushable()Z", at = @At("HEAD"), cancellable = true)
private void onIsPushable(CallbackInfoReturnable<Boolean> info) { private void onIsPushable(CallbackInfoReturnable<Boolean> info) {
CasterUtils.toCaster(this) Caster.of(this)
.flatMap(c -> c.getSpellOrEmpty(DisguiseSpell.class, false)) .flatMap(c -> c.getSpellOrEmpty(DisguiseSpell.class, false))
.map(DisguiseSpell::getDisguise) .map(DisguiseSpell::getDisguise)
.map(Disguise::getAppearance) .map(Disguise::getAppearance)

View file

@ -5,7 +5,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.ability.magic.CasterUtils; import com.minelittlepony.unicopia.ability.magic.Caster;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
@ -18,6 +18,6 @@ abstract class MixinMilkBucketItem extends Item {
@Inject(method = "finishUsing", at = @At("HEAD"), cancellable = true) @Inject(method = "finishUsing", at = @At("HEAD"), cancellable = true)
private void finishUsing(ItemStack stack, World world, LivingEntity entity, CallbackInfoReturnable<ItemStack> info) { private void finishUsing(ItemStack stack, World world, LivingEntity entity, CallbackInfoReturnable<ItemStack> info) {
CasterUtils.toCaster(entity).ifPresent(c -> c.setSpell(null)); Caster.of(entity).ifPresent(c -> c.setSpell(null));
} }
} }

View file

@ -132,6 +132,7 @@
"ability.unicopia.shoot": "Magic Missle", "ability.unicopia.shoot": "Magic Missle",
"ability.unicopia.cast": "Counterspell", "ability.unicopia.cast": "Counterspell",
"ability.unicopia.teleport": "Teleport", "ability.unicopia.teleport": "Teleport",
"ability.unicopia.group_teleport": "Teleport (Group)",
"ability.unicopia.grow": "Earthly Nourishment", "ability.unicopia.grow": "Earthly Nourishment",
"ability.unicopia.stomp": "Ground Pound", "ability.unicopia.stomp": "Ground Pound",
"ability.unicopia.kick": "Crushing Blow", "ability.unicopia.kick": "Crushing Blow",

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 B