diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index bf5153953..39f097c6d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -60,7 +60,7 @@ jobs:
- name: JDK setup
uses: actions/setup-java@v4.2.1
with:
- java-version: 17
+ java-version: 21
distribution: temurin
- name: Build project
if: inputs.build-project
@@ -91,7 +91,7 @@ jobs:
NeoForgeMod/build/libs/*.jar
if-no-files-found: error
- name: Upload Forge jars
- if: inputs.build-project && inputs.upload-output
+ if: inputs.build-project && inputs.upload-output && false
uses: actions/upload-artifact@v4.3.1
with:
name: forge-jars
diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml
index 42abf334b..55758047a 100644
--- a/.github/workflows/publish-release.yml
+++ b/.github/workflows/publish-release.yml
@@ -33,6 +33,7 @@ jobs:
name: neoforge-jars
path: dist/neoforge
- name: Download Forge jars
+ if: false
uses: actions/download-artifact@v4.1.4
with:
name: forge-jars
@@ -95,6 +96,7 @@ jobs:
game-version-filter: releases
java: ${{ steps.load-java-version.outputs.value }}
- name: Publish Forge mod to Modrinth and CurseForge
+ if: false
uses: Kir-Antipov/mc-publish@v3.3.0
with:
modrinth-id: ${{ vars.MODRINTH_ID }}
@@ -123,8 +125,6 @@ jobs:
dist/fabric/*-@(dev|sources|javadoc).jar
dist/neoforge/!(*-@(dev|sources|javadoc)).jar
dist/neoforge/*-@(dev|sources|javadoc).jar
- dist/forge/!(*-@(dev|sources|javadoc)).jar
- dist/forge/*-@(dev|sources|javadoc).jar
changelog-file: CHANGELOG.g.md
- name: Delete build output
uses: geekyeggo/delete-artifact@v5.0.0
@@ -132,7 +132,6 @@ jobs:
name: |
fabric-jars
neoforge-jars
- forge-jars
useGlob: false
failOnError: false
diff --git a/FabricMod/build.gradle.kts b/FabricMod/build.gradle.kts
index 237fd40a4..34a0093ac 100644
--- a/FabricMod/build.gradle.kts
+++ b/FabricMod/build.gradle.kts
@@ -1,3 +1,21 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
plugins {
alias(libs.plugins.shadow)
}
@@ -77,6 +95,7 @@ tasks {
from(rootDir.resolve("COPYING"))
from(rootDir.resolve("COPYING.LESSER"))
+ from(rootDir.resolve("README.md"))
}
remapJar {
@@ -87,6 +106,7 @@ tasks {
from(rootDir.resolve("COPYING"))
from(rootDir.resolve("COPYING.LESSER"))
+ from(rootDir.resolve("README.md"))
}
sourcesJar {
diff --git a/FabricMod/src/main/java/opekope2/avm_staff/mixin/fabric/LivingEntityMixin.java b/FabricMod/src/main/java/opekope2/avm_staff/mixin/fabric/LivingEntityMixin.java
new file mode 100644
index 000000000..7ffb50757
--- /dev/null
+++ b/FabricMod/src/main/java/opekope2/avm_staff/mixin/fabric/LivingEntityMixin.java
@@ -0,0 +1,79 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+package opekope2.avm_staff.mixin.fabric;
+
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityType;
+import net.minecraft.entity.LivingEntity;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.Hand;
+import net.minecraft.world.World;
+import opekope2.avm_staff.api.StaffMod;
+import opekope2.avm_staff.api.staff.StaffHandler;
+import opekope2.avm_staff.util.StaffUtil;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+@Mixin(LivingEntity.class)
+public abstract class LivingEntityMixin extends Entity {
+ private LivingEntityMixin(EntityType> type, World world) {
+ super(type, world);
+ }
+
+ @Shadow
+ public abstract ItemStack getMainHandStack();
+
+ @Shadow
+ public abstract ItemStack getStackInHand(Hand hand);
+
+ @Inject(method = "disablesShield", at = @At("HEAD"), cancellable = true)
+ public void disableShield(CallbackInfoReturnable cir) {
+ ItemStack mainHandStack = getMainHandStack();
+ if (!mainHandStack.isIn(StaffMod.getStaffsTag())) return;
+
+ Item itemInStaff = StaffUtil.getItemInStaff(mainHandStack);
+ if (itemInStaff == null) return;
+
+ StaffHandler handlerOfItem = StaffUtil.getStaffHandlerOrDefault(itemInStaff);
+ if (handlerOfItem.disablesShield()) {
+ cir.setReturnValue(true);
+ }
+ }
+
+ @SuppressWarnings("UnreachableCode") // Calm down IDEA, this is not what it looks like. Literally
+ @Inject(method = "swingHand(Lnet/minecraft/util/Hand;Z)V", at = @At("HEAD"), cancellable = true)
+ public void swingHand(Hand hand, boolean fromServerPlayer, CallbackInfo ci) {
+ ItemStack stackInHand = getStackInHand(hand);
+ if (stackInHand.isEmpty()) return;
+ if (!stackInHand.isIn(StaffMod.getStaffsTag())) return;
+
+ Item itemInStaff = StaffUtil.getItemInStaff(stackInHand);
+ if (itemInStaff == null) return;
+
+ StaffHandler handlerOfItem = StaffUtil.getStaffHandlerOrDefault(itemInStaff);
+ if (!handlerOfItem.canSwingHand(stackInHand, getEntityWorld(), (LivingEntity) (Object) this, hand)) {
+ ci.cancel();
+ }
+ }
+}
diff --git a/FabricMod/src/main/java/opekope2/avm_staff/mixin/fabric/PiglinBrainMixin.java b/FabricMod/src/main/java/opekope2/avm_staff/mixin/fabric/PiglinBrainMixin.java
new file mode 100644
index 000000000..5485a962e
--- /dev/null
+++ b/FabricMod/src/main/java/opekope2/avm_staff/mixin/fabric/PiglinBrainMixin.java
@@ -0,0 +1,41 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+package opekope2.avm_staff.mixin.fabric;
+
+import net.minecraft.entity.LivingEntity;
+import net.minecraft.entity.mob.PiglinBrain;
+import net.minecraft.item.ItemStack;
+import opekope2.avm_staff.api.item.CrownItem;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+@Mixin(PiglinBrain.class)
+public abstract class PiglinBrainMixin {
+ @Inject(method = "wearsGoldArmor", at = @At("HEAD"), cancellable = true)
+ private static void wearsGoldArmor(LivingEntity entity, CallbackInfoReturnable cir) {
+ for (ItemStack armorStack : entity.getArmorItems()) {
+ if (armorStack.getItem() instanceof CrownItem) {
+ cir.setReturnValue(true);
+ return;
+ }
+ }
+ }
+}
diff --git a/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/StaffMod.kt b/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/StaffMod.kt
index 418d25290..a2ecda7fc 100644
--- a/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/StaffMod.kt
+++ b/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/StaffMod.kt
@@ -18,72 +18,41 @@
package opekope2.avm_staff.internal.fabric
-import net.fabricmc.api.EnvType
import net.fabricmc.api.ModInitializer
-import net.fabricmc.fabric.api.event.player.AttackBlockCallback
import net.fabricmc.fabric.api.event.player.AttackEntityCallback
-import net.fabricmc.fabric.api.item.v1.FabricItemSettings
-import net.fabricmc.fabric.api.particle.v1.FabricParticleTypes
-import net.fabricmc.loader.api.FabricLoader
import net.minecraft.entity.Entity
import net.minecraft.entity.player.PlayerEntity
-import net.minecraft.item.Item
-import net.minecraft.particle.DefaultParticleType
-import net.minecraft.registry.Registries
-import net.minecraft.registry.Registry
-import net.minecraft.registry.RegistryKeys
-import net.minecraft.registry.tag.TagKey
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
-import net.minecraft.util.Identifier
import net.minecraft.util.hit.EntityHitResult
import net.minecraft.world.World
-import opekope2.avm_staff.IStaffMod
-import opekope2.avm_staff.api.item.StaffItem
-import opekope2.avm_staff.internal.event_handler.attackBlock
-import opekope2.avm_staff.internal.event_handler.attackEntity
-import opekope2.avm_staff.internal.fabric.item.FabricStaffItem
-import opekope2.avm_staff.util.MOD_ID
+import opekope2.avm_staff.api.staffsTag
+import opekope2.avm_staff.util.contains
+import opekope2.avm_staff.util.itemInStaff
+import opekope2.avm_staff.util.staffHandler
@Suppress("unused")
-object StaffMod : ModInitializer, IStaffMod {
- override val staffItem: StaffItem = Registry.register(
- Registries.ITEM,
- Identifier(MOD_ID, "staff"),
- FabricStaffItem(FabricItemSettings().maxCount(1))
- )
-
- override val isPhysicalClient: Boolean
- get() = FabricLoader.getInstance().environmentType == EnvType.CLIENT
-
- override val staffsTag: TagKey- = TagKey.of(RegistryKeys.ITEM, Identifier(MOD_ID, "staffs"))
-
- override val flamethrowerParticleType: DefaultParticleType = Registry.register(
- Registries.PARTICLE_TYPE,
- Identifier(MOD_ID, "flame"),
- FabricParticleTypes.simple()
- )
-
- override val soulFlamethrowerParticleType: DefaultParticleType = Registry.register(
- Registries.PARTICLE_TYPE,
- Identifier(MOD_ID, "soul_fire_flame"),
- FabricParticleTypes.simple()
- )
-
+object StaffMod : ModInitializer {
override fun onInitialize() {
- AttackBlockCallback.EVENT.register(::attackBlock)
AttackEntityCallback.EVENT.register(::handleEntityAttackEvent)
}
+ @Suppress("UNUSED_PARAMETER")
private fun handleEntityAttackEvent(
player: PlayerEntity,
world: World,
hand: Hand,
target: Entity,
- @Suppress("UNUSED_PARAMETER") hit: EntityHitResult?
+ hit: EntityHitResult?
): ActionResult {
- if (world.isClient) return ActionResult.PASS // Handled with mixin
+ val itemStack = player.getStackInHand(hand)
+ if (itemStack !in staffsTag) return ActionResult.PASS
+
+ val itemInStaff = itemStack.itemInStaff ?: return ActionResult.PASS
+ val staffHandler = itemInStaff.staffHandler ?: return ActionResult.PASS
- return attackEntity(player, world, hand, target)
+ val result = staffHandler.attackEntity(itemStack, world, player, target, hand)
+ return if (result.interruptsFurtherEvaluation()) ActionResult.SUCCESS
+ else ActionResult.PASS
}
}
diff --git a/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/StaffModClient.kt b/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/StaffModClient.kt
index a3bde36d5..d290456fd 100644
--- a/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/StaffModClient.kt
+++ b/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/StaffModClient.kt
@@ -21,65 +21,22 @@ package opekope2.avm_staff.internal.fabric
import net.fabricmc.api.ClientModInitializer
import net.fabricmc.api.EnvType
import net.fabricmc.api.Environment
-import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
-import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper
-import net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin
-import net.fabricmc.fabric.api.client.model.loading.v1.ModelModifier
import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry
-import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents
import net.minecraft.client.item.ModelPredicateProviderRegistry
-import net.minecraft.client.render.model.UnbakedModel
-import net.minecraft.client.render.model.json.JsonUnbakedModel
-import net.minecraft.item.ItemGroups
-import net.minecraft.item.Items
-import opekope2.avm_staff.IStaffMod
+import opekope2.avm_staff.api.flamethrowerParticleType
import opekope2.avm_staff.api.particle.FlamethrowerParticle
-import opekope2.avm_staff.internal.event_handler.ADD_REMOVE_KEYBINDING
-import opekope2.avm_staff.internal.event_handler.handleKeyBindings
-import opekope2.avm_staff.internal.fabric.item.model.UnbakedFabricStaffItemModel
-import opekope2.avm_staff.internal.registerModelPredicateProviders
-import opekope2.avm_staff.util.MOD_ID
+import opekope2.avm_staff.api.soulFlamethrowerParticleType
+import opekope2.avm_staff.internal.model.registerModelPredicateProviders
@Suppress("unused")
@Environment(EnvType.CLIENT)
object StaffModClient : ClientModInitializer {
override fun onInitializeClient() {
- ItemGroupEvents.modifyEntriesEvent(ItemGroups.TOOLS).register { entries ->
- entries.addAfter(Items.NETHERITE_HOE, StaffMod.staffItem)
+ ParticleFactoryRegistry.getInstance().apply {
+ register(flamethrowerParticleType.get(), FlamethrowerParticle::Factory)
+ register(soulFlamethrowerParticleType.get(), FlamethrowerParticle::Factory)
}
- ItemGroupEvents.modifyEntriesEvent(ItemGroups.COMBAT).register { entries ->
- entries.addAfter(Items.TRIDENT, StaffMod.staffItem)
- }
-
- ModelLoadingPlugin.register(::modelLoadingPlugin)
-
- KeyBindingHelper.registerKeyBinding(ADD_REMOVE_KEYBINDING)
-
- ClientTickEvents.END_CLIENT_TICK.register(::handleKeyBindings)
-
- ParticleFactoryRegistry.getInstance().register(
- IStaffMod.get().flamethrowerParticleType,
- FlamethrowerParticle::Factory
- )
- ParticleFactoryRegistry.getInstance().register(
- IStaffMod.get().soulFlamethrowerParticleType,
- FlamethrowerParticle::Factory
- )
registerModelPredicateProviders(ModelPredicateProviderRegistry::register)
}
-
- private fun modelLoadingPlugin(pluginContext: ModelLoadingPlugin.Context) {
- pluginContext.modifyModelBeforeBake().register(::modifyModelBeforeBake)
- }
-
- private fun modifyModelBeforeBake(model: UnbakedModel, context: ModelModifier.BeforeBake.Context): UnbakedModel {
- if (context.id().namespace != MOD_ID) return model
-
- return when (context.id().path) {
- // TODO hardcoded paths
- "staff", "item/staff_in_use" -> UnbakedFabricStaffItemModel(model as JsonUnbakedModel) // FIXME
- else -> model
- }
- }
}
diff --git a/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/StaffModPlatformImpl.kt b/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/StaffModPlatformImpl.kt
new file mode 100644
index 000000000..6df8cd726
--- /dev/null
+++ b/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/StaffModPlatformImpl.kt
@@ -0,0 +1,43 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+@file: JvmName("StaffModPlatformImpl")
+@file: Suppress("unused")
+
+package opekope2.avm_staff.internal.fabric
+
+import net.fabricmc.api.EnvType
+import net.fabricmc.fabric.api.client.rendering.v1.BuiltinItemRendererRegistry
+import net.fabricmc.loader.api.FabricLoader
+import net.minecraft.item.Item
+import opekope2.avm_staff.api.item.CrownItem
+import opekope2.avm_staff.api.item.StaffItem
+import opekope2.avm_staff.api.item.renderer.StaffRenderer
+import opekope2.avm_staff.internal.fabric.item.FabricStaffItem
+
+fun createStaffItem(settings: Item.Settings): StaffItem = FabricStaffItem(settings)
+
+fun createStaffRendererItem(settings: Item.Settings): Item {
+ return Item(settings).also { item ->
+ if (FabricLoader.getInstance().environmentType == EnvType.CLIENT) {
+ BuiltinItemRendererRegistry.INSTANCE.register(item, StaffRenderer::renderStaff)
+ }
+ }
+}
+
+fun createCrownItem(settings: Item.Settings): CrownItem = CrownItem(settings)
diff --git a/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/item/FabricStaffItem.kt b/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/item/FabricStaffItem.kt
index 18c31282f..db0f4a948 100644
--- a/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/item/FabricStaffItem.kt
+++ b/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/item/FabricStaffItem.kt
@@ -18,37 +18,36 @@
package opekope2.avm_staff.internal.fabric.item
-import com.google.common.collect.Multimap
+import net.fabricmc.api.EnvType
+import net.fabricmc.fabric.api.client.rendering.v1.BuiltinItemRendererRegistry
import net.fabricmc.fabric.api.item.v1.FabricItem
-import net.minecraft.entity.EquipmentSlot
-import net.minecraft.entity.attribute.EntityAttribute
-import net.minecraft.entity.attribute.EntityAttributeModifier
+import net.fabricmc.loader.api.FabricLoader
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.util.Hand
import opekope2.avm_staff.api.item.StaffItem
-import opekope2.avm_staff.util.handlerOfItemOrFallback
+import opekope2.avm_staff.api.item.renderer.StaffRenderer
import opekope2.avm_staff.util.itemInStaff
+import opekope2.avm_staff.util.staffHandlerOrDefault
class FabricStaffItem(settings: Item.Settings) : StaffItem(settings), FabricItem {
- override fun allowNbtUpdateAnimation(
+ init {
+ if (FabricLoader.getInstance().environmentType == EnvType.CLIENT) {
+ BuiltinItemRendererRegistry.INSTANCE.register(this, StaffRenderer::renderStaff)
+ }
+ }
+
+ override fun allowComponentsUpdateAnimation(
player: PlayerEntity,
hand: Hand,
oldStack: ItemStack,
newStack: ItemStack
): Boolean {
- val oldHandler = oldStack.itemInStaff.handlerOfItemOrFallback
- val newHandler = newStack.itemInStaff.handlerOfItemOrFallback
+ val oldHandler = oldStack.itemInStaff.staffHandlerOrDefault
+ val newHandler = newStack.itemInStaff.staffHandlerOrDefault
return if (oldHandler !== newHandler) true
- else oldHandler.allowNbtUpdateAnimation(oldStack, newStack, player, hand)
- }
-
- override fun getAttributeModifiers(
- stack: ItemStack,
- slot: EquipmentSlot
- ): Multimap {
- return stack.itemInStaff.handlerOfItemOrFallback.getAttributeModifiers(stack, slot)
+ else oldHandler.allowComponentsUpdateAnimation(oldStack, newStack, player, hand)
}
}
diff --git a/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/item/model/BakedFabricStaffItemModel.kt b/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/item/model/BakedFabricStaffItemModel.kt
deleted file mode 100644
index d8ba1c49c..000000000
--- a/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/item/model/BakedFabricStaffItemModel.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2023-2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.internal.fabric.item.model
-
-import net.fabricmc.api.EnvType
-import net.fabricmc.api.Environment
-import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel
-import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
-import net.minecraft.client.render.model.BakedModel
-import net.minecraft.item.ItemStack
-import net.minecraft.registry.Registries
-import net.minecraft.util.Identifier
-import net.minecraft.util.math.random.Random
-import opekope2.avm_staff.api.item.model.IStaffItemBakedModel
-import opekope2.avm_staff.util.isItemInStaff
-import opekope2.avm_staff.util.itemInStaff
-import java.util.function.Supplier
-
-@Environment(EnvType.CLIENT)
-class BakedFabricStaffItemModel(
- original: BakedModel,
- private val itemModels: Map,
- private val missingModel: BakedModel
-) : BakedModel by original {
- override fun emitItemQuads(stack: ItemStack, randomSupplier: Supplier, context: RenderContext) {
- super.emitItemQuads(stack, randomSupplier, context)
-
- if (!stack.isItemInStaff) return
-
- val itemStack = stack.itemInStaff ?: return
-
- val model = itemModels[Registries.ITEM.getId(itemStack.item)]?.getModel(stack) ?: missingModel
- (model as FabricBakedModel).emitItemQuads(stack, randomSupplier, context)
- }
-
- override fun isVanillaAdapter() = false
-}
diff --git a/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/item/model/UnbakedFabricStaffItemModel.kt b/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/item/model/UnbakedFabricStaffItemModel.kt
deleted file mode 100644
index ec53b66ba..000000000
--- a/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/fabric/item/model/UnbakedFabricStaffItemModel.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.internal.fabric.item.model
-
-import net.fabricmc.api.EnvType
-import net.fabricmc.api.Environment
-import net.minecraft.client.render.model.BakedModel
-import net.minecraft.client.render.model.Baker
-import net.minecraft.client.render.model.ModelBakeSettings
-import net.minecraft.client.render.model.ModelLoader
-import net.minecraft.client.render.model.json.JsonUnbakedModel
-import net.minecraft.client.texture.Sprite
-import net.minecraft.client.util.SpriteIdentifier
-import net.minecraft.util.Identifier
-import opekope2.avm_staff.api.item.model.IStaffItemBakedModel
-import opekope2.avm_staff.internal.item.model.UnbakedStaffItemModel
-import java.util.function.Function
-
-@Environment(EnvType.CLIENT)
-class UnbakedFabricStaffItemModel(private val original: JsonUnbakedModel) : UnbakedStaffItemModel(original) {
- override fun bake(
- baker: Baker,
- textureGetter: Function,
- rotationContainer: ModelBakeSettings,
- modelId: Identifier,
- itemModels: Map
- ): BakedModel? {
- return BakedFabricStaffItemModel(
- original.bake(baker, textureGetter, rotationContainer, modelId) ?: return null,
- itemModels,
- baker.bake(ModelLoader.MISSING_ID, rotationContainer)!!
- )
- }
-}
diff --git a/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/platform/fabric/RenderPlatformImpl.kt b/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/platform/fabric/RenderPlatformImpl.kt
deleted file mode 100644
index ccbceffbc..000000000
--- a/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/platform/fabric/RenderPlatformImpl.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-@file: JvmName("RenderPlatformImpl")
-
-package opekope2.avm_staff.internal.platform.fabric
-
-import net.fabricmc.fabric.api.renderer.v1.RendererAccess
-import net.minecraft.client.render.VertexConsumer
-import net.minecraft.client.render.model.BakedQuad
-import net.minecraft.client.texture.Sprite
-import opekope2.avm_staff.api.render.IQuadBakerVertexConsumer
-import java.util.function.Consumer
-
-fun getQuadBakerVertexConsumer(sprite: Sprite, bakedQuadConsumer: Consumer): IQuadBakerVertexConsumer {
- return FabricQuadBakerVertexConsumer(sprite, bakedQuadConsumer)
-}
-
-private class FabricQuadBakerVertexConsumer(
- override var sprite: Sprite,
- private val bakedQuadConsumer: Consumer
-) : IQuadBakerVertexConsumer {
- private val meshBuilder = renderer.meshBuilder()
- private var emitter = meshBuilder.emitter
- private var vertex = 0
-
- override fun vertex(x: Double, y: Double, z: Double): VertexConsumer {
- emitter.pos(vertex, x.toFloat(), y.toFloat(), z.toFloat())
- return this
- }
-
- override fun color(red: Int, green: Int, blue: Int, alpha: Int): VertexConsumer {
- emitter.color(vertex, (alpha shl 24) or (red shl 16) or (green shl 8) or blue)
- return this
- }
-
- override fun texture(u: Float, v: Float): VertexConsumer {
- emitter.uv(vertex, u, v)
- return this
- }
-
- override fun overlay(u: Int, v: Int): VertexConsumer {
- // Not supported by Fabric Rendering API
- return this
- }
-
- override fun light(u: Int, v: Int): VertexConsumer {
- emitter.lightmap(vertex, u or (v shl 16))
- return this
- }
-
- override fun normal(x: Float, y: Float, z: Float): VertexConsumer {
- emitter.normal(vertex, x, y, z)
- return this
- }
-
- override fun next() {
- if (++vertex != 4) return
-
- bakedQuadConsumer.accept(emitter.toBakedQuad(sprite))
-
- vertex = 0
- emitter = meshBuilder.emitter
- }
-
- override fun fixedColor(red: Int, green: Int, blue: Int, alpha: Int) {
- }
-
- override fun unfixColor() {
- }
-
- private companion object {
- private val renderer = RendererAccess.INSTANCE.renderer
- ?: throw IllegalStateException("No Fabric renderer is available")
- }
-}
diff --git a/FabricMod/src/main/resources/avm_staff_fabric.mixins.json b/FabricMod/src/main/resources/avm_staff_fabric.mixins.json
new file mode 100644
index 000000000..f1bdac7b3
--- /dev/null
+++ b/FabricMod/src/main/resources/avm_staff_fabric.mixins.json
@@ -0,0 +1,13 @@
+{
+ "required": true,
+ "package": "opekope2.avm_staff.mixin.fabric",
+ "compatibilityLevel": "JAVA_21",
+ "minVersion": "0.8",
+ "injectors": {
+ "defaultRequire": 1
+ },
+ "mixins": [
+ "LivingEntityMixin",
+ "PiglinBrainMixin"
+ ]
+}
diff --git a/FabricMod/src/main/resources/fabric.mod.json b/FabricMod/src/main/resources/fabric.mod.json
index d3423993f..c37e3a037 100644
--- a/FabricMod/src/main/resources/fabric.mod.json
+++ b/FabricMod/src/main/resources/fabric.mod.json
@@ -2,11 +2,15 @@
"schemaVersion": 1,
"id": "avm_staff",
"version": "$version",
- "name": "Staff Mod (AvM Shorts)",
- "description": "Staff from Animation vs Minecraft Shorts",
+ "name": "Staff Mod (Animation vs Minecraft)",
+ "description": "AvM Staff Mod is a fan-made, mostly canonically accurate, and close to vanilla mod adding staffs from Animation vs Minecraft series to the latest version of Minecraft: Java Edition",
"authors": [
"opekope2"
],
+ "contributors": [
+ "Brother_OliviƤr",
+ "Ink&Soul"
+ ],
"contact": {
"homepage": "https://opekope2.dev/StaffMod",
"issues": "https://github.com/opekope2/StaffMod/issues",
@@ -21,24 +25,49 @@
"adapter": "kotlin",
"value": "opekope2.avm_staff.internal.fabric.StaffMod"
},
+ {
+ "adapter": "kotlin",
+ "value": "opekope2.avm_staff.internal.InitializerKt::registerContent"
+ },
{
"adapter": "kotlin",
"value": "opekope2.avm_staff.internal.InitializerKt::initializeNetworking"
},
{
"adapter": "kotlin",
- "value": "opekope2.avm_staff.internal.staff_item_handler.VanillaStaffItemHandlersKt::registerVanillaStaffItemHandlers"
+ "value": "opekope2.avm_staff.internal.InitializerKt::subscribeToEvents"
+ },
+ {
+ "adapter": "kotlin",
+ "value": "opekope2.avm_staff.internal.staff_handler.VanillaStaffHandlersKt::registerVanillaStaffHandlers"
}
],
"client": [
{
"adapter": "kotlin",
"value": "opekope2.avm_staff.internal.fabric.StaffModClient"
+ },
+ {
+ "adapter": "kotlin",
+ "value": "opekope2.avm_staff.internal.InitializerKt::registerClientContent"
+ },
+ {
+ "adapter": "kotlin",
+ "value": "opekope2.avm_staff.internal.InitializerKt::registerSmithingTableTextures"
+ },
+ {
+ "adapter": "kotlin",
+ "value": "opekope2.avm_staff.internal.InitializerKt::subscribeToClientEvents"
+ },
+ {
+ "adapter": "kotlin",
+ "value": "opekope2.avm_staff.internal.staff_handler.VanillaStaffHandlersKt::registerVanillaStaffItemRenderers"
}
]
},
"mixins": [
- "avm_staff.mixins.json"
+ "avm_staff.mixins.json",
+ "avm_staff_fabric.mixins.json"
],
"depends": {
"fabric-api": ">=$fabric_api",
diff --git a/ForgeMod/Fabric.license b/ForgeMod/Fabric.license
deleted file mode 100644
index 8dada3eda..000000000
--- a/ForgeMod/Fabric.license
+++ /dev/null
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "{}"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright {yyyy} {name of copyright owner}
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/ForgeMod/build.gradle.kts b/ForgeMod/build.gradle.kts
index 984d42fb1..c81fc5da0 100644
--- a/ForgeMod/build.gradle.kts
+++ b/ForgeMod/build.gradle.kts
@@ -1,3 +1,21 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
plugins {
alias(libs.plugins.shadow)
}
@@ -56,7 +74,6 @@ loom {
extraAccessWideners.add(loom.accessWidenerPath.get().asFile.name)
mixinConfig("avm_staff.mixins.json")
- mixinConfig("avm_staff_forge.mixins.json")
}
mods {
@@ -95,7 +112,6 @@ tasks {
from(rootDir.resolve("COPYING"))
from(rootDir.resolve("COPYING.LESSER"))
- from(projectDir.resolve("Fabric.license"))
}
remapJar {
@@ -106,7 +122,6 @@ tasks {
from(rootDir.resolve("COPYING"))
from(rootDir.resolve("COPYING.LESSER"))
- from(projectDir.resolve("Fabric.license"))
}
sourcesJar {
diff --git a/ForgeMod/src/main/java/opekope2/avm_staff/mixin/forge/ClientPlayerInteractionManagerMixin.java b/ForgeMod/src/main/java/opekope2/avm_staff/mixin/forge/ClientPlayerInteractionManagerMixin.java
deleted file mode 100644
index 1ebb471b0..000000000
--- a/ForgeMod/src/main/java/opekope2/avm_staff/mixin/forge/ClientPlayerInteractionManagerMixin.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package opekope2.avm_staff.mixin.forge;
-
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.network.ClientPlayerInteractionManager;
-import net.minecraft.client.network.SequencedPacketCreator;
-import net.minecraft.client.world.ClientWorld;
-import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
-import net.minecraft.util.ActionResult;
-import net.minecraft.util.Hand;
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.Direction;
-import net.minecraft.world.GameMode;
-import opekope2.avm_staff.internal.event_handler.StaffAttackHandlerKt;
-import org.spongepowered.asm.mixin.Final;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
-
-@Mixin(ClientPlayerInteractionManager.class)
-public abstract class ClientPlayerInteractionManagerMixin {
- @Shadow
- @Final
- private MinecraftClient client;
-
- @Shadow
- private GameMode gameMode;
-
- @Shadow
- protected abstract void sendSequencedPacket(ClientWorld clientWorld, SequencedPacketCreator supplier);
-
- // Change method to directly call Staff Mod instead of an exposed event
- @Inject(
- method = "attackBlock",
- at = @At(value = "INVOKE", target = "Lnet/minecraft/world/GameMode;isCreative()Z", ordinal = 0),
- cancellable = true
- )
- private void attackBlock(BlockPos pos, Direction direction, CallbackInfoReturnable info) {
- assert client.player != null;
- assert client.world != null;
-
- ActionResult result = StaffAttackHandlerKt.attackBlock(client.player, client.world, Hand.MAIN_HAND, pos, direction);
-
- if (result != ActionResult.PASS) {
- // Returning true will spawn particles and trigger the animation of the hand -> only for SUCCESS.
- info.setReturnValue(result == ActionResult.SUCCESS);
-
- // We also need to let the server process the action if it's accepted.
- if (result.isAccepted()) {
- sendSequencedPacket(client.world, id -> new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, pos, direction, id));
- }
- }
- }
-
- @Inject(
- method = "updateBlockBreakingProgress",
- at = @At(value = "INVOKE", target = "Lnet/minecraft/world/GameMode;isCreative()Z", ordinal = 0),
- cancellable = true
- )
- private void updateBlockBreakingProgress(BlockPos pos, Direction direction, CallbackInfoReturnable info) {
- if (gameMode.isCreative()) {
- attackBlock(pos, direction, info);
- }
- }
-
- // Inline fabric_fireAttackBlockCallback
- // Remove unused mixins: fabric$onBlockBroken, interactBlock, interactItem, attackEntity
-}
diff --git a/ForgeMod/src/main/java/opekope2/avm_staff/mixin/forge/ModelLoaderBakerImplMixin.java b/ForgeMod/src/main/java/opekope2/avm_staff/mixin/forge/ModelLoaderBakerImplMixin.java
deleted file mode 100644
index b8aa98105..000000000
--- a/ForgeMod/src/main/java/opekope2/avm_staff/mixin/forge/ModelLoaderBakerImplMixin.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2016-2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.mixin.forge;
-
-import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
-import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
-import net.minecraft.client.render.model.Baker;
-import net.minecraft.client.render.model.UnbakedModel;
-import net.minecraft.client.render.model.json.JsonUnbakedModel;
-import net.minecraft.util.Identifier;
-import opekope2.avm_staff.internal.forge.item.model.UnbakedForgeStaffItemModel;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Coerce;
-
-import static opekope2.avm_staff.util.Constants.MOD_ID;
-
-// Forge geometry loaders are way more difficult, than porting a Fabric mixin
-@Mixin(targets = "net/minecraft/client/render/model/ModelLoader$BakerImpl")
-public class ModelLoaderBakerImplMixin {
- // Change method to directly call Staff Mod instead of an exposed event
- // Ignore this error, IntelliJ/mcdev is not looking at the correct jar
- @WrapOperation(
- method = "bake(Lnet/minecraft/util/Identifier;Lnet/minecraft/client/render/model/ModelBakeSettings;Ljava/util/function/Function;)Lnet/minecraft/client/render/model/BakedModel;",
- at = @At(
- value = "INVOKE",
- target = "Lnet/minecraft/client/render/model/ModelLoader$BakerImpl;getOrLoadModel(Lnet/minecraft/util/Identifier;)Lnet/minecraft/client/render/model/UnbakedModel;"
- )
- )
- private UnbakedModel modifyUnbakedModel(@Coerce Baker thiz, Identifier id, Operation original) {
- UnbakedModel model = original.call(thiz, id);
- if (!MOD_ID.equals(id.getNamespace())) return model;
-
- return switch (id.getPath()) {
- // TODO hardcoded paths
- case "staff", "item/staff_in_use" -> new UnbakedForgeStaffItemModel((JsonUnbakedModel) model); // FIXME
- default -> model;
- };
- }
-
- // Remove unused mixins: invokeModifyAfterBake
-}
diff --git a/ForgeMod/src/main/java/opekope2/avm_staff/mixin/forge/ServerPlayerEntityMixin.java b/ForgeMod/src/main/java/opekope2/avm_staff/mixin/forge/ServerPlayerEntityMixin.java
deleted file mode 100644
index e2c32284c..000000000
--- a/ForgeMod/src/main/java/opekope2/avm_staff/mixin/forge/ServerPlayerEntityMixin.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package opekope2.avm_staff.mixin.forge;
-
-import net.minecraft.entity.Entity;
-import net.minecraft.server.network.ServerPlayerEntity;
-import net.minecraft.util.ActionResult;
-import net.minecraft.util.Hand;
-import opekope2.avm_staff.internal.event_handler.StaffAttackHandlerKt;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-
-@Mixin(ServerPlayerEntity.class)
-public class ServerPlayerEntityMixin {
- // Change method to directly call Staff Mod instead of an exposed event
- @Inject(method = "attack", at = @At("HEAD"), cancellable = true)
- public void onPlayerInteractEntity(Entity target, CallbackInfo info) {
- ServerPlayerEntity player = (ServerPlayerEntity) (Object) this;
- ActionResult result = StaffAttackHandlerKt.attackEntity(player, player.getEntityWorld(), Hand.MAIN_HAND, target);
-
- if (result != ActionResult.PASS) {
- info.cancel();
- }
- }
-}
diff --git a/ForgeMod/src/main/java/opekope2/avm_staff/mixin/forge/ServerPlayerInteractionManagerMixin.java b/ForgeMod/src/main/java/opekope2/avm_staff/mixin/forge/ServerPlayerInteractionManagerMixin.java
deleted file mode 100644
index 65c0e7ad2..000000000
--- a/ForgeMod/src/main/java/opekope2/avm_staff/mixin/forge/ServerPlayerInteractionManagerMixin.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package opekope2.avm_staff.mixin.forge;
-
-import net.minecraft.block.entity.BlockEntity;
-import net.minecraft.network.listener.ClientPlayPacketListener;
-import net.minecraft.network.packet.Packet;
-import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
-import net.minecraft.network.packet.s2c.play.BlockUpdateS2CPacket;
-import net.minecraft.server.network.ServerPlayerEntity;
-import net.minecraft.server.network.ServerPlayerInteractionManager;
-import net.minecraft.server.world.ServerWorld;
-import net.minecraft.util.ActionResult;
-import net.minecraft.util.Hand;
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.Direction;
-import opekope2.avm_staff.internal.event_handler.StaffAttackHandlerKt;
-import org.spongepowered.asm.mixin.Final;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-
-@Mixin(ServerPlayerInteractionManager.class)
-public abstract class ServerPlayerInteractionManagerMixin {
- @Shadow
- protected ServerWorld world;
-
- @Shadow
- @Final
- protected ServerPlayerEntity player;
-
- // Change method to directly call Staff Mod instead of an exposed event
- @Inject(at = @At("HEAD"), method = "processBlockBreakingAction", cancellable = true)
- public void startBlockBreak(BlockPos pos, PlayerActionC2SPacket.Action playerAction, Direction direction, int worldHeight, int i, CallbackInfo info) {
- if (playerAction != PlayerActionC2SPacket.Action.START_DESTROY_BLOCK) return;
-
- ActionResult result = StaffAttackHandlerKt.attackBlock(player, world, Hand.MAIN_HAND, pos, direction);
-
- if (result == ActionResult.PASS) return;
-
- // The client might have broken the block on its side, so make sure to let it know.
- this.player.networkHandler.sendPacket(new BlockUpdateS2CPacket(world, pos));
-
- if (world.getBlockState(pos).hasBlockEntity()) {
- BlockEntity blockEntity = world.getBlockEntity(pos);
-
- if (blockEntity != null) {
- Packet updatePacket = blockEntity.toUpdatePacket();
-
- if (updatePacket != null) {
- this.player.networkHandler.sendPacket(updatePacket);
- }
- }
- }
-
- info.cancel();
- }
-
- // Remove unused mixins: interactBlock, interactItem, breakBlock, onBlockBroken
-}
diff --git a/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/StaffMod.kt b/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/StaffMod.kt
index ed6cdc265..171d6347d 100644
--- a/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/StaffMod.kt
+++ b/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/StaffMod.kt
@@ -18,64 +18,25 @@
package opekope2.avm_staff.internal.forge
-import net.minecraft.item.Item
-import net.minecraft.particle.DefaultParticleType
-import net.minecraft.registry.tag.ItemTags
-import net.minecraft.registry.tag.TagKey
-import net.minecraft.util.Identifier
+import dev.architectury.platform.forge.EventBuses
import net.minecraftforge.api.distmarker.Dist
import net.minecraftforge.fml.common.Mod
-import net.minecraftforge.registries.DeferredRegister
-import net.minecraftforge.registries.ForgeRegistries
-import opekope2.avm_staff.IStaffMod
-import opekope2.avm_staff.api.item.StaffItem
-import opekope2.avm_staff.internal.forge.item.ForgeStaffItem
import opekope2.avm_staff.internal.initializeNetworking
-import opekope2.avm_staff.internal.staff_item_handler.registerVanillaStaffItemHandlers
+import opekope2.avm_staff.internal.registerContent
+import opekope2.avm_staff.internal.staff_handler.registerVanillaStaffHandlers
+import opekope2.avm_staff.internal.subscribeToEvents
import opekope2.avm_staff.util.MOD_ID
-import thedarkcolour.kotlinforforge.forge.DIST
import thedarkcolour.kotlinforforge.forge.MOD_BUS
import thedarkcolour.kotlinforforge.forge.runWhenOn
@Mod(MOD_ID)
-object StaffMod : IStaffMod {
- private val ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MOD_ID)
-
- private val STAFF_ITEM = ITEMS.register("staff") { ForgeStaffItem(Item.Settings().maxCount(1)) }
-
- private val PARTICLE_TYPES = DeferredRegister.create(ForgeRegistries.PARTICLE_TYPES, MOD_ID)
-
- private val FLAMETHROWER_PARTICLE_TYPE = PARTICLE_TYPES.register("flame") {
- DefaultParticleType(false)
- }
-
- private val SOUL_FLAMETHROWER_PARTICLE_TYPE = PARTICLE_TYPES.register("soul_fire_flame") {
- DefaultParticleType(false)
- }
-
+object StaffMod {
init {
- initialize()
+ EventBuses.registerModEventBus(MOD_ID, MOD_BUS)
+ registerContent()
initializeNetworking()
- registerVanillaStaffItemHandlers()
+ subscribeToEvents()
+ registerVanillaStaffHandlers()
runWhenOn(Dist.CLIENT, StaffModClient::initializeClient)
}
-
- override val staffItem: StaffItem
- get() = STAFF_ITEM.get()
-
- override val isPhysicalClient: Boolean
- get() = DIST.isClient
-
- override val staffsTag: TagKey
- = ItemTags.create(Identifier(MOD_ID, "staffs"))
-
- override val flamethrowerParticleType: DefaultParticleType
- get() = FLAMETHROWER_PARTICLE_TYPE.get()
-
- override val soulFlamethrowerParticleType: DefaultParticleType
- get() = SOUL_FLAMETHROWER_PARTICLE_TYPE.get()
-
- private fun initialize() {
- ITEMS.register(MOD_BUS)
- PARTICLE_TYPES.register(MOD_BUS)
- }
}
diff --git a/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/StaffModClient.kt b/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/StaffModClient.kt
index 4acdef1b1..4fecce391 100644
--- a/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/StaffModClient.kt
+++ b/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/StaffModClient.kt
@@ -18,76 +18,32 @@
package opekope2.avm_staff.internal.forge
-import net.minecraft.client.MinecraftClient
import net.minecraft.client.item.ModelPredicateProviderRegistry
-import net.minecraft.item.ItemGroup.StackVisibility.PARENT_AND_SEARCH_TABS
-import net.minecraft.item.ItemGroups
-import net.minecraft.item.ItemStack
-import net.minecraft.item.Items
import net.minecraftforge.api.distmarker.Dist
import net.minecraftforge.api.distmarker.OnlyIn
-import net.minecraftforge.client.event.RegisterKeyMappingsEvent
-import net.minecraftforge.client.event.RegisterParticleProvidersEvent
-import net.minecraftforge.event.BuildCreativeModeTabContentsEvent
-import net.minecraftforge.event.TickEvent
import net.minecraftforge.eventbus.api.SubscribeEvent
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent
-import opekope2.avm_staff.IStaffMod
-import opekope2.avm_staff.api.particle.FlamethrowerParticle
-import opekope2.avm_staff.internal.event_handler.ADD_REMOVE_KEYBINDING
-import opekope2.avm_staff.internal.event_handler.handleKeyBindings
-import opekope2.avm_staff.internal.platform.forge.getStaffMod
-import opekope2.avm_staff.internal.registerModelPredicateProviders
-import thedarkcolour.kotlinforforge.forge.FORGE_BUS
+import opekope2.avm_staff.internal.model.registerModelPredicateProviders
+import opekope2.avm_staff.internal.registerClientContent
+import opekope2.avm_staff.internal.registerSmithingTableTextures
+import opekope2.avm_staff.internal.staff_handler.registerVanillaStaffItemRenderers
+import opekope2.avm_staff.internal.subscribeToClientEvents
import thedarkcolour.kotlinforforge.forge.MOD_BUS
@OnlyIn(Dist.CLIENT)
object StaffModClient {
fun initializeClient() {
+ registerClientContent()
+ registerSmithingTableTextures()
+ subscribeToClientEvents()
+ registerVanillaStaffItemRenderers()
MOD_BUS.register(this)
- FORGE_BUS.register(javaClass)
}
@SubscribeEvent
fun initializeClient(event: FMLClientSetupEvent) {
event.enqueueWork {
- registerModelPredicateProviders(ModelPredicateProviderRegistry::register)
+ registerModelPredicateProviders(ModelPredicateProviderRegistry::registerGeneric)
}
}
-
- @SubscribeEvent
- fun addItemsToItemGroups(event: BuildCreativeModeTabContentsEvent) {
- if (event.tabKey === ItemGroups.TOOLS) {
- event.entries.putAfter(
- ItemStack(Items.NETHERITE_HOE),
- ItemStack(getStaffMod().staffItem),
- PARENT_AND_SEARCH_TABS
- )
- } else if (event.tabKey === ItemGroups.COMBAT) {
- event.entries.putAfter(
- ItemStack(Items.TRIDENT),
- ItemStack(getStaffMod().staffItem),
- PARENT_AND_SEARCH_TABS
- )
- }
- }
-
- @SubscribeEvent
- fun registerParticleProviders(event: RegisterParticleProvidersEvent) {
- event.registerSpriteSet(IStaffMod.get().flamethrowerParticleType, FlamethrowerParticle::Factory)
- event.registerSpriteSet(IStaffMod.get().soulFlamethrowerParticleType, FlamethrowerParticle::Factory)
- }
-
- @SubscribeEvent
- fun registerKeyBindings(event: RegisterKeyMappingsEvent) {
- event.register(ADD_REMOVE_KEYBINDING)
- }
-
- @JvmStatic
- @SubscribeEvent
- fun handleStaffKeybinding(event: TickEvent.ClientTickEvent) {
- if (event.phase != TickEvent.Phase.END) return
-
- handleKeyBindings(MinecraftClient.getInstance())
- }
}
diff --git a/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/platform/forge/StaffModPlatformImpl.kt b/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/StaffModPlatformImpl.kt
similarity index 54%
rename from ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/platform/forge/StaffModPlatformImpl.kt
rename to ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/StaffModPlatformImpl.kt
index df3e0f312..7db0c701b 100644
--- a/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/platform/forge/StaffModPlatformImpl.kt
+++ b/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/StaffModPlatformImpl.kt
@@ -17,10 +17,19 @@
*/
@file: JvmName("StaffModPlatformImpl")
+@file: Suppress("unused")
-package opekope2.avm_staff.internal.platform.forge
+package opekope2.avm_staff.internal.forge
-import opekope2.avm_staff.IStaffMod
-import opekope2.avm_staff.internal.forge.StaffMod
+import net.minecraft.item.Item
+import opekope2.avm_staff.api.item.CrownItem
+import opekope2.avm_staff.api.item.StaffItem
+import opekope2.avm_staff.internal.forge.item.ForgeCrownItem
+import opekope2.avm_staff.internal.forge.item.ForgeStaffItem
+import opekope2.avm_staff.internal.forge.item.ForgeStaffRendererItem
-fun getStaffMod(): IStaffMod = StaffMod
+fun createStaffItem(settings: Item.Settings): StaffItem = ForgeStaffItem(settings)
+
+fun createStaffRendererItem(settings: Item.Settings): Item = ForgeStaffRendererItem(settings)
+
+fun createCrownItem(settings: Item.Settings): CrownItem = ForgeCrownItem(settings)
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/model/IStaffItemBakedModel.kt b/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/item/ForgeCrownItem.kt
similarity index 62%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/model/IStaffItemBakedModel.kt
rename to ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/item/ForgeCrownItem.kt
index 7883468e4..aaab164ca 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/model/IStaffItemBakedModel.kt
+++ b/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/item/ForgeCrownItem.kt
@@ -16,20 +16,16 @@
* along with this mod. If not, see .
*/
-package opekope2.avm_staff.api.item.model
+package opekope2.avm_staff.internal.forge.item
-import net.fabricmc.api.EnvType
-import net.fabricmc.api.Environment
-import net.minecraft.client.render.model.BakedModel
+import net.minecraft.entity.LivingEntity
+import net.minecraft.item.Item
import net.minecraft.item.ItemStack
+import net.minecraftforge.common.extensions.IForgeItem
+import opekope2.avm_staff.api.item.CrownItem
-/**
- * A [BakedModel] of an item, which can be placed into the staff.
- */
-@Environment(EnvType.CLIENT)
-interface IStaffItemBakedModel : BakedModel {
- /**
- * Gets a model for the given [staff item stack][staffStack].
- */
- fun getModel(staffStack: ItemStack): BakedModel = this
+class ForgeCrownItem(settings: Item.Settings) : CrownItem(settings), IForgeItem {
+ override fun isRepairable(arg: ItemStack?) = false
+
+ override fun makesPiglinsNeutral(stack: ItemStack, wearer: LivingEntity?) = stack.item is CrownItem
}
diff --git a/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/item/ForgeStaffItem.kt b/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/item/ForgeStaffItem.kt
index 7b8a4594d..dd9cc33b2 100644
--- a/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/item/ForgeStaffItem.kt
+++ b/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/item/ForgeStaffItem.kt
@@ -19,15 +19,27 @@
package opekope2.avm_staff.internal.forge.item
import com.google.common.collect.Multimap
+import net.minecraft.client.MinecraftClient
+import net.minecraft.client.render.VertexConsumerProvider
+import net.minecraft.client.render.item.BuiltinModelItemRenderer
+import net.minecraft.client.render.model.json.ModelTransformationMode
+import net.minecraft.client.util.math.MatrixStack
+import net.minecraft.entity.Entity
import net.minecraft.entity.EquipmentSlot
+import net.minecraft.entity.LivingEntity
import net.minecraft.entity.attribute.EntityAttribute
import net.minecraft.entity.attribute.EntityAttributeModifier
+import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
+import net.minecraft.util.Hand
+import net.minecraftforge.client.extensions.common.IClientItemExtensions
import net.minecraftforge.common.extensions.IForgeItem
import opekope2.avm_staff.api.item.StaffItem
+import opekope2.avm_staff.api.item.renderer.StaffRenderer
import opekope2.avm_staff.util.handlerOfItemOrFallback
import opekope2.avm_staff.util.itemInStaff
+import java.util.function.Consumer
class ForgeStaffItem(settings: Item.Settings) : StaffItem(settings), IForgeItem {
override fun getAttributeModifiers(
@@ -37,10 +49,27 @@ class ForgeStaffItem(settings: Item.Settings) : StaffItem(settings), IForgeItem
return stack.itemInStaff.handlerOfItemOrFallback.getAttributeModifiers(stack, slot)
}
+ @Suppress("RemoveExplicitSuperQualifier") // Required because StaffItem apparently also has canDisableShield
+ override fun canDisableShield(
+ stack: ItemStack,
+ shield: ItemStack?,
+ entity: LivingEntity?,
+ attacker: LivingEntity?
+ ): Boolean {
+ return stack.itemInStaff.handlerOfItemOrFallback.disablesShield() ||
+ super.canDisableShield(stack, shield, entity, attacker)
+ }
+
override fun isRepairable(arg: ItemStack): Boolean {
return false
}
+ override fun onLeftClickEntity(stack: ItemStack, player: PlayerEntity, entity: Entity): Boolean {
+ return stack.itemInStaff.handlerOfItemOrFallback.attackEntity(
+ stack, player.entityWorld, player, entity, Hand.MAIN_HAND
+ ).interruptsFurtherEvaluation()
+ }
+
override fun shouldCauseReequipAnimation(
oldStack: ItemStack,
newStack: ItemStack,
@@ -52,4 +81,27 @@ class ForgeStaffItem(settings: Item.Settings) : StaffItem(settings), IForgeItem
return if (oldHandler !== newHandler) true
else oldHandler.allowReequipAnimation(oldStack, newStack, slotChanged)
}
+
+ // Calm down IDEA, this is beyond your understanding
+ override fun initializeClient(consumer: Consumer) {
+ consumer.accept(object : IClientItemExtensions {
+ override fun getCustomRenderer() = Renderer
+ })
+ }
+
+ object Renderer : BuiltinModelItemRenderer(
+ MinecraftClient.getInstance().blockEntityRenderDispatcher,
+ MinecraftClient.getInstance().entityModelLoader
+ ) {
+ override fun render(
+ stack: ItemStack,
+ mode: ModelTransformationMode,
+ matrices: MatrixStack,
+ vertexConsumers: VertexConsumerProvider,
+ light: Int,
+ overlay: Int
+ ) {
+ StaffRenderer.renderStaff(stack, mode, matrices, vertexConsumers, light, overlay)
+ }
+ }
}
diff --git a/StaffMod/src/main/java/opekope2/avm_staff/mixin/IEntityMixin.java b/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/item/ForgeStaffRendererItem.kt
similarity index 60%
rename from StaffMod/src/main/java/opekope2/avm_staff/mixin/IEntityMixin.java
rename to ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/item/ForgeStaffRendererItem.kt
index 382d74c25..ee6b15920 100644
--- a/StaffMod/src/main/java/opekope2/avm_staff/mixin/IEntityMixin.java
+++ b/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/item/ForgeStaffRendererItem.kt
@@ -16,15 +16,16 @@
* along with this mod. If not, see .
*/
-package opekope2.avm_staff.mixin;
+package opekope2.avm_staff.internal.forge.item
-import net.minecraft.entity.Entity;
-import net.minecraft.util.math.Vec3d;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.gen.Invoker;
+import net.minecraft.item.Item
+import net.minecraftforge.client.extensions.common.IClientItemExtensions
+import java.util.function.Consumer
-@Mixin(Entity.class)
-public interface IEntityMixin {
- @Invoker
- Vec3d invokeGetRotationVector(float pitch, float yaw);
+class ForgeStaffRendererItem(settings: Settings) : Item(settings) {
+ override fun initializeClient(consumer: Consumer) {
+ consumer.accept(object : IClientItemExtensions {
+ override fun getCustomRenderer() = ForgeStaffItem.Renderer
+ })
+ }
}
diff --git a/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/item/model/BakedForgeStaffItemModel.kt b/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/item/model/BakedForgeStaffItemModel.kt
deleted file mode 100644
index fdb7cb8c5..000000000
--- a/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/item/model/BakedForgeStaffItemModel.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.internal.forge.item.model
-
-import net.minecraft.client.render.model.BakedModel
-import net.minecraft.client.render.model.json.ModelTransformationMode
-import net.minecraft.client.util.math.MatrixStack
-import net.minecraft.item.ItemStack
-import net.minecraft.util.Identifier
-import net.minecraftforge.api.distmarker.Dist
-import net.minecraftforge.api.distmarker.OnlyIn
-import net.minecraftforge.client.model.BakedModelWrapper
-import net.minecraftforge.registries.ForgeRegistries
-import opekope2.avm_staff.api.item.model.IStaffItemBakedModel
-import opekope2.avm_staff.util.isItemInStaff
-import opekope2.avm_staff.util.itemInStaff
-
-@OnlyIn(Dist.CLIENT)
-class BakedForgeStaffItemModel(
- original: BakedModel,
- private val itemModels: Map,
- private val missingModel: BakedModel
-) : BakedModelWrapper(original) {
- override fun applyTransform(
- cameraTransformType: ModelTransformationMode,
- poseStack: MatrixStack,
- applyLeftHandTransform: Boolean
- ): BakedModel {
- // BakedModelWrapper delegates this to the original model, which returns the original model instead of this
- super.applyTransform(cameraTransformType, poseStack, applyLeftHandTransform)
- return this
- }
-
- override fun getRenderPasses(stack: ItemStack, fabulous: Boolean): MutableList {
- if (!stack.isItemInStaff) return mutableListOf(this)
-
- val itemStack = stack.itemInStaff ?: return mutableListOf(this)
-
- val model = itemModels[ForgeRegistries.ITEMS.getKey(itemStack.item)]?.getModel(stack) ?: missingModel
- return mutableListOf(this, model)
- }
-}
diff --git a/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/item/model/UnbakedForgeStaffItemModel.kt b/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/item/model/UnbakedForgeStaffItemModel.kt
deleted file mode 100644
index a0c0d16a9..000000000
--- a/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/forge/item/model/UnbakedForgeStaffItemModel.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.internal.forge.item.model
-
-import net.minecraft.client.render.model.BakedModel
-import net.minecraft.client.render.model.Baker
-import net.minecraft.client.render.model.ModelBakeSettings
-import net.minecraft.client.render.model.ModelLoader
-import net.minecraft.client.render.model.json.JsonUnbakedModel
-import net.minecraft.client.texture.Sprite
-import net.minecraft.client.util.SpriteIdentifier
-import net.minecraft.util.Identifier
-import net.minecraftforge.api.distmarker.Dist
-import net.minecraftforge.api.distmarker.OnlyIn
-import opekope2.avm_staff.api.item.model.IStaffItemBakedModel
-import opekope2.avm_staff.internal.item.model.UnbakedStaffItemModel
-import java.util.function.Function
-
-@OnlyIn(Dist.CLIENT)
-class UnbakedForgeStaffItemModel(private val original: JsonUnbakedModel) : UnbakedStaffItemModel(original) {
- override fun bake(
- baker: Baker,
- textureGetter: Function,
- rotationContainer: ModelBakeSettings,
- modelId: Identifier,
- itemModels: Map
- ): BakedModel? {
- return BakedForgeStaffItemModel(
- original.bake(baker, original, textureGetter, rotationContainer, modelId, true) ?: return null,
- itemModels,
- baker.bake(ModelLoader.MISSING_ID, rotationContainer, textureGetter)!!
- )
- }
-}
diff --git a/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/platform/forge/RenderPlatformImpl.kt b/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/platform/forge/RenderPlatformImpl.kt
deleted file mode 100644
index d85a36465..000000000
--- a/ForgeMod/src/main/kotlin/opekope2/avm_staff/internal/platform/forge/RenderPlatformImpl.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-@file: JvmName("RenderPlatformImpl")
-
-package opekope2.avm_staff.internal.platform.forge
-
-import net.minecraft.client.render.VertexConsumer
-import net.minecraft.client.render.model.BakedQuad
-import net.minecraft.client.texture.Sprite
-import net.minecraftforge.client.model.pipeline.QuadBakingVertexConsumer
-import opekope2.avm_staff.api.render.IQuadBakerVertexConsumer
-import java.util.function.Consumer
-
-fun getQuadBakerVertexConsumer(sprite: Sprite, bakedQuadConsumer: Consumer): IQuadBakerVertexConsumer {
- return ForgeQuadBakerVertexConsumer(QuadBakingVertexConsumer(bakedQuadConsumer), sprite)
-}
-
-private class ForgeQuadBakerVertexConsumer(private val wrapped: QuadBakingVertexConsumer, sprite: Sprite) :
- VertexConsumer by wrapped, IQuadBakerVertexConsumer {
- override var sprite: Sprite = sprite
- set(value) {
- field = value
- wrapped.setSprite(value)
- }
-
- init {
- this.sprite = sprite // Call the setter instead of setting the field
- }
-
- override fun overlay(u: Int, v: Int): VertexConsumer {
- // Not supported by Fabric Rendering API, override as NO-OP for parity
- return this
- }
-}
diff --git a/ForgeMod/src/main/resources/avm_staff_forge.mixins.json b/ForgeMod/src/main/resources/avm_staff_forge.mixins.json
deleted file mode 100644
index 59b98338a..000000000
--- a/ForgeMod/src/main/resources/avm_staff_forge.mixins.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "required": true,
- "package": "opekope2.avm_staff.mixin.forge",
- "compatibilityLevel": "JAVA_17",
- "minVersion": "0.8",
- "injectors": {
- "defaultRequire": 1
- },
- "client": [
- "ClientPlayerInteractionManagerMixin",
- "ModelLoaderBakerImplMixin"
- ],
- "mixins": [
- "ServerPlayerEntityMixin",
- "ServerPlayerInteractionManagerMixin"
- ]
-}
diff --git a/NeoForgeMod/Fabric.license b/NeoForgeMod/Fabric.license
deleted file mode 100644
index 8dada3eda..000000000
--- a/NeoForgeMod/Fabric.license
+++ /dev/null
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "{}"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright {yyyy} {name of copyright owner}
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/NeoForgeMod/build.gradle.kts b/NeoForgeMod/build.gradle.kts
index 0ebdc4f15..1f17d6d97 100644
--- a/NeoForgeMod/build.gradle.kts
+++ b/NeoForgeMod/build.gradle.kts
@@ -1,3 +1,21 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
plugins {
alias(libs.plugins.shadow)
}
@@ -57,7 +75,7 @@ tasks {
}
processResources {
- filesMatching("META-INF/mods.toml") {
+ filesMatching("META-INF/neoforge.mods.toml") {
expand(
mutableMapOf(
"version" to version as String,
@@ -80,7 +98,7 @@ tasks {
from(rootDir.resolve("COPYING"))
from(rootDir.resolve("COPYING.LESSER"))
- from(projectDir.resolve("Fabric.license"))
+ from(rootDir.resolve("README.md"))
}
remapJar {
@@ -88,10 +106,11 @@ tasks {
inputFile = shadowJar.get().archiveFile
injectAccessWidener = true
archiveClassifier = null
+ atAccessWideners.add(loom.accessWidenerPath.get().asFile.name)
from(rootDir.resolve("COPYING"))
from(rootDir.resolve("COPYING.LESSER"))
- from(projectDir.resolve("Fabric.license"))
+ from(rootDir.resolve("README.md"))
}
sourcesJar {
diff --git a/NeoForgeMod/src/main/java/opekope2/avm_staff/mixin/neoforge/ClientPlayerInteractionManagerMixin.java b/NeoForgeMod/src/main/java/opekope2/avm_staff/mixin/neoforge/ClientPlayerInteractionManagerMixin.java
deleted file mode 100644
index 763950019..000000000
--- a/NeoForgeMod/src/main/java/opekope2/avm_staff/mixin/neoforge/ClientPlayerInteractionManagerMixin.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package opekope2.avm_staff.mixin.neoforge;
-
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.network.ClientPlayerInteractionManager;
-import net.minecraft.client.network.SequencedPacketCreator;
-import net.minecraft.client.world.ClientWorld;
-import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
-import net.minecraft.util.ActionResult;
-import net.minecraft.util.Hand;
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.Direction;
-import net.minecraft.world.GameMode;
-import opekope2.avm_staff.internal.event_handler.StaffAttackHandlerKt;
-import org.spongepowered.asm.mixin.Final;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
-
-@Mixin(ClientPlayerInteractionManager.class)
-public abstract class ClientPlayerInteractionManagerMixin {
- @Shadow
- @Final
- private MinecraftClient client;
-
- @Shadow
- private GameMode gameMode;
-
- @Shadow
- protected abstract void sendSequencedPacket(ClientWorld clientWorld, SequencedPacketCreator supplier);
-
- // Change method to directly call Staff Mod instead of an exposed event
- @Inject(
- method = "attackBlock",
- at = @At(value = "INVOKE", target = "Lnet/minecraft/world/GameMode;isCreative()Z", ordinal = 0),
- cancellable = true
- )
- private void attackBlock(BlockPos pos, Direction direction, CallbackInfoReturnable info) {
- assert client.player != null;
- assert client.world != null;
-
- ActionResult result = StaffAttackHandlerKt.attackBlock(client.player, client.world, Hand.MAIN_HAND, pos, direction);
-
- if (result != ActionResult.PASS) {
- // Returning true will spawn particles and trigger the animation of the hand -> only for SUCCESS.
- info.setReturnValue(result == ActionResult.SUCCESS);
-
- // We also need to let the server process the action if it's accepted.
- if (result.isAccepted()) {
- sendSequencedPacket(client.world, id -> new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, pos, direction, id));
- }
- }
- }
-
- @Inject(
- method = "updateBlockBreakingProgress",
- at = @At(value = "INVOKE", target = "Lnet/minecraft/world/GameMode;isCreative()Z", ordinal = 0),
- cancellable = true
- )
- private void updateBlockBreakingProgress(BlockPos pos, Direction direction, CallbackInfoReturnable info) {
- if (gameMode.isCreative()) {
- attackBlock(pos, direction, info);
- }
- }
-
- // Inline fabric_fireAttackBlockCallback
- // Remove unused mixins: fabric$onBlockBroken, interactBlock, interactItem, attackEntity
-}
diff --git a/NeoForgeMod/src/main/java/opekope2/avm_staff/mixin/neoforge/ModelLoaderBakerImplMixin.java b/NeoForgeMod/src/main/java/opekope2/avm_staff/mixin/neoforge/ModelLoaderBakerImplMixin.java
deleted file mode 100644
index 44426b237..000000000
--- a/NeoForgeMod/src/main/java/opekope2/avm_staff/mixin/neoforge/ModelLoaderBakerImplMixin.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2016-2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.mixin.neoforge;
-
-import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
-import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
-import net.minecraft.client.render.model.Baker;
-import net.minecraft.client.render.model.UnbakedModel;
-import net.minecraft.client.render.model.json.JsonUnbakedModel;
-import net.minecraft.util.Identifier;
-import opekope2.avm_staff.internal.neoforge.item.model.UnbakedNeoForgeStaffItemModel;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Coerce;
-
-import static opekope2.avm_staff.util.Constants.MOD_ID;
-
-// Forge geometry loaders are way more difficult, than porting a Fabric mixin
-@Mixin(targets = "net/minecraft/client/render/model/ModelLoader$BakerImpl")
-public class ModelLoaderBakerImplMixin {
- // Change method to directly call Staff Mod instead of an exposed event
- // Ignore this error, IntelliJ/mcdev is not looking at the correct jar
- @WrapOperation(
- method = "bake(Lnet/minecraft/util/Identifier;Lnet/minecraft/client/render/model/ModelBakeSettings;Ljava/util/function/Function;)Lnet/minecraft/client/render/model/BakedModel;",
- at = @At(
- value = "INVOKE",
- target = "Lnet/minecraft/client/render/model/ModelLoader$BakerImpl;getOrLoadModel(Lnet/minecraft/util/Identifier;)Lnet/minecraft/client/render/model/UnbakedModel;"
- )
- )
- private UnbakedModel modifyUnbakedModel(@Coerce Baker thiz, Identifier id, Operation original) {
- UnbakedModel model = original.call(thiz, id);
- if (!MOD_ID.equals(id.getNamespace())) return model;
-
- return switch (id.getPath()) {
- // TODO hardcoded paths
- case "staff", "item/staff_in_use" -> new UnbakedNeoForgeStaffItemModel((JsonUnbakedModel) model); // FIXME
- default -> model;
- };
- }
-
- // Remove unused mixins: invokeModifyAfterBake
-}
diff --git a/NeoForgeMod/src/main/java/opekope2/avm_staff/mixin/neoforge/ServerPlayerEntityMixin.java b/NeoForgeMod/src/main/java/opekope2/avm_staff/mixin/neoforge/ServerPlayerEntityMixin.java
deleted file mode 100644
index e6d66146a..000000000
--- a/NeoForgeMod/src/main/java/opekope2/avm_staff/mixin/neoforge/ServerPlayerEntityMixin.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package opekope2.avm_staff.mixin.neoforge;
-
-import net.minecraft.entity.Entity;
-import net.minecraft.server.network.ServerPlayerEntity;
-import net.minecraft.util.ActionResult;
-import net.minecraft.util.Hand;
-import opekope2.avm_staff.internal.event_handler.StaffAttackHandlerKt;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-
-@Mixin(ServerPlayerEntity.class)
-public class ServerPlayerEntityMixin {
- // Change method to directly call Staff Mod instead of an exposed event
- @Inject(method = "attack", at = @At("HEAD"), cancellable = true)
- public void onPlayerInteractEntity(Entity target, CallbackInfo info) {
- ServerPlayerEntity player = (ServerPlayerEntity) (Object) this;
- ActionResult result = StaffAttackHandlerKt.attackEntity(player, player.getEntityWorld(), Hand.MAIN_HAND, target);
-
- if (result != ActionResult.PASS) {
- info.cancel();
- }
- }
-}
diff --git a/NeoForgeMod/src/main/java/opekope2/avm_staff/mixin/neoforge/ServerPlayerInteractionManagerMixin.java b/NeoForgeMod/src/main/java/opekope2/avm_staff/mixin/neoforge/ServerPlayerInteractionManagerMixin.java
deleted file mode 100644
index db7780495..000000000
--- a/NeoForgeMod/src/main/java/opekope2/avm_staff/mixin/neoforge/ServerPlayerInteractionManagerMixin.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package opekope2.avm_staff.mixin.neoforge;
-
-import net.minecraft.block.entity.BlockEntity;
-import net.minecraft.network.listener.ClientPlayPacketListener;
-import net.minecraft.network.packet.Packet;
-import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
-import net.minecraft.network.packet.s2c.play.BlockUpdateS2CPacket;
-import net.minecraft.server.network.ServerPlayerEntity;
-import net.minecraft.server.network.ServerPlayerInteractionManager;
-import net.minecraft.server.world.ServerWorld;
-import net.minecraft.util.ActionResult;
-import net.minecraft.util.Hand;
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.Direction;
-import opekope2.avm_staff.internal.event_handler.StaffAttackHandlerKt;
-import org.spongepowered.asm.mixin.Final;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-
-@Mixin(ServerPlayerInteractionManager.class)
-public abstract class ServerPlayerInteractionManagerMixin {
- @Shadow
- protected ServerWorld world;
-
- @Shadow
- @Final
- protected ServerPlayerEntity player;
-
- // Change method to directly call Staff Mod instead of an exposed event
- @Inject(at = @At("HEAD"), method = "processBlockBreakingAction", cancellable = true)
- public void startBlockBreak(BlockPos pos, PlayerActionC2SPacket.Action playerAction, Direction direction, int worldHeight, int i, CallbackInfo info) {
- if (playerAction != PlayerActionC2SPacket.Action.START_DESTROY_BLOCK) return;
-
- ActionResult result = StaffAttackHandlerKt.attackBlock(player, world, Hand.MAIN_HAND, pos, direction);
-
- if (result == ActionResult.PASS) return;
-
- // The client might have broken the block on its side, so make sure to let it know.
- this.player.networkHandler.sendPacket(new BlockUpdateS2CPacket(world, pos));
-
- if (world.getBlockState(pos).hasBlockEntity()) {
- BlockEntity blockEntity = world.getBlockEntity(pos);
-
- if (blockEntity != null) {
- Packet updatePacket = blockEntity.toUpdatePacket();
-
- if (updatePacket != null) {
- this.player.networkHandler.sendPacket(updatePacket);
- }
- }
- }
-
- info.cancel();
- }
-
- // Remove unused mixins: interactBlock, interactItem, breakBlock, onBlockBroken
-}
diff --git a/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/StaffMod.kt b/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/StaffMod.kt
index 78185af3a..5b65bbc15 100644
--- a/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/StaffMod.kt
+++ b/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/StaffMod.kt
@@ -18,64 +18,36 @@
package opekope2.avm_staff.internal.neoforge
-import net.minecraft.item.Item
-import net.minecraft.particle.DefaultParticleType
-import net.minecraft.registry.Registries
-import net.minecraft.registry.tag.ItemTags
-import net.minecraft.registry.tag.TagKey
-import net.minecraft.util.Identifier
import net.neoforged.api.distmarker.Dist
import net.neoforged.fml.common.Mod
-import net.neoforged.neoforge.registries.DeferredRegister
-import opekope2.avm_staff.IStaffMod
-import opekope2.avm_staff.api.item.StaffItem
+import net.neoforged.neoforge.event.entity.living.LivingDropsEvent
import opekope2.avm_staff.internal.initializeNetworking
-import opekope2.avm_staff.internal.neoforge.item.NeoForgeStaffItem
-import opekope2.avm_staff.internal.staff_item_handler.registerVanillaStaffItemHandlers
+import opekope2.avm_staff.internal.registerContent
+import opekope2.avm_staff.internal.staff_handler.registerVanillaStaffHandlers
+import opekope2.avm_staff.internal.stopUsingStaffWhenDropped
+import opekope2.avm_staff.internal.subscribeToEvents
import opekope2.avm_staff.util.MOD_ID
-import thedarkcolour.kotlinforforge.neoforge.forge.DIST
-import thedarkcolour.kotlinforforge.neoforge.forge.MOD_BUS
+import thedarkcolour.kotlinforforge.neoforge.forge.FORGE_BUS
import thedarkcolour.kotlinforforge.neoforge.forge.runWhenOn
@Mod(MOD_ID)
-object StaffMod : IStaffMod {
- private val ITEMS = DeferredRegister.create(Registries.ITEM, MOD_ID)
-
- private val STAFF_ITEM = ITEMS.register("staff") { -> NeoForgeStaffItem(Item.Settings().maxCount(1)) }
-
- private val PARTICLE_TYPES = DeferredRegister.create(Registries.PARTICLE_TYPE, MOD_ID)
-
- private val FLAMETHROWER_PARTICLE_TYPE = PARTICLE_TYPES.register("flame") { ->
- DefaultParticleType(false)
- }
-
- private val SOUL_FLAMETHROWER_PARTICLE_TYPE = PARTICLE_TYPES.register("soul_fire_flame") { ->
- DefaultParticleType(false)
- }
-
+object StaffMod {
init {
- initialize()
+ registerContent()
initializeNetworking()
- registerVanillaStaffItemHandlers()
+ subscribeToEvents()
+ subscribeToNeoForgeEvents()
+ registerVanillaStaffHandlers()
runWhenOn(Dist.CLIENT, StaffModClient::initializeClient)
}
- override val staffItem: StaffItem
- get() = STAFF_ITEM.get()
-
- override val isPhysicalClient: Boolean
- get() = DIST.isClient
-
- override val staffsTag: TagKey
- = ItemTags.create(Identifier(MOD_ID, "staffs"))
-
- override val flamethrowerParticleType: DefaultParticleType
- get() = FLAMETHROWER_PARTICLE_TYPE.get()
-
- override val soulFlamethrowerParticleType: DefaultParticleType
- get() = SOUL_FLAMETHROWER_PARTICLE_TYPE.get()
+ private fun subscribeToNeoForgeEvents() {
+ FORGE_BUS.addListener(::dropInventory)
+ }
- private fun initialize() {
- ITEMS.register(MOD_BUS)
- PARTICLE_TYPES.register(MOD_BUS)
+ private fun dropInventory(event: LivingDropsEvent) {
+ for (item in event.drops) {
+ stopUsingStaffWhenDropped(event.entity, item)
+ }
}
}
diff --git a/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/StaffModClient.kt b/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/StaffModClient.kt
index 19fe88898..dca76b42b 100644
--- a/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/StaffModClient.kt
+++ b/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/StaffModClient.kt
@@ -18,75 +18,42 @@
package opekope2.avm_staff.internal.neoforge
-import net.minecraft.client.MinecraftClient
import net.minecraft.client.item.ModelPredicateProviderRegistry
-import net.minecraft.item.ItemGroup.StackVisibility.PARENT_AND_SEARCH_TABS
-import net.minecraft.item.ItemGroups
-import net.minecraft.item.ItemStack
-import net.minecraft.item.Items
import net.neoforged.api.distmarker.Dist
import net.neoforged.api.distmarker.OnlyIn
import net.neoforged.bus.api.SubscribeEvent
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent
-import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent
import net.neoforged.neoforge.client.event.RegisterParticleProvidersEvent
-import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent
-import net.neoforged.neoforge.event.TickEvent
-import opekope2.avm_staff.IStaffMod
+import opekope2.avm_staff.api.flamethrowerParticleType
import opekope2.avm_staff.api.particle.FlamethrowerParticle
-import opekope2.avm_staff.internal.event_handler.ADD_REMOVE_KEYBINDING
-import opekope2.avm_staff.internal.event_handler.handleKeyBindings
-import opekope2.avm_staff.internal.platform.neoforge.getStaffMod
-import opekope2.avm_staff.internal.registerModelPredicateProviders
-import thedarkcolour.kotlinforforge.neoforge.forge.FORGE_BUS
+import opekope2.avm_staff.api.soulFlamethrowerParticleType
+import opekope2.avm_staff.internal.model.registerModelPredicateProviders
+import opekope2.avm_staff.internal.registerClientContent
+import opekope2.avm_staff.internal.registerSmithingTableTextures
+import opekope2.avm_staff.internal.staff_handler.registerVanillaStaffItemRenderers
+import opekope2.avm_staff.internal.subscribeToClientEvents
import thedarkcolour.kotlinforforge.neoforge.forge.MOD_BUS
@OnlyIn(Dist.CLIENT)
object StaffModClient {
fun initializeClient() {
+ registerClientContent()
+ registerSmithingTableTextures()
+ subscribeToClientEvents()
+ registerVanillaStaffItemRenderers()
MOD_BUS.register(this)
- FORGE_BUS.addListener(::handleStaffKeybinding)
}
@SubscribeEvent
fun initializeClient(event: FMLClientSetupEvent) {
event.enqueueWork {
- registerModelPredicateProviders(ModelPredicateProviderRegistry::register)
- }
- }
-
- @SubscribeEvent
- fun addItemsToItemGroups(event: BuildCreativeModeTabContentsEvent) {
- if (event.tabKey === ItemGroups.TOOLS) {
- event.entries.putAfter(
- ItemStack(Items.NETHERITE_HOE),
- ItemStack(getStaffMod().staffItem),
- PARENT_AND_SEARCH_TABS
- )
- } else if (event.tabKey === ItemGroups.COMBAT) {
- event.entries.putAfter(
- ItemStack(Items.TRIDENT),
- ItemStack(getStaffMod().staffItem),
- PARENT_AND_SEARCH_TABS
- )
+ registerModelPredicateProviders(ModelPredicateProviderRegistry::registerGeneric)
}
}
@SubscribeEvent
fun registerParticleProviders(event: RegisterParticleProvidersEvent) {
- event.registerSpriteSet(IStaffMod.get().flamethrowerParticleType, FlamethrowerParticle::Factory)
- event.registerSpriteSet(IStaffMod.get().soulFlamethrowerParticleType, FlamethrowerParticle::Factory)
- }
-
- @SubscribeEvent
- fun registerKeyBindings(event: RegisterKeyMappingsEvent) {
- event.register(ADD_REMOVE_KEYBINDING)
- }
-
- @JvmStatic
- fun handleStaffKeybinding(event: TickEvent.ClientTickEvent) {
- if (event.phase != TickEvent.Phase.END) return
-
- handleKeyBindings(MinecraftClient.getInstance())
+ event.registerSpriteSet(flamethrowerParticleType.get(), FlamethrowerParticle::Factory)
+ event.registerSpriteSet(soulFlamethrowerParticleType.get(), FlamethrowerParticle::Factory)
}
}
diff --git a/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/platform/fabric/StaffModPlatformImpl.kt b/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/StaffModPlatformImpl.kt
similarity index 52%
rename from FabricMod/src/main/kotlin/opekope2/avm_staff/internal/platform/fabric/StaffModPlatformImpl.kt
rename to NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/StaffModPlatformImpl.kt
index eec850632..70e059ca2 100644
--- a/FabricMod/src/main/kotlin/opekope2/avm_staff/internal/platform/fabric/StaffModPlatformImpl.kt
+++ b/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/StaffModPlatformImpl.kt
@@ -17,10 +17,19 @@
*/
@file: JvmName("StaffModPlatformImpl")
+@file: Suppress("unused")
-package opekope2.avm_staff.internal.platform.fabric
+package opekope2.avm_staff.internal.neoforge
-import opekope2.avm_staff.IStaffMod
-import opekope2.avm_staff.internal.fabric.StaffMod
+import net.minecraft.item.Item
+import opekope2.avm_staff.api.item.CrownItem
+import opekope2.avm_staff.api.item.StaffItem
+import opekope2.avm_staff.internal.neoforge.item.NeoForgeCrownItem
+import opekope2.avm_staff.internal.neoforge.item.NeoForgeStaffItem
+import opekope2.avm_staff.internal.neoforge.item.NeoForgeStaffRendererItem
-fun getStaffMod(): IStaffMod = StaffMod
+fun createStaffItem(settings: Item.Settings): StaffItem = NeoForgeStaffItem(settings)
+
+fun createStaffRendererItem(settings: Item.Settings): Item = NeoForgeStaffRendererItem(settings)
+
+fun createCrownItem(settings: Item.Settings): CrownItem = NeoForgeCrownItem(settings)
diff --git a/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/item/NeoForgeCrownItem.kt b/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/item/NeoForgeCrownItem.kt
new file mode 100644
index 000000000..7859e88b5
--- /dev/null
+++ b/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/item/NeoForgeCrownItem.kt
@@ -0,0 +1,31 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+package opekope2.avm_staff.internal.neoforge.item
+
+import net.minecraft.entity.LivingEntity
+import net.minecraft.item.Item
+import net.minecraft.item.ItemStack
+import net.neoforged.neoforge.common.extensions.IItemExtension
+import opekope2.avm_staff.api.item.CrownItem
+
+class NeoForgeCrownItem(settings: Item.Settings) : CrownItem(settings), IItemExtension {
+ override fun isRepairable(arg: ItemStack) = false
+
+ override fun makesPiglinsNeutral(stack: ItemStack, wearer: LivingEntity) = stack.item is CrownItem
+}
diff --git a/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/item/NeoForgeStaffItem.kt b/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/item/NeoForgeStaffItem.kt
index da9d0893b..69c73202b 100644
--- a/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/item/NeoForgeStaffItem.kt
+++ b/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/item/NeoForgeStaffItem.kt
@@ -18,38 +18,89 @@
package opekope2.avm_staff.internal.neoforge.item
-import com.google.common.collect.Multimap
-import net.minecraft.entity.EquipmentSlot
-import net.minecraft.entity.attribute.EntityAttribute
-import net.minecraft.entity.attribute.EntityAttributeModifier
+import net.minecraft.client.MinecraftClient
+import net.minecraft.client.render.VertexConsumerProvider
+import net.minecraft.client.render.item.BuiltinModelItemRenderer
+import net.minecraft.client.render.model.json.ModelTransformationMode
+import net.minecraft.client.util.math.MatrixStack
+import net.minecraft.entity.Entity
+import net.minecraft.entity.LivingEntity
+import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
+import net.minecraft.util.Hand
+import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions
import net.neoforged.neoforge.common.extensions.IItemExtension
import opekope2.avm_staff.api.item.StaffItem
-import opekope2.avm_staff.util.handlerOfItemOrFallback
+import opekope2.avm_staff.api.item.renderer.StaffRenderer
import opekope2.avm_staff.util.itemInStaff
+import opekope2.avm_staff.util.staffHandlerOrDefault
+import java.util.function.Consumer
class NeoForgeStaffItem(settings: Item.Settings) : StaffItem(settings), IItemExtension {
- override fun getAttributeModifiers(
- slot: EquipmentSlot,
- stack: ItemStack
- ): Multimap {
- return stack.itemInStaff.handlerOfItemOrFallback.getAttributeModifiers(stack, slot)
+ @Suppress("RemoveExplicitSuperQualifier") // Required because StaffItem apparently also has canDisableShield
+ override fun canDisableShield(
+ stack: ItemStack,
+ shield: ItemStack,
+ entity: LivingEntity,
+ attacker: LivingEntity
+ ): Boolean {
+ return stack.itemInStaff.staffHandlerOrDefault.disablesShield() ||
+ super.canDisableShield(stack, shield, entity, attacker)
}
override fun isRepairable(arg: ItemStack): Boolean {
return false
}
+ override fun onEntitySwing(stack: ItemStack, entity: LivingEntity): Boolean {
+ return !stack.itemInStaff.staffHandlerOrDefault.canSwingHand(
+ stack,
+ entity.entityWorld,
+ entity,
+ if (stack === entity.getStackInHand(Hand.MAIN_HAND)) Hand.MAIN_HAND
+ else Hand.OFF_HAND
+ )
+ }
+
+ override fun onLeftClickEntity(stack: ItemStack, player: PlayerEntity, entity: Entity): Boolean {
+ return stack.itemInStaff.staffHandlerOrDefault.attackEntity(
+ stack, player.entityWorld, player, entity, Hand.MAIN_HAND
+ ).interruptsFurtherEvaluation()
+ }
+
override fun shouldCauseReequipAnimation(
oldStack: ItemStack,
newStack: ItemStack,
slotChanged: Boolean
): Boolean {
- val oldHandler = oldStack.itemInStaff.handlerOfItemOrFallback
- val newHandler = newStack.itemInStaff.handlerOfItemOrFallback
+ val oldHandler = oldStack.itemInStaff.staffHandlerOrDefault
+ val newHandler = newStack.itemInStaff.staffHandlerOrDefault
return if (oldHandler !== newHandler) true
else oldHandler.allowReequipAnimation(oldStack, newStack, slotChanged)
}
+
+ // Calm down IDEA, this is beyond your understanding
+ override fun initializeClient(consumer: Consumer) {
+ consumer.accept(object : IClientItemExtensions {
+ override fun getCustomRenderer() = Renderer
+ })
+ }
+
+ object Renderer : BuiltinModelItemRenderer(
+ MinecraftClient.getInstance().blockEntityRenderDispatcher,
+ MinecraftClient.getInstance().entityModelLoader
+ ) {
+ override fun render(
+ stack: ItemStack,
+ mode: ModelTransformationMode,
+ matrices: MatrixStack,
+ vertexConsumers: VertexConsumerProvider,
+ light: Int,
+ overlay: Int
+ ) {
+ StaffRenderer.renderStaff(stack, mode, matrices, vertexConsumers, light, overlay)
+ }
+ }
}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/render/IQuadBakerVertexConsumer.kt b/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/item/NeoForgeStaffRendererItem.kt
similarity index 59%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/api/render/IQuadBakerVertexConsumer.kt
rename to NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/item/NeoForgeStaffRendererItem.kt
index abd9812a4..1b0c9963e 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/render/IQuadBakerVertexConsumer.kt
+++ b/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/item/NeoForgeStaffRendererItem.kt
@@ -16,18 +16,16 @@
* along with this mod. If not, see .
*/
-package opekope2.avm_staff.api.render
+package opekope2.avm_staff.internal.neoforge.item
-import net.minecraft.client.render.VertexConsumer
-import net.minecraft.client.render.model.BakedQuad
-import net.minecraft.client.texture.Sprite
+import net.minecraft.item.Item
+import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions
+import java.util.function.Consumer
-/**
- * A vertex consumer, which creates [BakedQuad]s.
- */
-interface IQuadBakerVertexConsumer : VertexConsumer {
- /**
- * Gets or sets the sprite used to create [BakedQuad]s.
- */
- var sprite: Sprite
+class NeoForgeStaffRendererItem(settings: Settings) : Item(settings) {
+ override fun initializeClient(consumer: Consumer) {
+ consumer.accept(object : IClientItemExtensions {
+ override fun getCustomRenderer() = NeoForgeStaffItem.Renderer
+ })
+ }
}
diff --git a/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/item/model/BakedNeoForgeStaffItemModel.kt b/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/item/model/BakedNeoForgeStaffItemModel.kt
deleted file mode 100644
index 7218e3f4d..000000000
--- a/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/item/model/BakedNeoForgeStaffItemModel.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.internal.neoforge.item.model
-
-import net.minecraft.client.render.model.BakedModel
-import net.minecraft.client.render.model.json.ModelTransformationMode
-import net.minecraft.client.util.math.MatrixStack
-import net.minecraft.item.ItemStack
-import net.minecraft.registry.Registries
-import net.minecraft.util.Identifier
-import net.neoforged.api.distmarker.Dist
-import net.neoforged.api.distmarker.OnlyIn
-import net.neoforged.neoforge.client.model.BakedModelWrapper
-import opekope2.avm_staff.api.item.model.IStaffItemBakedModel
-import opekope2.avm_staff.util.isItemInStaff
-import opekope2.avm_staff.util.itemInStaff
-
-@OnlyIn(Dist.CLIENT)
-class BakedNeoForgeStaffItemModel(
- original: BakedModel,
- private val itemModels: Map,
- private val missingModel: BakedModel
-) : BakedModelWrapper(original) {
- override fun applyTransform(
- cameraTransformType: ModelTransformationMode,
- poseStack: MatrixStack,
- applyLeftHandTransform: Boolean
- ): BakedModel {
- // BakedModelWrapper delegates this to the original model, which returns the original model instead of this
- super.applyTransform(cameraTransformType, poseStack, applyLeftHandTransform)
- return this
- }
-
- override fun getRenderPasses(stack: ItemStack, fabulous: Boolean): MutableList {
- if (!stack.isItemInStaff) return mutableListOf(this)
-
- val itemStack = stack.itemInStaff ?: return mutableListOf(this)
-
- val model = itemModels[Registries.ITEM.getId(itemStack.item)]?.getModel(stack) ?: missingModel
- return mutableListOf(this, model)
- }
-}
diff --git a/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/item/model/UnbakedNeoForgeStaffItemModel.kt b/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/item/model/UnbakedNeoForgeStaffItemModel.kt
deleted file mode 100644
index 6f7ae6ae9..000000000
--- a/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/neoforge/item/model/UnbakedNeoForgeStaffItemModel.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.internal.neoforge.item.model
-
-import net.minecraft.client.render.model.BakedModel
-import net.minecraft.client.render.model.Baker
-import net.minecraft.client.render.model.ModelBakeSettings
-import net.minecraft.client.render.model.ModelLoader
-import net.minecraft.client.render.model.json.JsonUnbakedModel
-import net.minecraft.client.texture.Sprite
-import net.minecraft.client.util.SpriteIdentifier
-import net.minecraft.util.Identifier
-import net.neoforged.api.distmarker.Dist
-import net.neoforged.api.distmarker.OnlyIn
-import opekope2.avm_staff.api.item.model.IStaffItemBakedModel
-import opekope2.avm_staff.internal.item.model.UnbakedStaffItemModel
-import java.util.function.Function
-
-@OnlyIn(Dist.CLIENT)
-class UnbakedNeoForgeStaffItemModel(private val original: JsonUnbakedModel) : UnbakedStaffItemModel(original) {
- override fun bake(
- baker: Baker,
- textureGetter: Function,
- rotationContainer: ModelBakeSettings,
- modelId: Identifier,
- itemModels: Map
- ): BakedModel? {
- return BakedNeoForgeStaffItemModel(
- original.bake(baker, original, textureGetter, rotationContainer, modelId, true) ?: return null,
- itemModels,
- baker.bake(ModelLoader.MISSING_ID, rotationContainer, textureGetter)!!
- )
- }
-}
diff --git a/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/platform/neoforge/RenderPlatformImpl.kt b/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/platform/neoforge/RenderPlatformImpl.kt
deleted file mode 100644
index 7eeb568e1..000000000
--- a/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/platform/neoforge/RenderPlatformImpl.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-@file: JvmName("RenderPlatformImpl")
-
-package opekope2.avm_staff.internal.platform.neoforge
-
-import net.minecraft.client.render.VertexConsumer
-import net.minecraft.client.render.model.BakedQuad
-import net.minecraft.client.texture.Sprite
-import net.neoforged.neoforge.client.model.pipeline.QuadBakingVertexConsumer
-import opekope2.avm_staff.api.render.IQuadBakerVertexConsumer
-import java.util.function.Consumer
-
-fun getQuadBakerVertexConsumer(sprite: Sprite, bakedQuadConsumer: Consumer): IQuadBakerVertexConsumer {
- return ForgeQuadBakerVertexConsumer(QuadBakingVertexConsumer(bakedQuadConsumer), sprite)
-}
-
-private class ForgeQuadBakerVertexConsumer(private val wrapped: QuadBakingVertexConsumer, sprite: Sprite) :
- VertexConsumer by wrapped, IQuadBakerVertexConsumer {
- override var sprite: Sprite = sprite
- set(value) {
- field = value
- wrapped.setSprite(value)
- }
-
- init {
- this.sprite = sprite // Call the setter instead of setting the field
- }
-
- override fun overlay(u: Int, v: Int): VertexConsumer {
- // Not supported by Fabric Rendering API, override as NO-OP for parity
- return this
- }
-}
diff --git a/NeoForgeMod/src/main/resources/META-INF/mods.toml b/NeoForgeMod/src/main/resources/META-INF/neoforge.mods.toml
similarity index 79%
rename from NeoForgeMod/src/main/resources/META-INF/mods.toml
rename to NeoForgeMod/src/main/resources/META-INF/neoforge.mods.toml
index 51f87f9e8..58e21f9a4 100644
--- a/NeoForgeMod/src/main/resources/META-INF/mods.toml
+++ b/NeoForgeMod/src/main/resources/META-INF/neoforge.mods.toml
@@ -6,10 +6,11 @@ license = "LGPL-3.0-or-later"
[[mods]]
modId = "avm_staff"
version = "$version"
-displayName = "Staff Mod (AvM Shorts)"
+displayName = "Staff Mod (Animation vs Minecraft)"
authors = "opekope2"
+credits = "Alan Becker, Brother_OliviƤr, Ink&Soul"
description = '''
-Staff from Animation vs Minecraft Shorts
+AvM Staff Mod is a fan-made, mostly canonically accurate, and close to vanilla mod adding staffs from Animation vs Minecraft series to the latest version of Minecraft: Java Edition
'''
logoFile = "assets/avm_staff/icon.png"
features = { java_version = "[$java,)" }
@@ -18,9 +19,6 @@ displayURL = "https://opekope2.dev/StaffMod"
[[mixins]]
config = "avm_staff.mixins.json"
-[[mixins]]
-config = "avm_staff_neoforge.mixins.json"
-
[[dependencies.avm_staff]]
modId = "neoforge"
type = "required"
diff --git a/NeoForgeMod/src/main/resources/avm_staff_neoforge.mixins.json b/NeoForgeMod/src/main/resources/avm_staff_neoforge.mixins.json
deleted file mode 100644
index 8a0cf73be..000000000
--- a/NeoForgeMod/src/main/resources/avm_staff_neoforge.mixins.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "required": true,
- "package": "opekope2.avm_staff.mixin.neoforge",
- "compatibilityLevel": "JAVA_17",
- "minVersion": "0.8",
- "injectors": {
- "defaultRequire": 1
- },
- "client": [
- "ClientPlayerInteractionManagerMixin",
- "ModelLoaderBakerImplMixin"
- ],
- "mixins": [
- "ServerPlayerEntityMixin",
- "ServerPlayerInteractionManagerMixin"
- ]
-}
diff --git a/README.md b/README.md
index 0cb680c90..fa2081a03 100644
--- a/README.md
+++ b/README.md
@@ -8,37 +8,43 @@
[![Wiki](https://img.shields.io/badge/Read_the-wiki-8ca1af?style=flat&logo=readthedocs)](https://opekope2.dev/StaffMod)
[![Buy me a coffee](https://img.shields.io/badge/Buy_me_a_coffee-Ko--fi-f16061?style=flat&logo=ko-fi)](https://ko-fi.com/opekope2)
-Adds staff from Animation vs Minecraft shorts.
+Fan-made, mostly canonically accurate, and close to vanilla mod adding [staffs](https://animatorvsanimation.fandom.com/wiki/Staffs) from Animation vs Minecraft series
-## Roadmap
+## Credits
-Functionality will be added gradually.
-This will bring (hopefully) many smaller updates before a big update, so you can enjoy new (experimental) functionality,
-even when I don't have much time for development.
-Please note that the development cycle may be slow, because I'm working on a quite big update
-for [OptiGUI](https://github.com/opekope2/optigui), my other mod, and I'm also in university.
+* *[Alan Becker](https://www.youtube.com/@alanbecker)*, for animating Animation vs Minecraft series
+* *Brother OliviƤr*, for drawing *Royal Staff Ingredient* and *Staff Infusion Smithing Template* textures
+* *[Ink&Soul](https://github.com/MBYL-InkAndSoul)*, for fixing a bug
-### Phase 0 (done)
+## Roadmap
-* Add staff item
-* Add basic functionality
-* Alpha release
+Functionality will be added gradually, bringing many smaller updates before the 1.0.0 release.
+See [this GitHub discussion](https://github.com/opekope2/StaffMod/discussions/31) for upcoming features.
-### Phase 1 (in development)
+## [Staff Mod items](https://opekope2.dev/StaffMod/items.html)
-* Add block functionalities inside staff item (a bunch of simple use blocks)
-* Beta releases
+Staff Mod adds the following items to the game:
-### Phase 2
+* [Faint staff rod](https://opekope2.dev/StaffMod/items.html#faint-staff-rod)
+* [Faint royal staff head](https://opekope2.dev/StaffMod/items.html#faint-royal-staff-head)
+* [Faint royal staff](https://opekope2.dev/StaffMod/items.html#faint-royal-staff)
+* [Royal staff](https://opekope2.dev/StaffMod/items.html#royal-staff)
+* [Royal staff ingredient](https://opekope2.dev/StaffMod/items.html#royal-staff-ingredient)
+* [Crown of King Orange](https://opekope2.dev/StaffMod/items.html#crown-of-king-orange)
+* [Staff infusion smithing template](https://opekope2.dev/StaffMod/items.html#staff-infusion-smithing-template)
-* Survival mode support and balancing (head over to GitHub to discuss/suggest functionality)
-* Advanced block functionalities inside staff item
-* Fix rendering, models, and textures
-* Add server-side configuration support
-* First stable release (big update)
+## [Supported items in staffs](https://opekope2.dev/StaffMod/staff.html)
-### Phase 3
+Staff Mod implements functionalities for the following items:
-* Maybe add staff add/remove animations
-* Add the rest of the blocks seen in AvM Shorts ep 33
-* Everything else
+* [Anvil, chipped anvil, damaged anvil](https://opekope2.dev/StaffMod/staff.html#anvil-chipped-anvil-damaged-anvil)
+* [Bell](https://opekope2.dev/StaffMod/staff.html#bell)
+* [Bone block](https://opekope2.dev/StaffMod/staff.html#bone-block)
+* [Campfire, soul campfire](https://opekope2.dev/StaffMod/staff.html#campfire-soul-campfire)
+* [Furnace, blast furnace, smoker](https://opekope2.dev/StaffMod/staff.html#furnace-blast-furnace-smoker)
+* [Lightning rod](https://opekope2.dev/StaffMod/staff.html#lightning-rod)
+* [Magma block](https://opekope2.dev/StaffMod/staff.html#magma-block)
+* [Snow block](https://opekope2.dev/StaffMod/staff.html#snow-block)
+* [TNT](https://opekope2.dev/StaffMod/staff.html#tnt)
+* [Wither Skeleton Skull](https://opekope2.dev/StaffMod/staff.html#wither-skeleton-skull)
+* [Wool (any color)](https://opekope2.dev/StaffMod/staff.html#wool-any-color)
diff --git a/StaffMod/build.gradle.kts b/StaffMod/build.gradle.kts
index 336efc459..5f1504d9e 100644
--- a/StaffMod/build.gradle.kts
+++ b/StaffMod/build.gradle.kts
@@ -26,7 +26,7 @@ plugins {
}
architectury {
- common("fabric", "forge", "neoforge")
+ common("fabric", /*"forge",*/ "neoforge")
}
repositories {}
@@ -58,6 +58,7 @@ tasks {
pluginConfiguration {
footerMessage =
"Ā© 2023-${Year.now().value} opekope2. AvM Staff Mod is not an official Minecraft product. Not associated with or endorsed by Mojang Studios."
+ customAssets = listOf(projectDir.resolve("logo-icon.svg"))
separateInheritedMembers = true
}
diff --git a/StaffMod/logo-icon.svg b/StaffMod/logo-icon.svg
new file mode 100644
index 000000000..703012369
--- /dev/null
+++ b/StaffMod/logo-icon.svg
@@ -0,0 +1,488 @@
+
+
+
diff --git a/StaffMod/src/main/java/opekope2/avm_staff/api/entity/IImpactTnt.java b/StaffMod/src/main/java/opekope2/avm_staff/api/entity/IImpactTnt.java
deleted file mode 100644
index b842cfc7a..000000000
--- a/StaffMod/src/main/java/opekope2/avm_staff/api/entity/IImpactTnt.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.api.entity;
-
-import net.minecraft.entity.TntEntity;
-import net.minecraft.entity.data.DataTracker;
-import net.minecraft.entity.data.TrackedData;
-import net.minecraft.entity.data.TrackedDataHandlerRegistry;
-
-/**
- * A TNT, which can be configured to explode when collides (with blocks or other entities).
- */
-public interface IImpactTnt {
- /**
- * Returns if the current TNT explodes, when it collides.
- */
- boolean explodesOnImpact();
-
- /**
- * Configures the current TNT to explode or not, when it collides.
- *
- * @param explode Whether to explode on collision
- */
- void explodeOnImpact(boolean explode);
-
- /**
- * NBT Key for TNT's explodes on impact property.
- */
- String EXPLODES_ON_IMPACT_NBT_KEY = "ExplodesOnImpact";
-
- /**
- * Data tracker for TNT's explodes on impact property.
- */
- TrackedData EXPLODES_ON_IMPACT = DataTracker.registerData(TntEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
-}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/model/UnbakedBlockStateModelSupplier.kt b/StaffMod/src/main/java/opekope2/avm_staff/api/item/IActiveItemTempDataHolder.java
similarity index 55%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/model/UnbakedBlockStateModelSupplier.kt
rename to StaffMod/src/main/java/opekope2/avm_staff/api/item/IActiveItemTempDataHolder.java
index 54e80322c..5728da9a5 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/model/UnbakedBlockStateModelSupplier.kt
+++ b/StaffMod/src/main/java/opekope2/avm_staff/api/item/IActiveItemTempDataHolder.java
@@ -16,19 +16,24 @@
* along with this mod. If not, see .
*/
-package opekope2.avm_staff.api.item.model
+package opekope2.avm_staff.api.item;
-import net.fabricmc.api.EnvType
-import net.fabricmc.api.Environment
-import net.minecraft.block.BlockState
-import java.util.function.Supplier
+import org.jetbrains.annotations.Nullable;
/**
- * Convenience implementation of [Supplier] with the same parameters as [UnbakedBlockStateModel].
- *
- * @param blockState The block state to bake
+ * Holder of a temporary data while an entity is using an item. To be used by the item class being used on server side.
*/
-@Environment(EnvType.CLIENT)
-class UnbakedBlockStateModelSupplier(private val blockState: BlockState) : Supplier {
- override fun get() = UnbakedBlockStateModel(blockState)
+public interface IActiveItemTempDataHolder {
+ /**
+ * Gets the temporary data associated with the entity.
+ */
+ @Nullable
+ Object staffMod$getActiveItemTempData();
+
+ /**
+ * Associates a temporary data with an entity, overwriting previous data.
+ *
+ * @param value The data to associate
+ */
+ void staffMod$setActiveItemTempData(@Nullable Object value);
}
diff --git a/StaffMod/src/main/java/opekope2/avm_staff/mixin/BipedEntityModelMixin.java b/StaffMod/src/main/java/opekope2/avm_staff/mixin/BipedEntityModelMixin.java
index ba0e666ee..e2602e461 100644
--- a/StaffMod/src/main/java/opekope2/avm_staff/mixin/BipedEntityModelMixin.java
+++ b/StaffMod/src/main/java/opekope2/avm_staff/mixin/BipedEntityModelMixin.java
@@ -21,7 +21,8 @@
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.entity.LivingEntity;
-import opekope2.avm_staff.IStaffMod;
+import opekope2.avm_staff.api.StaffMod;
+import opekope2.avm_staff.api.staff.StaffRendererOverrideComponent;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -46,24 +47,32 @@ public abstract class BipedEntityModelMixin {
@Final
public ModelPart rightArm;
- @Unique
- private float staffMod$degToRad(float degrees) {
- return degrees * (float) Math.PI / 180f;
- }
+ @Shadow
+ @Final
+ public ModelPart head;
@Inject(method = "positionLeftArm", at = @At("TAIL"))
private void positionLeftArm(LivingEntity entity, CallbackInfo ci) {
- if (leftArmPose == BipedEntityModel.ArmPose.ITEM && entity.getActiveItem().isIn(IStaffMod.get().getStaffsTag())) {
- leftArm.yaw = staffMod$degToRad(entity.headYaw - entity.bodyYaw);
- leftArm.pitch = staffMod$degToRad(entity.getPitch() - 90f);
+ if (staffMod$pointForward(leftArmPose, entity)) {
+ leftArm.yaw = head.yaw;
+ leftArm.pitch = head.pitch - 0.5f * (float) Math.PI;
}
}
@Inject(method = "positionRightArm", at = @At("TAIL"))
private void positionRightArm(LivingEntity entity, CallbackInfo ci) {
- if (rightArmPose == BipedEntityModel.ArmPose.ITEM && entity.getActiveItem().isIn(IStaffMod.get().getStaffsTag())) {
- rightArm.yaw = staffMod$degToRad(entity.headYaw - entity.bodyYaw);
- rightArm.pitch = staffMod$degToRad(entity.getPitch() - 90f);
+ if (staffMod$pointForward(rightArmPose, entity)) {
+ rightArm.yaw = head.yaw;
+ rightArm.pitch = head.pitch - 0.5f * (float) Math.PI;
}
}
+
+ @Unique
+ private boolean staffMod$pointForward(BipedEntityModel.ArmPose armPose, LivingEntity entity) {
+ if (armPose != BipedEntityModel.ArmPose.ITEM) return false;
+ if (entity.getActiveItem().isIn(StaffMod.getStaffsTag())) return true;
+
+ StaffRendererOverrideComponent rendererOverride = entity.getMainHandStack().get(StaffMod.getStaffRendererOverrideComponentType().get());
+ return rendererOverride != null && rendererOverride.isActive();
+ }
}
diff --git a/StaffMod/src/main/java/opekope2/avm_staff/mixin/IAbstractFurnaceBlockEntityMixin.java b/StaffMod/src/main/java/opekope2/avm_staff/mixin/IAbstractFurnaceBlockEntityMixin.java
deleted file mode 100644
index e5837d923..000000000
--- a/StaffMod/src/main/java/opekope2/avm_staff/mixin/IAbstractFurnaceBlockEntityMixin.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.mixin;
-
-import net.minecraft.block.entity.AbstractFurnaceBlockEntity;
-import net.minecraft.server.world.ServerWorld;
-import net.minecraft.util.math.Vec3d;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.gen.Invoker;
-
-@Mixin(AbstractFurnaceBlockEntity.class)
-public interface IAbstractFurnaceBlockEntityMixin {
- @Invoker
- static void invokeDropExperience(ServerWorld world, Vec3d pos, int multiplier, float experience) {
- throw new AssertionError();
- }
-}
diff --git a/StaffMod/src/main/java/opekope2/avm_staff/mixin/LivingEntityMixin.java b/StaffMod/src/main/java/opekope2/avm_staff/mixin/LivingEntityMixin.java
index ebed235ad..82f9306bb 100644
--- a/StaffMod/src/main/java/opekope2/avm_staff/mixin/LivingEntityMixin.java
+++ b/StaffMod/src/main/java/opekope2/avm_staff/mixin/LivingEntityMixin.java
@@ -19,33 +19,34 @@
package opekope2.avm_staff.mixin;
import net.minecraft.entity.LivingEntity;
-import net.minecraft.item.ItemStack;
-import opekope2.avm_staff.IStaffMod;
-import opekope2.avm_staff.api.item.IDisablesShield;
-import opekope2.avm_staff.api.item.StaffItemHandler;
-import opekope2.avm_staff.util.StaffUtil;
+import opekope2.avm_staff.api.item.IActiveItemTempDataHolder;
+import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LivingEntity.class)
-public abstract class LivingEntityMixin {
- @Shadow
- public abstract ItemStack getMainHandStack();
+public abstract class LivingEntityMixin implements IActiveItemTempDataHolder {
+ @Unique
+ @Nullable
+ private Object staffMod$activeItemTempData;
- @Inject(method = "disablesShield", at = @At("HEAD"), cancellable = true)
- public void disableShield(CallbackInfoReturnable cir) {
- ItemStack mainHandStack = getMainHandStack();
- if (!mainHandStack.isOf(IStaffMod.get().getStaffItem())) return;
+ // Only invoke on server
+ @Inject(method = "clearActiveItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;setLivingFlag(IZ)V"))
+ public void clearActiveItem(CallbackInfo ci) {
+ staffMod$setActiveItemTempData(null);
+ }
- ItemStack itemInStaff = StaffUtil.getItemInStaff(mainHandStack);
- if (itemInStaff == null) return;
+ @Nullable
+ @Override
+ public Object staffMod$getActiveItemTempData() {
+ return staffMod$activeItemTempData;
+ }
- StaffItemHandler handlerOfItem = StaffUtil.getHandlerOfItem(itemInStaff);
- if (handlerOfItem instanceof IDisablesShield) {
- cir.setReturnValue(true);
- }
+ @Override
+ public void staffMod$setActiveItemTempData(@Nullable Object value) {
+ staffMod$activeItemTempData = value;
}
}
diff --git a/StaffMod/src/main/java/opekope2/avm_staff/mixin/MinecraftClientMixin.java b/StaffMod/src/main/java/opekope2/avm_staff/mixin/MinecraftClientMixin.java
deleted file mode 100644
index fd7cc2bf9..000000000
--- a/StaffMod/src/main/java/opekope2/avm_staff/mixin/MinecraftClientMixin.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.mixin;
-
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.network.ClientPlayNetworkHandler;
-import net.minecraft.client.network.ClientPlayerEntity;
-import net.minecraft.client.world.ClientWorld;
-import net.minecraft.entity.Entity;
-import net.minecraft.item.ItemStack;
-import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket;
-import net.minecraft.util.ActionResult;
-import net.minecraft.util.Hand;
-import net.minecraft.util.hit.EntityHitResult;
-import net.minecraft.util.hit.HitResult;
-import opekope2.avm_staff.internal.event_handler.StaffAttackHandlerKt;
-import opekope2.avm_staff.internal.networking.c2s.play.StaffAttackC2SPacket;
-import org.jetbrains.annotations.Nullable;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
-import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
-
-import java.util.Objects;
-
-@Mixin(MinecraftClient.class)
-public abstract class MinecraftClientMixin {
- @Shadow
- @Nullable
- public ClientPlayerEntity player;
-
- @Shadow
- @Nullable
- public ClientWorld world;
-
- @Shadow
- @Nullable
- public HitResult crosshairTarget;
-
- @Shadow
- @Nullable
- public abstract ClientPlayNetworkHandler getNetworkHandler();
-
- // Because Fabric API can't not swing hand (it always swings hand)
- @Inject(
- method = "doAttack",
- at = @At(
- value = "INVOKE",
- target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;attackEntity(Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/entity/Entity;)V"
- ),
- locals = LocalCapture.CAPTURE_FAILHARD,
- cancellable = true
- )
- private void handleEntityAttack(CallbackInfoReturnable cir) {
- assert player != null;
- assert world != null;
- assert crosshairTarget != null;
-
- Entity target = ((EntityHitResult) crosshairTarget).getEntity();
- ActionResult result = StaffAttackHandlerKt.attackEntity(player, world, Hand.MAIN_HAND, target);
- switch (result) {
- case SUCCESS -> {
- Objects.requireNonNull(getNetworkHandler())
- .sendPacket(PlayerInteractEntityC2SPacket.attack(target, player.isSneaking()));
- player.swingHand(Hand.MAIN_HAND);
- cir.setReturnValue(false);
- }
- case CONSUME, CONSUME_PARTIAL -> {
- Objects.requireNonNull(getNetworkHandler())
- .sendPacket(PlayerInteractEntityC2SPacket.attack(target, player.isSneaking()));
- cir.setReturnValue(false);
- }
- case FAIL -> cir.setReturnValue(false);
- }
- }
-
- @Inject(
- method = "doAttack",
- at = @At(
- value = "INVOKE",
- target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;hasLimitedAttackSpeed()Z",
- ordinal = 1
- ),
- cancellable = true
- )
- private void handleAttack(CallbackInfoReturnable cir) {
- assert player != null;
- assert world != null;
-
- ItemStack stackInHand = player.getStackInHand(Hand.MAIN_HAND);
-
- ActionResult result = StaffAttackHandlerKt.attack(stackInHand, world, player, Hand.MAIN_HAND);
- switch (result) {
- case SUCCESS -> {
- new StaffAttackC2SPacket().send();
- player.swingHand(Hand.MAIN_HAND);
- cir.setReturnValue(false);
- }
- case CONSUME, CONSUME_PARTIAL -> {
- new StaffAttackC2SPacket().send();
- cir.setReturnValue(false);
- }
- case FAIL -> cir.setReturnValue(false);
- }
- }
-}
diff --git a/StaffMod/src/main/java/opekope2/avm_staff/mixin/TntEntityMixin.java b/StaffMod/src/main/java/opekope2/avm_staff/mixin/TntEntityMixin.java
deleted file mode 100644
index e369e74e0..000000000
--- a/StaffMod/src/main/java/opekope2/avm_staff/mixin/TntEntityMixin.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.mixin;
-
-import net.minecraft.entity.Entity;
-import net.minecraft.entity.EntityType;
-import net.minecraft.entity.TntEntity;
-import net.minecraft.nbt.NbtCompound;
-import net.minecraft.nbt.NbtElement;
-import net.minecraft.world.World;
-import opekope2.avm_staff.api.entity.IImpactTnt;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-
-import java.util.List;
-
-@Mixin(TntEntity.class)
-public abstract class TntEntityMixin extends Entity implements IImpactTnt {
- private TntEntityMixin(EntityType> type, World world) {
- super(type, world);
- }
-
- @Shadow
- protected abstract void explode();
-
- @Inject(method = "initDataTracker", at = @At("TAIL"))
- private void initDataTracker(CallbackInfo ci) {
- dataTracker.startTracking(EXPLODES_ON_IMPACT, false);
- }
-
- @Inject(method = "writeCustomDataToNbt", at = @At("TAIL"))
- private void writeCustomDataToNbt(NbtCompound nbt, CallbackInfo ci) {
- nbt.putBoolean(EXPLODES_ON_IMPACT_NBT_KEY, explodesOnImpact());
- }
-
- @Inject(method = "readCustomDataFromNbt", at = @At("TAIL"))
- private void readCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) {
- if (nbt.contains(EXPLODES_ON_IMPACT_NBT_KEY, NbtElement.BYTE_TYPE)) {
- explodeOnImpact(nbt.getBoolean(EXPLODES_ON_IMPACT_NBT_KEY));
- }
- }
-
- @Inject(
- method = "tick",
- at = @At(
- value = "INVOKE",
- target = "Lnet/minecraft/entity/TntEntity;move(Lnet/minecraft/entity/MovementType;Lnet/minecraft/util/math/Vec3d;)V",
- shift = At.Shift.AFTER
- )
- )
- private void explodeOnImpact(CallbackInfo ci) {
- if (!explodesOnImpact()) return;
-
- boolean explode = horizontalCollision || verticalCollision;
- if (!explode) {
- List collisions = getWorld().getOtherEntities(this, getBoundingBox(), entity -> true);
- explode = !collisions.isEmpty();
-
- for (Entity collider : collisions) {
- if (collider instanceof TntEntity tnt && ((IImpactTnt) tnt).explodesOnImpact()) {
- // Force explode other TNT, because the current TNT gets discarded before the other TNT gets processed
- tnt.setFuse(0);
- }
- }
- }
-
- if (!explode) return;
-
- if (!getWorld().isClient) {
- // Server sends EntitiesDestroyS2CPacket to client, because the position desync makes collision detection unreliable
- discard();
- explode();
- }
- }
-
- @Override
- public boolean explodesOnImpact() {
- return dataTracker.get(EXPLODES_ON_IMPACT);
- }
-
- @Override
- public void explodeOnImpact(boolean explode) {
- dataTracker.set(EXPLODES_ON_IMPACT, explode);
- }
-}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/IStaffMod.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/IStaffMod.kt
deleted file mode 100644
index f5bca0589..000000000
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/IStaffMod.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff
-
-import net.minecraft.client.particle.ParticleManager
-import net.minecraft.item.Item
-import net.minecraft.particle.DefaultParticleType
-import net.minecraft.registry.tag.TagKey
-import opekope2.avm_staff.api.item.StaffItem
-import opekope2.avm_staff.internal.platform.getStaffMod
-
-/**
- * A non-loader-specific interface of the Staff Mod.
- */
-interface IStaffMod {
- /**
- * Gets the Staff item registered in Minecraft.
- *
- * Due to how Forge registries work, *always* use this getter instead of storing the result.
- */
- val staffItem: StaffItem
-
- /**
- * Returns if Staff Mod is running on the physical client.
- */
- val isPhysicalClient: Boolean
-
- /**
- * Gets the [TagKey] containing all the staffs.
- */
- val staffsTag: TagKey
-
-
- /**
- * Gets the flamethrower's flame particle type.
- *
- * Due to how Forge registries work, *always* use this getter instead of storing the result.
- *
- * @see ParticleManager.addParticle
- */
- val flamethrowerParticleType: DefaultParticleType
-
- /**
- * Gets the soul fire flamethrower's flame particle type.
- *
- * Due to how Forge registries work, *always* use this getter instead of storing the result.
- *
- * @see ParticleManager.addParticle
- */
- val soulFlamethrowerParticleType: DefaultParticleType
-
- companion object Holder {
- /**
- * Gets the currently running loader-specific Staff Mod instance.
- */
- @JvmStatic
- fun get(): IStaffMod = getStaffMod()
- }
-}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/StaffMod.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/StaffMod.kt
new file mode 100644
index 000000000..3ef3e0ee8
--- /dev/null
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/StaffMod.kt
@@ -0,0 +1,230 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+@file: JvmName("StaffMod")
+@file: Suppress("unused")
+
+package opekope2.avm_staff.api
+
+import com.mojang.serialization.Codec
+import dev.architectury.registry.CreativeTabRegistry
+import dev.architectury.registry.registries.DeferredRegister
+import dev.architectury.registry.registries.RegistrySupplier
+import net.minecraft.client.particle.ParticleManager
+import net.minecraft.component.DataComponentType
+import net.minecraft.entity.EntityType
+import net.minecraft.entity.SpawnGroup
+import net.minecraft.item.Item
+import net.minecraft.item.ItemGroup
+import net.minecraft.item.Items
+import net.minecraft.item.SmithingTemplateItem
+import net.minecraft.network.codec.PacketCodec
+import net.minecraft.particle.SimpleParticleType
+import net.minecraft.registry.RegistryKeys
+import net.minecraft.registry.tag.TagKey
+import net.minecraft.text.Text
+import net.minecraft.util.Identifier
+import net.minecraft.util.Rarity
+import opekope2.avm_staff.api.entity.ImpactTntEntity
+import opekope2.avm_staff.api.item.CrownItem
+import opekope2.avm_staff.api.item.StaffItem
+import opekope2.avm_staff.api.staff.StaffHandler
+import opekope2.avm_staff.api.staff.StaffInfusionSmithingRecipeTextures
+import opekope2.avm_staff.api.staff.StaffItemComponent
+import opekope2.avm_staff.api.staff.StaffRendererOverrideComponent
+import opekope2.avm_staff.internal.MinecraftUnit
+import opekope2.avm_staff.internal.createCrownItem
+import opekope2.avm_staff.internal.createStaffItem
+import opekope2.avm_staff.internal.createStaffRendererItem
+import opekope2.avm_staff.util.MOD_ID
+import opekope2.avm_staff.util.mutableItemStackInStaff
+
+private val ITEMS = DeferredRegister.create(MOD_ID, RegistryKeys.ITEM)
+private val ITEM_GROUPS = DeferredRegister.create(MOD_ID, RegistryKeys.ITEM_GROUP)
+private val ENTITY_TYPES = DeferredRegister.create(MOD_ID, RegistryKeys.ENTITY_TYPE)
+private val PARTICLE_TYPES = DeferredRegister.create(MOD_ID, RegistryKeys.PARTICLE_TYPE)
+private val DATA_COMPONENT_TYPES = DeferredRegister.create(MOD_ID, RegistryKeys.DATA_COMPONENT_TYPE)
+
+/**
+ * Item registered as `avm_staff:faint_staff_rod`.
+ */
+val faintStaffRodItem: RegistrySupplier
- = ITEMS.register("faint_staff_rod") {
+ Item(Item.Settings().`arch$tab`(staffModItemGroup))
+}
+
+/**
+ * Item registered as `avm_staff:faint_royal_staff_head`.
+ */
+val faintRoyalStaffHeadItem: RegistrySupplier
- = ITEMS.register("faint_royal_staff_head") {
+ Item(
+ Item.Settings().maxCount(16).rarity(Rarity.RARE).`arch$tab`(staffModItemGroup)
+ )
+}
+
+/**
+ * Item registered as `avm_staff:faint_royal_staff`.
+ */
+val faintRoyalStaffItem: RegistrySupplier
- = ITEMS.register("faint_royal_staff") {
+ createStaffRendererItem(Item.Settings().maxCount(1).rarity(Rarity.RARE).`arch$tab`(staffModItemGroup))
+}
+
+/**
+ * Item registered as `avm_staff:royal_staff`.
+ */
+val royalStaffItem: RegistrySupplier = ITEMS.register("royal_staff") {
+ createStaffItem(
+ Item.Settings().maxCount(1).rarity(Rarity.EPIC).attributeModifiers(StaffHandler.Default.ATTRIBUTE_MODIFIERS)
+ .`arch$tab`(staffModItemGroup)
+ )
+}
+
+/**
+ * Item registered as `avm_staff:royal_staff_ingredient`.
+ */
+val royalStaffIngredientItem: RegistrySupplier
- = ITEMS.register("royal_staff_ingredient") {
+ Item(Item.Settings().`arch$tab`(staffModItemGroup))
+}
+
+/**
+ * Item registered as `avm_staff:crown_of_king_orange`.
+ */
+val crownOfKingOrangeItem: RegistrySupplier = ITEMS.register("crown_of_king_orange") {
+ createCrownItem(Item.Settings().maxCount(1).rarity(Rarity.UNCOMMON).`arch$tab`(staffModItemGroup))
+}
+
+/**
+ * Item registered as `avm_staff:staff_infusion_smithing_template`.
+ */
+val staffInfusionSmithingTemplateItem: RegistrySupplier
- = ITEMS.register("staff_infusion_smithing_template") {
+ SmithingTemplateItem(
+ Text.translatable("item.$MOD_ID.staff_infusion_smithing_template.applies_to")
+ .formatted(SmithingTemplateItem.DESCRIPTION_FORMATTING),
+ SmithingTemplateItem.ARMOR_TRIM_INGREDIENTS_TEXT,
+ Text.translatable("item.$MOD_ID.staff_infusion_smithing_template.title")
+ .formatted(SmithingTemplateItem.TITLE_FORMATTING),
+ Text.translatable("item.$MOD_ID.staff_infusion_smithing_template.base_slot_description"),
+ SmithingTemplateItem.ARMOR_TRIM_ADDITIONS_SLOT_DESCRIPTION_TEXT,
+ StaffInfusionSmithingRecipeTextures.baseSlotTextures,
+ StaffInfusionSmithingRecipeTextures.additionsSlotTextures
+ )
+}
+
+/**
+ * Tag registered as `avm_staff:staffs`.
+ */
+val staffsTag: TagKey
- = TagKey.of(RegistryKeys.ITEM, Identifier(MOD_ID, "staffs"))
+
+/**
+ * Item group containing items added by Staff Mod.
+ */
+val staffModItemGroup: RegistrySupplier = ITEM_GROUPS.register("${MOD_ID}_items") {
+ CreativeTabRegistry.create(Text.translatable("itemGroup.${MOD_ID}_items")) {
+ royalStaffItem.get().defaultStack.apply {
+ mutableItemStackInStaff = Items.COMMAND_BLOCK.defaultStack
+ }
+ }
+}
+
+/**
+ * Entity registered as `avm_staff:impact_tnt`.
+ */
+val impactTntEntityType: RegistrySupplier> = ENTITY_TYPES.register("impact_tnt") {
+ EntityType.Builder.create(::ImpactTntEntity, SpawnGroup.MISC)
+ .makeFireImmune()
+ .dimensions(EntityType.TNT.dimensions.width, EntityType.TNT.dimensions.height)
+ .eyeHeight(EntityType.TNT.dimensions.eyeHeight)
+ .maxTrackingRange(EntityType.TNT.maxTrackDistance)
+ .trackingTickInterval(EntityType.TNT.trackTickInterval)
+ .build(Identifier(MOD_ID, "impact_tnt").toString())
+}
+
+/**
+ * Particle registered as `avm_staff:flame`.
+ *
+ * @see ParticleManager.addParticle
+ */
+val flamethrowerParticleType: RegistrySupplier =
+ PARTICLE_TYPES.register("flame") { SimpleParticleType(false) }
+
+/**
+ * Particle registered as `avm_staff:soul_fire_flame`.
+ *
+ * @see ParticleManager.addParticle
+ */
+val soulFlamethrowerParticleType: RegistrySupplier =
+ PARTICLE_TYPES.register("soul_fire_flame") { SimpleParticleType(false) }
+
+/**
+ * Data component registered as `avm_staff:staff_item`. Stores the item inserted into the staff.
+ */
+val staffItemComponentType: RegistrySupplier> =
+ DATA_COMPONENT_TYPES.register("staff_item") {
+ DataComponentType.builder()
+ .codec(StaffItemComponent.CODEC)
+ .packetCodec(StaffItemComponent.PACKET_CODEC)
+ .build()
+ }
+
+/**
+ * Data component registered as `avm_staff:rocket_mode`. Stores if a campfire staff should propel its user.
+ */
+val rocketModeComponentType: RegistrySupplier> =
+ DATA_COMPONENT_TYPES.register("rocket_mode") {
+ DataComponentType.builder()
+ .codec(Codec.unit(MinecraftUnit.INSTANCE))
+ .packetCodec(PacketCodec.unit(MinecraftUnit.INSTANCE))
+ .build()
+ }
+
+/**
+ * Data component registered as `avm_staff:furnace_lit`. Stores if a furnace staff is lit. Only used for rendering.
+ */
+val furnaceLitComponentType: RegistrySupplier> =
+ DATA_COMPONENT_TYPES.register("furnace_lit") {
+ DataComponentType.builder()
+ .codec(Codec.unit(MinecraftUnit.INSTANCE))
+ .packetCodec(PacketCodec.unit(MinecraftUnit.INSTANCE))
+ .build()
+ }
+
+/**
+ * Data component registered as `avm_staff:staff_renderer_override`. Specifies how a staff is rendered. Intended for
+ * Isometric Renders mod compatibility.
+ */
+val staffRendererOverrideComponentType: RegistrySupplier> =
+ DATA_COMPONENT_TYPES.register("staff_renderer_override") {
+ DataComponentType.builder()
+ .codec(StaffRendererOverrideComponent.CODEC)
+ .packetCodec(StaffRendererOverrideComponent.PACKET_CODEC)
+ .build()
+ }
+
+/**
+ * @suppress
+ */
+@JvmSynthetic
+internal fun registerContent() {
+ ITEMS.register()
+ ITEM_GROUPS.register()
+ ENTITY_TYPES.register()
+ PARTICLE_TYPES.register()
+ DATA_COMPONENT_TYPES.register()
+
+ // Because SmithingTemplateItem doesn't take Item.Settings in its constructor
+ CreativeTabRegistry.append(staffModItemGroup, staffInfusionSmithingTemplateItem)
+}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/entity/ImpactTntEntity.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/entity/ImpactTntEntity.kt
new file mode 100644
index 000000000..ebedb9104
--- /dev/null
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/entity/ImpactTntEntity.kt
@@ -0,0 +1,73 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+package opekope2.avm_staff.api.entity
+
+import net.minecraft.entity.*
+import net.minecraft.predicate.entity.EntityPredicates
+import net.minecraft.util.math.Vec3d
+import net.minecraft.world.World
+import opekope2.avm_staff.api.impactTntEntityType
+
+class ImpactTntEntity(entityType: EntityType, world: World) : TntEntity(entityType, world), Ownable {
+ private var owner: LivingEntity? = null
+
+ constructor(world: World, x: Double, y: Double, z: Double, velocity: Vec3d, igniter: LivingEntity?) :
+ this(impactTntEntityType.get(), world) {
+ setPosition(x, y, z)
+ this.velocity = velocity
+ fuse = 80
+ prevX = x
+ prevY = y
+ prevZ = z
+ owner = igniter
+ }
+
+ override fun move(movementType: MovementType?, movement: Vec3d?) {
+ super.move(movementType, movement)
+ explodeOnImpact()
+ }
+
+ private fun explodeOnImpact() {
+ var explode = horizontalCollision || verticalCollision
+ if (!explode) {
+ val collisions = world.getOtherEntities(this, boundingBox, EntityPredicates.EXCEPT_SPECTATOR)
+ explode = collisions.isNotEmpty()
+
+ for (collider in collisions) {
+ if (collider is ImpactTntEntity) {
+ // Force explode other TNT, because the current TNT gets discarded before the other TNT gets processed
+ collider.fuse = 0
+ }
+ }
+ }
+
+ if (explode && !world.isClient) {
+ fuse = 0
+ }
+ }
+
+ override fun copyFrom(original: Entity?) {
+ super.copyFrom(original)
+ if (original is ImpactTntEntity) {
+ owner = original.owner
+ }
+ }
+
+ override fun getOwner() = owner
+}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/CrownItem.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/CrownItem.kt
new file mode 100644
index 000000000..1286874e1
--- /dev/null
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/CrownItem.kt
@@ -0,0 +1,50 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+package opekope2.avm_staff.api.item
+
+import net.minecraft.block.DispenserBlock
+import net.minecraft.entity.EquipmentSlot
+import net.minecraft.entity.player.PlayerEntity
+import net.minecraft.item.ArmorItem
+import net.minecraft.item.Equipment
+import net.minecraft.item.Item
+import net.minecraft.item.ItemStack
+import net.minecraft.registry.entry.RegistryEntry
+import net.minecraft.sound.SoundEvent
+import net.minecraft.sound.SoundEvents
+import net.minecraft.util.Hand
+import net.minecraft.util.TypedActionResult
+import net.minecraft.world.World
+
+/**
+ * A crown, which makes piglins neutral when worn, just like gold armor.
+ */
+open class CrownItem(settings: Settings) : Item(settings), Equipment {
+ init {
+ DispenserBlock.registerBehavior(this, ArmorItem.DISPENSER_BEHAVIOR)
+ }
+
+ override fun use(world: World, user: PlayerEntity, hand: Hand): TypedActionResult {
+ return equipAndSwap(this, world, user, hand)
+ }
+
+ override fun getEquipSound(): RegistryEntry = SoundEvents.ITEM_ARMOR_EQUIP_GOLD
+
+ override fun getSlotType(): EquipmentSlot = EquipmentSlot.HEAD
+}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/StaffItem.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/StaffItem.kt
index cb1e352b6..fbbaf34e6 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/StaffItem.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/StaffItem.kt
@@ -18,6 +18,7 @@
package opekope2.avm_staff.api.item
+import net.minecraft.component.DataComponentTypes
import net.minecraft.entity.ItemEntity
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.player.PlayerEntity
@@ -30,46 +31,48 @@ import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.TypedActionResult
import net.minecraft.world.World
-import opekope2.avm_staff.util.handlerOfItemOrFallback
-import opekope2.avm_staff.util.isItemInStaff
-import opekope2.avm_staff.util.itemInStaff
-import java.util.stream.Stream
+import opekope2.avm_staff.api.staff.StaffHandler
+import opekope2.avm_staff.util.*
/**
- * Staff item dispatching functionality to [StaffItemHandler] without loader specific functionality.
- * Implementing `FabricItem` or `IForgeItem` (on the appropriate loader) is highly recommended when extending the class
- * to pass loader-specific functionality to [StaffItemHandler].
+ * Staff item dispatching functionality to [StaffHandler] without loader specific functionality.
+ * Implementing loader-specific interfaces is highly recommended when extending the class to pass loader-specific
+ * functionality to [StaffHandler].
*/
abstract class StaffItem(settings: Settings) : Item(settings) {
override fun onItemEntityDestroyed(entity: ItemEntity) {
val staffStack = entity.stack
- val staffItem = staffStack.itemInStaff ?: return
- ItemUsage.spawnItemContents(entity, Stream.of(staffItem))
+ val staffItem = staffStack.mutableItemStackInStaff ?: return
+ ItemUsage.spawnItemContents(entity, listOf(staffItem))
+ }
+
+ override fun postProcessComponents(stack: ItemStack) {
+ stack[DataComponentTypes.ATTRIBUTE_MODIFIERS] = stack.itemInStaff.staffHandlerOrDefault.attributeModifiers
}
override fun getMaxUseTime(stack: ItemStack): Int {
- return stack.itemInStaff.handlerOfItemOrFallback.maxUseTime
+ return stack.itemInStaff.staffHandlerOrDefault.maxUseTime
}
override fun use(world: World, user: PlayerEntity, hand: Hand): TypedActionResult {
val staffStack = user.getStackInHand(hand)
- return staffStack.itemInStaff.handlerOfItemOrFallback.use(staffStack, world, user, hand)
+ return staffStack.itemInStaff.staffHandlerOrDefault.use(staffStack, world, user, hand)
}
override fun usageTick(world: World, user: LivingEntity, stack: ItemStack, remainingUseTicks: Int) {
- stack.itemInStaff.handlerOfItemOrFallback.usageTick(stack, world, user, remainingUseTicks)
+ stack.itemInStaff.staffHandlerOrDefault.usageTick(stack, world, user, remainingUseTicks)
}
override fun onStoppedUsing(stack: ItemStack, world: World, user: LivingEntity, remainingUseTicks: Int) {
- stack.itemInStaff.handlerOfItemOrFallback.onStoppedUsing(stack, world, user, remainingUseTicks)
+ stack.itemInStaff.staffHandlerOrDefault.onStoppedUsing(stack, world, user, remainingUseTicks)
}
override fun finishUsing(stack: ItemStack, world: World, user: LivingEntity): ItemStack {
- return stack.itemInStaff.handlerOfItemOrFallback.finishUsing(stack, world, user)
+ return stack.itemInStaff.staffHandlerOrDefault.finishUsing(stack, world, user)
}
override fun useOnBlock(context: ItemUsageContext): ActionResult {
- return context.stack.itemInStaff.handlerOfItemOrFallback.useOnBlock(
+ return context.stack.itemInStaff.staffHandlerOrDefault.useOnBlock(
context.stack,
context.world,
context.player ?: return ActionResult.PASS,
@@ -80,11 +83,11 @@ abstract class StaffItem(settings: Settings) : Item(settings) {
}
override fun useOnEntity(stack: ItemStack, user: PlayerEntity, entity: LivingEntity, hand: Hand): ActionResult {
- return stack.itemInStaff.handlerOfItemOrFallback.useOnEntity(stack, user.world, user, entity, hand)
+ return stack.itemInStaff.staffHandlerOrDefault.useOnEntity(stack, user.world, user, entity, hand)
}
override fun getName(stack: ItemStack): Text {
- val staffItem = stack.itemInStaff ?: return super.getName(stack)
+ val staffItem = stack.itemStackInStaff ?: return super.getName(stack)
val staffItemText = Text.translatable(staffItem.item.getTranslationKey(staffItem))
return Text.translatable(getTranslationKey(stack), staffItemText)
}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/model/IStaffItemUnbakedModel.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/model/IStaffItemUnbakedModel.kt
deleted file mode 100644
index 4e765fc5a..000000000
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/model/IStaffItemUnbakedModel.kt
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.api.item.model
-
-import net.fabricmc.api.EnvType
-import net.fabricmc.api.Environment
-import net.minecraft.client.render.model.BakedModel
-import net.minecraft.client.render.model.Baker
-import net.minecraft.client.render.model.ModelBakeSettings
-import net.minecraft.client.render.model.UnbakedModel
-import net.minecraft.client.render.model.json.Transformation
-import net.minecraft.client.texture.Sprite
-import net.minecraft.client.util.SpriteIdentifier
-import net.minecraft.util.Identifier
-import opekope2.avm_staff.api.item.StaffItemHandler
-import java.util.function.Function
-
-/**
- * An [UnbakedModel] of an item, which can be placed into a staff.
- *
- * @see StaffItemHandler.register
- */
-@Environment(EnvType.CLIENT)
-interface IStaffItemUnbakedModel : UnbakedModel {
- override fun bake(
- baker: Baker,
- textureGetter: Function,
- rotationContainer: ModelBakeSettings,
- modelId: Identifier
- ): BakedModel? = bake(baker, textureGetter, rotationContainer, modelId, Transformation.IDENTITY)
-
- /**
- * Bakes the item model.
- *
- * @param baker The baker used to load and bake additional models
- * @param textureGetter Function to get a [Sprite] from a [SpriteIdentifier]
- * @param rotationContainer The model rotation container
- * @param modelId The identifier of the staff model being baked. This is not the ID of the item in the staff
- * @param transformation The transformation, which transforms the item into the staff
- */
- fun bake(
- baker: Baker,
- textureGetter: Function,
- rotationContainer: ModelBakeSettings,
- modelId: Identifier,
- transformation: Transformation
- ): IStaffItemBakedModel?
-}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/model/UnbakedBlockStateModel.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/model/UnbakedBlockStateModel.kt
deleted file mode 100644
index ee8075a30..000000000
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/model/UnbakedBlockStateModel.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.api.item.model
-
-import net.fabricmc.api.EnvType
-import net.fabricmc.api.Environment
-import net.minecraft.block.BlockState
-import net.minecraft.client.render.block.BlockModels
-import net.minecraft.client.render.model.Baker
-import net.minecraft.client.render.model.ModelBakeSettings
-import net.minecraft.client.render.model.UnbakedModel
-import net.minecraft.client.render.model.json.Transformation
-import net.minecraft.client.texture.Sprite
-import net.minecraft.client.util.SpriteIdentifier
-import net.minecraft.util.Identifier
-import opekope2.avm_staff.util.transform
-import java.util.function.Function
-
-/**
- * Default implementation of [IStaffItemUnbakedModel].
- *
- * @param blockState The block state to bake
- */
-@Environment(EnvType.CLIENT)
-class UnbakedBlockStateModel(private val blockState: BlockState) : IStaffItemUnbakedModel {
- private val blockStateId = BlockModels.getModelId(blockState)
- private val dependencies = setOf(blockStateId)
-
- override fun getModelDependencies() = dependencies
-
- override fun setParents(modelLoader: Function?) {
- }
-
- override fun bake(
- baker: Baker,
- textureGetter: Function,
- rotationContainer: ModelBakeSettings,
- modelId: Identifier,
- transformation: Transformation
- ): IStaffItemBakedModel? {
- val baked = baker.bake(blockStateId, rotationContainer) ?: return null
-
- return StaffItemBakedModel(baked.transform(blockState, transformation, textureGetter))
- }
-}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/renderer/BlockStateStaffItemRenderer.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/renderer/BlockStateStaffItemRenderer.kt
new file mode 100644
index 000000000..51d9dc1db
--- /dev/null
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/renderer/BlockStateStaffItemRenderer.kt
@@ -0,0 +1,56 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+package opekope2.avm_staff.api.item.renderer
+
+import net.fabricmc.api.EnvType
+import net.fabricmc.api.Environment
+import net.minecraft.block.BlockState
+import net.minecraft.client.MinecraftClient
+import net.minecraft.client.render.VertexConsumerProvider
+import net.minecraft.client.render.block.BlockModels
+import net.minecraft.client.render.model.json.ModelTransformationMode
+import net.minecraft.client.util.math.MatrixStack
+import net.minecraft.item.ItemStack
+
+/**
+ * A [IStaffItemRenderer], always which renders a single block state.
+ *
+ * @param blockState The block state to render
+ */
+@Environment(EnvType.CLIENT)
+class BlockStateStaffItemRenderer(blockState: BlockState) : IStaffItemRenderer {
+ private val blockStateId = BlockModels.getModelId(blockState)
+ private val blockItem = blockState.block.asItem().defaultStack
+
+ override fun renderItemInStaff(
+ staffStack: ItemStack,
+ mode: ModelTransformationMode,
+ matrices: MatrixStack,
+ vertexConsumers: VertexConsumerProvider,
+ light: Int,
+ overlay: Int
+ ) {
+ val itemRenderer = MinecraftClient.getInstance().itemRenderer
+ val modelManager = MinecraftClient.getInstance().bakedModelManager
+ val model = modelManager.getModel(blockStateId)
+ itemRenderer.renderItem(
+ blockItem, ModelTransformationMode.NONE, false, matrices, vertexConsumers, light, overlay, model
+ )
+ }
+}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/renderer/IStaffItemRenderer.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/renderer/IStaffItemRenderer.kt
new file mode 100644
index 000000000..14738ccdb
--- /dev/null
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/renderer/IStaffItemRenderer.kt
@@ -0,0 +1,91 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+package opekope2.avm_staff.api.item.renderer
+
+import net.fabricmc.api.EnvType
+import net.fabricmc.api.Environment
+import net.minecraft.client.render.VertexConsumerProvider
+import net.minecraft.client.render.model.json.ModelTransformationMode
+import net.minecraft.client.util.math.MatrixStack
+import net.minecraft.item.ItemStack
+import net.minecraft.util.Identifier
+
+/**
+ * A renderer for an item, which can be placed into a staff.
+ *
+ * @see IStaffItemRenderer.register
+ */
+@Environment(EnvType.CLIENT)
+fun interface IStaffItemRenderer {
+ /**
+ * Renders an item.
+ *
+ * @param staffStack The staff item stack
+ * @param mode The transformation the staff is rendered in. You likely want to pass
+ * [ModelTransformationMode.NONE] to rendering calls
+ * @param matrices Matrix stack for rendering calls
+ * @param vertexConsumers Vertex consumer provider for rendering calls
+ * @param light Light component for rendering calls
+ * @param overlay Overlay component for rendering calls
+ */
+ fun renderItemInStaff(
+ staffStack: ItemStack,
+ mode: ModelTransformationMode,
+ matrices: MatrixStack,
+ vertexConsumers: VertexConsumerProvider,
+ light: Int,
+ overlay: Int
+ )
+
+ @Environment(EnvType.CLIENT)
+ companion object {
+ private val staffItemRenderers = mutableMapOf()
+
+ /**
+ * Registers a renderer for a given [item ID][staffItem].
+ *
+ * @param staffItem The item ID to register a renderer for
+ * @param renderer The item's renderer
+ * @return `true`, if the registration was successful, `false`, if a renderer for the item was already registered
+ */
+ @JvmStatic
+ fun register(staffItem: Identifier, renderer: IStaffItemRenderer): Boolean {
+ if (staffItem in staffItemRenderers) return false
+
+ staffItemRenderers[staffItem] = renderer
+ return true
+ }
+
+ /**
+ * Checks if a renderer for the [given item][staffItem] is registered.
+ *
+ * @param staffItem The item ID, which can be inserted into the staff
+ */
+ @JvmStatic
+ operator fun contains(staffItem: Identifier): Boolean = staffItem in staffItemRenderers
+
+ /**
+ * Gets the registered renderer for the [given item][staffItem] or `null`, if no renderer was registered.
+ *
+ * @param staffItem The item ID, which can be inserted into the staff
+ */
+ @JvmStatic
+ operator fun get(staffItem: Identifier): IStaffItemRenderer? = staffItemRenderers[staffItem]
+ }
+}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/renderer/StaffRenderer.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/renderer/StaffRenderer.kt
new file mode 100644
index 000000000..9de27cc5d
--- /dev/null
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/renderer/StaffRenderer.kt
@@ -0,0 +1,217 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+package opekope2.avm_staff.api.item.renderer
+
+import net.fabricmc.api.EnvType
+import net.fabricmc.api.Environment
+import net.minecraft.client.MinecraftClient
+import net.minecraft.client.render.VertexConsumerProvider
+import net.minecraft.client.render.model.BakedModel
+import net.minecraft.client.render.model.json.ModelTransformationMode
+import net.minecraft.client.util.math.MatrixStack
+import net.minecraft.item.ItemStack
+import net.minecraft.registry.Registries
+import opekope2.avm_staff.api.staffRendererOverrideComponentType
+import opekope2.avm_staff.internal.model.HEAD_SEED
+import opekope2.avm_staff.internal.model.ITEM_SEED
+import opekope2.avm_staff.internal.model.ROD_BOTTOM_SEED
+import opekope2.avm_staff.internal.model.ROD_TOP_SEED
+import opekope2.avm_staff.util.itemStackInStaff
+import opekope2.avm_staff.util.push
+
+/**
+ * Builtin model item renderer for staffs.
+ */
+@Environment(EnvType.CLIENT)
+object StaffRenderer {
+ /**
+ * Renders the staff.
+ *
+ * @param staffStack The staff item stack
+ * @param mode The transformation the staff is rendered in
+ * @param matrices Matrix stack for rendering calls
+ * @param vertexConsumers Vertex consumer provider for rendering calls
+ * @param light Light component for rendering calls
+ * @param overlay Overlay component for rendering calls
+ */
+ fun renderStaff(
+ staffStack: ItemStack,
+ mode: ModelTransformationMode,
+ matrices: MatrixStack,
+ vertexConsumers: VertexConsumerProvider,
+ light: Int,
+ overlay: Int
+ ) {
+ val renderMode = staffStack[staffRendererOverrideComponentType.get()]?.renderMode ?: mode
+
+ when (renderMode) {
+ ModelTransformationMode.GUI -> renderInventoryStaff(
+ renderMode, staffStack, matrices, vertexConsumers, light, overlay
+ )
+
+ ModelTransformationMode.FIXED -> renderItemFrameStaff(
+ renderMode, staffStack, matrices, vertexConsumers, light, overlay
+ )
+
+ else -> renderFullStaff(
+ renderMode, staffStack, matrices, vertexConsumers, light, overlay
+ )
+ }
+ }
+
+ private fun renderFullStaff(
+ mode: ModelTransformationMode,
+ staffStack: ItemStack,
+ matrices: MatrixStack,
+ vertexConsumers: VertexConsumerProvider,
+ light: Int,
+ overlay: Int
+ ) {
+ matrices.push {
+ translate(0.5f, 0.5f, 0.5f)
+
+ // Head
+ push {
+ translate(0f, 16f / 16f, 0f)
+ renderPart(staffStack, this, vertexConsumers, light, overlay, HEAD_SEED)
+
+ // Item
+ staffStack.itemStackInStaff?.let { itemInStaff ->
+ renderItem(mode, this, staffStack, itemInStaff, light, overlay, vertexConsumers)
+ }
+ }
+
+ // Rod (top)
+ push {
+ translate(0f, 2f / 16f, 0f)
+ renderPart(staffStack, this, vertexConsumers, light, overlay, ROD_TOP_SEED)
+ }
+
+ // Rod (bottom)
+ push {
+ translate(0f, -12f / 16f, 0f)
+ renderPart(staffStack, this, vertexConsumers, light, overlay, ROD_BOTTOM_SEED)
+ }
+ }
+ }
+
+ private fun renderInventoryStaff(
+ mode: ModelTransformationMode,
+ staffStack: ItemStack,
+ matrices: MatrixStack,
+ vertexConsumers: VertexConsumerProvider,
+ light: Int,
+ overlay: Int
+ ) {
+ matrices.push {
+ translate(0.5f, 0.5f, 0.5f)
+
+ // Head
+ push {
+ renderPart(staffStack, this, vertexConsumers, light, overlay, HEAD_SEED)
+
+ // Item
+ staffStack.itemStackInStaff?.let { itemInStaff ->
+ renderItem(mode, this, staffStack, itemInStaff, light, overlay, vertexConsumers)
+ }
+ }
+ }
+ }
+
+ private fun renderItemFrameStaff(
+ mode: ModelTransformationMode,
+ staffStack: ItemStack,
+ matrices: MatrixStack,
+ vertexConsumers: VertexConsumerProvider,
+ light: Int,
+ overlay: Int
+ ) {
+ matrices.push {
+ translate(0.5f, 0.5f, 0.5f)
+
+ // Head
+ push {
+ translate(0f, 9f / 16f, 0f)
+ renderPart(staffStack, this, vertexConsumers, light, overlay, HEAD_SEED)
+
+ // Item
+ staffStack.itemStackInStaff?.let { itemInStaff ->
+ renderItem(mode, this, staffStack, itemInStaff, light, overlay, vertexConsumers)
+ }
+ }
+
+ // Rod (top)
+ push {
+ translate(0f, -5f / 16f, 0f)
+ renderPart(staffStack, this, vertexConsumers, light, overlay, ROD_TOP_SEED)
+ }
+ }
+ }
+
+ private fun renderItem(
+ mode: ModelTransformationMode,
+ matrices: MatrixStack,
+ staffStack: ItemStack,
+ itemStackInStaff: ItemStack,
+ light: Int,
+ overlay: Int,
+ vertexConsumers: VertexConsumerProvider
+ ) {
+ matrices.push {
+ safeGetModel(staffStack, ITEM_SEED).transformation.fixed.apply(false, this)
+
+ val staffItemRenderer = IStaffItemRenderer[Registries.ITEM.getId(itemStackInStaff.item)]
+ if (staffItemRenderer != null) {
+ staffItemRenderer.renderItemInStaff(staffStack, mode, this, vertexConsumers, light, overlay)
+ } else {
+ val itemRenderer = MinecraftClient.getInstance().itemRenderer
+
+ val model = MinecraftClient.getInstance().bakedModelManager.missingModel
+ itemRenderer.renderItem(itemStackInStaff, mode, false, this, vertexConsumers, light, overlay, model)
+ }
+ }
+ }
+
+ private fun renderPart(
+ staffStack: ItemStack,
+ matrices: MatrixStack,
+ vertexConsumers: VertexConsumerProvider,
+ light: Int,
+ overlay: Int,
+ partSeed: Int
+ ) {
+ val itemRenderer = MinecraftClient.getInstance().itemRenderer
+
+ val model = safeGetModel(staffStack, partSeed)
+
+ itemRenderer.renderItem(
+ staffStack, ModelTransformationMode.NONE, false, matrices, vertexConsumers, light, overlay, model
+ )
+ }
+
+ private fun safeGetModel(staffStack: ItemStack, partSeed: Int): BakedModel {
+ val itemRenderer = MinecraftClient.getInstance().itemRenderer
+
+ val model = itemRenderer.getModel(staffStack, null, null, partSeed)
+
+ // Prevent StackOverflowError if an override is missing
+ return if (!model.isBuiltin) model
+ else MinecraftClient.getInstance().bakedModelManager.missingModel
+ }
+}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/particle/FlamethrowerParticle.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/particle/FlamethrowerParticle.kt
index 098a100e3..3955dfb61 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/particle/FlamethrowerParticle.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/particle/FlamethrowerParticle.kt
@@ -18,11 +18,14 @@
package opekope2.avm_staff.api.particle
+import net.fabricmc.api.EnvType
+import net.fabricmc.api.Environment
import net.minecraft.client.particle.*
import net.minecraft.client.world.ClientWorld
-import net.minecraft.particle.DefaultParticleType
+import net.minecraft.particle.SimpleParticleType
import net.minecraft.util.math.BlockPos
-import opekope2.avm_staff.IStaffMod
+import opekope2.avm_staff.api.flamethrowerParticleType
+import opekope2.avm_staff.api.soulFlamethrowerParticleType
import opekope2.avm_staff.mixin.IParticleMixin
/**
@@ -37,6 +40,7 @@ import opekope2.avm_staff.mixin.IParticleMixin
* @param velocityZ The Z component of the particle's velocity
* @see FlamethrowerParticle.Factory
*/
+@Environment(EnvType.CLIENT)
class FlamethrowerParticle(
world: ClientWorld,
x: Double,
@@ -103,12 +107,13 @@ class FlamethrowerParticle(
* Factory class for [FlamethrowerParticle], intended to register in Minecraft instead of direct consumption.
*
* @param spriteProvider Flame sprite provider
- * @see IStaffMod.flamethrowerParticleType
+ * @see flamethrowerParticleType
+ * @see soulFlamethrowerParticleType
* @see ParticleManager.addParticle
*/
- class Factory(private val spriteProvider: SpriteProvider) : ParticleFactory {
+ class Factory(private val spriteProvider: SpriteProvider) : ParticleFactory {
override fun createParticle(
- parameters: DefaultParticleType,
+ parameters: SimpleParticleType,
world: ClientWorld,
x: Double,
y: Double,
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/StaffItemHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/staff/StaffHandler.kt
similarity index 50%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/StaffItemHandler.kt
rename to StaffMod/src/main/kotlin/opekope2/avm_staff/api/staff/StaffHandler.kt
index 14c3bdf2f..1402bf6ab 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/StaffItemHandler.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/staff/StaffHandler.kt
@@ -16,18 +16,13 @@
* along with this mod. If not, see .
*/
-package opekope2.avm_staff.api.item
+package opekope2.avm_staff.api.staff
-import com.google.common.collect.ImmutableMultimap
-import com.google.common.collect.Multimap
-import net.fabricmc.api.EnvType
-import net.fabricmc.api.Environment
+import dev.architectury.event.EventResult
import net.minecraft.advancement.criterion.Criteria
+import net.minecraft.component.type.AttributeModifiersComponent
import net.minecraft.entity.Entity
-import net.minecraft.entity.EquipmentSlot
import net.minecraft.entity.LivingEntity
-import net.minecraft.entity.attribute.EntityAttribute
-import net.minecraft.entity.attribute.EntityAttributeModifier
import net.minecraft.entity.attribute.EntityAttributes
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.Item
@@ -41,40 +36,46 @@ import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.world.World
import net.minecraft.world.event.GameEvent
-import opekope2.avm_staff.IStaffMod
-import opekope2.avm_staff.api.item.model.IStaffItemUnbakedModel
-import opekope2.avm_staff.util.attackDamage
-import opekope2.avm_staff.util.attackSpeed
-import java.util.function.Supplier
+import opekope2.avm_staff.util.addDefault
/**
* Provides functionality for a staff, when an item is inserted into it.
- *
- * @see IDisablesShield
*/
-abstract class StaffItemHandler {
+abstract class StaffHandler {
/**
* The number of ticks the staff can be used for using the current item.
*/
open val maxUseTime: Int
get() = 0
+ /**
+ * Gets the attribute modifiers (damage, attack speed, etc.) of the staff when held.
+ */
+ open val attributeModifiers: AttributeModifiersComponent
+ get() = Default.ATTRIBUTE_MODIFIERS
+
/**
* Called on both the client and the server by Minecraft when the player uses the staff.
*
* If the staff can be used for multiple ticks, override [maxUseTime] to return a positive number, and call
* [PlayerEntity.setCurrentHand] on [user] with [hand] as the argument.
*
- * On the logical client, the return values have the following meaning:
+ * @return
+ * On the logical client:
*
- * - SUCCESS: Swing hand, and reset equip progress
- * - CONSUME, CONSUME_PARTIAL: Don't swing hand, and reset equip progress
- * - PASS, FAIL: Don't swing hand, and don't reset equip progress
+ * - [ActionResult.SUCCESS]:
+ * swings hand, and resets equip progress
+ * - [ActionResult.CONSUME], [ActionResult.CONSUME_PARTIAL]:
+ * doesn't swing hand, and resets equip progress
+ * - [ActionResult.PASS], [ActionResult.FAIL]:
+ * doesn't swing hand, and doesn't reset equip progress
*
- * On the logical server, the return values have the following meaning (if used by player):
+ * On the logical server (if used by player):
*
- * - SUCCESS: Swing hand
- * - CONSUME, CONSUME_PARTIAL, PASS, FAIL: Don't swing hand
+ * - [ActionResult.SUCCESS]:
+ * swings hand
+ * - [ActionResult.CONSUME], [ActionResult.CONSUME_PARTIAL], [ActionResult.PASS], [ActionResult.FAIL]:
+ * doesn't swing hand
*
* @param staffStack The item stack used to perform the action
* @param world The world the [user] is in
@@ -130,32 +131,30 @@ abstract class StaffItemHandler {
* Called on both the client and the server by Minecraft, when an entity uses the staff on a block.
* This method may not be called, if the block handles the use event (for example, a chest).
*
- * On the logical client, the return values have the following meaning:
- *
- * - SUCCESS: send a packet to the server, and swing hand
- * - CONSUME, CONSUME_PARTIAL, FAIL: send a packet to the server, and don't swing hand
- * - PASS: send a packet to the server, don't swing hand, then interact with the item by itself (see [use])
- *
- * On the logical server, the return values have the following meaning (if used by player):
- *
- * - SUCCESS:
- * Increment [*player used item* stat][Stats.USED],
- * trigger [*item used on block* criterion][Criteria.ITEM_USED_ON_BLOCK],
- * and swing hand
- * - CONSUME:
- * Increment [*player used item* stat][Stats.USED],
- * trigger [*item used on block* criterion][Criteria.ITEM_USED_ON_BLOCK],
- * and don't swing hand
- * - CONSUME_PARTIAL:
- * Don't increment [*player used item* stat][Stats.USED],
- * trigger [*item used on block* criterion][Criteria.ITEM_USED_ON_BLOCK],
- * and don't swing hand
- * - PASS, FAIL:
- * Don't increment [*player used item* stat][Stats.USED],
- * don't trigger [*item used on block* criterion][Criteria.ITEM_USED_ON_BLOCK],
- * and don't swing hand
- *
- * On the logical server, the return values are processed by the caller code (if not used by player).
+ * @return
+ * On the logical client:
+ *
+ * - [ActionResult.SUCCESS]:
+ * sends a packet to the server, and swings hand
+ * - [ActionResult.CONSUME], [ActionResult.CONSUME_PARTIAL], [ActionResult.FAIL]:
+ * sends a packet to the server, and doesn't swing hand
+ * - [ActionResult.PASS]:
+ * sends a packet to the server, doesn't swing hand, then interacts with the item using [use]
+ *
+ * On the logical server (if used by player):
+ *
+ * - [ActionResult.SUCCESS]:
+ * increments [*player used item* stat][Stats.USED], triggers
+ * [*item used on block* criterion][Criteria.ITEM_USED_ON_BLOCK], and swings hand
+ * - [ActionResult.CONSUME]:
+ * increments [*player used item* stat][Stats.USED], triggers
+ * [*item used on block* criterion][Criteria.ITEM_USED_ON_BLOCK], and doesn't swing hand
+ * - [ActionResult.CONSUME_PARTIAL]:
+ * doesn't increment [*player used item* stat][Stats.USED], triggers
+ * [*item used on block* criterion][Criteria.ITEM_USED_ON_BLOCK], and doesn't swing hand
+ * - [ActionResult.PASS], [ActionResult.FAIL]:
+ * doesn't increment [*player used item* stat][Stats.USED], doesn't trigger
+ * [*item used on block* criterion][Criteria.ITEM_USED_ON_BLOCK], and doesn't swing hand
*
* @param staffStack The item stack used to perform the action
* @param world The world the [user] is in
@@ -181,38 +180,29 @@ abstract class StaffItemHandler {
* This method may not be called, if the entity handles the use event (for example, a horse).
* This method will not be called, if the player is in spectator mode.
*
- * On the logical client, the return values have the following meaning:
- *
- * - SUCCESS:
- * send a packet to the server,
- * emit [*entity interact* game event][GameEvent.ENTITY_INTERACT],
- * and swing hand
- * - CONSUME, CONSUME_PARTIAL:
- * send a packet to the server,
- * emit [*entity interact* game event][GameEvent.ENTITY_INTERACT],
- * and don't swing hand
- * - PASS, FAIL:
- * send a packet to the server,
- * don't emit [*entity interact* game event][GameEvent.ENTITY_INTERACT],
- * don't swing hand,
- * then interact with the item by itself (see [use])
- *
- * On the logical server, the return values have the following meaning (if used by player):
- *
- * - SUCCESS:
- * Emit [*entity interact* game event][GameEvent.ENTITY_INTERACT],
- * trigger [*player interacted with entity* criteria][Criteria.PLAYER_INTERACTED_WITH_ENTITY],
- * and swing hand
- * - CONSUME, CONSUME_PARTIAL:
- * Emit [*entity interact* game event][GameEvent.ENTITY_INTERACT],
- * trigger [*player interacted with entity* criteria][Criteria.PLAYER_INTERACTED_WITH_ENTITY],
- * and don't swing hand
- * - PASS, FAIL:
- * Don't emit [*entity interact* game event][GameEvent.ENTITY_INTERACT],
- * don't trigger [*player interacted with entity* criteria][Criteria.PLAYER_INTERACTED_WITH_ENTITY],
- * and don't swing hand
- *
- * On the logical server, the return values are processed by the caller code (if not used by player).
+ * @return
+ * On the logical client:
+ *
+ * - [ActionResult.SUCCESS]:
+ * sends a packet to the server, emits [*entity interact* game event][GameEvent.ENTITY_INTERACT], and swings hand
+ * - [ActionResult.CONSUME], [ActionResult.CONSUME_PARTIAL]:
+ * sends a packet to the server, emits [*entity interact* game event][GameEvent.ENTITY_INTERACT], and doesn't
+ * swing hand
+ * - [ActionResult.PASS], [ActionResult.FAIL]:
+ * sends a packet to the server, doesn't emit [*entity interact* game event][GameEvent.ENTITY_INTERACT], doesn't
+ * swing hand, then interacts with the item using [use]
+ *
+ * On the logical server (if used by player):
+ *
+ * - [ActionResult.SUCCESS]:
+ * Emits [*entity interact* game event][GameEvent.ENTITY_INTERACT], triggers
+ * [*player interacted with entity* criteria][Criteria.PLAYER_INTERACTED_WITH_ENTITY], and swings hand
+ * - [ActionResult.CONSUME], [ActionResult.CONSUME_PARTIAL]:
+ * Emits [*entity interact* game event][GameEvent.ENTITY_INTERACT], triggers
+ * [*player interacted with entity* criteria][Criteria.PLAYER_INTERACTED_WITH_ENTITY], and doesn't swing hand
+ * - [ActionResult.PASS], [ActionResult.FAIL]:
+ * Doesn't emit [*entity interact* game event][GameEvent.ENTITY_INTERACT], doesn't trigger
+ * [*player interacted with entity* criteria][Criteria.PLAYER_INTERACTED_WITH_ENTITY], and doesn't swing hand
*
* @param staffStack The item stack used to perform the action
* @param world The world the [user] is in
@@ -232,65 +222,24 @@ abstract class StaffItemHandler {
}
/**
- * Called on both the client and the server by Staff Mod, when an entity attacks nothing (left clicks on air) with
- * a staff.
- *
- * On the logical client, the return values have the following meaning:
- *
- * - SUCCESS: send a packet to the server, and swing hand. This doesn't reset attack cooldown
- * - CONSUME, CONSUME_PARTIAL: send a packet to the server, and don't swing hand. This doesn't reset attack cooldown
- * - PASS: Let Minecraft handle vanilla attack
- * - FAIL: don't send a packet to the server, and don't swing hand. This doesn't reset attack cooldown
- *
- * On the logical server, the return value is ignored (if used by player) or processed by the caller code
- * (if the attacker is not a player).
+ * Called on both the client by Architectury API and the server by Staff Mod, when an entity attacks thin air with a
+ * staff.
*
* @param staffStack The item stack used to perform the action
* @param world The world the [attacker] is in
* @param attacker The entity, which attacked with the staff
* @param hand The hand of the [attacker], in which the [staff][staffStack] is
*/
- open fun attack(
- staffStack: ItemStack,
- world: World,
- attacker: LivingEntity,
- hand: Hand
- ): ActionResult {
- return ActionResult.PASS
- }
+ open fun attack(staffStack: ItemStack, world: World, attacker: LivingEntity, hand: Hand) {}
/**
- * Called on both the client and the server by Fabric API (Fabric) or Staff Mod (Forge),when an entity attacks a
- * block with a staff.
- *
- * On the logical client, the return values have the following meaning:
- *
- * - SUCCESS:
- * Cancel vanilla block breaking,
- * send a packet to the server,
- * spawn block breaking particles,
- * and swing hand.
- * This doesn't reset the block breaking cooldown
- * - CONSUME, CONSUME_PARTIAL:
- * Cancel vanilla block breaking,
- * send a packet to the server,
- * don't spawn block breaking particles,
- * and don't swing hand.
- * This doesn't reset the block breaking cooldown
- * - PASS: Let Minecraft handle vanilla block breaking
- * - FAIL:
- * Cancel vanilla block breaking,
- * don't send a packet to the server,
- * don't spawn block breaking particles,
- * and don't swing hand.
- * This doesn't reset the block breaking cooldown
- *
- * On the logical server, the return values have the following meaning (if used by player):
+ * Called on both the client and the server by Architectury API, when an entity attacks a block with a staff.
*
- * - SUCCESS, CONSUME, CONSUME_PARTIAL, FAIL: Cancel vanilla block breaking, and notify the client
- * - PASS: Let Minecraft handle vanilla block breaking
- *
- * On the logical server, the return values are processed by the caller code (if the attacker is not a player).
+ * @return
+ * - [EventResult.interruptTrue], [EventResult.interruptFalse]:
+ * Cancels vanilla block breaking, and on a Neo/Forge logical client, sends a packet to the server.
+ * - [EventResult.interruptDefault], [EventResult.pass]:
+ * Lets Minecraft handle vanilla block breaking.
*
* @param staffStack The item stack used to perform the action
* @param world The world the [attacker] is in
@@ -306,39 +255,19 @@ abstract class StaffItemHandler {
target: BlockPos,
side: Direction,
hand: Hand
- ): ActionResult {
- return ActionResult.PASS
+ ): EventResult {
+ return EventResult.pass()
}
/**
- * Called on both the client by Staff Mod and the server by Fabric API (Fabric) or Staff Mod (Forge), when an entity
- * attacks an entity with a staff.
+ * Called on both the client by Fabric/Neo/Forge API and the server by Fabric/Neo/Forge API, when an entity attacks
+ * an entity with a staff.
*
- * On the logical client, the return values have the following meaning:
- *
- * - SUCCESS:
- * Cancel vanilla entity attack,
- * send a packet to the server,
- * and swing hand.
- * This doesn't reset the entity attack cooldown
- * - CONSUME, CONSUME_PARTIAL:
- * Cancel vanilla entity attack,
- * send a packet to the server,
- * and don't swing hand.
- * This doesn't reset the entity attack cooldown
- * - PASS: Let Minecraft handle vanilla entity attack
- * - FAIL:
- * Cancel vanilla entity attack,
- * don't send a packet to the server,
- * and don't swing hand.
- * This doesn't reset the entity attack cooldown
- *
- * On the logical server, the return values have the following meaning (if used by player):
- *
- * - SUCCESS, CONSUME, CONSUME_PARTIAL, FAIL: Cancel vanilla entity attack, don't attack the entity
- * - PASS: Let Minecraft handle vanilla entity attack
- *
- * On the logical server, the return values are processed by the caller code (if the attacker is not a player).
+ * @return
+ * - [EventResult.interrupt], [EventResult.interruptTrue], [EventResult.interruptFalse], [EventResult.interruptDefault]:
+ * Cancels vanilla entity attack, and on the logical client, sends a packet to the server.
+ * - [EventResult.pass]:
+ * Lets Minecraft handle vanilla entity attack.
*
* @param staffStack The item stack used to perform the action
* @param world The world the [attacker] is in
@@ -352,8 +281,29 @@ abstract class StaffItemHandler {
attacker: LivingEntity,
target: Entity,
hand: Hand
- ): ActionResult {
- return ActionResult.PASS
+ ): EventResult {
+ return EventResult.pass()
+ }
+
+ /**
+ * Called on both the client and the server by Staff Mod on Fabric and Neo/Forge API on Neo/Forge, when an entity
+ * holding a staff tries to swing its hand.
+ *
+ * @param staffStack The item stack used to perform the action
+ * @param world The world the [holder] is in
+ * @param holder The entity, which holds the staff
+ * @param hand The hand of the [holder], in which the [staff][staffStack] is
+ * @return `true` to allow hand swing, `false` to cancel it
+ */
+ open fun canSwingHand(staffStack: ItemStack, world: World, holder: LivingEntity, hand: Hand): Boolean {
+ return true
+ }
+
+ /**
+ * Returns if attacking with the staff should disable the target's shield.
+ */
+ open fun disablesShield(): Boolean {
+ return false
}
/**
@@ -363,9 +313,9 @@ abstract class StaffItemHandler {
* @param newStaffStack The updated item stack
* @param player The holder of [oldStaffStack]
* @param hand The hand of [player], in which the [old staff][oldStaffStack] is
- * @return true to play the update/equip animation, false to skip it
+ * @return `true` to play the update/equip animation, `false` to skip it
*/
- open fun allowNbtUpdateAnimation(
+ open fun allowComponentsUpdateAnimation(
oldStaffStack: ItemStack,
newStaffStack: ItemStack,
player: PlayerEntity,
@@ -375,12 +325,12 @@ abstract class StaffItemHandler {
}
/**
- * Called on the client side by Forge, when the NBT of the held item gets updated.
+ * Called on the client side by Neo/Forge, when the NBT of the held item gets updated.
*
* @param oldStaffStack The previous item stack
* @param newStaffStack The updated item stack
* @param selectedSlotChanged If the selected hotbar slot was changed
- * @return true to play the update/equip animation, false to skip it
+ * @return `true` to play the update/equip animation, `false` to skip it
*/
open fun allowReequipAnimation(
oldStaffStack: ItemStack,
@@ -391,61 +341,35 @@ abstract class StaffItemHandler {
}
/**
- * Gets the attribute modifiers (damage, attack speed, etc.) of the staff when held.
- *
- * @param staffStack The staff item stack (not the item in the staff)
- * @param slot The slot the staff is equipped in
+ * Handler of a staff with no item inserted into it.
*/
- open fun getAttributeModifiers(
- staffStack: ItemStack,
- slot: EquipmentSlot
- ): Multimap {
- return if (slot == EquipmentSlot.MAINHAND) ATTRIBUTE_MODIFIERS
- else ImmutableMultimap.of()
+ object Default : StaffHandler() {
+ @JvmField
+ val ATTRIBUTE_MODIFIERS: AttributeModifiersComponent = AttributeModifiersComponent.builder()
+ .addDefault(EntityAttributes.GENERIC_ATTACK_DAMAGE)
+ .addDefault(EntityAttributes.GENERIC_ATTACK_SPEED)
+ .addDefault(EntityAttributes.PLAYER_ENTITY_INTERACTION_RANGE)
+ .addDefault(EntityAttributes.PLAYER_BLOCK_INTERACTION_RANGE)
+ .build()
}
- object EmptyStaffHandler : StaffItemHandler()
-
- object FallbackStaffHandler : StaffItemHandler()
-
companion object {
- private val ATTRIBUTE_MODIFIERS = ImmutableMultimap.of(
- EntityAttributes.GENERIC_ATTACK_DAMAGE,
- attackDamage(4.0),
- EntityAttributes.GENERIC_ATTACK_SPEED,
- attackSpeed(2.0)
- )
-
- private val staffItemsHandlers = mutableMapOf()
-
- private val staffItemModelProviders = mutableMapOf>()
- @Suppress("RedundantGetter") // Force generate getter to annotate
- @Environment(EnvType.CLIENT)
- get() = field
+ private val staffItemsHandlers = mutableMapOf()
/**
- * Registers a [StaffItemHandler] for the given [item ID][staffItem].
+ * Registers a [StaffHandler] for the given [item ID][staffItem]. Call this from your common mod initializer.
*
* @param staffItem The item ID to register a handler for. This is the item, which can be
* inserted into the staff
* @param handler The staff item handler, which processes staff interactions, while the
* [registered item][staffItem] is inserted into it
- * @param staffItemModelSupplierFactory The staff item's unbaked model supplier's supplier. The nesting is needed
- * to prevent loading client classes on the server
* @return `true`, if the registration was successful, `false`, if the item was already registered
*/
@JvmStatic
- fun register(
- staffItem: Identifier,
- handler: StaffItemHandler,
- staffItemModelSupplierFactory: Supplier>
- ): Boolean {
+ fun register(staffItem: Identifier, handler: StaffHandler): Boolean {
if (staffItem in staffItemsHandlers) return false
staffItemsHandlers[staffItem] = handler
- if (IStaffMod.get().isPhysicalClient) {
- staffItemModelProviders[staffItem] = staffItemModelSupplierFactory.get()
- }
return true
}
@@ -464,14 +388,6 @@ abstract class StaffItemHandler {
* @param staffItem The item ID, which can be inserted into the staff
*/
@JvmStatic
- operator fun get(staffItem: Identifier): StaffItemHandler? = staffItemsHandlers[staffItem]
-
- /**
- * @suppress
- */
- @Environment(EnvType.CLIENT)
- internal fun iterateStaffItemModelProviders(): Iterator>> {
- return staffItemModelProviders.iterator()
- }
+ operator fun get(staffItem: Identifier): StaffHandler? = staffItemsHandlers[staffItem]
}
}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/staff/StaffInfusionSmithingRecipeTextures.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/staff/StaffInfusionSmithingRecipeTextures.kt
new file mode 100644
index 000000000..16fb987d0
--- /dev/null
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/staff/StaffInfusionSmithingRecipeTextures.kt
@@ -0,0 +1,56 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+package opekope2.avm_staff.api.staff
+
+import net.fabricmc.api.EnvType
+import net.fabricmc.api.Environment
+import net.minecraft.util.Identifier
+import opekope2.avm_staff.api.staffInfusionSmithingTemplateItem
+
+/**
+ * Object holding textures to be displayed in a smithing table, when using a
+ * [staff infusion smithing template][staffInfusionSmithingTemplateItem].
+ */
+@Environment(EnvType.CLIENT)
+object StaffInfusionSmithingRecipeTextures {
+ /**
+ * @suppress
+ */
+ @JvmSynthetic
+ internal val baseSlotTextures = mutableListOf()
+
+ /**
+ * @suppress
+ */
+ @JvmSynthetic
+ internal val additionsSlotTextures = mutableListOf()
+
+ /**
+ * Registers a pair of staff texture and an ingredient texture to be displayed in a smithing table, when using a
+ * [staff infusion smithing template][staffInfusionSmithingTemplateItem]. This method should be called for every
+ * `minecraft:smithing_transform` recipe in the mod's data pack, which infuses an ingredient into a faint staff.
+ *
+ * @param baseSlotTexture The background texture of the 2nd slot of the smithing table.
+ * @param additionsSlotTexture The background texture of the 3rd slot of the smithing table.
+ */
+ fun register(baseSlotTexture: Identifier, additionsSlotTexture: Identifier) {
+ baseSlotTextures += baseSlotTexture
+ additionsSlotTextures += additionsSlotTexture
+ }
+}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/staff/StaffItemComponent.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/staff/StaffItemComponent.kt
new file mode 100644
index 000000000..8beb159e1
--- /dev/null
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/staff/StaffItemComponent.kt
@@ -0,0 +1,65 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+package opekope2.avm_staff.api.staff
+
+import com.mojang.serialization.Codec
+import com.mojang.serialization.codecs.RecordCodecBuilder
+import net.minecraft.component.DataComponentType
+import net.minecraft.item.ItemStack
+import net.minecraft.network.RegistryByteBuf
+import net.minecraft.network.codec.PacketCodec
+
+/**
+ * [ItemStack] wrapper to make them compatible with [DataComponentType]s.
+ *
+ * @param item The item stored in this component. Must be [copied][ItemStack.copy] before modifying it
+ */
+class StaffItemComponent(val item: ItemStack) {
+ override fun equals(other: Any?): Boolean {
+ return when {
+ this === other -> true
+ other is StaffItemComponent -> ItemStack.areEqual(item, other.item)
+ else -> false
+ }
+ }
+
+ override fun hashCode(): Int {
+ return ItemStack.hashCode(item)
+ }
+
+ companion object {
+ /**
+ * [Codec] for [StaffItemComponent].
+ */
+ @JvmField
+ val CODEC: Codec = RecordCodecBuilder.create { instance ->
+ instance.group(
+ ItemStack.CODEC.fieldOf("item").forGetter(StaffItemComponent::item)
+ ).apply(instance, ::StaffItemComponent)
+ }
+
+ /**
+ * [PacketCodec] for [StaffItemComponent].
+ */
+ @JvmField
+ val PACKET_CODEC: PacketCodec = PacketCodec.tuple(
+ ItemStack.PACKET_CODEC, StaffItemComponent::item, ::StaffItemComponent
+ )
+ }
+}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/staff/StaffRendererOverrideComponent.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/staff/StaffRendererOverrideComponent.kt
new file mode 100644
index 000000000..5ffe52493
--- /dev/null
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/api/staff/StaffRendererOverrideComponent.kt
@@ -0,0 +1,61 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+package opekope2.avm_staff.api.staff
+
+import com.mojang.serialization.Codec
+import com.mojang.serialization.codecs.RecordCodecBuilder
+import net.minecraft.client.render.model.json.ModelTransformationMode
+import net.minecraft.entity.LivingEntity
+import net.minecraft.network.RegistryByteBuf
+import net.minecraft.network.codec.PacketCodec
+import opekope2.avm_staff.api.item.renderer.StaffRenderer
+
+/**
+ * Data component to override the behavior of a [StaffRenderer].
+ *
+ * @param renderMode The display transform of the model to use
+ * @param isActive The item should be treated as if it was [LivingEntity.getActiveItem]
+ */
+data class StaffRendererOverrideComponent(val renderMode: ModelTransformationMode, val isActive: Boolean) {
+ private constructor(buf: RegistryByteBuf) : this(
+ buf.readEnumConstant(ModelTransformationMode::class.java),
+ buf.readBoolean()
+ )
+
+ private fun encode(buf: RegistryByteBuf) {
+ buf.writeEnumConstant(renderMode)
+ buf.writeBoolean(isActive)
+ }
+
+ companion object {
+ @JvmField
+ val CODEC: Codec = RecordCodecBuilder.create { instance ->
+ instance.group(
+ ModelTransformationMode.CODEC.fieldOf("renderMode")
+ .forGetter(StaffRendererOverrideComponent::renderMode),
+ Codec.BOOL.fieldOf("pointForward")
+ .forGetter(StaffRendererOverrideComponent::isActive)
+ ).apply(instance, ::StaffRendererOverrideComponent)
+ }
+
+ @JvmField
+ val PACKET_CODEC: PacketCodec =
+ PacketCodec.of(StaffRendererOverrideComponent::encode, ::StaffRendererOverrideComponent)
+ }
+}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/IDisablesShield.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/Aliases.kt
similarity index 81%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/IDisablesShield.kt
rename to StaffMod/src/main/kotlin/opekope2/avm_staff/internal/Aliases.kt
index a1901f326..c6afad535 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/IDisablesShield.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/Aliases.kt
@@ -16,9 +16,6 @@
* along with this mod. If not, see .
*/
-package opekope2.avm_staff.api.item
+package opekope2.avm_staff.internal
-/**
- * A [StaffItemHandler] implementing this interface disables the attacked entity's shield.
- */
-interface IDisablesShield
+internal typealias MinecraftUnit = net.minecraft.util.Unit
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/Initializer.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/Initializer.kt
index 6f98a13d4..17b9d599a 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/Initializer.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/Initializer.kt
@@ -18,43 +18,121 @@
package opekope2.avm_staff.internal
+import dev.architectury.event.EventResult
+import dev.architectury.event.events.client.ClientTickEvent
+import dev.architectury.event.events.common.EntityEvent
+import dev.architectury.event.events.common.InteractionEvent
+import dev.architectury.event.events.common.LootEvent
+import dev.architectury.event.events.common.PlayerEvent
+import dev.architectury.registry.client.keymappings.KeyMappingRegistry
+import dev.architectury.registry.client.level.entity.EntityRendererRegistry
import net.fabricmc.api.EnvType
import net.fabricmc.api.Environment
-import net.minecraft.client.item.ClampedModelPredicateProvider
-import net.minecraft.client.world.ClientWorld
+import net.minecraft.client.render.entity.TntEntityRenderer
+import net.minecraft.entity.ItemEntity
import net.minecraft.entity.LivingEntity
-import net.minecraft.item.Item
-import net.minecraft.item.ItemStack
+import net.minecraft.entity.damage.DamageSource
+import net.minecraft.entity.player.PlayerEntity
+import net.minecraft.entity.player.PlayerInventory
+import net.minecraft.item.SmithingTemplateItem
+import net.minecraft.loot.LootPool
+import net.minecraft.loot.LootTable
+import net.minecraft.loot.entry.LootTableEntry
+import net.minecraft.registry.RegistryKey
+import net.minecraft.registry.RegistryKeys
import net.minecraft.util.Identifier
-import opekope2.avm_staff.IStaffMod
-import opekope2.avm_staff.internal.event_handler.addBlockToStaff
-import opekope2.avm_staff.internal.event_handler.attack
-import opekope2.avm_staff.internal.event_handler.removeBlockFromStaff
-import opekope2.avm_staff.internal.networking.c2s.play.AddItemToStaffC2SPacket
+import opekope2.avm_staff.api.impactTntEntityType
+import opekope2.avm_staff.api.staff.StaffInfusionSmithingRecipeTextures
+import opekope2.avm_staff.api.staffsTag
+import opekope2.avm_staff.internal.event_handler.*
+import opekope2.avm_staff.internal.networking.c2s.play.AttackC2SPacket
+import opekope2.avm_staff.internal.networking.c2s.play.InsertItemIntoStaffC2SPacket
import opekope2.avm_staff.internal.networking.c2s.play.RemoveItemFromStaffC2SPacket
-import opekope2.avm_staff.internal.networking.c2s.play.StaffAttackC2SPacket
import opekope2.avm_staff.util.MOD_ID
+import opekope2.avm_staff.util.contains
+import opekope2.avm_staff.util.itemInStaff
+import opekope2.avm_staff.util.staffHandlerOrDefault
+
+fun registerContent() {
+ opekope2.avm_staff.api.registerContent()
+}
fun initializeNetworking() {
- AddItemToStaffC2SPacket.registerHandler(::addBlockToStaff)
- RemoveItemFromStaffC2SPacket.registerHandler(::removeBlockFromStaff)
- StaffAttackC2SPacket.registerHandler(::attack)
+ InsertItemIntoStaffC2SPacket.registerHandler(::addItemToStaff)
+ RemoveItemFromStaffC2SPacket.registerHandler(::removeItemFromStaff)
+ AttackC2SPacket.registerHandler(::attack)
}
-val USING_ITEM_PREDICATE = Identifier(MOD_ID, "using_item")
+private val MODIFIABLE_LOOT_TABLES = setOf(
+ Identifier("chests/bastion_treasure"),
+ Identifier("chests/trial_chambers/reward_unique")
+)
+
+fun modifyLootTables(
+ lootTable: RegistryKey,
+ context: LootEvent.LootTableModificationContext,
+ builtin: Boolean
+) {
+ // FIXME builtin check after updating to 1.21 because Fabric detects experiments as data pack
+ if (lootTable.value !in MODIFIABLE_LOOT_TABLES) return
+
+ context.addPool(
+ LootPool.builder().with(
+ LootTableEntry.builder(
+ RegistryKey.of(RegistryKeys.LOOT_TABLE, Identifier(MOD_ID, "add_loot_pool/${lootTable.value.path}"))
+ )
+ )
+ )
+}
+
+fun subscribeToEvents() {
+ InteractionEvent.LEFT_CLICK_BLOCK.register(::attackBlock)
+ LootEvent.MODIFY_LOOT_TABLE.register(::modifyLootTables)
+ EntityEvent.LIVING_DEATH.register(::stopUsingStaffOnPlayerDeath)
+ PlayerEvent.DROP_ITEM.register(::stopUsingStaffWhenDropped)
+}
@Suppress("UNUSED_PARAMETER")
+fun stopUsingStaffOnPlayerDeath(entity: LivingEntity, damageSource: DamageSource): EventResult {
+ if (entity !is PlayerEntity) return EventResult.pass()
+
+ iterator {
+ yieldAll(0 until PlayerInventory.MAIN_SIZE)
+ yield(PlayerInventory.OFF_HAND_SLOT)
+ }.forEach { slot ->
+ if (entity.inventory.getStack(slot) in staffsTag) {
+ entity.stopUsingItem()
+ }
+ }
+
+ return EventResult.pass()
+}
+
+fun stopUsingStaffWhenDropped(entity: LivingEntity, item: ItemEntity): EventResult {
+ if (item.stack in staffsTag) {
+ item.stack.itemInStaff.staffHandlerOrDefault.onStoppedUsing(
+ item.stack, entity.entityWorld, entity, entity.itemUseTimeLeft
+ )
+ }
+ return EventResult.pass()
+}
+
@Environment(EnvType.CLIENT)
-private fun usingItemPredicate(stack: ItemStack, world: ClientWorld?, entity: LivingEntity?, seed: Int): Float {
- return if (entity != null && entity.isUsingItem) 1f else 0f
+fun registerClientContent() {
+ KeyMappingRegistry.register(addRemoveStaffItemKeyBinding)
+ EntityRendererRegistry.register(impactTntEntityType, ::TntEntityRenderer)
}
-// ModelPredicateProviderRegistry.register is private, so pass it from Fabric and Forge projects
@Environment(EnvType.CLIENT)
-fun registerModelPredicateProviders(register: (Item, Identifier, ClampedModelPredicateProvider) -> Unit) {
- register(
- IStaffMod.get().staffItem,
- USING_ITEM_PREDICATE,
- ::usingItemPredicate
+fun registerSmithingTableTextures() {
+ StaffInfusionSmithingRecipeTextures.register(
+ Identifier(MOD_ID, "item/smithing_table/empty_slot_royal_staff"),
+ SmithingTemplateItem.EMPTY_SLOT_REDSTONE_DUST_TEXTURE
)
}
+
+@Environment(EnvType.CLIENT)
+fun subscribeToClientEvents() {
+ InteractionEvent.CLIENT_LEFT_CLICK_AIR.register(::clientAttack)
+ ClientTickEvent.CLIENT_POST.register(::handleKeyBindings)
+}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/platform/RenderPlatform.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/StaffModPlatform.kt
similarity index 61%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/internal/platform/RenderPlatform.kt
rename to StaffMod/src/main/kotlin/opekope2/avm_staff/internal/StaffModPlatform.kt
index 85addd5eb..19af7e540 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/platform/RenderPlatform.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/StaffModPlatform.kt
@@ -16,18 +16,21 @@
* along with this mod. If not, see .
*/
-@file: JvmName("RenderPlatform")
+@file: JvmName("StaffModPlatform")
+@file: Suppress("UNUSED_PARAMETER")
-package opekope2.avm_staff.internal.platform
+package opekope2.avm_staff.internal
import dev.architectury.injectables.annotations.ExpectPlatform
-import net.minecraft.client.render.model.BakedQuad
-import net.minecraft.client.texture.Sprite
-import opekope2.avm_staff.api.render.IQuadBakerVertexConsumer
-import java.util.function.Consumer
+import net.minecraft.item.Item
+import opekope2.avm_staff.api.item.CrownItem
+import opekope2.avm_staff.api.item.StaffItem
@ExpectPlatform
-@Suppress("UNUSED_PARAMETER")
-fun getQuadBakerVertexConsumer(sprite: Sprite, bakedQuadConsumer: Consumer): IQuadBakerVertexConsumer {
- throw AssertionError()
-}
+fun createStaffItem(settings: Item.Settings): StaffItem = throw AssertionError()
+
+@ExpectPlatform
+fun createStaffRendererItem(settings: Item.Settings): Item = throw AssertionError()
+
+@ExpectPlatform
+fun createCrownItem(settings: Item.Settings): CrownItem = throw AssertionError()
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/event_handler/InteractionHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/event_handler/InteractionHandler.kt
new file mode 100644
index 000000000..2a3caae9d
--- /dev/null
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/event_handler/InteractionHandler.kt
@@ -0,0 +1,54 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+package opekope2.avm_staff.internal.event_handler
+
+import dev.architectury.event.EventResult
+import net.fabricmc.api.EnvType
+import net.fabricmc.api.Environment
+import net.minecraft.entity.player.PlayerEntity
+import net.minecraft.util.Hand
+import net.minecraft.util.math.BlockPos
+import net.minecraft.util.math.Direction
+import opekope2.avm_staff.api.staffsTag
+import opekope2.avm_staff.internal.networking.c2s.play.AttackC2SPacket
+import opekope2.avm_staff.util.contains
+import opekope2.avm_staff.util.itemInStaff
+import opekope2.avm_staff.util.staffHandler
+
+fun attackBlock(player: PlayerEntity, hand: Hand, target: BlockPos, direction: Direction): EventResult {
+ val staffStack = player.getStackInHand(hand)
+ if (staffStack !in staffsTag) return EventResult.pass()
+
+ val itemInStaff = staffStack.itemInStaff ?: return EventResult.pass()
+ val staffHandler = itemInStaff.staffHandler ?: return EventResult.pass()
+
+ return staffHandler.attackBlock(staffStack, player.entityWorld, player, target, direction, hand)
+}
+
+@Environment(EnvType.CLIENT)
+fun clientAttack(player: PlayerEntity, hand: Hand) {
+ val staffStack = player.getStackInHand(hand)
+ if (staffStack !in staffsTag) return
+
+ val itemInStaff = staffStack.itemInStaff ?: return
+ val staffHandler = itemInStaff.staffHandler ?: return
+
+ staffHandler.attack(staffStack, player.entityWorld, player, hand)
+ AttackC2SPacket(hand).sendToServer()
+}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/event_handler/ClientHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/event_handler/KeyBindingHandler.kt
similarity index 60%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/internal/event_handler/ClientHandler.kt
rename to StaffMod/src/main/kotlin/opekope2/avm_staff/internal/event_handler/KeyBindingHandler.kt
index c4942107a..6e3500ce4 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/event_handler/ClientHandler.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/event_handler/KeyBindingHandler.kt
@@ -16,33 +16,40 @@
* along with this mod. If not, see .
*/
+@file: Environment(EnvType.CLIENT)
+
package opekope2.avm_staff.internal.event_handler
+import net.fabricmc.api.EnvType
+import net.fabricmc.api.Environment
import net.minecraft.client.MinecraftClient
import net.minecraft.client.option.KeyBinding
import net.minecraft.client.util.InputUtil
-import opekope2.avm_staff.internal.networking.c2s.play.AddItemToStaffC2SPacket
+import opekope2.avm_staff.internal.networking.c2s.play.InsertItemIntoStaffC2SPacket
import opekope2.avm_staff.internal.networking.c2s.play.RemoveItemFromStaffC2SPacket
-import opekope2.avm_staff.util.isItemInStaff
+import opekope2.avm_staff.util.MOD_ID
import org.lwjgl.glfw.GLFW
-@JvmField
-val ADD_REMOVE_KEYBINDING = KeyBinding(
- "key.avm_staff.add_remove_staff_block",
+val addRemoveStaffItemKeyBinding = KeyBinding(
+ "key.$MOD_ID.add_remove_staff_item",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_R,
- "key.categories.avm_staff"
+ "key.categories.$MOD_ID"
)
fun handleKeyBindings(client: MinecraftClient) {
- if (!ADD_REMOVE_KEYBINDING.isPressed) return
- ADD_REMOVE_KEYBINDING.isPressed = false
+ if (!addRemoveStaffItemKeyBinding.isPressed) return
+ addRemoveStaffItemKeyBinding.isPressed = false
val player = client.player ?: return
- if (player.mainHandStack.isItemInStaff || player.offHandStack.isItemInStaff) {
- RemoveItemFromStaffC2SPacket().send()
- } else {
- AddItemToStaffC2SPacket().send()
+ player.canInsertIntoStaff().ifSuccess {
+ InsertItemIntoStaffC2SPacket().sendToServer()
+ player.resetLastAttackedTicks()
+ }.ifError {
+ player.canRemoveFromStaff().ifSuccess {
+ RemoveItemFromStaffC2SPacket().sendToServer()
+ player.resetLastAttackedTicks()
+ }
}
}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/event_handler/NetworkHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/event_handler/NetworkHandler.kt
index 838e75424..fd40115fd 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/event_handler/NetworkHandler.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/event_handler/NetworkHandler.kt
@@ -18,86 +18,106 @@
package opekope2.avm_staff.internal.event_handler
+import com.mojang.serialization.DataResult
import dev.architectury.networking.NetworkManager.PacketContext
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.item.ItemStack
-import net.minecraft.util.ActionResult
-import net.minecraft.util.Hand
-import opekope2.avm_staff.IStaffMod
-import opekope2.avm_staff.internal.networking.c2s.play.AddItemToStaffC2SPacket
+import opekope2.avm_staff.api.staffsTag
+import opekope2.avm_staff.internal.networking.c2s.play.AttackC2SPacket
+import opekope2.avm_staff.internal.networking.c2s.play.InsertItemIntoStaffC2SPacket
import opekope2.avm_staff.internal.networking.c2s.play.RemoveItemFromStaffC2SPacket
-import opekope2.avm_staff.internal.networking.c2s.play.StaffAttackC2SPacket
-import opekope2.avm_staff.util.hasHandlerOfItem
-import opekope2.avm_staff.util.isItemInStaff
-import opekope2.avm_staff.util.itemInStaff
-
-private val staffMod: IStaffMod = IStaffMod.get()
+import opekope2.avm_staff.util.*
@Suppress("UNUSED_PARAMETER")
-fun addBlockToStaff(packet: AddItemToStaffC2SPacket, context: PacketContext) {
- val player = context.player
- val (staffStack, itemStack) = findStaffAndItemStack(player) ?: return
-
- if (itemStack.isEmpty) return
- if (!itemStack.hasHandlerOfItem) return
- if (staffStack.isItemInStaff) return
- staffStack.itemInStaff = itemStack
+fun addItemToStaff(packet: InsertItemIntoStaffC2SPacket, context: PacketContext) {
+ context.player.canInsertIntoStaff().ifSuccess { (staffStack, itemStackToAdd) ->
+ staffStack.mutableItemStackInStaff = itemStackToAdd.split(1)
+ context.player.resetLastAttackedTicks()
+ }
}
@Suppress("UNUSED_PARAMETER")
-fun removeBlockFromStaff(packet: RemoveItemFromStaffC2SPacket, context: PacketContext) {
- val player = context.player
- if (player.isUsingItem) return
-
- val (staffStack, itemSlot) = findStaffStackAndItemSlot(player) ?: return
- val inventory = player.inventory
- val itemStack = inventory.getStack(itemSlot)
- val staffItem = staffStack.itemInStaff ?: return
-
- if (itemStack.canAccept(staffItem, inventory.maxCountPerStack)) {
- inventory.insertStack(itemSlot, staffItem)
- staffStack.itemInStaff = null
+fun removeItemFromStaff(packet: RemoveItemFromStaffC2SPacket, context: PacketContext) {
+ context.player.canRemoveFromStaff().ifSuccess { (staffStack, targetSlot) ->
+ context.player.inventory.insertStack(targetSlot, staffStack.mutableItemStackInStaff)
+ staffStack.mutableItemStackInStaff = null
+ context.player.resetLastAttackedTicks()
}
}
-@Suppress("UNUSED_PARAMETER")
-fun attack(packet: StaffAttackC2SPacket, context: PacketContext): ActionResult {
+fun attack(packet: AttackC2SPacket, context: PacketContext) {
val player = context.player
val staffStack = player.mainHandStack
- return attack(staffStack, player.world, player, Hand.MAIN_HAND)
-}
+ if (staffStack !in staffsTag) return
-private fun ItemStack.canAccept(other: ItemStack, maxCountPerStack: Int): Boolean {
- val canCombine = isEmpty || ItemStack.canCombine(this, other)
- val totalCount = count + other.count
+ val itemInStaff = staffStack.itemInStaff ?: return
+ val staffHandler = itemInStaff.staffHandler ?: return
- return canCombine && totalCount <= item.maxCount && totalCount <= maxCountPerStack
+ staffHandler.attack(staffStack, player.entityWorld, player, packet.hand)
}
-private fun findStaffStackAndItemSlot(player: PlayerEntity): Pair? {
- val mainStack = player.mainHandStack
- val offStack = player.offHandStack
+fun PlayerEntity.canInsertIntoStaff(): DataResult> {
+ val staffStack: ItemStack
+ val itemStackToAdd: ItemStack
- return when {
- mainStack.isOf(staffMod.staffItem) && !offStack.isOf(staffMod.staffItem) ->
- mainStack to PlayerInventory.OFF_HAND_SLOT
+ when {
+ mainHandStack in staffsTag && offHandStack !in staffsTag -> {
+ staffStack = mainHandStack
+ itemStackToAdd = offHandStack
+ }
- offStack.isOf(staffMod.staffItem) && !mainStack.isOf(staffMod.staffItem) ->
- offStack to player.inventory.selectedSlot
+ offHandStack in staffsTag && mainHandStack !in staffsTag -> {
+ staffStack = offHandStack
+ itemStackToAdd = mainHandStack
+ }
- else -> null
+ else -> return DataResult.error { "The player doesn't hold exactly 1 staff" }
}
+
+ if (itemStackToAdd.isEmpty) return DataResult.error { "Can't insert empty ItemStack into staff" }
+ if (staffStack.isItemInStaff) return DataResult.error { "An item is already inserted into the staff" }
+ if (isItemCoolingDown(staffStack.item)) return DataResult.error { "Staff is cooling down" }
+ if (!itemStackToAdd.item.hasStaffHandler) return DataResult.error { "Can't insert item without a StaffHandler into the staff" }
+
+ return DataResult.success(staffStack to itemStackToAdd)
}
-private fun findStaffAndItemStack(player: PlayerEntity): Pair? {
- val mainStack = player.mainHandStack
- val offStack = player.offHandStack
+fun PlayerEntity.canRemoveFromStaff(): DataResult> {
+ if (isUsingItem) return DataResult.error { "The player is using an item" }
- return when {
- mainStack.isOf(staffMod.staffItem) && !offStack.isOf(staffMod.staffItem) -> mainStack to offStack
- offStack.isOf(staffMod.staffItem) && !mainStack.isOf(staffMod.staffItem) -> offStack to mainStack
- else -> null
+ val staffStack: ItemStack
+ val targetSlot: Int
+
+ when {
+ mainHandStack in staffsTag && offHandStack !in staffsTag -> {
+ staffStack = mainHandStack
+ targetSlot = PlayerInventory.OFF_HAND_SLOT
+ }
+
+ offHandStack in staffsTag && mainHandStack !in staffsTag -> {
+ staffStack = offHandStack
+ targetSlot = inventory.selectedSlot
+ }
+
+ else -> return DataResult.error { "The player doesn't hold exactly 1 staff" }
}
+
+ val targetStack = inventory.getStack(targetSlot)
+ val itemStackInStaff = staffStack.itemStackInStaff ?: return DataResult.error { "Staff is empty" }
+
+ if (isItemCoolingDown(staffStack.item)) return DataResult.error { "Staff is cooling down" }
+ if (!targetStack.canAccept(itemStackInStaff, inventory.maxCountPerStack)) return DataResult.error {
+ "Target stack is incompatible with staff item"
+ }
+
+ return DataResult.success(staffStack to targetSlot)
+}
+
+private fun ItemStack.canAccept(other: ItemStack, maxCountPerStack: Int): Boolean {
+ val canCombine = isEmpty || ItemStack.areItemsAndComponentsEqual(this, other)
+ val totalCount = count + other.count
+
+ return canCombine && totalCount <= item.maxCount && totalCount <= maxCountPerStack
}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/event_handler/StaffAttackHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/event_handler/StaffAttackHandler.kt
deleted file mode 100644
index 339969193..000000000
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/event_handler/StaffAttackHandler.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.internal.event_handler
-
-import net.minecraft.entity.Entity
-import net.minecraft.entity.LivingEntity
-import net.minecraft.entity.player.PlayerEntity
-import net.minecraft.item.ItemStack
-import net.minecraft.util.ActionResult
-import net.minecraft.util.Hand
-import net.minecraft.util.math.BlockPos
-import net.minecraft.util.math.Direction
-import net.minecraft.world.World
-import opekope2.avm_staff.IStaffMod
-import opekope2.avm_staff.util.handlerOfItem
-import opekope2.avm_staff.util.itemInStaff
-
-private val staffMod = IStaffMod.get()
-
-fun attackBlock(
- player: PlayerEntity,
- world: World,
- hand: Hand,
- target: BlockPos,
- direction: Direction
-): ActionResult {
- val staffStack = player.getStackInHand(hand)
- if (!staffStack.isOf(staffMod.staffItem)) return ActionResult.PASS
-
- val itemInStaff = staffStack.itemInStaff ?: return ActionResult.PASS
- val staffHandler = itemInStaff.handlerOfItem ?: return ActionResult.PASS
-
- return staffHandler.attackBlock(staffStack, world, player, target, direction, hand)
-}
-
-fun attackEntity(player: PlayerEntity, world: World, hand: Hand, target: Entity): ActionResult {
- val itemStack = player.getStackInHand(hand)
- if (!itemStack.isOf(staffMod.staffItem)) return ActionResult.PASS
-
- val itemInStaff = itemStack.itemInStaff ?: return ActionResult.PASS
- val staffHandler = itemInStaff.handlerOfItem ?: return ActionResult.PASS
-
- return staffHandler.attackEntity(itemStack, world, player, target, hand)
-}
-
-fun attack(staffStack: ItemStack, world: World, attacker: LivingEntity, hand: Hand): ActionResult {
- if (!staffStack.isOf(staffMod.staffItem)) return ActionResult.PASS
-
- val itemInStaff: ItemStack = staffStack.itemInStaff ?: return ActionResult.PASS
- val staffHandler = itemInStaff.handlerOfItem ?: return ActionResult.PASS
-
- return staffHandler.attack(staffStack, world, attacker, hand)
-}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/item/model/UnbakedStaffItemModel.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/item/model/UnbakedStaffItemModel.kt
deleted file mode 100644
index 6ba3a765b..000000000
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/item/model/UnbakedStaffItemModel.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.internal.item.model
-
-import net.minecraft.client.render.model.BakedModel
-import net.minecraft.client.render.model.Baker
-import net.minecraft.client.render.model.ModelBakeSettings
-import net.minecraft.client.render.model.UnbakedModel
-import net.minecraft.client.render.model.json.JsonUnbakedModel
-import net.minecraft.client.texture.Sprite
-import net.minecraft.client.util.SpriteIdentifier
-import net.minecraft.util.Identifier
-import opekope2.avm_staff.api.item.StaffItemHandler
-import opekope2.avm_staff.api.item.model.IStaffItemBakedModel
-import opekope2.avm_staff.util.TRANSFORM_INTO_STAFF
-import java.util.function.Function
-
-abstract class UnbakedStaffItemModel(private val original: JsonUnbakedModel) : UnbakedModel by original {
- abstract fun bake(
- baker: Baker,
- textureGetter: Function,
- rotationContainer: ModelBakeSettings,
- modelId: Identifier,
- itemModels: Map
- ): BakedModel?
-
- override fun bake(
- baker: Baker,
- textureGetter: Function,
- rotationContainer: ModelBakeSettings,
- modelId: Identifier
- ): BakedModel? {
- val itemModels = mutableMapOf()
-
- for ((id, modelSupplier) in StaffItemHandler.iterateStaffItemModelProviders()) {
- val unbaked = modelSupplier.get()
- // TODO cache baked model, because it will be baked each time UnbakedStaffItemModel is baked. Maybe use resource loader
- val baked = unbaked.bake(baker, textureGetter, rotationContainer, modelId, TRANSFORM_INTO_STAFF)
- itemModels[id] = baked ?: continue
- }
-
- return bake(baker, textureGetter, rotationContainer, modelId, itemModels)
- }
-}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/model/ModelPredicates.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/model/ModelPredicates.kt
new file mode 100644
index 000000000..a06c0e553
--- /dev/null
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/model/ModelPredicates.kt
@@ -0,0 +1,58 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+package opekope2.avm_staff.internal.model
+
+import net.fabricmc.api.EnvType
+import net.fabricmc.api.Environment
+import net.minecraft.client.item.ClampedModelPredicateProvider
+import net.minecraft.item.ItemStack
+import net.minecraft.util.Identifier
+import opekope2.avm_staff.api.staffRendererOverrideComponentType
+import opekope2.avm_staff.util.MOD_ID
+
+const val HEAD_SEED = -0b10011_10100_00001_00110_00110_00000
+const val ITEM_SEED = -0b10011_10100_00001_00110_00110_00001
+const val ROD_TOP_SEED = -0b10011_10100_00001_00110_00110_00010
+const val ROD_BOTTOM_SEED = -0b10011_10100_00001_00110_00110_00011
+
+// ModelPredicateProviderRegistry.register is private in common project
+@Environment(EnvType.CLIENT)
+fun registerModelPredicateProviders(register: (Identifier, ClampedModelPredicateProvider) -> Unit) {
+ register(Identifier(MOD_ID, "using_item")) { stack, _, entity, _ ->
+ if (entity != null && entity.isUsingItem && ItemStack.areEqual(entity.activeItem, stack)) 1f
+ else if (stack[staffRendererOverrideComponentType.get()]?.isActive == true) 1f
+ else 0f
+ }
+ register(Identifier(MOD_ID, "head")) { _, _, _, seed ->
+ if (seed == HEAD_SEED) 1f
+ else 0f
+ }
+ register(Identifier(MOD_ID, "item")) { _, _, _, seed ->
+ if (seed == ITEM_SEED) 1f
+ else 0f
+ }
+ register(Identifier(MOD_ID, "rod_top")) { _, _, _, seed ->
+ if (seed == ROD_TOP_SEED) 1f
+ else 0f
+ }
+ register(Identifier(MOD_ID, "rod_bottom")) { _, _, _, seed ->
+ if (seed == ROD_BOTTOM_SEED) 1f
+ else 0f
+ }
+}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/platform/StaffModPlatform.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/IC2SPacket.kt
similarity index 71%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/internal/platform/StaffModPlatform.kt
rename to StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/IC2SPacket.kt
index 373279657..5b485dd02 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/platform/StaffModPlatform.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/IC2SPacket.kt
@@ -16,14 +16,13 @@
* along with this mod. If not, see .
*/
-@file: JvmName("StaffModPlatform")
+package opekope2.avm_staff.internal.networking
-package opekope2.avm_staff.internal.platform
+import dev.architectury.networking.NetworkManager
+import net.fabricmc.api.EnvType
+import net.fabricmc.api.Environment
-import dev.architectury.injectables.annotations.ExpectPlatform
-import opekope2.avm_staff.IStaffMod
-
-@ExpectPlatform
-fun getStaffMod(): IStaffMod {
- throw AssertionError()
+interface IC2SPacket : IPacket {
+ @Environment(EnvType.CLIENT)
+ fun sendToServer() = NetworkManager.sendToServer(this)
}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/IPacket.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/IPacket.kt
index 2a301d6de..49c2bf04e 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/IPacket.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/IPacket.kt
@@ -19,7 +19,8 @@
package opekope2.avm_staff.internal.networking
import net.minecraft.network.PacketByteBuf
+import net.minecraft.network.packet.CustomPayload
-interface IPacket {
+interface IPacket : CustomPayload {
fun write(buf: PacketByteBuf)
}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/PacketRegistrar.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/PacketRegistrar.kt
index 91dfb453b..4bb72ec7d 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/PacketRegistrar.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/PacketRegistrar.kt
@@ -18,22 +18,21 @@
package opekope2.avm_staff.internal.networking
-import dev.architectury.networking.NetworkChannel
import dev.architectury.networking.NetworkManager
import net.minecraft.network.PacketByteBuf
-import java.util.function.Function
+import net.minecraft.network.codec.PacketCodec
+import net.minecraft.network.packet.CustomPayload
+import net.minecraft.util.Identifier
abstract class PacketRegistrar(
- val channel: NetworkChannel,
- private val packetClass: Class,
- private val packetConstructor: Function
+ private val side: NetworkManager.Side,
+ id: Identifier,
+ packetConstructor: (PacketByteBuf) -> TPacket
) {
- fun registerHandler(handler: (TPacket, NetworkManager.PacketContext) -> Unit) {
- channel.register(packetClass, IPacket::write, packetConstructor) { packet, contextSupplier ->
- val context = contextSupplier.get()
- context.queue {
- handler(packet, context)
- }
- }
+ protected val payloadId = CustomPayload.Id(id)
+ private val codec = PacketCodec.of(IPacket::write, packetConstructor)
+
+ fun registerHandler(receiver: NetworkManager.NetworkReceiver) {
+ NetworkManager.registerReceiver(side, payloadId, codec, receiver)
}
}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/c2s/play/StaffAttackC2SPacket.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/c2s/play/AttackC2SPacket.kt
similarity index 63%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/c2s/play/StaffAttackC2SPacket.kt
rename to StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/c2s/play/AttackC2SPacket.kt
index fb79dbeac..9e84520e3 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/c2s/play/StaffAttackC2SPacket.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/c2s/play/AttackC2SPacket.kt
@@ -18,27 +18,26 @@
package opekope2.avm_staff.internal.networking.c2s.play
-import dev.architectury.networking.NetworkChannel
-import net.fabricmc.api.EnvType
-import net.fabricmc.api.Environment
+import dev.architectury.networking.NetworkManager
import net.minecraft.network.PacketByteBuf
+import net.minecraft.util.Hand
import net.minecraft.util.Identifier
-import opekope2.avm_staff.internal.networking.IPacket
+import opekope2.avm_staff.internal.networking.IC2SPacket
import opekope2.avm_staff.internal.networking.PacketRegistrar
import opekope2.avm_staff.util.MOD_ID
-class StaffAttackC2SPacket() : IPacket {
- constructor(@Suppress("UNUSED_PARAMETER") buf: PacketByteBuf) : this()
+class AttackC2SPacket(val hand: Hand) : IC2SPacket {
+ constructor(buf: PacketByteBuf) : this(buf.readEnumConstant(Hand::class.java))
+
+ override fun getId() = payloadId
override fun write(buf: PacketByteBuf) {
+ buf.writeEnumConstant(hand)
}
- @Environment(EnvType.CLIENT)
- fun send() = channel.sendToServer(this)
-
- companion object : PacketRegistrar(
- NetworkChannel.create(Identifier(MOD_ID, "remove_item_from_staff")),
- StaffAttackC2SPacket::class.java,
- ::StaffAttackC2SPacket
+ companion object : PacketRegistrar(
+ NetworkManager.c2s(),
+ Identifier(MOD_ID, "attack"),
+ ::AttackC2SPacket
)
}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/c2s/play/AddItemToStaffC2SPacket.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/c2s/play/InsertItemIntoStaffC2SPacket.kt
similarity index 63%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/c2s/play/AddItemToStaffC2SPacket.kt
rename to StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/c2s/play/InsertItemIntoStaffC2SPacket.kt
index c02a75621..f14d33a8a 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/c2s/play/AddItemToStaffC2SPacket.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/c2s/play/InsertItemIntoStaffC2SPacket.kt
@@ -18,27 +18,25 @@
package opekope2.avm_staff.internal.networking.c2s.play
-import dev.architectury.networking.NetworkChannel
-import net.fabricmc.api.EnvType
-import net.fabricmc.api.Environment
+import dev.architectury.networking.NetworkManager
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
-import opekope2.avm_staff.internal.networking.IPacket
+import opekope2.avm_staff.internal.networking.IC2SPacket
import opekope2.avm_staff.internal.networking.PacketRegistrar
import opekope2.avm_staff.util.MOD_ID
-class AddItemToStaffC2SPacket() : IPacket {
- constructor(@Suppress("UNUSED_PARAMETER") buf: PacketByteBuf) : this()
+class InsertItemIntoStaffC2SPacket() : IC2SPacket {
+ @Suppress("UNUSED_PARAMETER")
+ constructor(buf: PacketByteBuf) : this()
+
+ override fun getId() = payloadId
override fun write(buf: PacketByteBuf) {
}
- @Environment(EnvType.CLIENT)
- fun send() = channel.sendToServer(this)
-
- companion object : PacketRegistrar(
- NetworkChannel.create(Identifier(MOD_ID, "add_item_to_staff")),
- AddItemToStaffC2SPacket::class.java,
- ::AddItemToStaffC2SPacket
+ companion object : PacketRegistrar(
+ NetworkManager.c2s(),
+ Identifier(MOD_ID, "add_item"),
+ ::InsertItemIntoStaffC2SPacket
)
}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/c2s/play/RemoveItemFromStaffC2SPacket.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/c2s/play/RemoveItemFromStaffC2SPacket.kt
index 3b226c403..7fc9276bf 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/c2s/play/RemoveItemFromStaffC2SPacket.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/networking/c2s/play/RemoveItemFromStaffC2SPacket.kt
@@ -18,27 +18,25 @@
package opekope2.avm_staff.internal.networking.c2s.play
-import dev.architectury.networking.NetworkChannel
-import net.fabricmc.api.EnvType
-import net.fabricmc.api.Environment
+import dev.architectury.networking.NetworkManager
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
-import opekope2.avm_staff.internal.networking.IPacket
+import opekope2.avm_staff.internal.networking.IC2SPacket
import opekope2.avm_staff.internal.networking.PacketRegistrar
import opekope2.avm_staff.util.MOD_ID
-class RemoveItemFromStaffC2SPacket() : IPacket {
- constructor(@Suppress("UNUSED_PARAMETER") buf: PacketByteBuf) : this()
+class RemoveItemFromStaffC2SPacket() : IC2SPacket {
+ @Suppress("UNUSED_PARAMETER")
+ constructor(buf: PacketByteBuf) : this()
+
+ override fun getId() = payloadId
override fun write(buf: PacketByteBuf) {
}
- @Environment(EnvType.CLIENT)
- fun send() = channel.sendToServer(this)
-
companion object : PacketRegistrar(
- NetworkChannel.create(Identifier(MOD_ID, "remove_item_from_staff")),
- RemoveItemFromStaffC2SPacket::class.java,
+ NetworkManager.c2s(),
+ Identifier(MOD_ID, "remove_item"),
::RemoveItemFromStaffC2SPacket
)
}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/AnvilHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/AnvilHandler.kt
new file mode 100644
index 000000000..c394f1d3c
--- /dev/null
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/AnvilHandler.kt
@@ -0,0 +1,136 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+package opekope2.avm_staff.internal.staff_handler
+
+import dev.architectury.event.EventResult
+import net.minecraft.block.AnvilBlock
+import net.minecraft.component.type.AttributeModifierSlot
+import net.minecraft.component.type.AttributeModifiersComponent
+import net.minecraft.entity.Entity
+import net.minecraft.entity.LivingEntity
+import net.minecraft.entity.attribute.EntityAttributeModifier
+import net.minecraft.entity.attribute.EntityAttributes
+import net.minecraft.entity.player.PlayerEntity
+import net.minecraft.item.ItemStack
+import net.minecraft.predicate.entity.EntityPredicates
+import net.minecraft.util.Hand
+import net.minecraft.util.math.BlockPos
+import net.minecraft.util.math.Box
+import net.minecraft.util.math.Direction
+import net.minecraft.world.World
+import net.minecraft.world.WorldEvents
+import opekope2.avm_staff.api.staff.StaffHandler
+import opekope2.avm_staff.util.attackDamage
+import opekope2.avm_staff.util.equipTime
+import opekope2.avm_staff.util.mutableItemStackInStaff
+import kotlin.math.ceil
+import kotlin.math.floor
+
+class AnvilHandler(private val damagedStackFactory: () -> ItemStack?) : StaffHandler() {
+ override val attributeModifiers: AttributeModifiersComponent
+ get() = ATTRIBUTE_MODIFIERS
+
+ override fun attackBlock(
+ staffStack: ItemStack,
+ world: World,
+ attacker: LivingEntity,
+ target: BlockPos,
+ side: Direction,
+ hand: Hand
+ ): EventResult {
+ return EventResult.interruptFalse()
+ }
+
+ override fun attackEntity(
+ staffStack: ItemStack,
+ world: World,
+ attacker: LivingEntity,
+ target: Entity,
+ hand: Hand
+ ): EventResult {
+ if (world.isClient) return EventResult.interruptDefault()
+
+ val fallDistance = ceil(attacker.fallDistance - 1f)
+ if (fallDistance <= 0) return EventResult.interruptDefault()
+
+ aoeAttack(world, attacker, target, fallDistance)
+ world.syncWorldEvent(WorldEvents.SMASH_ATTACK, target.steppingPos, 750)
+ attacker.fallDistance = 0f
+
+ val broke = damageAnvil(staffStack, attacker, fallDistance)
+ world.syncWorldEvent(
+ if (broke) WorldEvents.ANVIL_DESTROYED
+ else WorldEvents.ANVIL_LANDS,
+ target.blockPos,
+ 0
+ )
+
+ return EventResult.interruptDefault()
+ }
+
+ private fun aoeAttack(world: World, attacker: LivingEntity, target: Entity, fallDistance: Float) {
+ val cappedFallDistance = floor(fallDistance * AnvilBlock.FALLING_BLOCK_ENTITY_DAMAGE_MULTIPLIER)
+ .coerceAtMost(AnvilBlock.FALLING_BLOCK_ENTITY_MAX_DAMAGE.toFloat())
+ val cooldownProgress =
+ if (attacker is PlayerEntity) attacker.getAttackCooldownProgress(0f)
+ else 1f
+ val amount = cappedFallDistance * cooldownProgress
+ val radius = cappedFallDistance / 20f
+ val radius2 = radius * radius
+ val box = Box(target.pos, target.pos).expand(radius.toDouble())
+ val predicate = EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR
+ .and(EntityPredicates.VALID_LIVING_ENTITY)
+ .and { it.squaredDistanceTo(target) <= radius2 }
+
+ world.getOtherEntities(attacker, box, predicate).forEach { entity ->
+ entity.damage(world.damageSources.fallingAnvil(attacker), amount / (entity.distanceTo(target) + 1))
+ }
+ }
+
+ private fun damageAnvil(staffStack: ItemStack, attacker: LivingEntity, fallDistance: Float): Boolean {
+ if (attacker is PlayerEntity && !attacker.abilities.creativeMode && attacker.random.nextFloat() < 0.05f + fallDistance * 0.05f) {
+ val damagedStack = damagedStackFactory()
+ staffStack.mutableItemStackInStaff = damagedStack
+ return damagedStack == null
+ }
+
+ return false
+ }
+
+ override fun canSwingHand(staffStack: ItemStack, world: World, holder: LivingEntity, hand: Hand): Boolean {
+ return ceil(holder.fallDistance - 1f) > 0f
+ }
+
+ override fun disablesShield() = true
+
+ private companion object {
+ private val ATTRIBUTE_MODIFIERS = AttributeModifiersComponent.builder()
+ .add(EntityAttributes.GENERIC_ATTACK_DAMAGE, attackDamage(40.0), AttributeModifierSlot.MAINHAND)
+ .add(EntityAttributes.GENERIC_ATTACK_SPEED, equipTime(4.0), AttributeModifierSlot.MAINHAND)
+ .add(EntityAttributes.GENERIC_MOVEMENT_SPEED, anvilModifier(), AttributeModifierSlot.MAINHAND)
+ .add(EntityAttributes.GENERIC_MOVEMENT_SPEED, anvilModifier(), AttributeModifierSlot.OFFHAND)
+ .add(EntityAttributes.GENERIC_JUMP_STRENGTH, anvilModifier(), AttributeModifierSlot.MAINHAND)
+ .add(EntityAttributes.GENERIC_JUMP_STRENGTH, anvilModifier(), AttributeModifierSlot.OFFHAND)
+ .build()
+
+ private fun anvilModifier() = EntityAttributeModifier(
+ "Anvil modifier", -1.0, EntityAttributeModifier.Operation.ADD_MULTIPLIED_TOTAL
+ )
+ }
+}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/BellBlockHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/BellBlockHandler.kt
new file mode 100644
index 000000000..2b8bf6b0e
--- /dev/null
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/BellBlockHandler.kt
@@ -0,0 +1,121 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+package opekope2.avm_staff.internal.staff_handler
+
+import dev.architectury.event.EventResult
+import net.fabricmc.api.EnvType
+import net.fabricmc.api.Environment
+import net.minecraft.client.render.RenderLayer
+import net.minecraft.client.render.VertexConsumerProvider
+import net.minecraft.client.render.block.entity.BellBlockEntityRenderer
+import net.minecraft.client.render.model.json.ModelTransformationMode
+import net.minecraft.client.util.math.MatrixStack
+import net.minecraft.component.type.AttributeModifierSlot
+import net.minecraft.component.type.AttributeModifiersComponent
+import net.minecraft.entity.Entity
+import net.minecraft.entity.LivingEntity
+import net.minecraft.entity.attribute.EntityAttributes
+import net.minecraft.entity.player.PlayerEntity
+import net.minecraft.item.ItemStack
+import net.minecraft.sound.SoundCategory
+import net.minecraft.sound.SoundEvents
+import net.minecraft.util.Hand
+import net.minecraft.util.TypedActionResult
+import net.minecraft.world.World
+import opekope2.avm_staff.api.item.renderer.IStaffItemRenderer
+import opekope2.avm_staff.api.staff.StaffHandler
+import opekope2.avm_staff.util.addDefault
+import opekope2.avm_staff.util.attackDamage
+import opekope2.avm_staff.util.attackSpeed
+import opekope2.avm_staff.util.push
+
+class BellBlockHandler : StaffHandler() {
+ override val attributeModifiers: AttributeModifiersComponent
+ get() = ATTRIBUTE_MODIFIERS
+
+ override fun use(
+ staffStack: ItemStack,
+ world: World,
+ user: PlayerEntity,
+ hand: Hand
+ ): TypedActionResult {
+ world.playSound(user, user.blockPos, SoundEvents.BLOCK_BELL_USE, SoundCategory.BLOCKS, 2f, 1f)
+
+ return TypedActionResult.success(staffStack)
+ }
+
+ override fun attackEntity(
+ staffStack: ItemStack,
+ world: World,
+ attacker: LivingEntity,
+ target: Entity,
+ hand: Hand
+ ): EventResult {
+ world.playSound(
+ target as? PlayerEntity,
+ target.blockPos,
+ SoundEvents.BLOCK_BELL_USE,
+ attacker.soundCategory,
+ 2f,
+ 1f
+ )
+
+ return EventResult.pass()
+ }
+
+ @Environment(EnvType.CLIENT)
+ class BellStaffItemRenderer : IStaffItemRenderer {
+ private val bellModel = BellBlockEntityRenderer.getTexturedModelData().createModel().apply {
+ setPivot(-8f, -12f, -8f)
+ }
+
+ override fun renderItemInStaff(
+ staffStack: ItemStack,
+ mode: ModelTransformationMode,
+ matrices: MatrixStack,
+ vertexConsumers: VertexConsumerProvider,
+ light: Int,
+ overlay: Int
+ ) {
+ matrices.push {
+ scale(16f / 9f, 16f / 9f, 16f / 9f)
+ translate(0f, 2f / 9f, 0f)
+
+ bellModel.render(
+ matrices,
+ BellBlockEntityRenderer.BELL_BODY_TEXTURE.getVertexConsumer(
+ vertexConsumers,
+ RenderLayer::getEntitySolid
+ ),
+ light,
+ overlay
+ )
+ }
+ }
+ }
+
+ companion object {
+ private val ATTRIBUTE_MODIFIERS = AttributeModifiersComponent.builder()
+ .add(EntityAttributes.GENERIC_ATTACK_DAMAGE, attackDamage(8.0), AttributeModifierSlot.MAINHAND)
+ .add(EntityAttributes.GENERIC_ATTACK_SPEED, attackSpeed(1.5), AttributeModifierSlot.MAINHAND)
+ .addDefault(EntityAttributes.PLAYER_ENTITY_INTERACTION_RANGE)
+ .addDefault(EntityAttributes.PLAYER_BLOCK_INTERACTION_RANGE)
+ .build()
+ }
+}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/BoneBlockHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/BoneBlockHandler.kt
similarity index 59%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/BoneBlockHandler.kt
rename to StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/BoneBlockHandler.kt
index d0f8a6ea9..f575a8669 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/BoneBlockHandler.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/BoneBlockHandler.kt
@@ -16,11 +16,15 @@
* along with this mod. If not, see .
*/
-package opekope2.avm_staff.internal.staff_item_handler
+package opekope2.avm_staff.internal.staff_handler
+import net.minecraft.component.type.AttributeModifierSlot
+import net.minecraft.component.type.AttributeModifiersComponent
import net.minecraft.entity.LivingEntity
+import net.minecraft.entity.attribute.EntityAttributes
import net.minecraft.item.BoneMealItem
import net.minecraft.item.ItemStack
+import net.minecraft.item.Items
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.math.BlockPos
@@ -28,9 +32,15 @@ import net.minecraft.util.math.Direction
import net.minecraft.world.World
import net.minecraft.world.WorldEvents
import net.minecraft.world.event.GameEvent
-import opekope2.avm_staff.api.item.StaffItemHandler
+import opekope2.avm_staff.api.staff.StaffHandler
+import opekope2.avm_staff.util.addDefault
+import opekope2.avm_staff.util.attackDamage
+import opekope2.avm_staff.util.attackSpeed
+
+class BoneBlockHandler : StaffHandler() {
+ override val attributeModifiers: AttributeModifiersComponent
+ get() = ATTRIBUTE_MODIFIERS
-class BoneBlockHandler : StaffItemHandler() {
override fun useOnBlock(
staffStack: ItemStack,
world: World,
@@ -39,14 +49,14 @@ class BoneBlockHandler : StaffItemHandler() {
side: Direction,
hand: Hand
): ActionResult {
- if (BoneMealItem.useOnFertilizable(staffStack.copy(), world, target)) {
+ if (BoneMealItem.useOnFertilizable(Items.BONE_MEAL.defaultStack, world, target)) {
// TODO fertilize area when enchanted
if (!world.isClient) {
- user.emitGameEvent(GameEvent.ITEM_INTERACT_FINISH) // FIXME user originally PlayerEntity
- world.syncWorldEvent(WorldEvents.BONE_MEAL_USED, target, 0)
+ user.emitGameEvent(GameEvent.ITEM_INTERACT_FINISH)
+ world.syncWorldEvent(WorldEvents.BONE_MEAL_USED, target, 15)
}
- return ActionResult.success(world.isClient)
+ return ActionResult.SUCCESS
}
val targetState = world.getBlockState(target)
@@ -56,10 +66,19 @@ class BoneBlockHandler : StaffItemHandler() {
if (!BoneMealItem.useOnGround(staffStack.copy(), world, neighborOnUsedSide, side)) return ActionResult.PASS
if (!world.isClient) {
- user.emitGameEvent(GameEvent.ITEM_INTERACT_FINISH) // FIXME user originally PlayerEntity
- world.syncWorldEvent(WorldEvents.BONE_MEAL_USED, neighborOnUsedSide, 0)
+ user.emitGameEvent(GameEvent.ITEM_INTERACT_FINISH)
+ world.syncWorldEvent(WorldEvents.BONE_MEAL_USED, neighborOnUsedSide, 15)
}
- return ActionResult.success(world.isClient)
+ return ActionResult.SUCCESS
+ }
+
+ companion object {
+ private val ATTRIBUTE_MODIFIERS = AttributeModifiersComponent.builder()
+ .add(EntityAttributes.GENERIC_ATTACK_DAMAGE, attackDamage(5.0), AttributeModifierSlot.MAINHAND)
+ .add(EntityAttributes.GENERIC_ATTACK_SPEED, attackSpeed(2.0), AttributeModifierSlot.MAINHAND)
+ .addDefault(EntityAttributes.PLAYER_ENTITY_INTERACTION_RANGE)
+ .addDefault(EntityAttributes.PLAYER_BLOCK_INTERACTION_RANGE)
+ .build()
}
}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/CampfireHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/CampfireHandler.kt
similarity index 90%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/CampfireHandler.kt
rename to StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/CampfireHandler.kt
index e2fbe0703..50442902c 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/CampfireHandler.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/CampfireHandler.kt
@@ -16,9 +16,10 @@
* along with this mod. If not, see .
*/
-package opekope2.avm_staff.internal.staff_item_handler
+package opekope2.avm_staff.internal.staff_handler
import dev.architectury.event.events.common.TickEvent
+import dev.architectury.registry.registries.RegistrySupplier
import net.fabricmc.api.EnvType
import net.fabricmc.api.Environment
import net.minecraft.block.*
@@ -29,7 +30,7 @@ import net.minecraft.entity.LivingEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.projectile.ProjectileUtil
import net.minecraft.item.ItemStack
-import net.minecraft.particle.ParticleEffect
+import net.minecraft.particle.SimpleParticleType
import net.minecraft.server.MinecraftServer
import net.minecraft.state.property.Properties.LIT
import net.minecraft.util.Hand
@@ -43,15 +44,15 @@ import net.minecraft.util.math.random.Random
import net.minecraft.world.RaycastContext
import net.minecraft.world.World
import net.minecraft.world.event.GameEvent
-import opekope2.avm_staff.IStaffMod
-import opekope2.avm_staff.api.item.StaffItemHandler
-import opekope2.avm_staff.mixin.IEntityMixin
+import opekope2.avm_staff.api.rocketModeComponentType
+import opekope2.avm_staff.api.staff.StaffHandler
+import opekope2.avm_staff.internal.MinecraftUnit
import opekope2.avm_staff.util.*
class CampfireHandler(
- private val particleEffectGetter: (IStaffMod) -> ParticleEffect,
+ private val particleEffectSupplier: RegistrySupplier,
private val properties: Properties
-) : StaffItemHandler() {
+) : StaffHandler() {
override val maxUseTime: Int
get() = 72000
@@ -61,10 +62,12 @@ class CampfireHandler(
user: PlayerEntity,
hand: Hand
): TypedActionResult {
- staffStack.getOrCreateNbt().putBoolean(ROCKET_MODE_KEY, user.isSneaking && !user.isOnGround)
+ if (user.isSneaking && !user.isOnGround) {
+ staffStack[rocketModeComponentType.get()] = MinecraftUnit.INSTANCE
+ }
user.setCurrentHand(hand)
- return TypedActionResult.pass(staffStack)
+ return TypedActionResult.consume(staffStack)
}
override fun usageTick(staffStack: ItemStack, world: World, user: LivingEntity, remainingUseTicks: Int) {
@@ -73,8 +76,7 @@ class CampfireHandler(
val forward = user.rotationVector
val origin = user.approximateStaffTipPosition
val target = origin + forward * FLAME_MAX_DISTANCE
- val relativeRight =
- (user as IEntityMixin).invokeGetRotationVector(0f, MathHelper.wrapDegrees(user.yaw + 90f)).normalize()
+ val relativeRight = user.getRotationVector(0f, MathHelper.wrapDegrees(user.yaw + 90f)).normalize()
val relativeUp = relativeRight.crossProduct(forward).normalize()
if (world.isClient) {
@@ -103,7 +105,7 @@ class CampfireHandler(
}
}
- if (staffStack.nbt?.getBoolean(ROCKET_MODE_KEY) == true) {
+ if (rocketModeComponentType.get() in staffStack) {
user.addVelocity(forward * -properties.rocketThrust)
user.velocityModified = true
if (forward.y < 0.0) {
@@ -133,19 +135,15 @@ class CampfireHandler(
val particleSpeed = targetDirection.normalize() * FLAME_SPEED * (0.9 + Math.random() * 0.2)
particleManager.addParticle(
- particleEffectGetter(IStaffMod.get()),
- origin.x,
- origin.y,
- origin.z,
- particleSpeed.x,
- particleSpeed.y,
- particleSpeed.z
+ particleEffectSupplier.get(),
+ origin.x, origin.y, origin.z,
+ particleSpeed.x, particleSpeed.y, particleSpeed.z
)!!.maxAge = (0.25 * FLAME_MAX_AGE / (Math.random() * 0.8 + 0.2) - 0.05 * FLAME_MAX_AGE).toInt()
}
}
override fun onStoppedUsing(staffStack: ItemStack, world: World, user: LivingEntity, remainingUseTicks: Int) {
- staffStack.getOrCreateNbt().remove(ROCKET_MODE_KEY)
+ staffStack.remove(rocketModeComponentType.get())
}
override fun finishUsing(staffStack: ItemStack, world: World, user: LivingEntity): ItemStack {
@@ -250,8 +248,6 @@ class CampfireHandler(
private const val FLAMETHROWER_CONE_RAYS = 16
private const val FLAMETHROWER_CONE_RAYS_TOTAL = FLAMETHROWER_CONE_RAYS * FLAMETHROWER_CONE_RAYS
- private const val ROCKET_MODE_KEY = "RocketMode"
-
private val flameParticleCount: Int
@Environment(EnvType.CLIENT)
get() = when (MinecraftClient.getInstance().options.graphicsMode.value!!) {
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/FurnaceHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/FurnaceHandler.kt
similarity index 54%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/FurnaceHandler.kt
rename to StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/FurnaceHandler.kt
index b7dcd87bd..a542bbf60 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/FurnaceHandler.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/FurnaceHandler.kt
@@ -16,29 +16,22 @@
* along with this mod. If not, see .
*/
-package opekope2.avm_staff.internal.staff_item_handler
+package opekope2.avm_staff.internal.staff_handler
-import com.google.common.collect.ImmutableMultimap
-import com.google.common.collect.Multimap
import net.fabricmc.api.EnvType
import net.fabricmc.api.Environment
import net.minecraft.block.AbstractFurnaceBlock
import net.minecraft.block.Block
import net.minecraft.block.BlockState
+import net.minecraft.block.entity.AbstractFurnaceBlockEntity
import net.minecraft.client.MinecraftClient
-import net.minecraft.client.render.block.BlockModels
-import net.minecraft.client.render.model.BakedModel
-import net.minecraft.client.render.model.Baker
-import net.minecraft.client.render.model.ModelBakeSettings
-import net.minecraft.client.render.model.UnbakedModel
-import net.minecraft.client.render.model.json.Transformation
-import net.minecraft.client.texture.Sprite
-import net.minecraft.client.util.SpriteIdentifier
-import net.minecraft.entity.EquipmentSlot
+import net.minecraft.client.render.VertexConsumerProvider
+import net.minecraft.client.render.model.json.ModelTransformationMode
+import net.minecraft.client.util.math.MatrixStack
+import net.minecraft.component.type.AttributeModifierSlot
+import net.minecraft.component.type.AttributeModifiersComponent
import net.minecraft.entity.ItemEntity
import net.minecraft.entity.LivingEntity
-import net.minecraft.entity.attribute.EntityAttribute
-import net.minecraft.entity.attribute.EntityAttributeModifier
import net.minecraft.entity.attribute.EntityAttributes
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.inventory.SingleStackInventory
@@ -50,38 +43,39 @@ import net.minecraft.server.world.ServerWorld
import net.minecraft.sound.SoundCategory
import net.minecraft.sound.SoundEvent
import net.minecraft.util.Hand
-import net.minecraft.util.Identifier
import net.minecraft.util.TypedActionResult
import net.minecraft.util.math.Box
import net.minecraft.world.World
-import opekope2.avm_staff.api.item.StaffItemHandler
-import opekope2.avm_staff.api.item.model.IStaffItemBakedModel
-import opekope2.avm_staff.api.item.model.IStaffItemUnbakedModel
-import opekope2.avm_staff.mixin.IAbstractFurnaceBlockEntityMixin
+import opekope2.avm_staff.api.furnaceLitComponentType
+import opekope2.avm_staff.api.item.renderer.BlockStateStaffItemRenderer
+import opekope2.avm_staff.api.item.renderer.IStaffItemRenderer
+import opekope2.avm_staff.api.staff.StaffHandler
+import opekope2.avm_staff.internal.MinecraftUnit
import opekope2.avm_staff.util.*
-import java.util.function.Function
-import java.util.function.Supplier
import kotlin.jvm.optionals.getOrNull
class FurnaceHandler(
private val recipeType: RecipeType,
private val smeltSound: SoundEvent
-) : StaffItemHandler() {
+) : StaffHandler() {
override val maxUseTime = 72000
+ override val attributeModifiers: AttributeModifiersComponent
+ get() = ATTRIBUTE_MODIFIERS
+
override fun use(
staffStack: ItemStack,
world: World,
user: PlayerEntity,
hand: Hand
): TypedActionResult {
- staffStack.getOrCreateNbt().apply {
- putBoolean(LIT_KEY, true)
- putInt(BURN_TIME_KEY, 0)
+ staffStack[furnaceLitComponentType.get()] = MinecraftUnit.INSTANCE
+ if (!world.isClient) {
+ user.activeItemTempData = BurnTimeTempData(0)
}
user.setCurrentHand(hand)
- return TypedActionResult.pass(staffStack)
+ return TypedActionResult.consume(staffStack)
}
override fun usageTick(staffStack: ItemStack, world: World, user: LivingEntity, remainingUseTicks: Int) {
@@ -94,12 +88,11 @@ class FurnaceHandler(
return
}
- val nbt = staffStack.getOrCreateNbt()
- var burnTime = nbt.getInt(BURN_TIME_KEY)
- nbt.putInt(BURN_TIME_KEY, burnTime + 1)
+ val burnTimeData = user.activeItemTempData as BurnTimeTempData
+ burnTimeData.burnTime++
val stackToSmelt = itemToSmelt?.stack ?: return
- if (burnTime < stackToSmelt.count) return
+ if (burnTimeData.burnTime < stackToSmelt.count) return
val inventory = ItemEntityInventory(itemToSmelt)
val recipe = world.recipeManager.getFirstMatch(recipeType, inventory, world).getOrNull()?.value ?: return
@@ -107,17 +100,15 @@ class FurnaceHandler(
val (vx, vy, vz) = itemToSmelt.velocity
world.spawnEntity(ItemEntity(world, itemToSmelt.x, itemToSmelt.y, itemToSmelt.z, resultItem, vx, vy, vz))
- IAbstractFurnaceBlockEntityMixin.invokeDropExperience(
+ AbstractFurnaceBlockEntity.dropExperience(
world as ServerWorld,
itemToSmelt.pos,
stackToSmelt.count,
recipe.experience
)
-
- burnTime -= stackToSmelt.count
- nbt.putInt(BURN_TIME_KEY, burnTime + 1)
-
itemToSmelt.discard()
+
+ burnTimeData.burnTime -= stackToSmelt.count
}
private fun findItemToSmelt(
@@ -135,7 +126,7 @@ class FurnaceHandler(
if (Math.random() >= 0.1) return
val (x, y, z) = itemToSmelt.pos
- world.playSound(x, y, z, smeltSound, SoundCategory.BLOCKS, 1.0f, 1.0f, false)
+ world.playSound(x, y, z, smeltSound, SoundCategory.BLOCKS, 1f, 1f, false)
val rx = Math.random() * 0.25 - 0.25 / 2
val ry = Math.random() * 0.5
@@ -147,9 +138,9 @@ class FurnaceHandler(
}
override fun onStoppedUsing(staffStack: ItemStack, world: World, user: LivingEntity, remainingUseTicks: Int) {
- staffStack.getOrCreateNbt().apply {
- remove(LIT_KEY)
- remove(BURN_TIME_KEY)
+ staffStack.remove(furnaceLitComponentType.get())
+ if (!world.isClient) {
+ user.activeItemTempData = null
}
}
@@ -158,7 +149,7 @@ class FurnaceHandler(
return staffStack
}
- override fun allowNbtUpdateAnimation(
+ override fun allowComponentsUpdateAnimation(
oldStaffStack: ItemStack,
newStaffStack: ItemStack,
player: PlayerEntity,
@@ -175,82 +166,54 @@ class FurnaceHandler(
return selectedSlotChanged
}
- override fun getAttributeModifiers(
- staffStack: ItemStack,
- slot: EquipmentSlot
- ): Multimap {
- return if (slot == EquipmentSlot.MAINHAND) ATTRIBUTE_MODIFIERS
- else super.getAttributeModifiers(staffStack, slot)
- }
-
@Environment(EnvType.CLIENT)
- private class FurnaceUnbakedModel(private val unlitState: BlockState, private val litState: BlockState) :
- IStaffItemUnbakedModel {
- private val litStateId = BlockModels.getModelId(litState)
- private val unlitStateId = BlockModels.getModelId(unlitState)
- private val dependencies = setOf(litStateId, unlitStateId)
-
- override fun getModelDependencies() = dependencies
-
- override fun setParents(modelLoader: Function?) {
- }
+ class FurnaceStaffItemRenderer(unlitState: BlockState, litState: BlockState) : IStaffItemRenderer {
+ constructor(furnaceBlock: Block) : this(
+ furnaceBlock.defaultState,
+ furnaceBlock.defaultState.with(AbstractFurnaceBlock.LIT, true)
+ )
- override fun bake(
- baker: Baker,
- textureGetter: Function,
- rotationContainer: ModelBakeSettings,
- modelId: Identifier,
- transformation: Transformation
- ): IStaffItemBakedModel? {
- val unlitModel = baker.bake(unlitStateId, rotationContainer) ?: return null
- val litModel = baker.bake(litStateId, rotationContainer) ?: return null
-
- return FurnaceBakedModel(
- unlitModel.transform(unlitState, transformation, textureGetter),
- litModel.transform(litState, transformation, textureGetter)
- )
+ private val unlitRenderer = BlockStateStaffItemRenderer(unlitState)
+ private val litRenderer = BlockStateStaffItemRenderer(litState)
+
+ override fun renderItemInStaff(
+ staffStack: ItemStack,
+ mode: ModelTransformationMode,
+ matrices: MatrixStack,
+ vertexConsumers: VertexConsumerProvider,
+ light: Int,
+ overlay: Int
+ ) {
+ val renderer =
+ if (furnaceLitComponentType.get() in staffStack) litRenderer
+ else unlitRenderer
+
+ renderer.renderItemInStaff(staffStack, mode, matrices, vertexConsumers, light, overlay)
}
}
@Environment(EnvType.CLIENT)
- private class FurnaceBakedModel(private val unlitModel: BakedModel, private val litModel: BakedModel) :
- BakedModel by unlitModel, IStaffItemBakedModel {
- override fun getModel(staffStack: ItemStack): BakedModel {
- return if (staffStack.nbt?.getBoolean(LIT_KEY) == true) litModel
- else unlitModel
- }
- }
+ private data class BurnTimeTempData(var burnTime: Int)
private class ItemEntityInventory(private val itemEntity: ItemEntity) : SingleStackInventory {
override fun getStack(): ItemStack = itemEntity.stack
- override fun setStack(stack: ItemStack) {}
+ override fun setStack(stack: ItemStack?) {}
override fun markDirty() {}
- override fun decreaseStack(count: Int): ItemStack = ItemStack.EMPTY
+ override fun canPlayerUse(player: PlayerEntity?) = false
- override fun asBlockEntity(): Nothing = throw UnsupportedOperationException()
+ override fun decreaseStack(count: Int): ItemStack = ItemStack.EMPTY
}
companion object {
- private const val LIT_KEY = "Lit"
- private const val BURN_TIME_KEY = "BurnTime"
private val SMELTING_VOLUME = Box(-0.5, -0.5, -0.5, 0.5, 0.5, 0.5).contract(0.25 / 2)
- private val ATTRIBUTE_MODIFIERS = ImmutableMultimap.of(
- EntityAttributes.GENERIC_ATTACK_DAMAGE,
- attackDamage(5.0),
- EntityAttributes.GENERIC_ATTACK_SPEED,
- attackSpeed(2.0)
- )
-
- fun getModelSupplierFactory(furnaceBlock: Block): Supplier> = Supplier {
- Supplier {
- FurnaceUnbakedModel(
- furnaceBlock.defaultState,
- furnaceBlock.defaultState.with(AbstractFurnaceBlock.LIT, true)
- )
- }
- }
+ private val ATTRIBUTE_MODIFIERS = AttributeModifiersComponent.builder()
+ .add(EntityAttributes.GENERIC_ATTACK_DAMAGE, attackDamage(10.0), AttributeModifierSlot.MAINHAND)
+ .add(EntityAttributes.GENERIC_ATTACK_SPEED, attackSpeed(1.25), AttributeModifierSlot.MAINHAND)
+ .addDefault(EntityAttributes.PLAYER_ENTITY_INTERACTION_RANGE)
+ .addDefault(EntityAttributes.PLAYER_BLOCK_INTERACTION_RANGE)
+ .build()
}
}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/LightningRodHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/LightningRodHandler.kt
similarity index 51%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/LightningRodHandler.kt
rename to StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/LightningRodHandler.kt
index 721563364..7e2ec8c5d 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/LightningRodHandler.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/LightningRodHandler.kt
@@ -16,40 +16,38 @@
* along with this mod. If not, see .
*/
-package opekope2.avm_staff.internal.staff_item_handler
+package opekope2.avm_staff.internal.staff_handler
import net.fabricmc.api.EnvType
import net.fabricmc.api.Environment
-import net.minecraft.block.BlockState
import net.minecraft.block.Blocks
-import net.minecraft.client.render.block.BlockModels
-import net.minecraft.client.render.model.Baker
-import net.minecraft.client.render.model.ModelBakeSettings
-import net.minecraft.client.render.model.UnbakedModel
-import net.minecraft.client.render.model.json.Transformation
-import net.minecraft.client.texture.Sprite
-import net.minecraft.client.util.SpriteIdentifier
+import net.minecraft.client.render.VertexConsumerProvider
+import net.minecraft.client.render.model.json.ModelTransformationMode
+import net.minecraft.client.util.math.MatrixStack
+import net.minecraft.component.type.AttributeModifierSlot
+import net.minecraft.component.type.AttributeModifiersComponent
import net.minecraft.entity.EntityType
import net.minecraft.entity.LivingEntity
+import net.minecraft.entity.attribute.EntityAttributes
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.ItemStack
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
-import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.world.World
-import opekope2.avm_staff.api.item.StaffItemHandler
-import opekope2.avm_staff.api.item.model.IStaffItemBakedModel
-import opekope2.avm_staff.api.item.model.IStaffItemUnbakedModel
-import opekope2.avm_staff.api.item.model.StaffItemBakedModel
+import opekope2.avm_staff.api.item.renderer.BlockStateStaffItemRenderer
+import opekope2.avm_staff.api.item.renderer.IStaffItemRenderer
+import opekope2.avm_staff.api.staff.StaffHandler
+import opekope2.avm_staff.util.addDefault
+import opekope2.avm_staff.util.interactionRange
import opekope2.avm_staff.util.isItemCoolingDown
-import opekope2.avm_staff.util.transform
-import org.joml.Vector3f
-import java.util.function.Function
-import java.util.function.Supplier
+import opekope2.avm_staff.util.push
+
+class LightningRodHandler : StaffHandler() {
+ override val attributeModifiers: AttributeModifiersComponent
+ get() = ATTRIBUTE_MODIFIERS
-class LightningRodHandler : StaffItemHandler() {
override fun useOnBlock(
staffStack: ItemStack,
world: World,
@@ -84,45 +82,34 @@ class LightningRodHandler : StaffItemHandler() {
}
@Environment(EnvType.CLIENT)
- private class LightningRodUnbakedModel(private val state: BlockState) : IStaffItemUnbakedModel {
- private val stateId = BlockModels.getModelId(state)
- private val dependencies = setOf(stateId)
-
- override fun getModelDependencies() = dependencies
-
- override fun setParents(modelLoader: Function?) {
- }
-
- override fun bake(
- baker: Baker,
- textureGetter: Function,
- rotationContainer: ModelBakeSettings,
- modelId: Identifier,
- transformation: Transformation
- ): IStaffItemBakedModel? {
- val baked = baker.bake(stateId, rotationContainer) ?: return null
-
- return StaffItemBakedModel(
- baked
- .transform(state, lightningRodTransformation, textureGetter)
- .transform(state, transformation, textureGetter)
- )
- }
-
- private companion object {
- private val lightningRodTransformation = Transformation(
- Vector3f(),
- Vector3f(-1f / 14f, 10f / 7f, -1f / 14f),
- Vector3f(8f / 7f)
- )
+ class LightningRodStaffItemRenderer : IStaffItemRenderer {
+ private val lightningRodRenderer = BlockStateStaffItemRenderer(Blocks.LIGHTNING_ROD.defaultState)
+
+ override fun renderItemInStaff(
+ staffStack: ItemStack,
+ mode: ModelTransformationMode,
+ matrices: MatrixStack,
+ vertexConsumers: VertexConsumerProvider,
+ light: Int,
+ overlay: Int
+ ) {
+ matrices.push {
+ if (mode != ModelTransformationMode.GUI && mode != ModelTransformationMode.FIXED) {
+ translate(0f, 22f / 16f, 0f)
+ }
+ lightningRodRenderer.renderItemInStaff(staffStack, mode, matrices, vertexConsumers, light, overlay)
+ }
}
}
- companion object {
- val modelSupplier: Supplier> = Supplier {
- Supplier {
- LightningRodUnbakedModel(Blocks.LIGHTNING_ROD.defaultState)
- }
- }
+ private companion object {
+ val ATTRIBUTE_MODIFIERS: AttributeModifiersComponent = AttributeModifiersComponent.builder()
+ .addDefault(EntityAttributes.GENERIC_ATTACK_DAMAGE)
+ .addDefault(EntityAttributes.GENERIC_ATTACK_SPEED)
+ .add(
+ EntityAttributes.PLAYER_ENTITY_INTERACTION_RANGE, interactionRange(2.0), AttributeModifierSlot.MAINHAND
+ )
+ .add(EntityAttributes.PLAYER_BLOCK_INTERACTION_RANGE, interactionRange(2.0), AttributeModifierSlot.MAINHAND)
+ .build()
}
}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/MagmaBlockHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/MagmaBlockHandler.kt
similarity index 66%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/MagmaBlockHandler.kt
rename to StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/MagmaBlockHandler.kt
index 48270d6a6..907f1fd5f 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/MagmaBlockHandler.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/MagmaBlockHandler.kt
@@ -16,30 +16,30 @@
* along with this mod. If not, see .
*/
-package opekope2.avm_staff.internal.staff_item_handler
+package opekope2.avm_staff.internal.staff_handler
-import com.google.common.collect.ImmutableMultimap
-import com.google.common.collect.Multimap
+import dev.architectury.event.EventResult
+import net.minecraft.component.type.AttributeModifierSlot
+import net.minecraft.component.type.AttributeModifiersComponent
import net.minecraft.entity.Entity
-import net.minecraft.entity.EquipmentSlot
import net.minecraft.entity.LivingEntity
-import net.minecraft.entity.attribute.EntityAttribute
-import net.minecraft.entity.attribute.EntityAttributeModifier
import net.minecraft.entity.attribute.EntityAttributes
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.projectile.SmallFireballEntity
import net.minecraft.item.ItemStack
-import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.TypedActionResult
import net.minecraft.world.World
import net.minecraft.world.WorldEvents
-import opekope2.avm_staff.api.item.StaffItemHandler
+import opekope2.avm_staff.api.staff.StaffHandler
import opekope2.avm_staff.util.*
-class MagmaBlockHandler : StaffItemHandler() {
+class MagmaBlockHandler : StaffHandler() {
override val maxUseTime = 72000
+ override val attributeModifiers: AttributeModifiersComponent
+ get() = ATTRIBUTE_MODIFIERS
+
override fun use(
staffStack: ItemStack,
world: World,
@@ -47,7 +47,7 @@ class MagmaBlockHandler : StaffItemHandler() {
hand: Hand
): TypedActionResult {
user.setCurrentHand(hand)
- return TypedActionResult.pass(staffStack)
+ return TypedActionResult.consume(staffStack)
}
override fun usageTick(staffStack: ItemStack, world: World, user: LivingEntity, remainingUseTicks: Int) {
@@ -56,10 +56,9 @@ class MagmaBlockHandler : StaffItemHandler() {
}
}
- override fun attack(staffStack: ItemStack, world: World, attacker: LivingEntity, hand: Hand): ActionResult {
+ override fun attack(staffStack: ItemStack, world: World, attacker: LivingEntity, hand: Hand) {
shootFireball(world, attacker)
(attacker as? PlayerEntity)?.resetLastAttackedTicks()
- return ActionResult.SUCCESS
}
override fun attackEntity(
@@ -68,22 +67,20 @@ class MagmaBlockHandler : StaffItemHandler() {
attacker: LivingEntity,
target: Entity,
hand: Hand
- ): ActionResult {
+ ): EventResult {
if (!world.isClient) {
target.setOnFireFor(8) // TODO duration
}
- return ActionResult.PASS
+ return EventResult.pass()
}
private fun shootFireball(world: World, user: LivingEntity) {
+ if (world.isClient) return
if (!user.canUseStaff) return
-
if (user is PlayerEntity && user.isAttackCoolingDown) return
- world.syncWorldEvent(user as? PlayerEntity, WorldEvents.BLAZE_SHOOTS, user.blockPos, 0)
-
- if (world.isClient) return
+ world.syncWorldEvent(WorldEvents.BLAZE_SHOOTS, user.blockPos, 0)
val (x, y, z) = user.rotationVector
world.spawnEntity(SmallFireballEntity(world, user, x, y, z).apply {
@@ -91,20 +88,12 @@ class MagmaBlockHandler : StaffItemHandler() {
})
}
- override fun getAttributeModifiers(
- staffStack: ItemStack,
- slot: EquipmentSlot
- ): Multimap {
- return if (slot == EquipmentSlot.MAINHAND) ATTRIBUTE_MODIFIERS
- else super.getAttributeModifiers(staffStack, slot)
- }
-
private companion object {
- private val ATTRIBUTE_MODIFIERS = ImmutableMultimap.of(
- EntityAttributes.GENERIC_ATTACK_DAMAGE,
- attackDamage(5.0),
- EntityAttributes.GENERIC_ATTACK_SPEED,
- attackSpeed(2.0)
- )
+ private val ATTRIBUTE_MODIFIERS = AttributeModifiersComponent.builder()
+ .add(EntityAttributes.GENERIC_ATTACK_DAMAGE, attackDamage(10.0), AttributeModifierSlot.MAINHAND)
+ .add(EntityAttributes.GENERIC_ATTACK_SPEED, attackSpeed(1.25), AttributeModifierSlot.MAINHAND)
+ .addDefault(EntityAttributes.PLAYER_ENTITY_INTERACTION_RANGE)
+ .addDefault(EntityAttributes.PLAYER_BLOCK_INTERACTION_RANGE)
+ .build()
}
}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/SnowBlockHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/SnowBlockHandler.kt
similarity index 87%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/SnowBlockHandler.kt
rename to StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/SnowBlockHandler.kt
index 19b780446..766b275c9 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/SnowBlockHandler.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/SnowBlockHandler.kt
@@ -16,21 +16,20 @@
* along with this mod. If not, see .
*/
-package opekope2.avm_staff.internal.staff_item_handler
+package opekope2.avm_staff.internal.staff_handler
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.projectile.thrown.SnowballEntity
import net.minecraft.item.ItemStack
import net.minecraft.sound.SoundEvents
-import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.TypedActionResult
import net.minecraft.world.World
-import opekope2.avm_staff.api.item.StaffItemHandler
+import opekope2.avm_staff.api.staff.StaffHandler
import opekope2.avm_staff.util.*
-class SnowBlockHandler : StaffItemHandler() {
+class SnowBlockHandler : StaffHandler() {
override val maxUseTime = 72000
override fun use(
@@ -40,22 +39,20 @@ class SnowBlockHandler : StaffItemHandler() {
hand: Hand
): TypedActionResult {
user.setCurrentHand(hand)
- return TypedActionResult.pass(staffStack)
+ return TypedActionResult.consume(staffStack)
}
override fun usageTick(staffStack: ItemStack, world: World, user: LivingEntity, remainingUseTicks: Int) {
throwSnowball(world, user)
}
- override fun attack(staffStack: ItemStack, world: World, attacker: LivingEntity, hand: Hand): ActionResult {
+ override fun attack(staffStack: ItemStack, world: World, attacker: LivingEntity, hand: Hand) {
throwSnowball(world, attacker)
(attacker as? PlayerEntity)?.resetLastAttackedTicks()
- return ActionResult.SUCCESS
}
private fun throwSnowball(world: World, user: LivingEntity) {
if (!user.canUseStaff) return
-
if (user is PlayerEntity && user.isAttackCoolingDown) return
world.playSound(
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/TntHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/TntHandler.kt
similarity index 59%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/TntHandler.kt
rename to StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/TntHandler.kt
index f1113e73f..af69b0b60 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/TntHandler.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/TntHandler.kt
@@ -16,10 +16,9 @@
* along with this mod. If not, see .
*/
-package opekope2.avm_staff.internal.staff_item_handler
+package opekope2.avm_staff.internal.staff_handler
import net.minecraft.entity.LivingEntity
-import net.minecraft.entity.TntEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.ItemStack
import net.minecraft.sound.SoundCategory
@@ -28,15 +27,14 @@ import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.world.World
import net.minecraft.world.event.GameEvent
-import opekope2.avm_staff.api.entity.IImpactTnt
-import opekope2.avm_staff.api.item.StaffItemHandler
+import opekope2.avm_staff.api.entity.ImpactTntEntity
+import opekope2.avm_staff.api.staff.StaffHandler
import opekope2.avm_staff.util.*
-class TntHandler : StaffItemHandler() {
- override fun attack(staffStack: ItemStack, world: World, attacker: LivingEntity, hand: Hand): ActionResult {
- return shootTnt(world, attacker).also {
- (attacker as? PlayerEntity)?.resetLastAttackedTicks()
- }
+class TntHandler : StaffHandler() {
+ override fun attack(staffStack: ItemStack, world: World, attacker: LivingEntity, hand: Hand) {
+ shootTnt(world, attacker)
+ (attacker as? PlayerEntity)?.resetLastAttackedTicks()
}
private fun shootTnt(world: World, attacker: LivingEntity): ActionResult {
@@ -44,16 +42,11 @@ class TntHandler : StaffItemHandler() {
if (attacker is PlayerEntity && attacker.isAttackCoolingDown) return ActionResult.FAIL
- val (x, y, z) = attacker.approximateStaffTipPosition
- world.spawnEntity(
- TntEntity(world, x, y, z, attacker).apply {
- velocity = attacker.rotationVector + attacker.velocity
- @Suppress("KotlinConstantConditions") // IImpactTnt is ducked into TntEntity
- (this as IImpactTnt).explodeOnImpact(true)
- world.playSound(null, x, y, z, SoundEvents.ENTITY_TNT_PRIMED, SoundCategory.BLOCKS, 1.0f, 1.0f)
- world.emitGameEvent(attacker, GameEvent.PRIME_FUSE, pos)
- }
- )
+ val spawnPos = attacker.approximateStaffTipPosition
+ val (x, y, z) = spawnPos
+ world.spawnEntity(ImpactTntEntity(world, x, y, z, attacker.rotationVector + attacker.velocity, attacker))
+ world.playSound(null, x, y, z, SoundEvents.ENTITY_TNT_PRIMED, SoundCategory.BLOCKS, 1f, 1f)
+ world.emitGameEvent(attacker, GameEvent.PRIME_FUSE, spawnPos)
return ActionResult.SUCCESS
}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/VanillaStaffHandlers.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/VanillaStaffHandlers.kt
new file mode 100644
index 000000000..d7ccb9d77
--- /dev/null
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/VanillaStaffHandlers.kt
@@ -0,0 +1,154 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+package opekope2.avm_staff.internal.staff_handler
+
+import net.fabricmc.api.EnvType
+import net.fabricmc.api.Environment
+import net.minecraft.block.Block
+import net.minecraft.block.Blocks
+import net.minecraft.item.BlockItem
+import net.minecraft.item.Item
+import net.minecraft.item.Items.*
+import net.minecraft.recipe.RecipeType
+import net.minecraft.registry.Registries
+import net.minecraft.sound.SoundEvents
+import opekope2.avm_staff.api.flamethrowerParticleType
+import opekope2.avm_staff.api.item.renderer.BlockStateStaffItemRenderer
+import opekope2.avm_staff.api.item.renderer.IStaffItemRenderer
+import opekope2.avm_staff.api.soulFlamethrowerParticleType
+import opekope2.avm_staff.api.staff.StaffHandler
+
+private fun Item.registerHandler(handler: StaffHandler) {
+ StaffHandler.register(Registries.ITEM.getId(this), handler)
+}
+
+fun registerVanillaStaffHandlers() {
+ ANVIL.registerHandler(AnvilHandler(CHIPPED_ANVIL::getDefaultStack))
+ CHIPPED_ANVIL.registerHandler(AnvilHandler(DAMAGED_ANVIL::getDefaultStack))
+ DAMAGED_ANVIL.registerHandler(AnvilHandler { null })
+
+ BELL.registerHandler(BellBlockHandler())
+
+ BONE_BLOCK.registerHandler(BoneBlockHandler())
+
+ CAMPFIRE.registerHandler(
+ CampfireHandler(flamethrowerParticleType, CampfireHandler.Properties(1 / 20.0, 5 / 20.0, 1, 0.1))
+ )
+ SOUL_CAMPFIRE.registerHandler(
+ CampfireHandler(soulFlamethrowerParticleType, CampfireHandler.Properties(2 / 20.0, 10 / 20.0, 2, 0.12))
+ )
+
+ // TODO command block
+
+ FURNACE.registerHandler(
+ FurnaceHandler(RecipeType.SMELTING, SoundEvents.BLOCK_FURNACE_FIRE_CRACKLE)
+ )
+ BLAST_FURNACE.registerHandler(
+ FurnaceHandler(RecipeType.BLASTING, SoundEvents.BLOCK_BLASTFURNACE_FIRE_CRACKLE)
+ )
+ SMOKER.registerHandler(
+ FurnaceHandler(RecipeType.SMOKING, SoundEvents.BLOCK_SMOKER_SMOKE)
+ )
+
+ LIGHTNING_ROD.registerHandler(LightningRodHandler())
+
+ MAGMA_BLOCK.registerHandler(MagmaBlockHandler())
+
+ SNOW_BLOCK.registerHandler(SnowBlockHandler())
+
+ TNT.registerHandler(TntHandler())
+
+ WITHER_SKELETON_SKULL.registerHandler(
+ WitherSkeletonSkullHandler()
+ )
+
+ WHITE_WOOL.registerHandler(WoolHandler(WHITE_WOOL as BlockItem, WHITE_CARPET as BlockItem))
+ ORANGE_WOOL.registerHandler(WoolHandler(ORANGE_WOOL as BlockItem, ORANGE_CARPET as BlockItem))
+ MAGENTA_WOOL.registerHandler(WoolHandler(MAGENTA_WOOL as BlockItem, MAGENTA_CARPET as BlockItem))
+ LIGHT_BLUE_WOOL.registerHandler(WoolHandler(LIGHT_BLUE_WOOL as BlockItem, LIGHT_BLUE_CARPET as BlockItem))
+ YELLOW_WOOL.registerHandler(WoolHandler(YELLOW_WOOL as BlockItem, YELLOW_CARPET as BlockItem))
+ LIME_WOOL.registerHandler(WoolHandler(LIME_WOOL as BlockItem, LIME_CARPET as BlockItem))
+ PINK_WOOL.registerHandler(WoolHandler(PINK_WOOL as BlockItem, PINK_CARPET as BlockItem))
+ GRAY_WOOL.registerHandler(WoolHandler(GRAY_WOOL as BlockItem, GRAY_CARPET as BlockItem))
+ LIGHT_GRAY_WOOL.registerHandler(WoolHandler(LIGHT_GRAY_WOOL as BlockItem, LIGHT_GRAY_CARPET as BlockItem))
+ CYAN_WOOL.registerHandler(WoolHandler(CYAN_WOOL as BlockItem, CYAN_CARPET as BlockItem))
+ PURPLE_WOOL.registerHandler(WoolHandler(PURPLE_WOOL as BlockItem, PURPLE_CARPET as BlockItem))
+ BLUE_WOOL.registerHandler(WoolHandler(BLUE_WOOL as BlockItem, BLUE_CARPET as BlockItem))
+ BROWN_WOOL.registerHandler(WoolHandler(BROWN_WOOL as BlockItem, BROWN_CARPET as BlockItem))
+ GREEN_WOOL.registerHandler(WoolHandler(GREEN_WOOL as BlockItem, GREEN_CARPET as BlockItem))
+ RED_WOOL.registerHandler(WoolHandler(RED_WOOL as BlockItem, RED_CARPET as BlockItem))
+ BLACK_WOOL.registerHandler(WoolHandler(BLACK_WOOL as BlockItem, BLACK_CARPET as BlockItem))
+}
+
+@Environment(EnvType.CLIENT)
+private fun Item.registerStaffItemRenderer(renderer: IStaffItemRenderer) {
+ IStaffItemRenderer.register(Registries.ITEM.getId(this), renderer)
+}
+
+@Environment(EnvType.CLIENT)
+private fun Item.registerStaffItemRenderer(staffItem: Block) {
+ registerStaffItemRenderer(BlockStateStaffItemRenderer(staffItem.defaultState))
+}
+
+@Environment(EnvType.CLIENT)
+fun registerVanillaStaffItemRenderers() {
+ ANVIL.registerStaffItemRenderer(Blocks.ANVIL)
+ CHIPPED_ANVIL.registerStaffItemRenderer(Blocks.CHIPPED_ANVIL)
+ DAMAGED_ANVIL.registerStaffItemRenderer(Blocks.DAMAGED_ANVIL)
+
+ BELL.registerStaffItemRenderer(BellBlockHandler.BellStaffItemRenderer())
+
+ BONE_BLOCK.registerStaffItemRenderer(Blocks.BONE_BLOCK)
+
+ CAMPFIRE.registerStaffItemRenderer(Blocks.CAMPFIRE)
+ SOUL_CAMPFIRE.registerStaffItemRenderer(Blocks.SOUL_CAMPFIRE)
+
+ COMMAND_BLOCK.registerStaffItemRenderer(Blocks.COMMAND_BLOCK)
+
+ FURNACE.registerStaffItemRenderer(FurnaceHandler.FurnaceStaffItemRenderer(Blocks.FURNACE))
+ BLAST_FURNACE.registerStaffItemRenderer(FurnaceHandler.FurnaceStaffItemRenderer(Blocks.BLAST_FURNACE))
+ SMOKER.registerStaffItemRenderer(FurnaceHandler.FurnaceStaffItemRenderer(Blocks.SMOKER))
+
+ LIGHTNING_ROD.registerStaffItemRenderer(LightningRodHandler.LightningRodStaffItemRenderer())
+
+ MAGMA_BLOCK.registerStaffItemRenderer(Blocks.MAGMA_BLOCK)
+
+ SNOW_BLOCK.registerStaffItemRenderer(Blocks.SNOW_BLOCK)
+
+ TNT.registerStaffItemRenderer(Blocks.TNT)
+
+ WITHER_SKELETON_SKULL.registerStaffItemRenderer(WitherSkeletonSkullHandler.WitherSkeletonSkullStaffItemRenderer())
+
+ WHITE_WOOL.registerStaffItemRenderer(Blocks.WHITE_WOOL)
+ ORANGE_WOOL.registerStaffItemRenderer(Blocks.ORANGE_WOOL)
+ MAGENTA_WOOL.registerStaffItemRenderer(Blocks.MAGENTA_WOOL)
+ LIGHT_BLUE_WOOL.registerStaffItemRenderer(Blocks.LIGHT_BLUE_WOOL)
+ YELLOW_WOOL.registerStaffItemRenderer(Blocks.YELLOW_WOOL)
+ LIME_WOOL.registerStaffItemRenderer(Blocks.LIME_WOOL)
+ PINK_WOOL.registerStaffItemRenderer(Blocks.PINK_WOOL)
+ GRAY_WOOL.registerStaffItemRenderer(Blocks.GRAY_WOOL)
+ LIGHT_GRAY_WOOL.registerStaffItemRenderer(Blocks.LIGHT_GRAY_WOOL)
+ CYAN_WOOL.registerStaffItemRenderer(Blocks.CYAN_WOOL)
+ PURPLE_WOOL.registerStaffItemRenderer(Blocks.PURPLE_WOOL)
+ BLUE_WOOL.registerStaffItemRenderer(Blocks.BLUE_WOOL)
+ BROWN_WOOL.registerStaffItemRenderer(Blocks.BROWN_WOOL)
+ GREEN_WOOL.registerStaffItemRenderer(Blocks.GREEN_WOOL)
+ RED_WOOL.registerStaffItemRenderer(Blocks.RED_WOOL)
+ BLACK_WOOL.registerStaffItemRenderer(Blocks.BLACK_WOOL)
+}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/WitherSkeletonSkullHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/WitherSkeletonSkullHandler.kt
new file mode 100644
index 000000000..3f6331a24
--- /dev/null
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/WitherSkeletonSkullHandler.kt
@@ -0,0 +1,141 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
+package opekope2.avm_staff.internal.staff_handler
+
+import dev.architectury.event.EventResult
+import net.fabricmc.api.EnvType
+import net.fabricmc.api.Environment
+import net.minecraft.block.AbstractSkullBlock
+import net.minecraft.block.Blocks
+import net.minecraft.client.render.VertexConsumerProvider
+import net.minecraft.client.render.block.entity.SkullBlockEntityRenderer
+import net.minecraft.client.render.entity.model.SkullEntityModel
+import net.minecraft.client.render.model.json.ModelTransformationMode
+import net.minecraft.client.util.math.MatrixStack
+import net.minecraft.entity.Entity
+import net.minecraft.entity.LivingEntity
+import net.minecraft.entity.effect.StatusEffectInstance
+import net.minecraft.entity.effect.StatusEffects
+import net.minecraft.entity.player.PlayerEntity
+import net.minecraft.entity.projectile.WitherSkullEntity
+import net.minecraft.item.ItemStack
+import net.minecraft.util.Hand
+import net.minecraft.util.TypedActionResult
+import net.minecraft.world.Difficulty
+import net.minecraft.world.World
+import net.minecraft.world.WorldEvents
+import opekope2.avm_staff.api.item.renderer.IStaffItemRenderer
+import opekope2.avm_staff.api.staff.StaffHandler
+import opekope2.avm_staff.util.*
+
+class WitherSkeletonSkullHandler : StaffHandler() {
+ override val maxUseTime = 20
+
+ override fun use(
+ staffStack: ItemStack,
+ world: World,
+ user: PlayerEntity,
+ hand: Hand
+ ): TypedActionResult {
+ user.setCurrentHand(hand)
+ return TypedActionResult.consume(staffStack)
+ }
+
+ override fun usageTick(staffStack: ItemStack, world: World, user: LivingEntity, remainingUseTicks: Int) {
+ if ((remainingUseTicks and 1) == 0) {
+ shootSkull(world, user, Math.random() < 0.1f) // TODO ratio
+ }
+ }
+
+ override fun attack(staffStack: ItemStack, world: World, attacker: LivingEntity, hand: Hand) {
+ if (attacker is PlayerEntity && attacker.itemCooldownManager.isCoolingDown(staffStack.item)) return
+
+ shootSkull(world, attacker, false)
+ (attacker as? PlayerEntity)?.resetLastAttackedTicks()
+ }
+
+ private fun shootSkull(world: World, user: LivingEntity, charged: Boolean) {
+ if (world.isClient) return
+ if (!user.canUseStaff) return
+ if (user is PlayerEntity && user.isAttackCoolingDown) return
+
+ world.syncWorldEvent(WorldEvents.WITHER_SHOOTS, user.blockPos, 0)
+
+ val (x, y, z) = user.rotationVector
+ world.spawnEntity(WitherSkullEntity(world, user, x, y, z).apply {
+ isCharged = charged
+ setPosition(user.approximateStaffTipPosition)
+ })
+ }
+
+ override fun attackEntity(
+ staffStack: ItemStack,
+ world: World,
+ attacker: LivingEntity,
+ target: Entity,
+ hand: Hand
+ ): EventResult {
+ if (world.isClient) return EventResult.pass()
+ if (target is LivingEntity && !target.isInvulnerableTo(world.damageSources.wither())) {
+ val amplifier = if (world.difficulty == Difficulty.HARD) 1 else 0
+ target.addStatusEffect(StatusEffectInstance(StatusEffects.WITHER, 10 * 20, amplifier))
+ }
+
+ return EventResult.pass()
+ }
+
+ override fun onStoppedUsing(staffStack: ItemStack, world: World, user: LivingEntity, remainingUseTicks: Int) {
+ (user as? PlayerEntity)?.itemCooldownManager?.set(staffStack.item, 4 * (maxUseTime - remainingUseTicks))
+ }
+
+ override fun finishUsing(staffStack: ItemStack, world: World, user: LivingEntity): ItemStack {
+ onStoppedUsing(staffStack, world, user, 0)
+ return staffStack
+ }
+
+ @Environment(EnvType.CLIENT)
+ class WitherSkeletonSkullStaffItemRenderer : IStaffItemRenderer {
+ private val skullModel = SkullEntityModel.getSkullTexturedModelData().createModel()
+
+ override fun renderItemInStaff(
+ staffStack: ItemStack,
+ mode: ModelTransformationMode,
+ matrices: MatrixStack,
+ vertexConsumers: VertexConsumerProvider,
+ light: Int,
+ overlay: Int
+ ) {
+ matrices.push {
+ scale(-1f, -1f, 1f)
+ translate(0f, 8f / 16f, 0f)
+ scale(2f, 2f, 2f)
+ skullModel.render(
+ matrices,
+ vertexConsumers.getBuffer(
+ SkullBlockEntityRenderer.getRenderLayer(
+ (Blocks.WITHER_SKELETON_SKULL as AbstractSkullBlock).skullType, null
+ )
+ ),
+ light,
+ overlay
+ )
+ }
+ }
+ }
+}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/WoolHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/WoolHandler.kt
similarity index 56%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/WoolHandler.kt
rename to StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/WoolHandler.kt
index 9b8f889ce..170c1f266 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/WoolHandler.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_handler/WoolHandler.kt
@@ -16,38 +16,35 @@
* along with this mod. If not, see .
*/
-package opekope2.avm_staff.internal.staff_item_handler
+package opekope2.avm_staff.internal.staff_handler
-import com.google.common.collect.ImmutableMultimap
-import com.google.common.collect.Multimap
-import net.minecraft.block.Block
import net.minecraft.client.MinecraftClient
import net.minecraft.client.network.ClientPlayerEntity
-import net.minecraft.entity.EquipmentSlot
+import net.minecraft.component.type.AttributeModifierSlot
+import net.minecraft.component.type.AttributeModifiersComponent
import net.minecraft.entity.LivingEntity
-import net.minecraft.entity.attribute.EntityAttribute
-import net.minecraft.entity.attribute.EntityAttributeModifier
import net.minecraft.entity.attribute.EntityAttributes
import net.minecraft.entity.player.PlayerEntity
+import net.minecraft.item.BlockItem
import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack
import net.minecraft.registry.tag.BlockTags
-import net.minecraft.sound.SoundCategory
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.world.World
-import net.minecraft.world.event.GameEvent
-import opekope2.avm_staff.api.item.StaffItemHandler
+import opekope2.avm_staff.api.staff.StaffHandler
import opekope2.avm_staff.mixin.IMinecraftClientMixin
+import opekope2.avm_staff.util.addDefault
import opekope2.avm_staff.util.attackDamage
import opekope2.avm_staff.util.attackSpeed
+import opekope2.avm_staff.util.mutableItemStackInStaff
-class WoolHandler(woolBlock: Block, carpetBlock: Block) : StaffItemHandler() {
- private val woolState = woolBlock.defaultState
- private val carpetState = carpetBlock.defaultState
+class WoolHandler(private val woolBlockItem: BlockItem, private val carpetBlockItem: BlockItem) : StaffHandler() {
+ override val attributeModifiers: AttributeModifiersComponent
+ get() = ATTRIBUTE_MODIFIERS
override fun useOnBlock(
staffStack: ItemStack,
@@ -65,40 +62,15 @@ class WoolHandler(woolBlock: Block, carpetBlock: Block) : StaffItemHandler() {
val originalState = world.getBlockState(target)
if (originalState.isIn(BlockTags.WOOL) || originalState.isIn(BlockTags.WOOL_CARPETS)) return ActionResult.FAIL
+ val itemToPlace = if (side == Direction.UP) carpetBlockItem else woolBlockItem
val woolPlaceContext = WoolPlacementContext(
world,
user as? PlayerEntity,
hand,
- staffStack,
+ staffStack.mutableItemStackInStaff!!,
BlockHitResult(target.toCenterPos(), side, target, false)
)
- if (!woolPlaceContext.canPlace()) return ActionResult.FAIL
-
- val placedState = if (side == Direction.UP) carpetState else woolState
- if (!world.isClient) {
- world.setBlockState(woolPlaceContext.blockPos, placedState)
- }
-
- val woolSoundGroup = placedState.soundGroup
- world.playSound(
- user,
- woolPlaceContext.blockPos,
- woolSoundGroup.placeSound,
- SoundCategory.BLOCKS,
- (woolSoundGroup.volume + 1.0f) / 2.0f,
- woolSoundGroup.pitch * 0.8f
- )
- world.emitGameEvent(GameEvent.BLOCK_PLACE, woolPlaceContext.blockPos, GameEvent.Emitter.of(user, placedState))
-
- return ActionResult.SUCCESS
- }
-
- override fun getAttributeModifiers(
- staffStack: ItemStack,
- slot: EquipmentSlot
- ): Multimap {
- return if (slot == EquipmentSlot.MAINHAND) ATTRIBUTE_MODIFIERS
- else super.getAttributeModifiers(staffStack, slot)
+ return itemToPlace.place(woolPlaceContext)
}
private class WoolPlacementContext(
@@ -110,11 +82,11 @@ class WoolHandler(woolBlock: Block, carpetBlock: Block) : StaffItemHandler() {
) : ItemPlacementContext(world, playerEntity, hand, itemStack, blockHitResult)
private companion object {
- private val ATTRIBUTE_MODIFIERS = ImmutableMultimap.of(
- EntityAttributes.GENERIC_ATTACK_DAMAGE,
- attackDamage(2.0),
- EntityAttributes.GENERIC_ATTACK_SPEED,
- attackSpeed(2.0)
- )
+ private val ATTRIBUTE_MODIFIERS = AttributeModifiersComponent.builder()
+ .add(EntityAttributes.GENERIC_ATTACK_DAMAGE, attackDamage(2.0), AttributeModifierSlot.MAINHAND)
+ .add(EntityAttributes.GENERIC_ATTACK_SPEED, attackSpeed(2.0), AttributeModifierSlot.MAINHAND)
+ .addDefault(EntityAttributes.PLAYER_ENTITY_INTERACTION_RANGE)
+ .addDefault(EntityAttributes.PLAYER_BLOCK_INTERACTION_RANGE)
+ .build()
}
}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/AnvilHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/AnvilHandler.kt
deleted file mode 100644
index 512efa24d..000000000
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/AnvilHandler.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.internal.staff_item_handler
-
-import com.google.common.collect.ImmutableMultimap
-import com.google.common.collect.Multimap
-import net.minecraft.entity.Entity
-import net.minecraft.entity.EquipmentSlot
-import net.minecraft.entity.LivingEntity
-import net.minecraft.entity.attribute.EntityAttribute
-import net.minecraft.entity.attribute.EntityAttributeModifier
-import net.minecraft.entity.attribute.EntityAttributes
-import net.minecraft.entity.player.PlayerEntity
-import net.minecraft.item.ItemStack
-import net.minecraft.util.ActionResult
-import net.minecraft.util.Hand
-import net.minecraft.world.World
-import net.minecraft.world.WorldEvents
-import opekope2.avm_staff.api.item.IDisablesShield
-import opekope2.avm_staff.api.item.StaffItemHandler
-import opekope2.avm_staff.util.attackDamage
-import opekope2.avm_staff.util.equipTime
-import opekope2.avm_staff.util.itemInStaff
-import java.util.*
-
-class AnvilHandler(private val damagedStackFactory: () -> ItemStack?) : StaffItemHandler(), IDisablesShield {
- override fun attackEntity(
- staffStack: ItemStack,
- world: World,
- attacker: LivingEntity,
- target: Entity,
- hand: Hand
- ): ActionResult {
- if (world.isClient) return ActionResult.PASS
-
- var broke = false
- if (attacker is PlayerEntity && !attacker.abilities.creativeMode && attacker.random.nextFloat() < 0.12F) {
- val damagedStack = damagedStackFactory()
- staffStack.itemInStaff = damagedStack
- broke = damagedStack == null
- }
-
- world.syncWorldEvent(
- if (broke) WorldEvents.ANVIL_DESTROYED
- else WorldEvents.ANVIL_LANDS,
- target.blockPos,
- 0
- )
-
- return ActionResult.PASS
- }
-
- override fun getAttributeModifiers(
- staffStack: ItemStack,
- slot: EquipmentSlot
- ): Multimap {
- return when (slot) {
- EquipmentSlot.MAINHAND -> MAIN_HAND_ATTRIBUTE_MODIFIERS
- EquipmentSlot.OFFHAND -> OFF_HAND_ATTRIBUTE_MODIFIERS
- else -> super.getAttributeModifiers(staffStack, slot)
- }
- }
-
- private companion object {
- private val MAIN_HAND_ATTRIBUTE_MODIFIERS = ImmutableMultimap.of(
- EntityAttributes.GENERIC_ATTACK_DAMAGE,
- attackDamage(40.0),
- EntityAttributes.GENERIC_ATTACK_SPEED,
- equipTime(4.0),
- EntityAttributes.GENERIC_MOVEMENT_SPEED,
- EntityAttributeModifier(
- UUID.fromString("c0374b4f-d600-4b6a-9984-3ee35d37750d"),
- "Weapon modifier",
- -1.0,
- EntityAttributeModifier.Operation.MULTIPLY_TOTAL
- )
- )
-
- private val OFF_HAND_ATTRIBUTE_MODIFIERS = ImmutableMultimap.of(
- EntityAttributes.GENERIC_MOVEMENT_SPEED,
- EntityAttributeModifier(
- UUID.fromString("c0374b4f-d600-4b6a-9984-3ee35d37750e"),
- "Weapon modifier",
- -1.0,
- EntityAttributeModifier.Operation.MULTIPLY_TOTAL
- )
- )
- }
-}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/BellBlockHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/BellBlockHandler.kt
deleted file mode 100644
index fba1168be..000000000
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/BellBlockHandler.kt
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.internal.staff_item_handler
-
-import com.google.common.collect.ImmutableMultimap
-import com.google.common.collect.Multimap
-import net.fabricmc.api.EnvType
-import net.fabricmc.api.Environment
-import net.minecraft.client.render.block.entity.BellBlockEntityRenderer
-import net.minecraft.client.render.model.*
-import net.minecraft.client.render.model.json.ModelOverrideList
-import net.minecraft.client.render.model.json.ModelTransformation
-import net.minecraft.client.render.model.json.Transformation
-import net.minecraft.client.texture.Sprite
-import net.minecraft.client.util.SpriteIdentifier
-import net.minecraft.entity.Entity
-import net.minecraft.entity.EquipmentSlot
-import net.minecraft.entity.LivingEntity
-import net.minecraft.entity.attribute.EntityAttribute
-import net.minecraft.entity.attribute.EntityAttributeModifier
-import net.minecraft.entity.attribute.EntityAttributes
-import net.minecraft.entity.player.PlayerEntity
-import net.minecraft.item.ItemStack
-import net.minecraft.sound.SoundCategory
-import net.minecraft.sound.SoundEvents
-import net.minecraft.util.ActionResult
-import net.minecraft.util.Hand
-import net.minecraft.util.Identifier
-import net.minecraft.util.TypedActionResult
-import net.minecraft.util.math.Direction
-import net.minecraft.world.World
-import opekope2.avm_staff.api.item.StaffItemHandler
-import opekope2.avm_staff.api.item.model.IStaffItemBakedModel
-import opekope2.avm_staff.api.item.model.IStaffItemUnbakedModel
-import opekope2.avm_staff.api.item.model.StaffItemBakedModel
-import opekope2.avm_staff.util.attackDamage
-import opekope2.avm_staff.util.attackSpeed
-import opekope2.avm_staff.util.getBakedQuads
-import opekope2.avm_staff.util.transform
-import org.joml.Vector3f
-import java.util.function.Function
-import java.util.function.Supplier
-
-class BellBlockHandler : StaffItemHandler() {
- override fun use(
- staffStack: ItemStack,
- world: World,
- user: PlayerEntity,
- hand: Hand
- ): TypedActionResult {
- world.playSound(user, user.blockPos, SoundEvents.BLOCK_BELL_USE, SoundCategory.BLOCKS, 2f, 1f)
-
- return TypedActionResult.success(staffStack)
- }
-
- override fun attackEntity(
- staffStack: ItemStack,
- world: World,
- attacker: LivingEntity,
- target: Entity,
- hand: Hand
- ): ActionResult {
- world.playSound(
- target as? PlayerEntity,
- target.blockPos,
- SoundEvents.BLOCK_BELL_USE,
- attacker.soundCategory,
- 2f,
- 1f
- )
-
- return ActionResult.PASS
- }
-
- override fun getAttributeModifiers(
- staffStack: ItemStack,
- slot: EquipmentSlot
- ): Multimap {
- return if (slot == EquipmentSlot.MAINHAND) ATTRIBUTE_MODIFIERS
- else super.getAttributeModifiers(staffStack, slot)
- }
-
- @Environment(EnvType.CLIENT)
- private class BellUnbakedModel : IStaffItemUnbakedModel {
- override fun getModelDependencies() = setOf()
-
- override fun setParents(modelLoader: Function?) {
- }
-
- override fun bake(
- baker: Baker,
- textureGetter: Function,
- rotationContainer: ModelBakeSettings,
- modelId: Identifier,
- transformation: Transformation
- ): IStaffItemBakedModel {
- val bellSprite = textureGetter.apply(BellBlockEntityRenderer.BELL_BODY_TEXTURE)
- val bellModel = BellBlockEntityRenderer.getTexturedModelData().createModel().getChild("bell_body")
- val quads = bellModel.getBakedQuads(bellSprite, bellTransformation)
- val baked = BasicBakedModel(
- quads,
- createEmptyFaceQuads(),
- true,
- false,
- false,
- bellSprite,
- ModelTransformation.NONE,
- ModelOverrideList.EMPTY
- )
-
- return StaffItemBakedModel(baked.transform(null, transformation, textureGetter))
- }
-
- private companion object {
- private val bellTransformation = Transformation(
- Vector3f(),
- Vector3f(-3.5f / 9f, -4f / 9f, -3.5f / 9f),
- Vector3f(16f / 9f)
- )
-
- private fun createEmptyFaceQuads(): Map> = mapOf(
- Direction.DOWN to listOf(),
- Direction.UP to listOf(),
- Direction.NORTH to listOf(),
- Direction.SOUTH to listOf(),
- Direction.WEST to listOf(),
- Direction.EAST to listOf(),
- )
- }
- }
-
- companion object {
- private val ATTRIBUTE_MODIFIERS = ImmutableMultimap.of(
- EntityAttributes.GENERIC_ATTACK_DAMAGE,
- attackDamage(8.0),
- EntityAttributes.GENERIC_ATTACK_SPEED,
- attackSpeed(1.5)
- )
-
- val modelSupplierFactory: Supplier> = Supplier {
- Supplier(::BellUnbakedModel)
- }
- }
-}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/VanillaStaffItemHandlers.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/VanillaStaffItemHandlers.kt
deleted file mode 100644
index 968b98365..000000000
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/VanillaStaffItemHandlers.kt
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.internal.staff_item_handler
-
-import net.minecraft.block.Block
-import net.minecraft.block.Blocks.*
-import net.minecraft.item.Item
-import net.minecraft.item.Items
-import net.minecraft.recipe.RecipeType
-import net.minecraft.registry.Registries
-import net.minecraft.sound.SoundEvents
-import opekope2.avm_staff.IStaffMod
-import opekope2.avm_staff.api.item.StaffItemHandler
-import opekope2.avm_staff.api.item.model.IStaffItemUnbakedModel
-import opekope2.avm_staff.api.item.model.UnbakedBlockStateModelSupplier
-import java.util.function.Supplier
-
-private fun Item.registerHandler(
- handler: StaffItemHandler,
- itemModelSupplierFactory: Supplier>
-) {
- StaffItemHandler.register(Registries.ITEM.getId(this), handler, itemModelSupplierFactory)
-}
-
-private fun Item.registerHandler(handler: StaffItemHandler, staffItem: Block) {
- registerHandler(handler) { UnbakedBlockStateModelSupplier(staffItem.defaultState) }
-}
-
-@Suppress("unused")
-fun registerVanillaStaffItemHandlers() {
- Items.ANVIL.registerHandler(AnvilHandler(Items.CHIPPED_ANVIL::getDefaultStack), ANVIL)
- Items.CHIPPED_ANVIL.registerHandler(AnvilHandler(Items.DAMAGED_ANVIL::getDefaultStack), CHIPPED_ANVIL)
- Items.DAMAGED_ANVIL.registerHandler(AnvilHandler { null }, DAMAGED_ANVIL)
-
- Items.BELL.registerHandler(BellBlockHandler(), BellBlockHandler.modelSupplierFactory)
-
- Items.BONE_BLOCK.registerHandler(BoneBlockHandler(), BONE_BLOCK)
-
- Items.CAMPFIRE.registerHandler(
- CampfireHandler(
- IStaffMod::flamethrowerParticleType,
- CampfireHandler.Properties(1 / 20.0, 5 / 20.0, 1, 0.1)
- ),
- CAMPFIRE
- )
- Items.SOUL_CAMPFIRE.registerHandler(
- CampfireHandler(
- IStaffMod::soulFlamethrowerParticleType,
- CampfireHandler.Properties(2 / 20.0, 10 / 20.0, 2, 0.12)
- ),
- SOUL_CAMPFIRE
- )
-
- Items.FURNACE.registerHandler(
- FurnaceHandler(RecipeType.SMELTING, SoundEvents.BLOCK_FURNACE_FIRE_CRACKLE),
- FurnaceHandler.getModelSupplierFactory(FURNACE)
- )
- Items.BLAST_FURNACE.registerHandler(
- FurnaceHandler(RecipeType.BLASTING, SoundEvents.BLOCK_BLASTFURNACE_FIRE_CRACKLE),
- FurnaceHandler.getModelSupplierFactory(BLAST_FURNACE)
- )
- Items.SMOKER.registerHandler(
- FurnaceHandler(RecipeType.SMOKING, SoundEvents.BLOCK_SMOKER_SMOKE),
- FurnaceHandler.getModelSupplierFactory(SMOKER)
- )
-
- Items.LIGHTNING_ROD.registerHandler(LightningRodHandler(), LightningRodHandler.modelSupplier)
-
- Items.MAGMA_BLOCK.registerHandler(MagmaBlockHandler(), MAGMA_BLOCK)
-
- Items.SNOW_BLOCK.registerHandler(SnowBlockHandler(), SNOW_BLOCK)
-
- Items.TNT.registerHandler(TntHandler(), TNT)
-
- Items.WITHER_SKELETON_SKULL.registerHandler(
- WitherSkeletonSkullHandler(),
- WitherSkeletonSkullHandler.modelSupplierFactory
- )
-
- Items.WHITE_WOOL.registerHandler(WoolHandler(WHITE_WOOL, WHITE_CARPET), WHITE_WOOL)
- Items.ORANGE_WOOL.registerHandler(WoolHandler(ORANGE_WOOL, ORANGE_CARPET), ORANGE_WOOL)
- Items.MAGENTA_WOOL.registerHandler(WoolHandler(MAGENTA_WOOL, MAGENTA_CARPET), MAGENTA_WOOL)
- Items.LIGHT_BLUE_WOOL.registerHandler(WoolHandler(LIGHT_BLUE_WOOL, LIGHT_BLUE_CARPET), LIGHT_BLUE_WOOL)
- Items.YELLOW_WOOL.registerHandler(WoolHandler(YELLOW_WOOL, YELLOW_CARPET), YELLOW_WOOL)
- Items.LIME_WOOL.registerHandler(WoolHandler(LIME_WOOL, LIME_CARPET), LIME_WOOL)
- Items.PINK_WOOL.registerHandler(WoolHandler(PINK_WOOL, PINK_CARPET), PINK_WOOL)
- Items.GRAY_WOOL.registerHandler(WoolHandler(GRAY_WOOL, GRAY_CARPET), GRAY_WOOL)
- Items.LIGHT_GRAY_WOOL.registerHandler(WoolHandler(LIGHT_GRAY_WOOL, LIGHT_GRAY_CARPET), LIGHT_GRAY_WOOL)
- Items.CYAN_WOOL.registerHandler(WoolHandler(CYAN_WOOL, CYAN_CARPET), CYAN_WOOL)
- Items.PURPLE_WOOL.registerHandler(WoolHandler(PURPLE_WOOL, PURPLE_CARPET), PURPLE_WOOL)
- Items.BLUE_WOOL.registerHandler(WoolHandler(BLUE_WOOL, BLUE_CARPET), BLUE_WOOL)
- Items.BROWN_WOOL.registerHandler(WoolHandler(BROWN_WOOL, BROWN_CARPET), BROWN_WOOL)
- Items.GREEN_WOOL.registerHandler(WoolHandler(GREEN_WOOL, GREEN_CARPET), GREEN_WOOL)
- Items.RED_WOOL.registerHandler(WoolHandler(RED_WOOL, RED_CARPET), RED_WOOL)
- Items.BLACK_WOOL.registerHandler(WoolHandler(BLACK_WOOL, BLACK_CARPET), BLACK_WOOL)
-}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/WitherSkeletonSkullHandler.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/WitherSkeletonSkullHandler.kt
deleted file mode 100644
index a84dc8068..000000000
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/internal/staff_item_handler/WitherSkeletonSkullHandler.kt
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-package opekope2.avm_staff.internal.staff_item_handler
-
-import net.fabricmc.api.EnvType
-import net.fabricmc.api.Environment
-import net.minecraft.client.render.entity.model.SkullEntityModel
-import net.minecraft.client.render.model.*
-import net.minecraft.client.render.model.json.ModelOverrideList
-import net.minecraft.client.render.model.json.ModelTransformation
-import net.minecraft.client.render.model.json.Transformation
-import net.minecraft.client.texture.Sprite
-import net.minecraft.client.texture.SpriteAtlasTexture
-import net.minecraft.client.util.SpriteIdentifier
-import net.minecraft.entity.Entity
-import net.minecraft.entity.LivingEntity
-import net.minecraft.entity.effect.StatusEffectInstance
-import net.minecraft.entity.effect.StatusEffects
-import net.minecraft.entity.player.PlayerEntity
-import net.minecraft.entity.projectile.WitherSkullEntity
-import net.minecraft.item.ItemStack
-import net.minecraft.util.ActionResult
-import net.minecraft.util.Hand
-import net.minecraft.util.Identifier
-import net.minecraft.util.TypedActionResult
-import net.minecraft.util.math.Direction
-import net.minecraft.world.Difficulty
-import net.minecraft.world.World
-import net.minecraft.world.WorldEvents
-import opekope2.avm_staff.api.item.StaffItemHandler
-import opekope2.avm_staff.api.item.model.IStaffItemBakedModel
-import opekope2.avm_staff.api.item.model.IStaffItemUnbakedModel
-import opekope2.avm_staff.api.item.model.StaffItemBakedModel
-import opekope2.avm_staff.util.*
-import org.joml.Vector3f
-import java.util.function.Function
-import java.util.function.Supplier
-
-class WitherSkeletonSkullHandler : StaffItemHandler() {
- override val maxUseTime = 20
-
- override fun use(
- staffStack: ItemStack,
- world: World,
- user: PlayerEntity,
- hand: Hand
- ): TypedActionResult {
- user.setCurrentHand(hand)
- return TypedActionResult.pass(staffStack)
- }
-
- override fun usageTick(staffStack: ItemStack, world: World, user: LivingEntity, remainingUseTicks: Int) {
- if ((remainingUseTicks and 1) == 0) {
- shootSkull(world, user, Math.random() < 0.1f) // TODO ratio
- }
- }
-
- override fun attack(staffStack: ItemStack, world: World, attacker: LivingEntity, hand: Hand): ActionResult {
- if (attacker is PlayerEntity && attacker.itemCooldownManager.isCoolingDown(staffStack.item)) return ActionResult.FAIL
-
- shootSkull(world, attacker, false)
- (attacker as? PlayerEntity)?.resetLastAttackedTicks()
- return ActionResult.SUCCESS
- }
-
- private fun shootSkull(world: World, user: LivingEntity, charged: Boolean) {
- if (!user.canUseStaff) return
- if (user is PlayerEntity && user.isAttackCoolingDown) return
-
- world.syncWorldEvent(null, WorldEvents.WITHER_SHOOTS, user.blockPos, 0)
-
- if (world.isClient) return
-
- val (x, y, z) = user.rotationVector
- world.spawnEntity(WitherSkullEntity(world, user, x, y, z).apply {
- isCharged = charged
- setPosition(user.approximateStaffTipPosition)
- })
- }
-
- override fun attackEntity(
- staffStack: ItemStack,
- world: World,
- attacker: LivingEntity,
- target: Entity,
- hand: Hand
- ): ActionResult {
- if (!world.isClient) {
- if (world.difficulty.id >= Difficulty.NORMAL.id) {
- if (target is LivingEntity && !target.isInvulnerableTo(world.damageSources.wither())) {
- val amplifier = if (world.difficulty == Difficulty.HARD) 1 else 0
- target.addStatusEffect(StatusEffectInstance(StatusEffects.WITHER, 5 * 20, amplifier))
- }
- }
- }
-
- return super.attackEntity(staffStack, world, attacker, target, hand)
- }
-
- override fun onStoppedUsing(staffStack: ItemStack, world: World, user: LivingEntity, remainingUseTicks: Int) {
- (user as? PlayerEntity)?.itemCooldownManager?.set(staffStack.item, 4 * (maxUseTime - remainingUseTicks))
- }
-
- override fun finishUsing(staffStack: ItemStack, world: World, user: LivingEntity): ItemStack {
- onStoppedUsing(staffStack, world, user, 0)
- return staffStack
- }
-
- @Environment(EnvType.CLIENT)
- private class WitherSkeletonSkullUnbakedModel : IStaffItemUnbakedModel {
- override fun getModelDependencies() = setOf()
-
- override fun setParents(modelLoader: Function?) {
- }
-
- override fun bake(
- baker: Baker,
- textureGetter: Function,
- rotationContainer: ModelBakeSettings,
- modelId: Identifier,
- transformation: Transformation
- ): IStaffItemBakedModel {
- val skullSprite = textureGetter.apply(WITHER_SKELETON_SKULL_TEXTURE)
- val skullModel = SkullEntityModel.getSkullTexturedModelData().createModel().getChild("head")
- val quads = skullModel.getBakedQuads(skullSprite, skullTransformation)
- val baked = BasicBakedModel(
- quads,
- createEmptyFaceQuads(),
- true,
- false,
- false,
- skullSprite,
- ModelTransformation.NONE,
- ModelOverrideList.EMPTY
- )
-
- return StaffItemBakedModel(baked.transform(null, transformation, textureGetter))
- }
-
- private companion object {
- private val WITHER_SKELETON_SKULL_TEXTURE = SpriteIdentifier(
- SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
- Identifier("entity/skeleton/wither_skeleton")
- )
-
- private val skullTransformation = Transformation(
- Vector3f(0f, 0f, 180f),
- Vector3f(.5f, 0f, .5f),
- Vector3f(2f)
- )
-
- private fun createEmptyFaceQuads(): Map> = mapOf(
- Direction.DOWN to listOf(),
- Direction.UP to listOf(),
- Direction.NORTH to listOf(),
- Direction.SOUTH to listOf(),
- Direction.WEST to listOf(),
- Direction.EAST to listOf(),
- )
- }
- }
-
- companion object {
- val modelSupplierFactory: Supplier> = Supplier {
- Supplier(::WitherSkeletonSkullUnbakedModel)
- }
- }
-}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/model/StaffItemBakedModel.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/util/ActiveItemTempDataHolderUtil.kt
similarity index 54%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/model/StaffItemBakedModel.kt
rename to StaffMod/src/main/kotlin/opekope2/avm_staff/util/ActiveItemTempDataHolderUtil.kt
index 8effd7b5d..a9cebc9a5 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/api/item/model/StaffItemBakedModel.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/util/ActiveItemTempDataHolderUtil.kt
@@ -16,16 +16,25 @@
* along with this mod. If not, see .
*/
-package opekope2.avm_staff.api.item.model
+@file: JvmSynthetic
-import net.fabricmc.api.EnvType
-import net.fabricmc.api.Environment
-import net.minecraft.client.render.model.BakedModel
+package opekope2.avm_staff.util
+
+import net.minecraft.entity.LivingEntity
+import opekope2.avm_staff.api.item.IActiveItemTempDataHolder
/**
- * Default implementation of [IStaffItemBakedModel], which returns itself as a model for each block state.
+ * Kotlin utility to get or set the temporary data associated with an entity. To be used by the item class being used on
+ * server side.
*
- * @param blockStateModel The model of a block state. [BakedModel] implementation is delegated to it
+ * @see IActiveItemTempDataHolder
*/
-@Environment(EnvType.CLIENT)
-class StaffItemBakedModel(blockStateModel: BakedModel) : BakedModel by blockStateModel, IStaffItemBakedModel
+inline var LivingEntity.activeItemTempData: Any?
+ get() {
+ this as IActiveItemTempDataHolder
+ return `staffMod$getActiveItemTempData`()
+ }
+ set(value) {
+ this as IActiveItemTempDataHolder
+ `staffMod$setActiveItemTempData`(value)
+ }
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/util/AttributeUtil.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/util/AttributeUtil.kt
index 53aaea462..1437b4d44 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/util/AttributeUtil.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/util/AttributeUtil.kt
@@ -20,8 +20,13 @@
package opekope2.avm_staff.util
+import net.minecraft.component.type.AttributeModifierSlot
+import net.minecraft.component.type.AttributeModifiersComponent
+import net.minecraft.entity.attribute.EntityAttribute
import net.minecraft.entity.attribute.EntityAttributeModifier
+import net.minecraft.entity.attribute.EntityAttributes
import net.minecraft.item.Item
+import net.minecraft.registry.entry.RegistryEntry
private const val PLAYER_BASE_ATTACK_DAMAGE = 1.0
private const val PLAYER_BASE_ATTACK_SPEED = 4.0
@@ -34,9 +39,9 @@ private const val PLAYER_BASE_ATTACK_SPEED = 4.0
*/
fun attackDamage(totalAttackDamage: Double): EntityAttributeModifier = EntityAttributeModifier(
Item.ATTACK_DAMAGE_MODIFIER_ID,
- "Weapon modifier",
+ "Staff modifier",
totalAttackDamage - PLAYER_BASE_ATTACK_DAMAGE,
- EntityAttributeModifier.Operation.ADDITION
+ EntityAttributeModifier.Operation.ADD_VALUE
)
/**
@@ -47,9 +52,9 @@ fun attackDamage(totalAttackDamage: Double): EntityAttributeModifier = EntityAtt
*/
fun attackSpeed(totalAttackSpeed: Double): EntityAttributeModifier = EntityAttributeModifier(
Item.ATTACK_SPEED_MODIFIER_ID,
- "Weapon modifier",
+ "Staff modifier",
totalAttackSpeed - PLAYER_BASE_ATTACK_SPEED,
- EntityAttributeModifier.Operation.ADDITION
+ EntityAttributeModifier.Operation.ADD_VALUE
)
/**
@@ -59,3 +64,38 @@ fun attackSpeed(totalAttackSpeed: Double): EntityAttributeModifier = EntityAttri
* @param totalEquipTime The desired equip time in seconds
*/
fun equipTime(totalEquipTime: Double): EntityAttributeModifier = attackSpeed(1.0 / totalEquipTime)
+
+/**
+ * Creates an [EntityAttributeModifier], which increases the interaction range by [additionalRange].
+ *
+ * @param additionalRange The number of blocks to add to the interaction range
+ */
+fun interactionRange(additionalRange: Double) = EntityAttributeModifier(
+ "Staff modifier",
+ additionalRange,
+ EntityAttributeModifier.Operation.ADD_VALUE
+)
+
+/**
+ * Adds the default staff modifier of the given entity attribute as [main hand][AttributeModifierSlot.MAINHAND] modifier.
+ *
+ * @param attribute The entity attribute to add the default value of
+ * @see EntityAttributes.GENERIC_ATTACK_DAMAGE
+ * @see EntityAttributes.GENERIC_ATTACK_SPEED
+ * @see EntityAttributes.PLAYER_ENTITY_INTERACTION_RANGE
+ * @see EntityAttributes.PLAYER_BLOCK_INTERACTION_RANGE
+ */
+fun AttributeModifiersComponent.Builder.addDefault(attribute: RegistryEntry): AttributeModifiersComponent.Builder {
+ add(
+ attribute,
+ when (attribute) {
+ EntityAttributes.GENERIC_ATTACK_DAMAGE -> attackDamage(4.0)
+ EntityAttributes.GENERIC_ATTACK_SPEED -> attackSpeed(2.0)
+ EntityAttributes.PLAYER_ENTITY_INTERACTION_RANGE -> interactionRange(1.0)
+ EntityAttributes.PLAYER_BLOCK_INTERACTION_RANGE -> interactionRange(1.0)
+ else -> throw IllegalArgumentException("Attribute has no default value")
+ },
+ AttributeModifierSlot.MAINHAND
+ )
+ return this
+}
diff --git a/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/platform/neoforge/StaffModPlatformImpl.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/util/ItemStackUtil.kt
similarity index 72%
rename from NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/platform/neoforge/StaffModPlatformImpl.kt
rename to StaffMod/src/main/kotlin/opekope2/avm_staff/util/ItemStackUtil.kt
index 9c17cf228..45fee1bee 100644
--- a/NeoForgeMod/src/main/kotlin/opekope2/avm_staff/internal/platform/neoforge/StaffModPlatformImpl.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/util/ItemStackUtil.kt
@@ -16,11 +16,13 @@
* along with this mod. If not, see .
*/
-@file: JvmName("StaffModPlatformImpl")
+@file: JvmSynthetic
+@file: Suppress("NOTHING_TO_INLINE")
-package opekope2.avm_staff.internal.platform.neoforge
+package opekope2.avm_staff.util
-import opekope2.avm_staff.IStaffMod
-import opekope2.avm_staff.internal.neoforge.StaffMod
+import net.minecraft.item.Item
+import net.minecraft.item.ItemStack
+import net.minecraft.registry.tag.TagKey
-fun getStaffMod(): IStaffMod = StaffMod
+inline operator fun TagKey
- .contains(stack: ItemStack) = stack.isIn(this)
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/util/DfuUtil.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/util/MatrixUtil.kt
similarity index 62%
rename from StaffMod/src/main/kotlin/opekope2/avm_staff/util/DfuUtil.kt
rename to StaffMod/src/main/kotlin/opekope2/avm_staff/util/MatrixUtil.kt
index 7d8065cff..b4d42f011 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/util/DfuUtil.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/util/MatrixUtil.kt
@@ -16,19 +16,20 @@
* along with this mod. If not, see .
*/
-@file: JvmName("DfuUtil")
-@file: Suppress("NOTHING_TO_INLINE")
+@file: JvmName("MatrixUtil")
package opekope2.avm_staff.util
-import com.mojang.datafixers.util.Either
-import com.mojang.datafixers.util.Pair
-import java.util.*
+import net.minecraft.client.util.math.MatrixStack
-inline operator fun Pair.component1(): F = first
-
-inline operator fun Pair.component2(): S = second
-
-inline operator fun Either.component1(): Optional = left()
-
-inline operator fun Either.component2(): Optional = right()
+/**
+ * [Pushes][MatrixStack.push] to the given matrix stack, invokes [action], then [pops][MatrixStack.pop] from the given
+ * matrix stack.
+ *
+ * @param action The action to invoke between [MatrixStack.push] and [MatrixStack.pop]
+ */
+inline fun MatrixStack.push(action: MatrixStack.() -> Unit) {
+ push()
+ action()
+ pop()
+}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/util/ModelUtil.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/util/ModelUtil.kt
deleted file mode 100644
index 273ec783d..000000000
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/util/ModelUtil.kt
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * AvM Staff Mod
- * Copyright (c) 2024 opekope2
- *
- * This mod is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This mod is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this mod. If not, see .
- */
-
-@file: JvmName("ModelUtil")
-
-package opekope2.avm_staff.util
-
-import net.minecraft.block.BlockState
-import net.minecraft.client.model.ModelPart
-import net.minecraft.client.render.LightmapTextureManager
-import net.minecraft.client.render.model.BakedModel
-import net.minecraft.client.render.model.BakedQuad
-import net.minecraft.client.render.model.BasicBakedModel
-import net.minecraft.client.render.model.UnbakedModel
-import net.minecraft.client.render.model.json.Transformation
-import net.minecraft.client.texture.MissingSprite
-import net.minecraft.client.texture.Sprite
-import net.minecraft.client.texture.SpriteAtlasTexture
-import net.minecraft.client.util.SpriteIdentifier
-import net.minecraft.client.util.math.MatrixStack
-import net.minecraft.util.math.Direction
-import net.minecraft.util.math.random.Random
-import opekope2.avm_staff.internal.platform.getQuadBakerVertexConsumer
-import org.joml.Vector3f
-import java.util.function.Consumer
-import java.util.function.Function
-
-/**
- * The transform of an item in the default position of the staff.
- */
-@JvmField
-val TRANSFORM_INTO_STAFF = Transformation(
- Vector3f(),
- Vector3f((16f - 7f) / 16f / 2f, 22f / 16f, (16f - 7f) / 16f / 2f),
- Vector3f(7f / 16f)
-)
-
-private val cullFaces = arrayOf(*Direction.values(), null)
-
-/**
- * Creates a new transformed model.
- *
- * @param blockState Passed to [BakedModel.getQuads]
- * @param transformation The transformation to apply to the model
- * @param textureGetter Function from [UnbakedModel.bake] used to obtain the missing sprite
- */
-fun BakedModel.transform(
- blockState: BlockState?,
- transformation: Transformation,
- textureGetter: Function
-): BakedModel {
- val matrices = MatrixStack()
- transformation.apply(false, matrices)
- val random = Random.create()
- val missingSprite = textureGetter.apply(
- SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, MissingSprite.getMissingSpriteId())
- )
-
- val quads = mutableMapOf>()
- for (cullFace in cullFaces) {
- val cullQuads = quads.getOrPut(cullFace, ::mutableListOf)
- val vertexConsumer = getQuadBakerVertexConsumer(missingSprite, cullQuads::add)
-
- random.setSeed(42L)
- for (quad in getQuads(blockState, cullFace, random)) {
- vertexConsumer.sprite = quad.sprite
- vertexConsumer.quad(
- matrices.peek(),
- quad,
- 1f,
- 1f,
- 1f,
- LightmapTextureManager.pack(blockState?.luminance ?: 0, 0),
- 0 // Overlay is ignored
- )
- }
- }
-
- return BasicBakedModel(
- quads.remove(null)!!,
- quads,
- useAmbientOcclusion(),
- isSideLit,
- hasDepth(),
- particleSprite,
- this.transformation,
- overrides
- )
-}
-
-/**
- * Renders the model to [BakedQuad]s.
- *
- * @param sprite The sprite of the model part
- * @param luminance The luminance of the model. Must be in range `[0,15]`
- * @param transformation The transformation to apply to the model part while rendering
- */
-@JvmOverloads
-fun ModelPart.getBakedQuads(
- sprite: Sprite,
- transformation: Transformation = Transformation.IDENTITY,
- luminance: Int = 0
-): MutableList {
- val quads = mutableListOf()
- getBakedQuads(quads::add, sprite, transformation, luminance)
- return quads
-}
-
-/**
- * Renders the model to [BakedQuad]s.
- *
- * @param bakedQuadConsumer The consumer of the output quads
- * @param sprite The sprite of the model part
- * @param transformation The transformation to apply to the model part while rendering
- * @param luminance The luminance of the model. Must be in range `[0,15]`
- */
-@JvmOverloads
-fun ModelPart.getBakedQuads(
- bakedQuadConsumer: Consumer,
- sprite: Sprite,
- transformation: Transformation = Transformation.IDENTITY,
- luminance: Int = 0
-) {
- val vertexConsumer = sprite.getTextureSpecificVertexConsumer(getQuadBakerVertexConsumer(sprite, bakedQuadConsumer))
- val matrices = MatrixStack()
- transformation.apply(false, matrices)
- render(matrices, vertexConsumer, LightmapTextureManager.pack(luminance, 0), 0) // Overlay is ignored
-}
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/util/StaffUtil.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/util/StaffUtil.kt
index 85f39fc44..4be586af7 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/util/StaffUtil.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/util/StaffUtil.kt
@@ -20,75 +20,84 @@
package opekope2.avm_staff.util
+import net.minecraft.component.ComponentChanges
import net.minecraft.entity.Entity
+import net.minecraft.item.Item
import net.minecraft.item.ItemStack
-import net.minecraft.nbt.NbtCompound
import net.minecraft.registry.Registries
import net.minecraft.util.hit.HitResult
import net.minecraft.util.math.Vec3d
import net.minecraft.world.RaycastContext
-import opekope2.avm_staff.api.item.StaffItemHandler
-
-/**
- * NBT key
- */
-private const val ITEM_KEY = "Item"
+import opekope2.avm_staff.api.staff.StaffHandler
+import opekope2.avm_staff.api.staff.StaffItemComponent
+import opekope2.avm_staff.api.staffItemComponentType
/**
* Checks if an item is added the given staff item stack.
*/
val ItemStack.isItemInStaff: Boolean
@JvmName("isItemInStaff")
- get() = nbt?.contains(ITEM_KEY) ?: false
+ get() = staffItemComponentType.get() in this
/**
- * Gets or sets the item added to the given staff item stack.
+ * Gets the item inserted into the given staff item stack.
*/
-var ItemStack.itemInStaff: ItemStack?
- get() {
- return if (!isItemInStaff) null
- else ItemStack.fromNbt(nbt?.getCompound(ITEM_KEY) ?: return null)
- }
+val ItemStack.itemInStaff: Item?
+ get() = getOrDefault(staffItemComponentType.get(), null)?.item?.item
+
+/**
+ * Gets the item stack inserted into the given staff item stack.
+ * The value returned MUST NOT be modified in any way, use [mutableItemStackInStaff] instead.
+ *
+ * @see mutableItemStackInStaff
+ */
+val ItemStack.itemStackInStaff: ItemStack?
+ get() = getOrDefault(staffItemComponentType.get(), null)?.item
+
+/**
+ * Gets or sets a copy of the item stack inserted into the given staff item stack. The value returned or passed in can
+ * be freely modified.
+ *
+ * @see itemStackInStaff
+ */
+var ItemStack.mutableItemStackInStaff: ItemStack?
+ get() = itemStackInStaff?.copy()
set(value) {
- if (value == null) {
- removeSubNbt(ITEM_KEY)
- return
+ val changes = ComponentChanges.builder()
+
+ if (value == null || value.isEmpty) {
+ changes.remove(staffItemComponentType.get())
+ } else {
+ changes.add(staffItemComponentType.get(), StaffItemComponent(value.copy()))
}
- val staffItemStack = value.split(1)
- val nbt = getOrCreateNbt()
- nbt.put(ITEM_KEY, NbtCompound().also(staffItemStack::writeNbt))
+ applyChanges(changes.build())
}
/**
- * Returns if the given staff item stack has a registered handler.
- * This item stack is not the staff item stack, but the one can be inserted into the staff.
+ * Returns if the given item has a registered handler when inserted into a staff.
*/
-val ItemStack.hasHandlerOfItem: Boolean
- @JvmName("hasHandlerOfStaff")
+val Item.hasStaffHandler: Boolean
+ @JvmName("hasStaffHandler")
get() {
- val itemId = Registries.ITEM.getId(item)
- return itemId in StaffItemHandler
+ val itemId = Registries.ITEM.getId(this)
+ return itemId in StaffHandler
}
/**
- * Returns the handler of the given item stack, if available.
- * This item stack is not the staff item stack, but the one can be inserted into the staff.
+ * Returns the registered staff handler of the given item if available.
*/
-val ItemStack.handlerOfItem: StaffItemHandler?
+val Item.staffHandler: StaffHandler?
get() {
- val itemId = Registries.ITEM.getId(item)
- return StaffItemHandler[itemId]
+ val itemId = Registries.ITEM.getId(this)
+ return StaffHandler[itemId]
}
/**
- * Returns the handler of the given item stack, if available, a dummy one if not, and the empty staff handler if the
- * item stack is `null`.
- * This item stack is not the staff item stack, but the one can be inserted into the staff.
+ * Returns the registered staff handler of the given item if available, [StaffHandler.Default] otherwise.
*/
-val ItemStack?.handlerOfItemOrFallback: StaffItemHandler
- get() = if (this == null) StaffItemHandler.EmptyStaffHandler
- else handlerOfItem ?: StaffItemHandler.FallbackStaffHandler
+val Item?.staffHandlerOrDefault: StaffHandler
+ get() = this?.staffHandler ?: StaffHandler.Default
private const val STAFF_MODEL_LENGTH = 40.0 / 16.0
private const val STAFF_MODEL_ITEM_POSITION_CENTER = 33.5 / 16.0
diff --git a/StaffMod/src/main/kotlin/opekope2/avm_staff/util/VectorUtil.kt b/StaffMod/src/main/kotlin/opekope2/avm_staff/util/VectorUtil.kt
index cbf2db161..82133b793 100644
--- a/StaffMod/src/main/kotlin/opekope2/avm_staff/util/VectorUtil.kt
+++ b/StaffMod/src/main/kotlin/opekope2/avm_staff/util/VectorUtil.kt
@@ -16,7 +16,7 @@
* along with this mod. If not, see .
*/
-@file: JvmName("VectorUtil")
+@file: JvmSynthetic
@file: Suppress("NOTHING_TO_INLINE")
package opekope2.avm_staff.util
diff --git a/StaffMod/src/main/resources/assets/avm_staff/icon.png b/StaffMod/src/main/resources/assets/avm_staff/icon.png
index 05d4286c5..9fe22898b 100644
Binary files a/StaffMod/src/main/resources/assets/avm_staff/icon.png and b/StaffMod/src/main/resources/assets/avm_staff/icon.png differ
diff --git a/StaffMod/src/main/resources/assets/avm_staff/lang/en_us.json b/StaffMod/src/main/resources/assets/avm_staff/lang/en_us.json
index cf4061fc5..7857e8097 100644
--- a/StaffMod/src/main/resources/assets/avm_staff/lang/en_us.json
+++ b/StaffMod/src/main/resources/assets/avm_staff/lang/en_us.json
@@ -1,6 +1,17 @@
{
- "item.avm_staff.staff": "Staff",
- "item.avm_staff.staff.with_item": "Staff with %s",
+ "item.avm_staff.faint_staff_rod": "Faint staff rod",
+ "item.avm_staff.faint_royal_staff_head": "Faint royal staff head",
+ "item.avm_staff.faint_royal_staff": "Faint royal staff",
+ "item.avm_staff.royal_staff": "Royal staff",
+ "item.avm_staff.royal_staff.with_item": "Royal staff with %s",
+ "item.avm_staff.royal_staff_ingredient": "Royal staff ingredient",
+ "item.avm_staff.staff_infusion_smithing_template": "Smithing Template",
+ "item.avm_staff.staff_infusion_smithing_template.title": "Staff Infusion Template",
+ "item.avm_staff.staff_infusion_smithing_template.applies_to": "Faint staffs",
+ "item.avm_staff.staff_infusion_smithing_template.base_slot_description": "Add faint staff",
+ "item.avm_staff.crown_of_king_orange": "Crown of King Orange",
"key.categories.avm_staff": "Staff Mod",
- "key.avm_staff.add_remove_staff_block": "Add/remove staff block"
+ "key.avm_staff.add_remove_staff_item": "Add/remove staff item",
+ "tag.item.avm_staff.staffs": "Staffs",
+ "itemGroup.avm_staff_items": "Staff Mod"
}
diff --git a/StaffMod/src/main/resources/assets/avm_staff/models/item/crown_of_king_orange.json b/StaffMod/src/main/resources/assets/avm_staff/models/item/crown_of_king_orange.json
new file mode 100644
index 000000000..8faf74524
--- /dev/null
+++ b/StaffMod/src/main/resources/assets/avm_staff/models/item/crown_of_king_orange.json
@@ -0,0 +1,62 @@
+{
+ "credit": "Made with Blockbench",
+ "textures": {
+ "particle": "avm_staff:item/crown_of_king_orange",
+ "crown": "avm_staff:item/crown_of_king_orange"
+ },
+ "elements": [
+ {
+ "from": [4, 0, 4],
+ "to": [12, 12, 12],
+ "faces": {
+ "north": {"uv": [0, 0, 8, 12], "texture": "#crown"},
+ "east": {"uv": [0, 0, 8, 12], "texture": "#crown"},
+ "south": {"uv": [0, 0, 8, 12], "texture": "#crown"},
+ "west": {"uv": [0, 0, 8, 12], "texture": "#crown"}
+ }
+ },
+ {
+ "name": "cube inverted",
+ "from": [12, 12, 12],
+ "to": [4, 0, 4],
+ "faces": {
+ "north": {"uv": [0, 12, 8, 0], "texture": "#crown"},
+ "east": {"uv": [0, 12, 8, 0], "texture": "#crown"},
+ "south": {"uv": [0, 12, 8, 0], "texture": "#crown"},
+ "west": {"uv": [0, 12, 8, 0], "texture": "#crown"}
+ }
+ }
+ ],
+ "gui_light": "front",
+ "display": {
+ "thirdperson_righthand": {
+ "translation": [0, 3, 1],
+ "scale": [0.55, 0.55, 0.55]
+ },
+ "thirdperson_lefthand": {
+ "translation": [0, 3, 1],
+ "scale": [0.55, 0.55, 0.55]
+ },
+ "firstperson_righthand": {
+ "rotation": [0, -90, 25],
+ "translation": [1.13, 3.2, 1.13],
+ "scale": [0.68, 0.68, 0.68]
+ },
+ "firstperson_lefthand": {
+ "rotation": [0, -90, 25],
+ "translation": [1.13, 3.2, 1.13],
+ "scale": [0.68, 0.68, 0.68]
+ },
+ "ground": {
+ "translation": [0, 2, 0],
+ "scale": [0.5, 0.5, 0.5]
+ },
+ "gui": {
+ "rotation": [30, 225, 0],
+ "translation": [0, 1.75, 0]
+ },
+ "head": {
+ "translation": [0, 14.5, 0]
+ }
+ }
+}
\ No newline at end of file
diff --git a/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_royal_staff.json b/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_royal_staff.json
new file mode 100644
index 000000000..ba43a8616
--- /dev/null
+++ b/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_royal_staff.json
@@ -0,0 +1,45 @@
+{
+ "credit": "Made with Blockbench",
+ "parent": "minecraft:builtin/entity",
+ "overrides": [
+ {"predicate": {"avm_staff:head": 1}, "model": "avm_staff:item/faint_royal_staff/head"},
+ {"predicate": {"avm_staff:item": 1}, "model": "avm_staff:item/faint_royal_staff/item"},
+ {"predicate": {"avm_staff:rod_top": 1}, "model": "avm_staff:item/faint_royal_staff/rod_top"},
+ {"predicate": {"avm_staff:rod_bottom": 1}, "model": "avm_staff:item/faint_royal_staff/rod_bottom"}
+ ],
+ "display": {
+ "thirdperson_righthand": {
+ "rotation": [0, 90, 0],
+ "translation": [0, 5.4, 0],
+ "scale": [0.85, 0.85, 0.85]
+ },
+ "thirdperson_lefthand": {
+ "rotation": [0, 90, 0],
+ "translation": [0, 5.4, 0],
+ "scale": [0.85, 0.85, 0.85]
+ },
+ "firstperson_righthand": {
+ "rotation": [-25, 90, 0],
+ "translation": [1.13, 0.71, 2.29],
+ "scale": [0.68, 0.68, 0.68]
+ },
+ "firstperson_lefthand": {
+ "rotation": [0, 90, -25],
+ "translation": [1.13, 0.71, 2.29],
+ "scale": [0.68, 0.68, 0.68]
+ },
+ "ground": {
+ "rotation": [90, 45, -90],
+ "translation": [0, 6, 0],
+ "scale": [0.5, 0.5, 0.5]
+ },
+ "gui": {
+ "rotation": [30, 45, 0],
+ "translation": [0, 3, 0],
+ "scale": [1.25, 1.25, 1.25]
+ },
+ "fixed": {
+ "rotation": [90, -45, 90]
+ }
+ }
+}
\ No newline at end of file
diff --git a/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_royal_staff/head.json b/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_royal_staff/head.json
new file mode 100644
index 000000000..b8186dabd
--- /dev/null
+++ b/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_royal_staff/head.json
@@ -0,0 +1,92 @@
+{
+ "credit": "Made with Blockbench",
+ "texture_size": [32, 32],
+ "textures": {
+ "particle": "avm_staff:item/faint_royal_staff/head",
+ "head": "avm_staff:item/faint_royal_staff/head"
+ },
+ "elements": [
+ {
+ "from": [7, 10, 7],
+ "to": [9, 12, 9],
+ "rotation": {"angle": 0, "axis": "y", "origin": [8, 11, 8]},
+ "faces": {
+ "north": {"uv": [8, 2, 10, 4], "texture": "#head"},
+ "east": {"uv": [14, 2, 16, 4], "texture": "#head"},
+ "south": {"uv": [12, 2, 14, 4], "texture": "#head"},
+ "west": {"uv": [10, 2, 12, 4], "texture": "#head"},
+ "up": {"uv": [12, 0, 14, 2], "texture": "#head"}
+ }
+ },
+ {
+ "from": [7, 9, 7],
+ "to": [9, 10, 13],
+ "rotation": {"angle": 0, "axis": "y", "origin": [8, 9.5, 8]},
+ "faces": {
+ "north": {"uv": [0, 3, 1, 5], "rotation": 90, "texture": "#head"},
+ "east": {"uv": [1, 5, 7, 6], "rotation": 180, "texture": "#head"},
+ "south": {"uv": [7, 3, 8, 5], "rotation": 270, "texture": "#head"},
+ "west": {"uv": [1, 2, 7, 3], "texture": "#head"},
+ "up": {"uv": [1, 0, 7, 2], "rotation": 90, "texture": "#head"},
+ "down": {"uv": [1, 3, 7, 5], "rotation": 270, "texture": "#head"}
+ }
+ },
+ {
+ "from": [7, 1, 12],
+ "to": [9, 9, 13],
+ "rotation": {"angle": 0, "axis": "y", "origin": [8, 5, 8]},
+ "faces": {
+ "north": {"uv": [8, 4, 10, 12], "texture": "#head"},
+ "east": {"uv": [13, 4, 14, 12], "texture": "#head"},
+ "south": {"uv": [11, 4, 13, 12], "texture": "#head"},
+ "west": {"uv": [10, 4, 11, 12], "texture": "#head"}
+ }
+ },
+ {
+ "from": [7, 0, 7],
+ "to": [9, 1, 13],
+ "rotation": {"angle": 0, "axis": "y", "origin": [8, 0.5, 8]},
+ "faces": {
+ "north": {"uv": [0, 9, 1, 11], "rotation": 90, "texture": "#head"},
+ "east": {"uv": [1, 11, 7, 12], "rotation": 180, "texture": "#head"},
+ "south": {"uv": [7, 9, 8, 11], "rotation": 270, "texture": "#head"},
+ "west": {"uv": [1, 8, 7, 9], "texture": "#head"},
+ "up": {"uv": [1, 6, 7, 8], "rotation": 90, "texture": "#head"},
+ "down": {"uv": [1, 9, 7, 11], "rotation": 270, "texture": "#head"}
+ }
+ }
+ ],
+ "gui_light": "front",
+ "display": {
+ "thirdperson_righthand": {
+ "translation": [0, -0.5, -1.25],
+ "scale": [0.55, 0.55, 0.55]
+ },
+ "thirdperson_lefthand": {
+ "translation": [0, -0.5, -1.25],
+ "scale": [0.55, 0.55, 0.55]
+ },
+ "firstperson_righthand": {
+ "rotation": [-25, 0, 0],
+ "translation": [-0.37, 3.2, 1.13],
+ "scale": [0.68, 0.68, 0.68]
+ },
+ "firstperson_lefthand": {
+ "rotation": [-25, 0, 0],
+ "translation": [-0.37, 3.2, 1.13],
+ "scale": [0.68, 0.68, 0.68]
+ },
+ "ground": {
+ "translation": [0, 2, -1],
+ "scale": [0.5, 0.5, 0.5]
+ },
+ "gui": {
+ "rotation": [0, 90, 0],
+ "translation": [0, 2, 0]
+ },
+ "fixed": {
+ "rotation": [0, -90, 0],
+ "translation": [0, 2, 0]
+ }
+ }
+}
\ No newline at end of file
diff --git a/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_royal_staff/item.json b/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_royal_staff/item.json
new file mode 100644
index 000000000..0878ced6b
--- /dev/null
+++ b/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_royal_staff/item.json
@@ -0,0 +1,9 @@
+{
+ "credit": "Made with Blockbench",
+ "display": {
+ "fixed": {
+ "translation": [0, -3, 0],
+ "scale": [0.5, 0.5, 0.5]
+ }
+ }
+}
\ No newline at end of file
diff --git a/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_royal_staff/rod_bottom.json b/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_royal_staff/rod_bottom.json
new file mode 100644
index 000000000..20155a93f
--- /dev/null
+++ b/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_royal_staff/rod_bottom.json
@@ -0,0 +1,7 @@
+{
+ "credit": "Made with Blockbench",
+ "parent": "avm_staff:item/faint_staff_rod",
+ "textures": {
+ "rod": "avm_staff:item/faint_royal_staff/rod_bottom"
+ }
+}
\ No newline at end of file
diff --git a/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_royal_staff/rod_top.json b/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_royal_staff/rod_top.json
new file mode 100644
index 000000000..7ea8a216b
--- /dev/null
+++ b/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_royal_staff/rod_top.json
@@ -0,0 +1,7 @@
+{
+ "credit": "Made with Blockbench",
+ "parent": "avm_staff:item/faint_staff_rod",
+ "textures": {
+ "rod": "avm_staff:item/faint_royal_staff/rod_top"
+ }
+}
\ No newline at end of file
diff --git a/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_royal_staff_head.json b/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_royal_staff_head.json
new file mode 100644
index 000000000..057711595
--- /dev/null
+++ b/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_royal_staff_head.json
@@ -0,0 +1,4 @@
+{
+ "credit": "Made with Blockbench",
+ "parent": "avm_staff:item/faint_royal_staff/head"
+}
\ No newline at end of file
diff --git a/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_staff_rod.json b/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_staff_rod.json
new file mode 100644
index 000000000..f81a4f47e
--- /dev/null
+++ b/StaffMod/src/main/resources/assets/avm_staff/models/item/faint_staff_rod.json
@@ -0,0 +1,55 @@
+{
+ "credit": "Made with Blockbench",
+ "texture_size": [32, 32],
+ "textures": {
+ "particle": "avm_staff:item/faint_staff_rod",
+ "rod": "avm_staff:item/faint_staff_rod"
+ },
+ "elements": [
+ {
+ "from": [7, 0, 7],
+ "to": [9, 14, 9],
+ "rotation": {"angle": 0, "axis": "y", "origin": [8, 7, 8]},
+ "faces": {
+ "north": {"uv": [0, 2, 2, 16], "texture": "#rod"},
+ "east": {"uv": [6, 2, 8, 16], "texture": "#rod"},
+ "south": {"uv": [4, 2, 6, 16], "texture": "#rod"},
+ "west": {"uv": [2, 2, 4, 16], "texture": "#rod"},
+ "up": {"uv": [4, 0, 6, 2], "texture": "#rod"},
+ "down": {"uv": [2, 0, 4, 2], "texture": "#rod"}
+ }
+ }
+ ],
+ "gui_light": "front",
+ "display": {
+ "thirdperson_righthand": {
+ "translation": [0, 3, 1],
+ "scale": [0.55, 0.55, 0.55]
+ },
+ "thirdperson_lefthand": {
+ "translation": [0, 3, 1],
+ "scale": [0.55, 0.55, 0.55]
+ },
+ "firstperson_righthand": {
+ "rotation": [0, -90, 25],
+ "translation": [1.13, 3.2, 1.13],
+ "scale": [0.68, 0.68, 0.68]
+ },
+ "firstperson_lefthand": {
+ "rotation": [0, -90, 25],
+ "translation": [1.13, 3.2, 1.13],
+ "scale": [0.68, 0.68, 0.68]
+ },
+ "ground": {
+ "translation": [0, 2, 0],
+ "scale": [0.5, 0.5, 0.5]
+ },
+ "gui": {
+ "translation": [0, 1, 0]
+ },
+ "fixed": {
+ "rotation": [0, 180, 0],
+ "translation": [0, 1, 0]
+ }
+ }
+}
\ No newline at end of file
diff --git a/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff.json b/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff.json
new file mode 100644
index 000000000..6bc726f20
--- /dev/null
+++ b/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff.json
@@ -0,0 +1,46 @@
+{
+ "credit": "Made with Blockbench",
+ "parent": "minecraft:builtin/entity",
+ "overrides": [
+ {"predicate": {"avm_staff:using_item": 1}, "model": "avm_staff:item/royal_staff/point_forward"},
+ {"predicate": {"avm_staff:head": 1}, "model": "avm_staff:item/royal_staff/head"},
+ {"predicate": {"avm_staff:item": 1}, "model": "avm_staff:item/royal_staff/item"},
+ {"predicate": {"avm_staff:rod_top": 1}, "model": "avm_staff:item/royal_staff/rod_top"},
+ {"predicate": {"avm_staff:rod_bottom": 1}, "model": "avm_staff:item/royal_staff/rod_bottom"}
+ ],
+ "display": {
+ "thirdperson_righthand": {
+ "rotation": [0, 90, 0],
+ "translation": [0, 5.4, 0],
+ "scale": [0.85, 0.85, 0.85]
+ },
+ "thirdperson_lefthand": {
+ "rotation": [0, 90, 0],
+ "translation": [0, 5.4, 0],
+ "scale": [0.85, 0.85, 0.85]
+ },
+ "firstperson_righthand": {
+ "rotation": [-25, 90, 0],
+ "translation": [1.13, 0.71, 2.29],
+ "scale": [0.68, 0.68, 0.68]
+ },
+ "firstperson_lefthand": {
+ "rotation": [0, 90, -25],
+ "translation": [1.13, 0.71, 2.29],
+ "scale": [0.68, 0.68, 0.68]
+ },
+ "ground": {
+ "rotation": [90, 45, -90],
+ "translation": [0, 6, 0],
+ "scale": [0.5, 0.5, 0.5]
+ },
+ "gui": {
+ "rotation": [30, 45, 0],
+ "translation": [0, 3, 0],
+ "scale": [1.25, 1.25, 1.25]
+ },
+ "fixed": {
+ "rotation": [90, -45, 90]
+ }
+ }
+}
\ No newline at end of file
diff --git a/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff/head.json b/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff/head.json
new file mode 100644
index 000000000..5efafb67f
--- /dev/null
+++ b/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff/head.json
@@ -0,0 +1,7 @@
+{
+ "credit": "Made with Blockbench",
+ "parent": "avm_staff:item/faint_royal_staff/head",
+ "textures": {
+ "head": "avm_staff:item/royal_staff/head"
+ }
+}
\ No newline at end of file
diff --git a/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff/item.json b/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff/item.json
new file mode 100644
index 000000000..0878ced6b
--- /dev/null
+++ b/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff/item.json
@@ -0,0 +1,9 @@
+{
+ "credit": "Made with Blockbench",
+ "display": {
+ "fixed": {
+ "translation": [0, -3, 0],
+ "scale": [0.5, 0.5, 0.5]
+ }
+ }
+}
\ No newline at end of file
diff --git a/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff/point_forward.json b/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff/point_forward.json
new file mode 100644
index 000000000..e6595007c
--- /dev/null
+++ b/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff/point_forward.json
@@ -0,0 +1,26 @@
+{
+ "credit": "Made with Blockbench",
+ "parent": "avm_staff:item/faint_royal_staff",
+ "display": {
+ "thirdperson_righthand": {
+ "rotation": [-75, 90, 0],
+ "translation": [0, -1.12, -7.28],
+ "scale": [0.85, 0.85, 0.85]
+ },
+ "thirdperson_lefthand": {
+ "rotation": [-75, 90, 0],
+ "translation": [0, -1.12, -7.28],
+ "scale": [0.85, 0.85, 0.85]
+ },
+ "firstperson_righthand": {
+ "rotation": [-180, 70, 105],
+ "translation": [-2.86, 3.98, 0.74],
+ "scale": [0.68, 0.68, 0.68]
+ },
+ "firstperson_lefthand": {
+ "rotation": [-180, 70, 105],
+ "translation": [-2.86, 3.96, 0.74],
+ "scale": [0.68, 0.68, 0.68]
+ }
+ }
+}
\ No newline at end of file
diff --git a/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff/rod_bottom.json b/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff/rod_bottom.json
new file mode 100644
index 000000000..8da8c3361
--- /dev/null
+++ b/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff/rod_bottom.json
@@ -0,0 +1,7 @@
+{
+ "credit": "Made with Blockbench",
+ "parent": "avm_staff:item/faint_staff_rod",
+ "textures": {
+ "rod": "avm_staff:item/royal_staff/rod_bottom"
+ }
+}
\ No newline at end of file
diff --git a/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff/rod_top.json b/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff/rod_top.json
new file mode 100644
index 000000000..19c8ec946
--- /dev/null
+++ b/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff/rod_top.json
@@ -0,0 +1,7 @@
+{
+ "credit": "Made with Blockbench",
+ "parent": "avm_staff:item/faint_staff_rod",
+ "textures": {
+ "rod": "avm_staff:item/royal_staff/rod_top"
+ }
+}
\ No newline at end of file
diff --git a/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff_ingredient.json b/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff_ingredient.json
new file mode 100644
index 000000000..e0e3a23a5
--- /dev/null
+++ b/StaffMod/src/main/resources/assets/avm_staff/models/item/royal_staff_ingredient.json
@@ -0,0 +1,6 @@
+{
+ "parent": "minecraft:item/generated",
+ "textures": {
+ "layer0": "avm_staff:item/royal_staff_ingredient"
+ }
+}
diff --git a/StaffMod/src/main/resources/assets/avm_staff/models/item/staff.json b/StaffMod/src/main/resources/assets/avm_staff/models/item/staff.json
deleted file mode 100644
index b5a1f4477..000000000
--- a/StaffMod/src/main/resources/assets/avm_staff/models/item/staff.json
+++ /dev/null
@@ -1,108 +0,0 @@
-{
- "credit": "Made with Blockbench",
- "texture_size": [32, 32],
- "textures": {
- "particle": "avm_staff:item/staff",
- "staff": "avm_staff:item/staff"
- },
- "elements": [
- {
- "from": [7, 30, 7],
- "to": [9, 32, 9],
- "rotation": {"angle": 0, "axis": "y", "origin": [8, 31, 8]},
- "faces": {
- "north": {"uv": [5, 2.5, 6, 3.5], "texture": "#staff"},
- "east": {"uv": [8, 2.5, 9, 3.5], "texture": "#staff"},
- "south": {"uv": [7, 2.5, 8, 3.5], "texture": "#staff"},
- "west": {"uv": [6, 2.5, 7, 3.5], "texture": "#staff"},
- "up": {"uv": [6, 1.5, 7, 2.5], "texture": "#staff"}
- }
- },
- {
- "from": [7, 29, 7],
- "to": [9, 30, 12.5],
- "rotation": {"angle": 0, "axis": "y", "origin": [8, 29.5, 8]},
- "faces": {
- "north": {"uv": [5, 6, 6, 6.5], "texture": "#staff"},
- "east": {"uv": [9.75, 6, 12.5, 6.5], "texture": "#staff"},
- "south": {"uv": [8.75, 6, 9.75, 6.5], "texture": "#staff"},
- "west": {"uv": [6, 6, 8.75, 6.5], "texture": "#staff"},
- "up": {"uv": [6, 5, 8.75, 6], "rotation": 90, "texture": "#staff"},
- "down": {"uv": [6, 6.5, 8.75, 7.5], "rotation": 270, "texture": "#staff"}
- }
- },
- {
- "from": [7, 22, 11.5],
- "to": [9, 29, 12.5],
- "rotation": {"angle": 0, "axis": "y", "origin": [8, 25.5, 8]},
- "faces": {
- "north": {"uv": [7.25, 8.5, 8.25, 12], "texture": "#staff"},
- "east": {"uv": [6.75, 8.5, 7.25, 12], "texture": "#staff"},
- "south": {"uv": [8.75, 8.5, 9.75, 12], "texture": "#staff"},
- "west": {"uv": [8.25, 8.5, 8.75, 12], "texture": "#staff"}
- }
- },
- {
- "from": [7, 21, 7],
- "to": [9, 22, 12.5],
- "rotation": {"angle": 0, "axis": "y", "origin": [8, 21.5, 8]},
- "faces": {
- "north": {"uv": [5, 14.5, 6, 15], "texture": "#staff"},
- "east": {"uv": [8.75, 13.5, 6, 13], "texture": "#staff"},
- "south": {"uv": [8.75, 14.5, 9.75, 15], "texture": "#staff"},
- "west": {"uv": [6, 14.5, 8.75, 15], "texture": "#staff"},
- "up": {"uv": [6, 13.5, 8.75, 14.5], "rotation": 90, "texture": "#staff"},
- "down": {"uv": [6, 15, 8.75, 16], "rotation": 270, "texture": "#staff"}
- }
- },
- {
- "from": [7, -8, 7],
- "to": [9, 21, 9],
- "rotation": {"angle": 0, "axis": "y", "origin": [8, 6.5, 8]},
- "faces": {
- "north": {"uv": [0, 0.5, 1, 15], "texture": "#staff"},
- "east": {"uv": [3, 0.5, 4, 15], "texture": "#staff"},
- "south": {"uv": [2, 0.5, 3, 15], "texture": "#staff"},
- "west": {"uv": [1, 0.5, 2, 15], "texture": "#staff"},
- "down": {"uv": [1, 15, 2, 16], "texture": "#staff"}
- }
- }
- ],
- "gui_light": "front",
- "overrides": [
- {"predicate": {"avm_staff:using_item": 1}, "model": "avm_staff:item/staff_in_use"}
- ],
- "display": {
- "thirdperson_righthand": {
- "rotation": [0, 90, 0],
- "translation": [0, 2, 0],
- "scale": [0.85, 0.85, 0.85]
- },
- "thirdperson_lefthand": {
- "rotation": [0, 90, 0],
- "translation": [0, 2, 0],
- "scale": [0.85, 0.85, 0.85]
- },
- "firstperson_righthand": {
- "rotation": [-25, 90, 0],
- "translation": [1.13, -1.8, 3.46],
- "scale": [0.68, 0.68, 0.68]
- },
- "firstperson_lefthand": {
- "rotation": [0, 90, -25],
- "translation": [1.13, -1.8, 3.46],
- "scale": [0.68, 0.68, 0.68]
- },
- "ground": {
- "rotation": [90, 45, -90],
- "translation": [-1.75, 6, 0],
- "scale": [0.5, 0.5, 0.5]
- },
- "gui": {
- "rotation": [90, 45, -90]
- },
- "fixed": {
- "rotation": [90, -45, 90]
- }
- }
-}
\ No newline at end of file
diff --git a/StaffMod/src/main/resources/assets/avm_staff/models/item/staff_infusion_smithing_template.json b/StaffMod/src/main/resources/assets/avm_staff/models/item/staff_infusion_smithing_template.json
new file mode 100644
index 000000000..9847816b4
--- /dev/null
+++ b/StaffMod/src/main/resources/assets/avm_staff/models/item/staff_infusion_smithing_template.json
@@ -0,0 +1,6 @@
+{
+ "parent": "minecraft:item/generated",
+ "textures": {
+ "layer0": "avm_staff:item/staff_infusion_smithing_template"
+ }
+}
diff --git a/StaffMod/src/main/resources/assets/avm_staff/textures/item/crown_of_king_orange.png b/StaffMod/src/main/resources/assets/avm_staff/textures/item/crown_of_king_orange.png
new file mode 100644
index 000000000..4b9a08358
Binary files /dev/null and b/StaffMod/src/main/resources/assets/avm_staff/textures/item/crown_of_king_orange.png differ
diff --git a/StaffMod/src/main/resources/assets/avm_staff/textures/item/faint_royal_staff/head.png b/StaffMod/src/main/resources/assets/avm_staff/textures/item/faint_royal_staff/head.png
new file mode 100644
index 000000000..cfc250ba5
Binary files /dev/null and b/StaffMod/src/main/resources/assets/avm_staff/textures/item/faint_royal_staff/head.png differ
diff --git a/StaffMod/src/main/resources/assets/avm_staff/textures/item/faint_royal_staff/rod_bottom.png b/StaffMod/src/main/resources/assets/avm_staff/textures/item/faint_royal_staff/rod_bottom.png
new file mode 100644
index 000000000..44e514237
Binary files /dev/null and b/StaffMod/src/main/resources/assets/avm_staff/textures/item/faint_royal_staff/rod_bottom.png differ
diff --git a/StaffMod/src/main/resources/assets/avm_staff/textures/item/faint_royal_staff/rod_top.png b/StaffMod/src/main/resources/assets/avm_staff/textures/item/faint_royal_staff/rod_top.png
new file mode 100644
index 000000000..db55d4893
Binary files /dev/null and b/StaffMod/src/main/resources/assets/avm_staff/textures/item/faint_royal_staff/rod_top.png differ
diff --git a/StaffMod/src/main/resources/assets/avm_staff/textures/item/faint_staff_rod.png b/StaffMod/src/main/resources/assets/avm_staff/textures/item/faint_staff_rod.png
new file mode 100644
index 000000000..af9d01519
Binary files /dev/null and b/StaffMod/src/main/resources/assets/avm_staff/textures/item/faint_staff_rod.png differ
diff --git a/StaffMod/src/main/resources/assets/avm_staff/textures/item/royal_staff/head.png b/StaffMod/src/main/resources/assets/avm_staff/textures/item/royal_staff/head.png
new file mode 100644
index 000000000..ad33a5ffc
Binary files /dev/null and b/StaffMod/src/main/resources/assets/avm_staff/textures/item/royal_staff/head.png differ
diff --git a/StaffMod/src/main/resources/assets/avm_staff/textures/item/royal_staff/rod_bottom.png b/StaffMod/src/main/resources/assets/avm_staff/textures/item/royal_staff/rod_bottom.png
new file mode 100644
index 000000000..4b68ab497
Binary files /dev/null and b/StaffMod/src/main/resources/assets/avm_staff/textures/item/royal_staff/rod_bottom.png differ
diff --git a/StaffMod/src/main/resources/assets/avm_staff/textures/item/royal_staff/rod_top.png b/StaffMod/src/main/resources/assets/avm_staff/textures/item/royal_staff/rod_top.png
new file mode 100644
index 000000000..c2cc211c8
Binary files /dev/null and b/StaffMod/src/main/resources/assets/avm_staff/textures/item/royal_staff/rod_top.png differ
diff --git a/StaffMod/src/main/resources/assets/avm_staff/textures/item/royal_staff_ingredient.png b/StaffMod/src/main/resources/assets/avm_staff/textures/item/royal_staff_ingredient.png
new file mode 100644
index 000000000..f78501cde
Binary files /dev/null and b/StaffMod/src/main/resources/assets/avm_staff/textures/item/royal_staff_ingredient.png differ
diff --git a/StaffMod/src/main/resources/assets/avm_staff/textures/item/smithing_table/empty_slot_royal_staff.png b/StaffMod/src/main/resources/assets/avm_staff/textures/item/smithing_table/empty_slot_royal_staff.png
new file mode 100644
index 000000000..479438eb4
Binary files /dev/null and b/StaffMod/src/main/resources/assets/avm_staff/textures/item/smithing_table/empty_slot_royal_staff.png differ
diff --git a/StaffMod/src/main/resources/assets/avm_staff/textures/item/staff.png b/StaffMod/src/main/resources/assets/avm_staff/textures/item/staff.png
deleted file mode 100644
index ec47bd7b4..000000000
Binary files a/StaffMod/src/main/resources/assets/avm_staff/textures/item/staff.png and /dev/null differ
diff --git a/StaffMod/src/main/resources/assets/avm_staff/textures/item/staff_infusion_smithing_template.png b/StaffMod/src/main/resources/assets/avm_staff/textures/item/staff_infusion_smithing_template.png
new file mode 100644
index 000000000..eb270787d
Binary files /dev/null and b/StaffMod/src/main/resources/assets/avm_staff/textures/item/staff_infusion_smithing_template.png differ
diff --git a/StaffMod/src/main/resources/avm_staff.accesswidener b/StaffMod/src/main/resources/avm_staff.accesswidener
index 51740a8ee..616ef51d5 100644
--- a/StaffMod/src/main/resources/avm_staff.accesswidener
+++ b/StaffMod/src/main/resources/avm_staff.accesswidener
@@ -1,4 +1,12 @@
accessWidener v2 named
-accessible field net/minecraft/item/Item ATTACK_DAMAGE_MODIFIER_ID Ljava/util/UUID;
-accessible field net/minecraft/item/Item ATTACK_SPEED_MODIFIER_ID Ljava/util/UUID;
+accessible field net/minecraft/block/AnvilBlock FALLING_BLOCK_ENTITY_DAMAGE_MULTIPLIER F
+accessible field net/minecraft/block/AnvilBlock FALLING_BLOCK_ENTITY_MAX_DAMAGE I
+accessible method net/minecraft/block/entity/AbstractFurnaceBlockEntity dropExperience (Lnet/minecraft/server/world/ServerWorld;Lnet/minecraft/util/math/Vec3d;IF)V
+accessible method net/minecraft/entity/Entity getRotationVector (FF)Lnet/minecraft/util/math/Vec3d;
+accessible field net/minecraft/item/SmithingTemplateItem ARMOR_TRIM_ADDITIONS_SLOT_DESCRIPTION_TEXT Lnet/minecraft/text/Text;
+accessible field net/minecraft/item/SmithingTemplateItem ARMOR_TRIM_INGREDIENTS_TEXT Lnet/minecraft/text/Text;
+accessible field net/minecraft/item/SmithingTemplateItem DESCRIPTION_FORMATTING Lnet/minecraft/util/Formatting;
+accessible field net/minecraft/item/SmithingTemplateItem EMPTY_SLOT_REDSTONE_DUST_TEXTURE Lnet/minecraft/util/Identifier;
+accessible field net/minecraft/item/SmithingTemplateItem TITLE_FORMATTING Lnet/minecraft/util/Formatting;
+accessible method net/minecraft/particle/SimpleParticleType (Z)V
diff --git a/StaffMod/src/main/resources/avm_staff.mixins.json b/StaffMod/src/main/resources/avm_staff.mixins.json
index 58c443522..8f9f0b4cd 100644
--- a/StaffMod/src/main/resources/avm_staff.mixins.json
+++ b/StaffMod/src/main/resources/avm_staff.mixins.json
@@ -1,7 +1,7 @@
{
"required": true,
"package": "opekope2.avm_staff.mixin",
- "compatibilityLevel": "JAVA_17",
+ "compatibilityLevel": "JAVA_21",
"minVersion": "0.8",
"injectors": {
"defaultRequire": 1
@@ -9,13 +9,9 @@
"client": [
"BipedEntityModelMixin",
"IMinecraftClientMixin",
- "IParticleMixin",
- "MinecraftClientMixin"
+ "IParticleMixin"
],
"mixins": [
- "IAbstractFurnaceBlockEntityMixin",
- "IEntityMixin",
- "LivingEntityMixin",
- "TntEntityMixin"
+ "LivingEntityMixin"
]
}
diff --git a/StaffMod/src/main/resources/data/avm_staff/loot_tables/add_loot_pool/chests/bastion_treasure.json b/StaffMod/src/main/resources/data/avm_staff/loot_tables/add_loot_pool/chests/bastion_treasure.json
new file mode 100644
index 000000000..c26f719ff
--- /dev/null
+++ b/StaffMod/src/main/resources/data/avm_staff/loot_tables/add_loot_pool/chests/bastion_treasure.json
@@ -0,0 +1,13 @@
+{
+ "pools": [
+ {
+ "rolls": 1,
+ "entries": [
+ {
+ "type": "minecraft:item",
+ "name": "avm_staff:crown_of_king_orange"
+ }
+ ]
+ }
+ ]
+}
diff --git a/StaffMod/src/main/resources/data/avm_staff/loot_tables/add_loot_pool/chests/trial_chambers/reward_unique.json b/StaffMod/src/main/resources/data/avm_staff/loot_tables/add_loot_pool/chests/trial_chambers/reward_unique.json
new file mode 100644
index 000000000..bed1e4d3a
--- /dev/null
+++ b/StaffMod/src/main/resources/data/avm_staff/loot_tables/add_loot_pool/chests/trial_chambers/reward_unique.json
@@ -0,0 +1,17 @@
+{
+ "pools": [
+ {
+ "rolls": 1,
+ "entries": [
+ {
+ "type": "minecraft:item",
+ "name": "avm_staff:staff_infusion_smithing_template"
+ },
+ {
+ "type": "minecraft:empty",
+ "weight": 5
+ }
+ ]
+ }
+ ]
+}
diff --git a/StaffMod/src/main/resources/data/avm_staff/recipes/faint_royal_staff.json b/StaffMod/src/main/resources/data/avm_staff/recipes/faint_royal_staff.json
new file mode 100644
index 000000000..116e169a6
--- /dev/null
+++ b/StaffMod/src/main/resources/data/avm_staff/recipes/faint_royal_staff.json
@@ -0,0 +1,19 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "H",
+ "R",
+ "R"
+ ],
+ "key": {
+ "H": {
+ "item": "avm_staff:faint_royal_staff_head"
+ },
+ "R": {
+ "item": "avm_staff:faint_staff_rod"
+ }
+ },
+ "result": {
+ "id": "avm_staff:faint_royal_staff"
+ }
+}
diff --git a/StaffMod/src/main/resources/data/avm_staff/recipes/faint_royal_staff_head.json b/StaffMod/src/main/resources/data/avm_staff/recipes/faint_royal_staff_head.json
new file mode 100644
index 000000000..2d0a53f75
--- /dev/null
+++ b/StaffMod/src/main/resources/data/avm_staff/recipes/faint_royal_staff_head.json
@@ -0,0 +1,19 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "II",
+ "SI",
+ "II"
+ ],
+ "key": {
+ "I": {
+ "item": "avm_staff:royal_staff_ingredient"
+ },
+ "S": {
+ "item": "minecraft:nether_star"
+ }
+ },
+ "result": {
+ "id": "avm_staff:faint_royal_staff_head"
+ }
+}
diff --git a/StaffMod/src/main/resources/data/avm_staff/recipes/faint_staff_rod.json b/StaffMod/src/main/resources/data/avm_staff/recipes/faint_staff_rod.json
new file mode 100644
index 000000000..88c595ab9
--- /dev/null
+++ b/StaffMod/src/main/resources/data/avm_staff/recipes/faint_staff_rod.json
@@ -0,0 +1,22 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "E",
+ "N",
+ "O"
+ ],
+ "key": {
+ "E": {
+ "item": "minecraft:end_rod"
+ },
+ "N": {
+ "item": "minecraft:blaze_rod"
+ },
+ "O": {
+ "item": "minecraft:breeze_rod"
+ }
+ },
+ "result": {
+ "id": "avm_staff:faint_staff_rod"
+ }
+}
diff --git a/StaffMod/src/main/resources/data/avm_staff/recipes/royal_staff_ingredient.json b/StaffMod/src/main/resources/data/avm_staff/recipes/royal_staff_ingredient.json
new file mode 100644
index 000000000..796c2da8d
--- /dev/null
+++ b/StaffMod/src/main/resources/data/avm_staff/recipes/royal_staff_ingredient.json
@@ -0,0 +1,28 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "ISI",
+ "CKC",
+ "INI"
+ ],
+ "key": {
+ "I": {
+ "item": "minecraft:iron_ingot"
+ },
+ "S": {
+ "item": "minecraft:echo_shard"
+ },
+ "C": {
+ "item": "minecraft:copper_ingot"
+ },
+ "K": {
+ "item": "avm_staff:crown_of_king_orange"
+ },
+ "N": {
+ "item": "minecraft:netherite_ingot"
+ }
+ },
+ "result": {
+ "id": "avm_staff:royal_staff_ingredient"
+ }
+}
diff --git a/StaffMod/src/main/resources/data/avm_staff/recipes/royal_staff_smithing.json b/StaffMod/src/main/resources/data/avm_staff/recipes/royal_staff_smithing.json
new file mode 100644
index 000000000..0a373bec4
--- /dev/null
+++ b/StaffMod/src/main/resources/data/avm_staff/recipes/royal_staff_smithing.json
@@ -0,0 +1,15 @@
+{
+ "type": "minecraft:smithing_transform",
+ "template": {
+ "item": "avm_staff:staff_infusion_smithing_template"
+ },
+ "base": {
+ "item": "avm_staff:faint_royal_staff"
+ },
+ "addition": {
+ "item": "minecraft:redstone"
+ },
+ "result": {
+ "id": "avm_staff:royal_staff"
+ }
+}
diff --git a/StaffMod/src/main/resources/data/avm_staff/recipes/staff_infusion_smithing_template.json b/StaffMod/src/main/resources/data/avm_staff/recipes/staff_infusion_smithing_template.json
new file mode 100644
index 000000000..aff3d0983
--- /dev/null
+++ b/StaffMod/src/main/resources/data/avm_staff/recipes/staff_infusion_smithing_template.json
@@ -0,0 +1,23 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "DTD",
+ "DMD",
+ "DDD"
+ ],
+ "key": {
+ "D": {
+ "item": "minecraft:diamond"
+ },
+ "T": {
+ "item": "avm_staff:staff_infusion_smithing_template"
+ },
+ "M": {
+ "item": "minecraft:lapis_block"
+ }
+ },
+ "result": {
+ "id": "avm_staff:staff_infusion_smithing_template",
+ "count": 2
+ }
+}
diff --git a/StaffMod/src/main/resources/data/avm_staff/tags/items/staffs.json b/StaffMod/src/main/resources/data/avm_staff/tags/items/staffs.json
index 8d8e67097..d252465a6 100644
--- a/StaffMod/src/main/resources/data/avm_staff/tags/items/staffs.json
+++ b/StaffMod/src/main/resources/data/avm_staff/tags/items/staffs.json
@@ -1,6 +1,6 @@
{
"replace": false,
"values": [
- "avm_staff:staff"
+ "avm_staff:royal_staff"
]
}
diff --git a/StaffMod/src/main/resources/data/minecraft/advancements/recipes/avm_staff/faint_royal_staff.json b/StaffMod/src/main/resources/data/minecraft/advancements/recipes/avm_staff/faint_royal_staff.json
new file mode 100644
index 000000000..0f05a7fc5
--- /dev/null
+++ b/StaffMod/src/main/resources/data/minecraft/advancements/recipes/avm_staff/faint_royal_staff.json
@@ -0,0 +1,43 @@
+{
+ "parent": "minecraft:recipes/root",
+ "criteria": {
+ "has_faint_royal_staff_head": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": "avm_staff:faint_royal_staff_head"
+ }
+ ]
+ }
+ },
+ "has_faint_staff_rod": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": "avm_staff:faint_staff_rod"
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "avm_staff:faint_royal_staff"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_faint_royal_staff_head",
+ "has_faint_staff_rod",
+ "has_the_recipe"
+ ]
+ ],
+ "rewards": {
+ "recipes": [
+ "avm_staff:faint_royal_staff"
+ ]
+ }
+}
diff --git a/StaffMod/src/main/resources/data/minecraft/advancements/recipes/avm_staff/faint_royal_staff_head.json b/StaffMod/src/main/resources/data/minecraft/advancements/recipes/avm_staff/faint_royal_staff_head.json
new file mode 100644
index 000000000..e5439a214
--- /dev/null
+++ b/StaffMod/src/main/resources/data/minecraft/advancements/recipes/avm_staff/faint_royal_staff_head.json
@@ -0,0 +1,43 @@
+{
+ "parent": "minecraft:recipes/root",
+ "criteria": {
+ "has_royal_staff_ingredient": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": "avm_staff:royal_staff_ingredient"
+ }
+ ]
+ }
+ },
+ "has_nether_star": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": "minecraft:nether_star"
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "avm_staff:faint_royal_staff_head"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_royal_staff_ingredient",
+ "has_nether_star",
+ "has_the_recipe"
+ ]
+ ],
+ "rewards": {
+ "recipes": [
+ "avm_staff:faint_royal_staff_head"
+ ]
+ }
+}
diff --git a/StaffMod/src/main/resources/data/minecraft/advancements/recipes/avm_staff/faint_staff_rod.json b/StaffMod/src/main/resources/data/minecraft/advancements/recipes/avm_staff/faint_staff_rod.json
new file mode 100644
index 000000000..348e84c87
--- /dev/null
+++ b/StaffMod/src/main/resources/data/minecraft/advancements/recipes/avm_staff/faint_staff_rod.json
@@ -0,0 +1,54 @@
+{
+ "parent": "minecraft:recipes/root",
+ "criteria": {
+ "has_end_rod": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": "minecraft:end_rod"
+ }
+ ]
+ }
+ },
+ "has_blaze_rod": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": "minecraft:blaze_rod"
+ }
+ ]
+ }
+ },
+ "has_breeze_rod": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": "minecraft:breeze_rod"
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "avm_staff:faint_staff_rod"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_end_rod",
+ "has_blaze_rod",
+ "has_breeze_rod",
+ "has_the_recipe"
+ ]
+ ],
+ "rewards": {
+ "recipes": [
+ "avm_staff:faint_staff_rod"
+ ]
+ }
+}
diff --git a/StaffMod/src/main/resources/data/minecraft/advancements/recipes/avm_staff/royal_staff_ingredient.json b/StaffMod/src/main/resources/data/minecraft/advancements/recipes/avm_staff/royal_staff_ingredient.json
new file mode 100644
index 000000000..8ba9ad669
--- /dev/null
+++ b/StaffMod/src/main/resources/data/minecraft/advancements/recipes/avm_staff/royal_staff_ingredient.json
@@ -0,0 +1,32 @@
+{
+ "parent": "minecraft:recipes/root",
+ "criteria": {
+ "has_crown_of_king_orange": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": "avm_staff:crown_of_king_orange"
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "avm_staff:faint_royal_staff_head"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_crown_of_king_orange",
+ "has_the_recipe"
+ ]
+ ],
+ "rewards": {
+ "recipes": [
+ "avm_staff:royal_staff_ingredient"
+ ]
+ }
+}
diff --git a/StaffMod/src/main/resources/data/minecraft/advancements/recipes/avm_staff/royal_staff_smithing.json b/StaffMod/src/main/resources/data/minecraft/advancements/recipes/avm_staff/royal_staff_smithing.json
new file mode 100644
index 000000000..1d65c1375
--- /dev/null
+++ b/StaffMod/src/main/resources/data/minecraft/advancements/recipes/avm_staff/royal_staff_smithing.json
@@ -0,0 +1,32 @@
+{
+ "parent": "minecraft:recipes/root",
+ "criteria": {
+ "has_faint_royal_staff": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": "avm_staff:faint_royal_staff"
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "avm_staff:royal_staff"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_faint_royal_staff",
+ "has_the_recipe"
+ ]
+ ],
+ "rewards": {
+ "recipes": [
+ "avm_staff:royal_staff_smithing"
+ ]
+ }
+}
diff --git a/StaffMod/src/main/resources/data/minecraft/tags/items/piglin_loved.json b/StaffMod/src/main/resources/data/minecraft/tags/items/piglin_loved.json
new file mode 100644
index 000000000..fca333cf3
--- /dev/null
+++ b/StaffMod/src/main/resources/data/minecraft/tags/items/piglin_loved.json
@@ -0,0 +1,6 @@
+{
+ "replace": false,
+ "values": [
+ "avm_staff:crown_of_king_orange"
+ ]
+}
diff --git a/build.gradle.kts b/build.gradle.kts
index 091b6cfa5..cdbb62ca6 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,3 +1,21 @@
+/*
+ * AvM Staff Mod
+ * Copyright (c) 2024 opekope2
+ *
+ * This mod is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This mod is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this mod. If not, see .
+ */
+
plugins {
java
alias(libs.plugins.kotlin.jvm)
@@ -24,9 +42,15 @@ buildscript {
subprojects {
apply(plugin = "dev.architectury.loom")
+ val loom = project.extensions.getByName("loom")
+
dependencies {
"minecraft"(rootProject.libs.minecraft)
- "mappings"(variantOf(rootProject.libs.yarn) { classifier("v2") })
+ //"mappings"(variantOf(rootProject.libs.yarn) { classifier("v2") })
+ "mappings"(loom.layered { // FIXME
+ mappings(variantOf(rootProject.libs.yarn) { classifier("v2") })
+ mappings(rootProject.libs.yarn.patch.neoforge)
+ })
}
}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 55c9fb9da..bacab910a 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,39 +1,40 @@
[versions]
-java = "17" # Don't forget to update *.mixins.json
-kotlin = "1.8.22"
-staff-mod = "0.14.1-beta"
+java = "21" # Don't forget to update *.mixins.json
+kotlin = "1.9.23"
+staff-mod = "0.15.0"
architectury-plugin = "3.4-SNAPSHOT"
-architectury-loom = "1.5-SNAPSHOT"
-yarn = "1.20.4+build.3"
+architectury-loom = "1.6-SNAPSHOT"
+yarn = "1.20.6+build.2"
shadow = "8.1.1"
-minecraft = "1.20.4"
-architectury-api = "11.0.11"
-fabric-loader = "0.15.3"
-forge = "1.20.4-49.0.27"
-neoforge = "20.4.233"
-fabric-api = "0.91.3+1.20.4"
-fabric-language-kotlin = "1.10.18+kotlin.1.9.22"
-kotlinforforge = "4.10.0"
-mixin-extras = "0.3.5"
+minecraft = "1.20.6"
+architectury-api = "12.1.2"
+fabric-loader = "0.15.11"
+#forge = "1.20.4-49.0.27"
+neoforge = "20.6.72-beta"
+fabric-api = "0.99.0+1.20.6"
+fabric-language-kotlin = "1.10.19+kotlin.1.9.23"
+kotlinforforge = "5.1.0"
+mixin-extras = "0.3.6"
dokka = "1.9.20"
[libraries]
minecraft = { group = "com.mojang", name = "minecraft", version.ref = "minecraft" }
yarn = { group = "net.fabricmc", name = "yarn", version.ref = "yarn" }
+yarn-patch-neoforge = { group = "dev.architectury", name = "yarn-mappings-patch-neoforge", version.require = "1.20.5+build.3" } # FIXME
kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" }
architectury = { group = "dev.architectury", name = "architectury", version.ref = "architectury-api" }
architectury-fabric = { group = "dev.architectury", name = "architectury-fabric", version.ref = "architectury-api" }
-architectury-forge = { group = "dev.architectury", name = "architectury-forge", version.ref = "architectury-api" }
+#architectury-forge = { group = "dev.architectury", name = "architectury-forge", version.ref = "architectury-api" }
architectury-neoforge = { group = "dev.architectury", name = "architectury-neoforge", version.ref = "architectury-api" }
fabric-loader = { group = "net.fabricmc", name = "fabric-loader", version.ref = "fabric-loader" }
fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api" }
fabric-language-kotlin = { group = "net.fabricmc", name = "fabric-language-kotlin", version.ref = "fabric-language-kotlin" }
-forge = { group = "net.minecraftforge", name = "forge", version.ref = "forge" }
+#forge = { group = "net.minecraftforge", name = "forge", version.ref = "forge" }
neoforge = { group = "net.neoforged", name = "neoforge", version.ref = "neoforge" }
-kotlinforforge = { group = "thedarkcolour", name = "kotlinforforge", version.ref = "kotlinforforge" }
+#kotlinforforge = { group = "thedarkcolour", name = "kotlinforforge", version.ref = "kotlinforforge" }
kotlinforforge-neoforge = { group = "thedarkcolour", name = "kotlinforforge-neoforge", version.ref = "kotlinforforge" }
mixinextras-common = { group = "io.github.llamalad7", name = "mixinextras-common", version.ref = "mixin-extras" }
-mixinextras-forge = { group = "io.github.llamalad7", name = "mixinextras-forge", version.ref = "mixin-extras" }
+#mixinextras-forge = { group = "io.github.llamalad7", name = "mixinextras-forge", version.ref = "mixin-extras" }
dokka-base = { group = "org.jetbrains.dokka", name = "dokka-base", version.ref = "dokka" }
dokka-plugin-java-syntax = { group = "org.jetbrains.dokka", name = "kotlin-as-java-plugin", version.ref = "dokka" }
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index d64cd4917..e6441136f 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 1af9e0930..b82aa23a4 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew.bat b/gradlew.bat
index 93e3f59f1..25da30dbd 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
diff --git a/settings.gradle.kts b/settings.gradle.kts
index eedb8e76e..81a4dd8c3 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -36,5 +36,5 @@ include(
"StaffMod",
"FabricMod",
"NeoForgeMod",
- "ForgeMod"
+ //"ForgeMod"
)