mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-30 16:28:00 +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()) {
|
if (player.getPhysics().isFlying() && !player.getSpellSlot().isPresent()) {
|
||||||
player.getMagicalReserves().getMana().multiply(0.1F);
|
player.getMagicalReserves().getMana().multiply(0.1F);
|
||||||
player.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, player.getPhysics().getMotionAngle()), player.getOriginVector(), Vec3d.ZERO);
|
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.EquinePredicates;
|
||||||
import com.minelittlepony.unicopia.Owned;
|
import com.minelittlepony.unicopia.Owned;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
|
||||||
import com.minelittlepony.unicopia.entity.Physics;
|
import com.minelittlepony.unicopia.entity.Physics;
|
||||||
import com.minelittlepony.unicopia.entity.PonyContainer;
|
import com.minelittlepony.unicopia.entity.PonyContainer;
|
||||||
import com.minelittlepony.unicopia.particle.ParticleSource;
|
import com.minelittlepony.unicopia.particle.ParticleSource;
|
||||||
|
@ -29,10 +28,6 @@ public interface Caster<E extends LivingEntity> extends Owned<E>, Levelled, Affi
|
||||||
|
|
||||||
SpellContainer getSpellSlot();
|
SpellContainer getSpellSlot();
|
||||||
|
|
||||||
default void setSpell(@Nullable Spell spell) {
|
|
||||||
getSpellSlot().put(spell);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the entity directly responsible for casting.
|
* Gets the entity directly responsible for casting.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.minelittlepony.unicopia.ability.magic;
|
package com.minelittlepony.unicopia.ability.magic;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
@ -15,6 +16,12 @@ public interface SpellContainer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void put(Spell effect) { }
|
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.
|
* Sets the active effect.
|
||||||
*/
|
*/
|
||||||
void put(@Nullable Spell 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
|
@Override
|
||||||
public boolean apply(Caster<?> caster) {
|
public boolean apply(Caster<?> caster) {
|
||||||
caster.setSpell(this);
|
caster.getSpellSlot().put(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
|
||||||
CastSpellEntity entity = UEntities.CAST_SPELL.create(source.getWorld());
|
CastSpellEntity entity = UEntities.CAST_SPELL.create(source.getWorld());
|
||||||
Vec3d pos = castEntity.getPosition().orElse(source.getOriginVector());
|
Vec3d pos = castEntity.getPosition().orElse(source.getOriginVector());
|
||||||
entity.updatePositionAndAngles(pos.x, pos.y, pos.z, 0, 0);
|
entity.updatePositionAndAngles(pos.x, pos.y, pos.z, 0, 0);
|
||||||
entity.setSpell(this);
|
entity.getSpellSlot().put(this);
|
||||||
entity.setMaster(source.getMaster());
|
entity.setMaster(source.getMaster());
|
||||||
entity.world.spawnEntity(entity);
|
entity.world.spawnEntity(entity);
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ public final class ThrowableSpell extends AbstractDelegatingSpell {
|
||||||
MagicProjectileEntity projectile = new MagicProjectileEntity(world, entity);
|
MagicProjectileEntity projectile = new MagicProjectileEntity(world, entity);
|
||||||
|
|
||||||
projectile.setItem(GemstoneItem.enchanted(UItems.GEMSTONE.getDefaultStack(), spell.getType()));
|
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.setProperties(entity, entity.getPitch(), entity.getYaw(), 0, 1.5F, 1);
|
||||||
projectile.setHydrophobic();
|
projectile.setHydrophobic();
|
||||||
configureProjectile(projectile, caster);
|
configureProjectile(projectile, caster);
|
||||||
|
|
|
@ -67,7 +67,7 @@ public abstract class AbstractSpell implements Spell {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean apply(Caster<?> caster) {
|
public final boolean apply(Caster<?> caster) {
|
||||||
caster.setSpell(this);
|
caster.getSpellSlot().put(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,7 +141,7 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
|
||||||
@Nullable
|
@Nullable
|
||||||
public T apply(Caster<?> caster, SpellTraits traits) {
|
public T apply(Caster<?> caster, SpellTraits traits) {
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
caster.setSpell(null);
|
caster.getSpellSlot().clear();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,19 @@ public class CastSpellEntity extends Entity implements Caster<LivingEntity> {
|
||||||
|
|
||||||
private final EntityReference<LivingEntity> owner = new EntityReference<>();
|
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 BlockPos lastPos;
|
||||||
|
|
||||||
private int orphanedTicks;
|
private int orphanedTicks;
|
||||||
|
@ -94,7 +107,7 @@ public class CastSpellEntity extends Entity implements Caster<LivingEntity> {
|
||||||
UUID spellId = dataTracker.get(SPELL).orElse(null);
|
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()) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -118,11 +131,6 @@ public class CastSpellEntity extends Entity implements Caster<LivingEntity> {
|
||||||
this.owner.set(owner);
|
this.owner.set(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setSpell(Spell spell) {
|
|
||||||
getDataTracker().set(SPELL, Optional.ofNullable(spell).map(Spell::getUuid));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Entity getEntity() {
|
public Entity getEntity() {
|
||||||
return this;
|
return this;
|
||||||
|
@ -150,7 +158,7 @@ public class CastSpellEntity extends Entity implements Caster<LivingEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpellContainer getSpellSlot() {
|
public SpellContainer getSpellSlot() {
|
||||||
return Caster.of(getMaster()).map(Caster::getSpellSlot).orElse(SpellContainer.EMPTY);
|
return spell;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -106,7 +106,7 @@ public class Creature extends Living<LivingEntity> {
|
||||||
public void fromNBT(NbtCompound compound) {
|
public void fromNBT(NbtCompound compound) {
|
||||||
super.fromNBT(compound);
|
super.fromNBT(compound);
|
||||||
if (compound.contains("effect")) {
|
if (compound.contains("effect")) {
|
||||||
setSpell(SpellType.fromNBT(compound.getCompound("effect")));
|
getSpellSlot().put(SpellType.fromNBT(compound.getCompound("effect")));
|
||||||
}
|
}
|
||||||
physics.fromNBT(compound);
|
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.Situation;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||||
import com.minelittlepony.unicopia.item.UItems;
|
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.projectile.ProjectileImpactListener;
|
||||||
import com.minelittlepony.unicopia.util.MagicalDamageSource;
|
import com.minelittlepony.unicopia.util.MagicalDamageSource;
|
||||||
|
|
||||||
|
@ -85,11 +85,7 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void tick() {
|
public void tick() {
|
||||||
getSpellSlot().get(true).ifPresent(effect -> {
|
getSpellSlot().removeIf(effect -> !effect.tick(this, Situation.BODY), true);
|
||||||
if (!effect.tick(this, Situation.BODY)) {
|
|
||||||
setSpell(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (invinsibilityTicks > 0) {
|
if (invinsibilityTicks > 0) {
|
||||||
invinsibilityTicks--;
|
invinsibilityTicks--;
|
||||||
|
|
|
@ -196,7 +196,7 @@ public class Disguise implements NbtSerialisable, PlayerDimensions.Provider, Fli
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Caster.of(entity).ifPresent(c -> c.setSpell(null));
|
Caster.of(entity).ifPresent(c -> c.getSpellSlot().clear());
|
||||||
|
|
||||||
if (entity instanceof LivingEntity) {
|
if (entity instanceof LivingEntity) {
|
||||||
((LivingEntity) entity).getAttributeInstance(PlayerAttributes.ENTITY_GRAVTY_MODIFIER).clearModifiers();
|
((LivingEntity) entity).getAttributeInstance(PlayerAttributes.ENTITY_GRAVTY_MODIFIER).clearModifiers();
|
||||||
|
|
|
@ -104,7 +104,7 @@ public class RaceChangeStatusEffect extends StatusEffect {
|
||||||
|
|
||||||
eq.setSpecies(species);
|
eq.setSpecies(species);
|
||||||
if (eq instanceof Caster) {
|
if (eq instanceof Caster) {
|
||||||
((Caster<?>)eq).setSpell(null);
|
((Caster<?>)eq).getSpellSlot().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eq instanceof Pony) {
|
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.Channel;
|
||||||
import com.minelittlepony.unicopia.network.MsgOtherPlayerCapabilities;
|
import com.minelittlepony.unicopia.network.MsgOtherPlayerCapabilities;
|
||||||
import com.minelittlepony.unicopia.network.MsgRequestSpeciesChange;
|
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.Copieable;
|
||||||
import com.minelittlepony.unicopia.util.MagicalDamageSource;
|
import com.minelittlepony.unicopia.util.MagicalDamageSource;
|
||||||
import com.minelittlepony.unicopia.util.Tickable;
|
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.BlockPos;
|
||||||
import net.minecraft.util.math.Direction;
|
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);
|
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) {
|
public void copyFrom(Pony oldPlayer) {
|
||||||
speciesPersisted = oldPlayer.speciesPersisted;
|
speciesPersisted = oldPlayer.speciesPersisted;
|
||||||
if (!oldPlayer.getEntity().isRemoved()) {
|
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());
|
setSpecies(oldPlayer.getSpecies());
|
||||||
getDiscoveries().copyFrom(oldPlayer.getDiscoveries());
|
getDiscoveries().copyFrom(oldPlayer.getDiscoveries());
|
||||||
setDirty();
|
setDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setSpell(@Nullable Spell effect) {
|
public void onSpellSet(@Nullable Spell spell) {
|
||||||
super.setSpell(effect);
|
|
||||||
setDirty();
|
setDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,6 @@ abstract class MixinMilkBucketItem extends Item {
|
||||||
|
|
||||||
@Inject(method = "finishUsing", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "finishUsing", at = @At("HEAD"), cancellable = true)
|
||||||
private void finishUsing(ItemStack stack, World world, LivingEntity entity, CallbackInfoReturnable<ItemStack> info) {
|
private void finishUsing(ItemStack stack, World world, LivingEntity entity, CallbackInfoReturnable<ItemStack> info) {
|
||||||
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 {
|
public interface Transmittable {
|
||||||
void sendCapabilities(boolean full);
|
void sendCapabilities(boolean full);
|
|
@ -16,8 +16,8 @@ import com.minelittlepony.unicopia.entity.Physics;
|
||||||
import com.minelittlepony.unicopia.entity.UEntities;
|
import com.minelittlepony.unicopia.entity.UEntities;
|
||||||
import com.minelittlepony.unicopia.item.UItems;
|
import com.minelittlepony.unicopia.item.UItems;
|
||||||
import com.minelittlepony.unicopia.network.Channel;
|
import com.minelittlepony.unicopia.network.Channel;
|
||||||
import com.minelittlepony.unicopia.network.EffectSync;
|
|
||||||
import com.minelittlepony.unicopia.network.MsgSpawnProjectile;
|
import com.minelittlepony.unicopia.network.MsgSpawnProjectile;
|
||||||
|
import com.minelittlepony.unicopia.network.datasync.EffectSync;
|
||||||
|
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
|
@ -220,7 +220,7 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Li
|
||||||
super.readCustomDataFromNbt(compound);
|
super.readCustomDataFromNbt(compound);
|
||||||
physics.fromNBT(compound);
|
physics.fromNBT(compound);
|
||||||
if (compound.contains("effect")) {
|
if (compound.contains("effect")) {
|
||||||
setSpell(SpellType.fromNBT(compound.getCompound("effect")));
|
getSpellSlot().put(SpellType.fromNBT(compound.getCompound("effect")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue