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" )