repo for my hex addons :3
0
fork

Configure Feed

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

watch them somehow break each other

+1381 -289
+31 -2
README.md
··· 1 1 <center> 2 2 3 - <img height="200" src="src/main/resources/assets/hexic/icon.png" title="hexic icon: a blue stringworm" width="200"/> 3 + <img height="200" src="https://codeberg.org/api/v1/repos/poollovernathan/hexic/raw/src/main/resources/assets/hexic/icon.png" title="hexic icon: a blue stringworm" width="200"/> 4 4 5 5 <h1 style="margin-top: 0">hexic</h1> 6 6 ··· 32 32 * hexical Hopper can be used with kinetic's Conduits; bypasses ambit 33 33 34 34 [hexcasting]: https://modrinth.com/mod/hex-casting 35 - [siege]: https://hexic.pool.net.eu.org/siege.html 35 + [siege]: https://hexic.pool.net.eu.org/siege.html 36 + 37 + --- 38 + 39 + ## development 40 + 41 + put code in the following files: 42 + 43 + * [`EarlyRiser.scala`](src/main/scala/org/eu/net/pool/hexic/EarlyRiser.scala) — something that needs to get loaded before launch, e.g. agents 44 + * [`Utils.scala`](src/main/scala/org/eu/net/pool/hexic/Utils.scala) — utilities that aren't specifically related to the mod 45 + * [`Hexic.scala`](src/main/scala/org/eu/net/pool/hexic/Hexic.scala) — anything else 46 + 47 + ## building 48 + 49 + there are three major ways to build: 50 + 51 + * **nix**: `nix-shell`, `gradle runDatagen`, `gradle build` 52 + * **manual**: install aseprite, go, jujutsu, imagemagick, gradle 8.14, and gnu m4, then `gradle runDatagen; gradle build` as usual 53 + 54 + ### docker 55 + 56 + isolated container environment gives you a guarantee of build reproducibility, if you don't care about build speed 57 + 58 + * **simple local build**: `docker build . -f build.Dockerfile -o some/output/path/` 59 + * **build without cloning**: `docker build https://codeberg.org/poollovernathan/hexic.git#main --build-arg BUILDKIT_CONTEXT_KEEP_GIT_DIR=1 -f build.Dockerfile -o some/output/path/` 60 + 61 + you can optimize your builds by using the cache, which requires creating a build container: `docker buildx create --driver docker-container --name hexic-builder` 62 + 63 + * **precached local build**: `docker build . -f build.Dockerfile -o some/output/path/ --builder hexic-builder --cache-from docker.pool.net.eu.org/hexic:cache` 64 + * **precached build without cloning**: `docker build https://codeberg.org/poollovernathan/hexic.git#main --build-arg BUILDKIT_CONTEXT_KEEP_GIT_DIR=1 -f build.Dockerfile -o some/output/path/ --builder hexic-builder --cache-from docker.pool.net.eu.org/hexic:cache`
+15
TODO.md
··· 1 + # before next release 2 + * `ktwznkmp` making Noetic Gateway always go to a demiplane's center 3 + * `ktwznkmp` special demiplane iota handling 4 + * demiplane rupturing 5 + * implement table stuff 6 + * chisel texture 7 + * table in-inventory model 8 + # probably soon 9 + * `szrotpky` filtering and sorting would be nice to have 10 + * The Suffering / Ascension 11 + * document demiplanes 12 + * document snow pattern 13 + * document parathoth 14 + # at some point 15 + * spellminds
+104 -16
build.gradle.kts
··· 5 5 import org.gradle.kotlin.dsl.support.uppercaseFirstChar 6 6 import kotlin.io.path.exists 7 7 import kotlin.io.path.readText 8 + import groovy.json.JsonOutput 8 9 9 10 plugins { 10 - id("fabric-loom") version "1.10-SNAPSHOT" 11 + id("fabric-loom") version "1.13-SNAPSHOT" 11 12 id("scala") 12 13 kotlin("jvm") version "2.2.0" 13 14 id("maven-publish") ··· 15 16 id("org.eu.net.pool.mc-plugin") version "0.1.1" 16 17 } 17 18 19 + try { 20 + tasks.named("downloadRenderDoc") { 21 + setProperty("output", file("$buildDir/renderdoc_1.37.tar.gz")) 22 + } 23 + 24 + tasks.named("extractRenderDoc") { 25 + enabled = false 26 + } 27 + 28 + val erd by tasks.register<Sync>("myExtractRenderDoc") { 29 + dependsOn("downloadRenderDoc") 30 + from(tarTree(resources.gzip("$buildDir/renderdoc_1.37.tar.gz"))) 31 + into("$buildDir/renderdoc") 32 + } 33 + 34 + tasks.named("runClientRenderDoc") { 35 + dependsOn(erd) 36 + } 37 + } catch (ignored: UnknownTaskException) {} 38 + 39 + loom.runs["client"].programArgs += listOf("--username", "Player", "--uuid", "bd346dd5-ac1c-427d-87e8-73bdd4bf3e13") 40 + 41 + //tasks.withType<RenderDocR>() 18 42 19 43 val release: Boolean = !System.getenv("release").isNullOrEmpty() 20 44 val p = P(project) ··· 122 146 "dev.kineticcat.hexportation", 123 147 "miyucomics.hexcellular", 124 148 "miyucomics.hexical", 149 + "miyucomics.overevaluate", 125 150 "org.eu.net.pool", 126 151 "poollovernathan") 127 152 exactRepo("https://maven.shedaniel.me/", ··· 132 157 "dev.emi") 133 158 exactRepo("https://repo.sleeping.town/", 134 159 "com.unascribed") 160 + exactRepo("https://masa.dy.fi/maven/", 161 + "carpet") 135 162 exactRepo("https://maven.nucleoid.xyz/", 136 163 "xyz.nucleoid") 137 164 } ··· 234 261 val minecraft_version = "1.20.1" 235 262 modDepends(implementation(annotationProcessor("io.github.llamalad7:mixinextras-fabric:0.5.0")!!)!!) 236 263 modDepends(modImplementation("net.fabricmc.fabric-api:fabric-api:${project.property("fabric_version")}")!!) 237 - modDepends(modImplementation("poollovernathan.fabric:mod-tools:1.1.5+1.20.1")!!) 238 - modDepends(include(modApi("org.eu.net.pool:common-curses:1.1.5-SNAPSHOT")!!)!!) 264 + modDepends(include(modImplementation("poollovernathan.fabric:mod-tools:1.1.5+1.20.1")!!)!!) 239 265 include(api("org.scala-lang:scala3-library_3:3.7.1")!!) 240 266 include(api("org.scala-lang:scala-library:2.13.6")!!) 241 267 modDepends(modImplementation("at.petra-k.hexcasting:hexcasting-fabric-$minecraft_version:0.11.2-pre-751")!!) 242 268 modImplementation("at.petra-k.paucal:paucal-fabric-$minecraft_version:0.6.0-pre-118") 243 269 modImplementation("com.samsthenerd.inline:inline-fabric:$minecraft_version-1.0.1") 244 - include(implementation("com.github.Chocohead:Fabric-ASM:v2.3")!!) 270 + modDepends(include(implementation("com.github.Chocohead:Fabric-ASM:v2.3")!!)!!) 245 271 modCompileOnly("dev.kineticcat.hexportation:hexportation-fabric-1.20.1-fabric-fabric:0.0.3") 272 + modCompileOnly("carpet:fabric-carpet:1.20-1.+") 273 + // modRuntimeOnly("carpet:fabric-carpet:1.20-1.+") 274 + compat("gay.object.ioticblocks:ioticblocks-fabric:1.0.2+1.20.1") 246 275 modImplementation("io.github.tropheusj:serialization-hooks:0.4.99999") 247 276 modImplementation(files("./libs/oneironaut-fabric-1.20.1-0.5.0-476cee2.jar")) 248 277 compat("maven.modrinth:hexcassettes:1.1.4") 278 + modLocalRuntime("maven.modrinth:trinkets:3.7.2") 249 279 modDepends(modImplementation("maven.modrinth:spasm:0.2.2")!!) 250 280 // modImplementation("maven.modrinth:slate-works:1.0.5") 251 281 compat("miyucomics.hexical:hexical:main-SNAPSHOT") 282 + compat("miyucomics.overevaluate:overevaluate:main-SNAPSHOT") 252 283 modDepends(modImplementation("ram.talia.moreiotas:moreiotas-fabric-$minecraft_version:0.1.1") { exclude(module = "serialization-hooks") }) 253 284 modDepends(modImplementation("ram.talia.hexal:hexal-fabric-1.20.1:0.3.0") { exclude(module = "serialization-hooks") }) 254 285 modDepends(modImplementation("miyucomics.hexcellular:hexcellular:1.1.0")!!) ··· 335 366 custom { 336 367 array("cardinal-components") { 337 368 put("hexic:player_wisp") 369 + put("hexic:server_info") 338 370 } 339 371 } 340 372 } ··· 342 374 343 375 dependsOn(cloth) 344 376 dependsOn(*downloadedBags.values.toTypedArray()) 345 - val itemsRoot = "$destinationDir/assets/hexic/textures/item" 377 + val itemsRoot = destinationDir.resolve("assets/hexic/textures/item") 378 + val langRoot = destinationDir.resolve("assets/hexic/lang") 379 + val bookRoot = destinationDir.resolve("assets/hexcasting/patchouli_books/thehexbook") 346 380 doLast { 347 381 for ((name, color) in colors) { 348 382 exec { 349 - commandLine("env", "magick", cloth.dest, "-channel", "red,green,blue", "-fx", "u*#${color.toString(16)}", "$itemsRoot/${name}_mediaweave.png") 383 + commandLine("env", "magick", cloth.dest, "-channel", "red,green,blue", "-fx", "u*#${color.toString(16)}", itemsRoot.resolve("${name}_mediaweave.png")) 350 384 } 351 385 val bag = downloadedBags[name]!!.dest 352 386 exec { 353 - commandLine("env", "magick", bag, "-write", "$itemsRoot/large_${name}_bundle.png", "-sample", "14x14", "-background", "transparent", "-extent", "16x16-1-2", "$itemsRoot/small_${name}_bundle.png") 387 + 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")) 354 388 } 355 389 } 356 390 exec { 357 - commandLine("env", "magick", "wizard:", "$itemsRoot/wizard.png") 391 + commandLine("env", "magick", "wizard:", itemsRoot.resolve("wizard.png")) 358 392 } 359 393 exec { 360 - commandLine("env", "magick", "null:", "$itemsRoot/no.png") 394 + commandLine("env", "magick", "null:", itemsRoot.resolve("no.png")) 361 395 } 362 396 exec { 363 397 commandLine("env", "magick", ··· 374 408 "-compose", "copy_opacity", 375 409 "-composite", 376 410 "-fx", "u*2", 377 - "$itemsRoot/stringworm.miff" 411 + itemsRoot.resolve("stringworm.miff") 378 412 ) 379 413 } 380 414 for ((name, expr) in mapOf( ··· 385 419 "pure" to "u", 386 420 )) { 387 421 exec { 388 - commandLine("env", "magick", "$itemsRoot/stringworm.miff", "-channel", "rgb", "-fx", expr, "$itemsRoot/stringworm_$name.png") 422 + commandLine("env", "magick", itemsRoot.resolve("stringworm.miff"), "-channel", "rgb", "-fx", expr, "$itemsRoot/stringworm_$name.png") 389 423 } 390 424 } 391 425 // people will hate this 392 426 for (i in 0..31) { 393 - file("$itemsRoot/stringworm_tinted_$i.png").outputStream().use { 427 + itemsRoot.resolve("stringworm_tinted_$i.png").outputStream().use { 394 428 exec { 395 - commandLine("env", "magick", "$itemsRoot/stringworm.miff", "-fx", "i+j == $i ? u : Transparent", "png:-") 429 + commandLine("env", "magick", itemsRoot.resolve("stringworm.miff"), "-fx", "i+j == $i ? u : Transparent", "png:-") 396 430 standardOutput = it 397 431 } 398 432 } 399 433 } 400 434 file("$itemsRoot/../block").mkdir() 401 435 exec { 402 - commandLine("env", "magick", "xc:#ffffff[16x16]", "$itemsRoot/../block/border.png") 436 + commandLine("env", "magick", "xc:#ffffff[16x16]", itemsRoot.resolveSibling("block/border.png")) 403 437 } 404 438 //file("$itemsRoot/stringworm.miff").delete() 405 439 exec { 406 - commandLine("env", "magick", "https://www.masterbuilt.com/cdn/shop/articles/162_20-_20Voodoo_20Baked_20Beans.jpg", "-sample", "256x256", "$itemsRoot/beans.png") 440 + commandLine("env", "magick", "https://www.masterbuilt.com/cdn/shop/articles/162_20-_20Voodoo_20Baked_20Beans.jpg", "-sample", "256x256", itemsRoot.resolve("beans.png")) 441 + } 442 + } 443 + 444 + doLast { 445 + for (lang in bookRoot.list()) { 446 + val langFile = langRoot.resolve("$lang.json") 447 + if (langFile.exists()) { 448 + val entries = JsonSlurper().parseText(langFile.readText()) as MutableMap<String, String> 449 + var n = 0 450 + for (bookFile in bookRoot.resolve(lang).walkTopDown()) { 451 + if (bookFile.isFile) { 452 + val json = JsonSlurper().parseText(bookFile.readText()) 453 + if (json !is Map<*, *>) continue 454 + json as MutableMap<Any, Any> 455 + val name = json["name"] 456 + if (name is String) { 457 + entries["text.hexic.book.${n}"] = name 458 + json["name"] = "text.hexic.book.${n}" 459 + n++ 460 + } 461 + val pages = json["pages"] 462 + if (pages !is MutableList<*>) continue 463 + pages as MutableList<Any> 464 + for (i in pages.indices) { 465 + val page = pages[i] 466 + if (page is String) { 467 + entries["text.hexic.book.${n}"] = page 468 + pages[i] = "text.hexic.book.${n}" 469 + n++ 470 + } else if (page is MutableMap<*, *>) { 471 + page as MutableMap<Any, Any> 472 + for (key in listOf("text", "title")) { 473 + val text = page[key] 474 + if (text != null && text is String) { 475 + entries["text.hexic.book.${n}"] = text 476 + page[key] = "text.hexic.book.${n}" 477 + n++ 478 + } 479 + } 480 + } 481 + } 482 + bookFile.writeText(JsonOutput.toJson(json)) 483 + } 484 + } 485 + langFile.writeText(JsonOutput.toJson(entries)) 486 + } 407 487 } 408 488 } 409 489 ··· 497 577 commandLine("git", "push", "origin", "+$wheelCommit:refs/heads/wheels") 498 578 } 499 579 580 + tasks.named("clean") { 581 + doLast { 582 + file("src/main/generated").deleteRecursively() 583 + file("dist").deleteRecursively() 584 + } 585 + } 586 + 500 587 open class Hexdoc: Exec() { 501 588 init { 502 589 environment["GITHUB_PAGES_URL"] = "https://hexic.pool.net.eu.org/" ··· 558 645 commandLine("env", "pip", "install", "-e", ".") 559 646 } 560 647 val hexdoc by tasks.register<Hexdoc>("hexdoc") { 561 - dependsOn(syncPip, tasks.processResources, "runDatagen") 648 + dependsOn(syncPip, "processWithDatagen") 562 649 docsPrefix = file("_site/src/docs") 563 650 cleanPrefix() 564 651 hexdocArgs = listOf("build", "--branch", p.change_id) ··· 574 661 } 575 662 val wheel by tasks.register<Exec>("wheel") { 576 663 dependsOn(hexdoc) 664 + doFirst { file("dist").deleteRecursively() } 577 665 commandLine("env", "uv", "build") 578 666 outputs.file(wheelPath) 579 667 }
+4 -2
doc/hexdoc.toml
··· 6 6 resource_dirs = [ 7 7 "resources", 8 8 "../build2001/resources/main", 9 - "../src/main/generated", 10 9 { modid="hexcasting" }, 11 10 { modid="minecraft" }, 12 11 { modid="hexdoc" }, ··· 25 24 regex = "{^_pattern_regex}" 26 25 27 26 [textures] 28 - missing = ["hexic:preferred_mediaweave", "minecraft:chest"] 27 + missing = ["hexic:preferred_mediaweave", "minecraft:chest", "hexic:chisel"] 29 28 30 29 [template] 31 30 icon = "../src/main/resources/assets/hexic/icon.png" ··· 36 35 author = "PoolloverNathan" 37 36 show_landing_text = false 38 37 38 + [[template.args.navbar.center]] 39 + text = "Download" 40 + href = "https://modrinth.com/mod/hexic" 39 41 [[template.args.navbar.center]] 40 42 text = "Source" 41 43 href = "https://codeberg.org/poollovernathan/hexic"
-16
doc/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/hexic/addendum/strings.json
··· 1 - { 2 - "name": "Strings: An Addendum", 3 - "icon": "minecraft:string", 4 - "category": "hexcasting:patterns", 5 - "pages": [ 6 - "For some strange reason, I feel the need to document this pattern in a separate chapter, despite its resemblance to $(l:patterns/strings#moreiotas:string/chat/caster)Whisper Reflection/$ — something about \"mods shouldn't modify other mods' entries\" and all that.", 7 - { 8 - "type": "hexcasting:pattern", 9 - "op_id": "hexic:murmur", 10 - "anchor": "hexic:murmur", 11 - "input": "", 12 - "output": "str", 13 - "text": "Adds the phrase on the $(o)tip of my tongue/$ to the stack, regardless of whether I intend to say it." 14 - } 15 - ] 16 - }
+48
doc/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/hexic/appendix.json
··· 1 + { 2 + "name": "Appendices", 3 + "icon": "minecraft:string", 4 + "category": "hexcasting:patterns", 5 + "pages": [ 6 + "For some strange reason (a sort of 'hex bug'?), I feel the need to document these patterns in a separate chapter. I've properly spliced these patterns into my other set of notes.", 7 + { 8 + "type": "hexcasting:pattern", 9 + "op_id": "hexic:murmur", 10 + "anchor": "hexic:murmur", 11 + "input": "", 12 + "output": "str", 13 + "text": "Adds the phrase on the $(o)tip of my tongue/$ to the stack, regardless of whether I intend to say it." 14 + }, 15 + { 16 + "type": "hexcasting:pattern", 17 + "op_id": "hexic:modulo", 18 + "anchor": "hexic:modulo", 19 + "input": "num, num", 20 + "output": "num", 21 + "text": "book.hexic.page.modulo" 22 + }, 23 + { 24 + "type": "hexcasting:pattern", 25 + "op_id": "hexic:dye_offhand", 26 + "anchor": "hexic:dye_offhand", 27 + "input": "pigment", 28 + "output": "", 29 + "text": "book.hexic.page.dye_offhand" 30 + }, 31 + { 32 + "type": "hexcasting:pattern", 33 + "op_id": "hexic:get_other_caster", 34 + "anchor": "hexic:get_other_caster", 35 + "input": "", 36 + "output": "entity | null", 37 + "text": "book.hexic.page.get_other_caster" 38 + }, 39 + { 40 + "type": "hexcasting:pattern", 41 + "op_id": "hexic:erase", 42 + "anchor": "hexic:erase", 43 + "input": "vec | entity", 44 + "output": "", 45 + "text": "book.hexic.page.erase" 46 + } 47 + ] 48 + }
-16
doc/resources/assets/hexcasting/patchouli_books/thehexbook/zh_cn/entries/addon/hexic/addendum/strings.json
··· 1 - { 2 - "name": "字符串:附录", 3 - "icon": "minecraft:string", 4 - "category": "hexcasting:patterns", 5 - "pages": [ 6 - "不知为什么,我总觉得这个图案应该新开一章介绍,尽管它$(l:patterns/strings#moreiotas:string/chat/caster)耳语之精思/$很像。大概是因为“模组不应改动其他模组的条目”吧。", 7 - { 8 - "type": "hexcasting:pattern", 9 - "op_id": "hexic:murmur", 10 - "anchor": "hexic:murmur", 11 - "input": "", 12 - "output": "str", 13 - "text": "将$(o)在我嘴边/$的话语压入栈中,后续是否要说出不影响结果。" 14 - } 15 - ] 16 - }
+48
doc/resources/assets/hexcasting/patchouli_books/thehexbook/zh_cn/entries/addon/hexic/appendix.json
··· 1 + { 2 + "name": "附录", 3 + "icon": "minecraft:string", 4 + "category": "hexcasting:patterns", 5 + "pages": [ 6 + "由于某些奇怪的原因(某种“咒法漏洞”(HexBug)?),我认为这些图案需要在单独的章节中记录。我已将这些图案拼贴、融入到其他笔记中去了。", 7 + { 8 + "type": "hexcasting:pattern", 9 + "op_id": "hexic:murmur", 10 + "anchor": "hexic:murmur", 11 + "input": "", 12 + "output": "str", 13 + "text": "book.hexic.page.murmur" 14 + }, 15 + { 16 + "type": "hexcasting:pattern", 17 + "op_id": "hexic:modulo", 18 + "anchor": "hexic:modulo", 19 + "input": "num, num", 20 + "output": "num", 21 + "text": "book.hexic.page.modulo" 22 + }, 23 + { 24 + "type": "hexcasting:pattern", 25 + "op_id": "hexic:dye_offhand", 26 + "anchor": "hexic:dye_offhand", 27 + "input": "pigment", 28 + "output": "", 29 + "text": "book.hexic.page.dye_offhand" 30 + }, 31 + { 32 + "type": "hexcasting:pattern", 33 + "op_id": "hexic:get_other_caster", 34 + "anchor": "hexic:get_other_caster", 35 + "input": "", 36 + "output": "entity | null", 37 + "text": "book.hexic.page.get_other_caster" 38 + }, 39 + { 40 + "type": "hexcasting:pattern", 41 + "op_id": "hexic:erase", 42 + "anchor": "hexic:erase", 43 + "input": "vec | entity", 44 + "output": "", 45 + "text": "book.hexic.page.erase" 46 + } 47 + ] 48 + }
+1 -1
gradle.properties
··· 8 8 loader_version=0.16.14 9 9 scala_loader_version=0.3.1.11 10 10 # Mod Properties 11 - mod_version=1.4.5 11 + mod_version=1.4.7-pre.1 12 12 py_version=1.0 13 13 maven_group=org.eu.net.pool 14 14 archives_base_name=hexic
+9 -3
gradlew
··· 10 10 rm "$cache/nix.txz" 11 11 fi 12 12 shell="$cache/shell_$(sha256sum shell.nix)" 13 - [ -f "$shell" ] || "$cache/nix-build" shell.nix -o "$shell" --log-format bar --substituters https://poollovernathan.cachix.io'?'trusted=1 14 - . <(tail +5 "$shell") 15 - exec gradle "$@" 13 + nix_args=(--extra-experimental-features 'flakes' --keep-going --extra-substituters https://poollovernathan.cachix.org'?'trusted=1) 14 + if [ -n "${NO_CRASH:-}" ]; then 15 + "$cache/nix-shell" "${nix_args[@]}" --command "$(printf '%q ' gradle "$@")" 16 + else 17 + #[ -d "$cache/_nix" ] || "$cache/nix" --extra-experimental-features 'nix-command flakes' build github:nixos/nixpkgs/fe51d34885f7b5e3e7b59572796e1bcb427eccb1#nix -o "$cache/_nix" 18 + [ -f "$shell" ] || "$cache/nix-build" "${nix_args[@]}" shell.nix -o "$shell" --log-format bar 19 + . <(tail +5 "$shell") 20 + exec gradle "$@" 21 + fi
+11 -3
pyproject.toml
··· 1 1 [project] 2 2 name = "hexdoc-hexic" 3 + description = "Miscellaneous neat features and QoL patterns for Hex Casting." 4 + readme = "README.md" 5 + license = "LGPL-3.0" 3 6 dynamic = ["version"] 4 7 dependencies = [ 5 8 "hexdoc>=1!0.1.0a31", ··· 11 14 ] 12 15 requires-python = ">=3.12" 13 16 17 + [[project.authors]] 18 + name = "PoolloverNathan" 19 + 20 + [project.urls] 21 + Homepage = "https://modrinth.com/mod/hexic" 22 + Source = "https://codeberg.org/PoolloverNathan/hexic" 23 + Issues = "https://codeberg.org/PoolloverNathan/hexic/issues" 24 + 14 25 [tool.uv.sources] 15 26 hexdoc-hexal = { url = "https://github.com/FallingColors/Hexal/raw/a5d1dbc6528e4a6c7c8f67ba3adc4c143dafb6e5/docs/v/latest/main/dist/hexdoc_hexal-0.3.0.1.0rc4.dev0-py3-none-any.whl" } 16 27 17 28 [tool.hatch.metadata] 18 29 allow-direct-references = true 19 - 20 - [[project.authors]] 21 - name = "PoolloverNathan" 22 30 23 31 [project.entry-points.hexdoc] 24 32 hexic = "hexdoc_hexic._hooks:HexicPlugin"
+217 -39
src/client/scala/org/eu/net/pool/hexic/client/HexicClient.scala
··· 5 5 import at.petrak.hexcasting.api.mod.HexTags 6 6 import at.petrak.hexcasting.api.pigment.FrozenPigment 7 7 import com.google.gson.reflect.TypeToken 8 - import com.google.gson.{Gson, JsonObject} 8 + import com.google.gson.{Gson, JsonArray, JsonObject} 9 9 import com.llamalad7.mixinextras.injector.wrapoperation.Operation 10 + import kotlin.jvm.JvmField 10 11 import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking 11 12 import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry 12 13 import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator ··· 14 15 import net.fabricmc.fabric.api.networking.v1.PacketByteBufs 15 16 import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant 16 17 import net.minecraft.advancement.criterion.InventoryChangedCriterion 18 + import net.minecraft.block.entity.{BlockEntity, BlockEntityType} 17 19 import net.minecraft.client.MinecraftClient 18 20 import net.minecraft.client.color.item.ItemColorProvider 19 21 import net.minecraft.client.gui.screen.ChatScreen 20 22 import net.minecraft.client.gui.widget.TextFieldWidget 21 23 import net.minecraft.client.network.{ClientPlayNetworkHandler, ClientPlayerEntity} 24 + import net.minecraft.client.render.{BufferBuilder, RenderLayer, RenderLayers, VertexConsumer, VertexConsumerProvider, VertexFormat} 25 + import net.minecraft.client.render.block.entity.{BlockEntityRenderer, BlockEntityRendererFactories} 22 26 import net.minecraft.client.render.model.json 27 + import net.minecraft.client.texture.Sprite 28 + import net.minecraft.client.util.math.MatrixStack 23 29 import net.minecraft.data.client.{BlockStateModelGenerator, ItemModelGenerator, ModelIds, Models, TextureKey, TextureMap} 24 30 import net.minecraft.data.server.recipe.{RecipeJsonProvider, ShapedRecipeJsonBuilder} 25 31 import net.minecraft.entity.player.PlayerEntity ··· 31 37 import net.minecraft.text.{CharacterVisitor, OrderedText, Style} 32 38 import net.minecraft.util.DyeColor 33 39 import net.minecraft.util.collection.DefaultedList 34 - import net.minecraft.util.math.Vec3d 35 - import org.eu.net.pool.common_curses.{HotbarRendering, SlotAccess, TextManipulator} 36 - import org.eu.net.pool.common_curses.client.CommonCursesClientKt 40 + import net.minecraft.util.math.{Direction, MathHelper, Vec3d} 37 41 import org.eu.net.pool.hexic.mixin.client.ChatScreenAccess 38 42 39 43 import java.io.{InputStreamReader, Reader} 40 44 import java.util.function.Consumer 41 45 import scala.collection.JavaConverters.mapAsScalaMapConverter 46 + import scala.collection.immutable.BitSet 42 47 import scala.language.experimental.{macros, saferExceptions} 48 + import scala.reflect.Selectable.reflectiveSelectable 43 49 import scala.util.boundary 50 + import scala.util.boundary.Label 44 51 import scala.util.chaining.scalaUtilChainingOps 45 52 46 53 given client: MinecraftClient = MinecraftClient.getInstance ··· 106 113 true 107 114 108 115 def init(): Unit = 109 - HotbarRendering.Companion.getEvent.register: () => 110 - foldLocalPlayer(HotbarRendering.ALL): 111 - _.getComponent(PlayerInfoComponent.key).wispMedia.fold(HotbarRendering.ALL)(_ => HotbarRendering.NONE) 116 + BlockEntityRendererFactories.register( 117 + Registries.BLOCK_ENTITY_TYPE("chisel_table").asInstanceOf[BlockEntityType[? <: BlockEntity { val bits: BitSet }]], 118 + ctx => (tbl: BlockEntity { val bits: BitSet }, dt, mats, bufs, light, overlay) => 119 + given MatrixStack = mats 120 + given buf: VertexConsumer = bufs.getBuffer(RenderLayer.getTranslucent) 121 + for bit <- tbl.bits do 122 + val x = bit / 16 123 + val y = bit % 16 124 + val n = x * 3f + y - 5f 125 + val time = tbl.getWorld.getTime + dt 126 + val lighten = Math.sin(n / 20f + time / 400f) 127 + val darken = Math.sin(n / 24f + time / 400f) 128 + val color = ((1.0f - (darken max 0) * 0.08f).toFloat, (0.6f + lighten * 0.125).toFloat, (1.0f + (darken min 0) * 0.08f).toFloat, 1.0f) // season to taste 129 + if color._1 > 1 || color._2 > 1 || color._3 > 1 || color._4 > 1 then 130 + given_Logger.error(s"Out-of-bounds pixel color! x=$x y=$y n=$n time=$time lighten=$lighten darken=$darken color=$color") 131 + else 132 + given Lighting = Lighting(light, overlay, color = color) 133 + cuboid( 134 + ((x+1)/16f, 12/16f, (y+1)/16f) -> ((x+2)/16f, 13/16f, (y+2)/16f), 135 + // TODO 136 + Direction.values.map(_ -> (null, (0f, 0f) -> (1f, 1f)))* 137 + ) 138 + ) 112 139 ColorProviderRegistry.ITEM.register((stack, idx) => boundary: 113 140 val nbt = stack.getSubNbt("pigment") 114 141 if nbt == null then boundary.break(0xFFFFFFFF) ··· 130 157 131 158 extension (s: DyeColor) def humanName: String = s.getName.split('_').map(_.capitalize).mkString(" ") 132 159 160 + inline def pushMatrices[T](using stack: MatrixStack)(body: => T): T = 161 + stack.push() 162 + try 163 + body 164 + finally 165 + stack.pop() 166 + 167 + case class Lighting(light: Int | (Int, Int), overlay: Int | (Int, Int) = (255, 255) /* trial-and-error with no effect */, color: (Float, Float, Float, Float) = (1, 1, 1, 1)): 168 + def writeLight()(using buf: VertexConsumer) = 169 + light match 170 + case (i, j) => buf.light(i, j) 171 + case i: Int => buf.light(i) 172 + def writeOverlay()(using buf: VertexConsumer) = 173 + overlay match 174 + case (i, j) => buf.overlay(i, j) 175 + case i: Int => buf.overlay(i) 176 + def writeColor()(using buf: VertexConsumer) = 177 + buf.color(color._1, color._2, color._3, color._4) 178 + 179 + def vert(using buf: VertexConsumer, mats: MatrixStack, light: Lighting)(pos: (Float, Float, Float), normal: (Float, Float, Float), uv: (Float, Float)) = 180 + buf.vertex(mats.peek.getPositionMatrix, pos._1, pos._2, pos._3) 181 + light.writeColor() 182 + buf.texture(uv._1 / 48, uv._2 / 32) 183 + light.writeLight() 184 + light.writeOverlay() 185 + buf.normal(mats.peek.getNormalMatrix, normal._1, normal._2, normal._3) 186 + buf.next() 187 + 188 + def verts(using VertexConsumer, MatrixStack, Lighting)(verts: Seq[((Float, Float, Float), (Float, Float))], normal: (Float, Float, Float)) = 189 + for (pos, uv) <- verts yield 190 + vert(pos, normal, uv) 191 + 192 + def cuboid(using VertexConsumer, MatrixStack, Lighting)(span: ((Float, Float, Float), (Float, Float, Float)), faces: (Direction, (Sprite | Null, ((Float, Float), (Float, Float))))*) = 193 + val (from, to) = span 194 + val (x1, y1, z1) = (from._1 min to._1, from._2 min to._2, from._3 min to._3) 195 + val (x2, y2, z2) = (from._1 max to._1, from._2 max to._2, from._3 max to._3) 196 + for (dir, (sprite, (uv1, uv2))) <- faces do 197 + val ((minU, minV), (maxU, maxV)) = sprite match 198 + case null => (0f, 0f) -> (1f, 1f) 199 + case s: Sprite => (s.getMinU, s.getMinV) -> (s.getMaxU, s.getMaxV) 200 + val u1 = MathHelper.lerp(uv1._1, minU, maxU) 201 + val v1 = MathHelper.lerp(uv1._2, minV, maxV) 202 + val u2 = MathHelper.lerp(uv2._1, minU, maxU) 203 + val v2 = MathHelper.lerp(uv2._2, minV, maxV) 204 + // the remainder of this function has been generated by a qwen3-coder:480b since I'm too lazy to write all this by hand 205 + val vertsSeq = dir match 206 + case Direction.UP => 207 + Seq( 208 + (x1, y2, z2) -> (u1, v1), 209 + (x2, y2, z2) -> (u2, v1), 210 + (x2, y2, z1) -> (u2, v2), 211 + (x1, y2, z1) -> (u1, v2), 212 + ) 213 + case Direction.DOWN => 214 + Seq( 215 + (x1, y1, z1) -> (u1, v1), 216 + (x2, y1, z1) -> (u2, v1), 217 + (x2, y1, z2) -> (u2, v2), 218 + (x1, y1, z2) -> (u1, v2), 219 + ) 220 + case Direction.NORTH => 221 + Seq( 222 + (x2, y1, z1) -> (u1, v2), 223 + (x1, y1, z1) -> (u2, v2), 224 + (x1, y2, z1) -> (u2, v1), 225 + (x2, y2, z1) -> (u1, v1), 226 + ) 227 + case Direction.SOUTH => 228 + Seq( 229 + (x1, y1, z2) -> (u1, v2), 230 + (x2, y1, z2) -> (u2, v2), 231 + (x2, y2, z2) -> (u2, v1), 232 + (x1, y2, z2) -> (u1, v1), 233 + ) 234 + case Direction.WEST => 235 + Seq( 236 + (x1, y1, z1) -> (u1, v2), 237 + (x1, y1, z2) -> (u2, v2), 238 + (x1, y2, z2) -> (u2, v1), 239 + (x1, y2, z1) -> (u1, v1), 240 + ) 241 + case Direction.EAST => 242 + Seq( 243 + (x2, y1, z2) -> (u1, v2), 244 + (x2, y1, z1) -> (u2, v2), 245 + (x2, y2, z1) -> (u2, v1), 246 + (x2, y2, z2) -> (u1, v1), 247 + ) 248 + 249 + val normal = dir match 250 + case Direction.UP => (0f, 1f, 0f) 251 + case Direction.DOWN => (0f, -1f, 0f) 252 + case Direction.NORTH => (0f, 0f, -1f) 253 + case Direction.SOUTH => (0f, 0f, 1f) 254 + case Direction.WEST => (-1f, 0f, 0f) 255 + case Direction.EAST => (1f, 0f, 0f) 256 + verts(vertsSeq, normal) 257 + 133 258 def datagen(gen: FabricDataGenerator): Unit = 134 259 val pack = gen.createPack() 135 260 pack.addProvider: 136 261 new FabricModelProvider(_): 137 262 override def generateBlockStateModels(gen: BlockStateModelGenerator): Unit = 138 263 gen.registerSimpleCubeAll(Registries.BLOCK("border")) 264 + gen.registerSimpleState(Registries.BLOCK("chisel_table")) 265 + gen.modelCollector.accept(ModelIds.getBlockModelId(Registries.BLOCK("chisel_table")), () => 266 + new JsonObject().tap: j => 267 + j.addProperty("parent", "minecraft:block/block") 268 + j.add("textures", new JsonObject().tap: j => 269 + j.addProperty("particle", "hexcasting:block/slate") 270 + ) 271 + j.add("elements", new JsonArray().tap: j => 272 + def elem(name: String, from: (Float, Float, Float), to: (Float, Float, Float), config: JsonObject ?=> Unit, faces: (Direction, JsonObject ?=> Label[Unit] ?=> Unit)*) = 273 + j.add(new JsonObject().tap: j => 274 + j.addProperty("name", name) 275 + j.add("from", JsonArray().tap(_.add(from._1)).tap(_.add(from._2)).tap(_.add(from._3))) 276 + j.add("to", JsonArray().tap(_.add(to._1)).tap(_.add(to._2)).tap(_.add(to._3))) 277 + j.add("faces", JsonObject().tap: j => 278 + for (face, action) <- if faces.nonEmpty then faces else Direction.values.toSeq.map { _ -> {} } do 279 + boundary: 280 + j.add(face.asString, JsonObject().tap: 281 + case given JsonObject => 282 + val j = summon[JsonObject] 283 + if face == Direction.WEST && from._1 == 0 then j.addProperty("cullface", "west") 284 + if face == Direction.DOWN && from._2 == 0 then j.addProperty("cullface", "down") 285 + if face == Direction.NORTH && from._3 == 0 then j.addProperty("cullface", "north") 286 + if face == Direction.EAST && to._1 == 16 then j.addProperty("cullface", "east") 287 + if face == Direction.UP && to._2 == 16 then j.addProperty("cullface", "up") 288 + if face == Direction.SOUTH && to._3 == 16 then j.addProperty("cullface", "south") 289 + config; action 290 + ) 291 + ) 292 + ) 293 + // TODO: we can optimize this later 294 + elem("small_leg", (0, 0, 0), (4, 8, 4), j ?=> j.addProperty("texture", "#particle")) 295 + elem("big_leg", (12, 0, 12), (16, 8, 16), j ?=> j.addProperty("texture", "#particle")) 296 + elem("surface", (0, 8, 0), (16, 12, 16), j ?=> j.addProperty("texture", "#particle")) 297 + ) 298 + ) 139 299 override def generateItemModels(gen: ItemModelGenerator): Unit = 140 300 for (_, item) <- Mediaweave.colors do gen.register(item, Models.GENERATED) 141 301 for (_, item) <- stringworms do gen.register(item, Models.GENERATED) ··· 156 316 j.addProperty(s"layer$i", s"hexic:item/stringworm_tinted_$i") 157 317 ) 158 318 ) 319 + gen.register(Registries.ITEM("chisel"), Models.GENERATED) 159 320 gen.register(wizard, Models.GENERATED) 160 321 pack.addProvider: 161 322 new FabricLanguageProvider(_): 162 323 override def generateTranslations(gen: FabricLanguageProvider.TranslationBuilder): Unit = 163 324 for (action, name) <- Vector( 325 + "deleteworld" -> "Shatter Demiplane", 326 + "drop" -> "Rejection Distillation", 327 + "dye_offhand" -> "Apply Pigment", 328 + "empty_map" -> "Vacant Reflection: Map", 329 + "erase" -> "Erase Block", 330 + "extract" -> "Excisor's Gambit", 331 + "fox" -> "Vulpine Gambit", 332 + "free" -> "Deallocator's Gambit", 333 + "get_other_caster" -> "Dual's Reflection", 334 + "grep" -> "Refinement Distillation", 335 + "jvm/class_of_iota" -> "Classifier Purification II", 336 + "jvm/class_of_payload" -> "Classifier Purification I", 337 + "jvm/newinstance_boxed" -> "Constructor Purification I", 338 + "jvm/newinstance_unboxed" -> "Constructor Purification II", 339 + "make_cme" -> "Thoth's Pseudogambit", 340 + "makeworld" -> "Conjure Demiplane", 341 + "malloc" -> "Allocator's Purification", 342 + "metatable" -> "Patchwork Exaltation", 343 + "modulo" -> "Modulus Distillation II", 344 + "murmur" -> "Murmur Reflection", 345 + "nbt/deserialize" -> "Importer's Purification", 164 346 "nbt/lift1" -> "Secretary's Purification: Byte", 165 347 "nbt/lift2" -> "Secretary's Purification: Short", 166 348 "nbt/lift4" -> "Secretary's Purification: Integer", 167 349 "nbt/lift8" -> "Secretary's Purification: Long", 168 - "nbt/liftf" -> "Secretary's Purification: Float", 169 350 "nbt/liftd" -> "Secretary's Purification: Double", 170 - "nbt/literal/collection" -> "Secretary's Reflection: Collection", 171 - "nbt/literal/list" -> "Secretary's Reflection: Vacant List", 351 + "nbt/liftf" -> "Secretary's Purification: Float", 172 352 "nbt/literal/array1" -> "Secretary's Reflection: Vacant Byte Array", 173 353 "nbt/literal/array2" -> "Secretary's Reflection: Vacant Integer Array", 174 354 "nbt/literal/array4" -> "Secretary's Reflection: Vacant Long Array", 175 - "empty_map" -> "Vacant Reflection: Map", 355 + "nbt/literal/collection" -> "Secretary's Reflection: Collection", 356 + "nbt/literal/list" -> "Secretary's Reflection: Vacant List", 176 357 "nbt/serialize" -> "Exporter's Purification", 177 - "tripwire" -> "Tripwire Reflection", 178 - "nbt/deserialize" -> "Importer's Purification", 179 - "jvm/class_of_iota" -> "Classifier Purification II", 180 - "jvm/class_of_payload" -> "Classifier Purification I", 181 - "jvm/newinstance_unboxed" -> "Constructor Purification II", 182 - "jvm/newinstance_boxed" -> "Constructor Purification I", 183 - "malloc" -> "Allocator's Purification", 184 - "free" -> "Deallocator's Gambit", 185 - "staffcast_factory" -> "Lani's Greater Gambit", 186 - "staffcast_factory/lazy" -> "Lani's Lesser Gambit", 187 - "metatable" -> "Patchwork Exaltation", 188 - "murmur" -> "Murmur Reflection", 189 358 "reveal" -> "Greater Reveal", 190 - "dye_offhand" -> "Apply Pigment", 191 359 "rotate" -> "Ferris Distillation", 360 + "snow" -> "Summon Snow", 361 + "staffcast_factory" -> "Lani's Greater Gambit", 362 + "staffcast_factory/lazy" -> "Lani's Lesser Gambit", 192 363 "take" -> "Retention Distillation", 193 - "drop" -> "Rejection Distillation", 194 - "where" -> "Deductive Purification", 364 + "tripwire" -> "Tripwire Reflection", 365 + "unfox" -> "Vulpine Expulsion", 195 366 "whatthefuck" -> "Suffering", 196 - "modulo" -> "Modulus Distillation II", 367 + "where" -> "Deductive Purification", 197 368 ) do gen.add(s"hexcasting.action.hexic:$action", name) 369 + gen.add("hexcasting.special.hexic:tuple", "Coupler's Gambit") 370 + gen.add("hexcasting.special.hexic:tuple.n", "Coupler's Gambit %s") 198 371 for (klass, name) <- Vector( 372 + "erase" -> "an item entity or vector", 199 373 "int_or_list" -> "§aint§r or §5[§aint§5]§r", 200 374 ) do gen.add(s"hexcasting.mishap.invalid_value.class.hexic:$klass", name) 201 375 for (ty, name) <- Vector( 202 - "tripwire" -> "Tripwire", 203 376 "nbt" -> "Tag", 377 + "tripwire" -> "Tripwire", 204 378 "variant" -> "Concept", 205 379 ) do gen.add(s"hexcasting.iota.hexic:$ty", name) 206 - gen.add("itemGroup.hexic.group", "Hexic") 380 + gen.add("hexcasting.mishap.bad_block.hexic:erase", "a block holding a casting item or acting as an iota holder") 381 + gen.add("hexcasting.mishap.bad_item.hexic:erase", "a casting item or iota holder") 207 382 gen.add("hexic.bad_metatable", "Expected a map in the §a%s§r property but got %s") 383 + gen.add("itemGroup.hexic.group", "Hexic") 208 384 gen.add("text.hexic.or_map", "%s or map") 209 385 210 386 for (color, item) <- Mediaweave.colors do ··· 218 394 case 6 => s"${item.color.humanName} Media Pouch" 219 395 case 12 => s"Large ${item.color.humanName} Media Pouch" 220 396 case s => throw IllegalStateException(s"Unhandled bundle size $s")) 397 + gen.add(wizard, "Wizard") 221 398 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 222 399 Registries.ITEM.forEach: 223 400 case p: PigmentItem => gen.add("item.hexic.stringworm." + p.getTranslationKey, "Shimmering " + hexLang(p.getTranslationKey).replace("Pigment", "Stringworm")) 224 401 case e => println(e) 225 - gen.add("tag.item.hexic.mediaweaves", "Mediaweave") 226 - gen.add("hexic.media_bundle.items", "%s/%s") 227 - gen.add("hexic.media.infinite", "%s: %s") 228 - gen.add("hexic.media.finite", "%s: %s/%s (%s)") 402 + 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.") 403 + gen.add("book.hexic.page.erase", "Erases the _Hex or iota contained within a dropped item or block. Costs one dust per item.") 404 + gen.add("book.hexic.page.get_other_caster", "Adds the closest sentient being, excluding me, to the stack.") 405 + gen.add("book.hexic.page.modulo", "Similar to Modulus, but differs for negative numbers: -8 %%₁ 3 = -2, but -8 %%₂ 3 = 1.") 406 + gen.add("book.hexic.page.murmur", "Adds the phrase on the $(o)tip of my tongue/$ to the stack, regardless of whether I intend to say it.") 407 + gen.add("hexdoc.hexic.description", "Miscellaneous neat features and QoL patterns for Hex Casting") 408 + gen.add("hexdoc.hexic.title", "Hexic") 229 409 gen.add("hexic.media.external", "Media") 410 + gen.add("hexic.media.finite", "%s: %s/%s (%s)") 411 + gen.add("hexic.media.infinite", "%s: %s") 230 412 gen.add("hexic.media.internal", "Trinkets") 413 + gen.add("hexic.media_bundle.items", "%s/%s") 231 414 gen.add("hexic.spell_memory", "Hex memorized") 415 + gen.add("tag.item.hexic.mediaweaves", "Mediaweave") 232 416 gen.add("text.hexic.pigment_holder_item", "an item storing a pigment") 233 - gen.add(wizard, "Wizard") 234 - gen.add("hexdoc.hexic.title", "Hexic") 235 - gen.add("hexdoc.hexic.description", "Miscellaneous neat features and QoL patterns for Hex Casting") 236 - 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.") 237 - gen.add("book.hexic.page.modulo", "Similar to Modulus, but differs for negative numbers: -8 %%₁ 3 = -2, but -8 %%₂ 3 = 1.") 238 - gen.add("book.hexic.page.murmur", "Adds the phrase on the $(o)tip of my tongue/$ to the stack, regardless of whether I intend to say it.") 239 417 pack.addProvider: 240 418 new FabricRecipeProvider(_): 241 419 override def generate(consumer: Consumer[RecipeJsonProvider]): Unit =
+8 -6
src/client/scala/org/eu/net/pool/hexic/mixin/client/InventoryScreenMixin.java
··· 29 29 30 30 @Inject(method = "mouseClicked", at = @At(value = "HEAD"), cancellable = true) 31 31 void mouseClicked(double mouseX, double mouseY, int button, CallbackInfoReturnable<Boolean> cir) { 32 - boolean creative = (AbstractInventoryScreen<?>) this instanceof CreativeInventoryScreen; 33 - if (Math.random() == 0 && Math.random() == 0 && Math.random() == 0 && Math.random() == 0) creative = Math.random() == 0; // intellij is too smart for its own good 32 + boolean creative = false; 33 + if ((AbstractInventoryScreen<?>) this instanceof CreativeInventoryScreen c) { 34 + if (!c.isInventoryTabSelected()) return; 35 + creative = true; 36 + } 34 37 mouseX -= x; 35 38 mouseY -= y; 36 39 double lx = creative ? mouseX - 89 : mouseX - 50; ··· 40 43 if (FabricLoader.getInstance().isDevelopmentEnvironment()) 41 44 System.out.printf("s=(%d, %d) m=(%f, %f) l=(%f, %f)\n", x, y, mouseX, mouseY, lx, ly); 42 45 if (lx < w && lx > -w && ly < 0 && ly > -h) { 43 - cir.setReturnValue(true); 44 46 byte flags = 0; 45 47 var c = client.player.getComponent(PlayerInfoComponent.key()); 46 48 var held = handler.getCursorStack(); ··· 54 56 handler.setCursorStack(right); 55 57 c.rightWeave_$eq(ItemStack.EMPTY); 56 58 flags |= 3; 57 - } 59 + } else return; 58 60 } else { 59 61 var left = c.leftWeave(); 60 62 if (left.isEmpty() && !held.isEmpty() && held.getItem() instanceof org.eu.net.pool.hexic.Mediaweave) { ··· 65 67 handler.setCursorStack(left); 66 68 c.leftWeave_$eq(ItemStack.EMPTY); 67 69 flags |= 7; 68 - } 70 + } else return; 69 71 } 70 - if (creative && (flags & 1) != 0) flags--; 71 72 // submit our changes to the server 72 73 if (flags != 0) { 73 74 var buf = PacketByteBufs.create(); ··· 79 80 else 80 81 buf.writeItemStack(c.rightWeave()); 81 82 ClientPlayNetworking.send(Identifier.of("hexic", "sync_mediaweave"), buf); 83 + cir.setReturnValue(true); 82 84 } 83 85 } 84 86 }
+33 -1
src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/hexic/lists2.json
··· 11 11 "anchor": "hexic:rotate", 12 12 "input": "[a], int", 13 13 "output": "[a]", 14 - "text": "Rotates the array leftward: an argument of 1 moves the first item of the list to the end. Negative numbers allow rotating the list rightward." 14 + "text": "Rotates the list leftward: an argument of 1 moves the first item of the list to the end. Negative numbers allow rotating the list rightward." 15 15 }, 16 16 { 17 17 "type": "hexcasting:pattern", ··· 60 60 "input": "[num]", 61 61 "output": "[int]", 62 62 "text": "Adds each index to the list based on the value. For example, [1, 2, 3] becomes [0, 1, 1, 2, 2, 2]. Zero or negative numbers do not appear at all." 63 + }, 64 + { 65 + "type": "hexcasting:pattern", 66 + "op_id": "hexcasting:mul", 67 + "anchor": "hexcasting:mul", 68 + "input": "[a], [b]", 69 + "output": "[[a, b]]", 70 + "text": "Calculates the outer product of two lists. The resulting list has an entry for every possible pairing between the input lists." 71 + }, 72 + { 73 + "type": "hexcasting:pattern", 74 + "op_id": "hexcasting:mul", 75 + "anchor": "hexcasting:mul", 76 + "input": "[a], [b]", 77 + "output": "[[a, b]]", 78 + "text": "Calculates the inner product of two lists. If the two lists were arranged side-by-side, the resulting list has one entry for every row (unpaired elements are skipped)." 79 + }, 80 + { 81 + "type": "hexcasting:pattern", 82 + "op_id": "hexic:extract", 83 + "anchor": "hexic:extract", 84 + "input": "[a], int", 85 + "output": "[a], [a], a", 86 + "text": "Extracts an iota from the given list, and breaks the list apart around it. Returns all items before the iota, all items after the iota, and finally the iota itself." 87 + }, 88 + { 89 + "type": "hexcasting:pattern", 90 + "op_id": "hexic:grep", 91 + "anchor": "hexic:grep", 92 + "input": "[a], [a → bool]", 93 + "output": "[a]", 94 + "text": "Discards iotas from the given list unless they satisfy the given predicate. Using $(l:patterns/meta#hexcasting:halt)$(action)Charon's Gambit/$ discards the current and all remaining iotas." 63 95 } 64 96 ] 65 97 }
+32
src/main/resources/assets/hexcasting/patchouli_books/thehexbook/zh_cn/entries/addon/hexic/lists2.json
··· 60 60 "input": "[num]", 61 61 "output": "[int]", 62 62 "text": "根据数值将各下标组为列表返回。例如,[1, 2, 3] 会变为 [0, 1, 1, 2, 2, 2]。0 和负数处的下标不会在返回值中出现。" 63 + }, 64 + { 65 + "type": "hexcasting:pattern", 66 + "op_id": "hexcasting:mul", 67 + "anchor": "hexcasting:mul", 68 + "input": "[a], [b]", 69 + "output": "[[a, b]]", 70 + "text": "计算两列表的外积。即在两输入列表各取一个元素,将所有可能的值对组为列表返回。" 71 + }, 72 + { 73 + "type": "hexcasting:pattern", 74 + "op_id": "hexcasting:mul", 75 + "anchor": "hexcasting:mul", 76 + "input": "[a], [b]", 77 + "output": "[[a, b]]", 78 + "text": "计算两列表的内积。即纵向排布两列表的元素,而后相邻排列两列表,将每行的元素对组为列表返回(跳过未配对的元素)。" 79 + }, 80 + { 81 + "type": "hexcasting:pattern", 82 + "op_id": "hexic:extract", 83 + "anchor": "hexic:extract", 84 + "input": "[a], int", 85 + "output": "[a], [a], a", 86 + "text": "从所给列表中取出 iota,并在该位置分割列表。返回指定 iota 左侧的所有元素,而后返回其右侧的所有元素,最后返回该 iota。" 87 + }, 88 + { 89 + "type": "hexcasting:pattern", 90 + "op_id": "hexic:grep", 91 + "anchor": "hexic:grep", 92 + "input": "[a], [a → bool]", 93 + "output": "[a]", 94 + "text": "从所给列表中移除不满足所给谓词的 iota。使用$(l:patterns/meta#hexcasting:halt)$(action)卡戎之策略/$会清除当前 iota 和后续所有 iota。" 63 95 } 64 96 ] 65 97 }
+12
src/main/resources/assets/hexic/jsonpatch/basics.jsonpatch
··· 1 + @version "1"; 2 + @target "hexcasting:patchouli_books/thehexbook/en_us/entries/patterns/basics.json"; 3 + 4 + arrays.insert($pages, 4, arrays.removeAt($pages, 11)); 5 + arrays.insert($pages, 1, { 6 + type: "hexcasting:pattern", 7 + op_id: "hexic:get_other_caster", 8 + anchor: "hexic:get_other_caster", 9 + input: "", 10 + output: "entity | null", 11 + text: "book.hexic.page.get_other_caster" 12 + });
+11
src/main/resources/assets/hexic/jsonpatch/readwrite_blocks.jsonpatch
··· 1 + @version "1"; 2 + @target "hexcasting:patchouli_books/thehexbook/en_us/entries/patterns/readwrite_blocks.json"; 3 + 4 + $pages = arrays.push($pages, { 5 + type: "hexcasting:pattern", 6 + op_id: "hexic:erase", 7 + anchor: "hexic:erase", 8 + input: "vec | entity", 9 + output: "", 10 + text: "book.hexic.page.erase" 11 + });
+20 -4
src/main/resources/assets/hexic/lang/zh_cn.json
··· 1 1 { 2 2 "book.hexic.page.dye_offhand": "将所给染色剂内化到我副手中的物品(如$(l:items/hexcasting)$(item)施法物品/$)里。", 3 + "book.hexic.page.erase": "清除物品实体或方块中的$(hex)咒术/$或 iota。每个物品消耗 1 个紫水晶粉。", 4 + "book.hexic.page.get_other_caster": "将离我最近的有智慧存在压入栈,我自己不计入统计范围。", 3 5 "book.hexic.page.modulo": "与余数之馏化类似,但两者在负数上的表现不同:-8 %%₁ 3 = -2,而 -8 %%₂ 3 = 1。", 4 6 "book.hexic.page.murmur": "将$(o)在我嘴边/$的话语压入栈中,后续是否要说出不影响结果。", 5 7 "hexcasting.action.hexic:drop": "拒斥之馏化", 6 8 "hexcasting.action.hexic:dye_offhand": "应用染色剂", 7 9 "hexcasting.action.hexic:empty_map": "空无之精思:映射", 10 + "hexcasting.action.hexic:erase": "清除方块", 11 + "hexcasting.action.hexic:extract": "切除器之策略", 12 + "hexcasting.action.hexic:fox": "狐狸之策略", 8 13 "hexcasting.action.hexic:free": "释放器之策略", 14 + "hexcasting.action.hexic:get_other_caster": "对偶之精思", 15 + "hexcasting.action.hexic:grep": "精炼之馏化", 9 16 "hexcasting.action.hexic:jvm/class_of_iota": "判类器之纯化,第二型", 10 17 "hexcasting.action.hexic:jvm/class_of_payload": "判类器之纯化,第一型", 11 18 "hexcasting.action.hexic:jvm/newinstance_boxed": "构造器之纯化,第一型", ··· 22 29 "hexcasting.action.hexic:nbt/liftd": "秘书之纯化:双精度浮点型", 23 30 "hexcasting.action.hexic:nbt/liftf": "秘书之纯化:单精度浮点型", 24 31 "hexcasting.action.hexic:nbt/literal/array1": "秘书之精思:空字节数组", 25 - "hexcasting.action.hexic:nbt/literal/array2": "秘书之精思:空短整型数组", 26 - "hexcasting.action.hexic:nbt/literal/array4": "秘书之精思:空整型数组", 32 + "hexcasting.action.hexic:nbt/literal/array2": "秘书之精思:空整型数组", 33 + "hexcasting.action.hexic:nbt/literal/array4": "秘书之精思:空长整型数组", 27 34 "hexcasting.action.hexic:nbt/literal/collection": "秘书之精思:容器", 28 35 "hexcasting.action.hexic:nbt/literal/list": "秘书之精思:空列表", 29 36 "hexcasting.action.hexic:nbt/serialize": "导出器之纯化", ··· 33 40 "hexcasting.action.hexic:staffcast_factory/lazy": "Lani之初等策略", 34 41 "hexcasting.action.hexic:take": "维持之馏化", 35 42 "hexcasting.action.hexic:tripwire": "绊线之精思", 43 + "hexcasting.action.hexic:unfox": "狐狸之驱逐", 36 44 "hexcasting.action.hexic:whatthefuck": "苦痛", 37 45 "hexcasting.action.hexic:where": "演绎之纯化", 38 - "hexcasting.iota.hexic:jvm/class": "类", 39 - "hexcasting.iota.hexic:jvm/pointer": "地址", 46 + "hexcasting.special.hexic:tuple": "耦合器之策略", 47 + "hexcasting.special.hexic:tuple.n": "耦合器之策略:%s", 40 48 "hexcasting.iota.hexic:nbt": "标签", 41 49 "hexcasting.iota.hexic:tripwire": "绊线", 42 50 "hexcasting.iota.hexic:variant": "概念", 51 + "hexcasting.mishap.bad_block.hexic:erase": "一个存有施法物品或用于存储iota的方块", 52 + "hexcasting.mishap.bad_item.hexic:erase": "一个施法物品或iota存储器", 53 + "hexcasting.mishap.invalid_value.class.hexic:erase": "一个物品实体或向量", 43 54 "hexcasting.mishap.invalid_value.class.hexic:int_or_list": "§aint§r或§5[§aint§5]§r", 44 55 "hexdoc.hexic.description": "各种好用的特性和提升体验的图案,专为咒法学设计", 45 56 "hexdoc.hexic.title": "Hexic", ··· 144 155 "item.hexic.stringworm.item.hexcasting.pride_colorizer_pansexual": "闪亮的泛性恋毛绒线虫", 145 156 "item.hexic.stringworm.item.hexcasting.pride_colorizer_plural": "闪亮的多重人格毛绒线虫", 146 157 "item.hexic.stringworm.item.hexcasting.pride_colorizer_transgender": "闪亮的跨性别毛绒线虫", 158 + "item.hexic.stringworm.item.oneironaut.pigment_flame": "闪亮的火焰毛绒线虫", 159 + "item.hexic.stringworm.item.oneironaut.pigment_echo": "闪亮的回响毛绒线虫", 160 + "item.hexic.stringworm.item.oneironaut.pigment_noosphere": "闪亮的智识毛绒线虫", 161 + "item.hexic.stringworm.item.oneironaut.pigment_frenzyflame": "闪亮的狂热毛绒线虫", 147 162 "item.hexic.stringworm_action": "毛绒线虫", 148 163 "item.hexic.stringworm_hex": "毛绒线虫", 149 164 "item.hexic.stringworm_media": "毛绒线虫", ··· 154 169 "item.hexic.yellow_mediaweave": "黄色媒质布匹", 155 170 "itemGroup.hexic.group": "Hexic", 156 171 "tag.item.hexic.mediaweaves": "媒质布匹", 172 + "text.hexic.or_map": "%s或映射", 157 173 "text.hexic.pigment_holder_item": "存有染色剂的物品" 158 174 }
-6
src/main/resources/data/hexcasting/hexcasting/tags/action/per_world_pattern.json
··· 1 - { 2 - "values": [ 3 - "hexcasting:craft/artifact", 4 - "hexcasting:craft/trinket" 5 - ] 6 - }
+7
src/main/resources/data/hexcasting/tags/action/per_world_pattern.json
··· 1 + { 2 + "values": [ 3 + "hexcasting:craft/artifact", 4 + "hexcasting:craft/trinket", 5 + "hexic:snow" 6 + ] 7 + }
+5
src/main/resources/data/hexcasting/tags/action/requires_enlightenment.json
··· 1 + { 2 + "values": [ 3 + "hexic:snow" 4 + ] 5 + }
+1
src/main/resources/data/hexic/dimension_type/cell.json
··· 2 2 "ultrawarm": false, 3 3 "natural": false, 4 4 "coordinate_scale": 1.0, 5 + "effects": "the_end", 5 6 "has_skylight": false, 6 7 "has_ceiling": false, 7 8 "ambient_light": 1.0,
+6 -2
src/main/resources/hexic.mixins.json
··· 5 5 "compatibilityLevel": "JAVA_17", 6 6 "mixins": [ 7 7 "ActionRegistryEntryMixin", 8 + "BiomeMixin", 9 + "CastingVMMixin", 10 + "DimIotaMixin", 8 11 "EntityMixin", 9 12 "HopperEndpointRegistryMixin", 10 13 "ItemMixin", ··· 13 16 "ItemStackMixin", 14 17 "LivingEntityAccess", 15 18 "MediafiedItemManagerIndexMixin", 19 + "OpDimTeleport$SpellMixin", 16 20 "OpEdifySaplingMixin", 17 21 "OpEdifySaplingMixin$Spell", 18 22 "OpObservePropertyMixin", ··· 22 26 "OpTickMixin$SpellMixin", 23 27 "PacketByteBufMixin", 24 28 "PatternIotaMixin", 25 - "PhantomEntityMixin", 26 29 "PlayerInventoryMixin", 27 30 "SimpleRegistryMixin", 28 - "StaffCastEnvMixin" 31 + "StaffCastEnvMixin", 32 + "WorldMixin" 29 33 ], 30 34 "injectors": { 31 35 "defaultRequire": 1
+2 -40
src/main/scala/org/eu/net/pool/hexic/EarlyRiser.scala
··· 16 16 import java.security.ProtectionDomain 17 17 import org.objectweb.asm.{Attribute, ClassReader, ClassWriter, Handle, Opcodes, Type} 18 18 import sun.instrument.InstrumentationImpl 19 - import sun.misc.Unsafe 20 19 21 20 import java.io.File 22 21 import java.lang.invoke.{MethodHandles, MethodType} ··· 40 39 class Dummy: 41 40 type T <: Enum[T] 42 41 43 - given Unsafe = 44 - val klass = classNamed("sun.misc.Unsafe").get.runtimeClass 45 - val field = klass.getDeclaredField("theUnsafe") 46 - field.setAccessible(true) 47 - field.get(null).asInstanceOf[Unsafe] 48 - 49 - extension (f: Field) 50 - inline def staticBase(using u: Unsafe) = u.staticFieldBase(f) 51 - inline def staticOffset(using u: Unsafe) = u.staticFieldOffset(f) 52 - 53 - val godLookup: MethodHandles.Lookup = 54 - val c = classOf[MethodHandles.Lookup] 55 - val f = c.getDeclaredField("IMPL_LOOKUP") 56 - unsafe.getObject(f.staticBase, f.staticOffset) 57 - .asInstanceOf[MethodHandles.Lookup] 58 - 59 - given instrumentation: Instrumentation = 60 - try 61 - val d = Dummy() 62 - val byteBuddy = classNamed("net.bytebuddy.agent.ByteBuddyAgent$AgentProvider$ForByteBuddyAgent").get.runtimeClass.asInstanceOf[Class[d.T]] 63 - val mth = byteBuddy.getMethod("resolve") 64 - val inst = Enum.valueOf(byteBuddy, "INSTANCE") 65 - val path = mth.invoke(inst).asInstanceOf[File] 66 - val instr = classNamed("sun.instrument.InstrumentationImpl").get.runtimeClass 67 - val h = godLookup.findStatic(instr, "loadAgent", MethodType.methodType(Void.TYPE, classOf[String])) 68 - h.invoke(path.getAbsolutePath) 69 - ByteBuddyAgent.getInstrumentation 70 - catch case e: Throwable => 71 - e.printStackTrace() 72 - ByteBuddyAgent.install() 42 + given instrumentation: Instrumentation = ByteBuddyAgent.install() 73 43 class ChangesFlag private[hexic](): 74 44 private[hexic] var value = false 75 45 ··· 181 151 ClassTinkerers.define(k.dots, writer.toByteArray) 182 152 addTransformer: (name: String, c: ClassNode) => 183 153 assert(name == name.dots) 184 - if transformedClasses isDefinedAt name.slashes then panic(name) 185 154 println(s"Remapping $name") 186 155 madeChanges() // not gonna try checking 187 156 c.methods.forEach: m => ··· 246 215 madeChanges() 247 216 catch case e: Throwable => 248 217 e.printStackTrace() 249 - panic(s"$e") 250 - 251 - @tailrec 252 - def panic(reason: String): Nothing = 253 - Bootstrap.SYSOUT.println(s"thread '${Thread.currentThread.getName}' panicked at '$reason'") 254 - Bootstrap.SYSOUT.flush() 255 - Runtime.getRuntime.halt(101) 256 - panic(reason) 218 + sys.exit(1)
+627 -109
src/main/scala/org/eu/net/pool/hexic/Hexic.scala
··· 1 1 //noinspection NotImplementedCode 2 2 package org.eu.net.pool.hexic 3 3 4 + import at.petrak.hexcasting.api.addldata.ADMediaHolder 4 5 import at.petrak.hexcasting.api.casting.{ActionRegistryEntry, ParticleSpray, RenderedSpell, SpellList} 5 6 import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic 6 7 import at.petrak.hexcasting.api.casting.arithmetic.operator.Operator ··· 8 9 import at.petrak.hexcasting.api.casting.eval.env.PlayerBasedCastEnv 9 10 import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect.DoMishap 10 11 import at.petrak.hexcasting.api.casting.eval.sideeffects.{EvalSound, OperatorSideEffect} 11 - import at.petrak.hexcasting.api.casting.eval.vm.{CastingImage, CastingVM, ContinuationFrame, SpellContinuation} 12 - import at.petrak.hexcasting.api.casting.eval.{CastResult, CastingEnvironment, MishapEnvironment, OperationResult, ResolvedPattern, ResolvedPatternType} 12 + import at.petrak.hexcasting.api.casting.eval.vm.{CastingImage, CastingVM, ContinuationFrame, FrameEvaluate, SpellContinuation} 13 + import at.petrak.hexcasting.api.casting.eval.{CastResult, CastingEnvironment, CastingEnvironmentComponent, MishapEnvironment, OperationResult, ResolvedPattern, ResolvedPatternType} 13 14 import at.petrak.hexcasting.api.casting.iota.* 14 15 import at.petrak.hexcasting.api.casting.math.{HexDir, HexPattern} 15 - import at.petrak.hexcasting.api.casting.mishaps.{Mishap, MishapBadCaster, MishapBadOffhandItem, MishapInvalidIota, MishapInvalidOperatorArgs, MishapNotEnoughArgs, MishapOthersName} 16 + import at.petrak.hexcasting.api.casting.mishaps.{Mishap, MishapBadCaster, MishapBadOffhandItem, MishapInvalidIota, MishapInvalidOperatorArgs, MishapNotEnoughArgs, MishapOthersName, MishapTooManyCloseParens} 16 17 import at.petrak.hexcasting.api.pigment.FrozenPigment 17 18 import at.petrak.hexcasting.api.utils.{HexUtils, MediaHelper} 18 19 import at.petrak.hexcasting.common.lib.{HexAttributes, HexItems, HexRegistries, HexSounds} 19 20 import at.petrak.hexcasting.common.lib.hex.HexEvalSounds 20 21 import at.petrak.hexcasting.fabric.cc.HexCardinalComponents 21 22 import at.petrak.hexcasting.xplat.IXplatAbstractions 23 + import carpet.patches.EntityPlayerMPFake 22 24 import com.chocohead.mm.api.ClassTinkerers 23 25 import com.google.gson.{JsonElement, JsonObject} 24 26 import com.ibm.icu.util.MeasureUnit ··· 33 35 import dev.onyxstudios.cca.api.v3.component.{Component, ComponentKey, ComponentRegistry} 34 36 import dev.onyxstudios.cca.api.v3.component.sync.AutoSyncedComponent 35 37 import dev.onyxstudios.cca.api.v3.entity.{EntityComponentFactoryRegistry, EntityComponentInitializer, RespawnCopyStrategy} 38 + import dev.onyxstudios.cca.api.v3.level.{LevelComponentFactoryRegistry, LevelComponentInitializer} 36 39 import kotlin.Pair 37 40 import kotlin.text.Charsets 38 41 import miyucomics.hexcellular.{PropertyIota, StateStorage} ··· 51 54 import net.fabricmc.fabric.api.transfer.v1.transaction.{Transaction, TransactionContext} 52 55 import net.fabricmc.loader.api.FabricLoader 53 56 import net.minecraft.Bootstrap 54 - import net.minecraft.block.Block 57 + import net.minecraft.block.{AbstractBlock, Block, BlockRenderType, BlockState, BlockWithEntity, ShapeContext} 55 58 import net.minecraft.command.argument.{EntityArgumentType, NbtElementArgumentType, UuidArgumentType} 56 59 import net.minecraft.command.{CommandException, EntitySelector} 57 60 import net.minecraft.entity.player.PlayerEntity 58 61 import net.minecraft.fluid.Fluid 59 62 import net.minecraft.inventory.{SidedInventory, StackReference} 60 - import net.minecraft.item.{Item, ItemStack, Items} 63 + import net.minecraft.item.{BlockItem, Item, ItemStack, ItemUsageContext, Items} 61 64 import net.minecraft.nbt.* 62 65 import net.minecraft.nbt.visitor.StringNbtWriter 63 66 import net.minecraft.registry.tag.TagKey ··· 69 72 import net.minecraft.text.{HoverEvent, LiteralTextContent, MutableText, Style, Text, TextColor, TextContent, Texts} 70 73 import net.minecraft.util.dynamic.Codecs 71 74 import net.minecraft.util.math.{BlockPos, ChunkPos, Direction, Vec3d} 72 - import net.minecraft.util.{Arm, ClickType, DyeColor, Formatting, Hand, Identifier, Rarity, TypedActionResult, Util, Uuids, WorldSavePath} 73 - import net.minecraft.world.{TeleportTarget, World} 74 - import org.eu.net.pool.common_curses.client.CommonCursesClientKt 75 - import org.eu.net.pool.common_curses.{CommonCursesKt, SlotAccess, TextManipulator} 75 + import net.minecraft.util.{ActionResult, Arm, ClickType, DyeColor, Formatting, Hand, Identifier, Rarity, TypedActionResult, Util, Uuids, WorldSavePath} 76 + import net.minecraft.world.biome.Biome 77 + import net.minecraft.world.{BlockView, TeleportTarget, World} 76 78 import org.eu.net.pool.hexic 77 79 import org.eu.net.pool.hexic.ducks.SimpleRegistryDuck 78 80 import org.objectweb.asm.{ClassWriter, tree} ··· 81 83 import org.spongepowered.asm.mixin.injection.callback.{CallbackInfo, CallbackInfoReturnable} 82 84 import ram.talia.hexal.api.casting.iota.{GateIota, MoteIota} 83 85 import ram.talia.moreiotas.api.casting.iota.{EntityTypeIota, IotaTypeIota, ItemStackIota, ItemTypeIota, MatrixIota, StringIota} 84 - import sun.misc.Unsafe 85 86 86 87 import java.io.{File, FileNotFoundException, FileOutputStream, IOException, InputStream} 87 88 import java.lang.invoke.MethodHandles 88 89 import java.lang.reflect.{Constructor, Field, Member, Method} 90 + import java.nio.ByteBuffer 89 91 import java.nio.file.{Files, Path, Paths, StandardOpenOption} 90 92 import java.util.{Optional, UUID} 91 93 import java.{lang, util} 92 94 import scala.annotation.unchecked.uncheckedVariance 93 - import scala.annotation.{experimental, showAsInfix, tailrec, targetName, unused} 94 - import scala.collection.convert.ImplicitConversions.* 95 + import scala.annotation.{elidable, experimental, showAsInfix, tailrec, targetName, unused} 96 + import scala.ref.WeakReference 97 + import scala.util.{Failure, Success, Try} 98 + export scala.collection.convert.ImplicitConversions.* 95 99 import scala.collection.mutable 96 100 import scala.compiletime.summonFrom 97 - import scala.concurrent.{Future, Promise} 101 + import scala.concurrent.{Await, ExecutionContext, Future, Promise} 98 102 import scala.jdk.CollectionConverters.* 99 103 import scala.language.experimental.{macros, saferExceptions} 100 104 import scala.language.{dynamics, existentials, implicitConversions, postfixOps, reflectiveCalls} ··· 114 118 import at.petrak.hexcasting.api.casting.eval.vm.ContinuationFrame.Type 115 119 import at.petrak.hexcasting.api.item.{IotaHolderItem, MediaHolderItem} 116 120 import at.petrak.hexcasting.api.misc.MediaConstants 121 + import at.petrak.hexcasting.common.casting.actions.eval.OpEval 117 122 import at.petrak.hexcasting.common.items.magic.{ItemMediaHolder, ItemPackagedHex} 118 123 import at.petrak.hexcasting.common.msgs.{MsgClearSpiralPatternsS2C, MsgNewSpiralPatternsS2C, MsgOpenSpellGuiS2C} 119 124 import at.petrak.hexcasting.fabric.cc.adimpl.CCMediaHolder ··· 135 140 import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents 136 141 import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup 137 142 import net.minecraft.block.AbstractBlock 143 + import net.minecraft.entity.passive.FoxEntity 138 144 import net.minecraft.stat.Stats 139 145 import org.eu.net.pool.hexic.mixin.{ItemStackAccess, LivingEntityAccess} 140 146 import at.petrak.hexcasting.common.casting.actions.eval.OpEval 141 147 import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType 142 148 import at.petrak.hexcasting.common.casting.actions.spells.OpBreakBlock 143 - import kotlin.io.FileAlreadyExistsException 149 + import at.petrak.hexcasting.common.casting.actions.spells.OpErase 150 + import at.petrak.hexcasting.api.casting.mishaps.MishapBadEntity 151 + import net.minecraft.entity.ItemEntity 152 + import net.minecraft.entity.decoration.ItemFrameEntity 153 + import at.petrak.hexcasting.api.casting.castables.SpellAction 154 + import gay.`object`.ioticblocks.api.IoticBlocksAPI 155 + import at.petrak.hexcasting.api.casting.mishaps.MishapBadBlock 156 + import at.petrak.hexcasting.common.casting.actions.eval.OpEval 157 + import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType 158 + import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod 159 + import com.llamalad7.mixinextras.injector.wrapoperation.{Operation, WrapOperation} 160 + import net.fabricmc.fabric.api.`object`.builder.v1.block.entity.FabricBlockEntityTypeBuilder 161 + import net.minecraft.block.entity.BlockEntity 162 + import net.minecraft.network.listener.ClientPlayPacketListener 163 + import net.minecraft.network.packet.Packet 164 + import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket 165 + import net.minecraft.util.hit.BlockHitResult 166 + import net.minecraft.util.shape.{VoxelShape, VoxelShapes} 167 + import org.spongepowered.asm.mixin.Mixin 168 + 169 + import scala.collection.immutable.BitSet 170 + import at.petrak.hexcasting.common.casting.actions.eval.OpEval 171 + import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType 172 + 173 + import scala.util.matching.Regex 174 + import at.petrak.hexcasting.api.casting.eval.vm.CastingImage.ParenthesizedIota 144 175 import net.beholderface.oneironaut.casting.iotatypes.DimIota 145 176 import net.fabricmc.fabric.api.dimension.v1.FabricDimensions 146 177 import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents ··· 154 185 import net.minecraft.entity.ExperienceOrbEntity 155 186 import net.minecraft.network.packet.s2c.play.PositionFlag 156 187 import net.minecraft.world.chunk.{ChunkStatus, WorldChunk} 188 + 189 + import scala.concurrent.duration.Duration 157 190 158 191 given Logger = LoggerFactory.getLogger("hexic") 159 192 ··· 190 223 def apply[T: Outcome](xs: T*): OperationResult => OperationResult = res => res.->(xs*) 191 224 extension (op: OperationResult) 192 225 def ->[T: Outcome](xs: T*): OperationResult = 193 - xs.foldLeft(op)(summon[Outcome[T]](_, _)) 226 + (op /: xs)(summon[Outcome[T]](_, _)) 194 227 195 228 given Outcome[OperationResult => OperationResult] = (res, f) => f(res) 196 229 given [T: Outcome]: Outcome[Seq[T]] = (res, value) => res -> Outcome(value*) ··· 256 289 Patterns.register(id, pattern): 257 290 OperationAction(pattern) 258 291 259 - inline def unsafe(using u: Unsafe) = u 260 292 val hexXplat: IXplatAbstractions = IXplatAbstractions.INSTANCE 261 293 262 294 extension (ctx: StringContext) def ifModLoaded(`then`: => Unit, `else`: => Unit = {}): Unit = ··· 400 432 401 433 class PlayerInfoComponent( 402 434 val player: PlayerEntity, 403 - var wispMedia: Option[Long] = None, 404 435 var murmur: Option[String] = None, 405 436 var leftWeave: ItemStack = ItemStack.EMPTY, 406 437 var rightWeave: ItemStack = ItemStack.EMPTY, 407 438 var chatLines: Seq[Text] = Seq(), 439 + var foxType: Option[FoxEntity.Type] = None, 408 440 ) extends Component, AutoSyncedComponent: 409 441 override def readFromNbt(c: NbtCompound): Unit = 410 - if c.getBoolean("isWisp") then 411 - wispMedia = Some(c.getLong("media")) 412 - else 413 - wispMedia = None 414 442 if c.contains("shl", NbtElement.COMPOUND_TYPE) then 415 443 leftWeave = ItemStack.fromNbt(c.getCompound("shl")) 416 444 else ··· 420 448 else 421 449 rightWeave = ItemStack.EMPTY 422 450 chatLines = c.getList("chat", NbtElement.COMPOUND_TYPE).map(NbtOps.INSTANCE.convertTo(JsonOps.INSTANCE, _)).map(Text.Serializer.fromJson).toSeq 423 - 451 + if c.contains("fox", NbtElement.STRING_TYPE) then 452 + foxType = Some(FoxEntity.Type.valueOf(c.getString("fox"))) 453 + else 454 + foxType = None 424 455 override def writeToNbt(c: NbtCompound): Unit = 425 - wispMedia match 426 - case None => 427 - c.putBoolean("isWisp", false) 428 - case Some(media) => 429 - c.putBoolean("isWisp", true) 430 - c.putLong("media", media) 431 456 if !leftWeave.isEmpty then c.put("shl", NbtCompound().tap(leftWeave.writeNbt)) 432 457 if !rightWeave.isEmpty then c.put("shr", NbtCompound().tap(rightWeave.writeNbt)) 433 458 c.put("chat", NbtList().tap(_.addAll(chatLines.map(Text.Serializer.toJsonTree).map(JsonOps.INSTANCE.convertTo(NbtOps.INSTANCE, _))))) 459 + foxType.fold(c.remove("fox"))(f => c.putString("fox", f.name)) 434 460 object PlayerInfoComponent: 435 - val key: ComponentKey[PlayerInfoComponent] = ComponentRegistry.getOrCreate("player_wisp", classOf[PlayerInfoComponent]) 461 + given key: ComponentKey[PlayerInfoComponent] = ComponentRegistry.getOrCreate("player_wisp", classOf[PlayerInfoComponent]) 462 + given Conversion[PlayerEntity, PlayerInfoComponent] = _.getComponent(key) 436 463 private[hexic] def register(using fac: EntityComponentFactoryRegistry) = 437 464 fac.registerForPlayers(key, PlayerInfoComponent(_), RespawnCopyStrategy.LOSSLESS_ONLY) 438 465 466 + class ServerInfoComponent( 467 + var endSnowTick: Long = 0 468 + ) extends Component, AutoSyncedComponent: 469 + override def readFromNbt(tag: NbtCompound): Unit = 470 + endSnowTick = tag.getLong("snow") 471 + override def writeToNbt(tag: NbtCompound): Unit = 472 + tag.putLong("snow", endSnowTick) 473 + object ServerInfoComponent: 474 + given key: ComponentKey[ServerInfoComponent] = ComponentRegistry.getOrCreate("server_info", classOf[ServerInfoComponent]) 475 + given get: (server: MinecraftServer) => ServerInfoComponent = server.getOverworld.getLevelProperties.getComponent(key) 476 + def sync()(using server: MinecraftServer): Unit = server.getOverworld.getLevelProperties.syncComponent(key) 477 + private[hexic] def register(using fac: LevelComponentFactoryRegistry) = 478 + fac.register(key, _ => ServerInfoComponent()) 479 + 439 480 extension [S, T <: ArgumentBuilder[S, T]] (builder: T) 440 481 def literal(name: String)(body: LiteralArgumentBuilder[S] => Unit): T = builder.`then`(LiteralArgumentBuilder.literal[S](name).tap(body)) 441 482 def argument(name: String, typ: ArgumentType[?])(body: RequiredArgumentBuilder[S, ?] => Unit): T = builder.`then`(RequiredArgumentBuilder.argument(name, typ).tap(body)) ··· 508 549 inline def ?[R](f: T => R): R | Null = x match 509 550 case null => null 510 551 case x: T => f(x) 552 + inline def ??(y: T): T = x match 553 + case null => y 554 + case x: T => x 511 555 512 556 case class Pen private [hexic] (color: DyeColor) extends Item(Item.Settings().maxCount(1)): 513 557 override def use(world: World, player: PlayerEntity, hand: Hand): TypedActionResult[ItemStack] = ··· 658 702 val (canProvide, cantProvide) = nonrecursive.flatten.partition(_.canProvide) 659 703 val (canRecharge, consumables) = canProvide.partition(_.canRecharge) 660 704 val mine: M = (Option.when(consumables.nonEmpty)(consumables.map(_.getMedia).sum), Option.when(canRecharge.nonEmpty)((canRecharge.map(_.getMedia).sum, canRecharge.map(_.getMaxMedia).sum)), Option.when(cantProvide.nonEmpty)((cantProvide.map(_.getMedia).sum, cantProvide.map(_.getMaxMedia).sum))) 661 - recursive.foldLeft(mine)((p: M, q: M) => ( 705 + (mine /: recursive)((p: M, q: M) => ( 662 706 (p._1, q._1) match 663 707 case (None, None) => None 664 708 case (Some(x), None) => Some(x) ··· 702 746 case null => super.getName(stack) 703 747 case n => Text.translatable("item.hexic.stringworm." + FrozenPigment.fromNBT(n).item.getTranslationKey) 704 748 749 + def toRoman(value: Int): String = 750 + "M" * (value / 1000) + ("", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM").productElement(value % 1000 / 100) + ("", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC").productElement(value % 100 / 10) + ("", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX").productElement(value % 10) 751 + 705 752 private [hexic] object Extern: 753 + private [hexic] val worlds = mutable.Set[WeakReference[World]]() 754 + private [hexic] def getWorld(biome: Biome): World = 755 + worlds.collect: 756 + case WeakReference(world) if world.getRegistryManager.get(RegistryKeys.BIOME).getId(biome) != null => return world 757 + case dropped => worlds -= dropped 758 + null 706 759 private [hexic] def getStringworm(idx: Int) = stringworms(Stringworm.flavors(idx)) 707 760 private [hexic] def observePropertyHook(args: util.List[? <: Iota], idx: Int, argc: Int)(original: => String)(using cir: CallbackInfoReturnable[util.List[Iota]]) = 708 761 args.lastOption match ··· 725 778 original 726 779 catch case e: MishapInvalidIota => 727 780 throw MishapInvalidIota(e.getPerpetrator, e.getReverseIdx, t"${e.getExpected} or writer") 781 + private val introPattern = """^q(w*)d\1q$""".r 782 + private [hexic] def handleParentheses(vm: CastingVM, iota: Iota): Option[(CastingImage, ResolvedPatternType)] = boundary: 783 + val p = iota match 784 + case p: PatternIota => p.getPattern 785 + case _ => boundary.break(None) 786 + val measure = p.anglesSignature match 787 + case introPattern (measure) => measure 788 + case _ => boundary.break(None) 789 + val size = measure.length + 1 790 + def mishap(m: Mishap) = 791 + val safeVM = CastingVM(vm.getImage, vm.getEnv) 792 + OperatorSideEffect.DoMishap(m, Mishap.Context(p, Text.translatable("hexcasting.action.hexic:parenthesize"))).performEffect(safeVM) 793 + boundary.break(Some(safeVM.getImage, ResolvedPatternType.ERRORED)) 794 + val img = vm.getImage 795 + val parens = img.getParenCount 796 + if parens == size then 797 + img.getStack.toSeq match 798 + case Seq() => mishap(MishapNotEnoughArgs(1, 0)) 799 + case tail :+ head => Some(( 800 + CastingImage( 801 + stack = tail, 802 + parenCount = parens, 803 + parenthesized = img.getParenthesized :+ ParenthesizedIota(head, false), 804 + escapeNext = false, 805 + opsConsumed = img.getOpsConsumed, 806 + userData = img.getUserData, 807 + null 808 + ), 809 + ResolvedPatternType.EVALUATED 810 + )) 811 + else if parens > size then 812 + None // leave unescaped, so a nested hex can introject 813 + else 814 + mishap(new Mishap: 815 + override def accentColor(env: CastingEnvironment, ctx: Context): FrozenPigment = dyeColor(DyeColor.ORANGE) 816 + override def errorMessage(env: CastingEnvironment, ctx: Context): Text = ??? 817 + override def execute(env: CastingEnvironment, ctx: Context, stack: util.List[Iota]): Unit = 818 + stack.add(PatternIota(p)) 819 + ) 728 820 def splat(original: (args: util.List[Iota], env: CastingEnvironment) => util.List[Iota])(args: util.List[Iota], env: CastingEnvironment): util.List[Iota] = 729 821 try 730 822 original(args, env) ··· 736 828 737 829 val _ = 738 830 Interop.playerDeathHook = (p: PlayerEntity, out: util.List[ItemStack]) => 739 - val c = p.getComponent(PlayerInfoComponent.key) 831 + val c = p: PlayerInfoComponent 740 832 if !c.rightWeave.isEmpty then 741 833 out.add(c.rightWeave) 742 834 c.rightWeave = ItemStack.EMPTY ··· 755 847 private val DUST_AMOUNT = new DecimalFormat("###,###.##") 756 848 val wizard = Item(Item.Settings().rarity(Rarity.EPIC).maxCount(1)) 757 849 850 + val aLotOfMedia = (200000 /* max phial size */ * 6 /* phials per small pouch */ * 4 /* small pouches per large pouch */ * (36 /* inventory slots */ + 4 /* armor slots */ + 2 /* offhands */) + 20 /* healthcasting */) * MediaConstants.DUST_UNIT 851 + 852 + class Event[T, R](default: T => R) extends (T => R): 853 + private var current = default 854 + def apply(x: T): R = current(x) 855 + def apply(fn: PartialFunction[T, R]): Unit = 856 + val old = current 857 + current = fn.applyOrElse(_, old) 858 + 859 + val useItemEvent = Event[(Item, ItemUsageContext, ItemUsageContext => ActionResult), ActionResult](p => p._3(p._2)) 860 + 758 861 trait HasCodec: 759 862 def getCodec: Codec[? <: this.type] 760 863 given [T <: Mishap] => Conversion[T, HasCodec] = _.asInstanceOf 761 864 865 + class DeferMut[T](initial: => T): 866 + private var value = () => initial 867 + def apply() = value() 868 + def update(x: => T): Unit = value = () => x 869 + 762 870 lazy val itemGroup = FabricItemGroup.builder() 763 871 .icon(() => new ItemStack(stringworms("media"))) 764 872 .displayName(Text.translatable("itemGroup.hexic.group")) ··· 773 881 .build() 774 882 775 883 val goodModulo = ne"daawdda" 884 + val getEntity: PartialFunction[Iota, Entity] = { case e: EntityIota => e.getEntity } 776 885 777 886 def memo[T, R](f: T => R, limit: Option[Int] = Some(128)): T => R = 778 887 val cache = limit.fold(ju.HashMap()): cap => 779 - new ju.LinkedHashMap[T, R]: 888 + new ju.LinkedHashMap[T, R](cap + 1, 1, true): 780 889 override def removeEldestEntry(eldest: ju.Map.Entry[T, R]): Boolean = size > cap 781 - cache.computeIfAbsent(_, f(_)) 890 + x => 891 + cache.synchronized: 892 + cache.computeIfAbsent(x, f(_)) 893 + 894 + trait MutableFunction[T, R] extends (T => R): 895 + def update(key: T, value: R): Unit 782 896 783 897 def init(): Unit = 784 898 given_Logger.info: ··· 820 934 Registries.ITEM(s"stringworm_$flavor") = item 821 935 Registries.ITEM("stringworm_pigmented") = dyedStringworm 822 936 Registries.ITEM("wizard") = wizard 937 + val cutItem = new Item(Item.Settings().maxCount(16)) with MediaHolderItem: 938 + Registries.ITEM("cut") = this 939 + override def getMedia(stack: ItemStack): Long = stack.getNbt.getLong("c") 940 + override def getMaxMedia(stack: ItemStack): Long = stack.getNbt.getLong("c") 941 + override def setMedia(stack: ItemStack, l: Media): Unit = () 942 + override def canProvideMedia(stack: ItemStack): Boolean = true 943 + override def canRecharge(stack: ItemStack): Boolean = true 944 + override def getConsumptionPriority(stack: ItemStack): Int = 1100 945 + 946 + Registries.ITEM("chisel") = new Item(Item.Settings().maxCount(1)): 947 + val Chisel = this 948 + object table extends BlockWithEntity(AbstractBlock.Settings.create().nonOpaque()): 949 + private val entityType = FabricBlockEntityTypeBuilder.create(createBlockEntity, table).build() 950 + Registries.BLOCK_ENTITY_TYPE("chisel_table") = entityType 951 + override def getRenderType(state: BlockState) = BlockRenderType.MODEL 952 + sealed trait entity extends BlockEntity: 953 + var bits: BitSet = BitSet() 954 + private[table] object bit: 955 + def apply(x: Int, y: Int): Boolean = bits(x * 16 + y) 956 + def update(x: Int, y: Int, value: Boolean): Unit = 957 + if value then 958 + bits += x * 16 + y 959 + else 960 + bits -= x * 16 + y 961 + markDirty() 962 + override def createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = 963 + new BlockEntity(entityType, pos, state) with entity: 964 + override def readNbt(nbt: NbtCompound): Unit = 965 + bits = BitSet.fromBitMask(nbt.getLongArray("b")) 966 + override def writeNbt(nbt: NbtCompound): Unit = 967 + nbt.putLongArray("b", bits.toBitMask) 968 + override def toUpdatePacket: Packet[ClientPlayPacketListener] = BlockEntityUpdateS2CPacket.create(this) 969 + override def toInitialChunkDataNbt: NbtCompound = createNbt() 970 + 971 + def findEntity(world: BlockView, pos: BlockPos): Option[entity] = 972 + world.getBlockEntity(pos) match 973 + case p: entity => Some(p) 974 + case p => 975 + given_Logger.error(s"Unexpected block entity at $pos for chisel table, got $p (${summon[ClassTag[p.type]]}).") 976 + None 977 + 978 + val emptyShape = VoxelShapes.union( 979 + VoxelShapes.cuboid(0.00, 0.00, 0.00, 0.25, 0.50, 0.25), 980 + VoxelShapes.cuboid(0.75, 0.00, 0.75, 1.00, 0.50, 1.00), 981 + VoxelShapes.cuboid(0.00, 0.50, 0.00, 1.00, 0.75, 1.00), 982 + VoxelShapes.cuboid(0.00, 0.75, 0.00, 0.0625, 0.8125, 1.00), 983 + VoxelShapes.cuboid(0.00, 0.75, 0.00, 1.00, 0.8125, 0.0625), 984 + VoxelShapes.cuboid(0.00, 0.75, 1.00, 0.9375, 0.8125, 1.00), 985 + VoxelShapes.cuboid(1.00, 0.75, 0.00, 1.00, 0.8125, 0.9375), 986 + ) 987 + val chunks = memo: (i: Int) => 988 + val x = i / 16 989 + val z = i % 16 990 + assume(x < 14 && z < 14) 991 + val dx = (x + 1) / 16.0 992 + val dz = (z + 1) / 16.0 993 + VoxelShapes.cuboid(dx, 0.75, dz, dx + 0.0625, 0.8125, dz + 0.0625) 994 + val shapes = memo { (bits: BitSet) => VoxelShapes.union(emptyShape, bits.toSeq.map(chunks)*) } 995 + 996 + override def getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape = 997 + val entity = findEntity(world, pos) 998 + shapes(entity.fold(BitSet.empty)(_.bits)) 999 + override def getCollisionShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape = 1000 + getOutlineShape(state, world, pos, context) 1001 + 1002 + override def onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult = boundary: 1003 + lazy val entity = findEntity(world, pos).getOrElse(boundary.break(ActionResult.FAIL)) 1004 + player.getStackInHand(hand) match 1005 + case stack@ItemStackAccess(HexItems.CHARGED_AMETHYST, _, _) if !(for i <- 0 until 14; j <- 0 until 14 yield entity.bit(i, j)).all => 1006 + for i <- 0 until 14; j <- 0 until 14 do 1007 + entity.bit(i, j) = true 1008 + stack.decrement(1) 1009 + ActionResult.SUCCESS 1010 + case stack@ItemStackAccess(_, c, _) if c == 0 && entity.bits.nonEmpty && player.isSneaking => 1011 + stack.setItem(cutItem) 1012 + stack.setCount(1) 1013 + stack.getOrCreateNbt().putLongArray("b", entity.bits.toBitMask) 1014 + entity.bits = BitSet.empty 1015 + entity.markDirty() 1016 + ActionResult.SUCCESS 1017 + case stack@ItemStackAccess(_: Chisel.type, _, _) => 1018 + val pos = hit.getPos.add(hit.getSide.getOffsetX * -1/32, hit.getSide.getOffsetY * -1/32, hit.getSide.getOffsetZ * -1/32) 1019 + val x = ((pos.x * 16 % 16 + 16) % 16 - 1).toInt 1020 + val y = ((pos.z * 16 % 16 + 16) % 16 - 1).toInt 1021 + if x >= 0 && y >= 0 && x < 14 && y < 14 then 1022 + if entity.bit(x, y) then 1023 + stack.damage(1, player, { _ => }) 1024 + entity.bit(x, y) = false 1025 + ActionResult.SUCCESS 1026 + else 1027 + ActionResult.PASS 1028 + else 1029 + ActionResult.PASS 1030 + case _ => ActionResult.PASS 1031 + Registries.BLOCK("chisel_table") = table 1032 + Registries.ITEM("chisel_table") = BlockItem(table, Item.Settings()) 823 1033 for (color, item) <- Pen.instances do Registries.ITEM(s"pen/${color.asString}") = item 824 1034 Registries.ITEM_GROUP("group") = itemGroup 825 1035 //Registries.ITEM("echo") = EchoItem ··· 1124 1334 GREATER_EQ -> ((a: MapIota, b: MapIota) => a.map containsAll b.map), 1125 1335 LESS_EQ -> ((a: MapIota, b: MapIota) => b.map containsAll a.map), 1126 1336 ) 1337 + def fox(tr: PlayerEntity ?=> PartialFunction[Option[FoxEntity.Type], Option[FoxEntity.Type]]): Action = 1338 + Patterns.mkAction: (img, cont) => 1339 + img.getStack.lastOption match 1340 + case None => throw MishapNotEnoughArgs(1, 0) 1341 + case Some(getEntity(given ServerPlayerEntity)) => 1342 + val c: PlayerInfoComponent = summon[PlayerEntity] 1343 + c.foxType match 1344 + case tr(newFoxType) => 1345 + OperationResult(img.withStack(_.init), Seq( 1346 + OperatorSideEffect.ConsumeMedia(MediaConstants.SHARD_UNIT + MediaConstants.DUST_UNIT), 1347 + OperatorSideEffect.AttemptSpell( 1348 + new RenderedSpell: 1349 + override def cast(env: CastingEnvironment): Unit = 1350 + c.foxType = newFoxType 1351 + summon[PlayerEntity].syncComponent(PlayerInfoComponent.key) 1352 + override def cast(env: CastingEnvironment, img: CastingImage): CastingImage = { cast(env); img } 1353 + , true, true) 1354 + ), cont, HexEvalSounds.SPELL) 1355 + case _ => 1356 + OperationResult(img.withStack(_.init), Seq(), cont, HexEvalSounds.SPELL) 1357 + case Some(x) => throw MishapInvalidIota(x, 0, "player") 1358 + Patterns.register("fox", se"wqwqeeeweedqqeqwaeeaw"): 1359 + fox: 1360 + case None => Some: 1361 + val p = summon[PlayerEntity] 1362 + FoxEntity.Type.fromBiome(p.getWorld.getBiome(p.getBlockPos)) 1363 + Patterns.register("unfox", se"wqwqwqwaeeaw"): 1364 + fox { case Some(_) => None } 1127 1365 hexXplat.getArithmeticRegistry("null_abs") = arith("null_abs", Arithmetic.ABS -> ((_: NullIota) => DoubleIota(0))) 1128 1366 val planeCaches = ju.WeakHashMap[MinecraftServer, mutable.Map[UUID, RuntimeWorldHandle]]() 1129 1367 def planeCache(using server: MinecraftServer): mutable.Map[UUID, RuntimeWorldHandle] = planeCaches.computeIfAbsent(server, _ => mutable.Map()) 1130 1368 def planes(uuid: UUID)(using server: MinecraftServer) = 1131 1369 planeCache.computeIfAbsent(uuid, _ => 1132 1370 val dimID: Identifier = s"fresh-${uuid.toString.replace("-", "")}" 1133 - val handle = Fantasy get server getOrOpenPersistentWorld(dimID, new RuntimeWorldConfig setGenerator new VoidChunkGenerator(server.getOverworld.getRegistryManager get RegistryKeys.BIOME)) 1371 + val handle = Fantasy get server getOrOpenPersistentWorld(dimID, new RuntimeWorldConfig setDimensionType RegistryKey.of(RegistryKeys.DIMENSION_TYPE, "cell") setGenerator new VoidChunkGenerator(server.getOverworld.getRegistryManager get RegistryKeys.BIOME)) 1134 1372 server.submit((() => handle.asWorld.getChunkManager.setChunkForced(ChunkPos.ORIGIN, true)): Runnable) 1135 1373 handle 1136 1374 ) 1375 + extension (w: ServerWorld) 1376 + def meta: String MutableFunction Option[String] = 1377 + val path = w.getServer.getSavePath(WorldSavePath.ROOT).resolve(s"dimensions/${w.getRegistryKey.getValue.getNamespace}/${w.getRegistryKey.getValue.getPath}") 1378 + new MutableFunction: 1379 + def apply(name: String) = Option(Files.getAttribute(path, "user:" + name)).asInstanceOf[Option[Array[Byte]]].map(buf => String(buf, StandardCharsets.UTF_8)) 1380 + def update(name: String, value: Option[String]): Unit = Files.setAttribute(path, "user:" + name, value.map(StandardCharsets.UTF_8.encode).orNull) 1381 + 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)))) 1382 + def parentInfo_=(parent: Option[(RegistryKey[World], BlockPos)]) = 1383 + parent.fold { 1384 + w.meta("parent") = None 1385 + w.meta("bound_x") = None 1386 + w.meta("bound_y") = None 1387 + w.meta("bound_z") = None 1388 + } { (world, pos) => 1389 + w.meta("parent") = Some(world.getValue.toString) 1390 + w.meta("bound_x") = Some(pos.getX.toString) 1391 + w.meta("bound_y") = Some(pos.getY.toString) 1392 + w.meta("bound_z") = Some(pos.getZ.toString) 1393 + } 1137 1394 extension (server: MinecraftServer) 1138 1395 def savedPlanes = 1139 1396 val file = server.getSavePath(WorldSavePath("fresh")) ··· 1144 1401 def savedPlanes_=(planes: Set[UUID]) = 1145 1402 val file = server.getSavePath(WorldSavePath("fresh")) 1146 1403 Files.write(file, planes.map(_.toString)) 1404 + hexXplat.getArithmeticRegistry("list_math") = arith("list_math", 1405 + Arithmetic.MUL -> ((a: ListIota, b: ListIota) => ListIota(for x <- a.getList.toSeq; y <- b.getList yield ListIota(Seq(x, y)))), 1406 + Arithmetic.DIV -> ((a: ListIota, b: ListIota) => ListIota(for (x, y) <- a.getList.toSeq zip b.getList yield ListIota(Seq(x, y)))), 1407 + ) 1408 + hexXplat.getSpecialHandlerRegistry("tuple") = ((pattern, env) => 1409 + val regex = "qq(w*)qq".r 1410 + pattern.anglesSignature match 1411 + case regex(middle) => 1412 + val size = middle.length + 1 1413 + new SpecialHandler: 1414 + override val act: Action = Patterns.mkConstAction(size)(ListIota(_).pipe(Seq(_))) 1415 + override val getName: Text = Text.translatable("hexcasting.special.hexic:tuple.n", toRoman(size)) 1416 + case _ => null 1417 + ): SpecialHandler.Factory[? <: SpecialHandler] 1147 1418 CommandRegistrationCallback.EVENT.register: (d, r, e) => 1148 1419 d.getRoot.addChild(LiteralArgumentBuilder.literal[ServerCommandSource]("gimmeiota") 1149 1420 .requires(c => c.hasPermissionLevel(2) || (c.getPlayer != null && c.getPlayer.isCreative)) ··· 1208 1479 h.delete() 1209 1480 1 1210 1481 case None => throw CommandException(Text.literal("Plane not mapped")) 1211 - d.getRoot.addChild(LiteralArgumentBuilder.literal[ServerCommandSource]("playerwisp").pipe: c => 1212 - c.requires(_.hasPermissionLevel(2)) 1213 - c.argument("target", EntityArgumentType.players()): c => 1214 - c.literal("make"): c => 1215 - c.executes: (ctx: CommandContext[ServerCommandSource]) => 1216 - val player = EntityArgumentType.getPlayer(ctx, "target") 1217 - player.getComponent(PlayerInfoComponent.key).wispMedia = Some(-1) 1218 - PlayerInfoComponent.key.sync(player) 1219 - 1 1220 - c.literal("unmake"): c => 1221 - c.executes: (ctx: CommandContext[ServerCommandSource]) => 1222 - val player = EntityArgumentType.getPlayer(ctx, "target") 1223 - player.getComponent(PlayerInfoComponent.key).wispMedia = None 1224 - PlayerInfoComponent.key.sync(player) 1225 - 1 1226 - c.literal("media"): c => 1227 - c.literal("add"): c => 1228 - c.executes: (ctx: CommandContext[ServerCommandSource]) => 1229 - ??? 1230 - c.literal("set"): c => 1231 - c.executes: (ctx: CommandContext[ServerCommandSource]) => 1232 - ??? 1233 - // only rasonable to query one player 1234 - c.argument("target", EntityArgumentType.players()): c => 1235 - c.literal("media"): c => 1236 - c.literal("query"): c => 1237 - c.executes: (ctx: CommandContext[ServerCommandSource]) => 1238 - ??? 1239 - c.build() 1240 - ) 1241 1482 d.getRoot.addChild(LiteralArgumentBuilder.literal[ServerCommandSource]("property").pipe: c => 1242 1483 c.requires(_.hasPermissionLevel(2)) 1243 1484 c.`then`(LiteralArgumentBuilder.literal("get") ··· 1320 1561 world.setBlockState(bp, state, 0) 1321 1562 world.getServer.savedPlanes += uuid 1322 1563 Seq(DimIota(world)) 1323 - Patterns.register("deleteworld", e"qaaqqwaeddeawqqaaqawwwawwwwwwwaqaaqqwaeddeawqqaaqawwwwwwwawwwaqaaqqwaeddeawqqaaq"): 1564 + Patterns.register("attachworld", e"qaaqqwaeddeawqqaaqawwwawwwwwwwqwwwawwwqwwwwwwwawwwaqaaqqwaeddeawqqaaq"): 1324 1565 new SpellAction: 1325 1566 override def getArgc: Int = 2 1326 1567 override def awardsCastingStat(env: CastingEnvironment): Boolean = true 1327 1568 override def execute(stack: util.List[? <: Iota], env: CastingEnvironment): SpellAction.Result = 1328 1569 stack.toSeq match 1329 1570 case Seq(plane: DimIota, dest: Vec3Iota) if plane.getDimString.startsWith("hexic:fresh-") => 1330 - val pos = dest.getVec3 1331 - env.assertPosInRangeForEditing(BlockPos.ofFloored(pos)) 1571 + val pos = BlockPos.ofFloored(dest.getVec3) 1572 + env.assertPosInRangeForEditing(pos) 1332 1573 val world = env.getWorld.getServer.getWorld(plane.getWorldKey) 1333 1574 given server: MinecraftServer = world.getServer 1575 + if world == env.getWorld then 1576 + throw new Mishap: 1577 + override def accentColor(env: CastingEnvironment, ctx: Context): FrozenPigment = dyeColor(DyeColor.PINK) 1578 + override def errorMessage(env: CastingEnvironment, ctx: Context): Text = Text.literal("Cannot bind a demiplane to itself") // TODO: make translatable 1579 + override def execute(env: CastingEnvironment, ctx: Context, stack: util.List[Iota]): Unit = 1580 + if env.getWorld.getBlockState(pos).isReplaceable then 1581 + env.getWorld.setBlockState(pos, Interop.VOID_AIR.getDefaultState) 1582 + SpellAction.Result( 1583 + new RenderedSpell: 1584 + override def cast(env: CastingEnvironment): Unit = 1585 + world.parentInfo = Some(env.getWorld.getRegistryKey, pos) 1586 + override def cast(env: CastingEnvironment, image: CastingImage): CastingImage = { cast(env); image }, 1587 + MediaConstants.SHARD_UNIT * 3, 1588 + Seq(), 1589 + 1 1590 + ) 1591 + override def executeWithUserdata(list: util.List[? <: Iota], env: CastingEnvironment, data: NbtCompound): SpellAction.Result = SpellAction.DefaultImpls.executeWithUserdata(this, list, env, data) 1592 + override def hasCastingSound(env: CastingEnvironment): Boolean = true 1593 + override def operate(env: CastingEnvironment, castingImage: CastingImage, cont: SpellContinuation): OperationResult = SpellAction.DefaultImpls.operate(this, env, castingImage, cont) 1594 + Patterns.register("deleteworld", e"qaaqqwaeddeawqqaaqawwwawwwwwwwaqaaqqwaeddeawqqaaqawwwwwwwawwwaqaaqqwaeddeawqqaaq"): 1595 + new SpellAction: 1596 + override def getArgc: Int = 1 1597 + override def awardsCastingStat(env: CastingEnvironment): Boolean = true 1598 + override def execute(stack: util.List[? <: Iota], env: CastingEnvironment): SpellAction.Result = 1599 + stack.toSeq match 1600 + case Seq(plane: DimIota) if plane.getDimString.startsWith("hexic:fresh-") => 1601 + given server: MinecraftServer = env.getWorld.getServer 1602 + val planeWorld = server.getWorld(plane.getWorldKey) 1603 + val (outer, pos) = (for 1604 + (key, pos) <- planeWorld.parentInfo 1605 + world = server.getWorld(key) 1606 + if world != null 1607 + yield (world, pos.toCenterPos)).getOrElse: 1608 + throw new Mishap: 1609 + override def accentColor(env: CastingEnvironment, context: Context): FrozenPigment = dyeColor(DyeColor.PINK) 1610 + override def errorMessage(env: CastingEnvironment, context: Context): Text = "Cannot shatter a Demiplane that is not tethered" // TODO: translate 1611 + override def execute(env: CastingEnvironment, context: Context, list: util.List[Iota]): Unit = 1612 + val r = planeWorld.random 1613 + val pos = BlockPos.Mutable(r.nextBetween(1, 9), r.nextBetween(1, 9), r.nextBetween(1, 9)) 1614 + Seq(pos.setX, pos.setY, pos.setZ)(r.nextInt(2))(if r.nextBoolean() then 10 else 0) 1615 + planeWorld.breakBlock(pos, true) 1334 1616 val id = getPocketID(plane.getWorldKey.getValue).get 1617 + if isDev then println(s"Destroying pocket $id $planeWorld into $outer@$pos") 1335 1618 SpellAction.Result( 1336 1619 new RenderedSpell: 1337 1620 override def cast(env: CastingEnvironment): Unit = 1338 - given outer: ServerWorld = env.getWorld 1339 1621 val loc = BlockPos.Mutable() 1340 1622 for x <- 1 to 9; y <- 1 to 9; z <- 1 to 9 do 1341 1623 loc.set(x, y, z) 1342 - world.breakBlock(loc, true) 1624 + planeWorld.breakBlock(loc, true) 1343 1625 val itemsToSpawn = mutable.Map[ItemVariant, Long]().withDefaultValue(0) 1344 1626 var xpToSpawn: Long = 0 1345 - val chunk = world.getChunkManager.getChunk(0, 0, ChunkStatus.FULL, false) 1627 + val chunk = planeWorld.getChunkManager.getChunk(0, 0, ChunkStatus.FULL, false) 1346 1628 chunk.asInstanceOf[WorldChunk].loadEntities() 1347 - if isDev then world.dump(Files.createDirectories(Paths.get("deleted_world"))) 1629 + if isDev then planeWorld.dump(Files.createDirectories(Paths.get("deleted_world"))) 1348 1630 boundary: 1349 1631 if isDev then println("Beginning lurker cleanup") 1350 1632 try 1351 1633 var pass = 0 1352 1634 // test dim: hexic:fresh-9116c992558d4aca854d75270e100b84, uuid 9116c992-558d-4aca-854d-75270e100b84 1353 - iterated(world.iterateEntities): (entities, recurse) => 1635 + iterated(planeWorld.iterateEntities): (entities, recurse) => 1354 1636 val entitySeq = entities.toSeq 1355 1637 if isDev then println(s"Performing pass $pass over ${entitySeq.size} entities") 1356 1638 if entitySeq.nonEmpty then ··· 1375 1657 if isDev then println(s"Killing living entity $e") 1376 1658 var n = 0 1377 1659 while !e.isRemoved do 1378 - if isDev then println(s" Waiting ${n += 1} ticks to die") 1660 + n += 1 1661 + if isDev then println(s"Waiting $n ticks to die") 1379 1662 (e: LivingEntityAccess).callUpdatePostDeath() 1380 1663 case e => 1381 1664 if isDev then println(s"Killing nonliving entity $e") ··· 1387 1670 if isDev then println(s"Ledger: $itemsToSpawn, $xpToSpawn XP") 1388 1671 for (item, count) <- itemsToSpawn do 1389 1672 if isDev then println(s"Spawning $item ($count)") 1390 - spawnManyItems(dest.getVec3, item, count) 1673 + spawnManyItems(pos, item, count)(using outer) 1391 1674 while xpToSpawn > 2477 do 1392 1675 if isDev then println(s"Spawning max XP orb, $xpToSpawn left") 1393 1676 ExperienceOrbEntity.spawn(outer, pos, 2477) ··· 1408 1691 Seq(), 1409 1692 1 1410 1693 ) 1411 - case Seq(plane: DimIota, x) if plane.getDimString.startsWith("hexic:fresh-") => 1412 - throw MishapInvalidIota(x, 1, "vec3") 1413 - case Seq(x, _) => 1414 - throw MishapInvalidIota(x, 2, "hexic:world") 1694 + case Seq(x) => 1695 + throw MishapInvalidIota(x, 0, "hexic:world") 1415 1696 override def executeWithUserdata(list: util.List[? <: Iota], env: CastingEnvironment, data: NbtCompound): SpellAction.Result = SpellAction.DefaultImpls.executeWithUserdata(this, list, env, data) 1416 1697 override def hasCastingSound(env: CastingEnvironment): Boolean = true 1417 1698 override def operate(env: CastingEnvironment, castingImage: CastingImage, cont: SpellContinuation): OperationResult = SpellAction.DefaultImpls.operate(this, env, castingImage, cont) 1699 + Patterns.register("snow", nw"eewwweewdadadaddwwwaqwwq"): 1700 + Patterns.mkAction: (img, cont) => 1701 + (img, cont, HexEvalSounds.SPELL, Seq( 1702 + OperatorSideEffect.ConsumeMedia(12 * MediaConstants.DUST_UNIT), 1703 + OperatorSideEffect.AttemptSpell( 1704 + new RenderedSpell: 1705 + override def cast(env: CastingEnvironment): Unit = 1706 + given MinecraftServer = env.getWorld.getServer 1707 + val duration = env.getWorld.random.nextBetween(15, 30) * 20 * 60 1708 + env.getWorld.setWeather(0, duration, true, false) 1709 + ServerInfoComponent.get.endSnowTick = env.getWorld.getTime + duration 1710 + ServerInfoComponent.sync() 1711 + override def cast(env: CastingEnvironment, img: CastingImage): CastingImage = { cast(env); img } 1712 + , true, true) 1713 + )) 1418 1714 Patterns.register("staffcast_factory", ne"wwwwwaqqqqqeaqeaeaeaeaeq"): 1419 1715 Patterns.mkAction: (img, cont) => 1420 1716 summon[CastingEnvironment].getCastingEntity match ··· 1424 1720 staffcast.setImage(img) 1425 1721 val vm = staffcast.getVM(summon[CastingEnvironment].getCastingHand) 1426 1722 try 1427 - vm.queueExecuteAndWrapIota(PatternIota(se"deaqq"), summon) 1723 + propagateMishaps(vm.getEnv): 1724 + vm.queueExecuteAndWrapIota(PatternIota(se"deaqq"), summon) 1428 1725 finally 1429 1726 staffcast.setImage(oldImage) 1430 1727 HexCardinalComponents.STAFFCAST_IMAGE.sync(caster) ··· 1448 1745 null // kotlin bullshit 1449 1746 )) 1450 1747 try 1451 - vm.queueExecuteAndWrapIota(PatternIota((HexDir.SOUTH_EAST, "deaqq")), summon) 1748 + propagateMishaps(vm.getEnv): 1749 + vm.queueExecuteAndWrapIota(PatternIota((HexDir.SOUTH_EAST, "deaqq")), summon) 1452 1750 finally 1453 1751 staffcast.setImage(new CastingImage( 1454 1752 stack = vm.getImage.getStack, ··· 1459 1757 userData = oldImage.getUserData, 1460 1758 null // kotlin bullshit 1461 1759 )) 1462 - HexCardinalComponents.STAFFCAST_IMAGE.sync(caster); 1760 + HexCardinalComponents.STAFFCAST_IMAGE.sync(caster) 1761 + // do not remove this comment 1463 1762 (new CastingImage( 1464 1763 stack = img.getStack.asScala.init.asJava, 1465 1764 parenCount = img.getParenCount, ··· 1482 1781 Seq: 1483 1782 val ty = MetatableIotaType.colors((r * 3, g * 3, b * 3)) 1484 1783 ty.Instance(userdata, display.display, metatable.getName, metatable.getReadonly) 1784 + Patterns.register("get_other_caster", nw"ede"): 1785 + Patterns.mkLiteral: 1786 + val players: Set[LivingEntity] = summon[CastingEnvironment].getWorld.getPlayers.toSet 1787 + var others = players - summon[CastingEnvironment].getCastingEntity 1788 + for case given ClassTag[EntityPlayerMPFake] <- classNamed("carpet.patches.EntityPlayerMPFake") do 1789 + others = others.filter: 1790 + case _: EntityPlayerMPFake => false 1791 + case _ => true 1792 + val sorted = others.toSeq.sortBy(_.getPos.squaredDistanceTo(summon[CastingEnvironment].mishapSprayPos)) 1793 + sorted.headOption.fold(NullIota())(EntityIota(_)) 1794 + Patterns.register("erase", e"wqwdwqwawwwwwawwwww"): 1795 + Patterns.mkAction: (img, cont) => 1796 + def mkResult(scale: Int, pos: => Vec3d, spell: => Unit) = 1797 + OperationResult( 1798 + img.withStack(_.init), 1799 + Seq( 1800 + OperatorSideEffect.ConsumeMedia(MediaConstants.DUST_UNIT * scale), 1801 + OperatorSideEffect.AttemptSpell( 1802 + new RenderedSpell: 1803 + override def cast(env: CastingEnvironment): Unit = 1804 + spell(using env) 1805 + override def cast(env: CastingEnvironment, img: CastingImage): CastingImage = { cast(env); img } 1806 + , true, true 1807 + ), 1808 + OperatorSideEffect.Particles( 1809 + ParticleSpray(pos, Vec3d(1, 0, 0), 0.25, 3.14, 40) 1810 + ), 1811 + ), 1812 + cont, HexEvalSounds.SPELL 1813 + ) 1814 + img.getStack.lastOption.getOrElse(throw MishapNotEnoughArgs(1, 0)) match 1815 + case s: Vec3Iota if fabric.isModLoaded("ioticblocks") => 1816 + val pos = BlockPos.ofFloored(s.getVec3) 1817 + summon[CastingEnvironment].assertPosInRangeForEditing(pos) 1818 + val holder = IoticBlocksAPI.INSTANCE.findIotaHolder(summon[CastingEnvironment].getWorld, pos) 1819 + if holder == null || !holder.writeIota(null, true) then throw MishapBadBlock.of(pos, "hexic:erase") 1820 + mkResult(1, pos.toCenterPos, holder.writeIota(null, false)) 1821 + case s: EntityIota => 1822 + summon[CastingEnvironment].assertEntityInRange(s.getEntity) 1823 + def result(scale: Int, spell: CastingEnvironment ?=> Unit) = mkResult(scale, s.getEntity match { case e: ItemEntity => e.getPos.add(0, .375, .0); case e => e.getPos }, spell) 1824 + boundary: outer ?=> 1825 + val maybeItem = s.getEntity match 1826 + case i: ItemEntity => Some(i.getStack) 1827 + case f: ItemFrameEntity => Some(f.getHeldItemStack) 1828 + case _ => None 1829 + boundary: 1830 + val item = maybeItem.getOrElse(boundary.break()) 1831 + val holder = hexXplat.findHexHolder(item) 1832 + if holder == null || !holder.hasHex then boundary.break() 1833 + boundary.break(result(item.getCount, holder.clearHex()))(using outer) 1834 + val holder = hexXplat.findDataHolder(s.getEntity) 1835 + if holder == null || !holder.writeIota(null, true) then throw MishapBadEntity.of(s.getEntity, "hexic:erase") 1836 + result(maybeItem.fold(1)(_.getCount), holder.writeIota(null, false)) 1837 + case i => throw MishapInvalidIota.ofType(i, 0, "hexic:erase") 1485 1838 Patterns.register("rotate", nw"qaeaqweeee"): 1486 1839 Patterns.mkConstAction(2): 1487 1840 case Seq(ary: ListIota, nr: DoubleIota) => ··· 1491 1844 if (d - n).abs > DoubleIota.TOLERANCE then 1492 1845 throw MishapInvalidIota.ofType(nr, 0, "hexic:int_or_list") 1493 1846 val size = list.size 1494 - val delta = (n % size + size) % size 1495 - Seq(ListIota((list.drop(delta) ++ list.take(delta)).toSeq.asJava)) 1847 + if size == 0 then 1848 + Seq(ListIota(Seq())) 1849 + else 1850 + val delta = (n % size + size) % size 1851 + Seq(ListIota((list.drop(delta) ++ list.take(delta)).toSeq.asJava)) 1496 1852 case Seq(ary: ListIota, nr) => throw MishapInvalidIota.ofType(nr, 0, "hexic:int_or_list") 1497 1853 case Seq(ary, _) => throw MishapInvalidIota.ofType(ary, 1, "list") 1498 1854 Patterns.register("take", nw"qaeaqwd"): ··· 1525 1881 Seq(ListIota(list.indices.filter(!excl.contains(_)).map(list(_)).toSeq.asJava)) 1526 1882 case Seq(ary: ListIota, nr) => throw MishapInvalidIota.ofType(nr, 0, "int") 1527 1883 case Seq(ary, _) => throw MishapInvalidIota.ofType(ary, 1, "list") 1528 - Patterns.register("extract", ne"dewaqawed"): 1884 + lazy val filterFrameType: ContinuationFrame.Type[FilterFrame] = (c: NbtCompound, world: ServerWorld) => 1885 + FilterFrame( 1886 + stack = c.getList("p", NbtElement.COMPOUND_TYPE).asScala.collect { case c: NbtCompound => IotaType.deserialize(c, world) }.toSeq, 1887 + filter = c.getList("d", NbtElement.COMPOUND_TYPE).asScala.collect { case c: NbtCompound => IotaType.deserialize(c, world) }.toSeq, 1888 + focus = IotaType.deserialize(c.getCompound("f"), world), 1889 + received = c.getList("k", NbtElement.COMPOUND_TYPE).asScala.collect { case c: NbtCompound => IotaType.deserialize(c, world) }.toSeq, 1890 + remaining = c.getList("r", NbtElement.COMPOUND_TYPE).asScala.collect { case c: NbtCompound => IotaType.deserialize(c, world) }.toSeq, 1891 + ) 1892 + class FilterFrame(stack: Seq[Iota], filter: Seq[Iota], focus: Iota, received: Seq[Iota], remaining: Seq[Iota]) extends ContinuationFrame: 1893 + override def getType: ContinuationFrame.Type[FilterFrame] = filterFrameType 1894 + override def evaluate(cont: SpellContinuation, world: ServerWorld, vm: CastingVM): CastResult = 1895 + val newReceived = vm.getImage.getStack.toSeq match 1896 + case Seq() => throw MishapNotEnoughArgs(1, 0) 1897 + case _ :+ x => if x.isTruthy then received :+ focus else received 1898 + val (newStack, newCont) = remaining match 1899 + case next +: rest => (stack :+ next, cont.pushFrame(FilterFrame(stack, filter, next, newReceived, rest)).pushFrame(FrameEvaluate(SpellList.LList(0, filter), true))) 1900 + case Seq() => (stack :+ ListIota(newReceived), cont) 1901 + CastResult(ListIota(filter), newCont, vm.getImage.withStack(_ => newStack), Seq(), ResolvedPatternType.EVALUATED, HexEvalSounds.THOTH) 1902 + override def serializeToNBT(): NbtCompound = NbtCompound() 1903 + .tap(_.put("p", seqToNBT(stack.map(IotaType.serialize)))) 1904 + .tap(_.put("k", seqToNBT(received.map(IotaType.serialize)))) 1905 + .tap(_.put("r", seqToNBT(remaining.map(IotaType.serialize)))) 1906 + .tap(_.put("f", IotaType.serialize(focus))) 1907 + .tap(_.put("d", seqToNBT(filter.map(IotaType.serialize)))) 1908 + override def breakDownwards(stack: ju.List[? <: Iota]): Pair[java.lang.Boolean, ju.List[Iota]] = Pair(true, this.stack :+ ListIota(received)) 1909 + override def size = 0 1910 + Patterns.register("grep", nw"qaeaqea"): 1911 + Patterns.mkAction: (img, cont) => 1912 + img.getStack.toSeq match 1913 + case Seq() => throw MishapNotEnoughArgs(2, 0) 1914 + case Seq(_) => throw MishapNotEnoughArgs(2, 1) 1915 + case saved:+(target: ListIota):+(filter: ListIota) => 1916 + if filter.isEmpty then (img.withStack(_ :+ ListIota(target.getList.filter(_.isTruthy).toSeq)), cont, HexEvalSounds.THOTH, Seq()) // short-circuit on empty filter 1917 + else target.getList.toSeq match 1918 + case first +: rest => 1919 + // set up filter, ideally FilterFrame would do this 1920 + (img.withStack(_ :+ first), cont.pushFrame(FilterFrame(saved, filter.getList.toSeq, first, Seq(), rest)).pushFrame(FrameEvaluate(filter.getList, true)), HexEvalSounds.THOTH, Seq()) 1921 + case _ => 1922 + // we can't start a filter with no iota, but it'd always be empty anyway 1923 + (img.withStack(_ :+ ListIota(Seq())), cont, HexEvalSounds.THOTH, Seq()) 1924 + case saved:+(_: ListIota):+filter => throw MishapInvalidIota.ofType(filter, 1, "list") 1925 + case saved:+target:+_ => throw MishapInvalidIota.ofType(target, 1, "list") 1926 + Patterns.register("extract", nw"dewaqawed"): 1529 1927 Patterns.mkConstAction(2): 1530 1928 case Seq(ary: ListIota, nr: DoubleIota) => 1531 1929 val ls = ary.getList.toSeq ··· 1539 1937 case null => throw MishapBadCaster() 1540 1938 case p: ServerPlayerEntity => p.getComponent(PlayerInfoComponent.key).murmur.fold(NullIota())(StringIota.make) 1541 1939 case _ => throw MishapBadCaster() 1940 + Patterns.register("make_cme", ne"dqqd"): 1941 + Patterns.mkAction: (img, cont) => 1942 + img.getStack.toSeq.reverse match 1943 + case Seq(args: ListIota, fn: ListIota, stack*) => 1944 + given ExecutionContext = ExecutionContext.global 1945 + val p = Promise[Seq[CastingImage]]() 1946 + if isDev then println(s"parathoth args=$args fn=${fn.getList} stack=$stack") 1947 + val imgs = args.getList.map: x => 1948 + Future: 1949 + try 1950 + val subImg = new CastingImage( 1951 + stack = stack :+ x, 1952 + parenCount = 0, 1953 + parenthesized = Seq.empty, 1954 + escapeNext = false, 1955 + opsConsumed = img.getOpsConsumed, 1956 + userData = img.getUserData, 1957 + null // kotlin bullshit 1958 + ) 1959 + println(s"- thread $x started") 1960 + val env = summon[CastingEnvironment] 1961 + val vm = CastingVM(subImg, new CastingEnvironment(env.getWorld): 1962 + export env._ 1963 + override def hasEditPermissionsAtEnvironment(pos: BlockPos): Boolean = false 1964 + override def extractMediaEnvironment(cost: Media, simulate: Boolean): Media = 0 1965 + override def isVecInRangeEnvironment(vec: Vec3d): Boolean = false 1966 + override def getUsableStacks(mode: CastingEnvironment.StackDiscoveryMode): util.List[ItemStack] = Seq() 1967 + override def getPrimaryStacks: util.List[CastingEnvironment.HeldItemInfo] = Seq() 1968 + ) 1969 + vm.queueExecuteAndWrapIotas(fn.getList.toSeq, env.getWorld) 1970 + println(s"- thread $x ended") 1971 + vm.getImage 1972 + catch case e => 1973 + p.tryFailure(e) 1974 + throw e 1975 + Future.sequence(imgs).onComplete(p tryComplete _.map(_.toSeq)) 1976 + val results = Await.result(p.future, Duration.Inf) 1977 + OperationResult( 1978 + newImage = CastingImage( 1979 + stack = stack :+ ListIota(results.flatMap(_.getStack)), 1980 + parenCount = img.getParenCount, 1981 + parenthesized = img.getParenthesized, 1982 + escapeNext = img.getEscapeNext, 1983 + opsConsumed = results.map(_.getOpsConsumed).max, 1984 + userData = img.getUserData, 1985 + null // kotlin bullshit 1986 + ), 1987 + sideEffects = Seq(), 1988 + newContinuation = cont, 1989 + sound = HexEvalSounds.THOTH, 1990 + ) 1991 + case Seq(i, _: ListIota, _*) => throw MishapInvalidIota.ofType(i, 0, "list") 1992 + case Seq(_, i, _*) => throw MishapInvalidIota.ofType(i, 1, "list") 1993 + case s => throw MishapNotEnoughArgs(2, s.size) 1542 1994 Patterns.register("reveal", ne"deqed"): 1543 1995 Patterns.mkConstAction(1, 0): 1544 1996 case Seq(iota: Iota) => ··· 1551 2003 p.syncComponent(PlayerInfoComponent.key) 1552 2004 Seq() 1553 2005 case _ => throw MishapBadCaster() 1554 - SlotAccess.playerInventory.register: (player, slot, stack) => 1555 - player.getComponent(PlayerInfoComponent.key).wispMedia match 1556 - case Some(_) => SlotAccess.LOCK_AND_DROP 1557 - case None => SlotAccess.ALLOW 1558 2006 ServerPlayNetworking.registerGlobalReceiver("murmur", (_, player, _, buf, _) => 1559 2007 val in = Option.when(buf.readBoolean())(buf.readString()) 1560 2008 if isDev then println(s"${player.getName.getString} murmurs: $in") 1561 2009 player.getComponent(PlayerInfoComponent.key).murmur = in) 2010 + lazy val messageFrameType: ContinuationFrame.Type[MessageFrame] = (c: NbtCompound, world: ServerWorld) => 2011 + val id = Uuids.toUuid(c.getIntArray("id")) 2012 + MessageFrame(id, Text.Serializer.fromJson(NbtOps.INSTANCE.convertTo(JsonOps.INSTANCE, c.getCompound("t"))), world.getServer.getPlayerManager.getPlayer(id)) 2013 + class MessageFrame(id: UUID, text: Text, player: => ServerPlayerEntity) extends ContinuationFrame: 2014 + override def getType: ContinuationFrame.Type[MessageFrame] = messageFrameType 2015 + override def evaluate(rest: SpellContinuation, world: ServerWorld, vm: CastingVM): CastResult = 2016 + boundary: 2017 + def mishap(m: Mishap) = boundary.break(CastResult(NullIota(), rest, vm.getImage, Seq(DoMishap(m, Mishap.Context(null, text))), ResolvedPatternType.EVALUATED, HexEvalSounds.NORMAL_EXECUTE)) 2018 + vm.getImage.getStack.toSeq.reverse match 2019 + case Seq() => 2020 + mishap(MishapNotEnoughArgs(1, 0)) 2021 + case Seq(s: StringIota, stack*) => 2022 + CastResult(NullIota(), rest, vm.getImage.withStack(_ => stack), Seq( 2023 + OperatorSideEffect.AttemptSpell( 2024 + new RenderedSpell: 2025 + override def cast(env: CastingEnvironment): Unit = 2026 + ServerPlayNetworking.send(player, "msg", PacketByteBufs.create.tap(_.writeString(s.getString))) 2027 + override def cast(env: CastingEnvironment, img: CastingImage): CastingImage = { cast(env); img } 2028 + , false, false 2029 + ) 2030 + ), ResolvedPatternType.EVALUATED, HexEvalSounds.NORMAL_EXECUTE) 2031 + case Seq(i, _*) => 2032 + mishap(MishapInvalidIota.ofType(i, 0, "string")) 2033 + override def serializeToNBT(): NbtCompound = NbtCompound() 2034 + .tap(_.putIntArray("id", Uuids.toIntArray(id))) 2035 + .tap(_.put("t", JsonOps.INSTANCE.convertTo(NbtOps.INSTANCE, Text.Serializer.toJsonTree(text)))) 2036 + override def breakDownwards(stack: ju.List[? <: Iota]): Pair[java.lang.Boolean, ju.List[Iota]] = Pair(false, stack.toSeq) 2037 + override def size = 0 2038 + hexXplat.getContinuationTypeRegistry("send_message") = messageFrameType 2039 + CastingEnvironment.addCreateEventListener: (env: CastingEnvironment, data: NbtCompound) => 2040 + val id = env.getWorld.getRegistryKey.getValue 2041 + if isDev then println(s"Environment created in $id") 2042 + for pocketID <- getPocketID(id) do 2043 + if isDev then println(s"Preparing pocket $pocketID for environment $env") 2044 + env.addExtension: 2045 + new CastingEnvironmentComponent with CastingEnvironmentComponent.IsVecInRange with CastingEnvironmentComponent.HasEditPermissionsAt: 2046 + object getKey extends CastingEnvironmentComponent.Key[this.type] 2047 + override def onIsVecInRange(vec: Vec3d, current: Boolean): Boolean = boundary: 2048 + for axis <- Direction.Axis.values do 2049 + val x = vec.getComponentAlongAxis(axis) 2050 + if x < 0 || x >= 11 then boundary.break(false) 2051 + true 2052 + override def onHasEditPermissionsAt(pos: BlockPos, current: Boolean): Boolean = boundary: 2053 + for axis <- Direction.Axis.values do 2054 + val x = pos.getComponentAlongAxis(axis) 2055 + if x < 0 || x >= 11 then boundary.break(false) 2056 + true 1562 2057 ServerPlayNetworking.registerGlobalReceiver("sync_mediaweave", (_, player, _, buf, _) => 1563 2058 val flags = buf.readByte() 1564 2059 val c = player.getComponent(PlayerInfoComponent.key) ··· 1601 2096 if player.isCreative then 0L else extractMediaFromInventory(cost, canOvercast, simulate) 1602 2097 override def getCastingHand: Hand = castingHand 1603 2098 override def getPigment = FrozenPigment(ItemStack(HexItems.DYE_PIGMENTS.get(color)), Util.NIL_UUID) 1604 - val stack = 2099 + val context = 1605 2100 if (flags & 1) != 0 then 1606 2101 Seq.fill(buf.readInt)(buf.readUnlimitedNbt: Iota) 1607 2102 else 1608 2103 Seq() 1609 - val image = CastingImage(stack :+ StringIota.make(text), 0, Seq(), false, 0, NbtCompound(), null) 2104 + val image = CastingImage(context :+ StringIota.make(text), 0, Seq(), false, 0, NbtCompound(), null) 1610 2105 val instrs = s.getList.asScala.toSeq 1611 2106 val vm = CastingVM(image, env) 1612 - val view = vm.queueExecuteAndWrapIotas(instrs.asJava, player.getServerWorld) 1613 - if view.getResolutionType == ResolvedPatternType.EVALUATED then 1614 - vm.getImage.getStack.lastOption match 1615 - case Some(s: StringIota) => 1616 - if s.getString != "" then 1617 - ServerPlayNetworking.send(player, "msg", PacketByteBufs.create.tap(_.writeString(s.getString))) 1618 - case Some(_: NullIota) | None => 1619 - case Some(x) => 1620 - ServerPlayNetworking.send(player, "msg", PacketByteBufs.create.tap(_.writeString(x.display.getString))) 1621 - vm.performSideEffects(Seq(DoMishap(MishapInvalidIota(x, 0, "string"), Mishap.Context(null, null)))) 2107 + val view = vm.queueExecuteAndWrapIotas(instrs.asJava, player.getServerWorld, SpellContinuation.NotDone(MessageFrame(player.getUuid, stack.getName, player), SpellContinuation.Done.INSTANCE)) 1622 2108 val packet = MsgNewSpiralPatternsS2C(player.getUuid, instrs.collect { case p: PatternIota => p.getPattern }.asJava, 140) 1623 2109 hexXplat.sendPacketToPlayer(player, packet) 1624 2110 hexXplat.sendPacketTracking(player, packet) ··· 1646 2132 i 1647 2133 case _ => er 1648 2134 2135 + extension [T, R] (f: T => R) def ∘ [U](g: U => T) = (x: U) => f(g(x)) 2136 + def wrapReturn[T](body: (T => Nothing) => T): T = body(return _) 2137 + def wrapThrow[T, E <: Throwable](body: (E => Nothing) => T): T = wrapReturn[Try[T]](r => Success(body(r∘Failure))).get 2138 + 2139 + def propagateMishaps[T](env: CastingEnvironment)(body: => T): T = 2140 + wrapThrow[T, Mishap]: doThrow => 2141 + object key extends CastingEnvironmentComponent.Key[?] 2142 + env.addExtension: 2143 + new CastingEnvironmentComponent with CastingEnvironmentComponent.PostExecution: 2144 + override def getKey: CastingEnvironmentComponent.Key[?] = key 2145 + override def onPostExecution(result: CastResult): Unit = 2146 + result.getSideEffects.collectFirst: 2147 + case m: OperatorSideEffect.DoMishap => 2148 + if isDev then println(s"Propagating mishap: $m") 2149 + doThrow(m.getMishap) 2150 + try body finally env.removeExtension(key) 2151 + 1649 2152 def clamp[@specialized T: Ordering](x: T)(min: T, max: T): T = 1650 2153 assume(max > min) 1651 2154 if x < min then min ··· 1665 2168 inline given [T <: Singleton] => Const[T] = Const[T](compiletime.constValue[T]) 1666 2169 given [T] => Conversion[Const[T], T] = _.value 1667 2170 1668 - private[hexic] class ComponentInit extends EntityComponentInitializer: 2171 + private[hexic] class ComponentInit extends EntityComponentInitializer, LevelComponentInitializer: 1669 2172 override def registerEntityComponentFactories(using EntityComponentFactoryRegistry): Unit = 1670 2173 PlayerInfoComponent.register 2174 + override def registerLevelComponentFactories(using LevelComponentFactoryRegistry): Unit = 2175 + ServerInfoComponent.register 1671 2176 1672 2177 opaque type Attrition = Unit 1673 2178 object Attrition extends Registrar[Attrition]("attrition") 1674 2179 2180 + @tailrec 2181 + def finishOperation(p: OperationResult)(using env: CastingEnvironment): OperationResult = 2182 + p.getNewContinuation match 2183 + case c: SpellContinuation.Done => p 2184 + case c: SpellContinuation.NotDone => 2185 + finishCast(c.getFrame.evaluate(c.getNext, env.getWorld, CastingVM(p.getNewImage, env)), p.getNewImage) 2186 + 2187 + inline def finishCast(p: CastResult, oldImage: CastingImage)(using env: CastingEnvironment): OperationResult = 2188 + finishOperation(OperationResult(p.getNewData??oldImage, p.getSideEffects, p.getContinuation, p.getSound)) 2189 + 1675 2190 type subtypes[T, R <: T] = T 1676 2191 //case class StaffcastFrame(owner: ServerPlayerEntity, oldImage: CastingImage) extends ContinuationFrame: 1677 2192 // override def getType: ContinuationFrame.Type[StaffcastFrame] = StaffcastFrame ··· 1866 2381 val value: F[R] = v 1867 2382 def unapply[F[_ <: R], R](v: Tagged[F, R]): (F[v.T], ClassTag[v.T]) = (v.value, summon) 1868 2383 2384 + def seqToNBT(data: Seq[NbtElement]) = 2385 + val l = NbtList() 2386 + data.forEach(l.add(_)) 2387 + l 2388 + 1869 2389 extension [T](l: util.AbstractList[T]) 1870 2390 def apply(n: Int): T = l.get(n) 1871 2391 def update(n: Int, x: T): Unit = l.set(n, x) ··· 2078 2598 else 2079 2599 (value, None) 2080 2600 2081 - def x10[T: Numeric](power: T) = "x10" + (power.toString: String).map((c: Char) => "⁰¹²³⁴⁵⁶⁷⁸⁹".charAt(c - '0')) 2082 - 2083 2601 //noinspection UnstableApiUsage 2084 2602 trait MediaContainer: 2085 2603 def -=(using Transaction)(amount: Long): Boolean ··· 2153 2671 def display: Text 2154 2672 def color: Int = 0x720a0a 2155 2673 private[hexic] def parseVariant(c: NbtCompound): Option[(TaggedVariant, RegistryKey[Reader])] = 2156 - Identifier.tryParse(c.getString("type")) match 2157 - case null => None 2158 - case i => Option.fromNullable(registry.get(i)).flatMap(_(c)).map((_, RegistryKey.of(VariantIota, i))) 2674 + for 2675 + i <- Option(Identifier.tryParse(c.getString("type"))) 2676 + entry <- Option.fromNullable(registry.get(i)) 2677 + parsed <- entry(c) 2678 + yield (parsed, RegistryKey.of(VariantIota, i)) 2159 2679 end parseVariant 2160 2680 def deserialize(using NbtElement, ServerWorld): VariantIota[?] | Null = 2161 2681 summon[NbtElement] match ··· 2167 2687 case None => null 2168 2688 case _ => null 2169 2689 end deserialize 2170 - override def display(e: NbtElement): Text = 2171 - val c = HexUtils.downcast(e, NbtCompound.TYPE) 2172 - parseVariant(c).fold(NullIota.DISPLAY)(_._1.display) 2690 + override def display(e: NbtElement): Text = parseVariant(e.downcast).fold(NullIota.DISPLAY)(_._1.display) 2173 2691 end display 2174 2692 registry(Identifier("item")) = c => 2175 2693 val s = ItemVariant.fromNbt(c)
+19
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*", 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 + }
+23
src/main/scala/org/eu/net/pool/hexic/mixin/CastingVMMixin.java
··· 1 + package org.eu.net.pool.hexic.mixin; 2 + 3 + import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType; 4 + import at.petrak.hexcasting.api.casting.eval.vm.CastingImage; 5 + import at.petrak.hexcasting.api.casting.eval.vm.CastingVM; 6 + import at.petrak.hexcasting.api.casting.iota.Iota; 7 + import kotlin.Pair; 8 + import org.eu.net.pool.hexic.Extern; 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.CallbackInfoReturnable; 13 + import scala.Tuple2; 14 + 15 + @Mixin(CastingVM.class) 16 + class CastingVMMixin { 17 + @Inject(method = "handleParentheses", at = @At("HEAD"), cancellable = true) 18 + void handleParentheses(Iota iota, CallbackInfoReturnable<Pair<CastingImage, ResolvedPatternType>> cir) { 19 + if (Extern.handleParentheses((CastingVM) (Object) this, iota) instanceof scala.Some<Tuple2<CastingImage,ResolvedPatternType>> result) { 20 + cir.setReturnValue(new Pair(result.get()._1(), result.get()._2())); 21 + } 22 + } 23 + }
+20
src/main/scala/org/eu/net/pool/hexic/mixin/DimIotaMixin.java
··· 1 1 package org.eu.net.pool.hexic.mixin; 2 2 3 + import com.llamalad7.mixinextras.injector.ModifyReturnValue; 4 + import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; 5 + import net.beholderface.oneironaut.casting.iotatypes.DimIota; 6 + import net.fabricmc.loader.api.FabricLoader; 7 + import net.minecraft.nbt.NbtElement; 8 + import net.minecraft.server.world.ServerWorld; 3 9 import net.minecraft.text.Text; 4 10 import org.spongepowered.asm.mixin.Mixin; 5 11 import org.spongepowered.asm.mixin.injection.At; ··· 15 21 @WrapOperation(method = "display", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/Text;of(Ljava/lang/String;)Lnet/minecraft/text/Text;")) 16 22 Text getName(String dim, Operation<Text> original) { 17 23 return dim.startsWith("hexic:fresh_") ? org.eu.net.pool.hexic.Extern.getPocketName(dim) : original.call(dim); 24 + } 25 + 26 + @WrapMethod(method = "deserialize(Lnet/minecraft/nbt/NbtElement;Lnet/minecraft/server/world/ServerWorld;)Lnet/beholderface/oneironaut/casting/iotatypes/DimIota;") 27 + DimIota wrapDeserialize(NbtElement tag, ServerWorld world, Operation<DimIota> original) { 28 + DimIota orig = original.call(tag, world); 29 + if (FabricLoader.getInstance().isDevelopmentEnvironment()) { 30 + System.out.printf("Dimension iota %s returns world %s%n", orig, orig.toWorld(world.getServer())); 31 + } 32 + if (orig.toWorld(world.getServer()) == null) { 33 + return null; 34 + } else { 35 + return orig; 36 + } 37 + // okay, what just happened 18 38 } 19 39 }
+16 -1
src/main/scala/org/eu/net/pool/hexic/mixin/EntityMixin.java
··· 1 1 package org.eu.net.pool.hexic.mixin; 2 2 3 3 import net.minecraft.entity.Entity; 4 + import net.minecraft.entity.damage.DamageSource; 5 + import net.minecraft.entity.damage.DamageSources; 6 + import net.minecraft.entity.player.PlayerEntity; 4 7 import net.minecraft.util.CuboidBlockIterator; 5 8 import net.minecraft.util.math.BlockPos; 6 9 import net.minecraft.util.math.Box; 7 10 import net.minecraft.util.math.MathHelper; 11 + import net.minecraft.util.math.Vec3d; 8 12 import net.minecraft.world.World; 9 13 import org.spongepowered.asm.mixin.Mixin; 10 14 import org.spongepowered.asm.mixin.Shadow; ··· 25 29 26 30 @Shadow protected abstract void tickInVoid(); 27 31 32 + @Shadow public abstract boolean damage(DamageSource source, float amount); 33 + 34 + @Shadow public abstract Vec3d getPos(); 35 + 28 36 @Inject(at = @At("TAIL"), method = {"attemptTickInVoid", "method_31473"}, cancellable = true) 29 37 void attemptTickInVoidBlocks(CallbackInfo ci) { 30 38 Box box = getBoundingBox(); 31 - 39 + if (!((Object) this instanceof PlayerEntity p && (p.isCreative() || p.isSpectator()))) { 40 + var id = getWorld().getRegistryKey().getValue(); 41 + 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)) { 42 + damage(getWorld().getDamageSources().outOfWorld(), Float.MAX_VALUE); 43 + ci.cancel(); 44 + return; 45 + } 46 + } 32 47 CuboidBlockIterator iter = new CuboidBlockIterator(MathHelper.floor(box.minX), MathHelper.floor(box.minY), MathHelper.floor(box.minZ), MathHelper.ceil(box.maxX), MathHelper.ceil(box.maxY), MathHelper.ceil(box.maxZ)); 33 48 while (iter.step()) { 34 49 hexic$scanPos.set(iter.getX(), iter.getY(), iter.getZ());
+33
src/main/scala/org/eu/net/pool/hexic/mixin/OpDimTeleport$SpellMixin.java
··· 1 + package org.eu.net.pool.hexic.mixin; 2 + 3 + import at.petrak.hexcasting.api.casting.eval.CastingEnvironment; 4 + import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 5 + import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 6 + import net.minecraft.server.world.ServerWorld; 7 + 8 + import net.minecraft.util.Identifier; 9 + import net.minecraft.util.math.Vec3d; 10 + import net.minecraft.world.dimension.DimensionType; 11 + import org.spongepowered.asm.mixin.*; 12 + import org.spongepowered.asm.mixin.injection.At; 13 + import org.spongepowered.asm.mixin.injection.Inject; 14 + import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 15 + 16 + @Mixin(targets = "net.beholderface.oneironaut.casting.patterns.spells.great.OpDimTeleport$Spell") 17 + class OpDimTeleport$SpellMixin { 18 + @Shadow @Final private ServerWorld destination; 19 + @Shadow @Final @Mutable private Vec3d coords; 20 + @Unique private boolean toDemiplane; 21 + @Inject(method = "cast(Lat/petrak/hexcasting/api/casting/eval/CastingEnvironment;)V", at = @At("HEAD")) 22 + void preCast(CastingEnvironment env, CallbackInfo ci) { 23 + Identifier targetID = destination.getRegistryKey().getValue(); 24 + toDemiplane = targetID.getNamespace().equals("hexic") && targetID.getPath().startsWith("fresh-"); 25 + if (toDemiplane) { 26 + coords = new Vec3d(5.5, 1.0, 5.5); 27 + } 28 + } 29 + @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)) 30 + double wrapCoordinateScale(DimensionType instance, Operation<Double> original) { 31 + return toDemiplane ? 1.0 : original.call(instance); 32 + } 33 + }
-22
src/main/scala/org/eu/net/pool/hexic/mixin/PhantomEntityMixin.java
··· 1 - package org.eu.net.pool.hexic.mixin; 2 - 3 - import net.minecraft.entity.Entity; 4 - import net.minecraft.entity.EntityType; 5 - import net.minecraft.entity.mob.PhantomEntity; 6 - import net.minecraft.world.World; 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.CallbackInfo; 11 - 12 - @Mixin(PhantomEntity.class) 13 - public abstract class PhantomEntityMixin extends Entity { 14 - public PhantomEntityMixin(EntityType<?> type, World world) { 15 - super(type, world); 16 - } 17 - 18 - @Inject(method = "tick", at = @At("HEAD")) 19 - void phantomsSelfDestruct(CallbackInfo ci) { 20 - this.remove(RemovalReason.DISCARDED); 21 - } 22 - }
+18
src/main/scala/org/eu/net/pool/hexic/mixin/WorldMixin.java
··· 1 + package org.eu.net.pool.hexic.mixin; 2 + 3 + import net.minecraft.world.World; 4 + import org.eu.net.pool.hexic.Extern; 5 + import org.spongepowered.asm.mixin.Mixin; 6 + import org.spongepowered.asm.mixin.injection.At; 7 + import org.spongepowered.asm.mixin.injection.Inject; 8 + import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 9 + import scala.ref.ReferenceQueue; 10 + import scala.ref.WeakReference; 11 + 12 + @Mixin(World.class) 13 + public class WorldMixin { 14 + @Inject(at = @At("TAIL"), method = "<init>") 15 + void postConstruct(CallbackInfo ci) { 16 + Extern.worlds().$plus$eq(new WeakReference(this)); 17 + } 18 + }