Merge branch 'main' into fancy-lightning

This commit is contained in:
Alwinfy 2022-05-25 18:46:00 -04:00
commit 0a85f08877
No known key found for this signature in database
GPG key ID: 2CCB99445F0C949E
90 changed files with 2003 additions and 852 deletions

29
.github/workflows/build_docs.yml vendored Normal file
View file

@ -0,0 +1,29 @@
name: Build the Python doc-gen
on:
push:
branches: [main]
jobs:
build_docs:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v3
- name: Generate file
run: doc/collate_data.py Common/src/main/resources hexcasting thehexbook doc/template.html index.html.uncommitted
- name: Check out gh-pages
uses: actions/checkout@v3
with:
clean: false
ref: gh-pages
- name: Overwrite file and commmit
run: |
mv index.html.uncommitted index.html
git config user.name "Documentation Generation Bot"
git config user.email "noreply@github.com"
git add index.html
git diff-index --quiet HEAD || git commit -m "Update docs at index.html from $GITHUB_REF"
- name: Upload changes
run: git push

View file

@ -86,14 +86,14 @@ object PatternRegistry {
// when we try to look
for (handler in specialHandlers) {
val op = handler.handler.handlePattern(pat)
if (op != null) return Pair(op, handler.id)
if (op != null) return op to handler.id
}
// Is it global?
val sig = pat.anglesSignature()
this.regularPatternLookup[sig]?.let {
val op = this.operatorLookup[it.opId] ?: throw MishapInvalidPattern()
return Pair(op, it.opId)
return op to it.opId
}
// Look it up in the world?
@ -102,7 +102,7 @@ object PatternRegistry {
ds.computeIfAbsent(Save.Companion::load, { Save.create(overworld.seed) }, TAG_SAVED_DATA)
perWorldPatterns.lookup[sig]?.let {
val op = this.operatorLookup[it.first]!!
return Pair(op, it.first)
return op to it.first
}
throw MishapInvalidPattern()
@ -129,7 +129,7 @@ object PatternRegistry {
}
for ((sig, entry) in this.regularPatternLookup) {
if (entry.opId == opId) {
val pattern = HexPattern.FromAnglesSig(sig, entry.preferredStart)
val pattern = HexPattern.fromAngles(sig, entry.preferredStart)
return PatternEntry(pattern, this.operatorLookup[entry.opId]!!, false)
}
}
@ -188,7 +188,7 @@ object PatternRegistry {
val entry = tag.getCompound(sig)
val opId = ResourceLocation.tryParse(entry.getString(TAG_OP_ID))!!
val startDir = HexDir.values()[entry.getInt(TAG_START_DIR)]
map[sig] = Pair(opId, startDir)
map[sig] = opId to startDir
}
return Save(map)
}
@ -199,7 +199,7 @@ object PatternRegistry {
for ((opId, entry) in PatternRegistry.perWorldPatternLookup) {
// waugh why doesn't kotlin recursively destructure things
val scrungled = EulerPathFinder.findAltDrawing(entry.prototype, seed)
map[scrungled.anglesSignature()] = Pair(opId, scrungled.startDir)
map[scrungled.anglesSignature()] = opId to scrungled.startDir
}
val save = Save(map)
save.setDirty()

View file

@ -13,7 +13,7 @@ public interface DataHolder {
default SpellDatum<?> readDatum(ServerLevel world) {
var tag = readRawDatum();
if (tag != null) {
return SpellDatum.DeserializeFromNBT(tag, world);
return SpellDatum.fromNBT(tag, world);
} else {
return null;
}

View file

@ -132,7 +132,7 @@ public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implemen
if (this.activator != null && this.colorizer != null && this.nextBlock != null && this.trackedBlocks != null) {
tag.putUUID(TAG_ACTIVATOR, this.activator);
tag.put(TAG_NEXT_BLOCK, NbtUtils.writeBlockPos(this.nextBlock));
tag.put(TAG_COLORIZER, this.colorizer.serialize());
tag.put(TAG_COLORIZER, this.colorizer.serializeToNBT());
tag.putBoolean(TAG_FOUND_ALL, this.foundAll);
var trackeds = new ListTag();
@ -155,7 +155,7 @@ public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implemen
tag.contains(TAG_NEXT_BLOCK, Tag.TAG_COMPOUND) &&
tag.contains(TAG_TRACKED_BLOCKS, Tag.TAG_LIST)) {
this.activator = tag.getUUID(TAG_ACTIVATOR);
this.colorizer = FrozenColorizer.deserialize(tag.getCompound(TAG_COLORIZER));
this.colorizer = FrozenColorizer.fromNBT(tag.getCompound(TAG_COLORIZER));
this.nextBlock = NbtUtils.readBlockPos(tag.getCompound(TAG_NEXT_BLOCK));
this.foundAll = tag.getBoolean(TAG_FOUND_ALL);
var trackeds = tag.getList(TAG_TRACKED_BLOCKS, Tag.TAG_COMPOUND);
@ -526,11 +526,6 @@ public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implemen
}
}
@Override
public void setChanged() {
this.sync();
}
@Override
public boolean stillValid(Player player) {
return false;

View file

@ -28,7 +28,7 @@ public interface DataHolderItem {
var tag = dh.readDatumTag(stack);
if (tag != null) {
return SpellDatum.DeserializeFromNBT(tag, world);
return SpellDatum.fromNBT(tag, world);
} else {
return null;
}
@ -47,7 +47,7 @@ public interface DataHolderItem {
TooltipFlag pIsAdvanced) {
var datumTag = self.readDatumTag(pStack);
if (datumTag != null) {
var component = SpellDatum.DisplayFromTag(datumTag);
var component = SpellDatum.displayFromNBT(datumTag);
pTooltipComponents.add(new TranslatableComponent("hexcasting.spelldata.onitem", component));
if (pIsAdvanced.isAdvanced()) {

View file

@ -29,14 +29,14 @@ public record FrozenColorizer(ItemStack item, UUID owner) {
public static final Supplier<FrozenColorizer> DEFAULT =
() -> new FrozenColorizer(new ItemStack(HexItems.DYE_COLORIZERS.get(DyeColor.WHITE)), Util.NIL_UUID);
public CompoundTag serialize() {
public CompoundTag serializeToNBT() {
var out = new CompoundTag();
out.put(TAG_STACK, HexUtils.serialize(this.item));
out.put(TAG_STACK, HexUtils.serializeToNBT(this.item));
out.putUUID(TAG_OWNER, this.owner);
return out;
}
public static FrozenColorizer deserialize(CompoundTag tag) {
public static FrozenColorizer fromNBT(CompoundTag tag) {
if (tag.isEmpty()) {
return FrozenColorizer.DEFAULT.get();
}

View file

@ -12,7 +12,7 @@ import net.minecraft.world.entity.Entity
import net.minecraft.world.phys.Vec3
import kotlin.math.abs
fun GetNumOrVec(datum: SpellDatum<*>, reverseIdx: Int): Either<Double, Vec3> =
fun numOrVec(datum: SpellDatum<*>, reverseIdx: Int): Either<Double, Vec3> =
when (datum.payload) {
is Double -> Either.left(datum.payload)
is Vec3 -> Either.right(datum.payload)
@ -23,7 +23,7 @@ fun GetNumOrVec(datum: SpellDatum<*>, reverseIdx: Int): Either<Double, Vec3> =
)
}
fun GetNumOrList(datum: SpellDatum<*>, reverseIdx: Int): Either<Double, SpellList> =
fun numOrList(datum: SpellDatum<*>, reverseIdx: Int): Either<Double, SpellList> =
when (datum.payload) {
is Double -> Either.left(datum.payload)
is SpellList -> Either.right(datum.payload)

View file

@ -9,12 +9,12 @@ import net.minecraft.world.phys.Vec3
data class ParticleSpray(val pos: Vec3, val vel: Vec3, val fuzziness: Double, val spread: Double, val count: Int = 20) {
companion object {
@JvmStatic
fun Burst(pos: Vec3, size: Double, count: Int = 20): ParticleSpray {
fun burst(pos: Vec3, size: Double, count: Int = 20): ParticleSpray {
return ParticleSpray(pos, Vec3(size, 0.0, 0.0), 0.0, 3.14, count)
}
@JvmStatic
fun Cloud(pos: Vec3, size: Double, count: Int = 20): ParticleSpray {
fun cloud(pos: Vec3, size: Double, count: Int = 20): ParticleSpray {
return ParticleSpray(pos, Vec3(0.0, 0.001, 0.0), size, 0.0, count)
}
}

View file

@ -3,11 +3,11 @@ package at.petrak.hexcasting.api.spell
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.math.HexPattern
import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidSpellDatumType
import at.petrak.hexcasting.api.utils.HexUtils
import at.petrak.hexcasting.api.utils.HexUtils.serializeToNBT
import at.petrak.hexcasting.api.utils.getList
import at.petrak.hexcasting.api.utils.*
import net.minecraft.ChatFormatting
import net.minecraft.nbt.*
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtUtils
import net.minecraft.nbt.Tag
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.TextComponent
import net.minecraft.network.chat.TranslatableComponent
@ -22,7 +22,7 @@ import java.util.*
* We use the following types:
* * [Entity]
* * [Double]
* * [Vec3][net.minecraft.world.phys.Vec3] as both position and (when normalized) direction
* * [Vec3] as both position and (when normalized) direction
* * [Widget]; [Widget.NULL] is used as our null value
* * [SpellList]
* * [HexPattern]! Yes, we have meta-evaluation everyone.
@ -33,39 +33,19 @@ import java.util.*
class SpellDatum<T : Any> private constructor(val payload: T) {
val clazz: Class<T> = payload.javaClass
fun serializeToNBT(): CompoundTag {
val out = CompoundTag()
when (val pl = this.payload) {
is Entity -> {
val subtag = CompoundTag()
subtag.put(TAG_ENTITY_UUID, NbtUtils.createUUID(pl.uuid))
// waayyghg
val json = Component.Serializer.toJson(pl.displayName)
subtag.putString(TAG_ENTITY_NAME_CHEATY, json)
out.put(TAG_ENTITY, subtag)
}
is Double -> out.put(
TAG_DOUBLE, DoubleTag.valueOf(pl)
)
is Vec3 -> out.put(
TAG_VEC3, pl.serializeToNBT()
)
is SpellList -> {
val subtag = ListTag()
for (elt in pl)
subtag.add(elt.serializeToNBT())
out.put(TAG_LIST, subtag)
}
is Widget -> {
out.putString(TAG_WIDGET, pl.name)
}
is HexPattern -> {
out.put(TAG_PATTERN, pl.serializeToNBT())
fun serializeToNBT() = NBTBuilder {
when (val pl = payload) {
is Entity -> TAG_ENTITY %= compound {
TAG_ENTITY_UUID %= NbtUtils.createUUID(pl.uuid)
TAG_ENTITY_NAME_CHEATY %= Component.Serializer.toJson(pl.displayName)
}
is Double -> TAG_DOUBLE %= pl
is Vec3 -> TAG_VEC3 %= pl.serializeToNBT()
is SpellList -> TAG_LIST %= pl.serializeToNBT()
is Widget -> TAG_WIDGET %= pl.name
is HexPattern -> TAG_PATTERN %= pl.serializeToNBT()
else -> throw RuntimeException("cannot serialize $pl because it is of type ${pl.javaClass.canonicalName} which is not serializable")
}
return out
}
override fun toString(): String =
@ -85,7 +65,7 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
fun display(): Component {
val nbt = this.serializeToNBT()
return DisplayFromTag(nbt)
return displayFromNBT(nbt)
}
fun getType(): DatumType =
@ -101,6 +81,7 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
companion object {
@JvmStatic
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
fun make(payload: Any): SpellDatum<*> =
if (payload is SpellDatum<*>) {
payload
@ -114,23 +95,23 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
} else if (payload is Vec3) {
SpellDatum(
Vec3(
HexUtils.FixNANs(payload.x),
HexUtils.FixNANs(payload.y),
HexUtils.FixNANs(payload.z),
fixNAN(payload.x),
fixNAN(payload.y),
fixNAN(payload.z),
)
)
} else if (IsValidType(payload)) {
} else if (isValidType(payload)) {
SpellDatum(payload)
} else if (payload is java.lang.Double) {
// Check to see if it's a java *boxed* double, for when we call this from Java
val num = payload.toDouble()
SpellDatum(HexUtils.FixNANs(num))
SpellDatum(fixNAN(num))
} else {
throw MishapInvalidSpellDatumType(payload)
}
@JvmStatic
fun DeserializeFromNBT(nbt: CompoundTag, world: ServerLevel): SpellDatum<*> {
fun fromNBT(nbt: CompoundTag, world: ServerLevel): SpellDatum<*> {
val keys = nbt.allKeys
if (keys.size != 1)
throw IllegalArgumentException("Expected exactly one kv pair: $nbt")
@ -144,21 +125,15 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
SpellDatum(if (entity == null || !entity.isAlive) Widget.NULL else entity)
}
TAG_DOUBLE -> SpellDatum(nbt.getDouble(key))
TAG_VEC3 -> SpellDatum(HexUtils.DeserializeVec3FromNBT(nbt.getLongArray(key)))
TAG_VEC3 -> SpellDatum(vecFromNBT(nbt.getLongArray(key)))
TAG_LIST -> {
val arr = nbt.getList(key, Tag.TAG_COMPOUND)
val out = ArrayList<SpellDatum<*>>(arr.size)
for (subtag in arr) {
// this is safe because otherwise we wouldn't have been able to get the list before
out.add(DeserializeFromNBT(subtag as CompoundTag, world))
}
SpellDatum(SpellList.LList(0, out))
SpellDatum(SpellList.fromNBT(nbt.getList(key, Tag.TAG_COMPOUND), world))
}
TAG_WIDGET -> {
SpellDatum(Widget.valueOf(nbt.getString(key)))
}
TAG_PATTERN -> {
SpellDatum(HexPattern.DeserializeFromNBT(nbt.getCompound(TAG_PATTERN)))
SpellDatum(HexPattern.fromNBT(nbt.getCompound(TAG_PATTERN)))
}
else -> throw IllegalArgumentException("Unknown key $key: $nbt")
}
@ -171,11 +146,11 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
)
)
@JvmStatic
fun DeserializeFromNBT(nbt: CompoundTag, ctx: CastingContext): SpellDatum<*> =
DeserializeFromNBT(nbt, ctx.world)
fun fromNBT(nbt: CompoundTag, ctx: CastingContext): SpellDatum<*> =
fromNBT(nbt, ctx.world)
@JvmStatic
fun DisplayFromTag(nbt: CompoundTag): Component {
fun displayFromNBT(nbt: CompoundTag): Component {
val keys = nbt.allKeys
if (keys.size != 1)
throw IllegalArgumentException("Expected exactly one kv pair: $nbt")
@ -188,7 +163,7 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
)
).withStyle(ChatFormatting.GREEN)
TAG_VEC3 -> {
val vec = HexUtils.DeserializeVec3FromNBT(nbt.getLongArray(key))
val vec = vecFromNBT(nbt.getLongArray(key))
// the focus color is really more red, but we don't want to show an error-y color
TextComponent(
String.format(
@ -205,7 +180,7 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
val arr = nbt.getList(key, Tag.TAG_COMPOUND)
for ((i, subtag) in arr.withIndex()) {
// this is safe because otherwise we wouldn't have been able to get the list before
out.append(DisplayFromTag(subtag as CompoundTag))
out.append(displayFromNBT(subtag as CompoundTag))
if (i != arr.lastIndex) {
out.append(", ")
}
@ -223,7 +198,7 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
else TextComponent(widget.toString()).withStyle(ChatFormatting.DARK_PURPLE)
}
TAG_PATTERN -> {
val pat = HexPattern.DeserializeFromNBT(nbt.getCompound(TAG_PATTERN))
val pat = HexPattern.fromNBT(nbt.getCompound(TAG_PATTERN))
var angleDesc = pat.anglesSignature()
if (angleDesc.isNotBlank()) angleDesc = " $angleDesc";
val out = TextComponent("HexPattern(").withStyle(ChatFormatting.GOLD)
@ -267,11 +242,11 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
// Also encode the entity's name as a component for the benefit of the client
const val TAG_ENTITY_NAME_CHEATY = "name"
fun <T : Any> IsValidType(checkee: T): Boolean =
fun <T : Any> isValidType(checkee: T): Boolean =
ValidTypes.any { clazz -> clazz.isAssignableFrom(checkee.javaClass) }
@JvmStatic
fun GetTagName(datumType: DatumType): String {
fun tagForType(datumType: DatumType): String {
return when (datumType) {
DatumType.ENTITY -> TAG_ENTITY
DatumType.WIDGET -> TAG_WIDGET

View file

@ -1,5 +1,9 @@
package at.petrak.hexcasting.api.spell
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.server.level.ServerLevel
/**
* Restricted interface for functional lists.
*
@ -73,4 +77,16 @@ sealed class SpellList: Iterable<SpellDatum<*>> {
return car
}
}
companion object {
@JvmStatic
fun fromNBT(nbt: ListTag, world: ServerLevel): LList {
val out = ArrayList<SpellDatum<*>>(nbt.size)
for (subtag in nbt) {
// this is safe because otherwise we wouldn't have been able to get the list before
out.add(SpellDatum.fromNBT(subtag as CompoundTag, world))
}
return LList(0, out)
}
}
}

View file

@ -6,7 +6,7 @@ import at.petrak.hexcasting.api.spell.Operator
import at.petrak.hexcasting.api.spell.mishaps.MishapEntityTooFarAway
import at.petrak.hexcasting.api.spell.mishaps.MishapEvalTooDeep
import at.petrak.hexcasting.api.spell.mishaps.MishapLocationTooFarAway
import at.petrak.hexcasting.api.utils.HexUtils
import at.petrak.hexcasting.api.utils.otherHand
import at.petrak.hexcasting.xplat.IXplatAbstractions
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
@ -33,7 +33,7 @@ data class CastingContext(
private var depth: Int = 0
val world: ServerLevel get() = caster.getLevel()
val otherHand: InteractionHand get() = HexUtils.OtherHand(this.castingHand)
val otherHand: InteractionHand get() = otherHand(this.castingHand)
val position: Vec3 get() = caster.position()
private val entitiesGivenMotion = mutableSetOf<Entity>()

View file

@ -12,12 +12,9 @@ import at.petrak.hexcasting.api.spell.*
import at.petrak.hexcasting.api.spell.math.HexDir
import at.petrak.hexcasting.api.spell.math.HexPattern
import at.petrak.hexcasting.api.spell.mishaps.*
import at.petrak.hexcasting.api.utils.ManaHelper
import at.petrak.hexcasting.api.utils.asCompound
import at.petrak.hexcasting.api.utils.getList
import at.petrak.hexcasting.api.utils.*
import at.petrak.hexcasting.xplat.IXplatAbstractions
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.Tag
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
@ -57,7 +54,7 @@ class CastingHarness private constructor(
// Initialize the continuation stack to a single top-level eval for all iotas.
var continuation = SpellContinuation.Done.pushFrame(ContinuationFrame.Evaluate(SpellList.LList(0, iotas)))
// Begin aggregating info
val info = TempControllerInfo(false, false)
val info = TempControllerInfo(playSound = false, earlyExit = false)
var lastResolutionType = ResolvedPatternType.UNRESOLVED
while (continuation is SpellContinuation.NotDone && !info.earlyExit) {
// Take the top of the continuation stack...
@ -388,10 +385,10 @@ class CastingHarness private constructor(
}
if (casterStack.`is`(HexItemTags.WANDS) || ipsCanDrawFromInv) {
val manableItems = this.ctx.caster.inventory.items
.filter(ManaHelper::isManaItem)
.sortedWith(Comparator(ManaHelper::compare).reversed())
.filter(::isManaItem)
.sortedWith(Comparator(::compareManaItem).reversed())
for (stack in manableItems) {
costLeft -= ManaHelper.extractMana(stack, costLeft)
costLeft -= extractMana(stack, costLeft)
if (costLeft <= 0)
break
}
@ -431,29 +428,17 @@ class CastingHarness private constructor(
}
fun serializeToNBT(): CompoundTag {
val out = CompoundTag()
fun serializeToNBT() = NBTBuilder {
TAG_STACK %= stack.serializeToNBT()
val stackTag = ListTag()
for (datum in this.stack)
stackTag.add(datum.serializeToNBT())
out.put(TAG_STACK, stackTag)
TAG_LOCAL %= localIota.serializeToNBT()
TAG_PAREN_COUNT %= parenCount
TAG_ESCAPE_NEXT %= escapeNext
out.put(TAG_LOCAL, localIota.serializeToNBT())
TAG_PARENTHESIZED %= parenthesized.serializeToNBT()
out.putInt(TAG_PAREN_COUNT, this.parenCount)
out.putBoolean(TAG_ESCAPE_NEXT, this.escapeNext)
val parensTag = ListTag()
for (pat in this.parenthesized)
parensTag.add(pat.serializeToNBT())
out.put(TAG_PARENTHESIZED, parensTag)
if (this.prepackagedColorizer != null) {
out.put(TAG_PREPACKAGED_COLORIZER, this.prepackagedColorizer.serialize())
}
return out
if (prepackagedColorizer != null)
TAG_PREPACKAGED_COLORIZER %= prepackagedColorizer.serializeToNBT()
}
@ -466,18 +451,18 @@ class CastingHarness private constructor(
const val TAG_PREPACKAGED_COLORIZER = "prepackaged_colorizer"
@JvmStatic
fun DeserializeFromNBT(nbt: CompoundTag, ctx: CastingContext): CastingHarness {
fun fromNBT(nbt: CompoundTag, ctx: CastingContext): CastingHarness {
return try {
val stack = mutableListOf<SpellDatum<*>>()
val stackTag = nbt.getList(TAG_STACK, Tag.TAG_COMPOUND)
for (subtag in stackTag) {
val datum = SpellDatum.DeserializeFromNBT(subtag.asCompound, ctx.world)
val datum = SpellDatum.fromNBT(subtag.asCompound, ctx.world)
stack.add(datum)
}
val localTag = nbt.getCompound(TAG_LOCAL)
val localIota =
if (localTag.size() == 1) SpellDatum.DeserializeFromNBT(localTag, ctx.world) else SpellDatum.make(
if (localTag.size() == 1) SpellDatum.fromNBT(localTag, ctx.world) else SpellDatum.make(
Widget.NULL
)
@ -485,16 +470,16 @@ class CastingHarness private constructor(
val parenTag = nbt.getList(TAG_PARENTHESIZED, Tag.TAG_COMPOUND)
for (subtag in parenTag) {
if (subtag.asCompound.size() != 1)
parenthesized.add(SpellDatum.make(HexPattern.DeserializeFromNBT(subtag.asCompound)))
parenthesized.add(SpellDatum.make(HexPattern.fromNBT(subtag.asCompound)))
else
parenthesized.add(SpellDatum.DeserializeFromNBT(subtag.asCompound, ctx.world))
parenthesized.add(SpellDatum.fromNBT(subtag.asCompound, ctx.world))
}
val parenCount = nbt.getInt(TAG_PAREN_COUNT)
val escapeNext = nbt.getBoolean(TAG_ESCAPE_NEXT)
val colorizer = if (nbt.contains(TAG_PREPACKAGED_COLORIZER)) {
FrozenColorizer.deserialize(nbt.getCompound(TAG_PREPACKAGED_COLORIZER))
FrozenColorizer.fromNBT(nbt.getCompound(TAG_PREPACKAGED_COLORIZER))
} else {
null
}

View file

@ -3,6 +3,12 @@ package at.petrak.hexcasting.api.spell.casting
import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.SpellList
import at.petrak.hexcasting.api.spell.casting.CastingHarness.CastResult
import at.petrak.hexcasting.api.utils.NBTBuilder
import at.petrak.hexcasting.api.utils.getList
import at.petrak.hexcasting.api.utils.hasList
import at.petrak.hexcasting.api.utils.serializeToNBT
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.server.level.ServerLevel
/**
@ -32,13 +38,18 @@ sealed interface ContinuationFrame {
*/
fun breakDownwards(stack: List<SpellDatum<*>>): Pair<Boolean, List<SpellDatum<*>>>
/**
* Serializes this frame. Used for things like delays, where we pause execution.
*/
fun serializeToNBT(): CompoundTag
/**
* A list of patterns to be evaluated in sequence.
* @property list the *remaining* list of patterns to be evaluated
*/
data class Evaluate(val list: SpellList) : ContinuationFrame {
// Discard this frame and keep discarding frames.
override fun breakDownwards(stack: List<SpellDatum<*>>) = Pair(false, stack)
override fun breakDownwards(stack: List<SpellDatum<*>>) = false to stack
// Step the list of patterns, evaluating a single one.
override fun evaluate(
@ -60,15 +71,19 @@ sealed interface ContinuationFrame {
}
}
override fun serializeToNBT() = NBTBuilder {
"type" %= "evaluate"
"patterns" %= list.serializeToNBT()
}
}
/**
* A stack marker representing the end of a Hermes evaluation,
* so that we know when to stop removing frames during a Halt.
*/
class FinishEval() : ContinuationFrame {
object FinishEval : ContinuationFrame {
// Don't do anything else to the stack, just finish the halt statement.
override fun breakDownwards(stack: List<SpellDatum<*>>) = Pair(true, stack)
override fun breakDownwards(stack: List<SpellDatum<*>>) = true to stack
// Evaluating it does nothing; it's only a boundary condition.
override fun evaluate(
@ -83,6 +98,10 @@ sealed interface ContinuationFrame {
listOf()
)
}
override fun serializeToNBT() = NBTBuilder {
"type" %= "end"
}
}
/**
@ -103,10 +122,10 @@ sealed interface ContinuationFrame {
/** When halting, we add the stack state at halt to the stack accumulator, then return the original pre-Thoth stack, plus the accumulator. */
override fun breakDownwards(stack: List<SpellDatum<*>>): Pair<Boolean, List<SpellDatum<*>>> {
val newStack = baseStack!!.toMutableList()
val newStack = baseStack?.toMutableList() ?: mutableListOf()
acc.addAll(stack)
newStack.add(SpellDatum.make(acc))
return Pair(true, newStack)
return true to newStack
}
/** Step the Thoth computation, enqueueing one code evaluation. */
@ -127,17 +146,15 @@ sealed interface ContinuationFrame {
// If we still have data to process...
val (stackTop, newCont) = if (data.nonEmpty) {
Pair(
data.car, // Push the next datum to the top of the stack,
continuation
// put the next Thoth object back on the stack for the next Thoth cycle,
.pushFrame(ForEach(data.cdr, code, stack, acc))
// and prep the Thoth'd code block for evaluation.
.pushFrame(Evaluate(code))
)
// Push the next datum to the top of the stack,
data.car to continuation
// put the next Thoth object back on the stack for the next Thoth cycle,
.pushFrame(ForEach(data.cdr, code, stack, acc))
// and prep the Thoth'd code block for evaluation.
.pushFrame(Evaluate(code))
} else {
// Else, dump our final list onto the stack.
Pair(SpellDatum.make(acc), continuation)
SpellDatum.make(acc) to continuation
}
val tStack = stack.toMutableList()
tStack.add(stackTop)
@ -148,5 +165,31 @@ sealed interface ContinuationFrame {
listOf()
)
}
override fun serializeToNBT() = NBTBuilder {
"type" %= "foreach"
"data" %= data.serializeToNBT()
"code" %= code.serializeToNBT()
if (baseStack != null)
"base" %= baseStack.serializeToNBT()
"accumulator" %= acc.serializeToNBT()
}
}
companion object {
@JvmStatic
fun fromNBT(tag: CompoundTag, world: ServerLevel): ContinuationFrame {
return when (tag.getString("type")) {
"eval" -> Evaluate(SpellList.fromNBT(tag.getList("patterns", Tag.TAG_COMPOUND), world))
"end" -> FinishEval
"foreach" -> ForEach(
SpellList.fromNBT(tag.getList("data", Tag.TAG_COMPOUND), world),
SpellList.fromNBT(tag.getList("code", Tag.TAG_COMPOUND), world),
if (tag.hasList("base", Tag.TAG_COMPOUND)) SpellList.fromNBT(tag.getList("base", Tag.TAG_COMPOUND), world).toList() else null,
SpellList.fromNBT(tag.getList("accumulator", Tag.TAG_COMPOUND), world).toMutableList()
)
else -> Evaluate(SpellList.LList(0, listOf()));
}
}
}
}

View file

@ -4,8 +4,6 @@ import at.petrak.hexcasting.api.spell.SpellDatum
/**
* A change to the data in a CastHarness after a pattern is drawn.
*
* [wasThisPatternInvalid] is for the benefit of the controller.
*/
data class FunctionalData(
val stack: List<SpellDatum<*>>,

View file

@ -2,24 +2,23 @@ package at.petrak.hexcasting.api.spell.casting
import at.petrak.hexcasting.api.spell.math.HexCoord
import at.petrak.hexcasting.api.spell.math.HexPattern
import at.petrak.hexcasting.api.utils.NBTBuilder
import net.minecraft.nbt.CompoundTag
import java.util.*
data class ResolvedPattern(val pattern: HexPattern, val origin: HexCoord, var type: ResolvedPatternType) {
fun serializeToNBT(): CompoundTag {
val tag = CompoundTag()
tag.put("Pattern", pattern.serializeToNBT())
tag.putInt("OriginQ", origin.q)
tag.putInt("OriginR", origin.r)
tag.putString("Valid", type.name.lowercase(Locale.ROOT))
return tag
fun serializeToNBT() = NBTBuilder {
"Pattern" %= pattern.serializeToNBT()
"OriginQ" %= origin.q
"OriginR" %= origin.r
"Valid" %= type.name.lowercase(Locale.ROOT)
}
companion object {
@JvmStatic
fun DeserializeFromNBT(tag: CompoundTag): ResolvedPattern {
val pattern = HexPattern.DeserializeFromNBT(tag.getCompound("Pattern"))
fun fromNBT(tag: CompoundTag): ResolvedPattern {
val pattern = HexPattern.fromNBT(tag.getCompound("Pattern"))
val origin = HexCoord(tag.getInt("OriginQ"), tag.getInt("OriginR"))
val valid = try {
ResolvedPatternType.valueOf(tag.getString("Valid").uppercase(Locale.ROOT))

View file

@ -1,5 +1,6 @@
package at.petrak.hexcasting.api.spell.casting
import at.petrak.hexcasting.api.utils.NBTBuilder
import net.minecraft.core.BlockPos
import net.minecraft.nbt.CompoundTag
import net.minecraft.world.phys.AABB
@ -8,23 +9,19 @@ import net.minecraft.world.phys.AABB
* Optional field on a [CastingContext] for the spell circle
*/
data class SpellCircleContext(val impetusPos: BlockPos, val aabb: AABB, val activatorAlwaysInRange: Boolean) {
fun serializeToNBT(): CompoundTag {
val out = CompoundTag()
fun serializeToNBT() = NBTBuilder {
TAG_IMPETUS_X %= impetusPos.x
TAG_IMPETUS_Y %= impetusPos.y
TAG_IMPETUS_Z %= impetusPos.z
out.putInt(TAG_IMPETUS_X, impetusPos.x)
out.putInt(TAG_IMPETUS_Y, impetusPos.y)
out.putInt(TAG_IMPETUS_Z, impetusPos.z)
TAG_MIN_X %= aabb.minX
TAG_MIN_Y %= aabb.minY
TAG_MIN_Z %= aabb.minZ
TAG_MAX_X %= aabb.maxX
TAG_MAX_Y %= aabb.maxY
TAG_MAX_Z %= aabb.maxZ
out.putDouble(TAG_MIN_X, aabb.minX)
out.putDouble(TAG_MIN_Y, aabb.minY)
out.putDouble(TAG_MIN_Z, aabb.minZ)
out.putDouble(TAG_MAX_X, aabb.maxX)
out.putDouble(TAG_MAX_Y, aabb.maxY)
out.putDouble(TAG_MAX_Z, aabb.maxZ)
out.putBoolean(TAG_PLAYER_ALWAYS_IN_RANGE, activatorAlwaysInRange)
return out
TAG_PLAYER_ALWAYS_IN_RANGE %= activatorAlwaysInRange
}
companion object {
@ -39,7 +36,7 @@ data class SpellCircleContext(val impetusPos: BlockPos, val aabb: AABB, val acti
const val TAG_MAX_Z = "max_z"
const val TAG_PLAYER_ALWAYS_IN_RANGE = "player_always_in_range"
fun DeserializeFromNBT(tag: CompoundTag): SpellCircleContext {
fun fromNBT(tag: CompoundTag): SpellCircleContext {
val impX = tag.getInt(TAG_IMPETUS_X)
val impY = tag.getInt(TAG_IMPETUS_Y)
val impZ = tag.getInt(TAG_IMPETUS_Z)

View file

@ -1,8 +1,8 @@
package at.petrak.hexcasting.api.spell.math
import at.petrak.hexcasting.api.utils.HexUtils
import net.minecraft.nbt.ByteArrayTag
import net.minecraft.nbt.ByteTag
import at.petrak.hexcasting.api.utils.NBTBuilder
import at.petrak.hexcasting.api.utils.coordToPx
import at.petrak.hexcasting.api.utils.findCenter
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.world.phys.Vec2
@ -23,15 +23,15 @@ data class HexPattern(public val startDir: HexDir, public val angles: MutableLis
var compass = this.startDir
var cursor = HexCoord.Origin
for (a in this.angles) {
linesSeen.add(Pair(cursor, compass))
linesSeen.add(cursor to compass)
// Line from here to there also blocks there to here
linesSeen.add(Pair(cursor + compass, compass.rotatedBy(HexAngle.BACK)))
linesSeen.add(cursor + compass to compass.rotatedBy(HexAngle.BACK))
cursor += compass
compass *= a
}
cursor += compass
val potentialNewLine = Pair(cursor, newDir)
val potentialNewLine = cursor to newDir
if (potentialNewLine in linesSeen) return false
val nextAngle = newDir - compass
if (nextAngle == HexAngle.BACK) return false
@ -71,12 +71,9 @@ data class HexPattern(public val startDir: HexDir, public val angles: MutableLis
this.angles.fold(this.startDir) { acc, angle -> acc * angle }
fun serializeToNBT(): CompoundTag {
val out = CompoundTag()
out.put(TAG_START_DIR, ByteTag.valueOf(this.startDir.ordinal.toByte()))
val anglesTag = ByteArrayTag(this.angles.map { it.ordinal.toByte() })
out.put(TAG_ANGLES, anglesTag)
return out
fun serializeToNBT() = NBTBuilder {
TAG_START_DIR %= byte(startDir.ordinal)
TAG_ANGLES %= byteArray(angles.map(HexAngle::ordinal))
}
// Terrible shorthand method for easy matching
@ -103,9 +100,9 @@ data class HexPattern(public val startDir: HexDir, public val angles: MutableLis
*/
@JvmOverloads
fun getCenter(hexRadius: Float, origin: HexCoord = HexCoord.Origin): Vec2 {
val originPx = HexUtils.coordToPx(origin, hexRadius, Vec2.ZERO)
val originPx = coordToPx(origin, hexRadius, Vec2.ZERO)
val points = this.toLines(hexRadius, originPx)
return HexUtils.FindCenter(points)
return findCenter(points)
}
@ -113,7 +110,7 @@ data class HexPattern(public val startDir: HexDir, public val angles: MutableLis
* Convert a hex pattern into a sequence of straight linePoints spanning its points.
*/
fun toLines(hexSize: Float, origin: Vec2): List<Vec2> =
this.positions().map { HexUtils.coordToPx(it, hexSize, origin) }
this.positions().map { coordToPx(it, hexSize, origin) }
override fun toString(): String = buildString {
append("HexPattern[")
@ -128,7 +125,7 @@ data class HexPattern(public val startDir: HexDir, public val angles: MutableLis
const val TAG_ANGLES = "angles"
@JvmStatic
fun IsHexPattern(tag: CompoundTag): Boolean {
fun isPattern(tag: CompoundTag): Boolean {
return tag.contains(TAG_START_DIR, Tag.TAG_ANY_NUMERIC.toInt()) && tag.contains(
TAG_ANGLES,
Tag.TAG_BYTE_ARRAY.toInt()
@ -136,14 +133,14 @@ data class HexPattern(public val startDir: HexDir, public val angles: MutableLis
}
@JvmStatic
fun DeserializeFromNBT(tag: CompoundTag): HexPattern {
fun fromNBT(tag: CompoundTag): HexPattern {
val startDir = HexDir.values()[tag.getByte(TAG_START_DIR).toInt()]
val angles = tag.getByteArray(TAG_ANGLES).map { HexAngle.values()[it.toInt()] }
return HexPattern(startDir, angles.toMutableList())
}
@JvmStatic
fun FromAnglesSig(signature: String, startDir: HexDir): HexPattern {
fun fromAngles(signature: String, startDir: HexDir): HexPattern {
val out = HexPattern(startDir)
var compass = startDir

View file

@ -18,7 +18,7 @@ class MishapAlreadyBrainswept(val villager: Villager) : Mishap() {
}
override fun particleSpray(ctx: CastingContext): ParticleSpray {
return ParticleSpray.Burst(villager.eyePosition, 1.0)
return ParticleSpray.burst(villager.eyePosition, 1.0)
}
override fun errorMessage(ctx: CastingContext, errorCtx: Context): Component =

View file

@ -20,7 +20,7 @@ class MishapBadBlock(val pos: BlockPos, val expected: Component) : Mishap() {
}
override fun particleSpray(ctx: CastingContext): ParticleSpray {
return ParticleSpray.Burst(Vec3.atCenterOf(pos), 1.0)
return ParticleSpray.burst(Vec3.atCenterOf(pos), 1.0)
}
override fun errorMessage(ctx: CastingContext, errorCtx: Context): Component {

View file

@ -20,7 +20,7 @@ class MishapBadBrainsweep(val villager: Villager, val pos: BlockPos) : Mishap()
}
override fun particleSpray(ctx: CastingContext): ParticleSpray {
return ParticleSpray.Burst(Vec3.atCenterOf(pos), 1.0)
return ParticleSpray.burst(Vec3.atCenterOf(pos), 1.0)
}
override fun errorMessage(ctx: CastingContext, errorCtx: Context): Component {

View file

@ -1,7 +1,10 @@
@file:JvmName("HexUtils")
package at.petrak.hexcasting.api.utils
import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.math.HexCoord
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.LongArrayTag
import net.minecraft.world.InteractionHand
import net.minecraft.world.item.ItemStack
@ -12,88 +15,82 @@ import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt
object HexUtils {
const val SQRT_3 = 1.7320508f
const val TAU = Math.PI * 2.0
const val SQRT_3 = 1.7320508f
@JvmStatic
fun Vec3.serializeToNBT(): LongArrayTag =
LongArrayTag(longArrayOf(this.x.toRawBits(), this.y.toRawBits(), this.z.toRawBits()))
fun Vec3.serializeToNBT(): LongArrayTag =
LongArrayTag(longArrayOf(this.x.toRawBits(), this.y.toRawBits(), this.z.toRawBits()))
@JvmStatic
fun DeserializeVec3FromNBT(tag: LongArray): Vec3 = if (tag.size != 3) Vec3.ZERO else
Vec3(
Double.fromBits(tag[0]),
Double.fromBits(tag[1]),
Double.fromBits(tag[2])
)
fun vecFromNBT(tag: LongArray): Vec3 = if (tag.size != 3) Vec3.ZERO else
Vec3(
Double.fromBits(tag[0]),
Double.fromBits(tag[1]),
Double.fromBits(tag[2])
)
@JvmStatic
fun Vec2.serializeToNBT(): LongArrayTag =
LongArrayTag(longArrayOf(this.x.toDouble().toRawBits(), this.y.toDouble().toRawBits()))
fun Vec2.serializeToNBT(): LongArrayTag =
LongArrayTag(longArrayOf(this.x.toDouble().toRawBits(), this.y.toDouble().toRawBits()))
@JvmStatic
fun DeserializeVec2FromNBT(tag: LongArray): Vec2 = if (tag.size != 2) Vec2.ZERO else
Vec2(
Double.fromBits(tag[0]).toFloat(),
Double.fromBits(tag[1]).toFloat(),
)
fun vec2FromNBT(tag: LongArray): Vec2 = if (tag.size != 2) Vec2.ZERO else
Vec2(
Double.fromBits(tag[0]).toFloat(),
Double.fromBits(tag[1]).toFloat(),
)
@JvmStatic
fun OtherHand(hand: InteractionHand) =
if (hand == InteractionHand.MAIN_HAND) InteractionHand.OFF_HAND else InteractionHand.MAIN_HAND
fun otherHand(hand: InteractionHand) =
if (hand == InteractionHand.MAIN_HAND) InteractionHand.OFF_HAND else InteractionHand.MAIN_HAND
@JvmStatic
fun FixNANs(x: Double): Double = if (x.isFinite()) x else 0.0
fun fixNAN(x: Double): Double = if (x.isFinite()) x else 0.0
@JvmStatic
fun FindCenter(points: List<Vec2>): Vec2 {
var minX = Float.POSITIVE_INFINITY
var minY = Float.POSITIVE_INFINITY
var maxX = Float.NEGATIVE_INFINITY
var maxY = Float.NEGATIVE_INFINITY
fun findCenter(points: List<Vec2>): Vec2 {
var minX = Float.POSITIVE_INFINITY
var minY = Float.POSITIVE_INFINITY
var maxX = Float.NEGATIVE_INFINITY
var maxY = Float.NEGATIVE_INFINITY
for (pos in points) {
minX = min(minX, pos.x)
minY = min(minY, pos.y)
maxX = max(maxX, pos.x)
maxY = max(maxY, pos.y)
}
return Vec2(
(minX + maxX) / 2f,
(minY + maxY) / 2f
)
}
@JvmStatic
fun coordToPx(coord: HexCoord, size: Float, offset: Vec2) =
Vec2(
SQRT_3 * coord.q.toFloat() + SQRT_3 / 2.0f * coord.r.toFloat(),
1.5f * coord.r.toFloat()
).scale(size).add(offset)
@JvmStatic
fun pxToCoord(px: Vec2, size: Float, offset: Vec2): HexCoord {
val offsetted = px.add(offset.negated())
var qf = (SQRT_3 / 3.0f * offsetted.x - 0.33333f * offsetted.y) / size
var rf = (0.66666f * offsetted.y) / size
val q = qf.roundToInt()
val r = rf.roundToInt()
qf -= q
rf -= r
return if (q.absoluteValue >= r.absoluteValue)
HexCoord(q + (qf + 0.5f * rf).roundToInt(), r)
else
HexCoord(q, r + (rf + 0.5 * qf).roundToInt())
}
const val TAU = Math.PI * 2.0
// Copy the impl from forge
@JvmStatic
fun ItemStack.serialize(): CompoundTag {
val out = CompoundTag()
this.save(out)
return out
for (pos in points) {
minX = min(minX, pos.x)
minY = min(minY, pos.y)
maxX = max(maxX, pos.x)
maxY = max(maxY, pos.y)
}
return Vec2(
(minX + maxX) / 2f,
(minY + maxY) / 2f
)
}
fun coordToPx(coord: HexCoord, size: Float, offset: Vec2): Vec2 =
Vec2(
SQRT_3 * coord.q.toFloat() + SQRT_3 / 2.0f * coord.r.toFloat(),
1.5f * coord.r.toFloat()
).scale(size).add(offset)
fun pxToCoord(px: Vec2, size: Float, offset: Vec2): HexCoord {
val offsetted = px.add(offset.negated())
var qf = (SQRT_3 / 3.0f * offsetted.x - 0.33333f * offsetted.y) / size
var rf = (0.66666f * offsetted.y) / size
val q = qf.roundToInt()
val r = rf.roundToInt()
qf -= q
rf -= r
return if (q.absoluteValue >= r.absoluteValue)
HexCoord(q + (qf + 0.5f * rf).roundToInt(), r)
else
HexCoord(q, r + (rf + 0.5 * qf).roundToInt())
}
fun Iterable<SpellDatum<*>>.serializeToNBT(): ListTag {
val tag = ListTag()
for (elt in this)
tag.add(elt.serializeToNBT())
return tag
}
// Copy the impl from forge
fun ItemStack.serializeToNBT(): CompoundTag {
val out = CompoundTag()
this.save(out)
return out
}

View file

@ -1,3 +1,4 @@
@file:JvmName("ManaHelper")
package at.petrak.hexcasting.api.utils
import at.petrak.hexcasting.xplat.IXplatAbstractions
@ -5,76 +6,70 @@ import net.minecraft.util.Mth
import net.minecraft.world.item.ItemStack
import kotlin.math.roundToInt
object ManaHelper {
@JvmStatic
fun isManaItem(stack: ItemStack): Boolean {
val manaHolder = IXplatAbstractions.INSTANCE.findManaHolder(stack) ?: return false
if (!manaHolder.canProvide())
return false
return manaHolder.withdrawMana(-1, true) > 0
}
fun isManaItem(stack: ItemStack): Boolean {
val manaHolder = IXplatAbstractions.INSTANCE.findManaHolder(stack) ?: return false
if (!manaHolder.canProvide())
return false
return manaHolder.withdrawMana(-1, true) > 0
}
/**
* Extract [cost] mana from [stack]. If [cost] is less than zero, extract all mana instead.
* This may mutate [stack] (and may consume it) unless [simulate] is set.
*
* If [drainForBatteries] is false, this will only consider forms of mana that can be used to make new batteries.
*
* Return the amount of mana extracted. This may be over [cost] if mana is wasted.
*/
@JvmStatic
@JvmOverloads
fun extractMana(
stack: ItemStack,
cost: Int = -1,
drainForBatteries: Boolean = false,
simulate: Boolean = false
): Int {
val manaHolder = IXplatAbstractions.INSTANCE.findManaHolder(stack) ?: return 0
/**
* Extract [cost] mana from [stack]. If [cost] is less than zero, extract all mana instead.
* This may mutate [stack] (and may consume it) unless [simulate] is set.
*
* If [drainForBatteries] is false, this will only consider forms of mana that can be used to make new batteries.
*
* Return the amount of mana extracted. This may be over [cost] if mana is wasted.
*/
@JvmOverloads
fun extractMana(
stack: ItemStack,
cost: Int = -1,
drainForBatteries: Boolean = false,
simulate: Boolean = false
): Int {
val manaHolder = IXplatAbstractions.INSTANCE.findManaHolder(stack) ?: return 0
if (drainForBatteries && !manaHolder.canConstructBattery())
return 0
if (drainForBatteries && !manaHolder.canConstructBattery())
return 0
return manaHolder.withdrawMana(cost, simulate)
}
return manaHolder.withdrawMana(cost, simulate)
}
/**
* Sorted from least important to most important
*/
fun compare(astack: ItemStack, bstack: ItemStack): Int {
val aMana = IXplatAbstractions.INSTANCE.findManaHolder(astack)
val bMana = IXplatAbstractions.INSTANCE.findManaHolder(bstack)
/**
* Sorted from least important to most important
*/
fun compareManaItem(astack: ItemStack, bstack: ItemStack): Int {
val aMana = IXplatAbstractions.INSTANCE.findManaHolder(astack)
val bMana = IXplatAbstractions.INSTANCE.findManaHolder(bstack)
return if (astack.item != bstack.item) {
(aMana?.consumptionPriority ?: 0) - (bMana?.consumptionPriority ?: 0)
} else if (aMana != null && bMana != null) {
aMana.mana - bMana.mana
} else {
astack.count - bstack.count
}
}
@JvmStatic
fun barColor(mana: Int, maxMana: Int): Int {
val amt = if (maxMana == 0) {
0f
} else {
mana.toFloat() / maxMana.toFloat()
}
val r = Mth.lerp(amt, 84f, 254f)
val g = Mth.lerp(amt, 57f, 203f)
val b = Mth.lerp(amt, 138f, 230f)
return Mth.color(r / 255f, g / 255f, b / 255f)
}
@JvmStatic
fun barWidth(mana: Int, maxMana: Int): Int {
val amt = if (maxMana == 0) {
0f
} else {
mana.toFloat() / maxMana.toFloat()
}
return (13f * amt).roundToInt()
return if (astack.item != bstack.item) {
(aMana?.consumptionPriority ?: 0) - (bMana?.consumptionPriority ?: 0)
} else if (aMana != null && bMana != null) {
aMana.mana - bMana.mana
} else {
astack.count - bstack.count
}
}
fun manaBarColor(mana: Int, maxMana: Int): Int {
val amt = if (maxMana == 0) {
0f
} else {
mana.toFloat() / maxMana.toFloat()
}
val r = Mth.lerp(amt, 84f, 254f)
val g = Mth.lerp(amt, 57f, 203f)
val b = Mth.lerp(amt, 138f, 230f)
return Mth.color(r / 255f, g / 255f, b / 255f)
}
fun manaBarWidth(mana: Int, maxMana: Int): Int {
val amt = if (maxMana == 0) {
0f
} else {
mana.toFloat() / maxMana.toFloat()
}
return (13f * amt).roundToInt()
}

View file

@ -0,0 +1,210 @@
@file:Suppress("NOTHING_TO_INLINE")
package at.petrak.hexcasting.api.utils
import net.minecraft.nbt.*
// https://github.com/TeamWizardry/LibrarianLib/blob/9cfb2cf3e35685568942ad41395265a2edc27d30/modules/core/src/main/kotlin/com/teamwizardry/librarianlib/core/util/kotlin/NbtBuilder.kt
@DslMarker
internal annotation class NBTDslMarker
@NBTDslMarker
object NBTBuilder {
inline operator fun invoke(block: NbtCompoundBuilder.() -> Unit) = compound(block)
inline operator fun invoke(tag: CompoundTag, block: NbtCompoundBuilder.() -> Unit) = use(tag, block)
inline fun use(tag: CompoundTag, block: NbtCompoundBuilder.() -> Unit): CompoundTag =
NbtCompoundBuilder(tag).also(block).tag
inline fun compound(block: NbtCompoundBuilder.() -> Unit): CompoundTag =
NbtCompoundBuilder(CompoundTag()).also(block).tag
inline fun list(block: NbtListBuilder.() -> Unit): ListTag =
NbtListBuilder(ListTag()).also(block).tag
inline fun list(vararg elements: Tag, block: NbtListBuilder.() -> Unit): ListTag =
NbtListBuilder(ListTag()).also {
it.addAll(elements.toList())
it.block()
}.tag
inline fun list(vararg elements: Tag): ListTag = ListTag().also { it.addAll(elements) }
inline fun list(elements: Collection<Tag>): ListTag = ListTag().also { it.addAll(elements) }
inline fun <T> list(elements: Collection<T>, mapper: (T) -> Tag): ListTag = ListTag().also { it.addAll(elements.map(mapper)) }
inline fun double(value: Number): DoubleTag = DoubleTag.valueOf(value.toDouble())
inline fun float(value: Number): FloatTag = FloatTag.valueOf(value.toFloat())
inline fun long(value: Number): LongTag = LongTag.valueOf(value.toLong())
inline fun int(value: Number): IntTag = IntTag.valueOf(value.toInt())
inline fun short(value: Number): ShortTag = ShortTag.valueOf(value.toShort())
inline fun byte(value: Number): ByteTag = ByteTag.valueOf(value.toByte())
inline fun string(value: String): StringTag = StringTag.valueOf(value)
inline fun byteArray(value: Collection<Number>): ByteArrayTag = ByteArrayTag(value.map { it.toByte() })
inline fun byteArray(vararg value: Int): ByteArrayTag = ByteArrayTag(ByteArray(value.size) { value[it].toByte() })
inline fun byteArray(vararg value: Byte): ByteArrayTag = ByteArrayTag(value)
inline fun byteArray(): ByteArrayTag = ByteArrayTag(byteArrayOf()) // avoiding overload ambiguity
inline fun longArray(value: Collection<Number>): LongArrayTag = LongArrayTag(value.map { it.toLong() })
inline fun longArray(vararg value: Int): LongArrayTag = LongArrayTag(LongArray(value.size) { value[it].toLong() })
inline fun longArray(vararg value: Long): LongArrayTag = LongArrayTag(value)
inline fun longArray(): LongArrayTag = LongArrayTag(longArrayOf()) // avoiding overload ambiguity
inline fun intArray(value: Collection<Number>): IntArrayTag = IntArrayTag(value.map { it.toInt() })
inline fun intArray(vararg value: Int): IntArrayTag = IntArrayTag(value)
}
@JvmInline
@NBTDslMarker
value class NbtCompoundBuilder(val tag: CompoundTag) {
// configuring this tag
inline operator fun String.remAssign(nbt: Tag) {
tag.put(this, nbt)
}
inline operator fun String.remAssign(str: String) {
tag.put(this, string(str))
}
inline operator fun String.remAssign(num: Int) {
tag.put(this, int(num))
}
inline operator fun String.remAssign(num: Double) {
tag.put(this, double(num))
}
inline operator fun String.remAssign(num: Float) {
tag.put(this, float(num))
}
inline operator fun String.remAssign(bool: Boolean) {
tag.put(this, byte(if (bool) 1 else 0))
}
// creating new tags
inline fun compound(block: NbtCompoundBuilder.() -> Unit): CompoundTag =
NbtCompoundBuilder(CompoundTag()).also { it.block() }.tag
inline fun list(block: NbtListBuilder.() -> Unit): ListTag =
NbtListBuilder(ListTag()).also { it.block() }.tag
inline fun list(vararg elements: Tag, block: NbtListBuilder.() -> Unit): ListTag =
NbtListBuilder(ListTag()).also {
it.addAll(elements.toList())
it.block()
}.tag
inline fun list(vararg elements: Tag): ListTag = ListTag().also { it.addAll(elements) }
inline fun list(elements: Collection<Tag>): ListTag = ListTag().also { it.addAll(elements) }
inline fun <T> list(elements: Collection<T>, mapper: (T) -> Tag): ListTag = ListTag().also { it.addAll(elements.map(mapper)) }
inline fun double(value: Number): DoubleTag = DoubleTag.valueOf(value.toDouble())
inline fun float(value: Number): FloatTag = FloatTag.valueOf(value.toFloat())
inline fun long(value: Number): LongTag = LongTag.valueOf(value.toLong())
inline fun int(value: Number): IntTag = IntTag.valueOf(value.toInt())
inline fun short(value: Number): ShortTag = ShortTag.valueOf(value.toShort())
inline fun byte(value: Number): ByteTag = ByteTag.valueOf(value.toByte())
inline fun string(value: String): StringTag = StringTag.valueOf(value)
inline fun byteArray(value: Collection<Number>): ByteArrayTag = ByteArrayTag(value.map { it.toByte() })
inline fun byteArray(vararg value: Int): ByteArrayTag = ByteArrayTag(ByteArray(value.size) { value[it].toByte() })
inline fun byteArray(vararg value: Byte): ByteArrayTag = ByteArrayTag(value)
inline fun byteArray(): ByteArrayTag = ByteArrayTag(byteArrayOf()) // avoiding overload ambiguity
inline fun longArray(value: Collection<Number>): LongArrayTag = LongArrayTag(value.map { it.toLong() })
inline fun longArray(vararg value: Int): LongArrayTag = LongArrayTag(LongArray(value.size) { value[it].toLong() })
inline fun longArray(vararg value: Long): LongArrayTag = LongArrayTag(value)
inline fun longArray(): LongArrayTag = LongArrayTag(longArrayOf()) // avoiding overload ambiguity
inline fun intArray(value: Collection<Number>): IntArrayTag = IntArrayTag(value.map { it.toInt() })
inline fun intArray(vararg value: Int): IntArrayTag = IntArrayTag(value)
}
@JvmInline
@NBTDslMarker
value class NbtListBuilder(val tag: ListTag) {
// configuring this tag
/**
* Add the given tag to this list
*/
inline operator fun Tag.unaryPlus() {
tag.add(this)
}
/**
* Add the given tags to this list
*/
inline operator fun Collection<Tag>.unaryPlus() {
tag.addAll(this)
}
/**
* Add the given tag to this list. This is explicitly defined for [ListTag] because otherwise there is overload
* ambiguity between the [Tag] and [Collection]<Tag> methods.
*/
inline operator fun ListTag.unaryPlus() {
tag.add(this)
}
inline fun addAll(nbt: Collection<Tag>) {
this.tag.addAll(nbt)
}
inline fun add(nbt: Tag) {
this.tag.add(nbt)
}
// creating new tags
inline fun compound(block: NbtCompoundBuilder.() -> Unit): CompoundTag =
NbtCompoundBuilder(CompoundTag()).also { it.block() }.tag
inline fun list(block: NbtListBuilder.() -> Unit): ListTag =
NbtListBuilder(ListTag()).also { it.block() }.tag
inline fun list(vararg elements: Tag, block: NbtListBuilder.() -> Unit): ListTag =
NbtListBuilder(ListTag()).also {
it.addAll(elements.toList())
it.block()
}.tag
inline fun list(vararg elements: Tag): ListTag = ListTag().also { it.addAll(elements) }
inline fun list(elements: Collection<Tag>): ListTag = ListTag().also { it.addAll(elements) }
inline fun <T> list(elements: Collection<T>, mapper: (T) -> Tag): ListTag = ListTag().also { it.addAll(elements.map(mapper)) }
inline fun double(value: Number): DoubleTag = DoubleTag.valueOf(value.toDouble())
inline fun float(value: Number): FloatTag = FloatTag.valueOf(value.toFloat())
inline fun long(value: Number): LongTag = LongTag.valueOf(value.toLong())
inline fun int(value: Number): IntTag = IntTag.valueOf(value.toInt())
inline fun short(value: Number): ShortTag = ShortTag.valueOf(value.toShort())
inline fun byte(value: Number): ByteTag = ByteTag.valueOf(value.toByte())
inline fun string(value: String): StringTag = StringTag.valueOf(value)
inline fun byteArray(value: Collection<Number>): ByteArrayTag = ByteArrayTag(value.map { it.toByte() })
inline fun byteArray(vararg value: Int): ByteArrayTag = ByteArrayTag(ByteArray(value.size) { value[it].toByte() })
inline fun byteArray(vararg value: Byte): ByteArrayTag = ByteArrayTag(value)
inline fun byteArray(): ByteArrayTag = ByteArrayTag(byteArrayOf()) // avoiding overload ambiguity
inline fun longArray(value: Collection<Number>): LongArrayTag = LongArrayTag(value.map { it.toLong() })
inline fun longArray(vararg value: Int): LongArrayTag = LongArrayTag(LongArray(value.size) { value[it].toLong() })
inline fun longArray(vararg value: Long): LongArrayTag = LongArrayTag(value)
inline fun longArray(): LongArrayTag = LongArrayTag(longArrayOf()) // avoiding overload ambiguity
inline fun intArray(value: Collection<Number>): IntArrayTag = IntArrayTag(value.map { it.toInt() })
inline fun intArray(vararg value: Int): IntArrayTag = IntArrayTag(value)
inline fun doubles(vararg value: Int): List<DoubleTag> = value.map { DoubleTag.valueOf(it.toDouble()) }
inline fun doubles(vararg value: Double): List<DoubleTag> = value.map { DoubleTag.valueOf(it) }
inline fun floats(vararg value: Int): List<FloatTag> = value.map { FloatTag.valueOf(it.toFloat()) }
inline fun floats(vararg value: Float): List<FloatTag> = value.map { FloatTag.valueOf(it) }
inline fun longs(vararg value: Int): List<LongTag> = value.map { LongTag.valueOf(it.toLong()) }
inline fun longs(vararg value: Long): List<LongTag> = value.map { LongTag.valueOf(it) }
inline fun ints(vararg value: Int): List<IntTag> = value.map { IntTag.valueOf(it) }
inline fun shorts(vararg value: Int): List<ShortTag> = value.map { ShortTag.valueOf(it.toShort()) }
inline fun shorts(vararg value: Short): List<ShortTag> = value.map { ShortTag.valueOf(it) }
inline fun bytes(vararg value: Int): List<ByteTag> = value.map { ByteTag.valueOf(it.toByte()) }
inline fun bytes(vararg value: Byte): List<ByteTag> = value.map { ByteTag.valueOf(it) }
fun strings(vararg value: String): List<StringTag> = value.map { StringTag.valueOf(it) }
}

View file

@ -223,6 +223,7 @@ public class HexAdditionalRenderers {
if (text.isEmpty()) {
ps.translate(0, mc.font.lineHeight, 0);
}
ps.translate(0, 6, 0);
}
ps.popPose();

View file

@ -1,8 +1,10 @@
@file:JvmName("RenderLib")
package at.petrak.hexcasting.client
import at.petrak.hexcasting.api.mod.HexConfig
import at.petrak.hexcasting.api.spell.math.HexPattern
import at.petrak.hexcasting.api.utils.HexUtils
import at.petrak.hexcasting.api.utils.TAU
import at.petrak.hexcasting.client.gui.GuiSpellcasting
import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.DefaultVertexFormat
import com.mojang.blaze3d.vertex.PoseStack
@ -12,6 +14,7 @@ import com.mojang.math.Matrix4f
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.screens.Screen
import net.minecraft.core.BlockPos
import net.minecraft.util.FastColor
import net.minecraft.util.Mth
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.levelgen.XoroshiroRandomSource
@ -19,271 +22,258 @@ import net.minecraft.world.level.levelgen.synth.PerlinNoise
import net.minecraft.world.phys.Vec2
import kotlin.math.floor
import kotlin.math.min
import net.minecraft.util.FastColor.ARGB32 as FC
/**
* Common draw code
* Source of perlin noise
*/
object RenderLib {
/**
* Source of perlin noise
*/
val NOISE = PerlinNoise.create(XoroshiroRandomSource(9001L), listOf(0, 1, 2, 3, 4))
val NOISE: PerlinNoise = PerlinNoise.create(XoroshiroRandomSource(9001L), listOf(0, 1, 2, 3, 4))
/**
* Draw a sequence of linePoints spanning the given points.
*
* Please make sure to enable the right asinine shaders; see [GuiSpellcasting][at.petrak.hexcasting.client.gui.GuiSpellcasting]
*/
@JvmStatic
@JvmOverloads
fun drawLineSeq(
mat: Matrix4f,
points: List<Vec2>,
width: Float,
z: Float,
tail: Int,
head: Int,
animTime: Float? = null,
) {
if (points.size <= 1) return
/**
* Draw a sequence of linePoints spanning the given points.
*
* Please make sure to enable the right asinine shaders; see [GuiSpellcasting]
*/
@JvmOverloads
fun drawLineSeq(
mat: Matrix4f,
points: List<Vec2>,
width: Float,
z: Float,
tail: Int,
head: Int,
animTime: Float? = null,
) {
if (points.size <= 1) return
val r1 = FC.red(tail).toFloat()
val g1 = FC.green(tail).toFloat()
val b1 = FC.blue(tail).toFloat()
val a = FC.alpha(tail)
val headSource = if (Screen.hasControlDown() != HexConfig.client().ctrlTogglesOffStrokeOrder())
head
else
tail
val r2 = FC.red(headSource).toFloat()
val g2 = FC.green(headSource).toFloat()
val b2 = FC.blue(headSource).toFloat()
val r1 = FastColor.ARGB32.red(tail).toFloat()
val g1 = FastColor.ARGB32.green(tail).toFloat()
val b1 = FastColor.ARGB32.blue(tail).toFloat()
val a = FastColor.ARGB32.alpha(tail)
val headSource = if (Screen.hasControlDown() != HexConfig.client().ctrlTogglesOffStrokeOrder())
head
else
tail
val r2 = FastColor.ARGB32.red(headSource).toFloat()
val g2 = FastColor.ARGB32.green(headSource).toFloat()
val b2 = FastColor.ARGB32.blue(headSource).toFloat()
// they spell it wrong at mojang lmao
val tess = Tesselator.getInstance()
val buf = tess.builder
// they spell it wrong at mojang lmao
val tess = Tesselator.getInstance()
val buf = tess.builder
// We use one single TRIANGLE_STRIP
// in order to connect adjacent segments together and not get the weird hinge effect.
// There's still some artifacting but this is passable, at least.
buf.begin(VertexFormat.Mode.TRIANGLE_STRIP, DefaultVertexFormat.POSITION_COLOR)
// We use one single TRIANGLE_STRIP
// in order to connect adjacent segments together and not get the weird hinge effect.
// There's still some artifacting but this is passable, at least.
buf.begin(VertexFormat.Mode.TRIANGLE_STRIP, DefaultVertexFormat.POSITION_COLOR)
val n = points.size
for ((i, pair) in points.zipWithNext().withIndex()) {
val (p1, p2) = pair
// 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)
val dx = p2.x - p1.x
val dy = p2.y - p1.y
// normal x and y, presumably?
val nx = -dy
val ny = dx
// thickness?
val tlen = Mth.sqrt(nx * nx + ny * ny) / (width * 0.5f)
val tx = nx / tlen
val ty = ny / tlen
val n = points.size
for ((i, pair) in points.zipWithNext().withIndex()) {
val (p1, p2) = pair
// 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)
val dx = p2.x - p1.x
val dy = p2.y - p1.y
// normal x and y, presumably?
val nx = -dy
val ny = dx
// thickness?
val tlen = Mth.sqrt(nx * nx + ny * ny) / (width * 0.5f)
val tx = nx / tlen
val ty = ny / tlen
fun color(time: Float): BlockPos =
BlockPos(Mth.lerp(time, r1, r2).toInt(), Mth.lerp(time, g1, g2).toInt(), Mth.lerp(time, b1, b2).toInt())
fun color(time: Float): BlockPos =
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)
buf.vertex(mat, p1.x + tx, p1.y + ty, z).color(color1.x, color1.y, color1.z, a).endVertex()
buf.vertex(mat, p1.x - tx, p1.y - ty, z).color(color1.x, color1.y, color1.z, a).endVertex()
buf.vertex(mat, p2.x + tx, p2.y + ty, z).color(color2.x, color2.y, color2.z, a).endVertex()
buf.vertex(mat, p2.x - tx, p2.y - ty, z).color(color2.x, color2.y, color2.z, a).endVertex()
}
tess.end()
if (animTime != null) {
val pointCircuit =
(animTime * 30f * HexConfig.client().patternPointSpeedMultiplier().toFloat()) % (points.size + 10)
// subtract 1 to avoid the point appearing between the end and start for 1 frame
if (pointCircuit < points.size - 1) {
val pointMacro = floor(pointCircuit).toInt()
val pointMicro = pointCircuit - pointMacro
val p1 = points[pointMacro]
val p2 = points[(pointMacro + 1) % points.size]
val drawPos = Vec2(
p1.x + (p2.x - p1.x) * pointMicro,
p1.y + (p2.y - p1.y) * pointMicro,
)
drawSpot(
mat,
drawPos,
2f,
(r1 + 255) / 2f / 255f,
(g1 + 255) / 2f / 255f,
(b1 + 255) / 2f / 255f,
a / 1.2f / 255f
)
}
}
val color1 = color(i.toFloat() / n)
val color2 = color((i + 1f) / n)
buf.vertex(mat, p1.x + tx, p1.y + ty, z).color(color1.x, color1.y, color1.z, a).endVertex()
buf.vertex(mat, p1.x - tx, p1.y - ty, z).color(color1.x, color1.y, color1.z, a).endVertex()
buf.vertex(mat, p2.x + tx, p2.y + ty, z).color(color2.x, color2.y, color2.z, a).endVertex()
buf.vertex(mat, p2.x - tx, p2.y - ty, z).color(color2.x, color2.y, color2.z, a).endVertex()
}
tess.end()
/**
* 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.)
*/
@JvmStatic
fun drawPatternFromPoints(
mat: Matrix4f,
points: List<Vec2>,
drawLast: Boolean,
tail: Int,
head: Int,
flowIrregular: Float,
animTime: Float? = null
) {
val zappyPts = makeZappy(points, 10f, 2.5f, 0.1f, flowIrregular)
val nodes = if (drawLast) {
points
} else {
points.dropLast(1)
}
drawLineSeq(mat, zappyPts, 5f, 0f, tail, head, null)
drawLineSeq(mat, zappyPts, 2f, 1f, screenCol(tail), screenCol(head), animTime)
for (node in nodes) {
if (animTime != null) {
val pointCircuit =
(animTime * 30f * HexConfig.client().patternPointSpeedMultiplier().toFloat()) % (points.size + 10)
// subtract 1 to avoid the point appearing between the end and start for 1 frame
if (pointCircuit < points.size - 1) {
val pointMacro = floor(pointCircuit).toInt()
val pointMicro = pointCircuit - pointMacro
val p1 = points[pointMacro]
val p2 = points[(pointMacro + 1) % points.size]
val drawPos = Vec2(
p1.x + (p2.x - p1.x) * pointMicro,
p1.y + (p2.y - p1.y) * pointMicro,
)
drawSpot(
mat,
node,
drawPos,
2f,
dodge(FC.red(head)) / 255f,
dodge(FC.green(head)) / 255f,
dodge(FC.blue(head)) / 255f,
FC.alpha(head) / 255f
);
(r1 + 255) / 2f / 255f,
(g1 + 255) / 2f / 255f,
(b1 + 255) / 2f / 255f,
a / 1.2f / 255f
)
}
}
/**
* 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
*/
@JvmStatic
fun makeZappy(points: List<Vec2>, hops: Float, variance: Float, speed: Float, flowIrregular: Float): List<Vec2> {
// Nothing in, nothing out
if (points.isEmpty()) {
return emptyList()
}
val scaleVariance = { it: Double -> Math.min(1.0, 8 * (0.5 - Math.abs(0.5 - it))) }
val hops = hops.toInt()
val zSeed = ClientTickCounter.total.toDouble() * speed
// Create our output list of zap points
val zappyPts = mutableListOf(points[0])
// For each segment in the original...
for ((i, pair) in points.zipWithNext().withIndex()) {
val (src, target) = pair
val delta = target.add(src.negated())
// Take hop distance
val hopDist = Mth.sqrt(src.distanceToSqr(target)) / hops
// Compute how big the radius of variance should be
val maxVariance = hopDist * variance
for (j in 1..hops) {
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(), Math.sin(zSeed)) * flowIrregular
val theta = (3 * NOISE.getValue(i.toDouble() + j.toDouble() / (hops + 1) + minorPerturb - zSeed, 1337.0, 0.0) * HexUtils.TAU).toFloat()
val r = (NOISE.getValue(i.toDouble() + j.toDouble() / (hops + 1) - zSeed, 69420.0, 0.0) * 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))
}
// Finally, we hit the destination, add that too
zappyPts.add(target)
}
return zappyPts
}
/**
* Draw a little circle, because Minecraft rendering code is a nightmare and doesn't
* include primitive drawing code...
*/
@JvmStatic
fun drawSpot(mat: Matrix4f, point: Vec2, radius: Float, r: Float, g: Float, b: Float, a: Float) {
val tess = Tesselator.getInstance()
val buf = tess.builder
// https://stackoverflow.com/questions/20394727/gl-triangle-strip-vs-gl-triangle-fan
// Starting point is the center
buf.begin(VertexFormat.Mode.TRIANGLE_FAN, DefaultVertexFormat.POSITION_COLOR)
buf.vertex(mat, point.x, point.y, 1f).color(r, g, b, a).endVertex()
// https://github.com/not-fl3/macroquad/blob/master/src/shapes.rs#L98
// yes they are gonna be little hexagons fite me
val fracOfCircle = 6
// run 0 AND last; this way the circle closes
for (i in 0..fracOfCircle) {
val theta = i.toFloat() / fracOfCircle * HexUtils.TAU.toFloat()
val rx = Mth.cos(theta) * radius + point.x
val ry = Mth.sin(theta) * radius + point.y
buf.vertex(mat, rx, ry, 1f).color(r, g, b, a).endVertex()
}
tess.end()
}
fun dodge(n: Int): Float = n * 0.9f
fun screen(n: Int): Int = (n + 255) / 2
@JvmStatic
fun screenCol(n: Int): Int {
return FC.color(
FC.alpha(n),
screen(FC.red(n)),
screen(FC.green(n)),
screen(FC.blue(n)),
)
}
/**
* Return the scale and dots formed by the pattern when centered.
*/
@JvmStatic
fun getCenteredPattern(pattern: HexPattern, width: Float, height: Float, minSize: Float): Pair<Float, List<Vec2>> {
// Do two passes: one with a random size to find a good COM and one with the real calculation
val com1: Vec2 = pattern.getCenter(1f)
val lines1: List<Vec2> = pattern.toLines(1f, Vec2.ZERO)
var maxDx = -1f
var maxDy = -1f
for (dot in lines1) {
val dx = Mth.abs(dot.x - com1.x)
if (dx > maxDx) {
maxDx = dx
}
val dy = Mth.abs(dot.y - com1.y)
if (dy > maxDy) {
maxDy = dy
}
}
val scale =
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 Pair(scale, lines2)
}
@JvmStatic
fun renderItemStackInGui(ms: PoseStack, stack: ItemStack, x: Int, y: Int) {
transferMsToGl(ms) { Minecraft.getInstance().itemRenderer.renderAndDecorateItem(stack, x, y) }
}
@JvmStatic
fun transferMsToGl(ms: PoseStack, toRun: Runnable) {
val mvs = RenderSystem.getModelViewStack()
mvs.pushPose()
mvs.mulPoseMatrix(ms.last().pose())
RenderSystem.applyModelViewMatrix()
toRun.run()
mvs.popPose()
RenderSystem.applyModelViewMatrix()
}
}
/**
* * 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.)
* */
@JvmOverloads
fun drawPatternFromPoints(
mat: Matrix4f,
points: List<Vec2>,
drawLast: Boolean,
tail: Int,
head: Int,
flowIrregular: Float,
animTime: Float? = null
) {
val zappyPts = makeZappy(points, 10f, 2.5f, 0.1f, flowIrregular)
val nodes = if (drawLast) {
points
} else {
points.dropLast(1)
}
drawLineSeq(mat, zappyPts, 5f, 0f, tail, head, null)
drawLineSeq(mat, zappyPts, 2f, 1f, screenCol(tail), screenCol(head), animTime)
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
);
}
}
/**
* 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(points: List<Vec2>, hops: Float, variance: Float, speed: Float, flowIrregular: Float): List<Vec2> {
// Nothing in, nothing out
if (points.isEmpty()) {
return emptyList()
}
val scaleVariance = { it: Double -> Math.min(1.0, 8 * (0.5 - Math.abs(0.5 - it))) }
val hops = hops.toInt()
val zSeed = ClientTickCounter.total.toDouble() * speed
// Create our output list of zap points
val zappyPts = mutableListOf(points[0])
// For each segment in the original...
for ((i, pair) in points.zipWithNext().withIndex()) {
val (src, target) = pair
val delta = target.add(src.negated())
// Take hop distance
val hopDist = Mth.sqrt(src.distanceToSqr(target)) / hops
// Compute how big the radius of variance should be
val maxVariance = hopDist * variance
for (j in 1..hops) {
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(), Math.sin(zSeed)) * flowIrregular
val theta = (3 * NOISE.getValue(i.toDouble() + j.toDouble() / (hops + 1) + minorPerturb - zSeed, 1337.0, 0.0) * TAU).toFloat()
val r = (NOISE.getValue(i.toDouble() + j.toDouble() / (hops + 1) - zSeed, 69420.0, 0.0) * 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))
}
// Finally, we hit the destination, add that too
zappyPts.add(target)
}
return zappyPts
}
/**
* Draw a little circle, because Minecraft rendering code is a nightmare and doesn't
* include primitive drawing code...
*/
fun drawSpot(mat: Matrix4f, point: Vec2, radius: Float, r: Float, g: Float, b: Float, a: Float) {
val tess = Tesselator.getInstance()
val buf = tess.builder
// https://stackoverflow.com/questions/20394727/gl-triangle-strip-vs-gl-triangle-fan
// Starting point is the center
buf.begin(VertexFormat.Mode.TRIANGLE_FAN, DefaultVertexFormat.POSITION_COLOR)
buf.vertex(mat, point.x, point.y, 1f).color(r, g, b, a).endVertex()
// https://github.com/not-fl3/macroquad/blob/master/src/shapes.rs#L98
// yes they are gonna be little hexagons fite me
val fracOfCircle = 6
// run 0 AND last; this way the circle closes
for (i in 0..fracOfCircle) {
val theta = i.toFloat() / fracOfCircle * TAU.toFloat()
val rx = Mth.cos(theta) * radius + point.x
val ry = Mth.sin(theta) * radius + point.y
buf.vertex(mat, rx, ry, 1f).color(r, g, b, a).endVertex()
}
tess.end()
}
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)),
)
}
fun screen(n: Int) = (n + 255) / 2
fun dodge(n: Int) = n * 0.9f
/**
* Return the scale and dots formed by the pattern when centered.
*/
fun getCenteredPattern(pattern: HexPattern, width: Float, height: Float, minSize: Float): Pair<Float, List<Vec2>> {
// Do two passes: one with a random size to find a good COM and one with the real calculation
val com1: Vec2 = pattern.getCenter(1f)
val lines1: List<Vec2> = pattern.toLines(1f, Vec2.ZERO)
var maxDx = -1f
var maxDy = -1f
for (dot in lines1) {
val dx = Mth.abs(dot.x - com1.x)
if (dx > maxDx) {
maxDx = dx
}
val dy = Mth.abs(dot.y - com1.y)
if (dy > maxDy) {
maxDy = dy
}
}
val scale =
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
}
fun renderItemStackInGui(ms: PoseStack, stack: ItemStack, x: Int, y: Int) {
transferMsToGl(ms) { Minecraft.getInstance().itemRenderer.renderAndDecorateItem(stack, x, y) }
}
fun transferMsToGl(ms: PoseStack, toRun: Runnable) {
val mvs = RenderSystem.getModelViewStack()
mvs.pushPose()
mvs.mulPoseMatrix(ms.last().pose())
RenderSystem.applyModelViewMatrix()
toRun.run()
mvs.popPose()
RenderSystem.applyModelViewMatrix()
}

View file

@ -8,8 +8,9 @@ import at.petrak.hexcasting.api.spell.math.HexAngle
import at.petrak.hexcasting.api.spell.math.HexCoord
import at.petrak.hexcasting.api.spell.math.HexDir
import at.petrak.hexcasting.api.spell.math.HexPattern
import at.petrak.hexcasting.api.utils.HexUtils
import at.petrak.hexcasting.client.RenderLib
import at.petrak.hexcasting.api.utils.otherHand
import at.petrak.hexcasting.client.drawPatternFromPoints
import at.petrak.hexcasting.client.drawSpot
import at.petrak.hexcasting.client.sound.GridSoundInstance
import at.petrak.hexcasting.common.items.ItemSpellbook
import at.petrak.hexcasting.common.lib.HexItems
@ -231,7 +232,7 @@ class GuiSpellcasting(
override fun mouseScrolled(pMouseX: Double, pMouseY: Double, pDelta: Double): Boolean {
super.mouseScrolled(pMouseX, pMouseY, pDelta)
val otherHand = HexUtils.OtherHand(this.handOpenedWith)
val otherHand = otherHand(this.handOpenedWith)
if (Minecraft.getInstance().player!!.getItemInHand(otherHand).item is ItemSpellbook)
IClientXplatAbstractions.INSTANCE.sendPacketToServer(
MsgShiftScrollSyn(
@ -279,7 +280,7 @@ class GuiSpellcasting(
0f,
1f
)
RenderLib.drawSpot(
drawSpot(
mat,
dotPx,
scaledDist * 2f,
@ -293,7 +294,7 @@ class GuiSpellcasting(
RenderSystem.defaultBlendFunc()
for ((pat, origin, valid) in this.patterns) {
RenderLib.drawPatternFromPoints(mat, pat.toLines(
drawPatternFromPoints(mat, pat.toLines(
this.hexSize(),
this.coordToPx(origin)
), true, valid.color or (0xC8 shl 24), valid.fadeColor or (0xC8 shl 24), if (valid.success) 0.2f else 0.9f)
@ -315,7 +316,7 @@ class GuiSpellcasting(
}
points.add(mousePos)
RenderLib.drawPatternFromPoints(mat, points, false, 0xff_64c8ff_u.toInt(), 0xff_fecbe6_u.toInt(), 0.2f)
drawPatternFromPoints(mat, points, false, 0xff_64c8ff_u.toInt(), 0xff_fecbe6_u.toInt(), 0.1f)
}
RenderSystem.setShader { prevShader }
@ -338,7 +339,7 @@ class GuiSpellcasting(
/** Distance between adjacent hex centers */
fun hexSize(): Float {
val hasLens = Minecraft.getInstance().player!!
.getItemInHand(HexUtils.OtherHand(this.handOpenedWith)).`is`(HexItems.SCRYING_LENS)
.getItemInHand(otherHand(this.handOpenedWith)).`is`(HexItems.SCRYING_LENS)
// Originally, we allowed 32 dots across. Assuming a 1920x1080 screen this allowed like 500-odd area.
// Let's be generous and give them 512.
@ -348,8 +349,9 @@ class GuiSpellcasting(
fun coordsOffset(): Vec2 = Vec2(this.width.toFloat() * 0.5f, this.height.toFloat() * 0.5f)
fun coordToPx(coord: HexCoord) = HexUtils.coordToPx(coord, this.hexSize(), this.coordsOffset())
fun pxToCoord(px: Vec2) = HexUtils.pxToCoord(px, this.hexSize(), this.coordsOffset())
fun coordToPx(coord: HexCoord) =
at.petrak.hexcasting.api.utils.coordToPx(coord, this.hexSize(), this.coordsOffset())
fun pxToCoord(px: Vec2) = at.petrak.hexcasting.api.utils.pxToCoord(px, this.hexSize(), this.coordsOffset())
private sealed class PatternDrawState {

View file

@ -1,8 +1,8 @@
package at.petrak.hexcasting.client.gui;
import at.petrak.hexcasting.client.ClientTickCounter;
import at.petrak.hexcasting.client.RenderLib;
import at.petrak.hexcasting.api.spell.math.HexPattern;
import at.petrak.hexcasting.client.RenderLib;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.*;

View file

@ -70,8 +70,8 @@ public class BlockEntityAkashicBookshelf extends HexBlockEntity {
} else {
this.recordPos = null;
}
if (HexPattern.IsHexPattern(pattern)) {
this.pattern = HexPattern.DeserializeFromNBT(pattern);
if (HexPattern.isPattern(pattern)) {
this.pattern = HexPattern.fromNBT(pattern);
} else {
this.pattern = null;
}

View file

@ -81,14 +81,14 @@ public class BlockEntityAkashicRecord extends HexBlockEntity {
if (entry == null) {
return null;
} else {
return SpellDatum.DeserializeFromNBT(entry.datum, slevel);
return SpellDatum.fromNBT(entry.datum, slevel);
}
}
public Component getDisplayAt(HexPattern key) {
var entry = this.entries.get(getKey(key));
if (entry != null) {
return SpellDatum.DisplayFromTag(entry.datum);
return SpellDatum.displayFromNBT(entry.datum);
} else {
return new TranslatableComponent("hexcasting.spelldata.akashic.nopos").withStyle(ChatFormatting.RED);
}

View file

@ -32,8 +32,8 @@ public class BlockEntitySlate extends HexBlockEntity {
protected void loadModData(CompoundTag tag) {
if (tag.contains(TAG_PATTERN, Tag.TAG_COMPOUND)) {
CompoundTag patternTag = tag.getCompound(TAG_PATTERN);
if (HexPattern.IsHexPattern(patternTag)) {
this.pattern = HexPattern.DeserializeFromNBT(patternTag);
if (HexPattern.isPattern(patternTag)) {
this.pattern = HexPattern.fromNBT(patternTag);
} else {
this.pattern = null;
}

View file

@ -90,12 +90,12 @@ public class BlockEntityConjured extends HexBlockEntity {
@Override
protected void saveModData(CompoundTag tag) {
tag.put(TAG_COLORIZER, this.colorizer.serialize());
tag.put(TAG_COLORIZER, this.colorizer.serializeToNBT());
}
@Override
protected void loadModData(CompoundTag tag) {
this.colorizer = FrozenColorizer.deserialize(tag.getCompound(TAG_COLORIZER));
this.colorizer = FrozenColorizer.fromNBT(tag.getCompound(TAG_COLORIZER));
}
public FrozenColorizer getColorizer() {

View file

@ -16,7 +16,6 @@ import at.petrak.hexcasting.common.casting.operators.circles.OpCircleBounds;
import at.petrak.hexcasting.common.casting.operators.circles.OpImpetusDir;
import at.petrak.hexcasting.common.casting.operators.circles.OpImpetusPos;
import at.petrak.hexcasting.common.casting.operators.eval.OpEval;
import at.petrak.hexcasting.common.casting.operators.eval.OpEvalDelay;
import at.petrak.hexcasting.common.casting.operators.eval.OpForEach;
import at.petrak.hexcasting.common.casting.operators.eval.OpHalt;
import at.petrak.hexcasting.common.casting.operators.lists.*;
@ -52,417 +51,417 @@ public class RegisterPatterns {
// - CW is the special or destruction version
// == Getters ==
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qaq", HexDir.NORTH_EAST), modLoc("get_caster"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qaq", HexDir.NORTH_EAST), modLoc("get_caster"),
OpGetCaster.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aa", HexDir.EAST), modLoc("get_entity_pos"),
PatternRegistry.mapPattern(HexPattern.fromAngles("aa", HexDir.EAST), modLoc("get_entity_pos"),
OpEntityPos.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wa", HexDir.EAST), modLoc("get_entity_look"),
PatternRegistry.mapPattern(HexPattern.fromAngles("wa", HexDir.EAST), modLoc("get_entity_look"),
OpEntityLook.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("awq", HexDir.NORTH_EAST), modLoc("get_entity_height"),
PatternRegistry.mapPattern(HexPattern.fromAngles("awq", HexDir.NORTH_EAST), modLoc("get_entity_height"),
OpEntityHeight.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wq", HexDir.EAST), modLoc("get_entity_velocity"),
PatternRegistry.mapPattern(HexPattern.fromAngles("wq", HexDir.EAST), modLoc("get_entity_velocity"),
OpEntityVelocity.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wqaawdd", HexDir.EAST), modLoc("raycast"),
PatternRegistry.mapPattern(HexPattern.fromAngles("wqaawdd", HexDir.EAST), modLoc("raycast"),
OpBlockRaycast.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("weddwaa", HexDir.EAST), modLoc("raycast/axis"),
PatternRegistry.mapPattern(HexPattern.fromAngles("weddwaa", HexDir.EAST), modLoc("raycast/axis"),
OpBlockAxisRaycast.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("weaqa", HexDir.EAST), modLoc("raycast/entity"),
PatternRegistry.mapPattern(HexPattern.fromAngles("weaqa", HexDir.EAST), modLoc("raycast/entity"),
OpEntityRaycast.INSTANCE);
// == spell circle getters ==
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eaqwqae", HexDir.SOUTH_WEST),
PatternRegistry.mapPattern(HexPattern.fromAngles("eaqwqae", HexDir.SOUTH_WEST),
modLoc("circle/impetus_pos"), OpImpetusPos.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eaqwqaewede", HexDir.SOUTH_WEST),
PatternRegistry.mapPattern(HexPattern.fromAngles("eaqwqaewede", HexDir.SOUTH_WEST),
modLoc("circle/impetus_dir"), OpImpetusDir.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eaqwqaewdd", HexDir.SOUTH_WEST),
PatternRegistry.mapPattern(HexPattern.fromAngles("eaqwqaewdd", HexDir.SOUTH_WEST),
modLoc("circle/bounds/min"), new OpCircleBounds(false));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aqwqawaaqa", HexDir.WEST),
PatternRegistry.mapPattern(HexPattern.fromAngles("aqwqawaaqa", HexDir.WEST),
modLoc("circle/bounds/max"), new OpCircleBounds(true));
// == Modify Stack ==
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aadaa", HexDir.EAST), modLoc("duplicate"),
PatternRegistry.mapPattern(HexPattern.fromAngles("aadaa", HexDir.EAST), modLoc("duplicate"),
OpDuplicate.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aadaadaa", HexDir.EAST), modLoc("duplicate_n"),
PatternRegistry.mapPattern(HexPattern.fromAngles("aadaadaa", HexDir.EAST), modLoc("duplicate_n"),
OpDuplicateN.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qwaeawqaeaqa", HexDir.NORTH_WEST), modLoc("stack_len"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qwaeawqaeaqa", HexDir.NORTH_WEST), modLoc("stack_len"),
OpStackSize.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aawdd", HexDir.EAST), modLoc("swap"), OpSwap.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("ddad", HexDir.WEST), modLoc("fisherman"),
PatternRegistry.mapPattern(HexPattern.fromAngles("aawdd", HexDir.EAST), modLoc("swap"), OpSwap.INSTANCE);
PatternRegistry.mapPattern(HexPattern.fromAngles("ddad", HexDir.WEST), modLoc("fisherman"),
OpFisherman.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qaawdde", HexDir.SOUTH_EAST), modLoc("swizzle"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qaawdde", HexDir.SOUTH_EAST), modLoc("swizzle"),
OpAlwinfyHasAscendedToABeingOfPureMath.INSTANCE);
// == Math ==
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("waaw", HexDir.NORTH_EAST), modLoc("add"),
PatternRegistry.mapPattern(HexPattern.fromAngles("waaw", HexDir.NORTH_EAST), modLoc("add"),
OpAdd.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wddw", HexDir.NORTH_WEST), modLoc("sub"),
PatternRegistry.mapPattern(HexPattern.fromAngles("wddw", HexDir.NORTH_WEST), modLoc("sub"),
OpSub.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("waqaw", HexDir.SOUTH_EAST), modLoc("mul_dot"),
PatternRegistry.mapPattern(HexPattern.fromAngles("waqaw", HexDir.SOUTH_EAST), modLoc("mul_dot"),
OpMulDot.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wdedw", HexDir.NORTH_EAST), modLoc("div_cross"),
PatternRegistry.mapPattern(HexPattern.fromAngles("wdedw", HexDir.NORTH_EAST), modLoc("div_cross"),
OpDivCross.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wqaqw", HexDir.NORTH_EAST), modLoc("abs_len"),
PatternRegistry.mapPattern(HexPattern.fromAngles("wqaqw", HexDir.NORTH_EAST), modLoc("abs_len"),
OpAbsLen.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wedew", HexDir.NORTH_WEST), modLoc("pow_proj"),
PatternRegistry.mapPattern(HexPattern.fromAngles("wedew", HexDir.NORTH_WEST), modLoc("pow_proj"),
OpPowProj.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("ewq", HexDir.EAST), modLoc("floor"),
PatternRegistry.mapPattern(HexPattern.fromAngles("ewq", HexDir.EAST), modLoc("floor"),
OpFloor.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qwe", HexDir.EAST), modLoc("ceil"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qwe", HexDir.EAST), modLoc("ceil"),
OpCeil.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eqqqqq", HexDir.EAST), modLoc("construct_vec"),
PatternRegistry.mapPattern(HexPattern.fromAngles("eqqqqq", HexDir.EAST), modLoc("construct_vec"),
OpConstructVec.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qeeeee", HexDir.EAST), modLoc("deconstruct_vec"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qeeeee", HexDir.EAST), modLoc("deconstruct_vec"),
OpDeconstructVec.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqaww", HexDir.NORTH_WEST), modLoc("coerce_axial"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqaww", HexDir.NORTH_WEST), modLoc("coerce_axial"),
OpCoerceToAxial.INSTANCE);
// == Logic ==
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wdw", HexDir.NORTH_EAST), modLoc("and"),
PatternRegistry.mapPattern(HexPattern.fromAngles("wdw", HexDir.NORTH_EAST), modLoc("and"),
OpBoolAnd.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("waw", HexDir.SOUTH_EAST), modLoc("or"),
PatternRegistry.mapPattern(HexPattern.fromAngles("waw", HexDir.SOUTH_EAST), modLoc("or"),
OpBoolOr.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("dwa", HexDir.NORTH_WEST), modLoc("xor"),
PatternRegistry.mapPattern(HexPattern.fromAngles("dwa", HexDir.NORTH_WEST), modLoc("xor"),
OpBoolXor.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("e", HexDir.SOUTH_EAST), modLoc("greater"),
PatternRegistry.mapPattern(HexPattern.fromAngles("e", HexDir.SOUTH_EAST), modLoc("greater"),
new OpCompare(false, (a, b) -> a > b));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("q", HexDir.SOUTH_WEST), modLoc("less"),
PatternRegistry.mapPattern(HexPattern.fromAngles("q", HexDir.SOUTH_WEST), modLoc("less"),
new OpCompare(false, (a, b) -> a < b));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("ee", HexDir.SOUTH_EAST), modLoc("greater_eq"),
PatternRegistry.mapPattern(HexPattern.fromAngles("ee", HexDir.SOUTH_EAST), modLoc("greater_eq"),
new OpCompare(true, (a, b) -> a >= b));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qq", HexDir.SOUTH_WEST), modLoc("less_eq"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qq", HexDir.SOUTH_WEST), modLoc("less_eq"),
new OpCompare(true, (a, b) -> a <= b));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("ad", HexDir.EAST), modLoc("equals"),
PatternRegistry.mapPattern(HexPattern.fromAngles("ad", HexDir.EAST), modLoc("equals"),
new OpEquality(false));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("da", HexDir.EAST), modLoc("not_equals"),
PatternRegistry.mapPattern(HexPattern.fromAngles("da", HexDir.EAST), modLoc("not_equals"),
new OpEquality(true));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("dw", HexDir.NORTH_WEST), modLoc("not"),
PatternRegistry.mapPattern(HexPattern.fromAngles("dw", HexDir.NORTH_WEST), modLoc("not"),
OpBoolNot.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aw", HexDir.NORTH_EAST), modLoc("identity"),
PatternRegistry.mapPattern(HexPattern.fromAngles("aw", HexDir.NORTH_EAST), modLoc("identity"),
OpBoolIdentityKindOf.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eqqq", HexDir.NORTH_WEST), modLoc("random"),
PatternRegistry.mapPattern(HexPattern.fromAngles("eqqq", HexDir.NORTH_WEST), modLoc("random"),
OpRandom.INSTANCE);
// == Advanced Math ==
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqaa", HexDir.SOUTH_EAST), modLoc("sin"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqaa", HexDir.SOUTH_EAST), modLoc("sin"),
OpSin.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqad", HexDir.SOUTH_EAST), modLoc("cos"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqad", HexDir.SOUTH_EAST), modLoc("cos"),
OpCos.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wqqqqqadq", HexDir.SOUTH_WEST), modLoc("tan"),
PatternRegistry.mapPattern(HexPattern.fromAngles("wqqqqqadq", HexDir.SOUTH_WEST), modLoc("tan"),
OpTan.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("ddeeeee", HexDir.SOUTH_EAST), modLoc("arcsin"),
PatternRegistry.mapPattern(HexPattern.fromAngles("ddeeeee", HexDir.SOUTH_EAST), modLoc("arcsin"),
OpArcSin.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("adeeeee", HexDir.NORTH_EAST), modLoc("arccos"),
PatternRegistry.mapPattern(HexPattern.fromAngles("adeeeee", HexDir.NORTH_EAST), modLoc("arccos"),
OpArcCos.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eadeeeeew", HexDir.NORTH_EAST), modLoc("arctan"),
PatternRegistry.mapPattern(HexPattern.fromAngles("eadeeeeew", HexDir.NORTH_EAST), modLoc("arctan"),
OpArcTan.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eqaqe", HexDir.NORTH_WEST), modLoc("logarithm"),
PatternRegistry.mapPattern(HexPattern.fromAngles("eqaqe", HexDir.NORTH_WEST), modLoc("logarithm"),
OpLog.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("addwaad", HexDir.NORTH_EAST), modLoc("modulo"),
PatternRegistry.mapPattern(HexPattern.fromAngles("addwaad", HexDir.NORTH_EAST), modLoc("modulo"),
OpModulo.INSTANCE);
// == Sets ==
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wdweaqa", HexDir.NORTH_EAST), modLoc("and_bit"),
PatternRegistry.mapPattern(HexPattern.fromAngles("wdweaqa", HexDir.NORTH_EAST), modLoc("and_bit"),
OpAnd.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("waweaqa", HexDir.SOUTH_EAST), modLoc("or_bit"),
PatternRegistry.mapPattern(HexPattern.fromAngles("waweaqa", HexDir.SOUTH_EAST), modLoc("or_bit"),
OpOr.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("dwaeaqa", HexDir.NORTH_WEST), modLoc("xor_bit"),
PatternRegistry.mapPattern(HexPattern.fromAngles("dwaeaqa", HexDir.NORTH_WEST), modLoc("xor_bit"),
OpXor.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("dweaqa", HexDir.NORTH_WEST), modLoc("not_bit"),
PatternRegistry.mapPattern(HexPattern.fromAngles("dweaqa", HexDir.NORTH_WEST), modLoc("not_bit"),
OpNot.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aweaqa", HexDir.NORTH_EAST), modLoc("to_set"),
PatternRegistry.mapPattern(HexPattern.fromAngles("aweaqa", HexDir.NORTH_EAST), modLoc("to_set"),
OpToSet.INSTANCE);
// == Spells ==
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("de", HexDir.NORTH_EAST), modLoc("print"),
PatternRegistry.mapPattern(HexPattern.fromAngles("de", HexDir.NORTH_EAST), modLoc("print"),
OpPrint.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aawaawaa", HexDir.EAST), modLoc("explode"),
PatternRegistry.mapPattern(HexPattern.fromAngles("aawaawaa", HexDir.EAST), modLoc("explode"),
new OpExplode(false));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("ddwddwdd", HexDir.EAST), modLoc("explode/fire"),
PatternRegistry.mapPattern(HexPattern.fromAngles("ddwddwdd", HexDir.EAST), modLoc("explode/fire"),
new OpExplode(true));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("awqqqwaqw", HexDir.SOUTH_WEST), modLoc("add_motion"),
PatternRegistry.mapPattern(HexPattern.fromAngles("awqqqwaqw", HexDir.SOUTH_WEST), modLoc("add_motion"),
OpAddMotion.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("awqqqwaq", HexDir.SOUTH_WEST), modLoc("blink"),
PatternRegistry.mapPattern(HexPattern.fromAngles("awqqqwaq", HexDir.SOUTH_WEST), modLoc("blink"),
OpBlink.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qaqqqqq", HexDir.EAST), modLoc("break_block"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qaqqqqq", HexDir.EAST), modLoc("break_block"),
OpBreakBlock.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eeeeede", HexDir.SOUTH_WEST), modLoc("place_block"),
PatternRegistry.mapPattern(HexPattern.fromAngles("eeeeede", HexDir.SOUTH_WEST), modLoc("place_block"),
OpPlaceBlock.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("awddwqawqwawq", HexDir.EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("awddwqawqwawq", HexDir.EAST),
modLoc("colorize"),
OpColorize.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aqawqadaq", HexDir.SOUTH_EAST), modLoc("create_water"),
PatternRegistry.mapPattern(HexPattern.fromAngles("aqawqadaq", HexDir.SOUTH_EAST), modLoc("create_water"),
OpCreateWater.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("dedwedade", HexDir.SOUTH_WEST),
PatternRegistry.mapPattern(HexPattern.fromAngles("dedwedade", HexDir.SOUTH_WEST),
modLoc("destroy_water"),
OpDestroyWater.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aaqawawa", HexDir.SOUTH_EAST), modLoc("ignite"),
PatternRegistry.mapPattern(HexPattern.fromAngles("aaqawawa", HexDir.SOUTH_EAST), modLoc("ignite"),
OpIgnite.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("ddedwdwd", HexDir.SOUTH_WEST), modLoc("extinguish"),
PatternRegistry.mapPattern(HexPattern.fromAngles("ddedwdwd", HexDir.SOUTH_WEST), modLoc("extinguish"),
OpExtinguish.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqa", HexDir.NORTH_EAST), modLoc("conjure_block"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqa", HexDir.NORTH_EAST), modLoc("conjure_block"),
new OpConjure(false));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqd", HexDir.NORTH_EAST), modLoc("conjure_light"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqd", HexDir.NORTH_EAST), modLoc("conjure_light"),
new OpConjure(true));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wqaqwawqaqw", HexDir.NORTH_EAST), modLoc("bonemeal"),
PatternRegistry.mapPattern(HexPattern.fromAngles("wqaqwawqaqw", HexDir.NORTH_EAST), modLoc("bonemeal"),
OpTheOnlyReasonAnyoneDownloadedPsi.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqwaeaeaeaeaea", HexDir.NORTH_WEST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqwaeaeaeaeaea", HexDir.NORTH_WEST),
modLoc("recharge"),
OpRecharge.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qdqawwaww", HexDir.EAST), modLoc("erase"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qdqawwaww", HexDir.EAST), modLoc("erase"),
new OpErase());
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wqaqwd", HexDir.NORTH_EAST), modLoc("edify"),
PatternRegistry.mapPattern(HexPattern.fromAngles("wqaqwd", HexDir.NORTH_EAST), modLoc("edify"),
OpEdifySapling.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("adaa", HexDir.WEST), modLoc("beep"),
PatternRegistry.mapPattern(HexPattern.fromAngles("adaa", HexDir.WEST), modLoc("beep"),
OpBeep.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("waqqqqq", HexDir.EAST), modLoc("craft/cypher"),
PatternRegistry.mapPattern(HexPattern.fromAngles("waqqqqq", HexDir.EAST), modLoc("craft/cypher"),
new OpMakePackagedSpell<>(HexItems.CYPHER, ManaConstants.CRYSTAL_UNIT));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wwaqqqqqeaqeaeqqqeaeq", HexDir.EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("wwaqqqqqeaqeaeqqqeaeq", HexDir.EAST),
modLoc("craft/trinket"),
new OpMakePackagedSpell<>(HexItems.TRINKET, 5 * ManaConstants.CRYSTAL_UNIT));
PatternRegistry.mapPattern(
HexPattern.FromAnglesSig("wwaqqqqqeawqwqwqwqwqwwqqeadaeqqeqqeadaeqq", HexDir.EAST),
HexPattern.fromAngles("wwaqqqqqeawqwqwqwqwqwwqqeadaeqqeqqeadaeqq", HexDir.EAST),
modLoc("craft/artifact"),
new OpMakePackagedSpell<>(HexItems.ARTIFACT, 10 * ManaConstants.CRYSTAL_UNIT));
PatternRegistry.mapPattern(
HexPattern.FromAnglesSig("aqqqaqwwaqqqqqeqaqqqawwqwqwqwqwqw", HexDir.SOUTH_WEST),
HexPattern.fromAngles("aqqqaqwwaqqqqqeqaqqqawwqwqwqwqwqw", HexDir.SOUTH_WEST),
modLoc("craft/battery"),
OpMakeBattery.INSTANCE,
true);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqaqwawaw", HexDir.NORTH_WEST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqaqwawaw", HexDir.NORTH_WEST),
modLoc("potion/weakness"),
new OpPotionEffect(MobEffects.WEAKNESS, ManaConstants.DUST_UNIT / 10, true, false, false));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqawwawawd", HexDir.WEST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqawwawawd", HexDir.WEST),
modLoc("potion/levitation"),
new OpPotionEffect(MobEffects.LEVITATION, ManaConstants.DUST_UNIT / 5, false, false, false));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqaewawawe", HexDir.SOUTH_WEST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqaewawawe", HexDir.SOUTH_WEST),
modLoc("potion/wither"),
new OpPotionEffect(MobEffects.WITHER, ManaConstants.DUST_UNIT, true, false, false));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqadwawaww", HexDir.SOUTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqadwawaww", HexDir.SOUTH_EAST),
modLoc("potion/poison"),
new OpPotionEffect(MobEffects.POISON, ManaConstants.DUST_UNIT / 3, true, false, false));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqadwawaw", HexDir.SOUTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqadwawaw", HexDir.SOUTH_EAST),
modLoc("potion/slowness"),
new OpPotionEffect(MobEffects.MOVEMENT_SLOWDOWN, ManaConstants.DUST_UNIT / 3, true, false, false));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqaawawaedd", HexDir.NORTH_WEST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqaawawaedd", HexDir.NORTH_WEST),
modLoc("potion/regeneration"),
new OpPotionEffect(MobEffects.REGENERATION, ManaConstants.DUST_UNIT, true, true, true), true);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqaawawaeqdd", HexDir.WEST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqaawawaeqdd", HexDir.WEST),
modLoc("potion/night_vision"),
new OpPotionEffect(MobEffects.NIGHT_VISION, ManaConstants.DUST_UNIT / 5, false, true, true), true);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqaawawaeqqdd", HexDir.SOUTH_WEST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqaawawaeqqdd", HexDir.SOUTH_WEST),
modLoc("potion/absorption"),
new OpPotionEffect(MobEffects.ABSORPTION, ManaConstants.DUST_UNIT, true, true, true), true);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qaawawaeqqqdd", HexDir.SOUTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qaawawaeqqqdd", HexDir.SOUTH_EAST),
modLoc("potion/haste"),
new OpPotionEffect(MobEffects.DIG_SPEED, ManaConstants.DUST_UNIT / 3, true, true, true), true);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aawawaeqqqqdd", HexDir.EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("aawawaeqqqqdd", HexDir.EAST),
modLoc("potion/strength"),
new OpPotionEffect(MobEffects.DAMAGE_BOOST, ManaConstants.DUST_UNIT / 3, true, true, true), true);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("waeawae", HexDir.EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("waeawae", HexDir.EAST),
modLoc("sentinel/create"),
new OpCreateSentinel(false));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qdwdqdw", HexDir.NORTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qdwdqdw", HexDir.NORTH_EAST),
modLoc("sentinel/destroy"),
OpDestroySentinel.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("waeawaede", HexDir.EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("waeawaede", HexDir.EAST),
modLoc("sentinel/get_pos"),
OpGetSentinelPos.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("waeawaedwa", HexDir.EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("waeawaedwa", HexDir.EAST),
modLoc("sentinel/wayfind"),
OpGetSentinelWayfind.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("waadwawdaaweewq", HexDir.EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("waadwawdaaweewq", HexDir.EAST),
modLoc("lightning"), OpLightning.INSTANCE, true);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eawwaeawawaa", HexDir.NORTH_WEST),
PatternRegistry.mapPattern(HexPattern.fromAngles("eawwaeawawaa", HexDir.NORTH_WEST),
modLoc("flight"), OpFlight.INSTANCE, true);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eaqawqadaqd", HexDir.EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("eaqawqadaqd", HexDir.EAST),
modLoc("create_lava"), OpCreateLava.INSTANCE, true);
PatternRegistry.mapPattern(
HexPattern.FromAnglesSig("wwwqqqwwwqqeqqwwwqqwqqdqqqqqdqq", HexDir.EAST),
HexPattern.fromAngles("wwwqqqwwwqqeqqwwwqqwqqdqqqqqdqq", HexDir.EAST),
modLoc("teleport"), OpTeleport.INSTANCE, true);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("waeawaeqqqwqwqqwq", HexDir.EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("waeawaeqqqwqwqqwq", HexDir.EAST),
modLoc("sentinel/create/great"),
new OpCreateSentinel(true), true);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eeewwweeewwaqqddqdqd", HexDir.EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("eeewwweeewwaqqddqdqd", HexDir.EAST),
modLoc("dispel_rain"),
new OpWeather(false), true);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wwweeewwweewdawdwad", HexDir.WEST),
PatternRegistry.mapPattern(HexPattern.fromAngles("wwweeewwweewdawdwad", HexDir.WEST),
modLoc("summon_rain"),
new OpWeather(true), true);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qeqwqwqwqwqeqaeqeaqeqaeqaqded", HexDir.NORTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qeqwqwqwqwqeqaeqeaqeqaeqaqded", HexDir.NORTH_EAST),
modLoc("brainsweep"),
OpBrainsweep.INSTANCE, true);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqwqqqqqaq", HexDir.WEST), modLoc("akashic/read"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqwqqqqqaq", HexDir.WEST), modLoc("akashic/read"),
OpAkashicRead.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eeeweeeeede", HexDir.EAST), modLoc("akashic/write"),
PatternRegistry.mapPattern(HexPattern.fromAngles("eeeweeeeede", HexDir.EAST), modLoc("akashic/write"),
OpAkashicWrite.INSTANCE);
// == Meta stuff ==
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqq", HexDir.WEST), modLoc("open_paren"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqq", HexDir.WEST), modLoc("open_paren"),
Widget.OPEN_PAREN);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eee", HexDir.EAST), modLoc("close_paren"),
PatternRegistry.mapPattern(HexPattern.fromAngles("eee", HexDir.EAST), modLoc("close_paren"),
Widget.CLOSE_PAREN);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqaw", HexDir.WEST), modLoc("escape"), Widget.ESCAPE);
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqaw", HexDir.WEST), modLoc("escape"), Widget.ESCAPE);
// http://www.toroidalsnark.net/mkss3-pix/CalderheadJMM2014.pdf
// eval being a space filling curve feels apt doesn't it
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("deaqq", HexDir.SOUTH_EAST), modLoc("eval"),
PatternRegistry.mapPattern(HexPattern.fromAngles("deaqq", HexDir.SOUTH_EAST), modLoc("eval"),
OpEval.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aqdee", HexDir.SOUTH_WEST), modLoc("halt"),
PatternRegistry.mapPattern(HexPattern.fromAngles("aqdee", HexDir.SOUTH_WEST), modLoc("halt"),
OpHalt.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aqqqqq", HexDir.EAST), modLoc("read"),
PatternRegistry.mapPattern(HexPattern.fromAngles("aqqqqq", HexDir.EAST), modLoc("read"),
OpRead.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("deeeee", HexDir.EAST), modLoc("write"),
PatternRegistry.mapPattern(HexPattern.fromAngles("deeeee", HexDir.EAST), modLoc("write"),
OpWrite.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aqqqqqe", HexDir.EAST), modLoc("readable"),
PatternRegistry.mapPattern(HexPattern.fromAngles("aqqqqqe", HexDir.EAST), modLoc("readable"),
OpReadable.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("deeeeeq", HexDir.EAST), modLoc("writable"),
PatternRegistry.mapPattern(HexPattern.fromAngles("deeeeeq", HexDir.EAST), modLoc("writable"),
OpWritable.INSTANCE);
// lorge boyes
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wawqwqwqwqwqw", HexDir.EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("wawqwqwqwqwqw", HexDir.EAST),
modLoc("read/entity"), OpTheCoolerRead.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wawqwqwqwqwqwew", HexDir.EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("wawqwqwqwqwqwew", HexDir.EAST),
modLoc("readable/entity"), OpTheCoolerReadable.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qeewdweddw", HexDir.NORTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qeewdweddw", HexDir.NORTH_EAST),
modLoc("read/local"), OpPeekLocal.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eqqwawqaaw", HexDir.NORTH_WEST),
PatternRegistry.mapPattern(HexPattern.fromAngles("eqqwawqaaw", HexDir.NORTH_WEST),
modLoc("write/local"), OpPushLocal.INSTANCE);
// == Consts ==
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("d", HexDir.EAST), modLoc("const/null"), Widget.NULL);
PatternRegistry.mapPattern(HexPattern.fromAngles("d", HexDir.EAST), modLoc("const/null"), Widget.NULL);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqea", HexDir.NORTH_WEST), modLoc("const/vec/px"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqea", HexDir.NORTH_WEST), modLoc("const/vec/px"),
Operator.makeConstantOp(SpellDatum.make(new Vec3(1.0, 0.0, 0.0))));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqew", HexDir.NORTH_WEST), modLoc("const/vec/py"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqew", HexDir.NORTH_WEST), modLoc("const/vec/py"),
Operator.makeConstantOp(SpellDatum.make(new Vec3(0.0, 1.0, 0.0))));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqed", HexDir.NORTH_WEST), modLoc("const/vec/pz"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqed", HexDir.NORTH_WEST), modLoc("const/vec/pz"),
Operator.makeConstantOp(SpellDatum.make(new Vec3(0.0, 0.0, 1.0))));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eeeeeqa", HexDir.SOUTH_WEST), modLoc("const/vec/nx"),
PatternRegistry.mapPattern(HexPattern.fromAngles("eeeeeqa", HexDir.SOUTH_WEST), modLoc("const/vec/nx"),
Operator.makeConstantOp(SpellDatum.make(new Vec3(-1.0, 0.0, 0.0))));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eeeeeqw", HexDir.SOUTH_WEST), modLoc("const/vec/ny"),
PatternRegistry.mapPattern(HexPattern.fromAngles("eeeeeqw", HexDir.SOUTH_WEST), modLoc("const/vec/ny"),
Operator.makeConstantOp(SpellDatum.make(new Vec3(0.0, -1.0, 0.0))));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eeeeeqd", HexDir.SOUTH_WEST), modLoc("const/vec/nz"),
PatternRegistry.mapPattern(HexPattern.fromAngles("eeeeeqd", HexDir.SOUTH_WEST), modLoc("const/vec/nz"),
Operator.makeConstantOp(SpellDatum.make(new Vec3(0.0, 0.0, -1.0))));
// Yep, this is what I spend the "plain hexagon" pattern on.
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqq", HexDir.NORTH_WEST), modLoc("const/vec/0"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqq", HexDir.NORTH_WEST), modLoc("const/vec/0"),
Operator.makeConstantOp(SpellDatum.make(new Vec3(0.0, 0.0, 0.0))));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qdwdq", HexDir.NORTH_EAST), modLoc("const/double/pi"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qdwdq", HexDir.NORTH_EAST), modLoc("const/double/pi"),
Operator.makeConstantOp(SpellDatum.make(Math.PI)));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eawae", HexDir.NORTH_WEST), modLoc("const/double/tau"),
PatternRegistry.mapPattern(HexPattern.fromAngles("eawae", HexDir.NORTH_WEST), modLoc("const/double/tau"),
Operator.makeConstantOp(SpellDatum.make(HexUtils.TAU)));
// e
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aaq", HexDir.EAST), modLoc("const/double/e"),
PatternRegistry.mapPattern(HexPattern.fromAngles("aaq", HexDir.EAST), modLoc("const/double/e"),
Operator.makeConstantOp(SpellDatum.make(Math.E)));
// == Entities ==
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqdaqa", HexDir.SOUTH_EAST), modLoc("get_entity"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqdaqa", HexDir.SOUTH_EAST), modLoc("get_entity"),
new OpGetEntityAt(e -> true));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqdaqaawa", HexDir.SOUTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqdaqaawa", HexDir.SOUTH_EAST),
modLoc("get_entity/animal"),
new OpGetEntityAt(OpGetEntitiesBy::isAnimal));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqdaqaawq", HexDir.SOUTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqdaqaawq", HexDir.SOUTH_EAST),
modLoc("get_entity/monster"),
new OpGetEntityAt(OpGetEntitiesBy::isMonster));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqdaqaaww", HexDir.SOUTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqdaqaaww", HexDir.SOUTH_EAST),
modLoc("get_entity/item"),
new OpGetEntityAt(OpGetEntitiesBy::isItem));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqdaqaawe", HexDir.SOUTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqdaqaawe", HexDir.SOUTH_EAST),
modLoc("get_entity/player"),
new OpGetEntityAt(OpGetEntitiesBy::isPlayer));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqdaqaawd", HexDir.SOUTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqdaqaawd", HexDir.SOUTH_EAST),
modLoc("get_entity/living"),
new OpGetEntityAt(OpGetEntitiesBy::isLiving));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqwded", HexDir.SOUTH_EAST), modLoc("zone_entity"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqwded", HexDir.SOUTH_EAST), modLoc("zone_entity"),
new OpGetEntitiesBy(e -> true, false));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqwdeddwa", HexDir.SOUTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqwdeddwa", HexDir.SOUTH_EAST),
modLoc("zone_entity/animal"),
new OpGetEntitiesBy(OpGetEntitiesBy::isAnimal, false));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eeeeewaqaawa", HexDir.NORTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("eeeeewaqaawa", HexDir.NORTH_EAST),
modLoc("zone_entity/not_animal"),
new OpGetEntitiesBy(OpGetEntitiesBy::isAnimal, true));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqwdeddwq", HexDir.SOUTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqwdeddwq", HexDir.SOUTH_EAST),
modLoc("zone_entity/monster"),
new OpGetEntitiesBy(OpGetEntitiesBy::isMonster, false));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eeeeewaqaawq", HexDir.NORTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("eeeeewaqaawq", HexDir.NORTH_EAST),
modLoc("zone_entity/not_monster"),
new OpGetEntitiesBy(OpGetEntitiesBy::isMonster, true));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqwdeddww", HexDir.SOUTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqwdeddww", HexDir.SOUTH_EAST),
modLoc("zone_entity/item"),
new OpGetEntitiesBy(OpGetEntitiesBy::isItem, false));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eeeeewaqaaww", HexDir.NORTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("eeeeewaqaaww", HexDir.NORTH_EAST),
modLoc("zone_entity/not_item"),
new OpGetEntitiesBy(OpGetEntitiesBy::isItem, true));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqwdeddwe", HexDir.SOUTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqwdeddwe", HexDir.SOUTH_EAST),
modLoc("zone_entity/player"),
new OpGetEntitiesBy(OpGetEntitiesBy::isPlayer, false));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eeeeewaqaawe", HexDir.NORTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("eeeeewaqaawe", HexDir.NORTH_EAST),
modLoc("zone_entity/not_player"),
new OpGetEntitiesBy(OpGetEntitiesBy::isPlayer, true));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqwdeddwd", HexDir.SOUTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqqqwdeddwd", HexDir.SOUTH_EAST),
modLoc("zone_entity/living"),
new OpGetEntitiesBy(OpGetEntitiesBy::isLiving, false));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eeeeewaqaawd", HexDir.NORTH_EAST),
PatternRegistry.mapPattern(HexPattern.fromAngles("eeeeewaqaawd", HexDir.NORTH_EAST),
modLoc("zone_entity/not_living"),
new OpGetEntitiesBy(OpGetEntitiesBy::isLiving, true));
// == Lists ==
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("edqde", HexDir.SOUTH_WEST), modLoc("append"),
PatternRegistry.mapPattern(HexPattern.fromAngles("edqde", HexDir.SOUTH_WEST), modLoc("append"),
OpAppend.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qaeaq", HexDir.NORTH_WEST), modLoc("concat"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qaeaq", HexDir.NORTH_WEST), modLoc("concat"),
OpConcat.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("deeed", HexDir.NORTH_WEST), modLoc("index"),
PatternRegistry.mapPattern(HexPattern.fromAngles("deeed", HexDir.NORTH_WEST), modLoc("index"),
OpIndex.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("dadad", HexDir.NORTH_EAST), modLoc("for_each"),
PatternRegistry.mapPattern(HexPattern.fromAngles("dadad", HexDir.NORTH_EAST), modLoc("for_each"),
OpForEach.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aqaeaq", HexDir.EAST), modLoc("list_size"),
PatternRegistry.mapPattern(HexPattern.fromAngles("aqaeaq", HexDir.EAST), modLoc("list_size"),
OpListSize.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("adeeed", HexDir.EAST), modLoc("singleton"),
PatternRegistry.mapPattern(HexPattern.fromAngles("adeeed", HexDir.EAST), modLoc("singleton"),
OpSingleton.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqaeaae", HexDir.NORTH_EAST), modLoc("empty_list"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqaeaae", HexDir.NORTH_EAST), modLoc("empty_list"),
OpEmptyList.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqaede", HexDir.EAST), modLoc("reverse_list"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qqqaede", HexDir.EAST), modLoc("reverse_list"),
OpReverski.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("ewdqdwe", HexDir.SOUTH_WEST), modLoc("last_n_list"),
PatternRegistry.mapPattern(HexPattern.fromAngles("ewdqdwe", HexDir.SOUTH_WEST), modLoc("last_n_list"),
OpLastNToList.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qwaeawq", HexDir.NORTH_WEST), modLoc("splat"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qwaeawq", HexDir.NORTH_WEST), modLoc("splat"),
OpSplat.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("dedqde", HexDir.EAST), modLoc("index_of"),
PatternRegistry.mapPattern(HexPattern.fromAngles("dedqde", HexDir.EAST), modLoc("index_of"),
OpIndexOf.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("edqdewaqa", HexDir.SOUTH_WEST), modLoc("list_remove"),
PatternRegistry.mapPattern(HexPattern.fromAngles("edqdewaqa", HexDir.SOUTH_WEST), modLoc("list_remove"),
OpRemove.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qaeaqwded", HexDir.NORTH_WEST), modLoc("slice"),
PatternRegistry.mapPattern(HexPattern.fromAngles("qaeaqwded", HexDir.NORTH_WEST), modLoc("slice"),
OpSlice.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wqaeaqw", HexDir.NORTH_WEST), modLoc("modify_in_place"),
PatternRegistry.mapPattern(HexPattern.fromAngles("wqaeaqw", HexDir.NORTH_WEST), modLoc("modify_in_place"),
OpModifyInPlace.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("ddewedd", HexDir.SOUTH_EAST), modLoc("construct"),
PatternRegistry.mapPattern(HexPattern.fromAngles("ddewedd", HexDir.SOUTH_EAST), modLoc("construct"),
OpCons.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aaqwqaa", HexDir.SOUTH_WEST), modLoc("deconstruct"),
PatternRegistry.mapPattern(HexPattern.fromAngles("aaqwqaa", HexDir.SOUTH_WEST), modLoc("deconstruct"),
OpUnCons.INSTANCE);
} catch (PatternRegistry.RegisterPatternException exn) {

View file

@ -1,14 +1,8 @@
package at.petrak.hexcasting.common.casting.operators.eval
import at.petrak.hexcasting.api.spell.OperationResult
import at.petrak.hexcasting.api.spell.Operator
import at.petrak.hexcasting.api.spell.getChecked
import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.SpellList
import at.petrak.hexcasting.api.spell.*
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.CastingHarness
import at.petrak.hexcasting.api.spell.casting.ContinuationFrame
import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect
import at.petrak.hexcasting.api.spell.casting.SpellContinuation
object OpEval : Operator {
@ -22,7 +16,7 @@ object OpEval : Operator {
val newCont = if (continuation is SpellContinuation.NotDone && continuation.frame is ContinuationFrame.FinishEval) {
continuation
} else {
continuation.pushFrame(ContinuationFrame.FinishEval()) // install a break-boundary after eval
continuation.pushFrame(ContinuationFrame.FinishEval) // install a break-boundary after eval
}
val frame = ContinuationFrame.Evaluate(instrs)

View file

@ -1,7 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.math
import at.petrak.hexcasting.api.spell.ConstManaOperator
import at.petrak.hexcasting.api.spell.GetNumOrVec
import at.petrak.hexcasting.api.spell.numOrVec
import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.asSpellResult
import at.petrak.hexcasting.api.spell.casting.CastingContext
@ -12,7 +12,7 @@ object OpAbsLen : ConstManaOperator {
get() = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val x = GetNumOrVec(args[0], 0)
val x = numOrVec(args[0], 0)
return x.map({ num -> num.absoluteValue }, { vec -> vec.length() }).asSpellResult
}

View file

@ -1,7 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.math
import at.petrak.hexcasting.api.spell.ConstManaOperator
import at.petrak.hexcasting.api.spell.GetNumOrVec
import at.petrak.hexcasting.api.spell.numOrVec
import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.spellListOf
@ -11,8 +11,8 @@ object OpAdd : ConstManaOperator {
get() = 2
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val lhs = GetNumOrVec(args[0], 1)
val rhs = GetNumOrVec(args[1], 0)
val lhs = numOrVec(args[0], 1)
val rhs = numOrVec(args[1], 0)
return spellListOf(
lhs.map({ lnum ->

View file

@ -1,7 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.math
import at.petrak.hexcasting.api.spell.ConstManaOperator
import at.petrak.hexcasting.api.spell.GetNumOrVec
import at.petrak.hexcasting.api.spell.numOrVec
import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.mishaps.MishapDivideByZero
@ -13,8 +13,8 @@ object OpDivCross : ConstManaOperator {
get() = 2
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val lhs = GetNumOrVec(args[0], 1)
val rhs = GetNumOrVec(args[1], 0)
val lhs = numOrVec(args[0], 1)
val rhs = numOrVec(args[1], 0)
return spellListOf(
lhs.map({ lnum ->

View file

@ -1,7 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.math
import at.petrak.hexcasting.api.spell.ConstManaOperator
import at.petrak.hexcasting.api.spell.GetNumOrVec
import at.petrak.hexcasting.api.spell.numOrVec
import at.petrak.hexcasting.api.spell.spellListOf
import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.casting.CastingContext
@ -11,8 +11,8 @@ object OpMulDot : ConstManaOperator {
get() = 2
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val lhs = GetNumOrVec(args[0], 1)
val rhs = GetNumOrVec(args[1], 0)
val lhs = numOrVec(args[0], 1)
val rhs = numOrVec(args[1], 0)
return spellListOf(
lhs.map({ lnum ->

View file

@ -1,7 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.math
import at.petrak.hexcasting.api.spell.ConstManaOperator
import at.petrak.hexcasting.api.spell.GetNumOrVec
import at.petrak.hexcasting.api.spell.numOrVec
import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.mishaps.MishapDivideByZero
@ -14,8 +14,8 @@ object OpPowProj : ConstManaOperator {
get() = 2
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val lhs = GetNumOrVec(args[0], 1)
val rhs = GetNumOrVec(args[1], 0)
val lhs = numOrVec(args[0], 1)
val rhs = numOrVec(args[1], 0)
return spellListOf(
lhs.map({ lnum ->

View file

@ -1,7 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.math
import at.petrak.hexcasting.api.spell.ConstManaOperator
import at.petrak.hexcasting.api.spell.GetNumOrVec
import at.petrak.hexcasting.api.spell.numOrVec
import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.spellListOf
@ -12,8 +12,8 @@ object OpSub : ConstManaOperator {
get() = 2
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val lhs = GetNumOrVec(args[0], 1)
val rhs = GetNumOrVec(args[1], 0)
val lhs = numOrVec(args[0], 1)
val rhs = numOrVec(args[1], 0)
return spellListOf(
lhs.map({ lnum ->

View file

@ -8,7 +8,7 @@ object OpAnd : ConstManaOperator {
override val argc = 2
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val firstParam = GetNumOrList(args[0], 0)
val firstParam = numOrList(args[0], 0)
if (firstParam.right().isPresent) {
val list1 = firstParam.right().get()

View file

@ -8,7 +8,7 @@ object OpOr : ConstManaOperator {
override val argc = 2
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val firstParam = GetNumOrList(args[0], 0)
val firstParam = numOrList(args[0], 0)
if (firstParam.right().isPresent) {
val list1 = firstParam.right().get()

View file

@ -8,7 +8,7 @@ object OpXor : ConstManaOperator {
override val argc = 2
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val firstParam = GetNumOrList(args[0], 0)
val firstParam = numOrList(args[0], 0)
if (firstParam.right().isPresent) {
val list1 = firstParam.right().get()

View file

@ -27,7 +27,7 @@ object OpBeep : SpellOperator {
return Triple(
Spell(target, note, NoteBlockInstrument.values()[instrument]),
ManaConstants.DUST_UNIT / 10,
listOf(ParticleSpray.Cloud(target, 1.0))
listOf(ParticleSpray.cloud(target, 1.0))
)
}

View file

@ -39,8 +39,8 @@ object OpBlink : SpellOperator {
Spell(target, delta),
ManaConstants.SHARD_UNIT * delta.roundToInt(),
listOf(
ParticleSpray.Cloud(targetMiddlePos, 2.0, 50),
ParticleSpray.Burst(targetMiddlePos.add(dvec), 2.0, 100)
ParticleSpray.cloud(targetMiddlePos, 2.0, 50),
ParticleSpray.burst(targetMiddlePos.add(dvec), 2.0, 100)
)
)
}

View file

@ -27,7 +27,7 @@ object OpBreakBlock : SpellOperator {
return Triple(
Spell(pos),
ManaConstants.DUST_UNIT * 2,
listOf(ParticleSpray.Burst(centered, 1.0))
listOf(ParticleSpray.burst(centered, 1.0))
)
}

View file

@ -36,7 +36,7 @@ class OpConjure(val light: Boolean) : SpellOperator {
return Triple(
Spell(target, light),
ManaConstants.DUST_UNIT,
listOf(ParticleSpray.Cloud(Vec3.atCenterOf(pos), 1.0))
listOf(ParticleSpray.cloud(Vec3.atCenterOf(pos), 1.0))
)
}

View file

@ -31,7 +31,7 @@ object OpCreateWater : SpellOperator {
return Triple(
Spell(target),
ManaConstants.DUST_UNIT,
listOf(ParticleSpray.Burst(Vec3.atCenterOf(BlockPos(target)), 1.0))
listOf(ParticleSpray.burst(Vec3.atCenterOf(BlockPos(target)), 1.0))
)
}

View file

@ -29,7 +29,7 @@ object OpDestroyWater : SpellOperator {
return Triple(
Spell(target),
2 * ManaConstants.CRYSTAL_UNIT,
listOf(ParticleSpray.Burst(target, 3.0))
listOf(ParticleSpray.burst(target, 3.0))
)
}

View file

@ -22,7 +22,7 @@ class OpExplode(val fire: Boolean) : SpellOperator {
return Triple(
Spell(pos, strength, this.fire),
((1 + Mth.clamp(strength.toFloat(), 0f, 10f) + if (this.fire) 2 else 0) * ManaConstants.SHARD_UNIT).toInt(),
listOf(ParticleSpray.Burst(pos, strength, 50))
listOf(ParticleSpray.burst(pos, strength, 50))
)
}

View file

@ -28,7 +28,7 @@ object OpExtinguish : SpellOperator {
return Triple(
Spell(target),
ManaConstants.CRYSTAL_UNIT,
listOf(ParticleSpray.Burst(target, 1.0))
listOf(ParticleSpray.burst(target, 1.0))
)
}

View file

@ -31,7 +31,7 @@ object OpIgnite : SpellOperator {
return Triple(
Spell(target),
ManaConstants.DUST_UNIT,
listOf(ParticleSpray.Burst(Vec3.atCenterOf(BlockPos(target)), 1.0))
listOf(ParticleSpray.burst(Vec3.atCenterOf(BlockPos(target)), 1.0))
)
}

View file

@ -10,7 +10,8 @@ import at.petrak.hexcasting.api.spell.SpellOperator
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.mishaps.MishapBadItem
import at.petrak.hexcasting.api.spell.mishaps.MishapBadOffhandItem
import at.petrak.hexcasting.api.utils.ManaHelper
import at.petrak.hexcasting.api.utils.extractMana
import at.petrak.hexcasting.api.utils.isManaItem
import at.petrak.hexcasting.common.items.magic.ItemManaHolder
import at.petrak.hexcasting.common.lib.HexItems
import net.minecraft.world.entity.item.ItemEntity
@ -46,7 +47,7 @@ object OpMakeBattery : SpellOperator {
ctx.assertEntityInRange(entity)
if (!ManaHelper.isManaItem(entity.item) || ManaHelper.extractMana(
if (!isManaItem(entity.item) || extractMana(
entity.item,
drainForBatteries = true,
simulate = true
@ -59,7 +60,7 @@ object OpMakeBattery : SpellOperator {
}
return Triple(Spell(entity),
ManaConstants.CRYSTAL_UNIT, listOf(ParticleSpray.Burst(entity.position(), 0.5)))
ManaConstants.CRYSTAL_UNIT, listOf(ParticleSpray.burst(entity.position(), 0.5)))
}
private data class Spell(val itemEntity: ItemEntity) : RenderedSpell {
@ -67,7 +68,7 @@ object OpMakeBattery : SpellOperator {
val (handStack, hand) = ctx.getHeldItemToOperateOn { it.`is`(HexItemTags.PHIAL_BASE) }
if (handStack.`is`(HexItemTags.PHIAL_BASE) && itemEntity.isAlive) {
val entityStack = itemEntity.item.copy()
val manaAmt = ManaHelper.extractMana(entityStack, drainForBatteries = true)
val manaAmt = extractMana(entityStack, drainForBatteries = true)
if (manaAmt > 0) {
ctx.caster.setItemInHand(
hand,

View file

@ -10,7 +10,8 @@ import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.mishaps.MishapBadItem
import at.petrak.hexcasting.api.spell.mishaps.MishapBadOffhandItem
import at.petrak.hexcasting.api.spell.mishaps.MishapOthersName
import at.petrak.hexcasting.api.utils.ManaHelper
import at.petrak.hexcasting.api.utils.extractMana
import at.petrak.hexcasting.api.utils.isManaItem
import at.petrak.hexcasting.common.items.magic.ItemPackagedHex
import at.petrak.hexcasting.xplat.IXplatAbstractions
import net.minecraft.world.entity.item.ItemEntity
@ -36,7 +37,7 @@ class OpMakePackagedSpell<T : ItemPackagedHex>(val itemType: T, val cost: Int) :
}
ctx.assertEntityInRange(entity)
if (!ManaHelper.isManaItem(entity.item) || ManaHelper.extractMana(
if (!isManaItem(entity.item) || extractMana(
entity.item,
drainForBatteries = true,
simulate = true
@ -52,7 +53,7 @@ class OpMakePackagedSpell<T : ItemPackagedHex>(val itemType: T, val cost: Int) :
if (trueName != null)
throw MishapOthersName(trueName)
return Triple(Spell(entity, patterns), cost, listOf(ParticleSpray.Burst(entity.position(), 0.5)))
return Triple(Spell(entity, patterns), cost, listOf(ParticleSpray.burst(entity.position(), 0.5)))
}
private inner class Spell(val itemEntity: ItemEntity, val patterns: List<SpellDatum<*>>) : RenderedSpell {
@ -64,7 +65,7 @@ class OpMakePackagedSpell<T : ItemPackagedHex>(val itemType: T, val cost: Int) :
&& itemEntity.isAlive
) {
val entityStack = itemEntity.item.copy()
val manaAmt = ManaHelper.extractMana(entityStack, drainForBatteries = true)
val manaAmt = extractMana(entityStack, drainForBatteries = true)
if (manaAmt > 0) {
hexHolder.writeHex(patterns, manaAmt)
}

View file

@ -45,7 +45,7 @@ object OpPlaceBlock : SpellOperator {
return Triple(
Spell(target),
ManaConstants.DUST_UNIT,
listOf(ParticleSpray.Cloud(Vec3.atCenterOf(pos), 1.0))
listOf(ParticleSpray.cloud(Vec3.atCenterOf(pos), 1.0))
)
}

View file

@ -44,7 +44,7 @@ class OpPotionEffect(
return Triple(
Spell(effect, target, duration, potency),
cost.toInt(),
listOf(ParticleSpray.Cloud(target.position().add(0.0, target.eyeHeight / 2.0, 0.0), 1.0))
listOf(ParticleSpray.cloud(target.position().add(0.0, target.eyeHeight / 2.0, 0.0), 1.0))
)
}

View file

@ -10,7 +10,8 @@ import at.petrak.hexcasting.api.spell.SpellOperator
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.mishaps.MishapBadItem
import at.petrak.hexcasting.api.spell.mishaps.MishapBadOffhandItem
import at.petrak.hexcasting.api.utils.ManaHelper
import at.petrak.hexcasting.api.utils.extractMana
import at.petrak.hexcasting.api.utils.isManaItem
import at.petrak.hexcasting.xplat.IXplatAbstractions
import net.minecraft.world.entity.item.ItemEntity
@ -37,7 +38,7 @@ object OpRecharge : SpellOperator {
val entity = args.getChecked<ItemEntity>(0, argc)
ctx.assertEntityInRange(entity)
if (!ManaHelper.isManaItem(entity.item)) {
if (!isManaItem(entity.item)) {
throw MishapBadItem.of(
entity,
"mana"
@ -48,7 +49,7 @@ object OpRecharge : SpellOperator {
return null
return Triple(Spell(entity),
ManaConstants.CRYSTAL_UNIT, listOf(ParticleSpray.Burst(entity.position(), 0.5)))
ManaConstants.CRYSTAL_UNIT, listOf(ParticleSpray.burst(entity.position(), 0.5)))
}
private data class Spell(val itemEntity: ItemEntity) : RenderedSpell {
@ -65,7 +66,7 @@ object OpRecharge : SpellOperator {
val maxMana = mana.maxMana
val existingMana = mana.mana
val manaAmt = ManaHelper.extractMana(entityStack, maxMana - existingMana)
val manaAmt = extractMana(entityStack, maxMana - existingMana)
mana.mana = manaAmt + existingMana

View file

@ -29,7 +29,7 @@ object OpTheOnlyReasonAnyoneDownloadedPsi : SpellOperator {
return Triple(
Spell(target),
ManaConstants.DUST_UNIT,
listOf(ParticleSpray.Burst(Vec3.atCenterOf(BlockPos(target)), 1.0))
listOf(ParticleSpray.burst(Vec3.atCenterOf(BlockPos(target)), 1.0))
)
}

View file

@ -47,7 +47,7 @@ object OpBrainsweep : SpellOperator {
return Triple(
Spell(bpos, state, sacrifice, recipe),
10 * ManaConstants.CRYSTAL_UNIT,
listOf(ParticleSpray.Cloud(sacrifice.position(), 1.0), ParticleSpray.Burst(Vec3.atCenterOf(bpos), 0.3, 100))
listOf(ParticleSpray.cloud(sacrifice.position(), 1.0), ParticleSpray.burst(Vec3.atCenterOf(bpos), 0.3, 100))
)
}

View file

@ -31,7 +31,7 @@ object OpCreateLava : SpellOperator {
return Triple(
Spell(target),
ManaConstants.CRYSTAL_UNIT,
listOf(ParticleSpray.Burst(Vec3.atCenterOf(BlockPos(target)), 1.0)),
listOf(ParticleSpray.burst(Vec3.atCenterOf(BlockPos(target)), 1.0)),
)
}

View file

@ -36,7 +36,7 @@ object OpTeleport : SpellOperator {
return Triple(
Spell(teleportee, delta),
10 * ManaConstants.CRYSTAL_UNIT,
listOf(ParticleSpray.Cloud(targetMiddlePos, 2.0), ParticleSpray.Burst(targetMiddlePos.add(delta), 2.0))
listOf(ParticleSpray.cloud(targetMiddlePos, 2.0), ParticleSpray.burst(targetMiddlePos.add(delta), 2.0))
)
}

View file

@ -26,7 +26,7 @@ class OpCreateSentinel(val extendsRange: Boolean) : SpellOperator {
return Triple(
Spell(target, this.extendsRange),
ManaConstants.DUST_UNIT,
listOf(ParticleSpray.Burst(target, 2.0))
listOf(ParticleSpray.burst(target, 2.0))
)
}

View file

@ -22,7 +22,7 @@ object OpDestroySentinel : SpellOperator {
// TODO why can't you remove things from other dimensions?
if (sentinel.dimension != ctx.world.dimension())
throw MishapLocationInWrongDimension(sentinel.dimension.location())
particles.add(ParticleSpray.Cloud(sentinel.position, 2.0))
particles.add(ParticleSpray.cloud(sentinel.position, 2.0))
return Triple(
Spell,

View file

@ -32,7 +32,7 @@ public class ListPatternsCommand {
for (var pair : listing) {
ctx.getSource().sendSuccess(new TextComponent(pair.getValue().getFirst().toString())
.append(": ")
.append(SpellDatum.make(HexPattern.FromAnglesSig(pair.getKey(), pair.getValue().getSecond()))
.append(SpellDatum.make(HexPattern.fromAngles(pair.getKey(), pair.getValue().getSecond()))
.display()), false);
}
@ -86,7 +86,7 @@ public class ListPatternsCommand {
var tag = new CompoundTag();
tag.putString(ItemScroll.TAG_OP_ID, opId.toString());
tag.put(ItemScroll.TAG_PATTERN,
HexPattern.FromAnglesSig(pattern, startDir).serializeToNBT());
HexPattern.fromAngles(pattern, startDir).serializeToNBT());
var stack = new ItemStack(HexItems.SCROLL);
stack.setTag(tag);

View file

@ -38,7 +38,7 @@ public class PatternResLocArgument extends ResourceLocationArgument {
for (var key : lookup.keySet()) {
var rhs = lookup.get(key);
if (rhs.getFirst().equals(targetId)) {
foundPat = HexPattern.FromAnglesSig(key, rhs.getSecond());
foundPat = HexPattern.fromAngles(key, rhs.getSecond());
break;
}
}

View file

@ -1,6 +1,5 @@
package at.petrak.hexcasting.common.entities;
import at.petrak.hexcasting.annotations.SoftImplement;
import at.petrak.hexcasting.api.spell.math.HexPattern;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.api.utils.NBTHelper;
@ -27,7 +26,6 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
@ -60,7 +58,7 @@ public class EntityWallScroll extends HangingEntity {
CompoundTag patternTag = NBTHelper.getCompound(scroll, ItemScroll.TAG_PATTERN);
if (patternTag != null) {
this.pattern = HexPattern.DeserializeFromNBT(patternTag);
this.pattern = HexPattern.fromNBT(patternTag);
if (this.level.isClientSide) {
var pair = RenderLib.getCenteredPattern(pattern, 128, 128, 16f);
var dots = pair.getSecond();
@ -160,7 +158,7 @@ public class EntityWallScroll extends HangingEntity {
@Override
public void addAdditionalSaveData(CompoundTag tag) {
tag.putByte("direction", (byte) this.direction.ordinal());
tag.put("scroll", HexUtils.serialize(this.scroll));
tag.put("scroll", HexUtils.serializeToNBT(this.scroll));
tag.putBoolean("showsStrokeOrder", this.getShowsStrokeOrder());
super.addAdditionalSaveData(tag);
}

View file

@ -125,7 +125,7 @@ public class ItemScroll extends Item implements DataHolderItem {
public Optional<TooltipComponent> getTooltipImage(ItemStack stack) {
var compound = NBTHelper.getCompound(stack, ItemScroll.TAG_PATTERN);
if (compound != null) {
var pattern = HexPattern.DeserializeFromNBT(compound);
var pattern = HexPattern.fromNBT(compound);
return Optional.of(new PatternTooltipGreeble(
pattern,
NBTHelper.hasString(stack, ItemScroll.TAG_OP_ID) ? PatternTooltipGreeble.ANCIENT_BG : PatternTooltipGreeble.PRISTINE_BG));

View file

@ -106,7 +106,7 @@ public class ItemSlate extends BlockItem implements DataHolderItem {
if (bet != null && bet.contains(BlockEntitySlate.TAG_PATTERN, Tag.TAG_COMPOUND)) {
var patTag = bet.getCompound(BlockEntitySlate.TAG_PATTERN);
if (!patTag.isEmpty()) {
var pattern = HexPattern.DeserializeFromNBT(patTag);
var pattern = HexPattern.fromNBT(patTag);
return Optional.of(new PatternTooltipGreeble(
pattern,
PatternTooltipGreeble.SLATE_BG));

View file

@ -57,14 +57,14 @@ public abstract class ItemManaHolder extends Item implements ManaHolderItem {
public int getBarColor(ItemStack pStack) {
var mana = getMana(pStack);
var maxMana = getMaxMana(pStack);
return ManaHelper.barColor(mana, maxMana);
return ManaHelper.manaBarColor(mana, maxMana);
}
@Override
public int getBarWidth(ItemStack pStack) {
var mana = getMana(pStack);
var maxMana = getMaxMana(pStack);
return ManaHelper.barWidth(mana, maxMana);
return ManaHelper.manaBarWidth(mana, maxMana);
}
@Override

View file

@ -68,9 +68,9 @@ public abstract class ItemPackagedHex extends ItemManaHolder implements HexHolde
for (var patTag : patsTag) {
CompoundTag tag = NBTHelper.getAsCompound(patTag);
if (tag.size() != 1)
out.add(SpellDatum.make(HexPattern.DeserializeFromNBT(tag)));
out.add(SpellDatum.make(HexPattern.fromNBT(tag)));
else
out.add(SpellDatum.DeserializeFromNBT(tag, level));
out.add(SpellDatum.fromNBT(tag, level));
}
return out;
}

View file

@ -32,7 +32,7 @@ public class PatternScrollFunc extends LootItemConditionalFunction {
var startDir = entry.component2();
var tag = new CompoundTag();
tag.putString(ItemScroll.TAG_OP_ID, opId.toString());
tag.put(ItemScroll.TAG_PATTERN, HexPattern.FromAnglesSig(sig, startDir).serializeToNBT());
tag.put(ItemScroll.TAG_PATTERN, HexPattern.fromAngles(sig, startDir).serializeToNBT());
stack.getOrCreateTag().merge(tag);

View file

@ -37,7 +37,7 @@ public record MsgCastParticleAck(ParticleSpray spray, FrozenColorizer colorizer)
var spread = buf.readDouble();
var count = buf.readInt();
var tag = buf.readAnySizeNbt();
var colorizer = FrozenColorizer.deserialize(tag);
var colorizer = FrozenColorizer.fromNBT(tag);
return new MsgCastParticleAck(
new ParticleSpray(new Vec3(posX, posY, posZ), new Vec3(velX, velY, velZ), fuzziness, spread, count),
colorizer);
@ -54,7 +54,7 @@ public record MsgCastParticleAck(ParticleSpray spray, FrozenColorizer colorizer)
buf.writeDouble(this.spray.getFuzziness());
buf.writeDouble(this.spray.getSpread());
buf.writeInt(this.spray.getCount());
buf.writeNbt(this.colorizer.serialize());
buf.writeNbt(this.colorizer.serializeToNBT());
}

View file

@ -39,12 +39,12 @@ public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern
public static MsgNewSpellPatternSyn deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
var hand = buf.readEnum(InteractionHand.class);
var pattern = HexPattern.DeserializeFromNBT(buf.readAnySizeNbt());
var pattern = HexPattern.fromNBT(buf.readAnySizeNbt());
var resolvedPatternsLen = buf.readInt();
var resolvedPatterns = new ArrayList<ResolvedPattern>(resolvedPatternsLen);
for (int i = 0; i < resolvedPatternsLen; i++) {
resolvedPatterns.add(ResolvedPattern.DeserializeFromNBT(buf.readAnySizeNbt()));
resolvedPatterns.add(ResolvedPattern.fromNBT(buf.readAnySizeNbt()));
}
return new MsgNewSpellPatternSyn(hand, pattern, resolvedPatterns);
}

View file

@ -34,7 +34,7 @@ public record MsgOpenSpellGuiAck(InteractionHand hand, List<ResolvedPattern> pat
var patternsLen = buf.readInt();
var patterns = new ArrayList<ResolvedPattern>(patternsLen);
for (int i = 0; i < patternsLen; i++) {
patterns.add(ResolvedPattern.DeserializeFromNBT(buf.readAnySizeNbt()));
patterns.add(ResolvedPattern.fromNBT(buf.readAnySizeNbt()));
}
var descsLen = buf.readInt();

View file

@ -121,7 +121,7 @@ public record MsgShiftScrollSyn(InteractionHand hand, double scrollDelta, boolea
var datumTag = HexItems.ABACUS.readDatumTag(stack);
if (datumTag != null) {
var popup = SpellDatum.DisplayFromTag(datumTag);
var popup = SpellDatum.displayFromNBT(datumTag);
sender.displayClientMessage(
new TranslatableComponent("hexcasting.tooltip.abacus", popup).withStyle(ChatFormatting.GREEN), true);
}

View file

@ -35,7 +35,7 @@ public class ManualPatternComponent extends AbstractPatternComponent {
RawPattern raw = new Gson().fromJson(json, RawPattern.class);
var dir = HexDir.valueOf(raw.startdir);
var pat = HexPattern.FromAnglesSig(raw.signature, dir);
var pat = HexPattern.fromAngles(raw.signature, dir);
var origin = new HexCoord(raw.q, raw.r);
out.add(new Pair<>(pat, origin));
}

View file

@ -77,7 +77,7 @@ public final class PatternDrawingUtil {
patternEntries.add(new PatternEntry(pattern, origin, new ArrayList<>()));
seenCoords.addAll(pattern.positions(origin));
}
var fakeCom = HexUtils.FindCenter(seenFakePoints);
var fakeCom = HexUtils.findCenter(seenFakePoints);
var maxDx = -1f;
var maxDy = -1f;
@ -100,7 +100,7 @@ public final class PatternDrawingUtil {
seenRealPoints.add(px);
}
}
var realCom = HexUtils.FindCenter(seenRealPoints);
var realCom = HexUtils.findCenter(seenRealPoints);
// and NOW for real!
for (var pat : patternEntries) {

View file

@ -1,13 +1,13 @@
import at.petrak.hexcasting.api.spell.math.EulerPathFinder.findAltDrawing
import at.petrak.hexcasting.api.spell.math.HexDir
import at.petrak.hexcasting.api.spell.math.HexPattern.Companion.FromAnglesSig
import at.petrak.hexcasting.api.spell.math.HexPattern.Companion.fromAngles
import org.junit.jupiter.api.Test
internal class EulerPathFinderTest {
@Test
fun findAltDrawing() {
val sig = "dadaddwwaadada"
val pat = FromAnglesSig(sig, HexDir.NORTH_EAST)
val pat = fromAngles(sig, HexDir.NORTH_EAST)
for (i in 0 until 8) {
val scrungled = findAltDrawing(pat, i.toLong())
println(scrungled)

View file

@ -31,11 +31,11 @@ public class CCFavoredColorizer implements Component, AutoSyncedComponent {
@Override
public void readFromNbt(CompoundTag tag) {
this.colorizer = FrozenColorizer.deserialize(tag.getCompound(TAG_COLORIZER));
this.colorizer = FrozenColorizer.fromNBT(tag.getCompound(TAG_COLORIZER));
}
@Override
public void writeToNbt(CompoundTag tag) {
tag.put(TAG_COLORIZER, this.colorizer.serialize());
tag.put(TAG_COLORIZER, this.colorizer.serializeToNBT());
}
}

View file

@ -42,7 +42,7 @@ public class CCFlight implements Component {
var timeLeft = tag.getInt(TAG_TIME_LEFT);
var dim = ResourceKey.create(Registry.DIMENSION_REGISTRY,
new ResourceLocation(tag.getString(TAG_DIMENSION)));
var origin = HexUtils.DeserializeVec3FromNBT(tag.getLongArray(TAG_ORIGIN));
var origin = HexUtils.vecFromNBT(tag.getLongArray(TAG_ORIGIN));
var radius = tag.getDouble(TAG_RADIUS);
this.flight = new FlightAbility(true, timeLeft, dim, origin, radius);
}

View file

@ -23,7 +23,7 @@ public class CCHarness implements Component {
if (this.lazyLoadedTag.isEmpty()) {
return new CastingHarness(ctx);
} else {
return CastingHarness.DeserializeFromNBT(this.lazyLoadedTag, ctx);
return CastingHarness.fromNBT(this.lazyLoadedTag, ctx);
}
}

View file

@ -39,7 +39,7 @@ public class CCPatterns implements Component {
List<ResolvedPattern> patterns = new ArrayList<>(patternsTag.size());
for (int i = 0; i < patternsTag.size(); i++) {
patterns.add(ResolvedPattern.DeserializeFromNBT(patternsTag.getCompound(i)));
patterns.add(ResolvedPattern.fromNBT(patternsTag.getCompound(i)));
}
this.patterns = patterns;
}

View file

@ -38,7 +38,7 @@ public class CCSentinel implements Component, AutoSyncedComponent {
var hasSentinel = tag.getBoolean(TAG_HAS_SENTINEL);
if (hasSentinel) {
var extendsRange = tag.getBoolean(TAG_EXTENDS_RANGE);
var position = HexUtils.DeserializeVec3FromNBT(tag.getLongArray(TAG_POSITION));
var position = HexUtils.vecFromNBT(tag.getLongArray(TAG_POSITION));
var dim = ResourceKey.create(Registry.DIMENSION_REGISTRY,
new ResourceLocation(tag.getString(TAG_DIMENSION)));
this.sentinel = new Sentinel(true, extendsRange, position, dim);

View file

@ -33,7 +33,7 @@ public class FabricUnsealedIngredient extends Ingredient {
.filter((it) -> it != DatumType.EMPTY && it != DatumType.OTHER)
.map((type) -> {
ItemStack newStack = stack.copy();
NBTHelper.putString(newStack, DataHolderItem.TAG_OVERRIDE_VISUALLY, SpellDatum.GetTagName(type));
NBTHelper.putString(newStack, DataHolderItem.TAG_OVERRIDE_VISUALLY, SpellDatum.tagForType(type));
return new Ingredient.ItemValue(newStack);
}));
this.stack = stack;

View file

@ -22,8 +22,10 @@ public class ForgeHexClientInitializer {
evBus.addListener((RenderLevelLastEvent e) ->
HexAdditionalRenderers.overlayLevel(e.getPoseStack(), e.getPartialTick()));
evBus.addListener((RenderGameOverlayEvent.PreLayer e) ->
HexAdditionalRenderers.overlayGui(e.getMatrixStack(), e.getPartialTicks()));
evBus.addListener((RenderGameOverlayEvent.Post e) -> {
if (e.getType() == RenderGameOverlayEvent.ElementType.ALL)
HexAdditionalRenderers.overlayGui(e.getMatrixStack(), e.getPartialTicks());
});
evBus.addListener((TickEvent.RenderTickEvent e) -> {

View file

@ -25,13 +25,13 @@ public record MsgColorizerUpdateAck(FrozenColorizer update) implements IMessage
var buf = new FriendlyByteBuf(buffer);
var tag = buf.readAnySizeNbt();
var colorizer = FrozenColorizer.deserialize(tag);
var colorizer = FrozenColorizer.fromNBT(tag);
return new MsgColorizerUpdateAck(colorizer);
}
@Override
public void serialize(FriendlyByteBuf buf) {
buf.writeNbt(this.update.serialize());
buf.writeNbt(this.update.serializeToNBT());
}
public static void handle(MsgColorizerUpdateAck self) {

View file

@ -30,7 +30,7 @@ public class ForgeUnsealedIngredient extends AbstractIngredient {
.filter((it) -> it != DatumType.EMPTY && it != DatumType.OTHER)
.map((type) -> {
ItemStack newStack = stack.copy();
NBTHelper.putString(newStack, DataHolderItem.TAG_OVERRIDE_VISUALLY, SpellDatum.GetTagName(type));
NBTHelper.putString(newStack, DataHolderItem.TAG_OVERRIDE_VISUALLY, SpellDatum.tagForType(type));
return new Ingredient.ItemValue(newStack);
}));
this.stack = stack;

View file

@ -124,7 +124,7 @@ public class ForgeXplatImpl implements IXplatAbstractions {
@Override
public void setColorizer(Player player, FrozenColorizer colorizer) {
CompoundTag tag = player.getPersistentData();
tag.put(TAG_COLOR, colorizer.serialize());
tag.put(TAG_COLOR, colorizer.serializeToNBT());
if (player instanceof ServerPlayer serverPlayer) {
CapSyncers.syncColorizer(serverPlayer);
@ -175,7 +175,7 @@ public class ForgeXplatImpl implements IXplatAbstractions {
boolean allowed = tag.getBoolean(TAG_FLIGHT_ALLOWED);
if (allowed) {
var timeLeft = tag.getInt(TAG_FLIGHT_TIME);
var origin = HexUtils.DeserializeVec3FromNBT(tag.getLongArray(TAG_FLIGHT_ORIGIN));
var origin = HexUtils.vecFromNBT(tag.getLongArray(TAG_FLIGHT_ORIGIN));
var radius = tag.getDouble(TAG_FLIGHT_RADIUS);
var dimension = ResourceKey.create(Registry.DIMENSION_REGISTRY,
new ResourceLocation(tag.getString(TAG_FLIGHT_DIMENSION)));
@ -186,7 +186,7 @@ public class ForgeXplatImpl implements IXplatAbstractions {
@Override
public FrozenColorizer getColorizer(Player player) {
return FrozenColorizer.deserialize(player.getPersistentData().getCompound(TAG_COLOR));
return FrozenColorizer.fromNBT(player.getPersistentData().getCompound(TAG_COLOR));
}
@Override
@ -197,7 +197,7 @@ public class ForgeXplatImpl implements IXplatAbstractions {
return Sentinel.none();
}
var extendsRange = tag.getBoolean(TAG_SENTINEL_GREATER);
var position = HexUtils.DeserializeVec3FromNBT(tag.getLongArray(TAG_SENTINEL_POSITION));
var position = HexUtils.vecFromNBT(tag.getLongArray(TAG_SENTINEL_POSITION));
var dimension = ResourceKey.create(Registry.DIMENSION_REGISTRY,
new ResourceLocation(tag.getString(TAG_SENTINEL_DIMENSION)));
@ -207,7 +207,7 @@ public class ForgeXplatImpl implements IXplatAbstractions {
@Override
public CastingHarness getHarness(ServerPlayer player, InteractionHand hand) {
var ctx = new CastingContext(player, hand);
return CastingHarness.DeserializeFromNBT(player.getPersistentData().getCompound(TAG_HARNESS), ctx);
return CastingHarness.fromNBT(player.getPersistentData().getCompound(TAG_HARNESS), ctx);
}
@Override
@ -217,7 +217,7 @@ public class ForgeXplatImpl implements IXplatAbstractions {
List<ResolvedPattern> patterns = new ArrayList<>(patternsTag.size());
for (int i = 0; i < patternsTag.size(); i++) {
patterns.add(ResolvedPattern.DeserializeFromNBT(patternsTag.getCompound(i)));
patterns.add(ResolvedPattern.fromNBT(patternsTag.getCompound(i)));
}
return patterns;
}

View file

@ -1,4 +1,4 @@
# Hex
# Hex Casting
A minecraft mod about casting Hexes, powerful and programmable magical effects, inspired by PSI.
@ -6,3 +6,4 @@ A minecraft mod about casting Hexes, powerful and programmable magical effects,
This mod requires PAUCAL, Patchouli and Kotlin for Forge!
## [Read the documentation online here!](https://gamma-delta.github.io/HexMod/)

562
doc/collate_data.py Executable file
View file

@ -0,0 +1,562 @@
#!/usr/bin/env python3
from sys import argv, stdout
from collections import namedtuple
import json # codec
import re # parsing
import os # listdir
# TO USE: put in Hexcasting root dir, collate_data.py src/main/resources hexcasting thehexbook out.html
# extra info :(
lang = "en_us"
repo_names = {
"hexcasting": "https://raw.githubusercontent.com/gamma-delta/HexMod/main/Common/src/main/resources",
}
extra_i18n = {
"item.minecraft.amethyst_shard": "Amethyst Shard",
"item.minecraft.budding_amethyst": "Budding Amethyst",
"block.hexcasting.slate": "Blank Slate",
}
default_macros = {
"$(obf)": "$(k)",
"$(bold)": "$(l)",
"$(strike)": "$(m)",
"$(italic)": "$(o)",
"$(italics)": "$(o)",
"$(list": "$(li",
"$(reset)": "$()",
"$(clear)": "$()",
"$(2br)": "$(br2)",
"$(p)": "$(br2)",
"/$": "$()",
"<br>": "$(br)",
"$(nocolor)": "$(0)",
"$(item)": "$(#b0b)",
"$(thing)": "$(#490)",
}
colors = {
"0": None,
"1": "00a",
"2": "0a0",
"3": "0aa",
"4": "a00",
"5": "a0a",
"6": "fa0",
"7": "aaa",
"8": "555",
"9": "55f",
"a": "5f5",
"b": "5ff",
"c": "f55",
"d": "f5f",
"e": "ff5",
"f": "fff",
}
types = {
"k": "obf",
"l": "bold",
"m": "strikethrough",
"n": "underline",
"o": "italic",
}
keys = {
"use": "Right Click",
"sneak": "Left Shift",
}
bind1 = (lambda: None).__get__(0).__class__
def slurp(filename):
with open(filename, "r") as fh:
return json.load(fh)
FormatTree = namedtuple("FormatTree", ["style", "children"])
Style = namedtuple("Style", ["type", "value"])
def parse_style(sty):
if sty == "br":
return "<br />", None
if sty == "br2":
return "", Style("para", {})
if sty == "li":
return "", Style("para", {"clazz": "fake-li"})
if sty[:2] == "k:":
return keys[sty[2:]], None
if sty[:2] == "l:":
return "", Style("link", sty[2:])
if sty == "/l":
return "", Style("link", None)
if sty == "playername":
return "[Playername]", None
if sty[:2] == "t:":
return "", Style("tooltip", sty[2:])
if sty == "/t":
return "", Style("tooltip", None)
if sty[:2] == "c:":
return "", Style("cmd_click", sty[2:])
if sty == "/c":
return "", Style("cmd_click", None)
if sty == "r" or not sty:
return "", Style("base", None)
if sty in types:
return "", Style(types[sty], True)
if sty in colors:
return "", Style("color", colors[sty])
if sty.startswith("#") and len(sty) in [4, 7]:
return "", Style("color", sty[1:])
# TODO more style parse
raise ValueError("Unknown style: " + sty)
def localize(i18n, string):
return i18n.get(string, string) if i18n else string
format_re = re.compile(r"\$\(([^)]*)\)")
def format_string(root_data, string):
# resolve lang
string = localize(root_data["i18n"], string)
# resolve macros
old_string = None
while old_string != string:
old_string = string
for macro, replace in root_data["macros"].items():
string = string.replace(macro, replace)
else: break
# lex out parsed styles
text_nodes = []
styles = []
last_end = 0
extra_text = ""
for mobj in re.finditer(format_re, string):
bonus_text, sty = parse_style(mobj.group(1))
text = string[last_end:mobj.start()] + bonus_text
if sty:
styles.append(sty)
text_nodes.append(extra_text + text)
extra_text = ""
else:
extra_text += text
last_end = mobj.end()
text_nodes.append(extra_text + string[last_end:])
first_node, *text_nodes = text_nodes
# parse
style_stack = [FormatTree(Style("base", True), []), FormatTree(Style("para", {}), [first_node])]
for style, text in zip(styles, text_nodes):
tmp_stylestack = []
if style.type == "base":
while style_stack[-1].style.type != "para":
last_node = style_stack.pop()
style_stack[-1].children.append(last_node)
elif any(tree.style.type == style.type for tree in style_stack):
while len(style_stack) >= 2:
last_node = style_stack.pop()
style_stack[-1].children.append(last_node)
if last_node.style.type == style.type:
break
tmp_stylestack.append(last_node.style)
for sty in tmp_stylestack:
style_stack.append(FormatTree(sty, []))
if style.value is None:
if text: style_stack[-1].children.append(text)
else:
style_stack.append(FormatTree(style, [text] if text else []))
while len(style_stack) >= 2:
last_node = style_stack.pop()
style_stack[-1].children.append(last_node)
return style_stack[0]
test_root = {"i18n": {}, "macros": default_macros, "resource_dir": "Common/src/main/resources", "modid": "hexcasting"}
test_str = "Write the given iota to my $(l:patterns/readwrite#hexcasting:write/local)$(#490)local$().$(br)The $(l:patterns/readwrite#hexcasting:write/local)$(#490)local$() is a lot like a $(l:items/focus)$(#b0b)Focus$(). It's cleared when I stop casting a Hex, starts with $(l:casting/influences)$(#490)Null$() in it, and is preserved between casts of $(l:patterns/meta#hexcasting:for_each)$(#fc77be)Thoth's Gambit$(). "
def do_localize(root_data, obj, *names):
for name in names:
if name in obj:
obj[name] = localize(root_data["i18n"], obj[name])
def do_format(root_data, obj, *names):
for name in names:
if name in obj:
obj[name] = format_string(root_data, obj[name])
def identity(x): return x
pattern_pat = re.compile(r'HexPattern\.fromAngles\("([qweasd]+)", HexDir\.(\w+)\),\s*modLoc\("([^"]+)"\)([^;]*true\);)?')
def fetch_patterns(root_data):
filename = f"{root_data['resource_dir']}/../java/at/petrak/hexcasting/common/casting/RegisterPatterns.java"
registry = {}
with open(filename, "r") as fh:
pattern_data = fh.read()
for mobj in re.finditer(pattern_pat, pattern_data):
string, start_angle, name, is_per_world = mobj.groups()
registry[root_data["modid"] + ":" + name] = (string, start_angle, bool(is_per_world))
return registry
def resolve_pattern(root_data, page):
if "pattern_reg" not in root_data:
root_data["pattern_reg"] = fetch_patterns(root_data)
page["op"] = [root_data["pattern_reg"][page["op_id"]]]
page["name"] = localize(root_data["i18n"], "hexcasting.spell." + page["op_id"])
def fixup_pattern(do_sig, root_data, page):
patterns = page["patterns"]
if not isinstance(patterns, list): patterns = [patterns]
if do_sig:
inp = page.get("input", None) or ""
oup = page.get("output", None) or ""
pipe = f"{inp} \u2192 {oup}".strip()
suffix = f" ({pipe})" if inp or oup else ""
page["header"] += suffix
page["op"] = [(p["signature"], p["startdir"], False) for p in patterns]
def fetch_recipe(root_data, recipe):
modid, recipeid = recipe.split(":")
gen_resource_dir = root_data["resource_dir"].replace("/main/", "/generated/").replace("Common/", "Forge/") # TODO hack
recipe_path = f"{gen_resource_dir}/data/{modid}/recipes/{recipeid}.json"
return slurp(recipe_path)
def fetch_recipe_result(root_data, recipe):
return fetch_recipe(root_data, recipe)["result"]["item"]
def fetch_bswp_recipe_result(root_data, recipe):
return fetch_recipe(root_data, recipe)["result"]["name"]
def localize_item(root_data, item):
# TODO hack
item = re.sub("{.*", "", item.replace(":", "."))
block = "block." + item
block_l = localize(root_data["i18n"], block)
if block_l != block: return block_l
return localize(root_data["i18n"], "item." + item)
page_types = {
"hexcasting:pattern": resolve_pattern,
"hexcasting:manual_pattern": bind1(fixup_pattern, True),
"hexcasting:manual_pattern_nosig": bind1(fixup_pattern, False),
"hexcasting:brainsweep": lambda rd, page: page.__setitem__("output_name", localize_item(rd, fetch_bswp_recipe_result(rd, page["recipe"]))),
"patchouli:link": lambda rd, page: do_localize(rd, page, "link_text"),
"patchouli:crafting": lambda rd, page: page.__setitem__("item_name", [localize_item(rd, fetch_recipe_result(rd, page[ty])) for ty in ("recipe", "recipe2") if ty in page]),
"hexcasting:crafting_multi": lambda rd, page: page.__setitem__("item_name", [localize_item(rd, fetch_recipe_result(rd, recipe)) for recipe in page["recipes"]]),
"patchouli:spotlight": lambda rd, page: page.__setitem__("item_name", localize_item(rd, page["item"]))
}
def walk_dir(root_dir, prefix):
search_dir = root_dir + '/' + prefix
for fh in os.scandir(search_dir):
if fh.is_dir():
yield from walk_dir(root_dir, prefix + fh.name + '/')
elif fh.name.endswith(".json"):
yield prefix + fh.name
def parse_entry(root_data, entry_path, ent_name):
data = slurp(f"{entry_path}")
do_localize(root_data, data, "name")
for page in data["pages"]:
do_localize(root_data, page, "title", "header")
do_format(root_data, page, "text")
if page["type"] in page_types:
page_types[page["type"]](root_data, page)
data["id"] = ent_name
return data
def parse_category(root_data, base_dir, cat_name):
data = slurp(f"{base_dir}/categories/{cat_name}.json")
do_localize(root_data, data, "name")
do_format(root_data, data, "description")
entry_dir = f"{base_dir}/entries/{cat_name}"
entries = []
for filename in os.listdir(entry_dir):
if filename.endswith(".json"):
basename = filename[:-5]
entries.append(parse_entry(root_data, f"{entry_dir}/{filename}", cat_name + "/" + basename))
entries.sort(key=lambda ent: (not ent.get("priority", False), ent.get("sortnum", 0), ent["name"]))
data["entries"] = entries
data["id"] = cat_name
return data
def parse_sortnum(cats, name):
if '/' in name:
ix = name.rindex('/')
return parse_sortnum(cats, name[:ix]) + (cats[name].get("sortnum", 0),)
return cats[name].get("sortnum", 0),
def parse_book(root, mod_name, book_name):
base_dir = f"{root}/data/{mod_name}/patchouli_books/{book_name}"
root_info = slurp(f"{base_dir}/book.json")
root_info["resource_dir"] = root
root_info["modid"] = mod_name
root_info.setdefault("macros", {}).update(default_macros)
if root_info.setdefault("i18n", {}):
root_info["i18n"] = slurp(f"{root}/assets/{mod_name}/lang/{lang}.json")
root_info["i18n"].update(extra_i18n)
book_dir = f"{base_dir}/{lang}"
categories = []
for filename in walk_dir(f"{book_dir}/categories", ""):
basename = filename[:-5]
categories.append(parse_category(root_info, book_dir, basename))
cats = {cat["id"]: cat for cat in categories}
categories.sort(key=lambda cat: (parse_sortnum(cats, cat["id"]), cat["name"]))
do_localize(root_info, root_info, "name")
do_format(root_info, root_info, "landing_text")
root_info["categories"] = categories
root_info["blacklist"] = set()
root_info["spoilers"] = set()
return root_info
def tag_args(kwargs):
return "".join(f" {'class' if key == 'clazz' else key.replace('_', '-')}={repr(value)}" for key, value in kwargs.items())
class PairTag:
__slots__ = ["stream", "name", "kwargs"]
def __init__(self, stream, name, **kwargs):
self.stream = stream
self.name = name
self.kwargs = tag_args(kwargs)
def __enter__(self):
print(f"<{self.name}{self.kwargs}>", file=self.stream, end="")
def __exit__(self, _1, _2, _3):
print(f"</{self.name}>", file=self.stream, end="")
class Empty:
def __enter__(self): pass
def __exit__(self, _1, _2, _3): pass
class Stream:
__slots__ = ["stream", "thunks"]
def __init__(self, stream):
self.stream = stream
self.thunks = []
def tag(self, name, **kwargs):
keywords = tag_args(kwargs)
print(f"<{name}{keywords} />", file=self.stream, end="")
return self
def pair_tag(self, name, **kwargs):
return PairTag(self.stream, name, **kwargs)
def pair_tag_if(self, cond, name, **kwargs):
return self.pair_tag(name, **kwargs) if cond else Empty()
def empty_pair_tag(self, name, **kwargs):
with self.pair_tag(name, **kwargs): pass
def text(self, txt):
print(txt, file=self.stream, end="")
return self
def get_format(out, ty, value):
if ty == "para":
return out.pair_tag("p", **value)
if ty == "color":
return out.pair_tag("span", style=f"color: #{value}")
if ty == "link":
link = value
if "://" not in link:
link = "#" + link.replace("#", "@")
return out.pair_tag("a", href=link)
if ty == "tooltip":
return out.pair_tag("span", clazz="has-tooltip", title=value)
if ty == "cmd_click":
return out.pair_tag("span", clazz="has-cmd_click", title="When clicked, would execute: "+value)
if ty == "obf":
return out.pair_tag("span", clazz="obfuscated")
if ty == "bold":
return out.pair_tag("strong")
if ty == "italic":
return out.pair_tag("i")
if ty == "strikethrough":
return out.pair_tag("s")
if ty == "underline":
return out.pair_tag("span", style="text-decoration: underline")
raise ValueError("Unknown format type: " + ty)
def entry_spoilered(root_info, entry):
return entry.get("advancement", None) in root_info["spoilers"]
def category_spoilered(root_info, category):
return all(entry_spoilered(root_info, ent) for ent in category["entries"])
def write_block(out, block):
if isinstance(block, str):
out.text(block)
return
sty_type = block.style.type
if sty_type == "base":
for child in block.children: write_block(out, child)
return
tag = get_format(out, sty_type, block.style.value)
with tag:
for child in block.children:
write_block(out, child)
# TODO modularize
def write_page(out, pageid, page):
if "anchor" in page:
anchor_id = pageid + "@" + page["anchor"]
else: anchor_id = None
with out.pair_tag_if(anchor_id, "div", id=anchor_id):
if "header" in page or "title" in page:
with out.pair_tag("h4"):
out.text(page.get("header", page.get("title", None)))
if anchor_id:
with out.pair_tag("a", href="#" + anchor_id, clazz="permalink small"):
out.empty_pair_tag("i", clazz="bi bi-link-45deg")
ty = page["type"]
if ty == "patchouli:text":
write_block(out, page["text"])
elif ty == "patchouli:empty": pass
elif ty == "patchouli:link":
write_block(out, page["text"])
with out.pair_tag("h4", clazz="linkout"):
with out.pair_tag("a", href=page["url"]):
out.text(page["link_text"])
elif ty == "patchouli:spotlight":
with out.pair_tag("h4", clazz="spotlight-title page-header"):
out.text(page["item_name"])
if "text" in page: write_block(out, page["text"])
elif ty == "patchouli:crafting":
with out.pair_tag("blockquote", clazz="crafting-info"):
out.text(f"Depicted in the book: The crafting recipe for the ")
first = True
for name in page["item_name"]:
if not first: out.text(" and ")
first = False
with out.pair_tag("code"): out.text(name)
out.text(".")
if "text" in page: write_block(out, page["text"])
elif ty == "patchouli:image":
with out.pair_tag("p", clazz="img-wrapper"):
for img in page["images"]:
modid, coords = img.split(":")
out.empty_pair_tag("img", src=f"{repo_names[modid]}/assets/{modid}/{coords}")
if "text" in page: write_block(out, page["text"])
elif ty == "hexcasting:crafting_multi":
recipes = page["item_name"]
with out.pair_tag("blockquote", clazz="crafting-info"):
out.text(f"Depicted in the book: Several crafting recipes, for the ")
with out.pair_tag("code"): out.text(recipes[0])
for i in recipes[1:]:
out.text(", ")
with out.pair_tag("code"): out.text(i)
out.text(".")
if "text" in page: write_block(out, page["text"])
elif ty == "hexcasting:brainsweep":
with out.pair_tag("blockquote", clazz="crafting-info"):
out.text(f"Depicted in the book: A mind-flaying recipe producing the ")
with out.pair_tag("code"): out.text(page["output_name"])
out.text(".")
if "text" in page: write_block(out, page["text"])
elif ty in ("hexcasting:pattern", "hexcasting:manual_pattern_nosig", "hexcasting:manual_pattern"):
if "name" in page:
with out.pair_tag("h4", clazz="pattern-title"):
inp = page.get("input", None) or ""
oup = page.get("output", None) or ""
pipe = f"{inp} \u2192 {oup}".strip()
suffix = f" ({pipe})" if inp or oup else ""
out.text(f"{page['name']}{suffix}")
if anchor_id:
with out.pair_tag("a", href="#" + anchor_id, clazz="permalink small"):
out.empty_pair_tag("i", clazz="bi bi-link-45deg")
with out.pair_tag("details", clazz="spell-collapsible"):
out.empty_pair_tag("summary", clazz="collapse-spell")
for string, start_angle, per_world in page["op"]:
with out.pair_tag("canvas", clazz="spell-viz", width=216, height=216, data_string=string, data_start=start_angle.lower(), data_per_world=per_world):
out.text("Your browser does not support visualizing patterns. Pattern code: " + string)
write_block(out, page["text"])
else:
with out.pair_tag("p", clazz="todo-note"):
out.text("TODO: Missing processor for type: " + ty)
if "text" in page:
write_block(out, page["text"])
out.tag("br")
def write_entry(out, book, entry):
with out.pair_tag("div", id=entry["id"]):
with out.pair_tag_if(entry_spoilered(book, entry), "div", clazz="spoilered"):
with out.pair_tag("h3", clazz="entry-title page-header"):
write_block(out, entry["name"])
with out.pair_tag("a", href="#" + entry["id"], clazz="permalink small"):
out.empty_pair_tag("i", clazz="bi bi-link-45deg")
for page in entry["pages"]:
write_page(out, entry["id"], page)
def write_category(out, book, category):
with out.pair_tag("section", id=category["id"]):
with out.pair_tag_if(category_spoilered(book, category), "div", clazz="spoilered"):
with out.pair_tag("h2", clazz="category-title page-header"):
write_block(out, category["name"])
with out.pair_tag("a", href="#" + category["id"], clazz="permalink small"):
out.empty_pair_tag("i", clazz="bi bi-link-45deg")
write_block(out, category["description"])
for entry in category["entries"]:
if entry["id"] not in book["blacklist"]:
write_entry(out, book, entry)
def write_toc(out, book):
with out.pair_tag("h2", id="table-of-contents", clazz="page-header"):
out.text("Table of Contents")
with out.pair_tag("a", href="javascript:void(0)", clazz="toggle-link small", data_target="toc-category"):
out.text("(toggle all)")
with out.pair_tag("a", href="#table-of-contents", clazz="permalink small"):
out.empty_pair_tag("i", clazz="bi bi-link-45deg")
for category in book["categories"]:
with out.pair_tag("details", clazz="toc-category"):
with out.pair_tag("summary"):
with out.pair_tag("a", href="#" + category["id"], clazz="spoilered" if category_spoilered(book, category) else ""):
out.text(category["name"])
with out.pair_tag("ul"):
for entry in category["entries"]:
with out.pair_tag("li"):
with out.pair_tag("a", href="#" + entry["id"], clazz="spoilered" if entry_spoilered(book, entry) else ""):
out.text(entry["name"])
def write_book(out, book):
with out.pair_tag("div", clazz="container"):
with out.pair_tag("header", clazz="jumbotron"):
with out.pair_tag("h1", clazz="book-title"):
write_block(out, book["name"])
write_block(out, book["landing_text"])
with out.pair_tag("nav"):
write_toc(out, book)
with out.pair_tag("main", clazz="book-body"):
for category in book["categories"]:
write_category(out, book, category)
def main(argv):
if len(argv) < 5:
print(f"Usage: {argv[0]} <resources dir> <mod name> <book name> <template file> [<output>]")
return
root = argv[1]
mod_name = argv[2]
book_name = argv[3]
book = parse_book(root, mod_name, book_name)
template_file = argv[4]
with open(template_file, "r") as fh:
with stdout if len(argv) < 6 else open(argv[5], "w") as out:
for line in fh:
if line.startswith("#DO_NOT_RENDER"):
_, *blacklist = line.split()
book["blacklist"].update(blacklist)
if line.startswith("#SPOILER"):
_, *spoilers = line.split()
book["spoilers"].update(spoilers)
elif line == "#DUMP_BODY_HERE\n":
write_book(Stream(out), book)
print('', file=out)
else: print(line, end='', file=out)
if __name__ == "__main__":
main(argv)

363
doc/template.html Normal file
View file

@ -0,0 +1,363 @@
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="The Hex Book, all in one place.">
<meta name="author" content="petrak@, Alwinfy">
<link rel="icon" href="../../favicon.ico">
<title>Hex Book</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css">
<style>
summary { display: list-item; }
details.spell-collapsible {
display: inline-block;
border: 1px solid #aaa;
border-radius: 4px;
padding: .5em .5em 0;
margin-bottom: .5em;
}
summary.collapse-spell {
font-weight: bold;
margin: -.5em -.5em 0;
padding: .5em;
}
details.spell-collapsible[open] {
padding: .5em;
}
details[open] summary.collapse-spell {
border-bottom: 1px solid #aaa;
margin-bottom: .5em;
}
details .collapse-spell::before {
content: "Click to show spell";
}
details[open] .collapse-spell::before {
content: "Click to hide spell";
}
blockquote.crafting-info {
font-size: inherit;
}
a.toggle-link {
margin-left: 0.5em;
}
a.permalink {
margin-left: 0.5em;
}
a.permalink:hover {
color: lightgray;
}
p {
margin: 0.5ex 0;
}
p.fake-li {
margin: 0;
}
p.fake-li::before {
content: "\2022";
margin: 1ex;
}
.linkout::before {
content: "Link: ";
}
p.todo-note {
font-style: italic;
color: lightgray;
}
.obfuscated {
filter: blur(1em);
}
.spoilered {
filter: blur(1ex);
-moz-transition: filter 0.04s linear;
}
.spoilered:hover {
filter: blur(0.5ex);
}
.spoilered.unspoilered {
filter: blur(0);
}
canvas.spell-viz {
--dot-color: #777f;
--start-dot-color: #f009;
--moving-dot-color: #0fa9;
--path-color: darkgray;
--visited-path-color: #0c8;
--dot-scale: 0.0625;
--moving-dot-scale: 0.125;
--line-scale: 0.08333;
--pausetext-scale: 0.5;
}
</style>
<noscript>
<style>
/* for accessibility */
.spoilered {
filter: none !important;
}
</style>
</noscript>
<script>
"use strict";
const speeds = [0, 0.25, 0.5, 1, 2, 4];
const scrollThreshold = 100;
const rfaQueue = [];
function startAngle(str) {
switch (str) {
case "east": return 0;
case "north_east": return 1;
case "north_west": return 2;
case "west": return 3;
case "south_west": return 4;
case "south_east": return 5;
default: return 0;
}
}
function offsetAngle(str) {
switch (str) {
case "w": return 0;
case "q": return 1;
case "a": return 2;
case "s": return 3;
case "d": return 4;
case "e": return 5;
default: return -1;
}
}
function initializeElem(canvas) {
const str = canvas.dataset.string;
let angle = startAngle(canvas.dataset.start);
const perWorld = canvas.dataset.perWorld === "True";
// build geometry
const points = [[0, 0]];
let lastPoint = points[0];
let minPoint = lastPoint, maxPoint = lastPoint;
for (const ch of "w" + str) {
const addAngle = offsetAngle(ch);
if (addAngle < 0) continue;
angle = (angle + addAngle) % 6;
const trueAngle = Math.PI / 3 * angle;
const [lx, ly] = lastPoint;
const newPoint = [lx + Math.cos(trueAngle), ly - Math.sin(trueAngle)];
points.push(newPoint);
lastPoint = newPoint;
const [mix, miy] = minPoint;
minPoint = [Math.min(mix, newPoint[0]), Math.min(miy, newPoint[1])];
const [max, may] = maxPoint;
maxPoint = [Math.max(max, newPoint[0]), Math.max(may, newPoint[1])];
}
const size = Math.min(canvas.width, canvas.height) * 0.8;
const scale = size / Math.max(3, Math.max(maxPoint[1] - minPoint[1], maxPoint[0] - minPoint[0]));
const center = [(minPoint[0] + maxPoint[0]) * 0.5, (minPoint[1] + maxPoint[1]) * 0.5];
const truePoints = points.map(p => [canvas.width * 0.5 + scale * (p[0] - center[0]), canvas.height * 0.5 + scale * (p[1] - center[1])]);
let uniqPoints = [];
l1: for (const point of truePoints) {
for (const pt of uniqPoints) {
if (Math.abs(point[0] - pt[0]) < 0.00001 && Math.abs(point[1] - pt[1]) < 0.00001) {
continue l1;
}
}
uniqPoints.push(point);
}
// rendering code
const speed = 0.0025;
const context = canvas.getContext("2d");
const negaProgress = -3;
let progress = 0;
let scrollTimeout = 1e309;
let speedLevel = 3;
let speedIncrement = 0;
function speedScale() {
return speeds[speedLevel];
}
const style = getComputedStyle(canvas);
const getProp = n => style.getPropertyValue(n);
const tick = dt => {
scrollTimeout += dt;
if (canvas.offsetParent === null) return;
const strokeStyle = getProp("--path-color");
const strokeVisitedStyle = getProp("--visited-path-color");
const startDotStyle = getProp("--start-dot-color");
const dotStyle = getProp("--dot-color");
const movDotStyle = getProp("--moving-dot-color");
const strokeWidth = scale * +getProp("--line-scale");
const dotRadius = scale * +getProp("--dot-scale");
const movDotRadius = scale * +getProp("--moving-dot-scale");
const pauseScale = scale * +getProp("--pausetext-scale");
if (!perWorld) {
progress += speed * dt * (progress > 0 ? speedScale() : Math.sqrt(speedScale()));
}
if (progress >= truePoints.length - 1) {
progress = negaProgress;
}
let ix = Math.floor(progress), frac = progress - ix, core = null, fadeColor = 0;
if (ix < 0) {
const rawFade = 2 * progress / negaProgress - 1;
fadeColor = 1 - Math.abs(rawFade);
context.strokeStyle = rawFade > 0 ? strokeVisitedStyle : strokeStyle;
ix = rawFade > 0 ? truePoints.length - 2 : 0;
frac = +(rawFade > 0);
} else {
context.strokeStyle = strokeVisitedStyle;
}
const [lx, ly] = truePoints[ix];
const [rx, ry] = truePoints[ix + 1];
core = [lx + (rx - lx) * frac, ly + (ry - ly) * frac];
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.lineWidth = strokeWidth;
context.moveTo(truePoints[0][0], truePoints[0][1]);
for (let i = 1; i < ix + 1; i++) {
context.lineTo(truePoints[i][0], truePoints[i][1]);
}
context.lineTo(core[0], core[1]);
context.stroke();
context.beginPath();
context.strokeStyle = strokeStyle;
context.moveTo(core[0], core[1]);
for (let i = ix + 1; i < truePoints.length; i++) {
context.lineTo(truePoints[i][0], truePoints[i][1]);
}
context.stroke();
for (let i = 0; i < uniqPoints.length; i++) {
context.beginPath();
context.fillStyle = (i == 0 && !perWorld) ? startDotStyle : dotStyle;
const radius = (i == 0 && !perWorld) ? movDotRadius : dotRadius;
context.arc(uniqPoints[i][0], uniqPoints[i][1], radius, 0, 2 * Math.PI);
context.fill();
}
if (!perWorld) {
context.beginPath();
context.fillStyle = movDotStyle;
context.arc(core[0], core[1], movDotRadius, 0, 2 * Math.PI);
context.fill();
}
if (fadeColor) {
context.fillStyle = `rgba(255, 255, 255, ${fadeColor})`;
context.fillRect(0, 0, canvas.width, canvas.height);
}
if (scrollTimeout <= 2000) {
context.fillStyle = `rgba(200, 200, 200, ${(2000 - scrollTimeout) / 1000})`;
context.font = `${pauseScale}px sans-serif`;
context.fillText(speedScale() ? speedScale() + "x" : "Paused", 0.2 * scale, canvas.height - 0.2 * scale);
}
};
rfaQueue.push(tick);
// scrolling input
if (!perWorld) {
canvas.addEventListener("wheel", ev => {
speedIncrement += ev.deltaY;
const oldSpeedLevel = speedLevel;
if (speedIncrement >= scrollThreshold) {
speedLevel--;
} else if (speedIncrement <= -scrollThreshold) {
speedLevel++;
}
if (oldSpeedLevel != speedLevel) {
speedIncrement = 0;
speedLevel = Math.max(0, Math.min(speeds.length - 1, speedLevel));
scrollTimeout = 0;
}
ev.preventDefault();
});
}
}
function hookLoad(elem) {
let init = false;
const canvases = elem.querySelectorAll("canvas");
elem.addEventListener("toggle", () => {
if (!init) {
canvases.forEach(initializeElem);
init = true;
}
});
}
function hookToggle(elem) {
const details = Array.from(document.querySelectorAll("details." + elem.dataset.target));
elem.addEventListener("click", () => {
if (details.some(x => x.open)) {
details.forEach(x => x.open = false);
} else {
details.forEach(x => x.open = true);
}
});
}
const params = new URLSearchParams(document.location.search);
function hookSpoiler(elem) {
if (params.get("nospoiler") !== null) {
elem.classList.add("unspoilered");
} else {
const thunk = ev => {
if (!elem.classList.contains("unspoilered")) {
ev.preventDefault();
ev.stopImmediatePropagation();
elem.classList.add("unspoilered");
}
elem.removeEventListener("click", thunk);
};
elem.addEventListener("click", thunk);
if (elem instanceof HTMLAnchorElement) {
const href = elem.getAttribute("href");
if (href.startsWith("#")) {
elem.addEventListener("click", () => document.getElementById(href.substring(1)).querySelector(".spoilered").classList.add("unspoilered"));
}
}
}
}
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('details.spell-collapsible').forEach(hookLoad);
document.querySelectorAll('a.toggle-link').forEach(hookToggle);
document.querySelectorAll('.spoilered').forEach(hookSpoiler);
function tick(prevTime, time) {
const dt = time - prevTime;
for (const q of rfaQueue) {
q(dt);
}
requestAnimationFrame(t => tick(time, t));
}
requestAnimationFrame(t => tick(t, t));
});
</script>
</head>
<body>
<div class="container" style="margin-top: 3em;">
<blockquote>
<h1>This is the online version of the Hexcasting documentation.</h1>
<p>Embedded images and patterns are included, but not crafting recipes or items. There's an in-game book for those.</p>
<p>Additionally, this is built from the latest code on GitHub. It may describe <b>newer</b> features that you may not necessarily have, even on the latest CurseForge version!</p>
<p><b>Entries which are blurred are spoilers</b>. Click to reveal them, but be aware that they may spoil endgame progression. Alternatively, click <a href="?nospoiler">here</a> to get a version with all spoilers showing.</p>
</blockquote>
</div>
#SPOILER hexcasting:opened_eyes hexcasting:y_u_no_cast_angy hexcasting:enlightenment
#DUMP_BODY_HERE
</body>
</html>