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

View file

@ -86,12 +86,18 @@ public interface IotaHolderItem {
return HexIotaTypes.getColor(tag); return HexIotaTypes.getColor(tag);
} }
/**
* Write {@code null} to indicate erasing
*/
boolean canWrite(ItemStack stack, @Nullable Iota iota); boolean canWrite(ItemStack stack, @Nullable Iota iota);
/**
* Write {@code null} to indicate erasing
*/
void writeDatum(ItemStack stack, @Nullable Iota iota); void writeDatum(ItemStack stack, @Nullable Iota iota);
static void appendHoverText(IotaHolderItem self, ItemStack stack, List<Component> components, static void appendHoverText(IotaHolderItem self, ItemStack stack, List<Component> components,
TooltipFlag flag) { TooltipFlag flag) {
var datumTag = self.readIotaTag(stack); var datumTag = self.readIotaTag(stack);
if (datumTag != null) { if (datumTag != null) {
var cmp = HexIotaTypes.getDisplay(datumTag); 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.getValue
import at.petrak.hexcasting.api.utils.setValue import at.petrak.hexcasting.api.utils.setValue
import at.petrak.hexcasting.api.utils.weakMapped import at.petrak.hexcasting.api.utils.weakMapped
import at.petrak.hexcasting.client.gui.GuiSpellcasting
import at.petrak.hexcasting.common.recipe.ingredient.brainsweep.BrainsweepIngredient import at.petrak.hexcasting.common.recipe.ingredient.brainsweep.BrainsweepIngredient
import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.DefaultVertexFormat 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.entity.npc.VillagerType
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraft.world.level.levelgen.XoroshiroRandomSource import net.minecraft.world.level.levelgen.SingleThreadedRandomSource
import net.minecraft.world.level.levelgen.synth.PerlinNoise import net.minecraft.world.level.levelgen.synth.SimplexNoise
import net.minecraft.world.phys.Vec2 import net.minecraft.world.phys.Vec2
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.min import kotlin.math.min
import kotlin.math.roundToInt
import kotlin.math.sin import kotlin.math.sin
/** val NOISE: SimplexNoise = SimplexNoise(SingleThreadedRandomSource(9001L))
* Source of perlin noise
*/
val NOISE: PerlinNoise = PerlinNoise.create(XoroshiroRandomSource(9001L), listOf(0, 1, 2, 3, 4))
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. * 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] * Please make sure to enable the right asinine shaders; see [GuiSpellcasting]
*/ */
fun drawLineSeq( fun drawLineSeq(
mat: Matrix4f, mat: Matrix4f,
points: List<Vec2>, points: List<Vec2>,
width: Float, width: Float,
z: Float, z: Float,
tail: Int, tail: Int,
head: Int, head: Int,
) { ) {
if (points.size <= 1) return if (points.size <= 1) return
@ -79,24 +84,27 @@ fun drawLineSeq(
val n = points.size val n = points.size
val joinAngles = FloatArray(n) val joinAngles = FloatArray(n)
val joinOffsets = FloatArray(n) val joinOffsets = FloatArray(n)
for (i in 2..n - 1) { for (i in 2 until n) {
val p0 = points[i - 2]; val p0 = points[i - 2];
val p1 = points[i - 1]; val p1 = points[i - 1];
val p2 = points[i]; val p2 = points[i];
val prev = p1.add(p0.negated()) val prev = p1.add(p0.negated())
val next = p2.add(p1.negated()) val next = p2.add(p1.negated())
val angle = val angle =
Mth.atan2((prev.x * next.y - prev.y * next.x).toDouble(), (prev.x * next.x + prev.y * next.y).toDouble()) Mth.atan2((prev.x * next.y - prev.y * next.x).toDouble(), (prev.x * next.x + prev.y * next.y).toDouble())
.toFloat() .toFloat()
joinAngles[i - 1] = angle joinAngles[i - 1] = angle
val clamp = Math.min(prev.length(), next.length()) / (width * 0.5f); 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) joinOffsets[i - 1] = Mth.clamp(Mth.sin(angle) / (1 + Mth.cos(angle)), -clamp, clamp)
} }
fun vertex(color: BlockPos, pos: Vec2) = fun vertex(color: BlockPos, pos: Vec2) =
buf.vertex(mat, pos.x, pos.y, z).color(color.x, color.y, color.z, a).endVertex() 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.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 // https://github.com/not-fl3/macroquad/blob/master/src/shapes.rs#L163
// GuiComponent::innerFill line 52 // GuiComponent::innerFill line 52
// fedor have useful variable names challenge (99% can't beat) // fedor have useful variable names challenge (99% can't beat)
@ -104,22 +112,38 @@ fun drawLineSeq(
val normal = Vec2(-tangent.y, tangent.x) val normal = Vec2(-tangent.y, tangent.x)
fun color(time: Float): BlockPos = 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 color1 = color(i.toFloat() / n)
val color2 = color((i + 1f) / n) val color2 = color((i + 1f) / n)
val jlow = joinOffsets[i] val jlow = joinOffsets[i]
val jhigh = joinOffsets[i + 1] val jhigh = joinOffsets[i + 1]
buf.begin(VertexFormat.Mode.TRIANGLE_FAN, DefaultVertexFormat.POSITION_COLOR) // Draw the line segment as a hexagon, sort of
vertex(color1, p1.add(tangent.scale(Math.max(0f, jlow))).add(normal)) // 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)
vertex(color1, p1.add(tangent.scale(Math.max(0f, -jlow))).add(normal.negated())) vertex(color1, p1Up)
vertex(color2, p2.add(tangent.scale(Math.max(0f, -jhigh)).negated()).add(normal.negated()))
vertex(color1, p1Down)
vertex(color1, p1Up)
vertex(color2, p2Up)
vertex(color1, p1Down)
vertex(color2, p2Up)
vertex(color2, p2) 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) { if (i > 0) {
// Draw the connector to the next line segment
val sangle = joinAngles[i] val sangle = joinAngles[i]
val angle = Math.abs(sangle) val angle = Math.abs(sangle)
val rnormal = normal.negated() val rnormal = normal.negated()
@ -127,22 +151,35 @@ fun drawLineSeq(
if (joinSteps < 1) { if (joinSteps < 1) {
continue 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) { 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)) 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 { } 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)) 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) { fun drawCaps(color: BlockPos, point: Vec2, prev: Vec2) {
val tangent = point.add(prev.negated()).normalized().scale(0.5f * width) val tangent = point.add(prev.negated()).normalized().scale(0.5f * width)
val normal = Vec2(-tangent.y, tangent.x) 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, * 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.) * you have to do the conversion yourself.)
* */ */
fun drawPatternFromPoints( fun drawPatternFromPoints(
mat: Matrix4f, mat: Matrix4f,
points: List<Vec2>, points: List<Vec2>,
dupIndices: Set<Int>?, dupIndices: Set<Int>?,
drawLast: Boolean, drawLast: Boolean,
tail: Int, tail: Int,
head: Int, head: Int,
flowIrregular: Float, 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) { val nodes = if (drawLast) {
points points
} else { } else {
@ -188,45 +228,36 @@ fun drawPatternFromPoints(
drawLineSeq(mat, zappyPts, 2f, 1f, screenCol(tail), screenCol(head)) drawLineSeq(mat, zappyPts, 2f, 1f, screenCol(tail), screenCol(head))
for (node in nodes) { for (node in nodes) {
drawSpot( drawSpot(
mat, mat,
node, node,
2f, 2f,
dodge(FastColor.ARGB32.red(head)) / 255f, dodge(FastColor.ARGB32.red(head)) / 255f,
dodge(FastColor.ARGB32.green(head)) / 255f, dodge(FastColor.ARGB32.green(head)) / 255f,
dodge(FastColor.ARGB32.blue(head)) / 255f, dodge(FastColor.ARGB32.blue(head)) / 255f,
FastColor.ARGB32.alpha(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 * Split up a sequence of linePoints with a lightning effect
* @param hops: rough number of points to subdivide each segment into * @param hops: rough number of points to subdivide each segment into
* @param speed: rate at which the lightning effect should move/shake/etc * @param speed: rate at which the lightning effect should move/shake/etc
*/ */
fun makeZappy( fun makeZappy(
barePoints: List<Vec2>, dupIndices: Set<Int>?, hops: Int, variance: Float, speed: Float, flowIrregular: Float, barePoints: List<Vec2>, dupIndices: Set<Int>?, hops: Int, variance: Float, speed: Float, flowIrregular: Float,
readabilityOffset: Float readabilityOffset: Float, lastSegmentLenProportion: Float, seed: Double
): List<Vec2> { ): List<Vec2> {
// Nothing in, nothing out // Nothing in, nothing out
if (barePoints.isEmpty()) { if (barePoints.isEmpty()) {
return emptyList() 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 scaleVariance = { it: Double -> 1.0.coerceAtMost(8 * (0.5 - abs(0.5 - it))) }
val zSeed = ClientTickCounter.getTotal().toDouble() * speed val zSeed = ClientTickCounter.getTotal().toDouble() * speed
// Create our output list of zap points // 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 each segment in the original...
for ((i, pair) in points.zipWithNext().withIndex()) { for ((i, pair) in points.zipWithNext().withIndex()) {
val (src, target) = pair val (src, target) = pair
@ -236,30 +267,40 @@ fun makeZappy(
// Compute how big the radius of variance should be // Compute how big the radius of variance should be
val maxVariance = hopDist * variance 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) val progress = j.toDouble() / (hops + 1)
// Add the next hop... // Add the next hop...
val pos = src.add(delta.scale(progress.toFloat())) val pos = src.add(delta.scale(progress.toFloat()))
// as well as some random variance... // as well as some random variance...
// (We use i, j (segment #, subsegment #) as seeds for the Perlin noise, // (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) // 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 minorPerturb = getNoise(i.toDouble(), j.toDouble(), sin(zSeed)) * flowIrregular
val theta = (3 * NOISE.getValue( val theta = (3 * getNoise(
i.toDouble() + j.toDouble() / (hops + 1) + minorPerturb - zSeed, i + progress + minorPerturb - zSeed,
1337.0, 1337.0,
0.0 seed
) * TAU).toFloat() ) * TAU).toFloat()
val r = (NOISE.getValue( val r = (getNoise(
i.toDouble() + j.toDouble() / (hops + 1) - zSeed, i + progress - zSeed,
69420.0, 69420.0,
0.0 seed
) * maxVariance * scaleVariance(progress)).toFloat() ) * maxVariance * scaleVariance(progress)).toFloat()
val randomHop = Vec2(r * Mth.cos(theta), r * Mth.sin(theta)) val randomHop = Vec2(r * Mth.cos(theta), r * Mth.sin(theta))
// Then record the new location. // Then record the new location.
zappyPts.add(pos.add(randomHop)) 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 return zappyPts
} }
@ -277,16 +318,16 @@ fun makeZappy(
} }
if (i == barePoints.size - 2) { if (i == barePoints.size - 2) {
daisyChain.add(tail) daisyChain.add(tail)
points.addAll(zappify(daisyChain)) points.addAll(zappify(daisyChain, true))
} else if (dupIndices.contains(i + 1)) { } else if (dupIndices.contains(i + 1)) {
daisyChain.add(tail.add(tangent.negated())) daisyChain.add(tail.add(tangent.negated()))
points.addAll(zappify(daisyChain)) points.addAll(zappify(daisyChain, false))
daisyChain.clear() daisyChain.clear()
} }
} }
points points
} else { } 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 { fun screenCol(n: Int): Int {
return FastColor.ARGB32.color( return FastColor.ARGB32.color(
FastColor.ARGB32.alpha(n), FastColor.ARGB32.alpha(n),
screen(FastColor.ARGB32.red(n)), screen(FastColor.ARGB32.red(n)),
screen(FastColor.ARGB32.green(n)), screen(FastColor.ARGB32.green(n)),
screen(FastColor.ARGB32.blue(n)), screen(FastColor.ARGB32.blue(n)),
) )
} }
@ -363,7 +404,7 @@ fun getCenteredPattern(pattern: HexPattern, width: Float, height: Float, minSize
} }
} }
val scale = 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 com2: Vec2 = pattern.getCenter(scale)
val lines2: List<Vec2> = pattern.toLines(scale, com2.negated()) val lines2: List<Vec2> = pattern.toLines(scale, com2.negated())
return scale to lines2 return scale to lines2
@ -388,9 +429,9 @@ private var villager: Villager? by weakMapped(Villager::level)
fun prepareVillagerForRendering(ingredient: BrainsweepIngredient, level: Level): Villager { fun prepareVillagerForRendering(ingredient: BrainsweepIngredient, level: Level): Villager {
val minLevel: Int = ingredient.minLevel() val minLevel: Int = ingredient.minLevel()
val profession: VillagerProfession = Registry.VILLAGER_PROFESSION.getOptional(ingredient.profession()) val profession: VillagerProfession = Registry.VILLAGER_PROFESSION.getOptional(ingredient.profession())
.orElse(VillagerProfession.NONE) .orElse(VillagerProfession.NONE)
val biome: VillagerType = Registry.VILLAGER_TYPE.getOptional(ingredient.biome()) val biome: VillagerType = Registry.VILLAGER_TYPE.getOptional(ingredient.biome())
.orElse(VillagerType.PLAINS) .orElse(VillagerType.PLAINS)
val instantiatedVillager = villager ?: run { val instantiatedVillager = villager ?: run {
val newVillager = Villager(EntityType.VILLAGER, level) val newVillager = Villager(EntityType.VILLAGER, level)
@ -399,18 +440,18 @@ fun prepareVillagerForRendering(ingredient: BrainsweepIngredient, level: Level):
} }
instantiatedVillager.villagerData = instantiatedVillager.villagerData instantiatedVillager.villagerData = instantiatedVillager.villagerData
.setProfession(profession) .setProfession(profession)
.setType(biome) .setType(biome)
.setLevel(minLevel) .setLevel(minLevel)
return instantiatedVillager return instantiatedVillager
} }
@JvmOverloads @JvmOverloads
fun renderEntity( fun renderEntity(
ms: PoseStack, entity: Entity, world: Level, x: Float, y: Float, rotation: Float, ms: PoseStack, entity: Entity, world: Level, x: Float, y: Float, rotation: Float,
renderScale: Float, offset: Float, renderScale: Float, offset: Float,
bufferTransformer: (MultiBufferSource) -> MultiBufferSource = { it -> it } bufferTransformer: (MultiBufferSource) -> MultiBufferSource = { it -> it }
) { ) {
entity.level = world entity.level = world
ms.pushPose() ms.pushPose()
@ -432,23 +473,23 @@ fun renderEntity(
* Make sure you have the `PositionColorShader` set * Make sure you have the `PositionColorShader` set
*/ */
fun renderQuad( 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 mat = ps.last().pose()
val tess = Tesselator.getInstance() val tess = Tesselator.getInstance()
val buf = tess.builder val buf = tess.builder
buf.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR) buf.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR)
buf.vertex(mat, x, y, 0f) buf.vertex(mat, x, y, 0f)
.color(color) .color(color)
.endVertex() .endVertex()
buf.vertex(mat, x, y + h, 0f) buf.vertex(mat, x, y + h, 0f)
.color(color) .color(color)
.endVertex() .endVertex()
buf.vertex(mat, x + w, y + h, 0f) buf.vertex(mat, x + w, y + h, 0f)
.color(color) .color(color)
.endVertex() .endVertex()
buf.vertex(mat, x + w, y, 0f) buf.vertex(mat, x + w, y, 0f)
.color(color) .color(color)
.endVertex() .endVertex()
tess.end() tess.end()
} }

View file

@ -22,7 +22,7 @@ public class BlockEntityAkashicBookshelfRenderer implements BlockEntityRenderer<
@Override @Override
public void render(BlockEntityAkashicBookshelf tile, float pPartialTick, PoseStack ps, 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(); HexPattern pattern = tile.getPattern();
if (pattern == null) { if (pattern == null) {
return; return;
@ -73,7 +73,9 @@ public class BlockEntityAkashicBookshelfRenderer implements BlockEntityRenderer<
lines2.set(j, new Vec2(-v.x, v.y)); 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 outer = 0xff_d2c8c8;
int inner = 0xc8_322b33; int inner = 0xc8_322b33;

View file

@ -22,7 +22,7 @@ public class BlockEntitySlateRenderer implements BlockEntityRenderer<BlockEntity
@Override @Override
public void render(BlockEntitySlate tile, float pPartialTick, PoseStack ps, 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) { if (tile.pattern == null) {
return; return;
} }
@ -84,7 +84,11 @@ public class BlockEntitySlateRenderer implements BlockEntityRenderer<BlockEntity
} }
var isLit = bs.getValue(BlockSlate.ENERGIZED); 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 outer = isLit ? 0xff_64c8ff : 0xff_d2c8c8;
int inner = isLit ? RenderLib.screenCol(outer) : 0xc8_322b33; int inner = isLit ? RenderLib.screenCol(outer) : 0xc8_322b33;

View file

@ -1,5 +1,6 @@
package at.petrak.hexcasting.client.entity; package at.petrak.hexcasting.client.entity;
import at.petrak.hexcasting.client.RenderLib;
import at.petrak.hexcasting.common.entities.EntityWallScroll; import at.petrak.hexcasting.common.entities.EntityWallScroll;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
@ -38,7 +39,7 @@ public class WallScrollRenderer extends EntityRenderer<EntityWallScroll> {
// I do as the PaintingRenderer guides // I do as the PaintingRenderer guides
@Override @Override
public void render(EntityWallScroll wallScroll, float yaw, float partialTicks, PoseStack ps, public void render(EntityWallScroll wallScroll, float yaw, float partialTicks, PoseStack ps,
MultiBufferSource bufSource, int packedLight) { MultiBufferSource bufSource, int packedLight) {
RenderSystem.setShader(GameRenderer::getPositionTexShader); 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, private static void vertex(Matrix4f mat, Matrix3f normal, int light, VertexConsumer verts, float x, float y,
float z, float u, float z, float u,
float v, float nx, float ny, float nz) { float v, float nx, float ny, float nz) {
verts.vertex(mat, x, y, z) verts.vertex(mat, x, y, z)
.color(0xffffffff) .color(0xffffffff)
.uv(u, v).overlayCoords(OverlayTexture.NO_OVERLAY).uv2(light) .uv(u, v).overlayCoords(OverlayTexture.NO_OVERLAY).uv2(light)
@ -166,82 +167,136 @@ public class WallScrollRenderer extends EntityRenderer<EntityWallScroll> {
.endVertex(); .endVertex();
} }
private static void vertexCol(Matrix4f mat, Matrix3f normal, int light, VertexConsumer verts, int col, float x, private static void vertexCol(Matrix4f mat, Matrix3f normal, int light, VertexConsumer verts, int col, Vec2 pos) {
float y) { verts.vertex(mat, -pos.x, pos.y, 0)
verts.vertex(mat, -x, y, 0)
.color(col) .color(col)
.uv(0, 0).overlayCoords(OverlayTexture.NO_OVERLAY).uv2(light) .uv(0, 0).overlayCoords(OverlayTexture.NO_OVERLAY).uv2(light)
.normal(normal, 0, 0, 1) .normal(normal, 0, 0, 1)
.endVertex(); .endVertex();
} }
private static void theCoolerDrawLineSeq(Matrix4f mat, Matrix3f normal, int light, VertexConsumer verts, private static void theCoolerDrawLineSeq(Matrix4f mat, Matrix3f normalMat, int light, VertexConsumer verts,
List<Vec2> points, float width, int color List<Vec2> points, float width, int color
) { ) {
if (points.size() <= 1) { if (points.size() <= 1) {
return; return;
} }
float prevXHi, prevYHi, prevXLo, prevYLo; // TODO: abstract some of this out with RenderLib to stop WET code
{ var joinAngles = new float[points.size()];
var p1 = points.get(0); var joinOffsets = new float[points.size()];
var p2 = points.get(1); for (int i = 2; i < points.size(); i++) {
var p0 = points.get(i - 2);
var dx = p2.x - p1.x; var p1 = points.get(i - 1);
var dy = p2.y - p1.y; var p2 = points.get(i);
var nx = -dy; var prev = p1.add(p0.negated());
var ny = dx; var next = p2.add(p1.negated());
var tlen = Mth.sqrt(nx * nx + ny * ny) / (width * 0.5f); var angle = (float) Mth.atan2(
var tx = nx / tlen; prev.x * next.y - prev.y * next.x,
var ty = ny / tlen; prev.x * next.x + prev.y * next.y);
joinAngles[i - 1] = angle;
prevXHi = p1.x - tx; var clamp = Math.min(prev.length(), next.length()) / (width * 0.5f);
prevYHi = p1.y - ty; joinOffsets[i - 1] = Mth.clamp(Mth.sin(angle) / (1 + Mth.cos(angle)), -clamp, clamp);
prevXLo = p1.x + tx;
prevYLo = p1.y + ty;
} }
for (var i = 0; i < points.size() - 1; i++) { for (var i = 0; i < points.size() - 1; i++) {
var p1 = points.get(i); var p1 = points.get(i);
var p2 = points.get(i + 1); var p2 = points.get(i + 1);
var dx = p2.x - p1.x; var tangent = p2.add(p1.negated()).normalized().scale(width * 0.5f);
var dy = p2.y - p1.y; var normal = new Vec2(-tangent.y, tangent.x);
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 xHi = p2.x - tx; var jlow = joinOffsets[i];
var yHi = p2.y - ty; var jhigh = joinOffsets[i + 1];
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);
prevXHi = xHi; var p1Down = p1.add(tangent.scale(Math.max(0f, jlow))).add(normal);
prevYHi = yHi; var p1Up = p1.add(tangent.scale(Math.max(0f, -jlow))).add(normal.negated());
prevXLo = xLo; var p2Down = p2.add(tangent.scale(Math.max(0f, jhigh)).negated()).add(normal);
prevYLo = yLo; 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, 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; var fracOfCircle = 6;
for (int i = 0; i < fracOfCircle; i++) { for (int i = 0; i < fracOfCircle; i++) {
// We do need rects, irritatingly // We do need rects, irritatingly
// so we do fake triangles // so we do fake triangles
vertexCol(mat, normal, light, verts, color, point.x, point.y); vertexCol(mat, normal, light, verts, color, point);
vertexCol(mat, normal, light, verts, color, point.x, point.y); vertexCol(mat, normal, light, verts, color, point);
for (int j = 0; j <= 1; j++) { for (int j = 0; j <= 1; j++) {
var theta = (i - j) / (float) fracOfCircle * Mth.TWO_PI; var theta = (i - j) / (float) fracOfCircle * Mth.TWO_PI;
var rx = Mth.cos(theta) * radius + point.x; var rx = Mth.cos(theta) * radius + point.x;
var ry = Mth.sin(theta) * radius + point.y; 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. // Well, we never managed to get anything on the stack this go-around.
this.drawState = PatternDrawState.BetweenPatterns this.drawState = PatternDrawState.BetweenPatterns
} }
is PatternDrawState.Drawing -> { is PatternDrawState.Drawing -> {
val (start, _, pat) = this.drawState as PatternDrawState.Drawing val (start, _, pat) = this.drawState as PatternDrawState.Drawing
this.drawState = PatternDrawState.BetweenPatterns this.drawState = PatternDrawState.BetweenPatterns
@ -325,7 +326,8 @@ class GuiSpellcasting constructor(
} }
RenderSystem.defaultBlendFunc() RenderSystem.defaultBlendFunc()
for ((pat, origin, valid) in this.patterns) { for ((idx, elts) in this.patterns.withIndex()) {
val (pat, origin, valid) = elts
drawPatternFromPoints( drawPatternFromPoints(
mat, mat,
pat.toLines( pat.toLines(
@ -336,7 +338,10 @@ class GuiSpellcasting constructor(
true, true,
valid.color or (0xC8 shl 24), valid.color or (0xC8 shl 24),
valid.fadeColor 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) 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() 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 ANCIENT_BG = modLoc("textures/gui/scroll_ancient.png");
public static final ResourceLocation SLATE_BG = modLoc("textures/gui/slate.png"); public static final ResourceLocation SLATE_BG = modLoc("textures/gui/slate.png");
private static final float RENDER_SIZE = 72f; private static final float RENDER_SIZE = 128f;
private static final float TEXTURE_SIZE = 48f;
private final HexPattern pattern; private final HexPattern pattern;
private final List<Vec2> zappyPoints; private final List<Vec2> zappyPoints;
@ -45,10 +44,13 @@ public class PatternTooltipComponent implements ClientTooltipComponent {
this.pattern = tt.pattern(); this.pattern = tt.pattern();
this.background = tt.background(); 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(); this.scale = pair.getFirst();
var dots = pair.getSecond(); 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()); this.pathfinderDots = dots.stream().distinct().collect(Collectors.toList());
} }
@ -79,17 +81,17 @@ public class PatternTooltipComponent implements ClientTooltipComponent {
RenderSystem.setShader(GameRenderer::getPositionColorShader); RenderSystem.setShader(GameRenderer::getPositionColorShader);
RenderSystem.disableCull(); RenderSystem.disableCull();
RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, 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); ps.translate(width / 2f, height / 2f, 1);
var mat = ps.last().pose(); var mat = ps.last().pose();
var outer = 0xff_d2c8c8; var outer = 0xff_d2c8c8;
var innerLight = 0xc8_aba2a2; var innerLight = 0xc8_aba2a2;
var innerDark = 0xc8_322b33; var innerDark = 0xc8_322b33;
RenderLib.drawLineSeq(mat, this.zappyPoints, 5f, 0, RenderLib.drawLineSeq(mat, this.zappyPoints, 6f, 0,
outer, outer); outer, outer);
RenderLib.drawLineSeq(mat, this.zappyPoints, 2f, 0, RenderLib.drawLineSeq(mat, this.zappyPoints, 6f * 0.4f, 0,
innerDark, innerLight); innerDark, innerLight);
RenderLib.drawSpot(mat, this.zappyPoints.get(0), 2.5f, 1f, 0.1f, 0.15f, 0.6f); RenderLib.drawSpot(mat, this.zappyPoints.get(0), 2.5f, 1f, 0.1f, 0.15f, 0.6f);
for (var dot : this.pathfinderDots) { for (var dot : this.pathfinderDots) {
@ -104,7 +106,8 @@ public class PatternTooltipComponent implements ClientTooltipComponent {
RenderSystem.setShaderTexture(0, background); RenderSystem.setShaderTexture(0, background);
// x y blitoffset sw sh w h ... ? // x y blitoffset sw sh w h ... ?
// parchment doesn't have this mapped // 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 @Override

View file

@ -53,7 +53,7 @@ public class EntityWallScroll extends HangingEntity {
} }
public EntityWallScroll(Level world, BlockPos pos, Direction dir, ItemStack scroll, boolean showStrokeOrder, public EntityWallScroll(Level world, BlockPos pos, Direction dir, ItemStack scroll, boolean showStrokeOrder,
int blockSize) { int blockSize) {
super(HexEntities.WALL_SCROLL, world, pos); super(HexEntities.WALL_SCROLL, world, pos);
this.setDirection(dir); this.setDirection(dir);
this.blockSize = blockSize; this.blockSize = blockSize;
@ -72,8 +72,10 @@ public class EntityWallScroll extends HangingEntity {
var pair = RenderLib.getCenteredPattern(pattern, 128f / 3 * blockSize, 128f / 3 * blockSize, var pair = RenderLib.getCenteredPattern(pattern, 128f / 3 * blockSize, 128f / 3 * blockSize,
16f / 3 * blockSize); 16f / 3 * blockSize);
var dots = pair.getSecond(); 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, 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); 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, public void readSpawnData(BlockPos pos, Direction dir, ItemStack scrollItem,
boolean showsStrokeOrder, int blockSize) { boolean showsStrokeOrder, int blockSize) {
this.pos = pos; this.pos = pos;
this.scroll = scrollItem; this.scroll = scrollItem;
this.blockSize = blockSize; this.blockSize = blockSize;
@ -201,7 +203,7 @@ public class EntityWallScroll extends HangingEntity {
@Override @Override
public void lerpTo(double pX, double pY, double pZ, float pYaw, float pPitch, int pPosRotationIncrements, 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()); BlockPos blockpos = this.pos.offset(pX - this.getX(), pY - this.getY(), pZ - this.getZ());
this.setPos(blockpos.getX(), blockpos.getY(), blockpos.getZ()); this.setPos(blockpos.getX(), blockpos.getY(), blockpos.getZ());
} }

View file

@ -86,7 +86,10 @@ public class ItemSlate extends BlockItem implements IotaHolderItem {
@Override @Override
public boolean canWrite(ItemStack stack, Iota datum) { 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 @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.HexCoord;
import at.petrak.hexcasting.api.spell.math.HexPattern; 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.PatternDrawingUtil;
import at.petrak.hexcasting.interop.utils.PatternEntry; import at.petrak.hexcasting.interop.utils.PatternEntry;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
@ -22,7 +23,7 @@ abstract public class AbstractPatternComponent implements ICustomComponent {
protected transient float hexSize; protected transient float hexSize;
private transient List<PatternEntry> patterns; private transient List<PatternEntry> patterns;
private transient List<Vec2> pathfinderDots; private transient List<Vec2> zappyPoints;
/** /**
* Pass -1, -1 to center it. * Pass -1, -1 to center it.
@ -39,17 +40,20 @@ abstract public class AbstractPatternComponent implements ICustomComponent {
@Override @Override
public void render(PoseStack poseStack, IComponentRenderContext ctx, float partialTicks, int mouseX, int mouseY) { public void render(PoseStack poseStack, IComponentRenderContext ctx, float partialTicks, int mouseX, int mouseY) {
PatternDrawingUtil.drawPattern(poseStack, this.x, this.y, this.patterns, this.pathfinderDots, PatternDrawingUtil.drawPattern(poseStack, this.x, this.y, this.patterns, this.zappyPoints,
this.showStrokeOrder(), ctx.getTicksInBook(), this.showStrokeOrder(),
0xff_d2c8c8, 0xc8_aba2a2, 0xc8_322b33, 0x80_d1cccc); 0xff_d2c8c8, 0xc8_aba2a2, 0xc8_322b33, 0x80_d1cccc);
} }
@Override @Override
public void onVariablesAvailable(UnaryOperator<IVariable> lookup) { 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.hexSize = data.hexSize();
this.patterns = data.patterns(); this.patterns = data.patterns();
this.pathfinderDots = data.pathfinderDots(); this.zappyPoints = data.pathfinderDots();
} }
protected static class RawPattern { protected static class RawPattern {

View file

@ -19,7 +19,8 @@ import java.util.List;
public final class PatternDrawingUtil { public final class PatternDrawingUtil {
public static void drawPattern(PoseStack poseStack, int x, int y, List<PatternEntry> patterns, List<Vec2> dots, 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.pushPose();
poseStack.translate(x, y, 1); poseStack.translate(x, y, 1);
var mat = poseStack.last().pose(); var mat = poseStack.last().pose();
@ -59,7 +60,8 @@ public final class PatternDrawingUtil {
poseStack.popPose(); 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 patternEntries = new ArrayList<PatternEntry>(patterns.size());
var fakeScale = 1; var fakeScale = 1;
@ -103,12 +105,13 @@ public final class PatternDrawingUtil {
var realCom = HexUtils.findCenter(seenRealPoints); var realCom = HexUtils.findCenter(seenRealPoints);
// and NOW for real! // 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 localOrigin = HexUtils.coordToPx(pat.origin(), hexSize, realCom.negated());
var points = pat.pattern().toLines(hexSize, localOrigin); var points = pat.pattern().toLines(hexSize, localOrigin);
pat.zappyPoints() pat.zappyPoints()
.addAll(RenderLib.makeZappy(points, RenderLib.findDupIndices(pat.pattern().positions()), 10f, 0.8f, 0f, .addAll(RenderLib.makeZappy(points, RenderLib.findDupIndices(pat.pattern().positions()), 10, 0.8f, 0f,
0f)); 0f, readabilityOffset, lastLineLenProp, i));
} }
var pathfinderDots = seenCoords.stream() 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/x": "Vector Rfln. +X/-X",
"hexcasting.spell.book.hexcasting:const/vec/y": "Vector Rfln. +Y/-Y", "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: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:number": "Numerical Reflection",
"hexcasting.spell.book.hexcasting:mask": "Bookkeeper's Gambit", "hexcasting.spell.book.hexcasting:mask": "Bookkeeper's Gambit",
@ -340,7 +340,7 @@
"hexcasting.spell.hexcasting:rotate": "Rotation Gambit", "hexcasting.spell.hexcasting:rotate": "Rotation Gambit",
"hexcasting.spell.hexcasting:rotate_reverse": "Rotation Gambit II", "hexcasting.spell.hexcasting:rotate_reverse": "Rotation Gambit II",
"hexcasting.spell.hexcasting:duplicate": "Gemini Decomposition", "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:tuck": "Undertaker's Gambit",
"hexcasting.spell.hexcasting:2dup": "Dioscuri Gambit", "hexcasting.spell.hexcasting:2dup": "Dioscuri Gambit",
"hexcasting.spell.hexcasting:duplicate_n": "Gemini 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/x": "向量之精思,+X/-X型",
"hexcasting.spell.book.hexcasting:const/vec/y": "向量之精思,+Y/-Y型", "hexcasting.spell.book.hexcasting:const/vec/y": "向量之精思,+Y/-Y型",
"hexcasting.spell.book.hexcasting:const/vec/z": "向量之精思,+Z/-Z型", "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:number": "数字之精思",
"hexcasting.spell.book.hexcasting:mask": "簿记员之策略", "hexcasting.spell.book.hexcasting:mask": "簿记员之策略",
@ -336,12 +336,17 @@
"hexcasting.spell.hexcasting:zone_entity/not_player": "区域之馏化:非玩家", "hexcasting.spell.hexcasting:zone_entity/not_player": "区域之馏化:非玩家",
"hexcasting.spell.hexcasting:zone_entity/not_living": "区域之馏化:非生物", "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: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": "渔夫之策略",
"hexcasting.spell.hexcasting:fisherman/copy": "渔夫之策略,第二型",
"hexcasting.spell.hexcasting:swizzle": "骗徒之策略", "hexcasting.spell.hexcasting:swizzle": "骗徒之策略",
"hexcasting.spell.hexcasting:and_bit": "交叉之馏化", "hexcasting.spell.hexcasting:and_bit": "交叉之馏化",
@ -897,10 +902,15 @@
"hexcasting.page.stackmanip.pseudo-novice": "移除栈顶的 iota。", "hexcasting.page.stackmanip.pseudo-novice": "移除栈顶的 iota。",
"hexcasting.page.stackmanip.duplicate": "复制栈顶的 iota。", "hexcasting.page.stackmanip.duplicate": "复制栈顶的 iota。",
"hexcasting.page.stackmanip.swap": "交换栈顶两个 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.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.duplicate_n": "移除栈顶的数,然后将现在的栈顶元素复制该数次。(若所给数为 2则栈顶会有两个同一元素。",
"hexcasting.page.stackmanip.fisherman": "提出下标为所给数的元素并将其置于栈顶。", "hexcasting.page.stackmanip.fisherman": "提出下标为所给数的元素并将其置于栈顶。",
"hexcasting.page.stackmanip.fisherman/copy": "与$(action)渔夫之策略/$类似,但会复制 iota 而非将其提出。",
"hexcasting.page.stackmanip.stack_len": "以数的形式压入栈中元素的个数。(例如,一个形如 [0, 1] 的栈会变为 [0, 1, 2]。)", "hexcasting.page.stackmanip.stack_len": "以数的形式压入栈中元素的个数。(例如,一个形如 [0, 1] 的栈会变为 [0, 1, 2]。)",
"hexcasting.page.stackmanip.last_n_list": "移除$(italic)所给数/$个元素,然后压入一个由其组成的列表。", "hexcasting.page.stackmanip.last_n_list": "移除$(italic)所给数/$个元素,然后压入一个由其组成的列表。",
"hexcasting.page.stackmanip.splat": "移除栈顶的列表,然后依次压入其元素。", "hexcasting.page.stackmanip.splat": "移除栈顶的列表,然后依次压入其元素。",

View file

@ -36,7 +36,7 @@
"type": "hexcasting:pattern", "type": "hexcasting:pattern",
"op_id": "hexcasting:write/entity", "op_id": "hexcasting:write/entity",
"anchor": "hexcasting:write/entity", "anchor": "hexcasting:write/entity",
"input": "any, entity", "input": "entity, any",
"output": "", "output": "",
"text": "hexcasting.page.readwrite.write/entity" "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) { public PatternRendererEMI(ResourceLocation pattern, int w, int h) {
var entry = PatternRegistry.lookupPattern(pattern); var entry = PatternRegistry.lookupPattern(pattern);
this.strokeOrder = !entry.isPerWorld(); 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.patterns = data.patterns();
this.pathfinderDots = data.pathfinderDots(); this.pathfinderDots = data.pathfinderDots();
this.width = w; this.width = w;
@ -54,7 +55,7 @@ public class PatternRendererEMI implements EmiRenderable {
poseStack.pushPose(); poseStack.pushPose();
poseStack.translate(xOffset + x - 0.5f + width / 2f, yOffset + y + 1 + height / 2f, 0); poseStack.translate(xOffset + x - 0.5f + width / 2f, yOffset + y + 1 + height / 2f, 0);
poseStack.scale(width / 64f, height / 64f, 1f); 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); 0xff_333030, 0xff_191818, 0xc8_0c0a0c, 0x80_666363);
poseStack.popPose(); 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 // 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, private static <T> void bind(ResourceKey<Registry<T>> registry,
Consumer<BiConsumer<T, ResourceLocation>> source) { Consumer<BiConsumer<T, ResourceLocation>> source) {
getModEventBus().addListener((RegisterEvent event) -> { getModEventBus().addListener((RegisterEvent event) -> {
if (registry.equals(event.getRegistryKey())) { if (registry.equals(event.getRegistryKey())) {
source.accept((t, rl) -> event.register(registry, rl, () -> t)); source.accept((t, rl) -> event.register(registry, rl, () -> t));
@ -123,7 +123,7 @@ public class ForgeHexInitializer {
var modBus = getModEventBus(); var modBus = getModEventBus();
var evBus = MinecraftForge.EVENT_BUS; var evBus = MinecraftForge.EVENT_BUS;
modBus.register(ForgeHexClientInitializer.class); DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> modBus.register(ForgeHexClientInitializer.class));
modBus.addListener((FMLCommonSetupEvent evt) -> modBus.addListener((FMLCommonSetupEvent evt) ->
evt.enqueueWork(() -> { evt.enqueueWork(() -> {

View file

@ -26,7 +26,10 @@ public class PatternDrawable implements IDrawable {
public PatternDrawable(ResourceLocation pattern, int w, int h) { public PatternDrawable(ResourceLocation pattern, int w, int h) {
var entry = PatternRegistry.lookupPattern(pattern); var entry = PatternRegistry.lookupPattern(pattern);
this.strokeOrder = !entry.isPerWorld(); 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.patterns = data.patterns();
this.pathfinderDots = data.pathfinderDots(); this.pathfinderDots = data.pathfinderDots();
this.width = w; this.width = w;
@ -54,7 +57,7 @@ public class PatternDrawable implements IDrawable {
poseStack.pushPose(); poseStack.pushPose();
poseStack.translate(xOffset - 0.5f + width / 2f, yOffset + height / 2f, 0); poseStack.translate(xOffset - 0.5f + width / 2f, yOffset + height / 2f, 0);
poseStack.scale(width / 64f, height / 64f, 1f); 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); 0xff_333030, 0xff_191818, 0xc8_0c0a0c, 0x80_666363);
poseStack.popPose(); poseStack.popPose();
} }