repo for my hex addons :3
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

resolve divergence

+1377 -1440
+1
.gitignore
··· 7 7 8 8 # IntelliJ 9 9 out/ 10 + .kotlin/ 10 11 # mpeltonen/sbt-idea plugin 11 12 .idea_modules/ 12 13
+17 -5
README.md
··· 2 2 hi! this is a repository for my hexcasting addons (both wip and released). i've spent way too much time (![look at how much time i wasted](https://hackatime-badge.hackclub.com/U081HJSMURJ/hexic)) on these, with little to show for it. 3 3 4 4 ## where to find stuff 5 - * code: [`hex-addons @ seed.lama2923.dev`](https://app.radicle.xyz/nodes/seed.lama2923.dev/rad%3AzChmxddnxPemALy4RuZ7zQgzDxYv) 6 - * issues: see above 7 - * reporting issues: check if your issue isn't already reported, then https://discord.com/invite/4xxHGYteWk and ping 8 - * pull requests: `hh clone pool.net.eu.org/hex-addons`, `hh sync` 5 + * repository: [`hex-addons @ seed.lama2923.dev`](https://app.radicle.xyz/nodes/seed.lama2923.dev/rad%3AzChmxddnxPemALy4RuZ7zQgzDxYv) 6 + (use `rad clone rad:zChmxddnxPemALy4RuZ7zQgzDxYv` to clone) 7 + * issues: see above, Issues tab 8 + * submitting an issue: clone the repository above, then `rad issue open` 9 + **PLEASE READ BEFORE CREATING AN ISSUE:** 10 + * please *describe* your issue 11 + * if possible, provide precise reproduction steps 12 + * attach logs and crash reports (both!) 13 + * ideally, provide a video 14 + 15 + if this is ignored I reserve the right to send your issue directly to the bit bucket 16 + * submitting patches: 17 + 1. clone the repo 18 + 2. `git switch -d` 19 + 3. make your code 20 + 4. `git push HEAD:refs/patches` 9 21 10 22 ## mods here 23 + * [**hexic**](https://modrinth.com/mod/hexic) adds a bunch of small fun things to play with 11 24 * [hex in yo chests](https://modrinth.com/mod/hexxychests) **(WIP)** adds spells for manipulating storage (and anything you could conceivably think of as storage) 12 25 * [**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 13 26 * [tongued hexxy](https://modrinth.com/project/hexxytounge) **(WIP)** will add methods of manipulating chat behavior (once I actually implement it) 14 27 * [**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 15 - * ~~[**hexic**](https://modrinth.com/mod/hexic)~~ <span style="color: red">(deprecated)</span> [![badge linking to documentation](https://img.shields.io/endpoint?url=https://hexxy.media/api/v0/badge/hexdoc/)](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
··· 1 1 import de.undercouch.gradle.tasks.download.Download 2 2 import groovy.json.JsonSlurper 3 - import org.eu.net.pool.mc_plugin.Environment 4 3 import org.gradle.api.publish.maven.internal.publication.MavenPomInternal 5 4 import org.gradle.kotlin.dsl.support.uppercaseFirstChar 6 5 import kotlin.io.path.exists ··· 27 26 id("maven-publish") 28 27 id("idea") 29 28 id("de.undercouch.download") version "5.6.0" 30 - id("org.eu.net.pool.mc-plugin") 29 + //id("org.eu.net.pool.mc-plugin") 31 30 } 32 31 33 32 allprojects { ··· 54 53 55 54 val release: Boolean = !System.getenv("release").isNullOrEmpty() 56 55 allprojects { 57 - val p = P(project) 56 + val p by lazy { 57 + val p = P(project) 58 + ext["p"] = p 59 + p 60 + } 58 61 val modid: String by project.properties 59 - ext["p"] = p 60 62 ext["release"] = release 61 - version = project.property("mod_version") as String 62 - if (!release) version = "${version}+${p.commit_id.take(7)}" 63 - group = rootProject.property("maven_group") as String 64 - println("configuring $modid ($project) v$version @ $group") 63 + val isProject = project.projectDir.path.contains("/project/") || project == project(":util") 64 + 65 65 plugins.withId("java") { 66 - base { 67 - archivesBaseName = modid 68 - } 69 66 java { 70 67 toolchain.languageVersion = JavaLanguageVersion.of(17) 71 68 withSourcesJar() 72 69 } 73 - // be extra sure 74 - version = project.property("mod_version") as String 70 + } 71 + 72 + if (isProject) { 75 73 if (!release) version = "${version}+${p.commit_id.take(7)}" 76 74 group = rootProject.property("maven_group") as String 77 - 78 - tasks.named<Jar>("jar").configure { 79 - from("LICENSE") { 80 - rename { "LICENSE_$modid" } 75 + println("configuring $modid ($project) v$version @ $group") 76 + plugins.withId("java") { 77 + base { 78 + archivesBaseName = modid 81 79 } 82 - duplicatesStrategy = DuplicatesStrategy.WARN 83 - } 84 - } 85 - 86 - plugins.withId("scala") { 87 - scala { 88 - scalaVersion = "3.7.1" 80 + tasks.named<Jar>("jar").configure { 81 + from("LICENSE") { 82 + rename { "LICENSE_$modid" } 83 + } 84 + duplicatesStrategy = DuplicatesStrategy.WARN 85 + } 89 86 } 90 - } 91 87 92 - plugins.withId("fabric-loom") { 93 - extensions.getByType<LoomGradleExtensionAPI>().apply { 94 - splitEnvironmentSourceSets() 95 - runs["client"].programArgs += listOf("--username", "Player", "--uuid", "9e1b34e3-8031-4623-8918-eb7914ab564b") 88 + plugins.withId("fabric-loom") { 89 + extensions.getByType<LoomGradleExtensionAPI>().apply { 90 + splitEnvironmentSourceSets() 91 + runs["client"].programArgs += listOf( 92 + "--username", 93 + "Player", 94 + "--uuid", 95 + "9e1b34e3-8031-4623-8918-eb7914ab564b" 96 + ) 96 97 97 - mods { 98 - register(modid) { 99 - sourceSet("main") 100 - sourceSet("client") 98 + mods { 99 + register(modid) { 100 + sourceSet("main") 101 + sourceSet("client") 102 + } 101 103 } 104 + 105 + mixin.useLegacyMixinAp = false 102 106 } 103 107 104 - mixin.useLegacyMixinAp = false 105 - } 106 - 107 - extensions.getByType<net.fabricmc.loom.api.fabricapi.FabricApiExtension>().apply { 108 - configureTests { 109 - modId = modid 110 - eula = true 108 + extensions.getByType<net.fabricmc.loom.api.fabricapi.FabricApiExtension>().apply { 109 + configureTests { 110 + modId = modid 111 + eula = true 112 + } 111 113 } 112 - } 113 114 114 - dependencies { 115 - "modLocalRuntime"("maven.modrinth:ears:1.4.7+fabric-1.20") 116 - } 115 + dependencies { 116 + "modLocalRuntime"("maven.modrinth:ears:1.4.7+fabric-1.20") 117 + } 117 118 118 - if (project != rootProject) { 119 - tasks.named("runClient") { 120 - doFirst { 121 - val rootOptions = rootProject.file("run/options.txt").toPath() 122 - val options = file("run/options.txt").toPath() 123 - options.deleteIfExists() 124 - Files.createSymbolicLink(options, rootOptions) 119 + if (project != rootProject) { 120 + tasks.named("runClient") { 121 + doFirst { 122 + val rootOptions = rootProject.file("run/options.txt").toPath() 123 + val options = file("run/options.txt").toPath() 124 + options.deleteIfExists() 125 + Files.createSymbolicLink(options, rootOptions) 126 + } 125 127 } 126 128 } 127 - } 128 129 129 - tasks.processResources { 130 - val bookRoot = destinationDir.resolve("assets/hexcasting/patchouli_books/thehexbook") 131 - val langRoot = destinationDir.resolve("assets/$modid/lang") 130 + tasks.processResources { 131 + val bookRoot = destinationDir.resolve("assets/hexcasting/patchouli_books/thehexbook") 132 + val langRoot = destinationDir.resolve("assets/$modid/lang") 132 133 133 - doLast { 134 - bookRoot.list()?.forEach { lang -> 135 - val langFile = langRoot.resolve("$lang.json") 136 - if (langFile.exists()) { 137 - val entries = JsonSlurper().parseText(langFile.readText()) as MutableMap<String, String> 138 - var n = 0 139 - for (bookFile in bookRoot.resolve(lang).walkTopDown()) { 140 - if (bookFile.isFile) { 141 - val json = JsonSlurper().parseText(bookFile.readText()) 142 - if (json !is Map<*, *>) continue 143 - json as MutableMap<Any, Any> 144 - val name = json["name"] 145 - if (name is String) { 146 - entries["text.$modid.book.${n}"] = name 147 - json["name"] = "text.$modid.book.${n}" 148 - n++ 149 - } 150 - val pages = json["pages"] 151 - if (pages !is MutableList<*>) continue 152 - pages as MutableList<Any> 153 - for (i in pages.indices) { 154 - val page = pages[i] 155 - if (page is String) { 156 - entries["text.$modid.book.${n}"] = page 157 - pages[i] = "text.$modid.book.${n}" 134 + doLast { 135 + bookRoot.list()?.forEach { lang -> 136 + val langFile = langRoot.resolve("$lang.json") 137 + if (langFile.exists()) { 138 + val entries = JsonSlurper().parseText(langFile.readText()) as MutableMap<String, String> 139 + var n = 0 140 + for (bookFile in bookRoot.resolve(lang).walkTopDown()) { 141 + if (bookFile.isFile) { 142 + val json = JsonSlurper().parseText(bookFile.readText()) 143 + if (json !is Map<*, *>) continue 144 + json as MutableMap<Any, Any> 145 + val name = json["name"] 146 + if (name is String) { 147 + entries["text.$modid.book.${n}"] = name 148 + json["name"] = "text.$modid.book.${n}" 158 149 n++ 159 - } else if (page is MutableMap<*, *>) { 160 - page as MutableMap<Any, Any> 161 - for (key in listOf("text", "title", "header")) { 162 - val text = page[key] 163 - if (text != null && text is String) { 164 - entries["text.$modid.book.${n}"] = text 165 - page[key] = "text.$modid.book.${n}" 166 - n++ 150 + } 151 + val pages = json["pages"] 152 + if (pages !is MutableList<*>) continue 153 + pages as MutableList<Any> 154 + for (i in pages.indices) { 155 + val page = pages[i] 156 + if (page is String) { 157 + entries["text.$modid.book.${n}"] = page 158 + pages[i] = "text.$modid.book.${n}" 159 + n++ 160 + } else if (page is MutableMap<*, *>) { 161 + page as MutableMap<Any, Any> 162 + for (key in listOf("text", "title", "header")) { 163 + val text = page[key] 164 + if (text != null && text is String) { 165 + entries["text.$modid.book.${n}"] = text 166 + page[key] = "text.$modid.book.${n}" 167 + n++ 168 + } 167 169 } 168 170 } 169 171 } 172 + bookFile.writeText(JsonOutput.toJson(json)) 170 173 } 171 - bookFile.writeText(JsonOutput.toJson(json)) 172 174 } 175 + langFile.writeText(JsonOutput.toJson(entries)) 173 176 } 174 - langFile.writeText(JsonOutput.toJson(entries)) 175 177 } 176 178 } 177 179 } 180 + } 181 + } 182 + 183 + plugins.withId("scala") { 184 + scala { 185 + scalaVersion = "3.7.1" 178 186 } 179 187 } 180 188 ··· 224 232 exactRepo("https://maven.ladysnake.org/releases/", 225 233 "dev.onyxstudios") 226 234 exactRepo("https://pool.net.eu.org/", 235 + "com.unascribed", 227 236 "dev.kineticcat.hexportation", 228 237 "miyucomics.hexcellular", 229 238 "miyucomics.hexical", ··· 234 243 exactRepo("https://maven.shedaniel.me/", 235 244 "dev.architectury", 236 245 "me.shedaniel") 237 - exactRepo("https://maven.terraformersmc.com/", 246 + exactRepo("https://maven.terraformersmc.com/releases", 238 247 "com.terraformersmc", 239 248 "dev.emi") 240 - exactRepo("https://repo.sleeping.town/", 241 - "com.unascribed") 249 + // mirrored by poolmaven due to invalid cert 250 + //exactRepo("https://repo.sleeping.town/", 251 + // "com.unascribed") 242 252 exactRepo("https://masa.dy.fi/maven/", 243 253 "carpet") 244 254 exactRepo("https://maven.nucleoid.xyz/",
+1 -15
gradle.properties
··· 1 - # Done to increase the memory available to gradle. 2 1 org.gradle.jvmargs=-Xmx1G 3 2 org.gradle.java.installations.fromEnv=JDK17 4 - # Fabric Properties 5 - # check these on https://modmuss50.me/fabric.html 6 - minecraft_version=1.20.1 7 - yarn_mappings=1.20.1+build.10 8 - loader_version=0.16.14 9 - scala_loader_version=0.3.1.11 10 - # Mod Properties 11 - modid=hexic 12 - mod_version=2.0.0-alpha.01 13 - py_version=1.0 14 - maven_group=org.eu.net.pool 15 - # Dependencies 16 - # check this on https://modmuss50.me/fabric.html 17 - fabric_version=0.92.6+1.20.1 3 + maven_group=org.eu.net.pool
+46 -57
project.org
··· 1 + # -*- mode: org; mode: visual-line -*- 1 2 #+TITLE: Hex Addon Planning 2 3 #+AUTHOR: poolcritter 3 4 #+CATEGORY: hex-addons ··· 8 9 :PROPERTIES: 9 10 :CATEGORY: hexic 10 11 :END: 11 - pile of old ideas, deprecated and ripe for theft 12 - *** TODO finish enumerating features 13 - :LOGBOOK: 14 - CLOCK: [2026-01-08 Thu 13:25]--[2026-01-08 Thu 13:26] => 0:01 15 - CLOCK: [2026-01-08 Thu 13:03]--[2026-01-08 Thu 13:19] => 0:16 16 - :END: 17 - if any of y'all people want to steal this, ping me so I can remove it from the list 18 - 19 - - [X] [[./project/iotaworks/][patchworks]] 20 - - [X] [[./project/hexxytounge][murmur refl]] 21 - - [X] [[./project/hexxytounge/][greater reveal]] 22 - - [ ] mediaweave 23 - - [ ] wool edification 24 - - [ ] messaging frames (for mediaweave) 25 - - [ ] stringworms 26 - - [ ] shimmering stringworms 27 - - [ ] stream iotas 28 - - [ ] pattern remapping (modpacks) 29 - - [ ] pens 30 - - [ ] media pouches 31 - - [ ] NBT iotas 32 - - [ ] tripwire iotas (unimplemented) 33 - - [ ] media chiseling 34 - - [ ] (+hexical) hopper into/out of conduits 35 - - [X] (+hexical) Apply Pigment pattern 36 - - [X] works on trinkets 37 - - [ ] list manipulation patterns (where, take, rotate, drop, grep, extract) 38 - - [ ] modulo 2 39 - - [ ] vulpine gambit 40 - - [ ] tupling exaltations 41 - - [ ] snow pattern 42 - - [ ] lani gambits 43 - - [ ] dual's reflection 44 - - [X] [[https://github.com/object-Object/IoticBlocks][erase block/entity]] 45 - - [ ] pseudothoth 46 - - [ ] echo shard casting 47 - - [ ] item overstacking 12 + the misc stuff addon for doing misc addon things 13 + ** Chiseled amethyst 48 14 *** TODO give chisels a texture 49 - *** TODO special demiplane NG handling 50 - when entering, always enter at the center 51 - when exiting, exit where you last entered from 52 - or get sent to the deep noo 53 15 *** TODO give tables a model in-inventory 16 + *** TODO tripwires 17 + deleted code: 18 + #+BEGIN_SRC scala 19 + hexXplat.getContinuationTypeRegistry("tripwire") = TripwireIota.Frame 20 + Patterns.register("tripwire", w"edewqwaqede"): 21 + Patterns.mkLiteral(TripwireIota) 22 + #+END_SRC 23 + #+BEGIN_SRC scala 24 + object TripwireIota extends Iota(new IotaType[TripwireIota.type]: 25 + override def deserialize(tag: NbtElement, world: ServerWorld): TripwireIota.type = TripwireIota 26 + override def color: Int = 0xba4216 27 + override def display(tag: NbtElement): Text = typeName 28 + , Object()): 29 + override def isTruthy: Boolean = true 30 + override def toleratesOther(that: Iota): Boolean = eq(that) 31 + override def serialize(): NbtElement = NbtCompound() 32 + class Frame(mishap: Mishap) extends ContinuationFrame: 33 + override def breakDownwards(x: ju.List[? <: Iota]): Pair[java.lang.Boolean, ju.List[Iota]] = ??? 34 + override def evaluate(cont: SpellContinuation, world: ServerWorld, vm: CastingVM): CastResult = throw mishap 35 + override def getType: Type[?] = Frame 36 + override def serializeToNBT(): NbtCompound = 37 + val c = NbtCompound() 38 + val codec = mishap.getCodec 39 + c.putString("Class", mishap.getClass.getName) 40 + c.put("Data", codec.encodeStart(NbtOps.INSTANCE, mishap.asInstanceOf).getOrThrow(false, _ => {})) 41 + c 42 + override def size: Int = 1 43 + object Frame extends ContinuationFrame.Type[Frame]: 44 + override def deserializeFromNBT(c: NbtCompound, world: ServerWorld): Frame = 45 + val klass = classNamed(c.getString("Class")) 46 + val codec = klass.get.runtimeClass.getMethod("getCodec").invoke(null).asInstanceOf[Codec[? <: Mishap]] 47 + Frame(codec.decode(NbtOps.INSTANCE, c.get("Data")).getOrThrow(false, _ => {}).getFirst) 48 + #+END_SRC 54 49 *** TODO crafted amethyst 55 50 created by a table; worth a shard 56 51 need to implement texture generation 57 52 can be charmed; breaks if discharmed 58 53 *** TODO document undocumented patterns 59 54 - [X] demiplane patterns 60 - - [ ] conjure snow 61 55 - [ ] pseudothoth 56 + ** Demiplanes 57 + *** TODO special demiplane NG handling 58 + when entering, always enter at the center 59 + when exiting, exit where you last entered from 60 + or get sent to the deep noo 62 61 ** [[https://modrinth.com/mod/phlib][PHLib]] 63 62 *** TODO [#A] make a mod icon 64 63 probably my player head holding a quenched amethyst 65 64 *** TODO reimplement Flock Decomp on maps 66 65 *** TODO phlib ↔ [[https://modrinth.com/mod/hexthings][hexthings]] map conversion 67 66 *** TODO dialect conversion in general, actually 68 - ** Underevaluate 69 - *** TODO create project 70 - it needs a real name 71 - *** TODO Hermes' Gambling 72 - executes given iota with a 25% chance, <ne,qqqeeaqq> 73 - *** TODO find other scrapped ideas 74 67 ** [[https://modrinth.com/mod/iotaworks][Iotaworks]] 75 68 *** TODO finish implementing subscripts 76 69 *** TODO implement patchwork subscripts 77 - ** HexxyChests 78 - *** TODO make sure everything works 79 - *** TODO make todo list 80 - ** HexxyTongue 81 - *** TODO port mediaweaves 82 - *** TODO channeled reveal 83 - maybe revealing a map merges it with an internal 'channel' map? 70 + *** TODO steal labels and annotations from hex\mu 71 + probably blocked on [[IoticIotas]] 84 72 ** IoticIotas 85 73 *** TODO revisit 86 74 does this still exist? is it still worth it? ··· 101 89 *** TODO wait for reply wrt Apply Pigment 102 90 **** TODO ping Miyu about [[https://discord.com/channels/936370934292549712/1011455473528098857/1459290686841946153][message]] 103 91 SCHEDULED: <2026-01-10 12:00:00> 104 - *** TODO PR Apply Pigment 105 - make sure it works on trinkets 106 92 * Ideas 107 93 ** TODO Spellminds 108 94 effectively cross-server cross-instance iota transfer bound permanenently to one specific player ··· 110 96 we need iota fmapping though 111 97 ** TODO Liquidification greatspell 112 98 [[./.project.org.assets/liquidify-spell.png]] 99 + * Bugs 100 + ** TODO see if latest VS2 is still incompatible 101 + ** TODO fix Windows compatibility with Bind Demiplane
+48 -40
project/hexic/build.gradle.kts
··· 114 114 } 115 115 } 116 116 117 + loom.runs { 118 + register("mixinDebugClient") { 119 + inherit(loom.runs["client"]) 120 + vmArgs("-Dmixin.debug.export=true", "-Dmixin.debug.export.decompile=true") 121 + } 122 + } 123 + 117 124 val modDepends: Configuration by configurations.creating { 118 125 isTransitive = false 119 126 isCanBeResolved = true ··· 138 145 } 139 146 140 147 val minecraft_version = "1.20.1" 141 - modDepends(implementation(annotationProcessor("io.github.llamalad7:mixinextras-fabric:0.5.0")!!)!!) 148 + implementation(annotationProcessor("io.github.llamalad7:mixinextras-fabric:0.5.0")!!) 142 149 implementation(project(":util", "namedElements")) 143 150 modImplementation("io.github.tropheusj:serialization-hooks:0.4.99999") 144 151 modImplementation("at.petra-k.hexcasting:hexcasting-fabric-$minecraft_version:0.11.3") 145 152 modImplementation("com.samsthenerd.inline:inline-fabric:$minecraft_version-1.0.1") 146 - modDepends(include(implementation("com.github.Chocohead:Fabric-ASM:v2.3")!!)!!) 153 + include(implementation("com.github.Chocohead:Fabric-ASM:v2.3")!!) 147 154 modCompileOnly("dev.kineticcat.hexportation:hexportation-fabric-1.20.1-fabric-fabric:0.0.3") 148 155 modCompileOnly("carpet:fabric-carpet:1.20-1.+") 149 156 modLocalRuntime("maven.modrinth:lithium:mc1.20.1-0.11.4-fabric") ··· 152 159 modImplementation("net.beholderface.oneironaut:oneironaut-fabric-1.20.1-fabric-fabric:1.20.1-SNAPSHOT") 153 160 compat("maven.modrinth:hexcassettes:1.1.4") 154 161 modLocalRuntime("maven.modrinth:trinkets:3.7.2") 155 - modDepends(modImplementation("maven.modrinth:spasm:0.2.2")!!) 156 162 // modImplementation("maven.modrinth:slate-works:1.0.5") 157 163 compat("miyucomics.hexical:hexical:2.0.0+a3c47ad9") 158 164 compat("miyucomics.overevaluate:overevaluate:main-SNAPSHOT") 159 - modDepends(modImplementation("ram.talia.moreiotas:moreiotas-fabric-$minecraft_version:0.1.1") { exclude(module = "serialization-hooks") }) 160 - modDepends(modImplementation("ram.talia.hexal:hexal-fabric-1.20.1:0.3.0") { exclude(module = "serialization-hooks") }) 161 - modDepends(modImplementation("miyucomics.hexcellular:hexcellular:1.1.0")!!) 162 - modDepends(modImplementation("maven.modrinth:jsonpatcher:1.0.0-beta.4+mc.1.20.1")!!) 165 + modImplementation("ram.talia.moreiotas:moreiotas-fabric-$minecraft_version:0.1.1") { exclude(module = "serialization-hooks") } 166 + modImplementation("ram.talia.hexal:hexal-fabric-1.20.1:0.3.0") { exclude(module = "serialization-hooks") } 167 + modImplementation("miyucomics.hexcellular:hexcellular:1.1.0") 168 + modImplementation("maven.modrinth:jsonpatcher:1.0.0-beta.4+mc.1.20.1") 163 169 implementation("com.github.mattidragon:JsonPatcherLang:v1.0.0-beta.3") // trans maven.modrinth:jsonpatcher 164 170 modImplementation("com.github.mattidragon:ConfigToolkit:v1.0.0") // trans maven.modrinth:jsonpatcher 165 - modDepends(modImplementation("miyucomics.hexpose:hexpose:1.0.0")!!) 171 + modImplementation("miyucomics.hexpose:hexpose:1.0.0") 166 172 include(modApi("xyz.nucleoid:fantasy:0.4.11+1.20-rc1")!!) 173 + modImplementation("dev.emi:trinkets:3.7.2") 167 174 // modImplementation("miyucomics:hexpose:1.0.0") 168 175 // modImplementation(files("hexical-2.0.0.jar")) 169 176 include(implementation("net.bytebuddy:byte-buddy:1.17.7")!!) ··· 172 179 modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-entity:5.2.3") 173 180 modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-level:5.2.3") 174 181 modRuntimeOnly("dev.onyxstudios.cardinal-components-api:cardinal-components-api:5.2.3") 175 - 176 - modRuntimeOnly("gay.object.hexdebug:hexdebug-fabric:0.5.0+1.20.1-SNAPSHOT") 182 + modRuntimeOnly("com.unascribed:lib39-core:[2.0.0,)!!2.0.27+1.20.1") 183 + modRuntimeOnly("com.unascribed:lib39-avant:[2.0.0,3.0.0)!!2.0.27+1.20.1") 184 + modRuntimeOnly("com.unascribed:lib39-phantom:[2.0.0,3.0.0)!!2.0.27+1.20.1") 185 + modLocalRuntime("gay.object.hexdebug:hexdebug-fabric:0.5.0+1.20.1-SNAPSHOT") 186 + modLocalRuntime("maven.modrinth:hexcessible:0.2.0") 187 + modLocalRuntime("maven.modrinth:complex-hex:0.1.3-beta") 177 188 } 178 189 179 190 val colors = mapOf( ··· 214 225 put("discord", "https://discord.com/users/402104961812660226") 215 226 } 216 227 217 - for (p in modDepends.resolve()) { 218 - val root = 219 - if (p.isDirectory) p.toPath() 220 - else if (p.name.endsWith(".jar")) `java.nio.file`.FileSystems.newFileSystem(p.toPath()).rootDirectories.single() 221 - else continue 222 - val fmj = root.resolve("fabric.mod.json") 223 - if (fmj.exists()) { 224 - val json = JsonSlurper().parse(fmj) as Map<String, Any> 225 - depends(json["id"].toString(), ">=${json["version"]}") 226 - } else { 227 - println("Attempt to add dependency '$p' to modDepends, which is not a Fabric mod.") 228 - } 229 - } 230 - 228 + depends("mixinextras", "*") 229 + depends("mm", "^2.3") 230 + depends("moreiotas", ">=0.1.1") 231 + depends("hexal", ">=0.3.0") 232 + depends("hexcellular", "^1.1.0") 233 + depends("jsonpatcher", "^1.0.0-beta.4+mc.1.20.1") 234 + depends("hexpose", ">=1.0.0 <3.0.0") 235 + depends("trinkets", "^3.7.2") 236 + depends("phlib", ">=0.1.2 <0.2.0") 231 237 conflicts("valkyrienskies", "*") // need to figure out how to create dimensions without causing a crash 238 + breaks("java", ">=26") 232 239 233 240 entrypoint("org.eu.net.pool.hexic.main\$package::init") 234 - entrypoint("org.eu.net.pool.hexic.client.main\$package::init", Environment.Client) 235 - entrypoint("fabric-datagen", "org.eu.net.pool.hexic.client.main\$package::datagen") 241 + entrypoint("org.eu.net.pool.hexic.client\$package::init", Environment.Client) 242 + entrypoint("fabric-datagen", "org.eu.net.pool.hexic.client\$package::datagen") 236 243 entrypoint("mm:early_risers", "org.eu.net.pool.hexic.early_riser\$package::warCrimes") 237 244 entrypoint("cardinal-components", "org.eu.net.pool.hexic.ComponentInit") 238 245 mixins("hexic.mixins.json") 239 246 mixins("hexic.client.mixins.json", Environment.Client) 240 - cardinalComponents("player_wisp", "server_info") 247 + cardinalComponents("player_wisp", "server_info", "excursion", "murmur", "reveal", "cat") 241 248 } 242 249 } 243 250 244 251 dependsOn(cloth) 245 252 dependsOn(*downloadedBags.values.toTypedArray()) 246 253 val itemsRoot = destinationDir.resolve("assets/hexic/textures/item") 254 + val jxlOpts = arrayOf("-quality", "100", "-define", "jxl:effort=11", "-define", "jxl:lossless=true", "-define", "jxl:modular=true") 247 255 doLast { 248 256 for ((name, color) in colors) { 249 257 exec { 250 - commandLine("env", "magick", cloth.dest, "-channel", "red,green,blue", "-fx", "u*#${color.toString(16)}", itemsRoot.resolve("${name}_mediaweave.png")) 258 + commandLine("env", "magick", cloth.dest, "-channel", "red,green,blue", "-fx", "u*#${color.toString(16)}", *jxlOpts, "jxl:${itemsRoot.resolve("${name}_mediaweave.png")}") 251 259 } 252 260 val bag = downloadedBags[name]!!.dest 253 261 exec { 254 - commandLine("env", "magick", bag, "-write", itemsRoot.resolve("large_${name}_bundle.png"), "-sample", "14x14", "-background", "transparent", "-extent", "16x16-1-2", itemsRoot.resolve("small_${name}_bundle.png")) 262 + commandLine("env", "magick", bag, *jxlOpts, "-write", "jxl:${itemsRoot.resolve("large_${name}_bundle.png")}", "-sample", "14x14", "-background", "transparent", "-extent", "16x16-1-2", "jxl:${itemsRoot.resolve("small_${name}_bundle.png")}") 255 263 } 256 264 } 257 265 exec { 258 - commandLine("env", "magick", "wizard:", itemsRoot.resolve("wizard.png")) 266 + commandLine("env", "magick", "wizard:", *jxlOpts, "jxl:${itemsRoot.resolve("wizard.png")}") 259 267 } 260 268 exec { 261 - commandLine("env", "magick", "null:", itemsRoot.resolve("no.png")) 269 + commandLine("env", "magick", "null:", *jxlOpts, "jxl:${itemsRoot.resolve("no.jxl")}") 262 270 } 263 271 exec { 264 272 commandLine("env", "magick", ··· 286 294 "pure" to "u", 287 295 )) { 288 296 exec { 289 - commandLine("env", "magick", itemsRoot.resolve("stringworm.miff"), "-channel", "rgb", "-fx", expr, "$itemsRoot/stringworm_$name.png") 297 + commandLine("env", "magick", itemsRoot.resolve("stringworm.miff"), "-channel", "rgb", "-fx", expr, *jxlOpts, "jxl:$itemsRoot/stringworm_$name.png") 290 298 } 291 299 } 292 300 // people will hate this 293 301 for (i in 0..31) { 294 - itemsRoot.resolve("stringworm_tinted_$i.png").outputStream().use { 295 - exec { 296 - commandLine("env", "magick", itemsRoot.resolve("stringworm.miff"), "-fx", "i+j == $i ? u : Transparent", "png:-") 297 - standardOutput = it 298 - } 302 + exec { 303 + commandLine("env", "magick", itemsRoot.resolve("stringworm.miff"), "-fx", "i+j == $i ? u : Transparent", *jxlOpts, "jxl:${itemsRoot.resolve("stringworm_tinted_$i.png")}") 299 304 } 300 305 } 301 306 file("$itemsRoot/../block").mkdir() 302 307 exec { 303 - commandLine("env", "magick", "xc:#ffffff[16x16]", itemsRoot.resolveSibling("block/border.png")) 308 + commandLine("env", "magick", "xc:#ffffff[16x16]", *jxlOpts, "jxl:${itemsRoot.resolveSibling("block/border.png")}") 309 + } 310 + exec { 311 + commandLine("env", "magick", "xc:#000000[16x16]", *jxlOpts, "jxl:${itemsRoot.resolveSibling("block/void_air.png")}") 304 312 } 305 - //file("$itemsRoot/stringworm.miff").delete() 313 + file("$itemsRoot/stringworm.miff").delete() 306 314 exec { 307 - commandLine("env", "magick", "https://www.masterbuilt.com/cdn/shop/articles/162_20-_20Voodoo_20Baked_20Beans.jpg", "-sample", "256x256", itemsRoot.resolve("beans.png")) 315 + commandLine("env", "magick", "https://www.masterbuilt.com/cdn/shop/articles/162_20-_20Voodoo_20Baked_20Beans.jpg", "-sample", "256x256", *jxlOpts, "jxl:${itemsRoot.resolve("beans.png")}") 308 316 } 309 317 } 310 318
+34
project/hexic/changelog
··· 1 + 1.4.7-pre.2 add a pattern to force snow 2 + 1.4.7-pre.2 add demiplane binding, replacing the positional arg for shattering demiplanes 3 + 1.4.7-pre.2 allow re-enabling old Lani behavior in config 4 + 1.4.7-pre.2 fix crash with canon 0.11.3 versions 5 + 1.4.7-pre.2 fix list math breaking hexdoc 6 + 1.4.7-pre.2 fix void-air scan 7 + 1.4.7-pre.2 make demiplanes not save redundant chunks 8 + 1.4.7-pre.2 make greatspells actually greatspells 9 + 1.4.7-pre.2 meow 10 + 1.4.7-pre.2 remove internal error with out-of-bounds excisor's gambit 11 + 2.0.0 add casting pouch sealing 12 + 2.0.0 add Cessation and Resumption 13 + 2.0.0 add docs for plane-related patterns 14 + 2.0.0 add Hidden Sun's Nadir (blindness) 15 + 2.0.0 add view iotas (experimental!) 16 + 2.0.0 fix getting kicked if mediaweave sends an overly long message 17 + 2.0.0 load config from config/*.properties instead of config/jvm.properties 18 + 2.0.0 made demiplanes not instantly kill you and void your items if you look at them wrong 19 + 2.0.0 made demiplanes remember where they were entered from 20 + 2.0.0 made stringworms obtainable 21 + 2.0.0 mediaweave now applies to all chat messages 22 + 2.0.0 !mediaweave now uses trinket slots 23 + 2.0.0 murmur and reveal now use separate components 24 + 2.0.0 !only one mediaweave can be equipped at a time 25 + 2.0.0 removed map iotas → moved to phlib with identical behavior 26 + 2.0.0 !removed NBT iotas 27 + 2.0.0 removed patchwork iotas → moved to iotaworks with identical behavior 28 + 2.0.0 !removed tripwires 29 + 2.0.0 remove snow pattern & fix lithium incompatibility 30 + 2.0.0 !remove suffering 31 + 2.0.0 rename media bundles to casting pouches 32 + 2.0.0 sent Stickia to the milk dimension 33 + 2.0.0 textures now use JPEG XL 34 + 2.0.1 fix hitbox for void blocks
+8
project/hexic/gradle.properties
··· 1 + minecraft_version=1.20.1 2 + yarn_mappings=1.20.1+build.10 3 + loader_version=0.16.14 4 + scala_loader_version=0.3.1.11 5 + modid=hexic 6 + version=2.0.1 7 + py_version=1.0 8 + fabric_version=0.92.6+1.20.1
+3 -3
project/hexic/src/client/resources/hexic.client.mixins.json
··· 1 1 { 2 2 "required": true, 3 3 "minVersion": "0.8", 4 - "package": "org.eu.net.pool.hexic.mixin.client", 4 + "package": "org.eu.net.pool.hexic.mixin", 5 5 "compatibilityLevel": "JAVA_17", 6 6 "client": [ 7 7 "ChatHudMixin", 8 - "ChatInputSuggestorMixin", 9 8 "ChatScreenAccess", 10 9 "ChatScreenMixin", 11 - "InventoryScreenMixin" 10 + "EntityRendererDispatcher", 11 + "PlayerEntityRendererMixin" 12 12 ], 13 13 "injectors": { 14 14 "defaultRequire": 1
+35 -69
project/hexic/src/client/scala/org/eu/net/pool/hexic/client/main.scala project/hexic/src/client/scala/org/eu/net/pool/hexic/client.scala
··· 1 1 package org.eu.net.pool 2 2 package hexic 3 - package client 4 3 5 4 import at.petrak.hexcasting.api.item.PigmentItem 6 5 import at.petrak.hexcasting.api.mod.HexTags ··· 8 7 import com.google.gson.reflect.TypeToken 9 8 import com.google.gson.{Gson, JsonArray, JsonObject} 10 9 import com.llamalad7.mixinextras.injector.wrapoperation.Operation 10 + import dev.emi.trinkets.api.{TrinketComponent, TrinketsApi} 11 11 import kotlin.jvm.JvmField 12 12 import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking 13 13 import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry ··· 22 22 import net.minecraft.client.gui.screen.ChatScreen 23 23 import net.minecraft.client.gui.widget.TextFieldWidget 24 24 import net.minecraft.client.network.{ClientPlayNetworkHandler, ClientPlayerEntity} 25 - import net.minecraft.client.render.{BufferBuilder, RenderLayer, RenderLayers, VertexConsumer, VertexConsumerProvider, VertexFormat} 26 25 import net.minecraft.client.render.block.entity.{BlockEntityRenderer, BlockEntityRendererFactories} 27 26 import net.minecraft.client.render.model.json 27 + import net.minecraft.client.render.* 28 28 import net.minecraft.client.texture.Sprite 29 29 import net.minecraft.client.util.math.MatrixStack 30 - import net.minecraft.data.client.{BlockStateModelGenerator, ItemModelGenerator, ModelIds, Models, TextureKey, TextureMap} 30 + import net.minecraft.data.client.* 31 31 import net.minecraft.data.server.recipe.{RecipeJsonProvider, ShapedRecipeJsonBuilder} 32 32 import net.minecraft.entity.player.PlayerEntity 33 33 import net.minecraft.inventory.Inventory ··· 36 36 import net.minecraft.registry.{MutableRegistry, Registries, RegistryKeys, RegistryWrapper} 37 37 import net.minecraft.screen.slot.Slot 38 38 import net.minecraft.text.{CharacterVisitor, OrderedText, Style} 39 - import net.minecraft.util.{DyeColor, Identifier} 40 39 import net.minecraft.util.collection.DefaultedList 41 40 import net.minecraft.util.math.{Direction, MathHelper, Vec3d} 41 + import net.minecraft.util.{DyeColor, Identifier} 42 42 43 43 import java.io.{InputStreamReader, Reader} 44 44 import java.util.function.Consumer ··· 58 58 case null => default 59 59 case player => ifPresent(player) 60 60 61 - object Hooks: 62 - def provideRenderText(string: String, firstCharacterIndex: Int, field: TextFieldWidget, original: OrderedText): OrderedText = 63 - foldLocalPlayer(original): p => 64 - val c = p.getComponent(PlayerInfoComponent.key) 65 - boundary[OrderedText]: 66 - if c.rightWeave.hasCustomName && c.rightWeave.getItem.isInstanceOf[Mediaweave] then 67 - val wake = c.rightWeave.getName.getString.toLowerCase 68 - if field.getText.toLowerCase.startsWith(s"$wake:") then 69 - boundary.break[OrderedText]: v => 70 - original.accept: (idx, style, p) => 71 - v.accept(idx, if idx + firstCharacterIndex <= wake.length then style.withColor(c.rightWeave.getItem.asInstanceOf[Mediaweave].color.getSignColor) else style, p) 72 - if c.leftWeave.hasCustomName && c.leftWeave.getItem.isInstanceOf[Mediaweave] then 73 - val wake = c.leftWeave.getName.getString.toLowerCase 74 - if field.getText.toLowerCase.startsWith(s"$wake:") then 75 - boundary.break[OrderedText]: v => 76 - original.accept: (idx, style, p) => 77 - v.accept(idx, if idx + firstCharacterIndex <= wake.length then style.withColor(c.leftWeave.getItem.asInstanceOf[Mediaweave].color.getSignColor) else style, p) 78 - original 61 + object ClientHooks: 79 62 def interceptSendMessage(handler: ClientPlayNetworkHandler, msg: String): Boolean = 80 63 foldLocalPlayer(false): p => 81 - boundary[Boolean]: 82 - val c = p.getComponent(PlayerInfoComponent.key) 83 - val (left, text) = boundary[(Boolean, String)]: 84 - if c.rightWeave.hasCustomName && c.rightWeave.getItem.isInstanceOf[Mediaweave] then 85 - val wake = c.rightWeave.getName.getString.toLowerCase 86 - if msg.toLowerCase.startsWith(s"$wake:") then 87 - boundary.break((false, msg.substring(wake.length + 1))) 88 - if c.leftWeave.hasCustomName && c.leftWeave.getItem.isInstanceOf[Mediaweave] then 89 - val wake = c.leftWeave.getName.getString.toLowerCase 90 - if msg.toLowerCase.startsWith(s"$wake:") then 91 - boundary.break((true, msg.substring(wake.length + 1))) 92 - boundary.break(false) 64 + if p.validMediaweave.isDefined then 93 65 val buf = PacketByteBufs.create() 94 - buf.writeByte(if left then 12 else 8) 95 - buf.writeString(text.trim) 96 - ClientPlayNetworking.send("sync_mediaweave", buf) 66 + buf.writeByte(0) 67 + buf.writeString(msg) 68 + ClientPlayNetworking.send("message", buf) 97 69 true 70 + else false 98 71 99 72 def init(): Unit = 100 73 BlockEntityRendererFactories.register( ··· 136 109 val preferredColor = DyeColor.values()(client.getSession.getUuidOrNull.getLeastSignificantBits.abs.%(16).toInt) 137 110 val preferredStringworm = stringworms(Stringworm.flavors(client.getSession.getUuidOrNull.getLeastSignificantBits.abs.%(48).*(7).%(4).toInt)) 138 111 val preferredMediaweave = Mediaweave.colors(preferredColor) 139 - val preferredPen = Pen(preferredColor) 112 + val preferredPen = Pen.instances(preferredColor) 140 113 val preferredPouch = memo(MediaBundle(preferredColor, _)) 141 114 { 142 115 case (Registries.ITEM, id) if id == ("preferred_mediaweave": Identifier) => preferredMediaweave ··· 148 121 ClientPlayNetworking.registerGlobalReceiver("msg", (_, handler, buf, _) => 149 122 val s = buf.readString 150 123 if s.startsWith("/") then 151 - handler.sendChatCommand(s) 124 + handler.sendChatCommand(s.drop(1)) 152 125 else 153 126 handler.sendChatMessage(s)) 154 127 ··· 258 231 new FabricModelProvider(_): 259 232 override def generateBlockStateModels(gen: BlockStateModelGenerator): Unit = 260 233 gen.registerSimpleCubeAll(Registries.BLOCK("border")) 234 + gen.registerSimpleCubeAll(Registries.BLOCK("void_air")) 261 235 gen.registerSimpleState(Registries.BLOCK("chisel_table")) 262 236 gen.modelCollector.accept(ModelIds.getBlockModelId(Registries.BLOCK("chisel_table")), () => 263 237 new JsonObject().tap: j => ··· 318 292 pack.addProvider: 319 293 new FabricLanguageProvider(_): 320 294 override def generateTranslations(gen: FabricLanguageProvider.TranslationBuilder): Unit = 321 - for (action, name) <- Vector( 295 + for action -> name <- Vector( 296 + "attachworld" -> "Bind Demiplane", 297 + "blind" -> "Hidden Sun's Nadir", 322 298 "deleteworld" -> "Shatter Demiplane", 323 299 "drop" -> "Rejection Distillation", 324 300 "dye_offhand" -> "Apply Pigment", 325 301 "erase" -> "Erase Block", 326 302 "extract" -> "Excisor's Gambit", 303 + "findview" -> "Reflection Purification", 327 304 "fox" -> "Vulpine Gambit", 328 305 "free" -> "Deallocator's Gambit", 329 306 "get_other_caster" -> "Dual's Reflection", 330 307 "grep" -> "Refinement Distillation", 331 - "jvm/class_of_iota" -> "Classifier Purification II", 332 - "jvm/class_of_payload" -> "Classifier Purification I", 333 - "jvm/newinstance_boxed" -> "Constructor Purification I", 334 - "jvm/newinstance_unboxed" -> "Constructor Purification II", 335 308 "make_cme" -> "Thoth's Pseudogambit", 336 309 "makeworld" -> "Conjure Demiplane", 337 310 "malloc" -> "Allocator's Purification", 338 311 "modulo" -> "Modulus Distillation II", 339 - "nbt/deserialize" -> "Importer's Purification", 340 - "nbt/lift1" -> "Secretary's Purification: Byte", 341 - "nbt/lift2" -> "Secretary's Purification: Short", 342 - "nbt/lift4" -> "Secretary's Purification: Integer", 343 - "nbt/lift8" -> "Secretary's Purification: Long", 344 - "nbt/liftd" -> "Secretary's Purification: Double", 345 - "nbt/liftf" -> "Secretary's Purification: Float", 346 - "nbt/literal/array1" -> "Secretary's Reflection: Vacant Byte Array", 347 - "nbt/literal/array2" -> "Secretary's Reflection: Vacant Integer Array", 348 - "nbt/literal/array4" -> "Secretary's Reflection: Vacant Long Array", 349 - "nbt/literal/collection" -> "Secretary's Reflection: Collection", 350 - "nbt/literal/list" -> "Secretary's Reflection: Vacant List", 351 - "nbt/serialize" -> "Exporter's Purification", 312 + "moveconcept" -> "Transfer Substance", 313 + "moveentity" -> "Transfer Creature", 314 + "murmur" -> "Murmur Reflection", 315 + "omni_close" -> "Cessation", 316 + "omni_open" -> "Resumption", 352 317 "reveal" -> "Greater Reveal", 353 318 "rotate" -> "Ferris Distillation", 354 - "snow" -> "Summon Snow", 355 319 "staffcast_factory" -> "Lani's Greater Gambit", 356 320 "staffcast_factory/lazy" -> "Lani's Lesser Gambit", 357 321 "take" -> "Retention Distillation", 358 - "tripwire" -> "Tripwire Reflection", 359 322 "unfox" -> "Vulpine Expulsion", 360 - "whatthefuck" -> "Suffering", 361 323 "where" -> "Deductive Purification", 362 324 ) do gen.add(s"hexcasting.action.hexic:$action", name) 363 325 gen.add("hexcasting.special.hexic:tuple", "Coupler's Gambit") 364 326 gen.add("hexcasting.special.hexic:tuple.n", "Coupler's Gambit %s") 365 - for (klass, name) <- Vector( 327 + for klass ->name<- Vector( 366 328 "erase" -> "an item entity or vector", 367 329 "int_or_list" -> "§aint§r or §5[§aint§5]§r", 368 330 ) do gen.add(s"hexcasting.mishap.invalid_value.class.hexic:$klass", name) 369 - for (ty, name) <- Vector( 331 + for ty ->name<- Vector( 370 332 "nbt" -> "Tag", 371 - "tripwire" -> "Tripwire", 372 333 "variant" -> "Concept", 373 334 ) do gen.add(s"hexcasting.iota.hexic:$ty", name) 374 335 gen.add("hexcasting.mishap.bad_block.hexic:erase", "a block holding a casting item or acting as an iota holder") ··· 385 346 gen.add(item, s"Stringworm") 386 347 for item <- MediaBundle.items do 387 348 gen.add(item, item.size match 388 - case 6 => s"${item.color.humanName} Media Pouch" 389 - case 12 => s"Large ${item.color.humanName} Media Pouch" 349 + case 6 => s"${item.color.humanName} Casting Pouch" 350 + case 12 => s"Large ${item.color.humanName} Casting Pouch" 390 351 case s => throw IllegalStateException(s"Unhandled bundle size $s")) 391 352 gen.add(wizard, "Wizard") 392 353 val hexLang = Seq("hexcasting", "oneironaut").flatMap(mod => Gson().fromJson(InputStreamReader(getClass.getResourceAsStream(s"/assets/$mod/lang/en_us.json")), new TypeToken[java.util.Map[String, String]]() {}).asScala).toMap 393 354 Registries.ITEM.forEach: 394 355 case p: PigmentItem => gen.add("item.hexic.stringworm." + p.getTranslationKey, "Shimmering " + hexLang(p.getTranslationKey).replace("Pigment", "Stringworm")) 395 356 case e => println(e) 396 - gen.add("book.hexic.page.dye_offhand", "Imbues the item held in my offhand (e.g. a $(l:items/hexcasting)$(item)casting item/$) with the given pigment.") 397 - gen.add("book.hexic.page.erase", "Erases the _Hex or iota contained within a dropped item or block. Costs one dust per item.") 398 - gen.add("book.hexic.page.get_other_caster", "Adds the closest sentient being, excluding me, to the stack.") 399 - gen.add("book.hexic.page.modulo", "Similar to Modulus, but differs for negative numbers: -8 %%₁ 3 = -2, but -8 %%₂ 3 = 1.") 357 + for page -> text <- Vector( 358 + "dye_offhand" -> "Imbues the item held in my offhand (e.g. a $(l:items/hexcasting)$(item)casting item/$) with the given pigment.", 359 + "erase" -> "Erases the _Hex or iota contained within a dropped item or block. Costs one dust per item.", 360 + "get_other_caster" -> "Adds the closest sentient being, excluding me, to the stack.", 361 + "modulo" -> "Similar to Modulus, but differs for negative numbers: -8 %%₁ 3 = -2, but -8 %%₂ 3 = 1.", 362 + "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.", 363 + "blind" -> "Inflicts $(thing)blindness/$. Base cost is one $(l:items/amethyst)$(item)Amethyst Dust/$ per 10 seconds.", 364 + ) do gen.add(s"book.hexic.page.$page", text) 400 365 gen.add("hexdoc.hexic.description", "Miscellaneous neat features and QoL patterns for Hex Casting") 401 366 gen.add("hexdoc.hexic.title", "Hexic") 402 367 gen.add("hexic.media.external", "Media") ··· 407 372 gen.add("hexic.spell_memory", "Hex memorized") 408 373 gen.add("tag.item.hexic.mediaweaves", "Mediaweave") 409 374 gen.add("text.hexic.pigment_holder_item", "an item storing a pigment") 375 + gen.add("trinkets.slot.chest.hexic_mediaweave", "Mediaweave") 410 376 pack.addProvider: 411 377 new FabricRecipeProvider(_): 412 378 override def generate(consumer: Consumer[RecipeJsonProvider]): Unit =
+27
project/hexic/src/client/scala/org/eu/net/pool/hexic/mixin/EntityRendererDispatcher.java
··· 1 + package org.eu.net.pool.hexic.mixin; 2 + 3 + import com.llamalad7.mixinextras.sugar.Local; 4 + import com.llamalad7.mixinextras.sugar.Share; 5 + import com.llamalad7.mixinextras.sugar.ref.LocalRef; 6 + import net.minecraft.client.render.VertexConsumerProvider; 7 + import net.minecraft.client.render.entity.EntityRenderDispatcher; 8 + import net.minecraft.client.util.math.MatrixStack; 9 + import net.minecraft.entity.Entity; 10 + import net.minecraft.entity.LivingEntity; 11 + import net.minecraft.entity.passive.CatEntity; 12 + import org.eu.net.pool.hexic.CatHolder; 13 + import org.spongepowered.asm.mixin.Mixin; 14 + import org.spongepowered.asm.mixin.injection.At; 15 + import org.spongepowered.asm.mixin.injection.Inject; 16 + import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 17 + 18 + @Mixin(EntityRenderDispatcher.class) 19 + public class EntityRendererDispatcher { 20 + @Inject( 21 + method = "render", at = @At("HEAD") 22 + ) 23 + private void replaceYouWithCat(Entity entity, double x, double y, double z, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, CallbackInfo ci, @Local(argsOnly = true) LocalRef<Entity> entityRef) { 24 + LivingEntity disguise = (CatEntity) CatHolder.getSyncedCat(entity); 25 + if (disguise != null) entityRef.set(disguise); 26 + } 27 + }
+22
project/hexic/src/client/scala/org/eu/net/pool/hexic/mixin/PlayerEntityRendererMixin.java
··· 1 + package org.eu.net.pool.hexic.mixin; 2 + 3 + import net.minecraft.client.model.ModelPart; 4 + import net.minecraft.client.network.AbstractClientPlayerEntity; 5 + import net.minecraft.client.render.VertexConsumerProvider; 6 + import net.minecraft.client.render.entity.PlayerEntityRenderer; 7 + import net.minecraft.client.util.math.MatrixStack; 8 + import org.eu.net.pool.hexic.CatHolder; 9 + import org.spongepowered.asm.mixin.Mixin; 10 + import org.spongepowered.asm.mixin.injection.At; 11 + import org.spongepowered.asm.mixin.injection.Inject; 12 + import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 13 + 14 + // rephrased from trickster 15 + @Mixin(PlayerEntityRenderer.class) 16 + public class PlayerEntityRendererMixin { 17 + @Inject(method = "renderArm", at = @At("HEAD"), cancellable = true) 18 + private void stealsYourArmsCutely(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, 19 + AbstractClientPlayerEntity player, ModelPart arm, ModelPart sleeve, CallbackInfo ci) { 20 + if (CatHolder.getCat(player) != null) ci.cancel(); 21 + } 22 + }
-22
project/hexic/src/client/scala/org/eu/net/pool/hexic/mixin/client/ChatInputSuggestorMixin.java
··· 1 - package org.eu.net.pool.hexic.mixin.client; 2 - 3 - import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; 4 - import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 5 - import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 6 - import net.minecraft.client.gui.screen.ChatInputSuggestor; 7 - import net.minecraft.client.gui.widget.TextFieldWidget; 8 - import net.minecraft.text.OrderedText; 9 - import org.eu.net.pool.hexic.client.Hooks; 10 - import org.spongepowered.asm.mixin.Final; 11 - import org.spongepowered.asm.mixin.Mixin; 12 - import org.spongepowered.asm.mixin.Shadow; 13 - 14 - @Mixin(ChatInputSuggestor.class) 15 - public class ChatInputSuggestorMixin { 16 - @Shadow @Final TextFieldWidget textField; 17 - 18 - @WrapMethod(method = "provideRenderText") 19 - OrderedText provideRenderText(String string, int firstCharacterIndex, Operation<OrderedText> original) { 20 - return Hooks.provideRenderText(string, firstCharacterIndex, textField, original.call(string, firstCharacterIndex)); 21 - } 22 - }
+3 -3
project/hexic/src/client/scala/org/eu/net/pool/hexic/mixin/client/ChatScreenMixin.java project/hexic/src/client/scala/org/eu/net/pool/hexic/mixin/ChatScreenMixin.java
··· 1 - package org.eu.net.pool.hexic.mixin.client; 1 + package org.eu.net.pool.hexic.mixin; 2 2 3 3 import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 4 4 import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 5 5 import net.minecraft.client.gui.screen.ChatScreen; 6 6 import net.minecraft.client.network.ClientPlayNetworkHandler; 7 - import org.eu.net.pool.hexic.client.Hooks; 7 + import org.eu.net.pool.hexic.ClientHooks; 8 8 import org.spongepowered.asm.mixin.Mixin; 9 9 import org.spongepowered.asm.mixin.injection.At; 10 10 ··· 12 12 public class ChatScreenMixin { 13 13 @WrapOperation(method = "sendMessage", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;sendChatMessage(Ljava/lang/String;)V")) 14 14 void sendChatMessage(ClientPlayNetworkHandler instance, String content, Operation<Void> original) { 15 - if (!Hooks.interceptSendMessage(instance, content)) original.call(instance, content); 15 + if (!ClientHooks.interceptSendMessage(instance, content)) original.call(instance, content); 16 16 } 17 17 }
-87
project/hexic/src/client/scala/org/eu/net/pool/hexic/mixin/client/InventoryScreenMixin.java
··· 1 - package org.eu.net.pool.hexic.mixin.client; 2 - 3 - import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 4 - import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 5 - import net.fabricmc.fabric.impl.networking.client.ClientPlayNetworkAddon; 6 - import net.fabricmc.loader.api.FabricLoader; 7 - import net.fabricmc.loader.impl.launch.FabricLauncher; 8 - import net.minecraft.client.gui.screen.ingame.AbstractInventoryScreen; 9 - import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; 10 - import net.minecraft.client.gui.screen.ingame.InventoryScreen; 11 - import net.minecraft.entity.player.PlayerInventory; 12 - import net.minecraft.item.ItemStack; 13 - import net.minecraft.screen.PlayerScreenHandler; 14 - import net.minecraft.screen.ScreenHandler; 15 - import net.minecraft.text.Text; 16 - import net.minecraft.util.Identifier; 17 - import org.eu.net.pool.hexic.PlayerInfoComponent; 18 - import org.spongepowered.asm.mixin.Mixin; 19 - import org.spongepowered.asm.mixin.Unique; 20 - import org.spongepowered.asm.mixin.injection.At; 21 - import org.spongepowered.asm.mixin.injection.Inject; 22 - import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 23 - 24 - @Mixin({InventoryScreen.class, CreativeInventoryScreen.class}) 25 - public abstract class InventoryScreenMixin extends AbstractInventoryScreen<ScreenHandler> { 26 - public InventoryScreenMixin(ScreenHandler screenHandler, PlayerInventory playerInventory, Text text) { 27 - super(screenHandler, playerInventory, text); 28 - } 29 - 30 - @Inject(method = "mouseClicked", at = @At(value = "HEAD"), cancellable = true) 31 - void mouseClicked(double mouseX, double mouseY, int button, CallbackInfoReturnable<Boolean> cir) { 32 - boolean creative = false; 33 - if ((AbstractInventoryScreen<?>) this instanceof CreativeInventoryScreen c) { 34 - if (!c.isInventoryTabSelected()) return; 35 - creative = true; 36 - } 37 - mouseX -= x; 38 - mouseY -= y; 39 - double lx = creative ? mouseX - 89 : mouseX - 50; 40 - double ly = creative ? mouseY - 42 : mouseY - 65; 41 - double w = creative ? 15 : 20; 42 - double h = creative ? 35 : 60; 43 - if (FabricLoader.getInstance().isDevelopmentEnvironment()) 44 - System.out.printf("s=(%d, %d) m=(%f, %f) l=(%f, %f)\n", x, y, mouseX, mouseY, lx, ly); 45 - if (lx < w && lx > -w && ly < 0 && ly > -h) { 46 - byte flags = 0; 47 - var c = client.player.getComponent(PlayerInfoComponent.key()); 48 - var held = handler.getCursorStack(); 49 - if (lx < 0) { 50 - var right = c.rightWeave(); 51 - if (right.isEmpty() && !held.isEmpty() && held.getItem() instanceof org.eu.net.pool.hexic.Mediaweave) { 52 - c.rightWeave_$eq(held); 53 - handler.setCursorStack(ItemStack.EMPTY); 54 - flags |= 3; 55 - } else if (held.isEmpty() && !right.isEmpty()) { 56 - handler.setCursorStack(right); 57 - c.rightWeave_$eq(ItemStack.EMPTY); 58 - flags |= 3; 59 - } else return; 60 - } else { 61 - var left = c.leftWeave(); 62 - if (left.isEmpty() && !held.isEmpty() && held.getItem() instanceof org.eu.net.pool.hexic.Mediaweave) { 63 - c.leftWeave_$eq(held); 64 - handler.setCursorStack(ItemStack.EMPTY); 65 - flags |= 7; 66 - } else if (held.isEmpty() && !left.isEmpty()) { 67 - handler.setCursorStack(left); 68 - c.leftWeave_$eq(ItemStack.EMPTY); 69 - flags |= 7; 70 - } else return; 71 - } 72 - // submit our changes to the server 73 - if (flags != 0) { 74 - var buf = PacketByteBufs.create(); 75 - buf.writeByte(flags); 76 - if ((flags & 1) != 0) buf.writeItemStack(handler.getCursorStack()); 77 - if ((flags & 2) != 0) 78 - if ((flags & 4) != 0) 79 - buf.writeItemStack(c.leftWeave()); 80 - else 81 - buf.writeItemStack(c.rightWeave()); 82 - ClientPlayNetworking.send(Identifier.of("hexic", "sync_mediaweave"), buf); 83 - cir.setReturnValue(true); 84 - } 85 - } 86 - } 87 - }
+2 -3
project/hexic/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/hexic/media_bundle.json
··· 1 - 2 1 { 3 - "name": "Media Pouches", 2 + "name": "Casting Pouches", 4 3 "category": "hexcasting:items", 5 4 "icon": "hexic:small_preferred_bundle", 6 5 "sortnum": 1, 7 6 "pages": [ 8 7 "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.", 9 - "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.", 8 + "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.", 10 9 { 11 10 "type": "patchouli:text", 12 11 "advancement": "hexcasting:enlightenment",
+2 -1
project/hexic/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/hexic/mediaweave.json
··· 4 4 "icon": "hexic:preferred_mediaweave", 5 5 "advancement": "hexcasting:root", 6 6 "pages": [ 7 - "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." 7 + "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.", 8 + "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." 8 9 ] 9 10 }
-58
project/hexic/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/hexic/nbt.json
··· 1 - { 2 - "name": "Nature's Records", 3 - "category": "hexcasting:patterns", 4 - "icon": "minecraft:glow_ink_sac", 5 - "advancement": "hexcasting:enlightenment", 6 - "pages": [ 7 - "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.", 8 - "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.", 9 - { 10 - "type": "hexcasting:pattern", 11 - "anchor": "hexic:nbt/lift1", 12 - "op_id": "hexic:nbt/lift1", 13 - "input": "number", 14 - "output": "nbt", 15 - "text": "Converts a number into a Byte tag: an integer between -128 and 127, inclusive." 16 - }, 17 - { 18 - "type": "hexcasting:pattern", 19 - "anchor": "hexic:nbt/lift2", 20 - "op_id": "hexic:nbt/lift2", 21 - "input": "number", 22 - "output": "nbt", 23 - "text": "Converts a number into a Short tag, between -65536 and 65535." 24 - }, 25 - { 26 - "type": "hexcasting:pattern", 27 - "anchor": "hexic:nbt/lift4", 28 - "op_id": "hexic:nbt/lift4", 29 - "input": "number", 30 - "output": "nbt", 31 - "text": "Converts a number into an Int tag, between roughly negative 2 billion and positive 2 billion." 32 - }, 33 - { 34 - "type": "hexcasting:pattern", 35 - "anchor": "hexic:nbt/lift8", 36 - "op_id": "hexic:nbt/lift8", 37 - "input": "number", 38 - "output": "nbt", 39 - "text": "Converts a number into a Long tag: bounded to 9 quintillion, which I will likely never reach." 40 - }, 41 - { 42 - "type": "hexcasting:pattern", 43 - "anchor": "hexic:nbt/liftd", 44 - "op_id": "hexic:nbt/liftd", 45 - "input": "number", 46 - "output": "nbt", 47 - "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." 48 - }, 49 - { 50 - "type": "hexcasting:pattern", 51 - "anchor": "hexic:nbt/liftf", 52 - "op_id": "hexic:nbt/liftf", 53 - "input": "number", 54 - "output": "nbt", 55 - "text": "Converts a number into a Float tag. These seem similar to Doubles, albeit with less precision." 56 - } 57 - ] 58 - }
-17
project/hexic/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/hexic/no.json
··· 1 - { 2 - "name": "Nature's Revenge", 3 - "advancement": "hexcasting:enlightenment", 4 - "category": "hexcasting:patterns", 5 - "icon": "arrow", 6 - "pages": [ 7 - "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— ", 8 - { 9 - "type": "hexcasting:pattern", 10 - "op_id": "hexic:whatthefuck", 11 - "anchor": "hexic:whatthefuck", 12 - "input": "???", 13 - "output": "???", 14 - "text": "How could one even begin to draw this?" 15 - } 16 - ] 17 - }
+1 -1
project/hexic/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/hexic/stringworms.json
··· 4 4 "advancement": "hexcasting:root", 5 5 "icon": "hexic:preferred_stringworm", 6 6 "pages": [ 7 - "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." 7 + "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." 8 8 ] 9 9 }
+5 -5
project/hexic/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/hexic/world.json
··· 2 2 "name": "Demiplanes", 3 3 "advancement": "hexcasting:enlightenment", 4 4 "category": "hexcasting:patterns", 5 - "icon": "white_carpet", 5 + "icon": "minecraft:white_carpet", 6 6 "pages": [ 7 - "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/$", 7 + "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/$", 8 8 { 9 9 "type": "hexcasting:pattern", 10 10 "op_id": "hexic:makeworld", ··· 22 22 "output": "", 23 23 "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." 24 24 }, 25 - "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.", 25 + "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.", 26 26 { 27 27 "type": "hexcasting:pattern", 28 - "op_id": "hexic:attachworld", 29 - "anchor": "hexic:attachworld", 28 + "op_id": "hexic:deleteworld", 29 + "anchor": "hexic:deleteworld", 30 30 "input": "imprint, vec", 31 31 "output": "", 32 32 "text": "Ruptures the boundaries of the given demiplane, destroying its contents. All dropped items and experience are spewed at the plane's attachment point."
+2 -3
project/hexic/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/zh_cn/entries/addon/hexic/media_bundle.json
··· 1 - 2 1 { 3 - "name": "媒质口袋", 2 + "name": "施法口袋", 4 3 "category": "hexcasting:items", 5 4 "icon": "hexic:small_preferred_bundle", 6 5 "sortnum": 1, 7 6 "pages": [ 8 7 "$(l:items/phials)$(item)媒质之瓶/$是我在$(media)媒质/$存储研究上的终极目标,不过,用$(l:addon/hexic/mediaweave)$(item)媒质布匹/$编织出的口袋也可以协助我管理紫水晶。要的布匹数量不多,造出的口袋容量却不小。每个口袋都有六个槽位,再花少量布匹可以扩充到十二个。小号口袋可以放在大号口袋里面,但两者都不接受同尺寸的口袋。", 9 - "在自然要求支付$(media)媒质/$时,口袋的内容物会优先消耗。与之类似,对口袋$(l:patterns/spells/hexcasting#hexcasting:recharge)$(action)重新充能/$时,充入的$(media)媒质/$会在口袋内形成某种“云”,而后就将凝集在口袋内的任意$(l:items/hexcasting)$(item)缀品/$及其类似物。", 8 + "在自然要求支付$(media)媒质/$时,口袋的内容物会优先消耗。与之类似,对口袋$(l:patterns/spells/hexcasting#hexcasting:recharge)$(action)重新充能/$时,充入的$(media)媒质/$会在口袋内形成某种“云”,而后就将凝集在口袋内的任意$(l:items/hexcasting)$(item)缀品/$及其类似物。如需禁用此行为,可用$(item)蜜脾/$密封施法口袋,操作方法与放入物品一致。而后可用湿海绵以同种方法洗去密封层。", 10 9 { 11 10 "type": "patchouli:text", 12 11 "advancement": "hexcasting:enlightenment",
-58
project/hexic/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/zh_cn/entries/addon/hexic/nbt.json
··· 1 - { 2 - "name": "自然的记录", 3 - "category": "hexcasting:patterns", 4 - "icon": "minecraft:glow_ink_sac", 5 - "advancement": "hexcasting:enlightenment", 6 - "pages": [ 7 - "我的研究向我揭示了这个世界中现实之纤维的方方面面。自然的记录表现为多个类型的“标签”构成的递归式结构。我在后面几页记述了它们。虽然有些类型在其他类型面前像是冗余,我也必须时刻小心,要记住它们不可随意互换。而因为我操纵的是自然的记录本身,犯下的错可能会远超所谓“致命”的范畴。", 8 - "如果万事万物都能简化成标签……那我是什么?我的存在难道也能简化成一堆数据?不过这起码让我安了心,我先前牺牲的所有村民本质上没有它们自己的生命……那我呢?我是什么?我现在在这本书上写下的字,难道只是一串零和一?$(2br)我最好别想得太多。我能随意操控万事万物的所有侧面,想太多只会让这种能力变得无趣。", 9 - { 10 - "type": "hexcasting:pattern", 11 - "anchor": "hexic:nbt/lift1", 12 - "op_id": "hexic:nbt/lift1", 13 - "input": "number", 14 - "output": "nbt", 15 - "text": "将数转变成字节标签:在 -128 到 127 之间(两端闭)的整数。" 16 - }, 17 - { 18 - "type": "hexcasting:pattern", 19 - "anchor": "hexic:nbt/lift2", 20 - "op_id": "hexic:nbt/lift2", 21 - "input": "number", 22 - "output": "nbt", 23 - "text": "将数转变成短整型标签:在 -65536 到 65535 之间。" 24 - }, 25 - { 26 - "type": "hexcasting:pattern", 27 - "anchor": "hexic:nbt/lift4", 28 - "op_id": "hexic:nbt/lift4", 29 - "input": "number", 30 - "output": "nbt", 31 - "text": "将数转变成整型标签:在大约负 20 亿到正 20 亿之间。" 32 - }, 33 - { 34 - "type": "hexcasting:pattern", 35 - "anchor": "hexic:nbt/lift8", 36 - "op_id": "hexic:nbt/lift8", 37 - "input": "number", 38 - "output": "nbt", 39 - "text": "将数转变成长整型标签:范围约是 900 京,大概率永远都超不出去。" 40 - }, 41 - { 42 - "type": "hexcasting:pattern", 43 - "anchor": "hexic:nbt/liftd", 44 - "op_id": "hexic:nbt/liftd", 45 - "input": "number", 46 - "output": "nbt", 47 - "text": "将数转变为双精度浮点型标签。奇怪的是,双精度浮点型似乎和我对数的认知完全一致,而且还有表示无限和非数的能力。" 48 - }, 49 - { 50 - "type": "hexcasting:pattern", 51 - "anchor": "hexic:nbt/liftf", 52 - "op_id": "hexic:nbt/liftf", 53 - "input": "number", 54 - "output": "nbt", 55 - "text": "将数转变为单精度浮点型标签。它们和双精度浮点型很相似,但其精度较低。" 56 - } 57 - ] 58 - }
-17
project/hexic/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/zh_cn/entries/addon/hexic/no.json
··· 1 - { 2 - "name": "自然的复仇", 3 - "advancement": "hexcasting:enlightenment", 4 - "category": "hexcasting:patterns", 5 - "icon": "arrow", 6 - "pages": [ 7 - "不、不、不。我——我还觉得“可怕的东西”已经够糟了——没法从意识中消除——不——不——好痛——必须绘制——画不出来——我都做了什么——自然为什么要这样——", 8 - { 9 - "type": "hexcasting:pattern", 10 - "op_id": "hexic:whatthefuck", 11 - "anchor": "hexic:whatthefuck", 12 - "input": "???", 13 - "output": "???", 14 - "text": "到底要怎么画啊?" 15 - } 16 - ] 17 - }
+10
project/hexic/src/main/resources/assets/hexic/jsonpatch/nadirs.jsonpatch
··· 1 + @version "1"; 2 + @target "hexcasting:patchouli_books/thehexbook/en_us/entries/patterns/spells/nadirs.json"; 3 + $pages = arrays.push($pages, { 4 + type: "hexcasting:pattern", 5 + op_id: "hexic:blind", 6 + anchor: "hexic:blind", 7 + input: "entity, number", 8 + output: "", 9 + text: "book.hexic.page.blind" 10 + });
+43 -54
project/hexic/src/main/resources/assets/hexic/lang/zh_cn.json
··· 3 3 "book.hexic.page.erase": "清除物品实体或方块中的$(hex)咒术/$或 iota。每个物品消耗 1 个紫水晶粉。", 4 4 "book.hexic.page.get_other_caster": "将离我最近的有智慧存在压入栈,我自己不计入统计范围。", 5 5 "book.hexic.page.modulo": "与余数之馏化类似,但两者在负数上的表现不同:-8 %%₁ 3 = -2,而 -8 %%₂ 3 = 1。", 6 + "book.hexic.page.murmur": "找到我意识中名为“聊天栏”的区域,将其内容压栈;无法找到则返回 Null。", 7 + "book.hexic.page.blind": "给予$(thing)失明/$。每 10 秒持续时间的基础消耗为 1 个$(l:items/amethyst)$(item)紫水晶粉/$。", 8 + "hexcasting.action.hexic:blind": "隐阳西沉", 6 9 "hexcasting.action.hexic:deleteworld": "击碎半位面", 7 10 "hexcasting.action.hexic:drop": "拒斥之馏化", 8 11 "hexcasting.action.hexic:dye_offhand": "应用染色剂", 9 12 "hexcasting.action.hexic:erase": "清除方块", 10 13 "hexcasting.action.hexic:extract": "切除器之策略", 14 + "hexcasting.action.hexic:findview": "精思之纯化", 11 15 "hexcasting.action.hexic:fox": "狐狸之策略", 12 16 "hexcasting.action.hexic:free": "释放器之策略", 13 17 "hexcasting.action.hexic:get_other_caster": "对偶之精思", 14 18 "hexcasting.action.hexic:grep": "精炼之馏化", 15 - "hexcasting.action.hexic:jvm/class_of_iota": "判类器之纯化,第二型", 16 - "hexcasting.action.hexic:jvm/class_of_payload": "判类器之纯化,第一型", 17 - "hexcasting.action.hexic:jvm/newinstance_boxed": "构造器之纯化,第一型", 18 - "hexcasting.action.hexic:jvm/newinstance_unboxed": "构造器之纯化,第二型", 19 19 "hexcasting.action.hexic:make_cme": "托特之伪策略", 20 20 "hexcasting.action.hexic:makeworld": "构筑半位面", 21 21 "hexcasting.action.hexic:malloc": "分配器之纯化", 22 22 "hexcasting.action.hexic:modulo": "余数之馏化,第二型", 23 - "hexcasting.action.hexic:nbt/deserialize": "导入器之纯化", 24 - "hexcasting.action.hexic:nbt/lift1": "秘书之纯化:字节", 25 - "hexcasting.action.hexic:nbt/lift2": "秘书之纯化:短整型", 26 - "hexcasting.action.hexic:nbt/lift4": "秘书之纯化:整型", 27 - "hexcasting.action.hexic:nbt/lift8": "秘书之纯化:长整型", 28 - "hexcasting.action.hexic:nbt/liftd": "秘书之纯化:双精度浮点型", 29 - "hexcasting.action.hexic:nbt/liftf": "秘书之纯化:单精度浮点型", 30 - "hexcasting.action.hexic:nbt/literal/array1": "秘书之精思:空字节数组", 31 - "hexcasting.action.hexic:nbt/literal/array2": "秘书之精思:空整型数组", 32 - "hexcasting.action.hexic:nbt/literal/array4": "秘书之精思:空长整型数组", 33 - "hexcasting.action.hexic:nbt/literal/collection": "秘书之精思:容器", 34 - "hexcasting.action.hexic:nbt/literal/list": "秘书之精思:空列表", 35 - "hexcasting.action.hexic:nbt/serialize": "导出器之纯化", 23 + "hexcasting.action.hexic:moveconcept": "转运物质", 24 + "hexcasting.action.hexic:moveentity": "转运生物", 25 + "hexcasting.action.hexic:murmur": "私语之精思", 26 + "hexcasting.action.hexic:omni_close": "止咒", 27 + "hexcasting.action.hexic:omni_open": "继咒", 36 28 "hexcasting.action.hexic:reveal": "卓越揭示", 37 29 "hexcasting.action.hexic:rotate": "摩天轮之馏化", 38 - "hexcasting.action.hexic:snow": "召雪", 39 30 "hexcasting.action.hexic:staffcast_factory": "Lani之卓越策略", 40 31 "hexcasting.action.hexic:staffcast_factory/lazy": "Lani之初等策略", 41 32 "hexcasting.action.hexic:take": "维持之馏化", 42 - "hexcasting.action.hexic:tripwire": "绊线之精思", 43 33 "hexcasting.action.hexic:unfox": "狐狸之驱逐", 44 - "hexcasting.action.hexic:whatthefuck": "苦痛", 45 34 "hexcasting.action.hexic:where": "演绎之纯化", 46 35 "hexcasting.special.hexic:tuple": "耦合器之策略", 47 36 "hexcasting.special.hexic:tuple.n": "耦合器之策略:%s", 48 37 "hexcasting.iota.hexic:nbt": "标签", 49 - "hexcasting.iota.hexic:tripwire": "绊线", 50 38 "hexcasting.iota.hexic:variant": "概念", 51 39 "hexcasting.mishap.bad_block.hexic:erase": "一个存有施法物品或用于存储iota的方块", 52 40 "hexcasting.mishap.bad_item.hexic:erase": "一个施法物品或iota存储器", ··· 67 55 "item.hexic.cyan_mediaweave": "青色媒质布匹", 68 56 "item.hexic.gray_mediaweave": "灰色媒质布匹", 69 57 "item.hexic.green_mediaweave": "绿色媒质布匹", 70 - "item.hexic.large_black_bundle": "大型黑色媒质口袋", 71 - "item.hexic.large_blue_bundle": "大型蓝色媒质口袋", 72 - "item.hexic.large_brown_bundle": "大型棕色媒质口袋", 73 - "item.hexic.large_cyan_bundle": "大型青色媒质口袋", 74 - "item.hexic.large_gray_bundle": "大型灰色媒质口袋", 75 - "item.hexic.large_green_bundle": "大型绿色媒质口袋", 76 - "item.hexic.large_light_blue_bundle": "大型淡蓝色媒质口袋", 77 - "item.hexic.large_light_gray_bundle": "大型淡灰色媒质口袋", 78 - "item.hexic.large_lime_bundle": "大型黄绿色媒质口袋", 79 - "item.hexic.large_magenta_bundle": "大型品红色媒质口袋", 80 - "item.hexic.large_orange_bundle": "大型橙色媒质口袋", 81 - "item.hexic.large_pink_bundle": "大型粉红色媒质口袋", 82 - "item.hexic.large_purple_bundle": "大型紫色媒质口袋", 83 - "item.hexic.large_red_bundle": "大型红色媒质口袋", 84 - "item.hexic.large_white_bundle": "大型白色媒质口袋", 85 - "item.hexic.large_yellow_bundle": "大型黄色媒质口袋", 58 + "item.hexic.large_black_bundle": "大型黑色施法口袋", 59 + "item.hexic.large_blue_bundle": "大型蓝色施法口袋", 60 + "item.hexic.large_brown_bundle": "大型棕色施法口袋", 61 + "item.hexic.large_cyan_bundle": "大型青色施法口袋", 62 + "item.hexic.large_gray_bundle": "大型灰色施法口袋", 63 + "item.hexic.large_green_bundle": "大型绿色施法口袋", 64 + "item.hexic.large_light_blue_bundle": "大型淡蓝色施法口袋", 65 + "item.hexic.large_light_gray_bundle": "大型淡灰色施法口袋", 66 + "item.hexic.large_lime_bundle": "大型黄绿色施法口袋", 67 + "item.hexic.large_magenta_bundle": "大型品红色施法口袋", 68 + "item.hexic.large_orange_bundle": "大型橙色施法口袋", 69 + "item.hexic.large_pink_bundle": "大型粉红色施法口袋", 70 + "item.hexic.large_purple_bundle": "大型紫色施法口袋", 71 + "item.hexic.large_red_bundle": "大型红色施法口袋", 72 + "item.hexic.large_white_bundle": "大型白色施法口袋", 73 + "item.hexic.large_yellow_bundle": "大型黄色施法口袋", 86 74 "item.hexic.light_blue_mediaweave": "淡蓝色媒质布匹", 87 75 "item.hexic.light_gray_mediaweave": "淡灰色媒质布匹", 88 76 "item.hexic.lime_mediaweave": "黄绿色媒质布匹", ··· 107 95 "item.hexic.pink_mediaweave": "粉红色媒质布匹", 108 96 "item.hexic.purple_mediaweave": "紫色媒质布匹", 109 97 "item.hexic.red_mediaweave": "红色媒质布匹", 110 - "item.hexic.small_black_bundle": "小型黑色媒质口袋", 111 - "item.hexic.small_blue_bundle": "小型蓝色媒质口袋", 112 - "item.hexic.small_brown_bundle": "小型棕色媒质口袋", 113 - "item.hexic.small_cyan_bundle": "小型青色媒质口袋", 114 - "item.hexic.small_gray_bundle": "小型灰色媒质口袋", 115 - "item.hexic.small_green_bundle": "小型绿色媒质口袋", 116 - "item.hexic.small_light_blue_bundle": "小型淡蓝色媒质口袋", 117 - "item.hexic.small_light_gray_bundle": "小型淡灰色媒质口袋", 118 - "item.hexic.small_lime_bundle": "小型黄绿色媒质口袋", 119 - "item.hexic.small_magenta_bundle": "小型品红色媒质口袋", 120 - "item.hexic.small_orange_bundle": "小型橙色媒质口袋", 121 - "item.hexic.small_pink_bundle": "小型粉红色媒质口袋", 122 - "item.hexic.small_purple_bundle": "小型紫色媒质口袋", 123 - "item.hexic.small_red_bundle": "小型红色媒质口袋", 124 - "item.hexic.small_white_bundle": "小型白色媒质口袋", 125 - "item.hexic.small_yellow_bundle": "小型黄色媒质口袋", 98 + "item.hexic.small_black_bundle": "小型黑色施法口袋", 99 + "item.hexic.small_blue_bundle": "小型蓝色施法口袋", 100 + "item.hexic.small_brown_bundle": "小型棕色施法口袋", 101 + "item.hexic.small_cyan_bundle": "小型青色施法口袋", 102 + "item.hexic.small_gray_bundle": "小型灰色施法口袋", 103 + "item.hexic.small_green_bundle": "小型绿色施法口袋", 104 + "item.hexic.small_light_blue_bundle": "小型淡蓝色施法口袋", 105 + "item.hexic.small_light_gray_bundle": "小型淡灰色施法口袋", 106 + "item.hexic.small_lime_bundle": "小型黄绿色施法口袋", 107 + "item.hexic.small_magenta_bundle": "小型品红色施法口袋", 108 + "item.hexic.small_orange_bundle": "小型橙色施法口袋", 109 + "item.hexic.small_pink_bundle": "小型粉红色施法口袋", 110 + "item.hexic.small_purple_bundle": "小型紫色施法口袋", 111 + "item.hexic.small_red_bundle": "小型红色施法口袋", 112 + "item.hexic.small_white_bundle": "小型白色施法口袋", 113 + "item.hexic.small_yellow_bundle": "小型黄色施法口袋", 126 114 "item.hexic.stringworm.item.hexcasting.dye_colorizer_black": "闪亮的黑色毛绒线虫", 127 115 "item.hexic.stringworm.item.hexcasting.dye_colorizer_blue": "闪亮的蓝色毛绒线虫", 128 116 "item.hexic.stringworm.item.hexcasting.dye_colorizer_brown": "闪亮的棕色毛绒线虫", ··· 172 160 "itemGroup.hexic.group": "Hexic", 173 161 "tag.item.hexic.mediaweaves": "媒质布匹", 174 162 "text.hexic.or_map": "%s或映射", 175 - "text.hexic.pigment_holder_item": "存有染色剂的物品" 163 + "text.hexic.pigment_holder_item": "存有染色剂的物品", 164 + "trinkets.slot.chest.hexic_mediaweave": "媒质布匹" 176 165 }
project/hexic/src/main/resources/assets/hexic/textures/gui/mediaweave.png

This is a binary file and will not be displayed.

project/hexic/src/main/resources/assets/hexic/textures/item/coconut.jpg

This is a binary file and will not be displayed.

project/hexic/src/main/resources/assets/hexic/textures/item/coconut.png

This is a binary file and will not be displayed.

project/hexic/src/main/resources/assets/hexic/textures/item/echo.png

This is a binary file and will not be displayed.

+5 -4
project/hexic/src/main/resources/data/hexcasting/tags/action/per_world_pattern.json
··· 1 1 { 2 2 "values": [ 3 - "hexcasting:craft/artifact", 4 - "hexcasting:craft/trinket", 5 - "hexic:snow" 3 + "hexic:moveentity", 4 + "hexic:makeworld", 5 + "hexic:attachworld", 6 + "hexic:deleteworld" 6 7 ] 7 - } 8 + }
+5 -2
project/hexic/src/main/resources/data/hexcasting/tags/action/requires_enlightenment.json
··· 1 1 { 2 2 "values": [ 3 - "hexic:snow" 3 + "hexic:moveentity", 4 + "hexic:makeworld", 5 + "hexic:attachworld", 6 + "hexic:deleteworld" 4 7 ] 5 - } 8 + }
+8
project/hexic/src/main/resources/data/trinkets/entities/hexic.json
··· 1 + { 2 + "entities": [ 3 + "player" 4 + ], 5 + "slots": [ 6 + "chest/hexic_mediaweave" 7 + ] 8 + }
+4
project/hexic/src/main/resources/data/trinkets/slots/chest/hexic_mediaweave.json
··· 1 + { 2 + "icon": "hexic:gui/mediaweave", 3 + "drop_rule": "keep" 4 + }
+5
project/hexic/src/main/resources/data/trinkets/tags/items/chest/hexic_mediaweave.json
··· 1 + { 2 + "values": [ 3 + "#hexic:mediaweaves" 4 + ] 5 + }
+5 -1
project/hexic/src/main/resources/hexic.mixins.json
··· 6 6 "mixins": [ 7 7 "AbstractFurnaceBlockEntityMixin", 8 8 "ActionRegistryEntryMixin", 9 - "BiomeMixin", 10 9 "CastingEnvironmentMixin", 11 10 "CastingVMMixin", 11 + "ChatMessageC2SPacketMixin", 12 12 "DimIotaMixin", 13 13 "EntityMixin", 14 14 "HopperEndpointRegistryMixin", ··· 16 16 "ItemPackagedHexMixin", 17 17 "ItemStackAccess", 18 18 "ItemStackMixin", 19 + "LimbAnimatorAccess", 19 20 "LivingEntityAccess", 21 + "LivingEntityMixin", 20 22 "MediafiedItemManagerIndexMixin", 21 23 "OpDimTeleport$SpellMixin", 24 + "OpDimTeleportMixin", 22 25 "OpEdifySaplingMixin", 23 26 "OpEdifySaplingMixin$Spell", 24 27 "OpObservePropertyMixin", ··· 28 31 "PacketByteBufMixin", 29 32 "PlayerInventoryMixin", 30 33 "SimpleRegistryMixin", 34 + "SpellAction$ResultAccessor", 31 35 "StaffCastEnvMixin", 32 36 "WorldMixin" 33 37 ],
+239
project/hexic/src/main/scala/org/eu/net/pool/hexic/chat.scala
··· 1 + package org.eu.net.pool 2 + package hexic 3 + 4 + import at.petrak.hexcasting.api.casting.RenderedSpell 5 + import at.petrak.hexcasting.api.casting.eval.env.PlayerBasedCastEnv 6 + import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect 7 + import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect.DoMishap 8 + import at.petrak.hexcasting.api.casting.eval.{CastResult, CastingEnvironment, ResolvedPatternType} 9 + import at.petrak.hexcasting.api.casting.eval.vm.{CastingImage, CastingVM, ContinuationFrame, SpellContinuation} 10 + import at.petrak.hexcasting.api.casting.iota.{ContinuationIota, Iota, IotaType, ListIota, NullIota} 11 + import at.petrak.hexcasting.api.casting.mishaps.{Mishap, MishapBadCaster, MishapInvalidIota, MishapNotEnoughArgs} 12 + import at.petrak.hexcasting.api.pigment.FrozenPigment 13 + import at.petrak.hexcasting.common.lib.HexItems 14 + import at.petrak.hexcasting.common.lib.hex.HexEvalSounds 15 + import com.google.gson.JsonElement 16 + import com.mojang.serialization.JsonOps 17 + import dev.emi.trinkets.api.{TrinketComponent, TrinketsApi} 18 + import dev.onyxstudios.cca.api.v3.component.sync.AutoSyncedComponent 19 + import dev.onyxstudios.cca.api.v3.component.{Component, ComponentAccess, ComponentKey, ComponentRegistry} 20 + import dev.onyxstudios.cca.api.v3.entity.{EntityComponentFactoryRegistry, EntityComponentInitializer, RespawnCopyStrategy} 21 + import net.fabricmc.fabric.api.networking.v1.{PacketByteBufs, ServerPlayNetworking} 22 + import net.minecraft.entity.{Entity, EntityPose, EntityType, LimbAnimator} 23 + import net.minecraft.entity.passive.CatEntity 24 + import net.minecraft.entity.player.PlayerEntity 25 + import net.minecraft.item.ItemStack 26 + import net.minecraft.nbt.{NbtCompound, NbtElement, NbtOps} 27 + import net.minecraft.server.network.ServerPlayerEntity 28 + import net.minecraft.server.world.ServerWorld 29 + import net.minecraft.text.Text 30 + import net.minecraft.util.{DyeColor, Hand, Identifier, Pair, Util, Uuids} 31 + import org.eu.net.pool.phlib.{Events as PhEvents, *, given} 32 + import org.slf4j.{Logger, LoggerFactory} 33 + import ram.talia.moreiotas.api.casting.iota.StringIota 34 + 35 + import java.util.UUID 36 + import scala.annotation.{meta, static, targetName} 37 + import scala.collection.immutable.* 38 + import scala.jdk.CollectionConverters.* 39 + import scala.language.implicitConversions 40 + import scala.util.boundary 41 + 42 + private[hexic] case class MurmurCache(var value: Option[String]) extends Component: 43 + override def readFromNbt(tag: NbtCompound): Unit = 44 + if tag.getBoolean("active") then 45 + value = Some(tag.getString("value")) 46 + else 47 + value = None 48 + override def writeToNbt(tag: NbtCompound): Unit = 49 + tag.putBoolean("active", false) 50 + for value <- value do 51 + tag.putBoolean("active", true) 52 + tag.putString("value", value) 53 + private[hexic] object MurmurCache: 54 + given ComponentKey[MurmurCache] = ComponentRegistry.getOrCreate("murmur", classOf[MurmurCache]) 55 + 56 + private[hexic] case class RevealComponent(var lines: Seq[Text]) extends AutoSyncedComponent: 57 + override def readFromNbt(tag: NbtCompound): Unit = 58 + lines = for n <- 0 until tag.getInt("lineCount") yield Text.Serializer.fromJson(tag(s"line$n")) // nbt is json apparently? 59 + override def writeToNbt(tag: NbtCompound): Unit = 60 + tag.putInt("lineCount", lines.size) 61 + for (line, n) <- lines.zipWithIndex do tag(s"line$n") = Text.Serializer.toJsonTree(line) 62 + private[hexic] object RevealComponent: 63 + given ComponentKey[RevealComponent] = ComponentRegistry.getOrCreate("reveal", classOf[RevealComponent]) 64 + 65 + def keyOf[C <: Component: ComponentKey as key] = key 66 + 67 + extension (c: ComponentAccess) 68 + def component[C <: Component: ComponentKey as key]: C = c.getComponent(key) 69 + def syncComponent[C <: Component: ComponentKey as key](): Unit = c.syncComponent(key) 70 + 71 + // rephrased from trickster 72 + class CatHolder private[hexic] (p: PlayerEntity) extends Component: 73 + lazy private val realCat = 74 + val cat = CatEntity(EntityType.CAT, p.getWorld) 75 + cat.setAiDisabled(true) 76 + cat.setInvulnerable(true) 77 + cat.setTamed(true) 78 + cat 79 + def syncCat(collarColor: DyeColor) = 80 + val cat = this.realCat 81 + cat.setYaw(p.getYaw) 82 + cat.prevYaw = p.prevYaw 83 + cat.setPitch(p.getPitch) 84 + cat.prevPitch = p.prevPitch 85 + cat.setPos(p.getX, p.getY, p.getZ) 86 + cat.prevX = p.prevX 87 + cat.prevY = p.prevY 88 + cat.prevZ = p.prevZ 89 + cat.setBodyYaw(p.bodyYaw) 90 + cat.prevBodyYaw = p.prevBodyYaw 91 + cat.setHeadYaw(p.headYaw) 92 + cat.prevHeadYaw = p.prevHeadYaw 93 + cat.hurtTime = p.hurtTime 94 + cat.handSwinging = p.handSwinging 95 + cat.handSwingTicks = p.handSwingTicks 96 + cat.handSwingProgress = p.handSwingProgress 97 + cat.lastHandSwingProgress = p.lastHandSwingProgress 98 + cat.limbAnimator.prevSpeed = p.limbAnimator.prevSpeed 99 + cat.limbAnimator.speed = p.limbAnimator.speed 100 + cat.limbAnimator.pos = p.limbAnimator.pos 101 + cat.setCollarColor(collarColor) 102 + if p.getPose == EntityPose.CROUCHING then 103 + cat.setInSittingPose(true) 104 + cat.setPose(EntityPose.STANDING) 105 + else 106 + cat.setInSittingPose(false) 107 + cat.setPose(p.getPose) 108 + 109 + def cat = p.catCollarColor.map(_ => realCat) 110 + def syncAndGetCat() = p.catCollarColor.map { c => syncCat(c); realCat } 111 + def catOrNull: CatEntity | Null = cat.orNull 112 + 113 + override def readFromNbt(nbtCompound: NbtCompound): Unit = () 114 + override def writeToNbt(nbtCompound: NbtCompound): Unit = () 115 + object CatHolder: 116 + given key: ComponentKey[CatHolder] = ComponentRegistry.getOrCreate("cat", classOf[CatHolder]) 117 + def apply(p: PlayerEntity) = p.getComponent(key) 118 + @static def getCat(e: Entity): CatEntity | Null = e match 119 + case p: PlayerEntity => CatHolder(p).cat.orNull 120 + case _ => null 121 + @static def getSyncedCat(e: Entity): CatEntity | Null = e match 122 + case p: PlayerEntity => CatHolder(p).syncAndGetCat().orNull 123 + case _ => null 124 + 125 + given Conversion[LimbAnimator, hexic.mixin.LimbAnimatorAccess] = _.asInstanceOf 126 + package mixin: 127 + import org.spongepowered.asm.mixin.Mixin 128 + import org.spongepowered.asm.mixin.gen.Accessor 129 + @Mixin(value = Array(classOf[LimbAnimator])) 130 + private[hexic] trait LimbAnimatorAccess: 131 + this: LimbAnimator => 132 + @targetName("hexic$getPrevSpeed") @Accessor("prevSpeed") private[hexic] def prevSpeed: Float 133 + @targetName("hexic$getSpeed") @Accessor("speed") private[hexic] def speed: Float 134 + @targetName("hexic$getPos") @Accessor("pos") private[hexic] def pos: Float 135 + @targetName("hexic$setPrevSpeed") @Accessor("prevSpeed") private[hexic] def prevSpeed_=(prevSpeed: Float): Unit 136 + @targetName("hexic$setSpeed") @Accessor("speed") private[hexic] def speed_=(speed: Float): Unit 137 + @targetName("hexic$setPos") @Accessor("pos") private[hexic] def pos_=(pos: Float): Unit 138 + 139 + extension (p: PlayerEntity) 140 + def validMediaweave: Option[(NbtCompound, DyeColor, ItemStack)] = 141 + TrinketsApi.getTrinketComponent(p) 142 + .pipe(o => Option.when[TrinketComponent](o.isPresent)(o.get())) 143 + .flatMap: (c: TrinketComponent) => 144 + c.getEquipped(_.getItem.isInstanceOf[Mediaweave]).asScala.collectFirst: 145 + Function.unlift: p => 146 + p.getRight.getItem match 147 + case m@Mediaweave(color) => Option(m.readIotaTag(p.getRight)).map((_, color, p.getRight)) 148 + case _ => None 149 + def catCollarColor: Option[DyeColor] = 150 + TrinketsApi.getTrinketComponent(p) 151 + .pipe(o => Option.when[TrinketComponent](o.isPresent)(o.get())) 152 + .flatMap: (c: TrinketComponent) => 153 + c.getEquipped(_.getItem.isInstanceOf[Mediaweave]).asScala.collectFirst: 154 + Function.unlift: p => 155 + p.getRight.getItem match 156 + case m@Mediaweave(color) if p.getRight.hasCustomName && p.getRight.getName.getString.toLowerCase == "instant cat" => Some(m.color) 157 + case _ => None 158 + extension (p: ServerPlayerEntity) 159 + def executeMediaweave(text: String, ctx: Seq[Iota]): Boolean = 160 + p.validMediaweave match 161 + case Some(hex, color, stack) => 162 + given world: ServerWorld = p.getWorld.asInstanceOf[ServerWorld] 163 + lazy val env = new PlayerBasedCastEnv(p, Hand.OFF_HAND): 164 + override def extractMediaEnvironment(cost: Long, simulate: Boolean): Long = 165 + if p.isCreative then 0L else extractMediaFromInventory(cost, canOvercast, simulate) 166 + override def getCastingHand: Hand = castingHand 167 + override def getPigment = FrozenPigment(ItemStack(HexItems.DYE_PIGMENTS.get(color)), Util.NIL_UUID) 168 + val image = CastingImage(ctx :+ StringIota.make(text), 0, Seq(), false, 0, NbtCompound(), null) 169 + val instrs = IotaType.deserialize(hex, world) match 170 + case list: ListIota => list.getList.asScala.toSeq 171 + case iota => Seq(iota) 172 + val vm = CastingVM(image, env) 173 + val view = vm.queueExecuteAndWrapIotas(instrs :+ ContinuationIota(SpellContinuation.NotDone(MessageFrame(p.getUuid, stack.getName, p), SpellContinuation.Done.INSTANCE)), world) 174 + true 175 + case _ => false 176 + object MessageFrame extends ContinuationFrame.Type[MessageFrame]: 177 + override def deserializeFromNBT(c: NbtCompound, world: ServerWorld): MessageFrame = 178 + val id = Uuids.toUuid(c.getIntArray("id")) 179 + MessageFrame(id, Text.Serializer.fromJson(NbtOps.INSTANCE.convertTo(JsonOps.INSTANCE, c.getCompound("t"))), world.getServer.getPlayerManager.getPlayer(id)) 180 + class MessageFrame(id: UUID, text: Text, player: => ServerPlayerEntity) extends ContinuationFrame: 181 + override def getType: ContinuationFrame.Type[MessageFrame] = MessageFrame 182 + override def evaluate(rest: SpellContinuation, world: ServerWorld, vm: CastingVM): CastResult = 183 + boundary: 184 + def mishap(m: Mishap) = boundary.break(CastResult(NullIota(), rest, vm.getImage, Seq(DoMishap(m, Mishap.Context(null, text))), ResolvedPatternType.EVALUATED, HexEvalSounds.NORMAL_EXECUTE)) 185 + vm.getImage.getStack.toSeq.reverse match 186 + case Seq() => 187 + mishap(MishapNotEnoughArgs(1, 0)) 188 + case Seq(s: StringIota, stack*) => 189 + CastResult(NullIota(), rest, vm.getImage.withStack(_ => stack), Seq( 190 + OperatorSideEffect.AttemptSpell( 191 + new RenderedSpell: 192 + override def cast(env: CastingEnvironment): Unit = 193 + ServerPlayNetworking.send(player, "msg", PacketByteBufs.create.tap(_.writeString(s.getString))) 194 + override def cast(env: CastingEnvironment, img: CastingImage): CastingImage = { cast(env); img } 195 + , false, false 196 + ) 197 + ), ResolvedPatternType.EVALUATED, HexEvalSounds.NORMAL_EXECUTE) 198 + case Seq(i, _*) => 199 + mishap(MishapInvalidIota.ofType(i, 0, "string")) 200 + override def serializeToNBT(): NbtCompound = NbtCompound() 201 + .tap(_.putIntArray("id", Uuids.toIntArray(id))) 202 + .tap(_.put("t", JsonOps.INSTANCE.convertTo(NbtOps.INSTANCE, Text.Serializer.toJsonTree(text)))) 203 + override def breakDownwards(stack: java.util.List[? <: Iota]): kotlin.Pair[java.lang.Boolean, java.util.List[Iota]] = kotlin.Pair(false, stack.toSeq) 204 + override def size = 0 205 + 206 + object hasComponent: 207 + def unapply[C <: Component: ComponentKey](ctx: ComponentAccess): Option[C] = 208 + try 209 + Some(ctx.component[C]) 210 + catch case _: NoSuchElementException => 211 + None 212 + 213 + def initChat() = 214 + Patterns.register("reveal", ne"deqed" ): 215 + Patterns.mkConstAction(1, 0): 216 + case Seq(iota: Iota) => 217 + locally(summon[CastingEnvironment]).getCastingEntity match 218 + case null => throw MishapBadCaster() 219 + case p: ServerPlayerEntity => 220 + p.component[RevealComponent].lines = iota match 221 + case s: ListIota => s.getList.map(_.display).toSeq 222 + //case m: MapIota => m.map.toSeq.map(p => IotaType.getDisplay(p._1) -> IotaType.getDisplay(p._2)).sortBy(_._1.getString) 223 + case _: NullIota => Seq() 224 + case _ => Seq(iota.display) 225 + p.syncComponent[RevealComponent]() 226 + Seq() 227 + case _ => throw MishapBadCaster() 228 + Patterns.register("murmur", e"wwaqwa"): 229 + Patterns.mkLiteral: (env, _) ?=> 230 + hasComponent.unapply[MurmurCache](env.getCastingEntity).fold(throw MishapBadCaster())(_.value.fold(NullIota())(StringIota.make)) 231 + hexXplat.getContinuationTypeRegistry("send_message") = MessageFrame 232 + ServerPlayNetworking.registerGlobalReceiver("murmur", { case (_, hasComponent[MurmurCache](c), _, buf, _) => c.value = Option.when(buf.readBoolean())(buf.readString()) }: ServerPlayNetworking.PlayChannelHandler) 233 + ServerPlayNetworking.registerGlobalReceiver("message", (_, player, _, buf, _) => 234 + val context = buf.readByte() 235 + if context != 0 then 236 + throw IllegalArgumentException("Nonzero context is reserved for future use") 237 + val text = buf.readString() 238 + player.executeMediaweave(text, Seq()) 239 + )
+252 -498
project/hexic/src/main/scala/org/eu/net/pool/hexic/main.scala
··· 3 3 package hexic 4 4 5 5 import at.petrak.hexcasting.api.addldata.ADMediaHolder 6 - import at.petrak.hexcasting.api.casting.{ActionRegistryEntry, ParticleSpray, RenderedSpell, SpellList} 6 + import at.petrak.hexcasting.api.casting.{ActionRegistryEntry, OperatorUtils, ParticleSpray, RenderedSpell, SpellList} 7 7 import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic 8 8 import at.petrak.hexcasting.api.casting.arithmetic.operator.Operator 9 9 import at.petrak.hexcasting.api.casting.castables.{Action, ConstMediaAction, OperationAction, SpecialHandler, SpellAction} ··· 55 55 import net.fabricmc.fabric.api.transfer.v1.transaction.{Transaction, TransactionContext} 56 56 import net.fabricmc.loader.api.FabricLoader 57 57 import net.minecraft.Bootstrap 58 - import net.minecraft.block.{AbstractBlock, Block, BlockRenderType, BlockState, BlockWithEntity, ShapeContext} 58 + import net.minecraft.block.{AbstractBlock, Block, BlockRenderType, BlockState, BlockWithEntity, DispenserBlock, ShapeContext} 59 59 import net.minecraft.command.argument.{EntityArgumentType, NbtElementArgumentType, UuidArgumentType} 60 60 import net.minecraft.command.{CommandException, EntitySelector} 61 61 import net.minecraft.entity.player.PlayerEntity ··· 72 72 import net.minecraft.server.world.ServerWorld 73 73 import net.minecraft.text.{HoverEvent, LiteralTextContent, MutableText, Style, Text, TextColor, TextContent, Texts} 74 74 import net.minecraft.util.dynamic.Codecs 75 - import net.minecraft.util.math.{BlockPos, ChunkPos, Direction, Vec3d} 75 + import net.minecraft.util.math.{BlockPointer, BlockPos, ChunkPos, Direction, Vec3d} 76 76 import net.minecraft.util.{ActionResult, Arm, ClickType, DyeColor, Formatting, Hand, Identifier, Rarity, TypedActionResult, Util, Uuids, WorldSavePath} 77 77 import net.minecraft.world.biome.Biome 78 78 import net.minecraft.world.{BlockView, TeleportTarget, World} ··· 89 89 import java.lang.invoke.MethodHandles 90 90 import java.lang.reflect.{Constructor, Field, Member, Method} 91 91 import java.nio.ByteBuffer 92 - import java.nio.file.{Files, Path, Paths, StandardOpenOption} 92 + import java.nio.file.{FileSystemException, Files, Path, Paths, StandardOpenOption} 93 93 import java.util.{Optional, UUID} 94 94 import java.{lang, util} 95 95 import scala.annotation.unchecked.uncheckedVariance 96 96 import scala.annotation.{elidable, experimental, showAsInfix, tailrec, targetName, unused} 97 97 import scala.ref.WeakReference 98 - import scala.util.{Failure, Success, Try} 98 + import scala.util.{Failure, NotGiven, Random, Success, Try, TupledFunction, Using, boundary} 99 99 import scala.collection.mutable 100 100 import scala.compiletime.summonFrom 101 101 import scala.concurrent.{Await, ExecutionContext, Future, Promise} ··· 103 103 import scala.language.experimental.{macros, saferExceptions} 104 104 import scala.language.{dynamics, existentials, implicitConversions, postfixOps, reflectiveCalls} 105 105 import scala.reflect.{ClassTag, classTag} 106 - import scala.util.{NotGiven, Random, TupledFunction, boundary} 107 106 import at.petrak.hexcasting.api.casting.mishaps.Mishap.Context 108 107 import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking.PlayChannelHandler 109 108 import net.fabricmc.fabric.api.networking.v1.{FabricPacket, PacketByteBufs, PacketSender, PacketType, ServerPlayNetworking} ··· 138 137 import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents 139 138 import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents 140 139 import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup 141 - import net.minecraft.block.AbstractBlock 142 140 import net.minecraft.entity.passive.FoxEntity 143 141 import net.minecraft.stat.Stats 144 142 import org.eu.net.pool.hexic.mixin.{ItemStackAccess, LivingEntityAccess} 145 143 import at.petrak.hexcasting.common.casting.actions.eval.OpEval 146 144 import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType 147 - import at.petrak.hexcasting.common.casting.actions.spells.OpBreakBlock 148 - import at.petrak.hexcasting.common.casting.actions.spells.OpErase 145 + import at.petrak.hexcasting.common.casting.actions.spells.{OpBreakBlock, OpErase, OpPotionEffect} 149 146 import at.petrak.hexcasting.api.casting.mishaps.MishapBadEntity 150 147 import net.minecraft.entity.ItemEntity 151 148 import net.minecraft.entity.decoration.ItemFrameEntity ··· 171 168 172 169 import scala.util.matching.Regex 173 170 import at.petrak.hexcasting.api.casting.eval.vm.CastingImage.ParenthesizedIota 171 + import dev.emi.trinkets.api.{TrinketComponent, TrinketsApi} 174 172 import net.beholderface.oneironaut.casting.iotatypes.DimIota 175 173 import net.fabricmc.fabric.api.dimension.v1.FabricDimensions 176 174 import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents 175 + import net.minecraft.block.dispenser.ItemDispenserBehavior 177 176 import net.minecraft.world.gen.chunk.{ChunkGenerator, ChunkGenerators} 178 177 import xyz.nucleoid.fantasy.util.VoidChunkGenerator 179 178 import xyz.nucleoid.fantasy.{Fantasy, RuntimeWorldConfig, RuntimeWorldHandle} ··· 182 181 import java.math.BigInteger 183 182 import net.minecraft.entity.ItemEntity 184 183 import net.minecraft.entity.ExperienceOrbEntity 184 + import net.minecraft.entity.damage.DamageSources 185 + import net.minecraft.entity.effect.{StatusEffectInstance, StatusEffects} 185 186 import net.minecraft.network.packet.s2c.play.PositionFlag 187 + import net.minecraft.predicate.entity.EntityPredicates 186 188 import net.minecraft.world.chunk.{ChunkStatus, WorldChunk} 187 189 188 190 import scala.concurrent.duration.Duration 189 191 import phlib.{Events as PhEvents, *, given} 190 192 191 - given Logger = LoggerFactory.getLogger("hexic") 192 - given Conversion[String, Identifier] = Identifier.of("hexic", _) 193 + private[hexic] given Logger = LoggerFactory.getLogger("hexic") 194 + private[hexic] given Conversion[String, Identifier] = Identifier.of("hexic", _) 193 195 194 196 extension (i: Iota) 195 197 def asIotaType[T <: Iota: ClassTag](idx: Int, expected: => Text): T = i match ··· 366 368 367 369 class PlayerInfoComponent( 368 370 val player: PlayerEntity, 369 - var murmur: Option[String] = None, 370 371 var leftWeave: ItemStack = ItemStack.EMPTY, 371 372 var rightWeave: ItemStack = ItemStack.EMPTY, 372 - var chatLines: Seq[Text] = Seq(), 373 373 var foxType: Option[FoxEntity.Type] = None, 374 374 ) extends Component, AutoSyncedComponent: 375 375 override def readFromNbt(c: NbtCompound): Unit = ··· 381 381 rightWeave = ItemStack.fromNbt(c.getCompound("shr")) 382 382 else 383 383 rightWeave = ItemStack.EMPTY 384 - chatLines = c.getList("chat", NbtElement.COMPOUND_TYPE).map(NbtOps.INSTANCE.convertTo(JsonOps.INSTANCE, _)).map(Text.Serializer.fromJson).toSeq 385 384 if c.contains("fox", NbtElement.STRING_TYPE) then 386 385 foxType = Some(FoxEntity.Type.valueOf(c.getString("fox"))) 387 386 else ··· 389 388 override def writeToNbt(c: NbtCompound): Unit = 390 389 if !leftWeave.isEmpty then c.put("shl", NbtCompound().tap(leftWeave.writeNbt)) 391 390 if !rightWeave.isEmpty then c.put("shr", NbtCompound().tap(rightWeave.writeNbt)) 392 - c.put("chat", NbtList().tap(_.addAll(chatLines.map(Text.Serializer.toJsonTree).map(JsonOps.INSTANCE.convertTo(NbtOps.INSTANCE, _))))) 393 391 foxType.fold(c.remove("fox"))(f => c.putString("fox", f.name)) 394 392 object PlayerInfoComponent: 395 393 given key: ComponentKey[PlayerInfoComponent] = ComponentRegistry.getOrCreate("player_wisp", classOf[PlayerInfoComponent]) ··· 397 395 private[hexic] def register(using fac: EntityComponentFactoryRegistry) = 398 396 fac.registerForPlayers(key, PlayerInfoComponent(_), RespawnCopyStrategy.LOSSLESS_ONLY) 399 397 400 - class ServerInfoComponent( 401 - var endSnowTick: Long = 0 402 - ) extends Component, AutoSyncedComponent: 403 - override def readFromNbt(tag: NbtCompound): Unit = 404 - endSnowTick = tag.getLong("snow") 405 - override def writeToNbt(tag: NbtCompound): Unit = 406 - tag.putLong("snow", endSnowTick) 398 + class ServerInfoComponent() extends Component, AutoSyncedComponent: 399 + override def readFromNbt(tag: NbtCompound): Unit = () 400 + override def writeToNbt(tag: NbtCompound): Unit = () 407 401 object ServerInfoComponent: 408 402 given key: ComponentKey[ServerInfoComponent] = ComponentRegistry.getOrCreate("server_info", classOf[ServerInfoComponent]) 409 403 given get: (server: MinecraftServer) => ServerInfoComponent = server.getOverworld.getLevelProperties.getComponent(key) ··· 411 405 private[hexic] def register(using fac: LevelComponentFactoryRegistry) = 412 406 fac.register(key, _ => ServerInfoComponent()) 413 407 408 + class ExcursionComponent(var enteredDemiplaneTick: Long = 0, var excursion: Option[(RegistryKey[World], Vec3d)] = None) extends Component: 409 + override def readFromNbt(tag: NbtCompound): Unit = 410 + enteredDemiplaneTick = tag.getLong("grace") 411 + excursion = for 412 + case c: NbtString <- Option(tag.get("dim")) 413 + id <- Option(Identifier.tryParse(c.asString)) 414 + yield (RegistryKey.of(RegistryKeys.WORLD, id), Vec3d(tag.getDouble("x"), tag.getDouble("y"), tag.getDouble("z"))) 415 + override def writeToNbt(tag: NbtCompound): Unit = 416 + tag.putLong("grace", enteredDemiplaneTick) 417 + for world -> pos <- excursion do 418 + tag.putString("dim", world.getValue.toString) 419 + tag.putDouble("x", pos.getX) 420 + tag.putDouble("y", pos.getY) 421 + tag.putDouble("z", pos.getZ) 422 + object ExcursionComponent: 423 + given key: ComponentKey[ExcursionComponent] = ComponentRegistry.getOrCreate("excursion", classOf[ExcursionComponent]) 424 + 414 425 extension [S, T <: ArgumentBuilder[S, T]] (builder: T) 415 426 def literal(name: String)(body: LiteralArgumentBuilder[S] => Unit): T = builder.`then`(LiteralArgumentBuilder.literal[S](name).tap(body)) 416 427 def argument(name: String, typ: ArgumentType[?])(body: RequiredArgumentBuilder[S, ?] => Unit): T = builder.`then`(RequiredArgumentBuilder.argument(name, typ).tap(body)) ··· 476 487 given [T <: java.lang.Enum[T]: ClassTag as ct] => FromString[T]: 477 488 override def fromString(s: String): T = Enum.valueOf[T](ct.runtimeClass.asInstanceOf[Class[T]], s) 478 489 479 - case class Pen private [hexic] (color: DyeColor) extends Item(Item.Settings().maxCount(1)): 490 + case class Pen private [hexic] (color: DyeColor) extends Item(Item.Settings().maxCount(1)) with Registered(Registries.ITEM, s"pen/$color"): 491 + override def toString = s"$getClass(color=$color)${super[Item].toString}" 480 492 override def use(world: World, player: PlayerEntity, hand: Hand): TypedActionResult[ItemStack] = 481 493 // if player.getAttributeValue(HexAttributes.FEEBLE_MIND) > 0.0 then 482 494 // TypedActionResult.fail(player.getStackInHand(hand)) ··· 502 514 case c => c.get("Hex") match 503 515 case c: NbtCompound => c 504 516 case _ => null 505 - override def writeable(stack: ItemStack): Boolean = readIotaTag(stack) == null 506 - override def canWrite(stack: ItemStack, iota: Iota): Boolean = writeable(stack) && (iota match 507 - case l: ListIota => 508 - val i = l.getList.asScala 509 - i.isEmpty || i.head.executable && i.last.executable 510 - case _ => false) 517 + override def writeable(stack: ItemStack): Boolean = true 518 + override def canWrite(stack: ItemStack, iota: Iota): Boolean = iota match 519 + case l: ListIota => true 520 + case _ => iota.executable 511 521 override def writeDatum(stack: ItemStack, iota: Iota): Unit = 512 - assume(canWrite(stack, iota)) 513 522 stack.getOrCreateNbt.put("Hex", IotaType.serialize(iota)) 514 523 override def appendTooltip(stack: ItemStack, world: World, tooltip: util.List[Text], context: TooltipContext): Unit = 515 524 IotaHolderItem.appendHoverText(this, stack, tooltip, context) 525 + DispenserBlock.registerBehavior(this, new ItemDispenserBehavior: 526 + override def dispenseSilently(pointer: BlockPointer, stack: ItemStack): ItemStack = 527 + val pos = pointer.getPos.offset(pointer.getBlockState.get(DispenserBlock.FACING)) 528 + val candidates = pointer.getWorld.getEntitiesByClass(classOf[LivingEntity], net.minecraft.util.math.Box(pos), EntityPredicates.EXCEPT_SPECTATOR) 529 + for 530 + candidate <- candidates 531 + component_? = TrinketsApi.getTrinketComponent(candidate) 532 + if component_?.isPresent 533 + component = component_?.get() 534 + trinkets = component.getInventory 535 + group <- Option(trinkets.get("chest")) 536 + inventory <- Option(group.get("hexic_mediaweave")) 537 + i <- 0 until inventory.size 538 + if inventory.getStack(i).isEmpty 539 + do 540 + inventory.setStack(i, stack.split(1)) 541 + return stack 542 + super.dispenseSilently(pointer, stack) 543 + ) 516 544 object Mediaweave: 517 545 val colors: DyeColor :> Mediaweave = DyeColor.values().map(c => c -> Mediaweave(c)).toMap 518 546 val tag: TagKey[Item] = TagKey.of(Registries.ITEM, "mediaweaves") ··· 536 564 .toSeq 537 565 private def heldItems_=(x: Seq[ItemStack]): Unit = 538 566 stack.getOrCreateNbt.put("Contents", NbtList().tap: l => 539 - for item <- x do 567 + for item <- x if !item.isEmpty do 540 568 val c = NbtCompound() 541 569 item.writeNbt(c) 542 570 l.add(c) 543 571 ) 572 + private def isWaxed = Option(stack.getNbt).exists(_.contains("ro")) 573 + private def isWaxed_=(value: Boolean) = 574 + if value then 575 + stack.getOrCreateNbt.put("ro", NbtCompound()) 576 + else 577 + stack.getOrCreateNbt.remove("ro") 544 578 private def withMediaHolders[T](f: Seq[CCMediaHolder] => T): T = 545 - val heldItems = stack.heldItems 546 - try 547 - f(heldItems.flatMap(p => Option(HexCardinalComponents.MEDIA_HOLDER.getNullable(p)))) 548 - finally 549 - stack.heldItems = heldItems 579 + if stack.isWaxed then 580 + f(Seq()) 581 + else 582 + val heldItems = stack.heldItems 583 + try 584 + f(heldItems.flatMap(p => Option(HexCardinalComponents.MEDIA_HOLDER.getNullable(p)))) 585 + finally 586 + stack.heldItems = heldItems 550 587 private def mediaHolders = stack.heldItems.flatMap(p => Option(HexCardinalComponents.MEDIA_HOLDER.getNullable(p))) 551 588 override def getMedia(stack: ItemStack): Long = stack.mediaHolders.map(_.getMedia).sum 552 589 override def getMaxMedia(stack: ItemStack): Long = stack.mediaHolders.map(_.getMaxMedia).sum ··· 587 624 cursorStackReference.set(p) 588 625 stack.heldItems = held.tail 589 626 player.playSound(SoundEvents.ITEM_BUNDLE_REMOVE_ONE, 0.8F, 0.8F + player.getWorld.getRandom.nextFloat * 0.4F) 627 + else if otherStack.isOf(Items.HONEYCOMB) && !stack.isWaxed then 628 + stack.isWaxed = true 629 + otherStack.decrement(1) 630 + player.playSound(SoundEvents.ITEM_HONEYCOMB_WAX_ON, 0.8F, 0.8F + player.getWorld.getRandom.nextFloat * 0.4F) 631 + else if otherStack.isOf(Items.WET_SPONGE) && stack.isWaxed then 632 + stack.isWaxed = false 633 + player.playSound(SoundEvents.BLOCK_SLIME_BLOCK_PLACE, 0.8F, 0.8F + player.getWorld.getRandom.nextFloat * 0.4F) 590 634 else if HexCardinalComponents.MEDIA_HOLDER.getNullable(otherStack) != null then 591 635 val held = stack.heldItems 592 636 if fits(held, otherStack.getItem) then ··· 645 689 override def appendTooltip(stack: ItemStack, world: World, tooltip: util.List[Text], context: TooltipContext): Unit = 646 690 tooltip.add(Text.translatable("hexic.media_bundle.items", stack.heldItems.size, size).styled(_.withColor(Formatting.GRAY))) 647 691 val (consumables, batteries, trinkets) = getMediaInfo(stack) 692 + val isWaxed = stack.isWaxed 693 + var mentionedSealing = false 694 + def convertForWaxing(text: MutableText) = 695 + if isWaxed then 696 + mentionedSealing = true 697 + Text.empty().append(text.styled(_.withStrikethrough(true))).append(" ").append(Text.translatable("hexcasting.tooltip.spellbook.sealed").formatted(Formatting.GOLD)) 698 + else 699 + text 648 700 batteries match 649 701 case Some((total, max)) => tooltip.add(showMedia("external", total + consumables.getOrElse(0L), max)) 650 702 case None => for value <- consumables do 651 - tooltip.add(showMedia("external", value)) 703 + tooltip.add(convertForWaxing(showMedia("external", value))) 652 704 for (total, max) <- trinkets do 653 - tooltip.add(showMedia("internal", total, max)) 705 + tooltip.add(convertForWaxing(showMedia("internal", total, max))) 706 + if !mentionedSealing then 707 + tooltip.add(Text.translatable("hexcasting.tooltip.spellbook.sealed").formatted(Formatting.GOLD)) 654 708 private def showMedia(tag: String, media: Long) = Text.translatable("hexic.media.infinite", Text.translatable(s"hexic.media.$tag"), Text.translatable("hexcasting.tooltip.media", dustAmount(media).styled(_.withColor(ItemMediaHolder.HEX_COLOR)))) 655 709 private def showMedia(tag: String, media: Long, maxMedia: Long) = Text.translatable("hexic.media.finite", Text.translatable(s"hexic.media.$tag"), dustAmount(media).styled(_.withColor(ItemMediaHolder.HEX_COLOR)), Text.translatable("hexcasting.tooltip.media", dustAmount(maxMedia)).styled(_.withColor(ItemMediaHolder.HEX_COLOR)), Text.literal(PERCENTAGE.format(100.0 * media / maxMedia)+"%").styled(_.withColor(MediaHelper.mediaBarColor(media, maxMedia)))) 656 710 private def dustAmount(media: Long) = Text.literal(DUST_AMOUNT.format(media / MediaConstants.DUST_UNIT.toDouble)) 657 711 712 + extension [T] (s: => Seq[T]) def *^(n: Int) = Seq.fill(n)(()).flatMap((_) => s) 658 713 class Stringworm extends Item(Stringworm.settings) 659 714 object Stringworm: 660 715 val settings = Item.Settings().maxCount(16) 661 716 val flavors = Seq("pure", "action", "hex", "media", "thing") 662 - 663 - val stringworms = 664 - Stringworm.flavors.map(_ -> new Stringworm).toMap 717 + val biasedFlavors = "pure" +: Seq("action", "hex", "media", "thing") *^ 3 718 + def randomFlavor(using rng: net.minecraft.util.math.random.Random) = items(biasedFlavors(rng.nextInt(biasedFlavors.size))) 719 + val items = 720 + Stringworm.flavors.map(_ -> new Stringworm).toMap 721 + export Stringworm.items as stringworms 665 722 666 723 object dyedStringworm extends Stringworm: 667 724 override def getName(stack: ItemStack): Text = ··· 706 763 val p = iota match 707 764 case p: PatternIota => p.getPattern 708 765 case _ => boundary.break(None) 709 - val measure = p.anglesSignature match 710 - case introPattern (measure) => measure 711 - case _ => boundary.break(None) 712 - val size = measure.length + 1 713 - def mishap(m: Mishap) = 714 - val safeVM = CastingVM(vm.getImage, vm.getEnv) 715 - OperatorSideEffect.DoMishap(m, Mishap.Context(p, Text.translatable("hexcasting.action.hexic:parenthesize"))).performEffect(safeVM) 716 - boundary.break(Some(safeVM.getImage, ResolvedPatternType.ERRORED)) 717 - val img = vm.getImage 718 - val parens = img.getParenCount 719 - if parens == size then 720 - img.getStack.toSeq match 721 - case Seq() => mishap(MishapNotEnoughArgs(1, 0)) 722 - case tail :+ head => Some(( 723 - CastingImage( 724 - stack = tail, 725 - parenCount = parens, 726 - parenthesized = img.getParenthesized :+ ParenthesizedIota(head, false), 727 - escapeNext = false, 728 - opsConsumed = img.getOpsConsumed, 729 - userData = img.getUserData, 730 - null 731 - ), 732 - ResolvedPatternType.EVALUATED 733 - )) 734 - else if parens > size then 735 - None // leave unescaped, so a nested hex can introject 736 - else 737 - mishap(new Mishap: 738 - override def accentColor(env: CastingEnvironment, ctx: Context): FrozenPigment = dyeColor(DyeColor.ORANGE) 739 - override def errorMessage(env: CastingEnvironment, ctx: Context): Text = ??? 740 - override def execute(env: CastingEnvironment, ctx: Context, stack: util.List[Iota]): Unit = 741 - stack.add(PatternIota(p)) 742 - ) 743 - private [hexic] def getPocketName(pocket: String) = Text.of(pocketNames(getPocketID(Identifier.tryParse(pocket)).get)) 766 + p.anglesSignature match 767 + case introPattern (measure) => 768 + val size = measure.length + 1 769 + def mishap(m: Mishap) = 770 + val safeVM = CastingVM(vm.getImage, vm.getEnv) 771 + OperatorSideEffect.DoMishap(m, Mishap.Context(p, Text.translatable("hexcasting.action.hexic:parenthesize"))).performEffect(safeVM) 772 + boundary.break(Some(safeVM.getImage, ResolvedPatternType.ERRORED)) 773 + val img = vm.getImage 774 + val parens = img.getParenCount 775 + if parens == size then 776 + img.getStack.toSeq match 777 + case Seq() => mishap(MishapNotEnoughArgs(1, 0)) 778 + case tail :+ head => Some(( 779 + CastingImage( 780 + stack = tail, 781 + parenCount = parens, 782 + parenthesized = img.getParenthesized :+ ParenthesizedIota(head, false), 783 + escapeNext = false, 784 + opsConsumed = img.getOpsConsumed, 785 + userData = img.getUserData, 786 + null 787 + ), 788 + ResolvedPatternType.EVALUATED 789 + )) 790 + else if parens > size then 791 + None // leave unescaped, so a nested hex can introject 792 + else 793 + mishap(new Mishap: 794 + override def accentColor(env: CastingEnvironment, ctx: Context): FrozenPigment = dyeColor(DyeColor.ORANGE) 795 + override def errorMessage(env: CastingEnvironment, ctx: Context): Text = ??? 796 + override def execute(env: CastingEnvironment, ctx: Context, stack: util.List[Iota]): Unit = 797 + stack.add(PatternIota(p)) 798 + ) 799 + case "eadedae" => 800 + val img = vm.getImage 801 + Some(CastingImage(img.getStack:+ListIota(img.getParenthesized.map(_.getIota)):+DoubleIota(img.getParenCount), 0, Seq(), false, img.getOpsConsumed, img.getUserData, null), ResolvedPatternType.EVALUATED) 802 + case _ => None 803 + private [hexic] def getPocketName(pocket: String) = Text.of("Demiplane " + pocketNames(getPocketID(Identifier.tryParse(pocket)).get)) 744 804 745 805 val _ = 746 806 Interop.playerDeathHook = (p: PlayerEntity, out: util.List[ItemStack]) => ··· 752 812 out.add(c.leftWeave) 753 813 c.leftWeave = ItemStack.EMPTY 754 814 815 + given demiplaneExtensions: AnyRef with 816 + extension (w: ServerWorld) 817 + def meta: String MutableFunction Option[String] = 818 + val path = w.getServer.getSavePath(WorldSavePath.ROOT).resolve(s"dimensions/${w.getRegistryKey.getValue.getNamespace}/${w.getRegistryKey.getValue.getPath}") 819 + new MutableFunction: 820 + def apply(name: String) = try Option(Files.getAttribute(path, "user:" + name)).asInstanceOf[Option[Array[Byte]]].map(buf => String(buf, StandardCharsets.UTF_8)) catch case _: FileSystemException => None 821 + def update(name: String, value: Option[String]): Unit = Files.setAttribute(path, "user:" + name, value.map(StandardCharsets.UTF_8.encode).orNull) 822 + def parentInfo: Option[(RegistryKey[World], BlockPos)] = for 823 + parentStr <- w.meta("parent") 824 + parentId <- Option(Identifier.tryParse(parentStr)) 825 + parentKey = RegistryKey.of(RegistryKeys.WORLD, parentId) 826 + if w.getServer.getWorld(parentKey) ne null 827 + x <- w.meta("bound_x").flatMap(v => Try(Integer.parseInt(v)).toOption) 828 + y <- w.meta("bound_y").flatMap(v => Try(Integer.parseInt(v)).toOption) 829 + z <- w.meta("bound_z").flatMap(v => Try(Integer.parseInt(v)).toOption) 830 + yield parentKey -> BlockPos(x, y, z) 831 + def parentInfo_=(parent: Option[(RegistryKey[World], BlockPos)]) = 832 + parent.fold { 833 + w.meta("parent") = None 834 + w.meta("bound_x") = None 835 + w.meta("bound_y") = None 836 + w.meta("bound_z") = None 837 + } { (world, pos) => 838 + w.meta("parent") = Some(world.getValue.toString) 839 + w.meta("bound_x") = Some(pos.getX.toString) 840 + w.meta("bound_y") = Some(pos.getY.toString) 841 + w.meta("bound_z") = Some(pos.getZ.toString) 842 + } 843 + 844 + object JavaPlaneAccess: 845 + def logExcursion(sp: ServerPlayerEntity) = 846 + val c = sp.component[ExcursionComponent] 847 + val curDim = sp.getWorld.getRegistryKey 848 + c.excursion = Some(curDim, sp.getPos) 849 + c.enteredDemiplaneTick = sp.getWorld.getTime 850 + sp.syncComponent(ExcursionComponent.key) 851 + def getDefaultExcursion(/** must be a demiplane */ world: ServerWorld): (ServerWorld, Vec3d) = 852 + locally: 853 + // if the plane is bound, where it's bound to is the default excursion 854 + for 855 + case (key, pos) <- world.parentInfo 856 + boundWorld <- Option(world.getServer.getWorld(key)) 857 + yield 858 + (boundWorld, Vec3d.ofBottomCenter(pos)) 859 + .getOrElse: 860 + // idfk just go to spawn or something 861 + // someone would never do something silly like setting the world spawn to y 10000 or something like that riiiight? 862 + (world.getServer.getOverworld, Vec3d.ofBottomCenter(world.getServer.getOverworld.getSpawnPos)) 863 + def findExcursion(/* must be in a demiplane */ sp: ServerPlayerEntity): (ServerWorld, Vec3d) = 864 + locally: 865 + // the happy path, assuming we have a player involved 866 + for 867 + case (key, pos) <- sp.component[ExcursionComponent].excursion 868 + world <- Option(sp.getServer.getWorld(key)) 869 + yield 870 + (world, pos) 871 + .getOrElse: 872 + given_Logger.warn(s"$sp claims to have never entered a demiplane") 873 + getDefaultExcursion(sp.getServerWorld) 874 + def findExcursion(target: Entity)(using env: CastingEnvironment): (ServerWorld, Vec3d) = 875 + env match 876 + case p: PlayerBasedCastEnv => 877 + val (world, pos) = findExcursion(p.getCaster) 878 + world -> pos.add(target.getPos).subtract(p.getCaster.getPos) 879 + case _ => target match 880 + case p: ServerPlayerEntity => findExcursion(p) 881 + case _ => target.getWorld match 882 + case sw: ServerWorld => getDefaultExcursion(sw) 883 + case _ => throw new IllegalStateException("why are you casting Spatial Interchange client-sided") 884 + def sendDirectlyToHell(sp: ServerPlayerEntity) = 885 + val c = sp.component[ExcursionComponent] 886 + if c.enteredDemiplaneTick + cfg[Int]("hexic.demiplaneGracePeriod").getOrElse(5) > sp.getWorld.getTime then 887 + given_Logger.warn(s"$sp found outside kitchen") 888 + sp.requestTeleport(5.5, 1.0, 5.5) 889 + else 890 + given_Logger.warn(s"$sp broke out of cell ${sp.getWorld.getRegistryKey}! mods, please check if the borders are broken") 891 + val (world, pos) = findExcursion(sp) 892 + val sp2 = FabricDimensions.teleport(sp, world, TeleportTarget(pos, Vec3d.ZERO, sp.getYaw, sp.getPitch)) 893 + sp2.addStatusEffect(StatusEffectInstance(StatusEffects.NAUSEA, 30*20)) 894 + sp2.addStatusEffect(StatusEffectInstance(StatusEffects.BLINDNESS, 30*20)) 895 + sp2.damage(sp2.getWorld.getDamageSources.outOfWorld, sp2.getMaxHealth / 2) 896 + 755 897 given Codec[Int] = Codec.INT.xmap(p => p, p => p) 756 898 757 899 type Media = Long ··· 853 995 ) 854 996 possible(Random.nextInt(possible.size)) 855 997 Interop.thoughtWorld = RegistryKey.of(RegistryKeys.WORLD, "thought") 856 - try System.getProperties.load(Files.newBufferedReader(Path.of("config/jvm.properties"), Charsets.UTF_8)) 857 - catch 858 - case _: FileNotFoundException => 859 - case i: IOException => summon[Logger].warn("Failed to read properties", i) 860 - iotaTypeRegistry("location") = LocationIota 861 - iotaTypeRegistry("nbt") = NbtIota 862 - iotaTypeRegistry("tripwire") = TripwireIota.getType 863 998 iotaTypeRegistry("access") = PropertyAccessIota.Type 864 - hexXplat.getContinuationTypeRegistry("tripwire") = TripwireIota.Frame 865 - for (color, item) <- Mediaweave.colors do 866 - Registries.ITEM(s"${color.asString}_mediaweave") = item 999 + for color -> item <- Mediaweave.colors do Registries.ITEM(s"${color.asString}_mediaweave") = item 867 1000 for item <- MediaBundle.items do 868 1001 Registries.ITEM(item.size match 869 1002 case 6 => s"small_${item.color.asString}_bundle" 870 1003 case 12 => s"large_${item.color.asString}_bundle") = item 871 1004 for (flavor, item) <- stringworms do 872 1005 Registries.ITEM(s"stringworm_$flavor") = item 1006 + Pen.instances 873 1007 Registries.ITEM("stringworm_pigmented") = dyedStringworm 874 1008 Registries.ITEM("wizard") = wizard 875 1009 val cutItem = new Item(Item.Settings().maxCount(16)) with MediaHolderItem: ··· 968 1102 case _ => ActionResult.PASS 969 1103 Registries.BLOCK("chisel_table") = table 970 1104 Registries.ITEM("chisel_table") = BlockItem(table, Item.Settings()) 971 - for (color, item) <- Pen.instances do Registries.ITEM(s"pen/${color.asString}") = item 972 1105 Registries.ITEM_GROUP("group") = itemGroup 973 1106 //Registries.ITEM("echo") = EchoItem 1107 + initChat() 1108 + initViews() 974 1109 if fabric.isModLoaded("hexical") then 975 1110 for 976 1111 HopperEndpointRegistry <- classNamed("miyucomics.hexical.features.hopper.HopperEndpointRegistry") ··· 997 1132 , true, true 998 1133 )) 999 1134 case i => throw MishapInvalidIota.ofType(i, 0, "pigment") 1000 - def mkNbtLiftAction[T: FromIota](lift: T => NbtElement, expected: Identifier) = 1001 - Patterns.mkConstAction(1): 1002 - case Seq(iotaLike[T](x)) => Seq(NbtIota(lift(x))) 1003 - case Seq(x) => throw MishapInvalidIota.of(x, 0, expected.toString) 1004 - def mkNbtLiftArrayAction[T: FromIota](lift: T => NbtElement, liftArray: Array[T] => NbtElement, expected: Identifier)(using FromIota[Array[T]]) = 1005 - Patterns.mkConstAction(1): 1006 - case Seq(iotaLike[T](x)) => Seq(NbtIota(lift(x))) 1007 - case Seq(iotaLike[Array[T]](x)) => Seq(NbtIota(liftArray(x))) 1008 - case Seq(x) => throw MishapInvalidIota.of(x, 0, expected.toString) 1009 - Patterns.register("nbt/lift1", (HexDir.NORTH_WEST, "edwaqw")): 1010 - mkNbtLiftArrayAction[Byte](NbtByte.of, (b => NbtByteArray(b)): Array[Byte] => NbtElement, "byte") 1011 - Patterns.register("nbt/lift2", (HexDir.NORTH_WEST, "edwaqww")): 1012 - mkNbtLiftAction[Short](NbtShort.of, "short") 1013 - Patterns.register("nbt/lift4", (HexDir.NORTH_WEST, "edwaqwww")): 1014 - mkNbtLiftArrayAction[Int](NbtInt.of, (b: Array[Int]) => NbtIntArray(b), "int") 1015 - Patterns.register("nbt/lift8", (HexDir.NORTH_WEST, "edwaqwwww")): 1016 - mkNbtLiftArrayAction[Long](NbtLong.of, (b: Array[Long]) => NbtLongArray(b), "long") 1017 - Patterns.register("nbt/liftf", (HexDir.NORTH_WEST, "edwaqwaa")): 1018 - mkNbtLiftAction[Float](NbtFloat.of, "float") 1019 - Patterns.register("nbt/liftd", (HexDir.NORTH_WEST, "edwaqwaawaa")): 1020 - mkNbtLiftAction[Double](NbtDouble.of, "double") 1021 - Patterns.register("nbt/literal/collection", (HexDir.EAST, "qqddqdewqaeaaee")): 1022 - Patterns.mkLiteral(NbtIota(NbtCompound())) 1023 - Patterns.register("nbt/literal/list", (HexDir.EAST, "eedwaqq")): 1024 - Patterns.mkLiteral(NbtIota(NbtList())) 1025 - Patterns.register("nbt/literal/array1", (HexDir.EAST, "eedwaqqe")): 1026 - Patterns.mkLiteral(NbtIota(NbtByteArray(Array[Byte]()))) 1027 - Patterns.register("nbt/literal/array2", (HexDir.EAST, "eedwaqqew")): 1028 - Patterns.mkLiteral(NbtIota(NbtIntArray(Array[Int]()))) 1029 - Patterns.register("nbt/literal/array4", (HexDir.EAST, "eedwaqqewww")): 1030 - Patterns.mkLiteral(NbtIota(NbtLongArray(Array[Long]()))) 1031 1135 Patterns.register("prop_fi", sw"aawqe"): 1032 1136 Patterns.mkConstAction(1): 1033 1137 case Seq(x: PropertyIota) => Seq(PropertyAccessIota.Writer(x.getName, "head")) ··· 1044 1148 Patterns.mkConstAction(1): 1045 1149 case Seq(x: PropertyIota) => Seq(PropertyAccessIota.Stream(x.getName, "tail")) 1046 1150 case Seq(x) => throw MishapInvalidIota(x, 0, "property") 1047 - Patterns.register("nbt/serialize", nw"edwaq"): 1048 - Patterns.mkConstAction(1): 1049 - case Seq(x: Iota) => Seq(IotaType.serialize(x)) 1050 1151 Patterns.register("where", nw"qaeaqwdd"): 1051 1152 Patterns.mkConstAction(1): i => 1052 1153 val Seq(x) = i ··· 1065 1166 case _ => mishap 1066 1167 )) 1067 1168 case _ => mishap 1068 - Patterns.register("tripwire", w"edewqwaqede"): 1069 - Patterns.mkLiteral(TripwireIota) 1070 1169 Patterns.arithmetic("modulo", goodModulo) 1071 1170 Registry.register(hexXplat.getArithmeticRegistry, "goodModulo": Identifier, arith("goodModulo", 1072 1171 goodModulo -> ((x: DoubleIota, y: DoubleIota) => Seq(DoubleIota((x.getDouble % y.getDouble + y.getDouble) % y.getDouble))), ··· 1078 1177 Patterns.register("spellmind/restore", e"deeeeeqdwewewewewewqdwwewwewwewwewwewwqdwwwewwwewwwewwwewwwewww"): 1079 1178 Patterns.mkAction: (img, cont) => 1080 1179 ??? 1081 - Patterns.register("whatthefuck", ne"daadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadadda"): 1082 - Patterns.mkLiteral(PatternIota(e"wedqawqeewdeaqeewdeaqqedqawqqedqawqeedqawqqewdeaqeedqawqeewdeaqqewdeaqeewdeaqeedqawqqedqawqqewdeaqeedqawqeewdeaqqewdeaqeewdeaqeedqawqqedqawqqewdeaqqedqawqeewdeaqeewdeaqqedqawqqedqawqeedqawqqewdeaqqedqawqeewdeaqeewdeaqqedqawqqedqawqeedqawqqewdeaqeedqawqeewdeaqeewdeaqqedqawqqedqawqeedqawqqewdeaqqedqawqeewdeaqqewdeaqeewdeaqeedqawqqedqawqqewdeaqe")) 1083 - Patterns.register("nbt/deserialize", (HexDir.NORTH_WEST, "edwaqa")): 1084 - Patterns.mkConstAction(1): 1085 - case Seq(data: NbtIota) => 1086 - val env = summon[CastingEnvironment] 1087 - given ServerWorld = env.getWorld 1088 - val iota = data.data.asInstanceOf[NbtCompound].iota 1089 - iota match 1090 - case p: EntityIota => p.getEntity match 1091 - case t: PlayerEntity if t != env.getCastingEntity => 1092 - throw MishapOthersName(t) 1093 - case _ => 1094 - case _ => 1095 - Seq(iota) 1096 - case Seq(x) => 1097 - throw MishapInvalidIota(x, 0, Text.literal("an ").append(Text.literal("NBT compound").styled(_.withColor(NbtIota.color)))) 1098 - Registry.register(hexXplat.getArithmeticRegistry, "nbt": Identifier, { 1099 - import Arithmetic.* 1100 - given Conversion[NbtIota, NbtElement] = _.data 1101 - given Conversion[NbtElement, NbtIota] = NbtIota(_) 1102 - arith("nbt", 1103 - ADD -> ((a: NbtIota, b: Iota) => 1104 - Seq[NbtIota]: 1105 - (a.data, b) match 1106 - case (a: NbtDouble, iotaLike[Double](b)) => NbtDouble.of(a.doubleValue + b) 1107 - case (a: NbtFloat, iotaLike[Float](b)) => NbtFloat.of(a.floatValue + b) 1108 - case (a: NbtLong, iotaLike[Long](b)) => NbtLong.of(a.longValue + b) 1109 - case (a: NbtInt, iotaLike[Int](b)) => NbtInt.of(a.intValue + b) 1110 - case (a: NbtShort, iotaLike[Short](b)) => NbtShort.of((a.shortValue + b).toShort) 1111 - case (a: NbtByte, iotaLike[Byte](b)) => NbtByte.of((a.byteValue + b).toByte) 1112 - case (a: NbtByteArray, iotaLike[Seq[Byte]](b)) => NbtByteArray(a.getByteArray ++ b) 1113 - case (a: NbtIntArray, iotaLike[Seq[Int]](b)) => NbtIntArray(a.getIntArray ++ b) 1114 - case (a: NbtLongArray, iotaLike[Seq[Long]](b)) => NbtLongArray(a.getLongArray ++ b) 1115 - case (a: NbtList, iotaLike[Seq[NbtElement]](b)) => NbtList().tap: l => 1116 - l.addAll(a) 1117 - l.addAll(b) 1118 - case (a: NbtCompound, iotaLike[NbtCompound](b)) => NbtCompound().tap: c => 1119 - c.copyFrom(a) 1120 - c.copyFrom(b) 1121 - ), 1122 - SUB -> ((a: NbtIota, b: Iota) => 1123 - Seq[NbtIota]: 1124 - (a.data, b) match 1125 - case (a: NbtDouble, iotaLike[Double](b)) => NbtDouble.of(a.doubleValue - b) 1126 - case (a: NbtFloat, iotaLike[Float](b)) => NbtFloat.of(a.floatValue - b) 1127 - case (a: NbtLong, iotaLike[Long](b)) => NbtLong.of(a.longValue - b) 1128 - case (a: NbtInt, iotaLike[Int](b)) => NbtInt.of(a.intValue - b) 1129 - case (a: NbtShort, iotaLike[Short](b)) => NbtShort.of((a.shortValue - b).toShort) 1130 - case (a: NbtByte, iotaLike[Byte](b)) => NbtByte.of((a.byteValue - b).toByte) 1131 - ), 1132 - MUL -> ((a: NbtIota, b: Iota) => 1133 - Seq[NbtIota]: 1134 - (a.data, b) match 1135 - case (a: NbtDouble, iotaLike[Double](b)) => NbtDouble.of(a.doubleValue * b) 1136 - case (a: NbtFloat, iotaLike[Float](b)) => NbtFloat.of(a.floatValue * b) 1137 - case (a: NbtLong, iotaLike[Long](b)) => NbtLong.of(a.longValue * b) 1138 - case (a: NbtInt, iotaLike[Int](b)) => NbtInt.of(a.intValue * b) 1139 - case (a: NbtShort, iotaLike[Short](b)) => NbtShort.of((a.shortValue * b).toShort) 1140 - case (a: NbtByte, iotaLike[Byte](b)) => NbtByte.of((a.byteValue * b).toByte) 1141 - ), 1142 - DIV -> ((a: NbtIota, b: Iota) => 1143 - Seq[NbtIota]: 1144 - (a.data, b) match 1145 - case (a: NbtDouble, iotaLike[Double](b)) => NbtDouble.of(a.doubleValue / b) 1146 - case (a: NbtFloat, iotaLike[Float](b)) => NbtFloat.of(a.floatValue / b) 1147 - case (a: NbtLong, iotaLike[Long](b)) => NbtLong.of(a.longValue / b) 1148 - case (a: NbtInt, iotaLike[Int](b)) => NbtInt.of(a.intValue / b) 1149 - case (a: NbtShort, iotaLike[Short](b)) => NbtShort.of((a.shortValue / b).toShort) 1150 - case (a: NbtByte, iotaLike[Byte](b)) => NbtByte.of((a.byteValue / b).toByte) 1151 - ), 1152 - INDEX -> ((a: NbtIota, b: DoubleIota | StringIota) => 1153 - Seq[NbtIota]: 1154 - (a.data, b) match 1155 - case (a: AbstractNbtList[? <: NbtElement], b: DoubleIota) => a.get(b.asIntOrThrow(0)) 1156 - case (a: NbtCompound, iotaLike[String](b)) => a.get(b) 1157 - ), 1158 - SLICE -> ((a: NbtIota, f: DoubleIota | StringIota, t: DoubleIota) => 1159 - Seq[NbtIota]: 1160 - (a.data, f, t) match 1161 - case (a: NbtByteArray, b: DoubleIota, c: DoubleIota) => (a: Array[Byte]).slice(b `asIntOrThrow` 1, c `asIntOrThrow` 2): NbtByteArray 1162 - case (a: NbtIntArray, b: DoubleIota, c: DoubleIota) => (a: Array[Int]).slice(b `asIntOrThrow` 1, c `asIntOrThrow` 2): NbtIntArray 1163 - case (a: NbtLongArray, b: DoubleIota, c: DoubleIota) => (a: Array[Long]).slice(b `asIntOrThrow` 1, c `asIntOrThrow` 2): NbtLongArray 1164 - case (a: NbtList, b: DoubleIota, c: DoubleIota) => 1165 - val l = NbtList() 1166 - a.slice(b `asIntOrThrow` 1, c `asIntOrThrow` 2).foreach(l.add) 1167 - l 1168 - case (a: NbtCompound, b: StringIota, c: StringIota) => 1169 - NbtCompound().tap: r => 1170 - a.getKeys.collect: 1171 - case k if k <= b.getString && k > c.getString => r(k) = a(k) 1172 - ), 1173 - INDEX_OF -> ((a: NbtIota, b: NbtIota) => 1174 - Seq[NbtIota]: 1175 - (a.data, b.data) match 1176 - case (a: AbstractNbtList[? <: NbtElement], b: NbtElement) => NbtInt.of(a.indexOf(b)) 1177 - case (a: NbtCompound, b: NbtElement) => 1178 - val list = NbtList() 1179 - a.getKeys.foreach: k => 1180 - if a.get(k) == b then 1181 - list.add(NbtString.of(k)) 1182 - NbtIota(list) 1183 - ), 1184 - APPEND -> ((a: NbtIota, b: NbtIota) => 1185 - Seq[NbtIota]: 1186 - (a.data, b.data) match 1187 - case (a: AbstractNbtList[t], b) if b.isInstanceOf[t] => 1188 - a.copy().tap: 1189 - case c: AbstractNbtList[t] => 1190 - c.add(b.asInstanceOf[t]) 1191 - ), 1192 - UNAPPEND -> ((a: NbtIota) => 1193 - a.data match 1194 - case a: AbstractNbtList[?] => 1195 - val s = a.asScala 1196 - Seq[NbtIota](NbtList().tap(_.addAll(s.init)), s.last) 1197 - case c: NbtCompound => 1198 - val k = c.getKeys.asScala.toBuffer 1199 - Seq[NbtIota]( 1200 - NbtCompound().tap: d => 1201 - k.init.foreach: k => 1202 - d(k) = c(k), 1203 - NbtString.of(k.last), 1204 - c(k.last), 1205 - ) 1206 - ), 1207 - CONS -> ((a: NbtIota, b: Iota) => 1208 - Seq[NbtIota]: 1209 - (a.data, b) match 1210 - case (nbtList(Tagged(a: AbstractNbtList[t], given _)), b) => 1211 - b match 1212 - case iotaLike[t](b) => a.copy.asInstanceOf[AbstractNbtList[t]].tap(_.add(0, b)) 1213 - ), 1214 - UNCONS -> ((a: NbtIota) => 1215 - a.data match 1216 - case a: AbstractNbtList[?] => 1217 - val s = a.asScala 1218 - Seq[NbtIota](NbtList().tap(_.addAll(s.tail)), s.head) 1219 - case c: NbtCompound => 1220 - val k = c.getKeys.asScala.toBuffer 1221 - Seq[NbtIota]( 1222 - NbtCompound().tap: d => 1223 - k.tail.foreach: k => 1224 - d(k) = c(k), 1225 - NbtString.of(k.head), 1226 - c(k.head), 1227 - ) 1228 - ), 1229 - REMOVE -> ((a: NbtIota, key: Iota) => 1230 - a match 1231 - case nbtList(Tagged(l: AbstractNbtList[t], given _)) => 1232 - key match 1233 - case iotaLike[Int](i) => 1234 - val c: l.type = l.copy.asInstanceOf 1235 - c.remove(i) 1236 - Seq(NbtIota(c)) 1237 - case _ => throw MishapInvalidIota.of(key, 0, "int") 1238 - ), 1239 - REPLACE -> ((t: NbtIota, k: Iota, v: NbtIota) => 1240 - Seq: 1241 - NbtIota: 1242 - t.data.copy.tap: 1243 - case c: NbtCompound => 1244 - c(k.asValue[String](1, StringIota.TYPE.typeName)) = v.data 1245 - case l: AbstractNbtList[t] => 1246 - given ClassTag[t] = elementTag(l) 1247 - l(k.asValue[Int](1, Text.translatable("hexcasting.iota.int"))) = ??? 1248 - ), 1249 - ) 1250 - }) 1251 1180 def fox(tr: PlayerEntity ?=> PartialFunction[Option[FoxEntity.Type], Option[FoxEntity.Type]]): Action = 1252 1181 Patterns.mkAction: (img, cont) => 1253 1182 img.getStack.lastOption match ··· 1286 1215 server.submit((() => handle.asWorld.getChunkManager.setChunkForced(ChunkPos.ORIGIN, true)): Runnable) 1287 1216 handle 1288 1217 ) 1289 - extension (w: ServerWorld) 1290 - def meta: String MutableFunction Option[String] = 1291 - val path = w.getServer.getSavePath(WorldSavePath.ROOT).resolve(s"dimensions/${w.getRegistryKey.getValue.getNamespace}/${w.getRegistryKey.getValue.getPath}") 1292 - new MutableFunction: 1293 - def apply(name: String) = Option(Files.getAttribute(path, "user:" + name)).asInstanceOf[Option[Array[Byte]]].map(buf => String(buf, StandardCharsets.UTF_8)) 1294 - def update(name: String, value: Option[String]): Unit = Files.setAttribute(path, "user:" + name, value.map(StandardCharsets.UTF_8.encode).orNull) 1295 - def parentInfo: Option[(RegistryKey[World], BlockPos)] = w.meta("parent").flatMap(value => Option(Identifier.tryParse(value))).map(RegistryKey.of(RegistryKeys.WORLD, _)).filter(w.getServer.getWorld(_) ne null).map((_, BlockPos(Integer.parseInt(w.meta("bound_x").get), Integer.parseInt(w.meta("bound_y").get), Integer.parseInt(w.meta("bound_z").get)))) 1296 - def parentInfo_=(parent: Option[(RegistryKey[World], BlockPos)]) = 1297 - parent.fold { 1298 - w.meta("parent") = None 1299 - w.meta("bound_x") = None 1300 - w.meta("bound_y") = None 1301 - w.meta("bound_z") = None 1302 - } { (world, pos) => 1303 - w.meta("parent") = Some(world.getValue.toString) 1304 - w.meta("bound_x") = Some(pos.getX.toString) 1305 - w.meta("bound_y") = Some(pos.getY.toString) 1306 - w.meta("bound_z") = Some(pos.getZ.toString) 1307 - } 1308 1218 extension (server: MinecraftServer) 1309 1219 def savedPlanes = 1310 1220 val file = server.getSavePath(WorldSavePath("fresh")) ··· 1426 1336 for id <- server.savedPlanes do 1427 1337 planes(id) 1428 1338 Registries.BLOCK("border") = border 1429 - Patterns.register("makeworld", ne"qaaqqwaeddeawqqaaqqwwwaeddeewdqaaqdweeddeawwwqqaaqqwaeddeawqqaaqawwwwwwwawwwwwww"): 1430 - Patterns.mkConstAction(argc = 0, mediaCost = MediaConstants.SHARD_UNIT * 3645): _ => 1339 + Patterns.register("makeworld", e"qaaqqwaeddeawqqaaqqwwwaeddeewdqaaqdweeddeawwwqqaaqqwaeddeawqqaaqawwwwwwwawwwwwww"): 1340 + Patterns.mkConstAction(argc = 0, mediaCost = MediaConstants.QUENCHED_BLOCK_UNIT * 6): _ => 1431 1341 val uuid = UUID.randomUUID() 1432 1342 val world = planes(uuid).asWorld 1433 1343 // TODO: world config ··· 1531 1441 case p: PlayerEntity => 1532 1442 // thanks but please stop eating my ears 1533 1443 if isDev then println(s"Teleporting player $p") 1534 - FabricDimensions.teleport(p, outer, TeleportTarget(pos, Vec3d.ZERO, p.getYaw, p.getPitch)) 1535 - if !p.isCreative && !p.isSpectator then p.kill() 1444 + val p2 = FabricDimensions.teleport(p, outer, TeleportTarget(pos, Vec3d.ZERO, p.getYaw, p.getPitch)) 1445 + if !p2.isCreative && !p2.isSpectator then p2.kill() 1536 1446 case e: LivingEntity => 1537 1447 e.kill() 1538 1448 if isDev then println(s"Killing living entity $e") ··· 1543 1453 (e: LivingEntityAccess).callUpdatePostDeath() 1544 1454 case e => 1545 1455 if isDev then println(s"Killing nonliving entity $e") 1546 - FabricDimensions.teleport(e, outer, TeleportTarget(pos, Vec3d.ZERO, e.getYaw, e.getPitch)) 1547 - e.kill() 1456 + val e2 = FabricDimensions.teleport(e, outer, TeleportTarget(pos, Vec3d.ZERO, e.getYaw, e.getPitch)) 1457 + e2.kill() 1548 1458 pass += 1 1549 1459 if isDev then println(s"Proceeding to pass ${pass}") 1550 1460 recurse ··· 1560 1470 if isDev then println(s"Spawning final XP orb, $xpToSpawn") 1561 1471 ExperienceOrbEntity.spawn(outer, pos, xpToSpawn.toInt) 1562 1472 catch case e: Throwable => 1563 - println(s"FAILED TO REMOVE DIMENSION!! $id. Not unloading.") 1473 + println(s"\u0007FAILED TO REMOVE DIMENSION!! $id. Not unloading.") 1564 1474 e.printStackTrace() 1565 1475 boundary.break() 1566 1476 throw e ··· 1568 1478 server.savedPlanes -= id 1569 1479 planes(id).unload() 1570 1480 override def cast(env: CastingEnvironment, image: CastingImage): CastingImage = { cast(env); image }, 1571 - MediaConstants.SHARD_UNIT * 750, 1481 + MediaConstants.SHARD_UNIT * 25, 1572 1482 Seq(), 1573 1483 1 1574 1484 ) ··· 1577 1487 override def executeWithUserdata(list: util.List[? <: Iota], env: CastingEnvironment, data: NbtCompound): SpellAction.Result = SpellAction.DefaultImpls.executeWithUserdata(this, list, env, data) 1578 1488 override def hasCastingSound(env: CastingEnvironment): Boolean = true 1579 1489 override def operate(env: CastingEnvironment, castingImage: CastingImage, cont: SpellContinuation): OperationResult = SpellAction.DefaultImpls.operate(this, env, castingImage, cont) 1580 - Patterns.register("snow", nw"eewwweewdadadaddwwwaqwwq"): 1490 + Patterns.register("omni_open", w"qdaqadq"): 1581 1491 Patterns.mkAction: (img, cont) => 1582 - (img, cont, HexEvalSounds.SPELL, Seq( 1583 - OperatorSideEffect.ConsumeMedia(12 * MediaConstants.DUST_UNIT), 1584 - OperatorSideEffect.AttemptSpell( 1585 - new RenderedSpell: 1586 - override def cast(env: CastingEnvironment): Unit = 1587 - given MinecraftServer = env.getWorld.getServer 1588 - val duration = env.getWorld.random.nextBetween(15, 30) * 20 * 60 1589 - env.getWorld.setWeather(0, duration, true, false) 1590 - ServerInfoComponent.get.endSnowTick = env.getWorld.getTime + duration 1591 - ServerInfoComponent.sync() 1592 - override def cast(env: CastingEnvironment, img: CastingImage): CastingImage = { cast(env); img } 1593 - , true, true) 1594 - )) 1492 + (img.getStack.toSeq: Seq[Iota]) match 1493 + case stack:+allegedCount => 1494 + val count = OperatorUtils.getPositiveInt(Seq(allegedCount), 0, 1) 1495 + (new CastingImage(stack, count, Seq(), false, img.getOpsConsumed, img.getUserData, null), cont, HexEvalSounds.NORMAL_EXECUTE, Seq()) 1496 + Patterns.register("omni_close", e"eadedae"): 1497 + Patterns.mkConstAction(0): 1498 + case Seq() => Seq(ListIota(Seq()), DoubleIota(0)) 1595 1499 Patterns.register("staffcast_factory", ne"wwwwwaqqqqqeaqeaeaeaeaeq"): 1596 1500 Patterns.mkAction: (img, cont) => 1597 1501 summon[CastingEnvironment].getCastingEntity match ··· 1666 1570 case _ => true 1667 1571 val sorted = others.toSeq.sortBy(_.getPos.squaredDistanceTo(summon[CastingEnvironment].mishapSprayPos)) 1668 1572 sorted.headOption.fold(NullIota())(EntityIota(_)) 1573 + Patterns.register("blind", se"qqqqqadwawawd")(OpPotionEffect(StatusEffects.BLINDNESS, 1000, false, false)) 1669 1574 Patterns.register("erase", e"wqwdwqwawwwwwawwwww"): 1670 1575 Patterns.mkAction: (img, cont) => 1671 1576 def mkResult(scale: Int, pos: => Vec3d, spell: => Unit) = ··· 1860 1765 case Seq(i, _: ListIota, _*) => throw MishapInvalidIota.ofType(i, 0, "list") 1861 1766 case Seq(_, i, _*) => throw MishapInvalidIota.ofType(i, 1, "list") 1862 1767 case s => throw MishapNotEnoughArgs(2, s.size) 1863 - lazy val messageFrameType: ContinuationFrame.Type[MessageFrame] = (c: NbtCompound, world: ServerWorld) => 1864 - val id = Uuids.toUuid(c.getIntArray("id")) 1865 - MessageFrame(id, Text.Serializer.fromJson(NbtOps.INSTANCE.convertTo(JsonOps.INSTANCE, c.getCompound("t"))), world.getServer.getPlayerManager.getPlayer(id)) 1866 - class MessageFrame(id: UUID, text: Text, player: => ServerPlayerEntity) extends ContinuationFrame: 1867 - override def getType: ContinuationFrame.Type[MessageFrame] = messageFrameType 1868 - override def evaluate(rest: SpellContinuation, world: ServerWorld, vm: CastingVM): CastResult = 1869 - boundary: 1870 - def mishap(m: Mishap) = boundary.break(CastResult(NullIota(), rest, vm.getImage, Seq(DoMishap(m, Mishap.Context(null, text))), ResolvedPatternType.EVALUATED, HexEvalSounds.NORMAL_EXECUTE)) 1871 - vm.getImage.getStack.toSeq.reverse match 1872 - case Seq() => 1873 - mishap(MishapNotEnoughArgs(1, 0)) 1874 - case Seq(s: StringIota, stack*) => 1875 - CastResult(NullIota(), rest, vm.getImage.withStack(_ => stack), Seq( 1876 - OperatorSideEffect.AttemptSpell( 1877 - new RenderedSpell: 1878 - override def cast(env: CastingEnvironment): Unit = 1879 - ServerPlayNetworking.send(player, "msg", PacketByteBufs.create.tap(_.writeString(s.getString))) 1880 - override def cast(env: CastingEnvironment, img: CastingImage): CastingImage = { cast(env); img } 1881 - , false, false 1882 - ) 1883 - ), ResolvedPatternType.EVALUATED, HexEvalSounds.NORMAL_EXECUTE) 1884 - case Seq(i, _*) => 1885 - mishap(MishapInvalidIota.ofType(i, 0, "string")) 1886 - override def serializeToNBT(): NbtCompound = NbtCompound() 1887 - .tap(_.putIntArray("id", Uuids.toIntArray(id))) 1888 - .tap(_.put("t", JsonOps.INSTANCE.convertTo(NbtOps.INSTANCE, Text.Serializer.toJsonTree(text)))) 1889 - override def breakDownwards(stack: ju.List[? <: Iota]): Pair[java.lang.Boolean, ju.List[Iota]] = Pair(false, stack.toSeq) 1890 - override def size = 0 1891 - hexXplat.getContinuationTypeRegistry("send_message") = messageFrameType 1892 1768 CastingEnvironment.addCreateEventListener: (env: CastingEnvironment, data: NbtCompound) => 1893 1769 val id = env.getWorld.getRegistryKey.getValue 1894 1770 if isDev then println(s"Environment created in $id") ··· 1907 1783 val x = pos.getComponentAlongAxis(axis) 1908 1784 if x < 0 || x >= 11 then boundary.break(false) 1909 1785 true 1910 - ServerPlayNetworking.registerGlobalReceiver("sync_mediaweave", (_, player, _, buf, _) => 1911 - val flags = buf.readByte() 1912 - val c = player.getComponent(PlayerInfoComponent.key) 1913 - val cursorStack = player.playerScreenHandler.getCursorStack() 1914 - def currentWeave = 1915 - if (flags & 4) != 0 then 1916 - c.leftWeave 1917 - else 1918 - c.rightWeave 1919 - lazy val lastWeave = currentWeave 1920 - if (flags & 1) != 0 && (flags & 8) == 0 then 1921 - assume(cursorStack.isEmpty || (flags & 2) != 0, "if this isn't empty, the player's held item gets voided") 1922 - buf.readItemStack() 1923 - player.playerScreenHandler.setCursorStack(lastWeave.copyAndEmpty()) 1924 - PlayerInfoComponent.key.sync(player) 1925 - if (flags & 2) != 0 then 1926 - buf.readItemStack() 1927 - assume(currentWeave.isEmpty, "if this isn't empty, an equipped mediaweave gets voided") 1928 - if (flags & 4) != 0 then 1929 - c.leftWeave = cursorStack 1930 - else 1931 - c.rightWeave = cursorStack 1932 - PlayerInfoComponent.key.sync(player) 1933 - if (flags & 8) != 0 then 1934 - val text = buf.readString() 1935 - val c = player.getComponent(PlayerInfoComponent.key) 1936 - val stack = if (flags & 4) != 0 then c.leftWeave else c.rightWeave 1937 - stack.getItem match 1938 - case m@Mediaweave(color) => m.readIotaTag(stack) match 1939 - case t: NbtCompound => IotaType.deserialize(t, player.getServerWorld) match 1940 - case s: ListIota => 1941 - given ServerWorld = env.getWorld 1942 - lazy val env = new PlayerBasedCastEnv(player, 1943 - if player.getMainArm match 1944 - case Arm.LEFT => (flags & 4) != 0 1945 - case Arm.RIGHT => (flags & 4) == 0 1946 - then Hand.MAIN_HAND else Hand.OFF_HAND 1947 - ): 1948 - override def extractMediaEnvironment(cost: Long, simulate: Boolean): Long = 1949 - if player.isCreative then 0L else extractMediaFromInventory(cost, canOvercast, simulate) 1950 - override def getCastingHand: Hand = castingHand 1951 - override def getPigment = FrozenPigment(ItemStack(HexItems.DYE_PIGMENTS.get(color)), Util.NIL_UUID) 1952 - val context = 1953 - if (flags & 1) != 0 then 1954 - Seq.fill(buf.readInt)(buf.readUnlimitedNbt: Iota) 1955 - else 1956 - Seq() 1957 - val image = CastingImage(context :+ StringIota.make(text), 0, Seq(), false, 0, NbtCompound(), null) 1958 - val instrs = s.getList.asScala.toSeq 1959 - val vm = CastingVM(image, env) 1960 - val view = vm.queueExecuteAndWrapIotas(instrs :+ ContinuationIota(SpellContinuation.NotDone(MessageFrame(player.getUuid, stack.getName, player), SpellContinuation.Done.INSTANCE)), player.getServerWorld) 1961 - val packet = MsgNewSpiralPatternsS2C(player.getUuid, instrs.collect { case p: PatternIota => p.getPattern }.asJava, 140) 1962 - hexXplat.sendPacketToPlayer(player, packet) 1963 - hexXplat.sendPacketTracking(player, packet) 1964 - case _ => 1965 - case null => 1966 - case _ =>) 1967 1786 // dump patterns 1968 1787 val out = Files.newOutputStream(Path.of("patterns.csv")) 1969 1788 try ··· 1982 1801 given [T] => Conversion[Const[T], T] = _.value 1983 1802 1984 1803 private[hexic] class ComponentInit extends EntityComponentInitializer, LevelComponentInitializer: 1985 - override def registerEntityComponentFactories(using EntityComponentFactoryRegistry): Unit = 1804 + override def registerEntityComponentFactories(using r: EntityComponentFactoryRegistry): Unit = 1986 1805 PlayerInfoComponent.register 1806 + r.registerForPlayers(summon[ComponentKey[MurmurCache]], _ => MurmurCache(None), RespawnCopyStrategy.ALWAYS_COPY) 1807 + r.registerForPlayers(summon[ComponentKey[ExcursionComponent]], _ => ExcursionComponent(), RespawnCopyStrategy.ALWAYS_COPY) 1808 + r.registerForPlayers(summon[ComponentKey[RevealComponent]], _ => RevealComponent(Seq.empty), RespawnCopyStrategy.LOSSLESS_ONLY) 1809 + r.registerForPlayers(CatHolder.key, new CatHolder(_), RespawnCopyStrategy.NEVER_COPY) 1987 1810 override def registerLevelComponentFactories(using LevelComponentFactoryRegistry): Unit = 1988 1811 ServerInfoComponent.register 1989 1812 ··· 2051 1874 def unapply[T <: NbtElement](l: AbstractNbtList[T]): Some[ClassTag[T]] = Some(elementTag(l)) 2052 1875 2053 1876 private[hexic] object cfg: 1877 + locally: 1878 + Using.resource(Files.list(Path.of("config/"))): dir => 1879 + dir.forEach: file => 1880 + if file.toString.endsWith(".properties") then 1881 + try 1882 + System.getProperties.load(Files.newBufferedReader(file, Charsets.UTF_8)) 1883 + catch 1884 + case i: IOException => summon[Logger].warn(s"Failed to read properties from $file", i) 2054 1885 def apply[T: FromString as t](key: String): Option[T] = 2055 1886 sys.props.get(key).map(t.fromString) 2056 1887 def flag(key: String): Boolean = cfg[Boolean](s"hexic.$key").contains(true) ··· 2091 1922 given FromIota[Iota] = Some(_) 2092 1923 given FromIota[String] = FromIota.lift: 2093 1924 case s: StringIota => s.getString 2094 - case NbtIota(s: NbtString) => s.asString 2095 1925 given FromIota[Boolean] = FromIota.lift: 2096 1926 case b: BooleanIota => b.getBool 2097 - case NbtIota(n: AbstractNbtNumber) => n.longValue != 0 2098 1927 given [T: ClassTag](using elems: FromIota[T]): FromIota[Seq[T]] = FromIota.liftFlat: 2099 1928 case l: ListIota => 2100 1929 boundary: ··· 2103 1932 case Some(p) => b.add(p) 2104 1933 case None => boundary.break(None) 2105 1934 Some(b.toSeq) 2106 - given arrayByteFromIota: FromIota[Array[Byte]] = FromIota.lift: 2107 - case NbtIota(t: NbtByteArray) => t.getByteArray 2108 - given arrayIntFromIota: FromIota[Array[Int]] = FromIota.lift: 2109 - case NbtIota(t: NbtIntArray) => t.getIntArray 2110 - given arrayLongFromIota: FromIota[Array[Long]] = FromIota.lift: 2111 - case NbtIota(t: NbtLongArray) => t.getLongArray 2112 - given [T <: NbtElement: ClassTag]: FromIota[T] = FromIota.lift: 2113 - case NbtIota(data: T) => data 2114 1935 given FromIota[Double] = FromIota.lift: 2115 1936 case d: DoubleIota => d.getDouble 2116 1937 given FromIota[Float] = FromIota.lift: 2117 1938 case d: DoubleIota if (d.getDouble.round.toFloat - d.getDouble) < DoubleIota.TOLERANCE => d.getDouble.round.toFloat 2118 - case NbtIota(n: NbtFloat) => n.floatValue 2119 1939 given FromIota[Byte] = FromIota.lift: 2120 1940 case d: DoubleIota if d.getDouble < Byte.MaxValue && d.getDouble > Byte.MinValue && (d.getDouble.round.toByte - d.getDouble) < DoubleIota.TOLERANCE => d.getDouble.round.toByte 2121 - case NbtIota(n: AbstractNbtNumber) => n.byteValue 2122 1941 given FromIota[Short] = FromIota.lift: 2123 1942 case d: DoubleIota if d.getDouble < Short.MaxValue && d.getDouble > Short.MinValue && (d.getDouble.round.toShort - d.getDouble) < DoubleIota.TOLERANCE => d.getDouble.round.toShort 2124 - case NbtIota(n: AbstractNbtNumber) => n.shortValue 2125 1943 given FromIota[Int] = FromIota.lift: 2126 1944 case d: DoubleIota if d.getDouble < Int.MaxValue && d.getDouble > Int.MinValue && (d.getDouble.round.toInt - d.getDouble) < DoubleIota.TOLERANCE => d.getDouble.round.toInt 2127 - case NbtIota(n: AbstractNbtNumber) => n.intValue 2128 1945 given FromIota[Long] = FromIota.lift: 2129 1946 case d: DoubleIota if d.getDouble < Long.MaxValue && d.getDouble > Long.MinValue && (d.getDouble.round - d.getDouble) < DoubleIota.TOLERANCE => d.getDouble.round 2130 - case NbtIota(n: AbstractNbtNumber) => n.longValue 2131 1947 2132 1948 object nbtList: 2133 1949 def unapply(l: NbtElement): Option[Tagged[AbstractNbtList, NbtElement]] = ··· 2144 1960 given Conversion[NbtByteArray, Array[Byte]] = _.getByteArray 2145 1961 given Conversion[NbtIntArray, Array[Int]] = _.getIntArray 2146 1962 given Conversion[NbtLongArray, Array[Long]] = _.getLongArray 2147 - 2148 - case class MishapWrongDimensionKiddo(val dim1: RegistryKey[World], val dim2: RegistryKey[World]) extends Mishap: 2149 - override def accentColor(using CastingEnvironment, Mishap.Context): FrozenPigment = dyeColor(DyeColor.MAGENTA) 2150 - override def errorMessage(using CastingEnvironment, Mishap.Context): Text = 2151 - val env = summon[CastingEnvironment] 2152 - val color = env.getPigment.getColorProvider.getColor(0, Vec3d.ZERO) 2153 - val mind = t"Mind".styled(_ `withColor` color) 2154 - t"My ${mind} cannot think in ${dim1.getValue `toTranslationKey` "dimension"} and ${dim2.getValue `toTranslationKey` "dimension"} at once" 2155 - override def execute(using CastingEnvironment, Mishap.Context, util.List[Iota]): Unit = ??? 2156 1963 2157 1964 trait Tagged[+F[_ <: U @uncheckedVariance], +U]: 2158 1965 type T <: U: ClassTag ··· 2244 2051 (piece +: Iterator.continually(piece).takeWhile(_ => rand.nextInt(5) == 0).toSeq).mkString("-") 2245 2052 val pocketNames = memo((id: UUID) => pocketName(using Random(id.getLeastSignificantBits))) 2246 2053 2247 - object TripwireIota extends Iota(new IotaType[TripwireIota.type]: 2248 - override def deserialize(tag: NbtElement, world: ServerWorld): TripwireIota.type = TripwireIota 2249 - override def color: Int = 0xba4216 2250 - override def display(tag: NbtElement): Text = typeName 2251 - , Object()): 2252 - override def isTruthy: Boolean = true 2253 - override def toleratesOther(that: Iota): Boolean = eq(that) 2254 - override def serialize(): NbtElement = NbtCompound() 2255 - class Frame(mishap: Mishap) extends ContinuationFrame: 2256 - override def breakDownwards(x: ju.List[? <: Iota]): Pair[java.lang.Boolean, ju.List[Iota]] = ??? 2257 - override def evaluate(cont: SpellContinuation, world: ServerWorld, vm: CastingVM): CastResult = throw mishap 2258 - override def getType: Type[?] = Frame 2259 - override def serializeToNBT(): NbtCompound = 2260 - val c = NbtCompound() 2261 - val codec = mishap.getCodec 2262 - c.putString("Class", mishap.getClass.getName) 2263 - c.put("Data", codec.encodeStart(NbtOps.INSTANCE, mishap.asInstanceOf).getOrThrow(false, _ => {})) 2264 - c 2265 - override def size: Int = 1 2266 - object Frame extends ContinuationFrame.Type[Frame]: 2267 - override def deserializeFromNBT(c: NbtCompound, world: ServerWorld): Frame = 2268 - val klass = classNamed(c.getString("Class")) 2269 - val codec = klass.get.runtimeClass.getMethod("getCodec").invoke(null).asInstanceOf[Codec[? <: Mishap]] 2270 - Frame(codec.decode(NbtOps.INSTANCE, c.get("Data")).getOrThrow(false, _ => {}).getFirst) 2271 - 2272 - case class LocationIota(vec: Vec3d, dim: Option[RegistryKey[World]]) extends Vec3Iota(vec), IotaTypeHint: 2273 - override def serialize: NbtElement = NbtCompound().tap(_.put("vec", super.serialize())).tap(n => dim.map(v => n.putString("dim", v.getValue.toString))) 2274 - override def hexic$iotaType(): IotaType[?] = LocationIota 2275 - 2276 - object LocationIota extends IotaType[LocationIota]: 2277 - override def color: Int = Vec3Iota.TYPE.color() 2278 - override def deserialize(using NbtElement, ServerWorld): LocationIota = ??? 2279 - override def display(d: NbtElement): Text = d match 2280 - case d: NbtCompound => Vec3Iota.TYPE.display(d.get("vec")) 2281 - case _ => null 2282 - 2283 2054 inline def repeat[T](inline value: T, inline cond: T => Boolean)(inline body: T => T): T = 2284 2055 var current = value 2285 2056 while (cond(current)) current = body(current) ··· 2310 2081 @targetName("hexic$MediaContainerProvider$getMediaContainer") 2311 2082 def getMediaContainer(c: Context): Option[MediaContainer] 2312 2083 2313 - case class NbtIota(data: NbtElement) extends Iota(NbtIota, data): 2314 - override def isTruthy: Boolean = data match 2315 - case d: AbstractNbtNumber => d.numberValue != 0 2316 - case a: AbstractNbtList[?] => a.size != 0 2317 - case s: NbtString => s.asString != "" 2318 - case c: NbtCompound => c.getSize != 0 2319 - case _ => true 2320 - override def toleratesOther(that: Iota): Boolean = that match 2321 - case that: NbtIota => this.data == that.data 2322 - case _ => this.data == that 2323 - override def serialize: NbtElement = data 2324 - 2325 2084 object registerHopperEndpoint extends (() => Unit): 2326 2085 def apply(): Unit = 2327 2086 HopperEndpointRegistry.INSTANCE.register: (iota: Iota, env: CastingEnvironment, slot: Integer) => ··· 2356 2115 def downcast[R: ClassTag](t: Any): Option[R] = t match 2357 2116 case r: R => Some(r) 2358 2117 case _ => None 2359 - object NbtIota extends IotaType[NbtIota]: 2360 - def name: Text = ("NBT": MutableText).styled(_.withColor(color)) 2361 - def color: Int = Formatting.DARK_AQUA.getColorValue 2362 - def deserialize(using NbtElement, ServerWorld): NbtIota = NbtIota(summon) 2363 - def display(e: NbtElement): Text = Text.literal(StringNbtWriter()(e)).styled(_.withColor(color))
-19
project/hexic/src/main/scala/org/eu/net/pool/hexic/mixin/BiomeMixin.java
··· 1 - package org.eu.net.pool.hexic.mixin; 2 - 3 - import net.minecraft.world.World; 4 - import net.minecraft.world.biome.Biome; 5 - import org.eu.net.pool.hexic.Extern; 6 - import org.eu.net.pool.hexic.ServerInfoComponent; 7 - import org.spongepowered.asm.mixin.Mixin; 8 - import org.spongepowered.asm.mixin.injection.At; 9 - import org.spongepowered.asm.mixin.injection.Inject; 10 - import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 11 - 12 - @Mixin(Biome.class) 13 - public class BiomeMixin { 14 - @Inject(at = @At("HEAD"), method = {"getTemperature()F", "getTemperature(Lnet/minecraft/util/math/BlockPos;)F"}, cancellable = true) 15 - void preGetTemperature(CallbackInfoReturnable<Float> ci) { 16 - World world = Extern.getWorld((Biome) (Object) this); 17 - if (world.getLevelProperties().getComponent(ServerInfoComponent.key()).endSnowTick() > world.getTime()) ci.setReturnValue(0.1f); 18 - } 19 - }
+20
project/hexic/src/main/scala/org/eu/net/pool/hexic/mixin/ChatMessageC2SPacketMixin.java
··· 1 + package org.eu.net.pool.hexic.mixin; 2 + 3 + import net.minecraft.network.packet.c2s.play.ChatMessageC2SPacket; 4 + import org.spongepowered.asm.mixin.Mixin; 5 + import org.spongepowered.asm.mixin.injection.At; 6 + import org.spongepowered.asm.mixin.injection.ModifyArg; 7 + import ram.talia.moreiotas.api.mod.MoreIotasConfig; 8 + 9 + @Mixin(ChatMessageC2SPacket.class) 10 + public class ChatMessageC2SPacketMixin { 11 + @ModifyArg(at = @At(value = "INVOKE", target = "Lnet/minecraft/network/PacketByteBuf;readString(I)Ljava/lang/String;"), method = "<init>(Lnet/minecraft/network/PacketByteBuf;)V") 12 + private static int modifyReadCap(int oldCap) { 13 + return Math.max(oldCap, Math.min(MoreIotasConfig.getServer().getMaxStringLength(), 32767)); 14 + } 15 + @ModifyArg(at = @At(value = "INVOKE", target = "Lnet/minecraft/network/PacketByteBuf;writeString(Ljava/lang/String;I)Lnet/minecraft/network/PacketByteBuf;"), method = "write") 16 + private int modifyWriteCap(int oldCap) { 17 + // we don't know what the server's cap is, so just use the max 18 + return 32767; 19 + } 20 + }
+1 -1
project/hexic/src/main/scala/org/eu/net/pool/hexic/mixin/DimIotaMixin.java
··· 20 20 abstract class DimIotaMixin { 21 21 @WrapOperation(method = "display", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/Text;of(Ljava/lang/String;)Lnet/minecraft/text/Text;")) 22 22 Text getName(String dim, Operation<Text> original) { 23 - return dim.startsWith("hexic:fresh_") ? org.eu.net.pool.hexic.Extern.getPocketName(dim) : original.call(dim); 23 + return dim.startsWith("hexic:fresh-") ? org.eu.net.pool.hexic.Extern.getPocketName(dim) : original.call(dim); 24 24 } 25 25 26 26 @WrapMethod(method = "deserialize(Lnet/minecraft/nbt/NbtElement;Lnet/minecraft/server/world/ServerWorld;)Lnet/beholderface/oneironaut/casting/iotatypes/DimIota;")
+27 -12
project/hexic/src/main/scala/org/eu/net/pool/hexic/mixin/EntityMixin.java
··· 1 1 package org.eu.net.pool.hexic.mixin; 2 2 3 + import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 3 4 import net.minecraft.entity.Entity; 5 + import net.minecraft.entity.EntityType; 4 6 import net.minecraft.entity.LivingEntity; 5 7 import net.minecraft.entity.damage.DamageSource; 6 8 import net.minecraft.entity.damage.DamageSources; 7 9 import net.minecraft.entity.player.PlayerEntity; 10 + import net.minecraft.server.network.ServerPlayerEntity; 8 11 import net.minecraft.util.CuboidBlockIterator; 9 12 import net.minecraft.util.math.BlockPos; 10 13 import net.minecraft.util.math.Box; 11 14 import net.minecraft.util.math.MathHelper; 12 15 import net.minecraft.util.math.Vec3d; 13 16 import net.minecraft.world.World; 17 + import org.eu.net.pool.hexic.JavaPlaneAccess; 18 + import org.objectweb.asm.Opcodes; 14 19 import org.spongepowered.asm.mixin.Mixin; 15 20 import org.spongepowered.asm.mixin.Shadow; 16 21 import org.spongepowered.asm.mixin.Unique; ··· 22 27 23 28 @Mixin(Entity.class) 24 29 public abstract class EntityMixin { 25 - @Unique BlockPos.Mutable hexic$scanPos = new BlockPos.Mutable(); 26 - 27 30 @Shadow public abstract World getWorld(); 28 31 @Shadow public abstract Box getBoundingBox(); 29 32 @Shadow private World world; ··· 35 38 36 39 @Shadow public abstract boolean damage(DamageSource source, float amount); 37 40 41 + @Shadow public abstract boolean isPlayer(); 42 + 38 43 @Inject(at = @At("TAIL"), method = {"attemptTickInVoid", "method_31473"}, cancellable = true) 39 44 void attemptTickInVoidBlocks(CallbackInfo ci) { 40 45 Box box = getBoundingBox(); 41 46 if (!((Object) this instanceof PlayerEntity p && (p.isCreative() || p.isSpectator()))) { 42 47 var id = getWorld().getRegistryKey().getValue(); 43 48 if (id.getNamespace().equals("hexic") && id.getPath().startsWith("fresh-") && (box.minX < 0 || box.minY < 0 || box.minZ < 0 || box.maxX > 11 || box.maxY > 11 || box.maxZ > 11)) { 44 - damage(getWorld().getDamageSources().outsideBorder(), Float.MAX_VALUE); 45 - remove(Entity.RemovalReason.KILLED); 49 + if ((Object) this instanceof PlayerEntity p) { 50 + if (p instanceof ServerPlayerEntity sp) { 51 + JavaPlaneAccess.sendDirectlyToHell(sp); 52 + } 53 + } else { 54 + discard(); 55 + } 46 56 ci.cancel(); 47 57 return; 48 58 } 49 59 } 50 - CuboidBlockIterator iter = new CuboidBlockIterator(MathHelper.ceil(box.minX), MathHelper.ceil(box.minY), MathHelper.ceil(box.minZ), MathHelper.ceil(box.maxX), MathHelper.ceil(box.maxY), MathHelper.ceil(box.maxZ)); 51 - while (iter.step()) { 52 - hexic$scanPos.set(iter.getX(), iter.getY(), iter.getZ()); 53 - if (world.getBlockState(hexic$scanPos).isOf(VOID_AIR)) { 54 - tickInVoid(); 55 - ci.cancel(); 56 - return; 57 - } 60 + if (BlockPos.stream(box).anyMatch(p -> world.getBlockState(p).isOf(VOID_AIR))) { 61 + tickInVoid(); 62 + ci.cancel(); 63 + return; 64 + } 65 + } 66 + 67 + // stolen from trickster 68 + @ModifyExpressionValue(method = {"handleFallDamage", "isFireImmune", "isInvulnerableTo"}, at = {@At(value = "FIELD", target = "Lnet/minecraft/entity/Entity;type:Lnet/minecraft/entity/EntityType;", opcode = Opcodes.GETFIELD), @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;getType()Lnet/minecraft/entity/EntityType;")}) 69 + private EntityType<?> modifyEntityType(EntityType<?> original) { 70 + if ((Object) this instanceof PlayerEntity) { 71 + 58 72 } 73 + return original; 59 74 } 60 75 }
+1
project/hexic/src/main/scala/org/eu/net/pool/hexic/mixin/ItemMixin.java
··· 22 22 import net.minecraft.world.World; 23 23 import org.jetbrains.annotations.Nullable; 24 24 import org.spongepowered.asm.mixin.Mixin; 25 + import org.spongepowered.asm.mixin.Unique; 25 26 import org.spongepowered.asm.mixin.injection.At; 26 27 import org.spongepowered.asm.mixin.injection.Inject; 27 28 import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+42
project/hexic/src/main/scala/org/eu/net/pool/hexic/mixin/LivingEntityMixin.java
··· 1 + package org.eu.net.pool.hexic.mixin; 2 + 3 + import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 + import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 5 + import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 6 + import net.minecraft.entity.*; 7 + import net.minecraft.entity.damage.DamageSource; 8 + import net.minecraft.entity.passive.CatEntity; 9 + import net.minecraft.sound.SoundEvent; 10 + import net.minecraft.sound.SoundEvents; 11 + import net.minecraft.world.World; 12 + import org.eu.net.pool.hexic.CatHolder; 13 + import org.spongepowered.asm.mixin.Mixin; 14 + import org.spongepowered.asm.mixin.injection.At; 15 + import org.spongepowered.asm.mixin.injection.Inject; 16 + import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 17 + 18 + // rephrased from trickster 19 + @Mixin(LivingEntity.class) 20 + public abstract class LivingEntityMixin extends Entity { 21 + public LivingEntityMixin(EntityType<?> type, World world) { 22 + super(type, world); 23 + } 24 + 25 + @Inject(method = "getDimensions", at = @At("HEAD"), cancellable = true) 26 + private void morphDimensions(EntityPose pose, CallbackInfoReturnable<EntityDimensions> cir) { 27 + LivingEntity disguise = (CatEntity) CatHolder.getCat(this); 28 + if (disguise != null) cir.setReturnValue(disguise.getDimensions(pose)); 29 + } 30 + 31 + @WrapOperation(method = {"playHurtSound", "onDamaged"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;getHurtSound(Lnet/minecraft/entity/damage/DamageSource;)Lnet/minecraft/sound/SoundEvent;")) 32 + private SoundEvent morphHurtSound(LivingEntity instance, DamageSource source, Operation<SoundEvent> original) { 33 + LivingEntity disguise = (CatEntity) CatHolder.getCat(this); 34 + return disguise != null ? SoundEvents.ENTITY_CAT_HURT : original.call(instance, source); 35 + } 36 + 37 + @ModifyExpressionValue(method = {"computeFallDamage", "canBreatheInWater"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;getType()Lnet/minecraft/entity/EntityType;")) 38 + private EntityType<?> modifyEntityType(EntityType<?> original) { 39 + LivingEntity disguise = (CatEntity) CatHolder.getCat(this); 40 + return disguise != null ? disguise.getType() : original; 41 + } 42 + }
+17 -1
project/hexic/src/main/scala/org/eu/net/pool/hexic/mixin/OpDimTeleport$SpellMixin.java
··· 3 3 import at.petrak.hexcasting.api.casting.eval.CastingEnvironment; 4 4 import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 5 5 import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 6 + import net.minecraft.entity.Entity; 7 + import net.minecraft.server.network.ServerPlayerEntity; 6 8 import net.minecraft.server.world.ServerWorld; 7 9 8 10 import net.minecraft.util.Identifier; 9 11 import net.minecraft.util.math.Vec3d; 10 12 import net.minecraft.world.dimension.DimensionType; 13 + import org.eu.net.pool.hexic.JavaPlaneAccess; 11 14 import org.spongepowered.asm.mixin.*; 12 15 import org.spongepowered.asm.mixin.injection.At; 13 16 import org.spongepowered.asm.mixin.injection.Inject; ··· 15 18 16 19 @Mixin(targets = "net.beholderface.oneironaut.casting.patterns.spells.great.OpDimTeleport$Spell") 17 20 class OpDimTeleport$SpellMixin { 21 + @Shadow private Entity target; 22 + @Shadow @Final @Mutable private ServerWorld origin; 18 23 @Shadow @Final private ServerWorld destination; 19 24 @Shadow @Final @Mutable private Vec3d coords; 25 + @Unique private boolean fromDemiplane; 20 26 @Unique private boolean toDemiplane; 21 27 @Inject(method = "cast(Lat/petrak/hexcasting/api/casting/eval/CastingEnvironment;)V", at = @At("HEAD")) 22 28 void preCast(CastingEnvironment env, CallbackInfo ci) { 29 + Identifier sourceID = origin.getRegistryKey().getValue(); 30 + fromDemiplane = sourceID.getNamespace().equals("hexic") && sourceID.getPath().startsWith("fresh-"); 23 31 Identifier targetID = destination.getRegistryKey().getValue(); 24 32 toDemiplane = targetID.getNamespace().equals("hexic") && targetID.getPath().startsWith("fresh-"); 25 - if (toDemiplane) { 33 + if (toDemiplane && !fromDemiplane) { 34 + // entering a demiplane for the first time, try to make sure nobody goes to the milk dimension 26 35 coords = new Vec3d(5.5, 1.0, 5.5); 36 + if (target instanceof ServerPlayerEntity sp) JavaPlaneAccess.logExcursion(sp); 37 + } else if (fromDemiplane && !toDemiplane) { 38 + // change ~origin~ and ~coords~ to the excursion; beholderface will handle the rest 39 + // this /definitely/ won't break the NG discount :clueless: 40 + final var dest = JavaPlaneAccess.findExcursion(target, env); 41 + origin = dest._1; 42 + coords = dest._2; 27 43 } 28 44 } 29 45 @WrapOperation(method = "cast(Lat/petrak/hexcasting/api/casting/eval/CastingEnvironment;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/dimension/DimensionType;coordinateScale()D", ordinal = -1))
+27
project/hexic/src/main/scala/org/eu/net/pool/hexic/mixin/OpDimTeleportMixin.java
··· 1 + package org.eu.net.pool.hexic.mixin; 2 + 3 + import at.petrak.hexcasting.api.casting.castables.SpellAction; 4 + import at.petrak.hexcasting.api.misc.MediaConstants; 5 + import com.llamalad7.mixinextras.injector.ModifyReturnValue; 6 + import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 7 + import com.llamalad7.mixinextras.sugar.Local; 8 + import net.beholderface.oneironaut.casting.patterns.spells.great.OpDimTeleport; 9 + import net.minecraft.server.world.ServerWorld; 10 + import net.minecraft.util.Identifier; 11 + import org.spongepowered.asm.mixin.Mixin; 12 + import org.spongepowered.asm.mixin.injection.At; 13 + import org.spongepowered.asm.mixin.injection.ModifyArg; 14 + 15 + @Mixin(OpDimTeleport.class) 16 + public class OpDimTeleportMixin { 17 + @ModifyReturnValue(at = @At("RETURN"), method = "execute") 18 + SpellAction.Result modifyCost(SpellAction.Result result, @Local(name = "origin") ServerWorld origin, @Local(name = "destination") ServerWorld destination) { 19 + Identifier sourceID = origin.getRegistryKey().getValue(); 20 + boolean fromDemiplane = sourceID.getNamespace().equals("hexic") && sourceID.getPath().startsWith("fresh-"); 21 + Identifier targetID = destination.getRegistryKey().getValue(); 22 + boolean toDemiplane = targetID.getNamespace().equals("hexic") && targetID.getPath().startsWith("fresh-"); 23 + if (fromDemiplane && toDemiplane) ((SpellAction$ResultAccessor) (Object) result).setCost(0); 24 + else if (fromDemiplane || toDemiplane) ((SpellAction$ResultAccessor) (Object) result).setCost(5 * MediaConstants.SHARD_UNIT); 25 + return result; 26 + } 27 + }
+24 -11
project/hexic/src/main/scala/org/eu/net/pool/hexic/mixin/OpEdifySaplingMixin.java
··· 22 22 import net.minecraft.util.DyeColor; 23 23 import net.minecraft.util.math.BlockPos; 24 24 import org.eu.net.pool.hexic.Mediaweave$; 25 + import org.eu.net.pool.hexic.Stringworm; 25 26 import org.eu.net.pool.hexic.duck.OpEdifySapling$SpellAccess; 26 27 import org.spongepowered.asm.mixin.Final; 27 28 import org.spongepowered.asm.mixin.Mixin; ··· 45 46 } 46 47 if (original.call(instance, BlockTags.WOOL_CARPETS)) { 47 48 mediaweave.set(2); 49 + return true; 50 + } 51 + if (instance.isOf(Blocks.TRIPWIRE)) { 52 + mediaweave.set(-1); 48 53 return true; 49 54 } 50 55 return false; ··· 71 76 @Inject(method = "cast(Lat/petrak/hexcasting/api/casting/eval/CastingEnvironment;)V", at = @At("HEAD"), cancellable = true) 72 77 void preCast(CastingEnvironment env, CallbackInfo ci) { 73 78 if (hexic$mediaweave != 0) { 74 - BlockState state = env.getWorld().getBlockState(pos); 75 - String id = Registries.BLOCK.getId(state.getBlock()).getPath(); 76 - for (DyeColor color: DyeColor.values()) { 77 - if (id.startsWith(color.asString())) { 78 - int count = Math.max( 79 - env.getWorld().getRandom().nextBetween(hexic$mediaweave, hexic$mediaweave * 3), 80 - env.getWorld().getRandom().nextBetween(hexic$mediaweave, hexic$mediaweave * 2) 81 - ); 82 - env.getWorld().setBlockState(pos, Blocks.AIR.getDefaultState()); 83 - Block.dropStack(env.getWorld(), pos, new ItemStack(Mediaweave$.MODULE$.colors().apply(color), count)); 84 - ci.cancel(); 79 + ItemStack droppedStack; 80 + a: if (hexic$mediaweave == -1) { 81 + droppedStack = new ItemStack(Stringworm.randomFlavor(env.getWorld().getRandom())); 82 + } else { 83 + BlockState state = env.getWorld().getBlockState(pos); 84 + String id = Registries.BLOCK.getId(state.getBlock()).getPath(); 85 + for (DyeColor color : DyeColor.values()) { 86 + if (id.startsWith(color.asString())) { 87 + int count = Math.max( 88 + env.getWorld().getRandom().nextBetween(hexic$mediaweave, hexic$mediaweave * 3), 89 + env.getWorld().getRandom().nextBetween(hexic$mediaweave, hexic$mediaweave * 2) 90 + ); 91 + droppedStack = new ItemStack(Mediaweave$.MODULE$.colors().apply(color), count); 92 + break a; 93 + } 85 94 } 95 + return; 86 96 } 97 + env.getWorld().setBlockState(pos, Blocks.AIR.getDefaultState()); 98 + Block.dropStack(env.getWorld(), pos, droppedStack); 99 + ci.cancel(); 87 100 } 88 101 } 89 102 }
+12
project/hexic/src/main/scala/org/eu/net/pool/hexic/mixin/SpellAction$ResultAccessor.java
··· 1 + package org.eu.net.pool.hexic.mixin; 2 + 3 + import at.petrak.hexcasting.api.casting.castables.SpellAction; 4 + import org.spongepowered.asm.mixin.Mixin; 5 + import org.spongepowered.asm.mixin.Mutable; 6 + import org.spongepowered.asm.mixin.Shadow; 7 + import org.spongepowered.asm.mixin.gen.Accessor; 8 + 9 + @Mixin(SpellAction.Result.class) 10 + public interface SpellAction$ResultAccessor { 11 + @Accessor @Mutable void setCost(long cost); 12 + }
-44
project/hexxychests/build.gradle.kts
··· 1 - plugins { 2 - id("fabric-loom") 3 - id("scala") 4 - kotlin("jvm") version "2.2.0" 5 - id("maven-publish") 6 - id("de.undercouch.download") version "5.6.0" 7 - id("org.eu.net.pool.mc-plugin") 8 - } 9 - 10 - dependencies { 11 - minecraft("com.mojang:minecraft:${project.property("minecraft_version")}") 12 - mappings("net.fabricmc:yarn:${project.property("yarn_mappings")}:v2") 13 - modImplementation("net.fabricmc:fabric-loader:${project.property("loader_version")}") 14 - modImplementation("net.fabricmc.fabric-api:fabric-api:${project.property("fabric_version")}") 15 - api("org.scala-lang:scala3-library_3:3.7.1") 16 - api("org.scala-lang:scala-library:2.13.6") 17 - api(project(":util", "namedElements")) 18 - modImplementation("at.petra-k.hexcasting:hexcasting-fabric-$minecraft_version:0.11.3") 19 - modImplementation("at.petra-k.paucal:paucal-fabric-$minecraft_version:0.6.0-pre-118") 20 - modImplementation("com.samsthenerd.inline:inline-fabric:$minecraft_version-1.0.1") 21 - modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-block:5.2.3") 22 - modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-item:5.2.3") 23 - } 24 - 25 - tasks.processResources { 26 - preprocessor { 27 - fabricMod("hexxychests", version as String) { 28 - name = "Hex In Yo' Chests" 29 - description = "A Hex Casting addon for manipulating iotas." 30 - license = "LGPL-3.0" 31 - icon = "assets/hexxychests/icon.png" 32 - 33 - author("pool") { 34 - put("discord", "https://discord.com/users/758407438251720795") 35 - } 36 - 37 - depends("phlib", ">=0.2.0") 38 - depends("hexcasting", ">=0.11.2") 39 - 40 - entrypoint("org.eu.net.pool.hexxychests.main\$package::init") 41 - mixins("hexxychests.mixins.json") 42 - } 43 - } 44 - }
-9
project/hexxychests/gradle.properties
··· 1 - minecraft_version=1.20.1 2 - yarn_mappings=1.20.1+build.10 3 - loader_version=0.16.14 4 - scala_loader_version=0.3.1.11 5 - mod_version=0.1.0 6 - maven_group=org.eu.net.pool 7 - modid=hexxychests 8 - archives_base_name=hexxychests 9 - fabric_version=0.92.6+1.20.1
-7
project/hexxychests/src/main/resources/data/hexcasting/tags/action/per_world_pattern.json
··· 1 - { 2 - "values": [ 3 - "hexxychests:findview", 4 - "hexxychests:moveconcept", 5 - "hexxychests:moveentity" 6 - ] 7 - }
-5
project/hexxychests/src/main/resources/data/hexcasting/tags/action/requires_enlightenment.json
··· 1 - { 2 - "values": [ 3 - "hexxychests:moveentity" 4 - ] 5 - }
-7
project/hexxychests/src/main/resources/hexxychests.mixins.json
··· 1 - { 2 - "package": "org.eu.net.pool.hexxychests.mixin", 3 - "parent": "phlib.mixins.json", 4 - "mixins": [ 5 - "AbstractFurnaceBlockEntityMixin" 6 - ] 7 - }
+20 -15
project/hexxychests/src/main/scala/org/eu/net/pool/hexxychests/main.scala project/hexic/src/main/scala/org/eu/net/pool/hexic/views.scala
··· 1 - package org.eu.net.pool.hexxychests 1 + package org.eu.net.pool.hexic 2 + 2 3 import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic 3 4 import at.petrak.hexcasting.api.casting.circles.BlockEntityAbstractImpetus 4 5 import at.petrak.hexcasting.api.casting.eval.CastingEnvironment 5 - import at.petrak.hexcasting.api.casting.iota.{DoubleIota, Iota, IotaType, NullIota, Vec3Iota} 6 + import at.petrak.hexcasting.api.casting.iota.* 6 7 import at.petrak.hexcasting.api.casting.mishaps.{Mishap, MishapInvalidIota} 7 8 import com.mojang.serialization.Codec 8 9 import com.samsthenerd.inline.api.data.ItemInlineData ··· 30 31 import org.slf4j.{Logger, LoggerFactory} 31 32 32 33 import java.util.UUID 34 + import scala.collection.immutable.ArraySeq.unsafeWrapArray 33 35 import scala.collection.mutable 34 36 import scala.reflect.ClassTag 35 37 import scala.util.{Failure, Success, Using} 36 - 37 - private[hexxychests] given Conversion[String, Identifier] = Identifier.of("hexxychests", _) 38 - private[hexxychests] given Logger = LoggerFactory.getLogger("hexxychests") 39 38 40 39 trait InventoryView(val viewType: InventoryView.Type[?]) extends InventoryView.Handler: 41 40 def isTruthy = true ··· 60 59 @throws[Mishap] 61 60 def teleportEntity(ent: Entity)(using TransactionContext, CastingEnvironment): Boolean = false 62 61 object Events: 63 - val forEntity: Event[Entity => ServerWorld ?=> Seq[Handler]] = EventFactory.createArrayBacked(classOf, _ => Seq(), fns => e => fns.flatMap(_(e))) 64 - val forBlock: Event[(BlockPos, BlockState) => ServerWorld ?=> Seq[Handler]] = EventFactory.createArrayBacked(classOf, (_, _) => Seq(), fns => (pos, state) => fns.flatMap(_(pos, state))) 62 + val forEntity: Event[Entity => ServerWorld ?=> Seq[Handler]] = EventFactory.createArrayBacked(classOf, _ => Seq(), fns => e => unsafeWrapArray(fns).flatMap(_(e))) 63 + val forBlock: Event[(BlockPos, BlockState) => ServerWorld ?=> Seq[Handler]] = EventFactory.createArrayBacked(classOf, (_, _) => Seq(), fns => (pos, state) => unsafeWrapArray(fns).flatMap(_(pos, state))) 65 64 // 'implementation restriction' my ass 66 65 val forIota: Event[CastingEnvironment ?=> PartialFunction[Iota, InventoryView]] = EventFactory.createArrayBacked(classOf, PartialFunction.empty, new java.util.function.Function[Array[CastingEnvironment ?=> PartialFunction[Iota, InventoryView]], CastingEnvironment ?=> PartialFunction[Iota, InventoryView]]: 67 66 def apply(fns: Array[CastingEnvironment ?=> PartialFunction[Iota, InventoryView]]): CastingEnvironment ?=> PartialFunction[Iota, InventoryView] = (((_: CastingEnvironment) ?=> PartialFunction.empty[Iota, InventoryView]) /: fns) { _ orElse _ } 68 67 ) 69 68 abstract class OfMerged(viewType: InventoryView.Type[?], views: => Seq[Handler]) extends InventoryView(viewType): 70 69 def getViews = views 71 - override def apply(idx: Int)(using CastingEnvironment): Option[SlotReference] = views.collectFirst(hexicVisibilityHack.unlifted(_(idx))) 70 + override def apply(idx: Int)(using CastingEnvironment): Option[SlotReference] = views.collectFirst(Function.unlift(_(idx))) 72 71 override def tryExtract(variant: TransferVariant[?], amount: Long)(using TransactionContext, CastingEnvironment): Long = LazyList.from(views).scanLeft(0L)((n, view) => view.tryExtract(variant, amount - n) + n).findFirstOrLast(_ >= amount).getOrElse(0) 73 72 override def tryInsert(variant: TransferVariant[?], amount: Long)(using TransactionContext, CastingEnvironment): Long = LazyList.from(views).scanLeft(0L)((n, view) => view.tryInsert(variant, amount - n) + n).findFirstOrLast(_ >= amount).getOrElse(0) 74 73 override def capacity(variant: TransferVariant[?])(using TransactionContext, CastingEnvironment) = views.map(_.capacity(variant)).sum ··· 88 87 case v: OfSum => v.views 89 88 case v => Iterable(v) 90 89 ) 91 - class OfEntity(id: UUID)(using server: MinecraftServer) extends OfMerged(typeOfEntity, server.getWorlds.collectFirst(hexicVisibilityHack.unlifted(w => Option(w.getEntity(id)).map(Events.forEntity.invoker()(_)(using w)))).getOrElse(Seq())): 90 + class OfEntity(id: UUID)(using server: MinecraftServer) extends OfMerged(typeOfEntity, server.getWorlds.collectFirst(Function.unlift(w => Option(w.getEntity(id)).map(Events.forEntity.invoker()(_)(using w)))).getOrElse(Seq())): 92 91 override def serialize = 93 92 val c = super.serialize 94 93 c.putLong("m", id.getMostSignificantBits) ··· 210 209 def fuelTime: Int 211 210 def fuelTime_=(fuelTime: Int): Unit 212 211 213 - private [hexxychests] val conceptScale = mutable.Map[ClassTag[? <: TransferVariant[?]], Double]().withDefaultValue(1.0) 212 + private [hexic] val conceptScale = mutable.Map[ClassTag[? <: TransferVariant[?]], Double]().withDefaultValue(1.0) 214 213 def setConceptScale[T <: TransferVariant[?]: ClassTag as ct](scale: Int) = 215 214 if conceptScale.isDefinedAt(ct) && conceptScale(ct) != scale then 216 215 throw IllegalStateException(s"Conflicting scales ${conceptScale(ct)} and $scale defined for class $ct") ··· 253 252 object SlotReference extends Registrar[SlotReference.Type[?]]("slot"): 254 253 class Type[T <: SlotReference: Codec] 255 254 256 - def init() = 257 - hexXplat.getIotaTypeRegistry("variant") = VariantIota 258 - hexXplat.getIotaTypeRegistry("reference") = BoxedView 255 + object id: 256 + def unapply(x: Identifier) = (x.getNamespace, x.getPath) 257 + 258 + def initViews() = 259 + hexXplat.getIotaTypeRegistry("transfer_type") = VariantIota 260 + hexXplat.getIotaTypeRegistry("inventory_view") = BoxedView 261 + Events.registryLookup.register: 262 + case (r, id("hexic" | "hexxychests", "variant")) => VariantIota 263 + case (r, id("hexic" | "hexxychests", "reference")) => BoxedView 259 264 Patterns.register("findview", e"addaadewewedaaddqwawqddaadewewedaaddqwawdeeweee"): 260 265 // 2026-01-01 pool: nathan, we call this 'jank'. why would you do this? 261 266 inline def lookup = InventoryView.Events.forIota.invoker()(using compiletime.summonInline) ··· 291 296 ??? // TODO: mishap 292 297 tx.commit() 293 298 Seq(DoubleIota(insert / scale)) 294 - Patterns.register("moveentity", e"edeeewawdweaaddaqwqwqaddaaewdwawewdqd"): 299 + Patterns.register("moveentity", se"edeeewawdweaaddaqwqwqaddaaewdwawewdqd"): 295 300 Patterns.mkConstAction(3): 296 301 case Seq(isIota[BoxedView.Instance, 2](from), isIota[BoxedView.Instance, 1](into), isIota[DoubleIota, 0](count)) => 297 302 Using.resource(Transaction.openOuter()): ··· 318 323 def variant: TransferVariant[T] 319 324 def display: Text 320 325 def color: Int = 0x720a0a 321 - private[hexxychests] def parseVariant(c: NbtCompound): Option[(TaggedVariant, RegistryKey[Reader])] = 326 + private[hexic] def parseVariant(c: NbtCompound): Option[(TaggedVariant, RegistryKey[Reader])] = 322 327 for 323 328 i <- Option(Identifier.tryParse(c.getString("type"))) 324 329 entry <- Option.fromNullable(registry.get(i))
+2 -2
project/hexxychests/src/main/scala/org/eu/net/pool/hexxychests/mixin/AbstractFurnaceBlockEntityMixin.java project/hexic/src/main/scala/org/eu/net/pool/hexic/mixin/AbstractFurnaceBlockEntityMixin.java
··· 1 - package org.eu.net.pool.hexxychests.mixin; 1 + package org.eu.net.pool.hexic.mixin; 2 2 3 3 import net.minecraft.block.entity.AbstractFurnaceBlockEntity; 4 - import org.eu.net.pool.hexxychests.AbstractFurnaceBlockEntityAccess; 4 + import org.eu.net.pool.hexic.AbstractFurnaceBlockEntityAccess; 5 5 import org.spongepowered.asm.mixin.Mixin; 6 6 import org.spongepowered.asm.mixin.gen.Accessor; 7 7
-2
project/hexxychests/src/main/scala/scala/hexicVisibilityHack/hack.scala
··· 1 - package scala.hexicVisibilityHack 2 - export PartialFunction.unlifted
+2 -2
project/hexxytounge/gradle.properties
··· 2 2 yarn_mappings=1.20.1+build.10 3 3 loader_version=0.16.14 4 4 scala_loader_version=0.3.1.11 5 - mod_version=0.1.0 5 + version=0.1.0 6 6 maven_group=org.eu.net.pool 7 7 modid=hexxytognue 8 8 archives_base_name=hexxytognue 9 - fabric_version=0.92.6+1.20.1 9 + fabric_version=0.92.6+1.20.1
+6 -7
project/hexxytounge/src/client/scala/org/eu/net/pool/hexxytounge/client.scala project/hexic/src/client/scala/org/eu/net/pool/hexic/chatClient.scala
··· 1 - package org.eu.net.pool.hexxytounge 1 + package org.eu.net.pool 2 + package hexic 2 3 3 4 import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking 4 5 import net.fabricmc.fabric.api.networking.v1.PacketByteBufs ··· 10 11 given Conversion[ChatScreen, mixin.ChatScreenAccess] = _.asInstanceOf 11 12 12 13 var lastMurmur = None: Option[String] 13 - def tick() = 14 + def tickChat() = 14 15 val currentMurmur = MinecraftClient.getInstance.currentScreen match 15 16 case null => None 16 17 case c: ChatScreen => Some(c.chatField.getText) ··· 32 33 import net.minecraft.client.gui.hud.{ChatHud, ChatHudLine, MessageIndicator} 33 34 import net.minecraft.client.gui.screen.ChatScreen 34 35 import net.minecraft.client.gui.widget.TextFieldWidget 35 - import org.eu.net.pool.hexxytounge 36 36 import org.eu.net.pool.phlib.given 37 - import org.spongepowered.asm.mixin.{Mixin, Unique} 38 37 import org.spongepowered.asm.mixin.gen.Accessor 38 + import org.spongepowered.asm.mixin.injection.callback.CallbackInfo 39 39 import org.spongepowered.asm.mixin.injection.{At, Inject} 40 - import org.spongepowered.asm.mixin.injection.callback.CallbackInfo 40 + import org.spongepowered.asm.mixin.{Mixin, Unique} 41 41 42 42 @Mixin(Array(classOf[MinecraftClient])) 43 43 private[mixin] class MinecraftClientMixin: 44 44 @Inject(at = Array(new At(value = "HEAD")), method = Array("tick")) 45 45 def tick(using CallbackInfo) = 46 - hexxytounge.tick() 46 + tickChat() 47 47 @Mixin(Array(classOf[ChatScreen])) 48 48 trait ChatScreenAccess: 49 49 @Accessor("chatField") val chatField: TextFieldWidget ··· 54 54 val p = MinecraftClient.getInstance.player 55 55 if p != null then 56 56 val lines = p.component[RevealComponent].lines.map(line => new ChatHudLine.Visible(currentTick, line.asOrderedText, MessageIndicator.system, true)) 57 - println(lines) 58 57 if lines.nonEmpty then 59 58 lines.reverse ++ original 60 59 else
+2 -2
project/hexxytounge/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/hexxytongue/greater_reveal.json project/hexic/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/hexic/greater_reveal.json
··· 7 7 "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.", 8 8 { 9 9 "type": "hexcasting:pattern", 10 - "anchor": "hexxytounge:reveal", 11 - "op_id": "hexxytounge:reveal", 10 + "anchor": "hexic:reveal", 11 + "op_id": "hexic:reveal", 12 12 "input": "[iota] | iota", 13 13 "output": "", 14 14 "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."
+2 -2
project/hexxytounge/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/zh_cn/entries/addon/hexxytongue/greater_reveal.json project/hexic/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/zh_cn/entries/addon/hexic/greater_reveal.json
··· 7 7 "因此,我自己设计了一个图案。它的功能和$(l:hexcasting:patterns/basics#hexcasting:print)$(action)揭示/$类似,但发来的消息会牢牢嵌在我的意识里,而且一经替换就会立刻遗忘。为方便起见,它还能接受一个消息列表。", 8 8 { 9 9 "type": "hexcasting:pattern", 10 - "anchor": "hexxytounge:reveal", 11 - "op_id": "hexxytounge:reveal", 10 + "anchor": "hexic:reveal", 11 + "op_id": "hexic:reveal", 12 12 "input": "[iota] | iota", 13 13 "output": "", 14 14 "text": "接受 iota 列表(或单个非 iota,此时视作单元素列表)并永久性将其嵌入我的意识。清除普通揭示的方法无法清除此操作展示的 iota,而是只能通过再次施放卓越揭示或死亡清除。"
+3 -3
project/hexxytounge/src/main/resources/assets/hexxytounge/jsonpatch/strings.jsonpatch project/hexic/src/main/resources/assets/hexic/jsonpatch/strings.jsonpatch
··· 3 3 4 4 $pages = arrays.insert($pages, 11, { 5 5 type: "hexcasting:pattern", 6 - op_id: "hexxytounge:murmur", 7 - anchor: "hexxytounge:murmur", 6 + op_id: "hexic:murmur", 7 + anchor: "hexic:murmur", 8 8 input: "", 9 9 output: "str", 10 - text: "book.hexxytounge.page.murmur" 10 + text: "book.hexic.page.murmur" 11 11 });
-4
project/hexxytounge/src/main/resources/assets/hexxytounge/lang/en_us.json
··· 1 - { 2 - "hexcasting.action.hexxytounge:murmur": "Murmur Reflection", 3 - "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." 4 - }
-4
project/hexxytounge/src/main/resources/assets/hexxytounge/lang/zh_cn.json
··· 1 - { 2 - "hexcasting.action.hexxytounge:murmur": "私语之精思", 3 - "book.hexxytounge.page.murmur": "找到我意识中名为“聊天栏”的区域,将其内容压栈;无法找到则返回 Null。" 4 - }
-85
project/hexxytounge/src/main/scala/org/eu/net/pool/hexxytounge/main.scala
··· 1 - package org.eu.net.pool.hexxytounge 2 - 3 - import at.petrak.hexcasting.api.casting.eval.CastingEnvironment 4 - import at.petrak.hexcasting.api.casting.iota.{Iota, IotaType, ListIota, NullIota} 5 - import at.petrak.hexcasting.api.casting.mishaps.MishapBadCaster 6 - import com.google.gson.JsonElement 7 - import com.mojang.serialization.JsonOps 8 - import dev.onyxstudios.cca.api.v3.component.sync.AutoSyncedComponent 9 - import dev.onyxstudios.cca.api.v3.component.{Component, ComponentAccess, ComponentKey, ComponentRegistry} 10 - import dev.onyxstudios.cca.api.v3.entity.{EntityComponentFactoryRegistry, EntityComponentInitializer, RespawnCopyStrategy} 11 - import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking 12 - import net.minecraft.nbt.{NbtCompound, NbtElement} 13 - import net.minecraft.server.network.ServerPlayerEntity 14 - import net.minecraft.text.Text 15 - import net.minecraft.util.Identifier 16 - import org.eu.net.pool.phlib.{Events as PhEvents, *, given} 17 - import org.slf4j.{Logger, LoggerFactory} 18 - import ram.talia.moreiotas.api.casting.iota.StringIota 19 - 20 - import scala.collection.immutable.* 21 - import scala.language.implicitConversions 22 - 23 - private[hexxytounge] given Logger = LoggerFactory.getLogger("hexxytounge") 24 - private[hexxytounge] given Conversion[String, Identifier] = Identifier.of("hexxytounge", _) 25 - 26 - private[hexxytounge] case class MurmurCache(var value: Option[String]) extends Component: 27 - override def readFromNbt(tag: NbtCompound): Unit = 28 - if tag.getBoolean("active") then 29 - value = Some(tag.getString("value")) 30 - else 31 - value = None 32 - override def writeToNbt(tag: NbtCompound): Unit = 33 - tag.putBoolean("active", false) 34 - for value <- value do 35 - tag.putBoolean("active", true) 36 - tag.putString("value", value) 37 - private[hexxytounge] object MurmurCache: 38 - given ComponentKey[MurmurCache] = ComponentRegistry.getOrCreate("murmur", classOf[MurmurCache]) 39 - 40 - private[hexxytounge] case class RevealComponent(var lines: Seq[Text]) extends AutoSyncedComponent: 41 - override def readFromNbt(tag: NbtCompound): Unit = 42 - lines = for n <- 0 until tag.getInt("lineCount") yield Text.Serializer.fromJson(tag(s"line$n")) // nbt is json apparently? 43 - override def writeToNbt(tag: NbtCompound): Unit = 44 - tag.putInt("lineCount", lines.size) 45 - for (line, n) <- lines.zipWithIndex do tag(s"line$n") = Text.Serializer.toJsonTree(line) 46 - private[hexxytounge] object RevealComponent: 47 - given ComponentKey[RevealComponent] = ComponentRegistry.getOrCreate("reveal", classOf[RevealComponent]) 48 - 49 - class Components extends EntityComponentInitializer: 50 - override def registerEntityComponentFactories(registry: EntityComponentFactoryRegistry): Unit = 51 - registry.registerForPlayers(summon[ComponentKey[MurmurCache]], _ => MurmurCache(None), RespawnCopyStrategy.ALWAYS_COPY) 52 - registry.registerForPlayers(summon[ComponentKey[RevealComponent]], _ => RevealComponent(Seq.empty), RespawnCopyStrategy.LOSSLESS_ONLY) 53 - 54 - def keyOf[C <: Component: ComponentKey as key] = key 55 - 56 - extension (c: ComponentAccess) 57 - def component[C <: Component: ComponentKey as key]: C = c.getComponent(key) 58 - def syncComponent[C <: Component: ComponentKey as key](): Unit = c.syncComponent(key) 59 - 60 - object hasComponent: 61 - def unapply[C <: Component: ComponentKey](ctx: ComponentAccess): Option[C] = 62 - try 63 - Some(ctx.component[C]) 64 - catch case _: NoSuchElementException => 65 - None 66 - 67 - def init() = 68 - Patterns.register("reveal", ne"deqed" ): 69 - Patterns.mkConstAction(1, 0): 70 - case Seq(iota: Iota) => 71 - locally(summon[CastingEnvironment]).getCastingEntity match 72 - case null => throw MishapBadCaster() 73 - case p: ServerPlayerEntity => 74 - p.component[RevealComponent].lines = iota match 75 - case s: ListIota => s.getList.map(_.display).toSeq 76 - //case m: MapIota => m.map.toSeq.map(p => IotaType.getDisplay(p._1) -> IotaType.getDisplay(p._2)).sortBy(_._1.getString) 77 - case _: NullIota => Seq() 78 - case _ => Seq(iota.display) 79 - p.syncComponent[RevealComponent]() 80 - Seq() 81 - case _ => throw MishapBadCaster() 82 - Patterns.register("murmur", e"wwaqwa"): 83 - Patterns.mkLiteral: (env, _) ?=> 84 - hasComponent.unapply[MurmurCache](env.getCastingEntity).fold(throw MishapBadCaster())(_.value.fold(NullIota())(StringIota.make)) 85 - ServerPlayNetworking.registerGlobalReceiver("murmur", { case (_, hasComponent[MurmurCache](c), _, buf, _) => c.value = Option.when(buf.readBoolean())(buf.readString()) }: ServerPlayNetworking.PlayChannelHandler)
+1 -1
project/iotaworks/build.gradle.kts
··· 37 37 put("discord", "https://discord.com/users/758407438251720795") 38 38 } 39 39 40 - depends("phlib", "0.1.1") 40 + depends("phlib", ">=0.1.2") 41 41 depends("hexcasting", ">=0.11.2") 42 42 depends("hexcellular", "^1.0.4") 43 43 recommends("hexpose", "*")
+1 -1
project/iotaworks/gradle.properties
··· 2 2 yarn_mappings=1.20.1+build.10 3 3 loader_version=0.16.14 4 4 scala_loader_version=0.3.1.11 5 - mod_version=0.1.3 5 + version=0.1.4 6 6 maven_group=org.eu.net.pool 7 7 modid=iotaworks 8 8 archives_base_name=iotaworks
+5 -1
settings.gradle.kts
··· 18 18 } 19 19 } 20 20 21 + plugins { 22 + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" 23 + } 24 + 21 25 include("util") 22 26 includeBuild("plugin") 23 - for (mod in listOf("hexic", "iotaworks", "hexxytounge", "hexxychests")) { 27 + for (mod in listOf("hexic", "iotaworks", "hexxytounge")) { 24 28 include(mod) 25 29 project(":$mod").projectDir = file("project/$mod") 26 30 }
+8
util/build.gradle.kts
··· 29 29 modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-level:$cardinal_version") 30 30 modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-world:$cardinal_version") 31 31 modRuntimeOnly("dev.onyxstudios.cardinal-components-api:cardinal-components-api:$cardinal_version") 32 + include("com.unascribed:lib39-core:[2.0.0,3.0.0)!!2.0.27+1.20.1") 33 + include("com.unascribed:lib39-avant:[2.0.0,3.0.0)!!2.0.27+1.20.1") 34 + include("com.unascribed:lib39-phantom:[2.0.0,3.0.0)!!2.0.27+1.20.1") 32 35 } 33 36 34 37 tasks.processResources { ··· 45 48 46 49 depends("hexcasting", ">=0.11.2") 47 50 breaks("hexic", "<2.0.0") 51 + breaks("essential", "*") 48 52 49 53 entrypoint("org.eu.net.pool.phlib.main\$package::init") 50 54 mixins("phlib.mixins.json") 51 55 } 52 56 } 57 + } 58 + 59 + tasks.jar { 60 + duplicatesStrategy = DuplicatesStrategy.WARN 53 61 } 54 62 55 63 publishing {
+4
util/changelog
··· 1 + 0.1.2 add portals 2 + 0.1.2 documented Flock's Disintegration and Speaker's Distillation for maps 3 + 0.1.2 intrusive holder allocation tracking 4 + 0.1.2 made maps insertion-ordered
+1 -1
util/gradle.properties
··· 2 2 yarn_mappings=1.20.1+build.10 3 3 loader_version=0.16.14 4 4 scala_loader_version=0.3.1.11 5 - mod_version=0.1.1 5 + version=0.1.2 6 6 maven_group=org.eu.net.pool 7 7 modid=phlib 8 8 archives_base_name=phlib
+36 -20
util/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/phlib/maps.json
··· 15 15 }, 16 16 { 17 17 "type": "hexcasting:pattern", 18 - "op_id": "hexcasting:add", 19 - "anchor": "hexcasting:add", 20 - "input": "map<k, v>, map<k, v>", 18 + "op_id": "hexcasting:replace", 19 + "anchor": "hexcasting:replace", 20 + "input": "map<k, v>, k, v", 21 21 "output": "map<k, v>", 22 - "text": "Merges two maps together. Maps cannot have duplicate items: any entry in the latter map will override that in the first map." 22 + "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." 23 23 }, 24 24 { 25 25 "type": "hexcasting:pattern", 26 - "op_id": "hexcasting:sub", 27 - "anchor": "hexcasting:sub", 28 - "input": "map<k, v>, map<t, *>", 26 + "op_id": "hexcasting:remove_from", 27 + "anchor": "hexcasting:remove_from", 28 + "input": "map<k, v>, k", 29 29 "output": "map<k, v>", 30 - "text": "Removes every entry in the first map that is found in the second map. The values in the second map are ignored." 30 + "text": "Throws out the map's association for the given key. If there is no matching association, the map is returned unchanged." 31 31 }, 32 32 { 33 33 "type": "hexcasting:pattern", ··· 35 35 "anchor": "hexcasting:index", 36 36 "input": "map<k, v>, k", 37 37 "output": "v?", 38 - "text": "Disintegrates the map, returning only the value associated with the given key. O(1), unlike scanning a list." 38 + "text": "Destroys the map, returning only the value associated with the given key. O(1), unlike scanning a list." 39 + }, 40 + { 41 + "type": "hexcasting:pattern", 42 + "op_id": "hexcasting:splat", 43 + "anchor": "hexcasting:splat", 44 + "input": "map<k, v>", 45 + "output": "k, v, k, v...", 46 + "text": "Disintegrates the map, returning the key-value pairs in the order I inserted them." 39 47 }, 40 48 { 41 49 "type": "hexcasting:pattern", 42 50 "op_id": "hexcasting:unappend", 43 51 "anchor": "hexcasting:unappend", 44 - "input": "map<k, v>", 45 - "output": "map<k, v>, (k, v)?", 46 - "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." 52 + "input": "map<k,v>", 53 + "output": "map<k,v>, (k,v | nulls)", 54 + "text": "Shaves off the element I added most recently from a map. If used on an empty map, returns two nulls." 47 55 }, 48 56 { 49 57 "type": "hexcasting:pattern", 50 - "op_id": "hexcasting:replace", 51 - "anchor": "hexcasting:replace", 52 - "input": "map<k, v>, k, v", 58 + "op_id": "hexcasting:deconstruct", 59 + "anchor": "hexcasting:deconstruct", 60 + "input": "map<k,v>", 61 + "output": "map<k,v>, (k,v | nulls)", 62 + "text": "Shaves off the element I added least recently from a map. If used on an empty map, returns two nulls." 63 + }, 64 + { 65 + "type": "hexcasting:pattern", 66 + "op_id": "hexcasting:add", 67 + "anchor": "hexcasting:add", 68 + "input": "map<k, v>, map<k, v>", 53 69 "output": "map<k, v>", 54 - "text": "Replaces the element associated with $(n)k/$ with $(n)v/$. If $(n)k/$ is not present in the map, the entry is created." 70 + "text": "Merges two maps together. Maps cannot have duplicate items: any entry in the latter map will override that in the first map." 55 71 }, 56 72 { 57 73 "type": "hexcasting:pattern", 58 - "op_id": "hexcasting:remove_from", 59 - "anchor": "hexcasting:remove_from", 60 - "input": "map<k, v>, k", 74 + "op_id": "hexcasting:sub", 75 + "anchor": "hexcasting:sub", 76 + "input": "map<k, v>, map<t, *>", 61 77 "output": "map<k, v>", 62 - "text": "Throws out the map's association for the given key. If there is no matching association, the map is returned unchanged." 78 + "text": "Removes every entry in the first map that is found in the second map. The values in the second map are ignored." 63 79 } 64 80 ] 65 81 }
+35 -19
util/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/zh_cn/entries/addon/phlib/maps.json
··· 15 15 }, 16 16 { 17 17 "type": "hexcasting:pattern", 18 - "op_id": "hexcasting:add", 19 - "anchor": "hexcasting:add", 20 - "input": "map<k, v>, map<k, v>", 18 + "op_id": "hexcasting:replace", 19 + "anchor": "hexcasting:replace", 20 + "input": "map<k, v>, k, v", 21 21 "output": "map<k, v>", 22 - "text": "合并两个映射。映射中不允许存在重复项:后一映射的项会覆盖前一映射的。" 22 + "text": "将键 $(n)k/$ 所对应项的值换为 $(n)v/$。若映射内没有 $(n)k/$ 键,则新建项并插入末尾。" 23 23 }, 24 24 { 25 25 "type": "hexcasting:pattern", 26 - "op_id": "hexcasting:sub", 27 - "anchor": "hexcasting:sub", 28 - "input": "map<k, v>, map<t, *>", 26 + "op_id": "hexcasting:remove_from", 27 + "anchor": "hexcasting:remove_from", 28 + "input": "map<k, v>, k", 29 29 "output": "map<k, v>", 30 - "text": "从第一个映射中移除第二个映射的项。比对时忽略第二个映射中项的值。" 30 + "text": "从映射中删去所给键对应的项。若未找到相应项,则原样返回映射。" 31 31 }, 32 32 { 33 33 "type": "hexcasting:pattern", ··· 39 39 }, 40 40 { 41 41 "type": "hexcasting:pattern", 42 + "op_id": "hexcasting:splat", 43 + "anchor": "hexcasting:splat", 44 + "input": "map<k, v>", 45 + "output": "k, v, k, v...", 46 + "text": "拆散映射,按插入顺序返回键值对。" 47 + }, 48 + { 49 + "type": "hexcasting:pattern", 42 50 "op_id": "hexcasting:unappend", 43 51 "anchor": "hexcasting:unappend", 44 - "input": "map<k, v>", 45 - "output": "map<k, v>, (k, v)?", 46 - "text": "从映射中取出一个项。删去哪个项没有定论,完全由自然随意决定。对空映射使用时返回两个 Null。" 52 + "input": "map<k,v>", 53 + "output": "map<k,v>, (k,v | nulls)", 54 + "text": "从映射中取出最后添加的项。对空映射使用时返回两个 Null。" 47 55 }, 48 56 { 49 57 "type": "hexcasting:pattern", 50 - "op_id": "hexcasting:replace", 51 - "anchor": "hexcasting:replace", 52 - "input": "map<k, v>, k, v", 58 + "op_id": "hexcasting:deconstruct", 59 + "anchor": "hexcasting:deconstruct", 60 + "input": "map<k,v>", 61 + "output": "map<k,v>, (k,v | nulls)", 62 + "text": "从映射中取出最先添加的项。对空映射使用时返回两个 Null。" 63 + }, 64 + { 65 + "type": "hexcasting:pattern", 66 + "op_id": "hexcasting:add", 67 + "anchor": "hexcasting:add", 68 + "input": "map<k, v>, map<k, v>", 53 69 "output": "map<k, v>", 54 - "text": "将键为 $(n)k/$ 的项的值换为 $(n)v/$。若映射内没有 $(n)k/$ 键,则新建项。" 70 + "text": "合并两个映射。映射中不允许存在重复项:后一映射的项会覆盖前一映射的。" 55 71 }, 56 72 { 57 73 "type": "hexcasting:pattern", 58 - "op_id": "hexcasting:remove_from", 59 - "anchor": "hexcasting:remove_from", 60 - "input": "map<k, v>, k", 74 + "op_id": "hexcasting:sub", 75 + "anchor": "hexcasting:sub", 76 + "input": "map<k, v>, map<t, *>", 61 77 "output": "map<k, v>", 62 - "text": "从映射中删去所给键对应的项。若未找到相应项,则原样返回映射。" 78 + "text": "从第一个映射中移除第二个映射的项。比对时忽略第二个映射中项的值。" 63 79 } 64 80 ] 65 81 }
+3
util/src/main/resources/phlib.mixins.json
··· 4 4 "package": "org.eu.net.pool.phlib.mixin", 5 5 "compatibilityLevel": "JAVA_17", 6 6 "mixins": [ 7 + "ActuallySimpleRegistryMixin", 8 + "AllocationTrackerMixin", 9 + "OpSplatMixin", 7 10 "PatternIotaMixin", 8 11 "SimpleRegistryMixin" 9 12 ]
+34
util/src/main/scala/org/eu/net/pool/phlib/aperture.scala
··· 1 + package org.eu.net.pool 2 + package phlib 3 + 4 + import java.util.NoSuchElementException 5 + import scala.util.{Failure, Success, Try, boundary} 6 + 7 + class Scope[T](initial: T): 8 + private object local extends InheritableThreadLocal[Option[T]]: 9 + override def initialValue: Option[T] = None 10 + def enter[R](newValue: T)(body: => R): R = 11 + val oldValue = local.get 12 + try 13 + local.set(Some(newValue)) 14 + body 15 + finally 16 + local.set(oldValue) 17 + def value: T = local.get.getOrElse(initial) 18 + def valueIfChanged: Option[T] = local.get 19 + def isChanged: Boolean = local.get.isDefined 20 + object Scope: 21 + given [T] => Conversion[Scope[T], T] = _.value 22 + 23 + class Portal[T]: 24 + private val scope = Scope[Try[T] => Nothing]: 25 + case Success(value) => sys.exit(0) 26 + case Failure(exc) => { exc.printStackTrace(); sys.exit(1) } 27 + def enter[R](body: => R): Either[T, R] = 28 + def wrap: Either[Try[T], R] = scope.enter(x => return Left(x))(Right(body)) 29 + wrap.left.map(_.get) 30 + def isEntered: Boolean = scope.isChanged 31 + def exit(value: T): Nothing = scope(Success(value)) 32 + def abort(error: Throwable): Nothing = scope(Failure(error)) 33 + def tryExit(value: T): Unit = for jump <- scope.valueIfChanged do jump(Success(value)) 34 + def tryAbort(error: Throwable): Unit = for jump <- scope.valueIfChanged do jump(Failure(error))
+16 -1
util/src/main/scala/org/eu/net/pool/phlib/main.scala
··· 155 155 env.addExtension: 156 156 new CastingEnvironmentComponent with CastingEnvironmentComponent.PostExecution: 157 157 override def getKey: CastingEnvironmentComponent.Key[?] = key 158 - 159 158 override def onPostExecution(result: CastResult): Unit = 160 159 result.getSideEffects.collectFirst: 161 160 case m: OperatorSideEffect.DoMishap => ··· 241 240 else 242 241 `else` 243 242 243 + trait Registered[T](registry: Registry[T], id: Identifier): 244 + this: T => 245 + registry(id) = this 246 + 244 247 extension (p: ServerPlayerEntity) def gimmeIota(iota: Iota): Unit = 245 248 val m = p.getComponent(HexCardinalComponents.STAFFCAST_IMAGE) 246 249 m.setImage(m.getVM(Hand.MAIN_HAND).getImage.withStack(_ ++ Vector(iota))) 250 + 251 + private[phlib] trait AllocationTracked: 252 + private[phlib] val phlib$createdAt: Exception 253 + 254 + package mixin: 255 + import net.minecraft.block.Block 256 + import net.minecraft.item.Item 257 + import org.spongepowered.asm.mixin.injection.{At, Inject} 258 + import org.spongepowered.asm.mixin.Mixin 259 + @Mixin(value = Array(classOf[Item], classOf[Block])) 260 + private[phlib] class AllocationTrackerMixin() extends AnyRef with AllocationTracked: 261 + private[phlib] val phlib$createdAt: Exception = new RuntimeException(s"unregistered $this ($getClass) created") 247 262 248 263 // this is out-of-scope for phlib but I have no idea where else to put it 249 264 def init() =
+31 -35
util/src/main/scala/org/eu/net/pool/phlib/maps.scala
··· 11 11 import net.minecraft.text.{MutableText, Text} 12 12 import net.minecraft.util.Formatting 13 13 14 + import java.util 14 15 import java.util.List => JavaList 16 + import scala.collection.immutable.ListMap 15 17 import net.minecraft.server.world.ServerWorld 16 18 import at.petrak.hexcasting.api.utils.HexUtils 17 19 18 - case class MapIota(map: Map[NbtCompound, NbtCompound] = Map(), trusted: Boolean = false)(using val world: ServerWorld) extends Iota(MapIota, map): 20 + case class MapIota(map: Map[NbtCompound, NbtCompound] = ListMap())(using val world: ServerWorld) extends Iota(MapIota, map): 19 21 def get(key: Iota): Option[Iota] = map.get(IotaType.serialize(key)).map(IotaType.deserialize(_, summon)) 20 22 def apply(key: Iota): Iota = get(key) getOrElse NullIota() 21 23 def -(keys: Iota*): MapIota = MapIota(map -- (keys map IotaType.serialize)) ··· 45 47 c.put("k", p._1) 46 48 c.put("v", p._2)) tap l.add) 47 49 override def size = map.toSeq.map(_.size + _.size - 1).sum + 1 48 - override def subIotas(): java.lang.Iterable[Iota] = 49 - if trusted then 50 - // skip deserialization for performance 51 - // this is safe because `trusted` can only be true if truenames have 52 - // already been checked, and we override `size` 53 - Seq() 54 - else 55 - toList 56 - def asJavaMap: java.util.Map[Iota, Iota] = map.map(e => (IotaType.deserialize(e._1, summon), IotaType.deserialize(e._2, summon))).toMap 50 + override def subIotas(): java.lang.Iterable[Iota] = toList 51 + def asJavaMap: java.util.Map[Iota, Iota] = 52 + val ret = util.LinkedHashMap[Iota, Iota]() 53 + for k -> v <- map do 54 + ret(IotaType.deserialize(k, summon)) = IotaType.deserialize(v, summon) 55 + ret 57 56 object MapIota extends IotaType[MapIota]: 58 57 def color: Int = 0xb0641c 59 58 def deserialize(using data: NbtElement, world: ServerWorld): MapIota = 60 59 val l = HexUtils.downcast(data, NbtList.TYPE) 61 60 val o = HexUtils.downcast(_, NbtCompound.TYPE) 62 - l.map(o) 63 - .map(c => (o(c("k")), o(c("v")))) 64 - .toMap[NbtCompound, NbtCompound] 65 - .pipe(new MapIota(_, trusted = true)) 61 + new MapIota(ListMap.from(l.map(o).map(c => (o(c("k")), o(c("v")))))) 66 62 def display(data: NbtElement): Text = 67 63 val items = HexUtils.downcast(data, NbtList.TYPE) 68 64 val output: MutableText = "[" ··· 84 80 output 85 81 def fromJavaMap(map: java.util.Map[Iota, Iota])(using ServerWorld) = MapIota((map: collection.Map[Iota, Iota]).map(e => IotaType.serialize(e._1) -> IotaType.serialize(e._2)).toMap) 86 82 val mapArithmetic = 87 - import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic.* 88 - arith("map", 89 - ADD -> ((a: MapIota, b: MapIota) => a ++ b), 90 - SUB -> ((a: MapIota, b: MapIota) => a -- b), 91 - ABS -> ((a: MapIota) => DoubleIota(a.map.size)), 92 - INDEX -> ((a: MapIota, k: Iota) => a(k)), 93 - UNAPPEND -> ((a: MapIota) => a.lastOption.map(p => Seq(p._1, p._2)).getOrElse(Seq(NullIota(), NullIota())) prepended a.init), 94 - INDEX_OF -> ((a: MapIota, v: Iota) => 95 - val c = IotaType.serialize(v) 96 - a.update(_.filter(_._2 == c))), 97 - REMOVE -> ((a: MapIota, k: Iota) => a - k), 98 - REPLACE -> ((a: MapIota, k: Iota, v: Iota) => a + (k -> v)), 99 - UNCONS -> ((a: MapIota) => a.headOption.map(p => Seq(p._1, p._2)).getOrElse(Seq(NullIota(), NullIota())) prepended a.tail), 100 - AND -> ((a: MapIota, b: MapIota) => a & b), 101 - OR -> ((a: MapIota, b: MapIota) => b ++ a), 102 - XOR -> ((a: MapIota, b: MapIota) => a ^ b), 103 - GREATER -> ((a: MapIota, b: MapIota) => a.map.containsAll(b.map) && a.map != b.map), 104 - LESS -> ((a: MapIota, b: MapIota) => b.map.containsAll(a.map) && a.map != b.map), 105 - GREATER_EQ -> ((a: MapIota, b: MapIota) => a.map containsAll b.map), 106 - LESS_EQ -> ((a: MapIota, b: MapIota) => b.map containsAll a.map), 107 - ) 83 + import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic.* 84 + arith("map", 85 + ADD -> ((a: MapIota, b: MapIota) => a ++ b), 86 + SUB -> ((a: MapIota, b: MapIota) => a -- b), 87 + ABS -> ((a: MapIota) => DoubleIota(a.map.size)), 88 + INDEX -> ((a: MapIota, k: Iota) => a(k)), 89 + UNAPPEND -> ((a: MapIota) => a.lastOption.map(p => Seq(p._1, p._2)).getOrElse(Seq(NullIota(), NullIota())) prepended a.init), 90 + INDEX_OF -> ((a: MapIota, v: Iota) => 91 + val c = IotaType.serialize(v) 92 + a.update(_.filter(_._2 == c))), 93 + REMOVE -> ((a: MapIota, k: Iota) => a - k), 94 + REPLACE -> ((a: MapIota, k: Iota, v: Iota) => a + (k -> v)), 95 + UNCONS -> ((a: MapIota) => a.headOption.map(p => Seq(p._1, p._2)).getOrElse(Seq(NullIota(), NullIota())) prepended a.tail), 96 + AND -> ((a: MapIota, b: MapIota) => a & b), 97 + OR -> ((a: MapIota, b: MapIota) => b ++ a), 98 + XOR -> ((a: MapIota, b: MapIota) => a ^ b), 99 + GREATER -> ((a: MapIota, b: MapIota) => a.map.containsAll(b.map) && a.map != b.map), 100 + LESS -> ((a: MapIota, b: MapIota) => b.map.containsAll(a.map) && a.map != b.map), 101 + GREATER_EQ -> ((a: MapIota, b: MapIota) => a.map containsAll b.map), 102 + LESS_EQ -> ((a: MapIota, b: MapIota) => b.map containsAll a.map), 103 + )
+25
util/src/main/scala/org/eu/net/pool/phlib/mixin/ActuallySimpleRegistryMixin.java
··· 1 + package org.eu.net.pool.phlib.mixin; 2 + 3 + import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 + import net.minecraft.registry.SimpleRegistry; 5 + import net.minecraft.registry.entry.RegistryEntry; 6 + import org.eu.net.pool.phlib.AllocationTracked; 7 + import org.jetbrains.annotations.Nullable; 8 + import org.spongepowered.asm.mixin.Mixin; 9 + import org.spongepowered.asm.mixin.Shadow; 10 + import org.spongepowered.asm.mixin.injection.At; 11 + 12 + import java.util.Map; 13 + 14 + @Mixin(SimpleRegistry.class) 15 + public class ActuallySimpleRegistryMixin<T> { 16 + @Shadow private Map<T, RegistryEntry.Reference<T>> intrusiveValueToEntry; 17 + 18 + @ModifyExpressionValue(method = "freeze", at = @At(value = "NEW", target = "(Ljava/lang/String;)Ljava/lang/IllegalStateException;", ordinal = 1)) 19 + IllegalStateException addSuppressed(IllegalStateException original) { 20 + for (var entry: intrusiveValueToEntry.entrySet()) { 21 + if (entry.getKey() instanceof AllocationTracked t) original.addSuppressed(t.phlib$createdAt()); 22 + } 23 + return original; 24 + } 25 + }
+33
util/src/main/scala/org/eu/net/pool/phlib/mixin/OpSplatMixin.java
··· 1 + package org.eu.net.pool.phlib.mixin; 2 + 3 + import at.petrak.hexcasting.api.casting.SpellList; 4 + import at.petrak.hexcasting.api.casting.iota.Iota; 5 + import at.petrak.hexcasting.api.casting.iota.ListIota; 6 + import at.petrak.hexcasting.common.casting.actions.lists.OpSplat; 7 + import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 8 + import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 9 + import org.eu.net.pool.phlib.MapIota; 10 + import org.spongepowered.asm.mixin.Mixin; 11 + import org.spongepowered.asm.mixin.injection.At; 12 + 13 + import java.util.ArrayList; 14 + import java.util.List; 15 + 16 + // 2026-03-20 01:29 pool: this is *very* out-of-scope for phlib 17 + // but what are you, a fed? 18 + @Mixin(OpSplat.class) 19 + public class OpSplatMixin { 20 + @WrapOperation(at = @At(value = "INVOKE", target = "Lat/petrak/hexcasting/api/casting/OperatorUtils;getList(Ljava/util/List;II)Lat/petrak/hexcasting/api/casting/SpellList;"), method = "execute") 21 + SpellList redirectList(List<? extends Iota> stack, int it, int x, Operation<SpellList> original) { 22 + if (stack.get(it) instanceof MapIota map) { 23 + final var retList = new ArrayList(map.size() * 2); 24 + for (var pair: map.asJavaMap().entrySet()) { 25 + retList.add(pair.getKey()); 26 + retList.add(pair.getValue()); 27 + } 28 + return new ListIota(retList).getList(); 29 + } else { 30 + return original.call(stack, it, x); 31 + } 32 + } 33 + }
-1
util/src/main/scala/org/eu/net/pool/phlib/util.scala
··· 96 96 override def operate(env: CastingEnvironment, img: CastingImage, cont: SpellContinuation): OperationResult = 97 97 val stack = img.getStack.asScala.toSeq 98 98 val args = stack.takeRight(${Expr(a.size)}) 99 - // I'm fairly certain the remainder of this method is considered a war crime 100 99 ${ 101 100 Block(a.zipWithIndex.map { p => 102 101 val (v@ValDef(n, ty, _), i) = p