mirror of
https://github.com/Sollace/Unicopia.git
synced 2025-02-01 03:26:44 +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.ability.data.Hit;
|
||||
import com.minelittlepony.unicopia.entity.Living;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.minelittlepony.unicopia.util.TraceHelper;
|
||||
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
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;
|
||||
|
||||
/**
|
||||
|
@ -76,9 +75,7 @@ public class CarryAbility implements Ability<Hit> {
|
|||
rider.startRiding(player, true);
|
||||
}
|
||||
|
||||
if (player instanceof ServerPlayerEntity) {
|
||||
((ServerPlayerEntity)player).networkHandler.sendPacket(new EntityPassengersSetS2CPacket(player));
|
||||
}
|
||||
Living.transmitPassengers(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.minelittlepony.unicopia.USounds;
|
|||
import com.minelittlepony.unicopia.ability.data.Hit;
|
||||
import com.minelittlepony.unicopia.ability.data.Pos;
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.entity.Living;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
|
||||
import com.minelittlepony.unicopia.util.Trace;
|
||||
|
@ -17,9 +18,7 @@ import net.minecraft.block.WallBlock;
|
|||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket;
|
||||
import net.minecraft.predicate.entity.EntityPredicates;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
@ -142,10 +141,7 @@ public class UnicornTeleportAbility implements Ability<Pos> {
|
|||
Entity mount = player.getVehicle();
|
||||
|
||||
player.stopRiding();
|
||||
|
||||
if (mount instanceof ServerPlayerEntity) {
|
||||
((ServerPlayerEntity)player).networkHandler.sendPacket(new EntityPassengersSetS2CPacket(player));
|
||||
}
|
||||
Living.transmitPassengers(mount);
|
||||
}
|
||||
|
||||
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 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 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<ProjectileSpell> HAS_PROJECTILE_EVENTS = s -> s instanceof ProjectileSpell;
|
||||
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<TimedSpell> IS_TIMED = spell -> spell instanceof TimedSpell;
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.minelittlepony.unicopia.ability.magic.spell;
|
||||
|
||||
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
||||
import com.minelittlepony.unicopia.util.Tickable;
|
||||
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
@ -11,16 +12,17 @@ import net.minecraft.util.math.MathHelper;
|
|||
public interface TimedSpell extends Spell {
|
||||
Timer getTimer();
|
||||
|
||||
class Timer implements NbtSerialisable {
|
||||
public int maxDuration;
|
||||
public int duration;
|
||||
public int prevDuration;
|
||||
class Timer implements Tickable, NbtSerialisable {
|
||||
private int maxDuration;
|
||||
private int duration;
|
||||
private int prevDuration;
|
||||
|
||||
public Timer(int initial) {
|
||||
maxDuration = initial;
|
||||
duration = initial;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
prevDuration = duration;
|
||||
duration--;
|
||||
|
|
|
@ -37,7 +37,7 @@ public class AttractiveSpell extends ShieldSpell implements ProjectileSpell, Hom
|
|||
public boolean tick(Caster<?> caster, Situation situation) {
|
||||
timer.tick();
|
||||
|
||||
if (timer.duration <= 0) {
|
||||
if (timer.getTicksRemaining() <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,9 +37,11 @@ public class AwkwardSpell extends AbstractSpell implements TimedSpell {
|
|||
if (situation != Situation.PROJECTILE) {
|
||||
timer.tick();
|
||||
|
||||
if (timer.duration <= 0) {
|
||||
if (timer.getTicksRemaining() <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
setDirty();
|
||||
}
|
||||
|
||||
if (source.isClient()) {
|
||||
|
|
|
@ -52,7 +52,7 @@ public class FeatherFallSpell extends AbstractSpell implements TimedSpell {
|
|||
public boolean tick(Caster<?> caster, Situation situation) {
|
||||
timer.tick();
|
||||
|
||||
if (timer.duration <= 0) {
|
||||
if (timer.getTicksRemaining() <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ public class FeatherFallSpell extends AbstractSpell implements TimedSpell {
|
|||
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() {
|
||||
|
|
|
@ -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();
|
||||
|
||||
if (timer.duration <= 0) {
|
||||
if (timer.getTicksRemaining() <= 0) {
|
||||
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.ability.magic.Affine;
|
||||
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.RainboomAbilitySpell;
|
||||
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.nbt.NbtCompound;
|
||||
import net.minecraft.tag.FluidTags;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
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<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<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<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<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() {}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.block.data;
|
|||
import java.util.*;
|
||||
|
||||
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.spell.effect.SpellType;
|
||||
import com.minelittlepony.unicopia.entity.EntityReference;
|
||||
|
@ -14,7 +15,7 @@ import net.minecraft.util.Util;
|
|||
import net.minecraft.world.PersistentState;
|
||||
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");
|
||||
|
||||
public static Ether get(World world) {
|
||||
|
@ -25,6 +26,8 @@ public class Ether extends PersistentState {
|
|||
|
||||
private final Object locker = new Object();
|
||||
|
||||
private final World world;
|
||||
|
||||
Ether(World world, NbtCompound compound) {
|
||||
this(world);
|
||||
compound.getKeys().forEach(key -> {
|
||||
|
@ -41,7 +44,7 @@ public class Ether extends PersistentState {
|
|||
}
|
||||
|
||||
Ether(World world) {
|
||||
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -116,6 +119,11 @@ public class Ether extends PersistentState {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
public class Entry implements NbtSerialisable {
|
||||
public final EntityReference<?> entity;
|
||||
private boolean removed;
|
||||
|
|
|
@ -84,7 +84,7 @@ public class DismissSpellScreen extends GameGui {
|
|||
DrawableUtil.drawArc(matrices, 160, 1600, 0, DrawableUtil.TAU, 0x00000020, false);
|
||||
|
||||
super.render(matrices, mouseX, mouseY, delta);
|
||||
DrawableUtil.renderRaceIcon(matrices, pony.getActualSpecies(), 0, 0, 16);
|
||||
DrawableUtil.renderRaceIcon(matrices, pony.getSpecies(), 0, 0, 16);
|
||||
matrices.pop();
|
||||
|
||||
DrawableUtil.drawLine(matrices, mouseX, mouseY - 4, mouseX, mouseY + 4, 0xFFAAFF99);
|
||||
|
|
|
@ -309,8 +309,8 @@ public class ButterflyEntity extends AmbientEntity {
|
|||
super.writeCustomDataToNbt(nbt);
|
||||
nbt.putInt("ticksResting", ticksResting);
|
||||
nbt.putInt("breedingCooldown", breedingCooldown);
|
||||
NbtSerialisable.writeBlockPos("hoveringPosition", hoveringPosition, nbt);
|
||||
NbtSerialisable.writeBlockPos("flowerPosition", flowerPosition, nbt);
|
||||
NbtSerialisable.BLOCK_POS.writeOptional("hoveringPosition", nbt, hoveringPosition);
|
||||
NbtSerialisable.BLOCK_POS.writeOptional("flowerPosition", nbt, flowerPosition);
|
||||
NbtCompound visited = new NbtCompound();
|
||||
this.visited.forEach((pos, time) -> {
|
||||
visited.putLong(String.valueOf(pos.asLong()), time);
|
||||
|
@ -323,8 +323,8 @@ public class ButterflyEntity extends AmbientEntity {
|
|||
super.readCustomDataFromNbt(nbt);
|
||||
ticksResting = nbt.getInt("ticksResting");
|
||||
breedingCooldown = nbt.getInt("breedingCooldown");
|
||||
hoveringPosition = NbtSerialisable.readBlockPos("hoveringPosition", nbt);
|
||||
flowerPosition = NbtSerialisable.readBlockPos("flowerPosition", nbt);
|
||||
hoveringPosition = NbtSerialisable.BLOCK_POS.readOptional("hoveringPosition", nbt);
|
||||
flowerPosition = NbtSerialisable.BLOCK_POS.readOptional("flowerPosition", nbt);
|
||||
NbtCompound visited = nbt.getCompound("visited");
|
||||
this.visited.clear();
|
||||
visited.getKeys().forEach(key -> {
|
||||
|
|
|
@ -30,7 +30,9 @@ import net.minecraft.entity.player.PlayerEntity;
|
|||
import net.minecraft.entity.projectile.ProjectileEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket;
|
||||
import net.minecraft.particle.ParticleTypes;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.util.Hand;
|
||||
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));
|
||||
//}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
static abstract class PlayerAccess extends PlayerEntity {
|
||||
public static abstract class PlayerAccess extends PlayerEntity {
|
||||
public PlayerAccess() { super(null, null, 0, null, null); }
|
||||
static TrackedData<Byte> getModelBitFlag() {
|
||||
public static TrackedData<Byte> getModelBitFlag() {
|
||||
return PLAYER_MODEL_PARTS;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ import net.minecraft.entity.mob.VexEntity;
|
|||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.projectile.ShulkerBulletEntity;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.nbt.NbtElement;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
|
||||
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.setCustomName(source.getMaster().getName());
|
||||
((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.extinguish();
|
||||
|
||||
|
@ -292,7 +296,7 @@ public class EntityAppearance implements NbtSerialisable, PlayerDimensions.Provi
|
|||
String newId = compound.getString("entityId");
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
|
@ -303,13 +307,11 @@ public class EntityAppearance implements NbtSerialisable, PlayerDimensions.Provi
|
|||
remove();
|
||||
}
|
||||
|
||||
if (compound.contains("entity")) {
|
||||
if (compound.contains("entity", NbtElement.COMPOUND_TYPE)) {
|
||||
entityId = newId;
|
||||
|
||||
entityNbt = compound.getCompound("entity");
|
||||
|
||||
compound.getString("entityData");
|
||||
|
||||
if (entity != null) {
|
||||
try {
|
||||
entity.readNbt(entityNbt);
|
||||
|
@ -332,20 +334,16 @@ public class EntityAppearance implements NbtSerialisable, PlayerDimensions.Provi
|
|||
private static NbtCompound encodeEntityToNBT(Entity entity) {
|
||||
NbtCompound entityNbt = new NbtCompound();
|
||||
|
||||
if (entity instanceof PlayerEntity) {
|
||||
GameProfile profile = ((PlayerEntity)entity).getGameProfile();
|
||||
if (entity instanceof PlayerEntity player) {
|
||||
GameProfile profile = player.getGameProfile();
|
||||
|
||||
entityNbt.putString("id", "player");
|
||||
if (profile.getId() != null) {
|
||||
entityNbt.putUuid("playerId", profile.getId());
|
||||
}
|
||||
entityNbt.putString("playerName", profile.getName());
|
||||
|
||||
NbtCompound tag = new NbtCompound();
|
||||
|
||||
entity.writeNbt(tag);
|
||||
|
||||
entityNbt.put("playerNbt", tag);
|
||||
entityNbt.putByte("playerVisibleParts", player.getDataTracker().get(Disguise.PlayerAccess.getModelBitFlag()));
|
||||
entityNbt.put("playerNbt", player.writeNbt(new NbtCompound()));
|
||||
} else {
|
||||
entity.saveSelfNbt(entityNbt);
|
||||
}
|
||||
|
|
|
@ -169,6 +169,7 @@ public class EntityBehaviour<T extends Entity> {
|
|||
to.prevYaw = from.prevYaw;
|
||||
to.horizontalSpeed = from.horizontalSpeed;
|
||||
to.prevHorizontalSpeed = from.prevHorizontalSpeed;
|
||||
to.fallDistance = 0;
|
||||
to.setOnGround(from.isOnGround());
|
||||
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.EarthPonyStompAbility;
|
||||
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.effect.SpellType;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.TraitDiscovery;
|
||||
import com.minelittlepony.unicopia.advancement.UCriteria;
|
||||
import com.minelittlepony.unicopia.entity.*;
|
||||
import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance;
|
||||
import com.minelittlepony.unicopia.entity.duck.LivingEntityDuck;
|
||||
import com.minelittlepony.unicopia.entity.effect.SunBlindnessStatusEffect;
|
||||
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.item.ItemStack;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
|
@ -167,7 +168,13 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
|
|||
|
||||
@Override
|
||||
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() {
|
||||
|
@ -318,10 +325,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
|
|||
}
|
||||
} else {
|
||||
entity.stopRiding();
|
||||
|
||||
if (ridee instanceof ServerPlayerEntity) {
|
||||
((ServerPlayerEntity)ridee).networkHandler.sendPacket(new EntityPassengersSetS2CPacket(ridee));
|
||||
}
|
||||
Living.transmitPassengers(ridee);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -567,7 +571,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
|
|||
compound.putString("playerSpecies", Race.REGISTRY.getId(getActualSpecies()).toString());
|
||||
compound.putFloat("magicExhaustion", magicExhaustion);
|
||||
compound.putInt("ticksHanging", ticksHanging);
|
||||
NbtSerialisable.writeBlockPos("hangingPosition", hangingPosition, compound);
|
||||
BLOCK_POS.writeOptional("hangingPosition", compound, hangingPosition);
|
||||
compound.putInt("ticksInSun", ticksInSun);
|
||||
compound.putBoolean("hasShades", hasShades);
|
||||
compound.put("powers", powers.toNBT());
|
||||
|
@ -604,7 +608,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
|
|||
|
||||
magicExhaustion = compound.getFloat("magicExhaustion");
|
||||
ticksHanging = compound.getInt("ticksHanging");
|
||||
hangingPosition = NbtSerialisable.readBlockPos("hangingPosition", compound);
|
||||
hangingPosition = NbtSerialisable.BLOCK_POS.readOptional("hangingPosition", compound);
|
||||
ticksInSun = compound.getInt("ticksInSun");
|
||||
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
|
||||
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.stream.Stream;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
|
||||
import net.minecraft.nbt.*;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
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
|
||||
*
|
||||
|
@ -41,12 +45,12 @@ public interface NbtSerialisable {
|
|||
return new Vec3d(list.getDouble(0), list.getDouble(1), list.getDouble(2));
|
||||
}
|
||||
|
||||
static void writeBlockPos(String name, Optional<BlockPos> pos, NbtCompound nbt) {
|
||||
pos.map(NbtHelper::fromBlockPos).ifPresent(p -> nbt.put(name, p));
|
||||
static <T> T decode(Codec<T> codec, NbtElement nbt) {
|
||||
return codec.decode(NbtOps.INSTANCE, nbt).result().get().getFirst();
|
||||
}
|
||||
|
||||
static Optional<BlockPos> readBlockPos(String name, NbtCompound nbt) {
|
||||
return nbt.contains(name) ? Optional.ofNullable(NbtHelper.toBlockPos(nbt.getCompound(name))) : Optional.empty();
|
||||
static <T> NbtElement encode(Codec<T> codec, T value) {
|
||||
return codec.encodeStart(NbtOps.INSTANCE, value).result().get();
|
||||
}
|
||||
|
||||
interface Serializer<T> {
|
||||
|
@ -54,6 +58,16 @@ public interface NbtSerialisable {
|
|||
|
||||
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) {
|
||||
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.util.math.Box;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.EntityView;
|
||||
|
||||
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;
|
||||
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).
|
||||
*/
|
||||
public interface PointGenerator {
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
final PointGenerator source = this;
|
||||
return new PointGenerator() {
|
||||
|
||||
@Override
|
||||
public double getVolumeOfSpawnableSpace() {
|
||||
return source.getVolumeOfSpawnableSpace();
|
||||
|
|
|
@ -2,8 +2,8 @@ package com.minelittlepony.unicopia.util.shape;
|
|||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.*;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -45,4 +45,51 @@ public interface Shape extends PointGenerator {
|
|||
}
|
||||
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.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.lore": "Alters the ability of certain objects to distenguish between hot and cold",
|
||||
"spell.unicopia.scorch": "Scorching",
|
||||
|
@ -202,6 +204,10 @@
|
|||
"spell.unicopia.vortex.lore": "Creates a magnetic force that pulls in other targets",
|
||||
"spell.unicopia.dark_vortex": "Dark Vortex",
|
||||
"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.lore": "Swaps the caster's locatin with that of another entity",
|
||||
"spell.unicopia.portal": "Arcane Rift",
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
"MixinEntity",
|
||||
"MixinFallingBlock",
|
||||
"MixinFallingBlockEntity",
|
||||
"MixinFlowableFluid",
|
||||
"MixinItem",
|
||||
"MixinItemEntity",
|
||||
"MixinLivingEntity",
|
||||
|
|
Loading…
Reference in a new issue