mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-27 07:17:58 +01:00
Work on allowing more than one spell to be attached
This commit is contained in:
parent
bb50b20be4
commit
f3d6c7ce28
22 changed files with 416 additions and 143 deletions
|
@ -68,7 +68,7 @@ public class PegasusRainboomAbility implements Ability<Hit> {
|
|||
if (player.getPhysics().isFlying() && !player.getSpellSlot().isPresent()) {
|
||||
player.getMagicalReserves().getMana().multiply(0.1F);
|
||||
player.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, player.getPhysics().getMotionAngle()), player.getOriginVector(), Vec3d.ZERO);
|
||||
player.setSpell(SpellType.RAINBOOM.create(SpellTraits.EMPTY));
|
||||
player.getSpellSlot().put(SpellType.RAINBOOM.create(SpellTraits.EMPTY));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ import org.jetbrains.annotations.Nullable;
|
|||
|
||||
import com.minelittlepony.unicopia.EquinePredicates;
|
||||
import com.minelittlepony.unicopia.Owned;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||
import com.minelittlepony.unicopia.entity.Physics;
|
||||
import com.minelittlepony.unicopia.entity.PonyContainer;
|
||||
import com.minelittlepony.unicopia.particle.ParticleSource;
|
||||
|
@ -29,10 +28,6 @@ public interface Caster<E extends LivingEntity> extends Owned<E>, Levelled, Affi
|
|||
|
||||
SpellContainer getSpellSlot();
|
||||
|
||||
default void setSpell(@Nullable Spell spell) {
|
||||
getSpellSlot().put(spell);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entity directly responsible for casting.
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.minelittlepony.unicopia.ability.magic;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
@ -15,6 +16,12 @@ public interface SpellContainer {
|
|||
|
||||
@Override
|
||||
public void put(Spell effect) { }
|
||||
|
||||
@Override
|
||||
public void clear() { }
|
||||
|
||||
@Override
|
||||
public void removeIf(Predicate<Spell> effect, boolean update) { }
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -40,4 +47,39 @@ public interface SpellContainer {
|
|||
* Sets the active effect.
|
||||
*/
|
||||
void put(@Nullable Spell effect);
|
||||
|
||||
/**
|
||||
* Removes all matching active effects.
|
||||
*/
|
||||
void removeIf(Predicate<Spell> effect, boolean update);
|
||||
|
||||
/**
|
||||
* Removes all effects currently active in this slot.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
interface Delegate extends SpellContainer {
|
||||
|
||||
SpellContainer delegate();
|
||||
|
||||
@Override
|
||||
default <T extends Spell> Optional<T> get(@Nullable SpellPredicate<T> type, boolean update) {
|
||||
return delegate().get(type, update);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void put(@Nullable Spell effect) {
|
||||
delegate().put(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void removeIf(Predicate<Spell> effect, boolean update) {
|
||||
delegate().removeIf(effect, update);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void clear() {
|
||||
delegate().clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ public abstract class AbstractDelegatingSpell implements ProjectileSpell {
|
|||
|
||||
@Override
|
||||
public boolean apply(Caster<?> caster) {
|
||||
caster.setSpell(this);
|
||||
caster.getSpellSlot().put(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
|
|||
CastSpellEntity entity = UEntities.CAST_SPELL.create(source.getWorld());
|
||||
Vec3d pos = castEntity.getPosition().orElse(source.getOriginVector());
|
||||
entity.updatePositionAndAngles(pos.x, pos.y, pos.z, 0, 0);
|
||||
entity.setSpell(this);
|
||||
entity.getSpellSlot().put(this);
|
||||
entity.setMaster(source.getMaster());
|
||||
entity.world.spawnEntity(entity);
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ public final class ThrowableSpell extends AbstractDelegatingSpell {
|
|||
MagicProjectileEntity projectile = new MagicProjectileEntity(world, entity);
|
||||
|
||||
projectile.setItem(GemstoneItem.enchanted(UItems.GEMSTONE.getDefaultStack(), spell.getType()));
|
||||
projectile.setSpell(this);
|
||||
projectile.getSpellSlot().put(this);
|
||||
projectile.setProperties(entity, entity.getPitch(), entity.getYaw(), 0, 1.5F, 1);
|
||||
projectile.setHydrophobic();
|
||||
configureProjectile(projectile, caster);
|
||||
|
|
|
@ -67,7 +67,7 @@ public abstract class AbstractSpell implements Spell {
|
|||
|
||||
@Override
|
||||
public final boolean apply(Caster<?> caster) {
|
||||
caster.setSpell(this);
|
||||
caster.getSpellSlot().put(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -141,7 +141,7 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
|
|||
@Nullable
|
||||
public T apply(Caster<?> caster, SpellTraits traits) {
|
||||
if (isEmpty()) {
|
||||
caster.setSpell(null);
|
||||
caster.getSpellSlot().clear();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,19 @@ public class CastSpellEntity extends Entity implements Caster<LivingEntity> {
|
|||
|
||||
private final EntityReference<LivingEntity> owner = new EntityReference<>();
|
||||
|
||||
private final SpellContainer spell = new SpellContainer.Delegate() {
|
||||
@Override
|
||||
public SpellContainer delegate() {
|
||||
return Caster.of(getMaster()).map(Caster::getSpellSlot).orElse(SpellContainer.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Spell spell) {
|
||||
getDataTracker().set(SPELL, Optional.ofNullable(spell).map(Spell::getUuid));
|
||||
SpellContainer.Delegate.super.put(spell);
|
||||
}
|
||||
};
|
||||
|
||||
private BlockPos lastPos;
|
||||
|
||||
private int orphanedTicks;
|
||||
|
@ -94,7 +107,7 @@ public class CastSpellEntity extends Entity implements Caster<LivingEntity> {
|
|||
UUID spellId = dataTracker.get(SPELL).orElse(null);
|
||||
|
||||
if (!c.getSpellSlot().get(true).filter(s -> s.getUuid().equals(spellId) && s.tick(this, Situation.GROUND_ENTITY)).isPresent()) {
|
||||
c.setSpell(null);
|
||||
c.getSpellSlot().clear();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -118,11 +131,6 @@ public class CastSpellEntity extends Entity implements Caster<LivingEntity> {
|
|||
this.owner.set(owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpell(Spell spell) {
|
||||
getDataTracker().set(SPELL, Optional.ofNullable(spell).map(Spell::getUuid));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity getEntity() {
|
||||
return this;
|
||||
|
@ -150,7 +158,7 @@ public class CastSpellEntity extends Entity implements Caster<LivingEntity> {
|
|||
|
||||
@Override
|
||||
public SpellContainer getSpellSlot() {
|
||||
return Caster.of(getMaster()).map(Caster::getSpellSlot).orElse(SpellContainer.EMPTY);
|
||||
return spell;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -106,7 +106,7 @@ public class Creature extends Living<LivingEntity> {
|
|||
public void fromNBT(NbtCompound compound) {
|
||||
super.fromNBT(compound);
|
||||
if (compound.contains("effect")) {
|
||||
setSpell(SpellType.fromNBT(compound.getCompound("effect")));
|
||||
getSpellSlot().put(SpellType.fromNBT(compound.getCompound("effect")));
|
||||
}
|
||||
physics.fromNBT(compound);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import com.minelittlepony.unicopia.ability.magic.SpellContainer;
|
|||
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||
import com.minelittlepony.unicopia.item.UItems;
|
||||
import com.minelittlepony.unicopia.network.EffectSync;
|
||||
import com.minelittlepony.unicopia.network.datasync.EffectSync;
|
||||
import com.minelittlepony.unicopia.projectile.ProjectileImpactListener;
|
||||
import com.minelittlepony.unicopia.util.MagicalDamageSource;
|
||||
|
||||
|
@ -85,11 +85,7 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
|
|||
|
||||
@Override
|
||||
public void tick() {
|
||||
getSpellSlot().get(true).ifPresent(effect -> {
|
||||
if (!effect.tick(this, Situation.BODY)) {
|
||||
setSpell(null);
|
||||
}
|
||||
});
|
||||
getSpellSlot().removeIf(effect -> !effect.tick(this, Situation.BODY), true);
|
||||
|
||||
if (invinsibilityTicks > 0) {
|
||||
invinsibilityTicks--;
|
||||
|
|
|
@ -196,7 +196,7 @@ public class Disguise implements NbtSerialisable, PlayerDimensions.Provider, Fli
|
|||
return;
|
||||
}
|
||||
|
||||
Caster.of(entity).ifPresent(c -> c.setSpell(null));
|
||||
Caster.of(entity).ifPresent(c -> c.getSpellSlot().clear());
|
||||
|
||||
if (entity instanceof LivingEntity) {
|
||||
((LivingEntity) entity).getAttributeInstance(PlayerAttributes.ENTITY_GRAVTY_MODIFIER).clearModifiers();
|
||||
|
|
|
@ -104,7 +104,7 @@ public class RaceChangeStatusEffect extends StatusEffect {
|
|||
|
||||
eq.setSpecies(species);
|
||||
if (eq instanceof Caster) {
|
||||
((Caster<?>)eq).setSpell(null);
|
||||
((Caster<?>)eq).getSpellSlot().clear();
|
||||
}
|
||||
|
||||
if (eq instanceof Pony) {
|
||||
|
|
|
@ -30,7 +30,8 @@ import com.minelittlepony.unicopia.item.toxin.Toxin;
|
|||
import com.minelittlepony.unicopia.network.Channel;
|
||||
import com.minelittlepony.unicopia.network.MsgOtherPlayerCapabilities;
|
||||
import com.minelittlepony.unicopia.network.MsgRequestSpeciesChange;
|
||||
import com.minelittlepony.unicopia.network.Transmittable;
|
||||
import com.minelittlepony.unicopia.network.datasync.Transmittable;
|
||||
import com.minelittlepony.unicopia.network.datasync.EffectSync.UpdateCallback;
|
||||
import com.minelittlepony.unicopia.util.Copieable;
|
||||
import com.minelittlepony.unicopia.util.MagicalDamageSource;
|
||||
import com.minelittlepony.unicopia.util.Tickable;
|
||||
|
@ -61,7 +62,7 @@ import net.minecraft.text.TranslatableText;
|
|||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
|
||||
public class Pony extends Living<PlayerEntity> implements Transmittable, Copieable<Pony> {
|
||||
public class Pony extends Living<PlayerEntity> implements Transmittable, Copieable<Pony>, UpdateCallback {
|
||||
|
||||
private static final TrackedData<Integer> RACE = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.INTEGER);
|
||||
|
||||
|
@ -505,17 +506,16 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
|
|||
public void copyFrom(Pony oldPlayer) {
|
||||
speciesPersisted = oldPlayer.speciesPersisted;
|
||||
if (!oldPlayer.getEntity().isRemoved()) {
|
||||
setSpell(oldPlayer.getSpellSlot().get(true).orElse(null));
|
||||
getSpellSlot().put(oldPlayer.getSpellSlot().get(true).orElse(null));
|
||||
}
|
||||
oldPlayer.setSpell(null);
|
||||
oldPlayer.getSpellSlot().put(null);
|
||||
setSpecies(oldPlayer.getSpecies());
|
||||
getDiscoveries().copyFrom(oldPlayer.getDiscoveries());
|
||||
setDirty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpell(@Nullable Spell effect) {
|
||||
super.setSpell(effect);
|
||||
public void onSpellSet(@Nullable Spell spell) {
|
||||
setDirty();
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,6 @@ abstract class MixinMilkBucketItem extends Item {
|
|||
|
||||
@Inject(method = "finishUsing", at = @At("HEAD"), cancellable = true)
|
||||
private void finishUsing(ItemStack stack, World world, LivingEntity entity, CallbackInfoReturnable<ItemStack> info) {
|
||||
Caster.of(entity).ifPresent(c -> c.setSpell(null));
|
||||
Caster.of(entity).ifPresent(c -> c.getSpellSlot().clear());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,106 +0,0 @@
|
|||
package com.minelittlepony.unicopia.network;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.SpellContainer;
|
||||
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||
|
||||
import net.minecraft.entity.data.TrackedData;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
|
||||
/**
|
||||
* Synchronisation class for spells.
|
||||
* Since we can't have our own serializers, we have to intelligently
|
||||
* determine whether to update it from an nbt tag.
|
||||
*
|
||||
* @param <T> The owning entity
|
||||
*/
|
||||
public class EffectSync implements SpellContainer {
|
||||
|
||||
private Optional<Spell> spell = Optional.empty();
|
||||
|
||||
private final Caster<?> owner;
|
||||
|
||||
private final TrackedData<NbtCompound> param;
|
||||
|
||||
@Nullable
|
||||
private NbtCompound lastValue;
|
||||
|
||||
public EffectSync(Caster<?> owner, TrackedData<NbtCompound> param) {
|
||||
this.owner = owner;
|
||||
this.param = param;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Spell> Optional<T> get(@Nullable SpellPredicate<T> type, boolean update) {
|
||||
if (update) {
|
||||
sync(true);
|
||||
}
|
||||
|
||||
if (checkReference() && (type == null || type.test(spell.get()))) {
|
||||
return (Optional<T>)spell;
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPresent() {
|
||||
sync(false);
|
||||
return checkReference();
|
||||
}
|
||||
|
||||
private boolean checkReference() {
|
||||
return spell.isPresent() && !spell.get().isDead();
|
||||
}
|
||||
|
||||
private void sync(boolean force) {
|
||||
@Nullable
|
||||
NbtCompound comp = owner.getEntity().getDataTracker().get(param);
|
||||
|
||||
@Nullable
|
||||
Spell effect = spell.orElse(null);
|
||||
|
||||
if (comp == null || !comp.contains("effect_id") || !comp.contains("uuid")) {
|
||||
if (effect != null) {
|
||||
updateReference(null);
|
||||
}
|
||||
} else if (effect == null || !effect.getUuid().equals(comp.getUuid("uuid"))) {
|
||||
updateReference(SpellType.fromNBT(comp));
|
||||
} else if (owner.isClient()) {
|
||||
if (!Objects.equals(lastValue, comp)) {
|
||||
lastValue = comp;
|
||||
effect.fromNBT(comp);
|
||||
}
|
||||
} else if (force && effect.isDirty()) {
|
||||
put(effect);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(@Nullable Spell effect) {
|
||||
effect = effect == null || effect.isDead() ? null : effect;
|
||||
updateReference(effect);
|
||||
owner.getEntity().getDataTracker().set(param, effect == null ? new NbtCompound() : SpellType.toNBT(effect));
|
||||
}
|
||||
|
||||
private void updateReference(@Nullable Spell effect) {
|
||||
@Nullable
|
||||
Spell old = spell.orElse(null);
|
||||
if (old != effect) {
|
||||
spell = Optional.ofNullable(effect);
|
||||
|
||||
if (old != null && (effect == null || !old.getUuid().equals(effect.getUuid()))) {
|
||||
old.setDead();
|
||||
old.onDestroyed(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package com.minelittlepony.unicopia.network.datasync;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.SpellContainer;
|
||||
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||
|
||||
import net.minecraft.entity.data.TrackedData;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
|
||||
/**
|
||||
* Synchronisation class for spells.
|
||||
* Since we can't have our own serializers, we have to intelligently
|
||||
* determine whether to update it from an nbt tag.
|
||||
*
|
||||
* @param <T> The owning entity
|
||||
*/
|
||||
public class EffectSync implements SpellContainer {
|
||||
|
||||
private final NetworkedReferenceSet<Spell> spells;
|
||||
|
||||
private final Caster<?> owner;
|
||||
|
||||
private final TrackedData<NbtCompound> param;
|
||||
|
||||
@Nullable
|
||||
private NbtCompound lastValue;
|
||||
|
||||
public EffectSync(Caster<?> owner, TrackedData<NbtCompound> param) {
|
||||
spells = new NetworkedReferenceSet<>(Spell::getUuid, () -> new SpellNetworkedReference<>(owner));
|
||||
this.owner = owner;
|
||||
this.param = param;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Spell> Optional<T> get(@Nullable SpellPredicate<T> type, boolean update) {
|
||||
return (Optional<T>)(type == null ? read(update, true).findFirst() : read(update, true).filter(type).findFirst());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPresent() {
|
||||
return read(true, false).findFirst().isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(@Nullable Spell effect) {
|
||||
spells.addReference(effect);
|
||||
write();
|
||||
if (owner instanceof UpdateCallback) {
|
||||
((UpdateCallback)owner).onSpellSet(effect);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeIf(Predicate<Spell> test, boolean update) {
|
||||
read(effect -> {
|
||||
if (test.test(effect)) {
|
||||
spells.removeReference(effect);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
put(null);
|
||||
}
|
||||
|
||||
private Stream<Spell> read(boolean synchronize, boolean sendUpdate) {
|
||||
if (synchronize && spells.fromNbt(owner.getEntity().getDataTracker().get(param)) && sendUpdate) {
|
||||
write();
|
||||
}
|
||||
|
||||
return spells.getReferences();
|
||||
}
|
||||
|
||||
private void read(Consumer<Spell> consumer) {
|
||||
spells.fromNbt(owner.getEntity().getDataTracker().get(param));
|
||||
spells.getReferences().toList().forEach(consumer);
|
||||
write();
|
||||
}
|
||||
|
||||
private void write() {
|
||||
if (spells.isDirty()) {
|
||||
owner.getEntity().getDataTracker().set(param, spells.toNbt());
|
||||
}
|
||||
}
|
||||
|
||||
public interface UpdateCallback {
|
||||
void onSpellSet(@Nullable Spell spell);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.minelittlepony.unicopia.network.datasync;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
|
||||
public interface NetworkedReference<T> {
|
||||
Optional<T> getReference();
|
||||
|
||||
Optional<T> updateReference(@Nullable T newValue);
|
||||
|
||||
boolean fromNbt(NbtCompound comp);
|
||||
|
||||
NbtCompound toNbt();
|
||||
|
||||
boolean isDirty();
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
package com.minelittlepony.unicopia.network.datasync;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.nbt.NbtElement;
|
||||
import net.minecraft.nbt.NbtList;
|
||||
import net.minecraft.nbt.NbtString;
|
||||
|
||||
public class NetworkedReferenceSet<T> {
|
||||
|
||||
private final List<UUID> ids = new ArrayList<>();
|
||||
|
||||
private final Map<UUID, NetworkedReference<T>> values = new HashMap<>();
|
||||
|
||||
private final Function<T, UUID> uuidConverter;
|
||||
private final Supplier<NetworkedReference<T>> factory;
|
||||
|
||||
private boolean dirty;
|
||||
|
||||
public NetworkedReferenceSet(Function<T, UUID> uuidConverter, Supplier<NetworkedReference<T>> factory) {
|
||||
this.uuidConverter = uuidConverter;
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public Stream<T> getReferences() {
|
||||
return ids.stream().map(id -> values.get(id))
|
||||
.map(a -> a.getReference())
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get);
|
||||
}
|
||||
|
||||
public void addReference(@Nullable T newValue) {
|
||||
if (newValue != null) {
|
||||
addReference(uuidConverter.apply(newValue));
|
||||
}
|
||||
}
|
||||
|
||||
private NetworkedReference<T> addReference(UUID newValue) {
|
||||
return values.computeIfAbsent(newValue, id -> {
|
||||
dirty = true;
|
||||
ids.remove(id);
|
||||
ids.add(0, id);
|
||||
return factory.get();
|
||||
});
|
||||
}
|
||||
|
||||
public void removeReference(@Nullable T oldValue) {
|
||||
if (oldValue != null) {
|
||||
removeReference(uuidConverter.apply(oldValue));
|
||||
}
|
||||
}
|
||||
|
||||
private void removeReference(UUID id) {
|
||||
dirty |= ids.remove(id);
|
||||
NetworkedReference<T> i = values.remove(id);
|
||||
if (i != null) {
|
||||
dirty = true;
|
||||
i.updateReference(null);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean fromNbt(NbtCompound comp) {
|
||||
|
||||
List<UUID> incoming = new ArrayList<>();
|
||||
comp.getList("keys", NbtElement.STRING_TYPE).forEach(key -> {
|
||||
incoming.add(UUID.fromString(key.asString()));
|
||||
});
|
||||
|
||||
ids.stream().filter(id -> !incoming.contains(id)).forEach(this::removeReference);
|
||||
|
||||
boolean[] send = new boolean[0];
|
||||
incoming.forEach(kept -> {
|
||||
NetworkedReference<T> i = addReference(kept);
|
||||
send[0] |= i.fromNbt(comp.getCompound(kept.toString()));
|
||||
if (i.getReference().isEmpty()) {
|
||||
removeReference(kept);
|
||||
}
|
||||
});
|
||||
dirty = false;
|
||||
return send[0];
|
||||
}
|
||||
|
||||
public NbtCompound toNbt() {
|
||||
NbtCompound tag = new NbtCompound();
|
||||
NbtList ids = new NbtList();
|
||||
this.ids.forEach(id -> {
|
||||
String sid = id.toString();
|
||||
ids.add(NbtString.of(sid));
|
||||
tag.put(sid, values.get(id).toNbt());
|
||||
});
|
||||
tag.put("key", ids);
|
||||
dirty = false;
|
||||
return tag;
|
||||
}
|
||||
|
||||
public boolean isDirty() {
|
||||
return dirty || values.values().stream().anyMatch(NetworkedReference::isDirty);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
package com.minelittlepony.unicopia.network.datasync;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
|
||||
public class SpellNetworkedReference<T extends Spell> implements NetworkedReference<T> {
|
||||
private Optional<T> currentValue = Optional.empty();
|
||||
|
||||
@Nullable
|
||||
private NbtCompound lastValue;
|
||||
|
||||
private final Caster<?> owner;
|
||||
|
||||
private boolean dirty;
|
||||
|
||||
public SpellNetworkedReference(Caster<?> owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> getReference() {
|
||||
return currentValue.filter(s -> !s.isDead());
|
||||
}
|
||||
|
||||
private boolean mustDelete(@Nullable NbtCompound comp) {
|
||||
return (comp == null || !comp.contains("effect_id") || !comp.contains("uuid")) && currentValue.isPresent();
|
||||
}
|
||||
|
||||
private boolean mustReplace(NbtCompound comp) {
|
||||
return currentValue.isEmpty() || !currentValue.get().getUuid().equals(comp.getUuid("uuid"));
|
||||
}
|
||||
|
||||
private boolean mustUpdate(NbtCompound comp) {
|
||||
if (owner.isClient() && !Objects.equals(lastValue, comp)) {
|
||||
lastValue = comp;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean mustSend() {
|
||||
return currentValue.filter(Spell::isDirty).isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> updateReference(@Nullable T newValue) {
|
||||
newValue = newValue == null || newValue.isDead() ? null : newValue;
|
||||
|
||||
@Nullable
|
||||
T oldValue = currentValue.orElse(null);
|
||||
if (oldValue != newValue) {
|
||||
dirty = true;
|
||||
currentValue = Optional.ofNullable(newValue);
|
||||
|
||||
if (oldValue != null && (newValue == null || !oldValue.getUuid().equals(newValue.getUuid()))) {
|
||||
oldValue.setDead();
|
||||
oldValue.onDestroyed(owner);
|
||||
}
|
||||
}
|
||||
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean fromNbt(NbtCompound comp) {
|
||||
dirty = false;
|
||||
|
||||
if (mustDelete(comp)) {
|
||||
updateReference(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mustReplace(comp)) {
|
||||
updateReference((T)SpellType.fromNBT(comp));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mustUpdate(comp)) {
|
||||
currentValue.ifPresent(s -> s.fromNBT(comp));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mustSend()) {
|
||||
updateReference(getReference().orElse(null));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtCompound toNbt() {
|
||||
dirty = false;
|
||||
return getReference().map(SpellType::toNBT).orElseGet(NbtCompound::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty() {
|
||||
return dirty || mustSend();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.minelittlepony.unicopia.network;
|
||||
package com.minelittlepony.unicopia.network.datasync;
|
||||
|
||||
public interface Transmittable {
|
||||
void sendCapabilities(boolean full);
|
|
@ -16,8 +16,8 @@ import com.minelittlepony.unicopia.entity.Physics;
|
|||
import com.minelittlepony.unicopia.entity.UEntities;
|
||||
import com.minelittlepony.unicopia.item.UItems;
|
||||
import com.minelittlepony.unicopia.network.Channel;
|
||||
import com.minelittlepony.unicopia.network.EffectSync;
|
||||
import com.minelittlepony.unicopia.network.MsgSpawnProjectile;
|
||||
import com.minelittlepony.unicopia.network.datasync.EffectSync;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityType;
|
||||
|
@ -220,7 +220,7 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Li
|
|||
super.readCustomDataFromNbt(compound);
|
||||
physics.fromNBT(compound);
|
||||
if (compound.contains("effect")) {
|
||||
setSpell(SpellType.fromNBT(compound.getCompound("effect")));
|
||||
getSpellSlot().put(SpellType.fromNBT(compound.getCompound("effect")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue