From fa28e37c313bf83f330c611162709216609aae14 Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Tue, 23 Jun 2026 22:02:35 -0400 Subject: [PATCH 01/16] Implement falling block --- .../casting/actions/spells/OpFallingBlock.kt | 128 ++++++++++++++++++ .../hexcasting/common/lib/hex/HexActions.java | 2 + 2 files changed, 130 insertions(+) create mode 100644 Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFallingBlock.kt diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFallingBlock.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFallingBlock.kt new file mode 100644 index 000000000..72ec9d71d --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFallingBlock.kt @@ -0,0 +1,128 @@ +package at.petrak.hexcasting.common.casting.actions.spells + +import at.petrak.hexcasting.api.mod.HexConfig +import at.petrak.hexcasting.api.casting.* +import at.petrak.hexcasting.api.casting.castables.SpellAction +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment +import at.petrak.hexcasting.api.casting.iota.Iota +import at.petrak.hexcasting.api.misc.MediaConstants +import at.petrak.hexcasting.xplat.IXplatAbstractions +import net.minecraft.core.BlockPos +import net.minecraft.core.particles.BlockParticleOption +import net.minecraft.core.particles.ParticleTypes +import net.minecraft.core.registries.Registries +import net.minecraft.server.level.ServerLevel +import net.minecraft.server.level.ServerPlayer +import net.minecraft.tags.BlockTags +import net.minecraft.world.entity.Entity +import net.minecraft.world.entity.item.FallingBlockEntity +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items +import net.minecraft.world.item.Tier +import net.minecraft.world.item.Tiers +import net.minecraft.world.item.enchantment.Enchantments +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.FallingBlock +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.phys.Vec3 +import kotlin.math.roundToLong + +// https://github.com/VazkiiMods/Botania/blob/1.21.1-porting/Xplat/src/main/java/vazkii/botania/common/item/lens/WeightLens.java +object OpFallingBlock : SpellAction { + override val argc = 1 + + override fun execute(args: List, env: CastingEnvironment): SpellAction.Result { + val pos = args.getVec3(0, argc) + env.assertVecInRange(pos) + + val centered = Vec3.atCenterOf(BlockPos.containing(pos)) + return SpellAction.Result( + Spell(pos), + (1.5 * MediaConstants.DUST_UNIT).roundToLong(), + listOf(ParticleSpray.burst(centered, 1.0)) + ) + } + + private data class Spell(val v: Vec3) : RenderedSpell { + override fun cast(env: CastingEnvironment) { + val pos = BlockPos.containing(v) + + val blockstate = env.world.getBlockState(pos) + if (!env.canEditBlockAt(pos) || !IXplatAbstractions.INSTANCE.isBreakingAllowed(env.world, pos, blockstate, env.caster)) + return + + val tier = HexConfig.server().opBreakHarvestLevel() + + val stateBelow = env.world.getBlockState(pos.below()) + + if (( + FallingBlock.isFree(stateBelow) + || !stateBelow.canOcclude() + || stateBelow.`is`(BlockTags.SLABS) + ) + && !blockstate.isAir + && blockstate.getDestroySpeed(env.world, pos) >= 0f // fix being able to break bedrock &c + && env.world.getBlockEntity(pos) == null + && IXplatAbstractions.INSTANCE.isCorrectTierForDrops(tier, blockstate) + && canSilkTouch(env.world, pos, blockstate, tier, env.castingEntity as? ServerPlayer) + ) { + val falling: FallingBlockEntity = FallingBlockEntity.fall(env.world, pos, blockstate) + falling.time = 1 + env.world.sendParticles( + BlockParticleOption(ParticleTypes.FALLING_DUST, blockstate), + pos.x + 0.5, + pos.y + 0.5, + pos.z + 0.5, + 10, + 0.45, + 0.45, + 0.45, + 5.0 + ) + } + } + + fun canSilkTouch(level: ServerLevel, pos: BlockPos, state: BlockState, harvestTier: Tier, owner: Entity?): Boolean { + val harvestToolStack: ItemStack = getHarvestToolStack(harvestTier, state) + if (harvestToolStack.isEmpty) { + return false + } + harvestToolStack.enchant(level.holderLookup(Registries.ENCHANTMENT).getOrThrow(Enchantments.SILK_TOUCH), 1) + val drops: List = Block.getDrops(state, level, pos, null, owner, harvestToolStack) + val blockItem: Item = state.block.asItem() + return drops.any { s -> s.item === blockItem } + } + + companion object { + fun getHarvestToolStack(harvestTier: Tier, state: BlockState): ItemStack { + return getTool(harvestTier, state).copy() + } + + private fun getTool(harvestTier: Tier, state: BlockState): ItemStack { + if (harvestTier !in HARVEST_TOOLS_BY_LEVEL.keys) return ItemStack.EMPTY + if (!state.requiresCorrectToolForDrops()) { + return HARVEST_TOOLS_BY_LEVEL[harvestTier]!![0] + } + for (tool in HARVEST_TOOLS_BY_LEVEL[harvestTier]!!) { + if (tool.isCorrectToolForDrops(state)) { + return tool + } + } + return ItemStack.EMPTY + } + + private val HARVEST_TOOLS_BY_LEVEL: Map> = mapOf( + Tiers.WOOD to stacks(Items.WOODEN_PICKAXE, Items.WOODEN_AXE, Items.WOODEN_HOE, Items.WOODEN_SHOVEL), + Tiers.STONE to stacks(Items.STONE_PICKAXE, Items.STONE_AXE, Items.STONE_HOE, Items.STONE_SHOVEL), + Tiers.IRON to stacks(Items.IRON_PICKAXE, Items.IRON_AXE, Items.IRON_HOE, Items.IRON_SHOVEL), + Tiers.DIAMOND to stacks(Items.DIAMOND_PICKAXE, Items.DIAMOND_AXE, Items.DIAMOND_HOE, Items.DIAMOND_SHOVEL), + Tiers.NETHERITE to stacks(Items.NETHERITE_PICKAXE, Items.NETHERITE_AXE, Items.NETHERITE_HOE, Items.NETHERITE_SHOVEL) + ) + + private fun stacks(vararg items: Item): List { + return items.map { item -> ItemStack(item) } + } + } + } +} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java index eb049a8e7..6e65a78f0 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java @@ -275,6 +275,8 @@ public class HexActions { new ActionRegistryEntry(HexPattern.fromAngles("qdqawwaww", HexDir.EAST), OpErase.INSTANCE)); public static final ActionRegistryEntry EDIFY = make("edify", new ActionRegistryEntry(HexPattern.fromAngles("wqaqwd", HexDir.NORTH_EAST), OpEdifySapling.INSTANCE)); + public static final ActionRegistryEntry FALLING_BLOCK = make("falling_block", + new ActionRegistryEntry(HexPattern.fromAngles("wqwawqwqwqwqwqw", HexDir.EAST), OpFallingBlock.INSTANCE)); public static final ActionRegistryEntry BEEP = make("beep", new ActionRegistryEntry(HexPattern.fromAngles("adaa", HexDir.WEST), OpBeep.INSTANCE)); From d400c0012af60049b76c45706ece6b12036d1506 Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Tue, 23 Jun 2026 22:02:43 -0400 Subject: [PATCH 02/16] Implement freeze --- .../recipes/freeze/freeze/blue_ice.json | 32 +++++++ .../recipes/freeze/freeze/packed_ice.json | 32 +++++++ .../freeze/freeze/powder_snow_cauldron.json | 32 +++++++ .../recipe/freeze/freeze/blue_ice.json | 10 +++ .../recipe/freeze/freeze/packed_ice.json | 10 +++ .../freeze/freeze/powder_snow_cauldron.json | 13 +++ .../common/casting/actions/spells/OpFreeze.kt | 64 ++++++++++++++ .../actions/spells/great/OpBrainsweep.kt | 3 +- .../hexcasting/common/lib/hex/HexActions.java | 11 ++- .../common/recipe/BrainsweepRecipe.java | 13 --- .../common/recipe/CopyProperties.java | 19 +++++ .../common/recipe/FreezeRecipe.java | 85 +++++++++++++++++++ .../common/recipe/HexRecipeStuffRegistry.java | 3 + .../datagen/recipe/HexplatRecipes.java | 11 +++ .../recipe/builders/FreezeRecipeBuilder.java | 58 +++++++++++++ .../recipes/freeze/freeze/blue_ice.json | 32 +++++++ .../recipes/freeze/freeze/packed_ice.json | 32 +++++++ .../freeze/freeze/powder_snow_cauldron.json | 32 +++++++ .../recipe/freeze/freeze/blue_ice.json | 10 +++ .../recipe/freeze/freeze/packed_ice.json | 10 +++ .../freeze/freeze/powder_snow_cauldron.json | 13 +++ .../recipes/freeze/freeze/blue_ice.json | 32 +++++++ .../recipes/freeze/freeze/packed_ice.json | 32 +++++++ .../freeze/freeze/powder_snow_cauldron.json | 32 +++++++ .../recipe/freeze/freeze/blue_ice.json | 10 +++ .../recipe/freeze/freeze/packed_ice.json | 10 +++ .../freeze/freeze/powder_snow_cauldron.json | 13 +++ 27 files changed, 636 insertions(+), 18 deletions(-) create mode 100644 Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json create mode 100644 Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json create mode 100644 Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json create mode 100644 Common/src/generated/resources/data/hexcasting/recipe/freeze/freeze/blue_ice.json create mode 100644 Common/src/generated/resources/data/hexcasting/recipe/freeze/freeze/packed_ice.json create mode 100644 Common/src/generated/resources/data/hexcasting/recipe/freeze/freeze/powder_snow_cauldron.json create mode 100644 Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFreeze.kt create mode 100644 Common/src/main/java/at/petrak/hexcasting/common/recipe/CopyProperties.java create mode 100644 Common/src/main/java/at/petrak/hexcasting/common/recipe/FreezeRecipe.java create mode 100644 Common/src/main/java/at/petrak/hexcasting/datagen/recipe/builders/FreezeRecipeBuilder.java create mode 100644 Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json create mode 100644 Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json create mode 100644 Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json create mode 100644 Fabric/src/generated/resources/data/hexcasting/recipe/freeze/freeze/blue_ice.json create mode 100644 Fabric/src/generated/resources/data/hexcasting/recipe/freeze/freeze/packed_ice.json create mode 100644 Fabric/src/generated/resources/data/hexcasting/recipe/freeze/freeze/powder_snow_cauldron.json create mode 100644 Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json create mode 100644 Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json create mode 100644 Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json create mode 100644 Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/freeze/blue_ice.json create mode 100644 Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/freeze/packed_ice.json create mode 100644 Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/freeze/powder_snow_cauldron.json diff --git a/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json new file mode 100644 index 000000000..db092ef28 --- /dev/null +++ b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:freeze/blue_ice" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:freeze/blue_ice" + ] + } +} \ No newline at end of file diff --git a/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json new file mode 100644 index 000000000..ccded8855 --- /dev/null +++ b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:freeze/packed_ice" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:freeze/packed_ice" + ] + } +} \ No newline at end of file diff --git a/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json new file mode 100644 index 000000000..1e49ebee8 --- /dev/null +++ b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:freeze/powder_snow_cauldron" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:freeze/powder_snow_cauldron" + ] + } +} \ No newline at end of file diff --git a/Common/src/generated/resources/data/hexcasting/recipe/freeze/freeze/blue_ice.json b/Common/src/generated/resources/data/hexcasting/recipe/freeze/freeze/blue_ice.json new file mode 100644 index 000000000..8106c7878 --- /dev/null +++ b/Common/src/generated/resources/data/hexcasting/recipe/freeze/freeze/blue_ice.json @@ -0,0 +1,10 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:block", + "block": "minecraft:packed_ice" + }, + "result": { + "Name": "minecraft:blue_ice" + } +} \ No newline at end of file diff --git a/Common/src/generated/resources/data/hexcasting/recipe/freeze/freeze/packed_ice.json b/Common/src/generated/resources/data/hexcasting/recipe/freeze/freeze/packed_ice.json new file mode 100644 index 000000000..cc60d0637 --- /dev/null +++ b/Common/src/generated/resources/data/hexcasting/recipe/freeze/freeze/packed_ice.json @@ -0,0 +1,10 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:block", + "block": "minecraft:ice" + }, + "result": { + "Name": "minecraft:packed_ice" + } +} \ No newline at end of file diff --git a/Common/src/generated/resources/data/hexcasting/recipe/freeze/freeze/powder_snow_cauldron.json b/Common/src/generated/resources/data/hexcasting/recipe/freeze/freeze/powder_snow_cauldron.json new file mode 100644 index 000000000..da6f3a7d3 --- /dev/null +++ b/Common/src/generated/resources/data/hexcasting/recipe/freeze/freeze/powder_snow_cauldron.json @@ -0,0 +1,13 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:block", + "block": "minecraft:water_cauldron" + }, + "result": { + "Name": "minecraft:powder_snow_cauldron", + "Properties": { + "level": "1" + } + } +} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFreeze.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFreeze.kt new file mode 100644 index 000000000..1f8e7c24a --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFreeze.kt @@ -0,0 +1,64 @@ +package at.petrak.hexcasting.common.casting.actions.spells + +import at.petrak.hexcasting.api.casting.* +import at.petrak.hexcasting.api.casting.castables.SpellAction +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment +import at.petrak.hexcasting.api.casting.iota.Iota +import at.petrak.hexcasting.api.misc.MediaConstants +import at.petrak.hexcasting.common.recipe.CopyProperties +import at.petrak.hexcasting.common.recipe.HexRecipeStuffRegistry +import at.petrak.hexcasting.xplat.IXplatAbstractions +import net.minecraft.core.BlockPos +import net.minecraft.server.level.ServerPlayer +import net.minecraft.world.level.block.Blocks +import net.minecraft.world.level.block.LiquidBlock +import net.minecraft.world.level.material.Fluids +import net.minecraft.world.phys.Vec3 + +object OpFreeze : SpellAction { + override val argc = 1 + + override fun execute(args: List, env: CastingEnvironment): SpellAction.Result { + val toFreeze = Vec3.atCenterOf(BlockPos.containing(args.getVec3(0, argc))) + + env.assertVecInRange(toFreeze) + + return SpellAction.Result( + Spell(toFreeze), + MediaConstants.DUST_UNIT, + listOf(ParticleSpray.burst(toFreeze, 1.0)) + ) + } + + private data class Spell(val vec: Vec3) : RenderedSpell { + override fun cast(env: CastingEnvironment) { + val pos = BlockPos.containing(vec) + val blockState = env.world.getBlockState(pos) + val fluidState = env.world.getFluidState(pos) + + if (!env.canEditBlockAt(pos) || !IXplatAbstractions.INSTANCE.isBreakingAllowed(env.world, pos, blockState, env.castingEntity as? ServerPlayer)) + return + + if (fluidState.type == Fluids.WATER && blockState.block is LiquidBlock) { + env.world.setBlockAndUpdate(pos, Blocks.ICE.defaultBlockState()) + return + } + if (fluidState.type == Fluids.LAVA && blockState.block is LiquidBlock) { + env.world.setBlockAndUpdate(pos, Blocks.OBSIDIAN.defaultBlockState()) + return + } + if (fluidState.type == Fluids.FLOWING_LAVA && blockState.block is LiquidBlock) { + env.world.setBlockAndUpdate(pos, Blocks.COBBLESTONE.defaultBlockState()) + return + } + + val recman = env.world.recipeManager + val recipes = recman.getAllRecipesFor(HexRecipeStuffRegistry.FREEZE_TYPE).map{ holder -> holder.value } + + val recipe = recipes.find{ it.matches(blockState) } + + if (recipe != null) + env.world.setBlockAndUpdate(pos, CopyProperties.copyProperties(blockState, recipe.result)) + } + } +} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/great/OpBrainsweep.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/great/OpBrainsweep.kt index 29730acca..2e0f9ade7 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/great/OpBrainsweep.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/great/OpBrainsweep.kt @@ -14,6 +14,7 @@ import at.petrak.hexcasting.api.casting.mishaps.MishapBadLocation import at.petrak.hexcasting.api.mod.HexConfig import at.petrak.hexcasting.api.mod.HexTags import at.petrak.hexcasting.common.recipe.BrainsweepRecipe +import at.petrak.hexcasting.common.recipe.CopyProperties import at.petrak.hexcasting.common.recipe.HexRecipeStuffRegistry import at.petrak.hexcasting.ktxt.tellWitnessesThatIWasMurdered import at.petrak.hexcasting.mixin.accessor.AccessorLivingEntity @@ -75,7 +76,7 @@ object OpBrainsweep : SpellAction { val recipe: BrainsweepRecipe ) : RenderedSpell { override fun cast(env: CastingEnvironment) { - env.world.setBlockAndUpdate(pos, BrainsweepRecipe.copyProperties(state, recipe.result)) + env.world.setBlockAndUpdate(pos, CopyProperties.copyProperties(state, recipe.result)) HexAPI.instance().brainsweep(sacrifice) diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java index 6e65a78f0..33158c6fc 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java @@ -258,10 +258,6 @@ public class HexActions { Fluids.WATER))); public static final ActionRegistryEntry DESTROY_WATER = make("destroy_water", new ActionRegistryEntry(HexPattern.fromAngles("dedwedade", HexDir.SOUTH_WEST), OpDestroyFluid.INSTANCE)); - public static final ActionRegistryEntry IGNITE = make("ignite", - new ActionRegistryEntry(HexPattern.fromAngles("aaqawawa", HexDir.SOUTH_EAST), OpIgnite.INSTANCE)); - public static final ActionRegistryEntry EXTINGUISH = make("extinguish", - new ActionRegistryEntry(HexPattern.fromAngles("ddedwdwd", HexDir.SOUTH_WEST), OpExtinguish.INSTANCE)); public static final ActionRegistryEntry CONJURE_BLOCK = make("conjure_block", new ActionRegistryEntry(HexPattern.fromAngles("qqa", HexDir.NORTH_EAST), new OpConjureBlock(false))); public static final ActionRegistryEntry CONJURE_LIGHT = make("conjure_light", @@ -278,6 +274,13 @@ public class HexActions { public static final ActionRegistryEntry FALLING_BLOCK = make("falling_block", new ActionRegistryEntry(HexPattern.fromAngles("wqwawqwqwqwqwqw", HexDir.EAST), OpFallingBlock.INSTANCE)); + public static final ActionRegistryEntry IGNITE = make("ignite", + new ActionRegistryEntry(HexPattern.fromAngles("aaqawawa", HexDir.SOUTH_EAST), OpIgnite.INSTANCE)); + public static final ActionRegistryEntry EXTINGUISH = make("extinguish", + new ActionRegistryEntry(HexPattern.fromAngles("ddedwdwd", HexDir.SOUTH_WEST), OpExtinguish.INSTANCE)); + public static final ActionRegistryEntry FREEZE = make("freeze", + new ActionRegistryEntry(HexPattern.fromAngles("weeeweedada", HexDir.WEST), OpFreeze.INSTANCE)); + public static final ActionRegistryEntry BEEP = make("beep", new ActionRegistryEntry(HexPattern.fromAngles("adaa", HexDir.WEST), OpBeep.INSTANCE)); diff --git a/Common/src/main/java/at/petrak/hexcasting/common/recipe/BrainsweepRecipe.java b/Common/src/main/java/at/petrak/hexcasting/common/recipe/BrainsweepRecipe.java index a9f8ea338..844fccf2d 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/recipe/BrainsweepRecipe.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/recipe/BrainsweepRecipe.java @@ -67,19 +67,6 @@ public ItemStack getResultItem(HolderLookup.Provider registries) { return ItemStack.EMPTY.copy(); } - // Because kotlin doesn't like doing raw, unchecked types - // Can't blame it, but that's what we need to do - @SuppressWarnings({"rawtypes", "unchecked"}) - public static BlockState copyProperties(BlockState original, BlockState copyTo) { - for (Property prop : original.getProperties()) { - if (copyTo.hasProperty(prop)) { - copyTo = copyTo.setValue(prop, original.getValue(prop)); - } - } - - return copyTo; - } - public static class Serializer extends RecipeSerializerBase { public static MapCodec CODEC = RecordCodecBuilder.mapCodec(inst -> inst.group( diff --git a/Common/src/main/java/at/petrak/hexcasting/common/recipe/CopyProperties.java b/Common/src/main/java/at/petrak/hexcasting/common/recipe/CopyProperties.java new file mode 100644 index 000000000..0cf3fca47 --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/recipe/CopyProperties.java @@ -0,0 +1,19 @@ +package at.petrak.hexcasting.common.recipe; + +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.Property; + +public class CopyProperties { + // Because kotlin doesn't like doing raw, unchecked types + // Can't blame it, but that's what we need to do + @SuppressWarnings({"rawtypes", "unchecked"}) + public static BlockState copyProperties(BlockState original, BlockState copyTo) { + for (Property prop : original.getProperties()) { + if (copyTo.hasProperty(prop)) { + copyTo = copyTo.setValue(prop, original.getValue(prop)); + } + } + + return copyTo; + } +} diff --git a/Common/src/main/java/at/petrak/hexcasting/common/recipe/FreezeRecipe.java b/Common/src/main/java/at/petrak/hexcasting/common/recipe/FreezeRecipe.java new file mode 100644 index 000000000..2979527f3 --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/recipe/FreezeRecipe.java @@ -0,0 +1,85 @@ +package at.petrak.hexcasting.common.recipe; + +import at.petrak.hexcasting.common.lib.HexStateIngredients; +import at.petrak.hexcasting.common.recipe.ingredient.state.StateIngredient; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.HolderLookup; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeInput; +import net.minecraft.world.item.crafting.RecipeSerializer; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.NotNull; + +public record FreezeRecipe( + StateIngredient blockIn, + BlockState result +) implements Recipe { + public boolean matches(BlockState blockIn) { + return this.blockIn.test(blockIn); + } + + @Override + public RecipeType getType() { + return HexRecipeStuffRegistry.FREEZE_TYPE; + } + + @Override + public RecipeSerializer getSerializer() { + return HexRecipeStuffRegistry.FREEZE; + } + + // in order to get this to be a "Recipe" we need to do a lot of bending-over-backwards + // to get the implementation to be satisfied even though we never use it + @Override + public boolean matches(RecipeInput input, Level level) { + return false; + } + + @Override + public ItemStack assemble(RecipeInput input, HolderLookup.Provider registries) { + return ItemStack.EMPTY; + } + + @Override + public boolean canCraftInDimensions(int pWidth, int pHeight) { + return false; + } + + @Override + public ItemStack getResultItem(HolderLookup.Provider registries) { + return ItemStack.EMPTY.copy(); + } + + public static class Serializer extends RecipeSerializerBase { + public static MapCodec CODEC = RecordCodecBuilder.mapCodec(inst -> + inst.group( + HexStateIngredients.TYPED_CODEC.fieldOf("blockIn").forGetter(FreezeRecipe::blockIn), + BlockState.CODEC.fieldOf("result").forGetter(FreezeRecipe::result) + ).apply(inst, FreezeRecipe::new) + ); + public static StreamCodec STREAM_CODEC = StreamCodec.composite( + HexStateIngredients.TYPED_STREAM_CODEC, FreezeRecipe::blockIn, + ByteBufCodecs.VAR_INT, (recipe) -> Block.getId(recipe.result), + (state, stateId) -> + new FreezeRecipe(state, Block.stateById(stateId)) + ); + + @Override + public @NotNull MapCodec codec() { + return CODEC; + } + + @Override + public @NotNull StreamCodec streamCodec() { + return STREAM_CODEC; + } + } +} diff --git a/Common/src/main/java/at/petrak/hexcasting/common/recipe/HexRecipeStuffRegistry.java b/Common/src/main/java/at/petrak/hexcasting/common/recipe/HexRecipeStuffRegistry.java index cc2ffbba0..2dfc941d3 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/recipe/HexRecipeStuffRegistry.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/recipe/HexRecipeStuffRegistry.java @@ -30,12 +30,15 @@ public static void registerTypes(BiConsumer, ResourceLocation> r) public static final RecipeSerializer BRAINSWEEP = registerSerializer("brainsweep", new BrainsweepRecipe.Serializer()); + public static final RecipeSerializer FREEZE = registerSerializer("freeze", + new FreezeRecipe.Serializer()); public static final RecipeSerializer SEAL_FOCUS = registerSerializer( "seal_focus", SealThingsRecipe.FOCUS_SERIALIZER); public static final RecipeSerializer SEAL_SPELLBOOK = registerSerializer( "seal_spellbook", SealThingsRecipe.SPELLBOOK_SERIALIZER); public static RecipeType BRAINSWEEP_TYPE = registerType("brainsweep"); + public static RecipeType FREEZE_TYPE = registerType("freeze"); private static > RecipeSerializer registerSerializer(String name, RecipeSerializer rs) { var old = SERIALIZERS.put(modLoc(name), rs); diff --git a/Common/src/main/java/at/petrak/hexcasting/datagen/recipe/HexplatRecipes.java b/Common/src/main/java/at/petrak/hexcasting/datagen/recipe/HexplatRecipes.java index 35e4ab202..02bd956bb 100644 --- a/Common/src/main/java/at/petrak/hexcasting/datagen/recipe/HexplatRecipes.java +++ b/Common/src/main/java/at/petrak/hexcasting/datagen/recipe/HexplatRecipes.java @@ -19,6 +19,7 @@ import at.petrak.hexcasting.datagen.recipe.builders.BrainsweepRecipeBuilder; import at.petrak.hexcasting.datagen.recipe.builders.CreateCrushingRecipeBuilder; import at.petrak.hexcasting.datagen.recipe.builders.FarmersDelightCuttingRecipeBuilder; +import at.petrak.hexcasting.datagen.recipe.builders.FreezeRecipeBuilder; import at.petrak.paucal.api.PaucalAPI; import at.petrak.paucal.api.datagen.PaucalAdvancementSubProvider; import net.minecraft.advancements.CriteriaTriggers; @@ -469,6 +470,16 @@ public void buildRecipes(RecipeOutput recipes) { .requires(HexBlocks.AMETHYST_PILLAR) .unlockedBy("has_item", has(HexBlocks.SLATE)).save(recipes); + new FreezeRecipeBuilder(HexStateIngredients.of(Blocks.ICE), Blocks.PACKED_ICE.defaultBlockState()) + .unlockedBy("has_item", hasItem(HexTags.Items.STAVES)) + .save(recipes, modLoc("freeze/packed_ice")); + new FreezeRecipeBuilder(HexStateIngredients.of(Blocks.PACKED_ICE), Blocks.BLUE_ICE.defaultBlockState()) + .unlockedBy("has_item", hasItem(HexTags.Items.STAVES)) + .save(recipes, modLoc("freeze/blue_ice")); + new FreezeRecipeBuilder(HexStateIngredients.of(Blocks.WATER_CAULDRON), Blocks.POWDER_SNOW_CAULDRON.defaultBlockState()) + .unlockedBy("has_item", hasItem(HexTags.Items.STAVES)) + .save(recipes, modLoc("freeze/powder_snow_cauldron")); + new BrainsweepRecipeBuilder(HexStateIngredients.of(Blocks.AMETHYST_BLOCK), new VillagerIngredient(null, null, 3), Blocks.BUDDING_AMETHYST.defaultBlockState(), MediaConstants.CRYSTAL_UNIT * 10) diff --git a/Common/src/main/java/at/petrak/hexcasting/datagen/recipe/builders/FreezeRecipeBuilder.java b/Common/src/main/java/at/petrak/hexcasting/datagen/recipe/builders/FreezeRecipeBuilder.java new file mode 100644 index 000000000..4193f007b --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/datagen/recipe/builders/FreezeRecipeBuilder.java @@ -0,0 +1,58 @@ +package at.petrak.hexcasting.datagen.recipe.builders; + +import at.petrak.hexcasting.common.recipe.FreezeRecipe; +import at.petrak.hexcasting.common.recipe.ingredient.state.StateIngredient; +import net.minecraft.advancements.Advancement; +import net.minecraft.advancements.AdvancementRequirements; +import net.minecraft.advancements.AdvancementRewards; +import net.minecraft.advancements.Criterion; +import net.minecraft.advancements.critereon.RecipeUnlockedTrigger; +import net.minecraft.data.recipes.RecipeBuilder; +import net.minecraft.data.recipes.RecipeOutput; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class FreezeRecipeBuilder implements RecipeBuilder { + private final StateIngredient blockIn; + private final BlockState result; + + private final Map> criteria = new LinkedHashMap<>(); + + public FreezeRecipeBuilder(StateIngredient blockIn, BlockState result) { + this.blockIn = blockIn; + this.result = result; + } + + @Override + public RecipeBuilder unlockedBy(String pCriterionName, Criterion pCriterionTrigger) { + criteria.put(pCriterionName, pCriterionTrigger); + return this; + } + + @Override + public RecipeBuilder group(@Nullable String pGroupName) { + return this; + } + + @Override + public Item getResult() { + return this.result.getBlock().asItem(); + } + + @Override + public void save(RecipeOutput recipeOutput, ResourceLocation id) { + Advancement.Builder advancement = recipeOutput.advancement() + .addCriterion("has_the_recipe", RecipeUnlockedTrigger.unlocked(id)) + .rewards(AdvancementRewards.Builder.recipe(id)) + .requirements(AdvancementRequirements.Strategy.OR); + this.criteria.forEach(advancement::addCriterion); + + var recipe = new FreezeRecipe(blockIn, result); + recipeOutput.accept(id.withPrefix("freeze/"), recipe, advancement.build(id.withPrefix("recipes/freeze/"))); + } +} diff --git a/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json new file mode 100644 index 000000000..db092ef28 --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:freeze/blue_ice" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:freeze/blue_ice" + ] + } +} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json new file mode 100644 index 000000000..ccded8855 --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:freeze/packed_ice" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:freeze/packed_ice" + ] + } +} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json new file mode 100644 index 000000000..1e49ebee8 --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:freeze/powder_snow_cauldron" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:freeze/powder_snow_cauldron" + ] + } +} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/freeze/blue_ice.json b/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/freeze/blue_ice.json new file mode 100644 index 000000000..8106c7878 --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/freeze/blue_ice.json @@ -0,0 +1,10 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:block", + "block": "minecraft:packed_ice" + }, + "result": { + "Name": "minecraft:blue_ice" + } +} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/freeze/packed_ice.json b/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/freeze/packed_ice.json new file mode 100644 index 000000000..cc60d0637 --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/freeze/packed_ice.json @@ -0,0 +1,10 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:block", + "block": "minecraft:ice" + }, + "result": { + "Name": "minecraft:packed_ice" + } +} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/freeze/powder_snow_cauldron.json b/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/freeze/powder_snow_cauldron.json new file mode 100644 index 000000000..da6f3a7d3 --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/freeze/powder_snow_cauldron.json @@ -0,0 +1,13 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:block", + "block": "minecraft:water_cauldron" + }, + "result": { + "Name": "minecraft:powder_snow_cauldron", + "Properties": { + "level": "1" + } + } +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json new file mode 100644 index 000000000..db092ef28 --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:freeze/blue_ice" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:freeze/blue_ice" + ] + } +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json new file mode 100644 index 000000000..ccded8855 --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:freeze/packed_ice" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:freeze/packed_ice" + ] + } +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json new file mode 100644 index 000000000..1e49ebee8 --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:freeze/powder_snow_cauldron" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:freeze/powder_snow_cauldron" + ] + } +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/freeze/blue_ice.json b/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/freeze/blue_ice.json new file mode 100644 index 000000000..8106c7878 --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/freeze/blue_ice.json @@ -0,0 +1,10 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:block", + "block": "minecraft:packed_ice" + }, + "result": { + "Name": "minecraft:blue_ice" + } +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/freeze/packed_ice.json b/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/freeze/packed_ice.json new file mode 100644 index 000000000..cc60d0637 --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/freeze/packed_ice.json @@ -0,0 +1,10 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:block", + "block": "minecraft:ice" + }, + "result": { + "Name": "minecraft:packed_ice" + } +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/freeze/powder_snow_cauldron.json b/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/freeze/powder_snow_cauldron.json new file mode 100644 index 000000000..da6f3a7d3 --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/freeze/powder_snow_cauldron.json @@ -0,0 +1,13 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:block", + "block": "minecraft:water_cauldron" + }, + "result": { + "Name": "minecraft:powder_snow_cauldron", + "Properties": { + "level": "1" + } + } +} \ No newline at end of file From 346999b0db65544183b55a8348feb5a7bf008837 Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Tue, 23 Jun 2026 22:17:53 -0400 Subject: [PATCH 03/16] Implement smelt --- .../common/casting/actions/spells/OpSmelt.kt | 104 ++++++++++++++++++ .../hexcasting/common/lib/hex/HexActions.java | 2 + 2 files changed, 106 insertions(+) create mode 100644 Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpSmelt.kt diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpSmelt.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpSmelt.kt new file mode 100644 index 000000000..41b406a49 --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpSmelt.kt @@ -0,0 +1,104 @@ +package at.petrak.hexcasting.common.casting.actions.spells + +import at.petrak.hexcasting.api.casting.* +import at.petrak.hexcasting.api.casting.castables.SpellAction +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment +import at.petrak.hexcasting.api.casting.iota.EntityIota +import at.petrak.hexcasting.api.casting.iota.Iota +import at.petrak.hexcasting.api.casting.iota.Vec3Iota +import at.petrak.hexcasting.api.casting.mishaps.MishapInvalidIota +import at.petrak.hexcasting.api.misc.MediaConstants +import at.petrak.hexcasting.xplat.IXplatAbstractions +import net.minecraft.core.BlockPos +import net.minecraft.server.level.ServerPlayer +import net.minecraft.world.entity.Entity +import net.minecraft.world.entity.item.ItemEntity +import net.minecraft.world.item.BlockItem +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.crafting.RecipeHolder +import net.minecraft.world.item.crafting.RecipeType +import net.minecraft.world.item.crafting.SingleRecipeInput +import net.minecraft.world.item.crafting.SmeltingRecipe +import net.minecraft.world.phys.Vec3 +import java.util.* +import kotlin.math.roundToLong + +object OpSmelt : SpellAction { + override val argc = 1 + + override fun execute(args: List, env: CastingEnvironment): SpellAction.Result { + when (val target = args[0]) { + is EntityIota -> { + val itemEntity = args.getItemEntity(env.world, 0, argc) + env.assertEntityInRange(itemEntity) + return SpellAction.Result( + ItemSpell(itemEntity), + (itemEntity.item.count * 0.75 * MediaConstants.DUST_UNIT).roundToLong(), + listOf(ParticleSpray.burst(itemEntity.position(), 1.0)) + ) + } + is Vec3Iota -> { + val pos = args.getBlockPos(0, argc) + env.assertPosInRangeForEditing(pos) + return SpellAction.Result( + BlockSpell(pos), + (0.75 * MediaConstants.DUST_UNIT).roundToLong(), + listOf(ParticleSpray.burst(Vec3.atCenterOf(pos), 1.0)) + ) + } + else -> throw MishapInvalidIota.ofType(target, 0, "entity_or_vector") + } + } + + fun smeltResult(item: Item, env: CastingEnvironment): ItemStack? { + val optional: Optional> = env.world.recipeManager.getRecipeFor( + RecipeType.SMELTING, SingleRecipeInput(ItemStack(item, 1)), + env.world + ) + + if (!optional.isPresent) return null + + val result = optional.get().value.getResultItem(env.world.registryAccess()).copy() + + if (result.isEmpty) return null + + return result + } + + private data class ItemSpell(val itemEntity: ItemEntity) : RenderedSpell { + override fun cast(env: CastingEnvironment) { + val result = smeltResult(itemEntity.item.item, env) ?: return // cursed .item.item to map from ItemEntity to ItemLike to ItemStack + + result.count *= itemEntity.item.count + + env.world.addFreshEntity(ItemEntity(env.world, itemEntity.x, itemEntity.y, itemEntity.z, result.copy())) + itemEntity.remove(Entity.RemovalReason.DISCARDED) + } + } + + private data class BlockSpell(val pos: BlockPos) : RenderedSpell { + override fun cast(env: CastingEnvironment) { + if (!env.canEditBlockAt(pos)) return + val blockState = env.world.getBlockState(pos) + if (!IXplatAbstractions.INSTANCE.isBreakingAllowed(env.world, pos, blockState, env.castingEntity as? ServerPlayer)) return + + val itemStack = smeltResult(blockState.block.asItem(), env) ?: return + + if (itemStack.item is BlockItem) { + env.world.setBlockAndUpdate(pos, (itemStack.item as BlockItem).block.defaultBlockState()) + + if (itemStack.count > 1) { + itemStack.count -= 1 + env.world.addFreshEntity(ItemEntity(env.world, pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(), itemStack.copy())) + } + } else { + env.world.destroyBlock(pos, false, env.caster) + env.world.addFreshEntity(ItemEntity(env.world, pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(), itemStack.copy())) + // Send a block update, also copied from Ars Nouveau (this is all copied from Ars Nouveau) + if (!env.world.isOutsideBuildHeight(pos)) + env.world.sendBlockUpdated(pos, env.world.getBlockState(pos), env.world.getBlockState(pos), 3) // don't know how this works + } + } + } +} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java index 33158c6fc..108df01ca 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java @@ -278,6 +278,8 @@ public class HexActions { new ActionRegistryEntry(HexPattern.fromAngles("aaqawawa", HexDir.SOUTH_EAST), OpIgnite.INSTANCE)); public static final ActionRegistryEntry EXTINGUISH = make("extinguish", new ActionRegistryEntry(HexPattern.fromAngles("ddedwdwd", HexDir.SOUTH_WEST), OpExtinguish.INSTANCE)); + public static final ActionRegistryEntry SMELT = make("smelt", + new ActionRegistryEntry(HexPattern.fromAngles("wqqqwqqadad", HexDir.EAST), OpSmelt.INSTANCE)); public static final ActionRegistryEntry FREEZE = make("freeze", new ActionRegistryEntry(HexPattern.fromAngles("weeeweedada", HexDir.WEST), OpFreeze.INSTANCE)); From 4414300215a66e703d08a6da034faf70a1eb89f0 Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Tue, 23 Jun 2026 23:10:10 -0400 Subject: [PATCH 04/16] Implement particles --- .../hexcasting/api/casting/ActionUtils.kt | 19 ++++++ .../petrak/hexcasting/api/utils/HexUtils.kt | 13 ++++ .../casting/actions/spells/OpParticles.kt | 62 +++++++++++++++++++ .../hexcasting/common/lib/hex/HexActions.java | 2 + .../common/msgs/MsgParticleLinesS2C.java | 56 +++++++++++++++++ .../common/msgs/MsgSingleParticleS2C.java | 54 ++++++++++++++++ .../fabric/network/FabricPacketHandler.java | 2 + .../forge/network/ForgePacketHandler.java | 4 ++ 8 files changed, 212 insertions(+) create mode 100644 Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpParticles.kt create mode 100644 Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgParticleLinesS2C.java create mode 100644 Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgSingleParticleS2C.java diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt index d67e0693b..9331605dc 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt @@ -271,6 +271,25 @@ fun List.getNumOrVec(idx: Int, argc: Int = 0): Either { } } +fun List.getVecOrVecList(idx: Int, argc: Int = 0): Either> { + val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) } + return when (x) { + is Vec3Iota -> Either.left(x.vec3) + is ListIota -> { + val out = mutableListOf() + for (v in x.list) { + if (v is Vec3Iota) { + out.add(v.vec3) + } else { + throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "veclist") + } + } + Either.right(out) + } + else -> throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "veclist") + } +} + fun List.getLongOrList(idx: Int, argc: Int = 0): Either { val datum = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) } if (datum is DoubleIota) { diff --git a/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt b/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt index aa4999873..776390a8e 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt @@ -8,6 +8,7 @@ import at.petrak.hexcasting.api.casting.iota.ListIota import at.petrak.hexcasting.api.casting.iota.NullIota import at.petrak.hexcasting.api.casting.math.HexCoord import at.petrak.hexcasting.api.casting.validateSubIotas +import at.petrak.hexcasting.api.pigment.FrozenPigment import net.minecraft.ChatFormatting import net.minecraft.core.HolderLookup import net.minecraft.core.Registry @@ -19,6 +20,7 @@ import net.minecraft.resources.ResourceKey import net.minecraft.resources.ResourceLocation import net.minecraft.server.level.ServerLevel import net.minecraft.tags.TagKey +import net.minecraft.util.RandomSource import net.minecraft.world.InteractionHand import net.minecraft.world.item.ItemStack import net.minecraft.world.phys.Vec2 @@ -64,6 +66,17 @@ fun vec2FromNBT(tag: LongArray): Vec2 = if (tag.size != 2) Vec2.ZERO else Double.fromBits(tag[1]).toFloat(), ) +fun FrozenPigment.nextColor(random: RandomSource): Int { + return colorProvider.getColor( + random.nextFloat() * 16384, + Vec3( + random.nextFloat().toDouble(), + random.nextFloat().toDouble(), + random.nextFloat().toDouble() + ).scale((random.nextFloat() * 3).toDouble()) + ) +} + fun otherHand(hand: InteractionHand) = if (hand == InteractionHand.MAIN_HAND) InteractionHand.OFF_HAND else InteractionHand.MAIN_HAND diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpParticles.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpParticles.kt new file mode 100644 index 000000000..9c79de3a3 --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpParticles.kt @@ -0,0 +1,62 @@ +package at.petrak.hexcasting.common.casting.actions.spells + +import at.petrak.hexcasting.api.casting.RenderedSpell +import at.petrak.hexcasting.api.casting.castables.SpellAction +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment +import at.petrak.hexcasting.api.casting.getVecOrVecList +import at.petrak.hexcasting.api.casting.iota.Iota +import at.petrak.hexcasting.api.casting.mishaps.MishapBadLocation +import at.petrak.hexcasting.api.misc.MediaConstants +import at.petrak.hexcasting.common.msgs.MsgParticleLinesS2C +import at.petrak.hexcasting.common.msgs.MsgSingleParticleS2C +import at.petrak.hexcasting.xplat.IXplatAbstractions +import com.mojang.datafixers.util.Either +import net.minecraft.server.level.ServerPlayer +import net.minecraft.world.phys.Vec3 +import kotlin.math.roundToLong + +object OpParticles : SpellAction { + override val argc = 1 + + override fun execute(args: List, env: CastingEnvironment): SpellAction.Result { + val loc = args.getVecOrVecList(0, argc) + + // assert all locs in ambit. + loc.map({ env.assertVecInRange(it) }, { assertVecListInRange(env, it, 32.0) }) + + return SpellAction.Result( + Spell(loc), + loc.map({ 0.002 * MediaConstants.DUST_UNIT }, { it.size * 0.002 * MediaConstants.DUST_UNIT }).roundToLong(), + listOf() + ) + } + + fun assertVecListInRange(env: CastingEnvironment, list: List, intraRange: Double) { + for (vec in list) { + env.assertVecInRange(vec) + } + + val sqrRange = intraRange * intraRange + + for (i in list.indices) { + for (j in i until list.size) { + if (list[i].distanceToSqr(list[j]) > sqrRange) throw MishapBadLocation(list[j]) + } + } + } + + data class Spell(val loc: Either>) : RenderedSpell { + override fun cast(env: CastingEnvironment) { + val colouriser = IXplatAbstractions.INSTANCE.getPigment(env.castingEntity as? ServerPlayer) + + loc.map({ + IXplatAbstractions.INSTANCE.sendPacketNear(it, 128.0, env.world, MsgSingleParticleS2C(it, colouriser)) + }, { + if (it.isNotEmpty()) { + val first = it[0] + IXplatAbstractions.INSTANCE.sendPacketNear(first, 128.0, env.world, MsgParticleLinesS2C(it, colouriser)) + } + }) + } + } +} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java index 108df01ca..e7460a172 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java @@ -285,6 +285,8 @@ public class HexActions { public static final ActionRegistryEntry BEEP = make("beep", new ActionRegistryEntry(HexPattern.fromAngles("adaa", HexDir.WEST), OpBeep.INSTANCE)); + public static final ActionRegistryEntry PARTICLES = make("particles", + new ActionRegistryEntry(HexPattern.fromAngles("eqqqqa", HexDir.NORTH_EAST), OpParticles.INSTANCE)); public static final ActionRegistryEntry CRAFT$CYPHER = make("craft/cypher", new ActionRegistryEntry( HexPattern.fromAngles("waqqqqq", HexDir.EAST), diff --git a/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgParticleLinesS2C.java b/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgParticleLinesS2C.java new file mode 100644 index 000000000..0cc1e19ea --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgParticleLinesS2C.java @@ -0,0 +1,56 @@ +package at.petrak.hexcasting.common.msgs; + +import at.petrak.hexcasting.api.HexAPI; +import at.petrak.hexcasting.api.pigment.FrozenPigment; +import at.petrak.hexcasting.api.utils.HexUtils; +import at.petrak.hexcasting.common.particles.ConjureParticleOptions; +import at.petrak.paucal.api.PaucalCodecs; +import net.minecraft.client.Minecraft; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.world.phys.Vec3; + +import java.util.List; + +public record MsgParticleLinesS2C(List locs, FrozenPigment colorizer) implements CustomPacketPayload { + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(HexAPI.modLoc("prtcl_l")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + PaucalCodecs.VEC3.apply(ByteBufCodecs.list()), MsgParticleLinesS2C::locs, + FrozenPigment.STREAM_CODEC, MsgParticleLinesS2C::colorizer, + MsgParticleLinesS2C::new + ); + + @Override + public Type type() { + return TYPE; + } + + public void handle() { + MsgParticleLinesS2C.Handler.handle(this); + } + + public static final class Handler { + + public static void handle(MsgParticleLinesS2C msg) { + Minecraft.getInstance().execute(() -> { + var level = Minecraft.getInstance().level; + if (level == null) return; + + for (int i = 0; i < msg.locs.size()-1; ++i) { + Vec3 start = msg.locs.get(i); + Vec3 end = msg.locs.get(i+1); + int steps = (int) (end.subtract(start).length() * 10); + for (int j = 0; j <= steps; ++j) { + Vec3 pos = start.add(end.subtract(start).scale((double) j / steps)); + var color = HexUtils.nextColor(msg.colorizer, level.random); + level.addParticle(new ConjureParticleOptions(color), + pos.x, pos.y, pos.z, 0.0, 0.0, 0.0); + } + } + }); + } + } +} diff --git a/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgSingleParticleS2C.java b/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgSingleParticleS2C.java new file mode 100644 index 000000000..0c03aa92b --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgSingleParticleS2C.java @@ -0,0 +1,54 @@ +package at.petrak.hexcasting.common.msgs; + +import at.petrak.hexcasting.api.HexAPI; +import at.petrak.hexcasting.api.utils.HexUtils; +import at.petrak.hexcasting.api.pigment.FrozenPigment; +import at.petrak.hexcasting.common.particles.ConjureParticleOptions; +import at.petrak.paucal.api.PaucalCodecs; +import net.minecraft.client.Minecraft; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.world.phys.Vec3; + +import java.util.Random; + +public record MsgSingleParticleS2C(Vec3 pos, FrozenPigment colorizer) implements CustomPacketPayload { + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(HexAPI.modLoc("prtcl_s")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + PaucalCodecs.VEC3, MsgSingleParticleS2C::pos, + FrozenPigment.STREAM_CODEC, MsgSingleParticleS2C::colorizer, + MsgSingleParticleS2C::new + ); + + @Override + public Type type() { + return TYPE; + } + + public void handle() { + MsgSingleParticleS2C.Handler.handle(this); + } + + public static final class Handler { + + public static void handle(MsgSingleParticleS2C msg) { + Minecraft.getInstance().execute(() -> { + var level = Minecraft.getInstance().level; + if (level == null) return; + var color = HexUtils.nextColor(msg.colorizer, level.random); + level.addParticle(new ConjureParticleOptions(color), + msg.pos.x, msg.pos.y, msg.pos.z, 0.0, 0.0, 0.0); + for (int i = 0; i <= 10; i++) { + color = HexUtils.nextColor(msg.colorizer, level.random); + double offsetX = level.random.nextFloat() * 0.1 - 0.05; + double offsetY = level.random.nextFloat() * 0.1 - 0.05; + double offsetZ = level.random.nextFloat() * 0.1 - 0.05; + level.addParticle(new ConjureParticleOptions(color), + msg.pos.x + offsetX, msg.pos.y + offsetY, msg.pos.z + offsetZ, 0.0, 0.0, 0.0); + } + }); + } + } +} diff --git a/Fabric/src/main/java/at/petrak/hexcasting/fabric/network/FabricPacketHandler.java b/Fabric/src/main/java/at/petrak/hexcasting/fabric/network/FabricPacketHandler.java index c7e2346e9..d6d866f34 100644 --- a/Fabric/src/main/java/at/petrak/hexcasting/fabric/network/FabricPacketHandler.java +++ b/Fabric/src/main/java/at/petrak/hexcasting/fabric/network/FabricPacketHandler.java @@ -24,6 +24,8 @@ public static void initPackets() { PayloadTypeRegistry.playS2C().register(MsgNewWallScrollS2C.TYPE, MsgNewWallScrollS2C.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(MsgRecalcWallScrollDisplayS2C.TYPE, MsgRecalcWallScrollDisplayS2C.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(MsgNewSpiralPatternsS2C.TYPE, MsgNewSpiralPatternsS2C.STREAM_CODEC); + PayloadTypeRegistry.playS2C().register(MsgSingleParticleS2C.TYPE, MsgSingleParticleS2C.STREAM_CODEC); + PayloadTypeRegistry.playS2C().register(MsgParticleLinesS2C.TYPE, MsgParticleLinesS2C.STREAM_CODEC); } public static void init() { diff --git a/Neoforge/src/main/java/at/petrak/hexcasting/forge/network/ForgePacketHandler.java b/Neoforge/src/main/java/at/petrak/hexcasting/forge/network/ForgePacketHandler.java index c53a796a4..66e9b58b6 100644 --- a/Neoforge/src/main/java/at/petrak/hexcasting/forge/network/ForgePacketHandler.java +++ b/Neoforge/src/main/java/at/petrak/hexcasting/forge/network/ForgePacketHandler.java @@ -49,6 +49,10 @@ public static void init(IEventBus modBus) { makeClientBoundHandler(MsgNewSpiralPatternsS2C::handle)); registar.playToClient(MsgClearSpiralPatternsS2C.TYPE, MsgClearSpiralPatternsS2C.STREAM_CODEC, makeClientBoundHandler(MsgClearSpiralPatternsS2C::handle)); + registar.playToClient(MsgSingleParticleS2C.TYPE, MsgSingleParticleS2C.STREAM_CODEC, + makeClientBoundHandler(MsgSingleParticleS2C::handle)); + registar.playToClient(MsgParticleLinesS2C.TYPE, MsgParticleLinesS2C.STREAM_CODEC, + makeClientBoundHandler(MsgParticleLinesS2C::handle)); }); } From b9399f1aed9e2f1c30a23e3ece98f05beab30e8c Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Wed, 24 Jun 2026 01:01:50 -0400 Subject: [PATCH 05/16] Implement factorial --- .../api/casting/arithmetic/Arithmetic.java | 1 + .../casting/arithmetic/DoubleArithmetic.kt | 5 +- .../casting/arithmetic/Vec3Arithmetic.java | 6 +- .../arithmetic/operator/OperatorFactorial.kt | 60 +++++++++++++++++++ .../hexcasting/common/lib/hex/HexActions.java | 2 + 5 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/operator/OperatorFactorial.kt diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/arithmetic/Arithmetic.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/arithmetic/Arithmetic.java index 10f9f9cd6..cecd0cc8f 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/arithmetic/Arithmetic.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/arithmetic/Arithmetic.java @@ -41,6 +41,7 @@ public interface Arithmetic { HexPattern ARCTAN2 = HexPattern.fromAngles("deadeeeeewd", HexDir.WEST); HexPattern LOG = HexPattern.fromAngles("eqaqe", HexDir.NORTH_WEST); HexPattern MOD = HexPattern.fromAngles("addwaad", HexDir.NORTH_EAST); + HexPattern FACT = HexPattern.fromAngles("wawdedwaw", HexDir.SOUTH_EAST); // Vecs diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/DoubleArithmetic.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/DoubleArithmetic.kt index 075a17a76..9aadf9295 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/DoubleArithmetic.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/DoubleArithmetic.kt @@ -12,6 +12,7 @@ import at.petrak.hexcasting.api.casting.iota.DoubleIota import at.petrak.hexcasting.api.casting.iota.Iota import at.petrak.hexcasting.api.casting.math.HexPattern import at.petrak.hexcasting.api.casting.mishaps.MishapDivideByZero +import at.petrak.hexcasting.common.casting.arithmetic.operator.OperatorFactorial import at.petrak.hexcasting.common.casting.arithmetic.operator.OperatorLog import at.petrak.hexcasting.common.casting.arithmetic.operator.asDoubleBetween import at.petrak.hexcasting.common.lib.hex.HexIotaTypes @@ -38,7 +39,8 @@ object DoubleArithmetic : Arithmetic { ARCTAN, ARCTAN2, LOG, - MOD + MOD, + FACT ) /** @@ -70,6 +72,7 @@ object DoubleArithmetic : Arithmetic { ARCTAN2 -> make2 { a, b -> atan2(a, b) } LOG -> OperatorLog MOD -> make2 { a, b -> if (b == 0.0) throw MishapDivideByZero.of(a, b) else a % b } + FACT -> OperatorFactorial else -> throw InvalidOperatorException("$pattern is not a valid operator in Arithmetic $this.") } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/Vec3Arithmetic.java b/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/Vec3Arithmetic.java index a1d0af1ce..ab0ca37f1 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/Vec3Arithmetic.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/Vec3Arithmetic.java @@ -9,6 +9,7 @@ import at.petrak.hexcasting.api.casting.iota.DoubleIota; import at.petrak.hexcasting.api.casting.iota.Vec3Iota; import at.petrak.hexcasting.api.casting.math.HexPattern; +import at.petrak.hexcasting.common.casting.arithmetic.operator.OperatorFactorial; import at.petrak.hexcasting.common.casting.arithmetic.operator.vec.OperatorPack; import at.petrak.hexcasting.common.casting.arithmetic.operator.vec.OperatorUnpack; import at.petrak.hexcasting.common.casting.arithmetic.operator.vec.OperatorVec3Delegating; @@ -36,7 +37,8 @@ public enum Vec3Arithmetic implements Arithmetic { POW, FLOOR, CEIL, - MOD + MOD, + FACT ); public static final IotaMultiPredicate ACCEPTS = IotaMultiPredicate.any(IotaPredicate.ofType(VEC3), IotaPredicate.ofType(DOUBLE)); @@ -75,6 +77,8 @@ public Operator getOperator(HexPattern pattern) { return make1(v -> new Vec3(Math.ceil(v.x), Math.ceil(v.y), Math.ceil(v.z))); } else if (pattern.equals(MOD)) { return make2Fallback(pattern); + } else if (pattern.equals(FACT)) { + return OperatorFactorial.INSTANCE; } throw new InvalidOperatorException(pattern + " is not a valid operator in Arithmetic " + this + "."); } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/operator/OperatorFactorial.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/operator/OperatorFactorial.kt new file mode 100644 index 000000000..5c336e725 --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/operator/OperatorFactorial.kt @@ -0,0 +1,60 @@ +package at.petrak.hexcasting.common.casting.arithmetic.operator + +import at.petrak.hexcasting.api.casting.arithmetic.operator.OperatorBasic +import at.petrak.hexcasting.api.casting.arithmetic.predicates.IotaMultiPredicate +import at.petrak.hexcasting.api.casting.arithmetic.predicates.IotaPredicate +import at.petrak.hexcasting.api.casting.asActionResult +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment +import at.petrak.hexcasting.api.casting.iota.DoubleIota +import at.petrak.hexcasting.api.casting.iota.Iota +import at.petrak.hexcasting.api.casting.iota.Vec3Iota +import at.petrak.hexcasting.api.casting.mishaps.MishapInvalidIota +import at.petrak.hexcasting.common.lib.hex.HexIotaTypes.DOUBLE +import at.petrak.hexcasting.common.lib.hex.HexIotaTypes.VEC3 +import net.minecraft.world.phys.Vec3 +import kotlin.math.PI +import kotlin.math.exp +import kotlin.math.ln +import kotlin.math.roundToInt +import kotlin.math.sqrt + +object OperatorFactorial : OperatorBasic(1, IotaMultiPredicate.all(IotaPredicate.or( + IotaPredicate.ofType(DOUBLE), IotaPredicate.ofType(VEC3) +))) { + override fun apply(iotas: Iterable, env : CastingEnvironment): Iterable { + val iota = iotas.first() + return when (iota) { + is DoubleIota -> compute(iota.double).asActionResult + is Vec3Iota -> Vec3(compute(iota.vec3.x), compute(iota.vec3.y), compute(iota.vec3.z)).asActionResult + else -> throw MishapInvalidIota.of(iota, 0, "numvec") + } + } + + // Take the standard factorial if it's an integer, otherwise use the gamma function. + private fun compute(arg: Double): Double { + val argInt = arg.roundToInt() + if (arg >= 0 && DoubleIota.tolerates(arg, argInt.toDouble())) { + return factorial(argInt) + } + return exp(logGamma(arg + 1)) + } + + private fun factorial(number: Int): Double { + var result: Long = 1 + + for (factor in 2..number) { + result *= factor + } + + return result.toDouble() + } + + // https://introcs.cs.princeton.edu/java/91float/Gamma.java.html + private fun logGamma(x: Double): Double { + val ser = ( 1.0 + 76.18009173 / (x + 0) - 86.50532033 / (x + 1) + + 24.01409822 / (x + 2) - 1.231739516 / (x + 3) + + 0.00120858003 / (x + 4) - 0.00000536382 / (x + 5) ) + + return (x - 0.5) * ln(x + 4.5) - (x + 4.5) + ln(ser * sqrt(2*PI)) + } +} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java index e7460a172..7b796bad0 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java @@ -223,6 +223,8 @@ public class HexActions { new OperationAction(HexPattern.fromAngles("eqaqe", HexDir.NORTH_WEST))); public static final ActionRegistryEntry MODULO = make("modulo", new OperationAction(HexPattern.fromAngles("addwaad", HexDir.NORTH_EAST))); + public static final ActionRegistryEntry FACTORIAL = make("factorial", + new OperationAction(HexPattern.fromAngles("wawdedwaw", HexDir.SOUTH_EAST))); // == Sets == From cb7be7374328f458719b04546b02b1e248fcc848 Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Mon, 29 Jun 2026 15:16:30 -0400 Subject: [PATCH 06/16] Better factorial implementation --- .../casting/arithmetic/DoubleArithmetic.kt | 4 +- .../casting/arithmetic/Vec3Arithmetic.java | 6 +- .../arithmetic/operator/OperatorFactorial.kt | 60 ------------------- .../arithmetic/operator/OperatorUtils.kt | 29 ++++++++- 4 files changed, 34 insertions(+), 65 deletions(-) delete mode 100644 Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/operator/OperatorFactorial.kt diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/DoubleArithmetic.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/DoubleArithmetic.kt index 9aadf9295..37f893871 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/DoubleArithmetic.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/DoubleArithmetic.kt @@ -12,9 +12,9 @@ import at.petrak.hexcasting.api.casting.iota.DoubleIota import at.petrak.hexcasting.api.casting.iota.Iota import at.petrak.hexcasting.api.casting.math.HexPattern import at.petrak.hexcasting.api.casting.mishaps.MishapDivideByZero -import at.petrak.hexcasting.common.casting.arithmetic.operator.OperatorFactorial import at.petrak.hexcasting.common.casting.arithmetic.operator.OperatorLog import at.petrak.hexcasting.common.casting.arithmetic.operator.asDoubleBetween +import at.petrak.hexcasting.common.casting.arithmetic.operator.generalFactorial import at.petrak.hexcasting.common.lib.hex.HexIotaTypes import java.util.function.DoubleBinaryOperator import java.util.function.DoubleUnaryOperator @@ -72,7 +72,7 @@ object DoubleArithmetic : Arithmetic { ARCTAN2 -> make2 { a, b -> atan2(a, b) } LOG -> OperatorLog MOD -> make2 { a, b -> if (b == 0.0) throw MishapDivideByZero.of(a, b) else a % b } - FACT -> OperatorFactorial + FACT -> make1 { a -> generalFactorial(a) } else -> throw InvalidOperatorException("$pattern is not a valid operator in Arithmetic $this.") } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/Vec3Arithmetic.java b/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/Vec3Arithmetic.java index ab0ca37f1..e06ddce67 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/Vec3Arithmetic.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/Vec3Arithmetic.java @@ -9,7 +9,7 @@ import at.petrak.hexcasting.api.casting.iota.DoubleIota; import at.petrak.hexcasting.api.casting.iota.Vec3Iota; import at.petrak.hexcasting.api.casting.math.HexPattern; -import at.petrak.hexcasting.common.casting.arithmetic.operator.OperatorFactorial; +import at.petrak.hexcasting.common.casting.arithmetic.operator.OperatorUtilsKt; import at.petrak.hexcasting.common.casting.arithmetic.operator.vec.OperatorPack; import at.petrak.hexcasting.common.casting.arithmetic.operator.vec.OperatorUnpack; import at.petrak.hexcasting.common.casting.arithmetic.operator.vec.OperatorVec3Delegating; @@ -78,7 +78,9 @@ public Operator getOperator(HexPattern pattern) { } else if (pattern.equals(MOD)) { return make2Fallback(pattern); } else if (pattern.equals(FACT)) { - return OperatorFactorial.INSTANCE; + return make1(v -> new Vec3(OperatorUtilsKt.generalFactorial(v.x), + OperatorUtilsKt.generalFactorial(v.y), + OperatorUtilsKt.generalFactorial(v.z))); } throw new InvalidOperatorException(pattern + " is not a valid operator in Arithmetic " + this + "."); } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/operator/OperatorFactorial.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/operator/OperatorFactorial.kt deleted file mode 100644 index 5c336e725..000000000 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/operator/OperatorFactorial.kt +++ /dev/null @@ -1,60 +0,0 @@ -package at.petrak.hexcasting.common.casting.arithmetic.operator - -import at.petrak.hexcasting.api.casting.arithmetic.operator.OperatorBasic -import at.petrak.hexcasting.api.casting.arithmetic.predicates.IotaMultiPredicate -import at.petrak.hexcasting.api.casting.arithmetic.predicates.IotaPredicate -import at.petrak.hexcasting.api.casting.asActionResult -import at.petrak.hexcasting.api.casting.eval.CastingEnvironment -import at.petrak.hexcasting.api.casting.iota.DoubleIota -import at.petrak.hexcasting.api.casting.iota.Iota -import at.petrak.hexcasting.api.casting.iota.Vec3Iota -import at.petrak.hexcasting.api.casting.mishaps.MishapInvalidIota -import at.petrak.hexcasting.common.lib.hex.HexIotaTypes.DOUBLE -import at.petrak.hexcasting.common.lib.hex.HexIotaTypes.VEC3 -import net.minecraft.world.phys.Vec3 -import kotlin.math.PI -import kotlin.math.exp -import kotlin.math.ln -import kotlin.math.roundToInt -import kotlin.math.sqrt - -object OperatorFactorial : OperatorBasic(1, IotaMultiPredicate.all(IotaPredicate.or( - IotaPredicate.ofType(DOUBLE), IotaPredicate.ofType(VEC3) -))) { - override fun apply(iotas: Iterable, env : CastingEnvironment): Iterable { - val iota = iotas.first() - return when (iota) { - is DoubleIota -> compute(iota.double).asActionResult - is Vec3Iota -> Vec3(compute(iota.vec3.x), compute(iota.vec3.y), compute(iota.vec3.z)).asActionResult - else -> throw MishapInvalidIota.of(iota, 0, "numvec") - } - } - - // Take the standard factorial if it's an integer, otherwise use the gamma function. - private fun compute(arg: Double): Double { - val argInt = arg.roundToInt() - if (arg >= 0 && DoubleIota.tolerates(arg, argInt.toDouble())) { - return factorial(argInt) - } - return exp(logGamma(arg + 1)) - } - - private fun factorial(number: Int): Double { - var result: Long = 1 - - for (factor in 2..number) { - result *= factor - } - - return result.toDouble() - } - - // https://introcs.cs.princeton.edu/java/91float/Gamma.java.html - private fun logGamma(x: Double): Double { - val ser = ( 1.0 + 76.18009173 / (x + 0) - 86.50532033 / (x + 1) - + 24.01409822 / (x + 2) - 1.231739516 / (x + 3) - + 0.00120858003 / (x + 4) - 0.00000536382 / (x + 5) ) - - return (x - 0.5) * ln(x + 4.5) - (x + 4.5) + ln(ser * sqrt(2*PI)) - } -} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/operator/OperatorUtils.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/operator/OperatorUtils.kt index 24247286c..cde87842e 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/operator/OperatorUtils.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/operator/OperatorUtils.kt @@ -5,8 +5,12 @@ import at.petrak.hexcasting.api.casting.iota.DoubleIota import at.petrak.hexcasting.api.casting.iota.Iota import at.petrak.hexcasting.api.casting.iota.ListIota import at.petrak.hexcasting.api.casting.mishaps.MishapInvalidIota +import kotlin.math.PI import kotlin.math.abs +import kotlin.math.exp +import kotlin.math.ln import kotlin.math.roundToInt +import kotlin.math.sqrt fun Iterator>.nextList(argc: Int = 0): SpellList { val (idx, x) = this.next() @@ -64,4 +68,27 @@ fun Iterator>.nextPositiveIntUnderInclusive(max: Int, argc: I * the double's index from the top of the stack (i.e. top iota has [idx]=0, second from the top has [idx]=1, etc.). */ fun Double.asDoubleBetween(min: Double, max: Double, idx: Int) = if (this in min .. max) this - else throw MishapInvalidIota.of(DoubleIota(this), idx, "double.between", min, max) \ No newline at end of file + else throw MishapInvalidIota.of(DoubleIota(this), idx, "double.between", min, max) + +/** + * Takes the standard factorial if `arg` is a positive integer, otherwise uses the gamma function. + */ +fun generalFactorial(arg: Double): Double { + val argInt = arg.roundToInt() + if (arg >= 0 && DoubleIota.tolerates(arg, argInt.toDouble())) { + // normal integer factorial + var result: Long = 1 + for (factor in 2..argInt) { + result *= factor + } + return result.toDouble() + } + // gamma function of arg+1 for non-integer factorial + // https://introcs.cs.princeton.edu/java/91float/Gamma.java.html + val x = arg + 1 + val ser = ( 1.0 + 76.18009173 / (x + 0) - 86.50532033 / (x + 1) + + 24.01409822 / (x + 2) - 1.231739516 / (x + 3) + + 0.00120858003 / (x + 4) - 0.00000536382 / (x + 5) ) + val logGamma = (x - 0.5) * ln(x + 4.5) - (x + 4.5) + ln(ser * sqrt(2*PI)) + return exp(logGamma) +} \ No newline at end of file From 81760576af8a2df687249a732548f7e9a62599f3 Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Tue, 30 Jun 2026 17:28:18 -0400 Subject: [PATCH 07/16] Action/mishap lang, tweak Freeze pattern --- .../java/at/petrak/hexcasting/api/casting/ActionUtils.kt | 4 ++-- .../at/petrak/hexcasting/common/lib/hex/HexActions.java | 2 +- .../resources/assets/hexcasting/lang/en_us.flatten.json5 | 8 +++++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt index 9331605dc..e1da55fed 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt @@ -281,12 +281,12 @@ fun List.getVecOrVecList(idx: Int, argc: Int = 0): Either if (v is Vec3Iota) { out.add(v.vec3) } else { - throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "veclist") + throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "vec_or_veclist") } } Either.right(out) } - else -> throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "veclist") + else -> throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "vec_or_veclist") } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java index 7b796bad0..4f267c5c8 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java @@ -283,7 +283,7 @@ public class HexActions { public static final ActionRegistryEntry SMELT = make("smelt", new ActionRegistryEntry(HexPattern.fromAngles("wqqqwqqadad", HexDir.EAST), OpSmelt.INSTANCE)); public static final ActionRegistryEntry FREEZE = make("freeze", - new ActionRegistryEntry(HexPattern.fromAngles("weeeweedada", HexDir.WEST), OpFreeze.INSTANCE)); + new ActionRegistryEntry(HexPattern.fromAngles("weeeweedada", HexDir.EAST), OpFreeze.INSTANCE)); public static final ActionRegistryEntry BEEP = make("beep", new ActionRegistryEntry(HexPattern.fromAngles("adaa", HexDir.WEST), OpBeep.INSTANCE)); diff --git a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 index 528f82646..18aeb3339 100644 --- a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 +++ b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 @@ -837,6 +837,7 @@ random: "Entropy Reflection", logarithm: "Logarithmic Distillation", coerce_axial: "Axial Purification", + factorial: "Factorial Purification", read: "Scribe's Reflection", "read/entity": "Chronicler's Purification", @@ -855,6 +856,7 @@ print: "Reveal", beep: "Make Note", + particles: "Particles", explode: "Explosion", "explode/fire": "Fireball", add_motion: "Impulse", @@ -873,10 +875,13 @@ destroy_water: "Destroy Liquid", ignite: "Ignite", extinguish: "Extinguish Area", + smelt: "Smelt", + freeze: "Freeze", conjure_block: "Conjure Block", conjure_light: "Conjure Light", bonemeal: "Overgrow", edify: "Edify Sapling", + falling_block: "Gravitate Block", colorize: "Internalize Pigment", cycle_variant: "Caster's Glamour", @@ -1106,7 +1111,7 @@ living: "a living entity", }, - entity_or_vector: "an entity or a vector", + entity_or_vector: "an entity or vector", unknown: "(unknown, uh-oh, this is a bug)", }, @@ -1114,6 +1119,7 @@ numvec: "a number or vector", numlist: "an integer or list", "list.pattern": "a list of patterns", + vec_or_veclist: "a vector or list of vectors", double: { positive: { From 82e3d6d47b8f5e78c9601f03f09ad4addf845af3 Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Tue, 30 Jun 2026 18:02:39 -0400 Subject: [PATCH 08/16] Book entries for new patterns --- .../hexcasting/api/casting/ActionUtils.kt | 4 +- .../hexcasting/lang/en_us.flatten.json5 | 10 +++++ .../en_us/entries/patterns/math.json | 8 ++++ .../en_us/entries/patterns/spells/basic.json | 8 ++++ .../entries/patterns/spells/blockworks.json | 14 ++----- .../entries/patterns/spells/heatworks.json | 42 +++++++++++++++++++ 6 files changed, 73 insertions(+), 13 deletions(-) create mode 100644 Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/spells/heatworks.json diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt index e1da55fed..266e5f791 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt @@ -281,12 +281,12 @@ fun List.getVecOrVecList(idx: Int, argc: Int = 0): Either if (v is Vec3Iota) { out.add(v.vec3) } else { - throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "vec_or_veclist") + throw MishapInvalidIota.of(x, if (argc == 0) idx else argc - (idx + 1), "vec_or_veclist") } } Either.right(out) } - else -> throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "vec_or_veclist") + else -> throw MishapInvalidIota.of(x, if (argc == 0) idx else argc - (idx + 1), "vec_or_veclist") } } diff --git a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 index 18aeb3339..3f72cc846 100644 --- a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 +++ b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 @@ -1324,6 +1324,7 @@ itempicking: "Working with Items", basic_spell: "Basic Spells", blockworks: "Block Manipulation", + heatworks: "Heat Manipulation", nadirs: "Nadirs", hexcasting_spell: "Crafting Casting Items", sentinels: "Sentinels", @@ -1825,6 +1826,7 @@ construct_vec: "Combine three numbers at the top of the stack into a vector's X, Y, and Z components (bottom to top).", deconstruct_vec: "Split a vector into its X, Y, and Z components (bottom to top).", modulo: "Takes the modulus of two numbers. This is the amount $(italics)remaining/$ after division - for example, 5 %% 2 is 1, and 5 %% 3 is 2. When applied on vectors, performs the above operation elementwise.", + factorial: "Takes the factorial of a number. This is the product of all integers from 1 to the number. For non-integers, uses the $(l:https://en.wikipedia.org/wiki/Gamma_function)gamma function/$. For vectors, performs the above operation elementwise.", coerce_axial: "For a vector, coerce it to its nearest axial direction, a unit vector. For a number, return the sign of the number; 1 if positive, -1 if negative. In both cases, zero is unaffected.", random: "Creates a random number between 0 and 1.", }, @@ -2053,6 +2055,8 @@ "beep.1": "Remove a vector and two numbers from the stack. Plays an $(thing)instrument/$ defined by the first number at the given location, with a $(thing)note/$ defined by the second number. Costs a negligible amount of _media.", "beep.2": "There appear to be 16 different $(thing)instruments/$ and 25 different $(thing)notes/$. Both are indexed by zero.$(br2)These seem to be the same instruments I can produce with a $(item)Note Block/$, though the reason for each instrument's number being what it is eludes me.$(br2)Either way, I can find the numbers I need to use by inspecting a $(item)Note Block/$ through a $(l:items/lens)$(item)Scrying Lens/$.", + + particles: "Accepts a location or a list of locations, and either creates a particle at the location, or creates lines of particles between the locations in the list. Costs two thousandths of an $(l:items/amethyst)$(item)Amethyst Dust/$ per location.", }, blockworks: { @@ -2064,8 +2068,14 @@ conjure_light: "Conjure a magical light that softly glows with my pigment at the given position. Costs about one $(l:items/amethyst)$(item)Amethyst Dust/$.", bonemeal: "Encourage a plant or sapling at the target position to grow, as if $(item)Bonemeal/$ was applied. Costs a bit more than one $(l:items/amethyst)$(item)Amethyst Dust/$.", edify: "Forcibly infuse _media into the sapling at the target position, causing it to grow into an $(l:items/edified)$(thing)Edified Tree/$. Costs about one $(l:items/amethyst)$(item)Charged Amethyst/$.", + falling_block: "Force the block at the target position to fall as if it were sand or gravel. Certain blocks seem mysteriously immune to this effect. Costs one and a half $(l:items/amethyst)$(item)Amethyst Dust/$.", + }, + + heatworks: { ignite: "Start a fire on top of the given location, as if a $(item)Fire Charge/$ was applied, or set fire to an entity. Costs about one $(l:items/amethyst)$(item)Amethyst Dust/$.", extinguish: "Extinguish blocks in a large area. Costs about six $(l:items/amethyst)$(item)Amethyst Dust/$.", + smelt: "Smelts either the block at the given location or all the items in the given item entity. Costs three quarters of an $(l:items/amethyst)$(item)Amethyst Dust/$ per block/item smelted.", + freeze: "Freezes the block at the given location. For example, turns water into ice, and ice into packed ice. Costs one $(l:items/amethyst)$(item)Amethyst Dust/$.", }, nadirs: { diff --git a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/math.json b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/math.json index 668875a54..86a6b1573 100644 --- a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/math.json +++ b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/math.json @@ -125,6 +125,14 @@ "output": "num|vec", "text": "hexcasting.page.math.modulo" }, + { + "type": "hexcasting:pattern", + "op_id": "hexcasting:factorial", + "anchor": "hexcasting:factorial", + "input": "num|vec", + "output": "num|vec", + "text": "hexcasting.page.math.factorial" + }, { "type": "hexcasting:pattern", "op_id": "hexcasting:coerce_axial", diff --git a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/spells/basic.json b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/spells/basic.json index 27b2c90e8..64937ec1f 100644 --- a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/spells/basic.json +++ b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/spells/basic.json @@ -57,6 +57,14 @@ { "type": "patchouli:text", "text": "hexcasting.page.basic_spell.beep.2" + }, + { + "type": "hexcasting:pattern", + "op_id": "hexcasting:particles", + "anchor": "hexcasting:particles", + "input": "vector | [vector]", + "output": "", + "text": "hexcasting.page.basic_spell.particles" } ] } diff --git a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/spells/blockworks.json b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/spells/blockworks.json index f39e871d4..eb2c5c27e 100644 --- a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/spells/blockworks.json +++ b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/spells/blockworks.json @@ -72,19 +72,11 @@ }, { "type": "hexcasting:pattern", - "op_id": "hexcasting:ignite", - "anchor": "hexcasting:ignite", - "input": "entity | vector", - "output": "", - "text": "hexcasting.page.blockworks.ignite" - }, - { - "type": "hexcasting:pattern", - "op_id": "hexcasting:extinguish", - "anchor": "hexcasting:extinguish", + "op_id": "hexcasting:falling_block", + "anchor": "hexcasting:falling_block", "input": "vector", "output": "", - "text": "hexcasting.page.blockworks.extinguish" + "text": "hexcasting.page.blockworks.falling_block" } ] } \ No newline at end of file diff --git a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/spells/heatworks.json b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/spells/heatworks.json new file mode 100644 index 000000000..b02adda11 --- /dev/null +++ b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/spells/heatworks.json @@ -0,0 +1,42 @@ +{ + "name": "hexcasting.entry.heatworks", + "category": "hexcasting:patterns/spells", + "icon": "minecraft:magma_block", + "sortnum": 1, + "advancement": "hexcasting:root", + "read_by_default": true, + "pages": [ + { + "type": "hexcasting:pattern", + "op_id": "hexcasting:ignite", + "anchor": "hexcasting:ignite", + "input": "vector | entity", + "output": "", + "text": "hexcasting.page.heatworks.ignite" + }, + { + "type": "hexcasting:pattern", + "op_id": "hexcasting:extinguish", + "anchor": "hexcasting:extinguish", + "input": "vector", + "output": "", + "text": "hexcasting.page.heatworks.extinguish" + }, + { + "type": "hexcasting:pattern", + "op_id": "hexcasting:smelt", + "anchor": "hexcasting:smelt", + "input": "vector | entity", + "output": "", + "text": "hexcasting.page.heatworks.smelt" + }, + { + "type": "hexcasting:pattern", + "op_id": "hexcasting:freeze", + "anchor": "hexcasting:freeze", + "input": "vector", + "output": "", + "text": "hexcasting.page.heatworks.freeze" + } + ] +} \ No newline at end of file From f21aaf16815f151d704122cbac41e16af70eb817 Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Wed, 8 Jul 2026 13:56:58 -0400 Subject: [PATCH 09/16] Properly cache ColorProvider in new particle msgs, refactor random color method --- .../hexcasting/api/pigment/ColorProvider.java | 9 +++++++++ .../java/at/petrak/hexcasting/api/utils/HexUtils.kt | 13 ------------- .../hexcasting/common/msgs/MsgParticleLinesS2C.java | 4 ++-- .../common/msgs/MsgSingleParticleS2C.java | 12 +++++------- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/pigment/ColorProvider.java b/Common/src/main/java/at/petrak/hexcasting/api/pigment/ColorProvider.java index 8c0611db2..1131473a2 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/pigment/ColorProvider.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/pigment/ColorProvider.java @@ -2,6 +2,7 @@ import at.petrak.hexcasting.api.addldata.ADPigment; import net.minecraft.util.FastColor; +import net.minecraft.util.RandomSource; import net.minecraft.world.phys.Vec3; public abstract class ColorProvider { @@ -41,6 +42,14 @@ public final int getColor(float time, Vec3 position) { return 0xff_000000 | (r << 16) | (g << 8) | b; } + public final int getRandomColor(RandomSource random) { + return getColor( + random.nextFloat() * 16384, + new Vec3(random.nextFloat(),random.nextFloat(),random.nextFloat()) + .scale((random.nextFloat() * 3)) + ); + } + public static final ColorProvider MISSING = new ColorProvider() { @Override protected int getRawColor(float time, Vec3 position) { diff --git a/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt b/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt index 776390a8e..aa4999873 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt @@ -8,7 +8,6 @@ import at.petrak.hexcasting.api.casting.iota.ListIota import at.petrak.hexcasting.api.casting.iota.NullIota import at.petrak.hexcasting.api.casting.math.HexCoord import at.petrak.hexcasting.api.casting.validateSubIotas -import at.petrak.hexcasting.api.pigment.FrozenPigment import net.minecraft.ChatFormatting import net.minecraft.core.HolderLookup import net.minecraft.core.Registry @@ -20,7 +19,6 @@ import net.minecraft.resources.ResourceKey import net.minecraft.resources.ResourceLocation import net.minecraft.server.level.ServerLevel import net.minecraft.tags.TagKey -import net.minecraft.util.RandomSource import net.minecraft.world.InteractionHand import net.minecraft.world.item.ItemStack import net.minecraft.world.phys.Vec2 @@ -66,17 +64,6 @@ fun vec2FromNBT(tag: LongArray): Vec2 = if (tag.size != 2) Vec2.ZERO else Double.fromBits(tag[1]).toFloat(), ) -fun FrozenPigment.nextColor(random: RandomSource): Int { - return colorProvider.getColor( - random.nextFloat() * 16384, - Vec3( - random.nextFloat().toDouble(), - random.nextFloat().toDouble(), - random.nextFloat().toDouble() - ).scale((random.nextFloat() * 3).toDouble()) - ) -} - fun otherHand(hand: InteractionHand) = if (hand == InteractionHand.MAIN_HAND) InteractionHand.OFF_HAND else InteractionHand.MAIN_HAND diff --git a/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgParticleLinesS2C.java b/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgParticleLinesS2C.java index 0cc1e19ea..b3a89f38a 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgParticleLinesS2C.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgParticleLinesS2C.java @@ -2,7 +2,6 @@ import at.petrak.hexcasting.api.HexAPI; import at.petrak.hexcasting.api.pigment.FrozenPigment; -import at.petrak.hexcasting.api.utils.HexUtils; import at.petrak.hexcasting.common.particles.ConjureParticleOptions; import at.petrak.paucal.api.PaucalCodecs; import net.minecraft.client.Minecraft; @@ -38,6 +37,7 @@ public static void handle(MsgParticleLinesS2C msg) { Minecraft.getInstance().execute(() -> { var level = Minecraft.getInstance().level; if (level == null) return; + var colProvider = msg.colorizer().getColorProvider(); for (int i = 0; i < msg.locs.size()-1; ++i) { Vec3 start = msg.locs.get(i); @@ -45,7 +45,7 @@ public static void handle(MsgParticleLinesS2C msg) { int steps = (int) (end.subtract(start).length() * 10); for (int j = 0; j <= steps; ++j) { Vec3 pos = start.add(end.subtract(start).scale((double) j / steps)); - var color = HexUtils.nextColor(msg.colorizer, level.random); + var color = colProvider.getRandomColor(level.random); level.addParticle(new ConjureParticleOptions(color), pos.x, pos.y, pos.z, 0.0, 0.0, 0.0); } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgSingleParticleS2C.java b/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgSingleParticleS2C.java index 0c03aa92b..29e8bd9ff 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgSingleParticleS2C.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgSingleParticleS2C.java @@ -1,7 +1,6 @@ package at.petrak.hexcasting.common.msgs; import at.petrak.hexcasting.api.HexAPI; -import at.petrak.hexcasting.api.utils.HexUtils; import at.petrak.hexcasting.api.pigment.FrozenPigment; import at.petrak.hexcasting.common.particles.ConjureParticleOptions; import at.petrak.paucal.api.PaucalCodecs; @@ -11,8 +10,6 @@ import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.world.phys.Vec3; -import java.util.Random; - public record MsgSingleParticleS2C(Vec3 pos, FrozenPigment colorizer) implements CustomPacketPayload { public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(HexAPI.modLoc("prtcl_s")); @@ -37,11 +34,12 @@ public static void handle(MsgSingleParticleS2C msg) { Minecraft.getInstance().execute(() -> { var level = Minecraft.getInstance().level; if (level == null) return; - var color = HexUtils.nextColor(msg.colorizer, level.random); - level.addParticle(new ConjureParticleOptions(color), - msg.pos.x, msg.pos.y, msg.pos.z, 0.0, 0.0, 0.0); + var colProvider = msg.colorizer().getColorProvider(); + + var color = colProvider.getRandomColor(level.random); + level.addParticle(new ConjureParticleOptions(color), msg.pos.x, msg.pos.y, msg.pos.z, 0.0, 0.0, 0.0); for (int i = 0; i <= 10; i++) { - color = HexUtils.nextColor(msg.colorizer, level.random); + color = colProvider.getRandomColor(level.random); double offsetX = level.random.nextFloat() * 0.1 - 0.05; double offsetY = level.random.nextFloat() * 0.1 - 0.05; double offsetZ = level.random.nextFloat() * 0.1 - 0.05; From d627900bbda4d7c6e03437812903e5727e183dab Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Wed, 8 Jul 2026 14:29:03 -0400 Subject: [PATCH 10/16] Use blockpos directly, trim redundant code --- .../common/casting/actions/spells/OpFallingBlock.kt | 13 +++++-------- .../common/casting/actions/spells/OpFreeze.kt | 11 +++++------ .../common/casting/actions/spells/OpSmelt.kt | 3 +-- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFallingBlock.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFallingBlock.kt index 72ec9d71d..5e084290e 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFallingBlock.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFallingBlock.kt @@ -33,23 +33,20 @@ object OpFallingBlock : SpellAction { override val argc = 1 override fun execute(args: List, env: CastingEnvironment): SpellAction.Result { - val pos = args.getVec3(0, argc) - env.assertVecInRange(pos) + val pos = args.getBlockPos(0, argc) + env.assertPosInRangeForEditing(pos) - val centered = Vec3.atCenterOf(BlockPos.containing(pos)) return SpellAction.Result( Spell(pos), (1.5 * MediaConstants.DUST_UNIT).roundToLong(), - listOf(ParticleSpray.burst(centered, 1.0)) + listOf(ParticleSpray.burst(Vec3.atCenterOf(pos), 1.0)) ) } - private data class Spell(val v: Vec3) : RenderedSpell { + private data class Spell(val pos: BlockPos) : RenderedSpell { override fun cast(env: CastingEnvironment) { - val pos = BlockPos.containing(v) - val blockstate = env.world.getBlockState(pos) - if (!env.canEditBlockAt(pos) || !IXplatAbstractions.INSTANCE.isBreakingAllowed(env.world, pos, blockstate, env.caster)) + if (!IXplatAbstractions.INSTANCE.isBreakingAllowed(env.world, pos, blockstate, env.castingEntity as? ServerPlayer)) return val tier = HexConfig.server().opBreakHarvestLevel() diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFreeze.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFreeze.kt index 1f8e7c24a..e3d11cd12 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFreeze.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFreeze.kt @@ -19,24 +19,23 @@ object OpFreeze : SpellAction { override val argc = 1 override fun execute(args: List, env: CastingEnvironment): SpellAction.Result { - val toFreeze = Vec3.atCenterOf(BlockPos.containing(args.getVec3(0, argc))) + val toFreeze = args.getBlockPos(0, argc) - env.assertVecInRange(toFreeze) + env.assertPosInRangeForEditing(toFreeze) return SpellAction.Result( Spell(toFreeze), MediaConstants.DUST_UNIT, - listOf(ParticleSpray.burst(toFreeze, 1.0)) + listOf(ParticleSpray.burst(Vec3.atCenterOf(toFreeze), 1.0)) ) } - private data class Spell(val vec: Vec3) : RenderedSpell { + private data class Spell(val pos: BlockPos) : RenderedSpell { override fun cast(env: CastingEnvironment) { - val pos = BlockPos.containing(vec) val blockState = env.world.getBlockState(pos) val fluidState = env.world.getFluidState(pos) - if (!env.canEditBlockAt(pos) || !IXplatAbstractions.INSTANCE.isBreakingAllowed(env.world, pos, blockState, env.castingEntity as? ServerPlayer)) + if (!IXplatAbstractions.INSTANCE.isBreakingAllowed(env.world, pos, blockState, env.castingEntity as? ServerPlayer)) return if (fluidState.type == Fluids.WATER && blockState.block is LiquidBlock) { diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpSmelt.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpSmelt.kt index 41b406a49..c6b2c18b5 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpSmelt.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpSmelt.kt @@ -79,7 +79,6 @@ object OpSmelt : SpellAction { private data class BlockSpell(val pos: BlockPos) : RenderedSpell { override fun cast(env: CastingEnvironment) { - if (!env.canEditBlockAt(pos)) return val blockState = env.world.getBlockState(pos) if (!IXplatAbstractions.INSTANCE.isBreakingAllowed(env.world, pos, blockState, env.castingEntity as? ServerPlayer)) return @@ -93,7 +92,7 @@ object OpSmelt : SpellAction { env.world.addFreshEntity(ItemEntity(env.world, pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(), itemStack.copy())) } } else { - env.world.destroyBlock(pos, false, env.caster) + env.world.destroyBlock(pos, false, env.castingEntity as? ServerPlayer) env.world.addFreshEntity(ItemEntity(env.world, pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(), itemStack.copy())) // Send a block update, also copied from Ars Nouveau (this is all copied from Ars Nouveau) if (!env.world.isOutsideBuildHeight(pos)) From d7d43bcf90aec3b4bb5f0d39a40f273e23b466f2 Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Wed, 8 Jul 2026 14:29:23 -0400 Subject: [PATCH 11/16] Use double to avoid overflow in factorial --- .../common/casting/arithmetic/operator/OperatorUtils.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/operator/OperatorUtils.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/operator/OperatorUtils.kt index cde87842e..e67924a72 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/operator/OperatorUtils.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/operator/OperatorUtils.kt @@ -77,11 +77,11 @@ fun generalFactorial(arg: Double): Double { val argInt = arg.roundToInt() if (arg >= 0 && DoubleIota.tolerates(arg, argInt.toDouble())) { // normal integer factorial - var result: Long = 1 + var result = 1.0 for (factor in 2..argInt) { result *= factor } - return result.toDouble() + return result } // gamma function of arg+1 for non-integer factorial // https://introcs.cs.princeton.edu/java/91float/Gamma.java.html From 8a3eb0574e1ebc45faee46cda7f9ab0029ae5ac9 Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Wed, 8 Jul 2026 15:15:35 -0400 Subject: [PATCH 12/16] Data-driven fluid freezing, fix freeze recipe datagen --- .../recipes/freeze/{freeze => }/blue_ice.json | 4 +- .../recipes/freeze/cobblestone.json | 32 +++++++ .../freeze/freeze/powder_snow_cauldron.json | 32 ------- .../advancement/recipes/freeze/ice.json | 32 +++++++ .../advancement/recipes/freeze/obsidian.json | 32 +++++++ .../recipes}/freeze/packed_ice.json | 4 +- ...ked_ice.json => powder_snow_cauldron.json} | 4 +- .../recipes/freeze/snow_block.json | 32 +++++++ .../recipe/freeze/{freeze => }/blue_ice.json | 0 .../hexcasting/recipe/freeze/cobblestone.json | 10 ++ .../data/hexcasting/recipe/freeze/ice.json | 10 ++ .../hexcasting/recipe/freeze/obsidian.json | 10 ++ .../freeze/{freeze => }/packed_ice.json | 0 .../{freeze => }/powder_snow_cauldron.json | 0 .../hexcasting/recipe/freeze/snow_block.json | 10 ++ .../common/casting/actions/spells/OpFreeze.kt | 14 --- .../common/lib/HexStateIngredients.java | 5 + .../state/StateIngredientFluid.java | 92 +++++++++++++++++++ .../datagen/recipe/HexplatRecipes.java | 20 +++- .../advancement/recipes}/freeze/blue_ice.json | 4 +- .../recipes/freeze/cobblestone.json | 32 +++++++ .../freeze/freeze/powder_snow_cauldron.json | 32 ------- .../advancement/recipes/freeze/ice.json | 32 +++++++ .../advancement/recipes/freeze/obsidian.json | 32 +++++++ .../recipes/freeze/packed_ice.json | 32 +++++++ .../recipes/freeze/powder_snow_cauldron.json | 32 +++++++ .../recipes/freeze/snow_block.json | 32 +++++++ .../recipe/freeze/{freeze => }/blue_ice.json | 0 .../hexcasting/recipe/freeze/cobblestone.json | 10 ++ .../data/hexcasting/recipe/freeze/ice.json | 10 ++ .../hexcasting/recipe/freeze/obsidian.json | 10 ++ .../freeze/{freeze => }/packed_ice.json | 0 .../{freeze => }/powder_snow_cauldron.json | 0 .../hexcasting/recipe/freeze/snow_block.json | 10 ++ .../advancement/recipes}/freeze/blue_ice.json | 4 +- .../recipes/freeze/cobblestone.json | 32 +++++++ .../recipes/freeze/freeze/packed_ice.json | 32 ------- .../freeze/freeze/powder_snow_cauldron.json | 32 ------- .../advancement/recipes/freeze/ice.json | 32 +++++++ .../advancement/recipes/freeze/obsidian.json | 32 +++++++ .../recipes/freeze/packed_ice.json | 32 +++++++ .../recipes/freeze/powder_snow_cauldron.json | 32 +++++++ .../recipes/freeze/snow_block.json | 32 +++++++ .../recipe/freeze/{freeze => }/blue_ice.json | 0 .../hexcasting/recipe/freeze/cobblestone.json | 10 ++ .../data/hexcasting/recipe/freeze/ice.json | 10 ++ .../hexcasting/recipe/freeze/obsidian.json | 10 ++ .../freeze/{freeze => }/packed_ice.json | 0 .../{freeze => }/powder_snow_cauldron.json | 0 .../hexcasting/recipe/freeze/snow_block.json | 10 ++ 50 files changed, 756 insertions(+), 155 deletions(-) rename Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/{freeze => }/blue_ice.json (85%) create mode 100644 Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/cobblestone.json delete mode 100644 Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json create mode 100644 Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/ice.json create mode 100644 Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/obsidian.json rename {Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze => Common/src/generated/resources/data/hexcasting/advancement/recipes}/freeze/packed_ice.json (84%) rename Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/{freeze/packed_ice.json => powder_snow_cauldron.json} (84%) create mode 100644 Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/snow_block.json rename Common/src/generated/resources/data/hexcasting/recipe/freeze/{freeze => }/blue_ice.json (100%) create mode 100644 Common/src/generated/resources/data/hexcasting/recipe/freeze/cobblestone.json create mode 100644 Common/src/generated/resources/data/hexcasting/recipe/freeze/ice.json create mode 100644 Common/src/generated/resources/data/hexcasting/recipe/freeze/obsidian.json rename Common/src/generated/resources/data/hexcasting/recipe/freeze/{freeze => }/packed_ice.json (100%) rename Common/src/generated/resources/data/hexcasting/recipe/freeze/{freeze => }/powder_snow_cauldron.json (100%) create mode 100644 Common/src/generated/resources/data/hexcasting/recipe/freeze/snow_block.json create mode 100644 Common/src/main/java/at/petrak/hexcasting/common/recipe/ingredient/state/StateIngredientFluid.java rename {Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze => Fabric/src/generated/resources/data/hexcasting/advancement/recipes}/freeze/blue_ice.json (85%) create mode 100644 Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/cobblestone.json delete mode 100644 Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json create mode 100644 Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/ice.json create mode 100644 Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/obsidian.json create mode 100644 Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/packed_ice.json create mode 100644 Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/powder_snow_cauldron.json create mode 100644 Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/snow_block.json rename Fabric/src/generated/resources/data/hexcasting/recipe/freeze/{freeze => }/blue_ice.json (100%) create mode 100644 Fabric/src/generated/resources/data/hexcasting/recipe/freeze/cobblestone.json create mode 100644 Fabric/src/generated/resources/data/hexcasting/recipe/freeze/ice.json create mode 100644 Fabric/src/generated/resources/data/hexcasting/recipe/freeze/obsidian.json rename Fabric/src/generated/resources/data/hexcasting/recipe/freeze/{freeze => }/packed_ice.json (100%) rename Fabric/src/generated/resources/data/hexcasting/recipe/freeze/{freeze => }/powder_snow_cauldron.json (100%) create mode 100644 Fabric/src/generated/resources/data/hexcasting/recipe/freeze/snow_block.json rename {Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze => Neoforge/src/generated/resources/data/hexcasting/advancement/recipes}/freeze/blue_ice.json (85%) create mode 100644 Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/cobblestone.json delete mode 100644 Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json delete mode 100644 Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json create mode 100644 Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/ice.json create mode 100644 Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/obsidian.json create mode 100644 Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/packed_ice.json create mode 100644 Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/powder_snow_cauldron.json create mode 100644 Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/snow_block.json rename Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/{freeze => }/blue_ice.json (100%) create mode 100644 Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/cobblestone.json create mode 100644 Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/ice.json create mode 100644 Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/obsidian.json rename Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/{freeze => }/packed_ice.json (100%) rename Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/{freeze => }/powder_snow_cauldron.json (100%) create mode 100644 Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/snow_block.json diff --git a/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/blue_ice.json similarity index 85% rename from Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json rename to Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/blue_ice.json index db092ef28..04774bbe1 100644 --- a/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json +++ b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/blue_ice.json @@ -13,7 +13,7 @@ }, "has_the_recipe": { "conditions": { - "recipe": "hexcasting:freeze/blue_ice" + "recipe": "hexcasting:blue_ice" }, "trigger": "minecraft:recipe_unlocked" } @@ -26,7 +26,7 @@ ], "rewards": { "recipes": [ - "hexcasting:freeze/blue_ice" + "hexcasting:blue_ice" ] } } \ No newline at end of file diff --git a/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/cobblestone.json b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/cobblestone.json new file mode 100644 index 000000000..ea73b2f9b --- /dev/null +++ b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/cobblestone.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:cobblestone" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:cobblestone" + ] + } +} \ No newline at end of file diff --git a/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json deleted file mode 100644 index 1e49ebee8..000000000 --- a/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "parent": "minecraft:recipes/root", - "criteria": { - "has_item": { - "conditions": { - "items": [ - { - "items": "#hexcasting:staves" - } - ] - }, - "trigger": "minecraft:inventory_changed" - }, - "has_the_recipe": { - "conditions": { - "recipe": "hexcasting:freeze/powder_snow_cauldron" - }, - "trigger": "minecraft:recipe_unlocked" - } - }, - "requirements": [ - [ - "has_the_recipe", - "has_item" - ] - ], - "rewards": { - "recipes": [ - "hexcasting:freeze/powder_snow_cauldron" - ] - } -} \ No newline at end of file diff --git a/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/ice.json b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/ice.json new file mode 100644 index 000000000..3ab7d6327 --- /dev/null +++ b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/ice.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:ice" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:ice" + ] + } +} \ No newline at end of file diff --git a/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/obsidian.json b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/obsidian.json new file mode 100644 index 000000000..037351938 --- /dev/null +++ b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/obsidian.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:obsidian" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:obsidian" + ] + } +} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/packed_ice.json similarity index 84% rename from Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json rename to Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/packed_ice.json index ccded8855..4ebdafe90 100644 --- a/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json +++ b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/packed_ice.json @@ -13,7 +13,7 @@ }, "has_the_recipe": { "conditions": { - "recipe": "hexcasting:freeze/packed_ice" + "recipe": "hexcasting:packed_ice" }, "trigger": "minecraft:recipe_unlocked" } @@ -26,7 +26,7 @@ ], "rewards": { "recipes": [ - "hexcasting:freeze/packed_ice" + "hexcasting:packed_ice" ] } } \ No newline at end of file diff --git a/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/powder_snow_cauldron.json similarity index 84% rename from Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json rename to Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/powder_snow_cauldron.json index ccded8855..3f16e3b8f 100644 --- a/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json +++ b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/powder_snow_cauldron.json @@ -13,7 +13,7 @@ }, "has_the_recipe": { "conditions": { - "recipe": "hexcasting:freeze/packed_ice" + "recipe": "hexcasting:powder_snow_cauldron" }, "trigger": "minecraft:recipe_unlocked" } @@ -26,7 +26,7 @@ ], "rewards": { "recipes": [ - "hexcasting:freeze/packed_ice" + "hexcasting:powder_snow_cauldron" ] } } \ No newline at end of file diff --git a/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/snow_block.json b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/snow_block.json new file mode 100644 index 000000000..acb66f14d --- /dev/null +++ b/Common/src/generated/resources/data/hexcasting/advancement/recipes/freeze/snow_block.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:snow_block" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:snow_block" + ] + } +} \ No newline at end of file diff --git a/Common/src/generated/resources/data/hexcasting/recipe/freeze/freeze/blue_ice.json b/Common/src/generated/resources/data/hexcasting/recipe/freeze/blue_ice.json similarity index 100% rename from Common/src/generated/resources/data/hexcasting/recipe/freeze/freeze/blue_ice.json rename to Common/src/generated/resources/data/hexcasting/recipe/freeze/blue_ice.json diff --git a/Common/src/generated/resources/data/hexcasting/recipe/freeze/cobblestone.json b/Common/src/generated/resources/data/hexcasting/recipe/freeze/cobblestone.json new file mode 100644 index 000000000..62eb26ee5 --- /dev/null +++ b/Common/src/generated/resources/data/hexcasting/recipe/freeze/cobblestone.json @@ -0,0 +1,10 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:fluid", + "fluid": "minecraft:flowing_lava" + }, + "result": { + "Name": "minecraft:cobblestone" + } +} \ No newline at end of file diff --git a/Common/src/generated/resources/data/hexcasting/recipe/freeze/ice.json b/Common/src/generated/resources/data/hexcasting/recipe/freeze/ice.json new file mode 100644 index 000000000..8cc38bd0e --- /dev/null +++ b/Common/src/generated/resources/data/hexcasting/recipe/freeze/ice.json @@ -0,0 +1,10 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:fluid", + "fluid": "minecraft:water" + }, + "result": { + "Name": "minecraft:ice" + } +} \ No newline at end of file diff --git a/Common/src/generated/resources/data/hexcasting/recipe/freeze/obsidian.json b/Common/src/generated/resources/data/hexcasting/recipe/freeze/obsidian.json new file mode 100644 index 000000000..02e435b8a --- /dev/null +++ b/Common/src/generated/resources/data/hexcasting/recipe/freeze/obsidian.json @@ -0,0 +1,10 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:fluid", + "fluid": "minecraft:lava" + }, + "result": { + "Name": "minecraft:obsidian" + } +} \ No newline at end of file diff --git a/Common/src/generated/resources/data/hexcasting/recipe/freeze/freeze/packed_ice.json b/Common/src/generated/resources/data/hexcasting/recipe/freeze/packed_ice.json similarity index 100% rename from Common/src/generated/resources/data/hexcasting/recipe/freeze/freeze/packed_ice.json rename to Common/src/generated/resources/data/hexcasting/recipe/freeze/packed_ice.json diff --git a/Common/src/generated/resources/data/hexcasting/recipe/freeze/freeze/powder_snow_cauldron.json b/Common/src/generated/resources/data/hexcasting/recipe/freeze/powder_snow_cauldron.json similarity index 100% rename from Common/src/generated/resources/data/hexcasting/recipe/freeze/freeze/powder_snow_cauldron.json rename to Common/src/generated/resources/data/hexcasting/recipe/freeze/powder_snow_cauldron.json diff --git a/Common/src/generated/resources/data/hexcasting/recipe/freeze/snow_block.json b/Common/src/generated/resources/data/hexcasting/recipe/freeze/snow_block.json new file mode 100644 index 000000000..24aa4e0ec --- /dev/null +++ b/Common/src/generated/resources/data/hexcasting/recipe/freeze/snow_block.json @@ -0,0 +1,10 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:fluid", + "fluid": "minecraft:flowing_water" + }, + "result": { + "Name": "minecraft:snow_block" + } +} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFreeze.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFreeze.kt index e3d11cd12..d0343184f 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFreeze.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFreeze.kt @@ -33,24 +33,10 @@ object OpFreeze : SpellAction { private data class Spell(val pos: BlockPos) : RenderedSpell { override fun cast(env: CastingEnvironment) { val blockState = env.world.getBlockState(pos) - val fluidState = env.world.getFluidState(pos) if (!IXplatAbstractions.INSTANCE.isBreakingAllowed(env.world, pos, blockState, env.castingEntity as? ServerPlayer)) return - if (fluidState.type == Fluids.WATER && blockState.block is LiquidBlock) { - env.world.setBlockAndUpdate(pos, Blocks.ICE.defaultBlockState()) - return - } - if (fluidState.type == Fluids.LAVA && blockState.block is LiquidBlock) { - env.world.setBlockAndUpdate(pos, Blocks.OBSIDIAN.defaultBlockState()) - return - } - if (fluidState.type == Fluids.FLOWING_LAVA && blockState.block is LiquidBlock) { - env.world.setBlockAndUpdate(pos, Blocks.COBBLESTONE.defaultBlockState()) - return - } - val recman = env.world.recipeManager val recipes = recman.getAllRecipesFor(HexRecipeStuffRegistry.FREEZE_TYPE).map{ holder -> holder.value } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/HexStateIngredients.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/HexStateIngredients.java index ae4b7abde..7217ae9d5 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/lib/HexStateIngredients.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/HexStateIngredients.java @@ -13,6 +13,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Fluid; import java.util.List; import java.util.Random; @@ -30,6 +31,7 @@ public class HexStateIngredients { public static final StateIngredientType BLOCK_TYPE = new StateIngredientBlock.Type(); public static final StateIngredientType BLOCK_STATE = new StateIngredientBlockState.Type(); public static final StateIngredientType BLOCKS = new StateIngredientBlocks.Type(); + public static final StateIngredientType FLUID_TYPE = new StateIngredientFluid.Type(); public static final StateIngredientType TAG = new StateIngredientTag.Type(); public static final StateIngredientType TAG_EXCLUDING = new StateIngredientTagExcluding.Type(); @@ -77,6 +79,7 @@ public static void register(BiConsumer, ResourceLocation> r.accept(BLOCK_TYPE, HexAPI.modLoc("block")); r.accept(BLOCK_STATE, HexAPI.modLoc("state")); r.accept(BLOCKS, HexAPI.modLoc("blocks")); + r.accept(FLUID_TYPE, HexAPI.modLoc("fluid")); r.accept(TAG, HexAPI.modLoc("tag")); r.accept(TAG_EXCLUDING, HexAPI.modLoc("tag_excluding")); } @@ -92,4 +95,6 @@ public static StateIngredient of(BlockState state) { public static StateIngredient of(TagKey tag) { return new StateIngredientTag(tag); } + + public static StateIngredient of(Fluid fluid) { return new StateIngredientFluid(fluid); } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/recipe/ingredient/state/StateIngredientFluid.java b/Common/src/main/java/at/petrak/hexcasting/common/recipe/ingredient/state/StateIngredientFluid.java new file mode 100644 index 000000000..f40cb1283 --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/recipe/ingredient/state/StateIngredientFluid.java @@ -0,0 +1,92 @@ +package at.petrak.hexcasting.common.recipe.ingredient.state; + +import at.petrak.hexcasting.common.lib.HexStateIngredients; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.Fluids; + +import java.util.Collections; +import java.util.List; +import java.util.Random; + +public class StateIngredientFluid implements StateIngredient { + private final Fluid fluid; + + public StateIngredientFluid(Fluid fluid) { this.fluid = fluid; } + + @Override + public StateIngredientType getType() { + return HexStateIngredients.FLUID_TYPE; + } + + @Override + public boolean test(BlockState blockState) { return fluid == blockState.getFluidState().getType(); } + + @Override + public BlockState pick(Random random) { return fluid.defaultFluidState().createLegacyBlock(); } + + @Override + public List getDisplayedStacks() { + if (fluid == Fluids.EMPTY) { + return Collections.emptyList(); + } + return Collections.singletonList(new ItemStack(fluid.getBucket())); + } + + @Override + public List getDisplayed() { + return Collections.singletonList(fluid.defaultFluidState().createLegacyBlock()); + } + + public Fluid getFluid() { return fluid; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + return fluid == ((StateIngredientFluid) o).fluid; + } + + @Override + public int hashCode() { + return fluid.hashCode(); + } + + @Override + public String toString() { + return "StateIngredientBlock{" + fluid + "}"; + } + + public static class Type implements StateIngredientType { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + BuiltInRegistries.FLUID.byNameCodec().fieldOf("fluid").forGetter(StateIngredientFluid::getFluid) + ).apply(instance, StateIngredientFluid::new)); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.registry(Registries.FLUID), StateIngredientFluid::getFluid, + StateIngredientFluid::new + ); + + @Override + public MapCodec codec() { + return CODEC; + } + + @Override + public StreamCodec streamCodec() { + return STREAM_CODEC; + } + } +} diff --git a/Common/src/main/java/at/petrak/hexcasting/datagen/recipe/HexplatRecipes.java b/Common/src/main/java/at/petrak/hexcasting/datagen/recipe/HexplatRecipes.java index 02bd956bb..3058addef 100644 --- a/Common/src/main/java/at/petrak/hexcasting/datagen/recipe/HexplatRecipes.java +++ b/Common/src/main/java/at/petrak/hexcasting/datagen/recipe/HexplatRecipes.java @@ -47,6 +47,7 @@ import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.material.Fluids; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -470,15 +471,28 @@ public void buildRecipes(RecipeOutput recipes) { .requires(HexBlocks.AMETHYST_PILLAR) .unlockedBy("has_item", has(HexBlocks.SLATE)).save(recipes); + new FreezeRecipeBuilder(HexStateIngredients.of(Fluids.WATER), Blocks.ICE.defaultBlockState()) + .unlockedBy("has_item", hasItem(HexTags.Items.STAVES)) + .save(recipes, modLoc("ice")); + new FreezeRecipeBuilder(HexStateIngredients.of(Fluids.FLOWING_WATER), Blocks.SNOW_BLOCK.defaultBlockState()) + .unlockedBy("has_item", hasItem(HexTags.Items.STAVES)) + .save(recipes, modLoc("snow_block")); + new FreezeRecipeBuilder(HexStateIngredients.of(Fluids.LAVA), Blocks.OBSIDIAN.defaultBlockState()) + .unlockedBy("has_item", hasItem(HexTags.Items.STAVES)) + .save(recipes, modLoc("obsidian")); + new FreezeRecipeBuilder(HexStateIngredients.of(Fluids.FLOWING_LAVA), Blocks.COBBLESTONE.defaultBlockState()) + .unlockedBy("has_item", hasItem(HexTags.Items.STAVES)) + .save(recipes, modLoc("cobblestone")); + new FreezeRecipeBuilder(HexStateIngredients.of(Blocks.ICE), Blocks.PACKED_ICE.defaultBlockState()) .unlockedBy("has_item", hasItem(HexTags.Items.STAVES)) - .save(recipes, modLoc("freeze/packed_ice")); + .save(recipes, modLoc("packed_ice")); new FreezeRecipeBuilder(HexStateIngredients.of(Blocks.PACKED_ICE), Blocks.BLUE_ICE.defaultBlockState()) .unlockedBy("has_item", hasItem(HexTags.Items.STAVES)) - .save(recipes, modLoc("freeze/blue_ice")); + .save(recipes, modLoc("blue_ice")); new FreezeRecipeBuilder(HexStateIngredients.of(Blocks.WATER_CAULDRON), Blocks.POWDER_SNOW_CAULDRON.defaultBlockState()) .unlockedBy("has_item", hasItem(HexTags.Items.STAVES)) - .save(recipes, modLoc("freeze/powder_snow_cauldron")); + .save(recipes, modLoc("powder_snow_cauldron")); new BrainsweepRecipeBuilder(HexStateIngredients.of(Blocks.AMETHYST_BLOCK), new VillagerIngredient(null, null, 3), diff --git a/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/blue_ice.json similarity index 85% rename from Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json rename to Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/blue_ice.json index db092ef28..04774bbe1 100644 --- a/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json +++ b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/blue_ice.json @@ -13,7 +13,7 @@ }, "has_the_recipe": { "conditions": { - "recipe": "hexcasting:freeze/blue_ice" + "recipe": "hexcasting:blue_ice" }, "trigger": "minecraft:recipe_unlocked" } @@ -26,7 +26,7 @@ ], "rewards": { "recipes": [ - "hexcasting:freeze/blue_ice" + "hexcasting:blue_ice" ] } } \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/cobblestone.json b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/cobblestone.json new file mode 100644 index 000000000..ea73b2f9b --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/cobblestone.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:cobblestone" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:cobblestone" + ] + } +} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json deleted file mode 100644 index 1e49ebee8..000000000 --- a/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "parent": "minecraft:recipes/root", - "criteria": { - "has_item": { - "conditions": { - "items": [ - { - "items": "#hexcasting:staves" - } - ] - }, - "trigger": "minecraft:inventory_changed" - }, - "has_the_recipe": { - "conditions": { - "recipe": "hexcasting:freeze/powder_snow_cauldron" - }, - "trigger": "minecraft:recipe_unlocked" - } - }, - "requirements": [ - [ - "has_the_recipe", - "has_item" - ] - ], - "rewards": { - "recipes": [ - "hexcasting:freeze/powder_snow_cauldron" - ] - } -} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/ice.json b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/ice.json new file mode 100644 index 000000000..3ab7d6327 --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/ice.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:ice" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:ice" + ] + } +} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/obsidian.json b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/obsidian.json new file mode 100644 index 000000000..037351938 --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/obsidian.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:obsidian" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:obsidian" + ] + } +} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/packed_ice.json b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/packed_ice.json new file mode 100644 index 000000000..4ebdafe90 --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/packed_ice.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:packed_ice" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:packed_ice" + ] + } +} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/powder_snow_cauldron.json b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/powder_snow_cauldron.json new file mode 100644 index 000000000..3f16e3b8f --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/powder_snow_cauldron.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:powder_snow_cauldron" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:powder_snow_cauldron" + ] + } +} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/snow_block.json b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/snow_block.json new file mode 100644 index 000000000..acb66f14d --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/snow_block.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:snow_block" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:snow_block" + ] + } +} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/freeze/blue_ice.json b/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/blue_ice.json similarity index 100% rename from Fabric/src/generated/resources/data/hexcasting/recipe/freeze/freeze/blue_ice.json rename to Fabric/src/generated/resources/data/hexcasting/recipe/freeze/blue_ice.json diff --git a/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/cobblestone.json b/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/cobblestone.json new file mode 100644 index 000000000..62eb26ee5 --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/cobblestone.json @@ -0,0 +1,10 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:fluid", + "fluid": "minecraft:flowing_lava" + }, + "result": { + "Name": "minecraft:cobblestone" + } +} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/ice.json b/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/ice.json new file mode 100644 index 000000000..8cc38bd0e --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/ice.json @@ -0,0 +1,10 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:fluid", + "fluid": "minecraft:water" + }, + "result": { + "Name": "minecraft:ice" + } +} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/obsidian.json b/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/obsidian.json new file mode 100644 index 000000000..02e435b8a --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/obsidian.json @@ -0,0 +1,10 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:fluid", + "fluid": "minecraft:lava" + }, + "result": { + "Name": "minecraft:obsidian" + } +} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/freeze/packed_ice.json b/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/packed_ice.json similarity index 100% rename from Fabric/src/generated/resources/data/hexcasting/recipe/freeze/freeze/packed_ice.json rename to Fabric/src/generated/resources/data/hexcasting/recipe/freeze/packed_ice.json diff --git a/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/freeze/powder_snow_cauldron.json b/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/powder_snow_cauldron.json similarity index 100% rename from Fabric/src/generated/resources/data/hexcasting/recipe/freeze/freeze/powder_snow_cauldron.json rename to Fabric/src/generated/resources/data/hexcasting/recipe/freeze/powder_snow_cauldron.json diff --git a/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/snow_block.json b/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/snow_block.json new file mode 100644 index 000000000..24aa4e0ec --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/recipe/freeze/snow_block.json @@ -0,0 +1,10 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:fluid", + "fluid": "minecraft:flowing_water" + }, + "result": { + "Name": "minecraft:snow_block" + } +} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/blue_ice.json similarity index 85% rename from Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json rename to Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/blue_ice.json index db092ef28..04774bbe1 100644 --- a/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/blue_ice.json +++ b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/blue_ice.json @@ -13,7 +13,7 @@ }, "has_the_recipe": { "conditions": { - "recipe": "hexcasting:freeze/blue_ice" + "recipe": "hexcasting:blue_ice" }, "trigger": "minecraft:recipe_unlocked" } @@ -26,7 +26,7 @@ ], "rewards": { "recipes": [ - "hexcasting:freeze/blue_ice" + "hexcasting:blue_ice" ] } } \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/cobblestone.json b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/cobblestone.json new file mode 100644 index 000000000..ea73b2f9b --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/cobblestone.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:cobblestone" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:cobblestone" + ] + } +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json deleted file mode 100644 index ccded8855..000000000 --- a/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/packed_ice.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "parent": "minecraft:recipes/root", - "criteria": { - "has_item": { - "conditions": { - "items": [ - { - "items": "#hexcasting:staves" - } - ] - }, - "trigger": "minecraft:inventory_changed" - }, - "has_the_recipe": { - "conditions": { - "recipe": "hexcasting:freeze/packed_ice" - }, - "trigger": "minecraft:recipe_unlocked" - } - }, - "requirements": [ - [ - "has_the_recipe", - "has_item" - ] - ], - "rewards": { - "recipes": [ - "hexcasting:freeze/packed_ice" - ] - } -} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json deleted file mode 100644 index 1e49ebee8..000000000 --- a/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/freeze/powder_snow_cauldron.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "parent": "minecraft:recipes/root", - "criteria": { - "has_item": { - "conditions": { - "items": [ - { - "items": "#hexcasting:staves" - } - ] - }, - "trigger": "minecraft:inventory_changed" - }, - "has_the_recipe": { - "conditions": { - "recipe": "hexcasting:freeze/powder_snow_cauldron" - }, - "trigger": "minecraft:recipe_unlocked" - } - }, - "requirements": [ - [ - "has_the_recipe", - "has_item" - ] - ], - "rewards": { - "recipes": [ - "hexcasting:freeze/powder_snow_cauldron" - ] - } -} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/ice.json b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/ice.json new file mode 100644 index 000000000..3ab7d6327 --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/ice.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:ice" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:ice" + ] + } +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/obsidian.json b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/obsidian.json new file mode 100644 index 000000000..037351938 --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/obsidian.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:obsidian" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:obsidian" + ] + } +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/packed_ice.json b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/packed_ice.json new file mode 100644 index 000000000..4ebdafe90 --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/packed_ice.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:packed_ice" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:packed_ice" + ] + } +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/powder_snow_cauldron.json b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/powder_snow_cauldron.json new file mode 100644 index 000000000..3f16e3b8f --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/powder_snow_cauldron.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:powder_snow_cauldron" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:powder_snow_cauldron" + ] + } +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/snow_block.json b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/snow_block.json new file mode 100644 index 000000000..acb66f14d --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/freeze/snow_block.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "#hexcasting:staves" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:snow_block" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:snow_block" + ] + } +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/freeze/blue_ice.json b/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/blue_ice.json similarity index 100% rename from Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/freeze/blue_ice.json rename to Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/blue_ice.json diff --git a/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/cobblestone.json b/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/cobblestone.json new file mode 100644 index 000000000..62eb26ee5 --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/cobblestone.json @@ -0,0 +1,10 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:fluid", + "fluid": "minecraft:flowing_lava" + }, + "result": { + "Name": "minecraft:cobblestone" + } +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/ice.json b/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/ice.json new file mode 100644 index 000000000..8cc38bd0e --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/ice.json @@ -0,0 +1,10 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:fluid", + "fluid": "minecraft:water" + }, + "result": { + "Name": "minecraft:ice" + } +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/obsidian.json b/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/obsidian.json new file mode 100644 index 000000000..02e435b8a --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/obsidian.json @@ -0,0 +1,10 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:fluid", + "fluid": "minecraft:lava" + }, + "result": { + "Name": "minecraft:obsidian" + } +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/freeze/packed_ice.json b/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/packed_ice.json similarity index 100% rename from Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/freeze/packed_ice.json rename to Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/packed_ice.json diff --git a/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/freeze/powder_snow_cauldron.json b/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/powder_snow_cauldron.json similarity index 100% rename from Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/freeze/powder_snow_cauldron.json rename to Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/powder_snow_cauldron.json diff --git a/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/snow_block.json b/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/snow_block.json new file mode 100644 index 000000000..24aa4e0ec --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/recipe/freeze/snow_block.json @@ -0,0 +1,10 @@ +{ + "type": "hexcasting:freeze", + "blockIn": { + "type": "hexcasting:fluid", + "fluid": "minecraft:flowing_water" + }, + "result": { + "Name": "minecraft:snow_block" + } +} \ No newline at end of file From edca0c485bb614ced838cc5201fd460112e91a0f Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Wed, 8 Jul 2026 18:02:26 -0400 Subject: [PATCH 13/16] Streamline canSilkTouch a little --- .../casting/actions/spells/OpFallingBlock.kt | 36 +++++++------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFallingBlock.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFallingBlock.kt index 5e084290e..1d4ab7bb5 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFallingBlock.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFallingBlock.kt @@ -80,36 +80,24 @@ object OpFallingBlock : SpellAction { } } - fun canSilkTouch(level: ServerLevel, pos: BlockPos, state: BlockState, harvestTier: Tier, owner: Entity?): Boolean { - val harvestToolStack: ItemStack = getHarvestToolStack(harvestTier, state) - if (harvestToolStack.isEmpty) { - return false - } - harvestToolStack.enchant(level.holderLookup(Registries.ENCHANTMENT).getOrThrow(Enchantments.SILK_TOUCH), 1) - val drops: List = Block.getDrops(state, level, pos, null, owner, harvestToolStack) - val blockItem: Item = state.block.asItem() - return drops.any { s -> s.item === blockItem } - } - companion object { - fun getHarvestToolStack(harvestTier: Tier, state: BlockState): ItemStack { - return getTool(harvestTier, state).copy() + fun canSilkTouch(level: ServerLevel, pos: BlockPos, state: BlockState, harvestTier: Tier, owner: Entity?): Boolean { + // need to use .copy() here or the stack in HARVEST_TOOLS_BY_TIER will get enchanted + val harvestToolStack: ItemStack = getTool(harvestTier, state).copy() + if (harvestToolStack.isEmpty) return false + harvestToolStack.enchant(level.holderLookup(Registries.ENCHANTMENT).getOrThrow(Enchantments.SILK_TOUCH), 1) + val drops: List = Block.getDrops(state, level, pos, null, owner, harvestToolStack) + val blockItem: Item = state.block.asItem() + return drops.any { s -> s.item === blockItem } } private fun getTool(harvestTier: Tier, state: BlockState): ItemStack { - if (harvestTier !in HARVEST_TOOLS_BY_LEVEL.keys) return ItemStack.EMPTY - if (!state.requiresCorrectToolForDrops()) { - return HARVEST_TOOLS_BY_LEVEL[harvestTier]!![0] - } - for (tool in HARVEST_TOOLS_BY_LEVEL[harvestTier]!!) { - if (tool.isCorrectToolForDrops(state)) { - return tool - } - } - return ItemStack.EMPTY + val toolsForTier = HARVEST_TOOLS_BY_TIER.getOrElse(harvestTier) { return ItemStack.EMPTY } + if (!state.requiresCorrectToolForDrops()) return toolsForTier[0] + return toolsForTier.firstOrNull { it.isCorrectToolForDrops(state) } ?: ItemStack.EMPTY } - private val HARVEST_TOOLS_BY_LEVEL: Map> = mapOf( + private val HARVEST_TOOLS_BY_TIER: Map> = mapOf( Tiers.WOOD to stacks(Items.WOODEN_PICKAXE, Items.WOODEN_AXE, Items.WOODEN_HOE, Items.WOODEN_SHOVEL), Tiers.STONE to stacks(Items.STONE_PICKAXE, Items.STONE_AXE, Items.STONE_HOE, Items.STONE_SHOVEL), Tiers.IRON to stacks(Items.IRON_PICKAXE, Items.IRON_AXE, Items.IRON_HOE, Items.IRON_SHOVEL), From 90abafc007109159b38a2df2d9875a0c0ad87482 Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Wed, 8 Jul 2026 22:19:06 -0400 Subject: [PATCH 14/16] Update falling block pattern --- .../java/at/petrak/hexcasting/common/lib/hex/HexActions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java index 4f267c5c8..fe35ec557 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java @@ -274,7 +274,7 @@ public class HexActions { public static final ActionRegistryEntry EDIFY = make("edify", new ActionRegistryEntry(HexPattern.fromAngles("wqaqwd", HexDir.NORTH_EAST), OpEdifySapling.INSTANCE)); public static final ActionRegistryEntry FALLING_BLOCK = make("falling_block", - new ActionRegistryEntry(HexPattern.fromAngles("wqwawqwqwqwqwqw", HexDir.EAST), OpFallingBlock.INSTANCE)); + new ActionRegistryEntry(HexPattern.fromAngles("qaqqwwddwwq", HexDir.EAST), OpFallingBlock.INSTANCE)); public static final ActionRegistryEntry IGNITE = make("ignite", new ActionRegistryEntry(HexPattern.fromAngles("aaqawawa", HexDir.SOUTH_EAST), OpIgnite.INSTANCE)); From cff1aa9b3ca11bdd58111c534dd5a9f243cd8e1d Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Wed, 8 Jul 2026 22:21:03 -0400 Subject: [PATCH 15/16] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d401546f7..7884dcf89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Added - Updated to Minecraft 1.21.1 ([#985](https://github.com/FallingColors/HexMod/pull/985)) @SuperKnux @slava110 +- Added Smelt, Freeze, Gravitate Block, Particles, and Factorial Purification, all of which were previously in Hexal ([#1183](https://github.com/FallingColors/HexMod/pull/1183)) @Robotgiggle + - Freeze now supports data-driven recipes for freezing liquids as well as blocks + - Factorial Purification now works on vectors as well as numbers, applying element-wise ### Fixed From 703b314c93fb544fae428173f2032bc7574e1f08 Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Thu, 9 Jul 2026 21:19:29 -0400 Subject: [PATCH 16/16] Add mishap for smelt/freeze with invalid recipe --- CHANGELOG.md | 1 + .../common/casting/actions/spells/OpFreeze.kt | 30 ++++++------- .../common/casting/actions/spells/OpSmelt.kt | 43 +++++++++++-------- .../hexcasting/lang/en_us.flatten.json5 | 3 ++ 4 files changed, 42 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7884dcf89..c505aa4a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Updated to Minecraft 1.21.1 ([#985](https://github.com/FallingColors/HexMod/pull/985)) @SuperKnux @slava110 - Added Smelt, Freeze, Gravitate Block, Particles, and Factorial Purification, all of which were previously in Hexal ([#1183](https://github.com/FallingColors/HexMod/pull/1183)) @Robotgiggle - Freeze now supports data-driven recipes for freezing liquids as well as blocks + - Smelt and Freeze now mishap if the target does not have a valid smelting/freezing recipe - Factorial Purification now works on vectors as well as numbers, applying element-wise ### Fixed diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFreeze.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFreeze.kt index d0343184f..9a83eb749 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFreeze.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpFreeze.kt @@ -4,6 +4,7 @@ import at.petrak.hexcasting.api.casting.* import at.petrak.hexcasting.api.casting.castables.SpellAction import at.petrak.hexcasting.api.casting.eval.CastingEnvironment import at.petrak.hexcasting.api.casting.iota.Iota +import at.petrak.hexcasting.api.casting.mishaps.MishapBadBlock import at.petrak.hexcasting.api.misc.MediaConstants import at.petrak.hexcasting.common.recipe.CopyProperties import at.petrak.hexcasting.common.recipe.HexRecipeStuffRegistry @@ -12,6 +13,7 @@ import net.minecraft.core.BlockPos import net.minecraft.server.level.ServerPlayer import net.minecraft.world.level.block.Blocks import net.minecraft.world.level.block.LiquidBlock +import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.material.Fluids import net.minecraft.world.phys.Vec3 @@ -19,31 +21,27 @@ object OpFreeze : SpellAction { override val argc = 1 override fun execute(args: List, env: CastingEnvironment): SpellAction.Result { - val toFreeze = args.getBlockPos(0, argc) + val pos = args.getBlockPos(0, argc) - env.assertPosInRangeForEditing(toFreeze) + env.assertPosInRangeForEditing(pos) + + val blockState = env.world.getBlockState(pos) + val recipes = env.world.recipeManager.getAllRecipesFor(HexRecipeStuffRegistry.FREEZE_TYPE).map{ holder -> holder.value } + val recipe = recipes.find{ it.matches(blockState) } ?: throw MishapBadBlock.of(pos, "freezable") return SpellAction.Result( - Spell(toFreeze), + Spell(pos, blockState, recipe.result), MediaConstants.DUST_UNIT, - listOf(ParticleSpray.burst(Vec3.atCenterOf(toFreeze), 1.0)) + listOf(ParticleSpray.burst(Vec3.atCenterOf(pos), 1.0)) ) } - private data class Spell(val pos: BlockPos) : RenderedSpell { + private data class Spell(val pos: BlockPos, val oldState: BlockState, val newState: BlockState) : RenderedSpell { override fun cast(env: CastingEnvironment) { - val blockState = env.world.getBlockState(pos) - - if (!IXplatAbstractions.INSTANCE.isBreakingAllowed(env.world, pos, blockState, env.castingEntity as? ServerPlayer)) + if (!IXplatAbstractions.INSTANCE.isBreakingAllowed(env.world, pos, oldState, env.castingEntity as? ServerPlayer)) return - - val recman = env.world.recipeManager - val recipes = recman.getAllRecipesFor(HexRecipeStuffRegistry.FREEZE_TYPE).map{ holder -> holder.value } - - val recipe = recipes.find{ it.matches(blockState) } - - if (recipe != null) - env.world.setBlockAndUpdate(pos, CopyProperties.copyProperties(blockState, recipe.result)) + + env.world.setBlockAndUpdate(pos, CopyProperties.copyProperties(oldState, newState)) } } } \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpSmelt.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpSmelt.kt index c6b2c18b5..aa080be03 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpSmelt.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpSmelt.kt @@ -6,6 +6,8 @@ import at.petrak.hexcasting.api.casting.eval.CastingEnvironment import at.petrak.hexcasting.api.casting.iota.EntityIota import at.petrak.hexcasting.api.casting.iota.Iota import at.petrak.hexcasting.api.casting.iota.Vec3Iota +import at.petrak.hexcasting.api.casting.mishaps.MishapBadBlock +import at.petrak.hexcasting.api.casting.mishaps.MishapBadItem import at.petrak.hexcasting.api.casting.mishaps.MishapInvalidIota import at.petrak.hexcasting.api.misc.MediaConstants import at.petrak.hexcasting.xplat.IXplatAbstractions @@ -20,6 +22,7 @@ import net.minecraft.world.item.crafting.RecipeHolder import net.minecraft.world.item.crafting.RecipeType import net.minecraft.world.item.crafting.SingleRecipeInput import net.minecraft.world.item.crafting.SmeltingRecipe +import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.phys.Vec3 import java.util.* import kotlin.math.roundToLong @@ -32,17 +35,26 @@ object OpSmelt : SpellAction { is EntityIota -> { val itemEntity = args.getItemEntity(env.world, 0, argc) env.assertEntityInRange(itemEntity) + + val oldStack = itemEntity.item + val newStack = smeltResult(oldStack.item, env) ?: throw MishapBadItem.of(itemEntity, "smeltable") + newStack.count *= oldStack.count + return SpellAction.Result( - ItemSpell(itemEntity), - (itemEntity.item.count * 0.75 * MediaConstants.DUST_UNIT).roundToLong(), + ItemSpell(itemEntity, newStack), + (oldStack.count * 0.75 * MediaConstants.DUST_UNIT).roundToLong(), listOf(ParticleSpray.burst(itemEntity.position(), 1.0)) ) } is Vec3Iota -> { val pos = args.getBlockPos(0, argc) env.assertPosInRangeForEditing(pos) + + val blockState = env.world.getBlockState(pos) + val resultStack = smeltResult(blockState.block.asItem(), env) ?: throw MishapBadBlock.of(pos, "smeltable") + return SpellAction.Result( - BlockSpell(pos), + BlockSpell(pos, blockState, resultStack), (0.75 * MediaConstants.DUST_UNIT).roundToLong(), listOf(ParticleSpray.burst(Vec3.atCenterOf(pos), 1.0)) ) @@ -66,34 +78,27 @@ object OpSmelt : SpellAction { return result } - private data class ItemSpell(val itemEntity: ItemEntity) : RenderedSpell { + private data class ItemSpell(val itemEntity: ItemEntity, val resultStack: ItemStack) : RenderedSpell { override fun cast(env: CastingEnvironment) { - val result = smeltResult(itemEntity.item.item, env) ?: return // cursed .item.item to map from ItemEntity to ItemLike to ItemStack - - result.count *= itemEntity.item.count - - env.world.addFreshEntity(ItemEntity(env.world, itemEntity.x, itemEntity.y, itemEntity.z, result.copy())) + env.world.addFreshEntity(ItemEntity(env.world, itemEntity.x, itemEntity.y, itemEntity.z, resultStack.copy())) itemEntity.remove(Entity.RemovalReason.DISCARDED) } } - private data class BlockSpell(val pos: BlockPos) : RenderedSpell { + private data class BlockSpell(val pos: BlockPos, val blockState: BlockState, val resultStack: ItemStack) : RenderedSpell { override fun cast(env: CastingEnvironment) { - val blockState = env.world.getBlockState(pos) if (!IXplatAbstractions.INSTANCE.isBreakingAllowed(env.world, pos, blockState, env.castingEntity as? ServerPlayer)) return - val itemStack = smeltResult(blockState.block.asItem(), env) ?: return - - if (itemStack.item is BlockItem) { - env.world.setBlockAndUpdate(pos, (itemStack.item as BlockItem).block.defaultBlockState()) + if (resultStack.item is BlockItem) { + env.world.setBlockAndUpdate(pos, (resultStack.item as BlockItem).block.defaultBlockState()) - if (itemStack.count > 1) { - itemStack.count -= 1 - env.world.addFreshEntity(ItemEntity(env.world, pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(), itemStack.copy())) + if (resultStack.count > 1) { + resultStack.count -= 1 + env.world.addFreshEntity(ItemEntity(env.world, pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(), resultStack.copy())) } } else { env.world.destroyBlock(pos, false, env.castingEntity as? ServerPlayer) - env.world.addFreshEntity(ItemEntity(env.world, pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(), itemStack.copy())) + env.world.addFreshEntity(ItemEntity(env.world, pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(), resultStack.copy())) // Send a block update, also copied from Ars Nouveau (this is all copied from Ars Nouveau) if (!env.world.isOutsideBuildHeight(pos)) env.world.sendBlockUpdated(pos, env.world.getBlockState(pos), env.world.getBlockState(pos), 3) // don't know how this works diff --git a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 index 3f72cc846..a28a92c1d 100644 --- a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 +++ b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 @@ -1173,11 +1173,14 @@ colorizer: "a pigment", variant: "an item with variants", placeable: "a placeable item", + smeltable: "a smeltable item", }, bad_block: { "": "Expected %s at %s, but got %s", sapling: "a sapling", + smeltable: "a smeltable block", + freezable: "a freezable block", replaceable: "somewhere to place a block", },