mirror of
https://github.com/Sollace/Unicopia.git
synced 2025-02-01 03:26:44 +01:00
Merge branch 'Sollace:1.20.1' into 1.20.1
This commit is contained in:
commit
b9b2dac114
22 changed files with 731 additions and 149 deletions
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -1,11 +1,23 @@
|
|||
package com.minelittlepony.unicopia.block.state;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.google.common.base.CharMatcher;
|
||||
import com.google.common.base.Splitter;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.state.property.Property;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public interface StateUtil {
|
||||
Splitter COMMA_SPLITTER = Splitter.on(',');
|
||||
Splitter KEY_VALUE_SPLITTER = Splitter.on('=').limit(2);
|
||||
Splitter STATE_SPLITTER = Splitter.on(CharMatcher.anyOf("[]")).limit(2);
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
static BlockState copyState(BlockState from, @Nullable BlockState to) {
|
||||
if (to == null) {
|
||||
|
@ -16,4 +28,44 @@ public interface StateUtil {
|
|||
}
|
||||
return to;
|
||||
}
|
||||
|
||||
static BlockState stateFromString(String string) {
|
||||
Iterator<String> pair = Splitter.on(CharMatcher.anyOf("[]")).limit(3).split(string).iterator();
|
||||
if (!pair.hasNext()) {
|
||||
return Blocks.AIR.getDefaultState();
|
||||
}
|
||||
Block block = Identifier.validate(pair.next()).result().map(Registries.BLOCK::get).orElse(null);
|
||||
if (block == null) {
|
||||
return Blocks.AIR.getDefaultState();
|
||||
}
|
||||
if (!pair.hasNext()) {
|
||||
return block.getDefaultState();
|
||||
}
|
||||
return stateFromKeyMap(block, pair.next());
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
static BlockState stateFromKeyMap(Block block, String keyMap) {
|
||||
var stateFactory = block.getStateManager();
|
||||
var state = block.getDefaultState();
|
||||
for (String pair : COMMA_SPLITTER.split(keyMap)) {
|
||||
Iterator<String> iterator = KEY_VALUE_SPLITTER.split(pair).iterator();
|
||||
if (!iterator.hasNext()) continue;
|
||||
String propertyName = iterator.next();
|
||||
var property = stateFactory.getProperty(propertyName);
|
||||
if (property != null && iterator.hasNext()) {
|
||||
String value = iterator.next();
|
||||
var comparable = property.parse(value).orElse(null);
|
||||
if (comparable != null) {
|
||||
state = state.with((Property)property, (Comparable)comparable);
|
||||
continue;
|
||||
}
|
||||
throw new RuntimeException("Unknown value: '" + value + "' for blockstate property: '" + propertyName + "' " + property.getValues());
|
||||
}
|
||||
if (!propertyName.isEmpty()) {
|
||||
throw new RuntimeException("Unknown blockstate property: '" + propertyName + "'");
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ import net.minecraft.block.entity.BlockEntity;
|
|||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.color.world.BiomeColors;
|
||||
import net.minecraft.client.color.world.FoliageColors;
|
||||
import net.minecraft.client.item.ClampedModelPredicateProvider;
|
||||
import net.minecraft.client.item.ModelPredicateProviderRegistry;
|
||||
import net.minecraft.client.particle.Particle;
|
||||
import net.minecraft.client.particle.SpriteProvider;
|
||||
|
@ -55,6 +56,8 @@ import net.minecraft.client.render.item.ItemRenderer;
|
|||
import net.minecraft.client.render.model.json.ModelTransformationMode;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.fluid.Fluids;
|
||||
import net.minecraft.item.*;
|
||||
import net.minecraft.particle.ParticleEffect;
|
||||
|
@ -116,7 +119,32 @@ public interface URenderers {
|
|||
PolearmRenderer.register(UItems.WOODEN_POLEARM, UItems.STONE_POLEARM, UItems.IRON_POLEARM, UItems.GOLDEN_POLEARM, UItems.DIAMOND_POLEARM, UItems.NETHERITE_POLEARM);
|
||||
ModelPredicateProviderRegistry.register(UItems.GEMSTONE, new Identifier("affinity"), (stack, world, entity, seed) -> EnchantableItem.isEnchanted(stack) ? EnchantableItem.getSpellKey(stack).getAffinity().getAlignment() : 0);
|
||||
ModelPredicateProviderRegistry.register(UItems.ROCK_CANDY, new Identifier("count"), (stack, world, entity, seed) -> stack.getCount() / (float)stack.getMaxCount());
|
||||
ModelPredicateProviderRegistry.register(Unicopia.id("zap_cycle"), (stack, world, entity, seed) -> UnicopiaClient.getInstance().getZapStageDelta());
|
||||
ModelPredicateProviderRegistry.register(Unicopia.id("zap_cycle"), new ClampedModelPredicateProvider() {
|
||||
private double targetAngle;
|
||||
private double lastAngle;
|
||||
private long lastTick;
|
||||
|
||||
@Override
|
||||
public float unclampedCall(ItemStack stack, ClientWorld world, LivingEntity e, int var4) {
|
||||
Entity entity = e != null ? e : stack.getHolder();
|
||||
if (entity == null) {
|
||||
return 0;
|
||||
}
|
||||
if (world == null && entity.getWorld() instanceof ClientWorld) {
|
||||
world = (ClientWorld)entity.getWorld();
|
||||
}
|
||||
|
||||
if (world == null) {
|
||||
return 0;
|
||||
}
|
||||
if (world.getTime() != lastTick) {
|
||||
targetAngle = world.getDimension().natural() ? UnicopiaClient.getInstance().getZapAppleStage().getCycleProgress(world) : Math.random();
|
||||
lastAngle = lastAngle + (targetAngle - lastAngle) * 0.1;
|
||||
}
|
||||
|
||||
return (float)lastAngle;
|
||||
}
|
||||
});
|
||||
|
||||
ColorProviderRegistry.BLOCK.register(URenderers::getTintedBlockColor, TintedBlock.REGISTRY.stream().toArray(Block[]::new));
|
||||
ColorProviderRegistry.ITEM.register((stack, i) -> getTintedBlockColor(Block.getBlockFromItem(stack.getItem()).getDefaultState(), null, null, i), TintedBlock.REGISTRY.stream().map(Block::asItem).filter(i -> i != Items.AIR).toArray(Item[]::new));
|
||||
|
|
|
@ -55,7 +55,6 @@ public class UnicopiaClient implements ClientModInitializer {
|
|||
public final Lerp skyAngle = new Lerp(0, true);
|
||||
|
||||
private ZapAppleStageStore.Stage zapAppleStage = ZapAppleStageStore.Stage.HIBERNATING;
|
||||
private long zapStageTime;
|
||||
|
||||
public static Optional<PlayerCamera> getCamera() {
|
||||
PlayerEntity player = MinecraftClient.getInstance().player;
|
||||
|
@ -88,13 +87,12 @@ public class UnicopiaClient implements ClientModInitializer {
|
|||
instance = this;
|
||||
}
|
||||
|
||||
public void setZapAppleStage(ZapAppleStageStore.Stage stage, long delta) {
|
||||
public void setZapAppleStage(ZapAppleStageStore.Stage stage) {
|
||||
zapAppleStage = stage;
|
||||
zapStageTime = delta;
|
||||
}
|
||||
|
||||
public float getZapStageDelta() {
|
||||
return zapAppleStage.getCycleProgress(zapStageTime);
|
||||
public ZapAppleStageStore.Stage getZapAppleStage() {
|
||||
return zapAppleStage;
|
||||
}
|
||||
|
||||
public float getSkyAngleDelta(float tickDelta) {
|
||||
|
@ -148,8 +146,6 @@ public class UnicopiaClient implements ClientModInitializer {
|
|||
world.setRainGradient(gradient);
|
||||
world.setThunderGradient(gradient);
|
||||
}
|
||||
|
||||
zapStageTime++;
|
||||
}
|
||||
|
||||
private Float getTargetRainGradient(ClientWorld world, BlockPos pos, float tickDelta) {
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.client.gui;
|
|||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.*;
|
||||
import com.minelittlepony.unicopia.ability.*;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
|
||||
|
|
|
@ -93,6 +93,7 @@ public class DynamicContent implements Content {
|
|||
class Page implements Drawable {
|
||||
private final Text title;
|
||||
private final int level;
|
||||
private final int color;
|
||||
|
||||
private final List<PageElement> elements;
|
||||
|
||||
|
@ -103,6 +104,7 @@ public class DynamicContent implements Content {
|
|||
public Page(PacketByteBuf buffer) {
|
||||
title = buffer.readText();
|
||||
level = buffer.readInt();
|
||||
color = buffer.readInt();
|
||||
elements = buffer.readList(r -> PageElement.read(this, r));
|
||||
}
|
||||
|
||||
|
@ -132,6 +134,10 @@ public class DynamicContent implements Content {
|
|||
return bounds;
|
||||
}
|
||||
|
||||
public int getColor() {
|
||||
return color == 0 ? MagicText.getColor() : color;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
compiled = false;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ public interface PageElement extends Drawable {
|
|||
default -> throw new IllegalArgumentException("Unexpected value: " + t);
|
||||
};
|
||||
}));
|
||||
case 5 -> new Structure.Builder().fromBuffer(buffer).build();
|
||||
default -> throw new IllegalArgumentException("Unexpected value: " + type);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
package com.minelittlepony.unicopia.client.gui.spellbook.element;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.minelittlepony.common.client.gui.IViewRoot;
|
||||
import com.minelittlepony.common.client.gui.dimension.Bounds;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.render.DiffuseLighting;
|
||||
import net.minecraft.client.render.LightmapTextureManager;
|
||||
import net.minecraft.client.render.OverlayTexture;
|
||||
import net.minecraft.client.render.VertexConsumerProvider.Immediate;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.RotationAxis;
|
||||
|
||||
public record Structure(Bounds bounds, List<List<List<BlockState>>> states) implements PageElement {
|
||||
static final BlockState DIAMOND = Blocks.DIAMOND_BLOCK.getDefaultState();
|
||||
static final BlockState AIR = Blocks.AIR.getDefaultState();
|
||||
static final BlockState OBS = Blocks.OBSIDIAN.getDefaultState();
|
||||
static final BlockState SOU = Blocks.SOUL_SAND.getDefaultState();
|
||||
public static final Structure CRYSTAL_HEART_ALTAR = new Structure.Builder()
|
||||
.fill(0, 0, 0, 2, 0, 2, DIAMOND)
|
||||
.set(1, 1, 1, DIAMOND)
|
||||
.set(1, 2, 1, Blocks.END_ROD.getDefaultState().with(Properties.FACING, Direction.UP))
|
||||
.set(1, 4, 1, Blocks.END_ROD.getDefaultState().with(Properties.FACING, Direction.DOWN))
|
||||
.set(1, 5, 1, DIAMOND)
|
||||
.fill(0, 6, 0, 2, 6, 2, DIAMOND)
|
||||
.build();
|
||||
public static final Structure ALTAR_STRUCTURE = new Structure.Builder()
|
||||
.fill(0, 0, 0, 8, 0, 8, SOU)
|
||||
.fill(3, 1, 3, 5, 1, 5, OBS)
|
||||
.set(4, 1, 4, SOU)
|
||||
.set(4, 1, 6, Blocks.LODESTONE.getDefaultState())
|
||||
.fill(0, 1, 2, 0, 4, 2, OBS).fill(0, 1, 6, 0, 4, 6, OBS)
|
||||
.fill(2, 1, 0, 2, 4, 0, OBS).fill(6, 1, 0, 6, 4, 0, OBS)
|
||||
.fill(8, 1, 2, 8, 4, 2, OBS).fill(8, 1, 6, 8, 4, 6, OBS)
|
||||
.fill(2, 1, 8, 2, 4, 8, OBS).fill(6, 1, 8, 6, 4, 8, OBS)
|
||||
.build();
|
||||
|
||||
@Override
|
||||
public void draw(DrawContext context, int mouseX, int mouseY, IViewRoot container) {
|
||||
int height = states.size();
|
||||
if (height == 0) {
|
||||
return;
|
||||
}
|
||||
int depth = states.get(0).size();
|
||||
if (depth == 0) {
|
||||
return;
|
||||
}
|
||||
int width = states.get(0).get(0).size();
|
||||
if (width == 0) {
|
||||
return;
|
||||
}
|
||||
MatrixStack matrices = context.getMatrices();
|
||||
Immediate immediate = context.getVertexConsumers();
|
||||
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
float tickDelta = client.player.age + client.getTickDelta();
|
||||
float age = tickDelta % 360F;
|
||||
|
||||
matrices.push();
|
||||
if (container != null) {
|
||||
matrices.translate(container.getBounds().width / 2, container.getBounds().height / 2, 100);
|
||||
float minDimensions = Math.min(container.getBounds().width, container.getBounds().height) - 30;
|
||||
int minSize = Math.max(height, Math.max(width, depth)) * 16;
|
||||
float scale = minDimensions / minSize;
|
||||
matrices.scale(scale, scale, 1);
|
||||
}
|
||||
matrices.scale(-16, -16, -16);
|
||||
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-20 + MathHelper.sin(tickDelta / 10F) * 2));
|
||||
matrices.peek().getPositionMatrix().rotate(RotationAxis.POSITIVE_Y.rotationDegrees(age));
|
||||
DiffuseLighting.enableForLevel(matrices.peek().getPositionMatrix());
|
||||
|
||||
matrices.translate(-width / 2F, -height / 2F, -depth / 2F);
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
for (int z = 0; z < depth; z++) {
|
||||
BlockState state = states.get(y).get(z).get(x);
|
||||
matrices.push();
|
||||
matrices.translate(x, y, z);
|
||||
client.getBlockRenderManager().renderBlockAsEntity(state, matrices, immediate, LightmapTextureManager.MAX_LIGHT_COORDINATE, OverlayTexture.DEFAULT_UV);
|
||||
matrices.pop();
|
||||
}
|
||||
|
||||
matrices.pop();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private int dx = -1;
|
||||
private int dy = -1;
|
||||
private int dz = -1;
|
||||
private final List<List<List<BlockState>>> layers = new ArrayList<>();
|
||||
|
||||
private void resize(int x, int y, int z) {
|
||||
int ddx = Math.max(dx, x);
|
||||
int ddy = Math.max(dy, y);
|
||||
int ddz = Math.max(dz, z);
|
||||
|
||||
if (ddx <= dx && ddy <= dy && ddz <= dz) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ddy > dy) {
|
||||
dy = ddy;
|
||||
while (layers.size() <= ddy) {
|
||||
layers.add(createFilledList(ddz, () -> createFilledList(ddx, () -> AIR)));
|
||||
}
|
||||
}
|
||||
if (ddz > dz || ddx > dx) {
|
||||
layers.forEach(layer -> {
|
||||
if (ddz > dz) {
|
||||
while (layer.size() <= ddz) {
|
||||
layer.add(createFilledList(ddx, () -> AIR));
|
||||
}
|
||||
}
|
||||
if (ddx > dx) {
|
||||
layer.forEach(row -> {
|
||||
while (row.size() <= ddx) {
|
||||
row.add(AIR);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
dz = ddz;
|
||||
dx = ddx;
|
||||
}
|
||||
}
|
||||
|
||||
private <T> List<T> createFilledList(int length, Supplier<T> contentSupplier) {
|
||||
List<T> list = new ArrayList<>();
|
||||
while (list.size() <= length) {
|
||||
list.add(contentSupplier.get());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public Builder set(int x, int y, int z, BlockState state) {
|
||||
resize(x, y, z);
|
||||
layers.get(y).get(z).set(x, state);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder fill(int minX, int minY, int minZ, int maxX, int maxY, int maxZ, BlockState state) {
|
||||
resize(maxX, maxY, maxZ);
|
||||
for (int x = minX; x <= maxX; x++)
|
||||
for (int y = minY; y <= maxY; y++)
|
||||
for (int z = minZ; z <= maxZ; z++)
|
||||
layers.get(y).get(z).set(x, state);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder fromBuffer(PacketByteBuf buffer) {
|
||||
|
||||
buffer.readCollection(ArrayList::new, buf -> {
|
||||
byte op = buf.readByte();
|
||||
return switch (op) {
|
||||
case 1 -> set(buf.readInt(), buf.readInt(), buf.readInt(), Block.getStateFromRawId(buf.readInt()));
|
||||
case 2 -> fill(buf.readInt(), buf.readInt(), buf.readInt(), buf.readInt(), buf.readInt(), buf.readInt(), Block.getStateFromRawId(buf.readInt()));
|
||||
default -> this;
|
||||
};
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Structure build() {
|
||||
return new Structure(Bounds.empty(), layers);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ import java.util.function.Supplier;
|
|||
|
||||
import com.minelittlepony.common.client.gui.IViewRoot;
|
||||
import com.minelittlepony.common.client.gui.dimension.Bounds;
|
||||
import com.minelittlepony.unicopia.client.gui.MagicText;
|
||||
import com.minelittlepony.unicopia.client.gui.ParagraphWrappingVisitor;
|
||||
import com.minelittlepony.unicopia.container.SpellbookChapterLoader.Flow;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
|
@ -53,7 +52,7 @@ class TextBlock implements PageElement {
|
|||
MatrixStack matrices = context.getMatrices();
|
||||
matrices.push();
|
||||
wrappedText.forEach(line -> {
|
||||
context.drawText(font, needsMoreXp ? line.text().copy().formatted(Formatting.OBFUSCATED) : line.text().copy(), line.x(), 0, MagicText.getColor(), false);
|
||||
context.drawText(font, needsMoreXp ? line.text().copy().formatted(Formatting.OBFUSCATED) : line.text().copy(), line.x(), 0, page.getColor(), false);
|
||||
matrices.translate(0, font.fontHeight, 0);
|
||||
});
|
||||
matrices.pop();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -6,9 +6,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.effect.PortalSpell;
|
|||
import com.minelittlepony.unicopia.client.render.RenderLayers;
|
||||
import com.minelittlepony.unicopia.client.render.model.SphereModel;
|
||||
import com.minelittlepony.unicopia.entity.EntityReference;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.Frustum;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
|
@ -80,10 +78,4 @@ public class PortalSpellRenderer extends SpellRenderer<PortalSpell> {
|
|||
|
||||
matrices.pop();
|
||||
}
|
||||
|
||||
public interface WorldRendererDuck {
|
||||
ObjectArrayList<?> unicopia_getChunkInfos();
|
||||
|
||||
Frustum unicopia_getFrustum();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,18 +13,21 @@ import com.minelittlepony.unicopia.Debug;
|
|||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.crafting.IngredientWithSpell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
||||
import com.minelittlepony.unicopia.block.state.StateUtil;
|
||||
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.*;
|
||||
import com.minelittlepony.unicopia.network.Channel;
|
||||
import com.minelittlepony.unicopia.network.MsgServerResources;
|
||||
import com.minelittlepony.unicopia.util.Resources;
|
||||
import com.mojang.logging.LogUtils;
|
||||
|
||||
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.resource.JsonDataLoader;
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.text.TextColor;
|
||||
import net.minecraft.util.*;
|
||||
import net.minecraft.util.profiler.Profiler;
|
||||
|
||||
|
@ -128,9 +131,10 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab
|
|||
private record Page (
|
||||
Text title,
|
||||
int level,
|
||||
int color,
|
||||
List<Element> elements
|
||||
) {
|
||||
private static final Page EMPTY = new Page(Text.empty(), 0, List.of());
|
||||
private static final Page EMPTY = new Page(Text.empty(), 0, 0, List.of());
|
||||
|
||||
public static Page of(JsonElement json) {
|
||||
return json.isJsonObject() && json.getAsJsonObject().keySet().isEmpty() ? EMPTY : new Page(json);
|
||||
|
@ -144,6 +148,7 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab
|
|||
this(
|
||||
readText(json.get("title")),
|
||||
JsonHelper.getInt(json, "level", 0),
|
||||
Optional.ofNullable(TextColor.parse(JsonHelper.getString(json, "color", ""))).map(TextColor::getRgb).orElse(0),
|
||||
new ArrayList<>()
|
||||
);
|
||||
JsonHelper.getArray(json, "elements", new JsonArray()).forEach(element -> {
|
||||
|
@ -154,6 +159,7 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab
|
|||
public void toBuffer(PacketByteBuf buffer) {
|
||||
buffer.writeText(title);
|
||||
buffer.writeInt(level);
|
||||
buffer.writeInt(color);
|
||||
buffer.writeCollection(elements, Element::write);
|
||||
}
|
||||
|
||||
|
@ -208,6 +214,59 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab
|
|||
}
|
||||
}
|
||||
|
||||
record Structure(List<Element> commands) implements Element {
|
||||
static Element loadCommand(JsonObject json) {
|
||||
|
||||
if (json.has("pos")) {
|
||||
var pos = JsonHelper.getArray(json, "pos");
|
||||
return new Set(
|
||||
pos.get(0).getAsInt(), pos.get(1).getAsInt(), pos.get(2).getAsInt(),
|
||||
StateUtil.stateFromString(json.get("state").getAsString())
|
||||
);
|
||||
}
|
||||
|
||||
var min = JsonHelper.getArray(json, "min");
|
||||
var max = JsonHelper.getArray(json, "max");
|
||||
return new Fill(
|
||||
min.get(0).getAsInt(), min.get(1).getAsInt(), min.get(2).getAsInt(),
|
||||
max.get(0).getAsInt(), max.get(1).getAsInt(), max.get(2).getAsInt(),
|
||||
StateUtil.stateFromString(json.get("state").getAsString())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBuffer(PacketByteBuf buffer) {
|
||||
buffer.writeByte(5);
|
||||
buffer.writeCollection(commands, (b, c) -> c.toBuffer(b));
|
||||
}
|
||||
|
||||
record Set(int x, int y, int z, BlockState state) implements Element {
|
||||
@Override
|
||||
public void toBuffer(PacketByteBuf buffer) {
|
||||
buffer.writeByte(1);
|
||||
buffer.writeInt(x);
|
||||
buffer.writeInt(y);
|
||||
buffer.writeInt(z);
|
||||
buffer.writeInt(Block.getRawIdFromState(state));
|
||||
}
|
||||
|
||||
}
|
||||
record Fill(int x1, int y1, int z1, int x2, int y2, int z2, BlockState state) implements Element {
|
||||
@Override
|
||||
public void toBuffer(PacketByteBuf buffer) {
|
||||
buffer.writeByte(2);
|
||||
buffer.writeInt(x1);
|
||||
buffer.writeInt(y1);
|
||||
buffer.writeInt(z1);
|
||||
buffer.writeInt(x2);
|
||||
buffer.writeInt(y2);
|
||||
buffer.writeInt(z2);
|
||||
buffer.writeInt(Block.getRawIdFromState(state));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
record Ingredients(List<Element> entries) implements Element {
|
||||
static Element loadIngredient(JsonObject json) {
|
||||
int count = JsonHelper.getInt(json, "count", 1);
|
||||
|
@ -263,6 +322,14 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab
|
|||
.toList()
|
||||
);
|
||||
}
|
||||
|
||||
if (el.has("structure")) {
|
||||
return new Structure(JsonHelper.getArray(el, "structure").asList().stream()
|
||||
.map(JsonElement::getAsJsonObject)
|
||||
.map(Structure::loadCommand)
|
||||
.toList()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new TextBlock(readText(json));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||
|
||||
import com.minelittlepony.unicopia.client.ClientBlockDestructionManager;
|
||||
import com.minelittlepony.unicopia.client.UnicopiaClient;
|
||||
import com.minelittlepony.unicopia.client.render.spell.PortalSpellRenderer;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.client.render.BlockBreakingInfo;
|
||||
import net.minecraft.client.render.Camera;
|
||||
import net.minecraft.client.render.WorldRenderer;
|
||||
|
@ -28,7 +25,7 @@ import net.minecraft.resource.SynchronousResourceReloader;
|
|||
import net.minecraft.util.math.RotationAxis;
|
||||
|
||||
@Mixin(value = WorldRenderer.class, priority = 1001)
|
||||
abstract class MixinWorldRenderer implements SynchronousResourceReloader, AutoCloseable, ClientBlockDestructionManager.Source, PortalSpellRenderer.WorldRendererDuck {
|
||||
abstract class MixinWorldRenderer implements SynchronousResourceReloader, AutoCloseable, ClientBlockDestructionManager.Source {
|
||||
|
||||
private final ClientBlockDestructionManager destructions = new ClientBlockDestructionManager();
|
||||
|
||||
|
@ -44,10 +41,6 @@ abstract class MixinWorldRenderer implements SynchronousResourceReloader, AutoCl
|
|||
return destructions;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Accessor("chunkInfos")
|
||||
public abstract ObjectArrayList<?> unicopia_getChunkInfos();
|
||||
|
||||
@Override
|
||||
@Accessor("ticks")
|
||||
public abstract int getTicks();
|
||||
|
|
|
@ -55,7 +55,7 @@ public interface Channel {
|
|||
sender.sendPacket(SERVER_RESOURCES.id(), new MsgServerResources().toBuffer());
|
||||
sender.sendPacket(SERVER_SKY_ANGLE.id(), new MsgSkyAngle(UnicopiaWorldProperties.forWorld(handler.getPlayer().getServerWorld()).getTangentalSkyAngle()).toBuffer());
|
||||
ZapAppleStageStore store = ZapAppleStageStore.get(handler.player.getServerWorld());
|
||||
sender.sendPacket(SERVER_ZAP_STAGE.id(), new MsgZapAppleStage(store.getStage(), store.getStageDelta()).toBuffer());
|
||||
sender.sendPacket(SERVER_ZAP_STAGE.id(), new MsgZapAppleStage(store.getStage()).toBuffer());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,17 +7,15 @@ import net.minecraft.entity.player.PlayerEntity;
|
|||
import net.minecraft.network.PacketByteBuf;
|
||||
|
||||
public record MsgZapAppleStage (
|
||||
ZapAppleStageStore.Stage stage,
|
||||
long delta
|
||||
ZapAppleStageStore.Stage stage
|
||||
) implements Packet<PlayerEntity> {
|
||||
|
||||
public MsgZapAppleStage(PacketByteBuf buffer) {
|
||||
this(buffer.readEnumConstant(ZapAppleStageStore.Stage.class), buffer.readLong());
|
||||
this(buffer.readEnumConstant(ZapAppleStageStore.Stage.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBuffer(PacketByteBuf buffer) {
|
||||
buffer.writeEnumConstant(stage);
|
||||
buffer.writeLong(delta);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ public class ClientNetworkHandlerImpl {
|
|||
}
|
||||
|
||||
private void handleZapStage(PlayerEntity sender, MsgZapAppleStage packet) {
|
||||
UnicopiaClient.getInstance().setZapAppleStage(packet.stage(), packet.delta());
|
||||
UnicopiaClient.getInstance().setZapAppleStage(packet.stage());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
|
@ -3,12 +3,15 @@ package com.minelittlepony.unicopia.server.world;
|
|||
import java.util.Locale;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.USounds;
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import com.minelittlepony.unicopia.network.Channel;
|
||||
import com.minelittlepony.unicopia.network.MsgZapAppleStage;
|
||||
import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect;
|
||||
import com.minelittlepony.unicopia.particle.ParticleUtils;
|
||||
import com.minelittlepony.unicopia.util.MeteorlogicalUtil;
|
||||
import com.minelittlepony.unicopia.util.Tickable;
|
||||
|
||||
import net.minecraft.entity.EntityType;
|
||||
|
@ -37,17 +40,15 @@ public class ZapAppleStageStore extends PersistentState implements Tickable {
|
|||
private final World world;
|
||||
|
||||
private Stage lastStage = Stage.HIBERNATING;
|
||||
private long stageDelta;
|
||||
private long lastTime;
|
||||
|
||||
private boolean stageChanged;
|
||||
private boolean playedMoonEffect;
|
||||
private int nextLightningEvent = 1200;
|
||||
private float prevSkyAngle;
|
||||
|
||||
ZapAppleStageStore(World world, NbtCompound compound) {
|
||||
this(world);
|
||||
lastStage = Stage.VALUES[Math.max(0, compound.getInt("stage")) % Stage.VALUES.length];
|
||||
stageDelta = compound.getLong("stageDelta");
|
||||
stageChanged = compound.getBoolean("stageChanged");
|
||||
playedMoonEffect = compound.getBoolean("playedMoonEffect");
|
||||
nextLightningEvent = compound.getInt("nextLightningEvent");
|
||||
|
@ -59,41 +60,36 @@ public class ZapAppleStageStore extends PersistentState implements Tickable {
|
|||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (!world.isDay()) {
|
||||
float skyAngle = MeteorlogicalUtil.getSkyAngle(world);
|
||||
|
||||
if (skyAngle > MeteorlogicalUtil.SUNSET) {
|
||||
if (nextLightningEvent > 0) {
|
||||
nextLightningEvent--;
|
||||
markDirty();
|
||||
}
|
||||
|
||||
if (!stageChanged && (lastStage != Stage.HIBERNATING || (world.getMoonPhase() == 0))) {
|
||||
if (!stageChanged && MathHelper.approximatelyEquals(skyAngle, MeteorlogicalUtil.MIDNIGHT) || (
|
||||
MeteorlogicalUtil.isBetween(skyAngle, MeteorlogicalUtil.MIDNIGHT, MeteorlogicalUtil.MOONSET)
|
||||
&& MeteorlogicalUtil.isBetween(prevSkyAngle, MeteorlogicalUtil.SUNSET, MeteorlogicalUtil.MIDNIGHT)
|
||||
)) {
|
||||
stageChanged = true;
|
||||
lastStage = lastStage.getNext();
|
||||
stageDelta = 0;
|
||||
playedMoonEffect = false;
|
||||
sendUpdate();
|
||||
if (lastStage != Stage.HIBERNATING || world.getMoonPhase() == 0) {
|
||||
lastStage = lastStage.getNext();
|
||||
playedMoonEffect = false;
|
||||
markDirty();
|
||||
sendUpdate();
|
||||
}
|
||||
}
|
||||
} else if (stageChanged) {
|
||||
stageChanged = false;
|
||||
markDirty();
|
||||
}
|
||||
|
||||
long timeOfDay = world.getTimeOfDay();
|
||||
if (stageDelta != 0 && (timeOfDay < lastTime || timeOfDay > lastTime + 10)) {
|
||||
long timeDifference = timeOfDay - lastTime;
|
||||
Unicopia.LOGGER.info("Times a changing {}!", timeDifference);
|
||||
while (timeDifference < 0) {
|
||||
timeDifference += DAY_LENGTH;
|
||||
}
|
||||
stageDelta += timeDifference;
|
||||
sendUpdate();
|
||||
}
|
||||
lastTime = timeOfDay;
|
||||
|
||||
|
||||
stageDelta++;
|
||||
markDirty();
|
||||
prevSkyAngle = skyAngle;
|
||||
}
|
||||
|
||||
protected void sendUpdate() {
|
||||
Channel.SERVER_ZAP_STAGE.sendToAllPlayers(new MsgZapAppleStage(getStage(), stageDelta), world);
|
||||
Channel.SERVER_ZAP_STAGE.sendToAllPlayers(new MsgZapAppleStage(getStage()), world);
|
||||
}
|
||||
|
||||
public void playMoonEffect(BlockPos pos) {
|
||||
|
@ -137,22 +133,9 @@ public class ZapAppleStageStore extends PersistentState implements Tickable {
|
|||
return lastStage;
|
||||
}
|
||||
|
||||
public long getStageDelta() {
|
||||
return stageDelta;
|
||||
}
|
||||
|
||||
public float getStageProgress() {
|
||||
return getStage().getStageProgress(getStageDelta());
|
||||
}
|
||||
|
||||
public float getCycleProgress() {
|
||||
return getStage().getCycleProgress(getStageDelta());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtCompound writeNbt(NbtCompound compound) {
|
||||
compound.putInt("stage", lastStage.ordinal());
|
||||
compound.putLong("stageDelta", stageDelta);
|
||||
compound.putBoolean("stageChanged", stageChanged);
|
||||
compound.putBoolean("playedMoonEffect", playedMoonEffect);
|
||||
compound.putInt("nextLightningEvent", nextLightningEvent);
|
||||
|
@ -160,19 +143,16 @@ public class ZapAppleStageStore extends PersistentState implements Tickable {
|
|||
}
|
||||
|
||||
public enum Stage implements StringIdentifiable {
|
||||
HIBERNATING(MOON_PHASES * DAY_LENGTH),
|
||||
GREENING(DAY_LENGTH),
|
||||
FLOWERING(DAY_LENGTH),
|
||||
FRUITING(DAY_LENGTH),
|
||||
RIPE(DAY_LENGTH);
|
||||
HIBERNATING,
|
||||
GREENING,
|
||||
FLOWERING,
|
||||
FRUITING,
|
||||
RIPE;
|
||||
|
||||
static final Stage[] VALUES = values();
|
||||
static final float MAX = VALUES.length;
|
||||
|
||||
private final long duration;
|
||||
|
||||
Stage(long duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
private final float ordinal = ordinal();
|
||||
|
||||
public Stage getNext() {
|
||||
return byId((ordinal() + 1) % VALUES.length);
|
||||
|
@ -182,13 +162,21 @@ public class ZapAppleStageStore extends PersistentState implements Tickable {
|
|||
return byId(((ordinal() - 1) + VALUES.length) % VALUES.length);
|
||||
}
|
||||
|
||||
public float getStageProgress(long time) {
|
||||
return (time % duration) / (float)duration;
|
||||
public float getStageProgress(@Nullable World world) {
|
||||
if (world == null) {
|
||||
return 0;
|
||||
}
|
||||
float skyAngle = MeteorlogicalUtil.getSkyAngle(world);
|
||||
float dayProgress = ((skyAngle + 1.5F) % 3F) / 3F;
|
||||
if (this == HIBERNATING) {
|
||||
return (world.getMoonPhase() + dayProgress) / MOON_PHASES;
|
||||
}
|
||||
|
||||
return dayProgress;
|
||||
}
|
||||
|
||||
public float getCycleProgress(long time) {
|
||||
float ordinal = ordinal();
|
||||
return MathHelper.lerp(getStageProgress(time), ordinal, ordinal + 1) / 5F;
|
||||
public float getCycleProgress(@Nullable World world) {
|
||||
return MathHelper.lerp(getStageProgress(world), ordinal, ordinal + 1) / MAX;
|
||||
}
|
||||
|
||||
public static Stage byId(int id) {
|
||||
|
|
|
@ -12,6 +12,15 @@ import net.minecraft.world.LightType;
|
|||
import net.minecraft.world.World;
|
||||
|
||||
public interface MeteorlogicalUtil {
|
||||
float SUNRISE = 0;
|
||||
float NOON = 0.5F;
|
||||
float SUNSET = 1;
|
||||
float MIDNIGHT = 1.5F;
|
||||
float MOONSET = 2;
|
||||
|
||||
static boolean isBetween(float skyAngle, float start, float end) {
|
||||
return skyAngle >= start && skyAngle <= end;
|
||||
}
|
||||
|
||||
static boolean isLookingIntoSun(World world, Entity entity) {
|
||||
|
||||
|
@ -52,7 +61,7 @@ public interface MeteorlogicalUtil {
|
|||
|
||||
static float getSunIntensity(World world) {
|
||||
float skyAngle = getSkyAngle(world);
|
||||
if (skyAngle > 1) {
|
||||
if (skyAngle > SUNSET) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -69,7 +78,21 @@ public interface MeteorlogicalUtil {
|
|||
return intensity;
|
||||
}
|
||||
|
||||
// we translate sun angle to a scale of 0-1 (0=sunrise, 1=sunset, >1 nighttime)
|
||||
/**
|
||||
* Gets the sun angle on a scale of 0-2 where 0=sunrise, 1=sunset, and >1 is night
|
||||
*
|
||||
* 0 = sunrisde
|
||||
* 0-0.5 = morning
|
||||
* 0.5 = noon
|
||||
* 0.5-1 = evening
|
||||
* 1 = sunset
|
||||
* 1-1.5 = night
|
||||
* 1.5 = midnight
|
||||
* 1.5-0 = night
|
||||
*
|
||||
* @param world
|
||||
* @return The sun angle
|
||||
*/
|
||||
static float getSkyAngle(World world) {
|
||||
return ((world.getSkyAngle(1) + 0.25F) % 1F) * 2;
|
||||
}
|
||||
|
|
|
@ -911,8 +911,6 @@
|
|||
"gui.unicopia.spellbook.chapter.ice.p7.title": "Bonfire II",
|
||||
"gui.unicopia.spellbook.chapter.ice.p7.1.body": "On the way back Luna was telling me of the stories her friend told her. The town has a lot of legends, as to be expected.",
|
||||
"gui.unicopia.spellbook.chapter.ice.p7.2.body": "One of them was about a scary old warlock who lived in a haunted tower at the edge of town. There's no mystery who that was about.",
|
||||
"gui.unicopia.spellbook.chapter.ice.p7.3.body": "Ice Spell II",
|
||||
"gui.unicopia.spellbook.chapter.ice.p7.4.body": "Creates a cooling affect up to a radius of 3 hooves from any surfaces it touches.",
|
||||
"gui.unicopia.spellbook.chapter.ice.p8.title": "6th Trot '12",
|
||||
"gui.unicopia.spellbook.chapter.ice.p8.1.body": "There was a strange noise in the village last night. Very strange. I heard a lot ponies shouting and there may have been a fire.",
|
||||
"gui.unicopia.spellbook.chapter.ice.p8.2.body": "I hope everything is okay.",
|
||||
|
@ -1118,6 +1116,10 @@
|
|||
"gui.unicopia.spellbook.chapter.artefacts.torn_page.2.body": "§kAasa sasa fwefsd q43rgfd wqklmsdfl as, klasn.§r",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.torn_page.3.body": "Building Materials:",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.crystal_podium.title": "Crystal Podium",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.altar.title": "Altar",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.altar.1.body": "An an§kc§rient altar con§ktr§ructed by an early t§kr§ribe of §kunicorn§rs. It's thought that these were used to perform r§kituals§r sum§kmoni§rng upon §keven o§rlder mag§kics§r.",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.altar.2.body": "Not much is §kkn§rown about these my§ks§rterious structures, and they hold many secrets even to this day.",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.altar.3.body": "U§ks§re the §ka§rltar",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.2.body": "It's, um a scroll that you write somepony's name on it and you hold it in one hoof and something in the other hoof and, like, um it goes whooosh and the item is sent to that pony.",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.title": "2nd Hoof '12",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.3.body": "P.S. Uncle Starswirly is a dunderhead.",
|
||||
|
@ -1126,6 +1128,9 @@
|
|||
"gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.title": "13th Mare '12",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.3.body": "Anyone wearing a bangle you have signed will be able to benefit from the positive effects of your spells, or will be allowed through protection and shield spells.",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.4.body": "Mana costs are also shared equally between all nearby members.",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.spectral_clock.title": "14th Mare '12",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.spectral_clock.1.body": "Not so much an artefact as a strange trinket. Luna happened to bring this home from the market last week, and though at first glance it may seem to be an ordinary broken clock what I've found is far stranger.",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.spectral_clock.2.body": "This clock doesn't tell the time. Well, it does, but not directly. Rather it seems to be following the cycles of some of the plants in the surrounding forest.",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.1.body": "Commander Hurricane informed me of this, though I've found little texts to back up his claims.",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.2.body": "The Pegasus Amulet is claimed to grant the wearer temporary flight, like a pegasus.",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.title": "21st Trot '12",
|
||||
|
|
|
@ -58,17 +58,77 @@
|
|||
"title": "gui.unicopia.spellbook.chapter.artefacts.crystal_podium.title",
|
||||
"level": 0,
|
||||
"elements": [
|
||||
{ "x": 40, "item": { "item": "minecraft:diamond_block" } },
|
||||
{ "x": 60, "y": -17, "item": { "item": "minecraft:diamond_block" } },
|
||||
{ "x": 80, "y": -34, "item": { "item": "minecraft:diamond_block" } },
|
||||
{ "x": 60, "y": -34, "item": { "item": "minecraft:diamond_block" } },
|
||||
{ "x": 60, "y": -34, "item": { "item": "minecraft:end_rod" } },
|
||||
{ "x": 60, "y": -34, "item": { "item": "unicopia:crystal_heart" } },
|
||||
{ "x": 60, "y": -34, "item": { "item": "minecraft:end_rod" } },
|
||||
{ "x": 60, "y": -34, "item": { "item": "minecraft:diamond_block" } },
|
||||
{ "x": 40, "y": -34, "item": { "item": "minecraft:diamond_block" } },
|
||||
{ "x": 60, "y": -51, "item": { "item": "minecraft:diamond_block" } },
|
||||
{ "x": 80, "y": -68, "item": { "item": "minecraft:diamond_block" } }
|
||||
{
|
||||
"structure": [
|
||||
{ "state": "minecraft:diamond_block", "min": [0,0,0], "max": [2,0,2] },
|
||||
{ "state": "minecraft:diamond_block", "pos": [1,1,1] },
|
||||
{ "state": "minecraft:end_rod[facing=up]", "pos": [1,2,1] },
|
||||
{ "state": "minecraft:end_rod[facing=down]", "pos": [1,4,1] },
|
||||
{ "state": "minecraft:diamond_block", "pos": [1,5,1] },
|
||||
{ "state": "minecraft:diamond_block", "min": [0,6,0], "max": [2,6,2] }
|
||||
]
|
||||
},
|
||||
{ "x": 60, "y": -34, "item": { "item": "unicopia:crystal_heart" } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "gui.unicopia.spellbook.chapter.artefacts.altar.title",
|
||||
"level": 0,
|
||||
"elements": [
|
||||
"gui.unicopia.spellbook.chapter.artefacts.altar.1.body",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.altar.2.body",
|
||||
{
|
||||
"ingredients": [
|
||||
{ "count": 40, "item": "minecraft:obsidian" },
|
||||
{ "count": 1, "item": "minecraft:soul_sand" },
|
||||
{ "count": 1, "item": "minecraft:lodestone" },
|
||||
{ "count": 1, "item": "unicopia:spellbook" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "gui.unicopia.spellbook.chapter.artefacts.altar.title",
|
||||
"level": 0,
|
||||
"elements": [
|
||||
{
|
||||
"structure": [
|
||||
{ "state": "minecraft:soul_sand", "min": [0,0,0], "max": [8,0,8] },
|
||||
{ "state": "minecraft:obsidian", "min": [3,1,3], "max": [5,1,5] },
|
||||
{ "state": "minecraft:soul_sand", "pos": [4,1,4] },
|
||||
{ "state": "minecraft:lodestone", "pos": [4,1,6] },
|
||||
{ "state": "minecraft:obsidian", "min": [0,1,2], "max": [0,4,2] },
|
||||
{ "state": "minecraft:obsidian", "min": [0,1,6], "max": [0,4,6] },
|
||||
{ "state": "minecraft:obsidian", "min": [2,1,0], "max": [2,4,0] },
|
||||
{ "state": "minecraft:obsidian", "min": [6,1,0], "max": [6,4,0] },
|
||||
{ "state": "minecraft:obsidian", "min": [8,1,2], "max": [8,4,2] },
|
||||
{ "state": "minecraft:obsidian", "min": [8,1,6], "max": [8,4,6] },
|
||||
{ "state": "minecraft:obsidian", "min": [2,1,8], "max": [2,4,8] },
|
||||
{ "state": "minecraft:obsidian", "min": [6,1,8], "max": [6,4,8] }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "item.unicopia.spectral_clock",
|
||||
"level": 0,
|
||||
"elements": [
|
||||
{ "item": { "item": "unicopia:spectral_clock" } },
|
||||
"gui.unicopia.spellbook.chapter.artefacts.status.unconfirmed",
|
||||
"gui.unicopia.spellbook.chapter.artefacts.spectral_clock.1.body",
|
||||
{
|
||||
"translate": "gui.unicopia.spellbook.chapter.artefacts.altar.3.body",
|
||||
"color": "red"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "gui.unicopia.spellbook.chapter.artefacts.spectral_clock.title",
|
||||
"level": 0,
|
||||
"elements": [
|
||||
"gui.unicopia.spellbook.chapter.artefacts.spectral_clock.2.body",
|
||||
"gui.unicopia.spellbook.author1.sign_off",
|
||||
"gui.unicopia.spellbook.author1.name"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -97,22 +97,6 @@
|
|||
]
|
||||
},
|
||||
{ },
|
||||
{
|
||||
"title": "",
|
||||
"level": 6,
|
||||
"elements": [
|
||||
"gui.unicopia.spellbook.chapter.ice.p7.3.body",
|
||||
"gui.unicopia.spellbook.chapter.ice.p7.4.body",
|
||||
"gui.unicopia.spellbook.recipe.requires",
|
||||
{
|
||||
"ingredients": [
|
||||
{ "count": 15, "trait": "unicopia:ice" }
|
||||
]
|
||||
},
|
||||
{ "x": 115, "y": -10, "width": 32, "height": 32, "texture": "minecraft:textures/item/snowball.png" },
|
||||
{ "x": 115, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "gui.unicopia.spellbook.chapter.ice.p8.title",
|
||||
"level": 4,
|
||||
|
@ -123,6 +107,7 @@
|
|||
"gui.unicopia.spellbook.author1.name"
|
||||
]
|
||||
},
|
||||
{ },
|
||||
{
|
||||
"title": "spell.unicopia.light",
|
||||
"level": 5,
|
||||
|
|
Loading…
Reference in a new issue