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.USounds;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
import com.minelittlepony.unicopia.entity.mob.UEntities; import com.minelittlepony.unicopia.projectile.MagicBeamEntity;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.world.World; 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). * 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); 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). * 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(); 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)); caster.playSound(USounds.SPELL_CAST_SHOOT, 0.7F, 0.4F / (world.random.nextFloat() * 0.4F + 0.8F));
if (caster.isClient()) { if (caster.isClient()) {
return Optional.empty(); return Optional.empty();
} }
Spell s = spell.get().prepareForCast(caster, CastingMethod.STORED); return Optional.ofNullable(spell.get().prepareForCast(caster, CastingMethod.STORED)).map(s -> {
if (s == null) { MagicBeamEntity projectile = new MagicBeamEntity(world, caster.asEntity(), divergance, s);
return Optional.empty();
}
MagicProjectileEntity projectile = UEntities.MAGIC_BEAM.create(world); configureProjectile(projectile, caster);
projectile.setPosition(entity.getX(), entity.getEyeY() - 0.1F, entity.getZ()); world.spawnEntity(projectile);
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);
return Optional.of(projectile); return projectile;
});
} }
@Override @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.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.mixin.MixinFallingBlockEntity; import com.minelittlepony.unicopia.mixin.MixinFallingBlockEntity;
import com.minelittlepony.unicopia.projectile.MagicBeamEntity;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate; import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
@ -42,15 +43,15 @@ public class CatapultSpell extends AbstractSpell implements ProjectileDelegate.B
@Override @Override
public void onImpact(MagicProjectileEntity projectile, BlockHitResult hit) { public void onImpact(MagicProjectileEntity projectile, BlockHitResult hit) {
if (!projectile.isClient() && projectile.canModifyAt(hit.getBlockPos())) { if (!projectile.isClient() && projectile instanceof MagicBeamEntity source && source.canModifyAt(hit.getBlockPos())) {
createBlockEntity(projectile.getWorld(), hit.getBlockPos(), e -> apply(projectile, e)); createBlockEntity(projectile.getWorld(), hit.getBlockPos(), e -> apply(source, e));
} }
} }
@Override @Override
public void onImpact(MagicProjectileEntity projectile, EntityHitResult hit) { public void onImpact(MagicProjectileEntity projectile, EntityHitResult hit) {
if (!projectile.isClient()) { if (!projectile.isClient() && projectile instanceof MagicBeamEntity source) {
apply(projectile, hit.getEntity()); 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.LightningBoltParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.projectile.MagicBeamEntity;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate; import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
import com.minelittlepony.unicopia.util.shape.Sphere; import com.minelittlepony.unicopia.util.shape.Sphere;
@ -55,10 +56,10 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
@Override @Override
public void onImpact(MagicProjectileEntity projectile, BlockHitResult hit) { public void onImpact(MagicProjectileEntity projectile, BlockHitResult hit) {
if (!projectile.isClient()) { if (!projectile.isClient() && projectile instanceof MagicBeamEntity source) {
BlockPos pos = hit.getBlockPos(); BlockPos pos = hit.getBlockPos();
projectile.getWorld().createExplosion(projectile, pos.getX(), pos.getY(), pos.getZ(), 3, ExplosionSourceType.NONE); 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.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect; import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect;
import com.minelittlepony.unicopia.projectile.MagicBeamEntity;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate; import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
@ -48,6 +49,8 @@ public class DispellEvilSpell extends AbstractSpell implements ProjectileDelegat
@Override @Override
public void onImpact(MagicProjectileEntity projectile) { 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 @Override
public boolean tick(Caster<?> caster, Situation situation) { public boolean tick(Caster<?> caster, Situation situation) {
if (situation == Situation.PROJECTILE) { 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( caster.findAllEntitiesInRange(
getTraits().get(Trait.FOCUS) - 49, getTraits().get(Trait.FOCUS) - 49,
EntityPredicates.VALID_LIVING_ENTITY.and(TargetSelecter.validTarget(this, caster)) EntityPredicates.VALID_LIVING_ENTITY.and(TargetSelecter.validTarget(this, caster))
).findFirst().ifPresent(target -> { ).findFirst().ifPresent(target -> projectile.setHomingTarget(target));
((MagicProjectileEntity)caster).setHomingTarget(target);
});
} }
return true; 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.Creature;
import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.EntityReference;
import com.minelittlepony.unicopia.entity.Equine; import com.minelittlepony.unicopia.entity.Equine;
import com.minelittlepony.unicopia.projectile.MagicBeamEntity;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate; import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
import com.minelittlepony.unicopia.util.Weighted; import com.minelittlepony.unicopia.util.Weighted;
@ -231,9 +232,10 @@ public class NecromancySpell extends AbstractAreaEffectSpell implements Projecti
} }
@Override @Override
public void onImpact(MagicProjectileEntity source, BlockHitResult hit) { public void onImpact(MagicProjectileEntity projectile, BlockHitResult hit) {
if (!(projectile instanceof MagicBeamEntity source)) {
// source.asWorld().createExplosion(source, hit.getPos().x, hit.getPos().y, hit.getPos().z, 3, ExplosionSourceType.MOB); return;
}
Shape affectRegion = new Sphere(false, 3); Shape affectRegion = new Sphere(false, 3);

View file

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

View file

@ -4,6 +4,7 @@ import java.util.function.Predicate;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.entity.behaviour.EntityBehaviour; import com.minelittlepony.unicopia.entity.behaviour.EntityBehaviour;
import com.minelittlepony.unicopia.projectile.MagicBeamEntity;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.PhysicsBodyProjectileEntity; import com.minelittlepony.unicopia.projectile.PhysicsBodyProjectileEntity;
@ -32,7 +33,7 @@ public interface UEntities {
.trackRangeBlocks(100) .trackRangeBlocks(100)
.trackedUpdateRate(2) .trackedUpdateRate(2)
.dimensions(EntityDimensions.fixed(0.25F, 0.25F))); .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) .trackRangeBlocks(100)
.trackedUpdateRate(2) .trackedUpdateRate(2)
.dimensions(EntityDimensions.fixed(0.25F, 0.25F))); .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); 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<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<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<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); 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 java.util.Map;
import com.minelittlepony.unicopia.InteractionManager;
import com.minelittlepony.unicopia.Owned;
import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.data.Rot; import com.minelittlepony.unicopia.ability.data.Rot;
import com.minelittlepony.unicopia.ability.data.tree.TreeTypes; 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.ClientChapters;
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Chapter; import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Chapter;
import com.minelittlepony.unicopia.diet.PonyDiets; import com.minelittlepony.unicopia.diet.PonyDiets;
import com.minelittlepony.unicopia.entity.mob.UEntities;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.network.*; import com.minelittlepony.unicopia.network.*;
import com.minelittlepony.unicopia.network.MsgCasterLookRequest.Reply; import com.minelittlepony.unicopia.network.MsgCasterLookRequest.Reply;
import net.minecraft.client.MinecraftClient; 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.entity.player.PlayerEntity;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
@ -32,7 +27,6 @@ public class ClientNetworkHandlerImpl {
public ClientNetworkHandlerImpl() { public ClientNetworkHandlerImpl() {
Channel.SERVER_SELECT_TRIBE.receiver().addPersistentListener(this::handleTribeScreen); 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.SERVER_BLOCK_DESTRUCTION.receiver().addPersistentListener(this::handleBlockDestruction);
Channel.CANCEL_PLAYER_ABILITY.receiver().addPersistentListener(this::handleCancelAbility); Channel.CANCEL_PLAYER_ABILITY.receiver().addPersistentListener(this::handleCancelAbility);
Channel.UNLOCK_TRAITS.receiver().addPersistentListener(this::handleUnlockTraits); Channel.UNLOCK_TRAITS.receiver().addPersistentListener(this::handleUnlockTraits);
@ -47,30 +41,6 @@ public class ClientNetworkHandlerImpl {
client.setScreen(new TribeSelectionScreen(packet.availableRaces(), packet.serverMessage())); 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) { private void handleBlockDestruction(PlayerEntity sender, MsgBlockDestruction packet) {
ClientBlockDestructionManager destr = ((ClientBlockDestructionManager.Source)client.worldRenderer).getDestructionManager(); 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 org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.WeaklyOwned; 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.EntityReference;
import com.minelittlepony.unicopia.entity.MagicImmune;
import com.minelittlepony.unicopia.entity.Physics;
import com.minelittlepony.unicopia.entity.mob.UEntities; import com.minelittlepony.unicopia.entity.mob.UEntities;
import com.minelittlepony.unicopia.item.UItems; 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.Entity;
import net.minecraft.entity.EntityType; import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
@ -40,8 +24,6 @@ import net.minecraft.item.ItemStack;
import net.minecraft.item.Items; import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement; 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.ItemStackParticleEffect;
import net.minecraft.particle.ParticleEffect; import net.minecraft.particle.ParticleEffect;
import net.minecraft.particle.ParticleTypes; 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.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult; import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
/** /**
* A generalised version of Mojang's projectile entity class with added support for a custom appearance and water phobia. * 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> 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; 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 final EntityReference<Entity> homingTarget = new EntityReference<>();
private EntityReference<LivingEntity> owner; private EntityReference<LivingEntity> owner;
private int maxAge = 90; private int maxAge = 90;
public MagicProjectileEntity(EntityType<MagicProjectileEntity> type, World world) { public MagicProjectileEntity(EntityType<? extends MagicProjectileEntity> type, World world) {
super(type, world); super(type, world);
} }
@ -86,27 +58,24 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Ma
super(UEntities.THROWN_ITEM, thrower, world); super(UEntities.THROWN_ITEM, thrower, world);
} }
protected MagicProjectileEntity(EntityType<? extends MagicProjectileEntity> type, World world, LivingEntity thrower) {
super(type, thrower, world);
}
@Override @Override
protected void initDataTracker() { protected void initDataTracker() {
super.initDataTracker(); super.initDataTracker();
getDataTracker().startTracking(GRAVITY, 1F);
getDataTracker().startTracking(DAMAGE, 0F); getDataTracker().startTracking(DAMAGE, 0F);
getDataTracker().startTracking(EFFECT, new NbtCompound()); }
getDataTracker().startTracking(HYDROPHOBIC, false);
@Override
public World asWorld() {
return getWorld();
} }
@Override @Override
protected Item getDefaultItem() { protected Item getDefaultItem() {
switch (getSpellSlot().get(false).map(Spell::getAffinity).orElse(Affinity.NEUTRAL)) { return Items.AIR;
case GOOD: return Items.SNOWBALL;
case BAD: return Items.MAGMA_CREAM;
default: return Items.AIR;
}
}
@Override
public MagicProjectileEntity asEntity() {
return this;
} }
@Override @Override
@ -140,36 +109,6 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Ma
homingTarget.set(target); 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) { public void addThrowDamage(float damage) {
setThrowDamage(getThrowDamage() + damage); setThrowDamage(getThrowDamage() + damage);
} }
@ -182,14 +121,6 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Ma
return getDataTracker().get(DAMAGE); return getDataTracker().get(DAMAGE);
} }
public void setHydrophobic() {
getDataTracker().set(HYDROPHOBIC, true);
}
public boolean getHydrophobic() {
return getDataTracker().get(HYDROPHOBIC);
}
public void setMaxAge(int maxAge) { public void setMaxAge(int maxAge) {
this.maxAge = maxAge; this.maxAge = maxAge;
} }
@ -202,28 +133,6 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Ma
super.tick(); 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 -> { homingTarget.ifPresent(getWorld(), e -> {
setNoGravity(true); setNoGravity(true);
noClip = true; noClip = true;
@ -261,12 +170,8 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Ma
@Override @Override
public void readCustomDataFromNbt(NbtCompound compound) { public void readCustomDataFromNbt(NbtCompound compound) {
super.readCustomDataFromNbt(compound); super.readCustomDataFromNbt(compound);
physics.fromNBT(compound);
homingTarget.fromNBT(compound.getCompound("homingTarget")); homingTarget.fromNBT(compound.getCompound("homingTarget"));
getMasterReference().fromNBT(compound.getCompound("owner")); getMasterReference().fromNBT(compound.getCompound("owner"));
if (compound.contains("effect")) {
getSpellSlot().put(Spell.readNbt(compound.getCompound("effect")));
}
if (compound.contains("maxAge", NbtElement.INT_TYPE)) { if (compound.contains("maxAge", NbtElement.INT_TYPE)) {
maxAge = compound.getInt("maxAge"); maxAge = compound.getInt("maxAge");
} }
@ -275,13 +180,9 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Ma
@Override @Override
public void writeCustomDataToNbt(NbtCompound compound) { public void writeCustomDataToNbt(NbtCompound compound) {
super.writeCustomDataToNbt(compound); super.writeCustomDataToNbt(compound);
physics.toNBT(compound);
compound.put("homingTarget", homingTarget.toNBT()); compound.put("homingTarget", homingTarget.toNBT());
compound.put("owner", getMasterReference().toNBT()); compound.put("owner", getMasterReference().toNBT());
compound.putInt("maxAge", maxAge); compound.putInt("maxAge", maxAge);
getSpellSlot().get(true).ifPresent(effect -> {
compound.put("effect", Spell.writeNbt(effect));
});
} }
@Override @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 @Override
protected void onBlockHit(BlockHitResult hit) { protected void onBlockHit(BlockHitResult hit) {
super.onBlockHit(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) { 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 { try {
Optional.ofNullable(predicate.apply(getItem().getItem())).ifPresent(consumer); Optional.ofNullable(predicate.apply(getItem().getItem())).ifPresent(consumer);
} catch (Throwable t) { } catch (Throwable t) {
Unicopia.LOGGER.error("Error whilst ticking spell on entity {}", getMasterReference(), 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));
}
} }