mirror of
https://github.com/Sollace/Unicopia.git
synced 2025-02-12 16:14:24 +01:00
Added mimic, mind swap, and hydrophobe spells
This commit is contained in:
parent
69c8ceac45
commit
afda7a6ef8
32 changed files with 798 additions and 62 deletions
|
@ -2,13 +2,12 @@ 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.entity.Living;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
import com.minelittlepony.unicopia.util.TraceHelper;
|
import com.minelittlepony.unicopia.util.TraceHelper;
|
||||||
|
|
||||||
import net.minecraft.entity.LivingEntity;
|
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.server.network.ServerPlayerEntity;
|
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -76,9 +75,7 @@ public class CarryAbility implements Ability<Hit> {
|
||||||
rider.startRiding(player, true);
|
rider.startRiding(player, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player instanceof ServerPlayerEntity) {
|
Living.transmitPassengers(player);
|
||||||
((ServerPlayerEntity)player).networkHandler.sendPacket(new EntityPassengersSetS2CPacket(player));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -5,6 +5,7 @@ import com.minelittlepony.unicopia.USounds;
|
||||||
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.ability.magic.Caster;
|
||||||
|
import com.minelittlepony.unicopia.entity.Living;
|
||||||
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.Trace;
|
import com.minelittlepony.unicopia.util.Trace;
|
||||||
|
@ -17,9 +18,7 @@ import net.minecraft.block.WallBlock;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.LivingEntity;
|
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.predicate.entity.EntityPredicates;
|
import net.minecraft.predicate.entity.EntityPredicates;
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
|
||||||
import net.minecraft.sound.SoundCategory;
|
import net.minecraft.sound.SoundCategory;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
@ -142,10 +141,7 @@ public class UnicornTeleportAbility implements Ability<Pos> {
|
||||||
Entity mount = player.getVehicle();
|
Entity mount = player.getVehicle();
|
||||||
|
|
||||||
player.stopRiding();
|
player.stopRiding();
|
||||||
|
Living.transmitPassengers(mount);
|
||||||
if (mount instanceof ServerPlayerEntity) {
|
|
||||||
((ServerPlayerEntity)player).networkHandler.sendPacket(new EntityPassengersSetS2CPacket(player));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3d offset = teleportee.getOriginVector().subtract(teleporter.getOriginVector());
|
Vec3d offset = teleportee.getOriginVector().subtract(teleporter.getOriginVector());
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.minelittlepony.unicopia.ability.magic;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.EquinePredicates;
|
||||||
|
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||||
|
import com.minelittlepony.unicopia.util.VecHelper;
|
||||||
|
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
import net.minecraft.world.*;
|
||||||
|
|
||||||
|
public interface CasterView {
|
||||||
|
EntityView getWorld();
|
||||||
|
|
||||||
|
default <S extends Spell> Stream<Map.Entry<Caster<?>, S>> findAllSpellsInRange(BlockPos pos, double radius, SpellPredicate<S> type) {
|
||||||
|
return findAllCastersInRange(pos, radius).flatMap(caster -> {
|
||||||
|
return caster.getSpellSlot().stream(type, false).map(spell -> {
|
||||||
|
return Map.entry(caster, spell);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
default Stream<Caster<?>> findAllCastersInRange(BlockPos pos, double radius) {
|
||||||
|
return findAllCastersInRange(pos, radius, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
default Stream<Caster<?>> findAllCastersInRange(BlockPos pos, double radius, @Nullable Predicate<Entity> test) {
|
||||||
|
return Caster.stream(findAllEntitiesInRange(pos, radius, test == null ? EquinePredicates.IS_CASTER : EquinePredicates.IS_CASTER.and(test)));
|
||||||
|
}
|
||||||
|
|
||||||
|
default Stream<Entity> findAllEntitiesInRange(BlockPos pos, double radius, @Nullable Predicate<Entity> test) {
|
||||||
|
return VecHelper.findInRange(null, getWorld(), Vec3d.ofCenter(pos), radius, test).stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
default Stream<Entity> findAllEntitiesInRange(BlockPos pos, double radius) {
|
||||||
|
return findAllEntitiesInRange(pos, radius, null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.ability.magic;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.*;
|
import com.minelittlepony.unicopia.ability.magic.spell.*;
|
||||||
|
import com.minelittlepony.unicopia.ability.magic.spell.effect.MimicSpell;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.ShieldSpell;
|
import com.minelittlepony.unicopia.ability.magic.spell.effect.ShieldSpell;
|
||||||
|
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
|
@ -12,6 +13,7 @@ public interface SpellPredicate<T extends Spell> extends Predicate<Spell> {
|
||||||
SpellPredicate<PlaceableSpell> IS_PLACED = s -> s instanceof PlaceableSpell;
|
SpellPredicate<PlaceableSpell> IS_PLACED = s -> s instanceof PlaceableSpell;
|
||||||
SpellPredicate<ProjectileSpell> HAS_PROJECTILE_EVENTS = s -> s instanceof ProjectileSpell;
|
SpellPredicate<ProjectileSpell> HAS_PROJECTILE_EVENTS = s -> s instanceof ProjectileSpell;
|
||||||
SpellPredicate<AbstractDisguiseSpell> IS_DISGUISE = s -> s instanceof AbstractDisguiseSpell;
|
SpellPredicate<AbstractDisguiseSpell> IS_DISGUISE = s -> s instanceof AbstractDisguiseSpell;
|
||||||
|
SpellPredicate<MimicSpell> IS_MIMIC = s -> s instanceof MimicSpell;
|
||||||
SpellPredicate<ShieldSpell> IS_SHIELD_LIKE = spell -> spell instanceof ShieldSpell;
|
SpellPredicate<ShieldSpell> IS_SHIELD_LIKE = spell -> spell instanceof ShieldSpell;
|
||||||
SpellPredicate<TimedSpell> IS_TIMED = spell -> spell instanceof TimedSpell;
|
SpellPredicate<TimedSpell> IS_TIMED = spell -> spell instanceof TimedSpell;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.minelittlepony.unicopia.ability.magic.spell;
|
package com.minelittlepony.unicopia.ability.magic.spell;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
||||||
|
import com.minelittlepony.unicopia.util.Tickable;
|
||||||
|
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
import net.minecraft.util.math.MathHelper;
|
import net.minecraft.util.math.MathHelper;
|
||||||
|
@ -11,16 +12,17 @@ import net.minecraft.util.math.MathHelper;
|
||||||
public interface TimedSpell extends Spell {
|
public interface TimedSpell extends Spell {
|
||||||
Timer getTimer();
|
Timer getTimer();
|
||||||
|
|
||||||
class Timer implements NbtSerialisable {
|
class Timer implements Tickable, NbtSerialisable {
|
||||||
public int maxDuration;
|
private int maxDuration;
|
||||||
public int duration;
|
private int duration;
|
||||||
public int prevDuration;
|
private int prevDuration;
|
||||||
|
|
||||||
public Timer(int initial) {
|
public Timer(int initial) {
|
||||||
maxDuration = initial;
|
maxDuration = initial;
|
||||||
duration = initial;
|
duration = initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void tick() {
|
public void tick() {
|
||||||
prevDuration = duration;
|
prevDuration = duration;
|
||||||
duration--;
|
duration--;
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class AttractiveSpell extends ShieldSpell implements ProjectileSpell, Hom
|
||||||
public boolean tick(Caster<?> caster, Situation situation) {
|
public boolean tick(Caster<?> caster, Situation situation) {
|
||||||
timer.tick();
|
timer.tick();
|
||||||
|
|
||||||
if (timer.duration <= 0) {
|
if (timer.getTicksRemaining() <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,9 +37,11 @@ public class AwkwardSpell extends AbstractSpell implements TimedSpell {
|
||||||
if (situation != Situation.PROJECTILE) {
|
if (situation != Situation.PROJECTILE) {
|
||||||
timer.tick();
|
timer.tick();
|
||||||
|
|
||||||
if (timer.duration <= 0) {
|
if (timer.getTicksRemaining() <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source.isClient()) {
|
if (source.isClient()) {
|
||||||
|
|
|
@ -52,7 +52,7 @@ public class FeatherFallSpell extends AbstractSpell implements TimedSpell {
|
||||||
public boolean tick(Caster<?> caster, Situation situation) {
|
public boolean tick(Caster<?> caster, Situation situation) {
|
||||||
timer.tick();
|
timer.tick();
|
||||||
|
|
||||||
if (timer.duration <= 0) {
|
if (timer.getTicksRemaining() <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ public class FeatherFallSpell extends AbstractSpell implements TimedSpell {
|
||||||
ParticleUtils.spawnParticles(new MagicParticleEffect(getType().getColor()), target, 7);
|
ParticleUtils.spawnParticles(new MagicParticleEffect(getType().getColor()), target, 7);
|
||||||
});
|
});
|
||||||
|
|
||||||
return caster.subtractEnergyCost(timer.duration % 50 == 0 ? getCostPerEntity() * targets.size() : 0);
|
return caster.subtractEnergyCost(timer.getTicksRemaining() % 50 == 0 ? getCostPerEntity() * targets.size() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected double getCostPerEntity() {
|
protected double getCostPerEntity() {
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
package com.minelittlepony.unicopia.ability.magic.spell.effect;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||||
|
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
|
||||||
|
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
|
||||||
|
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
||||||
|
import com.minelittlepony.unicopia.block.data.Ether;
|
||||||
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
|
import com.minelittlepony.unicopia.particle.UParticles;
|
||||||
|
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
||||||
|
import com.minelittlepony.unicopia.util.shape.*;
|
||||||
|
|
||||||
|
import net.minecraft.block.*;
|
||||||
|
import net.minecraft.fluid.*;
|
||||||
|
import net.minecraft.nbt.*;
|
||||||
|
import net.minecraft.sound.SoundEvents;
|
||||||
|
import net.minecraft.state.property.Properties;
|
||||||
|
import net.minecraft.tag.TagKey;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
public class HydrophobicSpell extends AbstractSpell {
|
||||||
|
public static final SpellTraits DEFAULT_TRAITS = new SpellTraits.Builder()
|
||||||
|
.with(Trait.FOCUS, 5)
|
||||||
|
.with(Trait.KNOWLEDGE, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
private final TagKey<Fluid> affectedFluid;
|
||||||
|
|
||||||
|
private final Set<Entry> storedFluidPositions = new HashSet<>();
|
||||||
|
|
||||||
|
protected HydrophobicSpell(CustomisedSpellType<?> type, TagKey<Fluid> affectedFluid) {
|
||||||
|
super(type);
|
||||||
|
this.affectedFluid = affectedFluid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Caster<?> source) {
|
||||||
|
if (getTraits().get(Trait.GENEROSITY) > 0) {
|
||||||
|
return toPlaceable().apply(source);
|
||||||
|
}
|
||||||
|
return super.apply(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean tick(Caster<?> source, Situation situation) {
|
||||||
|
if (!source.isClient()) {
|
||||||
|
World world = source.getReferenceWorld();
|
||||||
|
|
||||||
|
Shape area = new Sphere(false, getRange(source)).offset(source.getOriginVector());
|
||||||
|
|
||||||
|
storedFluidPositions.removeIf(entry -> {
|
||||||
|
if (!area.isPointInside(Vec3d.ofCenter(entry.pos()))) {
|
||||||
|
entry.restore(world);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
area.getBlockPositions().forEach(pos -> {
|
||||||
|
BlockState state = world.getBlockState(pos);
|
||||||
|
|
||||||
|
if (state.getFluidState().isIn(affectedFluid)) {
|
||||||
|
Block block = state.getBlock();
|
||||||
|
|
||||||
|
if (block instanceof FluidBlock) {
|
||||||
|
world.setBlockState(pos, Blocks.AIR.getDefaultState(), Block.NOTIFY_LISTENERS);
|
||||||
|
storedFluidPositions.add(new Entry(pos, state.getFluidState()));
|
||||||
|
} else if (state.contains(Properties.WATERLOGGED)) {
|
||||||
|
world.setBlockState(pos, state.cycle(Properties.WATERLOGGED), Block.NOTIFY_LISTENERS);
|
||||||
|
storedFluidPositions.add(new Entry(pos, state.getFluidState()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
source.subtractEnergyCost(storedFluidPositions.isEmpty() ? 0.001F : 0.02F);
|
||||||
|
source.spawnParticles(new Sphere(true, getRange(source)), 10, pos -> {
|
||||||
|
BlockPos bp = new BlockPos(pos);
|
||||||
|
if (source.getReferenceWorld().getFluidState(bp.up()).isIn(affectedFluid)) {
|
||||||
|
source.addParticle(UParticles.RAIN_DROPS, pos, Vec3d.ZERO);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (source.getMaster().age % 200 == 0) {
|
||||||
|
source.playSound(SoundEvents.BLOCK_BEACON_AMBIENT, 0.5F);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !isDead();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyed(Caster<?> caster) {
|
||||||
|
storedFluidPositions.removeIf(entry -> {
|
||||||
|
entry.restore(caster.getReferenceWorld());
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void toNBT(NbtCompound compound) {
|
||||||
|
super.toNBT(compound);
|
||||||
|
compound.put("storedFluidPositions", Entry.SERIALIZER.writeAll(storedFluidPositions));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fromNBT(NbtCompound compound) {
|
||||||
|
super.fromNBT(compound);
|
||||||
|
storedFluidPositions.clear();
|
||||||
|
storedFluidPositions.addAll(Entry.SERIALIZER.readAll(compound.getList("storedFluidPositions", NbtElement.COMPOUND_TYPE)).toList());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Calculates the maximum radius of the shield. aka The area of effect.
|
||||||
|
*/
|
||||||
|
public double getRange(Caster<?> source) {
|
||||||
|
float multiplier = 1;
|
||||||
|
float min = (source instanceof Pony ? 4 : 6) + getTraits().get(Trait.POWER);
|
||||||
|
double range = (min + (source.getLevel().getScaled(source instanceof Pony ? 4 : 40) * (source instanceof Pony ? 2 : 10))) / multiplier;
|
||||||
|
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
record Entry (BlockPos pos, FluidState fluidState) {
|
||||||
|
public static final Serializer<Entry> SERIALIZER = Serializer.of(compound -> new Entry(
|
||||||
|
NbtSerialisable.BLOCK_POS.read(compound.getCompound("pos")),
|
||||||
|
NbtSerialisable.decode(FluidState.CODEC, compound.get("state"))
|
||||||
|
), entry -> {
|
||||||
|
NbtCompound compound = new NbtCompound();
|
||||||
|
compound.put("pos", NbtSerialisable.BLOCK_POS.write(entry.pos));
|
||||||
|
compound.put("state", NbtSerialisable.encode(FluidState.CODEC, entry.fluidState));
|
||||||
|
return compound;
|
||||||
|
});
|
||||||
|
|
||||||
|
void restore(World world) {
|
||||||
|
BlockState state = world.getBlockState(pos);
|
||||||
|
|
||||||
|
if (state.isAir()) {
|
||||||
|
world.setBlockState(pos, Fluids.WATER.getDefaultState().getBlockState(), Block.NOTIFY_LISTENERS);
|
||||||
|
} else if (state.contains(Properties.WATERLOGGED)) {
|
||||||
|
world.setBlockState(pos, state.cycle(Properties.WATERLOGGED), Block.NOTIFY_LISTENERS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean blocksFlow(Caster<?> caster, BlockPos pos, FluidState fluid) {
|
||||||
|
if (fluid.isIn(affectedFluid) && pos.isWithinDistance(caster.getOrigin(), getRange(caster) + 1)) {
|
||||||
|
System.out.println("AHA!");
|
||||||
|
}
|
||||||
|
return fluid.isIn(affectedFluid) && pos.isWithinDistance(caster.getOrigin(), getRange(caster) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean blocksFluidFlow(World world, BlockPos pos, BlockState state, Fluid fluid) {
|
||||||
|
return Ether.get(world).findAllSpellsInRange(pos, 500, SpellType.HYDROPHOBIC).anyMatch(pair -> {
|
||||||
|
return pair.getValue().blocksFlow(pair.getKey(), pos, fluid.getDefaultState());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,7 +48,7 @@ public class LightSpell extends AbstractSpell implements TimedSpell {
|
||||||
|
|
||||||
timer.tick();
|
timer.tick();
|
||||||
|
|
||||||
if (timer.duration <= 0) {
|
if (timer.getTicksRemaining() <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
package com.minelittlepony.unicopia.ability.magic.spell.effect;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||||
|
import com.minelittlepony.unicopia.ability.magic.spell.*;
|
||||||
|
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
|
||||||
|
public class MimicSpell extends AbstractDisguiseSpell implements HomingSpell, TimedSpell {
|
||||||
|
|
||||||
|
private final Timer timer;
|
||||||
|
|
||||||
|
protected MimicSpell(CustomisedSpellType<?> type) {
|
||||||
|
super(type);
|
||||||
|
timer = new Timer((120 + (int)(getTraits().get(Trait.FOCUS, 0, 160) * 19)) * 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Timer getTimer() {
|
||||||
|
return timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean tick(Caster<?> caster, Situation situation) {
|
||||||
|
timer.tick();
|
||||||
|
|
||||||
|
if (timer.getTicksRemaining() <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDirty();
|
||||||
|
|
||||||
|
return super.tick(caster, situation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setTarget(Entity target) {
|
||||||
|
setDisguise(target);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void toNBT(NbtCompound compound) {
|
||||||
|
super.toNBT(compound);
|
||||||
|
timer.toNBT(compound);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fromNBT(NbtCompound compound) {
|
||||||
|
super.fromNBT(compound);
|
||||||
|
timer.fromNBT(compound);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
package com.minelittlepony.unicopia.ability.magic.spell.effect;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||||
|
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
|
||||||
|
import com.minelittlepony.unicopia.entity.EntityReference;
|
||||||
|
import com.minelittlepony.unicopia.entity.behaviour.EntitySwap;
|
||||||
|
import com.minelittlepony.unicopia.entity.behaviour.Inventory;
|
||||||
|
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
import net.minecraft.entity.damage.DamageSource;
|
||||||
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
import net.minecraft.nbt.NbtElement;
|
||||||
|
import net.minecraft.sound.SoundEvents;
|
||||||
|
|
||||||
|
public class MindSwapSpell extends MimicSpell {
|
||||||
|
|
||||||
|
private final EntityReference<LivingEntity> counterpart = new EntityReference<>();
|
||||||
|
|
||||||
|
private Optional<Inventory> myStoredInventory = Optional.empty();
|
||||||
|
private Optional<Inventory> theirStoredInventory = Optional.empty();
|
||||||
|
|
||||||
|
private boolean initialized;
|
||||||
|
|
||||||
|
protected MindSwapSpell(CustomisedSpellType<?> type) {
|
||||||
|
super(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyed(Caster<?> caster) {
|
||||||
|
super.onDestroyed(caster);
|
||||||
|
if (initialized && !caster.isClient()) {
|
||||||
|
counterpart.ifPresent(caster.getReferenceWorld(), e -> {
|
||||||
|
EntitySwap.ALL.accept(e, caster.getMaster());
|
||||||
|
Inventory.swapInventories(
|
||||||
|
e, myStoredInventory.or(() -> Inventory.of(e)),
|
||||||
|
caster.getMaster(), theirStoredInventory.or(() -> Inventory.of(caster.getMaster())),
|
||||||
|
a -> {},
|
||||||
|
a -> {}
|
||||||
|
);
|
||||||
|
|
||||||
|
Caster<?> other = Caster.of(e).get();
|
||||||
|
other.getSpellSlot().removeIf(SpellType.MIMIC, true);
|
||||||
|
|
||||||
|
other.playSound(SoundEvents.ENTITY_ZOMBIE_INFECT, 1);
|
||||||
|
caster.playSound(SoundEvents.ENTITY_ZOMBIE_INFECT, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean tick(Caster<?> caster, Situation situation) {
|
||||||
|
|
||||||
|
if (!caster.isClient()) {
|
||||||
|
if (!initialized) {
|
||||||
|
initialized = true;
|
||||||
|
setDirty();
|
||||||
|
counterpart.ifPresent(caster.getReferenceWorld(), e -> {
|
||||||
|
setDisguise(e);
|
||||||
|
Caster<?> other = Caster.of(e).get();
|
||||||
|
SpellType.MIMIC.withTraits().apply(other).setDisguise(caster.getMaster());
|
||||||
|
|
||||||
|
EntitySwap.ALL.accept(caster.getMaster(), e);
|
||||||
|
Inventory.swapInventories(
|
||||||
|
caster.getMaster(), Inventory.of(caster.getMaster()),
|
||||||
|
e, Inventory.of(e),
|
||||||
|
a -> myStoredInventory = Optional.of(a),
|
||||||
|
a -> theirStoredInventory = Optional.of(a)
|
||||||
|
);
|
||||||
|
|
||||||
|
other.playSound(SoundEvents.ENTITY_ZOMBIE_INFECT, 1);
|
||||||
|
caster.playSound(SoundEvents.ENTITY_ZOMBIE_INFECT, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (counterpart.getId().isPresent() && counterpart.get(caster.getReferenceWorld()) == null) {
|
||||||
|
caster.getMaster().damage(DamageSource.MAGIC, Float.MAX_VALUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.tick(caster, situation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setTarget(Entity target) {
|
||||||
|
if (target instanceof LivingEntity living && Caster.of(target).isPresent()) {
|
||||||
|
counterpart.set(living);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void toNBT(NbtCompound compound) {
|
||||||
|
super.toNBT(compound);
|
||||||
|
compound.put("counterpart", counterpart.toNBT());
|
||||||
|
compound.putBoolean("initialized", initialized);
|
||||||
|
myStoredInventory.ifPresent(mine -> compound.put("myStoredInventory", mine.toNBT(new NbtCompound())));
|
||||||
|
theirStoredInventory.ifPresent(mine -> compound.put("theirStoredInventory", mine.toNBT(new NbtCompound())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fromNBT(NbtCompound compound) {
|
||||||
|
super.fromNBT(compound);
|
||||||
|
counterpart.fromNBT(compound.getCompound("counterpart"));
|
||||||
|
initialized = compound.getBoolean("initialized");
|
||||||
|
myStoredInventory = Optional.ofNullable(compound.contains("myStoredInventory", NbtElement.COMPOUND_TYPE) ? Inventory.fromNBT(compound.getCompound("myStoredInventory")) : null);
|
||||||
|
theirStoredInventory = Optional.ofNullable(compound.contains("theirStoredInventory", NbtElement.COMPOUND_TYPE) ? Inventory.fromNBT(compound.getCompound("theirStoredInventory")) : null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ import com.minelittlepony.unicopia.Affinity;
|
||||||
import com.minelittlepony.unicopia.Unicopia;
|
import com.minelittlepony.unicopia.Unicopia;
|
||||||
import com.minelittlepony.unicopia.ability.magic.Affine;
|
import com.minelittlepony.unicopia.ability.magic.Affine;
|
||||||
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
|
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.AbstractDisguiseSpell;
|
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.DispersableDisguiseSpell;
|
import com.minelittlepony.unicopia.ability.magic.spell.DispersableDisguiseSpell;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.RainboomAbilitySpell;
|
import com.minelittlepony.unicopia.ability.magic.spell.RainboomAbilitySpell;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell;
|
import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell;
|
||||||
|
@ -24,6 +23,7 @@ import com.minelittlepony.unicopia.util.Registries;
|
||||||
|
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
import net.minecraft.tag.FluidTags;
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.Util;
|
import net.minecraft.util.Util;
|
||||||
|
@ -39,7 +39,7 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
|
||||||
public static final SpellType<PlaceableSpell> PLACED_SPELL = register("placed", Affinity.NEUTRAL, 0, false, SpellTraits.EMPTY, PlaceableSpell::new);
|
public static final SpellType<PlaceableSpell> PLACED_SPELL = register("placed", Affinity.NEUTRAL, 0, false, SpellTraits.EMPTY, PlaceableSpell::new);
|
||||||
public static final SpellType<ThrowableSpell> THROWN_SPELL = register("thrown", Affinity.NEUTRAL, 0, false, SpellTraits.EMPTY, ThrowableSpell::new);
|
public static final SpellType<ThrowableSpell> THROWN_SPELL = register("thrown", Affinity.NEUTRAL, 0, false, SpellTraits.EMPTY, ThrowableSpell::new);
|
||||||
|
|
||||||
public static final SpellType<AbstractDisguiseSpell> CHANGELING_DISGUISE = register("disguise", Affinity.BAD, 0x19E48E, false, SpellTraits.EMPTY, DispersableDisguiseSpell::new);
|
public static final SpellType<DispersableDisguiseSpell> CHANGELING_DISGUISE = register("disguise", Affinity.BAD, 0x19E48E, false, SpellTraits.EMPTY, DispersableDisguiseSpell::new);
|
||||||
public static final SpellType<RainboomAbilitySpell> RAINBOOM = register("rainboom", Affinity.GOOD, 0xBDBDF9, false, SpellTraits.EMPTY, RainboomAbilitySpell::new);
|
public static final SpellType<RainboomAbilitySpell> RAINBOOM = register("rainboom", Affinity.GOOD, 0xBDBDF9, false, SpellTraits.EMPTY, RainboomAbilitySpell::new);
|
||||||
|
|
||||||
public static final SpellType<IceSpell> FROST = register("frost", Affinity.GOOD, 0xEABBFF, true, IceSpell.DEFAULT_TRAITS, IceSpell::new);
|
public static final SpellType<IceSpell> FROST = register("frost", Affinity.GOOD, 0xEABBFF, true, IceSpell.DEFAULT_TRAITS, IceSpell::new);
|
||||||
|
@ -62,6 +62,9 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
|
||||||
public static final SpellType<LightSpell> LIGHT = register("light", Affinity.GOOD, 0xEEFFAA, true, LightSpell.DEFAULT_TRAITS, LightSpell::new);
|
public static final SpellType<LightSpell> LIGHT = register("light", Affinity.GOOD, 0xEEFFAA, true, LightSpell.DEFAULT_TRAITS, LightSpell::new);
|
||||||
public static final SpellType<DisplacementSpell> DISPLACEMENT = register("displacement", Affinity.NEUTRAL, 0x9900FF, true, PortalSpell.DEFAULT_TRAITS, DisplacementSpell::new);
|
public static final SpellType<DisplacementSpell> DISPLACEMENT = register("displacement", Affinity.NEUTRAL, 0x9900FF, true, PortalSpell.DEFAULT_TRAITS, DisplacementSpell::new);
|
||||||
public static final SpellType<PortalSpell> PORTAL = register("portal", Affinity.GOOD, 0x99FFFF, true, PortalSpell.DEFAULT_TRAITS, PortalSpell::new);
|
public static final SpellType<PortalSpell> PORTAL = register("portal", Affinity.GOOD, 0x99FFFF, true, PortalSpell.DEFAULT_TRAITS, PortalSpell::new);
|
||||||
|
public static final SpellType<MimicSpell> MIMIC = register("mimic", Affinity.GOOD, 0xFFFF00, true, SpellTraits.EMPTY, MimicSpell::new);
|
||||||
|
public static final SpellType<MindSwapSpell> MIND_SWAP = register("mind_swap", Affinity.BAD, 0xF9FF99, true, SpellTraits.EMPTY, MindSwapSpell::new);
|
||||||
|
public static final SpellType<HydrophobicSpell> HYDROPHOBIC = register("hydrophobic", Affinity.NEUTRAL, 0xF999FF, true, SpellTraits.EMPTY, s -> new HydrophobicSpell(s, FluidTags.WATER));
|
||||||
|
|
||||||
public static void bootstrap() {}
|
public static void bootstrap() {}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.block.data;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.Unicopia;
|
import com.minelittlepony.unicopia.Unicopia;
|
||||||
|
import com.minelittlepony.unicopia.ability.magic.CasterView;
|
||||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||||
import com.minelittlepony.unicopia.entity.EntityReference;
|
import com.minelittlepony.unicopia.entity.EntityReference;
|
||||||
|
@ -14,7 +15,7 @@ import net.minecraft.util.Util;
|
||||||
import net.minecraft.world.PersistentState;
|
import net.minecraft.world.PersistentState;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class Ether extends PersistentState {
|
public class Ether extends PersistentState implements CasterView {
|
||||||
private static final Identifier ID = Unicopia.id("ether");
|
private static final Identifier ID = Unicopia.id("ether");
|
||||||
|
|
||||||
public static Ether get(World world) {
|
public static Ether get(World world) {
|
||||||
|
@ -25,6 +26,8 @@ public class Ether extends PersistentState {
|
||||||
|
|
||||||
private final Object locker = new Object();
|
private final Object locker = new Object();
|
||||||
|
|
||||||
|
private final World world;
|
||||||
|
|
||||||
Ether(World world, NbtCompound compound) {
|
Ether(World world, NbtCompound compound) {
|
||||||
this(world);
|
this(world);
|
||||||
compound.getKeys().forEach(key -> {
|
compound.getKeys().forEach(key -> {
|
||||||
|
@ -41,7 +44,7 @@ public class Ether extends PersistentState {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ether(World world) {
|
Ether(World world) {
|
||||||
|
this.world = world;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -116,6 +119,11 @@ public class Ether extends PersistentState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public World getWorld() {
|
||||||
|
return world;
|
||||||
|
}
|
||||||
|
|
||||||
public class Entry implements NbtSerialisable {
|
public class Entry implements NbtSerialisable {
|
||||||
public final EntityReference<?> entity;
|
public final EntityReference<?> entity;
|
||||||
private boolean removed;
|
private boolean removed;
|
||||||
|
|
|
@ -84,7 +84,7 @@ public class DismissSpellScreen extends GameGui {
|
||||||
DrawableUtil.drawArc(matrices, 160, 1600, 0, DrawableUtil.TAU, 0x00000020, false);
|
DrawableUtil.drawArc(matrices, 160, 1600, 0, DrawableUtil.TAU, 0x00000020, false);
|
||||||
|
|
||||||
super.render(matrices, mouseX, mouseY, delta);
|
super.render(matrices, mouseX, mouseY, delta);
|
||||||
DrawableUtil.renderRaceIcon(matrices, pony.getActualSpecies(), 0, 0, 16);
|
DrawableUtil.renderRaceIcon(matrices, pony.getSpecies(), 0, 0, 16);
|
||||||
matrices.pop();
|
matrices.pop();
|
||||||
|
|
||||||
DrawableUtil.drawLine(matrices, mouseX, mouseY - 4, mouseX, mouseY + 4, 0xFFAAFF99);
|
DrawableUtil.drawLine(matrices, mouseX, mouseY - 4, mouseX, mouseY + 4, 0xFFAAFF99);
|
||||||
|
|
|
@ -309,8 +309,8 @@ public class ButterflyEntity extends AmbientEntity {
|
||||||
super.writeCustomDataToNbt(nbt);
|
super.writeCustomDataToNbt(nbt);
|
||||||
nbt.putInt("ticksResting", ticksResting);
|
nbt.putInt("ticksResting", ticksResting);
|
||||||
nbt.putInt("breedingCooldown", breedingCooldown);
|
nbt.putInt("breedingCooldown", breedingCooldown);
|
||||||
NbtSerialisable.writeBlockPos("hoveringPosition", hoveringPosition, nbt);
|
NbtSerialisable.BLOCK_POS.writeOptional("hoveringPosition", nbt, hoveringPosition);
|
||||||
NbtSerialisable.writeBlockPos("flowerPosition", flowerPosition, nbt);
|
NbtSerialisable.BLOCK_POS.writeOptional("flowerPosition", nbt, flowerPosition);
|
||||||
NbtCompound visited = new NbtCompound();
|
NbtCompound visited = new NbtCompound();
|
||||||
this.visited.forEach((pos, time) -> {
|
this.visited.forEach((pos, time) -> {
|
||||||
visited.putLong(String.valueOf(pos.asLong()), time);
|
visited.putLong(String.valueOf(pos.asLong()), time);
|
||||||
|
@ -323,8 +323,8 @@ public class ButterflyEntity extends AmbientEntity {
|
||||||
super.readCustomDataFromNbt(nbt);
|
super.readCustomDataFromNbt(nbt);
|
||||||
ticksResting = nbt.getInt("ticksResting");
|
ticksResting = nbt.getInt("ticksResting");
|
||||||
breedingCooldown = nbt.getInt("breedingCooldown");
|
breedingCooldown = nbt.getInt("breedingCooldown");
|
||||||
hoveringPosition = NbtSerialisable.readBlockPos("hoveringPosition", nbt);
|
hoveringPosition = NbtSerialisable.BLOCK_POS.readOptional("hoveringPosition", nbt);
|
||||||
flowerPosition = NbtSerialisable.readBlockPos("flowerPosition", nbt);
|
flowerPosition = NbtSerialisable.BLOCK_POS.readOptional("flowerPosition", nbt);
|
||||||
NbtCompound visited = nbt.getCompound("visited");
|
NbtCompound visited = nbt.getCompound("visited");
|
||||||
this.visited.clear();
|
this.visited.clear();
|
||||||
visited.getKeys().forEach(key -> {
|
visited.getKeys().forEach(key -> {
|
||||||
|
|
|
@ -30,7 +30,9 @@ import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.entity.projectile.ProjectileEntity;
|
import net.minecraft.entity.projectile.ProjectileEntity;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket;
|
||||||
import net.minecraft.particle.ParticleTypes;
|
import net.minecraft.particle.ParticleTypes;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.sound.SoundEvents;
|
import net.minecraft.sound.SoundEvents;
|
||||||
import net.minecraft.util.Hand;
|
import net.minecraft.util.Hand;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
@ -247,4 +249,10 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
|
||||||
// ply.networkHandler.sendPacket(new EntityVelocityUpdateS2CPacket(ply));
|
// ply.networkHandler.sendPacket(new EntityVelocityUpdateS2CPacket(ply));
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void transmitPassengers(Entity entity) {
|
||||||
|
if (entity.world instanceof ServerWorld sw) {
|
||||||
|
sw.getChunkManager().sendToNearbyPlayers(entity, new EntityPassengersSetS2CPacket(entity));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,9 +112,9 @@ public interface Disguise extends FlightType.Provider, PlayerDimensions.Provider
|
||||||
return !isDead() && !source.getMaster().isDead();
|
return !isDead() && !source.getMaster().isDead();
|
||||||
}
|
}
|
||||||
|
|
||||||
static abstract class PlayerAccess extends PlayerEntity {
|
public static abstract class PlayerAccess extends PlayerEntity {
|
||||||
public PlayerAccess() { super(null, null, 0, null, null); }
|
public PlayerAccess() { super(null, null, 0, null, null); }
|
||||||
static TrackedData<Byte> getModelBitFlag() {
|
public static TrackedData<Byte> getModelBitFlag() {
|
||||||
return PLAYER_MODEL_PARTS;
|
return PLAYER_MODEL_PARTS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ import net.minecraft.entity.mob.VexEntity;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.entity.projectile.ShulkerBulletEntity;
|
import net.minecraft.entity.projectile.ShulkerBulletEntity;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
import net.minecraft.nbt.NbtElement;
|
||||||
import net.minecraft.util.shape.VoxelShape;
|
import net.minecraft.util.shape.VoxelShape;
|
||||||
|
|
||||||
public class EntityAppearance implements NbtSerialisable, PlayerDimensions.Provider, FlightType.Provider, EntityCollisions.ComplexCollidable {
|
public class EntityAppearance implements NbtSerialisable, PlayerDimensions.Provider, FlightType.Provider, EntityCollisions.ComplexCollidable {
|
||||||
|
@ -132,6 +133,9 @@ public class EntityAppearance implements NbtSerialisable, PlayerDimensions.Provi
|
||||||
entity = InteractionManager.instance().createPlayer(source.getEntity(), profile);
|
entity = InteractionManager.instance().createPlayer(source.getEntity(), profile);
|
||||||
entity.setCustomName(source.getMaster().getName());
|
entity.setCustomName(source.getMaster().getName());
|
||||||
((PlayerEntity)entity).readNbt(nbt.getCompound("playerNbt"));
|
((PlayerEntity)entity).readNbt(nbt.getCompound("playerNbt"));
|
||||||
|
if (nbt.contains("playerVisibleParts", NbtElement.BYTE_TYPE)) {
|
||||||
|
entity.getDataTracker().set(Disguise.PlayerAccess.getModelBitFlag(), nbt.getByte("playerVisibleParts"));
|
||||||
|
}
|
||||||
entity.setUuid(UUID.randomUUID());
|
entity.setUuid(UUID.randomUUID());
|
||||||
entity.extinguish();
|
entity.extinguish();
|
||||||
|
|
||||||
|
@ -292,7 +296,7 @@ public class EntityAppearance implements NbtSerialisable, PlayerDimensions.Provi
|
||||||
String newId = compound.getString("entityId");
|
String newId = compound.getString("entityId");
|
||||||
|
|
||||||
String newPlayerName = null;
|
String newPlayerName = null;
|
||||||
if (compound.contains("entity") && compound.getCompound("entity").contains("playerName")) {
|
if (compound.contains("entity", NbtElement.COMPOUND_TYPE) && compound.getCompound("entity").contains("playerName", NbtElement.STRING_TYPE)) {
|
||||||
newPlayerName = compound.getCompound("entity").getString("playerName");
|
newPlayerName = compound.getCompound("entity").getString("playerName");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,13 +307,11 @@ public class EntityAppearance implements NbtSerialisable, PlayerDimensions.Provi
|
||||||
remove();
|
remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compound.contains("entity")) {
|
if (compound.contains("entity", NbtElement.COMPOUND_TYPE)) {
|
||||||
entityId = newId;
|
entityId = newId;
|
||||||
|
|
||||||
entityNbt = compound.getCompound("entity");
|
entityNbt = compound.getCompound("entity");
|
||||||
|
|
||||||
compound.getString("entityData");
|
|
||||||
|
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
try {
|
try {
|
||||||
entity.readNbt(entityNbt);
|
entity.readNbt(entityNbt);
|
||||||
|
@ -332,20 +334,16 @@ public class EntityAppearance implements NbtSerialisable, PlayerDimensions.Provi
|
||||||
private static NbtCompound encodeEntityToNBT(Entity entity) {
|
private static NbtCompound encodeEntityToNBT(Entity entity) {
|
||||||
NbtCompound entityNbt = new NbtCompound();
|
NbtCompound entityNbt = new NbtCompound();
|
||||||
|
|
||||||
if (entity instanceof PlayerEntity) {
|
if (entity instanceof PlayerEntity player) {
|
||||||
GameProfile profile = ((PlayerEntity)entity).getGameProfile();
|
GameProfile profile = player.getGameProfile();
|
||||||
|
|
||||||
entityNbt.putString("id", "player");
|
entityNbt.putString("id", "player");
|
||||||
if (profile.getId() != null) {
|
if (profile.getId() != null) {
|
||||||
entityNbt.putUuid("playerId", profile.getId());
|
entityNbt.putUuid("playerId", profile.getId());
|
||||||
}
|
}
|
||||||
entityNbt.putString("playerName", profile.getName());
|
entityNbt.putString("playerName", profile.getName());
|
||||||
|
entityNbt.putByte("playerVisibleParts", player.getDataTracker().get(Disguise.PlayerAccess.getModelBitFlag()));
|
||||||
NbtCompound tag = new NbtCompound();
|
entityNbt.put("playerNbt", player.writeNbt(new NbtCompound()));
|
||||||
|
|
||||||
entity.writeNbt(tag);
|
|
||||||
|
|
||||||
entityNbt.put("playerNbt", tag);
|
|
||||||
} else {
|
} else {
|
||||||
entity.saveSelfNbt(entityNbt);
|
entity.saveSelfNbt(entityNbt);
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,6 +169,7 @@ public class EntityBehaviour<T extends Entity> {
|
||||||
to.prevYaw = from.prevYaw;
|
to.prevYaw = from.prevYaw;
|
||||||
to.horizontalSpeed = from.horizontalSpeed;
|
to.horizontalSpeed = from.horizontalSpeed;
|
||||||
to.prevHorizontalSpeed = from.prevHorizontalSpeed;
|
to.prevHorizontalSpeed = from.prevHorizontalSpeed;
|
||||||
|
to.fallDistance = 0;
|
||||||
to.setOnGround(from.isOnGround());
|
to.setOnGround(from.isOnGround());
|
||||||
to.setInvulnerable(from.isInvulnerable() || (from instanceof PlayerEntity player && player.getAbilities().creativeMode));
|
to.setInvulnerable(from.isInvulnerable() || (from instanceof PlayerEntity player && player.getAbilities().creativeMode));
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package com.minelittlepony.unicopia.entity.behaviour;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.entity.Living;
|
||||||
|
import com.minelittlepony.unicopia.util.Swap;
|
||||||
|
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
import net.minecraft.entity.mob.PathAwareEntity;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.network.packet.s2c.play.EntitySetHeadYawS2CPacket;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.util.math.MathHelper;
|
||||||
|
|
||||||
|
public interface EntitySwap {
|
||||||
|
Swap<Entity> POSITION = Swap.of(Entity::getPos, (entity, pos) -> {
|
||||||
|
entity.teleport(pos.getX(), pos.getY(), pos.getZ());
|
||||||
|
if (entity instanceof PathAwareEntity pae && !(entity instanceof PlayerEntity)) {
|
||||||
|
pae.getNavigation().stop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Swap<Entity> VELOCITY = Swap.of(Entity::getVelocity, (entity, vel) -> {
|
||||||
|
entity.setVelocity(vel);
|
||||||
|
Living.updateVelocity(entity);
|
||||||
|
});
|
||||||
|
Swap<Entity> PITCH = Swap.of(Entity::getPitch, Entity::setPitch);
|
||||||
|
Swap<Entity> YAW = Swap.of(Entity::getYaw, Entity::setYaw);
|
||||||
|
Swap<Entity> HEAD_YAW = Swap.of(Entity::getHeadYaw, (entity, headYaw) -> {
|
||||||
|
entity.setHeadYaw(headYaw);
|
||||||
|
if (entity.world instanceof ServerWorld sw) {
|
||||||
|
sw.getChunkManager().sendToNearbyPlayers(entity, new EntitySetHeadYawS2CPacket(entity, (byte)MathHelper.floor(entity.getHeadYaw() * 256F / 360F)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Swap<Entity> BODY_YAW = Swap.of(Entity::getBodyYaw, Entity::setBodyYaw);
|
||||||
|
Swap<Entity> FIRE_TICKS = Swap.of(Entity::getFireTicks, Entity::setFireTicks);
|
||||||
|
Swap<Entity> PASSENGERS = Swap.of(entity -> new ArrayList<>(entity.getPassengerList()), (entity, passengers) -> {
|
||||||
|
entity.removeAllPassengers();
|
||||||
|
passengers.forEach(passenger -> passenger.startRiding(entity));
|
||||||
|
Living.transmitPassengers(entity);
|
||||||
|
});
|
||||||
|
Swap<LivingEntity> STATUS_EFFECTS = Swap.of(
|
||||||
|
entity -> Set.copyOf(entity.getActiveStatusEffects().values()),
|
||||||
|
(entity, effects) -> {
|
||||||
|
entity.clearStatusEffects();
|
||||||
|
effects.forEach(entity::addStatusEffect);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Swap<LivingEntity> HEALTH = Swap.of(LivingEntity::getHealth, LivingEntity::getMaxHealth, LivingEntity::setHealth, Number::floatValue);
|
||||||
|
Swap<LivingEntity> AIR = Swap.of(LivingEntity::getAir, LivingEntity::getMaxAir, LivingEntity::setAir, Number::intValue);
|
||||||
|
|
||||||
|
List<Swap<Entity>> REGISTRY = new ArrayList<>(List.of(
|
||||||
|
Swap.union(POSITION, VELOCITY, PITCH, YAW, HEAD_YAW, BODY_YAW, FIRE_TICKS, PASSENGERS),
|
||||||
|
Swap.union(STATUS_EFFECTS, HEALTH, AIR).upcast(e -> e instanceof LivingEntity)
|
||||||
|
));
|
||||||
|
Swap<Entity> ALL = Swap.union(REGISTRY);
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
package com.minelittlepony.unicopia.entity.behaviour;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import net.minecraft.entity.EquipmentSlot;
|
||||||
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.entity.player.PlayerInventory;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.nbt.*;
|
||||||
|
import net.minecraft.util.collection.DefaultedList;
|
||||||
|
|
||||||
|
public record Inventory (
|
||||||
|
Map<EquipmentSlot, ItemStack> equipment,
|
||||||
|
Optional<DefaultedList<ItemStack>> mainInventory
|
||||||
|
) {
|
||||||
|
|
||||||
|
public static Optional<Inventory> of(LivingEntity entity) {
|
||||||
|
Map<EquipmentSlot, ItemStack> equipment = new EnumMap<>(EquipmentSlot.class);
|
||||||
|
for (var slot : EquipmentSlot.values()) {
|
||||||
|
equipment.put(slot, entity.getEquippedStack(slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity instanceof PlayerEntity player) {
|
||||||
|
PlayerInventory inventory = player.getInventory();
|
||||||
|
DefaultedList<ItemStack> mainInventory = DefaultedList.ofSize(inventory.size(), ItemStack.EMPTY);
|
||||||
|
for (int i = 0; i < mainInventory.size(); i++) {
|
||||||
|
mainInventory.set(i, inventory.getStack(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.of(new Inventory(equipment, Optional.of(mainInventory)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.of(new Inventory(equipment, Optional.empty()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the inventory into another entity.
|
||||||
|
*
|
||||||
|
* @return Returns the left overs that could not be copied
|
||||||
|
*/
|
||||||
|
public Inventory copyInto(LivingEntity into) {
|
||||||
|
if (into instanceof PlayerEntity player) {
|
||||||
|
mainInventory().ifPresentOrElse(main -> {
|
||||||
|
PlayerInventory pe = player.getInventory();
|
||||||
|
int i = 0;
|
||||||
|
for (; i < pe.size(); i++) {
|
||||||
|
pe.setStack(i, i < main.size() ? main.get(i) : ItemStack.EMPTY);
|
||||||
|
}
|
||||||
|
for (; i < main.size(); i++) {
|
||||||
|
into.dropStack(main.get(i));
|
||||||
|
}
|
||||||
|
}, () -> {
|
||||||
|
PlayerInventory pe = player.getInventory();
|
||||||
|
for (int i = 0; i < pe.size(); i++) {
|
||||||
|
pe.setStack(i, ItemStack.EMPTY);
|
||||||
|
}
|
||||||
|
equipment().forEach(player::equipStack);
|
||||||
|
});
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
equipment().forEach(into::equipStack);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NbtCompound toNBT(NbtCompound compound) {
|
||||||
|
NbtCompound eq = new NbtCompound();
|
||||||
|
equipment().forEach((slot, stack) -> {
|
||||||
|
eq.put(slot.getName(), stack.writeNbt(new NbtCompound()));
|
||||||
|
});
|
||||||
|
compound.put("equipment", eq);
|
||||||
|
mainInventory().ifPresent(main -> {
|
||||||
|
NbtList list = new NbtList();
|
||||||
|
main.forEach(stack -> {
|
||||||
|
list.add(stack.writeNbt(new NbtCompound()));
|
||||||
|
});
|
||||||
|
compound.put("main", list);
|
||||||
|
});
|
||||||
|
return compound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void swapInventories(LivingEntity me, Optional<Inventory> myInv, LivingEntity them, Optional<Inventory> theirInv,
|
||||||
|
Consumer<Inventory> outOverflowConsumer,
|
||||||
|
Consumer<Inventory> inOverflowConsumer) {
|
||||||
|
Optional<Inventory> outOverflow = Inventory.copyInventoryInto(myInv, them);
|
||||||
|
Optional<Inventory> inOverflow = Inventory.copyInventoryInto(theirInv, me);
|
||||||
|
|
||||||
|
outOverflow.ifPresent(outOverflowConsumer);
|
||||||
|
inOverflow.ifPresent(inOverflowConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<Inventory> copyInventoryInto(Optional<Inventory> inventory, LivingEntity to) {
|
||||||
|
return inventory.map(inv -> inv.copyInto(to));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Inventory fromNBT(NbtCompound compound) {
|
||||||
|
Map<EquipmentSlot, ItemStack> equipment = new EnumMap<>(EquipmentSlot.class);
|
||||||
|
NbtCompound eq = compound.getCompound("equipment");
|
||||||
|
eq.getKeys().forEach(key -> {
|
||||||
|
equipment.put(EquipmentSlot.byName(key), ItemStack.fromNbt(eq.getCompound(key)));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!compound.contains("main", NbtElement.LIST_TYPE)) {
|
||||||
|
return new Inventory(equipment, Optional.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
NbtList list = compound.getList("main", NbtElement.COMPOUND_TYPE);
|
||||||
|
DefaultedList<ItemStack> main = DefaultedList.ofSize(list.size(), ItemStack.EMPTY);
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
main.set(i, ItemStack.fromNbt(list.getCompound(i)));
|
||||||
|
}
|
||||||
|
return new Inventory(equipment, Optional.of(main));
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,11 +15,13 @@ import com.minelittlepony.unicopia.*;
|
||||||
import com.minelittlepony.unicopia.ability.AbilityDispatcher;
|
import com.minelittlepony.unicopia.ability.AbilityDispatcher;
|
||||||
import com.minelittlepony.unicopia.ability.EarthPonyStompAbility;
|
import com.minelittlepony.unicopia.ability.EarthPonyStompAbility;
|
||||||
import com.minelittlepony.unicopia.ability.magic.*;
|
import com.minelittlepony.unicopia.ability.magic.*;
|
||||||
|
import com.minelittlepony.unicopia.ability.magic.spell.AbstractDisguiseSpell;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.TraitDiscovery;
|
import com.minelittlepony.unicopia.ability.magic.spell.trait.TraitDiscovery;
|
||||||
import com.minelittlepony.unicopia.advancement.UCriteria;
|
import com.minelittlepony.unicopia.advancement.UCriteria;
|
||||||
import com.minelittlepony.unicopia.entity.*;
|
import com.minelittlepony.unicopia.entity.*;
|
||||||
|
import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance;
|
||||||
import com.minelittlepony.unicopia.entity.duck.LivingEntityDuck;
|
import com.minelittlepony.unicopia.entity.duck.LivingEntityDuck;
|
||||||
import com.minelittlepony.unicopia.entity.effect.SunBlindnessStatusEffect;
|
import com.minelittlepony.unicopia.entity.effect.SunBlindnessStatusEffect;
|
||||||
import com.minelittlepony.unicopia.entity.effect.UEffects;
|
import com.minelittlepony.unicopia.entity.effect.UEffects;
|
||||||
|
@ -52,7 +54,6 @@ import net.minecraft.entity.effect.StatusEffectInstance;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket;
|
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.sound.SoundEvents;
|
import net.minecraft.sound.SoundEvents;
|
||||||
|
@ -167,7 +168,13 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Race getSpecies() {
|
public Race getSpecies() {
|
||||||
return getActualSpecies();
|
return getSpellSlot()
|
||||||
|
.get(SpellPredicate.IS_MIMIC, true)
|
||||||
|
.map(AbstractDisguiseSpell::getDisguise)
|
||||||
|
.map(EntityAppearance::getAppearance)
|
||||||
|
.flatMap(Pony::of)
|
||||||
|
.map(Pony::getActualSpecies)
|
||||||
|
.orElse(getActualSpecies());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Race getActualSpecies() {
|
public Race getActualSpecies() {
|
||||||
|
@ -318,10 +325,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
entity.stopRiding();
|
entity.stopRiding();
|
||||||
|
Living.transmitPassengers(ridee);
|
||||||
if (ridee instanceof ServerPlayerEntity) {
|
|
||||||
((ServerPlayerEntity)ridee).networkHandler.sendPacket(new EntityPassengersSetS2CPacket(ridee));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -567,7 +571,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
|
||||||
compound.putString("playerSpecies", Race.REGISTRY.getId(getActualSpecies()).toString());
|
compound.putString("playerSpecies", Race.REGISTRY.getId(getActualSpecies()).toString());
|
||||||
compound.putFloat("magicExhaustion", magicExhaustion);
|
compound.putFloat("magicExhaustion", magicExhaustion);
|
||||||
compound.putInt("ticksHanging", ticksHanging);
|
compound.putInt("ticksHanging", ticksHanging);
|
||||||
NbtSerialisable.writeBlockPos("hangingPosition", hangingPosition, compound);
|
BLOCK_POS.writeOptional("hangingPosition", compound, hangingPosition);
|
||||||
compound.putInt("ticksInSun", ticksInSun);
|
compound.putInt("ticksInSun", ticksInSun);
|
||||||
compound.putBoolean("hasShades", hasShades);
|
compound.putBoolean("hasShades", hasShades);
|
||||||
compound.put("powers", powers.toNBT());
|
compound.put("powers", powers.toNBT());
|
||||||
|
@ -604,7 +608,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
|
||||||
|
|
||||||
magicExhaustion = compound.getFloat("magicExhaustion");
|
magicExhaustion = compound.getFloat("magicExhaustion");
|
||||||
ticksHanging = compound.getInt("ticksHanging");
|
ticksHanging = compound.getInt("ticksHanging");
|
||||||
hangingPosition = NbtSerialisable.readBlockPos("hangingPosition", compound);
|
hangingPosition = NbtSerialisable.BLOCK_POS.readOptional("hangingPosition", compound);
|
||||||
ticksInSun = compound.getInt("ticksInSun");
|
ticksInSun = compound.getInt("ticksInSun");
|
||||||
hasShades = compound.getBoolean("hasShades");
|
hasShades = compound.getBoolean("hasShades");
|
||||||
|
|
||||||
|
|
|
@ -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.ability.magic.spell.effect.HydrophobicSpell;
|
||||||
|
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.fluid.FlowableFluid;
|
||||||
|
import net.minecraft.fluid.Fluid;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.world.BlockView;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
@Mixin(FlowableFluid.class)
|
||||||
|
abstract class MixinFlowableFluid {
|
||||||
|
@Inject(method = "canFill", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void onCanFill(BlockView world, BlockPos pos, BlockState state, Fluid fluid, CallbackInfoReturnable<Boolean> info) {
|
||||||
|
if (world instanceof World w && HydrophobicSpell.blocksFluidFlow(w, pos, state, fluid)) {
|
||||||
|
info.setReturnValue(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,6 +39,6 @@ public interface ParticleSource extends ParticleSpawner {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default void addParticle(ParticleEffect effect, Vec3d position, Vec3d velocity) {
|
default void addParticle(ParticleEffect effect, Vec3d position, Vec3d velocity) {
|
||||||
getReferenceWorld().addParticle(effect, position.x, position.y, position.z, velocity.x, velocity.y, velocity.z);
|
ParticleUtils.spawnParticle(getReferenceWorld(), effect, position, velocity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,15 @@ import java.util.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
|
||||||
import net.minecraft.nbt.*;
|
import net.minecraft.nbt.*;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
|
||||||
public interface NbtSerialisable {
|
public interface NbtSerialisable {
|
||||||
|
Serializer<BlockPos> BLOCK_POS = Serializer.of(NbtHelper::toBlockPos, NbtHelper::fromBlockPos);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to save this to nbt to persist state on file or to transmit over the network
|
* Called to save this to nbt to persist state on file or to transmit over the network
|
||||||
*
|
*
|
||||||
|
@ -41,12 +45,12 @@ public interface NbtSerialisable {
|
||||||
return new Vec3d(list.getDouble(0), list.getDouble(1), list.getDouble(2));
|
return new Vec3d(list.getDouble(0), list.getDouble(1), list.getDouble(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeBlockPos(String name, Optional<BlockPos> pos, NbtCompound nbt) {
|
static <T> T decode(Codec<T> codec, NbtElement nbt) {
|
||||||
pos.map(NbtHelper::fromBlockPos).ifPresent(p -> nbt.put(name, p));
|
return codec.decode(NbtOps.INSTANCE, nbt).result().get().getFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Optional<BlockPos> readBlockPos(String name, NbtCompound nbt) {
|
static <T> NbtElement encode(Codec<T> codec, T value) {
|
||||||
return nbt.contains(name) ? Optional.ofNullable(NbtHelper.toBlockPos(nbt.getCompound(name))) : Optional.empty();
|
return codec.encodeStart(NbtOps.INSTANCE, value).result().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Serializer<T> {
|
interface Serializer<T> {
|
||||||
|
@ -54,6 +58,16 @@ public interface NbtSerialisable {
|
||||||
|
|
||||||
NbtCompound write(T t);
|
NbtCompound write(T t);
|
||||||
|
|
||||||
|
default Optional<T> readOptional(String name, NbtCompound compound) {
|
||||||
|
return compound.contains(name, NbtElement.COMPOUND_TYPE)
|
||||||
|
? Optional.ofNullable(read(compound.getCompound(name)))
|
||||||
|
: Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
default void writeOptional(String name, NbtCompound compound, Optional<T> t) {
|
||||||
|
t.map(this::write).ifPresent(tag -> compound.put(name, tag));
|
||||||
|
}
|
||||||
|
|
||||||
default T read(NbtElement element) {
|
default T read(NbtElement element) {
|
||||||
return read((NbtCompound)element);
|
return read((NbtCompound)element);
|
||||||
}
|
}
|
||||||
|
|
67
src/main/java/com/minelittlepony/unicopia/util/Swap.java
Normal file
67
src/main/java/com/minelittlepony/unicopia/util/Swap.java
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
package com.minelittlepony.unicopia.util;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility for performing an atomic swap of two values.
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Swap<T> extends BiConsumer<T, T> {
|
||||||
|
/**
|
||||||
|
* Returns a new swap that performs the same action as this one only if the passed in shouldRun check passes on both inputs.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
default <E> Swap<E> upcast(Predicate<E> shouldRun) {
|
||||||
|
Swap<T> swap = this;
|
||||||
|
return (a, b) -> {
|
||||||
|
if (shouldRun.test(a) && shouldRun.test(b)) {
|
||||||
|
swap.accept((T)a, (T)b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
static <E> Swap<E> union(Swap<E>... swaps) {
|
||||||
|
return union(List.of(swaps));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a swap from a collection of multiple swaps.
|
||||||
|
* Executes them in the order they are presented by Iterable#forEach
|
||||||
|
*
|
||||||
|
* Changes to the underlying list an equivalent change to the swap returned by this method.
|
||||||
|
*/
|
||||||
|
static <E> Swap<E> union(Iterable<Swap<E>> swaps) {
|
||||||
|
return (a, b) -> swaps.forEach(consumer -> consumer.accept(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a swap for switching numerical values.
|
||||||
|
*/
|
||||||
|
static <E, N extends Number> Swap<E> of(Function<E, N> getter, BiConsumer<E, N> setter, Function<Float, N> converter) {
|
||||||
|
return of(getter, e -> converter.apply(1F), setter, converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a swap for converting numerical values where the source and destination may have different scales.
|
||||||
|
*/
|
||||||
|
static <E, N extends Number> Swap<E> of(Function<E, N> getter, Function<E, N> maxGetter, BiConsumer<E, N> setter, Function<Float, N> converter) {
|
||||||
|
return of(
|
||||||
|
e -> getter.apply(e).floatValue() / maxGetter.apply(e).floatValue(),
|
||||||
|
(e, value) -> setter.accept(e, converter.apply(value.floatValue() * maxGetter.apply(e).floatValue()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a swap from a getter and setter.
|
||||||
|
*/
|
||||||
|
static <E, T> Swap<E> of(Function<E, T> getter, BiConsumer<E, T> setter) {
|
||||||
|
return (a, b) -> {
|
||||||
|
T aa = getter.apply(a);
|
||||||
|
T bb = getter.apply(b);
|
||||||
|
setter.accept(a, bb);
|
||||||
|
setter.accept(b, aa);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ import org.jetbrains.annotations.Nullable;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.util.math.Box;
|
import net.minecraft.util.math.Box;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.EntityView;
|
||||||
|
|
||||||
public interface VecHelper {
|
public interface VecHelper {
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ public interface VecHelper {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<Entity> findInRange(@Nullable Entity origin, World w, Vec3d pos, double radius, @Nullable Predicate<Entity> predicate) {
|
static List<Entity> findInRange(@Nullable Entity origin, EntityView w, Vec3d pos, double radius, @Nullable Predicate<Entity> predicate) {
|
||||||
double diameter = radius * 2;
|
double diameter = radius * 2;
|
||||||
return w.getOtherEntities(origin, Box.of(pos, diameter, diameter, diameter), predicate == null ? inRange(pos, radius) : inRange(pos, radius).and(predicate));
|
return w.getOtherEntities(origin, Box.of(pos, diameter, diameter, diameter), predicate == null ? inRange(pos, radius) : inRange(pos, radius).and(predicate));
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ import net.minecraft.util.math.random.Random;
|
||||||
*Interface for a 3d shape, used for spawning particles in a designated area (or anything else you need shapes for).
|
*Interface for a 3d shape, used for spawning particles in a designated area (or anything else you need shapes for).
|
||||||
*/
|
*/
|
||||||
public interface PointGenerator {
|
public interface PointGenerator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the volume of space filled by this shape, or the surface area if hollow.
|
* Get the volume of space filled by this shape, or the surface area if hollow.
|
||||||
*
|
*
|
||||||
|
@ -72,7 +71,6 @@ public interface PointGenerator {
|
||||||
default PointGenerator offset(Vec3d offset) {
|
default PointGenerator offset(Vec3d offset) {
|
||||||
final PointGenerator source = this;
|
final PointGenerator source = this;
|
||||||
return new PointGenerator() {
|
return new PointGenerator() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getVolumeOfSpawnableSpace() {
|
public double getVolumeOfSpawnableSpace() {
|
||||||
return source.getVolumeOfSpawnableSpace();
|
return source.getVolumeOfSpawnableSpace();
|
||||||
|
|
|
@ -2,8 +2,8 @@ package com.minelittlepony.unicopia.util.shape;
|
||||||
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.*;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.random.Random;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -45,4 +45,51 @@ public interface Shape extends PointGenerator {
|
||||||
}
|
}
|
||||||
return new RotatedShape(this, pitch, yaw);
|
return new RotatedShape(this, pitch, yaw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new point generator where all of its points are offset by the specified amount.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
default Shape offset(Vec3i offset) {
|
||||||
|
return offset(Vec3d.of(offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new point generator where all of its points are offset by the specified amount.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
default Shape offset(Vec3d offset) {
|
||||||
|
final Shape source = this;
|
||||||
|
return new Shape() {
|
||||||
|
@Override
|
||||||
|
public double getVolumeOfSpawnableSpace() {
|
||||||
|
return source.getVolumeOfSpawnableSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vec3d computePoint(Random rand) {
|
||||||
|
return source.computePoint(rand).add(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<BlockPos> getBlockPositions() {
|
||||||
|
return source.getBlockPositions().map(pos -> pos.add(offset.x, offset.y, offset.z));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vec3d getLowerBound() {
|
||||||
|
return source.getLowerBound().add(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vec3d getUpperBound() {
|
||||||
|
return source.getLowerBound().add(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPointInside(Vec3d point) {
|
||||||
|
return source.isPointInside(point.subtract(offset));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,6 +184,8 @@
|
||||||
|
|
||||||
"spell.unicopia.frost": "Frost",
|
"spell.unicopia.frost": "Frost",
|
||||||
"spell.unicopia.frost.lore": "Chilling to the touch, this gem will freeze whatever it is used on",
|
"spell.unicopia.frost.lore": "Chilling to the touch, this gem will freeze whatever it is used on",
|
||||||
|
"spell.unicopia.hydrophobic": "Repel Water",
|
||||||
|
"spell.unicopia.hydrophobic.lore": "Creates a protective bubble around the user that prevents water from entering",
|
||||||
"spell.unicopia.chilling_breath": "Chilling Breath",
|
"spell.unicopia.chilling_breath": "Chilling Breath",
|
||||||
"spell.unicopia.chilling_breath.lore": "Alters the ability of certain objects to distenguish between hot and cold",
|
"spell.unicopia.chilling_breath.lore": "Alters the ability of certain objects to distenguish between hot and cold",
|
||||||
"spell.unicopia.scorch": "Scorching",
|
"spell.unicopia.scorch": "Scorching",
|
||||||
|
@ -202,6 +204,10 @@
|
||||||
"spell.unicopia.vortex.lore": "Creates a magnetic force that pulls in other targets",
|
"spell.unicopia.vortex.lore": "Creates a magnetic force that pulls in other targets",
|
||||||
"spell.unicopia.dark_vortex": "Dark Vortex",
|
"spell.unicopia.dark_vortex": "Dark Vortex",
|
||||||
"spell.unicopia.dark_vortex.lore": "Creates a black hole from which nothing can escape",
|
"spell.unicopia.dark_vortex.lore": "Creates a black hole from which nothing can escape",
|
||||||
|
"spell.unicopia.mimic": "Mimic",
|
||||||
|
"spell.unicopia.mimic.lore": "Temporarily changes the caster's appearance to look like another entity or player",
|
||||||
|
"spell.unicopia.mind_swap": "Mind Swap",
|
||||||
|
"spell.unicopia.mind_swap.lore": "Temporarily swaps the caster's mind into the body of another entity or player",
|
||||||
"spell.unicopia.displacement": "Displacement",
|
"spell.unicopia.displacement": "Displacement",
|
||||||
"spell.unicopia.displacement.lore": "Swaps the caster's locatin with that of another entity",
|
"spell.unicopia.displacement.lore": "Swaps the caster's locatin with that of another entity",
|
||||||
"spell.unicopia.portal": "Arcane Rift",
|
"spell.unicopia.portal": "Arcane Rift",
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
"MixinEntity",
|
"MixinEntity",
|
||||||
"MixinFallingBlock",
|
"MixinFallingBlock",
|
||||||
"MixinFallingBlockEntity",
|
"MixinFallingBlockEntity",
|
||||||
|
"MixinFlowableFluid",
|
||||||
"MixinItem",
|
"MixinItem",
|
||||||
"MixinItemEntity",
|
"MixinItemEntity",
|
||||||
"MixinLivingEntity",
|
"MixinLivingEntity",
|
||||||
|
|
Loading…
Reference in a new issue