Separate the magic beam into its own entity

This commit is contained in:
Sollace 2024-03-03 14:07:36 +00:00
parent 3165f3cd2a
commit 308cb721cd
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
12 changed files with 220 additions and 203 deletions

View file

@ -7,11 +7,7 @@ import java.util.Optional;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
import com.minelittlepony.unicopia.entity.mob.UEntities;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import net.minecraft.entity.Entity;
import com.minelittlepony.unicopia.projectile.MagicBeamEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.world.World;
@ -43,7 +39,7 @@ public final class ThrowableSpell extends AbstractDelegatingSpell {
*
* Returns the resulting projectile entity for customization (or null if on the client).
*/
public Optional<MagicProjectileEntity> throwProjectile(Caster<?> caster) {
public Optional<MagicBeamEntity> throwProjectile(Caster<?> caster) {
return throwProjectile(caster, 1);
}
@ -52,33 +48,23 @@ public final class ThrowableSpell extends AbstractDelegatingSpell {
*
* Returns the resulting projectile entity for customization (or null if on the client).
*/
public Optional<MagicProjectileEntity> throwProjectile(Caster<?> caster, float divergance) {
public Optional<MagicBeamEntity> throwProjectile(Caster<?> caster, float divergance) {
World world = caster.asWorld();
Entity entity = caster.asEntity();
caster.playSound(USounds.SPELL_CAST_SHOOT, 0.7F, 0.4F / (world.random.nextFloat() * 0.4F + 0.8F));
if (caster.isClient()) {
return Optional.empty();
}
Spell s = spell.get().prepareForCast(caster, CastingMethod.STORED);
if (s == null) {
return Optional.empty();
}
return Optional.ofNullable(spell.get().prepareForCast(caster, CastingMethod.STORED)).map(s -> {
MagicBeamEntity projectile = new MagicBeamEntity(world, caster.asEntity(), divergance, s);
MagicProjectileEntity projectile = UEntities.MAGIC_BEAM.create(world);
projectile.setPosition(entity.getX(), entity.getEyeY() - 0.1F, entity.getZ());
projectile.setOwner(entity);
projectile.setItem(UItems.GEMSTONE.getDefaultStack(spell.get().getType()));
s.apply(projectile);
projectile.setVelocity(entity, entity.getPitch(), entity.getYaw(), 0, 1.5F, divergance);
projectile.setNoGravity(true);
configureProjectile(projectile, caster);
world.spawnEntity(projectile);
configureProjectile(projectile, caster);
world.spawnEntity(projectile);
return Optional.of(projectile);
return projectile;
});
}
@Override

View file

@ -10,6 +10,7 @@ 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.mixin.MixinFallingBlockEntity;
import com.minelittlepony.unicopia.projectile.MagicBeamEntity;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
@ -42,15 +43,15 @@ public class CatapultSpell extends AbstractSpell implements ProjectileDelegate.B
@Override
public void onImpact(MagicProjectileEntity projectile, BlockHitResult hit) {
if (!projectile.isClient() && projectile.canModifyAt(hit.getBlockPos())) {
createBlockEntity(projectile.getWorld(), hit.getBlockPos(), e -> apply(projectile, e));
if (!projectile.isClient() && projectile instanceof MagicBeamEntity source && source.canModifyAt(hit.getBlockPos())) {
createBlockEntity(projectile.getWorld(), hit.getBlockPos(), e -> apply(source, e));
}
}
@Override
public void onImpact(MagicProjectileEntity projectile, EntityHitResult hit) {
if (!projectile.isClient()) {
apply(projectile, hit.getEntity());
if (!projectile.isClient() && projectile instanceof MagicBeamEntity source) {
apply(source, hit.getEntity());
}
}

View file

@ -16,6 +16,7 @@ import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.projectile.MagicBeamEntity;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
import com.minelittlepony.unicopia.util.shape.Sphere;
@ -55,10 +56,10 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
@Override
public void onImpact(MagicProjectileEntity projectile, BlockHitResult hit) {
if (!projectile.isClient()) {
if (!projectile.isClient() && projectile instanceof MagicBeamEntity source) {
BlockPos pos = hit.getBlockPos();
projectile.getWorld().createExplosion(projectile, pos.getX(), pos.getY(), pos.getZ(), 3, ExplosionSourceType.NONE);
toPlaceable().tick(projectile, Situation.BODY);
toPlaceable().tick(source, Situation.BODY);
}
}

View file

@ -5,6 +5,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.*;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect;
import com.minelittlepony.unicopia.projectile.MagicBeamEntity;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
@ -48,6 +49,8 @@ public class DispellEvilSpell extends AbstractSpell implements ProjectileDelegat
@Override
public void onImpact(MagicProjectileEntity projectile) {
tick(projectile, Situation.GROUND);
if (projectile instanceof MagicBeamEntity source) {
tick(source, Situation.GROUND);
}
}
}

View file

@ -45,13 +45,11 @@ public class FireBoltSpell extends AbstractSpell implements HomingSpell,
@Override
public boolean tick(Caster<?> caster, Situation situation) {
if (situation == Situation.PROJECTILE) {
if (caster instanceof MagicProjectileEntity && getTraits().get(Trait.FOCUS) >= 50) {
if (caster instanceof MagicProjectileEntity projectile && getTraits().get(Trait.FOCUS) >= 50) {
caster.findAllEntitiesInRange(
getTraits().get(Trait.FOCUS) - 49,
EntityPredicates.VALID_LIVING_ENTITY.and(TargetSelecter.validTarget(this, caster))
).findFirst().ifPresent(target -> {
((MagicProjectileEntity)caster).setHomingTarget(target);
});
).findFirst().ifPresent(target -> projectile.setHomingTarget(target));
}
return true;

View file

@ -12,6 +12,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.Creature;
import com.minelittlepony.unicopia.entity.EntityReference;
import com.minelittlepony.unicopia.entity.Equine;
import com.minelittlepony.unicopia.projectile.MagicBeamEntity;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
import com.minelittlepony.unicopia.util.Weighted;
@ -231,9 +232,10 @@ public class NecromancySpell extends AbstractAreaEffectSpell implements Projecti
}
@Override
public void onImpact(MagicProjectileEntity source, BlockHitResult hit) {
// source.asWorld().createExplosion(source, hit.getPos().x, hit.getPos().y, hit.getPos().z, 3, ExplosionSourceType.MOB);
public void onImpact(MagicProjectileEntity projectile, BlockHitResult hit) {
if (!(projectile instanceof MagicBeamEntity source)) {
return;
}
Shape affectRegion = new Sphere(false, 3);

View file

@ -1,8 +1,7 @@
package com.minelittlepony.unicopia.client.render.entity;
import com.minelittlepony.unicopia.client.render.RenderLayers;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.MagicBeamEntity;
import net.minecraft.client.model.Dilation;
import net.minecraft.client.model.ModelData;
import net.minecraft.client.model.ModelPart;
@ -23,7 +22,7 @@ import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
public class MagicBeamEntityRenderer extends EntityRenderer<MagicProjectileEntity> {
public class MagicBeamEntityRenderer extends EntityRenderer<MagicBeamEntity> {
private final Model model;
public MagicBeamEntityRenderer(EntityRendererFactory.Context ctx) {
@ -32,17 +31,17 @@ public class MagicBeamEntityRenderer extends EntityRenderer<MagicProjectileEntit
}
@Override
public Identifier getTexture(MagicProjectileEntity entity) {
public Identifier getTexture(MagicBeamEntity entity) {
return PlayerScreenHandler.BLOCK_ATLAS_TEXTURE;
}
@Override
protected int getBlockLight(MagicProjectileEntity entity, BlockPos pos) {
protected int getBlockLight(MagicBeamEntity entity, BlockPos pos) {
return 15;
}
@Override
public void render(MagicProjectileEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light) {
public void render(MagicBeamEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light) {
if (entity.age < 2 && dispatcher.camera.getFocusedEntity().squaredDistanceTo(entity) < 8) {
return;
}
@ -68,7 +67,7 @@ public class MagicBeamEntityRenderer extends EntityRenderer<MagicProjectileEntit
super.render(entity, yaw, tickDelta, matrices, vertexConsumers, light);
}
public class Model extends EntityModel<MagicProjectileEntity> {
public class Model extends EntityModel<MagicBeamEntity> {
private final ModelPart part;
@ -89,7 +88,7 @@ public class MagicBeamEntityRenderer extends EntityRenderer<MagicProjectileEntit
}
@Override
public void setAngles(MagicProjectileEntity entity, float limbAngle, float limbDistance, float customAngle, float headYaw, float headPitch) {
public void setAngles(MagicBeamEntity entity, float limbAngle, float limbDistance, float customAngle, float headYaw, float headPitch) {
part.pitch = headPitch;
part.yaw = headYaw;
}

View file

@ -4,6 +4,7 @@ import java.util.function.Predicate;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.entity.behaviour.EntityBehaviour;
import com.minelittlepony.unicopia.projectile.MagicBeamEntity;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.PhysicsBodyProjectileEntity;
@ -32,7 +33,7 @@ public interface UEntities {
.trackRangeBlocks(100)
.trackedUpdateRate(2)
.dimensions(EntityDimensions.fixed(0.25F, 0.25F)));
EntityType<MagicProjectileEntity> MAGIC_BEAM = register("magic_beam", FabricEntityTypeBuilder.<MagicProjectileEntity>create(SpawnGroup.MISC, MagicProjectileEntity::new)
EntityType<MagicBeamEntity> MAGIC_BEAM = register("magic_beam", FabricEntityTypeBuilder.<MagicBeamEntity>create(SpawnGroup.MISC, MagicBeamEntity::new)
.trackRangeBlocks(100)
.trackedUpdateRate(2)
.dimensions(EntityDimensions.fixed(0.25F, 0.25F)));

View file

@ -19,7 +19,6 @@ public interface Channel {
C2SPacketType<MsgPlayerFlightControlsInput> FLIGHT_CONTROLS_INPUT = SimpleNetworking.clientToServer(Unicopia.id("flight_controls"), MsgPlayerFlightControlsInput::new);
S2CPacketType<MsgPlayerCapabilities> SERVER_PLAYER_CAPABILITIES = SimpleNetworking.serverToClient(Unicopia.id("player_capabilities"), MsgPlayerCapabilities::new);
S2CPacketType<MsgSpawnProjectile> SERVER_SPAWN_PROJECTILE = SimpleNetworking.serverToClient(Unicopia.id("projectile_entity"), MsgSpawnProjectile::new);
S2CPacketType<MsgBlockDestruction> SERVER_BLOCK_DESTRUCTION = SimpleNetworking.serverToClient(Unicopia.id("block_destruction"), MsgBlockDestruction::new);
S2CPacketType<MsgCancelPlayerAbility> CANCEL_PLAYER_ABILITY = SimpleNetworking.serverToClient(Unicopia.id("player_ability_cancel"), MsgCancelPlayerAbility::read);
S2CPacketType<MsgCasterLookRequest> SERVER_REQUEST_PLAYER_LOOK = SimpleNetworking.serverToClient(Unicopia.id("request_player_look"), MsgCasterLookRequest::new);

View file

@ -2,8 +2,6 @@ package com.minelittlepony.unicopia.network.handler;
import java.util.Map;
import com.minelittlepony.unicopia.InteractionManager;
import com.minelittlepony.unicopia.Owned;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.data.Rot;
import com.minelittlepony.unicopia.ability.data.tree.TreeTypes;
@ -16,14 +14,11 @@ import com.minelittlepony.unicopia.client.gui.TribeSelectionScreen;
import com.minelittlepony.unicopia.client.gui.spellbook.ClientChapters;
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Chapter;
import com.minelittlepony.unicopia.diet.PonyDiets;
import com.minelittlepony.unicopia.entity.mob.UEntities;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.network.*;
import com.minelittlepony.unicopia.network.MsgCasterLookRequest.Reply;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Identifier;
@ -32,7 +27,6 @@ public class ClientNetworkHandlerImpl {
public ClientNetworkHandlerImpl() {
Channel.SERVER_SELECT_TRIBE.receiver().addPersistentListener(this::handleTribeScreen);
Channel.SERVER_SPAWN_PROJECTILE.receiver().addPersistentListener(this::handleSpawnProjectile);
Channel.SERVER_BLOCK_DESTRUCTION.receiver().addPersistentListener(this::handleBlockDestruction);
Channel.CANCEL_PLAYER_ABILITY.receiver().addPersistentListener(this::handleCancelAbility);
Channel.UNLOCK_TRAITS.receiver().addPersistentListener(this::handleUnlockTraits);
@ -47,30 +41,6 @@ public class ClientNetworkHandlerImpl {
client.setScreen(new TribeSelectionScreen(packet.availableRaces(), packet.serverMessage()));
}
@SuppressWarnings("unchecked")
private void handleSpawnProjectile(PlayerEntity sender, MsgSpawnProjectile packet) {
ClientWorld world = client.world;
Entity entity = packet.getEntityType().create(world);
entity.updateTrackedPosition(packet.getX(), packet.getY(), packet.getZ());
entity.refreshPositionAfterTeleport(packet.getX(), packet.getY(), packet.getZ());
entity.setVelocity(packet.getVelocityX(), packet.getVelocityY(), packet.getVelocityZ());
entity.setPitch(packet.getPitch() * 360 / 256F);
entity.setYaw(packet.getYaw() * 360 / 256F);
entity.setId(packet.getId());
entity.setUuid(packet.getUuid());
if (entity instanceof Owned.Mutable) {
((Owned.Mutable<Entity>) entity).setMaster(world.getEntityById(packet.getEntityData()));
}
if (entity.getType() == UEntities.MAGIC_BEAM) {
InteractionManager.instance().playLoopingSound(entity, InteractionManager.SOUND_MAGIC_BEAM, entity.getId());
}
world.addEntity(packet.getId(), entity);
}
private void handleBlockDestruction(PlayerEntity sender, MsgBlockDestruction packet) {
ClientBlockDestructionManager destr = ((ClientBlockDestructionManager.Source)client.worldRenderer).getDestructionManager();

View file

@ -0,0 +1,171 @@
package com.minelittlepony.unicopia.projectile;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.InteractionManager;
import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Levelled;
import com.minelittlepony.unicopia.ability.magic.SpellContainer;
import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.block.state.StatePredicate;
import com.minelittlepony.unicopia.entity.EntityPhysics;
import com.minelittlepony.unicopia.entity.MagicImmune;
import com.minelittlepony.unicopia.entity.Physics;
import com.minelittlepony.unicopia.entity.mob.UEntities;
import com.minelittlepony.unicopia.network.datasync.EffectSync;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.entity.data.TrackedData;
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.packet.s2c.play.EntitySpawnS2CPacket;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class MagicBeamEntity extends MagicProjectileEntity implements Caster<MagicBeamEntity>, MagicImmune {
private static final TrackedData<Float> GRAVITY = DataTracker.registerData(MagicBeamEntity.class, TrackedDataHandlerRegistry.FLOAT);
private static final TrackedData<Boolean> HYDROPHOBIC = DataTracker.registerData(MagicBeamEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
private static final TrackedData<NbtCompound> EFFECT = DataTracker.registerData(MagicBeamEntity.class, TrackedDataHandlerRegistry.NBT_COMPOUND);
private final EffectSync effectDelegate = new EffectSync(this, EFFECT);
private final EntityPhysics<MagicProjectileEntity> physics = new EntityPhysics<>(this, GRAVITY);
public MagicBeamEntity(EntityType<MagicBeamEntity> type, World world) {
super(type, world);
}
public MagicBeamEntity(World world, Entity owner, float divergance, Spell spell) {
super(UEntities.MAGIC_BEAM, world);
setPosition(owner.getX(), owner.getEyeY() - 0.1F, owner.getZ());
setOwner(owner);
setVelocity(owner, owner.getPitch(), owner.getYaw(), 0, 1.5F, divergance);
setNoGravity(true);
spell.apply(this);
}
@Override
protected void initDataTracker() {
super.initDataTracker();
getDataTracker().startTracking(GRAVITY, 1F);
getDataTracker().startTracking(HYDROPHOBIC, false);
getDataTracker().startTracking(EFFECT, new NbtCompound());
}
@Override
public void tick() {
super.tick();
if (getOwner() != null) {
effectDelegate.tick(Situation.PROJECTILE);
}
if (getHydrophobic()) {
if (StatePredicate.isFluid(getWorld().getBlockState(getBlockPos()))) {
Vec3d vel = getVelocity();
double velY = vel.y;
velY *= -1;
if (!hasNoGravity()) {
velY += 0.16;
}
setVelocity(new Vec3d(vel.x, velY, vel.z));
}
}
}
public void setHydrophobic() {
getDataTracker().set(HYDROPHOBIC, true);
}
public boolean getHydrophobic() {
return getDataTracker().get(HYDROPHOBIC);
}
@Override
public MagicBeamEntity asEntity() {
return this;
}
@Override
public LevelStore getLevel() {
return getMasterReference().getTarget().map(target -> target.level()).orElse(Levelled.EMPTY);
}
@Override
public LevelStore getCorruption() {
return getMasterReference().getTarget().map(target -> target.corruption()).orElse(Levelled.EMPTY);
}
@Override
public Physics getPhysics() {
return physics;
}
@Override
public Affinity getAffinity() {
return getSpellSlot().get(true).map(Affine::getAffinity).orElse(Affinity.NEUTRAL);
}
@Override
public SpellContainer getSpellSlot() {
return effectDelegate;
}
@Override
public boolean subtractEnergyCost(double amount) {
return Caster.of(getMaster()).filter(c -> c.subtractEnergyCost(amount)).isPresent();
}
@Override
public void onSpawnPacket(EntitySpawnS2CPacket packet) {
super.onSpawnPacket(packet);
InteractionManager.instance().playLoopingSound(this, InteractionManager.SOUND_MAGIC_BEAM, getId());
}
@Override
public void remove(RemovalReason reason) {
super.remove(reason);
getSpellSlot().clear();
}
@Override
protected <T extends ProjectileDelegate> void forEachDelegates(Consumer<T> consumer, Function<Object, T> predicate) {
effectDelegate.tick(spell -> {
Optional.ofNullable(predicate.apply(spell)).ifPresent(consumer);
return Operation.SKIP;
});
super.forEachDelegates(consumer, predicate);
}
@Override
public void readCustomDataFromNbt(NbtCompound compound) {
super.readCustomDataFromNbt(compound);
getDataTracker().set(HYDROPHOBIC, compound.getBoolean("hydrophobic"));
physics.fromNBT(compound);
if (compound.contains("effect")) {
getSpellSlot().put(Spell.readNbt(compound.getCompound("effect")));
}
}
@Override
public void writeCustomDataToNbt(NbtCompound compound) {
super.writeCustomDataToNbt(compound);
compound.putBoolean("hydrophobic", getHydrophobic());
physics.toNBT(compound);
getSpellSlot().get(true).ifPresent(effect -> {
compound.put("effect", Spell.writeNbt(effect));
});
}
}

View file

@ -6,28 +6,12 @@ import java.util.function.Function;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.WeaklyOwned;
import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Levelled;
import com.minelittlepony.unicopia.ability.magic.SpellContainer;
import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.block.state.StatePredicate;
import com.minelittlepony.unicopia.entity.EntityPhysics;
import com.minelittlepony.unicopia.entity.EntityReference;
import com.minelittlepony.unicopia.entity.MagicImmune;
import com.minelittlepony.unicopia.entity.Physics;
import com.minelittlepony.unicopia.entity.mob.UEntities;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.network.Channel;
import com.minelittlepony.unicopia.network.MsgSpawnProjectile;
import com.minelittlepony.unicopia.network.datasync.EffectSync;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
@ -40,8 +24,6 @@ import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.network.packet.Packet;
import net.minecraft.network.listener.ClientPlayPacketListener;
import net.minecraft.particle.ItemStackParticleEffect;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.particle.ParticleTypes;
@ -49,32 +31,22 @@ import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
/**
* A generalised version of Mojang's projectile entity class with added support for a custom appearance and water phobia.
*
* Can also carry a spell if needed.
*/
public class MagicProjectileEntity extends ThrownItemEntity implements Caster<MagicProjectileEntity>, WeaklyOwned.Mutable<LivingEntity>, MagicImmune {
public class MagicProjectileEntity extends ThrownItemEntity implements WeaklyOwned.Mutable<LivingEntity> {
private static final TrackedData<Float> DAMAGE = DataTracker.registerData(MagicProjectileEntity.class, TrackedDataHandlerRegistry.FLOAT);
private static final TrackedData<Float> GRAVITY = DataTracker.registerData(MagicProjectileEntity.class, TrackedDataHandlerRegistry.FLOAT);
private static final TrackedData<Boolean> HYDROPHOBIC = DataTracker.registerData(MagicProjectileEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
private static final TrackedData<NbtCompound> EFFECT = DataTracker.registerData(MagicProjectileEntity.class, TrackedDataHandlerRegistry.NBT_COMPOUND);
public static final byte PROJECTILE_COLLISSION = 3;
private final EffectSync effectDelegate = new EffectSync(this, EFFECT);
private final EntityPhysics<MagicProjectileEntity> physics = new EntityPhysics<>(this, GRAVITY);
private final EntityReference<Entity> homingTarget = new EntityReference<>();
private EntityReference<LivingEntity> owner;
private int maxAge = 90;
public MagicProjectileEntity(EntityType<MagicProjectileEntity> type, World world) {
public MagicProjectileEntity(EntityType<? extends MagicProjectileEntity> type, World world) {
super(type, world);
}
@ -86,27 +58,24 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Ma
super(UEntities.THROWN_ITEM, thrower, world);
}
protected MagicProjectileEntity(EntityType<? extends MagicProjectileEntity> type, World world, LivingEntity thrower) {
super(type, thrower, world);
}
@Override
protected void initDataTracker() {
super.initDataTracker();
getDataTracker().startTracking(GRAVITY, 1F);
getDataTracker().startTracking(DAMAGE, 0F);
getDataTracker().startTracking(EFFECT, new NbtCompound());
getDataTracker().startTracking(HYDROPHOBIC, false);
}
@Override
public World asWorld() {
return getWorld();
}
@Override
protected Item getDefaultItem() {
switch (getSpellSlot().get(false).map(Spell::getAffinity).orElse(Affinity.NEUTRAL)) {
case GOOD: return Items.SNOWBALL;
case BAD: return Items.MAGMA_CREAM;
default: return Items.AIR;
}
}
@Override
public MagicProjectileEntity asEntity() {
return this;
return Items.AIR;
}
@Override
@ -140,36 +109,6 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Ma
homingTarget.set(target);
}
@Override
public LevelStore getLevel() {
return getMasterReference().getTarget().map(target -> target.level()).orElse(Levelled.EMPTY);
}
@Override
public LevelStore getCorruption() {
return getMasterReference().getTarget().map(target -> target.corruption()).orElse(Levelled.EMPTY);
}
@Override
public Physics getPhysics() {
return physics;
}
@Override
public Affinity getAffinity() {
return getSpellSlot().get(true).map(Affine::getAffinity).orElse(Affinity.NEUTRAL);
}
@Override
public SpellContainer getSpellSlot() {
return effectDelegate;
}
@Override
public boolean subtractEnergyCost(double amount) {
return Caster.of(getMaster()).filter(c -> c.subtractEnergyCost(amount)).isPresent();
}
public void addThrowDamage(float damage) {
setThrowDamage(getThrowDamage() + damage);
}
@ -182,14 +121,6 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Ma
return getDataTracker().get(DAMAGE);
}
public void setHydrophobic() {
getDataTracker().set(HYDROPHOBIC, true);
}
public boolean getHydrophobic() {
return getDataTracker().get(HYDROPHOBIC);
}
public void setMaxAge(int maxAge) {
this.maxAge = maxAge;
}
@ -202,28 +133,6 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Ma
super.tick();
if (getOwner() == null) {
return;
}
effectDelegate.tick(Situation.PROJECTILE);
if (getHydrophobic()) {
if (StatePredicate.isFluid(getWorld().getBlockState(getBlockPos()))) {
Vec3d vel = getVelocity();
double velY = vel.y;
velY *= -1;
if (!hasNoGravity()) {
velY += 0.16;
}
setVelocity(new Vec3d(vel.x, velY, vel.z));
}
}
homingTarget.ifPresent(getWorld(), e -> {
setNoGravity(true);
noClip = true;
@ -261,12 +170,8 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Ma
@Override
public void readCustomDataFromNbt(NbtCompound compound) {
super.readCustomDataFromNbt(compound);
physics.fromNBT(compound);
homingTarget.fromNBT(compound.getCompound("homingTarget"));
getMasterReference().fromNBT(compound.getCompound("owner"));
if (compound.contains("effect")) {
getSpellSlot().put(Spell.readNbt(compound.getCompound("effect")));
}
if (compound.contains("maxAge", NbtElement.INT_TYPE)) {
maxAge = compound.getInt("maxAge");
}
@ -275,13 +180,9 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Ma
@Override
public void writeCustomDataToNbt(NbtCompound compound) {
super.writeCustomDataToNbt(compound);
physics.toNBT(compound);
compound.put("homingTarget", homingTarget.toNBT());
compound.put("owner", getMasterReference().toNBT());
compound.putInt("maxAge", maxAge);
getSpellSlot().get(true).ifPresent(effect -> {
compound.put("effect", Spell.writeNbt(effect));
});
}
@Override
@ -296,12 +197,6 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Ma
}
}
@Override
public void remove(RemovalReason reason) {
super.remove(reason);
getSpellSlot().clear();
}
@Override
protected void onBlockHit(BlockHitResult hit) {
super.onBlockHit(hit);
@ -329,19 +224,10 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Ma
}
protected <T extends ProjectileDelegate> void forEachDelegates(Consumer<T> consumer, Function<Object, T> predicate) {
effectDelegate.tick(spell -> {
Optional.ofNullable(predicate.apply(spell)).ifPresent(consumer);
return Operation.SKIP;
});
try {
Optional.ofNullable(predicate.apply(getItem().getItem())).ifPresent(consumer);
} catch (Throwable t) {
Unicopia.LOGGER.error("Error whilst ticking spell on entity {}", getMasterReference(), t);
}
}
@Override
public Packet<ClientPlayPacketListener> createSpawnPacket() {
return Channel.SERVER_SPAWN_PROJECTILE.toPacket(new MsgSpawnProjectile(this));
}
}