fix conflicts

This commit is contained in:
petrak@ 2022-11-21 12:20:56 -06:00
commit aecf017b06
20 changed files with 396 additions and 205 deletions

0
Common/logs/latest.log Normal file
View file

View file

@ -34,7 +34,7 @@ public class PatternRegistry {
new ConcurrentHashMap<>();
public static void mapPattern(HexPattern pattern, ResourceLocation id,
Action action) throws RegisterPatternException {
Action action) throws RegisterPatternException {
mapPattern(pattern, id, action, false);
}
@ -42,7 +42,7 @@ public class PatternRegistry {
* Associate a given angle signature with a SpellOperator.
*/
public static void mapPattern(HexPattern pattern, ResourceLocation id, Action action,
boolean isPerWorld) throws RegisterPatternException {
boolean isPerWorld) throws RegisterPatternException {
if (actionLookup.containsKey(id)) {
throw new RegisterPatternException("The operator with id `%s` was already registered to: %s", id,
actionLookup.get(id));
@ -83,7 +83,7 @@ public class PatternRegistry {
* Internal use only.
*/
public static Pair<Action, ResourceLocation> matchPatternAndID(HexPattern pat,
ServerLevel overworld) throws MishapInvalidPattern {
ServerLevel overworld) throws MishapInvalidPattern {
// Pipeline:
// patterns are registered here every time the game boots
// when we try to look
@ -232,13 +232,13 @@ public class PatternRegistry {
/**
* Maps angle sigs to resource locations and their preferred start dir so we can look them up in the main registry
* Save this on the world in case the random algorithm changes.
*/
public static class Save extends SavedData {
private static final String TAG_OP_ID = "op_id";
private static final String TAG_START_DIR = "start_dir";
// TODO: this is slightly weird that we save it *on* the world
// might it be better to recalculate it every time the world loads?
// Maps hex signatures to (op ids, canonical start dir)
private Map<String, Pair<ResourceLocation, HexDir>> lookup;
private boolean missingEntries;
@ -279,7 +279,7 @@ public class PatternRegistry {
public CompoundTag save(CompoundTag tag) {
this.lookup.forEach((sig, rhs) -> {
var entry = new CompoundTag();
entry.putString(TAG_OP_ID, sig.toString());
entry.putString(TAG_OP_ID, rhs.getFirst().toString());
entry.putInt(TAG_START_DIR, rhs.getSecond().ordinal());
tag.put(sig, entry);
});

View file

@ -86,12 +86,18 @@ public interface IotaHolderItem {
return HexIotaTypes.getColor(tag);
}
/**
* Write {@code null} to indicate erasing
*/
boolean canWrite(ItemStack stack, @Nullable Iota iota);
/**
* Write {@code null} to indicate erasing
*/
void writeDatum(ItemStack stack, @Nullable Iota iota);
static void appendHoverText(IotaHolderItem self, ItemStack stack, List<Component> components,
TooltipFlag flag) {
TooltipFlag flag) {
var datumTag = self.readIotaTag(stack);
if (datumTag != null) {
var cmp = HexIotaTypes.getDisplay(datumTag);

View file

@ -8,7 +8,6 @@ import at.petrak.hexcasting.api.utils.TAU
import at.petrak.hexcasting.api.utils.getValue
import at.petrak.hexcasting.api.utils.setValue
import at.petrak.hexcasting.api.utils.weakMapped
import at.petrak.hexcasting.client.gui.GuiSpellcasting
import at.petrak.hexcasting.common.recipe.ingredient.brainsweep.BrainsweepIngredient
import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.DefaultVertexFormat
@ -31,19 +30,25 @@ import net.minecraft.world.entity.npc.VillagerProfession
import net.minecraft.world.entity.npc.VillagerType
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.levelgen.XoroshiroRandomSource
import net.minecraft.world.level.levelgen.synth.PerlinNoise
import net.minecraft.world.level.levelgen.SingleThreadedRandomSource
import net.minecraft.world.level.levelgen.synth.SimplexNoise
import net.minecraft.world.phys.Vec2
import kotlin.math.abs
import kotlin.math.min
import kotlin.math.roundToInt
import kotlin.math.sin
/**
* Source of perlin noise
*/
val NOISE: PerlinNoise = PerlinNoise.create(XoroshiroRandomSource(9001L), listOf(0, 1, 2, 3, 4))
val NOISE: SimplexNoise = SimplexNoise(SingleThreadedRandomSource(9001L))
val CAP_THETA: Float = 18f
// see the test; perlin noise seems to output almost exclusively between -0.5 and 0.5
// i could probably impl this "properly" with some kind of exponent but it's faster and easier to divide
fun getNoise(x: Double, y: Double, z: Double): Double =
NOISE.getValue(x * 0.6, y * 0.6, z * 0.6) / 2.0
// how many degrees are between each triangle on the smooth caps of the lines
const val CAP_THETA = 180f / 10f
const val DEFAULT_READABILITY_OFFSET = 0.2f
const val DEFAULT_LAST_SEGMENT_LEN_PROP = 0.8f
/**
* Draw a sequence of linePoints spanning the given points.
@ -51,12 +56,12 @@ val CAP_THETA: Float = 18f
* Please make sure to enable the right asinine shaders; see [GuiSpellcasting]
*/
fun drawLineSeq(
mat: Matrix4f,
points: List<Vec2>,
width: Float,
z: Float,
tail: Int,
head: Int,
mat: Matrix4f,
points: List<Vec2>,
width: Float,
z: Float,
tail: Int,
head: Int,
) {
if (points.size <= 1) return
@ -79,24 +84,27 @@ fun drawLineSeq(
val n = points.size
val joinAngles = FloatArray(n)
val joinOffsets = FloatArray(n)
for (i in 2..n - 1) {
for (i in 2 until n) {
val p0 = points[i - 2];
val p1 = points[i - 1];
val p2 = points[i];
val prev = p1.add(p0.negated())
val next = p2.add(p1.negated())
val angle =
Mth.atan2((prev.x * next.y - prev.y * next.x).toDouble(), (prev.x * next.x + prev.y * next.y).toDouble())
.toFloat()
Mth.atan2((prev.x * next.y - prev.y * next.x).toDouble(), (prev.x * next.x + prev.y * next.y).toDouble())
.toFloat()
joinAngles[i - 1] = angle
val clamp = Math.min(prev.length(), next.length()) / (width * 0.5f);
joinOffsets[i - 1] = Mth.clamp(Mth.sin(angle) / (1 + Mth.cos(angle)), -clamp, clamp)
}
fun vertex(color: BlockPos, pos: Vec2) =
buf.vertex(mat, pos.x, pos.y, z).color(color.x, color.y, color.z, a).endVertex()
for ((i, pair) in points.zipWithNext().withIndex()) {
val (p1, p2) = pair
buf.vertex(mat, pos.x, pos.y, z).color(color.x, color.y, color.z, a).endVertex()
buf.begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION_COLOR)
for (i in 0 until points.size - 1) {
val p1 = points[i]
val p2 = points[i + 1]
// https://github.com/not-fl3/macroquad/blob/master/src/shapes.rs#L163
// GuiComponent::innerFill line 52
// fedor have useful variable names challenge (99% can't beat)
@ -104,22 +112,38 @@ fun drawLineSeq(
val normal = Vec2(-tangent.y, tangent.x)
fun color(time: Float): BlockPos =
BlockPos(Mth.lerp(time, r1, r2).toInt(), Mth.lerp(time, g1, g2).toInt(), Mth.lerp(time, b1, b2).toInt())
BlockPos(Mth.lerp(time, r1, r2).toInt(), Mth.lerp(time, g1, g2).toInt(), Mth.lerp(time, b1, b2).toInt())
val color1 = color(i.toFloat() / n)
val color2 = color((i + 1f) / n)
val jlow = joinOffsets[i]
val jhigh = joinOffsets[i + 1]
buf.begin(VertexFormat.Mode.TRIANGLE_FAN, DefaultVertexFormat.POSITION_COLOR)
vertex(color1, p1.add(tangent.scale(Math.max(0f, jlow))).add(normal))
// Draw the line segment as a hexagon, sort of
// I can't imagine what the hell alwinfy is up to but this is implementing what TRIANGLE_FAN does
// using normal triangles so we can send the entire segment to the buffer at once
val p1Down = p1.add(tangent.scale(Math.max(0f, jlow))).add(normal)
val p1Up = p1.add(tangent.scale(Math.max(0f, -jlow))).add(normal.negated())
val p2Down = p2.add(tangent.scale(Math.max(0f, jhigh)).negated()).add(normal)
val p2Up = p2.add(tangent.scale(Math.max(0f, -jhigh)).negated()).add(normal.negated())
vertex(color1, p1Down)
vertex(color1, p1)
vertex(color1, p1.add(tangent.scale(Math.max(0f, -jlow))).add(normal.negated()))
vertex(color2, p2.add(tangent.scale(Math.max(0f, -jhigh)).negated()).add(normal.negated()))
vertex(color1, p1Up)
vertex(color1, p1Down)
vertex(color1, p1Up)
vertex(color2, p2Up)
vertex(color1, p1Down)
vertex(color2, p2Up)
vertex(color2, p2)
vertex(color2, p2.add(tangent.scale(Math.max(0f, jhigh)).negated()).add(normal))
tess.end()
vertex(color1, p1Down)
vertex(color2, p2)
vertex(color2, p2Down)
if (i > 0) {
// Draw the connector to the next line segment
val sangle = joinAngles[i]
val angle = Math.abs(sangle)
val rnormal = normal.negated()
@ -127,22 +151,35 @@ fun drawLineSeq(
if (joinSteps < 1) {
continue
}
buf.begin(VertexFormat.Mode.TRIANGLE_FAN, DefaultVertexFormat.POSITION_COLOR)
buf.vertex(mat, p1.x, p1.y, z).color(color1.x, color1.y, color1.z, a).endVertex()
if (sangle < 0) {
for (j in 0..joinSteps) {
var prevVert = Vec2(p1.x - rnormal.x, p1.y - rnormal.y)
for (j in 1..joinSteps) {
val fan = rotate(rnormal, -sangle * (j.toFloat() / joinSteps))
buf.vertex(mat, p1.x - fan.x, p1.y - fan.y, z).color(color1.x, color1.y, color1.z, a).endVertex()
val fanShift = Vec2(p1.x - fan.x, p1.y - fan.y)
vertex(color1, p1)
vertex(color1, prevVert)
vertex(color1, fanShift)
prevVert = fanShift
}
} else {
for (j in joinSteps downTo 0) {
val startFan = rotate(normal, -sangle)
var prevVert = Vec2(p1.x - startFan.x, p1.y - startFan.y)
for (j in joinSteps - 1 downTo 0) {
val fan = rotate(normal, -sangle * (j.toFloat() / joinSteps))
buf.vertex(mat, p1.x - fan.x, p1.y - fan.y, z).color(color1.x, color1.y, color1.z, a).endVertex()
val fanShift = Vec2(p1.x - fan.x, p1.y - fan.y)
vertex(color1, p1)
vertex(color1, prevVert)
vertex(color1, fanShift)
prevVert = fanShift
}
}
tess.end()
}
}
tess.end()
fun drawCaps(color: BlockPos, point: Vec2, prev: Vec2) {
val tangent = point.add(prev.negated()).normalized().scale(0.5f * width)
val normal = Vec2(-tangent.y, tangent.x)
@ -166,19 +203,22 @@ fun rotate(vec: Vec2, theta: Float): Vec2 {
}
/**
* * Draw a hex pattern from the given list of non-zappy points (as in, do the *style* of drawing it,
* * you have to do the conversion yourself.)
* */
* Draw a hex pattern from the given list of non-zappy points (as in, do the *style* of drawing it,
* you have to do the conversion yourself.)
*/
fun drawPatternFromPoints(
mat: Matrix4f,
points: List<Vec2>,
dupIndices: Set<Int>?,
drawLast: Boolean,
tail: Int,
head: Int,
flowIrregular: Float,
mat: Matrix4f,
points: List<Vec2>,
dupIndices: Set<Int>?,
drawLast: Boolean,
tail: Int,
head: Int,
flowIrregular: Float,
readabilityOffset: Float,
lastSegmentLenProportion: Float,
seed: Double
) {
val zappyPts = makeZappy(points, dupIndices, 10f, 2.5f, 0.1f, flowIrregular)
val zappyPts = makeZappy(points, dupIndices, 10, 2.5f, 0.1f, flowIrregular, readabilityOffset, lastSegmentLenProportion, seed)
val nodes = if (drawLast) {
points
} else {
@ -188,45 +228,36 @@ fun drawPatternFromPoints(
drawLineSeq(mat, zappyPts, 2f, 1f, screenCol(tail), screenCol(head))
for (node in nodes) {
drawSpot(
mat,
node,
2f,
dodge(FastColor.ARGB32.red(head)) / 255f,
dodge(FastColor.ARGB32.green(head)) / 255f,
dodge(FastColor.ARGB32.blue(head)) / 255f,
FastColor.ARGB32.alpha(head) / 255f
mat,
node,
2f,
dodge(FastColor.ARGB32.red(head)) / 255f,
dodge(FastColor.ARGB32.green(head)) / 255f,
dodge(FastColor.ARGB32.blue(head)) / 255f,
FastColor.ARGB32.alpha(head) / 255f
);
}
}
fun makeZappy(
points: List<Vec2>,
dupIndices: Set<Int>?,
hops: Float,
variance: Float,
speed: Float,
flowIrregular: Float
) =
makeZappy(points, dupIndices, hops.toInt(), variance, speed, flowIrregular, 0.2f)
/**
* Split up a sequence of linePoints with a lightning effect
* @param hops: rough number of points to subdivide each segment into
* @param speed: rate at which the lightning effect should move/shake/etc
*/
fun makeZappy(
barePoints: List<Vec2>, dupIndices: Set<Int>?, hops: Int, variance: Float, speed: Float, flowIrregular: Float,
readabilityOffset: Float
barePoints: List<Vec2>, dupIndices: Set<Int>?, hops: Int, variance: Float, speed: Float, flowIrregular: Float,
readabilityOffset: Float, lastSegmentLenProportion: Float, seed: Double
): List<Vec2> {
// Nothing in, nothing out
if (barePoints.isEmpty()) {
return emptyList()
}
fun zappify(points: List<Vec2>): List<Vec2> {
fun zappify(points: List<Vec2>, truncateLast: Boolean): List<Vec2> {
val scaleVariance = { it: Double -> 1.0.coerceAtMost(8 * (0.5 - abs(0.5 - it))) }
val zSeed = ClientTickCounter.getTotal().toDouble() * speed
// Create our output list of zap points
val zappyPts = mutableListOf(points[0])
val zappyPts = ArrayList<Vec2>(points.size * hops)
zappyPts.add(points[0])
// For each segment in the original...
for ((i, pair) in points.zipWithNext().withIndex()) {
val (src, target) = pair
@ -236,30 +267,40 @@ fun makeZappy(
// Compute how big the radius of variance should be
val maxVariance = hopDist * variance
for (j in 1..hops) {
// for a list of length n, there will be n-1 pairs,
// and so the last index will be (n-1)-1
val maxJ = if (truncateLast && i == points.size - 2) {
(lastSegmentLenProportion * hops.toFloat()).roundToInt()
} else hops
for (j in 1..maxJ) {
val progress = j.toDouble() / (hops + 1)
// Add the next hop...
val pos = src.add(delta.scale(progress.toFloat()))
// as well as some random variance...
// (We use i, j (segment #, subsegment #) as seeds for the Perlin noise,
// and zSeed (i.e. time elapsed) to perturb the shape gradually over time)
val minorPerturb = NOISE.getValue(i.toDouble(), j.toDouble(), sin(zSeed)) * flowIrregular
val theta = (3 * NOISE.getValue(
i.toDouble() + j.toDouble() / (hops + 1) + minorPerturb - zSeed,
1337.0,
0.0
val minorPerturb = getNoise(i.toDouble(), j.toDouble(), sin(zSeed)) * flowIrregular
val theta = (3 * getNoise(
i + progress + minorPerturb - zSeed,
1337.0,
seed
) * TAU).toFloat()
val r = (NOISE.getValue(
i.toDouble() + j.toDouble() / (hops + 1) - zSeed,
69420.0,
0.0
val r = (getNoise(
i + progress - zSeed,
69420.0,
seed
) * maxVariance * scaleVariance(progress)).toFloat()
val randomHop = Vec2(r * Mth.cos(theta), r * Mth.sin(theta))
// Then record the new location.
zappyPts.add(pos.add(randomHop))
if (j == hops) {
// Finally, we hit the destination, add that too
// but we might not hit the destination if we want to stop short
zappyPts.add(target)
}
}
// Finally, we hit the destination, add that too
zappyPts.add(target)
}
return zappyPts
}
@ -277,16 +318,16 @@ fun makeZappy(
}
if (i == barePoints.size - 2) {
daisyChain.add(tail)
points.addAll(zappify(daisyChain))
points.addAll(zappify(daisyChain, true))
} else if (dupIndices.contains(i + 1)) {
daisyChain.add(tail.add(tangent.negated()))
points.addAll(zappify(daisyChain))
points.addAll(zappify(daisyChain, false))
daisyChain.clear()
}
}
points
} else {
zappify(barePoints)
zappify(barePoints, true)
}
}
@ -333,10 +374,10 @@ fun drawSpot(mat: Matrix4f, point: Vec2, radius: Float, r: Float, g: Float, b: F
fun screenCol(n: Int): Int {
return FastColor.ARGB32.color(
FastColor.ARGB32.alpha(n),
screen(FastColor.ARGB32.red(n)),
screen(FastColor.ARGB32.green(n)),
screen(FastColor.ARGB32.blue(n)),
FastColor.ARGB32.alpha(n),
screen(FastColor.ARGB32.red(n)),
screen(FastColor.ARGB32.green(n)),
screen(FastColor.ARGB32.blue(n)),
)
}
@ -363,7 +404,7 @@ fun getCenteredPattern(pattern: HexPattern, width: Float, height: Float, minSize
}
}
val scale =
min(minSize, min(width / 3f / maxDx, height / 3f / maxDy))
min(minSize, min(width / 3f / maxDx, height / 3f / maxDy))
val com2: Vec2 = pattern.getCenter(scale)
val lines2: List<Vec2> = pattern.toLines(scale, com2.negated())
return scale to lines2
@ -388,9 +429,9 @@ private var villager: Villager? by weakMapped(Villager::level)
fun prepareVillagerForRendering(ingredient: BrainsweepIngredient, level: Level): Villager {
val minLevel: Int = ingredient.minLevel()
val profession: VillagerProfession = Registry.VILLAGER_PROFESSION.getOptional(ingredient.profession())
.orElse(VillagerProfession.NONE)
.orElse(VillagerProfession.NONE)
val biome: VillagerType = Registry.VILLAGER_TYPE.getOptional(ingredient.biome())
.orElse(VillagerType.PLAINS)
.orElse(VillagerType.PLAINS)
val instantiatedVillager = villager ?: run {
val newVillager = Villager(EntityType.VILLAGER, level)
@ -399,18 +440,18 @@ fun prepareVillagerForRendering(ingredient: BrainsweepIngredient, level: Level):
}
instantiatedVillager.villagerData = instantiatedVillager.villagerData
.setProfession(profession)
.setType(biome)
.setLevel(minLevel)
.setProfession(profession)
.setType(biome)
.setLevel(minLevel)
return instantiatedVillager
}
@JvmOverloads
fun renderEntity(
ms: PoseStack, entity: Entity, world: Level, x: Float, y: Float, rotation: Float,
renderScale: Float, offset: Float,
bufferTransformer: (MultiBufferSource) -> MultiBufferSource = { it -> it }
ms: PoseStack, entity: Entity, world: Level, x: Float, y: Float, rotation: Float,
renderScale: Float, offset: Float,
bufferTransformer: (MultiBufferSource) -> MultiBufferSource = { it -> it }
) {
entity.level = world
ms.pushPose()
@ -432,23 +473,23 @@ fun renderEntity(
* Make sure you have the `PositionColorShader` set
*/
fun renderQuad(
ps: PoseStack, x: Float, y: Float, w: Float, h: Float, color: Int
ps: PoseStack, x: Float, y: Float, w: Float, h: Float, color: Int
) {
val mat = ps.last().pose()
val tess = Tesselator.getInstance()
val buf = tess.builder
buf.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR)
buf.vertex(mat, x, y, 0f)
.color(color)
.endVertex()
.color(color)
.endVertex()
buf.vertex(mat, x, y + h, 0f)
.color(color)
.endVertex()
.color(color)
.endVertex()
buf.vertex(mat, x + w, y + h, 0f)
.color(color)
.endVertex()
.color(color)
.endVertex()
buf.vertex(mat, x + w, y, 0f)
.color(color)
.endVertex()
.color(color)
.endVertex()
tess.end()
}

View file

@ -22,7 +22,7 @@ public class BlockEntityAkashicBookshelfRenderer implements BlockEntityRenderer<
@Override
public void render(BlockEntityAkashicBookshelf tile, float pPartialTick, PoseStack ps,
MultiBufferSource buffer, int light, int overlay) {
MultiBufferSource buffer, int light, int overlay) {
HexPattern pattern = tile.getPattern();
if (pattern == null) {
return;
@ -73,7 +73,9 @@ public class BlockEntityAkashicBookshelfRenderer implements BlockEntityRenderer<
lines2.set(j, new Vec2(-v.x, v.y));
}
var zappy = RenderLib.makeZappy(lines2, RenderLib.findDupIndices(pattern.positions()), 10f, 0.5f, 0f, 0f);
var stupidHash = tile.getBlockPos().hashCode();
var zappy = RenderLib.makeZappy(lines2, RenderLib.findDupIndices(pattern.positions()), 10, 0.5f, 0f, 0f, 0f,
1f, stupidHash);
int outer = 0xff_d2c8c8;
int inner = 0xc8_322b33;

View file

@ -22,7 +22,7 @@ public class BlockEntitySlateRenderer implements BlockEntityRenderer<BlockEntity
@Override
public void render(BlockEntitySlate tile, float pPartialTick, PoseStack ps,
MultiBufferSource buffer, int light, int overlay) {
MultiBufferSource buffer, int light, int overlay) {
if (tile.pattern == null) {
return;
}
@ -84,7 +84,11 @@ public class BlockEntitySlateRenderer implements BlockEntityRenderer<BlockEntity
}
var isLit = bs.getValue(BlockSlate.ENERGIZED);
var zappy = RenderLib.makeZappy(lines2, RenderLib.findDupIndices(tile.pattern.positions()), 10f, isLit ? 2.5f : 0.5f, isLit ? 0.1f : 0f, 0.2f);
var variance = isLit ? 2.5f : 0.5f;
var speed = isLit ? 0.1f : 0f;
var stupidHash = tile.getBlockPos().hashCode();
var zappy = RenderLib.makeZappy(lines2, RenderLib.findDupIndices(tile.pattern.positions()),
10, variance, speed, 0.2f, 0f, 1f, stupidHash);
int outer = isLit ? 0xff_64c8ff : 0xff_d2c8c8;
int inner = isLit ? RenderLib.screenCol(outer) : 0xc8_322b33;

View file

@ -1,5 +1,6 @@
package at.petrak.hexcasting.client.entity;
import at.petrak.hexcasting.client.RenderLib;
import at.petrak.hexcasting.common.entities.EntityWallScroll;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
@ -38,7 +39,7 @@ public class WallScrollRenderer extends EntityRenderer<EntityWallScroll> {
// I do as the PaintingRenderer guides
@Override
public void render(EntityWallScroll wallScroll, float yaw, float partialTicks, PoseStack ps,
MultiBufferSource bufSource, int packedLight) {
MultiBufferSource bufSource, int packedLight) {
RenderSystem.setShader(GameRenderer::getPositionTexShader);
@ -157,8 +158,8 @@ public class WallScrollRenderer extends EntityRenderer<EntityWallScroll> {
}
private static void vertex(Matrix4f mat, Matrix3f normal, int light, VertexConsumer verts, float x, float y,
float z, float u,
float v, float nx, float ny, float nz) {
float z, float u,
float v, float nx, float ny, float nz) {
verts.vertex(mat, x, y, z)
.color(0xffffffff)
.uv(u, v).overlayCoords(OverlayTexture.NO_OVERLAY).uv2(light)
@ -166,82 +167,136 @@ public class WallScrollRenderer extends EntityRenderer<EntityWallScroll> {
.endVertex();
}
private static void vertexCol(Matrix4f mat, Matrix3f normal, int light, VertexConsumer verts, int col, float x,
float y) {
verts.vertex(mat, -x, y, 0)
private static void vertexCol(Matrix4f mat, Matrix3f normal, int light, VertexConsumer verts, int col, Vec2 pos) {
verts.vertex(mat, -pos.x, pos.y, 0)
.color(col)
.uv(0, 0).overlayCoords(OverlayTexture.NO_OVERLAY).uv2(light)
.normal(normal, 0, 0, 1)
.endVertex();
}
private static void theCoolerDrawLineSeq(Matrix4f mat, Matrix3f normal, int light, VertexConsumer verts,
List<Vec2> points, float width, int color
private static void theCoolerDrawLineSeq(Matrix4f mat, Matrix3f normalMat, int light, VertexConsumer verts,
List<Vec2> points, float width, int color
) {
if (points.size() <= 1) {
return;
}
float prevXHi, prevYHi, prevXLo, prevYLo;
{
var p1 = points.get(0);
var p2 = points.get(1);
var dx = p2.x - p1.x;
var dy = p2.y - p1.y;
var nx = -dy;
var ny = dx;
var tlen = Mth.sqrt(nx * nx + ny * ny) / (width * 0.5f);
var tx = nx / tlen;
var ty = ny / tlen;
prevXHi = p1.x - tx;
prevYHi = p1.y - ty;
prevXLo = p1.x + tx;
prevYLo = p1.y + ty;
// TODO: abstract some of this out with RenderLib to stop WET code
var joinAngles = new float[points.size()];
var joinOffsets = new float[points.size()];
for (int i = 2; i < points.size(); i++) {
var p0 = points.get(i - 2);
var p1 = points.get(i - 1);
var p2 = points.get(i);
var prev = p1.add(p0.negated());
var next = p2.add(p1.negated());
var angle = (float) Mth.atan2(
prev.x * next.y - prev.y * next.x,
prev.x * next.x + prev.y * next.y);
joinAngles[i - 1] = angle;
var clamp = Math.min(prev.length(), next.length()) / (width * 0.5f);
joinOffsets[i - 1] = Mth.clamp(Mth.sin(angle) / (1 + Mth.cos(angle)), -clamp, clamp);
}
for (var i = 0; i < points.size() - 1; i++) {
var p1 = points.get(i);
var p2 = points.get(i + 1);
var dx = p2.x - p1.x;
var dy = p2.y - p1.y;
var nx = -dy;
var ny = dx;
var tlen = Mth.sqrt(nx * nx + ny * ny) / (width * 0.5f);
var tx = nx / tlen;
var ty = ny / tlen;
var tangent = p2.add(p1.negated()).normalized().scale(width * 0.5f);
var normal = new Vec2(-tangent.y, tangent.x);
var xHi = p2.x - tx;
var yHi = p2.y - ty;
var xLo = p2.x + tx;
var yLo = p2.y + ty;
vertexCol(mat, normal, light, verts, color, prevXHi, prevYHi);
vertexCol(mat, normal, light, verts, color, prevXLo, prevYLo);
vertexCol(mat, normal, light, verts, color, xLo, yLo);
vertexCol(mat, normal, light, verts, color, xHi, yHi);
var jlow = joinOffsets[i];
var jhigh = joinOffsets[i + 1];
prevXHi = xHi;
prevYHi = yHi;
prevXLo = xLo;
prevYLo = yLo;
var p1Down = p1.add(tangent.scale(Math.max(0f, jlow))).add(normal);
var p1Up = p1.add(tangent.scale(Math.max(0f, -jlow))).add(normal.negated());
var p2Down = p2.add(tangent.scale(Math.max(0f, jhigh)).negated()).add(normal);
var p2Up = p2.add(tangent.scale(Math.max(0f, -jhigh)).negated()).add(normal.negated());
// Draw the chamfer hexagon as two trapezoids
// the points are in different orders to keep clockwise
vertexCol(mat, normalMat, light, verts, color, p1);
vertexCol(mat, normalMat, light, verts, color, p2);
vertexCol(mat, normalMat, light, verts, color, p2Up);
vertexCol(mat, normalMat, light, verts, color, p1Up);
vertexCol(mat, normalMat, light, verts, color, p1);
vertexCol(mat, normalMat, light, verts, color, p1Down);
vertexCol(mat, normalMat, light, verts, color, p2Down);
vertexCol(mat, normalMat, light, verts, color, p2);
if (i > 0) {
var sangle = joinAngles[i];
var angle = Math.abs(sangle);
var rnormal = normal.negated();
var joinSteps = Mth.ceil(angle * 180 / (RenderLib.CAP_THETA * Mth.PI));
if (joinSteps < 1) continue;
if (sangle < 0) {
var prevVert = new Vec2(p1.x - rnormal.x, p1.y - rnormal.y);
for (var j = 1; j <= joinSteps; j++) {
var fan = RenderLib.rotate(rnormal, -sangle * ((float) j / joinSteps));
var fanShift = new Vec2(p1.x - fan.x, p1.y - fan.y);
vertexCol(mat, normalMat, light, verts, color, p1);
vertexCol(mat, normalMat, light, verts, color, p1);
vertexCol(mat, normalMat, light, verts, color, fanShift);
vertexCol(mat, normalMat, light, verts, color, prevVert);
prevVert = fanShift;
}
} else {
var startFan = RenderLib.rotate(normal, -sangle);
var prevVert = new Vec2(p1.x - startFan.x, p1.y - startFan.y);
for (var j = joinSteps - 1; j >= 0; j--) {
var fan = RenderLib.rotate(normal, -sangle * ((float) j / joinSteps));
var fanShift = new Vec2(p1.x - fan.x, p1.y - fan.y);
vertexCol(mat, normalMat, light, verts, color, p1);
vertexCol(mat, normalMat, light, verts, color, p1);
vertexCol(mat, normalMat, light, verts, color, fanShift);
vertexCol(mat, normalMat, light, verts, color, prevVert);
prevVert = fanShift;
}
}
}
}
for (var pair : new Vec2[][]{
{points.get(0), points.get(1)},
{points.get(points.size() - 1), points.get(points.size() - 2)}
}) {
var point = pair[0];
var prev = pair[1];
var tangent = point.add(prev.negated()).normalized().scale(0.5f * width);
var normal = new Vec2(-tangent.y, tangent.x);
var joinSteps = Mth.ceil(180f / RenderLib.CAP_THETA);
for (int j = joinSteps; j > 0; j--) {
var fan0 = RenderLib.rotate(normal, -Mth.PI * ((float) j / joinSteps));
var fan1 = RenderLib.rotate(normal, -Mth.PI * ((float) (j - 1) / joinSteps));
vertexCol(mat, normalMat, light, verts, color, point);
vertexCol(mat, normalMat, light, verts, color, point);
vertexCol(mat, normalMat, light, verts, color, point.add(fan1));
vertexCol(mat, normalMat, light, verts, color, point.add(fan0));
}
}
}
private static void theCoolerDrawSpot(Matrix4f mat, Matrix3f normal, int light, VertexConsumer verts,
Vec2 point, float radius, int color) {
Vec2 point, float radius, int color) {
var fracOfCircle = 6;
for (int i = 0; i < fracOfCircle; i++) {
// We do need rects, irritatingly
// so we do fake triangles
vertexCol(mat, normal, light, verts, color, point.x, point.y);
vertexCol(mat, normal, light, verts, color, point.x, point.y);
vertexCol(mat, normal, light, verts, color, point);
vertexCol(mat, normal, light, verts, color, point);
for (int j = 0; j <= 1; j++) {
var theta = (i - j) / (float) fracOfCircle * Mth.TWO_PI;
var rx = Mth.cos(theta) * radius + point.x;
var ry = Mth.sin(theta) * radius + point.y;
vertexCol(mat, normal, light, verts, color, rx, ry);
vertexCol(mat, normal, light, verts, color, new Vec2(rx, ry));
}
}
}

View file

@ -234,6 +234,7 @@ class GuiSpellcasting constructor(
// Well, we never managed to get anything on the stack this go-around.
this.drawState = PatternDrawState.BetweenPatterns
}
is PatternDrawState.Drawing -> {
val (start, _, pat) = this.drawState as PatternDrawState.Drawing
this.drawState = PatternDrawState.BetweenPatterns
@ -325,7 +326,8 @@ class GuiSpellcasting constructor(
}
RenderSystem.defaultBlendFunc()
for ((pat, origin, valid) in this.patterns) {
for ((idx, elts) in this.patterns.withIndex()) {
val (pat, origin, valid) = elts
drawPatternFromPoints(
mat,
pat.toLines(
@ -336,7 +338,10 @@ class GuiSpellcasting constructor(
true,
valid.color or (0xC8 shl 24),
valid.fadeColor or (0xC8 shl 24),
if (valid.success) 0.2f else 0.9f
if (valid.success) 0.2f else 0.9f,
DEFAULT_READABILITY_OFFSET,
1f,
idx.toDouble()
)
}
@ -358,7 +363,17 @@ class GuiSpellcasting constructor(
}
points.add(mousePos)
drawPatternFromPoints(mat, points, dupIndices, false, 0xff_64c8ff_u.toInt(), 0xff_fecbe6_u.toInt(), 0.1f)
// Use the size of the patterns as the seed so that way when this one is added the zappies don't jump
drawPatternFromPoints(mat,
points,
dupIndices,
false,
0xff_64c8ff_u.toInt(),
0xff_fecbe6_u.toInt(),
0.1f,
DEFAULT_READABILITY_OFFSET,
1f,
this.patterns.size.toDouble())
}
RenderSystem.enableDepthTest()

View file

@ -32,8 +32,7 @@ public class PatternTooltipComponent implements ClientTooltipComponent {
public static final ResourceLocation ANCIENT_BG = modLoc("textures/gui/scroll_ancient.png");
public static final ResourceLocation SLATE_BG = modLoc("textures/gui/slate.png");
private static final float RENDER_SIZE = 72f;
private static final float TEXTURE_SIZE = 48f;
private static final float RENDER_SIZE = 128f;
private final HexPattern pattern;
private final List<Vec2> zappyPoints;
@ -45,10 +44,13 @@ public class PatternTooltipComponent implements ClientTooltipComponent {
this.pattern = tt.pattern();
this.background = tt.background();
var pair = RenderLib.getCenteredPattern(pattern, RENDER_SIZE, RENDER_SIZE, 8f);
var pair = RenderLib.getCenteredPattern(pattern, RENDER_SIZE, RENDER_SIZE, 16f);
this.scale = pair.getFirst();
var dots = pair.getSecond();
this.zappyPoints = RenderLib.makeZappy(dots, RenderLib.findDupIndices(pattern.positions()), 10f, 0.8f, 0f, 0f);
this.zappyPoints = RenderLib.makeZappy(
dots, RenderLib.findDupIndices(pattern.positions()),
10, 0.8f, 0f, 0f, RenderLib.DEFAULT_READABILITY_OFFSET, RenderLib.DEFAULT_LAST_SEGMENT_LEN_PROP,
0.0);
this.pathfinderDots = dots.stream().distinct().collect(Collectors.toList());
}
@ -79,17 +81,17 @@ public class PatternTooltipComponent implements ClientTooltipComponent {
RenderSystem.setShader(GameRenderer::getPositionColorShader);
RenderSystem.disableCull();
RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA,
GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
ps.translate(width / 2f, height / 2f, 1);
var mat = ps.last().pose();
var outer = 0xff_d2c8c8;
var innerLight = 0xc8_aba2a2;
var innerDark = 0xc8_322b33;
RenderLib.drawLineSeq(mat, this.zappyPoints, 5f, 0,
outer, outer);
RenderLib.drawLineSeq(mat, this.zappyPoints, 2f, 0,
innerDark, innerLight);
RenderLib.drawLineSeq(mat, this.zappyPoints, 6f, 0,
outer, outer);
RenderLib.drawLineSeq(mat, this.zappyPoints, 6f * 0.4f, 0,
innerDark, innerLight);
RenderLib.drawSpot(mat, this.zappyPoints.get(0), 2.5f, 1f, 0.1f, 0.15f, 0.6f);
for (var dot : this.pathfinderDots) {
@ -104,7 +106,8 @@ public class PatternTooltipComponent implements ClientTooltipComponent {
RenderSystem.setShaderTexture(0, background);
// x y blitoffset sw sh w h ... ?
// parchment doesn't have this mapped
GuiComponent.blit(ps, 0, 0, blitOffset, 0f, 0f, (int) RENDER_SIZE, (int) RENDER_SIZE, (int) RENDER_SIZE, (int) RENDER_SIZE);
GuiComponent.blit(ps, 0, 0, blitOffset, 0f, 0f, (int) RENDER_SIZE, (int) RENDER_SIZE, (int) RENDER_SIZE,
(int) RENDER_SIZE);
}
@Override

View file

@ -53,7 +53,7 @@ public class EntityWallScroll extends HangingEntity {
}
public EntityWallScroll(Level world, BlockPos pos, Direction dir, ItemStack scroll, boolean showStrokeOrder,
int blockSize) {
int blockSize) {
super(HexEntities.WALL_SCROLL, world, pos);
this.setDirection(dir);
this.blockSize = blockSize;
@ -72,8 +72,10 @@ public class EntityWallScroll extends HangingEntity {
var pair = RenderLib.getCenteredPattern(pattern, 128f / 3 * blockSize, 128f / 3 * blockSize,
16f / 3 * blockSize);
var dots = pair.getSecond();
var readOffset = this.getShowsStrokeOrder() ? RenderLib.DEFAULT_READABILITY_OFFSET : 0f;
var lastProp = this.getShowsStrokeOrder() ? RenderLib.DEFAULT_LAST_SEGMENT_LEN_PROP : 1f;
this.zappyPoints = RenderLib.makeZappy(dots, RenderLib.findDupIndices(pattern.positions()), 10, 0.4f,
0f, 0f, this.getShowsStrokeOrder() ? 0.2f : 0f);
0f, 0f, readOffset, lastProp, this.getId());
}
this.isAncient = NBTHelper.hasString(scroll, ItemScroll.TAG_OP_ID);
@ -158,7 +160,7 @@ public class EntityWallScroll extends HangingEntity {
}
public void readSpawnData(BlockPos pos, Direction dir, ItemStack scrollItem,
boolean showsStrokeOrder, int blockSize) {
boolean showsStrokeOrder, int blockSize) {
this.pos = pos;
this.scroll = scrollItem;
this.blockSize = blockSize;
@ -201,7 +203,7 @@ public class EntityWallScroll extends HangingEntity {
@Override
public void lerpTo(double pX, double pY, double pZ, float pYaw, float pPitch, int pPosRotationIncrements,
boolean pTeleport) {
boolean pTeleport) {
BlockPos blockpos = this.pos.offset(pX - this.getX(), pY - this.getY(), pZ - this.getZ());
this.setPos(blockpos.getX(), blockpos.getY(), blockpos.getZ());
}

View file

@ -86,7 +86,10 @@ public class ItemSlate extends BlockItem implements IotaHolderItem {
@Override
public boolean canWrite(ItemStack stack, Iota datum) {
return datum instanceof PatternIota && !NBTHelper.hasCompound(stack, BlockEntitySlate.TAG_PATTERN);
var isWritten = NBTHelper.hasCompound(stack, "BlockEntityTag")
&& stack.getTag().getCompound("BlockEntityTag").contains(BlockEntitySlate.TAG_PATTERN);
return (datum instanceof PatternIota && !isWritten)
|| (datum == null && isWritten);
}
@Override

View file

@ -2,6 +2,7 @@ package at.petrak.hexcasting.interop.patchouli;
import at.petrak.hexcasting.api.spell.math.HexCoord;
import at.petrak.hexcasting.api.spell.math.HexPattern;
import at.petrak.hexcasting.client.RenderLib;
import at.petrak.hexcasting.interop.utils.PatternDrawingUtil;
import at.petrak.hexcasting.interop.utils.PatternEntry;
import com.mojang.blaze3d.vertex.PoseStack;
@ -22,7 +23,7 @@ abstract public class AbstractPatternComponent implements ICustomComponent {
protected transient float hexSize;
private transient List<PatternEntry> patterns;
private transient List<Vec2> pathfinderDots;
private transient List<Vec2> zappyPoints;
/**
* Pass -1, -1 to center it.
@ -39,17 +40,20 @@ abstract public class AbstractPatternComponent implements ICustomComponent {
@Override
public void render(PoseStack poseStack, IComponentRenderContext ctx, float partialTicks, int mouseX, int mouseY) {
PatternDrawingUtil.drawPattern(poseStack, this.x, this.y, this.patterns, this.pathfinderDots,
this.showStrokeOrder(), ctx.getTicksInBook(),
PatternDrawingUtil.drawPattern(poseStack, this.x, this.y, this.patterns, this.zappyPoints,
this.showStrokeOrder(),
0xff_d2c8c8, 0xc8_aba2a2, 0xc8_322b33, 0x80_d1cccc);
}
@Override
public void onVariablesAvailable(UnaryOperator<IVariable> lookup) {
var data = PatternDrawingUtil.loadPatterns(this.getPatterns(lookup));
var data = PatternDrawingUtil.loadPatterns(
this.getPatterns(lookup),
this.showStrokeOrder() ? RenderLib.DEFAULT_READABILITY_OFFSET : 0f,
this.showStrokeOrder() ? RenderLib.DEFAULT_LAST_SEGMENT_LEN_PROP : 1f);
this.hexSize = data.hexSize();
this.patterns = data.patterns();
this.pathfinderDots = data.pathfinderDots();
this.zappyPoints = data.pathfinderDots();
}
protected static class RawPattern {

View file

@ -19,7 +19,8 @@ import java.util.List;
public final class PatternDrawingUtil {
public static void drawPattern(PoseStack poseStack, int x, int y, List<PatternEntry> patterns, List<Vec2> dots,
boolean strokeOrder, long animTicks, int outer, int innerLight, int innerDark, int dotColor) {
boolean strokeOrder, int outer, int innerLight, int innerDark,
int dotColor) {
poseStack.pushPose();
poseStack.translate(x, y, 1);
var mat = poseStack.last().pose();
@ -59,7 +60,8 @@ public final class PatternDrawingUtil {
poseStack.popPose();
}
public static PatternRenderingData loadPatterns(List<Pair<HexPattern, HexCoord>> patterns) {
public static PatternRenderingData loadPatterns(List<Pair<HexPattern, HexCoord>> patterns,
float readabilityOffset, float lastLineLenProp) {
var patternEntries = new ArrayList<PatternEntry>(patterns.size());
var fakeScale = 1;
@ -103,12 +105,13 @@ public final class PatternDrawingUtil {
var realCom = HexUtils.findCenter(seenRealPoints);
// and NOW for real!
for (var pat : patternEntries) {
for (int i = 0; i < patternEntries.size(); i++) {
PatternEntry pat = patternEntries.get(i);
var localOrigin = HexUtils.coordToPx(pat.origin(), hexSize, realCom.negated());
var points = pat.pattern().toLines(hexSize, localOrigin);
pat.zappyPoints()
.addAll(RenderLib.makeZappy(points, RenderLib.findDupIndices(pat.pattern().positions()), 10f, 0.8f, 0f,
0f));
.addAll(RenderLib.makeZappy(points, RenderLib.findDupIndices(pat.pattern().positions()), 10, 0.8f, 0f,
0f, readabilityOffset, lastLineLenProp, i));
}
var pathfinderDots = seenCoords.stream()

View file

@ -270,7 +270,7 @@
"hexcasting.spell.book.hexcasting:const/vec/x": "Vector Rfln. +X/-X",
"hexcasting.spell.book.hexcasting:const/vec/y": "Vector Rfln. +Y/-Y",
"hexcasting.spell.book.hexcasting:const/vec/z": "Vector Rfln. +Z/-Z",
"hexcasting.spell.book.hexcasting:write/entity": "Chronicler's Prfn.",
"hexcasting.spell.book.hexcasting:read/entity": "Chronicler's Prfn.",
"hexcasting.spell.book.hexcasting:number": "Numerical Reflection",
"hexcasting.spell.book.hexcasting:mask": "Bookkeeper's Gambit",
@ -340,7 +340,7 @@
"hexcasting.spell.hexcasting:rotate": "Rotation Gambit",
"hexcasting.spell.hexcasting:rotate_reverse": "Rotation Gambit II",
"hexcasting.spell.hexcasting:duplicate": "Gemini Decomposition",
"hexcasting.spell.hexcasting:over": "Prospecter's Gambit",
"hexcasting.spell.hexcasting:over": "Prospector's Gambit",
"hexcasting.spell.hexcasting:tuck": "Undertaker's Gambit",
"hexcasting.spell.hexcasting:2dup": "Dioscuri Gambit",
"hexcasting.spell.hexcasting:duplicate_n": "Gemini Gambit",

View file

@ -270,7 +270,7 @@
"hexcasting.spell.book.hexcasting:const/vec/x": "向量之精思,+X/-X型",
"hexcasting.spell.book.hexcasting:const/vec/y": "向量之精思,+Y/-Y型",
"hexcasting.spell.book.hexcasting:const/vec/z": "向量之精思,+Z/-Z型",
"hexcasting.spell.book.hexcasting:write/entity": "编年史家之策略",
"hexcasting.spell.book.hexcasting:read/entity": "编年史家之纯化",
"hexcasting.spell.book.hexcasting:number": "数字之精思",
"hexcasting.spell.book.hexcasting:mask": "簿记员之策略",
@ -336,12 +336,17 @@
"hexcasting.spell.hexcasting:zone_entity/not_player": "区域之馏化:非玩家",
"hexcasting.spell.hexcasting:zone_entity/not_living": "区域之馏化:非生物",
"hexcasting.spell.hexcasting:duplicate": "双子之分解",
"hexcasting.spell.hexcasting:duplicate_n": "双子之策略",
"hexcasting.spell.hexcasting:over": "狄俄斯库里之策略",
"hexcasting.spell.hexcasting:stack_len": "群体之精思",
"hexcasting.spell.hexcasting:swap": "弄臣之策略",
"hexcasting.spell.hexcasting:rotate": "轮换之策略",
"hexcasting.spell.hexcasting:rotate_reverse": "轮换之策略,第二型",
"hexcasting.spell.hexcasting:duplicate": "双子之分解",
"hexcasting.spell.hexcasting:over": "勘探者之策略",
"hexcasting.spell.hexcasting:tuck": "送葬者之策略",
"hexcasting.spell.hexcasting:2dup": "狄俄斯库里之策略",
"hexcasting.spell.hexcasting:duplicate_n": "双子之策略",
"hexcasting.spell.hexcasting:stack_len": "群体之精思",
"hexcasting.spell.hexcasting:fisherman": "渔夫之策略",
"hexcasting.spell.hexcasting:fisherman/copy": "渔夫之策略,第二型",
"hexcasting.spell.hexcasting:swizzle": "骗徒之策略",
"hexcasting.spell.hexcasting:and_bit": "交叉之馏化",
@ -897,10 +902,15 @@
"hexcasting.page.stackmanip.pseudo-novice": "移除栈顶的 iota。",
"hexcasting.page.stackmanip.duplicate": "复制栈顶的 iota。",
"hexcasting.page.stackmanip.swap": "交换栈顶两个 iota 的位置。",
"hexcasting.page.stackmanip.rotate": "将栈顶往下第三元素拉至栈顶。[0, 1, 2] 变为 [1, 2, 0]。",
"hexcasting.page.stackmanip.over": "将栈顶往下第二元素复制至栈顶。[0, 1] 变为 [0, 1, 0]。",
"hexcasting.page.stackmanip.rotate": "将栈顶往下第三元素拉至栈顶。[0, 1, 2] 变为 [1, 2, 0]。",
"hexcasting.page.stackmanip.rotate_reverse": "将栈顶元素沉至栈顶往下第三位处。[0, 1, 2] 变为 [2, 0, 1]。",
"hexcasting.page.stackmanip.over": "将栈顶往下第二元素复制至栈顶。[0, 1] 变为 [0, 1, 0]。",
"hexcasting.page.stackmanip.tuck": "将栈底元素复制至栈顶往下第二元素下方。[0, 1] 变为 [1, 0, 1]。",
"hexcasting.page.stackmanip.2dup": "复制栈顶的两个 iota。[0, 1] 变为 [0, 1, 0, 1]。",
"hexcasting.page.stackmanip.duplicate_n": "移除栈顶的数,然后将现在的栈顶元素复制该数次。(若所给数为 2则栈顶会有两个同一元素。",
"hexcasting.page.stackmanip.fisherman": "提出下标为所给数的元素并将其置于栈顶。",
"hexcasting.page.stackmanip.fisherman/copy": "与$(action)渔夫之策略/$类似,但会复制 iota 而非将其提出。",
"hexcasting.page.stackmanip.stack_len": "以数的形式压入栈中元素的个数。(例如,一个形如 [0, 1] 的栈会变为 [0, 1, 2]。)",
"hexcasting.page.stackmanip.last_n_list": "移除$(italic)所给数/$个元素,然后压入一个由其组成的列表。",
"hexcasting.page.stackmanip.splat": "移除栈顶的列表,然后依次压入其元素。",

View file

@ -36,7 +36,7 @@
"type": "hexcasting:pattern",
"op_id": "hexcasting:write/entity",
"anchor": "hexcasting:write/entity",
"input": "any, entity",
"input": "entity, any",
"output": "",
"text": "hexcasting.page.readwrite.write/entity"
},

View file

@ -0,0 +1,39 @@
import net.minecraft.world.level.levelgen.SingleThreadedRandomSource;
import net.minecraft.world.level.levelgen.synth.PerlinNoise;
import net.minecraft.world.level.levelgen.synth.SimplexNoise;
import org.junit.jupiter.api.Test;
import java.util.List;
public class WhatRangeDoTheNoisesOutputAnywaysTest {
@Test
public void test() {
var perlin = PerlinNoise.create(new SingleThreadedRandomSource(12345), List.of(0, 1, 2, 3, 4));
var simplex = new SimplexNoise(new SingleThreadedRandomSource(12345));
System.out.println("Perlin:");
for (int i = 0; i < 20; i++) {
System.out.printf(" %f%n", perlin.getValue(i / 10.0, 69420.0, 1337.0));
}
System.out.println("Simplex:");
for (int i = 0; i < 20; i++) {
System.out.printf(" %f%n", simplex.getValue(i / 10.0, 69420.0, 1337.0));
}
}
@Test
public void perlinBounds() {
var perlin = PerlinNoise.create(new SingleThreadedRandomSource(12345), List.of(0, 1, 2, 3, 4));
var min = Double.POSITIVE_INFINITY;
var max = Double.NEGATIVE_INFINITY;
for (int i = 0; i < 10000; i++) {
var it = perlin.getValue(i / 10.0, 12345.0, 7604.0);
min = Math.min(min, it);
max = Math.max(max, it);
}
System.out.printf("Min: %f\nMax: %f\n", min, max);
}
}

View file

@ -30,7 +30,8 @@ public class PatternRendererEMI implements EmiRenderable {
public PatternRendererEMI(ResourceLocation pattern, int w, int h) {
var entry = PatternRegistry.lookupPattern(pattern);
this.strokeOrder = !entry.isPerWorld();
var data = PatternDrawingUtil.loadPatterns(List.of(new Pair<>(entry.prototype(), HexCoord.getOrigin())));
var data = PatternDrawingUtil.loadPatterns(List.of(new Pair<>(entry.prototype(), HexCoord.getOrigin())), 0f,
1f);
this.patterns = data.patterns();
this.pathfinderDots = data.pathfinderDots();
this.width = w;
@ -54,7 +55,7 @@ public class PatternRendererEMI implements EmiRenderable {
poseStack.pushPose();
poseStack.translate(xOffset + x - 0.5f + width / 2f, yOffset + y + 1 + height / 2f, 0);
poseStack.scale(width / 64f, height / 64f, 1f);
PatternDrawingUtil.drawPattern(poseStack, 0, 0, this.patterns, this.pathfinderDots, this.strokeOrder, time,
PatternDrawingUtil.drawPattern(poseStack, 0, 0, this.patterns, this.pathfinderDots, this.strokeOrder,
0xff_333030, 0xff_191818, 0xc8_0c0a0c, 0x80_666363);
poseStack.popPose();
}

View file

@ -111,7 +111,7 @@ public class ForgeHexInitializer {
// https://github.com/VazkiiMods/Botania/blob/1.18.x/Forge/src/main/java/vazkii/botania/forge/ForgeCommonInitializer.java
private static <T> void bind(ResourceKey<Registry<T>> registry,
Consumer<BiConsumer<T, ResourceLocation>> source) {
Consumer<BiConsumer<T, ResourceLocation>> source) {
getModEventBus().addListener((RegisterEvent event) -> {
if (registry.equals(event.getRegistryKey())) {
source.accept((t, rl) -> event.register(registry, rl, () -> t));
@ -123,7 +123,7 @@ public class ForgeHexInitializer {
var modBus = getModEventBus();
var evBus = MinecraftForge.EVENT_BUS;
modBus.register(ForgeHexClientInitializer.class);
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> modBus.register(ForgeHexClientInitializer.class));
modBus.addListener((FMLCommonSetupEvent evt) ->
evt.enqueueWork(() -> {

View file

@ -26,7 +26,10 @@ public class PatternDrawable implements IDrawable {
public PatternDrawable(ResourceLocation pattern, int w, int h) {
var entry = PatternRegistry.lookupPattern(pattern);
this.strokeOrder = !entry.isPerWorld();
var data = PatternDrawingUtil.loadPatterns(List.of(new Pair<>(entry.prototype(), HexCoord.getOrigin())));
var data = PatternDrawingUtil.loadPatterns(
List.of(new Pair<>(entry.prototype(), HexCoord.getOrigin())),
0f,
1f);
this.patterns = data.patterns();
this.pathfinderDots = data.pathfinderDots();
this.width = w;
@ -54,7 +57,7 @@ public class PatternDrawable implements IDrawable {
poseStack.pushPose();
poseStack.translate(xOffset - 0.5f + width / 2f, yOffset + height / 2f, 0);
poseStack.scale(width / 64f, height / 64f, 1f);
PatternDrawingUtil.drawPattern(poseStack, 0, 0, this.patterns, this.pathfinderDots, this.strokeOrder, time,
PatternDrawingUtil.drawPattern(poseStack, 0, 0, this.patterns, this.pathfinderDots, this.strokeOrder,
0xff_333030, 0xff_191818, 0xc8_0c0a0c, 0x80_666363);
poseStack.popPose();
}