diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/math/HexAngle.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/math/HexAngle.kt index bca8b0b94b..c5d98997c8 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/math/HexAngle.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/math/HexAngle.kt @@ -5,4 +5,19 @@ enum class HexAngle { fun rotatedBy(a: HexAngle) = values()[(this.ordinal + a.ordinal) % values().size] operator fun times(a: HexAngle) = this.rotatedBy(a) + + companion object { + fun fromChar(c: Char): HexAngle? { + return when (c) { + 'w' -> FORWARD + 'e' -> RIGHT + 'd' -> RIGHT_BACK + // for completeness ... + 's' -> BACK + 'a' -> LEFT_BACK + 'q' -> LEFT + else -> null + } + } + } } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/math/HexPattern.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/math/HexPattern.kt index 001cfdeab4..f491f90fca 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/math/HexPattern.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/math/HexPattern.kt @@ -9,6 +9,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.Tag import net.minecraft.world.phys.Vec2 +import kotlin.jvm.Throws /** * Sequence of angles to define a pattern traced. @@ -130,11 +131,12 @@ data class HexPattern(val startDir: HexDir, val angles: MutableList = const val TAG_ANGLES = "angles" @JvmField - val CODEC: Codec = RecordCodecBuilder.create({instance -> instance.group( - Codec.STRING.fieldOf(TAG_START_DIR).forGetter(HexPattern::anglesSignature), - HexDir.CODEC.fieldOf(TAG_ANGLES).forGetter(HexPattern::startDir) - ).apply(instance, HexPattern::fromAngles) - }) + val CODEC: Codec = RecordCodecBuilder.create { instance -> + instance.group( + Codec.STRING.fieldOf(TAG_START_DIR).forGetter(HexPattern::anglesSignature), + HexDir.CODEC.fieldOf(TAG_ANGLES).forGetter(HexPattern::startDir) + ).apply(instance, HexPattern::fromAnglesUnchecked) + } @JvmStatic fun isPattern(tag: CompoundTag): Boolean { @@ -149,30 +151,61 @@ data class HexPattern(val startDir: HexDir, val angles: MutableList = return HexPattern(startDir, angles.toMutableList()) } + /** + * Construct a [HexPattern] from an angle signature and starting direction. + * + * Throws if the signature contains an invalid character, or if the resulting pattern would contain overlaps. + */ @JvmStatic + @Throws(IllegalArgumentException::class, IllegalStateException::class) fun fromAngles(signature: String, startDir: HexDir): HexPattern { val out = HexPattern(startDir) + + var cursor = HexCoord.Origin var compass = startDir + val linesSeen = mutableSetOf( + cursor to compass, + cursor + compass to compass.rotatedBy(HexAngle.BACK), + ) for ((idx, c) in signature.withIndex()) { - val angle = when (c) { - 'w' -> HexAngle.FORWARD - 'e' -> HexAngle.RIGHT - 'd' -> HexAngle.RIGHT_BACK - // for completeness ... - 's' -> HexAngle.BACK - 'a' -> HexAngle.LEFT_BACK - 'q' -> HexAngle.LEFT - else -> throw IllegalArgumentException("Cannot match $c at idx $idx to a direction") - } + val angle = HexAngle.fromChar(c) + ?: throw IllegalArgumentException("Cannot match $c at idx $idx to a direction") + + cursor += compass compass *= angle - val success = out.tryAppendDir(compass) - if (!success) { + + if ( + !linesSeen.add(cursor to compass) + // Line from here to there also blocks there to here + || !linesSeen.add(cursor + compass to compass.rotatedBy(HexAngle.BACK)) + ) { throw IllegalStateException("Adding the angle $c at index $idx made the pattern invalid by looping back on itself") } + + out.angles.add(angle) } + return out } + /** + * Construct a [HexPattern] from an angle signature and starting direction, without checking for overlaps. + * + * Throws if the signature contains an invalid character. + */ + @JvmStatic + @Throws(IllegalArgumentException::class) + fun fromAnglesUnchecked(signature: String, startDir: HexDir): HexPattern { + val out = HexPattern(startDir) + + for ((idx, c) in signature.withIndex()) { + val angle = HexAngle.fromChar(c) + ?: throw IllegalArgumentException("Cannot match $c at idx $idx to a direction") + out.angles.add(angle) + } + + return out + } } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/PatternRegistryManifest.java b/Common/src/main/java/at/petrak/hexcasting/common/casting/PatternRegistryManifest.java index 66199bfba1..5d15f8fe42 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/PatternRegistryManifest.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/PatternRegistryManifest.java @@ -128,6 +128,6 @@ public static HexPattern getCanonicalStrokesPerWorld(ResourceKey t var found = save.lookupReverse(key); var signature = found.getFirst(); var startDir = found.getSecond().canonicalStartDir(); - var pat = HexPattern.fromAngles(signature, startDir); + var pat = HexPattern.fromAnglesUnchecked(signature, startDir); var tag = new CompoundTag(); tag.putString(ItemScroll.TAG_OP_ID, key.location().toString()); diff --git a/Common/src/main/java/at/petrak/hexcasting/common/loot/AddHexToAncientCypherFunc.java b/Common/src/main/java/at/petrak/hexcasting/common/loot/AddHexToAncientCypherFunc.java index aa0fa4af2b..b614d6a31b 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/loot/AddHexToAncientCypherFunc.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/loot/AddHexToAncientCypherFunc.java @@ -44,7 +44,7 @@ public static ItemStack doStatic(ItemStack stack, RandomSource rand) { var patsTag = new ListTag(); for (var patString : hex.getSecond()){ var pieces = patString.split(" "); - var pat = HexPattern.fromAngles(pieces[1],HexDir.fromString(pieces[0])); + var pat = HexPattern.fromAnglesUnchecked(pieces[1],HexDir.fromString(pieces[0])); patsTag.add(IotaType.serialize(new PatternIota(pat))); } diff --git a/Common/src/main/java/at/petrak/hexcasting/interop/inline/HexPatternMatcher.java b/Common/src/main/java/at/petrak/hexcasting/interop/inline/HexPatternMatcher.java index 55af9756e8..114b75101a 100644 --- a/Common/src/main/java/at/petrak/hexcasting/interop/inline/HexPatternMatcher.java +++ b/Common/src/main/java/at/petrak/hexcasting/interop/inline/HexPatternMatcher.java @@ -53,7 +53,7 @@ public Tuple getMatchAndGroup(MatchResult regexMatch, Matc angleSigs = ""; } try{ - pat = HexPattern.fromAngles(angleSigs.toLowerCase(), dir); + pat = HexPattern.fromAnglesUnchecked(angleSigs.toLowerCase(), dir); InlinePatternData patData = new InlinePatternData(pat); Style patDataStyle = patData.getExtraStyle(); if(sizeModString != null && sizeModString.equals("+")) diff --git a/Common/src/main/java/at/petrak/hexcasting/interop/patchouli/ManualPatternComponent.java b/Common/src/main/java/at/petrak/hexcasting/interop/patchouli/ManualPatternComponent.java index 06f77903ba..f737701fc7 100644 --- a/Common/src/main/java/at/petrak/hexcasting/interop/patchouli/ManualPatternComponent.java +++ b/Common/src/main/java/at/petrak/hexcasting/interop/patchouli/ManualPatternComponent.java @@ -33,7 +33,7 @@ public List getPatterns(UnaryOperator lookup) { RawPattern raw = new Gson().fromJson(json, RawPattern.class); var dir = HexDir.fromString(raw.startdir); - var pat = HexPattern.fromAngles(raw.signature, dir); + var pat = HexPattern.fromAnglesUnchecked(raw.signature, dir); out.add(pat); }