Move the code dealing with disguise entities into its own class

This commit is contained in:
Sollace 2020-09-27 13:29:19 +02:00
parent e8292e20ec
commit c7e1cea0c2
6 changed files with 274 additions and 212 deletions

View file

@ -5,6 +5,7 @@ import javax.annotation.Nullable;
import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell; import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.entity.behaviour.VirtualEntity;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.util.VecHelper; import com.minelittlepony.unicopia.util.VecHelper;
@ -54,6 +55,7 @@ public class ChangelingDisguiseAbility extends ChangelingFeedAbility {
looked = Pony.of((PlayerEntity)looked) looked = Pony.of((PlayerEntity)looked)
.getSpellOrEmpty(DisguiseSpell.class) .getSpellOrEmpty(DisguiseSpell.class)
.map(DisguiseSpell::getDisguise) .map(DisguiseSpell::getDisguise)
.map(VirtualEntity::getAppearance)
.orElse(looked); .orElse(looked);
} }

View file

@ -1,7 +1,5 @@
package com.minelittlepony.unicopia.ability.magic.spell; package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.UUID;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.minelittlepony.unicopia.Affinity; import com.minelittlepony.unicopia.Affinity;
@ -11,45 +9,24 @@ import com.minelittlepony.unicopia.ability.FlightPredicate;
import com.minelittlepony.unicopia.ability.HeightPredicate; import com.minelittlepony.unicopia.ability.HeightPredicate;
import com.minelittlepony.unicopia.ability.magic.AttachableSpell; import com.minelittlepony.unicopia.ability.magic.AttachableSpell;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.CasterUtils;
import com.minelittlepony.unicopia.ability.magic.Spell; import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.Suppressable; import com.minelittlepony.unicopia.ability.magic.Suppressable;
import com.minelittlepony.unicopia.entity.behaviour.EntityBehaviour; import com.minelittlepony.unicopia.entity.behaviour.EntityBehaviour;
import com.minelittlepony.unicopia.entity.behaviour.VirtualEntity;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.projectile.ProjectileUtil;
import com.mojang.authlib.GameProfile;
import net.minecraft.block.entity.SkullBlockEntity;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.FallingBlockEntity;
import net.minecraft.entity.Flutterer;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.boss.dragon.EnderDragonEntity;
import net.minecraft.entity.data.TrackedData; import net.minecraft.entity.data.TrackedData;
import net.minecraft.entity.decoration.AbstractDecorationEntity;
import net.minecraft.entity.mob.AmbientEntity;
import net.minecraft.entity.mob.FlyingEntity;
import net.minecraft.entity.mob.MobEntity; import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.mob.ShulkerEntity;
import net.minecraft.entity.mob.VexEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.ProjectileEntity; import net.minecraft.entity.projectile.ProjectileEntity;
import net.minecraft.entity.projectile.ShulkerBulletEntity;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
public class DisguiseSpell extends AbstractSpell implements AttachableSpell, Suppressable, FlightPredicate, HeightPredicate { public class DisguiseSpell extends AbstractSpell implements AttachableSpell, Suppressable, FlightPredicate, HeightPredicate {
@Nonnull private final VirtualEntity disguise = new VirtualEntity();
private String entityId = "";
@Nullable
private Entity entity;
@Nullable
private CompoundTag entityNbt;
private int suppressionCounter; private int suppressionCounter;
@ -89,120 +66,23 @@ public class DisguiseSpell extends AbstractSpell implements AttachableSpell, Sup
return suppressionCounter > 0; return suppressionCounter > 0;
} }
public Entity getDisguise() { public VirtualEntity getDisguise() {
return entity; return disguise;
} }
public DisguiseSpell setDisguise(@Nullable Entity entity) { public DisguiseSpell setDisguise(@Nullable Entity entity) {
if (entity == this.entity) { if (entity == disguise.getAppearance()) {
entity = null; entity = null;
} }
this.entityNbt = null;
this.entityId = "";
removeDisguise();
if (entity != null) {
entityNbt = encodeEntityToNBT(entity);
entityId = entityNbt.getString("id");
}
disguise.setAppearance(entity);
setDirty(true); setDirty(true);
return this; return this;
} }
protected void removeDisguise() {
if (entity != null) {
entity.remove();
entity = null;
}
}
protected CompoundTag encodeEntityToNBT(Entity entity) {
CompoundTag entityNbt = new CompoundTag();
if (entity instanceof PlayerEntity) {
GameProfile profile = ((PlayerEntity)entity).getGameProfile();
entityNbt.putString("id", "player");
entityNbt.putUuid("playerId", profile.getId());
entityNbt.putString("playerName", profile.getName());
CompoundTag tag = new CompoundTag();
entity.saveToTag(tag);
entityNbt.put("playerNbt", tag);
} else {
entity.saveToTag(entityNbt);
}
return entityNbt;
}
protected synchronized void createPlayer(CompoundTag nbt, GameProfile profile, Caster<?> source) {
removeDisguise();
entity = InteractionManager.instance().createPlayer(source.getEntity(), profile);
entity.setCustomName(source.getOwner().getName());
((PlayerEntity)entity).fromTag(nbt.getCompound("playerNbt"));
entity.setUuid(UUID.randomUUID());
entity.extinguish();
onEntityLoaded(source);
}
protected void checkAndCreateDisguiseEntity(Caster<?> source) {
if (entity == null && entityNbt != null) {
CompoundTag nbt = entityNbt;
entityNbt = null;
if ("player".equals(entityId)) {
createPlayer(nbt, new GameProfile(
nbt.getUuid("playerId"),
nbt.getString("playerName")
), source);
new Thread(() -> createPlayer(nbt, SkullBlockEntity.loadProperties(new GameProfile(
null,
nbt.getString("playerName")
)), source)).start();
} else {
if (source.isClient()) {
entity = EntityType.fromTag(nbt).map(type -> type.create(source.getWorld())).orElse(null);
if (entity != null) {
EntityBehaviour.forEntity(entity).onCreate(entity);
}
} else {
entity = EntityType.loadEntityWithPassengers(nbt, source.getWorld(), e -> {
EntityBehaviour.forEntity(e).onCreate(e);
return e;
});
}
}
onEntityLoaded(source);
}
}
protected void onEntityLoaded(Caster<?> source) {
source.getEntity().calculateDimensions();
if (entity == null) {
return;
}
CasterUtils.toCaster(entity).ifPresent(c -> c.setSpell(null));
if (source.isClient()) {
source.getWorld().spawnEntity(entity);
}
}
@Override @Override
public boolean handleProjectileImpact(ProjectileEntity projectile) { public boolean handleProjectileImpact(ProjectileEntity projectile) {
return getDisguise() == projectile; return disguise.getAppearance() == projectile;
} }
@Override @Override
@ -219,6 +99,8 @@ public class DisguiseSpell extends AbstractSpell implements AttachableSpell, Sup
public boolean update(Caster<?> source, boolean tick) { public boolean update(Caster<?> source, boolean tick) {
LivingEntity owner = source.getOwner(); LivingEntity owner = source.getOwner();
Entity entity = disguise.getAppearance();
if (isSuppressed()) { if (isSuppressed()) {
suppressionCounter--; suppressionCounter--;
@ -235,7 +117,7 @@ public class DisguiseSpell extends AbstractSpell implements AttachableSpell, Sup
return true; return true;
} }
checkAndCreateDisguiseEntity(source); entity = disguise.getOrCreate(source);
if (owner == null) { if (owner == null) {
return true; return true;
@ -264,7 +146,7 @@ public class DisguiseSpell extends AbstractSpell implements AttachableSpell, Sup
behaviour.copyBaseAttributes(owner, entity); behaviour.copyBaseAttributes(owner, entity);
if (tick && !skipsUpdate(entity)) { if (tick && !disguise.skipsUpdate()) {
entity.tick(); entity.tick();
} }
@ -296,7 +178,7 @@ public class DisguiseSpell extends AbstractSpell implements AttachableSpell, Sup
@Override @Override
public void setDead() { public void setDead() {
super.setDead(); super.setDead();
removeDisguise(); disguise.remove();
} }
@Override @Override
@ -314,13 +196,7 @@ public class DisguiseSpell extends AbstractSpell implements AttachableSpell, Sup
super.toNBT(compound); super.toNBT(compound);
compound.putInt("suppressionCounter", suppressionCounter); compound.putInt("suppressionCounter", suppressionCounter);
compound.putString("entityId", entityId); disguise.toNBT(compound);
if (entityNbt != null) {
compound.put("entity", entityNbt);
} else if (entity != null) {
compound.put("entity", encodeEntityToNBT(entity));
}
} }
@Override @Override
@ -328,85 +204,22 @@ public class DisguiseSpell extends AbstractSpell implements AttachableSpell, Sup
super.fromNBT(compound); super.fromNBT(compound);
suppressionCounter = compound.getInt("suppressionCounter"); suppressionCounter = compound.getInt("suppressionCounter");
disguise.fromNBT(compound);
String newId = compound.getString("entityId");
if (!newId.contentEquals(entityId)) {
entityNbt = null;
removeDisguise();
}
if (compound.contains("entity")) {
entityId = newId;
entityNbt = compound.getCompound("entity");
compound.getString("entityData");
if (entity != null) {
try {
entity.fromTag(entityNbt);
} catch (Exception ignored) {
// Mojang pls
}
}
}
} }
@Override @Override
public boolean checkCanFly(Pony player) { public boolean checkCanFly(Pony player) {
if (entity == null || !player.getSpecies().canFly()) { return disguise.canFly() && player.getSpecies().canFly();
return false;
}
if (entity instanceof Owned) {
@SuppressWarnings("unchecked")
Pony iplayer = Pony.of(((Owned<PlayerEntity>)entity).getOwner());
return iplayer != null && iplayer.getSpecies().canFly();
}
return entity instanceof FlyingEntity
|| entity instanceof AmbientEntity
|| entity instanceof EnderDragonEntity
|| entity instanceof VexEntity
|| entity instanceof ShulkerBulletEntity
|| entity instanceof Flutterer
|| ProjectileUtil.isProjectile(entity);
} }
@Override @Override
public float getTargetEyeHeight(Pony player) { public float getTargetEyeHeight(Pony player) {
if (entity != null && !isSuppressed()) { return isSuppressed() ? -1 : disguise.getStandingEyeHeight();
if (entity instanceof FallingBlockEntity) {
return 0.5F;
}
return entity.getStandingEyeHeight();
}
return -1;
} }
@Override @Override
public float getTargetBodyHeight(Pony player) { public float getTargetBodyHeight(Pony player) {
if (entity != null && !isSuppressed()) { return isSuppressed() ? -1 : disguise.getHeight();
if (entity instanceof FallingBlockEntity) {
return 0.9F;
}
return entity.getHeight() - 0.1F;
}
return -1;
}
public static boolean skipsUpdate(Entity entity) {
return entity instanceof FallingBlockEntity
|| entity instanceof AbstractDecorationEntity
|| entity instanceof PlayerEntity;
}
public static boolean isAttachedEntity(Entity entity) {
return entity instanceof ShulkerEntity
|| entity instanceof AbstractDecorationEntity
|| entity instanceof FallingBlockEntity;
} }
static abstract class PlayerAccess extends PlayerEntity { static abstract class PlayerAccess extends PlayerEntity {

View file

@ -6,7 +6,6 @@ import javax.annotation.Nullable;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Spell; import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.util.Registries; import com.minelittlepony.unicopia.util.Registries;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
@ -39,8 +38,7 @@ public class EntityBehaviour<T extends Entity> {
to.removed = from.removed; to.removed = from.removed;
to.setOnGround(from.isOnGround()); to.setOnGround(from.isOnGround());
if (DisguiseSpell.isAttachedEntity(to)) { if (VirtualEntity.isAxisAligned(to)) {
double x = Math.floor(from.getX()) + 0.5; double x = Math.floor(from.getX()) + 0.5;
double y = Math.floor(from.getY()); double y = Math.floor(from.getY());
double z = Math.floor(from.getZ()) + 0.5; double z = Math.floor(from.getZ()) + 0.5;

View file

@ -0,0 +1,247 @@
package com.minelittlepony.unicopia.entity.behaviour;
import java.util.UUID;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.InteractionManager;
import com.minelittlepony.unicopia.Owned;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.CasterUtils;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.projectile.ProjectileUtil;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import com.mojang.authlib.GameProfile;
import net.minecraft.block.entity.SkullBlockEntity;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.FallingBlockEntity;
import net.minecraft.entity.Flutterer;
import net.minecraft.entity.boss.dragon.EnderDragonEntity;
import net.minecraft.entity.decoration.AbstractDecorationEntity;
import net.minecraft.entity.mob.AmbientEntity;
import net.minecraft.entity.mob.FlyingEntity;
import net.minecraft.entity.mob.ShulkerEntity;
import net.minecraft.entity.mob.SpiderEntity;
import net.minecraft.entity.mob.VexEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.ShulkerBulletEntity;
import net.minecraft.nbt.CompoundTag;
public class VirtualEntity implements NbtSerialisable {
@Nonnull
private String entityId = "";
@Nullable
private Entity entity;
@Nullable
private CompoundTag entityNbt;
@Nullable
public Entity getAppearance() {
return entity;
}
public void setAppearance(@Nullable Entity entity) {
remove();
entityNbt = entity == null ? null : encodeEntityToNBT(entity);
entityId = entityNbt == null ? "" : entityNbt.getString("id");
}
public boolean isPresent() {
return entity != null;
}
public void remove() {
if (entity != null) {
entity.remove();
entity = null;
}
}
private synchronized void createPlayer(CompoundTag nbt, GameProfile profile, Caster<?> source) {
remove();
entity = InteractionManager.instance().createPlayer(source.getEntity(), profile);
entity.setCustomName(source.getOwner().getName());
((PlayerEntity)entity).fromTag(nbt.getCompound("playerNbt"));
entity.setUuid(UUID.randomUUID());
entity.extinguish();
onEntityLoaded(source);
}
public Entity getOrCreate(Caster<?> source) {
if (entity == null && entityNbt != null) {
CompoundTag nbt = entityNbt;
entityNbt = null;
if ("player".equals(entityId)) {
createPlayer(nbt, new GameProfile(
nbt.getUuid("playerId"),
nbt.getString("playerName")
), source);
new Thread(() -> createPlayer(nbt, SkullBlockEntity.loadProperties(new GameProfile(
null,
nbt.getString("playerName")
)), source)).start();
} else {
if (source.isClient()) {
entity = EntityType.fromTag(nbt).map(type -> type.create(source.getWorld())).orElse(null);
if (entity != null) {
EntityBehaviour.forEntity(entity).onCreate(entity);
}
} else {
entity = EntityType.loadEntityWithPassengers(nbt, source.getWorld(), e -> {
EntityBehaviour.forEntity(e).onCreate(e);
return e;
});
}
}
onEntityLoaded(source);
}
return entity;
}
private void onEntityLoaded(Caster<?> source) {
source.getEntity().calculateDimensions();
if (entity == null) {
return;
}
CasterUtils.toCaster(entity).ifPresent(c -> c.setSpell(null));
if (source.isClient()) {
source.getWorld().spawnEntity(entity);
}
}
public boolean canFly() {
if (entity == null) {
return false;
}
if (entity instanceof Owned) {
@SuppressWarnings("unchecked")
Pony iplayer = Pony.of(((Owned<PlayerEntity>)entity).getOwner());
return iplayer != null && iplayer.getSpecies().canFly();
}
return entity instanceof FlyingEntity
|| entity instanceof AmbientEntity
|| entity instanceof EnderDragonEntity
|| entity instanceof VexEntity
|| entity instanceof ShulkerBulletEntity
|| entity instanceof Flutterer
|| ProjectileUtil.isProjectile(entity);
}
public float getStandingEyeHeight() {
if (entity != null) {
if (entity instanceof FallingBlockEntity) {
return 0.5F;
}
return entity.getStandingEyeHeight();
}
return -1;
}
public float getHeight() {
if (entity != null) {
if (entity instanceof FallingBlockEntity) {
return 0.9F;
}
return entity.getHeight() - 0.1F;
}
return -1;
}
public boolean skipsUpdate() {
return entity instanceof FallingBlockEntity
|| entity instanceof AbstractDecorationEntity
|| entity instanceof PlayerEntity;
}
public boolean isAxisAligned() {
return isAxisAligned(entity);
}
public boolean canClimbWalls() {
return entity instanceof SpiderEntity;
}
@Override
public void toNBT(CompoundTag compound) {
compound.putString("entityId", entityId);
if (entityNbt != null) {
compound.put("entity", entityNbt);
} else if (entity != null) {
compound.put("entity", encodeEntityToNBT(entity));
}
}
@Override
public void fromNBT(CompoundTag compound) {
String newId = compound.getString("entityId");
if (!newId.contentEquals(entityId)) {
entityNbt = null;
remove();
}
if (compound.contains("entity")) {
entityId = newId;
entityNbt = compound.getCompound("entity");
compound.getString("entityData");
if (entity != null) {
try {
entity.fromTag(entityNbt);
} catch (Exception ignored) {
// Mojang pls
}
}
}
}
public static boolean isAxisAligned(@Nullable Entity entity) {
return entity instanceof ShulkerEntity
|| entity instanceof AbstractDecorationEntity
|| entity instanceof FallingBlockEntity;
}
private static CompoundTag encodeEntityToNBT(Entity entity) {
CompoundTag entityNbt = new CompoundTag();
if (entity instanceof PlayerEntity) {
GameProfile profile = ((PlayerEntity)entity).getGameProfile();
entityNbt.putString("id", "player");
entityNbt.putUuid("playerId", profile.getId());
entityNbt.putString("playerName", profile.getName());
CompoundTag tag = new CompoundTag();
entity.saveToTag(tag);
entityNbt.put("playerNbt", tag);
} else {
entity.saveToTag(entityNbt);
}
return entityNbt;
}
}

View file

@ -14,12 +14,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell; import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.entity.Creature; import com.minelittlepony.unicopia.entity.Creature;
import com.minelittlepony.unicopia.entity.PonyContainer; import com.minelittlepony.unicopia.entity.PonyContainer;
import com.minelittlepony.unicopia.entity.behaviour.VirtualEntity;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.entity.Equine; import com.minelittlepony.unicopia.entity.Equine;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.mob.SpiderEntity;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@ -49,11 +49,13 @@ abstract class MixinLivingEntity extends Entity implements PonyContainer<Equine<
@Inject(method = "isClimbing()Z", at = @At("HEAD"), cancellable = true) @Inject(method = "isClimbing()Z", at = @At("HEAD"), cancellable = true)
public void onIsClimbing(CallbackInfoReturnable<Boolean> info) { public void onIsClimbing(CallbackInfoReturnable<Boolean> info) {
if (get() instanceof Pony && horizontalCollision) { if (get() instanceof Pony && horizontalCollision) {
DisguiseSpell disguise = ((Pony)get()).getSpell(DisguiseSpell.class, false); ((Pony)get()).getSpellOrEmpty(DisguiseSpell.class, false)
if (disguise != null && disguise.getDisguise() instanceof SpiderEntity) { .map(DisguiseSpell::getDisguise)
.filter(VirtualEntity::canClimbWalls)
.ifPresent(v -> {
climbingPos = Optional.of(getBlockPos()); climbingPos = Optional.of(getBlockPos());
info.setReturnValue(true); info.setReturnValue(true);
} });
} }
} }

View file

@ -21,7 +21,7 @@ abstract class MixinTargetPredicate {
Equine<?> eq = Equine.of(targetEntity); Equine<?> eq = Equine.of(targetEntity);
if (eq instanceof Pony) { if (eq instanceof Pony) {
((Pony)eq).getSpellOrEmpty(DisguiseSpell.class).ifPresent(spell -> { ((Pony)eq).getSpellOrEmpty(DisguiseSpell.class).ifPresent(spell -> {
if (spell.getDisguise() == baseEntity) { if (spell.getDisguise().getAppearance() == baseEntity) {
info.setReturnValue(false); info.setReturnValue(false);
} }
}); });