repo for my hex addons :3
0
fork

Configure Feed

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

Merge commit 'ad74e8c5f420597f38ade3bfe7c6699ca2736163'

+183 -151
+4 -2
build.gradle.kts
··· 232 232 exactRepo("https://maven.ladysnake.org/releases/", 233 233 "dev.onyxstudios") 234 234 exactRepo("https://pool.net.eu.org/", 235 + "com.unascribed", 235 236 "dev.kineticcat.hexportation", 236 237 "miyucomics.hexcellular", 237 238 "miyucomics.hexical", ··· 245 246 exactRepo("https://maven.terraformersmc.com/releases", 246 247 "com.terraformersmc", 247 248 "dev.emi") 248 - exactRepo("https://repo.sleeping.town/", 249 - "com.unascribed") 249 + // mirrored by poolmaven due to invalid cert 250 + //exactRepo("https://repo.sleeping.town/", 251 + // "com.unascribed") 250 252 exactRepo("https://masa.dy.fi/maven/", 251 253 "carpet") 252 254 exactRepo("https://maven.nucleoid.xyz/",
+5 -2
project/hexic/build.gradle.kts
··· 180 180 modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-level:5.2.3") 181 181 modRuntimeOnly("dev.onyxstudios.cardinal-components-api:cardinal-components-api:5.2.3") 182 182 modRuntimeOnly("com.unascribed:lib39-core:[2.0.0,)!!2.0.27+1.20.1") 183 - modRuntimeOnly("com.unascribed:lib39-avant:[2.0.0,)!!2.0.27+1.20.1") 184 - modRuntimeOnly("com.unascribed:lib39-phantom:[2.0.0,)!!2.0.27+1.20.1") 183 + modRuntimeOnly("com.unascribed:lib39-avant:[2.0.0,3.0.0)!!2.0.27+1.20.1") 184 + modRuntimeOnly("com.unascribed:lib39-phantom:[2.0.0,3.0.0)!!2.0.27+1.20.1") 185 + include("com.unascribed:lib39-core:[2.0.0,3.0.0)!!2.0.27+1.20.1") 186 + include("com.unascribed:lib39-avant:[2.0.0,3.0.0)!!2.0.27+1.20.1") 187 + include("com.unascribed:lib39-phantom:[2.0.0,3.0.0)!!2.0.27+1.20.1") 185 188 modLocalRuntime("gay.object.hexdebug:hexdebug-fabric:0.5.0+1.20.1-SNAPSHOT") 186 189 modLocalRuntime("maven.modrinth:hexcessible:0.2.0") 187 190 modLocalRuntime("maven.modrinth:complex-hex:0.1.3-beta")
+20 -1
project/hexic/changelog
··· 1 - 2.0.0 engulfed hexxychests 1 + 1.4.7-pre.2 add a pattern to force snow 2 + 1.4.7-pre.2 add demiplane binding, replacing the positional arg for shattering demiplanes 3 + 1.4.7-pre.2 allow re-enabling old Lani behavior in config 4 + 1.4.7-pre.2 fix crash with canon 0.11.3 versions 5 + 1.4.7-pre.2 fix list math breaking hexdoc 6 + 1.4.7-pre.2 fix void-air scan 7 + 1.4.7-pre.2 make demiplanes not save redundant chunks 8 + 1.4.7-pre.2 make greatspells actually greatspells 9 + 1.4.7-pre.2 meow 10 + 1.4.7-pre.2 remove internal error with out-of-bounds excisor's gambit 11 + 2.0.0 add casting pouch sealing 12 + 2.0.0 add Cessation and Resumption 13 + 2.0.0 add docs for plane-related patterns 14 + 2.0.0 add Hidden Sun's Nadir (blindness) 15 + 2.0.0 add view iotas (experimental!) 2 16 2.0.0 load config from config/*.properties instead of config/jvm.properties 3 17 2.0.0 mediaweave now applies to all chat messages 4 18 2.0.0 !mediaweave now uses trinket slots 5 19 2.0.0 murmur and reveal now use separate components 6 20 2.0.0 !only one mediaweave can be equipped at a time 21 + 2.0.0 removed map iotas → moved to phlib with identical behavior 7 22 2.0.0 !removed NBT iotas 23 + 2.0.0 removed patchwork iotas → moved to iotaworks with identical behavior 8 24 2.0.0 !removed tripwires 25 + 2.0.0 remove snow pattern & fix lithium incompatibility 26 + 2.0.0 !remove suffering 27 + 2.0.0 rename media bundles to casting pouches 9 28 2.0.0 textures now use JPEG XL
+2 -1
project/hexic/src/client/scala/org/eu/net/pool/hexic/client.scala
··· 292 292 new FabricLanguageProvider(_): 293 293 override def generateTranslations(gen: FabricLanguageProvider.TranslationBuilder): Unit = 294 294 for action -> name <- Vector( 295 + "blind" -> "Hidden Sun's Nadir", 295 296 "deleteworld" -> "Shatter Demiplane", 296 297 "drop" -> "Rejection Distillation", 297 298 "dye_offhand" -> "Apply Pigment", ··· 317 318 "staffcast_factory/lazy" -> "Lani's Lesser Gambit", 318 319 "take" -> "Retention Distillation", 319 320 "unfox" -> "Vulpine Expulsion", 320 - "whatthefuck" -> "Suffering", 321 321 "where" -> "Deductive Purification", 322 322 ) do gen.add(s"hexcasting.action.hexic:$action", name) 323 323 gen.add("hexcasting.special.hexic:tuple", "Coupler's Gambit") ··· 358 358 "get_other_caster" -> "Adds the closest sentient being, excluding me, to the stack.", 359 359 "modulo" -> "Similar to Modulus, but differs for negative numbers: -8 %%₁ 3 = -2, but -8 %%₂ 3 = 1.", 360 360 "murmur" -> "Finds the region of my mind known as the 'chat box' and adds its contents to the stack. If it cannot be found, adds Null instead.", 361 + "blind" -> "Inflicts $(thing)blindness/$. Base cost is one $(l:items/amethyst)$(item)Amethyst Dust/$ per 10 seconds.", 361 362 ) do gen.add(s"book.hexic.page.$page", text) 362 363 gen.add("hexdoc.hexic.description", "Miscellaneous neat features and QoL patterns for Hex Casting") 363 364 gen.add("hexdoc.hexic.title", "Hexic")
+1 -1
project/hexic/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/hexic/media_bundle.json
··· 5 5 "sortnum": 1, 6 6 "pages": [ 7 7 "Though a $(l:items/phials)$(item)Phial/$ is my ultimate goal for _media storage, knitting a pouch out of $(l:addon/hexic/mediaweave)$(item)mediaweave/$ may help me manage my amethyst better. Only a few cloths are needed to give me some decent capacity. Each pouch holds six slots, and I can extend their capacity to twelve with a bit more weave. A small pouch may go within a large pouch, but neither size can be placed within itself.", 8 - "Upon Nature's request for _media, my pouches will offer themselves and their contents first of all; similarly, if I attempt to $(l:patterns/spells/hexcasting#hexcasting:recharge)$(action)Recharge/$ a pouch, the inserted _media will form a 'cloud' of sorts inside, which will settle into any $(l:items/hexcasting)$(item)trinket/$ or similar within.", 8 + "Upon Nature's request for _media, my pouches will offer themselves and their contents first of all; similarly, if I attempt to $(l:patterns/spells/hexcasting#hexcasting:recharge)$(action)Recharge/$ a pouch, the inserted _media will form a 'cloud' of sorts inside, which will settle into any $(l:items/hexcasting)$(item)trinket/$ or similar within. To inhibit this behavior, casting pouches may be sealed by applying $(item)Honeycomb/$ the same way I'd insert an item. I can later wash off the seal by applying a wet sponge.", 9 9 { 10 10 "type": "patchouli:text", 11 11 "advancement": "hexcasting:enlightenment",
-17
project/hexic/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/hexic/no.json
··· 1 - { 2 - "name": "Nature's Revenge", 3 - "advancement": "hexcasting:enlightenment", 4 - "category": "hexcasting:patterns", 5 - "icon": "arrow", 6 - "pages": [ 7 - "No. No. No. I— I thought Horrible was bad— etched into every waking moment— no— no— it hurts— must draw— can't draw— what have I done— why must Nature be like this— ", 8 - { 9 - "type": "hexcasting:pattern", 10 - "op_id": "hexic:whatthefuck", 11 - "anchor": "hexic:whatthefuck", 12 - "input": "???", 13 - "output": "???", 14 - "text": "How could one even begin to draw this?" 15 - } 16 - ] 17 - }
-17
project/hexic/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/zh_cn/entries/addon/hexic/no.json
··· 1 - { 2 - "name": "自然的复仇", 3 - "advancement": "hexcasting:enlightenment", 4 - "category": "hexcasting:patterns", 5 - "icon": "arrow", 6 - "pages": [ 7 - "不、不、不。我——我还觉得“可怕的东西”已经够糟了——没法从意识中消除——不——不——好痛——必须绘制——画不出来——我都做了什么——自然为什么要这样——", 8 - { 9 - "type": "hexcasting:pattern", 10 - "op_id": "hexic:whatthefuck", 11 - "anchor": "hexic:whatthefuck", 12 - "input": "???", 13 - "output": "???", 14 - "text": "到底要怎么画啊?" 15 - } 16 - ] 17 - }
+10
project/hexic/src/main/resources/assets/hexic/jsonpatch/nadirs.jsonpatch
··· 1 + @version "1"; 2 + @target "hexcasting:patchouli_books/thehexbook/en_us/entries/patterns/spells/nadirs.json"; 3 + $pages = arrays.push($pages, { 4 + type: "hexcasting:pattern", 5 + op_id: "hexic:blind", 6 + anchor: "hexic:blind", 7 + input: "entity, number", 8 + output: "", 9 + text: "book.hexic.page.blind" 10 + });
-1
project/hexic/src/main/resources/assets/hexic/lang/zh_cn.json
··· 29 29 "hexcasting.action.hexic:staffcast_factory/lazy": "Lani之初等策略", 30 30 "hexcasting.action.hexic:take": "维持之馏化", 31 31 "hexcasting.action.hexic:unfox": "狐狸之驱逐", 32 - "hexcasting.action.hexic:whatthefuck": "苦痛", 33 32 "hexcasting.action.hexic:where": "演绎之纯化", 34 33 "hexcasting.special.hexic:tuple": "耦合器之策略", 35 34 "hexcasting.special.hexic:tuple.n": "耦合器之策略:%s",
-39
project/hexic/src/main/resources/hexic.mixins.json
··· 1 - { 2 - "required": true, 3 - "minVersion": "0.8", 4 - "package": "org.eu.net.pool.hexic.mixin", 5 - "compatibilityLevel": "JAVA_17", 6 - "mixins": [ 7 - "AbstractFurnaceBlockEntityMixin", 8 - "ActionRegistryEntryMixin", 9 - "CastingEnvironmentMixin", 10 - "CastingVMMixin", 11 - "DimIotaMixin", 12 - "EntityMixin", 13 - "HopperEndpointRegistryMixin", 14 - "ItemMixin", 15 - "ItemPackagedHexMixin", 16 - "ItemStackAccess", 17 - "ItemStackMixin", 18 - "LivingEntityAccess", 19 - "MediafiedItemManagerIndexMixin", 20 - "OpDimTeleport$SpellMixin", 21 - "OpEdifySaplingMixin", 22 - "OpEdifySaplingMixin$Spell", 23 - "OpObservePropertyMixin", 24 - "OpSetPropertyMixin", 25 - "OpTickMixin", 26 - "OpTickMixin$SpellMixin", 27 - "PacketByteBufMixin", 28 - "PlayerInventoryMixin", 29 - "SimpleRegistryMixin", 30 - "StaffCastEnvMixin", 31 - "WorldMixin" 32 - ], 33 - "injectors": { 34 - "defaultRequire": 1 35 - }, 36 - "overwrites": { 37 - "requireAnnotations": true 38 - } 39 - }
+38 -14
project/hexic/src/main/scala/org/eu/net/pool/hexic/main.scala
··· 143 143 import org.eu.net.pool.hexic.mixin.{ItemStackAccess, LivingEntityAccess} 144 144 import at.petrak.hexcasting.common.casting.actions.eval.OpEval 145 145 import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType 146 - import at.petrak.hexcasting.common.casting.actions.spells.OpBreakBlock 147 - import at.petrak.hexcasting.common.casting.actions.spells.OpErase 146 + import at.petrak.hexcasting.common.casting.actions.spells.{OpBreakBlock, OpErase, OpPotionEffect} 148 147 import at.petrak.hexcasting.api.casting.mishaps.MishapBadEntity 149 148 import net.minecraft.entity.ItemEntity 150 149 import net.minecraft.entity.decoration.ItemFrameEntity ··· 182 181 import java.math.BigInteger 183 182 import net.minecraft.entity.ItemEntity 184 183 import net.minecraft.entity.ExperienceOrbEntity 184 + import net.minecraft.entity.effect.StatusEffects 185 185 import net.minecraft.network.packet.s2c.play.PositionFlag 186 186 import net.minecraft.world.chunk.{ChunkStatus, WorldChunk} 187 187 ··· 526 526 .toSeq 527 527 private def heldItems_=(x: Seq[ItemStack]): Unit = 528 528 stack.getOrCreateNbt.put("Contents", NbtList().tap: l => 529 - for item <- x do 529 + for item <- x if !item.isEmpty do 530 530 val c = NbtCompound() 531 531 item.writeNbt(c) 532 532 l.add(c) 533 533 ) 534 + private def isWaxed = Option(stack.getNbt).exists(_.contains("ro")) 535 + private def isWaxed_=(value: Boolean) = 536 + if value then 537 + stack.getOrCreateNbt.put("ro", NbtCompound()) 538 + else 539 + stack.getOrCreateNbt.remove("ro") 534 540 private def withMediaHolders[T](f: Seq[CCMediaHolder] => T): T = 535 - val heldItems = stack.heldItems 536 - try 537 - f(heldItems.flatMap(p => Option(HexCardinalComponents.MEDIA_HOLDER.getNullable(p)))) 538 - finally 539 - stack.heldItems = heldItems 541 + if stack.isWaxed then 542 + f(Seq()) 543 + else 544 + val heldItems = stack.heldItems 545 + try 546 + f(heldItems.flatMap(p => Option(HexCardinalComponents.MEDIA_HOLDER.getNullable(p)))) 547 + finally 548 + stack.heldItems = heldItems 540 549 private def mediaHolders = stack.heldItems.flatMap(p => Option(HexCardinalComponents.MEDIA_HOLDER.getNullable(p))) 541 550 override def getMedia(stack: ItemStack): Long = stack.mediaHolders.map(_.getMedia).sum 542 551 override def getMaxMedia(stack: ItemStack): Long = stack.mediaHolders.map(_.getMaxMedia).sum ··· 577 586 cursorStackReference.set(p) 578 587 stack.heldItems = held.tail 579 588 player.playSound(SoundEvents.ITEM_BUNDLE_REMOVE_ONE, 0.8F, 0.8F + player.getWorld.getRandom.nextFloat * 0.4F) 589 + else if otherStack.isOf(Items.HONEYCOMB) && !stack.isWaxed then 590 + stack.isWaxed = true 591 + otherStack.decrement(1) 592 + player.playSound(SoundEvents.ITEM_HONEYCOMB_WAX_ON, 0.8F, 0.8F + player.getWorld.getRandom.nextFloat * 0.4F) 593 + else if otherStack.isOf(Items.WET_SPONGE) && stack.isWaxed then 594 + stack.isWaxed = false 595 + player.playSound(SoundEvents.BLOCK_SLIME_BLOCK_PLACE, 0.8F, 0.8F + player.getWorld.getRandom.nextFloat * 0.4F) 580 596 else if HexCardinalComponents.MEDIA_HOLDER.getNullable(otherStack) != null then 581 597 val held = stack.heldItems 582 598 if fits(held, otherStack.getItem) then ··· 635 651 override def appendTooltip(stack: ItemStack, world: World, tooltip: util.List[Text], context: TooltipContext): Unit = 636 652 tooltip.add(Text.translatable("hexic.media_bundle.items", stack.heldItems.size, size).styled(_.withColor(Formatting.GRAY))) 637 653 val (consumables, batteries, trinkets) = getMediaInfo(stack) 654 + val isWaxed = stack.isWaxed 655 + var mentionedSealing = false 656 + def convertForWaxing(text: MutableText) = 657 + if isWaxed then 658 + mentionedSealing = true 659 + Text.empty().append(text.styled(_.withStrikethrough(true))).append(" ").append(Text.translatable("hexcasting.tooltip.spellbook.sealed").formatted(Formatting.GOLD)) 660 + else 661 + text 638 662 batteries match 639 663 case Some((total, max)) => tooltip.add(showMedia("external", total + consumables.getOrElse(0L), max)) 640 664 case None => for value <- consumables do 641 - tooltip.add(showMedia("external", value)) 665 + tooltip.add(convertForWaxing(showMedia("external", value))) 642 666 for (total, max) <- trinkets do 643 - tooltip.add(showMedia("internal", total, max)) 667 + tooltip.add(convertForWaxing(showMedia("internal", total, max))) 668 + if !mentionedSealing then 669 + tooltip.add(Text.translatable("hexcasting.tooltip.spellbook.sealed").formatted(Formatting.GOLD)) 644 670 private def showMedia(tag: String, media: Long) = Text.translatable("hexic.media.infinite", Text.translatable(s"hexic.media.$tag"), Text.translatable("hexcasting.tooltip.media", dustAmount(media).styled(_.withColor(ItemMediaHolder.HEX_COLOR)))) 645 671 private def showMedia(tag: String, media: Long, maxMedia: Long) = Text.translatable("hexic.media.finite", Text.translatable(s"hexic.media.$tag"), dustAmount(media).styled(_.withColor(ItemMediaHolder.HEX_COLOR)), Text.translatable("hexcasting.tooltip.media", dustAmount(maxMedia)).styled(_.withColor(ItemMediaHolder.HEX_COLOR)), Text.literal(PERCENTAGE.format(100.0 * media / maxMedia)+"%").styled(_.withColor(MediaHelper.mediaBarColor(media, maxMedia)))) 646 672 private def dustAmount(media: Long) = Text.literal(DUST_AMOUNT.format(media / MediaConstants.DUST_UNIT.toDouble)) ··· 847 873 possible(Random.nextInt(possible.size)) 848 874 Interop.thoughtWorld = RegistryKey.of(RegistryKeys.WORLD, "thought") 849 875 iotaTypeRegistry("access") = PropertyAccessIota.Type 850 - for (color, item) <- Mediaweave.colors do 851 - Registries.ITEM(s"${color.asString}_mediaweave") = item 876 + for color -> item <- Mediaweave.colors do Registries.ITEM(s"${color.asString}_mediaweave") = item 852 877 for item <- MediaBundle.items do 853 878 Registries.ITEM(item.size match 854 879 case 6 => s"small_${item.color.asString}_bundle" ··· 1029 1054 Patterns.register("spellmind/restore", e"deeeeeqdwewewewewewqdwwewwewwewwewwewwqdwwwewwwewwwewwwewwwewww"): 1030 1055 Patterns.mkAction: (img, cont) => 1031 1056 ??? 1032 - Patterns.register("whatthefuck", ne"daadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaadaadaddaddaadaddaadaadaddaddaadaddaddaadaddaadaadadda"): 1033 - Patterns.mkLiteral(PatternIota(e"wedqawqeewdeaqeewdeaqqedqawqqedqawqeedqawqqewdeaqeedqawqeewdeaqqewdeaqeewdeaqeedqawqqedqawqqewdeaqeedqawqeewdeaqqewdeaqeewdeaqeedqawqqedqawqqewdeaqqedqawqeewdeaqeewdeaqqedqawqqedqawqeedqawqqewdeaqqedqawqeewdeaqeewdeaqqedqawqqedqawqeedqawqqewdeaqeedqawqeewdeaqeewdeaqqedqawqqedqawqeedqawqqewdeaqqedqawqeewdeaqqewdeaqeewdeaqeedqawqqedqawqqewdeaqe")) 1034 1057 def fox(tr: PlayerEntity ?=> PartialFunction[Option[FoxEntity.Type], Option[FoxEntity.Type]]): Action = 1035 1058 Patterns.mkAction: (img, cont) => 1036 1059 img.getStack.lastOption match ··· 1443 1466 case _ => true 1444 1467 val sorted = others.toSeq.sortBy(_.getPos.squaredDistanceTo(summon[CastingEnvironment].mishapSprayPos)) 1445 1468 sorted.headOption.fold(NullIota())(EntityIota(_)) 1469 + Patterns.register("blind", se"qqqqqadwawawd")(OpPotionEffect(StatusEffects.BLINDNESS, 1000, false, false)) 1446 1470 Patterns.register("erase", e"wqwdwqwawwwwwawwwww"): 1447 1471 Patterns.mkAction: (img, cont) => 1448 1472 def mkResult(scale: Int, pos: => Vec3d, spell: => Unit) =
+2
util/changelog
··· 1 1 0.1.2 add portals 2 + 0.1.2 documented Flock's Disintegration and Speaker's Distillation for maps 2 3 0.1.2 intrusive holder allocation tracking 4 + 0.1.2 made maps insertion-ordered
+36 -20
util/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/phlib/maps.json
··· 15 15 }, 16 16 { 17 17 "type": "hexcasting:pattern", 18 - "op_id": "hexcasting:add", 19 - "anchor": "hexcasting:add", 20 - "input": "map<k, v>, map<k, v>", 18 + "op_id": "hexcasting:replace", 19 + "anchor": "hexcasting:replace", 20 + "input": "map<k, v>, k, v", 21 21 "output": "map<k, v>", 22 - "text": "Merges two maps together. Maps cannot have duplicate items: any entry in the latter map will override that in the first map." 22 + "text": "Replaces the element associated with $(n)k/$ with $(n)v/$. If $(n)k/$ is not present in the map, the entry is created and placed at the end of the map." 23 23 }, 24 24 { 25 25 "type": "hexcasting:pattern", 26 - "op_id": "hexcasting:sub", 27 - "anchor": "hexcasting:sub", 28 - "input": "map<k, v>, map<t, *>", 26 + "op_id": "hexcasting:remove_from", 27 + "anchor": "hexcasting:remove_from", 28 + "input": "map<k, v>, k", 29 29 "output": "map<k, v>", 30 - "text": "Removes every entry in the first map that is found in the second map. The values in the second map are ignored." 30 + "text": "Throws out the map's association for the given key. If there is no matching association, the map is returned unchanged." 31 31 }, 32 32 { 33 33 "type": "hexcasting:pattern", ··· 35 35 "anchor": "hexcasting:index", 36 36 "input": "map<k, v>, k", 37 37 "output": "v?", 38 - "text": "Disintegrates the map, returning only the value associated with the given key. O(1), unlike scanning a list." 38 + "text": "Destroys the map, returning only the value associated with the given key. O(1), unlike scanning a list." 39 + }, 40 + { 41 + "type": "hexcasting:pattern", 42 + "op_id": "hexcasting:splat", 43 + "anchor": "hexcasting:splat", 44 + "input": "map<k, v>", 45 + "output": "k, v, k, v...", 46 + "text": "Disintegrates the map, returning the key-value pairs in the order I inserted them." 39 47 }, 40 48 { 41 49 "type": "hexcasting:pattern", 42 50 "op_id": "hexcasting:unappend", 43 51 "anchor": "hexcasting:unappend", 44 - "input": "map<k, v>", 45 - "output": "map<k, v>, (k, v)?", 46 - "text": "Shaves off one element of a map. Which element gets shaved off is undefined and subject to Nature's whims. If used on an empty map, returns two nulls." 52 + "input": "map<k,v>", 53 + "output": "map<k,v>, (k,v | nulls)", 54 + "text": "Shaves off the element I added most recently from a map. If used on an empty map, returns two nulls." 47 55 }, 48 56 { 49 57 "type": "hexcasting:pattern", 50 - "op_id": "hexcasting:replace", 51 - "anchor": "hexcasting:replace", 52 - "input": "map<k, v>, k, v", 58 + "op_id": "hexcasting:deconstruct", 59 + "anchor": "hexcasting:deconstruct", 60 + "input": "map<k,v>", 61 + "output": "map<k,v>, (k,v | nulls)", 62 + "text": "Shaves off the element I added least recently from a map. If used on an empty map, returns two nulls." 63 + }, 64 + { 65 + "type": "hexcasting:pattern", 66 + "op_id": "hexcasting:add", 67 + "anchor": "hexcasting:add", 68 + "input": "map<k, v>, map<k, v>", 53 69 "output": "map<k, v>", 54 - "text": "Replaces the element associated with $(n)k/$ with $(n)v/$. If $(n)k/$ is not present in the map, the entry is created." 70 + "text": "Merges two maps together. Maps cannot have duplicate items: any entry in the latter map will override that in the first map." 55 71 }, 56 72 { 57 73 "type": "hexcasting:pattern", 58 - "op_id": "hexcasting:remove_from", 59 - "anchor": "hexcasting:remove_from", 60 - "input": "map<k, v>, k", 74 + "op_id": "hexcasting:sub", 75 + "anchor": "hexcasting:sub", 76 + "input": "map<k, v>, map<t, *>", 61 77 "output": "map<k, v>", 62 - "text": "Throws out the map's association for the given key. If there is no matching association, the map is returned unchanged." 78 + "text": "Removes every entry in the first map that is found in the second map. The values in the second map are ignored." 63 79 } 64 80 ] 65 81 }
+1
util/src/main/resources/phlib.mixins.json
··· 6 6 "mixins": [ 7 7 "ActuallySimpleRegistryMixin", 8 8 "AllocationTrackerMixin", 9 + "OpSplatMixin", 9 10 "PatternIotaMixin", 10 11 "SimpleRegistryMixin" 11 12 ]
+31 -35
util/src/main/scala/org/eu/net/pool/phlib/maps.scala
··· 11 11 import net.minecraft.text.{MutableText, Text} 12 12 import net.minecraft.util.Formatting 13 13 14 + import java.util 14 15 import java.util.List => JavaList 16 + import scala.collection.immutable.ListMap 15 17 import net.minecraft.server.world.ServerWorld 16 18 import at.petrak.hexcasting.api.utils.HexUtils 17 19 18 - case class MapIota(map: Map[NbtCompound, NbtCompound] = Map(), trusted: Boolean = false)(using val world: ServerWorld) extends Iota(MapIota, map): 20 + case class MapIota(map: Map[NbtCompound, NbtCompound] = ListMap())(using val world: ServerWorld) extends Iota(MapIota, map): 19 21 def get(key: Iota): Option[Iota] = map.get(IotaType.serialize(key)).map(IotaType.deserialize(_, summon)) 20 22 def apply(key: Iota): Iota = get(key) getOrElse NullIota() 21 23 def -(keys: Iota*): MapIota = MapIota(map -- (keys map IotaType.serialize)) ··· 45 47 c.put("k", p._1) 46 48 c.put("v", p._2)) tap l.add) 47 49 override def size = map.toSeq.map(_.size + _.size - 1).sum + 1 48 - override def subIotas(): java.lang.Iterable[Iota] = 49 - if trusted then 50 - // skip deserialization for performance 51 - // this is safe because `trusted` can only be true if truenames have 52 - // already been checked, and we override `size` 53 - Seq() 54 - else 55 - toList 56 - def asJavaMap: java.util.Map[Iota, Iota] = map.map(e => (IotaType.deserialize(e._1, summon), IotaType.deserialize(e._2, summon))).toMap 50 + override def subIotas(): java.lang.Iterable[Iota] = toList 51 + def asJavaMap: java.util.Map[Iota, Iota] = 52 + val ret = util.LinkedHashMap[Iota, Iota]() 53 + for k -> v <- map do 54 + ret(IotaType.deserialize(k, summon)) = IotaType.deserialize(v, summon) 55 + ret 57 56 object MapIota extends IotaType[MapIota]: 58 57 def color: Int = 0xb0641c 59 58 def deserialize(using data: NbtElement, world: ServerWorld): MapIota = 60 59 val l = HexUtils.downcast(data, NbtList.TYPE) 61 60 val o = HexUtils.downcast(_, NbtCompound.TYPE) 62 - l.map(o) 63 - .map(c => (o(c("k")), o(c("v")))) 64 - .toMap[NbtCompound, NbtCompound] 65 - .pipe(new MapIota(_, trusted = true)) 61 + new MapIota(ListMap.from(l.map(o).map(c => (o(c("k")), o(c("v")))))) 66 62 def display(data: NbtElement): Text = 67 63 val items = HexUtils.downcast(data, NbtList.TYPE) 68 64 val output: MutableText = "[" ··· 84 80 output 85 81 def fromJavaMap(map: java.util.Map[Iota, Iota])(using ServerWorld) = MapIota((map: collection.Map[Iota, Iota]).map(e => IotaType.serialize(e._1) -> IotaType.serialize(e._2)).toMap) 86 82 val mapArithmetic = 87 - import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic.* 88 - arith("map", 89 - ADD -> ((a: MapIota, b: MapIota) => a ++ b), 90 - SUB -> ((a: MapIota, b: MapIota) => a -- b), 91 - ABS -> ((a: MapIota) => DoubleIota(a.map.size)), 92 - INDEX -> ((a: MapIota, k: Iota) => a(k)), 93 - UNAPPEND -> ((a: MapIota) => a.lastOption.map(p => Seq(p._1, p._2)).getOrElse(Seq(NullIota(), NullIota())) prepended a.init), 94 - INDEX_OF -> ((a: MapIota, v: Iota) => 95 - val c = IotaType.serialize(v) 96 - a.update(_.filter(_._2 == c))), 97 - REMOVE -> ((a: MapIota, k: Iota) => a - k), 98 - REPLACE -> ((a: MapIota, k: Iota, v: Iota) => a + (k -> v)), 99 - UNCONS -> ((a: MapIota) => a.headOption.map(p => Seq(p._1, p._2)).getOrElse(Seq(NullIota(), NullIota())) prepended a.tail), 100 - AND -> ((a: MapIota, b: MapIota) => a & b), 101 - OR -> ((a: MapIota, b: MapIota) => b ++ a), 102 - XOR -> ((a: MapIota, b: MapIota) => a ^ b), 103 - GREATER -> ((a: MapIota, b: MapIota) => a.map.containsAll(b.map) && a.map != b.map), 104 - LESS -> ((a: MapIota, b: MapIota) => b.map.containsAll(a.map) && a.map != b.map), 105 - GREATER_EQ -> ((a: MapIota, b: MapIota) => a.map containsAll b.map), 106 - LESS_EQ -> ((a: MapIota, b: MapIota) => b.map containsAll a.map), 107 - ) 83 + import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic.* 84 + arith("map", 85 + ADD -> ((a: MapIota, b: MapIota) => a ++ b), 86 + SUB -> ((a: MapIota, b: MapIota) => a -- b), 87 + ABS -> ((a: MapIota) => DoubleIota(a.map.size)), 88 + INDEX -> ((a: MapIota, k: Iota) => a(k)), 89 + UNAPPEND -> ((a: MapIota) => a.lastOption.map(p => Seq(p._1, p._2)).getOrElse(Seq(NullIota(), NullIota())) prepended a.init), 90 + INDEX_OF -> ((a: MapIota, v: Iota) => 91 + val c = IotaType.serialize(v) 92 + a.update(_.filter(_._2 == c))), 93 + REMOVE -> ((a: MapIota, k: Iota) => a - k), 94 + REPLACE -> ((a: MapIota, k: Iota, v: Iota) => a + (k -> v)), 95 + UNCONS -> ((a: MapIota) => a.headOption.map(p => Seq(p._1, p._2)).getOrElse(Seq(NullIota(), NullIota())) prepended a.tail), 96 + AND -> ((a: MapIota, b: MapIota) => a & b), 97 + OR -> ((a: MapIota, b: MapIota) => b ++ a), 98 + XOR -> ((a: MapIota, b: MapIota) => a ^ b), 99 + GREATER -> ((a: MapIota, b: MapIota) => a.map.containsAll(b.map) && a.map != b.map), 100 + LESS -> ((a: MapIota, b: MapIota) => b.map.containsAll(a.map) && a.map != b.map), 101 + GREATER_EQ -> ((a: MapIota, b: MapIota) => a.map containsAll b.map), 102 + LESS_EQ -> ((a: MapIota, b: MapIota) => b.map containsAll a.map), 103 + )
+33
util/src/main/scala/org/eu/net/pool/phlib/mixin/OpSplatMixin.java
··· 1 + package org.eu.net.pool.phlib.mixin; 2 + 3 + import at.petrak.hexcasting.api.casting.SpellList; 4 + import at.petrak.hexcasting.api.casting.iota.Iota; 5 + import at.petrak.hexcasting.api.casting.iota.ListIota; 6 + import at.petrak.hexcasting.common.casting.actions.lists.OpSplat; 7 + import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 8 + import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 9 + import org.eu.net.pool.phlib.MapIota; 10 + import org.spongepowered.asm.mixin.Mixin; 11 + import org.spongepowered.asm.mixin.injection.At; 12 + 13 + import java.util.ArrayList; 14 + import java.util.List; 15 + 16 + // 2026-03-20 01:29 pool: this is *very* out-of-scope for phlib 17 + // but what are you, a fed? 18 + @Mixin(OpSplat.class) 19 + public class OpSplatMixin { 20 + @WrapOperation(at = @At(value = "INVOKE", target = "Lat/petrak/hexcasting/api/casting/OperatorUtils;getList(Ljava/util/List;II)Lat/petrak/hexcasting/api/casting/SpellList;"), method = "execute") 21 + SpellList redirectList(List<? extends Iota> stack, int it, int x, Operation<SpellList> original) { 22 + if (stack.get(it) instanceof MapIota map) { 23 + final var retList = new ArrayList(map.size() * 2); 24 + for (var pair: map.asJavaMap().entrySet()) { 25 + retList.add(pair.getKey()); 26 + retList.add(pair.getValue()); 27 + } 28 + return new ListIota(retList).getList(); 29 + } else { 30 + return original.call(stack, it, x); 31 + } 32 + } 33 + }
-1
util/src/main/scala/org/eu/net/pool/phlib/util.scala
··· 96 96 override def operate(env: CastingEnvironment, img: CastingImage, cont: SpellContinuation): OperationResult = 97 97 val stack = img.getStack.asScala.toSeq 98 98 val args = stack.takeRight(${Expr(a.size)}) 99 - // I'm fairly certain the remainder of this method is considered a war crime 100 99 ${ 101 100 Block(a.zipWithIndex.map { p => 102 101 val (v@ValDef(n, ty, _), i) = p