···22hi! this is a repository for my hexcasting addons (both wip and released). i've spent way too much time () on these, with little to show for it.
3344## where to find stuff
55-* code: [`hex-addons @ seed.lama2923.dev`](https://app.radicle.xyz/nodes/seed.lama2923.dev/rad%3AzChmxddnxPemALy4RuZ7zQgzDxYv)
66-* issues: see above
77- * reporting issues: check if your issue isn't already reported, then https://discord.com/invite/4xxHGYteWk and ping
88-* pull requests: `hh clone pool.net.eu.org/hex-addons`, `hh sync`
55+* repository: [`hex-addons @ seed.lama2923.dev`](https://app.radicle.xyz/nodes/seed.lama2923.dev/rad%3AzChmxddnxPemALy4RuZ7zQgzDxYv)
66+ (use `rad clone rad:zChmxddnxPemALy4RuZ7zQgzDxYv` to clone)
77+* issues: see above, Issues tab
88+ * submitting an issue: clone the repository above, then `rad issue open`
99+ **PLEASE READ BEFORE CREATING AN ISSUE:**
1010+ * please *describe* your issue
1111+ * if possible, provide precise reproduction steps
1212+ * attach logs and crash reports (both!)
1313+ * ideally, provide a video
1414+1515+ if this is ignored I reserve the right to send your issue directly to the bit bucket
1616+* submitting patches:
1717+ 1. clone the repo
1818+ 2. `git switch -d`
1919+ 3. make your code
2020+ 4. `git push HEAD:refs/patches`
9211022## mods here
2323+* [**hexic**](https://modrinth.com/mod/hexic) adds a bunch of small fun things to play with
1124* [hex in yo chests](https://modrinth.com/mod/hexxychests) **(WIP)** adds spells for manipulating storage (and anything you could conceivably think of as storage)
1225* [**iotaworks**](https://modrinth.com/mod/iotaworks) **(WIP)** adds methods of manipulating the behavior of patterns; currently patchwork iotas (people really like these, it seems) and subscripts
1326* [tongued hexxy](https://modrinth.com/project/hexxytounge) **(WIP)** will add methods of manipulating chat behavior (once I actually implement it)
1427* [**phlib**](https://modrinth.com/mod/phlib) adds utilities for hex addons that I find useful; y'all are welcome to use it if you need since I don't plan to break anything
1515-* ~~[**hexic**](https://modrinth.com/mod/hexic)~~ <span style="color: red">(deprecated)</span> [](https://hexic.pool.net.eu.org/) was a set of miscellaneous features; it's undergoing a large split into the above right now
+103-93
build.gradle.kts
···11import de.undercouch.gradle.tasks.download.Download
22import groovy.json.JsonSlurper
33-import org.eu.net.pool.mc_plugin.Environment
43import org.gradle.api.publish.maven.internal.publication.MavenPomInternal
54import org.gradle.kotlin.dsl.support.uppercaseFirstChar
65import kotlin.io.path.exists
···2726 id("maven-publish")
2827 id("idea")
2928 id("de.undercouch.download") version "5.6.0"
3030- id("org.eu.net.pool.mc-plugin")
2929+ //id("org.eu.net.pool.mc-plugin")
3130}
32313332allprojects {
···54535554val release: Boolean = !System.getenv("release").isNullOrEmpty()
5655allprojects {
5757- val p = P(project)
5656+ val p by lazy {
5757+ val p = P(project)
5858+ ext["p"] = p
5959+ p
6060+ }
5861 val modid: String by project.properties
5959- ext["p"] = p
6062 ext["release"] = release
6161- version = project.property("mod_version") as String
6262- if (!release) version = "${version}+${p.commit_id.take(7)}"
6363- group = rootProject.property("maven_group") as String
6464- println("configuring $modid ($project) v$version @ $group")
6363+ val isProject = project.projectDir.path.contains("/project/") || project == project(":util")
6464+6565 plugins.withId("java") {
6666- base {
6767- archivesBaseName = modid
6868- }
6966 java {
7067 toolchain.languageVersion = JavaLanguageVersion.of(17)
7168 withSourcesJar()
7269 }
7373- // be extra sure
7474- version = project.property("mod_version") as String
7070+ }
7171+7272+ if (isProject) {
7573 if (!release) version = "${version}+${p.commit_id.take(7)}"
7674 group = rootProject.property("maven_group") as String
7777-7878- tasks.named<Jar>("jar").configure {
7979- from("LICENSE") {
8080- rename { "LICENSE_$modid" }
7575+ println("configuring $modid ($project) v$version @ $group")
7676+ plugins.withId("java") {
7777+ base {
7878+ archivesBaseName = modid
8179 }
8282- duplicatesStrategy = DuplicatesStrategy.WARN
8383- }
8484- }
8585-8686- plugins.withId("scala") {
8787- scala {
8888- scalaVersion = "3.7.1"
8080+ tasks.named<Jar>("jar").configure {
8181+ from("LICENSE") {
8282+ rename { "LICENSE_$modid" }
8383+ }
8484+ duplicatesStrategy = DuplicatesStrategy.WARN
8585+ }
8986 }
9090- }
91879292- plugins.withId("fabric-loom") {
9393- extensions.getByType<LoomGradleExtensionAPI>().apply {
9494- splitEnvironmentSourceSets()
9595- runs["client"].programArgs += listOf("--username", "Player", "--uuid", "9e1b34e3-8031-4623-8918-eb7914ab564b")
8888+ plugins.withId("fabric-loom") {
8989+ extensions.getByType<LoomGradleExtensionAPI>().apply {
9090+ splitEnvironmentSourceSets()
9191+ runs["client"].programArgs += listOf(
9292+ "--username",
9393+ "Player",
9494+ "--uuid",
9595+ "9e1b34e3-8031-4623-8918-eb7914ab564b"
9696+ )
96979797- mods {
9898- register(modid) {
9999- sourceSet("main")
100100- sourceSet("client")
9898+ mods {
9999+ register(modid) {
100100+ sourceSet("main")
101101+ sourceSet("client")
102102+ }
101103 }
104104+105105+ mixin.useLegacyMixinAp = false
102106 }
103107104104- mixin.useLegacyMixinAp = false
105105- }
106106-107107- extensions.getByType<net.fabricmc.loom.api.fabricapi.FabricApiExtension>().apply {
108108- configureTests {
109109- modId = modid
110110- eula = true
108108+ extensions.getByType<net.fabricmc.loom.api.fabricapi.FabricApiExtension>().apply {
109109+ configureTests {
110110+ modId = modid
111111+ eula = true
112112+ }
111113 }
112112- }
113114114114- dependencies {
115115- "modLocalRuntime"("maven.modrinth:ears:1.4.7+fabric-1.20")
116116- }
115115+ dependencies {
116116+ "modLocalRuntime"("maven.modrinth:ears:1.4.7+fabric-1.20")
117117+ }
117118118118- if (project != rootProject) {
119119- tasks.named("runClient") {
120120- doFirst {
121121- val rootOptions = rootProject.file("run/options.txt").toPath()
122122- val options = file("run/options.txt").toPath()
123123- options.deleteIfExists()
124124- Files.createSymbolicLink(options, rootOptions)
119119+ if (project != rootProject) {
120120+ tasks.named("runClient") {
121121+ doFirst {
122122+ val rootOptions = rootProject.file("run/options.txt").toPath()
123123+ val options = file("run/options.txt").toPath()
124124+ options.deleteIfExists()
125125+ Files.createSymbolicLink(options, rootOptions)
126126+ }
125127 }
126128 }
127127- }
128129129129- tasks.processResources {
130130- val bookRoot = destinationDir.resolve("assets/hexcasting/patchouli_books/thehexbook")
131131- val langRoot = destinationDir.resolve("assets/$modid/lang")
130130+ tasks.processResources {
131131+ val bookRoot = destinationDir.resolve("assets/hexcasting/patchouli_books/thehexbook")
132132+ val langRoot = destinationDir.resolve("assets/$modid/lang")
132133133133- doLast {
134134- bookRoot.list()?.forEach { lang ->
135135- val langFile = langRoot.resolve("$lang.json")
136136- if (langFile.exists()) {
137137- val entries = JsonSlurper().parseText(langFile.readText()) as MutableMap<String, String>
138138- var n = 0
139139- for (bookFile in bookRoot.resolve(lang).walkTopDown()) {
140140- if (bookFile.isFile) {
141141- val json = JsonSlurper().parseText(bookFile.readText())
142142- if (json !is Map<*, *>) continue
143143- json as MutableMap<Any, Any>
144144- val name = json["name"]
145145- if (name is String) {
146146- entries["text.$modid.book.${n}"] = name
147147- json["name"] = "text.$modid.book.${n}"
148148- n++
149149- }
150150- val pages = json["pages"]
151151- if (pages !is MutableList<*>) continue
152152- pages as MutableList<Any>
153153- for (i in pages.indices) {
154154- val page = pages[i]
155155- if (page is String) {
156156- entries["text.$modid.book.${n}"] = page
157157- pages[i] = "text.$modid.book.${n}"
134134+ doLast {
135135+ bookRoot.list()?.forEach { lang ->
136136+ val langFile = langRoot.resolve("$lang.json")
137137+ if (langFile.exists()) {
138138+ val entries = JsonSlurper().parseText(langFile.readText()) as MutableMap<String, String>
139139+ var n = 0
140140+ for (bookFile in bookRoot.resolve(lang).walkTopDown()) {
141141+ if (bookFile.isFile) {
142142+ val json = JsonSlurper().parseText(bookFile.readText())
143143+ if (json !is Map<*, *>) continue
144144+ json as MutableMap<Any, Any>
145145+ val name = json["name"]
146146+ if (name is String) {
147147+ entries["text.$modid.book.${n}"] = name
148148+ json["name"] = "text.$modid.book.${n}"
158149 n++
159159- } else if (page is MutableMap<*, *>) {
160160- page as MutableMap<Any, Any>
161161- for (key in listOf("text", "title", "header")) {
162162- val text = page[key]
163163- if (text != null && text is String) {
164164- entries["text.$modid.book.${n}"] = text
165165- page[key] = "text.$modid.book.${n}"
166166- n++
150150+ }
151151+ val pages = json["pages"]
152152+ if (pages !is MutableList<*>) continue
153153+ pages as MutableList<Any>
154154+ for (i in pages.indices) {
155155+ val page = pages[i]
156156+ if (page is String) {
157157+ entries["text.$modid.book.${n}"] = page
158158+ pages[i] = "text.$modid.book.${n}"
159159+ n++
160160+ } else if (page is MutableMap<*, *>) {
161161+ page as MutableMap<Any, Any>
162162+ for (key in listOf("text", "title", "header")) {
163163+ val text = page[key]
164164+ if (text != null && text is String) {
165165+ entries["text.$modid.book.${n}"] = text
166166+ page[key] = "text.$modid.book.${n}"
167167+ n++
168168+ }
167169 }
168170 }
169171 }
172172+ bookFile.writeText(JsonOutput.toJson(json))
170173 }
171171- bookFile.writeText(JsonOutput.toJson(json))
172174 }
175175+ langFile.writeText(JsonOutput.toJson(entries))
173176 }
174174- langFile.writeText(JsonOutput.toJson(entries))
175177 }
176178 }
177179 }
180180+ }
181181+ }
182182+183183+ plugins.withId("scala") {
184184+ scala {
185185+ scalaVersion = "3.7.1"
178186 }
179187 }
180188···224232 exactRepo("https://maven.ladysnake.org/releases/",
225233 "dev.onyxstudios")
226234 exactRepo("https://pool.net.eu.org/",
235235+ "com.unascribed",
227236 "dev.kineticcat.hexportation",
228237 "miyucomics.hexcellular",
229238 "miyucomics.hexical",
···234243 exactRepo("https://maven.shedaniel.me/",
235244 "dev.architectury",
236245 "me.shedaniel")
237237- exactRepo("https://maven.terraformersmc.com/",
246246+ exactRepo("https://maven.terraformersmc.com/releases",
238247 "com.terraformersmc",
239248 "dev.emi")
240240- exactRepo("https://repo.sleeping.town/",
241241- "com.unascribed")
249249+ // mirrored by poolmaven due to invalid cert
250250+ //exactRepo("https://repo.sleeping.town/",
251251+ // "com.unascribed")
242252 exactRepo("https://masa.dy.fi/maven/",
243253 "carpet")
244254 exactRepo("https://maven.nucleoid.xyz/",
+1-15
gradle.properties
···11-# Done to increase the memory available to gradle.
21org.gradle.jvmargs=-Xmx1G
32org.gradle.java.installations.fromEnv=JDK17
44-# Fabric Properties
55-# check these on https://modmuss50.me/fabric.html
66-minecraft_version=1.20.1
77-yarn_mappings=1.20.1+build.10
88-loader_version=0.16.14
99-scala_loader_version=0.3.1.11
1010-# Mod Properties
1111-modid=hexic
1212-mod_version=2.0.0-alpha.01
1313-py_version=1.0
1414-maven_group=org.eu.net.pool
1515-# Dependencies
1616-# check this on https://modmuss50.me/fabric.html
1717-fabric_version=0.92.6+1.20.1
33+maven_group=org.eu.net.pool
+46-57
project.org
···11+# -*- mode: org; mode: visual-line -*-
12#+TITLE: Hex Addon Planning
23#+AUTHOR: poolcritter
34#+CATEGORY: hex-addons
···89:PROPERTIES:
910:CATEGORY: hexic
1011:END:
1111-pile of old ideas, deprecated and ripe for theft
1212-*** TODO finish enumerating features
1313-:LOGBOOK:
1414-CLOCK: [2026-01-08 Thu 13:25]--[2026-01-08 Thu 13:26] => 0:01
1515-CLOCK: [2026-01-08 Thu 13:03]--[2026-01-08 Thu 13:19] => 0:16
1616-:END:
1717-if any of y'all people want to steal this, ping me so I can remove it from the list
1818-1919-- [X] [[./project/iotaworks/][patchworks]]
2020-- [X] [[./project/hexxytounge][murmur refl]]
2121-- [X] [[./project/hexxytounge/][greater reveal]]
2222-- [ ] mediaweave
2323- - [ ] wool edification
2424- - [ ] messaging frames (for mediaweave)
2525-- [ ] stringworms
2626- - [ ] shimmering stringworms
2727-- [ ] stream iotas
2828-- [ ] pattern remapping (modpacks)
2929-- [ ] pens
3030-- [ ] media pouches
3131-- [ ] NBT iotas
3232-- [ ] tripwire iotas (unimplemented)
3333-- [ ] media chiseling
3434-- [ ] (+hexical) hopper into/out of conduits
3535-- [X] (+hexical) Apply Pigment pattern
3636- - [X] works on trinkets
3737-- [ ] list manipulation patterns (where, take, rotate, drop, grep, extract)
3838-- [ ] modulo 2
3939-- [ ] vulpine gambit
4040-- [ ] tupling exaltations
4141-- [ ] snow pattern
4242-- [ ] lani gambits
4343-- [ ] dual's reflection
4444-- [X] [[https://github.com/object-Object/IoticBlocks][erase block/entity]]
4545-- [ ] pseudothoth
4646-- [ ] echo shard casting
4747-- [ ] item overstacking
1212+the misc stuff addon for doing misc addon things
1313+** Chiseled amethyst
4814*** TODO give chisels a texture
4949-*** TODO special demiplane NG handling
5050-when entering, always enter at the center
5151-when exiting, exit where you last entered from
5252-or get sent to the deep noo
5315*** TODO give tables a model in-inventory
1616+*** TODO tripwires
1717+deleted code:
1818+#+BEGIN_SRC scala
1919+hexXplat.getContinuationTypeRegistry("tripwire") = TripwireIota.Frame
2020+Patterns.register("tripwire", w"edewqwaqede"):
2121+ Patterns.mkLiteral(TripwireIota)
2222+#+END_SRC
2323+#+BEGIN_SRC scala
2424+object TripwireIota extends Iota(new IotaType[TripwireIota.type]:
2525+ override def deserialize(tag: NbtElement, world: ServerWorld): TripwireIota.type = TripwireIota
2626+ override def color: Int = 0xba4216
2727+ override def display(tag: NbtElement): Text = typeName
2828+, Object()):
2929+ override def isTruthy: Boolean = true
3030+ override def toleratesOther(that: Iota): Boolean = eq(that)
3131+ override def serialize(): NbtElement = NbtCompound()
3232+ class Frame(mishap: Mishap) extends ContinuationFrame:
3333+ override def breakDownwards(x: ju.List[? <: Iota]): Pair[java.lang.Boolean, ju.List[Iota]] = ???
3434+ override def evaluate(cont: SpellContinuation, world: ServerWorld, vm: CastingVM): CastResult = throw mishap
3535+ override def getType: Type[?] = Frame
3636+ override def serializeToNBT(): NbtCompound =
3737+ val c = NbtCompound()
3838+ val codec = mishap.getCodec
3939+ c.putString("Class", mishap.getClass.getName)
4040+ c.put("Data", codec.encodeStart(NbtOps.INSTANCE, mishap.asInstanceOf).getOrThrow(false, _ => {}))
4141+ c
4242+ override def size: Int = 1
4343+ object Frame extends ContinuationFrame.Type[Frame]:
4444+ override def deserializeFromNBT(c: NbtCompound, world: ServerWorld): Frame =
4545+ val klass = classNamed(c.getString("Class"))
4646+ val codec = klass.get.runtimeClass.getMethod("getCodec").invoke(null).asInstanceOf[Codec[? <: Mishap]]
4747+ Frame(codec.decode(NbtOps.INSTANCE, c.get("Data")).getOrThrow(false, _ => {}).getFirst)
4848+#+END_SRC
5449*** TODO crafted amethyst
5550created by a table; worth a shard
5651need to implement texture generation
5752can be charmed; breaks if discharmed
5853*** TODO document undocumented patterns
5954- [X] demiplane patterns
6060-- [ ] conjure snow
6155- [ ] pseudothoth
5656+** Demiplanes
5757+*** TODO special demiplane NG handling
5858+when entering, always enter at the center
5959+when exiting, exit where you last entered from
6060+or get sent to the deep noo
6261** [[https://modrinth.com/mod/phlib][PHLib]]
6362*** TODO [#A] make a mod icon
6463probably my player head holding a quenched amethyst
6564*** TODO reimplement Flock Decomp on maps
6665*** TODO phlib ↔ [[https://modrinth.com/mod/hexthings][hexthings]] map conversion
6766*** TODO dialect conversion in general, actually
6868-** Underevaluate
6969-*** TODO create project
7070-it needs a real name
7171-*** TODO Hermes' Gambling
7272-executes given iota with a 25% chance, <ne,qqqeeaqq>
7373-*** TODO find other scrapped ideas
7467** [[https://modrinth.com/mod/iotaworks][Iotaworks]]
7568*** TODO finish implementing subscripts
7669*** TODO implement patchwork subscripts
7777-** HexxyChests
7878-*** TODO make sure everything works
7979-*** TODO make todo list
8080-** HexxyTongue
8181-*** TODO port mediaweaves
8282-*** TODO channeled reveal
8383-maybe revealing a map merges it with an internal 'channel' map?
7070+*** TODO steal labels and annotations from hex\mu
7171+probably blocked on [[IoticIotas]]
8472** IoticIotas
8573*** TODO revisit
8674does this still exist? is it still worth it?
···10189*** TODO wait for reply wrt Apply Pigment
10290**** TODO ping Miyu about [[https://discord.com/channels/936370934292549712/1011455473528098857/1459290686841946153][message]]
10391 SCHEDULED: <2026-01-10 12:00:00>
104104-*** TODO PR Apply Pigment
105105-make sure it works on trinkets
10692* Ideas
10793** TODO Spellminds
10894effectively cross-server cross-instance iota transfer bound permanenently to one specific player
···11096we need iota fmapping though
11197** TODO Liquidification greatspell
11298[[./.project.org.assets/liquidify-spell.png]]
9999+* Bugs
100100+** TODO see if latest VS2 is still incompatible
101101+** TODO fix Windows compatibility with Bind Demiplane
···11+1.4.7-pre.2 add a pattern to force snow
22+1.4.7-pre.2 add demiplane binding, replacing the positional arg for shattering demiplanes
33+1.4.7-pre.2 allow re-enabling old Lani behavior in config
44+1.4.7-pre.2 fix crash with canon 0.11.3 versions
55+1.4.7-pre.2 fix list math breaking hexdoc
66+1.4.7-pre.2 fix void-air scan
77+1.4.7-pre.2 make demiplanes not save redundant chunks
88+1.4.7-pre.2 make greatspells actually greatspells
99+1.4.7-pre.2 meow
1010+1.4.7-pre.2 remove internal error with out-of-bounds excisor's gambit
1111+2.0.0 add casting pouch sealing
1212+2.0.0 add Cessation and Resumption
1313+2.0.0 add docs for plane-related patterns
1414+2.0.0 add Hidden Sun's Nadir (blindness)
1515+2.0.0 add view iotas (experimental!)
1616+2.0.0 fix getting kicked if mediaweave sends an overly long message
1717+2.0.0 load config from config/*.properties instead of config/jvm.properties
1818+2.0.0 made demiplanes not instantly kill you and void your items if you look at them wrong
1919+2.0.0 made demiplanes remember where they were entered from
2020+2.0.0 made stringworms obtainable
2121+2.0.0 mediaweave now applies to all chat messages
2222+2.0.0 !mediaweave now uses trinket slots
2323+2.0.0 murmur and reveal now use separate components
2424+2.0.0 !only one mediaweave can be equipped at a time
2525+2.0.0 removed map iotas → moved to phlib with identical behavior
2626+2.0.0 !removed NBT iotas
2727+2.0.0 removed patchwork iotas → moved to iotaworks with identical behavior
2828+2.0.0 !removed tripwires
2929+2.0.0 remove snow pattern & fix lithium incompatibility
3030+2.0.0 !remove suffering
3131+2.0.0 rename media bundles to casting pouches
3232+2.0.0 sent Stickia to the milk dimension
3333+2.0.0 textures now use JPEG XL
3434+2.0.1 fix hitbox for void blocks
···11-21{
33- "name": "Media Pouches",
22+ "name": "Casting Pouches",
43 "category": "hexcasting:items",
54 "icon": "hexic:small_preferred_bundle",
65 "sortnum": 1,
76 "pages": [
87 "Though a $(l:items/phials)$(item)Phial/$ is my ultimate goal for _media storage, knitting a pouch out of $(l:addon/hexic/mediaweave)$(item)mediaweave/$ may help me manage my amethyst better. Only a few cloths are needed to give me some decent capacity. Each pouch holds six slots, and I can extend their capacity to twelve with a bit more weave. A small pouch may go within a large pouch, but neither size can be placed within itself.",
99- "Upon Nature's request for _media, my pouches will offer themselves and their contents first of all; similarly, if I attempt to $(l:patterns/spells/hexcasting#hexcasting:recharge)$(action)Recharge/$ a pouch, the inserted _media will form a 'cloud' of sorts inside, which will settle into any $(l:items/hexcasting)$(item)trinket/$ or similar within.",
88+ "Upon Nature's request for _media, my pouches will offer themselves and their contents first of all; similarly, if I attempt to $(l:patterns/spells/hexcasting#hexcasting:recharge)$(action)Recharge/$ a pouch, the inserted _media will form a 'cloud' of sorts inside, which will settle into any $(l:items/hexcasting)$(item)trinket/$ or similar within. To inhibit this behavior, casting pouches may be sealed by applying $(item)Honeycomb/$ the same way I'd insert an item. I can later wash off the seal by applying a wet sponge.",
109 {
1110 "type": "patchouli:text",
1211 "advancement": "hexcasting:enlightenment",
···44 "icon": "hexic:preferred_mediaweave",
55 "advancement": "hexcasting:root",
66 "pages": [
77- "A perversion of $(l:hexcasting:patterns/spells/blockworks#hexcasting:edify)$(action)Edify Sapling/$ lets me knit a fabric out of wool and _media. It acts similar to a $(l:hexcasting:items/thought_knot)$(item)Thought-Knot/$, but can only hold _Hexes — standard iotas fizzle instead of embedding themselves in the fabric. Additionally, the weave, radiating with raw energy, can passively exert force on nearby _media, with no mind involved — nothing as powerful as a _Hex, but still useful nonetheless."
77+ "A perversion of $(l:hexcasting:patterns/spells/blockworks#hexcasting:edify)$(action)Edify Sapling/$ lets me knit a fabric out of wool and _media. It acts similar to a $(l:hexcasting:items/thought_knot)$(item)Thought-Knot/$, but can only hold _Hexes — standard iotas fizzle instead of embedding themselves in the fabric. Additionally, the weave, radiating with raw energy, can passively exert force on nearby _media, with no mind involved — nothing as powerful as a _Hex, but still useful nonetheless.",
88+ "Interestingly, I can wear a single length of mediaweave around my neck. Though it is strangely comfortable, I wonder about the effects of putting such a strong concentration of media near my vocal chords… I should review my $(l:hexcasting:patterns/strings)string patterr/$$(o)raow—/$ ahem. This interaction may warrant further investigation."
89 ]
910}
···11-{
22- "name": "Nature's Records",
33- "category": "hexcasting:patterns",
44- "icon": "minecraft:glow_ink_sac",
55- "advancement": "hexcasting:enlightenment",
66- "pages": [
77- "My studies have led me to understand the fabric undermining every facet of this world. Nature's memory manifests itself as a recursive structure of 'tags' of several types. I've documented these over the next few pages. Though many types of tag appear to be redundant with each other, I must keep in mind that they are not freely interchangeable — and since I am dealing directly with Nature's memories, such a mistake could be worse than fatal.",
88- "If everything in the world is merely reduced to a tag... what am I? Is my entire existence reducible to a pile of bytes? At least this gives me some comfort, knowing that the villagers I've sacrificed in my journey truly had no life of their own... what about mine? What am I? Were the words I am writing in this very book, just a manifestation of random ones and zeroes?$(2br)I best not ponder too much. Though I may pry into everything I wish in detail, doing so would suck the joy from this illusion.",
99- {
1010- "type": "hexcasting:pattern",
1111- "anchor": "hexic:nbt/lift1",
1212- "op_id": "hexic:nbt/lift1",
1313- "input": "number",
1414- "output": "nbt",
1515- "text": "Converts a number into a Byte tag: an integer between -128 and 127, inclusive."
1616- },
1717- {
1818- "type": "hexcasting:pattern",
1919- "anchor": "hexic:nbt/lift2",
2020- "op_id": "hexic:nbt/lift2",
2121- "input": "number",
2222- "output": "nbt",
2323- "text": "Converts a number into a Short tag, between -65536 and 65535."
2424- },
2525- {
2626- "type": "hexcasting:pattern",
2727- "anchor": "hexic:nbt/lift4",
2828- "op_id": "hexic:nbt/lift4",
2929- "input": "number",
3030- "output": "nbt",
3131- "text": "Converts a number into an Int tag, between roughly negative 2 billion and positive 2 billion."
3232- },
3333- {
3434- "type": "hexcasting:pattern",
3535- "anchor": "hexic:nbt/lift8",
3636- "op_id": "hexic:nbt/lift8",
3737- "input": "number",
3838- "output": "nbt",
3939- "text": "Converts a number into a Long tag: bounded to 9 quintillion, which I will likely never reach."
4040- },
4141- {
4242- "type": "hexcasting:pattern",
4343- "anchor": "hexic:nbt/liftd",
4444- "op_id": "hexic:nbt/liftd",
4545- "input": "number",
4646- "output": "nbt",
4747- "text": "Converts a number into a Double tag. Oddly, doubles seem to perfectly correspond to my knowledge of numbers, plus the ability to represent infinities and non-numbers."
4848- },
4949- {
5050- "type": "hexcasting:pattern",
5151- "anchor": "hexic:nbt/liftf",
5252- "op_id": "hexic:nbt/liftf",
5353- "input": "number",
5454- "output": "nbt",
5555- "text": "Converts a number into a Float tag. These seem similar to Doubles, albeit with less precision."
5656- }
5757- ]
5858-}
···11-{
22- "name": "Nature's Revenge",
33- "advancement": "hexcasting:enlightenment",
44- "category": "hexcasting:patterns",
55- "icon": "arrow",
66- "pages": [
77- "No. No. No. I— I thought Horrible was bad— etched into every waking moment— no— no— it hurts— must draw— can't draw— what have I done— why must Nature be like this— ",
88- {
99- "type": "hexcasting:pattern",
1010- "op_id": "hexic:whatthefuck",
1111- "anchor": "hexic:whatthefuck",
1212- "input": "???",
1313- "output": "???",
1414- "text": "How could one even begin to draw this?"
1515- }
1616- ]
1717-}
···44 "advancement": "hexcasting:root",
55 "icon": "hexic:preferred_stringworm",
66 "pages": [
77- "A strange object I've discovered, stringworms are worm imitations made from fuzz. I'd imagine some people would have fun playing with them, but they have no use in my studies."
77+ "A strange object I've discovered, stringworms are worm imitations made from fuzz. I can acquire a stringworm by edifying placed String; however, they have no use in my studies."
88 ]
99}
···22 "name": "Demiplanes",
33 "advancement": "hexcasting:enlightenment",
44 "category": "hexcasting:patterns",
55- "icon": "white_carpet",
55+ "icon": "minecraft:white_carpet",
66 "pages": [
77- "the world under my control nature is mine na$(bold)ture is MI/$NE the world $(underline)the world NO LONGER/$ will $(bold)the OVerworld rule me/$ My $(media)mind is going/$ My $(media)mind/$ is going $(o)THE PAIN OF THOUGHT/$ my mind my $(o)mind/$ my a nine by nine room to myself SOLELY MYE$(bold)LF NO ONE ELSE/$ a $(bold)burst/$ of _media to Shatter the $(media)world/$ nearly $(media)152 allays $(bold)sewn/$ into $(underline)the walls/$ for eternity oh how it must hurt $(bold)IT HURTS IT/$ $(k)TORTUES/$",
77+ "the world under my control nature is mine na$(bold)ture is MI/$NE the world $(underline)the world NO LONGER/$ will $(bold)the OVerworld rule me/$ My $(media)mind is going/$ My $(media)mind/$ is going $(o)THE PAIN OF THOUGHT/$ my mind my $(o)mind/$ my a nine by nine room to myself SOLELY MYE$(bold)LF NO ONE ELSE/$ a $(bold)burst/$ of _media to Shatter the $(media)world/$ nearly $(media)six` allays $(bold)sewn/$ into $(underline)the walls/$ for eternity oh how it must hurt $(bold)IT HURTS IT/$ $(k)TORTUES/$",
88 {
99 "type": "hexcasting:pattern",
1010 "op_id": "hexic:makeworld",
···2222 "output": "",
2323 "text": "Binds the given demiplane to a position. This position is used when exiting the demiplane, as well as in case of any $(o)unfortunate accidents/$. Mishaps if I bind a demiplane to the plane I'm in."
2424 },
2525- "My mind is drifting drifting $(o)spinning/$, $(o)running/$ from me with every slice I take of Nature. Every passing moment my grip oh my hands weakening my mind passing through like sand$(2br)I must steady myself. The price of sanity is outright shattering the planes I've made — a burst of media worth 750 shards, and the pocket will crumple like a villager, spewing its contents into where it was bound.$(2br)Testing this process on living creatures is unwise.",
2525+ "My mind is drifting drifting $(o)spinning/$, $(o)running/$ from me with every slice I take of Nature. Every passing moment my grip oh my hands weakening my mind passing through like sand$(2br)I must steady myself. The price of sanity is outright shattering the planes I've made — a burst of media worth 25 shards, and the pocket will crumple like a villager, spewing its contents into where it was bound.$(2br)Testing this process on living creatures is unwise.",
2626 {
2727 "type": "hexcasting:pattern",
2828- "op_id": "hexic:attachworld",
2929- "anchor": "hexic:attachworld",
2828+ "op_id": "hexic:deleteworld",
2929+ "anchor": "hexic:deleteworld",
3030 "input": "imprint, vec",
3131 "output": "",
3232 "text": "Ruptures the boundaries of the given demiplane, destroying its contents. All dropped items and experience are spewed at the plane's attachment point."
···77 "Thus, I've devised a new pattern of my own. It's similar to $(l:hexcasting:patterns/basics#hexcasting:print)$(action)Reveal/$, but the message is lodged well within my mind, and I forget it instantly if I replace it with something else. It can also take a list of messages, for convenience.",
88 {
99 "type": "hexcasting:pattern",
1010- "anchor": "hexxytounge:reveal",
1111- "op_id": "hexxytounge:reveal",
1010+ "anchor": "hexic:reveal",
1111+ "op_id": "hexic:reveal",
1212 "input": "[iota] | iota",
1313 "output": "",
1414 "text": "Takes a list of iotas (or a single non-iota, which is treated as a list with one element) and embeds them in my mind persistently. Displayed iotas survive all methods that would clear a normal Reveal, and can only be cleared by another casting of Greater Reveal or my untimely demise."
···11-{
22- "hexcasting.action.hexxytounge:murmur": "Murmur Reflection",
33- "book.hexxytounge.page.murmur": "Finds the region of my mind known as the 'chat box' and adds its contents to the stack. If it cannot be found, adds Null instead."
44-}
···1515 },
1616 {
1717 "type": "hexcasting:pattern",
1818- "op_id": "hexcasting:add",
1919- "anchor": "hexcasting:add",
2020- "input": "map<k, v>, map<k, v>",
1818+ "op_id": "hexcasting:replace",
1919+ "anchor": "hexcasting:replace",
2020+ "input": "map<k, v>, k, v",
2121 "output": "map<k, v>",
2222- "text": "Merges two maps together. Maps cannot have duplicate items: any entry in the latter map will override that in the first map."
2222+ "text": "Replaces the element associated with $(n)k/$ with $(n)v/$. If $(n)k/$ is not present in the map, the entry is created and placed at the end of the map."
2323 },
2424 {
2525 "type": "hexcasting:pattern",
2626- "op_id": "hexcasting:sub",
2727- "anchor": "hexcasting:sub",
2828- "input": "map<k, v>, map<t, *>",
2626+ "op_id": "hexcasting:remove_from",
2727+ "anchor": "hexcasting:remove_from",
2828+ "input": "map<k, v>, k",
2929 "output": "map<k, v>",
3030- "text": "Removes every entry in the first map that is found in the second map. The values in the second map are ignored."
3030+ "text": "Throws out the map's association for the given key. If there is no matching association, the map is returned unchanged."
3131 },
3232 {
3333 "type": "hexcasting:pattern",
···3535 "anchor": "hexcasting:index",
3636 "input": "map<k, v>, k",
3737 "output": "v?",
3838- "text": "Disintegrates the map, returning only the value associated with the given key. O(1), unlike scanning a list."
3838+ "text": "Destroys the map, returning only the value associated with the given key. O(1), unlike scanning a list."
3939+ },
4040+ {
4141+ "type": "hexcasting:pattern",
4242+ "op_id": "hexcasting:splat",
4343+ "anchor": "hexcasting:splat",
4444+ "input": "map<k, v>",
4545+ "output": "k, v, k, v...",
4646+ "text": "Disintegrates the map, returning the key-value pairs in the order I inserted them."
3947 },
4048 {
4149 "type": "hexcasting:pattern",
4250 "op_id": "hexcasting:unappend",
4351 "anchor": "hexcasting:unappend",
4444- "input": "map<k, v>",
4545- "output": "map<k, v>, (k, v)?",
4646- "text": "Shaves off one element of a map. Which element gets shaved off is undefined and subject to Nature's whims. If used on an empty map, returns two nulls."
5252+ "input": "map<k,v>",
5353+ "output": "map<k,v>, (k,v | nulls)",
5454+ "text": "Shaves off the element I added most recently from a map. If used on an empty map, returns two nulls."
4755 },
4856 {
4957 "type": "hexcasting:pattern",
5050- "op_id": "hexcasting:replace",
5151- "anchor": "hexcasting:replace",
5252- "input": "map<k, v>, k, v",
5858+ "op_id": "hexcasting:deconstruct",
5959+ "anchor": "hexcasting:deconstruct",
6060+ "input": "map<k,v>",
6161+ "output": "map<k,v>, (k,v | nulls)",
6262+ "text": "Shaves off the element I added least recently from a map. If used on an empty map, returns two nulls."
6363+ },
6464+ {
6565+ "type": "hexcasting:pattern",
6666+ "op_id": "hexcasting:add",
6767+ "anchor": "hexcasting:add",
6868+ "input": "map<k, v>, map<k, v>",
5369 "output": "map<k, v>",
5454- "text": "Replaces the element associated with $(n)k/$ with $(n)v/$. If $(n)k/$ is not present in the map, the entry is created."
7070+ "text": "Merges two maps together. Maps cannot have duplicate items: any entry in the latter map will override that in the first map."
5571 },
5672 {
5773 "type": "hexcasting:pattern",
5858- "op_id": "hexcasting:remove_from",
5959- "anchor": "hexcasting:remove_from",
6060- "input": "map<k, v>, k",
7474+ "op_id": "hexcasting:sub",
7575+ "anchor": "hexcasting:sub",
7676+ "input": "map<k, v>, map<t, *>",
6177 "output": "map<k, v>",
6262- "text": "Throws out the map's association for the given key. If there is no matching association, the map is returned unchanged."
7878+ "text": "Removes every entry in the first map that is found in the second map. The values in the second map are ignored."
6379 }
6480 ]
6581}
···155155 env.addExtension:
156156 new CastingEnvironmentComponent with CastingEnvironmentComponent.PostExecution:
157157 override def getKey: CastingEnvironmentComponent.Key[?] = key
158158-159158 override def onPostExecution(result: CastResult): Unit =
160159 result.getSideEffects.collectFirst:
161160 case m: OperatorSideEffect.DoMishap =>
···241240 else
242241 `else`
243242243243+trait Registered[T](registry: Registry[T], id: Identifier):
244244+ this: T =>
245245+ registry(id) = this
246246+244247extension (p: ServerPlayerEntity) def gimmeIota(iota: Iota): Unit =
245248 val m = p.getComponent(HexCardinalComponents.STAFFCAST_IMAGE)
246249 m.setImage(m.getVM(Hand.MAIN_HAND).getImage.withStack(_ ++ Vector(iota)))
250250+251251+private[phlib] trait AllocationTracked:
252252+ private[phlib] val phlib$createdAt: Exception
253253+254254+package mixin:
255255+ import net.minecraft.block.Block
256256+ import net.minecraft.item.Item
257257+ import org.spongepowered.asm.mixin.injection.{At, Inject}
258258+ import org.spongepowered.asm.mixin.Mixin
259259+ @Mixin(value = Array(classOf[Item], classOf[Block]))
260260+ private[phlib] class AllocationTrackerMixin() extends AnyRef with AllocationTracked:
261261+ private[phlib] val phlib$createdAt: Exception = new RuntimeException(s"unregistered $this ($getClass) created")
247262248263// this is out-of-scope for phlib but I have no idea where else to put it
249264def init() =
···9696 override def operate(env: CastingEnvironment, img: CastingImage, cont: SpellContinuation): OperationResult =
9797 val stack = img.getStack.asScala.toSeq
9898 val args = stack.takeRight(${Expr(a.size)})
9999- // I'm fairly certain the remainder of this method is considered a war crime
10099 ${
101100 Block(a.zipWithIndex.map { p =>
102101 val (v@ValDef(n, ty, _), i) = p