Implement method to obtain the spectral clock using the altar

This commit is contained in:
Sollace 2024-02-02 21:04:20 +00:00
parent 28e64eebe1
commit 5a5ec8b24c
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
3 changed files with 238 additions and 31 deletions

View file

@ -0,0 +1,42 @@
package com.minelittlepony.unicopia.ability.magic.spell.crafting;
import java.util.List;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.item.UItems;
import net.minecraft.entity.Entity;
import net.minecraft.entity.ItemEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
public record AltarRecipeMatch(
ItemEntity target,
List<ItemEntity> ingredients,
ItemStack result
) {
@Nullable
public static AltarRecipeMatch of(List<ItemEntity> inputs) {
ItemEntity clock = inputs.stream().filter(item -> item.getStack().isOf(Items.CLOCK)).findFirst().orElse(null);
if (clock != null) {
return new AltarRecipeMatch(clock, List.of(), UItems.SPECTRAL_CLOCK.getDefaultStack());
}
return null;
}
public boolean isRemoved() {
return target.isRemoved() || ingredients.stream().anyMatch(ItemEntity::isRemoved);
}
public void craft() {
ItemStack clockStack = result.copyWithCount(target.getStack().getCount());
clockStack.setNbt(target.getStack().getNbt());
target.setStack(clockStack);
target.setInvulnerable(true);
ingredients.forEach(Entity::discard);
}
}

View file

@ -1,19 +1,31 @@
package com.minelittlepony.unicopia.client.render.entity;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.entity.mob.SpellbookEntity;
import com.minelittlepony.unicopia.server.world.Altar;
import net.minecraft.client.render.OverlayTexture;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.EntityRendererFactory;
import net.minecraft.client.render.entity.LivingEntityRenderer;
import net.minecraft.client.render.entity.feature.FeatureRenderer;
import net.minecraft.client.render.entity.feature.FeatureRendererContext;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.*;
public class SpellbookEntityRenderer extends LivingEntityRenderer<SpellbookEntity, SpellbookModel> {
private static final Identifier TEXTURE = Unicopia.id("textures/entity/spellbook/normal.png");
private static final Identifier ALTAR_BEAM_TEXTURE = new Identifier("textures/entity/end_crystal/end_crystal_beam.png");
public SpellbookEntityRenderer(EntityRendererFactory.Context context) {
super(context, new SpellbookModel(SpellbookModel.getTexturedModelData().createModel()), 0);
addFeature(new AltarBeamFeature(this));
}
@Override
@ -51,4 +63,79 @@ public class SpellbookEntityRenderer extends LivingEntityRenderer<SpellbookEntit
|| targetEntity.hasCustomName()
&& targetEntity == dispatcher.targetedEntity);
}
static class AltarBeamFeature extends FeatureRenderer<SpellbookEntity, SpellbookModel> {
public AltarBeamFeature(FeatureRendererContext<SpellbookEntity, SpellbookModel> context) {
super(context);
}
@Override
public void render(MatrixStack matrices, VertexConsumerProvider vertices, int light, SpellbookEntity entity, float limbPos, float limbSpeed, float tickDelta, float animationProgress, float yaw, float pitch) {
if (!entity.hasBeams()) {
return;
}
matrices.peek();
matrices.pop();
matrices.push();
Altar altar = entity.getAltar().get();
Vec3d center = altar.origin().toCenterPos();
float x = (float)MathHelper.lerp(tickDelta, entity.prevX, entity.getX());
float y = (float)MathHelper.lerp(tickDelta, entity.prevY, entity.getY());
float z = (float)MathHelper.lerp(tickDelta, entity.prevZ, entity.getZ());
Vec3d bookPos = new Vec3d(x, y, z);
Vec3d shift = bookPos.subtract(center);
matrices.push();
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(180));
matrices.translate(shift.x, shift.y - 1, shift.z);
for (BlockPos pillar : altar.pillars()) {
renderBeam(center.subtract(pillar.toCenterPos()), -tickDelta, -entity.age, matrices, vertices, light, 1, 0, 1);
}
matrices.pop();
}
public static float getYOffset(float animationProgress) {
animationProgress = MathHelper.sin(animationProgress * 0.2F) * 0.5F + 0.5F;
return ((animationProgress * animationProgress + animationProgress) * 0.4F) - 1.4F;
}
}
public static void renderBeam(Vec3d offset, float tickDelta, int age, MatrixStack matrices, VertexConsumerProvider buffers, int light, float r, float g, float b) {
final float horizontalDistance = (float)offset.horizontalLength();
final float distance = (float)offset.length();
matrices.push();
matrices.multiply(RotationAxis.POSITIVE_Y.rotation((float)(-Math.atan2(offset.z, offset.x)) - 1.5707964f));
matrices.multiply(RotationAxis.POSITIVE_X.rotation((float)(-Math.atan2(horizontalDistance, offset.y)) - 1.5707964f));
VertexConsumer buffer = buffers.getBuffer(RenderLayer.getEntityTranslucent(ALTAR_BEAM_TEXTURE));
final float minV = -(age + tickDelta) * 0.01f;
final float maxV = minV + (distance / 32F);
final int sides = 8;
final float diameter = 0.35F;
float segmentX = 0;
float segmentY = diameter;
float minU = 0;
MatrixStack.Entry entry = matrices.peek();
Matrix4f positionMat = entry.getPositionMatrix();
Matrix3f normalMat = entry.getNormalMatrix();
for (int i = 1; i <= sides; i++) {
float o = MathHelper.sin(i * MathHelper.TAU / sides) * diameter;
float p = MathHelper.cos(i * MathHelper.TAU / sides) * diameter;
float maxU = i / (float)sides;
buffer.vertex(positionMat, segmentX * 0.2F, segmentY * 0.2F, 0).color(0, 0, 0, 255).texture(minU, minV).overlay(OverlayTexture.DEFAULT_UV).light(light).normal(normalMat, 0, -1, 0).next();
buffer.vertex(positionMat, segmentX, segmentY, distance).color(r, g, b, 1).texture(minU, maxV).overlay(OverlayTexture.DEFAULT_UV).light(light).normal(normalMat, 0, -1, 0).next();
buffer.vertex(positionMat, o, p, distance).color(r, g, b, 1).texture(maxU, maxV).overlay(OverlayTexture.DEFAULT_UV).light(light).normal(normalMat, 0, -1, 0).next();
buffer.vertex(positionMat, o * 0.2F, p * 0.2F, 0).color(0, 0, 0, 255).texture(maxU, minV).overlay(OverlayTexture.DEFAULT_UV).light(light).normal(normalMat, 0, -1, 0).next();
segmentX = o;
segmentY = p;
minU = maxU;
}
matrices.pop();
}
}

View file

@ -2,9 +2,12 @@ package com.minelittlepony.unicopia.entity.mob;
import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.ability.magic.spell.crafting.AltarRecipeMatch;
import com.minelittlepony.unicopia.container.SpellbookScreenHandler;
import com.minelittlepony.unicopia.container.SpellbookState;
import com.minelittlepony.unicopia.entity.MagicImmune;
@ -19,6 +22,7 @@ import net.fabricmc.fabric.api.util.TriState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.entity.data.TrackedData;
@ -30,6 +34,7 @@ import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.screen.*;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
@ -38,13 +43,18 @@ import net.minecraft.sound.SoundCategory;
import net.minecraft.text.Text;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.GameRules;
import net.minecraft.world.World;
import net.minecraft.world.World.ExplosionSourceType;
public class SpellbookEntity extends MobEntity implements MagicImmune {
private static final TrackedData<Byte> LOCKED = DataTracker.registerData(SpellbookEntity.class, TrackedDataHandlerRegistry.BYTE);
private static final TrackedData<Boolean> ALTERED = DataTracker.registerData(SpellbookEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
private static final byte ALTAR_BEAMS_START = 61;
private static final byte ALTAR_BEAMS_END = 62;
private static final int TICKS_TO_SLEEP = 600;
@ -55,6 +65,12 @@ public class SpellbookEntity extends MobEntity implements MagicImmune {
private Optional<Altar> altar = Optional.empty();
private boolean hasBeams;
private int beamsActive;
@Nullable
private AltarRecipeMatch activeRecipe;
public SpellbookEntity(EntityType<SpellbookEntity> type, World world) {
super(type, world);
setPersistent();
@ -104,6 +120,14 @@ public class SpellbookEntity extends MobEntity implements MagicImmune {
this.altar = Optional.of(altar);
}
public Optional<Altar> getAltar() {
return altar;
}
public boolean hasBeams() {
return hasBeams && altar.isPresent();
}
public boolean isAltered() {
return dataTracker.get(ALTERED);
}
@ -205,44 +229,83 @@ public class SpellbookEntity extends MobEntity implements MagicImmune {
return false;
}
altar.pillars().forEach(pillar -> {
Vec3d center = pillar.toCenterPos().add(
random.nextTriangular(0.5, 0.2),
random.nextTriangular(0.5, 0.2),
random.nextTriangular(0.5, 0.2)
);
tickAltarCrafting(altar);
((ServerWorld)getWorld()).spawnParticles(
ParticleTypes.SOUL_FIRE_FLAME,
center.x - 0.5, center.y + 0.5, center.z - 0.5,
0,
0.5, 0.5, 0.5, 0);
if (random.nextInt(12) != 0) {
return;
}
Vec3d vel = center.subtract(this.altar.get().origin().toCenterPos()).normalize();
((ServerWorld)getWorld()).spawnParticles(
ParticleTypes.SOUL_FIRE_FLAME,
center.x - 0.5, center.y + 0.5, center.z - 0.5,
0,
vel.x, vel.y, vel.z, -0.2);
if (random.nextInt(2000) == 0) {
if (getWorld().getBlockState(pillar).isOf(Blocks.CRYING_OBSIDIAN)) {
pillar = pillar.down();
}
getWorld().setBlockState(pillar, Blocks.CRYING_OBSIDIAN.getDefaultState());
}
});
Vec3d origin = altar.origin().toCenterPos();
altar.pillars().forEach(pillar -> tickAltarPillar(origin, pillar));
return true;
});
}
}
public void setBeamTicks(int ticks) {
getWorld().sendEntityStatus(this, ticks > 0 ? ALTAR_BEAMS_START : ALTAR_BEAMS_END);
beamsActive = ticks;
}
private void tickAltarCrafting(Altar altar) {
if (activeRecipe == null || activeRecipe.isRemoved()) {
activeRecipe = AltarRecipeMatch.of(getWorld().getEntitiesByClass(ItemEntity.class, Box.of(altar.origin().toCenterPos(), 2, 2, 2), EntityPredicates.VALID_ENTITY));
if (activeRecipe != null) {
setBeamTicks(5);
}
}
if (beamsActive <= 0) {
return;
}
if (--beamsActive > 0) {
playSound(USounds.Vanilla.ENTITY_GUARDIAN_ATTACK, 1.5F, 0.5F);
return;
}
//setBeamTicks(0);
if (activeRecipe == null) {
return;
}
activeRecipe.craft();
activeRecipe = null;
getWorld().createExplosion(this, altar.origin().getX(), altar.origin().getY(), altar.origin().getZ(), 0, ExplosionSourceType.NONE);
}
private void tickAltarPillar(Vec3d origin, BlockPos pillar) {
Vec3d center = pillar.toCenterPos().add(
random.nextTriangular(0.5, 0.2),
random.nextTriangular(0.5, 0.2),
random.nextTriangular(0.5, 0.2)
);
((ServerWorld)getWorld()).spawnParticles(
ParticleTypes.SOUL_FIRE_FLAME,
center.x - 0.5, center.y + 0.5, center.z - 0.5,
0,
0.5, 0.5, 0.5, 0);
if (random.nextInt(12) != 0) {
return;
}
Vec3d vel = center.subtract(origin).normalize();
((ServerWorld)getWorld()).spawnParticles(
ParticleTypes.SOUL_FIRE_FLAME,
center.x - 0.5, center.y + 0.5, center.z - 0.5,
0,
vel.x, vel.y, vel.z, -0.2);
if (random.nextInt(2000) == 0) {
if (getWorld().getBlockState(pillar).isOf(Blocks.CRYING_OBSIDIAN)) {
pillar = pillar.down();
}
getWorld().setBlockState(pillar, Blocks.CRYING_OBSIDIAN.getDefaultState());
}
}
private boolean shouldBeSleeping() {
return MeteorlogicalUtil.getSkyAngle(getWorld()) > 1 && activeTicks <= 0;
}
@ -337,4 +400,19 @@ public class SpellbookEntity extends MobEntity implements MagicImmune {
});
Altar.SERIALIZER.writeOptional("altar", compound, altar);
}
@Override
public void handleStatus(byte status) {
switch (status) {
case ALTAR_BEAMS_START:
altar = Altar.locateAltar(getWorld(), getBlockPos());
hasBeams = altar.isPresent();
break;
case ALTAR_BEAMS_END:
altar = Optional.empty();
hasBeams = false;
default:
super.handleStatus(status);
}
}
}